Skip to content

Commit

Permalink
net/linux: add net_netlink_addrs (#1232)
Browse files Browse the repository at this point in the history
This pull request adds support for obtaining network interface addresses using the RTNETLINK API on Linux systems.

fixes baresip/baresip#3245
  • Loading branch information
sreimers authored Dec 16, 2024
1 parent 982f164 commit 45c810a
Show file tree
Hide file tree
Showing 6 changed files with 230 additions and 13 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,7 @@ elseif(${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD")
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
list(APPEND SRCS
src/net/linux/rt.c
src/net/linux/addrs.c
)
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Android")
list(APPEND SRCS
Expand Down
1 change: 1 addition & 0 deletions include/re_net.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ int net_if_getlinklocal(const char *ifname, int af, struct sa *ip);

/* Net interface (ifaddrs.c) */
int net_getifaddrs(net_ifaddr_h *ifh, void *arg);
int net_netlink_addrs(net_ifaddr_h *ifh, void *arg);


/* Net route */
Expand Down
210 changes: 210 additions & 0 deletions src/net/linux/addrs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
/**
* @file linux/addrs.c Get interface addresses (See rtnetlink(7))
*
* Copyright (C) 2024 Sebastian Reimers
*/

#include <string.h>
#include <unistd.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <net/if.h>

#include <re_types.h>
#include <re_fmt.h>
#include <re_sa.h>
#include <re_net.h>
#include "macros.h"

#define DEBUG_MODULE "linuxaddrs"
#define DEBUG_LEVEL 5
#include <re_dbg.h>

enum { BUFSZ = 8192, MAXIF = 255 };


static void parse_rtattr(struct rtattr *tb[], struct rtattr *rta, int len)
{
memset(tb, 0, sizeof(struct rtattr *) * (IFA_MAX + 1));
while (RTA_OK(rta, len)) {
if (rta->rta_type <= IFA_MAX) {
tb[rta->rta_type] = rta;
}
rta = RTA_NEXT(rta, len);
}
}


static bool is_ipv6_deprecated(uint32_t flags)
{
if (flags & (IFA_F_TENTATIVE | IFA_F_OPTIMISTIC | IFA_F_DADFAILED |
IFA_F_DEPRECATED))
return true;

return false;
}


static bool parse_msg_link(struct nlmsghdr *msg, ssize_t len, int *iff_up)
{
struct nlmsghdr *nlh;
struct ifinfomsg *ifi;

for (nlh = msg; NLMSG_OK(nlh, len); nlh = NLMSG_NEXT(nlh, len)) {
if (nlh->nlmsg_type == NLMSG_DONE) {
return true;
}
if (nlh->nlmsg_type == NLMSG_ERROR) {
DEBUG_WARNING("netlink recv error\n");
return true;
}

ifi = NLMSG_DATA(nlh);

if (ifi->ifi_index >= MAXIF) {
DEBUG_WARNING("Max interface index [%d] reached!\n",
MAXIF);
return false;
}

iff_up[ifi->ifi_index] = ifi->ifi_flags & IFF_UP;
}

return false;
}


static bool parse_msg_addr(struct nlmsghdr *msg, ssize_t len,
net_ifaddr_h *ifh, int *iff_up, void *arg)
{
struct nlmsghdr *nlh;
for (nlh = msg; NLMSG_OK(nlh, len); nlh = NLMSG_NEXT(nlh, len)) {
struct sa sa;
uint32_t flags;
char if_name[IF_NAMESIZE];

if (nlh->nlmsg_type == NLMSG_DONE) {
return true;
}
if (nlh->nlmsg_type == NLMSG_ERROR) {
DEBUG_WARNING("netlink recv error\n");
return true;
}

struct ifaddrmsg *ifa = NLMSG_DATA(nlh);
struct rtattr *rta_tb[IFA_MAX + 1];

parse_rtattr(rta_tb, IFA_RTA(ifa),
nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));

if (!rta_tb[IFA_ADDRESS])
continue;

if (ifa->ifa_index < MAXIF && !iff_up[ifa->ifa_index])
continue;

if (rta_tb[IFA_FLAGS] && ifa->ifa_family == AF_INET6) {
flags = *(uint32_t *)RTA_DATA(rta_tb[IFA_FLAGS]);
if (is_ipv6_deprecated(flags))
continue;
}

if (ifa->ifa_family == AF_INET) {
sa_init(&sa, AF_INET);
sa.u.in.sin_addr.s_addr =
*(uint32_t *)RTA_DATA(rta_tb[IFA_ADDRESS]);
}
else if (ifa->ifa_family == AF_INET6) {
sa_set_in6(&sa, RTA_DATA(rta_tb[IFA_ADDRESS]), 0);
sa_set_scopeid(&sa, ifa->ifa_index);
}
else
continue;

if (!if_indextoname(ifa->ifa_index, if_name))
continue;

if (ifh(if_name, &sa, arg))
return true;
}

return false;
}


int net_netlink_addrs(net_ifaddr_h *ifh, void *arg)
{
int err = 0;
char buffer[BUFSZ];
re_sock_t sock;
ssize_t len;
int iff_up[MAXIF] = {0};

struct {
struct nlmsghdr nlh;
struct ifaddrmsg ifa;
} req;

if (!ifh)
return EINVAL;

if ((sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {
err = errno;
DEBUG_WARNING("socket failed %m\n", err);
return err;
}

struct timeval timeout = {5, 0};
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));

/* GETLINK */
memset(&req, 0, sizeof(req));
req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
req.nlh.nlmsg_type = RTM_GETLINK;

if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0) {
err = errno;
DEBUG_WARNING("GETLINK send failed %m\n", err);
goto out;
}

while ((len = recv(sock, buffer, sizeof(buffer), 0)) > 0) {
if (parse_msg_link((struct nlmsghdr *)buffer, len, iff_up))
break;
}

if (len < 0) {
err = errno;
DEBUG_WARNING("GETLINK recv failed %m\n", err);
goto out;
}

/* GETADDR */
memset(&req, 0, sizeof(req));
req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
req.nlh.nlmsg_type = RTM_GETADDR;

if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0) {
err = errno;
DEBUG_WARNING("GETADDR send failed %m\n", err);
goto out;
}

while ((len = recv(sock, buffer, sizeof(buffer), 0)) > 0) {
if (parse_msg_addr((struct nlmsghdr *)buffer, len, ifh, iff_up,
arg))
break;
}

if (len < 0) {
err = errno;
DEBUG_WARNING("GETADDR recv failed %m\n", err);
}

out:
close(sock);

return err;
}
14 changes: 14 additions & 0 deletions src/net/linux/macros.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/* Override macros to avoid casting alignment warning */
#undef RTM_RTA
#define RTM_RTA(r) (void *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct rtmsg)))
#undef RTA_NEXT
#define RTA_NEXT(rta, len) \
((len) -= RTA_ALIGN((rta)->rta_len), \
(void *)(((char *)(rta)) + RTA_ALIGN((rta)->rta_len)))
#undef NLMSG_NEXT
#define NLMSG_NEXT(nlh, len) \
((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \
(void *)(((char *)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))
#undef IFA_RTA
#define IFA_RTA(r) \
((void *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
13 changes: 1 addition & 12 deletions src/net/linux/rt.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,13 @@
#include <re_fmt.h>
#include <re_sa.h>
#include <re_net.h>
#include "macros.h"


#define DEBUG_MODULE "linuxrt"
#define DEBUG_LEVEL 5
#include <re_dbg.h>


/* Override macros to avoid casting alignment warning */
#undef RTM_RTA
#define RTM_RTA(r) (void *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct rtmsg)))
#undef RTA_NEXT
#define RTA_NEXT(rta, len) ((len) -= RTA_ALIGN((rta)->rta_len), \
(void *)(((char *)(rta)) + RTA_ALIGN((rta)->rta_len)))
#undef NLMSG_NEXT
#define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \
(void*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))


enum {BUFSIZE = 8192};


Expand Down
4 changes: 3 additions & 1 deletion src/net/net.c
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,9 @@ int net_default_source_addr_get(int af, struct sa *ip)
*/
int net_if_apply(net_ifaddr_h *ifh, void *arg)
{
#ifdef HAVE_GETIFADDRS
#ifdef LINUX
return net_netlink_addrs(ifh, arg);
#elif HAVE_GETIFADDRS
return net_getifaddrs(ifh, arg);
#else
return net_if_list(ifh, arg);
Expand Down

0 comments on commit 45c810a

Please sign in to comment.