parlib: Expand our printf hacks
[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, sizeof(etheraddr)) < sizeof(etheraddr)) {
50                         fprintf(stderr, "Read addr from %s: %r", d->d_name);
51                         close(addr_fd);
52                         continue;
53                 }
54                 /* getifaddrs is a stupid design as it only admits of
55                  * one address per interface.  Don't even bother
56                  * filling in ifa_{addr,netmask}. They're allowed to
57                  * be NULL.  Broadaddr need be set IFF a bit is set
58                  * in the flags field. We don't set either one.
59                  */
60                 new_ifa = calloc(sizeof(*ifa), 1);
61                 new_ifa->ifa_next = ifa;
62                 ifa = new_ifa;
63                 ifa->ifa_name = strdup(d->d_name);
64                 sa_ll = calloc(sizeof(struct sockaddr_ll), 1);
65                 ifa->ifa_addr = (struct sockaddr*)sa_ll;
66                 sa_ll->sll_family = AF_PACKET;
67                 /* TODO: could set protocol and hatype, if we ever get the headers for
68                  * the options.  Probably not worth it. */
69                 sa_ll->sll_ifindex = atoi(&ifa->ifa_name[SIZE_OF_ETHER]);
70                 sa_ll->sll_halen = 6;
71                 for (int i = 0; i < 6; i++)
72                         sscanf(&etheraddr[i * 2], "%02x", &sa_ll->sll_addr[i]);
73                 close(addr_fd);
74         }
75         closedir(net);
76         return ifa;
77 }
78
79 /* Given a list of ifas (possibly null), prepend an ifa for the given lifc. */
80 static struct ifaddrs *get_lifc_addr(struct ifaddrs *ifa, struct iplifc *lifc)
81 {
82         struct ifaddrs *new_ifa;
83         struct sockaddr_in *sa_in, *mask_in;
84         struct sockaddr_in6 *sa_in6, *mask_in6;
85
86         if (!ipcmp(lifc->ip, IPnoaddr))
87                 return ifa;
88         new_ifa = calloc(sizeof(*ifa), 1);
89         new_ifa->ifa_next = ifa;
90         ifa = new_ifa;
91         ifa->ifa_name = NULL;
92         if (isv4(lifc->ip)) {
93                 sa_in = calloc(sizeof(struct sockaddr_in), 1);
94                 sa_in->sin_family = AF_INET;
95                 ifa->ifa_addr = (struct sockaddr*)sa_in;
96                 v6tov4((uint8_t*)&sa_in->sin_addr, lifc->ip);
97
98                 mask_in = calloc(sizeof(struct sockaddr_in), 1);
99                 mask_in->sin_family = AF_INET;
100                 ifa->ifa_netmask = (struct sockaddr*)mask_in;
101                 /* The V4 mask is the last four bytes of the full v6 mask */
102                 memcpy((uint8_t*)&mask_in->sin_addr, &lifc->mask[12],
103                        sizeof(struct in_addr));
104         } else {
105                 /* TODO: check this when we have a v6 system */
106                 sa_in6 = calloc(sizeof(struct sockaddr_in6), 1);
107                 sa_in6->sin6_family = AF_INET6;
108                 ifa->ifa_addr = (struct sockaddr*)sa_in6;
109                 memcpy(&sa_in6->sin6_addr, lifc->ip, sizeof(struct in6_addr));
110
111                 mask_in6 = calloc(sizeof(struct sockaddr_in6), 1);
112                 mask_in6->sin6_family = AF_INET6;
113                 ifa->ifa_netmask = (struct sockaddr*)mask_in6;
114                 memcpy((uint8_t*)&mask_in6->sin6_addr, lifc->mask,
115                        sizeof(struct in6_addr));
116         }
117         return ifa;
118 }
119
120 /* Given a list of ifas (possibly null), prepend ifas for inet devices.
121  * Returns the new head of the list (also possibly null). */
122 static struct ifaddrs *get_inet_addrs(struct ifaddrs *ifa)
123 {
124         struct ipifc *ifc, *ifc_i;
125         struct iplifc *lifc_i;
126
127         /* This gives us a list of ipifcs (actual interfaces), each of which has a
128          * list of lifcs (local interface, including the IP addr). */
129         ifc = readipifc(NULL, NULL, -1);
130         for (ifc_i = ifc; ifc_i; ifc_i = ifc_i->next) {
131                 for (lifc_i = ifc_i->lifc; lifc_i; lifc_i = lifc_i->next)
132                         ifa = get_lifc_addr(ifa, lifc_i);
133         }
134         free_ipifc(ifc);
135         return ifa;
136 }
137
138 int getifaddrs(struct ifaddrs **ifap)
139 {
140         struct ifaddrs *ifa = NULL;
141
142         *ifap = NULL;
143         ifa = get_ether_addrs(ifa);
144         ifa = get_inet_addrs(ifa);
145         *ifap = ifa;
146         return 0;
147 }
148
149 void freeifaddrs(struct ifaddrs *ifa)
150 {
151         struct ifaddrs *next = ifa;
152
153         while (ifa) {
154                 next = ifa->ifa_next;
155
156                 free(ifa->ifa_name);
157                 free(ifa->ifa_addr);
158                 free(ifa->ifa_netmask);
159                 free(ifa);
160
161                 ifa = next;
162         }
163 }