summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRemi NGUYEN VAN <reminv@google.com>2019-10-28 17:04:55 +0900
committerXiao Ma <xiaom@google.com>2020-02-19 21:54:01 +0900
commit2a7389299668893d0532838af5f9210f0884604a (patch)
treeba7578961ae0e0bc5684b9b1825e9cbcc4dce5bd /src
parent21907e1cdc3dc9f86c4446d7e111a6637aefcec4 (diff)
Add captive portal info to DhcpClient output
Requesting the captive portal option is flagged off by default. The URL it provides will be used to support the captive portal API; see RFC7710bis. Bug: 139269711 Test: atest NetworkStackTests NetworkStackNextTests Test: atest NetworkStackIntegrationTests NetworkStackNextIntegrationTests Change-Id: I783466e0e60f364e79cd76af3fe43a7862d35cf2
Diffstat (limited to 'src')
-rw-r--r--src/android/net/dhcp/DhcpClient.java32
-rw-r--r--src/android/net/dhcp/DhcpPacket.java64
-rw-r--r--src/android/net/dhcp/DhcpServer.java7
-rw-r--r--src/android/net/ip/IpClient.java11
-rw-r--r--src/android/net/util/ConnectivityPacketSummary.java14
5 files changed, 108 insertions, 20 deletions
diff --git a/src/android/net/dhcp/DhcpClient.java b/src/android/net/dhcp/DhcpClient.java
index fbcd0f6..41e54a4 100644
--- a/src/android/net/dhcp/DhcpClient.java
+++ b/src/android/net/dhcp/DhcpClient.java
@@ -17,6 +17,7 @@
package android.net.dhcp;
import static android.net.dhcp.DhcpPacket.DHCP_BROADCAST_ADDRESS;
+import static android.net.dhcp.DhcpPacket.DHCP_CAPTIVE_PORTAL;
import static android.net.dhcp.DhcpPacket.DHCP_DNS_SERVER;
import static android.net.dhcp.DhcpPacket.DHCP_DOMAIN_NAME;
import static android.net.dhcp.DhcpPacket.DHCP_LEASE_TIME;
@@ -95,6 +96,7 @@ import com.android.internal.util.StateMachine;
import com.android.internal.util.TrafficStatsConstants;
import com.android.internal.util.WakeupMessage;
import com.android.networkstack.R;
+import com.android.networkstack.apishim.CaptivePortalDataShimImpl;
import com.android.networkstack.apishim.SocketUtilsShimImpl;
import com.android.networkstack.arp.ArpPacket;
@@ -260,8 +262,9 @@ public class DhcpClient extends StateMachine {
private static final SparseArray<String> sMessageNames =
MessageUtils.findMessageNames(sMessageClasses);
- // DHCP parameters that we request.
- /* package */ static final byte[] REQUESTED_PARAMS = new byte[] {
+ // DHCP parameters that we request by default.
+ @VisibleForTesting
+ /* package */ static final byte[] DEFAULT_REQUESTED_PARAMS = new byte[] {
DHCP_SUBNET_MASK,
DHCP_ROUTER,
DHCP_DNS_SERVER,
@@ -274,6 +277,21 @@ public class DhcpClient extends StateMachine {
DHCP_VENDOR_INFO,
};
+ @NonNull
+ private byte[] getRequestedParams() {
+ if (isCapportApiEnabled()) {
+ final byte[] params = Arrays.copyOf(DEFAULT_REQUESTED_PARAMS,
+ DEFAULT_REQUESTED_PARAMS.length + 1);
+ params[params.length - 1] = DHCP_CAPTIVE_PORTAL;
+ return params;
+ }
+ return DEFAULT_REQUESTED_PARAMS;
+ }
+
+ private static boolean isCapportApiEnabled() {
+ return CaptivePortalDataShimImpl.isSupported();
+ }
+
// DHCP flag that means "yes, we support unicast."
private static final boolean DO_UNICAST = false;
@@ -556,8 +574,10 @@ public class DhcpClient extends StateMachine {
@Override
protected void handlePacket(byte[] recvbuf, int length) {
try {
+ final byte[] optionsToSkip =
+ isCapportApiEnabled() ? new byte[0] : new byte[] { DHCP_CAPTIVE_PORTAL };
final DhcpPacket packet = DhcpPacket.decodeFullPacket(recvbuf, length,
- DhcpPacket.ENCAP_L2);
+ DhcpPacket.ENCAP_L2, optionsToSkip);
if (DBG) Log.d(TAG, "Received packet: " + packet);
sendMessage(CMD_RECEIVED_PACKET, packet);
} catch (DhcpPacket.ParseException e) {
@@ -649,7 +669,7 @@ public class DhcpClient extends StateMachine {
private boolean sendDiscoverPacket() {
final ByteBuffer packet = DhcpPacket.buildDiscoverPacket(
DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr,
- DO_UNICAST, REQUESTED_PARAMS, isDhcpRapidCommitEnabled(), mHostname);
+ DO_UNICAST, getRequestedParams(), isDhcpRapidCommitEnabled(), mHostname);
return transmitPacket(packet, "DHCPDISCOVER", DhcpPacket.ENCAP_L2, INADDR_BROADCAST);
}
@@ -663,7 +683,7 @@ public class DhcpClient extends StateMachine {
final ByteBuffer packet = DhcpPacket.buildRequestPacket(
encap, mTransactionId, getSecs(), clientAddress,
DO_UNICAST, mHwAddr, requestedAddress,
- serverAddress, REQUESTED_PARAMS, mHostname);
+ serverAddress, getRequestedParams(), mHostname);
String serverStr = (serverAddress != null) ? serverAddress.getHostAddress() : null;
String description = "DHCPREQUEST ciaddr=" + clientAddress.getHostAddress() +
" request=" + requestedAddress.getHostAddress() +
@@ -1267,7 +1287,7 @@ public class DhcpClient extends StateMachine {
final Layer2PacketParcelable l2Packet = new Layer2PacketParcelable();
final ByteBuffer packet = DhcpPacket.buildDiscoverPacket(
DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr,
- DO_UNICAST, REQUESTED_PARAMS, true /* rapid commit */, mHostname);
+ DO_UNICAST, getRequestedParams(), true /* rapid commit */, mHostname);
l2Packet.dstMacAddress = MacAddress.fromBytes(DhcpPacket.ETHER_BROADCAST);
l2Packet.payload = Arrays.copyOf(packet.array(), packet.limit());
diff --git a/src/android/net/dhcp/DhcpPacket.java b/src/android/net/dhcp/DhcpPacket.java
index 9eca0b5..19af74c 100644
--- a/src/android/net/dhcp/DhcpPacket.java
+++ b/src/android/net/dhcp/DhcpPacket.java
@@ -307,6 +307,10 @@ public abstract class DhcpPacket {
protected static final byte DHCP_RAPID_COMMIT = 80;
protected boolean mRapidCommit;
+ @VisibleForTesting
+ public static final byte DHCP_CAPTIVE_PORTAL = (byte) 114;
+ protected String mCaptivePortalUrl;
+
/**
* DHCP zero-length option code: pad
*/
@@ -785,6 +789,7 @@ public abstract class DhcpPacket {
if (mMtu != null && Short.toUnsignedInt(mMtu) >= IPV4_MIN_MTU) {
addTlv(buf, DHCP_MTU, mMtu);
}
+ addTlv(buf, DHCP_CAPTIVE_PORTAL, mCaptivePortalUrl);
}
/**
@@ -871,6 +876,23 @@ public abstract class DhcpPacket {
}
}
+ private static int skipOption(ByteBuffer packet, int optionLen)
+ throws BufferUnderflowException {
+ int expectedLen = 0;
+ for (int i = 0; i < optionLen; i++) {
+ expectedLen++;
+ packet.get();
+ }
+ return expectedLen;
+ }
+
+ private static boolean shouldSkipOption(byte optionType, byte[] optionsToSkip) {
+ for (byte option : optionsToSkip) {
+ if (option == optionType) return true;
+ }
+ return false;
+ }
+
/**
* Creates a concrete DhcpPacket from the supplied ByteBuffer. The
* buffer may have an L2 encapsulation (which is the full EthernetII
@@ -881,8 +903,8 @@ public abstract class DhcpPacket {
* in object fields.
*/
@VisibleForTesting
- static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType) throws ParseException
- {
+ static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType, byte[] optionsToSkip)
+ throws ParseException {
// bootp parameters
int transactionId;
short secs;
@@ -900,6 +922,7 @@ public abstract class DhcpPacket {
String vendorId = null;
String vendorInfo = null;
boolean rapidCommit = false;
+ String captivePortalUrl = null;
byte[] expectedParams = null;
String hostName = null;
String domainName = null;
@@ -1080,6 +1103,11 @@ public abstract class DhcpPacket {
int optionLen = packet.get() & 0xFF;
int expectedLen = 0;
+ if (shouldSkipOption(optionType, optionsToSkip)) {
+ skipOption(packet, optionLen);
+ continue;
+ }
+
switch(optionType) {
case DHCP_SUBNET_MASK:
netMask = readIpAddress(packet);
@@ -1172,12 +1200,12 @@ public abstract class DhcpPacket {
expectedLen = 0;
rapidCommit = true;
break;
+ case DHCP_CAPTIVE_PORTAL:
+ expectedLen = optionLen;
+ captivePortalUrl = readAsciiString(packet, optionLen, true);
+ break;
default:
- // ignore any other parameters
- for (int i = 0; i < optionLen; i++) {
- expectedLen++;
- byte throwaway = packet.get();
- }
+ expectedLen = skipOption(packet, optionLen);
}
if (expectedLen != optionLen) {
@@ -1263,6 +1291,7 @@ public abstract class DhcpPacket {
newPacket.mT2 = T2;
newPacket.mVendorId = vendorId;
newPacket.mVendorInfo = vendorInfo;
+ newPacket.mCaptivePortalUrl = captivePortalUrl;
if ((optionOverload & OPTION_OVERLOAD_SNAME) == 0) {
newPacket.mServerHostName = serverHostName;
} else {
@@ -1274,11 +1303,11 @@ public abstract class DhcpPacket {
/**
* Parse a packet from an array of bytes, stopping at the given length.
*/
- public static DhcpPacket decodeFullPacket(byte[] packet, int length, int pktType)
- throws ParseException {
+ public static DhcpPacket decodeFullPacket(byte[] packet, int length, int pktType,
+ byte[] optionsToSkip) throws ParseException {
ByteBuffer buffer = ByteBuffer.wrap(packet, 0, length).order(ByteOrder.BIG_ENDIAN);
try {
- return decodeFullPacket(buffer, pktType);
+ return decodeFullPacket(buffer, pktType, optionsToSkip);
} catch (ParseException e) {
throw e;
} catch (Exception e) {
@@ -1287,6 +1316,14 @@ public abstract class DhcpPacket {
}
/**
+ * Parse a packet from an array of bytes, stopping at the given length.
+ */
+ public static DhcpPacket decodeFullPacket(byte[] packet, int length, int pktType)
+ throws ParseException {
+ return decodeFullPacket(packet, length, pktType, new byte[0]);
+ }
+
+ /**
* Construct a DhcpResults object from a DHCP reply packet.
*/
public DhcpResults toDhcpResults() {
@@ -1328,6 +1365,7 @@ public abstract class DhcpPacket {
results.leaseDuration = (mLeaseTime != null) ? mLeaseTime : INFINITE_LEASE;
results.mtu = (mMtu != null && MIN_MTU <= mMtu && mMtu <= MAX_MTU) ? mMtu : 0;
results.serverHostName = mServerHostName;
+ results.captivePortalApiUrl = mCaptivePortalUrl;
return results;
}
@@ -1369,7 +1407,7 @@ public abstract class DhcpPacket {
Inet4Address yourIp, byte[] mac, Integer timeout, Inet4Address netMask,
Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers,
Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered,
- short mtu) {
+ short mtu, String captivePortalUrl) {
DhcpPacket pkt = new DhcpOfferPacket(
transactionId, (short) 0, broadcast, serverIpAddr, relayIp,
INADDR_ANY /* clientIp */, yourIp, mac);
@@ -1382,6 +1420,7 @@ public abstract class DhcpPacket {
pkt.mSubnetMask = netMask;
pkt.mBroadcastAddress = bcAddr;
pkt.mMtu = mtu;
+ pkt.mCaptivePortalUrl = captivePortalUrl;
if (metered) {
pkt.mVendorInfo = VENDOR_INFO_ANDROID_METERED;
}
@@ -1396,7 +1435,7 @@ public abstract class DhcpPacket {
Inet4Address requestClientIp, byte[] mac, Integer timeout, Inet4Address netMask,
Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers,
Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered,
- short mtu, boolean rapidCommit) {
+ short mtu, boolean rapidCommit, String captivePortalUrl) {
DhcpPacket pkt = new DhcpAckPacket(
transactionId, (short) 0, broadcast, serverIpAddr, relayIp, requestClientIp, yourIp,
mac, rapidCommit);
@@ -1409,6 +1448,7 @@ public abstract class DhcpPacket {
pkt.mServerIdentifier = dhcpServerIdentifier;
pkt.mBroadcastAddress = bcAddr;
pkt.mMtu = mtu;
+ pkt.mCaptivePortalUrl = captivePortalUrl;
if (metered) {
pkt.mVendorInfo = VENDOR_INFO_ANDROID_METERED;
}
diff --git a/src/android/net/dhcp/DhcpServer.java b/src/android/net/dhcp/DhcpServer.java
index 9e54a69..9c5b3c6 100644
--- a/src/android/net/dhcp/DhcpServer.java
+++ b/src/android/net/dhcp/DhcpServer.java
@@ -529,7 +529,9 @@ public class DhcpServer extends IDhcpServer.Stub {
broadcastAddr, new ArrayList<>(mServingParams.defaultRouters),
new ArrayList<>(mServingParams.dnsServers),
mServingParams.getServerInet4Addr(), null /* domainName */, hostname,
- mServingParams.metered, (short) mServingParams.linkMtu);
+ mServingParams.metered, (short) mServingParams.linkMtu,
+ // TODO (b/144402437): advertise the URL if known
+ null /* captivePortalApiUrl */);
return transmitOfferOrAckPacket(offerPacket, request, lease, clientMac, broadcastFlag);
}
@@ -549,7 +551,8 @@ public class DhcpServer extends IDhcpServer.Stub {
new ArrayList<>(mServingParams.dnsServers),
mServingParams.getServerInet4Addr(), null /* domainName */, hostname,
mServingParams.metered, (short) mServingParams.linkMtu,
- packet.mRapidCommit && mDhcpRapidCommitEnabled);
+ // TODO (b/144402437): advertise the URL if known
+ packet.mRapidCommit && mDhcpRapidCommitEnabled, null /* captivePortalApiUrl */);
return transmitOfferOrAckPacket(ackPacket, packet, lease, clientMac, broadcastFlag);
}
diff --git a/src/android/net/ip/IpClient.java b/src/android/net/ip/IpClient.java
index 47f955a..173f136 100644
--- a/src/android/net/ip/IpClient.java
+++ b/src/android/net/ip/IpClient.java
@@ -36,6 +36,7 @@ import android.net.ProvisioningConfigurationParcelable;
import android.net.ProxyInfo;
import android.net.RouteInfo;
import android.net.TcpKeepalivePacketDataParcelable;
+import android.net.Uri;
import android.net.apf.ApfCapabilities;
import android.net.apf.ApfFilter;
import android.net.dhcp.DhcpClient;
@@ -57,6 +58,7 @@ import android.text.TextUtils;
import android.util.LocalLog;
import android.util.Log;
import android.util.Pair;
+import android.util.Patterns;
import android.util.SparseArray;
import androidx.annotation.NonNull;
@@ -1192,6 +1194,15 @@ public class IpClient extends StateMachine {
if (mDhcpResults.mtu != 0) {
newLp.setMtu(mDhcpResults.mtu);
}
+
+ final String capportUrl = mDhcpResults.captivePortalApiUrl;
+ // Uri.parse does no syntax check; do a simple regex check to eliminate garbage.
+ // If the URL is still incorrect data fetching will fail later, which is fine.
+ if (capportUrl != null && Patterns.WEB_URL.matcher(capportUrl).matches()) {
+ NetworkInformationShimImpl.newInstance()
+ .setCaptivePortalApiUrl(newLp, Uri.parse(capportUrl));
+ }
+ // TODO: also look at the IPv6 RA (netlink) for captive portal URL
}
// [4] Add in TCP buffer sizes and HTTP Proxy config, if available.
diff --git a/src/android/net/util/ConnectivityPacketSummary.java b/src/android/net/util/ConnectivityPacketSummary.java
index 08c3f60..4d04911 100644
--- a/src/android/net/util/ConnectivityPacketSummary.java
+++ b/src/android/net/util/ConnectivityPacketSummary.java
@@ -82,12 +82,26 @@ public class ConnectivityPacketSummary {
private final ByteBuffer mPacket;
private final String mSummary;
+ /**
+ * Create a string summary of a received packet.
+ * @param hwaddr MacAddress of the receiving device.
+ * @param buffer Buffer of the packet. Length is assumed to be the buffer length.
+ * @return A summary of the packet.
+ */
public static String summarize(MacAddress hwaddr, byte[] buffer) {
return summarize(hwaddr, buffer, buffer.length);
}
// Methods called herein perform some but by no means all error checking.
// They may throw runtime exceptions on malformed packets.
+
+ /**
+ * Create a string summary of a received packet.
+ * @param macAddr MacAddress of the receiving device.
+ * @param buffer Buffer of the packet.
+ * @param length Length of the packet.
+ * @return A summary of the packet.
+ */
public static String summarize(MacAddress macAddr, byte[] buffer, int length) {
if ((macAddr == null) || (buffer == null)) return null;
length = Math.min(length, buffer.length);