summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXiao Ma <xiaom@google.com>2021-11-05 09:58:23 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2021-11-05 09:58:23 +0000
commit95f78385fd77a9e91db3621d4604a1f4a02f3d45 (patch)
tree724a7448ec3d47aec8a8ae0f0c3ad10766f9a049
parent37e0cdf61042a0599eb8840bcac75b0375534dd9 (diff)
parentef623a23fee24cf8bf059cb1b0cc282c85c26e33 (diff)
downloadnet-95f78385fd77a9e91db3621d4604a1f4a02f3d45.tar.gz
Merge "Add data structures to parse netlink route messages." am: ef623a23fe
Original change: https://android-review.googlesource.com/c/platform/frameworks/libs/net/+/1798227 Change-Id: Ia4b8760aa7878387a6808b70696f459cde21e37c
-rw-r--r--common/device/com/android/net/module/util/netlink/NetlinkConstants.java14
-rw-r--r--common/device/com/android/net/module/util/netlink/NetlinkMessage.java3
-rw-r--r--common/device/com/android/net/module/util/netlink/RtNetlinkRouteMessage.java193
-rw-r--r--common/device/com/android/net/module/util/netlink/StructRtMsg.java95
-rw-r--r--common/tests/unit/src/com/android/net/module/util/netlink/RtNetlinkRouteMessageTest.java193
5 files changed, 498 insertions, 0 deletions
diff --git a/common/device/com/android/net/module/util/netlink/NetlinkConstants.java b/common/device/com/android/net/module/util/netlink/NetlinkConstants.java
index 07b52d8c..83a82b74 100644
--- a/common/device/com/android/net/module/util/netlink/NetlinkConstants.java
+++ b/common/device/com/android/net/module/util/netlink/NetlinkConstants.java
@@ -146,12 +146,26 @@ public class NetlinkConstants {
public static final int RTMGRP_LINK = 1;
public static final int RTMGRP_IPV4_IFADDR = 0x10;
public static final int RTMGRP_IPV6_IFADDR = 0x100;
+ public static final int RTMGRP_IPV6_ROUTE = 0x400;
public static final int RTNLGRP_ND_USEROPT = 20;
public static final int RTMGRP_ND_USEROPT = 1 << (RTNLGRP_ND_USEROPT - 1);
// Device flags.
public static final int IFF_LOWER_UP = 1 << 16;
+ // Known values for struct rtmsg rtm_protocol.
+ public static final short RTPROT_KERNEL = 2;
+ public static final short RTPROT_RA = 9;
+
+ // Known values for struct rtmsg rtm_scope.
+ public static final short RT_SCOPE_UNIVERSE = 0;
+
+ // Known values for struct rtmsg rtm_type.
+ public static final short RTN_UNICAST = 1;
+
+ // Known values for struct rtmsg rtm_flags.
+ public static final int RTM_F_CLONED = 0x200;
+
/**
* Convert a netlink message type to a string for control message.
*/
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 708736e5..a216752b 100644
--- a/common/device/com/android/net/module/util/netlink/NetlinkMessage.java
+++ b/common/device/com/android/net/module/util/netlink/NetlinkMessage.java
@@ -126,6 +126,9 @@ public class NetlinkMessage {
case NetlinkConstants.RTM_NEWADDR:
case NetlinkConstants.RTM_DELADDR:
return (NetlinkMessage) RtNetlinkAddressMessage.parse(nlmsghdr, byteBuffer);
+ case NetlinkConstants.RTM_NEWROUTE:
+ case NetlinkConstants.RTM_DELROUTE:
+ return (NetlinkMessage) RtNetlinkRouteMessage.parse(nlmsghdr, byteBuffer);
case NetlinkConstants.RTM_NEWNEIGH:
case NetlinkConstants.RTM_DELNEIGH:
case NetlinkConstants.RTM_GETNEIGH:
diff --git a/common/device/com/android/net/module/util/netlink/RtNetlinkRouteMessage.java b/common/device/com/android/net/module/util/netlink/RtNetlinkRouteMessage.java
new file mode 100644
index 00000000..c5efcb26
--- /dev/null
+++ b/common/device/com/android/net/module/util/netlink/RtNetlinkRouteMessage.java
@@ -0,0 +1,193 @@
+/*
+ * 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 android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+
+import static com.android.net.module.util.NetworkStackConstants.IPV4_ADDR_ANY;
+import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ANY;
+
+import android.net.IpPrefix;
+import android.system.OsConstants;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+
+/**
+ * A NetlinkMessage subclass for rtnetlink route messages.
+ *
+ * RtNetlinkRouteMessage.parse() must be called with a ByteBuffer that contains exactly one
+ * netlink message.
+ *
+ * see also:
+ *
+ * include/uapi/linux/rtnetlink.h
+ *
+ * @hide
+ */
+public class RtNetlinkRouteMessage extends NetlinkMessage {
+ public static final short RTA_DST = 1;
+ public static final short RTA_OIF = 4;
+ public static final short RTA_GATEWAY = 5;
+
+ private int mIfindex;
+ @NonNull
+ private StructRtMsg mRtmsg;
+ @NonNull
+ private IpPrefix mDestination;
+ @Nullable
+ private InetAddress mGateway;
+
+ private RtNetlinkRouteMessage(StructNlMsgHdr header) {
+ super(header);
+ mRtmsg = null;
+ mDestination = null;
+ mGateway = null;
+ mIfindex = 0;
+ }
+
+ public int getInterfaceIndex() {
+ return mIfindex;
+ }
+
+ @NonNull
+ public StructRtMsg getRtMsgHeader() {
+ return mRtmsg;
+ }
+
+ @NonNull
+ public IpPrefix getDestination() {
+ return mDestination;
+ }
+
+ @Nullable
+ public InetAddress getGateway() {
+ return mGateway;
+ }
+
+ /**
+ * Check whether the address families of destination and gateway match rtm_family in
+ * StructRtmsg.
+ *
+ * For example, IPv4-mapped IPv6 addresses as an IPv6 address will be always converted to IPv4
+ * address, that's incorrect when upper layer creates a new {@link RouteInfo} class instance
+ * for IPv6 route with the converted IPv4 gateway.
+ */
+ private static boolean matchRouteAddressFamily(@NonNull final InetAddress address,
+ int family) {
+ return ((address instanceof Inet4Address) && (family == AF_INET))
+ || ((address instanceof Inet6Address) && (family == AF_INET6));
+ }
+
+ /**
+ * Parse rtnetlink route message from {@link ByteBuffer}. This method must be called with a
+ * ByteBuffer that contains exactly one netlink message.
+ *
+ * @param header netlink message header.
+ * @param byteBuffer the ByteBuffer instance that wraps the raw netlink message bytes.
+ */
+ @Nullable
+ public static RtNetlinkRouteMessage parse(@NonNull final StructNlMsgHdr header,
+ @NonNull final ByteBuffer byteBuffer) {
+ final RtNetlinkRouteMessage routeMsg = new RtNetlinkRouteMessage(header);
+
+ routeMsg.mRtmsg = StructRtMsg.parse(byteBuffer);
+ if (routeMsg.mRtmsg == null) return null;
+ int rtmFamily = routeMsg.mRtmsg.family;
+
+ // RTA_DST
+ final int baseOffset = byteBuffer.position();
+ StructNlAttr nlAttr = StructNlAttr.findNextAttrOfType(RTA_DST, byteBuffer);
+ if (nlAttr != null) {
+ final InetAddress destination = nlAttr.getValueAsInetAddress();
+ // If the RTA_DST attribute is malformed, return null.
+ if (destination == null) return null;
+ // If the address family of destination doesn't match rtm_family, return null.
+ if (!matchRouteAddressFamily(destination, rtmFamily)) return null;
+ routeMsg.mDestination = new IpPrefix(destination, routeMsg.mRtmsg.dstLen);
+ } else if (rtmFamily == AF_INET) {
+ routeMsg.mDestination = new IpPrefix(IPV4_ADDR_ANY, 0);
+ } else if (rtmFamily == AF_INET6) {
+ routeMsg.mDestination = new IpPrefix(IPV6_ADDR_ANY, 0);
+ } else {
+ return null;
+ }
+
+ // RTA_GATEWAY
+ byteBuffer.position(baseOffset);
+ nlAttr = StructNlAttr.findNextAttrOfType(RTA_GATEWAY, byteBuffer);
+ if (nlAttr != null) {
+ routeMsg.mGateway = nlAttr.getValueAsInetAddress();
+ // If the RTA_GATEWAY attribute is malformed, return null.
+ if (routeMsg.mGateway == null) return null;
+ // If the address family of gateway doesn't match rtm_family, return null.
+ if (!matchRouteAddressFamily(routeMsg.mGateway, rtmFamily)) return null;
+ }
+
+ // RTA_OIF
+ byteBuffer.position(baseOffset);
+ nlAttr = StructNlAttr.findNextAttrOfType(RTA_OIF, byteBuffer);
+ if (nlAttr != null) {
+ // Any callers that deal with interface names are responsible for converting
+ // the interface index to a name themselves. This may not succeed or may be
+ // incorrect, because the interface might have been deleted, or even deleted
+ // and re-added with a different index, since the netlink message was sent.
+ routeMsg.mIfindex = nlAttr.getValueAsInt(0 /* 0 isn't a valid ifindex */);
+ }
+
+ return routeMsg;
+ }
+
+ /**
+ * Write a rtnetlink address message to {@link ByteBuffer}.
+ */
+ @VisibleForTesting
+ protected void pack(ByteBuffer byteBuffer) {
+ getHeader().pack(byteBuffer);
+ mRtmsg.pack(byteBuffer);
+
+ final StructNlAttr destination = new StructNlAttr(RTA_DST, mDestination.getAddress());
+ destination.pack(byteBuffer);
+
+ if (mGateway != null) {
+ final StructNlAttr gateway = new StructNlAttr(RTA_GATEWAY, mGateway.getAddress());
+ gateway.pack(byteBuffer);
+ }
+ if (mIfindex != 0) {
+ final StructNlAttr ifindex = new StructNlAttr(RTA_OIF, mIfindex);
+ ifindex.pack(byteBuffer);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "RtNetlinkRouteMessage{ "
+ + "nlmsghdr{" + mHeader.toString(OsConstants.NETLINK_ROUTE) + "}, "
+ + "Rtmsg{" + mRtmsg.toString() + "}, "
+ + "destination{" + mDestination.getAddress().getHostAddress() + "}, "
+ + "gateway{" + (mGateway == null ? "" : mGateway.getHostAddress()) + "}, "
+ + "ifindex{" + mIfindex + "} "
+ + "}";
+ }
+}
diff --git a/common/device/com/android/net/module/util/netlink/StructRtMsg.java b/common/device/com/android/net/module/util/netlink/StructRtMsg.java
new file mode 100644
index 00000000..3cd72922
--- /dev/null
+++ b/common/device/com/android/net/module/util/netlink/StructRtMsg.java
@@ -0,0 +1,95 @@
+/*
+ * 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 androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+import java.nio.ByteBuffer;
+
+/**
+ * struct rtmsg
+ *
+ * see also:
+ *
+ * include/uapi/linux/rtnetlink.h
+ *
+ * @hide
+ */
+public class StructRtMsg extends Struct {
+ // Already aligned.
+ public static final int STRUCT_SIZE = 12;
+
+ @Field(order = 0, type = Type.U8)
+ public final short family; // Address family of route.
+ @Field(order = 1, type = Type.U8)
+ public final short dstLen; // Length of destination.
+ @Field(order = 2, type = Type.U8)
+ public final short srcLen; // Length of source.
+ @Field(order = 3, type = Type.U8)
+ public final short tos; // TOS filter.
+ @Field(order = 4, type = Type.U8)
+ public final short table; // Routing table ID.
+ @Field(order = 5, type = Type.U8)
+ public final short protocol; // Routing protocol.
+ @Field(order = 6, type = Type.U8)
+ public final short scope; // distance to the destination.
+ @Field(order = 7, type = Type.U8)
+ public final short type; // route type
+ @Field(order = 8, type = Type.U32)
+ public final long flags;
+
+ StructRtMsg(short family, short dstLen, short srcLen, short tos, short table, short protocol,
+ short scope, short type, long flags) {
+ this.family = family;
+ this.dstLen = dstLen;
+ this.srcLen = srcLen;
+ this.tos = tos;
+ this.table = table;
+ this.protocol = protocol;
+ this.scope = scope;
+ this.type = type;
+ this.flags = flags;
+ }
+
+ /**
+ * Parse a rtmsg struct from a {@link ByteBuffer}.
+ *
+ * @param byteBuffer The buffer from which to parse the rtmsg struct.
+ * @return the parsed rtmsg struct, or {@code null} if the rtmsg struct could not be
+ * parsed successfully (for example, if it was truncated).
+ */
+ @Nullable
+ public static StructRtMsg parse(@NonNull final ByteBuffer byteBuffer) {
+ if (byteBuffer.remaining() < STRUCT_SIZE) return null;
+
+ // The ByteOrder must already have been set to native order.
+ return Struct.parse(StructRtMsg.class, byteBuffer);
+ }
+
+ /**
+ * Write the rtmsg struct to {@link ByteBuffer}.
+ */
+ public void pack(@NonNull final ByteBuffer byteBuffer) {
+ // The ByteOrder must already have been set to native order.
+ this.writeToByteBuffer(byteBuffer);
+ }
+}
diff --git a/common/tests/unit/src/com/android/net/module/util/netlink/RtNetlinkRouteMessageTest.java b/common/tests/unit/src/com/android/net/module/util/netlink/RtNetlinkRouteMessageTest.java
new file mode 100644
index 00000000..392314fb
--- /dev/null
+++ b/common/tests/unit/src/com/android/net/module/util/netlink/RtNetlinkRouteMessageTest.java
@@ -0,0 +1,193 @@
+/*
+ * 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 android.system.OsConstants.NETLINK_ROUTE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.net.InetAddresses;
+import android.net.IpPrefix;
+import android.system.OsConstants;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.net.module.util.HexDump;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.Inet6Address;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class RtNetlinkRouteMessageTest {
+ private static final IpPrefix TEST_IPV6_GLOBAL_PREFIX = new IpPrefix("2001:db8:1::/64");
+ private static final Inet6Address TEST_IPV6_LINK_LOCAL_GATEWAY =
+ (Inet6Address) InetAddresses.parseNumericAddress("fe80::1");
+
+ // An example of the full RTM_NEWROUTE message.
+ private static final String RTM_NEWROUTE_HEX =
+ "88000000180000060000000000000000" // struct nlmsghr
+ + "0A400000FC02000100000000" // struct rtmsg
+ + "08000F00C7060000" // RTA_TABLE
+ + "1400010020010DB8000100000000000000000000" // RTA_DST
+ + "08000400DF020000" // RTA_OIF
+ + "0800060000010000" // RTA_PRIORITY
+ + "24000C0000000000000000005EEA000000000000" // RTA_CACHEINFO
+ + "00000000000000000000000000000000"
+ + "14000500FE800000000000000000000000000001" // RTA_GATEWAY
+ + "0500140000000000"; // RTA_PREF
+
+ private ByteBuffer toByteBuffer(final String hexString) {
+ return ByteBuffer.wrap(HexDump.hexStringToByteArray(hexString));
+ }
+
+ @Test
+ public void testParseRtmRouteAddress() {
+ final ByteBuffer byteBuffer = toByteBuffer(RTM_NEWROUTE_HEX);
+ byteBuffer.order(ByteOrder.LITTLE_ENDIAN); // For testing.
+ final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer, NETLINK_ROUTE);
+ assertNotNull(msg);
+ assertTrue(msg instanceof RtNetlinkRouteMessage);
+ final RtNetlinkRouteMessage routeMsg = (RtNetlinkRouteMessage) msg;
+
+ final StructNlMsgHdr hdr = routeMsg.getHeader();
+ assertNotNull(hdr);
+ assertEquals(136, hdr.nlmsg_len);
+ assertEquals(NetlinkConstants.RTM_NEWROUTE, hdr.nlmsg_type);
+ assertEquals(0x600, hdr.nlmsg_flags);
+ assertEquals(0, hdr.nlmsg_seq);
+ assertEquals(0, hdr.nlmsg_pid);
+
+ final StructRtMsg rtmsg = routeMsg.getRtMsgHeader();
+ assertNotNull(rtmsg);
+ assertEquals((byte) OsConstants.AF_INET6, rtmsg.family);
+ assertEquals(64, rtmsg.dstLen);
+ assertEquals(0, rtmsg.srcLen);
+ assertEquals(0, rtmsg.tos);
+ assertEquals(0xFC, rtmsg.table);
+ assertEquals(NetlinkConstants.RTPROT_KERNEL, rtmsg.protocol);
+ assertEquals(NetlinkConstants.RT_SCOPE_UNIVERSE, rtmsg.scope);
+ assertEquals(NetlinkConstants.RTN_UNICAST, rtmsg.type);
+ assertEquals(0, rtmsg.flags);
+
+ assertEquals(routeMsg.getDestination(), TEST_IPV6_GLOBAL_PREFIX);
+ assertEquals(735, routeMsg.getInterfaceIndex());
+ assertEquals((Inet6Address) routeMsg.getGateway(), TEST_IPV6_LINK_LOCAL_GATEWAY);
+ }
+
+ private static final String RTM_NEWROUTE_PACK_HEX =
+ "4C000000180000060000000000000000" // struct nlmsghr
+ + "0A400000FC02000100000000" // struct rtmsg
+ + "1400010020010DB8000100000000000000000000" // RTA_DST
+ + "14000500FE800000000000000000000000000001" // RTA_GATEWAY
+ + "08000400DF020000"; // RTA_OIF
+
+ @Test
+ public void testPackRtmNewRoute() {
+ final ByteBuffer byteBuffer = toByteBuffer(RTM_NEWROUTE_PACK_HEX);
+ byteBuffer.order(ByteOrder.LITTLE_ENDIAN); // For testing.
+ final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer, NETLINK_ROUTE);
+ assertNotNull(msg);
+ assertTrue(msg instanceof RtNetlinkRouteMessage);
+ final RtNetlinkRouteMessage routeMsg = (RtNetlinkRouteMessage) msg;
+
+ final ByteBuffer packBuffer = ByteBuffer.allocate(76);
+ packBuffer.order(ByteOrder.LITTLE_ENDIAN); // For testing.
+ routeMsg.pack(packBuffer);
+ assertEquals(RTM_NEWROUTE_PACK_HEX, HexDump.toHexString(packBuffer.array()));
+ }
+
+ private static final String RTM_NEWROUTE_TRUNCATED_HEX =
+ "48000000180000060000000000000000" // struct nlmsghr
+ + "0A400000FC02000100000000" // struct rtmsg
+ + "1400010020010DB8000100000000000000000000" // RTA_DST
+ + "10000500FE8000000000000000000000" // RTA_GATEWAY(truncated)
+ + "08000400DF020000"; // RTA_OIF
+
+ @Test
+ public void testTruncatedRtmNewRoute() {
+ final ByteBuffer byteBuffer = toByteBuffer(RTM_NEWROUTE_TRUNCATED_HEX);
+ byteBuffer.order(ByteOrder.LITTLE_ENDIAN); // For testing.
+ final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer, NETLINK_ROUTE);
+ // Parsing RTM_NEWROUTE with truncated RTA_GATEWAY attribute returns null.
+ assertNull(msg);
+ }
+
+ private static final String RTM_NEWROUTE_IPV4_MAPPED_IPV6_GATEWAY_HEX =
+ "4C000000180000060000000000000000" // struct nlmsghr
+ + "0A400000FC02000100000000" // struct rtmsg
+ + "1400010020010DB8000100000000000000000000" // RTA_DST(2001:db8:1::/64)
+ + "1400050000000000000000000000FFFF0A010203" // RTA_GATEWAY(::ffff:10.1.2.3)
+ + "08000400DF020000"; // RTA_OIF
+
+ @Test
+ public void testParseRtmRouteAddress_IPv4MappedIPv6Gateway() {
+ final ByteBuffer byteBuffer = toByteBuffer(RTM_NEWROUTE_IPV4_MAPPED_IPV6_GATEWAY_HEX);
+ byteBuffer.order(ByteOrder.LITTLE_ENDIAN); // For testing.
+ final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer, NETLINK_ROUTE);
+ // Parsing RTM_NEWROUTE with IPv4-mapped IPv6 gateway address, which doesn't match
+ // rtm_family after address parsing.
+ assertNull(msg);
+ }
+
+ private static final String RTM_NEWROUTE_IPV4_MAPPED_IPV6_DST_HEX =
+ "4C000000180000060000000000000000" // struct nlmsghr
+ + "0A780000FC02000100000000" // struct rtmsg
+ + "1400010000000000000000000000FFFF0A000000" // RTA_DST(::ffff:10.0.0.0/120)
+ + "14000500FE800000000000000000000000000001" // RTA_GATEWAY(fe80::1)
+ + "08000400DF020000"; // RTA_OIF
+
+ @Test
+ public void testParseRtmRouteAddress_IPv4MappedIPv6Destination() {
+ final ByteBuffer byteBuffer = toByteBuffer(RTM_NEWROUTE_IPV4_MAPPED_IPV6_DST_HEX);
+ byteBuffer.order(ByteOrder.LITTLE_ENDIAN); // For testing.
+ final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer, NETLINK_ROUTE);
+ // Parsing RTM_NEWROUTE with IPv4-mapped IPv6 destination prefix, which doesn't match
+ // rtm_family after address parsing.
+ assertNull(msg);
+ }
+
+ @Test
+ public void testToString() {
+ final ByteBuffer byteBuffer = toByteBuffer(RTM_NEWROUTE_HEX);
+ byteBuffer.order(ByteOrder.LITTLE_ENDIAN); // For testing.
+ final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer, NETLINK_ROUTE);
+ assertNotNull(msg);
+ assertTrue(msg instanceof RtNetlinkRouteMessage);
+ final RtNetlinkRouteMessage routeMsg = (RtNetlinkRouteMessage) msg;
+ final String expected = "RtNetlinkRouteMessage{ "
+ + "nlmsghdr{"
+ + "StructNlMsgHdr{ nlmsg_len{136}, nlmsg_type{24(RTM_NEWROUTE)}, "
+ + "nlmsg_flags{1536(NLM_F_MATCH)}, nlmsg_seq{0}, nlmsg_pid{0} }}, "
+ + "Rtmsg{"
+ + "family: 10, dstLen: 64, srcLen: 0, tos: 0, table: 252, protocol: 2, "
+ + "scope: 0, type: 1, flags: 0}, "
+ + "destination{2001:db8:1::}, "
+ + "gateway{fe80::1}, "
+ + "ifindex{735} "
+ + "}";
+ assertEquals(expected, routeMsg.toString());
+ }
+}