Fix getifaddrs (XCC)
authorBarret Rhoden <brho@cs.berkeley.edu>
Thu, 4 May 2017 18:44:58 +0000 (14:44 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Thu, 4 May 2017 18:45:16 +0000 (14:45 -0400)
There were a bunch of problems.  First, there was a buffer overflow with
the etheraddr, which was being used for the pathname.  That always went
over.  Second, the for loop for freeing the ifaddrs was wrong, since it'd
access ifa->ifa_next after ifa was freed.

Next up, it wasn't reporting the ethernet address in a way that anyone knew
to look for, and it didn't support IP addresses.

All of that should work now, though I didn't test IPv6.

Rebuild glibc.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
tests/getifaddrs.c
tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/Makefile
tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/ifaddrs.c [deleted file]
user/iplib/ifaddrs.c [new file with mode: 0644]

index 6783386..e3982ae 100644 (file)
 #include <parlib/alarm.h>
 #include <ndblib/ndb.h>
 #include <ifaddrs.h>
+#include <netpacket/packet.h>
+#include <arpa/inet.h>
+
+static void print_eth(struct ifaddrs *ifa)
+{
+       struct sockaddr_ll *sa_ll = (struct sockaddr_ll*)ifa->ifa_addr;
+
+       printf("\tAddr: ");
+       for (int i = 0; i < sa_ll->sll_halen; i++) {
+               printf("%02x", sa_ll->sll_addr[i]);
+               if (i < sa_ll->sll_halen - 1)
+                       printf(":");
+       }
+       printf("\n");
+       printf("\tNIC %d\n", sa_ll->sll_ifindex);
+}
+
+static void print_inet(struct ifaddrs *ifa)
+{
+       struct sockaddr_in *sa_in = (struct sockaddr_in*)ifa->ifa_addr;
+       struct sockaddr_in *mask_in = (struct sockaddr_in*)ifa->ifa_netmask;
+       char buf[INET_ADDRSTRLEN];
+
+       printf("\tAddr: %s\n", inet_ntop(AF_INET, &sa_in->sin_addr, buf,
+                                        sizeof(buf)));
+       if (mask_in)
+               printf("\tMask: %s\n", inet_ntop(AF_INET, &mask_in->sin_addr, buf,
+                                                sizeof(buf)));
+}
+
+static void print_inet6(struct ifaddrs *ifa)
+{
+       struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6*)ifa->ifa_addr;
+       char buf[INET6_ADDRSTRLEN];
+
+       printf("\tAddr: %s\n", inet_ntop(AF_INET6, &sa_in6->sin6_addr, buf,
+                                        sizeof(buf)));
+}
 
 int main(int argc, char **argv)
 {
-       int i, naddr, o;
-       uint8_t *cp;
-       struct ifaddrs *ifa;
+       int family;
+       struct ifaddrs *ifaddrs, *ifa;
 
-       naddr = getifaddrs(&ifa);
+       if (getifaddrs(&ifaddrs) != 0) {
+               perror("getifaddr");
+               exit(-1);
+       }
 
-       for (naddr = 0; ifa; ifa = ifa->ifa_next, naddr++) {
+       for (ifa = ifaddrs; ifa; ifa = ifa->ifa_next) {
                printf("%s: ", ifa->ifa_name);
-               cp = ifa->ifa_data;
-               for (o = 0; o < 6; o++) {
-                       printf("%02x", cp[o]);
-                       if (o < 5)
-                               printf(":");
+               if (!ifa->ifa_addr) {
+                       printf("No addr\n");
+                       continue;
+               } else {
+                       printf("\n");
+               }
+               family = ifa->ifa_addr->sa_family;
+               printf("\tFamily: %s\n", (family == AF_PACKET) ? "AF_PACKET" :
+                                        (family == AF_INET) ? "AF_INET" :
+                                        (family == AF_INET6) ? "AF_INET6" :
+                                        "Unknown");
+               switch (family) {
+               case AF_PACKET:
+                       print_eth(ifa);
+                       break;
+               case AF_INET:
+                       print_inet(ifa);
+                       break;
+               case AF_INET6:
+                       print_inet6(ifa);
+                       break;
                }
-               printf("\n");
        }
-       printf("%d ifaddrs\n", naddr);
-       freeifaddrs(ifa);
+       freeifaddrs(ifaddrs);
 }
index 38cbb74..613bcaa 100644 (file)
@@ -27,6 +27,7 @@ sysdep_headers += sys/syscall.h sys/tls.h
 # lack the subdir, we need to include the C files and headers manually.
 ifeq ($(subdir),inet)
 sysdep_routines += inet_addr inet_ntop inet_pton
+sysdep_headers += netpacket/packet.h
 endif
 sysdep_headers += netdb.h resolv.h
 sysdep_headers += arpa/nameser.h arpa/nameser_compat.h sys/bitypes.h
diff --git a/tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/ifaddrs.c b/tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/ifaddrs.c
deleted file mode 100644 (file)
index dfc7a99..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-#define _LARGEFILE64_SOURCE /* needed to use lseek64 */
-
-#include <stdio.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <parlib/arch/arch.h>
-#include <unistd.h>
-#include <errno.h>
-#include <dirent.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <dirent.h>
-#include <arpa/inet.h>
-#include <sys/socket.h>
-#include <netdb.h>
-#include <ifaddrs.h>
-#include <ros/syscall.h>
-#include <ros/fs.h>
-
-int getifaddrs(struct ifaddrs **ifap)
-{
-       DIR *net;
-       struct dirent *d;
-       int addr;
-       /* 6 is known everywhere, defined nowhere. So is 6 * 2 */
-       char etheraddr[12];
-       struct ifaddrs *ifa = NULL;
-       uint8_t *v;
-       int i;
-
-       *ifap = NULL;
-
-       net = opendir("/net");
-       if (net == NULL) {
-               fprintf(stderr, "/net: %r");
-               return -1;
-       }
-
-       for (d = readdir(net); d; d = readdir(net)) {
-               /*
-                * For now we only do ethernet MACs.  It's easy to
-                * change later: just get rid of the following 2
-                * lines.
-                */
-               if (strncmp(d->d_name, "ether", 5))
-                       continue;
-               sprintf(etheraddr, "/net/%s/addr", d->d_name);
-               addr = open(etheraddr, O_RDONLY);
-               if (addr < 0)
-                       continue;
-               if (read(addr, etheraddr, sizeof(etheraddr)) < sizeof(etheraddr)) {
-                       fprintf(stderr, "Read addr from %s: %r", d->d_name);
-                       close(addr);
-                       continue;
-               }
-               /* getifaddrds is a stupid design as it only admits of
-                * one address per interface.  Don't even bother
-                * filling in ifa_{addr,netmask}. They're allowed to
-                * be NULL.  Broadaddr need be set IFF a bit is set
-                * in the flags field. We don't set either one.
-                */
-               if (!ifa) {
-                       ifa = calloc(sizeof(*ifa), 1);
-                       *ifap = ifa;
-               } else {
-                       ifa->ifa_next = calloc(sizeof(*ifa), 1);
-                       ifa = ifa->ifa_next;
-               }
-               ifa->ifa_name = strdup(d->d_name);
-               /*
-                * 6 is known everywhere, not defined anywhere.  oh
-                * yeah, another binary thing. 1976 all over again.
-                */
-               v = malloc(6);
-               for (i = 0; i < 6; i++)
-                       sscanf(&etheraddr[i*2], "%02x", &v[i]);
-               ifa->ifa_data = v;
-               close(addr);
-       }
-       closedir(net);
-       return 0;
-}
-libc_hidden_def(getifaddrs)
-
-void freeifaddrs(struct ifaddrs *ifa)
-{
-       for (; ifa; ifa = ifa->ifa_next)
-               free(ifa);
-}
-libc_hidden_def(freeifaddrs)
diff --git a/user/iplib/ifaddrs.c b/user/iplib/ifaddrs.c
new file mode 100644 (file)
index 0000000..09a159f
--- /dev/null
@@ -0,0 +1,163 @@
+#define _LARGEFILE64_SOURCE /* needed to use lseek64 */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <parlib/arch/arch.h>
+#include <unistd.h>
+#include <errno.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <dirent.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <ifaddrs.h>
+#include <ros/syscall.h>
+#include <ros/fs.h>
+#include <iplib/iplib.h>
+#include <netpacket/packet.h>
+
+/* Given a list of ifas (possibly null), prepend ifas for ethernet devices.
+ * Returns the new head of the list (also possibly null). */
+static struct ifaddrs *get_ether_addrs(struct ifaddrs *ifa)
+{
+       struct ifaddrs *new_ifa;
+       struct sockaddr_ll *sa_ll;
+       DIR *net;
+       struct dirent *d;
+       int addr_fd;
+       char path[MAX_PATH_LEN];
+       /* 6 is known everywhere, defined nowhere. So is 6 * 2 */
+       char etheraddr[12];
+       #define SIZE_OF_ETHER 5
+
+       net = opendir("/net");
+       if (net == NULL)
+               return ifa;
+
+       for (d = readdir(net); d; d = readdir(net)) {
+               if (strncmp(d->d_name, "ether", SIZE_OF_ETHER))
+                       continue;
+               snprintf(path, sizeof(path), "/net/%s/addr", d->d_name);
+               addr_fd = open(path, O_RDONLY);
+               if (addr_fd < 0)
+                       continue;
+               if (read(addr_fd, etheraddr, sizeof(etheraddr)) < sizeof(etheraddr)) {
+                       fprintf(stderr, "Read addr from %s: %r", d->d_name);
+                       close(addr_fd);
+                       continue;
+               }
+               /* getifaddrs is a stupid design as it only admits of
+                * one address per interface.  Don't even bother
+                * filling in ifa_{addr,netmask}. They're allowed to
+                * be NULL.  Broadaddr need be set IFF a bit is set
+                * in the flags field. We don't set either one.
+                */
+               new_ifa = calloc(sizeof(*ifa), 1);
+               new_ifa->ifa_next = ifa;
+               ifa = new_ifa;
+               ifa->ifa_name = strdup(d->d_name);
+               sa_ll = calloc(sizeof(struct sockaddr_ll), 1);
+               ifa->ifa_addr = (struct sockaddr*)sa_ll;
+               sa_ll->sll_family = AF_PACKET;
+               /* TODO: could set protocol and hatype, if we ever get the headers for
+                * the options.  Probably not worth it. */
+               sa_ll->sll_ifindex = atoi(&ifa->ifa_name[SIZE_OF_ETHER]);
+               sa_ll->sll_halen = 6;
+               for (int i = 0; i < 6; i++)
+                       sscanf(&etheraddr[i * 2], "%02x", &sa_ll->sll_addr[i]);
+               close(addr_fd);
+       }
+       closedir(net);
+       return ifa;
+}
+
+/* Given a list of ifas (possibly null), prepend an ifa for the given lifc. */
+static struct ifaddrs *get_lifc_addr(struct ifaddrs *ifa, struct iplifc *lifc)
+{
+       struct ifaddrs *new_ifa;
+       struct sockaddr_in *sa_in, *mask_in;
+       struct sockaddr_in6 *sa_in6, *mask_in6;
+
+       if (!ipcmp(lifc->ip, IPnoaddr))
+               return ifa;
+       new_ifa = calloc(sizeof(*ifa), 1);
+       new_ifa->ifa_next = ifa;
+       ifa = new_ifa;
+       ifa->ifa_name = NULL;
+       if (isv4(lifc->ip)) {
+               sa_in = calloc(sizeof(struct sockaddr_in), 1);
+               sa_in->sin_family = AF_INET;
+               ifa->ifa_addr = (struct sockaddr*)sa_in;
+               v6tov4((uint8_t*)&sa_in->sin_addr, lifc->ip);
+
+               mask_in = calloc(sizeof(struct sockaddr_in), 1);
+               mask_in->sin_family = AF_INET;
+               ifa->ifa_netmask = (struct sockaddr*)mask_in;
+               /* The V4 mask is the last four bytes of the full v6 mask */
+               memcpy((uint8_t*)&mask_in->sin_addr, &lifc->mask[12],
+                      sizeof(struct in_addr));
+       } else {
+               /* TODO: check this when we have a v6 system */
+               sa_in6 = calloc(sizeof(struct sockaddr_in6), 1);
+               sa_in6->sin6_family = AF_INET6;
+               ifa->ifa_addr = (struct sockaddr*)sa_in6;
+               memcpy(&sa_in6->sin6_addr, lifc->ip, sizeof(struct in6_addr));
+
+               mask_in6 = calloc(sizeof(struct sockaddr_in6), 1);
+               mask_in6->sin6_family = AF_INET6;
+               ifa->ifa_netmask = (struct sockaddr*)mask_in6;
+               memcpy((uint8_t*)&mask_in6->sin6_addr, lifc->mask,
+                      sizeof(struct in6_addr));
+       }
+       return ifa;
+}
+
+/* Given a list of ifas (possibly null), prepend ifas for inet devices.
+ * Returns the new head of the list (also possibly null). */
+static struct ifaddrs *get_inet_addrs(struct ifaddrs *ifa)
+{
+       struct ipifc *ifc, *ifc_i;
+       struct iplifc *lifc_i;
+
+       /* This gives us a list of ipifcs (actual interfaces), each of which has a
+        * list of lifcs (local interface, including the IP addr). */
+       ifc = readipifc(NULL, NULL, -1);
+       for (ifc_i = ifc; ifc_i; ifc_i = ifc_i->next) {
+               for (lifc_i = ifc_i->lifc; lifc_i; lifc_i = lifc_i->next)
+                       ifa = get_lifc_addr(ifa, lifc_i);
+       }
+       free_ipifc(ifc);
+       return ifa;
+}
+
+int getifaddrs(struct ifaddrs **ifap)
+{
+       struct ifaddrs *ifa = NULL;
+
+       *ifap = NULL;
+       ifa = get_ether_addrs(ifa);
+       ifa = get_inet_addrs(ifa);
+       *ifap = ifa;
+       return 0;
+}
+
+void freeifaddrs(struct ifaddrs *ifa)
+{
+       struct ifaddrs *next = ifa;
+
+       while (ifa) {
+               next = ifa->ifa_next;
+
+               free(ifa->ifa_name);
+               free(ifa->ifa_addr);
+               free(ifa->ifa_netmask);
+               free(ifa);
+
+               ifa = next;
+       }
+}