Move IpClient to NetworkStack
Test: atest FrameworksNetTests NetworkStackTests
Bug: b/112869080
Change-Id: I7d00848c052382cd1b6ce458868bed6a1e9e8ec5
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index fae7a8d..d6cd902 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -100,7 +100,7 @@
import android.net.shared.NetworkMonitorUtils;
import android.net.shared.PrivateDnsConfig;
import android.net.util.MultinetworkPolicyTracker;
-import android.net.util.NetdService;
+import android.net.shared.NetdService;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 126bf65..371276f 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -44,7 +44,7 @@
import android.net.Network;
import android.net.NetworkUtils;
import android.net.TrafficStats;
-import android.net.util.NetdService;
+import android.net.shared.NetdService;
import android.os.Binder;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 5ef3fe49..61a992f 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -60,7 +60,7 @@
import android.net.RouteInfo;
import android.net.TetherStatsParcel;
import android.net.UidRange;
-import android.net.util.NetdService;
+import android.net.shared.NetdService;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.Handler;
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
index ac74598..79b56c6 100644
--- a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
+++ b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
@@ -20,7 +20,6 @@
import android.net.ConnectivityMetricsEvent;
import android.net.IIpConnectivityMetrics;
import android.net.INetdEventCallback;
-import android.net.ip.IpClient;
import android.net.metrics.ApfProgramEvent;
import android.net.metrics.IpConnectivityLog;
import android.os.Binder;
@@ -270,8 +269,6 @@
// Dump the rolling buffer of metrics event and pretty print events using a human readable
// format. Also print network dns/connect statistics and default network event time series.
static final String CMD_LIST = "list";
- // Dump all IpClient logs ("ipclient").
- static final String CMD_IPCLIENT = IpClient.DUMP_ARG;
// By default any other argument will fall into the default case which is the equivalent
// of calling both the "list" and "ipclient" commands. This includes most notably bug
// reports collected by dumpsys.cpp with the "-a" argument.
@@ -295,20 +292,9 @@
case CMD_PROTO:
cmdListAsProto(pw);
return;
- case CMD_IPCLIENT: {
- final String[] ipclientArgs = ((args != null) && (args.length > 1))
- ? Arrays.copyOfRange(args, 1, args.length)
- : null;
- IpClient.dumpAllLogs(pw, ipclientArgs);
- return;
- }
case CMD_LIST:
- cmdList(pw);
- return;
default:
cmdList(pw);
- pw.println("");
- IpClient.dumpAllLogs(pw, null);
return;
}
}
diff --git a/services/net/Android.bp b/services/net/Android.bp
index 3b4d6a7..30c7de5 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -3,18 +3,18 @@
srcs: ["java/**/*.java"],
}
-// TODO: move to networking module with DhcpClient and remove lib
-java_library {
- name: "dhcp-packet-lib",
- srcs: [
- "java/android/net/dhcp/*Packet.java",
- ]
-}
-
filegroup {
name: "services-networkstack-shared-srcs",
srcs: [
- "java/android/net/util/FdEventsReader.java", // TODO: move to NetworkStack with IpClient
+ "java/android/net/ip/InterfaceController.java", // TODO: move to NetworkStack with tethering
+ "java/android/net/util/InterfaceParams.java", // TODO: move to NetworkStack with IpServer
"java/android/net/shared/*.java",
+ ],
+}
+
+java_library {
+ name: "services-netlink-lib",
+ srcs: [
+ "java/android/net/netlink/*.java",
]
}
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
deleted file mode 100644
index 4943952..0000000
--- a/services/net/java/android/net/apf/ApfFilter.java
+++ /dev/null
@@ -1,1591 +0,0 @@
-/*
- * Copyright (C) 2016 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.apf;
-
-import static android.net.util.NetworkConstants.ICMPV6_ECHO_REQUEST_TYPE;
-import static android.net.util.NetworkConstants.ICMPV6_NEIGHBOR_ADVERTISEMENT;
-import static android.net.util.NetworkConstants.ICMPV6_ROUTER_ADVERTISEMENT;
-import static android.net.util.NetworkConstants.ICMPV6_ROUTER_SOLICITATION;
-import static android.net.util.SocketUtils.makePacketSocketAddress;
-import static android.system.OsConstants.AF_PACKET;
-import static android.system.OsConstants.ARPHRD_ETHER;
-import static android.system.OsConstants.ETH_P_ARP;
-import static android.system.OsConstants.ETH_P_IP;
-import static android.system.OsConstants.ETH_P_IPV6;
-import static android.system.OsConstants.IPPROTO_ICMPV6;
-import static android.system.OsConstants.IPPROTO_UDP;
-import static android.system.OsConstants.SOCK_RAW;
-
-import static com.android.internal.util.BitUtils.bytesToBEInt;
-import static com.android.internal.util.BitUtils.getUint16;
-import static com.android.internal.util.BitUtils.getUint32;
-import static com.android.internal.util.BitUtils.getUint8;
-import static com.android.internal.util.BitUtils.uint32;
-
-import android.annotation.Nullable;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.LinkAddress;
-import android.net.LinkProperties;
-import android.net.NetworkUtils;
-import android.net.apf.ApfGenerator.IllegalInstructionException;
-import android.net.apf.ApfGenerator.Register;
-import android.net.ip.IpClientCallbacks;
-import android.net.metrics.ApfProgramEvent;
-import android.net.metrics.ApfStats;
-import android.net.metrics.IpConnectivityLog;
-import android.net.metrics.RaEvent;
-import android.net.util.InterfaceParams;
-import android.os.PowerManager;
-import android.os.SystemClock;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.text.format.DateUtils;
-import android.util.Log;
-import android.util.Pair;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.HexDump;
-import com.android.internal.util.IndentingPrintWriter;
-
-import libcore.io.IoBridge;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.net.Inet4Address;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.SocketAddress;
-import java.net.SocketException;
-import java.net.UnknownHostException;
-import java.nio.BufferUnderflowException;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.Arrays;
-
-/**
- * For networks that support packet filtering via APF programs, {@code ApfFilter}
- * listens for IPv6 ICMPv6 router advertisements (RAs) and generates APF programs to
- * filter out redundant duplicate ones.
- *
- * Threading model:
- * A collection of RAs we've received is kept in mRas. Generating APF programs uses mRas to
- * know what RAs to filter for, thus generating APF programs is dependent on mRas.
- * mRas can be accessed by multiple threads:
- * - ReceiveThread, which listens for RAs and adds them to mRas, and generates APF programs.
- * - callers of:
- * - setMulticastFilter(), which can cause an APF program to be generated.
- * - dump(), which dumps mRas among other things.
- * - shutdown(), which clears mRas.
- * So access to mRas is synchronized.
- *
- * @hide
- */
-public class ApfFilter {
-
- // Helper class for specifying functional filter parameters.
- public static class ApfConfiguration {
- public ApfCapabilities apfCapabilities;
- public boolean multicastFilter;
- public boolean ieee802_3Filter;
- public int[] ethTypeBlackList;
- }
-
- // Enums describing the outcome of receiving an RA packet.
- private static enum ProcessRaResult {
- MATCH, // Received RA matched a known RA
- DROPPED, // Received RA ignored due to MAX_RAS
- PARSE_ERROR, // Received RA could not be parsed
- ZERO_LIFETIME, // Received RA had 0 lifetime
- UPDATE_NEW_RA, // APF program updated for new RA
- UPDATE_EXPIRY // APF program updated for expiry
- }
-
- /**
- * APF packet counters.
- *
- * Packet counters are 32bit big-endian values, and allocated near the end of the APF data
- * buffer, using negative byte offsets, where -4 is equivalent to maximumApfProgramSize - 4,
- * the last writable 32bit word.
- */
- @VisibleForTesting
- public static enum Counter {
- RESERVED_OOB, // Points to offset 0 from the end of the buffer (out-of-bounds)
- TOTAL_PACKETS,
- PASSED_ARP,
- PASSED_DHCP,
- PASSED_IPV4,
- PASSED_IPV6_NON_ICMP,
- PASSED_IPV4_UNICAST,
- PASSED_IPV6_ICMP,
- PASSED_IPV6_UNICAST_NON_ICMP,
- PASSED_ARP_NON_IPV4,
- PASSED_ARP_UNKNOWN,
- PASSED_ARP_UNICAST_REPLY,
- PASSED_NON_IP_UNICAST,
- DROPPED_ETH_BROADCAST,
- DROPPED_RA,
- DROPPED_GARP_REPLY,
- DROPPED_ARP_OTHER_HOST,
- DROPPED_IPV4_L2_BROADCAST,
- DROPPED_IPV4_BROADCAST_ADDR,
- DROPPED_IPV4_BROADCAST_NET,
- DROPPED_IPV4_MULTICAST,
- DROPPED_IPV6_ROUTER_SOLICITATION,
- DROPPED_IPV6_MULTICAST_NA,
- DROPPED_IPV6_MULTICAST,
- DROPPED_IPV6_MULTICAST_PING,
- DROPPED_IPV6_NON_ICMP_MULTICAST,
- DROPPED_802_3_FRAME,
- DROPPED_ETHERTYPE_BLACKLISTED,
- DROPPED_ARP_REPLY_SPA_NO_HOST;
-
- // Returns the negative byte offset from the end of the APF data segment for
- // a given counter.
- public int offset() {
- return - this.ordinal() * 4; // Currently, all counters are 32bit long.
- }
-
- // Returns the total size of the data segment in bytes.
- public static int totalSize() {
- return (Counter.class.getEnumConstants().length - 1) * 4;
- }
- }
-
- /**
- * When APFv4 is supported, loads R1 with the offset of the specified counter.
- */
- private void maybeSetupCounter(ApfGenerator gen, Counter c) {
- if (mApfCapabilities.hasDataAccess()) {
- gen.addLoadImmediate(Register.R1, c.offset());
- }
- }
-
- // When APFv4 is supported, these point to the trampolines generated by emitEpilogue().
- // Otherwise, they're just aliases for PASS_LABEL and DROP_LABEL.
- private final String mCountAndPassLabel;
- private final String mCountAndDropLabel;
-
- // Thread to listen for RAs.
- @VisibleForTesting
- class ReceiveThread extends Thread {
- private final byte[] mPacket = new byte[1514];
- private final FileDescriptor mSocket;
- private final long mStart = SystemClock.elapsedRealtime();
-
- private int mReceivedRas = 0;
- private int mMatchingRas = 0;
- private int mDroppedRas = 0;
- private int mParseErrors = 0;
- private int mZeroLifetimeRas = 0;
- private int mProgramUpdates = 0;
-
- private volatile boolean mStopped;
-
- public ReceiveThread(FileDescriptor socket) {
- mSocket = socket;
- }
-
- public void halt() {
- mStopped = true;
- try {
- // Interrupts the read() call the thread is blocked in.
- IoBridge.closeAndSignalBlockedThreads(mSocket);
- } catch (IOException ignored) {}
- }
-
- @Override
- public void run() {
- log("begin monitoring");
- while (!mStopped) {
- try {
- int length = Os.read(mSocket, mPacket, 0, mPacket.length);
- updateStats(processRa(mPacket, length));
- } catch (IOException|ErrnoException e) {
- if (!mStopped) {
- Log.e(TAG, "Read error", e);
- }
- }
- }
- logStats();
- }
-
- private void updateStats(ProcessRaResult result) {
- mReceivedRas++;
- switch(result) {
- case MATCH:
- mMatchingRas++;
- return;
- case DROPPED:
- mDroppedRas++;
- return;
- case PARSE_ERROR:
- mParseErrors++;
- return;
- case ZERO_LIFETIME:
- mZeroLifetimeRas++;
- return;
- case UPDATE_EXPIRY:
- mMatchingRas++;
- mProgramUpdates++;
- return;
- case UPDATE_NEW_RA:
- mProgramUpdates++;
- return;
- }
- }
-
- private void logStats() {
- final long nowMs = SystemClock.elapsedRealtime();
- synchronized (this) {
- final ApfStats stats = new ApfStats.Builder()
- .setReceivedRas(mReceivedRas)
- .setMatchingRas(mMatchingRas)
- .setDroppedRas(mDroppedRas)
- .setParseErrors(mParseErrors)
- .setZeroLifetimeRas(mZeroLifetimeRas)
- .setProgramUpdates(mProgramUpdates)
- .setDurationMs(nowMs - mStart)
- .setMaxProgramSize(mApfCapabilities.maximumApfProgramSize)
- .setProgramUpdatesAll(mNumProgramUpdates)
- .setProgramUpdatesAllowingMulticast(mNumProgramUpdatesAllowingMulticast)
- .build();
- mMetricsLog.log(stats);
- logApfProgramEventLocked(nowMs / DateUtils.SECOND_IN_MILLIS);
- }
- }
- }
-
- private static final String TAG = "ApfFilter";
- private static final boolean DBG = true;
- private static final boolean VDBG = false;
-
- private static final int ETH_HEADER_LEN = 14;
- private static final int ETH_DEST_ADDR_OFFSET = 0;
- private static final int ETH_ETHERTYPE_OFFSET = 12;
- private static final int ETH_TYPE_MIN = 0x0600;
- private static final int ETH_TYPE_MAX = 0xFFFF;
- private static final byte[] ETH_BROADCAST_MAC_ADDRESS =
- {(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
- // TODO: Make these offsets relative to end of link-layer header; don't include ETH_HEADER_LEN.
- private static final int IPV4_FRAGMENT_OFFSET_OFFSET = ETH_HEADER_LEN + 6;
- // Endianness is not an issue for this constant because the APF interpreter always operates in
- // network byte order.
- private static final int IPV4_FRAGMENT_OFFSET_MASK = 0x1fff;
- private static final int IPV4_PROTOCOL_OFFSET = ETH_HEADER_LEN + 9;
- private static final int IPV4_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 16;
- private static final int IPV4_ANY_HOST_ADDRESS = 0;
- private static final int IPV4_BROADCAST_ADDRESS = -1; // 255.255.255.255
-
- // Traffic class and Flow label are not byte aligned. Luckily we
- // don't care about either value so we'll consider bytes 1-3 of the
- // IPv6 header as don't care.
- private static final int IPV6_FLOW_LABEL_OFFSET = ETH_HEADER_LEN + 1;
- private static final int IPV6_FLOW_LABEL_LEN = 3;
- private static final int IPV6_NEXT_HEADER_OFFSET = ETH_HEADER_LEN + 6;
- private static final int IPV6_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 8;
- private static final int IPV6_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 24;
- private static final int IPV6_HEADER_LEN = 40;
- // The IPv6 all nodes address ff02::1
- private static final byte[] IPV6_ALL_NODES_ADDRESS =
- { (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
-
- private static final int ICMP6_TYPE_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN;
-
- // NOTE: this must be added to the IPv4 header length in IPV4_HEADER_SIZE_MEMORY_SLOT
- private static final int UDP_DESTINATION_PORT_OFFSET = ETH_HEADER_LEN + 2;
- private static final int UDP_HEADER_LEN = 8;
-
- private static final int DHCP_CLIENT_PORT = 68;
- // NOTE: this must be added to the IPv4 header length in IPV4_HEADER_SIZE_MEMORY_SLOT
- private static final int DHCP_CLIENT_MAC_OFFSET = ETH_HEADER_LEN + UDP_HEADER_LEN + 28;
-
- private static final int ARP_HEADER_OFFSET = ETH_HEADER_LEN;
- private static final byte[] ARP_IPV4_HEADER = {
- 0, 1, // Hardware type: Ethernet (1)
- 8, 0, // Protocol type: IP (0x0800)
- 6, // Hardware size: 6
- 4, // Protocol size: 4
- };
- private static final int ARP_OPCODE_OFFSET = ARP_HEADER_OFFSET + 6;
- // Opcode: ARP request (0x0001), ARP reply (0x0002)
- private static final short ARP_OPCODE_REQUEST = 1;
- private static final short ARP_OPCODE_REPLY = 2;
- private static final int ARP_SOURCE_IP_ADDRESS_OFFSET = ARP_HEADER_OFFSET + 14;
- private static final int ARP_TARGET_IP_ADDRESS_OFFSET = ARP_HEADER_OFFSET + 24;
- // Do not log ApfProgramEvents whose actual lifetimes was less than this.
- private static final int APF_PROGRAM_EVENT_LIFETIME_THRESHOLD = 2;
- // Limit on the Black List size to cap on program usage for this
- // TODO: Select a proper max length
- private static final int APF_MAX_ETH_TYPE_BLACK_LIST_LEN = 20;
-
- private final ApfCapabilities mApfCapabilities;
- private final IpClientCallbacks mIpClientCallback;
- private final InterfaceParams mInterfaceParams;
- private final IpConnectivityLog mMetricsLog;
-
- @VisibleForTesting
- byte[] mHardwareAddress;
- @VisibleForTesting
- ReceiveThread mReceiveThread;
- @GuardedBy("this")
- private long mUniqueCounter;
- @GuardedBy("this")
- private boolean mMulticastFilter;
- @GuardedBy("this")
- private boolean mInDozeMode;
- private final boolean mDrop802_3Frames;
- private final int[] mEthTypeBlackList;
-
- // Detects doze mode state transitions.
- private final BroadcastReceiver mDeviceIdleReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action.equals(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)) {
- PowerManager powerManager =
- (PowerManager) context.getSystemService(Context.POWER_SERVICE);
- final boolean deviceIdle = powerManager.isDeviceIdleMode();
- setDozeMode(deviceIdle);
- }
- }
- };
- private final Context mContext;
-
- // Our IPv4 address, if we have just one, otherwise null.
- @GuardedBy("this")
- private byte[] mIPv4Address;
- // The subnet prefix length of our IPv4 network. Only valid if mIPv4Address is not null.
- @GuardedBy("this")
- private int mIPv4PrefixLength;
-
- @VisibleForTesting
- ApfFilter(Context context, ApfConfiguration config, InterfaceParams ifParams,
- IpClientCallbacks ipClientCallback, IpConnectivityLog log) {
- mApfCapabilities = config.apfCapabilities;
- mIpClientCallback = ipClientCallback;
- mInterfaceParams = ifParams;
- mMulticastFilter = config.multicastFilter;
- mDrop802_3Frames = config.ieee802_3Filter;
- mContext = context;
-
- if (mApfCapabilities.hasDataAccess()) {
- mCountAndPassLabel = "countAndPass";
- mCountAndDropLabel = "countAndDrop";
- } else {
- // APFv4 unsupported: turn jumps to the counter trampolines to immediately PASS or DROP,
- // preserving the original pre-APFv4 behavior.
- mCountAndPassLabel = ApfGenerator.PASS_LABEL;
- mCountAndDropLabel = ApfGenerator.DROP_LABEL;
- }
-
- // Now fill the black list from the passed array
- mEthTypeBlackList = filterEthTypeBlackList(config.ethTypeBlackList);
-
- mMetricsLog = log;
-
- // TODO: ApfFilter should not generate programs until IpClient sends provisioning success.
- maybeStartFilter();
-
- // Listen for doze-mode transition changes to enable/disable the IPv6 multicast filter.
- mContext.registerReceiver(mDeviceIdleReceiver,
- new IntentFilter(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED));
- }
-
- public synchronized void setDataSnapshot(byte[] data) {
- mDataSnapshot = data;
- }
-
- private void log(String s) {
- Log.d(TAG, "(" + mInterfaceParams.name + "): " + s);
- }
-
- @GuardedBy("this")
- private long getUniqueNumberLocked() {
- return mUniqueCounter++;
- }
-
- @GuardedBy("this")
- private static int[] filterEthTypeBlackList(int[] ethTypeBlackList) {
- ArrayList<Integer> bl = new ArrayList<Integer>();
-
- for (int p : ethTypeBlackList) {
- // Check if the protocol is a valid ether type
- if ((p < ETH_TYPE_MIN) || (p > ETH_TYPE_MAX)) {
- continue;
- }
-
- // Check if the protocol is not repeated in the passed array
- if (bl.contains(p)) {
- continue;
- }
-
- // Check if list reach its max size
- if (bl.size() == APF_MAX_ETH_TYPE_BLACK_LIST_LEN) {
- Log.w(TAG, "Passed EthType Black List size too large (" + bl.size() +
- ") using top " + APF_MAX_ETH_TYPE_BLACK_LIST_LEN + " protocols");
- break;
- }
-
- // Now add the protocol to the list
- bl.add(p);
- }
-
- return bl.stream().mapToInt(Integer::intValue).toArray();
- }
-
- /**
- * Attempt to start listening for RAs and, if RAs are received, generating and installing
- * filters to ignore useless RAs.
- */
- @VisibleForTesting
- void maybeStartFilter() {
- FileDescriptor socket;
- try {
- mHardwareAddress = mInterfaceParams.macAddr.toByteArray();
- synchronized(this) {
- // Clear the APF memory to reset all counters upon connecting to the first AP
- // in an SSID. This is limited to APFv4 devices because this large write triggers
- // a crash on some older devices (b/78905546).
- if (mApfCapabilities.hasDataAccess()) {
- byte[] zeroes = new byte[mApfCapabilities.maximumApfProgramSize];
- mIpClientCallback.installPacketFilter(zeroes);
- }
-
- // Install basic filters
- installNewProgramLocked();
- }
- socket = Os.socket(AF_PACKET, SOCK_RAW, ETH_P_IPV6);
- SocketAddress addr = makePacketSocketAddress(
- (short) ETH_P_IPV6, mInterfaceParams.index);
- Os.bind(socket, addr);
- NetworkUtils.attachRaFilter(socket, mApfCapabilities.apfPacketFormat);
- } catch(SocketException|ErrnoException e) {
- Log.e(TAG, "Error starting filter", e);
- return;
- }
- mReceiveThread = new ReceiveThread(socket);
- mReceiveThread.start();
- }
-
- // Returns seconds since device boot.
- @VisibleForTesting
- protected long currentTimeSeconds() {
- return SystemClock.elapsedRealtime() / DateUtils.SECOND_IN_MILLIS;
- }
-
- public static class InvalidRaException extends Exception {
- public InvalidRaException(String m) {
- super(m);
- }
- }
-
- // A class to hold information about an RA.
- @VisibleForTesting
- class Ra {
- // From RFC4861:
- private static final int ICMP6_RA_HEADER_LEN = 16;
- private static final int ICMP6_RA_CHECKSUM_OFFSET =
- ETH_HEADER_LEN + IPV6_HEADER_LEN + 2;
- private static final int ICMP6_RA_CHECKSUM_LEN = 2;
- private static final int ICMP6_RA_OPTION_OFFSET =
- ETH_HEADER_LEN + IPV6_HEADER_LEN + ICMP6_RA_HEADER_LEN;
- private static final int ICMP6_RA_ROUTER_LIFETIME_OFFSET =
- ETH_HEADER_LEN + IPV6_HEADER_LEN + 6;
- private static final int ICMP6_RA_ROUTER_LIFETIME_LEN = 2;
- // Prefix information option.
- private static final int ICMP6_PREFIX_OPTION_TYPE = 3;
- private static final int ICMP6_PREFIX_OPTION_LEN = 32;
- private static final int ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET = 4;
- private static final int ICMP6_PREFIX_OPTION_VALID_LIFETIME_LEN = 4;
- private static final int ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET = 8;
- private static final int ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_LEN = 4;
-
- // From RFC6106: Recursive DNS Server option
- private static final int ICMP6_RDNSS_OPTION_TYPE = 25;
- // From RFC6106: DNS Search List option
- private static final int ICMP6_DNSSL_OPTION_TYPE = 31;
-
- // From RFC4191: Route Information option
- private static final int ICMP6_ROUTE_INFO_OPTION_TYPE = 24;
- // Above three options all have the same format:
- private static final int ICMP6_4_BYTE_LIFETIME_OFFSET = 4;
- private static final int ICMP6_4_BYTE_LIFETIME_LEN = 4;
-
- // Note: mPacket's position() cannot be assumed to be reset.
- private final ByteBuffer mPacket;
- // List of binary ranges that include the whole packet except the lifetimes.
- // Pairs consist of offset and length.
- private final ArrayList<Pair<Integer, Integer>> mNonLifetimes =
- new ArrayList<Pair<Integer, Integer>>();
- // Minimum lifetime in packet
- long mMinLifetime;
- // When the packet was last captured, in seconds since Unix Epoch
- long mLastSeen;
-
- // For debugging only. Offsets into the packet where PIOs are.
- private final ArrayList<Integer> mPrefixOptionOffsets = new ArrayList<>();
-
- // For debugging only. Offsets into the packet where RDNSS options are.
- private final ArrayList<Integer> mRdnssOptionOffsets = new ArrayList<>();
-
- // For debugging only. How many times this RA was seen.
- int seenCount = 0;
-
- // For debugging only. Returns the hex representation of the last matching packet.
- String getLastMatchingPacket() {
- return HexDump.toHexString(mPacket.array(), 0, mPacket.capacity(),
- false /* lowercase */);
- }
-
- // For debugging only. Returns the string representation of the IPv6 address starting at
- // position pos in the packet.
- private String IPv6AddresstoString(int pos) {
- try {
- byte[] array = mPacket.array();
- // Can't just call copyOfRange() and see if it throws, because if it reads past the
- // end it pads with zeros instead of throwing.
- if (pos < 0 || pos + 16 > array.length || pos + 16 < pos) {
- return "???";
- }
- byte[] addressBytes = Arrays.copyOfRange(array, pos, pos + 16);
- InetAddress address = (Inet6Address) InetAddress.getByAddress(addressBytes);
- return address.getHostAddress();
- } catch (UnsupportedOperationException e) {
- // array() failed. Cannot happen, mPacket is array-backed and read-write.
- return "???";
- } catch (ClassCastException|UnknownHostException e) {
- // Cannot happen.
- return "???";
- }
- }
-
- // Can't be static because it's in a non-static inner class.
- // TODO: Make this static once RA is its own class.
- private void prefixOptionToString(StringBuffer sb, int offset) {
- String prefix = IPv6AddresstoString(offset + 16);
- int length = getUint8(mPacket, offset + 2);
- long valid = getUint32(mPacket, offset + 4);
- long preferred = getUint32(mPacket, offset + 8);
- sb.append(String.format("%s/%d %ds/%ds ", prefix, length, valid, preferred));
- }
-
- private void rdnssOptionToString(StringBuffer sb, int offset) {
- int optLen = getUint8(mPacket, offset + 1) * 8;
- if (optLen < 24) return; // Malformed or empty.
- long lifetime = getUint32(mPacket, offset + 4);
- int numServers = (optLen - 8) / 16;
- sb.append("DNS ").append(lifetime).append("s");
- for (int server = 0; server < numServers; server++) {
- sb.append(" ").append(IPv6AddresstoString(offset + 8 + 16 * server));
- }
- }
-
- public String toString() {
- try {
- StringBuffer sb = new StringBuffer();
- sb.append(String.format("RA %s -> %s %ds ",
- IPv6AddresstoString(IPV6_SRC_ADDR_OFFSET),
- IPv6AddresstoString(IPV6_DEST_ADDR_OFFSET),
- getUint16(mPacket, ICMP6_RA_ROUTER_LIFETIME_OFFSET)));
- for (int i: mPrefixOptionOffsets) {
- prefixOptionToString(sb, i);
- }
- for (int i: mRdnssOptionOffsets) {
- rdnssOptionToString(sb, i);
- }
- return sb.toString();
- } catch (BufferUnderflowException|IndexOutOfBoundsException e) {
- return "<Malformed RA>";
- }
- }
-
- /**
- * Add a binary range of the packet that does not include a lifetime to mNonLifetimes.
- * Assumes mPacket.position() is as far as we've parsed the packet.
- * @param lastNonLifetimeStart offset within packet of where the last binary range of
- * data not including a lifetime.
- * @param lifetimeOffset offset from mPacket.position() to the next lifetime data.
- * @param lifetimeLength length of the next lifetime data.
- * @return offset within packet of where the next binary range of data not including
- * a lifetime. This can be passed into the next invocation of this function
- * via {@code lastNonLifetimeStart}.
- */
- private int addNonLifetime(int lastNonLifetimeStart, int lifetimeOffset,
- int lifetimeLength) {
- lifetimeOffset += mPacket.position();
- mNonLifetimes.add(new Pair<Integer, Integer>(lastNonLifetimeStart,
- lifetimeOffset - lastNonLifetimeStart));
- return lifetimeOffset + lifetimeLength;
- }
-
- private int addNonLifetimeU32(int lastNonLifetimeStart) {
- return addNonLifetime(lastNonLifetimeStart,
- ICMP6_4_BYTE_LIFETIME_OFFSET, ICMP6_4_BYTE_LIFETIME_LEN);
- }
-
- // Note that this parses RA and may throw InvalidRaException (from
- // Buffer.position(int) or due to an invalid-length option) or IndexOutOfBoundsException
- // (from ByteBuffer.get(int) ) if parsing encounters something non-compliant with
- // specifications.
- Ra(byte[] packet, int length) throws InvalidRaException {
- if (length < ICMP6_RA_OPTION_OFFSET) {
- throw new InvalidRaException("Not an ICMP6 router advertisement");
- }
-
- mPacket = ByteBuffer.wrap(Arrays.copyOf(packet, length));
- mLastSeen = currentTimeSeconds();
-
- // Sanity check packet in case a packet arrives before we attach RA filter
- // to our packet socket. b/29586253
- if (getUint16(mPacket, ETH_ETHERTYPE_OFFSET) != ETH_P_IPV6 ||
- getUint8(mPacket, IPV6_NEXT_HEADER_OFFSET) != IPPROTO_ICMPV6 ||
- getUint8(mPacket, ICMP6_TYPE_OFFSET) != ICMPV6_ROUTER_ADVERTISEMENT) {
- throw new InvalidRaException("Not an ICMP6 router advertisement");
- }
-
-
- RaEvent.Builder builder = new RaEvent.Builder();
-
- // Ignore the flow label and low 4 bits of traffic class.
- int lastNonLifetimeStart = addNonLifetime(0,
- IPV6_FLOW_LABEL_OFFSET,
- IPV6_FLOW_LABEL_LEN);
-
- // Ignore the checksum.
- lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
- ICMP6_RA_CHECKSUM_OFFSET,
- ICMP6_RA_CHECKSUM_LEN);
-
- // Parse router lifetime
- lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
- ICMP6_RA_ROUTER_LIFETIME_OFFSET,
- ICMP6_RA_ROUTER_LIFETIME_LEN);
- builder.updateRouterLifetime(getUint16(mPacket, ICMP6_RA_ROUTER_LIFETIME_OFFSET));
-
- // Ensures that the RA is not truncated.
- mPacket.position(ICMP6_RA_OPTION_OFFSET);
- while (mPacket.hasRemaining()) {
- final int position = mPacket.position();
- final int optionType = getUint8(mPacket, position);
- final int optionLength = getUint8(mPacket, position + 1) * 8;
- long lifetime;
- switch (optionType) {
- case ICMP6_PREFIX_OPTION_TYPE:
- // Parse valid lifetime
- lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
- ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET,
- ICMP6_PREFIX_OPTION_VALID_LIFETIME_LEN);
- lifetime = getUint32(mPacket,
- position + ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET);
- builder.updatePrefixValidLifetime(lifetime);
- // Parse preferred lifetime
- lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
- ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET,
- ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_LEN);
- lifetime = getUint32(mPacket,
- position + ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET);
- builder.updatePrefixPreferredLifetime(lifetime);
- mPrefixOptionOffsets.add(position);
- break;
- // These three options have the same lifetime offset and size, and
- // are processed with the same specialized addNonLifetimeU32:
- case ICMP6_RDNSS_OPTION_TYPE:
- mRdnssOptionOffsets.add(position);
- lastNonLifetimeStart = addNonLifetimeU32(lastNonLifetimeStart);
- lifetime = getUint32(mPacket, position + ICMP6_4_BYTE_LIFETIME_OFFSET);
- builder.updateRdnssLifetime(lifetime);
- break;
- case ICMP6_ROUTE_INFO_OPTION_TYPE:
- lastNonLifetimeStart = addNonLifetimeU32(lastNonLifetimeStart);
- lifetime = getUint32(mPacket, position + ICMP6_4_BYTE_LIFETIME_OFFSET);
- builder.updateRouteInfoLifetime(lifetime);
- break;
- case ICMP6_DNSSL_OPTION_TYPE:
- lastNonLifetimeStart = addNonLifetimeU32(lastNonLifetimeStart);
- lifetime = getUint32(mPacket, position + ICMP6_4_BYTE_LIFETIME_OFFSET);
- builder.updateDnsslLifetime(lifetime);
- break;
- default:
- // RFC4861 section 4.2 dictates we ignore unknown options for fowards
- // compatibility.
- break;
- }
- if (optionLength <= 0) {
- throw new InvalidRaException(String.format(
- "Invalid option length opt=%d len=%d", optionType, optionLength));
- }
- mPacket.position(position + optionLength);
- }
- // Mark non-lifetime bytes since last lifetime.
- addNonLifetime(lastNonLifetimeStart, 0, 0);
- mMinLifetime = minLifetime(packet, length);
- mMetricsLog.log(builder.build());
- }
-
- // Ignoring lifetimes (which may change) does {@code packet} match this RA?
- boolean matches(byte[] packet, int length) {
- if (length != mPacket.capacity()) return false;
- byte[] referencePacket = mPacket.array();
- for (Pair<Integer, Integer> nonLifetime : mNonLifetimes) {
- for (int i = nonLifetime.first; i < (nonLifetime.first + nonLifetime.second); i++) {
- if (packet[i] != referencePacket[i]) return false;
- }
- }
- return true;
- }
-
- // What is the minimum of all lifetimes within {@code packet} in seconds?
- // Precondition: matches(packet, length) already returned true.
- long minLifetime(byte[] packet, int length) {
- long minLifetime = Long.MAX_VALUE;
- // Wrap packet in ByteBuffer so we can read big-endian values easily
- ByteBuffer byteBuffer = ByteBuffer.wrap(packet);
- for (int i = 0; (i + 1) < mNonLifetimes.size(); i++) {
- int offset = mNonLifetimes.get(i).first + mNonLifetimes.get(i).second;
-
- // The flow label is in mNonLifetimes, but it's not a lifetime.
- if (offset == IPV6_FLOW_LABEL_OFFSET) {
- continue;
- }
-
- // The checksum is in mNonLifetimes, but it's not a lifetime.
- if (offset == ICMP6_RA_CHECKSUM_OFFSET) {
- continue;
- }
-
- final int lifetimeLength = mNonLifetimes.get(i+1).first - offset;
- final long optionLifetime;
- switch (lifetimeLength) {
- case 2:
- optionLifetime = getUint16(byteBuffer, offset);
- break;
- case 4:
- optionLifetime = getUint32(byteBuffer, offset);
- break;
- default:
- throw new IllegalStateException("bogus lifetime size " + lifetimeLength);
- }
- minLifetime = Math.min(minLifetime, optionLifetime);
- }
- return minLifetime;
- }
-
- // How many seconds does this RA's have to live, taking into account the fact
- // that we might have seen it a while ago.
- long currentLifetime() {
- return mMinLifetime - (currentTimeSeconds() - mLastSeen);
- }
-
- boolean isExpired() {
- // TODO: We may want to handle 0 lifetime RAs differently, if they are common. We'll
- // have to calculte the filter lifetime specially as a fraction of 0 is still 0.
- return currentLifetime() <= 0;
- }
-
- // Append a filter for this RA to {@code gen}. Jump to DROP_LABEL if it should be dropped.
- // Jump to the next filter if packet doesn't match this RA.
- @GuardedBy("ApfFilter.this")
- long generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
- String nextFilterLabel = "Ra" + getUniqueNumberLocked();
- // Skip if packet is not the right size
- gen.addLoadFromMemory(Register.R0, gen.PACKET_SIZE_MEMORY_SLOT);
- gen.addJumpIfR0NotEquals(mPacket.capacity(), nextFilterLabel);
- int filterLifetime = (int)(currentLifetime() / FRACTION_OF_LIFETIME_TO_FILTER);
- // Skip filter if expired
- gen.addLoadFromMemory(Register.R0, gen.FILTER_AGE_MEMORY_SLOT);
- gen.addJumpIfR0GreaterThan(filterLifetime, nextFilterLabel);
- for (int i = 0; i < mNonLifetimes.size(); i++) {
- // Generate code to match the packet bytes
- Pair<Integer, Integer> nonLifetime = mNonLifetimes.get(i);
- // Don't generate JNEBS instruction for 0 bytes as it always fails the
- // ASSERT_FORWARD_IN_PROGRAM(pc + cmp_imm - 1) check where cmp_imm is
- // the number of bytes to compare. nonLifetime is zero between the
- // valid and preferred lifetimes in the prefix option.
- if (nonLifetime.second != 0) {
- gen.addLoadImmediate(Register.R0, nonLifetime.first);
- gen.addJumpIfBytesNotEqual(Register.R0,
- Arrays.copyOfRange(mPacket.array(), nonLifetime.first,
- nonLifetime.first + nonLifetime.second),
- nextFilterLabel);
- }
- // Generate code to test the lifetimes haven't gone down too far
- if ((i + 1) < mNonLifetimes.size()) {
- Pair<Integer, Integer> nextNonLifetime = mNonLifetimes.get(i + 1);
- int offset = nonLifetime.first + nonLifetime.second;
-
- // Skip the Flow label.
- if (offset == IPV6_FLOW_LABEL_OFFSET) {
- continue;
- }
- // Skip the checksum.
- if (offset == ICMP6_RA_CHECKSUM_OFFSET) {
- continue;
- }
- int length = nextNonLifetime.first - offset;
- switch (length) {
- case 4: gen.addLoad32(Register.R0, offset); break;
- case 2: gen.addLoad16(Register.R0, offset); break;
- default: throw new IllegalStateException("bogus lifetime size " + length);
- }
- gen.addJumpIfR0LessThan(filterLifetime, nextFilterLabel);
- }
- }
- maybeSetupCounter(gen, Counter.DROPPED_RA);
- gen.addJump(mCountAndDropLabel);
- gen.defineLabel(nextFilterLabel);
- return filterLifetime;
- }
- }
-
- // Maximum number of RAs to filter for.
- private static final int MAX_RAS = 10;
-
- @GuardedBy("this")
- private ArrayList<Ra> mRas = new ArrayList<Ra>();
-
- // There is always some marginal benefit to updating the installed APF program when an RA is
- // seen because we can extend the program's lifetime slightly, but there is some cost to
- // updating the program, so don't bother unless the program is going to expire soon. This
- // constant defines "soon" in seconds.
- private static final long MAX_PROGRAM_LIFETIME_WORTH_REFRESHING = 30;
- // We don't want to filter an RA for it's whole lifetime as it'll be expired by the time we ever
- // see a refresh. Using half the lifetime might be a good idea except for the fact that
- // packets may be dropped, so let's use 6.
- private static final int FRACTION_OF_LIFETIME_TO_FILTER = 6;
-
- // When did we last install a filter program? In seconds since Unix Epoch.
- @GuardedBy("this")
- private long mLastTimeInstalledProgram;
- // How long should the last installed filter program live for? In seconds.
- @GuardedBy("this")
- private long mLastInstalledProgramMinLifetime;
- @GuardedBy("this")
- private ApfProgramEvent.Builder mLastInstallEvent;
-
- // For debugging only. The last program installed.
- @GuardedBy("this")
- private byte[] mLastInstalledProgram;
-
- /**
- * For debugging only. Contains the latest APF buffer snapshot captured from the firmware.
- *
- * A typical size for this buffer is 4KB. It is present only if the WiFi HAL supports
- * IWifiStaIface#readApfPacketFilterData(), and the APF interpreter advertised support for
- * the opcodes to access the data buffer (LDDW and STDW).
- */
- @GuardedBy("this") @Nullable
- private byte[] mDataSnapshot;
-
- // How many times the program was updated since we started.
- @GuardedBy("this")
- private int mNumProgramUpdates = 0;
- // How many times the program was updated since we started for allowing multicast traffic.
- @GuardedBy("this")
- private int mNumProgramUpdatesAllowingMulticast = 0;
-
- /**
- * Generate filter code to process ARP packets. Execution of this code ends in either the
- * DROP_LABEL or PASS_LABEL and does not fall off the end.
- * Preconditions:
- * - Packet being filtered is ARP
- */
- @GuardedBy("this")
- private void generateArpFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
- // Here's a basic summary of what the ARP filter program does:
- //
- // if not ARP IPv4
- // pass
- // if not ARP IPv4 reply or request
- // pass
- // if ARP reply source ip is 0.0.0.0
- // drop
- // if unicast ARP reply
- // pass
- // if interface has no IPv4 address
- // if target ip is 0.0.0.0
- // drop
- // else
- // if target ip is not the interface ip
- // drop
- // pass
-
- final String checkTargetIPv4 = "checkTargetIPv4";
-
- // Pass if not ARP IPv4.
- gen.addLoadImmediate(Register.R0, ARP_HEADER_OFFSET);
- maybeSetupCounter(gen, Counter.PASSED_ARP_NON_IPV4);
- gen.addJumpIfBytesNotEqual(Register.R0, ARP_IPV4_HEADER, mCountAndPassLabel);
-
- // Pass if unknown ARP opcode.
- gen.addLoad16(Register.R0, ARP_OPCODE_OFFSET);
- gen.addJumpIfR0Equals(ARP_OPCODE_REQUEST, checkTargetIPv4); // Skip to unicast check
- maybeSetupCounter(gen, Counter.PASSED_ARP_UNKNOWN);
- gen.addJumpIfR0NotEquals(ARP_OPCODE_REPLY, mCountAndPassLabel);
-
- // Drop if ARP reply source IP is 0.0.0.0
- gen.addLoad32(Register.R0, ARP_SOURCE_IP_ADDRESS_OFFSET);
- maybeSetupCounter(gen, Counter.DROPPED_ARP_REPLY_SPA_NO_HOST);
- gen.addJumpIfR0Equals(IPV4_ANY_HOST_ADDRESS, mCountAndDropLabel);
-
- // Pass if unicast reply.
- gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET);
- maybeSetupCounter(gen, Counter.PASSED_ARP_UNICAST_REPLY);
- gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, mCountAndPassLabel);
-
- // Either a unicast request, a unicast reply, or a broadcast reply.
- gen.defineLabel(checkTargetIPv4);
- if (mIPv4Address == null) {
- // When there is no IPv4 address, drop GARP replies (b/29404209).
- gen.addLoad32(Register.R0, ARP_TARGET_IP_ADDRESS_OFFSET);
- maybeSetupCounter(gen, Counter.DROPPED_GARP_REPLY);
- gen.addJumpIfR0Equals(IPV4_ANY_HOST_ADDRESS, mCountAndDropLabel);
- } else {
- // When there is an IPv4 address, drop unicast/broadcast requests
- // and broadcast replies with a different target IPv4 address.
- gen.addLoadImmediate(Register.R0, ARP_TARGET_IP_ADDRESS_OFFSET);
- maybeSetupCounter(gen, Counter.DROPPED_ARP_OTHER_HOST);
- gen.addJumpIfBytesNotEqual(Register.R0, mIPv4Address, mCountAndDropLabel);
- }
-
- maybeSetupCounter(gen, Counter.PASSED_ARP);
- gen.addJump(mCountAndPassLabel);
- }
-
- /**
- * Generate filter code to process IPv4 packets. Execution of this code ends in either the
- * DROP_LABEL or PASS_LABEL and does not fall off the end.
- * Preconditions:
- * - Packet being filtered is IPv4
- */
- @GuardedBy("this")
- private void generateIPv4FilterLocked(ApfGenerator gen) throws IllegalInstructionException {
- // Here's a basic summary of what the IPv4 filter program does:
- //
- // if filtering multicast (i.e. multicast lock not held):
- // if it's DHCP destined to our MAC:
- // pass
- // if it's L2 broadcast:
- // drop
- // if it's IPv4 multicast:
- // drop
- // if it's IPv4 broadcast:
- // drop
- // pass
-
- if (mMulticastFilter) {
- final String skipDhcpv4Filter = "skip_dhcp_v4_filter";
-
- // Pass DHCP addressed to us.
- // Check it's UDP.
- gen.addLoad8(Register.R0, IPV4_PROTOCOL_OFFSET);
- gen.addJumpIfR0NotEquals(IPPROTO_UDP, skipDhcpv4Filter);
- // Check it's not a fragment. This matches the BPF filter installed by the DHCP client.
- gen.addLoad16(Register.R0, IPV4_FRAGMENT_OFFSET_OFFSET);
- gen.addJumpIfR0AnyBitsSet(IPV4_FRAGMENT_OFFSET_MASK, skipDhcpv4Filter);
- // Check it's addressed to DHCP client port.
- gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
- gen.addLoad16Indexed(Register.R0, UDP_DESTINATION_PORT_OFFSET);
- gen.addJumpIfR0NotEquals(DHCP_CLIENT_PORT, skipDhcpv4Filter);
- // Check it's DHCP to our MAC address.
- gen.addLoadImmediate(Register.R0, DHCP_CLIENT_MAC_OFFSET);
- // NOTE: Relies on R1 containing IPv4 header offset.
- gen.addAddR1();
- gen.addJumpIfBytesNotEqual(Register.R0, mHardwareAddress, skipDhcpv4Filter);
- maybeSetupCounter(gen, Counter.PASSED_DHCP);
- gen.addJump(mCountAndPassLabel);
-
- // Drop all multicasts/broadcasts.
- gen.defineLabel(skipDhcpv4Filter);
-
- // If IPv4 destination address is in multicast range, drop.
- gen.addLoad8(Register.R0, IPV4_DEST_ADDR_OFFSET);
- gen.addAnd(0xf0);
- maybeSetupCounter(gen, Counter.DROPPED_IPV4_MULTICAST);
- gen.addJumpIfR0Equals(0xe0, mCountAndDropLabel);
-
- // If IPv4 broadcast packet, drop regardless of L2 (b/30231088).
- maybeSetupCounter(gen, Counter.DROPPED_IPV4_BROADCAST_ADDR);
- gen.addLoad32(Register.R0, IPV4_DEST_ADDR_OFFSET);
- gen.addJumpIfR0Equals(IPV4_BROADCAST_ADDRESS, mCountAndDropLabel);
- if (mIPv4Address != null && mIPv4PrefixLength < 31) {
- maybeSetupCounter(gen, Counter.DROPPED_IPV4_BROADCAST_NET);
- int broadcastAddr = ipv4BroadcastAddress(mIPv4Address, mIPv4PrefixLength);
- gen.addJumpIfR0Equals(broadcastAddr, mCountAndDropLabel);
- }
-
- // If L2 broadcast packet, drop.
- // TODO: can we invert this condition to fall through to the common pass case below?
- maybeSetupCounter(gen, Counter.PASSED_IPV4_UNICAST);
- gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET);
- gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, mCountAndPassLabel);
- maybeSetupCounter(gen, Counter.DROPPED_IPV4_L2_BROADCAST);
- gen.addJump(mCountAndDropLabel);
- }
-
- // Otherwise, pass
- maybeSetupCounter(gen, Counter.PASSED_IPV4);
- gen.addJump(mCountAndPassLabel);
- }
-
-
- /**
- * Generate filter code to process IPv6 packets. Execution of this code ends in either the
- * DROP_LABEL or PASS_LABEL, or falls off the end for ICMPv6 packets.
- * Preconditions:
- * - Packet being filtered is IPv6
- */
- @GuardedBy("this")
- private void generateIPv6FilterLocked(ApfGenerator gen) throws IllegalInstructionException {
- // Here's a basic summary of what the IPv6 filter program does:
- //
- // if we're dropping multicast
- // if it's not IPCMv6 or it's ICMPv6 but we're in doze mode:
- // if it's multicast:
- // drop
- // pass
- // if it's ICMPv6 RS to any:
- // drop
- // if it's ICMPv6 NA to ff02::1:
- // drop
-
- gen.addLoad8(Register.R0, IPV6_NEXT_HEADER_OFFSET);
-
- // Drop multicast if the multicast filter is enabled.
- if (mMulticastFilter) {
- final String skipIPv6MulticastFilterLabel = "skipIPv6MulticastFilter";
- final String dropAllIPv6MulticastsLabel = "dropAllIPv6Multicast";
-
- // While in doze mode, drop ICMPv6 multicast pings, let the others pass.
- // While awake, let all ICMPv6 multicasts through.
- if (mInDozeMode) {
- // Not ICMPv6? -> Proceed to multicast filtering
- gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6, dropAllIPv6MulticastsLabel);
-
- // ICMPv6 but not ECHO? -> Skip the multicast filter.
- // (ICMPv6 ECHO requests will go through the multicast filter below).
- gen.addLoad8(Register.R0, ICMP6_TYPE_OFFSET);
- gen.addJumpIfR0NotEquals(ICMPV6_ECHO_REQUEST_TYPE, skipIPv6MulticastFilterLabel);
- } else {
- gen.addJumpIfR0Equals(IPPROTO_ICMPV6, skipIPv6MulticastFilterLabel);
- }
-
- // Drop all other packets sent to ff00::/8 (multicast prefix).
- gen.defineLabel(dropAllIPv6MulticastsLabel);
- maybeSetupCounter(gen, Counter.DROPPED_IPV6_NON_ICMP_MULTICAST);
- gen.addLoad8(Register.R0, IPV6_DEST_ADDR_OFFSET);
- gen.addJumpIfR0Equals(0xff, mCountAndDropLabel);
- // Not multicast. Pass.
- maybeSetupCounter(gen, Counter.PASSED_IPV6_UNICAST_NON_ICMP);
- gen.addJump(mCountAndPassLabel);
- gen.defineLabel(skipIPv6MulticastFilterLabel);
- } else {
- // If not ICMPv6, pass.
- maybeSetupCounter(gen, Counter.PASSED_IPV6_NON_ICMP);
- gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6, mCountAndPassLabel);
- }
-
- // If we got this far, the packet is ICMPv6. Drop some specific types.
-
- // Add unsolicited multicast neighbor announcements filter
- String skipUnsolicitedMulticastNALabel = "skipUnsolicitedMulticastNA";
- gen.addLoad8(Register.R0, ICMP6_TYPE_OFFSET);
- // Drop all router solicitations (b/32833400)
- maybeSetupCounter(gen, Counter.DROPPED_IPV6_ROUTER_SOLICITATION);
- gen.addJumpIfR0Equals(ICMPV6_ROUTER_SOLICITATION, mCountAndDropLabel);
- // If not neighbor announcements, skip filter.
- gen.addJumpIfR0NotEquals(ICMPV6_NEIGHBOR_ADVERTISEMENT, skipUnsolicitedMulticastNALabel);
- // If to ff02::1, drop.
- // TODO: Drop only if they don't contain the address of on-link neighbours.
- gen.addLoadImmediate(Register.R0, IPV6_DEST_ADDR_OFFSET);
- gen.addJumpIfBytesNotEqual(Register.R0, IPV6_ALL_NODES_ADDRESS,
- skipUnsolicitedMulticastNALabel);
- maybeSetupCounter(gen, Counter.DROPPED_IPV6_MULTICAST_NA);
- gen.addJump(mCountAndDropLabel);
- gen.defineLabel(skipUnsolicitedMulticastNALabel);
- }
-
- /**
- * Begin generating an APF program to:
- * <ul>
- * <li>Drop/Pass 802.3 frames (based on policy)
- * <li>Drop packets with EtherType within the Black List
- * <li>Drop ARP requests not for us, if mIPv4Address is set,
- * <li>Drop IPv4 broadcast packets, except DHCP destined to our MAC,
- * <li>Drop IPv4 multicast packets, if mMulticastFilter,
- * <li>Pass all other IPv4 packets,
- * <li>Drop all broadcast non-IP non-ARP packets.
- * <li>Pass all non-ICMPv6 IPv6 packets,
- * <li>Pass all non-IPv4 and non-IPv6 packets,
- * <li>Drop IPv6 ICMPv6 NAs to ff02::1.
- * <li>Drop IPv6 ICMPv6 RSs.
- * <li>Let execution continue off the end of the program for IPv6 ICMPv6 packets. This allows
- * insertion of RA filters here, or if there aren't any, just passes the packets.
- * </ul>
- */
- @GuardedBy("this")
- private ApfGenerator emitPrologueLocked() throws IllegalInstructionException {
- // This is guaranteed to succeed because of the check in maybeCreate.
- ApfGenerator gen = new ApfGenerator(mApfCapabilities.apfVersionSupported);
-
- if (mApfCapabilities.hasDataAccess()) {
- // Increment TOTAL_PACKETS
- maybeSetupCounter(gen, Counter.TOTAL_PACKETS);
- gen.addLoadData(Register.R0, 0); // load counter
- gen.addAdd(1);
- gen.addStoreData(Register.R0, 0); // write-back counter
- }
-
- // Here's a basic summary of what the initial program does:
- //
- // if it's a 802.3 Frame (ethtype < 0x0600):
- // drop or pass based on configurations
- // if it has a ether-type that belongs to the black list
- // drop
- // if it's ARP:
- // insert ARP filter to drop or pass these appropriately
- // if it's IPv4:
- // insert IPv4 filter to drop or pass these appropriately
- // if it's not IPv6:
- // if it's broadcast:
- // drop
- // pass
- // insert IPv6 filter to drop, pass, or fall off the end for ICMPv6 packets
-
- gen.addLoad16(Register.R0, ETH_ETHERTYPE_OFFSET);
-
- if (mDrop802_3Frames) {
- // drop 802.3 frames (ethtype < 0x0600)
- maybeSetupCounter(gen, Counter.DROPPED_802_3_FRAME);
- gen.addJumpIfR0LessThan(ETH_TYPE_MIN, mCountAndDropLabel);
- }
-
- // Handle ether-type black list
- maybeSetupCounter(gen, Counter.DROPPED_ETHERTYPE_BLACKLISTED);
- for (int p : mEthTypeBlackList) {
- gen.addJumpIfR0Equals(p, mCountAndDropLabel);
- }
-
- // Add ARP filters:
- String skipArpFiltersLabel = "skipArpFilters";
- gen.addJumpIfR0NotEquals(ETH_P_ARP, skipArpFiltersLabel);
- generateArpFilterLocked(gen);
- gen.defineLabel(skipArpFiltersLabel);
-
- // Add IPv4 filters:
- String skipIPv4FiltersLabel = "skipIPv4Filters";
- // NOTE: Relies on R0 containing ethertype. This is safe because if we got here, we did not
- // execute the ARP filter, since that filter does not fall through, but either drops or
- // passes.
- gen.addJumpIfR0NotEquals(ETH_P_IP, skipIPv4FiltersLabel);
- generateIPv4FilterLocked(gen);
- gen.defineLabel(skipIPv4FiltersLabel);
-
- // Check for IPv6:
- // NOTE: Relies on R0 containing ethertype. This is safe because if we got here, we did not
- // execute the ARP or IPv4 filters, since those filters do not fall through, but either
- // drop or pass.
- String ipv6FilterLabel = "IPv6Filters";
- gen.addJumpIfR0Equals(ETH_P_IPV6, ipv6FilterLabel);
-
- // Drop non-IP non-ARP broadcasts, pass the rest
- gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET);
- maybeSetupCounter(gen, Counter.PASSED_NON_IP_UNICAST);
- gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, mCountAndPassLabel);
- maybeSetupCounter(gen, Counter.DROPPED_ETH_BROADCAST);
- gen.addJump(mCountAndDropLabel);
-
- // Add IPv6 filters:
- gen.defineLabel(ipv6FilterLabel);
- generateIPv6FilterLocked(gen);
- return gen;
- }
-
- /**
- * Append packet counting epilogue to the APF program.
- *
- * Currently, the epilogue consists of two trampolines which count passed and dropped packets
- * before jumping to the actual PASS and DROP labels.
- */
- @GuardedBy("this")
- private void emitEpilogue(ApfGenerator gen) throws IllegalInstructionException {
- // If APFv4 is unsupported, no epilogue is necessary: if execution reached this far, it
- // will just fall-through to the PASS label.
- if (!mApfCapabilities.hasDataAccess()) return;
-
- // Execution will reach the bottom of the program if none of the filters match,
- // which will pass the packet to the application processor.
- maybeSetupCounter(gen, Counter.PASSED_IPV6_ICMP);
-
- // Append the count & pass trampoline, which increments the counter at the data address
- // pointed to by R1, then jumps to the pass label. This saves a few bytes over inserting
- // the entire sequence inline for every counter.
- gen.defineLabel(mCountAndPassLabel);
- gen.addLoadData(Register.R0, 0); // R0 = *(R1 + 0)
- gen.addAdd(1); // R0++
- gen.addStoreData(Register.R0, 0); // *(R1 + 0) = R0
- gen.addJump(gen.PASS_LABEL);
-
- // Same as above for the count & drop trampoline.
- gen.defineLabel(mCountAndDropLabel);
- gen.addLoadData(Register.R0, 0); // R0 = *(R1 + 0)
- gen.addAdd(1); // R0++
- gen.addStoreData(Register.R0, 0); // *(R1 + 0) = R0
- gen.addJump(gen.DROP_LABEL);
- }
-
- /**
- * Generate and install a new filter program.
- */
- @GuardedBy("this")
- @VisibleForTesting
- void installNewProgramLocked() {
- purgeExpiredRasLocked();
- ArrayList<Ra> rasToFilter = new ArrayList<>();
- final byte[] program;
- long programMinLifetime = Long.MAX_VALUE;
- long maximumApfProgramSize = mApfCapabilities.maximumApfProgramSize;
- if (mApfCapabilities.hasDataAccess()) {
- // Reserve space for the counters.
- maximumApfProgramSize -= Counter.totalSize();
- }
-
- try {
- // Step 1: Determine how many RA filters we can fit in the program.
- ApfGenerator gen = emitPrologueLocked();
-
- // The epilogue normally goes after the RA filters, but add it early to include its
- // length when estimating the total.
- emitEpilogue(gen);
-
- // Can't fit the program even without any RA filters?
- if (gen.programLengthOverEstimate() > maximumApfProgramSize) {
- Log.e(TAG, "Program exceeds maximum size " + maximumApfProgramSize);
- return;
- }
-
- for (Ra ra : mRas) {
- ra.generateFilterLocked(gen);
- // Stop if we get too big.
- if (gen.programLengthOverEstimate() > maximumApfProgramSize) break;
- rasToFilter.add(ra);
- }
-
- // Step 2: Actually generate the program
- gen = emitPrologueLocked();
- for (Ra ra : rasToFilter) {
- programMinLifetime = Math.min(programMinLifetime, ra.generateFilterLocked(gen));
- }
- emitEpilogue(gen);
- program = gen.generate();
- } catch (IllegalInstructionException|IllegalStateException e) {
- Log.e(TAG, "Failed to generate APF program.", e);
- return;
- }
- final long now = currentTimeSeconds();
- mLastTimeInstalledProgram = now;
- mLastInstalledProgramMinLifetime = programMinLifetime;
- mLastInstalledProgram = program;
- mNumProgramUpdates++;
-
- if (VDBG) {
- hexDump("Installing filter: ", program, program.length);
- }
- mIpClientCallback.installPacketFilter(program);
- logApfProgramEventLocked(now);
- mLastInstallEvent = new ApfProgramEvent.Builder()
- .setLifetime(programMinLifetime)
- .setFilteredRas(rasToFilter.size())
- .setCurrentRas(mRas.size())
- .setProgramLength(program.length)
- .setFlags(mIPv4Address != null, mMulticastFilter);
- }
-
- @GuardedBy("this")
- private void logApfProgramEventLocked(long now) {
- if (mLastInstallEvent == null) {
- return;
- }
- ApfProgramEvent.Builder ev = mLastInstallEvent;
- mLastInstallEvent = null;
- final long actualLifetime = now - mLastTimeInstalledProgram;
- ev.setActualLifetime(actualLifetime);
- if (actualLifetime < APF_PROGRAM_EVENT_LIFETIME_THRESHOLD) {
- return;
- }
- mMetricsLog.log(ev.build());
- }
-
- /**
- * Returns {@code true} if a new program should be installed because the current one dies soon.
- */
- private boolean shouldInstallnewProgram() {
- long expiry = mLastTimeInstalledProgram + mLastInstalledProgramMinLifetime;
- return expiry < currentTimeSeconds() + MAX_PROGRAM_LIFETIME_WORTH_REFRESHING;
- }
-
- private void hexDump(String msg, byte[] packet, int length) {
- log(msg + HexDump.toHexString(packet, 0, length, false /* lowercase */));
- }
-
- @GuardedBy("this")
- private void purgeExpiredRasLocked() {
- for (int i = 0; i < mRas.size();) {
- if (mRas.get(i).isExpired()) {
- log("Expiring " + mRas.get(i));
- mRas.remove(i);
- } else {
- i++;
- }
- }
- }
-
- /**
- * Process an RA packet, updating the list of known RAs and installing a new APF program
- * if the current APF program should be updated.
- * @return a ProcessRaResult enum describing what action was performed.
- */
- @VisibleForTesting
- synchronized ProcessRaResult processRa(byte[] packet, int length) {
- if (VDBG) hexDump("Read packet = ", packet, length);
-
- // Have we seen this RA before?
- for (int i = 0; i < mRas.size(); i++) {
- Ra ra = mRas.get(i);
- if (ra.matches(packet, length)) {
- if (VDBG) log("matched RA " + ra);
- // Update lifetimes.
- ra.mLastSeen = currentTimeSeconds();
- ra.mMinLifetime = ra.minLifetime(packet, length);
- ra.seenCount++;
-
- // Keep mRas in LRU order so as to prioritize generating filters for recently seen
- // RAs. LRU prioritizes this because RA filters are generated in order from mRas
- // until the filter program exceeds the maximum filter program size allowed by the
- // chipset, so RAs appearing earlier in mRas are more likely to make it into the
- // filter program.
- // TODO: consider sorting the RAs in order of increasing expiry time as well.
- // Swap to front of array.
- mRas.add(0, mRas.remove(i));
-
- // If the current program doesn't expire for a while, don't update.
- if (shouldInstallnewProgram()) {
- installNewProgramLocked();
- return ProcessRaResult.UPDATE_EXPIRY;
- }
- return ProcessRaResult.MATCH;
- }
- }
- purgeExpiredRasLocked();
- // TODO: figure out how to proceed when we've received more then MAX_RAS RAs.
- if (mRas.size() >= MAX_RAS) {
- return ProcessRaResult.DROPPED;
- }
- final Ra ra;
- try {
- ra = new Ra(packet, length);
- } catch (Exception e) {
- Log.e(TAG, "Error parsing RA", e);
- return ProcessRaResult.PARSE_ERROR;
- }
- // Ignore 0 lifetime RAs.
- if (ra.isExpired()) {
- return ProcessRaResult.ZERO_LIFETIME;
- }
- log("Adding " + ra);
- mRas.add(ra);
- installNewProgramLocked();
- return ProcessRaResult.UPDATE_NEW_RA;
- }
-
- /**
- * Create an {@link ApfFilter} if {@code apfCapabilities} indicates support for packet
- * filtering using APF programs.
- */
- public static ApfFilter maybeCreate(Context context, ApfConfiguration config,
- InterfaceParams ifParams, IpClientCallbacks ipClientCallback) {
- if (context == null || config == null || ifParams == null) return null;
- ApfCapabilities apfCapabilities = config.apfCapabilities;
- if (apfCapabilities == null) return null;
- if (apfCapabilities.apfVersionSupported == 0) return null;
- if (apfCapabilities.maximumApfProgramSize < 512) {
- Log.e(TAG, "Unacceptably small APF limit: " + apfCapabilities.maximumApfProgramSize);
- return null;
- }
- // For now only support generating programs for Ethernet frames. If this restriction is
- // lifted:
- // 1. the program generator will need its offsets adjusted.
- // 2. the packet filter attached to our packet socket will need its offset adjusted.
- if (apfCapabilities.apfPacketFormat != ARPHRD_ETHER) return null;
- if (!ApfGenerator.supportsVersion(apfCapabilities.apfVersionSupported)) {
- Log.e(TAG, "Unsupported APF version: " + apfCapabilities.apfVersionSupported);
- return null;
- }
-
- return new ApfFilter(context, config, ifParams, ipClientCallback, new IpConnectivityLog());
- }
-
- public synchronized void shutdown() {
- if (mReceiveThread != null) {
- log("shutting down");
- mReceiveThread.halt(); // Also closes socket.
- mReceiveThread = null;
- }
- mRas.clear();
- mContext.unregisterReceiver(mDeviceIdleReceiver);
- }
-
- public synchronized void setMulticastFilter(boolean isEnabled) {
- if (mMulticastFilter == isEnabled) return;
- mMulticastFilter = isEnabled;
- if (!isEnabled) {
- mNumProgramUpdatesAllowingMulticast++;
- }
- installNewProgramLocked();
- }
-
- @VisibleForTesting
- public synchronized void setDozeMode(boolean isEnabled) {
- if (mInDozeMode == isEnabled) return;
- mInDozeMode = isEnabled;
- installNewProgramLocked();
- }
-
- /** Find the single IPv4 LinkAddress if there is one, otherwise return null. */
- private static LinkAddress findIPv4LinkAddress(LinkProperties lp) {
- LinkAddress ipv4Address = null;
- for (LinkAddress address : lp.getLinkAddresses()) {
- if (!(address.getAddress() instanceof Inet4Address)) {
- continue;
- }
- if (ipv4Address != null && !ipv4Address.isSameAddressAs(address)) {
- // More than one IPv4 address, abort.
- return null;
- }
- ipv4Address = address;
- }
- return ipv4Address;
- }
-
- public synchronized void setLinkProperties(LinkProperties lp) {
- // NOTE: Do not keep a copy of LinkProperties as it would further duplicate state.
- final LinkAddress ipv4Address = findIPv4LinkAddress(lp);
- final byte[] addr = (ipv4Address != null) ? ipv4Address.getAddress().getAddress() : null;
- final int prefix = (ipv4Address != null) ? ipv4Address.getPrefixLength() : 0;
- if ((prefix == mIPv4PrefixLength) && Arrays.equals(addr, mIPv4Address)) {
- return;
- }
- mIPv4Address = addr;
- mIPv4PrefixLength = prefix;
- installNewProgramLocked();
- }
-
- static public long counterValue(byte[] data, Counter counter)
- throws ArrayIndexOutOfBoundsException {
- // Follow the same wrap-around addressing scheme of the interpreter.
- int offset = counter.offset();
- if (offset < 0) {
- offset = data.length + offset;
- }
-
- // Decode 32bit big-endian integer into a long so we can count up beyond 2^31.
- long value = 0;
- for (int i = 0; i < 4; i++) {
- value = value << 8 | (data[offset] & 0xFF);
- offset++;
- }
- return value;
- }
-
- public synchronized void dump(IndentingPrintWriter pw) {
- pw.println("Capabilities: " + mApfCapabilities);
- pw.println("Receive thread: " + (mReceiveThread != null ? "RUNNING" : "STOPPED"));
- pw.println("Multicast: " + (mMulticastFilter ? "DROP" : "ALLOW"));
- try {
- pw.println("IPv4 address: " + InetAddress.getByAddress(mIPv4Address).getHostAddress());
- } catch (UnknownHostException|NullPointerException e) {}
-
- if (mLastTimeInstalledProgram == 0) {
- pw.println("No program installed.");
- return;
- }
- pw.println("Program updates: " + mNumProgramUpdates);
- pw.println(String.format(
- "Last program length %d, installed %ds ago, lifetime %ds",
- mLastInstalledProgram.length, currentTimeSeconds() - mLastTimeInstalledProgram,
- mLastInstalledProgramMinLifetime));
-
- pw.println("RA filters:");
- pw.increaseIndent();
- for (Ra ra: mRas) {
- pw.println(ra);
- pw.increaseIndent();
- pw.println(String.format(
- "Seen: %d, last %ds ago", ra.seenCount, currentTimeSeconds() - ra.mLastSeen));
- if (DBG) {
- pw.println("Last match:");
- pw.increaseIndent();
- pw.println(ra.getLastMatchingPacket());
- pw.decreaseIndent();
- }
- pw.decreaseIndent();
- }
- pw.decreaseIndent();
-
- if (DBG) {
- pw.println("Last program:");
- pw.increaseIndent();
- pw.println(HexDump.toHexString(mLastInstalledProgram, false /* lowercase */));
- pw.decreaseIndent();
- }
-
- pw.println("APF packet counters: ");
- pw.increaseIndent();
- if (!mApfCapabilities.hasDataAccess()) {
- pw.println("APF counters not supported");
- } else if (mDataSnapshot == null) {
- pw.println("No last snapshot.");
- } else {
- try {
- Counter[] counters = Counter.class.getEnumConstants();
- for (Counter c : Arrays.asList(counters).subList(1, counters.length)) {
- long value = counterValue(mDataSnapshot, c);
- // Only print non-zero counters
- if (value != 0) {
- pw.println(c.toString() + ": " + value);
- }
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- pw.println("Uh-oh: " + e);
- }
- if (VDBG) {
- pw.println("Raw data dump: ");
- pw.println(HexDump.dumpHexString(mDataSnapshot));
- }
- }
- pw.decreaseIndent();
- }
-
- // TODO: move to android.net.NetworkUtils
- @VisibleForTesting
- public static int ipv4BroadcastAddress(byte[] addrBytes, int prefixLength) {
- return bytesToBEInt(addrBytes) | (int) (uint32(-1) >>> prefixLength);
- }
-}
diff --git a/services/net/java/android/net/apf/ApfGenerator.java b/services/net/java/android/net/apf/ApfGenerator.java
deleted file mode 100644
index 87a1b5e..0000000
--- a/services/net/java/android/net/apf/ApfGenerator.java
+++ /dev/null
@@ -1,937 +0,0 @@
-/*
- * Copyright (C) 2016 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.apf;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-
-/**
- * APF assembler/generator. A tool for generating an APF program.
- *
- * Call add*() functions to add instructions to the program, then call
- * {@link generate} to get the APF bytecode for the program.
- *
- * @hide
- */
-public class ApfGenerator {
- /**
- * This exception is thrown when an attempt is made to generate an illegal instruction.
- */
- public static class IllegalInstructionException extends Exception {
- IllegalInstructionException(String msg) {
- super(msg);
- }
- }
- private enum Opcodes {
- LABEL(-1),
- LDB(1), // Load 1 byte from immediate offset, e.g. "ldb R0, [5]"
- LDH(2), // Load 2 bytes from immediate offset, e.g. "ldh R0, [5]"
- LDW(3), // Load 4 bytes from immediate offset, e.g. "ldw R0, [5]"
- LDBX(4), // Load 1 byte from immediate offset plus register, e.g. "ldbx R0, [5]R0"
- LDHX(5), // Load 2 byte from immediate offset plus register, e.g. "ldhx R0, [5]R0"
- LDWX(6), // Load 4 byte from immediate offset plus register, e.g. "ldwx R0, [5]R0"
- ADD(7), // Add, e.g. "add R0,5"
- MUL(8), // Multiply, e.g. "mul R0,5"
- DIV(9), // Divide, e.g. "div R0,5"
- AND(10), // And, e.g. "and R0,5"
- OR(11), // Or, e.g. "or R0,5"
- SH(12), // Left shift, e.g, "sh R0, 5" or "sh R0, -5" (shifts right)
- LI(13), // Load immediate, e.g. "li R0,5" (immediate encoded as signed value)
- JMP(14), // Jump, e.g. "jmp label"
- JEQ(15), // Compare equal and branch, e.g. "jeq R0,5,label"
- JNE(16), // Compare not equal and branch, e.g. "jne R0,5,label"
- JGT(17), // Compare greater than and branch, e.g. "jgt R0,5,label"
- JLT(18), // Compare less than and branch, e.g. "jlt R0,5,label"
- JSET(19), // Compare any bits set and branch, e.g. "jset R0,5,label"
- JNEBS(20), // Compare not equal byte sequence, e.g. "jnebs R0,5,label,0x1122334455"
- EXT(21), // Followed by immediate indicating ExtendedOpcodes.
- LDDW(22), // Load 4 bytes from data memory address (register + immediate): "lddw R0, [5]R1"
- STDW(23); // Store 4 bytes to data memory address (register + immediate): "stdw R0, [5]R1"
-
- final int value;
-
- private Opcodes(int value) {
- this.value = value;
- }
- }
- // Extended opcodes. Primary opcode is Opcodes.EXT. ExtendedOpcodes are encoded in the immediate
- // field.
- private enum ExtendedOpcodes {
- LDM(0), // Load from memory, e.g. "ldm R0,5"
- STM(16), // Store to memory, e.g. "stm R0,5"
- NOT(32), // Not, e.g. "not R0"
- NEG(33), // Negate, e.g. "neg R0"
- SWAP(34), // Swap, e.g. "swap R0,R1"
- MOVE(35); // Move, e.g. "move R0,R1"
-
- final int value;
-
- private ExtendedOpcodes(int value) {
- this.value = value;
- }
- }
- public enum Register {
- R0(0),
- R1(1);
-
- final int value;
-
- private Register(int value) {
- this.value = value;
- }
- }
- private class Instruction {
- private final byte mOpcode; // A "Opcode" value.
- private final byte mRegister; // A "Register" value.
- private boolean mHasImm;
- private byte mImmSize;
- private boolean mImmSigned;
- private int mImm;
- // When mOpcode is a jump:
- private byte mTargetLabelSize;
- private String mTargetLabel;
- // When mOpcode == Opcodes.LABEL:
- private String mLabel;
- // When mOpcode == Opcodes.JNEBS:
- private byte[] mCompareBytes;
- // Offset in bytes from the begining of this program. Set by {@link ApfGenerator#generate}.
- int offset;
-
- Instruction(Opcodes opcode, Register register) {
- mOpcode = (byte)opcode.value;
- mRegister = (byte)register.value;
- }
-
- Instruction(Opcodes opcode) {
- this(opcode, Register.R0);
- }
-
- void setImm(int imm, boolean signed) {
- mHasImm = true;
- mImm = imm;
- mImmSigned = signed;
- mImmSize = calculateImmSize(imm, signed);
- }
-
- void setUnsignedImm(int imm) {
- setImm(imm, false);
- }
-
- void setSignedImm(int imm) {
- setImm(imm, true);
- }
-
- void setLabel(String label) throws IllegalInstructionException {
- if (mLabels.containsKey(label)) {
- throw new IllegalInstructionException("duplicate label " + label);
- }
- if (mOpcode != Opcodes.LABEL.value) {
- throw new IllegalStateException("adding label to non-label instruction");
- }
- mLabel = label;
- mLabels.put(label, this);
- }
-
- void setTargetLabel(String label) {
- mTargetLabel = label;
- mTargetLabelSize = 4; // May shrink later on in generate().
- }
-
- void setCompareBytes(byte[] bytes) {
- if (mOpcode != Opcodes.JNEBS.value) {
- throw new IllegalStateException("adding compare bytes to non-JNEBS instruction");
- }
- mCompareBytes = bytes;
- }
-
- /**
- * @return size of instruction in bytes.
- */
- int size() {
- if (mOpcode == Opcodes.LABEL.value) {
- return 0;
- }
- int size = 1;
- if (mHasImm) {
- size += generatedImmSize();
- }
- if (mTargetLabel != null) {
- size += generatedImmSize();
- }
- if (mCompareBytes != null) {
- size += mCompareBytes.length;
- }
- return size;
- }
-
- /**
- * Resize immediate value field so that it's only as big as required to
- * contain the offset of the jump destination.
- * @return {@code true} if shrunk.
- */
- boolean shrink() throws IllegalInstructionException {
- if (mTargetLabel == null) {
- return false;
- }
- int oldSize = size();
- int oldTargetLabelSize = mTargetLabelSize;
- mTargetLabelSize = calculateImmSize(calculateTargetLabelOffset(), false);
- if (mTargetLabelSize > oldTargetLabelSize) {
- throw new IllegalStateException("instruction grew");
- }
- return size() < oldSize;
- }
-
- /**
- * Assemble value for instruction size field.
- */
- private byte generateImmSizeField() {
- byte immSize = generatedImmSize();
- // Encode size field to fit in 2 bits: 0->0, 1->1, 2->2, 3->4.
- return immSize == 4 ? 3 : immSize;
- }
-
- /**
- * Assemble first byte of generated instruction.
- */
- private byte generateInstructionByte() {
- byte sizeField = generateImmSizeField();
- return (byte)((mOpcode << 3) | (sizeField << 1) | mRegister);
- }
-
- /**
- * Write {@code value} at offset {@code writingOffset} into {@code bytecode}.
- * {@link generatedImmSize} bytes are written. {@code value} is truncated to
- * {@code generatedImmSize} bytes. {@code value} is treated simply as a
- * 32-bit value, so unsigned values should be zero extended and the truncation
- * should simply throw away their zero-ed upper bits, and signed values should
- * be sign extended and the truncation should simply throw away their signed
- * upper bits.
- */
- private int writeValue(int value, byte[] bytecode, int writingOffset) {
- for (int i = generatedImmSize() - 1; i >= 0; i--) {
- bytecode[writingOffset++] = (byte)((value >> (i * 8)) & 255);
- }
- return writingOffset;
- }
-
- /**
- * Generate bytecode for this instruction at offset {@link offset}.
- */
- void generate(byte[] bytecode) throws IllegalInstructionException {
- if (mOpcode == Opcodes.LABEL.value) {
- return;
- }
- int writingOffset = offset;
- bytecode[writingOffset++] = generateInstructionByte();
- if (mTargetLabel != null) {
- writingOffset = writeValue(calculateTargetLabelOffset(), bytecode, writingOffset);
- }
- if (mHasImm) {
- writingOffset = writeValue(mImm, bytecode, writingOffset);
- }
- if (mCompareBytes != null) {
- System.arraycopy(mCompareBytes, 0, bytecode, writingOffset, mCompareBytes.length);
- writingOffset += mCompareBytes.length;
- }
- if ((writingOffset - offset) != size()) {
- throw new IllegalStateException("wrote " + (writingOffset - offset) +
- " but should have written " + size());
- }
- }
-
- /**
- * Calculate the size of either the immediate field or the target label field, if either is
- * present. Most instructions have either an immediate or a target label field, but for the
- * instructions that have both, the size of the target label field must be the same as the
- * size of the immediate field, because there is only one length field in the instruction
- * byte, hence why this function simply takes the maximum of the two sizes, so neither is
- * truncated.
- */
- private byte generatedImmSize() {
- return mImmSize > mTargetLabelSize ? mImmSize : mTargetLabelSize;
- }
-
- private int calculateTargetLabelOffset() throws IllegalInstructionException {
- Instruction targetLabelInstruction;
- if (mTargetLabel == DROP_LABEL) {
- targetLabelInstruction = mDropLabel;
- } else if (mTargetLabel == PASS_LABEL) {
- targetLabelInstruction = mPassLabel;
- } else {
- targetLabelInstruction = mLabels.get(mTargetLabel);
- }
- if (targetLabelInstruction == null) {
- throw new IllegalInstructionException("label not found: " + mTargetLabel);
- }
- // Calculate distance from end of this instruction to instruction.offset.
- final int targetLabelOffset = targetLabelInstruction.offset - (offset + size());
- if (targetLabelOffset < 0) {
- throw new IllegalInstructionException("backward branches disallowed; label: " +
- mTargetLabel);
- }
- return targetLabelOffset;
- }
-
- private byte calculateImmSize(int imm, boolean signed) {
- if (imm == 0) {
- return 0;
- }
- if (signed && (imm >= -128 && imm <= 127) ||
- !signed && (imm >= 0 && imm <= 255)) {
- return 1;
- }
- if (signed && (imm >= -32768 && imm <= 32767) ||
- !signed && (imm >= 0 && imm <= 65535)) {
- return 2;
- }
- return 4;
- }
- }
-
- /**
- * Jump to this label to terminate the program and indicate the packet
- * should be dropped.
- */
- public static final String DROP_LABEL = "__DROP__";
-
- /**
- * Jump to this label to terminate the program and indicate the packet
- * should be passed to the AP.
- */
- public static final String PASS_LABEL = "__PASS__";
-
- /**
- * Number of memory slots available for access via APF stores to memory and loads from memory.
- * The memory slots are numbered 0 to {@code MEMORY_SLOTS} - 1. This must be kept in sync with
- * the APF interpreter.
- */
- public static final int MEMORY_SLOTS = 16;
-
- /**
- * Memory slot number that is prefilled with the IPv4 header length.
- * Note that this memory slot may be overwritten by a program that
- * executes stores to this memory slot. This must be kept in sync with
- * the APF interpreter.
- */
- public static final int IPV4_HEADER_SIZE_MEMORY_SLOT = 13;
-
- /**
- * Memory slot number that is prefilled with the size of the packet being filtered in bytes.
- * Note that this memory slot may be overwritten by a program that
- * executes stores to this memory slot. This must be kept in sync with the APF interpreter.
- */
- public static final int PACKET_SIZE_MEMORY_SLOT = 14;
-
- /**
- * Memory slot number that is prefilled with the age of the filter in seconds. The age of the
- * filter is the time since the filter was installed until now.
- * Note that this memory slot may be overwritten by a program that
- * executes stores to this memory slot. This must be kept in sync with the APF interpreter.
- */
- public static final int FILTER_AGE_MEMORY_SLOT = 15;
-
- /**
- * First memory slot containing prefilled values. Can be used in range comparisons to determine
- * if memory slot index is within prefilled slots.
- */
- public static final int FIRST_PREFILLED_MEMORY_SLOT = IPV4_HEADER_SIZE_MEMORY_SLOT;
-
- /**
- * Last memory slot containing prefilled values. Can be used in range comparisons to determine
- * if memory slot index is within prefilled slots.
- */
- public static final int LAST_PREFILLED_MEMORY_SLOT = FILTER_AGE_MEMORY_SLOT;
-
- // This version number syncs up with APF_VERSION in hardware/google/apf/apf_interpreter.h
- private static final int MIN_APF_VERSION = 2;
-
- private final ArrayList<Instruction> mInstructions = new ArrayList<Instruction>();
- private final HashMap<String, Instruction> mLabels = new HashMap<String, Instruction>();
- private final Instruction mDropLabel = new Instruction(Opcodes.LABEL);
- private final Instruction mPassLabel = new Instruction(Opcodes.LABEL);
- private final int mVersion;
- private boolean mGenerated;
-
- /**
- * Creates an ApfGenerator instance which is able to emit instructions for the specified
- * {@code version} of the APF interpreter. Throws {@code IllegalInstructionException} if
- * the requested version is unsupported.
- */
- ApfGenerator(int version) throws IllegalInstructionException {
- mVersion = version;
- requireApfVersion(MIN_APF_VERSION);
- }
-
- /**
- * Returns true if the ApfGenerator supports the specified {@code version}, otherwise false.
- */
- public static boolean supportsVersion(int version) {
- return version >= MIN_APF_VERSION;
- }
-
- private void requireApfVersion(int minimumVersion) throws IllegalInstructionException {
- if (mVersion < minimumVersion) {
- throw new IllegalInstructionException("Requires APF >= " + minimumVersion);
- }
- }
-
- private void addInstruction(Instruction instruction) {
- if (mGenerated) {
- throw new IllegalStateException("Program already generated");
- }
- mInstructions.add(instruction);
- }
-
- /**
- * Define a label at the current end of the program. Jumps can jump to this label. Labels are
- * their own separate instructions, though with size 0. This facilitates having labels with
- * no corresponding code to execute, for example a label at the end of a program. For example
- * an {@link ApfGenerator} might be passed to a function that adds a filter like so:
- * <pre>
- * load from packet
- * compare loaded data, jump if not equal to "next_filter"
- * load from packet
- * compare loaded data, jump if not equal to "next_filter"
- * jump to drop label
- * define "next_filter" here
- * </pre>
- * In this case "next_filter" may not have any generated code associated with it.
- */
- public ApfGenerator defineLabel(String name) throws IllegalInstructionException {
- Instruction instruction = new Instruction(Opcodes.LABEL);
- instruction.setLabel(name);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an unconditional jump instruction to the end of the program.
- */
- public ApfGenerator addJump(String target) {
- Instruction instruction = new Instruction(Opcodes.JMP);
- instruction.setTargetLabel(target);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to load the byte at offset {@code offset}
- * bytes from the begining of the packet into {@code register}.
- */
- public ApfGenerator addLoad8(Register register, int offset) {
- Instruction instruction = new Instruction(Opcodes.LDB, register);
- instruction.setUnsignedImm(offset);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to load 16-bits at offset {@code offset}
- * bytes from the begining of the packet into {@code register}.
- */
- public ApfGenerator addLoad16(Register register, int offset) {
- Instruction instruction = new Instruction(Opcodes.LDH, register);
- instruction.setUnsignedImm(offset);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to load 32-bits at offset {@code offset}
- * bytes from the begining of the packet into {@code register}.
- */
- public ApfGenerator addLoad32(Register register, int offset) {
- Instruction instruction = new Instruction(Opcodes.LDW, register);
- instruction.setUnsignedImm(offset);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to load a byte from the packet into
- * {@code register}. The offset of the loaded byte from the begining of the packet is
- * the sum of {@code offset} and the value in register R1.
- */
- public ApfGenerator addLoad8Indexed(Register register, int offset) {
- Instruction instruction = new Instruction(Opcodes.LDBX, register);
- instruction.setUnsignedImm(offset);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to load 16-bits from the packet into
- * {@code register}. The offset of the loaded 16-bits from the begining of the packet is
- * the sum of {@code offset} and the value in register R1.
- */
- public ApfGenerator addLoad16Indexed(Register register, int offset) {
- Instruction instruction = new Instruction(Opcodes.LDHX, register);
- instruction.setUnsignedImm(offset);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to load 32-bits from the packet into
- * {@code register}. The offset of the loaded 32-bits from the begining of the packet is
- * the sum of {@code offset} and the value in register R1.
- */
- public ApfGenerator addLoad32Indexed(Register register, int offset) {
- Instruction instruction = new Instruction(Opcodes.LDWX, register);
- instruction.setUnsignedImm(offset);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to add {@code value} to register R0.
- */
- public ApfGenerator addAdd(int value) {
- Instruction instruction = new Instruction(Opcodes.ADD);
- instruction.setSignedImm(value);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to multiply register R0 by {@code value}.
- */
- public ApfGenerator addMul(int value) {
- Instruction instruction = new Instruction(Opcodes.MUL);
- instruction.setSignedImm(value);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to divide register R0 by {@code value}.
- */
- public ApfGenerator addDiv(int value) {
- Instruction instruction = new Instruction(Opcodes.DIV);
- instruction.setSignedImm(value);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to logically and register R0 with {@code value}.
- */
- public ApfGenerator addAnd(int value) {
- Instruction instruction = new Instruction(Opcodes.AND);
- instruction.setUnsignedImm(value);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to logically or register R0 with {@code value}.
- */
- public ApfGenerator addOr(int value) {
- Instruction instruction = new Instruction(Opcodes.OR);
- instruction.setUnsignedImm(value);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to shift left register R0 by {@code value} bits.
- */
- public ApfGenerator addLeftShift(int value) {
- Instruction instruction = new Instruction(Opcodes.SH);
- instruction.setSignedImm(value);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to shift right register R0 by {@code value}
- * bits.
- */
- public ApfGenerator addRightShift(int value) {
- Instruction instruction = new Instruction(Opcodes.SH);
- instruction.setSignedImm(-value);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to add register R1 to register R0.
- */
- public ApfGenerator addAddR1() {
- Instruction instruction = new Instruction(Opcodes.ADD, Register.R1);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to multiply register R0 by register R1.
- */
- public ApfGenerator addMulR1() {
- Instruction instruction = new Instruction(Opcodes.MUL, Register.R1);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to divide register R0 by register R1.
- */
- public ApfGenerator addDivR1() {
- Instruction instruction = new Instruction(Opcodes.DIV, Register.R1);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to logically and register R0 with register R1
- * and store the result back into register R0.
- */
- public ApfGenerator addAndR1() {
- Instruction instruction = new Instruction(Opcodes.AND, Register.R1);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to logically or register R0 with register R1
- * and store the result back into register R0.
- */
- public ApfGenerator addOrR1() {
- Instruction instruction = new Instruction(Opcodes.OR, Register.R1);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to shift register R0 left by the value in
- * register R1.
- */
- public ApfGenerator addLeftShiftR1() {
- Instruction instruction = new Instruction(Opcodes.SH, Register.R1);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to move {@code value} into {@code register}.
- */
- public ApfGenerator addLoadImmediate(Register register, int value) {
- Instruction instruction = new Instruction(Opcodes.LI, register);
- instruction.setSignedImm(value);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to jump to {@code target} if register R0's
- * value equals {@code value}.
- */
- public ApfGenerator addJumpIfR0Equals(int value, String target) {
- Instruction instruction = new Instruction(Opcodes.JEQ);
- instruction.setUnsignedImm(value);
- instruction.setTargetLabel(target);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to jump to {@code target} if register R0's
- * value does not equal {@code value}.
- */
- public ApfGenerator addJumpIfR0NotEquals(int value, String target) {
- Instruction instruction = new Instruction(Opcodes.JNE);
- instruction.setUnsignedImm(value);
- instruction.setTargetLabel(target);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to jump to {@code target} if register R0's
- * value is greater than {@code value}.
- */
- public ApfGenerator addJumpIfR0GreaterThan(int value, String target) {
- Instruction instruction = new Instruction(Opcodes.JGT);
- instruction.setUnsignedImm(value);
- instruction.setTargetLabel(target);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to jump to {@code target} if register R0's
- * value is less than {@code value}.
- */
- public ApfGenerator addJumpIfR0LessThan(int value, String target) {
- Instruction instruction = new Instruction(Opcodes.JLT);
- instruction.setUnsignedImm(value);
- instruction.setTargetLabel(target);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to jump to {@code target} if register R0's
- * value has any bits set that are also set in {@code value}.
- */
- public ApfGenerator addJumpIfR0AnyBitsSet(int value, String target) {
- Instruction instruction = new Instruction(Opcodes.JSET);
- instruction.setUnsignedImm(value);
- instruction.setTargetLabel(target);
- addInstruction(instruction);
- return this;
- }
- /**
- * Add an instruction to the end of the program to jump to {@code target} if register R0's
- * value equals register R1's value.
- */
- public ApfGenerator addJumpIfR0EqualsR1(String target) {
- Instruction instruction = new Instruction(Opcodes.JEQ, Register.R1);
- instruction.setTargetLabel(target);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to jump to {@code target} if register R0's
- * value does not equal register R1's value.
- */
- public ApfGenerator addJumpIfR0NotEqualsR1(String target) {
- Instruction instruction = new Instruction(Opcodes.JNE, Register.R1);
- instruction.setTargetLabel(target);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to jump to {@code target} if register R0's
- * value is greater than register R1's value.
- */
- public ApfGenerator addJumpIfR0GreaterThanR1(String target) {
- Instruction instruction = new Instruction(Opcodes.JGT, Register.R1);
- instruction.setTargetLabel(target);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to jump to {@code target} if register R0's
- * value is less than register R1's value.
- */
- public ApfGenerator addJumpIfR0LessThanR1(String target) {
- Instruction instruction = new Instruction(Opcodes.JLT, Register.R1);
- instruction.setTargetLabel(target);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to jump to {@code target} if register R0's
- * value has any bits set that are also set in R1's value.
- */
- public ApfGenerator addJumpIfR0AnyBitsSetR1(String target) {
- Instruction instruction = new Instruction(Opcodes.JSET, Register.R1);
- instruction.setTargetLabel(target);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to jump to {@code target} if the bytes of the
- * packet at an offset specified by {@code register} match {@code bytes}.
- */
- public ApfGenerator addJumpIfBytesNotEqual(Register register, byte[] bytes, String target)
- throws IllegalInstructionException {
- if (register == Register.R1) {
- throw new IllegalInstructionException("JNEBS fails with R1");
- }
- Instruction instruction = new Instruction(Opcodes.JNEBS, register);
- instruction.setUnsignedImm(bytes.length);
- instruction.setTargetLabel(target);
- instruction.setCompareBytes(bytes);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to load memory slot {@code slot} into
- * {@code register}.
- */
- public ApfGenerator addLoadFromMemory(Register register, int slot)
- throws IllegalInstructionException {
- if (slot < 0 || slot > (MEMORY_SLOTS - 1)) {
- throw new IllegalInstructionException("illegal memory slot number: " + slot);
- }
- Instruction instruction = new Instruction(Opcodes.EXT, register);
- instruction.setUnsignedImm(ExtendedOpcodes.LDM.value + slot);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to store {@code register} into memory slot
- * {@code slot}.
- */
- public ApfGenerator addStoreToMemory(Register register, int slot)
- throws IllegalInstructionException {
- if (slot < 0 || slot > (MEMORY_SLOTS - 1)) {
- throw new IllegalInstructionException("illegal memory slot number: " + slot);
- }
- Instruction instruction = new Instruction(Opcodes.EXT, register);
- instruction.setUnsignedImm(ExtendedOpcodes.STM.value + slot);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to logically not {@code register}.
- */
- public ApfGenerator addNot(Register register) {
- Instruction instruction = new Instruction(Opcodes.EXT, register);
- instruction.setUnsignedImm(ExtendedOpcodes.NOT.value);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to negate {@code register}.
- */
- public ApfGenerator addNeg(Register register) {
- Instruction instruction = new Instruction(Opcodes.EXT, register);
- instruction.setUnsignedImm(ExtendedOpcodes.NEG.value);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to swap the values in register R0 and register R1.
- */
- public ApfGenerator addSwap() {
- Instruction instruction = new Instruction(Opcodes.EXT);
- instruction.setUnsignedImm(ExtendedOpcodes.SWAP.value);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to move the value into
- * {@code register} from the other register.
- */
- public ApfGenerator addMove(Register register) {
- Instruction instruction = new Instruction(Opcodes.EXT, register);
- instruction.setUnsignedImm(ExtendedOpcodes.MOVE.value);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to load 32 bits from the data memory into
- * {@code register}. The source address is computed by adding the signed immediate
- * @{code offset} to the other register.
- * Requires APF v3 or greater.
- */
- public ApfGenerator addLoadData(Register destinationRegister, int offset)
- throws IllegalInstructionException {
- requireApfVersion(3);
- Instruction instruction = new Instruction(Opcodes.LDDW, destinationRegister);
- instruction.setSignedImm(offset);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to store 32 bits from {@code register} into the
- * data memory. The destination address is computed by adding the signed immediate
- * @{code offset} to the other register.
- * Requires APF v3 or greater.
- */
- public ApfGenerator addStoreData(Register sourceRegister, int offset)
- throws IllegalInstructionException {
- requireApfVersion(3);
- Instruction instruction = new Instruction(Opcodes.STDW, sourceRegister);
- instruction.setSignedImm(offset);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Updates instruction offset fields using latest instruction sizes.
- * @return current program length in bytes.
- */
- private int updateInstructionOffsets() {
- int offset = 0;
- for (Instruction instruction : mInstructions) {
- instruction.offset = offset;
- offset += instruction.size();
- }
- return offset;
- }
-
- /**
- * Returns an overestimate of the size of the generated program. {@link #generate} may return
- * a program that is smaller.
- */
- public int programLengthOverEstimate() {
- return updateInstructionOffsets();
- }
-
- /**
- * Generate the bytecode for the APF program.
- * @return the bytecode.
- * @throws IllegalStateException if a label is referenced but not defined.
- */
- public byte[] generate() throws IllegalInstructionException {
- // Enforce that we can only generate once because we cannot unshrink instructions and
- // PASS/DROP labels may move further away requiring unshrinking if we add further
- // instructions.
- if (mGenerated) {
- throw new IllegalStateException("Can only generate() once!");
- }
- mGenerated = true;
- int total_size;
- boolean shrunk;
- // Shrink the immediate value fields of instructions.
- // As we shrink the instructions some branch offset
- // fields may shrink also, thereby shrinking the
- // instructions further. Loop until we've reached the
- // minimum size. Rarely will this loop more than a few times.
- // Limit iterations to avoid O(n^2) behavior.
- int iterations_remaining = 10;
- do {
- total_size = updateInstructionOffsets();
- // Update drop and pass label offsets.
- mDropLabel.offset = total_size + 1;
- mPassLabel.offset = total_size;
- // Limit run-time in aberant circumstances.
- if (iterations_remaining-- == 0) break;
- // Attempt to shrink instructions.
- shrunk = false;
- for (Instruction instruction : mInstructions) {
- if (instruction.shrink()) {
- shrunk = true;
- }
- }
- } while (shrunk);
- // Generate bytecode for instructions.
- byte[] bytecode = new byte[total_size];
- for (Instruction instruction : mInstructions) {
- instruction.generate(bytecode);
- }
- return bytecode;
- }
-}
-
diff --git a/services/net/java/android/net/dhcp/DhcpAckPacket.java b/services/net/java/android/net/dhcp/DhcpAckPacket.java
deleted file mode 100644
index b2eb4e2..0000000
--- a/services/net/java/android/net/dhcp/DhcpAckPacket.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2010 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.dhcp;
-
-import java.net.Inet4Address;
-import java.nio.ByteBuffer;
-
-/**
- * This class implements the DHCP-ACK packet.
- */
-class DhcpAckPacket extends DhcpPacket {
-
- /**
- * The address of the server which sent this packet.
- */
- private final Inet4Address mSrcIp;
-
- DhcpAckPacket(int transId, short secs, boolean broadcast, Inet4Address serverAddress,
- Inet4Address relayIp, Inet4Address clientIp, Inet4Address yourIp, byte[] clientMac) {
- super(transId, secs, clientIp, yourIp, serverAddress, relayIp, clientMac, broadcast);
- mBroadcast = broadcast;
- mSrcIp = serverAddress;
- }
-
- public String toString() {
- String s = super.toString();
- String dnsServers = " DNS servers: ";
-
- for (Inet4Address dnsServer: mDnsServers) {
- dnsServers += dnsServer.toString() + " ";
- }
-
- return s + " ACK: your new IP " + mYourIp +
- ", netmask " + mSubnetMask +
- ", gateways " + mGateways + dnsServers +
- ", lease time " + mLeaseTime;
- }
-
- /**
- * Fills in a packet with the requested ACK parameters.
- */
- public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
- ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
- Inet4Address destIp = mBroadcast ? INADDR_BROADCAST : mYourIp;
- Inet4Address srcIp = mBroadcast ? INADDR_ANY : mSrcIp;
-
- fillInPacket(encap, destIp, srcIp, destUdp, srcUdp, result,
- DHCP_BOOTREPLY, mBroadcast);
- result.flip();
- return result;
- }
-
- /**
- * Adds the optional parameters to the client-generated ACK packet.
- */
- void finishPacket(ByteBuffer buffer) {
- addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_ACK);
- addTlv(buffer, DHCP_SERVER_IDENTIFIER, mServerIdentifier);
-
- addCommonServerTlvs(buffer);
- addTlvEnd(buffer);
- }
-
- /**
- * Un-boxes an Integer, returning 0 if a null reference is supplied.
- */
- private static final int getInt(Integer v) {
- if (v == null) {
- return 0;
- } else {
- return v.intValue();
- }
- }
-}
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
deleted file mode 100644
index 04ac9a3..0000000
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ /dev/null
@@ -1,1052 +0,0 @@
-/*
- * Copyright (C) 2015 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.dhcp;
-
-import static android.net.dhcp.DhcpPacket.DHCP_BROADCAST_ADDRESS;
-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;
-import static android.net.dhcp.DhcpPacket.DHCP_MTU;
-import static android.net.dhcp.DhcpPacket.DHCP_REBINDING_TIME;
-import static android.net.dhcp.DhcpPacket.DHCP_RENEWAL_TIME;
-import static android.net.dhcp.DhcpPacket.DHCP_ROUTER;
-import static android.net.dhcp.DhcpPacket.DHCP_SUBNET_MASK;
-import static android.net.dhcp.DhcpPacket.DHCP_VENDOR_INFO;
-import static android.net.dhcp.DhcpPacket.INADDR_ANY;
-import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST;
-import static android.net.util.SocketUtils.makePacketSocketAddress;
-import static android.system.OsConstants.AF_INET;
-import static android.system.OsConstants.AF_PACKET;
-import static android.system.OsConstants.ETH_P_IP;
-import static android.system.OsConstants.IPPROTO_UDP;
-import static android.system.OsConstants.SOCK_DGRAM;
-import static android.system.OsConstants.SOCK_RAW;
-import static android.system.OsConstants.SOL_SOCKET;
-import static android.system.OsConstants.SO_BROADCAST;
-import static android.system.OsConstants.SO_RCVBUF;
-import static android.system.OsConstants.SO_REUSEADDR;
-
-import android.content.Context;
-import android.net.DhcpResults;
-import android.net.NetworkUtils;
-import android.net.TrafficStats;
-import android.net.ip.IpClient;
-import android.net.metrics.DhcpClientEvent;
-import android.net.metrics.DhcpErrorEvent;
-import android.net.metrics.IpConnectivityLog;
-import android.net.util.InterfaceParams;
-import android.net.util.SocketUtils;
-import android.os.Message;
-import android.os.SystemClock;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.util.EventLog;
-import android.util.Log;
-import android.util.SparseArray;
-
-import com.android.internal.util.HexDump;
-import com.android.internal.util.MessageUtils;
-import com.android.internal.util.State;
-import com.android.internal.util.StateMachine;
-import com.android.internal.util.WakeupMessage;
-
-import libcore.io.IoBridge;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.net.Inet4Address;
-import java.net.SocketAddress;
-import java.net.SocketException;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-import java.util.Random;
-
-/**
- * A DHCPv4 client.
- *
- * Written to behave similarly to the DhcpStateMachine + dhcpcd 5.5.6 combination used in Android
- * 5.1 and below, as configured on Nexus 6. The interface is the same as DhcpStateMachine.
- *
- * TODO:
- *
- * - Exponential backoff when receiving NAKs (not specified by the RFC, but current behaviour).
- * - Support persisting lease state and support INIT-REBOOT. Android 5.1 does this, but it does not
- * do so correctly: instead of requesting the lease last obtained on a particular network (e.g., a
- * given SSID), it requests the last-leased IP address on the same interface, causing a delay if
- * the server NAKs or a timeout if it doesn't.
- *
- * Known differences from current behaviour:
- *
- * - Does not request the "static routes" option.
- * - Does not support BOOTP servers. DHCP has been around since 1993, should be everywhere now.
- * - Requests the "broadcast" option, but does nothing with it.
- * - Rejects invalid subnet masks such as 255.255.255.1 (current code treats that as 255.255.255.0).
- *
- * @hide
- */
-public class DhcpClient extends StateMachine {
-
- private static final String TAG = "DhcpClient";
- private static final boolean DBG = true;
- private static final boolean STATE_DBG = false;
- private static final boolean MSG_DBG = false;
- private static final boolean PACKET_DBG = false;
-
- // Timers and timeouts.
- private static final int SECONDS = 1000;
- private static final int FIRST_TIMEOUT_MS = 2 * SECONDS;
- private static final int MAX_TIMEOUT_MS = 128 * SECONDS;
-
- // This is not strictly needed, since the client is asynchronous and implements exponential
- // backoff. It's maintained for backwards compatibility with the previous DHCP code, which was
- // a blocking operation with a 30-second timeout. We pick 36 seconds so we can send packets at
- // t=0, t=2, t=6, t=14, t=30, allowing for 10% jitter.
- private static final int DHCP_TIMEOUT_MS = 36 * SECONDS;
-
- // DhcpClient uses IpClient's handler.
- private static final int PUBLIC_BASE = IpClient.DHCPCLIENT_CMD_BASE;
-
- /* Commands from controller to start/stop DHCP */
- public static final int CMD_START_DHCP = PUBLIC_BASE + 1;
- public static final int CMD_STOP_DHCP = PUBLIC_BASE + 2;
-
- /* Notification from DHCP state machine prior to DHCP discovery/renewal */
- public static final int CMD_PRE_DHCP_ACTION = PUBLIC_BASE + 3;
- /* Notification from DHCP state machine post DHCP discovery/renewal. Indicates
- * success/failure */
- public static final int CMD_POST_DHCP_ACTION = PUBLIC_BASE + 4;
- /* Notification from DHCP state machine before quitting */
- public static final int CMD_ON_QUIT = PUBLIC_BASE + 5;
-
- /* Command from controller to indicate DHCP discovery/renewal can continue
- * after pre DHCP action is complete */
- public static final int CMD_PRE_DHCP_ACTION_COMPLETE = PUBLIC_BASE + 6;
-
- /* Command and event notification to/from IpManager requesting the setting
- * (or clearing) of an IPv4 LinkAddress.
- */
- public static final int CMD_CLEAR_LINKADDRESS = PUBLIC_BASE + 7;
- public static final int CMD_CONFIGURE_LINKADDRESS = PUBLIC_BASE + 8;
- public static final int EVENT_LINKADDRESS_CONFIGURED = PUBLIC_BASE + 9;
-
- /* Message.arg1 arguments to CMD_POST_DHCP_ACTION notification */
- public static final int DHCP_SUCCESS = 1;
- public static final int DHCP_FAILURE = 2;
-
- // Internal messages.
- private static final int PRIVATE_BASE = IpClient.DHCPCLIENT_CMD_BASE + 100;
- private static final int CMD_KICK = PRIVATE_BASE + 1;
- private static final int CMD_RECEIVED_PACKET = PRIVATE_BASE + 2;
- private static final int CMD_TIMEOUT = PRIVATE_BASE + 3;
- private static final int CMD_RENEW_DHCP = PRIVATE_BASE + 4;
- private static final int CMD_REBIND_DHCP = PRIVATE_BASE + 5;
- private static final int CMD_EXPIRE_DHCP = PRIVATE_BASE + 6;
-
- // For message logging.
- private static final Class[] sMessageClasses = { DhcpClient.class };
- private static final SparseArray<String> sMessageNames =
- MessageUtils.findMessageNames(sMessageClasses);
-
- // DHCP parameters that we request.
- /* package */ static final byte[] REQUESTED_PARAMS = new byte[] {
- DHCP_SUBNET_MASK,
- DHCP_ROUTER,
- DHCP_DNS_SERVER,
- DHCP_DOMAIN_NAME,
- DHCP_MTU,
- DHCP_BROADCAST_ADDRESS, // TODO: currently ignored.
- DHCP_LEASE_TIME,
- DHCP_RENEWAL_TIME,
- DHCP_REBINDING_TIME,
- DHCP_VENDOR_INFO,
- };
-
- // DHCP flag that means "yes, we support unicast."
- private static final boolean DO_UNICAST = false;
-
- // System services / libraries we use.
- private final Context mContext;
- private final Random mRandom;
- private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
-
- // Sockets.
- // - We use a packet socket to receive, because servers send us packets bound for IP addresses
- // which we have not yet configured, and the kernel protocol stack drops these.
- // - We use a UDP socket to send, so the kernel handles ARP and routing for us (DHCP servers can
- // be off-link as well as on-link).
- private FileDescriptor mPacketSock;
- private FileDescriptor mUdpSock;
- private ReceiveThread mReceiveThread;
-
- // State variables.
- private final StateMachine mController;
- private final WakeupMessage mKickAlarm;
- private final WakeupMessage mTimeoutAlarm;
- private final WakeupMessage mRenewAlarm;
- private final WakeupMessage mRebindAlarm;
- private final WakeupMessage mExpiryAlarm;
- private final String mIfaceName;
-
- private boolean mRegisteredForPreDhcpNotification;
- private InterfaceParams mIface;
- // TODO: MacAddress-ify more of this class hierarchy.
- private byte[] mHwAddr;
- private SocketAddress mInterfaceBroadcastAddr;
- private int mTransactionId;
- private long mTransactionStartMillis;
- private DhcpResults mDhcpLease;
- private long mDhcpLeaseExpiry;
- private DhcpResults mOffer;
-
- // Milliseconds SystemClock timestamps used to record transition times to DhcpBoundState.
- private long mLastInitEnterTime;
- private long mLastBoundExitTime;
-
- // States.
- private State mStoppedState = new StoppedState();
- private State mDhcpState = new DhcpState();
- private State mDhcpInitState = new DhcpInitState();
- private State mDhcpSelectingState = new DhcpSelectingState();
- private State mDhcpRequestingState = new DhcpRequestingState();
- private State mDhcpHaveLeaseState = new DhcpHaveLeaseState();
- private State mConfiguringInterfaceState = new ConfiguringInterfaceState();
- private State mDhcpBoundState = new DhcpBoundState();
- private State mDhcpRenewingState = new DhcpRenewingState();
- private State mDhcpRebindingState = new DhcpRebindingState();
- private State mDhcpInitRebootState = new DhcpInitRebootState();
- private State mDhcpRebootingState = new DhcpRebootingState();
- private State mWaitBeforeStartState = new WaitBeforeStartState(mDhcpInitState);
- private State mWaitBeforeRenewalState = new WaitBeforeRenewalState(mDhcpRenewingState);
-
- private WakeupMessage makeWakeupMessage(String cmdName, int cmd) {
- cmdName = DhcpClient.class.getSimpleName() + "." + mIfaceName + "." + cmdName;
- return new WakeupMessage(mContext, getHandler(), cmdName, cmd);
- }
-
- // TODO: Take an InterfaceParams instance instead of an interface name String.
- private DhcpClient(Context context, StateMachine controller, String iface) {
- super(TAG, controller.getHandler());
-
- mContext = context;
- mController = controller;
- mIfaceName = iface;
-
- addState(mStoppedState);
- addState(mDhcpState);
- addState(mDhcpInitState, mDhcpState);
- addState(mWaitBeforeStartState, mDhcpState);
- addState(mDhcpSelectingState, mDhcpState);
- addState(mDhcpRequestingState, mDhcpState);
- addState(mDhcpHaveLeaseState, mDhcpState);
- addState(mConfiguringInterfaceState, mDhcpHaveLeaseState);
- addState(mDhcpBoundState, mDhcpHaveLeaseState);
- addState(mWaitBeforeRenewalState, mDhcpHaveLeaseState);
- addState(mDhcpRenewingState, mDhcpHaveLeaseState);
- addState(mDhcpRebindingState, mDhcpHaveLeaseState);
- addState(mDhcpInitRebootState, mDhcpState);
- addState(mDhcpRebootingState, mDhcpState);
-
- setInitialState(mStoppedState);
-
- mRandom = new Random();
-
- // Used to schedule packet retransmissions.
- mKickAlarm = makeWakeupMessage("KICK", CMD_KICK);
- // Used to time out PacketRetransmittingStates.
- mTimeoutAlarm = makeWakeupMessage("TIMEOUT", CMD_TIMEOUT);
- // Used to schedule DHCP reacquisition.
- mRenewAlarm = makeWakeupMessage("RENEW", CMD_RENEW_DHCP);
- mRebindAlarm = makeWakeupMessage("REBIND", CMD_REBIND_DHCP);
- mExpiryAlarm = makeWakeupMessage("EXPIRY", CMD_EXPIRE_DHCP);
- }
-
- public void registerForPreDhcpNotification() {
- mRegisteredForPreDhcpNotification = true;
- }
-
- public static DhcpClient makeDhcpClient(
- Context context, StateMachine controller, InterfaceParams ifParams) {
- DhcpClient client = new DhcpClient(context, controller, ifParams.name);
- client.mIface = ifParams;
- client.start();
- return client;
- }
-
- private boolean initInterface() {
- if (mIface == null) mIface = InterfaceParams.getByName(mIfaceName);
- if (mIface == null) {
- Log.e(TAG, "Can't determine InterfaceParams for " + mIfaceName);
- return false;
- }
-
- mHwAddr = mIface.macAddr.toByteArray();
- mInterfaceBroadcastAddr = makePacketSocketAddress(mIface.index, DhcpPacket.ETHER_BROADCAST);
- return true;
- }
-
- private void startNewTransaction() {
- mTransactionId = mRandom.nextInt();
- mTransactionStartMillis = SystemClock.elapsedRealtime();
- }
-
- private boolean initSockets() {
- return initPacketSocket() && initUdpSocket();
- }
-
- private boolean initPacketSocket() {
- try {
- mPacketSock = Os.socket(AF_PACKET, SOCK_RAW, ETH_P_IP);
- SocketAddress addr = makePacketSocketAddress((short) ETH_P_IP, mIface.index);
- Os.bind(mPacketSock, addr);
- NetworkUtils.attachDhcpFilter(mPacketSock);
- } catch(SocketException|ErrnoException e) {
- Log.e(TAG, "Error creating packet socket", e);
- return false;
- }
- return true;
- }
-
- private boolean initUdpSocket() {
- final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_DHCP);
- try {
- mUdpSock = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
- SocketUtils.bindSocketToInterface(mUdpSock, mIfaceName);
- Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_REUSEADDR, 1);
- Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_BROADCAST, 1);
- Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_RCVBUF, 0);
- Os.bind(mUdpSock, Inet4Address.ANY, DhcpPacket.DHCP_CLIENT);
- } catch(SocketException|ErrnoException e) {
- Log.e(TAG, "Error creating UDP socket", e);
- return false;
- } finally {
- TrafficStats.setThreadStatsTag(oldTag);
- }
- return true;
- }
-
- private boolean connectUdpSock(Inet4Address to) {
- try {
- Os.connect(mUdpSock, to, DhcpPacket.DHCP_SERVER);
- return true;
- } catch (SocketException|ErrnoException e) {
- Log.e(TAG, "Error connecting UDP socket", e);
- return false;
- }
- }
-
- private static void closeQuietly(FileDescriptor fd) {
- try {
- IoBridge.closeAndSignalBlockedThreads(fd);
- } catch (IOException ignored) {}
- }
-
- private void closeSockets() {
- closeQuietly(mUdpSock);
- closeQuietly(mPacketSock);
- }
-
- class ReceiveThread extends Thread {
-
- private final byte[] mPacket = new byte[DhcpPacket.MAX_LENGTH];
- private volatile boolean mStopped = false;
-
- public void halt() {
- mStopped = true;
- closeSockets(); // Interrupts the read() call the thread is blocked in.
- }
-
- @Override
- public void run() {
- if (DBG) Log.d(TAG, "Receive thread started");
- while (!mStopped) {
- int length = 0; // Or compiler can't tell it's initialized if a parse error occurs.
- try {
- length = Os.read(mPacketSock, mPacket, 0, mPacket.length);
- DhcpPacket packet = null;
- packet = DhcpPacket.decodeFullPacket(mPacket, length, DhcpPacket.ENCAP_L2);
- if (DBG) Log.d(TAG, "Received packet: " + packet);
- sendMessage(CMD_RECEIVED_PACKET, packet);
- } catch (IOException|ErrnoException e) {
- if (!mStopped) {
- Log.e(TAG, "Read error", e);
- logError(DhcpErrorEvent.RECEIVE_ERROR);
- }
- } catch (DhcpPacket.ParseException e) {
- Log.e(TAG, "Can't parse packet: " + e.getMessage());
- if (PACKET_DBG) {
- Log.d(TAG, HexDump.dumpHexString(mPacket, 0, length));
- }
- if (e.errorCode == DhcpErrorEvent.DHCP_NO_COOKIE) {
- int snetTagId = 0x534e4554;
- String bugId = "31850211";
- int uid = -1;
- String data = DhcpPacket.ParseException.class.getName();
- EventLog.writeEvent(snetTagId, bugId, uid, data);
- }
- logError(e.errorCode);
- }
- }
- if (DBG) Log.d(TAG, "Receive thread stopped");
- }
- }
-
- private short getSecs() {
- return (short) ((SystemClock.elapsedRealtime() - mTransactionStartMillis) / 1000);
- }
-
- private boolean transmitPacket(ByteBuffer buf, String description, int encap, Inet4Address to) {
- try {
- if (encap == DhcpPacket.ENCAP_L2) {
- if (DBG) Log.d(TAG, "Broadcasting " + description);
- Os.sendto(mPacketSock, buf.array(), 0, buf.limit(), 0, mInterfaceBroadcastAddr);
- } else if (encap == DhcpPacket.ENCAP_BOOTP && to.equals(INADDR_BROADCAST)) {
- if (DBG) Log.d(TAG, "Broadcasting " + description);
- // We only send L3-encapped broadcasts in DhcpRebindingState,
- // where we have an IP address and an unconnected UDP socket.
- //
- // N.B.: We only need this codepath because DhcpRequestPacket
- // hardcodes the source IP address to 0.0.0.0. We could reuse
- // the packet socket if this ever changes.
- Os.sendto(mUdpSock, buf, 0, to, DhcpPacket.DHCP_SERVER);
- } else {
- // It's safe to call getpeername here, because we only send unicast packets if we
- // have an IP address, and we connect the UDP socket in DhcpBoundState#enter.
- if (DBG) Log.d(TAG, String.format("Unicasting %s to %s",
- description, Os.getpeername(mUdpSock)));
- Os.write(mUdpSock, buf);
- }
- } catch(ErrnoException|IOException e) {
- Log.e(TAG, "Can't send packet: ", e);
- return false;
- }
- return true;
- }
-
- private boolean sendDiscoverPacket() {
- ByteBuffer packet = DhcpPacket.buildDiscoverPacket(
- DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr,
- DO_UNICAST, REQUESTED_PARAMS);
- return transmitPacket(packet, "DHCPDISCOVER", DhcpPacket.ENCAP_L2, INADDR_BROADCAST);
- }
-
- private boolean sendRequestPacket(
- Inet4Address clientAddress, Inet4Address requestedAddress,
- Inet4Address serverAddress, Inet4Address to) {
- // TODO: should we use the transaction ID from the server?
- final int encap = INADDR_ANY.equals(clientAddress)
- ? DhcpPacket.ENCAP_L2 : DhcpPacket.ENCAP_BOOTP;
-
- ByteBuffer packet = DhcpPacket.buildRequestPacket(
- encap, mTransactionId, getSecs(), clientAddress,
- DO_UNICAST, mHwAddr, requestedAddress,
- serverAddress, REQUESTED_PARAMS, null);
- String serverStr = (serverAddress != null) ? serverAddress.getHostAddress() : null;
- String description = "DHCPREQUEST ciaddr=" + clientAddress.getHostAddress() +
- " request=" + requestedAddress.getHostAddress() +
- " serverid=" + serverStr;
- return transmitPacket(packet, description, encap, to);
- }
-
- private void scheduleLeaseTimers() {
- if (mDhcpLeaseExpiry == 0) {
- Log.d(TAG, "Infinite lease, no timer scheduling needed");
- return;
- }
-
- final long now = SystemClock.elapsedRealtime();
-
- // TODO: consider getting the renew and rebind timers from T1 and T2.
- // See also:
- // https://tools.ietf.org/html/rfc2131#section-4.4.5
- // https://tools.ietf.org/html/rfc1533#section-9.9
- // https://tools.ietf.org/html/rfc1533#section-9.10
- final long remainingDelay = mDhcpLeaseExpiry - now;
- final long renewDelay = remainingDelay / 2;
- final long rebindDelay = remainingDelay * 7 / 8;
- mRenewAlarm.schedule(now + renewDelay);
- mRebindAlarm.schedule(now + rebindDelay);
- mExpiryAlarm.schedule(now + remainingDelay);
- Log.d(TAG, "Scheduling renewal in " + (renewDelay / 1000) + "s");
- Log.d(TAG, "Scheduling rebind in " + (rebindDelay / 1000) + "s");
- Log.d(TAG, "Scheduling expiry in " + (remainingDelay / 1000) + "s");
- }
-
- private void notifySuccess() {
- mController.sendMessage(
- CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, new DhcpResults(mDhcpLease));
- }
-
- private void notifyFailure() {
- mController.sendMessage(CMD_POST_DHCP_ACTION, DHCP_FAILURE, 0, null);
- }
-
- private void acceptDhcpResults(DhcpResults results, String msg) {
- mDhcpLease = results;
- mOffer = null;
- Log.d(TAG, msg + " lease: " + mDhcpLease);
- notifySuccess();
- }
-
- private void clearDhcpState() {
- mDhcpLease = null;
- mDhcpLeaseExpiry = 0;
- mOffer = null;
- }
-
- /**
- * Quit the DhcpStateMachine.
- *
- * @hide
- */
- public void doQuit() {
- Log.d(TAG, "doQuit");
- quit();
- }
-
- @Override
- protected void onQuitting() {
- Log.d(TAG, "onQuitting");
- mController.sendMessage(CMD_ON_QUIT);
- }
-
- abstract class LoggingState extends State {
- private long mEnterTimeMs;
-
- @Override
- public void enter() {
- if (STATE_DBG) Log.d(TAG, "Entering state " + getName());
- mEnterTimeMs = SystemClock.elapsedRealtime();
- }
-
- @Override
- public void exit() {
- long durationMs = SystemClock.elapsedRealtime() - mEnterTimeMs;
- logState(getName(), (int) durationMs);
- }
-
- private String messageName(int what) {
- return sMessageNames.get(what, Integer.toString(what));
- }
-
- private String messageToString(Message message) {
- long now = SystemClock.uptimeMillis();
- return new StringBuilder(" ")
- .append(message.getWhen() - now)
- .append(messageName(message.what))
- .append(" ").append(message.arg1)
- .append(" ").append(message.arg2)
- .append(" ").append(message.obj)
- .toString();
- }
-
- @Override
- public boolean processMessage(Message message) {
- if (MSG_DBG) {
- Log.d(TAG, getName() + messageToString(message));
- }
- return NOT_HANDLED;
- }
-
- @Override
- public String getName() {
- // All DhcpClient's states are inner classes with a well defined name.
- // Use getSimpleName() and avoid super's getName() creating new String instances.
- return getClass().getSimpleName();
- }
- }
-
- // Sends CMD_PRE_DHCP_ACTION to the controller, waits for the controller to respond with
- // CMD_PRE_DHCP_ACTION_COMPLETE, and then transitions to mOtherState.
- abstract class WaitBeforeOtherState extends LoggingState {
- protected State mOtherState;
-
- @Override
- public void enter() {
- super.enter();
- mController.sendMessage(CMD_PRE_DHCP_ACTION);
- }
-
- @Override
- public boolean processMessage(Message message) {
- super.processMessage(message);
- switch (message.what) {
- case CMD_PRE_DHCP_ACTION_COMPLETE:
- transitionTo(mOtherState);
- return HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
- }
-
- class StoppedState extends State {
- @Override
- public boolean processMessage(Message message) {
- switch (message.what) {
- case CMD_START_DHCP:
- if (mRegisteredForPreDhcpNotification) {
- transitionTo(mWaitBeforeStartState);
- } else {
- transitionTo(mDhcpInitState);
- }
- return HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
- }
-
- class WaitBeforeStartState extends WaitBeforeOtherState {
- public WaitBeforeStartState(State otherState) {
- super();
- mOtherState = otherState;
- }
- }
-
- class WaitBeforeRenewalState extends WaitBeforeOtherState {
- public WaitBeforeRenewalState(State otherState) {
- super();
- mOtherState = otherState;
- }
- }
-
- class DhcpState extends State {
- @Override
- public void enter() {
- clearDhcpState();
- if (initInterface() && initSockets()) {
- mReceiveThread = new ReceiveThread();
- mReceiveThread.start();
- } else {
- notifyFailure();
- transitionTo(mStoppedState);
- }
- }
-
- @Override
- public void exit() {
- if (mReceiveThread != null) {
- mReceiveThread.halt(); // Also closes sockets.
- mReceiveThread = null;
- }
- clearDhcpState();
- }
-
- @Override
- public boolean processMessage(Message message) {
- super.processMessage(message);
- switch (message.what) {
- case CMD_STOP_DHCP:
- transitionTo(mStoppedState);
- return HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
- }
-
- public boolean isValidPacket(DhcpPacket packet) {
- // TODO: check checksum.
- int xid = packet.getTransactionId();
- if (xid != mTransactionId) {
- Log.d(TAG, "Unexpected transaction ID " + xid + ", expected " + mTransactionId);
- return false;
- }
- if (!Arrays.equals(packet.getClientMac(), mHwAddr)) {
- Log.d(TAG, "MAC addr mismatch: got " +
- HexDump.toHexString(packet.getClientMac()) + ", expected " +
- HexDump.toHexString(packet.getClientMac()));
- return false;
- }
- return true;
- }
-
- public void setDhcpLeaseExpiry(DhcpPacket packet) {
- long leaseTimeMillis = packet.getLeaseTimeMillis();
- mDhcpLeaseExpiry =
- (leaseTimeMillis > 0) ? SystemClock.elapsedRealtime() + leaseTimeMillis : 0;
- }
-
- /**
- * Retransmits packets using jittered exponential backoff with an optional timeout. Packet
- * transmission is triggered by CMD_KICK, which is sent by an AlarmManager alarm. If a subclass
- * sets mTimeout to a positive value, then timeout() is called by an AlarmManager alarm mTimeout
- * milliseconds after entering the state. Kicks and timeouts are cancelled when leaving the
- * state.
- *
- * Concrete subclasses must implement sendPacket, which is called when the alarm fires and a
- * packet needs to be transmitted, and receivePacket, which is triggered by CMD_RECEIVED_PACKET
- * sent by the receive thread. They may also set mTimeout and implement timeout.
- */
- abstract class PacketRetransmittingState extends LoggingState {
-
- private int mTimer;
- protected int mTimeout = 0;
-
- @Override
- public void enter() {
- super.enter();
- initTimer();
- maybeInitTimeout();
- sendMessage(CMD_KICK);
- }
-
- @Override
- public boolean processMessage(Message message) {
- super.processMessage(message);
- switch (message.what) {
- case CMD_KICK:
- sendPacket();
- scheduleKick();
- return HANDLED;
- case CMD_RECEIVED_PACKET:
- receivePacket((DhcpPacket) message.obj);
- return HANDLED;
- case CMD_TIMEOUT:
- timeout();
- return HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
-
- @Override
- public void exit() {
- super.exit();
- mKickAlarm.cancel();
- mTimeoutAlarm.cancel();
- }
-
- abstract protected boolean sendPacket();
- abstract protected void receivePacket(DhcpPacket packet);
- protected void timeout() {}
-
- protected void initTimer() {
- mTimer = FIRST_TIMEOUT_MS;
- }
-
- protected int jitterTimer(int baseTimer) {
- int maxJitter = baseTimer / 10;
- int jitter = mRandom.nextInt(2 * maxJitter) - maxJitter;
- return baseTimer + jitter;
- }
-
- protected void scheduleKick() {
- long now = SystemClock.elapsedRealtime();
- long timeout = jitterTimer(mTimer);
- long alarmTime = now + timeout;
- mKickAlarm.schedule(alarmTime);
- mTimer *= 2;
- if (mTimer > MAX_TIMEOUT_MS) {
- mTimer = MAX_TIMEOUT_MS;
- }
- }
-
- protected void maybeInitTimeout() {
- if (mTimeout > 0) {
- long alarmTime = SystemClock.elapsedRealtime() + mTimeout;
- mTimeoutAlarm.schedule(alarmTime);
- }
- }
- }
-
- class DhcpInitState extends PacketRetransmittingState {
- public DhcpInitState() {
- super();
- }
-
- @Override
- public void enter() {
- super.enter();
- startNewTransaction();
- mLastInitEnterTime = SystemClock.elapsedRealtime();
- }
-
- protected boolean sendPacket() {
- return sendDiscoverPacket();
- }
-
- protected void receivePacket(DhcpPacket packet) {
- if (!isValidPacket(packet)) return;
- if (!(packet instanceof DhcpOfferPacket)) return;
- mOffer = packet.toDhcpResults();
- if (mOffer != null) {
- Log.d(TAG, "Got pending lease: " + mOffer);
- transitionTo(mDhcpRequestingState);
- }
- }
- }
-
- // Not implemented. We request the first offer we receive.
- class DhcpSelectingState extends LoggingState {
- }
-
- class DhcpRequestingState extends PacketRetransmittingState {
- public DhcpRequestingState() {
- mTimeout = DHCP_TIMEOUT_MS / 2;
- }
-
- protected boolean sendPacket() {
- return sendRequestPacket(
- INADDR_ANY, // ciaddr
- (Inet4Address) mOffer.ipAddress.getAddress(), // DHCP_REQUESTED_IP
- (Inet4Address) mOffer.serverAddress, // DHCP_SERVER_IDENTIFIER
- INADDR_BROADCAST); // packet destination address
- }
-
- protected void receivePacket(DhcpPacket packet) {
- if (!isValidPacket(packet)) return;
- if ((packet instanceof DhcpAckPacket)) {
- DhcpResults results = packet.toDhcpResults();
- if (results != null) {
- setDhcpLeaseExpiry(packet);
- acceptDhcpResults(results, "Confirmed");
- transitionTo(mConfiguringInterfaceState);
- }
- } else if (packet instanceof DhcpNakPacket) {
- // TODO: Wait a while before returning into INIT state.
- Log.d(TAG, "Received NAK, returning to INIT");
- mOffer = null;
- transitionTo(mDhcpInitState);
- }
- }
-
- @Override
- protected void timeout() {
- // After sending REQUESTs unsuccessfully for a while, go back to init.
- transitionTo(mDhcpInitState);
- }
- }
-
- class DhcpHaveLeaseState extends State {
- @Override
- public boolean processMessage(Message message) {
- switch (message.what) {
- case CMD_EXPIRE_DHCP:
- Log.d(TAG, "Lease expired!");
- notifyFailure();
- transitionTo(mDhcpInitState);
- return HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
-
- @Override
- public void exit() {
- // Clear any extant alarms.
- mRenewAlarm.cancel();
- mRebindAlarm.cancel();
- mExpiryAlarm.cancel();
- clearDhcpState();
- // Tell IpManager to clear the IPv4 address. There is no need to
- // wait for confirmation since any subsequent packets are sent from
- // INADDR_ANY anyway (DISCOVER, REQUEST).
- mController.sendMessage(CMD_CLEAR_LINKADDRESS);
- }
- }
-
- class ConfiguringInterfaceState extends LoggingState {
- @Override
- public void enter() {
- super.enter();
- mController.sendMessage(CMD_CONFIGURE_LINKADDRESS, mDhcpLease.ipAddress);
- }
-
- @Override
- public boolean processMessage(Message message) {
- super.processMessage(message);
- switch (message.what) {
- case EVENT_LINKADDRESS_CONFIGURED:
- transitionTo(mDhcpBoundState);
- return HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
- }
-
- class DhcpBoundState extends LoggingState {
- @Override
- public void enter() {
- super.enter();
- if (mDhcpLease.serverAddress != null && !connectUdpSock(mDhcpLease.serverAddress)) {
- // There's likely no point in going into DhcpInitState here, we'll probably
- // just repeat the transaction, get the same IP address as before, and fail.
- //
- // NOTE: It is observed that connectUdpSock() basically never fails, due to
- // SO_BINDTODEVICE. Examining the local socket address shows it will happily
- // return an IPv4 address from another interface, or even return "0.0.0.0".
- //
- // TODO: Consider deleting this check, following testing on several kernels.
- notifyFailure();
- transitionTo(mStoppedState);
- }
-
- scheduleLeaseTimers();
- logTimeToBoundState();
- }
-
- @Override
- public void exit() {
- super.exit();
- mLastBoundExitTime = SystemClock.elapsedRealtime();
- }
-
- @Override
- public boolean processMessage(Message message) {
- super.processMessage(message);
- switch (message.what) {
- case CMD_RENEW_DHCP:
- if (mRegisteredForPreDhcpNotification) {
- transitionTo(mWaitBeforeRenewalState);
- } else {
- transitionTo(mDhcpRenewingState);
- }
- return HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
-
- private void logTimeToBoundState() {
- long now = SystemClock.elapsedRealtime();
- if (mLastBoundExitTime > mLastInitEnterTime) {
- logState(DhcpClientEvent.RENEWING_BOUND, (int)(now - mLastBoundExitTime));
- } else {
- logState(DhcpClientEvent.INITIAL_BOUND, (int)(now - mLastInitEnterTime));
- }
- }
- }
-
- abstract class DhcpReacquiringState extends PacketRetransmittingState {
- protected String mLeaseMsg;
-
- @Override
- public void enter() {
- super.enter();
- startNewTransaction();
- }
-
- abstract protected Inet4Address packetDestination();
-
- protected boolean sendPacket() {
- return sendRequestPacket(
- (Inet4Address) mDhcpLease.ipAddress.getAddress(), // ciaddr
- INADDR_ANY, // DHCP_REQUESTED_IP
- null, // DHCP_SERVER_IDENTIFIER
- packetDestination()); // packet destination address
- }
-
- protected void receivePacket(DhcpPacket packet) {
- if (!isValidPacket(packet)) return;
- if ((packet instanceof DhcpAckPacket)) {
- final DhcpResults results = packet.toDhcpResults();
- if (results != null) {
- if (!mDhcpLease.ipAddress.equals(results.ipAddress)) {
- Log.d(TAG, "Renewed lease not for our current IP address!");
- notifyFailure();
- transitionTo(mDhcpInitState);
- }
- setDhcpLeaseExpiry(packet);
- // Updating our notion of DhcpResults here only causes the
- // DNS servers and routes to be updated in LinkProperties
- // in IpManager and by any overridden relevant handlers of
- // the registered IpManager.Callback. IP address changes
- // are not supported here.
- acceptDhcpResults(results, mLeaseMsg);
- transitionTo(mDhcpBoundState);
- }
- } else if (packet instanceof DhcpNakPacket) {
- Log.d(TAG, "Received NAK, returning to INIT");
- notifyFailure();
- transitionTo(mDhcpInitState);
- }
- }
- }
-
- class DhcpRenewingState extends DhcpReacquiringState {
- public DhcpRenewingState() {
- mLeaseMsg = "Renewed";
- }
-
- @Override
- public boolean processMessage(Message message) {
- if (super.processMessage(message) == HANDLED) {
- return HANDLED;
- }
-
- switch (message.what) {
- case CMD_REBIND_DHCP:
- transitionTo(mDhcpRebindingState);
- return HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
-
- @Override
- protected Inet4Address packetDestination() {
- // Not specifying a SERVER_IDENTIFIER option is a violation of RFC 2131, but...
- // http://b/25343517 . Try to make things work anyway by using broadcast renews.
- return (mDhcpLease.serverAddress != null) ?
- mDhcpLease.serverAddress : INADDR_BROADCAST;
- }
- }
-
- class DhcpRebindingState extends DhcpReacquiringState {
- public DhcpRebindingState() {
- mLeaseMsg = "Rebound";
- }
-
- @Override
- public void enter() {
- super.enter();
-
- // We need to broadcast and possibly reconnect the socket to a
- // completely different server.
- closeQuietly(mUdpSock);
- if (!initUdpSocket()) {
- Log.e(TAG, "Failed to recreate UDP socket");
- transitionTo(mDhcpInitState);
- }
- }
-
- @Override
- protected Inet4Address packetDestination() {
- return INADDR_BROADCAST;
- }
- }
-
- class DhcpInitRebootState extends LoggingState {
- }
-
- class DhcpRebootingState extends LoggingState {
- }
-
- private void logError(int errorCode) {
- mMetricsLog.log(mIfaceName, new DhcpErrorEvent(errorCode));
- }
-
- private void logState(String name, int durationMs) {
- final DhcpClientEvent event = new DhcpClientEvent.Builder()
- .setMsg(name)
- .setDurationMs(durationMs)
- .build();
- mMetricsLog.log(mIfaceName, event);
- }
-}
diff --git a/services/net/java/android/net/dhcp/DhcpDeclinePacket.java b/services/net/java/android/net/dhcp/DhcpDeclinePacket.java
deleted file mode 100644
index 7ecdea7..0000000
--- a/services/net/java/android/net/dhcp/DhcpDeclinePacket.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2010 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.dhcp;
-
-import java.net.Inet4Address;
-import java.nio.ByteBuffer;
-
-/**
- * This class implements the DHCP-DECLINE packet.
- */
-class DhcpDeclinePacket extends DhcpPacket {
- /**
- * Generates a DECLINE packet with the specified parameters.
- */
- DhcpDeclinePacket(int transId, short secs, Inet4Address clientIp, Inet4Address yourIp,
- Inet4Address nextIp, Inet4Address relayIp,
- byte[] clientMac) {
- super(transId, secs, clientIp, yourIp, nextIp, relayIp, clientMac, false);
- }
-
- public String toString() {
- String s = super.toString();
- return s + " DECLINE";
- }
-
- /**
- * Fills in a packet with the requested DECLINE attributes.
- */
- public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
- ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
-
- fillInPacket(encap, mClientIp, mYourIp, destUdp, srcUdp, result,
- DHCP_BOOTREQUEST, false);
- result.flip();
- return result;
- }
-
- /**
- * Adds optional parameters to the DECLINE packet.
- */
- void finishPacket(ByteBuffer buffer) {
- addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_DECLINE);
- addTlv(buffer, DHCP_CLIENT_IDENTIFIER, getClientId());
- // RFC 2131 says we MUST NOT include our common client TLVs or the parameter request list.
- addTlvEnd(buffer);
- }
-}
diff --git a/services/net/java/android/net/dhcp/DhcpDiscoverPacket.java b/services/net/java/android/net/dhcp/DhcpDiscoverPacket.java
deleted file mode 100644
index 11f2b61..0000000
--- a/services/net/java/android/net/dhcp/DhcpDiscoverPacket.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2010 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.dhcp;
-
-import java.net.Inet4Address;
-import java.nio.ByteBuffer;
-
-/**
- * This class implements the DHCP-DISCOVER packet.
- */
-class DhcpDiscoverPacket extends DhcpPacket {
- /**
- * The IP address of the client which sent this packet.
- */
- final Inet4Address mSrcIp;
-
- /**
- * Generates a DISCOVER packet with the specified parameters.
- */
- DhcpDiscoverPacket(int transId, short secs, Inet4Address relayIp, byte[] clientMac,
- boolean broadcast, Inet4Address srcIp) {
- super(transId, secs, INADDR_ANY, INADDR_ANY, INADDR_ANY, relayIp, clientMac, broadcast);
- mSrcIp = srcIp;
- }
-
- public String toString() {
- String s = super.toString();
- return s + " DISCOVER " +
- (mBroadcast ? "broadcast " : "unicast ");
- }
-
- /**
- * Fills in a packet with the requested DISCOVER parameters.
- */
- public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
- ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
- fillInPacket(encap, INADDR_BROADCAST, mSrcIp, destUdp, srcUdp, result, DHCP_BOOTREQUEST,
- mBroadcast);
- result.flip();
- return result;
- }
-
- /**
- * Adds optional parameters to a DISCOVER packet.
- */
- void finishPacket(ByteBuffer buffer) {
- addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_DISCOVER);
- addTlv(buffer, DHCP_CLIENT_IDENTIFIER, getClientId());
- addCommonClientTlvs(buffer);
- addTlv(buffer, DHCP_PARAMETER_LIST, mRequestedParams);
- addTlvEnd(buffer);
- }
-}
diff --git a/services/net/java/android/net/dhcp/DhcpInformPacket.java b/services/net/java/android/net/dhcp/DhcpInformPacket.java
deleted file mode 100644
index 7a83466..0000000
--- a/services/net/java/android/net/dhcp/DhcpInformPacket.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2010 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.dhcp;
-
-import java.net.Inet4Address;
-import java.nio.ByteBuffer;
-
-/**
- * This class implements the (unused) DHCP-INFORM packet.
- */
-class DhcpInformPacket extends DhcpPacket {
- /**
- * Generates an INFORM packet with the specified parameters.
- */
- DhcpInformPacket(int transId, short secs, Inet4Address clientIp, Inet4Address yourIp,
- Inet4Address nextIp, Inet4Address relayIp,
- byte[] clientMac) {
- super(transId, secs, clientIp, yourIp, nextIp, relayIp, clientMac, false);
- }
-
- public String toString() {
- String s = super.toString();
- return s + " INFORM";
- }
-
- /**
- * Builds an INFORM packet.
- */
- public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
- ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
-
- fillInPacket(encap, mClientIp, mYourIp, destUdp, srcUdp, result,
- DHCP_BOOTREQUEST, false);
- result.flip();
- return result;
- }
-
- /**
- * Adds additional parameters to the INFORM packet.
- */
- void finishPacket(ByteBuffer buffer) {
- addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_INFORM);
- addTlv(buffer, DHCP_CLIENT_IDENTIFIER, getClientId());
- addCommonClientTlvs(buffer);
- addTlv(buffer, DHCP_PARAMETER_LIST, mRequestedParams);
- addTlvEnd(buffer);
- }
-}
diff --git a/services/net/java/android/net/dhcp/DhcpNakPacket.java b/services/net/java/android/net/dhcp/DhcpNakPacket.java
deleted file mode 100644
index 1da0b73..0000000
--- a/services/net/java/android/net/dhcp/DhcpNakPacket.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2010 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.dhcp;
-
-import java.net.Inet4Address;
-import java.nio.ByteBuffer;
-
-/**
- * This class implements the DHCP-NAK packet.
- */
-class DhcpNakPacket extends DhcpPacket {
- /**
- * Generates a NAK packet with the specified parameters.
- */
- DhcpNakPacket(int transId, short secs, Inet4Address relayIp, byte[] clientMac,
- boolean broadcast) {
- super(transId, secs, INADDR_ANY /* clientIp */, INADDR_ANY /* yourIp */,
- INADDR_ANY /* nextIp */, relayIp, clientMac, broadcast);
- }
-
- public String toString() {
- String s = super.toString();
- return s + " NAK, reason " + (mMessage == null ? "(none)" : mMessage);
- }
-
- /**
- * Fills in a packet with the requested NAK attributes.
- */
- public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
- ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
- // Constructor does not set values for layers <= 3: use empty values
- Inet4Address destIp = INADDR_ANY;
- Inet4Address srcIp = INADDR_ANY;
-
- fillInPacket(encap, destIp, srcIp, destUdp, srcUdp, result, DHCP_BOOTREPLY, mBroadcast);
- result.flip();
- return result;
- }
-
- /**
- * Adds the optional parameters to the client-generated NAK packet.
- */
- void finishPacket(ByteBuffer buffer) {
- addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_NAK);
- addTlv(buffer, DHCP_SERVER_IDENTIFIER, mServerIdentifier);
- addTlv(buffer, DHCP_MESSAGE, mMessage);
- addTlvEnd(buffer);
- }
-}
diff --git a/services/net/java/android/net/dhcp/DhcpOfferPacket.java b/services/net/java/android/net/dhcp/DhcpOfferPacket.java
deleted file mode 100644
index 0eba77e..0000000
--- a/services/net/java/android/net/dhcp/DhcpOfferPacket.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2010 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.dhcp;
-
-import java.net.Inet4Address;
-import java.nio.ByteBuffer;
-
-/**
- * This class implements the DHCP-OFFER packet.
- */
-class DhcpOfferPacket extends DhcpPacket {
- /**
- * The IP address of the server which sent this packet.
- */
- private final Inet4Address mSrcIp;
-
- /**
- * Generates a OFFER packet with the specified parameters.
- */
- DhcpOfferPacket(int transId, short secs, boolean broadcast, Inet4Address serverAddress,
- Inet4Address relayIp, Inet4Address clientIp, Inet4Address yourIp, byte[] clientMac) {
- super(transId, secs, clientIp, yourIp, serverAddress, relayIp, clientMac, broadcast);
- mSrcIp = serverAddress;
- }
-
- public String toString() {
- String s = super.toString();
- String dnsServers = ", DNS servers: ";
-
- if (mDnsServers != null) {
- for (Inet4Address dnsServer: mDnsServers) {
- dnsServers += dnsServer + " ";
- }
- }
-
- return s + " OFFER, ip " + mYourIp + ", mask " + mSubnetMask +
- dnsServers + ", gateways " + mGateways +
- " lease time " + mLeaseTime + ", domain " + mDomainName;
- }
-
- /**
- * Fills in a packet with the specified OFFER attributes.
- */
- public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
- ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
- Inet4Address destIp = mBroadcast ? INADDR_BROADCAST : mYourIp;
- Inet4Address srcIp = mBroadcast ? INADDR_ANY : mSrcIp;
-
- fillInPacket(encap, destIp, srcIp, destUdp, srcUdp, result,
- DHCP_BOOTREPLY, mBroadcast);
- result.flip();
- return result;
- }
-
- /**
- * Adds the optional parameters to the server-generated OFFER packet.
- */
- void finishPacket(ByteBuffer buffer) {
- addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_OFFER);
- addTlv(buffer, DHCP_SERVER_IDENTIFIER, mServerIdentifier);
-
- addCommonServerTlvs(buffer);
- addTlvEnd(buffer);
- }
-}
diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java
deleted file mode 100644
index ce8b7e7..0000000
--- a/services/net/java/android/net/dhcp/DhcpPacket.java
+++ /dev/null
@@ -1,1364 +0,0 @@
-package android.net.dhcp;
-
-import android.annotation.Nullable;
-import android.net.DhcpResults;
-import android.net.LinkAddress;
-import android.net.NetworkUtils;
-import android.net.metrics.DhcpErrorEvent;
-import android.os.Build;
-import android.os.SystemProperties;
-import android.system.OsConstants;
-import android.text.TextUtils;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.io.UnsupportedEncodingException;
-import java.net.Inet4Address;
-import java.net.UnknownHostException;
-import java.nio.BufferUnderflowException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.ShortBuffer;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Defines basic data and operations needed to build and use packets for the
- * DHCP protocol. Subclasses create the specific packets used at each
- * stage of the negotiation.
- *
- * @hide
- */
-public abstract class DhcpPacket {
- protected static final String TAG = "DhcpPacket";
-
- // TODO: use NetworkStackConstants.IPV4_MIN_MTU once this class is moved to the network stack.
- private static final int IPV4_MIN_MTU = 68;
-
- // dhcpcd has a minimum lease of 20 seconds, but DhcpStateMachine would refuse to wake up the
- // CPU for anything shorter than 5 minutes. For sanity's sake, this must be higher than the
- // DHCP client timeout.
- public static final int MINIMUM_LEASE = 60;
- public static final int INFINITE_LEASE = (int) 0xffffffff;
-
- public static final Inet4Address INADDR_ANY = (Inet4Address) Inet4Address.ANY;
- public static final Inet4Address INADDR_BROADCAST = (Inet4Address) Inet4Address.ALL;
- public static final byte[] ETHER_BROADCAST = new byte[] {
- (byte) 0xff, (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff, (byte) 0xff,
- };
-
- /**
- * Packet encapsulations.
- */
- public static final int ENCAP_L2 = 0; // EthernetII header included
- public static final int ENCAP_L3 = 1; // IP/UDP header included
- public static final int ENCAP_BOOTP = 2; // BOOTP contents only
-
- /**
- * Minimum length of a DHCP packet, excluding options, in the above encapsulations.
- */
- public static final int MIN_PACKET_LENGTH_BOOTP = 236; // See diagram in RFC 2131, section 2.
- public static final int MIN_PACKET_LENGTH_L3 = MIN_PACKET_LENGTH_BOOTP + 20 + 8;
- public static final int MIN_PACKET_LENGTH_L2 = MIN_PACKET_LENGTH_L3 + 14;
-
- public static final int HWADDR_LEN = 16;
- public static final int MAX_OPTION_LEN = 255;
-
- /**
- * The minimum and maximum MTU that we are prepared to use. We set the minimum to the minimum
- * IPv6 MTU because the IPv6 stack enters unusual codepaths when the link MTU drops below 1280,
- * and does not recover if the MTU is brought above 1280 again. We set the maximum to 1500
- * because in general it is risky to assume that the hardware is able to send/receive packets
- * larger than 1500 bytes even if the network supports it.
- */
- private static final int MIN_MTU = 1280;
- private static final int MAX_MTU = 1500;
-
- /**
- * IP layer definitions.
- */
- private static final byte IP_TYPE_UDP = (byte) 0x11;
-
- /**
- * IP: Version 4, Header Length 20 bytes
- */
- private static final byte IP_VERSION_HEADER_LEN = (byte) 0x45;
-
- /**
- * IP: Flags 0, Fragment Offset 0, Don't Fragment
- */
- private static final short IP_FLAGS_OFFSET = (short) 0x4000;
-
- /**
- * IP: TOS
- */
- private static final byte IP_TOS_LOWDELAY = (byte) 0x10;
-
- /**
- * IP: TTL -- use default 64 from RFC1340
- */
- private static final byte IP_TTL = (byte) 0x40;
-
- /**
- * The client DHCP port.
- */
- static final short DHCP_CLIENT = (short) 68;
-
- /**
- * The server DHCP port.
- */
- static final short DHCP_SERVER = (short) 67;
-
- /**
- * The message op code indicating a request from a client.
- */
- protected static final byte DHCP_BOOTREQUEST = (byte) 1;
-
- /**
- * The message op code indicating a response from the server.
- */
- protected static final byte DHCP_BOOTREPLY = (byte) 2;
-
- /**
- * The code type used to identify an Ethernet MAC address in the
- * Client-ID field.
- */
- protected static final byte CLIENT_ID_ETHER = (byte) 1;
-
- /**
- * The maximum length of a packet that can be constructed.
- */
- protected static final int MAX_LENGTH = 1500;
-
- /**
- * The magic cookie that identifies this as a DHCP packet instead of BOOTP.
- */
- private static final int DHCP_MAGIC_COOKIE = 0x63825363;
-
- /**
- * DHCP Optional Type: DHCP Subnet Mask
- */
- protected static final byte DHCP_SUBNET_MASK = 1;
- protected Inet4Address mSubnetMask;
-
- /**
- * DHCP Optional Type: DHCP Router
- */
- protected static final byte DHCP_ROUTER = 3;
- protected List <Inet4Address> mGateways;
-
- /**
- * DHCP Optional Type: DHCP DNS Server
- */
- protected static final byte DHCP_DNS_SERVER = 6;
- protected List<Inet4Address> mDnsServers;
-
- /**
- * DHCP Optional Type: DHCP Host Name
- */
- protected static final byte DHCP_HOST_NAME = 12;
- protected String mHostName;
-
- /**
- * DHCP Optional Type: DHCP DOMAIN NAME
- */
- protected static final byte DHCP_DOMAIN_NAME = 15;
- protected String mDomainName;
-
- /**
- * DHCP Optional Type: DHCP Interface MTU
- */
- protected static final byte DHCP_MTU = 26;
- protected Short mMtu;
-
- /**
- * DHCP Optional Type: DHCP BROADCAST ADDRESS
- */
- protected static final byte DHCP_BROADCAST_ADDRESS = 28;
- protected Inet4Address mBroadcastAddress;
-
- /**
- * DHCP Optional Type: Vendor specific information
- */
- protected static final byte DHCP_VENDOR_INFO = 43;
- protected String mVendorInfo;
-
- /**
- * Value of the vendor specific option used to indicate that the network is metered
- */
- public static final String VENDOR_INFO_ANDROID_METERED = "ANDROID_METERED";
-
- /**
- * DHCP Optional Type: DHCP Requested IP Address
- */
- protected static final byte DHCP_REQUESTED_IP = 50;
- protected Inet4Address mRequestedIp;
-
- /**
- * DHCP Optional Type: DHCP Lease Time
- */
- protected static final byte DHCP_LEASE_TIME = 51;
- protected Integer mLeaseTime;
-
- /**
- * DHCP Optional Type: DHCP Message Type
- */
- protected static final byte DHCP_MESSAGE_TYPE = 53;
- // the actual type values
- protected static final byte DHCP_MESSAGE_TYPE_DISCOVER = 1;
- protected static final byte DHCP_MESSAGE_TYPE_OFFER = 2;
- protected static final byte DHCP_MESSAGE_TYPE_REQUEST = 3;
- protected static final byte DHCP_MESSAGE_TYPE_DECLINE = 4;
- protected static final byte DHCP_MESSAGE_TYPE_ACK = 5;
- protected static final byte DHCP_MESSAGE_TYPE_NAK = 6;
- protected static final byte DHCP_MESSAGE_TYPE_RELEASE = 7;
- protected static final byte DHCP_MESSAGE_TYPE_INFORM = 8;
-
- /**
- * DHCP Optional Type: DHCP Server Identifier
- */
- protected static final byte DHCP_SERVER_IDENTIFIER = 54;
- protected Inet4Address mServerIdentifier;
-
- /**
- * DHCP Optional Type: DHCP Parameter List
- */
- protected static final byte DHCP_PARAMETER_LIST = 55;
- protected byte[] mRequestedParams;
-
- /**
- * DHCP Optional Type: DHCP MESSAGE
- */
- protected static final byte DHCP_MESSAGE = 56;
- protected String mMessage;
-
- /**
- * DHCP Optional Type: Maximum DHCP Message Size
- */
- protected static final byte DHCP_MAX_MESSAGE_SIZE = 57;
- protected Short mMaxMessageSize;
-
- /**
- * DHCP Optional Type: DHCP Renewal Time Value
- */
- protected static final byte DHCP_RENEWAL_TIME = 58;
- protected Integer mT1;
-
- /**
- * DHCP Optional Type: Rebinding Time Value
- */
- protected static final byte DHCP_REBINDING_TIME = 59;
- protected Integer mT2;
-
- /**
- * DHCP Optional Type: Vendor Class Identifier
- */
- protected static final byte DHCP_VENDOR_CLASS_ID = 60;
- protected String mVendorId;
-
- /**
- * DHCP Optional Type: DHCP Client Identifier
- */
- protected static final byte DHCP_CLIENT_IDENTIFIER = 61;
- protected byte[] mClientId;
-
- /**
- * DHCP zero-length option code: pad
- */
- protected static final byte DHCP_OPTION_PAD = 0x00;
-
- /**
- * DHCP zero-length option code: end of options
- */
- protected static final byte DHCP_OPTION_END = (byte) 0xff;
-
- /**
- * The transaction identifier used in this particular DHCP negotiation
- */
- protected final int mTransId;
-
- /**
- * The seconds field in the BOOTP header. Per RFC, should be nonzero in client requests only.
- */
- protected final short mSecs;
-
- /**
- * The IP address of the client host. This address is typically
- * proposed by the client (from an earlier DHCP negotiation) or
- * supplied by the server.
- */
- protected final Inet4Address mClientIp;
- protected final Inet4Address mYourIp;
- private final Inet4Address mNextIp;
- protected final Inet4Address mRelayIp;
-
- /**
- * Does the client request a broadcast response?
- */
- protected boolean mBroadcast;
-
- /**
- * The six-octet MAC of the client.
- */
- protected final byte[] mClientMac;
-
- /**
- * Asks the packet object to create a ByteBuffer serialization of
- * the packet for transmission.
- */
- public abstract ByteBuffer buildPacket(int encap, short destUdp,
- short srcUdp);
-
- /**
- * Allows the concrete class to fill in packet-type-specific details,
- * typically optional parameters at the end of the packet.
- */
- abstract void finishPacket(ByteBuffer buffer);
-
- // Set in unit tests, to ensure that the test does not break when run on different devices and
- // on different releases.
- static String testOverrideVendorId = null;
- static String testOverrideHostname = null;
-
- protected DhcpPacket(int transId, short secs, Inet4Address clientIp, Inet4Address yourIp,
- Inet4Address nextIp, Inet4Address relayIp,
- byte[] clientMac, boolean broadcast) {
- mTransId = transId;
- mSecs = secs;
- mClientIp = clientIp;
- mYourIp = yourIp;
- mNextIp = nextIp;
- mRelayIp = relayIp;
- mClientMac = clientMac;
- mBroadcast = broadcast;
- }
-
- /**
- * Returns the transaction ID.
- */
- public int getTransactionId() {
- return mTransId;
- }
-
- /**
- * Returns the client MAC.
- */
- public byte[] getClientMac() {
- return mClientMac;
- }
-
- // TODO: refactor DhcpClient to set clientId when constructing packets and remove
- // hasExplicitClientId logic
- /**
- * Returns whether a client ID was set in the options for this packet.
- */
- public boolean hasExplicitClientId() {
- return mClientId != null;
- }
-
- /**
- * Convenience method to return the client ID if it was set explicitly, or null otherwise.
- */
- @Nullable
- public byte[] getExplicitClientIdOrNull() {
- return hasExplicitClientId() ? getClientId() : null;
- }
-
- /**
- * Returns the client ID. If not set explicitly, this follows RFC 2132 and creates a client ID
- * based on the hardware address.
- */
- public byte[] getClientId() {
- final byte[] clientId;
- if (hasExplicitClientId()) {
- clientId = Arrays.copyOf(mClientId, mClientId.length);
- } else {
- clientId = new byte[mClientMac.length + 1];
- clientId[0] = CLIENT_ID_ETHER;
- System.arraycopy(mClientMac, 0, clientId, 1, mClientMac.length);
- }
- 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
- * finishPacket() to insert the per-packet contents.
- */
- protected void fillInPacket(int encap, Inet4Address destIp,
- Inet4Address srcIp, short destUdp, short srcUdp, ByteBuffer buf,
- byte requestCode, boolean broadcast) {
- byte[] destIpArray = destIp.getAddress();
- byte[] srcIpArray = srcIp.getAddress();
- int ipHeaderOffset = 0;
- int ipLengthOffset = 0;
- int ipChecksumOffset = 0;
- int endIpHeader = 0;
- int udpHeaderOffset = 0;
- int udpLengthOffset = 0;
- int udpChecksumOffset = 0;
-
- buf.clear();
- buf.order(ByteOrder.BIG_ENDIAN);
-
- if (encap == ENCAP_L2) {
- buf.put(ETHER_BROADCAST);
- buf.put(mClientMac);
- buf.putShort((short) OsConstants.ETH_P_IP);
- }
-
- // if a full IP packet needs to be generated, put the IP & UDP
- // headers in place, and pre-populate with artificial values
- // needed to seed the IP checksum.
- if (encap <= ENCAP_L3) {
- ipHeaderOffset = buf.position();
- buf.put(IP_VERSION_HEADER_LEN);
- buf.put(IP_TOS_LOWDELAY); // tos: IPTOS_LOWDELAY
- ipLengthOffset = buf.position();
- buf.putShort((short)0); // length
- buf.putShort((short)0); // id
- buf.putShort(IP_FLAGS_OFFSET); // ip offset: don't fragment
- buf.put(IP_TTL); // TTL: use default 64 from RFC1340
- buf.put(IP_TYPE_UDP);
- ipChecksumOffset = buf.position();
- buf.putShort((short) 0); // checksum
-
- buf.put(srcIpArray);
- buf.put(destIpArray);
- endIpHeader = buf.position();
-
- // UDP header
- udpHeaderOffset = buf.position();
- buf.putShort(srcUdp);
- buf.putShort(destUdp);
- udpLengthOffset = buf.position();
- buf.putShort((short) 0); // length
- udpChecksumOffset = buf.position();
- buf.putShort((short) 0); // UDP checksum -- initially zero
- }
-
- // DHCP payload
- buf.put(requestCode);
- buf.put((byte) 1); // Hardware Type: Ethernet
- buf.put((byte) mClientMac.length); // Hardware Address Length
- buf.put((byte) 0); // Hop Count
- buf.putInt(mTransId); // Transaction ID
- buf.putShort(mSecs); // Elapsed Seconds
-
- if (broadcast) {
- buf.putShort((short) 0x8000); // Flags
- } else {
- buf.putShort((short) 0x0000); // Flags
- }
-
- buf.put(mClientIp.getAddress());
- buf.put(mYourIp.getAddress());
- buf.put(mNextIp.getAddress());
- buf.put(mRelayIp.getAddress());
- buf.put(mClientMac);
- buf.position(buf.position() +
- (HWADDR_LEN - mClientMac.length) // pad addr to 16 bytes
- + 64 // empty server host name (64 bytes)
- + 128); // empty boot file name (128 bytes)
- buf.putInt(DHCP_MAGIC_COOKIE); // magic number
- finishPacket(buf);
-
- // round up to an even number of octets
- if ((buf.position() & 1) == 1) {
- buf.put((byte) 0);
- }
-
- // If an IP packet is being built, the IP & UDP checksums must be
- // computed.
- if (encap <= ENCAP_L3) {
- // fix UDP header: insert length
- short udpLen = (short)(buf.position() - udpHeaderOffset);
- buf.putShort(udpLengthOffset, udpLen);
- // fix UDP header: checksum
- // checksum for UDP at udpChecksumOffset
- int udpSeed = 0;
-
- // apply IPv4 pseudo-header. Read IP address src and destination
- // values from the IP header and accumulate checksum.
- udpSeed += intAbs(buf.getShort(ipChecksumOffset + 2));
- udpSeed += intAbs(buf.getShort(ipChecksumOffset + 4));
- udpSeed += intAbs(buf.getShort(ipChecksumOffset + 6));
- udpSeed += intAbs(buf.getShort(ipChecksumOffset + 8));
-
- // accumulate extra data for the pseudo-header
- udpSeed += IP_TYPE_UDP;
- udpSeed += udpLen;
- // and compute UDP checksum
- buf.putShort(udpChecksumOffset, (short) checksum(buf, udpSeed,
- udpHeaderOffset,
- buf.position()));
- // fix IP header: insert length
- buf.putShort(ipLengthOffset, (short)(buf.position() - ipHeaderOffset));
- // fixup IP-header checksum
- buf.putShort(ipChecksumOffset,
- (short) checksum(buf, 0, ipHeaderOffset, endIpHeader));
- }
- }
-
- /**
- * Converts a signed short value to an unsigned int value. Needed
- * because Java does not have unsigned types.
- */
- private static int intAbs(short v) {
- return v & 0xFFFF;
- }
-
- /**
- * Performs an IP checksum (used in IP header and across UDP
- * payload) on the specified portion of a ByteBuffer. The seed
- * allows the checksum to commence with a specified value.
- */
- private int checksum(ByteBuffer buf, int seed, int start, int end) {
- int sum = seed;
- int bufPosition = buf.position();
-
- // set position of original ByteBuffer, so that the ShortBuffer
- // will be correctly initialized
- buf.position(start);
- ShortBuffer shortBuf = buf.asShortBuffer();
-
- // re-set ByteBuffer position
- buf.position(bufPosition);
-
- short[] shortArray = new short[(end - start) / 2];
- shortBuf.get(shortArray);
-
- for (short s : shortArray) {
- sum += intAbs(s);
- }
-
- start += shortArray.length * 2;
-
- // see if a singleton byte remains
- if (end != start) {
- short b = buf.get(start);
-
- // make it unsigned
- if (b < 0) {
- b += 256;
- }
-
- sum += b * 256;
- }
-
- sum = ((sum >> 16) & 0xFFFF) + (sum & 0xFFFF);
- sum = ((sum + ((sum >> 16) & 0xFFFF)) & 0xFFFF);
- int negated = ~sum;
- return intAbs((short) negated);
- }
-
- /**
- * Adds an optional parameter containing a single byte value.
- */
- protected static void addTlv(ByteBuffer buf, byte type, byte value) {
- buf.put(type);
- buf.put((byte) 1);
- buf.put(value);
- }
-
- /**
- * Adds an optional parameter containing an array of bytes.
- *
- * <p>This method is a no-op if the payload argument is null.
- */
- protected static void addTlv(ByteBuffer buf, byte type, @Nullable byte[] payload) {
- if (payload != null) {
- if (payload.length > MAX_OPTION_LEN) {
- throw new IllegalArgumentException("DHCP option too long: "
- + payload.length + " vs. " + MAX_OPTION_LEN);
- }
- buf.put(type);
- buf.put((byte) payload.length);
- buf.put(payload);
- }
- }
-
- /**
- * Adds an optional parameter containing an IP address.
- *
- * <p>This method is a no-op if the address argument is null.
- */
- protected static void addTlv(ByteBuffer buf, byte type, @Nullable Inet4Address addr) {
- if (addr != null) {
- addTlv(buf, type, addr.getAddress());
- }
- }
-
- /**
- * Adds an optional parameter containing a list of IP addresses.
- *
- * <p>This method is a no-op if the addresses argument is null or empty.
- */
- protected static void addTlv(ByteBuffer buf, byte type, @Nullable List<Inet4Address> addrs) {
- if (addrs == null || addrs.size() == 0) return;
-
- int optionLen = 4 * addrs.size();
- if (optionLen > MAX_OPTION_LEN) {
- throw new IllegalArgumentException("DHCP option too long: "
- + optionLen + " vs. " + MAX_OPTION_LEN);
- }
-
- buf.put(type);
- buf.put((byte)(optionLen));
-
- for (Inet4Address addr : addrs) {
- buf.put(addr.getAddress());
- }
- }
-
- /**
- * Adds an optional parameter containing a short integer.
- *
- * <p>This method is a no-op if the value argument is null.
- */
- protected static void addTlv(ByteBuffer buf, byte type, @Nullable Short value) {
- if (value != null) {
- buf.put(type);
- buf.put((byte) 2);
- buf.putShort(value.shortValue());
- }
- }
-
- /**
- * Adds an optional parameter containing a simple integer.
- *
- * <p>This method is a no-op if the value argument is null.
- */
- protected static void addTlv(ByteBuffer buf, byte type, @Nullable Integer value) {
- if (value != null) {
- buf.put(type);
- buf.put((byte) 4);
- buf.putInt(value.intValue());
- }
- }
-
- /**
- * Adds an optional parameter containing an ASCII string.
- *
- * <p>This method is a no-op if the string argument is null.
- */
- protected static void addTlv(ByteBuffer buf, byte type, @Nullable String str) {
- if (str != null) {
- try {
- addTlv(buf, type, str.getBytes("US-ASCII"));
- } catch (UnsupportedEncodingException e) {
- throw new IllegalArgumentException("String is not US-ASCII: " + str);
- }
- }
- }
-
- /**
- * Adds the special end-of-optional-parameters indicator.
- */
- protected static void addTlvEnd(ByteBuffer buf) {
- buf.put((byte) 0xFF);
- }
-
- private String getVendorId() {
- if (testOverrideVendorId != null) return testOverrideVendorId;
- return "android-dhcp-" + Build.VERSION.RELEASE;
- }
-
- private String getHostname() {
- if (testOverrideHostname != null) return testOverrideHostname;
- return SystemProperties.get("net.hostname");
- }
-
- /**
- * Adds common client TLVs.
- *
- * TODO: Does this belong here? The alternative would be to modify all the buildXyzPacket
- * methods to take them.
- */
- protected void addCommonClientTlvs(ByteBuffer buf) {
- addTlv(buf, DHCP_MAX_MESSAGE_SIZE, (short) MAX_LENGTH);
- addTlv(buf, DHCP_VENDOR_CLASS_ID, getVendorId());
- final String hn = getHostname();
- if (!TextUtils.isEmpty(hn)) addTlv(buf, DHCP_HOST_NAME, hn);
- }
-
- protected void addCommonServerTlvs(ByteBuffer buf) {
- addTlv(buf, DHCP_LEASE_TIME, mLeaseTime);
- if (mLeaseTime != null && mLeaseTime != INFINITE_LEASE) {
- // The client should renew at 1/2 the lease-expiry interval
- addTlv(buf, DHCP_RENEWAL_TIME, (int) (Integer.toUnsignedLong(mLeaseTime) / 2));
- // Default rebinding time is set as below by RFC2131
- addTlv(buf, DHCP_REBINDING_TIME,
- (int) (Integer.toUnsignedLong(mLeaseTime) * 875L / 1000L));
- }
- addTlv(buf, DHCP_SUBNET_MASK, mSubnetMask);
- addTlv(buf, DHCP_BROADCAST_ADDRESS, mBroadcastAddress);
- 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);
- }
- }
-
- /**
- * Converts a MAC from an array of octets to an ASCII string.
- */
- public static String macToString(byte[] mac) {
- String macAddr = "";
-
- for (int i = 0; i < mac.length; i++) {
- String hexString = "0" + Integer.toHexString(mac[i]);
-
- // substring operation grabs the last 2 digits: this
- // allows signed bytes to be converted correctly.
- macAddr += hexString.substring(hexString.length() - 2);
-
- if (i != (mac.length - 1)) {
- macAddr += ":";
- }
- }
-
- return macAddr;
- }
-
- public String toString() {
- String macAddr = macToString(mClientMac);
-
- return macAddr;
- }
-
- /**
- * Reads a four-octet value from a ByteBuffer and construct
- * an IPv4 address from that value.
- */
- private static Inet4Address readIpAddress(ByteBuffer packet) {
- Inet4Address result = null;
- byte[] ipAddr = new byte[4];
- packet.get(ipAddr);
-
- try {
- result = (Inet4Address) Inet4Address.getByAddress(ipAddr);
- } catch (UnknownHostException ex) {
- // ipAddr is numeric, so this should not be
- // triggered. However, if it is, just nullify
- result = null;
- }
-
- return result;
- }
-
- /**
- * Reads a string of specified length from the buffer.
- */
- private static String readAsciiString(ByteBuffer buf, int byteCount, boolean nullOk) {
- byte[] bytes = new byte[byteCount];
- buf.get(bytes);
- int length = bytes.length;
- if (!nullOk) {
- // Stop at the first null byte. This is because some DHCP options (e.g., the domain
- // name) are passed to netd via FrameworkListener, which refuses arguments containing
- // null bytes. We don't do this by default because vendorInfo is an opaque string which
- // could in theory contain null bytes.
- for (length = 0; length < bytes.length; length++) {
- if (bytes[length] == 0) {
- break;
- }
- }
- }
- return new String(bytes, 0, length, StandardCharsets.US_ASCII);
- }
-
- private static boolean isPacketToOrFromClient(short udpSrcPort, short udpDstPort) {
- return (udpSrcPort == DHCP_CLIENT) || (udpDstPort == DHCP_CLIENT);
- }
-
- private static boolean isPacketServerToServer(short udpSrcPort, short udpDstPort) {
- return (udpSrcPort == DHCP_SERVER) && (udpDstPort == DHCP_SERVER);
- }
-
- public static class ParseException extends Exception {
- public final int errorCode;
- public ParseException(int errorCode, String msg, Object... args) {
- super(String.format(msg, args));
- this.errorCode = errorCode;
- }
- }
-
- /**
- * Creates a concrete DhcpPacket from the supplied ByteBuffer. The
- * buffer may have an L2 encapsulation (which is the full EthernetII
- * format starting with the source-address MAC) or an L3 encapsulation
- * (which starts with the IP header).
- * <br>
- * A subset of the optional parameters are parsed and are stored
- * in object fields.
- */
- @VisibleForTesting
- static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType) throws ParseException
- {
- // bootp parameters
- int transactionId;
- short secs;
- Inet4Address clientIp;
- Inet4Address yourIp;
- Inet4Address nextIp;
- Inet4Address relayIp;
- byte[] clientMac;
- byte[] clientId = null;
- List<Inet4Address> dnsServers = new ArrayList<>();
- List<Inet4Address> gateways = new ArrayList<>(); // aka router
- Inet4Address serverIdentifier = null;
- Inet4Address netMask = null;
- String message = null;
- String vendorId = null;
- String vendorInfo = null;
- byte[] expectedParams = null;
- String hostName = null;
- String domainName = null;
- Inet4Address ipSrc = null;
- Inet4Address ipDst = null;
- Inet4Address bcAddr = null;
- Inet4Address requestedIp = null;
-
- // The following are all unsigned integers. Internally we store them as signed integers of
- // the same length because that way we're guaranteed that they can't be out of the range of
- // the unsigned field in the packet. Callers wanting to pass in an unsigned value will need
- // to cast it.
- Short mtu = null;
- Short maxMessageSize = null;
- Integer leaseTime = null;
- Integer T1 = null;
- Integer T2 = null;
-
- // dhcp options
- byte dhcpType = (byte) 0xFF;
-
- packet.order(ByteOrder.BIG_ENDIAN);
-
- // check to see if we need to parse L2, IP, and UDP encaps
- if (pktType == ENCAP_L2) {
- if (packet.remaining() < MIN_PACKET_LENGTH_L2) {
- throw new ParseException(DhcpErrorEvent.L2_TOO_SHORT,
- "L2 packet too short, %d < %d", packet.remaining(), MIN_PACKET_LENGTH_L2);
- }
-
- byte[] l2dst = new byte[6];
- byte[] l2src = new byte[6];
-
- packet.get(l2dst);
- packet.get(l2src);
-
- short l2type = packet.getShort();
-
- if (l2type != OsConstants.ETH_P_IP) {
- throw new ParseException(DhcpErrorEvent.L2_WRONG_ETH_TYPE,
- "Unexpected L2 type 0x%04x, expected 0x%04x", l2type, OsConstants.ETH_P_IP);
- }
- }
-
- if (pktType <= ENCAP_L3) {
- if (packet.remaining() < MIN_PACKET_LENGTH_L3) {
- throw new ParseException(DhcpErrorEvent.L3_TOO_SHORT,
- "L3 packet too short, %d < %d", packet.remaining(), MIN_PACKET_LENGTH_L3);
- }
-
- byte ipTypeAndLength = packet.get();
- int ipVersion = (ipTypeAndLength & 0xf0) >> 4;
- if (ipVersion != 4) {
- throw new ParseException(
- DhcpErrorEvent.L3_NOT_IPV4, "Invalid IP version %d", ipVersion);
- }
-
- // System.out.println("ipType is " + ipType);
- byte ipDiffServicesField = packet.get();
- short ipTotalLength = packet.getShort();
- short ipIdentification = packet.getShort();
- byte ipFlags = packet.get();
- byte ipFragOffset = packet.get();
- byte ipTTL = packet.get();
- byte ipProto = packet.get();
- short ipChksm = packet.getShort();
-
- ipSrc = readIpAddress(packet);
- ipDst = readIpAddress(packet);
-
- if (ipProto != IP_TYPE_UDP) {
- throw new ParseException(
- DhcpErrorEvent.L4_NOT_UDP, "Protocol not UDP: %d", ipProto);
- }
-
- // Skip options. This cannot cause us to read beyond the end of the buffer because the
- // IPv4 header cannot be more than (0x0f * 4) = 60 bytes long, and that is less than
- // MIN_PACKET_LENGTH_L3.
- int optionWords = ((ipTypeAndLength & 0x0f) - 5);
- for (int i = 0; i < optionWords; i++) {
- packet.getInt();
- }
-
- // assume UDP
- short udpSrcPort = packet.getShort();
- short udpDstPort = packet.getShort();
- short udpLen = packet.getShort();
- short udpChkSum = packet.getShort();
-
- // Only accept packets to or from the well-known client port (expressly permitting
- // packets from ports other than the well-known server port; http://b/24687559), and
- // server-to-server packets, e.g. for relays.
- if (!isPacketToOrFromClient(udpSrcPort, udpDstPort) &&
- !isPacketServerToServer(udpSrcPort, udpDstPort)) {
- // This should almost never happen because we use SO_ATTACH_FILTER on the packet
- // socket to drop packets that don't have the right source ports. However, it's
- // possible that a packet arrives between when the socket is bound and when the
- // filter is set. http://b/26696823 .
- throw new ParseException(DhcpErrorEvent.L4_WRONG_PORT,
- "Unexpected UDP ports %d->%d", udpSrcPort, udpDstPort);
- }
- }
-
- // We need to check the length even for ENCAP_L3 because the IPv4 header is variable-length.
- if (pktType > ENCAP_BOOTP || packet.remaining() < MIN_PACKET_LENGTH_BOOTP) {
- throw new ParseException(DhcpErrorEvent.BOOTP_TOO_SHORT,
- "Invalid type or BOOTP packet too short, %d < %d",
- packet.remaining(), MIN_PACKET_LENGTH_BOOTP);
- }
-
- byte type = packet.get();
- byte hwType = packet.get();
- int addrLen = packet.get() & 0xff;
- byte hops = packet.get();
- transactionId = packet.getInt();
- secs = packet.getShort();
- short bootpFlags = packet.getShort();
- boolean broadcast = (bootpFlags & 0x8000) != 0;
- byte[] ipv4addr = new byte[4];
-
- try {
- packet.get(ipv4addr);
- clientIp = (Inet4Address) Inet4Address.getByAddress(ipv4addr);
- packet.get(ipv4addr);
- yourIp = (Inet4Address) Inet4Address.getByAddress(ipv4addr);
- packet.get(ipv4addr);
- nextIp = (Inet4Address) Inet4Address.getByAddress(ipv4addr);
- packet.get(ipv4addr);
- relayIp = (Inet4Address) Inet4Address.getByAddress(ipv4addr);
- } catch (UnknownHostException ex) {
- throw new ParseException(DhcpErrorEvent.L3_INVALID_IP,
- "Invalid IPv4 address: %s", Arrays.toString(ipv4addr));
- }
-
- // Some DHCP servers have been known to announce invalid client hardware address values such
- // as 0xff. The legacy DHCP client accepted these becuause it does not check the length at
- // all but only checks that the interface MAC address matches the first bytes of the address
- // in the packets. We're a bit stricter: if the length is obviously invalid (i.e., bigger
- // than the size of the field), we fudge it to 6 (Ethernet). http://b/23725795
- // TODO: evaluate whether to make this test more liberal.
- if (addrLen > HWADDR_LEN) {
- addrLen = ETHER_BROADCAST.length;
- }
-
- clientMac = new byte[addrLen];
- packet.get(clientMac);
-
- // skip over address padding (16 octets allocated)
- packet.position(packet.position() + (16 - addrLen)
- + 64 // skip server host name (64 chars)
- + 128); // skip boot file name (128 chars)
-
- // Ensure this is a DHCP packet with a magic cookie, and not BOOTP. http://b/31850211
- if (packet.remaining() < 4) {
- throw new ParseException(DhcpErrorEvent.DHCP_NO_COOKIE, "not a DHCP message");
- }
-
- int dhcpMagicCookie = packet.getInt();
- if (dhcpMagicCookie != DHCP_MAGIC_COOKIE) {
- throw new ParseException(DhcpErrorEvent.DHCP_BAD_MAGIC_COOKIE,
- "Bad magic cookie 0x%08x, should be 0x%08x",
- dhcpMagicCookie, DHCP_MAGIC_COOKIE);
- }
-
- // parse options
- boolean notFinishedOptions = true;
-
- while ((packet.position() < packet.limit()) && notFinishedOptions) {
- final byte optionType = packet.get(); // cannot underflow because position < limit
- try {
- if (optionType == DHCP_OPTION_END) {
- notFinishedOptions = false;
- } else if (optionType == DHCP_OPTION_PAD) {
- // The pad option doesn't have a length field. Nothing to do.
- } else {
- int optionLen = packet.get() & 0xFF;
- int expectedLen = 0;
-
- switch(optionType) {
- case DHCP_SUBNET_MASK:
- netMask = readIpAddress(packet);
- expectedLen = 4;
- break;
- case DHCP_ROUTER:
- for (expectedLen = 0; expectedLen < optionLen; expectedLen += 4) {
- gateways.add(readIpAddress(packet));
- }
- break;
- case DHCP_DNS_SERVER:
- for (expectedLen = 0; expectedLen < optionLen; expectedLen += 4) {
- dnsServers.add(readIpAddress(packet));
- }
- break;
- case DHCP_HOST_NAME:
- expectedLen = optionLen;
- hostName = readAsciiString(packet, optionLen, false);
- break;
- case DHCP_MTU:
- expectedLen = 2;
- mtu = packet.getShort();
- break;
- case DHCP_DOMAIN_NAME:
- expectedLen = optionLen;
- domainName = readAsciiString(packet, optionLen, false);
- break;
- case DHCP_BROADCAST_ADDRESS:
- bcAddr = readIpAddress(packet);
- expectedLen = 4;
- break;
- case DHCP_REQUESTED_IP:
- requestedIp = readIpAddress(packet);
- expectedLen = 4;
- break;
- case DHCP_LEASE_TIME:
- leaseTime = Integer.valueOf(packet.getInt());
- expectedLen = 4;
- break;
- case DHCP_MESSAGE_TYPE:
- dhcpType = packet.get();
- expectedLen = 1;
- break;
- case DHCP_SERVER_IDENTIFIER:
- serverIdentifier = readIpAddress(packet);
- expectedLen = 4;
- break;
- case DHCP_PARAMETER_LIST:
- expectedParams = new byte[optionLen];
- packet.get(expectedParams);
- expectedLen = optionLen;
- break;
- case DHCP_MESSAGE:
- expectedLen = optionLen;
- message = readAsciiString(packet, optionLen, false);
- break;
- case DHCP_MAX_MESSAGE_SIZE:
- expectedLen = 2;
- maxMessageSize = Short.valueOf(packet.getShort());
- break;
- case DHCP_RENEWAL_TIME:
- expectedLen = 4;
- T1 = Integer.valueOf(packet.getInt());
- break;
- case DHCP_REBINDING_TIME:
- expectedLen = 4;
- T2 = Integer.valueOf(packet.getInt());
- break;
- case DHCP_VENDOR_CLASS_ID:
- expectedLen = optionLen;
- // Embedded nulls are safe as this does not get passed to netd.
- vendorId = readAsciiString(packet, optionLen, true);
- break;
- case DHCP_CLIENT_IDENTIFIER: { // Client identifier
- byte[] id = new byte[optionLen];
- packet.get(id);
- expectedLen = optionLen;
- } break;
- case DHCP_VENDOR_INFO:
- expectedLen = optionLen;
- // Embedded nulls are safe as this does not get passed to netd.
- vendorInfo = readAsciiString(packet, optionLen, true);
- break;
- default:
- // ignore any other parameters
- for (int i = 0; i < optionLen; i++) {
- expectedLen++;
- byte throwaway = packet.get();
- }
- }
-
- if (expectedLen != optionLen) {
- final int errorCode = DhcpErrorEvent.errorCodeWithOption(
- DhcpErrorEvent.DHCP_INVALID_OPTION_LENGTH, optionType);
- throw new ParseException(errorCode,
- "Invalid length %d for option %d, expected %d",
- optionLen, optionType, expectedLen);
- }
- }
- } catch (BufferUnderflowException e) {
- final int errorCode = DhcpErrorEvent.errorCodeWithOption(
- DhcpErrorEvent.BUFFER_UNDERFLOW, optionType);
- throw new ParseException(errorCode, "BufferUnderflowException");
- }
- }
-
- DhcpPacket newPacket;
-
- switch(dhcpType) {
- case (byte) 0xFF:
- throw new ParseException(DhcpErrorEvent.DHCP_NO_MSG_TYPE,
- "No DHCP message type option");
- case DHCP_MESSAGE_TYPE_DISCOVER:
- newPacket = new DhcpDiscoverPacket(transactionId, secs, relayIp, clientMac,
- broadcast, ipSrc);
- break;
- case DHCP_MESSAGE_TYPE_OFFER:
- newPacket = new DhcpOfferPacket(
- transactionId, secs, broadcast, ipSrc, relayIp, clientIp, yourIp, clientMac);
- break;
- case DHCP_MESSAGE_TYPE_REQUEST:
- newPacket = new DhcpRequestPacket(
- transactionId, secs, clientIp, relayIp, clientMac, broadcast);
- break;
- case DHCP_MESSAGE_TYPE_DECLINE:
- newPacket = new DhcpDeclinePacket(
- transactionId, secs, clientIp, yourIp, nextIp, relayIp,
- clientMac);
- break;
- case DHCP_MESSAGE_TYPE_ACK:
- newPacket = new DhcpAckPacket(
- transactionId, secs, broadcast, ipSrc, relayIp, clientIp, yourIp, clientMac);
- break;
- case DHCP_MESSAGE_TYPE_NAK:
- newPacket = new DhcpNakPacket(
- transactionId, secs, relayIp, clientMac, broadcast);
- break;
- case DHCP_MESSAGE_TYPE_RELEASE:
- if (serverIdentifier == null) {
- throw new ParseException(DhcpErrorEvent.MISC_ERROR,
- "DHCPRELEASE without server identifier");
- }
- newPacket = new DhcpReleasePacket(
- transactionId, serverIdentifier, clientIp, relayIp, clientMac);
- break;
- case DHCP_MESSAGE_TYPE_INFORM:
- newPacket = new DhcpInformPacket(
- transactionId, secs, clientIp, yourIp, nextIp, relayIp,
- clientMac);
- break;
- default:
- throw new ParseException(DhcpErrorEvent.DHCP_UNKNOWN_MSG_TYPE,
- "Unimplemented DHCP type %d", dhcpType);
- }
-
- newPacket.mBroadcastAddress = bcAddr;
- newPacket.mClientId = clientId;
- newPacket.mDnsServers = dnsServers;
- newPacket.mDomainName = domainName;
- newPacket.mGateways = gateways;
- newPacket.mHostName = hostName;
- newPacket.mLeaseTime = leaseTime;
- newPacket.mMessage = message;
- newPacket.mMtu = mtu;
- newPacket.mRequestedIp = requestedIp;
- newPacket.mRequestedParams = expectedParams;
- newPacket.mServerIdentifier = serverIdentifier;
- newPacket.mSubnetMask = netMask;
- newPacket.mMaxMessageSize = maxMessageSize;
- newPacket.mT1 = T1;
- newPacket.mT2 = T2;
- newPacket.mVendorId = vendorId;
- newPacket.mVendorInfo = vendorInfo;
- return newPacket;
- }
-
- /**
- * 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 {
- ByteBuffer buffer = ByteBuffer.wrap(packet, 0, length).order(ByteOrder.BIG_ENDIAN);
- try {
- return decodeFullPacket(buffer, pktType);
- } catch (ParseException e) {
- throw e;
- } catch (Exception e) {
- throw new ParseException(DhcpErrorEvent.PARSING_ERROR, e.getMessage());
- }
- }
-
- /**
- * Construct a DhcpResults object from a DHCP reply packet.
- */
- public DhcpResults toDhcpResults() {
- Inet4Address ipAddress = mYourIp;
- if (ipAddress.equals(Inet4Address.ANY)) {
- ipAddress = mClientIp;
- if (ipAddress.equals(Inet4Address.ANY)) {
- return null;
- }
- }
-
- int prefixLength;
- if (mSubnetMask != null) {
- try {
- prefixLength = NetworkUtils.netmaskToPrefixLength(mSubnetMask);
- } catch (IllegalArgumentException e) {
- // Non-contiguous netmask.
- return null;
- }
- } else {
- prefixLength = NetworkUtils.getImplicitNetmask(ipAddress);
- }
-
- DhcpResults results = new DhcpResults();
- try {
- results.ipAddress = new LinkAddress(ipAddress, prefixLength);
- } catch (IllegalArgumentException e) {
- return null;
- }
-
- if (mGateways.size() > 0) {
- results.gateway = mGateways.get(0);
- }
-
- results.dnsServers.addAll(mDnsServers);
- results.domains = mDomainName;
- results.serverAddress = mServerIdentifier;
- results.vendorInfo = mVendorInfo;
- results.leaseDuration = (mLeaseTime != null) ? mLeaseTime : INFINITE_LEASE;
- results.mtu = (mMtu != null && MIN_MTU <= mMtu && mMtu <= MAX_MTU) ? mMtu : 0;
-
- return results;
- }
-
- /**
- * Returns the parsed lease time, in milliseconds, or 0 for infinite.
- */
- public long getLeaseTimeMillis() {
- // dhcpcd treats the lack of a lease time option as an infinite lease.
- if (mLeaseTime == null || mLeaseTime == INFINITE_LEASE) {
- return 0;
- } else if (0 <= mLeaseTime && mLeaseTime < MINIMUM_LEASE) {
- return MINIMUM_LEASE * 1000;
- } else {
- return (mLeaseTime & 0xffffffffL) * 1000;
- }
- }
-
- /**
- * Builds a DHCP-DISCOVER packet from the required specified
- * parameters.
- */
- public static ByteBuffer buildDiscoverPacket(int encap, int transactionId,
- short secs, byte[] clientMac, boolean broadcast, byte[] expectedParams) {
- DhcpPacket pkt = new DhcpDiscoverPacket(transactionId, secs, INADDR_ANY /* relayIp */,
- clientMac, broadcast, INADDR_ANY /* srcIp */);
- pkt.mRequestedParams = expectedParams;
- return pkt.buildPacket(encap, DHCP_SERVER, DHCP_CLIENT);
- }
-
- /**
- * Builds a DHCP-OFFER packet from the required specified
- * parameters.
- */
- public static ByteBuffer buildOfferPacket(int encap, int transactionId,
- 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, String hostname, boolean metered,
- short mtu) {
- DhcpPacket pkt = new DhcpOfferPacket(
- transactionId, (short) 0, broadcast, serverIpAddr, relayIp,
- INADDR_ANY /* clientIp */, yourIp, mac);
- pkt.mGateways = gateways;
- 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;
- }
- return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER);
- }
-
- /**
- * Builds a DHCP-ACK packet from the required specified parameters.
- */
- public static ByteBuffer buildAckPacket(int encap, int transactionId,
- 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, String hostname, boolean metered,
- short mtu) {
- DhcpPacket pkt = new DhcpAckPacket(
- transactionId, (short) 0, broadcast, serverIpAddr, relayIp, requestClientIp, yourIp,
- mac);
- pkt.mGateways = gateways;
- 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;
- }
- return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER);
- }
-
- /**
- * Builds a DHCP-NAK packet from the required specified parameters.
- */
- public static ByteBuffer buildNakPacket(int encap, int transactionId, Inet4Address serverIpAddr,
- Inet4Address relayIp, byte[] mac, boolean broadcast, String message) {
- DhcpPacket pkt = new DhcpNakPacket(
- transactionId, (short) 0, relayIp, mac, broadcast);
- pkt.mMessage = message;
- pkt.mServerIdentifier = serverIpAddr;
- return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER);
- }
-
- /**
- * Builds a DHCP-REQUEST packet from the required specified parameters.
- */
- public static ByteBuffer buildRequestPacket(int encap,
- int transactionId, short secs, Inet4Address clientIp, boolean broadcast,
- byte[] clientMac, Inet4Address requestedIpAddress,
- Inet4Address serverIdentifier, byte[] requestedParams, String hostName) {
- DhcpPacket pkt = new DhcpRequestPacket(transactionId, secs, clientIp,
- INADDR_ANY /* relayIp */, clientMac, broadcast);
- pkt.mRequestedIp = requestedIpAddress;
- pkt.mServerIdentifier = serverIdentifier;
- pkt.mHostName = hostName;
- pkt.mRequestedParams = requestedParams;
- ByteBuffer result = pkt.buildPacket(encap, DHCP_SERVER, DHCP_CLIENT);
- return result;
- }
-}
diff --git a/services/net/java/android/net/dhcp/DhcpReleasePacket.java b/services/net/java/android/net/dhcp/DhcpReleasePacket.java
deleted file mode 100644
index 3958303..0000000
--- a/services/net/java/android/net/dhcp/DhcpReleasePacket.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2018 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.dhcp;
-
-import java.net.Inet4Address;
-import java.nio.ByteBuffer;
-
-/**
- * Implements DHCP-RELEASE
- */
-class DhcpReleasePacket extends DhcpPacket {
-
- final Inet4Address mClientAddr;
-
- /**
- * Generates a RELEASE packet with the specified parameters.
- */
- public DhcpReleasePacket(int transId, Inet4Address serverId, Inet4Address clientAddr,
- Inet4Address relayIp, byte[] clientMac) {
- super(transId, (short)0, clientAddr, INADDR_ANY /* yourIp */, INADDR_ANY /* nextIp */,
- relayIp, clientMac, false /* broadcast */);
- mServerIdentifier = serverId;
- mClientAddr = clientAddr;
- }
-
-
- @Override
- public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
- ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
- fillInPacket(encap, mServerIdentifier /* destIp */, mClientIp /* srcIp */, destUdp, srcUdp,
- result, DHCP_BOOTREPLY, mBroadcast);
- result.flip();
- return result;
- }
-
- @Override
- void finishPacket(ByteBuffer buffer) {
- addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_RELEASE);
- addTlv(buffer, DHCP_CLIENT_IDENTIFIER, getClientId());
- addTlv(buffer, DHCP_SERVER_IDENTIFIER, mServerIdentifier);
- addCommonClientTlvs(buffer);
- addTlvEnd(buffer);
- }
-}
diff --git a/services/net/java/android/net/dhcp/DhcpRequestPacket.java b/services/net/java/android/net/dhcp/DhcpRequestPacket.java
deleted file mode 100644
index 231d0457..0000000
--- a/services/net/java/android/net/dhcp/DhcpRequestPacket.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2010 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.dhcp;
-
-import android.util.Log;
-
-import java.net.Inet4Address;
-import java.nio.ByteBuffer;
-
-/**
- * This class implements the DHCP-REQUEST packet.
- */
-class DhcpRequestPacket extends DhcpPacket {
- /**
- * Generates a REQUEST packet with the specified parameters.
- */
- DhcpRequestPacket(int transId, short secs, Inet4Address clientIp, Inet4Address relayIp,
- byte[] clientMac, boolean broadcast) {
- super(transId, secs, clientIp, INADDR_ANY, INADDR_ANY, relayIp, clientMac, broadcast);
- }
-
- public String toString() {
- String s = super.toString();
- return s + " REQUEST, desired IP " + mRequestedIp + " from host '"
- + mHostName + "', param list length "
- + (mRequestedParams == null ? 0 : mRequestedParams.length);
- }
-
- /**
- * Fills in a packet with the requested REQUEST attributes.
- */
- public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
- ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
-
- fillInPacket(encap, INADDR_BROADCAST, INADDR_ANY, destUdp, srcUdp,
- result, DHCP_BOOTREQUEST, mBroadcast);
- result.flip();
- return result;
- }
-
- /**
- * Adds the optional parameters to the client-generated REQUEST packet.
- */
- void finishPacket(ByteBuffer buffer) {
- addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_REQUEST);
- addTlv(buffer, DHCP_CLIENT_IDENTIFIER, getClientId());
- if (!INADDR_ANY.equals(mRequestedIp)) {
- addTlv(buffer, DHCP_REQUESTED_IP, mRequestedIp);
- }
- if (!INADDR_ANY.equals(mServerIdentifier)) {
- addTlv(buffer, DHCP_SERVER_IDENTIFIER, mServerIdentifier);
- }
- addCommonClientTlvs(buffer);
- addTlv(buffer, DHCP_PARAMETER_LIST, mRequestedParams);
- addTlvEnd(buffer);
- }
-}
diff --git a/services/net/java/android/net/ip/ConnectivityPacketTracker.java b/services/net/java/android/net/ip/ConnectivityPacketTracker.java
deleted file mode 100644
index 385dd52..0000000
--- a/services/net/java/android/net/ip/ConnectivityPacketTracker.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.ip;
-
-import static android.net.util.SocketUtils.makePacketSocketAddress;
-import static android.system.OsConstants.AF_PACKET;
-import static android.system.OsConstants.ARPHRD_ETHER;
-import static android.system.OsConstants.ETH_P_ALL;
-import static android.system.OsConstants.SOCK_RAW;
-
-import android.net.NetworkUtils;
-import android.net.util.ConnectivityPacketSummary;
-import android.net.util.InterfaceParams;
-import android.net.util.PacketReader;
-import android.os.Handler;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.text.TextUtils;
-import android.util.LocalLog;
-import android.util.Log;
-
-import libcore.util.HexEncoding;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-
-
-/**
- * Critical connectivity packet tracking daemon.
- *
- * Tracks ARP, DHCPv4, and IPv6 RS/RA/NS/NA packets.
- *
- * This class's constructor, start() and stop() methods must only be called
- * from the same thread on which the passed in |log| is accessed.
- *
- * Log lines include a hexdump of the packet, which can be decoded via:
- *
- * echo -n H3XSTR1NG | sed -e 's/\([0-9A-F][0-9A-F]\)/\1 /g' -e 's/^/000000 /'
- * | text2pcap - -
- * | tcpdump -n -vv -e -r -
- *
- * @hide
- */
-public class ConnectivityPacketTracker {
- private static final String TAG = ConnectivityPacketTracker.class.getSimpleName();
- private static final boolean DBG = false;
- private static final String MARK_START = "--- START ---";
- private static final String MARK_STOP = "--- STOP ---";
- private static final String MARK_NAMED_START = "--- START (%s) ---";
- private static final String MARK_NAMED_STOP = "--- STOP (%s) ---";
-
- private final String mTag;
- private final LocalLog mLog;
- private final PacketReader mPacketListener;
- private boolean mRunning;
- private String mDisplayName;
-
- public ConnectivityPacketTracker(Handler h, InterfaceParams ifParams, LocalLog log) {
- if (ifParams == null) throw new IllegalArgumentException("null InterfaceParams");
-
- mTag = TAG + "." + ifParams.name;
- mLog = log;
- mPacketListener = new PacketListener(h, ifParams);
- }
-
- public void start(String displayName) {
- mRunning = true;
- mDisplayName = displayName;
- mPacketListener.start();
- }
-
- public void stop() {
- mPacketListener.stop();
- mRunning = false;
- mDisplayName = null;
- }
-
- private final class PacketListener extends PacketReader {
- private final InterfaceParams mInterface;
-
- PacketListener(Handler h, InterfaceParams ifParams) {
- super(h, ifParams.defaultMtu);
- mInterface = ifParams;
- }
-
- @Override
- protected FileDescriptor createFd() {
- FileDescriptor s = null;
- try {
- s = Os.socket(AF_PACKET, SOCK_RAW, 0);
- NetworkUtils.attachControlPacketFilter(s, ARPHRD_ETHER);
- Os.bind(s, makePacketSocketAddress((short) ETH_P_ALL, mInterface.index));
- } catch (ErrnoException | IOException e) {
- logError("Failed to create packet tracking socket: ", e);
- closeFd(s);
- return null;
- }
- return s;
- }
-
- @Override
- protected void handlePacket(byte[] recvbuf, int length) {
- final String summary = ConnectivityPacketSummary.summarize(
- mInterface.macAddr, recvbuf, length);
- if (summary == null) return;
-
- if (DBG) Log.d(mTag, summary);
- addLogEntry(summary +
- "\n[" + new String(HexEncoding.encode(recvbuf, 0, length)) + "]");
- }
-
- @Override
- protected void onStart() {
- final String msg = TextUtils.isEmpty(mDisplayName)
- ? MARK_START
- : String.format(MARK_NAMED_START, mDisplayName);
- mLog.log(msg);
- }
-
- @Override
- protected void onStop() {
- String msg = TextUtils.isEmpty(mDisplayName)
- ? MARK_STOP
- : String.format(MARK_NAMED_STOP, mDisplayName);
- if (!mRunning) msg += " (packet listener stopped unexpectedly)";
- mLog.log(msg);
- }
-
- @Override
- protected void logError(String msg, Exception e) {
- Log.e(mTag, msg, e);
- addLogEntry(msg + e);
- }
-
- private void addLogEntry(String entry) {
- mLog.log(entry);
- }
- }
-}
diff --git a/services/net/java/android/net/ip/IpClient.java b/services/net/java/android/net/ip/IpClient.java
deleted file mode 100644
index 233b86f..0000000
--- a/services/net/java/android/net/ip/IpClient.java
+++ /dev/null
@@ -1,1762 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.ip;
-
-import static android.net.shared.LinkPropertiesParcelableUtil.fromStableParcelable;
-
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.DhcpResults;
-import android.net.INetd;
-import android.net.IpPrefix;
-import android.net.LinkAddress;
-import android.net.LinkProperties;
-import android.net.Network;
-import android.net.ProvisioningConfigurationParcelable;
-import android.net.ProxyInfo;
-import android.net.ProxyInfoParcelable;
-import android.net.RouteInfo;
-import android.net.StaticIpConfiguration;
-import android.net.apf.ApfCapabilities;
-import android.net.apf.ApfFilter;
-import android.net.dhcp.DhcpClient;
-import android.net.metrics.IpConnectivityLog;
-import android.net.metrics.IpManagerEvent;
-import android.net.shared.InitialConfiguration;
-import android.net.util.InterfaceParams;
-import android.net.util.NetdService;
-import android.net.util.SharedLog;
-import android.os.ConditionVariable;
-import android.os.INetworkManagementService;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.text.TextUtils;
-import android.util.LocalLog;
-import android.util.Log;
-import android.util.SparseArray;
-
-import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.IState;
-import com.android.internal.util.IndentingPrintWriter;
-import com.android.internal.util.MessageUtils;
-import com.android.internal.util.Preconditions;
-import com.android.internal.util.State;
-import com.android.internal.util.StateMachine;
-import com.android.internal.util.WakeupMessage;
-import com.android.server.net.NetlinkTracker;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.net.InetAddress;
-import java.util.Collection;
-import java.util.List;
-import java.util.Objects;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CountDownLatch;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
-
-
-/**
- * IpClient
- *
- * This class provides the interface to IP-layer provisioning and maintenance
- * functionality that can be used by transport layers like Wi-Fi, Ethernet,
- * et cetera.
- *
- * [ Lifetime ]
- * IpClient is designed to be instantiated as soon as the interface name is
- * known and can be as long-lived as the class containing it (i.e. declaring
- * it "private final" is okay).
- *
- * @hide
- */
-public class IpClient extends StateMachine {
- private static final boolean DBG = false;
-
- // For message logging.
- private static final Class[] sMessageClasses = { IpClient.class, DhcpClient.class };
- private static final SparseArray<String> sWhatToString =
- MessageUtils.findMessageNames(sMessageClasses);
- // Two static concurrent hashmaps of interface name to logging classes.
- // One holds StateMachine logs and the other connectivity packet logs.
- private static final ConcurrentHashMap<String, SharedLog> sSmLogs = new ConcurrentHashMap<>();
- private static final ConcurrentHashMap<String, LocalLog> sPktLogs = new ConcurrentHashMap<>();
-
- // If |args| is non-empty, assume it's a list of interface names for which
- // we should print IpClient logs (filter out all others).
- public static void dumpAllLogs(PrintWriter writer, String[] args) {
- for (String ifname : sSmLogs.keySet()) {
- if (!ArrayUtils.isEmpty(args) && !ArrayUtils.contains(args, ifname)) continue;
-
- writer.println(String.format("--- BEGIN %s ---", ifname));
-
- final SharedLog smLog = sSmLogs.get(ifname);
- if (smLog != null) {
- writer.println("State machine log:");
- smLog.dump(null, writer, null);
- }
-
- writer.println("");
-
- final LocalLog pktLog = sPktLogs.get(ifname);
- if (pktLog != null) {
- writer.println("Connectivity packet log:");
- pktLog.readOnlyLocalLog().dump(null, writer, null);
- }
-
- writer.println(String.format("--- END %s ---", ifname));
- }
- }
-
- /**
- * TODO: remove after migrating clients to use IpClientCallbacks directly
- * @see IpClientCallbacks
- */
- public static class Callback extends IpClientCallbacks {}
-
- /**
- * TODO: remove once clients are migrated to IpClientUtil.WaitForProvisioningCallback
- * @see IpClientUtil.WaitForProvisioningCallbacks
- */
- public static class WaitForProvisioningCallback
- extends IpClientUtil.WaitForProvisioningCallbacks {}
-
- // Use a wrapper class to log in order to ensure complete and detailed
- // logging. This method is lighter weight than annotations/reflection
- // and has the following benefits:
- //
- // - No invoked method can be forgotten.
- // Any new method added to IpClient.Callback must be overridden
- // here or it will never be called.
- //
- // - No invoking call site can be forgotten.
- // Centralized logging in this way means call sites don't need to
- // remember to log, and therefore no call site can be forgotten.
- //
- // - No variation in log format among call sites.
- // Encourages logging of any available arguments, and all call sites
- // are necessarily logged identically.
- //
- // NOTE: Log first because passed objects may or may not be thread-safe and
- // once passed on to the callback they may be modified by another thread.
- //
- // TODO: Find an lighter weight approach.
- private class LoggingCallbackWrapper extends IpClientCallbacks {
- private static final String PREFIX = "INVOKE ";
- private final IpClientCallbacks mCallback;
-
- LoggingCallbackWrapper(IpClientCallbacks callback) {
- mCallback = (callback != null) ? callback : new IpClientCallbacks();
- }
-
- private void log(String msg) {
- mLog.log(PREFIX + msg);
- }
-
- @Override
- public void onPreDhcpAction() {
- log("onPreDhcpAction()");
- mCallback.onPreDhcpAction();
- }
- @Override
- public void onPostDhcpAction() {
- log("onPostDhcpAction()");
- mCallback.onPostDhcpAction();
- }
- @Override
- public void onNewDhcpResults(DhcpResults dhcpResults) {
- log("onNewDhcpResults({" + dhcpResults + "})");
- mCallback.onNewDhcpResults(dhcpResults);
- }
- @Override
- public void onProvisioningSuccess(LinkProperties newLp) {
- log("onProvisioningSuccess({" + newLp + "})");
- mCallback.onProvisioningSuccess(newLp);
- }
- @Override
- public void onProvisioningFailure(LinkProperties newLp) {
- log("onProvisioningFailure({" + newLp + "})");
- mCallback.onProvisioningFailure(newLp);
- }
- @Override
- public void onLinkPropertiesChange(LinkProperties newLp) {
- log("onLinkPropertiesChange({" + newLp + "})");
- mCallback.onLinkPropertiesChange(newLp);
- }
- @Override
- public void onReachabilityLost(String logMsg) {
- log("onReachabilityLost(" + logMsg + ")");
- mCallback.onReachabilityLost(logMsg);
- }
- @Override
- public void onQuit() {
- log("onQuit()");
- mCallback.onQuit();
- }
- @Override
- public void installPacketFilter(byte[] filter) {
- log("installPacketFilter(byte[" + filter.length + "])");
- mCallback.installPacketFilter(filter);
- }
- @Override
- public void startReadPacketFilter() {
- log("startReadPacketFilter()");
- mCallback.startReadPacketFilter();
- }
- @Override
- public void setFallbackMulticastFilter(boolean enabled) {
- log("setFallbackMulticastFilter(" + enabled + ")");
- mCallback.setFallbackMulticastFilter(enabled);
- }
- @Override
- public void setNeighborDiscoveryOffload(boolean enable) {
- log("setNeighborDiscoveryOffload(" + enable + ")");
- mCallback.setNeighborDiscoveryOffload(enable);
- }
- }
-
- /**
- * TODO: remove after migrating clients to use the shared configuration class directly.
- * @see android.net.shared.ProvisioningConfiguration
- */
- public static class ProvisioningConfiguration
- extends android.net.shared.ProvisioningConfiguration {
- public ProvisioningConfiguration(android.net.shared.ProvisioningConfiguration other) {
- super(other);
- }
-
- /**
- * @see android.net.shared.ProvisioningConfiguration.Builder
- */
- public static class Builder extends android.net.shared.ProvisioningConfiguration.Builder {
- // Override all methods to have a return type matching this Builder
- @Override
- public Builder withoutIPv4() {
- super.withoutIPv4();
- return this;
- }
-
- @Override
- public Builder withoutIPv6() {
- super.withoutIPv6();
- return this;
- }
-
- @Override
- public Builder withoutMultinetworkPolicyTracker() {
- super.withoutMultinetworkPolicyTracker();
- return this;
- }
-
- @Override
- public Builder withoutIpReachabilityMonitor() {
- super.withoutIpReachabilityMonitor();
- return this;
- }
-
- @Override
- public Builder withPreDhcpAction() {
- super.withPreDhcpAction();
- return this;
- }
-
- @Override
- public Builder withPreDhcpAction(int dhcpActionTimeoutMs) {
- super.withPreDhcpAction(dhcpActionTimeoutMs);
- return this;
- }
-
- @Override
- public Builder withStaticConfiguration(StaticIpConfiguration staticConfig) {
- super.withStaticConfiguration(staticConfig);
- return this;
- }
-
- @Override
- public Builder withApfCapabilities(ApfCapabilities apfCapabilities) {
- super.withApfCapabilities(apfCapabilities);
- return this;
- }
-
- @Override
- public Builder withProvisioningTimeoutMs(int timeoutMs) {
- super.withProvisioningTimeoutMs(timeoutMs);
- return this;
- }
-
- @Override
- public Builder withRandomMacAddress() {
- super.withRandomMacAddress();
- return this;
- }
-
- @Override
- public Builder withStableMacAddress() {
- super.withStableMacAddress();
- return this;
- }
-
- @Override
- public Builder withNetwork(Network network) {
- super.withNetwork(network);
- return this;
- }
-
- @Override
- public Builder withDisplayName(String displayName) {
- super.withDisplayName(displayName);
- return this;
- }
-
- @Override
- public ProvisioningConfiguration build() {
- return new ProvisioningConfiguration(mConfig);
- }
- }
- }
-
- public static final String DUMP_ARG = "ipclient";
- public static final String DUMP_ARG_CONFIRM = "confirm";
-
- private static final int CMD_TERMINATE_AFTER_STOP = 1;
- private static final int CMD_STOP = 2;
- private static final int CMD_START = 3;
- private static final int CMD_CONFIRM = 4;
- private static final int EVENT_PRE_DHCP_ACTION_COMPLETE = 5;
- // Triggered by NetlinkTracker to communicate netlink events.
- private static final int EVENT_NETLINK_LINKPROPERTIES_CHANGED = 6;
- private static final int CMD_UPDATE_TCP_BUFFER_SIZES = 7;
- private static final int CMD_UPDATE_HTTP_PROXY = 8;
- private static final int CMD_SET_MULTICAST_FILTER = 9;
- private static final int EVENT_PROVISIONING_TIMEOUT = 10;
- private static final int EVENT_DHCPACTION_TIMEOUT = 11;
- private static final int EVENT_READ_PACKET_FILTER_COMPLETE = 12;
-
- // Internal commands to use instead of trying to call transitionTo() inside
- // a given State's enter() method. Calling transitionTo() from enter/exit
- // encounters a Log.wtf() that can cause trouble on eng builds.
- private static final int CMD_JUMP_STARTED_TO_RUNNING = 100;
- private static final int CMD_JUMP_RUNNING_TO_STOPPING = 101;
- private static final int CMD_JUMP_STOPPING_TO_STOPPED = 102;
-
- // IpClient shares a handler with DhcpClient: commands must not overlap
- public static final int DHCPCLIENT_CMD_BASE = 1000;
-
- private static final int MAX_LOG_RECORDS = 500;
- private static final int MAX_PACKET_RECORDS = 100;
-
- private static final boolean NO_CALLBACKS = false;
- private static final boolean SEND_CALLBACKS = true;
-
- // This must match the interface prefix in clatd.c.
- // TODO: Revert this hack once IpClient and Nat464Xlat work in concert.
- private static final String CLAT_PREFIX = "v4-";
-
- private static final int IMMEDIATE_FAILURE_DURATION = 0;
-
- private static final int PROV_CHANGE_STILL_NOT_PROVISIONED = 1;
- private static final int PROV_CHANGE_LOST_PROVISIONING = 2;
- private static final int PROV_CHANGE_GAINED_PROVISIONING = 3;
- private static final int PROV_CHANGE_STILL_PROVISIONED = 4;
-
- private final State mStoppedState = new StoppedState();
- private final State mStoppingState = new StoppingState();
- private final State mStartedState = new StartedState();
- private final State mRunningState = new RunningState();
-
- private final String mTag;
- private final Context mContext;
- private final String mInterfaceName;
- private final String mClatInterfaceName;
- @VisibleForTesting
- protected final IpClientCallbacks mCallback;
- private final Dependencies mDependencies;
- private final CountDownLatch mShutdownLatch;
- private final ConnectivityManager mCm;
- private final INetworkManagementService mNwService;
- private final NetlinkTracker mNetlinkTracker;
- private final WakeupMessage mProvisioningTimeoutAlarm;
- private final WakeupMessage mDhcpActionTimeoutAlarm;
- private final SharedLog mLog;
- private final LocalLog mConnectivityPacketLog;
- private final MessageHandlingLogger mMsgStateLogger;
- private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
- private final InterfaceController mInterfaceCtrl;
-
- private InterfaceParams mInterfaceParams;
-
- /**
- * Non-final member variables accessed only from within our StateMachine.
- */
- private LinkProperties mLinkProperties;
- private android.net.shared.ProvisioningConfiguration mConfiguration;
- private IpReachabilityMonitor mIpReachabilityMonitor;
- private DhcpClient mDhcpClient;
- private DhcpResults mDhcpResults;
- private String mTcpBufferSizes;
- private ProxyInfo mHttpProxy;
- private ApfFilter mApfFilter;
- private boolean mMulticastFiltering;
- private long mStartTimeMillis;
-
- /**
- * Reading the snapshot is an asynchronous operation initiated by invoking
- * Callback.startReadPacketFilter() and completed when the WiFi Service responds with an
- * EVENT_READ_PACKET_FILTER_COMPLETE message. The mApfDataSnapshotComplete condition variable
- * signals when a new snapshot is ready.
- */
- private final ConditionVariable mApfDataSnapshotComplete = new ConditionVariable();
-
- public static class Dependencies {
- public INetworkManagementService getNMS() {
- return INetworkManagementService.Stub.asInterface(
- ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
- }
-
- public INetd getNetd() {
- return NetdService.getInstance();
- }
-
- /**
- * Get interface parameters for the specified interface.
- */
- public InterfaceParams getInterfaceParams(String ifname) {
- return InterfaceParams.getByName(ifname);
- }
- }
-
- public IpClient(Context context, String ifName, IpClientCallbacks callback) {
- this(context, ifName, callback, new Dependencies());
- }
-
- /**
- * An expanded constructor, useful for dependency injection.
- * TODO: migrate all test users to mock IpClient directly and remove this ctor.
- */
- public IpClient(Context context, String ifName, IpClientCallbacks callback,
- INetworkManagementService nwService) {
- this(context, ifName, callback, new Dependencies() {
- @Override
- public INetworkManagementService getNMS() {
- return nwService;
- }
- });
- }
-
- @VisibleForTesting
- IpClient(Context context, String ifName, IpClientCallbacks callback, Dependencies deps) {
- super(IpClient.class.getSimpleName() + "." + ifName);
- Preconditions.checkNotNull(ifName);
- Preconditions.checkNotNull(callback);
-
- mTag = getName();
-
- mContext = context;
- mInterfaceName = ifName;
- mClatInterfaceName = CLAT_PREFIX + ifName;
- mCallback = new LoggingCallbackWrapper(callback);
- mDependencies = deps;
- mShutdownLatch = new CountDownLatch(1);
- mCm = mContext.getSystemService(ConnectivityManager.class);
- mNwService = deps.getNMS();
-
- sSmLogs.putIfAbsent(mInterfaceName, new SharedLog(MAX_LOG_RECORDS, mTag));
- mLog = sSmLogs.get(mInterfaceName);
- sPktLogs.putIfAbsent(mInterfaceName, new LocalLog(MAX_PACKET_RECORDS));
- mConnectivityPacketLog = sPktLogs.get(mInterfaceName);
- mMsgStateLogger = new MessageHandlingLogger();
-
- // TODO: Consider creating, constructing, and passing in some kind of
- // InterfaceController.Dependencies class.
- mInterfaceCtrl = new InterfaceController(mInterfaceName, deps.getNetd(), mLog);
-
- mNetlinkTracker = new NetlinkTracker(
- mInterfaceName,
- new NetlinkTracker.Callback() {
- @Override
- public void update() {
- sendMessage(EVENT_NETLINK_LINKPROPERTIES_CHANGED);
- }
- }) {
- @Override
- public void interfaceAdded(String iface) {
- super.interfaceAdded(iface);
- if (mClatInterfaceName.equals(iface)) {
- mCallback.setNeighborDiscoveryOffload(false);
- } else if (!mInterfaceName.equals(iface)) {
- return;
- }
-
- final String msg = "interfaceAdded(" + iface + ")";
- logMsg(msg);
- }
-
- @Override
- public void interfaceRemoved(String iface) {
- super.interfaceRemoved(iface);
- // TODO: Also observe mInterfaceName going down and take some
- // kind of appropriate action.
- if (mClatInterfaceName.equals(iface)) {
- // TODO: consider sending a message to the IpClient main
- // StateMachine thread, in case "NDO enabled" state becomes
- // tied to more things that 464xlat operation.
- mCallback.setNeighborDiscoveryOffload(true);
- } else if (!mInterfaceName.equals(iface)) {
- return;
- }
-
- final String msg = "interfaceRemoved(" + iface + ")";
- logMsg(msg);
- }
-
- private void logMsg(String msg) {
- Log.d(mTag, msg);
- getHandler().post(() -> mLog.log("OBSERVED " + msg));
- }
- };
-
- mLinkProperties = new LinkProperties();
- mLinkProperties.setInterfaceName(mInterfaceName);
-
- mProvisioningTimeoutAlarm = new WakeupMessage(mContext, getHandler(),
- mTag + ".EVENT_PROVISIONING_TIMEOUT", EVENT_PROVISIONING_TIMEOUT);
- mDhcpActionTimeoutAlarm = new WakeupMessage(mContext, getHandler(),
- mTag + ".EVENT_DHCPACTION_TIMEOUT", EVENT_DHCPACTION_TIMEOUT);
-
- // Anything the StateMachine may access must have been instantiated
- // before this point.
- configureAndStartStateMachine();
-
- // Anything that may send messages to the StateMachine must only be
- // configured to do so after the StateMachine has started (above).
- startStateMachineUpdaters();
- }
-
- /**
- * Make a IIpClient connector to communicate with this IpClient.
- */
- public IIpClient makeConnector() {
- return new IpClientConnector();
- }
-
- class IpClientConnector extends IIpClient.Stub {
- @Override
- public void completedPreDhcpAction() {
- IpClient.this.completedPreDhcpAction();
- }
- @Override
- public void confirmConfiguration() {
- IpClient.this.confirmConfiguration();
- }
- @Override
- public void readPacketFilterComplete(byte[] data) {
- IpClient.this.readPacketFilterComplete(data);
- }
- @Override
- public void shutdown() {
- IpClient.this.shutdown();
- }
- @Override
- public void startProvisioning(ProvisioningConfigurationParcelable req) {
- IpClient.this.startProvisioning(
- android.net.shared.ProvisioningConfiguration.fromStableParcelable(req));
- }
- @Override
- public void stop() {
- IpClient.this.stop();
- }
- @Override
- public void setTcpBufferSizes(String tcpBufferSizes) {
- IpClient.this.setTcpBufferSizes(tcpBufferSizes);
- }
- @Override
- public void setHttpProxy(ProxyInfoParcelable proxyInfo) {
- IpClient.this.setHttpProxy(fromStableParcelable(proxyInfo));
- }
- @Override
- public void setMulticastFilter(boolean enabled) {
- IpClient.this.setMulticastFilter(enabled);
- }
- // TODO: remove and have IpClient logs dumped in NetworkStack dumpsys
- public void dumpIpClientLogs(FileDescriptor fd, PrintWriter pw, String[] args) {
- IpClient.this.dump(fd, pw, args);
- }
- }
-
- private void configureAndStartStateMachine() {
- // CHECKSTYLE:OFF IndentationCheck
- addState(mStoppedState);
- addState(mStartedState);
- addState(mRunningState, mStartedState);
- addState(mStoppingState);
- // CHECKSTYLE:ON IndentationCheck
-
- setInitialState(mStoppedState);
-
- super.start();
- }
-
- private void startStateMachineUpdaters() {
- try {
- mNwService.registerObserver(mNetlinkTracker);
- } catch (RemoteException e) {
- logError("Couldn't register NetlinkTracker: %s", e);
- }
- }
-
- private void stopStateMachineUpdaters() {
- try {
- mNwService.unregisterObserver(mNetlinkTracker);
- } catch (RemoteException e) {
- logError("Couldn't unregister NetlinkTracker: %s", e);
- }
- }
-
- @Override
- protected void onQuitting() {
- mCallback.onQuit();
- mShutdownLatch.countDown();
- }
-
- /**
- * Shut down this IpClient instance altogether.
- */
- public void shutdown() {
- stop();
- sendMessage(CMD_TERMINATE_AFTER_STOP);
- }
-
- // In order to avoid deadlock, this method MUST NOT be called on the
- // IpClient instance's thread. This prohibition includes code executed by
- // when methods on the passed-in IpClient.Callback instance are called.
- public void awaitShutdown() {
- try {
- mShutdownLatch.await();
- } catch (InterruptedException e) {
- mLog.e("Interrupted while awaiting shutdown: " + e);
- }
- }
-
- public static ProvisioningConfiguration.Builder buildProvisioningConfiguration() {
- return new ProvisioningConfiguration.Builder();
- }
-
- /**
- * Start provisioning with the provided parameters.
- */
- public void startProvisioning(android.net.shared.ProvisioningConfiguration req) {
- if (!req.isValid()) {
- doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING);
- return;
- }
-
- mInterfaceParams = mDependencies.getInterfaceParams(mInterfaceName);
- if (mInterfaceParams == null) {
- logError("Failed to find InterfaceParams for " + mInterfaceName);
- doImmediateProvisioningFailure(IpManagerEvent.ERROR_INTERFACE_NOT_FOUND);
- return;
- }
-
- mCallback.setNeighborDiscoveryOffload(true);
- sendMessage(CMD_START, new android.net.shared.ProvisioningConfiguration(req));
- }
-
- // TODO: Delete this.
- public void startProvisioning(StaticIpConfiguration staticIpConfig) {
- startProvisioning(buildProvisioningConfiguration()
- .withStaticConfiguration(staticIpConfig)
- .build());
- }
-
- public void startProvisioning() {
- startProvisioning(new android.net.shared.ProvisioningConfiguration());
- }
-
- /**
- * Stop this IpClient.
- *
- * <p>This does not shut down the StateMachine itself, which is handled by {@link #shutdown()}.
- */
- public void stop() {
- sendMessage(CMD_STOP);
- }
-
- /**
- * Confirm the provisioning configuration.
- */
- public void confirmConfiguration() {
- sendMessage(CMD_CONFIRM);
- }
-
- /**
- * For clients using {@link ProvisioningConfiguration.Builder#withPreDhcpAction()}, must be
- * called after {@link IIpClientCallbacks#onPreDhcpAction} to indicate that DHCP is clear to
- * proceed.
- */
- public void completedPreDhcpAction() {
- sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE);
- }
-
- /**
- * Indicate that packet filter read is complete.
- */
- public void readPacketFilterComplete(byte[] data) {
- sendMessage(EVENT_READ_PACKET_FILTER_COMPLETE, data);
- }
-
- /**
- * Set the TCP buffer sizes to use.
- *
- * This may be called, repeatedly, at any time before or after a call to
- * #startProvisioning(). The setting is cleared upon calling #stop().
- */
- public void setTcpBufferSizes(String tcpBufferSizes) {
- sendMessage(CMD_UPDATE_TCP_BUFFER_SIZES, tcpBufferSizes);
- }
-
- /**
- * Set the HTTP Proxy configuration to use.
- *
- * This may be called, repeatedly, at any time before or after a call to
- * #startProvisioning(). The setting is cleared upon calling #stop().
- */
- public void setHttpProxy(ProxyInfo proxyInfo) {
- sendMessage(CMD_UPDATE_HTTP_PROXY, proxyInfo);
- }
-
- /**
- * Enable or disable the multicast filter. Attempts to use APF to accomplish the filtering,
- * if not, Callback.setFallbackMulticastFilter() is called.
- */
- public void setMulticastFilter(boolean enabled) {
- sendMessage(CMD_SET_MULTICAST_FILTER, enabled);
- }
-
- /**
- * Dump logs of this IpClient.
- */
- public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
- if (args != null && args.length > 0 && DUMP_ARG_CONFIRM.equals(args[0])) {
- // Execute confirmConfiguration() and take no further action.
- confirmConfiguration();
- return;
- }
-
- // Thread-unsafe access to mApfFilter but just used for debugging.
- final ApfFilter apfFilter = mApfFilter;
- final android.net.shared.ProvisioningConfiguration provisioningConfig = mConfiguration;
- final ApfCapabilities apfCapabilities = (provisioningConfig != null)
- ? provisioningConfig.mApfCapabilities : null;
-
- IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
- pw.println(mTag + " APF dump:");
- pw.increaseIndent();
- if (apfFilter != null) {
- if (apfCapabilities.hasDataAccess()) {
- // Request a new snapshot, then wait for it.
- mApfDataSnapshotComplete.close();
- mCallback.startReadPacketFilter();
- if (!mApfDataSnapshotComplete.block(1000)) {
- pw.print("TIMEOUT: DUMPING STALE APF SNAPSHOT");
- }
- }
- apfFilter.dump(pw);
-
- } else {
- pw.print("No active ApfFilter; ");
- if (provisioningConfig == null) {
- pw.println("IpClient not yet started.");
- } else if (apfCapabilities == null || apfCapabilities.apfVersionSupported == 0) {
- pw.println("Hardware does not support APF.");
- } else {
- pw.println("ApfFilter not yet started, APF capabilities: " + apfCapabilities);
- }
- }
- pw.decreaseIndent();
- pw.println();
- pw.println(mTag + " current ProvisioningConfiguration:");
- pw.increaseIndent();
- pw.println(Objects.toString(provisioningConfig, "N/A"));
- pw.decreaseIndent();
-
- final IpReachabilityMonitor iprm = mIpReachabilityMonitor;
- if (iprm != null) {
- pw.println();
- pw.println(mTag + " current IpReachabilityMonitor state:");
- pw.increaseIndent();
- iprm.dump(pw);
- pw.decreaseIndent();
- }
-
- pw.println();
- pw.println(mTag + " StateMachine dump:");
- pw.increaseIndent();
- mLog.dump(fd, pw, args);
- pw.decreaseIndent();
-
- pw.println();
- pw.println(mTag + " connectivity packet log:");
- pw.println();
- pw.println("Debug with python and scapy via:");
- pw.println("shell$ python");
- pw.println(">>> from scapy import all as scapy");
- pw.println(">>> scapy.Ether(\"<paste_hex_string>\".decode(\"hex\")).show2()");
- pw.println();
-
- pw.increaseIndent();
- mConnectivityPacketLog.readOnlyLocalLog().dump(fd, pw, args);
- pw.decreaseIndent();
- }
-
-
- /**
- * Internals.
- */
-
- @Override
- protected String getWhatToString(int what) {
- return sWhatToString.get(what, "UNKNOWN: " + Integer.toString(what));
- }
-
- @Override
- protected String getLogRecString(Message msg) {
- final String logLine = String.format(
- "%s/%d %d %d %s [%s]",
- mInterfaceName, (mInterfaceParams == null) ? -1 : mInterfaceParams.index,
- msg.arg1, msg.arg2, Objects.toString(msg.obj), mMsgStateLogger);
-
- final String richerLogLine = getWhatToString(msg.what) + " " + logLine;
- mLog.log(richerLogLine);
- if (DBG) {
- Log.d(mTag, richerLogLine);
- }
-
- mMsgStateLogger.reset();
- return logLine;
- }
-
- @Override
- protected boolean recordLogRec(Message msg) {
- // Don't log EVENT_NETLINK_LINKPROPERTIES_CHANGED. They can be noisy,
- // and we already log any LinkProperties change that results in an
- // invocation of IpClient.Callback#onLinkPropertiesChange().
- final boolean shouldLog = (msg.what != EVENT_NETLINK_LINKPROPERTIES_CHANGED);
- if (!shouldLog) {
- mMsgStateLogger.reset();
- }
- return shouldLog;
- }
-
- private void logError(String fmt, Object... args) {
- final String msg = "ERROR " + String.format(fmt, args);
- Log.e(mTag, msg);
- mLog.log(msg);
- }
-
- // This needs to be called with care to ensure that our LinkProperties
- // are in sync with the actual LinkProperties of the interface. For example,
- // we should only call this if we know for sure that there are no IP addresses
- // assigned to the interface, etc.
- private void resetLinkProperties() {
- mNetlinkTracker.clearLinkProperties();
- mConfiguration = null;
- mDhcpResults = null;
- mTcpBufferSizes = "";
- mHttpProxy = null;
-
- mLinkProperties = new LinkProperties();
- mLinkProperties.setInterfaceName(mInterfaceName);
- }
-
- private void recordMetric(final int type) {
- // We may record error metrics prior to starting.
- // Map this to IMMEDIATE_FAILURE_DURATION.
- final long duration = (mStartTimeMillis > 0)
- ? (SystemClock.elapsedRealtime() - mStartTimeMillis)
- : IMMEDIATE_FAILURE_DURATION;
- mMetricsLog.log(mInterfaceName, new IpManagerEvent(type, duration));
- }
-
- // For now: use WifiStateMachine's historical notion of provisioned.
- @VisibleForTesting
- static boolean isProvisioned(LinkProperties lp, InitialConfiguration config) {
- // For historical reasons, we should connect even if all we have is
- // an IPv4 address and nothing else.
- if (lp.hasIPv4Address() || lp.isProvisioned()) {
- return true;
- }
- if (config == null) {
- return false;
- }
-
- // When an InitialConfiguration is specified, ignore any difference with previous
- // properties and instead check if properties observed match the desired properties.
- return config.isProvisionedBy(lp.getLinkAddresses(), lp.getRoutes());
- }
-
- // TODO: Investigate folding all this into the existing static function
- // LinkProperties.compareProvisioning() or some other single function that
- // takes two LinkProperties objects and returns a ProvisioningChange
- // object that is a correct and complete assessment of what changed, taking
- // account of the asymmetries described in the comments in this function.
- // Then switch to using it everywhere (IpReachabilityMonitor, etc.).
- private int compareProvisioning(LinkProperties oldLp, LinkProperties newLp) {
- int delta;
- InitialConfiguration config = mConfiguration != null ? mConfiguration.mInitialConfig : null;
- final boolean wasProvisioned = isProvisioned(oldLp, config);
- final boolean isProvisioned = isProvisioned(newLp, config);
-
- if (!wasProvisioned && isProvisioned) {
- delta = PROV_CHANGE_GAINED_PROVISIONING;
- } else if (wasProvisioned && isProvisioned) {
- delta = PROV_CHANGE_STILL_PROVISIONED;
- } else if (!wasProvisioned && !isProvisioned) {
- delta = PROV_CHANGE_STILL_NOT_PROVISIONED;
- } else {
- // (wasProvisioned && !isProvisioned)
- //
- // Note that this is true even if we lose a configuration element
- // (e.g., a default gateway) that would not be required to advance
- // into provisioned state. This is intended: if we have a default
- // router and we lose it, that's a sure sign of a problem, but if
- // we connect to a network with no IPv4 DNS servers, we consider
- // that to be a network without DNS servers and connect anyway.
- //
- // See the comment below.
- delta = PROV_CHANGE_LOST_PROVISIONING;
- }
-
- final boolean lostIPv6 = oldLp.isIPv6Provisioned() && !newLp.isIPv6Provisioned();
- final boolean lostIPv4Address = oldLp.hasIPv4Address() && !newLp.hasIPv4Address();
- final boolean lostIPv6Router = oldLp.hasIPv6DefaultRoute() && !newLp.hasIPv6DefaultRoute();
-
- // If bad wifi avoidance is disabled, then ignore IPv6 loss of
- // provisioning. Otherwise, when a hotspot that loses Internet
- // access sends out a 0-lifetime RA to its clients, the clients
- // will disconnect and then reconnect, avoiding the bad hotspot,
- // instead of getting stuck on the bad hotspot. http://b/31827713 .
- //
- // This is incorrect because if the hotspot then regains Internet
- // access with a different prefix, TCP connections on the
- // deprecated addresses will remain stuck.
- //
- // Note that we can still be disconnected by IpReachabilityMonitor
- // if the IPv6 default gateway (but not the IPv6 DNS servers; see
- // accompanying code in IpReachabilityMonitor) is unreachable.
- final boolean ignoreIPv6ProvisioningLoss =
- mConfiguration != null && mConfiguration.mUsingMultinetworkPolicyTracker
- && mCm.getAvoidBadWifi();
-
- // Additionally:
- //
- // Partial configurations (e.g., only an IPv4 address with no DNS
- // servers and no default route) are accepted as long as DHCPv4
- // succeeds. On such a network, isProvisioned() will always return
- // false, because the configuration is not complete, but we want to
- // connect anyway. It might be a disconnected network such as a
- // Chromecast or a wireless printer, for example.
- //
- // Because on such a network isProvisioned() will always return false,
- // delta will never be LOST_PROVISIONING. So check for loss of
- // provisioning here too.
- if (lostIPv4Address || (lostIPv6 && !ignoreIPv6ProvisioningLoss)) {
- delta = PROV_CHANGE_LOST_PROVISIONING;
- }
-
- // Additionally:
- //
- // If the previous link properties had a global IPv6 address and an
- // IPv6 default route then also consider the loss of that default route
- // to be a loss of provisioning. See b/27962810.
- if (oldLp.hasGlobalIPv6Address() && (lostIPv6Router && !ignoreIPv6ProvisioningLoss)) {
- delta = PROV_CHANGE_LOST_PROVISIONING;
- }
-
- return delta;
- }
-
- private void dispatchCallback(int delta, LinkProperties newLp) {
- switch (delta) {
- case PROV_CHANGE_GAINED_PROVISIONING:
- if (DBG) {
- Log.d(mTag, "onProvisioningSuccess()");
- }
- recordMetric(IpManagerEvent.PROVISIONING_OK);
- mCallback.onProvisioningSuccess(newLp);
- break;
-
- case PROV_CHANGE_LOST_PROVISIONING:
- if (DBG) {
- Log.d(mTag, "onProvisioningFailure()");
- }
- recordMetric(IpManagerEvent.PROVISIONING_FAIL);
- mCallback.onProvisioningFailure(newLp);
- break;
-
- default:
- if (DBG) {
- Log.d(mTag, "onLinkPropertiesChange()");
- }
- mCallback.onLinkPropertiesChange(newLp);
- break;
- }
- }
-
- // Updates all IpClient-related state concerned with LinkProperties.
- // Returns a ProvisioningChange for possibly notifying other interested
- // parties that are not fronted by IpClient.
- private int setLinkProperties(LinkProperties newLp) {
- if (mApfFilter != null) {
- mApfFilter.setLinkProperties(newLp);
- }
- if (mIpReachabilityMonitor != null) {
- mIpReachabilityMonitor.updateLinkProperties(newLp);
- }
-
- int delta = compareProvisioning(mLinkProperties, newLp);
- mLinkProperties = new LinkProperties(newLp);
-
- if (delta == PROV_CHANGE_GAINED_PROVISIONING) {
- // TODO: Add a proper ProvisionedState and cancel the alarm in
- // its enter() method.
- mProvisioningTimeoutAlarm.cancel();
- }
-
- return delta;
- }
-
- private LinkProperties assembleLinkProperties() {
- // [1] Create a new LinkProperties object to populate.
- LinkProperties newLp = new LinkProperties();
- newLp.setInterfaceName(mInterfaceName);
-
- // [2] Pull in data from netlink:
- // - IPv4 addresses
- // - IPv6 addresses
- // - IPv6 routes
- // - IPv6 DNS servers
- //
- // N.B.: this is fundamentally race-prone and should be fixed by
- // changing NetlinkTracker from a hybrid edge/level model to an
- // edge-only model, or by giving IpClient its own netlink socket(s)
- // so as to track all required information directly.
- LinkProperties netlinkLinkProperties = mNetlinkTracker.getLinkProperties();
- newLp.setLinkAddresses(netlinkLinkProperties.getLinkAddresses());
- for (RouteInfo route : netlinkLinkProperties.getRoutes()) {
- newLp.addRoute(route);
- }
- addAllReachableDnsServers(newLp, netlinkLinkProperties.getDnsServers());
-
- // [3] Add in data from DHCPv4, if available.
- //
- // mDhcpResults is never shared with any other owner so we don't have
- // to worry about concurrent modification.
- if (mDhcpResults != null) {
- for (RouteInfo route : mDhcpResults.getRoutes(mInterfaceName)) {
- newLp.addRoute(route);
- }
- addAllReachableDnsServers(newLp, mDhcpResults.dnsServers);
- newLp.setDomains(mDhcpResults.domains);
-
- if (mDhcpResults.mtu != 0) {
- newLp.setMtu(mDhcpResults.mtu);
- }
- }
-
- // [4] Add in TCP buffer sizes and HTTP Proxy config, if available.
- if (!TextUtils.isEmpty(mTcpBufferSizes)) {
- newLp.setTcpBufferSizes(mTcpBufferSizes);
- }
- if (mHttpProxy != null) {
- newLp.setHttpProxy(mHttpProxy);
- }
-
- // [5] Add data from InitialConfiguration
- if (mConfiguration != null && mConfiguration.mInitialConfig != null) {
- InitialConfiguration config = mConfiguration.mInitialConfig;
- // Add InitialConfiguration routes and dns server addresses once all addresses
- // specified in the InitialConfiguration have been observed with Netlink.
- if (config.isProvisionedBy(newLp.getLinkAddresses(), null)) {
- for (IpPrefix prefix : config.directlyConnectedRoutes) {
- newLp.addRoute(new RouteInfo(prefix, null, mInterfaceName));
- }
- }
- addAllReachableDnsServers(newLp, config.dnsServers);
- }
- final LinkProperties oldLp = mLinkProperties;
- if (DBG) {
- Log.d(mTag, String.format("Netlink-seen LPs: %s, new LPs: %s; old LPs: %s",
- netlinkLinkProperties, newLp, oldLp));
- }
-
- // TODO: also learn via netlink routes specified by an InitialConfiguration and specified
- // from a static IP v4 config instead of manually patching them in in steps [3] and [5].
- return newLp;
- }
-
- private static void addAllReachableDnsServers(
- LinkProperties lp, Iterable<InetAddress> dnses) {
- // TODO: Investigate deleting this reachability check. We should be
- // able to pass everything down to netd and let netd do evaluation
- // and RFC6724-style sorting.
- for (InetAddress dns : dnses) {
- if (!dns.isAnyLocalAddress() && lp.isReachable(dns)) {
- lp.addDnsServer(dns);
- }
- }
- }
-
- // Returns false if we have lost provisioning, true otherwise.
- private boolean handleLinkPropertiesUpdate(boolean sendCallbacks) {
- final LinkProperties newLp = assembleLinkProperties();
- if (Objects.equals(newLp, mLinkProperties)) {
- return true;
- }
- final int delta = setLinkProperties(newLp);
- if (sendCallbacks) {
- dispatchCallback(delta, newLp);
- }
- return (delta != PROV_CHANGE_LOST_PROVISIONING);
- }
-
- private void handleIPv4Success(DhcpResults dhcpResults) {
- mDhcpResults = new DhcpResults(dhcpResults);
- final LinkProperties newLp = assembleLinkProperties();
- final int delta = setLinkProperties(newLp);
-
- if (DBG) {
- Log.d(mTag, "onNewDhcpResults(" + Objects.toString(dhcpResults) + ")");
- }
- mCallback.onNewDhcpResults(dhcpResults);
- dispatchCallback(delta, newLp);
- }
-
- private void handleIPv4Failure() {
- // TODO: Investigate deleting this clearIPv4Address() call.
- //
- // DhcpClient will send us CMD_CLEAR_LINKADDRESS in all circumstances
- // that could trigger a call to this function. If we missed handling
- // that message in StartedState for some reason we would still clear
- // any addresses upon entry to StoppedState.
- mInterfaceCtrl.clearIPv4Address();
- mDhcpResults = null;
- if (DBG) {
- Log.d(mTag, "onNewDhcpResults(null)");
- }
- mCallback.onNewDhcpResults(null);
-
- handleProvisioningFailure();
- }
-
- private void handleProvisioningFailure() {
- final LinkProperties newLp = assembleLinkProperties();
- int delta = setLinkProperties(newLp);
- // If we've gotten here and we're still not provisioned treat that as
- // a total loss of provisioning.
- //
- // Either (a) static IP configuration failed or (b) DHCPv4 failed AND
- // there was no usable IPv6 obtained before a non-zero provisioning
- // timeout expired.
- //
- // Regardless: GAME OVER.
- if (delta == PROV_CHANGE_STILL_NOT_PROVISIONED) {
- delta = PROV_CHANGE_LOST_PROVISIONING;
- }
-
- dispatchCallback(delta, newLp);
- if (delta == PROV_CHANGE_LOST_PROVISIONING) {
- transitionTo(mStoppingState);
- }
- }
-
- private void doImmediateProvisioningFailure(int failureType) {
- logError("onProvisioningFailure(): %s", failureType);
- recordMetric(failureType);
- mCallback.onProvisioningFailure(new LinkProperties(mLinkProperties));
- }
-
- private boolean startIPv4() {
- // If we have a StaticIpConfiguration attempt to apply it and
- // handle the result accordingly.
- if (mConfiguration.mStaticIpConfig != null) {
- if (mInterfaceCtrl.setIPv4Address(mConfiguration.mStaticIpConfig.ipAddress)) {
- handleIPv4Success(new DhcpResults(mConfiguration.mStaticIpConfig));
- } else {
- return false;
- }
- } else {
- // Start DHCPv4.
- mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpClient.this, mInterfaceParams);
- mDhcpClient.registerForPreDhcpNotification();
- mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP);
- }
-
- return true;
- }
-
- private boolean startIPv6() {
- return mInterfaceCtrl.setIPv6PrivacyExtensions(true)
- && mInterfaceCtrl.setIPv6AddrGenModeIfSupported(mConfiguration.mIPv6AddrGenMode)
- && mInterfaceCtrl.enableIPv6();
- }
-
- private boolean applyInitialConfig(InitialConfiguration config) {
- // TODO: also support specifying a static IPv4 configuration in InitialConfiguration.
- for (LinkAddress addr : findAll(config.ipAddresses, LinkAddress::isIPv6)) {
- if (!mInterfaceCtrl.addAddress(addr)) return false;
- }
-
- return true;
- }
-
- private boolean startIpReachabilityMonitor() {
- try {
- // TODO: Fetch these parameters from settings, and install a
- // settings observer to watch for update and re-program these
- // parameters (Q: is this level of dynamic updatability really
- // necessary or does reading from settings at startup suffice?).
- final int numSolicits = 5;
- final int interSolicitIntervalMs = 750;
- setNeighborParameters(mDependencies.getNetd(), mInterfaceName,
- numSolicits, interSolicitIntervalMs);
- } catch (Exception e) {
- mLog.e("Failed to adjust neighbor parameters", e);
- // Carry on using the system defaults (currently: 3, 1000);
- }
-
- try {
- mIpReachabilityMonitor = new IpReachabilityMonitor(
- mContext,
- mInterfaceParams,
- getHandler(),
- mLog,
- new IpReachabilityMonitor.Callback() {
- @Override
- public void notifyLost(InetAddress ip, String logMsg) {
- mCallback.onReachabilityLost(logMsg);
- }
- },
- mConfiguration.mUsingMultinetworkPolicyTracker);
- } catch (IllegalArgumentException iae) {
- // Failed to start IpReachabilityMonitor. Log it and call
- // onProvisioningFailure() immediately.
- //
- // See http://b/31038971.
- logError("IpReachabilityMonitor failure: %s", iae);
- mIpReachabilityMonitor = null;
- }
-
- return (mIpReachabilityMonitor != null);
- }
-
- private void stopAllIP() {
- // We don't need to worry about routes, just addresses, because:
- // - disableIpv6() will clear autoconf IPv6 routes as well, and
- // - we don't get IPv4 routes from netlink
- // so we neither react to nor need to wait for changes in either.
-
- mInterfaceCtrl.disableIPv6();
- mInterfaceCtrl.clearAllAddresses();
- }
-
- class StoppedState extends State {
- @Override
- public void enter() {
- stopAllIP();
-
- resetLinkProperties();
- if (mStartTimeMillis > 0) {
- // Completed a life-cycle; send a final empty LinkProperties
- // (cleared in resetLinkProperties() above) and record an event.
- mCallback.onLinkPropertiesChange(new LinkProperties(mLinkProperties));
- recordMetric(IpManagerEvent.COMPLETE_LIFECYCLE);
- mStartTimeMillis = 0;
- }
- }
-
- @Override
- public boolean processMessage(Message msg) {
- switch (msg.what) {
- case CMD_TERMINATE_AFTER_STOP:
- stopStateMachineUpdaters();
- quit();
- break;
-
- case CMD_STOP:
- break;
-
- case CMD_START:
- mConfiguration = (android.net.shared.ProvisioningConfiguration) msg.obj;
- transitionTo(mStartedState);
- break;
-
- case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
- handleLinkPropertiesUpdate(NO_CALLBACKS);
- break;
-
- case CMD_UPDATE_TCP_BUFFER_SIZES:
- mTcpBufferSizes = (String) msg.obj;
- handleLinkPropertiesUpdate(NO_CALLBACKS);
- break;
-
- case CMD_UPDATE_HTTP_PROXY:
- mHttpProxy = (ProxyInfo) msg.obj;
- handleLinkPropertiesUpdate(NO_CALLBACKS);
- break;
-
- case CMD_SET_MULTICAST_FILTER:
- mMulticastFiltering = (boolean) msg.obj;
- break;
-
- case DhcpClient.CMD_ON_QUIT:
- // Everything is already stopped.
- logError("Unexpected CMD_ON_QUIT (already stopped).");
- break;
-
- default:
- return NOT_HANDLED;
- }
-
- mMsgStateLogger.handled(this, getCurrentState());
- return HANDLED;
- }
- }
-
- class StoppingState extends State {
- @Override
- public void enter() {
- if (mDhcpClient == null) {
- // There's no DHCPv4 for which to wait; proceed to stopped.
- deferMessage(obtainMessage(CMD_JUMP_STOPPING_TO_STOPPED));
- }
- }
-
- @Override
- public boolean processMessage(Message msg) {
- switch (msg.what) {
- case CMD_JUMP_STOPPING_TO_STOPPED:
- transitionTo(mStoppedState);
- break;
-
- case CMD_STOP:
- break;
-
- case DhcpClient.CMD_CLEAR_LINKADDRESS:
- mInterfaceCtrl.clearIPv4Address();
- break;
-
- case DhcpClient.CMD_ON_QUIT:
- mDhcpClient = null;
- transitionTo(mStoppedState);
- break;
-
- default:
- deferMessage(msg);
- }
-
- mMsgStateLogger.handled(this, getCurrentState());
- return HANDLED;
- }
- }
-
- class StartedState extends State {
- @Override
- public void enter() {
- mStartTimeMillis = SystemClock.elapsedRealtime();
-
- if (mConfiguration.mProvisioningTimeoutMs > 0) {
- final long alarmTime = SystemClock.elapsedRealtime()
- + mConfiguration.mProvisioningTimeoutMs;
- mProvisioningTimeoutAlarm.schedule(alarmTime);
- }
-
- if (readyToProceed()) {
- deferMessage(obtainMessage(CMD_JUMP_STARTED_TO_RUNNING));
- } else {
- // Clear all IPv4 and IPv6 before proceeding to RunningState.
- // Clean up any leftover state from an abnormal exit from
- // tethering or during an IpClient restart.
- stopAllIP();
- }
- }
-
- @Override
- public void exit() {
- mProvisioningTimeoutAlarm.cancel();
- }
-
- @Override
- public boolean processMessage(Message msg) {
- switch (msg.what) {
- case CMD_JUMP_STARTED_TO_RUNNING:
- transitionTo(mRunningState);
- break;
-
- case CMD_STOP:
- transitionTo(mStoppingState);
- break;
-
- case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
- handleLinkPropertiesUpdate(NO_CALLBACKS);
- if (readyToProceed()) {
- transitionTo(mRunningState);
- }
- break;
-
- case EVENT_PROVISIONING_TIMEOUT:
- handleProvisioningFailure();
- break;
-
- default:
- // It's safe to process messages out of order because the
- // only message that can both
- // a) be received at this time and
- // b) affect provisioning state
- // is EVENT_NETLINK_LINKPROPERTIES_CHANGED (handled above).
- deferMessage(msg);
- }
-
- mMsgStateLogger.handled(this, getCurrentState());
- return HANDLED;
- }
-
- private boolean readyToProceed() {
- return (!mLinkProperties.hasIPv4Address() && !mLinkProperties.hasGlobalIPv6Address());
- }
- }
-
- class RunningState extends State {
- private ConnectivityPacketTracker mPacketTracker;
- private boolean mDhcpActionInFlight;
-
- @Override
- public void enter() {
- ApfFilter.ApfConfiguration apfConfig = new ApfFilter.ApfConfiguration();
- apfConfig.apfCapabilities = mConfiguration.mApfCapabilities;
- apfConfig.multicastFilter = mMulticastFiltering;
- // Get the Configuration for ApfFilter from Context
- apfConfig.ieee802_3Filter =
- mContext.getResources().getBoolean(R.bool.config_apfDrop802_3Frames);
- apfConfig.ethTypeBlackList =
- mContext.getResources().getIntArray(R.array.config_apfEthTypeBlackList);
- mApfFilter = ApfFilter.maybeCreate(mContext, apfConfig, mInterfaceParams, mCallback);
- // TODO: investigate the effects of any multicast filtering racing/interfering with the
- // rest of this IP configuration startup.
- if (mApfFilter == null) {
- mCallback.setFallbackMulticastFilter(mMulticastFiltering);
- }
-
- mPacketTracker = createPacketTracker();
- if (mPacketTracker != null) mPacketTracker.start(mConfiguration.mDisplayName);
-
- if (mConfiguration.mEnableIPv6 && !startIPv6()) {
- doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV6);
- enqueueJumpToStoppingState();
- return;
- }
-
- if (mConfiguration.mEnableIPv4 && !startIPv4()) {
- doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV4);
- enqueueJumpToStoppingState();
- return;
- }
-
- final InitialConfiguration config = mConfiguration.mInitialConfig;
- if ((config != null) && !applyInitialConfig(config)) {
- // TODO introduce a new IpManagerEvent constant to distinguish this error case.
- doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING);
- enqueueJumpToStoppingState();
- return;
- }
-
- if (mConfiguration.mUsingIpReachabilityMonitor && !startIpReachabilityMonitor()) {
- doImmediateProvisioningFailure(
- IpManagerEvent.ERROR_STARTING_IPREACHABILITYMONITOR);
- enqueueJumpToStoppingState();
- return;
- }
- }
-
- @Override
- public void exit() {
- stopDhcpAction();
-
- if (mIpReachabilityMonitor != null) {
- mIpReachabilityMonitor.stop();
- mIpReachabilityMonitor = null;
- }
-
- if (mDhcpClient != null) {
- mDhcpClient.sendMessage(DhcpClient.CMD_STOP_DHCP);
- mDhcpClient.doQuit();
- }
-
- if (mPacketTracker != null) {
- mPacketTracker.stop();
- mPacketTracker = null;
- }
-
- if (mApfFilter != null) {
- mApfFilter.shutdown();
- mApfFilter = null;
- }
-
- resetLinkProperties();
- }
-
- private void enqueueJumpToStoppingState() {
- deferMessage(obtainMessage(CMD_JUMP_RUNNING_TO_STOPPING));
- }
-
- private ConnectivityPacketTracker createPacketTracker() {
- try {
- return new ConnectivityPacketTracker(
- getHandler(), mInterfaceParams, mConnectivityPacketLog);
- } catch (IllegalArgumentException e) {
- return null;
- }
- }
-
- private void ensureDhcpAction() {
- if (!mDhcpActionInFlight) {
- mCallback.onPreDhcpAction();
- mDhcpActionInFlight = true;
- final long alarmTime = SystemClock.elapsedRealtime()
- + mConfiguration.mRequestedPreDhcpActionMs;
- mDhcpActionTimeoutAlarm.schedule(alarmTime);
- }
- }
-
- private void stopDhcpAction() {
- mDhcpActionTimeoutAlarm.cancel();
- if (mDhcpActionInFlight) {
- mCallback.onPostDhcpAction();
- mDhcpActionInFlight = false;
- }
- }
-
- @Override
- public boolean processMessage(Message msg) {
- switch (msg.what) {
- case CMD_JUMP_RUNNING_TO_STOPPING:
- case CMD_STOP:
- transitionTo(mStoppingState);
- break;
-
- case CMD_START:
- logError("ALERT: START received in StartedState. Please fix caller.");
- break;
-
- case CMD_CONFIRM:
- // TODO: Possibly introduce a second type of confirmation
- // that both probes (a) on-link neighbors and (b) does
- // a DHCPv4 RENEW. We used to do this on Wi-Fi framework
- // roams.
- if (mIpReachabilityMonitor != null) {
- mIpReachabilityMonitor.probeAll();
- }
- break;
-
- case EVENT_PRE_DHCP_ACTION_COMPLETE:
- // It's possible to reach here if, for example, someone
- // calls completedPreDhcpAction() after provisioning with
- // a static IP configuration.
- if (mDhcpClient != null) {
- mDhcpClient.sendMessage(DhcpClient.CMD_PRE_DHCP_ACTION_COMPLETE);
- }
- break;
-
- case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
- if (!handleLinkPropertiesUpdate(SEND_CALLBACKS)) {
- transitionTo(mStoppingState);
- }
- break;
-
- case CMD_UPDATE_TCP_BUFFER_SIZES:
- mTcpBufferSizes = (String) msg.obj;
- // This cannot possibly change provisioning state.
- handleLinkPropertiesUpdate(SEND_CALLBACKS);
- break;
-
- case CMD_UPDATE_HTTP_PROXY:
- mHttpProxy = (ProxyInfo) msg.obj;
- // This cannot possibly change provisioning state.
- handleLinkPropertiesUpdate(SEND_CALLBACKS);
- break;
-
- case CMD_SET_MULTICAST_FILTER: {
- mMulticastFiltering = (boolean) msg.obj;
- if (mApfFilter != null) {
- mApfFilter.setMulticastFilter(mMulticastFiltering);
- } else {
- mCallback.setFallbackMulticastFilter(mMulticastFiltering);
- }
- break;
- }
-
- case EVENT_READ_PACKET_FILTER_COMPLETE: {
- if (mApfFilter != null) {
- mApfFilter.setDataSnapshot((byte[]) msg.obj);
- }
- mApfDataSnapshotComplete.open();
- break;
- }
-
- case EVENT_DHCPACTION_TIMEOUT:
- stopDhcpAction();
- break;
-
- case DhcpClient.CMD_PRE_DHCP_ACTION:
- if (mConfiguration.mRequestedPreDhcpActionMs > 0) {
- ensureDhcpAction();
- } else {
- sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE);
- }
- break;
-
- case DhcpClient.CMD_CLEAR_LINKADDRESS:
- mInterfaceCtrl.clearIPv4Address();
- break;
-
- case DhcpClient.CMD_CONFIGURE_LINKADDRESS: {
- final LinkAddress ipAddress = (LinkAddress) msg.obj;
- if (mInterfaceCtrl.setIPv4Address(ipAddress)) {
- mDhcpClient.sendMessage(DhcpClient.EVENT_LINKADDRESS_CONFIGURED);
- } else {
- logError("Failed to set IPv4 address.");
- dispatchCallback(PROV_CHANGE_LOST_PROVISIONING,
- new LinkProperties(mLinkProperties));
- transitionTo(mStoppingState);
- }
- break;
- }
-
- // This message is only received when:
- //
- // a) initial address acquisition succeeds,
- // b) renew succeeds or is NAK'd,
- // c) rebind succeeds or is NAK'd, or
- // c) the lease expires,
- //
- // but never when initial address acquisition fails. The latter
- // condition is now governed by the provisioning timeout.
- case DhcpClient.CMD_POST_DHCP_ACTION:
- stopDhcpAction();
-
- switch (msg.arg1) {
- case DhcpClient.DHCP_SUCCESS:
- handleIPv4Success((DhcpResults) msg.obj);
- break;
- case DhcpClient.DHCP_FAILURE:
- handleIPv4Failure();
- break;
- default:
- logError("Unknown CMD_POST_DHCP_ACTION status: %s", msg.arg1);
- }
- break;
-
- case DhcpClient.CMD_ON_QUIT:
- // DHCPv4 quit early for some reason.
- logError("Unexpected CMD_ON_QUIT.");
- mDhcpClient = null;
- break;
-
- default:
- return NOT_HANDLED;
- }
-
- mMsgStateLogger.handled(this, getCurrentState());
- return HANDLED;
- }
- }
-
- private static class MessageHandlingLogger {
- public String processedInState;
- public String receivedInState;
-
- public void reset() {
- processedInState = null;
- receivedInState = null;
- }
-
- public void handled(State processedIn, IState receivedIn) {
- processedInState = processedIn.getClass().getSimpleName();
- receivedInState = receivedIn.getName();
- }
-
- public String toString() {
- return String.format("rcvd_in=%s, proc_in=%s",
- receivedInState, processedInState);
- }
- }
-
- private static void setNeighborParameters(
- INetd netd, String ifName, int numSolicits, int interSolicitIntervalMs)
- throws RemoteException, IllegalArgumentException {
- Preconditions.checkNotNull(netd);
- Preconditions.checkArgument(!TextUtils.isEmpty(ifName));
- Preconditions.checkArgument(numSolicits > 0);
- Preconditions.checkArgument(interSolicitIntervalMs > 0);
-
- for (int family : new Integer[]{INetd.IPV4, INetd.IPV6}) {
- netd.setProcSysNet(family, INetd.NEIGH, ifName, "retrans_time_ms",
- Integer.toString(interSolicitIntervalMs));
- netd.setProcSysNet(family, INetd.NEIGH, ifName, "ucast_solicit",
- Integer.toString(numSolicits));
- }
- }
-
- // TODO: extract out into CollectionUtils.
- static <T> boolean any(Iterable<T> coll, Predicate<T> fn) {
- for (T t : coll) {
- if (fn.test(t)) {
- return true;
- }
- }
- return false;
- }
-
- static <T> boolean all(Iterable<T> coll, Predicate<T> fn) {
- return !any(coll, not(fn));
- }
-
- static <T> Predicate<T> not(Predicate<T> fn) {
- return (t) -> !fn.test(t);
- }
-
- static <T> String join(String delimiter, Collection<T> coll) {
- return coll.stream().map(Object::toString).collect(Collectors.joining(delimiter));
- }
-
- static <T> T find(Iterable<T> coll, Predicate<T> fn) {
- for (T t: coll) {
- if (fn.test(t)) {
- return t;
- }
- }
- return null;
- }
-
- static <T> List<T> findAll(Collection<T> coll, Predicate<T> fn) {
- return coll.stream().filter(fn).collect(Collectors.toList());
- }
-}
diff --git a/services/net/java/android/net/ip/IpClientUtil.java b/services/net/java/android/net/ip/IpClientUtil.java
index 0aec101..ddf7de3 100644
--- a/services/net/java/android/net/ip/IpClientUtil.java
+++ b/services/net/java/android/net/ip/IpClientUtil.java
@@ -16,8 +16,15 @@
package android.net.ip;
+import static android.net.shared.IpConfigurationParcelableUtil.fromStableParcelable;
+import static android.net.shared.LinkPropertiesParcelableUtil.fromStableParcelable;
+
import android.content.Context;
+import android.net.DhcpResultsParcelable;
import android.net.LinkProperties;
+import android.net.LinkPropertiesParcelable;
+import android.net.NetworkStack;
+import android.net.ip.IIpClientCallbacks;
import android.os.ConditionVariable;
import java.io.FileDescriptor;
@@ -31,8 +38,8 @@
* @hide
*/
public class IpClientUtil {
- // TODO: remove once IpClient dumps are moved to NetworkStack and callers don't need this arg
- public static final String DUMP_ARG = IpClient.DUMP_ARG;
+ // TODO: remove with its callers
+ public static final String DUMP_ARG = "ipclient";
/**
* Subclass of {@link IpClientCallbacks} allowing clients to block until provisioning is
@@ -69,24 +76,109 @@
*
* <p>This is a convenience method to allow clients to use {@link IpClientCallbacks} instead of
* {@link IIpClientCallbacks}.
+ * @see {@link NetworkStack#makeIpClient(String, IIpClientCallbacks)}
*/
public static void makeIpClient(Context context, String ifName, IpClientCallbacks callback) {
- // TODO: request IpClient asynchronously from NetworkStack.
- final IpClient ipClient = new IpClient(context, ifName, callback);
- callback.onIpClientCreated(ipClient.makeConnector());
+ context.getSystemService(NetworkStack.class)
+ .makeIpClient(ifName, new IpClientCallbacksProxy(callback));
+ }
+
+ private static class IpClientCallbacksProxy extends IIpClientCallbacks.Stub {
+ protected final IpClientCallbacks mCb;
+ IpClientCallbacksProxy(IpClientCallbacks cb) {
+ mCb = cb;
+ }
+
+ @Override
+ public void onIpClientCreated(IIpClient ipClient) {
+ mCb.onIpClientCreated(ipClient);
+ }
+
+ @Override
+ public void onPreDhcpAction() {
+ mCb.onPreDhcpAction();
+ }
+
+ @Override
+ public void onPostDhcpAction() {
+ mCb.onPostDhcpAction();
+ }
+
+ // This is purely advisory and not an indication of provisioning
+ // success or failure. This is only here for callers that want to
+ // expose DHCPv4 results to other APIs (e.g., WifiInfo#setInetAddress).
+ // DHCPv4 or static IPv4 configuration failure or success can be
+ // determined by whether or not the passed-in DhcpResults object is
+ // null or not.
+ @Override
+ public void onNewDhcpResults(DhcpResultsParcelable dhcpResults) {
+ mCb.onNewDhcpResults(fromStableParcelable(dhcpResults));
+ }
+
+ @Override
+ public void onProvisioningSuccess(LinkPropertiesParcelable newLp) {
+ mCb.onProvisioningSuccess(fromStableParcelable(newLp));
+ }
+ @Override
+ public void onProvisioningFailure(LinkPropertiesParcelable newLp) {
+ mCb.onProvisioningFailure(fromStableParcelable(newLp));
+ }
+
+ // Invoked on LinkProperties changes.
+ @Override
+ public void onLinkPropertiesChange(LinkPropertiesParcelable newLp) {
+ mCb.onLinkPropertiesChange(fromStableParcelable(newLp));
+ }
+
+ // Called when the internal IpReachabilityMonitor (if enabled) has
+ // detected the loss of a critical number of required neighbors.
+ @Override
+ public void onReachabilityLost(String logMsg) {
+ mCb.onReachabilityLost(logMsg);
+ }
+
+ // Called when the IpClient state machine terminates.
+ @Override
+ public void onQuit() {
+ mCb.onQuit();
+ }
+
+ // Install an APF program to filter incoming packets.
+ @Override
+ public void installPacketFilter(byte[] filter) {
+ mCb.installPacketFilter(filter);
+ }
+
+ // Asynchronously read back the APF program & data buffer from the wifi driver.
+ // Due to Wifi HAL limitations, the current implementation only supports dumping the entire
+ // buffer. In response to this request, the driver returns the data buffer asynchronously
+ // by sending an IpClient#EVENT_READ_PACKET_FILTER_COMPLETE message.
+ @Override
+ public void startReadPacketFilter() {
+ mCb.startReadPacketFilter();
+ }
+
+ // If multicast filtering cannot be accomplished with APF, this function will be called to
+ // actuate multicast filtering using another means.
+ @Override
+ public void setFallbackMulticastFilter(boolean enabled) {
+ mCb.setFallbackMulticastFilter(enabled);
+ }
+
+ // Enabled/disable Neighbor Discover offload functionality. This is
+ // called, for example, whenever 464xlat is being started or stopped.
+ @Override
+ public void setNeighborDiscoveryOffload(boolean enable) {
+ mCb.setNeighborDiscoveryOffload(enable);
+ }
}
/**
* Dump logs for the specified IpClient.
- * TODO: remove logging from this method once IpClient logs are dumped in NetworkStack dumpsys,
- * then remove callers and delete.
+ * TODO: remove callers and delete
*/
public static void dumpIpClient(
IIpClient connector, FileDescriptor fd, PrintWriter pw, String[] args) {
- if (!(connector instanceof IpClient.IpClientConnector)) {
- pw.println("Invalid connector");
- return;
- }
- ((IpClient.IpClientConnector) connector).dumpIpClientLogs(fd, pw, args);
+ pw.println("IpClient logs have moved to dumpsys network_stack");
}
}
diff --git a/services/net/java/android/net/ip/IpNeighborMonitor.java b/services/net/java/android/net/ip/IpNeighborMonitor.java
deleted file mode 100644
index eb993a4..0000000
--- a/services/net/java/android/net/ip/IpNeighborMonitor.java
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.ip;
-
-import static android.net.netlink.NetlinkConstants.RTM_DELNEIGH;
-import static android.net.netlink.NetlinkConstants.hexify;
-import static android.net.netlink.NetlinkConstants.stringForNlMsgType;
-import static android.net.util.SocketUtils.makeNetlinkSocketAddress;
-
-import android.net.MacAddress;
-import android.net.netlink.NetlinkErrorMessage;
-import android.net.netlink.NetlinkMessage;
-import android.net.netlink.NetlinkSocket;
-import android.net.netlink.RtNetlinkNeighborMessage;
-import android.net.netlink.StructNdMsg;
-import android.net.util.PacketReader;
-import android.net.util.SharedLog;
-import android.os.Handler;
-import android.os.SystemClock;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.system.OsConstants;
-import android.util.Log;
-
-import com.android.internal.util.BitUtils;
-
-import libcore.io.IoUtils;
-
-import java.io.FileDescriptor;
-import java.net.InetAddress;
-import java.net.SocketAddress;
-import java.net.SocketException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.StringJoiner;
-
-
-/**
- * IpNeighborMonitor.
- *
- * Monitors the kernel rtnetlink neighbor notifications and presents to callers
- * NeighborEvents describing each event. Callers can provide a consumer instance
- * to both filter (e.g. by interface index and IP address) and handle the
- * generated NeighborEvents.
- *
- * @hide
- */
-public class IpNeighborMonitor extends PacketReader {
- private static final String TAG = IpNeighborMonitor.class.getSimpleName();
- private static final boolean DBG = false;
- private static final boolean VDBG = false;
-
- /**
- * Make the kernel perform neighbor reachability detection (IPv4 ARP or IPv6 ND)
- * for the given IP address on the specified interface index.
- *
- * @return 0 if the request was successfully passed to the kernel; otherwise return
- * a non-zero error code.
- */
- public static int startKernelNeighborProbe(int ifIndex, InetAddress ip) {
- final String msgSnippet = "probing ip=" + ip.getHostAddress() + "%" + ifIndex;
- if (DBG) { Log.d(TAG, msgSnippet); }
-
- final byte[] msg = RtNetlinkNeighborMessage.newNewNeighborMessage(
- 1, ip, StructNdMsg.NUD_PROBE, ifIndex, null);
-
- try {
- NetlinkSocket.sendOneShotKernelMessage(OsConstants.NETLINK_ROUTE, msg);
- } catch (ErrnoException e) {
- Log.e(TAG, "Error " + msgSnippet + ": " + e);
- return -e.errno;
- }
-
- return 0;
- }
-
- public static class NeighborEvent {
- final long elapsedMs;
- final short msgType;
- final int ifindex;
- final InetAddress ip;
- final short nudState;
- final MacAddress macAddr;
-
- public NeighborEvent(long elapsedMs, short msgType, int ifindex, InetAddress ip,
- short nudState, MacAddress macAddr) {
- this.elapsedMs = elapsedMs;
- this.msgType = msgType;
- this.ifindex = ifindex;
- this.ip = ip;
- this.nudState = nudState;
- this.macAddr = macAddr;
- }
-
- boolean isConnected() {
- return (msgType != RTM_DELNEIGH) && StructNdMsg.isNudStateConnected(nudState);
- }
-
- boolean isValid() {
- return (msgType != RTM_DELNEIGH) && StructNdMsg.isNudStateValid(nudState);
- }
-
- @Override
- public String toString() {
- final StringJoiner j = new StringJoiner(",", "NeighborEvent{", "}");
- return j.add("@" + elapsedMs)
- .add(stringForNlMsgType(msgType))
- .add("if=" + ifindex)
- .add(ip.getHostAddress())
- .add(StructNdMsg.stringForNudState(nudState))
- .add("[" + macAddr + "]")
- .toString();
- }
- }
-
- public interface NeighborEventConsumer {
- // Every neighbor event received on the netlink socket is passed in
- // here. Subclasses should filter for events of interest.
- public void accept(NeighborEvent event);
- }
-
- private final SharedLog mLog;
- private final NeighborEventConsumer mConsumer;
-
- public IpNeighborMonitor(Handler h, SharedLog log, NeighborEventConsumer cb) {
- super(h, NetlinkSocket.DEFAULT_RECV_BUFSIZE);
- mLog = log.forSubComponent(TAG);
- mConsumer = (cb != null) ? cb : (event) -> { /* discard */ };
- }
-
- @Override
- protected FileDescriptor createFd() {
- FileDescriptor fd = null;
-
- try {
- fd = NetlinkSocket.forProto(OsConstants.NETLINK_ROUTE);
- Os.bind(fd, makeNetlinkSocketAddress(0, OsConstants.RTMGRP_NEIGH));
- NetlinkSocket.connectToKernel(fd);
-
- if (VDBG) {
- final SocketAddress nlAddr = Os.getsockname(fd);
- Log.d(TAG, "bound to sockaddr_nl{" + nlAddr.toString() + "}");
- }
- } catch (ErrnoException|SocketException e) {
- logError("Failed to create rtnetlink socket", e);
- IoUtils.closeQuietly(fd);
- return null;
- }
-
- return fd;
- }
-
- @Override
- protected void handlePacket(byte[] recvbuf, int length) {
- final long whenMs = SystemClock.elapsedRealtime();
-
- final ByteBuffer byteBuffer = ByteBuffer.wrap(recvbuf, 0, length);
- byteBuffer.order(ByteOrder.nativeOrder());
-
- parseNetlinkMessageBuffer(byteBuffer, whenMs);
- }
-
- private void parseNetlinkMessageBuffer(ByteBuffer byteBuffer, long whenMs) {
- while (byteBuffer.remaining() > 0) {
- final int position = byteBuffer.position();
- final NetlinkMessage nlMsg = NetlinkMessage.parse(byteBuffer);
- if (nlMsg == null || nlMsg.getHeader() == null) {
- byteBuffer.position(position);
- mLog.e("unparsable netlink msg: " + hexify(byteBuffer));
- break;
- }
-
- final int srcPortId = nlMsg.getHeader().nlmsg_pid;
- if (srcPortId != 0) {
- mLog.e("non-kernel source portId: " + BitUtils.uint32(srcPortId));
- break;
- }
-
- if (nlMsg instanceof NetlinkErrorMessage) {
- mLog.e("netlink error: " + nlMsg);
- continue;
- } else if (!(nlMsg instanceof RtNetlinkNeighborMessage)) {
- mLog.i("non-rtnetlink neighbor msg: " + nlMsg);
- continue;
- }
-
- evaluateRtNetlinkNeighborMessage((RtNetlinkNeighborMessage) nlMsg, whenMs);
- }
- }
-
- private void evaluateRtNetlinkNeighborMessage(
- RtNetlinkNeighborMessage neighMsg, long whenMs) {
- final short msgType = neighMsg.getHeader().nlmsg_type;
- final StructNdMsg ndMsg = neighMsg.getNdHeader();
- if (ndMsg == null) {
- mLog.e("RtNetlinkNeighborMessage without ND message header!");
- return;
- }
-
- final int ifindex = ndMsg.ndm_ifindex;
- final InetAddress destination = neighMsg.getDestination();
- final short nudState =
- (msgType == RTM_DELNEIGH)
- ? StructNdMsg.NUD_NONE
- : ndMsg.ndm_state;
-
- final NeighborEvent event = new NeighborEvent(
- whenMs, msgType, ifindex, destination, nudState,
- getMacAddress(neighMsg.getLinkLayerAddress()));
-
- if (VDBG) {
- Log.d(TAG, neighMsg.toString());
- }
- if (DBG) {
- Log.d(TAG, event.toString());
- }
-
- mConsumer.accept(event);
- }
-
- private static MacAddress getMacAddress(byte[] linkLayerAddress) {
- if (linkLayerAddress != null) {
- try {
- return MacAddress.fromBytes(linkLayerAddress);
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Failed to parse link-layer address: " + hexify(linkLayerAddress));
- }
- }
-
- return null;
- }
-}
diff --git a/services/net/java/android/net/ip/IpReachabilityMonitor.java b/services/net/java/android/net/ip/IpReachabilityMonitor.java
deleted file mode 100644
index 761db68..0000000
--- a/services/net/java/android/net/ip/IpReachabilityMonitor.java
+++ /dev/null
@@ -1,393 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.ip;
-
-import static android.net.metrics.IpReachabilityEvent.NUD_FAILED;
-import static android.net.metrics.IpReachabilityEvent.NUD_FAILED_ORGANIC;
-import static android.net.metrics.IpReachabilityEvent.PROVISIONING_LOST;
-import static android.net.metrics.IpReachabilityEvent.PROVISIONING_LOST_ORGANIC;
-
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.LinkProperties;
-import android.net.RouteInfo;
-import android.net.ip.IpNeighborMonitor.NeighborEvent;
-import android.net.metrics.IpConnectivityLog;
-import android.net.metrics.IpReachabilityEvent;
-import android.net.netlink.StructNdMsg;
-import android.net.util.InterfaceParams;
-import android.net.util.SharedLog;
-import android.os.Handler;
-import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
-import android.os.SystemClock;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.DumpUtils;
-import com.android.internal.util.DumpUtils.Dump;
-
-import java.io.PrintWriter;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-
-/**
- * IpReachabilityMonitor.
- *
- * Monitors on-link IP reachability and notifies callers whenever any on-link
- * addresses of interest appear to have become unresponsive.
- *
- * This code does not concern itself with "why" a neighbour might have become
- * unreachable. Instead, it primarily reacts to the kernel's notion of IP
- * reachability for each of the neighbours we know to be critically important
- * to normal network connectivity. As such, it is often "just the messenger":
- * the neighbours about which it warns are already deemed by the kernel to have
- * become unreachable.
- *
- *
- * How it works:
- *
- * 1. The "on-link neighbours of interest" found in a given LinkProperties
- * instance are added to a "watch list" via #updateLinkProperties().
- * This usually means all default gateways and any on-link DNS servers.
- *
- * 2. We listen continuously for netlink neighbour messages (RTM_NEWNEIGH,
- * RTM_DELNEIGH), watching only for neighbours in the watch list.
- *
- * - A neighbour going into NUD_REACHABLE, NUD_STALE, NUD_DELAY, and
- * even NUD_PROBE is perfectly normal; we merely record the new state.
- *
- * - A neighbour's entry may be deleted (RTM_DELNEIGH), for example due
- * to garbage collection. This is not necessarily of immediate
- * concern; we record the neighbour as moving to NUD_NONE.
- *
- * - A neighbour transitioning to NUD_FAILED (for any reason) is
- * critically important and is handled as described below in #4.
- *
- * 3. All on-link neighbours in the watch list can be forcibly "probed" by
- * calling #probeAll(). This should be called whenever it is important to
- * verify that critical neighbours on the link are still reachable, e.g.
- * when roaming between BSSIDs.
- *
- * - The kernel will send unicast ARP requests for IPv4 neighbours and
- * unicast NS packets for IPv6 neighbours. The expected replies will
- * likely be unicast.
- *
- * - The forced probing is done holding a wakelock. The kernel may,
- * however, initiate probing of a neighbor on its own, i.e. whenever
- * a neighbour has expired from NUD_DELAY.
- *
- * - The kernel sends:
- *
- * /proc/sys/net/ipv{4,6}/neigh/<ifname>/ucast_solicit
- *
- * number of probes (usually 3) every:
- *
- * /proc/sys/net/ipv{4,6}/neigh/<ifname>/retrans_time_ms
- *
- * number of milliseconds (usually 1000ms). This normally results in
- * 3 unicast packets, 1 per second.
- *
- * - If no response is received to any of the probe packets, the kernel
- * marks the neighbour as being in state NUD_FAILED, and the listening
- * process in #2 will learn of it.
- *
- * 4. We call the supplied Callback#notifyLost() function if the loss of a
- * neighbour in NUD_FAILED would cause IPv4 or IPv6 configuration to
- * become incomplete (a loss of provisioning).
- *
- * - For example, losing all our IPv4 on-link DNS servers (or losing
- * our only IPv6 default gateway) constitutes a loss of IPv4 (IPv6)
- * provisioning; Callback#notifyLost() would be called.
- *
- * - Since it can be non-trivial to reacquire certain IP provisioning
- * state it may be best for the link to disconnect completely and
- * reconnect afresh.
- *
- * Accessing an instance of this class from multiple threads is NOT safe.
- *
- * @hide
- */
-public class IpReachabilityMonitor {
- private static final String TAG = "IpReachabilityMonitor";
- private static final boolean DBG = false;
- private static final boolean VDBG = false;
-
- public interface Callback {
- // This callback function must execute as quickly as possible as it is
- // run on the same thread that listens to kernel neighbor updates.
- //
- // TODO: refactor to something like notifyProvisioningLost(String msg).
- public void notifyLost(InetAddress ip, String logMsg);
- }
-
- /**
- * Encapsulates IpReachabilityMonitor depencencies on systems that hinder unit testing.
- * TODO: consider also wrapping MultinetworkPolicyTracker in this interface.
- */
- interface Dependencies {
- void acquireWakeLock(long durationMs);
-
- static Dependencies makeDefault(Context context, String iface) {
- final String lockName = TAG + "." + iface;
- final PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
- final WakeLock lock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, lockName);
-
- return new Dependencies() {
- public void acquireWakeLock(long durationMs) {
- lock.acquire(durationMs);
- }
- };
- }
- }
-
- private final InterfaceParams mInterfaceParams;
- private final IpNeighborMonitor mIpNeighborMonitor;
- private final SharedLog mLog;
- private final Callback mCallback;
- private final Dependencies mDependencies;
- private final boolean mUsingMultinetworkPolicyTracker;
- private final ConnectivityManager mCm;
- private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
- private LinkProperties mLinkProperties = new LinkProperties();
- private Map<InetAddress, NeighborEvent> mNeighborWatchList = new HashMap<>();
- // Time in milliseconds of the last forced probe request.
- private volatile long mLastProbeTimeMs;
-
- public IpReachabilityMonitor(
- Context context, InterfaceParams ifParams, Handler h, SharedLog log, Callback callback,
- boolean usingMultinetworkPolicyTracker) {
- this(context, ifParams, h, log, callback, usingMultinetworkPolicyTracker,
- Dependencies.makeDefault(context, ifParams.name));
- }
-
- @VisibleForTesting
- IpReachabilityMonitor(Context context, InterfaceParams ifParams, Handler h, SharedLog log,
- Callback callback, boolean usingMultinetworkPolicyTracker, Dependencies dependencies) {
- if (ifParams == null) throw new IllegalArgumentException("null InterfaceParams");
-
- mInterfaceParams = ifParams;
- mLog = log.forSubComponent(TAG);
- mCallback = callback;
- mUsingMultinetworkPolicyTracker = usingMultinetworkPolicyTracker;
- mCm = context.getSystemService(ConnectivityManager.class);
- mDependencies = dependencies;
-
- mIpNeighborMonitor = new IpNeighborMonitor(h, mLog,
- (NeighborEvent event) -> {
- if (mInterfaceParams.index != event.ifindex) return;
- if (!mNeighborWatchList.containsKey(event.ip)) return;
-
- final NeighborEvent prev = mNeighborWatchList.put(event.ip, event);
-
- // TODO: Consider what to do with other states that are not within
- // NeighborEvent#isValid() (i.e. NUD_NONE, NUD_INCOMPLETE).
- if (event.nudState == StructNdMsg.NUD_FAILED) {
- mLog.w("ALERT neighbor went from: " + prev + " to: " + event);
- handleNeighborLost(event);
- }
- });
- mIpNeighborMonitor.start();
- }
-
- public void stop() {
- mIpNeighborMonitor.stop();
- clearLinkProperties();
- }
-
- public void dump(PrintWriter pw) {
- DumpUtils.dumpAsync(
- mIpNeighborMonitor.getHandler(),
- new Dump() {
- @Override
- public void dump(PrintWriter pw, String prefix) {
- pw.println(describeWatchList("\n"));
- }
- },
- pw, "", 1000);
- }
-
- private String describeWatchList() { return describeWatchList(" "); }
-
- private String describeWatchList(String sep) {
- final StringBuilder sb = new StringBuilder();
- sb.append("iface{" + mInterfaceParams + "}," + sep);
- sb.append("ntable=[" + sep);
- String delimiter = "";
- for (Map.Entry<InetAddress, NeighborEvent> entry : mNeighborWatchList.entrySet()) {
- sb.append(delimiter).append(entry.getKey().getHostAddress() + "/" + entry.getValue());
- delimiter = "," + sep;
- }
- sb.append("]");
- return sb.toString();
- }
-
- private static boolean isOnLink(List<RouteInfo> routes, InetAddress ip) {
- for (RouteInfo route : routes) {
- if (!route.hasGateway() && route.matches(ip)) {
- return true;
- }
- }
- return false;
- }
-
- public void updateLinkProperties(LinkProperties lp) {
- if (!mInterfaceParams.name.equals(lp.getInterfaceName())) {
- // TODO: figure out whether / how to cope with interface changes.
- Log.wtf(TAG, "requested LinkProperties interface '" + lp.getInterfaceName() +
- "' does not match: " + mInterfaceParams.name);
- return;
- }
-
- mLinkProperties = new LinkProperties(lp);
- Map<InetAddress, NeighborEvent> newNeighborWatchList = new HashMap<>();
-
- final List<RouteInfo> routes = mLinkProperties.getRoutes();
- for (RouteInfo route : routes) {
- if (route.hasGateway()) {
- InetAddress gw = route.getGateway();
- if (isOnLink(routes, gw)) {
- newNeighborWatchList.put(gw, mNeighborWatchList.getOrDefault(gw, null));
- }
- }
- }
-
- for (InetAddress dns : lp.getDnsServers()) {
- if (isOnLink(routes, dns)) {
- newNeighborWatchList.put(dns, mNeighborWatchList.getOrDefault(dns, null));
- }
- }
-
- mNeighborWatchList = newNeighborWatchList;
- if (DBG) { Log.d(TAG, "watch: " + describeWatchList()); }
- }
-
- public void clearLinkProperties() {
- mLinkProperties.clear();
- mNeighborWatchList.clear();
- if (DBG) { Log.d(TAG, "clear: " + describeWatchList()); }
- }
-
- private void handleNeighborLost(NeighborEvent event) {
- final LinkProperties whatIfLp = new LinkProperties(mLinkProperties);
-
- InetAddress ip = null;
- for (Map.Entry<InetAddress, NeighborEvent> entry : mNeighborWatchList.entrySet()) {
- // TODO: Consider using NeighborEvent#isValid() here; it's more
- // strict but may interact badly if other entries are somehow in
- // NUD_INCOMPLETE (say, during network attach).
- if (entry.getValue().nudState != StructNdMsg.NUD_FAILED) continue;
-
- ip = entry.getKey();
- for (RouteInfo route : mLinkProperties.getRoutes()) {
- if (ip.equals(route.getGateway())) {
- whatIfLp.removeRoute(route);
- }
- }
-
- if (avoidingBadLinks() || !(ip instanceof Inet6Address)) {
- // We should do this unconditionally, but alas we cannot: b/31827713.
- whatIfLp.removeDnsServer(ip);
- }
- }
-
- final boolean lostProvisioning =
- (mLinkProperties.isIPv4Provisioned() && !whatIfLp.isIPv4Provisioned())
- || (mLinkProperties.isIPv6Provisioned() && !whatIfLp.isIPv6Provisioned());
-
- if (lostProvisioning) {
- final String logMsg = "FAILURE: LOST_PROVISIONING, " + event;
- Log.w(TAG, logMsg);
- if (mCallback != null) {
- // TODO: remove |ip| when the callback signature no longer has
- // an InetAddress argument.
- mCallback.notifyLost(ip, logMsg);
- }
- }
- logNudFailed(lostProvisioning);
- }
-
- private boolean avoidingBadLinks() {
- return !mUsingMultinetworkPolicyTracker || mCm.getAvoidBadWifi();
- }
-
- public void probeAll() {
- final List<InetAddress> ipProbeList = new ArrayList<>(mNeighborWatchList.keySet());
-
- if (!ipProbeList.isEmpty()) {
- // Keep the CPU awake long enough to allow all ARP/ND
- // probes a reasonable chance at success. See b/23197666.
- //
- // The wakelock we use is (by default) refcounted, and this version
- // of acquire(timeout) queues a release message to keep acquisitions
- // and releases balanced.
- mDependencies.acquireWakeLock(getProbeWakeLockDuration());
- }
-
- for (InetAddress ip : ipProbeList) {
- final int rval = IpNeighborMonitor.startKernelNeighborProbe(mInterfaceParams.index, ip);
- mLog.log(String.format("put neighbor %s into NUD_PROBE state (rval=%d)",
- ip.getHostAddress(), rval));
- logEvent(IpReachabilityEvent.PROBE, rval);
- }
- mLastProbeTimeMs = SystemClock.elapsedRealtime();
- }
-
- private static long getProbeWakeLockDuration() {
- // Ideally, this would be computed by examining the values of:
- //
- // /proc/sys/net/ipv[46]/neigh/<ifname>/ucast_solicit
- //
- // and:
- //
- // /proc/sys/net/ipv[46]/neigh/<ifname>/retrans_time_ms
- //
- // For now, just make some assumptions.
- final long numUnicastProbes = 3;
- final long retransTimeMs = 1000;
- final long gracePeriodMs = 500;
- return (numUnicastProbes * retransTimeMs) + gracePeriodMs;
- }
-
- private void logEvent(int probeType, int errorCode) {
- int eventType = probeType | (errorCode & 0xff);
- mMetricsLog.log(mInterfaceParams.name, new IpReachabilityEvent(eventType));
- }
-
- private void logNudFailed(boolean lostProvisioning) {
- long duration = SystemClock.elapsedRealtime() - mLastProbeTimeMs;
- boolean isFromProbe = (duration < getProbeWakeLockDuration());
- int eventType = nudFailureEventType(isFromProbe, lostProvisioning);
- mMetricsLog.log(mInterfaceParams.name, new IpReachabilityEvent(eventType));
- }
-
- /**
- * Returns the NUD failure event type code corresponding to the given conditions.
- */
- private static int nudFailureEventType(boolean isFromProbe, boolean isProvisioningLost) {
- if (isFromProbe) {
- return isProvisioningLost ? PROVISIONING_LOST : NUD_FAILED;
- } else {
- return isProvisioningLost ? PROVISIONING_LOST_ORGANIC : NUD_FAILED_ORGANIC;
- }
- }
-}
diff --git a/services/net/java/android/net/ip/IpServer.java b/services/net/java/android/net/ip/IpServer.java
index 7910c9a..f7360f5 100644
--- a/services/net/java/android/net/ip/IpServer.java
+++ b/services/net/java/android/net/ip/IpServer.java
@@ -40,7 +40,7 @@
import android.net.ip.RouterAdvertisementDaemon.RaParams;
import android.net.util.InterfaceParams;
import android.net.util.InterfaceSet;
-import android.net.util.NetdService;
+import android.net.shared.NetdService;
import android.net.util.SharedLog;
import android.os.INetworkManagementService;
import android.os.Looper;
diff --git a/services/net/java/android/net/util/NetdService.java b/services/net/java/android/net/shared/NetdService.java
similarity index 99%
rename from services/net/java/android/net/util/NetdService.java
rename to services/net/java/android/net/shared/NetdService.java
index 80b2c27..be0f5f2 100644
--- a/services/net/java/android/net/util/NetdService.java
+++ b/services/net/java/android/net/shared/NetdService.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.net.util;
+package android.net.shared;
import android.net.INetd;
import android.os.RemoteException;
diff --git a/services/net/java/android/net/util/ConnectivityPacketSummary.java b/services/net/java/android/net/util/ConnectivityPacketSummary.java
deleted file mode 100644
index ec833b0..0000000
--- a/services/net/java/android/net/util/ConnectivityPacketSummary.java
+++ /dev/null
@@ -1,415 +0,0 @@
-/*
- * Copyright (C) 2016 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.util;
-
-import static android.net.util.NetworkConstants.ARP_HWTYPE_ETHER;
-import static android.net.util.NetworkConstants.ARP_PAYLOAD_LEN;
-import static android.net.util.NetworkConstants.ARP_REPLY;
-import static android.net.util.NetworkConstants.ARP_REQUEST;
-import static android.net.util.NetworkConstants.DHCP4_CLIENT_PORT;
-import static android.net.util.NetworkConstants.ETHER_ADDR_LEN;
-import static android.net.util.NetworkConstants.ETHER_DST_ADDR_OFFSET;
-import static android.net.util.NetworkConstants.ETHER_HEADER_LEN;
-import static android.net.util.NetworkConstants.ETHER_SRC_ADDR_OFFSET;
-import static android.net.util.NetworkConstants.ETHER_TYPE_ARP;
-import static android.net.util.NetworkConstants.ETHER_TYPE_IPV4;
-import static android.net.util.NetworkConstants.ETHER_TYPE_IPV6;
-import static android.net.util.NetworkConstants.ETHER_TYPE_OFFSET;
-import static android.net.util.NetworkConstants.ICMPV6_HEADER_MIN_LEN;
-import static android.net.util.NetworkConstants.ICMPV6_ND_OPTION_LENGTH_SCALING_FACTOR;
-import static android.net.util.NetworkConstants.ICMPV6_ND_OPTION_MIN_LENGTH;
-import static android.net.util.NetworkConstants.ICMPV6_ND_OPTION_MTU;
-import static android.net.util.NetworkConstants.ICMPV6_ND_OPTION_SLLA;
-import static android.net.util.NetworkConstants.ICMPV6_ND_OPTION_TLLA;
-import static android.net.util.NetworkConstants.ICMPV6_NEIGHBOR_ADVERTISEMENT;
-import static android.net.util.NetworkConstants.ICMPV6_NEIGHBOR_SOLICITATION;
-import static android.net.util.NetworkConstants.ICMPV6_ROUTER_ADVERTISEMENT;
-import static android.net.util.NetworkConstants.ICMPV6_ROUTER_SOLICITATION;
-import static android.net.util.NetworkConstants.IPV4_ADDR_LEN;
-import static android.net.util.NetworkConstants.IPV4_DST_ADDR_OFFSET;
-import static android.net.util.NetworkConstants.IPV4_FLAGS_OFFSET;
-import static android.net.util.NetworkConstants.IPV4_FRAGMENT_MASK;
-import static android.net.util.NetworkConstants.IPV4_HEADER_MIN_LEN;
-import static android.net.util.NetworkConstants.IPV4_IHL_MASK;
-import static android.net.util.NetworkConstants.IPV4_PROTOCOL_OFFSET;
-import static android.net.util.NetworkConstants.IPV4_SRC_ADDR_OFFSET;
-import static android.net.util.NetworkConstants.IPV6_ADDR_LEN;
-import static android.net.util.NetworkConstants.IPV6_HEADER_LEN;
-import static android.net.util.NetworkConstants.IPV6_PROTOCOL_OFFSET;
-import static android.net.util.NetworkConstants.IPV6_SRC_ADDR_OFFSET;
-import static android.net.util.NetworkConstants.UDP_HEADER_LEN;
-import static android.net.util.NetworkConstants.asString;
-import static android.net.util.NetworkConstants.asUint;
-import static android.system.OsConstants.IPPROTO_ICMPV6;
-import static android.system.OsConstants.IPPROTO_UDP;
-
-import android.net.MacAddress;
-import android.net.dhcp.DhcpPacket;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.StringJoiner;
-
-
-/**
- * Critical connectivity packet summarizing class.
- *
- * Outputs short descriptions of ARP, DHCPv4, and IPv6 RS/RA/NS/NA packets.
- *
- * @hide
- */
-public class ConnectivityPacketSummary {
- private static final String TAG = ConnectivityPacketSummary.class.getSimpleName();
-
- private final byte[] mHwAddr;
- private final byte[] mBytes;
- private final int mLength;
- private final ByteBuffer mPacket;
- private final String mSummary;
-
- 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.
- public static String summarize(MacAddress macAddr, byte[] buffer, int length) {
- if ((macAddr == null) || (buffer == null)) return null;
- length = Math.min(length, buffer.length);
- return (new ConnectivityPacketSummary(macAddr, buffer, length)).toString();
- }
-
- private ConnectivityPacketSummary(MacAddress macAddr, byte[] buffer, int length) {
- mHwAddr = macAddr.toByteArray();
- mBytes = buffer;
- mLength = Math.min(length, mBytes.length);
- mPacket = ByteBuffer.wrap(mBytes, 0, mLength);
- mPacket.order(ByteOrder.BIG_ENDIAN);
-
- final StringJoiner sj = new StringJoiner(" ");
- // TODO: support other link-layers, or even no link-layer header.
- parseEther(sj);
- mSummary = sj.toString();
- }
-
- public String toString() {
- return mSummary;
- }
-
- private void parseEther(StringJoiner sj) {
- if (mPacket.remaining() < ETHER_HEADER_LEN) {
- sj.add("runt:").add(asString(mPacket.remaining()));
- return;
- }
-
- mPacket.position(ETHER_SRC_ADDR_OFFSET);
- final ByteBuffer srcMac = (ByteBuffer) mPacket.slice().limit(ETHER_ADDR_LEN);
- sj.add(ByteBuffer.wrap(mHwAddr).equals(srcMac) ? "TX" : "RX");
- sj.add(getMacAddressString(srcMac));
-
- mPacket.position(ETHER_DST_ADDR_OFFSET);
- final ByteBuffer dstMac = (ByteBuffer) mPacket.slice().limit(ETHER_ADDR_LEN);
- sj.add(">").add(getMacAddressString(dstMac));
-
- mPacket.position(ETHER_TYPE_OFFSET);
- final int etherType = asUint(mPacket.getShort());
- switch (etherType) {
- case ETHER_TYPE_ARP:
- sj.add("arp");
- parseARP(sj);
- break;
- case ETHER_TYPE_IPV4:
- sj.add("ipv4");
- parseIPv4(sj);
- break;
- case ETHER_TYPE_IPV6:
- sj.add("ipv6");
- parseIPv6(sj);
- break;
- default:
- // Unknown ether type.
- sj.add("ethtype").add(asString(etherType));
- break;
- }
- }
-
- private void parseARP(StringJoiner sj) {
- if (mPacket.remaining() < ARP_PAYLOAD_LEN) {
- sj.add("runt:").add(asString(mPacket.remaining()));
- return;
- }
-
- if (asUint(mPacket.getShort()) != ARP_HWTYPE_ETHER ||
- asUint(mPacket.getShort()) != ETHER_TYPE_IPV4 ||
- asUint(mPacket.get()) != ETHER_ADDR_LEN ||
- asUint(mPacket.get()) != IPV4_ADDR_LEN) {
- sj.add("unexpected header");
- return;
- }
-
- final int opCode = asUint(mPacket.getShort());
-
- final String senderHwAddr = getMacAddressString(mPacket);
- final String senderIPv4 = getIPv4AddressString(mPacket);
- getMacAddressString(mPacket); // target hardware address, unused
- final String targetIPv4 = getIPv4AddressString(mPacket);
-
- if (opCode == ARP_REQUEST) {
- sj.add("who-has").add(targetIPv4);
- } else if (opCode == ARP_REPLY) {
- sj.add("reply").add(senderIPv4).add(senderHwAddr);
- } else {
- sj.add("unknown opcode").add(asString(opCode));
- }
- }
-
- private void parseIPv4(StringJoiner sj) {
- if (!mPacket.hasRemaining()) {
- sj.add("runt");
- return;
- }
-
- final int startOfIpLayer = mPacket.position();
- final int ipv4HeaderLength = (mPacket.get(startOfIpLayer) & IPV4_IHL_MASK) * 4;
- if (mPacket.remaining() < ipv4HeaderLength ||
- mPacket.remaining() < IPV4_HEADER_MIN_LEN) {
- sj.add("runt:").add(asString(mPacket.remaining()));
- return;
- }
- final int startOfTransportLayer = startOfIpLayer + ipv4HeaderLength;
-
- mPacket.position(startOfIpLayer + IPV4_FLAGS_OFFSET);
- final int flagsAndFragment = asUint(mPacket.getShort());
- final boolean isFragment = (flagsAndFragment & IPV4_FRAGMENT_MASK) != 0;
-
- mPacket.position(startOfIpLayer + IPV4_PROTOCOL_OFFSET);
- final int protocol = asUint(mPacket.get());
-
- mPacket.position(startOfIpLayer + IPV4_SRC_ADDR_OFFSET);
- final String srcAddr = getIPv4AddressString(mPacket);
-
- mPacket.position(startOfIpLayer + IPV4_DST_ADDR_OFFSET);
- final String dstAddr = getIPv4AddressString(mPacket);
-
- sj.add(srcAddr).add(">").add(dstAddr);
-
- mPacket.position(startOfTransportLayer);
- if (protocol == IPPROTO_UDP) {
- sj.add("udp");
- if (isFragment) sj.add("fragment");
- else parseUDP(sj);
- } else {
- sj.add("proto").add(asString(protocol));
- if (isFragment) sj.add("fragment");
- }
- }
-
- private void parseIPv6(StringJoiner sj) {
- if (mPacket.remaining() < IPV6_HEADER_LEN) {
- sj.add("runt:").add(asString(mPacket.remaining()));
- return;
- }
-
- final int startOfIpLayer = mPacket.position();
-
- mPacket.position(startOfIpLayer + IPV6_PROTOCOL_OFFSET);
- final int protocol = asUint(mPacket.get());
-
- mPacket.position(startOfIpLayer + IPV6_SRC_ADDR_OFFSET);
- final String srcAddr = getIPv6AddressString(mPacket);
- final String dstAddr = getIPv6AddressString(mPacket);
-
- sj.add(srcAddr).add(">").add(dstAddr);
-
- mPacket.position(startOfIpLayer + IPV6_HEADER_LEN);
- if (protocol == IPPROTO_ICMPV6) {
- sj.add("icmp6");
- parseICMPv6(sj);
- } else {
- sj.add("proto").add(asString(protocol));
- }
- }
-
- private void parseICMPv6(StringJoiner sj) {
- if (mPacket.remaining() < ICMPV6_HEADER_MIN_LEN) {
- sj.add("runt:").add(asString(mPacket.remaining()));
- return;
- }
-
- final int icmp6Type = asUint(mPacket.get());
- final int icmp6Code = asUint(mPacket.get());
- mPacket.getShort(); // checksum, unused
-
- switch (icmp6Type) {
- case ICMPV6_ROUTER_SOLICITATION:
- sj.add("rs");
- parseICMPv6RouterSolicitation(sj);
- break;
- case ICMPV6_ROUTER_ADVERTISEMENT:
- sj.add("ra");
- parseICMPv6RouterAdvertisement(sj);
- break;
- case ICMPV6_NEIGHBOR_SOLICITATION:
- sj.add("ns");
- parseICMPv6NeighborMessage(sj);
- break;
- case ICMPV6_NEIGHBOR_ADVERTISEMENT:
- sj.add("na");
- parseICMPv6NeighborMessage(sj);
- break;
- default:
- sj.add("type").add(asString(icmp6Type));
- sj.add("code").add(asString(icmp6Code));
- break;
- }
- }
-
- private void parseICMPv6RouterSolicitation(StringJoiner sj) {
- final int RESERVED = 4;
- if (mPacket.remaining() < RESERVED) {
- sj.add("runt:").add(asString(mPacket.remaining()));
- return;
- }
-
- mPacket.position(mPacket.position() + RESERVED);
- parseICMPv6NeighborDiscoveryOptions(sj);
- }
-
- private void parseICMPv6RouterAdvertisement(StringJoiner sj) {
- final int FLAGS_AND_TIMERS = 3 * 4;
- if (mPacket.remaining() < FLAGS_AND_TIMERS) {
- sj.add("runt:").add(asString(mPacket.remaining()));
- return;
- }
-
- mPacket.position(mPacket.position() + FLAGS_AND_TIMERS);
- parseICMPv6NeighborDiscoveryOptions(sj);
- }
-
- private void parseICMPv6NeighborMessage(StringJoiner sj) {
- final int RESERVED = 4;
- final int minReq = RESERVED + IPV6_ADDR_LEN;
- if (mPacket.remaining() < minReq) {
- sj.add("runt:").add(asString(mPacket.remaining()));
- return;
- }
-
- mPacket.position(mPacket.position() + RESERVED);
- sj.add(getIPv6AddressString(mPacket));
- parseICMPv6NeighborDiscoveryOptions(sj);
- }
-
- private void parseICMPv6NeighborDiscoveryOptions(StringJoiner sj) {
- // All ND options are TLV, where T is one byte and L is one byte equal
- // to the length of T + L + V in units of 8 octets.
- while (mPacket.remaining() >= ICMPV6_ND_OPTION_MIN_LENGTH) {
- final int ndType = asUint(mPacket.get());
- final int ndLength = asUint(mPacket.get());
- final int ndBytes = ndLength * ICMPV6_ND_OPTION_LENGTH_SCALING_FACTOR - 2;
- if (ndBytes < 0 || ndBytes > mPacket.remaining()) {
- sj.add("<malformed>");
- break;
- }
- final int position = mPacket.position();
-
- switch (ndType) {
- case ICMPV6_ND_OPTION_SLLA:
- sj.add("slla");
- sj.add(getMacAddressString(mPacket));
- break;
- case ICMPV6_ND_OPTION_TLLA:
- sj.add("tlla");
- sj.add(getMacAddressString(mPacket));
- break;
- case ICMPV6_ND_OPTION_MTU:
- sj.add("mtu");
- final short reserved = mPacket.getShort();
- sj.add(asString(mPacket.getInt()));
- break;
- default:
- // Skip.
- break;
- }
-
- mPacket.position(position + ndBytes);
- }
- }
-
- private void parseUDP(StringJoiner sj) {
- if (mPacket.remaining() < UDP_HEADER_LEN) {
- sj.add("runt:").add(asString(mPacket.remaining()));
- return;
- }
-
- final int previous = mPacket.position();
- final int srcPort = asUint(mPacket.getShort());
- final int dstPort = asUint(mPacket.getShort());
- sj.add(asString(srcPort)).add(">").add(asString(dstPort));
-
- mPacket.position(previous + UDP_HEADER_LEN);
- if (srcPort == DHCP4_CLIENT_PORT || dstPort == DHCP4_CLIENT_PORT) {
- sj.add("dhcp4");
- parseDHCPv4(sj);
- }
- }
-
- private void parseDHCPv4(StringJoiner sj) {
- final DhcpPacket dhcpPacket;
- try {
- dhcpPacket = DhcpPacket.decodeFullPacket(mBytes, mLength, DhcpPacket.ENCAP_L2);
- sj.add(dhcpPacket.toString());
- } catch (DhcpPacket.ParseException e) {
- sj.add("parse error: " + e);
- }
- }
-
- private static String getIPv4AddressString(ByteBuffer ipv4) {
- return getIpAddressString(ipv4, IPV4_ADDR_LEN);
- }
-
- private static String getIPv6AddressString(ByteBuffer ipv6) {
- return getIpAddressString(ipv6, IPV6_ADDR_LEN);
- }
-
- private static String getIpAddressString(ByteBuffer ip, int byteLength) {
- if (ip == null || ip.remaining() < byteLength) return "invalid";
-
- byte[] bytes = new byte[byteLength];
- ip.get(bytes, 0, byteLength);
- try {
- InetAddress addr = InetAddress.getByAddress(bytes);
- return addr.getHostAddress();
- } catch (UnknownHostException uhe) {
- return "unknown";
- }
- }
-
- private static String getMacAddressString(ByteBuffer mac) {
- if (mac == null || mac.remaining() < ETHER_ADDR_LEN) return "invalid";
-
- byte[] bytes = new byte[ETHER_ADDR_LEN];
- mac.get(bytes, 0, bytes.length);
- Object[] printableBytes = new Object[bytes.length];
- int i = 0;
- for (byte b : bytes) printableBytes[i++] = new Byte(b);
-
- final String MAC48_FORMAT = "%02x:%02x:%02x:%02x:%02x:%02x";
- return String.format(MAC48_FORMAT, printableBytes);
- }
-}
diff --git a/services/net/java/android/net/util/FdEventsReader.java b/services/net/java/android/net/util/FdEventsReader.java
deleted file mode 100644
index 8bbf449..0000000
--- a/services/net/java/android/net/util/FdEventsReader.java
+++ /dev/null
@@ -1,254 +0,0 @@
-/*
- * Copyright (C) 2016 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.util;
-
-import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
-import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.MessageQueue;
-import android.system.ErrnoException;
-import android.system.OsConstants;
-
-import libcore.io.IoUtils;
-
-import java.io.FileDescriptor;
-
-
-/**
- * This class encapsulates the mechanics of registering a file descriptor
- * with a thread's Looper and handling read events (and errors).
- *
- * Subclasses MUST implement createFd() and SHOULD override handlePacket(). They MAY override
- * onStop() and onStart().
- *
- * Subclasses can expect a call life-cycle like the following:
- *
- * [1] when a client calls start(), createFd() is called, followed by the onStart() hook if all
- * goes well. Implementations may override onStart() for additional initialization.
- *
- * [2] yield, waiting for read event or error notification:
- *
- * [a] readPacket() && handlePacket()
- *
- * [b] if (no error):
- * goto 2
- * else:
- * goto 3
- *
- * [3] when a client calls stop(), the onStop() hook is called (unless already stopped or never
- * started). Implementations may override onStop() for additional cleanup.
- *
- * The packet receive buffer is recycled on every read call, so subclasses
- * should make any copies they would like inside their handlePacket()
- * implementation.
- *
- * All public methods MUST only be called from the same thread with which
- * the Handler constructor argument is associated.
- *
- * @hide
- */
-public abstract class FdEventsReader<BufferType> {
- private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR;
- private static final int UNREGISTER_THIS_FD = 0;
-
- @NonNull
- private final Handler mHandler;
- @NonNull
- private final MessageQueue mQueue;
- @NonNull
- private final BufferType mBuffer;
- @Nullable
- private FileDescriptor mFd;
- private long mPacketsReceived;
-
- protected static void closeFd(FileDescriptor fd) {
- IoUtils.closeQuietly(fd);
- }
-
- protected FdEventsReader(@NonNull Handler h, @NonNull BufferType buffer) {
- mHandler = h;
- mQueue = mHandler.getLooper().getQueue();
- mBuffer = buffer;
- }
-
- public void start() {
- if (onCorrectThread()) {
- createAndRegisterFd();
- } else {
- mHandler.post(() -> {
- logError("start() called from off-thread", null);
- createAndRegisterFd();
- });
- }
- }
-
- public void stop() {
- if (onCorrectThread()) {
- unregisterAndDestroyFd();
- } else {
- mHandler.post(() -> {
- logError("stop() called from off-thread", null);
- unregisterAndDestroyFd();
- });
- }
- }
-
- @NonNull
- public Handler getHandler() { return mHandler; }
-
- protected abstract int recvBufSize(@NonNull BufferType buffer);
-
- public int recvBufSize() { return recvBufSize(mBuffer); }
-
- /**
- * Get the number of successful calls to {@link #readPacket(FileDescriptor, Object)}.
- *
- * <p>A call was successful if {@link #readPacket(FileDescriptor, Object)} returned a value > 0.
- */
- public final long numPacketsReceived() { return mPacketsReceived; }
-
- /**
- * Subclasses MUST create the listening socket here, including setting
- * all desired socket options, interface or address/port binding, etc.
- */
- @Nullable
- protected abstract FileDescriptor createFd();
-
- /**
- * Implementations MUST return the bytes read or throw an Exception.
- *
- * <p>The caller may throw a {@link ErrnoException} with {@link OsConstants#EAGAIN} or
- * {@link OsConstants#EINTR}, in which case {@link FdEventsReader} will ignore the buffer
- * contents and respectively wait for further input or retry the read immediately. For all other
- * exceptions, the {@link FdEventsReader} will be stopped with no more interactions with this
- * method.
- */
- protected abstract int readPacket(@NonNull FileDescriptor fd, @NonNull BufferType buffer)
- throws Exception;
-
- /**
- * Called by the main loop for every packet. Any desired copies of
- * |recvbuf| should be made in here, as the underlying byte array is
- * reused across all reads.
- */
- protected void handlePacket(@NonNull BufferType recvbuf, int length) {}
-
- /**
- * Called by the main loop to log errors. In some cases |e| may be null.
- */
- protected void logError(@NonNull String msg, @Nullable Exception e) {}
-
- /**
- * Called by start(), if successful, just prior to returning.
- */
- protected void onStart() {}
-
- /**
- * Called by stop() just prior to returning.
- */
- protected void onStop() {}
-
- private void createAndRegisterFd() {
- if (mFd != null) return;
-
- try {
- mFd = createFd();
- if (mFd != null) {
- // Force the socket to be non-blocking.
- IoUtils.setBlocking(mFd, false);
- }
- } catch (Exception e) {
- logError("Failed to create socket: ", e);
- closeFd(mFd);
- mFd = null;
- }
-
- if (mFd == null) return;
-
- mQueue.addOnFileDescriptorEventListener(
- mFd,
- FD_EVENTS,
- (fd, events) -> {
- // Always call handleInput() so read/recvfrom are given
- // a proper chance to encounter a meaningful errno and
- // perhaps log a useful error message.
- if (!isRunning() || !handleInput()) {
- unregisterAndDestroyFd();
- return UNREGISTER_THIS_FD;
- }
- return FD_EVENTS;
- });
- onStart();
- }
-
- private boolean isRunning() { return (mFd != null) && mFd.valid(); }
-
- // Keep trying to read until we get EAGAIN/EWOULDBLOCK or some fatal error.
- private boolean handleInput() {
- while (isRunning()) {
- final int bytesRead;
-
- try {
- bytesRead = readPacket(mFd, mBuffer);
- if (bytesRead < 1) {
- if (isRunning()) logError("Socket closed, exiting", null);
- break;
- }
- mPacketsReceived++;
- } catch (ErrnoException e) {
- if (e.errno == OsConstants.EAGAIN) {
- // We've read everything there is to read this time around.
- return true;
- } else if (e.errno == OsConstants.EINTR) {
- continue;
- } else {
- if (isRunning()) logError("readPacket error: ", e);
- break;
- }
- } catch (Exception e) {
- if (isRunning()) logError("readPacket error: ", e);
- break;
- }
-
- try {
- handlePacket(mBuffer, bytesRead);
- } catch (Exception e) {
- logError("handlePacket error: ", e);
- break;
- }
- }
-
- return false;
- }
-
- private void unregisterAndDestroyFd() {
- if (mFd == null) return;
-
- mQueue.removeOnFileDescriptorEventListener(mFd);
- closeFd(mFd);
- mFd = null;
- onStop();
- }
-
- private boolean onCorrectThread() {
- return (mHandler.getLooper() == Looper.myLooper());
- }
-}
diff --git a/services/net/java/android/net/util/InterfaceParams.java b/services/net/java/android/net/util/InterfaceParams.java
index 7b060da..f6bb873 100644
--- a/services/net/java/android/net/util/InterfaceParams.java
+++ b/services/net/java/android/net/util/InterfaceParams.java
@@ -16,9 +16,6 @@
package android.net.util;
-import static android.net.util.NetworkConstants.ETHER_MTU;
-import static android.net.util.NetworkConstants.IPV6_MIN_MTU;
-
import static com.android.internal.util.Preconditions.checkArgument;
import android.net.MacAddress;
@@ -44,6 +41,11 @@
public final MacAddress macAddr;
public final int defaultMtu;
+ // TODO: move the below to NetworkStackConstants when this class is moved to the NetworkStack.
+ private static final int ETHER_MTU = 1500;
+ private static final int IPV6_MIN_MTU = 1280;
+
+
public static InterfaceParams getByName(String name) {
final NetworkInterface netif = getNetworkInterfaceByName(name);
if (netif == null) return null;
diff --git a/services/net/java/android/net/util/MultinetworkPolicyTracker.java b/services/net/java/android/net/util/MultinetworkPolicyTracker.java
deleted file mode 100644
index 30c5cd9..0000000
--- a/services/net/java/android/net/util/MultinetworkPolicyTracker.java
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright (C) 2016 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.util;
-
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.database.ContentObserver;
-import android.net.ConnectivityManager;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Message;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.util.Slog;
-
-import java.util.Arrays;
-import java.util.List;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.R;
-
-import static android.provider.Settings.Global.NETWORK_AVOID_BAD_WIFI;
-import static android.provider.Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE;
-
-/**
- * A class to encapsulate management of the "Smart Networking" capability of
- * avoiding bad Wi-Fi when, for example upstream connectivity is lost or
- * certain critical link failures occur.
- *
- * This enables the device to switch to another form of connectivity, like
- * mobile, if it's available and working.
- *
- * The Runnable |avoidBadWifiCallback|, if given, is posted to the supplied
- * Handler' whenever the computed "avoid bad wifi" value changes.
- *
- * Disabling this reverts the device to a level of networking sophistication
- * circa 2012-13 by disabling disparate code paths each of which contribute to
- * maintaining continuous, working Internet connectivity.
- *
- * @hide
- */
-public class MultinetworkPolicyTracker {
- private static String TAG = MultinetworkPolicyTracker.class.getSimpleName();
-
- private final Context mContext;
- private final Handler mHandler;
- private final Runnable mReevaluateRunnable;
- private final List<Uri> mSettingsUris;
- private final ContentResolver mResolver;
- private final SettingObserver mSettingObserver;
- private final BroadcastReceiver mBroadcastReceiver;
-
- private volatile boolean mAvoidBadWifi = true;
- private volatile int mMeteredMultipathPreference;
-
- public MultinetworkPolicyTracker(Context ctx, Handler handler) {
- this(ctx, handler, null);
- }
-
- public MultinetworkPolicyTracker(Context ctx, Handler handler, Runnable avoidBadWifiCallback) {
- mContext = ctx;
- mHandler = handler;
- mReevaluateRunnable = () -> {
- if (updateAvoidBadWifi() && avoidBadWifiCallback != null) {
- avoidBadWifiCallback.run();
- }
- updateMeteredMultipathPreference();
- };
- mSettingsUris = Arrays.asList(
- Settings.Global.getUriFor(NETWORK_AVOID_BAD_WIFI),
- Settings.Global.getUriFor(NETWORK_METERED_MULTIPATH_PREFERENCE));
- mResolver = mContext.getContentResolver();
- mSettingObserver = new SettingObserver();
- mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- reevaluate();
- }
- };
-
- updateAvoidBadWifi();
- updateMeteredMultipathPreference();
- }
-
- public void start() {
- for (Uri uri : mSettingsUris) {
- mResolver.registerContentObserver(uri, false, mSettingObserver);
- }
-
- final IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
- mContext.registerReceiverAsUser(
- mBroadcastReceiver, UserHandle.ALL, intentFilter, null, null);
-
- reevaluate();
- }
-
- public void shutdown() {
- mResolver.unregisterContentObserver(mSettingObserver);
-
- mContext.unregisterReceiver(mBroadcastReceiver);
- }
-
- public boolean getAvoidBadWifi() {
- return mAvoidBadWifi;
- }
-
- // TODO: move this to MultipathPolicyTracker.
- public int getMeteredMultipathPreference() {
- return mMeteredMultipathPreference;
- }
-
- /**
- * Whether the device or carrier configuration disables avoiding bad wifi by default.
- */
- public boolean configRestrictsAvoidBadWifi() {
- return (mContext.getResources().getInteger(R.integer.config_networkAvoidBadWifi) == 0);
- }
-
- /**
- * Whether we should display a notification when wifi becomes unvalidated.
- */
- public boolean shouldNotifyWifiUnvalidated() {
- return configRestrictsAvoidBadWifi() && getAvoidBadWifiSetting() == null;
- }
-
- public String getAvoidBadWifiSetting() {
- return Settings.Global.getString(mResolver, NETWORK_AVOID_BAD_WIFI);
- }
-
- @VisibleForTesting
- public void reevaluate() {
- mHandler.post(mReevaluateRunnable);
- }
-
- public boolean updateAvoidBadWifi() {
- final boolean settingAvoidBadWifi = "1".equals(getAvoidBadWifiSetting());
- final boolean prev = mAvoidBadWifi;
- mAvoidBadWifi = settingAvoidBadWifi || !configRestrictsAvoidBadWifi();
- return mAvoidBadWifi != prev;
- }
-
- /**
- * The default (device and carrier-dependent) value for metered multipath preference.
- */
- public int configMeteredMultipathPreference() {
- return mContext.getResources().getInteger(
- R.integer.config_networkMeteredMultipathPreference);
- }
-
- public void updateMeteredMultipathPreference() {
- String setting = Settings.Global.getString(mResolver, NETWORK_METERED_MULTIPATH_PREFERENCE);
- try {
- mMeteredMultipathPreference = Integer.parseInt(setting);
- } catch (NumberFormatException e) {
- mMeteredMultipathPreference = configMeteredMultipathPreference();
- }
- }
-
- private class SettingObserver extends ContentObserver {
- public SettingObserver() {
- super(null);
- }
-
- @Override
- public void onChange(boolean selfChange) {
- Slog.wtf(TAG, "Should never be reached.");
- }
-
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- if (!mSettingsUris.contains(uri)) {
- Slog.wtf(TAG, "Unexpected settings observation: " + uri);
- }
- reevaluate();
- }
- }
-}
diff --git a/services/net/java/android/net/util/NetworkConstants.java b/services/net/java/android/net/util/NetworkConstants.java
index c183b81..ea5ce65 100644
--- a/services/net/java/android/net/util/NetworkConstants.java
+++ b/services/net/java/android/net/util/NetworkConstants.java
@@ -28,28 +28,6 @@
public final class NetworkConstants {
private NetworkConstants() { throw new RuntimeException("no instance permitted"); }
- /**
- * Ethernet constants.
- *
- * See also:
- * - https://tools.ietf.org/html/rfc894
- * - https://tools.ietf.org/html/rfc2464
- * - https://tools.ietf.org/html/rfc7042
- * - http://www.iana.org/assignments/ethernet-numbers/ethernet-numbers.xhtml
- * - http://www.iana.org/assignments/ieee-802-numbers/ieee-802-numbers.xhtml
- */
- public static final int ETHER_DST_ADDR_OFFSET = 0;
- public static final int ETHER_SRC_ADDR_OFFSET = 6;
- public static final int ETHER_ADDR_LEN = 6;
-
- public static final int ETHER_TYPE_OFFSET = 12;
- public static final int ETHER_TYPE_LENGTH = 2;
- public static final int ETHER_TYPE_ARP = 0x0806;
- public static final int ETHER_TYPE_IPV4 = 0x0800;
- public static final int ETHER_TYPE_IPV6 = 0x86dd;
-
- public static final int ETHER_HEADER_LEN = 14;
-
public static final byte FF = asByte(0xff);
public static final byte[] ETHER_ADDR_BROADCAST = {
FF, FF, FF, FF, FF, FF
@@ -58,34 +36,12 @@
public static final int ETHER_MTU = 1500;
/**
- * ARP constants.
- *
- * See also:
- * - https://tools.ietf.org/html/rfc826
- * - http://www.iana.org/assignments/arp-parameters/arp-parameters.xhtml
- */
- public static final int ARP_PAYLOAD_LEN = 28; // For Ethernet+IPv4.
- public static final int ARP_REQUEST = 1;
- public static final int ARP_REPLY = 2;
- public static final int ARP_HWTYPE_RESERVED_LO = 0;
- public static final int ARP_HWTYPE_ETHER = 1;
- public static final int ARP_HWTYPE_RESERVED_HI = 0xffff;
-
- /**
* IPv4 constants.
*
* See also:
* - https://tools.ietf.org/html/rfc791
*/
- public static final int IPV4_HEADER_MIN_LEN = 20;
- public static final int IPV4_IHL_MASK = 0xf;
- public static final int IPV4_FLAGS_OFFSET = 6;
- public static final int IPV4_FRAGMENT_MASK = 0x1fff;
- public static final int IPV4_PROTOCOL_OFFSET = 9;
- public static final int IPV4_SRC_ADDR_OFFSET = 12;
- public static final int IPV4_DST_ADDR_OFFSET = 16;
public static final int IPV4_ADDR_BITS = 32;
- public static final int IPV4_ADDR_LEN = 4;
/**
* IPv6 constants.
@@ -93,15 +49,10 @@
* See also:
* - https://tools.ietf.org/html/rfc2460
*/
- public static final int IPV6_HEADER_LEN = 40;
- public static final int IPV6_PROTOCOL_OFFSET = 6;
- public static final int IPV6_SRC_ADDR_OFFSET = 8;
- public static final int IPV6_DST_ADDR_OFFSET = 24;
public static final int IPV6_ADDR_BITS = 128;
public static final int IPV6_ADDR_LEN = 16;
public static final int IPV6_MIN_MTU = 1280;
public static final int RFC7421_PREFIX_LENGTH = 64;
- public static final int RFC6177_MIN_PREFIX_LENGTH = 48;
/**
* ICMP common (v4/v6) constants.
@@ -124,45 +75,7 @@
* - https://tools.ietf.org/html/rfc792
*/
public static final int ICMPV4_ECHO_REQUEST_TYPE = 8;
-
- /**
- * ICMPv6 constants.
- *
- * See also:
- * - https://tools.ietf.org/html/rfc4443
- * - https://tools.ietf.org/html/rfc4861
- */
- public static final int ICMPV6_HEADER_MIN_LEN = 4;
public static final int ICMPV6_ECHO_REQUEST_TYPE = 128;
- public static final int ICMPV6_ECHO_REPLY_TYPE = 129;
- public static final int ICMPV6_ROUTER_SOLICITATION = 133;
- public static final int ICMPV6_ROUTER_ADVERTISEMENT = 134;
- public static final int ICMPV6_NEIGHBOR_SOLICITATION = 135;
- public static final int ICMPV6_NEIGHBOR_ADVERTISEMENT = 136;
-
- public static final int ICMPV6_ND_OPTION_MIN_LENGTH = 8;
- public static final int ICMPV6_ND_OPTION_LENGTH_SCALING_FACTOR = 8;
- public static final int ICMPV6_ND_OPTION_SLLA = 1;
- public static final int ICMPV6_ND_OPTION_TLLA = 2;
- public static final int ICMPV6_ND_OPTION_MTU = 5;
-
-
- /**
- * UDP constants.
- *
- * See also:
- * - https://tools.ietf.org/html/rfc768
- */
- public static final int UDP_HEADER_LEN = 8;
-
- /**
- * DHCP(v4) constants.
- *
- * See also:
- * - https://tools.ietf.org/html/rfc2131
- */
- public static final int DHCP4_SERVER_PORT = 67;
- public static final int DHCP4_CLIENT_PORT = 68;
/**
* DNS constants.
@@ -176,9 +89,4 @@
* Utility functions.
*/
public static byte asByte(int i) { return (byte) i; }
-
- public static String asString(int i) { return Integer.toString(i); }
-
- public static int asUint(byte b) { return (b & 0xff); }
- public static int asUint(short s) { return (s & 0xffff); }
}
diff --git a/services/net/java/android/net/util/PacketReader.java b/services/net/java/android/net/util/PacketReader.java
deleted file mode 100644
index 4aec6b6..0000000
--- a/services/net/java/android/net/util/PacketReader.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2018 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.util;
-
-import static java.lang.Math.max;
-
-import android.os.Handler;
-import android.system.Os;
-
-import java.io.FileDescriptor;
-
-/**
- * Specialization of {@link FdEventsReader} that reads packets into a byte array.
- *
- * TODO: rename this class to something more correctly descriptive (something
- * like [or less horrible than] FdReadEventsHandler?).
- *
- * @hide
- */
-public abstract class PacketReader extends FdEventsReader<byte[]> {
-
- public static final int DEFAULT_RECV_BUF_SIZE = 2 * 1024;
-
- protected PacketReader(Handler h) {
- this(h, DEFAULT_RECV_BUF_SIZE);
- }
-
- protected PacketReader(Handler h, int recvBufSize) {
- super(h, new byte[max(recvBufSize, DEFAULT_RECV_BUF_SIZE)]);
- }
-
- @Override
- protected final int recvBufSize(byte[] buffer) {
- return buffer.length;
- }
-
- /**
- * Subclasses MAY override this to change the default read() implementation
- * in favour of, say, recvfrom().
- *
- * Implementations MUST return the bytes read or throw an Exception.
- */
- @Override
- protected int readPacket(FileDescriptor fd, byte[] packetBuffer) throws Exception {
- return Os.read(fd, packetBuffer, 0, packetBuffer.length);
- }
-}