Add support for multicast route in RtNetlinkRouteMessage
Test: atest NetworkStaticLibTests:com.android.net.moduletests.util.netlink.RtNetlinkRouteMessageTest Change-Id: I2f3d69a5bda81de65e4a36e0db61d98dcf082b2d
This commit is contained in:
parent
d4195824ab
commit
08622b8ecb
3 changed files with 255 additions and 24 deletions
|
|
@ -151,6 +151,9 @@ public class NetlinkConstants {
|
|||
public static final int RTNLGRP_ND_USEROPT = 20;
|
||||
public static final int RTMGRP_ND_USEROPT = 1 << (RTNLGRP_ND_USEROPT - 1);
|
||||
|
||||
// Netlink family
|
||||
public static final short RTNL_FAMILY_IP6MR = 129;
|
||||
|
||||
// Device flags.
|
||||
public static final int IFF_UP = 1 << 0;
|
||||
public static final int IFF_LOWER_UP = 1 << 16;
|
||||
|
|
|
|||
|
|
@ -19,11 +19,15 @@ package com.android.net.module.util.netlink;
|
|||
import static android.system.OsConstants.AF_INET;
|
||||
import static android.system.OsConstants.AF_INET6;
|
||||
|
||||
import static android.system.OsConstants.NETLINK_ROUTE;
|
||||
import static com.android.net.module.util.NetworkStackConstants.IPV4_ADDR_ANY;
|
||||
import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ANY;
|
||||
import static com.android.net.module.util.netlink.NetlinkConstants.hexify;
|
||||
import static com.android.net.module.util.netlink.NetlinkConstants.RTNL_FAMILY_IP6MR;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.net.IpPrefix;
|
||||
import android.net.RouteInfo;
|
||||
import android.system.OsConstants;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
|
@ -34,6 +38,9 @@ import java.net.Inet4Address;
|
|||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* A NetlinkMessage subclass for rtnetlink route messages.
|
||||
|
|
@ -49,31 +56,62 @@ import java.nio.ByteBuffer;
|
|||
*/
|
||||
public class RtNetlinkRouteMessage extends NetlinkMessage {
|
||||
public static final short RTA_DST = 1;
|
||||
public static final short RTA_SRC = 2;
|
||||
public static final short RTA_IIF = 3;
|
||||
public static final short RTA_OIF = 4;
|
||||
public static final short RTA_GATEWAY = 5;
|
||||
public static final short RTA_CACHEINFO = 12;
|
||||
public static final short RTA_EXPIRES = 23;
|
||||
|
||||
private int mIfindex;
|
||||
public static final short RTNH_F_UNRESOLVED = 32; // The multicast route is unresolved
|
||||
|
||||
public static final String TAG = "NetlinkRouteMessage";
|
||||
|
||||
// For multicast routes, whether the route is resolved or unresolved
|
||||
private boolean mIsResolved;
|
||||
// The interface index for incoming interface, this is set for multicast
|
||||
// routes, see common/net/ipv4/ipmr_base.c mr_fill_mroute
|
||||
private int mIifIndex; // Incoming interface of a route, for resolved multicast routes
|
||||
private int mOifIndex;
|
||||
@NonNull
|
||||
private StructRtMsg mRtmsg;
|
||||
@NonNull
|
||||
private IpPrefix mDestination;
|
||||
@Nullable
|
||||
private IpPrefix mSource; // Source address of a route, for all multicast routes
|
||||
@Nullable
|
||||
private IpPrefix mDestination; // Destination of a route, can be null for RTM_GETROUTE
|
||||
@Nullable
|
||||
private InetAddress mGateway;
|
||||
@Nullable
|
||||
private StructRtaCacheInfo mRtaCacheInfo;
|
||||
private long mSinceLastUseMillis; // Milliseconds since the route was used,
|
||||
// for resolved multicast routes
|
||||
|
||||
private RtNetlinkRouteMessage(StructNlMsgHdr header) {
|
||||
public RtNetlinkRouteMessage(StructNlMsgHdr header, StructRtMsg rtMsg) {
|
||||
super(header);
|
||||
mRtmsg = null;
|
||||
mRtmsg = rtMsg;
|
||||
mSource = null;
|
||||
mDestination = null;
|
||||
mGateway = null;
|
||||
mIfindex = 0;
|
||||
mIifIndex = 0;
|
||||
mOifIndex = 0;
|
||||
mRtaCacheInfo = null;
|
||||
mSinceLastUseMillis = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the route is resolved. This is always true for unicast,
|
||||
* and may be false only for multicast routes.
|
||||
*/
|
||||
public boolean isResolved() {
|
||||
return mIsResolved;
|
||||
}
|
||||
|
||||
public int getIifIndex() {
|
||||
return mIifIndex;
|
||||
}
|
||||
|
||||
public int getInterfaceIndex() {
|
||||
return mIfindex;
|
||||
return mOifIndex;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
|
@ -86,6 +124,14 @@ public class RtNetlinkRouteMessage extends NetlinkMessage {
|
|||
return mDestination;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get source address of a route. This is for multicast routes.
|
||||
*/
|
||||
@NonNull
|
||||
public IpPrefix getSource() {
|
||||
return mSource;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public InetAddress getGateway() {
|
||||
return mGateway;
|
||||
|
|
@ -96,6 +142,18 @@ public class RtNetlinkRouteMessage extends NetlinkMessage {
|
|||
return mRtaCacheInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* RTA_EXPIRES attribute returned by kernel to indicate the clock ticks
|
||||
* from the route was last used to now, converted to milliseconds.
|
||||
* This is set for multicast routes.
|
||||
*
|
||||
* Note that this value is not updated with the passage of time. It always
|
||||
* returns the value that was read when the netlink message was parsed.
|
||||
*/
|
||||
public long getSinceLastUseMillis() {
|
||||
return mSinceLastUseMillis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the address families of destination and gateway match rtm_family in
|
||||
* StructRtmsg.
|
||||
|
|
@ -107,7 +165,8 @@ public class RtNetlinkRouteMessage extends NetlinkMessage {
|
|||
private static boolean matchRouteAddressFamily(@NonNull final InetAddress address,
|
||||
int family) {
|
||||
return ((address instanceof Inet4Address) && (family == AF_INET))
|
||||
|| ((address instanceof Inet6Address) && (family == AF_INET6));
|
||||
|| ((address instanceof Inet6Address) &&
|
||||
(family == AF_INET6 || family == RTNL_FAMILY_IP6MR));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -121,11 +180,11 @@ public class RtNetlinkRouteMessage extends NetlinkMessage {
|
|||
@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;
|
||||
final StructRtMsg rtmsg = StructRtMsg.parse(byteBuffer);
|
||||
if (rtmsg == null) return null;
|
||||
final RtNetlinkRouteMessage routeMsg = new RtNetlinkRouteMessage(header, rtmsg);
|
||||
int rtmFamily = routeMsg.mRtmsg.family;
|
||||
routeMsg.mIsResolved = ((routeMsg.mRtmsg.flags & RTNH_F_UNRESOLVED) == 0);
|
||||
|
||||
// RTA_DST
|
||||
final int baseOffset = byteBuffer.position();
|
||||
|
|
@ -139,12 +198,24 @@ public class RtNetlinkRouteMessage extends NetlinkMessage {
|
|||
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) {
|
||||
} else if (rtmFamily == AF_INET6 || rtmFamily == RTNL_FAMILY_IP6MR) {
|
||||
routeMsg.mDestination = new IpPrefix(IPV6_ADDR_ANY, 0);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
// RTA_SRC
|
||||
byteBuffer.position(baseOffset);
|
||||
nlAttr = StructNlAttr.findNextAttrOfType(RTA_SRC, byteBuffer);
|
||||
if (nlAttr != null) {
|
||||
final InetAddress source = nlAttr.getValueAsInetAddress();
|
||||
// If the RTA_SRC attribute is malformed, return null.
|
||||
if (source == null) return null;
|
||||
// If the address family of destination doesn't match rtm_family, return null.
|
||||
if (!matchRouteAddressFamily(source, rtmFamily)) return null;
|
||||
routeMsg.mSource = new IpPrefix(source, routeMsg.mRtmsg.srcLen);
|
||||
}
|
||||
|
||||
// RTA_GATEWAY
|
||||
byteBuffer.position(baseOffset);
|
||||
nlAttr = StructNlAttr.findNextAttrOfType(RTA_GATEWAY, byteBuffer);
|
||||
|
|
@ -156,6 +227,17 @@ public class RtNetlinkRouteMessage extends NetlinkMessage {
|
|||
if (!matchRouteAddressFamily(routeMsg.mGateway, rtmFamily)) return null;
|
||||
}
|
||||
|
||||
// RTA_IIF
|
||||
byteBuffer.position(baseOffset);
|
||||
nlAttr = StructNlAttr.findNextAttrOfType(RTA_IIF, byteBuffer);
|
||||
if (nlAttr != null) {
|
||||
Integer iifInteger = nlAttr.getValueAsInteger();
|
||||
if (iifInteger == null) {
|
||||
return null;
|
||||
}
|
||||
routeMsg.mIifIndex = iifInteger;
|
||||
}
|
||||
|
||||
// RTA_OIF
|
||||
byteBuffer.position(baseOffset);
|
||||
nlAttr = StructNlAttr.findNextAttrOfType(RTA_OIF, byteBuffer);
|
||||
|
|
@ -164,7 +246,7 @@ public class RtNetlinkRouteMessage extends NetlinkMessage {
|
|||
// 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 */);
|
||||
routeMsg.mOifIndex = nlAttr.getValueAsInt(0 /* 0 isn't a valid ifindex */);
|
||||
}
|
||||
|
||||
// RTA_CACHEINFO
|
||||
|
|
@ -174,33 +256,59 @@ public class RtNetlinkRouteMessage extends NetlinkMessage {
|
|||
routeMsg.mRtaCacheInfo = StructRtaCacheInfo.parse(nlAttr.getValueAsByteBuffer());
|
||||
}
|
||||
|
||||
// RTA_EXPIRES
|
||||
byteBuffer.position(baseOffset);
|
||||
nlAttr = StructNlAttr.findNextAttrOfType(RTA_EXPIRES, byteBuffer);
|
||||
if (nlAttr != null) {
|
||||
final Long sinceLastUseCentis = nlAttr.getValueAsLong();
|
||||
// If the RTA_EXPIRES attribute is malformed, return null.
|
||||
if (sinceLastUseCentis == null) return null;
|
||||
// RTA_EXPIRES returns time in clock ticks of USER_HZ(100), which is centiseconds
|
||||
routeMsg.mSinceLastUseMillis = sinceLastUseCentis * 10;
|
||||
}
|
||||
|
||||
return routeMsg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a rtnetlink address message to {@link ByteBuffer}.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
protected void pack(ByteBuffer byteBuffer) {
|
||||
public void pack(ByteBuffer byteBuffer) {
|
||||
getHeader().pack(byteBuffer);
|
||||
mRtmsg.pack(byteBuffer);
|
||||
|
||||
final StructNlAttr destination = new StructNlAttr(RTA_DST, mDestination.getAddress());
|
||||
destination.pack(byteBuffer);
|
||||
if (mSource != null) {
|
||||
final StructNlAttr source = new StructNlAttr(RTA_SRC, mSource.getAddress());
|
||||
source.pack(byteBuffer);
|
||||
}
|
||||
|
||||
if (mDestination != null) {
|
||||
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);
|
||||
if (mIifIndex != 0) {
|
||||
final StructNlAttr iifindex = new StructNlAttr(RTA_IIF, mIifIndex);
|
||||
iifindex.pack(byteBuffer);
|
||||
}
|
||||
if (mOifIndex != 0) {
|
||||
final StructNlAttr oifindex = new StructNlAttr(RTA_OIF, mOifIndex);
|
||||
oifindex.pack(byteBuffer);
|
||||
}
|
||||
if (mRtaCacheInfo != null) {
|
||||
final StructNlAttr cacheInfo = new StructNlAttr(RTA_CACHEINFO,
|
||||
mRtaCacheInfo.writeToBytes());
|
||||
cacheInfo.pack(byteBuffer);
|
||||
}
|
||||
if (mSinceLastUseMillis >= 0) {
|
||||
final long sinceLastUseCentis = mSinceLastUseMillis / 10;
|
||||
final StructNlAttr expires = new StructNlAttr(RTA_EXPIRES, sinceLastUseCentis);
|
||||
expires.pack(byteBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -208,10 +316,14 @@ public class RtNetlinkRouteMessage extends NetlinkMessage {
|
|||
return "RtNetlinkRouteMessage{ "
|
||||
+ "nlmsghdr{" + mHeader.toString(OsConstants.NETLINK_ROUTE) + "}, "
|
||||
+ "Rtmsg{" + mRtmsg.toString() + "}, "
|
||||
+ "destination{" + mDestination.getAddress().getHostAddress() + "}, "
|
||||
+ (mSource == null ? "" : "source{" + mSource.getAddress().getHostAddress() + "}, ")
|
||||
+ (mDestination == null ?
|
||||
"" : "destination{" + mDestination.getAddress().getHostAddress() + "}, ")
|
||||
+ "gateway{" + (mGateway == null ? "" : mGateway.getHostAddress()) + "}, "
|
||||
+ "ifindex{" + mIfindex + "}, "
|
||||
+ (mIifIndex == 0 ? "" : "iifindex{" + mIifIndex + "}, ")
|
||||
+ "oifindex{" + mOifIndex + "}, "
|
||||
+ "rta_cacheinfo{" + (mRtaCacheInfo == null ? "" : mRtaCacheInfo.toString()) + "} "
|
||||
+ (mSinceLastUseMillis < 0 ? "" : "sinceLastUseMillis{" + mSinceLastUseMillis + "}")
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import org.junit.runner.RunWith;
|
|||
import java.net.Inet6Address;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Arrays;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
|
|
@ -127,6 +128,72 @@ public class RtNetlinkRouteMessageTest {
|
|||
assertEquals(RTM_NEWROUTE_PACK_HEX, HexDump.toHexString(packBuffer.array()));
|
||||
}
|
||||
|
||||
private static final String RTM_GETROUTE_MULTICAST_IPV6_HEX =
|
||||
"1C0000001A0001030000000000000000" // struct nlmsghr
|
||||
+ "810000000000000000000000"; // struct rtmsg
|
||||
|
||||
private static final String RTM_NEWROUTE_MULTICAST_IPV6_HEX =
|
||||
"88000000180002000000000000000000" // struct nlmsghr
|
||||
+ "81808000FE11000500000000" // struct rtmsg
|
||||
+ "08000F00FE000000" // RTA_TABLE
|
||||
+ "14000200FDACC0F1DBDB000195B7C1A464F944EA" // RTA_SRC
|
||||
+ "14000100FF040000000000000000000000001234" // RTA_DST
|
||||
+ "0800030014000000" // RTA_IIF
|
||||
+ "0C0009000800000111000000" // RTA_MULTIPATH
|
||||
+ "1C00110001000000000000009400000000000000" // RTA_STATS
|
||||
+ "0000000000000000"
|
||||
+ "0C0017007617000000000000"; // RTA_EXPIRES
|
||||
|
||||
@Test
|
||||
public void testParseRtmNewRoute_MulticastIpv6() {
|
||||
final ByteBuffer byteBuffer = toByteBuffer(RTM_NEWROUTE_MULTICAST_IPV6_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);
|
||||
|
||||
final StructRtMsg rtmsg = routeMsg.getRtMsgHeader();
|
||||
assertNotNull(rtmsg);
|
||||
assertEquals((byte) 129, (byte) rtmsg.family);
|
||||
assertEquals(128, rtmsg.dstLen);
|
||||
assertEquals(128, rtmsg.srcLen);
|
||||
assertEquals(0xFE, rtmsg.table);
|
||||
|
||||
assertEquals(routeMsg.getSource(),
|
||||
new IpPrefix("fdac:c0f1:dbdb:1:95b7:c1a4:64f9:44ea/128"));
|
||||
assertEquals(routeMsg.getDestination(), new IpPrefix("ff04::1234/128"));
|
||||
assertEquals(20, routeMsg.getIifIndex());
|
||||
assertEquals(60060, routeMsg.getSinceLastUseMillis());
|
||||
}
|
||||
|
||||
// NEWROUTE message for multicast IPv6 with the packed attributes
|
||||
private static final String RTM_NEWROUTE_MULTICAST_IPV6_PACK_HEX =
|
||||
"58000000180002000000000000000000" // struct nlmsghr
|
||||
+ "81808000FE11000500000000" // struct rtmsg
|
||||
+ "14000200FDACC0F1DBDB000195B7C1A464F944EA" // RTA_SRC
|
||||
+ "14000100FF040000000000000000000000001234" // RTA_DST
|
||||
+ "0800030014000000" // RTA_IIF
|
||||
+ "0C0017007617000000000000"; // RTA_EXPIRES
|
||||
@Test
|
||||
public void testPackRtmNewRoute_MulticastIpv6() {
|
||||
final ByteBuffer byteBuffer = toByteBuffer(RTM_NEWROUTE_MULTICAST_IPV6_PACK_HEX);
|
||||
byteBuffer.order(ByteOrder.LITTLE_ENDIAN); // For testing.
|
||||
final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer, NETLINK_ROUTE);
|
||||
final RtNetlinkRouteMessage routeMsg = (RtNetlinkRouteMessage) msg;
|
||||
|
||||
final ByteBuffer packBuffer = ByteBuffer.allocate(88);
|
||||
packBuffer.order(ByteOrder.LITTLE_ENDIAN); // For testing.
|
||||
routeMsg.pack(packBuffer);
|
||||
assertEquals(RTM_NEWROUTE_MULTICAST_IPV6_PACK_HEX,
|
||||
HexDump.toHexString(packBuffer.array()));
|
||||
}
|
||||
|
||||
private static final String RTM_NEWROUTE_TRUNCATED_HEX =
|
||||
"48000000180000060000000000000000" // struct nlmsghr
|
||||
+ "0A400000FC02000100000000" // struct rtmsg
|
||||
|
|
@ -220,10 +287,59 @@ public class RtNetlinkRouteMessageTest {
|
|||
+ "scope: 0, type: 1, flags: 0}, "
|
||||
+ "destination{2001:db8:1::}, "
|
||||
+ "gateway{fe80::1}, "
|
||||
+ "ifindex{735}, "
|
||||
+ "oifindex{735}, "
|
||||
+ "rta_cacheinfo{clntref: 0, lastuse: 0, expires: 59998, error: 0, used: 0, "
|
||||
+ "id: 0, ts: 0, tsage: 0} "
|
||||
+ "}";
|
||||
assertEquals(expected, routeMsg.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToString_RtmGetRoute() {
|
||||
final ByteBuffer byteBuffer = toByteBuffer(RTM_GETROUTE_MULTICAST_IPV6_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{28}, nlmsg_type{26(RTM_GETROUTE)}, "
|
||||
+ "nlmsg_flags{769(NLM_F_REQUEST|NLM_F_DUMP)}, nlmsg_seq{0}, nlmsg_pid{0} }}, "
|
||||
+ "Rtmsg{"
|
||||
+ "family: 129, dstLen: 0, srcLen: 0, tos: 0, table: 0, protocol: 0, "
|
||||
+ "scope: 0, type: 0, flags: 0}, "
|
||||
+ "destination{::}, "
|
||||
+ "gateway{}, "
|
||||
+ "oifindex{0}, "
|
||||
+ "rta_cacheinfo{} "
|
||||
+ "}";
|
||||
assertEquals(expected, routeMsg.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToString_RtmNewRouteMulticastIpv6() {
|
||||
final ByteBuffer byteBuffer = toByteBuffer(RTM_NEWROUTE_MULTICAST_IPV6_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{2(NLM_F_MULTI)}, nlmsg_seq{0}, nlmsg_pid{0} }}, "
|
||||
+ "Rtmsg{"
|
||||
+ "family: 129, dstLen: 128, srcLen: 128, tos: 0, table: 254, protocol: 17, "
|
||||
+ "scope: 0, type: 5, flags: 0}, "
|
||||
+ "source{fdac:c0f1:dbdb:1:95b7:c1a4:64f9:44ea}, "
|
||||
+ "destination{ff04::1234}, "
|
||||
+ "gateway{}, "
|
||||
+ "iifindex{20}, "
|
||||
+ "oifindex{0}, "
|
||||
+ "rta_cacheinfo{} "
|
||||
+ "sinceLastUseMillis{60060}"
|
||||
+ "}";
|
||||
assertEquals(expected, routeMsg.toString());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue