Merge "Refactor to IpNeighborMonitor and single-threaded semantics"
This commit is contained in:
@@ -19,7 +19,7 @@ package android.net.ip;
|
||||
import static android.system.OsConstants.*;
|
||||
|
||||
import android.net.NetworkUtils;
|
||||
import android.net.util.BlockingSocketReader;
|
||||
import android.net.util.PacketReader;
|
||||
import android.net.util.ConnectivityPacketSummary;
|
||||
import android.os.Handler;
|
||||
import android.system.ErrnoException;
|
||||
@@ -65,7 +65,7 @@ public class ConnectivityPacketTracker {
|
||||
|
||||
private final String mTag;
|
||||
private final LocalLog mLog;
|
||||
private final BlockingSocketReader mPacketListener;
|
||||
private final PacketReader mPacketListener;
|
||||
private boolean mRunning;
|
||||
private String mDisplayName;
|
||||
|
||||
@@ -101,7 +101,7 @@ public class ConnectivityPacketTracker {
|
||||
mDisplayName = null;
|
||||
}
|
||||
|
||||
private final class PacketListener extends BlockingSocketReader {
|
||||
private final class PacketListener extends PacketReader {
|
||||
private final int mIfIndex;
|
||||
private final byte mHwAddr[];
|
||||
|
||||
|
||||
@@ -815,6 +815,15 @@ public class IpClient extends StateMachine {
|
||||
pw.println(Objects.toString(provisioningConfig, "N/A"));
|
||||
pw.decreaseIndent();
|
||||
|
||||
final IpReachabilityMonitor iprm = mIpReachabilityMonitor;
|
||||
if (iprm != null) {
|
||||
pw.println();
|
||||
pw.println(mTag + " current IpReachabilityMonitor state:");
|
||||
pw.increaseIndent();
|
||||
iprm.dump(pw);
|
||||
pw.decreaseIndent();
|
||||
}
|
||||
|
||||
pw.println();
|
||||
pw.println(mTag + " StateMachine dump:");
|
||||
pw.increaseIndent();
|
||||
@@ -1237,6 +1246,7 @@ public class IpClient extends StateMachine {
|
||||
mIpReachabilityMonitor = new IpReachabilityMonitor(
|
||||
mContext,
|
||||
mInterfaceName,
|
||||
getHandler(),
|
||||
mLog,
|
||||
new IpReachabilityMonitor.Callback() {
|
||||
@Override
|
||||
|
||||
236
services/net/java/android/net/ip/IpNeighborMonitor.java
Normal file
236
services/net/java/android/net/ip/IpNeighborMonitor.java
Normal file
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.net.ip;
|
||||
|
||||
import android.net.netlink.NetlinkConstants;
|
||||
import android.net.netlink.NetlinkErrorMessage;
|
||||
import android.net.netlink.NetlinkMessage;
|
||||
import android.net.netlink.NetlinkSocket;
|
||||
import android.net.netlink.RtNetlinkNeighborMessage;
|
||||
import android.net.netlink.StructNdMsg;
|
||||
import android.net.netlink.StructNlMsgHdr;
|
||||
import android.net.util.PacketReader;
|
||||
import android.net.util.SharedLog;
|
||||
import android.os.Handler;
|
||||
import android.os.SystemClock;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.NetlinkSocketAddress;
|
||||
import android.system.Os;
|
||||
import android.system.OsConstants;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.util.BitUtils;
|
||||
|
||||
import libcore.io.IoUtils;
|
||||
import libcore.io.Libcore;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.net.InetAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.SocketException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
|
||||
/**
|
||||
* IpNeighborMonitor.
|
||||
*
|
||||
* Monitors the kernel rtnetlink neighbor notifications and presents to callers
|
||||
* NeighborEvents describing each event. Callers can provide a consumer instance
|
||||
* to both filter (e.g. by interface index and IP address) and handle the
|
||||
* generated NeighborEvents.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class IpNeighborMonitor extends PacketReader {
|
||||
private static final String TAG = IpNeighborMonitor.class.getSimpleName();
|
||||
private static final boolean DBG = false;
|
||||
private static final boolean VDBG = false;
|
||||
|
||||
/**
|
||||
* Make the kernel perform neighbor reachability detection (IPv4 ARP or IPv6 ND)
|
||||
* for the given IP address on the specified interface index.
|
||||
*
|
||||
* @return 0 if the request was successfully passed to the kernel; otherwise return
|
||||
* a non-zero error code.
|
||||
*/
|
||||
public static int startKernelNeighborProbe(int ifIndex, InetAddress ip) {
|
||||
final String msgSnippet = "probing ip=" + ip.getHostAddress() + "%" + ifIndex;
|
||||
if (DBG) { Log.d(TAG, msgSnippet); }
|
||||
|
||||
final byte[] msg = RtNetlinkNeighborMessage.newNewNeighborMessage(
|
||||
1, ip, StructNdMsg.NUD_PROBE, ifIndex, null);
|
||||
|
||||
try {
|
||||
NetlinkSocket.sendOneShotKernelMessage(OsConstants.NETLINK_ROUTE, msg);
|
||||
} catch (ErrnoException e) {
|
||||
Log.e(TAG, "Error " + msgSnippet + ": " + e);
|
||||
return -e.errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static class NeighborEvent {
|
||||
final long elapsedMs;
|
||||
final short msgType;
|
||||
final int ifindex;
|
||||
final InetAddress ip;
|
||||
final short nudState;
|
||||
final byte[] linkLayerAddr;
|
||||
|
||||
public NeighborEvent(long elapsedMs, short msgType, int ifindex, InetAddress ip,
|
||||
short nudState, byte[] linkLayerAddr) {
|
||||
this.elapsedMs = elapsedMs;
|
||||
this.msgType = msgType;
|
||||
this.ifindex = ifindex;
|
||||
this.ip = ip;
|
||||
this.nudState = nudState;
|
||||
this.linkLayerAddr = linkLayerAddr;
|
||||
}
|
||||
|
||||
boolean isConnected() {
|
||||
return (msgType != NetlinkConstants.RTM_DELNEIGH) &&
|
||||
StructNdMsg.isNudStateConnected(nudState);
|
||||
}
|
||||
|
||||
boolean isValid() {
|
||||
return (msgType != NetlinkConstants.RTM_DELNEIGH) &&
|
||||
StructNdMsg.isNudStateValid(nudState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringJoiner j = new StringJoiner(",", "NeighborEvent{", "}");
|
||||
return j.add("@" + elapsedMs)
|
||||
.add(NetlinkConstants.stringForNlMsgType(msgType))
|
||||
.add("if=" + ifindex)
|
||||
.add(ip.getHostAddress())
|
||||
.add(StructNdMsg.stringForNudState(nudState))
|
||||
.add("[" + NetlinkConstants.hexify(linkLayerAddr) + "]")
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public interface NeighborEventConsumer {
|
||||
// Every neighbor event received on the netlink socket is passed in
|
||||
// here. Subclasses should filter for events of interest.
|
||||
public void accept(NeighborEvent event);
|
||||
}
|
||||
|
||||
private final SharedLog mLog;
|
||||
private final NeighborEventConsumer mConsumer;
|
||||
|
||||
public IpNeighborMonitor(Handler h, SharedLog log, NeighborEventConsumer cb) {
|
||||
super(h, NetlinkSocket.DEFAULT_RECV_BUFSIZE);
|
||||
mLog = log.forSubComponent(TAG);
|
||||
mConsumer = (cb != null) ? cb : (event) -> { /* discard */ };
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FileDescriptor createFd() {
|
||||
FileDescriptor fd = null;
|
||||
|
||||
try {
|
||||
fd = NetlinkSocket.forProto(OsConstants.NETLINK_ROUTE);
|
||||
Os.bind(fd, (SocketAddress)(new NetlinkSocketAddress(0, OsConstants.RTMGRP_NEIGH)));
|
||||
Os.connect(fd, (SocketAddress)(new NetlinkSocketAddress(0, 0)));
|
||||
|
||||
if (VDBG) {
|
||||
final NetlinkSocketAddress nlAddr = (NetlinkSocketAddress) Os.getsockname(fd);
|
||||
Log.d(TAG, "bound to sockaddr_nl{"
|
||||
+ BitUtils.uint32(nlAddr.getPortId()) + ", "
|
||||
+ nlAddr.getGroupsMask()
|
||||
+ "}");
|
||||
}
|
||||
} catch (ErrnoException|SocketException e) {
|
||||
logError("Failed to create rtnetlink socket", e);
|
||||
IoUtils.closeQuietly(fd);
|
||||
return null;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handlePacket(byte[] recvbuf, int length) {
|
||||
final long whenMs = SystemClock.elapsedRealtime();
|
||||
|
||||
final ByteBuffer byteBuffer = ByteBuffer.wrap(recvbuf, 0, length);
|
||||
byteBuffer.order(ByteOrder.nativeOrder());
|
||||
|
||||
parseNetlinkMessageBuffer(byteBuffer, whenMs);
|
||||
}
|
||||
|
||||
private void parseNetlinkMessageBuffer(ByteBuffer byteBuffer, long whenMs) {
|
||||
while (byteBuffer.remaining() > 0) {
|
||||
final int position = byteBuffer.position();
|
||||
final NetlinkMessage nlMsg = NetlinkMessage.parse(byteBuffer);
|
||||
if (nlMsg == null || nlMsg.getHeader() == null) {
|
||||
byteBuffer.position(position);
|
||||
mLog.e("unparsable netlink msg: " + NetlinkConstants.hexify(byteBuffer));
|
||||
break;
|
||||
}
|
||||
|
||||
final int srcPortId = nlMsg.getHeader().nlmsg_pid;
|
||||
if (srcPortId != 0) {
|
||||
mLog.e("non-kernel source portId: " + BitUtils.uint32(srcPortId));
|
||||
break;
|
||||
}
|
||||
|
||||
if (nlMsg instanceof NetlinkErrorMessage) {
|
||||
mLog.e("netlink error: " + nlMsg);
|
||||
continue;
|
||||
} else if (!(nlMsg instanceof RtNetlinkNeighborMessage)) {
|
||||
mLog.i("non-rtnetlink neighbor msg: " + nlMsg);
|
||||
continue;
|
||||
}
|
||||
|
||||
evaluateRtNetlinkNeighborMessage((RtNetlinkNeighborMessage) nlMsg, whenMs);
|
||||
}
|
||||
}
|
||||
|
||||
private void evaluateRtNetlinkNeighborMessage(
|
||||
RtNetlinkNeighborMessage neighMsg, long whenMs) {
|
||||
final short msgType = neighMsg.getHeader().nlmsg_type;
|
||||
final StructNdMsg ndMsg = neighMsg.getNdHeader();
|
||||
if (ndMsg == null) {
|
||||
mLog.e("RtNetlinkNeighborMessage without ND message header!");
|
||||
return;
|
||||
}
|
||||
|
||||
final int ifindex = ndMsg.ndm_ifindex;
|
||||
final InetAddress destination = neighMsg.getDestination();
|
||||
final short nudState =
|
||||
(msgType == NetlinkConstants.RTM_DELNEIGH)
|
||||
? StructNdMsg.NUD_NONE
|
||||
: ndMsg.ndm_state;
|
||||
|
||||
final NeighborEvent event = new NeighborEvent(
|
||||
whenMs, msgType, ifindex, destination, nudState, neighMsg.getLinkLayerAddress());
|
||||
|
||||
if (VDBG) {
|
||||
Log.d(TAG, neighMsg.toString());
|
||||
}
|
||||
if (DBG) {
|
||||
Log.d(TAG, event.toString());
|
||||
}
|
||||
|
||||
mConsumer.accept(event);
|
||||
}
|
||||
}
|
||||
@@ -22,30 +22,27 @@ import android.net.LinkProperties;
|
||||
import android.net.LinkProperties.ProvisioningChange;
|
||||
import android.net.ProxyInfo;
|
||||
import android.net.RouteInfo;
|
||||
import android.net.ip.IpNeighborMonitor.NeighborEvent;
|
||||
import android.net.metrics.IpConnectivityLog;
|
||||
import android.net.metrics.IpReachabilityEvent;
|
||||
import android.net.netlink.NetlinkConstants;
|
||||
import android.net.netlink.NetlinkErrorMessage;
|
||||
import android.net.netlink.NetlinkMessage;
|
||||
import android.net.netlink.NetlinkSocket;
|
||||
import android.net.netlink.RtNetlinkNeighborMessage;
|
||||
import android.net.netlink.StructNdMsg;
|
||||
import android.net.netlink.StructNdaCacheInfo;
|
||||
import android.net.netlink.StructNlMsgHdr;
|
||||
import android.net.util.MultinetworkPolicyTracker;
|
||||
import android.net.util.SharedLog;
|
||||
import android.os.Handler;
|
||||
import android.os.PowerManager;
|
||||
import android.os.PowerManager.WakeLock;
|
||||
import android.os.SystemClock;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.NetlinkSocketAddress;
|
||||
import android.system.OsConstants;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.DumpUtils;
|
||||
import com.android.internal.util.DumpUtils.Dump;
|
||||
|
||||
import java.io.InterruptedIOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
@@ -134,6 +131,8 @@ import java.util.Set;
|
||||
* state it may be best for the link to disconnect completely and
|
||||
* reconnect afresh.
|
||||
*
|
||||
* Accessing an instance of this class from multiple threads is NOT safe.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class IpReachabilityMonitor {
|
||||
@@ -169,64 +168,33 @@ public class IpReachabilityMonitor {
|
||||
}
|
||||
}
|
||||
|
||||
private final Object mLock = new Object();
|
||||
private final String mInterfaceName;
|
||||
private final int mInterfaceIndex;
|
||||
private final IpNeighborMonitor mIpNeighborMonitor;
|
||||
private final SharedLog mLog;
|
||||
private final Callback mCallback;
|
||||
private final Dependencies mDependencies;
|
||||
private final MultinetworkPolicyTracker mMultinetworkPolicyTracker;
|
||||
private final NetlinkSocketObserver mNetlinkSocketObserver;
|
||||
private final Thread mObserverThread;
|
||||
private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
|
||||
@GuardedBy("mLock")
|
||||
private LinkProperties mLinkProperties = new LinkProperties();
|
||||
// TODO: consider a map to a private NeighborState class holding more
|
||||
// information than a single NUD state entry.
|
||||
@GuardedBy("mLock")
|
||||
private Map<InetAddress, Short> mIpWatchList = new HashMap<>();
|
||||
@GuardedBy("mLock")
|
||||
private int mIpWatchListVersion;
|
||||
private volatile boolean mRunning;
|
||||
private Map<InetAddress, NeighborEvent> mNeighborWatchList = new HashMap<>();
|
||||
// Time in milliseconds of the last forced probe request.
|
||||
private volatile long mLastProbeTimeMs;
|
||||
|
||||
/**
|
||||
* Make the kernel perform neighbor reachability detection (IPv4 ARP or IPv6 ND)
|
||||
* for the given IP address on the specified interface index.
|
||||
*
|
||||
* @return 0 if the request was successfully passed to the kernel; otherwise return
|
||||
* a non-zero error code.
|
||||
*/
|
||||
private static int probeNeighbor(int ifIndex, InetAddress ip) {
|
||||
final String msgSnippet = "probing ip=" + ip.getHostAddress() + "%" + ifIndex;
|
||||
if (DBG) { Log.d(TAG, msgSnippet); }
|
||||
|
||||
final byte[] msg = RtNetlinkNeighborMessage.newNewNeighborMessage(
|
||||
1, ip, StructNdMsg.NUD_PROBE, ifIndex, null);
|
||||
|
||||
try {
|
||||
NetlinkSocket.sendOneShotKernelMessage(OsConstants.NETLINK_ROUTE, msg);
|
||||
} catch (ErrnoException e) {
|
||||
Log.e(TAG, "Error " + msgSnippet + ": " + e);
|
||||
return -e.errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
public IpReachabilityMonitor(
|
||||
Context context, String ifName, Handler h, SharedLog log, Callback callback) {
|
||||
this(context, ifName, h, log, callback, null);
|
||||
}
|
||||
|
||||
public IpReachabilityMonitor(Context context, String ifName, SharedLog log, Callback callback) {
|
||||
this(context, ifName, log, callback, null);
|
||||
}
|
||||
|
||||
public IpReachabilityMonitor(Context context, String ifName, SharedLog log, Callback callback,
|
||||
public IpReachabilityMonitor(
|
||||
Context context, String ifName, Handler h, SharedLog log, Callback callback,
|
||||
MultinetworkPolicyTracker tracker) {
|
||||
this(ifName, getInterfaceIndex(ifName), log, callback, tracker,
|
||||
this(ifName, getInterfaceIndex(ifName), h, log, callback, tracker,
|
||||
Dependencies.makeDefault(context, ifName));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
IpReachabilityMonitor(String ifName, int ifIndex, SharedLog log, Callback callback,
|
||||
IpReachabilityMonitor(String ifName, int ifIndex, Handler h, SharedLog log, Callback callback,
|
||||
MultinetworkPolicyTracker tracker, Dependencies dependencies) {
|
||||
mInterfaceName = ifName;
|
||||
mLog = log.forSubComponent(TAG);
|
||||
@@ -234,47 +202,56 @@ public class IpReachabilityMonitor {
|
||||
mMultinetworkPolicyTracker = tracker;
|
||||
mInterfaceIndex = ifIndex;
|
||||
mDependencies = dependencies;
|
||||
mNetlinkSocketObserver = new NetlinkSocketObserver();
|
||||
mObserverThread = new Thread(mNetlinkSocketObserver);
|
||||
mObserverThread.start();
|
||||
|
||||
mIpNeighborMonitor = new IpNeighborMonitor(h, mLog,
|
||||
(NeighborEvent event) -> {
|
||||
if (mInterfaceIndex != event.ifindex) return;
|
||||
if (!mNeighborWatchList.containsKey(event.ip)) return;
|
||||
|
||||
final NeighborEvent prev = mNeighborWatchList.put(event.ip, event);
|
||||
|
||||
// TODO: Consider what to do with other states that are not within
|
||||
// NeighborEvent#isValid() (i.e. NUD_NONE, NUD_INCOMPLETE).
|
||||
if (event.nudState == StructNdMsg.NUD_FAILED) {
|
||||
mLog.w("ALERT neighbor went from: " + prev + " to: " + event);
|
||||
handleNeighborLost(event);
|
||||
}
|
||||
});
|
||||
mIpNeighborMonitor.start();
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
mRunning = false;
|
||||
mIpNeighborMonitor.stop();
|
||||
clearLinkProperties();
|
||||
mNetlinkSocketObserver.clearNetlinkSocket();
|
||||
}
|
||||
|
||||
// TODO: add a public dump() method that can be called during a bug report.
|
||||
public void dump(PrintWriter pw) {
|
||||
DumpUtils.dumpAsync(
|
||||
mIpNeighborMonitor.getHandler(),
|
||||
new Dump() {
|
||||
@Override
|
||||
public void dump(PrintWriter pw, String prefix) {
|
||||
pw.println(describeWatchList("\n"));
|
||||
}
|
||||
},
|
||||
pw, "", 1000);
|
||||
}
|
||||
|
||||
private String describeWatchList() {
|
||||
final String delimiter = ", ";
|
||||
StringBuilder sb = new StringBuilder();
|
||||
synchronized (mLock) {
|
||||
sb.append("iface{" + mInterfaceName + "/" + mInterfaceIndex + "}, ");
|
||||
sb.append("v{" + mIpWatchListVersion + "}, ");
|
||||
sb.append("ntable=[");
|
||||
boolean firstTime = true;
|
||||
for (Map.Entry<InetAddress, Short> entry : mIpWatchList.entrySet()) {
|
||||
if (firstTime) {
|
||||
firstTime = false;
|
||||
} else {
|
||||
sb.append(delimiter);
|
||||
}
|
||||
sb.append(entry.getKey().getHostAddress() + "/" +
|
||||
StructNdMsg.stringForNudState(entry.getValue()));
|
||||
}
|
||||
sb.append("]");
|
||||
private String describeWatchList() { return describeWatchList(" "); }
|
||||
|
||||
private String describeWatchList(String sep) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("iface{" + mInterfaceName + "/" + mInterfaceIndex + "}," + sep);
|
||||
sb.append("ntable=[" + sep);
|
||||
String delimiter = "";
|
||||
for (Map.Entry<InetAddress, NeighborEvent> entry : mNeighborWatchList.entrySet()) {
|
||||
sb.append(delimiter).append(entry.getKey().getHostAddress() + "/" + entry.getValue());
|
||||
delimiter = "," + sep;
|
||||
}
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private boolean isWatching(InetAddress ip) {
|
||||
synchronized (mLock) {
|
||||
return mRunning && mIpWatchList.containsKey(ip);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isOnLink(List<RouteInfo> routes, InetAddress ip) {
|
||||
for (RouteInfo route : routes) {
|
||||
if (!route.hasGateway() && route.matches(ip)) {
|
||||
@@ -284,13 +261,6 @@ public class IpReachabilityMonitor {
|
||||
return false;
|
||||
}
|
||||
|
||||
private short getNeighborStateLocked(InetAddress ip) {
|
||||
if (mIpWatchList.containsKey(ip)) {
|
||||
return mIpWatchList.get(ip);
|
||||
}
|
||||
return StructNdMsg.NUD_NONE;
|
||||
}
|
||||
|
||||
public void updateLinkProperties(LinkProperties lp) {
|
||||
if (!mInterfaceName.equals(lp.getInterfaceName())) {
|
||||
// TODO: figure out whether / how to cope with interface changes.
|
||||
@@ -299,70 +269,63 @@ public class IpReachabilityMonitor {
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (mLock) {
|
||||
mLinkProperties = new LinkProperties(lp);
|
||||
Map<InetAddress, Short> newIpWatchList = new HashMap<>();
|
||||
mLinkProperties = new LinkProperties(lp);
|
||||
Map<InetAddress, NeighborEvent> newNeighborWatchList = new HashMap<>();
|
||||
|
||||
final List<RouteInfo> routes = mLinkProperties.getRoutes();
|
||||
for (RouteInfo route : routes) {
|
||||
if (route.hasGateway()) {
|
||||
InetAddress gw = route.getGateway();
|
||||
if (isOnLink(routes, gw)) {
|
||||
newIpWatchList.put(gw, getNeighborStateLocked(gw));
|
||||
}
|
||||
final List<RouteInfo> routes = mLinkProperties.getRoutes();
|
||||
for (RouteInfo route : routes) {
|
||||
if (route.hasGateway()) {
|
||||
InetAddress gw = route.getGateway();
|
||||
if (isOnLink(routes, gw)) {
|
||||
newNeighborWatchList.put(gw, mNeighborWatchList.getOrDefault(gw, null));
|
||||
}
|
||||
}
|
||||
|
||||
for (InetAddress nameserver : lp.getDnsServers()) {
|
||||
if (isOnLink(routes, nameserver)) {
|
||||
newIpWatchList.put(nameserver, getNeighborStateLocked(nameserver));
|
||||
}
|
||||
}
|
||||
|
||||
mIpWatchList = newIpWatchList;
|
||||
mIpWatchListVersion++;
|
||||
}
|
||||
|
||||
for (InetAddress dns : lp.getDnsServers()) {
|
||||
if (isOnLink(routes, dns)) {
|
||||
newNeighborWatchList.put(dns, mNeighborWatchList.getOrDefault(dns, null));
|
||||
}
|
||||
}
|
||||
|
||||
mNeighborWatchList = newNeighborWatchList;
|
||||
if (DBG) { Log.d(TAG, "watch: " + describeWatchList()); }
|
||||
}
|
||||
|
||||
public void clearLinkProperties() {
|
||||
synchronized (mLock) {
|
||||
mLinkProperties.clear();
|
||||
mIpWatchList.clear();
|
||||
mIpWatchListVersion++;
|
||||
}
|
||||
mLinkProperties.clear();
|
||||
mNeighborWatchList.clear();
|
||||
if (DBG) { Log.d(TAG, "clear: " + describeWatchList()); }
|
||||
}
|
||||
|
||||
private void handleNeighborLost(String msg) {
|
||||
private void handleNeighborLost(NeighborEvent event) {
|
||||
final LinkProperties whatIfLp = new LinkProperties(mLinkProperties);
|
||||
|
||||
InetAddress ip = null;
|
||||
final ProvisioningChange delta;
|
||||
synchronized (mLock) {
|
||||
LinkProperties whatIfLp = new LinkProperties(mLinkProperties);
|
||||
for (Map.Entry<InetAddress, NeighborEvent> entry : mNeighborWatchList.entrySet()) {
|
||||
// TODO: Consider using NeighborEvent#isValid() here; it's more
|
||||
// strict but may interact badly if other entries are somehow in
|
||||
// NUD_INCOMPLETE (say, during network attach).
|
||||
if (entry.getValue().nudState != StructNdMsg.NUD_FAILED) continue;
|
||||
|
||||
for (Map.Entry<InetAddress, Short> entry : mIpWatchList.entrySet()) {
|
||||
if (entry.getValue() != StructNdMsg.NUD_FAILED) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ip = entry.getKey();
|
||||
for (RouteInfo route : mLinkProperties.getRoutes()) {
|
||||
if (ip.equals(route.getGateway())) {
|
||||
whatIfLp.removeRoute(route);
|
||||
}
|
||||
}
|
||||
|
||||
if (avoidingBadLinks() || !(ip instanceof Inet6Address)) {
|
||||
// We should do this unconditionally, but alas we cannot: b/31827713.
|
||||
whatIfLp.removeDnsServer(ip);
|
||||
ip = entry.getKey();
|
||||
for (RouteInfo route : mLinkProperties.getRoutes()) {
|
||||
if (ip.equals(route.getGateway())) {
|
||||
whatIfLp.removeRoute(route);
|
||||
}
|
||||
}
|
||||
|
||||
delta = LinkProperties.compareProvisioning(mLinkProperties, whatIfLp);
|
||||
if (avoidingBadLinks() || !(ip instanceof Inet6Address)) {
|
||||
// We should do this unconditionally, but alas we cannot: b/31827713.
|
||||
whatIfLp.removeDnsServer(ip);
|
||||
}
|
||||
}
|
||||
|
||||
final ProvisioningChange delta = LinkProperties.compareProvisioning(
|
||||
mLinkProperties, whatIfLp);
|
||||
|
||||
if (delta == ProvisioningChange.LOST_PROVISIONING) {
|
||||
final String logMsg = "FAILURE: LOST_PROVISIONING, " + msg;
|
||||
final String logMsg = "FAILURE: LOST_PROVISIONING, " + event;
|
||||
Log.w(TAG, logMsg);
|
||||
if (mCallback != null) {
|
||||
// TODO: remove |ip| when the callback signature no longer has
|
||||
@@ -378,12 +341,9 @@ public class IpReachabilityMonitor {
|
||||
}
|
||||
|
||||
public void probeAll() {
|
||||
final List<InetAddress> ipProbeList;
|
||||
synchronized (mLock) {
|
||||
ipProbeList = new ArrayList<>(mIpWatchList.keySet());
|
||||
}
|
||||
final List<InetAddress> ipProbeList = new ArrayList<>(mNeighborWatchList.keySet());
|
||||
|
||||
if (!ipProbeList.isEmpty() && mRunning) {
|
||||
if (!ipProbeList.isEmpty()) {
|
||||
// Keep the CPU awake long enough to allow all ARP/ND
|
||||
// probes a reasonable chance at success. See b/23197666.
|
||||
//
|
||||
@@ -394,13 +354,10 @@ public class IpReachabilityMonitor {
|
||||
}
|
||||
|
||||
for (InetAddress target : ipProbeList) {
|
||||
if (!mRunning) {
|
||||
break;
|
||||
}
|
||||
final int returnValue = probeNeighbor(mInterfaceIndex, target);
|
||||
final int rval = IpNeighborMonitor.startKernelNeighborProbe(mInterfaceIndex, target);
|
||||
mLog.log(String.format("put neighbor %s into NUD_PROBE state (rval=%d)",
|
||||
target.getHostAddress(), returnValue));
|
||||
logEvent(IpReachabilityEvent.PROBE, returnValue);
|
||||
target.getHostAddress(), rval));
|
||||
logEvent(IpReachabilityEvent.PROBE, rval);
|
||||
}
|
||||
mLastProbeTimeMs = SystemClock.elapsedRealtime();
|
||||
}
|
||||
@@ -446,153 +403,4 @@ public class IpReachabilityMonitor {
|
||||
int eventType = IpReachabilityEvent.nudFailureEventType(isFromProbe, isProvisioningLost);
|
||||
mMetricsLog.log(mInterfaceName, new IpReachabilityEvent(eventType));
|
||||
}
|
||||
|
||||
// TODO: simplify the number of objects by making this extend Thread.
|
||||
private final class NetlinkSocketObserver implements Runnable {
|
||||
private NetlinkSocket mSocket;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (VDBG) { Log.d(TAG, "Starting observing thread."); }
|
||||
mRunning = true;
|
||||
|
||||
try {
|
||||
setupNetlinkSocket();
|
||||
} catch (ErrnoException | SocketException e) {
|
||||
Log.e(TAG, "Failed to suitably initialize a netlink socket", e);
|
||||
mRunning = false;
|
||||
}
|
||||
|
||||
while (mRunning) {
|
||||
final ByteBuffer byteBuffer;
|
||||
try {
|
||||
byteBuffer = recvKernelReply();
|
||||
} catch (ErrnoException e) {
|
||||
if (mRunning) { Log.w(TAG, "ErrnoException: ", e); }
|
||||
break;
|
||||
}
|
||||
final long whenMs = SystemClock.elapsedRealtime();
|
||||
if (byteBuffer == null) {
|
||||
continue;
|
||||
}
|
||||
parseNetlinkMessageBuffer(byteBuffer, whenMs);
|
||||
}
|
||||
|
||||
clearNetlinkSocket();
|
||||
|
||||
mRunning = false; // Not a no-op when ErrnoException happened.
|
||||
if (VDBG) { Log.d(TAG, "Finishing observing thread."); }
|
||||
}
|
||||
|
||||
private void clearNetlinkSocket() {
|
||||
if (mSocket != null) {
|
||||
mSocket.close();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Refactor the main loop to recreate the socket upon recoverable errors.
|
||||
private void setupNetlinkSocket() throws ErrnoException, SocketException {
|
||||
clearNetlinkSocket();
|
||||
mSocket = new NetlinkSocket(OsConstants.NETLINK_ROUTE);
|
||||
|
||||
final NetlinkSocketAddress listenAddr = new NetlinkSocketAddress(
|
||||
0, OsConstants.RTMGRP_NEIGH);
|
||||
mSocket.bind(listenAddr);
|
||||
|
||||
if (VDBG) {
|
||||
final NetlinkSocketAddress nlAddr = mSocket.getLocalAddress();
|
||||
Log.d(TAG, "bound to sockaddr_nl{"
|
||||
+ ((long) (nlAddr.getPortId() & 0xffffffff)) + ", "
|
||||
+ nlAddr.getGroupsMask()
|
||||
+ "}");
|
||||
}
|
||||
}
|
||||
|
||||
private ByteBuffer recvKernelReply() throws ErrnoException {
|
||||
try {
|
||||
return mSocket.recvMessage(0);
|
||||
} catch (InterruptedIOException e) {
|
||||
// Interruption or other error, e.g. another thread closed our file descriptor.
|
||||
} catch (ErrnoException e) {
|
||||
if (e.errno != OsConstants.EAGAIN) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void parseNetlinkMessageBuffer(ByteBuffer byteBuffer, long whenMs) {
|
||||
while (byteBuffer.remaining() > 0) {
|
||||
final int position = byteBuffer.position();
|
||||
final NetlinkMessage nlMsg = NetlinkMessage.parse(byteBuffer);
|
||||
if (nlMsg == null || nlMsg.getHeader() == null) {
|
||||
byteBuffer.position(position);
|
||||
Log.e(TAG, "unparsable netlink msg: " + NetlinkConstants.hexify(byteBuffer));
|
||||
break;
|
||||
}
|
||||
|
||||
final int srcPortId = nlMsg.getHeader().nlmsg_pid;
|
||||
if (srcPortId != 0) {
|
||||
Log.e(TAG, "non-kernel source portId: " + ((long) (srcPortId & 0xffffffff)));
|
||||
break;
|
||||
}
|
||||
|
||||
if (nlMsg instanceof NetlinkErrorMessage) {
|
||||
Log.e(TAG, "netlink error: " + nlMsg);
|
||||
continue;
|
||||
} else if (!(nlMsg instanceof RtNetlinkNeighborMessage)) {
|
||||
if (DBG) {
|
||||
Log.d(TAG, "non-rtnetlink neighbor msg: " + nlMsg);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
evaluateRtNetlinkNeighborMessage((RtNetlinkNeighborMessage) nlMsg, whenMs);
|
||||
}
|
||||
}
|
||||
|
||||
private void evaluateRtNetlinkNeighborMessage(
|
||||
RtNetlinkNeighborMessage neighMsg, long whenMs) {
|
||||
final StructNdMsg ndMsg = neighMsg.getNdHeader();
|
||||
if (ndMsg == null || ndMsg.ndm_ifindex != mInterfaceIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
final InetAddress destination = neighMsg.getDestination();
|
||||
if (!isWatching(destination)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final short msgType = neighMsg.getHeader().nlmsg_type;
|
||||
final short nudState = ndMsg.ndm_state;
|
||||
final String eventMsg = "NeighborEvent{"
|
||||
+ "elapsedMs=" + whenMs + ", "
|
||||
+ destination.getHostAddress() + ", "
|
||||
+ "[" + NetlinkConstants.hexify(neighMsg.getLinkLayerAddress()) + "], "
|
||||
+ NetlinkConstants.stringForNlMsgType(msgType) + ", "
|
||||
+ StructNdMsg.stringForNudState(nudState)
|
||||
+ "}";
|
||||
|
||||
if (VDBG) {
|
||||
Log.d(TAG, neighMsg.toString());
|
||||
} else if (DBG) {
|
||||
Log.d(TAG, eventMsg);
|
||||
}
|
||||
|
||||
synchronized (mLock) {
|
||||
if (mIpWatchList.containsKey(destination)) {
|
||||
final short value =
|
||||
(msgType == NetlinkConstants.RTM_DELNEIGH)
|
||||
? StructNdMsg.NUD_NONE
|
||||
: nudState;
|
||||
mIpWatchList.put(destination, value);
|
||||
}
|
||||
}
|
||||
|
||||
if (nudState == StructNdMsg.NUD_FAILED) {
|
||||
Log.w(TAG, "ALERT: " + eventMsg);
|
||||
handleNeighborLost(eventMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,16 +16,24 @@
|
||||
|
||||
package android.net.netlink;
|
||||
|
||||
import static android.system.OsConstants.AF_NETLINK;
|
||||
import static android.system.OsConstants.EIO;
|
||||
import static android.system.OsConstants.EPROTO;
|
||||
import static android.system.OsConstants.ETIMEDOUT;
|
||||
import static android.system.OsConstants.SO_RCVBUF;
|
||||
import static android.system.OsConstants.SO_RCVTIMEO;
|
||||
import static android.system.OsConstants.SO_SNDTIMEO;
|
||||
import static android.system.OsConstants.SOCK_DGRAM;
|
||||
import static android.system.OsConstants.SOL_SOCKET;
|
||||
|
||||
import android.system.ErrnoException;
|
||||
import android.system.NetlinkSocketAddress;
|
||||
import android.system.Os;
|
||||
import android.system.OsConstants;
|
||||
import android.system.StructTimeval;
|
||||
import android.util.Log;
|
||||
import libcore.io.IoUtils;
|
||||
import libcore.io.Libcore;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.net.SocketAddress;
|
||||
@@ -37,28 +45,27 @@ import java.nio.ByteOrder;
|
||||
/**
|
||||
* NetlinkSocket
|
||||
*
|
||||
* A small wrapper class to assist with AF_NETLINK socket operations.
|
||||
* A small static class to assist with AF_NETLINK socket operations.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class NetlinkSocket implements Closeable {
|
||||
public class NetlinkSocket {
|
||||
private static final String TAG = "NetlinkSocket";
|
||||
private static final int SOCKET_RECV_BUFSIZE = 64 * 1024;
|
||||
private static final int DEFAULT_RECV_BUFSIZE = 8 * 1024;
|
||||
|
||||
final private FileDescriptor mDescriptor;
|
||||
private NetlinkSocketAddress mAddr;
|
||||
private long mLastRecvTimeoutMs;
|
||||
private long mLastSendTimeoutMs;
|
||||
public static final int DEFAULT_RECV_BUFSIZE = 8 * 1024;
|
||||
public static final int SOCKET_RECV_BUFSIZE = 64 * 1024;
|
||||
|
||||
public static void sendOneShotKernelMessage(int nlProto, byte[] msg) throws ErrnoException {
|
||||
final String errPrefix = "Error in NetlinkSocket.sendOneShotKernelMessage";
|
||||
final long IO_TIMEOUT = 300L;
|
||||
|
||||
try (NetlinkSocket nlSocket = new NetlinkSocket(nlProto)) {
|
||||
final long IO_TIMEOUT = 300L;
|
||||
nlSocket.connectToKernel();
|
||||
nlSocket.sendMessage(msg, 0, msg.length, IO_TIMEOUT);
|
||||
final ByteBuffer bytes = nlSocket.recvMessage(IO_TIMEOUT);
|
||||
FileDescriptor fd;
|
||||
|
||||
try {
|
||||
fd = forProto(nlProto);
|
||||
connectToKernel(fd);
|
||||
sendMessage(fd, msg, 0, msg.length, IO_TIMEOUT);
|
||||
final ByteBuffer bytes = recvMessage(fd, DEFAULT_RECV_BUFSIZE, IO_TIMEOUT);
|
||||
// recvMessage() guaranteed to not return null if it did not throw.
|
||||
final NetlinkMessage response = NetlinkMessage.parse(bytes);
|
||||
if (response != null && response instanceof NetlinkErrorMessage &&
|
||||
@@ -81,61 +88,30 @@ public class NetlinkSocket implements Closeable {
|
||||
errmsg = response.toString();
|
||||
}
|
||||
Log.e(TAG, errPrefix + ", errmsg=" + errmsg);
|
||||
throw new ErrnoException(errmsg, OsConstants.EPROTO);
|
||||
throw new ErrnoException(errmsg, EPROTO);
|
||||
}
|
||||
} catch (InterruptedIOException e) {
|
||||
Log.e(TAG, errPrefix, e);
|
||||
throw new ErrnoException(errPrefix, OsConstants.ETIMEDOUT, e);
|
||||
throw new ErrnoException(errPrefix, ETIMEDOUT, e);
|
||||
} catch (SocketException e) {
|
||||
Log.e(TAG, errPrefix, e);
|
||||
throw new ErrnoException(errPrefix, OsConstants.EIO, e);
|
||||
throw new ErrnoException(errPrefix, EIO, e);
|
||||
}
|
||||
|
||||
IoUtils.closeQuietly(fd);
|
||||
}
|
||||
|
||||
public NetlinkSocket(int nlProto) throws ErrnoException {
|
||||
mDescriptor = Os.socket(
|
||||
OsConstants.AF_NETLINK, OsConstants.SOCK_DGRAM, nlProto);
|
||||
|
||||
Os.setsockoptInt(
|
||||
mDescriptor, OsConstants.SOL_SOCKET,
|
||||
OsConstants.SO_RCVBUF, SOCKET_RECV_BUFSIZE);
|
||||
public static FileDescriptor forProto(int nlProto) throws ErrnoException {
|
||||
final FileDescriptor fd = Os.socket(AF_NETLINK, SOCK_DGRAM, nlProto);
|
||||
Os.setsockoptInt(fd, SOL_SOCKET, SO_RCVBUF, SOCKET_RECV_BUFSIZE);
|
||||
return fd;
|
||||
}
|
||||
|
||||
public NetlinkSocketAddress getLocalAddress() throws ErrnoException {
|
||||
return (NetlinkSocketAddress) Os.getsockname(mDescriptor);
|
||||
public static void connectToKernel(FileDescriptor fd) throws ErrnoException, SocketException {
|
||||
Os.connect(fd, (SocketAddress) (new NetlinkSocketAddress(0, 0)));
|
||||
}
|
||||
|
||||
public void bind(NetlinkSocketAddress localAddr) throws ErrnoException, SocketException {
|
||||
Os.bind(mDescriptor, (SocketAddress)localAddr);
|
||||
}
|
||||
|
||||
public void connectTo(NetlinkSocketAddress peerAddr)
|
||||
throws ErrnoException, SocketException {
|
||||
Os.connect(mDescriptor, (SocketAddress) peerAddr);
|
||||
}
|
||||
|
||||
public void connectToKernel() throws ErrnoException, SocketException {
|
||||
connectTo(new NetlinkSocketAddress(0, 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait indefinitely (or until underlying socket error) for a
|
||||
* netlink message of at most DEFAULT_RECV_BUFSIZE size.
|
||||
*/
|
||||
public ByteBuffer recvMessage()
|
||||
throws ErrnoException, InterruptedIOException {
|
||||
return recvMessage(DEFAULT_RECV_BUFSIZE, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait up to |timeoutMs| (or until underlying socket error) for a
|
||||
* netlink message of at most DEFAULT_RECV_BUFSIZE size.
|
||||
*/
|
||||
public ByteBuffer recvMessage(long timeoutMs) throws ErrnoException, InterruptedIOException {
|
||||
return recvMessage(DEFAULT_RECV_BUFSIZE, timeoutMs);
|
||||
}
|
||||
|
||||
private void checkTimeout(long timeoutMs) {
|
||||
private static void checkTimeout(long timeoutMs) {
|
||||
if (timeoutMs < 0) {
|
||||
throw new IllegalArgumentException("Negative timeouts not permitted");
|
||||
}
|
||||
@@ -147,21 +123,14 @@ public class NetlinkSocket implements Closeable {
|
||||
*
|
||||
* Multi-threaded calls with different timeouts will cause unexpected results.
|
||||
*/
|
||||
public ByteBuffer recvMessage(int bufsize, long timeoutMs)
|
||||
public static ByteBuffer recvMessage(FileDescriptor fd, int bufsize, long timeoutMs)
|
||||
throws ErrnoException, IllegalArgumentException, InterruptedIOException {
|
||||
checkTimeout(timeoutMs);
|
||||
|
||||
synchronized (mDescriptor) {
|
||||
if (mLastRecvTimeoutMs != timeoutMs) {
|
||||
Os.setsockoptTimeval(mDescriptor,
|
||||
OsConstants.SOL_SOCKET, OsConstants.SO_RCVTIMEO,
|
||||
StructTimeval.fromMillis(timeoutMs));
|
||||
mLastRecvTimeoutMs = timeoutMs;
|
||||
}
|
||||
}
|
||||
Os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, StructTimeval.fromMillis(timeoutMs));
|
||||
|
||||
ByteBuffer byteBuffer = ByteBuffer.allocate(bufsize);
|
||||
int length = Os.read(mDescriptor, byteBuffer);
|
||||
int length = Os.read(fd, byteBuffer);
|
||||
if (length == bufsize) {
|
||||
Log.w(TAG, "maximum read");
|
||||
}
|
||||
@@ -171,40 +140,17 @@ public class NetlinkSocket implements Closeable {
|
||||
return byteBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message to a peer to which this socket has previously connected.
|
||||
*
|
||||
* This blocks until completion or an error occurs.
|
||||
*/
|
||||
public boolean sendMessage(byte[] bytes, int offset, int count)
|
||||
throws ErrnoException, InterruptedIOException {
|
||||
return sendMessage(bytes, offset, count, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message to a peer to which this socket has previously connected,
|
||||
* waiting at most |timeoutMs| milliseconds for the send to complete.
|
||||
*
|
||||
* Multi-threaded calls with different timeouts will cause unexpected results.
|
||||
*/
|
||||
public boolean sendMessage(byte[] bytes, int offset, int count, long timeoutMs)
|
||||
public static int sendMessage(
|
||||
FileDescriptor fd, byte[] bytes, int offset, int count, long timeoutMs)
|
||||
throws ErrnoException, IllegalArgumentException, InterruptedIOException {
|
||||
checkTimeout(timeoutMs);
|
||||
|
||||
synchronized (mDescriptor) {
|
||||
if (mLastSendTimeoutMs != timeoutMs) {
|
||||
Os.setsockoptTimeval(mDescriptor,
|
||||
OsConstants.SOL_SOCKET, OsConstants.SO_SNDTIMEO,
|
||||
StructTimeval.fromMillis(timeoutMs));
|
||||
mLastSendTimeoutMs = timeoutMs;
|
||||
}
|
||||
}
|
||||
|
||||
return (count == Os.write(mDescriptor, bytes, offset, count));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
IoUtils.closeQuietly(mDescriptor);
|
||||
Os.setsockoptTimeval(fd, SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(timeoutMs));
|
||||
return Os.write(fd, bytes, offset, count);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +63,11 @@ public class StructNdMsg {
|
||||
return ((nudState & (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE)) != 0);
|
||||
}
|
||||
|
||||
public static boolean isNudStateValid(short nudState) {
|
||||
return (isNudStateConnected(nudState) ||
|
||||
((nudState & (NUD_PROBE|NUD_STALE|NUD_DELAY)) != 0));
|
||||
}
|
||||
|
||||
// Neighbor Cache Entry Flags
|
||||
public static byte NTF_USE = (byte) 0x01;
|
||||
public static byte NTF_SELF = (byte) 0x02;
|
||||
@@ -143,7 +148,7 @@ public class StructNdMsg {
|
||||
}
|
||||
|
||||
public boolean nudValid() {
|
||||
return (nudConnected() || ((ndm_state & (NUD_PROBE|NUD_STALE|NUD_DELAY)) != 0));
|
||||
return isNudStateValid(ndm_state);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -67,7 +67,7 @@ import java.io.IOException;
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public abstract class BlockingSocketReader {
|
||||
public abstract class PacketReader {
|
||||
private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR;
|
||||
private static final int UNREGISTER_THIS_FD = 0;
|
||||
|
||||
@@ -83,11 +83,11 @@ public abstract class BlockingSocketReader {
|
||||
IoUtils.closeQuietly(fd);
|
||||
}
|
||||
|
||||
protected BlockingSocketReader(Handler h) {
|
||||
protected PacketReader(Handler h) {
|
||||
this(h, DEFAULT_RECV_BUF_SIZE);
|
||||
}
|
||||
|
||||
protected BlockingSocketReader(Handler h, int recvbufsize) {
|
||||
protected PacketReader(Handler h, int recvbufsize) {
|
||||
mHandler = h;
|
||||
mQueue = mHandler.getLooper().getQueue();
|
||||
mPacket = new byte[Math.max(recvbufsize, DEFAULT_RECV_BUF_SIZE)];
|
||||
@@ -115,6 +115,8 @@ public abstract class BlockingSocketReader {
|
||||
}
|
||||
}
|
||||
|
||||
public Handler getHandler() { return mHandler; }
|
||||
|
||||
public final int recvBufSize() { return mPacket.length; }
|
||||
|
||||
public final long numPacketsReceived() { return mPacketsReceived; }
|
||||
@@ -18,10 +18,12 @@ package android.net.ip;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.anyInt;
|
||||
import static org.mockito.Mockito.anyString;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.net.util.SharedLog;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
@@ -42,14 +44,18 @@ public class IpReachabilityMonitorTest {
|
||||
@Mock IpReachabilityMonitor.Callback mCallback;
|
||||
@Mock IpReachabilityMonitor.Dependencies mDependencies;
|
||||
@Mock SharedLog mLog;
|
||||
Handler mHandler;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
when(mLog.forSubComponent(anyString())).thenReturn(mLog);
|
||||
mHandler = new Handler(Looper.getMainLooper());
|
||||
}
|
||||
|
||||
IpReachabilityMonitor makeMonitor() {
|
||||
return new IpReachabilityMonitor("fake0", 1, mLog, mCallback, null, mDependencies);
|
||||
return new IpReachabilityMonitor(
|
||||
"fake0", 1, mHandler, mLog, mCallback, null, mDependencies);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package android.net.netlink;
|
||||
|
||||
import static android.net.netlink.NetlinkSocket.DEFAULT_RECV_BUFSIZE;
|
||||
import static android.system.OsConstants.NETLINK_ROUTE;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
@@ -28,10 +30,12 @@ import android.support.test.runner.AndroidJUnit4;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.NetlinkSocketAddress;
|
||||
import android.system.OsConstants;
|
||||
import android.system.Os;
|
||||
import android.util.Log;
|
||||
import libcore.io.IoUtils;
|
||||
|
||||
import java.io.InterruptedIOException;
|
||||
import java.io.FileDescriptor;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
@@ -46,29 +50,28 @@ public class NetlinkSocketTest {
|
||||
|
||||
@Test
|
||||
public void testBasicWorkingGetNeighborsQuery() throws Exception {
|
||||
NetlinkSocket s = new NetlinkSocket(OsConstants.NETLINK_ROUTE);
|
||||
assertNotNull(s);
|
||||
final FileDescriptor fd = NetlinkSocket.forProto(NETLINK_ROUTE);
|
||||
assertNotNull(fd);
|
||||
|
||||
s.connectToKernel();
|
||||
NetlinkSocket.connectToKernel(fd);
|
||||
|
||||
NetlinkSocketAddress localAddr = s.getLocalAddress();
|
||||
final NetlinkSocketAddress localAddr = (NetlinkSocketAddress) Os.getsockname(fd);
|
||||
assertNotNull(localAddr);
|
||||
assertEquals(0, localAddr.getGroupsMask());
|
||||
assertTrue(0 != localAddr.getPortId());
|
||||
|
||||
final int TEST_SEQNO = 5;
|
||||
final byte[] request = RtNetlinkNeighborMessage.newGetNeighborsRequest(TEST_SEQNO);
|
||||
assertNotNull(request);
|
||||
final byte[] req = RtNetlinkNeighborMessage.newGetNeighborsRequest(TEST_SEQNO);
|
||||
assertNotNull(req);
|
||||
|
||||
final long TIMEOUT = 500;
|
||||
assertTrue(s.sendMessage(request, 0, request.length, TIMEOUT));
|
||||
assertEquals(req.length, NetlinkSocket.sendMessage(fd, req, 0, req.length, TIMEOUT));
|
||||
|
||||
int neighMessageCount = 0;
|
||||
int doneMessageCount = 0;
|
||||
|
||||
while (doneMessageCount == 0) {
|
||||
ByteBuffer response = null;
|
||||
response = s.recvMessage(TIMEOUT);
|
||||
ByteBuffer response = NetlinkSocket.recvMessage(fd, DEFAULT_RECV_BUFSIZE, TIMEOUT);
|
||||
assertNotNull(response);
|
||||
assertTrue(StructNlMsgHdr.STRUCT_SIZE <= response.limit());
|
||||
assertEquals(0, response.position());
|
||||
@@ -100,30 +103,6 @@ public class NetlinkSocketTest {
|
||||
// TODO: make sure this test passes sanely in airplane mode.
|
||||
assertTrue(neighMessageCount > 0);
|
||||
|
||||
s.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRepeatedCloseCallsAreQuiet() throws Exception {
|
||||
// Create a working NetlinkSocket.
|
||||
NetlinkSocket s = new NetlinkSocket(OsConstants.NETLINK_ROUTE);
|
||||
assertNotNull(s);
|
||||
s.connectToKernel();
|
||||
NetlinkSocketAddress localAddr = s.getLocalAddress();
|
||||
assertNotNull(localAddr);
|
||||
assertEquals(0, localAddr.getGroupsMask());
|
||||
assertTrue(0 != localAddr.getPortId());
|
||||
// Close once.
|
||||
s.close();
|
||||
// Test that it is closed.
|
||||
boolean expectedErrorSeen = false;
|
||||
try {
|
||||
localAddr = s.getLocalAddress();
|
||||
} catch (ErrnoException e) {
|
||||
expectedErrorSeen = true;
|
||||
}
|
||||
assertTrue(expectedErrorSeen);
|
||||
// Close once more.
|
||||
s.close();
|
||||
IoUtils.closeQuietly(fd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package android.net.util;
|
||||
|
||||
import static android.net.util.BlockingSocketReader.DEFAULT_RECV_BUF_SIZE;
|
||||
import static android.net.util.PacketReader.DEFAULT_RECV_BUF_SIZE;
|
||||
import static android.system.OsConstants.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
@@ -53,13 +53,13 @@ import org.junit.Test;
|
||||
import libcore.io.IoBridge;
|
||||
|
||||
/**
|
||||
* Tests for BlockingSocketReader.
|
||||
* Tests for PacketReader.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class BlockingSocketReaderTest {
|
||||
public class PacketReaderTest {
|
||||
static final InetAddress LOOPBACK6 = Inet6Address.getLoopbackAddress();
|
||||
static final StructTimeval TIMEO = StructTimeval.fromMillis(500);
|
||||
|
||||
@@ -69,9 +69,9 @@ public class BlockingSocketReaderTest {
|
||||
protected byte[] mLastRecvBuf;
|
||||
protected boolean mStopped;
|
||||
protected HandlerThread mHandlerThread;
|
||||
protected BlockingSocketReader mReceiver;
|
||||
protected PacketReader mReceiver;
|
||||
|
||||
class UdpLoopbackReader extends BlockingSocketReader {
|
||||
class UdpLoopbackReader extends PacketReader {
|
||||
public UdpLoopbackReader(Handler h) {
|
||||
super(h);
|
||||
}
|
||||
@@ -121,7 +121,7 @@ public class BlockingSocketReaderTest {
|
||||
mLastRecvBuf = null;
|
||||
mStopped = false;
|
||||
|
||||
mHandlerThread = new HandlerThread(BlockingSocketReaderTest.class.getSimpleName());
|
||||
mHandlerThread = new HandlerThread(PacketReaderTest.class.getSimpleName());
|
||||
mHandlerThread.start();
|
||||
}
|
||||
|
||||
@@ -188,8 +188,8 @@ public class BlockingSocketReaderTest {
|
||||
mReceiver = null;
|
||||
}
|
||||
|
||||
class NullBlockingSocketReader extends BlockingSocketReader {
|
||||
public NullBlockingSocketReader(Handler h, int recvbufsize) {
|
||||
class NullPacketReader extends PacketReader {
|
||||
public NullPacketReader(Handler h, int recvbufsize) {
|
||||
super(h, recvbufsize);
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ public class BlockingSocketReaderTest {
|
||||
final Handler h = mHandlerThread.getThreadHandler();
|
||||
|
||||
for (int i : new int[]{-1, 0, 1, DEFAULT_RECV_BUF_SIZE-1}) {
|
||||
final BlockingSocketReader b = new NullBlockingSocketReader(h, i);
|
||||
final PacketReader b = new NullPacketReader(h, i);
|
||||
assertEquals(DEFAULT_RECV_BUF_SIZE, b.recvBufSize());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user