PcapPlusPlus icon indicating copy to clipboard operation
PcapPlusPlus copied to clipboard

Improve the way to get default gateway on macOS

Open zhengfeihe opened this issue 1 year ago • 2 comments

Implement thoughts from #1355

Follow the source code of route on a Mac to use a raw socket for retrieving default gateway information from the routing table, thus eliminating the need for system calls.

Test will be added accordingly.

zhengfeihe avatar May 02 '24 00:05 zhengfeihe

@zhengfeihe next time don't resolve the issue yourself, so other people can easily track the issues. Also, could you let the tests pass?

tigercosmos avatar May 05 '24 09:05 tigercosmos

@zhengfeihe next time don't resolve the issue yourself, so other people can easily track the issues. Also, could you let the tests pass?

Thanks for the reminder. I will watch out next time.

zhengfeihe avatar May 05 '24 10:05 zhengfeihe

@zhengfeihe As we previously discussed, it appears that FreeBSD cannot easily share its implementation with macOS. Therefore, let's separate the FreeBSD implementation from macOS, allowing the macOS implementation to be merged first.

tigercosmos avatar May 25 '24 06:05 tigercosmos

@zhengfeihe As we previously discussed, it appears that FreeBSD cannot easily share its implementation with macOS. Therefore, let's separate the FreeBSD implementation from macOS, allowing the macOS implementation to be merged first.

Sorry for the delay. I have changed based on the request.

zhengfeihe avatar Jun 05 '24 11:06 zhengfeihe

@zhengfeihe could you check if the current test already covers your change? if not please add the corresponding tests. Also, please modify the description of the PR to explain why you separated the implementation for macOS/FreeBSD.

tigercosmos avatar Jun 05 '24 11:06 tigercosmos

@zhengfeihe could you check if the current test already covers your change? if not please add the corresponding tests. Also, please modify the description of the PR to explain why you separated the implementation for macOS/FreeBSD.

I think the test TestPcapLiveDeviceList already check the getDefaultGateway across different platforms, we don't need to add new tests.

zhengfeihe avatar Jun 08 '24 04:06 zhengfeihe

@seladb I think the PR is ready. Could you take a look?

tigercosmos avatar Jun 08 '24 06:06 tigercosmos

This PR is quite complex, I need to go over it carefully... @zhengfeihe can you share a link to the source code of route and some references on how its code works?

seladb avatar Jun 11 '24 08:06 seladb

This PR is quite complex, I need to go over it carefully... @zhengfeihe can you share a link to the source code of route and some references on how its code works?

Here's the source code link : https://github.com/apple-oss-distributions/network_cmds/blob/main/route.tproj/route.c.

I cannot find too much valuable info when I search online, so I implement the pr mainly based on running the source code on my own Mac. This is the test code that can work on my machine, maybe it will help


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <net/route.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if_dl.h>



#define ROUNDUP(a) \
	((a) > 0 ? (1 + (((a) - 1) | (sizeof(uint32_t) - 1))) : sizeof(uint32_t))
#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
#define C(x) ((unsigned char)((x) & 0xff))

int main() {
    int sockfd;

    struct bsd_route_message{
        struct	rt_msghdr m_rtm;
        char	m_space[512];
    } m_rtmsg;

    // Create the socket
    sockfd = socket(PF_ROUTE, SOCK_RAW, 0);
    if (sockfd < 0) {
        perror("socket");
        return EXIT_FAILURE;
    }


    bzero((char *)&m_rtmsg, sizeof(m_rtmsg));

    char* spacePtr = m_rtmsg.m_space;

    bzero((char *)&m_rtmsg, sizeof(m_rtmsg));
    m_rtmsg.m_rtm.rtm_msglen = sizeof(struct rt_msghdr);
    m_rtmsg.m_rtm.rtm_version = RTM_VERSION;
    m_rtmsg.m_rtm.rtm_type = RTM_GET;
    m_rtmsg.m_rtm.rtm_addrs = RTA_DST | RTA_NETMASK | RTA_IFP;
    m_rtmsg.m_rtm.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC;

    struct	sockaddr_in so_dst = {0} , so_mask = {0};
    struct	sockaddr_dl so_ifp = {0};

    size_t len = sizeof(sockaddr_in);
    bcopy(reinterpret_cast<char*>(&so_dst), spacePtr, len);
    spacePtr += len;
    bcopy(reinterpret_cast<char*>(&so_mask), spacePtr, len);
    spacePtr += len;
    m_rtmsg.m_rtm.rtm_msglen += 2*len ;
    len = sizeof(sockaddr_dl);
    spacePtr += len;
    bcopy(reinterpret_cast<char*>(&so_ifp), spacePtr, len);
    m_rtmsg.m_rtm.rtm_msglen += len ;

    if (write(sockfd, reinterpret_cast<char*>(&m_rtmsg), m_rtmsg.m_rtm.rtm_msglen) < 0) {
    //    PCPP_LOG_ERROR("Error retrieving default gateway address: couldn't write into the routing socket");
        return 1;
    }

    // Read the response from the route socket
    if (read(sockfd,reinterpret_cast<char*>(&m_rtmsg), sizeof(m_rtmsg)) < 0) {
     //   PCPP_LOG_ERROR("Error retrieving default gateway address: couldn't read from the routing socket");
        return 1;
    }

    struct sockaddr_in  *gate = nullptr;
    struct sockaddr_dl *ifp = nullptr;
    struct sockaddr *sa = nullptr;
    spacePtr = (reinterpret_cast<char*>(&m_rtmsg.m_rtm+1));
    for (int i = 1; i > 0; i <<= 1)
    {
        if (i & m_rtmsg.m_rtm.rtm_addrs)
        {
            sa =  reinterpret_cast<sockaddr *>(spacePtr);
            switch (i)
            {
                case RTA_GATEWAY:
                    gate = (sockaddr_in* )sa;
                    break;
                case RTA_IFP:
                    if (sa->sa_family == AF_LINK &&
                        ((sockaddr_dl *)sa)->sdl_nlen)
                        ifp = (sockaddr_dl *)sa;
                    break;
            }
            // Make sure the increment is the nearest multiple of the size of uint32_t
            spacePtr += sa->sa_len > 0 ? (1 + (((sa->sa_len) - 1) | (sizeof(uint32_t) - 1))) : sizeof(uint32_t);
        }
    }

    printf("%d", gate->sin_addr.s_addr);
    gate->sin_addr.s_addr = ntohl(gate->sin_addr.s_addr);
    printf("%d", gate->sin_addr.s_addr);
    printf("\n%u.%u.%u.%u",C(gate->sin_addr.s_addr>> 24), C(gate->sin_addr.s_addr>> 16), C(gate->sin_addr.s_addr>> 8), C(gate->sin_addr.s_addr));
    // Read the response from the route socket

    close(sockfd);
    
    return EXIT_SUCCESS;
}

zhengfeihe avatar Jun 11 '24 10:06 zhengfeihe

I cannot find too much valuable info when I search online, so I implement the pr mainly based on running the source code on my own Mac. This is the test code that can work on my machine, maybe it will help

This is great, thank you @zhengfeihe ! Let me try to run this code on my Mac to better understand how it works

seladb avatar Jun 12 '24 07:06 seladb

@zhengfeihe could you modify the remaining parts?

tigercosmos avatar Jun 17 '24 13:06 tigercosmos

@seladb Can you take a final look at this, and if there are no further comments, we can merge it?

zhengfeihe avatar Jun 23 '24 15:06 zhengfeihe

Thank you so much @zhengfeihe for working on this, very much appreciated! 🙏 ❤️

seladb avatar Jun 24 '24 02:06 seladb

@zhengfeihe congrats!

tigercosmos avatar Jun 24 '24 02:06 tigercosmos

Thank you so much @zhengfeihe for working on this, very much appreciated! 🙏 ❤️

Glad finally get merged! Thanks for all your kind help and review on my first contribution. @seladb @tigercosmos @Dimi1010

zhengfeihe avatar Jun 25 '24 00:06 zhengfeihe