]> git.ipfire.org Git - thirdparty/lldpd.git/commitdiff
lldp: when MSAP changes, send a shutdown LLPDU
authorVincent Bernat <vincent@bernat.im>
Sun, 4 Jan 2015 17:13:23 +0000 (18:13 +0100)
committerVincent Bernat <vincent@bernat.im>
Sun, 4 Jan 2015 17:13:23 +0000 (18:13 +0100)
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.

src/daemon/lldp.c
src/daemon/lldpd.c
src/lldpd-structs.h

index 3d0a988b9fe55d8bfeac7a340e07aaa04fec1795..0e6220e22f609de1e711cf26995ece2af5fa383d 100644 (file)
@@ -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", \
index c4b923df62d0b7939d19c3b28d6e178e8a037760..8e97fe86b204dd78badd3f63a1865e71d90b3b36 100644 (file)
@@ -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);
index 44b2d9ff633eae9e300e0641526c17dc8c07b55c..c2b69d26db30af2ac6639a2c3b8d90347d3ba6ac 100644 (file)
@@ -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);