blob: d848ea8a630eebb0d41f7bc246b455730747007e [file] [log] [blame]
markchien74a4fa92019-09-09 20:50:49 +08001/*
2 * Copyright (C) 2016 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.ip;
18
markchien74a4fa92019-09-09 20:50:49 +080019import static android.system.OsConstants.AF_INET6;
20import static android.system.OsConstants.IPPROTO_ICMPV6;
21import static android.system.OsConstants.SOCK_RAW;
22import static android.system.OsConstants.SOL_SOCKET;
markchien74a4fa92019-09-09 20:50:49 +080023import static android.system.OsConstants.SO_SNDTIMEO;
24
Xiao Ma3e557d72021-03-04 06:04:28 +000025import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_SLLA;
26import static com.android.net.module.util.NetworkStackConstants.ICMPV6_RA_HEADER_LEN;
27import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT;
28import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ROUTER_SOLICITATION;
29import static com.android.net.module.util.NetworkStackConstants.IPV6_MIN_MTU;
30import static com.android.net.module.util.NetworkStackConstants.PIO_FLAG_AUTONOMOUS;
31import static com.android.net.module.util.NetworkStackConstants.PIO_FLAG_ON_LINK;
Xiao Ma1f993302023-08-09 18:55:49 +090032import static com.android.net.module.util.NetworkStackConstants.RFC7421_PREFIX_LENGTH;
Xiao Ma3e557d72021-03-04 06:04:28 +000033import static com.android.net.module.util.NetworkStackConstants.TAG_SYSTEM_NEIGHBOR;
markchiend02f9af2021-11-04 11:26:03 +080034import static com.android.networkstack.tethering.util.TetheringUtils.getAllNodesForScopeId;
Xiao Ma3e557d72021-03-04 06:04:28 +000035
markchien74a4fa92019-09-09 20:50:49 +080036import android.net.IpPrefix;
37import android.net.LinkAddress;
Xiao Ma3e557d72021-03-04 06:04:28 +000038import android.net.MacAddress;
markchien74a4fa92019-09-09 20:50:49 +080039import android.net.TrafficStats;
markchien6cf0e552019-12-06 15:24:53 +080040import android.net.util.SocketUtils;
markchien74a4fa92019-09-09 20:50:49 +080041import android.system.ErrnoException;
42import android.system.Os;
43import android.system.StructTimeval;
KH Shiff1400b2023-11-08 14:50:15 +000044import android.util.ArraySet;
markchien74a4fa92019-09-09 20:50:49 +080045import android.util.Log;
46
47import com.android.internal.annotations.GuardedBy;
Patrick Rohr9f371f02022-03-04 15:14:27 +010048import com.android.net.module.util.InterfaceParams;
Xiao Ma3e557d72021-03-04 06:04:28 +000049import com.android.net.module.util.structs.Icmpv6Header;
50import com.android.net.module.util.structs.LlaOption;
51import com.android.net.module.util.structs.MtuOption;
52import com.android.net.module.util.structs.PrefixInformationOption;
53import com.android.net.module.util.structs.RaHeader;
54import com.android.net.module.util.structs.RdnssOption;
markchiend02f9af2021-11-04 11:26:03 +080055import com.android.networkstack.tethering.util.TetheringUtils;
markchien74a4fa92019-09-09 20:50:49 +080056
markchien74a4fa92019-09-09 20:50:49 +080057import java.io.FileDescriptor;
58import java.io.IOException;
59import java.net.Inet6Address;
60import java.net.InetAddress;
61import java.net.InetSocketAddress;
62import java.net.SocketException;
markchien74a4fa92019-09-09 20:50:49 +080063import java.nio.BufferOverflowException;
64import java.nio.ByteBuffer;
65import java.nio.ByteOrder;
66import java.util.HashMap;
67import java.util.HashSet;
68import java.util.Iterator;
69import java.util.Map;
70import java.util.Random;
71import java.util.Set;
72import java.util.concurrent.atomic.AtomicInteger;
73
74
75/**
76 * Basic IPv6 Router Advertisement Daemon.
77 *
78 * TODO:
79 *
80 * - Rewrite using Handler (and friends) so that AlarmManager can deliver
81 * "kick" messages when it's time to send a multicast RA.
82 *
83 * @hide
84 */
85public class RouterAdvertisementDaemon {
86 private static final String TAG = RouterAdvertisementDaemon.class.getSimpleName();
markchien74a4fa92019-09-09 20:50:49 +080087
88 // Summary of various timers and lifetimes.
89 private static final int MIN_RTR_ADV_INTERVAL_SEC = 300;
90 private static final int MAX_RTR_ADV_INTERVAL_SEC = 600;
91 // In general, router, prefix, and DNS lifetimes are all advised to be
Maciej Żenczykowskif95fd402023-02-16 22:37:22 +000092 // greater than or equal to 3 * MAX_RTR_ADV_INTERVAL. Here, we quadruple
markchien74a4fa92019-09-09 20:50:49 +080093 // that to allow for multicast packet loss.
94 //
95 // This MAX_RTR_ADV_INTERVAL_SEC and DEFAULT_LIFETIME are also consistent
96 // with the https://tools.ietf.org/html/rfc7772#section-4 discussion of
97 // "approximately 7 RAs per hour".
Maciej Żenczykowskif95fd402023-02-16 22:37:22 +000098 private static final int DEFAULT_LIFETIME = 12 * MAX_RTR_ADV_INTERVAL_SEC;
markchien74a4fa92019-09-09 20:50:49 +080099 // From https://tools.ietf.org/html/rfc4861#section-10 .
100 private static final int MIN_DELAY_BETWEEN_RAS_SEC = 3;
101 // Both initial and final RAs, but also for changes in RA contents.
102 // From https://tools.ietf.org/html/rfc4861#section-10 .
103 private static final int MAX_URGENT_RTR_ADVERTISEMENTS = 5;
104
105 private static final int DAY_IN_SECONDS = 86_400;
106
markchien74a4fa92019-09-09 20:50:49 +0800107 private final InterfaceParams mInterface;
108 private final InetSocketAddress mAllNodes;
109
110 // This lock is to protect the RA from being updated while being
111 // transmitted on another thread (multicast or unicast).
112 //
113 // TODO: This should be handled with a more RCU-like approach.
114 private final Object mLock = new Object();
115 @GuardedBy("mLock")
116 private final byte[] mRA = new byte[IPV6_MIN_MTU];
117 @GuardedBy("mLock")
118 private int mRaLength;
119 @GuardedBy("mLock")
120 private final DeprecatedInfoTracker mDeprecatedInfoTracker;
121 @GuardedBy("mLock")
122 private RaParams mRaParams;
123
124 private volatile FileDescriptor mSocket;
125 private volatile MulticastTransmitter mMulticastTransmitter;
126 private volatile UnicastResponder mUnicastResponder;
127
128 /** Encapsulate the RA parameters for RouterAdvertisementDaemon.*/
129 public static class RaParams {
130 // Tethered traffic will have the hop limit properly decremented.
131 // Consequently, set the hoplimit greater by one than the upstream
132 // unicast hop limit.
markchien74a4fa92019-09-09 20:50:49 +0800133 static final byte DEFAULT_HOPLIMIT = 65;
134
135 public boolean hasDefaultRoute;
136 public byte hopLimit;
137 public int mtu;
KH Shiff1400b2023-11-08 14:50:15 +0000138 public ArraySet<IpPrefix> prefixes;
139 public ArraySet<Inet6Address> dnses;
markchien74a4fa92019-09-09 20:50:49 +0800140
141 public RaParams() {
142 hasDefaultRoute = false;
143 hopLimit = DEFAULT_HOPLIMIT;
144 mtu = IPV6_MIN_MTU;
KH Shiff1400b2023-11-08 14:50:15 +0000145 prefixes = new ArraySet<IpPrefix>();
146 dnses = new ArraySet<Inet6Address>();
markchien74a4fa92019-09-09 20:50:49 +0800147 }
148
149 public RaParams(RaParams other) {
150 hasDefaultRoute = other.hasDefaultRoute;
151 hopLimit = other.hopLimit;
152 mtu = other.mtu;
KH Shiff1400b2023-11-08 14:50:15 +0000153 prefixes = new ArraySet<IpPrefix>(other.prefixes);
154 dnses = new ArraySet<Inet6Address>(other.dnses);
markchien74a4fa92019-09-09 20:50:49 +0800155 }
156
157 /**
158 * Returns the subset of RA parameters that become deprecated when
159 * moving from announcing oldRa to announcing newRa.
160 *
161 * Currently only tracks differences in |prefixes| and |dnses|.
162 */
163 public static RaParams getDeprecatedRaParams(RaParams oldRa, RaParams newRa) {
164 RaParams newlyDeprecated = new RaParams();
165
166 if (oldRa != null) {
167 for (IpPrefix ipp : oldRa.prefixes) {
168 if (newRa == null || !newRa.prefixes.contains(ipp)) {
169 newlyDeprecated.prefixes.add(ipp);
170 }
171 }
172
173 for (Inet6Address dns : oldRa.dnses) {
174 if (newRa == null || !newRa.dnses.contains(dns)) {
175 newlyDeprecated.dnses.add(dns);
176 }
177 }
178 }
179
180 return newlyDeprecated;
181 }
182 }
183
184 private static class DeprecatedInfoTracker {
185 private final HashMap<IpPrefix, Integer> mPrefixes = new HashMap<>();
186 private final HashMap<Inet6Address, Integer> mDnses = new HashMap<>();
187
188 Set<IpPrefix> getPrefixes() {
189 return mPrefixes.keySet();
190 }
191
192 void putPrefixes(Set<IpPrefix> prefixes) {
193 for (IpPrefix ipp : prefixes) {
194 mPrefixes.put(ipp, MAX_URGENT_RTR_ADVERTISEMENTS);
195 }
196 }
197
198 void removePrefixes(Set<IpPrefix> prefixes) {
199 for (IpPrefix ipp : prefixes) {
200 mPrefixes.remove(ipp);
201 }
202 }
203
204 Set<Inet6Address> getDnses() {
205 return mDnses.keySet();
206 }
207
208 void putDnses(Set<Inet6Address> dnses) {
209 for (Inet6Address dns : dnses) {
210 mDnses.put(dns, MAX_URGENT_RTR_ADVERTISEMENTS);
211 }
212 }
213
214 void removeDnses(Set<Inet6Address> dnses) {
215 for (Inet6Address dns : dnses) {
216 mDnses.remove(dns);
217 }
218 }
219
220 boolean isEmpty() {
221 return mPrefixes.isEmpty() && mDnses.isEmpty();
222 }
223
224 private boolean decrementCounters() {
225 boolean removed = decrementCounter(mPrefixes);
226 removed |= decrementCounter(mDnses);
227 return removed;
228 }
229
230 private <T> boolean decrementCounter(HashMap<T, Integer> map) {
231 boolean removed = false;
232
233 for (Iterator<Map.Entry<T, Integer>> it = map.entrySet().iterator();
234 it.hasNext();) {
235 Map.Entry<T, Integer> kv = it.next();
236 if (kv.getValue() == 0) {
237 it.remove();
238 removed = true;
239 } else {
240 kv.setValue(kv.getValue() - 1);
241 }
242 }
243
244 return removed;
245 }
246 }
247
markchien74a4fa92019-09-09 20:50:49 +0800248 public RouterAdvertisementDaemon(InterfaceParams ifParams) {
249 mInterface = ifParams;
250 mAllNodes = new InetSocketAddress(getAllNodesForScopeId(mInterface.index), 0);
251 mDeprecatedInfoTracker = new DeprecatedInfoTracker();
252 }
253
254 /** Build new RA.*/
255 public void buildNewRa(RaParams deprecatedParams, RaParams newParams) {
256 synchronized (mLock) {
257 if (deprecatedParams != null) {
258 mDeprecatedInfoTracker.putPrefixes(deprecatedParams.prefixes);
259 mDeprecatedInfoTracker.putDnses(deprecatedParams.dnses);
260 }
261
262 if (newParams != null) {
263 // Process information that is no longer deprecated.
264 mDeprecatedInfoTracker.removePrefixes(newParams.prefixes);
265 mDeprecatedInfoTracker.removeDnses(newParams.dnses);
266 }
267
268 mRaParams = newParams;
269 assembleRaLocked();
270 }
271
272 maybeNotifyMulticastTransmitter();
273 }
274
275 /** Start router advertisement daemon. */
276 public boolean start() {
277 if (!createSocket()) {
278 return false;
279 }
280
281 mMulticastTransmitter = new MulticastTransmitter();
282 mMulticastTransmitter.start();
283
284 mUnicastResponder = new UnicastResponder();
285 mUnicastResponder.start();
286
287 return true;
288 }
289
290 /** Stop router advertisement daemon. */
291 public void stop() {
292 closeSocket();
293 // Wake up mMulticastTransmitter thread to interrupt a potential 1 day sleep before
294 // the thread's termination.
295 maybeNotifyMulticastTransmitter();
296 mMulticastTransmitter = null;
297 mUnicastResponder = null;
298 }
299
300 @GuardedBy("mLock")
301 private void assembleRaLocked() {
302 final ByteBuffer ra = ByteBuffer.wrap(mRA);
303 ra.order(ByteOrder.BIG_ENDIAN);
304
305 final boolean haveRaParams = (mRaParams != null);
306 boolean shouldSendRA = false;
307
308 try {
309 putHeader(ra, haveRaParams && mRaParams.hasDefaultRoute,
310 haveRaParams ? mRaParams.hopLimit : RaParams.DEFAULT_HOPLIMIT);
311 putSlla(ra, mInterface.macAddr.toByteArray());
312 mRaLength = ra.position();
313
314 // https://tools.ietf.org/html/rfc5175#section-4 says:
315 //
316 // "MUST NOT be added to a Router Advertisement message
317 // if no flags in the option are set."
318 //
319 // putExpandedFlagsOption(ra);
320
321 if (haveRaParams) {
322 putMtu(ra, mRaParams.mtu);
323 mRaLength = ra.position();
324
325 for (IpPrefix ipp : mRaParams.prefixes) {
326 putPio(ra, ipp, DEFAULT_LIFETIME, DEFAULT_LIFETIME);
327 mRaLength = ra.position();
328 shouldSendRA = true;
329 }
330
331 if (mRaParams.dnses.size() > 0) {
332 putRdnss(ra, mRaParams.dnses, DEFAULT_LIFETIME);
333 mRaLength = ra.position();
334 shouldSendRA = true;
335 }
336 }
337
338 for (IpPrefix ipp : mDeprecatedInfoTracker.getPrefixes()) {
339 putPio(ra, ipp, 0, 0);
340 mRaLength = ra.position();
341 shouldSendRA = true;
342 }
343
344 final Set<Inet6Address> deprecatedDnses = mDeprecatedInfoTracker.getDnses();
345 if (!deprecatedDnses.isEmpty()) {
346 putRdnss(ra, deprecatedDnses, 0);
347 mRaLength = ra.position();
348 shouldSendRA = true;
349 }
350 } catch (BufferOverflowException e) {
351 // The packet up to mRaLength is valid, since it has been updated
352 // progressively as the RA was built. Log an error, and continue
353 // on as best as possible.
354 Log.e(TAG, "Could not construct new RA: " + e);
355 }
356
357 // We have nothing worth announcing; indicate as much to maybeSendRA().
358 if (!shouldSendRA) {
359 mRaLength = 0;
360 }
361 }
362
363 private void maybeNotifyMulticastTransmitter() {
364 final MulticastTransmitter m = mMulticastTransmitter;
365 if (m != null) {
366 m.hup();
367 }
368 }
369
markchien74a4fa92019-09-09 20:50:49 +0800370 private static byte asByte(int value) {
371 return (byte) value;
372 }
373 private static short asShort(int value) {
374 return (short) value;
375 }
376
377 private static void putHeader(ByteBuffer ra, boolean hasDefaultRoute, byte hopLimit) {
Xiao Ma3e557d72021-03-04 06:04:28 +0000378 // RFC 4191 "high" preference, iff. advertising a default route.
379 final byte flags = hasDefaultRoute ? asByte(0x08) : asByte(0);
380 final short lifetime = hasDefaultRoute ? asShort(DEFAULT_LIFETIME) : asShort(0);
381 final Icmpv6Header icmpv6Header =
382 new Icmpv6Header(asByte(ICMPV6_ROUTER_ADVERTISEMENT) /* type */,
383 asByte(0) /* code */, asShort(0) /* checksum */);
384 final RaHeader raHeader = new RaHeader(hopLimit, flags, lifetime, 0 /* reachableTime */,
385 0 /* retransTimer */);
386 icmpv6Header.writeToByteBuffer(ra);
387 raHeader.writeToByteBuffer(ra);
markchien74a4fa92019-09-09 20:50:49 +0800388 }
389
390 private static void putSlla(ByteBuffer ra, byte[] slla) {
markchien74a4fa92019-09-09 20:50:49 +0800391 if (slla == null || slla.length != 6) {
392 // Only IEEE 802.3 6-byte addresses are supported.
393 return;
394 }
395
Xiao Ma3e557d72021-03-04 06:04:28 +0000396 final ByteBuffer sllaOption = LlaOption.build(asByte(ICMPV6_ND_OPTION_SLLA),
397 MacAddress.fromBytes(slla));
398 ra.put(sllaOption);
markchien74a4fa92019-09-09 20:50:49 +0800399 }
400
401 private static void putExpandedFlagsOption(ByteBuffer ra) {
402 /**
403 Router Advertisement Expanded Flags Option
404
405 0 1 2 3
406 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
407 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
408 | Type | Length | Bit fields available ..
409 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
410 ... for assignment |
411 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
412 */
413
414 final byte nd_option__efo = 26;
415 final byte efo_num_8octets = 1;
416
417 ra.put(nd_option__efo)
418 .put(efo_num_8octets)
419 .putShort(asShort(0))
420 .putInt(0);
421 }
422
423 private static void putMtu(ByteBuffer ra, int mtu) {
Xiao Ma3e557d72021-03-04 06:04:28 +0000424 final ByteBuffer mtuOption = MtuOption.build((mtu < IPV6_MIN_MTU) ? IPV6_MIN_MTU : mtu);
425 ra.put(mtuOption);
markchien74a4fa92019-09-09 20:50:49 +0800426 }
427
428 private static void putPio(ByteBuffer ra, IpPrefix ipp,
429 int validTime, int preferredTime) {
markchien74a4fa92019-09-09 20:50:49 +0800430 final int prefixLength = ipp.getPrefixLength();
431 if (prefixLength != 64) {
432 return;
433 }
markchien74a4fa92019-09-09 20:50:49 +0800434
435 if (validTime < 0) validTime = 0;
436 if (preferredTime < 0) preferredTime = 0;
437 if (preferredTime > validTime) preferredTime = validTime;
438
Xiao Ma3e557d72021-03-04 06:04:28 +0000439 final ByteBuffer pioOption = PrefixInformationOption.build(ipp,
440 asByte(PIO_FLAG_ON_LINK | PIO_FLAG_AUTONOMOUS), validTime, preferredTime);
441 ra.put(pioOption);
markchien74a4fa92019-09-09 20:50:49 +0800442 }
443
444 private static void putRio(ByteBuffer ra, IpPrefix ipp) {
445 /**
446 Route Information Option
447
448 0 1 2 3
449 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
450 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
451 | Type | Length | Prefix Length |Resvd|Prf|Resvd|
452 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
453 | Route Lifetime |
454 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
455 | Prefix (Variable Length) |
456 . .
457 . .
458 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
459 */
460 final int prefixLength = ipp.getPrefixLength();
461 if (prefixLength > 64) {
462 return;
463 }
464 final byte nd_option_rio = 24;
465 final byte rio_num_8octets = asByte(
466 (prefixLength == 0) ? 1 : (prefixLength <= 8) ? 2 : 3);
467
468 final byte[] addr = ipp.getAddress().getAddress();
469 ra.put(nd_option_rio)
470 .put(rio_num_8octets)
471 .put(asByte(prefixLength))
472 .put(asByte(0x18))
473 .putInt(DEFAULT_LIFETIME);
474
475 // Rely upon an IpPrefix's address being properly zeroed.
476 if (prefixLength > 0) {
477 ra.put(addr, 0, (prefixLength <= 64) ? 8 : 16);
478 }
479 }
480
481 private static void putRdnss(ByteBuffer ra, Set<Inet6Address> dnses, int lifetime) {
markchien74a4fa92019-09-09 20:50:49 +0800482 final HashSet<Inet6Address> filteredDnses = new HashSet<>();
483 for (Inet6Address dns : dnses) {
484 if ((new LinkAddress(dns, RFC7421_PREFIX_LENGTH)).isGlobalPreferred()) {
485 filteredDnses.add(dns);
486 }
487 }
488 if (filteredDnses.isEmpty()) return;
489
Xiao Ma3e557d72021-03-04 06:04:28 +0000490 final Inet6Address[] dnsesArray =
491 filteredDnses.toArray(new Inet6Address[filteredDnses.size()]);
492 final ByteBuffer rdnssOption = RdnssOption.build(lifetime, dnsesArray);
493 // NOTE: If the full of list DNS servers doesn't fit in the packet,
494 // this code will cause a buffer overflow and the RA won't include
495 // this instance of the option at all.
496 //
497 // TODO: Consider looking at ra.remaining() to determine how many
498 // DNS servers will fit, and adding only those.
499 ra.put(rdnssOption);
markchien74a4fa92019-09-09 20:50:49 +0800500 }
501
502 private boolean createSocket() {
503 final int send_timout_ms = 300;
504
Xiao Ma3e557d72021-03-04 06:04:28 +0000505 final int oldTag = TrafficStats.getAndSetThreadStatsTag(TAG_SYSTEM_NEIGHBOR);
markchien74a4fa92019-09-09 20:50:49 +0800506 try {
507 mSocket = Os.socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
508 // Setting SNDTIMEO is purely for defensive purposes.
509 Os.setsockoptTimeval(
510 mSocket, SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(send_timout_ms));
markchien6cf0e552019-12-06 15:24:53 +0800511 SocketUtils.bindSocketToInterface(mSocket, mInterface.name);
markchienf87ebdc2019-12-07 22:02:28 +0800512 TetheringUtils.setupRaSocket(mSocket, mInterface.index);
markchien74a4fa92019-09-09 20:50:49 +0800513 } catch (ErrnoException | IOException e) {
514 Log.e(TAG, "Failed to create RA daemon socket: " + e);
515 return false;
516 } finally {
517 TrafficStats.setThreadStatsTag(oldTag);
518 }
519
520 return true;
521 }
522
523 private void closeSocket() {
524 if (mSocket != null) {
525 try {
markchien6cf0e552019-12-06 15:24:53 +0800526 SocketUtils.closeSocket(mSocket);
markchien74a4fa92019-09-09 20:50:49 +0800527 } catch (IOException ignored) { }
528 }
529 mSocket = null;
530 }
531
532 private boolean isSocketValid() {
533 final FileDescriptor s = mSocket;
534 return (s != null) && s.valid();
535 }
536
537 private boolean isSuitableDestination(InetSocketAddress dest) {
538 if (mAllNodes.equals(dest)) {
539 return true;
540 }
541
542 final InetAddress destip = dest.getAddress();
543 return (destip instanceof Inet6Address)
544 && destip.isLinkLocalAddress()
545 && (((Inet6Address) destip).getScopeId() == mInterface.index);
546 }
547
548 private void maybeSendRA(InetSocketAddress dest) {
549 if (dest == null || !isSuitableDestination(dest)) {
550 dest = mAllNodes;
551 }
552
553 try {
554 synchronized (mLock) {
Xiao Ma3e557d72021-03-04 06:04:28 +0000555 if (mRaLength < ICMPV6_RA_HEADER_LEN) {
markchien74a4fa92019-09-09 20:50:49 +0800556 // No actual RA to send.
557 return;
558 }
559 Os.sendto(mSocket, mRA, 0, mRaLength, 0, dest);
560 }
561 Log.d(TAG, "RA sendto " + dest.getAddress().getHostAddress());
562 } catch (ErrnoException | SocketException e) {
563 if (isSocketValid()) {
564 Log.e(TAG, "sendto error: " + e);
565 }
566 }
567 }
568
569 private final class UnicastResponder extends Thread {
markchienb799fa32020-01-08 20:58:23 +0800570 private final InetSocketAddress mSolicitor = new InetSocketAddress(0);
markchien74a4fa92019-09-09 20:50:49 +0800571 // The recycled buffer for receiving Router Solicitations from clients.
572 // If the RS is larger than IPV6_MIN_MTU the packets are truncated.
573 // This is fine since currently only byte 0 is examined anyway.
574 private final byte[] mSolicitation = new byte[IPV6_MIN_MTU];
575
576 @Override
577 public void run() {
578 while (isSocketValid()) {
579 try {
580 // Blocking receive.
581 final int rval = Os.recvfrom(
582 mSocket, mSolicitation, 0, mSolicitation.length, 0, mSolicitor);
583 // Do the least possible amount of validation.
Xiao Ma3e557d72021-03-04 06:04:28 +0000584 if (rval < 1 || mSolicitation[0] != asByte(ICMPV6_ROUTER_SOLICITATION)) {
markchien74a4fa92019-09-09 20:50:49 +0800585 continue;
586 }
587 } catch (ErrnoException | SocketException e) {
588 if (isSocketValid()) {
589 Log.e(TAG, "recvfrom error: " + e);
590 }
591 continue;
592 }
593
594 maybeSendRA(mSolicitor);
595 }
596 }
597 }
598
599 // TODO: Consider moving this to run on a provided Looper as a Handler,
600 // with WakeupMessage-style messages providing the timer driven input.
601 private final class MulticastTransmitter extends Thread {
602 private final Random mRandom = new Random();
603 private final AtomicInteger mUrgentAnnouncements = new AtomicInteger(0);
604
605 @Override
606 public void run() {
607 while (isSocketValid()) {
608 try {
609 Thread.sleep(getNextMulticastTransmitDelayMs());
610 } catch (InterruptedException ignored) {
611 // Stop sleeping, immediately send an RA, and continue.
612 }
613
614 maybeSendRA(mAllNodes);
615 synchronized (mLock) {
616 if (mDeprecatedInfoTracker.decrementCounters()) {
617 // At least one deprecated PIO has been removed;
618 // reassemble the RA.
619 assembleRaLocked();
620 }
621 }
622 }
623 }
624
625 public void hup() {
626 // Set to one fewer that the desired number, because as soon as
627 // the thread interrupt is processed we immediately send an RA
628 // and mUrgentAnnouncements is not examined until the subsequent
629 // sleep interval computation (i.e. this way we send 3 and not 4).
630 mUrgentAnnouncements.set(MAX_URGENT_RTR_ADVERTISEMENTS - 1);
631 interrupt();
632 }
633
634 private int getNextMulticastTransmitDelaySec() {
635 boolean deprecationInProgress = false;
636 synchronized (mLock) {
Xiao Ma3e557d72021-03-04 06:04:28 +0000637 if (mRaLength < ICMPV6_RA_HEADER_LEN) {
markchien74a4fa92019-09-09 20:50:49 +0800638 // No actual RA to send; just sleep for 1 day.
639 return DAY_IN_SECONDS;
640 }
641 deprecationInProgress = !mDeprecatedInfoTracker.isEmpty();
642 }
643
644 final int urgentPending = mUrgentAnnouncements.getAndDecrement();
645 if ((urgentPending > 0) || deprecationInProgress) {
646 return MIN_DELAY_BETWEEN_RAS_SEC;
647 }
648
649 return MIN_RTR_ADV_INTERVAL_SEC + mRandom.nextInt(
650 MAX_RTR_ADV_INTERVAL_SEC - MIN_RTR_ADV_INTERVAL_SEC);
651 }
652
653 private long getNextMulticastTransmitDelayMs() {
654 return 1000 * (long) getNextMulticastTransmitDelaySec();
655 }
656 }
657}