]> git.ipfire.org Git - thirdparty/lldpd.git/commitdiff
event: use a separate event timer for each port
authorVincent Bernat <bernat@luffy.cx>
Mon, 31 Dec 2012 17:25:42 +0000 (18:25 +0100)
committerVincent Bernat <bernat@luffy.cx>
Mon, 31 Dec 2012 17:27:23 +0000 (18:27 +0100)
Each port will have its own live with its own timer. If a port just
appeared, it will have its own 30 seconds timer. We also notice
changes independently on each port, so the timer is reset on each
change on a given port.

Also, as a safeguard, update port-related information every 10
minutes. We can catch changes in addresses this way (otherwise,
detecting them via netlink is noisy).

src/daemon/event.c
src/daemon/frame.h
src/daemon/lldpd.c
src/daemon/lldpd.h
src/lldpd-structs.h

index ecec1d37851d056a460281b92dd724a691e539bb..b39f4a3ecac9311c334e0d21808549c9c082916a 100644 (file)
@@ -396,9 +396,11 @@ static void
 levent_update_and_send(evutil_socket_t fd, short what, void *arg)
 {
        struct lldpd *cfg = arg;
-       struct timeval tv = {cfg->g_config.c_tx_interval, 0};
+       struct timeval tv = { cfg->g_config.c_tx_interval, 0 };
        (void)fd; (void)what;
        lldpd_loop(cfg);
+       if (cfg->g_iface_event != NULL)
+               tv.tv_sec *= 20;
        event_add(cfg->g_main_loop, &tv);
 }
 
@@ -436,7 +438,7 @@ levent_init(struct lldpd *cfg)
        }
 #endif
 
-       /* Setup loop that will run every 30 seconds. */
+       /* Setup loop that will run every X seconds. */
        log_debug("event", "register loop timer");
        if (!(cfg->g_main_loop = event_new(cfg->g_base, -1, 0,
                                           levent_update_and_send,
@@ -560,6 +562,7 @@ void
 levent_hardware_release(struct lldpd_hardware *hardware)
 {
        struct lldpd_events *ev, *ev_next;
+       event_free(hardware->h_timer); hardware->h_timer = NULL;
        if (!hardware->h_recv) return;
 
        log_debug("event", "release events for %s", hardware->h_ifname);
@@ -651,3 +654,46 @@ levent_iface_subscribe(struct lldpd *cfg, int socket)
                return;
        }
 }
+
+static void
+levent_send_pdu(evutil_socket_t fd, short what, void *arg)
+{
+       struct lldpd_hardware *hardware = arg;
+       log_debug("event", "trigger sending PDU for port %s",
+           hardware->h_ifname);
+       lldpd_send(hardware);
+
+       struct timeval tv = { hardware->h_cfg->g_config.c_tx_interval, 0 };
+       if (event_add(hardware->h_timer, &tv) == -1) {
+               log_warnx("event", "unable to re-register timer event for port %s",
+                   hardware->h_ifname);
+               event_free(hardware->h_timer);
+               hardware->h_timer = NULL;
+               return;
+       }
+}
+
+void
+levent_schedule_pdu(struct lldpd_hardware *hardware)
+{
+       log_debug("event", "schedule sending PDU on %s",
+           hardware->h_ifname);
+       if (hardware->h_timer == NULL) {
+               hardware->h_timer = evtimer_new(hardware->h_cfg->g_base,
+                   levent_send_pdu, hardware);
+               if (hardware->h_timer == NULL) {
+                       log_warnx("event", "unable to schedule PDU sending for port %s",
+                           hardware->h_ifname);
+                       return;
+               }
+       }
+
+       struct timeval tv = { 0, 0 };
+       if (event_add(hardware->h_timer, &tv) == -1) {
+               log_warnx("event", "unable to register timer event for port %s",
+                   hardware->h_ifname);
+               event_free(hardware->h_timer);
+               hardware->h_timer = NULL;
+               return;
+       }
+}
index 7bb1ea6f749034bcd2a54a382e97ffbb98c84417..17470f97774cca75567cae940c1922929878c356 100644 (file)
@@ -132,6 +132,4 @@ union {
        )
 #define POKE_END_EDP_TLV POKE_END_CDP_TLV
 
-u_int16_t frame_checksum(const u_int8_t *, int, int);
-
 #endif /* _FRAME_H */
index ee2000410911cd1f9ea501896233ed3a4b0e91cc..1c0d81e0f3b45cb05d6a2433fe93c2963e0b3da5 100644 (file)
@@ -227,6 +227,46 @@ notify_clients_deletion(struct lldpd_hardware *hardware,
 #endif
 }
 
+static void
+lldpd_reset_timer(struct lldpd *cfg)
+{
+       /* Reset timer for ports that have been changed. */
+       struct lldpd_hardware *hardware;
+       TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
+               /* We need to compute a checksum of the local port. To do this,
+                * we zero out fields that are not significant, marshal the
+                * port, compute the checksum, then restore. */
+               struct lldpd_port *port = &hardware->h_lport;
+               u_int16_t cksum;
+               u_int8_t *output = NULL;
+               size_t output_len;
+               char save[offsetof(struct lldpd_port, p_id_subtype)];
+               memcpy(save, port, sizeof(save));
+               memset(port, 0, sizeof(save));
+               output_len = marshal_serialize(lldpd_port, port, (void**)&output);
+               memcpy(port, save, sizeof(save));
+               if (output_len == -1) {
+                       log_warnx("localchassis",
+                           "unable to serialize local port %s to check for differences",
+                           hardware->h_ifname);
+                       continue;
+               }
+               cksum = frame_checksum(output, output_len, 0);
+               free(output);
+               if (cksum != hardware->h_lport_cksum) {
+                       log_info("localchassis",
+                           "change detected for port %s, resetting its timer",
+                           hardware->h_ifname);
+                       hardware->h_lport_cksum = cksum;
+                       levent_schedule_pdu(hardware);
+               } else {
+                       log_debug("localchassis",
+                           "no change detected for port %s",
+                           hardware->h_ifname);
+               }
+       }
+}
+
 static void
 lldpd_cleanup(struct lldpd *cfg)
 {
@@ -723,66 +763,61 @@ lldpd_recv(struct lldpd *cfg, struct lldpd_hardware *hardware, int fd)
        free(buffer);
 }
 
-static void
-lldpd_send_all(struct lldpd *cfg)
+void
+lldpd_send(struct lldpd_hardware *hardware)
 {
-       struct lldpd_hardware *hardware;
+       struct lldpd *cfg = hardware->h_cfg;
        struct lldpd_port *port;
        int i, sent;
 
        if (cfg->g_config.c_receiveonly) return;
+       if ((hardware->h_flags & IFF_RUNNING) == 0)
+               return;
 
-       log_debug("send", "send PDU on all ports");
-       TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
-               /* Ignore if interface is down */
-               if ((hardware->h_flags & IFF_RUNNING) == 0)
+       log_debug("send", "send PDU on %s", hardware->h_ifname);
+       sent = 0;
+       for (i=0; cfg->g_protocols[i].mode != 0; i++) {
+               if (!cfg->g_protocols[i].enabled)
+                       continue;
+               /* We send only if we have at least one remote system
+                * speaking this protocol or if the protocol is forced */
+               if (cfg->g_protocols[i].enabled > 1) {
+                       cfg->g_protocols[i].send(cfg, hardware);
+                       sent++;
                        continue;
-
-               log_debug("send", "send PDU on %s", hardware->h_ifname);
-               sent = 0;
-               for (i=0; cfg->g_protocols[i].mode != 0; i++) {
-                       if (!cfg->g_protocols[i].enabled)
-                               continue;
-                       /* We send only if we have at least one remote system
-                        * speaking this protocol or if the protocol is forced */
-                       if (cfg->g_protocols[i].enabled > 1) {
-                               cfg->g_protocols[i].send(cfg, hardware);
-                               sent++;
-                               continue;
-                       }
-                       TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
-                               /* If this remote port is disabled, we don't
-                                * consider it */
-                               if (port->p_hidden_out)
-                                       continue;
-                               if (port->p_protocol ==
-                                   cfg->g_protocols[i].mode) {
-                                       log_debug("send", "send PDU on %s with protocol %s",
-                                           hardware->h_ifname,
-                                           cfg->g_protocols[i].name);
-                                       cfg->g_protocols[i].send(cfg,
-                                           hardware);
-                                       sent++;
-                                       break;
-                               }
-                       }
                }
-
-               if (!sent) {
-                       /* Nothing was sent for this port, let's speak the first
-                        * available protocol. */
-                       for (i = 0; cfg->g_protocols[i].mode != 0; i++) {
-                               if (!cfg->g_protocols[i].enabled) continue;
-                               log_debug("send", "fallback to protocol %s for %s",
-                                   cfg->g_protocols[i].name, hardware->h_ifname);
+               TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
+                       /* If this remote port is disabled, we don't
+                        * consider it */
+                       if (port->p_hidden_out)
+                               continue;
+                       if (port->p_protocol ==
+                           cfg->g_protocols[i].mode) {
+                               log_debug("send", "send PDU on %s with protocol %s",
+                                   hardware->h_ifname,
+                                   cfg->g_protocols[i].name);
                                cfg->g_protocols[i].send(cfg,
                                    hardware);
+                               sent++;
                                break;
                        }
-                       if (cfg->g_protocols[i].mode == 0)
-                               log_warnx("send", "no protocol enabled, dunno what to send");
                }
        }
+
+       if (!sent) {
+               /* Nothing was sent for this port, let's speak the first
+                * available protocol. */
+               for (i = 0; cfg->g_protocols[i].mode != 0; i++) {
+                       if (!cfg->g_protocols[i].enabled) continue;
+                       log_debug("send", "fallback to protocol %s for %s",
+                           cfg->g_protocols[i].name, hardware->h_ifname);
+                       cfg->g_protocols[i].send(cfg,
+                           hardware);
+                       break;
+               }
+               if (cfg->g_protocols[i].mode == 0)
+                       log_warnx("send", "no protocol enabled, dunno what to send");
+       }
 }
 
 #ifdef ENABLE_LLDPMED
@@ -911,6 +946,7 @@ lldpd_update_localports(struct lldpd *cfg)
 
        interfaces_update(cfg);
        lldpd_cleanup(cfg);
+       lldpd_reset_timer(cfg);
 }
 
 void
@@ -918,21 +954,17 @@ lldpd_loop(struct lldpd *cfg)
 {
        /* Main loop.
           1. Update local ports information
-          2. Clean unwanted (removed) local ports
-          3. Update local chassis information
-          4. Send packets
-          5. Update events
+          2. Update local chassis information
        */
        log_debug("loop", "start new loop");
        LOCAL_CHASSIS(cfg)->c_cap_enabled = 0;
-       if (cfg->g_iface_event == NULL) {
-               log_debug("loop", "update information for local ports");
-               lldpd_update_localports(cfg);
-       }
+       /* Information for local ports is triggered even when it is possible to
+        * update them on some other event because we want to refresh them if we
+        * missed something. */
+       log_debug("loop", "update information for local ports");
+       lldpd_update_localports(cfg);
        log_debug("loop", "update information for local chassis");
        lldpd_update_localchassis(cfg);
-       log_debug("loop", "send appropriate PDU on all interfaces");
-       lldpd_send_all(cfg);
 }
 
 static void
index d444b110018bf5c4bc4a86cdce07cf238fce223e..7935ea0eb0eb0dd83e23293d5ecf392179dd232a 100644 (file)
@@ -124,10 +124,13 @@ struct lldpd_hardware     *lldpd_alloc_hardware(struct lldpd *, char *, int);
 void    lldpd_hardware_cleanup(struct lldpd*, struct lldpd_hardware *);
 struct lldpd_mgmt *lldpd_alloc_mgmt(int family, void *addr, size_t addrsize, u_int32_t iface);
 void    lldpd_recv(struct lldpd *, struct lldpd_hardware *, int);
+void    lldpd_send(struct lldpd_hardware *);
 void    lldpd_loop(struct lldpd *);
 int     lldpd_main(int, char **);
 void    lldpd_update_localports(struct lldpd *);
 
+/* frame.c */
+u_int16_t frame_checksum(const u_int8_t *, int, int);
 
 /* event.c */
 void    levent_loop(struct lldpd *);
@@ -137,7 +140,7 @@ void         levent_hardware_release(struct lldpd_hardware *);
 void    levent_ctl_notify(char *, int, struct lldpd_port *);
 void    levent_send_now(struct lldpd *);
 void    levent_iface_subscribe(struct lldpd *, int);
-
+void    levent_schedule_pdu(struct lldpd_hardware *);
 
 /* lldp.c */
 int     lldp_send(PROTO_SEND_SIG);
index 1f65de933778aa2543ea53e8f8ef2320897910fd..8a1ec5d9066cd4ef34ba292dca9ca1b759f4cf0d 100644 (file)
@@ -222,13 +222,16 @@ struct lldpd_port {
        time_t                   p_lastupdate; /* Time of last update received */
        struct lldpd_frame      *p_lastframe;  /* Frame received during last update */
        u_int8_t                 p_protocol;   /* Protocol used to get this port */
+       u_int8_t                 p_hidden_in:1; /* Considered as hidden for reception */
+       u_int8_t                 p_hidden_out:2; /* Considered as hidden for emission */
+       /* Important: all fields that should be ignored to check if a port has
+        * been changed should be before p_id_subtype. Check
+        * `lldpd_reset_timer()`. */
        u_int8_t                 p_id_subtype;
        char                    *p_id;
        int                      p_id_len;
        char                    *p_descr;
        u_int16_t                p_mfs;
-       u_int8_t                 p_hidden_in:1; /* Considered as hidden for reception */
-       u_int8_t                 p_hidden_out:2; /* Considered as hidden for emission */
 
 #ifdef ENABLE_DOT3
        /* Dot3 stuff */
@@ -361,6 +364,7 @@ struct lldpd_hardware {
        int                      h_sendfd;  /* FD for sending, only used by h_ops */
        struct lldpd_ops        *h_ops;     /* Hardware-dependent functions */
        void                    *h_data;    /* Hardware-dependent data */
+       void                    *h_timer;   /* Timer for this port */
 
        int                      h_mtu;
        int                      h_flags; /* Packets will be sent only
@@ -379,6 +383,7 @@ struct lldpd_hardware {
        u_int64_t                h_insert_cnt;
        u_int64_t                h_delete_cnt;
 
+       u_int16_t                h_lport_cksum; /* Checksum on local port to see if there is a change */
        struct lldpd_port        h_lport;  /* Port attached to this hardware port */
        TAILQ_HEAD(, lldpd_port) h_rports; /* Remote ports */
 };