2 * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 /* We also supports FDP which is very similar to CDPv1 */
21 #if defined (ENABLE_CDP) || defined (ENABLE_FDP)
26 #include <arpa/inet.h>
30 cdp_send(struct lldpd
*global
,
31 struct lldpd_hardware
*hardware
, int version
)
33 const char *platform
= "Linux";
34 struct lldpd_chassis
*chassis
;
35 struct lldpd_mgmt
*mgmt
;
36 u_int8_t mcastaddr
[] = CDP_MULTICAST_ADDR
;
37 u_int8_t llcorg
[] = LLC_ORG_CISCO
;
45 u_int8_t
*pos
, *pos_len_eh
, *pos_llc
, *pos_cdp
, *pos_checksum
, *tlv
, *end
;
47 chassis
= hardware
->h_lport
.p_chassis
;
51 /* With FDP, change multicast address and LLC PID */
52 const u_int8_t fdpmcastaddr
[] = FDP_MULTICAST_ADDR
;
53 const u_int8_t fdpllcorg
[] = LLC_ORG_FOUNDRY
;
54 memcpy(mcastaddr
, fdpmcastaddr
, sizeof(mcastaddr
));
55 memcpy(llcorg
, fdpllcorg
, sizeof(llcorg
));
59 length
= hardware
->h_mtu
;
60 if ((packet
= (u_int8_t
*)malloc(length
)) == NULL
)
62 memset(packet
, 0, length
);
67 POKE_BYTES(mcastaddr
, sizeof(mcastaddr
)) &&
68 POKE_BYTES(&hardware
->h_lladdr
, sizeof(hardware
->h_lladdr
)) &&
69 POKE_SAVE(pos_len_eh
) && /* We compute the len later */
76 POKE_UINT8(0xaa) && /* SSAP */
77 POKE_UINT8(0xaa) && /* DSAP */
78 POKE_UINT8(0x03) && /* Control field */
79 POKE_BYTES(llcorg
, sizeof(llcorg
)) &&
80 POKE_UINT16(LLC_PID_CDP
)))
86 POKE_UINT8((version
== 0)?1:version
) &&
87 POKE_UINT8(chassis
->c_ttl
) &&
88 POKE_SAVE(pos_checksum
) && /* Save checksum position */
94 POKE_START_CDP_TLV(CDP_TLV_CHASSIS
) &&
95 POKE_BYTES(chassis
->c_name
, strlen(chassis
->c_name
)) &&
101 * http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.htm#xtocid12
103 * It seems that Cisco implies that CDP supports IPv6 using
104 * 802.2 address format with 0xAAAA03 0x000000 0x0800, but
105 * 0x0800 is the Ethernet protocol type for IPv4. Therefore,
106 * we support only IPv4. */
108 TAILQ_FOREACH(mgmt
, &chassis
->c_mgmt
, m_entries
)
109 if (mgmt
->m_family
== LLDPD_AF_IPV4
) i
++;
112 POKE_START_CDP_TLV(CDP_TLV_ADDRESSES
) &&
115 TAILQ_FOREACH(mgmt
, &chassis
->c_mgmt
, m_entries
) {
116 switch (mgmt
->m_family
) {
119 POKE_UINT8(1) && /* Type: NLPID */
120 POKE_UINT8(1) && /* Length: 1 */
121 POKE_UINT8(CDP_ADDRESS_PROTO_IP
) && /* IP */
122 POKE_UINT16(sizeof(struct in_addr
)) && /* Address length */
123 POKE_BYTES(&mgmt
->m_addr
, sizeof(struct in_addr
))))
128 if (!(POKE_END_CDP_TLV
))
134 POKE_START_CDP_TLV(CDP_TLV_PORT
) &&
135 POKE_BYTES(hardware
->h_lport
.p_descr
,
136 strlen(hardware
->h_lport
.p_descr
)) &&
143 if (chassis
->c_cap_enabled
& LLDP_CAP_ROUTER
)
144 cap
|= CDP_CAP_ROUTER
;
145 if (chassis
->c_cap_enabled
& LLDP_CAP_BRIDGE
)
146 cap
|= CDP_CAP_SWITCH
;
149 POKE_START_CDP_TLV(CDP_TLV_CAPABILITIES
) &&
155 /* With FDP, it seems that a string is used in place of an int */
156 if (chassis
->c_cap_enabled
& LLDP_CAP_ROUTER
)
158 else if (chassis
->c_cap_enabled
& LLDP_CAP_BRIDGE
)
160 else if (chassis
->c_cap_enabled
& LLDP_CAP_REPEATER
)
165 POKE_START_CDP_TLV(CDP_TLV_CAPABILITIES
) &&
166 POKE_BYTES(capstr
, strlen(capstr
)) &&
174 if (version
>=2 && hardware
->h_lport
.p_pvid
!= 0) {
176 POKE_START_CDP_TLV(CDP_TLV_NATIVEVLAN
) &&
177 POKE_UINT16(hardware
->h_lport
.p_pvid
) &&
183 /* Software version */
185 POKE_START_CDP_TLV(CDP_TLV_SOFTWARE
) &&
186 POKE_BYTES(chassis
->c_descr
, strlen(chassis
->c_descr
)) &&
191 if (global
&& global
->g_platform_override
) platform
= global
->g_platform_override
;
193 POKE_START_CDP_TLV(CDP_TLV_PLATFORM
) &&
194 POKE_BYTES(platform
, strlen(platform
)) &&
199 /* Compute len and checksum */
200 POKE_RESTORE(pos_len_eh
);
201 if (!(POKE_UINT16(end
- pos_llc
))) goto toobig
;
202 checksum
= frame_checksum(pos_cdp
, end
- pos_cdp
, (version
!= 0) ? 1 : 0);
203 POKE_RESTORE(pos_checksum
);
204 if (!(POKE_UINT16(ntohs(checksum
)))) goto toobig
;
206 if (hardware
->h_ops
->send(global
, hardware
,
207 (char *)packet
, end
- packet
) == -1) {
208 LLOG_WARN("unable to send packet on real device for %s",
214 hardware
->h_tx_cnt
++;
223 #define CHECK_TLV_SIZE(x, name) \
224 do { if (tlv_len < (x)) { \
225 LLOG_WARNX(name " CDP/FDP TLV too short received on %s",\
226 hardware->h_ifname); \
229 /* cdp_decode also decodes FDP */
231 cdp_decode(struct lldpd
*cfg
, char *frame
, int s
,
232 struct lldpd_hardware
*hardware
,
233 struct lldpd_chassis
**newchassis
, struct lldpd_port
**newport
)
235 struct lldpd_chassis
*chassis
;
236 struct lldpd_port
*port
;
237 struct lldpd_mgmt
*mgmt
;
242 u_int8_t
*software
= NULL
, *platform
= NULL
;
243 int software_len
= 0, platform_len
= 0, proto
, version
, nb
, caps
;
244 const unsigned char cdpaddr
[] = CDP_MULTICAST_ADDR
;
246 const unsigned char fdpaddr
[] = CDP_MULTICAST_ADDR
;
249 u_int8_t
*pos
, *tlv
, *pos_address
, *pos_next_address
;
250 int length
, len_eth
, tlv_type
, tlv_len
, addresses_len
, address_len
;
252 struct lldpd_vlan
*vlan
;
255 if ((chassis
= calloc(1, sizeof(struct lldpd_chassis
))) == NULL
) {
256 LLOG_WARN("failed to allocate remote chassis");
259 TAILQ_INIT(&chassis
->c_mgmt
);
260 if ((port
= calloc(1, sizeof(struct lldpd_port
))) == NULL
) {
261 LLOG_WARN("failed to allocate remote port");
266 TAILQ_INIT(&port
->p_vlans
);
270 pos
= (u_int8_t
*)frame
;
272 if (length
< 2*ETH_ALEN
+ sizeof(u_int16_t
) /* Ethernet */ +
273 8 /* LLC */ + 4 /* CDP header */) {
274 LLOG_WARNX("too short CDP/FDP frame received on %s", hardware
->h_ifname
);
278 if (PEEK_CMP(cdpaddr
, sizeof(cdpaddr
)) != 0) {
280 PEEK_RESTORE((u_int8_t
*)frame
);
281 if (PEEK_CMP(fdpaddr
, sizeof(fdpaddr
)) != 0)
285 LLOG_INFO("frame not targeted at CDP/FDP multicast address received on %s",
292 PEEK_DISCARD(ETH_ALEN
); /* Don't care of source address */
293 len_eth
= PEEK_UINT16
;
294 if (len_eth
> length
) {
295 LLOG_WARNX("incorrect 802.3 frame size reported on %s",
299 PEEK_DISCARD(6); /* Skip beginning of LLC */
301 if (proto
!= LLC_PID_CDP
) {
302 if ((proto
!= LLC_PID_DRIP
) &&
303 (proto
!= LLC_PID_PAGP
) &&
304 (proto
!= LLC_PID_PVSTP
) &&
305 (proto
!= LLC_PID_UDLD
) &&
306 (proto
!= LLC_PID_VTP
) &&
307 (proto
!= LLC_PID_DTP
) &&
308 (proto
!= LLC_PID_STP
))
309 LLOG_DEBUG("incorrect LLC protocol ID received on %s",
316 cksum
= frame_checksum(pos
, len_eth
- 8,
318 !fdp
/* fdp = 0 -> cisco checksum */
320 1 /* cisco checksum */
323 /* An off-by-one error may happen. Just ignore it */
324 if ((cksum
!= 0) && (cksum
!= 0xfffe)) {
325 LLOG_INFO("incorrect CDP/FDP checksum for frame received on %s (%d)",
326 hardware
->h_ifname
, cksum
);
332 version
= PEEK_UINT8
;
333 if ((version
!= 1) && (version
!= 2)) {
334 LLOG_WARNX("incorrect CDP/FDP version (%d) for frame received on %s",
335 version
, hardware
->h_ifname
);
338 chassis
->c_ttl
= PEEK_UINT8
; /* TTL */
339 PEEK_DISCARD_UINT16
; /* Checksum, already checked */
343 LLOG_WARNX("CDP/FDP TLV header is too large for "
344 "frame received on %s",
348 tlv_type
= PEEK_UINT16
;
349 tlv_len
= PEEK_UINT16
- 4;
351 if ((tlv_len
< 0) || (length
< tlv_len
)) {
352 LLOG_WARNX("incorrect size in CDP/FDP TLV header for frame "
358 case CDP_TLV_CHASSIS
:
359 if ((chassis
->c_name
= (char *)calloc(1, tlv_len
+ 1)) == NULL
) {
360 LLOG_WARN("unable to allocate memory for chassis name");
363 PEEK_BYTES(chassis
->c_name
, tlv_len
);
364 chassis
->c_id_subtype
= LLDP_CHASSISID_SUBTYPE_LOCAL
;
365 if ((chassis
->c_id
= (char *)malloc(tlv_len
)) == NULL
) {
366 LLOG_WARN("unable to allocate memory for chassis ID");
369 memcpy(chassis
->c_id
, chassis
->c_name
, tlv_len
);
370 chassis
->c_id_len
= tlv_len
;
372 case CDP_TLV_ADDRESSES
:
373 CHECK_TLV_SIZE(4, "Address");
374 addresses_len
= tlv_len
- 4;
375 for (nb
= PEEK_UINT32
; nb
> 0; nb
--) {
376 PEEK_SAVE(pos_address
);
377 /* We first try to get the real length of the packet */
378 if (addresses_len
< 2) {
379 LLOG_WARN("too short address subframe "
384 PEEK_DISCARD_UINT8
; addresses_len
--;
385 address_len
= PEEK_UINT8
; addresses_len
--;
386 if (addresses_len
< address_len
+ 2) {
387 LLOG_WARN("too short address subframe "
392 PEEK_DISCARD(address_len
);
393 addresses_len
-= address_len
;
394 address_len
= PEEK_UINT16
; addresses_len
-= 2;
395 if (addresses_len
< address_len
) {
396 LLOG_WARN("too short address subframe "
401 PEEK_DISCARD(address_len
);
402 PEEK_SAVE(pos_next_address
);
403 /* Next, we go back and try to extract
405 PEEK_RESTORE(pos_address
);
406 if ((PEEK_UINT8
== 1) && (PEEK_UINT8
== 1) &&
407 (PEEK_UINT8
== CDP_ADDRESS_PROTO_IP
) &&
408 (PEEK_UINT16
== sizeof(struct in_addr
))) {
409 PEEK_BYTES(&addr
, sizeof(struct in_addr
));
410 mgmt
= lldpd_alloc_mgmt(LLDPD_AF_IPV4
, &addr
,
411 sizeof(struct in_addr
), 0);
413 assert(errno
== ENOMEM
);
414 LLOG_WARN("unable to allocate memory for management address");
417 TAILQ_INSERT_TAIL(&chassis
->c_mgmt
, mgmt
, m_entries
);
419 /* Go to the end of the address */
420 PEEK_RESTORE(pos_next_address
);
424 if ((port
->p_descr
= (char *)calloc(1, tlv_len
+ 1)) == NULL
) {
425 LLOG_WARN("unable to allocate memory for port description");
428 PEEK_BYTES(port
->p_descr
, tlv_len
);
429 port
->p_id_subtype
= LLDP_PORTID_SUBTYPE_IFNAME
;
430 if ((port
->p_id
= (char *)calloc(1, tlv_len
)) == NULL
) {
431 LLOG_WARN("unable to allocate memory for port ID");
434 memcpy(port
->p_id
, port
->p_descr
, tlv_len
);
435 port
->p_id_len
= tlv_len
;
437 case CDP_TLV_CAPABILITIES
:
440 /* Capabilities are string with FDP */
441 if (!strncmp("Router", (char*)pos
, tlv_len
))
442 chassis
->c_cap_enabled
= LLDP_CAP_ROUTER
;
443 else if (!strncmp("Switch", (char*)pos
, tlv_len
))
444 chassis
->c_cap_enabled
= LLDP_CAP_BRIDGE
;
445 else if (!strncmp("Bridge", (char*)pos
, tlv_len
))
446 chassis
->c_cap_enabled
= LLDP_CAP_REPEATER
;
448 chassis
->c_cap_enabled
= LLDP_CAP_STATION
;
449 chassis
->c_cap_available
= chassis
->c_cap_enabled
;
453 CHECK_TLV_SIZE(4, "Capabilities");
455 if (caps
& CDP_CAP_ROUTER
)
456 chassis
->c_cap_enabled
|= LLDP_CAP_ROUTER
;
458 chassis
->c_cap_enabled
|= LLDP_CAP_BRIDGE
;
459 if (chassis
->c_cap_enabled
== 0)
460 chassis
->c_cap_enabled
= LLDP_CAP_STATION
;
461 chassis
->c_cap_available
= chassis
->c_cap_enabled
;
463 case CDP_TLV_SOFTWARE
:
464 software_len
= tlv_len
;
467 case CDP_TLV_PLATFORM
:
468 platform_len
= tlv_len
;
472 case CDP_TLV_NATIVEVLAN
:
473 CHECK_TLV_SIZE(2, "Native VLAN");
474 if ((vlan
= (struct lldpd_vlan
*)calloc(1,
475 sizeof(struct lldpd_vlan
))) == NULL
) {
476 LLOG_WARN("unable to alloc vlan "
478 "tlv received on %s",
482 vlan
->v_vid
= port
->p_pvid
= PEEK_UINT16
;
483 if (asprintf(&vlan
->v_name
, "VLAN #%d", vlan
->v_vid
) == -1) {
484 LLOG_WARN("unable to alloc VLAN name for "
485 "TLV received on %s",
490 TAILQ_INSERT_TAIL(&port
->p_vlans
,
495 LLOG_DEBUG("unknown CDP/FDP TLV type (%d) received on %s",
496 ntohs(tlv_type
), hardware
->h_ifname
);
497 hardware
->h_rx_unrecognized_cnt
++;
499 PEEK_DISCARD(tlv
+ tlv_len
- pos
);
501 if (!software
&& platform
) {
502 if ((chassis
->c_descr
= (char *)calloc(1,
503 platform_len
+ 1)) == NULL
) {
504 LLOG_WARN("unable to allocate memory for chassis description");
507 memcpy(chassis
->c_descr
, platform
, platform_len
);
508 } else if (software
&& !platform
) {
509 if ((chassis
->c_descr
= (char *)calloc(1,
510 software_len
+ 1)) == NULL
) {
511 LLOG_WARN("unable to allocate memory for chassis description");
514 memcpy(chassis
->c_descr
, software
, software_len
);
515 } else if (software
&& platform
) {
516 #define CONCAT_PLATFORM " running on\n"
517 if ((chassis
->c_descr
= (char *)calloc(1,
518 software_len
+ platform_len
+
519 strlen(CONCAT_PLATFORM
) + 1)) == NULL
) {
520 LLOG_WARN("unable to allocate memory for chassis description");
523 memcpy(chassis
->c_descr
, platform
, platform_len
);
524 memcpy(chassis
->c_descr
+ platform_len
,
525 CONCAT_PLATFORM
, strlen(CONCAT_PLATFORM
));
526 memcpy(chassis
->c_descr
+ platform_len
+ strlen(CONCAT_PLATFORM
),
527 software
, software_len
);
529 if ((chassis
->c_id
== NULL
) ||
530 (port
->p_id
== NULL
) ||
531 (chassis
->c_name
== NULL
) ||
532 (chassis
->c_descr
== NULL
) ||
533 (port
->p_descr
== NULL
) ||
534 (chassis
->c_ttl
== 0) ||
535 (chassis
->c_cap_enabled
== 0)) {
536 LLOG_WARNX("some mandatory CDP/FDP tlv are missing for frame received on %s",
540 *newchassis
= chassis
;
545 lldpd_chassis_cleanup(chassis
, 1);
546 lldpd_port_cleanup(cfg
, port
, 1);
553 cdpv1_send(struct lldpd
*global
,
554 struct lldpd_hardware
*hardware
)
556 return cdp_send(global
, hardware
, 1);
560 cdpv2_send(struct lldpd
*global
,
561 struct lldpd_hardware
*hardware
)
563 return cdp_send(global
, hardware
, 2);
569 fdp_send(struct lldpd
*global
,
570 struct lldpd_hardware
*hardware
)
572 return cdp_send(global
, hardware
, 0);
578 cdp_guess(char *pos
, int length
, int version
)
580 const u_int8_t mcastaddr
[] = CDP_MULTICAST_ADDR
;
581 if (length
< 2*ETH_ALEN
+ sizeof(u_int16_t
) /* Ethernet */ +
582 8 /* LLC */ + 4 /* CDP header */)
584 if (PEEK_CMP(mcastaddr
, ETH_ALEN
) != 0)
586 PEEK_DISCARD(ETH_ALEN
); PEEK_DISCARD_UINT16
; /* Ethernet */
587 PEEK_DISCARD(8); /* LLC */
588 return (PEEK_UINT8
== version
);
592 cdpv1_guess(char *frame
, int len
)
594 return cdp_guess(frame
, len
, 1);
598 cdpv2_guess(char *frame
, int len
)
600 return cdp_guess(frame
, len
, 2);
604 #endif /* defined (ENABLE_CDP) || defined (ENABLE_FDP) */