]> git.ipfire.org Git - thirdparty/lldpd.git/commitdiff
Add back "listen on VLAN" feature.
authorVincent Bernat <bernat@luffy.cx>
Thu, 4 Jun 2009 21:12:40 +0000 (23:12 +0200)
committerVincent Bernat <bernat@luffy.cx>
Thu, 4 Jun 2009 21:12:40 +0000 (23:12 +0200)
man/lldpd.8
src/interfaces.c
src/lldpd.c
src/lldpd.h

index 243e1d903d4932ec31c2271a3b25746f9d92415c..af7c680cc552a1b82da4025e139bdb59d0c29dc2 100644 (file)
@@ -21,7 +21,7 @@
 .Nd LLDP daemon
 .Sh SYNOPSIS
 .Nm
-.Op Fl dxcsei
+.Op Fl dvxcsei
 .Op Fl m Ar management
 .Op Fl M Ar class
 .Sh DESCRIPTION
@@ -54,6 +54,12 @@ If this option is specified,
 will run in the foreground and log to
 .Em stderr .
 This option can be specified many times to increase verbosity.
+.It Fl v
+Listen on VLAN as well. This option might be needed if your equipment
+send frames on VLAN instead of physical interface. This option enables
+.Nm
+to receive frames on VLAN interfaces as well. If you don't need this
+option, do not set it.
 .It Fl x
 Enable SNMP subagent
 With this option,
index acd35c4921cdab0b36a84ee8b555e7aa809095bf..0ee860b499823706823245726fe3ad9b63a72514 100644 (file)
@@ -84,7 +84,7 @@ static int     iface_is_bond_slave(struct lldpd *,
 static int      iface_is_enslaved(struct lldpd *, const char *);
 static void     iface_get_permanent_mac(struct lldpd *, struct lldpd_hardware *);
 static int      iface_minimal_checks(struct lldpd *, struct ifaddrs *);
-static int      iface_set_filter(struct lldpd_hardware *, int);
+static int      iface_set_filter(const char *, int);
 
 static void     iface_portid(struct lldpd_hardware *);
 static void     iface_macphy(struct lldpd_hardware *);
@@ -92,7 +92,11 @@ static void   iface_mtu(struct lldpd *, struct lldpd_hardware *);
 static void     iface_multicast(struct lldpd *, const char *, int);
 static int      iface_eth_init(struct lldpd *, struct lldpd_hardware *);
 static int      iface_bond_init(struct lldpd *, struct lldpd_hardware *);
+static void     iface_fds_close(struct lldpd *, struct lldpd_hardware *);
 #ifdef ENABLE_DOT1
+static void     iface_vlan_close(struct lldpd *, struct lldpd_hardware *);
+static void     iface_listen_vlan(struct lldpd *,
+    struct lldpd_hardware *, struct ifaddrs *);
 static void     iface_append_vlan(struct lldpd *,
     struct lldpd_hardware *, struct ifaddrs *);
 #endif
@@ -418,7 +422,7 @@ iface_minimal_checks(struct lldpd *cfg, struct ifaddrs *ifa)
 }
 
 static int
-iface_set_filter(struct lldpd_hardware *hardware, int fd)
+iface_set_filter(const char *name, int fd)
 {
        const struct sock_fprog prog = {
                .filter = lldpd_filter_f,
@@ -426,7 +430,7 @@ iface_set_filter(struct lldpd_hardware *hardware, int fd)
        };
        if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER,
                 &prog, sizeof(prog)) < 0) {
-               LLOG_WARN("unable to change filter for %s", hardware->h_ifname);
+               LLOG_WARN("unable to change filter for %s", name);
                return ENETDOWN;
        }
        return 0;
@@ -543,6 +547,18 @@ iface_multicast(struct lldpd *cfg, const char *name, int remove)
        }
 }
 
+static void
+iface_fds_close(struct lldpd *cfg, struct lldpd_hardware *hardware)
+{
+       int i;
+       for (i=0; i < FD_SETSIZE; i++)
+               if (FD_ISSET(i, &hardware->h_recvfds)) {
+                       FD_CLR(i, &hardware->h_recvfds);
+                       close(i);
+               }
+}
+
+
 static int
 iface_eth_init(struct lldpd *cfg, struct lldpd_hardware *hardware)
 {
@@ -556,7 +572,7 @@ iface_eth_init(struct lldpd *cfg, struct lldpd_hardware *hardware)
        FD_SET(fd, &hardware->h_recvfds);
 
        /* Set filter */
-       if ((status = iface_set_filter(hardware, fd)) != 0) {
+       if ((status = iface_set_filter(hardware->h_ifname, fd)) != 0) {
                close(fd);
                return status;
        }
@@ -603,6 +619,9 @@ iface_eth_recv(struct lldpd *cfg, struct lldpd_hardware *hardware,
 static int
 iface_eth_close(struct lldpd *cfg, struct lldpd_hardware *hardware)
 {
+#ifdef ENABLE_DOT1
+       iface_vlan_close(cfg, hardware);
+#endif
        close(hardware->h_sendfd);
        iface_multicast(cfg, hardware->h_ifname, 1);
        return 0;
@@ -674,7 +693,7 @@ iface_bond_init(struct lldpd *cfg, struct lldpd_hardware *hardware)
                return -1;
        hardware->h_sendfd = fd;
        FD_SET(fd, &hardware->h_recvfds);
-       if ((status = iface_set_filter(hardware, fd)) != 0) {
+       if ((status = iface_set_filter(hardware->h_ifname, fd)) != 0) {
                close(fd);
                return status;
        }
@@ -686,7 +705,7 @@ iface_bond_init(struct lldpd *cfg, struct lldpd_hardware *hardware)
                return -1;
        }
        FD_SET(fd, &hardware->h_recvfds);
-       if ((status = iface_set_filter(hardware, fd)) != 0) {
+       if ((status = iface_set_filter(mastername, fd)) != 0) {
                close(hardware->h_sendfd);
                close(fd);
                return status;
@@ -773,11 +792,10 @@ iface_bond_recv(struct lldpd *cfg, struct lldpd_hardware *hardware,
 static int
 iface_bond_close(struct lldpd *cfg, struct lldpd_hardware *hardware)
 {
-       int i;
-       /* hardware->h_sendfd is with the other fd */
-       for (i=0; i < FD_SETSIZE; i++)
-               if (FD_ISSET(i, &hardware->h_recvfds))
-                       close(i);
+#ifdef ENABLE_DOT1
+       iface_vlan_close(cfg, hardware);
+#endif
+       iface_fds_close(cfg, hardware); /* h_sendfd is here too */
        iface_multicast(cfg, hardware->h_ifname, 1);
        iface_multicast(cfg, (char*)hardware->h_data, 1);
        free(hardware->h_data);
@@ -841,6 +859,85 @@ lldpd_ifh_bond(struct lldpd *cfg, struct ifaddrs *ifap)
 }
 
 #ifdef ENABLE_DOT1
+/* We keep here the list of VLAN we listen to. */
+struct iface_vlans {
+       TAILQ_ENTRY(iface_vlans) next;
+       struct lldpd_hardware *hardware;
+       char vlan[IFNAMSIZ];
+       int fd;
+       int refreshed;
+};
+TAILQ_HEAD(, iface_vlans) ifvls;
+
+static void
+_iface_vlan_setup()
+{
+       static int done = 0;
+       if (done) return;
+       TAILQ_INIT(&ifvls);
+}
+
+/* Close the list of VLAN associated to the given hardware port if we have
+   requested the "listen on vlan" feature. If hardware is NULL, then use
+   `refreshed' to clean up. */
+static void
+iface_vlan_close(struct lldpd *cfg, struct lldpd_hardware *hardware)
+{
+       struct iface_vlans *ifvl, *ifvl_next;
+       if (!cfg->g_listen_vlans) return;
+       _iface_vlan_setup();
+       for (ifvl = TAILQ_FIRST(&ifvls); ifvl; ifvl = ifvl_next) {
+               ifvl_next = TAILQ_NEXT(ifvl, next);
+               if (hardware && (ifvl->hardware != hardware)) continue;
+               if (!hardware && (ifvl->refreshed)) continue;
+               close(ifvl->fd);
+               iface_multicast(cfg, ifvl->vlan, 1);
+               FD_CLR(ifvl->fd, &hardware->h_recvfds);
+               TAILQ_REMOVE(&ifvls, ifvl, next);
+               free(ifvl);
+       }
+}
+
+/* Listen on the given vlan on behalf of the given interface */
+static void
+iface_listen_vlan(struct lldpd *cfg,
+    struct lldpd_hardware *hardware, struct ifaddrs *ifa)
+{
+       struct iface_vlans *ifvl;
+       int fd;
+
+       if (!cfg->g_listen_vlans) return;
+       _iface_vlan_setup();
+       if (!(ifa->ifa_flags & IFF_RUNNING)) return;
+       TAILQ_FOREACH(ifvl, &ifvls, next)
+               if ((ifvl->hardware == hardware) &&
+                   (strncmp(ifvl->vlan, ifa->ifa_name, IFNAMSIZ) == 0)) break;
+       if (ifvl) {
+               ifvl->refreshed = 1;
+               return; /* We are already listening to it */
+       }
+       if ((ifvl = (struct iface_vlans *)
+               malloc(sizeof(struct iface_vlans))) == NULL)
+               return;         /* Just give up */
+
+       if ((fd = priv_iface_init(ifa->ifa_name)) == -1) {
+               free(ifvl);
+               return;
+       }
+       if (iface_set_filter(ifa->ifa_name, fd) != 0) {
+               free(ifvl);
+               close(fd);
+               return;
+       }
+       FD_SET(fd, &hardware->h_recvfds);
+       ifvl->fd = fd;
+       iface_multicast(cfg, ifa->ifa_name, 0);
+       ifvl->refreshed = 1;
+       strlcpy(ifvl->vlan, ifa->ifa_name, IFNAMSIZ);
+       ifvl->hardware = hardware;
+       TAILQ_INSERT_TAIL(&ifvls, ifvl, next);
+}
+
 static void
 iface_append_vlan(struct lldpd *cfg,
     struct lldpd_hardware *hardware, struct ifaddrs *ifa)
@@ -863,10 +960,12 @@ iface_append_vlan(struct lldpd *cfg,
                /* Dunno what happened */
                free(vlan->v_name);
                free(vlan);
-       } else {
-               vlan->v_vid = ifv.u.VID;
-               TAILQ_INSERT_TAIL(&port->p_vlans, vlan, v_entries);
+               return;
        }
+       vlan->v_vid = ifv.u.VID;
+       TAILQ_INSERT_TAIL(&port->p_vlans, vlan, v_entries);
+
+       iface_listen_vlan(cfg, hardware, ifa);
 }
 
 void
@@ -875,6 +974,13 @@ lldpd_ifh_vlan(struct lldpd *cfg, struct ifaddrs *ifap)
        struct ifaddrs *ifa;
        struct vlan_ioctl_args ifv;
        struct lldpd_hardware *hardware;
+       struct iface_vlans *ifvl;
+       
+       if (cfg->g_listen_vlans) {
+               _iface_vlan_setup();
+               TAILQ_FOREACH(ifvl, &ifvls, next)
+                   ifvl->refreshed = 0;
+       }
 
        for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
                if (!ifa->ifa_flags)
@@ -916,6 +1022,7 @@ lldpd_ifh_vlan(struct lldpd *cfg, struct ifaddrs *ifap)
                            hardware, ifa);
                }
        }
+       iface_vlan_close(cfg, NULL);
 }
 #endif
 
index 635a300195e6079c17a379b94b29bc2724783fbf..85ec04fa4c272fea465828d5a044550ab1270e63 100644 (file)
@@ -725,8 +725,8 @@ main(int argc, char *argv[])
        int snmp = 0;
 #endif
        char *mgmtp = NULL;
-       char *popt, opts[] = "dxm:p:M:i@                    ";
-       int i, found;
+       char *popt, opts[] = "vdxm:p:M:i@                    ";
+       int i, found, vlan = 0;
 #ifdef ENABLE_LLDPMED
        int lldpmed = 0, noinventory = 0;
 #endif
@@ -744,6 +744,9 @@ main(int argc, char *argv[])
        *popt = '\0';
        while ((ch = getopt(argc, argv, opts)) != -1) {
                switch (ch) {
+               case 'v':
+                       vlan = 1;
+                       break;
                case 'd':
                        debug++;
                        break;
@@ -816,6 +819,7 @@ main(int argc, char *argv[])
                fatal(NULL);
 
        cfg->g_mgmt_pattern = mgmtp;
+       cfg->g_listen_vlans = vlan;
 
        /* Get ioctl socket */
        if ((cfg->g_sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
index 19e0f9bf43005f2c2a3aa9082faede792ff4398b..5e772f16588faf8f622646a4deae24fb13d4ffe0 100644 (file)
@@ -272,6 +272,7 @@ struct lldpd {
        int                      g_delay;
 
        struct protocol         *g_protocols;
+       int                      g_listen_vlans;
 #ifdef ENABLE_LLDPMED
        int                      g_noinventory;
 #endif