#define _GNU_SOURCE
+#include <arpa/inet.h>
#include <errno.h>
+#include <linux/if.h>
+#include <linux/if_addr.h>
+#include <linux/if_link.h>
+#include <linux/if_packet.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
-#include <net/if.h>
+#include <linux/types.h>
+#include <net/ethernet.h>
#include <netinet/in.h>
+#include <stdbool.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
-#include "ifaddrs.h"
+#include "nl.h"
+#include "macro.h"
+#include "netns_ifaddrs.h"
+
+#ifndef NETNS_RTA
+#define NETNS_RTA(r) \
+ ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct rtgenmsg))))
+#endif
#define IFADDRS_HASH_SIZE 64
#define __RTA_DATA(rta) ((void *)((char *)(rta) + sizeof(struct rtattr)))
-/* getifaddrs() reports hardware addresses with PF_PACKET that implies
- * struct sockaddr_ll. But e.g. Infiniband socket address length is
- * longer than sockaddr_ll.ssl_addr[8] can hold. Use this hack struct
- * to extend ssl_addr - callers should be able to still use it. */
+/* getifaddrs() reports hardware addresses with PF_PACKET that implies struct
+ * sockaddr_ll. But e.g. Infiniband socket address length is longer than
+ * sockaddr_ll.ssl_addr[8] can hold. Use this hack struct to extend ssl_addr -
+ * callers should be able to still use it.
+ */
struct sockaddr_ll_hack {
unsigned short sll_family, sll_protocol;
int sll_ifindex;
};
struct ifaddrs_storage {
- struct ifaddrs ifa;
+ struct netns_ifaddrs ifa;
struct ifaddrs_storage *hash_next;
union sockany addr, netmask, ifu;
unsigned int index;
struct ifaddrs_storage *hash[IFADDRS_HASH_SIZE];
};
-void freeifaddrs(struct ifaddrs *ifp)
-{
- struct ifaddrs *n;
-
- while (ifp) {
- n = ifp->ifa_next;
- free(ifp);
- ifp = n;
- }
-}
-
static void copy_addr(struct sockaddr **r, int af, union sockany *sa,
void *addr, size_t addrlen, int ifindex)
{
case AF_INET6:
dst = (uint8_t *)&sa->v6.sin6_addr;
len = 16;
- if (__IN6_IS_ADDR_LINKLOCAL(addr) || __IN6_IS_ADDR_MC_LINKLOCAL(addr))
+ if (__IN6_IS_ADDR_LINKLOCAL(addr) ||
+ __IN6_IS_ADDR_MC_LINKLOCAL(addr))
sa->v6.sin6_scope_id = ifindex;
break;
default:
*r = &sa->sa;
}
-static int nl_msg_to_ifaddr(void *pctx, struct nlmsghdr *h)
+static int nl_msg_to_ifaddr(void *pctx, bool *netnsid_aware, struct nlmsghdr *h)
{
struct ifaddrs_storage *ifs, *ifs0;
struct rtattr *rta;
if (h->nlmsg_type == RTM_NEWLINK) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-align"
-
for (rta = __NLMSG_RTA(h, sizeof(*ifi)); __NLMSG_RTAOK(rta, h);
rta = __RTA_NEXT(rta)) {
if (rta->rta_type != IFLA_STATS)
stats_len = __RTA_DATALEN(rta);
break;
}
-
#pragma GCC diagnostic pop
} else {
for (ifs0 = ctx->hash[ifa->ifa_index % IFADDRS_HASH_SIZE]; ifs0;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-align"
-
if (h->nlmsg_type == RTM_NEWLINK) {
ifs->index = ifi->ifi_index;
+ ifs->ifa.ifa_ifindex = ifi->ifi_index;
ifs->ifa.ifa_flags = ifi->ifi_flags;
for (rta = __NLMSG_RTA(h, sizeof(*ifi)); __NLMSG_RTAOK(rta, h);
ifi->ifi_index, ifi->ifi_type);
break;
case IFLA_BROADCAST:
- copy_lladdr(&ifs->ifa.ifa_broadaddr, &ifs->ifu,
+ copy_lladdr(&ifs->ifa.__ifa_broadaddr, &ifs->ifu,
__RTA_DATA(rta), __RTA_DATALEN(rta),
ifi->ifi_index, ifi->ifi_type);
break;
memcpy(ifs->ifa.ifa_data, __RTA_DATA(rta),
__RTA_DATALEN(rta));
break;
+ case IFLA_MTU:
+ memcpy(&ifs->ifa.ifa_mtu, __RTA_DATA(rta),
+ sizeof(int));
+ break;
+ case IFLA_TARGET_NETNSID:
+ *netnsid_aware = true;
+ break;
}
}
}
} else {
ifs->ifa.ifa_name = ifs0->ifa.ifa_name;
+ ifs->ifa.ifa_mtu = ifs0->ifa.ifa_mtu;
+ ifs->ifa.ifa_ifindex = ifs0->ifa.ifa_ifindex;
ifs->ifa.ifa_flags = ifs0->ifa.ifa_flags;
for (rta = __NLMSG_RTA(h, sizeof(*ifa)); __NLMSG_RTAOK(rta, h);
switch (rta->rta_type) {
case IFA_ADDRESS:
/* If ifa_addr is already set we, received an
- * IFA_LOCAL before so treat this as destination
- * address.
+ * IFA_LOCAL before so treat this as
+ * destination address.
*/
if (ifs->ifa.ifa_addr)
- copy_addr(&ifs->ifa.ifa_dstaddr,
+ copy_addr(&ifs->ifa.__ifa_dstaddr,
ifa->ifa_family, &ifs->ifu,
__RTA_DATA(rta),
__RTA_DATALEN(rta),
ifa->ifa_index);
break;
case IFA_BROADCAST:
- copy_addr(&ifs->ifa.ifa_broadaddr,
+ copy_addr(&ifs->ifa.__ifa_broadaddr,
ifa->ifa_family, &ifs->ifu,
__RTA_DATA(rta), __RTA_DATALEN(rta),
ifa->ifa_index);
break;
case IFA_LOCAL:
/* If ifa_addr is set and we get IFA_LOCAL,
- * assume we have a point-to-point network. Move
- * address to correct field.
+ * assume we have a point-to-point network.
+ * Move address to correct field.
*/
if (ifs->ifa.ifa_addr) {
ifs->ifu = ifs->addr;
- ifs->ifa.ifa_dstaddr = &ifs->ifu.sa;
+ ifs->ifa.__ifa_dstaddr = &ifs->ifu.sa;
memset(&ifs->addr, 0, sizeof(ifs->addr));
}
ifs->ifa.ifa_name = ifs->name;
}
break;
+ case IFA_TARGET_NETNSID:
+ *netnsid_aware = true;
+ break;
}
}
- if (ifs->ifa.ifa_addr)
+ if (ifs->ifa.ifa_addr) {
gen_netmask(&ifs->ifa.ifa_netmask, ifa->ifa_family,
&ifs->netmask, ifa->ifa_prefixlen);
+ ifs->ifa.ifa_prefixlen = ifa->ifa_prefixlen;
+ }
}
-
#pragma GCC diagnostic pop
if (ifs->ifa.ifa_name) {
return 0;
}
-static int __nl_recv(int fd, unsigned int seq, int type, int af,
- int (*cb)(void *ctx, struct nlmsghdr *h),
- void *ctx)
+static int __ifaddrs_netlink_send(int fd, struct nlmsghdr *nlmsghdr)
+{
+ int ret;
+ struct sockaddr_nl nladdr;
+ struct iovec iov = {
+ .iov_base = nlmsghdr,
+ .iov_len = nlmsghdr->nlmsg_len,
+ };
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+ nladdr.nl_pid = 0;
+ nladdr.nl_groups = 0;
+
+ ret = sendmsg(fd, &msg, MSG_NOSIGNAL);
+ if (ret < 0)
+ return -1;
+
+ return ret;
+}
+
+static int __ifaddrs_netlink_recv(int fd, unsigned int seq, int type, int af,
+ __s32 netns_id, bool *netnsid_aware,
+ int (*cb)(void *ctx, bool *netnsid_aware,
+ struct nlmsghdr *h),
+ void *ctx)
{
- struct nlmsghdr *h;
+ char getlink_buf[__NETLINK_ALIGN(sizeof(struct nlmsghdr)) +
+ __NETLINK_ALIGN(sizeof(struct ifinfomsg)) +
+ __NETLINK_ALIGN(1024)];
+ char getaddr_buf[__NETLINK_ALIGN(sizeof(struct nlmsghdr)) +
+ __NETLINK_ALIGN(sizeof(struct ifaddrmsg)) +
+ __NETLINK_ALIGN(1024)];
+ char *buf;
+ struct nlmsghdr *hdr;
+ struct ifinfomsg *ifi_msg;
+ struct ifaddrmsg *ifa_msg;
union {
uint8_t buf[8192];
struct {
} req;
struct nlmsghdr reply;
} u;
- int r, ret;
-
- memset(&u.req, 0, sizeof(u.req));
- u.req.nlh.nlmsg_len = sizeof(u.req);
- u.req.nlh.nlmsg_type = type;
- u.req.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
- u.req.nlh.nlmsg_seq = seq;
- u.req.g.rtgen_family = af;
- r = send(fd, &u.req, sizeof(u.req), 0);
+ int r, property, ret;
+
+ if (type == RTM_GETLINK)
+ buf = getlink_buf;
+ else if (type == RTM_GETADDR)
+ buf = getaddr_buf;
+ else
+ return -1;
+
+ memset(buf, 0, sizeof(*buf));
+ hdr = (struct nlmsghdr *)buf;
+ if (type == RTM_GETLINK)
+ ifi_msg = (struct ifinfomsg *)__NLMSG_DATA(hdr);
+ else
+ ifa_msg = (struct ifaddrmsg *)__NLMSG_DATA(hdr);
+
+ if (type == RTM_GETLINK)
+ hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*ifi_msg));
+ else
+ hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*ifa_msg));
+
+ hdr->nlmsg_type = type;
+ hdr->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
+ hdr->nlmsg_pid = 0;
+ hdr->nlmsg_seq = seq;
+ if (type == RTM_GETLINK)
+ ifi_msg->ifi_family = af;
+ else
+ ifa_msg->ifa_family = af;
+
+ errno = EINVAL;
+ if (type == RTM_GETLINK)
+ property = IFLA_TARGET_NETNSID;
+ else if (type == RTM_GETADDR)
+ property = IFA_TARGET_NETNSID;
+ else
+ return -1;
+
+ if (netns_id >= 0)
+ addattr(hdr, 1024, property, &netns_id, sizeof(netns_id));
+
+ r = __ifaddrs_netlink_send(fd, hdr);
if (r < 0)
- return r;
+ return -1;
for (;;) {
r = recv(fd, u.buf, sizeof(u.buf), MSG_DONTWAIT);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-align"
- for (h = &u.reply; __NLMSG_OK(h, (void *)&u.buf[r]);
- h = __NLMSG_NEXT(h)) {
- if (h->nlmsg_type == NLMSG_DONE)
+ for (hdr = &u.reply; __NLMSG_OK(hdr, (void *)&u.buf[r]);
+ hdr = __NLMSG_NEXT(hdr)) {
+ if (hdr->nlmsg_type == NLMSG_DONE)
return 0;
- if (h->nlmsg_type == NLMSG_ERROR) {
+ if (hdr->nlmsg_type == NLMSG_ERROR) {
errno = EINVAL;
return -1;
}
- ret = cb(ctx, h);
+ ret = cb(ctx, netnsid_aware, hdr);
if (ret)
return ret;
}
}
}
-static int __rtnl_enumerate(int link_af, int addr_af,
- int (*cb)(void *ctx, struct nlmsghdr *h), void *ctx)
+static int __rtnl_enumerate(int link_af, int addr_af, __s32 netns_id,
+ bool *netnsid_aware,
+ int (*cb)(void *ctx, bool *netnsid_aware, struct nlmsghdr *h),
+ void *ctx)
{
int fd, r, saved_errno;
+ bool getaddr_netnsid_aware = false, getlink_netnsid_aware = false;
fd = socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
if (fd < 0)
return -1;
- r = __nl_recv(fd, 1, RTM_GETLINK, link_af, cb, ctx);
+ r = __ifaddrs_netlink_recv(fd, 1, RTM_GETLINK, link_af, netns_id,
+ &getlink_netnsid_aware, cb, ctx);
if (!r)
- r = __nl_recv(fd, 2, RTM_GETADDR, addr_af, cb, ctx);
+ r = __ifaddrs_netlink_recv(fd, 2, RTM_GETADDR, addr_af, netns_id,
+ &getaddr_netnsid_aware, cb, ctx);
saved_errno = errno;
close(fd);
errno = saved_errno;
+ if (getaddr_netnsid_aware && getlink_netnsid_aware)
+ *netnsid_aware = true;
+ else
+ *netnsid_aware = false;
+
return r;
}
-int getifaddrs(struct ifaddrs **ifap)
+/* Get a pointer to the address structure from a sockaddr. */
+static void *get_addr_ptr(struct sockaddr *sockaddr_ptr)
+{
+ if (sockaddr_ptr->sa_family == AF_INET)
+ return &((struct sockaddr_in *)sockaddr_ptr)->sin_addr;
+
+ if (sockaddr_ptr->sa_family == AF_INET6)
+ return &((struct sockaddr_in6 *)sockaddr_ptr)->sin6_addr;
+
+ return NULL;
+}
+
+static char *get_packet_address(struct sockaddr *sockaddr_ptr, char *buf, size_t buflen)
+{
+ char *slider = buf;
+ unsigned char *m = ((struct sockaddr_ll *)sockaddr_ptr)->sll_addr;
+ unsigned char n = ((struct sockaddr_ll *)sockaddr_ptr)->sll_halen;
+
+ for (unsigned char i = 0; i < n; i++) {
+ int ret;
+
+ ret = snprintf(slider, buflen, "%02x%s", m[i], (i + 1) < n ? ":" : "");
+ if (ret < 0 || (size_t)ret >= buflen)
+ return NULL;
+
+ buflen -= ret;
+ slider = (slider + ret);
+ }
+
+ return buf;
+}
+
+void netns_freeifaddrs(struct netns_ifaddrs *ifp)
+{
+ struct netns_ifaddrs *n;
+
+ while (ifp) {
+ n = ifp->ifa_next;
+ free(ifp);
+ ifp = n;
+ }
+}
+
+int netns_getifaddrs(struct netns_ifaddrs **ifap, __s32 netns_id,
+ bool *netnsid_aware)
{
int r, saved_errno;
struct ifaddrs_ctx _ctx;
memset(ctx, 0, sizeof *ctx);
- r = __rtnl_enumerate(AF_UNSPEC, AF_UNSPEC, nl_msg_to_ifaddr, ctx);
+ r = __rtnl_enumerate(AF_UNSPEC, AF_UNSPEC, netns_id, netnsid_aware,
+ nl_msg_to_ifaddr, ctx);
saved_errno = errno;
if (r < 0)
- freeifaddrs(&ctx->first->ifa);
+ netns_freeifaddrs(&ctx->first->ifa);
else
*ifap = &ctx->first->ifa;
errno = saved_errno;