diff options
Diffstat (limited to 'libnetutils/ifc_utils.c')
-rw-r--r-- | libnetutils/ifc_utils.c | 251 |
1 files changed, 136 insertions, 115 deletions
diff --git a/libnetutils/ifc_utils.c b/libnetutils/ifc_utils.c index 0ca5fe6..c9d6ed2 100644 --- a/libnetutils/ifc_utils.c +++ b/libnetutils/ifc_utils.c @@ -25,6 +25,7 @@ #include <sys/types.h> #include <netinet/in.h> #include <arpa/inet.h> +#include <net/if.h> #include <linux/if.h> #include <linux/if_ether.h> @@ -50,6 +51,33 @@ static int ifc_ctl_sock = -1; static int ifc_ctl_sock6 = -1; void printerr(char *fmt, ...); +in_addr_t prefixLengthToIpv4Netmask(int prefix_length) +{ + in_addr_t mask = 0; + + // C99 (6.5.7): shifts of 32 bits have undefined results + if (prefix_length <= 0 || prefix_length > 32) { + return 0; + } + + mask = ~mask << (32 - prefix_length); + mask = htonl(mask); + + return mask; +} + +int ipv4NetmaskToPrefixLength(in_addr_t mask) +{ + mask = ntohl(mask); + int prefixLength = 0; + uint32_t m = (uint32_t)mask; + while (m & 0x80000000) { + prefixLength++; + m = m << 1; + } + return prefixLength; +} + static const char *ipaddr_to_string(in_addr_t addr) { struct in_addr in_addr; @@ -126,7 +154,7 @@ int ifc_get_ifindex(const char *name, int *if_indexp) if(r < 0) return -1; *if_indexp = ifr.ifr_ifindex; - return 0; + return 0; } static int ifc_set_flags(const char *name, unsigned set, unsigned clr) @@ -163,7 +191,7 @@ int ifc_set_addr(const char *name, in_addr_t addr) ifc_init_ifr(name, &ifr); init_sockaddr_in(&ifr.ifr_addr, addr); - + return ioctl(ifc_ctl_sock, SIOCSIFADDR, &ifr); } @@ -178,17 +206,37 @@ int ifc_set_hwaddr(const char *name, const void *ptr) return ioctl(ifc_ctl_sock, SIOCSIFHWADDR, &ifr); } -int ifc_set_mask(const char *name, in_addr_t mask) +int ifc_set_prefixLength(const char *name, int prefixLength) { struct ifreq ifr; + // TODO - support ipv6 + if (prefixLength > 32 || prefixLength < 0) return -1; + in_addr_t mask = prefixLengthToIpv4Netmask(prefixLength); ifc_init_ifr(name, &ifr); init_sockaddr_in(&ifr.ifr_addr, mask); - + return ioctl(ifc_ctl_sock, SIOCSIFNETMASK, &ifr); } -int ifc_get_info(const char *name, in_addr_t *addr, in_addr_t *mask, unsigned *flags) +int ifc_get_addr(const char *name, in_addr_t *addr) +{ + struct ifreq ifr; + int ret = 0; + + ifc_init_ifr(name, &ifr); + if (addr != NULL) { + ret = ioctl(ifc_ctl_sock, SIOCGIFADDR, &ifr); + if (ret < 0) { + *addr = 0; + } else { + *addr = ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr; + } + } + return ret; +} + +int ifc_get_info(const char *name, in_addr_t *addr, int *prefixLength, unsigned *flags) { struct ifreq ifr; ifc_init_ifr(name, &ifr); @@ -200,12 +248,13 @@ int ifc_get_info(const char *name, in_addr_t *addr, in_addr_t *mask, unsigned *f *addr = ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr; } } - - if (mask != NULL) { + + if (prefixLength != NULL) { if(ioctl(ifc_ctl_sock, SIOCGIFNETMASK, &ifr) < 0) { - *mask = 0; + *prefixLength = 0; } else { - *mask = ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr; + *prefixLength = ipv4NetmaskToPrefixLength((int) + ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr); } } @@ -220,17 +269,7 @@ int ifc_get_info(const char *name, in_addr_t *addr, in_addr_t *mask, unsigned *f return 0; } -in_addr_t get_ipv4_netmask(int prefix_length) -{ - in_addr_t mask = 0; - - mask = ~mask << (32 - prefix_length); - mask = htonl(mask); - - return mask; -} - -int ifc_add_ipv4_route(const char *ifname, struct in_addr dst, int prefix_length, +int ifc_act_on_ipv4_route(int action, const char *ifname, struct in_addr dst, int prefix_length, struct in_addr gw) { struct rtentry rt; @@ -242,7 +281,7 @@ int ifc_add_ipv4_route(const char *ifname, struct in_addr dst, int prefix_length rt.rt_dst.sa_family = AF_INET; rt.rt_dev = (void*) ifname; - netmask = get_ipv4_netmask(prefix_length); + netmask = prefixLengthToIpv4Netmask(prefix_length); init_sockaddr_in(&rt.rt_genmask, netmask); init_sockaddr_in(&rt.rt_dst, dst.s_addr); rt.rt_flags = RTF_UP; @@ -262,7 +301,7 @@ int ifc_add_ipv4_route(const char *ifname, struct in_addr dst, int prefix_length return -errno; } - result = ioctl(ifc_ctl_sock, SIOCADDRT, &rt); + result = ioctl(ifc_ctl_sock, action, &rt); if (result < 0) { if (errno == EEXIST) { result = 0; @@ -281,17 +320,7 @@ int ifc_create_default_route(const char *name, in_addr_t 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); + return ifc_act_on_route(SIOCADDRT, name, in_dst, 0, in_gw); } int ifc_enable(const char *ifname) @@ -306,29 +335,65 @@ int ifc_enable(const char *ifname) int ifc_disable(const char *ifname) { + unsigned addr, count; int result; ifc_init(); result = ifc_down(ifname); + ifc_set_addr(ifname, 0); + for (count=0, addr=1;((addr != 0) && (count < 255)); count++) { + if (ifc_get_addr(ifname, &addr) < 0) + break; + if (addr) + ifc_set_addr(ifname, 0); + } + ifc_close(); return result; } -int ifc_reset_connections(const char *ifname) +#define RESET_IPV4_ADDRESSES 0x01 +#define RESET_IPV6_ADDRESSES 0x02 +#define RESET_ALL_ADDRESSES (RESET_IPV4_ADDRESSES | RESET_IPV6_ADDRESSES) + +int ifc_reset_connections(const char *ifname, const int reset_mask) { #ifdef HAVE_ANDROID_OS - int result; + int result, success; in_addr_t myaddr; struct ifreq ifr; + struct in6_ifreq ifr6; + + if (reset_mask & RESET_IPV4_ADDRESSES) { + /* IPv4. Clear connections on the IP address. */ + ifc_init(); + ifc_get_info(ifname, &myaddr, NULL, NULL); + ifc_init_ifr(ifname, &ifr); + init_sockaddr_in(&ifr.ifr_addr, myaddr); + result = ioctl(ifc_ctl_sock, SIOCKILLADDR, &ifr); + ifc_close(); + } else { + result = 0; + } + + if (reset_mask & RESET_IPV6_ADDRESSES) { + /* + * IPv6. On Linux, when an interface goes down it loses all its IPv6 + * addresses, so we don't know which connections belonged to that interface + * So we clear all unused IPv6 connections on the device by specifying an + * empty IPv6 address. + */ + ifc_init6(); + // This implicitly specifies an address of ::, i.e., kill all IPv6 sockets. + memset(&ifr6, 0, sizeof(ifr6)); + success = ioctl(ifc_ctl_sock6, SIOCKILLADDR, &ifr6); + if (result == 0) { + result = success; + } + ifc_close6(); + } - ifc_init(); - ifc_get_info(ifname, &myaddr, NULL, NULL); - ifc_init_ifr(ifname, &ifr); - init_sockaddr_in(&ifr.ifr_addr, myaddr); - result = ioctl(ifc_ctl_sock, SIOCKILLADDR, &ifr); - ifc_close(); - return result; #else return 0; @@ -384,67 +449,6 @@ int ifc_remove_host_routes(const char *name) } /* - * Return the address of the default gateway - * - * TODO: factor out common code from this and remove_host_routes() - * so that we only scan /proc/net/route in one place. - */ -int ifc_get_default_route(const char *ifname) -{ - char name[64]; - in_addr_t dest, gway, mask; - int flags, refcnt, use, metric, mtu, win, irtt; - int result; - FILE *fp; - - fp = fopen("/proc/net/route", "r"); - if (fp == NULL) - return 0; - /* Skip the header line */ - if (fscanf(fp, "%*[^\n]\n") < 0) { - fclose(fp); - return 0; - } - ifc_init(); - result = 0; - for (;;) { - int nread = fscanf(fp, "%63s%X%X%X%d%d%d%X%d%d%d\n", - name, &dest, &gway, &flags, &refcnt, &use, &metric, &mask, - &mtu, &win, &irtt); - if (nread != 11) { - break; - } - if ((flags & (RTF_UP|RTF_GATEWAY)) == (RTF_UP|RTF_GATEWAY) - && dest == 0 - && strcmp(ifname, name) == 0) { - result = gway; - break; - } - } - fclose(fp); - ifc_close(); - return result; -} - -/* - * Sets the specified gateway as the default route for the named interface. - */ -int ifc_set_default_route(const char *ifname, in_addr_t gateway) -{ - struct in_addr addr; - int result; - - ifc_init(); - addr.s_addr = gateway; - if ((result = ifc_create_default_route(ifname, gateway)) < 0) { - LOGD("failed to add %s as default route for %s: %s", - inet_ntoa(addr), ifname, strerror(errno)); - } - ifc_close(); - return result; -} - -/* * Removes the default route for the named interface. */ int ifc_remove_default_route(const char *ifname) @@ -467,7 +471,7 @@ int ifc_remove_default_route(const char *ifname) int ifc_configure(const char *ifname, in_addr_t address, - in_addr_t netmask, + uint32_t prefixLength, in_addr_t gateway, in_addr_t dns1, in_addr_t dns2) { @@ -486,8 +490,8 @@ ifc_configure(const char *ifname, ifc_close(); return -1; } - if (ifc_set_mask(ifname, netmask)) { - printerr("failed to set netmask %s: %s\n", ipaddr_to_string(netmask), strerror(errno)); + if (ifc_set_prefixLength(ifname, prefixLength)) { + printerr("failed to set prefixLength %d: %s\n", prefixLength, strerror(errno)); ifc_close(); return -1; } @@ -507,7 +511,7 @@ ifc_configure(const char *ifname, return 0; } -int ifc_add_ipv6_route(const char *ifname, struct in6_addr dst, int prefix_length, +int ifc_act_on_ipv6_route(int action, const char *ifname, struct in6_addr dst, int prefix_length, struct in6_addr gw) { struct in6_rtmsg rtmsg; @@ -542,7 +546,7 @@ int ifc_add_ipv6_route(const char *ifname, struct in6_addr dst, int prefix_lengt return -errno; } - result = ioctl(ifc_ctl_sock6, SIOCADDRT, &rtmsg); + result = ioctl(ifc_ctl_sock6, action, &rtmsg); if (result < 0) { if (errno == EEXIST) { result = 0; @@ -554,8 +558,8 @@ int ifc_add_ipv6_route(const char *ifname, struct in6_addr dst, int prefix_lengt return result; } -int ifc_add_route(const char *ifname, const char *dst, int prefix_length, - const char *gw) +int ifc_act_on_route(int action, const char *ifname, const char *dst, int prefix_length, + const char *gw) { int ret = 0; struct sockaddr_in ipv4_dst, ipv4_gw; @@ -573,7 +577,7 @@ int ifc_add_route(const char *ifname, const char *dst, int prefix_length, return -EINVAL; } - if (gw == NULL) { + if (gw == NULL || (strlen(gw) == 0)) { if (addr_ai->ai_family == AF_INET6) { gw = "::"; } else if (addr_ai->ai_family == AF_INET) { @@ -581,6 +585,13 @@ int ifc_add_route(const char *ifname, const char *dst, int prefix_length, } } + if (((addr_ai->ai_family == AF_INET6) && (prefix_length < 0 || prefix_length > 128)) || + ((addr_ai->ai_family == AF_INET) && (prefix_length < 0 || prefix_length > 32))) { + printerr("ifc_add_route: invalid prefix length"); + freeaddrinfo(addr_ai); + return -EINVAL; + } + ret = getaddrinfo(gw, NULL, &hints, &gw_ai); if (ret != 0) { printerr("getaddrinfo failed: invalid gateway %s\n", gw); @@ -598,13 +609,13 @@ int ifc_add_route(const char *ifname, const char *dst, int prefix_length, 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); + ret = ifc_act_on_ipv6_route(action, 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); + ret = ifc_act_on_ipv4_route(action, 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); @@ -615,3 +626,13 @@ int ifc_add_route(const char *ifname, const char *dst, int prefix_length, freeaddrinfo(gw_ai); return ret; } + +int ifc_add_route(const char *ifname, const char *dst, int prefix_length, const char *gw) +{ + return ifc_act_on_route(SIOCADDRT, ifname, dst, prefix_length, gw); +} + +int ifc_remove_route(const char *ifname, const char*dst, int prefix_length, const char *gw) +{ + return ifc_act_on_route(SIOCDELRT, ifname, dst, prefix_length, gw); +} |