]> git.ipfire.org Git - thirdparty/lldpd.git/blobdiff - src/lldpd.c
Lower log level for problem when getting bridge info
[thirdparty/lldpd.git] / src / lldpd.c
index 4c28aefa3cd101874da6bf4df9338b65f47ed226..27a4bbcb83cc3de148085d5b5074763fcf6738bf 100644 (file)
 #include <sys/time.h>
 #include <sys/ioctl.h>
 #include <arpa/inet.h>
-#include <netpacket/packet.h>
 #include <ifaddrs.h>
 #include <net/if_arp.h>
 #include <linux/filter.h>
 #include <linux/if_vlan.h>
+#include <linux/if_packet.h>
 #include <linux/sockios.h>
 
 #ifdef USE_SNMP
 #include <net-snmp/agent/snmp_vars.h>
 #endif /* USE_SNMP */
 
-void            usage(void);
+static void             usage(void);
 
-int                     lldpd_iface_init(struct lldpd *, struct lldpd_hardware *);
-int                     lldpd_iface_init_vlan(struct lldpd *, struct lldpd_vif *);
-void                    lldpd_iface_init_mtu(struct lldpd *, struct lldpd_hardware *);
-int                     lldpd_iface_close(struct lldpd *, struct lldpd_hardware *);
-void                    lldpd_iface_multicast(struct lldpd *, const char *, int);
+static int              lldpd_iface_init(struct lldpd *, struct lldpd_hardware *);
+static int              lldpd_iface_init_vlan(struct lldpd *, struct lldpd_vif *);
+static void             lldpd_iface_init_mtu(struct lldpd *, struct lldpd_hardware *);
+static int              lldpd_iface_close(struct lldpd *, struct lldpd_hardware *);
+static void             lldpd_iface_multicast(struct lldpd *, const char *, int);
 
 /* "ether proto 0x88cc and ether dst 01:80:c2:00:00:0e" */
 #define LLDPD_FILTER_LLDP_F                                            \
@@ -64,7 +64,8 @@ void                   lldpd_iface_multicast(struct lldpd *, const char *, int);
         { 0x15, 0, 1, 0x00000180 },                                    \
         { 0x6, 0, 0, 0x0000ffff },                                     \
         { 0x6, 0, 0, 0x00000000 },
-struct sock_filter lldpd_filter_lldp_f[] = { LLDPD_FILTER_LLDP_F };
+static struct sock_filter lldpd_filter_lldp_f[] = { LLDPD_FILTER_LLDP_F };
+#ifdef ENABLE_FDP
 /* "ether dst 01:e0:52:cc:cc:cc" */
 #define LLDPD_FILTER_FDP_F                     \
         { 0x20, 0, 0, 0x00000002 },            \
@@ -73,7 +74,9 @@ struct sock_filter lldpd_filter_lldp_f[] = { LLDPD_FILTER_LLDP_F };
         { 0x15, 0, 1, 0x000001e0 },            \
         { 0x6, 0, 0, 0x0000ffff },             \
         { 0x6, 0, 0, 0x00000000 },
-struct sock_filter lldpd_filter_fdp_f[] = { LLDPD_FILTER_FDP_F };
+static struct sock_filter lldpd_filter_fdp_f[] = { LLDPD_FILTER_FDP_F };
+#endif /* ENABLE_FDP */
+#ifdef ENABLE_CDP
 /* "ether dst 01:00:0c:cc:cc:cc" */
 #define LLDPD_FILTER_CDP_F                     \
         { 0x20, 0, 0, 0x00000002 },            \
@@ -82,7 +85,9 @@ struct sock_filter lldpd_filter_fdp_f[] = { LLDPD_FILTER_FDP_F };
         { 0x15, 0, 1, 0x00000100 },            \
         { 0x6, 0, 0, 0x0000ffff },             \
         { 0x6, 0, 0, 0x00000000 },
-struct sock_filter lldpd_filter_cdp_f[] = { LLDPD_FILTER_CDP_F };
+static struct sock_filter lldpd_filter_cdp_f[] = { LLDPD_FILTER_CDP_F };
+#endif /* ENABLE_CDP */
+#ifdef ENABLE_SONMP
 /* "ether dst 01:00:81:00:01:00" */
 #define LLDPD_FILTER_SONMP_F                   \
         { 0x20, 0, 0, 0x00000002 },            \
@@ -91,7 +96,9 @@ struct sock_filter lldpd_filter_cdp_f[] = { LLDPD_FILTER_CDP_F };
         { 0x15, 0, 1, 0x00000100 },            \
         { 0x6, 0, 0, 0x0000ffff },             \
         { 0x6, 0, 0, 0x00000000 },
-struct sock_filter lldpd_filter_sonmp_f[] = { LLDPD_FILTER_SONMP_F };
+static struct sock_filter lldpd_filter_sonmp_f[] = { LLDPD_FILTER_SONMP_F };
+#endif /* ENABLE_SONMP */
+#ifdef ENABLE_EDP
 /* "ether dst 00:e0:2b:00:00:00" */
 #define LLDPD_FILTER_EDP_F              \
        { 0x20, 0, 0, 0x00000002 },     \
@@ -100,7 +107,8 @@ struct sock_filter lldpd_filter_sonmp_f[] = { LLDPD_FILTER_SONMP_F };
        { 0x15, 0, 1, 0x000000e0 },     \
        { 0x6, 0, 0, 0x0000ffff },      \
        { 0x6, 0, 0, 0x00000000 },
-struct sock_filter lldpd_filter_edp_f[] = { LLDPD_FILTER_EDP_F };
+static struct sock_filter lldpd_filter_edp_f[] = { LLDPD_FILTER_EDP_F };
+#endif /* ENABLE_EDP */
 #define LLDPD_FILTER_ANY_F             \
        { 0x28, 0, 0, 0x0000000c },     \
        { 0x15, 0, 4, 0x000088cc },     \
@@ -121,53 +129,62 @@ struct sock_filter lldpd_filter_edp_f[] = { LLDPD_FILTER_EDP_F };
        { 0x15, 0, 1, 0x000001e0 },     \
        { 0x6, 0, 0, 0x0000ffff },      \
        { 0x6, 0, 0, 0x00000000 },
-struct sock_filter lldpd_filter_any_f[] = { LLDPD_FILTER_ANY_F };
+static struct sock_filter lldpd_filter_any_f[] = { LLDPD_FILTER_ANY_F };
 
-struct protocol protos[] =
+static struct protocol protos[] =
 {
        { LLDPD_MODE_LLDP, 1, "LLDP", ' ', lldp_send, lldp_decode, NULL,
          LLDP_MULTICAST_ADDR, lldpd_filter_lldp_f, sizeof(lldpd_filter_lldp_f) },
+#ifdef ENABLE_CDP
        { LLDPD_MODE_CDPV1, 0, "CDPv1", 'c', cdpv1_send, cdp_decode, cdpv1_guess,
          CDP_MULTICAST_ADDR, lldpd_filter_cdp_f, sizeof(lldpd_filter_cdp_f) },
        { LLDPD_MODE_CDPV2, 0, "CDPv2", 'c', cdpv2_send, cdp_decode, cdpv2_guess,
          CDP_MULTICAST_ADDR, lldpd_filter_cdp_f, sizeof(lldpd_filter_cdp_f) },
+#endif
+#ifdef ENABLE_SONMP
        { LLDPD_MODE_SONMP, 0, "SONMP", 's', sonmp_send, sonmp_decode, NULL,
          SONMP_MULTICAST_ADDR, lldpd_filter_sonmp_f, sizeof(lldpd_filter_sonmp_f) },
+#endif
+#ifdef ENABLE_EDP
        { LLDPD_MODE_EDP, 0, "EDP", 'e', edp_send, edp_decode, NULL,
          EDP_MULTICAST_ADDR, lldpd_filter_edp_f, sizeof(lldpd_filter_edp_f) },
+#endif
+#ifdef ENABLE_FDP
        { LLDPD_MODE_FDP, 0, "FDP", 'f', fdp_send, cdp_decode, NULL,
          FDP_MULTICAST_ADDR, lldpd_filter_fdp_f, sizeof(lldpd_filter_fdp_f) },
+#endif
        { 0, 0, "any", ' ', NULL, NULL, NULL,
          {0,0,0,0,0,0}, lldpd_filter_any_f, sizeof(lldpd_filter_any_f) }
 };
 
-int                     lldpd_iface_switchto(struct lldpd *, short int,
+static int              lldpd_iface_switchto(struct lldpd *, short int,
                            struct lldpd_hardware *);
+static
 struct lldpd_hardware  *lldpd_port_add(struct lldpd *, struct ifaddrs *);
-void                    lldpd_loop(struct lldpd *);
-void                    lldpd_shutdown(int);
-void                    lldpd_exit();
-void                    lldpd_send_all(struct lldpd *);
-void                    lldpd_recv_all(struct lldpd *);
-int                     lldpd_guess_type(struct lldpd *, char *, int);
-void                    lldpd_decode(struct lldpd *, char *, int,
+static void             lldpd_loop(struct lldpd *);
+static void             lldpd_shutdown(int);
+static void             lldpd_exit();
+static void             lldpd_send_all(struct lldpd *);
+static void             lldpd_recv_all(struct lldpd *);
+static int              lldpd_guess_type(struct lldpd *, char *, int);
+static void             lldpd_decode(struct lldpd *, char *, int,
                            struct lldpd_hardware *, int);
+#ifdef ENABLE_LLDPMED
+static void             lldpd_med(struct lldpd_chassis *);
+#endif
 
-char   **saved_argv;
+static char            **saved_argv;
 
-void
+static void
 usage(void)
 {
        extern const char       *__progname;
-#ifndef USE_SNMP
-       fprintf(stderr, "usage: %s [-dvcse] [-p|-P] [-m ip]\n", __progname);
-#else /* USE_SNMP */
-       fprintf(stderr, "usage: %s [-dvcsex] [-p|-P] [-m ip]\n", __progname);
-#endif /* USE_SNMP */
+       fprintf(stderr, "usage: %s [options]\n", __progname);
+       fprintf(stderr, "see manual page lldpd(8) for more information\n");
        exit(1);
 }
 
-void
+static void
 lldpd_iface_init_mtu(struct lldpd *global, struct lldpd_hardware *hardware)
 {
        struct ifreq ifr;
@@ -179,10 +196,10 @@ lldpd_iface_init_mtu(struct lldpd *global, struct lldpd_hardware *hardware)
                LLOG_WARN("unable to get MTU of %s, using 1500", hardware->h_ifname);
                hardware->h_mtu = 1500;
        } else
-               hardware->h_mtu = ifr.ifr_mtu;
+               hardware->h_mtu = hardware->h_lport.p_mfs = ifr.ifr_mtu;
 }
 
-int
+static int
 lldpd_iface_init_vlan(struct lldpd *global, struct lldpd_vif *vif)
 {
        int status;
@@ -211,7 +228,7 @@ lldpd_iface_init_vlan(struct lldpd *global, struct lldpd_vif *vif)
        return 0;
 }
 
-int
+static int
 lldpd_iface_init(struct lldpd *global, struct lldpd_hardware *hardware)
 {
        int master;             /* Bond device */
@@ -261,11 +278,11 @@ lldpd_iface_init(struct lldpd *global, struct lldpd_hardware *hardware)
        return 0;
 }
 
-void
+static void
 lldpd_iface_multicast(struct lldpd *global, const char *name, int remove)
 {
        int i, rc;
-       
+
        for (i=0; global->g_protocols[i].mode != 0; i++) {
                if (!global->g_protocols[i].enabled) continue;
                if ((rc = priv_iface_multicast(name,
@@ -280,7 +297,7 @@ lldpd_iface_multicast(struct lldpd *global, const char *name, int remove)
        }
 }
 
-int
+static int
 lldpd_iface_close(struct lldpd *global, struct lldpd_hardware *hardware)
 {
        char listen[IFNAMSIZ];
@@ -304,7 +321,7 @@ lldpd_iface_close(struct lldpd *global, struct lldpd_hardware *hardware)
        return 0;
 }
 
-int
+static int
 lldpd_iface_switchto(struct lldpd *cfg, short int filter, struct lldpd_hardware *hardware)
 {
        struct sock_fprog prog;
@@ -332,7 +349,7 @@ lldpd_iface_switchto(struct lldpd *cfg, short int filter, struct lldpd_hardware
        return 0;
 }
 
-
+#ifdef ENABLE_DOT1
 void
 lldpd_vlan_cleanup(struct lldpd_port *port)
 {
@@ -346,11 +363,19 @@ lldpd_vlan_cleanup(struct lldpd_port *port)
                free(vlan);
        }
 }
+#endif
 
 void
 lldpd_port_cleanup(struct lldpd_port *port)
 {
+#ifdef ENABLE_LLDPMED
+       int i;
+       for (i=0; i < LLDPMED_LOCFORMAT_LAST; i++)
+               free(port->p_med_location[i].data);
+#endif
+#ifdef ENABLE_DOT1
        lldpd_vlan_cleanup(port);
+#endif
        free(port->p_id);
        free(port->p_descr);
        free(port);
@@ -359,6 +384,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);
@@ -387,6 +420,17 @@ lldpd_remote_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware, int res
        }
 }
 
+void
+lldpd_hardware_cleanup(struct lldpd_hardware *hardware)
+{
+#ifdef ENABLE_DOT1
+       lldpd_vlan_cleanup(&hardware->h_lport);
+#endif
+       free(hardware->h_proto_macs);
+       free(hardware->h_llastframe);
+       free(hardware);
+}
+
 void
 lldpd_cleanup(struct lldpd *cfg)
 {
@@ -399,11 +443,8 @@ lldpd_cleanup(struct lldpd *cfg)
                if (hardware->h_flags == 0) {
                        TAILQ_REMOVE(&cfg->g_hardware, hardware, h_entries);
                        lldpd_iface_close(cfg, hardware);
-                       lldpd_vlan_cleanup(&hardware->h_lport);
                        lldpd_remote_cleanup(cfg, hardware, 1);
-                       free(hardware->h_proto_macs);
-                       free(hardware->h_llastframe);
-                       free(hardware);
+                       lldpd_hardware_cleanup(hardware);
                } else if (hardware->h_rchassis != NULL) {
                        if (time(NULL) - hardware->h_rlastupdate >
                            hardware->h_rchassis->c_ttl) {
@@ -423,7 +464,7 @@ lldpd_cleanup(struct lldpd *cfg)
        }
 }
 
-struct lldpd_vif *
+static struct lldpd_vif *
 lldpd_port_add_vlan(struct lldpd *cfg, struct ifaddrs *ifa)
 {
        struct lldpd_vif *vif;
@@ -477,15 +518,21 @@ lldpd_port_add_vlan(struct lldpd *cfg, struct ifaddrs *ifa)
        return vif;
 }
 
-struct lldpd_hardware *
+static struct lldpd_hardware *
 lldpd_port_add(struct lldpd *cfg, struct ifaddrs *ifa)
 {
+#if defined (ENABLE_DOT1) || defined (ENABLE_DOT3)
        struct ifaddrs *oifap, *oifa;
+#endif
        struct lldpd_hardware *hardware;
        struct lldpd_port *port;
+#ifdef ENABLE_DOT1
        struct lldpd_vlan *vlan;
        struct vlan_ioctl_args ifv;
+#endif
+#ifdef ENABLE_DOT3
        struct ethtool_cmd ethc;
+#endif
        u_int8_t *lladdr;
 
        TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
@@ -501,9 +548,16 @@ lldpd_port_add(struct lldpd *cfg, struct ifaddrs *ifa)
                hardware->h_raw_real = -1;
                hardware->h_start_probe = 0;
                hardware->h_proto_macs = (u_int8_t*)calloc(cfg->g_multi+1, ETH_ALEN);
+#ifdef ENABLE_LLDPMED
+               hardware->h_lport.p_med_cap_enabled = LLDPMED_CAP_CAP;
+               if (!cfg->g_noinventory)
+                       hardware->h_lport.p_med_cap_enabled |= LLDPMED_CAP_IV;
+#endif
+#ifdef ENABLE_DOT1
                TAILQ_INIT(&hardware->h_lport.p_vlans);
        } else {
                lldpd_vlan_cleanup(&hardware->h_lport);
+#endif
        }
 
        port = &hardware->h_lport;
@@ -512,8 +566,11 @@ lldpd_port_add(struct lldpd *cfg, struct ifaddrs *ifa)
        strlcpy(hardware->h_ifname, ifa->ifa_name, sizeof(hardware->h_ifname));
        lladdr = (u_int8_t*)(((struct sockaddr_ll *)ifa->ifa_addr)->sll_addr);
        memcpy(&hardware->h_lladdr, lladdr, sizeof(hardware->h_lladdr));
+       iface_get_permanent_mac(cfg, hardware);
        port->p_id_subtype = LLDP_PORTID_SUBTYPE_LLADDR;
-       port->p_id = (char*)hardware->h_lladdr;
+       if ((port->p_id = calloc(1, sizeof(hardware->h_lladdr))) == NULL)
+               fatal(NULL);
+       memcpy(port->p_id, hardware->h_lladdr, sizeof(hardware->h_lladdr));
        port->p_id_len = sizeof(hardware->h_lladdr);
        port->p_descr = hardware->h_ifname;
 
@@ -528,9 +585,11 @@ lldpd_port_add(struct lldpd *cfg, struct ifaddrs *ifa)
        }
 
        /* Get VLANS and aggregation status */
+#if defined (ENABLE_DOT3) || defined (ENABLE_DOT1)
        if (getifaddrs(&oifap) != 0)
                fatal("lldpd_port_add: failed to get interface list");
        for (oifa = oifap; oifa != NULL; oifa = oifa->ifa_next) {
+#ifdef ENABLE_DOT1
                /* Check if we already have checked this one */
                int skip = 0;
                TAILQ_FOREACH(vlan, &port->p_vlans, v_entries) {
@@ -538,22 +597,26 @@ lldpd_port_add(struct lldpd *cfg, struct ifaddrs *ifa)
                                skip = 1;
                }
                if (skip) continue;
+#endif
 
                /* Aggregation check */
-               if (iface_is_bond_slave(cfg, hardware->h_ifname, oifa->ifa_name))
+#ifdef ENABLE_DOT3
+               if (iface_is_bond_slave(cfg, hardware->h_ifname, oifa->ifa_name, NULL))
                        port->p_aggregid = if_nametoindex(oifa->ifa_name);
-               
+#endif
+
+#ifdef ENABLE_DOT1     
                /* VLAN check */
                memset(&ifv, 0, sizeof(ifv));
                ifv.cmd = GET_VLAN_REALDEV_NAME_CMD;
                strlcpy(ifv.device1, oifa->ifa_name, sizeof(ifv.device1));
                if ((ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) >= 0) &&
-                   ((iface_is_bond_slave(cfg, hardware->h_ifname, ifv.u.device2)) ||
+                   ((iface_is_bond_slave(cfg, hardware->h_ifname, ifv.u.device2, NULL)) ||
                     (strncmp(hardware->h_ifname, ifv.u.device2, sizeof(ifv.u.device2)) == 0))) {
                        if ((vlan = (struct lldpd_vlan *)
                             calloc(1, sizeof(struct lldpd_vlan))) == NULL)
                                continue;
-                       if (asprintf(&vlan->v_name, "%s", oifa->ifa_name) == -1) {
+                       if ((vlan->v_name = strdup(oifa->ifa_name)) == NULL) {
                                free(vlan);
                                continue;
                        }
@@ -569,9 +632,12 @@ lldpd_port_add(struct lldpd *cfg, struct ifaddrs *ifa)
                                TAILQ_INSERT_TAIL(&port->p_vlans, vlan, v_entries);
                        }
                }
+#endif
        }
        freeifaddrs(oifap);
+#endif
 
+#ifdef ENABLE_DOT3
        /* MAC/PHY */
        if (priv_ethtool(hardware->h_ifname, &ethc) == 0) {
                int j;
@@ -628,14 +694,13 @@ lldpd_port_add(struct lldpd *cfg, struct ifaddrs *ifa)
                if (ethc.port == PORT_AUI) port->p_mau_type = LLDP_DOT3_MAU_AUI;
        } else
                LLOG_DEBUG("unable to get eth info for %s", hardware->h_ifname);
+#endif
 
        if (!INTERFACE_OPENED(hardware)) {
 
                if (lldpd_iface_init(cfg, hardware) != 0) {
                        LLOG_WARN("unable to initialize %s", hardware->h_ifname);
-                       lldpd_vlan_cleanup(&hardware->h_lport);
-                       free(hardware->h_proto_macs);
-                       free(hardware);
+                       lldpd_hardware_cleanup(hardware);
                        return (NULL);
                }
 
@@ -645,7 +710,7 @@ lldpd_port_add(struct lldpd *cfg, struct ifaddrs *ifa)
        return (hardware);
 }
 
-int
+static int
 lldpd_guess_type(struct lldpd *cfg, char *frame, int s)
 {
        int i;
@@ -665,7 +730,7 @@ lldpd_guess_type(struct lldpd *cfg, char *frame, int s)
        return -1;
 }
 
-void
+static void
 lldpd_decode(struct lldpd *cfg, char *frame, int s,
     struct lldpd_hardware *hardware, int bond)
 {
@@ -913,7 +978,7 @@ cleanup:
        return;
 }
 
-void
+static void
 lldpd_recv_all(struct lldpd *cfg)
 {
        struct lldpd_hardware *hardware;
@@ -944,7 +1009,8 @@ lldpd_recv_all(struct lldpd *cfg)
                
                TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
                        /* Ignore if interface is down */
-                       if ((hardware->h_flags & IFF_UP) == 0)
+                       if (((hardware->h_flags & IFF_UP) == 0) ||
+                           ((hardware->h_flags & IFF_RUNNING) == 0))
                                continue;
                        FD_SET(hardware->h_raw, &rfds);
                        if (nfds < hardware->h_raw)
@@ -958,7 +1024,8 @@ lldpd_recv_all(struct lldpd *cfg)
                        }
                }
                TAILQ_FOREACH(vif, &cfg->g_vif, vif_entries) {
-                       if ((vif->vif_flags & IFF_UP) == 0)
+                       if (((vif->vif_flags & IFF_UP) == 0) ||
+                           ((vif->vif_flags & IFF_RUNNING) == 0))
                                continue;
                        FD_SET(vif->vif_raw, &rfds);
                        if (nfds < vif->vif_raw)
@@ -1122,17 +1189,30 @@ lldpd_recv_all(struct lldpd *cfg)
        } while ((rc != 0) || (time(NULL) - cfg->g_lastsent < cfg->g_delay));
 }
 
-void
+static void
 lldpd_send_all(struct lldpd *cfg)
 {
        struct lldpd_hardware *hardware;
-       int i;
+       u_int8_t saved_lladdr[ETHER_ADDR_LEN];
+       int i, altermac;
+
        cfg->g_lastsent = time(NULL);
        TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
                /* Ignore if interface is down */
-               if ((hardware->h_flags & IFF_UP) == 0)
+               if (((hardware->h_flags & IFF_UP) == 0) ||
+                   ((hardware->h_flags & IFF_RUNNING) == 0))
                        continue;
 
+               /* When sending on inactive slaves, just send using a 0:0:0:0:0:0 address */
+               altermac = 0;
+               if ((hardware->h_raw_real > 0) &&
+                   (!iface_is_slave_active(cfg, hardware->h_master,
+                       hardware->h_ifname))) {
+                       altermac = 1;
+                       memcpy(saved_lladdr, hardware->h_lladdr, ETHER_ADDR_LEN);
+                       memset(hardware->h_lladdr, 0, ETHER_ADDR_LEN);
+               }
+
                for (i=0; cfg->g_protocols[i].mode != 0; i++) {
                        if (!cfg->g_protocols[i].enabled)
                                continue;
@@ -1140,10 +1220,32 @@ lldpd_send_all(struct lldpd *cfg)
                            (cfg->g_protocols[i].mode == LLDPD_MODE_LLDP))
                                cfg->g_protocols[i].send(cfg, &cfg->g_lchassis, hardware);
                }
+               /* Restore MAC if needed */
+               if (altermac)
+                       memcpy(hardware->h_lladdr, saved_lladdr, ETHER_ADDR_LEN);
        }
 }
 
-void
+#ifdef ENABLE_LLDPMED
+static 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
+
+static void
 lldpd_loop(struct lldpd *cfg)
 {
        struct ifaddrs *ifap, *ifa;
@@ -1164,13 +1266,11 @@ lldpd_loop(struct lldpd *cfg)
                fatal("failed to get system name");
        free(cfg->g_lchassis.c_name);
        free(cfg->g_lchassis.c_descr);
-       if (asprintf(&cfg->g_lchassis.c_name, "%s",
-               hp) == -1)
-               fatal("failed to set system name");
+       if ((cfg->g_lchassis.c_name = strdup(hp)) == NULL)
+               fatal(NULL);
        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;
@@ -1180,6 +1280,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;
@@ -1265,7 +1373,7 @@ lldpd_loop(struct lldpd *cfg)
        lldpd_recv_all(cfg);
 }
 
-void
+static void
 lldpd_shutdown(int sig)
 {
        LLOG_INFO("signal received, exiting");
@@ -1273,9 +1381,9 @@ lldpd_shutdown(int sig)
 }
 
 /* For signal handling */
-struct lldpd *gcfg = NULL;
+static struct lldpd *gcfg = NULL;
 
-void
+static void
 lldpd_exit()
 {
        struct lldpd_hardware *hardware;
@@ -1300,10 +1408,16 @@ int
 main(int argc, char *argv[])
 {
        struct lldpd *cfg;
-       int ch, snmp = 0, debug = 0;
+       int ch, debug = 0;
+#ifdef USE_SNMP
+       int snmp = 0;
+#endif
        char *mgmtp = NULL;
-       char *popt, opts[] = "vdxm:p:@                    ";
+       char *popt, opts[] = "vdxm:p:M:i@                    ";
        int probe = 0, i, found, vlan = 0;
+#ifdef ENABLE_LLDPMED
+       int lldpmed = 0, noinventory = 0;
+#endif
 
        saved_argv = argv;
 
@@ -1327,11 +1441,35 @@ main(int argc, char *argv[])
                case 'm':
                        mgmtp = optarg;
                        break;
+#ifdef ENABLE_LLDPMED
+               case 'M':
+                       lldpmed = atoi(optarg);
+                       if ((lldpmed < 1) || (lldpmed > 4)) {
+                               fprintf(stderr, "-M requires an argument between 1 and 4\n");
+                               usage();
+                       }
+                       break;
+               case 'i':
+                       noinventory = 1;
+                       break;
+#else
+               case 'M':
+               case 'i':
+               case 'P':
+                       fprintf(stderr, "LLDP-MED support is not built-in\n");
+                       usage();
+                       break;
+#endif
                case 'p':
                        probe = atoi(optarg);
                        break;
                case 'x':
+#ifdef USE_SNMP
                        snmp = 1;
+#else
+                       fprintf(stderr, "SNMP support is not built-in\n");
+                       usage();
+#endif
                        break;
                default:
                        found = 0;
@@ -1346,39 +1484,26 @@ main(int argc, char *argv[])
                                usage();
                }
        }
-
+       
        log_init(debug);
 
-#ifdef USE_SNMP
-       if (NETSNMP_AGENTX_SOCKET[0] == '/') {
-               /* AgentX socket is a file, we need to mangle it to be able to chroot */
-               char *caxsocket;
-               char *chrootdir;
-               char *axsocket;
-
-               /* We chroot into the directory containing the socket. At this
-                * point of the program, no config file has been read. If the
-                * socket is not in the default directory, this won't work. */
-               caxsocket = strdup(NETSNMP_AGENTX_SOCKET);
-               chrootdir = strdup(dirname(caxsocket));
-               free(caxsocket);
-               priv_init(chrootdir);
-               free(chrootdir);
-
-               /* We mangle the name of the socket since it is in the current directory */
-               caxsocket = strdup(NETSNMP_AGENTX_SOCKET);
-               axsocket = strdup(basename(caxsocket));
-               free(caxsocket);
-               netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID,
-                   NETSNMP_DS_AGENT_X_SOCKET,
-                   axsocket);
-               free(axsocket);
-       } else
-               /* Let's suppose that we can chroot normally */
-               priv_init(PRIVSEP_CHROOT);
-#else
+       if (!debug) {
+               int pid;
+               char *spid;
+               if (daemon(0, 0) != 0)
+                       fatal("failed to detach daemon");
+               if ((pid = open(LLDPD_PID_FILE,
+                           O_TRUNC | O_CREAT | O_WRONLY, 0644)) == -1)
+                       fatal("unable to open pid file " LLDPD_PID_FILE);
+               if (asprintf(&spid, "%d\n", getpid()) == -1)
+                       fatal("unable to create pid file " LLDPD_PID_FILE);
+               if (write(pid, spid, strlen(spid)) == -1)
+                       fatal("unable to write pid file " LLDPD_PID_FILE);
+               free(spid);
+               close(pid);
+       }
+
        priv_init(PRIVSEP_CHROOT);
-#endif 
 
        if (probe == 0) probe = LLDPD_TTL;
 
@@ -1397,6 +1522,16 @@ 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_available = LLDPMED_CAP_CAP |
+                   LLDPMED_CAP_IV | LLDPMED_CAP_LOCATION;
+               cfg->g_noinventory = noinventory;
+       }
+#endif
 
        /* Set TTL */
        cfg->g_lchassis.c_ttl = LLDPD_TTL;
@@ -1427,9 +1562,6 @@ main(int argc, char *argv[])
        TAILQ_INIT(&cfg->g_clients);
 
        gcfg = cfg;
-       if (!debug) {
-               priv_fork();
-       }
        if (atexit(lldpd_exit) != 0) {
                close(cfg->g_ctl);
                priv_ctl_cleanup();