From: Vincent Bernat Date: Sun, 4 Jan 2015 17:13:23 +0000 (+0100) Subject: lldp: when MSAP changes, send a shutdown LLPDU X-Git-Tag: 0.7.14~55 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=acb5f65b98b9ea66bd68bcd9a0e807f1e2e6b507;p=thirdparty%2Flldpd.git lldp: when MSAP changes, send a shutdown LLPDU Chassis ID and port ID are not supposed to change. However, it is possible for the port ID to change (for example, if the port name changes or if the MAC addresses change). Since they should be constant, we "fix" that by sending a shutdown LLPDU before any broadcasting any change. As a provision, this also applies to chassis ID even if it is currently not possible for it to be changed. --- diff --git a/src/daemon/lldp.c b/src/daemon/lldp.c index 3d0a988b..0e6220e2 100644 --- a/src/daemon/lldp.c +++ b/src/daemon/lldp.c @@ -52,9 +52,15 @@ lldpd_af_from_lldp_proto(int proto) } } -int -lldp_send(struct lldpd *global, - struct lldpd_hardware *hardware) +static int _lldp_send(struct lldpd *global, + struct lldpd_hardware *hardware, + u_int8_t c_id_subtype, + char *c_id, + int c_id_len, + u_int8_t p_id_subtype, + char *p_id, + int p_id_len, + int shutdown) { struct lldpd_port *port; struct lldpd_chassis *chassis; @@ -78,10 +84,6 @@ lldp_send(struct lldpd *global, int i; const u_int8_t med[] = LLDP_TLV_ORG_MED; #endif - - log_debug("lldp", "send LLDP PDU to %s", - hardware->h_ifname); - port = &hardware->h_lport; chassis = port->p_chassis; length = hardware->h_mtu; @@ -102,26 +104,29 @@ lldp_send(struct lldpd *global, /* Chassis ID */ if (!( POKE_START_LLDP_TLV(LLDP_TLV_CHASSIS_ID) && - POKE_UINT8(chassis->c_id_subtype) && - POKE_BYTES(chassis->c_id, chassis->c_id_len) && + POKE_UINT8(c_id_subtype) && + POKE_BYTES(c_id, c_id_len) && POKE_END_LLDP_TLV)) goto toobig; /* Port ID */ if (!( POKE_START_LLDP_TLV(LLDP_TLV_PORT_ID) && - POKE_UINT8(port->p_id_subtype) && - POKE_BYTES(port->p_id, port->p_id_len) && + POKE_UINT8(p_id_subtype) && + POKE_BYTES(p_id, p_id_len) && POKE_END_LLDP_TLV)) goto toobig; /* Time to live */ if (!( POKE_START_LLDP_TLV(LLDP_TLV_TTL) && - POKE_UINT16(chassis->c_ttl) && + POKE_UINT16(shutdown?0:chassis->c_ttl) && POKE_END_LLDP_TLV)) goto toobig; + if (shutdown) + goto end; + /* System name */ if (chassis->c_name && *chassis->c_name != '\0') { if (!( @@ -424,6 +429,7 @@ lldp_send(struct lldpd *global, } #endif +end: /* END */ if (!( POKE_START_LLDP_TLV(LLDP_TLV_END) && @@ -441,7 +447,7 @@ lldp_send(struct lldpd *global, hardware->h_tx_cnt++; /* We assume that LLDP frame is the reference */ - if ((frame = (struct lldpd_frame*)malloc( + if (!shutdown && (frame = (struct lldpd_frame*)malloc( sizeof(int) + pos - packet)) != NULL) { frame->size = pos - packet; memcpy(&frame->frame, packet, frame->size); @@ -450,10 +456,9 @@ lldp_send(struct lldpd *global, (memcmp(hardware->h_lport.p_lastframe->frame, frame->frame, frame->size) != 0)) { free(hardware->h_lport.p_lastframe); - hardware->h_lport.p_lastframe = frame; - hardware->h_lport.p_lastchange = time(NULL); - } else - free(frame); + hardware->h_lport.p_lastframe = frame; + hardware->h_lport.p_lastchange = time(NULL); + } else free(frame); } free(packet); @@ -464,6 +469,77 @@ toobig: return E2BIG; } +/* Send a shutdown LLDPDU. Should be called only if we have a previously sent + * LLDPDU. */ +static int +lldp_send_shutdown(struct lldpd *global, + struct lldpd_hardware *hardware) +{ + return _lldp_send(global, hardware, + hardware->h_lchassis_previous_id_subtype, + hardware->h_lchassis_previous_id, + hardware->h_lchassis_previous_id_len, + hardware->h_lport_previous_id_subtype, + hardware->h_lport_previous_id, + hardware->h_lport_previous_id_len, + 1); +} + +int +lldp_send(struct lldpd *global, + struct lldpd_hardware *hardware) +{ + struct lldpd_port *port = &hardware->h_lport; + struct lldpd_chassis *chassis = port->p_chassis; + int ret; + + /* Check if we have a change. */ + if (hardware->h_lchassis_previous_id != NULL && + hardware->h_lport_previous_id != NULL && + (hardware->h_lchassis_previous_id_subtype != chassis->c_id_subtype || + hardware->h_lchassis_previous_id_len != chassis->c_id_len || + hardware->h_lport_previous_id_subtype != port->p_id_subtype || + hardware->h_lport_previous_id_len != port->p_id_len || + memcmp(hardware->h_lchassis_previous_id, + chassis->c_id, chassis->c_id_len) || + memcmp(hardware->h_lport_previous_id, + port->p_id, port->p_id_len))) { + log_info("lldp", "MSAP has changed for port %s, sending a shutdown LLDPDU", + hardware->h_ifname); + if ((ret = lldp_send_shutdown(global, hardware)) != 0) + return ret; + } + + log_debug("lldp", "send LLDP PDU to %s", + hardware->h_ifname); + + if ((ret = _lldp_send(global, hardware, + chassis->c_id_subtype, + chassis->c_id, + chassis->c_id_len, + port->p_id_subtype, + port->p_id, + port->p_id_len, + 0)) != 0) + return ret; + + /* Record current chassis and port ID */ + free(hardware->h_lchassis_previous_id); + hardware->h_lchassis_previous_id_subtype = chassis->c_id_subtype; + hardware->h_lchassis_previous_id_len = chassis->c_id_len; + if ((hardware->h_lchassis_previous_id = malloc(chassis->c_id_len)) != NULL) + memcpy(hardware->h_lchassis_previous_id, chassis->c_id, + chassis->c_id_len); + free(hardware->h_lport_previous_id); + hardware->h_lport_previous_id_subtype = port->p_id_subtype; + hardware->h_lport_previous_id_len = port->p_id_len; + if ((hardware->h_lport_previous_id = malloc(port->p_id_len)) != NULL) + memcpy(hardware->h_lport_previous_id, port->p_id, + port->p_id_len); + + return 0; +} + #define CHECK_TLV_SIZE(x, name) \ do { if (tlv_size < (x)) { \ log_warnx("lldp", name " TLV too short received on %s", \ diff --git a/src/daemon/lldpd.c b/src/daemon/lldpd.c index c4b923df..8e97fe86 100644 --- a/src/daemon/lldpd.c +++ b/src/daemon/lldpd.c @@ -208,6 +208,8 @@ lldpd_hardware_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware) log_debug("alloc", "cleanup hardware port %s", hardware->h_ifname); free(hardware->h_lport_previous); + free(hardware->h_lchassis_previous_id); + free(hardware->h_lport_previous_id); lldpd_port_cleanup(&hardware->h_lport, 1); if (hardware->h_ops && hardware->h_ops->cleanup) hardware->h_ops->cleanup(cfg, hardware); diff --git a/src/lldpd-structs.h b/src/lldpd-structs.h index 44b2d9ff..c2b69d26 100644 --- a/src/lldpd-structs.h +++ b/src/lldpd-structs.h @@ -398,8 +398,23 @@ struct lldpd_hardware { u_int64_t h_delete_cnt; u_int64_t h_drop_cnt; - void *h_lport_previous; /* Backup of last value for localport */ + /* Previous values of different stuff. */ + /* Backup of the previous local port. Used to check if there was a + * change to send an immediate update. All those are not marshalled to + * the client. */ + void *h_lport_previous; ssize_t h_lport_previous_len; + /* Backup of the previous chassis ID. Used to check if there was a + * change and send an LLDP shutdown. */ + u_int8_t h_lchassis_previous_id_subtype; + char *h_lchassis_previous_id; + int h_lchassis_previous_id_len; + /* Backup of the previous port ID. Used to check if there was a change + * and send an LLDP shutdown. */ + u_int8_t h_lport_previous_id_subtype; + char *h_lport_previous_id; + int h_lport_previous_id_len; + struct lldpd_port h_lport; /* Port attached to this hardware port */ TAILQ_HEAD(, lldpd_port) h_rports; /* Remote ports */ @@ -415,6 +430,12 @@ MARSHAL_IGNORE(lldpd_hardware, h_data) MARSHAL_IGNORE(lldpd_hardware, h_cfg) MARSHAL_IGNORE(lldpd_hardware, h_lport_previous) MARSHAL_IGNORE(lldpd_hardware, h_lport_previous_len) +MARSHAL_IGNORE(lldpd_hardware, h_lchassis_previous_id_subtype) +MARSHAL_IGNORE(lldpd_hardware, h_lchassis_previous_id) +MARSHAL_IGNORE(lldpd_hardware, h_lchassis_previous_id_len) +MARSHAL_IGNORE(lldpd_hardware, h_lport_previous_id_subtype) +MARSHAL_IGNORE(lldpd_hardware, h_lport_previous_id) +MARSHAL_IGNORE(lldpd_hardware, h_lport_previous_id_len) MARSHAL_SUBSTRUCT(lldpd_hardware, lldpd_port, h_lport) MARSHAL_SUBTQ(lldpd_hardware, lldpd_port, h_rports) MARSHAL_END(lldpd_hardware);