From: João Valverde Date: Tue, 6 Mar 2012 14:08:59 +0000 (+0000) Subject: Support for multiple IPv4/IPv6 management addresses. X-Git-Tag: 0.6.0~38 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=e6b36c8751d167352fb16cf0d00fe6d4c19b07c1;p=thirdparty%2Flldpd.git Support for multiple IPv4/IPv6 management addresses. This patch introduces a new 'struct lldpd_mgmt' to store IPv4/IPv6 management addresses. These addresses are stored in a tail queue. For the local chassis we currently only use one IPv4 and one IPv6 address. Options for IPv6 addresses are the same as IPv4 (use -6). TODO: Fix SNMP agent. --- diff --git a/src/agent.c b/src/agent.c index 3b96ca46..5cef0c79 100644 --- a/src/agent.c +++ b/src/agent.c @@ -228,19 +228,23 @@ header_ipindexed_table(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { struct lldpd_chassis *chassis = LOCAL_CHASSIS(scfg); + struct lldpd_mgmt *mgmt; if (!header_index_init(vp, name, length, exact, var_len, write_method)) return NULL; - oid index[6] = { - 1, 4, - ((u_int8_t*)&chassis->c_mgmt.s_addr)[0], - ((u_int8_t*)&chassis->c_mgmt.s_addr)[1], - ((u_int8_t*)&chassis->c_mgmt.s_addr)[2], - ((u_int8_t*)&chassis->c_mgmt.s_addr)[3] }; - if (header_index_add(index, 6, - chassis) || header_index_best() != NULL) - return chassis; + TAILQ_FOREACH(mgmt, &chassis->c_mgmt, m_entries) { + if (mgmt->m_family != LLDPD_AF_IPV4) + continue; + oid index[6] = { + 1, 4, + mgmt->m_addr.octets[0], + mgmt->m_addr.octets[1], + mgmt->m_addr.octets[2], + mgmt->m_addr.octets[3] }; + if (header_index_add(index, 6, chassis)) + return chassis; + } - return NULL; + return header_index_best(); } static struct lldpd_chassis* @@ -249,24 +253,27 @@ header_tpripindexed_table(struct variable *vp, oid *name, size_t *length, { struct lldpd_hardware *hardware; struct lldpd_port *port; + struct lldpd_mgmt *mgmt; if (!header_index_init(vp, name, length, exact, var_len, write_method)) return NULL; TAILQ_FOREACH(hardware, &scfg->g_hardware, h_entries) { TAILQ_FOREACH(port, &hardware->h_rports, p_entries) { if (SMART_HIDDEN(port)) continue; - if (port->p_chassis->c_mgmt.s_addr == INADDR_ANY) - continue; - oid index[9] = { lastchange(port), - hardware->h_ifindex, - port->p_chassis->c_index, - 1, 4, - ((u_int8_t*)&port->p_chassis->c_mgmt.s_addr)[0], - ((u_int8_t*)&port->p_chassis->c_mgmt.s_addr)[1], - ((u_int8_t*)&port->p_chassis->c_mgmt.s_addr)[2], - ((u_int8_t*)&port->p_chassis->c_mgmt.s_addr)[3] }; - if (header_index_add(index, 9, - port->p_chassis)) - return port->p_chassis; + TAILQ_FOREACH(mgmt, &port->p_chassis->c_mgmt, m_entries) { + if (mgmt->m_family != LLDPD_AF_IPV4) + continue; + oid index[9] = { lastchange(port), + hardware->h_ifindex, + port->p_chassis->c_index, + 1, 4, + mgmt->m_addr.octets[0], + mgmt->m_addr.octets[1], + mgmt->m_addr.octets[2], + mgmt->m_addr.octets[3] }; + if (header_index_add(index, 9, + port->p_chassis)) + return port->p_chassis; + } } } return header_index_best(); @@ -1328,15 +1335,18 @@ agent_v_management(struct variable *vp, size_t *var_len, struct lldpd_chassis *c case LLDP_SNMP_ADDR_LEN: long_ret = 5; return (u_char*)&long_ret; +/* FIXME */ +/* case LLDP_SNMP_ADDR_IFSUBTYPE: - if (chassis->c_mgmt_if != 0) + if (chassis->c_mgmt4.iface != 0) long_ret = LLDP_MGMT_IFACE_IFINDEX; else long_ret = 1; return (u_char*)&long_ret; case LLDP_SNMP_ADDR_IFID: - long_ret = chassis->c_mgmt_if; + long_ret = chassis->c_mgmt4.iface; return (u_char*)&long_ret; +*/ case LLDP_SNMP_ADDR_OID: *var_len = sizeof(zeroDotZero); return (u_char*)zeroDotZero; diff --git a/src/cdp.c b/src/cdp.c index 609773b8..b66b8ba5 100644 --- a/src/cdp.c +++ b/src/cdp.c @@ -24,12 +24,14 @@ #include #include #include +#include static int cdp_send(struct lldpd *global, struct lldpd_hardware *hardware, int version) { struct lldpd_chassis *chassis; + struct lldpd_mgmt *mgmt; u_int8_t mcastaddr[] = CDP_MULTICAST_ADDR; u_int8_t llcorg[] = LLC_ORG_CISCO; #ifdef ENABLE_FDP @@ -94,16 +96,22 @@ cdp_send(struct lldpd *global, goto toobig; /* Adresses */ - if (!( - POKE_START_CDP_TLV(CDP_TLV_ADDRESSES) && - POKE_UINT32(1) && /* We ship only one address */ - POKE_UINT8(1) && /* Type: NLPID */ - POKE_UINT8(1) && /* Length: 1 */ - POKE_UINT8(CDP_ADDRESS_PROTO_IP) && /* IP */ - POKE_UINT16(sizeof(struct in_addr)) && /* Address length */ - POKE_BYTES(&chassis->c_mgmt, sizeof(struct in_addr)) && - POKE_END_CDP_TLV)) - goto toobig; + TAILQ_FOREACH(mgmt, &chassis->c_mgmt, m_entries) { + if (mgmt->m_family == LLDPD_AF_IPV4) { + if (!( + POKE_START_CDP_TLV(CDP_TLV_ADDRESSES) && + POKE_UINT32(1) && /* We ship only one address */ + POKE_UINT8(1) && /* Type: NLPID */ + POKE_UINT8(1) && /* Length: 1 */ + POKE_UINT8(CDP_ADDRESS_PROTO_IP) && /* IP */ + POKE_UINT16(sizeof(struct in_addr)) && /* Address length */ + POKE_BYTES(&mgmt->m_addr, sizeof(struct in_addr)) && + POKE_END_CDP_TLV)) { + goto toobig; + } + break; + } + } /* Port ID */ if (!( @@ -211,6 +219,8 @@ cdp_decode(struct lldpd *cfg, char *frame, int s, { struct lldpd_chassis *chassis; struct lldpd_port *port; + struct lldpd_mgmt *mgmt; + struct in_addr addr; #if 0 u_int16_t cksum; #endif @@ -231,6 +241,7 @@ cdp_decode(struct lldpd *cfg, char *frame, int s, LLOG_WARN("failed to allocate remote chassis"); return -1; } + TAILQ_INIT(&chassis->c_mgmt); if ((port = calloc(1, sizeof(struct lldpd_port))) == NULL) { LLOG_WARN("failed to allocate remote port"); free(chassis); @@ -379,10 +390,17 @@ cdp_decode(struct lldpd *cfg, char *frame, int s, PEEK_RESTORE(pos_address); if ((PEEK_UINT8 == 1) && (PEEK_UINT8 == 1) && (PEEK_UINT8 == CDP_ADDRESS_PROTO_IP) && - (PEEK_UINT16 == sizeof(struct in_addr)) && - (chassis->c_mgmt.s_addr == INADDR_ANY)) - PEEK_BYTES(&chassis->c_mgmt, - sizeof(struct in_addr)); + (PEEK_UINT16 == sizeof(struct in_addr))) { + PEEK_BYTES(&addr, sizeof(struct in_addr)); + mgmt = lldpd_alloc_mgmt(LLDPD_AF_IPV4, &addr, + sizeof(struct in_addr), 0); + if (mgmt == NULL) { + assert(errno == ENOMEM); + LLOG_WARN("unable to allocate memory for management address"); + goto malformed; + } + TAILQ_INSERT_TAIL(&chassis->c_mgmt, mgmt, m_entries); + } /* Go to the end of the address */ PEEK_RESTORE(pos_next_address); } diff --git a/src/display.c b/src/display.c index 480381c8..14b34f71 100644 --- a/src/display.c +++ b/src/display.c @@ -643,6 +643,8 @@ display_chassis(struct writer * w, struct lldpd_chassis *chassis) { char *cid; struct in_addr ip; + struct lldpd_mgmt *mgmt; + char addrbuf[INET6_ADDRSTRLEN]; if ((cid = (char *)malloc(chassis->c_id_len + 1)) == NULL) fatal(NULL); @@ -679,8 +681,18 @@ display_chassis(struct writer * w, struct lldpd_chassis *chassis) tag_datatag(w, "name", "SysName", chassis->c_name); tag_datatag(w, "descr", "SysDescr", chassis->c_descr); - if (chassis->c_mgmt.s_addr != INADDR_ANY) - tag_datatag(w, "mgmt-ip", "MgmtIP", inet_ntoa(chassis->c_mgmt)); + TAILQ_FOREACH(mgmt, &chassis->c_mgmt, m_entries) { + memset(addrbuf, 0, sizeof(addrbuf)); + inet_ntop(lldpd_af(mgmt->m_family), &mgmt->m_addr, addrbuf, sizeof(addrbuf)); + switch (mgmt->m_family) { + case LLDPD_AF_IPV4: + tag_datatag(w, "mgmt-ip", "MgmtIP", addrbuf); + break; + case LLDPD_AF_IPV6: + tag_datatag(w, "mgmt-ip6", "MgmtIPv6", addrbuf); + break; + } + } display_cap(w, chassis, LLDP_CAP_OTHER, "Other"); display_cap(w, chassis, LLDP_CAP_REPEATER, "Repeater"); diff --git a/src/edp.c b/src/edp.c index ee210065..a0d980df 100644 --- a/src/edp.c +++ b/src/edp.c @@ -24,6 +24,7 @@ #include #include #include +#include static int seq = 0; @@ -229,6 +230,7 @@ edp_decode(struct lldpd *cfg, char *frame, int s, { struct lldpd_chassis *chassis; struct lldpd_port *port; + struct lldpd_mgmt *mgmt, *m; #ifdef ENABLE_DOT1 struct lldpd_vlan *lvlan = NULL, *lvlan_next; #endif @@ -246,6 +248,7 @@ edp_decode(struct lldpd *cfg, char *frame, int s, LLOG_WARN("failed to allocate remote chassis"); return -1; } + TAILQ_INIT(&chassis->c_mgmt); if ((port = calloc(1, sizeof(struct lldpd_port))) == NULL) { LLOG_WARN("failed to allocate remote port"); free(chassis); @@ -410,18 +413,14 @@ edp_decode(struct lldpd *cfg, char *frame, int s, PEEK_BYTES(lvlan->v_name, tlv_len - 12); if (address.s_addr != INADDR_ANY) { - if (chassis->c_mgmt.s_addr == INADDR_ANY) - chassis->c_mgmt.s_addr = address.s_addr; - else - /* We need to guess the good one */ - if (cfg->g_mgmt_pattern != NULL) { - /* We can try to use this to prefer an address */ - char *ip; - ip = inet_ntoa(address); - if (fnmatch(cfg->g_mgmt_pattern, - ip, 0) == 0) - chassis->c_mgmt.s_addr = address.s_addr; - } + mgmt = lldpd_alloc_mgmt(LLDPD_AF_IPV4, &address, + sizeof(struct in_addr), 0); + if (mgmt == NULL) { + assert(errno == ENOMEM); + LLOG_WARN("Out of memory"); + goto malformed; + } + TAILQ_INSERT_TAIL(&chassis->c_mgmt, mgmt, m_entries); } TAILQ_INSERT_TAIL(&port->p_vlans, lvlan, v_entries); @@ -464,9 +463,16 @@ edp_decode(struct lldpd *cfg, char *frame, int s, lvlan, v_entries); } /* And the IP address */ - oport->p_chassis->c_mgmt.s_addr = - chassis->c_mgmt.s_addr; - break; + TAILQ_FOREACH(mgmt, &chassis->c_mgmt, m_entries) { + m = lldpd_alloc_mgmt(mgmt->m_family, + &mgmt->m_addr, mgmt->m_addrsize, mgmt->m_iface); + if (m == NULL) { + assert(errno == ENOMEM); + LLOG_WARN("Out of memory"); + goto malformed; + } + TAILQ_INSERT_TAIL(&oport->p_chassis->c_mgmt, m, m_entries); + } } /* We discard the remaining frame */ goto malformed; diff --git a/src/interfaces.c b/src/interfaces.c index 781784b3..ce91f5dc 100644 --- a/src/interfaces.c +++ b/src/interfaces.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -1042,6 +1043,17 @@ lldpd_ifh_vlan(struct lldpd *cfg, struct ifaddrs *ifap) } #endif +#ifndef IN_IS_ADDR_LOOPBACK +#define IN_IS_ADDR_LOOPBACK(a) ((a)->s_addr == htonl(INADDR_LOOPBACK)) +#endif +#ifndef IN_IS_ADDR_GLOBAL +#define IN_IS_ADDR_GLOBAL(a) (!IN_IS_ADDR_LOOPBACK(a)) +#endif +#ifndef IN6_IS_ADDR_GLOBAL +#define IN6_IS_ADDR_GLOBAL(a) (!IN6_IS_ADDR_LOOPBACK(a) && \ + !IN6_IS_ADDR_LINKLOCAL(a)) +#endif + /* Find a management address in all available interfaces, even those that were already handled. This is a special interface handler because it does not really handle interface related information (management address is attached @@ -1050,38 +1062,64 @@ void lldpd_ifh_mgmt(struct lldpd *cfg, struct ifaddrs *ifap) { struct ifaddrs *ifa; - struct sockaddr_in sa; - - if (LOCAL_CHASSIS(cfg)->c_mgmt.s_addr != INADDR_ANY) - return; /* We already have one */ + char addrstrbuf[INET6_ADDRSTRLEN]; + struct lldpd_mgmt *mgmt, *mgmt_next; + void *sin_addr_ptr; + size_t sin_addr_size; + char *mgmt_pattern_ptr; + int af; + + for (mgmt = TAILQ_FIRST(&LOCAL_CHASSIS(cfg)->c_mgmt); mgmt; + mgmt = mgmt_next) { + mgmt_next = TAILQ_NEXT(mgmt, m_entries); + TAILQ_REMOVE(&LOCAL_CHASSIS(cfg)->c_mgmt, mgmt, m_entries); + free(mgmt); + } - for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { - if ((ifa->ifa_addr != NULL) && - (ifa->ifa_addr->sa_family == AF_INET)) { - /* We have an IPv4 address (IPv6 not handled yet) */ - memcpy(&sa, ifa->ifa_addr, sizeof(struct sockaddr_in)); - if ((ntohl(*(u_int32_t*)&sa.sin_addr) != INADDR_LOOPBACK) && - (cfg->g_mgmt_pattern == NULL)) { - memcpy(&LOCAL_CHASSIS(cfg)->c_mgmt, - &sa.sin_addr, - sizeof(struct in_addr)); - LOCAL_CHASSIS(cfg)->c_mgmt_if = if_nametoindex(ifa->ifa_name); - } else if (cfg->g_mgmt_pattern != NULL) { - char *ip; - ip = inet_ntoa(sa.sin_addr); - if (fnmatch(cfg->g_mgmt_pattern, - ip, 0) == 0) { - memcpy(&LOCAL_CHASSIS(cfg)->c_mgmt, - &sa.sin_addr, - sizeof(struct in_addr)); - LOCAL_CHASSIS(cfg)->c_mgmt_if = - if_nametoindex(ifa->ifa_name); + /* Find management addresses */ + for (af = LLDPD_AF_UNSPEC + 1; af != LLDPD_AF_LAST; af++) { + /* We only take one of each address family */ + mgmt = NULL; + for (ifa = ifap; ifa != NULL && mgmt == NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == NULL) + continue; + if (ifa->ifa_addr->sa_family != lldpd_af(af)) + continue; + switch (af) { + case LLDPD_AF_IPV4: + sin_addr_ptr = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; + sin_addr_size = sizeof(struct in_addr); + if (!IN_IS_ADDR_GLOBAL((struct in_addr *)sin_addr_ptr)) + continue; + mgmt_pattern_ptr = cfg->g_mgmt_pattern; + break; + case LLDPD_AF_IPV6: + sin_addr_ptr = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; + sin_addr_size = sizeof(struct in6_addr); + if (!IN6_IS_ADDR_GLOBAL((struct in6_addr *)sin_addr_ptr)) + continue; + mgmt_pattern_ptr = cfg->g_mgmt_pattern6; + break; + default: + assert(0); + } + inet_ntop(lldpd_af(af), sin_addr_ptr, addrstrbuf, sizeof(addrstrbuf)); + if (mgmt_pattern_ptr == NULL || + fnmatch(mgmt_pattern_ptr, addrstrbuf, 0) == 0) { + mgmt = lldpd_alloc_mgmt(af, sin_addr_ptr, sin_addr_size, + if_nametoindex(ifa->ifa_name)); + if (mgmt == NULL) { + assert(errno == ENOMEM); /* anything else is a bug */ + LLOG_WARN("out of memory error"); + return; } + TAILQ_INSERT_TAIL(&LOCAL_CHASSIS(cfg)->c_mgmt, mgmt, m_entries); } } } } + /* Fill out chassis ID if not already done. This handler is special because we will only handle interfaces that are already handled. */ void diff --git a/src/lldp.c b/src/lldp.c index 31c6de46..c58f0d85 100644 --- a/src/lldp.c +++ b/src/lldp.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -26,6 +27,32 @@ #include #include +inline static int +lldpd_af_to_lldp_proto(int af) +{ + switch (af) { + case LLDPD_AF_IPV4: + return LLDP_MGMT_ADDR_IP4; + case LLDPD_AF_IPV6: + return LLDP_MGMT_ADDR_IP6; + default: + return LLDP_MGMT_ADDR_NONE; + } +} + +inline static int +lldpd_af_from_lldp_proto(int proto) +{ + switch (proto) { + case LLDP_MGMT_ADDR_IP4: + return LLDPD_AF_IPV4; + case LLDP_MGMT_ADDR_IP6: + return LLDPD_AF_IPV6; + default: + return LLDPD_AF_UNSPEC; + } +} + int lldp_send(struct lldpd *global, struct lldpd_hardware *hardware) @@ -35,6 +62,8 @@ lldp_send(struct lldpd *global, struct lldpd_frame *frame; int length; u_int8_t *packet, *pos, *tlv; + struct lldpd_mgmt *mgmt; + int proto; u_int8_t mcastaddr[] = LLDP_MULTICAST_ADDR; #ifdef ENABLE_DOT1 @@ -114,35 +143,36 @@ lldp_send(struct lldpd *global, POKE_END_LLDP_TLV)) goto toobig; - if (chassis->c_mgmt.s_addr != INADDR_ANY) { - /* Management address */ + /* Management addresses */ + TAILQ_FOREACH(mgmt, &chassis->c_mgmt, m_entries) { + proto = lldpd_af_to_lldp_proto(mgmt->m_family); + assert(proto != LLDP_MGMT_ADDR_NONE); if (!( - POKE_START_LLDP_TLV(LLDP_TLV_MGMT_ADDR) && - /* Size of the address, including its type */ - POKE_UINT8(sizeof(struct in_addr) + 1) && - /* Address is IPv4 */ - POKE_UINT8(LLDP_MGMT_ADDR_IP4) && - POKE_BYTES(&chassis->c_mgmt, sizeof(struct in_addr)))) + POKE_START_LLDP_TLV(LLDP_TLV_MGMT_ADDR) && + /* Size of the address, including its type */ + POKE_UINT8(mgmt->m_addrsize + 1) && + POKE_UINT8(proto) && + POKE_BYTES(&mgmt->m_addr, mgmt->m_addrsize))) goto toobig; /* Interface port type, OID */ - if (chassis->c_mgmt_if == 0) { + if (mgmt->m_iface == 0) { if (!( - /* We don't know the management interface */ - POKE_UINT8(LLDP_MGMT_IFACE_UNKNOWN) && - POKE_UINT32(0))) + /* We don't know the management interface */ + POKE_UINT8(LLDP_MGMT_IFACE_UNKNOWN) && + POKE_UINT32(0))) goto toobig; } else { if (!( - /* We have the index of the management interface */ - POKE_UINT8(LLDP_MGMT_IFACE_IFINDEX) && - POKE_UINT32(chassis->c_mgmt_if))) + /* We have the index of the management interface */ + POKE_UINT8(LLDP_MGMT_IFACE_IFINDEX) && + POKE_UINT32(mgmt->m_iface))) goto toobig; } if (!( - /* We don't provide an OID for management */ - POKE_UINT8(0) && - POKE_END_LLDP_TLV)) + /* We don't provide an OID for management */ + POKE_UINT8(0) && + POKE_END_LLDP_TLV)) goto toobig; } @@ -456,11 +486,17 @@ lldp_decode(struct lldpd *cfg, char *frame, int s, struct lldpd_ppvid *ppvid; struct lldpd_pi *pi; #endif + struct lldpd_mgmt *mgmt; + int af; + u_int8_t addr_str_length, addr_str_buffer[32]; + u_int8_t addr_family, addr_length, *addr_ptr, iface_subtype; + u_int32_t iface_number, iface; if ((chassis = calloc(1, sizeof(struct lldpd_chassis))) == NULL) { LLOG_WARN("failed to allocate remote chassis"); return -1; } + TAILQ_INIT(&chassis->c_mgmt); if ((port = calloc(1, sizeof(struct lldpd_port))) == NULL) { LLOG_WARN("failed to allocate remote port"); free(chassis); @@ -575,17 +611,32 @@ lldp_decode(struct lldpd *cfg, char *frame, int s, chassis->c_cap_enabled = PEEK_UINT16; break; case LLDP_TLV_MGMT_ADDR: - CHECK_TLV_SIZE(11, "Management address"); - if ((chassis->c_mgmt.s_addr == INADDR_ANY) && - (PEEK_UINT8 == 1+sizeof(struct in_addr)) && - (PEEK_UINT8 == LLDP_MGMT_ADDR_IP4)) { - /* We have an IPv4 address, we ignore anything else */ - PEEK_BYTES(&chassis->c_mgmt, sizeof(struct in_addr)); - chassis->c_mgmt_if = 0; - /* We only handle ifIndex subtype */ - if (PEEK_UINT8 == LLDP_MGMT_IFACE_IFINDEX) - chassis->c_mgmt_if = PEEK_UINT32; + CHECK_TLV_SIZE(1, "Management address"); + addr_str_length = PEEK_UINT8; + CHECK_TLV_SIZE(addr_str_length, "Management address"); + PEEK_BYTES(addr_str_buffer, addr_str_length); + addr_length = addr_str_length - 1; + addr_family = addr_str_buffer[0]; + addr_ptr = &addr_str_buffer[1]; + CHECK_TLV_SIZE(5, "Management address"); + iface_subtype = PEEK_UINT8; + iface_number = PEEK_UINT32; + + af = lldpd_af_from_lldp_proto(addr_family); + if (af == LLDPD_AF_UNSPEC) + break; + if (iface_subtype == LLDP_MGMT_IFACE_IFINDEX) + iface = iface_number; + else + iface = 0; + mgmt = lldpd_alloc_mgmt(af, addr_ptr, addr_length, iface); + if (mgmt == NULL) { + assert(errno == ENOMEM); + LLOG_WARN("unable to allocate memory " + "for management address"); + goto malformed; } + TAILQ_INSERT_TAIL(&chassis->c_mgmt, mgmt, m_entries); break; case LLDP_TLV_ORG: CHECK_TLV_SIZE(4, "Organisational"); diff --git a/src/lldp.h b/src/lldp.h index 7f7fc723..738b4ceb 100644 --- a/src/lldp.h +++ b/src/lldp.h @@ -174,6 +174,7 @@ enum { /* see http://www.iana.org/assignments/address-family-numbers */ enum { + LLDP_MGMT_ADDR_NONE = 0, LLDP_MGMT_ADDR_IP4 = 1, LLDP_MGMT_ADDR_IP6 = 2 }; diff --git a/src/lldpd.c b/src/lldpd.c index 5f4b1cdf..1ae7149e 100644 --- a/src/lldpd.c +++ b/src/lldpd.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -94,7 +95,9 @@ usage(void) fprintf(stderr, "-k Disable advertising of kernel release, version, machine.\n"); fprintf(stderr, "-S descr Override the default system description.\n"); fprintf(stderr, "-P name Override the default hardware platform.\n"); - fprintf(stderr, "-m IP Specify the management address of this system.\n"); + fprintf(stderr, "-4 IP Specify the IPv4 management address of this system.\n"); + fprintf(stderr, "-m IP Same as '-4', for backward compatibility.\n"); + fprintf(stderr, "-6 IP Specify the IPv6 management address of this system.\n"); fprintf(stderr, "-H mode Specify the behaviour when detecting multiple neighbors.\n"); fprintf(stderr, "-I iface Limit interfaces to use.\n"); #ifdef ENABLE_LLDPMED @@ -243,6 +246,32 @@ lldpd_port_cleanup(struct lldpd *cfg, struct lldpd_port *port, int all) } } +struct lldpd_mgmt * +lldpd_alloc_mgmt(int family, void *addrptr, size_t addrsize, u_int32_t iface) +{ + struct lldpd_mgmt *mgmt; + + if (family <= LLDPD_AF_UNSPEC || family >= LLDPD_AF_LAST) { + errno = EAFNOSUPPORT; + return NULL; + } + if (addrsize > LLDPD_MGMT_MAXADDRSIZE) { + errno = EOVERFLOW; + return NULL; + } + mgmt = calloc(1, sizeof(struct lldpd_mgmt)); + if (mgmt == NULL) { + errno = ENOMEM; + return NULL; + } + mgmt->m_family = family; + assert(addrsize <= LLDPD_MGMT_MAXADDRSIZE); + memcpy(&mgmt->m_addr, addrptr, addrsize); + mgmt->m_addrsize = addrsize; + mgmt->m_iface = iface; + return mgmt; +} + void lldpd_chassis_cleanup(struct lldpd_chassis *chassis, int all) { @@ -1011,7 +1040,6 @@ lldpd_update_localports(struct lldpd *cfg) TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) hardware->h_flags = 0; - LOCAL_CHASSIS(cfg)->c_mgmt.s_addr = INADDR_ANY; if (getifaddrs(&ifap) != 0) fatal("lldpd_update_localports: failed to get interface list"); @@ -1121,11 +1149,11 @@ lldpd_main(int argc, char *argv[]) int snmp = 0; char *agentx = NULL; /* AgentX socket */ #endif - char *mgmtp = NULL; + char *mgmtp = NULL, *mgmtp6 = NULL; char *cidp = NULL; char *interfaces = NULL; char *popt, opts[] = - "H:hkrdxX:m:I:C:p:M:P:S:i@ "; + "H:hkrdxX:m:4:6:I:C:p:M:P:S:i@ "; int i, found, advertise_version = 1; #ifdef ENABLE_LLDPMED int lldpmed = 0, noinventory = 0; @@ -1156,9 +1184,12 @@ lldpd_main(int argc, char *argv[]) case 'r': receiveonly = 1; break; - case 'm': + case 'm': /* fall through */ + case '4': mgmtp = optarg; break; + case '6': + mgmtp6 = optarg; case 'I': interfaces = optarg; break; @@ -1269,6 +1300,7 @@ lldpd_main(int argc, char *argv[]) fatal(NULL); cfg->g_mgmt_pattern = mgmtp; + cfg->g_mgmt_pattern6 = mgmtp6; cfg->g_cid_pattern = cidp; cfg->g_interfaces = interfaces; cfg->g_smart = smart; @@ -1296,6 +1328,7 @@ lldpd_main(int argc, char *argv[]) fatal(NULL); lchassis->c_cap_available = LLDP_CAP_BRIDGE | LLDP_CAP_WLAN | LLDP_CAP_ROUTER; + TAILQ_INIT(&lchassis->c_mgmt); #ifdef ENABLE_LLDPMED if (lldpmed > 0) { if (lldpmed == LLDPMED_CLASS_III) diff --git a/src/lldpd.h b/src/lldpd.h index 696c710f..9773e121 100644 --- a/src/lldpd.h +++ b/src/lldpd.h @@ -72,6 +72,28 @@ #define LLDPD_PPVID_CAP_SUPPORTED (1 << 1) #define LLDPD_PPVID_CAP_ENABLED (1 << 2) +enum { + LLDPD_AF_UNSPEC = 0, + LLDPD_AF_IPV4, + LLDPD_AF_IPV6, + LLDPD_AF_LAST +}; + +inline static int +lldpd_af(int af) +{ + switch (af) { + case LLDPD_AF_IPV4: + return AF_INET; + case LLDPD_AF_IPV6: + return AF_INET6; + case LLDPD_AF_LAST: + return AF_MAX; + default: + return AF_UNSPEC; + } +} + struct lldpd_ppvid { TAILQ_ENTRY(lldpd_ppvid) p_entries; u_int8_t p_cap_status; @@ -156,6 +178,22 @@ struct lldpd_dot3_power { MARSHAL(lldpd_dot3_power); #endif +#define LLDPD_MGMT_MAXADDRSIZE 16 /* sizeof(struct in6_addr) */ +struct lldpd_mgmt { + TAILQ_ENTRY(lldpd_mgmt) m_entries; + int m_family; + union { + struct in_addr inet; + struct in6_addr inet6; + u_int8_t octets[LLDPD_MGMT_MAXADDRSIZE]; + } m_addr; + size_t m_addrsize; + u_int32_t m_iface; +}; +MARSHAL_BEGIN(lldpd_mgmt) +MARSHAL_TQE(lldpd_mgmt, m_entries) +MARSHAL_END; + struct lldpd_chassis { TAILQ_ENTRY(lldpd_chassis) c_entries; u_int16_t c_refcount; /* Reference count by ports */ @@ -172,8 +210,7 @@ struct lldpd_chassis { u_int16_t c_ttl; - struct in_addr c_mgmt; - u_int32_t c_mgmt_if; + TAILQ_HEAD(, lldpd_mgmt) c_mgmt; #ifdef ENABLE_LLDPMED u_int16_t c_med_cap_available; @@ -193,6 +230,7 @@ MARSHAL_TQE(lldpd_chassis, c_entries) MARSHAL_FSTR(lldpd_chassis, c_id, c_id_len) MARSHAL_STR(lldpd_chassis, c_name) MARSHAL_STR(lldpd_chassis, c_descr) +MARSHAL_SUBTQ(lldpd_chassis, lldpd_mgmt, c_mgmt) #ifdef ENABLE_LLDPMED MARSHAL_STR(lldpd_chassis, c_med_hw) MARSHAL_STR(lldpd_chassis, c_med_fw) @@ -414,7 +452,7 @@ struct lldpd { TAILQ_HEAD(, lldpd_callback) g_callbacks; - char *g_mgmt_pattern; + char *g_mgmt_pattern, *g_mgmt_pattern6; char *g_cid_pattern; char *g_interfaces; @@ -452,6 +490,7 @@ void lldpd_pi_cleanup(struct lldpd_port *); #endif void lldpd_remote_cleanup(struct lldpd *, struct lldpd_hardware *, int); void lldpd_port_cleanup(struct lldpd*, struct lldpd_port *, int); +struct lldpd_mgmt *lldpd_alloc_mgmt(int family, void *addr, size_t addrsize, u_int32_t iface); void lldpd_chassis_cleanup(struct lldpd_chassis *, int); int lldpd_callback_add(struct lldpd *, int, void(*fn)(CALLBACK_SIG), void *); void lldpd_callback_del(struct lldpd *, int, void(*fn)(CALLBACK_SIG)); diff --git a/src/sonmp.c b/src/sonmp.c index a7a8f13e..ba905d93 100644 --- a/src/sonmp.c +++ b/src/sonmp.c @@ -23,6 +23,7 @@ #include #include #include +#include static struct sonmp_chassis sonmp_chassis_types[] = { {1, "unknown (via SONMP)"}, @@ -186,8 +187,10 @@ sonmp_send(struct lldpd *global, const u_int8_t mcastaddr[] = SONMP_MULTICAST_ADDR; const u_int8_t llcorg[] = LLC_ORG_NORTEL; struct lldpd_chassis *chassis; + struct lldpd_mgmt *mgmt; u_int8_t *packet, *pos, *pos_pid, *end; int length; + struct in_addr address; chassis = hardware->h_lport.p_chassis; length = hardware->h_mtu; @@ -219,21 +222,30 @@ sonmp_send(struct lldpd *global, POKE_UINT16(LLC_PID_SONMP_HELLO))) goto toobig; + + address.s_addr = htonl(INADDR_ANY); + TAILQ_FOREACH(mgmt, &chassis->c_mgmt, m_entries) { + if (mgmt->m_family == LLDPD_AF_IPV4) { + address.s_addr = mgmt->m_addr.inet.s_addr; + } + break; + } + /* SONMP */ if (!( - /* Our IP address */ - POKE_BYTES(&chassis->c_mgmt, sizeof(struct in_addr)) && - /* Segment on three bytes, we don't have slots, so we + /* Our IP address */ + POKE_BYTES(&address, sizeof(struct in_addr)) && + /* Segment on three bytes, we don't have slots, so we skip the first two bytes */ - POKE_UINT16(0) && - POKE_UINT8(hardware->h_ifindex) && - POKE_UINT8(1) && /* Chassis: Other */ - POKE_UINT8(12) && /* Back: Ethernet, Fast Ethernet and Gigabit */ - POKE_UINT8(SONMP_TOPOLOGY_NEW) && /* Should work. We have no state */ - POKE_UINT8(1) && /* Links: Dunno what it is */ - POKE_SAVE(end))) + POKE_UINT16(0) && + POKE_UINT8(hardware->h_ifindex) && + POKE_UINT8(1) && /* Chassis: Other */ + POKE_UINT8(12) && /* Back: Ethernet, Fast Ethernet and Gigabit */ + POKE_UINT8(SONMP_TOPOLOGY_NEW) && /* Should work. We have no state */ + POKE_UINT8(1) && /* Links: Dunno what it is */ + POKE_SAVE(end))) goto toobig; - + if (hardware->h_ops->send(global, hardware, (char *)packet, end - packet) == -1) { LLOG_WARN("unable to send packet on real device for %s", @@ -272,6 +284,7 @@ sonmp_decode(struct lldpd *cfg, char *frame, int s, const u_int8_t mcastaddr[] = SONMP_MULTICAST_ADDR; struct lldpd_chassis *chassis; struct lldpd_port *port; + struct lldpd_mgmt *mgmt; int length, i; u_int8_t *pos; u_int8_t seg[3], rchassis; @@ -281,6 +294,7 @@ sonmp_decode(struct lldpd *cfg, char *frame, int s, LLOG_WARN("failed to allocate remote chassis"); return -1; } + TAILQ_INIT(&chassis->c_mgmt); if ((port = calloc(1, sizeof(struct lldpd_port))) == NULL) { LLOG_WARN("failed to allocate remote port"); free(chassis); @@ -336,7 +350,13 @@ sonmp_decode(struct lldpd *cfg, char *frame, int s, hardware->h_ifname); goto malformed; } - memcpy(&chassis->c_mgmt, &address, sizeof(struct in_addr)); + mgmt = lldpd_alloc_mgmt(LLDPD_AF_IPV4, &address, sizeof(struct in_addr), 0); + if (mgmt == NULL) { + assert(errno == ENOMEM); + LLOG_WARN("unable to allocate memory for management address"); + goto malformed; + } + TAILQ_INSERT_TAIL(&chassis->c_mgmt, mgmt, m_entries); chassis->c_ttl = LLDPD_TTL; port->p_id_subtype = LLDP_PORTID_SUBTYPE_LOCAL; diff --git a/tests/check_cdp.c b/tests/check_cdp.c index f83117ba..4a6c2c89 100644 --- a/tests/check_cdp.c +++ b/tests/check_cdp.c @@ -102,7 +102,13 @@ Cisco Discovery Protocol chassis.c_name = "First chassis"; chassis.c_descr = "Chassis description"; chassis.c_cap_available = chassis.c_cap_enabled = LLDP_CAP_ROUTER; - chassis.c_mgmt.s_addr = inet_addr("172.17.142.37"); + TAILQ_INIT(&chassis.c_mgmt); + in_addr_t addr = inet_addr("172.17.142.37"); + struct lldpd_mgmt *mgmt = lldpd_alloc_mgmt(LLDPD_AF_IPV4, + &addr, sizeof(in_addr_t), 0); + if (mgmt == NULL) + ck_abort(); + TAILQ_INSERT_TAIL(&chassis.c_mgmt, mgmt, m_entries); /* Build packet */ n = cdpv1_send(NULL, &hardware); @@ -215,7 +221,13 @@ Cisco Discovery Protocol chassis.c_descr = "Chassis description"; chassis.c_cap_available = chassis.c_cap_enabled = LLDP_CAP_ROUTER | LLDP_CAP_BRIDGE; - chassis.c_mgmt.s_addr = inet_addr("172.17.142.36"); + TAILQ_INIT(&chassis.c_mgmt); + in_addr_t addr = inet_addr("172.17.142.36"); + struct lldpd_mgmt *mgmt = lldpd_alloc_mgmt(LLDPD_AF_IPV4, + &addr, sizeof(in_addr_t), 0); + if (mgmt == NULL) + ck_abort(); + TAILQ_INSERT_TAIL(&chassis.c_mgmt, mgmt, m_entries); /* Build packet */ n = cdpv2_send(NULL, &hardware); @@ -352,9 +364,9 @@ Cisco Discovery Protocol ck_assert_int_eq(nchassis->c_id_len, 2); fail_unless(memcmp(nchassis->c_id, "R1", 2) == 0); ck_assert_str_eq(nchassis->c_name, "R1"); - ck_assert_int_eq(nchassis->c_mgmt.s_addr, + ck_assert_int_eq(nchassis->c_mgmt.tqh_first->m_addr.inet.s_addr, (u_int32_t)inet_addr("192.168.10.1")); - ck_assert_int_eq(nchassis->c_mgmt_if, 0); + ck_assert_int_eq(nchassis->c_mgmt.tqh_first->m_iface, 0); ck_assert_int_eq(nport->p_id_subtype, LLDP_PORTID_SUBTYPE_IFNAME); ck_assert_int_eq(nport->p_id_len, strlen("Ethernet0")); @@ -496,9 +508,9 @@ Cisco Discovery Protocol fail_unless(memcmp(nchassis->c_id, "rtbg6test01", strlen("rtbg6test01")) == 0); ck_assert_str_eq(nchassis->c_name, "rtbg6test01"); - ck_assert_int_eq(nchassis->c_mgmt.s_addr, + ck_assert_int_eq(TAILQ_FIRST(&nchassis->c_mgmt)->m_addr.inet.s_addr, (u_int32_t)inet_addr("172.66.55.3")); - ck_assert_int_eq(nchassis->c_mgmt_if, 0); + ck_assert_int_eq(TAILQ_FIRST(&nchassis->c_mgmt)->m_iface, 0); ck_assert_int_eq(nport->p_id_subtype, LLDP_PORTID_SUBTYPE_IFNAME); ck_assert_int_eq(nport->p_id_len, strlen("FastEthernet0/0")); diff --git a/tests/check_edp.c b/tests/check_edp.c index e2e5aa42..8fc65333 100644 --- a/tests/check_edp.c +++ b/tests/check_edp.c @@ -536,7 +536,7 @@ Extreme Discovery Protocol fail("unable to find our previous port?"); return; } - ck_assert_int_eq(nport->p_chassis->c_mgmt.s_addr, + ck_assert_int_eq(TAILQ_FIRST(&nport->p_chassis->c_mgmt)->m_addr.inet.s_addr, (u_int32_t)inet_addr("10.50.0.63")); if (TAILQ_EMPTY(&nport->p_vlans)) { fail("no VLAN"); diff --git a/tests/check_lldp.c b/tests/check_lldp.c index c8693dd9..8672e3fb 100644 --- a/tests/check_lldp.c +++ b/tests/check_lldp.c @@ -703,9 +703,9 @@ Link Layer Discovery Protocol LLDP_CAP_WLAN | LLDP_CAP_ROUTER | LLDP_CAP_BRIDGE); ck_assert_int_eq(nchassis->c_cap_enabled, LLDP_CAP_ROUTER | LLDP_CAP_BRIDGE); - ck_assert_int_eq(nchassis->c_mgmt.s_addr, + ck_assert_int_eq(nchassis->c_mgmt.tqh_first->m_addr.inet.s_addr, (u_int32_t)inet_addr("10.238.80.75")); - ck_assert_int_eq(nchassis->c_mgmt_if, 3); + ck_assert_int_eq(nchassis->c_mgmt.tqh_first->m_iface, 3); #ifdef ENABLE_DOT3 ck_assert_int_eq(nport->p_aggregid, 0); ck_assert_int_eq(nport->p_macphy.autoneg_enabled, 1); diff --git a/tests/check_snmp.c b/tests/check_snmp.c index b420e366..b7871b44 100644 --- a/tests/check_snmp.c +++ b/tests/check_snmp.c @@ -32,8 +32,12 @@ struct lldpd_chassis chassis1 = { .c_cap_available = LLDP_CAP_BRIDGE | LLDP_CAP_WLAN | LLDP_CAP_ROUTER, .c_cap_enabled = LLDP_CAP_ROUTER, .c_ttl = 60, - .c_mgmt = { .s_addr = 251789504 }, /* 192.0.2.15 */ - .c_mgmt_if = 3, + .c_mgmt4 = { + .family = AF_INET, + .address = { .inet = { 251789504 } }, /* 192.0.2.15 */ + .addrsize = sizeof(struct in_addr), + .iface = 3 + }, #ifdef ENABLE_LLDPMED .c_med_cap_available = LLDPMED_CAP_CAP | LLDPMED_CAP_IV | \ LLDPMED_CAP_LOCATION | LLDPMED_CAP_POLICY | \ @@ -60,8 +64,12 @@ struct lldpd_chassis chassis2 = { .c_cap_available = LLDP_CAP_ROUTER, .c_cap_enabled = LLDP_CAP_ROUTER, .c_ttl = 60, - .c_mgmt = { .s_addr = 285343936 }, /* 192.0.2.17 */ - .c_mgmt_if = 5, + .c_mgmt4 = { + .family = AF_INET, + .address = { .inet = { 285343936 } }, /* 192.0.2.17 */ + .addrsize = sizeof(struct in_addr), + .iface = 5 + }, #ifdef ENABLE_LLDPMED .c_med_hw = "Hardware 2", /* We skip c_med_fw */ diff --git a/tests/check_sonmp.c b/tests/check_sonmp.c index 9f17dd95..b6175e2c 100644 --- a/tests/check_sonmp.c +++ b/tests/check_sonmp.c @@ -80,7 +80,13 @@ Nortel Networks / SynOptics Network Management Protocol chassis.c_id_subtype = LLDP_CHASSISID_SUBTYPE_LLADDR; chassis.c_id = macaddress; chassis.c_id_len = ETH_ALEN; - chassis.c_mgmt.s_addr = inet_addr("172.17.142.37"); + TAILQ_INIT(&chassis.c_mgmt); + in_addr_t addr = inet_addr("172.17.142.37"); + struct lldpd_mgmt *mgmt = lldpd_alloc_mgmt(LLDPD_AF_IPV4, + &addr, sizeof(in_addr_t), 0); + if (mgmt == NULL) + ck_abort(); + TAILQ_INSERT_TAIL(&chassis.c_mgmt, mgmt, m_entries); /* Build packet */ n = sonmp_send(NULL, &hardware); @@ -161,9 +167,9 @@ Nortel Networks / SynOptics Network Management Protocol fail_unless(memcmp(nchassis->c_id, cid, 5) == 0); ck_assert_str_eq(nchassis->c_name, "172.16.101.168"); ck_assert_str_eq(nchassis->c_descr, "Nortel Ethernet Routing 8610 L3 Switch"); - ck_assert_int_eq(nchassis->c_mgmt.s_addr, + ck_assert_int_eq(TAILQ_FIRST(&nchassis->c_mgmt)->m_addr.inet.s_addr, (u_int32_t)inet_addr("172.16.101.168")); - ck_assert_int_eq(nchassis->c_mgmt_if, 0); + ck_assert_int_eq(TAILQ_FIRST(&nchassis->c_mgmt)->m_iface, 0); ck_assert_str_eq(nport->p_descr, "port 2/8"); ck_assert_int_eq(nport->p_id_subtype, LLDP_PORTID_SUBTYPE_LOCAL);