static void _lldpctl_atom_free_port(lldpctl_atom_t *atom) { struct _lldpctl_atom_port_t *port = (struct _lldpctl_atom_port_t *)atom; struct lldpd_hardware *hardware = port->hardware; struct lldpd_chassis *one_chassis, *one_chassis_next; struct lldpd_port *one_port; /* Free internal chassis atom. Should be freed immediately since we * should have the only reference. */ lldpctl_atom_dec_ref((lldpctl_atom_t*)port->chassis); /* We need to free the whole struct lldpd_hardware: local port, local * chassis and remote ports... The same chassis may be present several * times. We build a list of chassis (we don't use reference count). */ struct chassis_list chassis_list; TAILQ_INIT(&chassis_list); if (port->parent) lldpctl_atom_dec_ref((lldpctl_atom_t*)port->parent); else if (!hardware && port->port) { /* No parent, no hardware, we assume a single neighbor: one * port, one chassis. */ if (port->port->p_chassis) { lldpd_chassis_cleanup(port->port->p_chassis, 1); port->port->p_chassis = NULL; } lldpd_port_cleanup(port->port, 1); free(port->port); } if (!hardware) return; add_chassis(&chassis_list, port->port->p_chassis); TAILQ_FOREACH(one_port, &hardware->h_rports, p_entries) add_chassis(&chassis_list, one_port->p_chassis); /* Free hardware port */ lldpd_remote_cleanup(hardware, NULL, 1); lldpd_port_cleanup(port->port, 1); free(port->hardware); /* Free list of chassis */ for (one_chassis = TAILQ_FIRST(&chassis_list); one_chassis != NULL; one_chassis = one_chassis_next) { one_chassis_next = TAILQ_NEXT(one_chassis, c_entries); lldpd_chassis_cleanup(one_chassis, 1); } }
/* Cleanup a remote port. The before last argument, `expire` is a function that * should be called when a remote port is removed. If the last argument is 1, * all remote ports are removed. */ void lldpd_remote_cleanup(struct lldpd_hardware *hardware, void(*expire)(struct lldpd_hardware *, struct lldpd_port *), int all) { struct lldpd_port *port, *port_next; int del; time_t now = time(NULL); log_debug("alloc", "cleanup remote port on %s", hardware->h_ifname); for (port = TAILQ_FIRST(&hardware->h_rports); port != NULL; port = port_next) { port_next = TAILQ_NEXT(port, p_entries); del = all; if (!all && expire && (now - port->p_lastupdate >= port->p_chassis->c_ttl)) { hardware->h_ageout_cnt++; hardware->h_delete_cnt++; del = 1; } if (del) { if (expire) expire(hardware, port); TAILQ_REMOVE(&hardware->h_rports, port, p_entries); lldpd_port_cleanup(port, 1); free(port); } } if (all) TAILQ_INIT(&hardware->h_rports); }
/* cdp_decode also decodes FDP */ int cdp_decode(struct lldpd *cfg, char *frame, int s, struct lldpd_hardware *hardware, struct lldpd_chassis **newchassis, struct lldpd_port **newport) { struct lldpd_chassis *chassis; struct lldpd_port *port; #if 0 u_int16_t cksum; #endif u_int8_t *software = NULL, *platform = NULL; int software_len = 0, platform_len = 0, proto, version, nb, caps; const unsigned char cdpaddr[] = CDP_MULTICAST_ADDR; #ifdef ENABLE_FDP const unsigned char fdpaddr[] = CDP_MULTICAST_ADDR; int fdp = 0; #endif u_int8_t *pos, *tlv, *pos_address, *pos_next_address; int length, len_eth, tlv_type, tlv_len, addresses_len, address_len; if ((chassis = calloc(1, sizeof(struct lldpd_chassis))) == NULL) { LLOG_WARN("failed to allocate remote chassis"); return -1; } if ((port = calloc(1, sizeof(struct lldpd_port))) == NULL) { LLOG_WARN("failed to allocate remote port"); free(chassis); return -1; } #ifdef ENABLE_DOT1 TAILQ_INIT(&port->p_vlans); #endif length = s; pos = (u_int8_t*)frame; if (length < 2*ETH_ALEN + sizeof(u_int16_t) /* Ethernet */ + 8 /* LLC */ + 4 /* CDP header */) { LLOG_WARNX("too short CDP/FDP frame received on %s", hardware->h_ifname); goto malformed; } if (PEEK_CMP(cdpaddr, sizeof(cdpaddr)) != 0) { #ifdef ENABLE_FDP PEEK_RESTORE((u_int8_t*)frame); if (PEEK_CMP(fdpaddr, sizeof(fdpaddr)) != 0) fdp = 1; else { #endif LLOG_INFO("frame not targeted at CDP/FDP multicast address received on %s", hardware->h_ifname); goto malformed; #ifdef ENABLE_FDP } #endif } PEEK_DISCARD(ETH_ALEN); /* Don't care of source address */ len_eth = PEEK_UINT16; if (len_eth > length) { LLOG_WARNX("incorrect 802.3 frame size reported on %s", hardware->h_ifname); goto malformed; } PEEK_DISCARD(6); /* Skip beginning of LLC */ proto = PEEK_UINT16; if (proto != LLC_PID_CDP) { if ((proto != LLC_PID_DRIP) && (proto != LLC_PID_PAGP) && (proto != LLC_PID_PVSTP) && (proto != LLC_PID_UDLD) && (proto != LLC_PID_VTP) && (proto != LLC_PID_DTP) && (proto != LLC_PID_STP)) LLOG_DEBUG("incorrect LLC protocol ID received on %s", hardware->h_ifname); goto malformed; } #if 0 /* Check checksum */ cksum = frame_checksum(pos, len_eth - 8, #ifdef ENABLE_FDP !fdp /* fdp = 0 -> cisco checksum */ #else 1 /* cisco checksum */ #endif ); /* An off-by-one error may happen. Just ignore it */ if ((cksum != 0) && (cksum != 0xfffe)) { LLOG_INFO("incorrect CDP/FDP checksum for frame received on %s (%d)", hardware->h_ifname, cksum); goto malformed; } #endif /* Check version */ version = PEEK_UINT8; if ((version != 1) && (version != 2)) { LLOG_WARNX("incorrect CDP/FDP version (%d) for frame received on %s", version, hardware->h_ifname); goto malformed; } chassis->c_ttl = PEEK_UINT8; /* TTL */ PEEK_DISCARD_UINT16; /* Checksum, already checked */ while (length) { if (length < 4) { LLOG_WARNX("CDP/FDP TLV header is too large for " "frame received on %s", hardware->h_ifname); goto malformed; } tlv_type = PEEK_UINT16; tlv_len = PEEK_UINT16 - 4; PEEK_SAVE(tlv); if ((tlv_len < 0) || (length < tlv_len)) { LLOG_WARNX("incorrect size in CDP/FDP TLV header for frame " "received on %s", hardware->h_ifname); goto malformed; } switch (tlv_type) { case CDP_TLV_CHASSIS: if ((chassis->c_name = (char *)calloc(1, tlv_len + 1)) == NULL) { LLOG_WARN("unable to allocate memory for chassis name"); goto malformed; } PEEK_BYTES(chassis->c_name, tlv_len); chassis->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LOCAL; if ((chassis->c_id = (char *)malloc(tlv_len)) == NULL) { LLOG_WARN("unable to allocate memory for chassis ID"); goto malformed; } memcpy(chassis->c_id, chassis->c_name, tlv_len); chassis->c_id_len = tlv_len; break; case CDP_TLV_ADDRESSES: CHECK_TLV_SIZE(4, "Address"); addresses_len = tlv_len - 4; for (nb = PEEK_UINT32; nb > 0; nb--) { PEEK_SAVE(pos_address); /* We first try to get the real length of the packet */ if (addresses_len < 2) { LLOG_WARN("too short address subframe " "received on %s", hardware->h_ifname); goto malformed; } PEEK_DISCARD_UINT8; addresses_len--; address_len = PEEK_UINT8; addresses_len--; if (addresses_len < address_len + 2) { LLOG_WARN("too short address subframe " "received on %s", hardware->h_ifname); goto malformed; } PEEK_DISCARD(address_len); addresses_len -= address_len; address_len = PEEK_UINT16; addresses_len -= 2; if (addresses_len < address_len) { LLOG_WARN("too short address subframe " "received on %s", hardware->h_ifname); goto malformed; } PEEK_DISCARD(address_len); PEEK_SAVE(pos_next_address); /* Next, we go back and try to extract IPv4 address */ 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)); /* Go to the end of the address */ PEEK_RESTORE(pos_next_address); } break; case CDP_TLV_PORT: if ((port->p_descr = (char *)calloc(1, tlv_len + 1)) == NULL) { LLOG_WARN("unable to allocate memory for port description"); goto malformed; } PEEK_BYTES(port->p_descr, tlv_len); port->p_id_subtype = LLDP_PORTID_SUBTYPE_IFNAME; if ((port->p_id = (char *)calloc(1, tlv_len)) == NULL) { LLOG_WARN("unable to allocate memory for port ID"); goto malformed; } memcpy(port->p_id, port->p_descr, tlv_len); port->p_id_len = tlv_len; break; case CDP_TLV_CAPABILITIES: #ifdef ENABLE_FDP if (fdp) { /* Capabilities are string with FDP */ if (!strncmp("Router", (char*)pos, tlv_len)) chassis->c_cap_enabled = LLDP_CAP_ROUTER; else if (!strncmp("Switch", (char*)pos, tlv_len)) chassis->c_cap_enabled = LLDP_CAP_BRIDGE; else if (!strncmp("Bridge", (char*)pos, tlv_len)) chassis->c_cap_enabled = LLDP_CAP_REPEATER; else chassis->c_cap_enabled = LLDP_CAP_STATION; chassis->c_cap_available = chassis->c_cap_enabled; break; } #endif CHECK_TLV_SIZE(4, "Capabilities"); caps = PEEK_UINT32; if (caps & CDP_CAP_ROUTER) chassis->c_cap_enabled |= LLDP_CAP_ROUTER; if (caps & 0x0e) chassis->c_cap_enabled |= LLDP_CAP_BRIDGE; if (chassis->c_cap_enabled == 0) chassis->c_cap_enabled = LLDP_CAP_STATION; chassis->c_cap_available = chassis->c_cap_enabled; break; case CDP_TLV_SOFTWARE: software_len = tlv_len; PEEK_SAVE(software); break; case CDP_TLV_PLATFORM: platform_len = tlv_len; PEEK_SAVE(platform); break; default: LLOG_DEBUG("unknown CDP/FDP TLV type (%d) received on %s", ntohs(tlv_type), hardware->h_ifname); hardware->h_rx_unrecognized_cnt++; } PEEK_DISCARD(tlv + tlv_len - pos); } if (!software && platform) { if ((chassis->c_descr = (char *)calloc(1, platform_len + 1)) == NULL) { LLOG_WARN("unable to allocate memory for chassis description"); goto malformed; } memcpy(chassis->c_descr, platform, platform_len); } else if (software && !platform) { if ((chassis->c_descr = (char *)calloc(1, software_len + 1)) == NULL) { LLOG_WARN("unable to allocate memory for chassis description"); goto malformed; } memcpy(chassis->c_descr, software, software_len); } else if (software && platform) { #define CONCAT_PLATFORM " running on\n" if ((chassis->c_descr = (char *)calloc(1, software_len + platform_len + strlen(CONCAT_PLATFORM) + 1)) == NULL) { LLOG_WARN("unable to allocate memory for chassis description"); goto malformed; } memcpy(chassis->c_descr, platform, platform_len); memcpy(chassis->c_descr + platform_len, CONCAT_PLATFORM, strlen(CONCAT_PLATFORM)); memcpy(chassis->c_descr + platform_len + strlen(CONCAT_PLATFORM), software, software_len); } if ((chassis->c_id == NULL) || (port->p_id == NULL) || (chassis->c_name == NULL) || (chassis->c_descr == NULL) || (port->p_descr == NULL) || (chassis->c_ttl == 0) || (chassis->c_cap_enabled == 0)) { LLOG_WARNX("some mandatory CDP/FDP tlv are missing for frame received on %s", hardware->h_ifname); goto malformed; } *newchassis = chassis; *newport = port; return 1; malformed: lldpd_chassis_cleanup(chassis, 1); lldpd_port_cleanup(cfg, port, 1); free(port); return -1; }
void interfaces_helper_physical(struct lldpd *cfg, struct interfaces_device_list *interfaces, struct lldpd_ops *ops, int(*init)(struct lldpd *, struct lldpd_hardware *)) { struct interfaces_device *iface; struct lldpd_hardware *hardware; TAILQ_FOREACH(iface, interfaces, next) { if (!(iface->type & IFACE_PHYSICAL_T)) continue; if (!iface->flags) continue; log_debug("interfaces", "%s is an acceptable ethernet device", iface->name); if ((hardware = lldpd_get_hardware(cfg, iface->name, iface->index, ops)) == NULL) { if ((hardware = lldpd_alloc_hardware(cfg, iface->name, iface->index)) == NULL) { log_warnx("interfaces", "Unable to allocate space for %s", iface->name); continue; } if (init(cfg, hardware) != 0) { log_warnx("interfaces", "unable to initialize %s", hardware->h_ifname); lldpd_hardware_cleanup(cfg, hardware); continue; } hardware->h_ops = ops; hardware->h_mangle = (iface->upper && iface->upper->type & IFACE_BOND_T); interfaces_helper_add_hardware(cfg, hardware); } else { if (hardware->h_flags) continue; /* Already seen this time */ lldpd_port_cleanup(&hardware->h_lport, 0); } hardware->h_flags = iface->flags; /* Should be non-zero */ iface->flags = 0; /* Future handlers don't have to care about this interface. */ /* Get local address */ memcpy(&hardware->h_lladdr, iface->address, ETHER_ADDR_LEN); /* Fill information about port */ interfaces_helper_port_name_desc(hardware, iface); /* Fill additional info */ hardware->h_mtu = iface->mtu ? iface->mtu : 1500; #ifdef ENABLE_DOT3 if (iface->upper && iface->upper->type & IFACE_BOND_T) hardware->h_lport.p_aggregid = iface->upper->index; #endif } }