Improve the way to get default gateway on macOS
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 next time don't resolve the issue yourself, so other people can easily track the issues. Also, could you let the tests pass?
@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 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.
@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 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.
@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.
@seladb I think the PR is ready. Could you take a look?
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?
This PR is quite complex, I need to go over it carefully... @zhengfeihe can you share a link to the source code of
routeand 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;
}
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
@zhengfeihe could you modify the remaining parts?
@seladb Can you take a final look at this, and if there are no further comments, we can merge it?
Thank you so much @zhengfeihe for working on this, very much appreciated! 🙏 ❤️
@zhengfeihe congrats!
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