summaryrefslogtreecommitdiff
path: root/proto_lldp.c
diff options
context:
space:
mode:
Diffstat (limited to 'proto_lldp.c')
-rw-r--r--proto_lldp.c469
1 files changed, 469 insertions, 0 deletions
diff --git a/proto_lldp.c b/proto_lldp.c
new file mode 100644
index 0000000..22b7684
--- /dev/null
+++ b/proto_lldp.c
@@ -0,0 +1,469 @@
+/*
+ * netsniff-ng - the packet sniffing beast
+ * Copyright 2012, 2013 Tobias Klauser <tklauser@distanz.ch>
+ * Subject to the GPL, version 2.
+ */
+
+#include <stdint.h>
+#include <arpa/inet.h> /* for inet_ntop() */
+#include <netinet/in.h> /* for ntohs()/ntohl() */
+
+#include "built_in.h"
+#include "oui.h"
+#include "pkt_buff.h"
+#include "proto.h"
+#include "protos.h"
+#include "xutils.h"
+
+#define EXTRACT_16BIT(x) ntohs(*((uint16_t *) (x)))
+#define EXTRACT_32BIT(x) ntohl(*((uint32_t *) (x)))
+
+#define LLDP_TLV_TYPE(tlv) (((tlv) & 0xFE00) >> 9)
+#define LLDP_TLV_LENGTH(tlv) ((tlv) & 0x01FF)
+
+/*
+ * LLDP TLV types
+ */
+#define LLDP_TLV_END 0
+#define LLDP_TLV_CHASSIS_ID 1
+#define LLDP_TLV_PORT_ID 2
+#define LLDP_TLV_TTL 3
+#define LLDP_TLV_PORT_DESC 4
+#define LLDP_TLV_SYSTEM_NAME 5
+#define LLDP_TLV_SYSTEM_DESC 6
+#define LLDP_TLV_SYSTEM_CAP 7
+#define LLDP_TLV_MGMT_ADDR 8
+#define LLDP_TLV_ORG_SPECIFIC 127
+
+/*
+ * Chassis ID subtypes
+ */
+#define LLDP_CHASSIS_SUBTYPE_CHASSIS 1
+#define LLDP_CHASSIS_SUBTYPE_IF_ALIAS 2
+#define LLDP_CHASSIS_SUBTYPE_PORT 3
+#define LLDP_CHASSIS_SUBTYPE_MAC_ADDR 4
+#define LLDP_CHASSIS_SUBTYPE_NET_ADDR 5
+#define LLDP_CHASSIS_SUBTYPE_IF_NAME 6
+#define LLDP_CHASSIS_SUBTYPE_LOCAL 7
+
+/*
+ * Port ID subtypes
+ */
+#define LLDP_PORT_SUBTYPE_IF_ALIAS 1
+#define LLDP_PORT_SUBTYPE_PORT_COMP 2
+#define LLDP_PORT_SUBTYPE_MAC_ADDR 3
+#define LLDP_PORT_SUBTYPE_NET_ADDR 4
+#define LLDP_PORT_SUBTYPE_IF_NAME 5
+#define LLDP_PORT_SUBTYPE_AGENT_CIRC_ID 6
+#define LLDP_PORT_SUBTYPE_LOCAL 7
+
+/*
+ * System capabilits bit masks
+ */
+#define LLDP_SYSTEM_CAP_OTHER (1 << 0)
+#define LLDP_SYSTEM_CAP_REPEATER (1 << 1)
+#define LLDP_SYSTEM_CAP_BRIDGE (1 << 2)
+#define LLDP_SYSTEM_CAP_WLAN_AP (1 << 3)
+#define LLDP_SYSTEM_CAP_ROUTER (1 << 4)
+#define LLDP_SYSTEM_CAP_TELEPHONE (1 << 5)
+#define LLDP_SYSTEM_CAP_DOCSIS (1 << 6)
+#define LLDP_SYSTEM_CAP_STATION_ONLY (1 << 7)
+
+/*
+ * Interface number subtypes (for Management addres TLV)
+ */
+#define LLDP_IFACE_NUM_SUBTYPE_UNKNOWN 1
+#define LLDP_IFACE_NUM_SUBTYPE_IF_INDEX 2
+#define LLDP_IFACE_NUM_SUBTYPE_SYS_PORT 3
+
+/*
+ * IANA address family numbers (only the ones we actually use)
+ * http://www.iana.org/assignments/address-family-numbers/address-family-numbers.txt
+ *
+ * TODO: Move these into own header if there are other users?
+ */
+#define IANA_AF_IPV4 1
+#define IANA_AF_IPV6 2
+#define IANA_AF_802 6
+
+static int lldp_print_net_addr(const uint8_t *addr, size_t addrlen)
+{
+ uint8_t af;
+ char buf[64];
+
+ if (addrlen < 1)
+ return -EINVAL;
+
+ af = *addr++;
+ addrlen--;
+ switch (af) {
+ case IANA_AF_IPV4:
+ if (addrlen < 4)
+ return -EINVAL;
+ inet_ntop(AF_INET, addr, buf, sizeof(buf));
+ tprintf("%s", buf);
+ break;
+ case IANA_AF_IPV6:
+ if (addrlen < 16)
+ return -EINVAL;
+ inet_ntop(AF_INET6, addr, buf, sizeof(buf));
+ tprintf("%s", buf);
+ break;
+ case IANA_AF_802:
+ if (addrlen < 6)
+ return -EINVAL;
+ tprintf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
+ addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+ break;
+ default:
+ tprintf("unknown address family");
+ break;
+ }
+
+ return 0;
+}
+
+static void lldp_print_cap(uint16_t cap)
+{
+ unsigned int prev = 0;
+
+ if (cap & LLDP_SYSTEM_CAP_OTHER)
+ tprintf("%sOther", prev++ ? ", " : "");
+ if (cap & LLDP_SYSTEM_CAP_REPEATER)
+ tprintf("%sRepeater", prev++ ? ", " : "");
+ if (cap & LLDP_SYSTEM_CAP_BRIDGE)
+ tprintf("%sBridge", prev++ ? ", " : "");
+ if (cap & LLDP_SYSTEM_CAP_WLAN_AP)
+ tprintf("%sWLAN AP", prev++ ? ", " : "");
+ if (cap & LLDP_SYSTEM_CAP_ROUTER)
+ tprintf("%sRouter", prev++ ? ", " : "");
+ if (cap & LLDP_SYSTEM_CAP_TELEPHONE)
+ tprintf("%sTelephone", prev++ ? ", " : "");
+ if (cap & LLDP_SYSTEM_CAP_DOCSIS)
+ tprintf("%sDOCSIS", prev++ ? ", " : "");
+ if (cap & LLDP_SYSTEM_CAP_STATION_ONLY)
+ tprintf("%sStation only", prev++ ? ", " : "");
+}
+
+static void lldp(struct pkt_buff *pkt)
+{
+ unsigned int n_tlv = 0;
+ uint8_t subtype, mgmt_alen, mgmt_oidlen;
+ uint16_t tlv_hdr;
+ unsigned int tlv_type, tlv_len;
+ unsigned int len;
+ uint8_t *tlv_info_str;
+ uint16_t sys_cap, en_cap;
+ uint32_t oui;
+
+ len = pkt_len(pkt);
+ if (len == 0)
+ return;
+
+ tprintf(" [ LLDP ");
+
+ while (len >= sizeof(tlv_hdr)) {
+ tlv_hdr = EXTRACT_16BIT(pkt_pull(pkt, sizeof(tlv_hdr)));
+ tlv_type = LLDP_TLV_TYPE(tlv_hdr);
+ tlv_len = LLDP_TLV_LENGTH(tlv_hdr);
+
+ len -= sizeof(tlv_hdr);
+
+ if (tlv_type == LLDP_TLV_END && tlv_len == 0) {
+ /* Chassis ID, Port ID and TTL are mandatory */
+ if (n_tlv < 3)
+ goto out_invalid;
+ else
+ break;
+ }
+ if (len < tlv_len)
+ goto out_invalid;
+
+ switch (tlv_type) {
+ case LLDP_TLV_CHASSIS_ID:
+ /*
+ * The mandatory chassis ID shall be the first TLV and
+ * shall appear exactly once.
+ */
+ if (n_tlv != 0)
+ goto out_invalid;
+
+ tprintf("Chassis ID");
+
+ if (tlv_len < 2)
+ goto out_invalid;
+
+ tlv_info_str = pkt_pull(pkt, tlv_len);
+ if (tlv_info_str == NULL)
+ goto out_invalid;
+
+ subtype = *tlv_info_str++;
+ tprintf(" (Subtype %u => ", subtype);
+
+ switch (subtype) {
+ case LLDP_CHASSIS_SUBTYPE_MAC_ADDR:
+ if (tlv_len < 7)
+ goto out_invalid;
+
+ tprintf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
+ tlv_info_str[0], tlv_info_str[1],
+ tlv_info_str[2], tlv_info_str[3],
+ tlv_info_str[4], tlv_info_str[5]);
+ break;
+ case LLDP_CHASSIS_SUBTYPE_NET_ADDR:
+ if (lldp_print_net_addr(tlv_info_str, tlv_len))
+ goto out_invalid;
+ break;
+ case LLDP_CHASSIS_SUBTYPE_CHASSIS:
+ case LLDP_CHASSIS_SUBTYPE_IF_ALIAS:
+ case LLDP_CHASSIS_SUBTYPE_PORT:
+ case LLDP_CHASSIS_SUBTYPE_IF_NAME:
+ case LLDP_CHASSIS_SUBTYPE_LOCAL:
+ tputs_safe((const char *) tlv_info_str, tlv_len - 1);
+ break;
+ default:
+ tprintf("Reserved");
+ break;
+ }
+
+ tprintf(")");
+ break;
+ case LLDP_TLV_PORT_ID:
+ /*
+ * The mandatory port ID shall be the second TLV and
+ * shall appear exactly once.
+ */
+ if (n_tlv != 1)
+ goto out_invalid;
+
+ tprintf(", Port ID");
+
+ if (tlv_len < 2)
+ goto out_invalid;
+
+ tlv_info_str = pkt_pull(pkt, tlv_len);
+ if (tlv_info_str == NULL)
+ goto out_invalid;
+
+ subtype = *tlv_info_str++;
+ tprintf(" (Subtype %u => ", subtype);
+
+ switch (subtype) {
+ case LLDP_PORT_SUBTYPE_MAC_ADDR:
+ if (tlv_len < 7)
+ goto out_invalid;
+
+ tprintf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
+ tlv_info_str[0], tlv_info_str[1],
+ tlv_info_str[2], tlv_info_str[3],
+ tlv_info_str[4], tlv_info_str[5]);
+ break;
+ case LLDP_PORT_SUBTYPE_NET_ADDR:
+ if (lldp_print_net_addr(tlv_info_str, tlv_len))
+ goto out_invalid;
+ break;
+ case LLDP_PORT_SUBTYPE_IF_ALIAS:
+ case LLDP_PORT_SUBTYPE_PORT_COMP:
+ case LLDP_PORT_SUBTYPE_IF_NAME:
+ case LLDP_PORT_SUBTYPE_AGENT_CIRC_ID:
+ case LLDP_PORT_SUBTYPE_LOCAL:
+ tputs_safe((const char *) tlv_info_str, tlv_len - 1);
+ break;
+ default:
+ tprintf("Reserved");
+ break;
+ }
+
+ tprintf(")");
+ break;
+ case LLDP_TLV_TTL:
+ /*
+ * The mandatory TTL shall be the third TLV and
+ * shall appear exactly once.
+ */
+ if (n_tlv != 2)
+ goto out_invalid;
+
+ tprintf(", TTL");
+
+ if (tlv_len != 2)
+ goto out_invalid;
+
+ tlv_info_str = pkt_pull(pkt, tlv_len);
+ if (tlv_info_str == NULL)
+ goto out_invalid;
+
+ tprintf(" (%u)", EXTRACT_16BIT(tlv_info_str));
+ break;
+ case LLDP_TLV_PORT_DESC:
+ tprintf(", Port desc (");
+
+ tlv_info_str = pkt_pull(pkt, tlv_len);
+ if (tlv_info_str == NULL)
+ tprintf("none");
+ else
+ tputs_safe((const char *) tlv_info_str, tlv_len);
+
+ tprintf(")");
+ break;
+ case LLDP_TLV_SYSTEM_NAME:
+ tprintf(", Sys name (");
+
+ tlv_info_str = pkt_pull(pkt, tlv_len);
+ if (tlv_info_str == NULL)
+ tprintf("none");
+ else
+ tputs_safe((const char *) tlv_info_str, tlv_len);
+
+ tprintf(")");
+ break;
+ case LLDP_TLV_SYSTEM_DESC:
+ tprintf(", Sys desc (");
+
+ tlv_info_str = pkt_pull(pkt, tlv_len);
+ if (tlv_info_str == NULL)
+ tprintf("none");
+ else
+ tputs_safe((const char *) tlv_info_str, tlv_len);
+
+ tprintf(")");
+ break;
+ case LLDP_TLV_SYSTEM_CAP:
+ tprintf(", Sys Cap");
+
+ if (tlv_len != 4)
+ goto out_invalid;
+
+ tlv_info_str = pkt_pull(pkt, tlv_len);
+ if (tlv_info_str == NULL)
+ goto out_invalid;
+
+ sys_cap = EXTRACT_16BIT(tlv_info_str);
+ tlv_info_str += sizeof(uint32_t);
+ en_cap = EXTRACT_16BIT(tlv_info_str);
+
+ tprintf(" (");
+ lldp_print_cap(sys_cap);
+ tprintf(")");
+ tprintf(" Ena Cap (");
+ lldp_print_cap(en_cap);
+ tprintf(")");
+ break;
+ case LLDP_TLV_MGMT_ADDR:
+ tprintf(", Mgmt Addr (");
+
+ if (tlv_len < 9 || tlv_len > 167)
+ goto out_invalid;
+
+ tlv_info_str = pkt_pull(pkt, tlv_len);
+ if (tlv_info_str == NULL)
+ goto out_invalid;
+
+ mgmt_alen = *tlv_info_str;
+ tlv_info_str++;
+ if (tlv_len - 1 < mgmt_alen)
+ goto out_invalid;
+
+ if (lldp_print_net_addr(tlv_info_str, mgmt_alen))
+ goto out_invalid;
+ tlv_info_str += mgmt_alen;
+
+ tprintf(", Iface Subtype %d/", *tlv_info_str);
+ switch (*tlv_info_str) {
+ case LLDP_IFACE_NUM_SUBTYPE_IF_INDEX:
+ tprintf("ifIndex");
+ break;
+ case LLDP_IFACE_NUM_SUBTYPE_SYS_PORT:
+ tprintf("System Port Number");
+ break;
+ default:
+ tprintf("Unknown");
+ break;
+ }
+
+ tlv_info_str++;
+ tprintf(", Iface Number %u", EXTRACT_32BIT(tlv_info_str));
+
+ tlv_info_str += 4;
+ mgmt_oidlen = *tlv_info_str;
+ if (tlv_len - mgmt_alen - sizeof(uint32_t) - 3 < mgmt_oidlen)
+ goto out_invalid;
+ if (mgmt_oidlen > 0) {
+ tprintf(", OID ");
+ tputs_safe((const char *) tlv_info_str + 1, mgmt_oidlen);
+ }
+
+ tprintf(")");
+ break;
+ case LLDP_TLV_ORG_SPECIFIC:
+ tprintf(", Org specific");
+
+ if (tlv_len < 4)
+ goto out_invalid;
+
+ tlv_info_str = pkt_pull(pkt, 4);
+ if (tlv_info_str == NULL)
+ goto out_invalid;
+
+ oui = ntohl(*((uint32_t *) tlv_info_str));
+ subtype = oui & 0xff;
+ oui >>= 8;
+ tprintf(" (OUI %s, Subtype %u)", lookup_vendor_str(oui),
+ subtype);
+
+ /* Just eat it up, we don't know how to interpret it */
+ pkt_pull(pkt, tlv_len - 4);
+ break;
+ default:
+ tprintf(", Unknown TLV %u", tlv_type);
+ pkt_pull(pkt, tlv_len);
+ break;
+ }
+
+ n_tlv++;
+ }
+
+ len -= tlv_len;
+
+ tprintf(" ]\n");
+ return;
+
+out_invalid:
+ tprintf(" %sINVALID%s ]\n", colorize_start_full(black, red),
+ colorize_end());
+}
+
+static void lldp_less(struct pkt_buff *pkt)
+{
+ unsigned int len, n_tlv = 0;
+ unsigned int tlv_type, tlv_len;
+ uint16_t tlv_hdr;
+
+ len = pkt_len(pkt);
+
+ while (len >= sizeof(tlv_hdr)) {
+ tlv_hdr = EXTRACT_16BIT(pkt_pull(pkt, sizeof(tlv_hdr)));
+ tlv_type = LLDP_TLV_TYPE(tlv_hdr);
+ tlv_len = LLDP_TLV_LENGTH(tlv_hdr);
+
+ n_tlv++;
+ len -= sizeof(tlv_hdr);
+
+ if (tlv_type == LLDP_TLV_END || tlv_len == 0)
+ break;
+ if (len < tlv_len)
+ break;
+
+ pkt_pull(pkt, tlv_len);
+
+ len -= tlv_len;
+ }
+
+ tprintf(" %u TLV%s", n_tlv, n_tlv == 1 ? "" : "s");
+}
+
+struct protocol lldp_ops = {
+ .key = 0x88cc,
+ .print_full = lldp,
+ .print_less = lldp_less,
+};