]>
git.proxmox.com Git - mirror_lxc.git/blob - src/include/netns_ifaddrs.c
5 #include <linux/if_addr.h>
6 #include <linux/if_link.h>
7 #include <linux/if_packet.h>
8 #include <linux/netlink.h>
9 #include <linux/rtnetlink.h>
10 #include <linux/types.h>
11 #include <net/ethernet.h>
12 #include <netinet/in.h>
17 #include <sys/socket.h>
22 #include "netns_ifaddrs.h"
25 #define NETNS_RTA(r) \
26 ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct rtgenmsg))))
29 #define IFADDRS_HASH_SIZE 64
31 #define __NETLINK_ALIGN(len) (((len) + 3) & ~3)
33 #define __NLMSG_OK(nlh, end) \
34 ((char *)(end) - (char *)(nlh) >= sizeof(struct nlmsghdr))
36 #define __NLMSG_NEXT(nlh) \
37 (struct nlmsghdr *)((char *)(nlh) + __NETLINK_ALIGN((nlh)->nlmsg_len))
39 #define __NLMSG_DATA(nlh) ((void *)((char *)(nlh) + sizeof(struct nlmsghdr)))
41 #define __NLMSG_DATAEND(nlh) ((char *)(nlh) + (nlh)->nlmsg_len)
43 #define __NLMSG_RTA(nlh, len) \
44 ((void *)((char *)(nlh) + sizeof(struct nlmsghdr) + \
45 __NETLINK_ALIGN(len)))
47 #define __RTA_DATALEN(rta) ((rta)->rta_len - sizeof(struct rtattr))
49 #define __RTA_NEXT(rta) \
50 (struct rtattr *)((char *)(rta) + __NETLINK_ALIGN((rta)->rta_len))
52 #define __RTA_OK(nlh, end) \
53 ((char *)(end) - (char *)(rta) >= sizeof(struct rtattr))
55 #define __NLMSG_RTAOK(rta, nlh) __RTA_OK(rta, __NLMSG_DATAEND(nlh))
57 #define __IN6_IS_ADDR_LINKLOCAL(a) \
58 ((((uint8_t *)(a))[0]) == 0xfe && (((uint8_t *)(a))[1] & 0xc0) == 0x80)
60 #define __IN6_IS_ADDR_MC_LINKLOCAL(a) \
61 (IN6_IS_ADDR_MULTICAST(a) && ((((uint8_t *)(a))[1] & 0xf) == 0x2))
63 #define __RTA_DATA(rta) ((void *)((char *)(rta) + sizeof(struct rtattr)))
65 /* getifaddrs() reports hardware addresses with PF_PACKET that implies struct
66 * sockaddr_ll. But e.g. Infiniband socket address length is longer than
67 * sockaddr_ll.ssl_addr[8] can hold. Use this hack struct to extend ssl_addr -
68 * callers should be able to still use it.
70 struct sockaddr_ll_hack
{
71 unsigned short sll_family
, sll_protocol
;
73 unsigned short sll_hatype
;
74 unsigned char sll_pkttype
, sll_halen
;
75 unsigned char sll_addr
[24];
80 struct sockaddr_ll_hack ll
;
81 struct sockaddr_in v4
;
82 struct sockaddr_in6 v6
;
85 struct ifaddrs_storage
{
86 struct netns_ifaddrs ifa
;
87 struct ifaddrs_storage
*hash_next
;
88 union sockany addr
, netmask
, ifu
;
90 char name
[IFNAMSIZ
+ 1];
94 struct ifaddrs_storage
*first
;
95 struct ifaddrs_storage
*last
;
96 struct ifaddrs_storage
*hash
[IFADDRS_HASH_SIZE
];
99 static void copy_addr(struct sockaddr
**r
, int af
, union sockany
*sa
,
100 void *addr
, size_t addrlen
, int ifindex
)
107 dst
= (uint8_t *)&sa
->v4
.sin_addr
;
111 dst
= (uint8_t *)&sa
->v6
.sin6_addr
;
113 if (__IN6_IS_ADDR_LINKLOCAL(addr
) ||
114 __IN6_IS_ADDR_MC_LINKLOCAL(addr
))
115 sa
->v6
.sin6_scope_id
= ifindex
;
124 sa
->sa
.sa_family
= af
;
126 memcpy(dst
, addr
, len
);
131 static void gen_netmask(struct sockaddr
**r
, int af
, union sockany
*sa
,
134 uint8_t addr
[16] = {0};
137 if ((size_t)prefixlen
> 8 * sizeof(addr
))
138 prefixlen
= 8 * sizeof(addr
);
142 memset(addr
, 0xff, i
);
144 if ((size_t)i
< sizeof(addr
))
145 addr
[i
++] = 0xff << (8 - (prefixlen
% 8));
147 copy_addr(r
, af
, sa
, addr
, sizeof(addr
), 0);
150 static void copy_lladdr(struct sockaddr
**r
, union sockany
*sa
, void *addr
,
151 size_t addrlen
, int ifindex
, unsigned short hatype
)
153 if (addrlen
> sizeof(sa
->ll
.sll_addr
))
156 sa
->ll
.sll_family
= AF_PACKET
;
157 sa
->ll
.sll_ifindex
= ifindex
;
158 sa
->ll
.sll_hatype
= hatype
;
159 sa
->ll
.sll_halen
= addrlen
;
161 memcpy(sa
->ll
.sll_addr
, addr
, addrlen
);
166 static int nl_msg_to_ifaddr(void *pctx
, bool *netnsid_aware
, struct nlmsghdr
*h
)
168 struct ifaddrs_storage
*ifs
, *ifs0
;
171 struct ifinfomsg
*ifi
= __NLMSG_DATA(h
);
172 struct ifaddrmsg
*ifa
= __NLMSG_DATA(h
);
173 struct ifaddrs_ctx
*ctx
= pctx
;
175 if (h
->nlmsg_type
== RTM_NEWLINK
) {
176 #pragma GCC diagnostic push
177 #pragma GCC diagnostic ignored "-Wcast-align"
178 for (rta
= __NLMSG_RTA(h
, sizeof(*ifi
)); __NLMSG_RTAOK(rta
, h
);
179 rta
= __RTA_NEXT(rta
)) {
180 if (rta
->rta_type
!= IFLA_STATS
)
183 stats_len
= __RTA_DATALEN(rta
);
186 #pragma GCC diagnostic pop
188 for (ifs0
= ctx
->hash
[ifa
->ifa_index
% IFADDRS_HASH_SIZE
]; ifs0
;
189 ifs0
= ifs0
->hash_next
)
190 if (ifs0
->index
== ifa
->ifa_index
)
196 ifs
= calloc(1, sizeof(struct ifaddrs_storage
) + stats_len
);
202 #pragma GCC diagnostic push
203 #pragma GCC diagnostic ignored "-Wcast-align"
204 if (h
->nlmsg_type
== RTM_NEWLINK
) {
205 ifs
->index
= ifi
->ifi_index
;
206 ifs
->ifa
.ifa_ifindex
= ifi
->ifi_index
;
207 ifs
->ifa
.ifa_flags
= ifi
->ifi_flags
;
209 for (rta
= __NLMSG_RTA(h
, sizeof(*ifi
)); __NLMSG_RTAOK(rta
, h
);
210 rta
= __RTA_NEXT(rta
)) {
211 switch (rta
->rta_type
) {
213 if (__RTA_DATALEN(rta
) < sizeof(ifs
->name
)) {
214 memcpy(ifs
->name
, __RTA_DATA(rta
),
216 ifs
->ifa
.ifa_name
= ifs
->name
;
220 copy_lladdr(&ifs
->ifa
.ifa_addr
, &ifs
->addr
,
221 __RTA_DATA(rta
), __RTA_DATALEN(rta
),
222 ifi
->ifi_index
, ifi
->ifi_type
);
225 copy_lladdr(&ifs
->ifa
.__ifa_broadaddr
, &ifs
->ifu
,
226 __RTA_DATA(rta
), __RTA_DATALEN(rta
),
227 ifi
->ifi_index
, ifi
->ifi_type
);
230 ifs
->ifa
.ifa_data
= (void *)(ifs
+ 1);
231 memcpy(ifs
->ifa
.ifa_data
, __RTA_DATA(rta
),
235 memcpy(&ifs
->ifa
.ifa_mtu
, __RTA_DATA(rta
),
238 case IFLA_TARGET_NETNSID
:
239 *netnsid_aware
= true;
244 if (ifs
->ifa
.ifa_name
) {
245 unsigned int bucket
= ifs
->index
% IFADDRS_HASH_SIZE
;
246 ifs
->hash_next
= ctx
->hash
[bucket
];
247 ctx
->hash
[bucket
] = ifs
;
250 ifs
->ifa
.ifa_name
= ifs0
->ifa
.ifa_name
;
251 ifs
->ifa
.ifa_mtu
= ifs0
->ifa
.ifa_mtu
;
252 ifs
->ifa
.ifa_ifindex
= ifs0
->ifa
.ifa_ifindex
;
253 ifs
->ifa
.ifa_flags
= ifs0
->ifa
.ifa_flags
;
255 for (rta
= __NLMSG_RTA(h
, sizeof(*ifa
)); __NLMSG_RTAOK(rta
, h
);
256 rta
= __RTA_NEXT(rta
)) {
257 switch (rta
->rta_type
) {
259 /* If ifa_addr is already set we, received an
260 * IFA_LOCAL before so treat this as
261 * destination address.
263 if (ifs
->ifa
.ifa_addr
)
264 copy_addr(&ifs
->ifa
.__ifa_dstaddr
,
265 ifa
->ifa_family
, &ifs
->ifu
,
270 copy_addr(&ifs
->ifa
.ifa_addr
,
271 ifa
->ifa_family
, &ifs
->addr
,
277 copy_addr(&ifs
->ifa
.__ifa_broadaddr
,
278 ifa
->ifa_family
, &ifs
->ifu
,
279 __RTA_DATA(rta
), __RTA_DATALEN(rta
),
283 /* If ifa_addr is set and we get IFA_LOCAL,
284 * assume we have a point-to-point network.
285 * Move address to correct field.
287 if (ifs
->ifa
.ifa_addr
) {
288 ifs
->ifu
= ifs
->addr
;
289 ifs
->ifa
.__ifa_dstaddr
= &ifs
->ifu
.sa
;
291 memset(&ifs
->addr
, 0, sizeof(ifs
->addr
));
294 copy_addr(&ifs
->ifa
.ifa_addr
, ifa
->ifa_family
,
295 &ifs
->addr
, __RTA_DATA(rta
),
296 __RTA_DATALEN(rta
), ifa
->ifa_index
);
299 if (__RTA_DATALEN(rta
) < sizeof(ifs
->name
)) {
300 memcpy(ifs
->name
, __RTA_DATA(rta
),
302 ifs
->ifa
.ifa_name
= ifs
->name
;
305 case IFA_TARGET_NETNSID
:
306 *netnsid_aware
= true;
311 if (ifs
->ifa
.ifa_addr
) {
312 gen_netmask(&ifs
->ifa
.ifa_netmask
, ifa
->ifa_family
,
313 &ifs
->netmask
, ifa
->ifa_prefixlen
);
314 ifs
->ifa
.ifa_prefixlen
= ifa
->ifa_prefixlen
;
317 #pragma GCC diagnostic pop
319 if (ifs
->ifa
.ifa_name
) {
324 ctx
->last
->ifa
.ifa_next
= &ifs
->ifa
;
334 static int __ifaddrs_netlink_send(int fd
, struct nlmsghdr
*nlmsghdr
)
337 struct sockaddr_nl nladdr
;
339 .iov_base
= nlmsghdr
,
340 .iov_len
= nlmsghdr
->nlmsg_len
,
342 struct msghdr msg
= {
344 .msg_namelen
= sizeof(nladdr
),
349 memset(&nladdr
, 0, sizeof(nladdr
));
350 nladdr
.nl_family
= AF_NETLINK
;
352 nladdr
.nl_groups
= 0;
354 ret
= sendmsg(fd
, &msg
, MSG_NOSIGNAL
);
361 static int __ifaddrs_netlink_recv(int fd
, unsigned int seq
, int type
, int af
,
362 __s32 netns_id
, bool *netnsid_aware
,
363 int (*cb
)(void *ctx
, bool *netnsid_aware
,
367 char getlink_buf
[__NETLINK_ALIGN(sizeof(struct nlmsghdr
)) +
368 __NETLINK_ALIGN(sizeof(struct ifinfomsg
)) +
369 __NETLINK_ALIGN(1024)];
370 char getaddr_buf
[__NETLINK_ALIGN(sizeof(struct nlmsghdr
)) +
371 __NETLINK_ALIGN(sizeof(struct ifaddrmsg
)) +
372 __NETLINK_ALIGN(1024)];
374 struct nlmsghdr
*hdr
;
375 struct ifinfomsg
*ifi_msg
;
376 struct ifaddrmsg
*ifa_msg
;
383 struct nlmsghdr reply
;
385 int r
, property
, ret
;
387 if (type
== RTM_GETLINK
)
389 else if (type
== RTM_GETADDR
)
394 memset(buf
, 0, sizeof(*buf
));
395 hdr
= (struct nlmsghdr
*)buf
;
396 if (type
== RTM_GETLINK
)
397 ifi_msg
= (struct ifinfomsg
*)__NLMSG_DATA(hdr
);
399 ifa_msg
= (struct ifaddrmsg
*)__NLMSG_DATA(hdr
);
401 if (type
== RTM_GETLINK
)
402 hdr
->nlmsg_len
= NLMSG_LENGTH(sizeof(*ifi_msg
));
404 hdr
->nlmsg_len
= NLMSG_LENGTH(sizeof(*ifa_msg
));
406 hdr
->nlmsg_type
= type
;
407 hdr
->nlmsg_flags
= NLM_F_DUMP
| NLM_F_REQUEST
;
409 hdr
->nlmsg_seq
= seq
;
410 if (type
== RTM_GETLINK
)
411 ifi_msg
->ifi_family
= af
;
413 ifa_msg
->ifa_family
= af
;
416 if (type
== RTM_GETLINK
)
417 property
= IFLA_TARGET_NETNSID
;
418 else if (type
== RTM_GETADDR
)
419 property
= IFA_TARGET_NETNSID
;
424 addattr(hdr
, 1024, property
, &netns_id
, sizeof(netns_id
));
426 r
= __ifaddrs_netlink_send(fd
, hdr
);
431 r
= recv(fd
, u
.buf
, sizeof(u
.buf
), MSG_DONTWAIT
);
435 #pragma GCC diagnostic push
436 #pragma GCC diagnostic ignored "-Wcast-align"
437 for (hdr
= &u
.reply
; __NLMSG_OK(hdr
, (void *)&u
.buf
[r
]);
438 hdr
= __NLMSG_NEXT(hdr
)) {
439 if (hdr
->nlmsg_type
== NLMSG_DONE
)
442 if (hdr
->nlmsg_type
== NLMSG_ERROR
) {
447 ret
= cb(ctx
, netnsid_aware
, hdr
);
451 #pragma GCC diagnostic pop
455 static int __rtnl_enumerate(int link_af
, int addr_af
, __s32 netns_id
,
457 int (*cb
)(void *ctx
, bool *netnsid_aware
, struct nlmsghdr
*h
),
460 int fd
, r
, saved_errno
;
461 bool getaddr_netnsid_aware
= false, getlink_netnsid_aware
= false;
463 fd
= socket(PF_NETLINK
, SOCK_RAW
| SOCK_CLOEXEC
, NETLINK_ROUTE
);
467 r
= __ifaddrs_netlink_recv(fd
, 1, RTM_GETLINK
, link_af
, netns_id
,
468 &getlink_netnsid_aware
, cb
, ctx
);
470 r
= __ifaddrs_netlink_recv(fd
, 2, RTM_GETADDR
, addr_af
, netns_id
,
471 &getaddr_netnsid_aware
, cb
, ctx
);
477 if (getaddr_netnsid_aware
&& getlink_netnsid_aware
)
478 *netnsid_aware
= true;
480 *netnsid_aware
= false;
485 /* Get a pointer to the address structure from a sockaddr. */
486 static void *get_addr_ptr(struct sockaddr
*sockaddr_ptr
)
488 if (sockaddr_ptr
->sa_family
== AF_INET
)
489 return &((struct sockaddr_in
*)sockaddr_ptr
)->sin_addr
;
491 if (sockaddr_ptr
->sa_family
== AF_INET6
)
492 return &((struct sockaddr_in6
*)sockaddr_ptr
)->sin6_addr
;
497 static char *get_packet_address(struct sockaddr
*sockaddr_ptr
, char *buf
, size_t buflen
)
500 unsigned char *m
= ((struct sockaddr_ll
*)sockaddr_ptr
)->sll_addr
;
501 unsigned char n
= ((struct sockaddr_ll
*)sockaddr_ptr
)->sll_halen
;
503 for (unsigned char i
= 0; i
< n
; i
++) {
506 ret
= snprintf(slider
, buflen
, "%02x%s", m
[i
], (i
+ 1) < n
? ":" : "");
507 if (ret
< 0 || (size_t)ret
>= buflen
)
511 slider
= (slider
+ ret
);
517 void netns_freeifaddrs(struct netns_ifaddrs
*ifp
)
519 struct netns_ifaddrs
*n
;
528 int netns_getifaddrs(struct netns_ifaddrs
**ifap
, __s32 netns_id
,
532 struct ifaddrs_ctx _ctx
;
533 struct ifaddrs_ctx
*ctx
= &_ctx
;
535 memset(ctx
, 0, sizeof *ctx
);
537 r
= __rtnl_enumerate(AF_UNSPEC
, AF_UNSPEC
, netns_id
, netnsid_aware
,
538 nl_msg_to_ifaddr
, ctx
);
541 netns_freeifaddrs(&ctx
->first
->ifa
);
543 *ifap
= &ctx
->first
->ifa
;