Merge "[XFRM_MSG_GETSA] Support XFRM_MSG_GETSA message" into main

This commit is contained in:
Yan Yan 2023-11-27 23:43:01 +00:00 • committed by Gerrit Code Review
commit 684f92b9fb
6 changed files with 260 additions and 3 deletions

View file

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

View file

@ -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.
*/

View file

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

View file

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

View file

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

View file

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