From c5e280590abcf376903ab6fb3da6e02d6784aecb Mon Sep 17 00:00:00 2001 From: Yan Yan <evitayan@google.com> Date: Fri, 10 Nov 2023 02:24:29 +0000 Subject: [PATCH] [XFRM_MSG_GETSA] Support XFRM_MSG_GETSA message Bug: 308011229 Test: atest NetworkStaticLibTests:com.android.net.moduletests.util.netlink (new tests added) Change-Id: I42e423c09ffb733a809636360468c963c7864872 --- .../module/util/netlink/NetlinkMessage.java | 22 ++++ .../module/util/netlink/StructNlMsgHdr.java | 8 ++ .../netlink/xfrm/XfrmNetlinkGetSaMessage.java | 116 ++++++++++++++++++ .../util/netlink/xfrm/XfrmNetlinkMessage.java | 40 +++++- .../netlink/xfrm/StructXfrmUsersaIdTest.java | 2 +- .../xfrm/XfrmNetlinkGetSaMessageTest.java | 75 +++++++++++ 6 files changed, 260 insertions(+), 3 deletions(-) create mode 100644 staticlibs/device/com/android/net/module/util/netlink/xfrm/XfrmNetlinkGetSaMessage.java create mode 100644 staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/XfrmNetlinkGetSaMessageTest.java diff --git a/staticlibs/device/com/android/net/module/util/netlink/NetlinkMessage.java b/staticlibs/device/com/android/net/module/util/netlink/NetlinkMessage.java index 9e1e26e760..111e0bae9a 100644 --- a/staticlibs/device/com/android/net/module/util/netlink/NetlinkMessage.java +++ b/staticlibs/device/com/android/net/module/util/netlink/NetlinkMessage.java @@ -16,12 +16,17 @@ package com.android.net.module.util.netlink; +import static com.android.net.module.util.netlink.xfrm.XfrmNetlinkMessage.NETLINK_XFRM; + import android.system.OsConstants; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.net.module.util.netlink.xfrm.XfrmNetlinkMessage; + import java.nio.ByteBuffer; +import java.nio.ByteOrder; /** * NetlinkMessage base class for other, more specific netlink message types. @@ -75,6 +80,8 @@ public class NetlinkMessage { parsed = parseInetDiagMessage(nlmsghdr, byteBuffer); } else if (nlFamily == OsConstants.NETLINK_NETFILTER) { parsed = parseNfMessage(nlmsghdr, byteBuffer); + } else if (nlFamily == NETLINK_XFRM) { + parsed = parseXfrmMessage(nlmsghdr, byteBuffer); } else { parsed = null; } @@ -168,4 +175,19 @@ public class NetlinkMessage { default: return null; } } + + @Nullable + private static NetlinkMessage parseXfrmMessage( + @NonNull final StructNlMsgHdr nlmsghdr, @NonNull final ByteBuffer byteBuffer) { + return (NetlinkMessage) XfrmNetlinkMessage.parseXfrmInternal(nlmsghdr, byteBuffer); + } + + /** A convenient method to create a ByteBuffer for encoding a new message */ + protected static ByteBuffer newNlMsgByteBuffer(int payloadLen) { + final int length = StructNlMsgHdr.STRUCT_SIZE + payloadLen; + final ByteBuffer byteBuffer = ByteBuffer.allocate(length); + byteBuffer.order(ByteOrder.nativeOrder()); + + return byteBuffer; + } } diff --git a/staticlibs/device/com/android/net/module/util/netlink/StructNlMsgHdr.java b/staticlibs/device/com/android/net/module/util/netlink/StructNlMsgHdr.java index 5052cb8a06..ff37639283 100644 --- a/staticlibs/device/com/android/net/module/util/netlink/StructNlMsgHdr.java +++ b/staticlibs/device/com/android/net/module/util/netlink/StructNlMsgHdr.java @@ -128,6 +128,14 @@ public class StructNlMsgHdr { nlmsg_pid = 0; } + public StructNlMsgHdr(int payloadLen, short type, short flags, int seq) { + nlmsg_len = StructNlMsgHdr.STRUCT_SIZE + payloadLen; + nlmsg_type = type; + nlmsg_flags = flags; + nlmsg_seq = seq; + nlmsg_pid = 0; + } + /** * Write netlink message header to ByteBuffer. */ diff --git a/staticlibs/device/com/android/net/module/util/netlink/xfrm/XfrmNetlinkGetSaMessage.java b/staticlibs/device/com/android/net/module/util/netlink/xfrm/XfrmNetlinkGetSaMessage.java new file mode 100644 index 0000000000..680a7ca89e --- /dev/null +++ b/staticlibs/device/com/android/net/module/util/netlink/xfrm/XfrmNetlinkGetSaMessage.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2023 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.xfrm; + +import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REQUEST; +import static com.android.net.module.util.netlink.xfrm.XfrmNetlinkMessage.XFRM_MSG_GETSA; + +import android.system.OsConstants; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.net.module.util.Struct; +import com.android.net.module.util.netlink.StructNlMsgHdr; + +import java.net.Inet4Address; +import java.net.InetAddress; +import java.nio.ByteBuffer; + +/** + * An XfrmNetlinkMessage subclass for XFRM_MSG_GETSA messages. + * + * <p>see include/uapi/linux/xfrm.h + * + * <p>XFRM_MSG_GETSA syntax + * + * <ul> + * <li>TLV: xfrm_usersa_id + * <li>Optional Attributes: XFRMA_MARK, XFRMA_SRCADDR + * </ul> + * + * @hide + */ +public class XfrmNetlinkGetSaMessage extends XfrmNetlinkMessage { + @NonNull private final StructXfrmUsersaId mXfrmUsersaId; + + private XfrmNetlinkGetSaMessage( + @NonNull final StructNlMsgHdr header, @NonNull final StructXfrmUsersaId xfrmUsersaId) { + super(header); + mXfrmUsersaId = xfrmUsersaId; + } + + private XfrmNetlinkGetSaMessage( + @NonNull final StructNlMsgHdr header, + @NonNull final InetAddress destAddress, + long spi, + short proto) { + super(header); + + final int family = + destAddress instanceof Inet4Address ? OsConstants.AF_INET : OsConstants.AF_INET6; + mXfrmUsersaId = new StructXfrmUsersaId(destAddress, spi, family, proto); + } + + @Override + protected void packPayload(@NonNull final ByteBuffer byteBuffer) { + mXfrmUsersaId.writeToByteBuffer(byteBuffer); + } + + /** + * Parse XFRM_MSG_GETSA message from ByteBuffer. + * + * <p>This method should be called from NetlinkMessage#parse(ByteBuffer, int) for generic + * message validation and processing + * + * @param nlmsghdr netlink message header. + * @param byteBuffer the ByteBuffer instance that wraps the raw netlink message bytes. MUST be + * host order + */ + @Nullable + static XfrmNetlinkGetSaMessage parseInternal( + @NonNull final StructNlMsgHdr nlmsghdr, @NonNull final ByteBuffer byteBuffer) { + final StructXfrmUsersaId xfrmUsersaId = Struct.parse(StructXfrmUsersaId.class, byteBuffer); + if (xfrmUsersaId == null) { + return null; + } + + // Attributes not supported. Don't bother handling them. + + return new XfrmNetlinkGetSaMessage(nlmsghdr, xfrmUsersaId); + } + + /** A convenient method to create a XFRM_MSG_GETSA message. */ + public static byte[] newXfrmNetlinkGetSaMessage( + @NonNull final InetAddress destAddress, long spi, short proto) { + final int payloadLen = StructXfrmUsersaId.STRUCT_SIZE; + + final StructNlMsgHdr nlmsghdr = + new StructNlMsgHdr(payloadLen, XFRM_MSG_GETSA, NLM_F_REQUEST, 0); + final XfrmNetlinkGetSaMessage message = + new XfrmNetlinkGetSaMessage(nlmsghdr, destAddress, spi, proto); + + final ByteBuffer byteBuffer = newNlMsgByteBuffer(payloadLen); + message.pack(byteBuffer); + + return byteBuffer.array(); + } + + public StructXfrmUsersaId getStructXfrmUsersaId() { + return mXfrmUsersaId; + } +} diff --git a/staticlibs/device/com/android/net/module/util/netlink/xfrm/XfrmNetlinkMessage.java b/staticlibs/device/com/android/net/module/util/netlink/xfrm/XfrmNetlinkMessage.java index ee34e5716c..e15342b061 100644 --- a/staticlibs/device/com/android/net/module/util/netlink/xfrm/XfrmNetlinkMessage.java +++ b/staticlibs/device/com/android/net/module/util/netlink/xfrm/XfrmNetlinkMessage.java @@ -17,10 +17,13 @@ package com.android.net.module.util.netlink.xfrm; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.android.net.module.util.netlink.NetlinkMessage; import com.android.net.module.util.netlink.StructNlMsgHdr; +import java.nio.ByteBuffer; + /** Base calss for XFRM netlink messages */ // Developer notes: The Linux kernel includes a number of XFRM structs that are not standard netlink // attributes (e.g., xfrm_usersa_id). These structs are unlikely to change size, so this XFRM @@ -28,12 +31,45 @@ import com.android.net.module.util.netlink.StructNlMsgHdr; // struct size changes, it should be caught by CTS and then developers should add // kernel-version-based behvaiours. public abstract class XfrmNetlinkMessage extends NetlinkMessage { - // TODO: STOPSHIP: b/308011229 Remove it when OsConstants.IPPROTO_ESP is exposed + // TODO: b/312498032 Remove it when OsConstants.IPPROTO_ESP is stable public static final int IPPROTO_ESP = 50; + // TODO: b/312498032 Remove it when OsConstants.NETLINK_XFRM is stable + public static final int NETLINK_XFRM = 6; + + /* see include/uapi/linux/xfrm.h */ + public static final short XFRM_MSG_NEWSA = 16; + public static final short XFRM_MSG_GETSA = 18; public XfrmNetlinkMessage(@NonNull final StructNlMsgHdr header) { super(header); } - // TODO: Add the support for parsing messages + /** + * Parse XFRM message from ByteBuffer. + * + * <p>This method should be called from NetlinkMessage#parse(ByteBuffer, int) for generic + * message validation and processing + * + * @param nlmsghdr netlink message header. + * @param byteBuffer the ByteBuffer instance that wraps the raw netlink message bytes. MUST be + * host order + */ + @Nullable + public static XfrmNetlinkMessage parseXfrmInternal( + @NonNull final StructNlMsgHdr nlmsghdr, @NonNull final ByteBuffer byteBuffer) { + switch (nlmsghdr.nlmsg_type) { + case XFRM_MSG_GETSA: + return XfrmNetlinkGetSaMessage.parseInternal(nlmsghdr, byteBuffer); + default: + return null; + } + } + + protected abstract void packPayload(@NonNull final ByteBuffer byteBuffer); + + /** Write a XFRM message to {@link ByteBuffer}. */ + public void pack(@NonNull final ByteBuffer byteBuffer) { + getHeader().pack(byteBuffer); + packPayload(byteBuffer); + } } diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/StructXfrmUsersaIdTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/StructXfrmUsersaIdTest.java index 52fd591247..b659f62a92 100644 --- a/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/StructXfrmUsersaIdTest.java +++ b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/StructXfrmUsersaIdTest.java @@ -52,7 +52,7 @@ public class StructXfrmUsersaIdTest { public void testEncode() throws Exception { final StructXfrmUsersaId struct = new StructXfrmUsersaId(DEST_ADDRESS, SPI, FAMILY, PROTO); - ByteBuffer buffer = ByteBuffer.allocate(EXPECTED_HEX.length); + final ByteBuffer buffer = ByteBuffer.allocate(EXPECTED_HEX.length); buffer.order(ByteOrder.nativeOrder()); struct.writeToByteBuffer(buffer); diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/XfrmNetlinkGetSaMessageTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/XfrmNetlinkGetSaMessageTest.java new file mode 100644 index 0000000000..0ab36e7604 --- /dev/null +++ b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/XfrmNetlinkGetSaMessageTest.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2023 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.xfrm; + +import static com.android.net.module.util.netlink.xfrm.XfrmNetlinkMessage.IPPROTO_ESP; +import static com.android.net.module.util.netlink.xfrm.XfrmNetlinkMessage.NETLINK_XFRM; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +import android.net.InetAddresses; +import android.system.OsConstants; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.net.module.util.HexDump; +import com.android.net.module.util.netlink.NetlinkMessage; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.net.InetAddress; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class XfrmNetlinkGetSaMessageTest { + private static final String EXPECTED_HEX_STRING = + "28000000120001000000000000000000" + + "C0000201000000000000000000000000" + + "7768440002003200"; + private static final byte[] EXPECTED_HEX = HexDump.hexStringToByteArray(EXPECTED_HEX_STRING); + private static final InetAddress DEST_ADDRESS = InetAddresses.parseNumericAddress("192.0.2.1"); + private static final long SPI = 0x77684400; + private static final int FAMILY = OsConstants.AF_INET; + private static final short PROTO = IPPROTO_ESP; + + @Test + public void testEncode() throws Exception { + final byte[] result = + XfrmNetlinkGetSaMessage.newXfrmNetlinkGetSaMessage(DEST_ADDRESS, SPI, PROTO); + assertArrayEquals(EXPECTED_HEX, result); + } + + @Test + public void testDecode() throws Exception { + final ByteBuffer buffer = ByteBuffer.wrap(EXPECTED_HEX); + buffer.order(ByteOrder.nativeOrder()); + final XfrmNetlinkGetSaMessage message = + (XfrmNetlinkGetSaMessage) NetlinkMessage.parse(buffer, NETLINK_XFRM); + final StructXfrmUsersaId struct = message.getStructXfrmUsersaId(); + + assertEquals(DEST_ADDRESS, struct.getDestAddress()); + assertEquals(SPI, struct.spi); + assertEquals(FAMILY, struct.family); + assertEquals(PROTO, struct.proto); + assertEquals(0, buffer.remaining()); + } +} -- GitLab