]> git.ipfire.org Git - thirdparty/lldpd.git/commitdiff
Add back support for bonding using the new architecture.
authorVincent Bernat <bernat@luffy.cx>
Wed, 3 Jun 2009 19:35:31 +0000 (21:35 +0200)
committerVincent Bernat <bernat@luffy.cx>
Thu, 4 Jun 2009 08:28:35 +0000 (10:28 +0200)
src/interfaces.c
src/lldpd.c
src/lldpd.h
src/priv.c

index a4ea3fd4f07740ceb5e9681eb1175fe075773fae..04294803a60f8236a0a99af343e1861bebc20d77 100644 (file)
@@ -82,15 +82,16 @@ static int   iface_is_bond(struct lldpd *, const char *);
 static int      iface_is_bond_slave(struct lldpd *,
     const char *, const char *, int *);
 static int      iface_is_enslaved(struct lldpd *, const char *);
-#if 0
-static int      iface_is_slave_active(struct lldpd *, int, const char *);
-#endif
 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 void     iface_portid(struct lldpd_hardware *);
 static void     iface_macphy(struct lldpd_hardware *);
 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 int      iface_eth_send(struct lldpd *, struct lldpd_hardware*, char *, size_t);
 static int      iface_eth_recv(struct lldpd *, struct lldpd_hardware*, int, char*, size_t);
@@ -100,6 +101,14 @@ struct lldpd_ops eth_ops = {
        .recv = iface_eth_recv,
        .cleanup = iface_eth_close,
 };
+static int      iface_bond_send(struct lldpd *, struct lldpd_hardware*, char *, size_t);
+static int      iface_bond_recv(struct lldpd *, struct lldpd_hardware*, int, char*, size_t);
+static int      iface_bond_close(struct lldpd *, struct lldpd_hardware *);
+struct lldpd_ops bond_ops = {
+       .send = iface_bond_send,
+       .recv = iface_bond_recv,
+       .cleanup = iface_bond_close,
+};
 
 static int
 old_iface_is_bridge(struct lldpd *cfg, const char *name)
@@ -275,26 +284,6 @@ iface_is_enslaved(struct lldpd *cfg, const char *name)
        return -1;
 }
 
-#if 0
-static int
-iface_is_slave_active(struct lldpd *cfg, int master, const char *slave)
-{
-       char mastername[IFNAMSIZ];
-       int active;
-       if (if_indextoname(master, mastername) == NULL) {
-               LLOG_WARNX("unable to get master name for %s",
-                   slave);
-               return 0;       /* Safest choice */
-       }
-       if (!iface_is_bond_slave(cfg, slave, mastername, &active)) {
-               LLOG_WARNX("unable to get slave status for %s",
-                   slave);
-               return 0;               /* Safest choice */
-       }
-       return (active == BOND_STATE_ACTIVE);
-}
-#endif
-
 static void
 iface_get_permanent_mac(struct lldpd *cfg, struct lldpd_hardware *hardware)
 {
@@ -383,6 +372,74 @@ iface_get_permanent_mac(struct lldpd *cfg, struct lldpd_hardware *hardware)
        fclose(netbond);
 }
 
+/* Generic minimal checks to handle a given interface. */
+static int
+iface_minimal_checks(struct lldpd *cfg, struct ifaddrs *ifa)
+{
+       struct sockaddr_ll *sdl;
+
+       if (!(LOCAL_CHASSIS(cfg)->c_cap_enabled & LLDP_CAP_BRIDGE) &&
+           iface_is_bridge(cfg, ifa->ifa_name)) {
+               LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_BRIDGE;
+               return 0;
+       }
+       
+       if (!(LOCAL_CHASSIS(cfg)->c_cap_enabled & LLDP_CAP_WLAN) &&
+           iface_is_wireless(cfg, ifa->ifa_name))
+               LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_WLAN;
+       
+       /* First, check if this interface has already been handled */
+       if (!ifa->ifa_flags)
+               return 0;
+
+       if (ifa->ifa_addr == NULL ||
+           ifa->ifa_addr->sa_family != PF_PACKET)
+               return 0;
+
+       sdl = (struct sockaddr_ll *)ifa->ifa_addr;
+       if (sdl->sll_hatype != ARPHRD_ETHER || !sdl->sll_halen)
+               return 0;
+
+       /* We request that the interface is able to do either multicast
+        * or broadcast to be able to send discovery frames. */
+       if (!(ifa->ifa_flags & (IFF_MULTICAST|IFF_BROADCAST)))
+               return 0;
+
+       /* Don't handle bond and VLAN  */
+       if ((iface_is_vlan(cfg, ifa->ifa_name)) ||
+           (iface_is_bond(cfg, ifa->ifa_name)))
+               return 0;
+
+       return 1;
+}
+
+static int
+iface_set_filter(struct lldpd_hardware *hardware, int fd)
+{
+       const struct sock_fprog prog = {
+               .filter = lldpd_filter_f,
+               .len = sizeof(lldpd_filter_f) / sizeof(struct sock_filter)
+       };
+       if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER,
+                &prog, sizeof(prog)) < 0) {
+               LLOG_WARN("unable to change filter for %s", hardware->h_ifname);
+               return ENETDOWN;
+       }
+       return 0;
+}
+
+/* Fill up port ID using hardware L2 address */
+static void
+iface_portid(struct lldpd_hardware *hardware)
+{
+       struct lldpd_port *port = &hardware->h_lport;
+       port->p_id_subtype = LLDP_PORTID_SUBTYPE_LLADDR;
+       if ((port->p_id = calloc(1, sizeof(hardware->h_lladdr))) == NULL)
+               fatal(NULL);
+       memcpy(port->p_id, hardware->h_lladdr, sizeof(hardware->h_lladdr));
+       port->p_id_len = sizeof(hardware->h_lladdr);
+}
+
 /* Fill up MAC/PHY for a given hardware port */
 static void
 iface_macphy(struct lldpd_hardware *hardware)
@@ -485,30 +542,25 @@ iface_multicast(struct lldpd *cfg, const char *name, int remove)
 static int
 iface_eth_init(struct lldpd *cfg, struct lldpd_hardware *hardware)
 {
-       int status;
-       const struct sock_fprog prog = {
-               .filter = lldpd_filter_f,
-               .len = sizeof(lldpd_filter_f) / sizeof(struct sock_filter)
-       };
+       int fd, status;
 
-       status = priv_iface_eth_init(hardware);
-       if (status != 0)
-               return status;
+       if ((fd = priv_iface_init(hardware->h_ifname)) == -1)
+               return -1;
+       hardware->h_sendfd = fd;
 
        /* fd to receive is the same as fd to send */
-       FD_SET(hardware->h_sendfd, &hardware->h_recvfds);
+       FD_SET(fd, &hardware->h_recvfds);
 
        /* Set filter */
-       if (setsockopt(hardware->h_sendfd, SOL_SOCKET, SO_ATTACH_FILTER,
-                &prog, sizeof(prog)) < 0) {
-               LLOG_WARN("unable to change filter for %s", hardware->h_ifname);
-               return ENETDOWN;
+       if ((status = iface_set_filter(hardware, fd)) != 0) {
+               close(fd);
+               return status;
        }
 
        iface_multicast(cfg, hardware->h_ifname, 0);
 
        LLOG_DEBUG("interface %s initialized (fd=%d)", hardware->h_ifname,
-           hardware->h_sendfd);
+           fd);
        return 0;
 }
 
@@ -556,45 +608,15 @@ void
 lldpd_ifh_eth(struct lldpd *cfg, struct ifaddrs *ifap)
 {
        struct ifaddrs *ifa;
-       struct sockaddr_ll *sdl;
        struct lldpd_hardware *hardware;
        struct lldpd_port *port;
        u_int8_t *lladdr;
 
        for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
-               /* First, check if this interface has already been handled */
-               if (!ifa->ifa_flags)
-                       continue;
-
-               if (ifa->ifa_addr == NULL ||
-                   ifa->ifa_addr->sa_family != PF_PACKET)
+               if (!iface_minimal_checks(cfg, ifa))
                        continue;
 
-               sdl = (struct sockaddr_ll *)ifa->ifa_addr;
-               if (sdl->sll_hatype != ARPHRD_ETHER || !sdl->sll_halen)
-                       continue;
-
-               if (iface_is_bridge(cfg, ifa->ifa_name)) {
-                       LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_BRIDGE;
-                       continue;
-               }
-
-               /* If the interface is a bond or a VLAN, we don't handle it
-                * here. If it is enslaved, it should have been handled earlier,
-                * therefore, we don't test that here. */
-               if ((iface_is_vlan(cfg, ifa->ifa_name)) ||
-                   (iface_is_bond(cfg, ifa->ifa_name)))
-                       continue;
-
-               /* We request that the interface is able to do either multicast
-                * or broadcast to be able to send discovery frames. */
-                if (!(ifa->ifa_flags & (IFF_MULTICAST|IFF_BROADCAST)))
-                        continue;
-
-               if (iface_is_wireless(cfg, ifa->ifa_name))
-                       LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_WLAN;
-
-               if ((hardware = lldpd_get_hardware(cfg, ifa->ifa_name)) == NULL) {
+               if ((hardware = lldpd_get_hardware(cfg, ifa->ifa_name, &eth_ops)) == NULL) {
                        if  ((hardware = lldpd_alloc_hardware(cfg,
                                    ifa->ifa_name)) == NULL) {
                                LLOG_WARNX("Unable to allocate space for %s",
@@ -621,14 +643,9 @@ lldpd_ifh_eth(struct lldpd *cfg, struct ifaddrs *ifap)
                /* Get local address */
                lladdr = (u_int8_t*)(((struct sockaddr_ll *)ifa->ifa_addr)->sll_addr);
                memcpy(&hardware->h_lladdr, lladdr, sizeof(hardware->h_lladdr));
-               iface_get_permanent_mac(cfg, hardware);
 
                /* Port ID is the same as hardware address */
-               port->p_id_subtype = LLDP_PORTID_SUBTYPE_LLADDR;
-               if ((port->p_id = calloc(1, sizeof(hardware->h_lladdr))) == NULL)
-                       fatal(NULL);
-               memcpy(port->p_id, hardware->h_lladdr, sizeof(hardware->h_lladdr));
-               port->p_id_len = sizeof(hardware->h_lladdr);
+               iface_portid(hardware);
 
                /* Port description is its name */
                port->p_descr = strdup(hardware->h_ifname);
@@ -639,6 +656,186 @@ lldpd_ifh_eth(struct lldpd *cfg, struct ifaddrs *ifap)
        }
 }
 
+static int
+iface_bond_init(struct lldpd *cfg, struct lldpd_hardware *hardware)
+{
+       char *mastername = (char *)hardware->h_data; /* Master name */
+       int fd, status;
+       int un = 1;
+
+       if (!mastername) return -1;
+
+       /* First, we get a socket to the raw physical interface */
+       if ((fd = priv_iface_init(hardware->h_ifname)) == -1)
+               return -1;
+       hardware->h_sendfd = fd;
+       FD_SET(fd, &hardware->h_recvfds);
+       if ((status = iface_set_filter(hardware, fd)) != 0) {
+               close(fd);
+               return status;
+       }
+       iface_multicast(cfg, hardware->h_ifname, 0);
+
+       /* Then, we open a raw interface for the master */
+       if ((fd = priv_iface_init(mastername)) == -1) {
+               close(hardware->h_sendfd);
+               return -1;
+       }
+       FD_SET(fd, &hardware->h_recvfds);
+       if ((status = iface_set_filter(hardware, fd)) != 0) {
+               close(hardware->h_sendfd);
+               close(fd);
+               return status;
+       }
+       /* With bonding and older kernels (< 2.6.27) we need to listen
+        * to bond device. We use setsockopt() PACKET_ORIGDEV to get
+        * physical device instead of bond device (works with >=
+        * 2.6.24). */
+       if (setsockopt(fd, SOL_PACKET,
+                      PACKET_ORIGDEV, &un, sizeof(un)) == -1) {
+               LLOG_DEBUG("[priv]: unable to setsockopt for master bonding device of %s. "
+                          "You will get inaccurate results",
+                          hardware->h_ifname);
+       }
+       iface_multicast(cfg, mastername, 0);
+
+       LLOG_DEBUG("interface %s initialized (fd=%d,master=%s[%d])",
+           hardware->h_ifname,
+           hardware->h_sendfd,
+           mastername, fd);
+       return 0;
+}
+
+static int
+iface_bond_send(struct lldpd *cfg, struct lldpd_hardware *hardware,
+    char *buffer, size_t size)
+{
+       /* With bonds, we have duplicate MAC address on different physical
+        * interfaces. We need to alter the source MAC address when we send on
+        * an inactive slave. */
+       char *master = (char*)hardware->h_data;
+       int active;
+       if (!iface_is_bond_slave(cfg, hardware->h_ifname, master, &active)) {
+               LLOG_WARNX("%s seems to not be enslaved anymore?",
+                   hardware->h_ifname);
+               return 0;
+       }
+       if (active) {
+               /* We need to modify the source MAC address */
+               if (size < 2 * ETH_ALEN) {
+                       LLOG_WARNX("packet to send on %s is too small!",
+                           hardware->h_ifname);
+                       return 0;
+               }
+               memset(buffer + ETH_ALEN, 0, ETH_ALEN);
+       }
+       return write(hardware->h_sendfd,
+           buffer, size);
+}
+
+static int
+iface_bond_recv(struct lldpd *cfg, struct lldpd_hardware *hardware,
+    int fd, char *buffer, size_t size)
+{
+       int n;
+       struct sockaddr_ll from;
+       socklen_t fromlen;
+
+       fromlen = sizeof(from);
+       if ((n = recvfrom(fd, buffer, size, 0,
+                   (struct sockaddr *)&from,
+                   &fromlen)) == -1) {
+               LLOG_WARN("error while receiving frame on %s",
+                   hardware->h_ifname);
+               hardware->h_rx_discarded_cnt++;
+               return -1;
+       }
+       if (from.sll_pkttype == PACKET_OUTGOING)
+               return -1;
+       if (fd == hardware->h_sendfd)
+               /* We received this on the physical interface. */
+               return n;
+       /* We received this on the bonding interface. Is it really for us? */
+       if (from.sll_ifindex == if_nametoindex(hardware->h_ifname))
+               /* This is for us */
+               return n;
+       if (from.sll_ifindex == if_nametoindex((char*)hardware->h_data))
+               /* We don't know from which physical interface it comes (kernel
+                * < 2.6.24). In doubt, this is for us. */
+               return n;
+       return -1;              /* Not for us */
+}
+
+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);
+       iface_multicast(cfg, hardware->h_ifname, 1);
+       iface_multicast(cfg, (char*)hardware->h_data, 1);
+       free(hardware->h_data);
+       return 0;
+}
+
+void
+lldpd_ifh_bond(struct lldpd *cfg, struct ifaddrs *ifap)
+{
+       struct ifaddrs *ifa;
+       struct lldpd_hardware *hardware;
+       struct lldpd_port *port;
+       int master;
+       for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
+               if (!iface_minimal_checks(cfg, ifa))
+                       continue;
+               if ((master = iface_is_enslaved(cfg, ifa->ifa_name)) == -1)
+                       continue;
+
+               if ((hardware = lldpd_get_hardware(cfg, ifa->ifa_name, &bond_ops)) == NULL) {
+                       if  ((hardware = lldpd_alloc_hardware(cfg,
+                                   ifa->ifa_name)) == NULL) {
+                               LLOG_WARNX("Unable to allocate space for %s",
+                                   ifa->ifa_name);
+                               continue;
+                       }
+                       hardware->h_data = (char *)calloc(1, IFNAMSIZ);
+                       if_indextoname(master, hardware->h_data);
+                       if (iface_bond_init(cfg, hardware) != 0) {
+                               LLOG_WARN("unable to initialize %s",
+                                   hardware->h_ifname);
+                               lldpd_hardware_cleanup(cfg, hardware);
+                               continue;
+                       }
+                       hardware->h_ops = &bond_ops;
+                       TAILQ_INSERT_TAIL(&cfg->g_hardware, hardware, h_entries);
+               } else {
+                       memset(hardware->h_data, 0, IFNAMSIZ);
+                       if_indextoname(master, hardware->h_data);
+                       lldpd_port_cleanup(&hardware->h_lport, 0);
+               }
+               
+               port = &hardware->h_lport;
+               hardware->h_flags = ifa->ifa_flags;
+               ifa->ifa_flags = 0;
+
+               /* Get local address */
+               iface_get_permanent_mac(cfg, hardware);
+               
+               /* Port ID is the same as hardware address */
+               iface_portid(hardware);
+               
+               /* Port description is its name */
+               port->p_descr = strdup(hardware->h_ifname);
+               
+               /* Fill additional info */
+               port->p_aggregid = master;
+               iface_macphy(hardware);
+               iface_mtu(cfg, hardware);
+       }
+}
+
 void
 lldpd_ifh_vlan(struct lldpd *cfg, struct ifaddrs *ifap)
 {
@@ -667,7 +864,7 @@ lldpd_ifh_vlan(struct lldpd *cfg, struct ifaddrs *ifap)
                            3. we get a bridge
                        */
                        if ((hardware = lldpd_get_hardware(cfg,
-                                   ifv.u.device2)) == NULL) {
+                                   ifv.u.device2, NULL)) == NULL) {
                                if (iface_is_bond(cfg, ifv.u.device2)) {
                                        TAILQ_FOREACH(hardware, &cfg->g_hardware,
                                            h_entries)
index 16c757bc33fd9bf70ba9edfeff7e06b15fb8a244..b12138c5a883eaf48f14d8012a4edb8fddb33731 100644 (file)
@@ -97,11 +97,12 @@ usage(void)
 }
 
 struct lldpd_hardware *
-lldpd_get_hardware(struct lldpd *cfg, char *name)
+lldpd_get_hardware(struct lldpd *cfg, char *name, struct lldpd_ops *ops)
 {
        struct lldpd_hardware *hardware;
        TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
-               if (strcmp(hardware->h_ifname, name) == 0)
+               if ((strcmp(hardware->h_ifname, name) == 0) &&
+                   ((!ops) || (ops == hardware->h_ops)))
                        break;
        }
        return hardware;
@@ -602,7 +603,6 @@ lldpd_update_localchassis(struct lldpd *cfg)
                fatal("failed to set system description");
 
        /* Check forwarding */
-       LOCAL_CHASSIS(cfg)->c_cap_enabled = 0;
        if ((f = priv_open("/proc/sys/net/ipv4/ip_forward")) >= 0) {
                if ((read(f, &status, 1) == 1) && (status == '1')) {
                        LOCAL_CHASSIS(cfg)->c_cap_enabled = LLDP_CAP_ROUTER;
@@ -636,6 +636,7 @@ lldpd_update_localports(struct lldpd *cfg)
        struct ifaddrs *ifap;
        struct lldpd_hardware *hardware;
        lldpd_ifhandlers ifhs[] = {
+               lldpd_ifh_bond, /* Handle bond */
                lldpd_ifh_eth,  /* Handle classic ethernet interfaces */
                lldpd_ifh_vlan, /* Handle VLAN */
                lldpd_ifh_mgmt, /* Handle management address (if not already handled) */
@@ -677,6 +678,7 @@ lldpd_loop(struct lldpd *cfg)
           4. Send packets
           5. Receive packets
        */
+       LOCAL_CHASSIS(cfg)->c_cap_enabled = 0;
        lldpd_update_localports(cfg);
        lldpd_cleanup(cfg);
        lldpd_update_localchassis(cfg);
index a5cebbd7078d800f34adb5275750b3ff00f2b859..56b962dcbf42d7cc2259281ad5f419672d554d44 100644 (file)
@@ -321,7 +321,7 @@ struct hmsg {
 #define MAX_HMSGSIZE           8192
 
 /* lldpd.c */
-struct lldpd_hardware  *lldpd_get_hardware(struct lldpd *, char *);
+struct lldpd_hardware  *lldpd_get_hardware(struct lldpd *, char *, struct lldpd_ops *);
 struct lldpd_hardware  *lldpd_alloc_hardware(struct lldpd *, char *);
 void    lldpd_hardware_cleanup(struct lldpd*, struct lldpd_hardware *);
 #ifdef ENABLE_DOT1
@@ -376,6 +376,7 @@ int  ctl_msg_pack_structure(char *, void *, unsigned int, struct hmsg *, void **
 int     ctl_msg_unpack_structure(char *, void *, unsigned int, struct hmsg *, void **);
 
 /* interfaces.c */
+void    lldpd_ifh_bond(struct lldpd *, struct ifaddrs *);
 void    lldpd_ifh_eth(struct lldpd *, struct ifaddrs *);
 void    lldpd_ifh_vlan(struct lldpd *, struct ifaddrs *);
 void    lldpd_ifh_mgmt(struct lldpd *, struct ifaddrs *);
@@ -437,7 +438,7 @@ void         priv_ctl_cleanup();
 char           *priv_gethostbyname();
 int             priv_open(char*);
 int             priv_ethtool(char*, struct ethtool_cmd*);
-int             priv_iface_eth_init(struct lldpd_hardware *);
+int             priv_iface_init(const char *);
 int     priv_iface_multicast(const char *, u_int8_t *, int);
 int     priv_snmp_socket(struct sockaddr_un *);
 
index 0091689204d699925a1e2e52ab33c63c110687c7..624d47c1c21eb57c93d8e2750f1ca382d956e0c8 100644 (file)
@@ -152,17 +152,15 @@ priv_ethtool(char *ifname, struct ethtool_cmd *ethc)
 }
 
 int
-priv_iface_eth_init(struct lldpd_hardware *hardware)
+priv_iface_init(const char *name)
 {
        int cmd, rc;
        cmd = PRIV_IFACE_INIT;
        must_write(remote, &cmd, sizeof(int));
-       must_write(remote, hardware->h_ifname, IFNAMSIZ);
+       must_write(remote, name, IFNAMSIZ);
        must_read(remote, &rc, sizeof(int));
-       if (rc != 0)
-               return rc;      /* It's errno */
-       hardware->h_sendfd = receive_fd(remote);
-       return 0;
+       if (rc != 0) return -1;
+       return receive_fd(remote);
 }
 
 int
@@ -335,7 +333,7 @@ asroot_ethtool()
 }
 
 static void
-asroot_iface_eth_init()
+asroot_iface_init()
 {
        struct sockaddr_ll sa;
        int s;
@@ -434,7 +432,7 @@ static struct dispatch_actions actions[] = {
        {PRIV_GET_HOSTNAME, asroot_gethostbyname},
        {PRIV_OPEN, asroot_open},
        {PRIV_ETHTOOL, asroot_ethtool},
-       {PRIV_IFACE_INIT, asroot_iface_eth_init},
+       {PRIV_IFACE_INIT, asroot_iface_init},
        {PRIV_IFACE_MULTICAST, asroot_iface_multicast},
        {PRIV_SNMP_SOCKET, asroot_snmp_socket},
        {-1, NULL}