]> git.ipfire.org Git - thirdparty/lldpd.git/commitdiff
Add smart mode support.
authorVincent Bernat <bernat@luffy.cx>
Wed, 9 Jun 2010 16:13:43 +0000 (18:13 +0200)
committerVincent Bernat <bernat@luffy.cx>
Thu, 10 Jun 2010 07:39:28 +0000 (09:39 +0200)
This features was removed in previous versions to allow to have
several neighbors on one port. It now uses a different approach. Each
port can still have several neighbors but some of them will be hidden.

CHANGELOG
man/lldpd.8
src/agent.c
src/client.c
src/lldpd.c
src/lldpd.h

index 09294bec155cbc950738924fb963a20fed859f4c..c92a9dc9e67ad09532da05d5206d40055067d0f6 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -2,6 +2,9 @@ lldpd (0.5.1)
   * Features:
     + Allow to force a protocol even when no peer for this protocol is
       detected.
+    + Add a smart mode that allows to discard bogus port information,
+      for example CDP packets that are flooded through a switch that
+      does not support CDP.
     + Allow to set LLDP-MED network policy from lldpctl, thanks to a patch from
       Philipp Kempgen.
     + Allow to set LLDP-MED POE-MDI from lldpctl.
index e3e656a5300fd13666d2fe07cf5e0265d8f54dc5..bb57473e6c6d6f3da0726b1f92e2084255232b0d 100644 (file)
@@ -26,6 +26,7 @@
 .Op Fl X Ar socket
 .Op Fl m Ar management
 .Op Fl M Ar class
+.Op Fl H Ar hide
 .Sh DESCRIPTION
 .Nm
 is a daemon able to receive and send
@@ -130,6 +131,52 @@ Disable LLDP-MED inventory TLV transmission.
 will still receive (and publish using SNMP if enabled) those LLDP-MED
 TLV but will not send them. Use this option if you don't want to
 transmit sensible information like serial numbers.
+.It Fl H Ar hide
+In a heterogeneous network, you may see several different hosts on the
+same port, even if there is only one physically plugged to this
+port. For example, if you have a Nortel switch running LLDP which is
+plugged to a Cisco switch running CDP and your host is plugged to the
+Cisco switch, you will see the Nortel switch as well because LLDP
+frames are forwarded by the Cisco switch. This may not be what you
+want. The
+.Fl H Ar hide
+parameter will allow you to tell
+.Nm
+to discard some frames that it receives and to avoid to send some
+other frames. The rationale behind the possible modes is that we
+should guess which protocol the equipment we are linked with is
+speaking. Moreover, it can speak several protocols. The main idea used
+is that if we receive on one port one CDP frame and three LLDP frames,
+we assume that the equipment is speaking CDP and that LLDP frames are
+just flooded through this equipment. The possible values are:
+.Bl -tag -width "XX"
+.It Sy 0
+Do not be smart, do not filter any frame
+.It Sy 1
+For each port, get the protocol with less neighbors and use only
+this protocol for reception and sending; in case of a tie, LLDP
+protocol wins. This is the default mode.
+.It Sy 2
+For each port, get the protocol with less neighbors and use only
+this protocol for reception; in case of a tie, LLDP protocol wins. No
+frame is filtered when sending.
+.It Sy 3
+For each port, get the protocol with less neighbors and use only this
+protocol for sending; in case of a tie, LLDP protocol wins. No frame
+is filtered on reception.
+.It Sy 4
+Same as 1 but in case of a tie, both protocols win.
+.It Sy 5
+Same as 2 but in case of a tie, both protocols win.
+.It Sy 6
+Same as 3 but in case of a tie, both protocols win.
+.It Sy 7
+Same as 1 but only one neighbor is kept.
+.It Sy 8
+Same as 2 but only one neighbor is kept.
+.It Sy 9
+Same as 3 but only one neighbor is kept.
+.El
 .El
 .Sh FILES
 .Bl -tag -width "/var/run/lldpd.socketXX" -compact
index 0a82e3f13b5ce9cbabeef0a09e47da626d7c5536..17c16d723f80ec0641582e89a4b8a2c83cf422ab 100644 (file)
@@ -185,6 +185,7 @@ header_tprindexed_table(struct variable *vp, oid *name, size_t *length,
         target_len = *length - vp->namelen;
        TAILQ_FOREACH(hardware, &scfg->g_hardware, h_entries) {
                TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
+                       if (SMART_HIDDEN(scfg, port)) continue;
                         if ((variant == TPR_VARIANT_IP) &&
                             (port->p_chassis->c_mgmt.s_addr == INADDR_ANY))
                                 continue;
@@ -322,6 +323,7 @@ header_tprvindexed_table(struct variable *vp, oid *name, size_t *length,
         target_len = *length - vp->namelen;
        TAILQ_FOREACH(hardware, &scfg->g_hardware, h_entries) {
                TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
+                       if (SMART_HIDDEN(scfg, port)) continue;
                         TAILQ_FOREACH(vlan, &port->p_vlans, v_entries) {
                                if (port->p_lastchange > starttime.tv_sec)
                                        current[0] =
@@ -501,9 +503,11 @@ agent_h_scalars(struct variable *vp, oid *name, size_t *length,
        case LLDP_SNMP_LASTUPDATE:
                long_ret = 0;
                TAILQ_FOREACH(hardware, &scfg->g_hardware, h_entries)
-                   TAILQ_FOREACH(port, &hardware->h_rports, p_entries)
+                   TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
+                       if (SMART_HIDDEN(scfg, port)) continue;
                        if (port->p_lastchange > long_ret)
                                long_ret = port->p_lastchange;
+               }
                if (long_ret)
                        long_ret = (long_ret - starttime.tv_sec) * 100;
                return (u_char *)&long_ret;
index 3f39feb8039a65b10da8ecf465a11dd5392a6ba9..93a35168f869c5e055e64d497d336ad09d490815 100644 (file)
@@ -228,7 +228,10 @@ client_handle_port_related(struct lldpd *cfg, struct hmsg *r, struct hmsg *s)
                        case HMSG_GET_NB_PORTS:
                                p = &s->data;
                                i = 0;
-                               TAILQ_FOREACH(port, &hardware->h_rports, p_entries) i++;
+                               TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
+                                       if (SMART_HIDDEN(cfg, port)) continue;
+                                       i++;
+                               }
                                memcpy(p, &i, sizeof(int));
                                s->hdr.len = sizeof(int);
                                break;
@@ -245,8 +248,10 @@ client_handle_port_related(struct lldpd *cfg, struct hmsg *r, struct hmsg *s)
                                p = (char*)&r->data + IFNAMSIZ;
                                memcpy(&i, p, sizeof(int));
                                p = &s->data;
-                               TAILQ_FOREACH(port, &hardware->h_rports, p_entries)
-                                   if (i-- == 0) break;
+                               TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
+                                       if (SMART_HIDDEN(cfg, port)) continue;
+                                       if (i-- == 0) break;
+                               }
                                if (!port) {
                                        LLOG_INFO("out of range index requested for port "
                                            "related information on interface %s for %d",
index 4cb9abf9a5add53c027160d6f163740d21186c48..ea98f1876479c35f4c53ee54400406167a46f3f5 100644 (file)
@@ -81,6 +81,7 @@ static void            lldpd_shutdown(int);
 static void             lldpd_exit(void);
 static void             lldpd_send_all(struct lldpd *);
 static void             lldpd_recv_all(struct lldpd *);
+static void             lldpd_hide_all(struct lldpd *);
 static int              lldpd_guess_type(struct lldpd *, char *, int);
 static void             lldpd_decode(struct lldpd *, char *, int,
                            struct lldpd_hardware *);
@@ -110,6 +111,7 @@ usage(void)
        fprintf(stderr, "-k       Disable advertising of kernel release, version, machine.\n");
        fprintf(stderr, "-S descr Override the default system description.\n");
        fprintf(stderr, "-m IP    Specify the management address of this system.\n");
+       fprintf(stderr, "-H mode  Specify the behaviour when detecting multiple neighbors.\n");
 #ifdef ENABLE_LLDPMED
        fprintf(stderr, "-M class Enable emission of LLDP-MED frame. 'class' should be one of:\n");
        fprintf(stderr, "             1 Generic Endpoint (Class I)\n");
@@ -449,8 +451,9 @@ lldpd_decode(struct lldpd *cfg, char *frame, int s,
           freed with lldpd_port_cleanup() and therefore, the refcount
           of the chassis that was attached to it is decreased.
        */
-       i = 0; TAILQ_FOREACH(oport, &hardware->h_rports, p_entries) i++;
-       LLOG_DEBUG("Currently, %s known %d neighbors",
+       i = 0; TAILQ_FOREACH(oport, &hardware->h_rports, p_entries)
+               i++;
+       LLOG_DEBUG("Currently, %s knows %d neighbors",
            hardware->h_ifname, i);
        return;
 }
@@ -579,6 +582,76 @@ lldpd_callback_del(struct lldpd *cfg, int fd, void(*fn)(CALLBACK_SIG))
        }
 }
 
+/* Hide unwanted ports depending on smart mode set by the user */
+static void
+lldpd_hide_all(struct lldpd *cfg)
+{
+       struct lldpd_hardware *hardware;
+       struct lldpd_port *port;
+       int protocols[LLDPD_MODE_MAX+1];
+       int i, j, found;
+       unsigned int min;
+
+       TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
+               /* Compute the number of occurrences of each protocol */
+               for (i = 0; i <= LLDPD_MODE_MAX; i++)
+                       protocols[i] = 0;
+               TAILQ_FOREACH(port, &hardware->h_rports, p_entries)
+                       protocols[port->p_protocol]++;
+
+               /* Turn the protocols[] array into an array of
+                  enabled/disabled protocols. 1 means enabled, 0
+                  means disabled. */
+               min = (unsigned int)-1;
+               for (i = 0; i <= LLDPD_MODE_MAX; i++)
+                       if (protocols[i] && (protocols[i] < min))
+                               min = protocols[i];
+               found = 0;
+               for (i = 0; i <= LLDPD_MODE_MAX; i++)
+                       if ((protocols[i] == min) && !found) {
+                               /* If we need a tie breaker, we take
+                                  the first protocol only */
+                               if (cfg->g_smart & SMART_FILTER_NO_TIE)
+                                       found = 1;
+                               protocols[i] = 1;
+                       } else protocols[i] = 0;
+
+               /* We set the p_hidden flag to 1 if the protocol is disabled */
+               TAILQ_FOREACH(port, &hardware->h_rports, p_entries)
+                       port->p_hidden = protocols[port->p_protocol]?0:1;
+
+               /* If we want only one neighbor, we take the first one */
+               if (cfg->g_smart & SMART_FILTER_ONE_NEIGH) {
+                       found = 0;
+                       TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
+                               if (!port->p_hidden) {
+                                       if (found)
+                                               port->p_hidden = 1;
+                                       else
+                                               found = 1;
+                               }
+                       }
+               }
+
+               /* Print a debug message summarizing the operation */
+               i = j = 0;
+               TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
+                   if (port->p_hidden) i++;
+                   j++;
+               }
+               if (i) {
+                       LLOG_DEBUG("On %s, out of %d neighbors, %d are hidden",
+                           hardware->h_ifname, j, i);
+                       for (i=0; protos[i].mode != 0; i++) {
+                               if (protos[i].enabled)
+                                       LLOG_DEBUG("On %s, %s is %s",
+                                           hardware->h_ifname, protos[i].name,
+                                           protocols[protos[i].mode]?"enabled":"disabled");
+                       }
+               }
+       }
+}
+
 static void
 lldpd_recv_all(struct lldpd *cfg)
 {
@@ -715,6 +788,11 @@ lldpd_send_all(struct lldpd *cfg)
                                continue;
                        }
                        TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
+                               /* If this remote port is disabled, we don't
+                                * consider it */
+                               if (port->p_hidden &&
+                                   (cfg->g_smart & SMART_FILTER_EMISSION))
+                                       continue;
                                if (port->p_protocol ==
                                    cfg->g_protocols[i].mode) {
                                        cfg->g_protocols[i].send(cfg,
@@ -867,6 +945,7 @@ lldpd_loop(struct lldpd *cfg)
           3. Update local chassis information
           4. Send packets
           5. Receive packets
+          6. Update smart mode
        */
        LOCAL_CHASSIS(cfg)->c_cap_enabled = 0;
        lldpd_update_localports(cfg);
@@ -874,6 +953,8 @@ lldpd_loop(struct lldpd *cfg)
        lldpd_update_localchassis(cfg);
        lldpd_send_all(cfg);
        lldpd_recv_all(cfg);
+       if (cfg->g_smart != SMART_NOFILTER)
+               lldpd_hide_all(cfg);
 }
 
 static void
@@ -915,13 +996,14 @@ lldpd_main(int argc, char *argv[])
 #endif
        char *mgmtp = NULL;
        char *popt, opts[] = 
-               "hkdxX:m:p:M:S:i@                    ";
+               "H:hkdxX:m:p:M:S:i@                    ";
        int i, found, advertise_version = 1;
 #ifdef ENABLE_LLDPMED
        int lldpmed = 0, noinventory = 0;
 #endif
         char *descr_override = NULL;
        char *lsb_release = NULL;
+       int smart = SMART_FILTER_NO_TIE | SMART_FILTER_EMISSION | SMART_FILTER_RECEPTION;
 
        saved_argv = argv;
 
@@ -983,6 +1065,23 @@ lldpd_main(int argc, char *argv[])
                 case 'S':
                         descr_override = strdup(optarg);
                         break;
+               case 'H':
+                       smart = SMART_NOFILTER;
+                       i = atoi(optarg);
+                       if (i == 0) break;
+                       if ((i < 0) || (i > 9)) {
+                               fprintf(stderr, "Incorrect mode for -H\n");
+                               usage();
+                       }
+                       if (i%3 != 0)
+                               smart |= SMART_FILTER_RECEPTION;
+                       if ((i + 1)%3 != 0)
+                               smart |= SMART_FILTER_EMISSION;
+                       if (i > 6)
+                               smart |= SMART_FILTER_ONE_NEIGH | SMART_FILTER_NO_TIE;
+                       if (i < 4)
+                               smart |= SMART_FILTER_NO_TIE;
+                       break;
                default:
                        found = 0;
                        for (i=0; protos[i].mode != 0; i++) {
@@ -1029,6 +1128,7 @@ lldpd_main(int argc, char *argv[])
                fatal(NULL);
 
        cfg->g_mgmt_pattern = mgmtp;
+       cfg->g_smart = smart;
 
        /* Get ioctl socket */
        if ((cfg->g_sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
index 90fa9855dda99bdf098788f47f70434b2d04aa59..1de74a09cbced1abbcacdeec8e2173a85ed928a1 100644 (file)
@@ -151,6 +151,8 @@ struct lldpd_port {
        int                      p_id_len;
        char                    *p_descr;
        u_int16_t                p_mfs;
+       u_int8_t                 p_hidden; /* Hidden, this port information should
+                                             be discarded if set to 1 */
 
 #ifdef ENABLE_DOT3
 #define STRUCT_LLDPD_PORT_DOT3 "lbbww"
@@ -195,7 +197,7 @@ struct lldpd_port {
 #endif
 };
 
-#define STRUCT_LLDPD_PORT "(LPttPbbCsw                               \
+#define STRUCT_LLDPD_PORT "(LPttPbbCswb"                               \
        STRUCT_LLDPD_PORT_DOT3                                  \
        STRUCT_LLDPD_PORT_MED                                   \
        STRUCT_LLDPD_PORT_DOT1 ")"
@@ -265,6 +267,7 @@ struct protocol {
 #define LLDPD_MODE_SONMP 4
 #define LLDPD_MODE_EDP 5
 #define LLDPD_MODE_FDP 6
+#define LLDPD_MODE_MAX LLDPD_MODE_FDP
        int              mode;          /* > 0 mode identifier (unique per protocol) */
        int              enabled;       /* Is this protocol enabled? */
        char            *name;          /* Name of protocol */
@@ -275,6 +278,15 @@ struct protocol {
        u_int8_t         mac[ETH_ALEN];  /* Destination MAC address used by this protocol */
 };
 
+/* Smart mode / Hide mode */
+#define SMART_NOFILTER         0
+#define SMART_FILTER_RECEPTION (1<<0) /* Filter received frames */
+#define SMART_FILTER_EMISSION  (1<<1) /* Filter frames to be sent */
+#define SMART_FILTER_ONE_NEIGH (1<<2) /* Only allow one neighbor */
+#define SMART_FILTER_NO_TIE    (1<<3) /* Only allow one protocol */
+#define SMART_HIDDEN(cfg, port) ((cfg->g_smart & SMART_FILTER_RECEPTION) && port->p_hidden)
+
+
 #define CALLBACK_SIG struct lldpd*, struct lldpd_callback*
 struct lldpd_callback {
        TAILQ_ENTRY(lldpd_callback) next;
@@ -290,6 +302,7 @@ struct lldpd {
        struct protocol         *g_protocols;
        time_t                   g_lastsent;
        int                      g_lastrid;
+       int                      g_smart;
 #ifdef USE_SNMP
        int                      g_snmp;
 #endif /* USE_SNMP */