From 89840df069d47ae787c90cb9ee2666ee9f55db05 Mon Sep 17 00:00:00 2001 From: Vincent Bernat Date: Wed, 26 Nov 2008 12:47:12 +0100 Subject: [PATCH] Start support of LLDP-MED: send support is implemented --- configure.ac | 16 +++++++++++ man/lldpd.8 | 13 +++++++++ src/features.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++- src/lldp.c | 59 ++++++++++++++++++++++++++++++++++++++++ src/lldp.h | 39 ++++++++++++++++++++++++++ src/lldpd.c | 61 ++++++++++++++++++++++++++++++++++++++++- src/lldpd.h | 27 ++++++++++++++++++ src/priv.c | 10 +++++-- 8 files changed, 295 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index db13cb9c..806a83bc 100644 --- a/configure.ac +++ b/configure.ac @@ -88,6 +88,7 @@ if test "${with_snmp}" != "no"; then fi # Options +# CDP AC_ARG_ENABLE(cdp, AC_HELP_STRING([--enable-cdp], [Enable Cisco Discovery Protocol]), [enable_cdp=$enableval],[enable_cdp=yes]) @@ -99,6 +100,7 @@ else AC_MSG_RESULT(no) fi +# FDP AC_ARG_ENABLE(fdp, AC_HELP_STRING([--enable-fdp], [Enable Foundry Discovery Protocol]), [enable_fdp=$enableval],[enable_fdp=yes]) @@ -110,6 +112,7 @@ else AC_MSG_RESULT(no) fi +# EDP AC_ARG_ENABLE(edp, AC_HELP_STRING([--enable-edp], [Enable Extreme Discovery Protocol]), [enable_edp=$enableval],[enable_edp=yes]) @@ -121,6 +124,7 @@ else AC_MSG_RESULT(no) fi +# SONMP AC_ARG_ENABLE(sonmp, AC_HELP_STRING([--enable-sonmp], [Enable SynOptics Network Management Protocol]), [enable_sonmp=$enableval],[enable_sonmp=yes]) @@ -132,4 +136,16 @@ else AC_MSG_RESULT(no) fi +#LLDPMED +AC_ARG_ENABLE(lldpmed, AC_HELP_STRING([--enable-lldpmed], + [Enable LLDP-MED extension]), + [enable_lldpmed=$enableval],[enable_lldpmed=yes]) +AC_MSG_CHECKING(whether to enable LLDP-MED) +if test x$enable_lldpmed = xyes; then + AC_MSG_RESULT(yes) + AC_DEFINE([ENABLE_LLDPMED],, [Enable LLDP-MED extension]) +else + AC_MSG_RESULT(no) +fi + AC_OUTPUT diff --git a/man/lldpd.8 b/man/lldpd.8 index d1fb04b9..701642b5 100644 --- a/man/lldpd.8 +++ b/man/lldpd.8 @@ -24,6 +24,7 @@ .Op Fl dvxcse .Op Fl m Ar management .Op Fl p Ar probe time +.Op Fl M Ar class .Sh DESCRIPTION .Nm is a daemon able to receive and send @@ -91,6 +92,18 @@ protocol. This time will be used by to detect false positives like SONMP frames running through a switch only supporting CDP. This value is only used when multiple protocols are enabled. +.It Fl M Ar class +Enable emission of LLDP-MED frame. The class should be one of the +following value: +.Bl -tag -width "0:XX" -compact +.It Ar 1 +Generic Endpoint (Class I) +.It Ar 2 +Media Endpoint (Class II) +.It Ar 3 +Communication Device Endpoints (Class III) +.It Ar 4 +Network Connectivity Device .El .Sh FILES .Bl -tag -width "/var/run/lldpd.socketXX" -compact diff --git a/src/features.c b/src/features.c index af8fdd39..bdcaa58b 100644 --- a/src/features.c +++ b/src/features.c @@ -34,7 +34,6 @@ #include #define SYSFS_PATH_MAX 256 -#define SYSFS_CLASS_NET "/sys/class/net/" #define MAX_PORTS 1024 /* net/if.h */ @@ -207,3 +206,76 @@ iface_is_enslaved(struct lldpd *cfg, const char *name) freeifaddrs(ifap); return -1; } + +#ifdef ENABLE_LLDPMED + /* Fill in inventory stuff: + - hardware version: /sys/class/dmi/id/product_version + - firmware version: /sys/class/dmi/id/bios_version + - software version: `uname -r` + - serial number: /sys/class/dmi/id/product_serial + - manufacturer: /sys/class/dmi/id/sys_vendor + - model: /sys/class/dmi/id/product_name + - asset: /sys/class/dmi/id/chassis_asset_tag + */ + +char* +dmi_get(char *file) +{ + int dmi, s; + char buffer[100]; + + if ((dmi = priv_open(file)) < 0) { + LLOG_DEBUG("cannot get DMI file %s", file); + return NULL; + } + memset(buffer, 0, sizeof(buffer)); + if ((s = read(dmi, buffer, sizeof(buffer))) == -1) { + LLOG_DEBUG("cannot read DMI file %s", file); + close(dmi); + return NULL; + } + close(dmi); + buffer[sizeof(buffer) - 1] = '\0'; + if ((s > 0) && (buffer[s-1] == '\n')) + buffer[s-1] = '\0'; + if (strlen(buffer)) + return strdup(buffer); + return NULL; +} + +char* +dmi_hw() +{ + return dmi_get(SYSFS_CLASS_DMI "product_version"); +} + +char* +dmi_fw() +{ + return dmi_get(SYSFS_CLASS_DMI "bios_version"); +} + +char* +dmi_sn() +{ + return dmi_get(SYSFS_CLASS_DMI "product_serial"); +} + +char* +dmi_manuf() +{ + return dmi_get(SYSFS_CLASS_DMI "sys_vendor"); +} + +char* +dmi_model() +{ + return dmi_get(SYSFS_CLASS_DMI "product_name"); +} + +char* +dmi_asset() +{ + return dmi_get(SYSFS_CLASS_DMI "chassis_asset_tag"); +} +#endif diff --git a/src/lldp.c b/src/lldp.c index 0eb945a4..d705a2f0 100644 --- a/src/lldp.c +++ b/src/lldp.c @@ -45,6 +45,12 @@ lldp_send(struct lldpd *global, struct lldpd_chassis *chassis, struct lldp_mgmt mgmt; struct lldp_aggreg aggreg; struct lldp_macphy macphy; +#ifdef ENABLE_LLDPMED + const u_int8_t med[] = LLDP_TLV_ORG_MED; + struct lldpmed_cap medcap; + struct lldp_org medhw, medfw, medsw, medsn, + medmodel, medasset, medmanuf; +#endif struct lldpd_vlan *vlan; struct lldpd_port *port = &hardware->h_lport; u_int c = -1, len = 0, v; @@ -234,6 +240,59 @@ lldp_send(struct lldpd *global, struct lldpd_chassis *chassis, iov[c].iov_base = &macphy; iov[c].iov_len = sizeof(macphy); +#ifdef ENABLE_LLDPMED + if (global->g_lchassis.c_med_cap) { + /* LLDP-MED cap */ + memset(&medcap, 0, sizeof(medcap)); + medcap.tlv_head.type_len = LLDP_TLV_HEAD(LLDP_TLV_ORG, + sizeof(medcap.tlv_org_id) + + sizeof(medcap.tlv_org_subtype) + + sizeof(medcap.tlv_cap) + sizeof(medcap.tlv_type)); + memcpy(medcap.tlv_org_id, med, sizeof(medcap.tlv_org_id)); + medcap.tlv_org_subtype = LLDP_TLV_MED_CAP; + medcap.tlv_cap = htons(global->g_lchassis.c_med_cap); + medcap.tlv_type = global->g_lchassis.c_med_type; + IOV_NEW; + iov[c].iov_base = &medcap; + iov[c].iov_len = sizeof(medcap); + + /* LLDP-MED inventory */ +#define LLDP_INVENTORY(value, target, subtype) \ + if (value) { \ + memset(&target, 0, sizeof(target)); \ + len = (strlen(value)>32)?32:strlen(value); \ + target.tlv_head.type_len = \ + LLDP_TLV_HEAD(LLDP_TLV_ORG, \ + sizeof(target.tlv_org_id) + \ + sizeof(target.tlv_org_subtype) + \ + len); \ + memcpy(target.tlv_org_id, med, \ + sizeof(target.tlv_org_id)); \ + target.tlv_org_subtype = subtype; \ + IOV_NEW; \ + iov[c].iov_base = ⌖ \ + iov[c].iov_len = sizeof(target); \ + IOV_NEW; \ + iov[c].iov_base = value; \ + iov[c].iov_len = len; \ + } + LLDP_INVENTORY(global->g_lchassis.c_med_hw, + medhw, LLDP_TLV_MED_IV_HW); + LLDP_INVENTORY(global->g_lchassis.c_med_fw, + medfw, LLDP_TLV_MED_IV_FW); + LLDP_INVENTORY(global->g_lchassis.c_med_sw, + medsw, LLDP_TLV_MED_IV_SW); + LLDP_INVENTORY(global->g_lchassis.c_med_sn, + medsn, LLDP_TLV_MED_IV_SN); + LLDP_INVENTORY(global->g_lchassis.c_med_manuf, + medmanuf, LLDP_TLV_MED_IV_MANUF); + LLDP_INVENTORY(global->g_lchassis.c_med_model, + medmodel, LLDP_TLV_MED_IV_MODEL); + LLDP_INVENTORY(global->g_lchassis.c_med_asset, + medasset, LLDP_TLV_MED_IV_ASSET); + } +#endif + /* END */ memset(&end, 0, sizeof(end)); IOV_NEW; diff --git a/src/lldp.h b/src/lldp.h index 54ef45e0..a16d26dd 100644 --- a/src/lldp.h +++ b/src/lldp.h @@ -46,6 +46,7 @@ enum { #define LLDP_TLV_ORG_DOT1 {0x00, 0x80, 0xc2} #define LLDP_TLV_ORG_DOT3 {0x00, 0x12, 0x0f} +#define LLDP_TLV_ORG_MED {0x00, 0x12, 0xbb} enum { LLDP_TLV_DOT1_PVID = 1, @@ -230,4 +231,42 @@ struct lldp_end { struct lldp_tlv_head tlv_head; } __attribute__ ((__packed__)); +#ifdef ENABLE_LLDPMED +enum { + LLDP_TLV_MED_CAP = 1, + LLDP_TLV_MED_POLICY = 2, + LLDP_TLV_MED_LOCATION = 3, + LLDP_TLV_MED_MDI = 4, + LLDP_TLV_MED_IV_HW = 5, + LLDP_TLV_MED_IV_FW = 6, + LLDP_TLV_MED_IV_SW = 7, + LLDP_TLV_MED_IV_SN = 8, + LLDP_TLV_MED_IV_MANUF = 9, + LLDP_TLV_MED_IV_MODEL = 10, + LLDP_TLV_MED_IV_ASSET = 11 +}; + +#define LLDPMED_CLASS_I 1 +#define LLDPMED_CLASS_II 2 +#define LLDPMED_CLASS_III 3 +#define LLDPMED_NETWORK_DEVICE 4 + +#define LLDPMED_CAP_CAP 0x01 +#define LLDPMED_CAP_POLICY 0x02 +#define LLDPMED_CAP_LOCATION 0x04 +#define LLDPMED_CAP_MDI1 0x08 +#define LLDPMED_CAP_MDI2 0x10 +#define LLDPMED_CAP_IV 0x20 + +struct lldpmed_cap { + struct lldp_tlv_head tlv_head; + u_int8_t tlv_org_id[3]; + u_int8_t tlv_org_subtype; + u_int16_t tlv_cap; + u_int8_t tlv_type; +} __attribute__ ((__packed__)); + +#endif /* ENABLE_LLDPMED */ + + #endif /* _LLDP_H */ diff --git a/src/lldpd.c b/src/lldpd.c index 28deec83..fe5dbbff 100644 --- a/src/lldpd.c +++ b/src/lldpd.c @@ -168,6 +168,9 @@ void lldpd_recv_all(struct lldpd *); int lldpd_guess_type(struct lldpd *, char *, int); void lldpd_decode(struct lldpd *, char *, int, struct lldpd_hardware *, int); +#ifdef ENABLE_LLDPMED +void lldpd_med(struct lldpd_chassis *); +#endif char **saved_argv; @@ -375,6 +378,14 @@ lldpd_port_cleanup(struct lldpd_port *port) void lldpd_chassis_cleanup(struct lldpd_chassis *chassis) { +#ifdef ENABLE_LLDPMED + free(chassis->c_med_hw); + free(chassis->c_med_fw); + free(chassis->c_med_sn); + free(chassis->c_med_manuf); + free(chassis->c_med_model); + free(chassis->c_med_asset); +#endif free(chassis->c_id); free(chassis->c_name); free(chassis->c_descr); @@ -1159,6 +1170,25 @@ lldpd_send_all(struct lldpd *cfg) } } +#ifdef ENABLE_LLDPMED +void +lldpd_med(struct lldpd_chassis *chassis) +{ + free(chassis->c_med_hw); + free(chassis->c_med_fw); + free(chassis->c_med_sn); + free(chassis->c_med_manuf); + free(chassis->c_med_model); + free(chassis->c_med_asset); + chassis->c_med_hw = dmi_hw(); + chassis->c_med_fw = dmi_fw(); + chassis->c_med_sn = dmi_sn(); + chassis->c_med_manuf = dmi_manuf(); + chassis->c_med_model = dmi_model(); + chassis->c_med_asset = dmi_asset(); +} +#endif + void lldpd_loop(struct lldpd *cfg) { @@ -1185,7 +1215,6 @@ lldpd_loop(struct lldpd *cfg) if (asprintf(&cfg->g_lchassis.c_descr, "%s %s %s %s", un->sysname, un->release, un->version, un->machine) == -1) fatal("failed to set system description"); - free(un); /* Check forwarding */ cfg->g_lchassis.c_cap_enabled = 0; @@ -1195,6 +1224,14 @@ lldpd_loop(struct lldpd *cfg) } close(f); } +#ifdef ENABLE_LLDPMED + if (cfg->g_lchassis.c_cap_available & LLDP_CAP_TELEPHONE) + cfg->g_lchassis.c_cap_enabled |= LLDP_CAP_TELEPHONE; + lldpd_med(&cfg->g_lchassis); + free(cfg->g_lchassis.c_med_sw); + cfg->g_lchassis.c_med_sw = strdup(un->release); +#endif + free(un); TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) hardware->h_flags = 0; @@ -1319,6 +1356,9 @@ main(int argc, char *argv[]) char *mgmtp = NULL; char *popt, opts[] = "vdxm:p:@ "; int probe = 0, i, found, vlan = 0; +#ifdef ENABLE_LLDPMED + int lldpmed = 0; +#endif saved_argv = argv; @@ -1330,6 +1370,10 @@ main(int argc, char *argv[]) if (protos[i].enabled == 1) continue; *(popt++) = protos[i].arg; } +#ifdef ENABLE_LLDPMED + *(popt++) = 'M'; + *(popt++) = ':'; +#endif *popt = '\0'; while ((ch = getopt(argc, argv, opts)) != -1) { switch (ch) { @@ -1342,6 +1386,13 @@ main(int argc, char *argv[]) case 'm': mgmtp = optarg; break; +#ifdef ENABLE_LLDPMED + case 'M': + lldpmed = atoi(optarg); + if ((lldpmed < 1) || (lldpmed > 4)) + usage(); + break; +#endif case 'p': probe = atoi(optarg); break; @@ -1399,6 +1450,14 @@ main(int argc, char *argv[]) /* Set system capabilities */ cfg->g_lchassis.c_cap_available = LLDP_CAP_BRIDGE | LLDP_CAP_WLAN | LLDP_CAP_ROUTER; +#ifdef ENABLE_LLDPMED + if (lldpmed > 0) { + if (lldpmed == LLDPMED_CLASS_III) + cfg->g_lchassis.c_cap_available |= LLDP_CAP_TELEPHONE; + cfg->g_lchassis.c_med_type = lldpmed; + cfg->g_lchassis.c_med_cap = LLDPMED_CAP_CAP | LLDPMED_CAP_IV; + } +#endif /* Set TTL */ cfg->g_lchassis.c_ttl = LLDPD_TTL; diff --git a/src/lldpd.h b/src/lldpd.h index 56c517c0..c811f315 100644 --- a/src/lldpd.h +++ b/src/lldpd.h @@ -47,6 +47,8 @@ #include "edp.h" #endif +#define SYSFS_CLASS_NET "/sys/class/net/" +#define SYSFS_CLASS_DMI "/sys/class/dmi/id/" #define LLDPD_TTL 120 #define LLDPD_TX_DELAY 30 #define LLDPD_TX_MSGDELAY 1 @@ -78,8 +80,25 @@ struct lldpd_chassis { struct in_addr c_mgmt; u_int32_t c_mgmt_if; + +#ifdef ENABLE_LLDPMED + u_int16_t c_med_cap; + u_int8_t c_med_type; + char *c_med_hw; + char *c_med_fw; + char *c_med_sw; + char *c_med_sn; + char *c_med_manuf; + char *c_med_model; + char *c_med_asset; +#endif + }; +#ifndef ENABLE_LLDP_MED #define STRUCT_LLDPD_CHASSIS "bCsswwwll" +#else +#define STRUCT_LLDPD_CHASSIS "bCsswwwllwbsssssss" +#endif struct lldpd_port { u_int8_t p_id_subtype; @@ -301,6 +320,14 @@ int iface_is_bond(struct lldpd *, const char *); int iface_is_bond_slave(struct lldpd *, const char *, const char *); int iface_is_enslaved(struct lldpd *, const char *); +#ifdef ENABLE_LLDPMED +char *dmi_hw(); +char *dmi_fw(); +char *dmi_sn(); +char *dmi_manuf(); +char *dmi_model(); +char *dmi_asset(); +#endif /* log.c */ void log_init(int); diff --git a/src/priv.c b/src/priv.c index 513bc005..deeb2e19 100644 --- a/src/priv.c +++ b/src/priv.c @@ -246,8 +246,14 @@ asroot_open() { const char* authorized[] = { "/proc/sys/net/ipv4/ip_forward", - "/sys/class/net/[^.][^/]*/brforward", - "/sys/class/net/[^.][^/]*/brport", + SYSFS_CLASS_NET "[^.][^/]*/brforward", + SYSFS_CLASS_NET "[^.][^/]*/brport", + SYSFS_CLASS_DMI "product_version", + SYSFS_CLASS_DMI "product_serial", + SYSFS_CLASS_DMI "product_name", + SYSFS_CLASS_DMI "bios_version", + SYSFS_CLASS_DMI "sys_vendor", + SYSFS_CLASS_DMI "chassis_asset_tag", NULL }; char **f; -- 2.39.5