Loading packages/Tethering/jni/android_net_util_TetheringUtils.cpp +51 −1 Original line number Diff line number Diff line Loading @@ -17,18 +17,63 @@ #include <errno.h> #include <error.h> #include <jni.h> #include <linux/filter.h> #include <nativehelper/JNIHelp.h> #include <nativehelper/JNIHelpCompat.h> #include <nativehelper/ScopedUtfChars.h> #include <net/if.h> #include <netinet/ether.h> #include <netinet/ip6.h> #include <netinet/icmp6.h> #include <sys/socket.h> #include <stdio.h> #define LOG_TAG "TetheringUtils" #include <android/log.h> namespace android { static const uint32_t kIPv6NextHeaderOffset = offsetof(ip6_hdr, ip6_nxt); static const uint32_t kIPv6PayloadStart = sizeof(ip6_hdr); static const uint32_t kICMPv6TypeOffset = kIPv6PayloadStart + offsetof(icmp6_hdr, icmp6_type); static void android_net_util_setupIcmpFilter(JNIEnv *env, jobject javaFd, uint32_t type) { sock_filter filter_code[] = { // Check header is ICMPv6. BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv6NextHeaderOffset), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 3), // Check ICMPv6 type. BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kICMPv6TypeOffset), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, type, 0, 1), // Accept or reject. BPF_STMT(BPF_RET | BPF_K, 0xffff), BPF_STMT(BPF_RET | BPF_K, 0) }; const sock_fprog filter = { sizeof(filter_code) / sizeof(filter_code[0]), filter_code, }; int fd = jniGetFDFromFileDescriptor(env, javaFd); if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) { jniThrowExceptionFmt(env, "java/net/SocketException", "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno)); } } static void android_net_util_setupNaSocket(JNIEnv *env, jobject clazz, jobject javaFd) { android_net_util_setupIcmpFilter(env, javaFd, ND_NEIGHBOR_ADVERT); } static void android_net_util_setupNsSocket(JNIEnv *env, jobject clazz, jobject javaFd) { android_net_util_setupIcmpFilter(env, javaFd, ND_NEIGHBOR_SOLICIT); } static void android_net_util_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd, jint ifIndex) { Loading Loading @@ -125,7 +170,12 @@ static void android_net_util_setupRaSocket(JNIEnv *env, jobject clazz, jobject j */ static const JNINativeMethod gMethods[] = { /* name, signature, funcPtr */ { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_util_setupRaSocket }, { "setupNaSocket", "(Ljava/io/FileDescriptor;)V", (void*) android_net_util_setupNaSocket }, { "setupNsSocket", "(Ljava/io/FileDescriptor;)V", (void*) android_net_util_setupNsSocket }, { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_util_setupRaSocket }, }; int register_android_net_util_TetheringUtils(JNIEnv* env) { Loading packages/Tethering/src/android/net/ip/DadProxy.java 0 → 100644 +54 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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. */ package android.net.ip; import android.net.util.InterfaceParams; import android.os.Handler; import androidx.annotation.VisibleForTesting; /** * Basic Duplicate address detection proxy. * * @hide */ public class DadProxy { private static final String TAG = DadProxy.class.getSimpleName(); @VisibleForTesting public static NeighborPacketForwarder naForwarder; public static NeighborPacketForwarder nsForwarder; public DadProxy(Handler h, InterfaceParams tetheredIface) { naForwarder = new NeighborPacketForwarder(h, tetheredIface, NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT); nsForwarder = new NeighborPacketForwarder(h, tetheredIface, NeighborPacketForwarder.ICMPV6_NEIGHBOR_SOLICITATION); } /** Stop NS/NA Forwarders. */ public void stop() { naForwarder.stop(); nsForwarder.stop(); } /** Set upstream iface on both forwarders. */ public void setUpstreamIface(InterfaceParams upstreamIface) { naForwarder.setUpstreamIface(upstreamIface); nsForwarder.setUpstreamIface(upstreamIface); } } packages/Tethering/src/android/net/ip/IpServer.java +37 −8 Original line number Diff line number Diff line Loading @@ -51,6 +51,7 @@ import android.net.util.InterfaceParams; import android.net.util.InterfaceSet; import android.net.util.PrefixUtils; import android.net.util.SharedLog; import android.os.Build; import android.os.Handler; import android.os.Looper; import android.os.Message; Loading Loading @@ -160,6 +161,15 @@ public class IpServer extends StateMachine { /** Capture IpServer dependencies, for injection. */ public abstract static class Dependencies { /** * Create a DadProxy instance to be used by IpServer. * To support multiple tethered interfaces concurrently DAD Proxy * needs to be supported per IpServer instead of per upstream. */ public DadProxy getDadProxy(Handler handler, InterfaceParams ifParams) { return new DadProxy(handler, ifParams); } /** Create an IpNeighborMonitor to be used by this IpServer */ public IpNeighborMonitor getIpNeighborMonitor(Handler handler, SharedLog log, IpNeighborMonitor.NeighborEventConsumer consumer) { Loading Loading @@ -256,6 +266,7 @@ public class IpServer extends StateMachine { // Advertisements (otherwise, we do not add them to mLinkProperties at all). private LinkProperties mLastIPv6LinkProperties; private RouterAdvertisementDaemon mRaDaemon; private DadProxy mDadProxy; // To be accessed only on the handler thread private int mDhcpServerStartIndex = 0; Loading Loading @@ -674,6 +685,13 @@ public class IpServer extends StateMachine { return false; } // TODO: use ShimUtils instead of explicitly checking the version here. if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R || "S".equals(Build.VERSION.CODENAME) || "T".equals(Build.VERSION.CODENAME)) { // DAD Proxy starts forwarding packets after IPv6 upstream is present. mDadProxy = mDeps.getDadProxy(getHandler(), mInterfaceParams); } return true; } Loading @@ -685,6 +703,11 @@ public class IpServer extends StateMachine { mRaDaemon.stop(); mRaDaemon = null; } if (mDadProxy != null) { mDadProxy.stop(); mDadProxy = null; } } // IPv6TetheringCoordinator sends updates with carefully curated IPv6-only Loading @@ -702,11 +725,16 @@ public class IpServer extends StateMachine { } RaParams params = null; int upstreamIfindex = 0; String upstreamIface = null; InterfaceParams upstreamIfaceParams = null; int upstreamIfIndex = 0; if (v6only != null) { final String upstreamIface = v6only.getInterfaceName(); upstreamIface = v6only.getInterfaceName(); upstreamIfaceParams = mDeps.getInterfaceParams(upstreamIface); if (upstreamIfaceParams != null) { upstreamIfIndex = upstreamIfaceParams.index; } params = new RaParams(); params.mtu = v6only.getMtu(); params.hasDefaultRoute = v6only.hasIpv6DefaultRoute(); Loading @@ -726,15 +754,13 @@ public class IpServer extends StateMachine { } } upstreamIfindex = mDeps.getIfindex(upstreamIface); // Add upstream index to name mapping for the tether stats usage in the coordinator. // Although this mapping could be added by both class Tethering and IpServer, adding // mapping from IpServer guarantees that the mapping is added before the adding // forwarding rules. That is because there are different state machines in both // classes. It is hard to guarantee the link property update order between multiple // state machines. mBpfCoordinator.addUpstreamNameToLookupTable(upstreamIfindex, upstreamIface); mBpfCoordinator.addUpstreamNameToLookupTable(upstreamIfIndex, upstreamIface); } // If v6only is null, we pass in null to setRaParams(), which handles Loading @@ -743,8 +769,11 @@ public class IpServer extends StateMachine { setRaParams(params); mLastIPv6LinkProperties = v6only; updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, upstreamIfindex, null); mLastIPv6UpstreamIfindex = upstreamIfindex; updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, upstreamIfIndex, null); mLastIPv6UpstreamIfindex = upstreamIfIndex; if (mDadProxy != null) { mDadProxy.setUpstreamIface(upstreamIfaceParams); } } private void removeRoutesFromLocalNetwork(@NonNull final List<RouteInfo> toBeRemoved) { Loading packages/Tethering/src/android/net/ip/NeighborPacketForwarder.java 0 → 100644 +180 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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. */ package android.net.ip; import static android.system.OsConstants.AF_INET6; import static android.system.OsConstants.AF_PACKET; import static android.system.OsConstants.ETH_P_IPV6; import static android.system.OsConstants.IPPROTO_RAW; import static android.system.OsConstants.SOCK_DGRAM; import static android.system.OsConstants.SOCK_NONBLOCK; import static android.system.OsConstants.SOCK_RAW; import android.net.util.InterfaceParams; import android.net.util.PacketReader; import android.net.util.SocketUtils; import android.net.util.TetheringUtils; import android.os.Handler; import android.system.ErrnoException; import android.system.Os; import android.util.Log; import java.io.FileDescriptor; import java.io.IOException; import java.net.Inet6Address; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.SocketException; import java.net.UnknownHostException; import java.util.Arrays; /** * Basic IPv6 Neighbor Advertisement Forwarder. * * Forward NA packets from upstream iface to tethered iface * and NS packets from tethered iface to upstream iface. * * @hide */ public class NeighborPacketForwarder extends PacketReader { private final String mTag; private FileDescriptor mFd; // TODO: get these from NetworkStackConstants. private static final int IPV6_ADDR_LEN = 16; private static final int IPV6_DST_ADDR_OFFSET = 24; private static final int IPV6_HEADER_LEN = 40; private static final int ETH_HEADER_LEN = 14; private InterfaceParams mListenIfaceParams, mSendIfaceParams; private final int mType; public static final int ICMPV6_NEIGHBOR_ADVERTISEMENT = 136; public static final int ICMPV6_NEIGHBOR_SOLICITATION = 135; public NeighborPacketForwarder(Handler h, InterfaceParams tetheredInterface, int type) { super(h); mTag = NeighborPacketForwarder.class.getSimpleName() + "-" + tetheredInterface.name + "-" + type; mType = type; if (mType == ICMPV6_NEIGHBOR_ADVERTISEMENT) { mSendIfaceParams = tetheredInterface; } else { mListenIfaceParams = tetheredInterface; } } /** Set new upstream iface and start/stop based on new params. */ public void setUpstreamIface(InterfaceParams upstreamParams) { final InterfaceParams oldUpstreamParams; if (mType == ICMPV6_NEIGHBOR_ADVERTISEMENT) { oldUpstreamParams = mListenIfaceParams; mListenIfaceParams = upstreamParams; } else { oldUpstreamParams = mSendIfaceParams; mSendIfaceParams = upstreamParams; } if (oldUpstreamParams == null && upstreamParams != null) { start(); } else if (oldUpstreamParams != null && upstreamParams == null) { stop(); } else if (oldUpstreamParams != null && upstreamParams != null && oldUpstreamParams.index != upstreamParams.index) { stop(); start(); } } // TODO: move NetworkStackUtils.closeSocketQuietly to // frameworks/libs/net/common/device/com/android/net/module/util/[someclass]. private void closeSocketQuietly(FileDescriptor fd) { try { SocketUtils.closeSocket(fd); } catch (IOException ignored) { } } @Override protected FileDescriptor createFd() { try { // ICMPv6 packets from modem do not have eth header, so RAW socket cannot be used. // To keep uniformity in both directions PACKET socket can be used. mFd = Os.socket(AF_PACKET, SOCK_DGRAM | SOCK_NONBLOCK, 0); // TODO: convert setup*Socket to setupICMPv6BpfFilter with filter type? if (mType == ICMPV6_NEIGHBOR_ADVERTISEMENT) { TetheringUtils.setupNaSocket(mFd); } else if (mType == ICMPV6_NEIGHBOR_SOLICITATION) { TetheringUtils.setupNsSocket(mFd); } SocketAddress bindAddress = SocketUtils.makePacketSocketAddress( ETH_P_IPV6, mListenIfaceParams.index); Os.bind(mFd, bindAddress); } catch (ErrnoException | SocketException e) { Log.wtf(mTag, "Failed to create socket", e); closeSocketQuietly(mFd); return null; } return mFd; } private Inet6Address getIpv6DestinationAddress(byte[] recvbuf) { Inet6Address dstAddr; try { dstAddr = (Inet6Address) Inet6Address.getByAddress(Arrays.copyOfRange(recvbuf, IPV6_DST_ADDR_OFFSET, IPV6_DST_ADDR_OFFSET + IPV6_ADDR_LEN)); } catch (UnknownHostException | ClassCastException impossible) { throw new AssertionError("16-byte array not valid IPv6 address?"); } return dstAddr; } @Override protected void handlePacket(byte[] recvbuf, int length) { if (mSendIfaceParams == null) { return; } // The BPF filter should already have checked the length of the packet, but... if (length < IPV6_HEADER_LEN) { return; } Inet6Address destv6 = getIpv6DestinationAddress(recvbuf); if (!destv6.isMulticastAddress()) { return; } InetSocketAddress dest = new InetSocketAddress(destv6, 0); FileDescriptor fd = null; try { fd = Os.socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW); SocketUtils.bindSocketToInterface(fd, mSendIfaceParams.name); int ret = Os.sendto(fd, recvbuf, 0, length, 0, dest); } catch (ErrnoException | SocketException e) { Log.e(mTag, "handlePacket error: " + e); } finally { closeSocketQuietly(fd); } } } packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java +1 −15 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.net.ip; import static android.net.util.NetworkConstants.IPV6_MIN_MTU; import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH; import static android.net.util.TetheringUtils.getAllNodesForScopeId; import static android.system.OsConstants.AF_INET6; import static android.system.OsConstants.IPPROTO_ICMPV6; import static android.system.OsConstants.SOCK_RAW; Loading @@ -44,7 +45,6 @@ import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketException; import java.net.UnknownHostException; import java.nio.BufferOverflowException; import java.nio.ByteBuffer; import java.nio.ByteOrder; Loading Loading @@ -92,10 +92,6 @@ public class RouterAdvertisementDaemon { private static final int DAY_IN_SECONDS = 86_400; private static final byte[] ALL_NODES = new byte[] { (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; private final InterfaceParams mInterface; private final InetSocketAddress mAllNodes; Loading Loading @@ -240,7 +236,6 @@ public class RouterAdvertisementDaemon { } } public RouterAdvertisementDaemon(InterfaceParams ifParams) { mInterface = ifParams; mAllNodes = new InetSocketAddress(getAllNodesForScopeId(mInterface.index), 0); Loading Loading @@ -363,15 +358,6 @@ public class RouterAdvertisementDaemon { } } private static Inet6Address getAllNodesForScopeId(int scopeId) { try { return Inet6Address.getByAddress("ff02::1", ALL_NODES, scopeId); } catch (UnknownHostException uhe) { Log.wtf(TAG, "Failed to construct ff02::1 InetAddress: " + uhe); return null; } } private static byte asByte(int value) { return (byte) value; } Loading Loading
packages/Tethering/jni/android_net_util_TetheringUtils.cpp +51 −1 Original line number Diff line number Diff line Loading @@ -17,18 +17,63 @@ #include <errno.h> #include <error.h> #include <jni.h> #include <linux/filter.h> #include <nativehelper/JNIHelp.h> #include <nativehelper/JNIHelpCompat.h> #include <nativehelper/ScopedUtfChars.h> #include <net/if.h> #include <netinet/ether.h> #include <netinet/ip6.h> #include <netinet/icmp6.h> #include <sys/socket.h> #include <stdio.h> #define LOG_TAG "TetheringUtils" #include <android/log.h> namespace android { static const uint32_t kIPv6NextHeaderOffset = offsetof(ip6_hdr, ip6_nxt); static const uint32_t kIPv6PayloadStart = sizeof(ip6_hdr); static const uint32_t kICMPv6TypeOffset = kIPv6PayloadStart + offsetof(icmp6_hdr, icmp6_type); static void android_net_util_setupIcmpFilter(JNIEnv *env, jobject javaFd, uint32_t type) { sock_filter filter_code[] = { // Check header is ICMPv6. BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv6NextHeaderOffset), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 3), // Check ICMPv6 type. BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kICMPv6TypeOffset), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, type, 0, 1), // Accept or reject. BPF_STMT(BPF_RET | BPF_K, 0xffff), BPF_STMT(BPF_RET | BPF_K, 0) }; const sock_fprog filter = { sizeof(filter_code) / sizeof(filter_code[0]), filter_code, }; int fd = jniGetFDFromFileDescriptor(env, javaFd); if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) { jniThrowExceptionFmt(env, "java/net/SocketException", "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno)); } } static void android_net_util_setupNaSocket(JNIEnv *env, jobject clazz, jobject javaFd) { android_net_util_setupIcmpFilter(env, javaFd, ND_NEIGHBOR_ADVERT); } static void android_net_util_setupNsSocket(JNIEnv *env, jobject clazz, jobject javaFd) { android_net_util_setupIcmpFilter(env, javaFd, ND_NEIGHBOR_SOLICIT); } static void android_net_util_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd, jint ifIndex) { Loading Loading @@ -125,7 +170,12 @@ static void android_net_util_setupRaSocket(JNIEnv *env, jobject clazz, jobject j */ static const JNINativeMethod gMethods[] = { /* name, signature, funcPtr */ { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_util_setupRaSocket }, { "setupNaSocket", "(Ljava/io/FileDescriptor;)V", (void*) android_net_util_setupNaSocket }, { "setupNsSocket", "(Ljava/io/FileDescriptor;)V", (void*) android_net_util_setupNsSocket }, { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_util_setupRaSocket }, }; int register_android_net_util_TetheringUtils(JNIEnv* env) { Loading
packages/Tethering/src/android/net/ip/DadProxy.java 0 → 100644 +54 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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. */ package android.net.ip; import android.net.util.InterfaceParams; import android.os.Handler; import androidx.annotation.VisibleForTesting; /** * Basic Duplicate address detection proxy. * * @hide */ public class DadProxy { private static final String TAG = DadProxy.class.getSimpleName(); @VisibleForTesting public static NeighborPacketForwarder naForwarder; public static NeighborPacketForwarder nsForwarder; public DadProxy(Handler h, InterfaceParams tetheredIface) { naForwarder = new NeighborPacketForwarder(h, tetheredIface, NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT); nsForwarder = new NeighborPacketForwarder(h, tetheredIface, NeighborPacketForwarder.ICMPV6_NEIGHBOR_SOLICITATION); } /** Stop NS/NA Forwarders. */ public void stop() { naForwarder.stop(); nsForwarder.stop(); } /** Set upstream iface on both forwarders. */ public void setUpstreamIface(InterfaceParams upstreamIface) { naForwarder.setUpstreamIface(upstreamIface); nsForwarder.setUpstreamIface(upstreamIface); } }
packages/Tethering/src/android/net/ip/IpServer.java +37 −8 Original line number Diff line number Diff line Loading @@ -51,6 +51,7 @@ import android.net.util.InterfaceParams; import android.net.util.InterfaceSet; import android.net.util.PrefixUtils; import android.net.util.SharedLog; import android.os.Build; import android.os.Handler; import android.os.Looper; import android.os.Message; Loading Loading @@ -160,6 +161,15 @@ public class IpServer extends StateMachine { /** Capture IpServer dependencies, for injection. */ public abstract static class Dependencies { /** * Create a DadProxy instance to be used by IpServer. * To support multiple tethered interfaces concurrently DAD Proxy * needs to be supported per IpServer instead of per upstream. */ public DadProxy getDadProxy(Handler handler, InterfaceParams ifParams) { return new DadProxy(handler, ifParams); } /** Create an IpNeighborMonitor to be used by this IpServer */ public IpNeighborMonitor getIpNeighborMonitor(Handler handler, SharedLog log, IpNeighborMonitor.NeighborEventConsumer consumer) { Loading Loading @@ -256,6 +266,7 @@ public class IpServer extends StateMachine { // Advertisements (otherwise, we do not add them to mLinkProperties at all). private LinkProperties mLastIPv6LinkProperties; private RouterAdvertisementDaemon mRaDaemon; private DadProxy mDadProxy; // To be accessed only on the handler thread private int mDhcpServerStartIndex = 0; Loading Loading @@ -674,6 +685,13 @@ public class IpServer extends StateMachine { return false; } // TODO: use ShimUtils instead of explicitly checking the version here. if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R || "S".equals(Build.VERSION.CODENAME) || "T".equals(Build.VERSION.CODENAME)) { // DAD Proxy starts forwarding packets after IPv6 upstream is present. mDadProxy = mDeps.getDadProxy(getHandler(), mInterfaceParams); } return true; } Loading @@ -685,6 +703,11 @@ public class IpServer extends StateMachine { mRaDaemon.stop(); mRaDaemon = null; } if (mDadProxy != null) { mDadProxy.stop(); mDadProxy = null; } } // IPv6TetheringCoordinator sends updates with carefully curated IPv6-only Loading @@ -702,11 +725,16 @@ public class IpServer extends StateMachine { } RaParams params = null; int upstreamIfindex = 0; String upstreamIface = null; InterfaceParams upstreamIfaceParams = null; int upstreamIfIndex = 0; if (v6only != null) { final String upstreamIface = v6only.getInterfaceName(); upstreamIface = v6only.getInterfaceName(); upstreamIfaceParams = mDeps.getInterfaceParams(upstreamIface); if (upstreamIfaceParams != null) { upstreamIfIndex = upstreamIfaceParams.index; } params = new RaParams(); params.mtu = v6only.getMtu(); params.hasDefaultRoute = v6only.hasIpv6DefaultRoute(); Loading @@ -726,15 +754,13 @@ public class IpServer extends StateMachine { } } upstreamIfindex = mDeps.getIfindex(upstreamIface); // Add upstream index to name mapping for the tether stats usage in the coordinator. // Although this mapping could be added by both class Tethering and IpServer, adding // mapping from IpServer guarantees that the mapping is added before the adding // forwarding rules. That is because there are different state machines in both // classes. It is hard to guarantee the link property update order between multiple // state machines. mBpfCoordinator.addUpstreamNameToLookupTable(upstreamIfindex, upstreamIface); mBpfCoordinator.addUpstreamNameToLookupTable(upstreamIfIndex, upstreamIface); } // If v6only is null, we pass in null to setRaParams(), which handles Loading @@ -743,8 +769,11 @@ public class IpServer extends StateMachine { setRaParams(params); mLastIPv6LinkProperties = v6only; updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, upstreamIfindex, null); mLastIPv6UpstreamIfindex = upstreamIfindex; updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, upstreamIfIndex, null); mLastIPv6UpstreamIfindex = upstreamIfIndex; if (mDadProxy != null) { mDadProxy.setUpstreamIface(upstreamIfaceParams); } } private void removeRoutesFromLocalNetwork(@NonNull final List<RouteInfo> toBeRemoved) { Loading
packages/Tethering/src/android/net/ip/NeighborPacketForwarder.java 0 → 100644 +180 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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. */ package android.net.ip; import static android.system.OsConstants.AF_INET6; import static android.system.OsConstants.AF_PACKET; import static android.system.OsConstants.ETH_P_IPV6; import static android.system.OsConstants.IPPROTO_RAW; import static android.system.OsConstants.SOCK_DGRAM; import static android.system.OsConstants.SOCK_NONBLOCK; import static android.system.OsConstants.SOCK_RAW; import android.net.util.InterfaceParams; import android.net.util.PacketReader; import android.net.util.SocketUtils; import android.net.util.TetheringUtils; import android.os.Handler; import android.system.ErrnoException; import android.system.Os; import android.util.Log; import java.io.FileDescriptor; import java.io.IOException; import java.net.Inet6Address; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.SocketException; import java.net.UnknownHostException; import java.util.Arrays; /** * Basic IPv6 Neighbor Advertisement Forwarder. * * Forward NA packets from upstream iface to tethered iface * and NS packets from tethered iface to upstream iface. * * @hide */ public class NeighborPacketForwarder extends PacketReader { private final String mTag; private FileDescriptor mFd; // TODO: get these from NetworkStackConstants. private static final int IPV6_ADDR_LEN = 16; private static final int IPV6_DST_ADDR_OFFSET = 24; private static final int IPV6_HEADER_LEN = 40; private static final int ETH_HEADER_LEN = 14; private InterfaceParams mListenIfaceParams, mSendIfaceParams; private final int mType; public static final int ICMPV6_NEIGHBOR_ADVERTISEMENT = 136; public static final int ICMPV6_NEIGHBOR_SOLICITATION = 135; public NeighborPacketForwarder(Handler h, InterfaceParams tetheredInterface, int type) { super(h); mTag = NeighborPacketForwarder.class.getSimpleName() + "-" + tetheredInterface.name + "-" + type; mType = type; if (mType == ICMPV6_NEIGHBOR_ADVERTISEMENT) { mSendIfaceParams = tetheredInterface; } else { mListenIfaceParams = tetheredInterface; } } /** Set new upstream iface and start/stop based on new params. */ public void setUpstreamIface(InterfaceParams upstreamParams) { final InterfaceParams oldUpstreamParams; if (mType == ICMPV6_NEIGHBOR_ADVERTISEMENT) { oldUpstreamParams = mListenIfaceParams; mListenIfaceParams = upstreamParams; } else { oldUpstreamParams = mSendIfaceParams; mSendIfaceParams = upstreamParams; } if (oldUpstreamParams == null && upstreamParams != null) { start(); } else if (oldUpstreamParams != null && upstreamParams == null) { stop(); } else if (oldUpstreamParams != null && upstreamParams != null && oldUpstreamParams.index != upstreamParams.index) { stop(); start(); } } // TODO: move NetworkStackUtils.closeSocketQuietly to // frameworks/libs/net/common/device/com/android/net/module/util/[someclass]. private void closeSocketQuietly(FileDescriptor fd) { try { SocketUtils.closeSocket(fd); } catch (IOException ignored) { } } @Override protected FileDescriptor createFd() { try { // ICMPv6 packets from modem do not have eth header, so RAW socket cannot be used. // To keep uniformity in both directions PACKET socket can be used. mFd = Os.socket(AF_PACKET, SOCK_DGRAM | SOCK_NONBLOCK, 0); // TODO: convert setup*Socket to setupICMPv6BpfFilter with filter type? if (mType == ICMPV6_NEIGHBOR_ADVERTISEMENT) { TetheringUtils.setupNaSocket(mFd); } else if (mType == ICMPV6_NEIGHBOR_SOLICITATION) { TetheringUtils.setupNsSocket(mFd); } SocketAddress bindAddress = SocketUtils.makePacketSocketAddress( ETH_P_IPV6, mListenIfaceParams.index); Os.bind(mFd, bindAddress); } catch (ErrnoException | SocketException e) { Log.wtf(mTag, "Failed to create socket", e); closeSocketQuietly(mFd); return null; } return mFd; } private Inet6Address getIpv6DestinationAddress(byte[] recvbuf) { Inet6Address dstAddr; try { dstAddr = (Inet6Address) Inet6Address.getByAddress(Arrays.copyOfRange(recvbuf, IPV6_DST_ADDR_OFFSET, IPV6_DST_ADDR_OFFSET + IPV6_ADDR_LEN)); } catch (UnknownHostException | ClassCastException impossible) { throw new AssertionError("16-byte array not valid IPv6 address?"); } return dstAddr; } @Override protected void handlePacket(byte[] recvbuf, int length) { if (mSendIfaceParams == null) { return; } // The BPF filter should already have checked the length of the packet, but... if (length < IPV6_HEADER_LEN) { return; } Inet6Address destv6 = getIpv6DestinationAddress(recvbuf); if (!destv6.isMulticastAddress()) { return; } InetSocketAddress dest = new InetSocketAddress(destv6, 0); FileDescriptor fd = null; try { fd = Os.socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW); SocketUtils.bindSocketToInterface(fd, mSendIfaceParams.name); int ret = Os.sendto(fd, recvbuf, 0, length, 0, dest); } catch (ErrnoException | SocketException e) { Log.e(mTag, "handlePacket error: " + e); } finally { closeSocketQuietly(fd); } } }
packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java +1 −15 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.net.ip; import static android.net.util.NetworkConstants.IPV6_MIN_MTU; import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH; import static android.net.util.TetheringUtils.getAllNodesForScopeId; import static android.system.OsConstants.AF_INET6; import static android.system.OsConstants.IPPROTO_ICMPV6; import static android.system.OsConstants.SOCK_RAW; Loading @@ -44,7 +45,6 @@ import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketException; import java.net.UnknownHostException; import java.nio.BufferOverflowException; import java.nio.ByteBuffer; import java.nio.ByteOrder; Loading Loading @@ -92,10 +92,6 @@ public class RouterAdvertisementDaemon { private static final int DAY_IN_SECONDS = 86_400; private static final byte[] ALL_NODES = new byte[] { (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; private final InterfaceParams mInterface; private final InetSocketAddress mAllNodes; Loading Loading @@ -240,7 +236,6 @@ public class RouterAdvertisementDaemon { } } public RouterAdvertisementDaemon(InterfaceParams ifParams) { mInterface = ifParams; mAllNodes = new InetSocketAddress(getAllNodesForScopeId(mInterface.index), 0); Loading Loading @@ -363,15 +358,6 @@ public class RouterAdvertisementDaemon { } } private static Inet6Address getAllNodesForScopeId(int scopeId) { try { return Inet6Address.getByAddress("ff02::1", ALL_NODES, scopeId); } catch (UnknownHostException uhe) { Log.wtf(TAG, "Failed to construct ff02::1 InetAddress: " + uhe); return null; } } private static byte asByte(int value) { return (byte) value; } Loading