diff --git a/src/common/netif/netif.c b/src/common/netif/netif.c index 8d02b1a542..17b8726a43 100644 --- a/src/common/netif/netif.c +++ b/src/common/netif/netif.c @@ -7,15 +7,18 @@ #include #endif -bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex); +bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex, uint32_t* preferredSourceAddr); enum { IF_INDEX_UNINITIALIZED = (uint32_t) -1, IF_INDEX_INVALID = (uint32_t) -2 }; static uint32_t ifIndex = IF_INDEX_UNINITIALIZED; static char ifName[IF_NAMESIZE + 1]; +static uint32_t preferredSourceAddr = 0; static inline void init() { - if (ifIndex == (uint32_t) IF_INDEX_UNINITIALIZED && !ffNetifGetDefaultRouteImpl(ifName, &ifIndex)) - ifIndex = (uint32_t) IF_INDEX_INVALID; + if (ifIndex == (uint32_t) IF_INDEX_UNINITIALIZED) { + if (!ffNetifGetDefaultRouteImpl(ifName, &ifIndex, &preferredSourceAddr)) + ifIndex = (uint32_t) IF_INDEX_INVALID; + } } const char* ffNetifGetDefaultRouteIfName() @@ -29,3 +32,9 @@ uint32_t ffNetifGetDefaultRouteIfIndex() init(); return ifIndex; } + +uint32_t ffNetifGetDefaultRoutePreferredSourceAddr() +{ + init(); + return preferredSourceAddr; +} diff --git a/src/common/netif/netif.h b/src/common/netif/netif.h index 40c97948d2..1d7bd1dd2b 100644 --- a/src/common/netif/netif.h +++ b/src/common/netif/netif.h @@ -4,3 +4,4 @@ const char* ffNetifGetDefaultRouteIfName(); uint32_t ffNetifGetDefaultRouteIfIndex(); +uint32_t ffNetifGetDefaultRoutePreferredSourceAddr(); diff --git a/src/common/netif/netif_bsd.c b/src/common/netif/netif_bsd.c index f85c39a629..8bdb22eefd 100644 --- a/src/common/netif/netif_bsd.c +++ b/src/common/netif/netif_bsd.c @@ -50,8 +50,11 @@ get_rt_address(struct rt_msghdr *rtm, int desired) return NULL; } -bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex) +bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex, uint32_t* preferredSourceAddr) { + if (preferredSourceAddr) + *preferredSourceAddr = 0; + #if defined(__OpenBSD__) || defined(__DragonFly__) int mib[6] = {CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_FLAGS, RTF_GATEWAY}; size_t needed; @@ -80,6 +83,12 @@ bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex) memcpy(iface, sdl->sdl_data, sdl->sdl_nlen); iface[sdl->sdl_nlen] = '\0'; *ifIndex = sdl->sdl_index; + + // Get the preferred source address + struct sockaddr_in* src = (struct sockaddr_in*)get_rt_address(rtm, RTA_IFA); + if (preferredSourceAddr && src && src->sin_family == AF_INET) + *preferredSourceAddr = src->sin_addr.s_addr; + return true; } } @@ -108,7 +117,7 @@ bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex) .rtm_type = RTM_GET, .rtm_flags = RTF_UP | RTF_GATEWAY, .rtm_version = RTM_VERSION, - .rtm_addrs = RTA_DST | RTA_IFP, + .rtm_addrs = RTA_DST | RTA_IFP | RTA_IFA, .rtm_msglen = sizeof(rtmsg.hdr) + sizeof(rtmsg.dst), .rtm_pid = pid, .rtm_seq = 1, @@ -139,6 +148,12 @@ bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex) memcpy(iface, sdl->sdl_data, sdl->sdl_nlen); iface[sdl->sdl_nlen] = '\0'; *ifIndex = sdl->sdl_index; + + // Get the preferred source address + struct sockaddr_in* src = (struct sockaddr_in*)get_rt_address(&rtmsg.hdr, RTA_IFA); + if (preferredSourceAddr && src && src->sin_family == AF_INET) + *preferredSourceAddr = src->sin_addr.s_addr; + return true; } return false; diff --git a/src/common/netif/netif_haiku.c b/src/common/netif/netif_haiku.c index daa0cd7a1c..cb7401ab68 100644 --- a/src/common/netif/netif_haiku.c +++ b/src/common/netif/netif_haiku.c @@ -11,8 +11,11 @@ // loosely based on Haiku's src/bin/network/route/route.cpp -bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex) +bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex, uint32_t* preferredSourceAddr) { + if (preferredSourceAddr) + *preferredSourceAddr = 0; + // TODO: AF_INET6 FF_AUTO_CLOSE_FD int pfRoute = socket(AF_INET, SOCK_RAW, AF_INET); if (pfRoute < 0) diff --git a/src/common/netif/netif_linux.c b/src/common/netif/netif_linux.c index 6c0dbd1e60..6a484e7897 100644 --- a/src/common/netif/netif_linux.c +++ b/src/common/netif/netif_linux.c @@ -1,27 +1,179 @@ #include "netif.h" #include "common/io/io.h" +#include "util/mallocHelper.h" +#include +#include #include -#include #define FF_STR_INDIR(x) #x #define FF_STR(x) FF_STR_INDIR(x) -static bool getDefaultRouteIPv4(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex) +struct req_t { + struct nlmsghdr nlh; + struct rtmsg rtm; + struct rtattr rta; + uint32_t table; +}; + +struct Route4Entry { + uint32_t dest; + uint32_t gateway; + uint32_t src; + uint8_t prefix_length; + uint32_t metric; + uint32_t ifindex; +}; + +static bool getDefaultRouteIPv4(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex, uint32_t* preferredSourceAddr) { - FILE* FF_AUTO_CLOSE_FILE netRoute = fopen("/proc/net/route", "r"); - if (!netRoute) return false; + FF_AUTO_CLOSE_FD int sock_fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE); + if (sock_fd < 0) + return false; - // skip first line - FF_UNUSED(fscanf(netRoute, "%*[^\n]\n")); + // Bind socket + struct sockaddr_nl addr; + memset(&addr, 0, sizeof(addr)); + addr.nl_family = AF_NETLINK; + addr.nl_pid = 0; // Let kernel assign PID + addr.nl_groups = 0; - unsigned long long destination; //, gateway, flags, refCount, use, metric, mask, mtu, ... - while (fscanf(netRoute, "%" FF_STR(IF_NAMESIZE) "s%llx%*[^\n]", iface, &destination) == 2) - { - if (destination != 0) continue; - *ifIndex = if_nametoindex(iface); + if (bind(sock_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) { + return false; + } + + struct req_t req; + memset(&req, 0, sizeof(req)); + + // Netlink message header + req.nlh.nlmsg_len = sizeof(req); + req.nlh.nlmsg_type = RTM_GETROUTE; + req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + req.nlh.nlmsg_seq = 0; + req.nlh.nlmsg_pid = 0; + + // Route message + req.rtm.rtm_family = AF_INET; + req.rtm.rtm_dst_len = 0; + req.rtm.rtm_src_len = 0; + req.rtm.rtm_tos = 0; + req.rtm.rtm_table = RT_TABLE_UNSPEC; + req.rtm.rtm_protocol = RTPROT_UNSPEC; + req.rtm.rtm_scope = RT_SCOPE_UNIVERSE; + req.rtm.rtm_type = RTN_UNSPEC; + req.rtm.rtm_flags = 0; + + // Route attribute for main table + req.rta.rta_len = RTA_LENGTH(sizeof(uint32_t)); + req.rta.rta_type = RTA_TABLE; + req.table = RT_TABLE_MAIN; + + struct sockaddr_nl dest_addr; + memset(&dest_addr, 0, sizeof(dest_addr)); + dest_addr.nl_family = AF_NETLINK; + dest_addr.nl_pid = 0; + dest_addr.nl_groups = 0; + + ssize_t sent = sendto(sock_fd, &req, sizeof(req), 0, + (struct sockaddr*)&dest_addr, sizeof(dest_addr)); + + if (sent != sizeof(req)) { + return false; + } + + struct sockaddr_nl src_addr; + socklen_t src_addr_len = sizeof(src_addr); + struct iovec iov = {NULL, 0}; + struct msghdr msg; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = &src_addr; + msg.msg_namelen = sizeof(src_addr); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + ssize_t peek_size = recvmsg(sock_fd, &msg, MSG_PEEK | MSG_TRUNC); + if (peek_size < 0) { + return false; + } + + FF_AUTO_FREE uint8_t* buffer = malloc((size_t)peek_size); + + ssize_t received = recvfrom(sock_fd, buffer, (size_t)peek_size, 0, + (struct sockaddr*)&src_addr, &src_addr_len); + if (received != peek_size) { + return false; + } + + struct Route4Entry best_gw; + memset(&best_gw, 0, sizeof(best_gw)); + + for (const struct nlmsghdr* nlh = (struct nlmsghdr*)buffer; + NLMSG_OK(nlh, received); + nlh = NLMSG_NEXT(nlh, received)) { + + if (nlh->nlmsg_type == NLMSG_DONE) + break; + + if (nlh->nlmsg_type != RTM_NEWROUTE) + continue; + + struct rtmsg* rtm = (struct rtmsg*)NLMSG_DATA(nlh); + if (rtm->rtm_family != AF_INET) + continue; + + struct Route4Entry entry; + memset(&entry, 0, sizeof(struct Route4Entry)); + entry.prefix_length = rtm->rtm_dst_len; + + // Parse route attributes + size_t rtm_len = RTM_PAYLOAD(nlh); + for (struct rtattr* rta = RTM_RTA(rtm); + RTA_OK(rta, rtm_len); + rta = RTA_NEXT(rta, rtm_len)) { + + switch (rta->rta_type) { + case RTA_DST: + entry.dest = *(uint32_t*)RTA_DATA(rta); + break; + case RTA_GATEWAY: + entry.gateway = *(uint32_t*)RTA_DATA(rta); + break; + case RTA_PREFSRC: + entry.src = *(uint32_t*)RTA_DATA(rta); + break; + case RTA_PRIORITY: + entry.metric = *(uint32_t*)RTA_DATA(rta); + break; + case RTA_OIF: + entry.ifindex = *(uint32_t*)RTA_DATA(rta); + break; + } + } + + if (entry.gateway == 0 || entry.dest != 0 || entry.prefix_length != 0) + continue; + + if (best_gw.gateway == 0 || entry.metric < best_gw.metric) { + memcpy(&best_gw, &entry, sizeof(struct Route4Entry)); + } + } + + if (best_gw.gateway != 0) { + if (ifIndex) { + *ifIndex = best_gw.ifindex; + } + if (iface) { + if (if_indextoname(best_gw.ifindex, iface) == NULL) { + iface[0] = '\0'; + } + } + if (preferredSourceAddr) { + *preferredSourceAddr = best_gw.src; + } return true; } + iface[0] = '\0'; return false; } @@ -43,9 +195,9 @@ static bool getDefaultRouteIPv6(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex) return false; } -bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex) +bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex, uint32_t* preferredSourceAddr) { - if (getDefaultRouteIPv4(iface, ifIndex)) + if (getDefaultRouteIPv4(iface, ifIndex, preferredSourceAddr)) return true; return getDefaultRouteIPv6(iface, ifIndex); diff --git a/src/common/netif/netif_windows.c b/src/common/netif/netif_windows.c index 0dfd2bd077..275c7b62e4 100644 --- a/src/common/netif/netif_windows.c +++ b/src/common/netif/netif_windows.c @@ -4,8 +4,11 @@ #include // AF_INET6, IN6_IS_ADDR_UNSPECIFIED #include -bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex) +bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex, uint32_t* preferredSourceAddr) { + if (preferredSourceAddr) + *preferredSourceAddr = 0; + PMIB_IPFORWARD_TABLE2 pIpForwardTable = NULL; if (!NETIO_SUCCESS(GetIpForwardTable2(AF_UNSPEC, &pIpForwardTable))) diff --git a/src/detection/localip/localip_linux.c b/src/detection/localip/localip_linux.c index f9cc343d6f..ac8ead7dd0 100644 --- a/src/detection/localip/localip_linux.c +++ b/src/detection/localip/localip_linux.c @@ -136,6 +136,7 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) return "getifaddrs(&ifAddrStruct) failed"; const char* defaultRouteIfName = ffNetifGetDefaultRouteIfName(); + const uint32_t preferredSourceAddr = ffNetifGetDefaultRoutePreferredSourceAddr(); for (struct ifaddrs* ifa = ifAddrStruct; ifa; ifa = ifa->ifa_next) { @@ -146,8 +147,8 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) continue; #endif - bool isDefaultRoute = ffStrEquals(defaultRouteIfName, ifa->ifa_name); - if ((options->showType & FF_LOCALIP_TYPE_DEFAULT_ROUTE_ONLY_BIT) && !isDefaultRoute) + bool isDefaultRouteIf = ffStrEquals(defaultRouteIfName, ifa->ifa_name); + if ((options->showType & FF_LOCALIP_TYPE_DEFAULT_ROUTE_ONLY_BIT) && !isDefaultRouteIf) continue; if ((ifa->ifa_flags & IFF_LOOPBACK) && !(options->showType & FF_LOCALIP_TYPE_LOOP_BIT)) @@ -164,6 +165,11 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) continue; struct sockaddr_in* ipv4 = (struct sockaddr_in*) ifa->ifa_addr; + + bool isDefaultRoute = isDefaultRouteIf && (preferredSourceAddr == 0 || ipv4->sin_addr.s_addr == preferredSourceAddr); + if ((options->showType & FF_LOCALIP_TYPE_DEFAULT_ROUTE_ONLY_BIT) && !isDefaultRoute) + continue; + char addressBuffer[INET_ADDRSTRLEN + 16]; inet_ntop(AF_INET, &ipv4->sin_addr, addressBuffer, INET_ADDRSTRLEN); @@ -203,7 +209,7 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) } } - addNewIp(results, ifa->ifa_name, addressBuffer, AF_INET6, isDefaultRoute, flags, !(options->showType & FF_LOCALIP_TYPE_ALL_IPS_BIT)); + addNewIp(results, ifa->ifa_name, addressBuffer, AF_INET6, isDefaultRouteIf, flags, !(options->showType & FF_LOCALIP_TYPE_ALL_IPS_BIT)); } #if __FreeBSD__ || __OpenBSD__ || __APPLE__ || __NetBSD__ || __HAIKU__ else if (ifa->ifa_addr->sa_family == AF_LINK) @@ -215,7 +221,7 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) uint8_t* ptr = (uint8_t*) LLADDR((struct sockaddr_dl *)ifa->ifa_addr); snprintf(addressBuffer, ARRAY_SIZE(addressBuffer), "%02x:%02x:%02x:%02x:%02x:%02x", ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5]); - addNewIp(results, ifa->ifa_name, addressBuffer, -1, isDefaultRoute, flags, false); + addNewIp(results, ifa->ifa_name, addressBuffer, -1, isDefaultRouteIf, flags, false); } #else else if (ifa->ifa_addr->sa_family == AF_PACKET) @@ -227,7 +233,7 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) uint8_t* ptr = ((struct sockaddr_ll *)ifa->ifa_addr)->sll_addr; snprintf(addressBuffer, ARRAY_SIZE(addressBuffer), "%02x:%02x:%02x:%02x:%02x:%02x", ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5]); - addNewIp(results, ifa->ifa_name, addressBuffer, -1, isDefaultRoute, flags, false); + addNewIp(results, ifa->ifa_name, addressBuffer, -1, isDefaultRouteIf, flags, false); } #endif }