build: flowtop: Only build ioops with GeoIP support enabled
[netsniff-ng.git] / proto_lldp.c
blobb3d2f8654b2f6a2aa527118ca09d5e562c565c42
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"
16 #define EXTRACT_16BIT(x) ntohs(*((uint16_t *) (x)))
17 #define EXTRACT_32BIT(x) ntohl(*((uint32_t *) (x)))
19 #define LLDP_TLV_TYPE(tlv) (((tlv) & 0xFE00) >> 9)
20 #define LLDP_TLV_LENGTH(tlv) ((tlv) & 0x01FF)
23 * LLDP TLV types
25 #define LLDP_TLV_END 0
26 #define LLDP_TLV_CHASSIS_ID 1
27 #define LLDP_TLV_PORT_ID 2
28 #define LLDP_TLV_TTL 3
29 #define LLDP_TLV_PORT_DESC 4
30 #define LLDP_TLV_SYSTEM_NAME 5
31 #define LLDP_TLV_SYSTEM_DESC 6
32 #define LLDP_TLV_SYSTEM_CAP 7
33 #define LLDP_TLV_MGMT_ADDR 8
34 #define LLDP_TLV_ORG_SPECIFIC 127
37 * Chassis ID subtypes
39 #define LLDP_CHASSIS_SUBTYPE_CHASSIS 1
40 #define LLDP_CHASSIS_SUBTYPE_IF_ALIAS 2
41 #define LLDP_CHASSIS_SUBTYPE_PORT 3
42 #define LLDP_CHASSIS_SUBTYPE_MAC_ADDR 4
43 #define LLDP_CHASSIS_SUBTYPE_NET_ADDR 5
44 #define LLDP_CHASSIS_SUBTYPE_IF_NAME 6
45 #define LLDP_CHASSIS_SUBTYPE_LOCAL 7
48 * Port ID subtypes
50 #define LLDP_PORT_SUBTYPE_IF_ALIAS 1
51 #define LLDP_PORT_SUBTYPE_PORT_COMP 2
52 #define LLDP_PORT_SUBTYPE_MAC_ADDR 3
53 #define LLDP_PORT_SUBTYPE_NET_ADDR 4
54 #define LLDP_PORT_SUBTYPE_IF_NAME 5
55 #define LLDP_PORT_SUBTYPE_AGENT_CIRC_ID 6
56 #define LLDP_PORT_SUBTYPE_LOCAL 7
59 * System capabilits bit masks
61 #define LLDP_SYSTEM_CAP_OTHER (1 << 0)
62 #define LLDP_SYSTEM_CAP_REPEATER (1 << 1)
63 #define LLDP_SYSTEM_CAP_BRIDGE (1 << 2)
64 #define LLDP_SYSTEM_CAP_WLAN_AP (1 << 3)
65 #define LLDP_SYSTEM_CAP_ROUTER (1 << 4)
66 #define LLDP_SYSTEM_CAP_TELEPHONE (1 << 5)
67 #define LLDP_SYSTEM_CAP_DOCSIS (1 << 6)
68 #define LLDP_SYSTEM_CAP_STATION_ONLY (1 << 7)
71 * Interface number subtypes (for Management addres TLV)
73 #define LLDP_IFACE_NUM_SUBTYPE_UNKNOWN 1
74 #define LLDP_IFACE_NUM_SUBTYPE_IF_INDEX 2
75 #define LLDP_IFACE_NUM_SUBTYPE_SYS_PORT 3
78 * IANA address family numbers (only the ones we actually use)
79 * http://www.iana.org/assignments/address-family-numbers/address-family-numbers.txt
81 * TODO: Move these into own header if there are other users?
83 #define IANA_AF_IPV4 1
84 #define IANA_AF_IPV6 2
85 #define IANA_AF_802 6
87 static int lldp_print_net_addr(const uint8_t *addr, size_t addrlen)
89 uint8_t af;
90 char buf[64];
92 if (addrlen < 1)
93 return -EINVAL;
95 af = *addr++;
96 addrlen--;
97 switch (af) {
98 case IANA_AF_IPV4:
99 if (addrlen < 4)
100 return -EINVAL;
101 inet_ntop(AF_INET, addr, buf, sizeof(buf));
102 tprintf("%s", buf);
103 break;
104 case IANA_AF_IPV6:
105 if (addrlen < 16)
106 return -EINVAL;
107 inet_ntop(AF_INET6, addr, buf, sizeof(buf));
108 tprintf("%s", buf);
109 break;
110 case IANA_AF_802:
111 if (addrlen < 6)
112 return -EINVAL;
113 tprintf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
114 addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
115 break;
116 default:
117 tprintf("unknown address family");
118 break;
121 return 0;
124 static inline void lldp_print_cap_one(const char *cap, unsigned int *prev)
126 tprintf("%s%s", *prev ? ", " : "", cap);
127 (*prev)++;
130 static void lldp_print_cap(uint16_t cap)
132 unsigned int prev = 0;
134 if (cap & LLDP_SYSTEM_CAP_OTHER)
135 lldp_print_cap_one("Other", &prev);
136 if (cap & LLDP_SYSTEM_CAP_REPEATER)
137 lldp_print_cap_one("Repeater", &prev);
138 if (cap & LLDP_SYSTEM_CAP_BRIDGE)
139 lldp_print_cap_one("Bridge", &prev);
140 if (cap & LLDP_SYSTEM_CAP_WLAN_AP)
141 lldp_print_cap_one("WLAN AP", &prev);
142 if (cap & LLDP_SYSTEM_CAP_ROUTER)
143 lldp_print_cap_one("Router", &prev);
144 if (cap & LLDP_SYSTEM_CAP_TELEPHONE)
145 lldp_print_cap_one("Telephone", &prev);
146 if (cap & LLDP_SYSTEM_CAP_DOCSIS)
147 lldp_print_cap_one("DOCSIS", &prev);
148 if (cap & LLDP_SYSTEM_CAP_STATION_ONLY)
149 lldp_print_cap_one("Station only", &prev);
152 static void lldp(struct pkt_buff *pkt)
154 unsigned int n_tlv = 0;
155 uint8_t subtype, mgmt_alen, mgmt_oidlen;
156 uint16_t tlv_hdr;
157 unsigned int tlv_type, tlv_len;
158 unsigned int len;
159 uint8_t *tlv_info_str;
160 uint16_t sys_cap, en_cap;
161 uint32_t oui;
163 len = pkt_len(pkt);
164 if (len == 0)
165 return;
167 tprintf(" [ LLDP ");
169 while (len >= sizeof(tlv_hdr)) {
170 uint8_t *data = pkt_pull(pkt, sizeof(tlv_hdr));
171 if (data == NULL)
172 goto out_invalid;
174 tlv_hdr = EXTRACT_16BIT(data);
175 tlv_type = LLDP_TLV_TYPE(tlv_hdr);
176 tlv_len = LLDP_TLV_LENGTH(tlv_hdr);
178 len -= sizeof(tlv_hdr);
180 if (tlv_type == LLDP_TLV_END && tlv_len == 0) {
181 /* Chassis ID, Port ID and TTL are mandatory */
182 if (n_tlv < 3)
183 goto out_invalid;
184 else
185 break;
187 if (len < tlv_len)
188 goto out_invalid;
190 switch (tlv_type) {
191 case LLDP_TLV_CHASSIS_ID:
193 * The mandatory chassis ID shall be the first TLV and
194 * shall appear exactly once.
196 if (n_tlv != 0)
197 goto out_invalid;
199 tprintf("Chassis ID");
201 if (tlv_len < 2)
202 goto out_invalid;
204 tlv_info_str = pkt_pull(pkt, tlv_len);
205 if (tlv_info_str == NULL)
206 goto out_invalid;
208 subtype = *tlv_info_str++;
209 tprintf(" (Subtype %u => ", subtype);
211 switch (subtype) {
212 case LLDP_CHASSIS_SUBTYPE_MAC_ADDR:
213 if (tlv_len < 7)
214 goto out_invalid;
216 tprintf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
217 tlv_info_str[0], tlv_info_str[1],
218 tlv_info_str[2], tlv_info_str[3],
219 tlv_info_str[4], tlv_info_str[5]);
220 break;
221 case LLDP_CHASSIS_SUBTYPE_NET_ADDR:
222 if (lldp_print_net_addr(tlv_info_str, tlv_len))
223 goto out_invalid;
224 break;
225 case LLDP_CHASSIS_SUBTYPE_CHASSIS:
226 case LLDP_CHASSIS_SUBTYPE_IF_ALIAS:
227 case LLDP_CHASSIS_SUBTYPE_PORT:
228 case LLDP_CHASSIS_SUBTYPE_IF_NAME:
229 case LLDP_CHASSIS_SUBTYPE_LOCAL:
230 tputs_safe((const char *) tlv_info_str, tlv_len - 1);
231 break;
232 default:
233 tprintf("Reserved");
234 break;
237 tprintf(")");
238 break;
239 case LLDP_TLV_PORT_ID:
241 * The mandatory port ID shall be the second TLV and
242 * shall appear exactly once.
244 if (n_tlv != 1)
245 goto out_invalid;
247 tprintf(", Port ID");
249 if (tlv_len < 2)
250 goto out_invalid;
252 tlv_info_str = pkt_pull(pkt, tlv_len);
253 if (tlv_info_str == NULL)
254 goto out_invalid;
256 subtype = *tlv_info_str++;
257 tprintf(" (Subtype %u => ", subtype);
259 switch (subtype) {
260 case LLDP_PORT_SUBTYPE_MAC_ADDR:
261 if (tlv_len < 7)
262 goto out_invalid;
264 tprintf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
265 tlv_info_str[0], tlv_info_str[1],
266 tlv_info_str[2], tlv_info_str[3],
267 tlv_info_str[4], tlv_info_str[5]);
268 break;
269 case LLDP_PORT_SUBTYPE_NET_ADDR:
270 if (lldp_print_net_addr(tlv_info_str, tlv_len))
271 goto out_invalid;
272 break;
273 case LLDP_PORT_SUBTYPE_IF_ALIAS:
274 case LLDP_PORT_SUBTYPE_PORT_COMP:
275 case LLDP_PORT_SUBTYPE_IF_NAME:
276 case LLDP_PORT_SUBTYPE_AGENT_CIRC_ID:
277 case LLDP_PORT_SUBTYPE_LOCAL:
278 tputs_safe((const char *) tlv_info_str, tlv_len - 1);
279 break;
280 default:
281 tprintf("Reserved");
282 break;
285 tprintf(")");
286 break;
287 case LLDP_TLV_TTL:
289 * The mandatory TTL shall be the third TLV and
290 * shall appear exactly once.
292 if (n_tlv != 2)
293 goto out_invalid;
295 tprintf(", TTL");
297 if (tlv_len != 2)
298 goto out_invalid;
300 tlv_info_str = pkt_pull(pkt, tlv_len);
301 if (tlv_info_str == NULL)
302 goto out_invalid;
304 tprintf(" (%u)", EXTRACT_16BIT(tlv_info_str));
305 break;
306 case LLDP_TLV_PORT_DESC:
307 tprintf(", Port desc (");
309 tlv_info_str = pkt_pull(pkt, tlv_len);
310 if (tlv_info_str == NULL)
311 tprintf("none");
312 else
313 tputs_safe((const char *) tlv_info_str, tlv_len);
315 tprintf(")");
316 break;
317 case LLDP_TLV_SYSTEM_NAME:
318 tprintf(", Sys name (");
320 tlv_info_str = pkt_pull(pkt, tlv_len);
321 if (tlv_info_str == NULL)
322 tprintf("none");
323 else
324 tputs_safe((const char *) tlv_info_str, tlv_len);
326 tprintf(")");
327 break;
328 case LLDP_TLV_SYSTEM_DESC:
329 tprintf(", Sys desc (");
331 tlv_info_str = pkt_pull(pkt, tlv_len);
332 if (tlv_info_str == NULL)
333 tprintf("none");
334 else
335 tputs_safe((const char *) tlv_info_str, tlv_len);
337 tprintf(")");
338 break;
339 case LLDP_TLV_SYSTEM_CAP:
340 tprintf(", Sys Cap");
342 if (tlv_len != 4)
343 goto out_invalid;
345 tlv_info_str = pkt_pull(pkt, tlv_len);
346 if (tlv_info_str == NULL)
347 goto out_invalid;
349 sys_cap = EXTRACT_16BIT(tlv_info_str);
350 tlv_info_str += sizeof(uint32_t);
351 en_cap = EXTRACT_16BIT(tlv_info_str);
353 tprintf(" (");
354 lldp_print_cap(sys_cap);
355 tprintf(")");
356 tprintf(" Ena Cap (");
357 lldp_print_cap(en_cap);
358 tprintf(")");
359 break;
360 case LLDP_TLV_MGMT_ADDR:
361 tprintf(", Mgmt Addr (");
363 if (tlv_len < 9 || tlv_len > 167)
364 goto out_invalid;
366 tlv_info_str = pkt_pull(pkt, tlv_len);
367 if (tlv_info_str == NULL)
368 goto out_invalid;
370 mgmt_alen = *tlv_info_str;
371 tlv_info_str++;
372 if (tlv_len - 1 < mgmt_alen)
373 goto out_invalid;
375 if (lldp_print_net_addr(tlv_info_str, mgmt_alen))
376 goto out_invalid;
377 tlv_info_str += mgmt_alen;
379 tprintf(", Iface Subtype %d/", *tlv_info_str);
380 switch (*tlv_info_str) {
381 case LLDP_IFACE_NUM_SUBTYPE_IF_INDEX:
382 tprintf("ifIndex");
383 break;
384 case LLDP_IFACE_NUM_SUBTYPE_SYS_PORT:
385 tprintf("System Port Number");
386 break;
387 default:
388 tprintf("Unknown");
389 break;
392 tlv_info_str++;
393 tprintf(", Iface Number %u", EXTRACT_32BIT(tlv_info_str));
395 tlv_info_str += 4;
396 mgmt_oidlen = *tlv_info_str;
397 if (tlv_len - mgmt_alen - sizeof(uint32_t) - 3 < mgmt_oidlen)
398 goto out_invalid;
399 if (mgmt_oidlen > 0) {
400 tprintf(", OID ");
401 tputs_safe((const char *) tlv_info_str + 1, mgmt_oidlen);
404 tprintf(")");
405 break;
406 case LLDP_TLV_ORG_SPECIFIC:
407 tprintf(", Org specific");
409 if (tlv_len < 4)
410 goto out_invalid;
412 tlv_info_str = pkt_pull(pkt, 4);
413 if (tlv_info_str == NULL)
414 goto out_invalid;
416 oui = ntohl(*((uint32_t *) tlv_info_str));
417 subtype = oui & 0xff;
418 oui >>= 8;
419 tprintf(" (OUI %s, Subtype %u)", lookup_vendor_str(oui),
420 subtype);
422 /* Just eat it up, we don't know how to interpret it */
423 pkt_pull(pkt, tlv_len - 4);
424 break;
425 default:
426 tprintf(", Unknown TLV %u", tlv_type);
427 pkt_pull(pkt, tlv_len);
428 break;
431 n_tlv++;
434 len -= tlv_len;
436 tprintf(" ]\n");
437 return;
439 out_invalid:
440 tprintf(" %sINVALID%s ]\n", colorize_start_full(black, red),
441 colorize_end());
444 static void lldp_less(struct pkt_buff *pkt)
446 unsigned int len, n_tlv = 0;
447 unsigned int tlv_type, tlv_len;
448 uint16_t tlv_hdr;
450 len = pkt_len(pkt);
452 while (len >= sizeof(tlv_hdr)) {
453 uint8_t *data = pkt_pull(pkt, sizeof(tlv_hdr));
454 if (data == NULL)
455 break;
457 tlv_hdr = EXTRACT_16BIT(data);
458 tlv_type = LLDP_TLV_TYPE(tlv_hdr);
459 tlv_len = LLDP_TLV_LENGTH(tlv_hdr);
461 n_tlv++;
462 len -= sizeof(tlv_hdr);
464 if (tlv_type == LLDP_TLV_END || tlv_len == 0)
465 break;
466 if (len < tlv_len)
467 break;
469 pkt_pull(pkt, tlv_len);
471 len -= tlv_len;
474 tprintf(" %u TLV%s", n_tlv, n_tlv == 1 ? "" : "s");
477 struct protocol lldp_ops = {
478 .key = 0x88cc,
479 .print_full = lldp,
480 .print_less = lldp_less,