blob: 0b7809e9f614ff7c40b2b6762f7eb4767a174e83 [file] [log] [blame]
Lorenzo Colittia071edc2015-03-10 01:38:38 +09001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.net.dhcp;
18
Remi NGUYEN VAN7b84fb32019-01-19 21:13:24 +090019import static android.net.dhcp.DhcpPacket.DHCP_BROADCAST_ADDRESS;
20import static android.net.dhcp.DhcpPacket.DHCP_DNS_SERVER;
21import static android.net.dhcp.DhcpPacket.DHCP_DOMAIN_NAME;
22import static android.net.dhcp.DhcpPacket.DHCP_LEASE_TIME;
23import static android.net.dhcp.DhcpPacket.DHCP_MTU;
24import static android.net.dhcp.DhcpPacket.DHCP_REBINDING_TIME;
25import static android.net.dhcp.DhcpPacket.DHCP_RENEWAL_TIME;
26import static android.net.dhcp.DhcpPacket.DHCP_ROUTER;
27import static android.net.dhcp.DhcpPacket.DHCP_SUBNET_MASK;
28import static android.net.dhcp.DhcpPacket.DHCP_VENDOR_INFO;
29import static android.net.dhcp.DhcpPacket.INADDR_ANY;
30import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST;
Remi NGUYEN VAN231b52b2019-01-29 15:38:52 +090031import static android.net.util.NetworkStackUtils.closeSocketQuietly;
Remi NGUYEN VAN811f6382019-01-20 12:08:21 +090032import static android.net.util.SocketUtils.makePacketSocketAddress;
Remi NGUYEN VAN7b84fb32019-01-19 21:13:24 +090033import static android.system.OsConstants.AF_INET;
34import static android.system.OsConstants.AF_PACKET;
35import static android.system.OsConstants.ETH_P_IP;
36import static android.system.OsConstants.IPPROTO_UDP;
37import static android.system.OsConstants.SOCK_DGRAM;
38import static android.system.OsConstants.SOCK_RAW;
39import static android.system.OsConstants.SOL_SOCKET;
Remi NGUYEN VAN7b84fb32019-01-19 21:13:24 +090040import static android.system.OsConstants.SO_BROADCAST;
41import static android.system.OsConstants.SO_RCVBUF;
42import static android.system.OsConstants.SO_REUSEADDR;
Lorenzo Colittia071edc2015-03-10 01:38:38 +090043
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +090044import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ANY;
45
Lorenzo Colittia071edc2015-03-10 01:38:38 +090046import android.content.Context;
Erik Kline29a21522016-02-17 21:44:25 +090047import android.net.DhcpResults;
Jeff Sharkey619a5112017-01-19 11:55:54 -070048import android.net.TrafficStats;
Remi NGUYEN VANc1c02dc2019-01-20 16:50:42 +090049import android.net.ip.IpClient;
Pierre Imai55618be2016-02-19 15:25:54 +090050import android.net.metrics.DhcpClientEvent;
Hugo Benichi2677b192016-04-18 09:04:04 +090051import android.net.metrics.DhcpErrorEvent;
Remi NGUYEN VAN7b84fb32019-01-19 21:13:24 +090052import android.net.metrics.IpConnectivityLog;
Erik Kline8bd00d52017-12-08 17:47:50 +090053import android.net.util.InterfaceParams;
Remi NGUYEN VAN811f6382019-01-20 12:08:21 +090054import android.net.util.SocketUtils;
Lorenzo Colittia071edc2015-03-10 01:38:38 +090055import android.os.Message;
Lorenzo Colittia071edc2015-03-10 01:38:38 +090056import android.os.SystemClock;
57import android.system.ErrnoException;
58import android.system.Os;
Hugo Benichia0289892016-10-18 13:09:04 +090059import android.util.EventLog;
Lorenzo Colittia071edc2015-03-10 01:38:38 +090060import android.util.Log;
Lorenzo Colittid2457a32016-02-18 00:32:44 +090061import android.util.SparseArray;
Lorenzo Colittia071edc2015-03-10 01:38:38 +090062
Remi NGUYEN VAN7b84fb32019-01-19 21:13:24 +090063import com.android.internal.util.HexDump;
64import com.android.internal.util.MessageUtils;
Remi NGUYEN VAN7b84fb32019-01-19 21:13:24 +090065import com.android.internal.util.State;
66import com.android.internal.util.StateMachine;
67import com.android.internal.util.WakeupMessage;
68
Lorenzo Colittia071edc2015-03-10 01:38:38 +090069import java.io.FileDescriptor;
70import java.io.IOException;
Lorenzo Colittia071edc2015-03-10 01:38:38 +090071import java.net.Inet4Address;
Remi NGUYEN VAN811f6382019-01-20 12:08:21 +090072import java.net.SocketAddress;
Lorenzo Colittia071edc2015-03-10 01:38:38 +090073import java.net.SocketException;
Lorenzo Colittia071edc2015-03-10 01:38:38 +090074import java.nio.ByteBuffer;
75import java.util.Arrays;
76import java.util.Random;
77
Lorenzo Colittia071edc2015-03-10 01:38:38 +090078/**
79 * A DHCPv4 client.
80 *
81 * Written to behave similarly to the DhcpStateMachine + dhcpcd 5.5.6 combination used in Android
82 * 5.1 and below, as configured on Nexus 6. The interface is the same as DhcpStateMachine.
83 *
84 * TODO:
85 *
86 * - Exponential backoff when receiving NAKs (not specified by the RFC, but current behaviour).
87 * - Support persisting lease state and support INIT-REBOOT. Android 5.1 does this, but it does not
88 * do so correctly: instead of requesting the lease last obtained on a particular network (e.g., a
89 * given SSID), it requests the last-leased IP address on the same interface, causing a delay if
90 * the server NAKs or a timeout if it doesn't.
91 *
92 * Known differences from current behaviour:
93 *
94 * - Does not request the "static routes" option.
95 * - Does not support BOOTP servers. DHCP has been around since 1993, should be everywhere now.
96 * - Requests the "broadcast" option, but does nothing with it.
97 * - Rejects invalid subnet masks such as 255.255.255.1 (current code treats that as 255.255.255.0).
98 *
99 * @hide
100 */
Erik Klineefa42092016-02-18 12:40:11 +0900101public class DhcpClient extends StateMachine {
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900102
103 private static final String TAG = "DhcpClient";
Lorenzo Colittie67ec4e2016-05-27 13:59:57 +0900104 private static final boolean DBG = true;
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900105 private static final boolean STATE_DBG = false;
106 private static final boolean MSG_DBG = false;
Joe Onoratof188d412016-02-01 17:49:10 -0800107 private static final boolean PACKET_DBG = false;
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900108
Remi NGUYEN VAN231b52b2019-01-29 15:38:52 +0900109 // Metrics events: must be kept in sync with server-side aggregation code.
110 /** Represents transitions from DhcpInitState to DhcpBoundState */
111 private static final String EVENT_INITIAL_BOUND = "InitialBoundState";
112 /** Represents transitions from and to DhcpBoundState via DhcpRenewingState */
113 private static final String EVENT_RENEWING_BOUND = "RenewingBoundState";
114
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900115 // Timers and timeouts.
116 private static final int SECONDS = 1000;
117 private static final int FIRST_TIMEOUT_MS = 2 * SECONDS;
118 private static final int MAX_TIMEOUT_MS = 128 * SECONDS;
119
120 // This is not strictly needed, since the client is asynchronous and implements exponential
121 // backoff. It's maintained for backwards compatibility with the previous DHCP code, which was
122 // a blocking operation with a 30-second timeout. We pick 36 seconds so we can send packets at
123 // t=0, t=2, t=6, t=14, t=30, allowing for 10% jitter.
124 private static final int DHCP_TIMEOUT_MS = 36 * SECONDS;
125
Remi NGUYEN VANc1c02dc2019-01-20 16:50:42 +0900126 // DhcpClient uses IpClient's handler.
127 private static final int PUBLIC_BASE = IpClient.DHCPCLIENT_CMD_BASE;
Erik Kline09bf1352016-02-12 00:17:02 +0900128
129 /* Commands from controller to start/stop DHCP */
130 public static final int CMD_START_DHCP = PUBLIC_BASE + 1;
131 public static final int CMD_STOP_DHCP = PUBLIC_BASE + 2;
Erik Kline09bf1352016-02-12 00:17:02 +0900132
133 /* Notification from DHCP state machine prior to DHCP discovery/renewal */
Erik Kline741b15d2016-04-26 11:48:09 +0900134 public static final int CMD_PRE_DHCP_ACTION = PUBLIC_BASE + 3;
Erik Kline09bf1352016-02-12 00:17:02 +0900135 /* Notification from DHCP state machine post DHCP discovery/renewal. Indicates
136 * success/failure */
Erik Kline741b15d2016-04-26 11:48:09 +0900137 public static final int CMD_POST_DHCP_ACTION = PUBLIC_BASE + 4;
Erik Kline09bf1352016-02-12 00:17:02 +0900138 /* Notification from DHCP state machine before quitting */
Erik Kline741b15d2016-04-26 11:48:09 +0900139 public static final int CMD_ON_QUIT = PUBLIC_BASE + 5;
Erik Kline09bf1352016-02-12 00:17:02 +0900140
141 /* Command from controller to indicate DHCP discovery/renewal can continue
142 * after pre DHCP action is complete */
Erik Kline741b15d2016-04-26 11:48:09 +0900143 public static final int CMD_PRE_DHCP_ACTION_COMPLETE = PUBLIC_BASE + 6;
Erik Kline09bf1352016-02-12 00:17:02 +0900144
Erik Kline5b766232016-04-06 11:18:10 +0900145 /* Command and event notification to/from IpManager requesting the setting
146 * (or clearing) of an IPv4 LinkAddress.
147 */
Erik Kline741b15d2016-04-26 11:48:09 +0900148 public static final int CMD_CLEAR_LINKADDRESS = PUBLIC_BASE + 7;
149 public static final int CMD_CONFIGURE_LINKADDRESS = PUBLIC_BASE + 8;
150 public static final int EVENT_LINKADDRESS_CONFIGURED = PUBLIC_BASE + 9;
Erik Kline5b766232016-04-06 11:18:10 +0900151
Erik Klined8e15922016-04-26 18:41:09 +0900152 /* Message.arg1 arguments to CMD_POST_DHCP_ACTION notification */
Erik Kline09bf1352016-02-12 00:17:02 +0900153 public static final int DHCP_SUCCESS = 1;
154 public static final int DHCP_FAILURE = 2;
155
Erik Klined8e15922016-04-26 18:41:09 +0900156 // Internal messages.
Remi NGUYEN VANc1c02dc2019-01-20 16:50:42 +0900157 private static final int PRIVATE_BASE = IpClient.DHCPCLIENT_CMD_BASE + 100;
Erik Kline09bf1352016-02-12 00:17:02 +0900158 private static final int CMD_KICK = PRIVATE_BASE + 1;
159 private static final int CMD_RECEIVED_PACKET = PRIVATE_BASE + 2;
160 private static final int CMD_TIMEOUT = PRIVATE_BASE + 3;
Erik Kline741b15d2016-04-26 11:48:09 +0900161 private static final int CMD_RENEW_DHCP = PRIVATE_BASE + 4;
Erik Klined8e15922016-04-26 18:41:09 +0900162 private static final int CMD_REBIND_DHCP = PRIVATE_BASE + 5;
163 private static final int CMD_EXPIRE_DHCP = PRIVATE_BASE + 6;
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900164
Lorenzo Colittid2457a32016-02-18 00:32:44 +0900165 // For message logging.
166 private static final Class[] sMessageClasses = { DhcpClient.class };
167 private static final SparseArray<String> sMessageNames =
168 MessageUtils.findMessageNames(sMessageClasses);
169
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900170 // DHCP parameters that we request.
Erik Kline1d511ab2016-03-24 17:30:13 +0900171 /* package */ static final byte[] REQUESTED_PARAMS = new byte[] {
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900172 DHCP_SUBNET_MASK,
173 DHCP_ROUTER,
174 DHCP_DNS_SERVER,
175 DHCP_DOMAIN_NAME,
176 DHCP_MTU,
177 DHCP_BROADCAST_ADDRESS, // TODO: currently ignored.
178 DHCP_LEASE_TIME,
179 DHCP_RENEWAL_TIME,
180 DHCP_REBINDING_TIME,
Erik Kline1d511ab2016-03-24 17:30:13 +0900181 DHCP_VENDOR_INFO,
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900182 };
183
184 // DHCP flag that means "yes, we support unicast."
185 private static final boolean DO_UNICAST = false;
186
187 // System services / libraries we use.
188 private final Context mContext;
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900189 private final Random mRandom;
Hugo Benichicfddd682016-05-31 16:28:06 +0900190 private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900191
192 // Sockets.
193 // - We use a packet socket to receive, because servers send us packets bound for IP addresses
194 // which we have not yet configured, and the kernel protocol stack drops these.
195 // - We use a UDP socket to send, so the kernel handles ARP and routing for us (DHCP servers can
196 // be off-link as well as on-link).
197 private FileDescriptor mPacketSock;
198 private FileDescriptor mUdpSock;
199 private ReceiveThread mReceiveThread;
200
201 // State variables.
202 private final StateMachine mController;
Lorenzo Colitti9d3aadb2015-12-02 17:51:28 +0900203 private final WakeupMessage mKickAlarm;
204 private final WakeupMessage mTimeoutAlarm;
205 private final WakeupMessage mRenewAlarm;
Erik Klined8e15922016-04-26 18:41:09 +0900206 private final WakeupMessage mRebindAlarm;
Erik Klineacda32c2016-05-13 17:50:25 +0900207 private final WakeupMessage mExpiryAlarm;
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900208 private final String mIfaceName;
209
210 private boolean mRegisteredForPreDhcpNotification;
Erik Kline8bd00d52017-12-08 17:47:50 +0900211 private InterfaceParams mIface;
212 // TODO: MacAddress-ify more of this class hierarchy.
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900213 private byte[] mHwAddr;
Remi NGUYEN VAN811f6382019-01-20 12:08:21 +0900214 private SocketAddress mInterfaceBroadcastAddr;
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900215 private int mTransactionId;
Lorenzo Colitti3e979322015-04-21 15:22:17 +0900216 private long mTransactionStartMillis;
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900217 private DhcpResults mDhcpLease;
218 private long mDhcpLeaseExpiry;
219 private DhcpResults mOffer;
220
Hugo Benichi76434232016-07-07 11:28:54 +0900221 // Milliseconds SystemClock timestamps used to record transition times to DhcpBoundState.
222 private long mLastInitEnterTime;
223 private long mLastBoundExitTime;
224
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900225 // States.
226 private State mStoppedState = new StoppedState();
227 private State mDhcpState = new DhcpState();
228 private State mDhcpInitState = new DhcpInitState();
229 private State mDhcpSelectingState = new DhcpSelectingState();
230 private State mDhcpRequestingState = new DhcpRequestingState();
Erik Kline5b766232016-04-06 11:18:10 +0900231 private State mDhcpHaveLeaseState = new DhcpHaveLeaseState();
232 private State mConfiguringInterfaceState = new ConfiguringInterfaceState();
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900233 private State mDhcpBoundState = new DhcpBoundState();
234 private State mDhcpRenewingState = new DhcpRenewingState();
235 private State mDhcpRebindingState = new DhcpRebindingState();
236 private State mDhcpInitRebootState = new DhcpInitRebootState();
237 private State mDhcpRebootingState = new DhcpRebootingState();
238 private State mWaitBeforeStartState = new WaitBeforeStartState(mDhcpInitState);
239 private State mWaitBeforeRenewalState = new WaitBeforeRenewalState(mDhcpRenewingState);
240
Lorenzo Colitti9d3aadb2015-12-02 17:51:28 +0900241 private WakeupMessage makeWakeupMessage(String cmdName, int cmd) {
242 cmdName = DhcpClient.class.getSimpleName() + "." + mIfaceName + "." + cmdName;
243 return new WakeupMessage(mContext, getHandler(), cmdName, cmd);
244 }
245
Erik Kline8bd00d52017-12-08 17:47:50 +0900246 // TODO: Take an InterfaceParams instance instead of an interface name String.
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900247 private DhcpClient(Context context, StateMachine controller, String iface) {
Erik Klinebe83aeb2017-06-09 19:20:26 +0900248 super(TAG, controller.getHandler());
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900249
250 mContext = context;
251 mController = controller;
252 mIfaceName = iface;
253
254 addState(mStoppedState);
255 addState(mDhcpState);
256 addState(mDhcpInitState, mDhcpState);
257 addState(mWaitBeforeStartState, mDhcpState);
258 addState(mDhcpSelectingState, mDhcpState);
259 addState(mDhcpRequestingState, mDhcpState);
Erik Kline5b766232016-04-06 11:18:10 +0900260 addState(mDhcpHaveLeaseState, mDhcpState);
261 addState(mConfiguringInterfaceState, mDhcpHaveLeaseState);
262 addState(mDhcpBoundState, mDhcpHaveLeaseState);
263 addState(mWaitBeforeRenewalState, mDhcpHaveLeaseState);
264 addState(mDhcpRenewingState, mDhcpHaveLeaseState);
265 addState(mDhcpRebindingState, mDhcpHaveLeaseState);
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900266 addState(mDhcpInitRebootState, mDhcpState);
267 addState(mDhcpRebootingState, mDhcpState);
268
269 setInitialState(mStoppedState);
270
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900271 mRandom = new Random();
272
Lorenzo Colitti5bc64b82015-06-04 17:52:55 +0900273 // Used to schedule packet retransmissions.
Lorenzo Colitti9d3aadb2015-12-02 17:51:28 +0900274 mKickAlarm = makeWakeupMessage("KICK", CMD_KICK);
Lorenzo Colitti5bc64b82015-06-04 17:52:55 +0900275 // Used to time out PacketRetransmittingStates.
Lorenzo Colitti9d3aadb2015-12-02 17:51:28 +0900276 mTimeoutAlarm = makeWakeupMessage("TIMEOUT", CMD_TIMEOUT);
Erik Klined8e15922016-04-26 18:41:09 +0900277 // Used to schedule DHCP reacquisition.
Erik Kline09bf1352016-02-12 00:17:02 +0900278 mRenewAlarm = makeWakeupMessage("RENEW", CMD_RENEW_DHCP);
Erik Klined8e15922016-04-26 18:41:09 +0900279 mRebindAlarm = makeWakeupMessage("REBIND", CMD_REBIND_DHCP);
Erik Klineacda32c2016-05-13 17:50:25 +0900280 mExpiryAlarm = makeWakeupMessage("EXPIRY", CMD_EXPIRE_DHCP);
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900281 }
282
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900283 public void registerForPreDhcpNotification() {
284 mRegisteredForPreDhcpNotification = true;
285 }
286
Erik Kline29a21522016-02-17 21:44:25 +0900287 public static DhcpClient makeDhcpClient(
Erik Kline8bd00d52017-12-08 17:47:50 +0900288 Context context, StateMachine controller, InterfaceParams ifParams) {
289 DhcpClient client = new DhcpClient(context, controller, ifParams.name);
290 client.mIface = ifParams;
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900291 client.start();
292 return client;
293 }
294
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900295 private boolean initInterface() {
Erik Kline8bd00d52017-12-08 17:47:50 +0900296 if (mIface == null) mIface = InterfaceParams.getByName(mIfaceName);
297 if (mIface == null) {
298 Log.e(TAG, "Can't determine InterfaceParams for " + mIfaceName);
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900299 return false;
300 }
Erik Kline8bd00d52017-12-08 17:47:50 +0900301
302 mHwAddr = mIface.macAddr.toByteArray();
Remi NGUYEN VAN811f6382019-01-20 12:08:21 +0900303 mInterfaceBroadcastAddr = makePacketSocketAddress(mIface.index, DhcpPacket.ETHER_BROADCAST);
Erik Kline8bd00d52017-12-08 17:47:50 +0900304 return true;
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900305 }
306
Lorenzo Colitti3e979322015-04-21 15:22:17 +0900307 private void startNewTransaction() {
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900308 mTransactionId = mRandom.nextInt();
Lorenzo Colitti3e979322015-04-21 15:22:17 +0900309 mTransactionStartMillis = SystemClock.elapsedRealtime();
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900310 }
311
312 private boolean initSockets() {
Erik Klined8e15922016-04-26 18:41:09 +0900313 return initPacketSocket() && initUdpSocket();
314 }
315
316 private boolean initPacketSocket() {
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900317 try {
318 mPacketSock = Os.socket(AF_PACKET, SOCK_RAW, ETH_P_IP);
Remi NGUYEN VAN811f6382019-01-20 12:08:21 +0900319 SocketAddress addr = makePacketSocketAddress((short) ETH_P_IP, mIface.index);
Remi NGUYEN VANb7bda392019-03-06 18:02:34 +0900320 Os.bind(mPacketSock, addr);
Remi NGUYEN VAN231b52b2019-01-29 15:38:52 +0900321 SocketUtils.attachDhcpFilter(mPacketSock);
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900322 } catch(SocketException|ErrnoException e) {
323 Log.e(TAG, "Error creating packet socket", e);
324 return false;
325 }
Erik Klined8e15922016-04-26 18:41:09 +0900326 return true;
327 }
328
329 private boolean initUdpSocket() {
Jeff Sharkey619a5112017-01-19 11:55:54 -0700330 final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_DHCP);
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900331 try {
332 mUdpSock = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
Remi NGUYEN VAN811f6382019-01-20 12:08:21 +0900333 SocketUtils.bindSocketToInterface(mUdpSock, mIfaceName);
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900334 Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_REUSEADDR, 1);
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900335 Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_BROADCAST, 1);
Lorenzo Colitti4e4d59e2015-09-10 18:12:18 +0900336 Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_RCVBUF, 0);
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900337 Os.bind(mUdpSock, IPV4_ADDR_ANY, DhcpPacket.DHCP_CLIENT);
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900338 } catch(SocketException|ErrnoException e) {
339 Log.e(TAG, "Error creating UDP socket", e);
340 return false;
Jeff Sharkey619a5112017-01-19 11:55:54 -0700341 } finally {
342 TrafficStats.setThreadStatsTag(oldTag);
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900343 }
344 return true;
345 }
346
Lorenzo Colitti4e4d59e2015-09-10 18:12:18 +0900347 private boolean connectUdpSock(Inet4Address to) {
348 try {
349 Os.connect(mUdpSock, to, DhcpPacket.DHCP_SERVER);
350 return true;
351 } catch (SocketException|ErrnoException e) {
352 Log.e(TAG, "Error connecting UDP socket", e);
353 return false;
354 }
355 }
356
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900357 private void closeSockets() {
Remi NGUYEN VAN231b52b2019-01-29 15:38:52 +0900358 closeSocketQuietly(mUdpSock);
359 closeSocketQuietly(mPacketSock);
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900360 }
361
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900362 class ReceiveThread extends Thread {
363
364 private final byte[] mPacket = new byte[DhcpPacket.MAX_LENGTH];
Hugo Benichi67530d62016-04-13 10:15:39 +0900365 private volatile boolean mStopped = false;
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900366
367 public void halt() {
Hugo Benichi67530d62016-04-13 10:15:39 +0900368 mStopped = true;
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900369 closeSockets(); // Interrupts the read() call the thread is blocked in.
370 }
371
372 @Override
373 public void run() {
Joe Onoratof188d412016-02-01 17:49:10 -0800374 if (DBG) Log.d(TAG, "Receive thread started");
Hugo Benichi67530d62016-04-13 10:15:39 +0900375 while (!mStopped) {
Erik Kline496906e2015-09-16 15:44:54 +0900376 int length = 0; // Or compiler can't tell it's initialized if a parse error occurs.
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900377 try {
Erik Kline496906e2015-09-16 15:44:54 +0900378 length = Os.read(mPacketSock, mPacket, 0, mPacket.length);
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900379 DhcpPacket packet = null;
380 packet = DhcpPacket.decodeFullPacket(mPacket, length, DhcpPacket.ENCAP_L2);
Joe Onoratof188d412016-02-01 17:49:10 -0800381 if (DBG) Log.d(TAG, "Received packet: " + packet);
Erik Kline496906e2015-09-16 15:44:54 +0900382 sendMessage(CMD_RECEIVED_PACKET, packet);
Lorenzo Colittif5c17222015-03-31 07:04:42 +0900383 } catch (IOException|ErrnoException e) {
Hugo Benichi67530d62016-04-13 10:15:39 +0900384 if (!mStopped) {
Lorenzo Colittif5c17222015-03-31 07:04:42 +0900385 Log.e(TAG, "Read error", e);
Hugo Benichicfddd682016-05-31 16:28:06 +0900386 logError(DhcpErrorEvent.RECEIVE_ERROR);
Lorenzo Colittif5c17222015-03-31 07:04:42 +0900387 }
Erik Kline496906e2015-09-16 15:44:54 +0900388 } catch (DhcpPacket.ParseException e) {
389 Log.e(TAG, "Can't parse packet: " + e.getMessage());
390 if (PACKET_DBG) {
391 Log.d(TAG, HexDump.dumpHexString(mPacket, 0, length));
392 }
Hugo Benichia0289892016-10-18 13:09:04 +0900393 if (e.errorCode == DhcpErrorEvent.DHCP_NO_COOKIE) {
394 int snetTagId = 0x534e4554;
395 String bugId = "31850211";
396 int uid = -1;
397 String data = DhcpPacket.ParseException.class.getName();
398 EventLog.writeEvent(snetTagId, bugId, uid, data);
399 }
Hugo Benichicfddd682016-05-31 16:28:06 +0900400 logError(e.errorCode);
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900401 }
402 }
Joe Onoratof188d412016-02-01 17:49:10 -0800403 if (DBG) Log.d(TAG, "Receive thread stopped");
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900404 }
405 }
406
Lorenzo Colitti3e979322015-04-21 15:22:17 +0900407 private short getSecs() {
408 return (short) ((SystemClock.elapsedRealtime() - mTransactionStartMillis) / 1000);
409 }
410
Erik Klined8e15922016-04-26 18:41:09 +0900411 private boolean transmitPacket(ByteBuffer buf, String description, int encap, Inet4Address to) {
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900412 try {
Erik Klined8e15922016-04-26 18:41:09 +0900413 if (encap == DhcpPacket.ENCAP_L2) {
Joe Onoratof188d412016-02-01 17:49:10 -0800414 if (DBG) Log.d(TAG, "Broadcasting " + description);
Remi NGUYEN VANb7bda392019-03-06 18:02:34 +0900415 Os.sendto(mPacketSock, buf.array(), 0, buf.limit(), 0, mInterfaceBroadcastAddr);
Erik Klined8e15922016-04-26 18:41:09 +0900416 } else if (encap == DhcpPacket.ENCAP_BOOTP && to.equals(INADDR_BROADCAST)) {
417 if (DBG) Log.d(TAG, "Broadcasting " + description);
418 // We only send L3-encapped broadcasts in DhcpRebindingState,
419 // where we have an IP address and an unconnected UDP socket.
420 //
421 // N.B.: We only need this codepath because DhcpRequestPacket
422 // hardcodes the source IP address to 0.0.0.0. We could reuse
423 // the packet socket if this ever changes.
424 Os.sendto(mUdpSock, buf, 0, to, DhcpPacket.DHCP_SERVER);
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900425 } else {
Lorenzo Colitti4e4d59e2015-09-10 18:12:18 +0900426 // It's safe to call getpeername here, because we only send unicast packets if we
Erik Klined8e15922016-04-26 18:41:09 +0900427 // have an IP address, and we connect the UDP socket in DhcpBoundState#enter.
428 if (DBG) Log.d(TAG, String.format("Unicasting %s to %s",
429 description, Os.getpeername(mUdpSock)));
Lorenzo Colitti4e4d59e2015-09-10 18:12:18 +0900430 Os.write(mUdpSock, buf);
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900431 }
432 } catch(ErrnoException|IOException e) {
433 Log.e(TAG, "Can't send packet: ", e);
434 return false;
435 }
436 return true;
437 }
438
439 private boolean sendDiscoverPacket() {
440 ByteBuffer packet = DhcpPacket.buildDiscoverPacket(
Lorenzo Colitti3e979322015-04-21 15:22:17 +0900441 DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr,
442 DO_UNICAST, REQUESTED_PARAMS);
Erik Klined8e15922016-04-26 18:41:09 +0900443 return transmitPacket(packet, "DHCPDISCOVER", DhcpPacket.ENCAP_L2, INADDR_BROADCAST);
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900444 }
445
446 private boolean sendRequestPacket(
447 Inet4Address clientAddress, Inet4Address requestedAddress,
448 Inet4Address serverAddress, Inet4Address to) {
449 // TODO: should we use the transaction ID from the server?
Erik Klined8e15922016-04-26 18:41:09 +0900450 final int encap = INADDR_ANY.equals(clientAddress)
451 ? DhcpPacket.ENCAP_L2 : DhcpPacket.ENCAP_BOOTP;
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900452
453 ByteBuffer packet = DhcpPacket.buildRequestPacket(
Lorenzo Colitti3e979322015-04-21 15:22:17 +0900454 encap, mTransactionId, getSecs(), clientAddress,
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900455 DO_UNICAST, mHwAddr, requestedAddress,
456 serverAddress, REQUESTED_PARAMS, null);
Lorenzo Colitti7611f1c2015-10-29 15:21:54 +0900457 String serverStr = (serverAddress != null) ? serverAddress.getHostAddress() : null;
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900458 String description = "DHCPREQUEST ciaddr=" + clientAddress.getHostAddress() +
459 " request=" + requestedAddress.getHostAddress() +
Lorenzo Colitti7611f1c2015-10-29 15:21:54 +0900460 " serverid=" + serverStr;
Erik Klined8e15922016-04-26 18:41:09 +0900461 return transmitPacket(packet, description, encap, to);
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900462 }
463
Erik Klineacda32c2016-05-13 17:50:25 +0900464 private void scheduleLeaseTimers() {
465 if (mDhcpLeaseExpiry == 0) {
466 Log.d(TAG, "Infinite lease, no timer scheduling needed");
467 return;
Lorenzo Colittid9735372015-06-02 13:15:50 +0900468 }
Erik Klineacda32c2016-05-13 17:50:25 +0900469
470 final long now = SystemClock.elapsedRealtime();
Erik Klineacda32c2016-05-13 17:50:25 +0900471
Erik Klined8e15922016-04-26 18:41:09 +0900472 // TODO: consider getting the renew and rebind timers from T1 and T2.
473 // See also:
474 // https://tools.ietf.org/html/rfc2131#section-4.4.5
475 // https://tools.ietf.org/html/rfc1533#section-9.9
476 // https://tools.ietf.org/html/rfc1533#section-9.10
477 final long remainingDelay = mDhcpLeaseExpiry - now;
478 final long renewDelay = remainingDelay / 2;
479 final long rebindDelay = remainingDelay * 7 / 8;
480 mRenewAlarm.schedule(now + renewDelay);
481 mRebindAlarm.schedule(now + rebindDelay);
482 mExpiryAlarm.schedule(now + remainingDelay);
483 Log.d(TAG, "Scheduling renewal in " + (renewDelay / 1000) + "s");
484 Log.d(TAG, "Scheduling rebind in " + (rebindDelay / 1000) + "s");
485 Log.d(TAG, "Scheduling expiry in " + (remainingDelay / 1000) + "s");
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900486 }
487
Lorenzo Colitti48f9c732015-06-02 16:50:36 +0900488 private void notifySuccess() {
Erik Kline09bf1352016-02-12 00:17:02 +0900489 mController.sendMessage(
490 CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, new DhcpResults(mDhcpLease));
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900491 }
492
493 private void notifyFailure() {
Erik Kline09bf1352016-02-12 00:17:02 +0900494 mController.sendMessage(CMD_POST_DHCP_ACTION, DHCP_FAILURE, 0, null);
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900495 }
496
Erik Klineacda32c2016-05-13 17:50:25 +0900497 private void acceptDhcpResults(DhcpResults results, String msg) {
498 mDhcpLease = results;
499 mOffer = null;
500 Log.d(TAG, msg + " lease: " + mDhcpLease);
501 notifySuccess();
502 }
503
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900504 private void clearDhcpState() {
505 mDhcpLease = null;
506 mDhcpLeaseExpiry = 0;
507 mOffer = null;
508 }
509
510 /**
511 * Quit the DhcpStateMachine.
512 *
513 * @hide
514 */
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900515 public void doQuit() {
516 Log.d(TAG, "doQuit");
517 quit();
518 }
519
Erik Klineefa42092016-02-18 12:40:11 +0900520 @Override
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900521 protected void onQuitting() {
522 Log.d(TAG, "onQuitting");
Erik Kline09bf1352016-02-12 00:17:02 +0900523 mController.sendMessage(CMD_ON_QUIT);
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900524 }
525
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900526 abstract class LoggingState extends State {
Hugo Benichi176ed012016-07-01 10:06:56 +0900527 private long mEnterTimeMs;
528
Erik Kline5b766232016-04-06 11:18:10 +0900529 @Override
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900530 public void enter() {
Hugo Benichi2677b192016-04-18 09:04:04 +0900531 if (STATE_DBG) Log.d(TAG, "Entering state " + getName());
Hugo Benichi176ed012016-07-01 10:06:56 +0900532 mEnterTimeMs = SystemClock.elapsedRealtime();
Hugo Benichi176ed012016-07-01 10:06:56 +0900533 }
534
535 @Override
536 public void exit() {
537 long durationMs = SystemClock.elapsedRealtime() - mEnterTimeMs;
Hugo Benichi76434232016-07-07 11:28:54 +0900538 logState(getName(), (int) durationMs);
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900539 }
540
541 private String messageName(int what) {
Lorenzo Colittid2457a32016-02-18 00:32:44 +0900542 return sMessageNames.get(what, Integer.toString(what));
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900543 }
544
545 private String messageToString(Message message) {
546 long now = SystemClock.uptimeMillis();
Remi NGUYEN VANc1c02dc2019-01-20 16:50:42 +0900547 return new StringBuilder(" ")
548 .append(message.getWhen() - now)
549 .append(messageName(message.what))
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900550 .append(" ").append(message.arg1)
551 .append(" ").append(message.arg2)
Remi NGUYEN VANc1c02dc2019-01-20 16:50:42 +0900552 .append(" ").append(message.obj)
553 .toString();
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900554 }
555
556 @Override
557 public boolean processMessage(Message message) {
558 if (MSG_DBG) {
559 Log.d(TAG, getName() + messageToString(message));
560 }
561 return NOT_HANDLED;
562 }
Hugo Benichi176ed012016-07-01 10:06:56 +0900563
564 @Override
565 public String getName() {
566 // All DhcpClient's states are inner classes with a well defined name.
567 // Use getSimpleName() and avoid super's getName() creating new String instances.
568 return getClass().getSimpleName();
569 }
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900570 }
571
572 // Sends CMD_PRE_DHCP_ACTION to the controller, waits for the controller to respond with
573 // CMD_PRE_DHCP_ACTION_COMPLETE, and then transitions to mOtherState.
574 abstract class WaitBeforeOtherState extends LoggingState {
575 protected State mOtherState;
576
577 @Override
578 public void enter() {
579 super.enter();
Erik Kline09bf1352016-02-12 00:17:02 +0900580 mController.sendMessage(CMD_PRE_DHCP_ACTION);
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900581 }
582
583 @Override
584 public boolean processMessage(Message message) {
585 super.processMessage(message);
586 switch (message.what) {
Erik Kline09bf1352016-02-12 00:17:02 +0900587 case CMD_PRE_DHCP_ACTION_COMPLETE:
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900588 transitionTo(mOtherState);
589 return HANDLED;
590 default:
591 return NOT_HANDLED;
592 }
593 }
594 }
595
Hugo Benichi176ed012016-07-01 10:06:56 +0900596 class StoppedState extends State {
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900597 @Override
598 public boolean processMessage(Message message) {
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900599 switch (message.what) {
Erik Kline09bf1352016-02-12 00:17:02 +0900600 case CMD_START_DHCP:
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900601 if (mRegisteredForPreDhcpNotification) {
602 transitionTo(mWaitBeforeStartState);
603 } else {
604 transitionTo(mDhcpInitState);
605 }
606 return HANDLED;
607 default:
608 return NOT_HANDLED;
609 }
610 }
611 }
612
613 class WaitBeforeStartState extends WaitBeforeOtherState {
614 public WaitBeforeStartState(State otherState) {
615 super();
616 mOtherState = otherState;
617 }
618 }
619
620 class WaitBeforeRenewalState extends WaitBeforeOtherState {
621 public WaitBeforeRenewalState(State otherState) {
622 super();
623 mOtherState = otherState;
624 }
625 }
626
Hugo Benichi176ed012016-07-01 10:06:56 +0900627 class DhcpState extends State {
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900628 @Override
629 public void enter() {
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900630 clearDhcpState();
631 if (initInterface() && initSockets()) {
632 mReceiveThread = new ReceiveThread();
633 mReceiveThread.start();
634 } else {
635 notifyFailure();
636 transitionTo(mStoppedState);
637 }
638 }
639
640 @Override
641 public void exit() {
Lorenzo Colitti31e19f32015-08-11 15:42:59 +0900642 if (mReceiveThread != null) {
643 mReceiveThread.halt(); // Also closes sockets.
644 mReceiveThread = null;
645 }
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900646 clearDhcpState();
647 }
648
649 @Override
650 public boolean processMessage(Message message) {
651 super.processMessage(message);
652 switch (message.what) {
Erik Kline09bf1352016-02-12 00:17:02 +0900653 case CMD_STOP_DHCP:
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900654 transitionTo(mStoppedState);
655 return HANDLED;
656 default:
657 return NOT_HANDLED;
658 }
659 }
660 }
661
662 public boolean isValidPacket(DhcpPacket packet) {
663 // TODO: check checksum.
664 int xid = packet.getTransactionId();
665 if (xid != mTransactionId) {
666 Log.d(TAG, "Unexpected transaction ID " + xid + ", expected " + mTransactionId);
667 return false;
668 }
669 if (!Arrays.equals(packet.getClientMac(), mHwAddr)) {
670 Log.d(TAG, "MAC addr mismatch: got " +
671 HexDump.toHexString(packet.getClientMac()) + ", expected " +
672 HexDump.toHexString(packet.getClientMac()));
673 return false;
674 }
675 return true;
676 }
677
Lorenzo Colittid9735372015-06-02 13:15:50 +0900678 public void setDhcpLeaseExpiry(DhcpPacket packet) {
679 long leaseTimeMillis = packet.getLeaseTimeMillis();
680 mDhcpLeaseExpiry =
681 (leaseTimeMillis > 0) ? SystemClock.elapsedRealtime() + leaseTimeMillis : 0;
682 }
683
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900684 /**
685 * Retransmits packets using jittered exponential backoff with an optional timeout. Packet
Lorenzo Colitti3a40b0d2015-06-03 13:46:12 +0900686 * transmission is triggered by CMD_KICK, which is sent by an AlarmManager alarm. If a subclass
687 * sets mTimeout to a positive value, then timeout() is called by an AlarmManager alarm mTimeout
688 * milliseconds after entering the state. Kicks and timeouts are cancelled when leaving the
689 * state.
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900690 *
691 * Concrete subclasses must implement sendPacket, which is called when the alarm fires and a
692 * packet needs to be transmitted, and receivePacket, which is triggered by CMD_RECEIVED_PACKET
Lorenzo Colitti5bc64b82015-06-04 17:52:55 +0900693 * sent by the receive thread. They may also set mTimeout and implement timeout.
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900694 */
695 abstract class PacketRetransmittingState extends LoggingState {
696
697 private int mTimer;
698 protected int mTimeout = 0;
699
700 @Override
701 public void enter() {
702 super.enter();
703 initTimer();
704 maybeInitTimeout();
705 sendMessage(CMD_KICK);
706 }
707
708 @Override
709 public boolean processMessage(Message message) {
710 super.processMessage(message);
711 switch (message.what) {
712 case CMD_KICK:
713 sendPacket();
714 scheduleKick();
715 return HANDLED;
716 case CMD_RECEIVED_PACKET:
717 receivePacket((DhcpPacket) message.obj);
718 return HANDLED;
719 case CMD_TIMEOUT:
720 timeout();
721 return HANDLED;
722 default:
723 return NOT_HANDLED;
724 }
725 }
726
Hugo Benichi176ed012016-07-01 10:06:56 +0900727 @Override
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900728 public void exit() {
Hugo Benichi176ed012016-07-01 10:06:56 +0900729 super.exit();
Lorenzo Colitti831204c2015-11-22 18:37:16 +0900730 mKickAlarm.cancel();
731 mTimeoutAlarm.cancel();
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900732 }
733
734 abstract protected boolean sendPacket();
735 abstract protected void receivePacket(DhcpPacket packet);
Lorenzo Colitti5bc64b82015-06-04 17:52:55 +0900736 protected void timeout() {}
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900737
738 protected void initTimer() {
739 mTimer = FIRST_TIMEOUT_MS;
740 }
741
742 protected int jitterTimer(int baseTimer) {
743 int maxJitter = baseTimer / 10;
744 int jitter = mRandom.nextInt(2 * maxJitter) - maxJitter;
745 return baseTimer + jitter;
746 }
747
748 protected void scheduleKick() {
749 long now = SystemClock.elapsedRealtime();
750 long timeout = jitterTimer(mTimer);
751 long alarmTime = now + timeout;
Lorenzo Colitti9d3aadb2015-12-02 17:51:28 +0900752 mKickAlarm.schedule(alarmTime);
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900753 mTimer *= 2;
754 if (mTimer > MAX_TIMEOUT_MS) {
755 mTimer = MAX_TIMEOUT_MS;
756 }
757 }
758
759 protected void maybeInitTimeout() {
760 if (mTimeout > 0) {
761 long alarmTime = SystemClock.elapsedRealtime() + mTimeout;
Lorenzo Colitti9d3aadb2015-12-02 17:51:28 +0900762 mTimeoutAlarm.schedule(alarmTime);
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900763 }
764 }
765 }
766
767 class DhcpInitState extends PacketRetransmittingState {
768 public DhcpInitState() {
769 super();
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900770 }
771
772 @Override
773 public void enter() {
774 super.enter();
Lorenzo Colitti3e979322015-04-21 15:22:17 +0900775 startNewTransaction();
Hugo Benichi76434232016-07-07 11:28:54 +0900776 mLastInitEnterTime = SystemClock.elapsedRealtime();
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900777 }
778
779 protected boolean sendPacket() {
780 return sendDiscoverPacket();
781 }
782
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900783 protected void receivePacket(DhcpPacket packet) {
784 if (!isValidPacket(packet)) return;
785 if (!(packet instanceof DhcpOfferPacket)) return;
786 mOffer = packet.toDhcpResults();
787 if (mOffer != null) {
788 Log.d(TAG, "Got pending lease: " + mOffer);
789 transitionTo(mDhcpRequestingState);
790 }
791 }
792 }
793
794 // Not implemented. We request the first offer we receive.
795 class DhcpSelectingState extends LoggingState {
796 }
797
798 class DhcpRequestingState extends PacketRetransmittingState {
799 public DhcpRequestingState() {
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900800 mTimeout = DHCP_TIMEOUT_MS / 2;
801 }
802
803 protected boolean sendPacket() {
804 return sendRequestPacket(
805 INADDR_ANY, // ciaddr
806 (Inet4Address) mOffer.ipAddress.getAddress(), // DHCP_REQUESTED_IP
807 (Inet4Address) mOffer.serverAddress, // DHCP_SERVER_IDENTIFIER
808 INADDR_BROADCAST); // packet destination address
809 }
810
811 protected void receivePacket(DhcpPacket packet) {
812 if (!isValidPacket(packet)) return;
813 if ((packet instanceof DhcpAckPacket)) {
814 DhcpResults results = packet.toDhcpResults();
815 if (results != null) {
Lorenzo Colittid9735372015-06-02 13:15:50 +0900816 setDhcpLeaseExpiry(packet);
Erik Klineacda32c2016-05-13 17:50:25 +0900817 acceptDhcpResults(results, "Confirmed");
Erik Kline5b766232016-04-06 11:18:10 +0900818 transitionTo(mConfiguringInterfaceState);
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900819 }
820 } else if (packet instanceof DhcpNakPacket) {
Lorenzo Colitti4e4d59e2015-09-10 18:12:18 +0900821 // TODO: Wait a while before returning into INIT state.
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900822 Log.d(TAG, "Received NAK, returning to INIT");
823 mOffer = null;
824 transitionTo(mDhcpInitState);
825 }
826 }
Lorenzo Colitti5bc64b82015-06-04 17:52:55 +0900827
828 @Override
829 protected void timeout() {
830 // After sending REQUESTs unsuccessfully for a while, go back to init.
831 transitionTo(mDhcpInitState);
832 }
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900833 }
834
Hugo Benichi176ed012016-07-01 10:06:56 +0900835 class DhcpHaveLeaseState extends State {
Lorenzo Colitti48f9c732015-06-02 16:50:36 +0900836 @Override
Erik Klineacda32c2016-05-13 17:50:25 +0900837 public boolean processMessage(Message message) {
Erik Klineacda32c2016-05-13 17:50:25 +0900838 switch (message.what) {
839 case CMD_EXPIRE_DHCP:
840 Log.d(TAG, "Lease expired!");
841 notifyFailure();
842 transitionTo(mDhcpInitState);
843 return HANDLED;
844 default:
845 return NOT_HANDLED;
846 }
847 }
848
849 @Override
Lorenzo Colitti48f9c732015-06-02 16:50:36 +0900850 public void exit() {
Erik Klined8e15922016-04-26 18:41:09 +0900851 // Clear any extant alarms.
852 mRenewAlarm.cancel();
853 mRebindAlarm.cancel();
Erik Klineacda32c2016-05-13 17:50:25 +0900854 mExpiryAlarm.cancel();
Erik Klined8e15922016-04-26 18:41:09 +0900855 clearDhcpState();
Erik Kline5b766232016-04-06 11:18:10 +0900856 // Tell IpManager to clear the IPv4 address. There is no need to
857 // wait for confirmation since any subsequent packets are sent from
858 // INADDR_ANY anyway (DISCOVER, REQUEST).
859 mController.sendMessage(CMD_CLEAR_LINKADDRESS);
860 }
861 }
862
863 class ConfiguringInterfaceState extends LoggingState {
864 @Override
865 public void enter() {
866 super.enter();
867 mController.sendMessage(CMD_CONFIGURE_LINKADDRESS, mDhcpLease.ipAddress);
868 }
869
870 @Override
871 public boolean processMessage(Message message) {
872 super.processMessage(message);
873 switch (message.what) {
874 case EVENT_LINKADDRESS_CONFIGURED:
Erik Klined8e15922016-04-26 18:41:09 +0900875 transitionTo(mDhcpBoundState);
Erik Kline5b766232016-04-06 11:18:10 +0900876 return HANDLED;
877 default:
878 return NOT_HANDLED;
879 }
Lorenzo Colitti48f9c732015-06-02 16:50:36 +0900880 }
881 }
882
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900883 class DhcpBoundState extends LoggingState {
884 @Override
885 public void enter() {
886 super.enter();
Erik Klined8e15922016-04-26 18:41:09 +0900887 if (mDhcpLease.serverAddress != null && !connectUdpSock(mDhcpLease.serverAddress)) {
888 // There's likely no point in going into DhcpInitState here, we'll probably
889 // just repeat the transaction, get the same IP address as before, and fail.
890 //
891 // NOTE: It is observed that connectUdpSock() basically never fails, due to
892 // SO_BINDTODEVICE. Examining the local socket address shows it will happily
893 // return an IPv4 address from another interface, or even return "0.0.0.0".
894 //
895 // TODO: Consider deleting this check, following testing on several kernels.
896 notifyFailure();
897 transitionTo(mStoppedState);
898 }
899
Erik Klineacda32c2016-05-13 17:50:25 +0900900 scheduleLeaseTimers();
Hugo Benichi76434232016-07-07 11:28:54 +0900901 logTimeToBoundState();
902 }
903
904 @Override
905 public void exit() {
906 super.exit();
907 mLastBoundExitTime = SystemClock.elapsedRealtime();
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900908 }
909
910 @Override
911 public boolean processMessage(Message message) {
912 super.processMessage(message);
913 switch (message.what) {
Erik Kline09bf1352016-02-12 00:17:02 +0900914 case CMD_RENEW_DHCP:
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900915 if (mRegisteredForPreDhcpNotification) {
916 transitionTo(mWaitBeforeRenewalState);
917 } else {
918 transitionTo(mDhcpRenewingState);
919 }
920 return HANDLED;
921 default:
922 return NOT_HANDLED;
923 }
924 }
Hugo Benichi76434232016-07-07 11:28:54 +0900925
926 private void logTimeToBoundState() {
927 long now = SystemClock.elapsedRealtime();
928 if (mLastBoundExitTime > mLastInitEnterTime) {
Remi NGUYEN VAN231b52b2019-01-29 15:38:52 +0900929 logState(EVENT_RENEWING_BOUND, (int) (now - mLastBoundExitTime));
Hugo Benichi76434232016-07-07 11:28:54 +0900930 } else {
Remi NGUYEN VAN231b52b2019-01-29 15:38:52 +0900931 logState(EVENT_INITIAL_BOUND, (int) (now - mLastInitEnterTime));
Hugo Benichi76434232016-07-07 11:28:54 +0900932 }
933 }
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900934 }
935
Erik Klined8e15922016-04-26 18:41:09 +0900936 abstract class DhcpReacquiringState extends PacketRetransmittingState {
937 protected String mLeaseMsg;
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900938
939 @Override
940 public void enter() {
941 super.enter();
Lorenzo Colitti3e979322015-04-21 15:22:17 +0900942 startNewTransaction();
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900943 }
944
Erik Klined8e15922016-04-26 18:41:09 +0900945 abstract protected Inet4Address packetDestination();
946
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900947 protected boolean sendPacket() {
948 return sendRequestPacket(
949 (Inet4Address) mDhcpLease.ipAddress.getAddress(), // ciaddr
950 INADDR_ANY, // DHCP_REQUESTED_IP
Lorenzo Colitti7611f1c2015-10-29 15:21:54 +0900951 null, // DHCP_SERVER_IDENTIFIER
Erik Klined8e15922016-04-26 18:41:09 +0900952 packetDestination()); // packet destination address
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900953 }
954
955 protected void receivePacket(DhcpPacket packet) {
956 if (!isValidPacket(packet)) return;
957 if ((packet instanceof DhcpAckPacket)) {
Erik Klineacda32c2016-05-13 17:50:25 +0900958 final DhcpResults results = packet.toDhcpResults();
959 if (results != null) {
960 if (!mDhcpLease.ipAddress.equals(results.ipAddress)) {
961 Log.d(TAG, "Renewed lease not for our current IP address!");
962 notifyFailure();
963 transitionTo(mDhcpInitState);
964 }
965 setDhcpLeaseExpiry(packet);
966 // Updating our notion of DhcpResults here only causes the
967 // DNS servers and routes to be updated in LinkProperties
968 // in IpManager and by any overridden relevant handlers of
969 // the registered IpManager.Callback. IP address changes
970 // are not supported here.
Erik Klined8e15922016-04-26 18:41:09 +0900971 acceptDhcpResults(results, mLeaseMsg);
Erik Klineacda32c2016-05-13 17:50:25 +0900972 transitionTo(mDhcpBoundState);
973 }
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900974 } else if (packet instanceof DhcpNakPacket) {
Erik Klineacda32c2016-05-13 17:50:25 +0900975 Log.d(TAG, "Received NAK, returning to INIT");
976 notifyFailure();
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900977 transitionTo(mDhcpInitState);
978 }
979 }
Lorenzo Colittia071edc2015-03-10 01:38:38 +0900980 }
981
Erik Klined8e15922016-04-26 18:41:09 +0900982 class DhcpRenewingState extends DhcpReacquiringState {
983 public DhcpRenewingState() {
984 mLeaseMsg = "Renewed";
985 }
986
987 @Override
988 public boolean processMessage(Message message) {
989 if (super.processMessage(message) == HANDLED) {
990 return HANDLED;
991 }
992
993 switch (message.what) {
994 case CMD_REBIND_DHCP:
995 transitionTo(mDhcpRebindingState);
996 return HANDLED;
997 default:
998 return NOT_HANDLED;
999 }
1000 }
1001
1002 @Override
1003 protected Inet4Address packetDestination() {
1004 // Not specifying a SERVER_IDENTIFIER option is a violation of RFC 2131, but...
1005 // http://b/25343517 . Try to make things work anyway by using broadcast renews.
1006 return (mDhcpLease.serverAddress != null) ?
1007 mDhcpLease.serverAddress : INADDR_BROADCAST;
1008 }
1009 }
1010
1011 class DhcpRebindingState extends DhcpReacquiringState {
1012 public DhcpRebindingState() {
1013 mLeaseMsg = "Rebound";
1014 }
1015
1016 @Override
1017 public void enter() {
1018 super.enter();
1019
1020 // We need to broadcast and possibly reconnect the socket to a
1021 // completely different server.
Remi NGUYEN VAN231b52b2019-01-29 15:38:52 +09001022 closeSocketQuietly(mUdpSock);
Erik Klined8e15922016-04-26 18:41:09 +09001023 if (!initUdpSocket()) {
1024 Log.e(TAG, "Failed to recreate UDP socket");
1025 transitionTo(mDhcpInitState);
1026 }
1027 }
1028
1029 @Override
1030 protected Inet4Address packetDestination() {
1031 return INADDR_BROADCAST;
1032 }
Lorenzo Colittia071edc2015-03-10 01:38:38 +09001033 }
1034
1035 class DhcpInitRebootState extends LoggingState {
1036 }
1037
1038 class DhcpRebootingState extends LoggingState {
1039 }
Hugo Benichicfddd682016-05-31 16:28:06 +09001040
1041 private void logError(int errorCode) {
Hugo Benichi948a8592017-03-16 16:33:47 +09001042 mMetricsLog.log(mIfaceName, new DhcpErrorEvent(errorCode));
Hugo Benichicfddd682016-05-31 16:28:06 +09001043 }
Hugo Benichi76434232016-07-07 11:28:54 +09001044
1045 private void logState(String name, int durationMs) {
Remi NGUYEN VAN7b84fb32019-01-19 21:13:24 +09001046 final DhcpClientEvent event = new DhcpClientEvent.Builder()
1047 .setMsg(name)
1048 .setDurationMs(durationMs)
1049 .build();
1050 mMetricsLog.log(mIfaceName, event);
Hugo Benichi76434232016-07-07 11:28:54 +09001051 }
Lorenzo Colittia071edc2015-03-10 01:38:38 +09001052}