summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjoern Johansson <bjoernj@google.com>2018-08-23 16:43:53 -0700
committerbohu <bohu@google.com>2018-08-24 20:18:46 -0700
commit6ef3b9b3299bef6f3cc57c5d0933f846ee98f277 (patch)
tree547d6988bd09d216817b645cb3a6258679326156
parentd9689ef63954619b589c32752a790e5336ade746 (diff)
downloadandroid_device_generic_goldfish-6ef3b9b3299bef6f3cc57c5d0933f846ee98f277.tar.gz
android_device_generic_goldfish-6ef3b9b3299bef6f3cc57c5d0933f846ee98f277.tar.bz2
android_device_generic_goldfish-6ef3b9b3299bef6f3cc57c5d0933f846ee98f277.zip
Improve IPv6 networking for radio
RIL only announced addresses supplied by the emulator host. For IPv6 the guest uses Neighbor Discovery Protocol to determine which IPv6 addresses to assign to an interface and what gateway to use. This was not reflected in RIL though which meant that the Android framework was unaware of the radio using these addresses. There are now CTS tests that rely on the framework knowing which addresses are assigned to verify that connections to external servers are made from the correct interface. These tests would fail on the radio interface. In addition to providing interface addresses RIL also provides DNS servers. It would only provide whatever DNS servers the DHCP client could find. On networks that are configured to use IPv6 DNS servers this left the radio interface without any usable DNS servers. This change adds an interface monitor to the RIL that is looking for address changes on the radio network interface. It then sends updates to the framework whenever the addresses change. And to solve the specific IPv6 problem it looks at IPv6 addreses. RIL still expects the IPv4 address to either be hardcoded for the Wifi case or supplied by the emulator host if the Wifi feature is not enabled. To fix the IPv6 DNS server issue the ipv6proxy will now inspect each incoming Router Advertisement packet and extract any IPv6 DNS servers provided in those packets. The proxy will set a system property for each extracted DNS server. The RIL will then take the value from these system properties and use in its messages to the framework, indicating which DNS servers to use. Test: run cts -m CtsNetTestCases -t android.net.cts.ConnectivityManagerTest#testOpenConnection BUG: 111691186 Change-Id: Ia970d2dda18c2615cacca496701c67f2066530a7
-rw-r--r--ril/Android.mk1
-rw-r--r--ril/atchannel.c1
-rw-r--r--ril/if_monitor.cpp352
-rw-r--r--ril/if_monitor.h49
-rw-r--r--ril/reference-ril.c127
-rw-r--r--sepolicy/common/ipv6proxy.te1
-rw-r--r--wifi/ipv6proxy/log.h2
-rw-r--r--wifi/ipv6proxy/proxy.cpp44
8 files changed, 568 insertions, 9 deletions
diff --git a/ril/Android.mk b/ril/Android.mk
index 34bd227..cb5f31a 100644
--- a/ril/Android.mk
+++ b/ril/Android.mk
@@ -8,6 +8,7 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
reference-ril.c \
atchannel.c \
+ if_monitor.cpp \
misc.c \
at_tok.c
diff --git a/ril/atchannel.c b/ril/atchannel.c
index 0041836..407a204 100644
--- a/ril/atchannel.c
+++ b/ril/atchannel.c
@@ -22,6 +22,7 @@
#include <string.h>
#include <pthread.h>
#include <ctype.h>
+#include <poll.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
diff --git a/ril/if_monitor.cpp b/ril/if_monitor.cpp
new file mode 100644
index 0000000..289477d
--- /dev/null
+++ b/ril/if_monitor.cpp
@@ -0,0 +1,352 @@
+/*
+ * Copyright 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "if_monitor.h"
+
+#include <errno.h>
+#include <linux/rtnetlink.h>
+#include <net/if.h>
+#include <poll.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <mutex>
+#include <thread>
+#include <unordered_map>
+#include <vector>
+
+#define LOG_TAG "RIL-IFMON"
+#include <utils/Log.h>
+
+static const size_t kReadBufferSize = 32768;
+
+static const size_t kControlServer = 0;
+static const size_t kControlClient = 1;
+
+// A list of commands that can be sent to the monitor. These should be one
+// character long as that is all that the monitor will read and process.
+static const char kMonitorStopCommand[] = "\1";
+static const char kMonitorAckCommand[] = "\2";
+
+static size_t addrLength(int addrFamily) {
+ switch (addrFamily) {
+ case AF_INET:
+ return 4;
+ case AF_INET6:
+ return 16;
+ default:
+ return 0;
+ }
+}
+
+bool operator==(const struct ifAddress& left, const struct ifAddress& right) {
+ // The prefix length does not factor in to whether two addresses are the
+ // same or not. Only the family and the address data. This matches the
+ // kernel behavior when attempting to add the same address with different
+ // prefix lengths, those changes are rejected because the address already
+ // exists.
+ return left.family == right.family &&
+ memcmp(&left.addr, &right.addr, addrLength(left.family)) == 0;
+}
+
+class InterfaceMonitor {
+public:
+ InterfaceMonitor() : mSocketFd(-1) {
+ mControlSocket[kControlServer] = -1;
+ mControlSocket[kControlClient] = -1;
+ }
+
+ ~InterfaceMonitor() {
+ if (mControlSocket[kControlClient] != -1) {
+ ::close(mControlSocket[kControlClient]);
+ mControlSocket[kControlClient] = -1;
+ }
+ if (mControlSocket[kControlServer] != -1) {
+ ::close(mControlSocket[kControlServer]);
+ mControlSocket[kControlServer] = -1;
+ }
+
+ if (mSocketFd != -1) {
+ ::close(mSocketFd);
+ mSocketFd = -1;
+ }
+ }
+
+ bool init() {
+ if (mSocketFd != -1) {
+ RLOGE("InterfaceMonitor already initialized");
+ return false;
+ }
+
+ mSocketFd = ::socket(AF_NETLINK,
+ SOCK_DGRAM | SOCK_CLOEXEC,
+ NETLINK_ROUTE);
+ if (mSocketFd == -1) {
+ RLOGE("InterfaceMonitor failed to open socket: %s", strerror(errno));
+ return false;
+ }
+
+ if (::socketpair(AF_UNIX, SOCK_DGRAM, 0, mControlSocket) != 0) {
+ RLOGE("Unable to create control socket pair: %s", strerror(errno));
+ return false;
+ }
+
+ struct sockaddr_nl addr;
+ memset(&addr, 0, sizeof(addr));
+ addr.nl_family = AF_NETLINK;
+ addr.nl_groups = (1 << (RTNLGRP_IPV4_IFADDR - 1)) |
+ (1 << (RTNLGRP_IPV6_IFADDR - 1));
+
+ struct sockaddr* sa = reinterpret_cast<struct sockaddr*>(&addr);
+ if (::bind(mSocketFd, sa, sizeof(addr)) != 0) {
+ RLOGE("InterfaceMonitor failed to bind socket: %s",
+ strerror(errno));
+ return false;
+ }
+
+ return true;
+ }
+
+ void setCallback(ifMonitorCallback callback) {
+ mOnAddressChangeCallback = callback;
+ }
+
+ void runAsync() {
+ std::unique_lock<std::mutex> lock(mThreadMutex);
+ mThread = std::make_unique<std::thread>([this]() { run(); });
+ }
+
+ void requestAddress() {
+ struct {
+ struct nlmsghdr hdr;
+ struct ifaddrmsg msg;
+ char padding[16];
+ } request;
+
+ memset(&request, 0, sizeof(request));
+ request.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(request.msg));
+ request.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
+ request.hdr.nlmsg_type = RTM_GETADDR;
+
+ int status = ::send(mSocketFd, &request, request.hdr.nlmsg_len, 0);
+ if (status < 0 ||
+ static_cast<unsigned int>(status) != request.hdr.nlmsg_len) {
+ if (status < 0) {
+ RLOGE("Failed to send netlink request: %s", strerror(errno));
+ } else {
+ RLOGE("Short send only sent %d out of %d bytes",
+ status, (int)request.hdr.nlmsg_len);
+ }
+ }
+ }
+
+ void run() {
+ requestAddress();
+
+ std::vector<struct pollfd> fds(2);
+ fds[0].events = POLLIN;
+ fds[0].fd = mControlSocket[kControlServer];
+ fds[1].events = POLLIN;
+ fds[1].fd = mSocketFd;
+ while (true) {
+ int status = ::poll(fds.data(), fds.size(), -1);
+ if (status < 0) {
+ if (errno == EINTR) {
+ // Interrupted, just keep going
+ continue;
+ }
+ // Actual error, time to quit
+ RLOGE("Polling failed: %s", strerror(errno));
+ break;
+ } else if (status == 0) {
+ // Timeout
+ continue;
+ }
+
+ if (fds[0].revents & POLLIN) {
+ // Control message received
+ char command = -1;
+ if (::read(mControlSocket[kControlServer],
+ &command,
+ sizeof(command)) == 1) {
+ if (command == kMonitorStopCommand[0]) {
+ break;
+ }
+ }
+ } else if (fds[1].revents & POLLIN) {
+ onReadAvailable();
+ }
+ }
+ ::write(mControlSocket[kControlServer], kMonitorAckCommand, 1);
+ }
+
+ void stop() {
+ std::unique_lock<std::mutex> lock(mThreadMutex);
+ if (mThread) {
+ ::write(mControlSocket[kControlClient], kMonitorStopCommand, 1);
+ char ack = -1;
+ while (ack != kMonitorAckCommand[0]) {
+ ::read(mControlSocket[kControlClient], &ack, sizeof(ack));
+ }
+ mThread->join();
+ mThread.reset();
+ }
+ }
+
+private:
+ void onReadAvailable() {
+ char buffer[kReadBufferSize];
+ struct sockaddr_storage storage;
+
+ while (true) {
+ socklen_t addrSize = sizeof(storage);
+ int status = ::recvfrom(mSocketFd,
+ buffer,
+ sizeof(buffer),
+ MSG_DONTWAIT,
+ reinterpret_cast<struct sockaddr*>(&storage),
+ &addrSize);
+ if (status < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
+ // Nothing to receive, everything is fine
+ return;
+ } else if (status < 0 && errno == EINTR) {
+ // Caught interrupt, try again
+ continue;
+ } else if (status < 0) {
+ RLOGE("InterfaceMonitor receive failed: %s", strerror(errno));
+ return;
+ } else if (addrSize < 0 ||
+ static_cast<size_t>(addrSize) != sizeof(struct sockaddr_nl)) {
+ RLOGE("InterfaceMonitor received invalid address size");
+ return;
+ }
+
+ size_t length = static_cast<size_t>(status);
+
+ auto hdr = reinterpret_cast<struct nlmsghdr*>(buffer);
+ while (NLMSG_OK(hdr, length) && hdr->nlmsg_type != NLMSG_DONE) {
+ switch (hdr->nlmsg_type) {
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ handleAddressChange(hdr);
+ break;
+ default:
+ RLOGE("Received message type %d", (int)hdr->nlmsg_type);
+ break;
+ }
+ NLMSG_NEXT(hdr, length);
+ }
+ }
+ }
+
+ void handleAddressChange(const struct nlmsghdr* hdr) {
+ if (!mOnAddressChangeCallback) {
+ return;
+ }
+
+ auto msg = reinterpret_cast<const struct ifaddrmsg*>(NLMSG_DATA(hdr));
+ std::vector<ifAddress>& ifAddrs = mAddresses[msg->ifa_index];
+
+ auto attr = reinterpret_cast<const struct rtattr*>(IFA_RTA(msg));
+ int attrLen = IFA_PAYLOAD(hdr);
+
+ bool somethingChanged = false;
+ for (;attr && RTA_OK(attr, attrLen); attr = RTA_NEXT(attr, attrLen)) {
+ if (attr->rta_type != IFA_LOCAL && attr->rta_type != IFA_ADDRESS) {
+ continue;
+ }
+
+ ifAddress addr;
+ memset(&addr, 0, sizeof(addr));
+
+ // Ensure that the payload matches the expected address length
+ if (RTA_PAYLOAD(attr) >= addrLength(msg->ifa_family)) {
+ addr.family = msg->ifa_family;
+ addr.prefix = msg->ifa_prefixlen;
+ memcpy(&addr.addr, RTA_DATA(attr), addrLength(addr.family));
+ } else {
+ RLOGE("Invalid address family (%d) and size (%d) combination",
+ int(msg->ifa_family), int(RTA_PAYLOAD(attr)));
+ continue;
+ }
+
+ auto it = std::find(ifAddrs.begin(), ifAddrs.end(), addr);
+ if (hdr->nlmsg_type == RTM_NEWADDR && it == ifAddrs.end()) {
+ // New address does not exist, add it
+ ifAddrs.push_back(addr);
+ somethingChanged = true;
+ } else if (hdr->nlmsg_type == RTM_DELADDR && it != ifAddrs.end()) {
+ // Address was removed and it exists, remove it
+ ifAddrs.erase(it);
+ somethingChanged = true;
+ }
+ }
+
+ if (somethingChanged) {
+ mOnAddressChangeCallback(msg->ifa_index,
+ ifAddrs.data(),
+ ifAddrs.size());
+ }
+ }
+
+ ifMonitorCallback mOnAddressChangeCallback;
+ std::unordered_map<unsigned int, std::vector<ifAddress>> mAddresses;
+ std::unique_ptr<std::thread> mThread;
+ std::mutex mThreadMutex;
+ int mSocketFd;
+ int mControlSocket[2];
+};
+
+extern "C"
+struct ifMonitor* ifMonitorCreate() {
+ auto monitor = std::make_unique<InterfaceMonitor>();
+ if (!monitor || !monitor->init()) {
+ return nullptr;
+ }
+ return reinterpret_cast<struct ifMonitor*>(monitor.release());
+}
+
+extern "C"
+void ifMonitorFree(struct ifMonitor* ifMonitor) {
+ InterfaceMonitor* monitor = reinterpret_cast<InterfaceMonitor*>(ifMonitor);
+ delete monitor;
+}
+
+extern "C"
+void ifMonitorSetCallback(struct ifMonitor* ifMonitor,
+ ifMonitorCallback callback) {
+ InterfaceMonitor* monitor = reinterpret_cast<InterfaceMonitor*>(ifMonitor);
+ monitor->setCallback(callback);
+}
+
+extern "C"
+void ifMonitorRunAsync(struct ifMonitor* ifMonitor) {
+ InterfaceMonitor* monitor = reinterpret_cast<InterfaceMonitor*>(ifMonitor);
+
+ monitor->runAsync();
+}
+
+extern "C"
+void ifMonitorStop(struct ifMonitor* ifMonitor) {
+ InterfaceMonitor* monitor = reinterpret_cast<InterfaceMonitor*>(ifMonitor);
+
+ monitor->stop();
+}
+
diff --git a/ril/if_monitor.h b/ril/if_monitor.h
new file mode 100644
index 0000000..118bf88
--- /dev/null
+++ b/ril/if_monitor.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ifMonitor;
+
+struct ifAddress {
+ int family;
+ int prefix;
+ unsigned char addr[16];
+};
+
+// A callback for when the addresses on an interface changes
+typedef void (*ifMonitorCallback)(unsigned int /*interface index*/,
+ const struct ifAddress* /*addresses*/,
+ size_t /*number of addresses */);
+
+struct ifMonitor* ifMonitorCreate();
+void ifMonitorFree(struct ifMonitor* monitor);
+
+void ifMonitorSetCallback(struct ifMonitor* monitor,
+ ifMonitorCallback callback);
+void ifMonitorRunAsync(struct ifMonitor* monitor);
+void ifMonitorStop(struct ifMonitor* monitor);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
diff --git a/ril/reference-ril.c b/ril/reference-ril.c
index 51f63ea..7e52e10 100644
--- a/ril/reference-ril.c
+++ b/ril/reference-ril.c
@@ -41,8 +41,10 @@
#include <sys/wait.h>
#include <stdbool.h>
#include <net/if.h>
+#include <arpa/inet.h>
#include <netinet/in.h>
+#include "if_monitor.h"
#include "ril.h"
#define LOG_TAG "RIL"
@@ -59,6 +61,11 @@ static void *noopRemoveWarning( void *a ) { return a; }
// This is used if Wifi is supported to separate radio and wifi interface
#define PPP_TTY_PATH_RADIO0 "radio0"
+// This is the IP address to provide for radio0 when WiFi is enabled
+// When WiFi is not enabled the RIL should provide the address given by
+// the modem.
+#define RADIO0_IPV4_ADDRESS "192.168.200.2/24"
+
// Default MTU value
#define DEFAULT_MTU 1500
@@ -266,6 +273,10 @@ static int s_mnc = 0;
static int s_lac = 0;
static int s_cid = 0;
+// A string containing all the IPv6 addresses of the radio interface
+static char s_ipv6_addresses[8192];
+static pthread_mutex_t s_ipv6_addresses_mutex = PTHREAD_MUTEX_INITIALIZER;
+
static void pollSIMState (void *param);
static void setRadioState(RIL_RadioState newState);
static void setRadioTechnology(ModemInfo *mdm, int newtech);
@@ -716,13 +727,32 @@ static void requestOrSendDataCallList(RIL_Token *t)
responses[i].ifname = alloca(ifname_size);
strlcpy(responses[i].ifname, radioInterfaceName, ifname_size);
+ // The next token is the IPv4 address provided by the emulator, only use
+ // it if WiFi is not enabled. When WiFi is enabled the network setup is
+ // specific to the system image and the emulator only provides the
+ // IP address for the external interface in the router namespace.
err = at_tok_nextstr(&line, &out);
if (err < 0)
goto error;
- int addresses_size = strlen(out) + 1;
+ pthread_mutex_lock(&s_ipv6_addresses_mutex);
+
+ // Extra space for null terminator and separating space
+ int addresses_size = strlen(out) + strlen(s_ipv6_addresses) + 2;
responses[i].addresses = alloca(addresses_size);
- strlcpy(responses[i].addresses, out, addresses_size);
+ if (*s_ipv6_addresses) {
+ // IPv6 addresses exist, add them
+ snprintf(responses[i].addresses, addresses_size,
+ "%s %s",
+ hasWifi ? RADIO0_IPV4_ADDRESS : out,
+ s_ipv6_addresses);
+ } else {
+ // Only provide the IPv4 address
+ strlcpy(responses[i].addresses,
+ hasWifi ? RADIO0_IPV4_ADDRESS : out,
+ addresses_size);
+ }
+ pthread_mutex_unlock(&s_ipv6_addresses_mutex);
if (isInEmulator()) {
/* We are in the emulator - the dns servers are listed
@@ -733,16 +763,16 @@ static void requestOrSendDataCallList(RIL_Token *t)
* - net.eth0.dns3
* - net.eth0.dns4
*/
- const int dnslist_sz = 128;
+ const int dnslist_sz = 256;
char* dnslist = alloca(dnslist_sz);
const char* separator = "";
int nn;
+ char propName[PROP_NAME_MAX];
+ char propValue[PROP_VALUE_MAX];
dnslist[0] = 0;
for (nn = 1; nn <= 4; nn++) {
/* Probe net.eth0.dns<n> */
- char propName[PROP_NAME_MAX];
- char propValue[PROP_VALUE_MAX];
snprintf(propName, sizeof propName, "net.eth0.dns%d", nn);
@@ -756,6 +786,18 @@ static void requestOrSendDataCallList(RIL_Token *t)
strlcat(dnslist, propValue, dnslist_sz);
separator = " ";
}
+ for (nn = 1; nn <= 4; ++nn) {
+ /* Probe net.eth0.ipv6dns<n> for IPv6 DNS servers */
+ snprintf(propName, sizeof propName, "net.eth0.ipv6dns%d", nn);
+ /* Ignore if undefined */
+ if (property_get(propName, propValue, "") <= 0) {
+ continue;
+ }
+ strlcat(dnslist, separator, dnslist_sz);
+ strlcat(dnslist, propValue, dnslist_sz);
+ separator = " ";
+ }
+
responses[i].dnses = dnslist;
/* There is only one gateway in the emulator. If WiFi is
@@ -3883,16 +3925,79 @@ static void usage(char *s __unused)
#endif
}
+static void onInterfaceAddressChange(unsigned int ifIndex,
+ const struct ifAddress* addresses,
+ size_t numAddresses) {
+ char ifName[IF_NAMESIZE];
+ size_t i;
+ bool hasWifi = hasWifiCapability();
+ const char* radioIfName = getRadioInterfaceName(hasWifi);
+ char* currentLoc;
+ size_t remaining;
+
+ if (if_indextoname(ifIndex, ifName) == NULL) {
+ RLOGE("Unable to get interface name for interface %u", ifIndex);
+ return;
+ }
+ if (strcmp(radioIfName, ifName) != 0) {
+ // This is not for the radio interface, ignore it
+ return;
+ }
+
+ pthread_mutex_lock(&s_ipv6_addresses_mutex);
+ // Clear out any existing addresses, we receive a full set of addresses
+ // that are going to replace the existing ones.
+ s_ipv6_addresses[0] = '\0';
+ currentLoc = s_ipv6_addresses;
+ remaining = sizeof(s_ipv6_addresses);
+ for (i = 0; i < numAddresses; ++i) {
+ if (addresses[i].family != AF_INET6) {
+ // Only care about IPv6 addresses
+ continue;
+ }
+ char address[INET6_ADDRSTRLEN];
+ if (inet_ntop(addresses[i].family, &addresses[i].addr,
+ address, sizeof(address))) {
+ int printed = 0;
+ if (s_ipv6_addresses[0]) {
+ // We've already printed something, separate them
+ if (remaining < 1) {
+ continue;
+ }
+ *currentLoc++ = ' ';
+ --remaining;
+ }
+ printed = snprintf(currentLoc, remaining, "%s/%d",
+ address, addresses[i].prefix);
+ if (printed > 0) {
+ remaining -= (size_t)printed;
+ currentLoc += printed;
+ }
+ } else {
+ RLOGE("Unable to convert address to string for if %s", ifName);
+ }
+ }
+ pthread_mutex_unlock(&s_ipv6_addresses_mutex);
+
+ // Send unsolicited call list change to notify upper layers about the new
+ // addresses
+ requestOrSendDataCallList(NULL);
+}
+
static void *
mainLoop(void *param __unused)
{
int fd;
int ret;
+ struct ifMonitor* monitor = ifMonitorCreate();
AT_DUMP("== ", "entering mainLoop()", -1 );
at_set_on_reader_closed(onATReaderClosed);
at_set_on_timeout(onATTimeout);
+ ifMonitorSetCallback(monitor, &onInterfaceAddressChange);
+ ifMonitorRunAsync(monitor);
+
for (;;) {
fd = -1;
while (fd < 0) {
@@ -3916,20 +4021,21 @@ mainLoop(void *param __unused)
}
if (fd < 0) {
- perror ("opening AT interface. retrying...");
+ RLOGE("Error opening AT interface, retrying...");
sleep(10);
/* never returns */
}
}
s_closed = 0;
- ret = at_open(fd, onUnsolicited);
+ ret = at_open(fd, onUnsolicited);
if (ret < 0) {
RLOGE ("AT error %d on at_open\n", ret);
- return 0;
+ break;
}
+
RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_0);
// Give initializeCallback a chance to dispatched, since
@@ -3939,6 +4045,11 @@ mainLoop(void *param __unused)
waitForClose();
RLOGI("Re-opening after close");
}
+
+ ifMonitorStop(monitor);
+ ifMonitorFree(monitor);
+
+ return NULL;
}
#ifdef RIL_SHLIB
diff --git a/sepolicy/common/ipv6proxy.te b/sepolicy/common/ipv6proxy.te
index 22976fe..1defe10 100644
--- a/sepolicy/common/ipv6proxy.te
+++ b/sepolicy/common/ipv6proxy.te
@@ -9,6 +9,7 @@ net_domain(ipv6proxy)
domain_auto_trans(execns, ipv6proxy_exec, ipv6proxy);
allow ipv6proxy execns:fd use;
+set_prop(ipv6proxy, net_eth0_prop);
allow ipv6proxy self:capability { sys_admin sys_module net_admin net_raw };
allow ipv6proxy self:packet_socket { bind create read };
allow ipv6proxy self:netlink_route_socket nlmsg_write;
diff --git a/wifi/ipv6proxy/log.h b/wifi/ipv6proxy/log.h
index 527be29..53e8935 100644
--- a/wifi/ipv6proxy/log.h
+++ b/wifi/ipv6proxy/log.h
@@ -18,7 +18,7 @@
#ifdef ANDROID
#define LOG_TAG "ipv6proxy"
-#include <cutils/log.h>
+#include <log/log.h>
#define loge(...) ALOGE(__VA_ARGS__)
#define logd(...) ALOGD(__VA_ARGS__)
diff --git a/wifi/ipv6proxy/proxy.cpp b/wifi/ipv6proxy/proxy.cpp
index 08fc0ed..cb19ff7 100644
--- a/wifi/ipv6proxy/proxy.cpp
+++ b/wifi/ipv6proxy/proxy.cpp
@@ -16,11 +16,14 @@
#include "proxy.h"
+#include <arpa/inet.h>
#include <errno.h>
#include <linux/if_packet.h>
#include <poll.h>
#include <signal.h>
+#include <cutils/properties.h>
+
#include "log.h"
#include "message.h"
#include "packet.h"
@@ -29,6 +32,7 @@
// The prefix length for an address of a single unique node
static const uint8_t kNodePrefixLength = 128;
static const size_t kLinkAddressSize = 6;
+static const size_t kRecursiveDnsOptHeaderSize = 8;
// Rewrite the link address of a neighbor discovery option to the link address
// of |interface|. This can be either a source or target link address as
@@ -46,6 +50,45 @@ static void rewriteLinkAddressOption(Packet& packet,
}
}
+static void extractRecursiveDnsServers(Packet& packet) {
+ for (nd_opt_hdr* opt = packet.firstOpt(); opt; opt = packet.nextOpt(opt)) {
+ if (opt->nd_opt_type != 25 || opt->nd_opt_len < 1) {
+ // Not a RNDSS option, skip it
+ continue;
+ }
+ size_t numEntries = (opt->nd_opt_len - 1) / 2;
+ //Found number of entries, dump each address
+ const char* option = reinterpret_cast<const char*>(opt);
+ option += kRecursiveDnsOptHeaderSize;
+ auto dnsServers = reinterpret_cast<const struct in6_addr*>(option);
+
+ std::vector<std::string> validServers;
+ for (size_t i = 0; i < numEntries; ++i) {
+ char buffer[INET6_ADDRSTRLEN];
+ if (inet_ntop(AF_INET6, &dnsServers[i], buffer, sizeof(buffer))) {
+ validServers.push_back(buffer);
+ } else {
+ loge("Failed to convert RDNSS to string\n");
+ }
+ }
+
+ auto server = validServers.begin();
+ char propName[PROP_NAME_MAX];
+ char propValue[PROP_VALUE_MAX];
+ for (int i = 1; i <= 4; ++i) {
+ snprintf(propName, sizeof(propName), "net.eth0.ipv6dns%d", i);
+ if (server != validServers.end()) {
+ property_set(propName, server->c_str());
+ ++server;
+ } else {
+ // Clear the property if it's no longer a valid server, don't
+ // want to leave old servers around
+ property_set(propName, "");
+ }
+ }
+ }
+}
+
int Proxy::run() {
sigset_t blockMask, originalMask;
int status = ::sigfillset(&blockMask);
@@ -128,6 +171,7 @@ void Proxy::handleOuterMessage(Message& message) {
uint32_t options = kForwardOnly;
switch (packet.type()) {
case Packet::Type::RouterAdvertisement:
+ extractRecursiveDnsServers(packet);
options = kRewriteSourceLink | kSetDefaultGateway;
break;
case Packet::Type::NeighborSolicitation: