xref: /openbsd/usr.sbin/tcpdump/print-lldp.c (revision a7b8518d)
1 /*	$OpenBSD: print-lldp.c,v 1.11 2025/01/02 01:21:35 dlg Exp $	*/
2 
3 /*
4  * Copyright (c) 2006 Reyk Floeter <reyk@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/time.h>
20 #include <sys/socket.h>
21 
22 #include <net/if.h>
23 
24 #include <netinet/in.h>
25 #include <netinet/if_ether.h>
26 #include <arpa/inet.h>
27 
28 #include <ctype.h>
29 #include <stdio.h>
30 #include <string.h>
31 
32 #include "addrtoname.h"
33 #include "extract.h"
34 #include "interface.h"
35 #include "afnum.h"
36 
37 enum {
38 	LLDP_TLV_END			= 0,
39 	LLDP_TLV_CHASSIS_ID		= 1,
40 	LLDP_TLV_PORT_ID		= 2,
41 	LLDP_TLV_TTL			= 3,
42 	LLDP_TLV_PORT_DESCR		= 4,
43 	LLDP_TLV_SYSTEM_NAME		= 5,
44 	LLDP_TLV_SYSTEM_DESCR		= 6,
45 	LLDP_TLV_SYSTEM_CAP		= 7,
46 	LLDP_TLV_MANAGEMENT_ADDR	= 8,
47 	LLDP_TLV_ORG			= 127
48 };
49 
50 enum {
51 	LLDP_CHASSISID_SUBTYPE_CHASSIS	= 1,
52 	LLDP_CHASSISID_SUBTYPE_IFALIAS	= 2,
53 	LLDP_CHASSISID_SUBTYPE_PORT	= 3,
54 	LLDP_CHASSISID_SUBTYPE_LLADDR	= 4,
55 	LLDP_CHASSISID_SUBTYPE_ADDR	= 5,
56 	LLDP_CHASSISID_SUBTYPE_IFNAME	= 6,
57 	LLDP_CHASSISID_SUBTYPE_LOCAL	= 7
58 };
59 
60 enum {
61 	LLDP_PORTID_SUBTYPE_IFALIAS	= 1,
62 	LLDP_PORTID_SUBTYPE_PORT	= 2,
63 	LLDP_PORTID_SUBTYPE_LLADDR	= 3,
64 	LLDP_PORTID_SUBTYPE_ADDR	= 4,
65 	LLDP_PORTID_SUBTYPE_IFNAME	= 5,
66 	LLDP_PORTID_SUBTYPE_AGENTCID	= 6,
67 	LLDP_PORTID_SUBTYPE_LOCAL	= 7
68 };
69 
70 #define LLDP_CAP_OTHER          0x01
71 #define LLDP_CAP_REPEATER       0x02
72 #define LLDP_CAP_BRIDGE         0x04
73 #define LLDP_CAP_WLAN           0x08
74 #define LLDP_CAP_ROUTER         0x10
75 #define LLDP_CAP_TELEPHONE      0x20
76 #define LLDP_CAP_DOCSIS         0x40
77 #define LLDP_CAP_STATION        0x80
78 #define LLDP_CAP_BITS								\
79 	"\20\01OTHER\02REPEATER\03BRIDGE\04WLAN\05ROUTER\06TELEPHONE"		\
80 	"\07DOCSIS\10STATION"
81 
82 enum {
83 	LLDP_MGMT_IFACE_UNKNOWN	= 1,
84 	LLDP_MGMT_IFACE_IFINDEX	= 2,
85 	LLDP_MGMT_IFACE_SYSPORT	= 3
86 };
87 
88 static const char *afnumber[] = AFNUM_NAME_STR;
89 
90 void		 lldp_print_str(const u_int8_t *, int);
91 const char	*lldp_print_addr(int, const void *);
92 void		 lldp_print_id(int, const u_int8_t *, int);
93 
94 void
lldp_print_str(const u_int8_t * str,int len)95 lldp_print_str(const u_int8_t *str, int len)
96 {
97 	int i;
98 	printf("\"");
99 	for (i = 0; i < len; i++) {
100 		int ch = str[i];
101 		if (ch == '\r')
102 			continue;
103 		if (ch == '\n')
104 			ch = ' ';
105 		printf("%c", isprint(ch) ? ch : '.');
106 	}
107 	printf("\"");
108 }
109 
110 const char *
lldp_print_addr(int af,const void * addr)111 lldp_print_addr(int af, const void *addr)
112 {
113 	static char buf[48];
114 	if (inet_ntop(af, addr, buf, sizeof(buf)) == NULL)
115 		return ("?");
116 	return (buf);
117 }
118 
119 void
lldp_print_id(int type,const u_int8_t * ptr,int len)120 lldp_print_id(int type, const u_int8_t *ptr, int len)
121 {
122 	u_int8_t id;
123 	const u_int8_t *data;
124 
125 	id = *(u_int8_t *)ptr;
126 	len -= sizeof(u_int8_t);
127 	data = ptr + sizeof(u_int8_t);
128 	if (len <= 0)
129 		return;
130 
131 	if (type == LLDP_TLV_CHASSIS_ID) {
132 		switch (id) {
133 		case LLDP_CHASSISID_SUBTYPE_CHASSIS:
134 			printf("chassis ");
135 			lldp_print_str(data, len);
136 			break;
137 		case LLDP_CHASSISID_SUBTYPE_IFALIAS:
138 			printf("ifalias");
139 			break;
140 		case LLDP_CHASSISID_SUBTYPE_PORT:
141 			printf("port");
142 			break;
143 		case LLDP_CHASSISID_SUBTYPE_LLADDR:
144 			printf("lladdr %s", etheraddr_string(data));
145 			break;
146 		case LLDP_CHASSISID_SUBTYPE_ADDR:
147 			printf("addr");
148 			break;
149 		case LLDP_CHASSISID_SUBTYPE_IFNAME:
150 			printf("ifname ");
151 			lldp_print_str(data, len);
152 			break;
153 		case LLDP_CHASSISID_SUBTYPE_LOCAL:
154 			printf("local ");
155 			lldp_print_str(data, len);
156 			break;
157 		default:
158 			printf("unknown 0x%02x", id);
159 			break;
160 		}
161 
162 	} else if (type == LLDP_TLV_PORT_ID) {
163 		switch (id) {
164 		case LLDP_PORTID_SUBTYPE_IFALIAS:
165 			printf("ifalias");
166 			break;
167 		case LLDP_PORTID_SUBTYPE_PORT:
168 			printf("port");
169 			break;
170 		case LLDP_PORTID_SUBTYPE_LLADDR:
171 			printf("lladdr %s", etheraddr_string(data));
172 			break;
173 		case LLDP_PORTID_SUBTYPE_ADDR:
174 			printf("addr");
175 			break;
176 		case LLDP_PORTID_SUBTYPE_IFNAME:
177 			printf("ifname ");
178 			lldp_print_str(data, len);
179 			break;
180 		case LLDP_PORTID_SUBTYPE_AGENTCID:
181 			printf("agentcid");
182 			break;
183 		case LLDP_PORTID_SUBTYPE_LOCAL:
184 			printf("local ");
185 			lldp_print_str(data, len);
186 			break;
187 		default:
188 			printf("unknown 0x%02x", id);
189 			break;
190 		}
191 	}
192 }
193 
194 static void
lldp_print_mgmt_addr(const u_int8_t * ptr,u_int len)195 lldp_print_mgmt_addr(const u_int8_t *ptr, u_int len)
196 {
197 	u_int alen;
198 	u_int afnum;
199 	const uint8_t *maddr;
200 	uint32_t ifidx;
201 
202 	if (len < 1) {
203 		printf(" unexpected len %u", len);
204 		return;
205 	}
206 	alen = ptr[0];
207 
208 	ptr++;
209 	len--;
210 
211 	if (alen < 2 || alen > len) {
212 		printf(" unexpected address len %u", len);
213 		return;
214 	}
215 	afnum = ptr[0];
216 	maddr = ptr + 1;
217 
218 	ptr += alen;
219 	len -= alen;
220 
221 	alen--;
222 	switch (afnum) {
223 	case AFNUM_INET:
224 		if (alen != sizeof(struct in_addr))
225 			goto afnum_default;
226 		printf(" %s", lldp_print_addr(AF_INET, maddr));
227 		break;
228 	case AFNUM_INET6:
229 		if (alen != sizeof(struct in6_addr))
230 			goto afnum_default;
231 		printf(" %s", lldp_print_addr(AF_INET6, maddr));
232 		break;
233 	case AFNUM_802:
234 		if (alen != ETHER_ADDR_LEN)
235 			goto afnum_default;
236 		printf(" %s", etheraddr_string(maddr));
237 		break;
238 	default:
239 	afnum_default:
240 		if (afnum < AFNUM_MAX)
241 			printf(" %s", afnumber[afnum]);
242 		else
243 			printf(" afnum-%u", afnum);
244 		printf(" len %u", alen);
245 		break;
246 	}
247 
248 	if (len < 5) {
249 		printf(" unexpected interface len %u", len);
250 		return;
251 	}
252 
253 	ifidx = EXTRACT_32BITS(ptr + 1);
254 	if (ifidx != 0) {
255 		switch (*ptr) {
256 		case LLDP_MGMT_IFACE_UNKNOWN:
257 			printf(" Unknown");
258 			break;
259 		case LLDP_MGMT_IFACE_IFINDEX:
260 			printf(" ifIndex");
261 			break;
262 		case LLDP_MGMT_IFACE_SYSPORT:
263 			printf(" sysPort");
264 			break;
265 		default:
266 			printf(" iface-type-%u", *ptr);
267 			break;
268 		}
269 		printf(" %u", ifidx);
270 	}
271 
272 	ptr += 5;
273 	len -= 5;
274 
275 	if (len < 1) {
276 		printf(" unexpected oid len %u", len);
277 		return;
278 	}
279 	alen = ptr[0];
280 	ptr++;
281 	len--;
282 
283 	if (alen != len) {
284 		printf(" unexpected oid len %u/%u", alen, len);
285 	}
286 	if (alen == 0)
287 		return;
288 
289 	printf(" oid 0x");
290 	do {
291 		printf("%02X", *ptr++);
292 	} while (--alen > 0);
293 }
294 
295 void
lldp_print(const u_char * p,u_int len)296 lldp_print(const u_char *p, u_int len)
297 {
298 	u_int16_t tlv;
299 	const u_int8_t *ptr = (u_int8_t *)p;
300 	int n, type, vlen;
301 
302 	printf("LLDP");
303 
304 	for (n = 0; n < len;) {
305 		TCHECK2(*ptr, sizeof(tlv));
306 
307 		tlv = EXTRACT_16BITS(ptr);
308 		type = (tlv & 0xfe00) >> 9;
309 		vlen = tlv & 0x1ff;
310 		n += vlen;
311 
312 		ptr += sizeof(tlv);
313 		TCHECK2(*ptr, vlen);
314 
315 		switch (type) {
316 		case LLDP_TLV_END:
317 			goto done;
318 			break;
319 
320 		case LLDP_TLV_CHASSIS_ID:
321 			printf(", ChassisId: ");
322 			lldp_print_id(type, ptr, vlen);
323 			break;
324 
325 		case LLDP_TLV_PORT_ID:
326 			printf(", PortId: ");
327 			lldp_print_id(type, ptr, vlen);
328 			break;
329 
330 		case LLDP_TLV_TTL:
331 			printf(", TTL: ");
332 			if (vlen != 2) {
333 				printf(" unexpected len %d", vlen);
334 				break;
335 			}
336 			printf("%ds", EXTRACT_16BITS(ptr));
337 			break;
338 
339 		case LLDP_TLV_PORT_DESCR:
340 			printf(", PortDescr: ");
341 			lldp_print_str(ptr, vlen);
342 			break;
343 
344 		case LLDP_TLV_SYSTEM_NAME:
345 			printf(", SysName: ");
346 			lldp_print_str(ptr, vlen);
347 			break;
348 
349 		case LLDP_TLV_SYSTEM_DESCR:
350 			printf(", SysDescr: ");
351 			lldp_print_str(ptr, vlen);
352 			break;
353 
354 		case LLDP_TLV_SYSTEM_CAP:
355 			printf(", CAP:");
356 			if (vlen != 4) {
357 				printf(" unexpected len %d", vlen);
358 				break;
359 			}
360 			printb(" available", EXTRACT_16BITS(ptr),
361 			    LLDP_CAP_BITS);
362 			printb(" enabled", EXTRACT_16BITS(ptr + 2),
363 			    LLDP_CAP_BITS);
364 			break;
365 
366 		case LLDP_TLV_MANAGEMENT_ADDR:
367 			printf(", MgmtAddr:");
368 			lldp_print_mgmt_addr(ptr, vlen);
369 			break;
370 
371 		case LLDP_TLV_ORG:
372 			printf(", Org:");
373 			if (vlen < 4) {
374 				printf(" unexpected len %d", vlen);
375 			}
376 			printf(" %02X-%02X-%02X type %02x len %u",
377 			    ptr[0], ptr[1], ptr[2], ptr[3], len - 4);
378 			break;
379 
380 		default:
381 			printf(", type %d length %d", type, vlen);
382 			break;
383 		}
384 		ptr += vlen;
385 	}
386 
387  done:
388 	return;
389 
390  trunc:
391 	printf(" [|LLDP]");
392 }
393 
394