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