]> git.ipfire.org Git - thirdparty/lldpd.git/commitdiff
lldpcli: add an option to enable promisc mode on managed interfaces
authorVincent Bernat <vincent@bernat.im>
Fri, 30 May 2014 13:21:34 +0000 (15:21 +0200)
committerVincent Bernat <vincent@bernat.im>
Fri, 30 May 2014 13:21:34 +0000 (15:21 +0200)
This allows LLDP frames to be received even when they are not in a
configured VLAN, like this may be the case with Cisco 2960.

17 files changed:
NEWS
README.md
src/client/conf-system.c
src/client/display.c
src/client/lldpcli.8.in
src/daemon/client.c
src/daemon/interfaces-bsd.c
src/daemon/interfaces-linux.c
src/daemon/interfaces-solaris.c
src/daemon/interfaces.c
src/daemon/lldpd.h
src/daemon/priv-bsd.c
src/daemon/priv-linux.c
src/daemon/priv.c
src/lib/atom-private.c
src/lib/lldpctl.h
src/lldpd-structs.h

diff --git a/NEWS b/NEWS
index 3d9fd7bb3a2a242e0e5f9198c6189facf812f421..f5b2f66132ff2b3a5bae814931a59647c0b566b3 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,8 @@
+lldpd (0.7.10)
+  * Features:
+    + Ability to set promiscuous mode to work around bugs of some
+      switches encapsulating LLDP frames inside 802.1Q frames.
+
 lldpd (0.7.9)
   * Changes:
     + Default location for chroot, socket and PID are now configurable
index c558e4e460b86de62403c177aad6995538dae0e8..b35845bf2a2b4e286b0671a8c1821f0f10f606a2 100644 (file)
--- a/README.md
+++ b/README.md
@@ -185,8 +185,8 @@ other solutions:
  1. Disable VLAN acceleration on the receive side (`ethtool -K eth0
     rxvlan off`) but this may or may not work. Check if there are
     similar properties that could apply with `ethtool -k eth0`.
- 2. Put the interface in promiscuous mode with `ip link eth0 set
-    promisc on`.
+ 2. Put the interface in promiscuous mode with `ip link set
+    promisc on dev eth0`.
 
 On modern networks, the performance impact should be nonexistent.
 
index dba48c01202e97374d5060f4c40afac0af68b390..31e6066269822ef7989f2b9bd676e334c1c7ac8b 100644 (file)
@@ -45,6 +45,31 @@ cmd_iface_pattern(struct lldpctl_conn_t *conn, struct writer *w,
        return 1;
 }
 
+static int
+cmd_iface_promisc(struct lldpctl_conn_t *conn, struct writer *w,
+    struct cmd_env *env, void *arg)
+{
+       lldpctl_atom_t *config = lldpctl_get_configuration(conn);
+       if (config == NULL) {
+               log_warnx("lldpctl", "unable to get configuration from lldpd. %s",
+                   lldpctl_last_strerror(conn));
+               return 0;
+       }
+       if (lldpctl_atom_set_int(config,
+               lldpctl_k_config_iface_promisc,
+               arg?1:0) == NULL) {
+               log_warnx("lldpctl", "unable to %s promiscuous mode: %s",
+                   arg?"enable":"disable",
+                   lldpctl_last_strerror(conn));
+               lldpctl_atom_dec_ref(config);
+               return 0;
+       }
+       log_info("lldpctl", "interface promiscuous mode %s",
+           arg?"enabled":"disabled");
+       lldpctl_atom_dec_ref(config);
+       return 1;
+}
+
 static int
 cmd_system_description(struct lldpctl_conn_t *conn, struct writer *w,
     struct cmd_env *env, void *arg)
@@ -347,6 +372,19 @@ register_commands_configure_system(struct cmd_node *configure,
                NEWLINE, "Don't update interface descriptions with neighbor name",
                NULL, cmd_update_descriptions, NULL);
 
+       commands_new(
+               commands_new(configure_interface,
+                   "promiscuous", "Enable promiscuous mode on managed interfaces",
+                   NULL, NULL, NULL),
+               NEWLINE, "Enable promiscuous mode on managed interfaces",
+               NULL, cmd_iface_promisc, "enable");
+       commands_new(
+               commands_new(unconfigure_interface,
+                   "promiscuous", "Don't enable promiscuous mode on managed interfaces",
+                   NULL, NULL, NULL),
+               NEWLINE, "Don't enable promiscuous mode on managed interfaces",
+               NULL, cmd_iface_promisc, NULL);
+
        register_commands_srcmac_type(configure_system);
 }
 
index fb221bffd93234c6b6037512a542919211b7ea93..85defbb0bb09e080d73896595df00a4ee29382c7 100644 (file)
@@ -731,6 +731,9 @@ display_configuration(lldpctl_conn_t *conn, struct writer *w)
        tag_datatag(w, "ifdescr-update", "Update interface descriptions",
            lldpctl_atom_get_int(configuration, lldpctl_k_config_ifdescr_update)?
            "yes":"no");
+       tag_datatag(w, "iface-promisc", "Promiscuous mode on managed interfaces",
+           lldpctl_atom_get_int(configuration, lldpctl_k_config_iface_promisc)?
+           "yes":"no");
        tag_datatag(w, "lldpmed-no-inventory", "Disable LLDP-MED inventory",
            (lldpctl_atom_get_int(configuration, lldpctl_k_config_lldpmed_noinventory) == 0)?
            "no":"yes");
index 013b4dc57b005e32994c1c1e3cb812ac805ed279..d137613d836e839b15fad1ca8c434d8701d01975 100644 (file)
@@ -218,6 +218,33 @@ to override this description with the name of the peer neighbor if one
 is found or with the number of neighbors found.
 .Ed
 
+.Cd configure
+.Cd system interface promiscuous
+.Bd -ragged -offset XXXXXX
+Enable promiscuous mode on managed interfaces. This option is only
+needed if you don't receive LLDP frames from the remote switch. The
+most plausible explanation for this is the frame is tagged with some
+VLAN (usually VLAN 1) and your network card is filtering VLAN. This is
+not the only available solution to work-around this problem. If you
+are concerned about performance issues, you can also tag the VLAN 1 on
+each interface.
+.Pp
+When the interface is not managed any more (or when quitting
+.Nm ) ,
+the interface is left in promiscuous mode as it is difficult to know
+if someone else also put the interface in promiscuous mode.
+.Pp
+This option is known to be useful when the remote switch is a Cisco
+2960.
+.Ed
+
+.Cd unconfigure
+.Cd system interface promiscuous
+.Bd -ragged -offset XXXXXX
+Do not set promiscuous mode on managed interfaces. This option does
+not disable promiscuous mode on interfaces already using this mode.
+.Ed
+
 .Cd configure
 .Cd system ip management pattern Ar pattern
 .Bd -ragged -offset XXXXXX
index 6407ad69b60507f9d476d1edce7208c73a17419e..ef16874b1a05fd74e149e403418b42a79eb670ca 100644 (file)
@@ -137,6 +137,12 @@ client_handle_set_configuration(struct lldpd *cfg, enum hmsg_type *type,
                cfg->g_config.c_set_ifdescr = config->c_set_ifdescr;
                levent_update_now(cfg);
        }
+       if (config->c_promisc != cfg->g_config.c_promisc) {
+               log_debug("rpc", "%s promiscuous mode on managed interfaces",
+                   config->c_promisc?"enable":"disable");
+               cfg->g_config.c_promisc = config->c_promisc;
+               levent_update_now(cfg);
+       }
        if (config->c_bond_slave_src_mac_type != 0) {
                if (config->c_bond_slave_src_mac_type >
                    LLDP_BOND_SLAVE_SRC_MAC_TYPE_UNKNOWN &&
index 0e9d5524d2c14d166d012530528fd0d0d9e0c0d8..7db99cf77a0900c863ab98c38eaabeb9fa968eee 100644 (file)
@@ -657,6 +657,7 @@ interfaces_update(struct lldpd *cfg)
        TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
                if (!hardware->h_flags) continue;
                ifbsd_macphy(cfg, hardware);
+               interfaces_helper_promisc(cfg, hardware);
        }
 
        if (cfg->g_iface_event == NULL) {
index c3b4f28a9ba2ef0918ce951443f5e0dc11b0de01..fcf359fb53ce12334a1dd68556bd74b189af5003 100644 (file)
@@ -795,6 +795,7 @@ interfaces_update(struct lldpd *cfg)
        TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
                if (!hardware->h_flags) continue;
                iflinux_macphy(hardware);
+               interfaces_helper_promisc(cfg, hardware);
        }
 
        if (cfg->g_iface_event == NULL) {
index 304f71c9871079059e3e2b33dc661815d7cfe136..9a4f0049691e5935fcd4edd41aa3d2732b512c5c 100644 (file)
@@ -119,6 +119,7 @@ ifsolaris_extract(struct lldpd *cfg,
 extern struct lldpd_ops bpf_ops;
 void
 interfaces_update(struct lldpd *cfg) {
+       struct lldpd_hardware *hardware;
        caddr_t buffer = NULL;
        struct interfaces_device_list *interfaces;
        struct interfaces_address_list *addresses;
@@ -171,6 +172,13 @@ interfaces_update(struct lldpd *cfg) {
        interfaces_helper_mgmt(cfg, addresses);
        interfaces_helper_chassis(cfg, interfaces);
 
+       /* Mac/PHY */
+       TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
+               if (!hardware->h_flags) continue;
+               /* TODO: mac/phy for Solaris */
+               interfaces_helper_promisc(cfg, hardware);
+       }
+
 end:
        free(buffer);
        interfaces_free_devices(interfaces);
index 2bd7e1b9c874b2c5750fc54278f9d85aab7b5bb3..9c9d024f587c43d6d2b29fdbbd5171cb0c3f19d7 100644 (file)
@@ -563,6 +563,18 @@ interfaces_helper_physical(struct lldpd *cfg,
        }
 }
 
+void
+interfaces_helper_promisc(struct lldpd *cfg,
+    struct lldpd_hardware *hardware)
+{
+       if (!cfg->g_config.c_promisc) return;
+       if (priv_iface_promisc(hardware->h_ifname) != 0) {
+               log_warnx("interfaces",
+                   "unable to enable promiscuous mode for %s",
+                   hardware->h_ifname);
+       }
+}
+
 /**
  * Send the packet using the hardware function. Optionnaly mangle the MAC address.
  *
index fcf2a7bfc30327ee37139dc33e02ee61ae3dcfff..797623c8f7757301be3fd254c4a30855c24f5d8b 100644 (file)
@@ -235,6 +235,8 @@ int  asroot_iface_init_os(int, char *, int *);
 int     priv_iface_multicast(const char *, 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*);
+int     asroot_iface_promisc_os(const char *);
 int     priv_snmp_socket(struct sockaddr_un *);
 
 enum priv_cmd {
@@ -246,6 +248,7 @@ enum priv_cmd {
        PRIV_IFACE_INIT,
        PRIV_IFACE_MULTICAST,
        PRIV_IFACE_DESCRIPTION,
+       PRIV_IFACE_PROMISC,
        PRIV_SNMP_SOCKET,
 };
 
@@ -367,6 +370,8 @@ struct interfaces_device* interfaces_nametointerface(
        struct interfaces_device_list *,
        const char *);
 
+void interfaces_helper_promisc(struct lldpd *,
+    struct lldpd_hardware *);
 void interfaces_helper_whitelist(struct lldpd *,
     struct interfaces_device_list *);
 void interfaces_helper_chassis(struct lldpd *,
index 0537f25c6f03283b819fc86e2a2c2a8a13616f38..f32f990cb15269d1da5b776a86971512691725d3 100644 (file)
@@ -209,3 +209,12 @@ asroot_iface_description_os(const char *name, const char *description)
        return 0;
 }
 
+int
+asroot_iface_promisc_os(const char *name)
+{
+       /* The promiscuous mode can be set when setting BPF
+          (BIOCPROMISC). Unfortunately, the interface is locked down and we
+          cannot change that without reopening a new socket. Let's do nothing
+          for now. */
+       return 0;
+}
index add31136ab51ebd8e89ae295b6f3adfbc8c07ddc..62eaf4ab0d0503361b895574c2af0fbf5e162513 100644 (file)
@@ -258,3 +258,40 @@ asroot_iface_description_os(const char *name, const char *description)
        fclose(fp);
        return 0;
 }
+
+int
+asroot_iface_promisc_os(const char *name)
+{
+       int s, rc;
+       if ((s = socket(PF_PACKET, SOCK_RAW,
+                   htons(ETH_P_ALL))) < 0) {
+               rc = errno;
+               log_warn("privsep", "unable to open raw socket");
+               return rc;
+       }
+
+       struct ifreq ifr = {};
+       strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+
+       if (ioctl(s, SIOCGIFFLAGS, &ifr) == -1) {
+               rc = errno;
+               log_warn("privsep", "unable to get interface flags for %s",
+                   name);
+               close(s);
+               return rc;
+       }
+
+       if (ifr.ifr_flags & IFF_PROMISC)
+               return 0;
+       ifr.ifr_flags |= IFF_PROMISC;
+       if (ioctl(s, SIOCSIFFLAGS, &ifr) == -1) {
+               rc = errno;
+               log_warn("privsep", "unable to set promisc mode for %s",
+                   name);
+               close(s);
+               return rc;
+       }
+       log_info("privsep", "promiscuous mode enabled for %s", name);
+       close(s);
+       return 0;
+}
index 8dfd9d76f1d69099c0327f168e01e8f49592ff5a..b6341e442daf8daafd976e4b895dc39cd5930e02 100644 (file)
@@ -157,6 +157,19 @@ priv_iface_description(const char *name, const char *description)
        return rc;
 }
 
+/* Proxy to set interface in promiscuous mode */
+int
+priv_iface_promisc(const char *ifname)
+{
+       int rc;
+       enum priv_cmd cmd = PRIV_IFACE_PROMISC;
+       must_write(PRIV_UNPRIVILEGED, &cmd, sizeof(enum priv_cmd));
+       must_write(PRIV_UNPRIVILEGED, ifname, IFNAMSIZ);
+       priv_wait();
+       must_read(PRIV_UNPRIVILEGED, &rc, sizeof(int));
+       return rc;
+}
+
 int
 priv_snmp_socket(struct sockaddr_un *addr)
 {
@@ -298,6 +311,17 @@ asroot_iface_description()
        free(description);
 }
 
+static void
+asroot_iface_promisc()
+{
+       char name[IFNAMSIZ];
+       int rc;
+       must_read(PRIV_PRIVILEGED, &name, sizeof(name));
+       name[sizeof(name) - 1] = '\0';
+       rc = asroot_iface_promisc_os(name);
+       must_write(PRIV_PRIVILEGED, &rc, sizeof(rc));
+}
+
 static void
 asroot_snmp_socket()
 {
@@ -351,6 +375,7 @@ static struct dispatch_actions actions[] = {
        {PRIV_IFACE_INIT, asroot_iface_init},
        {PRIV_IFACE_MULTICAST, asroot_iface_multicast},
        {PRIV_IFACE_DESCRIPTION, asroot_iface_description},
+       {PRIV_IFACE_PROMISC, asroot_iface_promisc},
        {PRIV_SNMP_SOCKET, asroot_snmp_socket},
        {-1, NULL}
 };
index 936b00d91340dadbf973d2c64f48158ef2580fd1..c16887268c33577bd9204769defa93b31d7c0151 100644 (file)
@@ -480,6 +480,8 @@ _lldpctl_atom_get_int_config(lldpctl_atom_t *atom, lldpctl_key_t key)
                return c->config->c_advertise_version;
        case lldpctl_k_config_ifdescr_update:
                return c->config->c_set_ifdescr;
+       case lldpctl_k_config_iface_promisc:
+               return c->config->c_promisc;
 #ifdef ENABLE_LLDPMED
        case lldpctl_k_config_lldpmed_noinventory:
                return c->config->c_noinventory;
@@ -515,6 +517,9 @@ _lldpctl_atom_set_int_config(lldpctl_atom_t *atom, lldpctl_key_t key,
        case lldpctl_k_config_ifdescr_update:
                config.c_set_ifdescr = c->config->c_set_ifdescr = value;
                break;
+       case lldpctl_k_config_iface_promisc:
+               config.c_promisc = c->config->c_promisc = value;
+               break;
 #ifdef ENABLE_LLDPMED
        case lldpctl_k_config_fast_start_enabled:
                config.c_enable_fast_start =  value?1:2;
index 6e6eb9bd60895a14faf225432b58f0cdd586e6d3..fa787c5f3f0d96fb606dcc219b66507bc263fcf4 100644 (file)
@@ -596,6 +596,7 @@ typedef enum {
        lldpctl_k_config_fast_start_enabled, /**< `(I,WO)` Is fast start enabled */
        lldpctl_k_config_fast_start_interval, /**< `(I,WO)` Start fast transmit interval */
        lldpctl_k_config_ifdescr_update, /**< `(I,WO)` Enable or disable setting interface description */
+       lldpctl_k_config_iface_promisc,  /**< `(I,WO)` Enable or disable promiscuous mode on interfaces */
 
        lldpctl_k_interface_name = 1000, /**< `(S)` The interface name. */
 
index ac4e6339b449c559c5aea07b4f6366be7cb3dfde..de134dbfc09cb0e94915e3f563668dc2ec7f3466 100644 (file)
@@ -326,6 +326,7 @@ struct lldpd_config {
        char *c_hostname;       /* Override system name */
        int c_advertise_version; /* Should the precise version be advertised? */
        int c_set_ifdescr;       /* Set interface description */
+       int c_promisc;           /* Interfaces should be in promiscuous mode */
 
 #ifdef ENABLE_LLDPMED
        int c_noinventory;      /* Don't send inventory with LLDP-MED */