Derive mtu from ipv6 route to ipv4 /96 prefix instead of device
Test: builds, atest, still needs testing on real network
x86 clatd_test
--------------
clatd_test (19 Tests)
...
[6/19] ClatdTest#DetectMtu: PASSED (0ms)
...
Bug: 147935930
Signed-off-by: Maciej Żenczykowski <maze@google.com>
Change-Id: I3d11ba082dabf70089867146acd84f6436953663
diff --git a/clatd.c b/clatd.c
index 8a0d55f..82df63d 100644
--- a/clatd.c
+++ b/clatd.c
@@ -46,7 +46,6 @@
#include "dump.h"
#include "getaddr.h"
#include "logging.h"
-#include "mtu.h"
#include "resolv_netid.h"
#include "ring.h"
#include "setif.h"
@@ -149,8 +148,9 @@
/* 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) {
+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 {
@@ -170,7 +170,7 @@
exit(1);
}
- status = if_up(tunnel->device4, Global_Clatd_Config.ipv4mtu);
+ status = if_up(tunnel->device4, mtu);
if (status < 0) {
logmsg(ANDROID_LOG_FATAL, "configure_tun_ip/if_up(4) failed: %s", strerror(-status));
exit(1);
@@ -346,41 +346,74 @@
return 1;
}
+int detect_mtu(const struct in6_addr *plat_subnet, uint32_t plat_suffix, uint32_t mark) {
+ // Create an IPv6 UDP socket.
+ int s = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ if (s < 0) {
+ logmsg(ANDROID_LOG_FATAL, "socket(AF_INET6, SOCK_DGRAM, 0) failed");
+ exit(1);
+ }
+
+ // Socket's mark affects routing decisions (network selection)
+ if ((mark != MARK_UNSET) && setsockopt(s, SOL_SOCKET, SO_MARK, &mark, sizeof(mark))) {
+ logmsg(ANDROID_LOG_FATAL, "setsockopt(SOL_SOCKET, SO_MARK) failed: %s", strerror(errno));
+ exit(1);
+ }
+
+ // Try to connect udp socket to plat_subnet(96 bits):plat_suffix(32 bits)
+ struct sockaddr_in6 dst = {
+ .sin6_family = AF_INET6,
+ .sin6_addr = *plat_subnet,
+ };
+ dst.sin6_addr.s6_addr32[3] = plat_suffix;
+ if (connect(s, (struct sockaddr *)&dst, sizeof(dst))) {
+ logmsg(ANDROID_LOG_FATAL, "connect() failed: %s", strerror(errno));
+ exit(1);
+ }
+
+ // Fetch the socket's IPv6 mtu - this is effectively fetching mtu from routing table
+ int mtu;
+ socklen_t sz_mtu = sizeof(mtu);
+ if (getsockopt(s, SOL_IPV6, IPV6_MTU, &mtu, &sz_mtu)) {
+ logmsg(ANDROID_LOG_FATAL, "getsockopt(SOL_IPV6, IPV6_MTU) failed: %s", strerror(errno));
+ exit(1);
+ }
+ if (sz_mtu != sizeof(mtu)) {
+ logmsg(ANDROID_LOG_FATAL, "getsockopt(SOL_IPV6, IPV6_MTU) returned unexpected size: %d",
+ sz_mtu);
+ exit(1);
+ }
+ close(s);
+
+ return mtu;
+}
+
/* function: configure_interface
* reads the configuration and applies it to the interface
* uplink_interface - network interface to use to reach the ipv6 internet
* plat_prefix - PLAT prefix to use
* tunnel - tun device data
* net_id - NetID to use, NETID_UNSET indicates use of default network
+ * mark - the socket mark to use for the sending raw socket
*/
void configure_interface(const char *uplink_interface, const char *plat_prefix, const char *v4_addr,
- const char *v6_addr, struct tun_data *tunnel, unsigned net_id) {
-
+ const char *v6_addr, struct tun_data *tunnel, unsigned net_id,
+ uint32_t mark) {
if (!read_config("/system/etc/clatd.conf", uplink_interface, plat_prefix, net_id)) {
logmsg(ANDROID_LOG_FATAL, "read_config failed");
exit(1);
}
- if (Global_Clatd_Config.mtu > MAXMTU) {
- logmsg(ANDROID_LOG_WARN, "Max MTU is %d, requested %d", MAXMTU, Global_Clatd_Config.mtu);
- Global_Clatd_Config.mtu = MAXMTU;
- }
- if (Global_Clatd_Config.mtu <= 0) {
- Global_Clatd_Config.mtu = getifmtu(Global_Clatd_Config.default_pdp_interface);
- logmsg(ANDROID_LOG_WARN, "ifmtu=%d", Global_Clatd_Config.mtu);
- }
- if (Global_Clatd_Config.mtu < 1280) {
- logmsg(ANDROID_LOG_WARN, "mtu too small = %d", Global_Clatd_Config.mtu);
- Global_Clatd_Config.mtu = 1280;
- }
+ int mtu = detect_mtu(&Global_Clatd_Config.plat_subnet, htonl(0x08080808), mark);
+ // clamp to minimum ipv6 mtu - this probably cannot ever trigger
+ if (mtu < 1280) mtu = 1280;
+ // clamp to buffer size
+ if (mtu > MAXMTU) mtu = MAXMTU;
+ // decrease by ipv6(40) + ipv6 fragmentation header(8) vs ipv4(20) overhead of 28 bytes
+ mtu -= MTU_DELTA;
+ logmsg(ANDROID_LOG_WARN, "ipv4 mtu is %d", mtu);
- if (Global_Clatd_Config.ipv4mtu <= 0 ||
- Global_Clatd_Config.ipv4mtu > Global_Clatd_Config.mtu - MTU_DELTA) {
- Global_Clatd_Config.ipv4mtu = Global_Clatd_Config.mtu - MTU_DELTA;
- logmsg(ANDROID_LOG_WARN, "ipv4mtu now set to = %d", Global_Clatd_Config.ipv4mtu);
- }
-
- configure_tun_ip(tunnel, v4_addr);
+ configure_tun_ip(tunnel, v4_addr, mtu);
if (!configure_clat_ipv6_address(tunnel, uplink_interface, v6_addr)) {
exit(1);