cdp_send(struct lldpd *global,
	 struct lldpd_hardware *hardware, int version)
{
	const char *platform = "Unknown";
	struct lldpd_chassis *chassis;
	struct lldpd_mgmt *mgmt;
	struct lldpd_port *port;
	u_int8_t mcastaddr[] = CDP_MULTICAST_ADDR;
	u_int8_t llcorg[] = LLC_ORG_CISCO;
#ifdef ENABLE_FDP
	char *capstr;
#endif
	u_int16_t checksum;
	int length, i;
	u_int32_t cap;
	u_int8_t *packet;
	u_int8_t *pos, *pos_len_eh, *pos_llc, *pos_cdp, *pos_checksum, *tlv, *end;

	log_debug("cdp", "send CDP frame on %s", hardware->h_ifname);

	port = &(hardware->h_lport);
	chassis = port->p_chassis;

#ifdef ENABLE_FDP
	if (version == 0) {
		/* With FDP, change multicast address and LLC PID */
		const u_int8_t fdpmcastaddr[] = FDP_MULTICAST_ADDR;
		const u_int8_t fdpllcorg[] = LLC_ORG_FOUNDRY;
		memcpy(mcastaddr, fdpmcastaddr, sizeof(mcastaddr));
		memcpy(llcorg, fdpllcorg, sizeof(llcorg));
	}
#endif

	length = hardware->h_mtu;
	if ((packet = (u_int8_t*)calloc(1, length)) == NULL)
		return ENOMEM;
	pos = packet;

	/* Ethernet header */
	if (!(
	      POKE_BYTES(mcastaddr, sizeof(mcastaddr)) &&
	      POKE_BYTES(&hardware->h_lladdr, ETHER_ADDR_LEN) &&
	      POKE_SAVE(pos_len_eh) && /* We compute the len later */
	      POKE_UINT16(0)))
		goto toobig;

	/* LLC */
	if (!(
	      POKE_SAVE(pos_llc) &&
	      POKE_UINT8(0xaa) && /* SSAP */
	      POKE_UINT8(0xaa) && /* DSAP */
	      POKE_UINT8(0x03) && /* Control field */
	      POKE_BYTES(llcorg, sizeof(llcorg)) &&
	      POKE_UINT16(LLC_PID_CDP)))
		goto toobig;

	/* CDP header */
	if (!(
	      POKE_SAVE(pos_cdp) &&
	      POKE_UINT8((version == 0)?1:version) &&
	      POKE_UINT8(chassis->c_ttl) &&
	      POKE_SAVE(pos_checksum) && /* Save checksum position */
	      POKE_UINT16(0)))
		goto toobig;

	/* Chassis ID */
	if (!(
	      POKE_START_CDP_TLV(CDP_TLV_CHASSIS) &&
	      (chassis->c_name?
		  POKE_BYTES(chassis->c_name, strlen(chassis->c_name)):
		  POKE_BYTES("", 0)) &&
	      POKE_END_CDP_TLV))
		goto toobig;

	/* Adresses */
	/* See:
	 *   http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.htm#xtocid12
	 *
	 * It seems that Cisco implies that CDP supports IPv6 using
	 * 802.2 address format with 0xAAAA03 0x000000 0x0800, but
	 * 0x0800 is the Ethernet protocol type for IPv4. Therefore,
	 * we support only IPv4. */
	i = 0;
	TAILQ_FOREACH(mgmt, &chassis->c_mgmt, m_entries)
		if (mgmt->m_family == LLDPD_AF_IPV4) i++;
	if (i > 0) {
		if (!(
		      POKE_START_CDP_TLV(CDP_TLV_ADDRESSES) &&
		      POKE_UINT32(i)))
			goto toobig;
		TAILQ_FOREACH(mgmt, &chassis->c_mgmt, m_entries) {
			switch (mgmt->m_family) {
			case LLDPD_AF_IPV4:
				if (!(
				      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))))
					goto toobig;
				break;
			}
		}
		if (!(POKE_END_CDP_TLV))
			goto toobig;
	}

	/* Port ID */
	if (!(
	      POKE_START_CDP_TLV(CDP_TLV_PORT) &&
	      (hardware->h_lport.p_descr?
		  POKE_BYTES(hardware->h_lport.p_descr,
		      strlen(hardware->h_lport.p_descr)):
		  POKE_BYTES("", 0)) &&
	      POKE_END_CDP_TLV))
		goto toobig;

	/* Capabilities */
	if (version != 0) {
		cap = 0;
		if (chassis->c_cap_enabled & LLDP_CAP_ROUTER)
			cap |= CDP_CAP_ROUTER;
		if (chassis->c_cap_enabled & LLDP_CAP_BRIDGE)
			cap |= CDP_CAP_SWITCH;
		cap |= CDP_CAP_HOST;
		if (!(
		      POKE_START_CDP_TLV(CDP_TLV_CAPABILITIES) &&
		      POKE_UINT32(cap) &&
		      POKE_END_CDP_TLV))
			goto toobig;
#ifdef ENABLE_FDP
	} else {
		/* With FDP, it seems that a string is used in place of an int */
		if (chassis->c_cap_enabled & LLDP_CAP_ROUTER)
			capstr = "Router";
		else if (chassis->c_cap_enabled & LLDP_CAP_BRIDGE)
			capstr = "Switch";
		else if (chassis->c_cap_enabled & LLDP_CAP_REPEATER)
			capstr = "Bridge";
		else
			capstr = "Host";
		if (!(
		      POKE_START_CDP_TLV(CDP_TLV_CAPABILITIES) &&
		      POKE_BYTES(capstr, strlen(capstr)) &&
		      POKE_END_CDP_TLV))
			goto toobig;
#endif
	}

	/* Native VLAN */
#ifdef ENABLE_DOT1
	if (version >=2 && hardware->h_lport.p_pvid != 0) {
		if (!(
		      POKE_START_CDP_TLV(CDP_TLV_NATIVEVLAN) &&
		      POKE_UINT16(hardware->h_lport.p_pvid) &&
		      POKE_END_CDP_TLV))
			goto toobig;
	}
#endif

	/* Software version */
	if (!(
	      POKE_START_CDP_TLV(CDP_TLV_SOFTWARE) &&
	      (chassis->c_descr?
		  POKE_BYTES(chassis->c_descr, strlen(chassis->c_descr)):
		  POKE_BYTES("", 0)) &&
	      POKE_END_CDP_TLV))
		goto toobig;

	/* Platform */
	if (global && global->g_config.c_platform) platform = global->g_config.c_platform;

	if (!(
	      POKE_START_CDP_TLV(CDP_TLV_PLATFORM) &&
	      POKE_BYTES(platform, strlen(platform)) &&
	      POKE_END_CDP_TLV))
		goto toobig;

#ifdef ENABLE_LLDPMED
	/* Power use */
	if ((version >= 2) &&
	    port->p_med_cap_enabled &&
	    (port->p_med_power.source != LLDP_MED_POW_SOURCE_LOCAL) &&
	    (port->p_med_power.val > 0) &&
	    (port->p_med_power.val <= 655)) {
		if (!(
		      POKE_START_CDP_TLV(CDP_TLV_POWER_CONSUMPTION) &&
		      POKE_UINT16(port->p_med_power.val * 100) &&
		      POKE_END_CDP_TLV))
			goto toobig;
	}
#endif
	(void)POKE_SAVE(end);

	/* Compute len and checksum */
	POKE_RESTORE(pos_len_eh);
	if (!(POKE_UINT16(end - pos_llc))) goto toobig;
	checksum = frame_checksum(pos_cdp, end - pos_cdp, (version != 0) ? 1 : 0);
	POKE_RESTORE(pos_checksum);
	if (!(POKE_UINT16(checksum))) goto toobig;

	if (interfaces_send_helper(global, hardware,
		(char *)packet, end - packet) == -1) {
		log_warn("cdp", "unable to send packet on real device for %s",
			   hardware->h_ifname);
		free(packet);
		return ENETDOWN;
	}

	hardware->h_tx_cnt++;

	free(packet);
	return 0;
 toobig:
	free(packet);
	return -1;
}
cdp_send(struct lldpd *global,
	 struct lldpd_hardware *hardware, int version)
{
	const char *platform = "Unknown";
	struct lldpd_chassis *chassis;
	struct lldpd_mgmt *mgmt;
	struct lldpd_port *port;
	u_int8_t mcastaddr[] = CDP_MULTICAST_ADDR;
	u_int8_t llcorg[] = LLC_ORG_CISCO;
#ifdef ENABLE_FDP
	char *capstr;
#endif
	u_int16_t checksum;
	int length, i;
	u_int32_t cap;
	u_int8_t *packet;
	u_int8_t *pos, *pos_len_eh, *pos_llc, *pos_cdp, *pos_checksum, *tlv, *end;

	log_debug("cdp", "send CDP frame on %s", hardware->h_ifname);

	port = &(hardware->h_lport);
	chassis = port->p_chassis;

#ifdef ENABLE_FDP
	if (version == 0) {
		 
		const u_int8_t fdpmcastaddr[] = FDP_MULTICAST_ADDR;
		const u_int8_t fdpllcorg[] = LLC_ORG_FOUNDRY;
		memcpy(mcastaddr, fdpmcastaddr, sizeof(mcastaddr));
		memcpy(llcorg, fdpllcorg, sizeof(llcorg));
	}
#endif

	length = hardware->h_mtu;
	if ((packet = (u_int8_t*)calloc(1, length)) == NULL)
		return ENOMEM;
	pos = packet;

	 
	if (!(
	      POKE_BYTES(mcastaddr, sizeof(mcastaddr)) &&
	      POKE_BYTES(&hardware->h_lladdr, ETHER_ADDR_LEN) &&
	      POKE_SAVE(pos_len_eh) &&  
	      POKE_UINT16(0)))
		goto toobig;

	 
	if (!(
	      POKE_SAVE(pos_llc) &&
	      POKE_UINT8(0xaa) &&  
	      POKE_UINT8(0xaa) &&  
	      POKE_UINT8(0x03) &&  
	      POKE_BYTES(llcorg, sizeof(llcorg)) &&
	      POKE_UINT16(LLC_PID_CDP)))
		goto toobig;

	 
	if (!(
	      POKE_SAVE(pos_cdp) &&
	      POKE_UINT8((version == 0)?1:version) &&
	      POKE_UINT8(chassis->c_ttl) &&
	      POKE_SAVE(pos_checksum) &&  
	      POKE_UINT16(0)))
		goto toobig;

	 
	if (!(
	      POKE_START_CDP_TLV(CDP_TLV_CHASSIS) &&
	      (chassis->c_name?
		  POKE_BYTES(chassis->c_name, strlen(chassis->c_name)):
		  POKE_BYTES("", 0)) &&
	      POKE_END_CDP_TLV))
		goto toobig;

	 
	 
	i = 0;
	TAILQ_FOREACH(mgmt, &chassis->c_mgmt, m_entries)
		if (mgmt->m_family == LLDPD_AF_IPV4) i++;
	if (i > 0) {
		if (!(
		      POKE_START_CDP_TLV(CDP_TLV_ADDRESSES) &&
		      POKE_UINT32(i)))
			goto toobig;
		TAILQ_FOREACH(mgmt, &chassis->c_mgmt, m_entries) {
			switch (mgmt->m_family) {
			case LLDPD_AF_IPV4:
				if (!(
				      POKE_UINT8(1) &&	 
				      POKE_UINT8(1) &&   
				      POKE_UINT8(CDP_ADDRESS_PROTO_IP) &&  
				      POKE_UINT16(sizeof(struct in_addr)) &&  
				      POKE_BYTES(&mgmt->m_addr, sizeof(struct in_addr))))
					goto toobig;
				break;
			}
		}
		if (!(POKE_END_CDP_TLV))
			goto toobig;
	}

	 
	if (!(
	      POKE_START_CDP_TLV(CDP_TLV_PORT) &&
	      (hardware->h_lport.p_descr?
		  POKE_BYTES(hardware->h_lport.p_descr,
		      strlen(hardware->h_lport.p_descr)):
		  POKE_BYTES("", 0)) &&
	      POKE_END_CDP_TLV))
		goto toobig;

	 
	if (version != 0) {
		cap = 0;
		if (chassis->c_cap_enabled & LLDP_CAP_ROUTER)
			cap |= CDP_CAP_ROUTER;
		if (chassis->c_cap_enabled & LLDP_CAP_BRIDGE)
			cap |= CDP_CAP_SWITCH;
		cap |= CDP_CAP_HOST;
		if (!(
		      POKE_START_CDP_TLV(CDP_TLV_CAPABILITIES) &&
		      POKE_UINT32(cap) &&
		      POKE_END_CDP_TLV))
			goto toobig;
#ifdef ENABLE_FDP
	} else {
		 
		if (chassis->c_cap_enabled & LLDP_CAP_ROUTER)
			capstr = "Router";
		else if (chassis->c_cap_enabled & LLDP_CAP_BRIDGE)
			capstr = "Switch";
		else if (chassis->c_cap_enabled & LLDP_CAP_REPEATER)
			capstr = "Bridge";
		else
			capstr = "Host";
		if (!(
		      POKE_START_CDP_TLV(CDP_TLV_CAPABILITIES) &&
		      POKE_BYTES(capstr, strlen(capstr)) &&
		      POKE_END_CDP_TLV))
			goto toobig;
#endif
	}

	 
#ifdef ENABLE_DOT1
	if (version >=2 && hardware->h_lport.p_pvid != 0) {
		if (!(
		      POKE_START_CDP_TLV(CDP_TLV_NATIVEVLAN) &&
		      POKE_UINT16(hardware->h_lport.p_pvid) &&
		      POKE_END_CDP_TLV))
			goto toobig;
	}
#endif

	 
	if (!(
	      POKE_START_CDP_TLV(CDP_TLV_SOFTWARE) &&
	      (chassis->c_descr?
		  POKE_BYTES(chassis->c_descr, strlen(chassis->c_descr)):
		  POKE_BYTES("", 0)) &&
	      POKE_END_CDP_TLV))
		goto toobig;

	 
	if (global && global->g_config.c_platform) platform = global->g_config.c_platform;

	if (!(
	      POKE_START_CDP_TLV(CDP_TLV_PLATFORM) &&
	      POKE_BYTES(platform, strlen(platform)) &&
	      POKE_END_CDP_TLV))
		goto toobig;

#ifdef ENABLE_LLDPMED
	 
	if ((version >= 2) &&
	    port->p_med_cap_enabled &&
	    (port->p_med_power.source != LLDP_MED_POW_SOURCE_LOCAL) &&
	    (port->p_med_power.val > 0) &&
	    (port->p_med_power.val <= 655)) {
		if (!(
		      POKE_START_CDP_TLV(CDP_TLV_POWER_CONSUMPTION) &&
		      POKE_UINT16(port->p_med_power.val * 100) &&
		      POKE_END_CDP_TLV))
			goto toobig;
	}
#endif
	(void)POKE_SAVE(end);

	 
	POKE_RESTORE(pos_len_eh);
	if (!(POKE_UINT16(end - pos_llc))) goto toobig;
	checksum = frame_checksum(pos_cdp, end - pos_cdp, (version != 0) ? 1 : 0);
	POKE_RESTORE(pos_checksum);
	if (!(POKE_UINT16(checksum))) goto toobig;

	if (interfaces_send_helper(global, hardware,
		(char *)packet, end - packet) == -1) {
		log_warn("cdp", "unable to send packet on real device for %s",
			   hardware->h_ifname);
		free(packet);
		return ENETDOWN;
	}

	hardware->h_tx_cnt++;

	free(packet);
	return 0;
 toobig:
	free(packet);
	return -1;
}