Merge "Add RtmNetlinkPrefixMessage class to represent RTM_NEWPREFIX message." into main

This commit is contained in:
Xiao Ma 2024-12-04 04:41:43 +00:00 • committed by Gerrit Code Review
commit 7dd2525646
3 changed files with 334 additions and 0 deletions

View file

@ -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;
}
}

View file

@ -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 + "} "
+ "}";
}
}

View file

@ -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());
}
}