Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

net/linux: add net_netlink_addrs #1232

Merged
merged 10 commits into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading