]> git.ipfire.org Git - thirdparty/lldpd.git/commitdiff
More flexible smart mode and new default.
authorVincent Bernat <bernat@luffy.cx>
Tue, 24 Aug 2010 17:39:06 +0000 (19:39 +0200)
committerVincent Bernat <bernat@luffy.cx>
Tue, 24 Aug 2010 20:13:26 +0000 (22:13 +0200)
Allow any combination of filtering, one neighbor and one protocol,
both for incoming frames and outgoing frames.

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

index c92a9dc9e67ad09532da05d5206d40055067d0f6..bc30ad894765cb2e9ab60895ed670d2e5a3e5a41 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,7 @@
+lldpd (0.5.2)
+  * Features:
+    + More flexible smart mode and new default. Manual page has been updated.
+
 lldpd (0.5.1)
   * Features:
     + Allow to force a protocol even when no peer for this protocol is
index bb57473e6c6d6f3da0726b1f92e2084255232b0d..ae3d8d246b41fd734d9dd237f7c07688e855bcc3 100644 (file)
@@ -132,6 +132,11 @@ 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
+Filter neighbors. See section
+.Sx FILTERING NEIGHBORS
+for details.
+.El
+.Sh FILTERING NEIGHBORS
 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
@@ -143,40 +148,92 @@ want. The
 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
+other frames.
+.Pp
+Incoming filtering and outgoing filtering are
+unrelated. Incoming filtering will hide some remote ports to get you a
+chance to know exactly what equipment is on the other side of the
+network cable. Outgoing filtering will avoid to use some protocols to
+avoid flooding your network with a protocol that is not handled by the
+nearest equipment. Keep in mind that even without filtering,
+.Nm
+will speak protocols for which at least one frame has been received
+and LLDP otherwise (there are other options to change this behaviour,
+for example
+.Fl cc , ss , ee , ll
+and
+.Fl ff
+).
+.Pp
+When enabling incoming filtering,
+.Nm
+will try to select one protocol and filter out neighbors using other
+protocols. To select this protocol, the rule is to take the less used
+protocol. If on one port, you get 12 CDP neighbors and 1 LLDP
+neighbor, this mean that the remote switch speaks LLDP and does not
+filter CDP. Therefore, we select LLDP. When enabling outgoing
+filtering,
+.Nm
+will also try to select one protocol and only speaks this
+protocol. The filtering is done per port. Each port may select a
+different protocol.
+.Pp
+There are two additional criteria when enabling filtering: allowing
+one or several protocols to be selected (in case of a tie) and
+allowing one or several neighbors to be selected. Even when allowing
+several protocols, the rule of selecting the protocols with the less
+neighbors still apply. If
+.Nm
+selects LLDP and CDP, this means they have the same number of
+neighbors. The selection of the neighbor is random. Incoming filtering
+will select a set of neighbors to be displayed while outgoing
+filtering will use the selected set of neighbors to decide which
+protocols to use: if a selected neighbor speaks LLDP and another one
+CDP,
+.Nm
+will speak both CDP and LLDP on this port.
+.Pp
+There are some corner cases. A typical example is a switch speaking
+two protocols (CDP and LLDP for example). You want to get the
+information from the best protocol but you want to speak both
+protocols because some tools use the CDP table and some other the LLDP
+table.
+.Pp
+The table below summarize all accepted values for the
+.Fl H Ar hide
+parameter. The default value is
+.Em 15
+which corresponds to the corner case described above. The
+.Em filter
+column means that filtering is enabled. The
+.Em 1proto
+column tells that only one protocol will be kept. The
+.Em 1neigh
+column tells that only one neighbor will be kept.
+.Pp
+.Bl -column -compact -offset indent "HXXX" "filterX" "1protoX" "1neighX" "filterX" "1protoX" "1neighX"
+.It Ta Ta incoming Ta Ta outgoing Ta
+.It Ta Em filter Ta Em 1proto Ta Em 1neigh Ta Em filter Ta Em 1proto Ta Em 1neigh
+.It Em 0  Ta   Ta   Ta   Ta   Ta   Ta  
+.It Em 1  Ta x Ta x Ta   Ta x Ta x Ta  
+.It Em 2  Ta x Ta x Ta   Ta   Ta   Ta  
+.It Em 3  Ta   Ta   Ta   Ta x Ta x Ta  
+.It Em 4  Ta x Ta   Ta   Ta x Ta   Ta  
+.It Em 5  Ta x Ta   Ta   Ta   Ta   Ta  
+.It Em 6  Ta   Ta   Ta   Ta x Ta   Ta  
+.It Em 7  Ta x Ta x Ta x Ta x Ta x Ta  
+.It Em 8  Ta x Ta x Ta x Ta   Ta   Ta  
+.It Em 9  Ta x Ta   Ta x Ta x Ta x Ta  
+.It Em 10 Ta   Ta   Ta   Ta x Ta   Ta x
+.It Em 11 Ta x Ta   Ta x Ta   Ta   Ta  
+.It Em 12 Ta x Ta   Ta x Ta x Ta   Ta x
+.It Em 13 Ta x Ta   Ta x Ta x Ta   Ta  
+.It Em 14 Ta x Ta x Ta   Ta x Ta   Ta x
+.It Em 15 Ta x Ta x Ta   Ta x Ta   Ta  
+.It Em 16 Ta x Ta x Ta x Ta x Ta   Ta x
+.It Em 17 Ta x Ta x Ta x Ta x Ta   Ta  
+.It Em 18 Ta x Ta   Ta   Ta x Ta   Ta x
+.It Em 19 Ta x Ta   Ta   Ta x Ta x Ta  
 .El
 .Sh FILES
 .Bl -tag -width "/var/run/lldpd.socketXX" -compact
index e11ea57d3b1868aab7d1055dfc4d36a3ea0ed65d..320a3fa4776fe192559dd20291d0a76d30692a6c 100644 (file)
@@ -82,6 +82,7 @@ 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 void             lldpd_hide_ports(struct lldpd *, struct lldpd_hardware *, int);
 static int              lldpd_guess_type(struct lldpd *, char *, int);
 static void             lldpd_decode(struct lldpd *, char *, int,
                            struct lldpd_hardware *);
@@ -587,69 +588,102 @@ static void
 lldpd_hide_all(struct lldpd *cfg)
 {
        struct lldpd_hardware *hardware;
+
+       if (!cfg->g_smart)
+               return;
+       TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
+               if (cfg->g_smart & SMART_INCOMING_FILTER)
+                       lldpd_hide_ports(cfg, hardware, SMART_INCOMING);
+               if (cfg->g_smart & SMART_OUTGOING_FILTER)
+                       lldpd_hide_ports(cfg, hardware, SMART_OUTGOING);
+       }
+}
+
+static void
+lldpd_hide_ports(struct lldpd *cfg, struct lldpd_hardware *hardware, int mask) {
        struct lldpd_port *port;
        int protocols[LLDPD_MODE_MAX+1];
-       int i, j, found;
+       char buffer[256];
+       int i, j, k, 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];
+       /* 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 & mask &
+                           (SMART_OUTGOING_ONE_PROTO | SMART_INCOMING_ONE_PROTO))
+                               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) {
+               if (mask == SMART_OUTGOING)
+                       port->p_hidden_out = protocols[port->p_protocol]?0:1;
+               else
+                       port->p_hidden_in = protocols[port->p_protocol]?0:1;
+       }
+
+       /* If we want only one neighbor, we take the first one */
+       if (cfg->g_smart & mask &
+           (SMART_OUTGOING_ONE_NEIGH | SMART_INCOMING_ONE_NEIGH)) {
                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)
+               TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
+                       if (mask == SMART_OUTGOING) {
+                               if (found) port->p_hidden_out = 1;
+                               if (!port->p_hidden_out)
+                                       found = 1;
+                       }
+                       if (mask == SMART_INCOMING) {
+                               if (found) port->p_hidden_in = 1;
+                               if (!port->p_hidden_in)
                                        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++;
+       /* Print a debug message summarizing the operation */
+       for (i = 0; i <= LLDPD_MODE_MAX; i++) protocols[i] = 0;
+       k = j = 0;
+       TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
+               if (!(((mask == SMART_OUTGOING) && port->p_hidden_out) ||
+                     ((mask == SMART_INCOMING) && port->p_hidden_in))) {
+                       k++;
+                       protocols[port->p_protocol] = 1;
                }
-               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");
+               j++;
+       }
+       buffer[0] = '\0';
+       for (i=0; cfg->g_protocols[i].mode != 0; i++) {
+               if (cfg->g_protocols[i].enabled && protocols[cfg->g_protocols[i].mode]) {
+                       if (strlen(buffer) +
+                           strlen(cfg->g_protocols[i].name) + 3 > sizeof(buffer)) {
+                               /* Unlikely, our buffer is too small */
+                               memcpy(buffer + sizeof(buffer) - 4, "...", 4);
+                               break;
                        }
+                       if (buffer[0])
+                               strcat(buffer, ", ");
+                       strcat(buffer, cfg->g_protocols[i].name);
                }
        }
+       LLOG_DEBUG("[%s] %s: %d visible neigh / %d. Protocols: %s.",
+                  (mask == SMART_OUTGOING)?"out filter":"in filter",
+                  hardware->h_ifname, k, j, buffer[0]?buffer:"(none)");
 }
 
 static void
@@ -790,8 +824,8 @@ lldpd_send_all(struct lldpd *cfg)
                        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))
+                               if (port->p_hidden_out &&
+                                   (cfg->g_smart & SMART_OUTGOING_FILTER))
                                        continue;
                                if (port->p_protocol ==
                                    cfg->g_protocols[i].mode) {
@@ -954,8 +988,7 @@ 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);
+       lldpd_hide_all(cfg);
 }
 
 static void
@@ -985,6 +1018,42 @@ lldpd_exit()
 #endif /* USE_SNMP */
 }
 
+struct intint { int a; int b; };
+static const struct intint filters[] = {
+       {  0, 0 },
+       {  1, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO |
+             SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO },
+       {  2, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO },
+       {  3, SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO },
+       {  4, SMART_INCOMING_FILTER | SMART_OUTGOING_FILTER },
+       {  5, SMART_INCOMING_FILTER },
+       {  6, SMART_OUTGOING_FILTER },
+       {  7, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | SMART_INCOMING_ONE_NEIGH |
+             SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO },
+       {  8, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | SMART_INCOMING_ONE_NEIGH },
+       {  9, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_NEIGH |
+             SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO },
+       { 10, SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH },
+       { 11, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_NEIGH },
+       { 12, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_NEIGH |
+             SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH },
+       { 13, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_NEIGH |
+             SMART_OUTGOING_FILTER },
+       { 14, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO |
+             SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH },
+       { 15, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO |
+             SMART_OUTGOING_FILTER },
+       { 16, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | SMART_INCOMING_ONE_NEIGH |
+             SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH },
+       { 17, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | SMART_INCOMING_ONE_NEIGH |
+             SMART_OUTGOING_FILTER },
+       { 18, SMART_INCOMING_FILTER |
+             SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH },
+       { 19, SMART_INCOMING_FILTER |
+             SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO },
+       { -1, 0 }
+};
+
 int
 lldpd_main(int argc, char *argv[])
 {
@@ -1004,7 +1073,7 @@ lldpd_main(int argc, char *argv[])
 #endif
         char *descr_override = NULL;
        char *lsb_release = NULL;
-       int smart = SMART_FILTER_NO_TIE | SMART_FILTER_EMISSION | SMART_FILTER_RECEPTION;
+       int smart = 15;
 
        saved_argv = argv;
 
@@ -1067,21 +1136,7 @@ lldpd_main(int argc, char *argv[])
                         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;
+                       smart = atoi(optarg);
                        break;
                default:
                        found = 0;
@@ -1100,6 +1155,14 @@ lldpd_main(int argc, char *argv[])
                                usage();
                }
        }
+
+       /* Set correct smart mode */
+       for (i=0; (filters[i].a != -1) && (filters[i].a != smart); i++);
+       if (filters[i].a == -1) {
+               fprintf(stderr, "Incorrect mode for -H\n");
+               usage();
+       }
+       smart = filters[i].b;
        
        log_init(debug, __progname);
        tzset();                /* Get timezone info before chroot */
index 1de74a09cbced1abbcacdeec8e2173a85ed928a1..0a58b401426b53c24801f270450fbf903388a549 100644 (file)
@@ -151,8 +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 */
+       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
 #define STRUCT_LLDPD_PORT_DOT3 "lbbww"
@@ -279,12 +279,19 @@ struct 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 SMART_INCOMING_FILTER          (1<<0) /* Incoming filtering enabled */
+#define SMART_INCOMING_ONE_PROTO       (1<<1) /* On reception, keep only one proto */
+#define SMART_INCOMING_ONE_NEIGH       (1<<2) /* On reception, keep only one neighbor */
+#define SMART_OUTGOING_FILTER          (1<<3) /* Outgoing filtering enabled */
+#define SMART_OUTGOING_ONE_PROTO       (1<<4) /* On emission, keep only one proto */
+#define SMART_OUTGOING_ONE_NEIGH       (1<<5) /* On emission, consider only one neighbor */
+#define SMART_INCOMING (SMART_INCOMING_FILTER |    \
+                        SMART_INCOMING_ONE_PROTO | \
+                        SMART_INCOMING_ONE_NEIGH)
+#define SMART_OUTGOING (SMART_OUTGOING_FILTER |                \
+                       SMART_OUTGOING_ONE_PROTO |      \
+                       SMART_OUTGOING_ONE_NEIGH)
+#define SMART_HIDDEN(cfg, port) ((cfg->g_smart & SMART_INCOMING_FILTER) && port->p_hidden_in)
 
 
 #define CALLBACK_SIG struct lldpd*, struct lldpd_callback*