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