1 /* -*- mode: c; c-file-style: "openbsd" -*- */
3 * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
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.
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.
18 /* We also supports FDP which is very similar to CDPv1 */
22 #if defined (ENABLE_CDP) || defined (ENABLE_FDP)
27 #include <arpa/inet.h>
30 cdp_send(struct lldpd
*global
,
31 struct lldpd_hardware
*hardware
, int version
)
33 const char *platform
= "Unknown";
34 struct lldpd_chassis
*chassis
;
35 struct lldpd_mgmt
*mgmt
;
36 struct lldpd_port
*port
;
37 u_int8_t mcastaddr
[] = CDP_MULTICAST_ADDR
;
38 u_int8_t llcorg
[] = LLC_ORG_CISCO
;
46 u_int8_t
*pos
, *pos_len_eh
, *pos_llc
, *pos_cdp
, *pos_checksum
, *tlv
, *end
;
48 log_debug("cdp", "send CDP frame on %s", hardware
->h_ifname
);
50 port
= &(hardware
->h_lport
);
51 chassis
= port
->p_chassis
;
55 /* With FDP, change multicast address and LLC PID */
56 const u_int8_t fdpmcastaddr
[] = FDP_MULTICAST_ADDR
;
57 const u_int8_t fdpllcorg
[] = LLC_ORG_FOUNDRY
;
58 memcpy(mcastaddr
, fdpmcastaddr
, sizeof(mcastaddr
));
59 memcpy(llcorg
, fdpllcorg
, sizeof(llcorg
));
63 length
= hardware
->h_mtu
;
64 if ((packet
= (u_int8_t
*)calloc(1, length
)) == NULL
)
70 POKE_BYTES(mcastaddr
, sizeof(mcastaddr
)) &&
71 POKE_BYTES(&hardware
->h_lladdr
, ETHER_ADDR_LEN
) &&
72 POKE_SAVE(pos_len_eh
) && /* We compute the len later */
79 POKE_UINT8(0xaa) && /* SSAP */
80 POKE_UINT8(0xaa) && /* DSAP */
81 POKE_UINT8(0x03) && /* Control field */
82 POKE_BYTES(llcorg
, sizeof(llcorg
)) &&
83 POKE_UINT16(LLC_PID_CDP
)))
89 POKE_UINT8((version
== 0)?1:version
) &&
90 POKE_UINT8(chassis
->c_ttl
) &&
91 POKE_SAVE(pos_checksum
) && /* Save checksum position */
96 const char *chassis_name
= chassis
->c_name
?chassis
->c_name
:"";
98 POKE_START_CDP_TLV(CDP_TLV_CHASSIS
) &&
99 POKE_BYTES(chassis_name
, strlen(chassis_name
)) &&
105 * http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.htm#xtocid12
107 * It seems that Cisco implies that CDP supports IPv6 using
108 * 802.2 address format with 0xAAAA03 0x000000 0x0800, but
109 * 0x0800 is the Ethernet protocol type for IPv4. Therefore,
110 * we support only IPv4. */
112 TAILQ_FOREACH(mgmt
, &chassis
->c_mgmt
, m_entries
)
113 if (mgmt
->m_family
== LLDPD_AF_IPV4
) i
++;
116 POKE_START_CDP_TLV(CDP_TLV_ADDRESSES
) &&
119 TAILQ_FOREACH(mgmt
, &chassis
->c_mgmt
, m_entries
) {
120 switch (mgmt
->m_family
) {
123 POKE_UINT8(1) && /* Type: NLPID */
124 POKE_UINT8(1) && /* Length: 1 */
125 POKE_UINT8(CDP_ADDRESS_PROTO_IP
) && /* IP */
126 POKE_UINT16(sizeof(struct in_addr
)) && /* Address length */
127 POKE_BYTES(&mgmt
->m_addr
, sizeof(struct in_addr
))))
132 if (!(POKE_END_CDP_TLV
))
137 const char *port_descr
= hardware
->h_lport
.p_descr
?hardware
->h_lport
.p_descr
:"";
139 POKE_START_CDP_TLV(CDP_TLV_PORT
) &&
140 POKE_BYTES(port_descr
, strlen(port_descr
)) &&
147 if (chassis
->c_cap_enabled
& LLDP_CAP_ROUTER
)
148 cap
|= CDP_CAP_ROUTER
;
149 if (chassis
->c_cap_enabled
& LLDP_CAP_BRIDGE
)
150 cap
|= CDP_CAP_SWITCH
;
153 POKE_START_CDP_TLV(CDP_TLV_CAPABILITIES
) &&
159 /* With FDP, it seems that a string is used in place of an int */
160 if (chassis
->c_cap_enabled
& LLDP_CAP_ROUTER
)
162 else if (chassis
->c_cap_enabled
& LLDP_CAP_BRIDGE
)
164 else if (chassis
->c_cap_enabled
& LLDP_CAP_REPEATER
)
169 POKE_START_CDP_TLV(CDP_TLV_CAPABILITIES
) &&
170 POKE_BYTES(capstr
, strlen(capstr
)) &&
178 if (version
>=2 && hardware
->h_lport
.p_pvid
!= 0) {
180 POKE_START_CDP_TLV(CDP_TLV_NATIVEVLAN
) &&
181 POKE_UINT16(hardware
->h_lport
.p_pvid
) &&
187 /* Software version */
188 const char * chassis_descr
= chassis
->c_descr
?chassis
->c_descr
:"";
190 POKE_START_CDP_TLV(CDP_TLV_SOFTWARE
) &&
191 POKE_BYTES(chassis_descr
, strlen(chassis_descr
)) &&
196 if (global
&& global
->g_config
.c_platform
) platform
= global
->g_config
.c_platform
;
199 POKE_START_CDP_TLV(CDP_TLV_PLATFORM
) &&
200 POKE_BYTES(platform
, strlen(platform
)) &&
204 #ifdef ENABLE_LLDPMED
206 if ((version
>= 2) &&
207 port
->p_med_cap_enabled
&&
208 (port
->p_med_power
.source
!= LLDP_MED_POW_SOURCE_LOCAL
) &&
209 (port
->p_med_power
.val
> 0) &&
210 (port
->p_med_power
.val
<= 655)) {
212 POKE_START_CDP_TLV(CDP_TLV_POWER_CONSUMPTION
) &&
213 POKE_UINT16(port
->p_med_power
.val
* 100) &&
218 (void)POKE_SAVE(end
);
220 /* Compute len and checksum */
221 POKE_RESTORE(pos_len_eh
);
222 if (!(POKE_UINT16(end
- pos_llc
))) goto toobig
;
223 checksum
= frame_checksum(pos_cdp
, end
- pos_cdp
, (version
!= 0) ? 1 : 0);
224 POKE_RESTORE(pos_checksum
);
225 if (!(POKE_UINT16(checksum
))) goto toobig
;
227 if (interfaces_send_helper(global
, hardware
,
228 (char *)packet
, end
- packet
) == -1) {
229 log_warn("cdp", "unable to send packet on real device for %s",
235 hardware
->h_tx_cnt
++;
244 #define CHECK_TLV_SIZE(x, name) \
245 do { if (tlv_len < (x)) { \
246 log_warnx("cdp", name " CDP/FDP TLV too short received on %s", \
247 hardware->h_ifname); \
250 /* cdp_decode also decodes FDP */
252 cdp_decode(struct lldpd
*cfg
, char *frame
, int s
,
253 struct lldpd_hardware
*hardware
,
254 struct lldpd_chassis
**newchassis
, struct lldpd_port
**newport
)
256 struct lldpd_chassis
*chassis
;
257 struct lldpd_port
*port
;
258 struct lldpd_mgmt
*mgmt
;
263 u_int8_t
*software
= NULL
, *platform
= NULL
;
264 int software_len
= 0, platform_len
= 0, proto
, version
, nb
, caps
;
265 const unsigned char cdpaddr
[] = CDP_MULTICAST_ADDR
;
267 const unsigned char fdpaddr
[] = CDP_MULTICAST_ADDR
;
270 u_int8_t
*pos
, *tlv
, *pos_address
, *pos_next_address
;
271 int length
, len_eth
, tlv_type
, tlv_len
, addresses_len
, address_len
;
273 struct lldpd_vlan
*vlan
;
276 log_debug("cdp", "decode CDP frame received on %s",
279 if ((chassis
= calloc(1, sizeof(struct lldpd_chassis
))) == NULL
) {
280 log_warn("cdp", "failed to allocate remote chassis");
283 TAILQ_INIT(&chassis
->c_mgmt
);
284 if ((port
= calloc(1, sizeof(struct lldpd_port
))) == NULL
) {
285 log_warn("cdp", "failed to allocate remote port");
290 TAILQ_INIT(&port
->p_vlans
);
294 pos
= (u_int8_t
*)frame
;
296 if (length
< 2*ETHER_ADDR_LEN
+ sizeof(u_int16_t
) /* Ethernet */ +
297 8 /* LLC */ + 4 /* CDP header */) {
298 log_warn("cdp", "too short CDP/FDP frame received on %s", hardware
->h_ifname
);
302 if (PEEK_CMP(cdpaddr
, sizeof(cdpaddr
)) != 0) {
304 PEEK_RESTORE((u_int8_t
*)frame
);
305 if (PEEK_CMP(fdpaddr
, sizeof(fdpaddr
)) != 0)
309 log_info("cdp", "frame not targeted at CDP/FDP multicast address received on %s",
316 PEEK_DISCARD(ETHER_ADDR_LEN
); /* Don't care of source address */
317 len_eth
= PEEK_UINT16
;
318 if (len_eth
> length
) {
319 log_warnx("cdp", "incorrect 802.3 frame size reported on %s",
323 PEEK_DISCARD(6); /* Skip beginning of LLC */
325 if (proto
!= LLC_PID_CDP
) {
326 if ((proto
!= LLC_PID_DRIP
) &&
327 (proto
!= LLC_PID_PAGP
) &&
328 (proto
!= LLC_PID_PVSTP
) &&
329 (proto
!= LLC_PID_UDLD
) &&
330 (proto
!= LLC_PID_VTP
) &&
331 (proto
!= LLC_PID_DTP
) &&
332 (proto
!= LLC_PID_STP
))
333 log_debug("cdp", "incorrect LLC protocol ID received on %s",
340 cksum
= frame_checksum(pos
, len_eth
- 8,
342 !fdp
/* fdp = 0 -> cisco checksum */
344 1 /* cisco checksum */
348 log_info("cdp", "incorrect CDP/FDP checksum for frame received on %s (%d)",
349 hardware
->h_ifname
, cksum
);
355 version
= PEEK_UINT8
;
356 if ((version
!= 1) && (version
!= 2)) {
357 log_warnx("cdp", "incorrect CDP/FDP version (%d) for frame received on %s",
358 version
, hardware
->h_ifname
);
361 port
->p_ttl
= PEEK_UINT8
; /* TTL */
362 PEEK_DISCARD_UINT16
; /* Checksum, already checked */
366 log_warnx("cdp", "CDP/FDP TLV header is too large for "
367 "frame received on %s",
371 tlv_type
= PEEK_UINT16
;
372 tlv_len
= PEEK_UINT16
- 4;
373 (void)PEEK_SAVE(tlv
);
374 if ((tlv_len
< 0) || (length
< tlv_len
)) {
375 log_warnx("cdp", "incorrect size in CDP/FDP TLV header for frame "
381 case CDP_TLV_CHASSIS
:
382 if ((chassis
->c_name
= (char *)calloc(1, tlv_len
+ 1)) == NULL
) {
383 log_warn("cdp", "unable to allocate memory for chassis name");
386 PEEK_BYTES(chassis
->c_name
, tlv_len
);
387 chassis
->c_id_subtype
= LLDP_CHASSISID_SUBTYPE_LOCAL
;
388 if ((chassis
->c_id
= (char *)malloc(tlv_len
)) == NULL
) {
389 log_warn("cdp", "unable to allocate memory for chassis ID");
392 memcpy(chassis
->c_id
, chassis
->c_name
, tlv_len
);
393 chassis
->c_id_len
= tlv_len
;
395 case CDP_TLV_ADDRESSES
:
396 CHECK_TLV_SIZE(4, "Address");
397 addresses_len
= tlv_len
- 4;
398 for (nb
= PEEK_UINT32
; nb
> 0; nb
--) {
399 (void)PEEK_SAVE(pos_address
);
400 /* We first try to get the real length of the packet */
401 if (addresses_len
< 2) {
402 log_warn("cdp", "too short address subframe "
407 PEEK_DISCARD_UINT8
; addresses_len
--;
408 address_len
= PEEK_UINT8
; addresses_len
--;
409 if (addresses_len
< address_len
+ 2) {
410 log_warn("cdp", "too short address subframe "
415 PEEK_DISCARD(address_len
);
416 addresses_len
-= address_len
;
417 address_len
= PEEK_UINT16
; addresses_len
-= 2;
418 if (addresses_len
< address_len
) {
419 log_warn("cdp", "too short address subframe "
424 PEEK_DISCARD(address_len
);
425 (void)PEEK_SAVE(pos_next_address
);
426 /* Next, we go back and try to extract
428 PEEK_RESTORE(pos_address
);
429 if ((PEEK_UINT8
== 1) && (PEEK_UINT8
== 1) &&
430 (PEEK_UINT8
== CDP_ADDRESS_PROTO_IP
) &&
431 (PEEK_UINT16
== sizeof(struct in_addr
))) {
432 PEEK_BYTES(&addr
, sizeof(struct in_addr
));
433 mgmt
= lldpd_alloc_mgmt(LLDPD_AF_IPV4
, &addr
,
434 sizeof(struct in_addr
), 0);
438 "unable to allocate memory for management address");
441 "too large management address received on %s",
445 TAILQ_INSERT_TAIL(&chassis
->c_mgmt
, mgmt
, m_entries
);
447 /* Go to the end of the address */
448 PEEK_RESTORE(pos_next_address
);
453 log_warn("cdp", "too short port description received");
456 if ((port
->p_descr
= (char *)calloc(1, tlv_len
+ 1)) == NULL
) {
457 log_warn("cdp", "unable to allocate memory for port description");
460 PEEK_BYTES(port
->p_descr
, tlv_len
);
461 port
->p_id_subtype
= LLDP_PORTID_SUBTYPE_IFNAME
;
462 if ((port
->p_id
= (char *)calloc(1, tlv_len
)) == NULL
) {
463 log_warn("cdp", "unable to allocate memory for port ID");
466 memcpy(port
->p_id
, port
->p_descr
, tlv_len
);
467 port
->p_id_len
= tlv_len
;
469 case CDP_TLV_CAPABILITIES
:
472 /* Capabilities are string with FDP */
473 if (!strncmp("Router", (char*)pos
, tlv_len
))
474 chassis
->c_cap_enabled
= LLDP_CAP_ROUTER
;
475 else if (!strncmp("Switch", (char*)pos
, tlv_len
))
476 chassis
->c_cap_enabled
= LLDP_CAP_BRIDGE
;
477 else if (!strncmp("Bridge", (char*)pos
, tlv_len
))
478 chassis
->c_cap_enabled
= LLDP_CAP_REPEATER
;
480 chassis
->c_cap_enabled
= LLDP_CAP_STATION
;
481 chassis
->c_cap_available
= chassis
->c_cap_enabled
;
485 CHECK_TLV_SIZE(4, "Capabilities");
487 if (caps
& CDP_CAP_ROUTER
)
488 chassis
->c_cap_enabled
|= LLDP_CAP_ROUTER
;
490 chassis
->c_cap_enabled
|= LLDP_CAP_BRIDGE
;
491 if (chassis
->c_cap_enabled
== 0)
492 chassis
->c_cap_enabled
= LLDP_CAP_STATION
;
493 chassis
->c_cap_available
= chassis
->c_cap_enabled
;
495 case CDP_TLV_SOFTWARE
:
496 software_len
= tlv_len
;
497 (void)PEEK_SAVE(software
);
499 case CDP_TLV_PLATFORM
:
500 platform_len
= tlv_len
;
501 (void)PEEK_SAVE(platform
);
504 case CDP_TLV_NATIVEVLAN
:
505 CHECK_TLV_SIZE(2, "Native VLAN");
506 if ((vlan
= (struct lldpd_vlan
*)calloc(1,
507 sizeof(struct lldpd_vlan
))) == NULL
) {
508 log_warn("cdp", "unable to alloc vlan "
510 "tlv received on %s",
514 vlan
->v_vid
= port
->p_pvid
= PEEK_UINT16
;
515 if (asprintf(&vlan
->v_name
, "VLAN #%d", vlan
->v_vid
) == -1) {
516 log_warn("cdp", "unable to alloc VLAN name for "
517 "TLV received on %s",
522 TAILQ_INSERT_TAIL(&port
->p_vlans
,
527 log_debug("cdp", "unknown CDP/FDP TLV type (%d) received on %s",
528 ntohs(tlv_type
), hardware
->h_ifname
);
529 hardware
->h_rx_unrecognized_cnt
++;
531 PEEK_DISCARD(tlv
+ tlv_len
- pos
);
533 if (!software
&& platform
) {
534 if ((chassis
->c_descr
= (char *)calloc(1,
535 platform_len
+ 1)) == NULL
) {
536 log_warn("cdp", "unable to allocate memory for chassis description");
539 memcpy(chassis
->c_descr
, platform
, platform_len
);
540 } else if (software
&& !platform
) {
541 if ((chassis
->c_descr
= (char *)calloc(1,
542 software_len
+ 1)) == NULL
) {
543 log_warn("cdp", "unable to allocate memory for chassis description");
546 memcpy(chassis
->c_descr
, software
, software_len
);
547 } else if (software
&& platform
) {
548 #define CONCAT_PLATFORM " running on\n"
549 if ((chassis
->c_descr
= (char *)calloc(1,
550 software_len
+ platform_len
+
551 strlen(CONCAT_PLATFORM
) + 1)) == NULL
) {
552 log_warn("cdp", "unable to allocate memory for chassis description");
555 memcpy(chassis
->c_descr
, platform
, platform_len
);
556 memcpy(chassis
->c_descr
+ platform_len
,
557 CONCAT_PLATFORM
, strlen(CONCAT_PLATFORM
));
558 memcpy(chassis
->c_descr
+ platform_len
+ strlen(CONCAT_PLATFORM
),
559 software
, software_len
);
561 if ((chassis
->c_id
== NULL
) ||
562 (port
->p_id
== NULL
) ||
563 (chassis
->c_name
== NULL
) ||
564 (chassis
->c_descr
== NULL
) ||
565 (port
->p_descr
== NULL
) ||
566 (port
->p_ttl
== 0) ||
567 (chassis
->c_cap_enabled
== 0)) {
568 log_warnx("cdp", "some mandatory CDP/FDP tlv are missing for frame received on %s",
572 *newchassis
= chassis
;
577 lldpd_chassis_cleanup(chassis
, 1);
578 lldpd_port_cleanup(port
, 1);
585 cdpv1_send(struct lldpd
*global
,
586 struct lldpd_hardware
*hardware
)
588 return cdp_send(global
, hardware
, 1);
592 cdpv2_send(struct lldpd
*global
,
593 struct lldpd_hardware
*hardware
)
595 return cdp_send(global
, hardware
, 2);
601 fdp_send(struct lldpd
*global
,
602 struct lldpd_hardware
*hardware
)
604 return cdp_send(global
, hardware
, 0);
610 cdp_guess(char *pos
, int length
, int version
)
612 const u_int8_t mcastaddr
[] = CDP_MULTICAST_ADDR
;
613 if (length
< 2*ETHER_ADDR_LEN
+ sizeof(u_int16_t
) /* Ethernet */ +
614 8 /* LLC */ + 4 /* CDP header */)
616 if (PEEK_CMP(mcastaddr
, ETHER_ADDR_LEN
) != 0)
618 PEEK_DISCARD(ETHER_ADDR_LEN
); PEEK_DISCARD_UINT16
; /* Ethernet */
619 PEEK_DISCARD(8); /* LLC */
620 return (PEEK_UINT8
== version
);
624 cdpv1_guess(char *frame
, int len
)
626 return cdp_guess(frame
, len
, 1);
630 cdpv2_guess(char *frame
, int len
)
632 return cdp_guess(frame
, len
, 2);
636 #endif /* defined (ENABLE_CDP) || defined (ENABLE_FDP) */