[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
diff --git a/staticlibs/device/com/android/net/module/util/netlink/xfrm/StructXfrmReplayStateEsn.java b/staticlibs/device/com/android/net/module/util/netlink/xfrm/StructXfrmReplayStateEsn.java
new file mode 100644
index 0000000..01dc66e
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/netlink/xfrm/StructXfrmReplayStateEsn.java
@@ -0,0 +1,203 @@
+/*
+ * 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;
+ }
+ }
+}
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 14a3a0f..72d02d4 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
@@ -44,6 +44,9 @@
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 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:
diff --git a/staticlibs/device/com/android/net/module/util/netlink/xfrm/XfrmNetlinkNewSaMessage.java b/staticlibs/device/com/android/net/module/util/netlink/xfrm/XfrmNetlinkNewSaMessage.java
new file mode 100644
index 0000000..2f374ba
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/netlink/xfrm/XfrmNetlinkNewSaMessage.java
@@ -0,0 +1,157 @@
+/*
+ * 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: <linux_src>/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;
+ }
+}
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/StructXfrmReplayStateEsnTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/StructXfrmReplayStateEsnTest.java
new file mode 100644
index 0000000..1eb968d
--- /dev/null
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/StructXfrmReplayStateEsnTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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
+}
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/XfrmNetlinkNewSaMessageTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/XfrmNetlinkNewSaMessageTest.java
new file mode 100644
index 0000000..3d0ce2c
--- /dev/null
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/XfrmNetlinkNewSaMessageTest.java
@@ -0,0 +1,150 @@
+/*
+ * 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());
+ }
+}