#include "rtutil.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/errno.h>
#include <sys/socket.h>
#include <net/route.h>
#include <net/if_dl.h>
#define KERNEL_PRIVATE
#include <sys/ioctl.h>
#undef KERNEL_PRIVATE
#include <mach/boolean.h>
#include "symbol_scope.h"
#include "util.h"
#include "arp.h"
#include "mylog.h"
#include "globals.h"
STATIC void
set_sockaddr_in(struct sockaddr_in * sin_p, struct in_addr iaddr)
{
sin_p->sin_len = sizeof(*sin_p);
sin_p->sin_family = AF_INET;
sin_p->sin_addr = iaddr;
}
PRIVATE_EXTERN boolean_t
host_route(int cmd, struct in_addr iaddr)
{
int len;
struct in_addr loopback = { htonl(INADDR_LOOPBACK) };
boolean_t ret = TRUE;
struct rt_msghdr * rtm;
struct {
struct rt_msghdr hdr;
struct sockaddr_in dst;
struct sockaddr_in gway;
} rtmsg;
int sockfd = -1;
sockfd = arp_open_routing_socket();
if (sockfd < 0) {
my_log(LOG_NOTICE, "host_route: open routing socket failed, %s",
strerror(errno));
ret = FALSE;
goto done;
}
memset(&rtmsg, 0, sizeof(rtmsg));
rtm = &rtmsg.hdr;
rtm->rtm_type = cmd;
rtm->rtm_flags = RTF_UP | RTF_STATIC | RTF_HOST;
rtm->rtm_version = RTM_VERSION;
rtm->rtm_seq = arp_get_next_seq();
rtm->rtm_addrs = RTA_DST | RTA_GATEWAY;
set_sockaddr_in(&rtmsg.dst, iaddr);
set_sockaddr_in(&rtmsg.gway, loopback);
len = sizeof(rtmsg);
rtm->rtm_msglen = len;
if (write(sockfd, &rtmsg, len) < 0) {
int error = errno;
switch (error) {
case ESRCH:
case EEXIST:
my_log(LOG_DEBUG, "host_route: write routing socket failed, %s",
strerror(error));
break;
default:
my_log(LOG_NOTICE, "host_route: write routing socket failed, %s",
strerror(error));
break;
}
ret = FALSE;
}
done:
if (sockfd >= 0) {
close(sockfd);
}
return (ret);
}
#define ROUNDUP(a) \
((a) > 0 ? (1 + (((a) - 1) | (sizeof(u_int32_t) - 1))) : sizeof(u_int32_t))
PRIVATE_EXTERN int
rt_xaddrs(const char * cp, const char * cplim, struct rt_addrinfo * rtinfo)
{
int i;
struct sockaddr * sa;
bzero(rtinfo->rti_info, sizeof(rtinfo->rti_info));
for (i = 0; (i < RTAX_MAX) && (cp < cplim); i++) {
if ((rtinfo->rti_addrs & (1 << i)) == 0) {
continue;
}
sa = (struct sockaddr *)cp;
if ((cp + sa->sa_len) > cplim) {
return (EINVAL);
}
rtinfo->rti_info[i] = sa;
cp += ROUNDUP(sa->sa_len);
}
return (0);
}
PRIVATE_EXTERN int
subnet_route_if_index(struct in_addr netaddr, struct in_addr netmask)
{
struct sockaddr_in * addr_p;
int len;
int n;
char * ptr;
int pid;
boolean_t ret_if_index = 0;
struct rt_msghdr * rtm;
int rtm_seq;
struct {
struct rt_msghdr hdr;
char data[512];
} rtmsg;
struct sockaddr_dl * sdl;
int sockfd = -1;
sockfd = arp_open_routing_socket();
if (sockfd < 0) {
my_log(LOG_NOTICE,
"subnet_route_if_index: open routing socket failed, %s",
strerror(errno));
goto done;
}
memset(&rtmsg, 0, sizeof(rtmsg));
rtm = &rtmsg.hdr;
rtm->rtm_type = RTM_GET_SILENT;
rtm->rtm_version = RTM_VERSION;
rtm->rtm_seq = rtm_seq = arp_get_next_seq();
rtm->rtm_addrs = RTA_DST | RTA_NETMASK;
ptr = rtmsg.data;
addr_p = (struct sockaddr_in *)ptr;
addr_p->sin_len = sizeof(*addr_p);
addr_p->sin_family = AF_INET;
addr_p->sin_addr = netaddr;
ptr += sizeof(*addr_p);
addr_p = (struct sockaddr_in *)ptr;
addr_p->sin_len = sizeof(*addr_p);
addr_p->sin_family = AF_INET;
addr_p->sin_addr = netmask;
ptr += sizeof(*addr_p);
len = ptr - (char *)&rtmsg;
rtm->rtm_msglen = len;
if (write(sockfd, &rtmsg, len) < 0) {
int error = errno;
switch (error) {
case ESRCH:
case EEXIST:
my_log(LOG_DEBUG,
"subnet_route_if_index: write routing socket failed, %s",
strerror(error));
break;
default:
my_log(LOG_NOTICE,
"subnet_route_if_index: write routing socket failed, %s",
strerror(error));
break;
}
goto done;
}
pid = getpid();
while ((n = read(sockfd, &rtmsg, sizeof(rtmsg))) > 0) {
struct rt_addrinfo info;
if (rtm->rtm_type != RTM_GET
|| rtm->rtm_seq != rtm_seq || rtm->rtm_pid != pid) {
continue;
}
info.rti_addrs = rtm->rtm_addrs;
if (rt_xaddrs(rtmsg.data, rtmsg.data + n, &info) != 0) {
syslog(LOG_DEBUG, "subnet_route_if_index: rt_xaddrs failed");
break;
}
sdl = (struct sockaddr_dl *)info.rti_info[RTAX_GATEWAY];
if (sdl == NULL || sdl->sdl_family != AF_LINK) {
syslog(LOG_DEBUG,
"subnet_route_if_index: can't get interface name");
break;
}
ret_if_index = sdl->sdl_index;
break;
}
done:
if (sockfd >= 0) {
close(sockfd);
}
return (ret_if_index);
}
STATIC boolean_t
subnet_route(int cmd, struct in_addr gateway, struct in_addr netaddr,
struct in_addr netmask, const char * ifname)
{
int len;
boolean_t ret = TRUE;
struct rt_msghdr * rtm;
struct {
struct rt_msghdr hdr;
struct sockaddr_in dst;
struct sockaddr_in gway;
struct sockaddr_in mask;
struct sockaddr_dl ifp;
struct sockaddr_in ifa;
} rtmsg;
int sockfd = -1;
sockfd = arp_open_routing_socket();
if (sockfd < 0) {
my_log(LOG_NOTICE, "subnet_route: open routing socket failed, %s",
strerror(errno));
ret = FALSE;
goto done;
}
memset(&rtmsg, 0, sizeof(rtmsg));
rtm = &rtmsg.hdr;
rtm->rtm_type = cmd;
rtm->rtm_flags = RTF_UP | RTF_STATIC | RTF_CLONING;
rtm->rtm_version = RTM_VERSION;
rtm->rtm_seq = arp_get_next_seq();
rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
set_sockaddr_in(&rtmsg.dst, netaddr);
set_sockaddr_in(&rtmsg.gway, gateway);
set_sockaddr_in(&rtmsg.mask, netmask);
len = sizeof(rtmsg);
if (ifname != NULL) {
rtm->rtm_addrs |= RTA_IFP | RTA_IFA;
rtmsg.ifp.sdl_len = sizeof(rtmsg.ifp);
rtmsg.ifp.sdl_family = AF_LINK;
rtmsg.ifp.sdl_nlen = strlen(ifname);
bcopy(ifname, rtmsg.ifp.sdl_data, rtmsg.ifp.sdl_nlen);
set_sockaddr_in(&rtmsg.ifa, gateway);
}
else {
len -= sizeof(rtmsg.ifp) + sizeof(rtmsg.ifa);
}
rtm->rtm_msglen = len;
if (write(sockfd, &rtmsg, len) < 0) {
int error = errno;
switch (error) {
case ESRCH:
case EEXIST:
my_log(LOG_DEBUG, "subnet_route: write routing socket failed, %s",
strerror(error));
break;
default:
my_log(LOG_NOTICE, "subnet_route: write routing socket failed, %s",
strerror(error));
break;
}
ret = FALSE;
}
done:
if (sockfd >= 0) {
close(sockfd);
}
return (ret);
}
PRIVATE_EXTERN boolean_t
subnet_route_add(struct in_addr gateway, struct in_addr netaddr,
struct in_addr netmask, const char * ifname)
{
return (subnet_route(RTM_ADD, gateway, netaddr, netmask, ifname));
}
PRIVATE_EXTERN boolean_t
subnet_route_delete(struct in_addr gateway, struct in_addr netaddr,
struct in_addr netmask)
{
return (subnet_route(RTM_DELETE, gateway, netaddr, netmask, NULL));
}
#define N_MIB 6
STATIC int
flush_dynamic_routes(int s)
{
char * buf = NULL;
int i;
char * lim;
int mib[N_MIB];
size_t needed;
char * next;
struct rt_msghdr * rtm;
struct sockaddr_in *sin;
mib[0] = CTL_NET;
mib[1] = PF_ROUTE;
mib[2] = 0;
mib[3] = AF_INET;
mib[4] = NET_RT_FLAGS;
mib[5] = RTF_DYNAMIC;
for (i = 0; i < 3; i++) {
if (sysctl(mib, N_MIB, NULL, &needed, NULL, 0) < 0) {
break;
}
if ((buf = malloc(needed)) == NULL) {
break;
}
if (sysctl(mib, N_MIB, buf, &needed, NULL, 0) >= 0) {
break;
}
free(buf);
buf = NULL;
}
if (buf == NULL) {
return (-1);
}
lim = buf + needed;
for (next = buf; next < lim; next += rtm->rtm_msglen) {
rtm = (struct rt_msghdr *)next;
sin = (struct sockaddr_in *)(rtm + 1);
rtm->rtm_type = RTM_DELETE;
rtm->rtm_seq = arp_get_next_seq();
if (write(s, rtm, rtm->rtm_msglen) < 0) {
my_log(LOG_NOTICE,
"IPConfiguration: removing dynamic route for "
IP_FORMAT " failed, %s",
IP_LIST(&sin->sin_addr),
strerror(errno));
}
else if (G_IPConfiguration_verbose) {
my_log(LOG_NOTICE,
"IPConfiguration: removed dynamic route for " IP_FORMAT,
IP_LIST(&sin->sin_addr));
}
}
free(buf);
return (0);
}
PRIVATE_EXTERN void
flush_routes(int if_index, const struct in_addr ip,
const struct in_addr broadcast)
{
int s;
s = arp_open_routing_socket();
if (s < 0) {
return;
}
if (ip.s_addr) {
(void)arp_delete(s, ip, 0);
}
if (broadcast.s_addr) {
(void)arp_delete(s, broadcast, 0);
}
(void)arp_flush(s, FALSE, if_index);
(void)flush_dynamic_routes(s);
close(s);
return;
}