]> git.ipfire.org Git - thirdparty/lldpd.git/blobdiff - src/lldpd.c
Add a summary of available options in usage() of lldpd and lldpctl
[thirdparty/lldpd.git] / src / lldpd.c
index b12138c5a883eaf48f14d8012a4edb8fddb33731..ae85a5bdc19500ccefe6ce30bf7ee6ba9f18701c 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>
@@ -73,7 +77,7 @@ static void            lldpd_update_localports(struct lldpd *);
 static void             lldpd_cleanup(struct lldpd *);
 static void             lldpd_loop(struct lldpd *);
 static void             lldpd_shutdown(int);
-static void             lldpd_exit();
+static void             lldpd_exit(void);
 static void             lldpd_send_all(struct lldpd *);
 static void             lldpd_recv_all(struct lldpd *);
 static int              lldpd_guess_type(struct lldpd *, char *, int);
@@ -86,22 +90,53 @@ static void          lldpd_med(struct lldpd_chassis *);
 #endif
 
 static char            **saved_argv;
+#ifdef HAVE___PROGNAME
+extern const char      *__progname;
+#else
+# define __progname "lldpd"
+#endif
 
 static void
 usage(void)
 {
-       extern const char       *__progname;
-       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");
+       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");
+       fprintf(stderr, "-x       Enable SNMP subagent.\n");
+#ifdef ENABLE_LISTENVLAN
+       fprintf(stderr, "-v       Listen on VLAN as well.\n");
+#endif
+       fprintf(stderr, "\n");
+
+       fprintf(stderr, "Protocol support. (Disabled by default)\n");
+       fprintf(stderr, "-c       Enable the support of CDP protocol. (Cisco)\n");
+       fprintf(stderr, "-e       Enable the support of EDP protocol. (Extreme)\n");
+       fprintf(stderr, "-f       Enable the support of FDP protocol. (Foundry)\n");
+       fprintf(stderr, "-s       Enable the support of SONMP protocol. (Nortel)\n");
+
+       fprintf(stderr, "\n");
+
        fprintf(stderr, "see manual page lldpd(8) for more information\n");
        exit(1);
 }
 
 struct lldpd_hardware *
-lldpd_get_hardware(struct lldpd *cfg, char *name, struct lldpd_ops *ops)
+lldpd_get_hardware(struct lldpd *cfg, char *name, int index, struct lldpd_ops *ops)
 {
        struct lldpd_hardware *hardware;
        TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
                if ((strcmp(hardware->h_ifname, name) == 0) &&
+                   (hardware->h_ifindex == index) &&
                    ((!ops) || (ops == hardware->h_ops)))
                        break;
        }
@@ -119,6 +154,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
@@ -153,7 +189,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;
@@ -168,8 +204,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;
+               }
        }
 }
 
@@ -209,7 +247,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);
                }
        }
@@ -219,7 +257,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. */
@@ -227,7 +265,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);
@@ -239,6 +277,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) {
@@ -250,6 +289,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
@@ -343,7 +391,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) {
@@ -353,7 +401,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++;
@@ -369,6 +416,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);
@@ -395,12 +455,42 @@ lldpd_update_chassis(struct lldpd_chassis *ochassis,
        memcpy(&ochassis->c_entries, &entries, sizeof(entries));
 }
 
+int
+lldpd_callback_add(struct lldpd *cfg, int fd, void(*fn)(CALLBACK_SIG), void *data)
+{
+       struct lldpd_callback *callback;
+       if ((callback = (struct lldpd_callback *)
+               malloc(sizeof(struct lldpd_callback))) == NULL)
+               return -1;
+       callback->fd = fd;
+       callback->function = fn;
+       callback->data = data;
+       TAILQ_INSERT_TAIL(&cfg->g_callbacks, callback, next);
+       return 0;
+}
+
+void
+lldpd_callback_del(struct lldpd *cfg, int fd, void(*fn)(CALLBACK_SIG))
+{
+       struct lldpd_callback *callback, *callback_next;
+       for (callback = TAILQ_FIRST(&cfg->g_callbacks);
+            callback;
+            callback = callback_next) {
+               callback_next = TAILQ_NEXT(callback, next);
+               if ((callback->fd == fd) &&
+                   (callback->function = fn)) {
+                       free(callback->data);
+                       TAILQ_REMOVE(&cfg->g_callbacks, callback, next);
+                       free(callback);
+               }
+       }
+}
 
 static void
 lldpd_recv_all(struct lldpd *cfg)
 {
        struct lldpd_hardware *hardware;
-       struct lldpd_client *client, *client_next;
+       struct lldpd_callback *callback, *callback_next;
        fd_set rfds;
        struct timeval tv;
 #ifdef USE_SNMP
@@ -427,21 +517,18 @@ 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)
                                                nfds = n;
                                }
                }
-               TAILQ_FOREACH(client, &cfg->g_clients, next) {
-                       FD_SET(client->fd, &rfds);
-                       if (nfds < client->fd)
-                               nfds = client->fd;
+               TAILQ_FOREACH(callback, &cfg->g_callbacks, next) {
+                       FD_SET(callback->fd, &rfds);
+                       if (nfds < callback->fd)
+                               nfds = callback->fd;
                }
-               FD_SET(cfg->g_ctl, &rfds);
-               if (nfds < cfg->g_ctl)
-                       nfds = cfg->g_ctl;
                
 #ifdef USE_SNMP
                if (cfg->g_snmp)
@@ -468,10 +555,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");
@@ -487,33 +574,13 @@ lldpd_recv_all(struct lldpd *cfg)
                        free(buffer);
                        break;
                }
-               if (FD_ISSET(cfg->g_ctl, &rfds)) {
-                       if (ctl_accept(cfg, cfg->g_ctl) == -1)
-                               LLOG_WARN("unable to accept new client");
-               }
-               for (client = TAILQ_FIRST(&cfg->g_clients);
-                    client != NULL;
-                    client = client_next) {
-                       client_next = TAILQ_NEXT(client, next);
-                       if (FD_ISSET(client->fd, &rfds)) {
-                               /* Got a message */
-                               if ((buffer = (char *)malloc(MAX_HMSGSIZE)) ==
-                                   NULL) {
-                                       LLOG_WARN("failed to alloc reception buffer");
-                                       continue;
-                               }
-                               if ((n = recv(client->fd, buffer,
-                                           MAX_HMSGSIZE, 0)) == -1) {
-                                       LLOG_WARN("error while receiving message");
-                                       free(buffer);
-                                       continue;
-                               }
-                               if (n > 0)
-                                       client_handle_client(cfg, client, buffer, n);
-                               else
-                                       ctl_close(cfg, client->fd); /* Will use TAILQ_REMOVE ! */
-                               free(buffer);
-                       }
+               for (callback = TAILQ_FIRST(&cfg->g_callbacks);
+                    callback;
+                    callback = callback_next) {
+                       /* Callback function can use TAILQ_REMOVE */
+                       callback_next = TAILQ_NEXT(callback, next);
+                       if (FD_ISSET(callback->fd, &rfds))
+                               callback->function(cfg, callback);
                }
 
 #ifdef USE_SNMP
@@ -530,7 +597,7 @@ lldpd_send_all(struct lldpd *cfg)
 {
        struct lldpd_hardware *hardware;
        struct lldpd_port *port;
-       int i, sent = 0;
+       int i, sent;
 
        cfg->g_lastsent = time(NULL);
        TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
@@ -538,6 +605,7 @@ lldpd_send_all(struct lldpd *cfg)
                if ((hardware->h_flags & IFF_RUNNING) == 0)
                        continue;
 
+               sent = 0;
                for (i=0; cfg->g_protocols[i].mode != 0; i++) {
                        if (!cfg->g_protocols[i].enabled)
                                continue;
@@ -598,9 +666,14 @@ lldpd_update_localchassis(struct lldpd *cfg)
        free(LOCAL_CHASSIS(cfg)->c_descr);
        if ((LOCAL_CHASSIS(cfg)->c_name = strdup(hp)) == NULL)
                fatal(NULL);
-       if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s %s %s %s",
-               un.sysname, un.release, un.version, un.machine) == -1)
-               fatal("failed to set system description");
+       if (cfg->g_advertise_version) {
+               if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s %s %s %s",
+                       un.sysname, un.release, un.version, un.machine) == -1)
+                       fatal("failed to set full system description");
+       } else {
+               if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s", un.sysname) == -1)
+                       fatal("failed to set minimal system description");
+       }
 
        /* Check forwarding */
        if ((f = priv_open("/proc/sys/net/ipv4/ip_forward")) >= 0) {
@@ -614,7 +687,10 @@ lldpd_update_localchassis(struct lldpd *cfg)
                LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_TELEPHONE;
        lldpd_med(LOCAL_CHASSIS(cfg));
        free(LOCAL_CHASSIS(cfg)->c_med_sw);
-       LOCAL_CHASSIS(cfg)->c_med_sw = strdup(un.release);
+       if (cfg->g_advertise_version)
+               LOCAL_CHASSIS(cfg)->c_med_sw = strdup(un.release);
+       else
+               LOCAL_CHASSIS(cfg)->c_med_sw = strdup("Unknown");
 #endif
 
        /* Set chassis ID if needed */
@@ -638,7 +714,9 @@ lldpd_update_localports(struct lldpd *cfg)
        lldpd_ifhandlers ifhs[] = {
                lldpd_ifh_bond, /* Handle bond */
                lldpd_ifh_eth,  /* Handle classic ethernet interfaces */
+#ifdef ENABLE_DOT1
                lldpd_ifh_vlan, /* Handle VLAN */
+#endif
                lldpd_ifh_mgmt, /* Handle management address (if not already handled) */
                NULL
        };
@@ -714,17 +792,25 @@ lldpd_exit()
 }
 
 int
-main(int argc, char *argv[])
+lldpd_main(int argc, char *argv[])
 {
        struct lldpd *cfg;
        struct lldpd_chassis *lchassis;
        int ch, debug = 0;
 #ifdef USE_SNMP
        int snmp = 0;
+       char *agentx = NULL;    /* AgentX socket */
 #endif
        char *mgmtp = NULL;
-       char *popt, opts[] = "dxm:p:M:i@                    ";
-       int i, found;
+       char *popt, opts[] = 
+#ifdef ENABLE_LISTENVLAN
+               "v"
+#endif
+               "kdxX:m:p:M:i@                    ";
+       int i, found, advertise_version = 1;
+#ifdef ENABLE_LISTENVLAN
+       int vlan = 0;
+#endif
 #ifdef ENABLE_LLDPMED
        int lldpmed = 0, noinventory = 0;
 #endif
@@ -734,7 +820,7 @@ main(int argc, char *argv[])
        /*
         * Get and parse command line options
         */
-       popt = index(opts, '@');
+       popt = strchr(opts, '@');
        for (i=0; protos[i].mode != 0; i++) {
                if (protos[i].enabled == 1) continue;
                *(popt++) = protos[i].arg;
@@ -742,12 +828,20 @@ main(int argc, char *argv[])
        *popt = '\0';
        while ((ch = getopt(argc, argv, opts)) != -1) {
                switch (ch) {
+#ifdef ENABLE_LISTENVLAN
+               case 'v':
+                       vlan = 1;
+                       break;
+#endif
                case 'd':
                        debug++;
                        break;
                case 'm':
                        mgmtp = optarg;
                        break;
+               case 'k':
+                       advertise_version = 0;
+                       break;
 #ifdef ENABLE_LLDPMED
                case 'M':
                        lldpmed = atoi(optarg);
@@ -767,10 +861,17 @@ 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
@@ -789,7 +890,8 @@ main(int argc, char *argv[])
                }
        }
        
-       log_init(debug);
+       log_init(debug, __progname);
+       tzset();                /* Get timezone info before chroot */
 
        if (!debug) {
                int pid;
@@ -814,6 +916,10 @@ main(int argc, char *argv[])
                fatal(NULL);
 
        cfg->g_mgmt_pattern = mgmtp;
+       cfg->g_advertise_version = advertise_version;
+#ifdef ENABLE_LISTENVLAN
+       cfg->g_listen_vlans = vlan;
+#endif
 
        /* Get ioctl socket */
        if ((cfg->g_sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
@@ -851,19 +957,22 @@ main(int argc, char *argv[])
        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 */
 
        /* Create socket */
-       if ((cfg->g_ctl = priv_ctl_create(cfg)) == -1)
+       if ((cfg->g_ctl = priv_ctl_create()) == -1)
                fatalx("unable to create control socket " LLDPD_CTL_SOCKET);
-       TAILQ_INIT(&cfg->g_clients);
+       if (lldpd_callback_add(cfg, cfg->g_ctl, ctl_accept, NULL) != 0)
+               fatalx("unable to add callback for control socket");
 
        gcfg = cfg;
        if (atexit(lldpd_exit) != 0) {