kconfig: use pkg-config for ncurses detection
[akaros.git] / user / iplib / ifaddrs.c
1 #define _LARGEFILE64_SOURCE /* needed to use lseek64 */
2
3 #include <stdio.h>
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <fcntl.h>
7 #include <parlib/arch/arch.h>
8 #include <unistd.h>
9 #include <errno.h>
10 #include <dirent.h>
11 #include <stdlib.h>
12 #include <string.h>
13
14 #include <dirent.h>
15 #include <arpa/inet.h>
16 #include <sys/socket.h>
17 #include <netdb.h>
18 #include <ifaddrs.h>
19 #include <ros/syscall.h>
20 #include <ros/fs.h>
21 #include <iplib/iplib.h>
22 #include <netpacket/packet.h>
23
24 /* Given a list of ifas (possibly null), prepend ifas for ethernet devices.
25  * Returns the new head of the list (also possibly null). */
26 static struct ifaddrs *get_ether_addrs(struct ifaddrs *ifa)
27 {
28         struct ifaddrs *new_ifa;
29         struct sockaddr_ll *sa_ll;
30         DIR *net;
31         struct dirent *d;
32         int addr_fd;
33         char path[MAX_PATH_LEN];
34         /* 6 is known everywhere, defined nowhere. So is 6 * 2 */
35         char etheraddr[12];
36         #define SIZE_OF_ETHER 5
37
38         net = opendir("/net");
39         if (net == NULL)
40                 return ifa;
41
42         for (d = readdir(net); d; d = readdir(net)) {
43                 if (strncmp(d->d_name, "ether", SIZE_OF_ETHER))
44                         continue;
45                 snprintf(path, sizeof(path), "/net/%s/addr", d->d_name);
46                 addr_fd = open(path, O_RDONLY);
47                 if (addr_fd < 0)
48                         continue;
49                 if (read(addr_fd, etheraddr,
50                          sizeof(etheraddr)) < sizeof(etheraddr)) {
51                         fprintf(stderr, "Read addr from %s: %r", d->d_name);
52                         close(addr_fd);
53                         continue;
54                 }
55                 /* getifaddrs is a stupid design as it only admits of
56                  * one address per interface.  Don't even bother
57                  * filling in ifa_{addr,netmask}. They're allowed to
58                  * be NULL.  Broadaddr need be set IFF a bit is set
59                  * in the flags field. We don't set either one.
60                  */
61                 new_ifa = calloc(sizeof(*ifa), 1);
62                 new_ifa->ifa_next = ifa;
63                 ifa = new_ifa;
64                 ifa->ifa_name = strdup(d->d_name);
65                 sa_ll = calloc(sizeof(struct sockaddr_ll), 1);
66                 ifa->ifa_addr = (struct sockaddr*)sa_ll;
67                 sa_ll->sll_family = AF_PACKET;
68                 /* TODO: could set protocol and hatype, if we ever get the
69                  * headers for the options.  Probably not worth it. */
70                 sa_ll->sll_ifindex = atoi(&ifa->ifa_name[SIZE_OF_ETHER]);
71                 sa_ll->sll_halen = 6;
72                 for (int i = 0; i < 6; i++)
73                         sscanf(&etheraddr[i * 2], "%02x", &sa_ll->sll_addr[i]);
74                 close(addr_fd);
75         }
76         closedir(net);
77         return ifa;
78 }
79
80 /* Given a list of ifas (possibly null), prepend an ifa for the given lifc. */
81 static struct ifaddrs *get_lifc_addr(struct ifaddrs *ifa, struct iplifc *lifc)
82 {
83         struct ifaddrs *new_ifa;
84         struct sockaddr_in *sa_in, *mask_in;
85         struct sockaddr_in6 *sa_in6, *mask_in6;
86
87         if (!ipcmp(lifc->ip, IPnoaddr))
88                 return ifa;
89         new_ifa = calloc(sizeof(*ifa), 1);
90         new_ifa->ifa_next = ifa;
91         ifa = new_ifa;
92         ifa->ifa_name = NULL;
93         if (isv4(lifc->ip)) {
94                 sa_in = calloc(sizeof(struct sockaddr_in), 1);
95                 sa_in->sin_family = AF_INET;
96                 ifa->ifa_addr = (struct sockaddr*)sa_in;
97                 v6tov4((uint8_t*)&sa_in->sin_addr, lifc->ip);
98
99                 mask_in = calloc(sizeof(struct sockaddr_in), 1);
100                 mask_in->sin_family = AF_INET;
101                 ifa->ifa_netmask = (struct sockaddr*)mask_in;
102                 /* The V4 mask is the last four bytes of the full v6 mask */
103                 memcpy((uint8_t*)&mask_in->sin_addr, &lifc->mask[12],
104                        sizeof(struct in_addr));
105         } else {
106                 /* TODO: check this when we have a v6 system */
107                 sa_in6 = calloc(sizeof(struct sockaddr_in6), 1);
108                 sa_in6->sin6_family = AF_INET6;
109                 ifa->ifa_addr = (struct sockaddr*)sa_in6;
110                 memcpy(&sa_in6->sin6_addr, lifc->ip, sizeof(struct in6_addr));
111
112                 mask_in6 = calloc(sizeof(struct sockaddr_in6), 1);
113                 mask_in6->sin6_family = AF_INET6;
114                 ifa->ifa_netmask = (struct sockaddr*)mask_in6;
115                 memcpy((uint8_t*)&mask_in6->sin6_addr, lifc->mask,
116                        sizeof(struct in6_addr));
117         }
118         return ifa;
119 }
120
121 /* Given a list of ifas (possibly null), prepend ifas for inet devices.
122  * Returns the new head of the list (also possibly null). */
123 static struct ifaddrs *get_inet_addrs(struct ifaddrs *ifa)
124 {
125         struct ipifc *ifc, *ifc_i;
126         struct iplifc *lifc_i;
127
128         /* This gives us a list of ipifcs (actual interfaces), each of which has
129          * a list of lifcs (local interface, including the IP addr). */
130         ifc = readipifc(NULL, NULL, -1);
131         for (ifc_i = ifc; ifc_i; ifc_i = ifc_i->next) {
132                 for (lifc_i = ifc_i->lifc; lifc_i; lifc_i = lifc_i->next)
133                         ifa = get_lifc_addr(ifa, lifc_i);
134         }
135         free_ipifc(ifc);
136         return ifa;
137 }
138
139 int getifaddrs(struct ifaddrs **ifap)
140 {
141         struct ifaddrs *ifa = NULL;
142
143         *ifap = NULL;
144         ifa = get_ether_addrs(ifa);
145         ifa = get_inet_addrs(ifa);
146         *ifap = ifa;
147         return 0;
148 }
149
150 void freeifaddrs(struct ifaddrs *ifa)
151 {
152         struct ifaddrs *next = ifa;
153
154         while (ifa) {
155                 next = ifa->ifa_next;
156
157                 free(ifa->ifa_name);
158                 free(ifa->ifa_addr);
159                 free(ifa->ifa_netmask);
160                 free(ifa);
161
162                 ifa = next;
163         }
164 }