Пример #1
0
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);
	}
}
Пример #2
0
/* 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);
}
Пример #3
0
/* 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;
}
Пример #4
0
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
	}
}