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
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
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 *);
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
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) {
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
#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[])
{
#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;
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;
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 */