diff options
Diffstat (limited to 'common/device')
11 files changed, 191 insertions, 26 deletions
diff --git a/common/device/com/android/net/module/util/netlink/ConntrackMessage.java b/common/device/com/android/net/module/util/netlink/ConntrackMessage.java index 1763c04b..dfed3efb 100644 --- a/common/device/com/android/net/module/util/netlink/ConntrackMessage.java +++ b/common/device/com/android/net/module/util/netlink/ConntrackMessage.java @@ -228,7 +228,9 @@ public class ConntrackMessage extends NetlinkMessage { * @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) { + @Nullable + public static ConntrackMessage parse(@NonNull StructNlMsgHdr header, + @NonNull ByteBuffer byteBuffer) { // Just build the netlink header and netfilter header for now and pretend the whole message // was consumed. // TODO: Parse the conntrack attributes. diff --git a/common/device/com/android/net/module/util/netlink/InetDiagMessage.java b/common/device/com/android/net/module/util/netlink/InetDiagMessage.java index 7b200e75..a8aef7b4 100644 --- a/common/device/com/android/net/module/util/netlink/InetDiagMessage.java +++ b/common/device/com/android/net/module/util/netlink/InetDiagMessage.java @@ -31,6 +31,7 @@ import android.net.util.SocketUtils; import android.system.ErrnoException; import android.util.Log; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import java.io.FileDescriptor; @@ -107,7 +108,7 @@ public class InetDiagMessage extends NetlinkMessage { public StructInetDiagMsg mStructInetDiagMsg; - private InetDiagMessage(StructNlMsgHdr header) { + private InetDiagMessage(@NonNull StructNlMsgHdr header) { super(header); mStructInetDiagMsg = new StructInetDiagMsg(); } @@ -115,7 +116,9 @@ public class InetDiagMessage extends NetlinkMessage { /** * Parse an inet_diag_req_v2 message from buffer. */ - public static InetDiagMessage parse(StructNlMsgHdr header, ByteBuffer byteBuffer) { + @NonNull + public static InetDiagMessage parse(@NonNull StructNlMsgHdr header, + @NonNull ByteBuffer byteBuffer) { final InetDiagMessage msg = new InetDiagMessage(header); msg.mStructInetDiagMsg = StructInetDiagMsg.parse(byteBuffer); return msg; diff --git a/common/device/com/android/net/module/util/netlink/NdOption.java b/common/device/com/android/net/module/util/netlink/NdOption.java index 50a34966..defc88a7 100644 --- a/common/device/com/android/net/module/util/netlink/NdOption.java +++ b/common/device/com/android/net/module/util/netlink/NdOption.java @@ -16,6 +16,8 @@ package com.android.net.module.util.netlink; +import androidx.annotation.NonNull; + import java.nio.ByteBuffer; /** @@ -50,8 +52,8 @@ public class NdOption { * @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; + public static NdOption parse(@NonNull ByteBuffer buf) { + if (buf.remaining() < STRUCT_SIZE) return null; // Peek the type without advancing the buffer. byte type = buf.get(buf.position()); @@ -62,6 +64,9 @@ public class NdOption { case StructNdOptPref64.TYPE: return StructNdOptPref64.parse(buf); + case StructNdOptRdnss.TYPE: + return StructNdOptRdnss.parse(buf); + default: int newPosition = Math.min(buf.limit(), buf.position() + length * 8); buf.position(newPosition); diff --git a/common/device/com/android/net/module/util/netlink/NduseroptMessage.java b/common/device/com/android/net/module/util/netlink/NduseroptMessage.java index 4e3b9f2d..bdf574db 100644 --- a/common/device/com/android/net/module/util/netlink/NduseroptMessage.java +++ b/common/device/com/android/net/module/util/netlink/NduseroptMessage.java @@ -19,6 +19,7 @@ package com.android.net.module.util.netlink; import static android.system.OsConstants.AF_INET6; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import java.net.Inet6Address; import java.net.InetAddress; @@ -56,6 +57,7 @@ public class NduseroptMessage extends NetlinkMessage { * But if it does, we can simply update this code, since userspace is typically newer than the * kernel. */ + @Nullable public final NdOption option; /** The IP address that sent the packet containing the option. */ @@ -80,22 +82,26 @@ public class NduseroptMessage extends NetlinkMessage { // 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. + // + // It's safer to pass the slice of original ByteBuffer to just parse the ND option field, + // although parsing ND option might throw exception or return null, it won't break the + // original ByteBuffer position. buf.order(ByteOrder.BIG_ENDIAN); - int oldLimit = buf.limit(); - buf.limit(start + STRUCT_SIZE + opts_len); try { - option = NdOption.parse(buf); + final ByteBuffer slice = buf.slice(); + slice.limit(opts_len); + option = NdOption.parse(slice); } finally { - buf.limit(oldLimit); + // Advance buffer position according to opts_len in the header. ND option length might + // be incorrect in the malformed packet. + int newPosition = start + STRUCT_SIZE + opts_len; + if (newPosition >= buf.limit()) { + throw new IllegalArgumentException("ND option extends past end of buffer"); + } + buf.position(newPosition); } - // 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); - + // The source address attribute. 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"); @@ -117,6 +123,7 @@ public class NduseroptMessage extends NetlinkMessage { * @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). */ + @Nullable public static NduseroptMessage parse(@NonNull StructNlMsgHdr header, @NonNull ByteBuffer buf) { if (buf == null || buf.remaining() < STRUCT_SIZE) return null; ByteOrder oldOrder = buf.order(); diff --git a/common/device/com/android/net/module/util/netlink/NetlinkErrorMessage.java b/common/device/com/android/net/module/util/netlink/NetlinkErrorMessage.java index d9fb09e6..48314323 100644 --- a/common/device/com/android/net/module/util/netlink/NetlinkErrorMessage.java +++ b/common/device/com/android/net/module/util/netlink/NetlinkErrorMessage.java @@ -16,6 +16,9 @@ package com.android.net.module.util.netlink; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import java.nio.ByteBuffer; /** @@ -32,7 +35,9 @@ public class NetlinkErrorMessage extends NetlinkMessage { * @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) { + @Nullable + public static NetlinkErrorMessage parse(@NonNull StructNlMsgHdr header, + @NonNull ByteBuffer byteBuffer) { final NetlinkErrorMessage errorMsg = new NetlinkErrorMessage(header); errorMsg.mNlMsgErr = StructNlMsgErr.parse(byteBuffer); @@ -45,7 +50,7 @@ public class NetlinkErrorMessage extends NetlinkMessage { private StructNlMsgErr mNlMsgErr; - NetlinkErrorMessage(StructNlMsgHdr header) { + NetlinkErrorMessage(@NonNull StructNlMsgHdr header) { super(header); mNlMsgErr = null; } diff --git a/common/device/com/android/net/module/util/netlink/NetlinkMessage.java b/common/device/com/android/net/module/util/netlink/NetlinkMessage.java index f425384f..723d6823 100644 --- a/common/device/com/android/net/module/util/netlink/NetlinkMessage.java +++ b/common/device/com/android/net/module/util/netlink/NetlinkMessage.java @@ -79,12 +79,14 @@ public class NetlinkMessage { return null; } - protected StructNlMsgHdr mHeader; + @NonNull + protected final StructNlMsgHdr mHeader; - public NetlinkMessage(StructNlMsgHdr nlmsghdr) { + public NetlinkMessage(@NonNull StructNlMsgHdr nlmsghdr) { mHeader = nlmsghdr; } + @NonNull public StructNlMsgHdr getHeader() { return mHeader; } @@ -96,7 +98,7 @@ public class NetlinkMessage { // 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()) + "}"; + return "NetlinkMessage{" + mHeader.toString() + "}"; } @NonNull diff --git a/common/device/com/android/net/module/util/netlink/RtNetlinkNeighborMessage.java b/common/device/com/android/net/module/util/netlink/RtNetlinkNeighborMessage.java index a75ef8dd..4a090151 100644 --- a/common/device/com/android/net/module/util/netlink/RtNetlinkNeighborMessage.java +++ b/common/device/com/android/net/module/util/netlink/RtNetlinkNeighborMessage.java @@ -23,6 +23,9 @@ import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REQUEST; import android.system.OsConstants; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import java.net.Inet6Address; import java.net.InetAddress; import java.nio.ByteBuffer; @@ -53,7 +56,9 @@ public class RtNetlinkNeighborMessage extends NetlinkMessage { * @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) { + @Nullable + public static RtNetlinkNeighborMessage parse(@NonNull StructNlMsgHdr header, + @NonNull ByteBuffer byteBuffer) { final RtNetlinkNeighborMessage neighMsg = new RtNetlinkNeighborMessage(header); neighMsg.mNdmsg = StructNdMsg.parse(byteBuffer); @@ -154,7 +159,7 @@ public class RtNetlinkNeighborMessage extends NetlinkMessage { private int mNumProbes; private StructNdaCacheInfo mCacheInfo; - private RtNetlinkNeighborMessage(StructNlMsgHdr header) { + private RtNetlinkNeighborMessage(@NonNull StructNlMsgHdr header) { super(header); mNdmsg = null; mDestination = null; diff --git a/common/device/com/android/net/module/util/netlink/StructNdOptPref64.java b/common/device/com/android/net/module/util/netlink/StructNdOptPref64.java index bde69831..f6b2e0e5 100644 --- a/common/device/com/android/net/module/util/netlink/StructNdOptPref64.java +++ b/common/device/com/android/net/module/util/netlink/StructNdOptPref64.java @@ -135,7 +135,7 @@ public class StructNdOptPref64 extends NdOption { * (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; + if (buf.remaining() < STRUCT_SIZE) return null; try { return new StructNdOptPref64(buf); } catch (IllegalArgumentException e) { diff --git a/common/device/com/android/net/module/util/netlink/StructNdOptRdnss.java b/common/device/com/android/net/module/util/netlink/StructNdOptRdnss.java new file mode 100644 index 00000000..6dee0c49 --- /dev/null +++ b/common/device/com/android/net/module/util/netlink/StructNdOptRdnss.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2021 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.NetworkStackConstants.IPV6_ADDR_LEN; + +import android.util.Log; + +import androidx.annotation.NonNull; + +import com.android.net.module.util.Struct; +import com.android.net.module.util.structs.RdnssOption; + +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.util.Objects; +import java.util.StringJoiner; + +/** + * The Recursive DNS Server Option. RFC 8106. + * + * 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 | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Lifetime | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * : Addresses of IPv6 Recursive DNS Servers : + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +public class StructNdOptRdnss extends NdOption { + private static final String TAG = StructNdOptRdnss.class.getSimpleName(); + public static final int TYPE = 25; + // Length in 8-byte units, only if one IPv6 address included. + public static final byte MIN_OPTION_LEN = 3; + + public final RdnssOption header; + @NonNull + public final Inet6Address[] servers; + + public StructNdOptRdnss(@NonNull final Inet6Address[] servers, long lifetime) { + super((byte) TYPE, servers.length * 2 + 1); + + Objects.requireNonNull(servers, "Recursive DNS Servers address array must not be null"); + if (servers.length == 0) { + throw new IllegalArgumentException("DNS server address array must not be empty"); + } + + this.header = new RdnssOption((byte) TYPE, (byte) (servers.length * 2 + 1), + (short) 0 /* reserved */, lifetime); + this.servers = servers.clone(); + } + + /** + * Parses an RDNSS 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. + */ + public static StructNdOptRdnss parse(@NonNull ByteBuffer buf) { + if (buf == null || buf.remaining() < MIN_OPTION_LEN * 8) return null; + try { + final RdnssOption header = Struct.parse(RdnssOption.class, buf); + if (header.type != TYPE) { + throw new IllegalArgumentException("Invalid type " + header.type); + } + if (header.length < MIN_OPTION_LEN || (header.length % 2 == 0)) { + throw new IllegalArgumentException("Invalid length " + header.length); + } + + final int numOfDnses = (header.length - 1) / 2; + final Inet6Address[] servers = new Inet6Address[numOfDnses]; + for (int i = 0; i < numOfDnses; i++) { + byte[] rawAddress = new byte[IPV6_ADDR_LEN]; + buf.get(rawAddress); + servers[i] = (Inet6Address) InetAddress.getByAddress(rawAddress); + } + return new StructNdOptRdnss(servers, header.lifetime); + } catch (IllegalArgumentException | BufferUnderflowException | UnknownHostException 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 + // or malformed, so callers must already handle it. + Log.d(TAG, "Invalid RDNSS option: " + e); + return null; + } + } + + protected void writeToByteBuffer(ByteBuffer buf) { + header.writeToByteBuffer(buf); + for (int i = 0; i < servers.length; i++) { + buf.put(servers[i].getAddress()); + } + } + + /** Outputs the wire format of the option to a new big-endian ByteBuffer. */ + public ByteBuffer toByteBuffer() { + final ByteBuffer buf = ByteBuffer.allocate(Struct.getSize(RdnssOption.class) + + servers.length * IPV6_ADDR_LEN); + writeToByteBuffer(buf); + buf.flip(); + return buf; + } + + @Override + @NonNull + public String toString() { + final StringJoiner sj = new StringJoiner(",", "[", "]"); + for (int i = 0; i < servers.length; i++) { + sj.add(servers[i].getHostAddress()); + } + return String.format("NdOptRdnss(%s,servers:%s)", header.toString(), sj.toString()); + } +} diff --git a/common/device/com/android/net/module/util/netlink/StructNlMsgHdr.java b/common/device/com/android/net/module/util/netlink/StructNlMsgHdr.java index ddf1562f..9567cce2 100644 --- a/common/device/com/android/net/module/util/netlink/StructNlMsgHdr.java +++ b/common/device/com/android/net/module/util/netlink/StructNlMsgHdr.java @@ -93,7 +93,8 @@ public class StructNlMsgHdr { /** * Parse netlink message header from buffer. */ - public static StructNlMsgHdr parse(ByteBuffer byteBuffer) { + @Nullable + public static StructNlMsgHdr parse(@NonNull ByteBuffer byteBuffer) { if (!hasAvailableSpace(byteBuffer)) return null; // The ByteOrder must have already been set by the caller. In most diff --git a/common/device/com/android/net/module/util/structs/RdnssOption.java b/common/device/com/android/net/module/util/structs/RdnssOption.java index b7c2b0c1..4a5bd7e9 100644 --- a/common/device/com/android/net/module/util/structs/RdnssOption.java +++ b/common/device/com/android/net/module/util/structs/RdnssOption.java @@ -53,7 +53,8 @@ public class RdnssOption extends Struct { @Field(order = 3, type = Type.U32) public final long lifetime; - RdnssOption(final byte type, final byte length, final short reserved, final long lifetime) { + public RdnssOption(final byte type, final byte length, final short reserved, + final long lifetime) { this.type = type; this.length = length; this.reserved = reserved; |