xref: /openbsd/usr.sbin/tcpdump/print-lldp.c (revision 404b540a)
1 /*	$OpenBSD: print-lldp.c,v 1.5 2007/10/26 18:18:13 moritz 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/param.h>
20 #include <sys/time.h>
21 #include <sys/socket.h>
22 
23 #include <net/if.h>
24 
25 #include <netinet/in.h>
26 #include <netinet/in_systm.h>
27 #include <netinet/if_ether.h>
28 #include <arpa/inet.h>
29 
30 #include <ctype.h>
31 #include <stdio.h>
32 #include <string.h>
33 
34 #include "addrtoname.h"
35 #include "extract.h"
36 #include "interface.h"
37 #include "afnum.h"
38 
39 enum {
40 	LLDP_TLV_END			= 0,
41 	LLDP_TLV_CHASSIS_ID		= 1,
42 	LLDP_TLV_PORT_ID		= 2,
43 	LLDP_TLV_TTL			= 3,
44 	LLDP_TLV_PORT_DESCR		= 4,
45 	LLDP_TLV_SYSTEM_NAME		= 5,
46 	LLDP_TLV_SYSTEM_DESCR		= 6,
47 	LLDP_TLV_SYSTEM_CAP		= 7,
48 	LLDP_TLV_MANAGEMENT_ADDR	= 8,
49 	LLDP_TLV_ORG			= 127
50 };
51 
52 enum {
53 	LLDP_CHASSISID_SUBTYPE_CHASSIS	= 1,
54 	LLDP_CHASSISID_SUBTYPE_IFALIAS	= 2,
55 	LLDP_CHASSISID_SUBTYPE_PORT	= 3,
56 	LLDP_CHASSISID_SUBTYPE_LLADDR	= 4,
57 	LLDP_CHASSISID_SUBTYPE_ADDR	= 5,
58 	LLDP_CHASSISID_SUBTYPE_IFNAME	= 6,
59 	LLDP_CHASSISID_SUBTYPE_LOCAL	= 7
60 };
61 
62 enum {
63 	LLDP_PORTID_SUBTYPE_IFALIAS	= 1,
64 	LLDP_PORTID_SUBTYPE_PORT	= 2,
65 	LLDP_PORTID_SUBTYPE_LLADDR	= 3,
66 	LLDP_PORTID_SUBTYPE_ADDR	= 4,
67 	LLDP_PORTID_SUBTYPE_IFNAME	= 5,
68 	LLDP_PORTID_SUBTYPE_AGENTCID	= 6,
69 	LLDP_PORTID_SUBTYPE_LOCAL	= 7
70 };
71 
72 #define LLDP_CAP_OTHER          0x01
73 #define LLDP_CAP_REPEATER       0x02
74 #define LLDP_CAP_BRIDGE         0x04
75 #define LLDP_CAP_WLAN           0x08
76 #define LLDP_CAP_ROUTER         0x10
77 #define LLDP_CAP_TELEPHONE      0x20
78 #define LLDP_CAP_DOCSIS         0x40
79 #define LLDP_CAP_STATION        0x80
80 #define LLDP_CAP_BITS								\
81 	"\20\01OTHER\02REPEATER\03BRIDGE\04WLAN\05ROUTER\06TELEPHONE"		\
82 	"\07DOCSIS\10STATION"
83 
84 enum {
85 	LLDP_MGMT_IFACE_UNKNOWN	= 1,
86 	LLDP_MGMT_IFACE_IFINDEX	= 2,
87 	LLDP_MGMT_IFACE_SYSPORT	= 3
88 };
89 
90 static const char *afnumber[] = AFNUM_NAME_STR;
91 
92 void		 lldp_print_str(u_int8_t *, int);
93 const char	*lldp_print_addr(int, const void *);
94 void		 lldp_print_id(int, u_int8_t *, int);
95 
96 void
97 lldp_print_str(u_int8_t *str, int len)
98 {
99 	int i;
100 	printf("\"");
101 	for (i = 0; i < len; i++)
102 		printf("%c", isprint(str[i]) ? str[i] : '.');
103 	printf("\"");
104 }
105 
106 const char *
107 lldp_print_addr(int af, const void *addr)
108 {
109 	static char buf[48];
110 	if (inet_ntop(af, addr, buf, sizeof(buf)) == NULL)
111 		return ("?");
112 	return (buf);
113 }
114 
115 void
116 lldp_print_id(int type, u_int8_t *ptr, int len)
117 {
118 	u_int8_t id;
119 	u_int8_t *data;
120 
121 	id = *(u_int8_t *)ptr;
122 	len -= sizeof(u_int8_t);
123 	data = ptr + sizeof(u_int8_t);
124 	if (len <= 0)
125 		return;
126 
127 	if (type == LLDP_TLV_CHASSIS_ID) {
128 		switch (id) {
129 		case LLDP_CHASSISID_SUBTYPE_CHASSIS:
130 			printf("chassis ");
131 			lldp_print_str(data, len);
132 			break;
133 		case LLDP_CHASSISID_SUBTYPE_IFALIAS:
134 			printf("ifalias");
135 			break;
136 		case LLDP_CHASSISID_SUBTYPE_PORT:
137 			printf("port");
138 			break;
139 		case LLDP_CHASSISID_SUBTYPE_LLADDR:
140 			printf("lladdr %s",
141 			    ether_ntoa((struct ether_addr *)data));
142 			break;
143 		case LLDP_CHASSISID_SUBTYPE_ADDR:
144 			printf("addr");
145 			break;
146 		case LLDP_CHASSISID_SUBTYPE_IFNAME:
147 			printf("ifname ");
148 			lldp_print_str(data, len);
149 			break;
150 		case LLDP_CHASSISID_SUBTYPE_LOCAL:
151 			printf("local ");
152 			lldp_print_str(data, len);
153 			break;
154 		default:
155 			printf("unknown 0x%02x", id);
156 			break;
157 		}
158 
159 	} else if (type == LLDP_TLV_PORT_ID) {
160 		switch (id) {
161 		case LLDP_PORTID_SUBTYPE_IFALIAS:
162 			printf("ifalias");
163 			break;
164 		case LLDP_PORTID_SUBTYPE_PORT:
165 			printf("port");
166 			break;
167 		case LLDP_PORTID_SUBTYPE_LLADDR:
168 			printf("lladdr %s",
169 			    ether_ntoa((struct ether_addr *)data));
170 			break;
171 		case LLDP_PORTID_SUBTYPE_ADDR:
172 			printf("addr");
173 			break;
174 		case LLDP_PORTID_SUBTYPE_IFNAME:
175 			printf("ifname ");
176 			lldp_print_str(data, len);
177 			break;
178 		case LLDP_PORTID_SUBTYPE_AGENTCID:
179 			printf("agentcid");
180 			break;
181 		case LLDP_PORTID_SUBTYPE_LOCAL:
182 			printf("local ");
183 			lldp_print_str(data, len);
184 			break;
185 		default:
186 			printf("unknown 0x%02x", id);
187 			break;
188 		}
189 	}
190 }
191 
192 void
193 lldp_print(const u_char *p, u_int len)
194 {
195 	u_int16_t tlv;
196 	u_int8_t *ptr = (u_int8_t *)p, v = 0;
197 	int n, type, vlen, alen;
198 
199 	printf("LLDP");
200 
201 #define _ptrinc(_v)	ptr += (_v); vlen -= (_v);
202 
203 	for (n = 0; n < len;) {
204 		TCHECK2(*ptr, sizeof(tlv));
205 
206 		tlv = EXTRACT_16BITS(ptr);
207 		type = (tlv & 0xfe00) >> 9;
208 		vlen = tlv & 0x1ff;
209 		n += vlen;
210 
211 		ptr += sizeof(tlv);
212 		TCHECK2(*ptr, vlen);
213 
214 		switch (type) {
215 		case LLDP_TLV_END:
216 			goto done;
217 			break;
218 
219 		case LLDP_TLV_CHASSIS_ID:
220 			printf(", ChassisId: ");
221 			lldp_print_id(type, ptr, vlen);
222 			break;
223 
224 		case LLDP_TLV_PORT_ID:
225 			printf(", PortId: ");
226 			lldp_print_id(type, ptr, vlen);
227 			break;
228 
229 		case LLDP_TLV_TTL:
230 			printf(", TTL: ");
231 			TCHECK2(*ptr, 2);
232 			printf("%ds", EXTRACT_16BITS(ptr));
233 			break;
234 
235 		case LLDP_TLV_PORT_DESCR:
236 			printf(", PortDescr: ");
237 			lldp_print_str(ptr, vlen);
238 			break;
239 
240 		case LLDP_TLV_SYSTEM_NAME:
241 			printf(", SysName: ");
242 			lldp_print_str(ptr, vlen);
243 			break;
244 
245 		case LLDP_TLV_SYSTEM_DESCR:
246 			printf(", SysDescr: ");
247 			lldp_print_str(ptr, vlen);
248 			break;
249 
250 		case LLDP_TLV_SYSTEM_CAP:
251 			printf(", CAP:");
252 			TCHECK2(*ptr, 4);
253 			printb(" available", EXTRACT_16BITS(ptr),
254 			    LLDP_CAP_BITS);
255 			_ptrinc(sizeof(u_int16_t));
256 			printb(" enabled", EXTRACT_16BITS(ptr),
257 			    LLDP_CAP_BITS);
258 			break;
259 
260 		case LLDP_TLV_MANAGEMENT_ADDR:
261 			printf(", MgmtAddr:");
262 			TCHECK2(*ptr, 2);
263 			alen = *ptr - sizeof(u_int8_t);
264 			_ptrinc(sizeof(u_int8_t));
265 			v = *ptr;
266 			_ptrinc(sizeof(u_int8_t));
267 			if (v <= AFNUM_MAX)
268 				printf(" %s", afnumber[v]);
269 			else
270 				printf(" type %d", v);
271 			TCHECK2(*ptr, alen);
272 			switch (v) {
273 			case AFNUM_INET:
274 				if (alen != sizeof(struct in_addr))
275 					goto trunc;
276 				printf(" %s",
277 				    lldp_print_addr(AF_INET, ptr));
278 				break;
279 			case AFNUM_INET6:
280 				if (alen != sizeof(struct in6_addr))
281 					goto trunc;
282 				printf(" %s",
283 				    lldp_print_addr(AF_INET6, ptr));
284 				break;
285 			}
286 			_ptrinc(alen);
287 			v = *(u_int8_t *)ptr;
288 			break;
289 
290 		case LLDP_TLV_ORG:
291 			printf(", Org");
292 			break;
293 
294 		default:
295 			printf(", type %d length %d", type, vlen);
296 			break;
297 		}
298 		ptr += vlen;
299 	}
300 
301  done:
302 	return;
303 
304  trunc:
305 	printf(" [|LLDP]");
306 }
307 
308