Migrate netlink-client to net-utils-device-common-netlink.
Move netlink stuff to frameworks/libs/net/common/device, and build the source files as an individual libraray. NetworkStack module just depends on the net-utils-device-common-netlink. Besides, also fix the incorrect format detected by checkstyle_hook script such as missing java doc and make some public function as private, rename the variable and etc. Bug: 192535368 Test: atest NetworkStaticlibTests Change-Id: I00e7f30be1bc9ebc2e24d7cd53efc403d6ba3daa
This commit is contained in:
parent
5fa15e6098
commit
57b3442c65
34 changed files with 5388 additions and 5 deletions
|
|
@ -34,7 +34,10 @@ package {
|
|||
java_library {
|
||||
name: "net-utils-device-common",
|
||||
srcs: [
|
||||
"device/com/android/net/module/util/**/*.java",
|
||||
"device/com/android/net/module/util/DeviceConfigUtils.java",
|
||||
"device/com/android/net/module/util/FdEventsReader.java",
|
||||
"device/com/android/net/module/util/HexDump.java",
|
||||
"device/com/android/net/module/util/PacketReader.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",
|
||||
|
|
@ -90,6 +93,37 @@ filegroup {
|
|||
],
|
||||
}
|
||||
|
||||
java_library {
|
||||
name: "net-utils-device-common-netlink",
|
||||
// TODO: Ipv6Utils and Struct stuff could be separated out of th 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: [
|
||||
"device/com/android/net/module/util/HexDump.java",
|
||||
"device/com/android/net/module/util/Ipv6Utils.java",
|
||||
"device/com/android/net/module/util/Struct.java",
|
||||
"device/com/android/net/module/util/netlink/*.java",
|
||||
"device/com/android/net/module/util/structs/*.java",
|
||||
],
|
||||
sdk_version: "system_current",
|
||||
min_sdk_version: "29",
|
||||
visibility: [
|
||||
"//frameworks/libs/net/common/testutils:__subpackages__",
|
||||
"//packages/modules/Connectivity:__subpackages__",
|
||||
"//packages/modules/NetworkStack:__subpackages__",
|
||||
],
|
||||
static_libs: [
|
||||
"net-utils-framework-common",
|
||||
],
|
||||
libs: [
|
||||
"androidx.annotation_annotation",
|
||||
],
|
||||
apex_available: [
|
||||
"com.android.tethering",
|
||||
"//apex_available:platform",
|
||||
],
|
||||
}
|
||||
|
||||
java_library {
|
||||
name: "net-utils-framework-common",
|
||||
srcs: [
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
package com.android.net.module.util;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Hex utility functions.
|
||||
|
|
|
|||
|
|
@ -16,10 +16,11 @@
|
|||
|
||||
package com.android.net.module.util;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.net.MacAddress;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,558 @@
|
|||
/*
|
||||
* 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.netlink;
|
||||
|
||||
import static android.system.OsConstants.IPPROTO_TCP;
|
||||
import static android.system.OsConstants.IPPROTO_UDP;
|
||||
|
||||
import static com.android.net.module.util.netlink.StructNlAttr.findNextAttrOfType;
|
||||
import static com.android.net.module.util.netlink.StructNlAttr.makeNestedType;
|
||||
import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_ACK;
|
||||
import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REPLACE;
|
||||
import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REQUEST;
|
||||
|
||||
import static java.nio.ByteOrder.BIG_ENDIAN;
|
||||
|
||||
import android.system.OsConstants;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import java.net.Inet4Address;
|
||||
import java.net.InetAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A NetlinkMessage subclass for netlink conntrack messages.
|
||||
*
|
||||
* see also: <linux_src>/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;
|
||||
|
||||
// enum ctattr_type
|
||||
public static final short CTA_TUPLE_ORIG = 1;
|
||||
public static final short CTA_TUPLE_REPLY = 2;
|
||||
public static final short CTA_STATUS = 3;
|
||||
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;
|
||||
|
||||
// enum ip_conntrack_status
|
||||
public static final int IPS_EXPECTED = 0x00000001;
|
||||
public static final int IPS_SEEN_REPLY = 0x00000002;
|
||||
public static final int IPS_ASSURED = 0x00000004;
|
||||
public static final int IPS_CONFIRMED = 0x00000008;
|
||||
public static final int IPS_SRC_NAT = 0x00000010;
|
||||
public static final int IPS_DST_NAT = 0x00000020;
|
||||
public static final int IPS_SEQ_ADJUST = 0x00000040;
|
||||
public static final int IPS_SRC_NAT_DONE = 0x00000080;
|
||||
public static final int IPS_DST_NAT_DONE = 0x00000100;
|
||||
public static final int IPS_DYING = 0x00000200;
|
||||
public static final int IPS_FIXED_TIMEOUT = 0x00000400;
|
||||
public static final int IPS_TEMPLATE = 0x00000800;
|
||||
public static final int IPS_UNTRACKED = 0x00001000;
|
||||
public static final int IPS_HELPER = 0x00002000;
|
||||
public static final int IPS_OFFLOAD = 0x00004000;
|
||||
public static final int IPS_HW_OFFLOAD = 0x00008000;
|
||||
|
||||
// ip_conntrack_status mask
|
||||
// Interesting on the NAT conntrack session which has already seen two direction traffic.
|
||||
// TODO: Probably IPS_{SRC, DST}_NAT_DONE are also interesting.
|
||||
public static final int ESTABLISHED_MASK = IPS_CONFIRMED | IPS_ASSURED | IPS_SEEN_REPLY
|
||||
| IPS_SRC_NAT;
|
||||
// Interesting on the established NAT conntrack session which is dying.
|
||||
public static final int DYING_MASK = ESTABLISHED_MASK | IPS_DYING;
|
||||
|
||||
/**
|
||||
* A tuple for the conntrack connection information.
|
||||
*
|
||||
* see also CTA_TUPLE_ORIG and CTA_TUPLE_REPLY.
|
||||
*/
|
||||
public static class Tuple {
|
||||
public final Inet4Address srcIp;
|
||||
public final Inet4Address dstIp;
|
||||
|
||||
// Both port and protocol number are unsigned numbers stored in signed integers, and that
|
||||
// callers that want to compare them to integers should either cast those integers, or
|
||||
// convert them to unsigned using Byte.toUnsignedInt() and Short.toUnsignedInt().
|
||||
public final short srcPort;
|
||||
public final short dstPort;
|
||||
public final byte protoNum;
|
||||
|
||||
public Tuple(TupleIpv4 ip, TupleProto proto) {
|
||||
this.srcIp = ip.src;
|
||||
this.dstIp = ip.dst;
|
||||
this.srcPort = proto.srcPort;
|
||||
this.dstPort = proto.dstPort;
|
||||
this.protoNum = proto.protoNum;
|
||||
}
|
||||
|
||||
@Override
|
||||
@VisibleForTesting
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof Tuple)) return false;
|
||||
Tuple that = (Tuple) o;
|
||||
return Objects.equals(this.srcIp, that.srcIp)
|
||||
&& Objects.equals(this.dstIp, that.dstIp)
|
||||
&& this.srcPort == that.srcPort
|
||||
&& this.dstPort == that.dstPort
|
||||
&& this.protoNum == that.protoNum;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(srcIp, dstIp, srcPort, dstPort, protoNum);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final String srcIpStr = (srcIp == null) ? "null" : srcIp.getHostAddress();
|
||||
final String dstIpStr = (dstIp == null) ? "null" : dstIp.getHostAddress();
|
||||
final String protoStr = NetlinkConstants.stringForProtocol(protoNum);
|
||||
|
||||
return "Tuple{"
|
||||
+ protoStr + ": "
|
||||
+ srcIpStr + ":" + Short.toUnsignedInt(srcPort) + " -> "
|
||||
+ dstIpStr + ":" + Short.toUnsignedInt(dstPort)
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A tuple for the conntrack connection address.
|
||||
*
|
||||
* see also CTA_TUPLE_IP.
|
||||
*/
|
||||
public static class TupleIpv4 {
|
||||
public final Inet4Address src;
|
||||
public final Inet4Address dst;
|
||||
|
||||
public TupleIpv4(Inet4Address src, Inet4Address dst) {
|
||||
this.src = src;
|
||||
this.dst = dst;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A tuple for the conntrack connection protocol.
|
||||
*
|
||||
* see also CTA_TUPLE_PROTO.
|
||||
*/
|
||||
public static class TupleProto {
|
||||
public final byte protoNum;
|
||||
public final short srcPort;
|
||||
public final short dstPort;
|
||||
|
||||
public TupleProto(byte protoNum, short srcPort, short dstPort) {
|
||||
this.protoNum = protoNum;
|
||||
this.srcPort = srcPort;
|
||||
this.dstPort = dstPort;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a netlink message to refresh IPv4 conntrack entry timeout.
|
||||
*/
|
||||
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 = (NetlinkConstants.NFNL_SUBSYS_CTNETLINK << 8)
|
||||
| NetlinkConstants.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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a netfilter conntrack message from a {@link ByteBuffer}.
|
||||
*
|
||||
* @param header the netlink message header.
|
||||
* @param byteBuffer The buffer from which to parse the netfilter conntrack message.
|
||||
* @return the parsed netfilter conntrack message, or {@code null} if the netfilter conntrack
|
||||
* message could not be parsed successfully (for example, if it was truncated).
|
||||
*/
|
||||
public static ConntrackMessage parse(StructNlMsgHdr header, ByteBuffer byteBuffer) {
|
||||
// Just build the netlink header and netfilter header for now and pretend the whole message
|
||||
// was consumed.
|
||||
// TODO: Parse the conntrack attributes.
|
||||
final StructNfGenMsg nfGenMsg = StructNfGenMsg.parse(byteBuffer);
|
||||
if (nfGenMsg == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final int baseOffset = byteBuffer.position();
|
||||
StructNlAttr nlAttr = findNextAttrOfType(CTA_STATUS, byteBuffer);
|
||||
int status = 0;
|
||||
if (nlAttr != null) {
|
||||
status = nlAttr.getValueAsBe32(0);
|
||||
}
|
||||
|
||||
byteBuffer.position(baseOffset);
|
||||
nlAttr = findNextAttrOfType(CTA_TIMEOUT, byteBuffer);
|
||||
int timeoutSec = 0;
|
||||
if (nlAttr != null) {
|
||||
timeoutSec = nlAttr.getValueAsBe32(0);
|
||||
}
|
||||
|
||||
byteBuffer.position(baseOffset);
|
||||
nlAttr = findNextAttrOfType(makeNestedType(CTA_TUPLE_ORIG), byteBuffer);
|
||||
Tuple tupleOrig = null;
|
||||
if (nlAttr != null) {
|
||||
tupleOrig = parseTuple(nlAttr.getValueAsByteBuffer());
|
||||
}
|
||||
|
||||
byteBuffer.position(baseOffset);
|
||||
nlAttr = findNextAttrOfType(makeNestedType(CTA_TUPLE_REPLY), byteBuffer);
|
||||
Tuple tupleReply = null;
|
||||
if (nlAttr != null) {
|
||||
tupleReply = parseTuple(nlAttr.getValueAsByteBuffer());
|
||||
}
|
||||
|
||||
// Advance to the end of the message.
|
||||
byteBuffer.position(baseOffset);
|
||||
final int kMinConsumed = StructNlMsgHdr.STRUCT_SIZE + StructNfGenMsg.STRUCT_SIZE;
|
||||
final int kAdditionalSpace = NetlinkConstants.alignedLengthOf(
|
||||
header.nlmsg_len - kMinConsumed);
|
||||
if (byteBuffer.remaining() < kAdditionalSpace) {
|
||||
return null;
|
||||
}
|
||||
byteBuffer.position(baseOffset + kAdditionalSpace);
|
||||
|
||||
return new ConntrackMessage(header, nfGenMsg, tupleOrig, tupleReply, status, timeoutSec);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a conntrack tuple from a {@link ByteBuffer}.
|
||||
*
|
||||
* The attribute parsing is interesting on:
|
||||
* - CTA_TUPLE_IP
|
||||
* CTA_IP_V4_SRC
|
||||
* CTA_IP_V4_DST
|
||||
* - CTA_TUPLE_PROTO
|
||||
* CTA_PROTO_NUM
|
||||
* CTA_PROTO_SRC_PORT
|
||||
* CTA_PROTO_DST_PORT
|
||||
*
|
||||
* Assume that the minimum size is the sum of CTA_TUPLE_IP (size: 20) and CTA_TUPLE_PROTO
|
||||
* (size: 28). Here is an example for an expected CTA_TUPLE_ORIG message in raw data:
|
||||
* +--------------------------------------------------------------------------------------+
|
||||
* | CTA_TUPLE_ORIG |
|
||||
* +--------------------------+-----------------------------------------------------------+
|
||||
* | 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 |
|
||||
* | 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) |
|
||||
* +--------------------------+-----------------------------------------------------------+
|
||||
*
|
||||
* The position of the byte buffer doesn't set to the end when the function returns. It is okay
|
||||
* because the caller ConntrackMessage#parse has passed a copy which is used for this parser
|
||||
* only. Moreover, the parser behavior is the same as other existing netlink struct class
|
||||
* parser. Ex: StructInetDiagMsg#parse.
|
||||
*/
|
||||
@Nullable
|
||||
private static Tuple parseTuple(@Nullable ByteBuffer byteBuffer) {
|
||||
if (byteBuffer == null) return null;
|
||||
|
||||
TupleIpv4 tupleIpv4 = null;
|
||||
TupleProto tupleProto = null;
|
||||
|
||||
final int baseOffset = byteBuffer.position();
|
||||
StructNlAttr nlAttr = findNextAttrOfType(makeNestedType(CTA_TUPLE_IP), byteBuffer);
|
||||
if (nlAttr != null) {
|
||||
tupleIpv4 = parseTupleIpv4(nlAttr.getValueAsByteBuffer());
|
||||
}
|
||||
if (tupleIpv4 == null) return null;
|
||||
|
||||
byteBuffer.position(baseOffset);
|
||||
nlAttr = findNextAttrOfType(makeNestedType(CTA_TUPLE_PROTO), byteBuffer);
|
||||
if (nlAttr != null) {
|
||||
tupleProto = parseTupleProto(nlAttr.getValueAsByteBuffer());
|
||||
}
|
||||
if (tupleProto == null) return null;
|
||||
|
||||
return new Tuple(tupleIpv4, tupleProto);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Inet4Address castToInet4Address(@Nullable InetAddress address) {
|
||||
if (address == null || !(address instanceof Inet4Address)) return null;
|
||||
return (Inet4Address) address;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static TupleIpv4 parseTupleIpv4(@Nullable ByteBuffer byteBuffer) {
|
||||
if (byteBuffer == null) return null;
|
||||
|
||||
Inet4Address src = null;
|
||||
Inet4Address dst = null;
|
||||
|
||||
final int baseOffset = byteBuffer.position();
|
||||
StructNlAttr nlAttr = findNextAttrOfType(CTA_IP_V4_SRC, byteBuffer);
|
||||
if (nlAttr != null) {
|
||||
src = castToInet4Address(nlAttr.getValueAsInetAddress());
|
||||
}
|
||||
if (src == null) return null;
|
||||
|
||||
byteBuffer.position(baseOffset);
|
||||
nlAttr = findNextAttrOfType(CTA_IP_V4_DST, byteBuffer);
|
||||
if (nlAttr != null) {
|
||||
dst = castToInet4Address(nlAttr.getValueAsInetAddress());
|
||||
}
|
||||
if (dst == null) return null;
|
||||
|
||||
return new TupleIpv4(src, dst);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static TupleProto parseTupleProto(@Nullable ByteBuffer byteBuffer) {
|
||||
if (byteBuffer == null) return null;
|
||||
|
||||
byte protoNum = 0;
|
||||
short srcPort = 0;
|
||||
short dstPort = 0;
|
||||
|
||||
final int baseOffset = byteBuffer.position();
|
||||
StructNlAttr nlAttr = findNextAttrOfType(CTA_PROTO_NUM, byteBuffer);
|
||||
if (nlAttr != null) {
|
||||
protoNum = nlAttr.getValueAsByte((byte) 0);
|
||||
}
|
||||
if (!(protoNum == IPPROTO_TCP || protoNum == IPPROTO_UDP)) return null;
|
||||
|
||||
byteBuffer.position(baseOffset);
|
||||
nlAttr = StructNlAttr.findNextAttrOfType(CTA_PROTO_SRC_PORT, byteBuffer);
|
||||
if (nlAttr != null) {
|
||||
srcPort = nlAttr.getValueAsBe16((short) 0);
|
||||
}
|
||||
if (srcPort == 0) return null;
|
||||
|
||||
byteBuffer.position(baseOffset);
|
||||
nlAttr = StructNlAttr.findNextAttrOfType(CTA_PROTO_DST_PORT, byteBuffer);
|
||||
if (nlAttr != null) {
|
||||
dstPort = nlAttr.getValueAsBe16((short) 0);
|
||||
}
|
||||
if (dstPort == 0) return null;
|
||||
|
||||
return new TupleProto(protoNum, srcPort, dstPort);
|
||||
}
|
||||
|
||||
/**
|
||||
* Netfilter header.
|
||||
*/
|
||||
public final StructNfGenMsg nfGenMsg;
|
||||
/**
|
||||
* Original direction conntrack tuple.
|
||||
*
|
||||
* The tuple is determined by the parsed attribute value CTA_TUPLE_ORIG, or null if the
|
||||
* tuple could not be parsed successfully (for example, if it was truncated or absent).
|
||||
*/
|
||||
@Nullable
|
||||
public final Tuple tupleOrig;
|
||||
/**
|
||||
* Reply direction conntrack tuple.
|
||||
*
|
||||
* The tuple is determined by the parsed attribute value CTA_TUPLE_REPLY, or null if the
|
||||
* tuple could not be parsed successfully (for example, if it was truncated or absent).
|
||||
*/
|
||||
@Nullable
|
||||
public final Tuple tupleReply;
|
||||
/**
|
||||
* Connection status. A bitmask of ip_conntrack_status enum flags.
|
||||
*
|
||||
* The status is determined by the parsed attribute value CTA_STATUS, or 0 if the status could
|
||||
* not be parsed successfully (for example, if it was truncated or absent). For the message
|
||||
* from kernel, the valid status is non-zero. For the message from user space, the status may
|
||||
* be 0 (absent).
|
||||
*/
|
||||
public final int status;
|
||||
/**
|
||||
* Conntrack timeout.
|
||||
*
|
||||
* The timeout is determined by the parsed attribute value CTA_TIMEOUT, or 0 if the timeout
|
||||
* could not be parsed successfully (for example, if it was truncated or absent). For
|
||||
* IPCTNL_MSG_CT_NEW event, the valid timeout is non-zero. For IPCTNL_MSG_CT_DELETE event, the
|
||||
* timeout is 0 (absent).
|
||||
*/
|
||||
public final int timeoutSec;
|
||||
|
||||
private ConntrackMessage() {
|
||||
super(new StructNlMsgHdr());
|
||||
nfGenMsg = new StructNfGenMsg((byte) OsConstants.AF_INET);
|
||||
|
||||
// This constructor is only used by #newIPv4TimeoutUpdateRequest which doesn't use these
|
||||
// data member for packing message. Simply fill them to null or 0.
|
||||
tupleOrig = null;
|
||||
tupleReply = null;
|
||||
status = 0;
|
||||
timeoutSec = 0;
|
||||
}
|
||||
|
||||
private ConntrackMessage(@NonNull StructNlMsgHdr header, @NonNull StructNfGenMsg nfGenMsg,
|
||||
@Nullable Tuple tupleOrig, @Nullable Tuple tupleReply, int status, int timeoutSec) {
|
||||
super(header);
|
||||
this.nfGenMsg = nfGenMsg;
|
||||
this.tupleOrig = tupleOrig;
|
||||
this.tupleReply = tupleReply;
|
||||
this.status = status;
|
||||
this.timeoutSec = timeoutSec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a netfilter message to {@link ByteBuffer}.
|
||||
*/
|
||||
public void pack(ByteBuffer byteBuffer) {
|
||||
mHeader.pack(byteBuffer);
|
||||
nfGenMsg.pack(byteBuffer);
|
||||
}
|
||||
|
||||
public short getMessageType() {
|
||||
return (short) (getHeader().nlmsg_type & ~(NetlinkConstants.NFNL_SUBSYS_CTNETLINK << 8));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an ip conntrack status to a string.
|
||||
*/
|
||||
public static String stringForIpConntrackStatus(int flags) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
||||
if ((flags & IPS_EXPECTED) != 0) {
|
||||
sb.append("IPS_EXPECTED");
|
||||
}
|
||||
if ((flags & IPS_SEEN_REPLY) != 0) {
|
||||
if (sb.length() > 0) sb.append("|");
|
||||
sb.append("IPS_SEEN_REPLY");
|
||||
}
|
||||
if ((flags & IPS_ASSURED) != 0) {
|
||||
if (sb.length() > 0) sb.append("|");
|
||||
sb.append("IPS_ASSURED");
|
||||
}
|
||||
if ((flags & IPS_CONFIRMED) != 0) {
|
||||
if (sb.length() > 0) sb.append("|");
|
||||
sb.append("IPS_CONFIRMED");
|
||||
}
|
||||
if ((flags & IPS_SRC_NAT) != 0) {
|
||||
if (sb.length() > 0) sb.append("|");
|
||||
sb.append("IPS_SRC_NAT");
|
||||
}
|
||||
if ((flags & IPS_DST_NAT) != 0) {
|
||||
if (sb.length() > 0) sb.append("|");
|
||||
sb.append("IPS_DST_NAT");
|
||||
}
|
||||
if ((flags & IPS_SEQ_ADJUST) != 0) {
|
||||
if (sb.length() > 0) sb.append("|");
|
||||
sb.append("IPS_SEQ_ADJUST");
|
||||
}
|
||||
if ((flags & IPS_SRC_NAT_DONE) != 0) {
|
||||
if (sb.length() > 0) sb.append("|");
|
||||
sb.append("IPS_SRC_NAT_DONE");
|
||||
}
|
||||
if ((flags & IPS_DST_NAT_DONE) != 0) {
|
||||
if (sb.length() > 0) sb.append("|");
|
||||
sb.append("IPS_DST_NAT_DONE");
|
||||
}
|
||||
if ((flags & IPS_DYING) != 0) {
|
||||
if (sb.length() > 0) sb.append("|");
|
||||
sb.append("IPS_DYING");
|
||||
}
|
||||
if ((flags & IPS_FIXED_TIMEOUT) != 0) {
|
||||
if (sb.length() > 0) sb.append("|");
|
||||
sb.append("IPS_FIXED_TIMEOUT");
|
||||
}
|
||||
if ((flags & IPS_TEMPLATE) != 0) {
|
||||
if (sb.length() > 0) sb.append("|");
|
||||
sb.append("IPS_TEMPLATE");
|
||||
}
|
||||
if ((flags & IPS_UNTRACKED) != 0) {
|
||||
if (sb.length() > 0) sb.append("|");
|
||||
sb.append("IPS_UNTRACKED");
|
||||
}
|
||||
if ((flags & IPS_HELPER) != 0) {
|
||||
if (sb.length() > 0) sb.append("|");
|
||||
sb.append("IPS_HELPER");
|
||||
}
|
||||
if ((flags & IPS_OFFLOAD) != 0) {
|
||||
if (sb.length() > 0) sb.append("|");
|
||||
sb.append("IPS_OFFLOAD");
|
||||
}
|
||||
if ((flags & IPS_HW_OFFLOAD) != 0) {
|
||||
if (sb.length() > 0) sb.append("|");
|
||||
sb.append("IPS_HW_OFFLOAD");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ConntrackMessage{"
|
||||
+ "nlmsghdr{"
|
||||
+ (mHeader == null ? "" : mHeader.toString(OsConstants.NETLINK_NETFILTER))
|
||||
+ "}, "
|
||||
+ "nfgenmsg{" + nfGenMsg + "}, "
|
||||
+ "tuple_orig{" + tupleOrig + "}, "
|
||||
+ "tuple_reply{" + tupleReply + "}, "
|
||||
+ "status{" + status + "(" + stringForIpConntrackStatus(status) + ")" + "}, "
|
||||
+ "timeout_sec{" + Integer.toUnsignedLong(timeoutSec) + "}"
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,231 @@
|
|||
/*
|
||||
* Copyright (C) 2018 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.netlink;
|
||||
|
||||
import static android.os.Process.INVALID_UID;
|
||||
import static android.system.OsConstants.AF_INET;
|
||||
import static android.system.OsConstants.AF_INET6;
|
||||
import static android.system.OsConstants.IPPROTO_UDP;
|
||||
import static android.system.OsConstants.NETLINK_INET_DIAG;
|
||||
|
||||
import static com.android.net.module.util.netlink.NetlinkConstants.SOCK_DIAG_BY_FAMILY;
|
||||
import static com.android.net.module.util.netlink.NetlinkSocket.DEFAULT_RECV_BUFSIZE;
|
||||
import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_DUMP;
|
||||
import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REQUEST;
|
||||
|
||||
import android.net.util.SocketUtils;
|
||||
import android.system.ErrnoException;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
/**
|
||||
* A NetlinkMessage subclass for netlink inet_diag messages.
|
||||
*
|
||||
* see also: <linux_src>/include/uapi/linux/inet_diag.h
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class InetDiagMessage extends NetlinkMessage {
|
||||
public static final String TAG = "InetDiagMessage";
|
||||
private static final int TIMEOUT_MS = 500;
|
||||
|
||||
/**
|
||||
* Construct an inet_diag_req_v2 message. This method will throw {@code NullPointerException}
|
||||
* if local and remote are not both null or both non-null.
|
||||
*/
|
||||
public static byte[] inetDiagReqV2(int protocol, InetSocketAddress local,
|
||||
InetSocketAddress remote, int family, short flags) {
|
||||
return inetDiagReqV2(protocol, local, remote, family, flags, 0 /* pad */,
|
||||
0 /* idiagExt */, StructInetDiagReqV2.INET_DIAG_REQ_V2_ALL_STATES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an inet_diag_req_v2 message. This method will throw {@code NullPointerException}
|
||||
* if local and remote are not both null or both non-null.
|
||||
*
|
||||
* @param protocol the request protocol type. This should be set to one of IPPROTO_TCP,
|
||||
* IPPROTO_UDP, or IPPROTO_UDPLITE.
|
||||
* @param local local socket address of the target socket. This will be packed into a
|
||||
* {@Code StructInetDiagSockId}. Request to diagnose for all sockets if both of
|
||||
* local or remote address is null.
|
||||
* @param remote remote socket address of the target socket. This will be packed into a
|
||||
* {@Code StructInetDiagSockId}. Request to diagnose for all sockets if both of
|
||||
* local or remote address is null.
|
||||
* @param family the ip family of the request message. This should be set to either AF_INET or
|
||||
* AF_INET6 for IPv4 or IPv6 sockets respectively.
|
||||
* @param flags message flags. See <linux_src>/include/uapi/linux/netlink.h.
|
||||
* @param pad for raw socket protocol specification.
|
||||
* @param idiagExt a set of flags defining what kind of extended information to report.
|
||||
* @param state a bit mask that defines a filter of socket states.
|
||||
*
|
||||
* @return bytes array representation of the message
|
||||
*/
|
||||
public static byte[] inetDiagReqV2(int protocol, @Nullable InetSocketAddress local,
|
||||
@Nullable InetSocketAddress remote, int family, short flags, int pad, int idiagExt,
|
||||
int state) throws NullPointerException {
|
||||
final byte[] bytes = new byte[StructNlMsgHdr.STRUCT_SIZE + StructInetDiagReqV2.STRUCT_SIZE];
|
||||
final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
|
||||
byteBuffer.order(ByteOrder.nativeOrder());
|
||||
|
||||
final StructNlMsgHdr nlMsgHdr = new StructNlMsgHdr();
|
||||
nlMsgHdr.nlmsg_len = bytes.length;
|
||||
nlMsgHdr.nlmsg_type = SOCK_DIAG_BY_FAMILY;
|
||||
nlMsgHdr.nlmsg_flags = flags;
|
||||
nlMsgHdr.pack(byteBuffer);
|
||||
final StructInetDiagReqV2 inetDiagReqV2 =
|
||||
new StructInetDiagReqV2(protocol, local, remote, family, pad, idiagExt, state);
|
||||
|
||||
inetDiagReqV2.pack(byteBuffer);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public StructInetDiagMsg mStructInetDiagMsg;
|
||||
|
||||
private InetDiagMessage(StructNlMsgHdr header) {
|
||||
super(header);
|
||||
mStructInetDiagMsg = new StructInetDiagMsg();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an inet_diag_req_v2 message from buffer.
|
||||
*/
|
||||
public static InetDiagMessage parse(StructNlMsgHdr header, ByteBuffer byteBuffer) {
|
||||
final InetDiagMessage msg = new InetDiagMessage(header);
|
||||
msg.mStructInetDiagMsg = StructInetDiagMsg.parse(byteBuffer);
|
||||
return msg;
|
||||
}
|
||||
|
||||
private static int lookupUidByFamily(int protocol, InetSocketAddress local,
|
||||
InetSocketAddress remote, int family, short flags,
|
||||
FileDescriptor fd)
|
||||
throws ErrnoException, InterruptedIOException {
|
||||
byte[] msg = inetDiagReqV2(protocol, local, remote, family, flags);
|
||||
NetlinkSocket.sendMessage(fd, msg, 0, msg.length, TIMEOUT_MS);
|
||||
ByteBuffer response = NetlinkSocket.recvMessage(fd, DEFAULT_RECV_BUFSIZE, TIMEOUT_MS);
|
||||
|
||||
final NetlinkMessage nlMsg = NetlinkMessage.parse(response, NETLINK_INET_DIAG);
|
||||
final StructNlMsgHdr hdr = nlMsg.getHeader();
|
||||
if (hdr.nlmsg_type == NetlinkConstants.NLMSG_DONE) {
|
||||
return INVALID_UID;
|
||||
}
|
||||
if (nlMsg instanceof InetDiagMessage) {
|
||||
return ((InetDiagMessage) nlMsg).mStructInetDiagMsg.idiag_uid;
|
||||
}
|
||||
return INVALID_UID;
|
||||
}
|
||||
|
||||
private static final int[] FAMILY = {AF_INET6, AF_INET};
|
||||
|
||||
private static int lookupUid(int protocol, InetSocketAddress local,
|
||||
InetSocketAddress remote, FileDescriptor fd)
|
||||
throws ErrnoException, InterruptedIOException {
|
||||
int uid;
|
||||
|
||||
for (int family : FAMILY) {
|
||||
/**
|
||||
* For exact match lookup, swap local and remote for UDP lookups due to kernel
|
||||
* bug which will not be fixed. See aosp/755889 and
|
||||
* https://www.mail-archive.com/netdev@vger.kernel.org/msg248638.html
|
||||
*/
|
||||
if (protocol == IPPROTO_UDP) {
|
||||
uid = lookupUidByFamily(protocol, remote, local, family, NLM_F_REQUEST, fd);
|
||||
} else {
|
||||
uid = lookupUidByFamily(protocol, local, remote, family, NLM_F_REQUEST, fd);
|
||||
}
|
||||
if (uid != INVALID_UID) {
|
||||
return uid;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For UDP it's possible for a socket to send packets to arbitrary destinations, even if the
|
||||
* socket is not connected (and even if the socket is connected to a different destination).
|
||||
* If we want this API to work for such packets, then on miss we need to do a second lookup
|
||||
* with only the local address and port filled in.
|
||||
* Always use flags == NLM_F_REQUEST | NLM_F_DUMP for wildcard.
|
||||
*/
|
||||
if (protocol == IPPROTO_UDP) {
|
||||
try {
|
||||
InetSocketAddress wildcard = new InetSocketAddress(
|
||||
Inet6Address.getByName("::"), 0);
|
||||
uid = lookupUidByFamily(protocol, local, wildcard, AF_INET6,
|
||||
(short) (NLM_F_REQUEST | NLM_F_DUMP), fd);
|
||||
if (uid != INVALID_UID) {
|
||||
return uid;
|
||||
}
|
||||
wildcard = new InetSocketAddress(Inet4Address.getByName("0.0.0.0"), 0);
|
||||
uid = lookupUidByFamily(protocol, local, wildcard, AF_INET,
|
||||
(short) (NLM_F_REQUEST | NLM_F_DUMP), fd);
|
||||
if (uid != INVALID_UID) {
|
||||
return uid;
|
||||
}
|
||||
} catch (UnknownHostException e) {
|
||||
Log.e(TAG, e.toString());
|
||||
}
|
||||
}
|
||||
return INVALID_UID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use an inet_diag socket to look up the UID associated with the input local and remote
|
||||
* address/port and protocol of a connection.
|
||||
*/
|
||||
public static int getConnectionOwnerUid(int protocol, InetSocketAddress local,
|
||||
InetSocketAddress remote) {
|
||||
int uid = INVALID_UID;
|
||||
FileDescriptor fd = null;
|
||||
try {
|
||||
fd = NetlinkSocket.forProto(NETLINK_INET_DIAG);
|
||||
NetlinkSocket.connectToKernel(fd);
|
||||
uid = lookupUid(protocol, local, remote, fd);
|
||||
} catch (ErrnoException | SocketException | IllegalArgumentException
|
||||
| InterruptedIOException e) {
|
||||
Log.e(TAG, e.toString());
|
||||
} finally {
|
||||
if (fd != null) {
|
||||
try {
|
||||
SocketUtils.closeSocket(fd);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
return uid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "InetDiagMessage{ "
|
||||
+ "nlmsghdr{"
|
||||
+ (mHeader == null ? "" : mHeader.toString(NETLINK_INET_DIAG)) + "}, "
|
||||
+ "inet_diag_msg{"
|
||||
+ (mStructInetDiagMsg == null ? "" : mStructInetDiagMsg.toString()) + "} "
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* 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.netlink;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Base class for IPv6 neighbour discovery options.
|
||||
*/
|
||||
public class NdOption {
|
||||
public static final int STRUCT_SIZE = 2;
|
||||
|
||||
/** The option type. */
|
||||
public final byte type;
|
||||
/** The length of the option in 8-byte units. Actually an unsigned 8-bit integer */
|
||||
public final int length;
|
||||
|
||||
/** Constructs a new NdOption. */
|
||||
NdOption(byte type, int length) {
|
||||
this.type = type;
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a neighbour discovery option.
|
||||
*
|
||||
* Parses (and consumes) the option if it is of a known type. If the option is of an unknown
|
||||
* type, advances the buffer (so the caller can continue parsing if desired) and returns
|
||||
* {@link #UNKNOWN}. If the option claims a length of 0, returns null because parsing cannot
|
||||
* continue.
|
||||
*
|
||||
* No checks are performed on the length other than ensuring it is not 0, so if a caller wants
|
||||
* to deal with options that might overflow the structure that contains them, it must explicitly
|
||||
* set the buffer's limit to the position at which that structure ends.
|
||||
*
|
||||
* @param buf the buffer to parse.
|
||||
* @return a subclass of {@link NdOption}, or {@code null} for an unknown or malformed option.
|
||||
*/
|
||||
public static NdOption parse(ByteBuffer buf) {
|
||||
if (buf == null || buf.remaining() < STRUCT_SIZE) return null;
|
||||
|
||||
// Peek the type without advancing the buffer.
|
||||
byte type = buf.get(buf.position());
|
||||
int length = Byte.toUnsignedInt(buf.get(buf.position() + 1));
|
||||
if (length == 0) return null;
|
||||
|
||||
switch (type) {
|
||||
case StructNdOptPref64.TYPE:
|
||||
return StructNdOptPref64.parse(buf);
|
||||
|
||||
default:
|
||||
int newPosition = Math.min(buf.limit(), buf.position() + length * 8);
|
||||
buf.position(newPosition);
|
||||
return UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
void writeToByteBuffer(ByteBuffer buf) {
|
||||
buf.put(type);
|
||||
buf.put((byte) length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("NdOption(%d, %d)", Byte.toUnsignedInt(type), length);
|
||||
}
|
||||
|
||||
public static final NdOption UNKNOWN = new NdOption((byte) 0, 0);
|
||||
}
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
* 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.netlink;
|
||||
|
||||
import static android.system.OsConstants.AF_INET6;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.BufferUnderflowException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
/**
|
||||
* A NetlinkMessage subclass for RTM_NEWNDUSEROPT messages.
|
||||
*/
|
||||
public class NduseroptMessage extends NetlinkMessage {
|
||||
public static final int STRUCT_SIZE = 16;
|
||||
|
||||
static final int NDUSEROPT_SRCADDR = 1;
|
||||
|
||||
/** The address family. Presumably always AF_INET6. */
|
||||
public final byte family;
|
||||
/**
|
||||
* The total length in bytes of the options that follow this structure.
|
||||
* Actually a 16-bit unsigned integer.
|
||||
*/
|
||||
public final int opts_len;
|
||||
/** The interface index on which the options were received. */
|
||||
public final int ifindex;
|
||||
/** The ICMP type of the packet that contained the options. */
|
||||
public final byte icmp_type;
|
||||
/** The ICMP code of the packet that contained the options. */
|
||||
public final byte icmp_code;
|
||||
|
||||
/**
|
||||
* ND option that was in this message.
|
||||
* Even though the length field is called "opts_len", the kernel only ever sends one option per
|
||||
* message. It is unlikely that this will ever change as it would break existing userspace code.
|
||||
* But if it does, we can simply update this code, since userspace is typically newer than the
|
||||
* kernel.
|
||||
*/
|
||||
public final NdOption option;
|
||||
|
||||
/** The IP address that sent the packet containing the option. */
|
||||
public final InetAddress srcaddr;
|
||||
|
||||
NduseroptMessage(@NonNull StructNlMsgHdr header, @NonNull ByteBuffer buf)
|
||||
throws UnknownHostException {
|
||||
super(header);
|
||||
|
||||
// The structure itself.
|
||||
buf.order(ByteOrder.nativeOrder()); // Restored in the finally clause inside parse().
|
||||
final int start = buf.position();
|
||||
family = buf.get();
|
||||
buf.get(); // Skip 1 byte of padding.
|
||||
opts_len = Short.toUnsignedInt(buf.getShort());
|
||||
ifindex = buf.getInt();
|
||||
icmp_type = buf.get();
|
||||
icmp_code = buf.get();
|
||||
buf.position(buf.position() + 6); // Skip 6 bytes of padding.
|
||||
|
||||
// The ND option.
|
||||
// Ensure we don't read past opts_len even if the option length is invalid.
|
||||
// Note that this check is not really necessary since if the option length is not valid,
|
||||
// this struct won't be very useful to the caller.
|
||||
buf.order(ByteOrder.BIG_ENDIAN);
|
||||
int oldLimit = buf.limit();
|
||||
buf.limit(start + STRUCT_SIZE + opts_len);
|
||||
try {
|
||||
option = NdOption.parse(buf);
|
||||
} finally {
|
||||
buf.limit(oldLimit);
|
||||
}
|
||||
|
||||
// The source address.
|
||||
int newPosition = start + STRUCT_SIZE + opts_len;
|
||||
if (newPosition >= buf.limit()) {
|
||||
throw new IllegalArgumentException("ND options extend past end of buffer");
|
||||
}
|
||||
buf.position(newPosition);
|
||||
|
||||
StructNlAttr nla = StructNlAttr.parse(buf);
|
||||
if (nla == null || nla.nla_type != NDUSEROPT_SRCADDR || nla.nla_value == null) {
|
||||
throw new IllegalArgumentException("Invalid source address in ND useropt");
|
||||
}
|
||||
if (family == AF_INET6) {
|
||||
// InetAddress.getByAddress only looks at the ifindex if the address type needs one.
|
||||
srcaddr = Inet6Address.getByAddress(null /* hostname */, nla.nla_value, ifindex);
|
||||
} else {
|
||||
srcaddr = InetAddress.getByAddress(nla.nla_value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a StructNduseroptmsg from a {@link ByteBuffer}.
|
||||
*
|
||||
* @param header the netlink message header.
|
||||
* @param buf The buffer from which to parse the option. The buffer's byte order must be
|
||||
* {@link java.nio.ByteOrder#BIG_ENDIAN}.
|
||||
* @return the parsed option, or {@code null} if the option could not be parsed successfully
|
||||
* (for example, if it was truncated, or if the prefix length code was wrong).
|
||||
*/
|
||||
public static NduseroptMessage parse(@NonNull StructNlMsgHdr header, @NonNull ByteBuffer buf) {
|
||||
if (buf == null || buf.remaining() < STRUCT_SIZE) return null;
|
||||
ByteOrder oldOrder = buf.order();
|
||||
try {
|
||||
return new NduseroptMessage(header, buf);
|
||||
} catch (IllegalArgumentException | UnknownHostException | BufferUnderflowException e) {
|
||||
// Not great, but better than throwing an exception that might crash the caller.
|
||||
// Convention in this package is that null indicates that the option was truncated, so
|
||||
// callers must already handle it.
|
||||
return null;
|
||||
} finally {
|
||||
buf.order(oldOrder);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Nduseroptmsg(%d, %d, %d, %d, %d, %s)",
|
||||
family, opts_len, ifindex, Byte.toUnsignedInt(icmp_type),
|
||||
Byte.toUnsignedInt(icmp_code), srcaddr.getHostAddress());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,260 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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.netlink;
|
||||
|
||||
import android.system.OsConstants;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Various constants and static helper methods for netlink communications.
|
||||
*
|
||||
* Values taken from:
|
||||
*
|
||||
* include/uapi/linux/netfilter/nfnetlink.h
|
||||
* include/uapi/linux/netfilter/nfnetlink_conntrack.h
|
||||
* include/uapi/linux/netlink.h
|
||||
* include/uapi/linux/rtnetlink.h
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class NetlinkConstants {
|
||||
private NetlinkConstants() {}
|
||||
|
||||
public static final int NLA_ALIGNTO = 4;
|
||||
/**
|
||||
* Flag for dumping struct tcp_info.
|
||||
* Corresponding to enum definition in external/strace/linux/inet_diag.h.
|
||||
*/
|
||||
public static final int INET_DIAG_MEMINFO = 1;
|
||||
|
||||
public static final int SOCKDIAG_MSG_HEADER_SIZE =
|
||||
StructNlMsgHdr.STRUCT_SIZE + StructInetDiagMsg.STRUCT_SIZE;
|
||||
|
||||
/**
|
||||
* Get the aligned length based on a Short type number.
|
||||
*/
|
||||
public static final int alignedLengthOf(short length) {
|
||||
final int intLength = (int) length & 0xffff;
|
||||
return alignedLengthOf(intLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the aligned length based on a Integer type number.
|
||||
*/
|
||||
public static final int alignedLengthOf(int length) {
|
||||
if (length <= 0) return 0;
|
||||
return (((length + NLA_ALIGNTO - 1) / NLA_ALIGNTO) * NLA_ALIGNTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a address family type to a string.
|
||||
*/
|
||||
public static String stringForAddressFamily(int family) {
|
||||
if (family == OsConstants.AF_INET) return "AF_INET";
|
||||
if (family == OsConstants.AF_INET6) return "AF_INET6";
|
||||
if (family == OsConstants.AF_NETLINK) return "AF_NETLINK";
|
||||
return String.valueOf(family);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a protocol type to a string.
|
||||
*/
|
||||
public static String stringForProtocol(int protocol) {
|
||||
if (protocol == OsConstants.IPPROTO_TCP) return "IPPROTO_TCP";
|
||||
if (protocol == OsConstants.IPPROTO_UDP) return "IPPROTO_UDP";
|
||||
return String.valueOf(protocol);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a byte array to a hexadecimal string.
|
||||
*/
|
||||
public static String hexify(byte[] bytes) {
|
||||
if (bytes == null) return "(null)";
|
||||
return toHexString(bytes, 0, bytes.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a {@link ByteBuffer} to a hexadecimal string.
|
||||
*/
|
||||
public static String hexify(ByteBuffer buffer) {
|
||||
if (buffer == null) return "(null)";
|
||||
return toHexString(
|
||||
buffer.array(), buffer.position(), buffer.remaining());
|
||||
}
|
||||
|
||||
// Known values for struct nlmsghdr nlm_type.
|
||||
public static final short NLMSG_NOOP = 1; // Nothing
|
||||
public static final short NLMSG_ERROR = 2; // Error
|
||||
public static final short NLMSG_DONE = 3; // End of a dump
|
||||
public static final short NLMSG_OVERRUN = 4; // Data lost
|
||||
public static final short NLMSG_MAX_RESERVED = 15; // Max reserved value
|
||||
|
||||
public static final short RTM_NEWLINK = 16;
|
||||
public static final short RTM_DELLINK = 17;
|
||||
public static final short RTM_GETLINK = 18;
|
||||
public static final short RTM_SETLINK = 19;
|
||||
public static final short RTM_NEWADDR = 20;
|
||||
public static final short RTM_DELADDR = 21;
|
||||
public static final short RTM_GETADDR = 22;
|
||||
public static final short RTM_NEWROUTE = 24;
|
||||
public static final short RTM_DELROUTE = 25;
|
||||
public static final short RTM_GETROUTE = 26;
|
||||
public static final short RTM_NEWNEIGH = 28;
|
||||
public static final short RTM_DELNEIGH = 29;
|
||||
public static final short RTM_GETNEIGH = 30;
|
||||
public static final short RTM_NEWRULE = 32;
|
||||
public static final short RTM_DELRULE = 33;
|
||||
public static final short RTM_GETRULE = 34;
|
||||
public static final short RTM_NEWNDUSEROPT = 68;
|
||||
|
||||
// Netfilter netlink message types are presented by two bytes: high byte subsystem and
|
||||
// low byte operation. See the macro NFNL_SUBSYS_ID and NFNL_MSG_TYPE in
|
||||
// include/uapi/linux/netfilter/nfnetlink.h
|
||||
public static final short NFNL_SUBSYS_CTNETLINK = 1;
|
||||
|
||||
public static final short IPCTNL_MSG_CT_NEW = 0;
|
||||
public static final short IPCTNL_MSG_CT_GET = 1;
|
||||
public static final short IPCTNL_MSG_CT_DELETE = 2;
|
||||
public static final short IPCTNL_MSG_CT_GET_CTRZERO = 3;
|
||||
public static final short IPCTNL_MSG_CT_GET_STATS_CPU = 4;
|
||||
public static final short IPCTNL_MSG_CT_GET_STATS = 5;
|
||||
public static final short IPCTNL_MSG_CT_GET_DYING = 6;
|
||||
public static final short IPCTNL_MSG_CT_GET_UNCONFIRMED = 7;
|
||||
|
||||
/* see include/uapi/linux/sock_diag.h */
|
||||
public static final short SOCK_DIAG_BY_FAMILY = 20;
|
||||
|
||||
// Netlink groups.
|
||||
public static final int RTNLGRP_ND_USEROPT = 20;
|
||||
public static final int RTMGRP_ND_USEROPT = 1 << (RTNLGRP_ND_USEROPT - 1);
|
||||
|
||||
/**
|
||||
* Convert a netlink message type to a string for control message.
|
||||
*/
|
||||
@NonNull
|
||||
private static String stringForCtlMsgType(short nlmType) {
|
||||
switch (nlmType) {
|
||||
case NLMSG_NOOP: return "NLMSG_NOOP";
|
||||
case NLMSG_ERROR: return "NLMSG_ERROR";
|
||||
case NLMSG_DONE: return "NLMSG_DONE";
|
||||
case NLMSG_OVERRUN: return "NLMSG_OVERRUN";
|
||||
default: return "unknown control message type: " + String.valueOf(nlmType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a netlink message type to a string for NETLINK_ROUTE.
|
||||
*/
|
||||
@NonNull
|
||||
private static String stringForRtMsgType(short nlmType) {
|
||||
switch (nlmType) {
|
||||
case RTM_NEWLINK: return "RTM_NEWLINK";
|
||||
case RTM_DELLINK: return "RTM_DELLINK";
|
||||
case RTM_GETLINK: return "RTM_GETLINK";
|
||||
case RTM_SETLINK: return "RTM_SETLINK";
|
||||
case RTM_NEWADDR: return "RTM_NEWADDR";
|
||||
case RTM_DELADDR: return "RTM_DELADDR";
|
||||
case RTM_GETADDR: return "RTM_GETADDR";
|
||||
case RTM_NEWROUTE: return "RTM_NEWROUTE";
|
||||
case RTM_DELROUTE: return "RTM_DELROUTE";
|
||||
case RTM_GETROUTE: return "RTM_GETROUTE";
|
||||
case RTM_NEWNEIGH: return "RTM_NEWNEIGH";
|
||||
case RTM_DELNEIGH: return "RTM_DELNEIGH";
|
||||
case RTM_GETNEIGH: return "RTM_GETNEIGH";
|
||||
case RTM_NEWRULE: return "RTM_NEWRULE";
|
||||
case RTM_DELRULE: return "RTM_DELRULE";
|
||||
case RTM_GETRULE: return "RTM_GETRULE";
|
||||
case RTM_NEWNDUSEROPT: return "RTM_NEWNDUSEROPT";
|
||||
default: return "unknown RTM type: " + String.valueOf(nlmType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a netlink message type to a string for NETLINK_INET_DIAG.
|
||||
*/
|
||||
@NonNull
|
||||
private static String stringForInetDiagMsgType(short nlmType) {
|
||||
switch (nlmType) {
|
||||
case SOCK_DIAG_BY_FAMILY: return "SOCK_DIAG_BY_FAMILY";
|
||||
default: return "unknown SOCK_DIAG type: " + String.valueOf(nlmType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a netlink message type to a string for NETLINK_NETFILTER.
|
||||
*/
|
||||
@NonNull
|
||||
private static String stringForNfMsgType(short nlmType) {
|
||||
final byte subsysId = (byte) (nlmType >> 8);
|
||||
final byte msgType = (byte) nlmType;
|
||||
switch (subsysId) {
|
||||
case NFNL_SUBSYS_CTNETLINK:
|
||||
switch (msgType) {
|
||||
case IPCTNL_MSG_CT_NEW: return "IPCTNL_MSG_CT_NEW";
|
||||
case IPCTNL_MSG_CT_GET: return "IPCTNL_MSG_CT_GET";
|
||||
case IPCTNL_MSG_CT_DELETE: return "IPCTNL_MSG_CT_DELETE";
|
||||
case IPCTNL_MSG_CT_GET_CTRZERO: return "IPCTNL_MSG_CT_GET_CTRZERO";
|
||||
case IPCTNL_MSG_CT_GET_STATS_CPU: return "IPCTNL_MSG_CT_GET_STATS_CPU";
|
||||
case IPCTNL_MSG_CT_GET_STATS: return "IPCTNL_MSG_CT_GET_STATS";
|
||||
case IPCTNL_MSG_CT_GET_DYING: return "IPCTNL_MSG_CT_GET_DYING";
|
||||
case IPCTNL_MSG_CT_GET_UNCONFIRMED: return "IPCTNL_MSG_CT_GET_UNCONFIRMED";
|
||||
}
|
||||
break;
|
||||
}
|
||||
return "unknown NETFILTER type: " + String.valueOf(nlmType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a netlink message type to a string by netlink family.
|
||||
*/
|
||||
@NonNull
|
||||
public static String stringForNlMsgType(short nlmType, int nlFamily) {
|
||||
// Reserved control messages. The netlink family is ignored.
|
||||
// See NLMSG_MIN_TYPE in include/uapi/linux/netlink.h.
|
||||
if (nlmType <= NLMSG_MAX_RESERVED) return stringForCtlMsgType(nlmType);
|
||||
|
||||
// Netlink family messages. The netlink family is required. Note that the reason for using
|
||||
// if-statement is that switch-case can't be used because the OsConstants.NETLINK_* are
|
||||
// not constant.
|
||||
if (nlFamily == OsConstants.NETLINK_ROUTE) return stringForRtMsgType(nlmType);
|
||||
if (nlFamily == OsConstants.NETLINK_INET_DIAG) return stringForInetDiagMsgType(nlmType);
|
||||
if (nlFamily == OsConstants.NETLINK_NETFILTER) return stringForNfMsgType(nlmType);
|
||||
|
||||
return "unknown type: " + String.valueOf(nlmType);
|
||||
}
|
||||
|
||||
private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
'A', 'B', 'C', 'D', 'E', 'F' };
|
||||
/**
|
||||
* Convert a byte array to a hexadecimal string.
|
||||
*/
|
||||
public static String toHexString(byte[] array, int offset, int length) {
|
||||
char[] buf = new char[length * 2];
|
||||
|
||||
int bufIndex = 0;
|
||||
for (int i = offset; i < offset + length; i++) {
|
||||
byte b = array[i];
|
||||
buf[bufIndex++] = HEX_DIGITS[(b >>> 4) & 0x0F];
|
||||
buf[bufIndex++] = HEX_DIGITS[b & 0x0F];
|
||||
}
|
||||
|
||||
return new String(buf);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* 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.netlink;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* A NetlinkMessage subclass for netlink error messages.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class NetlinkErrorMessage extends NetlinkMessage {
|
||||
|
||||
/**
|
||||
* Parse a netlink error message from a {@link ByteBuffer}.
|
||||
*
|
||||
* @param byteBuffer The buffer from which to parse the netlink error message.
|
||||
* @return the parsed netlink error message, or {@code null} if the netlink error message
|
||||
* could not be parsed successfully (for example, if it was truncated).
|
||||
*/
|
||||
public static NetlinkErrorMessage parse(StructNlMsgHdr header, ByteBuffer byteBuffer) {
|
||||
final NetlinkErrorMessage errorMsg = new NetlinkErrorMessage(header);
|
||||
|
||||
errorMsg.mNlMsgErr = StructNlMsgErr.parse(byteBuffer);
|
||||
if (errorMsg.mNlMsgErr == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return errorMsg;
|
||||
}
|
||||
|
||||
private StructNlMsgErr mNlMsgErr;
|
||||
|
||||
NetlinkErrorMessage(StructNlMsgHdr header) {
|
||||
super(header);
|
||||
mNlMsgErr = null;
|
||||
}
|
||||
|
||||
public StructNlMsgErr getNlMsgError() {
|
||||
return mNlMsgErr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NetlinkErrorMessage{ "
|
||||
+ "nlmsghdr{" + (mHeader == null ? "" : mHeader.toString()) + "}, "
|
||||
+ "nlmsgerr{" + (mNlMsgErr == null ? "" : mNlMsgErr.toString()) + "} "
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* 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.netlink;
|
||||
|
||||
import android.system.OsConstants;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* NetlinkMessage base class for other, more specific netlink message types.
|
||||
*
|
||||
* Classes that extend NetlinkMessage should:
|
||||
* - implement a public static parse(StructNlMsgHdr, ByteBuffer) method
|
||||
* - returning either null (parse errors) or a new object of the subclass
|
||||
* type (cast-able to NetlinkMessage)
|
||||
*
|
||||
* NetlinkMessage.parse() should be updated to know which nlmsg_type values
|
||||
* correspond with which message subclasses.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class NetlinkMessage {
|
||||
private static final String TAG = "NetlinkMessage";
|
||||
|
||||
/**
|
||||
* Parsing netlink messages for reserved control message or specific netlink message. The
|
||||
* netlink family is required for parsing specific netlink message. See man-pages/netlink.
|
||||
*/
|
||||
@Nullable
|
||||
public static NetlinkMessage parse(@NonNull ByteBuffer byteBuffer, int nlFamily) {
|
||||
final int startPosition = (byteBuffer != null) ? byteBuffer.position() : -1;
|
||||
final StructNlMsgHdr nlmsghdr = StructNlMsgHdr.parse(byteBuffer);
|
||||
if (nlmsghdr == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int payloadLength = NetlinkConstants.alignedLengthOf(nlmsghdr.nlmsg_len);
|
||||
payloadLength -= StructNlMsgHdr.STRUCT_SIZE;
|
||||
if (payloadLength < 0 || payloadLength > byteBuffer.remaining()) {
|
||||
// Malformed message or runt buffer. Pretend the buffer was consumed.
|
||||
byteBuffer.position(byteBuffer.limit());
|
||||
return null;
|
||||
}
|
||||
|
||||
// Reserved control messages. The netlink family is ignored.
|
||||
// See NLMSG_MIN_TYPE in include/uapi/linux/netlink.h.
|
||||
if (nlmsghdr.nlmsg_type <= NetlinkConstants.NLMSG_MAX_RESERVED) {
|
||||
return parseCtlMessage(nlmsghdr, byteBuffer, payloadLength);
|
||||
}
|
||||
|
||||
// Netlink family messages. The netlink family is required. Note that the reason for using
|
||||
// if-statement is that switch-case can't be used because the OsConstants.NETLINK_* are
|
||||
// not constant.
|
||||
if (nlFamily == OsConstants.NETLINK_ROUTE) {
|
||||
return parseRtMessage(nlmsghdr, byteBuffer);
|
||||
} else if (nlFamily == OsConstants.NETLINK_INET_DIAG) {
|
||||
return parseInetDiagMessage(nlmsghdr, byteBuffer);
|
||||
} else if (nlFamily == OsConstants.NETLINK_NETFILTER) {
|
||||
return parseNfMessage(nlmsghdr, byteBuffer);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected StructNlMsgHdr mHeader;
|
||||
|
||||
public NetlinkMessage(StructNlMsgHdr nlmsghdr) {
|
||||
mHeader = nlmsghdr;
|
||||
}
|
||||
|
||||
public StructNlMsgHdr getHeader() {
|
||||
return mHeader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
// The netlink family is not provided to StructNlMsgHdr#toString because NetlinkMessage
|
||||
// doesn't store the information. So the netlink message type can't be transformed into
|
||||
// a string by StructNlMsgHdr#toString and just keep as an integer. The specific message
|
||||
// which inherits NetlinkMessage could override NetlinkMessage#toString and provide the
|
||||
// specific netlink family to StructNlMsgHdr#toString.
|
||||
return "NetlinkMessage{" + (mHeader == null ? "" : mHeader.toString()) + "}";
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static NetlinkMessage parseCtlMessage(@NonNull StructNlMsgHdr nlmsghdr,
|
||||
@NonNull ByteBuffer byteBuffer, int payloadLength) {
|
||||
switch (nlmsghdr.nlmsg_type) {
|
||||
case NetlinkConstants.NLMSG_ERROR:
|
||||
return (NetlinkMessage) NetlinkErrorMessage.parse(nlmsghdr, byteBuffer);
|
||||
default: {
|
||||
// Other netlink control messages. Just parse the header for now,
|
||||
// pretending the whole message was consumed.
|
||||
byteBuffer.position(byteBuffer.position() + payloadLength);
|
||||
return new NetlinkMessage(nlmsghdr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static NetlinkMessage parseRtMessage(@NonNull StructNlMsgHdr nlmsghdr,
|
||||
@NonNull ByteBuffer byteBuffer) {
|
||||
switch (nlmsghdr.nlmsg_type) {
|
||||
case NetlinkConstants.RTM_NEWNEIGH:
|
||||
case NetlinkConstants.RTM_DELNEIGH:
|
||||
case NetlinkConstants.RTM_GETNEIGH:
|
||||
return (NetlinkMessage) RtNetlinkNeighborMessage.parse(nlmsghdr, byteBuffer);
|
||||
case NetlinkConstants.RTM_NEWNDUSEROPT:
|
||||
return (NetlinkMessage) NduseroptMessage.parse(nlmsghdr, byteBuffer);
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static NetlinkMessage parseInetDiagMessage(@NonNull StructNlMsgHdr nlmsghdr,
|
||||
@NonNull ByteBuffer byteBuffer) {
|
||||
switch (nlmsghdr.nlmsg_type) {
|
||||
case NetlinkConstants.SOCK_DIAG_BY_FAMILY:
|
||||
return (NetlinkMessage) InetDiagMessage.parse(nlmsghdr, byteBuffer);
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static NetlinkMessage parseNfMessage(@NonNull StructNlMsgHdr nlmsghdr,
|
||||
@NonNull ByteBuffer byteBuffer) {
|
||||
switch (nlmsghdr.nlmsg_type) {
|
||||
case NetlinkConstants.NFNL_SUBSYS_CTNETLINK << 8
|
||||
| NetlinkConstants.IPCTNL_MSG_CT_NEW:
|
||||
case NetlinkConstants.NFNL_SUBSYS_CTNETLINK << 8
|
||||
| NetlinkConstants.IPCTNL_MSG_CT_DELETE:
|
||||
return (NetlinkMessage) ConntrackMessage.parse(nlmsghdr, byteBuffer);
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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.netlink;
|
||||
|
||||
import static android.net.util.SocketUtils.makeNetlinkSocketAddress;
|
||||
import static android.system.OsConstants.AF_NETLINK;
|
||||
import static android.system.OsConstants.EIO;
|
||||
import static android.system.OsConstants.EPROTO;
|
||||
import static android.system.OsConstants.ETIMEDOUT;
|
||||
import static android.system.OsConstants.SOCK_DGRAM;
|
||||
import static android.system.OsConstants.SOL_SOCKET;
|
||||
import static android.system.OsConstants.SO_RCVBUF;
|
||||
import static android.system.OsConstants.SO_RCVTIMEO;
|
||||
import static android.system.OsConstants.SO_SNDTIMEO;
|
||||
|
||||
import android.net.util.SocketUtils;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
import android.system.StructTimeval;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.net.SocketException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
|
||||
/**
|
||||
* NetlinkSocket
|
||||
*
|
||||
* A small static class to assist with AF_NETLINK socket operations.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class NetlinkSocket {
|
||||
private static final String TAG = "NetlinkSocket";
|
||||
private static final long IO_TIMEOUT_MS = 300L;
|
||||
|
||||
public static final int DEFAULT_RECV_BUFSIZE = 8 * 1024;
|
||||
public static final int SOCKET_RECV_BUFSIZE = 64 * 1024;
|
||||
|
||||
/**
|
||||
* Send one netlink message to kernel via netlink socket.
|
||||
*
|
||||
* @param nlProto netlink protocol type.
|
||||
* @param msg the raw bytes of netlink message to be sent.
|
||||
*/
|
||||
public static void sendOneShotKernelMessage(int nlProto, byte[] msg) throws ErrnoException {
|
||||
final String errPrefix = "Error in NetlinkSocket.sendOneShotKernelMessage";
|
||||
final FileDescriptor fd = forProto(nlProto);
|
||||
|
||||
try {
|
||||
connectToKernel(fd);
|
||||
sendMessage(fd, msg, 0, msg.length, IO_TIMEOUT_MS);
|
||||
final ByteBuffer bytes = recvMessage(fd, DEFAULT_RECV_BUFSIZE, IO_TIMEOUT_MS);
|
||||
// recvMessage() guaranteed to not return null if it did not throw.
|
||||
final NetlinkMessage response = NetlinkMessage.parse(bytes, nlProto);
|
||||
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, EPROTO);
|
||||
}
|
||||
} catch (InterruptedIOException e) {
|
||||
Log.e(TAG, errPrefix, e);
|
||||
throw new ErrnoException(errPrefix, ETIMEDOUT, e);
|
||||
} catch (SocketException e) {
|
||||
Log.e(TAG, errPrefix, e);
|
||||
throw new ErrnoException(errPrefix, EIO, e);
|
||||
} finally {
|
||||
try {
|
||||
SocketUtils.closeSocket(fd);
|
||||
} catch (IOException e) {
|
||||
// Nothing we can do here
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create netlink socket with the given netlink protocol type.
|
||||
*
|
||||
* @return fd the fileDescriptor of the socket.
|
||||
* Throw ErrnoException if the exception is thrown.
|
||||
*/
|
||||
public static FileDescriptor forProto(int nlProto) throws ErrnoException {
|
||||
final FileDescriptor fd = Os.socket(AF_NETLINK, SOCK_DGRAM, nlProto);
|
||||
Os.setsockoptInt(fd, SOL_SOCKET, SO_RCVBUF, SOCKET_RECV_BUFSIZE);
|
||||
return fd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to kernel via netlink socket.
|
||||
*
|
||||
* Throw ErrnoException, SocketException if the exception is thrown.
|
||||
*/
|
||||
public static void connectToKernel(FileDescriptor fd) throws ErrnoException, SocketException {
|
||||
Os.connect(fd, makeNetlinkSocketAddress(0, 0));
|
||||
}
|
||||
|
||||
private static void checkTimeout(long timeoutMs) {
|
||||
if (timeoutMs < 0) {
|
||||
throw new IllegalArgumentException("Negative timeouts not permitted");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait up to |timeoutMs| (or until underlying socket error) for a
|
||||
* netlink message of at most |bufsize| size.
|
||||
*
|
||||
* Multi-threaded calls with different timeouts will cause unexpected results.
|
||||
*/
|
||||
public static ByteBuffer recvMessage(FileDescriptor fd, int bufsize, long timeoutMs)
|
||||
throws ErrnoException, IllegalArgumentException, InterruptedIOException {
|
||||
checkTimeout(timeoutMs);
|
||||
|
||||
Os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, StructTimeval.fromMillis(timeoutMs));
|
||||
|
||||
ByteBuffer byteBuffer = ByteBuffer.allocate(bufsize);
|
||||
int length = Os.read(fd, byteBuffer);
|
||||
if (length == bufsize) {
|
||||
Log.w(TAG, "maximum read");
|
||||
}
|
||||
byteBuffer.position(0);
|
||||
byteBuffer.limit(length);
|
||||
byteBuffer.order(ByteOrder.nativeOrder());
|
||||
return byteBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message to a peer to which this socket has previously connected,
|
||||
* waiting at most |timeoutMs| milliseconds for the send to complete.
|
||||
*
|
||||
* Multi-threaded calls with different timeouts will cause unexpected results.
|
||||
*/
|
||||
public static int sendMessage(
|
||||
FileDescriptor fd, byte[] bytes, int offset, int count, long timeoutMs)
|
||||
throws ErrnoException, IllegalArgumentException, InterruptedIOException {
|
||||
checkTimeout(timeoutMs);
|
||||
Os.setsockoptTimeval(fd, SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(timeoutMs));
|
||||
return Os.write(fd, bytes, offset, count);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,237 @@
|
|||
/*
|
||||
* 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.netlink;
|
||||
|
||||
import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_ACK;
|
||||
import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_DUMP;
|
||||
import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REPLACE;
|
||||
import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REQUEST;
|
||||
|
||||
import android.system.OsConstants;
|
||||
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
/**
|
||||
* A NetlinkMessage subclass for rtnetlink neighbor messages.
|
||||
*
|
||||
* see also: <linux_src>/include/uapi/linux/neighbour.h
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class RtNetlinkNeighborMessage extends NetlinkMessage {
|
||||
public static final short NDA_UNSPEC = 0;
|
||||
public static final short NDA_DST = 1;
|
||||
public static final short NDA_LLADDR = 2;
|
||||
public static final short NDA_CACHEINFO = 3;
|
||||
public static final short NDA_PROBES = 4;
|
||||
public static final short NDA_VLAN = 5;
|
||||
public static final short NDA_PORT = 6;
|
||||
public static final short NDA_VNI = 7;
|
||||
public static final short NDA_IFINDEX = 8;
|
||||
public static final short NDA_MASTER = 9;
|
||||
|
||||
/**
|
||||
* Parse routing socket netlink neighbor message from ByteBuffer.
|
||||
*
|
||||
* @param header netlink message header.
|
||||
* @param byteBuffer the ByteBuffer instance that wraps the raw netlink message bytes.
|
||||
*/
|
||||
public static RtNetlinkNeighborMessage parse(StructNlMsgHdr header, ByteBuffer byteBuffer) {
|
||||
final RtNetlinkNeighborMessage neighMsg = new RtNetlinkNeighborMessage(header);
|
||||
|
||||
neighMsg.mNdmsg = StructNdMsg.parse(byteBuffer);
|
||||
if (neighMsg.mNdmsg == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Some of these are message-type dependent, and not always present.
|
||||
final int baseOffset = byteBuffer.position();
|
||||
StructNlAttr nlAttr = StructNlAttr.findNextAttrOfType(NDA_DST, byteBuffer);
|
||||
if (nlAttr != null) {
|
||||
neighMsg.mDestination = nlAttr.getValueAsInetAddress();
|
||||
}
|
||||
|
||||
byteBuffer.position(baseOffset);
|
||||
nlAttr = StructNlAttr.findNextAttrOfType(NDA_LLADDR, byteBuffer);
|
||||
if (nlAttr != null) {
|
||||
neighMsg.mLinkLayerAddr = nlAttr.nla_value;
|
||||
}
|
||||
|
||||
byteBuffer.position(baseOffset);
|
||||
nlAttr = StructNlAttr.findNextAttrOfType(NDA_PROBES, byteBuffer);
|
||||
if (nlAttr != null) {
|
||||
neighMsg.mNumProbes = nlAttr.getValueAsInt(0);
|
||||
}
|
||||
|
||||
byteBuffer.position(baseOffset);
|
||||
nlAttr = StructNlAttr.findNextAttrOfType(NDA_CACHEINFO, byteBuffer);
|
||||
if (nlAttr != null) {
|
||||
neighMsg.mCacheInfo = StructNdaCacheInfo.parse(nlAttr.getValueAsByteBuffer());
|
||||
}
|
||||
|
||||
final int kMinConsumed = StructNlMsgHdr.STRUCT_SIZE + StructNdMsg.STRUCT_SIZE;
|
||||
final int kAdditionalSpace = NetlinkConstants.alignedLengthOf(
|
||||
neighMsg.mHeader.nlmsg_len - kMinConsumed);
|
||||
if (byteBuffer.remaining() < kAdditionalSpace) {
|
||||
byteBuffer.position(byteBuffer.limit());
|
||||
} else {
|
||||
byteBuffer.position(baseOffset + kAdditionalSpace);
|
||||
}
|
||||
|
||||
return neighMsg;
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method to create an RTM_GETNEIGH request message.
|
||||
*/
|
||||
public static byte[] newGetNeighborsRequest(int seqNo) {
|
||||
final int length = StructNlMsgHdr.STRUCT_SIZE + StructNdMsg.STRUCT_SIZE;
|
||||
final byte[] bytes = new byte[length];
|
||||
final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
|
||||
byteBuffer.order(ByteOrder.nativeOrder());
|
||||
|
||||
final StructNlMsgHdr nlmsghdr = new StructNlMsgHdr();
|
||||
nlmsghdr.nlmsg_len = length;
|
||||
nlmsghdr.nlmsg_type = NetlinkConstants.RTM_GETNEIGH;
|
||||
nlmsghdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
|
||||
nlmsghdr.nlmsg_seq = seqNo;
|
||||
nlmsghdr.pack(byteBuffer);
|
||||
|
||||
final StructNdMsg ndmsg = new StructNdMsg();
|
||||
ndmsg.pack(byteBuffer);
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method to create an RTM_NEWNEIGH message, to modify
|
||||
* the kernel's state information for a specific neighbor.
|
||||
*/
|
||||
public static byte[] newNewNeighborMessage(
|
||||
int seqNo, InetAddress ip, short nudState, int ifIndex, byte[] llAddr) {
|
||||
final StructNlMsgHdr nlmsghdr = new StructNlMsgHdr();
|
||||
nlmsghdr.nlmsg_type = NetlinkConstants.RTM_NEWNEIGH;
|
||||
nlmsghdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_REPLACE;
|
||||
nlmsghdr.nlmsg_seq = seqNo;
|
||||
|
||||
final RtNetlinkNeighborMessage msg = new RtNetlinkNeighborMessage(nlmsghdr);
|
||||
msg.mNdmsg = new StructNdMsg();
|
||||
msg.mNdmsg.ndm_family =
|
||||
(byte) ((ip instanceof Inet6Address) ? OsConstants.AF_INET6 : OsConstants.AF_INET);
|
||||
msg.mNdmsg.ndm_ifindex = ifIndex;
|
||||
msg.mNdmsg.ndm_state = nudState;
|
||||
msg.mDestination = ip;
|
||||
msg.mLinkLayerAddr = llAddr; // might be null
|
||||
|
||||
final byte[] bytes = new byte[msg.getRequiredSpace()];
|
||||
nlmsghdr.nlmsg_len = bytes.length;
|
||||
final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
|
||||
byteBuffer.order(ByteOrder.nativeOrder());
|
||||
msg.pack(byteBuffer);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private StructNdMsg mNdmsg;
|
||||
private InetAddress mDestination;
|
||||
private byte[] mLinkLayerAddr;
|
||||
private int mNumProbes;
|
||||
private StructNdaCacheInfo mCacheInfo;
|
||||
|
||||
private RtNetlinkNeighborMessage(StructNlMsgHdr header) {
|
||||
super(header);
|
||||
mNdmsg = null;
|
||||
mDestination = null;
|
||||
mLinkLayerAddr = null;
|
||||
mNumProbes = 0;
|
||||
mCacheInfo = null;
|
||||
}
|
||||
|
||||
public StructNdMsg getNdHeader() {
|
||||
return mNdmsg;
|
||||
}
|
||||
|
||||
public InetAddress getDestination() {
|
||||
return mDestination;
|
||||
}
|
||||
|
||||
public byte[] getLinkLayerAddress() {
|
||||
return mLinkLayerAddr;
|
||||
}
|
||||
|
||||
public int getProbes() {
|
||||
return mNumProbes;
|
||||
}
|
||||
|
||||
public StructNdaCacheInfo getCacheInfo() {
|
||||
return mCacheInfo;
|
||||
}
|
||||
|
||||
private int getRequiredSpace() {
|
||||
int spaceRequired = StructNlMsgHdr.STRUCT_SIZE + StructNdMsg.STRUCT_SIZE;
|
||||
if (mDestination != null) {
|
||||
spaceRequired += NetlinkConstants.alignedLengthOf(
|
||||
StructNlAttr.NLA_HEADERLEN + mDestination.getAddress().length);
|
||||
}
|
||||
if (mLinkLayerAddr != null) {
|
||||
spaceRequired += NetlinkConstants.alignedLengthOf(
|
||||
StructNlAttr.NLA_HEADERLEN + mLinkLayerAddr.length);
|
||||
}
|
||||
// Currently we don't write messages with NDA_PROBES nor NDA_CACHEINFO
|
||||
// attributes appended. Fix later, if necessary.
|
||||
return spaceRequired;
|
||||
}
|
||||
|
||||
private static void packNlAttr(short nlType, byte[] nlValue, ByteBuffer byteBuffer) {
|
||||
final StructNlAttr nlAttr = new StructNlAttr();
|
||||
nlAttr.nla_type = nlType;
|
||||
nlAttr.nla_value = nlValue;
|
||||
nlAttr.nla_len = (short) (StructNlAttr.NLA_HEADERLEN + nlAttr.nla_value.length);
|
||||
nlAttr.pack(byteBuffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a neighbor discovery netlink message to {@link ByteBuffer}.
|
||||
*/
|
||||
public void pack(ByteBuffer byteBuffer) {
|
||||
getHeader().pack(byteBuffer);
|
||||
mNdmsg.pack(byteBuffer);
|
||||
|
||||
if (mDestination != null) {
|
||||
packNlAttr(NDA_DST, mDestination.getAddress(), byteBuffer);
|
||||
}
|
||||
if (mLinkLayerAddr != null) {
|
||||
packNlAttr(NDA_LLADDR, mLinkLayerAddr, byteBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final String ipLiteral = (mDestination == null) ? "" : mDestination.getHostAddress();
|
||||
return "RtNetlinkNeighborMessage{ "
|
||||
+ "nlmsghdr{"
|
||||
+ (mHeader == null ? "" : mHeader.toString(OsConstants.NETLINK_ROUTE)) + "}, "
|
||||
+ "ndmsg{" + (mNdmsg == null ? "" : mNdmsg.toString()) + "}, "
|
||||
+ "destination{" + ipLiteral + "} "
|
||||
+ "linklayeraddr{" + NetlinkConstants.hexify(mLinkLayerAddr) + "} "
|
||||
+ "probes{" + mNumProbes + "} "
|
||||
+ "cacheinfo{" + (mCacheInfo == null ? "" : mCacheInfo.toString()) + "} "
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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.netlink;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* struct inet_diag_msg
|
||||
*
|
||||
* see <linux_src>/include/uapi/linux/inet_diag.h
|
||||
*
|
||||
* struct inet_diag_msg {
|
||||
* __u8 idiag_family;
|
||||
* __u8 idiag_state;
|
||||
* __u8 idiag_timer;
|
||||
* __u8 idiag_retrans;
|
||||
* struct inet_diag_sockid id;
|
||||
* __u32 idiag_expires;
|
||||
* __u32 idiag_rqueue;
|
||||
* __u32 idiag_wqueue;
|
||||
* __u32 idiag_uid;
|
||||
* __u32 idiag_inode;
|
||||
* };
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class StructInetDiagMsg {
|
||||
public static final int STRUCT_SIZE = 4 + StructInetDiagSockId.STRUCT_SIZE + 20;
|
||||
private static final int IDIAG_UID_OFFSET = StructNlMsgHdr.STRUCT_SIZE + 4
|
||||
+ StructInetDiagSockId.STRUCT_SIZE + 12;
|
||||
public int idiag_uid;
|
||||
|
||||
/**
|
||||
* Parse inet diag netlink message from buffer.
|
||||
*/
|
||||
public static StructInetDiagMsg parse(ByteBuffer byteBuffer) {
|
||||
StructInetDiagMsg struct = new StructInetDiagMsg();
|
||||
struct.idiag_uid = byteBuffer.getInt(IDIAG_UID_OFFSET);
|
||||
return struct;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "StructInetDiagMsg{ "
|
||||
+ "idiag_uid{" + idiag_uid + "}, "
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* 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.netlink;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* struct inet_diag_req_v2
|
||||
*
|
||||
* see <linux_src>/include/uapi/linux/inet_diag.h
|
||||
*
|
||||
* struct inet_diag_req_v2 {
|
||||
* __u8 sdiag_family;
|
||||
* __u8 sdiag_protocol;
|
||||
* __u8 idiag_ext;
|
||||
* __u8 pad;
|
||||
* __u32 idiag_states;
|
||||
* struct inet_diag_sockid id;
|
||||
* };
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class StructInetDiagReqV2 {
|
||||
public static final int STRUCT_SIZE = 8 + StructInetDiagSockId.STRUCT_SIZE;
|
||||
|
||||
private final byte mSdiagFamily;
|
||||
private final byte mSdiagProtocol;
|
||||
private final byte mIdiagExt;
|
||||
private final byte mPad;
|
||||
private final StructInetDiagSockId mId;
|
||||
private final int mState;
|
||||
public static final int INET_DIAG_REQ_V2_ALL_STATES = (int) 0xffffffff;
|
||||
|
||||
public StructInetDiagReqV2(int protocol, InetSocketAddress local, InetSocketAddress remote,
|
||||
int family) {
|
||||
this(protocol, local, remote, family, 0 /* pad */, 0 /* extension */,
|
||||
INET_DIAG_REQ_V2_ALL_STATES);
|
||||
}
|
||||
|
||||
public StructInetDiagReqV2(int protocol, @Nullable InetSocketAddress local,
|
||||
@Nullable InetSocketAddress remote, int family, int pad, int extension, int state)
|
||||
throws NullPointerException {
|
||||
mSdiagFamily = (byte) family;
|
||||
mSdiagProtocol = (byte) protocol;
|
||||
// Request for all sockets if no specific socket is requested. Specify the local and remote
|
||||
// socket address information for target request socket.
|
||||
if ((local == null) != (remote == null)) {
|
||||
throw new NullPointerException("Local and remote must be both null or both non-null");
|
||||
}
|
||||
mId = ((local != null && remote != null) ? new StructInetDiagSockId(local, remote) : null);
|
||||
mPad = (byte) pad;
|
||||
mIdiagExt = (byte) extension;
|
||||
mState = state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the int diag request v2 message to ByteBuffer.
|
||||
*/
|
||||
public void pack(ByteBuffer byteBuffer) {
|
||||
// The ByteOrder must have already been set by the caller.
|
||||
byteBuffer.put((byte) mSdiagFamily);
|
||||
byteBuffer.put((byte) mSdiagProtocol);
|
||||
byteBuffer.put((byte) mIdiagExt);
|
||||
byteBuffer.put((byte) mPad);
|
||||
byteBuffer.putInt(mState);
|
||||
if (mId != null) mId.pack(byteBuffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final String familyStr = NetlinkConstants.stringForAddressFamily(mSdiagFamily);
|
||||
final String protocolStr = NetlinkConstants.stringForAddressFamily(mSdiagProtocol);
|
||||
|
||||
return "StructInetDiagReqV2{ "
|
||||
+ "sdiag_family{" + familyStr + "}, "
|
||||
+ "sdiag_protocol{" + protocolStr + "}, "
|
||||
+ "idiag_ext{" + mIdiagExt + ")}, "
|
||||
+ "pad{" + mPad + "}, "
|
||||
+ "idiag_states{" + Integer.toHexString(mState) + "}, "
|
||||
+ ((mId != null) ? mId.toString() : "inet_diag_sockid=null")
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright (C) 2018 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.netlink;
|
||||
|
||||
import static java.nio.ByteOrder.BIG_ENDIAN;
|
||||
|
||||
import java.net.Inet4Address;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
/**
|
||||
* struct inet_diag_req_v2
|
||||
*
|
||||
* see <linux_src>/include/uapi/linux/inet_diag.h
|
||||
*
|
||||
* struct inet_diag_sockid {
|
||||
* __be16 idiag_sport;
|
||||
* __be16 idiag_dport;
|
||||
* __be32 idiag_src[4];
|
||||
* __be32 idiag_dst[4];
|
||||
* __u32 idiag_if;
|
||||
* __u32 idiag_cookie[2];
|
||||
* #define INET_DIAG_NOCOOKIE (~0U)
|
||||
* };
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class StructInetDiagSockId {
|
||||
public static final int STRUCT_SIZE = 48;
|
||||
|
||||
private static final byte[] INET_DIAG_NOCOOKIE = new byte[]{
|
||||
(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
|
||||
(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff};
|
||||
private static final byte[] IPV4_PADDING = new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
private final InetSocketAddress mLocSocketAddress;
|
||||
private final InetSocketAddress mRemSocketAddress;
|
||||
|
||||
public StructInetDiagSockId(InetSocketAddress loc, InetSocketAddress rem) {
|
||||
mLocSocketAddress = loc;
|
||||
mRemSocketAddress = rem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write inet diag socket id message to ByteBuffer in big endian.
|
||||
*/
|
||||
public void pack(ByteBuffer byteBuffer) {
|
||||
byteBuffer.order(BIG_ENDIAN);
|
||||
byteBuffer.putShort((short) mLocSocketAddress.getPort());
|
||||
byteBuffer.putShort((short) mRemSocketAddress.getPort());
|
||||
byteBuffer.put(mLocSocketAddress.getAddress().getAddress());
|
||||
if (mLocSocketAddress.getAddress() instanceof Inet4Address) {
|
||||
byteBuffer.put(IPV4_PADDING);
|
||||
}
|
||||
byteBuffer.put(mRemSocketAddress.getAddress().getAddress());
|
||||
if (mRemSocketAddress.getAddress() instanceof Inet4Address) {
|
||||
byteBuffer.put(IPV4_PADDING);
|
||||
}
|
||||
byteBuffer.order(ByteOrder.nativeOrder());
|
||||
byteBuffer.putInt(0);
|
||||
byteBuffer.put(INET_DIAG_NOCOOKIE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "StructInetDiagSockId{ "
|
||||
+ "idiag_sport{" + mLocSocketAddress.getPort() + "}, "
|
||||
+ "idiag_dport{" + mRemSocketAddress.getPort() + "}, "
|
||||
+ "idiag_src{" + mLocSocketAddress.getAddress().getHostAddress() + "}, "
|
||||
+ "idiag_dst{" + mRemSocketAddress.getAddress().getHostAddress() + "}, "
|
||||
+ "idiag_if{" + 0 + "} "
|
||||
+ "idiag_cookie{INET_DIAG_NOCOOKIE}"
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
* 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.netlink;
|
||||
|
||||
import android.system.OsConstants;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* struct ndmsg
|
||||
*
|
||||
* see: <linux_src>/include/uapi/linux/neighbour.h
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class StructNdMsg {
|
||||
// Already aligned.
|
||||
public static final int STRUCT_SIZE = 12;
|
||||
|
||||
// Neighbor Cache Entry States
|
||||
public static final short NUD_NONE = 0x00;
|
||||
public static final short NUD_INCOMPLETE = 0x01;
|
||||
public static final short NUD_REACHABLE = 0x02;
|
||||
public static final short NUD_STALE = 0x04;
|
||||
public static final short NUD_DELAY = 0x08;
|
||||
public static final short NUD_PROBE = 0x10;
|
||||
public static final short NUD_FAILED = 0x20;
|
||||
public static final short NUD_NOARP = 0x40;
|
||||
public static final short NUD_PERMANENT = 0x80;
|
||||
|
||||
/**
|
||||
* Convert neighbor cache entry state integer to string.
|
||||
*/
|
||||
public static String stringForNudState(short nudState) {
|
||||
switch (nudState) {
|
||||
case NUD_NONE: return "NUD_NONE";
|
||||
case NUD_INCOMPLETE: return "NUD_INCOMPLETE";
|
||||
case NUD_REACHABLE: return "NUD_REACHABLE";
|
||||
case NUD_STALE: return "NUD_STALE";
|
||||
case NUD_DELAY: return "NUD_DELAY";
|
||||
case NUD_PROBE: return "NUD_PROBE";
|
||||
case NUD_FAILED: return "NUD_FAILED";
|
||||
case NUD_NOARP: return "NUD_NOARP";
|
||||
case NUD_PERMANENT: return "NUD_PERMANENT";
|
||||
default:
|
||||
return "unknown NUD state: " + String.valueOf(nudState);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a neighbor is connected or not.
|
||||
*/
|
||||
public static boolean isNudStateConnected(short nudState) {
|
||||
return ((nudState & (NUD_PERMANENT | NUD_NOARP | NUD_REACHABLE)) != 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a neighbor is in the valid NUD state or not.
|
||||
*/
|
||||
public static boolean isNudStateValid(short nudState) {
|
||||
return (isNudStateConnected(nudState)
|
||||
|| ((nudState & (NUD_PROBE | NUD_STALE | NUD_DELAY)) != 0));
|
||||
}
|
||||
|
||||
// Neighbor Cache Entry Flags
|
||||
public static byte NTF_USE = (byte) 0x01;
|
||||
public static byte NTF_SELF = (byte) 0x02;
|
||||
public static byte NTF_MASTER = (byte) 0x04;
|
||||
public static byte NTF_PROXY = (byte) 0x08;
|
||||
public static byte NTF_ROUTER = (byte) 0x80;
|
||||
|
||||
private static String stringForNudFlags(byte flags) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
if ((flags & NTF_USE) != 0) {
|
||||
sb.append("NTF_USE");
|
||||
}
|
||||
if ((flags & NTF_SELF) != 0) {
|
||||
if (sb.length() > 0) {
|
||||
sb.append("|");
|
||||
}
|
||||
sb.append("NTF_SELF");
|
||||
}
|
||||
if ((flags & NTF_MASTER) != 0) {
|
||||
if (sb.length() > 0) {
|
||||
sb.append("|");
|
||||
}
|
||||
sb.append("NTF_MASTER");
|
||||
}
|
||||
if ((flags & NTF_PROXY) != 0) {
|
||||
if (sb.length() > 0) {
|
||||
sb.append("|");
|
||||
}
|
||||
sb.append("NTF_PROXY");
|
||||
}
|
||||
if ((flags & NTF_ROUTER) != 0) {
|
||||
if (sb.length() > 0) {
|
||||
sb.append("|");
|
||||
}
|
||||
sb.append("NTF_ROUTER");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static boolean hasAvailableSpace(ByteBuffer byteBuffer) {
|
||||
return byteBuffer != null && byteBuffer.remaining() >= STRUCT_SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a neighbor discovery netlink message header from a {@link ByteBuffer}.
|
||||
*
|
||||
* @param byteBuffer The buffer from which to parse the nd netlink message header.
|
||||
* @return the parsed nd netlink message header, or {@code null} if the nd netlink message
|
||||
* header could not be parsed successfully (for example, if it was truncated).
|
||||
*/
|
||||
public static StructNdMsg parse(ByteBuffer byteBuffer) {
|
||||
if (!hasAvailableSpace(byteBuffer)) return null;
|
||||
|
||||
// The ByteOrder must have already been set by the caller. In most
|
||||
// cases ByteOrder.nativeOrder() is correct, with the possible
|
||||
// exception of usage within unittests.
|
||||
final StructNdMsg struct = new StructNdMsg();
|
||||
struct.ndm_family = byteBuffer.get();
|
||||
final byte pad1 = byteBuffer.get();
|
||||
final short pad2 = byteBuffer.getShort();
|
||||
struct.ndm_ifindex = byteBuffer.getInt();
|
||||
struct.ndm_state = byteBuffer.getShort();
|
||||
struct.ndm_flags = byteBuffer.get();
|
||||
struct.ndm_type = byteBuffer.get();
|
||||
return struct;
|
||||
}
|
||||
|
||||
public byte ndm_family;
|
||||
public int ndm_ifindex;
|
||||
public short ndm_state;
|
||||
public byte ndm_flags;
|
||||
public byte ndm_type;
|
||||
|
||||
public StructNdMsg() {
|
||||
ndm_family = (byte) OsConstants.AF_UNSPEC;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the neighbor discovery message header to {@link ByteBuffer}.
|
||||
*/
|
||||
public void pack(ByteBuffer byteBuffer) {
|
||||
// The ByteOrder must have already been set by the caller. In most
|
||||
// cases ByteOrder.nativeOrder() is correct, with the exception
|
||||
// of usage within unittests.
|
||||
byteBuffer.put(ndm_family);
|
||||
byteBuffer.put((byte) 0); // pad1
|
||||
byteBuffer.putShort((short) 0); // pad2
|
||||
byteBuffer.putInt(ndm_ifindex);
|
||||
byteBuffer.putShort(ndm_state);
|
||||
byteBuffer.put(ndm_flags);
|
||||
byteBuffer.put(ndm_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a neighbor is connected or not.
|
||||
*/
|
||||
public boolean nudConnected() {
|
||||
return isNudStateConnected(ndm_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a neighbor is in the valid NUD state or not.
|
||||
*/
|
||||
public boolean nudValid() {
|
||||
return isNudStateValid(ndm_state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final String stateStr = "" + ndm_state + " (" + stringForNudState(ndm_state) + ")";
|
||||
final String flagsStr = "" + ndm_flags + " (" + stringForNudFlags(ndm_flags) + ")";
|
||||
return "StructNdMsg{ "
|
||||
+ "family{" + NetlinkConstants.stringForAddressFamily((int) ndm_family) + "}, "
|
||||
+ "ifindex{" + ndm_ifindex + "}, "
|
||||
+ "state{" + stateStr + "}, "
|
||||
+ "flags{" + flagsStr + "}, "
|
||||
+ "type{" + ndm_type + "} "
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* 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.netlink;
|
||||
|
||||
import android.net.IpPrefix;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* The PREF64 router advertisement option. RFC 8781.
|
||||
*
|
||||
* 0 1 2 3
|
||||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | Type | Length | Scaled Lifetime | PLC |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | |
|
||||
* + +
|
||||
* | Highest 96 bits of the Prefix |
|
||||
* + +
|
||||
* | |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*
|
||||
*/
|
||||
public class StructNdOptPref64 extends NdOption {
|
||||
public static final int STRUCT_SIZE = 16;
|
||||
public static final int TYPE = 38;
|
||||
public static final byte LENGTH = 2;
|
||||
|
||||
private static final String TAG = StructNdOptPref64.class.getSimpleName();
|
||||
|
||||
/**
|
||||
* How many seconds the prefix is expected to remain valid.
|
||||
* Valid values are from 0 to 65528 in multiples of 8.
|
||||
*/
|
||||
public final int lifetime;
|
||||
/** The NAT64 prefix. */
|
||||
@NonNull public final IpPrefix prefix;
|
||||
|
||||
static int plcToPrefixLength(int plc) {
|
||||
switch (plc) {
|
||||
case 0: return 96;
|
||||
case 1: return 64;
|
||||
case 2: return 56;
|
||||
case 3: return 48;
|
||||
case 4: return 40;
|
||||
case 5: return 32;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid prefix length code " + plc);
|
||||
}
|
||||
}
|
||||
|
||||
static int prefixLengthToPlc(int prefixLength) {
|
||||
switch (prefixLength) {
|
||||
case 96: return 0;
|
||||
case 64: return 1;
|
||||
case 56: return 2;
|
||||
case 48: return 3;
|
||||
case 40: return 4;
|
||||
case 32: return 5;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid prefix length " + prefixLength);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the 2-byte "scaled lifetime and prefix length code" field: 13-bit lifetime, 3-bit PLC
|
||||
*/
|
||||
static short getScaledLifetimePlc(int lifetime, int prefixLengthCode) {
|
||||
return (short) ((lifetime & 0xfff8) | (prefixLengthCode & 0x7));
|
||||
}
|
||||
|
||||
public StructNdOptPref64(@NonNull IpPrefix prefix, int lifetime) {
|
||||
super((byte) TYPE, LENGTH);
|
||||
|
||||
Objects.requireNonNull(prefix, "prefix must not be null");
|
||||
if (!(prefix.getAddress() instanceof Inet6Address)) {
|
||||
throw new IllegalArgumentException("Must be an IPv6 prefix: " + prefix);
|
||||
}
|
||||
prefixLengthToPlc(prefix.getPrefixLength()); // Throw if the prefix length is invalid.
|
||||
this.prefix = prefix;
|
||||
|
||||
if (lifetime < 0 || lifetime > 0xfff8) {
|
||||
throw new IllegalArgumentException("Invalid lifetime " + lifetime);
|
||||
}
|
||||
this.lifetime = lifetime & 0xfff8;
|
||||
}
|
||||
|
||||
private StructNdOptPref64(@NonNull ByteBuffer buf) {
|
||||
super(buf.get(), Byte.toUnsignedInt(buf.get()));
|
||||
if (type != TYPE) throw new IllegalArgumentException("Invalid type " + type);
|
||||
if (length != LENGTH) throw new IllegalArgumentException("Invalid length " + length);
|
||||
|
||||
int scaledLifetimePlc = Short.toUnsignedInt(buf.getShort());
|
||||
lifetime = scaledLifetimePlc & 0xfff8;
|
||||
|
||||
byte[] addressBytes = new byte[16];
|
||||
buf.get(addressBytes, 0, 12);
|
||||
InetAddress addr;
|
||||
try {
|
||||
addr = InetAddress.getByAddress(addressBytes);
|
||||
} catch (UnknownHostException e) {
|
||||
throw new AssertionError("16-byte array not valid InetAddress?");
|
||||
}
|
||||
prefix = new IpPrefix(addr, plcToPrefixLength(scaledLifetimePlc & 7));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an option from a {@link ByteBuffer}.
|
||||
*
|
||||
* @param buf The buffer from which to parse the option. The buffer's byte order must be
|
||||
* {@link java.nio.ByteOrder#BIG_ENDIAN}.
|
||||
* @return the parsed option, or {@code null} if the option could not be parsed successfully
|
||||
* (for example, if it was truncated, or if the prefix length code was wrong).
|
||||
*/
|
||||
public static StructNdOptPref64 parse(@NonNull ByteBuffer buf) {
|
||||
if (buf == null || buf.remaining() < STRUCT_SIZE) return null;
|
||||
try {
|
||||
return new StructNdOptPref64(buf);
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Not great, but better than throwing an exception that might crash the caller.
|
||||
// Convention in this package is that null indicates that the option was truncated, so
|
||||
// callers must already handle it.
|
||||
Log.d(TAG, "Invalid PREF64 option: " + e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected void writeToByteBuffer(ByteBuffer buf) {
|
||||
super.writeToByteBuffer(buf);
|
||||
buf.putShort(getScaledLifetimePlc(lifetime, prefixLengthToPlc(prefix.getPrefixLength())));
|
||||
buf.put(prefix.getRawAddress(), 0, 12);
|
||||
}
|
||||
|
||||
/** Outputs the wire format of the option to a new big-endian ByteBuffer. */
|
||||
public ByteBuffer toByteBuffer() {
|
||||
ByteBuffer buf = ByteBuffer.allocate(STRUCT_SIZE);
|
||||
writeToByteBuffer(buf);
|
||||
buf.flip();
|
||||
return buf;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public String toString() {
|
||||
return String.format("NdOptPref64(%s, %d)", prefix, lifetime);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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.netlink;
|
||||
|
||||
import android.system.Os;
|
||||
import android.system.OsConstants;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* struct nda_cacheinfo
|
||||
*
|
||||
* see: <linux_src>/include/uapi/linux/neighbour.h
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class StructNdaCacheInfo {
|
||||
// Already aligned.
|
||||
public static final int STRUCT_SIZE = 16;
|
||||
|
||||
private static boolean hasAvailableSpace(ByteBuffer byteBuffer) {
|
||||
return byteBuffer != null && byteBuffer.remaining() >= STRUCT_SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a nd cacheinfo netlink attribute from a {@link ByteBuffer}.
|
||||
*
|
||||
* @param byteBuffer The buffer from which to parse the nd cacheinfo attribute.
|
||||
* @return the parsed nd cacheinfo attribute, or {@code null} if the nd cacheinfo attribute
|
||||
* could not be parsed successfully (for example, if it was truncated).
|
||||
*/
|
||||
public static StructNdaCacheInfo parse(ByteBuffer byteBuffer) {
|
||||
if (!hasAvailableSpace(byteBuffer)) return null;
|
||||
|
||||
// The ByteOrder must have already been set by the caller. In most
|
||||
// cases ByteOrder.nativeOrder() is correct, with the possible
|
||||
// exception of usage within unittests.
|
||||
final StructNdaCacheInfo struct = new StructNdaCacheInfo();
|
||||
struct.ndm_used = byteBuffer.getInt();
|
||||
struct.ndm_confirmed = byteBuffer.getInt();
|
||||
struct.ndm_updated = byteBuffer.getInt();
|
||||
struct.ndm_refcnt = byteBuffer.getInt();
|
||||
return struct;
|
||||
}
|
||||
|
||||
// TODO: investigate whether this can change during device runtime and
|
||||
// decide what (if anything) should be done about that.
|
||||
private static final long CLOCK_TICKS_PER_SECOND = Os.sysconf(OsConstants._SC_CLK_TCK);
|
||||
|
||||
private static long ticksToMilliSeconds(int intClockTicks) {
|
||||
final long longClockTicks = (long) intClockTicks & 0xffffffff;
|
||||
return (longClockTicks * 1000) / CLOCK_TICKS_PER_SECOND;
|
||||
}
|
||||
|
||||
/**
|
||||
* Explanatory notes, for reference.
|
||||
*
|
||||
* Before being returned to user space, the neighbor entry times are
|
||||
* converted to clock_t's like so:
|
||||
*
|
||||
* ndm_used = jiffies_to_clock_t(now - neigh->used);
|
||||
* ndm_confirmed = jiffies_to_clock_t(now - neigh->confirmed);
|
||||
* ndm_updated = jiffies_to_clock_t(now - neigh->updated);
|
||||
*
|
||||
* meaning that these values are expressed as "clock ticks ago". To
|
||||
* convert these clock ticks to seconds divide by sysconf(_SC_CLK_TCK).
|
||||
* When _SC_CLK_TCK is 100, for example, the ndm_* times are expressed
|
||||
* in centiseconds.
|
||||
*
|
||||
* These values are unsigned, but fortunately being expressed as "some
|
||||
* clock ticks ago", these values are typically very small (and
|
||||
* 2^31 centiseconds = 248 days).
|
||||
*
|
||||
* By observation, it appears that:
|
||||
* ndm_used: the last time ARP/ND took place for this neighbor
|
||||
* ndm_confirmed: the last time ARP/ND succeeded for this neighbor OR
|
||||
* higher layer confirmation (TCP or MSG_CONFIRM)
|
||||
* was received
|
||||
* ndm_updated: the time when the current NUD state was entered
|
||||
*/
|
||||
public int ndm_used;
|
||||
public int ndm_confirmed;
|
||||
public int ndm_updated;
|
||||
public int ndm_refcnt;
|
||||
|
||||
public StructNdaCacheInfo() {}
|
||||
|
||||
/**
|
||||
* The last time ARP/ND took place for this neighbor.
|
||||
*/
|
||||
public long lastUsed() {
|
||||
return ticksToMilliSeconds(ndm_used);
|
||||
}
|
||||
|
||||
/**
|
||||
* The last time ARP/ND succeeded for this neighbor or higher layer confirmation (TCP or
|
||||
* MSG_CONFIRM) was received.
|
||||
*/
|
||||
public long lastConfirmed() {
|
||||
return ticksToMilliSeconds(ndm_confirmed);
|
||||
}
|
||||
|
||||
/**
|
||||
* The time when the current NUD state was entered.
|
||||
*/
|
||||
public long lastUpdated() {
|
||||
return ticksToMilliSeconds(ndm_updated);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NdaCacheInfo{ "
|
||||
+ "ndm_used{" + lastUsed() + "}, "
|
||||
+ "ndm_confirmed{" + lastConfirmed() + "}, "
|
||||
+ "ndm_updated{" + lastUpdated() + "}, "
|
||||
+ "ndm_refcnt{" + ndm_refcnt + "} "
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* 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.netlink;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* struct nfgenmsg
|
||||
*
|
||||
* see <linux_src>/include/uapi/linux/netfilter/nfnetlink.h
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class StructNfGenMsg {
|
||||
public static final int STRUCT_SIZE = 2 + Short.BYTES;
|
||||
|
||||
public static final int NFNETLINK_V0 = 0;
|
||||
|
||||
public final byte nfgen_family;
|
||||
public final byte version;
|
||||
public final short res_id; // N.B.: this is big endian in the kernel
|
||||
|
||||
/**
|
||||
* Parse a netfilter netlink header from a {@link ByteBuffer}.
|
||||
*
|
||||
* @param byteBuffer The buffer from which to parse the netfilter netlink header.
|
||||
* @return the parsed netfilter netlink header, or {@code null} if the netfilter netlink header
|
||||
* could not be parsed successfully (for example, if it was truncated).
|
||||
*/
|
||||
@Nullable
|
||||
public static StructNfGenMsg parse(@NonNull ByteBuffer byteBuffer) {
|
||||
Objects.requireNonNull(byteBuffer);
|
||||
|
||||
if (!hasAvailableSpace(byteBuffer)) return null;
|
||||
|
||||
final byte nfgen_family = byteBuffer.get();
|
||||
final byte version = byteBuffer.get();
|
||||
|
||||
final ByteOrder originalOrder = byteBuffer.order();
|
||||
byteBuffer.order(ByteOrder.BIG_ENDIAN);
|
||||
final short res_id = byteBuffer.getShort();
|
||||
byteBuffer.order(originalOrder);
|
||||
|
||||
return new StructNfGenMsg(nfgen_family, version, res_id);
|
||||
}
|
||||
|
||||
public StructNfGenMsg(byte family, byte ver, short id) {
|
||||
nfgen_family = family;
|
||||
version = ver;
|
||||
res_id = id;
|
||||
}
|
||||
|
||||
public StructNfGenMsg(byte family) {
|
||||
nfgen_family = family;
|
||||
version = (byte) NFNETLINK_V0;
|
||||
res_id = (short) 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a netfilter netlink header to a {@link ByteBuffer}.
|
||||
*/
|
||||
public void pack(ByteBuffer byteBuffer) {
|
||||
byteBuffer.put(nfgen_family);
|
||||
byteBuffer.put(version);
|
||||
|
||||
final ByteOrder originalOrder = byteBuffer.order();
|
||||
byteBuffer.order(ByteOrder.BIG_ENDIAN);
|
||||
byteBuffer.putShort(res_id);
|
||||
byteBuffer.order(originalOrder);
|
||||
}
|
||||
|
||||
private static boolean hasAvailableSpace(@NonNull ByteBuffer byteBuffer) {
|
||||
return byteBuffer.remaining() >= STRUCT_SIZE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final String familyStr = NetlinkConstants.stringForAddressFamily(nfgen_family);
|
||||
|
||||
return "NfGenMsg{ "
|
||||
+ "nfgen_family{" + familyStr + "}, "
|
||||
+ "version{" + Byte.toUnsignedInt(version) + "}, "
|
||||
+ "res_id{" + Short.toUnsignedInt(res_id) + "} "
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,315 @@
|
|||
/*
|
||||
* 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.netlink;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
/**
|
||||
* struct nlattr
|
||||
*
|
||||
* see: <linux_src>/include/uapi/linux/netlink.h
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class StructNlAttr {
|
||||
// Already aligned.
|
||||
public static final int NLA_HEADERLEN = 4;
|
||||
public static final int NLA_F_NESTED = (1 << 15);
|
||||
|
||||
/**
|
||||
* Set carries nested attributes bit.
|
||||
*/
|
||||
public static short makeNestedType(short type) {
|
||||
return (short) (type | NLA_F_NESTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Peek and parse the netlink attribute from {@link ByteBuffer}.
|
||||
*
|
||||
* Return a (length, type) object only, without consuming any bytes in
|
||||
* |byteBuffer| and without copying or interpreting any value bytes.
|
||||
* This is used for scanning over a packed set of struct nlattr's,
|
||||
* looking for instances of a particular type.
|
||||
*/
|
||||
public static StructNlAttr peek(ByteBuffer byteBuffer) {
|
||||
if (byteBuffer == null || byteBuffer.remaining() < NLA_HEADERLEN) {
|
||||
return null;
|
||||
}
|
||||
final int baseOffset = byteBuffer.position();
|
||||
|
||||
final StructNlAttr struct = new StructNlAttr();
|
||||
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) {
|
||||
// Malformed.
|
||||
return null;
|
||||
}
|
||||
return struct;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a netlink attribute from a {@link ByteBuffer}.
|
||||
*
|
||||
* @param byteBuffer The buffer from which to parse the netlink attriute.
|
||||
* @return the parsed netlink attribute, or {@code null} if the netlink attribute
|
||||
* could not be parsed successfully (for example, if it was truncated).
|
||||
*/
|
||||
public static StructNlAttr parse(ByteBuffer byteBuffer) {
|
||||
final StructNlAttr struct = peek(byteBuffer);
|
||||
if (struct == null || byteBuffer.remaining() < struct.getAlignedLength()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final int baseOffset = byteBuffer.position();
|
||||
byteBuffer.position(baseOffset + NLA_HEADERLEN);
|
||||
|
||||
int valueLen = ((int) struct.nla_len) & 0xffff;
|
||||
valueLen -= NLA_HEADERLEN;
|
||||
if (valueLen > 0) {
|
||||
struct.nla_value = new byte[valueLen];
|
||||
byteBuffer.get(struct.nla_value, 0, valueLen);
|
||||
byteBuffer.position(baseOffset + struct.getAlignedLength());
|
||||
}
|
||||
return struct;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find next netlink attribute with a given type from {@link ByteBuffer}.
|
||||
*
|
||||
* @param attrType The given netlink attribute type is requested for.
|
||||
* @param byteBuffer The buffer from which to find the netlink attribute.
|
||||
* @return the found netlink attribute, or {@code null} if the netlink attribute could not be
|
||||
* found or parsed successfully (for example, if it was truncated).
|
||||
*/
|
||||
@Nullable
|
||||
public static StructNlAttr findNextAttrOfType(short attrType,
|
||||
@Nullable ByteBuffer byteBuffer) {
|
||||
while (byteBuffer != null && byteBuffer.remaining() > 0) {
|
||||
final StructNlAttr nlAttr = StructNlAttr.peek(byteBuffer);
|
||||
if (nlAttr == null) {
|
||||
break;
|
||||
}
|
||||
if (nlAttr.nla_type == attrType) {
|
||||
return StructNlAttr.parse(byteBuffer);
|
||||
}
|
||||
if (byteBuffer.remaining() < nlAttr.getAlignedLength()) {
|
||||
break;
|
||||
}
|
||||
byteBuffer.position(byteBuffer.position() + nlAttr.getAlignedLength());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public short nla_len = (short) NLA_HEADERLEN;
|
||||
public short nla_type;
|
||||
public byte[] nla_value;
|
||||
|
||||
public StructNlAttr() {}
|
||||
|
||||
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) {
|
||||
nla_type = type;
|
||||
setValue(new byte[Short.BYTES]);
|
||||
final ByteBuffer buf = getValueAsByteBuffer();
|
||||
final ByteOrder originalOrder = buf.order();
|
||||
try {
|
||||
buf.order(order);
|
||||
buf.putShort(value);
|
||||
} finally {
|
||||
buf.order(originalOrder);
|
||||
}
|
||||
}
|
||||
|
||||
public StructNlAttr(short type, int value) {
|
||||
this(type, value, ByteOrder.nativeOrder());
|
||||
}
|
||||
|
||||
public StructNlAttr(short type, int value, ByteOrder order) {
|
||||
nla_type = type;
|
||||
setValue(new byte[Integer.BYTES]);
|
||||
final ByteBuffer buf = getValueAsByteBuffer();
|
||||
final ByteOrder originalOrder = buf.order();
|
||||
try {
|
||||
buf.order(order);
|
||||
buf.putInt(value);
|
||||
} finally {
|
||||
buf.order(originalOrder);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get aligned attribute length.
|
||||
*/
|
||||
public int getAlignedLength() {
|
||||
return NetlinkConstants.alignedLengthOf(nla_len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get attribute value as BE16.
|
||||
*/
|
||||
public short getValueAsBe16(short defaultValue) {
|
||||
final ByteBuffer byteBuffer = getValueAsByteBuffer();
|
||||
if (byteBuffer == null || byteBuffer.remaining() != Short.BYTES) {
|
||||
return defaultValue;
|
||||
}
|
||||
final ByteOrder originalOrder = byteBuffer.order();
|
||||
try {
|
||||
byteBuffer.order(ByteOrder.BIG_ENDIAN);
|
||||
return byteBuffer.getShort();
|
||||
} finally {
|
||||
byteBuffer.order(originalOrder);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get attribute value as BE32.
|
||||
*/
|
||||
public int getValueAsBe32(int defaultValue) {
|
||||
final ByteBuffer byteBuffer = getValueAsByteBuffer();
|
||||
if (byteBuffer == null || byteBuffer.remaining() != Integer.BYTES) {
|
||||
return defaultValue;
|
||||
}
|
||||
final ByteOrder originalOrder = byteBuffer.order();
|
||||
try {
|
||||
byteBuffer.order(ByteOrder.BIG_ENDIAN);
|
||||
return byteBuffer.getInt();
|
||||
} finally {
|
||||
byteBuffer.order(originalOrder);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get attribute value as ByteBuffer.
|
||||
*/
|
||||
public ByteBuffer getValueAsByteBuffer() {
|
||||
if (nla_value == null) return null;
|
||||
final ByteBuffer byteBuffer = ByteBuffer.wrap(nla_value);
|
||||
// By convention, all buffers in this library are in native byte order because netlink is in
|
||||
// native byte order. It's the order that is used by NetlinkSocket.recvMessage and the only
|
||||
// order accepted by NetlinkMessage.parse.
|
||||
byteBuffer.order(ByteOrder.nativeOrder());
|
||||
return byteBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get attribute value as byte.
|
||||
*/
|
||||
public byte getValueAsByte(byte defaultValue) {
|
||||
final ByteBuffer byteBuffer = getValueAsByteBuffer();
|
||||
if (byteBuffer == null || byteBuffer.remaining() != Byte.BYTES) {
|
||||
return defaultValue;
|
||||
}
|
||||
return getValueAsByteBuffer().get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get attribute value as Integer.
|
||||
*/
|
||||
public int getValueAsInt(int defaultValue) {
|
||||
final ByteBuffer byteBuffer = getValueAsByteBuffer();
|
||||
if (byteBuffer == null || byteBuffer.remaining() != Integer.BYTES) {
|
||||
return defaultValue;
|
||||
}
|
||||
return getValueAsByteBuffer().getInt();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get attribute value as InetAddress.
|
||||
*/
|
||||
public InetAddress getValueAsInetAddress() {
|
||||
if (nla_value == null) return null;
|
||||
|
||||
try {
|
||||
return InetAddress.getByAddress(nla_value);
|
||||
} catch (UnknownHostException ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the netlink attribute to {@link ByteBuffer}.
|
||||
*/
|
||||
public void pack(ByteBuffer byteBuffer) {
|
||||
final ByteOrder originalOrder = byteBuffer.order();
|
||||
final int originalPosition = byteBuffer.position();
|
||||
|
||||
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{ "
|
||||
+ "nla_len{" + nla_len + "}, "
|
||||
+ "nla_type{" + nla_type + "}, "
|
||||
+ "nla_value{" + NetlinkConstants.hexify(nla_value) + "}, "
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue