]> git.ipfire.org Git - thirdparty/lldpd.git/blob - src/display.c
14b34f715f6fd5a35b824fbf864340a308e08e1e
[thirdparty/lldpd.git] / src / display.c
1 /*
2 * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include "lldpctl.h"
18
19 #include <stdio.h>
20 #include <unistd.h>
21 #include <time.h>
22 #include <errno.h>
23 #include <sys/types.h>
24 #include <sys/socket.h>
25 #include <sys/un.h>
26 #include <arpa/inet.h>
27
28 TAILQ_HEAD(interfaces, lldpd_interface);
29
30 #define ntohll(x) (((u_int64_t)(ntohl((int)((x << 32) >> 32))) << 32) | \
31 (unsigned int)ntohl(((int)(x >> 32))))
32 #define htonll(x) ntohll(x)
33
34 struct value_string {
35 int value;
36 char *string;
37 };
38
39 static const struct value_string lldpd_protocol_map[] = {
40 { LLDPD_MODE_LLDP, "LLDP" },
41 { LLDPD_MODE_CDPV1, "CDPv1"},
42 { LLDPD_MODE_CDPV2, "CDPv2"},
43 { LLDPD_MODE_EDP, "EDP" },
44 { LLDPD_MODE_FDP, "FDP"},
45 { LLDPD_MODE_SONMP, "SONMP"},
46 { 0, NULL }
47 };
48
49 static const struct value_string chassis_id_subtype_map[] = {
50 { LLDP_CHASSISID_SUBTYPE_IFNAME, "ifname"},
51 { LLDP_CHASSISID_SUBTYPE_IFALIAS, "ifalias" },
52 { LLDP_CHASSISID_SUBTYPE_LOCAL, "local" },
53 { LLDP_CHASSISID_SUBTYPE_LLADDR, "mac" },
54 { LLDP_CHASSISID_SUBTYPE_ADDR, "ip" },
55 { LLDP_CHASSISID_SUBTYPE_PORT, "unhandled" },
56 { LLDP_CHASSISID_SUBTYPE_CHASSIS, "unhandled" },
57 { 0, NULL},
58 };
59
60 static const struct value_string port_id_subtype_map[] = {
61 { LLDP_PORTID_SUBTYPE_IFNAME, "ifname"},
62 { LLDP_PORTID_SUBTYPE_IFALIAS, "ifalias" },
63 { LLDP_PORTID_SUBTYPE_LOCAL, "local" },
64 { LLDP_PORTID_SUBTYPE_LLADDR, "mac" },
65 { LLDP_PORTID_SUBTYPE_ADDR, "ip" },
66 { LLDP_PORTID_SUBTYPE_PORT, "unhandled" },
67 { LLDP_PORTID_SUBTYPE_AGENTCID, "unhandled" },
68 { 0, NULL},
69 };
70
71 #ifdef ENABLE_LLDPMED
72 static const struct value_string chassis_med_type_map[] = {
73 { LLDPMED_CLASS_I, "Generic Endpoint (Class I)" },
74 { LLDPMED_CLASS_II, "Media Endpoint (Class II)" },
75 { LLDPMED_CLASS_III, "Communication Device Endpoint (Class III)" },
76 { LLDPMED_NETWORK_DEVICE, "Network Connectivity Device" },
77 { 0, NULL },
78 };
79
80 static const struct value_string lldpmed_capabilit_map[] = {
81 {LLDPMED_CAP_CAP, "Capabilities"},
82 {LLDPMED_CAP_POLICY, "Policy"},
83 {LLDPMED_CAP_LOCATION, "Location"},
84 {LLDPMED_CAP_MDI_PSE, "MDI/PSE"},
85 {LLDPMED_CAP_MDI_PD, "MDI/PD"},
86 {LLDPMED_CAP_IV, "Inventory"},
87 { 0, NULL },
88 };
89
90 static const struct value_string port_med_policy_map[] = {
91 { LLDPMED_APPTYPE_VOICE , "Voice"},
92 { LLDPMED_APPTYPE_VOICESIGNAL, "Voice Signaling"},
93 { LLDPMED_APPTYPE_GUESTVOICE, "Guest Voice"},
94 { LLDPMED_APPTYPE_GUESTVOICESIGNAL, "Guest Voice Signaling"},
95 { LLDPMED_APPTYPE_SOFTPHONEVOICE, "Softphone Voice"},
96 { LLDPMED_APPTYPE_VIDEOCONFERENCE, "Video Conferencing"},
97 { LLDPMED_APPTYPE_VIDEOSTREAM, "Streaming Video"},
98 { LLDPMED_APPTYPE_VIDEOSIGNAL, "Video Signaling"},
99 { 0, NULL },
100 };
101
102 static const struct value_string civic_address_type_values[] = {
103 { 0, "Language" },
104 { 1, "National subdivisions" },
105 { 2, "County, parish, district" },
106 { 3, "City, township" },
107 { 4, "City division, borough, ward" },
108 { 5, "Neighborhood, block" },
109 { 6, "Street" },
110 { 16, "Leading street direction" },
111 { 17, "Trailing street suffix" },
112 { 18, "Street suffix" },
113 { 19, "House number" },
114 { 20, "House number suffix" },
115 { 21, "Landmark or vanity address" },
116 { 22, "Additional location info" },
117 { 23, "Name" },
118 { 24, "Postal/ZIP code" },
119 { 25, "Building" },
120 { 26, "Unit" },
121 { 27, "Floor" },
122 { 28, "Room number" },
123 { 29, "Place type" },
124 { 128, "Script" },
125 { 0, NULL }
126 };
127
128 static const struct value_string civic_address_type_tags[] = {
129 { 0, "language" },
130 { 1, "country-subdivision" },
131 { 2, "county" },
132 { 3, "city" },
133 { 4, "city-division" },
134 { 5, "block" },
135 { 6, "street" },
136 { 16, "direction" },
137 { 17, "street-suffix" },
138 { 18, "street-suffix" },
139 { 19, "number" },
140 { 20, "number-suffix" },
141 { 21, "landmark" },
142 { 22, "additional" },
143 { 23, "name" },
144 { 24, "zip" },
145 { 25, "building" },
146 { 26, "unit" },
147 { 27, "floor" },
148 { 28, "room" },
149 { 29, "place-type" },
150 { 128, "Script" },
151 { 0, NULL }
152 };
153
154 static const struct value_string port_med_geoid_map[] = {
155 { 1, "WGS84" },
156 { 2, "NAD83" },
157 { 3, "NAD83/MLLW" },
158 { 0, NULL },
159 };
160
161 static const struct value_string port_med_pow_devicetype_map[] = {
162 { LLDPMED_POW_TYPE_PSE, "PSE Device" },
163 { LLDPMED_POW_TYPE_PD, "PD Device" },
164 { 0, NULL },
165 };
166
167 static const struct value_string port_med_pow_source_map[] = {
168 { LLDPMED_POW_SOURCE_PRIMARY, "Primary Power Source" },
169 { LLDPMED_POW_SOURCE_BACKUP, "Backup Power Source / Power Conservation Mode" },
170 { LLDPMED_POW_SOURCE_PSE, "PSE" },
171 { LLDPMED_POW_SOURCE_LOCAL, "Local"},
172 { LLDPMED_POW_SOURCE_BOTH, "PSE + Local"},
173 { 0, NULL },
174 };
175
176 static const struct value_string port_med_pow_priority_map[] = {
177 { LLDPMED_POW_PRIO_CRITICAL, "critical" },
178 { LLDPMED_POW_PRIO_HIGH, "high" },
179 { LLDPMED_POW_PRIO_LOW, "low" },
180 { 0, NULL },
181 };
182 #endif
183
184 #ifdef ENABLE_DOT3
185 static const struct value_string operational_mau_type_values[] = {
186 { 1, "AUI - no internal MAU, view from AUI" },
187 { 2, "10Base5 - thick coax MAU" },
188 { 3, "Foirl - FOIRL MAU" },
189 { 4, "10Base2 - thin coax MAU" },
190 { 5, "10BaseT - UTP MAU" },
191 { 6, "10BaseFP - passive fiber MAU" },
192 { 7, "10BaseFB - sync fiber MAU" },
193 { 8, "10BaseFL - async fiber MAU" },
194 { 9, "10Broad36 - broadband DTE MAU" },
195 { 10, "10BaseTHD - UTP MAU, half duplex mode" },
196 { 11, "10BaseTFD - UTP MAU, full duplex mode" },
197 { 12, "10BaseFLHD - async fiber MAU, half duplex mode" },
198 { 13, "10BaseFLDF - async fiber MAU, full duplex mode" },
199 { 14, "10BaseT4 - 4 pair category 3 UTP" },
200 { 15, "100BaseTXHD - 2 pair category 5 UTP, half duplex mode" },
201 { 16, "100BaseTXFD - 2 pair category 5 UTP, full duplex mode" },
202 { 17, "100BaseFXHD - X fiber over PMT, half duplex mode" },
203 { 18, "100BaseFXFD - X fiber over PMT, full duplex mode" },
204 { 19, "100BaseT2HD - 2 pair category 3 UTP, half duplex mode" },
205 { 20, "100BaseT2DF - 2 pair category 3 UTP, full duplex mode" },
206 { 21, "1000BaseXHD - PCS/PMA, unknown PMD, half duplex mode" },
207 { 22, "1000BaseXFD - PCS/PMA, unknown PMD, full duplex mode" },
208 { 23, "1000BaseLXHD - Fiber over long-wavelength laser, half duplex mode" },
209 { 24, "1000BaseLXFD - Fiber over long-wavelength laser, full duplex mode" },
210 { 25, "1000BaseSXHD - Fiber over short-wavelength laser, half duplex mode" },
211 { 26, "1000BaseSXFD - Fiber over short-wavelength laser, full duplex mode" },
212 { 27, "1000BaseCXHD - Copper over 150-Ohm balanced cable, half duplex mode" },
213 { 28, "1000BaseCXFD - Copper over 150-Ohm balanced cable, full duplex mode" },
214 { 29, "1000BaseTHD - Four-pair Category 5 UTP, half duplex mode" },
215 { 30, "1000BaseTFD - Four-pair Category 5 UTP, full duplex mode" },
216 { 31, "10GigBaseX - X PCS/PMA, unknown PMD." },
217 { 32, "10GigBaseLX4 - X fiber over WWDM optics" },
218 { 33, "10GigBaseR - R PCS/PMA, unknown PMD." },
219 { 34, "10GigBaseER - R fiber over 1550 nm optics" },
220 { 35, "10GigBaseLR - R fiber over 1310 nm optics" },
221 { 36, "10GigBaseSR - R fiber over 850 nm optics" },
222 { 37, "10GigBaseW - W PCS/PMA, unknown PMD." },
223 { 38, "10GigBaseEW - W fiber over 1550 nm optics" },
224 { 39, "10GigBaseLW - W fiber over 1310 nm optics" },
225 { 40, "10GigBaseSW - W fiber over 850 nm optics" },
226 { 41, "10GigBaseCX4 - X copper over 8 pair 100-Ohm balanced cable" },
227 { 42, "2BaseTL - Voice grade UTP copper, up to 2700m, optional PAF" },
228 { 43, "10PassTS - Voice grade UTP copper, up to 750m, optional PAF" },
229 { 44, "100BaseBX10D - One single-mode fiber OLT, long wavelength, 10km" },
230 { 45, "100BaseBX10U - One single-mode fiber ONU, long wavelength, 10km" },
231 { 46, "100BaseLX10 - Two single-mode fibers, long wavelength, 10km" },
232 { 47, "1000BaseBX10D - One single-mode fiber OLT, long wavelength, 10km" },
233 { 48, "1000BaseBX10U - One single-mode fiber ONU, long wavelength, 10km" },
234 { 49, "1000BaseLX10 - Two sigle-mode fiber, long wavelength, 10km" },
235 { 50, "1000BasePX10D - One single-mode fiber EPON OLT, 10km" },
236 { 51, "1000BasePX10U - One single-mode fiber EPON ONU, 10km" },
237 { 52, "1000BasePX20D - One single-mode fiber EPON OLT, 20km" },
238 { 53, "1000BasePX20U - One single-mode fiber EPON ONU, 20km" },
239 { 0, NULL }
240 };
241
242 static const struct value_string port_dot3_power_devicetype_map[] = {
243 { LLDP_DOT3_POWER_PSE, "PSE" },
244 { LLDP_DOT3_POWER_PD, "PD" },
245 { 0, NULL }
246 };
247
248 static const struct value_string port_dot3_power_pairs_map[] = {
249 { LLDP_DOT3_POWERPAIRS_SIGNAL, "signal" },
250 { LLDP_DOT3_POWERPAIRS_SPARE, "spare" },
251 { 0, NULL }
252 };
253
254 static const struct value_string port_dot3_power_class_map[] = {
255 { 1, "class 0" },
256 { 2, "class 1" },
257 { 3, "class 2" },
258 { 4, "class 3" },
259 { 5, "class 4" },
260 { 0, NULL }
261 };
262
263 static const struct value_string port_dot3_power_pse_source_map[] = {
264 { LLDP_DOT3_POWER_SOURCE_BOTH, "PSE + Local" },
265 { LLDP_DOT3_POWER_SOURCE_PSE, "PSE" },
266 { 0, NULL }
267 };
268
269 static const struct value_string port_dot3_power_pd_source_map[] = {
270 { LLDP_DOT3_POWER_SOURCE_BACKUP, "Backup source" },
271 { LLDP_DOT3_POWER_SOURCE_PRIMARY, "Primary power source" },
272 { 0, NULL }
273 };
274
275 static const struct value_string port_dot3_power_priority_map[] = {
276 { LLDPMED_POW_PRIO_CRITICAL, "critical" },
277 { LLDPMED_POW_PRIO_HIGH, "high" },
278 { LLDPMED_POW_PRIO_LOW, "low" },
279 { 0, NULL },
280 };
281 #endif
282
283 static const struct value_string chassis_capability_map[] = {
284 { LLDP_CAP_OTHER, "Other" },
285 { LLDP_CAP_REPEATER, "Repeater"},
286 { LLDP_CAP_BRIDGE, "Bridge"},
287 { LLDP_CAP_ROUTER, "Router"},
288 { LLDP_CAP_WLAN, "Wlan"},
289 { LLDP_CAP_TELEPHONE,"Telephone"},
290 { LLDP_CAP_DOCSIS, "Docsis"},
291 { LLDP_CAP_STATION, "Station"},
292 { 0, NULL},
293 };
294
295
296 static const char*
297 map_lookup(const struct value_string * list, int n)
298 {
299
300 unsigned int i;
301
302 for( i = 0; list[i].string != NULL; i ++ ) {
303 if( list[i].value == n ) {
304 return list[i].string;
305 }
306 }
307
308 return "unknown";
309 }
310
311 static char*
312 u2str(unsigned n)
313 {
314 static char buf[21];
315 snprintf(buf, sizeof(buf), "%u", n);
316 return buf;
317 }
318
319 static char*
320 dump(void *data, int size, int max, char sep)
321 {
322 int i;
323 size_t len;
324 static char *buffer = NULL;
325 static char truncation[] = "[...]";
326
327 free(buffer);
328 if (size > max)
329 len = max * 3 + sizeof(truncation) + 1;
330 else
331 len = size * 3;
332
333 if ((buffer = (char *)malloc(len)) == NULL)
334 fatal(NULL);
335
336 for (i = 0; (i < size) && (i < max); i++)
337 sprintf(buffer + i * 3, "%02x%c", *(u_int8_t*)(data + i), sep);
338 if (size > max)
339 sprintf(buffer + i * 3, "%s", truncation);
340 else
341 *(buffer + i*3 - 1) = 0;
342 return buffer;
343 }
344
345 static void
346 display_cap(struct writer * w, struct lldpd_chassis *chassis, u_int8_t bit, char *symbol)
347 {
348 if (chassis->c_cap_available & bit) {
349 tag_start(w, "capability", "Capability");
350 tag_attr (w, "type", "", symbol );
351 tag_attr (w, "enabled", "", (chassis->c_cap_enabled & bit)?"on":"off");
352 tag_end (w);
353 }
354 }
355
356 #ifdef ENABLE_LLDPMED
357 static int
358 display_fixed_precision(u_int64_t value, int intpart, int floatpart, int displaysign, char ** res)
359 {
360 static char buf[64];
361 u_int64_t tmp = value;
362 int negative = 0;
363 u_int32_t integer = 0;
364 if (value & (1ULL<<(intpart + floatpart - 1))) {
365 negative = 1;
366 tmp = ~value;
367 tmp += 1;
368 }
369 integer = (u_int32_t)((tmp &
370 (((1ULL << intpart)-1) << floatpart)) >> floatpart);
371 tmp = (tmp & ((1<< floatpart) - 1))*10000/(1ULL << floatpart);
372 snprintf(buf, sizeof(buf),"%s%u.%04llu", displaysign?(negative?"-":"+"):"",
373 integer, (unsigned long long int)tmp);
374
375 *res = buf;
376
377 return negative;
378 }
379
380 static void
381 display_latitude_or_longitude(struct writer *w, int option, u_int64_t value)
382 {
383 static char buf[70];
384 int negative;
385 char * str;
386
387 if ( option == 0 ) {
388 tag_start(w, "lat", "Latitude");
389 } else {
390 tag_start(w, "lon", "Longitude");
391 }
392 negative = display_fixed_precision(value, 9, 25, 0, &str);
393 if (option == 0)
394 snprintf(buf, sizeof(buf), "%s %s", str, negative?" S":" N");
395 else
396 snprintf(buf, sizeof(buf), "%s %s", str, negative?" W":" E");
397
398 tag_data(w, buf);
399 tag_end(w);
400 }
401
402 static void
403 display_med_capability(struct writer *w, struct lldpd_chassis *chassis, int cap)
404 {
405 if (chassis->c_med_cap_available & cap) {
406 tag_start(w, "capability", "Capability");
407 tag_attr(w, "type", "",
408 map_lookup(lldpmed_capabilit_map, cap));
409 tag_end(w);
410 }
411 }
412
413 static void
414 display_med(struct writer *w, struct lldpd_chassis *chassis, struct lldpd_port *port)
415 {
416 int i;
417 char *value;
418
419 tag_start(w, "lldp-med", "LLDP-MED");
420
421 tag_datatag(w, "device-type", "Device Type",
422 map_lookup(chassis_med_type_map, chassis->c_med_type));
423
424 display_med_capability(w, chassis, LLDPMED_CAP_CAP);
425 display_med_capability(w, chassis, LLDPMED_CAP_POLICY);
426 display_med_capability(w, chassis, LLDPMED_CAP_LOCATION);
427 display_med_capability(w, chassis, LLDPMED_CAP_MDI_PSE);
428 display_med_capability(w, chassis, LLDPMED_CAP_MDI_PD);
429 display_med_capability(w, chassis, LLDPMED_CAP_IV);
430
431 for (i = 0; i < LLDPMED_APPTYPE_LAST; i++) {
432 if (i+1 == port->p_med_policy[i].type) {
433 tag_start(w, "policy", "LLDP-MED Network Policy for");
434 tag_attr(w, "apptype", "AppType",
435 u2str(port->p_med_policy[i].type));
436 tag_attr(w, "defined", "Defined",
437 (port->p_med_policy[i].unknown)?"no":"yes");
438
439 tag_datatag(w, "descr", "",
440 map_lookup(port_med_policy_map, port->p_med_policy[i].type));
441
442 if (port->p_med_policy[i].tagged) {
443 tag_start(w, "vlan", "VLAN");
444 if (port->p_med_policy[i].vid == 0) {
445 tag_attr(w, "vid", "", "priority");
446 } else if (port->p_med_policy[i].vid == 4095) {
447 tag_attr(w, "vid", "", "reserved");
448 } else {
449 tag_attr(w, "vid", "",
450 u2str(port->p_med_policy[i].vid));
451 }
452 tag_end(w);
453 }
454
455 tag_datatag(w, "priority", "Layer 2 Priority",
456 u2str(port->p_med_policy[i].priority));
457
458 tag_datatag(w, "dscp", "DSCP Value",
459 u2str(port->p_med_policy[i].dscp));
460
461 tag_end(w);
462 }
463 }
464 for (i = 0; i < LLDPMED_LOCFORMAT_LAST; i++) {
465 if (i+1 == port->p_med_location[i].format) {
466 tag_start(w, "location", "LLDP-MED Location Identification");
467
468 switch(port->p_med_location[i].format) {
469 case LLDPMED_LOCFORMAT_COORD:
470 tag_attr(w, "type", "Type", "coordinates");
471
472 if (port->p_med_location[i].data_len != 16) {
473 tag_datatag(w, "error", "Error", "bad data length");
474 } else {
475 u_int64_t l;
476 u_int8_t v;
477 char * s;
478
479 v = *(u_int8_t*)(port->p_med_location[i].data + 15);
480 tag_attr(w, "geoid", "Geoid",
481 map_lookup(port_med_geoid_map,v));
482
483 /* Latitude and longitude */
484 memcpy(&l, port->p_med_location[i].data,
485 sizeof(u_int64_t));
486 l = (ntohll(l) &
487 0x03FFFFFFFF000000ULL) >> 24;
488 display_latitude_or_longitude(w,0, l);
489 memcpy(&l, port->p_med_location[i].data + 5,
490 sizeof(u_int64_t));
491 l = (ntohll(l) &
492 0x03FFFFFFFF000000ULL) >> 24;
493 display_latitude_or_longitude(w,1, l);
494
495 /* Altitude */
496 memcpy(&l, port->p_med_location[i].data + 10,
497 sizeof(u_int64_t));
498 l = (ntohll(l) &
499 0x3FFFFFFF000000ULL) >> 24;
500 display_fixed_precision(l, 22, 8, 1, &s);
501
502 tag_start(w, "altitude", "Altitude");
503 switch ((*(u_int8_t*)(port->p_med_location[i].data +
504 10)) & 0xf0) {
505 case (1 << 4):
506 tag_attr(w, "unit", "", "m");
507 break;
508 case (2 << 4):
509 tag_attr(w, "unit", "", "floor");
510 break;
511 default:
512 tag_attr(w, "unit", "", "unknown");
513 }
514 tag_data(w,s);
515 tag_end(w);
516
517 }
518 break;
519 case LLDPMED_LOCFORMAT_CIVIC:
520 tag_attr(w, "type", "Type", "address");
521
522 if ((port->p_med_location[i].data_len < 3) ||
523 (port->p_med_location[i].data_len - 1 !=
524 *(u_int8_t*)port->p_med_location[i].data)) {
525 tag_datatag(w, "error", "Error", "bad data length");
526 } else {
527 int l = 4, n, catype, calength;
528 char country[3];
529 country[0] = ((char *)port->p_med_location[i].data)[2];
530 country[1] = ((char *)port->p_med_location[i].data)[3];
531 country[2] = 0;
532
533 tag_datatag(w, "country", "Country", country);
534
535 while ((n = (port->
536 p_med_location[i].data_len - l)) >= 2) {
537 catype = *(u_int8_t*)(port->
538 p_med_location[i].data + l);
539 calength = *(u_int8_t*)(port->
540 p_med_location[i].data + l + 1);
541 if (n < 2 + calength) {
542 tag_datatag(w, "error", "Error", "bad data length");
543 break;
544 }
545
546 if ((value = strndup((char *)(port->
547 p_med_location[i].data + l + 2),
548 calength)) == NULL) {
549 fatalx("not enough memory");
550 break;
551 }
552 tag_datatag(w,
553 map_lookup(civic_address_type_tags,catype),
554 map_lookup(civic_address_type_values,catype),
555 value);
556 free(value);
557 l += 2 + calength;
558 }
559 }
560 break;
561 case LLDPMED_LOCFORMAT_ELIN:
562 if ((value = strndup((char *)(port->
563 p_med_location[i].data),
564 port->p_med_location[i].data_len)) == NULL) {
565 fatalx( "not enough memory");
566 break;
567 }
568 tag_attr(w, "type", "Type", "elin");
569 tag_datatag(w, "ecs", "ECS ELIN", value);
570 free(value);
571 break;
572 default:
573 tag_attr(w, "type", "", "unknown");
574 tag_datatag(w, "unknown", "Data",
575 dump(port->p_med_location[i].data,
576 port->p_med_location[i].data_len, 20, ' '));
577 }
578 tag_end(w);
579 }
580 }
581 if (port->p_med_power.devicetype) {
582 tag_start(w, "poe", "Extended Power-over-Ethernet");
583
584 tag_start(w, "device-type", "Power Type & Source");
585 tag_data(w, map_lookup(port_med_pow_devicetype_map, port->p_med_power.devicetype));
586 tag_end(w);
587
588 tag_start(w, "source", "Power Source");
589 tag_data(w, map_lookup(port_med_pow_source_map, port->p_med_power.source));
590 tag_end(w);
591
592 tag_start(w, "priority", "Power Priority");
593 tag_data(w, map_lookup(port_med_pow_priority_map, port->p_med_power.priority));
594 tag_end(w);
595
596 if(port->p_med_power.val < 1024) {
597 tag_start(w, "power", "Power Value");
598 tag_data(w, u2str(port->p_med_power.val * 100));
599 tag_end(w);
600 }
601 tag_end(w);
602 }
603 if (chassis->c_med_hw ||
604 chassis->c_med_sw ||
605 chassis->c_med_fw ||
606 chassis->c_med_sn ||
607 chassis->c_med_manuf ||
608 chassis->c_med_model ||
609 chassis->c_med_asset) {
610 tag_start(w, "inventory", "Inventory");
611
612 if (chassis->c_med_hw)
613 tag_datatag(w, "hardware", "Hardware Revision",
614 chassis->c_med_hw);
615 if (chassis->c_med_sw)
616 tag_datatag(w, "software", "Software Revision",
617 chassis->c_med_sw);
618 if (chassis->c_med_fw)
619 tag_datatag(w, "firmware", "Firmware Revision",
620 chassis->c_med_fw);
621 if (chassis->c_med_sn)
622 tag_datatag(w, "serial", "Serial Number",
623 chassis->c_med_sn);
624 if (chassis->c_med_manuf)
625 tag_datatag(w, "manufacturer", "Manufacturer",
626 chassis->c_med_manuf);
627 if (chassis->c_med_model)
628 tag_datatag(w, "model", "Model",
629 chassis->c_med_model);
630 if (chassis->c_med_asset)
631 tag_datatag(w, "asset", "Asset ID",
632 chassis->c_med_asset);
633
634 tag_end(w);
635 }
636
637 tag_end(w);
638 }
639 #endif
640
641 static void
642 display_chassis(struct writer * w, struct lldpd_chassis *chassis)
643 {
644 char *cid;
645 struct in_addr ip;
646 struct lldpd_mgmt *mgmt;
647 char addrbuf[INET6_ADDRSTRLEN];
648
649 if ((cid = (char *)malloc(chassis->c_id_len + 1)) == NULL)
650 fatal(NULL);
651 memcpy(cid, chassis->c_id, chassis->c_id_len);
652 cid[chassis->c_id_len] = 0;
653
654 tag_start(w, "chassis", "Chassis");
655 tag_start(w, "id", "ChassisID");
656 tag_attr (w, "type", "", map_lookup(chassis_id_subtype_map, chassis->c_id_subtype));
657
658 switch (chassis->c_id_subtype) {
659 case LLDP_CHASSISID_SUBTYPE_IFNAME:
660 case LLDP_CHASSISID_SUBTYPE_IFALIAS:
661 case LLDP_CHASSISID_SUBTYPE_LOCAL:
662 tag_data (w, cid);
663 break;
664 case LLDP_CHASSISID_SUBTYPE_LLADDR:
665 tag_data(w, dump(chassis->c_id, chassis->c_id_len, ETH_ALEN, ':'));
666 break;
667 case LLDP_CHASSISID_SUBTYPE_ADDR:
668 if (*(u_int8_t*)chassis->c_id == 1) {
669 memcpy(&ip, chassis->c_id + 1, sizeof(struct in_addr));
670 tag_data(w, inet_ntoa(ip));
671 break;
672 }
673 case LLDP_CHASSISID_SUBTYPE_PORT:
674 case LLDP_CHASSISID_SUBTYPE_CHASSIS:
675 default:
676 tag_data(w, dump(chassis->c_id, chassis->c_id_len, 16, ' '));
677 }
678
679 tag_end(w);
680
681 tag_datatag(w, "name", "SysName", chassis->c_name);
682 tag_datatag(w, "descr", "SysDescr", chassis->c_descr);
683
684 TAILQ_FOREACH(mgmt, &chassis->c_mgmt, m_entries) {
685 memset(addrbuf, 0, sizeof(addrbuf));
686 inet_ntop(lldpd_af(mgmt->m_family), &mgmt->m_addr, addrbuf, sizeof(addrbuf));
687 switch (mgmt->m_family) {
688 case LLDPD_AF_IPV4:
689 tag_datatag(w, "mgmt-ip", "MgmtIP", addrbuf);
690 break;
691 case LLDPD_AF_IPV6:
692 tag_datatag(w, "mgmt-ip6", "MgmtIPv6", addrbuf);
693 break;
694 }
695 }
696
697 display_cap(w, chassis, LLDP_CAP_OTHER, "Other");
698 display_cap(w, chassis, LLDP_CAP_REPEATER, "Repeater");
699 display_cap(w, chassis, LLDP_CAP_BRIDGE, "Bridge");
700 display_cap(w, chassis, LLDP_CAP_ROUTER, "Router");
701 display_cap(w, chassis, LLDP_CAP_WLAN, "Wlan");
702 display_cap(w, chassis, LLDP_CAP_TELEPHONE, "Tel");
703 display_cap(w, chassis, LLDP_CAP_DOCSIS, "Docsis");
704 display_cap(w, chassis, LLDP_CAP_STATION, "Station");
705
706 tag_end(w);
707 }
708
709 #ifdef ENABLE_DOT3
710 static void
711 display_autoneg(struct writer * w, struct lldpd_port *port, int bithd, int bitfd, char *desc)
712 {
713 if (!((port->p_macphy.autoneg_advertised & bithd) ||
714 (port->p_macphy.autoneg_advertised & bitfd)))
715 return;
716
717 tag_start(w, "advertised", "Adv");
718 tag_attr(w, "type", "", desc);
719 tag_attr(w, "hd", "HD", (port->p_macphy.autoneg_advertised & bithd)?"yes":"no");
720 tag_attr(w, "fd", "FD", (port->p_macphy.autoneg_advertised)?"yes":"no");
721 tag_end (w);
722 }
723 #endif
724
725 static void
726 display_port(struct writer * w, struct lldpd_port *port)
727 {
728 char *pid;
729 struct in_addr address;
730
731 if ((pid = (char *)malloc(port->p_id_len + 1)) == NULL)
732 fatal(NULL);
733 memcpy(pid, port->p_id, port->p_id_len);
734 pid[port->p_id_len] = 0;
735
736 tag_start(w, "port", "Port");
737 tag_start(w, "id", "PortID");
738 tag_attr (w, "type", "", map_lookup(port_id_subtype_map, port->p_id_subtype));
739
740 switch (port->p_id_subtype) {
741 case LLDP_PORTID_SUBTYPE_IFNAME:
742 case LLDP_PORTID_SUBTYPE_IFALIAS:
743 case LLDP_PORTID_SUBTYPE_LOCAL:
744 tag_data (w, pid);
745 break;
746 case LLDP_PORTID_SUBTYPE_LLADDR:
747 tag_data(w, dump(port->p_id, port->p_id_len, ETH_ALEN, ':'));
748 break;
749 case LLDP_PORTID_SUBTYPE_ADDR:
750 if (*(u_int8_t*)port->p_id == 1) {
751 memcpy(&address, port->p_id + 1,
752 sizeof(struct in_addr));
753 tag_data(w, inet_ntoa(address));
754 break;
755 }
756 case LLDP_PORTID_SUBTYPE_PORT:
757 case LLDP_PORTID_SUBTYPE_AGENTCID:
758 default:
759 tag_data(w, dump(port->p_id, port->p_id_len, 16, ' '));
760 }
761
762 tag_end(w);
763
764 tag_datatag(w, "descr", "PortDescr", port->p_descr);
765
766 #ifdef ENABLE_DOT3
767 if (port->p_mfs)
768 tag_datatag(w, "mfs", "MFS", u2str(port->p_mfs));
769
770 if (port->p_aggregid)
771 tag_datatag(w, "aggregation", " Port is aggregated. PortAggregID",
772 u2str(port->p_aggregid));
773
774 if (port->p_macphy.autoneg_support || port->p_macphy.autoneg_enabled ||
775 port->p_macphy.mau_type) {
776 tag_start(w, "auto-negotiation", "PMD autoneg");
777 tag_attr (w, "supported", "supported",
778 port->p_macphy.autoneg_support?"yes":"no");
779 tag_attr (w, "enabled", "enabled",
780 port->p_macphy.autoneg_enabled?"yes":"no");
781
782 if (port->p_macphy.autoneg_enabled) {
783 display_autoneg(w, port, LLDP_DOT3_LINK_AUTONEG_10BASE_T,
784 LLDP_DOT3_LINK_AUTONEG_10BASET_FD,
785 "10Base-T");
786 display_autoneg(w, port, LLDP_DOT3_LINK_AUTONEG_100BASE_TX,
787 LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD,
788 "100Base-T");
789 display_autoneg(w, port, LLDP_DOT3_LINK_AUTONEG_100BASE_T2,
790 LLDP_DOT3_LINK_AUTONEG_100BASE_T2FD,
791 "100Base-T2");
792 display_autoneg(w, port, LLDP_DOT3_LINK_AUTONEG_1000BASE_X,
793 LLDP_DOT3_LINK_AUTONEG_1000BASE_XFD,
794 "100Base-X");
795 display_autoneg(w, port, LLDP_DOT3_LINK_AUTONEG_1000BASE_T,
796 LLDP_DOT3_LINK_AUTONEG_1000BASE_TFD,
797 "1000Base-T");
798 }
799 tag_datatag(w, "current", "MAU oper type",
800 map_lookup(operational_mau_type_values, port->p_macphy.mau_type));
801 tag_end(w);
802 }
803 if (port->p_power.devicetype) {
804 tag_start(w, "power", "MDI Power");
805 tag_attr(w, "supported", "supported",
806 port->p_power.supported?"yes":"no");
807 tag_attr(w, "enabled", "enabled",
808 port->p_power.enabled?"yes":"no");
809 tag_attr(w, "paircontrol", "pair control",
810 port->p_power.paircontrol?"yes":"no");
811 tag_start(w, "device-type", "Device type");
812 tag_data(w, map_lookup(port_dot3_power_devicetype_map,
813 port->p_power.devicetype));
814 tag_end(w);
815 tag_start(w, "pairs", "Power pairs");
816 tag_data(w, map_lookup(port_dot3_power_pairs_map,
817 port->p_power.pairs));
818 tag_end(w);
819 tag_start(w, "class", "Class");
820 tag_data(w, map_lookup(port_dot3_power_class_map,
821 port->p_power.class));
822 tag_end(w);
823
824 /* 802.3at */
825 if (port->p_power.powertype != LLDP_DOT3_POWER_8023AT_OFF) {
826 tag_start(w, "power-type", "Power type");
827 tag_data(w, u2str(port->p_power.powertype));
828 tag_end(w);
829
830 tag_start(w, "source", "Power Source");
831 tag_data(w, map_lookup(
832 (port->p_power.devicetype == LLDP_DOT3_POWER_PSE)?
833 port_dot3_power_pse_source_map:
834 port_dot3_power_pd_source_map,
835 port->p_power.source));
836 tag_end(w);
837
838 tag_start(w, "priority", "Power Priority");
839 tag_data(w, map_lookup(port_dot3_power_priority_map,
840 port->p_power.priority));
841 tag_end(w);
842
843 tag_start(w, "requested", "PD requested power Value");
844 tag_data(w, u2str(port->p_power.requested * 100));
845 tag_end(w);
846
847 tag_start(w, "allocated", "PSE allocated power Value");
848 tag_data(w, u2str(port->p_power.allocated * 100));
849 tag_end(w);
850 }
851
852 tag_end(w);
853 }
854 #endif
855 tag_end(w);
856 }
857
858 #ifdef ENABLE_DOT1
859 static void
860 display_vlans(struct writer *w, struct lldpd_port *port)
861 {
862 int foundpvid = 0;
863 struct lldpd_vlan *vlan;
864 TAILQ_FOREACH(vlan, &port->p_vlans, v_entries) {
865 if (port->p_pvid == vlan->v_vid)
866 foundpvid = 1;
867
868 tag_start(w, "vlan", "VLAN");
869 tag_attr(w, "vlan-id", "", u2str(vlan->v_vid));
870 if (port->p_pvid == vlan->v_vid)
871 tag_attr(w, "pvid", "pvid", "yes");
872 tag_data(w, vlan->v_name);
873 tag_end(w);
874 }
875 if (!foundpvid && port->p_pvid) {
876 tag_start(w, "vlan", "VLAN");
877 tag_attr(w, "vlan-id", "", u2str(port->p_pvid));
878 tag_attr(w, "pvid", "pvid", "yes");
879 tag_end(w);
880 }
881 }
882
883 static void
884 display_ppvids(struct writer *w, struct lldpd_port *port)
885 {
886 struct lldpd_ppvid *ppvid;
887 TAILQ_FOREACH(ppvid, &port->p_ppvids, p_entries) {
888 tag_start(w, "ppvid", "PPVID");
889 if (ppvid->p_ppvid)
890 tag_attr(w, "value", "", u2str(ppvid->p_ppvid));
891 tag_attr(w, "supported", "supported",
892 (ppvid->p_cap_status & LLDPD_PPVID_CAP_SUPPORTED)?"yes":"no");
893 tag_attr(w, "enabled", "enabled",
894 (ppvid->p_cap_status & LLDPD_PPVID_CAP_ENABLED)?"yes":"no");
895 tag_end(w);
896 }
897 }
898
899 static void
900 display_pids(struct writer *w, struct lldpd_port *port)
901 {
902 struct lldpd_pi *pi;
903 char *hex;
904 TAILQ_FOREACH(pi, &port->p_pids, p_entries) {
905 if (!pi->p_pi_len) continue;
906 tag_start(w, "pi", "PI");
907 /* Convert to hex for display */
908 if ((hex = malloc(pi->p_pi_len * 2 + 1)) == NULL)
909 fatal(NULL);
910 for (int i = 0; i < pi->p_pi_len; i++)
911 snprintf(hex + 2*i, 3, "%02X", (unsigned char)pi->p_pi[i]);
912 tag_data(w, hex);
913 tag_end(w);
914 free(hex);
915 }
916 }
917 #endif
918
919 static const char*
920 display_age(struct lldpd_port *port)
921 {
922 static char sage[30];
923 int age = (int)(time(NULL) - port->p_lastchange);
924 if (snprintf(sage, sizeof(sage),
925 "%d day%s, %02d:%02d:%02d",
926 age / (60*60*24),
927 (age / (60*60*24) > 1)?"s":"",
928 (age / (60*60)) % 24,
929 (age / 60) % 60,
930 age % 60) >= sizeof(sage))
931 return "too much";
932 else
933 return sage;
934 }
935
936 void
937 display_interfaces(int s, const char * fmt, int hidden, int argc, char *argv[])
938 {
939 int i;
940 struct writer * w;
941 if ( strcmp(fmt,"plain") == 0 ) {
942 w = txt_init( stdout );
943 } else if (strcmp(fmt, "keyvalue") == 0) {
944 w = kv_init( stdout );
945 }
946 #ifdef USE_XML
947 else if ( strcmp(fmt,"xml") == 0 ) {
948 w = xml_init( stdout );
949 }
950 #endif
951 else {
952 w = txt_init( stdout );
953 }
954
955 char sep[80];
956 memset(sep, '-', 79);
957 sep[79] = 0;
958
959 struct lldpd_interface *iff;
960 struct lldpd_interface_list *ifs = get_interfaces(s);
961 tag_start(w, "lldp", "LLDP neighbors");
962
963 TAILQ_FOREACH(iff, ifs, next) {
964 if (optind < argc) {
965 for (i = optind; i < argc; i++)
966 if (strncmp(argv[i], iff->name, IFNAMSIZ) == 0)
967 break;
968 if (i == argc)
969 continue;
970 }
971
972 struct lldpd_port *port;
973 struct lldpd_chassis *chassis;
974 struct lldpd_hardware *hardware = get_interface(s, iff->name);
975 if (TAILQ_EMPTY(&hardware->h_rports))
976 continue;
977 TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
978 if (!hidden && SMART_HIDDEN(port)) continue;
979 chassis = port->p_chassis;
980
981 tag_start(w, "interface", "Interface");
982 tag_attr(w, "name", "", iff->name );
983 tag_attr(w, "via" , "via", map_lookup(lldpd_protocol_map, port->p_protocol));
984 tag_attr(w, "rid" , "RID", u2str(chassis->c_index));
985 tag_attr(w, "age" , "Time", display_age(port));
986
987 display_chassis(w,chassis);
988 display_port(w, port);
989 #ifdef ENABLE_DOT1
990 if (!TAILQ_EMPTY(&port->p_vlans) || port->p_pvid) {
991 display_vlans(w, port);
992 }
993 if (!TAILQ_EMPTY(&port->p_ppvids)) {
994 display_ppvids(w, port);
995 }
996 if (!TAILQ_EMPTY(&port->p_pids)) {
997 display_pids(w, port);
998 }
999 #endif
1000 #ifdef ENABLE_LLDPMED
1001 if (port->p_med_cap_enabled) {
1002 display_med(w, chassis, port);
1003 }
1004 #endif
1005 tag_end(w); /* interface */
1006 }
1007 }
1008
1009 tag_end(w);
1010 w->finish(w);
1011 }