xref: /openbsd/usr.sbin/tcpdump/print-lldp.c (revision e5dd7070)
1 /*	$OpenBSD: print-lldp.c,v 1.9 2016/11/28 17:47:15 jca 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(u_int8_t *, int);
91 const char	*lldp_print_addr(int, const void *);
92 void		 lldp_print_id(int, u_int8_t *, int);
93 
94 void
95 lldp_print_str(u_int8_t *str, int len)
96 {
97 	int i;
98 	printf("\"");
99 	for (i = 0; i < len; i++)
100 		printf("%c", isprint(str[i]) ? str[i] : '.');
101 	printf("\"");
102 }
103 
104 const char *
105 lldp_print_addr(int af, const void *addr)
106 {
107 	static char buf[48];
108 	if (inet_ntop(af, addr, buf, sizeof(buf)) == NULL)
109 		return ("?");
110 	return (buf);
111 }
112 
113 void
114 lldp_print_id(int type, u_int8_t *ptr, int len)
115 {
116 	u_int8_t id;
117 	u_int8_t *data;
118 
119 	id = *(u_int8_t *)ptr;
120 	len -= sizeof(u_int8_t);
121 	data = ptr + sizeof(u_int8_t);
122 	if (len <= 0)
123 		return;
124 
125 	if (type == LLDP_TLV_CHASSIS_ID) {
126 		switch (id) {
127 		case LLDP_CHASSISID_SUBTYPE_CHASSIS:
128 			printf("chassis ");
129 			lldp_print_str(data, len);
130 			break;
131 		case LLDP_CHASSISID_SUBTYPE_IFALIAS:
132 			printf("ifalias");
133 			break;
134 		case LLDP_CHASSISID_SUBTYPE_PORT:
135 			printf("port");
136 			break;
137 		case LLDP_CHASSISID_SUBTYPE_LLADDR:
138 			printf("lladdr %s", etheraddr_string(data));
139 			break;
140 		case LLDP_CHASSISID_SUBTYPE_ADDR:
141 			printf("addr");
142 			break;
143 		case LLDP_CHASSISID_SUBTYPE_IFNAME:
144 			printf("ifname ");
145 			lldp_print_str(data, len);
146 			break;
147 		case LLDP_CHASSISID_SUBTYPE_LOCAL:
148 			printf("local ");
149 			lldp_print_str(data, len);
150 			break;
151 		default:
152 			printf("unknown 0x%02x", id);
153 			break;
154 		}
155 
156 	} else if (type == LLDP_TLV_PORT_ID) {
157 		switch (id) {
158 		case LLDP_PORTID_SUBTYPE_IFALIAS:
159 			printf("ifalias");
160 			break;
161 		case LLDP_PORTID_SUBTYPE_PORT:
162 			printf("port");
163 			break;
164 		case LLDP_PORTID_SUBTYPE_LLADDR:
165 			printf("lladdr %s", etheraddr_string(data));
166 			break;
167 		case LLDP_PORTID_SUBTYPE_ADDR:
168 			printf("addr");
169 			break;
170 		case LLDP_PORTID_SUBTYPE_IFNAME:
171 			printf("ifname ");
172 			lldp_print_str(data, len);
173 			break;
174 		case LLDP_PORTID_SUBTYPE_AGENTCID:
175 			printf("agentcid");
176 			break;
177 		case LLDP_PORTID_SUBTYPE_LOCAL:
178 			printf("local ");
179 			lldp_print_str(data, len);
180 			break;
181 		default:
182 			printf("unknown 0x%02x", id);
183 			break;
184 		}
185 	}
186 }
187 
188 void
189 lldp_print(const u_char *p, u_int len)
190 {
191 	u_int16_t tlv;
192 	u_int8_t *ptr = (u_int8_t *)p, v = 0;
193 	int n, type, vlen, alen;
194 
195 	printf("LLDP");
196 
197 #define _ptrinc(_v)	ptr += (_v); vlen -= (_v);
198 
199 	for (n = 0; n < len;) {
200 		TCHECK2(*ptr, sizeof(tlv));
201 
202 		tlv = EXTRACT_16BITS(ptr);
203 		type = (tlv & 0xfe00) >> 9;
204 		vlen = tlv & 0x1ff;
205 		n += vlen;
206 
207 		ptr += sizeof(tlv);
208 		TCHECK2(*ptr, vlen);
209 
210 		switch (type) {
211 		case LLDP_TLV_END:
212 			goto done;
213 			break;
214 
215 		case LLDP_TLV_CHASSIS_ID:
216 			printf(", ChassisId: ");
217 			lldp_print_id(type, ptr, vlen);
218 			break;
219 
220 		case LLDP_TLV_PORT_ID:
221 			printf(", PortId: ");
222 			lldp_print_id(type, ptr, vlen);
223 			break;
224 
225 		case LLDP_TLV_TTL:
226 			printf(", TTL: ");
227 			TCHECK2(*ptr, 2);
228 			printf("%ds", EXTRACT_16BITS(ptr));
229 			break;
230 
231 		case LLDP_TLV_PORT_DESCR:
232 			printf(", PortDescr: ");
233 			lldp_print_str(ptr, vlen);
234 			break;
235 
236 		case LLDP_TLV_SYSTEM_NAME:
237 			printf(", SysName: ");
238 			lldp_print_str(ptr, vlen);
239 			break;
240 
241 		case LLDP_TLV_SYSTEM_DESCR:
242 			printf(", SysDescr: ");
243 			lldp_print_str(ptr, vlen);
244 			break;
245 
246 		case LLDP_TLV_SYSTEM_CAP:
247 			printf(", CAP:");
248 			TCHECK2(*ptr, 4);
249 			printb(" available", EXTRACT_16BITS(ptr),
250 			    LLDP_CAP_BITS);
251 			_ptrinc(sizeof(u_int16_t));
252 			printb(" enabled", EXTRACT_16BITS(ptr),
253 			    LLDP_CAP_BITS);
254 			break;
255 
256 		case LLDP_TLV_MANAGEMENT_ADDR:
257 			printf(", MgmtAddr:");
258 			TCHECK2(*ptr, 2);
259 			alen = *ptr - sizeof(u_int8_t);
260 			_ptrinc(sizeof(u_int8_t));
261 			v = *ptr;
262 			_ptrinc(sizeof(u_int8_t));
263 			if (v < AFNUM_MAX)
264 				printf(" %s", afnumber[v]);
265 			else
266 				printf(" type %d", v);
267 			TCHECK2(*ptr, alen);
268 			switch (v) {
269 			case AFNUM_INET:
270 				if (alen != sizeof(struct in_addr))
271 					goto trunc;
272 				printf(" %s",
273 				    lldp_print_addr(AF_INET, ptr));
274 				break;
275 			case AFNUM_INET6:
276 				if (alen != sizeof(struct in6_addr))
277 					goto trunc;
278 				printf(" %s",
279 				    lldp_print_addr(AF_INET6, ptr));
280 				break;
281 			}
282 			_ptrinc(alen);
283 			v = *(u_int8_t *)ptr;
284 			break;
285 
286 		case LLDP_TLV_ORG:
287 			printf(", Org");
288 			break;
289 
290 		default:
291 			printf(", type %d length %d", type, vlen);
292 			break;
293 		}
294 		ptr += vlen;
295 	}
296 
297  done:
298 	return;
299 
300  trunc:
301 	printf(" [|LLDP]");
302 }
303 
304