ifpps: Remove unused 'forks' member from struct ifstat
[netsniff-ng.git] / proto_lldp.c
blob205c8995b712762af94fe1ab8c5802f0b0501eec
1 /*
2 * netsniff-ng - the packet sniffing beast
3 * Copyright 2012, 2013 Tobias Klauser <tklauser@distanz.ch>
4 * Subject to the GPL, version 2.
5 */
7 #include <stdint.h>
8 #include <arpa/inet.h> /* for inet_ntop() */
9 #include <netinet/in.h> /* for ntohs()/ntohl() */
11 #include "built_in.h"
12 #include "oui.h"
13 #include "pkt_buff.h"
14 #include "proto.h"
15 #include "protos.h"
17 #define EXTRACT_16BIT(x) ntohs(*((uint16_t *) (x)))
18 #define EXTRACT_32BIT(x) ntohl(*((uint32_t *) (x)))
20 #define LLDP_TLV_TYPE(tlv) (((tlv) & 0xFE00) >> 9)
21 #define LLDP_TLV_LENGTH(tlv) ((tlv) & 0x01FF)
24 * LLDP TLV types
26 #define LLDP_TLV_END 0
27 #define LLDP_TLV_CHASSIS_ID 1
28 #define LLDP_TLV_PORT_ID 2
29 #define LLDP_TLV_TTL 3
30 #define LLDP_TLV_PORT_DESC 4
31 #define LLDP_TLV_SYSTEM_NAME 5
32 #define LLDP_TLV_SYSTEM_DESC 6
33 #define LLDP_TLV_SYSTEM_CAP 7
34 #define LLDP_TLV_MGMT_ADDR 8
35 #define LLDP_TLV_ORG_SPECIFIC 127
38 * Chassis ID subtypes
40 #define LLDP_CHASSIS_SUBTYPE_CHASSIS 1
41 #define LLDP_CHASSIS_SUBTYPE_IF_ALIAS 2
42 #define LLDP_CHASSIS_SUBTYPE_PORT 3
43 #define LLDP_CHASSIS_SUBTYPE_MAC_ADDR 4
44 #define LLDP_CHASSIS_SUBTYPE_NET_ADDR 5
45 #define LLDP_CHASSIS_SUBTYPE_IF_NAME 6
46 #define LLDP_CHASSIS_SUBTYPE_LOCAL 7
49 * Port ID subtypes
51 #define LLDP_PORT_SUBTYPE_IF_ALIAS 1
52 #define LLDP_PORT_SUBTYPE_PORT_COMP 2
53 #define LLDP_PORT_SUBTYPE_MAC_ADDR 3
54 #define LLDP_PORT_SUBTYPE_NET_ADDR 4
55 #define LLDP_PORT_SUBTYPE_IF_NAME 5
56 #define LLDP_PORT_SUBTYPE_AGENT_CIRC_ID 6
57 #define LLDP_PORT_SUBTYPE_LOCAL 7
60 * System capabilits bit masks
62 #define LLDP_SYSTEM_CAP_OTHER (1 << 0)
63 #define LLDP_SYSTEM_CAP_REPEATER (1 << 1)
64 #define LLDP_SYSTEM_CAP_BRIDGE (1 << 2)
65 #define LLDP_SYSTEM_CAP_WLAN_AP (1 << 3)
66 #define LLDP_SYSTEM_CAP_ROUTER (1 << 4)
67 #define LLDP_SYSTEM_CAP_TELEPHONE (1 << 5)
68 #define LLDP_SYSTEM_CAP_DOCSIS (1 << 6)
69 #define LLDP_SYSTEM_CAP_STATION_ONLY (1 << 7)
72 * Interface number subtypes (for Management addres TLV)
74 #define LLDP_IFACE_NUM_SUBTYPE_UNKNOWN 1
75 #define LLDP_IFACE_NUM_SUBTYPE_IF_INDEX 2
76 #define LLDP_IFACE_NUM_SUBTYPE_SYS_PORT 3
79 * IANA address family numbers (only the ones we actually use)
80 * http://www.iana.org/assignments/address-family-numbers/address-family-numbers.txt
82 * TODO: Move these into own header if there are other users?
84 #define IANA_AF_IPV4 1
85 #define IANA_AF_IPV6 2
86 #define IANA_AF_802 6
88 static int lldp_print_net_addr(const uint8_t *addr, size_t addrlen)
90 uint8_t af;
91 char buf[64];
93 if (addrlen < 1)
94 return -EINVAL;
96 af = *addr++;
97 addrlen--;
98 switch (af) {
99 case IANA_AF_IPV4:
100 if (addrlen < 4)
101 return -EINVAL;
102 inet_ntop(AF_INET, addr, buf, sizeof(buf));
103 tprintf("%s", buf);
104 break;
105 case IANA_AF_IPV6:
106 if (addrlen < 16)
107 return -EINVAL;
108 inet_ntop(AF_INET6, addr, buf, sizeof(buf));
109 tprintf("%s", buf);
110 break;
111 case IANA_AF_802:
112 if (addrlen < 6)
113 return -EINVAL;
114 tprintf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
115 addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
116 break;
117 default:
118 tprintf("unknown address family");
119 break;
122 return 0;
125 static void lldp_print_cap(uint16_t cap)
127 unsigned int prev = 0;
129 if (cap & LLDP_SYSTEM_CAP_OTHER)
130 tprintf("%sOther", prev++ ? ", " : "");
131 if (cap & LLDP_SYSTEM_CAP_REPEATER)
132 tprintf("%sRepeater", prev++ ? ", " : "");
133 if (cap & LLDP_SYSTEM_CAP_BRIDGE)
134 tprintf("%sBridge", prev++ ? ", " : "");
135 if (cap & LLDP_SYSTEM_CAP_WLAN_AP)
136 tprintf("%sWLAN AP", prev++ ? ", " : "");
137 if (cap & LLDP_SYSTEM_CAP_ROUTER)
138 tprintf("%sRouter", prev++ ? ", " : "");
139 if (cap & LLDP_SYSTEM_CAP_TELEPHONE)
140 tprintf("%sTelephone", prev++ ? ", " : "");
141 if (cap & LLDP_SYSTEM_CAP_DOCSIS)
142 tprintf("%sDOCSIS", prev++ ? ", " : "");
143 if (cap & LLDP_SYSTEM_CAP_STATION_ONLY)
144 tprintf("%sStation only", prev++ ? ", " : "");
147 static void lldp(struct pkt_buff *pkt)
149 unsigned int n_tlv = 0;
150 uint8_t subtype, mgmt_alen, mgmt_oidlen;
151 uint16_t tlv_hdr;
152 unsigned int tlv_type, tlv_len;
153 unsigned int len;
154 uint8_t *tlv_info_str;
155 uint16_t sys_cap, en_cap;
156 uint32_t oui;
158 len = pkt_len(pkt);
159 if (len == 0)
160 return;
162 tprintf(" [ LLDP ");
164 while (len >= sizeof(tlv_hdr)) {
165 uint8_t *data = pkt_pull(pkt, sizeof(tlv_hdr));
166 if (data == NULL)
167 goto out_invalid;
169 tlv_hdr = EXTRACT_16BIT(data);
170 tlv_type = LLDP_TLV_TYPE(tlv_hdr);
171 tlv_len = LLDP_TLV_LENGTH(tlv_hdr);
173 len -= sizeof(tlv_hdr);
175 if (tlv_type == LLDP_TLV_END && tlv_len == 0) {
176 /* Chassis ID, Port ID and TTL are mandatory */
177 if (n_tlv < 3)
178 goto out_invalid;
179 else
180 break;
182 if (len < tlv_len)
183 goto out_invalid;
185 switch (tlv_type) {
186 case LLDP_TLV_CHASSIS_ID:
188 * The mandatory chassis ID shall be the first TLV and
189 * shall appear exactly once.
191 if (n_tlv != 0)
192 goto out_invalid;
194 tprintf("Chassis ID");
196 if (tlv_len < 2)
197 goto out_invalid;
199 tlv_info_str = pkt_pull(pkt, tlv_len);
200 if (tlv_info_str == NULL)
201 goto out_invalid;
203 subtype = *tlv_info_str++;
204 tprintf(" (Subtype %u => ", subtype);
206 switch (subtype) {
207 case LLDP_CHASSIS_SUBTYPE_MAC_ADDR:
208 if (tlv_len < 7)
209 goto out_invalid;
211 tprintf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
212 tlv_info_str[0], tlv_info_str[1],
213 tlv_info_str[2], tlv_info_str[3],
214 tlv_info_str[4], tlv_info_str[5]);
215 break;
216 case LLDP_CHASSIS_SUBTYPE_NET_ADDR:
217 if (lldp_print_net_addr(tlv_info_str, tlv_len))
218 goto out_invalid;
219 break;
220 case LLDP_CHASSIS_SUBTYPE_CHASSIS:
221 case LLDP_CHASSIS_SUBTYPE_IF_ALIAS:
222 case LLDP_CHASSIS_SUBTYPE_PORT:
223 case LLDP_CHASSIS_SUBTYPE_IF_NAME:
224 case LLDP_CHASSIS_SUBTYPE_LOCAL:
225 tputs_safe((const char *) tlv_info_str, tlv_len - 1);
226 break;
227 default:
228 tprintf("Reserved");
229 break;
232 tprintf(")");
233 break;
234 case LLDP_TLV_PORT_ID:
236 * The mandatory port ID shall be the second TLV and
237 * shall appear exactly once.
239 if (n_tlv != 1)
240 goto out_invalid;
242 tprintf(", Port ID");
244 if (tlv_len < 2)
245 goto out_invalid;
247 tlv_info_str = pkt_pull(pkt, tlv_len);
248 if (tlv_info_str == NULL)
249 goto out_invalid;
251 subtype = *tlv_info_str++;
252 tprintf(" (Subtype %u => ", subtype);
254 switch (subtype) {
255 case LLDP_PORT_SUBTYPE_MAC_ADDR:
256 if (tlv_len < 7)
257 goto out_invalid;
259 tprintf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
260 tlv_info_str[0], tlv_info_str[1],
261 tlv_info_str[2], tlv_info_str[3],
262 tlv_info_str[4], tlv_info_str[5]);
263 break;
264 case LLDP_PORT_SUBTYPE_NET_ADDR:
265 if (lldp_print_net_addr(tlv_info_str, tlv_len))
266 goto out_invalid;
267 break;
268 case LLDP_PORT_SUBTYPE_IF_ALIAS:
269 case LLDP_PORT_SUBTYPE_PORT_COMP:
270 case LLDP_PORT_SUBTYPE_IF_NAME:
271 case LLDP_PORT_SUBTYPE_AGENT_CIRC_ID:
272 case LLDP_PORT_SUBTYPE_LOCAL:
273 tputs_safe((const char *) tlv_info_str, tlv_len - 1);
274 break;
275 default:
276 tprintf("Reserved");
277 break;
280 tprintf(")");
281 break;
282 case LLDP_TLV_TTL:
284 * The mandatory TTL shall be the third TLV and
285 * shall appear exactly once.
287 if (n_tlv != 2)
288 goto out_invalid;
290 tprintf(", TTL");
292 if (tlv_len != 2)
293 goto out_invalid;
295 tlv_info_str = pkt_pull(pkt, tlv_len);
296 if (tlv_info_str == NULL)
297 goto out_invalid;
299 tprintf(" (%u)", EXTRACT_16BIT(tlv_info_str));
300 break;
301 case LLDP_TLV_PORT_DESC:
302 tprintf(", Port desc (");
304 tlv_info_str = pkt_pull(pkt, tlv_len);
305 if (tlv_info_str == NULL)
306 tprintf("none");
307 else
308 tputs_safe((const char *) tlv_info_str, tlv_len);
310 tprintf(")");
311 break;
312 case LLDP_TLV_SYSTEM_NAME:
313 tprintf(", Sys name (");
315 tlv_info_str = pkt_pull(pkt, tlv_len);
316 if (tlv_info_str == NULL)
317 tprintf("none");
318 else
319 tputs_safe((const char *) tlv_info_str, tlv_len);
321 tprintf(")");
322 break;
323 case LLDP_TLV_SYSTEM_DESC:
324 tprintf(", Sys desc (");
326 tlv_info_str = pkt_pull(pkt, tlv_len);
327 if (tlv_info_str == NULL)
328 tprintf("none");
329 else
330 tputs_safe((const char *) tlv_info_str, tlv_len);
332 tprintf(")");
333 break;
334 case LLDP_TLV_SYSTEM_CAP:
335 tprintf(", Sys Cap");
337 if (tlv_len != 4)
338 goto out_invalid;
340 tlv_info_str = pkt_pull(pkt, tlv_len);
341 if (tlv_info_str == NULL)
342 goto out_invalid;
344 sys_cap = EXTRACT_16BIT(tlv_info_str);
345 tlv_info_str += sizeof(uint32_t);
346 en_cap = EXTRACT_16BIT(tlv_info_str);
348 tprintf(" (");
349 lldp_print_cap(sys_cap);
350 tprintf(")");
351 tprintf(" Ena Cap (");
352 lldp_print_cap(en_cap);
353 tprintf(")");
354 break;
355 case LLDP_TLV_MGMT_ADDR:
356 tprintf(", Mgmt Addr (");
358 if (tlv_len < 9 || tlv_len > 167)
359 goto out_invalid;
361 tlv_info_str = pkt_pull(pkt, tlv_len);
362 if (tlv_info_str == NULL)
363 goto out_invalid;
365 mgmt_alen = *tlv_info_str;
366 tlv_info_str++;
367 if (tlv_len - 1 < mgmt_alen)
368 goto out_invalid;
370 if (lldp_print_net_addr(tlv_info_str, mgmt_alen))
371 goto out_invalid;
372 tlv_info_str += mgmt_alen;
374 tprintf(", Iface Subtype %d/", *tlv_info_str);
375 switch (*tlv_info_str) {
376 case LLDP_IFACE_NUM_SUBTYPE_IF_INDEX:
377 tprintf("ifIndex");
378 break;
379 case LLDP_IFACE_NUM_SUBTYPE_SYS_PORT:
380 tprintf("System Port Number");
381 break;
382 default:
383 tprintf("Unknown");
384 break;
387 tlv_info_str++;
388 tprintf(", Iface Number %u", EXTRACT_32BIT(tlv_info_str));
390 tlv_info_str += 4;
391 mgmt_oidlen = *tlv_info_str;
392 if (tlv_len - mgmt_alen - sizeof(uint32_t) - 3 < mgmt_oidlen)
393 goto out_invalid;
394 if (mgmt_oidlen > 0) {
395 tprintf(", OID ");
396 tputs_safe((const char *) tlv_info_str + 1, mgmt_oidlen);
399 tprintf(")");
400 break;
401 case LLDP_TLV_ORG_SPECIFIC:
402 tprintf(", Org specific");
404 if (tlv_len < 4)
405 goto out_invalid;
407 tlv_info_str = pkt_pull(pkt, 4);
408 if (tlv_info_str == NULL)
409 goto out_invalid;
411 oui = ntohl(*((uint32_t *) tlv_info_str));
412 subtype = oui & 0xff;
413 oui >>= 8;
414 tprintf(" (OUI %s, Subtype %u)", lookup_vendor_str(oui),
415 subtype);
417 /* Just eat it up, we don't know how to interpret it */
418 pkt_pull(pkt, tlv_len - 4);
419 break;
420 default:
421 tprintf(", Unknown TLV %u", tlv_type);
422 pkt_pull(pkt, tlv_len);
423 break;
426 n_tlv++;
429 len -= tlv_len;
431 tprintf(" ]\n");
432 return;
434 out_invalid:
435 tprintf(" %sINVALID%s ]\n", colorize_start_full(black, red),
436 colorize_end());
439 static void lldp_less(struct pkt_buff *pkt)
441 unsigned int len, n_tlv = 0;
442 unsigned int tlv_type, tlv_len;
443 uint16_t tlv_hdr;
445 len = pkt_len(pkt);
447 while (len >= sizeof(tlv_hdr)) {
448 uint8_t *data = pkt_pull(pkt, sizeof(tlv_hdr));
449 if (data == NULL)
450 break;
452 tlv_hdr = EXTRACT_16BIT(data);
453 tlv_type = LLDP_TLV_TYPE(tlv_hdr);
454 tlv_len = LLDP_TLV_LENGTH(tlv_hdr);
456 n_tlv++;
457 len -= sizeof(tlv_hdr);
459 if (tlv_type == LLDP_TLV_END || tlv_len == 0)
460 break;
461 if (len < tlv_len)
462 break;
464 pkt_pull(pkt, tlv_len);
466 len -= tlv_len;
469 tprintf(" %u TLV%s", n_tlv, n_tlv == 1 ? "" : "s");
472 struct protocol lldp_ops = {
473 .key = 0x88cc,
474 .print_full = lldp,
475 .print_less = lldp_less,