Loading src/android/net/dhcp/DhcpLeaseRepository.java +20 −2 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ import android.util.ArrayMap; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import java.net.Inet4Address; import java.util.ArrayList; Loading Loading @@ -158,7 +159,7 @@ class DhcpLeaseRepository { /** * From a map keyed by {@link Inet4Address}, remove entries where the key is invalid (as * specified by {@link #isValidAddress(Inet4Address)}), or is a reserved address. * @return true iff at least one entry was removed. * @return true if and only if at least one entry was removed. */ private <T> boolean cleanMap(Map<Inet4Address, T> map) { final Iterator<Entry<Inet4Address, T>> it = map.entrySet().iterator(); Loading Loading @@ -397,7 +398,8 @@ class DhcpLeaseRepository { mEventCallbacks.finishBroadcast(); } public void markLeaseDeclined(@NonNull Inet4Address addr) { @VisibleForTesting void markLeaseDeclined(@NonNull Inet4Address addr) { if (mDeclinedAddrs.containsKey(addr) || !isValidAddress(addr)) { mLog.logf("Not marking %s as declined: already declined or not assignable", inet4AddrToString(addr)); Loading @@ -409,6 +411,22 @@ class DhcpLeaseRepository { maybeUpdateEarliestExpiration(expTime); } /** * Mark a committed lease matching the passed in clientId and hardware address parameters to be * declined, and delete it from the repository. * * @param clientId Client identifier option if specified, or {@link #CLIENTID_UNSPEC} * @param hwAddr client's mac address * @param Addr IPv4 address to be declined * @return true if a lease matching parameters was removed from committed repository. */ public boolean markAndReleaseDeclinedLease(@Nullable byte[] clientId, @NonNull MacAddress hwAddr, @NonNull Inet4Address addr) { if (!releaseLease(clientId, hwAddr, addr)) return false; markLeaseDeclined(addr); return true; } /** * Get the list of currently valid committed leases in the repository. */ Loading src/android/net/dhcp/DhcpServer.java +158 −83 Original line number Diff line number Diff line Loading @@ -45,6 +45,7 @@ import static java.lang.Integer.toUnsignedLong; import android.content.Context; import android.net.INetworkStackStatusCallback; import android.net.IpPrefix; import android.net.MacAddress; import android.net.TrafficStats; import android.net.util.NetworkStackUtils; Loading Loading @@ -96,7 +97,8 @@ public class DhcpServer extends StateMachine { private static final int CMD_START_DHCP_SERVER = 1; private static final int CMD_STOP_DHCP_SERVER = 2; private static final int CMD_UPDATE_PARAMS = 3; private static final int CMD_SUSPEND_DHCP_SERVER = 4; @VisibleForTesting protected static final int CMD_RECEIVE_PACKET = 4; @NonNull private final Context mContext; Loading Loading @@ -126,6 +128,8 @@ public class DhcpServer extends StateMachine { private final StoppedState mStoppedState = new StoppedState(); private final StartedState mStartedState = new StartedState(); private final RunningState mRunningState = new RunningState(); private final WaitBeforeRetrievalState mWaitBeforeRetrievalState = new WaitBeforeRetrievalState(); /** * Clock to be used by DhcpServer to track time for lease expiration. Loading Loading @@ -262,6 +266,7 @@ public class DhcpServer extends StateMachine { addState(mStoppedState); addState(mStartedState); addState(mRunningState, mStartedState); addState(mWaitBeforeRetrievalState, mStartedState); // CHECKSTYLE:ON IndentationCheck setInitialState(mStoppedState); Loading Loading @@ -372,6 +377,17 @@ public class DhcpServer extends StateMachine { } } private void handleUpdateServingParams(@NonNull DhcpServingParams params, @Nullable INetworkStackStatusCallback cb) { mServingParams = params; mLeaseRepo.updateParams( DhcpServingParams.makeIpPrefix(params.serverAddr), params.excludedAddrs, params.dhcpLeaseTimeSecs * 1000, params.singleClientAddr); maybeNotifyStatus(cb, STATUS_SUCCESS); } class StoppedState extends State { private INetworkStackStatusCallback mOnStopCallback; Loading Loading @@ -422,6 +438,8 @@ public class DhcpServer extends StateMachine { mLeaseRepo.addLeaseCallbacks(mEventCallbacks); } maybeNotifyStatus(mOnStartCallback, STATUS_SUCCESS); // Clear INetworkStackStatusCallback binder token, so that it's freed // on the other side. mOnStartCallback = null; } Loading @@ -431,14 +449,7 @@ public class DhcpServer extends StateMachine { case CMD_UPDATE_PARAMS: final Pair<DhcpServingParams, INetworkStackStatusCallback> pair = (Pair<DhcpServingParams, INetworkStackStatusCallback>) msg.obj; final DhcpServingParams params = pair.first; mServingParams = params; mLeaseRepo.updateParams( DhcpServingParams.makeIpPrefix(params.serverAddr), params.excludedAddrs, params.dhcpLeaseTimeSecs * 1000, params.singleClientAddr); maybeNotifyStatus(pair.second, STATUS_SUCCESS); handleUpdateServingParams(pair.first, pair.second); return HANDLED; case CMD_START_DHCP_SERVER: Loading Loading @@ -466,26 +477,19 @@ public class DhcpServer extends StateMachine { @Override public boolean processMessage(Message msg) { switch (msg.what) { case CMD_SUSPEND_DHCP_SERVER: // TODO: transition to the state which waits for IpServer to reconfigure the // new selected prefix. case CMD_RECEIVE_PACKET: processPacket((DhcpPacket) msg.obj); return HANDLED; default: // Fall through to StartedState. return NOT_HANDLED; } } } @VisibleForTesting void processPacket(@NonNull DhcpPacket packet, int srcPort) { final String packetType = packet.getClass().getSimpleName(); if (srcPort != DHCP_CLIENT) { mLog.logf("Ignored packet of type %s sent from client port %d", packetType, srcPort); return; } private void processPacket(@NonNull DhcpPacket packet) { mLog.log("Received packet of type " + packet.getClass().getSimpleName()); mLog.log("Received packet of type " + packetType); final Inet4Address sid = packet.mServerIdentifier; if (sid != null && !sid.equals(mServingParams.serverAddr.getAddress())) { mLog.log("Packet ignored due to wrong server identifier: " + sid); Loading @@ -499,6 +503,8 @@ public class DhcpServer extends StateMachine { processRequest((DhcpRequestPacket) packet); } else if (packet instanceof DhcpReleasePacket) { processRelease((DhcpReleasePacket) packet); } else if (packet instanceof DhcpDeclinePacket) { processDecline((DhcpDeclinePacket) packet); } else { mLog.e("Unknown packet type: " + packet.getClass().getSimpleName()); } Loading @@ -519,8 +525,8 @@ public class DhcpServer extends StateMachine { final MacAddress clientMac = getMacAddr(packet); try { if (mDhcpRapidCommitEnabled && packet.mRapidCommit) { lease = mLeaseRepo.getCommittedLease(packet.getExplicitClientIdOrNull(), clientMac, packet.mRelayIp, packet.mHostName); lease = mLeaseRepo.getCommittedLease(packet.getExplicitClientIdOrNull(), clientMac, packet.mRelayIp, packet.mHostName); transmitAck(packet, lease, clientMac); } else { lease = mLeaseRepo.getOffer(packet.getExplicitClientIdOrNull(), clientMac, Loading @@ -534,7 +540,8 @@ public class DhcpServer extends StateMachine { } } private void processRequest(@NonNull DhcpRequestPacket packet) throws MalformedPacketException { private void processRequest(@NonNull DhcpRequestPacket packet) throws MalformedPacketException { // If set, packet SID matches with this server's ID as checked in processPacket(). final boolean sidSet = packet.mServerIdentifier != null; final DhcpLease lease; Loading @@ -558,10 +565,72 @@ public class DhcpServer extends StateMachine { throws MalformedPacketException { final byte[] clientId = packet.getExplicitClientIdOrNull(); final MacAddress macAddr = getMacAddr(packet); // Don't care about success (there is no ACK/NAK); logging is already done in the repository // Don't care about success (there is no ACK/NAK); logging is already done // in the repository. mLeaseRepo.releaseLease(clientId, macAddr, packet.mClientIp); } private void processDecline(@NonNull DhcpDeclinePacket packet) throws MalformedPacketException { final byte[] clientId = packet.getExplicitClientIdOrNull(); final MacAddress macAddr = getMacAddr(packet); int committedLeasesCount = mLeaseRepo.getCommittedLeases().size(); // If peer's clientID and macAddr doesn't match with any issued lease, nothing to do. if (!mLeaseRepo.markAndReleaseDeclinedLease(clientId, macAddr, packet.mRequestedIp)) { return; } // Check whether the boolean flag which requests a new prefix is enabled, and if // it's enabled, make sure the issued lease count should be only one, otherwise, // changing a different prefix will cause other exist host(s) configured with the // current prefix lose appropriate route. if (!mServingParams.changePrefixOnDecline || committedLeasesCount > 1) return; if (mEventCallbacks == null) { mLog.e("changePrefixOnDecline enabled but caller didn't pass a valid" + "IDhcpEventCallbacks callback."); return; } try { mEventCallbacks.onNewPrefixRequest( DhcpServingParams.makeIpPrefix(mServingParams.serverAddr)); transitionTo(mWaitBeforeRetrievalState); } catch (RemoteException e) { mLog.e("could not request a new prefix to caller", e); } } } class WaitBeforeRetrievalState extends State { @Override public boolean processMessage(Message msg) { switch (msg.what) { case CMD_UPDATE_PARAMS: final Pair<DhcpServingParams, INetworkStackStatusCallback> pair = (Pair<DhcpServingParams, INetworkStackStatusCallback>) msg.obj; final IpPrefix currentPrefix = DhcpServingParams.makeIpPrefix(mServingParams.serverAddr); final IpPrefix newPrefix = DhcpServingParams.makeIpPrefix(pair.first.serverAddr); handleUpdateServingParams(pair.first, pair.second); if (currentPrefix != null && !currentPrefix.equals(newPrefix)) { transitionTo(mRunningState); } return HANDLED; case CMD_RECEIVE_PACKET: deferMessage(msg); return HANDLED; default: // Fall through to StartedState. return NOT_HANDLED; } } } private Inet4Address getAckOrOfferDst(@NonNull DhcpPacket request, @NonNull DhcpLease lease, boolean broadcastFlag) { // Unless relayed or broadcast, send to client IP if already configured on the client, or to Loading Loading @@ -748,7 +817,13 @@ public class DhcpServer extends StateMachine { @Override protected void onReceive(@NonNull DhcpPacket packet, @NonNull Inet4Address srcAddr, int srcPort) { processPacket(packet, srcPort); if (srcPort != DHCP_CLIENT) { final String packetType = packet.getClass().getSimpleName(); mLog.logf("Ignored packet of type %s sent from client port %d", packetType, srcPort); return; } sendMessage(CMD_RECEIVE_PACKET, packet); } @Override Loading src/android/net/ip/IpClient.java +9 −1 Original line number Diff line number Diff line Loading @@ -457,7 +457,7 @@ public class IpClient extends StateMachine { private final SharedLog mLog; private final LocalLog mConnectivityPacketLog; private final MessageHandlingLogger mMsgStateLogger; private final IpConnectivityLog mMetricsLog = new IpConnectivityLog(); private final IpConnectivityLog mMetricsLog; private final InterfaceController mInterfaceCtrl; // Ignore nonzero RDNSS option lifetimes below this value. 0 = disabled. Loading Loading @@ -536,6 +536,13 @@ public class IpClient extends StateMachine { return NetworkStackUtils.getDeviceConfigPropertyInt(NAMESPACE_CONNECTIVITY, name, defaultValue); } /** * Get a IpConnectivityLog instance. */ public IpConnectivityLog getIpConnectivityLog() { return new IpConnectivityLog(); } } public IpClient(Context context, String ifName, IIpClientCallbacks callback, Loading @@ -557,6 +564,7 @@ public class IpClient extends StateMachine { mInterfaceName = ifName; mClatInterfaceName = CLAT_PREFIX + ifName; mDependencies = deps; mMetricsLog = deps.getIpConnectivityLog(); mShutdownLatch = new CountDownLatch(1); mCm = mContext.getSystemService(ConnectivityManager.class); mObserverRegistry = observerRegistry; Loading tests/integration/src/android/net/netlink/InetDiagSocketIntegrationTest.java 0 → 100644 +223 −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.netlink; import static android.system.OsConstants.AF_INET; import static android.system.OsConstants.AF_INET6; import static android.system.OsConstants.IPPROTO_TCP; import static android.system.OsConstants.IPPROTO_UDP; import static android.system.OsConstants.SOCK_DGRAM; import static android.system.OsConstants.SOCK_STREAM; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assume.assumeTrue; import android.app.Instrumentation; import android.content.Context; import android.net.ConnectivityManager; import android.os.Process; import android.system.Os; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.networkstack.apishim.common.ShimUtils; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.io.FileDescriptor; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; @RunWith(AndroidJUnit4.class) @SmallTest public class InetDiagSocketIntegrationTest { private ConnectivityManager mCm; private Context mContext; @Before public void setUp() throws Exception { Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); mContext = instrumentation.getTargetContext(); mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); } private class Connection { public int socketDomain; public int socketType; public InetAddress localAddress; public InetAddress remoteAddress; public InetAddress localhostAddress; public InetSocketAddress local; public InetSocketAddress remote; public int protocol; public FileDescriptor localFd; public FileDescriptor remoteFd; public FileDescriptor createSocket() throws Exception { return Os.socket(socketDomain, socketType, protocol); } Connection(String to, String from) throws Exception { remoteAddress = InetAddress.getByName(to); if (from != null) { localAddress = InetAddress.getByName(from); } else { localAddress = (remoteAddress instanceof Inet4Address) ? Inet4Address.getByName("localhost") : Inet6Address.getByName("::"); } if ((localAddress instanceof Inet4Address) && (remoteAddress instanceof Inet4Address)) { socketDomain = AF_INET; localhostAddress = Inet4Address.getByName("localhost"); } else { socketDomain = AF_INET6; localhostAddress = Inet6Address.getByName("::"); } } public void close() throws Exception { Os.close(localFd); } } private class TcpConnection extends Connection { TcpConnection(String to, String from) throws Exception { super(to, from); protocol = IPPROTO_TCP; socketType = SOCK_STREAM; remoteFd = createSocket(); Os.bind(remoteFd, remoteAddress, 0); Os.listen(remoteFd, 10); int remotePort = ((InetSocketAddress) Os.getsockname(remoteFd)).getPort(); localFd = createSocket(); Os.bind(localFd, localAddress, 0); Os.connect(localFd, remoteAddress, remotePort); local = (InetSocketAddress) Os.getsockname(localFd); remote = (InetSocketAddress) Os.getpeername(localFd); } public void close() throws Exception { super.close(); Os.close(remoteFd); } } private class UdpConnection extends Connection { UdpConnection(String to, String from) throws Exception { super(to, from); protocol = IPPROTO_UDP; socketType = SOCK_DGRAM; remoteFd = null; localFd = createSocket(); Os.bind(localFd, localAddress, 0); Os.connect(localFd, remoteAddress, 7); local = (InetSocketAddress) Os.getsockname(localFd); remote = new InetSocketAddress(remoteAddress, 7); } } private void checkConnectionOwnerUid(int protocol, InetSocketAddress local, InetSocketAddress remote, boolean expectSuccess) { final int uid = mCm.getConnectionOwnerUid(protocol, local, remote); if (expectSuccess) { assertEquals(Process.myUid(), uid); } else { assertNotEquals(Process.myUid(), uid); } } private int findLikelyFreeUdpPort(UdpConnection conn) throws Exception { UdpConnection udp = new UdpConnection(conn.remoteAddress.getHostAddress(), conn.localAddress.getHostAddress()); final int localPort = udp.local.getPort(); udp.close(); return localPort; } /** * Create a test connection for UDP and TCP sockets and verify that this * {protocol, local, remote} socket result in receiving a valid UID. */ public void checkGetConnectionOwnerUid(String to, String from) throws Exception { TcpConnection tcp = new TcpConnection(to, from); checkConnectionOwnerUid(tcp.protocol, tcp.local, tcp.remote, true); checkConnectionOwnerUid(IPPROTO_UDP, tcp.local, tcp.remote, false); checkConnectionOwnerUid(tcp.protocol, new InetSocketAddress(0), tcp.remote, false); checkConnectionOwnerUid(tcp.protocol, tcp.local, new InetSocketAddress(0), false); tcp.close(); UdpConnection udp = new UdpConnection(to, from); checkConnectionOwnerUid(udp.protocol, udp.local, udp.remote, true); checkConnectionOwnerUid(IPPROTO_TCP, udp.local, udp.remote, false); checkConnectionOwnerUid(udp.protocol, new InetSocketAddress(findLikelyFreeUdpPort(udp)), udp.remote, false); udp.close(); } @Test public void testGetConnectionOwnerUid() throws Exception { // Skip the test for API <= Q, as b/141603906 this was only fixed in Q-QPR2 assumeTrue(ShimUtils.isAtLeastR()); checkGetConnectionOwnerUid("::", null); checkGetConnectionOwnerUid("::", "::"); checkGetConnectionOwnerUid("0.0.0.0", null); checkGetConnectionOwnerUid("0.0.0.0", "0.0.0.0"); checkGetConnectionOwnerUid("127.0.0.1", null); checkGetConnectionOwnerUid("127.0.0.1", "127.0.0.2"); checkGetConnectionOwnerUid("::1", null); checkGetConnectionOwnerUid("::1", "::1"); } /* Verify fix for b/141603906 */ @Test public void testB141603906() throws Exception { // Skip the test for API <= Q, as b/141603906 this was only fixed in Q-QPR2 assumeTrue(ShimUtils.isAtLeastR()); final InetSocketAddress src = new InetSocketAddress(0); final InetSocketAddress dst = new InetSocketAddress(0); final int numThreads = 8; final int numSockets = 5000; final Thread[] threads = new Thread[numThreads]; for (int i = 0; i < numThreads; i++) { threads[i] = new Thread(() -> { for (int j = 0; j < numSockets; j++) { mCm.getConnectionOwnerUid(IPPROTO_TCP, src, dst); } }); } for (Thread thread : threads) { thread.start(); } for (Thread thread : threads) { thread.join(); } } } tests/unit/Android.bp +1 −1 Original line number Diff line number Diff line Loading @@ -16,7 +16,7 @@ java_defaults { name: "NetworkStackTestsDefaults", certificate: "platform", platform_apis: true, srcs: ["src/**/*.java", "src/**/*.kt"], resource_dirs: ["res"], static_libs: [ Loading Loading
src/android/net/dhcp/DhcpLeaseRepository.java +20 −2 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ import android.util.ArrayMap; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import java.net.Inet4Address; import java.util.ArrayList; Loading Loading @@ -158,7 +159,7 @@ class DhcpLeaseRepository { /** * From a map keyed by {@link Inet4Address}, remove entries where the key is invalid (as * specified by {@link #isValidAddress(Inet4Address)}), or is a reserved address. * @return true iff at least one entry was removed. * @return true if and only if at least one entry was removed. */ private <T> boolean cleanMap(Map<Inet4Address, T> map) { final Iterator<Entry<Inet4Address, T>> it = map.entrySet().iterator(); Loading Loading @@ -397,7 +398,8 @@ class DhcpLeaseRepository { mEventCallbacks.finishBroadcast(); } public void markLeaseDeclined(@NonNull Inet4Address addr) { @VisibleForTesting void markLeaseDeclined(@NonNull Inet4Address addr) { if (mDeclinedAddrs.containsKey(addr) || !isValidAddress(addr)) { mLog.logf("Not marking %s as declined: already declined or not assignable", inet4AddrToString(addr)); Loading @@ -409,6 +411,22 @@ class DhcpLeaseRepository { maybeUpdateEarliestExpiration(expTime); } /** * Mark a committed lease matching the passed in clientId and hardware address parameters to be * declined, and delete it from the repository. * * @param clientId Client identifier option if specified, or {@link #CLIENTID_UNSPEC} * @param hwAddr client's mac address * @param Addr IPv4 address to be declined * @return true if a lease matching parameters was removed from committed repository. */ public boolean markAndReleaseDeclinedLease(@Nullable byte[] clientId, @NonNull MacAddress hwAddr, @NonNull Inet4Address addr) { if (!releaseLease(clientId, hwAddr, addr)) return false; markLeaseDeclined(addr); return true; } /** * Get the list of currently valid committed leases in the repository. */ Loading
src/android/net/dhcp/DhcpServer.java +158 −83 Original line number Diff line number Diff line Loading @@ -45,6 +45,7 @@ import static java.lang.Integer.toUnsignedLong; import android.content.Context; import android.net.INetworkStackStatusCallback; import android.net.IpPrefix; import android.net.MacAddress; import android.net.TrafficStats; import android.net.util.NetworkStackUtils; Loading Loading @@ -96,7 +97,8 @@ public class DhcpServer extends StateMachine { private static final int CMD_START_DHCP_SERVER = 1; private static final int CMD_STOP_DHCP_SERVER = 2; private static final int CMD_UPDATE_PARAMS = 3; private static final int CMD_SUSPEND_DHCP_SERVER = 4; @VisibleForTesting protected static final int CMD_RECEIVE_PACKET = 4; @NonNull private final Context mContext; Loading Loading @@ -126,6 +128,8 @@ public class DhcpServer extends StateMachine { private final StoppedState mStoppedState = new StoppedState(); private final StartedState mStartedState = new StartedState(); private final RunningState mRunningState = new RunningState(); private final WaitBeforeRetrievalState mWaitBeforeRetrievalState = new WaitBeforeRetrievalState(); /** * Clock to be used by DhcpServer to track time for lease expiration. Loading Loading @@ -262,6 +266,7 @@ public class DhcpServer extends StateMachine { addState(mStoppedState); addState(mStartedState); addState(mRunningState, mStartedState); addState(mWaitBeforeRetrievalState, mStartedState); // CHECKSTYLE:ON IndentationCheck setInitialState(mStoppedState); Loading Loading @@ -372,6 +377,17 @@ public class DhcpServer extends StateMachine { } } private void handleUpdateServingParams(@NonNull DhcpServingParams params, @Nullable INetworkStackStatusCallback cb) { mServingParams = params; mLeaseRepo.updateParams( DhcpServingParams.makeIpPrefix(params.serverAddr), params.excludedAddrs, params.dhcpLeaseTimeSecs * 1000, params.singleClientAddr); maybeNotifyStatus(cb, STATUS_SUCCESS); } class StoppedState extends State { private INetworkStackStatusCallback mOnStopCallback; Loading Loading @@ -422,6 +438,8 @@ public class DhcpServer extends StateMachine { mLeaseRepo.addLeaseCallbacks(mEventCallbacks); } maybeNotifyStatus(mOnStartCallback, STATUS_SUCCESS); // Clear INetworkStackStatusCallback binder token, so that it's freed // on the other side. mOnStartCallback = null; } Loading @@ -431,14 +449,7 @@ public class DhcpServer extends StateMachine { case CMD_UPDATE_PARAMS: final Pair<DhcpServingParams, INetworkStackStatusCallback> pair = (Pair<DhcpServingParams, INetworkStackStatusCallback>) msg.obj; final DhcpServingParams params = pair.first; mServingParams = params; mLeaseRepo.updateParams( DhcpServingParams.makeIpPrefix(params.serverAddr), params.excludedAddrs, params.dhcpLeaseTimeSecs * 1000, params.singleClientAddr); maybeNotifyStatus(pair.second, STATUS_SUCCESS); handleUpdateServingParams(pair.first, pair.second); return HANDLED; case CMD_START_DHCP_SERVER: Loading Loading @@ -466,26 +477,19 @@ public class DhcpServer extends StateMachine { @Override public boolean processMessage(Message msg) { switch (msg.what) { case CMD_SUSPEND_DHCP_SERVER: // TODO: transition to the state which waits for IpServer to reconfigure the // new selected prefix. case CMD_RECEIVE_PACKET: processPacket((DhcpPacket) msg.obj); return HANDLED; default: // Fall through to StartedState. return NOT_HANDLED; } } } @VisibleForTesting void processPacket(@NonNull DhcpPacket packet, int srcPort) { final String packetType = packet.getClass().getSimpleName(); if (srcPort != DHCP_CLIENT) { mLog.logf("Ignored packet of type %s sent from client port %d", packetType, srcPort); return; } private void processPacket(@NonNull DhcpPacket packet) { mLog.log("Received packet of type " + packet.getClass().getSimpleName()); mLog.log("Received packet of type " + packetType); final Inet4Address sid = packet.mServerIdentifier; if (sid != null && !sid.equals(mServingParams.serverAddr.getAddress())) { mLog.log("Packet ignored due to wrong server identifier: " + sid); Loading @@ -499,6 +503,8 @@ public class DhcpServer extends StateMachine { processRequest((DhcpRequestPacket) packet); } else if (packet instanceof DhcpReleasePacket) { processRelease((DhcpReleasePacket) packet); } else if (packet instanceof DhcpDeclinePacket) { processDecline((DhcpDeclinePacket) packet); } else { mLog.e("Unknown packet type: " + packet.getClass().getSimpleName()); } Loading @@ -519,8 +525,8 @@ public class DhcpServer extends StateMachine { final MacAddress clientMac = getMacAddr(packet); try { if (mDhcpRapidCommitEnabled && packet.mRapidCommit) { lease = mLeaseRepo.getCommittedLease(packet.getExplicitClientIdOrNull(), clientMac, packet.mRelayIp, packet.mHostName); lease = mLeaseRepo.getCommittedLease(packet.getExplicitClientIdOrNull(), clientMac, packet.mRelayIp, packet.mHostName); transmitAck(packet, lease, clientMac); } else { lease = mLeaseRepo.getOffer(packet.getExplicitClientIdOrNull(), clientMac, Loading @@ -534,7 +540,8 @@ public class DhcpServer extends StateMachine { } } private void processRequest(@NonNull DhcpRequestPacket packet) throws MalformedPacketException { private void processRequest(@NonNull DhcpRequestPacket packet) throws MalformedPacketException { // If set, packet SID matches with this server's ID as checked in processPacket(). final boolean sidSet = packet.mServerIdentifier != null; final DhcpLease lease; Loading @@ -558,10 +565,72 @@ public class DhcpServer extends StateMachine { throws MalformedPacketException { final byte[] clientId = packet.getExplicitClientIdOrNull(); final MacAddress macAddr = getMacAddr(packet); // Don't care about success (there is no ACK/NAK); logging is already done in the repository // Don't care about success (there is no ACK/NAK); logging is already done // in the repository. mLeaseRepo.releaseLease(clientId, macAddr, packet.mClientIp); } private void processDecline(@NonNull DhcpDeclinePacket packet) throws MalformedPacketException { final byte[] clientId = packet.getExplicitClientIdOrNull(); final MacAddress macAddr = getMacAddr(packet); int committedLeasesCount = mLeaseRepo.getCommittedLeases().size(); // If peer's clientID and macAddr doesn't match with any issued lease, nothing to do. if (!mLeaseRepo.markAndReleaseDeclinedLease(clientId, macAddr, packet.mRequestedIp)) { return; } // Check whether the boolean flag which requests a new prefix is enabled, and if // it's enabled, make sure the issued lease count should be only one, otherwise, // changing a different prefix will cause other exist host(s) configured with the // current prefix lose appropriate route. if (!mServingParams.changePrefixOnDecline || committedLeasesCount > 1) return; if (mEventCallbacks == null) { mLog.e("changePrefixOnDecline enabled but caller didn't pass a valid" + "IDhcpEventCallbacks callback."); return; } try { mEventCallbacks.onNewPrefixRequest( DhcpServingParams.makeIpPrefix(mServingParams.serverAddr)); transitionTo(mWaitBeforeRetrievalState); } catch (RemoteException e) { mLog.e("could not request a new prefix to caller", e); } } } class WaitBeforeRetrievalState extends State { @Override public boolean processMessage(Message msg) { switch (msg.what) { case CMD_UPDATE_PARAMS: final Pair<DhcpServingParams, INetworkStackStatusCallback> pair = (Pair<DhcpServingParams, INetworkStackStatusCallback>) msg.obj; final IpPrefix currentPrefix = DhcpServingParams.makeIpPrefix(mServingParams.serverAddr); final IpPrefix newPrefix = DhcpServingParams.makeIpPrefix(pair.first.serverAddr); handleUpdateServingParams(pair.first, pair.second); if (currentPrefix != null && !currentPrefix.equals(newPrefix)) { transitionTo(mRunningState); } return HANDLED; case CMD_RECEIVE_PACKET: deferMessage(msg); return HANDLED; default: // Fall through to StartedState. return NOT_HANDLED; } } } private Inet4Address getAckOrOfferDst(@NonNull DhcpPacket request, @NonNull DhcpLease lease, boolean broadcastFlag) { // Unless relayed or broadcast, send to client IP if already configured on the client, or to Loading Loading @@ -748,7 +817,13 @@ public class DhcpServer extends StateMachine { @Override protected void onReceive(@NonNull DhcpPacket packet, @NonNull Inet4Address srcAddr, int srcPort) { processPacket(packet, srcPort); if (srcPort != DHCP_CLIENT) { final String packetType = packet.getClass().getSimpleName(); mLog.logf("Ignored packet of type %s sent from client port %d", packetType, srcPort); return; } sendMessage(CMD_RECEIVE_PACKET, packet); } @Override Loading
src/android/net/ip/IpClient.java +9 −1 Original line number Diff line number Diff line Loading @@ -457,7 +457,7 @@ public class IpClient extends StateMachine { private final SharedLog mLog; private final LocalLog mConnectivityPacketLog; private final MessageHandlingLogger mMsgStateLogger; private final IpConnectivityLog mMetricsLog = new IpConnectivityLog(); private final IpConnectivityLog mMetricsLog; private final InterfaceController mInterfaceCtrl; // Ignore nonzero RDNSS option lifetimes below this value. 0 = disabled. Loading Loading @@ -536,6 +536,13 @@ public class IpClient extends StateMachine { return NetworkStackUtils.getDeviceConfigPropertyInt(NAMESPACE_CONNECTIVITY, name, defaultValue); } /** * Get a IpConnectivityLog instance. */ public IpConnectivityLog getIpConnectivityLog() { return new IpConnectivityLog(); } } public IpClient(Context context, String ifName, IIpClientCallbacks callback, Loading @@ -557,6 +564,7 @@ public class IpClient extends StateMachine { mInterfaceName = ifName; mClatInterfaceName = CLAT_PREFIX + ifName; mDependencies = deps; mMetricsLog = deps.getIpConnectivityLog(); mShutdownLatch = new CountDownLatch(1); mCm = mContext.getSystemService(ConnectivityManager.class); mObserverRegistry = observerRegistry; Loading
tests/integration/src/android/net/netlink/InetDiagSocketIntegrationTest.java 0 → 100644 +223 −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.netlink; import static android.system.OsConstants.AF_INET; import static android.system.OsConstants.AF_INET6; import static android.system.OsConstants.IPPROTO_TCP; import static android.system.OsConstants.IPPROTO_UDP; import static android.system.OsConstants.SOCK_DGRAM; import static android.system.OsConstants.SOCK_STREAM; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assume.assumeTrue; import android.app.Instrumentation; import android.content.Context; import android.net.ConnectivityManager; import android.os.Process; import android.system.Os; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.networkstack.apishim.common.ShimUtils; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.io.FileDescriptor; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; @RunWith(AndroidJUnit4.class) @SmallTest public class InetDiagSocketIntegrationTest { private ConnectivityManager mCm; private Context mContext; @Before public void setUp() throws Exception { Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); mContext = instrumentation.getTargetContext(); mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); } private class Connection { public int socketDomain; public int socketType; public InetAddress localAddress; public InetAddress remoteAddress; public InetAddress localhostAddress; public InetSocketAddress local; public InetSocketAddress remote; public int protocol; public FileDescriptor localFd; public FileDescriptor remoteFd; public FileDescriptor createSocket() throws Exception { return Os.socket(socketDomain, socketType, protocol); } Connection(String to, String from) throws Exception { remoteAddress = InetAddress.getByName(to); if (from != null) { localAddress = InetAddress.getByName(from); } else { localAddress = (remoteAddress instanceof Inet4Address) ? Inet4Address.getByName("localhost") : Inet6Address.getByName("::"); } if ((localAddress instanceof Inet4Address) && (remoteAddress instanceof Inet4Address)) { socketDomain = AF_INET; localhostAddress = Inet4Address.getByName("localhost"); } else { socketDomain = AF_INET6; localhostAddress = Inet6Address.getByName("::"); } } public void close() throws Exception { Os.close(localFd); } } private class TcpConnection extends Connection { TcpConnection(String to, String from) throws Exception { super(to, from); protocol = IPPROTO_TCP; socketType = SOCK_STREAM; remoteFd = createSocket(); Os.bind(remoteFd, remoteAddress, 0); Os.listen(remoteFd, 10); int remotePort = ((InetSocketAddress) Os.getsockname(remoteFd)).getPort(); localFd = createSocket(); Os.bind(localFd, localAddress, 0); Os.connect(localFd, remoteAddress, remotePort); local = (InetSocketAddress) Os.getsockname(localFd); remote = (InetSocketAddress) Os.getpeername(localFd); } public void close() throws Exception { super.close(); Os.close(remoteFd); } } private class UdpConnection extends Connection { UdpConnection(String to, String from) throws Exception { super(to, from); protocol = IPPROTO_UDP; socketType = SOCK_DGRAM; remoteFd = null; localFd = createSocket(); Os.bind(localFd, localAddress, 0); Os.connect(localFd, remoteAddress, 7); local = (InetSocketAddress) Os.getsockname(localFd); remote = new InetSocketAddress(remoteAddress, 7); } } private void checkConnectionOwnerUid(int protocol, InetSocketAddress local, InetSocketAddress remote, boolean expectSuccess) { final int uid = mCm.getConnectionOwnerUid(protocol, local, remote); if (expectSuccess) { assertEquals(Process.myUid(), uid); } else { assertNotEquals(Process.myUid(), uid); } } private int findLikelyFreeUdpPort(UdpConnection conn) throws Exception { UdpConnection udp = new UdpConnection(conn.remoteAddress.getHostAddress(), conn.localAddress.getHostAddress()); final int localPort = udp.local.getPort(); udp.close(); return localPort; } /** * Create a test connection for UDP and TCP sockets and verify that this * {protocol, local, remote} socket result in receiving a valid UID. */ public void checkGetConnectionOwnerUid(String to, String from) throws Exception { TcpConnection tcp = new TcpConnection(to, from); checkConnectionOwnerUid(tcp.protocol, tcp.local, tcp.remote, true); checkConnectionOwnerUid(IPPROTO_UDP, tcp.local, tcp.remote, false); checkConnectionOwnerUid(tcp.protocol, new InetSocketAddress(0), tcp.remote, false); checkConnectionOwnerUid(tcp.protocol, tcp.local, new InetSocketAddress(0), false); tcp.close(); UdpConnection udp = new UdpConnection(to, from); checkConnectionOwnerUid(udp.protocol, udp.local, udp.remote, true); checkConnectionOwnerUid(IPPROTO_TCP, udp.local, udp.remote, false); checkConnectionOwnerUid(udp.protocol, new InetSocketAddress(findLikelyFreeUdpPort(udp)), udp.remote, false); udp.close(); } @Test public void testGetConnectionOwnerUid() throws Exception { // Skip the test for API <= Q, as b/141603906 this was only fixed in Q-QPR2 assumeTrue(ShimUtils.isAtLeastR()); checkGetConnectionOwnerUid("::", null); checkGetConnectionOwnerUid("::", "::"); checkGetConnectionOwnerUid("0.0.0.0", null); checkGetConnectionOwnerUid("0.0.0.0", "0.0.0.0"); checkGetConnectionOwnerUid("127.0.0.1", null); checkGetConnectionOwnerUid("127.0.0.1", "127.0.0.2"); checkGetConnectionOwnerUid("::1", null); checkGetConnectionOwnerUid("::1", "::1"); } /* Verify fix for b/141603906 */ @Test public void testB141603906() throws Exception { // Skip the test for API <= Q, as b/141603906 this was only fixed in Q-QPR2 assumeTrue(ShimUtils.isAtLeastR()); final InetSocketAddress src = new InetSocketAddress(0); final InetSocketAddress dst = new InetSocketAddress(0); final int numThreads = 8; final int numSockets = 5000; final Thread[] threads = new Thread[numThreads]; for (int i = 0; i < numThreads; i++) { threads[i] = new Thread(() -> { for (int j = 0; j < numSockets; j++) { mCm.getConnectionOwnerUid(IPPROTO_TCP, src, dst); } }); } for (Thread thread : threads) { thread.start(); } for (Thread thread : threads) { thread.join(); } } }
tests/unit/Android.bp +1 −1 Original line number Diff line number Diff line Loading @@ -16,7 +16,7 @@ java_defaults { name: "NetworkStackTestsDefaults", certificate: "platform", platform_apis: true, srcs: ["src/**/*.java", "src/**/*.kt"], resource_dirs: ["res"], static_libs: [ Loading