Add data structures to parse netlink route messages.
Bug: 163492391 Test: atest NetworkStaticLibsTests Change-Id: If8c960849f4840b7c516c0d2579da8a805978e13
This commit is contained in:
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 + "} "
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user