* 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.
.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
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
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;
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] =
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;
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;
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",
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 *);
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");
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;
}
}
}
+/* 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)
{
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,
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);
lldpd_update_localchassis(cfg);
lldpd_send_all(cfg);
lldpd_recv_all(cfg);
+ if (cfg->g_smart != SMART_NOFILTER)
+ lldpd_hide_all(cfg);
}
static void
#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;
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++) {
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)
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"
#endif
};
-#define STRUCT_LLDPD_PORT "(LPttPbbCsw" \
+#define STRUCT_LLDPD_PORT "(LPttPbbCswb" \
STRUCT_LLDPD_PORT_DOT3 \
STRUCT_LLDPD_PORT_MED \
STRUCT_LLDPD_PORT_DOT1 ")"
#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 */
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;
struct protocol *g_protocols;
time_t g_lastsent;
int g_lastrid;
+ int g_smart;
#ifdef USE_SNMP
int g_snmp;
#endif /* USE_SNMP */