From: Vincent Bernat Date: Wed, 8 Jul 2009 22:10:11 +0000 (+0200) Subject: Add getifaddrs() replacement for system without it. X-Git-Tag: 0.5.0~37^2~17 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b79975281ed69c7845c49c76336767c34433d1fe;p=thirdparty%2Flldpd.git Add getifaddrs() replacement for system without it. --- diff --git a/configure.ac b/configure.ac index d74bcd01..c6b9fab7 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/src/compat.h b/src/compat.h index ac649d30..1ef9e56d 100644 --- a/src/compat.h +++ b/src/compat.h @@ -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 index 00000000..80ecbeb1 --- /dev/null +++ b/src/getifaddrs.c @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2008 Vincent Bernat + * + * 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 +#include +#include + +/* 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); + } +} diff --git a/src/interfaces.c b/src/interfaces.c index 046aa03a..99dc2990 100644 --- a/src/interfaces.c +++ b/src/interfaces.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include diff --git a/src/lldpd.h b/src/lldpd.h index 796786c4..0f77d00d 100644 --- a/src/lldpd.h +++ b/src/lldpd.h @@ -29,9 +29,12 @@ #include #else #include +#include #include #endif +#if HAVE_GETIFADDRS #include +#endif #include #include #include @@ -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; diff --git a/tests/Makefile.am b/tests/Makefile.am index 5b14d1e1..683dfe95 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -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 index 00000000..eb9feb74 --- /dev/null +++ b/tests/check_ifaddrs.c @@ -0,0 +1,85 @@ +#include +#include +#include +#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 ""; + case AF_UNSPEC: + return "<---->"; + case AF_PACKET: + return ""; + 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; +}