static int prefixToNetmask(int pfx, struct sockaddr_in *netmask_sin) { struct sockaddr *mask = (struct sockaddr *)netmask_sin; if (plen2mask(pfx, AF_INET, mask) != 0) { return (-1); } return (0); }
int kernel_route(int operation, const unsigned char *dest, unsigned short plen, const unsigned char *gate, int ifindex, unsigned int metric, const unsigned char *newgate, int newifindex, unsigned int newmetric) { struct { struct rt_msghdr m_rtm; char m_space[512]; } msg; char *data = msg.m_space; int rc, ipv4; char local6[1][1][16] = IN6ADDR_LOOPBACK_INIT; char local4[1][1][16] = {{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x01 }}}; /* Check that the protocol family is consistent. */ if(plen >= 96 && v4mapped(dest)) { if(!v4mapped(gate)) { errno = EINVAL; return -1; } ipv4 = 1; } else { if(v4mapped(gate)) { errno = EINVAL; return -1; } ipv4 = 0; } if(operation == ROUTE_MODIFY && newmetric == metric && memcmp(newgate, gate, 16) == 0 && newifindex == ifindex) return 0; if(operation == ROUTE_MODIFY) { /* Avoid atomic route changes that is buggy on OS X. */ kernel_route(ROUTE_FLUSH, dest, plen, gate, ifindex, metric, NULL, 0, 0); return kernel_route(ROUTE_ADD, dest, plen, newgate, newifindex, newmetric, NULL, 0, 0); } kdebugf("kernel_route: %s %s/%d metric %d dev %d nexthop %s\n", operation == ROUTE_ADD ? "add" : operation == ROUTE_FLUSH ? "flush" : "change", format_address(dest), plen, metric, ifindex, format_address(gate)); if(kernel_socket < 0) kernel_setup_socket(1); memset(&msg, 0, sizeof(msg)); msg.m_rtm.rtm_version = RTM_VERSION; switch(operation) { case ROUTE_FLUSH: msg.m_rtm.rtm_type = RTM_DELETE; break; case ROUTE_ADD: msg.m_rtm.rtm_type = RTM_ADD; break; case ROUTE_MODIFY: msg.m_rtm.rtm_type = RTM_CHANGE; break; default: return -1; }; msg.m_rtm.rtm_index = ifindex; msg.m_rtm.rtm_flags = RTF_UP | RTF_PROTO2; if(plen == 128) msg.m_rtm.rtm_flags |= RTF_HOST; if(metric == KERNEL_INFINITY) { msg.m_rtm.rtm_flags |= RTF_BLACKHOLE; if(ifindex_lo < 0) { ifindex_lo = if_nametoindex("lo0"); if(ifindex_lo <= 0) return -1; } msg.m_rtm.rtm_index = ifindex_lo; } msg.m_rtm.rtm_seq = ++seq; msg.m_rtm.rtm_addrs = RTA_DST | RTA_GATEWAY; if(plen != 128) msg.m_rtm.rtm_addrs |= RTA_NETMASK; #define PUSHEUI(ifindex) \ do { char ifname[IFNAMSIZ]; \ struct sockaddr_dl *sdl = (struct sockaddr_dl*) data; \ if(!if_indextoname((ifindex), ifname)) \ return -1; \ if(get_sdl(sdl, ifname) < 0) \ return -1; \ data = data + ROUNDUP(sdl->sdl_len); \ } while (0) #define PUSHADDR(src) \ do { struct sockaddr_in *sin = (struct sockaddr_in*) data; \ sin->sin_len = sizeof(struct sockaddr_in); \ sin->sin_family = AF_INET; \ memcpy(&sin->sin_addr, (src) + 12, 4); \ data = data + ROUNDUP(sin->sin_len); \ } while (0) #define PUSHADDR6(src) \ do { struct sockaddr_in6 *sin6 = (struct sockaddr_in6*) data; \ sin6->sin6_len = sizeof(struct sockaddr_in6); \ sin6->sin6_family = AF_INET6; \ memcpy(&sin6->sin6_addr, (src), 16); \ if(IN6_IS_ADDR_LINKLOCAL (&sin6->sin6_addr)) \ SET_IN6_LINKLOCAL_IFINDEX (sin6->sin6_addr, ifindex); \ data = data + ROUNDUP(sin6->sin6_len); \ } while (0) /* KAME ipv6 stack does not support IPv4 mapped IPv6, so we have to * duplicate the codepath */ if(ipv4) { PUSHADDR(dest); if (metric == KERNEL_INFINITY) { PUSHADDR(**local4); } else if(plen == 128 && memcmp(dest+12, gate+12, 4) == 0) { #if defined(RTF_CLONING) msg.m_rtm.rtm_flags |= RTF_CLONING; #endif PUSHEUI(ifindex); } else { msg.m_rtm.rtm_flags |= RTF_GATEWAY; PUSHADDR(gate); } if((msg.m_rtm.rtm_addrs & RTA_NETMASK) != 0) { struct in6_addr tmp_sin6_addr; plen2mask(plen, &tmp_sin6_addr); PUSHADDR((char *)&tmp_sin6_addr); } } else { PUSHADDR6(dest); if (metric == KERNEL_INFINITY) { PUSHADDR6(**local6); } else { msg.m_rtm.rtm_flags |= RTF_GATEWAY; PUSHADDR6(gate); } if((msg.m_rtm.rtm_addrs & RTA_NETMASK) != 0) { struct in6_addr tmp_sin6_addr; plen2mask(plen, &tmp_sin6_addr); PUSHADDR6((char*)&tmp_sin6_addr); } } #undef PUSHEUI #undef PUSHADDR #undef PUSHADDR6 msg.m_rtm.rtm_msglen = data - (char *)&msg; rc = write(kernel_socket, (char*)&msg, msg.m_rtm.rtm_msglen); if (rc < msg.m_rtm.rtm_msglen) return -1; return 1; }
/* * Tell the kernel to add, delete or change a route */ static void i_ipadm_rtioctl4(int rtsock, int action, /* RTM_DELETE, etc */ in_addr_t dst, in_addr_t gate, uint_t masklen, char *ifname, uint8_t metric, int flags) { static int rt_sock_seqno = 0; struct { struct rt_msghdr w_rtm; struct sockaddr_in w_dst; struct sockaddr_in w_gate; uint8_t w_space[512]; } w; struct sockaddr_in w_mask; struct sockaddr_dl w_ifp; uint8_t *cp; long cc; again: (void) memset(&w, 0, sizeof (w)); (void) memset(&w_mask, 0, sizeof (w_mask)); (void) memset(&w_ifp, 0, sizeof (w_ifp)); cp = w.w_space; w.w_rtm.rtm_msglen = sizeof (struct rt_msghdr) + 2 * ROUNDUP_LONG(sizeof (struct sockaddr_in)); w.w_rtm.rtm_version = RTM_VERSION; w.w_rtm.rtm_type = action; w.w_rtm.rtm_flags = (flags | RTF_ZONE); w.w_rtm.rtm_seq = ++rt_sock_seqno; w.w_rtm.rtm_addrs = RTA_DST|RTA_GATEWAY; if (metric != 0 || action == RTM_CHANGE) { w.w_rtm.rtm_rmx.rmx_hopcount = metric; w.w_rtm.rtm_inits |= RTV_HOPCOUNT; } w.w_dst.sin_family = AF_INET; w.w_dst.sin_addr.s_addr = dst; w.w_gate.sin_family = AF_INET; w.w_gate.sin_addr.s_addr = gate; if (masklen == HOST_MASK) { w.w_rtm.rtm_flags |= RTF_HOST; } else { struct sockaddr_storage m4; w.w_rtm.rtm_addrs |= RTA_NETMASK; w_mask.sin_family = AF_INET; if (plen2mask(masklen, AF_INET, (struct sockaddr *)&m4) != 0) { return; } w_mask.sin_addr = ((struct sockaddr_in *)&m4)->sin_addr; (void) memmove(cp, &w_mask, sizeof (w_mask)); cp += ROUNDUP_LONG(sizeof (struct sockaddr_in)); w.w_rtm.rtm_msglen += ROUNDUP_LONG(sizeof (struct sockaddr_in)); } w_ifp.sdl_family = AF_LINK; w.w_rtm.rtm_addrs |= RTA_IFP; w_ifp.sdl_index = if_nametoindex(ifname); (void) memmove(cp, &w_ifp, sizeof (w_ifp)); w.w_rtm.rtm_msglen += ROUNDUP_LONG(sizeof (struct sockaddr_dl)); cc = write(rtsock, &w, w.w_rtm.rtm_msglen); if (cc < 0) { if (errno == ESRCH && (action == RTM_CHANGE || action == RTM_DELETE)) { if (action == RTM_CHANGE) { action = RTM_ADD; goto again; } return; } return; } else if (cc != w.w_rtm.rtm_msglen) { return; } }
int kernel_route(int operation, const unsigned char *dest, unsigned short plen, const unsigned char *gate, int ifindex, unsigned int metric, const unsigned char *newgate, int newifindex, unsigned int newmetric) { unsigned char msg[512]; struct rt_msghdr *rtm; struct sockaddr_in6 *sin6; struct sockaddr_in *sin; int rc, len, ipv4; char local6[1][1][16] = IN6ADDR_LOOPBACK_INIT; char local4[1][1][16] = {{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x01 }}}; /* Check that the protocol family is consistent. */ if(plen >= 96 && v4mapped(dest)) { if(!v4mapped(gate)) { errno = EINVAL; return -1; } ipv4 = 1; } else { if(v4mapped(gate)) { errno = EINVAL; return -1; } ipv4 = 0; } if(operation == ROUTE_MODIFY && newmetric == metric && memcmp(newgate, gate, 16) == 0 && newifindex == ifindex) return 0; if(operation == ROUTE_MODIFY) { metric = newmetric; gate = newgate; ifindex = newifindex; } kdebugf("kernel_route: %s %s/%d metric %d dev %d nexthop %s\n", operation == ROUTE_ADD ? "add" : operation == ROUTE_FLUSH ? "flush" : "change", format_address(dest), plen, metric, ifindex, format_address(gate)); if(kernel_socket < 0) kernel_setup_socket(1); memset(&msg, 0, sizeof(msg)); rtm = (struct rt_msghdr *)msg; rtm->rtm_version = RTM_VERSION; switch(operation) { case ROUTE_FLUSH: rtm->rtm_type = RTM_DELETE; break; case ROUTE_ADD: rtm->rtm_type = RTM_ADD; break; case ROUTE_MODIFY: rtm->rtm_type = RTM_CHANGE; break; default: return -1; }; rtm->rtm_index = ifindex; rtm->rtm_flags = RTF_UP | RTF_PROTO2; if(plen == 128) rtm->rtm_flags |= RTF_HOST; /* if(memcmp(nexthop->id, dest, 16) == 0) { */ /* rtm -> rtm_flags |= RTF_LLINFO; */ /* rtm -> rtm_flags |= RTF_CLONING; */ /* } else { */ rtm->rtm_flags |= RTF_GATEWAY; /* } */ if(metric == KERNEL_INFINITY) { rtm->rtm_flags |= RTF_BLACKHOLE; if(ifindex_lo < 0) { ifindex_lo = if_nametoindex("lo0"); if(ifindex_lo <= 0) return -1; } rtm->rtm_index = ifindex_lo; } rtm->rtm_seq = ++seq; rtm->rtm_addrs = RTA_DST | RTA_GATEWAY; if(!(operation == ROUTE_MODIFY && plen == 128)) { rtm->rtm_addrs |= RTA_NETMASK; } #define push_sockaddr_in(ptr, offset) \ do { (ptr) = (struct sockaddr_in *)((char *)(ptr) + (offset)); \ (ptr)->sin_len = sizeof(struct sockaddr_in); \ (ptr)->sin_family = AF_INET; } while (0) #define get_sin_addr(dst,src) \ do { memcpy((dst), (src) + 12, 4); } while (0) #define push_sockaddr_in6(ptr, offset) \ do { (ptr) = (struct sockaddr_in6 *)((char *)(ptr) + (offset)); \ (ptr)->sin6_len = sizeof(struct sockaddr_in6); \ (ptr)->sin6_family = AF_INET6; } while (0) #define get_sin6_addr(dst,src) \ do { memcpy((dst), (src), 16); } while (0) /* KAME ipv6 stack does not support IPv4 mapped IPv6, so we have to * duplicate the codepath */ if(ipv4) { sin = (struct sockaddr_in *)msg; /* destination */ push_sockaddr_in(sin, sizeof(*rtm)); get_sin_addr(&(sin->sin_addr), dest); /* gateway */ push_sockaddr_in(sin, ROUNDUP(sin->sin_len)); if (metric == KERNEL_INFINITY) get_sin_addr(&(sin->sin_addr),**local4); else get_sin_addr(&(sin->sin_addr),gate); /* netmask */ if((rtm->rtm_addrs | RTA_NETMASK) != 0) { struct in6_addr tmp_sin6_addr; push_sockaddr_in(sin, ROUNDUP(sin->sin_len)); plen2mask(plen, &tmp_sin6_addr); get_sin_addr(&(sin->sin_addr), (char *)&tmp_sin6_addr); } len = (char *)sin + ROUNDUP(sin->sin_len) - (char *)msg; } else { sin6 = (struct sockaddr_in6 *)msg; /* destination */ push_sockaddr_in6(sin6, sizeof(*rtm)); get_sin6_addr(&(sin6->sin6_addr), dest); /* gateway */ push_sockaddr_in6(sin6, ROUNDUP(sin6->sin6_len)); if (metric == KERNEL_INFINITY) get_sin6_addr(&(sin6->sin6_addr),**local6); else get_sin6_addr(&(sin6->sin6_addr),gate); if(IN6_IS_ADDR_LINKLOCAL (&sin6->sin6_addr)) SET_IN6_LINKLOCAL_IFINDEX (sin6->sin6_addr, ifindex); /* netmask */ if((rtm->rtm_addrs | RTA_NETMASK) != 0) { push_sockaddr_in6(sin6, ROUNDUP(sin6->sin6_len)); plen2mask(plen, &sin6->sin6_addr); } len = (char *)sin6 + ROUNDUP(sin6->sin6_len) - (char *)msg; } rtm->rtm_msglen = len; rc = write(kernel_socket, msg, rtm->rtm_msglen); if (rc < rtm->rtm_msglen) return -1; return 1; }
static void i_ipadm_rtioctl6(int rtsock, int action, /* RTM_DELETE, etc */ in6_addr_t dst, in6_addr_t gate, uint_t prefix_length, char *ifname, int flags) { static int rt_sock_seqno = 0; struct { struct rt_msghdr w_rtm; struct sockaddr_in6 w_dst; struct sockaddr_in6 w_gate; uint8_t w_space[512]; } w; struct sockaddr_in6 w_mask; struct sockaddr_dl w_ifp; uint8_t *cp; long cc; again: (void) memset(&w, 0, sizeof (w)); (void) memset(&w_mask, 0, sizeof (w_mask)); (void) memset(&w_ifp, 0, sizeof (w_ifp)); cp = w.w_space; w.w_rtm.rtm_msglen = sizeof (struct rt_msghdr) + 2 * ROUNDUP_LONG(sizeof (struct sockaddr_in6)); w.w_rtm.rtm_version = RTM_VERSION; w.w_rtm.rtm_type = action; w.w_rtm.rtm_flags = (flags | RTF_ZONE); w.w_rtm.rtm_seq = ++rt_sock_seqno; w.w_rtm.rtm_addrs = RTA_DST|RTA_GATEWAY; w.w_dst.sin6_family = AF_INET6; w.w_dst.sin6_addr = dst; w.w_gate.sin6_family = AF_INET6; w.w_gate.sin6_addr = gate; if (prefix_length == IPV6_ABITS) { w.w_rtm.rtm_flags |= RTF_HOST; } else { struct sockaddr_storage m6; w.w_rtm.rtm_addrs |= RTA_NETMASK; w_mask.sin6_family = AF_INET6; if (plen2mask(prefix_length, AF_INET6, (struct sockaddr *)&m6) != 0) { return; } w_mask.sin6_addr = ((struct sockaddr_in6 *)&m6)->sin6_addr; (void) memmove(cp, &w_mask, sizeof (w_mask)); cp += ROUNDUP_LONG(sizeof (struct sockaddr_in6)); w.w_rtm.rtm_msglen += ROUNDUP_LONG(sizeof (struct sockaddr_in6)); } w_ifp.sdl_family = AF_LINK; w.w_rtm.rtm_addrs |= RTA_IFP; w_ifp.sdl_index = if_nametoindex(ifname); (void) memmove(cp, &w_ifp, sizeof (w_ifp)); w.w_rtm.rtm_msglen += ROUNDUP_LONG(sizeof (struct sockaddr_dl)); cc = write(rtsock, &w, w.w_rtm.rtm_msglen); if (cc < 0) { if (errno == ESRCH && (action == RTM_CHANGE || action == RTM_DELETE)) { if (action == RTM_CHANGE) { action = RTM_ADD; goto again; } return; } return; } else if (cc != w.w_rtm.rtm_msglen) { return; } }