diff options
| -rw-r--r-- | METADATA | 3 | ||||
| -rw-r--r-- | clatd.c | 89 | ||||
| -rw-r--r-- | clatd.conf | 14 | ||||
| -rw-r--r-- | clatd_test.cpp | 211 | ||||
| -rw-r--r-- | config.c | 225 | ||||
| -rw-r--r-- | config.h | 7 |
6 files changed, 45 insertions, 504 deletions
diff --git a/METADATA b/METADATA new file mode 100644 index 0000000..d97975c --- /dev/null +++ b/METADATA @@ -0,0 +1,3 @@ +third_party { + license_type: NOTICE +} @@ -109,52 +109,15 @@ int configure_packet_socket(int sock) { return 1; } -/* function: ipv4_address_generate - * picks a free IPv4 address from the local subnet or exits if there are no free addresses - * returns: the IPv4 address as an in_addr_t - */ -static in_addr_t ipv4_address_generate() { - // Pick an IPv4 address to use by finding a free address in the configured prefix. Technically, - // there is a race here - if another clatd calls config_select_ipv4_address after we do, but - // before we call add_address, it can end up having the same IP address as we do. But the time - // window in which this can happen is extremely small, and even if we end up with a duplicate - // address, the only damage is that IPv4 TCP connections won't be reset until both interfaces go - // down. - in_addr_t localaddr = config_select_ipv4_address(&Global_Clatd_Config.ipv4_local_subnet, - Global_Clatd_Config.ipv4_local_prefixlen); - if (localaddr == INADDR_NONE) { - logmsg(ANDROID_LOG_FATAL, "No free IPv4 address in %s/%d", - inet_ntoa(Global_Clatd_Config.ipv4_local_subnet), - Global_Clatd_Config.ipv4_local_prefixlen); - exit(1); - } - return localaddr; -} - -/* function: ipv4_address_from_cmdline - * configures the IPv4 address specified on the command line, or exits if the address is not valid - * v4_addr - a string, the IPv4 address - * returns: the IPv4 address as an in_addr_t - */ -static in_addr_t ipv4_address_from_cmdline(const char *v4_addr) { - in_addr_t localaddr; - if (!inet_pton(AF_INET, v4_addr, &localaddr)) { - logmsg(ANDROID_LOG_FATAL, "Invalid IPv4 address %s", v4_addr); - exit(1); - } - return localaddr; -} - /* function: configure_tun_ip * configures the ipv4 and ipv6 addresses on the tunnel interface * tunnel - tun device data * mtu - mtu of tun device */ void configure_tun_ip(const struct tun_data *tunnel, const char *v4_addr, int mtu) { - if (v4_addr) { - Global_Clatd_Config.ipv4_local_subnet.s_addr = ipv4_address_from_cmdline(v4_addr); - } else { - Global_Clatd_Config.ipv4_local_subnet.s_addr = ipv4_address_generate(); + if (!v4_addr || !inet_pton(AF_INET, v4_addr, &Global_Clatd_Config.ipv4_local_subnet.s_addr)) { + logmsg(ANDROID_LOG_FATAL, "Invalid IPv4 address %s", v4_addr); + exit(1); } char addrstr[INET_ADDRSTRLEN]; @@ -272,43 +235,6 @@ int ipv6_address_changed(const char *interface) { } } -/* function: clat_ipv6_address_from_interface - * picks the clat IPv6 address based on the interface address - * interface - uplink interface name - * returns: 1 on success, 0 on failure - */ -static int clat_ipv6_address_from_interface(const char *interface) { - union anyip *interface_ip; - - // TODO: check that the prefix length is /64. - interface_ip = getinterface_ip(interface, AF_INET6); - if (!interface_ip) { - logmsg(ANDROID_LOG_ERROR, "Unable to find an IPv6 address on interface %s", interface); - return 0; - } - - // Generate an interface ID. - config_generate_local_ipv6_subnet(&interface_ip->ip6); - - Global_Clatd_Config.ipv6_local_subnet = interface_ip->ip6; - free(interface_ip); - return 1; -} - -/* function: clat_ipv6_address_from_cmdline - * parses the clat IPv6 address from the command line - * v4_addr - a string, the IPv6 address - * returns: 1 on success, 0 on failure - */ -static int clat_ipv6_address_from_cmdline(const char *v6_addr) { - if (!inet_pton(AF_INET6, v6_addr, &Global_Clatd_Config.ipv6_local_subnet)) { - logmsg(ANDROID_LOG_FATAL, "Invalid source address %s", v6_addr); - return 0; - } - - return 1; -} - /* function: configure_clat_ipv6_address * picks the clat IPv6 address and configures packet translation to use it. * tunnel - tun device data @@ -317,13 +243,10 @@ static int clat_ipv6_address_from_cmdline(const char *v6_addr) { */ int configure_clat_ipv6_address(const struct tun_data *tunnel, const char *interface, const char *v6_addr) { - int ret; - if (v6_addr) { - ret = clat_ipv6_address_from_cmdline(v6_addr); - } else { - ret = clat_ipv6_address_from_interface(interface); + if (!v6_addr || !inet_pton(AF_INET6, v6_addr, &Global_Clatd_Config.ipv6_local_subnet)) { + logmsg(ANDROID_LOG_FATAL, "Invalid source address %s", v6_addr); + return 0; } - if (!ret) return 0; char addrstr[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &Global_Clatd_Config.ipv6_local_subnet, addrstr, sizeof(addrstr)); @@ -1,13 +1 @@ -# Host IID to use as the source of CLAT traffic. -# This is a /128 taken out of the /64 on the parent interface. -# A host IID of :: means to generate a checksum-neutral, random IID. -ipv6_host_id :: - -# IPv4 address configuration to use when selecting a host address. The first -# clat daemon started will use the address specified by ipv4_local_subnet. If -# more than one daemon is run at the same time, subsequent daemons will use -# other addresses in the prefix of length ipv4_local prefixlen that contains -# ipv4_local_subnet. The default is to use the IANA-assigned range 192.0.0.0/29, -# which allows up to 8 clat daemons (.4, .5, .6, .7, .0, .1, .2, .3). -ipv4_local_subnet 192.0.0.4 -ipv4_local_prefixlen 29 +temporary_placeholder 0 diff --git a/clatd_test.cpp b/clatd_test.cpp index 5de7b63..e24b036 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -585,8 +585,6 @@ class ClatdTest : public ::testing::Test { inet_pton(AF_INET, kIPv4LocalAddr, &Global_Clatd_Config.ipv4_local_subnet); inet_pton(AF_INET6, kIPv6PlatSubnet, &Global_Clatd_Config.plat_subnet); memset(&Global_Clatd_Config.ipv6_local_subnet, 0, sizeof(in6_addr)); - Global_Clatd_Config.ipv6_host_id = in6addr_any; - Global_Clatd_Config.use_dynamic_iid = 1; Global_Clatd_Config.default_pdp_interface = const_cast<char *>(sTun.name().c_str()); } @@ -627,183 +625,12 @@ TEST_F(ClatdTest, TestIPv6PrefixEqual) { EXPECT_FALSE(ipv6_prefix_equal(&subnet2, &Global_Clatd_Config.ipv6_local_subnet)); } -int count_onebits(const void *data, size_t size) { - int onebits = 0; - for (size_t pos = 0; pos < size; pos++) { - uint8_t *byte = ((uint8_t *)data) + pos; - for (int shift = 0; shift < 8; shift++) { - onebits += (*byte >> shift) & 1; - } - } - return onebits; -} - -TEST_F(ClatdTest, TestCountOnebits) { - uint64_t i; - i = 1; - ASSERT_EQ(1, count_onebits(&i, sizeof(i))); - i <<= 61; - ASSERT_EQ(1, count_onebits(&i, sizeof(i))); - i |= ((uint64_t)1 << 33); - ASSERT_EQ(2, count_onebits(&i, sizeof(i))); - i = 0xf1000202020000f0; - ASSERT_EQ(5 + 1 + 1 + 1 + 4, count_onebits(&i, sizeof(i))); -} - -TEST_F(ClatdTest, TestGenIIDConfigured) { - struct in6_addr myaddr, expected; - Global_Clatd_Config.use_dynamic_iid = 0; - ASSERT_TRUE(inet_pton(AF_INET6, "::bad:ace:d00d", &Global_Clatd_Config.ipv6_host_id)); - ASSERT_TRUE(inet_pton(AF_INET6, "2001:db8:1:2:0:bad:ace:d00d", &expected)); - ASSERT_TRUE(inet_pton(AF_INET6, "2001:db8:1:2:f076:ae99:124e:aa54", &myaddr)); - config_generate_local_ipv6_subnet(&myaddr); - expect_ipv6_addr_equal(&expected, &myaddr); - - Global_Clatd_Config.use_dynamic_iid = 1; - config_generate_local_ipv6_subnet(&myaddr); - EXPECT_FALSE(IN6_ARE_ADDR_EQUAL(&expected, &myaddr)); -} - -TEST_F(ClatdTest, TestGenIIDRandom) { - struct in6_addr interface_ipv6; - ASSERT_TRUE(inet_pton(AF_INET6, "2001:db8:1:2:f076:ae99:124e:aa54", &interface_ipv6)); - Global_Clatd_Config.ipv6_host_id = in6addr_any; - - // Generate a boatload of random IIDs. - int onebits = 0; - uint64_t prev_iid = 0; - for (int i = 0; i < 100000; i++) { - struct in6_addr myaddr = interface_ipv6; - - config_generate_local_ipv6_subnet(&myaddr); - - // Check the generated IP address is in the same prefix as the interface IPv6 address. - EXPECT_TRUE(ipv6_prefix_equal(&interface_ipv6, &myaddr)); - - // Check that consecutive IIDs are not the same. - uint64_t iid = *(uint64_t *)(&myaddr.s6_addr[8]); - ASSERT_TRUE(iid != prev_iid) - << "Two consecutive random IIDs are the same: " - << std::showbase << std::hex - << iid << "\n"; - prev_iid = iid; - - // Check that the IID is checksum-neutral with the NAT64 prefix and the - // local prefix. - struct in_addr *ipv4addr = &Global_Clatd_Config.ipv4_local_subnet; - struct in6_addr *plat_subnet = &Global_Clatd_Config.plat_subnet; - - uint16_t c1 = ip_checksum_finish(ip_checksum_add(0, ipv4addr, sizeof(*ipv4addr))); - uint16_t c2 = ip_checksum_finish(ip_checksum_add(0, plat_subnet, sizeof(*plat_subnet)) + - ip_checksum_add(0, &myaddr, sizeof(myaddr))); - - if (c1 != c2) { - char myaddr_str[INET6_ADDRSTRLEN], plat_str[INET6_ADDRSTRLEN], ipv4_str[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, &myaddr, myaddr_str, sizeof(myaddr_str)); - inet_ntop(AF_INET6, plat_subnet, plat_str, sizeof(plat_str)); - inet_ntop(AF_INET, ipv4addr, ipv4_str, sizeof(ipv4_str)); - FAIL() - << "Bad IID: " << myaddr_str - << " not checksum-neutral with " << ipv4_str << " and " << plat_str - << std::showbase << std::hex - << "\n IPv4 checksum: " << c1 - << "\n IPv6 checksum: " << c2 - << "\n"; - } - - // Check that IIDs are roughly random and use all the bits by counting the - // total number of bits set to 1 in a random sample of 100000 generated IIDs. - onebits += count_onebits(&iid, sizeof(iid)); - } - EXPECT_LE(3190000, onebits); - EXPECT_GE(3210000, onebits); -} - -extern "C" addr_free_func config_is_ipv4_address_free; -int never_free(in_addr_t /* addr */) { return 0; } -int always_free(in_addr_t /* addr */) { return 1; } -int only2_free(in_addr_t addr) { return (ntohl(addr) & 0xff) == 2; } -int over6_free(in_addr_t addr) { return (ntohl(addr) & 0xff) >= 6; } -int only10_free(in_addr_t addr) { return (ntohl(addr) & 0xff) == 10; } - -TEST_F(ClatdTest, SelectIPv4Address) { - struct in_addr addr; - - inet_pton(AF_INET, kIPv4LocalAddr, &addr); - - addr_free_func orig_config_is_ipv4_address_free = config_is_ipv4_address_free; - - // If no addresses are free, return INADDR_NONE. - config_is_ipv4_address_free = never_free; - EXPECT_EQ(INADDR_NONE, config_select_ipv4_address(&addr, 29)); - EXPECT_EQ(INADDR_NONE, config_select_ipv4_address(&addr, 16)); - - // If the configured address is free, pick that. But a prefix that's too big is invalid. - config_is_ipv4_address_free = always_free; - EXPECT_EQ(inet_addr(kIPv4LocalAddr), config_select_ipv4_address(&addr, 29)); - EXPECT_EQ(inet_addr(kIPv4LocalAddr), config_select_ipv4_address(&addr, 20)); - EXPECT_EQ(INADDR_NONE, config_select_ipv4_address(&addr, 15)); - - // A prefix length of 32 works, but anything above it is invalid. - EXPECT_EQ(inet_addr(kIPv4LocalAddr), config_select_ipv4_address(&addr, 32)); - EXPECT_EQ(INADDR_NONE, config_select_ipv4_address(&addr, 33)); - - // If another address is free, pick it. - config_is_ipv4_address_free = over6_free; - EXPECT_EQ(inet_addr("192.0.0.6"), config_select_ipv4_address(&addr, 29)); - - // Check that we wrap around to addresses that are lower than the first address. - config_is_ipv4_address_free = only2_free; - EXPECT_EQ(inet_addr("192.0.0.2"), config_select_ipv4_address(&addr, 29)); - EXPECT_EQ(INADDR_NONE, config_select_ipv4_address(&addr, 30)); - - // If a free address exists outside the prefix, we don't pick it. - config_is_ipv4_address_free = only10_free; - EXPECT_EQ(INADDR_NONE, config_select_ipv4_address(&addr, 29)); - EXPECT_EQ(inet_addr("192.0.0.10"), config_select_ipv4_address(&addr, 24)); - - // Now try using the real function which sees if IP addresses are free using bind(). - // Assume that the machine running the test has the address 127.0.0.1, but not 8.8.8.8. - config_is_ipv4_address_free = orig_config_is_ipv4_address_free; - addr.s_addr = inet_addr("8.8.8.8"); - EXPECT_EQ(inet_addr("8.8.8.8"), config_select_ipv4_address(&addr, 29)); - - addr.s_addr = inet_addr("127.0.0.1"); - EXPECT_EQ(inet_addr("127.0.0.2"), config_select_ipv4_address(&addr, 29)); -} - TEST_F(ClatdTest, DetectMtu) { // ::1 with bottom 32 bits set to 1 is still ::1 which routes via lo with mtu of 64KiB ASSERT_EQ(detect_mtu(&in6addr_loopback, htonl(1), 0 /*MARK_UNSET*/), 65536); } -TEST_F(ClatdTest, ConfigureTunIp) { - addr_free_func orig_config_is_ipv4_address_free = config_is_ipv4_address_free; - config_is_ipv4_address_free = over6_free; - - Global_Clatd_Config.ipv4_local_prefixlen = 29; - - // Create an interface for configure_tun_ip to configure and bring up. - TunInterface v4Iface; - ASSERT_EQ(0, v4Iface.init()); - struct tun_data tunnel = makeTunData(); - strlcpy(tunnel.device4, v4Iface.name().c_str(), sizeof(tunnel.device4)); - - configure_tun_ip(&tunnel, nullptr /* v4_addr */, 1472); - EXPECT_EQ(inet_addr("192.0.0.6"), Global_Clatd_Config.ipv4_local_subnet.s_addr); - - union anyip *ip = getinterface_ip(v4Iface.name().c_str(), AF_INET); - EXPECT_EQ(inet_addr("192.0.0.6"), ip->ip4.s_addr); - free(ip); - - config_is_ipv4_address_free = orig_config_is_ipv4_address_free; - v4Iface.destroy(); -} - TEST_F(ClatdTest, ConfigureTunIpManual) { - addr_free_func orig_config_is_ipv4_address_free = config_is_ipv4_address_free; - config_is_ipv4_address_free = over6_free; - Global_Clatd_Config.ipv4_local_prefixlen = 29; // Create an interface for configure_tun_ip to configure and bring up. @@ -820,7 +647,6 @@ TEST_F(ClatdTest, ConfigureTunIpManual) { EXPECT_EQ(inet_addr("192.0.2.1"), ip->ip4.s_addr); free(ip); - config_is_ipv4_address_free = orig_config_is_ipv4_address_free; v4Iface.destroy(); } @@ -986,6 +812,29 @@ TEST_F(ClatdTest, Fragmentation) { ARRAYSIZE(kIPv6Fragments), "IPv6->IPv4 fragment translation"); } +// picks a random interface ID that is checksum neutral with the IPv4 address and the NAT64 prefix +void gen_random_iid(struct in6_addr *myaddr, struct in_addr *ipv4_local_subnet, + struct in6_addr *plat_subnet) { + // Fill last 8 bytes of IPv6 address with random bits. + arc4random_buf(&myaddr->s6_addr[8], 8); + + // Make the IID checksum-neutral. That is, make it so that: + // checksum(Local IPv4 | Remote IPv4) = checksum(Local IPv6 | Remote IPv6) + // in other words (because remote IPv6 = NAT64 prefix | Remote IPv4): + // checksum(Local IPv4) = checksum(Local IPv6 | NAT64 prefix) + // Do this by adjusting the two bytes in the middle of the IID. + + uint16_t middlebytes = (myaddr->s6_addr[11] << 8) + myaddr->s6_addr[12]; + + uint32_t c1 = ip_checksum_add(0, ipv4_local_subnet, sizeof(*ipv4_local_subnet)); + uint32_t c2 = ip_checksum_add(0, plat_subnet, sizeof(*plat_subnet)) + + ip_checksum_add(0, myaddr, sizeof(*myaddr)); + + uint16_t delta = ip_checksum_adjust(middlebytes, c1, c2); + myaddr->s6_addr[11] = delta >> 8; + myaddr->s6_addr[12] = delta & 0xff; +} + void check_translate_checksum_neutral(const uint8_t *original, size_t original_len, size_t expected_len, const char *msg) { uint8_t translated[MAXMTU]; @@ -1003,10 +852,12 @@ void check_translate_checksum_neutral(const uint8_t *original, size_t original_l TEST_F(ClatdTest, TranslateChecksumNeutral) { // Generate a random clat IPv6 address and check that translation is checksum-neutral. - Global_Clatd_Config.ipv6_host_id = in6addr_any; ASSERT_TRUE(inet_pton(AF_INET6, "2001:db8:1:2:f076:ae99:124e:aa54", &Global_Clatd_Config.ipv6_local_subnet)); - config_generate_local_ipv6_subnet(&Global_Clatd_Config.ipv6_local_subnet); + + gen_random_iid(&Global_Clatd_Config.ipv6_local_subnet, &Global_Clatd_Config.ipv4_local_subnet, + &Global_Clatd_Config.plat_subnet); + ASSERT_NE(htonl((uint32_t)0x00000464), Global_Clatd_Config.ipv6_local_subnet.s6_addr32[3]); ASSERT_NE((uint32_t)0, Global_Clatd_Config.ipv6_local_subnet.s6_addr32[3]); @@ -1079,7 +930,13 @@ TEST_F(ClatdTest, Ipv6AddressChanged) { .write_fd6 = socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW), }; const char *ifname = sTun.name().c_str(); - ASSERT_EQ(1, configure_clat_ipv6_address(&tunnel, ifname, nullptr)); + + in6_addr myaddr = sTun.srcAddr(); + gen_random_iid(&myaddr, &Global_Clatd_Config.ipv4_local_subnet, &Global_Clatd_Config.plat_subnet); + char addrstr[INET6_ADDRSTRLEN]; + ASSERT_NE(nullptr, inet_ntop(AF_INET6, &myaddr, addrstr, sizeof(addrstr))); + + ASSERT_EQ(1, configure_clat_ipv6_address(&tunnel, ifname, addrstr)); EXPECT_EQ(0, ipv6_address_changed(ifname)); EXPECT_EQ(0, ipv6_address_changed(ifname)); @@ -35,66 +35,6 @@ struct clat_config Global_Clatd_Config; -/* function: config_item_str - * locates the config item and returns the pointer to a string, or NULL on failure. Caller frees - * pointer - * root - parsed configuration - * item_name - name of config item to locate - * defaultvar - value to use if config item isn't present - */ -char *config_item_str(cnode *root, const char *item_name, const char *defaultvar) { - const char *tmp; - - if (!(tmp = config_str(root, item_name, defaultvar))) { - logmsg(ANDROID_LOG_FATAL, "%s config item needed", item_name); - return NULL; - } - return strdup(tmp); -} - -/* function: config_item_int16_t - * locates the config item, parses the integer, and returns the pointer ret_val_ptr, or NULL on - * failure - * root - parsed configuration - * item_name - name of config item to locate - * defaultvar - value to use if config item isn't present - * ret_val_ptr - pointer for return value storage - */ -int16_t *config_item_int16_t(cnode *root, const char *item_name, const char *defaultvar, - int16_t *ret_val_ptr) { - const char *tmp; - char *endptr; - long int conf_int; - - if (!(tmp = config_str(root, item_name, defaultvar))) { - logmsg(ANDROID_LOG_FATAL, "%s config item needed", item_name); - return NULL; - } - - errno = 0; - conf_int = strtol(tmp, &endptr, 10); - if (errno > 0) { - logmsg(ANDROID_LOG_FATAL, "%s config item is not numeric: %s (error=%s)", item_name, tmp, - strerror(errno)); - return NULL; - } - if (endptr == tmp || *tmp == '\0') { - logmsg(ANDROID_LOG_FATAL, "%s config item is not numeric: %s", item_name, tmp); - return NULL; - } - if (*endptr != '\0') { - logmsg(ANDROID_LOG_FATAL, "%s config item contains non-numeric characters: %s", item_name, - endptr); - return NULL; - } - if (conf_int > INT16_MAX || conf_int < INT16_MIN) { - logmsg(ANDROID_LOG_FATAL, "%s config item is too big/small: %d", item_name, conf_int); - return NULL; - } - *ret_val_ptr = conf_int; - return ret_val_ptr; -} - /* function: config_item_ip * locates the config item, parses the ipv4 address, and returns the pointer ret_val_ptr, or NULL on * failure @@ -122,33 +62,6 @@ struct in_addr *config_item_ip(cnode *root, const char *item_name, const char *d return ret_val_ptr; } -/* function: config_item_ip6 - * locates the config item, parses the ipv6 address, and returns the pointer ret_val_ptr, or NULL on - * failure - * root - parsed configuration - * item_name - name of config item to locate - * defaultvar - value to use if config item isn't present - * ret_val_ptr - pointer for return value storage - */ -struct in6_addr *config_item_ip6(cnode *root, const char *item_name, const char *defaultvar, - struct in6_addr *ret_val_ptr) { - const char *tmp; - int status; - - if (!(tmp = config_str(root, item_name, defaultvar))) { - logmsg(ANDROID_LOG_FATAL, "%s config item needed", item_name); - return NULL; - } - - status = inet_pton(AF_INET6, tmp, ret_val_ptr); - if (status <= 0) { - logmsg(ANDROID_LOG_FATAL, "invalid IPv6 address specified for %s: %s", item_name, tmp); - return NULL; - } - - return ret_val_ptr; -} - /* function: ipv6_prefix_equal * compares the prefixes two ipv6 addresses. assumes the prefix lengths are both /64. * a1 - first address @@ -157,107 +70,6 @@ struct in6_addr *config_item_ip6(cnode *root, const char *item_name, const char */ int ipv6_prefix_equal(struct in6_addr *a1, struct in6_addr *a2) { return !memcmp(a1, a2, 8); } -/* function: gen_random_iid - * picks a random interface ID that is checksum neutral with the IPv4 address and the NAT64 prefix - * myaddr - IPv6 address to write to - * ipv4_local_subnet - clat IPv4 address - * plat_subnet - NAT64 prefix - */ -void gen_random_iid(struct in6_addr *myaddr, struct in_addr *ipv4_local_subnet, - struct in6_addr *plat_subnet) { - // Fill last 8 bytes of IPv6 address with random bits. - arc4random_buf(&myaddr->s6_addr[8], 8); - - // Make the IID checksum-neutral. That is, make it so that: - // checksum(Local IPv4 | Remote IPv4) = checksum(Local IPv6 | Remote IPv6) - // in other words (because remote IPv6 = NAT64 prefix | Remote IPv4): - // checksum(Local IPv4) = checksum(Local IPv6 | NAT64 prefix) - // Do this by adjusting the two bytes in the middle of the IID. - - uint16_t middlebytes = (myaddr->s6_addr[11] << 8) + myaddr->s6_addr[12]; - - uint32_t c1 = ip_checksum_add(0, ipv4_local_subnet, sizeof(*ipv4_local_subnet)); - uint32_t c2 = ip_checksum_add(0, plat_subnet, sizeof(*plat_subnet)) + - ip_checksum_add(0, myaddr, sizeof(*myaddr)); - - uint16_t delta = ip_checksum_adjust(middlebytes, c1, c2); - myaddr->s6_addr[11] = delta >> 8; - myaddr->s6_addr[12] = delta & 0xff; -} - -// Factored out to a separate function for testability. -int connect_is_ipv4_address_free(in_addr_t addr) { - int s = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); - if (s == -1) { - return 0; - } - - // Attempt to connect to the address. If the connection succeeds and getsockname returns the same - // the address then the address is already assigned to the system and we can't use it. - struct sockaddr_in sin = { .sin_family = AF_INET, .sin_addr = { addr }, .sin_port = htons(53) }; - socklen_t len = sizeof(sin); - int inuse = connect(s, (struct sockaddr *)&sin, sizeof(sin)) == 0 && - getsockname(s, (struct sockaddr *)&sin, &len) == 0 && (size_t)len >= sizeof(sin) && - sin.sin_addr.s_addr == addr; - - close(s); - return !inuse; -} - -addr_free_func config_is_ipv4_address_free = connect_is_ipv4_address_free; - -/* function: config_select_ipv4_address - * picks a free IPv4 address, starting from ip and trying all addresses in the prefix in order - * ip - the IP address from the configuration file - * prefixlen - the length of the prefix from which addresses may be selected. - * returns: the IPv4 address, or INADDR_NONE if no addresses were available - */ -in_addr_t config_select_ipv4_address(const struct in_addr *ip, int16_t prefixlen) { - in_addr_t chosen = INADDR_NONE; - - // Don't accept prefixes that are too large because we scan addresses one by one. - if (prefixlen < 16 || prefixlen > 32) { - return chosen; - } - - // All these are in host byte order. - in_addr_t mask = 0xffffffff >> (32 - prefixlen) << (32 - prefixlen); - in_addr_t ipv4 = ntohl(ip->s_addr); - in_addr_t first_ipv4 = ipv4; - in_addr_t prefix = ipv4 & mask; - - // Pick the first IPv4 address in the pool, wrapping around if necessary. - // So, for example, 192.0.0.4 -> 192.0.0.5 -> 192.0.0.6 -> 192.0.0.7 -> 192.0.0.0. - do { - if (config_is_ipv4_address_free(htonl(ipv4))) { - chosen = htonl(ipv4); - break; - } - ipv4 = prefix | ((ipv4 + 1) & ~mask); - } while (ipv4 != first_ipv4); - - return chosen; -} - -/* function: config_generate_local_ipv6_subnet - * generates the local ipv6 subnet when given the interface ip requires config.ipv6_host_id - * interface_ip - in: interface ip, out: local ipv6 host address - */ -void config_generate_local_ipv6_subnet(struct in6_addr *interface_ip) { - int i; - - if (Global_Clatd_Config.use_dynamic_iid) { - /* Generate a random interface ID. */ - gen_random_iid(interface_ip, &Global_Clatd_Config.ipv4_local_subnet, - &Global_Clatd_Config.plat_subnet); - } else { - /* Use the specified interface ID. */ - for (i = 2; i < 4; i++) { - interface_ip->s6_addr32[i] = Global_Clatd_Config.ipv6_host_id.s6_addr32[i]; - } - } -} - /* function: read_config * reads the config file and parses it into the global variable Global_Clatd_Config. returns 0 on * failure, 1 on success @@ -266,7 +78,6 @@ void config_generate_local_ipv6_subnet(struct in6_addr *interface_ip) { */ int read_config(const char *file, const char *uplink_interface) { cnode *root = config_node("", ""); - unsigned flags; if (!root) { logmsg(ANDROID_LOG_FATAL, "out of memory"); @@ -288,22 +99,7 @@ int read_config(const char *file, const char *uplink_interface) { &Global_Clatd_Config.ipv4_local_subnet)) goto failed; - if (!config_item_int16_t(root, "ipv4_local_prefixlen", DEFAULT_IPV4_LOCAL_PREFIXLEN, - &Global_Clatd_Config.ipv4_local_prefixlen)) - goto failed; - - if (!config_item_ip6(root, "ipv6_host_id", "::", &Global_Clatd_Config.ipv6_host_id)) goto failed; - - /* In order to prevent multiple devices attempting to use the same clat address, never use a - statically-configured interface ID on a broadcast interface such as wifi. */ - if (!IN6_IS_ADDR_UNSPECIFIED(&Global_Clatd_Config.ipv6_host_id)) { - ifc_init(); - ifc_get_info(Global_Clatd_Config.default_pdp_interface, NULL, NULL, &flags); - ifc_close(); - Global_Clatd_Config.use_dynamic_iid = (flags & IFF_BROADCAST) != 0; - } else { - Global_Clatd_Config.use_dynamic_iid = 1; - } + Global_Clatd_Config.ipv4_local_prefixlen = 29; return 1; @@ -311,22 +107,3 @@ failed: free(root); return 0; } - -/* function; dump_config - * prints the current config - */ -void dump_config() { - char charbuffer[INET6_ADDRSTRLEN]; - - logmsg( - ANDROID_LOG_DEBUG, "ipv6_local_subnet = %s", - inet_ntop(AF_INET6, &Global_Clatd_Config.ipv6_local_subnet, charbuffer, sizeof(charbuffer))); - logmsg( - ANDROID_LOG_DEBUG, "ipv4_local_subnet = %s", - inet_ntop(AF_INET, &Global_Clatd_Config.ipv4_local_subnet, charbuffer, sizeof(charbuffer))); - logmsg(ANDROID_LOG_DEBUG, "ipv4_local_prefixlen = %d", Global_Clatd_Config.ipv4_local_prefixlen); - logmsg(ANDROID_LOG_DEBUG, "plat_subnet = %s", - inet_ntop(AF_INET6, &Global_Clatd_Config.plat_subnet, charbuffer, sizeof(charbuffer))); - logmsg(ANDROID_LOG_DEBUG, "default_pdp_interface = %s", - Global_Clatd_Config.default_pdp_interface); -} @@ -22,25 +22,18 @@ #include <netinet/in.h> #define DEFAULT_IPV4_LOCAL_SUBNET "192.0.0.4" -#define DEFAULT_IPV4_LOCAL_PREFIXLEN "29" struct clat_config { struct in6_addr ipv6_local_subnet; - struct in6_addr ipv6_host_id; struct in_addr ipv4_local_subnet; int16_t ipv4_local_prefixlen; struct in6_addr plat_subnet; char *default_pdp_interface; - int use_dynamic_iid; }; extern struct clat_config Global_Clatd_Config; int read_config(const char *file, const char *uplink_interface); -void config_generate_local_ipv6_subnet(struct in6_addr *interface_ip); -in_addr_t config_select_ipv4_address(const struct in_addr *ip, int16_t prefixlen); int ipv6_prefix_equal(struct in6_addr *a1, struct in6_addr *a2); -typedef int (*addr_free_func)(in_addr_t addr); - #endif /* __CONFIG_H__ */ |
