From a98ed042b48eadff98abfe778a3214df69d60f9c Mon Sep 17 00:00:00 2001 From: Vincent Bernat Date: Sat, 11 Jun 2016 18:46:59 +0200 Subject: [PATCH] lldp: accept LLDP frames sent through S-VLAN/C-VLAN bridges See #171. --- src/daemon/interfaces.c | 31 ++++++++++++++++++++----------- src/daemon/lldp-tlv.h | 6 +++--- src/daemon/lldpd.c | 8 ++++++-- src/daemon/lldpd.h | 22 ++++++++++++++-------- src/daemon/priv.c | 2 +- src/daemon/protocols/lldp.c | 9 ++++++--- 6 files changed, 50 insertions(+), 28 deletions(-) diff --git a/src/daemon/interfaces.c b/src/daemon/interfaces.c index cefbec4d..f0aaccea 100644 --- a/src/daemon/interfaces.c +++ b/src/daemon/interfaces.c @@ -32,19 +32,28 @@ void interfaces_setup_multicast(struct lldpd *cfg, const char *name, int remove) { - int i, rc; + int rc; + size_t i, j; + const u_int8_t *mac; + const u_int8_t zero[ETHER_ADDR_LEN] = {}; - for (i=0; cfg->g_protocols[i].mode != 0; i++) { + for (i = 0; cfg->g_protocols[i].mode != 0; i++) { if (!cfg->g_protocols[i].enabled) continue; - if ((rc = priv_iface_multicast(name, - cfg->g_protocols[i].mac, !remove)) != 0) { - errno = rc; - if (errno != ENOENT) - log_debug("interfaces", - "unable to %s %s address to multicast filter for %s (%s)", - (remove)?"delete":"add", - cfg->g_protocols[i].name, - name, strerror(rc)); + for (mac = cfg->g_protocols[i].mac1, j = 0; + j < 3; + mac += ETHER_ADDR_LEN, + j++) { + if (memcmp(mac, zero, ETHER_ADDR_LEN) == 0) break; + if ((rc = priv_iface_multicast(name, + mac, !remove)) != 0) { + errno = rc; + if (errno != ENOENT) + log_debug("interfaces", + "unable to %s %s address to multicast filter for %s (%s)", + (remove)?"delete":"add", + cfg->g_protocols[i].name, + name, strerror(rc)); + } } } } diff --git a/src/daemon/lldp-tlv.h b/src/daemon/lldp-tlv.h index b67e1a5c..a8016bbf 100644 --- a/src/daemon/lldp-tlv.h +++ b/src/daemon/lldp-tlv.h @@ -18,9 +18,9 @@ #ifndef _LLDP_TLV_H #define _LLDP_TLV_H -#define LLDP_MULTICAST_ADDR { \ - 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e \ -} +#define LLDP_ADDR_NEAREST_BRIDGE {0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e} +#define LLDP_ADDR_NEAREST_NONTPMR_BRIDGE {0x01, 0x80, 0xc2, 0x00, 0x00, 0x03} +#define LLDP_ADDR_NEAREST_CUSTOMER_BRIDGE {0x01, 0x80, 0xc2, 0x00, 0x00, 0x00} #define LLDP_TLV_END 0 #define LLDP_TLV_CHASSIS_ID 1 diff --git a/src/daemon/lldpd.c b/src/daemon/lldpd.c index b464c2c0..e4d4a65a 100644 --- a/src/daemon/lldpd.c +++ b/src/daemon/lldpd.c @@ -44,7 +44,9 @@ static void usage(void); static struct protocol protos[] = { { LLDPD_MODE_LLDP, 1, "LLDP", 'l', lldp_send, lldp_decode, NULL, - LLDP_MULTICAST_ADDR }, + LLDP_ADDR_NEAREST_BRIDGE, + LLDP_ADDR_NEAREST_NONTPMR_BRIDGE, + LLDP_ADDR_NEAREST_CUSTOMER_BRIDGE }, #ifdef ENABLE_CDP { LLDPD_MODE_CDPV1, 0, "CDPv1", 'c', cdpv1_send, cdp_decode, cdpv1_guess, CDP_MULTICAST_ADDR }, @@ -485,7 +487,9 @@ lldpd_guess_type(struct lldpd *cfg, char *frame, int s) if (!cfg->g_protocols[i].enabled) continue; if (cfg->g_protocols[i].guess == NULL) { - if (memcmp(frame, cfg->g_protocols[i].mac, ETHER_ADDR_LEN) == 0) { + if (memcmp(frame, cfg->g_protocols[i].mac1, ETHER_ADDR_LEN) == 0 || + memcmp(frame, cfg->g_protocols[i].mac2, ETHER_ADDR_LEN) == 0 || + memcmp(frame, cfg->g_protocols[i].mac3, ETHER_ADDR_LEN) == 0) { log_debug("decode", "guessed protocol is %s (from MAC address)", cfg->g_protocols[i].name); return cfg->g_protocols[i].mode; diff --git a/src/daemon/lldpd.h b/src/daemon/lldpd.h index 9ec91e9f..263f8e2d 100644 --- a/src/daemon/lldpd.h +++ b/src/daemon/lldpd.h @@ -92,7 +92,9 @@ struct protocol { int(*send)(PROTO_SEND_SIG); /* How to send a frame */ int(*decode)(PROTO_DECODE_SIG); /* How to decode a frame */ int(*guess)(PROTO_GUESS_SIG); /* Can be NULL, use MAC address in this case */ - u_int8_t mac[ETHER_ADDR_LEN]; /* Destination MAC address used by this protocol */ + u_int8_t mac1[ETHER_ADDR_LEN]; /* Destination MAC address used by this protocol */ + u_int8_t mac2[ETHER_ADDR_LEN]; /* Destination MAC address used by this protocol */ + u_int8_t mac3[ETHER_ADDR_LEN]; /* Destination MAC address used by this protocol */ }; #define SMART_HIDDEN(port) (port->p_hidden_in) @@ -205,7 +207,7 @@ void asroot_iface_mac(void); #endif int priv_iface_init(int, char *); int asroot_iface_init_os(int, char *, int *); -int priv_iface_multicast(const char *, u_int8_t *, int); +int priv_iface_multicast(const char *, const u_int8_t *, int); int priv_iface_description(const char *, const char *); int asroot_iface_description_os(const char *, const char *); int priv_iface_promisc(const char*); @@ -257,7 +259,9 @@ void send_fd(enum priv_context, int); first byte is 1. if not, this can only be an EDP packet: tcpdump -dd "(ether[0] & 1 = 1 and - ((ether proto 0x88cc and ether dst 01:80:c2:00:00:0e) or + ((ether proto 0x88cc and (ether dst 01:80:c2:00:00:0e or + ether dst 01:80:c2:00:00:03 or + ether dst 01:80:c2:00:00:00)) or (ether dst 01:e0:52:cc:cc:cc) or (ether dst 01:00:0c:cc:cc:cc) or (ether dst 01:00:81:00:01:00))) or @@ -267,11 +271,13 @@ void send_fd(enum priv_context, int); #define LLDPD_FILTER_F \ { 0x30, 0, 0, 0x00000000 }, \ { 0x54, 0, 0, 0x00000001 }, \ - { 0x15, 0, 14, 0x00000001 }, \ + { 0x15, 0, 16, 0x00000001 }, \ { 0x28, 0, 0, 0x0000000c }, \ - { 0x15, 0, 4, 0x000088cc }, \ + { 0x15, 0, 6, 0x000088cc }, \ { 0x20, 0, 0, 0x00000002 }, \ - { 0x15, 0, 2, 0xc200000e }, \ + { 0x15, 2, 0, 0xc200000e }, \ + { 0x15, 1, 0, 0xc2000003 }, \ + { 0x15, 0, 2, 0xc2000000 }, \ { 0x28, 0, 0, 0x00000000 }, \ { 0x15, 12, 13, 0x00000180 }, \ { 0x20, 0, 0, 0x00000002 }, \ @@ -286,8 +292,8 @@ void send_fd(enum priv_context, int); { 0x15, 0, 3, 0x2b000000 }, \ { 0x28, 0, 0, 0x00000000 }, \ { 0x15, 0, 1, 0x000000e0 }, \ - { 0x6, 0, 0, 0x0000ffff }, \ - { 0x6, 0, 0, 0x00000000 }, + { 0x6, 0, 0, 0x00040000 }, \ + { 0x6, 0, 0, 0x00000000 } /* This function is responsible to refresh information about interfaces. It is * OS specific but should be present for each OS. It can use the functions in diff --git a/src/daemon/priv.c b/src/daemon/priv.c index c37685a0..7a65b120 100644 --- a/src/daemon/priv.c +++ b/src/daemon/priv.c @@ -130,7 +130,7 @@ priv_iface_init(int index, char *iface) } int -priv_iface_multicast(const char *name, u_int8_t *mac, int add) +priv_iface_multicast(const char *name, const u_int8_t *mac, int add) { int rc; enum priv_cmd cmd = PRIV_IFACE_MULTICAST; diff --git a/src/daemon/protocols/lldp.c b/src/daemon/protocols/lldp.c index deddc3e7..74ca8387 100644 --- a/src/daemon/protocols/lldp.c +++ b/src/daemon/protocols/lldp.c @@ -69,7 +69,7 @@ static int _lldp_send(struct lldpd *global, struct lldpd_mgmt *mgmt; int proto; - u_int8_t mcastaddr[] = LLDP_MULTICAST_ADDR; + u_int8_t mcastaddr[] = LLDP_ADDR_NEAREST_BRIDGE; #ifdef ENABLE_DOT1 const u_int8_t dot1[] = LLDP_TLV_ORG_DOT1; struct lldpd_vlan *vlan; @@ -572,7 +572,7 @@ lldp_decode(struct lldpd *cfg, char *frame, int s, { struct lldpd_chassis *chassis; struct lldpd_port *port; - const char lldpaddr[] = LLDP_MULTICAST_ADDR; + char lldpaddr[ETHER_ADDR_LEN]; const char dot1[] = LLDP_TLV_ORG_DOT1; const char dot3[] = LLDP_TLV_ORG_DOT3; const char med[] = LLDP_TLV_ORG_MED; @@ -626,7 +626,10 @@ lldp_decode(struct lldpd *cfg, char *frame, int s, log_warnx("lldp", "too short frame received on %s", hardware->h_ifname); goto malformed; } - if (PEEK_CMP(lldpaddr, ETHER_ADDR_LEN) != 0) { + PEEK_BYTES(lldpaddr, ETHER_ADDR_LEN); + if (memcmp(lldpaddr, (const char [])LLDP_ADDR_NEAREST_BRIDGE, ETHER_ADDR_LEN) && + memcmp(lldpaddr, (const char [])LLDP_ADDR_NEAREST_NONTPMR_BRIDGE, ETHER_ADDR_LEN) && + memcmp(lldpaddr, (const char [])LLDP_ADDR_NEAREST_CUSTOMER_BRIDGE, ETHER_ADDR_LEN)) { log_info("lldp", "frame not targeted at LLDP multicast address received on %s", hardware->h_ifname); goto malformed; -- 2.39.5