Skip to content

Commit

Permalink
netlink RTM_GETADDR: add support for AF_INET6 and AF_UNSPEC
Browse files Browse the repository at this point in the history
This adds netlink support for reporting IPv6 interface addresses via the
RTM_GETADDR request. It also handles the AF_UNSPEC address family by reporting
both IPv4 and IPv6 addresses.
  • Loading branch information
wjhun committed Jun 13, 2023
1 parent a507f9b commit 2e9393b
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 39 deletions.
52 changes: 41 additions & 11 deletions src/unix/netlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -359,10 +359,10 @@ static void nl_enqueue_ifinfo(nlsock s, u16 type, u16 flags, u32 seq, u32 pid, s
}

static void nl_enqueue_ifaddr(nlsock s, u16 type, u16 flags, u32 seq, u32 pid, struct netif *netif,
ip4_addr_t addr, ip4_addr_t netmask)
int family, void *addr, int addr_len, int prefix_len)
{
int resp_len = NLMSG_ALIGN(sizeof(struct nlmsghdr) + sizeof(struct ifaddrmsg) +
RTA_SPACE(sizeof(ip4_addr_t)) + RTA_SPACE(sizeof(netif->name) + 2));
RTA_SPACE(addr_len) + RTA_SPACE(sizeof(netif->name) + 2));
struct nlmsghdr *hdr = allocate(s->sock.h, resp_len);
if (hdr == INVALID_ADDRESS) {
msg_err("failed to allocate message\n");
Expand All @@ -374,22 +374,36 @@ static void nl_enqueue_ifaddr(nlsock s, u16 type, u16 flags, u32 seq, u32 pid, s
hdr->nlmsg_seq = seq;
hdr->nlmsg_pid = pid;
struct ifaddrmsg *ifa = (struct ifaddrmsg *)NLMSG_DATA(hdr);
ifa->ifa_family = AF_INET;
ifa->ifa_prefixlen = 32 - lsb(ntohl(netmask.addr));
ifa->ifa_family = family;
ifa->ifa_prefixlen = prefix_len;
ifa->ifa_flags = 0;
ifa->ifa_scope = netif_is_loopback(netif) ? RT_SCOPE_HOST : RT_SCOPE_UNIVERSE;
ifa->ifa_index = netif_get_index(netif);
struct rtattr *rta = (void*)ifa + NLMSG_ALIGN(sizeof(*ifa));
rta->rta_len = RTA_LENGTH(sizeof(ip4_addr_t));
rta->rta_len = RTA_LENGTH(addr_len);
rta->rta_type = IFA_ADDRESS;
runtime_memcpy(RTA_DATA(rta), &addr, sizeof(addr));
runtime_memcpy(RTA_DATA(rta), addr, addr_len);
rta = RTA_NEXT(rta);
rta->rta_len = RTA_LENGTH(sizeof(netif->name) + 2);
rta->rta_type = IFA_LABEL;
netif_name_cpy(RTA_DATA(rta), netif);
nl_enqueue(s, hdr, resp_len);
}

static inline void nl_enqueue_ifaddr4(nlsock s, u16 type, u16 flags, u32 seq, u32 pid,
struct netif *netif, ip4_addr_t addr, ip4_addr_t netmask)
{
nl_enqueue_ifaddr(s, type, flags, seq, pid, netif, AF_INET, &addr, sizeof(ip4_addr_t),
32 - lsb(ntohl(netmask.addr)));
}

static inline void nl_enqueue_ifaddr6(nlsock s, u16 type, u16 flags, u32 seq, u32 pid,
struct netif *netif, ip6_addr_t addr)
{
nl_enqueue_ifaddr(s, type, flags, seq, pid, netif, AF_INET6, &addr.addr, sizeof(addr.addr),
netif_is_loopback(netif) ? 128 : 64);
}

enum {
RTMSG_TYPE_IF,
RTMSG_TYPE_GW
Expand Down Expand Up @@ -541,8 +555,17 @@ static boolean nl_rtm_getaddr(struct netif *n, void *priv)
{
nl_rtm_netif_priv data = priv;
nlsock s = data->s;
nl_enqueue_ifaddr(s, RTM_NEWADDR, NLM_F_MULTI, data->hdr->nlmsg_seq, s->addr.nl_pid, n,
*netif_ip4_addr(n), *netif_ip4_netmask(n));
nl_enqueue_ifaddr4(s, RTM_NEWADDR, NLM_F_MULTI, data->hdr->nlmsg_seq, s->addr.nl_pid, n,
*netif_ip4_addr(n), *netif_ip4_netmask(n));
return false;
}

static boolean nl_rtm_getaddr6(struct netif *n, void *priv)
{
nl_rtm_netif_priv data = priv;
nlsock s = data->s;
nl_enqueue_ifaddr6(s, RTM_NEWADDR, NLM_F_MULTI, data->hdr->nlmsg_seq, s->addr.nl_pid, n,
*netif_ip6_addr(n, 0));
return false;
}

Expand Down Expand Up @@ -605,13 +628,20 @@ static void nl_route_req(nlsock s, struct nlmsghdr *hdr)
break;
if (hdr->nlmsg_flags & NLM_F_DUMP) {
u8 af = msg->rtgen_family;
if (af != AF_INET6) { /* retrieve IPv4 addresses */
if (af == AF_INET || af == AF_UNSPEC) { /* retrieve IPv4 addresses */
struct nl_rtm_netif_priv priv = {
.s = s,
.hdr = hdr,
};
netif_iterate(nl_rtm_getaddr, &priv);
}
if (af == AF_INET6 || af == AF_UNSPEC) { /* IPv6 */
struct nl_rtm_netif_priv priv = {
.s = s,
.hdr = hdr,
};
netif_iterate(nl_rtm_getaddr6, &priv);
}
nl_enqueue_done(s, hdr);
} else {
errno = EOPNOTSUPP;
Expand Down Expand Up @@ -1040,7 +1070,7 @@ static void nl_lwip_ext_callback(struct netif* netif, netif_nsc_reason_t reason,
if ((reason & LWIP_NSC_IPV4_ADDRESS_CHANGED) &&
!ip4_addr_isany(ip_2_ip4(args->ipv4_changed.old_address))) {
nl_lock(s);
nl_enqueue_ifaddr(s, RTM_DELADDR, 0, 0, NL_PID_KERNEL, netif,
nl_enqueue_ifaddr4(s, RTM_DELADDR, 0, 0, NL_PID_KERNEL, netif,
args->ipv4_changed.old_address->u_addr.ip4,
(reason & LWIP_NSC_IPV4_NETMASK_CHANGED) ?
*ip_2_ip4(args->ipv4_changed.old_netmask) :
Expand All @@ -1049,7 +1079,7 @@ static void nl_lwip_ext_callback(struct netif* netif, netif_nsc_reason_t reason,
}
if (!ip4_addr_isany(netif_ip4_addr(netif))) {
nl_lock(s);
nl_enqueue_ifaddr(s, RTM_NEWADDR, 0, 0, NL_PID_KERNEL, netif,
nl_enqueue_ifaddr4(s, RTM_NEWADDR, 0, 0, NL_PID_KERNEL, netif,
*netif_ip4_addr(netif), *netif_ip4_netmask(netif));
nl_unlock(s);
}
Expand Down
58 changes: 30 additions & 28 deletions test/runtime/netlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -270,34 +270,36 @@ static void test_getaddr(void)
struct ifaddrmsg *ifa;
int ret;

fd = netlink_open(&nladdr, 0);
nladdr.nl_pid = 0;
memset(&req, '\0', sizeof(req));
req.nlh.nlmsg_len = sizeof(req);
req.nlh.nlmsg_type = RTM_GETADDR;
req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
req.nlh.nlmsg_pid = nladdr.nl_pid;
req.nlh.nlmsg_seq = 2;
iov.iov_base = buf;
msg.msg_name = &nladdr;
msg.msg_namelen = sizeof(nladdr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;

req.msg.rtgen_family = AF_INET;
memcpy(iov.iov_base, &req, sizeof(req));
iov.iov_len = sizeof(req);
ret = sendmsg(fd, &msg, 0);
test_assert(ret == sizeof(req));
iov.iov_len = sizeof(buf);
recv_resp(fd, &msg, sizeof(struct ifaddrmsg), RTM_NEWADDR, NLM_F_MULTI);
ifa = (struct ifaddrmsg *)NLMSG_DATA(buf);
test_assert(ifa->ifa_family == AF_INET);

test_assert(close(fd) == 0);
const int afs[3] = { AF_INET, AF_INET6, AF_UNSPEC };
for (int i = 0; i < 3; i++) {
fd = netlink_open(&nladdr, 0);
nladdr.nl_pid = 0;
memset(&req, '\0', sizeof(req));
req.nlh.nlmsg_len = sizeof(req);
req.nlh.nlmsg_type = RTM_GETADDR;
req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
req.nlh.nlmsg_pid = nladdr.nl_pid;
req.nlh.nlmsg_seq = 2;
iov.iov_base = buf;
msg.msg_name = &nladdr;
msg.msg_namelen = sizeof(nladdr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
req.msg.rtgen_family = afs[i];
memcpy(iov.iov_base, &req, sizeof(req));
iov.iov_len = sizeof(req);
ret = sendmsg(fd, &msg, 0);
test_assert(ret == sizeof(req));
iov.iov_len = sizeof(buf);
recv_resp(fd, &msg, sizeof(struct ifaddrmsg), RTM_NEWADDR, NLM_F_MULTI);
ifa = (struct ifaddrmsg *)NLMSG_DATA(buf);
test_assert(afs[i] != AF_UNSPEC ? (ifa->ifa_family == afs[i]) :
(ifa->ifa_family == AF_INET || ifa->ifa_family == AF_INET6));
test_assert(close(fd) == 0);
}

fd = netlink_open(&nladdr, 0);
ret = send(fd, &req, sizeof(req), 0);
Expand Down

0 comments on commit 2e9393b

Please sign in to comment.