Loading packages/Tethering/src/android/net/ip/IpServer.java +142 −1 Original line number Diff line number Diff line Loading @@ -40,12 +40,14 @@ import android.net.dhcp.DhcpServingParamsParcel; import android.net.dhcp.DhcpServingParamsParcelExt; import android.net.dhcp.IDhcpLeaseCallbacks; import android.net.dhcp.IDhcpServer; import android.net.ip.IpNeighborMonitor.NeighborEvent; import android.net.ip.RouterAdvertisementDaemon.RaParams; import android.net.shared.NetdUtils; import android.net.shared.RouteUtils; import android.net.util.InterfaceParams; import android.net.util.InterfaceSet; import android.net.util.SharedLog; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.RemoteException; Loading @@ -59,14 +61,17 @@ import com.android.internal.util.MessageUtils; import com.android.internal.util.State; import com.android.internal.util.StateMachine; import java.io.IOException; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Objects; import java.util.Random; Loading Loading @@ -149,6 +154,12 @@ public class IpServer extends StateMachine { /** Capture IpServer dependencies, for injection. */ public abstract static class Dependencies { /** Create an IpNeighborMonitor to be used by this IpServer */ public IpNeighborMonitor getIpNeighborMonitor(Handler handler, SharedLog log, IpNeighborMonitor.NeighborEventConsumer consumer) { return new IpNeighborMonitor(handler, log, consumer); } /** Create a RouterAdvertisementDaemon instance to be used by IpServer.*/ public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) { return new RouterAdvertisementDaemon(ifParams); Loading @@ -159,6 +170,15 @@ public class IpServer extends StateMachine { return InterfaceParams.getByName(ifName); } /** Get |ifName|'s interface index. */ public int getIfindex(String ifName) { try { return NetworkInterface.getByName(ifName).getIndex(); } catch (IOException | NullPointerException e) { Log.e(TAG, "Can't determine interface index for interface " + ifName); return 0; } } /** Create a DhcpServer instance to be used by IpServer. */ public abstract void makeDhcpServer(String ifName, DhcpServingParamsParcel params, DhcpServerCallbacks cb); Loading @@ -184,6 +204,8 @@ public class IpServer extends StateMachine { public static final int CMD_TETHER_CONNECTION_CHANGED = BASE_IPSERVER + 9; // new IPv6 tethering parameters need to be processed public static final int CMD_IPV6_TETHER_UPDATE = BASE_IPSERVER + 10; // new neighbor cache entry on our interface public static final int CMD_NEIGHBOR_EVENT = BASE_IPSERVER + 11; private final State mInitialState; private final State mLocalHotspotState; Loading Loading @@ -223,6 +245,40 @@ public class IpServer extends StateMachine { @NonNull private List<TetheredClient> mDhcpLeases = Collections.emptyList(); private int mLastIPv6UpstreamIfindex = 0; private class MyNeighborEventConsumer implements IpNeighborMonitor.NeighborEventConsumer { public void accept(NeighborEvent e) { sendMessage(CMD_NEIGHBOR_EVENT, e); } } static class Ipv6ForwardingRule { public final int upstreamIfindex; public final int downstreamIfindex; public final Inet6Address address; public final MacAddress srcMac; public final MacAddress dstMac; Ipv6ForwardingRule(int upstreamIfindex, int downstreamIfIndex, Inet6Address address, MacAddress srcMac, MacAddress dstMac) { this.upstreamIfindex = upstreamIfindex; this.downstreamIfindex = downstreamIfIndex; this.address = address; this.srcMac = srcMac; this.dstMac = dstMac; } public Ipv6ForwardingRule onNewUpstream(int newUpstreamIfindex) { return new Ipv6ForwardingRule(newUpstreamIfindex, downstreamIfindex, address, srcMac, dstMac); } } private final LinkedHashMap<Inet6Address, Ipv6ForwardingRule> mIpv6ForwardingRules = new LinkedHashMap<>(); private final IpNeighborMonitor mIpNeighborMonitor; public IpServer( String ifaceName, Looper looper, int interfaceType, SharedLog log, INetd netd, Callback callback, boolean usingLegacyDhcp, Dependencies deps) { Loading @@ -240,6 +296,12 @@ public class IpServer extends StateMachine { mLastError = TetheringManager.TETHER_ERROR_NO_ERROR; mServingMode = STATE_AVAILABLE; mIpNeighborMonitor = mDeps.getIpNeighborMonitor(getHandler(), mLog, new MyNeighborEventConsumer()); if (!mIpNeighborMonitor.start()) { mLog.e("Failed to create IpNeighborMonitor on " + mIfaceName); } mInitialState = new InitialState(); mLocalHotspotState = new LocalHotspotState(); mTetheredState = new TetheredState(); Loading Loading @@ -607,8 +669,11 @@ public class IpServer extends StateMachine { } RaParams params = null; int upstreamIfindex = 0; if (v6only != null) { final String upstreamIface = v6only.getInterfaceName(); params = new RaParams(); // We advertise an mtu lower by 16, which is the closest multiple of 8 >= 14, // the ethernet header size. This makes kernel ebpf tethering offload happy. Loading @@ -618,7 +683,7 @@ public class IpServer extends StateMachine { params.mtu = v6only.getMtu() - 16; params.hasDefaultRoute = v6only.hasIpv6DefaultRoute(); if (params.hasDefaultRoute) params.hopLimit = getHopLimit(v6only.getInterfaceName()); if (params.hasDefaultRoute) params.hopLimit = getHopLimit(upstreamIface); for (LinkAddress linkAddr : v6only.getLinkAddresses()) { if (linkAddr.getPrefixLength() != RFC7421_PREFIX_LENGTH) continue; Loading @@ -632,12 +697,18 @@ public class IpServer extends StateMachine { params.dnses.add(dnsServer); } } upstreamIfindex = mDeps.getIfindex(upstreamIface); } // If v6only is null, we pass in null to setRaParams(), which handles // deprecation of any existing RA data. setRaParams(params); mLastIPv6LinkProperties = v6only; updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, upstreamIfindex, null); mLastIPv6UpstreamIfindex = upstreamIfindex; } private void configureLocalIPv6Routes( Loading Loading @@ -732,6 +803,73 @@ public class IpServer extends StateMachine { } } private void addIpv6ForwardingRule(Ipv6ForwardingRule rule) { try { mNetd.tetherRuleAddDownstreamIpv6(mInterfaceParams.index, rule.upstreamIfindex, rule.address.getAddress(), mInterfaceParams.macAddr.toByteArray(), rule.dstMac.toByteArray()); mIpv6ForwardingRules.put(rule.address, rule); } catch (RemoteException | ServiceSpecificException e) { Log.e(TAG, "Could not add IPv6 downstream rule: " + e); } } private void removeIpv6ForwardingRule(Ipv6ForwardingRule rule, boolean removeFromMap) { try { mNetd.tetherRuleRemoveDownstreamIpv6(rule.upstreamIfindex, rule.address.getAddress()); if (removeFromMap) { mIpv6ForwardingRules.remove(rule.address); } } catch (RemoteException | ServiceSpecificException e) { Log.e(TAG, "Could not remove IPv6 downstream rule: " + e); } } // Convenience method to replace a rule with the same rule on a new upstream interface. // Allows replacing the rules in one iteration pass without ConcurrentModificationExceptions. // Relies on the fact that rules are in a map indexed by IP address. private void updateIpv6ForwardingRule(Ipv6ForwardingRule rule, int newIfindex) { addIpv6ForwardingRule(rule.onNewUpstream(newIfindex)); removeIpv6ForwardingRule(rule, false /*removeFromMap*/); } // Handles all updates to IPv6 forwarding rules. These can currently change only if the upstream // changes or if a neighbor event is received. private void updateIpv6ForwardingRules(int prevUpstreamIfindex, int upstreamIfindex, NeighborEvent e) { // If the upstream interface has changed, remove all rules and re-add them with the new // upstream interface. if (prevUpstreamIfindex != upstreamIfindex) { for (Ipv6ForwardingRule rule : mIpv6ForwardingRules.values()) { updateIpv6ForwardingRule(rule, upstreamIfindex); } } // If we're here to process a NeighborEvent, do so now. if (e == null) return; if (!(e.ip instanceof Inet6Address) || e.ip.isMulticastAddress() || e.ip.isLoopbackAddress() || e.ip.isLinkLocalAddress()) { return; } Ipv6ForwardingRule rule = new Ipv6ForwardingRule(mLastIPv6UpstreamIfindex, mInterfaceParams.index, (Inet6Address) e.ip, mInterfaceParams.macAddr, e.macAddr); if (e.isValid()) { addIpv6ForwardingRule(rule); } else { removeIpv6ForwardingRule(rule, true /*removeFromMap*/); } } private void handleNeighborEvent(NeighborEvent e) { if (mInterfaceParams != null && mInterfaceParams.index == e.ifindex && mInterfaceParams.hasMacAddress) { updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, mLastIPv6UpstreamIfindex, e); } } private byte getHopLimit(String upstreamIface) { try { int upstreamHopLimit = Integer.parseUnsignedInt( Loading Loading @@ -1019,6 +1157,9 @@ public class IpServer extends StateMachine { } } break; case CMD_NEIGHBOR_EVENT: handleNeighborEvent((NeighborEvent) message.obj); break; default: return false; } Loading packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java +111 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,11 @@ import static android.net.ip.IpServer.STATE_AVAILABLE; import static android.net.ip.IpServer.STATE_LOCAL_ONLY; import static android.net.ip.IpServer.STATE_TETHERED; import static android.net.ip.IpServer.STATE_UNAVAILABLE; import static android.net.netlink.NetlinkConstants.RTM_DELNEIGH; import static android.net.netlink.NetlinkConstants.RTM_NEWNEIGH; import static android.net.netlink.StructNdMsg.NUD_FAILED; import static android.net.netlink.StructNdMsg.NUD_REACHABLE; import static android.net.netlink.StructNdMsg.NUD_STALE; import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH; import static org.junit.Assert.assertEquals; Loading @@ -41,6 +46,7 @@ import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; Loading @@ -52,6 +58,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.net.INetd; import android.net.InetAddresses; import android.net.InterfaceConfigurationParcel; import android.net.IpPrefix; import android.net.LinkAddress; Loading @@ -61,6 +68,8 @@ import android.net.RouteInfo; import android.net.dhcp.DhcpServingParamsParcel; import android.net.dhcp.IDhcpServer; import android.net.dhcp.IDhcpServerCallbacks; import android.net.ip.IpNeighborMonitor.NeighborEvent; import android.net.ip.IpNeighborMonitor.NeighborEventConsumer; import android.net.util.InterfaceParams; import android.net.util.InterfaceSet; import android.net.util.SharedLog; Loading @@ -81,6 +90,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.net.Inet4Address; import java.net.InetAddress; @RunWith(AndroidJUnit4.class) @SmallTest Loading @@ -88,6 +98,8 @@ public class IpServerTest { private static final String IFACE_NAME = "testnet1"; private static final String UPSTREAM_IFACE = "upstream0"; private static final String UPSTREAM_IFACE2 = "upstream1"; private static final int UPSTREAM_IFINDEX = 101; private static final int UPSTREAM_IFINDEX2 = 102; private static final String BLUETOOTH_IFACE_ADDR = "192.168.42.1"; private static final int BLUETOOTH_DHCP_PREFIX_LENGTH = 24; private static final int DHCP_LEASE_TIME_SECS = 3600; Loading @@ -102,6 +114,7 @@ public class IpServerTest { @Mock private SharedLog mSharedLog; @Mock private IDhcpServer mDhcpServer; @Mock private RouterAdvertisementDaemon mRaDaemon; @Mock private IpNeighborMonitor mIpNeighborMonitor; @Mock private IpServer.Dependencies mDependencies; @Captor private ArgumentCaptor<DhcpServingParamsParcel> mDhcpParamsCaptor; Loading @@ -111,6 +124,7 @@ public class IpServerTest { ArgumentCaptor.forClass(LinkProperties.class); private IpServer mIpServer; private InterfaceConfigurationParcel mInterfaceConfiguration; private NeighborEventConsumer mNeighborEventConsumer; private void initStateMachine(int interfaceType) throws Exception { initStateMachine(interfaceType, false /* usingLegacyDhcp */); Loading @@ -130,16 +144,28 @@ public class IpServerTest { }).when(mDependencies).makeDhcpServer(any(), mDhcpParamsCaptor.capture(), any()); when(mDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon); when(mDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS); when(mDependencies.getIfindex(eq(UPSTREAM_IFACE))).thenReturn(UPSTREAM_IFINDEX); when(mDependencies.getIfindex(eq(UPSTREAM_IFACE2))).thenReturn(UPSTREAM_IFINDEX2); mInterfaceConfiguration = new InterfaceConfigurationParcel(); mInterfaceConfiguration.flags = new String[0]; if (interfaceType == TETHERING_BLUETOOTH) { mInterfaceConfiguration.ipv4Addr = BLUETOOTH_IFACE_ADDR; mInterfaceConfiguration.prefixLength = BLUETOOTH_DHCP_PREFIX_LENGTH; } ArgumentCaptor<NeighborEventConsumer> neighborCaptor = ArgumentCaptor.forClass(NeighborEventConsumer.class); doReturn(mIpNeighborMonitor).when(mDependencies).getIpNeighborMonitor(any(), any(), neighborCaptor.capture()); mIpServer = new IpServer( IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd, mCallback, usingLegacyDhcp, mDependencies); mIpServer.start(); mNeighborEventConsumer = neighborCaptor.getValue(); // Starting the state machine always puts us in a consistent state and notifies // the rest of the world that we've changed from an unknown to available state. mLooper.dispatchAll(); Loading Loading @@ -172,6 +198,8 @@ public class IpServerTest { @Test public void startsOutAvailable() { when(mDependencies.getIpNeighborMonitor(any(), any(), any())) .thenReturn(mIpNeighborMonitor); mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog, mNetd, mCallback, false /* usingLegacyDhcp */, mDependencies); mIpServer.start(); Loading Loading @@ -469,6 +497,89 @@ public class IpServerTest { verify(mDependencies, never()).makeDhcpServer(any(), any(), any()); } private InetAddress addr(String addr) throws Exception { return InetAddresses.parseNumericAddress(addr); } private void recvNewNeigh(int ifindex, InetAddress addr, short nudState, MacAddress mac) { mNeighborEventConsumer.accept(new NeighborEvent(0, RTM_NEWNEIGH, ifindex, addr, nudState, mac)); mLooper.dispatchAll(); } private void recvDelNeigh(int ifindex, InetAddress addr, short nudState, MacAddress mac) { mNeighborEventConsumer.accept(new NeighborEvent(0, RTM_DELNEIGH, ifindex, addr, nudState, mac)); mLooper.dispatchAll(); } @Test public void addRemoveipv6ForwardingRules() throws Exception { initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */); final int myIfindex = TEST_IFACE_PARAMS.index; final int notMyIfindex = myIfindex - 1; final MacAddress myMac = TEST_IFACE_PARAMS.macAddr; final InetAddress neighA = InetAddresses.parseNumericAddress("2001:db8::1"); final InetAddress neighB = InetAddresses.parseNumericAddress("2001:db8::2"); final InetAddress neighLL = InetAddresses.parseNumericAddress("fe80::1"); final InetAddress neighMC = InetAddresses.parseNumericAddress("ff02::1234"); final MacAddress macA = MacAddress.fromString("00:00:00:00:00:0a"); final MacAddress macB = MacAddress.fromString("11:22:33:00:00:0b"); reset(mNetd); // Events on other interfaces are ignored. recvNewNeigh(notMyIfindex, neighA, NUD_REACHABLE, macA); verifyNoMoreInteractions(mNetd); // Events on this interface are received and sent to netd. recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA); verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX), eq(neighA.getAddress()), eq(myMac.toByteArray()), eq(macA.toByteArray())); reset(mNetd); recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB); verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX), eq(neighB.getAddress()), eq(myMac.toByteArray()), eq(macB.toByteArray())); reset(mNetd); // Link-local and multicast neighbors are ignored. recvNewNeigh(notMyIfindex, neighLL, NUD_REACHABLE, macA); verifyNoMoreInteractions(mNetd); recvNewNeigh(notMyIfindex, neighMC, NUD_REACHABLE, macA); verifyNoMoreInteractions(mNetd); // A neighbor that is no longer valid causes the rule to be removed. recvNewNeigh(myIfindex, neighA, NUD_FAILED, macA); verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighA.getAddress())); reset(mNetd); // A neighbor that is deleted causes the rule to be removed. recvDelNeigh(myIfindex, neighB, NUD_STALE, macB); verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighB.getAddress())); reset(mNetd); // Upstream changes result in deleting and re-adding the rules. recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA); recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB); reset(mNetd); InOrder inOrder = inOrder(mNetd); LinkProperties lp = new LinkProperties(); lp.setInterfaceName(UPSTREAM_IFACE2); dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp); inOrder.verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX2), eq(neighA.getAddress()), eq(myMac.toByteArray()), eq(macA.toByteArray())); inOrder.verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighA.getAddress())); inOrder.verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX2), eq(neighB.getAddress()), eq(myMac.toByteArray()), eq(macB.toByteArray())); inOrder.verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighB.getAddress())); } private void assertDhcpStarted(IpPrefix expectedPrefix) throws Exception { verify(mDependencies, times(1)).makeDhcpServer(eq(IFACE_NAME), any(), any()); verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).startWithCallbacks( Loading packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java +7 −0 Original line number Diff line number Diff line Loading @@ -95,6 +95,7 @@ import android.net.TetheringRequestParcel; import android.net.dhcp.DhcpServerCallbacks; import android.net.dhcp.DhcpServingParamsParcel; import android.net.dhcp.IDhcpServer; import android.net.ip.IpNeighborMonitor; import android.net.ip.IpServer; import android.net.ip.RouterAdvertisementDaemon; import android.net.util.InterfaceParams; Loading Loading @@ -173,6 +174,7 @@ public class TetheringTest { @Mock private UpstreamNetworkMonitor mUpstreamNetworkMonitor; @Mock private IPv6TetheringCoordinator mIPv6TetheringCoordinator; @Mock private RouterAdvertisementDaemon mRouterAdvertisementDaemon; @Mock private IpNeighborMonitor mIpNeighborMonitor; @Mock private IDhcpServer mDhcpServer; @Mock private INetd mNetd; @Mock private UserManager mUserManager; Loading Loading @@ -278,6 +280,11 @@ public class TetheringTest { } }).run(); } public IpNeighborMonitor getIpNeighborMonitor(Handler h, SharedLog l, IpNeighborMonitor.NeighborEventConsumer c) { return mIpNeighborMonitor; } } private class MockTetheringConfiguration extends TetheringConfiguration { Loading Loading
packages/Tethering/src/android/net/ip/IpServer.java +142 −1 Original line number Diff line number Diff line Loading @@ -40,12 +40,14 @@ import android.net.dhcp.DhcpServingParamsParcel; import android.net.dhcp.DhcpServingParamsParcelExt; import android.net.dhcp.IDhcpLeaseCallbacks; import android.net.dhcp.IDhcpServer; import android.net.ip.IpNeighborMonitor.NeighborEvent; import android.net.ip.RouterAdvertisementDaemon.RaParams; import android.net.shared.NetdUtils; import android.net.shared.RouteUtils; import android.net.util.InterfaceParams; import android.net.util.InterfaceSet; import android.net.util.SharedLog; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.RemoteException; Loading @@ -59,14 +61,17 @@ import com.android.internal.util.MessageUtils; import com.android.internal.util.State; import com.android.internal.util.StateMachine; import java.io.IOException; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Objects; import java.util.Random; Loading Loading @@ -149,6 +154,12 @@ public class IpServer extends StateMachine { /** Capture IpServer dependencies, for injection. */ public abstract static class Dependencies { /** Create an IpNeighborMonitor to be used by this IpServer */ public IpNeighborMonitor getIpNeighborMonitor(Handler handler, SharedLog log, IpNeighborMonitor.NeighborEventConsumer consumer) { return new IpNeighborMonitor(handler, log, consumer); } /** Create a RouterAdvertisementDaemon instance to be used by IpServer.*/ public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) { return new RouterAdvertisementDaemon(ifParams); Loading @@ -159,6 +170,15 @@ public class IpServer extends StateMachine { return InterfaceParams.getByName(ifName); } /** Get |ifName|'s interface index. */ public int getIfindex(String ifName) { try { return NetworkInterface.getByName(ifName).getIndex(); } catch (IOException | NullPointerException e) { Log.e(TAG, "Can't determine interface index for interface " + ifName); return 0; } } /** Create a DhcpServer instance to be used by IpServer. */ public abstract void makeDhcpServer(String ifName, DhcpServingParamsParcel params, DhcpServerCallbacks cb); Loading @@ -184,6 +204,8 @@ public class IpServer extends StateMachine { public static final int CMD_TETHER_CONNECTION_CHANGED = BASE_IPSERVER + 9; // new IPv6 tethering parameters need to be processed public static final int CMD_IPV6_TETHER_UPDATE = BASE_IPSERVER + 10; // new neighbor cache entry on our interface public static final int CMD_NEIGHBOR_EVENT = BASE_IPSERVER + 11; private final State mInitialState; private final State mLocalHotspotState; Loading Loading @@ -223,6 +245,40 @@ public class IpServer extends StateMachine { @NonNull private List<TetheredClient> mDhcpLeases = Collections.emptyList(); private int mLastIPv6UpstreamIfindex = 0; private class MyNeighborEventConsumer implements IpNeighborMonitor.NeighborEventConsumer { public void accept(NeighborEvent e) { sendMessage(CMD_NEIGHBOR_EVENT, e); } } static class Ipv6ForwardingRule { public final int upstreamIfindex; public final int downstreamIfindex; public final Inet6Address address; public final MacAddress srcMac; public final MacAddress dstMac; Ipv6ForwardingRule(int upstreamIfindex, int downstreamIfIndex, Inet6Address address, MacAddress srcMac, MacAddress dstMac) { this.upstreamIfindex = upstreamIfindex; this.downstreamIfindex = downstreamIfIndex; this.address = address; this.srcMac = srcMac; this.dstMac = dstMac; } public Ipv6ForwardingRule onNewUpstream(int newUpstreamIfindex) { return new Ipv6ForwardingRule(newUpstreamIfindex, downstreamIfindex, address, srcMac, dstMac); } } private final LinkedHashMap<Inet6Address, Ipv6ForwardingRule> mIpv6ForwardingRules = new LinkedHashMap<>(); private final IpNeighborMonitor mIpNeighborMonitor; public IpServer( String ifaceName, Looper looper, int interfaceType, SharedLog log, INetd netd, Callback callback, boolean usingLegacyDhcp, Dependencies deps) { Loading @@ -240,6 +296,12 @@ public class IpServer extends StateMachine { mLastError = TetheringManager.TETHER_ERROR_NO_ERROR; mServingMode = STATE_AVAILABLE; mIpNeighborMonitor = mDeps.getIpNeighborMonitor(getHandler(), mLog, new MyNeighborEventConsumer()); if (!mIpNeighborMonitor.start()) { mLog.e("Failed to create IpNeighborMonitor on " + mIfaceName); } mInitialState = new InitialState(); mLocalHotspotState = new LocalHotspotState(); mTetheredState = new TetheredState(); Loading Loading @@ -607,8 +669,11 @@ public class IpServer extends StateMachine { } RaParams params = null; int upstreamIfindex = 0; if (v6only != null) { final String upstreamIface = v6only.getInterfaceName(); params = new RaParams(); // We advertise an mtu lower by 16, which is the closest multiple of 8 >= 14, // the ethernet header size. This makes kernel ebpf tethering offload happy. Loading @@ -618,7 +683,7 @@ public class IpServer extends StateMachine { params.mtu = v6only.getMtu() - 16; params.hasDefaultRoute = v6only.hasIpv6DefaultRoute(); if (params.hasDefaultRoute) params.hopLimit = getHopLimit(v6only.getInterfaceName()); if (params.hasDefaultRoute) params.hopLimit = getHopLimit(upstreamIface); for (LinkAddress linkAddr : v6only.getLinkAddresses()) { if (linkAddr.getPrefixLength() != RFC7421_PREFIX_LENGTH) continue; Loading @@ -632,12 +697,18 @@ public class IpServer extends StateMachine { params.dnses.add(dnsServer); } } upstreamIfindex = mDeps.getIfindex(upstreamIface); } // If v6only is null, we pass in null to setRaParams(), which handles // deprecation of any existing RA data. setRaParams(params); mLastIPv6LinkProperties = v6only; updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, upstreamIfindex, null); mLastIPv6UpstreamIfindex = upstreamIfindex; } private void configureLocalIPv6Routes( Loading Loading @@ -732,6 +803,73 @@ public class IpServer extends StateMachine { } } private void addIpv6ForwardingRule(Ipv6ForwardingRule rule) { try { mNetd.tetherRuleAddDownstreamIpv6(mInterfaceParams.index, rule.upstreamIfindex, rule.address.getAddress(), mInterfaceParams.macAddr.toByteArray(), rule.dstMac.toByteArray()); mIpv6ForwardingRules.put(rule.address, rule); } catch (RemoteException | ServiceSpecificException e) { Log.e(TAG, "Could not add IPv6 downstream rule: " + e); } } private void removeIpv6ForwardingRule(Ipv6ForwardingRule rule, boolean removeFromMap) { try { mNetd.tetherRuleRemoveDownstreamIpv6(rule.upstreamIfindex, rule.address.getAddress()); if (removeFromMap) { mIpv6ForwardingRules.remove(rule.address); } } catch (RemoteException | ServiceSpecificException e) { Log.e(TAG, "Could not remove IPv6 downstream rule: " + e); } } // Convenience method to replace a rule with the same rule on a new upstream interface. // Allows replacing the rules in one iteration pass without ConcurrentModificationExceptions. // Relies on the fact that rules are in a map indexed by IP address. private void updateIpv6ForwardingRule(Ipv6ForwardingRule rule, int newIfindex) { addIpv6ForwardingRule(rule.onNewUpstream(newIfindex)); removeIpv6ForwardingRule(rule, false /*removeFromMap*/); } // Handles all updates to IPv6 forwarding rules. These can currently change only if the upstream // changes or if a neighbor event is received. private void updateIpv6ForwardingRules(int prevUpstreamIfindex, int upstreamIfindex, NeighborEvent e) { // If the upstream interface has changed, remove all rules and re-add them with the new // upstream interface. if (prevUpstreamIfindex != upstreamIfindex) { for (Ipv6ForwardingRule rule : mIpv6ForwardingRules.values()) { updateIpv6ForwardingRule(rule, upstreamIfindex); } } // If we're here to process a NeighborEvent, do so now. if (e == null) return; if (!(e.ip instanceof Inet6Address) || e.ip.isMulticastAddress() || e.ip.isLoopbackAddress() || e.ip.isLinkLocalAddress()) { return; } Ipv6ForwardingRule rule = new Ipv6ForwardingRule(mLastIPv6UpstreamIfindex, mInterfaceParams.index, (Inet6Address) e.ip, mInterfaceParams.macAddr, e.macAddr); if (e.isValid()) { addIpv6ForwardingRule(rule); } else { removeIpv6ForwardingRule(rule, true /*removeFromMap*/); } } private void handleNeighborEvent(NeighborEvent e) { if (mInterfaceParams != null && mInterfaceParams.index == e.ifindex && mInterfaceParams.hasMacAddress) { updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, mLastIPv6UpstreamIfindex, e); } } private byte getHopLimit(String upstreamIface) { try { int upstreamHopLimit = Integer.parseUnsignedInt( Loading Loading @@ -1019,6 +1157,9 @@ public class IpServer extends StateMachine { } } break; case CMD_NEIGHBOR_EVENT: handleNeighborEvent((NeighborEvent) message.obj); break; default: return false; } Loading
packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java +111 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,11 @@ import static android.net.ip.IpServer.STATE_AVAILABLE; import static android.net.ip.IpServer.STATE_LOCAL_ONLY; import static android.net.ip.IpServer.STATE_TETHERED; import static android.net.ip.IpServer.STATE_UNAVAILABLE; import static android.net.netlink.NetlinkConstants.RTM_DELNEIGH; import static android.net.netlink.NetlinkConstants.RTM_NEWNEIGH; import static android.net.netlink.StructNdMsg.NUD_FAILED; import static android.net.netlink.StructNdMsg.NUD_REACHABLE; import static android.net.netlink.StructNdMsg.NUD_STALE; import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH; import static org.junit.Assert.assertEquals; Loading @@ -41,6 +46,7 @@ import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; Loading @@ -52,6 +58,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.net.INetd; import android.net.InetAddresses; import android.net.InterfaceConfigurationParcel; import android.net.IpPrefix; import android.net.LinkAddress; Loading @@ -61,6 +68,8 @@ import android.net.RouteInfo; import android.net.dhcp.DhcpServingParamsParcel; import android.net.dhcp.IDhcpServer; import android.net.dhcp.IDhcpServerCallbacks; import android.net.ip.IpNeighborMonitor.NeighborEvent; import android.net.ip.IpNeighborMonitor.NeighborEventConsumer; import android.net.util.InterfaceParams; import android.net.util.InterfaceSet; import android.net.util.SharedLog; Loading @@ -81,6 +90,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.net.Inet4Address; import java.net.InetAddress; @RunWith(AndroidJUnit4.class) @SmallTest Loading @@ -88,6 +98,8 @@ public class IpServerTest { private static final String IFACE_NAME = "testnet1"; private static final String UPSTREAM_IFACE = "upstream0"; private static final String UPSTREAM_IFACE2 = "upstream1"; private static final int UPSTREAM_IFINDEX = 101; private static final int UPSTREAM_IFINDEX2 = 102; private static final String BLUETOOTH_IFACE_ADDR = "192.168.42.1"; private static final int BLUETOOTH_DHCP_PREFIX_LENGTH = 24; private static final int DHCP_LEASE_TIME_SECS = 3600; Loading @@ -102,6 +114,7 @@ public class IpServerTest { @Mock private SharedLog mSharedLog; @Mock private IDhcpServer mDhcpServer; @Mock private RouterAdvertisementDaemon mRaDaemon; @Mock private IpNeighborMonitor mIpNeighborMonitor; @Mock private IpServer.Dependencies mDependencies; @Captor private ArgumentCaptor<DhcpServingParamsParcel> mDhcpParamsCaptor; Loading @@ -111,6 +124,7 @@ public class IpServerTest { ArgumentCaptor.forClass(LinkProperties.class); private IpServer mIpServer; private InterfaceConfigurationParcel mInterfaceConfiguration; private NeighborEventConsumer mNeighborEventConsumer; private void initStateMachine(int interfaceType) throws Exception { initStateMachine(interfaceType, false /* usingLegacyDhcp */); Loading @@ -130,16 +144,28 @@ public class IpServerTest { }).when(mDependencies).makeDhcpServer(any(), mDhcpParamsCaptor.capture(), any()); when(mDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon); when(mDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS); when(mDependencies.getIfindex(eq(UPSTREAM_IFACE))).thenReturn(UPSTREAM_IFINDEX); when(mDependencies.getIfindex(eq(UPSTREAM_IFACE2))).thenReturn(UPSTREAM_IFINDEX2); mInterfaceConfiguration = new InterfaceConfigurationParcel(); mInterfaceConfiguration.flags = new String[0]; if (interfaceType == TETHERING_BLUETOOTH) { mInterfaceConfiguration.ipv4Addr = BLUETOOTH_IFACE_ADDR; mInterfaceConfiguration.prefixLength = BLUETOOTH_DHCP_PREFIX_LENGTH; } ArgumentCaptor<NeighborEventConsumer> neighborCaptor = ArgumentCaptor.forClass(NeighborEventConsumer.class); doReturn(mIpNeighborMonitor).when(mDependencies).getIpNeighborMonitor(any(), any(), neighborCaptor.capture()); mIpServer = new IpServer( IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd, mCallback, usingLegacyDhcp, mDependencies); mIpServer.start(); mNeighborEventConsumer = neighborCaptor.getValue(); // Starting the state machine always puts us in a consistent state and notifies // the rest of the world that we've changed from an unknown to available state. mLooper.dispatchAll(); Loading Loading @@ -172,6 +198,8 @@ public class IpServerTest { @Test public void startsOutAvailable() { when(mDependencies.getIpNeighborMonitor(any(), any(), any())) .thenReturn(mIpNeighborMonitor); mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog, mNetd, mCallback, false /* usingLegacyDhcp */, mDependencies); mIpServer.start(); Loading Loading @@ -469,6 +497,89 @@ public class IpServerTest { verify(mDependencies, never()).makeDhcpServer(any(), any(), any()); } private InetAddress addr(String addr) throws Exception { return InetAddresses.parseNumericAddress(addr); } private void recvNewNeigh(int ifindex, InetAddress addr, short nudState, MacAddress mac) { mNeighborEventConsumer.accept(new NeighborEvent(0, RTM_NEWNEIGH, ifindex, addr, nudState, mac)); mLooper.dispatchAll(); } private void recvDelNeigh(int ifindex, InetAddress addr, short nudState, MacAddress mac) { mNeighborEventConsumer.accept(new NeighborEvent(0, RTM_DELNEIGH, ifindex, addr, nudState, mac)); mLooper.dispatchAll(); } @Test public void addRemoveipv6ForwardingRules() throws Exception { initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */); final int myIfindex = TEST_IFACE_PARAMS.index; final int notMyIfindex = myIfindex - 1; final MacAddress myMac = TEST_IFACE_PARAMS.macAddr; final InetAddress neighA = InetAddresses.parseNumericAddress("2001:db8::1"); final InetAddress neighB = InetAddresses.parseNumericAddress("2001:db8::2"); final InetAddress neighLL = InetAddresses.parseNumericAddress("fe80::1"); final InetAddress neighMC = InetAddresses.parseNumericAddress("ff02::1234"); final MacAddress macA = MacAddress.fromString("00:00:00:00:00:0a"); final MacAddress macB = MacAddress.fromString("11:22:33:00:00:0b"); reset(mNetd); // Events on other interfaces are ignored. recvNewNeigh(notMyIfindex, neighA, NUD_REACHABLE, macA); verifyNoMoreInteractions(mNetd); // Events on this interface are received and sent to netd. recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA); verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX), eq(neighA.getAddress()), eq(myMac.toByteArray()), eq(macA.toByteArray())); reset(mNetd); recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB); verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX), eq(neighB.getAddress()), eq(myMac.toByteArray()), eq(macB.toByteArray())); reset(mNetd); // Link-local and multicast neighbors are ignored. recvNewNeigh(notMyIfindex, neighLL, NUD_REACHABLE, macA); verifyNoMoreInteractions(mNetd); recvNewNeigh(notMyIfindex, neighMC, NUD_REACHABLE, macA); verifyNoMoreInteractions(mNetd); // A neighbor that is no longer valid causes the rule to be removed. recvNewNeigh(myIfindex, neighA, NUD_FAILED, macA); verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighA.getAddress())); reset(mNetd); // A neighbor that is deleted causes the rule to be removed. recvDelNeigh(myIfindex, neighB, NUD_STALE, macB); verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighB.getAddress())); reset(mNetd); // Upstream changes result in deleting and re-adding the rules. recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA); recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB); reset(mNetd); InOrder inOrder = inOrder(mNetd); LinkProperties lp = new LinkProperties(); lp.setInterfaceName(UPSTREAM_IFACE2); dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp); inOrder.verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX2), eq(neighA.getAddress()), eq(myMac.toByteArray()), eq(macA.toByteArray())); inOrder.verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighA.getAddress())); inOrder.verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX2), eq(neighB.getAddress()), eq(myMac.toByteArray()), eq(macB.toByteArray())); inOrder.verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighB.getAddress())); } private void assertDhcpStarted(IpPrefix expectedPrefix) throws Exception { verify(mDependencies, times(1)).makeDhcpServer(eq(IFACE_NAME), any(), any()); verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).startWithCallbacks( Loading
packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java +7 −0 Original line number Diff line number Diff line Loading @@ -95,6 +95,7 @@ import android.net.TetheringRequestParcel; import android.net.dhcp.DhcpServerCallbacks; import android.net.dhcp.DhcpServingParamsParcel; import android.net.dhcp.IDhcpServer; import android.net.ip.IpNeighborMonitor; import android.net.ip.IpServer; import android.net.ip.RouterAdvertisementDaemon; import android.net.util.InterfaceParams; Loading Loading @@ -173,6 +174,7 @@ public class TetheringTest { @Mock private UpstreamNetworkMonitor mUpstreamNetworkMonitor; @Mock private IPv6TetheringCoordinator mIPv6TetheringCoordinator; @Mock private RouterAdvertisementDaemon mRouterAdvertisementDaemon; @Mock private IpNeighborMonitor mIpNeighborMonitor; @Mock private IDhcpServer mDhcpServer; @Mock private INetd mNetd; @Mock private UserManager mUserManager; Loading Loading @@ -278,6 +280,11 @@ public class TetheringTest { } }).run(); } public IpNeighborMonitor getIpNeighborMonitor(Handler h, SharedLog l, IpNeighborMonitor.NeighborEventConsumer c) { return mIpNeighborMonitor; } } private class MockTetheringConfiguration extends TetheringConfiguration { Loading