[Tether03] Migrate IpServer into module
Add IpServer which is used to serve ip configuration, dhcp, dns proxy
and nat for downstream interface.
Bug: 136040414
Test: -build, flash, boot
-atest TetheringTests
-atest FrameworksNetTests
Change-Id: I23652ae0b9509abe7d38da96d523eb22ab00a343
This commit is contained in:
761
Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
Normal file
761
Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
Normal file
@@ -0,0 +1,761 @@
|
||||
/*
|
||||
* 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.NetworkConstants.IPV6_MIN_MTU;
|
||||
import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
|
||||
import static android.system.OsConstants.AF_INET6;
|
||||
import static android.system.OsConstants.IPPROTO_ICMPV6;
|
||||
import static android.system.OsConstants.SOCK_RAW;
|
||||
import static android.system.OsConstants.SOL_SOCKET;
|
||||
import static android.system.OsConstants.SO_BINDTODEVICE;
|
||||
import static android.system.OsConstants.SO_SNDTIMEO;
|
||||
|
||||
import android.net.IpPrefix;
|
||||
import android.net.LinkAddress;
|
||||
import android.net.NetworkUtils;
|
||||
import android.net.TrafficStats;
|
||||
import android.net.util.InterfaceParams;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
import android.system.StructTimeval;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.util.TrafficStatsConstants;
|
||||
|
||||
import libcore.io.IoBridge;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.BufferOverflowException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
|
||||
/**
|
||||
* Basic IPv6 Router Advertisement Daemon.
|
||||
*
|
||||
* TODO:
|
||||
*
|
||||
* - Rewrite using Handler (and friends) so that AlarmManager can deliver
|
||||
* "kick" messages when it's time to send a multicast RA.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class RouterAdvertisementDaemon {
|
||||
private static final String TAG = RouterAdvertisementDaemon.class.getSimpleName();
|
||||
private static final byte ICMPV6_ND_ROUTER_SOLICIT = asByte(133);
|
||||
private static final byte ICMPV6_ND_ROUTER_ADVERT = asByte(134);
|
||||
private static final int MIN_RA_HEADER_SIZE = 16;
|
||||
|
||||
// Summary of various timers and lifetimes.
|
||||
private static final int MIN_RTR_ADV_INTERVAL_SEC = 300;
|
||||
private static final int MAX_RTR_ADV_INTERVAL_SEC = 600;
|
||||
// In general, router, prefix, and DNS lifetimes are all advised to be
|
||||
// greater than or equal to 3 * MAX_RTR_ADV_INTERVAL. Here, we double
|
||||
// that to allow for multicast packet loss.
|
||||
//
|
||||
// This MAX_RTR_ADV_INTERVAL_SEC and DEFAULT_LIFETIME are also consistent
|
||||
// with the https://tools.ietf.org/html/rfc7772#section-4 discussion of
|
||||
// "approximately 7 RAs per hour".
|
||||
private static final int DEFAULT_LIFETIME = 6 * MAX_RTR_ADV_INTERVAL_SEC;
|
||||
// From https://tools.ietf.org/html/rfc4861#section-10 .
|
||||
private static final int MIN_DELAY_BETWEEN_RAS_SEC = 3;
|
||||
// Both initial and final RAs, but also for changes in RA contents.
|
||||
// From https://tools.ietf.org/html/rfc4861#section-10 .
|
||||
private static final int MAX_URGENT_RTR_ADVERTISEMENTS = 5;
|
||||
|
||||
private static final int DAY_IN_SECONDS = 86_400;
|
||||
|
||||
private static final byte[] ALL_NODES = new byte[] {
|
||||
(byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
|
||||
};
|
||||
|
||||
private final InterfaceParams mInterface;
|
||||
private final InetSocketAddress mAllNodes;
|
||||
|
||||
// This lock is to protect the RA from being updated while being
|
||||
// transmitted on another thread (multicast or unicast).
|
||||
//
|
||||
// TODO: This should be handled with a more RCU-like approach.
|
||||
private final Object mLock = new Object();
|
||||
@GuardedBy("mLock")
|
||||
private final byte[] mRA = new byte[IPV6_MIN_MTU];
|
||||
@GuardedBy("mLock")
|
||||
private int mRaLength;
|
||||
@GuardedBy("mLock")
|
||||
private final DeprecatedInfoTracker mDeprecatedInfoTracker;
|
||||
@GuardedBy("mLock")
|
||||
private RaParams mRaParams;
|
||||
|
||||
private volatile FileDescriptor mSocket;
|
||||
private volatile MulticastTransmitter mMulticastTransmitter;
|
||||
private volatile UnicastResponder mUnicastResponder;
|
||||
|
||||
/** Encapsulate the RA parameters for RouterAdvertisementDaemon.*/
|
||||
public static class RaParams {
|
||||
// Tethered traffic will have the hop limit properly decremented.
|
||||
// Consequently, set the hoplimit greater by one than the upstream
|
||||
// unicast hop limit.
|
||||
//
|
||||
// TODO: Dynamically pass down the IPV6_UNICAST_HOPS value from the
|
||||
// upstream interface for more correct behaviour.
|
||||
static final byte DEFAULT_HOPLIMIT = 65;
|
||||
|
||||
public boolean hasDefaultRoute;
|
||||
public byte hopLimit;
|
||||
public int mtu;
|
||||
public HashSet<IpPrefix> prefixes;
|
||||
public HashSet<Inet6Address> dnses;
|
||||
|
||||
public RaParams() {
|
||||
hasDefaultRoute = false;
|
||||
hopLimit = DEFAULT_HOPLIMIT;
|
||||
mtu = IPV6_MIN_MTU;
|
||||
prefixes = new HashSet<IpPrefix>();
|
||||
dnses = new HashSet<Inet6Address>();
|
||||
}
|
||||
|
||||
public RaParams(RaParams other) {
|
||||
hasDefaultRoute = other.hasDefaultRoute;
|
||||
hopLimit = other.hopLimit;
|
||||
mtu = other.mtu;
|
||||
prefixes = (HashSet) other.prefixes.clone();
|
||||
dnses = (HashSet) other.dnses.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the subset of RA parameters that become deprecated when
|
||||
* moving from announcing oldRa to announcing newRa.
|
||||
*
|
||||
* Currently only tracks differences in |prefixes| and |dnses|.
|
||||
*/
|
||||
public static RaParams getDeprecatedRaParams(RaParams oldRa, RaParams newRa) {
|
||||
RaParams newlyDeprecated = new RaParams();
|
||||
|
||||
if (oldRa != null) {
|
||||
for (IpPrefix ipp : oldRa.prefixes) {
|
||||
if (newRa == null || !newRa.prefixes.contains(ipp)) {
|
||||
newlyDeprecated.prefixes.add(ipp);
|
||||
}
|
||||
}
|
||||
|
||||
for (Inet6Address dns : oldRa.dnses) {
|
||||
if (newRa == null || !newRa.dnses.contains(dns)) {
|
||||
newlyDeprecated.dnses.add(dns);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newlyDeprecated;
|
||||
}
|
||||
}
|
||||
|
||||
private static class DeprecatedInfoTracker {
|
||||
private final HashMap<IpPrefix, Integer> mPrefixes = new HashMap<>();
|
||||
private final HashMap<Inet6Address, Integer> mDnses = new HashMap<>();
|
||||
|
||||
Set<IpPrefix> getPrefixes() {
|
||||
return mPrefixes.keySet();
|
||||
}
|
||||
|
||||
void putPrefixes(Set<IpPrefix> prefixes) {
|
||||
for (IpPrefix ipp : prefixes) {
|
||||
mPrefixes.put(ipp, MAX_URGENT_RTR_ADVERTISEMENTS);
|
||||
}
|
||||
}
|
||||
|
||||
void removePrefixes(Set<IpPrefix> prefixes) {
|
||||
for (IpPrefix ipp : prefixes) {
|
||||
mPrefixes.remove(ipp);
|
||||
}
|
||||
}
|
||||
|
||||
Set<Inet6Address> getDnses() {
|
||||
return mDnses.keySet();
|
||||
}
|
||||
|
||||
void putDnses(Set<Inet6Address> dnses) {
|
||||
for (Inet6Address dns : dnses) {
|
||||
mDnses.put(dns, MAX_URGENT_RTR_ADVERTISEMENTS);
|
||||
}
|
||||
}
|
||||
|
||||
void removeDnses(Set<Inet6Address> dnses) {
|
||||
for (Inet6Address dns : dnses) {
|
||||
mDnses.remove(dns);
|
||||
}
|
||||
}
|
||||
|
||||
boolean isEmpty() {
|
||||
return mPrefixes.isEmpty() && mDnses.isEmpty();
|
||||
}
|
||||
|
||||
private boolean decrementCounters() {
|
||||
boolean removed = decrementCounter(mPrefixes);
|
||||
removed |= decrementCounter(mDnses);
|
||||
return removed;
|
||||
}
|
||||
|
||||
private <T> boolean decrementCounter(HashMap<T, Integer> map) {
|
||||
boolean removed = false;
|
||||
|
||||
for (Iterator<Map.Entry<T, Integer>> it = map.entrySet().iterator();
|
||||
it.hasNext();) {
|
||||
Map.Entry<T, Integer> kv = it.next();
|
||||
if (kv.getValue() == 0) {
|
||||
it.remove();
|
||||
removed = true;
|
||||
} else {
|
||||
kv.setValue(kv.getValue() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public RouterAdvertisementDaemon(InterfaceParams ifParams) {
|
||||
mInterface = ifParams;
|
||||
mAllNodes = new InetSocketAddress(getAllNodesForScopeId(mInterface.index), 0);
|
||||
mDeprecatedInfoTracker = new DeprecatedInfoTracker();
|
||||
}
|
||||
|
||||
/** Build new RA.*/
|
||||
public void buildNewRa(RaParams deprecatedParams, RaParams newParams) {
|
||||
synchronized (mLock) {
|
||||
if (deprecatedParams != null) {
|
||||
mDeprecatedInfoTracker.putPrefixes(deprecatedParams.prefixes);
|
||||
mDeprecatedInfoTracker.putDnses(deprecatedParams.dnses);
|
||||
}
|
||||
|
||||
if (newParams != null) {
|
||||
// Process information that is no longer deprecated.
|
||||
mDeprecatedInfoTracker.removePrefixes(newParams.prefixes);
|
||||
mDeprecatedInfoTracker.removeDnses(newParams.dnses);
|
||||
}
|
||||
|
||||
mRaParams = newParams;
|
||||
assembleRaLocked();
|
||||
}
|
||||
|
||||
maybeNotifyMulticastTransmitter();
|
||||
}
|
||||
|
||||
/** Start router advertisement daemon. */
|
||||
public boolean start() {
|
||||
if (!createSocket()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mMulticastTransmitter = new MulticastTransmitter();
|
||||
mMulticastTransmitter.start();
|
||||
|
||||
mUnicastResponder = new UnicastResponder();
|
||||
mUnicastResponder.start();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Stop router advertisement daemon. */
|
||||
public void stop() {
|
||||
closeSocket();
|
||||
// Wake up mMulticastTransmitter thread to interrupt a potential 1 day sleep before
|
||||
// the thread's termination.
|
||||
maybeNotifyMulticastTransmitter();
|
||||
mMulticastTransmitter = null;
|
||||
mUnicastResponder = null;
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private void assembleRaLocked() {
|
||||
final ByteBuffer ra = ByteBuffer.wrap(mRA);
|
||||
ra.order(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
final boolean haveRaParams = (mRaParams != null);
|
||||
boolean shouldSendRA = false;
|
||||
|
||||
try {
|
||||
putHeader(ra, haveRaParams && mRaParams.hasDefaultRoute,
|
||||
haveRaParams ? mRaParams.hopLimit : RaParams.DEFAULT_HOPLIMIT);
|
||||
putSlla(ra, mInterface.macAddr.toByteArray());
|
||||
mRaLength = ra.position();
|
||||
|
||||
// https://tools.ietf.org/html/rfc5175#section-4 says:
|
||||
//
|
||||
// "MUST NOT be added to a Router Advertisement message
|
||||
// if no flags in the option are set."
|
||||
//
|
||||
// putExpandedFlagsOption(ra);
|
||||
|
||||
if (haveRaParams) {
|
||||
putMtu(ra, mRaParams.mtu);
|
||||
mRaLength = ra.position();
|
||||
|
||||
for (IpPrefix ipp : mRaParams.prefixes) {
|
||||
putPio(ra, ipp, DEFAULT_LIFETIME, DEFAULT_LIFETIME);
|
||||
mRaLength = ra.position();
|
||||
shouldSendRA = true;
|
||||
}
|
||||
|
||||
if (mRaParams.dnses.size() > 0) {
|
||||
putRdnss(ra, mRaParams.dnses, DEFAULT_LIFETIME);
|
||||
mRaLength = ra.position();
|
||||
shouldSendRA = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (IpPrefix ipp : mDeprecatedInfoTracker.getPrefixes()) {
|
||||
putPio(ra, ipp, 0, 0);
|
||||
mRaLength = ra.position();
|
||||
shouldSendRA = true;
|
||||
}
|
||||
|
||||
final Set<Inet6Address> deprecatedDnses = mDeprecatedInfoTracker.getDnses();
|
||||
if (!deprecatedDnses.isEmpty()) {
|
||||
putRdnss(ra, deprecatedDnses, 0);
|
||||
mRaLength = ra.position();
|
||||
shouldSendRA = true;
|
||||
}
|
||||
} catch (BufferOverflowException e) {
|
||||
// The packet up to mRaLength is valid, since it has been updated
|
||||
// progressively as the RA was built. Log an error, and continue
|
||||
// on as best as possible.
|
||||
Log.e(TAG, "Could not construct new RA: " + e);
|
||||
}
|
||||
|
||||
// We have nothing worth announcing; indicate as much to maybeSendRA().
|
||||
if (!shouldSendRA) {
|
||||
mRaLength = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void maybeNotifyMulticastTransmitter() {
|
||||
final MulticastTransmitter m = mMulticastTransmitter;
|
||||
if (m != null) {
|
||||
m.hup();
|
||||
}
|
||||
}
|
||||
|
||||
private static Inet6Address getAllNodesForScopeId(int scopeId) {
|
||||
try {
|
||||
return Inet6Address.getByAddress("ff02::1", ALL_NODES, scopeId);
|
||||
} catch (UnknownHostException uhe) {
|
||||
Log.wtf(TAG, "Failed to construct ff02::1 InetAddress: " + uhe);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static byte asByte(int value) {
|
||||
return (byte) value;
|
||||
}
|
||||
private static short asShort(int value) {
|
||||
return (short) value;
|
||||
}
|
||||
|
||||
private static void putHeader(ByteBuffer ra, boolean hasDefaultRoute, byte hopLimit) {
|
||||
/**
|
||||
Router Advertisement Message Format
|
||||
|
||||
0 1 2 3
|
||||
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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Type | Code | Checksum |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Cur Hop Limit |M|O|H|Prf|P|R|R| Router Lifetime |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Reachable Time |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Retrans Timer |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Options ...
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-
|
||||
*/
|
||||
ra.put(ICMPV6_ND_ROUTER_ADVERT)
|
||||
.put(asByte(0))
|
||||
.putShort(asShort(0))
|
||||
.put(hopLimit)
|
||||
// RFC 4191 "high" preference, iff. advertising a default route.
|
||||
.put(hasDefaultRoute ? asByte(0x08) : asByte(0))
|
||||
.putShort(hasDefaultRoute ? asShort(DEFAULT_LIFETIME) : asShort(0))
|
||||
.putInt(0)
|
||||
.putInt(0);
|
||||
}
|
||||
|
||||
private static void putSlla(ByteBuffer ra, byte[] slla) {
|
||||
/**
|
||||
Source/Target Link-layer Address
|
||||
|
||||
0 1 2 3
|
||||
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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Type | Length | Link-Layer Address ...
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
if (slla == null || slla.length != 6) {
|
||||
// Only IEEE 802.3 6-byte addresses are supported.
|
||||
return;
|
||||
}
|
||||
|
||||
final byte nd_option_slla = 1;
|
||||
final byte slla_num_8octets = 1;
|
||||
ra.put(nd_option_slla)
|
||||
.put(slla_num_8octets)
|
||||
.put(slla);
|
||||
}
|
||||
|
||||
private static void putExpandedFlagsOption(ByteBuffer ra) {
|
||||
/**
|
||||
Router Advertisement Expanded Flags Option
|
||||
|
||||
0 1 2 3
|
||||
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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Type | Length | Bit fields available ..
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
... for assignment |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
|
||||
final byte nd_option__efo = 26;
|
||||
final byte efo_num_8octets = 1;
|
||||
|
||||
ra.put(nd_option__efo)
|
||||
.put(efo_num_8octets)
|
||||
.putShort(asShort(0))
|
||||
.putInt(0);
|
||||
}
|
||||
|
||||
private static void putMtu(ByteBuffer ra, int mtu) {
|
||||
/**
|
||||
MTU
|
||||
|
||||
0 1 2 3
|
||||
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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Type | Length | Reserved |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| MTU |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
final byte nd_option_mtu = 5;
|
||||
final byte mtu_num_8octs = 1;
|
||||
ra.put(nd_option_mtu)
|
||||
.put(mtu_num_8octs)
|
||||
.putShort(asShort(0))
|
||||
.putInt((mtu < IPV6_MIN_MTU) ? IPV6_MIN_MTU : mtu);
|
||||
}
|
||||
|
||||
private static void putPio(ByteBuffer ra, IpPrefix ipp,
|
||||
int validTime, int preferredTime) {
|
||||
/**
|
||||
Prefix Information
|
||||
|
||||
0 1 2 3
|
||||
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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Type | Length | Prefix Length |L|A| Reserved1 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Valid Lifetime |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Preferred Lifetime |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Reserved2 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| |
|
||||
+ +
|
||||
| |
|
||||
+ Prefix +
|
||||
| |
|
||||
+ +
|
||||
| |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
final int prefixLength = ipp.getPrefixLength();
|
||||
if (prefixLength != 64) {
|
||||
return;
|
||||
}
|
||||
final byte nd_option_pio = 3;
|
||||
final byte pio_num_8octets = 4;
|
||||
|
||||
if (validTime < 0) validTime = 0;
|
||||
if (preferredTime < 0) preferredTime = 0;
|
||||
if (preferredTime > validTime) preferredTime = validTime;
|
||||
|
||||
final byte[] addr = ipp.getAddress().getAddress();
|
||||
ra.put(nd_option_pio)
|
||||
.put(pio_num_8octets)
|
||||
.put(asByte(prefixLength))
|
||||
.put(asByte(0xc0)) /* L & A set */
|
||||
.putInt(validTime)
|
||||
.putInt(preferredTime)
|
||||
.putInt(0)
|
||||
.put(addr);
|
||||
}
|
||||
|
||||
private static void putRio(ByteBuffer ra, IpPrefix ipp) {
|
||||
/**
|
||||
Route Information Option
|
||||
|
||||
0 1 2 3
|
||||
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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Type | Length | Prefix Length |Resvd|Prf|Resvd|
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Route Lifetime |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Prefix (Variable Length) |
|
||||
. .
|
||||
. .
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
final int prefixLength = ipp.getPrefixLength();
|
||||
if (prefixLength > 64) {
|
||||
return;
|
||||
}
|
||||
final byte nd_option_rio = 24;
|
||||
final byte rio_num_8octets = asByte(
|
||||
(prefixLength == 0) ? 1 : (prefixLength <= 8) ? 2 : 3);
|
||||
|
||||
final byte[] addr = ipp.getAddress().getAddress();
|
||||
ra.put(nd_option_rio)
|
||||
.put(rio_num_8octets)
|
||||
.put(asByte(prefixLength))
|
||||
.put(asByte(0x18))
|
||||
.putInt(DEFAULT_LIFETIME);
|
||||
|
||||
// Rely upon an IpPrefix's address being properly zeroed.
|
||||
if (prefixLength > 0) {
|
||||
ra.put(addr, 0, (prefixLength <= 64) ? 8 : 16);
|
||||
}
|
||||
}
|
||||
|
||||
private static void putRdnss(ByteBuffer ra, Set<Inet6Address> dnses, int lifetime) {
|
||||
/**
|
||||
Recursive DNS Server (RDNSS) Option
|
||||
|
||||
0 1 2 3
|
||||
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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Type | Length | Reserved |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Lifetime |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| |
|
||||
: Addresses of IPv6 Recursive DNS Servers :
|
||||
| |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
|
||||
final HashSet<Inet6Address> filteredDnses = new HashSet<>();
|
||||
for (Inet6Address dns : dnses) {
|
||||
if ((new LinkAddress(dns, RFC7421_PREFIX_LENGTH)).isGlobalPreferred()) {
|
||||
filteredDnses.add(dns);
|
||||
}
|
||||
}
|
||||
if (filteredDnses.isEmpty()) return;
|
||||
|
||||
final byte nd_option_rdnss = 25;
|
||||
final byte rdnss_num_8octets = asByte(dnses.size() * 2 + 1);
|
||||
ra.put(nd_option_rdnss)
|
||||
.put(rdnss_num_8octets)
|
||||
.putShort(asShort(0))
|
||||
.putInt(lifetime);
|
||||
|
||||
for (Inet6Address dns : filteredDnses) {
|
||||
// NOTE: If the full of list DNS servers doesn't fit in the packet,
|
||||
// this code will cause a buffer overflow and the RA won't include
|
||||
// this instance of the option at all.
|
||||
//
|
||||
// TODO: Consider looking at ra.remaining() to determine how many
|
||||
// DNS servers will fit, and adding only those.
|
||||
ra.put(dns.getAddress());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean createSocket() {
|
||||
final int send_timout_ms = 300;
|
||||
|
||||
final int oldTag = TrafficStats.getAndSetThreadStatsTag(
|
||||
TrafficStatsConstants.TAG_SYSTEM_NEIGHBOR);
|
||||
try {
|
||||
mSocket = Os.socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
|
||||
// Setting SNDTIMEO is purely for defensive purposes.
|
||||
Os.setsockoptTimeval(
|
||||
mSocket, SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(send_timout_ms));
|
||||
Os.setsockoptIfreq(mSocket, SOL_SOCKET, SO_BINDTODEVICE, mInterface.name);
|
||||
NetworkUtils.protectFromVpn(mSocket);
|
||||
NetworkUtils.setupRaSocket(mSocket, mInterface.index);
|
||||
} catch (ErrnoException | IOException e) {
|
||||
Log.e(TAG, "Failed to create RA daemon socket: " + e);
|
||||
return false;
|
||||
} finally {
|
||||
TrafficStats.setThreadStatsTag(oldTag);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void closeSocket() {
|
||||
if (mSocket != null) {
|
||||
try {
|
||||
IoBridge.closeAndSignalBlockedThreads(mSocket);
|
||||
} catch (IOException ignored) { }
|
||||
}
|
||||
mSocket = null;
|
||||
}
|
||||
|
||||
private boolean isSocketValid() {
|
||||
final FileDescriptor s = mSocket;
|
||||
return (s != null) && s.valid();
|
||||
}
|
||||
|
||||
private boolean isSuitableDestination(InetSocketAddress dest) {
|
||||
if (mAllNodes.equals(dest)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final InetAddress destip = dest.getAddress();
|
||||
return (destip instanceof Inet6Address)
|
||||
&& destip.isLinkLocalAddress()
|
||||
&& (((Inet6Address) destip).getScopeId() == mInterface.index);
|
||||
}
|
||||
|
||||
private void maybeSendRA(InetSocketAddress dest) {
|
||||
if (dest == null || !isSuitableDestination(dest)) {
|
||||
dest = mAllNodes;
|
||||
}
|
||||
|
||||
try {
|
||||
synchronized (mLock) {
|
||||
if (mRaLength < MIN_RA_HEADER_SIZE) {
|
||||
// No actual RA to send.
|
||||
return;
|
||||
}
|
||||
Os.sendto(mSocket, mRA, 0, mRaLength, 0, dest);
|
||||
}
|
||||
Log.d(TAG, "RA sendto " + dest.getAddress().getHostAddress());
|
||||
} catch (ErrnoException | SocketException e) {
|
||||
if (isSocketValid()) {
|
||||
Log.e(TAG, "sendto error: " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class UnicastResponder extends Thread {
|
||||
private final InetSocketAddress mSolicitor = new InetSocketAddress();
|
||||
// The recycled buffer for receiving Router Solicitations from clients.
|
||||
// If the RS is larger than IPV6_MIN_MTU the packets are truncated.
|
||||
// This is fine since currently only byte 0 is examined anyway.
|
||||
private final byte[] mSolicitation = new byte[IPV6_MIN_MTU];
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (isSocketValid()) {
|
||||
try {
|
||||
// Blocking receive.
|
||||
final int rval = Os.recvfrom(
|
||||
mSocket, mSolicitation, 0, mSolicitation.length, 0, mSolicitor);
|
||||
// Do the least possible amount of validation.
|
||||
if (rval < 1 || mSolicitation[0] != ICMPV6_ND_ROUTER_SOLICIT) {
|
||||
continue;
|
||||
}
|
||||
} catch (ErrnoException | SocketException e) {
|
||||
if (isSocketValid()) {
|
||||
Log.e(TAG, "recvfrom error: " + e);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
maybeSendRA(mSolicitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Consider moving this to run on a provided Looper as a Handler,
|
||||
// with WakeupMessage-style messages providing the timer driven input.
|
||||
private final class MulticastTransmitter extends Thread {
|
||||
private final Random mRandom = new Random();
|
||||
private final AtomicInteger mUrgentAnnouncements = new AtomicInteger(0);
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (isSocketValid()) {
|
||||
try {
|
||||
Thread.sleep(getNextMulticastTransmitDelayMs());
|
||||
} catch (InterruptedException ignored) {
|
||||
// Stop sleeping, immediately send an RA, and continue.
|
||||
}
|
||||
|
||||
maybeSendRA(mAllNodes);
|
||||
synchronized (mLock) {
|
||||
if (mDeprecatedInfoTracker.decrementCounters()) {
|
||||
// At least one deprecated PIO has been removed;
|
||||
// reassemble the RA.
|
||||
assembleRaLocked();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void hup() {
|
||||
// Set to one fewer that the desired number, because as soon as
|
||||
// the thread interrupt is processed we immediately send an RA
|
||||
// and mUrgentAnnouncements is not examined until the subsequent
|
||||
// sleep interval computation (i.e. this way we send 3 and not 4).
|
||||
mUrgentAnnouncements.set(MAX_URGENT_RTR_ADVERTISEMENTS - 1);
|
||||
interrupt();
|
||||
}
|
||||
|
||||
private int getNextMulticastTransmitDelaySec() {
|
||||
boolean deprecationInProgress = false;
|
||||
synchronized (mLock) {
|
||||
if (mRaLength < MIN_RA_HEADER_SIZE) {
|
||||
// No actual RA to send; just sleep for 1 day.
|
||||
return DAY_IN_SECONDS;
|
||||
}
|
||||
deprecationInProgress = !mDeprecatedInfoTracker.isEmpty();
|
||||
}
|
||||
|
||||
final int urgentPending = mUrgentAnnouncements.getAndDecrement();
|
||||
if ((urgentPending > 0) || deprecationInProgress) {
|
||||
return MIN_DELAY_BETWEEN_RAS_SEC;
|
||||
}
|
||||
|
||||
return MIN_RTR_ADV_INTERVAL_SEC + mRandom.nextInt(
|
||||
MAX_RTR_ADV_INTERVAL_SEC - MIN_RTR_ADV_INTERVAL_SEC);
|
||||
}
|
||||
|
||||
private long getNextMulticastTransmitDelayMs() {
|
||||
return 1000 * (long) getNextMulticastTransmitDelaySec();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user