Merge "Support NAT keepalives" into oc-mr1-dev

am: 4fae111ca2

Change-Id: I82ac60e5ad79ec64a13df6ec56b5b51b223f8dde
This commit is contained in:
Erik Kline
2017-09-07 16:48:57 +00:00
committed by android-build-merger
9 changed files with 584 additions and 64 deletions

View File

@@ -30,6 +30,10 @@ import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NetworkStats;
import android.net.RouteInfo;
import android.net.netlink.ConntrackMessage;
import android.net.netlink.NetlinkConstants;
import android.net.netlink.NetlinkSocket;
import android.net.util.IpUtils;
import android.net.util.SharedLog;
import android.os.Handler;
import android.os.Looper;
@@ -37,10 +41,12 @@ import android.os.INetworkManagementService;
import android.os.RemoteException;
import android.os.SystemClock;
import android.provider.Settings;
import android.system.ErrnoException;
import android.system.OsConstants;
import android.text.TextUtils;
import com.android.server.connectivity.tethering.OffloadHardwareInterface.ForwardedStats;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.connectivity.tethering.OffloadHardwareInterface.ForwardedStats;
import java.net.Inet4Address;
import java.net.Inet6Address;
@@ -63,6 +69,7 @@ import java.util.concurrent.TimeUnit;
*/
public class OffloadController {
private static final String TAG = OffloadController.class.getSimpleName();
private static final boolean DBG = false;
private static final String ANYIP = "0.0.0.0";
private static final ForwardedStats EMPTY_STATS = new ForwardedStats();
@@ -96,6 +103,9 @@ public class OffloadController {
// includes upstream interfaces that have a quota set.
private HashMap<String, Long> mInterfaceQuotas = new HashMap<>();
private int mNatUpdateCallbacksReceived;
private int mNatUpdateNetlinkErrors;
public OffloadController(Handler h, OffloadHardwareInterface hwi,
ContentResolver contentResolver, INetworkManagementService nms, SharedLog log) {
mHandler = h;
@@ -115,12 +125,12 @@ public class OffloadController {
}
}
public void start() {
if (started()) return;
public boolean start() {
if (started()) return true;
if (isOffloadDisabled()) {
mLog.i("tethering offload disabled");
return;
return false;
}
if (!mConfigInitialized) {
@@ -128,11 +138,14 @@ public class OffloadController {
if (!mConfigInitialized) {
mLog.i("tethering offload config not supported");
stop();
return;
return false;
}
}
mControlInitialized = mHwInterface.initOffloadControl(
// OffloadHardwareInterface guarantees that these callback
// methods are called on the handler passed to it, which is the
// same as mHandler, as coordinated by the setup in Tethering.
new OffloadHardwareInterface.ControlCallback() {
@Override
public void onStarted() {
@@ -203,15 +216,20 @@ public class OffloadController {
String srcAddr, int srcPort,
String dstAddr, int dstPort) {
if (!started()) return;
mLog.log(String.format("NAT timeout update: %s (%s,%s) -> (%s,%s)",
proto, srcAddr, srcPort, dstAddr, dstPort));
updateNatTimeout(proto, srcAddr, srcPort, dstAddr, dstPort);
}
});
if (!mControlInitialized) {
final boolean isStarted = started();
if (!isStarted) {
mLog.i("tethering offload control not supported");
stop();
} else {
mLog.log("tethering offload started");
mNatUpdateCallbacksReceived = 0;
mNatUpdateNetlinkErrors = 0;
}
mLog.log("tethering offload started");
return isStarted;
}
public void stop() {
@@ -227,6 +245,10 @@ public class OffloadController {
if (wasStarted) mLog.log("tethering offload stopped");
}
private boolean started() {
return mConfigInitialized && mControlInitialized;
}
private class OffloadTetheringStatsProvider extends ITetheringStatsProvider.Stub {
@Override
public NetworkStats getTetherStats(int how) {
@@ -402,10 +424,6 @@ public class OffloadController {
mContentResolver, TETHER_OFFLOAD_DISABLED, defaultDisposition) != 0);
}
private boolean started() {
return mConfigInitialized && mControlInitialized;
}
private boolean pushUpstreamParameters(String prevUpstream) {
final String iface = currentUpstreamInterface();
@@ -516,10 +534,113 @@ public class OffloadController {
pw.println("Offload disabled");
return;
}
pw.println("Offload HALs " + (started() ? "started" : "not started"));
final boolean isStarted = started();
pw.println("Offload HALs " + (isStarted ? "started" : "not started"));
LinkProperties lp = mUpstreamLinkProperties;
String upstream = (lp != null) ? lp.getInterfaceName() : null;
pw.println("Current upstream: " + upstream);
pw.println("Exempt prefixes: " + mLastLocalPrefixStrs);
pw.println("NAT timeout update callbacks received during the "
+ (isStarted ? "current" : "last")
+ " offload session: "
+ mNatUpdateCallbacksReceived);
pw.println("NAT timeout update netlink errors during the "
+ (isStarted ? "current" : "last")
+ " offload session: "
+ mNatUpdateNetlinkErrors);
}
private void updateNatTimeout(
int proto, String srcAddr, int srcPort, String dstAddr, int dstPort) {
final String protoName = protoNameFor(proto);
if (protoName == null) {
mLog.e("Unknown NAT update callback protocol: " + proto);
return;
}
final Inet4Address src = parseIPv4Address(srcAddr);
if (src == null) {
mLog.e("Failed to parse IPv4 address: " + srcAddr);
return;
}
if (!IpUtils.isValidUdpOrTcpPort(srcPort)) {
mLog.e("Invalid src port: " + srcPort);
return;
}
final Inet4Address dst = parseIPv4Address(dstAddr);
if (dst == null) {
mLog.e("Failed to parse IPv4 address: " + dstAddr);
return;
}
if (!IpUtils.isValidUdpOrTcpPort(dstPort)) {
mLog.e("Invalid dst port: " + dstPort);
return;
}
mNatUpdateCallbacksReceived++;
if (DBG) {
mLog.log(String.format("NAT timeout update: %s (%s, %s) -> (%s, %s)",
protoName, srcAddr, srcPort, dstAddr, dstPort));
}
final int timeoutSec = connectionTimeoutUpdateSecondsFor(proto);
final byte[] msg = ConntrackMessage.newIPv4TimeoutUpdateRequest(
proto, src, srcPort, dst, dstPort, timeoutSec);
try {
NetlinkSocket.sendOneShotKernelMessage(OsConstants.NETLINK_NETFILTER, msg);
} catch (ErrnoException e) {
mNatUpdateNetlinkErrors++;
mLog.e("Error updating NAT conntrack entry: " + e
+ ", msg: " + NetlinkConstants.hexify(msg));
mLog.log("NAT timeout update callbacks received: " + mNatUpdateCallbacksReceived);
mLog.log("NAT timeout update netlink errors: " + mNatUpdateNetlinkErrors);
}
}
private static Inet4Address parseIPv4Address(String addrString) {
try {
final InetAddress ip = InetAddress.parseNumericAddress(addrString);
// TODO: Consider other sanitization steps here, including perhaps:
// not eql to 0.0.0.0
// not within 169.254.0.0/16
// not within ::ffff:0.0.0.0/96
// not within ::/96
// et cetera.
if (ip instanceof Inet4Address) {
return (Inet4Address) ip;
}
} catch (IllegalArgumentException iae) {}
return null;
}
private static String protoNameFor(int proto) {
// OsConstants values are not constant expressions; no switch statement.
if (proto == OsConstants.IPPROTO_UDP) {
return "UDP";
} else if (proto == OsConstants.IPPROTO_TCP) {
return "TCP";
}
return null;
}
private static int connectionTimeoutUpdateSecondsFor(int proto) {
// TODO: Replace this with more thoughtful work, perhaps reading from
// and maybe writing to any required
//
// /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_*
// /proc/sys/net/netfilter/nf_conntrack_udp_timeout{,_stream}
//
// entries. TBD.
if (proto == OsConstants.IPPROTO_TCP) {
// Cf. /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_established
return 432000;
} else {
// Cf. /proc/sys/net/netfilter/nf_conntrack_udp_timeout_stream
return 180;
}
}
}

View File

@@ -21,10 +21,12 @@ import static com.android.internal.util.BitUtils.uint16;
import android.hardware.tetheroffload.control.V1_0.IOffloadControl;
import android.hardware.tetheroffload.control.V1_0.ITetheringOffloadCallback;
import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate;
import android.hardware.tetheroffload.control.V1_0.NetworkProtocol;
import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent;
import android.os.Handler;
import android.os.RemoteException;
import android.net.util.SharedLog;
import android.system.OsConstants;
import java.util.ArrayList;
@@ -327,13 +329,24 @@ public class OffloadHardwareInterface {
public void updateTimeout(NatTimeoutUpdate params) {
handler.post(() -> {
controlCb.onNatTimeoutUpdate(
params.proto,
networkProtocolToOsConstant(params.proto),
params.src.addr, uint16(params.src.port),
params.dst.addr, uint16(params.dst.port));
});
}
}
private static int networkProtocolToOsConstant(int proto) {
switch (proto) {
case NetworkProtocol.TCP: return OsConstants.IPPROTO_TCP;
case NetworkProtocol.UDP: return OsConstants.IPPROTO_UDP;
default:
// The caller checks this value and will log an error. Just make
// sure it won't collide with valid OsContants.IPPROTO_* values.
return -Math.abs(proto);
}
}
private static class CbResults {
boolean success;
String errMsg;

View File

@@ -205,44 +205,14 @@ public class IpReachabilityMonitor {
final byte[] msg = RtNetlinkNeighborMessage.newNewNeighborMessage(
1, ip, StructNdMsg.NUD_PROBE, ifIndex, null);
int errno = -OsConstants.EPROTO;
try (NetlinkSocket nlSocket = new NetlinkSocket(OsConstants.NETLINK_ROUTE)) {
final long IO_TIMEOUT = 300L;
nlSocket.connectToKernel();
nlSocket.sendMessage(msg, 0, msg.length, IO_TIMEOUT);
final ByteBuffer bytes = nlSocket.recvMessage(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 &&
(((NetlinkErrorMessage) response).getNlMsgError() != null)) {
errno = ((NetlinkErrorMessage) response).getNlMsgError().error;
if (errno != 0) {
// TODO: consider ignoring EINVAL (-22), which appears to be
// normal when probing a neighbor for which the kernel does
// not already have / no longer has a link layer address.
Log.e(TAG, "Error " + msgSnippet + ", errmsg=" + response.toString());
}
} else {
String errmsg;
if (response == null) {
bytes.position(0);
errmsg = "raw bytes: " + NetlinkConstants.hexify(bytes);
} else {
errmsg = response.toString();
}
Log.e(TAG, "Error " + msgSnippet + ", errmsg=" + errmsg);
}
try {
NetlinkSocket.sendOneShotKernelMessage(OsConstants.NETLINK_ROUTE, msg);
} catch (ErrnoException e) {
Log.e(TAG, "Error " + msgSnippet, e);
errno = -e.errno;
} catch (InterruptedIOException e) {
Log.e(TAG, "Error " + msgSnippet, e);
errno = -OsConstants.ETIMEDOUT;
} catch (SocketException e) {
Log.e(TAG, "Error " + msgSnippet, e);
errno = -OsConstants.EIO;
Log.e(TAG, "Error " + msgSnippet + ": " + e);
return -e.errno;
}
return errno;
return 0;
}
public IpReachabilityMonitor(Context context, String ifName, SharedLog log, Callback callback) {

View File

@@ -0,0 +1,117 @@
/*
* 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.netlink;
import static android.net.netlink.NetlinkConstants.alignedLengthOf;
import static android.net.netlink.StructNlAttr.makeNestedType;
import static android.net.netlink.StructNlAttr.NLA_HEADERLEN;
import static android.net.netlink.StructNlMsgHdr.NLM_F_ACK;
import static android.net.netlink.StructNlMsgHdr.NLM_F_DUMP;
import static android.net.netlink.StructNlMsgHdr.NLM_F_REPLACE;
import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST;
import static android.net.util.NetworkConstants.IPV4_ADDR_LEN;
import static java.nio.ByteOrder.BIG_ENDIAN;
import android.system.OsConstants;
import android.util.Log;
import libcore.io.SizeOf;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
* A NetlinkMessage subclass for netlink conntrack messages.
*
* see also: &lt;linux_src&gt;/include/uapi/linux/netfilter/nfnetlink_conntrack.h
*
* @hide
*/
public class ConntrackMessage extends NetlinkMessage {
public static final int STRUCT_SIZE = StructNlMsgHdr.STRUCT_SIZE + StructNfGenMsg.STRUCT_SIZE;
public static final short NFNL_SUBSYS_CTNETLINK = 1;
public static final short IPCTNL_MSG_CT_NEW = 0;
// enum ctattr_type
public static final short CTA_TUPLE_ORIG = 1;
public static final short CTA_TUPLE_REPLY = 2;
public static final short CTA_TIMEOUT = 7;
// enum ctattr_tuple
public static final short CTA_TUPLE_IP = 1;
public static final short CTA_TUPLE_PROTO = 2;
// enum ctattr_ip
public static final short CTA_IP_V4_SRC = 1;
public static final short CTA_IP_V4_DST = 2;
// enum ctattr_l4proto
public static final short CTA_PROTO_NUM = 1;
public static final short CTA_PROTO_SRC_PORT = 2;
public static final short CTA_PROTO_DST_PORT = 3;
public static byte[] newIPv4TimeoutUpdateRequest(
int proto, Inet4Address src, int sport, Inet4Address dst, int dport, int timeoutSec) {
// *** STYLE WARNING ***
//
// Code below this point uses extra block indentation to highlight the
// packing of nested tuple netlink attribute types.
final StructNlAttr ctaTupleOrig = new StructNlAttr(CTA_TUPLE_ORIG,
new StructNlAttr(CTA_TUPLE_IP,
new StructNlAttr(CTA_IP_V4_SRC, src),
new StructNlAttr(CTA_IP_V4_DST, dst)),
new StructNlAttr(CTA_TUPLE_PROTO,
new StructNlAttr(CTA_PROTO_NUM, (byte) proto),
new StructNlAttr(CTA_PROTO_SRC_PORT, (short) sport, BIG_ENDIAN),
new StructNlAttr(CTA_PROTO_DST_PORT, (short) dport, BIG_ENDIAN)));
final StructNlAttr ctaTimeout = new StructNlAttr(CTA_TIMEOUT, timeoutSec, BIG_ENDIAN);
final int payloadLength = ctaTupleOrig.getAlignedLength() + ctaTimeout.getAlignedLength();
final byte[] bytes = new byte[STRUCT_SIZE + payloadLength];
final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
byteBuffer.order(ByteOrder.nativeOrder());
final ConntrackMessage ctmsg = new ConntrackMessage();
ctmsg.mHeader.nlmsg_len = bytes.length;
ctmsg.mHeader.nlmsg_type = (NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_NEW;
ctmsg.mHeader.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_REPLACE;
ctmsg.mHeader.nlmsg_seq = 1;
ctmsg.pack(byteBuffer);
ctaTupleOrig.pack(byteBuffer);
ctaTimeout.pack(byteBuffer);
return bytes;
}
protected StructNfGenMsg mNfGenMsg;
private ConntrackMessage() {
super(new StructNlMsgHdr());
mNfGenMsg = new StructNfGenMsg((byte) OsConstants.AF_INET);
}
public void pack(ByteBuffer byteBuffer) {
mHeader.pack(byteBuffer);
mNfGenMsg.pack(byteBuffer);
}
}

View File

@@ -51,6 +51,47 @@ public class NetlinkSocket implements Closeable {
private long mLastRecvTimeoutMs;
private long mLastSendTimeoutMs;
public static void sendOneShotKernelMessage(int nlProto, byte[] msg) throws ErrnoException {
final String errPrefix = "Error in NetlinkSocket.sendOneShotKernelMessage";
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);
// recvMessage() guaranteed to not return null if it did not throw.
final NetlinkMessage response = NetlinkMessage.parse(bytes);
if (response != null && response instanceof NetlinkErrorMessage &&
(((NetlinkErrorMessage) response).getNlMsgError() != null)) {
final int errno = ((NetlinkErrorMessage) response).getNlMsgError().error;
if (errno != 0) {
// TODO: consider ignoring EINVAL (-22), which appears to be
// normal when probing a neighbor for which the kernel does
// not already have / no longer has a link layer address.
Log.e(TAG, errPrefix + ", errmsg=" + response.toString());
// Note: convert kernel errnos (negative) into userspace errnos (positive).
throw new ErrnoException(response.toString(), Math.abs(errno));
}
} else {
final String errmsg;
if (response == null) {
bytes.position(0);
errmsg = "raw bytes: " + NetlinkConstants.hexify(bytes);
} else {
errmsg = response.toString();
}
Log.e(TAG, errPrefix + ", errmsg=" + errmsg);
throw new ErrnoException(errmsg, OsConstants.EPROTO);
}
} catch (InterruptedIOException e) {
Log.e(TAG, errPrefix, e);
throw new ErrnoException(errPrefix, OsConstants.ETIMEDOUT, e);
} catch (SocketException e) {
Log.e(TAG, errPrefix, e);
throw new ErrnoException(errPrefix, OsConstants.EIO, e);
}
}
public NetlinkSocket(int nlProto) throws ErrnoException {
mDescriptor = Os.socket(
OsConstants.AF_NETLINK, OsConstants.SOCK_DGRAM, nlProto);

View File

@@ -36,7 +36,7 @@ import java.nio.ByteOrder;
/**
* A NetlinkMessage subclass for netlink error messages.
* A NetlinkMessage subclass for rtnetlink neighbor messages.
*
* see also: &lt;linux_src&gt;/include/uapi/linux/neighbour.h
*

View File

@@ -0,0 +1,51 @@
/*
* 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.netlink;
import libcore.io.SizeOf;
import java.nio.ByteBuffer;
/**
* struct nfgenmsg
*
* see &lt;linux_src&gt;/include/uapi/linux/netfilter/nfnetlink.h
*
* @hide
*/
public class StructNfGenMsg {
public static final int STRUCT_SIZE = 2 + SizeOf.SHORT;
public static final int NFNETLINK_V0 = 0;
final public byte nfgen_family;
final public byte version;
final public short res_id; // N.B.: this is big endian in the kernel
public StructNfGenMsg(byte family) {
nfgen_family = family;
version = (byte) NFNETLINK_V0;
res_id = (short) 0;
}
public void pack(ByteBuffer byteBuffer) {
byteBuffer.put(nfgen_family);
byteBuffer.put(version);
byteBuffer.putShort(res_id);
}
}

View File

@@ -34,7 +34,12 @@ import java.nio.ByteBuffer;
*/
public class StructNlAttr {
// Already aligned.
public static final int NLA_HEADERLEN = 4;
public static final int NLA_HEADERLEN = 4;
public static final int NLA_F_NESTED = (1 << 15);
public static short makeNestedType(short type) {
return (short) (type | NLA_F_NESTED);
}
// Return a (length, type) object only, without consuming any bytes in
// |byteBuffer| and without copying or interpreting any value bytes.
@@ -46,10 +51,17 @@ public class StructNlAttr {
}
final int baseOffset = byteBuffer.position();
final StructNlAttr struct = new StructNlAttr();
struct.nla_len = byteBuffer.getShort();
struct.nla_type = byteBuffer.getShort();
struct.mByteOrder = byteBuffer.order();
// Assume the byte order of the buffer is the expected byte order of the value.
final StructNlAttr struct = new StructNlAttr(byteBuffer.order());
// The byte order of nla_len and nla_type is always native.
final ByteOrder originalOrder = byteBuffer.order();
byteBuffer.order(ByteOrder.nativeOrder());
try {
struct.nla_len = byteBuffer.getShort();
struct.nla_type = byteBuffer.getShort();
} finally {
byteBuffer.order(originalOrder);
}
byteBuffer.position(baseOffset);
if (struct.nla_len < NLA_HEADERLEN) {
@@ -78,13 +90,65 @@ public class StructNlAttr {
return struct;
}
public short nla_len;
public short nla_len = (short) NLA_HEADERLEN;
public short nla_type;
public byte[] nla_value;
public ByteOrder mByteOrder;
public StructNlAttr() {
mByteOrder = ByteOrder.nativeOrder();
// The byte order used to read/write the value member. Netlink length and
// type members are always read/written in native order.
private ByteOrder mByteOrder = ByteOrder.nativeOrder();
public StructNlAttr() {}
public StructNlAttr(ByteOrder byteOrder) {
mByteOrder = byteOrder;
}
public StructNlAttr(short type, byte value) {
nla_type = type;
setValue(new byte[1]);
nla_value[0] = value;
}
public StructNlAttr(short type, short value) {
this(type, value, ByteOrder.nativeOrder());
}
public StructNlAttr(short type, short value, ByteOrder order) {
this(order);
nla_type = type;
setValue(new byte[SizeOf.SHORT]);
getValueAsByteBuffer().putShort(value);
}
public StructNlAttr(short type, int value) {
this(type, value, ByteOrder.nativeOrder());
}
public StructNlAttr(short type, int value, ByteOrder order) {
this(order);
nla_type = type;
setValue(new byte[SizeOf.INT]);
getValueAsByteBuffer().putInt(value);
}
public StructNlAttr(short type, InetAddress ip) {
nla_type = type;
setValue(ip.getAddress());
}
public StructNlAttr(short type, StructNlAttr... nested) {
this();
nla_type = makeNestedType(type);
int payloadLength = 0;
for (StructNlAttr nla : nested) payloadLength += nla.getAlignedLength();
setValue(new byte[payloadLength]);
final ByteBuffer buf = getValueAsByteBuffer();
for (StructNlAttr nla : nested) {
nla.pack(buf);
}
}
public int getAlignedLength() {
@@ -117,13 +181,25 @@ public class StructNlAttr {
}
public void pack(ByteBuffer byteBuffer) {
final ByteOrder originalOrder = byteBuffer.order();
final int originalPosition = byteBuffer.position();
byteBuffer.putShort(nla_len);
byteBuffer.putShort(nla_type);
byteBuffer.put(nla_value);
byteBuffer.order(ByteOrder.nativeOrder());
try {
byteBuffer.putShort(nla_len);
byteBuffer.putShort(nla_type);
if (nla_value != null) byteBuffer.put(nla_value);
} finally {
byteBuffer.order(originalOrder);
}
byteBuffer.position(originalPosition + getAlignedLength());
}
private void setValue(byte[] value) {
nla_value = value;
nla_len = (short) (NLA_HEADERLEN + ((nla_value != null) ? nla_value.length : 0));
}
@Override
public String toString() {
return "StructNlAttr{ "

View File

@@ -0,0 +1,131 @@
/*
* 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.netlink;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assume.assumeTrue;
import android.system.OsConstants;
import libcore.util.HexEncoding;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import org.junit.runner.RunWith;
import org.junit.Test;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class ConntrackMessageTest {
private static final boolean USING_LE = (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN);
// Example 1: TCP (192.168.43.209, 44333) -> (23.211.13.26, 443)
public static final String CT_V4UPDATE_TCP_HEX =
// struct nlmsghdr
"50000000" + // length = 80
"0001" + // type = (1 << 8) | 0
"0501" + // flags
"01000000" + // seqno = 1
"00000000" + // pid = 0
// struct nfgenmsg
"02" + // nfgen_family = AF_INET
"00" + // version = NFNETLINK_V0
"0000" + // res_id
// 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 C0A82BD1" + // nla_type=CTA_IP_V4_SRC, ip=192.168.43.209
"0800 0200 17D30D1A" + // nla_type=CTA_IP_V4_DST, ip=23.211.13.26
// struct nlattr
"1C00" + // nla_len = 28
"0280" + // nla_type = nested CTA_TUPLE_PROTO
"0500 0100 06 000000" + // nla_type=CTA_PROTO_NUM, proto=6
"0600 0200 AD2D 0000" + // nla_type=CTA_PROTO_SRC_PORT, port=44333 (big endian)
"0600 0300 01BB 0000" + // nla_type=CTA_PROTO_DST_PORT, port=443 (big endian)
// struct nlattr
"0800" + // nla_len = 8
"0700" + // nla_type = CTA_TIMEOUT
"00069780"; // nla_value = 432000 (big endian)
public static final byte[] CT_V4UPDATE_TCP_BYTES =
HexEncoding.decode(CT_V4UPDATE_TCP_HEX.replaceAll(" ", "").toCharArray(), false);
// Example 2: UDP (100.96.167.146, 37069) -> (216.58.197.10, 443)
public static final String CT_V4UPDATE_UDP_HEX =
// struct nlmsghdr
"50000000" + // length = 80
"0001" + // type = (1 << 8) | 0
"0501" + // flags
"01000000" + // seqno = 1
"00000000" + // pid = 0
// struct nfgenmsg
"02" + // nfgen_family = AF_INET
"00" + // version = NFNETLINK_V0
"0000" + // res_id
// 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 6460A792" + // nla_type=CTA_IP_V4_SRC, ip=100.96.167.146
"0800 0200 D83AC50A" + // nla_type=CTA_IP_V4_DST, ip=216.58.197.10
// struct nlattr
"1C00" + // nla_len = 28
"0280" + // nla_type = nested CTA_TUPLE_PROTO
"0500 0100 11 000000" + // nla_type=CTA_PROTO_NUM, proto=17
"0600 0200 90CD 0000" + // nla_type=CTA_PROTO_SRC_PORT, port=37069 (big endian)
"0600 0300 01BB 0000" + // nla_type=CTA_PROTO_DST_PORT, port=443 (big endian)
// struct nlattr
"0800" + // nla_len = 8
"0700" + // nla_type = CTA_TIMEOUT
"000000B4"; // nla_value = 180 (big endian)
public static final byte[] CT_V4UPDATE_UDP_BYTES =
HexEncoding.decode(CT_V4UPDATE_UDP_HEX.replaceAll(" ", "").toCharArray(), false);
@Test
public void testConntrackIPv4TcpTimeoutUpdate() throws Exception {
assumeTrue(USING_LE);
final byte[] tcp = ConntrackMessage.newIPv4TimeoutUpdateRequest(
OsConstants.IPPROTO_TCP,
(Inet4Address) InetAddress.getByName("192.168.43.209"), 44333,
(Inet4Address) InetAddress.getByName("23.211.13.26"), 443,
432000);
assertArrayEquals(CT_V4UPDATE_TCP_BYTES, tcp);
}
@Test
public void testConntrackIPv4UdpTimeoutUpdate() throws Exception {
assumeTrue(USING_LE);
final byte[] udp = ConntrackMessage.newIPv4TimeoutUpdateRequest(
OsConstants.IPPROTO_UDP,
(Inet4Address) InetAddress.getByName("100.96.167.146"), 37069,
(Inet4Address) InetAddress.getByName("216.58.197.10"), 443,
180);
assertArrayEquals(CT_V4UPDATE_UDP_BYTES, udp);
}
}