Merge "Add RtmNetlinkPrefixMessage class to represent RTM_NEWPREFIX message." into main
This commit is contained in:
commit
7dd2525646
3 changed files with 334 additions and 0 deletions
|
|
@ -150,6 +150,8 @@ public class NetlinkMessage {
|
|||
return (NetlinkMessage) RtNetlinkNeighborMessage.parse(nlmsghdr, byteBuffer);
|
||||
case NetlinkConstants.RTM_NEWNDUSEROPT:
|
||||
return (NetlinkMessage) NduseroptMessage.parse(nlmsghdr, byteBuffer);
|
||||
case NetlinkConstants.RTM_NEWPREFIX:
|
||||
return (NetlinkMessage) RtNetlinkPrefixMessage.parse(nlmsghdr, byteBuffer);
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* Copyright (C) 2024 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 android.net.IpPrefix;
|
||||
import android.system.OsConstants;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import java.net.Inet6Address;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* A NetlinkMessage subclass for rtnetlink address messages.
|
||||
*
|
||||
* RtNetlinkPrefixMessage.parse() must be called with a ByteBuffer that contains exactly one
|
||||
* netlink message.
|
||||
*
|
||||
* see also:
|
||||
*
|
||||
* include/uapi/linux/rtnetlink.h
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class RtNetlinkPrefixMessage extends NetlinkMessage {
|
||||
public static final short PREFIX_ADDRESS = 1;
|
||||
public static final short PREFIX_CACHEINFO = 2;
|
||||
|
||||
@NonNull
|
||||
private StructPrefixMsg mPrefixmsg;
|
||||
@NonNull
|
||||
private IpPrefix mPrefix;
|
||||
private long mPreferredLifetime;
|
||||
private long mValidLifetime;
|
||||
|
||||
@VisibleForTesting
|
||||
public RtNetlinkPrefixMessage(@NonNull final StructNlMsgHdr header,
|
||||
@NonNull final StructPrefixMsg prefixmsg,
|
||||
@NonNull final IpPrefix prefix,
|
||||
long preferred, long valid) {
|
||||
super(header);
|
||||
mPrefixmsg = prefixmsg;
|
||||
mPrefix = prefix;
|
||||
mPreferredLifetime = preferred;
|
||||
mValidLifetime = valid;
|
||||
}
|
||||
|
||||
private RtNetlinkPrefixMessage(@NonNull StructNlMsgHdr header) {
|
||||
this(header, null, null, 0 /* preferredLifetime */, 0 /* validLifetime */);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public StructPrefixMsg getPrefixMsg() {
|
||||
return mPrefixmsg;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public IpPrefix getPrefix() {
|
||||
return mPrefix;
|
||||
}
|
||||
|
||||
public long getPreferredLifetime() {
|
||||
return mPreferredLifetime;
|
||||
}
|
||||
|
||||
public long getValidLifetime() {
|
||||
return mValidLifetime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse rtnetlink prefix message from {@link ByteBuffer}. This method must be called with a
|
||||
* ByteBuffer that contains exactly one netlink message.
|
||||
*
|
||||
* RTM_NEWPREFIX Message Format:
|
||||
* +----------+- - -+-------------+- - -+---------------------+-----------------------+
|
||||
* | nlmsghdr | Pad | prefixmsg | Pad | PREFIX_ADDRESS attr | PREFIX_CACHEINFO attr |
|
||||
* +----------+- - -+-------------+- - -+---------------------+-----------------------+
|
||||
*
|
||||
* @param header netlink message header.
|
||||
* @param byteBuffer the ByteBuffer instance that wraps the raw netlink message bytes.
|
||||
*/
|
||||
@Nullable
|
||||
public static RtNetlinkPrefixMessage parse(@NonNull final StructNlMsgHdr header,
|
||||
@NonNull final ByteBuffer byteBuffer) {
|
||||
try {
|
||||
final RtNetlinkPrefixMessage msg = new RtNetlinkPrefixMessage(header);
|
||||
msg.mPrefixmsg = StructPrefixMsg.parse(byteBuffer);
|
||||
|
||||
// PREFIX_ADDRESS
|
||||
final int baseOffset = byteBuffer.position();
|
||||
StructNlAttr nlAttr = StructNlAttr.findNextAttrOfType(PREFIX_ADDRESS, byteBuffer);
|
||||
if (nlAttr == null) return null;
|
||||
final Inet6Address addr = (Inet6Address) nlAttr.getValueAsInetAddress();
|
||||
if (addr == null) return null;
|
||||
msg.mPrefix = new IpPrefix(addr, msg.mPrefixmsg.prefix_len);
|
||||
|
||||
// PREFIX_CACHEINFO
|
||||
byteBuffer.position(baseOffset);
|
||||
nlAttr = StructNlAttr.findNextAttrOfType(PREFIX_CACHEINFO, byteBuffer);
|
||||
if (nlAttr == null) return null;
|
||||
final ByteBuffer buffer = nlAttr.getValueAsByteBuffer();
|
||||
if (buffer == null) return null;
|
||||
final StructPrefixCacheInfo cacheinfo = StructPrefixCacheInfo.parse(buffer);
|
||||
msg.mPreferredLifetime = cacheinfo.preferred_time;
|
||||
msg.mValidLifetime = cacheinfo.valid_time;
|
||||
|
||||
return msg;
|
||||
} catch (IllegalArgumentException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a rtnetlink prefix message to {@link ByteBuffer}.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
protected void pack(ByteBuffer byteBuffer) {
|
||||
getHeader().pack(byteBuffer);
|
||||
mPrefixmsg.pack(byteBuffer);
|
||||
|
||||
// PREFIX_ADDRESS attribute
|
||||
final StructNlAttr prefixAddress =
|
||||
new StructNlAttr(PREFIX_ADDRESS, mPrefix.getRawAddress());
|
||||
prefixAddress.pack(byteBuffer);
|
||||
|
||||
// PREFIX_CACHEINFO attribute
|
||||
final StructPrefixCacheInfo cacheinfo =
|
||||
new StructPrefixCacheInfo(mPreferredLifetime, mValidLifetime);
|
||||
final StructNlAttr prefixCacheinfo =
|
||||
new StructNlAttr(PREFIX_CACHEINFO, cacheinfo.writeToBytes());
|
||||
prefixCacheinfo.pack(byteBuffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RtNetlinkPrefixMessage{ "
|
||||
+ "nlmsghdr{" + mHeader.toString(OsConstants.NETLINK_ROUTE) + "}, "
|
||||
+ "prefixmsg{" + mPrefixmsg.toString() + "}, "
|
||||
+ "IP Prefix{" + mPrefix + "}, "
|
||||
+ "preferred lifetime{" + mPreferredLifetime + "}, "
|
||||
+ "valid lifetime{" + mValidLifetime + "} "
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* Copyright (C) 2024 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.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.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class RtNetlinkPrefixMessageTest {
|
||||
private static final IpPrefix TEST_PREFIX = new IpPrefix("2001:db8:1:1::/64");
|
||||
|
||||
// An example of the full RTM_NEWPREFIX message.
|
||||
private static final String RTM_NEWPREFIX_HEX =
|
||||
"3C000000340000000000000000000000" // struct nlmsghr
|
||||
+ "0A0000002F00000003400300" // struct prefixmsg
|
||||
+ "1400010020010DB8000100010000000000000000" // PREFIX_ADDRESS
|
||||
+ "0C000200803A0900008D2700"; // PREFIX_CACHEINFO
|
||||
|
||||
private ByteBuffer toByteBuffer(final String hexString) {
|
||||
return ByteBuffer.wrap(HexDump.hexStringToByteArray(hexString));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseRtmNewPrefix() {
|
||||
final ByteBuffer byteBuffer = toByteBuffer(RTM_NEWPREFIX_HEX);
|
||||
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer, NETLINK_ROUTE);
|
||||
assertNotNull(msg);
|
||||
assertTrue(msg instanceof RtNetlinkPrefixMessage);
|
||||
final RtNetlinkPrefixMessage prefixmsg = (RtNetlinkPrefixMessage) msg;
|
||||
|
||||
final StructNlMsgHdr hdr = prefixmsg.getHeader();
|
||||
assertNotNull(hdr);
|
||||
assertEquals(60, hdr.nlmsg_len);
|
||||
assertEquals(NetlinkConstants.RTM_NEWPREFIX, hdr.nlmsg_type);
|
||||
assertEquals(0, hdr.nlmsg_flags);
|
||||
assertEquals(0, hdr.nlmsg_seq);
|
||||
assertEquals(0, hdr.nlmsg_pid);
|
||||
|
||||
final StructPrefixMsg prefixmsgHdr = prefixmsg.getPrefixMsg();
|
||||
assertNotNull(prefixmsgHdr);
|
||||
assertEquals((byte) OsConstants.AF_INET6, prefixmsgHdr.prefix_family);
|
||||
assertEquals(3, prefixmsgHdr.prefix_type);
|
||||
assertEquals(64, prefixmsgHdr.prefix_len);
|
||||
assertEquals(0x03, prefixmsgHdr.prefix_flags);
|
||||
assertEquals(0x2F, prefixmsgHdr.prefix_ifindex);
|
||||
|
||||
assertEquals(prefixmsg.getPrefix(), TEST_PREFIX);
|
||||
assertEquals(604800L, prefixmsg.getPreferredLifetime());
|
||||
assertEquals(2592000L, prefixmsg.getValidLifetime());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPackRtmNewPrefix() {
|
||||
final ByteBuffer byteBuffer = toByteBuffer(RTM_NEWPREFIX_HEX);
|
||||
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer, NETLINK_ROUTE);
|
||||
assertNotNull(msg);
|
||||
assertTrue(msg instanceof RtNetlinkPrefixMessage);
|
||||
final RtNetlinkPrefixMessage prefixmsg = (RtNetlinkPrefixMessage) msg;
|
||||
|
||||
final ByteBuffer packBuffer = ByteBuffer.allocate(60);
|
||||
packBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
prefixmsg.pack(packBuffer);
|
||||
assertEquals(RTM_NEWPREFIX_HEX, HexDump.toHexString(packBuffer.array()));
|
||||
}
|
||||
|
||||
private static final String RTM_NEWPREFIX_WITHOUT_PREFIX_ADDRESS_HEX =
|
||||
"24000000340000000000000000000000" // struct nlmsghr
|
||||
+ "0A0000002F00000003400300" // struct prefixmsg
|
||||
+ "0C000200803A0900008D2700"; // PREFIX_CACHEINFO
|
||||
|
||||
@Test
|
||||
public void testParseRtmNewPrefix_withoutPrefixAddressAttribute() {
|
||||
final ByteBuffer byteBuffer = toByteBuffer(RTM_NEWPREFIX_WITHOUT_PREFIX_ADDRESS_HEX);
|
||||
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer, NETLINK_ROUTE);
|
||||
assertNull(msg);
|
||||
}
|
||||
|
||||
private static final String RTM_NEWPREFIX_WITHOUT_PREFIX_CACHEINFO_HEX =
|
||||
"30000000340000000000000000000000" // struct nlmsghr
|
||||
+ "0A0000002F00000003400300" // struct prefixmsg
|
||||
+ "140001002A0079E10ABCF6050000000000000000"; // PREFIX_ADDRESS
|
||||
|
||||
@Test
|
||||
public void testParseRtmNewPrefix_withoutPrefixCacheinfoAttribute() {
|
||||
final ByteBuffer byteBuffer = toByteBuffer(RTM_NEWPREFIX_WITHOUT_PREFIX_CACHEINFO_HEX);
|
||||
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer, NETLINK_ROUTE);
|
||||
assertNull(msg);
|
||||
}
|
||||
|
||||
private static final String RTM_NEWPREFIX_TRUNCATED_PREFIX_ADDRESS_HEX =
|
||||
"3C000000340000000000000000000000" // struct nlmsghr
|
||||
+ "0A0000002F00000003400300" // struct prefixmsg
|
||||
+ "140001002A0079E10ABCF605000000000000" // PREFIX_ADDRESS (truncated)
|
||||
+ "0C000200803A0900008D2700"; // PREFIX_CACHEINFO
|
||||
|
||||
@Test
|
||||
public void testParseRtmNewPrefix_truncatedPrefixAddressAttribute() {
|
||||
final ByteBuffer byteBuffer = toByteBuffer(RTM_NEWPREFIX_TRUNCATED_PREFIX_ADDRESS_HEX);
|
||||
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer, NETLINK_ROUTE);
|
||||
assertNull(msg);
|
||||
}
|
||||
|
||||
private static final String RTM_NEWPREFIX_TRUNCATED_PREFIX_CACHEINFO_HEX =
|
||||
"3C000000340000000000000000000000" // struct nlmsghr
|
||||
+ "0A0000002F00000003400300" // struct prefixmsg
|
||||
+ "140001002A0079E10ABCF6050000000000000000" // PREFIX_ADDRESS
|
||||
+ "0C000200803A0900008D"; // PREFIX_CACHEINFO (truncated)
|
||||
|
||||
@Test
|
||||
public void testParseRtmNewPrefix_truncatedPrefixCacheinfoAttribute() {
|
||||
final ByteBuffer byteBuffer = toByteBuffer(RTM_NEWPREFIX_TRUNCATED_PREFIX_CACHEINFO_HEX);
|
||||
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer, NETLINK_ROUTE);
|
||||
assertNull(msg);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToString() {
|
||||
final ByteBuffer byteBuffer = toByteBuffer(RTM_NEWPREFIX_HEX);
|
||||
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer, NETLINK_ROUTE);
|
||||
assertNotNull(msg);
|
||||
assertTrue(msg instanceof RtNetlinkPrefixMessage);
|
||||
final RtNetlinkPrefixMessage prefixmsg = (RtNetlinkPrefixMessage) msg;
|
||||
final String expected = "RtNetlinkPrefixMessage{ "
|
||||
+ "nlmsghdr{StructNlMsgHdr{ nlmsg_len{60}, nlmsg_type{52(RTM_NEWPREFIX)}, "
|
||||
+ "nlmsg_flags{0()}, nlmsg_seq{0}, nlmsg_pid{0} }}, "
|
||||
+ "prefixmsg{prefix_family: 10, prefix_ifindex: 47, prefix_type: 3, "
|
||||
+ "prefix_len: 64, prefix_flags: 3}, "
|
||||
+ "IP Prefix{2001:db8:1:1::/64}, "
|
||||
+ "preferred lifetime{604800}, valid lifetime{2592000} }";
|
||||
assertEquals(expected, prefixmsg.toString());
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue