]> git.ipfire.org Git - thirdparty/lldpd.git/commitdiff
Add getifaddrs() replacement for system without it.
authorVincent Bernat <bernat@luffy.cx>
Wed, 8 Jul 2009 22:10:11 +0000 (00:10 +0200)
committerVincent Bernat <bernat@luffy.cx>
Wed, 8 Jul 2009 22:10:11 +0000 (00:10 +0200)
configure.ac
src/compat.h
src/getifaddrs.c [new file with mode: 0644]
src/interfaces.c
src/lldpd.h
tests/Makefile.am
tests/check_ifaddrs.c [new file with mode: 0644]

index d74bcd011ccd0ced03c98c0ac1eb6725bc9c4afd..c6b9fab7b5c343d1687b12d0e5a4c90ea4f077ad 100644 (file)
@@ -65,6 +65,7 @@ AC_CHECK_MEMBERS([netsnmp_tdomain.f_create_from_tstring_new],,,
 
 # Checks for library functions.
 AC_REPLACE_FUNCS([strlcpy])
+AC_REPLACE_FUNCS([getifaddrs])
 
 AC_PROG_GCC_TRADITIONAL
 
index ac649d30b4ce330e5a88d980aac4c547f68d1f95..1ef9e56dcf1c957a98fe30eebe282d01df0ade43 100644 (file)
@@ -112,3 +112,37 @@ struct {                                                           \
 #if !HAVE_DECL_ETHERTYPE_VLAN
 #define ETHERTYPE_VLAN 0x8100
 #endif
+
+#if !HAVE_GETIFADDRS
+struct ifaddrs {
+       struct ifaddrs  *ifa_next;    /* Next item in list */
+       char            *ifa_name;    /* Name of interface */
+       unsigned int     ifa_flags;   /* Flags from SIOCGIFFLAGS */
+       struct sockaddr *ifa_addr;    /* Address of interface */
+       struct sockaddr *ifa_netmask; /* Netmask of interface */
+       /* At most one of the following two is valid.  If the IFF_BROADCAST
+          bit is set in `ifa_flags', then `ifa_broadaddr' is valid.  If the
+          IFF_POINTOPOINT bit is set, then `ifa_dstaddr' is valid.
+          It is never the case that both these bits are set at once.  */
+       union {
+               struct sockaddr *ifu_broadaddr;
+               /* Broadcast address of interface */
+               struct sockaddr *ifu_dstaddr;
+               /* Point-to-point destination address */
+       } ifa_ifu;
+# ifndef ifa_broadaddr
+#  define              ifa_broadaddr ifa_ifu.ifu_broadaddr
+# endif
+# ifndef ifa_dstaddr
+#  define              ifa_dstaddr   ifa_ifu.ifu_dstaddr
+# endif
+       void            *ifa_data;    /* Address-specific data */
+};
+
+int getifaddrs(struct ifaddrs **ifap);
+void freeifaddrs(struct ifaddrs *ifa);
+#endif
+
+#if !HAVE_STRLCPY
+size_t strlcpy(char *, const char *, size_t);
+#endif
diff --git a/src/getifaddrs.c b/src/getifaddrs.c
new file mode 100644 (file)
index 0000000..80ecbeb
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "lldpd.h"
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+/* This implementation uses ioctl and not netlink. This should work with many
+ * earlier Linux. However, because we use an AF_INET socket, we only get IPv4
+ * addresses. Since lldpd only handles IPv4 for now, this is not a
+ * problem. Moreover, IPv6 + libc not having getifaddrs should be pretty
+ * rare. */
+int
+getifaddrs(struct ifaddrs **ifap)
+{
+       int sock, n, i;
+       struct ifconf ifc;
+       struct ifreq *ifr;
+       char buffer[8192];
+       struct ifaddrs *ifa = NULL, *lifa = NULL;
+
+       *ifap = NULL;
+       if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+               return -1;
+
+       ifc.ifc_len = sizeof(buffer);
+       ifc.ifc_buf = buffer;
+       if (ioctl(sock, SIOCGIFCONF, &ifc) == -1)
+               goto fault;
+       ifr = ifc.ifc_req;
+       n = ifc.ifc_len / sizeof(struct ifreq);
+       for (i = 0; i < n; i++) {
+               if (ioctl(sock, SIOCGIFFLAGS, &ifr[i]) == -1)
+                       goto fault;
+               ifa = (struct ifaddrs*)calloc(1, sizeof(struct ifaddrs));
+               if ((ifa->ifa_name = strdup(ifr[i].ifr_name)) == NULL)
+                       goto fault;
+               ifa->ifa_flags = ifr[i].ifr_flags;
+               /* Address */
+               if (ioctl(sock, SIOCGIFADDR, &ifr[i]) != -1) {
+                       if ((ifa->ifa_addr =
+                               (struct  sockaddr *)malloc(
+                                   sizeof(struct sockaddr_storage))) == NULL)
+                               goto fault;
+                       memcpy(ifa->ifa_addr, &ifr[i].ifr_addr,
+                           sizeof(struct sockaddr_storage));
+               }
+               /* Netmask */
+               if (ioctl(sock, SIOCGIFNETMASK, &ifr[i]) != -1) {
+                       if ((ifa->ifa_netmask =
+                               (struct sockaddr *)malloc(
+                                   sizeof(struct sockaddr_storage))) == NULL)
+                               goto fault;
+                       memcpy(ifa->ifa_netmask, &ifr[i].ifr_addr,
+                           sizeof(struct sockaddr_storage));
+               }
+               /* Broadcast or point to point */
+               if (ifr[i].ifr_flags & IFF_BROADCAST) {
+                       if (ioctl(sock, SIOCGIFBRDADDR, &ifr[i]) != -1) {
+                               if ((ifa->ifa_ifu.ifu_broadaddr =
+                                       (struct sockaddr *)malloc(
+                                               sizeof(struct sockaddr_storage))) == NULL)
+                                       goto fault;
+                               memcpy(ifa->ifa_ifu.ifu_broadaddr,
+                                   &ifr[i].ifr_addr,
+                                   sizeof(struct sockaddr_storage));
+                       }
+               } else if (ifr[i].ifr_flags & IFF_POINTOPOINT) {
+                       if (ioctl(sock, SIOCGIFDSTADDR, &ifr[i]) != -1) {
+                               if ((ifa->ifa_ifu.ifu_dstaddr =
+                                       (struct sockaddr *)malloc(
+                                               sizeof(struct sockaddr_storage))) == NULL)
+                                       goto fault;
+                               memcpy(ifa->ifa_ifu.ifu_dstaddr,
+                                   &ifr[i].ifr_addr,
+                                   sizeof(struct sockaddr_storage));
+                       }
+               }
+               /* Link them together */
+               if (lifa)
+                       lifa->ifa_next = ifa;
+               else
+                       *ifap = ifa;
+               lifa = ifa;
+               ifa = NULL;
+       }
+       return 0;
+fault:
+       freeifaddrs(ifa);       /* It is not linked at anything if not NULL */
+       freeifaddrs(*ifap);
+       close(sock);
+       return -1;
+}
+
+void
+freeifaddrs(struct ifaddrs *ifa)
+{
+       struct ifaddrs *pifa;
+       while (ifa) {
+               pifa = ifa;
+               ifa = ifa->ifa_next;
+               free(pifa->ifa_name);
+               free(pifa->ifa_netmask);
+               free(pifa->ifa_addr);
+               if (pifa->ifa_flags & IFF_BROADCAST)
+                       free(pifa->ifa_ifu.ifu_broadaddr);
+               else if (pifa->ifa_flags & IFF_POINTOPOINT)
+                       free(pifa->ifa_ifu.ifu_dstaddr);
+               free(pifa->ifa_data);
+               free(pifa);
+       }
+}
index 046aa03aca86b9629a815524c7d7b61d46ba9fba..99dc2990e5f82a6c3d82e579df5e40e58a19cc4e 100644 (file)
@@ -26,7 +26,6 @@
 #include <fcntl.h>
 #include <fnmatch.h>
 #include <arpa/inet.h>
-#include <ifaddrs.h>
 #include <net/if_arp.h>
 #include <linux/if_vlan.h>
 #include <linux/if_bonding.h>
index 796786c4720b42e078333ebed6f48b5ae5c3b016..0f77d00d5f536cbe20640762c25cf5a1113bd679 100644 (file)
 #include <net/if.h>
 #else
 #include <arpa/inet.h>
+#include <sys/types.h>
 #include <linux/if.h>
 #endif
+#if HAVE_GETIFADDRS
 #include <ifaddrs.h>
+#endif
 #include <net/ethernet.h>
 #include <netinet/in.h>
 #include <linux/ethtool.h>
@@ -430,9 +433,6 @@ void                 agent_init(struct lldpd *, int);
 /* agent_priv.c */
 void            agent_priv_register_domain();
 
-/* strlcpy.c */
-size_t strlcpy(char *, const char *, size_t);
-
 /* client.c */
 struct client_handle {
        enum hmsg_type type;
index 5b14d1e1cd9108856ed6193e5a71a426040eb1d9..683dfe95e611f627f72c46a5b253e4a983a782c2 100644 (file)
@@ -1,4 +1,4 @@
-TESTS = check_pack check_lldp check_cdp check_sonmp check_edp
+TESTS = check_pack check_lldp check_cdp check_sonmp check_edp check_ifaddrs
 
 if HAVE_CHECK
 
@@ -33,14 +33,22 @@ check_edp_SOURCES = check_edp.c \
 check_edp_CFLAGS = @CHECK_CFLAGS@
 check_edp_LDADD = $(top_builddir)/src/liblldpd.la @CHECK_LIBS@
 
+check_ifaddrs_SOURCES = check_ifaddrs.c \
+       $(top_builddir)/src/lldpd.h \
+       $(top_builddir)/src/getifaddrs.c
+check_ifaddrs_CFLAGS = @CHECK_CFLAGS@
+check_ifaddrs_LDADD = $(top_builddir)/src/liblldpd.la @CHECK_LIBS@
+
 if USE_SNMP
 check_pack_LDADD += @NETSNMP_LIB@
 check_lldp_LDADD += @NETSNMP_LIB@
 check_cdp_LDADD += @NETSNMP_LIB@
 check_sonmp_LDADD += @NETSNMP_LIB@
 check_edp_LDADD += @NETSNMP_LIB@
+check_ifaddrs_LDADD += @NETSNMP_LIB@
 endif
 
 endif
 
-MOSTLYCLEANFILES = lldp_send_*.pcap cdp_send_*.pcap sonmp_send_*.pcap edp_send_*.pcap
+MOSTLYCLEANFILES = lldp_send_*.pcap cdp_send_*.pcap sonmp_send_*.pcap edp_send_*.pcap \
+       ifdump.txt
diff --git a/tests/check_ifaddrs.c b/tests/check_ifaddrs.c
new file mode 100644 (file)
index 0000000..eb9feb7
--- /dev/null
@@ -0,0 +1,85 @@
+#include <stdio.h>
+#include <arpa/inet.h>
+#include <check.h>
+#include "../src/lldpd.h"
+
+#define DUMP "ifdump.txt"
+
+/* This is not a real test. It should dump into a file the list of interfaces */
+
+static const char *
+addr_string (struct sockaddr *sa) {
+       static char buf[64];
+       if (sa == NULL)
+               return "<0000>";
+       switch (sa->sa_family) {
+       case AF_INET:
+               return inet_ntop(AF_INET,
+                   &((struct sockaddr_in *)sa)->sin_addr,
+                   buf, sizeof(buf));
+       case AF_INET6:
+               return "<ipv6>";
+       case AF_UNSPEC:
+               return "<---->";
+       case AF_PACKET:
+               return "<pckt>";
+       default:
+               snprintf(buf, 64, "<%4d>", sa->sa_family);
+       }
+       return buf;
+}
+
+START_TEST (test_ifaddrs)
+{
+       struct ifaddrs *ifap, *ifa;
+       FILE* dump;
+
+       if (getifaddrs(&ifap) < 0) {
+               fail("unable to get interface list");
+               return;
+       }
+       dump = fopen(DUMP, "w+");
+       if (dump == NULL) {
+               fail("unable to open dump file " DUMP);
+               return;
+       }
+       fprintf(dump,
+           "Name           Flags   Address         Netmask         Broadcast/Destination\n");
+       for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+               fprintf(dump, "%-15s%#.4x  ",
+                   ifa->ifa_name, ifa->ifa_flags);
+               fprintf(dump, "%-15s ",
+                   addr_string(ifa->ifa_addr));
+               fprintf(dump, "%-15s ",
+                   addr_string(ifa->ifa_netmask));
+               fprintf(dump, "%-15s\n",
+                   addr_string(ifa->ifa_broadaddr));
+       }
+       fclose(dump);
+}
+END_TEST
+
+Suite *
+ifaddrs_suite(void)
+{
+       Suite *s = suite_create("getifaddrs");
+
+       /* Single objects packing/unpacking */
+       TCase *tc_core = tcase_create("getifaddrs");
+       tcase_add_test(tc_core, test_ifaddrs);
+       suite_add_tcase(s, tc_core);
+
+       return s;
+}
+
+int
+main()
+{
+       int number_failed;
+       Suite *s = ifaddrs_suite ();
+       SRunner *sr = srunner_create (s);
+       srunner_run_all (sr, CK_ENV);
+       number_failed = srunner_ntests_failed (sr);
+       srunner_free (sr);
+       return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}