]> git.ipfire.org Git - thirdparty/lldpd.git/blobdiff - src/lldpd.c
Allow to force a protocol.
[thirdparty/lldpd.git] / src / lldpd.c
index 14b07d2da77a3453b2648b0f36921680a8afcc8e..05a840eff1a8b9ef8e9706c6616b71f16314c9d3 100644 (file)
 #include <arpa/inet.h>
 #include <net/if_arp.h>
 
+#if LLDPD_FD_SETSIZE != FD_SETSIZE
+# warning "FD_SETSIZE is set to an inconsistent value."
+#endif
+
 #ifdef USE_SNMP
 #include <net-snmp/net-snmp-config.h>
 #include <net-snmp/net-snmp-includes.h>
@@ -44,7 +48,7 @@ static void            usage(void);
 
 static struct protocol protos[] =
 {
-       { LLDPD_MODE_LLDP, 1, "LLDP", ' ', lldp_send, lldp_decode, NULL,
+       { LLDPD_MODE_LLDP, 1, "LLDP", 'l', lldp_send, lldp_decode, NULL,
          LLDP_MULTICAST_ADDR },
 #ifdef ENABLE_CDP
        { LLDPD_MODE_CDPV1, 0, "CDPv1", 'c', cdpv1_send, cdp_decode, cdpv1_guess,
@@ -95,7 +99,47 @@ extern const char    *__progname;
 static void
 usage(void)
 {
-       fprintf(stderr, "usage: %s [options]\n", __progname);
+       fprintf(stderr, "Usage: %s [OPTIONS ...]\n", __progname);
+
+       fprintf(stderr, "\n");
+
+       fprintf(stderr, "-d       Do not daemonize.\n");
+       fprintf(stderr, "-i       Disable LLDP-MED inventory TLV transmission.\n");
+       fprintf(stderr, "-k       Disable advertising of kernel release, version, machine.\n");
+       fprintf(stderr, "-m IP    Specify the management address of this system.\n");
+#ifdef ENABLE_LLDPMED
+       fprintf(stderr, "-M class Enable emission of LLDP-MED frame. 'class' should be one of:\n");
+       fprintf(stderr, "             1 Generic Endpoint (Class I)\n");
+       fprintf(stderr, "             2 Media Endpoint (Class II)\n");
+       fprintf(stderr, "             3 Communication Device Endpoints (Class III)\n");
+       fprintf(stderr, "             4 Network Connectivity Device\n");
+#endif
+#ifdef USE_SNMP
+       fprintf(stderr, "-x       Enable SNMP subagent.\n");
+#endif
+#ifdef ENABLE_LISTENVLAN
+       fprintf(stderr, "-v       Listen on VLAN as well.\n");
+#endif
+       fprintf(stderr, "\n");
+
+#if defined ENABLE_CDP || defined ENABLE_EDP || defined ENABLE_FDP || defined ENABLE_SONMP
+       fprintf(stderr, "Additional protocol support.\n");
+#ifdef ENABLE_CDP
+       fprintf(stderr, "-c       Enable the support of CDP protocol. (Cisco)\n");
+#endif
+#ifdef ENABLE_EDP
+       fprintf(stderr, "-e       Enable the support of EDP protocol. (Extreme)\n");
+#endif
+#ifdef ENABLE_FDP
+       fprintf(stderr, "-f       Enable the support of FDP protocol. (Foundry)\n");
+#endif
+#ifdef ENABLE_SONMP
+       fprintf(stderr, "-s       Enable the support of SONMP protocol. (Nortel)\n");
+#endif
+
+       fprintf(stderr, "\n");
+#endif
+
        fprintf(stderr, "see manual page lldpd(8) for more information\n");
        exit(1);
 }
@@ -124,6 +168,7 @@ lldpd_alloc_hardware(struct lldpd *cfg, char *name)
 
        strlcpy(hardware->h_ifname, name, sizeof(hardware->h_ifname));
        hardware->h_lport.p_chassis = LOCAL_CHASSIS(cfg);
+       hardware->h_lport.p_chassis->c_refcount++;
        TAILQ_INIT(&hardware->h_rports);
 
 #ifdef ENABLE_LLDPMED
@@ -158,7 +203,7 @@ lldpd_vlan_cleanup(struct lldpd_port *port)
 /* If `all' is true, clear all information, including information that
    are not refreshed periodically. Port should be freed manually. */
 void
-lldpd_port_cleanup(struct lldpd_port *port, int all)
+lldpd_port_cleanup(struct lldpd *cfg, struct lldpd_port *port, int all)
 {
 #ifdef ENABLE_LLDPMED
        int i;
@@ -173,8 +218,10 @@ lldpd_port_cleanup(struct lldpd_port *port, int all)
        free(port->p_descr);
        if (all) {
                free(port->p_lastframe);
-               if (port->p_chassis) /* chassis may not have been attributed, yet */
+               if (port->p_chassis) /* chassis may not have been attributed, yet */
                        port->p_chassis->c_refcount--;
+                       port->p_chassis = NULL;
+               }
        }
 }
 
@@ -214,7 +261,7 @@ lldpd_remote_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware, int all
                }
                if (del) {
                        TAILQ_REMOVE(&hardware->h_rports, port, p_entries);
-                       lldpd_port_cleanup(port, 1);
+                       lldpd_port_cleanup(cfg, port, 1);
                        free(port);
                }
        }
@@ -224,7 +271,7 @@ void
 lldpd_hardware_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware)
 {
        int i;
-       lldpd_port_cleanup(&hardware->h_lport, 1);
+       lldpd_port_cleanup(cfg, &hardware->h_lport, 1);
        /* If we have a dedicated cleanup function, use it. Otherwise,
           we just free the hardware-dependent data and close all FD
           in h_recvfds and h_sendfd. */
@@ -232,7 +279,7 @@ lldpd_hardware_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware)
                hardware->h_ops->cleanup(cfg, hardware);
        else {
                free(hardware->h_data);
-               for (i=0; i < FD_SETSIZE; i++)
+               for (i=0; i < LLDPD_FD_SETSIZE; i++)
                        if (FD_ISSET(i, &hardware->h_recvfds))
                                close(i);
                if (hardware->h_sendfd) close(hardware->h_sendfd);
@@ -244,6 +291,7 @@ static void
 lldpd_cleanup(struct lldpd *cfg)
 {
        struct lldpd_hardware *hardware, *hardware_next;
+       struct lldpd_chassis *chassis, *chassis_next;
 
        for (hardware = TAILQ_FIRST(&cfg->g_hardware); hardware != NULL;
             hardware = hardware_next) {
@@ -255,6 +303,15 @@ lldpd_cleanup(struct lldpd *cfg)
                } else
                        lldpd_remote_cleanup(cfg, hardware, 0);
        }
+
+       for (chassis = TAILQ_FIRST(&cfg->g_chassis); chassis;
+            chassis = chassis_next) {
+               chassis_next = TAILQ_NEXT(chassis, c_entries);
+               if (chassis->c_refcount == 0) {
+                       TAILQ_REMOVE(&cfg->g_chassis, chassis, c_entries);
+                       lldpd_chassis_cleanup(chassis, 1);
+               }
+       }
 }
 
 static int
@@ -348,7 +405,7 @@ lldpd_decode(struct lldpd *cfg, char *frame, int s,
        if (oport) {
                /* The port is known, remove it before adding it back */
                TAILQ_REMOVE(&hardware->h_rports, oport, p_entries);
-               lldpd_port_cleanup(oport, 1);
+               lldpd_port_cleanup(cfg, oport, 1);
                free(oport);
        }
        if (ochassis) {
@@ -358,7 +415,6 @@ lldpd_decode(struct lldpd *cfg, char *frame, int s,
        } else {
                /* Chassis not known, add it */
                chassis->c_index = ++cfg->g_lastrid;
-               port->p_chassis = chassis;
                chassis->c_refcount = 0;
                TAILQ_INSERT_TAIL(&cfg->g_chassis, chassis, c_entries);
                i = 0; TAILQ_FOREACH(ochassis, &cfg->g_chassis, c_entries) i++;
@@ -374,6 +430,19 @@ lldpd_decode(struct lldpd *cfg, char *frame, int s,
        TAILQ_INSERT_TAIL(&hardware->h_rports, port, p_entries);
        port->p_chassis = chassis;
        port->p_chassis->c_refcount++;
+       /* Several cases are possible :
+            1. chassis is new, its refcount was 0. It is now attached
+               to this port, its refcount is 1.
+            2. chassis already exists and was attached to another
+               port, we increase its refcount accordingly.
+            3. chassis already exists and was attached to the same
+               port, its refcount was decreased with
+               lldpd_port_cleanup() and is now increased again.
+
+          In all cases, if the port already existed, it has been
+          freed with lldpd_port_cleanup() and therefore, the refcount
+          of the chassis that was attached to it is decreased.
+       */
        i = 0; TAILQ_FOREACH(oport, &hardware->h_rports, p_entries) i++;
        LLOG_DEBUG("Currently, %s known %d neighbors",
            hardware->h_ifname, i);
@@ -462,7 +531,7 @@ lldpd_recv_all(struct lldpd *cfg)
                                continue;
                        /* This is quite expensive but we don't rely on internal
                         * structure of fd_set. */
-                       for (n = 0; n < FD_SETSIZE; n++)
+                       for (n = 0; n < LLDPD_FD_SETSIZE; n++)
                                if (FD_ISSET(n, &hardware->h_recvfds)) {
                                        FD_SET(n, &rfds);
                                        if (nfds < n)
@@ -500,10 +569,10 @@ lldpd_recv_all(struct lldpd *cfg)
                }
 #endif /* USE_SNMP */
                TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
-                       for (n = 0; n < FD_SETSIZE; n++)
+                       for (n = 0; n < LLDPD_FD_SETSIZE; n++)
                                if ((FD_ISSET(n, &hardware->h_recvfds)) &&
                                    (FD_ISSET(n, &rfds))) break;
-                       if (n == FD_SETSIZE) continue;
+                       if (n == LLDPD_FD_SETSIZE) continue;
                        if ((buffer = (char *)malloc(
                                        hardware->h_mtu)) == NULL) {
                                LLOG_WARN("failed to alloc reception buffer");
@@ -555,13 +624,18 @@ lldpd_send_all(struct lldpd *cfg)
                        if (!cfg->g_protocols[i].enabled)
                                continue;
                        /* We send only if we have at least one remote system
-                        * speaking this protocol */
+                        * speaking this protocol or if the protocol is forced */
+                       if (cfg->g_protocols[i].enabled > 1) {
+                               cfg->g_protocols[i].send(cfg, hardware);
+                               sent++;
+                               continue;
+                       }
                        TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
                                if (port->p_protocol ==
                                    cfg->g_protocols[i].mode) {
                                        cfg->g_protocols[i].send(cfg,
                                            hardware);
-                                       sent = 1;
+                                       sent++;
                                        break;
                                }
                        }
@@ -744,13 +818,14 @@ lldpd_main(int argc, char *argv[])
        int ch, debug = 0;
 #ifdef USE_SNMP
        int snmp = 0;
+       char *agentx = NULL;    /* AgentX socket */
 #endif
        char *mgmtp = NULL;
        char *popt, opts[] = 
 #ifdef ENABLE_LISTENVLAN
                "v"
 #endif
-               "kdxm:p:M:i@                    ";
+               "hkdxX:m:p:M:i@                    ";
        int i, found, advertise_version = 1;
 #ifdef ENABLE_LISTENVLAN
        int vlan = 0;
@@ -765,13 +840,14 @@ lldpd_main(int argc, char *argv[])
         * Get and parse command line options
         */
        popt = strchr(opts, '@');
-       for (i=0; protos[i].mode != 0; i++) {
-               if (protos[i].enabled == 1) continue;
+       for (i=0; protos[i].mode != 0; i++)
                *(popt++) = protos[i].arg;
-       }
        *popt = '\0';
        while ((ch = getopt(argc, argv, opts)) != -1) {
                switch (ch) {
+               case 'h':
+                       usage();
+                       break;
 #ifdef ENABLE_LISTENVLAN
                case 'v':
                        vlan = 1;
@@ -805,10 +881,17 @@ lldpd_main(int argc, char *argv[])
                        usage();
                        break;
 #endif
-               case 'x':
 #ifdef USE_SNMP
+               case 'x':
                        snmp = 1;
+                       break;
+               case 'X':
+                       snmp = 1;
+                       agentx = optarg;
+                       break;
 #else
+               case 'x':
+               case 'X':
                        fprintf(stderr, "SNMP support is not built-in\n");
                        usage();
 #endif
@@ -816,9 +899,13 @@ lldpd_main(int argc, char *argv[])
                default:
                        found = 0;
                        for (i=0; protos[i].mode != 0; i++) {
-                               if (protos[i].enabled) continue;
                                if (ch == protos[i].arg) {
-                                       protos[i].enabled = 1;
+                                       protos[i].enabled++;
+                                       /* When an argument enable
+                                          several protocols, only the
+                                          first one can be forced. */
+                                       if (found && protos[i].enabled > 1)
+                                               protos[i].enabled = 1;
                                        found = 1;
                                }
                        }
@@ -828,6 +915,7 @@ lldpd_main(int argc, char *argv[])
        }
        
        log_init(debug, __progname);
+       tzset();                /* Get timezone info before chroot */
 
        if (!debug) {
                int pid;
@@ -885,22 +973,24 @@ lldpd_main(int argc, char *argv[])
 
        cfg->g_protocols = protos;
        for (i=0; protos[i].mode != 0; i++)
-               if (protos[i].enabled) {
+               if (protos[i].enabled > 1)
+                       LLOG_INFO("protocol %s enabled and forced", protos[i].name);
+               else if (protos[i].enabled)
                        LLOG_INFO("protocol %s enabled", protos[i].name);
-               else
+               else
                        LLOG_INFO("protocol %s disabled", protos[i].name);
 
        TAILQ_INIT(&cfg->g_hardware);
        TAILQ_INIT(&cfg->g_chassis);
        TAILQ_INSERT_TAIL(&cfg->g_chassis, lchassis, c_entries);
-       lchassis->c_refcount++;
+       lchassis->c_refcount++; /* We should always keep a reference to local chassis */
 
        TAILQ_INIT(&cfg->g_callbacks);
 
 #ifdef USE_SNMP
        if (snmp) {
                cfg->g_snmp = 1;
-               agent_init(cfg, debug);
+               agent_init(cfg, agentx, debug);
        }
 #endif /* USE_SNMP */