Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f90a92bb authored by Remi NGUYEN VAN's avatar Remi NGUYEN VAN
Browse files

Send hostname and MTU options in DHCP ACK/OFFER

The hostname option is only sent when requested in the requested
parameters option. This matches current behavior.

Test: with aosp/763982, regression tests now all pass
      atest FrameworksNetTest
Bug: b/109584964
Change-Id: I793173fa893750ddbe72be09f4c2d70e5b285837
parent c457d8c9
Loading
Loading
Loading
Loading
+35 −2
Original line number Diff line number Diff line
package android.net.dhcp;

import static android.net.util.NetworkConstants.IPV4_MAX_MTU;
import static android.net.util.NetworkConstants.IPV4_MIN_MTU;

import android.annotation.Nullable;
import android.net.DhcpResults;
import android.net.LinkAddress;
@@ -380,6 +383,26 @@ public abstract class DhcpPacket {
        return clientId;
    }

    /**
     * Returns whether a parameter is included in the parameter request list option of this packet.
     *
     * <p>If there is no parameter request list option in the packet, false is returned.
     *
     * @param paramId ID of the parameter, such as {@link #DHCP_MTU} or {@link #DHCP_HOST_NAME}.
     */
    public boolean hasRequestedParam(byte paramId) {
        if (mRequestedParams == null) {
            return false;
        }

        for (byte reqParam : mRequestedParams) {
            if (reqParam == paramId) {
                return true;
            }
        }
        return false;
    }

    /**
     * Creates a new L3 packet (including IP header) containing the
     * DHCP udp packet.  This method relies upon the delegated method
@@ -696,7 +719,11 @@ public abstract class DhcpPacket {
        addTlv(buf, DHCP_ROUTER, mGateways);
        addTlv(buf, DHCP_DNS_SERVER, mDnsServers);
        addTlv(buf, DHCP_DOMAIN_NAME, mDomainName);
        addTlv(buf, DHCP_HOST_NAME, mHostName);
        addTlv(buf, DHCP_VENDOR_INFO, mVendorInfo);
        if (mMtu != null && Short.toUnsignedInt(mMtu) >= IPV4_MIN_MTU) {
            addTlv(buf, DHCP_MTU, mMtu);
        }
    }

    /**
@@ -1259,7 +1286,8 @@ public abstract class DhcpPacket {
        boolean broadcast, Inet4Address serverIpAddr, Inet4Address relayIp,
        Inet4Address yourIp, byte[] mac, Integer timeout, Inet4Address netMask,
        Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers,
        Inet4Address dhcpServerIdentifier, String domainName, boolean metered) {
        Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered,
        short mtu) {
        DhcpPacket pkt = new DhcpOfferPacket(
                transactionId, (short) 0, broadcast, serverIpAddr, relayIp,
                INADDR_ANY /* clientIp */, yourIp, mac);
@@ -1267,9 +1295,11 @@ public abstract class DhcpPacket {
        pkt.mDnsServers = dnsServers;
        pkt.mLeaseTime = timeout;
        pkt.mDomainName = domainName;
        pkt.mHostName = hostname;
        pkt.mServerIdentifier = dhcpServerIdentifier;
        pkt.mSubnetMask = netMask;
        pkt.mBroadcastAddress = bcAddr;
        pkt.mMtu = mtu;
        if (metered) {
            pkt.mVendorInfo = VENDOR_INFO_ANDROID_METERED;
        }
@@ -1283,7 +1313,8 @@ public abstract class DhcpPacket {
        boolean broadcast, Inet4Address serverIpAddr, Inet4Address relayIp, Inet4Address yourIp,
        Inet4Address requestClientIp, byte[] mac, Integer timeout, Inet4Address netMask,
        Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers,
        Inet4Address dhcpServerIdentifier, String domainName, boolean metered) {
        Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered,
        short mtu) {
        DhcpPacket pkt = new DhcpAckPacket(
                transactionId, (short) 0, broadcast, serverIpAddr, relayIp, requestClientIp, yourIp,
                mac);
@@ -1291,9 +1322,11 @@ public abstract class DhcpPacket {
        pkt.mDnsServers = dnsServers;
        pkt.mLeaseTime = timeout;
        pkt.mDomainName = domainName;
        pkt.mHostName = hostname;
        pkt.mSubnetMask = netMask;
        pkt.mServerIdentifier = dhcpServerIdentifier;
        pkt.mBroadcastAddress = bcAddr;
        pkt.mMtu = mtu;
        if (metered) {
            pkt.mVendorInfo = VENDOR_INFO_ANDROID_METERED;
        }
+21 −2
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import static android.net.NetworkUtils.getBroadcastAddress;
import static android.net.NetworkUtils.getPrefixMaskAsInet4Address;
import static android.net.TrafficStats.TAG_SYSTEM_DHCP_SERVER;
import static android.net.dhcp.DhcpPacket.DHCP_CLIENT;
import static android.net.dhcp.DhcpPacket.DHCP_HOST_NAME;
import static android.net.dhcp.DhcpPacket.DHCP_SERVER;
import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP;
import static android.net.dhcp.DhcpPacket.INFINITE_LEASE;
@@ -46,6 +47,7 @@ import android.os.Message;
import android.os.SystemClock;
import android.system.ErrnoException;
import android.system.Os;
import android.text.TextUtils;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.HexDump;
@@ -350,6 +352,19 @@ public class DhcpServer {
        return isEmpty(request.mClientIp) && (request.mBroadcast || isEmpty(lease.getNetAddr()));
    }

    /**
     * Get the hostname from a lease if non-empty and requested in the incoming request.
     * @param request The incoming request.
     * @return The hostname, or null if not requested or empty.
     */
    @Nullable
    private static String getHostnameIfRequested(@NonNull DhcpPacket request,
            @NonNull DhcpLease lease) {
        return request.hasRequestedParam(DHCP_HOST_NAME) && !TextUtils.isEmpty(lease.getHostname())
                ? lease.getHostname()
                : null;
    }

    private boolean transmitOffer(@NonNull DhcpPacket request, @NonNull DhcpLease lease,
            @NonNull MacAddress clientMac) {
        final boolean broadcastFlag = getBroadcastFlag(request, lease);
@@ -358,12 +373,14 @@ public class DhcpServer {
                getPrefixMaskAsInet4Address(mServingParams.serverAddr.getPrefixLength());
        final Inet4Address broadcastAddr = getBroadcastAddress(
                mServingParams.getServerInet4Addr(), mServingParams.serverAddr.getPrefixLength());
        final String hostname = getHostnameIfRequested(request, lease);
        final ByteBuffer offerPacket = DhcpPacket.buildOfferPacket(
                ENCAP_BOOTP, request.mTransId, broadcastFlag, mServingParams.getServerInet4Addr(),
                request.mRelayIp, lease.getNetAddr(), request.mClientMac, timeout, prefixMask,
                broadcastAddr, new ArrayList<>(mServingParams.defaultRouters),
                new ArrayList<>(mServingParams.dnsServers),
                mServingParams.getServerInet4Addr(), null /* domainName */, mServingParams.metered);
                mServingParams.getServerInet4Addr(), null /* domainName */, hostname,
                mServingParams.metered, (short) mServingParams.linkMtu);

        return transmitOfferOrAckPacket(offerPacket, request, lease, clientMac, broadcastFlag);
    }
@@ -374,13 +391,15 @@ public class DhcpServer {
        // transmitOffer above
        final boolean broadcastFlag = getBroadcastFlag(request, lease);
        final int timeout = getLeaseTimeout(lease);
        final String hostname = getHostnameIfRequested(request, lease);
        final ByteBuffer ackPacket = DhcpPacket.buildAckPacket(ENCAP_BOOTP, request.mTransId,
                broadcastFlag, mServingParams.getServerInet4Addr(), request.mRelayIp,
                lease.getNetAddr(), request.mClientIp, request.mClientMac, timeout,
                mServingParams.getPrefixMaskAsAddress(), mServingParams.getBroadcastAddress(),
                new ArrayList<>(mServingParams.defaultRouters),
                new ArrayList<>(mServingParams.dnsServers),
                mServingParams.getServerInet4Addr(), null /* domainName */, mServingParams.metered);
                mServingParams.getServerInet4Addr(), null /* domainName */, hostname,
                mServingParams.metered, (short) mServingParams.linkMtu);

        return transmitOfferOrAckPacket(ackPacket, request, lease, clientMac, broadcastFlag);
    }
+29 −8
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import android.annotation.Nullable;
import android.net.DhcpResults;
import android.net.LinkAddress;
import android.net.NetworkUtils;
@@ -37,6 +38,7 @@ import com.android.internal.util.HexDump;
import java.io.ByteArrayOutputStream;
import java.net.Inet4Address;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -56,6 +58,8 @@ public class DhcpPacketTest {
    private static final Inet4Address NETMASK = getPrefixMaskAsInet4Address(PREFIX_LENGTH);
    private static final Inet4Address BROADCAST_ADDR = getBroadcastAddress(
            SERVER_ADDR, PREFIX_LENGTH);
    private static final String HOSTNAME = "testhostname";
    private static final short MTU = 1500;
    // Use our own empty address instead of Inet4Address.ANY or INADDR_ANY to ensure that the code
    // doesn't use == instead of equals when comparing addresses.
    private static final Inet4Address ANY = (Inet4Address) v4Address("0.0.0.0");
@@ -960,7 +964,8 @@ public class DhcpPacketTest {
        assertTrue(msg, Arrays.equals(expected, actual));
    }

    public void checkBuildOfferPacket(int leaseTimeSecs) throws Exception {
    public void checkBuildOfferPacket(int leaseTimeSecs, @Nullable String hostname)
            throws Exception {
        final int renewalTime = (int) (Integer.toUnsignedLong(leaseTimeSecs) / 2);
        final int rebindingTime = (int) (Integer.toUnsignedLong(leaseTimeSecs) * 875 / 1000);
        final int transactionId = 0xdeadbeef;
@@ -971,7 +976,8 @@ public class DhcpPacketTest {
                CLIENT_MAC, leaseTimeSecs, NETMASK /* netMask */,
                BROADCAST_ADDR /* bcAddr */, Collections.singletonList(SERVER_ADDR) /* gateways */,
                Collections.singletonList(SERVER_ADDR) /* dnsServers */,
                SERVER_ADDR /* dhcpServerIdentifier */, null /* domainName */, false /* metered */);
                SERVER_ADDR /* dhcpServerIdentifier */, null /* domainName */, hostname,
                false /* metered */, MTU);

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        // BOOTP headers
@@ -1027,12 +1033,22 @@ public class DhcpPacketTest {
        // Nameserver
        bos.write(new byte[] { (byte) 0x06, (byte) 0x04 });
        bos.write(SERVER_ADDR.getAddress());
        // Hostname
        if (hostname != null) {
            bos.write(new byte[]{(byte) 0x0c, (byte) hostname.length()});
            bos.write(hostname.getBytes(Charset.forName("US-ASCII")));
        }
        // MTU
        bos.write(new byte[] { (byte) 0x1a, (byte) 0x02 });
        bos.write(shortToByteArray(MTU));
        // End options.
        bos.write(0xff);

        final byte[] expected = bos.toByteArray();
        assertTrue((expected.length & 1) == 0);
        if ((bos.size() & 1) != 0) {
            bos.write(0x00);
        }

        final byte[] expected = bos.toByteArray();
        final byte[] actual = new byte[packet.limit()];
        packet.get(actual);
        final String msg = "Expected:\n  " + HexDump.dumpHexString(expected) +
@@ -1042,13 +1058,18 @@ public class DhcpPacketTest {

    @Test
    public void testOfferPacket() throws Exception {
        checkBuildOfferPacket(3600);
        checkBuildOfferPacket(Integer.MAX_VALUE);
        checkBuildOfferPacket(0x80000000);
        checkBuildOfferPacket(INFINITE_LEASE);
        checkBuildOfferPacket(3600, HOSTNAME);
        checkBuildOfferPacket(Integer.MAX_VALUE, HOSTNAME);
        checkBuildOfferPacket(0x80000000, HOSTNAME);
        checkBuildOfferPacket(INFINITE_LEASE, HOSTNAME);
        checkBuildOfferPacket(3600, null);
    }

    private static byte[] intToByteArray(int val) {
        return ByteBuffer.allocate(4).putInt(val).array();
    }

    private static byte[] shortToByteArray(short val) {
        return ByteBuffer.allocate(2).putShort(val).array();
    }
}
+17 −6
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package android.net.dhcp;

import static android.net.dhcp.DhcpPacket.DHCP_CLIENT;
import static android.net.dhcp.DhcpPacket.DHCP_HOST_NAME;
import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP;
import static android.net.dhcp.DhcpPacket.INADDR_ANY;
import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST;
@@ -87,6 +88,7 @@ public class DhcpServerTest {
            Arrays.asList(parseAddr("192.168.0.200"), parseAddr("192.168.0.201")));
    private static final long TEST_LEASE_TIME_SECS = 3600L;
    private static final int TEST_MTU = 1500;
    private static final String TEST_HOSTNAME = "testhostname";

    private static final int TEST_TRANSACTION_ID = 123;
    private static final byte[] TEST_CLIENT_MAC_BYTES = new byte [] { 1, 2, 3, 4, 5, 6 };
@@ -96,7 +98,10 @@ public class DhcpServerTest {
    private static final long TEST_CLOCK_TIME = 1234L;
    private static final int TEST_LEASE_EXPTIME_SECS = 3600;
    private static final DhcpLease TEST_LEASE = new DhcpLease(null, TEST_CLIENT_MAC,
            TEST_CLIENT_ADDR, TEST_LEASE_EXPTIME_SECS*1000L + TEST_CLOCK_TIME, null /* hostname */);
            TEST_CLIENT_ADDR, TEST_LEASE_EXPTIME_SECS * 1000L + TEST_CLOCK_TIME,
            null /* hostname */);
    private static final DhcpLease TEST_LEASE_WITH_HOSTNAME = new DhcpLease(null, TEST_CLIENT_MAC,
            TEST_CLIENT_ADDR, TEST_LEASE_EXPTIME_SECS * 1000L + TEST_CLOCK_TIME, TEST_HOSTNAME);

    @NonNull @Mock
    private Dependencies mDeps;
@@ -217,15 +222,17 @@ public class DhcpServerTest {
    public void testRequest_Selecting_Ack() throws Exception {
        when(mRepository.requestLease(isNull() /* clientId */, eq(TEST_CLIENT_MAC),
                eq(INADDR_ANY) /* clientAddr */, eq(INADDR_ANY) /* relayAddr */,
                eq(TEST_CLIENT_ADDR) /* reqAddr */, eq(true) /* sidSet */, isNull() /* hostname */))
                .thenReturn(TEST_LEASE);
                eq(TEST_CLIENT_ADDR) /* reqAddr */, eq(true) /* sidSet */, eq(TEST_HOSTNAME)))
                .thenReturn(TEST_LEASE_WITH_HOSTNAME);

        final DhcpRequestPacket request = makeRequestSelectingPacket();
        request.mHostName = TEST_HOSTNAME;
        request.mRequestedParams = new byte[] { DHCP_HOST_NAME };
        mServer.processPacket(request, DHCP_CLIENT);

        assertResponseSentTo(TEST_CLIENT_ADDR);
        final DhcpAckPacket packet = assertAck(getPacket());
        assertMatchesTestLease(packet);
        assertMatchesTestLease(packet, TEST_HOSTNAME);
    }

    @Test
@@ -270,14 +277,18 @@ public class DhcpServerTest {
     *  - other request states (init-reboot/renewing/rebinding)
     */

    private void assertMatchesTestLease(@NonNull DhcpPacket packet) {
    private void assertMatchesTestLease(@NonNull DhcpPacket packet, @Nullable String hostname) {
        assertMatchesClient(packet);
        assertFalse(packet.hasExplicitClientId());
        assertEquals(TEST_SERVER_ADDR, packet.mServerIdentifier);
        assertEquals(TEST_CLIENT_ADDR, packet.mYourIp);
        assertNotNull(packet.mLeaseTime);
        assertEquals(TEST_LEASE_EXPTIME_SECS, (int) packet.mLeaseTime);
        assertNull(packet.mHostName);
        assertEquals(hostname, packet.mHostName);
    }

    private void assertMatchesTestLease(@NonNull DhcpPacket packet) {
        assertMatchesTestLease(packet, null);
    }

    private void assertMatchesClient(@NonNull DhcpPacket packet) {