2 * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
4 * Permission to use, copy, modify, and 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)
25 #include <arpa/inet.h>
28 cdp_send(struct lldpd
*global
,
29 struct lldpd_hardware
*hardware
, int version
)
31 struct lldpd_chassis
*chassis
;
32 u_int8_t mcastaddr
[] = CDP_MULTICAST_ADDR
;
33 u_int8_t llcorg
[] = LLC_ORG_CISCO
;
41 u_int8_t
*pos
, *pos_len_eh
, *pos_llc
, *pos_cdp
, *pos_checksum
, *tlv
, *end
;
43 chassis
= hardware
->h_lport
.p_chassis
;
47 /* With FDP, change multicast address and LLC PID */
48 const u_int8_t fdpmcastaddr
[] = FDP_MULTICAST_ADDR
;
49 const u_int8_t fdpllcorg
[] = LLC_ORG_FOUNDRY
;
50 memcpy(mcastaddr
, fdpmcastaddr
, sizeof(mcastaddr
));
51 memcpy(llcorg
, fdpllcorg
, sizeof(llcorg
));
55 length
= hardware
->h_mtu
;
56 if ((packet
= (u_int8_t
*)malloc(length
)) == NULL
)
58 memset(packet
, 0, length
);
63 POKE_BYTES(mcastaddr
, sizeof(mcastaddr
)) &&
64 POKE_BYTES(&hardware
->h_lladdr
, sizeof(hardware
->h_lladdr
)) &&
65 POKE_SAVE(pos_len_eh
) && /* We compute the len later */
72 POKE_UINT8(0xaa) && /* SSAP */
73 POKE_UINT8(0xaa) && /* DSAP */
74 POKE_UINT8(0x03) && /* Control field */
75 POKE_BYTES(llcorg
, sizeof(llcorg
)) &&
76 POKE_UINT16(LLC_PID_CDP
)))
82 POKE_UINT8((version
== 0)?1:version
) &&
83 POKE_UINT8(chassis
->c_ttl
) &&
84 POKE_SAVE(pos_checksum
) && /* Save checksum position */
90 POKE_START_CDP_TLV(CDP_TLV_CHASSIS
) &&
91 POKE_BYTES(chassis
->c_name
, strlen(chassis
->c_name
)) &&
97 POKE_START_CDP_TLV(CDP_TLV_ADDRESSES
) &&
98 POKE_UINT32(1) && /* We ship only one address */
99 POKE_UINT8(1) && /* Type: NLPID */
100 POKE_UINT8(1) && /* Length: 1 */
101 POKE_UINT8(CDP_ADDRESS_PROTO_IP
) && /* IP */
102 POKE_UINT16(sizeof(struct in_addr
)) && /* Address length */
103 POKE_BYTES(&chassis
->c_mgmt
, sizeof(struct in_addr
)) &&
109 POKE_START_CDP_TLV(CDP_TLV_PORT
) &&
110 POKE_BYTES(hardware
->h_lport
.p_descr
,
111 strlen(hardware
->h_lport
.p_descr
)) &&
118 if (chassis
->c_cap_enabled
& LLDP_CAP_ROUTER
)
119 cap
|= CDP_CAP_ROUTER
;
120 if (chassis
->c_cap_enabled
& LLDP_CAP_BRIDGE
)
121 cap
|= CDP_CAP_BRIDGE
;
123 POKE_START_CDP_TLV(CDP_TLV_CAPABILITIES
) &&
129 /* With FDP, it seems that a string is used in place of an int */
130 if (chassis
->c_cap_enabled
& LLDP_CAP_ROUTER
)
132 else if (chassis
->c_cap_enabled
& LLDP_CAP_BRIDGE
)
134 else if (chassis
->c_cap_enabled
& LLDP_CAP_REPEATER
)
139 POKE_START_CDP_TLV(CDP_TLV_CAPABILITIES
) &&
140 POKE_BYTES(capstr
, strlen(capstr
)) &&
146 /* Software version */
148 POKE_START_CDP_TLV(CDP_TLV_SOFTWARE
) &&
149 POKE_BYTES(chassis
->c_descr
, strlen(chassis
->c_descr
)) &&
155 POKE_START_CDP_TLV(CDP_TLV_PLATFORM
) &&
156 POKE_BYTES("Linux", strlen("Linux")) &&
161 /* Compute len and checksum */
162 POKE_RESTORE(pos_len_eh
);
163 if (!(POKE_UINT16(end
- pos_llc
))) goto toobig
;
164 checksum
= frame_checksum(pos_cdp
, end
- pos_cdp
, (version
!= 0) ? 1 : 0);
165 POKE_RESTORE(pos_checksum
);
166 if (!(POKE_UINT16(ntohs(checksum
)))) goto toobig
;
168 if (hardware
->h_ops
->send(global
, hardware
,
169 (char *)packet
, end
- packet
) == -1) {
170 LLOG_WARN("unable to send packet on real device for %s",
176 hardware
->h_tx_cnt
++;
185 #define CHECK_TLV_SIZE(x, name) \
186 do { if (tlv_len < (x)) { \
187 LLOG_WARNX(name " CDP/FDP TLV too short received on %s",\
188 hardware->h_ifname); \
191 /* cdp_decode also decodes FDP */
193 cdp_decode(struct lldpd
*cfg
, char *frame
, int s
,
194 struct lldpd_hardware
*hardware
,
195 struct lldpd_chassis
**newchassis
, struct lldpd_port
**newport
)
197 struct lldpd_chassis
*chassis
;
198 struct lldpd_port
*port
;
202 u_int8_t
*software
= NULL
, *platform
= NULL
;
203 int software_len
= 0, platform_len
= 0, proto
, version
, nb
, caps
;
204 const unsigned char cdpaddr
[] = CDP_MULTICAST_ADDR
;
206 const unsigned char fdpaddr
[] = CDP_MULTICAST_ADDR
;
209 u_int8_t
*pos
, *tlv
, *pos_address
, *pos_next_address
;
210 int length
, len_eth
, tlv_type
, tlv_len
, addresses_len
, address_len
;
212 if ((chassis
= calloc(1, sizeof(struct lldpd_chassis
))) == NULL
) {
213 LLOG_WARN("failed to allocate remote chassis");
216 if ((port
= calloc(1, sizeof(struct lldpd_port
))) == NULL
) {
217 LLOG_WARN("failed to allocate remote port");
222 TAILQ_INIT(&port
->p_vlans
);
226 pos
= (u_int8_t
*)frame
;
228 if (length
< 2*ETH_ALEN
+ sizeof(u_int16_t
) /* Ethernet */ +
229 8 /* LLC */ + 4 /* CDP header */) {
230 LLOG_WARNX("too short CDP/FDP frame received on %s", hardware
->h_ifname
);
234 if (PEEK_CMP(cdpaddr
, sizeof(cdpaddr
)) != 0) {
236 PEEK_RESTORE((u_int8_t
*)frame
);
237 if (PEEK_CMP(fdpaddr
, sizeof(fdpaddr
)) != 0)
241 LLOG_INFO("frame not targeted at CDP/FDP multicast address received on %s",
248 PEEK_DISCARD(ETH_ALEN
); /* Don't care of source address */
249 len_eth
= PEEK_UINT16
;
250 if (len_eth
> length
) {
251 LLOG_WARNX("incorrect 802.3 frame size reported on %s",
255 PEEK_DISCARD(6); /* Skip beginning of LLC */
257 if (proto
!= LLC_PID_CDP
) {
258 if ((proto
!= LLC_PID_DRIP
) &&
259 (proto
!= LLC_PID_PAGP
) &&
260 (proto
!= LLC_PID_PVSTP
) &&
261 (proto
!= LLC_PID_UDLD
) &&
262 (proto
!= LLC_PID_VTP
) &&
263 (proto
!= LLC_PID_DTP
) &&
264 (proto
!= LLC_PID_STP
))
265 LLOG_DEBUG("incorrect LLC protocol ID received on %s",
272 cksum
= frame_checksum(pos
, len_eth
- 8,
274 !fdp
/* fdp = 0 -> cisco checksum */
276 1 /* cisco checksum */
279 /* An off-by-one error may happen. Just ignore it */
280 if ((cksum
!= 0) && (cksum
!= 0xfffe)) {
281 LLOG_INFO("incorrect CDP/FDP checksum for frame received on %s (%d)",
282 hardware
->h_ifname
, cksum
);
288 version
= PEEK_UINT8
;
289 if ((version
!= 1) && (version
!= 2)) {
290 LLOG_WARNX("incorrect CDP/FDP version (%d) for frame received on %s",
291 version
, hardware
->h_ifname
);
294 chassis
->c_ttl
= PEEK_UINT8
; /* TTL */
295 PEEK_DISCARD_UINT16
; /* Checksum, already checked */
299 LLOG_WARNX("CDP/FDP TLV header is too large for "
300 "frame received on %s",
304 tlv_type
= PEEK_UINT16
;
305 tlv_len
= PEEK_UINT16
- 4;
307 if ((tlv_len
< 0) || (length
< tlv_len
)) {
308 LLOG_WARNX("incorrect size in CDP/FDP TLV header for frame "
314 case CDP_TLV_CHASSIS
:
315 if ((chassis
->c_name
= (char *)calloc(1, tlv_len
+ 1)) == NULL
) {
316 LLOG_WARN("unable to allocate memory for chassis name");
319 PEEK_BYTES(chassis
->c_name
, tlv_len
);
320 chassis
->c_id_subtype
= LLDP_CHASSISID_SUBTYPE_LOCAL
;
321 if ((chassis
->c_id
= (char *)malloc(tlv_len
)) == NULL
) {
322 LLOG_WARN("unable to allocate memory for chassis ID");
325 memcpy(chassis
->c_id
, chassis
->c_name
, tlv_len
);
326 chassis
->c_id_len
= tlv_len
;
328 case CDP_TLV_ADDRESSES
:
329 CHECK_TLV_SIZE(4, "Address");
330 addresses_len
= tlv_len
- 4;
331 for (nb
= PEEK_UINT32
; nb
> 0; nb
--) {
332 PEEK_SAVE(pos_address
);
333 /* We first try to get the real length of the packet */
334 if (addresses_len
< 2) {
335 LLOG_WARN("too short address subframe "
340 PEEK_DISCARD_UINT8
; addresses_len
--;
341 address_len
= PEEK_UINT8
; addresses_len
--;
342 if (addresses_len
< address_len
+ 2) {
343 LLOG_WARN("too short address subframe "
348 PEEK_DISCARD(address_len
);
349 addresses_len
-= address_len
;
350 address_len
= PEEK_UINT16
; addresses_len
-= 2;
351 if (addresses_len
< address_len
) {
352 LLOG_WARN("too short address subframe "
357 PEEK_DISCARD(address_len
);
358 PEEK_SAVE(pos_next_address
);
359 /* Next, we go back and try to extract
361 PEEK_RESTORE(pos_address
);
362 if ((PEEK_UINT8
== 1) && (PEEK_UINT8
== 1) &&
363 (PEEK_UINT8
== CDP_ADDRESS_PROTO_IP
) &&
364 (PEEK_UINT16
== sizeof(struct in_addr
)) &&
365 (chassis
->c_mgmt
.s_addr
== INADDR_ANY
))
366 PEEK_BYTES(&chassis
->c_mgmt
,
367 sizeof(struct in_addr
));
368 /* Go to the end of the address */
369 PEEK_RESTORE(pos_next_address
);
373 if ((port
->p_descr
= (char *)calloc(1, tlv_len
+ 1)) == NULL
) {
374 LLOG_WARN("unable to allocate memory for port description");
377 PEEK_BYTES(port
->p_descr
, tlv_len
);
378 port
->p_id_subtype
= LLDP_PORTID_SUBTYPE_IFNAME
;
379 if ((port
->p_id
= (char *)calloc(1, tlv_len
)) == NULL
) {
380 LLOG_WARN("unable to allocate memory for port ID");
383 memcpy(port
->p_id
, port
->p_descr
, tlv_len
);
384 port
->p_id_len
= tlv_len
;
386 case CDP_TLV_CAPABILITIES
:
389 /* Capabilities are string with FDP */
390 if (!strncmp("Router", (char*)pos
, tlv_len
))
391 chassis
->c_cap_enabled
= LLDP_CAP_ROUTER
;
392 else if (!strncmp("Switch", (char*)pos
, tlv_len
))
393 chassis
->c_cap_enabled
= LLDP_CAP_BRIDGE
;
394 else if (!strncmp("Bridge", (char*)pos
, tlv_len
))
395 chassis
->c_cap_enabled
= LLDP_CAP_REPEATER
;
397 chassis
->c_cap_enabled
= LLDP_CAP_STATION
;
398 chassis
->c_cap_available
= chassis
->c_cap_enabled
;
402 CHECK_TLV_SIZE(4, "Capabilities");
404 if (caps
& CDP_CAP_ROUTER
)
405 chassis
->c_cap_enabled
|= LLDP_CAP_ROUTER
;
407 chassis
->c_cap_enabled
|= LLDP_CAP_BRIDGE
;
408 if (chassis
->c_cap_enabled
== 0)
409 chassis
->c_cap_enabled
= LLDP_CAP_STATION
;
410 chassis
->c_cap_available
= chassis
->c_cap_enabled
;
412 case CDP_TLV_SOFTWARE
:
413 software_len
= tlv_len
;
416 case CDP_TLV_PLATFORM
:
417 platform_len
= tlv_len
;
421 LLOG_DEBUG("unknown CDP/FDP TLV type (%d) received on %s",
422 ntohs(tlv_type
), hardware
->h_ifname
);
423 hardware
->h_rx_unrecognized_cnt
++;
425 PEEK_DISCARD(tlv
+ tlv_len
- pos
);
427 if (!software
&& platform
) {
428 if ((chassis
->c_descr
= (char *)calloc(1,
429 platform_len
+ 1)) == NULL
) {
430 LLOG_WARN("unable to allocate memory for chassis description");
433 memcpy(chassis
->c_descr
, platform
, platform_len
);
434 } else if (software
&& !platform
) {
435 if ((chassis
->c_descr
= (char *)calloc(1,
436 software_len
+ 1)) == NULL
) {
437 LLOG_WARN("unable to allocate memory for chassis description");
440 memcpy(chassis
->c_descr
, software
, software_len
);
441 } else if (software
&& platform
) {
442 #define CONCAT_PLATFORM " running on\n"
443 if ((chassis
->c_descr
= (char *)calloc(1,
444 software_len
+ platform_len
+
445 strlen(CONCAT_PLATFORM
) + 1)) == NULL
) {
446 LLOG_WARN("unable to allocate memory for chassis description");
449 memcpy(chassis
->c_descr
, platform
, platform_len
);
450 memcpy(chassis
->c_descr
+ platform_len
,
451 CONCAT_PLATFORM
, strlen(CONCAT_PLATFORM
));
452 memcpy(chassis
->c_descr
+ platform_len
+ strlen(CONCAT_PLATFORM
),
453 software
, software_len
);
455 if ((chassis
->c_id
== NULL
) ||
456 (port
->p_id
== NULL
) ||
457 (chassis
->c_name
== NULL
) ||
458 (chassis
->c_descr
== NULL
) ||
459 (port
->p_descr
== NULL
) ||
460 (chassis
->c_ttl
== 0) ||
461 (chassis
->c_cap_enabled
== 0)) {
462 LLOG_WARNX("some mandatory CDP/FDP tlv are missing for frame received on %s",
466 *newchassis
= chassis
;
471 lldpd_chassis_cleanup(chassis
, 1);
472 lldpd_port_cleanup(cfg
, port
, 1);
479 cdpv1_send(struct lldpd
*global
,
480 struct lldpd_hardware
*hardware
)
482 return cdp_send(global
, hardware
, 1);
486 cdpv2_send(struct lldpd
*global
,
487 struct lldpd_hardware
*hardware
)
489 return cdp_send(global
, hardware
, 2);
495 fdp_send(struct lldpd
*global
,
496 struct lldpd_hardware
*hardware
)
498 return cdp_send(global
, hardware
, 0);
504 cdp_guess(char *pos
, int length
, int version
)
506 const u_int8_t mcastaddr
[] = CDP_MULTICAST_ADDR
;
507 if (length
< 2*ETH_ALEN
+ sizeof(u_int16_t
) /* Ethernet */ +
508 8 /* LLC */ + 4 /* CDP header */)
510 if (PEEK_CMP(mcastaddr
, ETH_ALEN
) != 0)
512 PEEK_DISCARD(ETH_ALEN
); PEEK_DISCARD_UINT16
; /* Ethernet */
513 PEEK_DISCARD(8); /* LLC */
514 return (PEEK_UINT8
== version
);
518 cdpv1_guess(char *frame
, int len
)
520 return cdp_guess(frame
, len
, 1);
524 cdpv2_guess(char *frame
, int len
)
526 return cdp_guess(frame
, len
, 2);
530 #endif /* defined (ENABLE_CDP) || defined (ENABLE_FDP) */