Last active
November 28, 2019 06:16
-
-
Save icpz/16434144953cf4d43a02899badac6723 to your computer and use it in GitHub Desktop.
manipulate routing table via route socket on darwin
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <sys/socket.h> | |
#include <sys/types.h> | |
#include <net/if.h> | |
#include <net/if_dl.h> | |
#include <netinet/in.h> | |
#include <net/route.h> | |
#include <arpa/inet.h> | |
#include <ifaddrs.h> | |
#include <unistd.h> | |
#include <string.h> | |
#include "darwin-route.h" | |
union sockaddr_union { | |
struct sockaddr sa; | |
struct sockaddr_in sin; | |
struct sockaddr_in6 sin6; | |
struct sockaddr_dl sdl; | |
struct sockaddr_storage ss; /* not used */ | |
}; | |
/* align to 4 bytes */ | |
static inline size_t round_up_4(size_t n) { | |
return (n > 0 ? (1 + ((n - 1) | 3)) : 4); | |
} | |
/* fixme: support ipv6 */ | |
static int get_sockaddr(union sockaddr_union *sa, const char *ad, int iface) { | |
struct ifaddrs *ifap, *ifa; | |
struct sockaddr_dl *sdl = NULL; | |
memset(sa, 0, sizeof *sa); | |
if (!iface) { | |
if (inet_pton(AF_INET, ad, &sa->sin.sin_addr) != 1) { | |
return -1; | |
} | |
sa->sin.sin_len = sizeof sa->sin; | |
sa->sin.sin_family = AF_INET; | |
} else { | |
if (getifaddrs(&ifap)) { | |
return -2; | |
} | |
for (ifa = ifap; ifa; ifa = ifa->ifa_next) { | |
if (ifa->ifa_addr->sa_family != AF_LINK) { | |
continue; | |
} | |
if (strcmp(ad, ifa->ifa_name)) { | |
continue; | |
} | |
sdl = (struct sockaddr_dl *)ifa->ifa_addr; | |
} | |
if (sdl) { | |
memcpy(&sa->sdl, sdl, sdl->sdl_len); | |
} | |
freeifaddrs(ifap); | |
if (!sdl) { | |
return -3; | |
} | |
} | |
return 0; | |
} | |
static inline void fix_mask_sockaddr(union sockaddr_union *mask) { | |
char *head = (char *)mask; | |
char *tail = head + mask->sa.sa_len; | |
while (head < tail && *--tail == 0) | |
; | |
mask->sa.sa_len = tail - head + 1; | |
} | |
int darwin_route_act(int act, const char *dst, const char *gateway, int masklen, int iface) { | |
struct { | |
struct rt_msghdr hdr; | |
char data[512]; | |
} rtmsg; | |
int fd; | |
union sockaddr_union sa_dst, sa_gw, sa_mask; | |
int err; | |
char *p = NULL; | |
memset(&rtmsg, 0, sizeof rtmsg); | |
memset(&sa_dst, 0, sizeof sa_dst); | |
memset(&sa_gw, 0, sizeof sa_gw); | |
memset(&sa_mask, 0, sizeof sa_mask); | |
if (get_sockaddr(&sa_dst, dst, 0) != 0) { | |
err = RTE_DST; | |
goto __err_arg; | |
} | |
if (get_sockaddr(&sa_gw, gateway, iface) != 0) { | |
err = RTE_GATE; | |
goto __err_arg; | |
} | |
if (masklen < 0 || masklen > 32) { | |
err = RTE_MASK; | |
goto __err_arg; | |
} | |
/* fixme: support ipv6 */ | |
sa_mask.sin.sin_len = sizeof sa_mask.sin; | |
sa_mask.sin.sin_family = AF_INET; | |
sa_mask.sin.sin_addr.s_addr = \ | |
htonl(masklen == 32 ? 0xFFFFFFFF : (((1 << masklen) - 1) << (32 - masklen))); | |
fd = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC); | |
if (fd < 0) { | |
err = RTE_SYS; | |
goto __no_fd; | |
} | |
rtmsg.hdr.rtm_type = act; | |
rtmsg.hdr.rtm_flags = RTF_UP | RTF_STATIC | (iface ? 0 : RTF_GATEWAY); | |
rtmsg.hdr.rtm_version = RTM_VERSION; | |
rtmsg.hdr.rtm_seq = 1; | |
rtmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK; | |
#define ADD_ADDR(u) \ | |
{ \ | |
int l = round_up_4(u.sa.sa_len); \ | |
memcpy(p, (char *)&(u), l); \ | |
p += l; \ | |
} | |
fix_mask_sockaddr(&sa_mask); | |
p = rtmsg.data; | |
ADD_ADDR(sa_dst); | |
ADD_ADDR(sa_gw); | |
ADD_ADDR(sa_mask); | |
rtmsg.hdr.rtm_msglen = p - (char *)&rtmsg; | |
if (write(fd, (char *)&rtmsg, rtmsg.hdr.rtm_msglen) < 0) { | |
err = RTE_SYS; | |
goto __err_write; | |
} | |
err = RTE_OK; | |
#undef ADD_ADDR | |
__err_write: | |
close(fd); | |
__no_fd: | |
__err_arg: | |
return err; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef DARWIN_ROUTE_H | |
#define DARWIN_ROUTE_H | |
#include <net/route.h> | |
enum { | |
RTE_OK, | |
RTE_DST, | |
RTE_MASK, | |
RTE_GATE, | |
RTE_SYS, /* syscall error, caller may check errno for detail */ | |
}; | |
/** | |
* act: RTM_ADD or RTM_DELETE or RTM_CHANGE | |
* dst: destination WITHOUT mask | |
* gateway: gateway address or interface name | |
* masklen: leading 1s of mask | |
* iface: if gateway is an interface | |
*/ | |
/* fixme: support ipv6 */ | |
int darwin_route_act(int act, const char *dst, const char *gateway, int masklen, int iface); | |
#endif /* DARWIN_ROUTE_H */ |
Author
icpz
commented
Nov 24, 2019
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment