From 8984bb9691f8d3e2665f7aae0896b9bd2ade0c19 Mon Sep 17 00:00:00 2001 From: "Banavathu, Srinivas Naik" Date: Wed, 11 Aug 2010 01:23:54 +0530 Subject: [PATCH] Add API to add a route to IPv4/IPv6 host/gateway. Add API to create a route to an IPv4/IPv6 host/gateway through a particular interface. Change-Id: I0ab5d2d211e0fc05f65ee6bb202b7f435cccae2a --- include/netutils/ifc.h | 5 + libnetutils/ifc_utils.c | 211 +++++++++++++++++++++++++++++++++++----- 2 files changed, 194 insertions(+), 22 deletions(-) diff --git a/include/netutils/ifc.h b/include/netutils/ifc.h index 2e502ab63..e24526299 100644 --- a/include/netutils/ifc.h +++ b/include/netutils/ifc.h @@ -40,12 +40,17 @@ extern int ifc_set_addr(const char *name, in_addr_t addr); extern int ifc_set_mask(const char *name, in_addr_t mask); extern int ifc_set_hwaddr(const char *name, const void *ptr); +/* This function is deprecated. Use ifc_add_route instead. */ extern int ifc_add_host_route(const char *name, in_addr_t addr); extern int ifc_remove_host_routes(const char *name); extern int ifc_get_default_route(const char *ifname); +/* This function is deprecated. Use ifc_add_route instead */ extern int ifc_set_default_route(const char *ifname, in_addr_t gateway); +/* This function is deprecated. Use ifc_add_route instead */ extern int ifc_create_default_route(const char *name, in_addr_t addr); extern int ifc_remove_default_route(const char *ifname); +extern int ifc_add_route(const char *name, const char *addr, int prefix_length, + const char *gw); extern int ifc_get_info(const char *name, in_addr_t *addr, in_addr_t *mask, in_addr_t *flags); diff --git a/libnetutils/ifc_utils.c b/libnetutils/ifc_utils.c index 296d617d4..fc5689687 100644 --- a/libnetutils/ifc_utils.c +++ b/libnetutils/ifc_utils.c @@ -31,6 +31,9 @@ #include #include #include +#include +#include +#include #include #ifdef ANDROID @@ -45,6 +48,7 @@ #endif static int ifc_ctl_sock = -1; +static int ifc_ctl_sock6 = -1; void printerr(char *fmt, ...); static const char *ipaddr_to_string(in_addr_t addr) @@ -66,6 +70,17 @@ int ifc_init(void) return ifc_ctl_sock < 0 ? -1 : 0; } +int ifc_init6(void) +{ + if (ifc_ctl_sock6 == -1) { + ifc_ctl_sock6 = socket(AF_INET6, SOCK_DGRAM, 0); + if (ifc_ctl_sock6 < 0) { + printerr("socket() failed: %s\n", strerror(errno)); + } + } + return ifc_ctl_sock6 < 0 ? -1 : 0; +} + void ifc_close(void) { if (ifc_ctl_sock != -1) { @@ -74,6 +89,14 @@ void ifc_close(void) } } +void ifc_close6(void) +{ + if (ifc_ctl_sock6 != -1) { + (void)close(ifc_ctl_sock6); + ifc_ctl_sock6 = -1; + } +} + static void ifc_init_ifr(const char *name, struct ifreq *ifr) { memset(ifr, 0, sizeof(struct ifreq)); @@ -198,45 +221,80 @@ int ifc_get_info(const char *name, in_addr_t *addr, in_addr_t *mask, unsigned *f return 0; } - -int ifc_create_default_route(const char *name, in_addr_t addr) +in_addr_t get_ipv4_netmask(int prefix_length) { - struct rtentry rt; + in_addr_t mask = 0; - memset(&rt, 0, sizeof(rt)); - - rt.rt_dst.sa_family = AF_INET; - rt.rt_flags = RTF_UP | RTF_GATEWAY; - rt.rt_dev = (void*) name; - init_sockaddr_in(&rt.rt_genmask, 0); - init_sockaddr_in(&rt.rt_gateway, addr); - - return ioctl(ifc_ctl_sock, SIOCADDRT, &rt); + mask = ~mask << (32 - prefix_length); + mask = htonl(mask); + + return mask; } -int ifc_add_host_route(const char *name, in_addr_t addr) +int ifc_add_ipv4_route(const char *ifname, struct in_addr dst, int prefix_length, + struct in_addr gw) { struct rtentry rt; int result; + in_addr_t netmask; memset(&rt, 0, sizeof(rt)); - + rt.rt_dst.sa_family = AF_INET; - rt.rt_flags = RTF_UP | RTF_HOST; - rt.rt_dev = (void*) name; - init_sockaddr_in(&rt.rt_dst, addr); - init_sockaddr_in(&rt.rt_genmask, 0); - init_sockaddr_in(&rt.rt_gateway, 0); - + rt.rt_dev = (void*) ifname; + + netmask = get_ipv4_netmask(prefix_length); + init_sockaddr_in(&rt.rt_genmask, netmask); + init_sockaddr_in(&rt.rt_dst, dst.s_addr); + rt.rt_flags = RTF_UP; + + if (prefix_length == 32) { + rt.rt_flags |= RTF_HOST; + } + + if (gw.s_addr != 0) { + rt.rt_flags |= RTF_GATEWAY; + init_sockaddr_in(&rt.rt_gateway, gw.s_addr); + } + ifc_init(); + + if (ifc_ctl_sock < 0) { + return -errno; + } + result = ioctl(ifc_ctl_sock, SIOCADDRT, &rt); - if (result < 0 && errno == EEXIST) { - result = 0; + if (result < 0) { + if (errno == EEXIST) { + result = 0; + } else { + result = -errno; + } } ifc_close(); return result; } +int ifc_create_default_route(const char *name, in_addr_t gw) +{ + struct in_addr in_dst, in_gw; + + in_dst.s_addr = 0; + in_gw.s_addr = gw; + + return ifc_add_ipv4_route(name, in_dst, 0, in_gw); +} + +int ifc_add_host_route(const char *name, in_addr_t dst) +{ + struct in_addr in_dst, in_gw; + + in_dst.s_addr = dst; + in_gw.s_addr = 0; + + return ifc_add_ipv4_route(name, in_dst, 32, in_gw); +} + int ifc_enable(const char *ifname) { int result; @@ -449,3 +507,112 @@ ifc_configure(const char *ifname, return 0; } + +int ifc_add_ipv6_route(const char *ifname, struct in6_addr dst, int prefix_length, + struct in6_addr gw) +{ + struct in6_rtmsg rtmsg; + int result; + int ifindex; + + memset(&rtmsg, 0, sizeof(rtmsg)); + + ifindex = if_nametoindex(ifname); + if (ifindex == 0) { + printerr("if_nametoindex() failed: interface %s\n", ifname); + return -ENXIO; + } + + rtmsg.rtmsg_ifindex = ifindex; + rtmsg.rtmsg_dst = dst; + rtmsg.rtmsg_dst_len = prefix_length; + rtmsg.rtmsg_flags = RTF_UP; + + if (prefix_length == 128) { + rtmsg.rtmsg_flags |= RTF_HOST; + } + + if (memcmp(&gw, &in6addr_any, sizeof(in6addr_any))) { + rtmsg.rtmsg_flags |= RTF_GATEWAY; + rtmsg.rtmsg_gateway = gw; + } + + ifc_init6(); + + if (ifc_ctl_sock6 < 0) { + return -errno; + } + + result = ioctl(ifc_ctl_sock6, SIOCADDRT, &rtmsg); + if (result < 0) { + if (errno == EEXIST) { + result = 0; + } else { + result = -errno; + } + } + ifc_close6(); + return result; +} + +int ifc_add_route(const char *ifname, const char *dst, int prefix_length, + const char *gw) +{ + int ret = 0; + struct sockaddr_in ipv4_dst, ipv4_gw; + struct sockaddr_in6 ipv6_dst, ipv6_gw; + struct addrinfo hints, *addr_ai, *gw_ai; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_flags = AI_NUMERICHOST; + + ret = getaddrinfo(dst, NULL, &hints, &addr_ai); + + if (ret != 0) { + printerr("getaddrinfo failed: invalid address %s\n", dst); + return -EINVAL; + } + + if (gw == NULL) { + if (addr_ai->ai_family == AF_INET6) { + gw = "::"; + } else if (addr_ai->ai_family == AF_INET) { + gw = "0.0.0.0"; + } + } + + ret = getaddrinfo(gw, NULL, &hints, &gw_ai); + if (ret != 0) { + printerr("getaddrinfo failed: invalid gateway %s\n", gw); + freeaddrinfo(addr_ai); + return -EINVAL; + } + + if (addr_ai->ai_family != gw_ai->ai_family) { + printerr("ifc_add_route: different address families: %s and %s\n", dst, gw); + freeaddrinfo(addr_ai); + freeaddrinfo(gw_ai); + return -EINVAL; + } + + if (addr_ai->ai_family == AF_INET6) { + memcpy(&ipv6_dst, addr_ai->ai_addr, sizeof(struct sockaddr_in6)); + memcpy(&ipv6_gw, gw_ai->ai_addr, sizeof(struct sockaddr_in6)); + ret = ifc_add_ipv6_route(ifname, ipv6_dst.sin6_addr, prefix_length, + ipv6_gw.sin6_addr); + } else if (addr_ai->ai_family == AF_INET) { + memcpy(&ipv4_dst, addr_ai->ai_addr, sizeof(struct sockaddr_in)); + memcpy(&ipv4_gw, gw_ai->ai_addr, sizeof(struct sockaddr_in)); + ret = ifc_add_ipv4_route(ifname, ipv4_dst.sin_addr, prefix_length, + ipv4_gw.sin_addr); + } else { + printerr("ifc_add_route: getaddrinfo returned un supported address family %d\n", + addr_ai->ai_family); + ret = -EAFNOSUPPORT; + } + + freeaddrinfo(addr_ai); + freeaddrinfo(gw_ai); + return ret; +}