Skip to content
Snippets Groups Projects
Commit 0a892e6e authored by Yan Yan's avatar Yan Yan
Browse files

[XFRM_MSG_NEWSA] Support xfrm_replay_state_esn and XFRM_MSG_NEWSA

Bug: 308011229
Test: atest NetworkStaticLibTests:com.android.net.moduletests.util.netlink
      (new tests added)
Change-Id: I4c21a64c634b1814d52ce81a48abe2550876b755
parent c9a194c4
No related branches found
No related tags found
No related merge requests found
/*
* 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 androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.net.module.util.Struct;
import com.android.net.module.util.Struct.Field;
import com.android.net.module.util.Struct.Type;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
* Struct xfrm_replay_state_esn
*
* <p>see include/uapi/linux/xfrm.h
*
* <pre>
* struct xfrm_replay_state_esn {
* unsigned int bmp_len;
* __u32 oseq;
* __u32 seq;
* __u32 oseq_hi;
* __u32 seq_hi;
* __u32 replay_window;
* __u32 bmp[];
* };
* </pre>
*
* @hide
*/
public class StructXfrmReplayStateEsn {
// include/uapi/linux/xfrm.h XFRMA_REPLAY_ESN_MAX
private static final int XFRMA_REPLAY_ESN_BMP_LEN_MAX = 128;
@NonNull private final StructXfrmReplayStateEsnWithoutBitmap mWithoutBitmap;
@NonNull private final byte[] mBitmap;
private StructXfrmReplayStateEsn(
@NonNull final StructXfrmReplayStateEsnWithoutBitmap withoutBitmap,
@NonNull final byte[] bitmap) {
mWithoutBitmap = withoutBitmap;
mBitmap = bitmap;
validate();
}
/** Constructor to build a new message */
public StructXfrmReplayStateEsn(
long bmpLen,
long oSeq,
long seq,
long oSeqHi,
long seqHi,
long replayWindow,
@NonNull final byte[] bitmap) {
mWithoutBitmap =
new StructXfrmReplayStateEsnWithoutBitmap(
bmpLen, oSeq, seq, oSeqHi, seqHi, replayWindow);
mBitmap = bitmap.clone();
validate();
}
private void validate() {
if (mWithoutBitmap.mBmpLenInBytes != mBitmap.length) {
throw new IllegalArgumentException(
"mWithoutBitmap.mBmpLenInBytes not aligned with bitmap."
+ " mWithoutBitmap.mBmpLenInBytes: "
+ mWithoutBitmap.mBmpLenInBytes
+ " bitmap.length "
+ mBitmap.length);
}
}
/** Parse IpSecStructXfrmReplayStateEsn from ByteBuffer. */
@Nullable
public static StructXfrmReplayStateEsn parse(@NonNull final ByteBuffer buf) {
final StructXfrmReplayStateEsnWithoutBitmap withoutBitmap =
Struct.parse(StructXfrmReplayStateEsnWithoutBitmap.class, buf);
if (withoutBitmap == null) {
return null;
}
final byte[] bitmap = new byte[withoutBitmap.mBmpLenInBytes];
buf.get(bitmap);
return new StructXfrmReplayStateEsn(withoutBitmap, bitmap);
}
/** Convert the parsed object to ByteBuffer. */
public void writeToByteBuffer(@NonNull final ByteBuffer buf) {
mWithoutBitmap.writeToByteBuffer(buf);
buf.put(mBitmap);
}
/** Return the struct size */
public int getStructSize() {
return StructXfrmReplayStateEsnWithoutBitmap.STRUCT_SIZE + mBitmap.length;
}
/** Return the bitmap */
public byte[] getBitmap() {
return mBitmap.clone();
}
/** Return the bmp_len */
public long getBmpLen() {
return mWithoutBitmap.bmpLen;
}
/** Return the replay_window */
public long getReplayWindow() {
return mWithoutBitmap.replayWindow;
}
/** Return the TX sequence number in unisgned long */
public long getTxSequenceNumber() {
return getSequenceNumber(mWithoutBitmap.seqHi, mWithoutBitmap.seq);
}
/** Return the RX sequence number in unisgned long */
public long getRxSequenceNumber() {
return getSequenceNumber(mWithoutBitmap.oSeqHi, mWithoutBitmap.oSeq);
}
@VisibleForTesting
static long getSequenceNumber(long hi, long low) {
final ByteBuffer buffer = ByteBuffer.allocate(8);
buffer.order(ByteOrder.BIG_ENDIAN);
buffer.putInt((int) hi).putInt((int) low);
buffer.rewind();
return buffer.getLong();
}
/** The xfrm_replay_state_esn struct without the bitmap */
// Because the size of the bitmap is decided at runtime, it cannot be included in a Struct
// subclass. Therefore, this nested class is defined to include all other fields supported by
// Struct for code reuse.
public static class StructXfrmReplayStateEsnWithoutBitmap extends Struct {
public static final int STRUCT_SIZE = 24;
@Field(order = 0, type = Type.U32)
public final long bmpLen; // replay bitmap length in 32-bit integers
@Field(order = 1, type = Type.U32)
public final long oSeq;
@Field(order = 2, type = Type.U32)
public final long seq;
@Field(order = 3, type = Type.U32)
public final long oSeqHi;
@Field(order = 4, type = Type.U32)
public final long seqHi;
@Field(order = 5, type = Type.U32)
public final long replayWindow; // replay bitmap length in bit
@Computed private final int mBmpLenInBytes; // replay bitmap length in bytes
public StructXfrmReplayStateEsnWithoutBitmap(
long bmpLen, long oSeq, long seq, long oSeqHi, long seqHi, long replayWindow) {
this.bmpLen = bmpLen;
this.oSeq = oSeq;
this.seq = seq;
this.oSeqHi = oSeqHi;
this.seqHi = seqHi;
this.replayWindow = replayWindow;
if (bmpLen > XFRMA_REPLAY_ESN_BMP_LEN_MAX) {
throw new IllegalArgumentException("Invalid bmpLen " + bmpLen);
}
if (bmpLen * 4 * 8 != replayWindow) {
throw new IllegalArgumentException(
"bmpLen not aligned with replayWindow. bmpLen: "
+ bmpLen
+ " replayWindow "
+ replayWindow);
}
mBmpLenInBytes = (int) bmpLen * 4;
}
}
}
......@@ -44,6 +44,9 @@ public abstract class XfrmNetlinkMessage extends NetlinkMessage {
public static final int XFRM_MODE_TRANSPORT = 0;
public static final int XFRM_MODE_TUNNEL = 1;
public static final short XFRMA_REPLAY_VAL = 10;
public static final short XFRMA_REPLAY_ESN_VAL = 23;
public static final BigInteger XFRM_INF = new BigInteger("FFFFFFFFFFFFFFFF", 16);
public XfrmNetlinkMessage(@NonNull final StructNlMsgHdr header) {
......@@ -64,6 +67,8 @@ public abstract class XfrmNetlinkMessage extends NetlinkMessage {
public static XfrmNetlinkMessage parseXfrmInternal(
@NonNull final StructNlMsgHdr nlmsghdr, @NonNull final ByteBuffer byteBuffer) {
switch (nlmsghdr.nlmsg_type) {
case XFRM_MSG_NEWSA:
return XfrmNetlinkNewSaMessage.parseInternal(nlmsghdr, byteBuffer);
case XFRM_MSG_GETSA:
return XfrmNetlinkGetSaMessage.parseInternal(nlmsghdr, byteBuffer);
default:
......
/*
* 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.XFRMA_REPLAY_ESN_VAL;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.net.module.util.Struct;
import com.android.net.module.util.netlink.StructNlAttr;
import com.android.net.module.util.netlink.StructNlMsgHdr;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
* A NetlinkMessage subclass for XFRM_MSG_NEWSA messages.
*
* <p>see also: &lt;linux_src&gt;/include/uapi/linux/xfrm.h
*
* <p>XFRM_MSG_NEWSA syntax
*
* <ul>
* <li>TLV: xfrm_usersa_info
* <li>Attributes: XFRMA_ALG_CRYPT, XFRMA_ALG_AUTH, XFRMA_OUTPUT_MARK, XFRMA_IF_ID,
* XFRMA_REPLAY_ESN_VAL,XFRMA_REPLAY_VAL
* </ul>
*
* @hide
*/
public class XfrmNetlinkNewSaMessage extends XfrmNetlinkMessage {
private static final String TAG = XfrmNetlinkNewSaMessage.class.getSimpleName();
@NonNull private final StructXfrmUsersaInfo mXfrmUsersaInfo;
@NonNull private final StructXfrmReplayStateEsn mXfrmReplayStateEsn;
private XfrmNetlinkNewSaMessage(
@NonNull final StructNlMsgHdr header,
@NonNull final StructXfrmUsersaInfo xfrmUsersaInfo,
@NonNull final StructXfrmReplayStateEsn xfrmReplayStateEsn) {
super(header);
mXfrmUsersaInfo = xfrmUsersaInfo;
mXfrmReplayStateEsn = xfrmReplayStateEsn;
}
@Override
protected void packPayload(@NonNull final ByteBuffer byteBuffer) {
mXfrmUsersaInfo.writeToByteBuffer(byteBuffer);
if (mXfrmReplayStateEsn != null) {
mXfrmReplayStateEsn.writeToByteBuffer(byteBuffer);
}
}
/**
* Parse XFRM_MSG_NEWSA 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 XfrmNetlinkNewSaMessage parseInternal(
@NonNull final StructNlMsgHdr nlmsghdr, @NonNull final ByteBuffer byteBuffer) {
final StructXfrmUsersaInfo xfrmUsersaInfo =
Struct.parse(StructXfrmUsersaInfo.class, byteBuffer);
if (xfrmUsersaInfo == null) {
Log.d(TAG, "parse: fail to parse xfrmUsersaInfo");
return null;
}
StructXfrmReplayStateEsn xfrmReplayStateEsn = null;
final int payloadLen = nlmsghdr.nlmsg_len - StructNlMsgHdr.STRUCT_SIZE;
int parsedLength = StructXfrmUsersaInfo.STRUCT_SIZE;
while (parsedLength < payloadLen) {
final StructNlAttr attr = StructNlAttr.parse(byteBuffer);
if (attr == null) {
Log.d(TAG, "parse: fail to parse netlink attributes");
return null;
}
final ByteBuffer attrValueBuff = ByteBuffer.wrap(attr.nla_value);
attrValueBuff.order(ByteOrder.nativeOrder());
if (attr.nla_type == XFRMA_REPLAY_ESN_VAL) {
xfrmReplayStateEsn = StructXfrmReplayStateEsn.parse(attrValueBuff);
}
parsedLength += attr.nla_len;
}
// TODO: Add the support of XFRMA_REPLAY_VAL
if (xfrmReplayStateEsn == null) {
Log.d(TAG, "parse: xfrmReplayStateEsn not found");
return null;
}
final XfrmNetlinkNewSaMessage msg =
new XfrmNetlinkNewSaMessage(nlmsghdr, xfrmUsersaInfo, xfrmReplayStateEsn);
return msg;
}
/** Return the TX sequence number in unisgned long */
public long getTxSequenceNumber() {
return mXfrmReplayStateEsn.getTxSequenceNumber();
}
/** Return the RX sequence number in unisgned long */
public long getRxSequenceNumber() {
return mXfrmReplayStateEsn.getRxSequenceNumber();
}
/** Return the bitmap */
public byte[] getBitmap() {
return mXfrmReplayStateEsn.getBitmap();
}
/** Return the packet count in unsigned long */
public long getPacketCount() {
// It is safe because "packets" is a 64-bit value
return mXfrmUsersaInfo.getCurrentLifetime().packets.longValue();
}
/** Return the byte count in unsigned long */
public long getByteCount() {
// It is safe because "bytes" is a 64-bit value
return mXfrmUsersaInfo.getCurrentLifetime().bytes.longValue();
}
/** Return the xfrm_usersa_info */
public StructXfrmUsersaInfo getXfrmUsersaInfo() {
return mXfrmUsersaInfo;
}
}
/*
* 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 org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
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 StructXfrmReplayStateEsnTest {
private static final String EXPECTED_HEX_STRING =
"80000000000000000000000000000000"
+ "00000000001000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "0000000000000000";
private static final byte[] EXPECTED_HEX = HexDump.hexStringToByteArray(EXPECTED_HEX_STRING);
private static final long BMP_LEN = 128;
private static final long REPLAY_WINDOW = 4096;
private static final byte[] BITMAP = new byte[512];
@Test
public void testEncode() throws Exception {
final StructXfrmReplayStateEsn struct =
new StructXfrmReplayStateEsn(BMP_LEN, 0L, 0L, 0L, 0L, REPLAY_WINDOW, BITMAP);
final ByteBuffer buffer = ByteBuffer.allocate(struct.getStructSize());
buffer.order(ByteOrder.nativeOrder());
struct.writeToByteBuffer(buffer);
assertArrayEquals(EXPECTED_HEX, buffer.array());
}
@Test
public void testDecode() throws Exception {
final ByteBuffer buffer = ByteBuffer.wrap(EXPECTED_HEX);
buffer.order(ByteOrder.nativeOrder());
final StructXfrmReplayStateEsn struct = StructXfrmReplayStateEsn.parse(buffer);
assertEquals(BMP_LEN, struct.getBmpLen());
assertEquals(REPLAY_WINDOW, struct.getReplayWindow());
assertArrayEquals(BITMAP, struct.getBitmap());
assertEquals(0L, struct.getRxSequenceNumber());
assertEquals(0L, struct.getTxSequenceNumber());
}
@Test
public void testGetSequenceNumber() throws Exception {
final long low = 0x00ab112233L;
final long hi = 0x01L;
assertEquals(0x01ab112233L, StructXfrmReplayStateEsn.getSequenceNumber(hi, low));
assertEquals(0xab11223300000001L, StructXfrmReplayStateEsn.getSequenceNumber(low, hi));
}
// TODO: Add test cases that the test bitmap is not all zeros
}
/*
* 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.NETLINK_XFRM;
import static com.android.net.module.util.netlink.xfrm.XfrmNetlinkMessage.XFRM_MODE_TRANSPORT;
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 XfrmNetlinkNewSaMessageTest {
private static final String EXPECTED_HEX_STRING =
"2004000010000000000000003FE1D4B6"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000A00000000000000"
+ "000000000000000020010DB800000000"
+ "0000000000000111AABBCCDD32000000"
+ "20010DB8000000000000000000000222"
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "FD464C65000000000000000000000000"
+ "00000000000000000000000000000000"
+ "024000000A0000000000000000000000"
+ "5C000100686D61632873686131290000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000A000000055F01AC07E15E437"
+ "115DDE0AEDD18A822BA9F81E60001400"
+ "686D6163287368613129000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "A00000006000000055F01AC07E15E437"
+ "115DDE0AEDD18A822BA9F81E58000200"
+ "63626328616573290000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "800000006AED4975ADF006D65C76F639"
+ "23A6265B1C0217008000000000000000"
+ "00000000000000000000000000100000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000";
private static final byte[] EXPECTED_HEX = HexDump.hexStringToByteArray(EXPECTED_HEX_STRING);
private static final InetAddress DEST_ADDRESS =
InetAddresses.parseNumericAddress("2001:db8::111");
private static final InetAddress SOURCE_ADDRESS =
InetAddresses.parseNumericAddress("2001:db8::222");
private static final int FAMILY = OsConstants.AF_INET6;
private static final long SPI = 0xaabbccddL;
private static final long SEQ = 0L;
private static final long REQ_ID = 16386L;
private static final short MODE = XFRM_MODE_TRANSPORT;
private static final short REPLAY_WINDOW_LEGACY = 0;
private static final short FLAGS = 0;
private static final byte[] BITMAP = new byte[512];
@Test
public void testDecode() throws Exception {
final ByteBuffer buffer = ByteBuffer.wrap(EXPECTED_HEX);
buffer.order(ByteOrder.nativeOrder());
final XfrmNetlinkNewSaMessage message =
(XfrmNetlinkNewSaMessage) NetlinkMessage.parse(buffer, NETLINK_XFRM);
final StructXfrmUsersaInfo xfrmUsersaInfo = message.getXfrmUsersaInfo();
assertEquals(DEST_ADDRESS, xfrmUsersaInfo.getDestAddress());
assertEquals(SOURCE_ADDRESS, xfrmUsersaInfo.getSrcAddress());
assertEquals(SPI, xfrmUsersaInfo.getSpi());
assertEquals(SEQ, xfrmUsersaInfo.seq);
assertEquals(REQ_ID, xfrmUsersaInfo.reqId);
assertEquals(FAMILY, xfrmUsersaInfo.family);
assertEquals(MODE, xfrmUsersaInfo.mode);
assertEquals(REPLAY_WINDOW_LEGACY, xfrmUsersaInfo.replayWindowLegacy);
assertEquals(FLAGS, xfrmUsersaInfo.flags);
assertArrayEquals(BITMAP, message.getBitmap());
assertEquals(0L, message.getRxSequenceNumber());
assertEquals(0L, message.getTxSequenceNumber());
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment