Example #1
0
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);
}
Example #2
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;
}
Example #3
0
/*
 * 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;
	}
}
Example #4
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)
{
    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;
}
Example #5
0
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;
	}
}