Skip to content

Instantly share code, notes, and snippets.

@icpz
Last active November 28, 2019 06:16
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save icpz/16434144953cf4d43a02899badac6723 to your computer and use it in GitHub Desktop.
Save icpz/16434144953cf4d43a02899badac6723 to your computer and use it in GitHub Desktop.
manipulate routing table via route socket on darwin
#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;
}
#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 */
@icpz
Copy link
Author

icpz commented Nov 24, 2019

/* normal item */
darwin_route_act(RTM_ADD, "10.3.0.0", "123.123.123.123", 16, 0);
/* directly reachable item */
darwin_route_act(RTM_ADD, "10.3.0.0", "en0", 16, 1);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment