Merge "[XFRM_MSG_NEWSA] Support xfrm_stats and xfrm_usersa_info" into main

This commit is contained in:
Yan Yan 2023-12-05 20:26:33 +00:00 • committed by Gerrit Code Review
commit 4e6bd8f7d4
4 changed files with 406 additions and 0 deletions

View file

@ -0,0 +1,61 @@
/*
* 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 com.android.net.module.util.Struct;
/**
* Struct xfrm_lifetime_cur
*
* <p>see include/uapi/linux/xfrm.h
*
* <pre>
* struct xfrm_stats {
* __u32 replay_window;
* __u32 replay;
* __u32 integrity_failed;
* };
* </pre>
*
* @hide
*/
public class StructXfrmStats extends Struct {
public static final int STRUCT_SIZE = 12;
/** Number of packets that fall out of the replay window */
@Field(order = 0, type = Type.U32)
public final long replayWindow;
/** Number of replayed packets */
@Field(order = 1, type = Type.U32)
public final long replay;
/** Number of packets that failed authentication */
@Field(order = 2, type = Type.U32)
public final long integrityFailed;
// Constructor that allows Strutc.parse(Class<T>, ByteBuffer) to work
public StructXfrmStats(long replayWindow, long replay, long integrityFailed) {
this.replayWindow = replayWindow;
this.replay = replay;
this.integrityFailed = integrityFailed;
}
// Constructor to build a new message
public StructXfrmStats() {
this(0, 0, 0);
}
}

View file

@ -0,0 +1,215 @@
/*
* 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 android.system.OsConstants;
import androidx.annotation.NonNull;
import com.android.net.module.util.Struct;
import com.android.net.module.util.Struct.Computed;
import com.android.net.module.util.Struct.Field;
import com.android.net.module.util.Struct.Type;
import java.math.BigInteger;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
* Struct xfrm_usersa_info
*
* <p>see include/uapi/linux/xfrm.h
*
* <pre>
* struct xfrm_usersa_info {
* struct xfrm_selector sel;
* struct xfrm_id id;
* xfrm_address_t saddr;
* struct xfrm_lifetime_cfg lft;
* struct xfrm_lifetime_cur curlft;
* struct xfrm_stats stats;
* __u32 seq;
* __u32 reqid;
* __u16 family;
* __u8 mode;
* __u8 replay_window;
* __u8 flags;
* };
* </pre>
*
* @hide
*/
public class StructXfrmUsersaInfo extends Struct {
private static final int NESTED_STRUCTS_SIZE =
StructXfrmSelector.STRUCT_SIZE
+ StructXfrmId.STRUCT_SIZE
+ StructXfrmAddressT.STRUCT_SIZE
+ StructXfrmLifetimeCfg.STRUCT_SIZE
+ StructXfrmLifetimeCur.STRUCT_SIZE
+ StructXfrmStats.STRUCT_SIZE;
public static final int STRUCT_SIZE = NESTED_STRUCTS_SIZE + 20;
@Computed private final StructXfrmSelector mXfrmSelector;
@Computed private final StructXfrmId mXfrmId;
@Computed private final StructXfrmAddressT mSourceXfrmAddressT;
@Computed private final StructXfrmLifetimeCfg mXfrmLifetime;
@Computed private final StructXfrmLifetimeCur mXfrmCurrentLifetime;
@Computed private final StructXfrmStats mXfrmStats;
@Field(order = 0, type = Type.ByteArray, arraysize = NESTED_STRUCTS_SIZE)
public final byte[] nestedStructs;
@Field(order = 1, type = Type.U32)
public final long seq;
@Field(order = 2, type = Type.U32)
public final long reqId;
@Field(order = 3, type = Type.U16)
public final int family;
@Field(order = 4, type = Type.U8)
public final short mode;
@Field(order = 5, type = Type.U8)
public final short replayWindowLegacy;
@Field(order = 6, type = Type.U8, padding = 7)
public final short flags;
// Constructor that allows Strutc.parse(Class<T>, ByteBuffer) to work
public StructXfrmUsersaInfo(
@NonNull final byte[] nestedStructs,
long seq,
long reqId,
int family,
short mode,
short replayWindowLegacy,
short flags) {
this.nestedStructs = nestedStructs.clone();
this.seq = seq;
this.reqId = reqId;
this.family = family;
this.mode = mode;
this.replayWindowLegacy = replayWindowLegacy;
this.flags = flags;
final ByteBuffer nestedStructsBuff = ByteBuffer.wrap(nestedStructs);
nestedStructsBuff.order(ByteOrder.nativeOrder());
// The parsing order matters
mXfrmSelector = Struct.parse(StructXfrmSelector.class, nestedStructsBuff);
mXfrmId = Struct.parse(StructXfrmId.class, nestedStructsBuff);
mSourceXfrmAddressT = Struct.parse(StructXfrmAddressT.class, nestedStructsBuff);
mXfrmLifetime = Struct.parse(StructXfrmLifetimeCfg.class, nestedStructsBuff);
mXfrmCurrentLifetime = Struct.parse(StructXfrmLifetimeCur.class, nestedStructsBuff);
mXfrmStats = Struct.parse(StructXfrmStats.class, nestedStructsBuff);
}
// Constructor to build a new message for TESTING
StructXfrmUsersaInfo(
@NonNull final InetAddress dAddr,
@NonNull final InetAddress sAddr,
@NonNull final BigInteger addTime,
int selectorFamily,
long spi,
long seq,
long reqId,
short proto,
short mode,
short replayWindowLegacy,
short flags) {
this.seq = seq;
this.reqId = reqId;
this.family = dAddr instanceof Inet4Address ? OsConstants.AF_INET : OsConstants.AF_INET6;
this.mode = mode;
this.replayWindowLegacy = replayWindowLegacy;
this.flags = flags;
mXfrmSelector = new StructXfrmSelector(selectorFamily);
mXfrmId = new StructXfrmId(dAddr, spi, proto);
mSourceXfrmAddressT = new StructXfrmAddressT(sAddr);
mXfrmLifetime = new StructXfrmLifetimeCfg();
mXfrmCurrentLifetime =
new StructXfrmLifetimeCur(
BigInteger.ZERO, BigInteger.ZERO, addTime, BigInteger.ZERO);
mXfrmStats = new StructXfrmStats();
final ByteBuffer nestedStructsBuff = ByteBuffer.allocate(NESTED_STRUCTS_SIZE);
nestedStructsBuff.order(ByteOrder.nativeOrder());
mXfrmSelector.writeToByteBuffer(nestedStructsBuff);
mXfrmId.writeToByteBuffer(nestedStructsBuff);
mSourceXfrmAddressT.writeToByteBuffer(nestedStructsBuff);
mXfrmLifetime.writeToByteBuffer(nestedStructsBuff);
mXfrmCurrentLifetime.writeToByteBuffer(nestedStructsBuff);
mXfrmStats.writeToByteBuffer(nestedStructsBuff);
this.nestedStructs = nestedStructsBuff.array();
}
// Constructor to build a new message
public StructXfrmUsersaInfo(
@NonNull final InetAddress dAddr,
@NonNull final InetAddress sAddr,
long spi,
long seq,
long reqId,
short proto,
short mode,
short replayWindowLegacy,
short flags) {
// Use AF_UNSPEC for all SAs selectors. In transport mode, kernel picks selector family
// based on usersa->family, while in tunnel mode, the XFRM_STATE_AF_UNSPEC flag allows
// dual-stack SAs.
this(
dAddr,
sAddr,
BigInteger.ZERO,
OsConstants.AF_UNSPEC,
spi,
seq,
reqId,
proto,
mode,
replayWindowLegacy,
flags);
}
/** Return the destination address */
public InetAddress getDestAddress() {
return mXfrmId.getDestAddress(family);
}
/** Return the source address */
public InetAddress getSrcAddress() {
return mSourceXfrmAddressT.getAddress(family);
}
/** Return the SPI */
public long getSpi() {
return mXfrmId.spi;
}
/** Return the current lifetime */
public StructXfrmLifetimeCur getCurrentLifetime() {
return mXfrmCurrentLifetime;
}
}

View file

@ -41,6 +41,9 @@ public abstract class XfrmNetlinkMessage extends NetlinkMessage {
public static final short XFRM_MSG_NEWSA = 16;
public static final short XFRM_MSG_GETSA = 18;
public static final int XFRM_MODE_TRANSPORT = 0;
public static final int XFRM_MODE_TUNNEL = 1;
public static final BigInteger XFRM_INF = new BigInteger("FFFFFFFFFFFFFFFF", 16);
public XfrmNetlinkMessage(@NonNull final StructNlMsgHdr header) {

View file

@ -0,0 +1,127 @@
/*
* 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.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 org.junit.Test;
import org.junit.runner.RunWith;
import java.math.BigInteger;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Calendar;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class StructXfrmUsersaInfoTest {
private static final String EXPECTED_HEX_STRING =
"00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000A00000000000000"
+ "000000000000000020010DB800000000"
+ "0000000000000111AABBCCDD32000000"
+ "20010DB8000000000000000000000222"
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "FD464C65000000000000000000000000"
+ "00000000000000000000000000000000"
+ "024000000A0000000000000000000000";
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 BigInteger ADD_TIME;
private static final int SELECTOR_FAMILY = OsConstants.AF_INET6;
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 PROTO = IPPROTO_ESP;
private static final short MODE = XFRM_MODE_TRANSPORT;
private static final short REPLAY_WINDOW_LEGACY = 0;
private static final short FLAGS = 0;
static {
final Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
cal.set(2023, Calendar.NOVEMBER, 9, 2, 42, 05);
final long timestampSeconds = TimeUnit.MILLISECONDS.toSeconds(cal.getTimeInMillis());
ADD_TIME = BigInteger.valueOf(timestampSeconds);
}
@Test
public void testEncode() throws Exception {
final StructXfrmUsersaInfo struct =
new StructXfrmUsersaInfo(
DEST_ADDRESS,
SOURCE_ADDRESS,
ADD_TIME,
SELECTOR_FAMILY,
SPI,
SEQ,
REQ_ID,
PROTO,
MODE,
REPLAY_WINDOW_LEGACY,
FLAGS);
final ByteBuffer buffer = ByteBuffer.allocate(EXPECTED_HEX.length);
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 StructXfrmUsersaInfo struct =
StructXfrmUsersaInfo.parse(StructXfrmUsersaInfo.class, buffer);
assertEquals(DEST_ADDRESS, struct.getDestAddress());
assertEquals(SOURCE_ADDRESS, struct.getSrcAddress());
assertEquals(SPI, struct.getSpi());
assertEquals(SEQ, struct.seq);
assertEquals(REQ_ID, struct.reqId);
assertEquals(FAMILY, struct.family);
assertEquals(MODE, struct.mode);
assertEquals(REPLAY_WINDOW_LEGACY, struct.replayWindowLegacy);
assertEquals(FLAGS, struct.flags);
}
}