]> git.ipfire.org Git - thirdparty/lldpd.git/blob - src/cdp.c
Update ISC license wording.
[thirdparty/lldpd.git] / src / cdp.c
1 /*
2 * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
3 *
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.
7 *
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.
15 */
16
17 /* We also supports FDP which is very similar to CDPv1 */
18 #include "lldpd.h"
19 #include "frame.h"
20
21 #if defined (ENABLE_CDP) || defined (ENABLE_FDP)
22
23 #include <unistd.h>
24 #include <errno.h>
25 #include <arpa/inet.h>
26
27 static int
28 cdp_send(struct lldpd *global,
29 struct lldpd_hardware *hardware, int version)
30 {
31 struct lldpd_chassis *chassis;
32 u_int8_t mcastaddr[] = CDP_MULTICAST_ADDR;
33 u_int8_t llcorg[] = LLC_ORG_CISCO;
34 #ifdef ENABLE_FDP
35 char *capstr;
36 #endif
37 u_int16_t checksum;
38 int length;
39 u_int32_t cap;
40 u_int8_t *packet;
41 u_int8_t *pos, *pos_len_eh, *pos_llc, *pos_cdp, *pos_checksum, *tlv, *end;
42
43 chassis = hardware->h_lport.p_chassis;
44
45 #ifdef ENABLE_FDP
46 if (version == 0) {
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));
52 }
53 #endif
54
55 length = hardware->h_mtu;
56 if ((packet = (u_int8_t*)malloc(length)) == NULL)
57 return ENOMEM;
58 memset(packet, 0, length);
59 pos = packet;
60
61 /* Ethernet header */
62 if (!(
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 */
66 POKE_UINT16(0)))
67 goto toobig;
68
69 /* LLC */
70 if (!(
71 POKE_SAVE(pos_llc) &&
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)))
77 goto toobig;
78
79 /* CDP header */
80 if (!(
81 POKE_SAVE(pos_cdp) &&
82 POKE_UINT8((version == 0)?1:version) &&
83 POKE_UINT8(chassis->c_ttl) &&
84 POKE_SAVE(pos_checksum) && /* Save checksum position */
85 POKE_UINT16(0)))
86 goto toobig;
87
88 /* Chassis ID */
89 if (!(
90 POKE_START_CDP_TLV(CDP_TLV_CHASSIS) &&
91 POKE_BYTES(chassis->c_name, strlen(chassis->c_name)) &&
92 POKE_END_CDP_TLV))
93 goto toobig;
94
95 /* Adresses */
96 if (!(
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)) &&
104 POKE_END_CDP_TLV))
105 goto toobig;
106
107 /* Port ID */
108 if (!(
109 POKE_START_CDP_TLV(CDP_TLV_PORT) &&
110 POKE_BYTES(hardware->h_lport.p_descr,
111 strlen(hardware->h_lport.p_descr)) &&
112 POKE_END_CDP_TLV))
113 goto toobig;
114
115 /* Capabilities */
116 if (version != 0) {
117 cap = 0;
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;
122 if (!(
123 POKE_START_CDP_TLV(CDP_TLV_CAPABILITIES) &&
124 POKE_UINT32(cap) &&
125 POKE_END_CDP_TLV))
126 goto toobig;
127 #ifdef ENABLE_FDP
128 } else {
129 /* With FDP, it seems that a string is used in place of an int */
130 if (chassis->c_cap_enabled & LLDP_CAP_ROUTER)
131 capstr = "Router";
132 else if (chassis->c_cap_enabled & LLDP_CAP_BRIDGE)
133 capstr = "Switch";
134 else if (chassis->c_cap_enabled & LLDP_CAP_REPEATER)
135 capstr = "Bridge";
136 else
137 capstr = "Host";
138 if (!(
139 POKE_START_CDP_TLV(CDP_TLV_CAPABILITIES) &&
140 POKE_BYTES(capstr, strlen(capstr)) &&
141 POKE_END_CDP_TLV))
142 goto toobig;
143 #endif
144 }
145
146 /* Software version */
147 if (!(
148 POKE_START_CDP_TLV(CDP_TLV_SOFTWARE) &&
149 POKE_BYTES(chassis->c_descr, strlen(chassis->c_descr)) &&
150 POKE_END_CDP_TLV))
151 goto toobig;
152
153 /* Platform */
154 if (!(
155 POKE_START_CDP_TLV(CDP_TLV_PLATFORM) &&
156 POKE_BYTES("Linux", strlen("Linux")) &&
157 POKE_END_CDP_TLV))
158 goto toobig;
159 POKE_SAVE(end);
160
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;
167
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",
171 hardware->h_ifname);
172 free(packet);
173 return ENETDOWN;
174 }
175
176 hardware->h_tx_cnt++;
177
178 free(packet);
179 return 0;
180 toobig:
181 free(packet);
182 return -1;
183 }
184
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); \
189 goto malformed; \
190 } } while (0)
191 /* cdp_decode also decodes FDP */
192 int
193 cdp_decode(struct lldpd *cfg, char *frame, int s,
194 struct lldpd_hardware *hardware,
195 struct lldpd_chassis **newchassis, struct lldpd_port **newport)
196 {
197 struct lldpd_chassis *chassis;
198 struct lldpd_port *port;
199 #if 0
200 u_int16_t cksum;
201 #endif
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;
205 #ifdef ENABLE_FDP
206 const unsigned char fdpaddr[] = CDP_MULTICAST_ADDR;
207 int fdp = 0;
208 #endif
209 u_int8_t *pos, *tlv, *pos_address, *pos_next_address;
210 int length, len_eth, tlv_type, tlv_len, addresses_len, address_len;
211
212 if ((chassis = calloc(1, sizeof(struct lldpd_chassis))) == NULL) {
213 LLOG_WARN("failed to allocate remote chassis");
214 return -1;
215 }
216 if ((port = calloc(1, sizeof(struct lldpd_port))) == NULL) {
217 LLOG_WARN("failed to allocate remote port");
218 free(chassis);
219 return -1;
220 }
221 #ifdef ENABLE_DOT1
222 TAILQ_INIT(&port->p_vlans);
223 #endif
224
225 length = s;
226 pos = (u_int8_t*)frame;
227
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);
231 goto malformed;
232 }
233
234 if (PEEK_CMP(cdpaddr, sizeof(cdpaddr)) != 0) {
235 #ifdef ENABLE_FDP
236 PEEK_RESTORE((u_int8_t*)frame);
237 if (PEEK_CMP(fdpaddr, sizeof(fdpaddr)) != 0)
238 fdp = 1;
239 else {
240 #endif
241 LLOG_INFO("frame not targeted at CDP/FDP multicast address received on %s",
242 hardware->h_ifname);
243 goto malformed;
244 #ifdef ENABLE_FDP
245 }
246 #endif
247 }
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",
252 hardware->h_ifname);
253 goto malformed;
254 }
255 PEEK_DISCARD(6); /* Skip beginning of LLC */
256 proto = PEEK_UINT16;
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",
266 hardware->h_ifname);
267 goto malformed;
268 }
269
270 #if 0
271 /* Check checksum */
272 cksum = frame_checksum(pos, len_eth - 8,
273 #ifdef ENABLE_FDP
274 !fdp /* fdp = 0 -> cisco checksum */
275 #else
276 1 /* cisco checksum */
277 #endif
278 );
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);
283 goto malformed;
284 }
285 #endif
286
287 /* Check version */
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);
292 goto malformed;
293 }
294 chassis->c_ttl = PEEK_UINT8; /* TTL */
295 PEEK_DISCARD_UINT16; /* Checksum, already checked */
296
297 while (length) {
298 if (length < 4) {
299 LLOG_WARNX("CDP/FDP TLV header is too large for "
300 "frame received on %s",
301 hardware->h_ifname);
302 goto malformed;
303 }
304 tlv_type = PEEK_UINT16;
305 tlv_len = PEEK_UINT16 - 4;
306 PEEK_SAVE(tlv);
307 if ((tlv_len < 0) || (length < tlv_len)) {
308 LLOG_WARNX("incorrect size in CDP/FDP TLV header for frame "
309 "received on %s",
310 hardware->h_ifname);
311 goto malformed;
312 }
313 switch (tlv_type) {
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");
317 goto malformed;
318 }
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");
323 goto malformed;
324 }
325 memcpy(chassis->c_id, chassis->c_name, tlv_len);
326 chassis->c_id_len = tlv_len;
327 break;
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 "
336 "received on %s",
337 hardware->h_ifname);
338 goto malformed;
339 }
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 "
344 "received on %s",
345 hardware->h_ifname);
346 goto malformed;
347 }
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 "
353 "received on %s",
354 hardware->h_ifname);
355 goto malformed;
356 }
357 PEEK_DISCARD(address_len);
358 PEEK_SAVE(pos_next_address);
359 /* Next, we go back and try to extract
360 IPv4 address */
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);
370 }
371 break;
372 case CDP_TLV_PORT:
373 if ((port->p_descr = (char *)calloc(1, tlv_len + 1)) == NULL) {
374 LLOG_WARN("unable to allocate memory for port description");
375 goto malformed;
376 }
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");
381 goto malformed;
382 }
383 memcpy(port->p_id, port->p_descr, tlv_len);
384 port->p_id_len = tlv_len;
385 break;
386 case CDP_TLV_CAPABILITIES:
387 #ifdef ENABLE_FDP
388 if (fdp) {
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;
396 else
397 chassis->c_cap_enabled = LLDP_CAP_STATION;
398 chassis->c_cap_available = chassis->c_cap_enabled;
399 break;
400 }
401 #endif
402 CHECK_TLV_SIZE(4, "Capabilities");
403 caps = PEEK_UINT32;
404 if (caps & CDP_CAP_ROUTER)
405 chassis->c_cap_enabled |= LLDP_CAP_ROUTER;
406 if (caps & 0x0e)
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;
411 break;
412 case CDP_TLV_SOFTWARE:
413 software_len = tlv_len;
414 PEEK_SAVE(software);
415 break;
416 case CDP_TLV_PLATFORM:
417 platform_len = tlv_len;
418 PEEK_SAVE(platform);
419 break;
420 default:
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++;
424 }
425 PEEK_DISCARD(tlv + tlv_len - pos);
426 }
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");
431 goto malformed;
432 }
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");
438 goto malformed;
439 }
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");
447 goto malformed;
448 }
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);
454 }
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",
463 hardware->h_ifname);
464 goto malformed;
465 }
466 *newchassis = chassis;
467 *newport = port;
468 return 1;
469
470 malformed:
471 lldpd_chassis_cleanup(chassis, 1);
472 lldpd_port_cleanup(cfg, port, 1);
473 free(port);
474 return -1;
475 }
476
477 #ifdef ENABLE_CDP
478 int
479 cdpv1_send(struct lldpd *global,
480 struct lldpd_hardware *hardware)
481 {
482 return cdp_send(global, hardware, 1);
483 }
484
485 int
486 cdpv2_send(struct lldpd *global,
487 struct lldpd_hardware *hardware)
488 {
489 return cdp_send(global, hardware, 2);
490 }
491 #endif
492
493 #ifdef ENABLE_FDP
494 int
495 fdp_send(struct lldpd *global,
496 struct lldpd_hardware *hardware)
497 {
498 return cdp_send(global, hardware, 0);
499 }
500 #endif
501
502 #ifdef ENABLE_CDP
503 static int
504 cdp_guess(char *pos, int length, int version)
505 {
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 */)
509 return 0;
510 if (PEEK_CMP(mcastaddr, ETH_ALEN) != 0)
511 return 0;
512 PEEK_DISCARD(ETH_ALEN); PEEK_DISCARD_UINT16; /* Ethernet */
513 PEEK_DISCARD(8); /* LLC */
514 return (PEEK_UINT8 == version);
515 }
516
517 int
518 cdpv1_guess(char *frame, int len)
519 {
520 return cdp_guess(frame, len, 1);
521 }
522
523 int
524 cdpv2_guess(char *frame, int len)
525 {
526 return cdp_guess(frame, len, 2);
527 }
528 #endif
529
530 #endif /* defined (ENABLE_CDP) || defined (ENABLE_FDP) */