From: Vincent Bernat Date: Tue, 24 Aug 2010 17:39:06 +0000 (+0200) Subject: More flexible smart mode and new default. X-Git-Tag: 0.5.2~6 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=8482abe91c08cb7a93a6d9b66e8746511dc16260;p=thirdparty%2Flldpd.git More flexible smart mode and new default. Allow any combination of filtering, one neighbor and one protocol, both for incoming frames and outgoing frames. --- diff --git a/CHANGELOG b/CHANGELOG index c92a9dc9..bc30ad89 100644 --- 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 diff --git a/man/lldpd.8 b/man/lldpd.8 index bb57473e..ae3d8d24 100644 --- a/man/lldpd.8 +++ b/man/lldpd.8 @@ -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 diff --git a/src/lldpd.c b/src/lldpd.c index e11ea57d..320a3fa4 100644 --- a/src/lldpd.c +++ b/src/lldpd.c @@ -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 */ diff --git a/src/lldpd.h b/src/lldpd.h index 1de74a09..0a58b401 100644 --- a/src/lldpd.h +++ b/src/lldpd.h @@ -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*