From 579bedd5494cdf7d8a232cdf992218055bb88eec Mon Sep 17 00:00:00 2001 From: Vincent Bernat Date: Mon, 31 Dec 2012 18:25:42 +0100 Subject: [PATCH] event: use a separate event timer for each port 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 | 50 ++++++++++++++- src/daemon/frame.h | 2 - src/daemon/lldpd.c | 144 +++++++++++++++++++++++++++----------------- src/daemon/lldpd.h | 5 +- src/lldpd-structs.h | 9 ++- 5 files changed, 147 insertions(+), 63 deletions(-) diff --git a/src/daemon/event.c b/src/daemon/event.c index ecec1d37..b39f4a3e 100644 --- a/src/daemon/event.c +++ b/src/daemon/event.c @@ -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; + } +} diff --git a/src/daemon/frame.h b/src/daemon/frame.h index 7bb1ea6f..17470f97 100644 --- a/src/daemon/frame.h +++ b/src/daemon/frame.h @@ -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 */ diff --git a/src/daemon/lldpd.c b/src/daemon/lldpd.c index ee200041..1c0d81e0 100644 --- a/src/daemon/lldpd.c +++ b/src/daemon/lldpd.c @@ -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 diff --git a/src/daemon/lldpd.h b/src/daemon/lldpd.h index d444b110..7935ea0e 100644 --- a/src/daemon/lldpd.h +++ b/src/daemon/lldpd.h @@ -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); diff --git a/src/lldpd-structs.h b/src/lldpd-structs.h index 1f65de93..8a1ec5d9 100644 --- a/src/lldpd-structs.h +++ b/src/lldpd-structs.h @@ -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 */ }; -- 2.39.5