summaryrefslogtreecommitdiff
path: root/common/netlinkclient/src
diff options
context:
space:
mode:
authorLorenzo Colitti <lorenzo@google.com>2020-04-16 04:45:27 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2020-04-16 04:45:27 +0000
commit77e026021fd4b42c51589b95c89edb52820e2cf9 (patch)
tree86f7334199283b6893b5cf308153679f3f11772d /common/netlinkclient/src
parent109ee272e371af74ff94f5413dfaa95a5d963bf8 (diff)
parent965f9b45cf6a411619e986958aa8c66cb451b440 (diff)
Merge "Support parsing ND option messages." into rvc-dev
Diffstat (limited to 'common/netlinkclient/src')
-rw-r--r--common/netlinkclient/src/android/net/netlink/NdOption.java78
-rw-r--r--common/netlinkclient/src/android/net/netlink/NduseroptMessage.java137
-rw-r--r--common/netlinkclient/src/android/net/netlink/NetlinkMessage.java2
-rw-r--r--common/netlinkclient/src/android/net/netlink/StructNdOptPref64.java11
4 files changed, 220 insertions, 8 deletions
diff --git a/common/netlinkclient/src/android/net/netlink/NdOption.java b/common/netlinkclient/src/android/net/netlink/NdOption.java
new file mode 100644
index 0000000..db262b9
--- /dev/null
+++ b/common/netlinkclient/src/android/net/netlink/NdOption.java
@@ -0,0 +1,78 @@
+/*
+ * 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 android.net.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. */
+ public 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;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return String.format("NdOption(%d, %d)", Byte.toUnsignedInt(type), length);
+ }
+
+ public static final NdOption UNKNOWN = new NdOption((byte) 0, 0);
+}
diff --git a/common/netlinkclient/src/android/net/netlink/NduseroptMessage.java b/common/netlinkclient/src/android/net/netlink/NduseroptMessage.java
new file mode 100644
index 0000000..4940f6e
--- /dev/null
+++ b/common/netlinkclient/src/android/net/netlink/NduseroptMessage.java
@@ -0,0 +1,137 @@
+/*
+ * 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 android.net.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());
+ 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.order(ByteOrder.BIG_ENDIAN);
+ 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.
+ int oldLimit = buf.limit();
+ buf.limit(STRUCT_SIZE + opts_len);
+ try {
+ option = NdOption.parse(buf);
+ } finally {
+ buf.limit(oldLimit);
+ }
+
+ // The source address.
+ int newPosition = 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;
+ 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;
+ }
+ }
+
+ @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());
+ }
+}
diff --git a/common/netlinkclient/src/android/net/netlink/NetlinkMessage.java b/common/netlinkclient/src/android/net/netlink/NetlinkMessage.java
index b730032..dafa66b 100644
--- a/common/netlinkclient/src/android/net/netlink/NetlinkMessage.java
+++ b/common/netlinkclient/src/android/net/netlink/NetlinkMessage.java
@@ -64,6 +64,8 @@ public class NetlinkMessage {
return (NetlinkMessage) RtNetlinkNeighborMessage.parse(nlmsghdr, byteBuffer);
case NetlinkConstants.SOCK_DIAG_BY_FAMILY:
return (NetlinkMessage) InetDiagMessage.parse(nlmsghdr, byteBuffer);
+ case NetlinkConstants.RTM_NEWNDUSEROPT:
+ return (NetlinkMessage) NduseroptMessage.parse(nlmsghdr, byteBuffer);
default:
if (nlmsghdr.nlmsg_type <= NetlinkConstants.NLMSG_MAX_RESERVED) {
// Netlink control message. Just parse the header for now,
diff --git a/common/netlinkclient/src/android/net/netlink/StructNdOptPref64.java b/common/netlinkclient/src/android/net/netlink/StructNdOptPref64.java
index 6a68df8..5cce3da 100644
--- a/common/netlinkclient/src/android/net/netlink/StructNdOptPref64.java
+++ b/common/netlinkclient/src/android/net/netlink/StructNdOptPref64.java
@@ -41,16 +41,12 @@ import java.nio.ByteBuffer;
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
*/
-public class StructNdOptPref64 {
+public class StructNdOptPref64 extends NdOption {
public static final int STRUCT_SIZE = 16;
public static final int TYPE = 38;
private static final String TAG = StructNdOptPref64.class.getSimpleName();
- /** The option type. Always ICMPV6_ND_OPTION_PREF64. */
- public final byte type;
- /** The length of the option in 8-byte units. Actually an unsigned 8-bit integer. */
- public final int length;
/**
* How many seconds the prefix is expected to remain valid.
* Valid values are from 0 to 65528 in multiples of 8.
@@ -72,9 +68,8 @@ public class StructNdOptPref64 {
}
}
- StructNdOptPref64(@NonNull ByteBuffer buf) {
- type = buf.get();
- length = buf.get();
+ public StructNdOptPref64(@NonNull ByteBuffer buf) {
+ super(buf.get(), Byte.toUnsignedInt(buf.get()));
if (type != TYPE) throw new IllegalArgumentException("Invalid type " + type);
if (length != 2) throw new IllegalArgumentException("Invalid length " + length);