1 #define _GNU_SOURCE
2 #include <errno.h>
3 #include <string.h>
4 #include <stdlib.h>
5 #include <unistd.h>
6 #include <ifaddrs.h>
7 #include <syscall.h>
8 #include <net/if.h>
9 #include <netinet/in.h>
10 #include <sys/ioctl.h>
11 #include "netlink.h"
12 #include<stdio.h>
13
14 #define IFADDRS_HASH_SIZE 64
15 #define MAX_IF_NO 10
16
17 /* getifaddrs() reports hardware addresses with PF_PACKET that implies
18 * struct sockaddr_ll. But e.g. Infiniband socket address length is
19 * longer than sockaddr_ll.ssl_addr[8] can hold. Use this hack struct
20 * to extend ssl_addr - callers should be able to still use it. */
21 struct sockaddr_ll_hack {
22 unsigned short sll_family, sll_protocol;
23 int sll_ifindex;
24 unsigned short sll_hatype;
25 unsigned char sll_pkttype, sll_halen;
26 unsigned char sll_addr[24];
27 };
28
29 union sockany {
30 struct sockaddr sa;
31 struct sockaddr_ll_hack ll;
32 struct sockaddr_in v4;
33 struct sockaddr_in6 v6;
34 };
35
36 struct ifaddrs_storage {
37 struct ifaddrs ifa;
38 struct ifaddrs_storage *hash_next;
39 union sockany addr, netmask, ifu;
40 unsigned int index;
41 char name[IFNAMSIZ+1];
42 };
43
44 struct ifaddrs_ctx {
45 struct ifaddrs_storage *first;
46 struct ifaddrs_storage *last;
47 struct ifaddrs_storage *hash[IFADDRS_HASH_SIZE];
48 };
49
freeifaddrs(struct ifaddrs * ifp)50 void freeifaddrs(struct ifaddrs *ifp)
51 {
52 struct ifaddrs *tmp = NULL;
53 while (ifp) {
54 if (!ifp->ifa_name)
55 free(ifp);
56 if (!ifp->ifa_addr)
57 free(ifp->ifa_addr);
58 if (!ifp->ifa_netmask)
59 free(ifp->ifa_netmask);
60 tmp = ifp->ifa_next;
61 free(ifp);
62 ifp = tmp;
63 }
64 }
65
copy_addr(struct sockaddr ** r,int af,union sockany * sa,void * addr,size_t addrlen,int ifindex)66 static void copy_addr(struct sockaddr **r, int af, union sockany *sa, void *addr, size_t addrlen, int ifindex)
67 {
68 uint8_t *dst;
69 int len;
70
71 switch (af) {
72 case AF_INET:
73 dst = (uint8_t*) &sa->v4.sin_addr;
74 len = 4;
75 break;
76 case AF_INET6:
77 dst = (uint8_t*) &sa->v6.sin6_addr;
78 len = 16;
79 if (IN6_IS_ADDR_LINKLOCAL(addr) || IN6_IS_ADDR_MC_LINKLOCAL(addr))
80 sa->v6.sin6_scope_id = ifindex;
81 break;
82 default:
83 return;
84 }
85 if (addrlen < len) return;
86 sa->sa.sa_family = af;
87 memcpy(dst, addr, len);
88 *r = &sa->sa;
89 }
90
gen_netmask(struct sockaddr ** r,int af,union sockany * sa,int prefixlen)91 static void gen_netmask(struct sockaddr **r, int af, union sockany *sa, int prefixlen)
92 {
93 uint8_t addr[16] = {0};
94 int i;
95
96 if (prefixlen > 8*sizeof(addr)) prefixlen = 8*sizeof(addr);
97 i = prefixlen / 8;
98 memset(addr, 0xff, i);
99 if (i < sizeof(addr)) addr[i++] = 0xff << (8 - (prefixlen % 8));
100 copy_addr(r, af, sa, addr, sizeof(addr), 0);
101 }
102
copy_lladdr(struct sockaddr ** r,union sockany * sa,void * addr,size_t addrlen,int ifindex,unsigned short hatype)103 static void copy_lladdr(struct sockaddr **r, union sockany *sa, void *addr, size_t addrlen, int ifindex, unsigned short hatype)
104 {
105 if (addrlen > sizeof(sa->ll.sll_addr)) return;
106 sa->ll.sll_family = AF_PACKET;
107 sa->ll.sll_ifindex = ifindex;
108 sa->ll.sll_hatype = hatype;
109 sa->ll.sll_halen = addrlen;
110 memcpy(sa->ll.sll_addr, addr, addrlen);
111 *r = &sa->sa;
112 }
113
netlink_msg_to_ifaddr(void * pctx,struct nlmsghdr * h)114 static int netlink_msg_to_ifaddr(void *pctx, struct nlmsghdr *h)
115 {
116 struct ifaddrs_ctx *ctx = pctx;
117 struct ifaddrs_storage *ifs, *ifs0;
118 struct ifinfomsg *ifi = NLMSG_DATA(h);
119 struct ifaddrmsg *ifa = NLMSG_DATA(h);
120 struct rtattr *rta;
121 int stats_len = 0;
122
123 if (h->nlmsg_type == RTM_NEWLINK) {
124 for (rta = NLMSG_RTA(h, sizeof(*ifi)); NLMSG_RTAOK(rta, h); rta = RTA_NEXT(rta)) {
125 if (rta->rta_type != IFLA_STATS) continue;
126 stats_len = RTA_DATALEN(rta);
127 break;
128 }
129 } else {
130 for (ifs0 = ctx->hash[ifa->ifa_index % IFADDRS_HASH_SIZE]; ifs0; ifs0 = ifs0->hash_next)
131 if (ifs0->index == ifa->ifa_index)
132 break;
133 if (!ifs0) return 0;
134 }
135
136 ifs = calloc(1, sizeof(struct ifaddrs_storage) + stats_len);
137 if (ifs == 0) return -1;
138
139 if (h->nlmsg_type == RTM_NEWLINK) {
140 ifs->index = ifi->ifi_index;
141 ifs->ifa.ifa_flags = ifi->ifi_flags;
142
143 for (rta = NLMSG_RTA(h, sizeof(*ifi)); NLMSG_RTAOK(rta, h); rta = RTA_NEXT(rta)) {
144 switch (rta->rta_type) {
145 case IFLA_IFNAME:
146 if (RTA_DATALEN(rta) < sizeof(ifs->name)) {
147 memcpy(ifs->name, RTA_DATA(rta), RTA_DATALEN(rta));
148 ifs->ifa.ifa_name = ifs->name;
149 }
150 break;
151 case IFLA_ADDRESS:
152 copy_lladdr(&ifs->ifa.ifa_addr, &ifs->addr, RTA_DATA(rta), RTA_DATALEN(rta), ifi->ifi_index, ifi->ifi_type);
153 break;
154 case IFLA_BROADCAST:
155 copy_lladdr(&ifs->ifa.ifa_broadaddr, &ifs->ifu, RTA_DATA(rta), RTA_DATALEN(rta), ifi->ifi_index, ifi->ifi_type);
156 break;
157 case IFLA_STATS:
158 ifs->ifa.ifa_data = (void*)(ifs+1);
159 memcpy(ifs->ifa.ifa_data, RTA_DATA(rta), RTA_DATALEN(rta));
160 break;
161 }
162 }
163 if (ifs->ifa.ifa_name) {
164 unsigned int bucket = ifs->index % IFADDRS_HASH_SIZE;
165 ifs->hash_next = ctx->hash[bucket];
166 ctx->hash[bucket] = ifs;
167 }
168 } else {
169 ifs->ifa.ifa_name = ifs0->ifa.ifa_name;
170 ifs->ifa.ifa_flags = ifs0->ifa.ifa_flags;
171 for (rta = NLMSG_RTA(h, sizeof(*ifa)); NLMSG_RTAOK(rta, h); rta = RTA_NEXT(rta)) {
172 switch (rta->rta_type) {
173 case IFA_ADDRESS:
174 /* If ifa_addr is already set we, received an IFA_LOCAL before
175 * so treat this as destination address */
176 if (ifs->ifa.ifa_addr)
177 copy_addr(&ifs->ifa.ifa_dstaddr, ifa->ifa_family, &ifs->ifu, RTA_DATA(rta), RTA_DATALEN(rta), ifa->ifa_index);
178 else
179 copy_addr(&ifs->ifa.ifa_addr, ifa->ifa_family, &ifs->addr, RTA_DATA(rta), RTA_DATALEN(rta), ifa->ifa_index);
180 break;
181 case IFA_BROADCAST:
182 copy_addr(&ifs->ifa.ifa_broadaddr, ifa->ifa_family, &ifs->ifu, RTA_DATA(rta), RTA_DATALEN(rta), ifa->ifa_index);
183 break;
184 case IFA_LOCAL:
185 /* If ifa_addr is set and we get IFA_LOCAL, assume we have
186 * a point-to-point network. Move address to correct field. */
187 if (ifs->ifa.ifa_addr) {
188 ifs->ifu = ifs->addr;
189 ifs->ifa.ifa_dstaddr = &ifs->ifu.sa;
190 memset(&ifs->addr, 0, sizeof(ifs->addr));
191 }
192 copy_addr(&ifs->ifa.ifa_addr, ifa->ifa_family, &ifs->addr, RTA_DATA(rta), RTA_DATALEN(rta), ifa->ifa_index);
193 break;
194 case IFA_LABEL:
195 if (RTA_DATALEN(rta) < sizeof(ifs->name)) {
196 memcpy(ifs->name, RTA_DATA(rta), RTA_DATALEN(rta));
197 ifs->ifa.ifa_name = ifs->name;
198 }
199 break;
200 }
201 }
202 if (ifs->ifa.ifa_addr)
203 gen_netmask(&ifs->ifa.ifa_netmask, ifa->ifa_family, &ifs->netmask, ifa->ifa_prefixlen);
204 }
205
206 if (ifs->ifa.ifa_name) {
207 if (!ctx->first) ctx->first = ifs;
208 if (ctx->last) ctx->last->ifa.ifa_next = &ifs->ifa;
209 ctx->last = ifs;
210 } else {
211 free(ifs);
212 }
213 return 0;
214 }
215
216
ifaddrs_init()217 static struct ifaddrs* ifaddrs_init()
218 {
219 struct ifaddrs *ifa = NULL;
220 ifa = malloc(sizeof(struct ifaddrs));
221 if (!ifa)
222 return NULL;
223 ifa->ifa_name = malloc(sizeof(char) *(IF_NAMESIZE + 1));
224 ifa->ifa_addr = malloc(sizeof(struct sockaddr));
225 ifa->ifa_netmask = malloc(sizeof(struct sockaddr));
226 ifa->ifa_next = NULL;
227 if (!ifa->ifa_name || !ifa->ifa_addr || !ifa->ifa_netmask) {
228 free(ifa->ifa_name);
229 free(ifa->ifa_addr);
230 free(ifa->ifa_netmask);
231 free(ifa);
232 return NULL;
233 }
234 return ifa;
235 }
236
getifaddrs(struct ifaddrs ** ifap)237 int getifaddrs(struct ifaddrs **ifap)
238 {
239 if (ifap == NULL) {
240 errno = EINVAL;
241 return -1;
242 }
243
244 int fd, ifno, ret;
245 struct ifreq ifr[MAX_IF_NO];
246 struct ifconf ifconfig;
247 struct ifaddrs *ifstart = NULL;
248
249 ifconfig.ifc_buf = ifr;
250 ifconfig.ifc_len = sizeof(ifr);
251 if ((fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)) < 0)
252 return -1;
253 if (ioctl(fd, SIOCGIFCONF, &ifconfig) < 0)
254 goto error;
255 if (ifconfig.ifc_len % sizeof(struct ifreq)) {
256 errno = EINVAL;
257 goto error;
258 }
259 ifno = ifconfig.ifc_len / sizeof(struct ifreq);
260
261 if (!(ifstart = ifaddrs_init())) {
262 errno = ENOMEM;
263 goto error;
264 }
265 struct ifaddrs *ifa = ifstart;
266
267 for (int i = 0; i < ifno; i++) {
268 memcpy(ifa->ifa_name, ifr[i].ifr_name, IF_NAMESIZE);
269 ifa->ifa_name[IF_NAMESIZE] = '\0';
270 memcpy(ifa->ifa_addr, &ifr[i].ifr_addr, sizeof(struct sockaddr));
271 if (ioctl(fd, SIOCGIFNETMASK, &ifr[i]) < 0)
272 goto error;
273 memcpy(ifa->ifa_netmask, &ifr[i].ifr_netmask, sizeof(struct sockaddr));
274
275 if (ioctl(fd, SIOCGIFFLAGS, &ifr[i]) < 0)
276 goto error;
277 ifa->ifa_flags = ifr[i].ifr_flags;
278
279 if (i < ifno - 1) {
280 ifa->ifa_next = ifaddrs_init();
281 if (!ifa->ifa_next) {
282 errno = ENOMEM;
283 goto error;
284 }
285 ifa = ifa->ifa_next;
286 }
287 }
288
289 *ifap = ifstart;
290 return 0;
291
292 error:
293 freeifaddrs(ifstart);
294 __syscall(SYS_close, fd);
295 return -1;
296 }
297
298