Merge "Move static utils to the static library"
This commit is contained in:
commit
68bb44645b
12 changed files with 1689 additions and 2 deletions
|
|
@ -38,6 +38,7 @@ java_library {
|
|||
"device/com/android/net/module/util/FdEventsReader.java",
|
||||
"device/com/android/net/module/util/HexDump.java",
|
||||
"device/com/android/net/module/util/PacketReader.java",
|
||||
"device/com/android/net/module/util/SharedLog.java",
|
||||
// This library is used by system modules, for which the system health impact of Kotlin
|
||||
// has not yet been evaluated. Annotations may need jarjar'ing.
|
||||
// "src_devicecommon/**/*.kt",
|
||||
|
|
@ -95,7 +96,7 @@ filegroup {
|
|||
|
||||
java_library {
|
||||
name: "net-utils-device-common-netlink",
|
||||
// TODO: Ipv6Utils and Struct stuff could be separated out of th netlink library into
|
||||
// TODO: Ipv6Utils and Struct stuff could be separated out of the netlink library into
|
||||
// an individual Struct library, and remove the net-utils-framework-common lib dependency.
|
||||
// But there is no need doing this at the moment.
|
||||
srcs: [
|
||||
|
|
@ -124,6 +125,36 @@ java_library {
|
|||
],
|
||||
}
|
||||
|
||||
java_library {
|
||||
// TODO : this target should probably be folded into net-utils-device-common
|
||||
name: "net-utils-device-common-ip",
|
||||
srcs: [
|
||||
"device/com/android/net/module/util/ip/*.java",
|
||||
],
|
||||
sdk_version: "module_current",
|
||||
min_sdk_version: "29",
|
||||
visibility: [
|
||||
"//frameworks/libs/net/common/tests:__subpackages__",
|
||||
"//frameworks/libs/net/common/testutils:__subpackages__",
|
||||
"//packages/modules/Connectivity:__subpackages__",
|
||||
"//packages/modules/NetworkStack:__subpackages__",
|
||||
],
|
||||
libs: [
|
||||
"framework-annotations-lib",
|
||||
"framework-connectivity",
|
||||
],
|
||||
static_libs: [
|
||||
"net-utils-device-common",
|
||||
"net-utils-device-common-netlink",
|
||||
"net-utils-framework-common",
|
||||
"netd-client",
|
||||
],
|
||||
apex_available: [
|
||||
"com.android.tethering",
|
||||
"//apex_available:platform",
|
||||
],
|
||||
}
|
||||
|
||||
java_library {
|
||||
name: "net-utils-framework-common",
|
||||
srcs: [
|
||||
|
|
@ -205,7 +236,11 @@ filegroup {
|
|||
filegroup {
|
||||
name: "net-utils-framework-wifi-common-srcs",
|
||||
srcs: [
|
||||
"framework/com/android/net/module/util/**/*.java",
|
||||
"framework/com/android/net/module/util/DnsSdTxtRecord.java",
|
||||
"framework/com/android/net/module/util/Inet4AddressUtils.java",
|
||||
"framework/com/android/net/module/util/InetAddressUtils.java",
|
||||
"framework/com/android/net/module/util/MacAddressUtils.java",
|
||||
"framework/com/android/net/module/util/NetUtils.java",
|
||||
],
|
||||
path: "framework",
|
||||
visibility: [
|
||||
|
|
|
|||
232
staticlibs/device/com/android/net/module/util/SharedLog.java
Normal file
232
staticlibs/device/com/android/net/module/util/SharedLog.java
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
/*
|
||||
* 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 com.android.net.module.util;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
|
||||
/**
|
||||
* Class to centralize logging functionality for tethering.
|
||||
*
|
||||
* All access to class methods other than dump() must be on the same thread.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class SharedLog {
|
||||
private static final int DEFAULT_MAX_RECORDS = 500;
|
||||
private static final String COMPONENT_DELIMITER = ".";
|
||||
|
||||
private enum Category {
|
||||
NONE,
|
||||
ERROR,
|
||||
MARK,
|
||||
WARN,
|
||||
}
|
||||
|
||||
private final LocalLog mLocalLog;
|
||||
// The tag to use for output to the system log. This is not output to the
|
||||
// LocalLog because that would be redundant.
|
||||
private final String mTag;
|
||||
// The component (or subcomponent) of a system that is sharing this log.
|
||||
// This can grow in depth if components call forSubComponent() to obtain
|
||||
// their SharedLog instance. The tag is not included in the component for
|
||||
// brevity.
|
||||
private final String mComponent;
|
||||
|
||||
public SharedLog(String tag) {
|
||||
this(DEFAULT_MAX_RECORDS, tag);
|
||||
}
|
||||
|
||||
public SharedLog(int maxRecords, String tag) {
|
||||
this(new LocalLog(maxRecords), tag, tag);
|
||||
}
|
||||
|
||||
private SharedLog(LocalLog localLog, String tag, String component) {
|
||||
mLocalLog = localLog;
|
||||
mTag = tag;
|
||||
mComponent = component;
|
||||
}
|
||||
|
||||
public String getTag() {
|
||||
return mTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a SharedLog based on this log with an additional component prefix on each logged line.
|
||||
*/
|
||||
public SharedLog forSubComponent(String component) {
|
||||
if (!isRootLogInstance()) {
|
||||
component = mComponent + COMPONENT_DELIMITER + component;
|
||||
}
|
||||
return new SharedLog(mLocalLog, mTag, component);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump the contents of this log.
|
||||
*
|
||||
* <p>This method may be called on any thread.
|
||||
*/
|
||||
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
|
||||
mLocalLog.dump(writer);
|
||||
}
|
||||
|
||||
//////
|
||||
// Methods that both log an entry and emit it to the system log.
|
||||
//////
|
||||
|
||||
/**
|
||||
* Log an error due to an exception. This does not include the exception stacktrace.
|
||||
*
|
||||
* <p>The log entry will be also added to the system log.
|
||||
* @see #e(String, Throwable)
|
||||
*/
|
||||
public void e(Exception e) {
|
||||
Log.e(mTag, record(Category.ERROR, e.toString()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Log an error message.
|
||||
*
|
||||
* <p>The log entry will be also added to the system log.
|
||||
*/
|
||||
public void e(String msg) {
|
||||
Log.e(mTag, record(Category.ERROR, msg));
|
||||
}
|
||||
|
||||
/**
|
||||
* Log an error due to an exception, with the exception stacktrace if provided.
|
||||
*
|
||||
* <p>The error and exception message appear in the shared log, but the stacktrace is only
|
||||
* logged in general log output (logcat). The log entry will be also added to the system log.
|
||||
*/
|
||||
public void e(@NonNull String msg, @Nullable Throwable exception) {
|
||||
if (exception == null) {
|
||||
e(msg);
|
||||
return;
|
||||
}
|
||||
Log.e(mTag, record(Category.ERROR, msg + ": " + exception.getMessage()), exception);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log an informational message.
|
||||
*
|
||||
* <p>The log entry will be also added to the system log.
|
||||
*/
|
||||
public void i(String msg) {
|
||||
Log.i(mTag, record(Category.NONE, msg));
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a warning message.
|
||||
*
|
||||
* <p>The log entry will be also added to the system log.
|
||||
*/
|
||||
public void w(String msg) {
|
||||
Log.w(mTag, record(Category.WARN, msg));
|
||||
}
|
||||
|
||||
//////
|
||||
// Methods that only log an entry (and do NOT emit to the system log).
|
||||
//////
|
||||
|
||||
/**
|
||||
* Log a general message to be only included in the in-memory log.
|
||||
*
|
||||
* <p>The log entry will *not* be added to the system log.
|
||||
*/
|
||||
public void log(String msg) {
|
||||
record(Category.NONE, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a general, formatted message to be only included in the in-memory log.
|
||||
*
|
||||
* <p>The log entry will *not* be added to the system log.
|
||||
* @see String#format(String, Object...)
|
||||
*/
|
||||
public void logf(String fmt, Object... args) {
|
||||
log(String.format(fmt, args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a message with MARK level.
|
||||
*
|
||||
* <p>The log entry will *not* be added to the system log.
|
||||
*/
|
||||
public void mark(String msg) {
|
||||
record(Category.MARK, msg);
|
||||
}
|
||||
|
||||
private String record(Category category, String msg) {
|
||||
final String entry = logLine(category, msg);
|
||||
mLocalLog.append(entry);
|
||||
return entry;
|
||||
}
|
||||
|
||||
private String logLine(Category category, String msg) {
|
||||
final StringJoiner sj = new StringJoiner(" ");
|
||||
if (!isRootLogInstance()) sj.add("[" + mComponent + "]");
|
||||
if (category != Category.NONE) sj.add(category.toString());
|
||||
return sj.add(msg).toString();
|
||||
}
|
||||
|
||||
// Check whether this SharedLog instance is nominally the top level in
|
||||
// a potential hierarchy of shared logs (the root of a tree),
|
||||
// or is a subcomponent within the hierarchy.
|
||||
private boolean isRootLogInstance() {
|
||||
return TextUtils.isEmpty(mComponent) || mComponent.equals(mTag);
|
||||
}
|
||||
|
||||
private static final class LocalLog {
|
||||
private final Deque<String> mLog;
|
||||
private final int mMaxLines;
|
||||
|
||||
LocalLog(int maxLines) {
|
||||
mMaxLines = Math.max(0, maxLines);
|
||||
mLog = new ArrayDeque<>(mMaxLines);
|
||||
}
|
||||
|
||||
synchronized void append(String logLine) {
|
||||
if (mMaxLines <= 0) return;
|
||||
while (mLog.size() >= mMaxLines) {
|
||||
mLog.remove();
|
||||
}
|
||||
mLog.add(LocalDateTime.now() + " - " + logLine);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps the content of local log to print writer with each log entry
|
||||
*
|
||||
* @param pw printer writer to write into
|
||||
*/
|
||||
synchronized void dump(PrintWriter pw) {
|
||||
for (final String s : mLog) {
|
||||
pw.println(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,203 @@
|
|||
/*
|
||||
* Copyright (C) 2020 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 com.android.net.module.util.ip;
|
||||
|
||||
import static com.android.net.module.util.netlink.ConntrackMessage.DYING_MASK;
|
||||
import static com.android.net.module.util.netlink.ConntrackMessage.ESTABLISHED_MASK;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.os.Handler;
|
||||
import android.system.OsConstants;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.net.module.util.SharedLog;
|
||||
import com.android.net.module.util.netlink.ConntrackMessage;
|
||||
import com.android.net.module.util.netlink.NetlinkConstants;
|
||||
import com.android.net.module.util.netlink.NetlinkMessage;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
|
||||
/**
|
||||
* ConntrackMonitor.
|
||||
*
|
||||
* Monitors the netfilter conntrack notifications and presents to callers
|
||||
* ConntrackEvents describing each event.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class ConntrackMonitor extends NetlinkMonitor {
|
||||
private static final String TAG = ConntrackMonitor.class.getSimpleName();
|
||||
private static final boolean DBG = false;
|
||||
private static final boolean VDBG = false;
|
||||
|
||||
// Reference kernel/uapi/linux/netfilter/nfnetlink_compat.h
|
||||
public static final int NF_NETLINK_CONNTRACK_NEW = 1;
|
||||
public static final int NF_NETLINK_CONNTRACK_UPDATE = 2;
|
||||
public static final int NF_NETLINK_CONNTRACK_DESTROY = 4;
|
||||
|
||||
// The socket receive buffer size in bytes. If too many conntrack messages are sent too
|
||||
// quickly, the conntrack messages can overflow the socket receive buffer. This can happen
|
||||
// if too many connections are disconnected by losing network and so on. Use a large-enough
|
||||
// buffer to avoid the error ENOBUFS while listening to the conntrack messages.
|
||||
private static final int SOCKET_RECV_BUFSIZE = 6 * 1024 * 1024;
|
||||
|
||||
/**
|
||||
* A class for describing parsed netfilter conntrack events.
|
||||
*/
|
||||
public static class ConntrackEvent {
|
||||
/**
|
||||
* Conntrack event type.
|
||||
*/
|
||||
public final short msgType;
|
||||
/**
|
||||
* Original direction conntrack tuple.
|
||||
*/
|
||||
public final ConntrackMessage.Tuple tupleOrig;
|
||||
/**
|
||||
* Reply direction conntrack tuple.
|
||||
*/
|
||||
public final ConntrackMessage.Tuple tupleReply;
|
||||
/**
|
||||
* Connection status. A bitmask of ip_conntrack_status enum flags.
|
||||
*/
|
||||
public final int status;
|
||||
/**
|
||||
* Conntrack timeout.
|
||||
*/
|
||||
public final int timeoutSec;
|
||||
|
||||
public ConntrackEvent(ConntrackMessage msg) {
|
||||
this.msgType = msg.getHeader().nlmsg_type;
|
||||
this.tupleOrig = msg.tupleOrig;
|
||||
this.tupleReply = msg.tupleReply;
|
||||
this.status = msg.status;
|
||||
this.timeoutSec = msg.timeoutSec;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public ConntrackEvent(short msgType, ConntrackMessage.Tuple tupleOrig,
|
||||
ConntrackMessage.Tuple tupleReply, int status, int timeoutSec) {
|
||||
this.msgType = msgType;
|
||||
this.tupleOrig = tupleOrig;
|
||||
this.tupleReply = tupleReply;
|
||||
this.status = status;
|
||||
this.timeoutSec = timeoutSec;
|
||||
}
|
||||
|
||||
@Override
|
||||
@VisibleForTesting
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof ConntrackEvent)) return false;
|
||||
ConntrackEvent that = (ConntrackEvent) o;
|
||||
return this.msgType == that.msgType
|
||||
&& Objects.equals(this.tupleOrig, that.tupleOrig)
|
||||
&& Objects.equals(this.tupleReply, that.tupleReply)
|
||||
&& this.status == that.status
|
||||
&& this.timeoutSec == that.timeoutSec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(msgType, tupleOrig, tupleReply, status, timeoutSec);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ConntrackEvent{"
|
||||
+ "msg_type{"
|
||||
+ NetlinkConstants.stringForNlMsgType(msgType, OsConstants.NETLINK_NETFILTER)
|
||||
+ "}, "
|
||||
+ "tuple_orig{" + tupleOrig + "}, "
|
||||
+ "tuple_reply{" + tupleReply + "}, "
|
||||
+ "status{"
|
||||
+ status + "(" + ConntrackMessage.stringForIpConntrackStatus(status) + ")"
|
||||
+ "}, "
|
||||
+ "timeout_sec{" + Integer.toUnsignedLong(timeoutSec) + "}"
|
||||
+ "}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the established NAT session conntrack message.
|
||||
*
|
||||
* @param msg the conntrack message to check.
|
||||
* @return true if an established NAT message, false if not.
|
||||
*/
|
||||
public static boolean isEstablishedNatSession(@NonNull ConntrackMessage msg) {
|
||||
if (msg.getMessageType() != NetlinkConstants.IPCTNL_MSG_CT_NEW) return false;
|
||||
if (msg.tupleOrig == null) return false;
|
||||
if (msg.tupleReply == null) return false;
|
||||
if (msg.timeoutSec == 0) return false;
|
||||
if ((msg.status & ESTABLISHED_MASK) != ESTABLISHED_MASK) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the dying NAT session conntrack message.
|
||||
* Note that IPCTNL_MSG_CT_DELETE event has no CTA_TIMEOUT attribute.
|
||||
*
|
||||
* @param msg the conntrack message to check.
|
||||
* @return true if a dying NAT message, false if not.
|
||||
*/
|
||||
public static boolean isDyingNatSession(@NonNull ConntrackMessage msg) {
|
||||
if (msg.getMessageType() != NetlinkConstants.IPCTNL_MSG_CT_DELETE) return false;
|
||||
if (msg.tupleOrig == null) return false;
|
||||
if (msg.tupleReply == null) return false;
|
||||
if (msg.timeoutSec != 0) return false;
|
||||
if ((msg.status & DYING_MASK) != DYING_MASK) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A callback to caller for conntrack event.
|
||||
*/
|
||||
public interface ConntrackEventConsumer {
|
||||
/**
|
||||
* Every conntrack event received on the netlink socket is passed in
|
||||
* here.
|
||||
*/
|
||||
void accept(@NonNull ConntrackEvent event);
|
||||
}
|
||||
|
||||
private final ConntrackEventConsumer mConsumer;
|
||||
|
||||
public ConntrackMonitor(@NonNull Handler h, @NonNull SharedLog log,
|
||||
@NonNull ConntrackEventConsumer cb) {
|
||||
super(h, log, TAG, OsConstants.NETLINK_NETFILTER, NF_NETLINK_CONNTRACK_NEW
|
||||
| NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY, SOCKET_RECV_BUFSIZE);
|
||||
mConsumer = cb;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNetlinkMessage(NetlinkMessage nlMsg, final long whenMs) {
|
||||
if (!(nlMsg instanceof ConntrackMessage)) {
|
||||
mLog.e("non-conntrack msg: " + nlMsg);
|
||||
return;
|
||||
}
|
||||
|
||||
final ConntrackMessage conntrackMsg = (ConntrackMessage) nlMsg;
|
||||
if (!(ConntrackEvent.isEstablishedNatSession(conntrackMsg)
|
||||
|| ConntrackEvent.isDyingNatSession(conntrackMsg))) {
|
||||
return;
|
||||
}
|
||||
|
||||
mConsumer.accept(new ConntrackEvent(conntrackMsg));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,211 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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 com.android.net.module.util.ip;
|
||||
|
||||
import static android.net.INetd.IF_STATE_DOWN;
|
||||
import static android.net.INetd.IF_STATE_UP;
|
||||
|
||||
import android.net.INetd;
|
||||
import android.net.InterfaceConfigurationParcel;
|
||||
import android.net.LinkAddress;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceSpecificException;
|
||||
import android.system.OsConstants;
|
||||
|
||||
import com.android.net.module.util.SharedLog;
|
||||
|
||||
import java.net.Inet4Address;
|
||||
import java.net.InetAddress;
|
||||
|
||||
/**
|
||||
* Encapsulates the multiple IP configuration operations performed on an interface.
|
||||
*
|
||||
* TODO: refactor/eliminate the redundant ways to set and clear addresses.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class InterfaceController {
|
||||
private static final boolean DBG = false;
|
||||
|
||||
private final String mIfName;
|
||||
private final INetd mNetd;
|
||||
private final SharedLog mLog;
|
||||
|
||||
public InterfaceController(String ifname, INetd netd, SharedLog log) {
|
||||
mIfName = ifname;
|
||||
mNetd = netd;
|
||||
mLog = log;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the IPv4 address and also optionally bring the interface up or down.
|
||||
*/
|
||||
public boolean setInterfaceConfiguration(final LinkAddress ipv4Addr,
|
||||
final Boolean setIfaceUp) {
|
||||
if (!(ipv4Addr.getAddress() instanceof Inet4Address)) {
|
||||
throw new IllegalArgumentException("Invalid or mismatched Inet4Address");
|
||||
}
|
||||
// Note: currently netd only support INetd#IF_STATE_UP and #IF_STATE_DOWN.
|
||||
// Other flags would be ignored.
|
||||
|
||||
final InterfaceConfigurationParcel ifConfig = new InterfaceConfigurationParcel();
|
||||
ifConfig.ifName = mIfName;
|
||||
ifConfig.ipv4Addr = ipv4Addr.getAddress().getHostAddress();
|
||||
ifConfig.prefixLength = ipv4Addr.getPrefixLength();
|
||||
// Netd ignores hwaddr in interfaceSetCfg.
|
||||
ifConfig.hwAddr = "";
|
||||
if (setIfaceUp == null) {
|
||||
// Empty array means no change.
|
||||
ifConfig.flags = new String[0];
|
||||
} else {
|
||||
// Netd ignores any flag that's not IF_STATE_UP or IF_STATE_DOWN in interfaceSetCfg.
|
||||
ifConfig.flags = setIfaceUp.booleanValue()
|
||||
? new String[] {IF_STATE_UP} : new String[] {IF_STATE_DOWN};
|
||||
}
|
||||
try {
|
||||
mNetd.interfaceSetCfg(ifConfig);
|
||||
} catch (RemoteException | ServiceSpecificException e) {
|
||||
logError("Setting IPv4 address to %s/%d failed: %s",
|
||||
ifConfig.ipv4Addr, ifConfig.prefixLength, e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the IPv4 address of the interface.
|
||||
*/
|
||||
public boolean setIPv4Address(final LinkAddress address) {
|
||||
return setInterfaceConfiguration(address, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the IPv4Address of the interface.
|
||||
*/
|
||||
public boolean clearIPv4Address() {
|
||||
return setIPv4Address(new LinkAddress("0.0.0.0/0"));
|
||||
}
|
||||
|
||||
private boolean setEnableIPv6(boolean enabled) {
|
||||
try {
|
||||
mNetd.interfaceSetEnableIPv6(mIfName, enabled);
|
||||
} catch (RemoteException | ServiceSpecificException e) {
|
||||
logError("%s IPv6 failed: %s", (enabled ? "enabling" : "disabling"), e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable IPv6 on the interface.
|
||||
*/
|
||||
public boolean enableIPv6() {
|
||||
return setEnableIPv6(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable IPv6 on the interface.
|
||||
*/
|
||||
public boolean disableIPv6() {
|
||||
return setEnableIPv6(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable IPv6 privacy extensions on the interface.
|
||||
* @param enabled Whether the extensions should be enabled.
|
||||
*/
|
||||
public boolean setIPv6PrivacyExtensions(boolean enabled) {
|
||||
try {
|
||||
mNetd.interfaceSetIPv6PrivacyExtensions(mIfName, enabled);
|
||||
} catch (RemoteException | ServiceSpecificException e) {
|
||||
logError("error %s IPv6 privacy extensions: %s",
|
||||
(enabled ? "enabling" : "disabling"), e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set IPv6 address generation mode on the interface.
|
||||
*
|
||||
* <p>IPv6 should be disabled before changing the mode.
|
||||
*/
|
||||
public boolean setIPv6AddrGenModeIfSupported(int mode) {
|
||||
try {
|
||||
mNetd.setIPv6AddrGenMode(mIfName, mode);
|
||||
} catch (RemoteException e) {
|
||||
logError("Unable to set IPv6 addrgen mode: %s", e);
|
||||
return false;
|
||||
} catch (ServiceSpecificException e) {
|
||||
if (e.errorCode != OsConstants.EOPNOTSUPP) {
|
||||
logError("Unable to set IPv6 addrgen mode: %s", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an address to the interface.
|
||||
*/
|
||||
public boolean addAddress(LinkAddress addr) {
|
||||
return addAddress(addr.getAddress(), addr.getPrefixLength());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an address to the interface.
|
||||
*/
|
||||
public boolean addAddress(InetAddress ip, int prefixLen) {
|
||||
try {
|
||||
mNetd.interfaceAddAddress(mIfName, ip.getHostAddress(), prefixLen);
|
||||
} catch (ServiceSpecificException | RemoteException e) {
|
||||
logError("failed to add %s/%d: %s", ip, prefixLen, e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an address from the interface.
|
||||
*/
|
||||
public boolean removeAddress(InetAddress ip, int prefixLen) {
|
||||
try {
|
||||
mNetd.interfaceDelAddress(mIfName, ip.getHostAddress(), prefixLen);
|
||||
} catch (ServiceSpecificException | RemoteException e) {
|
||||
logError("failed to remove %s/%d: %s", ip, prefixLen, e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all addresses from the interface.
|
||||
*/
|
||||
public boolean clearAllAddresses() {
|
||||
try {
|
||||
mNetd.interfaceClearAddrs(mIfName);
|
||||
} catch (Exception e) {
|
||||
logError("Failed to clear addresses: %s", e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void logError(String fmt, Object... args) {
|
||||
mLog.e(String.format(fmt, args));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
* 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 com.android.net.module.util.ip;
|
||||
|
||||
import static android.system.OsConstants.NETLINK_ROUTE;
|
||||
|
||||
import static com.android.net.module.util.netlink.NetlinkConstants.RTM_DELNEIGH;
|
||||
import static com.android.net.module.util.netlink.NetlinkConstants.hexify;
|
||||
import static com.android.net.module.util.netlink.NetlinkConstants.stringForNlMsgType;
|
||||
|
||||
import android.net.MacAddress;
|
||||
import android.os.Handler;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.OsConstants;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.net.module.util.SharedLog;
|
||||
import com.android.net.module.util.netlink.NetlinkMessage;
|
||||
import com.android.net.module.util.netlink.NetlinkSocket;
|
||||
import com.android.net.module.util.netlink.RtNetlinkNeighborMessage;
|
||||
import com.android.net.module.util.netlink.StructNdMsg;
|
||||
|
||||
import java.net.InetAddress;
|
||||
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 NetlinkMonitor {
|
||||
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(NETLINK_ROUTE, msg);
|
||||
} catch (ErrnoException e) {
|
||||
Log.e(TAG, "Error " + msgSnippet + ": " + e);
|
||||
return -e.errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* An event about a neighbor.
|
||||
*/
|
||||
public static class NeighborEvent {
|
||||
final long elapsedMs;
|
||||
final short msgType;
|
||||
final int ifindex;
|
||||
final InetAddress ip;
|
||||
final short nudState;
|
||||
final MacAddress macAddr;
|
||||
|
||||
public NeighborEvent(long elapsedMs, short msgType, int ifindex, InetAddress ip,
|
||||
short nudState, MacAddress macAddr) {
|
||||
this.elapsedMs = elapsedMs;
|
||||
this.msgType = msgType;
|
||||
this.ifindex = ifindex;
|
||||
this.ip = ip;
|
||||
this.nudState = nudState;
|
||||
this.macAddr = macAddr;
|
||||
}
|
||||
|
||||
boolean isConnected() {
|
||||
return (msgType != RTM_DELNEIGH) && StructNdMsg.isNudStateConnected(nudState);
|
||||
}
|
||||
|
||||
boolean isValid() {
|
||||
return (msgType != RTM_DELNEIGH) && StructNdMsg.isNudStateValid(nudState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringJoiner j = new StringJoiner(",", "NeighborEvent{", "}");
|
||||
return j.add("@" + elapsedMs)
|
||||
.add(stringForNlMsgType(msgType, NETLINK_ROUTE))
|
||||
.add("if=" + ifindex)
|
||||
.add(ip.getHostAddress())
|
||||
.add(StructNdMsg.stringForNudState(nudState))
|
||||
.add("[" + macAddr + "]")
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A client that consumes NeighborEvent instances.
|
||||
* Implement this to listen to neighbor events.
|
||||
*/
|
||||
public interface NeighborEventConsumer {
|
||||
// Every neighbor event received on the netlink socket is passed in
|
||||
// here. Subclasses should filter for events of interest.
|
||||
/**
|
||||
* Consume a neighbor event
|
||||
* @param event the event
|
||||
*/
|
||||
void accept(NeighborEvent event);
|
||||
}
|
||||
|
||||
private final NeighborEventConsumer mConsumer;
|
||||
|
||||
public IpNeighborMonitor(Handler h, SharedLog log, NeighborEventConsumer cb) {
|
||||
super(h, log, TAG, NETLINK_ROUTE, OsConstants.RTMGRP_NEIGH);
|
||||
mConsumer = (cb != null) ? cb : (event) -> { /* discard */ };
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNetlinkMessage(NetlinkMessage nlMsg, final long whenMs) {
|
||||
if (!(nlMsg instanceof RtNetlinkNeighborMessage)) {
|
||||
mLog.e("non-rtnetlink neighbor msg: " + nlMsg);
|
||||
return;
|
||||
}
|
||||
|
||||
final RtNetlinkNeighborMessage neighMsg = (RtNetlinkNeighborMessage) nlMsg;
|
||||
final short msgType = neighMsg.getHeader().nlmsg_type;
|
||||
final StructNdMsg ndMsg = neighMsg.getNdHeader();
|
||||
if (ndMsg == null) {
|
||||
mLog.e("RtNetlinkNeighborMessage without ND message header!");
|
||||
return;
|
||||
}
|
||||
|
||||
final int ifindex = ndMsg.ndm_ifindex;
|
||||
final InetAddress destination = neighMsg.getDestination();
|
||||
final short nudState =
|
||||
(msgType == RTM_DELNEIGH)
|
||||
? StructNdMsg.NUD_NONE
|
||||
: ndMsg.ndm_state;
|
||||
|
||||
final NeighborEvent event = new NeighborEvent(
|
||||
whenMs, msgType, ifindex, destination, nudState,
|
||||
getMacAddress(neighMsg.getLinkLayerAddress()));
|
||||
|
||||
if (VDBG) {
|
||||
Log.d(TAG, neighMsg.toString());
|
||||
}
|
||||
if (DBG) {
|
||||
Log.d(TAG, event.toString());
|
||||
}
|
||||
|
||||
mConsumer.accept(event);
|
||||
}
|
||||
|
||||
private static MacAddress getMacAddress(byte[] linkLayerAddress) {
|
||||
if (linkLayerAddress != null) {
|
||||
try {
|
||||
return MacAddress.fromBytes(linkLayerAddress);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.e(TAG, "Failed to parse link-layer address: " + hexify(linkLayerAddress));
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* Copyright (C) 2020 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 com.android.net.module.util.ip;
|
||||
|
||||
import static android.net.util.SocketUtils.makeNetlinkSocketAddress;
|
||||
import static android.system.OsConstants.AF_NETLINK;
|
||||
import static android.system.OsConstants.SOCK_DGRAM;
|
||||
import static android.system.OsConstants.SOCK_NONBLOCK;
|
||||
import static android.system.OsConstants.SOL_SOCKET;
|
||||
import static android.system.OsConstants.SO_RCVBUF;
|
||||
|
||||
import static com.android.net.module.util.netlink.NetlinkConstants.hexify;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.net.util.SocketUtils;
|
||||
import android.os.Handler;
|
||||
import android.os.SystemClock;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.net.module.util.PacketReader;
|
||||
import com.android.net.module.util.SharedLog;
|
||||
import com.android.net.module.util.netlink.NetlinkErrorMessage;
|
||||
import com.android.net.module.util.netlink.NetlinkMessage;
|
||||
import com.android.net.module.util.netlink.NetlinkSocket;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.SocketException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
/**
|
||||
* A simple base class to listen for netlink broadcasts.
|
||||
*
|
||||
* Opens a netlink socket of the given family and binds to the specified groups. Polls the socket
|
||||
* from the event loop of the passed-in {@link Handler}, and calls the subclass-defined
|
||||
* {@link #processNetlinkMessage} method on the handler thread for each netlink message that
|
||||
* arrives. Currently ignores all netlink errors.
|
||||
* @hide
|
||||
*/
|
||||
public class NetlinkMonitor extends PacketReader {
|
||||
protected final SharedLog mLog;
|
||||
protected final String mTag;
|
||||
private final int mFamily;
|
||||
private final int mBindGroups;
|
||||
private final int mSockRcvbufSize;
|
||||
|
||||
private static final boolean DBG = false;
|
||||
|
||||
// Default socket receive buffer size. This means the specific buffer size is not set.
|
||||
private static final int DEFAULT_SOCKET_RECV_BUFSIZE = -1;
|
||||
|
||||
/**
|
||||
* Constructs a new {@code NetlinkMonitor} instance.
|
||||
*
|
||||
* @param h The Handler on which to poll for messages and on which to call
|
||||
* {@link #processNetlinkMessage}.
|
||||
* @param log A SharedLog to log to.
|
||||
* @param tag The log tag to use for log messages.
|
||||
* @param family the Netlink socket family to, e.g., {@code NETLINK_ROUTE}.
|
||||
* @param bindGroups the netlink groups to bind to.
|
||||
* @param sockRcvbufSize the specific socket receive buffer size in bytes. -1 means that don't
|
||||
* set the specific socket receive buffer size in #createFd and use the default value in
|
||||
* /proc/sys/net/core/rmem_default file. See SO_RCVBUF in man-pages/socket.
|
||||
*/
|
||||
public NetlinkMonitor(@NonNull Handler h, @NonNull SharedLog log, @NonNull String tag,
|
||||
int family, int bindGroups, int sockRcvbufSize) {
|
||||
super(h, NetlinkSocket.DEFAULT_RECV_BUFSIZE);
|
||||
mLog = log.forSubComponent(tag);
|
||||
mTag = tag;
|
||||
mFamily = family;
|
||||
mBindGroups = bindGroups;
|
||||
mSockRcvbufSize = sockRcvbufSize;
|
||||
}
|
||||
|
||||
public NetlinkMonitor(@NonNull Handler h, @NonNull SharedLog log, @NonNull String tag,
|
||||
int family, int bindGroups) {
|
||||
this(h, log, tag, family, bindGroups, DEFAULT_SOCKET_RECV_BUFSIZE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FileDescriptor createFd() {
|
||||
FileDescriptor fd = null;
|
||||
|
||||
try {
|
||||
fd = Os.socket(AF_NETLINK, SOCK_DGRAM | SOCK_NONBLOCK, mFamily);
|
||||
if (mSockRcvbufSize != DEFAULT_SOCKET_RECV_BUFSIZE) {
|
||||
Os.setsockoptInt(fd, SOL_SOCKET, SO_RCVBUF, mSockRcvbufSize);
|
||||
}
|
||||
Os.bind(fd, makeNetlinkSocketAddress(0, mBindGroups));
|
||||
NetlinkSocket.connectToKernel(fd);
|
||||
|
||||
if (DBG) {
|
||||
final SocketAddress nlAddr = Os.getsockname(fd);
|
||||
Log.d(mTag, "bound to sockaddr_nl{" + nlAddr.toString() + "}");
|
||||
}
|
||||
} catch (ErrnoException | SocketException e) {
|
||||
logError("Failed to create rtnetlink socket", e);
|
||||
closeSocketQuietly(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());
|
||||
|
||||
while (byteBuffer.remaining() > 0) {
|
||||
try {
|
||||
final int position = byteBuffer.position();
|
||||
final NetlinkMessage nlMsg = NetlinkMessage.parse(byteBuffer, mFamily);
|
||||
if (nlMsg == null || nlMsg.getHeader() == null) {
|
||||
byteBuffer.position(position);
|
||||
mLog.e("unparsable netlink msg: " + hexify(byteBuffer));
|
||||
break;
|
||||
}
|
||||
|
||||
if (nlMsg instanceof NetlinkErrorMessage) {
|
||||
mLog.e("netlink error: " + nlMsg);
|
||||
continue;
|
||||
}
|
||||
|
||||
processNetlinkMessage(nlMsg, whenMs);
|
||||
} catch (Exception e) {
|
||||
mLog.e("Error handling netlink message", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: move NetworkStackUtils to frameworks/libs/net for NetworkStackUtils#closeSocketQuietly.
|
||||
private void closeSocketQuietly(FileDescriptor fd) {
|
||||
try {
|
||||
SocketUtils.closeSocket(fd);
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes one netlink message. Must be overridden by subclasses.
|
||||
* @param nlMsg the message to process.
|
||||
* @param whenMs the timestamp, as measured by {@link SystemClock#elapsedRealtime}, when the
|
||||
* message was received.
|
||||
*/
|
||||
protected void processNetlinkMessage(NetlinkMessage nlMsg, long whenMs) { }
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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 com.android.net.module.util;
|
||||
|
||||
import android.net.MacAddress;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
|
||||
|
||||
/**
|
||||
* Encapsulate the interface parameters common to IpClient/IpServer components.
|
||||
*
|
||||
* Basically all java.net.NetworkInterface methods throw Exceptions. IpClient
|
||||
* and IpServer (sub)components need most or all of this information at some
|
||||
* point during their lifecycles, so pass only this simplified object around
|
||||
* which can be created once when IpClient/IpServer are told to start.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class InterfaceParams {
|
||||
public final String name;
|
||||
public final int index;
|
||||
public final boolean hasMacAddress;
|
||||
public final MacAddress macAddr;
|
||||
public final int defaultMtu;
|
||||
|
||||
// TODO: move the below to NetworkStackConstants when this class is moved to the NetworkStack.
|
||||
private static final int ETHER_MTU = 1500;
|
||||
private static final int IPV6_MIN_MTU = 1280;
|
||||
|
||||
|
||||
/**
|
||||
* Return InterfaceParams corresponding with an interface name
|
||||
* @param name the interface name
|
||||
*/
|
||||
public static InterfaceParams getByName(String name) {
|
||||
final NetworkInterface netif = getNetworkInterfaceByName(name);
|
||||
if (netif == null) return null;
|
||||
|
||||
// Not all interfaces have MAC addresses, e.g. rmnet_data0.
|
||||
final MacAddress macAddr = getMacAddress(netif);
|
||||
|
||||
try {
|
||||
return new InterfaceParams(name, netif.getIndex(), macAddr, netif.getMTU());
|
||||
} catch (IllegalArgumentException | SocketException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public InterfaceParams(String name, int index, MacAddress macAddr) {
|
||||
this(name, index, macAddr, ETHER_MTU);
|
||||
}
|
||||
|
||||
public InterfaceParams(String name, int index, MacAddress macAddr, int defaultMtu) {
|
||||
if (TextUtils.isEmpty(name)) {
|
||||
throw new IllegalArgumentException("impossible interface name");
|
||||
}
|
||||
|
||||
if (index <= 0) throw new IllegalArgumentException("invalid interface index");
|
||||
|
||||
this.name = name;
|
||||
this.index = index;
|
||||
this.hasMacAddress = (macAddr != null);
|
||||
this.macAddr = hasMacAddress ? macAddr : MacAddress.fromBytes(new byte[] {
|
||||
0x02, 0x00, 0x00, 0x00, 0x00, 0x00 });
|
||||
this.defaultMtu = (defaultMtu > IPV6_MIN_MTU) ? defaultMtu : IPV6_MIN_MTU;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s/%d/%s/%d", name, index, macAddr, defaultMtu);
|
||||
}
|
||||
|
||||
private static NetworkInterface getNetworkInterfaceByName(String name) {
|
||||
try {
|
||||
return NetworkInterface.getByName(name);
|
||||
} catch (NullPointerException | SocketException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static MacAddress getMacAddress(NetworkInterface netif) {
|
||||
try {
|
||||
return MacAddress.fromBytes(netif.getHardwareAddress());
|
||||
} catch (IllegalArgumentException | NullPointerException | SocketException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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 com.android.net.module.util;
|
||||
|
||||
/** @hide */
|
||||
// RouteUtils is now empty, because some new methods will be added to it soon and it is less
|
||||
// expensive to keep it empty than to remove it now and add it again later.
|
||||
public class RouteUtils {
|
||||
}
|
||||
|
|
@ -13,10 +13,12 @@ android_library {
|
|||
defaults: ["framework-connectivity-test-defaults"],
|
||||
static_libs: [
|
||||
"net-utils-framework-common",
|
||||
"net-utils-device-common-ip",
|
||||
"androidx.test.rules",
|
||||
"mockito-target-extended-minus-junit4",
|
||||
"net-utils-device-common",
|
||||
"net-tests-utils",
|
||||
"netd-client",
|
||||
],
|
||||
libs: [
|
||||
"android.test.runner",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* 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 com.android.net.module.util;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class SharedLogTest {
|
||||
private static final String TIMESTAMP_PATTERN = "\\d{2}:\\d{2}:\\d{2}";
|
||||
private static final String TIMESTAMP = "HH:MM:SS";
|
||||
|
||||
@Test
|
||||
public void testBasicOperation() {
|
||||
final SharedLog logTop = new SharedLog("top");
|
||||
logTop.mark("first post!");
|
||||
|
||||
final SharedLog logLevel2a = logTop.forSubComponent("twoA");
|
||||
final SharedLog logLevel2b = logTop.forSubComponent("twoB");
|
||||
logLevel2b.e("2b or not 2b");
|
||||
logLevel2b.e("No exception", null);
|
||||
logLevel2b.e("Wait, here's one", new Exception("Test"));
|
||||
logLevel2a.w("second post?");
|
||||
|
||||
final SharedLog logLevel3 = logLevel2a.forSubComponent("three");
|
||||
logTop.log("still logging");
|
||||
logLevel3.log("3 >> 2");
|
||||
logLevel2a.mark("ok: last post");
|
||||
|
||||
final String[] expected = {
|
||||
" - MARK first post!",
|
||||
" - [twoB] ERROR 2b or not 2b",
|
||||
" - [twoB] ERROR No exception",
|
||||
// No stacktrace in shared log, only in logcat
|
||||
" - [twoB] ERROR Wait, here's one: Test",
|
||||
" - [twoA] WARN second post?",
|
||||
" - still logging",
|
||||
" - [twoA.three] 3 >> 2",
|
||||
" - [twoA] MARK ok: last post",
|
||||
};
|
||||
// Verify the logs are all there and in the correct order.
|
||||
verifyLogLines(expected, logTop);
|
||||
|
||||
// In fact, because they all share the same underlying LocalLog,
|
||||
// every subcomponent SharedLog's dump() is identical.
|
||||
verifyLogLines(expected, logLevel2a);
|
||||
verifyLogLines(expected, logLevel2b);
|
||||
verifyLogLines(expected, logLevel3);
|
||||
}
|
||||
|
||||
private static void verifyLogLines(String[] expected, SharedLog log) {
|
||||
final ByteArrayOutputStream ostream = new ByteArrayOutputStream();
|
||||
final PrintWriter pw = new PrintWriter(ostream, true);
|
||||
log.dump(null, pw, null);
|
||||
|
||||
final String dumpOutput = ostream.toString();
|
||||
assertTrue(dumpOutput != null);
|
||||
assertTrue(!"".equals(dumpOutput));
|
||||
|
||||
final String[] lines = dumpOutput.split("\n");
|
||||
assertEquals(expected.length, lines.length);
|
||||
|
||||
for (int i = 0; i < expected.length; i++) {
|
||||
String got = lines[i];
|
||||
String want = expected[i];
|
||||
assertTrue(String.format("'%s' did not contain '%s'", got, want), got.endsWith(want));
|
||||
assertTrue(String.format("'%s' did not contain a %s timestamp", got, TIMESTAMP),
|
||||
got.replaceFirst(TIMESTAMP_PATTERN, TIMESTAMP).contains(TIMESTAMP));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,336 @@
|
|||
/*
|
||||
* Copyright (C) 2020 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 com.android.net.module.util.ip;
|
||||
|
||||
import static android.system.OsConstants.AF_UNIX;
|
||||
import static android.system.OsConstants.IPPROTO_TCP;
|
||||
import static android.system.OsConstants.SOCK_DGRAM;
|
||||
|
||||
import static com.android.net.module.util.netlink.ConntrackMessage.Tuple;
|
||||
import static com.android.net.module.util.netlink.ConntrackMessage.TupleIpv4;
|
||||
import static com.android.net.module.util.netlink.ConntrackMessage.TupleProto;
|
||||
import static com.android.net.module.util.netlink.NetlinkConstants.IPCTNL_MSG_CT_DELETE;
|
||||
import static com.android.net.module.util.netlink.NetlinkConstants.IPCTNL_MSG_CT_NEW;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.timeout;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.net.InetAddresses;
|
||||
import android.os.ConditionVariable;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.net.module.util.SharedLog;
|
||||
import com.android.net.module.util.ip.ConntrackMonitor.ConntrackEvent;
|
||||
import com.android.net.module.util.netlink.NetlinkConstants;
|
||||
import com.android.net.module.util.netlink.NetlinkSocket;
|
||||
|
||||
import libcore.util.HexEncoding;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.net.Inet4Address;
|
||||
|
||||
/**
|
||||
* Tests for ConntrackMonitor.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class ConntrackMonitorTest {
|
||||
private static final long TIMEOUT_MS = 10_000L;
|
||||
|
||||
@Mock private SharedLog mLog;
|
||||
@Mock private ConntrackMonitor.ConntrackEventConsumer mConsumer;
|
||||
|
||||
private final HandlerThread mHandlerThread = new HandlerThread(
|
||||
ConntrackMonitorTest.class.getSimpleName());
|
||||
|
||||
// Late init since the handler thread has been started.
|
||||
private Handler mHandler;
|
||||
private TestConntrackMonitor mConntrackMonitor;
|
||||
|
||||
// A version of [ConntrackMonitor] that reads packets from the socket pair, and instead
|
||||
// allows the test to write test packets to the socket pair via [sendMessage].
|
||||
private class TestConntrackMonitor extends ConntrackMonitor {
|
||||
TestConntrackMonitor(@NonNull Handler h, @NonNull SharedLog log,
|
||||
@NonNull ConntrackEventConsumer cb) {
|
||||
super(h, log, cb);
|
||||
|
||||
mReadFd = new FileDescriptor();
|
||||
mWriteFd = new FileDescriptor();
|
||||
try {
|
||||
Os.socketpair(AF_UNIX, SOCK_DGRAM, 0, mWriteFd, mReadFd);
|
||||
} catch (ErrnoException e) {
|
||||
fail("Could not create socket pair: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FileDescriptor createFd() {
|
||||
return mReadFd;
|
||||
}
|
||||
|
||||
private void sendMessage(byte[] msg) {
|
||||
mHandler.post(() -> {
|
||||
try {
|
||||
NetlinkSocket.sendMessage(mWriteFd, msg, 0 /* offset */, msg.length,
|
||||
TIMEOUT_MS);
|
||||
} catch (ErrnoException | InterruptedIOException e) {
|
||||
fail("Unable to send netfilter message: " + e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private final FileDescriptor mReadFd;
|
||||
private final FileDescriptor mWriteFd;
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mHandlerThread.start();
|
||||
mHandler = new Handler(mHandlerThread.getLooper());
|
||||
|
||||
// ConntrackMonitor needs to be started from the handler thread.
|
||||
final ConditionVariable initDone = new ConditionVariable();
|
||||
mHandler.post(() -> {
|
||||
TestConntrackMonitor m = new TestConntrackMonitor(mHandler, mLog, mConsumer);
|
||||
m.start();
|
||||
mConntrackMonitor = m;
|
||||
|
||||
initDone.open();
|
||||
});
|
||||
if (!initDone.block(TIMEOUT_MS)) {
|
||||
fail("... init monitor timed-out after " + TIMEOUT_MS + "ms");
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
mHandlerThread.quitSafely();
|
||||
}
|
||||
|
||||
public static final String CT_V4NEW_TCP_HEX =
|
||||
// CHECKSTYLE:OFF IndentationCheck
|
||||
// struct nlmsghdr
|
||||
"8C000000" + // length = 140
|
||||
"0001" + // type = NFNL_SUBSYS_CTNETLINK (1) << 8 | IPCTNL_MSG_CT_NEW (0)
|
||||
"0006" + // flags = NLM_F_CREATE (1 << 10) | NLM_F_EXCL (1 << 9)
|
||||
"00000000" + // seqno = 0
|
||||
"00000000" + // pid = 0
|
||||
// struct nfgenmsg
|
||||
"02" + // nfgen_family = AF_INET
|
||||
"00" + // version = NFNETLINK_V0
|
||||
"1234" + // res_id = 0x1234 (big endian)
|
||||
// struct nlattr
|
||||
"3400" + // nla_len = 52
|
||||
"0180" + // nla_type = nested CTA_TUPLE_ORIG
|
||||
// struct nlattr
|
||||
"1400" + // nla_len = 20
|
||||
"0180" + // nla_type = nested CTA_TUPLE_IP
|
||||
"0800 0100 C0A8500C" + // nla_type=CTA_IP_V4_SRC, ip=192.168.80.12
|
||||
"0800 0200 8C700874" + // nla_type=CTA_IP_V4_DST, ip=140.112.8.116
|
||||
// struct nlattr
|
||||
"1C00" + // nla_len = 28
|
||||
"0280" + // nla_type = nested CTA_TUPLE_PROTO
|
||||
"0500 0100 06 000000" + // nla_type=CTA_PROTO_NUM, proto=IPPROTO_TCP (6)
|
||||
"0600 0200 F3F1 0000" + // nla_type=CTA_PROTO_SRC_PORT, port=62449 (big endian)
|
||||
"0600 0300 01BB 0000" + // nla_type=CTA_PROTO_DST_PORT, port=443 (big endian)
|
||||
// struct nlattr
|
||||
"3400" + // nla_len = 52
|
||||
"0280" + // nla_type = nested CTA_TUPLE_REPLY
|
||||
// struct nlattr
|
||||
"1400" + // nla_len = 20
|
||||
"0180" + // nla_type = nested CTA_TUPLE_IP
|
||||
"0800 0100 8C700874" + // nla_type=CTA_IP_V4_SRC, ip=140.112.8.116
|
||||
"0800 0200 6451B301" + // nla_type=CTA_IP_V4_DST, ip=100.81.179.1
|
||||
// struct nlattr
|
||||
"1C00" + // nla_len = 28
|
||||
"0280" + // nla_type = nested CTA_TUPLE_PROTO
|
||||
"0500 0100 06 000000" + // nla_type=CTA_PROTO_NUM, proto=IPPROTO_TCP (6)
|
||||
"0600 0200 01BB 0000" + // nla_type=CTA_PROTO_SRC_PORT, port=443 (big endian)
|
||||
"0600 0300 F3F1 0000" + // nla_type=CTA_PROTO_DST_PORT, port=62449 (big endian)
|
||||
// struct nlattr
|
||||
"0800" + // nla_len = 8
|
||||
"0300" + // nla_type = CTA_STATUS
|
||||
"0000019e" + // nla_value = 0b110011110 (big endian)
|
||||
// IPS_SEEN_REPLY (1 << 1) | IPS_ASSURED (1 << 2) |
|
||||
// IPS_CONFIRMED (1 << 3) | IPS_SRC_NAT (1 << 4) |
|
||||
// IPS_SRC_NAT_DONE (1 << 7) | IPS_DST_NAT_DONE (1 << 8)
|
||||
// struct nlattr
|
||||
"0800" + // nla_len = 8
|
||||
"0700" + // nla_type = CTA_TIMEOUT
|
||||
"00000078"; // nla_value = 120 (big endian)
|
||||
// CHECKSTYLE:ON IndentationCheck
|
||||
public static final byte[] CT_V4NEW_TCP_BYTES =
|
||||
HexEncoding.decode(CT_V4NEW_TCP_HEX.replaceAll(" ", "").toCharArray(), false);
|
||||
|
||||
@NonNull
|
||||
private ConntrackEvent makeTestConntrackEvent(short msgType, int status, int timeoutSec) {
|
||||
final Inet4Address privateIp =
|
||||
(Inet4Address) InetAddresses.parseNumericAddress("192.168.80.12");
|
||||
final Inet4Address remoteIp =
|
||||
(Inet4Address) InetAddresses.parseNumericAddress("140.112.8.116");
|
||||
final Inet4Address publicIp =
|
||||
(Inet4Address) InetAddresses.parseNumericAddress("100.81.179.1");
|
||||
|
||||
return new ConntrackEvent(
|
||||
(short) (NetlinkConstants.NFNL_SUBSYS_CTNETLINK << 8 | msgType),
|
||||
new Tuple(new TupleIpv4(privateIp, remoteIp),
|
||||
new TupleProto((byte) IPPROTO_TCP, (short) 62449, (short) 443)),
|
||||
new Tuple(new TupleIpv4(remoteIp, publicIp),
|
||||
new TupleProto((byte) IPPROTO_TCP, (short) 443, (short) 62449)),
|
||||
status,
|
||||
timeoutSec);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConntrackEventNew() throws Exception {
|
||||
final ConntrackEvent expectedEvent = makeTestConntrackEvent(IPCTNL_MSG_CT_NEW,
|
||||
0x19e /* status */, 120 /* timeoutSec */);
|
||||
mConntrackMonitor.sendMessage(CT_V4NEW_TCP_BYTES);
|
||||
verify(mConsumer, timeout(TIMEOUT_MS)).accept(eq(expectedEvent));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConntrackEventEquals() {
|
||||
final ConntrackEvent event1 = makeTestConntrackEvent(IPCTNL_MSG_CT_NEW, 1234 /* status */,
|
||||
5678 /* timeoutSec*/);
|
||||
final ConntrackEvent event2 = makeTestConntrackEvent(IPCTNL_MSG_CT_NEW, 1234 /* status */,
|
||||
5678 /* timeoutSec*/);
|
||||
assertEquals(event1, event2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConntrackEventNotEquals() {
|
||||
final ConntrackEvent e = makeTestConntrackEvent(IPCTNL_MSG_CT_NEW, 1234 /* status */,
|
||||
5678 /* timeoutSec*/);
|
||||
|
||||
final ConntrackEvent typeNotEqual = new ConntrackEvent((short) (e.msgType + 1) /* diff */,
|
||||
e.tupleOrig, e.tupleReply, e.status, e.timeoutSec);
|
||||
assertNotEquals(e, typeNotEqual);
|
||||
|
||||
final ConntrackEvent tupleOrigNotEqual = new ConntrackEvent(e.msgType,
|
||||
null /* diff */, e.tupleReply, e.status, e.timeoutSec);
|
||||
assertNotEquals(e, tupleOrigNotEqual);
|
||||
|
||||
final ConntrackEvent tupleReplyNotEqual = new ConntrackEvent(e.msgType,
|
||||
e.tupleOrig, null /* diff */, e.status, e.timeoutSec);
|
||||
assertNotEquals(e, tupleReplyNotEqual);
|
||||
|
||||
final ConntrackEvent statusNotEqual = new ConntrackEvent(e.msgType,
|
||||
e.tupleOrig, e.tupleReply, e.status + 1 /* diff */, e.timeoutSec);
|
||||
assertNotEquals(e, statusNotEqual);
|
||||
|
||||
final ConntrackEvent timeoutSecNotEqual = new ConntrackEvent(e.msgType,
|
||||
e.tupleOrig, e.tupleReply, e.status, e.timeoutSec + 1 /* diff */);
|
||||
assertNotEquals(e, timeoutSecNotEqual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToString() {
|
||||
final ConntrackEvent event = makeTestConntrackEvent(IPCTNL_MSG_CT_NEW,
|
||||
0x198 /* status */, 120 /* timeoutSec */);
|
||||
final String expected = ""
|
||||
+ "ConntrackEvent{"
|
||||
+ "msg_type{IPCTNL_MSG_CT_NEW}, "
|
||||
+ "tuple_orig{Tuple{IPPROTO_TCP: 192.168.80.12:62449 -> 140.112.8.116:443}}, "
|
||||
+ "tuple_reply{Tuple{IPPROTO_TCP: 140.112.8.116:443 -> 100.81.179.1:62449}}, "
|
||||
+ "status{408(IPS_CONFIRMED|IPS_SRC_NAT|IPS_SRC_NAT_DONE|IPS_DST_NAT_DONE)}, "
|
||||
+ "timeout_sec{120}}";
|
||||
assertEquals(expected, event.toString());
|
||||
}
|
||||
|
||||
public static final String CT_V4DELETE_TCP_HEX =
|
||||
// CHECKSTYLE:OFF IndentationCheck
|
||||
// struct nlmsghdr
|
||||
"84000000" + // length = 132
|
||||
"0201" + // type = NFNL_SUBSYS_CTNETLINK (1) << 8 | IPCTNL_MSG_CT_DELETE (2)
|
||||
"0000" + // flags = 0
|
||||
"00000000" + // seqno = 0
|
||||
"00000000" + // pid = 0
|
||||
// struct nfgenmsg
|
||||
"02" + // nfgen_family = AF_INET
|
||||
"00" + // version = NFNETLINK_V0
|
||||
"1234" + // res_id = 0x1234 (big endian)
|
||||
// struct nlattr
|
||||
"3400" + // nla_len = 52
|
||||
"0180" + // nla_type = nested CTA_TUPLE_ORIG
|
||||
// struct nlattr
|
||||
"1400" + // nla_len = 20
|
||||
"0180" + // nla_type = nested CTA_TUPLE_IP
|
||||
"0800 0100 C0A8500C" + // nla_type=CTA_IP_V4_SRC, ip=192.168.80.12
|
||||
"0800 0200 8C700874" + // nla_type=CTA_IP_V4_DST, ip=140.112.8.116
|
||||
// struct nlattr
|
||||
"1C00" + // nla_len = 28
|
||||
"0280" + // nla_type = nested CTA_TUPLE_PROTO
|
||||
"0500 0100 06 000000" + // nla_type=CTA_PROTO_NUM, proto=IPPROTO_TCP (6)
|
||||
"0600 0200 F3F1 0000" + // nla_type=CTA_PROTO_SRC_PORT, port=62449 (big endian)
|
||||
"0600 0300 01BB 0000" + // nla_type=CTA_PROTO_DST_PORT, port=433 (big endian)
|
||||
// struct nlattr
|
||||
"3400" + // nla_len = 52
|
||||
"0280" + // nla_type = nested CTA_TUPLE_REPLY
|
||||
// struct nlattr
|
||||
"1400" + // nla_len = 20
|
||||
"0180" + // nla_type = nested CTA_TUPLE_IP
|
||||
"0800 0100 8C700874" + // nla_type=CTA_IP_V4_SRC, ip=140.112.8.116
|
||||
"0800 0200 6451B301" + // nla_type=CTA_IP_V4_DST, ip=100.81.179.1
|
||||
// struct nlattr
|
||||
"1C00" + // nla_len = 28
|
||||
"0280" + // nla_type = nested CTA_TUPLE_PROTO
|
||||
"0500 0100 06 000000" + // nla_type=CTA_PROTO_NUM, proto=IPPROTO_TCP (6)
|
||||
"0600 0200 01BB 0000" + // nla_type=CTA_PROTO_SRC_PORT, port=433 (big endian)
|
||||
"0600 0300 F3F1 0000" + // nla_type=CTA_PROTO_DST_PORT, port=62449 (big endian)
|
||||
// struct nlattr
|
||||
"0800" + // nla_len = 8
|
||||
"0300" + // nla_type = CTA_STATUS
|
||||
"0000039E"; // nla_value = 0b1110011110 (big endian)
|
||||
// IPS_SEEN_REPLY (1 << 1) | IPS_ASSURED (1 << 2) |
|
||||
// IPS_CONFIRMED (1 << 3) | IPS_SRC_NAT (1 << 4) |
|
||||
// IPS_SRC_NAT_DONE (1 << 7) | IPS_DST_NAT_DONE (1 << 8) |
|
||||
// IPS_DYING (1 << 9)
|
||||
// CHECKSTYLE:ON IndentationCheck
|
||||
public static final byte[] CT_V4DELETE_TCP_BYTES =
|
||||
HexEncoding.decode(CT_V4DELETE_TCP_HEX.replaceAll(" ", "").toCharArray(), false);
|
||||
|
||||
@Test
|
||||
public void testConntrackEventDelete() throws Exception {
|
||||
final ConntrackEvent expectedEvent =
|
||||
makeTestConntrackEvent(IPCTNL_MSG_CT_DELETE, 0x39e /* status */,
|
||||
0 /* timeoutSec (absent) */);
|
||||
mConntrackMonitor.sendMessage(CT_V4DELETE_TCP_BYTES);
|
||||
verify(mConsumer, timeout(TIMEOUT_MS)).accept(eq(expectedEvent));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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 com.android.net.module.util.ip;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.net.INetd;
|
||||
import android.net.InetAddresses;
|
||||
import android.net.InterfaceConfigurationParcel;
|
||||
import android.net.LinkAddress;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.net.module.util.SharedLog;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class InterfaceControllerTest {
|
||||
private static final String TEST_IFACE = "testif";
|
||||
private static final String TEST_IPV4_ADDR = "192.168.123.28";
|
||||
private static final int TEST_PREFIXLENGTH = 31;
|
||||
|
||||
@Mock private INetd mNetd;
|
||||
@Mock private SharedLog mLog;
|
||||
@Captor private ArgumentCaptor<InterfaceConfigurationParcel> mConfigCaptor;
|
||||
|
||||
private InterfaceController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mController = new InterfaceController(TEST_IFACE, mNetd, mLog);
|
||||
|
||||
doNothing().when(mNetd).interfaceSetCfg(mConfigCaptor.capture());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetIPv4Address() throws Exception {
|
||||
mController.setIPv4Address(
|
||||
new LinkAddress(InetAddresses.parseNumericAddress(TEST_IPV4_ADDR),
|
||||
TEST_PREFIXLENGTH));
|
||||
verify(mNetd, times(1)).interfaceSetCfg(any());
|
||||
final InterfaceConfigurationParcel parcel = mConfigCaptor.getValue();
|
||||
assertEquals(TEST_IFACE, parcel.ifName);
|
||||
assertEquals(TEST_IPV4_ADDR, parcel.ipv4Addr);
|
||||
assertEquals(TEST_PREFIXLENGTH, parcel.prefixLength);
|
||||
assertEquals("", parcel.hwAddr);
|
||||
assertArrayEquals(new String[0], parcel.flags);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearIPv4Address() throws Exception {
|
||||
mController.clearIPv4Address();
|
||||
verify(mNetd, times(1)).interfaceSetCfg(any());
|
||||
final InterfaceConfigurationParcel parcel = mConfigCaptor.getValue();
|
||||
assertEquals(TEST_IFACE, parcel.ifName);
|
||||
assertEquals("0.0.0.0", parcel.ipv4Addr);
|
||||
assertEquals(0, parcel.prefixLength);
|
||||
assertEquals("", parcel.hwAddr);
|
||||
assertArrayEquals(new String[0], parcel.flags);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue