ipv6: use netlink RTM_GETADDR to properly detect permanent vs temporary IPv6 addresses
getifaddrs() ifa_flags contains interface flags (IFF_UP etc.), NOT address flags (IFA_F_TEMPORARY). Since IFA_F_TEMPORARY == IFA_F_SECONDARY == 0x01, and 0x01 in interface flags is IFF_UP, ALL addresses appeared as 'temporary', making the permanent>temporary priority non-functional. New netlink-based get_interface_ipv6_addr_nl() reads real IFA_FLAGS from RTM_GETADDR response, correctly identifying: - Temporary: IFA_F_TEMPORARY=1, IFA_F_MANAGETEMPADDR=0 - Stable/mngtmp: IFA_F_TEMPORARY=1, IFA_F_MANAGETEMPADDR=1 - Legacy static: IFA_F_TEMPORARY=0 Verified: wlo1 permanent (3a7a:eff:fef5:9637) now correctly selected for prefer_stable=1, temporary (1fb5:b7c:7538:dcf4) for prefer_stable=0.
This commit is contained in:
1 parent
f67bc23cd3
commit
fdbb5b7bc0
3 files changed
+104
-3
No files matched your search
@@ -24,9 +24,17 @@
|
||||
#include <arpa/inet.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <net/if.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#ifndef IFA_F_TEMPORARY
|
||||
#define IFA_F_TEMPORARY 0x01
|
||||
#endif
|
||||
#ifndef IFA_F_MANAGETEMPADDR
|
||||
#define IFA_F_MANAGETEMPADDR 0x100
|
||||
#endif
|
||||
#ifndef IFA_F_STABLE_PRIVACY
|
||||
#define IFA_F_STABLE_PRIVACY 0x800
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ -106,6 +114,87 @@ int get_interface_ipv6_by_index(uint32_t netif_index, int temporary, uint8_t* ad
|
||||
return found ? 0 : -1;
|
||||
}
|
||||
|
||||
/* get_interface_ipv6_addr_nl — netlink-based IPv6 address lookup with proper IFA_F_* flags
|
||||
*
|
||||
* getifaddrs() ifa_flags contains interface flags (IFF_UP etc.), NOT address flags
|
||||
* (IFA_F_TEMPORARY). To properly distinguish temporary vs permanent IPv6 addresses,
|
||||
* we must use netlink RTM_GETADDR which exposes IFA_FLAGS attribute with real flags:
|
||||
* - Temporary: IFA_F_TEMPORARY=1, IFA_F_MANAGETEMPADDR=0
|
||||
* - Stable/mngtmp: IFA_F_TEMPORARY=1, IFA_F_MANAGETEMPADDR=1
|
||||
* - Legacy static: IFA_F_TEMPORARY=0, IFA_F_MANAGETEMPADDR=0
|
||||
*
|
||||
* prefer_stable=1: prefer permanent/stable (IFA_F_MANAGETEMPADDR or !IFA_F_TEMPORARY)
|
||||
* prefer_stable=0: prefer temporary (IFA_F_TEMPORARY && !IFA_F_MANAGETEMPADDR)
|
||||
* Fallback: first non-link-local address */
|
||||
int get_interface_ipv6_addr_nl(uint32_t netif_index, int prefer_stable, uint8_t* addr_out) {
|
||||
if (!addr_out || !netif_index) return -1;
|
||||
memset(addr_out, 0, 16);
|
||||
|
||||
int nl_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
|
||||
if (nl_sock < 0) return -1;
|
||||
|
||||
struct {
|
||||
struct nlmsghdr nlh;
|
||||
struct ifaddrmsg ifa;
|
||||
char attrbuf[64];
|
||||
} req;
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
|
||||
req.nlh.nlmsg_type = RTM_GETADDR;
|
||||
req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
|
||||
req.nlh.nlmsg_seq = 1;
|
||||
req.ifa.ifa_family = AF_INET6;
|
||||
req.ifa.ifa_index = netif_index;
|
||||
|
||||
if (send(nl_sock, &req, req.nlh.nlmsg_len, 0) < 0) { close(nl_sock); return -1; }
|
||||
|
||||
uint8_t fallback[16], stable_addr[16], temp_addr[16];
|
||||
int has_fallback = 0, has_stable = 0, has_temp = 0;
|
||||
|
||||
char buf[8192];
|
||||
int done = 0;
|
||||
while (!done) {
|
||||
ssize_t len = recv(nl_sock, buf, sizeof(buf), 0);
|
||||
if (len < 0) break;
|
||||
struct nlmsghdr* nlh = (struct nlmsghdr*)buf;
|
||||
for (; NLMSG_OK(nlh, (size_t)len); nlh = NLMSG_NEXT(nlh, len)) {
|
||||
if (nlh->nlmsg_type == NLMSG_DONE) { done = 1; break; }
|
||||
if (nlh->nlmsg_type != RTM_NEWADDR) continue;
|
||||
|
||||
struct ifaddrmsg* ifa = (struct ifaddrmsg*)NLMSG_DATA(nlh);
|
||||
if (ifa->ifa_family != AF_INET6) continue;
|
||||
if (ifa->ifa_index != netif_index) continue;
|
||||
|
||||
uint8_t* addr = NULL;
|
||||
uint32_t ifa_flags = 0;
|
||||
struct rtattr* rta = IFA_RTA(ifa);
|
||||
int rta_len = IFA_PAYLOAD(nlh);
|
||||
for (; RTA_OK(rta, rta_len); rta = RTA_NEXT(rta, rta_len)) {
|
||||
if (rta->rta_type == IFA_ADDRESS) addr = RTA_DATA(rta);
|
||||
if (rta->rta_type == IFA_FLAGS) ifa_flags = *(uint32_t*)RTA_DATA(rta);
|
||||
}
|
||||
if (!addr) continue;
|
||||
if ((addr[0] & 0xfe) == 0xfe && addr[1] == 0x80) continue; // skip link-local
|
||||
|
||||
int is_temp = (ifa_flags & IFA_F_TEMPORARY) != 0;
|
||||
int is_mngtmp = (ifa_flags & IFA_F_MANAGETEMPADDR) != 0;
|
||||
|
||||
if (!has_fallback) { memcpy(fallback, addr, 16); has_fallback = 1; }
|
||||
|
||||
if (!has_stable && (!is_temp || is_mngtmp)) { memcpy(stable_addr, addr, 16); has_stable = 1; }
|
||||
if (!has_temp && is_temp && !is_mngtmp) { memcpy(temp_addr, addr, 16); has_temp = 1; }
|
||||
}
|
||||
}
|
||||
close(nl_sock);
|
||||
|
||||
if (prefer_stable && has_stable) { memcpy(addr_out, stable_addr, 16); return 0; }
|
||||
if (!prefer_stable && has_temp) { memcpy(addr_out, temp_addr, 16); return 0; }
|
||||
if (has_stable) { memcpy(addr_out, stable_addr, 16); return 0; }
|
||||
if (has_temp) { memcpy(addr_out, temp_addr, 16); return 0; }
|
||||
if (has_fallback) { memcpy(addr_out, fallback, 16); return 0; }
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t get_default_route_netif_index(int family) {
|
||||
if (family != AF_INET && family != AF_INET6) return 0;
|
||||
|
||||
@@ -191,6 +280,12 @@ int get_interface_ipv6_by_index(uint32_t netif_index, int temporary, uint8_t* ad
|
||||
return -1;
|
||||
}
|
||||
|
||||
int get_interface_ipv6_addr_nl(uint32_t netif_index, int prefer_stable, uint8_t* addr_out) {
|
||||
(void)netif_index; (void)prefer_stable;
|
||||
if (addr_out) memset(addr_out, 0, 16);
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t get_default_route_netif_index(int family) {
|
||||
(void)family;
|
||||
return 0;
|
||||
|
||||
@@ -189,6 +189,12 @@ uint32_t get_interface_ip_by_index(uint32_t netif_index);
|
||||
// Returns 0 on success (fills addr_out with 16 bytes), -1 on error or not found
|
||||
int get_interface_ipv6_by_index(uint32_t netif_index, int temporary, uint8_t* addr_out);
|
||||
|
||||
// Get IPv6 address using netlink (Linux-only, accurate IFA_F_* flags)
|
||||
// prefer_stable=1: prefer permanent/stable (IFA_F_MANAGETEMPADDR) over temporary
|
||||
// prefer_stable=0: prefer temporary over stable
|
||||
// Returns 0 on success (fills addr_out with 16 bytes), -1 on error
|
||||
int get_interface_ipv6_addr_nl(uint32_t netif_index, int prefer_stable, uint8_t* addr_out);
|
||||
|
||||
// Determine the interface index for the default route (outgoing traffic)
|
||||
// Creates a temp UDP socket, connects to a well-known address to force
|
||||
// kernel routing, then matches the chosen source IP to getifaddrs()
|
||||
|
||||
Reference in new issue
Block a user