PcapPlusPlus icon indicating copy to clipboard operation
PcapPlusPlus copied to clipboard

improve macOS get default gateway IP

Open tigercosmos opened this issue 2 years ago • 13 comments

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.

tigercosmos avatar Apr 05 '24 13:04 tigercosmos

Hey, I would really like to work on this!

ADTmux avatar Apr 05 '24 21:04 ADTmux

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

tigercosmos avatar Apr 05 '24 22:04 tigercosmos

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.

ADTmux avatar Apr 08 '24 05:04 ADTmux

@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?

tigercosmos avatar Apr 08 '24 06:04 tigercosmos

@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)

ADTmux avatar Apr 08 '24 07:04 ADTmux

@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 avatar Apr 08 '24 08:04 tigercosmos

@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.

tigercosmos avatar Apr 08 '24 08:04 tigercosmos

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

ADTmux avatar Apr 08 '24 09:04 ADTmux

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 avatar Apr 08 '24 09:04 tigercosmos

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

tigercosmos avatar Apr 08 '24 09:04 tigercosmos

@ADTmux please let me know if you encounter any issue

tigercosmos avatar Apr 16 '24 16:04 tigercosmos

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

zhengfeihe avatar Apr 24 '24 04:04 zhengfeihe

@zhengfeihe sure, please go ahead.

tigercosmos avatar Apr 24 '24 04:04 tigercosmos