]> git.ipfire.org Git - thirdparty/lldpd.git/blob - src/lldp.c
Handle reception and display of PVID
[thirdparty/lldpd.git] / src / lldp.c
1 /*
2 * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
3 *
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.
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 #include "lldpd.h"
18
19 #include <unistd.h>
20 #include <errno.h>
21 #include <time.h>
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <sys/ioctl.h>
25 #include <netpacket/packet.h>
26 #include <linux/sockios.h>
27
28 int
29 lldp_send(struct lldpd *global, struct lldpd_chassis *chassis,
30 struct lldpd_hardware *hardware)
31 {
32 struct ether_header eh;
33 const u_int8_t mcastaddr[] = LLDP_MULTICAST_ADDR;
34 struct iovec *iov = NULL;
35 struct lldp_id chid, pid;
36 struct lldp_ttl ttl;
37 struct lldp_end end;
38 struct lldp_string name;
39 struct lldp_string descr;
40 struct lldp_string str;
41 struct lldp_cap cap;
42 struct lldp_mgmt mgmt;
43 #ifdef ENABLE_DOT1
44 const u_int8_t dot1[] = LLDP_TLV_ORG_DOT1;
45 struct lldp_vlan *ovlan = NULL;
46 int v;
47 struct lldpd_vlan *vlan;
48 #endif
49 #ifdef ENABLE_DOT3
50 const u_int8_t dot3[] = LLDP_TLV_ORG_DOT3;
51 struct lldp_aggreg aggreg;
52 struct lldp_macphy macphy;
53 struct lldp_mfs mfs;
54 #endif
55 #ifdef ENABLE_LLDPMED
56 int i;
57 const u_int8_t med[] = LLDP_TLV_ORG_MED;
58 struct lldpmed_cap medcap;
59 struct lldp_org medhw, medfw, medsw, medsn,
60 medmodel, medasset, medmanuf, medloc[3];
61 #endif
62 struct lldpd_port *port = &hardware->h_lport;
63 u_int c = -1, len = 0;
64 struct lldpd_frame *buffer;
65
66 #define IOV_NEW \
67 if ((iov = (struct iovec*)realloc(iov, (++c + 1) * \
68 sizeof(struct iovec))) == NULL) \
69 fatal(NULL);
70
71 /* Ethernet header */
72 memset(&eh, 0, sizeof(eh));
73 memcpy(&eh.ether_shost, &hardware->h_lladdr,
74 sizeof(eh.ether_shost));
75 memcpy(&eh.ether_dhost, &mcastaddr,
76 sizeof(eh.ether_dhost));
77 eh.ether_type = htons(ETHERTYPE_LLDP);
78 IOV_NEW;
79 iov[c].iov_base = &eh;
80 iov[c].iov_len = sizeof(struct ether_header);
81
82 /* Chassis ID */
83 memset(&chid, 0, sizeof(chid));
84 len = chassis->c_id_len + sizeof(chid);
85 chid.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_CHASSIS_ID,
86 len - sizeof(struct lldp_tlv_head));
87 chid.tlv_id_subtype = chassis->c_id_subtype;
88 IOV_NEW;
89 iov[c].iov_base = &chid;
90 iov[c].iov_len = sizeof(chid);
91 IOV_NEW;
92 iov[c].iov_base = chassis->c_id;
93 iov[c].iov_len = chassis->c_id_len;
94
95 /* Port ID */
96 memset(&pid, 0, sizeof(pid));
97 len = port->p_id_len + sizeof(pid);
98 pid.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_PORT_ID,
99 len - sizeof(struct lldp_tlv_head));
100 pid.tlv_id_subtype = port->p_id_subtype;
101 IOV_NEW;
102 iov[c].iov_base = &pid;
103 iov[c].iov_len = sizeof(pid);
104 IOV_NEW;
105 iov[c].iov_base = port->p_id;
106 iov[c].iov_len = port->p_id_len;
107
108 /* Time to live */
109 memset(&ttl, 0, sizeof(ttl));
110 len = sizeof(ttl);
111 ttl.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_TTL,
112 len - sizeof(struct lldp_tlv_head));
113 ttl.tlv_ttl = htons(chassis->c_ttl);
114 IOV_NEW;
115 iov[c].iov_base = &ttl;
116 iov[c].iov_len = sizeof(ttl);
117
118 /* System name */
119 memset(&name, 0, sizeof(name));
120 len = sizeof(name) + strlen(chassis->c_name);
121 name.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_SYSTEM_NAME,
122 len - sizeof(struct lldp_tlv_head));
123 IOV_NEW;
124 iov[c].iov_base = &name;
125 iov[c].iov_len = sizeof(name);
126 IOV_NEW;
127 iov[c].iov_base = chassis->c_name;
128 iov[c].iov_len = strlen(chassis->c_name);
129
130 /* System description */
131 memset(&descr, 0, sizeof(descr));
132 len = sizeof(descr) + strlen(chassis->c_descr);
133 descr.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_SYSTEM_DESCR,
134 len - sizeof(struct lldp_tlv_head));
135 IOV_NEW;
136 iov[c].iov_base = &descr;
137 iov[c].iov_len = sizeof(descr);
138 IOV_NEW;
139 iov[c].iov_base = chassis->c_descr;
140 iov[c].iov_len = strlen(chassis->c_descr);
141
142 /* System capabilities */
143 memset(&cap, 0, sizeof(cap));
144 len = sizeof(cap);
145 cap.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_SYSTEM_CAP,
146 len - sizeof(struct lldp_tlv_head));
147 cap.tlv_cap_available = htons(chassis->c_cap_available);
148 cap.tlv_cap_enabled = htons(chassis->c_cap_enabled);
149 IOV_NEW;
150 iov[c].iov_base = &cap;
151 iov[c].iov_len = len;
152
153 if (chassis->c_mgmt.s_addr != INADDR_ANY) {
154 /* Management address */
155 memset(&mgmt, 0, sizeof(mgmt));
156 len = sizeof(mgmt);
157 mgmt.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_MGMT_ADDR,
158 len - sizeof(struct lldp_tlv_head));
159 mgmt.mgmt_len = sizeof(struct in_addr) + sizeof(u_int8_t);
160 mgmt.mgmt_subtype = LLDP_MGMT_ADDR_IP4;
161 memcpy(&mgmt.mgmt_addr, &chassis->c_mgmt,
162 sizeof(struct in_addr));
163
164 /* Interface port type, OID */
165 if (chassis->c_mgmt_if == 0)
166 mgmt.mgmt_iface_subtype =
167 LLDP_MGMT_IFACE_UNKNOWN;
168 else {
169 mgmt.mgmt_iface_subtype =
170 LLDP_MGMT_IFACE_IFINDEX;
171 mgmt.mgmt_iface_id =
172 htonl(chassis->c_mgmt_if);
173 }
174 IOV_NEW;
175 iov[c].iov_base = &mgmt;
176 iov[c].iov_len = len;
177 }
178
179 /* Port description */
180 memset(&str, 0, sizeof(str));
181 len = sizeof(str) + strlen(port->p_descr);
182 str.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_PORT_DESCR,
183 len - sizeof(struct lldp_tlv_head));
184 IOV_NEW;
185 iov[c].iov_base = &str;
186 iov[c].iov_len = sizeof(str);
187 IOV_NEW;
188 iov[c].iov_base = port->p_descr;
189 iov[c].iov_len = strlen(port->p_descr);
190
191 #ifdef ENABLE_DOT1
192 /* VLANs */
193 v = 0;
194 TAILQ_FOREACH(vlan, &port->p_vlans, v_entries)
195 v++;
196 if ((v > 0) &&
197 ((ovlan = (struct lldp_vlan*)malloc(v*sizeof(struct lldp_vlan))) == NULL))
198 LLOG_WARN("no room for vlans");
199 else {
200 TAILQ_FOREACH(vlan, &port->p_vlans, v_entries) {
201 v--;
202 memset(&ovlan[v], 0, sizeof(ovlan[v]));
203 ovlan[v].tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_ORG,
204 sizeof(ovlan[v].tlv_org_id) +
205 sizeof(ovlan[v].tlv_org_subtype) + sizeof(ovlan[v].vid) +
206 sizeof(ovlan[v].len) + strlen(vlan->v_name));
207 memcpy(ovlan[v].tlv_org_id, dot1, sizeof(ovlan[v].tlv_org_id));
208 ovlan[v].tlv_org_subtype = LLDP_TLV_DOT1_VLANNAME;
209 ovlan[v].vid = htons(vlan->v_vid);
210 ovlan[v].len = strlen(vlan->v_name);
211 IOV_NEW;
212 iov[c].iov_base = &ovlan[v];
213 iov[c].iov_len = sizeof(ovlan[v]);
214 IOV_NEW;
215 iov[c].iov_base = vlan->v_name;
216 iov[c].iov_len = strlen(vlan->v_name);
217 }
218 }
219 #endif
220
221 #ifdef ENABLE_DOT3
222 /* Aggregation status */
223 memset(&aggreg, 0, sizeof(aggreg));
224 aggreg.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_ORG,
225 sizeof(aggreg.tlv_org_id) +
226 sizeof(aggreg.tlv_org_subtype) +
227 sizeof(aggreg.status) + sizeof(aggreg.id));
228 memcpy(aggreg.tlv_org_id, dot3, sizeof(aggreg.tlv_org_id));
229 aggreg.tlv_org_subtype = LLDP_TLV_DOT3_LA;
230 aggreg.status = (port->p_aggregid) ? 3:1; /* Bit 0 = capability ; Bit 1 = status */
231 aggreg.id = htonl(port->p_aggregid);
232 IOV_NEW;
233 iov[c].iov_base = &aggreg;
234 iov[c].iov_len = sizeof(aggreg);
235
236 /* MAC/PHY */
237 memset(&macphy, 0, sizeof(macphy));
238 macphy.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_ORG,
239 sizeof(macphy.tlv_org_id) +
240 sizeof(macphy.tlv_org_subtype) +
241 sizeof(macphy.autoneg) + sizeof(macphy.advertised) +
242 sizeof(macphy.mau));
243 memcpy(macphy.tlv_org_id, dot3, sizeof(macphy.tlv_org_id));
244 macphy.tlv_org_subtype = LLDP_TLV_DOT3_MAC;
245 macphy.autoneg = port->p_autoneg_support |
246 (port->p_autoneg_enabled << 1);
247 macphy.advertised = htons(port->p_autoneg_advertised);
248 macphy.mau = htons(port->p_mau_type);
249 IOV_NEW;
250 iov[c].iov_base = &macphy;
251 iov[c].iov_len = sizeof(macphy);
252
253 /* MFS */
254 memset(&mfs, 0, sizeof(mfs));
255 mfs.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_ORG,
256 sizeof(mfs.tlv_org_id) +
257 sizeof(mfs.tlv_org_subtype) +
258 sizeof(mfs.mfs));
259 memcpy(mfs.tlv_org_id, dot3, sizeof(mfs.tlv_org_id));
260 mfs.tlv_org_subtype = LLDP_TLV_DOT3_MFS;
261 mfs.mfs = htons(port->p_mfs);
262 IOV_NEW;
263 iov[c].iov_base = &mfs;
264 iov[c].iov_len = sizeof(mfs);
265 #endif
266
267 #ifdef ENABLE_LLDPMED
268 if (global->g_lchassis.c_med_cap_enabled) {
269 /* LLDP-MED cap */
270 memset(&medcap, 0, sizeof(medcap));
271 medcap.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_ORG,
272 sizeof(medcap.tlv_org_id) +
273 sizeof(medcap.tlv_org_subtype) +
274 sizeof(medcap.tlv_cap) + sizeof(medcap.tlv_type));
275 memcpy(medcap.tlv_org_id, med, sizeof(medcap.tlv_org_id));
276 medcap.tlv_org_subtype = LLDP_TLV_MED_CAP;
277 medcap.tlv_cap = htons(global->g_lchassis.c_med_cap_available);
278 medcap.tlv_type = global->g_lchassis.c_med_type;
279 IOV_NEW;
280 iov[c].iov_base = &medcap;
281 iov[c].iov_len = sizeof(medcap);
282
283 /* LLDP-MED inventory */
284 #define LLDP_INVENTORY(value, target, subtype) \
285 if (value) { \
286 memset(&target, 0, sizeof(target)); \
287 len = (strlen(value)>32)?32:strlen(value); \
288 target.tlv_head.type_len = \
289 LLDP_TLV_HEAD(LLDP_TLV_ORG, \
290 sizeof(target.tlv_org_id) + \
291 sizeof(target.tlv_org_subtype) + \
292 len); \
293 memcpy(target.tlv_org_id, med, \
294 sizeof(target.tlv_org_id)); \
295 target.tlv_org_subtype = subtype; \
296 IOV_NEW; \
297 iov[c].iov_base = &target; \
298 iov[c].iov_len = sizeof(target); \
299 IOV_NEW; \
300 iov[c].iov_base = value; \
301 iov[c].iov_len = len; \
302 }
303
304 if (global->g_lchassis.c_med_cap_enabled & LLDPMED_CAP_IV) {
305 LLDP_INVENTORY(global->g_lchassis.c_med_hw,
306 medhw, LLDP_TLV_MED_IV_HW);
307 LLDP_INVENTORY(global->g_lchassis.c_med_fw,
308 medfw, LLDP_TLV_MED_IV_FW);
309 LLDP_INVENTORY(global->g_lchassis.c_med_sw,
310 medsw, LLDP_TLV_MED_IV_SW);
311 LLDP_INVENTORY(global->g_lchassis.c_med_sn,
312 medsn, LLDP_TLV_MED_IV_SN);
313 LLDP_INVENTORY(global->g_lchassis.c_med_manuf,
314 medmanuf, LLDP_TLV_MED_IV_MANUF);
315 LLDP_INVENTORY(global->g_lchassis.c_med_model,
316 medmodel, LLDP_TLV_MED_IV_MODEL);
317 LLDP_INVENTORY(global->g_lchassis.c_med_asset,
318 medasset, LLDP_TLV_MED_IV_ASSET);
319 }
320
321 /* LLDP-MED location */
322 for (i = 0; i < LLDPMED_LOCFORMAT_LAST; i++) {
323 if (global->g_lchassis.c_med_location[i].format == i + 1) {
324 memset(&medloc[i], 0, sizeof(struct lldp_org));
325 medloc[i].tlv_head.type_len =
326 LLDP_TLV_HEAD(LLDP_TLV_ORG,
327 sizeof(medloc[i].tlv_org_id) +
328 sizeof(medloc[i].tlv_org_subtype) + 1 +
329 global->g_lchassis.c_med_location[i].data_len);
330 memcpy(medloc[i].tlv_org_id, med,
331 sizeof(medloc[i].tlv_org_id));
332 medloc[i].tlv_org_subtype = LLDP_TLV_MED_LOCATION;
333 IOV_NEW;
334 iov[c].iov_base = &medloc[i];
335 iov[c].iov_len = sizeof(medloc[i]);
336 IOV_NEW;
337 iov[c].iov_base =
338 &global->g_lchassis.c_med_location[i].format;
339 iov[c].iov_len = 1;
340 IOV_NEW;
341 iov[c].iov_base =
342 global->g_lchassis.c_med_location[i].data;
343 iov[c].iov_len =
344 global->g_lchassis.c_med_location[i].data_len;
345 }
346 }
347 }
348 #endif
349
350 /* END */
351 memset(&end, 0, sizeof(end));
352 IOV_NEW;
353 iov[c].iov_base = &end;
354 iov[c].iov_len = sizeof(end);
355
356 c++;
357 if (!global->g_multi ||
358 (hardware->h_mode == LLDPD_MODE_ANY) ||
359 (hardware->h_mode == LLDPD_MODE_LLDP)) {
360
361 if (writev((hardware->h_raw_real > 0) ? hardware->h_raw_real :
362 hardware->h_raw, iov, c) == -1) {
363 LLOG_WARN("unable to send packet on real device for %s",
364 hardware->h_ifname);
365 free(iov);
366 #ifdef ENABLE_DOT1
367 free(ovlan);
368 #endif
369 return ENETDOWN;
370 }
371
372 hardware->h_tx_cnt++;
373 }
374
375 iov_dump(&buffer, iov, c);
376 free(iov);
377 #ifdef ENABLE_DOT1
378 free(ovlan);
379 #endif
380 if (buffer != NULL) {
381
382 /* We assume that LLDP frame is the reference */
383 if ((hardware->h_llastframe == NULL) ||
384 (hardware->h_llastframe->size != buffer->size) ||
385 (memcmp(hardware->h_llastframe->frame, buffer->frame,
386 buffer->size) != 0)) {
387 free(hardware->h_llastframe);
388 hardware->h_llastframe = buffer;
389 hardware->h_llastchange = time(NULL);
390 } else
391 free(buffer);
392 }
393
394 return 0;
395 }
396
397 int
398 lldp_decode(struct lldpd *cfg, char *frame, int s,
399 struct lldpd_hardware *hardware,
400 struct lldpd_chassis **newchassis, struct lldpd_port **newport)
401 {
402 struct lldpd_chassis *chassis;
403 struct lldpd_port *port;
404 struct ether_header *ether;
405 const char lldpaddr[] = LLDP_MULTICAST_ADDR;
406 const char dot1[] = LLDP_TLV_ORG_DOT1;
407 const char dot3[] = LLDP_TLV_ORG_DOT3;
408 const char med[] = LLDP_TLV_ORG_MED;
409 int f; /* Current position in frame */
410 int size, type, subtype; /* TLV header */
411 char *b;
412 int gotend = 0;
413 struct lldpd_vlan *vlan;
414 int vlan_len;
415
416 if ((chassis = calloc(1, sizeof(struct lldpd_chassis))) == NULL) {
417 LLOG_WARN("failed to allocate remote chassis");
418 return -1;
419 }
420 if ((port = calloc(1, sizeof(struct lldpd_port))) == NULL) {
421 LLOG_WARN("failed to allocate remote port");
422 free(chassis);
423 return -1;
424 }
425 #ifdef ENABLE_DOT1
426 TAILQ_INIT(&port->p_vlans);
427 #endif
428
429 if (s < sizeof(struct ether_header)) {
430 LLOG_WARNX("too short frame received on %s", hardware->h_ifname);
431 goto malformed;
432 }
433 ether = (struct ether_header *)frame;
434 if (memcmp(ether->ether_dhost, lldpaddr, sizeof(lldpaddr)) != 0) {
435 LLOG_INFO("frame not targeted at LLDP multicast address received on %s",
436 hardware->h_ifname);
437 goto malformed;
438 }
439 if (ETHERTYPE_LLDP != ntohs(ether->ether_type)) {
440 LLOG_INFO("non LLDP frame received on %s",
441 hardware->h_ifname);
442 goto malformed;
443 }
444
445 f = sizeof(struct ether_header);
446 while ((f < s) && (!gotend)) {
447 if (f + 2 > s) {
448 LLOG_WARNX("tlv header too short received on %s",
449 hardware->h_ifname);
450 goto malformed;
451 }
452 size = ntohs(*(u_int16_t*)(frame + f)) & 0x1ff;
453 type = ntohs(*(u_int16_t*)(frame + f)) >> 9;
454 f += 2;
455 if (f + size > s) {
456 LLOG_WARNX("tlv header too short received on %s",
457 hardware->h_ifname);
458 goto malformed;
459 }
460 switch (type) {
461 case LLDP_TLV_END:
462 if (size != 0) {
463 LLOG_WARNX("lldp end received with size not null on %s",
464 hardware->h_ifname);
465 goto malformed;
466 }
467 if (f != s)
468 LLOG_DEBUG("extra data after lldp end on %s",
469 hardware->h_ifname);
470 gotend = 1;
471 break;
472 case LLDP_TLV_CHASSIS_ID:
473 case LLDP_TLV_PORT_ID:
474 if (size < 2) {
475 LLOG_WARNX("tlv id too small received on %s",
476 hardware->h_ifname);
477 goto malformed;
478 }
479 subtype = *(u_int8_t*)(frame + f);
480 f++;
481 if ((subtype == 0) || (subtype > 7)) {
482 LLOG_WARNX("unknown subtype for tlv id received on %s",
483 hardware->h_ifname);
484 goto malformed;
485 }
486 if ((b = (char *)calloc(1, size - 1)) == NULL) {
487 LLOG_WARN("unable to allocate memory for id tlv "
488 "received on %s",
489 hardware->h_ifname);
490 goto malformed;
491 }
492 memcpy(b, frame + f, size - 1);
493 if (type == LLDP_TLV_PORT_ID) {
494 port->p_id_subtype = subtype;
495 port->p_id = b;
496 port->p_id_len = size - 1;
497 } else {
498 chassis->c_id_subtype = subtype;
499 chassis->c_id = b;
500 chassis->c_id_len = size - 1;
501 }
502 f += size - 1;
503 break;
504 case LLDP_TLV_TTL:
505 if (size < 2) {
506 LLOG_WARNX("too short frame for ttl tlv received on %s",
507 hardware->h_ifname);
508 goto malformed;
509 }
510 chassis->c_ttl = ntohs(*(u_int16_t*)(frame + f));
511 f += size;
512 break;
513 case LLDP_TLV_PORT_DESCR:
514 case LLDP_TLV_SYSTEM_NAME:
515 case LLDP_TLV_SYSTEM_DESCR:
516 if (size < 1) {
517 LLOG_DEBUG("empty tlv received on %s",
518 hardware->h_ifname);
519 break;
520 }
521 if ((b = (char *)calloc(1, size + 1)) == NULL) {
522 LLOG_WARN("unable to allocate memory for string tlv "
523 "received on %s",
524 hardware->h_ifname);
525 goto malformed;
526 }
527 memcpy(b, frame + f, size);
528 f += size;
529 if (type == LLDP_TLV_PORT_DESCR)
530 port->p_descr = b;
531 else if (type == LLDP_TLV_SYSTEM_NAME)
532 chassis->c_name = b;
533 else chassis->c_descr = b;
534 break;
535 case LLDP_TLV_SYSTEM_CAP:
536 if (size < 4) {
537 LLOG_WARNX("too short system cap tlv "
538 "received on %s",
539 hardware->h_ifname);
540 goto malformed;
541 }
542 chassis->c_cap_available = ntohs(*(u_int16_t*)(frame + f));
543 f += 2;
544 chassis->c_cap_enabled = ntohs(*(u_int16_t*)(frame + f));
545 f += size - 2;
546 break;
547 case LLDP_TLV_MGMT_ADDR:
548 if (size < 11) {
549 LLOG_WARNX("too short management tlv received on %s",
550 hardware->h_ifname);
551 goto malformed;
552 }
553 if ((chassis->c_mgmt.s_addr == INADDR_ANY) &&
554 (*(u_int8_t*)(frame + f) == 5) &&
555 (*(u_int8_t*)(frame + f + 1) == 1)) {
556 /* We have an IPv4 address, we ignore anything else */
557 memcpy(&chassis->c_mgmt, frame + f + 2, sizeof(struct in_addr));
558 chassis->c_mgmt_if = 0;
559 /* We only handle ifIndex subtype */
560 if (*(u_int8_t*)(frame + f + 6) == LLDP_MGMT_IFACE_IFINDEX)
561 chassis->c_mgmt_if = ntohl(*(u_int32_t*)(frame + f + 7));
562 }
563 f += size;
564 break;
565 case LLDP_TLV_ORG:
566 if (size < 4) {
567 LLOG_WARNX("too short org tlv received on %s",
568 hardware->h_ifname);
569 goto malformed;
570 }
571 if (memcmp(dot1, frame + f, 3) == 0) {
572 #ifndef ENABLE_DOT1
573 f += size;
574 hardware->h_rx_unrecognized_cnt++;
575 #else
576 /* Dot1 */
577 switch (*(u_int8_t*)(frame + f + 3)) {
578 case LLDP_TLV_DOT1_VLANNAME:
579 if ((size < 7) ||
580 (size < 7 + *(u_int8_t*)(frame + f + 6))) {
581 LLOG_WARNX("too short vlan tlv "
582 "received on %s",
583 hardware->h_ifname);
584 goto malformed;
585 }
586 f += 4;
587 if ((vlan = (struct lldpd_vlan *)calloc(1,
588 sizeof(struct lldpd_vlan))) == NULL) {
589 LLOG_WARN("unable to alloc vlan "
590 "structure for "
591 "tlv received on %s",
592 hardware->h_ifname);
593 goto malformed;
594 }
595 vlan->v_vid = ntohs(*(u_int16_t*)(frame + f));
596 f += 2;
597 vlan_len = *(u_int8_t*)(frame + f);
598 f += 1;
599 if ((vlan->v_name =
600 (char *)calloc(1, vlan_len + 1)) == NULL) {
601 LLOG_WARN("unable to alloc vlan name for "
602 "tlv received on %s",
603 hardware->h_ifname);
604 goto malformed;
605 }
606 memcpy(vlan->v_name, frame + f,
607 vlan_len);
608 TAILQ_INSERT_TAIL(&port->p_vlans,
609 vlan, v_entries);
610 f += size - 7;
611 break;
612 case LLDP_TLV_DOT1_PVID:
613 if (size < 6) {
614 LLOG_WARNX("too short pvid tlv "
615 "received on %s",
616 hardware->h_ifname);
617 goto malformed;
618 }
619 port->p_pvid =
620 ntohs(*(u_int16_t*)(frame + f + 4));
621 f += size;
622 break;
623 default:
624 /* Unknown Dot1 TLV, ignore it */
625 f += size;
626 hardware->h_rx_unrecognized_cnt++;
627 }
628 #endif
629 } else if (memcmp(dot3, frame + f, 3) == 0) {
630 #ifndef ENABLE_DOT3
631 f += size;
632 hardware->h_rx_unrecognized_cnt++;
633 #else
634 /* Dot3 */
635 subtype = *(u_int8_t*)(frame + f + 3);
636 switch (subtype) {
637 case LLDP_TLV_DOT3_MAC:
638 f += 4;
639 if (size < 9) {
640 LLOG_WARNX("too short mac/phy tlv "
641 "received on %s",
642 hardware->h_ifname);
643 goto malformed;
644 }
645 port->p_autoneg_support =
646 *(u_int8_t*)(frame + f) && 0x1;
647 port->p_autoneg_enabled =
648 *(u_int8_t*)(frame + f) && 0x2;
649 f += 1;
650 port->p_autoneg_advertised =
651 ntohs(*(u_int16_t*)(frame + f));
652 f += 2;
653 port->p_mau_type =
654 ntohs(*(u_int16_t*)(frame + f));
655 f += size - 7;
656 break;
657 case LLDP_TLV_DOT3_LA:
658 if (size < 9) {
659 LLOG_WARNX("too short aggreg tlv "
660 "received on %s",
661 hardware->h_ifname);
662 goto malformed;
663 }
664 port->p_aggregid =
665 ntohl(*(u_int32_t*)(frame + f + 5));
666 f += size;
667 break;
668 case LLDP_TLV_DOT3_MFS:
669 if (size < 6) {
670 LLOG_WARNX("too short mfs tlv "
671 "received on %s",
672 hardware->h_ifname);
673 goto malformed;
674 }
675 port->p_mfs =
676 ntohs(*(u_int16_t*)(frame + f + 4));
677 f += size;
678 break;
679 default:
680 /* Unknown Dot3 TLV, ignore it */
681 f += size;
682 hardware->h_rx_unrecognized_cnt++;
683 }
684 #endif
685 } else if (memcmp(med, frame + f, 3) == 0) {
686 /* LLDP-MED */
687 #ifndef ENABLE_LLDPMED
688 f += size;
689 hardware->h_rx_unrecognized_cnt++;
690 #else
691 u_int32_t policy;
692 int loctype;
693
694 subtype = *(u_int8_t*)(frame + f + 3);
695 switch (subtype) {
696 case LLDP_TLV_MED_CAP:
697 f += 4;
698 if (size < 7) {
699 LLOG_WARNX("too short LLDP-MED cap "
700 "tlv received on %s",
701 hardware->h_ifname);
702 goto malformed;
703 }
704 chassis->c_med_cap_available =
705 ntohs(*(u_int16_t*)(frame + f));
706 f += 2;
707 chassis->c_med_type =
708 *(u_int8_t*)(frame + f);
709 f += size - 6;
710 chassis->c_med_cap_enabled |=
711 LLDPMED_CAP_CAP;
712 break;
713 case LLDP_TLV_MED_POLICY:
714 f += 4;
715 if (size < 8) {
716 LLOG_WARNX("too short LLDP-MED policy "
717 "tlv received on %s",
718 hardware->h_ifname);
719 goto malformed;
720 }
721 policy = ntohl(*((u_int32_t *)(frame + f)));
722 if (((policy >> 24) < 1) ||
723 ((policy >> 24) > LLDPMED_APPTYPE_LAST)) {
724 LLOG_INFO("unknown policy field %d "
725 "received on %s",
726 hardware->h_ifname);
727 f += 4;
728 break;
729 }
730 chassis->c_med_policy[(policy >> 24) - 1].type =
731 (policy >> 24);
732 chassis->c_med_policy[(policy >> 24) - 1].unknown =
733 ((policy & 0x800000) != 0);
734 chassis->c_med_policy[(policy >> 24) - 1].tagged =
735 ((policy & 0x400000) != 0);
736 chassis->c_med_policy[(policy >> 24) - 1].vid =
737 (policy & 0x001FFE00) >> 9;
738 chassis->c_med_policy[(policy >> 24) - 1].priority =
739 (policy & 0x1C0) >> 6;
740 chassis->c_med_policy[(policy >> 24) - 1].dscp =
741 policy & 0x3F;
742 f += size - 4;
743 chassis->c_med_cap_enabled |=
744 LLDPMED_CAP_POLICY;
745 break;
746 case LLDP_TLV_MED_LOCATION:
747 f += 4;
748 if (size <= 5) {
749 LLOG_WARNX("too short LLDP-MED location "
750 "tlv received on %s",
751 hardware->h_ifname);
752 goto malformed;
753 }
754 loctype = *(u_int8_t*)(frame + f);
755 f += 1;
756 if ((loctype < 1) || (loctype > LLDPMED_LOCFORMAT_LAST)) {
757 LLOG_INFO("unknown location type "
758 "received on %s",
759 hardware->h_ifname);
760 f += size - 5;
761 break;
762 }
763 if ((chassis->c_med_location[loctype - 1].data =
764 (char*)malloc(size - 5)) == NULL) {
765 LLOG_WARN("unable to allocate memory "
766 "for LLDP-MED location for "
767 "frame received on %s",
768 hardware->h_ifname);
769 goto malformed;
770 }
771 memcpy(chassis->c_med_location[loctype - 1].data,
772 (char*)(frame + f),
773 size - 5);
774 chassis->c_med_location[loctype - 1].data_len =
775 size - 5;
776 chassis->c_med_location[loctype - 1].format = loctype;
777 f += size - 5;
778 chassis->c_med_cap_enabled |=
779 LLDPMED_CAP_LOCATION;
780 break;
781 case LLDP_TLV_MED_MDI:
782 f += 4;
783 if (size < 7) {
784 LLOG_WARNX("too short LLDP-MED PoE-MDI "
785 "tlv received on %s",
786 hardware->h_ifname);
787 goto malformed;
788 }
789 switch (*(u_int8_t*)(frame + f) & 0xC0) {
790 case 0x0:
791 chassis->c_med_pow_devicetype = LLDPMED_POW_TYPE_PSE;
792 chassis->c_med_cap_enabled |=
793 LLDPMED_CAP_MDI_PSE;
794 switch (*(u_int8_t*)(frame + f) & 0x30) {
795 case 0x0:
796 chassis->c_med_pow_source =
797 LLDPMED_POW_SOURCE_UNKNOWN;
798 break;
799 case 0x10:
800 chassis->c_med_pow_source =
801 LLDPMED_POW_SOURCE_PRIMARY;
802 break;
803 case 0x20:
804 chassis->c_med_pow_source =
805 LLDPMED_POW_SOURCE_BACKUP;
806 break;
807 default:
808 chassis->c_med_pow_source =
809 LLDPMED_POW_SOURCE_RESERVED;
810 }
811 break;
812 case 0x40:
813 chassis->c_med_pow_devicetype = LLDPMED_POW_TYPE_PD;
814 chassis->c_med_cap_enabled |=
815 LLDPMED_CAP_MDI_PD;
816 switch (*(u_int8_t*)(frame + f) & 0x30) {
817 case 0x0:
818 chassis->c_med_pow_source =
819 LLDPMED_POW_SOURCE_UNKNOWN;
820 break;
821 case 0x10:
822 chassis->c_med_pow_source =
823 LLDPMED_POW_SOURCE_PSE;
824 break;
825 case 0x20:
826 chassis->c_med_pow_source =
827 LLDPMED_POW_SOURCE_LOCAL;
828 break;
829 default:
830 chassis->c_med_pow_source =
831 LLDPMED_POW_SOURCE_BOTH;
832 }
833 break;
834 default:
835 chassis->c_med_pow_devicetype =
836 LLDPMED_POW_TYPE_RESERVED;
837 }
838 switch (*(u_int8_t*)(frame + f) & 0x0F) {
839 case 0x0:
840 chassis->c_med_pow_priority =
841 LLDPMED_POW_PRIO_UNKNOWN;
842 break;
843 case 0x1:
844 chassis->c_med_pow_priority =
845 LLDPMED_POW_PRIO_CRITICAL;
846 break;
847 case 0x2:
848 chassis->c_med_pow_priority =
849 LLDPMED_POW_PRIO_HIGH;
850 break;
851 case 0x3:
852 chassis->c_med_pow_priority =
853 LLDPMED_POW_PRIO_LOW;
854 break;
855 default:
856 chassis->c_med_pow_priority =
857 LLDPMED_POW_PRIO_UNKNOWN;
858 }
859 f += 1;
860 chassis->c_med_pow_val =
861 ntohs(*(u_int16_t*)(frame + f));
862 f += size - 5;
863 break;
864 case LLDP_TLV_MED_IV_HW:
865 case LLDP_TLV_MED_IV_SW:
866 case LLDP_TLV_MED_IV_FW:
867 case LLDP_TLV_MED_IV_SN:
868 case LLDP_TLV_MED_IV_MANUF:
869 case LLDP_TLV_MED_IV_MODEL:
870 case LLDP_TLV_MED_IV_ASSET:
871 f += 4;
872 if (size <= 4)
873 b = NULL;
874 else {
875 if ((b = (char*)malloc(size - 3)) ==
876 NULL) {
877 LLOG_WARN("unable to allocate "
878 "memory for LLDP-MED "
879 "inventory for frame "
880 "received on %s",
881 hardware->h_ifname);
882 goto malformed;
883 }
884 strlcpy(b,
885 (char*)(frame + f),
886 size - 3);
887 }
888 switch (subtype) {
889 case LLDP_TLV_MED_IV_HW:
890 chassis->c_med_hw = b;
891 break;
892 case LLDP_TLV_MED_IV_FW:
893 chassis->c_med_fw = b;
894 break;
895 case LLDP_TLV_MED_IV_SW:
896 chassis->c_med_sw = b;
897 break;
898 case LLDP_TLV_MED_IV_SN:
899 chassis->c_med_sn = b;
900 break;
901 case LLDP_TLV_MED_IV_MANUF:
902 chassis->c_med_manuf = b;
903 break;
904 case LLDP_TLV_MED_IV_MODEL:
905 chassis->c_med_fw = b;
906 break;
907 case LLDP_TLV_MED_IV_ASSET:
908 chassis->c_med_asset = b;
909 break;
910 default:
911 LLOG_WARNX("should not be there!");
912 free(b);
913 break;
914 }
915 f += size - 4;
916 chassis->c_med_cap_enabled |=
917 LLDPMED_CAP_IV;
918 break;
919 default:
920 /* Unknown LLDP MED, ignore it */
921 f += size;
922 hardware->h_rx_unrecognized_cnt++;
923 }
924 #endif /* ENABLE_LLDPMED */
925 } else {
926 LLOG_INFO("unknown org tlv received on %s",
927 hardware->h_ifname);
928 hardware->h_rx_unrecognized_cnt++;
929 f += size;
930 }
931 break;
932 default:
933 LLOG_WARNX("unknown tlv (%d) received on %s",
934 type, hardware->h_ifname);
935 goto malformed;
936 }
937 }
938
939 /* Some random check */
940 if ((chassis->c_id == NULL) ||
941 (port->p_id == NULL) ||
942 (chassis->c_ttl == 0) ||
943 (gotend == 0)) {
944 LLOG_WARNX("some mandatory tlv are missing for frame received on %s",
945 hardware->h_ifname);
946 goto malformed;
947 }
948 #define NOTRECEIVED "Not received"
949 if (chassis->c_name == NULL) {
950 if ((chassis->c_name = (char *)calloc(1, strlen(NOTRECEIVED) + 1)) == NULL) {
951 LLOG_WARNX("unable to allocate null chassis name");
952 goto malformed;
953 }
954 memcpy(chassis->c_name, NOTRECEIVED, strlen(NOTRECEIVED));
955 }
956 if (chassis->c_descr == NULL) {
957 if ((chassis->c_descr = (char *)calloc(1, strlen(NOTRECEIVED) + 1)) == NULL) {
958 LLOG_WARNX("unable to allocate null chassis description");
959 goto malformed;
960 }
961 memcpy(chassis->c_descr, NOTRECEIVED, strlen(NOTRECEIVED));
962 }
963 if (port->p_descr == NULL) {
964 if ((port->p_descr = (char *)calloc(1, strlen(NOTRECEIVED) + 1)) == NULL) {
965 LLOG_WARNX("unable to allocate null port description");
966 goto malformed;
967 }
968 memcpy(port->p_descr, NOTRECEIVED, strlen(NOTRECEIVED));
969 }
970 *newchassis = chassis;
971 *newport = port;
972 return 1;
973 malformed:
974 lldpd_chassis_cleanup(chassis);
975 lldpd_port_cleanup(port);
976 return -1;
977 }