From 0a892e6ec28f23827886dceff0e4997c10a1dd54 Mon Sep 17 00:00:00 2001 From: Yan Yan Date: Wed, 15 Nov 2023 19:27:50 +0000 Subject: [PATCH] [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 --- .../xfrm/StructXfrmReplayStateEsn.java | 203 ++++++++++++++++++ .../util/netlink/xfrm/XfrmNetlinkMessage.java | 5 + .../netlink/xfrm/XfrmNetlinkNewSaMessage.java | 157 ++++++++++++++ .../xfrm/StructXfrmReplayStateEsnTest.java | 114 ++++++++++ .../xfrm/XfrmNetlinkNewSaMessageTest.java | 150 +++++++++++++ 5 files changed, 629 insertions(+) create mode 100644 staticlibs/device/com/android/net/module/util/netlink/xfrm/StructXfrmReplayStateEsn.java create mode 100644 staticlibs/device/com/android/net/module/util/netlink/xfrm/XfrmNetlinkNewSaMessage.java create mode 100644 staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/StructXfrmReplayStateEsnTest.java create mode 100644 staticlibs/tests/unit/src/com/android/net/module/util/netlink/xfrm/XfrmNetlinkNewSaMessageTest.java 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 0000000000..01dc66e168 --- /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 + * + *

see include/uapi/linux/xfrm.h + * + *

+ * struct xfrm_replay_state_esn {
+ *      unsigned int bmp_len;
+ *      __u32 oseq;
+ *      __u32 seq;
+ *      __u32 oseq_hi;
+ *      __u32 seq_hi;
+ *      __u32 replay_window;
+ *      __u32 bmp[];
+ * };
+ * 
+ * + * @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 14a3a0f6e8..72d02d4066 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 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: 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 0000000000..2f374bac26 --- /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. + * + *

see also: <linux_src>/include/uapi/linux/xfrm.h + * + *

XFRM_MSG_NEWSA syntax + * + *

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

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 0000000000..1eb968d86d --- /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 0000000000..3d0ce2cd55 --- /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()); + } +}