-
Notifications
You must be signed in to change notification settings - Fork 84
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
net/linux: add net_netlink_addrs (#1232)
This pull request adds support for obtaining network interface addresses using the RTNETLINK API on Linux systems. fixes baresip/baresip#3245
- Loading branch information
Showing
6 changed files
with
230 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters