From 849954d702bf4b48167b90e1101eaa0ced878e01 Mon Sep 17 00:00:00 2001 From: Vincent Bernat Date: Wed, 3 Jun 2009 21:35:31 +0200 Subject: [PATCH] Add back support for bonding using the new architecture. --- src/interfaces.c | 349 ++++++++++++++++++++++++++++++++++++----------- src/lldpd.c | 8 +- src/lldpd.h | 5 +- src/priv.c | 14 +- 4 files changed, 287 insertions(+), 89 deletions(-) diff --git a/src/interfaces.c b/src/interfaces.c index a4ea3fd4..04294803 100644 --- a/src/interfaces.c +++ b/src/interfaces.c @@ -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, ð_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) diff --git a/src/lldpd.c b/src/lldpd.c index 16c757bc..b12138c5 100644 --- a/src/lldpd.c +++ b/src/lldpd.c @@ -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); diff --git a/src/lldpd.h b/src/lldpd.h index a5cebbd7..56b962dc 100644 --- a/src/lldpd.h +++ b/src/lldpd.h @@ -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 *); diff --git a/src/priv.c b/src/priv.c index 00916892..624d47c1 100644 --- a/src/priv.c +++ b/src/priv.c @@ -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} -- 2.39.5