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