Merge remote-tracking branch 'borkmann/master' into lldp
[netsniff-ng.git] / proto_lldp.c
blob30027b7f836a69589e8003b5c825c0edf60e2446
1 /*
2 * netsniff-ng - the packet sniffing st
3 * By Daniel Borkmann <daniel@netsniff-ng.org>
4 * Copyright 2012, 2013 Tobias Klauser <tklauser@distanz.ch>
5 * Subject to the GPL, version 2.
6 */
8 #include <stdint.h>
9 #include <arpa/inet.h> /* for inet_ntop() */
10 #include <netinet/in.h> /* for ntohs()/ntohl() */
12 #include "built_in.h"
13 #include "oui.h"
14 #include "pkt_buff.h"
15 #include "proto.h"
16 #include "protos.h"
17 #include "xutils.h"
19 #define EXTRACT_16BIT(x) ntohs(*((uint16_t *) (x)))
20 #define EXTRACT_32BIT(x) ntohl(*((uint32_t *) (x)))
22 #define LLDP_TLV_TYPE(tlv) (((tlv) & 0xFE00) >> 9)
23 #define LLDP_TLV_LENGTH(tlv) ((tlv) & 0x01FF)
26 * LLDP TLV types
28 #define LLDP_TLV_END 0
29 #define LLDP_TLV_CHASSIS_ID 1
30 #define LLDP_TLV_PORT_ID 2
31 #define LLDP_TLV_TTL 3
32 #define LLDP_TLV_PORT_DESC 4
33 #define LLDP_TLV_SYSTEM_NAME 5
34 #define LLDP_TLV_SYSTEM_DESC 6
35 #define LLDP_TLV_SYSTEM_CAP 7
36 #define LLDP_TLV_MGMT_ADDR 8
37 #define LLDP_TLV_ORG_SPECIFIC 127
40 * Chassis ID subtypes
42 #define LLDP_CHASSIS_SUBTYPE_CHASSIS 1
43 #define LLDP_CHASSIS_SUBTYPE_IF_ALIAS 2
44 #define LLDP_CHASSIS_SUBTYPE_PORT 3
45 #define LLDP_CHASSIS_SUBTYPE_MAC_ADDR 4
46 #define LLDP_CHASSIS_SUBTYPE_NET_ADDR 5
47 #define LLDP_CHASSIS_SUBTYPE_IF_NAME 6
48 #define LLDP_CHASSIS_SUBTYPE_LOCAL 7
51 * Port ID subtypes
53 #define LLDP_PORT_SUBTYPE_IF_ALIAS 1
54 #define LLDP_PORT_SUBTYPE_PORT_COMP 2
55 #define LLDP_PORT_SUBTYPE_MAC_ADDR 3
56 #define LLDP_PORT_SUBTYPE_NET_ADDR 4
57 #define LLDP_PORT_SUBTYPE_IF_NAME 5
58 #define LLDP_PORT_SUBTYPE_AGENT_CIRC_ID 6
59 #define LLDP_PORT_SUBTYPE_LOCAL 7
62 * System capabilits bit masks
64 #define LLDP_SYSTEM_CAP_OTHER (1 << 0)
65 #define LLDP_SYSTEM_CAP_REPEATER (1 << 1)
66 #define LLDP_SYSTEM_CAP_BRIDGE (1 << 2)
67 #define LLDP_SYSTEM_CAP_WLAN_AP (1 << 3)
68 #define LLDP_SYSTEM_CAP_ROUTER (1 << 4)
69 #define LLDP_SYSTEM_CAP_TELEPHONE (1 << 5)
70 #define LLDP_SYSTEM_CAP_DOCSIS (1 << 6)
71 #define LLDP_SYSTEM_CAP_STATION_ONLY (1 << 7)
74 * IANA address family numbers (only the ones we actually use)
75 * http://www.iana.org/assignments/address-family-numbers/address-family-numbers.txt
77 * TODO: Move these into own header if there are other users?
79 #define IANA_AF_IPV4 1
80 #define IANA_AF_IPV6 2
81 #define IANA_AF_802 6
83 static int lldp_print_net_addr(const uint8_t *addr, size_t addrlen)
85 uint8_t af;
86 char buf[64];
88 if (addrlen < 1)
89 return -EINVAL;
91 af = *addr++;
92 addrlen--;
93 switch (af) {
94 case IANA_AF_IPV4:
95 if (addrlen < 4)
96 return -EINVAL;
97 inet_ntop(AF_INET, addr, buf, sizeof(buf));
98 tprintf("%s", buf);
99 break;
100 case IANA_AF_IPV6:
101 if (addrlen < 16)
102 return -EINVAL;
103 inet_ntop(AF_INET6, addr, buf, sizeof(buf));
104 tprintf("%s", buf);
105 break;
106 case IANA_AF_802:
107 if (addrlen < 6)
108 return -EINVAL;
109 tprintf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
110 addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
111 break;
112 default:
113 tprintf("unknown address family");
114 break;
117 return 0;
120 static void lldp_print_cap(uint16_t cap)
122 unsigned int prev = 0;
124 if (cap & LLDP_SYSTEM_CAP_OTHER)
125 tprintf("%sOther", prev++ ? ", " : "");
126 if (cap & LLDP_SYSTEM_CAP_REPEATER)
127 tprintf("%sRepeater", prev++ ? ", " : "");
128 if (cap & LLDP_SYSTEM_CAP_BRIDGE)
129 tprintf("%sBridge", prev++ ? ", " : "");
130 if (cap & LLDP_SYSTEM_CAP_WLAN_AP)
131 tprintf("%sWLAN AP", prev++ ? ", " : "");
132 if (cap & LLDP_SYSTEM_CAP_ROUTER)
133 tprintf("%sRouter", prev++ ? ", " : "");
134 if (cap & LLDP_SYSTEM_CAP_TELEPHONE)
135 tprintf("%sTelephone", prev++ ? ", " : "");
136 if (cap & LLDP_SYSTEM_CAP_DOCSIS)
137 tprintf("%sDOCSIS", prev++ ? ", " : "");
138 if (cap & LLDP_SYSTEM_CAP_STATION_ONLY)
139 tprintf("%sStation only", prev++ ? ", " : "");
142 static void lldp(struct pkt_buff *pkt)
144 bool seen_chassis_id = false;
145 uint8_t subtype;
146 uint16_t tlv_hdr;
147 unsigned int tlv_type, tlv_len;
148 unsigned int len;
149 uint8_t *tlv_info_str;
150 uint16_t sys_cap, en_cap;
151 uint32_t oui;
153 len = pkt_len(pkt);
154 if (len == 0)
155 return;
157 tprintf(" [ LLDP ");
159 while (len >= sizeof(tlv_hdr)) {
160 tlv_hdr = EXTRACT_16BIT(pkt_pull(pkt, sizeof(tlv_hdr)));
161 tlv_type = LLDP_TLV_TYPE(tlv_hdr);
162 tlv_len = LLDP_TLV_LENGTH(tlv_hdr);
164 len -= sizeof(tlv_hdr);
166 if (tlv_type == LLDP_TLV_END || tlv_len == 0)
167 break; /* TODO: check if any (invalid) TLVs follow? */
168 if (len < tlv_len)
169 goto out_invalid;
170 if (!seen_chassis_id && tlv_type != LLDP_TLV_CHASSIS_ID)
171 goto out_invalid;
173 switch (tlv_type) {
174 case LLDP_TLV_CHASSIS_ID:
175 if (seen_chassis_id)
176 goto out_invalid;
178 tprintf("Chassis ID");
180 if (tlv_len < 2)
181 goto out_invalid;
183 tlv_info_str = pkt_pull(pkt, tlv_len);
184 if (tlv_info_str == NULL)
185 goto out_invalid;
187 subtype = *tlv_info_str++;
188 tprintf(" (Subtype %u => ", subtype);
190 switch (subtype) {
191 case LLDP_CHASSIS_SUBTYPE_MAC_ADDR:
192 if (tlv_len < 7)
193 goto out_invalid;
195 tprintf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
196 tlv_info_str[0], tlv_info_str[1],
197 tlv_info_str[2], tlv_info_str[3],
198 tlv_info_str[4], tlv_info_str[5]);
199 break;
200 case LLDP_CHASSIS_SUBTYPE_NET_ADDR:
201 if (lldp_print_net_addr(tlv_info_str, tlv_len))
202 goto out_invalid;
203 break;
204 case LLDP_CHASSIS_SUBTYPE_CHASSIS:
205 case LLDP_CHASSIS_SUBTYPE_IF_ALIAS:
206 case LLDP_CHASSIS_SUBTYPE_PORT:
207 case LLDP_CHASSIS_SUBTYPE_IF_NAME:
208 case LLDP_CHASSIS_SUBTYPE_LOCAL:
209 tputs_safe((const char *) tlv_info_str, tlv_len - 1);
210 break;
211 default:
212 tprintf("Reserved");
213 break;
216 tprintf(")");
217 seen_chassis_id = true;
218 break;
219 case LLDP_TLV_PORT_ID:
220 tprintf(", Port ID");
222 if (tlv_len < 2)
223 goto out_invalid;
225 tlv_info_str = pkt_pull(pkt, tlv_len);
226 if (tlv_info_str == NULL)
227 goto out_invalid;
229 subtype = *tlv_info_str++;
230 tprintf(" (Subtype %u => ", subtype);
232 switch (subtype) {
233 case LLDP_PORT_SUBTYPE_MAC_ADDR:
234 if (tlv_len < 7)
235 goto out_invalid;
237 tprintf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
238 tlv_info_str[0], tlv_info_str[1],
239 tlv_info_str[2], tlv_info_str[3],
240 tlv_info_str[4], tlv_info_str[5]);
241 break;
242 case LLDP_PORT_SUBTYPE_NET_ADDR:
243 if (lldp_print_net_addr(tlv_info_str, tlv_len))
244 goto out_invalid;
245 break;
246 case LLDP_PORT_SUBTYPE_IF_ALIAS:
247 case LLDP_PORT_SUBTYPE_PORT_COMP:
248 case LLDP_PORT_SUBTYPE_IF_NAME:
249 case LLDP_PORT_SUBTYPE_AGENT_CIRC_ID:
250 case LLDP_PORT_SUBTYPE_LOCAL:
251 tputs_safe((const char *) tlv_info_str, tlv_len - 1);
252 break;
253 default:
254 tprintf("Reserved");
255 break;
258 tprintf(")");
259 break;
260 case LLDP_TLV_TTL:
261 tprintf(", TTL");
263 if (tlv_len != 2)
264 goto out_invalid;
266 tlv_info_str = pkt_pull(pkt, tlv_len);
267 if (tlv_info_str == NULL)
268 goto out_invalid;
270 tprintf(" (%u)", EXTRACT_16BIT(tlv_info_str));
271 break;
272 case LLDP_TLV_PORT_DESC:
273 tprintf(", Port desc (");
275 tlv_info_str = pkt_pull(pkt, tlv_len);
276 if (tlv_info_str == NULL)
277 tprintf("none");
278 else
279 tputs_safe((const char *) tlv_info_str, tlv_len);
281 tprintf(")");
282 break;
283 case LLDP_TLV_SYSTEM_NAME:
284 tprintf(", Sys name (");
286 tlv_info_str = pkt_pull(pkt, tlv_len);
287 if (tlv_info_str == NULL)
288 tprintf("none");
289 else
290 tputs_safe((const char *) tlv_info_str, tlv_len);
292 tprintf(")");
293 break;
294 case LLDP_TLV_SYSTEM_DESC:
295 tprintf(", Sys desc (");
297 tlv_info_str = pkt_pull(pkt, tlv_len);
298 if (tlv_info_str == NULL)
299 tprintf("none");
300 else
301 tputs_safe((const char *) tlv_info_str, tlv_len);
303 tprintf(")");
304 break;
305 case LLDP_TLV_SYSTEM_CAP:
306 tprintf(", Sys Cap");
308 if (tlv_len != 4)
309 goto out_invalid;
311 tlv_info_str = pkt_pull(pkt, tlv_len);
312 if (tlv_info_str == NULL)
313 goto out_invalid;
315 sys_cap = EXTRACT_16BIT(tlv_info_str);
316 tlv_info_str += 2;
317 en_cap = EXTRACT_16BIT(tlv_info_str);
319 tprintf(" (");
320 lldp_print_cap(sys_cap);
321 tprintf(")");
322 tprintf(" Ena Cap (");
323 lldp_print_cap(en_cap);
324 tprintf(")");
325 break;
326 case LLDP_TLV_MGMT_ADDR:
327 tprintf(", Mgmt Addr");
329 if (tlv_len < 9 || tlv_len > 167)
330 goto out_invalid;
332 /* TODO */
334 pkt_pull(pkt, tlv_len);
335 break;
336 case LLDP_TLV_ORG_SPECIFIC:
337 tprintf(", Org specific");
339 if (tlv_len < 4)
340 goto out_invalid;
342 tlv_info_str = pkt_pull(pkt, 4);
343 if (tlv_info_str == NULL)
344 goto out_invalid;
346 oui = ntohl(*((uint32_t *) tlv_info_str));
347 subtype = oui & 0xff;
348 oui >>= 8;
349 tprintf(" (OUI %s, Subtype %u)", lookup_vendor_str(oui),
350 subtype);
352 /* Just eat it up, we don't know how to interpret it */
353 pkt_pull(pkt, tlv_len - 4);
354 break;
355 default:
356 pkt_pull(pkt, tlv_len);
357 /* TODO: just hexdump? */
358 break;
361 len -= tlv_len;
364 if (!seen_chassis_id)
365 goto out_invalid;
367 tprintf(" ]\n");
368 return;
370 out_invalid:
371 tprintf(" INVALID ]\n");
374 static void lldp_less(struct pkt_buff *pkt)
376 uint16_t *tmp_tlv = (uint16_t *) pkt_pull(pkt, sizeof(uint16_t));
378 if (tmp_tlv == NULL)
379 return;
381 tprintf("LLDP");
382 /* TODO: What is relevant here? */
385 struct protocol lldp_ops = {
386 .key = 0x88cc,
387 .print_full = lldp,
388 .print_less = lldp_less,