/src/lxc/src/include/netns_ifaddrs.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* SPDX-License-Identifier: LGPL-2.1+ */ |
2 | | |
3 | | #include <arpa/inet.h> |
4 | | #include <errno.h> |
5 | | #include <linux/if.h> |
6 | | #include <linux/if_addr.h> |
7 | | #include <linux/if_link.h> |
8 | | #include <linux/if_packet.h> |
9 | | #include <linux/netlink.h> |
10 | | #include <linux/rtnetlink.h> |
11 | | #include <linux/types.h> |
12 | | #include <net/ethernet.h> |
13 | | #include <netinet/in.h> |
14 | | #include <stdbool.h> |
15 | | #include <stdio.h> |
16 | | #include <stdlib.h> |
17 | | #include <string.h> |
18 | | #include <sys/socket.h> |
19 | | #include <unistd.h> |
20 | | |
21 | | #include "config.h" |
22 | | #include "nl.h" |
23 | | #include "macro.h" |
24 | | #include "netns_ifaddrs.h" |
25 | | |
26 | | #ifndef NETNS_RTA |
27 | | #define NETNS_RTA(r) \ |
28 | | ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct rtgenmsg)))) |
29 | | #endif |
30 | | |
31 | 2.28k | #define IFADDRS_HASH_SIZE 64 |
32 | | |
33 | 68.4k | #define __NETLINK_ALIGN(len) (((len) + 3) & ~3) |
34 | | |
35 | | #define __NLMSG_OK(nlh, end) \ |
36 | 4.56k | ((size_t)((char *)(end) - (char *)(nlh)) >= sizeof(struct nlmsghdr)) |
37 | | |
38 | | #define __NLMSG_NEXT(nlh) \ |
39 | 2.28k | (struct nlmsghdr *)((char *)(nlh) + __NETLINK_ALIGN((nlh)->nlmsg_len)) |
40 | | |
41 | 5.70k | #define __NLMSG_DATA(nlh) ((void *)((char *)(nlh) + sizeof(struct nlmsghdr))) |
42 | | |
43 | | #define __NLMSG_DATAEND(nlh) ((char *)(nlh) + (nlh)->nlmsg_len) |
44 | | |
45 | | #define __NLMSG_RTA(nlh, len) \ |
46 | 3.42k | ((void *)((char *)(nlh) + sizeof(struct nlmsghdr) + \ |
47 | 3.42k | __NETLINK_ALIGN(len))) |
48 | | |
49 | 11.9k | #define __RTA_DATALEN(rta) ((rta)->rta_len - sizeof(struct rtattr)) |
50 | | |
51 | | #define __RTA_NEXT(rta) \ |
52 | 62.7k | (struct rtattr *)((char *)(rta) + __NETLINK_ALIGN((rta)->rta_len)) |
53 | | |
54 | | #define __RTA_OK(nlh, end) \ |
55 | 66.1k | ((size_t)((char *)(end) - (char *)(rta)) >= sizeof(struct rtattr)) |
56 | | |
57 | | #define __NLMSG_RTAOK(rta, nlh) __RTA_OK(rta, __NLMSG_DATAEND(nlh)) |
58 | | |
59 | | #define __IN6_IS_ADDR_LINKLOCAL(a) \ |
60 | 0 | ((((uint8_t *)(a))[0]) == 0xfe && (((uint8_t *)(a))[1] & 0xc0) == 0x80) |
61 | | |
62 | | #define __IN6_IS_ADDR_MC_LINKLOCAL(a) \ |
63 | 0 | (IN6_IS_ADDR_MULTICAST(a) && ((((uint8_t *)(a))[1] & 0xf) == 0x2)) |
64 | | |
65 | 9.69k | #define __RTA_DATA(rta) ((void *)((char *)(rta) + sizeof(struct rtattr))) |
66 | | |
67 | | /* getifaddrs() reports hardware addresses with PF_PACKET that implies struct |
68 | | * sockaddr_ll. But e.g. Infiniband socket address length is longer than |
69 | | * sockaddr_ll.ssl_addr[8] can hold. Use this hack struct to extend ssl_addr - |
70 | | * callers should be able to still use it. |
71 | | */ |
72 | | struct sockaddr_ll_hack { |
73 | | unsigned short sll_family, sll_protocol; |
74 | | int sll_ifindex; |
75 | | unsigned short sll_hatype; |
76 | | unsigned char sll_pkttype, sll_halen; |
77 | | unsigned char sll_addr[24]; |
78 | | }; |
79 | | |
80 | | union sockany { |
81 | | struct sockaddr sa; |
82 | | struct sockaddr_ll_hack ll; |
83 | | struct sockaddr_in v4; |
84 | | struct sockaddr_in6 v6; |
85 | | }; |
86 | | |
87 | | struct ifaddrs_storage { |
88 | | struct netns_ifaddrs ifa; |
89 | | struct ifaddrs_storage *hash_next; |
90 | | union sockany addr, netmask, ifu; |
91 | | unsigned int index; |
92 | | char name[IFNAMSIZ + 1]; |
93 | | }; |
94 | | |
95 | | struct ifaddrs_ctx { |
96 | | struct ifaddrs_storage *first; |
97 | | struct ifaddrs_storage *last; |
98 | | struct ifaddrs_storage *hash[IFADDRS_HASH_SIZE]; |
99 | | }; |
100 | | |
101 | | static void copy_addr(struct sockaddr **r, int af, union sockany *sa, |
102 | | void *addr, size_t addrlen, int ifindex) |
103 | 3.99k | { |
104 | 3.99k | uint8_t *dst; |
105 | 3.99k | size_t len; |
106 | | |
107 | 3.99k | switch (af) { |
108 | 3.99k | case AF_INET: |
109 | 3.99k | dst = (uint8_t *)&sa->v4.sin_addr; |
110 | 3.99k | len = 4; |
111 | 3.99k | break; |
112 | 0 | case AF_INET6: |
113 | 0 | dst = (uint8_t *)&sa->v6.sin6_addr; |
114 | 0 | len = 16; |
115 | 0 | if (__IN6_IS_ADDR_LINKLOCAL(addr) || |
116 | 0 | __IN6_IS_ADDR_MC_LINKLOCAL(addr)) |
117 | 0 | sa->v6.sin6_scope_id = ifindex; |
118 | 0 | break; |
119 | 0 | default: |
120 | 0 | return; |
121 | 3.99k | } |
122 | | |
123 | 3.99k | if (addrlen < len) |
124 | 0 | return; |
125 | | |
126 | 3.99k | sa->sa.sa_family = af; |
127 | | |
128 | 3.99k | memcpy(dst, addr, len); |
129 | | |
130 | 3.99k | *r = &sa->sa; |
131 | 3.99k | } |
132 | | |
133 | | static void gen_netmask(struct sockaddr **r, int af, union sockany *sa, |
134 | | int prefixlen) |
135 | 1.14k | { |
136 | 1.14k | uint8_t addr[16] = {0}; |
137 | 1.14k | int i; |
138 | | |
139 | 1.14k | if ((size_t)prefixlen > 8 * sizeof(addr)) |
140 | 0 | prefixlen = 8 * sizeof(addr); |
141 | | |
142 | 1.14k | i = prefixlen / 8; |
143 | | |
144 | 1.14k | memset(addr, 0xff, i); |
145 | | |
146 | 1.14k | if ((size_t)i < sizeof(addr)) |
147 | 1.14k | addr[i++] = 0xff << (8 - (prefixlen % 8)); |
148 | | |
149 | 1.14k | copy_addr(r, af, sa, addr, sizeof(addr), 0); |
150 | 1.14k | } |
151 | | |
152 | | static void copy_lladdr(struct sockaddr **r, union sockany *sa, void *addr, |
153 | | size_t addrlen, int ifindex, unsigned short hatype) |
154 | 2.28k | { |
155 | 2.28k | if (addrlen > sizeof(sa->ll.sll_addr)) |
156 | 0 | return; |
157 | | |
158 | 2.28k | sa->ll.sll_family = AF_PACKET; |
159 | 2.28k | sa->ll.sll_ifindex = ifindex; |
160 | 2.28k | sa->ll.sll_hatype = hatype; |
161 | 2.28k | sa->ll.sll_halen = addrlen; |
162 | | |
163 | 2.28k | memcpy(sa->ll.sll_addr, addr, addrlen); |
164 | | |
165 | 2.28k | *r = &sa->sa; |
166 | 2.28k | } |
167 | | |
168 | | static int nl_msg_to_ifaddr(void *pctx, bool *netnsid_aware, struct nlmsghdr *h) |
169 | 2.28k | { |
170 | 2.28k | struct ifaddrs_storage *ifs, *ifs0; |
171 | 2.28k | struct rtattr *rta; |
172 | 2.28k | int stats_len = 0; |
173 | 2.28k | struct ifinfomsg *ifi = __NLMSG_DATA(h); |
174 | 2.28k | struct ifaddrmsg *ifa = __NLMSG_DATA(h); |
175 | 2.28k | struct ifaddrs_ctx *ctx = pctx; |
176 | | |
177 | 2.28k | if (h->nlmsg_type == RTM_NEWLINK) { |
178 | 1.14k | #pragma GCC diagnostic push |
179 | 1.14k | #pragma GCC diagnostic ignored "-Wcast-align" |
180 | 1.14k | for (rta = __NLMSG_RTA(h, sizeof(*ifi)); __NLMSG_RTAOK(rta, h); |
181 | 26.2k | rta = __RTA_NEXT(rta)) { |
182 | 26.2k | #if HAVE_STRUCT_RTNL_LINK_STATS64 |
183 | 26.2k | if (rta->rta_type != IFLA_STATS64) |
184 | | #else |
185 | | if (rta->rta_type != IFLA_STATS) |
186 | | #endif |
187 | 25.0k | continue; |
188 | | |
189 | 1.14k | stats_len = __RTA_DATALEN(rta); |
190 | 1.14k | break; |
191 | 26.2k | } |
192 | 1.14k | #pragma GCC diagnostic pop |
193 | 1.14k | } else { |
194 | 1.14k | for (ifs0 = ctx->hash[ifa->ifa_index % IFADDRS_HASH_SIZE]; ifs0; |
195 | 1.14k | ifs0 = ifs0->hash_next) |
196 | 1.14k | if (ifs0->index == ifa->ifa_index) |
197 | 1.14k | break; |
198 | 1.14k | if (!ifs0) |
199 | 0 | return 0; |
200 | 1.14k | } |
201 | | |
202 | 2.28k | ifs = calloc(1, sizeof(struct ifaddrs_storage) + stats_len); |
203 | 2.28k | if (!ifs) { |
204 | 0 | errno = ENOMEM; |
205 | 0 | return -1; |
206 | 0 | } |
207 | | |
208 | 2.28k | #pragma GCC diagnostic push |
209 | 2.28k | #pragma GCC diagnostic ignored "-Wcast-align" |
210 | 2.28k | if (h->nlmsg_type == RTM_NEWLINK) { |
211 | 1.14k | ifs->index = ifi->ifi_index; |
212 | 1.14k | ifs->ifa.ifa_ifindex = ifi->ifi_index; |
213 | 1.14k | ifs->ifa.ifa_flags = ifi->ifi_flags; |
214 | | |
215 | 1.14k | for (rta = __NLMSG_RTA(h, sizeof(*ifi)); __NLMSG_RTAOK(rta, h); |
216 | 31.3k | rta = __RTA_NEXT(rta)) { |
217 | 31.3k | switch (rta->rta_type) { |
218 | 1.14k | case IFLA_IFNAME: |
219 | 1.14k | if (__RTA_DATALEN(rta) < sizeof(ifs->name)) { |
220 | 1.14k | memcpy(ifs->name, __RTA_DATA(rta), |
221 | 1.14k | __RTA_DATALEN(rta)); |
222 | 1.14k | ifs->ifa.ifa_name = ifs->name; |
223 | 1.14k | } |
224 | 1.14k | break; |
225 | 1.14k | case IFLA_ADDRESS: |
226 | 1.14k | copy_lladdr(&ifs->ifa.ifa_addr, &ifs->addr, |
227 | 1.14k | __RTA_DATA(rta), __RTA_DATALEN(rta), |
228 | 1.14k | ifi->ifi_index, ifi->ifi_type); |
229 | 1.14k | break; |
230 | 1.14k | case IFLA_BROADCAST: |
231 | 1.14k | copy_lladdr(&ifs->ifa.__ifa_broadaddr, &ifs->ifu, |
232 | 1.14k | __RTA_DATA(rta), __RTA_DATALEN(rta), |
233 | 1.14k | ifi->ifi_index, ifi->ifi_type); |
234 | 1.14k | break; |
235 | 0 | #if HAVE_STRUCT_RTNL_LINK_STATS64 |
236 | 1.14k | case IFLA_STATS64: |
237 | 1.14k | ifs->ifa.ifa_stats_type = IFLA_STATS64; |
238 | | #else |
239 | | case IFLA_STATS: |
240 | | ifs->ifa.ifa_stats_type = IFLA_STATS; |
241 | | #endif |
242 | 1.14k | memcpy(&ifs->ifa.ifa_stats, __RTA_DATA(rta), |
243 | 1.14k | __RTA_DATALEN(rta)); |
244 | 1.14k | break; |
245 | 1.14k | case IFLA_MTU: |
246 | 1.14k | memcpy(&ifs->ifa.ifa_mtu, __RTA_DATA(rta), |
247 | 1.14k | sizeof(int)); |
248 | 1.14k | break; |
249 | 0 | case IFLA_TARGET_NETNSID: |
250 | 0 | *netnsid_aware = true; |
251 | 0 | break; |
252 | 31.3k | } |
253 | 31.3k | } |
254 | | |
255 | 1.14k | if (ifs->ifa.ifa_name) { |
256 | 1.14k | unsigned int bucket = ifs->index % IFADDRS_HASH_SIZE; |
257 | 1.14k | ifs->hash_next = ctx->hash[bucket]; |
258 | 1.14k | ctx->hash[bucket] = ifs; |
259 | 1.14k | } |
260 | 1.14k | } else { |
261 | 1.14k | ifs->ifa.ifa_name = ifs0->ifa.ifa_name; |
262 | 1.14k | ifs->ifa.ifa_mtu = ifs0->ifa.ifa_mtu; |
263 | 1.14k | ifs->ifa.ifa_ifindex = ifs0->ifa.ifa_ifindex; |
264 | 1.14k | ifs->ifa.ifa_flags = ifs0->ifa.ifa_flags; |
265 | | |
266 | 1.14k | for (rta = __NLMSG_RTA(h, sizeof(*ifa)); __NLMSG_RTAOK(rta, h); |
267 | 6.27k | rta = __RTA_NEXT(rta)) { |
268 | 6.27k | switch (rta->rta_type) { |
269 | 1.14k | case IFA_ADDRESS: |
270 | | /* If ifa_addr is already set we, received an |
271 | | * IFA_LOCAL before so treat this as |
272 | | * destination address. |
273 | | */ |
274 | 1.14k | if (ifs->ifa.ifa_addr) |
275 | 0 | copy_addr(&ifs->ifa.__ifa_dstaddr, |
276 | 0 | ifa->ifa_family, &ifs->ifu, |
277 | 0 | __RTA_DATA(rta), |
278 | 0 | __RTA_DATALEN(rta), |
279 | 0 | ifa->ifa_index); |
280 | 1.14k | else |
281 | 1.14k | copy_addr(&ifs->ifa.ifa_addr, |
282 | 1.14k | ifa->ifa_family, &ifs->addr, |
283 | 1.14k | __RTA_DATA(rta), |
284 | 1.14k | __RTA_DATALEN(rta), |
285 | 1.14k | ifa->ifa_index); |
286 | 1.14k | break; |
287 | 570 | case IFA_BROADCAST: |
288 | 570 | copy_addr(&ifs->ifa.__ifa_broadaddr, |
289 | 570 | ifa->ifa_family, &ifs->ifu, |
290 | 570 | __RTA_DATA(rta), __RTA_DATALEN(rta), |
291 | 570 | ifa->ifa_index); |
292 | 570 | break; |
293 | 1.14k | case IFA_LOCAL: |
294 | | /* If ifa_addr is set and we get IFA_LOCAL, |
295 | | * assume we have a point-to-point network. |
296 | | * Move address to correct field. |
297 | | */ |
298 | 1.14k | if (ifs->ifa.ifa_addr) { |
299 | 1.14k | ifs->ifu = ifs->addr; |
300 | 1.14k | ifs->ifa.__ifa_dstaddr = &ifs->ifu.sa; |
301 | | |
302 | 1.14k | memset(&ifs->addr, 0, sizeof(ifs->addr)); |
303 | 1.14k | } |
304 | | |
305 | 1.14k | copy_addr(&ifs->ifa.ifa_addr, ifa->ifa_family, |
306 | 1.14k | &ifs->addr, __RTA_DATA(rta), |
307 | 1.14k | __RTA_DATALEN(rta), ifa->ifa_index); |
308 | 1.14k | break; |
309 | 1.14k | case IFA_LABEL: |
310 | 1.14k | if (__RTA_DATALEN(rta) < sizeof(ifs->name)) { |
311 | 1.14k | memcpy(ifs->name, __RTA_DATA(rta), |
312 | 1.14k | __RTA_DATALEN(rta)); |
313 | 1.14k | ifs->ifa.ifa_name = ifs->name; |
314 | 1.14k | } |
315 | 1.14k | break; |
316 | 0 | case IFA_TARGET_NETNSID: |
317 | 0 | *netnsid_aware = true; |
318 | 0 | break; |
319 | 6.27k | } |
320 | 6.27k | } |
321 | | |
322 | 1.14k | if (ifs->ifa.ifa_addr) { |
323 | 1.14k | gen_netmask(&ifs->ifa.ifa_netmask, ifa->ifa_family, |
324 | 1.14k | &ifs->netmask, ifa->ifa_prefixlen); |
325 | 1.14k | ifs->ifa.ifa_prefixlen = ifa->ifa_prefixlen; |
326 | 1.14k | } |
327 | 1.14k | } |
328 | 2.28k | #pragma GCC diagnostic pop |
329 | | |
330 | 2.28k | if (ifs->ifa.ifa_name) { |
331 | 2.28k | if (!ctx->first) |
332 | 570 | ctx->first = ifs; |
333 | | |
334 | 2.28k | if (ctx->last) |
335 | 1.71k | ctx->last->ifa.ifa_next = &ifs->ifa; |
336 | | |
337 | 2.28k | ctx->last = ifs; |
338 | 2.28k | } else { |
339 | 0 | free(ifs); |
340 | 0 | } |
341 | | |
342 | 2.28k | return 0; |
343 | 2.28k | } |
344 | | |
345 | | static int __ifaddrs_netlink_send(int fd, struct nlmsghdr *nlmsghdr) |
346 | 1.14k | { |
347 | 1.14k | int ret; |
348 | 1.14k | struct sockaddr_nl nladdr; |
349 | 1.14k | struct iovec iov = { |
350 | 1.14k | .iov_base = nlmsghdr, |
351 | 1.14k | .iov_len = nlmsghdr->nlmsg_len, |
352 | 1.14k | }; |
353 | 1.14k | struct msghdr msg = { |
354 | 1.14k | .msg_name = &nladdr, |
355 | 1.14k | .msg_namelen = sizeof(nladdr), |
356 | 1.14k | .msg_iov = &iov, |
357 | 1.14k | .msg_iovlen = 1, |
358 | 1.14k | }; |
359 | | |
360 | 1.14k | memset(&nladdr, 0, sizeof(nladdr)); |
361 | 1.14k | nladdr.nl_family = AF_NETLINK; |
362 | 1.14k | nladdr.nl_pid = 0; |
363 | 1.14k | nladdr.nl_groups = 0; |
364 | | |
365 | 1.14k | ret = sendmsg(fd, &msg, MSG_NOSIGNAL); |
366 | 1.14k | if (ret < 0) |
367 | 0 | return -1; |
368 | | |
369 | 1.14k | return ret; |
370 | 1.14k | } |
371 | | |
372 | | static int __ifaddrs_netlink_recv(int fd, unsigned int seq, int type, int af, |
373 | | __s32 netns_id, bool *netnsid_aware, |
374 | | int (*cb)(void *ctx, bool *netnsid_aware, |
375 | | struct nlmsghdr *h), |
376 | | void *ctx) |
377 | 1.14k | { |
378 | 1.14k | int r, property, ret; |
379 | 1.14k | char *buf; |
380 | 1.14k | struct nlmsghdr *hdr; |
381 | 1.14k | struct ifinfomsg *ifi_msg; |
382 | 1.14k | struct ifaddrmsg *ifa_msg; |
383 | 1.14k | union { |
384 | 1.14k | uint8_t buf[8192]; |
385 | 1.14k | struct { |
386 | 1.14k | struct nlmsghdr nlh; |
387 | 1.14k | struct rtgenmsg g; |
388 | 1.14k | } req; |
389 | 1.14k | struct nlmsghdr reply; |
390 | 1.14k | } u; |
391 | 1.14k | char getlink_buf[__NETLINK_ALIGN(sizeof(struct nlmsghdr)) + |
392 | 1.14k | __NETLINK_ALIGN(sizeof(struct ifinfomsg)) + |
393 | 1.14k | __NETLINK_ALIGN(1024)] = {0}; |
394 | 1.14k | char getaddr_buf[__NETLINK_ALIGN(sizeof(struct nlmsghdr)) + |
395 | 1.14k | __NETLINK_ALIGN(sizeof(struct ifaddrmsg)) + |
396 | 1.14k | __NETLINK_ALIGN(1024)] = {0}; |
397 | | |
398 | 1.14k | #pragma GCC diagnostic push |
399 | 1.14k | #pragma GCC diagnostic ignored "-Wcast-align" |
400 | 1.14k | if (type == RTM_GETLINK) { |
401 | 570 | buf = getlink_buf; |
402 | 570 | hdr = (struct nlmsghdr *)buf; |
403 | 570 | hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*ifi_msg)); |
404 | | |
405 | 570 | ifi_msg = (struct ifinfomsg *)__NLMSG_DATA(hdr); |
406 | 570 | ifi_msg->ifi_family = af; |
407 | | |
408 | 570 | property = IFLA_TARGET_NETNSID; |
409 | 570 | } else if (type == RTM_GETADDR) { |
410 | 570 | buf = getaddr_buf; |
411 | 570 | hdr = (struct nlmsghdr *)buf; |
412 | 570 | hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*ifa_msg)); |
413 | | |
414 | 570 | ifa_msg = (struct ifaddrmsg *)__NLMSG_DATA(hdr); |
415 | 570 | ifa_msg->ifa_family = af; |
416 | | |
417 | 570 | property = IFA_TARGET_NETNSID; |
418 | 570 | } else { |
419 | 0 | errno = EINVAL; |
420 | 0 | return -1; |
421 | 0 | } |
422 | 1.14k | #pragma GCC diagnostic pop |
423 | | |
424 | 1.14k | hdr->nlmsg_type = type; |
425 | 1.14k | hdr->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; |
426 | 1.14k | hdr->nlmsg_pid = 0; |
427 | 1.14k | hdr->nlmsg_seq = seq; |
428 | | |
429 | 1.14k | if (netns_id >= 0) |
430 | 0 | addattr(hdr, 1024, property, &netns_id, sizeof(netns_id)); |
431 | | |
432 | 1.14k | r = __ifaddrs_netlink_send(fd, hdr); |
433 | 1.14k | if (r < 0) |
434 | 0 | return -1; |
435 | | |
436 | 2.28k | for (;;) { |
437 | 2.28k | r = recv(fd, u.buf, sizeof(u.buf), MSG_DONTWAIT); |
438 | 2.28k | if (r <= 0) |
439 | 0 | return -1; |
440 | | |
441 | 2.28k | #pragma GCC diagnostic push |
442 | 2.28k | #pragma GCC diagnostic ignored "-Wcast-align" |
443 | 4.56k | for (hdr = &u.reply; __NLMSG_OK(hdr, (void *)&u.buf[r]); |
444 | 3.42k | hdr = __NLMSG_NEXT(hdr)) { |
445 | 3.42k | if (hdr->nlmsg_type == NLMSG_DONE) |
446 | 1.14k | return 0; |
447 | | |
448 | 2.28k | if (hdr->nlmsg_type == NLMSG_ERROR) { |
449 | 0 | errno = EINVAL; |
450 | 0 | return -1; |
451 | 0 | } |
452 | | |
453 | 2.28k | ret = cb(ctx, netnsid_aware, hdr); |
454 | 2.28k | if (ret) |
455 | 0 | return ret; |
456 | 2.28k | } |
457 | 2.28k | #pragma GCC diagnostic pop |
458 | 2.28k | } |
459 | 1.14k | } |
460 | | |
461 | | static int __rtnl_enumerate(int link_af, int addr_af, __s32 netns_id, |
462 | | bool *netnsid_aware, |
463 | | int (*cb)(void *ctx, bool *netnsid_aware, struct nlmsghdr *h), |
464 | | void *ctx) |
465 | 570 | { |
466 | 570 | int fd, r, saved_errno; |
467 | 570 | bool getaddr_netnsid_aware = false, getlink_netnsid_aware = false; |
468 | | |
469 | 570 | fd = socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE); |
470 | 570 | if (fd < 0) |
471 | 0 | return -1; |
472 | | |
473 | 570 | r = setsockopt(fd, SOL_NETLINK, NETLINK_GET_STRICT_CHK, &(int){1}, |
474 | 570 | sizeof(int)); |
475 | 570 | if (r < 0 && netns_id >= 0) { |
476 | 0 | close(fd); |
477 | 0 | *netnsid_aware = false; |
478 | 0 | return -1; |
479 | 0 | } |
480 | | |
481 | 570 | r = __ifaddrs_netlink_recv(fd, 1, RTM_GETLINK, link_af, netns_id, |
482 | 570 | &getlink_netnsid_aware, cb, ctx); |
483 | 570 | if (!r) |
484 | 570 | r = __ifaddrs_netlink_recv(fd, 2, RTM_GETADDR, addr_af, netns_id, |
485 | 570 | &getaddr_netnsid_aware, cb, ctx); |
486 | | |
487 | 570 | saved_errno = errno; |
488 | 570 | close(fd); |
489 | 570 | errno = saved_errno; |
490 | | |
491 | 570 | if (getaddr_netnsid_aware && getlink_netnsid_aware) |
492 | 0 | *netnsid_aware = true; |
493 | 570 | else |
494 | 570 | *netnsid_aware = false; |
495 | | |
496 | 570 | return r; |
497 | 570 | } |
498 | | |
499 | | void netns_freeifaddrs(struct netns_ifaddrs *ifp) |
500 | 570 | { |
501 | 570 | struct netns_ifaddrs *n; |
502 | | |
503 | 2.85k | while (ifp) { |
504 | 2.28k | n = ifp->ifa_next; |
505 | 2.28k | free(ifp); |
506 | 2.28k | ifp = n; |
507 | 2.28k | } |
508 | 570 | } |
509 | | |
510 | | int netns_getifaddrs(struct netns_ifaddrs **ifap, __s32 netns_id, |
511 | | bool *netnsid_aware) |
512 | 570 | { |
513 | 570 | int r, saved_errno; |
514 | 570 | struct ifaddrs_ctx _ctx; |
515 | 570 | struct ifaddrs_ctx *ctx = &_ctx; |
516 | | |
517 | 570 | memset(ctx, 0, sizeof *ctx); |
518 | | |
519 | 570 | r = __rtnl_enumerate(AF_UNSPEC, AF_UNSPEC, netns_id, netnsid_aware, |
520 | 570 | nl_msg_to_ifaddr, ctx); |
521 | 570 | saved_errno = errno; |
522 | 570 | if (r < 0) |
523 | 0 | netns_freeifaddrs(&ctx->first->ifa); |
524 | 570 | else |
525 | 570 | *ifap = &ctx->first->ifa; |
526 | 570 | errno = saved_errno; |
527 | | |
528 | 570 | return r; |
529 | 570 | } |