Set accept_ra to 2 on all interfaces

Currently, we set accept_ra to 2 (accept RAs even if forwarding
is on) only on wifi and ethernet, but not on other interfaces
like mobile.  This breaks IPv6 over mobile on Nexus 7 3G and
all other devices where the IPv6 default route is configured via
RA, because as soon as we start 464xlat (which enables
forwarding) their default route goes away.

Rather than require all manufacturers to update their RILs to
set the flag themselves, set it ourselves at netd startup time.

Bug: 8276725
Change-Id: If066bb1aa3ff211da0a0bbe8d58d5a7f35298580
diff --git a/InterfaceController.cpp b/InterfaceController.cpp
index 00ab6d1..0d8f121 100644
--- a/InterfaceController.cpp
+++ b/InterfaceController.cpp
@@ -18,6 +18,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <string.h>
+#include <dirent.h>
 
 #include <dlfcn.h>
 
@@ -43,8 +44,17 @@
 char set_cmd_init_func_name[] = "net_iface_send_command_init";
 char set_cmd_fini_func_name[] = "net_iface_send_command_fini";
 
+const char ipv6_proc_path[] = "/proc/sys/net/ipv6/conf";
+
 InterfaceController::InterfaceController()
 	: sendCommand_(NULL) {
+	// Initial IPv6 settings.
+	// By default, accept_ra is set to 1 (accept RAs unless forwarding is on) on all interfaces.
+	// This causes RAs to work or not work based on whether forwarding is on, and causes routes
+	// learned from RAs to go away when forwarding is turned on. Make this behaviour predictable
+	// by always setting accept_ra to 2.
+	setAcceptRA("2");
+
 	libh_ = dlopen(if_cmd_lib_file_name, RTLD_NOW | RTLD_LOCAL);
 	if (libh_ == NULL) {
 		const char *err_str = dlerror();
@@ -104,20 +114,13 @@
 
 int InterfaceController::writeIPv6ProcPath(const char *interface, const char *setting, const char *value) {
 	char *path;
-	asprintf(&path, "/proc/sys/net/ipv6/conf/%s/%s", interface, setting);
+	asprintf(&path, "%s/%s/%s", ipv6_proc_path, interface, setting);
 	int success = writeFile(path, value, strlen(value));
 	free(path);
 	return success;
 }
 
 int InterfaceController::setEnableIPv6(const char *interface, const int on) {
-	// When IPv6 is on, accept RAs regardless of forwarding state.
-	// When IPv6 is off, accept RAs only if forwarding is off (the default).
-	const char *accept_ra = on ? "2" : "1";
-	if (writeIPv6ProcPath(interface, "accept_ra", accept_ra)) {
-		return -1;
-	}
-
 	// When disable_ipv6 changes from 1 to 0, the kernel starts autoconf.
 	// When disable_ipv6 changes from 0 to 1, the kernel clears all autoconf
 	// addresses and routes and disables IPv6 on the interface.
@@ -130,3 +133,33 @@
 	// 0: enable IPv6 privacy addresses and prefer them over non-privacy ones.
 	return writeIPv6ProcPath(interface, "use_tempaddr", on ? "2" : "0");
 }
+
+int InterfaceController::isInterfaceName(const char *name) {
+	return strcmp(name, ".") &&
+		strcmp(name, "..") &&
+		strcmp(name, "default") &&
+		strcmp(name, "all");
+}
+
+int InterfaceController::setAcceptRA(const char *value) {
+	// Set the default value, which is used by any interfaces that are created in the future.
+	writeIPv6ProcPath("default", "accept_ra", value);
+
+	// Set the value on all the interfaces.
+	DIR *dir = opendir(ipv6_proc_path);
+	if (!dir) {
+		ALOGE("Can't list %s: %s", ipv6_proc_path, strerror(errno));
+		return -errno;
+	}
+	struct dirent *d;
+	while((d = readdir(dir)) != NULL) {
+		if (d->d_type == DT_DIR && isInterfaceName(d->d_name)) {
+			if (writeIPv6ProcPath(d->d_name, "accept_ra", value) < 0) {
+				ALOGE("Can't write to %s/%s/accept_ra: %s", ipv6_proc_path,
+				      d->d_name, strerror(errno));
+			}
+		}
+	}
+	closedir(dir);
+	return 0;
+}
diff --git a/InterfaceController.h b/InterfaceController.h
index b11ab4f..5943a9a 100644
--- a/InterfaceController.h
+++ b/InterfaceController.h
@@ -43,6 +43,8 @@
 	int (*sendCommandFini_)(void);
 	int writeIPv6ProcPath(const char *interface, const char *setting,
 			      const char *value);
+        int isInterfaceName(const char *name);
+        int setAcceptRA(const char *value);
 };
 
 #endif