1 /* -*- mode: c; c-file-style: "openbsd" -*- */
2 /*
3  * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 /* We also supports FDP which is very similar to CDPv1 */
19 #include "lldpd.h"
20 #include "frame.h"
21 
22 /*
23  * CDP Requests Power at the switch output and therefore has to take into
24  * account the loss in the PoE cable. This is done by the switch automatically
25  * if lldp is used as the protocol.
26  */
27 #define CDP_CLASS_3_MAX_PSE_POE	          154 /* 15.4W Max PoE at PSE class 3 */
28 #define CDP_SWTICH_DEFAULT_POE_PD         130 /* 13.W default PoE at PD */
29 #define CDP_SWTICH_DEFAULT_POE_PSE        154 /* 15.4W default PoE at PSE */
30 #define CDP_SWITCH_POE_CLASS_4_OFFSET     45  /* 4.5W  max loss from cable */
31 #define CDP_SWITCH_POE_CLASS_3_OFFSET     24  /* 2.4W  max loss from cable */
32 
33 #if defined (ENABLE_CDP) || defined (ENABLE_FDP)
34 
35 #include <stdio.h>
36 #include <unistd.h>
37 #include <errno.h>
38 #include <arpa/inet.h>
39 
40 static int
cdp_send(struct lldpd * global,struct lldpd_hardware * hardware,int version)41 cdp_send(struct lldpd *global,
42 	 struct lldpd_hardware *hardware, int version)
43 {
44 	const char *platform = "Unknown";
45 	struct lldpd_chassis *chassis;
46 	struct lldpd_mgmt *mgmt;
47 	struct lldpd_port *port;
48 	u_int8_t mcastaddr[] = CDP_MULTICAST_ADDR;
49 	u_int8_t llcorg[] = LLC_ORG_CISCO;
50 #ifdef ENABLE_FDP
51 	char *capstr;
52 #endif
53 	u_int16_t checksum;
54 	int length, i;
55 	u_int32_t cap;
56 	u_int8_t *packet;
57 	u_int8_t *pos, *pos_len_eh, *pos_llc, *pos_cdp, *pos_checksum, *tlv, *end;
58 
59 	log_debug("cdp", "send CDP frame on %s", hardware->h_ifname);
60 
61 	port = &(hardware->h_lport);
62 	chassis = port->p_chassis;
63 
64 #ifdef ENABLE_FDP
65 	if (version == 0) {
66 		/* With FDP, change multicast address and LLC PID */
67 		const u_int8_t fdpmcastaddr[] = FDP_MULTICAST_ADDR;
68 		const u_int8_t fdpllcorg[] = LLC_ORG_FOUNDRY;
69 		memcpy(mcastaddr, fdpmcastaddr, sizeof(mcastaddr));
70 		memcpy(llcorg, fdpllcorg, sizeof(llcorg));
71 	}
72 #endif
73 
74 	length = hardware->h_mtu;
75 	if ((packet = (u_int8_t*)calloc(1, length)) == NULL)
76 		return ENOMEM;
77 	pos = packet;
78 
79 	/* Ethernet header */
80 	if (!(
81 	      POKE_BYTES(mcastaddr, sizeof(mcastaddr)) &&
82 	      POKE_BYTES(&hardware->h_lladdr, ETHER_ADDR_LEN) &&
83 	      POKE_SAVE(pos_len_eh) && /* We compute the len later */
84 	      POKE_UINT16(0)))
85 		goto toobig;
86 
87 	/* LLC */
88 	if (!(
89 	      POKE_SAVE(pos_llc) &&
90 	      POKE_UINT8(0xaa) && /* SSAP */
91 	      POKE_UINT8(0xaa) && /* DSAP */
92 	      POKE_UINT8(0x03) && /* Control field */
93 	      POKE_BYTES(llcorg, sizeof(llcorg)) &&
94 	      POKE_UINT16(LLC_PID_CDP)))
95 		goto toobig;
96 
97 	/* CDP header */
98 	if (!(
99 	      POKE_SAVE(pos_cdp) &&
100 	      POKE_UINT8((version == 0)?1:version) &&
101 	      POKE_UINT8(global?global->g_config.c_ttl:180) &&
102 	      POKE_SAVE(pos_checksum) && /* Save checksum position */
103 	      POKE_UINT16(0)))
104 		goto toobig;
105 
106 	/* Chassis ID */
107 	const char *chassis_name = chassis->c_name?chassis->c_name:"";
108 	if (!(
109 	      POKE_START_CDP_TLV(CDP_TLV_CHASSIS) &&
110 	      POKE_BYTES(chassis_name, strlen(chassis_name)) &&
111 	      POKE_END_CDP_TLV))
112 		goto toobig;
113 
114 	/* Adresses */
115 	/* See:
116 	 *   http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.htm#xtocid12
117 	 *
118 	 * It seems that Cisco implies that CDP supports IPv6 using
119 	 * 802.2 address format with 0xAAAA03 0x000000 0x0800, but
120 	 * 0x0800 is the Ethernet protocol type for IPv4. Therefore,
121 	 * we support only IPv4. */
122 	i = 0;
123 	TAILQ_FOREACH(mgmt, &chassis->c_mgmt, m_entries)
124 		if (mgmt->m_family == LLDPD_AF_IPV4) i++;
125 	if (i > 0) {
126 		if (!(
127 		      POKE_START_CDP_TLV(CDP_TLV_ADDRESSES) &&
128 		      POKE_UINT32(i)))
129 			goto toobig;
130 		TAILQ_FOREACH(mgmt, &chassis->c_mgmt, m_entries) {
131 			switch (mgmt->m_family) {
132 			case LLDPD_AF_IPV4:
133 				if (!(
134 				      POKE_UINT8(1) &&	/* Type: NLPID */
135 				      POKE_UINT8(1) &&  /* Length: 1 */
136 				      POKE_UINT8(CDP_ADDRESS_PROTO_IP) && /* IP */
137 				      POKE_UINT16(sizeof(struct in_addr)) && /* Address length */
138 				      POKE_BYTES(&mgmt->m_addr, sizeof(struct in_addr))))
139 					goto toobig;
140 				break;
141 			}
142 		}
143 		if (!(POKE_END_CDP_TLV))
144 			goto toobig;
145 	}
146 
147 	/* Port ID */
148 	const char *port_descr = hardware->h_lport.p_descr?hardware->h_lport.p_descr:"";
149 	if (!(
150 	      POKE_START_CDP_TLV(CDP_TLV_PORT) &&
151 	      POKE_BYTES(port_descr, strlen(port_descr)) &&
152 	      POKE_END_CDP_TLV))
153 		goto toobig;
154 
155 	/* Capabilities */
156 	if (version != 0) {
157 		cap = 0;
158 		if (chassis->c_cap_enabled & LLDP_CAP_ROUTER)
159 			cap |= CDP_CAP_ROUTER;
160 		if (chassis->c_cap_enabled & LLDP_CAP_BRIDGE)
161 			cap |= CDP_CAP_SWITCH;
162 		cap |= CDP_CAP_HOST;
163 		if (!(
164 		      POKE_START_CDP_TLV(CDP_TLV_CAPABILITIES) &&
165 		      POKE_UINT32(cap) &&
166 		      POKE_END_CDP_TLV))
167 			goto toobig;
168 #ifdef ENABLE_FDP
169 	} else {
170 		/* With FDP, it seems that a string is used in place of an int */
171 		if (chassis->c_cap_enabled & LLDP_CAP_ROUTER)
172 			capstr = "Router";
173 		else if (chassis->c_cap_enabled & LLDP_CAP_BRIDGE)
174 			capstr = "Switch";
175 		else if (chassis->c_cap_enabled & LLDP_CAP_REPEATER)
176 			capstr = "Bridge";
177 		else
178 			capstr = "Host";
179 		if (!(
180 		      POKE_START_CDP_TLV(CDP_TLV_CAPABILITIES) &&
181 		      POKE_BYTES(capstr, strlen(capstr)) &&
182 		      POKE_END_CDP_TLV))
183 			goto toobig;
184 #endif
185 	}
186 
187 	/* Native VLAN */
188 #ifdef ENABLE_DOT1
189 	if (version >=2 && hardware->h_lport.p_pvid != 0) {
190 		if (!(
191 		      POKE_START_CDP_TLV(CDP_TLV_NATIVEVLAN) &&
192 		      POKE_UINT16(hardware->h_lport.p_pvid) &&
193 		      POKE_END_CDP_TLV))
194 			goto toobig;
195 	}
196 #endif
197 
198 	/* Software version */
199 	const char * chassis_descr = chassis->c_descr?chassis->c_descr:"";
200 	if (!(
201 	      POKE_START_CDP_TLV(CDP_TLV_SOFTWARE) &&
202 	      POKE_BYTES(chassis_descr, strlen(chassis_descr)) &&
203 	      POKE_END_CDP_TLV))
204 		goto toobig;
205 
206 	/* Platform */
207 	if (global && global->g_config.c_platform) platform = global->g_config.c_platform;
208 
209 	if (!(
210 	      POKE_START_CDP_TLV(CDP_TLV_PLATFORM) &&
211 	      POKE_BYTES(platform, strlen(platform)) &&
212 	      POKE_END_CDP_TLV))
213 		goto toobig;
214 
215 #ifdef ENABLE_DOT3
216 	if ((version >= 2) &&
217 	    (port->p_power.powertype != LLDP_DOT3_POWER_8023AT_OFF) &&
218 	    (port->p_power.devicetype == LLDP_DOT3_POWER_PD) &&
219 	    (port->p_power.requested > 0) &&
220 	    (port->p_power.requested <= 655)) {
221 		u_int16_t requested;
222 		u_int16_t consumption;
223 
224 		if (port->p_power.requested != port->p_power.allocated) {
225 			port->p_cdp_power.request_id++;
226 			log_debug("cdp", "requested: %d, allocated:%d", port->p_power.requested, port->p_power.allocated);
227 		}
228 		consumption = port->p_power.allocated ? port->p_power.allocated : CDP_SWTICH_DEFAULT_POE_PD;
229 		if (consumption > 130) {
230 			consumption += CDP_SWITCH_POE_CLASS_4_OFFSET;
231 		} else {
232 			consumption += CDP_SWITCH_POE_CLASS_3_OFFSET;
233 		}
234 		if (port->p_power.requested > 130) { /* Class 4 */
235 			requested = port->p_power.requested + CDP_SWITCH_POE_CLASS_4_OFFSET;
236 		} else { /* Class 3 */
237 			requested = port->p_power.requested + CDP_SWITCH_POE_CLASS_3_OFFSET;
238 		}
239 		if (!(
240 		      POKE_START_CDP_TLV(CDP_TLV_POWER_CONSUMPTION) &&
241 		      POKE_UINT16(consumption * 100) &&
242 		      POKE_END_CDP_TLV))
243 			goto toobig;
244 		/* Avoid request id 0 from overflow */
245 		if (!port->p_cdp_power.request_id) {
246 			port->p_cdp_power.request_id = 1;
247 		}
248 		if (!port->p_cdp_power.management_id) {
249 			port->p_cdp_power.management_id = 1;
250 		}
251 		if (!(
252 		      POKE_START_CDP_TLV(CDP_TLV_POWER_REQUESTED) &&
253 		      POKE_UINT16(port->p_cdp_power.request_id) &&
254 		      POKE_UINT16(port->p_cdp_power.management_id) &&
255 		      POKE_UINT32(requested * 100) &&
256 		      POKE_END_CDP_TLV))
257 			goto toobig;
258 	}
259 #elif defined(ENABLE_LLDPMED)
260 	/* Power use */
261 	if ((version >= 2) &&
262 	    port->p_med_cap_enabled &&
263 	    (port->p_med_power.source != LLDP_MED_POW_SOURCE_LOCAL) &&
264 	    (port->p_med_power.val > 0) &&
265 	    (port->p_med_power.val <= 655)) {
266 		if (!(
267 		      POKE_START_CDP_TLV(CDP_TLV_POWER_CONSUMPTION) &&
268 		      POKE_UINT16(port->p_med_power.val * 100) &&
269 		      POKE_END_CDP_TLV))
270 			goto toobig;
271 	}
272 #endif
273 
274 	(void)POKE_SAVE(end);
275 
276 	/* Compute len and checksum */
277 	POKE_RESTORE(pos_len_eh);
278 	if (!(POKE_UINT16(end - pos_llc))) goto toobig;
279 	checksum = frame_checksum(pos_cdp, end - pos_cdp, (version != 0) ? 1 : 0);
280 	POKE_RESTORE(pos_checksum);
281 	if (!(POKE_UINT16(checksum))) goto toobig;
282 
283 	if (interfaces_send_helper(global, hardware,
284 		(char *)packet, end - packet) == -1) {
285 		log_warn("cdp", "unable to send packet on real device for %s",
286 			   hardware->h_ifname);
287 		free(packet);
288 		return ENETDOWN;
289 	}
290 
291 	hardware->h_tx_cnt++;
292 
293 	free(packet);
294 	return 0;
295  toobig:
296 	free(packet);
297 	return -1;
298 }
299 
300 #define CHECK_TLV_SIZE(x, name)				   \
301 	do { if (tlv_len < (x)) {			   \
302 		log_warnx("cdp", name " CDP/FDP TLV too short received on %s", \
303 	       hardware->h_ifname);			   \
304 	   goto malformed;				   \
305 	} } while (0)
306 /* cdp_decode also decodes FDP */
307 int
cdp_decode(struct lldpd * cfg,char * frame,int s,struct lldpd_hardware * hardware,struct lldpd_chassis ** newchassis,struct lldpd_port ** newport)308 cdp_decode(struct lldpd *cfg, char *frame, int s,
309     struct lldpd_hardware *hardware,
310     struct lldpd_chassis **newchassis, struct lldpd_port **newport)
311 {
312 	struct lldpd_chassis *chassis;
313 	struct lldpd_port *port;
314 	struct lldpd_mgmt *mgmt;
315 	struct in_addr addr;
316 #if 0
317 	u_int16_t cksum;
318 #endif
319 	u_int8_t *software = NULL, *platform = NULL;
320 	int software_len = 0, platform_len = 0, proto, version, nb, caps;
321 	const unsigned char cdpaddr[] = CDP_MULTICAST_ADDR;
322 #ifdef ENABLE_FDP
323 	const unsigned char fdpaddr[] = CDP_MULTICAST_ADDR;
324 	int fdp = 0;
325 #endif
326 	u_int8_t *pos, *tlv, *pos_address, *pos_next_address;
327 	int length, len_eth, tlv_type, tlv_len, addresses_len, address_len;
328 #ifdef ENABLE_DOT1
329 	struct lldpd_vlan *vlan;
330 #endif
331 
332 	log_debug("cdp", "decode CDP frame received on %s",
333 	    hardware->h_ifname);
334 
335 	if ((chassis = calloc(1, sizeof(struct lldpd_chassis))) == NULL) {
336 		log_warn("cdp", "failed to allocate remote chassis");
337 		return -1;
338 	}
339 	TAILQ_INIT(&chassis->c_mgmt);
340 	if ((port = calloc(1, sizeof(struct lldpd_port))) == NULL) {
341 		log_warn("cdp", "failed to allocate remote port");
342 		free(chassis);
343 		return -1;
344 	}
345 #ifdef ENABLE_DOT1
346 	TAILQ_INIT(&port->p_vlans);
347 #endif
348 
349 	length = s;
350 	pos = (u_int8_t*)frame;
351 
352 	if (length < 2*ETHER_ADDR_LEN + sizeof(u_int16_t) /* Ethernet */ +
353 	    8 /* LLC */ + 4 /* CDP header */) {
354 		log_warn("cdp", "too short CDP/FDP frame received on %s", hardware->h_ifname);
355 		goto malformed;
356 	}
357 
358 	if (PEEK_CMP(cdpaddr, sizeof(cdpaddr)) != 0) {
359 #ifdef ENABLE_FDP
360 		PEEK_RESTORE((u_int8_t*)frame);
361 		if (PEEK_CMP(fdpaddr, sizeof(fdpaddr)) != 0)
362 			fdp = 1;
363 		else {
364 #endif
365 			log_info("cdp", "frame not targeted at CDP/FDP multicast address received on %s",
366 			    hardware->h_ifname);
367 			goto malformed;
368 #ifdef ENABLE_FDP
369 		}
370 #endif
371 	}
372 	PEEK_DISCARD(ETHER_ADDR_LEN);	/* Don't care of source address */
373 	len_eth = PEEK_UINT16;
374 	if (len_eth > length) {
375 		log_warnx("cdp", "incorrect 802.3 frame size reported on %s",
376 		    hardware->h_ifname);
377 		goto malformed;
378 	}
379 
380 	/* This is the correct length of the CDP + LLC packets */
381 	length = len_eth;
382 
383 	PEEK_DISCARD(6);	/* Skip beginning of LLC */
384 	proto = PEEK_UINT16;
385 	if (proto != LLC_PID_CDP) {
386 		if ((proto != LLC_PID_DRIP) &&
387 		    (proto != LLC_PID_PAGP) &&
388 		    (proto != LLC_PID_PVSTP) &&
389 		    (proto != LLC_PID_UDLD) &&
390 		    (proto != LLC_PID_VTP) &&
391 		    (proto != LLC_PID_DTP) &&
392 		    (proto != LLC_PID_STP))
393 			log_debug("cdp", "incorrect LLC protocol ID received on %s",
394 			    hardware->h_ifname);
395 		goto malformed;
396 	}
397 
398 #if 0
399 	/* Check checksum */
400 	cksum = frame_checksum(pos, len_eth - 8,
401 #ifdef ENABLE_FDP
402 	    !fdp		/* fdp = 0 -> cisco checksum */
403 #else
404 	    1			/* cisco checksum */
405 #endif
406 		);
407 	if (cksum != 0) {
408 		log_info("cdp", "incorrect CDP/FDP checksum for frame received on %s (%d)",
409 			  hardware->h_ifname, cksum);
410 		goto malformed;
411 	}
412 #endif
413 
414 	/* Check version */
415 	version = PEEK_UINT8;
416 	if ((version != 1) && (version != 2)) {
417 		log_warnx("cdp", "incorrect CDP/FDP version (%d) for frame received on %s",
418 		    version, hardware->h_ifname);
419 		goto malformed;
420 	}
421 	port->p_ttl = PEEK_UINT8; /* TTL */
422 	PEEK_DISCARD_UINT16;	     /* Checksum, already checked */
423 
424 	while (length) {
425 		if (length < 4) {
426 			log_warnx("cdp", "CDP/FDP TLV header is too large for "
427 			    "frame received on %s",
428 			    hardware->h_ifname);
429 			goto malformed;
430 		}
431 		tlv_type = PEEK_UINT16;
432 		tlv_len = PEEK_UINT16 - 4;
433 
434 		(void)PEEK_SAVE(tlv);
435 		if ((tlv_len < 0) || (length < tlv_len)) {
436 			log_warnx("cdp", "incorrect size in CDP/FDP TLV header for frame "
437 			    "received on %s",
438 			    hardware->h_ifname);
439 			goto malformed;
440 		}
441 		switch (tlv_type) {
442 		case CDP_TLV_CHASSIS:
443 			if ((chassis->c_name = (char *)calloc(1, tlv_len + 1)) == NULL) {
444 				log_warn("cdp", "unable to allocate memory for chassis name");
445 				goto malformed;
446 			}
447 			PEEK_BYTES(chassis->c_name, tlv_len);
448 			chassis->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LOCAL;
449 			if ((chassis->c_id =  (char *)malloc(tlv_len)) == NULL) {
450 				log_warn("cdp", "unable to allocate memory for chassis ID");
451 				goto malformed;
452 			}
453 			memcpy(chassis->c_id, chassis->c_name, tlv_len);
454 			chassis->c_id_len = tlv_len;
455 			break;
456 		case CDP_TLV_ADDRESSES:
457 			CHECK_TLV_SIZE(4, "Address");
458 			addresses_len = tlv_len - 4;
459 			for (nb = PEEK_UINT32; nb > 0; nb--) {
460 				(void)PEEK_SAVE(pos_address);
461 				/* We first try to get the real length of the packet */
462 				if (addresses_len < 2) {
463 					log_warn("cdp", "too short address subframe "
464 						  "received on %s",
465 						  hardware->h_ifname);
466 					goto malformed;
467 				}
468 				PEEK_DISCARD_UINT8; addresses_len--;
469 				address_len = PEEK_UINT8; addresses_len--;
470 				if (addresses_len < address_len + 2) {
471 					log_warn("cdp", "too short address subframe "
472 						  "received on %s",
473 						  hardware->h_ifname);
474 					goto malformed;
475 				}
476 				PEEK_DISCARD(address_len);
477 				addresses_len -= address_len;
478 				address_len = PEEK_UINT16; addresses_len -= 2;
479 				if (addresses_len < address_len) {
480 					log_warn("cdp", "too short address subframe "
481 						  "received on %s",
482 						  hardware->h_ifname);
483 					goto malformed;
484 				}
485 				PEEK_DISCARD(address_len);
486 				(void)PEEK_SAVE(pos_next_address);
487 				/* Next, we go back and try to extract
488 				   IPv4 address */
489 				PEEK_RESTORE(pos_address);
490 				if ((PEEK_UINT8 == 1) && (PEEK_UINT8 == 1) &&
491 				    (PEEK_UINT8 == CDP_ADDRESS_PROTO_IP) &&
492 				    (PEEK_UINT16 == sizeof(struct in_addr))) {
493 						PEEK_BYTES(&addr, sizeof(struct in_addr));
494 						mgmt = lldpd_alloc_mgmt(LLDPD_AF_IPV4, &addr,
495 									sizeof(struct in_addr), 0);
496 						if (mgmt == NULL) {
497 							if (errno == ENOMEM)
498 								log_warn("cdp",
499 								    "unable to allocate memory for management address");
500 							else
501 								log_warn("cdp",
502 								    "too large management address received on %s",
503 								    hardware->h_ifname);
504 							goto malformed;
505 						}
506 						TAILQ_INSERT_TAIL(&chassis->c_mgmt, mgmt, m_entries);
507 				}
508 				/* Go to the end of the address */
509 				PEEK_RESTORE(pos_next_address);
510 			}
511 			break;
512 		case CDP_TLV_PORT:
513 			if (tlv_len == 0) {
514 				log_warn("cdp", "too short port description received");
515 				goto malformed;
516 			}
517 			if ((port->p_descr = (char *)calloc(1, tlv_len + 1)) == NULL) {
518 				log_warn("cdp", "unable to allocate memory for port description");
519 				goto malformed;
520 			}
521 			PEEK_BYTES(port->p_descr, tlv_len);
522 			port->p_id_subtype = LLDP_PORTID_SUBTYPE_IFNAME;
523 			if ((port->p_id =  (char *)calloc(1, tlv_len)) == NULL) {
524 				log_warn("cdp", "unable to allocate memory for port ID");
525 				goto malformed;
526 			}
527 			memcpy(port->p_id, port->p_descr, tlv_len);
528 			port->p_id_len = tlv_len;
529 			break;
530 		case CDP_TLV_CAPABILITIES:
531 #ifdef ENABLE_FDP
532 			if (fdp) {
533 				/* Capabilities are string with FDP */
534 				if (!strncmp("Router", (char*)pos, tlv_len))
535 					chassis->c_cap_enabled = LLDP_CAP_ROUTER;
536 				else if (!strncmp("Switch", (char*)pos, tlv_len))
537 					chassis->c_cap_enabled = LLDP_CAP_BRIDGE;
538 				else if (!strncmp("Bridge", (char*)pos, tlv_len))
539 					chassis->c_cap_enabled = LLDP_CAP_REPEATER;
540 				else
541 					chassis->c_cap_enabled = LLDP_CAP_STATION;
542 				chassis->c_cap_available = chassis->c_cap_enabled;
543 				break;
544 			}
545 #endif
546 			CHECK_TLV_SIZE(4, "Capabilities");
547 			caps = PEEK_UINT32;
548 			if (caps & CDP_CAP_ROUTER)
549 				chassis->c_cap_enabled |= LLDP_CAP_ROUTER;
550 			if (caps & 0x0e)
551 				chassis->c_cap_enabled |= LLDP_CAP_BRIDGE;
552 			if (chassis->c_cap_enabled == 0)
553 				chassis->c_cap_enabled = LLDP_CAP_STATION;
554 			chassis->c_cap_available = chassis->c_cap_enabled;
555 			break;
556 		case CDP_TLV_SOFTWARE:
557 			software_len = tlv_len;
558 			(void)PEEK_SAVE(software);
559 			break;
560 		case CDP_TLV_PLATFORM:
561 			platform_len = tlv_len;
562 			(void)PEEK_SAVE(platform);
563 			break;
564 #ifdef ENABLE_DOT1
565 		case CDP_TLV_NATIVEVLAN:
566 			CHECK_TLV_SIZE(2, "Native VLAN");
567 			if ((vlan = (struct lldpd_vlan *)calloc(1,
568 				sizeof(struct lldpd_vlan))) == NULL) {
569 				log_warn("cdp", "unable to alloc vlan "
570 					  "structure for "
571 					  "tlv received on %s",
572 					  hardware->h_ifname);
573 				goto malformed;
574 			}
575 			vlan->v_vid = port->p_pvid = PEEK_UINT16;
576 			if (asprintf(&vlan->v_name, "VLAN #%d", vlan->v_vid) == -1) {
577 				log_warn("cdp", "unable to alloc VLAN name for "
578 					  "TLV received on %s",
579 					  hardware->h_ifname);
580 				free(vlan);
581 				goto malformed;
582 			}
583 			TAILQ_INSERT_TAIL(&port->p_vlans,
584 					  vlan, v_entries);
585 			break;
586 #endif
587 #ifdef ENABLE_DOT3
588 		case CDP_TLV_POWER_AVAILABLE:
589 			CHECK_TLV_SIZE(12, "Power Available");
590 			/* check if it is a respone to a request id */
591 			if (PEEK_UINT16 > 0) {
592 				port->p_cdp_power.management_id = PEEK_UINT16;
593 				port->p_power.allocated = PEEK_UINT32;
594 				port->p_power.allocated /= 100;
595 				port->p_power.supported = 1;
596 				port->p_power.enabled = 1;
597 				port->p_power.devicetype = LLDP_DOT3_POWER_PSE;
598 				port->p_power.powertype = LLDP_DOT3_POWER_8023AT_TYPE2;
599 				log_debug("cdp", "Allocated power %d00", port->p_power.allocated);
600 				if (port->p_power.allocated > CDP_CLASS_3_MAX_PSE_POE) {
601 					port->p_power.allocated -= CDP_SWITCH_POE_CLASS_4_OFFSET;
602 				} else if (port->p_power.allocated > CDP_SWITCH_POE_CLASS_3_OFFSET ) {
603 					port->p_power.allocated -= CDP_SWITCH_POE_CLASS_3_OFFSET;
604 				} else {
605 					port->p_power.allocated = 0;
606 				}
607 				port->p_power.requested = hardware->h_lport.p_power.requested;
608 			}
609 			break;
610 #endif
611 		default:
612 			log_debug("cdp", "unknown CDP/FDP TLV type (%d) received on %s",
613 			    ntohs(tlv_type), hardware->h_ifname);
614 			hardware->h_rx_unrecognized_cnt++;
615 		}
616 		PEEK_DISCARD(tlv + tlv_len - pos);
617 	}
618 	if (!software && platform) {
619 		if ((chassis->c_descr = (char *)calloc(1,
620 			    platform_len + 1)) == NULL) {
621 			log_warn("cdp", "unable to allocate memory for chassis description");
622 			goto malformed;
623 		}
624 		memcpy(chassis->c_descr, platform, platform_len);
625 	} else if (software && !platform) {
626 		if ((chassis->c_descr = (char *)calloc(1,
627 			    software_len + 1)) == NULL) {
628 			log_warn("cdp", "unable to allocate memory for chassis description");
629 			goto malformed;
630 		}
631 		memcpy(chassis->c_descr, software, software_len);
632 	} else if (software && platform) {
633 #define CONCAT_PLATFORM " running on\n"
634 		if ((chassis->c_descr = (char *)calloc(1,
635 			    software_len + platform_len +
636 			    strlen(CONCAT_PLATFORM) + 1)) == NULL) {
637 			log_warn("cdp", "unable to allocate memory for chassis description");
638 			goto malformed;
639 		}
640 		memcpy(chassis->c_descr, platform, platform_len);
641 		memcpy(chassis->c_descr + platform_len,
642 		    CONCAT_PLATFORM, strlen(CONCAT_PLATFORM));
643 		memcpy(chassis->c_descr + platform_len + strlen(CONCAT_PLATFORM),
644 		    software, software_len);
645 	}
646 	if ((chassis->c_id == NULL) ||
647 	    (port->p_id == NULL) ||
648 	    (chassis->c_name == NULL) ||
649 	    (chassis->c_descr == NULL) ||
650 	    (port->p_descr == NULL) ||
651 	    (port->p_ttl == 0) ||
652 	    (chassis->c_cap_enabled == 0)) {
653 		log_warnx("cdp", "some mandatory CDP/FDP tlv are missing for frame received on %s",
654 		    hardware->h_ifname);
655 		goto malformed;
656 	}
657 	*newchassis = chassis;
658 	*newport = port;
659 	return 1;
660 
661 malformed:
662 	lldpd_chassis_cleanup(chassis, 1);
663 	lldpd_port_cleanup(port, 1);
664 	free(port);
665 	return -1;
666 }
667 
668 #ifdef ENABLE_CDP
669 int
cdpv1_send(struct lldpd * global,struct lldpd_hardware * hardware)670 cdpv1_send(struct lldpd *global,
671     struct lldpd_hardware *hardware)
672 {
673 	return cdp_send(global, hardware, 1);
674 }
675 
676 int
cdpv2_send(struct lldpd * global,struct lldpd_hardware * hardware)677 cdpv2_send(struct lldpd *global,
678     struct lldpd_hardware *hardware)
679 {
680 	return cdp_send(global, hardware, 2);
681 }
682 #endif
683 
684 #ifdef ENABLE_FDP
685 int
fdp_send(struct lldpd * global,struct lldpd_hardware * hardware)686 fdp_send(struct lldpd *global,
687     struct lldpd_hardware *hardware)
688 {
689 	return cdp_send(global, hardware, 0);
690 }
691 #endif
692 
693 #ifdef ENABLE_CDP
694 static int
cdp_guess(char * pos,int length,int version)695 cdp_guess(char *pos, int length, int version)
696 {
697 	const u_int8_t mcastaddr[] = CDP_MULTICAST_ADDR;
698 	if (length < 2*ETHER_ADDR_LEN + sizeof(u_int16_t) /* Ethernet */ +
699 	    8 /* LLC */ + 4 /* CDP header */)
700 		return 0;
701 	if (PEEK_CMP(mcastaddr, ETHER_ADDR_LEN) != 0)
702 		return 0;
703 	PEEK_DISCARD(ETHER_ADDR_LEN); PEEK_DISCARD_UINT16; /* Ethernet */
704 	PEEK_DISCARD(8);			     /* LLC */
705 	return (PEEK_UINT8 == version);
706 }
707 
708 int
cdpv1_guess(char * frame,int len)709 cdpv1_guess(char *frame, int len)
710 {
711 	return cdp_guess(frame, len, 1);
712 }
713 
714 int
cdpv2_guess(char * frame,int len)715 cdpv2_guess(char *frame, int len)
716 {
717 	return cdp_guess(frame, len, 2);
718 }
719 #endif
720 
721 #endif /* defined (ENABLE_CDP) || defined (ENABLE_FDP) */
722