]> git.ipfire.org Git - thirdparty/lldpd.git/blob - src/cdp.c
8d168c395ac5f3eae1766f9aae34e23b9a66dc06
[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 <stdio.h>
24 #include <unistd.h>
25 #include <errno.h>
26 #include <arpa/inet.h>
27 #include <assert.h>
28
29 static int
30 cdp_send(struct lldpd *global,
31 struct lldpd_hardware *hardware, int version)
32 {
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;
38 #ifdef ENABLE_FDP
39 char *capstr;
40 #endif
41 u_int16_t checksum;
42 int length, i;
43 u_int32_t cap;
44 u_int8_t *packet;
45 u_int8_t *pos, *pos_len_eh, *pos_llc, *pos_cdp, *pos_checksum, *tlv, *end;
46
47 chassis = hardware->h_lport.p_chassis;
48
49 #ifdef ENABLE_FDP
50 if (version == 0) {
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));
56 }
57 #endif
58
59 length = hardware->h_mtu;
60 if ((packet = (u_int8_t*)malloc(length)) == NULL)
61 return ENOMEM;
62 memset(packet, 0, length);
63 pos = packet;
64
65 /* Ethernet header */
66 if (!(
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 */
70 POKE_UINT16(0)))
71 goto toobig;
72
73 /* LLC */
74 if (!(
75 POKE_SAVE(pos_llc) &&
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)))
81 goto toobig;
82
83 /* CDP header */
84 if (!(
85 POKE_SAVE(pos_cdp) &&
86 POKE_UINT8((version == 0)?1:version) &&
87 POKE_UINT8(chassis->c_ttl) &&
88 POKE_SAVE(pos_checksum) && /* Save checksum position */
89 POKE_UINT16(0)))
90 goto toobig;
91
92 /* Chassis ID */
93 if (!(
94 POKE_START_CDP_TLV(CDP_TLV_CHASSIS) &&
95 POKE_BYTES(chassis->c_name, strlen(chassis->c_name)) &&
96 POKE_END_CDP_TLV))
97 goto toobig;
98
99 /* Adresses */
100 /* See:
101 * http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.htm#xtocid12
102 *
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. */
107 i = 0;
108 TAILQ_FOREACH(mgmt, &chassis->c_mgmt, m_entries)
109 if (mgmt->m_family == LLDPD_AF_IPV4) i++;
110 if (i > 0) {
111 if (!(
112 POKE_START_CDP_TLV(CDP_TLV_ADDRESSES) &&
113 POKE_UINT32(i)))
114 goto toobig;
115 TAILQ_FOREACH(mgmt, &chassis->c_mgmt, m_entries) {
116 switch (mgmt->m_family) {
117 case LLDPD_AF_IPV4:
118 if (!(
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))))
124 goto toobig;
125 break;
126 }
127 }
128 if (!(POKE_END_CDP_TLV))
129 goto toobig;
130 }
131
132 /* Port ID */
133 if (!(
134 POKE_START_CDP_TLV(CDP_TLV_PORT) &&
135 POKE_BYTES(hardware->h_lport.p_descr,
136 strlen(hardware->h_lport.p_descr)) &&
137 POKE_END_CDP_TLV))
138 goto toobig;
139
140 /* Capabilities */
141 if (version != 0) {
142 cap = 0;
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;
147 cap |= CDP_CAP_HOST;
148 if (!(
149 POKE_START_CDP_TLV(CDP_TLV_CAPABILITIES) &&
150 POKE_UINT32(cap) &&
151 POKE_END_CDP_TLV))
152 goto toobig;
153 #ifdef ENABLE_FDP
154 } else {
155 /* With FDP, it seems that a string is used in place of an int */
156 if (chassis->c_cap_enabled & LLDP_CAP_ROUTER)
157 capstr = "Router";
158 else if (chassis->c_cap_enabled & LLDP_CAP_BRIDGE)
159 capstr = "Switch";
160 else if (chassis->c_cap_enabled & LLDP_CAP_REPEATER)
161 capstr = "Bridge";
162 else
163 capstr = "Host";
164 if (!(
165 POKE_START_CDP_TLV(CDP_TLV_CAPABILITIES) &&
166 POKE_BYTES(capstr, strlen(capstr)) &&
167 POKE_END_CDP_TLV))
168 goto toobig;
169 #endif
170 }
171
172 /* Native VLAN */
173 #ifdef ENABLE_DOT1
174 if (version >=2 && hardware->h_lport.p_pvid != 0) {
175 if (!(
176 POKE_START_CDP_TLV(CDP_TLV_NATIVEVLAN) &&
177 POKE_UINT16(hardware->h_lport.p_pvid) &&
178 POKE_END_CDP_TLV))
179 goto toobig;
180 }
181 #endif
182
183 /* Software version */
184 if (!(
185 POKE_START_CDP_TLV(CDP_TLV_SOFTWARE) &&
186 POKE_BYTES(chassis->c_descr, strlen(chassis->c_descr)) &&
187 POKE_END_CDP_TLV))
188 goto toobig;
189
190 /* Platform */
191 if (global && global->g_platform_override) platform = global->g_platform_override;
192 if (!(
193 POKE_START_CDP_TLV(CDP_TLV_PLATFORM) &&
194 POKE_BYTES(platform, strlen(platform)) &&
195 POKE_END_CDP_TLV))
196 goto toobig;
197 POKE_SAVE(end);
198
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;
205
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",
209 hardware->h_ifname);
210 free(packet);
211 return ENETDOWN;
212 }
213
214 hardware->h_tx_cnt++;
215
216 free(packet);
217 return 0;
218 toobig:
219 free(packet);
220 return -1;
221 }
222
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); \
227 goto malformed; \
228 } } while (0)
229 /* cdp_decode also decodes FDP */
230 int
231 cdp_decode(struct lldpd *cfg, char *frame, int s,
232 struct lldpd_hardware *hardware,
233 struct lldpd_chassis **newchassis, struct lldpd_port **newport)
234 {
235 struct lldpd_chassis *chassis;
236 struct lldpd_port *port;
237 struct lldpd_mgmt *mgmt;
238 struct in_addr addr;
239 #if 0
240 u_int16_t cksum;
241 #endif
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;
245 #ifdef ENABLE_FDP
246 const unsigned char fdpaddr[] = CDP_MULTICAST_ADDR;
247 int fdp = 0;
248 #endif
249 u_int8_t *pos, *tlv, *pos_address, *pos_next_address;
250 int length, len_eth, tlv_type, tlv_len, addresses_len, address_len;
251 #ifdef ENABLE_DOT1
252 struct lldpd_vlan *vlan;
253 #endif
254
255 if ((chassis = calloc(1, sizeof(struct lldpd_chassis))) == NULL) {
256 LLOG_WARN("failed to allocate remote chassis");
257 return -1;
258 }
259 TAILQ_INIT(&chassis->c_mgmt);
260 if ((port = calloc(1, sizeof(struct lldpd_port))) == NULL) {
261 LLOG_WARN("failed to allocate remote port");
262 free(chassis);
263 return -1;
264 }
265 #ifdef ENABLE_DOT1
266 TAILQ_INIT(&port->p_vlans);
267 #endif
268
269 length = s;
270 pos = (u_int8_t*)frame;
271
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);
275 goto malformed;
276 }
277
278 if (PEEK_CMP(cdpaddr, sizeof(cdpaddr)) != 0) {
279 #ifdef ENABLE_FDP
280 PEEK_RESTORE((u_int8_t*)frame);
281 if (PEEK_CMP(fdpaddr, sizeof(fdpaddr)) != 0)
282 fdp = 1;
283 else {
284 #endif
285 LLOG_INFO("frame not targeted at CDP/FDP multicast address received on %s",
286 hardware->h_ifname);
287 goto malformed;
288 #ifdef ENABLE_FDP
289 }
290 #endif
291 }
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",
296 hardware->h_ifname);
297 goto malformed;
298 }
299 PEEK_DISCARD(6); /* Skip beginning of LLC */
300 proto = PEEK_UINT16;
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",
310 hardware->h_ifname);
311 goto malformed;
312 }
313
314 #if 0
315 /* Check checksum */
316 cksum = frame_checksum(pos, len_eth - 8,
317 #ifdef ENABLE_FDP
318 !fdp /* fdp = 0 -> cisco checksum */
319 #else
320 1 /* cisco checksum */
321 #endif
322 );
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);
327 goto malformed;
328 }
329 #endif
330
331 /* Check version */
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);
336 goto malformed;
337 }
338 chassis->c_ttl = PEEK_UINT8; /* TTL */
339 PEEK_DISCARD_UINT16; /* Checksum, already checked */
340
341 while (length) {
342 if (length < 4) {
343 LLOG_WARNX("CDP/FDP TLV header is too large for "
344 "frame received on %s",
345 hardware->h_ifname);
346 goto malformed;
347 }
348 tlv_type = PEEK_UINT16;
349 tlv_len = PEEK_UINT16 - 4;
350 PEEK_SAVE(tlv);
351 if ((tlv_len < 0) || (length < tlv_len)) {
352 LLOG_WARNX("incorrect size in CDP/FDP TLV header for frame "
353 "received on %s",
354 hardware->h_ifname);
355 goto malformed;
356 }
357 switch (tlv_type) {
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");
361 goto malformed;
362 }
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");
367 goto malformed;
368 }
369 memcpy(chassis->c_id, chassis->c_name, tlv_len);
370 chassis->c_id_len = tlv_len;
371 break;
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 "
380 "received on %s",
381 hardware->h_ifname);
382 goto malformed;
383 }
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 "
388 "received on %s",
389 hardware->h_ifname);
390 goto malformed;
391 }
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 "
397 "received on %s",
398 hardware->h_ifname);
399 goto malformed;
400 }
401 PEEK_DISCARD(address_len);
402 PEEK_SAVE(pos_next_address);
403 /* Next, we go back and try to extract
404 IPv4 address */
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);
412 if (mgmt == NULL) {
413 assert(errno == ENOMEM);
414 LLOG_WARN("unable to allocate memory for management address");
415 goto malformed;
416 }
417 TAILQ_INSERT_TAIL(&chassis->c_mgmt, mgmt, m_entries);
418 }
419 /* Go to the end of the address */
420 PEEK_RESTORE(pos_next_address);
421 }
422 break;
423 case CDP_TLV_PORT:
424 if ((port->p_descr = (char *)calloc(1, tlv_len + 1)) == NULL) {
425 LLOG_WARN("unable to allocate memory for port description");
426 goto malformed;
427 }
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");
432 goto malformed;
433 }
434 memcpy(port->p_id, port->p_descr, tlv_len);
435 port->p_id_len = tlv_len;
436 break;
437 case CDP_TLV_CAPABILITIES:
438 #ifdef ENABLE_FDP
439 if (fdp) {
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;
447 else
448 chassis->c_cap_enabled = LLDP_CAP_STATION;
449 chassis->c_cap_available = chassis->c_cap_enabled;
450 break;
451 }
452 #endif
453 CHECK_TLV_SIZE(4, "Capabilities");
454 caps = PEEK_UINT32;
455 if (caps & CDP_CAP_ROUTER)
456 chassis->c_cap_enabled |= LLDP_CAP_ROUTER;
457 if (caps & 0x0e)
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;
462 break;
463 case CDP_TLV_SOFTWARE:
464 software_len = tlv_len;
465 PEEK_SAVE(software);
466 break;
467 case CDP_TLV_PLATFORM:
468 platform_len = tlv_len;
469 PEEK_SAVE(platform);
470 break;
471 #ifdef ENABLE_DOT1
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 "
477 "structure for "
478 "tlv received on %s",
479 hardware->h_ifname);
480 goto malformed;
481 }
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",
486 hardware->h_ifname);
487 free(vlan);
488 goto malformed;
489 }
490 TAILQ_INSERT_TAIL(&port->p_vlans,
491 vlan, v_entries);
492 break;
493 #endif
494 default:
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++;
498 }
499 PEEK_DISCARD(tlv + tlv_len - pos);
500 }
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");
505 goto malformed;
506 }
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");
512 goto malformed;
513 }
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");
521 goto malformed;
522 }
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);
528 }
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",
537 hardware->h_ifname);
538 goto malformed;
539 }
540 *newchassis = chassis;
541 *newport = port;
542 return 1;
543
544 malformed:
545 lldpd_chassis_cleanup(chassis, 1);
546 lldpd_port_cleanup(cfg, port, 1);
547 free(port);
548 return -1;
549 }
550
551 #ifdef ENABLE_CDP
552 int
553 cdpv1_send(struct lldpd *global,
554 struct lldpd_hardware *hardware)
555 {
556 return cdp_send(global, hardware, 1);
557 }
558
559 int
560 cdpv2_send(struct lldpd *global,
561 struct lldpd_hardware *hardware)
562 {
563 return cdp_send(global, hardware, 2);
564 }
565 #endif
566
567 #ifdef ENABLE_FDP
568 int
569 fdp_send(struct lldpd *global,
570 struct lldpd_hardware *hardware)
571 {
572 return cdp_send(global, hardware, 0);
573 }
574 #endif
575
576 #ifdef ENABLE_CDP
577 static int
578 cdp_guess(char *pos, int length, int version)
579 {
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 */)
583 return 0;
584 if (PEEK_CMP(mcastaddr, ETH_ALEN) != 0)
585 return 0;
586 PEEK_DISCARD(ETH_ALEN); PEEK_DISCARD_UINT16; /* Ethernet */
587 PEEK_DISCARD(8); /* LLC */
588 return (PEEK_UINT8 == version);
589 }
590
591 int
592 cdpv1_guess(char *frame, int len)
593 {
594 return cdp_guess(frame, len, 1);
595 }
596
597 int
598 cdpv2_guess(char *frame, int len)
599 {
600 return cdp_guess(frame, len, 2);
601 }
602 #endif
603
604 #endif /* defined (ENABLE_CDP) || defined (ENABLE_FDP) */