From 42b39485ea6161d69cb642bda47c7a0f62edbe87 Mon Sep 17 00:00:00 2001 From: Vincent Bernat Date: Wed, 9 Jun 2010 18:13:43 +0200 Subject: [PATCH] Add smart mode support. 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 | 3 ++ man/lldpd.8 | 47 +++++++++++++++++++++++ src/agent.c | 6 ++- src/client.c | 11 ++++-- src/lldpd.c | 106 +++++++++++++++++++++++++++++++++++++++++++++++++-- src/lldpd.h | 15 +++++++- 6 files changed, 180 insertions(+), 8 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 09294bec..c92a9dc9 100644 --- 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. diff --git a/man/lldpd.8 b/man/lldpd.8 index e3e656a5..bb57473e 100644 --- a/man/lldpd.8 +++ b/man/lldpd.8 @@ -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 diff --git a/src/agent.c b/src/agent.c index 0a82e3f1..17c16d72 100644 --- a/src/agent.c +++ b/src/agent.c @@ -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; diff --git a/src/client.c b/src/client.c index 3f39feb8..93a35168 100644 --- a/src/client.c +++ b/src/client.c @@ -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", diff --git a/src/lldpd.c b/src/lldpd.c index 4cb9abf9..ea98f187 100644 --- a/src/lldpd.c +++ b/src/lldpd.c @@ -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) diff --git a/src/lldpd.h b/src/lldpd.h index 90fa9855..1de74a09 100644 --- a/src/lldpd.h +++ b/src/lldpd.h @@ -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 */ -- 2.39.2