seladb / PcapPlusPlus

PcapPlusPlus is a multiplatform C++ library for capturing, parsing and crafting of network packets. It is designed to be efficient, powerful and easy to use. It provides C++ wrappers for the most popular packet processing engines such as libpcap, Npcap, WinPcap, DPDK, AF_XDP and PF_RING.
https://pcapplusplus.github.io/
The Unlicense
2.63k stars 641 forks source link

improve macOS get default gateway IP #1355

Closed tigercosmos closed 2 weeks ago

tigercosmos commented 3 months ago

currently, we use a shell command to get the gateway in macOS: https://github.com/seladb/PcapPlusPlus/blob/e48be80c8671ca2018db24947c7d71df9c41b1fd/Pcap%2B%2B/src/PcapLiveDevice.cpp#L1013-L1032

We don't need to use shell script here, we can use normal C API instead.

ADTmux commented 3 months ago

Hey, I would really like to work on this!

tigercosmos commented 3 months ago

@ADTmux sure, let me know if you encounter any issue.

ADTmux commented 3 months ago

Hey @tigercosmos, I did experiment with the C API - https://man7.org/linux/man-pages/man3/getifaddrs.3.html , but I found that it does not directly contain the gateway for macOS that I can fetch. Are we thinking about any particular API here? Other methods could include using the routing table or ioctl() system call.

tigercosmos commented 3 months ago

@ADTmux That would be interesting. I tested with getifaddrs on macOS 14 (arm) and it works fine to me. What is your platform? Could you also post the testing code (a runnable minimum code in a single cpp file) here?

ADTmux commented 3 months ago

@tigercosmos I used this code snippet (https://file.io/sCmaptmi8x5A) in PcapPlusPlus/Examples/Tutorials/Tutorial-LiveTraffic/main.cpp for testing the C library, and tried to print as much info as I can get, and I can see all network interfaces' details except the Default Gateway The platform I used is macOS 13.4 (Intel)

tigercosmos commented 3 months ago

@ADTmux next time you can just copied the code here if it is not large (more than 200 lines).

The file you provided:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <ifaddrs.h>
#include <netdb.h> 

int main() {
    struct ifaddrs *ifaddr, *ifa;
    int family, s;
    char host[NI_MAXHOST];

    if (getifaddrs(&ifaddr) == -1) {
        perror("getifaddrs");
        exit(EXIT_FAILURE);
    }

    for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
        if (ifa->ifa_addr == NULL)
            continue;

        family = ifa->ifa_addr->sa_family;

        printf("Interface: %s\n", ifa->ifa_name);
        printf("  Family: ");

        switch (family) {
            case AF_INET:
                printf("AF_INET (IPv4)\n");
                break;
            case AF_INET6:
                printf("AF_INET6 (IPv6)\n");
                break;
            default:
                printf("Unknown\n");
                continue;
        }

        s = getnameinfo(ifa->ifa_addr, (family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6),
                        host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
        if (s != 0) {
            printf("getnameinfo() failed: %s\n", gai_strerror(s));
            exit(EXIT_FAILURE);
        }
        printf("  Address: %s\n", host);

        if (family == AF_INET) {
            struct sockaddr_in *netmask = (struct sockaddr_in *)ifa->ifa_netmask;
            if (netmask != NULL) {
                s = getnameinfo((struct sockaddr *)netmask, sizeof(struct sockaddr_in),
                                host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
                if (s != 0) {
                    printf("getnameinfo() failed: %s\n", gai_strerror(s));
                    exit(EXIT_FAILURE);
                }
                printf("  Netmask: %s\n", host);
            }
        }

        printf("  Flags: %x\n", ifa->ifa_flags);
    }

    freeifaddrs(ifaddr);
    return 0;
}

I will try to run it later.

tigercosmos commented 3 months ago

@ADTmux Just in case if you don't fully understand the requirement. In the code snip at the beginning of the issue, there is a line:

std::string command = "netstat -nr | grep default | grep " + m_Name; 

we parse the result (ifaceInfo) and use the IP to feed to IPv4Address:

m_DefaultGateway = IPv4Address(ifaceInfo);

your mission is to find out the correct ifaceInfo (of course you will need to change the name) by the C API.

Hint: the default route has the destination as 0.0.0.0.

ADTmux commented 3 months ago

Yes I did get the requirement. Basically if I can obtain the Default Gateway as a string I can pass it to IPv4Address()

tigercosmos commented 3 months ago

FYI, not sure if it helps or not, but you may want to refer to the source code of route, which provides the default route by calling route -n get default

https://opensource.apple.com/source/network_cmds/network_cmds-396.6/route.tproj/route.c.auto.html

tigercosmos commented 3 months ago

regarding your code, obviously using getnameinfo() is not enough, you may need to play around with C structs and C APIs more.

tigercosmos commented 2 months ago

@ADTmux please let me know if you encounter any issue

zhengfeihe commented 2 months ago

Hi I'd like to work on this issue. Can you re reassign this issue to me @tigercosmos ?

tigercosmos commented 2 months ago

@zhengfeihe sure, please go ahead.