Greetings,
I am trying to mimic what the official WireGuard client (available on AppStore, source code is publicly available) does regarding the routing tables. The client uses NetworkExtension framework.
When a VPN connection is established with all traffic routed through WireGuard (AllowedIPs = 0.0.0.0/0), the routing table is amend with something like this:
Destination Gateway RT_IFA Flags Refs Use Mtu Netif Expire
default link#36 10.10.10.2 UCSg 114 0 1420 utun7
10.10.10.2 10.10.10.2 10.10.10.2 UH 0 10 1420 utun7
224.0.0/4 link#36 10.10.10.2 UmCS 0 0 1420 utun7
255.255.255.255/32 link#36 10.10.10.2 UCS 0 0 1420 utun7
Please note that another default route exists to the working Ethernet interface, but I have not mentioned it above.
I would like to do something similar for wireguard-go (open source WireGuard implementation written in Go), in particular start it, assign an IP address, then add the routes.
sudo env LOG_LEVEL=debug wireguard-go -f utun
sudo ifconfig utun5 10.10.10.2 10.10.10.2 netmask 255.255.255.255
Here is the code fragment written in C which suppose to add default route (0.0.0.0/0) to the link layer address:
void add_link_route() {
struct {
struct rt_msghdr hdr;
struct sockaddr_in dest;
struct sockaddr_dl gateway;
struct sockaddr_in netmask;
} rt;
memset(&rt, 0, sizeof(rt));
int sockfd = socket(PF_ROUTE, SOCK_RAW, 0);
if (sockfd == -1) {
perror("socket");
return;
}
unsigned int if_index = if_nametoindex("utun5");
rt.hdr.rtm_msglen = sizeof(rt);
rt.hdr.rtm_version = RTM_VERSION;
rt.hdr.rtm_type = RTM_ADD;
rt.hdr.rtm_index = if_index;
rt.hdr.rtm_flags = RTF_UP | RTF_STATIC | RTF_CLONING;
rt.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
rt.hdr.rtm_seq = 1;
rt.hdr.rtm_pid = getpid();
rt.dest.sin_len = sizeof(struct sockaddr_in);
rt.dest.sin_family = AF_INET;
rt.dest.sin_addr.s_addr = INADDR_NONE;
rt.gateway.sdl_len = sizeof(struct sockaddr_dl);
rt.gateway.sdl_family = AF_LINK;
rt.gateway.sdl_index = if_index;
rt.gateway.sdl_type = IFT_PPP;
rt.netmask.sin_len = sizeof(struct sockaddr_in);
rt.netmask.sin_family = AF_INET;
rt.netmask.sin_addr.s_addr = INADDR_NONE;
if (write(sockfd, &rt, sizeof(rt)) == -1) {
perror("write");
}
close(sockfd);
}
But, when executed, write() returns EEXIST (File exists) error, meaning, the default route cannot be overwritten (because another default route exists which points to the existing Ethernet interface).
At this point I have no idea how the routes could be created successfully inside NetworkExtension, and I would like to do the same.
For comparison, there is another case when all traffice is not routed through the VPN. Then, the routes are created like this:
Destination Gateway RT_IFA Flags Refs Use Mtu Netif Expire
default link#36 10.10.10.2 UCSIg 0 0 1420 utun7
10.10.10.2 10.10.10.2 10.10.10.2 UH 0 0 1420 utun7
224.0.0/4 link#36 10.10.10.2 UmCSI 0 0 1420 utun7
255.255.255.255/32 link#36 10.10.10.2 UCSI 0 0 1420 utun7
The difference is that now the scope is bound to the network interface. And in such case, my C code succeeds, providing I add RTF_IFSCOPE flag to rtm_flags.
I would appreciate if someone helped me with this problem.