Snap for 6542686 from 50c6093b008cbb42c6b79a8b49857327d4ff4289 to rvc-release

Change-Id: I82c3833e6852637429ca9e809aebcef0067e3130
This commit is contained in:
android-build-team Robot
2020-05-30 01:01:26 +00:00
10 changed files with 1588 additions and 192 deletions

View File

@@ -39,6 +39,7 @@ java_defaults {
static_libs: [
"FrameworksNetCommonTests",
"TestNetworkStackLib",
"core-tests-support",
"compatibility-device-util-axt",
"cts-net-utils",

View File

@@ -0,0 +1,211 @@
/*
* Copyright (C) 2020 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 android.net.ipsec.ike.cts;
import android.net.InetAddresses;
import android.net.LinkAddress;
import android.net.ipsec.ike.IkeDerAsn1DnIdentification;
import android.net.ipsec.ike.IkeSession;
import android.net.ipsec.ike.IkeSessionParams;
import android.net.ipsec.ike.IkeTrafficSelector;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.internal.net.ipsec.ike.testutils.CertUtils;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.net.InetAddress;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.util.ArrayList;
import java.util.Arrays;
import javax.security.auth.x500.X500Principal;
/**
* Explicitly test setting up transport mode Child SA so that devices do not have
* FEATURE_IPSEC_TUNNELS will be test covered. Tunnel mode Child SA setup has been tested in
* IkeSessionPskTest and authentication method is orthogonal to Child mode.
*/
@RunWith(AndroidJUnit4.class)
public class IkeSessionDigitalSignatureTest extends IkeSessionTestBase {
private static final int EXPECTED_AUTH_REQ_FRAG_COUNT = 3;
private static final String IKE_INIT_RESP =
"46B8ECA1E0D72A18BF3FA1C2CB1EE86F21202220000000000000015022000030"
+ "0000002C010100040300000C0100000C800E0100030000080300000503000008"
+ "0200000400000008040000022800008800020000328451C8A976CE69E407966A"
+ "50D7320C4197A15A07267CE1B16BAFF9BDBBDEC1FDCDAAF7175ADF9AA8DB55DB"
+ "2D70C012D01D914C4EDEF6E8B226868EA1D01B2ED0C4C5C86E6BFE566010EC0C"
+ "33BA1C93666430B88BDA0470D82CC4F4416F49E3E361E3017C9F27811A66718B"
+ "389E1800915D776D59AA528A7E1D1B7815D35144290000249FE8FABE7F43D917"
+ "CE370DE2FD9C22BBC082951AC26C1BA26DE795470F2C25BC2900001C00004004"
+ "AE388EC86D6D1A470D44142D01AB2E85A7AC14182900001C0000400544A235A4"
+ "171C884286B170F48FFC181DB428D87D290000080000402E290000100000402F"
+ "00020003000400050000000800004014";
private static final String IKE_AUTH_RESP_FRAG_1 =
"46B8ECA1E0D72A18BF3FA1C2CB1EE86F3520232000000001000004E0240004C4"
+ "00010002DF6750A2D1D5675006F9F6230BB886FFD20CFB973FD04963CFD7A528"
+ "560598C58CC44178B2FCBBBBB271387AC81A664B7E7F1055B912F8C686E287C9"
+ "D31684C66339151AB86DA3CF1DA664052FA97687634558A1E9E6B37E16A86BD1"
+ "68D76DA5E2E1E0B7E98EB662D80D542307015D2BF134EBBBE425D6954FE8C2C4"
+ "D31D16C16AA0521C3C481F873ECF25BB8B05AC6083775C1821CAAB1E35A3955D"
+ "85ACC599574142E1DD5B262D6E5365CBF6EBE92FFCC16BC29EC3239456F3B202"
+ "492551C0F6D752ADCCA56D506D50CC8809EF6BC56EAD005586F7168F76445FD3"
+ "1366CC62D32C0C19B28210B8F813F97CD6A447C3857EFD6EC483DDA8ACD9870E"
+ "5A21B9C66F0FA44496C0C3D05E8859A1A4CFC88155D0C411BABC13033DD41FA4"
+ "AF08CE7734A146687F374F95634D1F26843203CA1FFD05CA3EB150CEA02FBF14"
+ "712B7A1C9BC7616A086E7FCA059E7D64EFF98DB895B32F8F7002762AF7D12F23"
+ "31E9DD25174C4CE273E5392BBB48F50B7A3E0187181216265F6A4FC7B91BE0AB"
+ "C601A580149D4B07411AE99DDB1944B977E86ADC9746605C60A92B569EEFAFFC"
+ "3A888D187B75D8F13249689FC28EBCD62B5E03AF171F3A561F0DEA3B1A75F531"
+ "971157DCE1E7BC6E7789FF3E8156015BC9C521EFE48996B41471D33BF09864E4"
+ "2436E8D7EB6218CDE7716DA754A924B123A63E25585BF27F4AC043A0C4AECE38"
+ "BB59DD62F5C0EC657206A76CED1BD26262237DA1CA6815435992A825758DEBEC"
+ "DDF598A22B8242AC4E34E70704DBA7B7B73DC3E067C1C98764F8791F84C99156"
+ "947D1FFC875F36FCE24B89369C1B5BF1D4C999DCA28E72A528D0E0163C66C067"
+ "E71B5E0025C13DA93313942F9EDA230B3ADC254821A4CB1A5DC9D0C5F4DC4E8E"
+ "CE46B7B8C72D3C5923C9B30DF1EF7B4EDEDA8BD05C86CA0162AE1BF8F277878E"
+ "607401BAA8F06E3EA873FA4C137324C4E0699277CDF649FE7F0F01945EE25FA7"
+ "0E4A89737E58185B11B4CB52FD5B0497D3E3CD1CEE7B1FBB3E969DB6F4C324A1"
+ "32DC6A0EA21F41332435FD99140C286F8ABBBA926953ADBEED17D30AAD953909"
+ "1347EF6D87163D6B1FF32D8B11FFB2E69FAEE7FE913D3826FBA7F9D11E0E3C57"
+ "27625B37D213710B5DD8965DAEFD3F491E8C029E2BF361039949BADEC31D60AC"
+ "355F26EE41339C03CC9D9B01C3C7F288F0E9D6DFEE78231BDA9AC10FED135913"
+ "2836B1A17CE060742B7E5B738A7177CCD59F70337BA251409C377A0FA5333204"
+ "D8622BA8C06DE0BEF4F32B6D4D77BE9DE977445D8A2A08C5C38341CB7974FBFB"
+ "22C8F983A7D6CEF068DDB2281E6673453521C831C1826861005AE5F37649BC64"
+ "0A6360B23284861441A440F1C5AADE1AB53CA63DB17F4C314D493C4C44DE5F20"
+ "75E084D080F92791F30BDD88373D50AB5A07BC72B0E7FFFA593103964E55603E"
+ "F7FEB7CA0762A1A7B86B6CCAD88CD6CBC7C6935D21F5F06B2700588A2530E619"
+ "DA1648AC809F3DDF56ACE5951737568FFEC7E2AB1AA0AE01B03A7F5A29CE73C0"
+ "5D2801B17CAAD0121082E9952FAB16BA1C386336C62D4CF3A5019CF61609433E"
+ "1C083237D47C4CF575097F7BF9000EF6B6C497A44E6480154A35669AD276BF05"
+ "6CC730B4E5962B6AF96CC6D236AE85CEFDA6877173F72D2F614F6696D1F9DF07"
+ "E107758B0978F69BC9DBE0CCBF252C40A3FDF7CE9104D3344F7B73593CCD73E0";
private static final String IKE_AUTH_RESP_FRAG_2 =
"46B8ECA1E0D72A18BF3FA1C2CB1EE86F3520232000000001000000F0000000D4"
+ "00020002155211EA41B37BC5F20568A6AE57038EEE208F94F9B444004F1EF391"
+ "2CABFCF857B9CD95FAAA9489ED10A3F5C93510820E22E23FC55ED8049E067D72"
+ "3645C00E1E08611916CE72D7F0A84123B63A8F3B9E78DBBE39967B7BB074AF4D"
+ "BF2178D991EDBDD01908A14A266D09236DB963B14AC33D894F0F83A580209EFD"
+ "61875BB56273AA336C22D6A4D890B93E0D42435667830CC32E4F608500E18569"
+ "3E6C1D88C0B5AE427333C86468E3474DAA4D1506AAB2A4021309A33DD759D0D0"
+ "A8C98BF7FBEA8109361A9F194D0FD756";
private static final String DELETE_IKE_RESP =
"46B8ECA1E0D72A18BF3FA1C2CB1EE86F2E202520000000020000004C00000030"
+ "342842D8DA37C8EFB92ED37C4FBB23CBDC90445137D6A0AF489F9F03641DBA9D"
+ "02F6F59FD8A7A78C7261CEB8";
// Using IPv4 for transport mode Child SA. IPv6 is currently infeasible because the IKE server
// that generates the test vectors is running in an IPv4 only network.
private static final IkeTrafficSelector TRANSPORT_MODE_INBOUND_TS =
new IkeTrafficSelector(
MIN_PORT,
MAX_PORT,
InetAddresses.parseNumericAddress("172.58.35.103"),
InetAddresses.parseNumericAddress("172.58.35.103"));
// TODO(b/157510502): Add test for IKE Session setup with transport mode Child in IPv6 network
private static final String LOCAL_ID_ASN1_DN =
"CN=client.test.ike.android.net, O=Android, C=US";
private static final String REMOTE_ID_ASN1_DN =
"CN=server.test.ike.android.net, O=Android, C=US";
private static X509Certificate sServerCaCert;
private static X509Certificate sClientEndCert;
private static X509Certificate sClientIntermediateCaCertOne;
private static X509Certificate sClientIntermediateCaCertTwo;
private static RSAPrivateKey sClientPrivateKey;
@BeforeClass
public static void setUpCertsBeforeClass() throws Exception {
sServerCaCert = CertUtils.createCertFromPemFile("server-a-self-signed-ca.pem");
sClientEndCert = CertUtils.createCertFromPemFile("client-a-end-cert.pem");
sClientIntermediateCaCertOne =
CertUtils.createCertFromPemFile("client-a-intermediate-ca-one.pem");
sClientIntermediateCaCertTwo =
CertUtils.createCertFromPemFile("client-a-intermediate-ca-two.pem");
sClientPrivateKey = CertUtils.createRsaPrivateKeyFromKeyFile("client-a-private-key.key");
}
private IkeSession openIkeSessionWithRemoteAddress(InetAddress remoteAddress) {
IkeSessionParams ikeParams =
new IkeSessionParams.Builder(sContext)
.setNetwork(mTunNetwork)
.setServerHostname(remoteAddress.getHostAddress())
.addSaProposal(SaProposalTest.buildIkeSaProposalWithNormalModeCipher())
.addSaProposal(SaProposalTest.buildIkeSaProposalWithCombinedModeCipher())
.setLocalIdentification(
new IkeDerAsn1DnIdentification(new X500Principal(LOCAL_ID_ASN1_DN)))
.setRemoteIdentification(
new IkeDerAsn1DnIdentification(
new X500Principal(REMOTE_ID_ASN1_DN)))
.setAuthDigitalSignature(
sServerCaCert,
sClientEndCert,
Arrays.asList(
sClientIntermediateCaCertOne, sClientIntermediateCaCertTwo),
sClientPrivateKey)
.build();
return new IkeSession(
sContext,
ikeParams,
buildTransportModeChildParamsWithTs(
TRANSPORT_MODE_INBOUND_TS, TRANSPORT_MODE_OUTBOUND_TS),
mUserCbExecutor,
mIkeSessionCallback,
mFirstChildSessionCallback);
}
@Test
public void testIkeSessionSetupAndChildSessionSetupWithTransportMode() throws Exception {
// Open IKE Session
IkeSession ikeSession = openIkeSessionWithRemoteAddress(mRemoteAddress);
performSetupIkeAndFirstChildBlocking(
IKE_INIT_RESP,
EXPECTED_AUTH_REQ_FRAG_COUNT /* expectedReqPktCnt */,
true /* expectedAuthUseEncap */,
IKE_AUTH_RESP_FRAG_1,
IKE_AUTH_RESP_FRAG_2);
// IKE INIT and IKE AUTH takes two exchanges. Message ID starts from 2
int expectedMsgId = 2;
verifyIkeSessionSetupBlocking();
verifyChildSessionSetupBlocking(
mFirstChildSessionCallback,
Arrays.asList(TRANSPORT_MODE_INBOUND_TS),
Arrays.asList(TRANSPORT_MODE_OUTBOUND_TS),
new ArrayList<LinkAddress>());
IpSecTransformCallRecord firstTransformRecordA =
mFirstChildSessionCallback.awaitNextCreatedIpSecTransform();
IpSecTransformCallRecord firstTransformRecordB =
mFirstChildSessionCallback.awaitNextCreatedIpSecTransform();
verifyCreateIpSecTransformPair(firstTransformRecordA, firstTransformRecordB);
// Close IKE Session
ikeSession.close();
performCloseIkeBlocking(expectedMsgId++, DELETE_IKE_RESP);
verifyCloseIkeAndChildBlocking(firstTransformRecordA, firstTransformRecordB);
}
}

View File

@@ -0,0 +1,220 @@
/*
* Copyright (C) 2020 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 android.net.ipsec.ike.cts;
import android.net.InetAddresses;
import android.net.LinkAddress;
import android.net.eap.EapSessionConfig;
import android.net.ipsec.ike.IkeFqdnIdentification;
import android.net.ipsec.ike.IkeSession;
import android.net.ipsec.ike.IkeSessionParams;
import android.net.ipsec.ike.IkeTrafficSelector;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.internal.net.ipsec.ike.testutils.CertUtils;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.net.InetAddress;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
/**
* Explicitly test setting up transport mode Child SA so that devices do not have
* FEATURE_IPSEC_TUNNELS will be test covered. Tunnel mode Child SA setup has been tested in
* IkeSessionPskTest and authentication method is orthogonal to Child mode.
*/
@RunWith(AndroidJUnit4.class)
public class IkeSessionMschapV2Test extends IkeSessionTestBase {
private static final String IKE_INIT_RESP =
"46B8ECA1E0D72A1873F643FF94D249A921202220000000000000015022000030"
+ "0000002C010100040300000C0100000C800E0080030000080300000203000008"
+ "0200000200000008040000022800008800020000CC6E71E67E32CED6BCE33FBD"
+ "A74113867E3FA3AE21C7C9AB44A7F8835DF602BFD6F6528B67FEE39821232380"
+ "C99E8FFC0A5D767F8F38906DA41946C2299DF18C15FA69BAC08D3EDB32E8C8CA"
+ "28431831561C04CB0CDE393F817151CD8DAF7A311838411F1C39BFDB5EBCF6A6"
+ "1DF66DEB067362649D64607D599B56C4227819D0290000241197004CF31AD00F"
+ "5E0C92E198488D8A2B6F6A25C82762AA49F565BCE9D857D72900001C00004004"
+ "A0D98FEABBFB92A6C0976EE83D2AACFCCF969A6B2900001C0000400575EBF73F"
+ "8EE5CC73917DE9D3F91FCD4A16A0444D290000080000402E290000100000402F"
+ "00020003000400050000000800004014";
private static final String IKE_AUTH_RESP_1_FRAG_1 =
"46B8ECA1E0D72A1873F643FF94D249A93520232000000001000004E0240004C4"
+ "00010002C4159CB756773B3F1911F4595107BC505D7A28C72F05182966076679"
+ "CA68ED92E4BC5CD441C9CB315F2F449A8A521CAFED3C5F285E295FC3791D3415"
+ "E3BACF66A08410DF4E35F7D88FE40DA28851C91C77A6549E186AC1B7846DF3FA"
+ "0A347A5ABBCAEE19E70F0EE5966DC6242A115F29523709302EDAD2E36C8F0395"
+ "CF5C42EC2D2898ECDD8A6AEDD686A70B589A981558667647F32F41E0D8913E94"
+ "A6693F53E59EA8938037F562CF1DC5E6E2CDC630B5FFB08949E3172249422F7D"
+ "EA069F9BAD5F96E48BADC7164A9269669AD0DF295A80C54D1D23CEA3F28AC485"
+ "86D2A9850DA23823037AB7D1577B7B2364C92C36B84238357129EB4A64D33310"
+ "B95DCD50CD53E78C32EFE7DC1627D9432E9BFDEE130045DE967B19F92A9D1270"
+ "F1E2C6BFBAA56802F3E63510578EF1ECB6872852F286EEC790AA1FE0CAF391CB"
+ "E276554922713BA4770CFE71E23F043DC620E22CC02A74F60725D18331B7F2C9"
+ "276EB6FBB7CBDAA040046D7ECBE1A5D7064E04E542807C5101B941D1C81B9D5E"
+ "90347B22BD4E638E2EDC98E369B51AA29BDB2CF8AA610D4B893EB83A4650717C"
+ "38B4D145EE939C18DCEDF6C79933CEB3D7C116B1F188DF9DDD560951B54E4A7D"
+ "80C999A32AB02BF39D7B498DAD36F1A5CBE2F64557D6401AE9DD6E0CEADA3F90"
+ "540FE9114BB6B8719C9064796354F4A180A6600CAD092F8302564E409B71ACB7"
+ "590F19B3AC88E7A606C718D0B97F7E4B4830F11D851C59F2255846DA22E2C805"
+ "0CA2AF2ACF3B6C769D11B75B5AC9AB82ED3D90014994B1BF6FED58FBEF2D72EF"
+ "8BDFE51F9A101393A7CA1ACF78FAEBF3E3CC25E09407D1E14AF351A159A13EE3"
+ "9B919BA8B49942792E7527C2FB6D418C4DF427669A4BF5A1AFBBB973BAF17918"
+ "9C9D520CAC2283B89A539ECE785EBE48FBB77D880A17D55C84A51F46068A4B87"
+ "FF48FEEE50E1E034CC8AFF5DA92105F55EC4823E67BDFE942CA8BE0DAECBBD52"
+ "E8AAF306049DC6C4CF87D987B0AC54FCE92E6AE8507965AAAC6AB8BD3405712F"
+ "EE170B70BC64BDCBD86D80C7AAAF341131F9A1210D7430B17218413AE1363183"
+ "5C98FA2428B1E9E987ADC9070E232310A28F4C3163E18366FFB112BADD7C5E0F"
+ "D13093A7C1428F87856BA0A7E46955589ACA267CE7A04320C4BCDBB60C672404"
+ "778F8D511AAB09349DAB482445D7F606F28E7FBBB18FC0F4EC0AF04F44C282F9"
+ "39C6E3B955C84DADEA350667236583069B74F492D600127636FA31F63E560851"
+ "2FC28B8EA5B4D01D110990B6EA46B9C2E7C7C856C240EF7A8147BA2C4344B85A"
+ "453C862024B5B6814D13CDEAEF7683D539BB50CAFFC0416F269F2F9EDEC5FA30"
+ "022FD7B4B186CD2020E7ED8D81ED90822EDD8B76F840DD68F09694CFF9B4F33E"
+ "11DF4E601A4212881A6D4E9259001705C41E9E23D18A7F3D4A3463649A38211A"
+ "5A90D0F17739A677C74E23F31C01D60B5A0F1E6A4D44FED9D25BF1E63418E1FC"
+ "0B19F6F4B71DE53C62B14B82279538A82DD4BE19AB6E00AFC20F124AAB7DF21A"
+ "42259BE4F40EC69B16917256F23E2C37376311D62E0A3A0EF8C2AD0C090221D5"
+ "C5ECA08F08178A4D31FFDB150C609827D18AD83C7B0A43AEE0406BD3FB494B53"
+ "A279FDD6447E234C926AD8CE47FFF779BB45B1FC8457C6E7D257D1359959D977"
+ "CEF6906A3367DC4D454993EFDC6F1EA94E17EB3DCB00A289346B4CFD7F19B16E";
private static final String IKE_AUTH_RESP_1_FRAG_2 =
"46B8ECA1E0D72A1873F643FF94D249A935202320000000010000008000000064"
+ "00020002C61F66025E821A5E69A4DE1F591A2C32C983C3154A5003660137D685"
+ "A5262B9FDF5EDC699DE4D8BD38F549E3CBD12024B45B4C86561C36C3EED839DA"
+ "9860C6AA0B764C662D08F1B6A98F68CF6E3038F737C0B415AD8A8B7D702BD92A";
private static final String IKE_AUTH_RESP_2 =
"46B8ECA1E0D72A1873F643FF94D249A92E202320000000020000008C30000070"
+ "62B90C2229FD23025BC2FD7FE6341E9EE04B17264CD619BCE18975A5F88BE438"
+ "D4AD4A5310057255AF568C293A29B10107E3EE3675C10AA2B26404D90C0528CC"
+ "F7605A86C96A1F2635CCC6CFC90EE65E5C2A2262EB33FE520EB708423A83CB63"
+ "274ECCBB102AF5DF35742657";
private static final String IKE_AUTH_RESP_3 =
"46B8ECA1E0D72A1873F643FF94D249A92E202320000000030000004C30000030"
+ "AB52C3C80123D3432C05AF457CE93C352395F73E861CD49561BA528CFE68D17D"
+ "78BBF6FC41E81C2B9EA051A2";
private static final String IKE_AUTH_RESP_4 =
"46B8ECA1E0D72A1873F643FF94D249A92E20232000000004000000CC270000B0"
+ "8D3342A7AB2666AC754F4B55C5C6B1A61255E62FBCA53D5CDEEDE60DADB7915C"
+ "7F962076A58BF7D39A05ED1B60FF349B6DE311AF7CEBC72B4BB9723A728A5D3E"
+ "9E508B2D7A11843D279B56ADA07E608D61F5CA7638F10372A440AD1DCE44E190"
+ "7B7B7A68B126EBBB86638D667D5B528D233BA8D32D7E0FAC4E1448E87396EEE6"
+ "0985B79841E1229D7962AACFD8F872722EC8D5B19D4C82D6C4ADCB276127A1A7"
+ "3FC84CDF85B2299BC96B64AC";
private static final String DELETE_IKE_RESP =
"46B8ECA1E0D72A1873F643FF94D249A92E202520000000050000004C00000030"
+ "622CE06C8CB132AA00567E9BC83F58B32BD7DB5130C76E385B306434DA227361"
+ "D50CC19D408A8D4F36F9697F";
// This value is align with the test vectors hex that are generated in an IPv4 environment
private static final IkeTrafficSelector TRANSPORT_MODE_INBOUND_TS =
new IkeTrafficSelector(
MIN_PORT,
MAX_PORT,
InetAddresses.parseNumericAddress("172.58.35.67"),
InetAddresses.parseNumericAddress("172.58.35.67"));
private static final EapSessionConfig EAP_CONFIG =
new EapSessionConfig.Builder()
.setEapIdentity(EAP_IDENTITY)
.setEapMsChapV2Config(EAP_MSCHAPV2_USERNAME, EAP_MSCHAPV2_PASSWORD)
.build();
private static X509Certificate sServerCaCert;
@BeforeClass
public static void setUpCertBeforeClass() throws Exception {
sServerCaCert = CertUtils.createCertFromPemFile("server-a-self-signed-ca.pem");
}
private IkeSession openIkeSessionWithRemoteAddress(InetAddress remoteAddress) {
IkeSessionParams ikeParams =
new IkeSessionParams.Builder(sContext)
.setNetwork(mTunNetwork)
.setServerHostname(remoteAddress.getHostAddress())
.addSaProposal(SaProposalTest.buildIkeSaProposalWithNormalModeCipher())
.addSaProposal(SaProposalTest.buildIkeSaProposalWithCombinedModeCipher())
.setLocalIdentification(new IkeFqdnIdentification(LOCAL_HOSTNAME))
.setRemoteIdentification(new IkeFqdnIdentification(REMOTE_HOSTNAME))
.setAuthEap(sServerCaCert, EAP_CONFIG)
.build();
return new IkeSession(
sContext,
ikeParams,
buildTransportModeChildParamsWithTs(
TRANSPORT_MODE_INBOUND_TS, TRANSPORT_MODE_OUTBOUND_TS),
mUserCbExecutor,
mIkeSessionCallback,
mFirstChildSessionCallback);
}
@Test
public void testIkeSessionSetupAndChildSessionSetupWithTransportMode() throws Exception {
// Open IKE Session
IkeSession ikeSession = openIkeSessionWithRemoteAddress(mRemoteAddress);
int expectedMsgId = 0;
mTunUtils.awaitReqAndInjectResp(
IKE_DETERMINISTIC_INITIATOR_SPI,
expectedMsgId++,
false /* expectedUseEncap */,
IKE_INIT_RESP);
mTunUtils.awaitReqAndInjectResp(
IKE_DETERMINISTIC_INITIATOR_SPI,
expectedMsgId++,
true /* expectedUseEncap */,
IKE_AUTH_RESP_1_FRAG_1,
IKE_AUTH_RESP_1_FRAG_2);
mTunUtils.awaitReqAndInjectResp(
IKE_DETERMINISTIC_INITIATOR_SPI,
expectedMsgId++,
true /* expectedUseEncap */,
IKE_AUTH_RESP_2);
mTunUtils.awaitReqAndInjectResp(
IKE_DETERMINISTIC_INITIATOR_SPI,
expectedMsgId++,
true /* expectedUseEncap */,
IKE_AUTH_RESP_3);
mTunUtils.awaitReqAndInjectResp(
IKE_DETERMINISTIC_INITIATOR_SPI,
expectedMsgId++,
true /* expectedUseEncap */,
IKE_AUTH_RESP_4);
verifyIkeSessionSetupBlocking();
verifyChildSessionSetupBlocking(
mFirstChildSessionCallback,
Arrays.asList(TRANSPORT_MODE_INBOUND_TS),
Arrays.asList(TRANSPORT_MODE_OUTBOUND_TS),
new ArrayList<LinkAddress>());
IpSecTransformCallRecord firstTransformRecordA =
mFirstChildSessionCallback.awaitNextCreatedIpSecTransform();
IpSecTransformCallRecord firstTransformRecordB =
mFirstChildSessionCallback.awaitNextCreatedIpSecTransform();
verifyCreateIpSecTransformPair(firstTransformRecordA, firstTransformRecordB);
// Close IKE Session
ikeSession.close();
performCloseIkeBlocking(expectedMsgId++, DELETE_IKE_RESP);
verifyCloseIkeAndChildBlocking(firstTransformRecordA, firstTransformRecordB);
}
}

View File

@@ -16,39 +16,34 @@
package android.net.ipsec.ike.cts;
import static android.net.ipsec.ike.IkeSessionConfiguration.EXTENSION_TYPE_FRAGMENTATION;
import static android.app.AppOpsManager.OP_MANAGE_IPSEC_TUNNELS;
import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED;
import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_INTERNAL_ADDRESS_FAILURE;
import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_NO_PROPOSAL_CHOSEN;
import static android.system.OsConstants.AF_INET;
import static android.system.OsConstants.AF_INET6;
import static com.android.internal.util.HexDump.hexStringToByteArray;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import android.net.ipsec.ike.ChildSessionConfiguration;
import android.net.LinkAddress;
import android.net.ipsec.ike.IkeFqdnIdentification;
import android.net.ipsec.ike.IkeSession;
import android.net.ipsec.ike.IkeSessionConfiguration;
import android.net.ipsec.ike.IkeSessionConnectionInfo;
import android.net.ipsec.ike.IkeSessionParams;
import android.net.ipsec.ike.TunnelModeChildSessionParams;
import android.net.ipsec.ike.exceptions.IkeException;
import android.net.ipsec.ike.exceptions.IkeProtocolException;
import android.platform.test.annotations.AppModeFull;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;
@RunWith(AndroidJUnit4.class)
@AppModeFull(reason = "MANAGE_TEST_NETWORKS permission can't be granted to instant apps")
@AppModeFull(reason = "MANAGE_IPSEC_TUNNELS permission can't be granted to instant apps")
public class IkeSessionPskTest extends IkeSessionTestBase {
// Test vectors for success workflow
private static final String SUCCESS_IKE_INIT_RESP =
@@ -89,16 +84,6 @@ public class IkeSessionPskTest extends IkeSessionTestBase {
+ "9352D71100777B00ABCC6BD7DBEA697827FFAAA48DF9A54D1D68161939F5DC8"
+ "6743A7CEB2BE34AC00095A5B8";
private static final long IKE_INIT_SPI = Long.parseLong("46B8ECA1E0D72A18", 16);
private static final TunnelModeChildSessionParams CHILD_PARAMS =
new TunnelModeChildSessionParams.Builder()
.addSaProposal(SaProposalTest.buildChildSaProposalWithNormalModeCipher())
.addSaProposal(SaProposalTest.buildChildSaProposalWithCombinedModeCipher())
.addInternalAddressRequest(AF_INET)
.addInternalAddressRequest(AF_INET6)
.build();
private IkeSession openIkeSessionWithRemoteAddress(InetAddress remoteAddress) {
IkeSessionParams ikeParams =
new IkeSessionParams.Builder(sContext)
@@ -113,110 +98,159 @@ public class IkeSessionPskTest extends IkeSessionTestBase {
return new IkeSession(
sContext,
ikeParams,
CHILD_PARAMS,
buildTunnelModeChildSessionParams(),
mUserCbExecutor,
mIkeSessionCallback,
mFirstChildSessionCallback);
}
@BeforeClass
public static void setUpTunnelPermissionBeforeClass() throws Exception {
// Under normal circumstances, the MANAGE_IPSEC_TUNNELS appop would be auto-granted, and
// a standard permission is insufficient. So we shell out the appop, to give us the
// right appop permissions.
setAppOp(OP_MANAGE_IPSEC_TUNNELS, true);
}
// This method is guaranteed to run in subclasses and will run after subclasses' @AfterClass
// methods.
@AfterClass
public static void tearDownTunnelPermissionAfterClass() throws Exception {
setAppOp(OP_MANAGE_IPSEC_TUNNELS, false);
}
@Test
public void testIkeSessionSetupAndChildSessionSetupWithTunnelMode() throws Exception {
if (!hasTunnelsFeature()) return;
// Open IKE Session
IkeSession ikeSession = openIkeSessionWithRemoteAddress(mRemoteAddress);
int expectedMsgId = 0;
mTunUtils.awaitReqAndInjectResp(
IKE_INIT_SPI,
expectedMsgId++,
false /* expectedUseEncap */,
hexStringToByteArray(SUCCESS_IKE_INIT_RESP));
performSetupIkeAndFirstChildBlocking(SUCCESS_IKE_INIT_RESP, SUCCESS_IKE_AUTH_RESP);
mTunUtils.awaitReqAndInjectResp(
IKE_INIT_SPI,
expectedMsgId++,
true /* expectedUseEncap */,
hexStringToByteArray(SUCCESS_IKE_AUTH_RESP));
// IKE INIT and IKE AUTH takes two exchanges. Message ID starts from 2
int expectedMsgId = 2;
// Verify opening IKE Session
IkeSessionConfiguration ikeConfig = mIkeSessionCallback.awaitIkeConfig();
assertNotNull(ikeConfig);
assertEquals(EXPECTED_REMOTE_APP_VERSION_EMPTY, ikeConfig.getRemoteApplicationVersion());
assertTrue(ikeConfig.getRemoteVendorIds().isEmpty());
assertTrue(ikeConfig.getPcscfServers().isEmpty());
assertTrue(ikeConfig.isIkeExtensionEnabled(EXTENSION_TYPE_FRAGMENTATION));
verifyIkeSessionSetupBlocking();
verifyChildSessionSetupBlocking(
mFirstChildSessionCallback,
Arrays.asList(TUNNEL_MODE_INBOUND_TS),
Arrays.asList(TUNNEL_MODE_OUTBOUND_TS),
Arrays.asList(EXPECTED_INTERNAL_LINK_ADDR));
IkeSessionConnectionInfo ikeConnectInfo = ikeConfig.getIkeSessionConnectionInfo();
assertNotNull(ikeConnectInfo);
assertEquals(mLocalAddress, ikeConnectInfo.getLocalAddress());
assertEquals(mRemoteAddress, ikeConnectInfo.getRemoteAddress());
assertEquals(mTunNetwork, ikeConnectInfo.getNetwork());
// Verify opening first Child Session
ChildSessionConfiguration firstChildConfig = mFirstChildSessionCallback.awaitChildConfig();
assertNotNull(firstChildConfig);
assertEquals(
Arrays.asList(EXPECTED_INBOUND_TS), firstChildConfig.getInboundTrafficSelectors());
assertEquals(Arrays.asList(DEFAULT_V4_TS), firstChildConfig.getOutboundTrafficSelectors());
assertEquals(
Arrays.asList(EXPECTED_INTERNAL_LINK_ADDR),
firstChildConfig.getInternalAddresses());
assertTrue(firstChildConfig.getInternalSubnets().isEmpty());
assertTrue(firstChildConfig.getInternalDnsServers().isEmpty());
assertTrue(firstChildConfig.getInternalDhcpServers().isEmpty());
assertNotNull(mFirstChildSessionCallback.awaitNextCreatedIpSecTransform());
assertNotNull(mFirstChildSessionCallback.awaitNextCreatedIpSecTransform());
IpSecTransformCallRecord firstTransformRecordA =
mFirstChildSessionCallback.awaitNextCreatedIpSecTransform();
IpSecTransformCallRecord firstTransformRecordB =
mFirstChildSessionCallback.awaitNextCreatedIpSecTransform();
verifyCreateIpSecTransformPair(firstTransformRecordA, firstTransformRecordB);
// Open additional Child Session
TestChildSessionCallback additionalChildCb = new TestChildSessionCallback();
ikeSession.openChildSession(CHILD_PARAMS, additionalChildCb);
ikeSession.openChildSession(buildTunnelModeChildSessionParams(), additionalChildCb);
mTunUtils.awaitReqAndInjectResp(
IKE_INIT_SPI,
IKE_DETERMINISTIC_INITIATOR_SPI,
expectedMsgId++,
true /* expectedUseEncap */,
hexStringToByteArray(SUCCESS_CREATE_CHILD_RESP));
SUCCESS_CREATE_CHILD_RESP);
// Verify opening additional Child Session
ChildSessionConfiguration additionalChildConfig = additionalChildCb.awaitChildConfig();
assertNotNull(additionalChildConfig);
assertEquals(
Arrays.asList(EXPECTED_INBOUND_TS), firstChildConfig.getInboundTrafficSelectors());
assertEquals(Arrays.asList(DEFAULT_V4_TS), firstChildConfig.getOutboundTrafficSelectors());
assertTrue(additionalChildConfig.getInternalAddresses().isEmpty());
assertTrue(additionalChildConfig.getInternalSubnets().isEmpty());
assertTrue(additionalChildConfig.getInternalDnsServers().isEmpty());
assertTrue(additionalChildConfig.getInternalDhcpServers().isEmpty());
assertNotNull(additionalChildCb.awaitNextCreatedIpSecTransform());
assertNotNull(additionalChildCb.awaitNextCreatedIpSecTransform());
verifyChildSessionSetupBlocking(
additionalChildCb,
Arrays.asList(TUNNEL_MODE_INBOUND_TS),
Arrays.asList(TUNNEL_MODE_OUTBOUND_TS),
new ArrayList<LinkAddress>());
IpSecTransformCallRecord additionalTransformRecordA =
additionalChildCb.awaitNextCreatedIpSecTransform();
IpSecTransformCallRecord additionalTransformRecordB =
additionalChildCb.awaitNextCreatedIpSecTransform();
verifyCreateIpSecTransformPair(additionalTransformRecordA, additionalTransformRecordB);
// Close additional Child Session
ikeSession.closeChildSession(additionalChildCb);
mTunUtils.awaitReqAndInjectResp(
IKE_INIT_SPI,
IKE_DETERMINISTIC_INITIATOR_SPI,
expectedMsgId++,
true /* expectedUseEncap */,
hexStringToByteArray(SUCCESS_DELETE_CHILD_RESP));
SUCCESS_DELETE_CHILD_RESP);
assertNotNull(additionalChildCb.awaitNextDeletedIpSecTransform());
assertNotNull(additionalChildCb.awaitNextDeletedIpSecTransform());
verifyDeleteIpSecTransformPair(
additionalChildCb, additionalTransformRecordA, additionalTransformRecordB);
additionalChildCb.awaitOnClosed();
// Close IKE Session
ikeSession.close();
mTunUtils.awaitReqAndInjectResp(
IKE_INIT_SPI,
expectedMsgId++,
true /* expectedUseEncap */,
hexStringToByteArray(SUCCESS_DELETE_IKE_RESP));
performCloseIkeBlocking(expectedMsgId++, SUCCESS_DELETE_IKE_RESP);
verifyCloseIkeAndChildBlocking(firstTransformRecordA, firstTransformRecordB);
}
assertNotNull(mFirstChildSessionCallback.awaitNextDeletedIpSecTransform());
assertNotNull(mFirstChildSessionCallback.awaitNextDeletedIpSecTransform());
mFirstChildSessionCallback.awaitOnClosed();
mIkeSessionCallback.awaitOnClosed();
@Test
public void testIkeSessionSetupAndChildSessionSetupWithTunnelModeV6() throws Exception {
if (!hasTunnelsFeature()) return;
// TODO: verify created and deleted IpSecTransform pair and their directions
final String ikeInitResp =
"46B8ECA1E0D72A186F7B6C2CEB77EB9021202220000000000000011822000030"
+ "0000002C010100040300000C0100000C800E0100030000080300000C03000008"
+ "0200000500000008040000022800008800020000DABAA04B38B491E2403F2125"
+ "96ECF1C8EF7B1DC19A422FDD46E1756C826BB3A16404361B775D9950577B5CDF"
+ "6AAA1642BD1427BDA8BC55354A97C1025E19C1E2EE2DF8A0C9406E545D829F52"
+ "75695008E3B742984B8DD1770F3514213B0DF3EE8B199416DF200D248115C057"
+ "1C193E4F96802E5EF48DD99CAC251882A8F7CCC329000024BC6F0F1D3653C2C7"
+ "679E02CDB6A3B32B2FEE9AF52F0326D4D9AE073D56CE8922290000080000402E"
+ "290000100000402F00020003000400050000000800004014";
final String ikeAuthResp =
"46B8ECA1E0D72A186F7B6C2CEB77EB902E202320000000010000015024000134"
+ "4D115AFDCDAD0310760BB664EB7D405A340869AD6EDF0AAEAD0663A9253DADCB"
+ "73EBE5CD29D4FA1CDEADE0B94391B5C4CF77BCC1596ACE3CE6A7891E44888FA5"
+ "46632C0EF4E6193C023C9DC59142C37D1C49D6EF5CD324EC6FC35C89E1721C78"
+ "91FDCDB723D8062709950F4AA9273D26A54C9C7E86862DBC15F7B6641D2B9BAD"
+ "E55069008201D12968D97B537B1518FE87B0FFA03C3EE6012C06721B1E2A3F68"
+ "92108BC4A4F7063F7F94562D8B60F291A1377A836CF12BCDA7E15C1A8F3C77BB"
+ "6DB7F2C833CCE4CDDED7506536621A3356CE2BC1874E7B1A1A9B447D7DF6AB09"
+ "638B8AD94A781B28BB91B514B611B24DF8E8A047A10AE27BBF15C754D3D2F792"
+ "D3E1CCADDAE934C98AE53A8FC3419C88AFF0355564F82A629C998012DA7BB704"
+ "5307270DF326377E3E1994476902035B";
final String deleteIkeResp =
"46B8ECA1E0D72A186F7B6C2CEB77EB902E202520000000020000005000000034"
+ "CF15C299F35688E5140A48B61C95F004121BF8236201415E5CD45BA41AAB16D4"
+ "90B44B9E6D5D92B5B97D24196A58C73F";
mLocalAddress = IPV6_ADDRESS_LOCAL;
mRemoteAddress = IPV6_ADDRESS_REMOTE;
// Teardown current test network that uses IPv4 address and set up new network with IPv6
// address.
tearDownTestNetwork();
setUpTestNetwork(mLocalAddress);
// Open IKE Session
IkeSession ikeSession = openIkeSessionWithRemoteAddress(mRemoteAddress);
performSetupIkeAndFirstChildBlocking(
ikeInitResp,
1 /* expectedAuthReqPktCnt */,
false /* expectedAuthUseEncap */,
ikeAuthResp);
// Local request message ID starts from 2 because there is one IKE_INIT message and a single
// IKE_AUTH message.
int expectedMsgId = 2;
verifyIkeSessionSetupBlocking();
verifyChildSessionSetupBlocking(
mFirstChildSessionCallback,
Arrays.asList(TUNNEL_MODE_INBOUND_TS_V6),
Arrays.asList(TUNNEL_MODE_OUTBOUND_TS_V6),
Arrays.asList(EXPECTED_INTERNAL_LINK_ADDR_V6),
Arrays.asList(EXPECTED_DNS_SERVERS_ONE, EXPECTED_DNS_SERVERS_TWO));
IpSecTransformCallRecord firstTransformRecordA =
mFirstChildSessionCallback.awaitNextCreatedIpSecTransform();
IpSecTransformCallRecord firstTransformRecordB =
mFirstChildSessionCallback.awaitNextCreatedIpSecTransform();
verifyCreateIpSecTransformPair(firstTransformRecordA, firstTransformRecordB);
// Close IKE Session
ikeSession.close();
performCloseIkeBlocking(expectedMsgId++, false /* expectedUseEncap */, deleteIkeResp);
verifyCloseIkeAndChildBlocking(firstTransformRecordA, firstTransformRecordB);
}
@Test
@@ -225,18 +259,7 @@ public class IkeSessionPskTest extends IkeSessionTestBase {
// Open IKE Session
IkeSession ikeSession = openIkeSessionWithRemoteAddress(mRemoteAddress);
int expectedMsgId = 0;
mTunUtils.awaitReqAndInjectResp(
IKE_INIT_SPI,
expectedMsgId++,
false /* expectedUseEncap */,
hexStringToByteArray(SUCCESS_IKE_INIT_RESP));
mTunUtils.awaitReqAndInjectResp(
IKE_INIT_SPI,
expectedMsgId++,
true /* expectedUseEncap */,
hexStringToByteArray(SUCCESS_IKE_AUTH_RESP));
performSetupIkeAndFirstChildBlocking(SUCCESS_IKE_INIT_RESP, SUCCESS_IKE_AUTH_RESP);
ikeSession.kill();
mFirstChildSessionCallback.awaitOnClosed();
@@ -245,29 +268,94 @@ public class IkeSessionPskTest extends IkeSessionTestBase {
@Test
public void testIkeInitFail() throws Exception {
String ikeInitFailRespHex =
final String ikeInitFailRespHex =
"46B8ECA1E0D72A180000000000000000292022200000000000000024000000080000000E";
// Open IKE Session
IkeSession ikeSession = openIkeSessionWithRemoteAddress(mRemoteAddress);
int expectedMsgId = 0;
mTunUtils.awaitReqAndInjectResp(
IKE_INIT_SPI,
IKE_DETERMINISTIC_INITIATOR_SPI,
expectedMsgId++,
false /* expectedUseEncap */,
hexStringToByteArray(ikeInitFailRespHex));
ikeInitFailRespHex);
mFirstChildSessionCallback.awaitOnClosed();
IkeException exception = mIkeSessionCallback.awaitOnClosedException();
assertNotNull(exception);
assertTrue(exception instanceof IkeProtocolException);
IkeProtocolException protocolException = (IkeProtocolException) exception;
IkeProtocolException protocolException =
(IkeProtocolException) mIkeSessionCallback.awaitOnClosedException();
assertEquals(ERROR_TYPE_NO_PROPOSAL_CHOSEN, protocolException.getErrorType());
assertArrayEquals(EXPECTED_PROTOCOL_ERROR_DATA_NONE, protocolException.getErrorData());
}
// TODO(b/155821007): Verify rekey process and handling IKE_AUTH failure
@Test
public void testIkeAuthHandlesAuthFailNotification() throws Exception {
final String ikeInitRespHex =
"46B8ECA1E0D72A18CF94CE3159486F002120222000000000000001502200"
+ "00300000002C010100040300000C0100000C800E01000300000803000005"
+ "0300000802000004000000080400000228000088000200001821AA854691"
+ "FA3292DF710F0AC149ACBD0CB421608B8796C1912AF04C5B4B23936FDEC4"
+ "7CB640E3EAFB56BBB562825E87AF68B40E4BAB80A49BAD44407450A4195A"
+ "1DD54BD99F48D28C9F0FBA315A3401C1C3C4AD55911F514A8DF2D2467C46"
+ "A73DDC1452AE81336E0F0D5EC896D2E7A77628AF2F9089F48943399DF216"
+ "EFCD2900002418D2B7E4E6AF0FEFF5962CF8D68F7793B1293FEDE13331D4"
+ "AB0CE9436C2EE1EC2900001C0000400457BD9AEF5B362A83DD7F3DDAA4A9"
+ "9B6B4041DAF32900001C000040055A81893582701E44D4B6729A22FE06DE"
+ "82A03A36290000080000402E290000100000402F00020003000400050000"
+ "000800004014";
final String ikeAuthFailRespHex =
"46B8ECA1E0D72A18CF94CE3159486F002E202320000000010000004C2900"
+ "00301B9E4C8242D3BE62E7F0A537FE8B92C6EAB7153105DA421DCE43A06D"
+ "AB6E4808BAC0CA1DAD6ADD0A126A41BD";
// TODO(b/155821007): Test creating transport mode Child SA
// Open IKE Session
IkeSession ikeSession = openIkeSessionWithRemoteAddress(mRemoteAddress);
performSetupIkeAndFirstChildBlocking(ikeInitRespHex, ikeAuthFailRespHex);
mFirstChildSessionCallback.awaitOnClosed();
IkeProtocolException protocolException =
(IkeProtocolException) mIkeSessionCallback.awaitOnClosedException();
assertEquals(ERROR_TYPE_AUTHENTICATION_FAILED, protocolException.getErrorType());
assertArrayEquals(EXPECTED_PROTOCOL_ERROR_DATA_NONE, protocolException.getErrorData());
}
@Test
public void testIkeAuthHandlesFirstChildCreationFail() throws Exception {
final String ikeInitRespHex =
"46B8ECA1E0D72A182B300285DA19E6452120222000000000000001502200"
+ "00300000002C010100040300000C0100000C800E01000300000803000005"
+ "0300000802000004000000080400000228000088000200005C9DE629981F"
+ "DB1FC45DB6CCF15D076C1F51BD9F63C771DC089F05CCDE6247965D15C616"
+ "C7B5A62342491715E4D1FEA19326477D24143E8E56AB6AD93F54B19BC32A"
+ "44BC0A5B5632E57D0A3C43E466E1547D8E4EF65EA4B864A348161666E229"
+ "84975A486251A17C4F096A6D5CF3DB83874B70324A31AA7ADDE2D73BADD8"
+ "238029000024CF06260F7C4923295E7C91F2B8479212892DA7A519A0322F"
+ "F5B2BF570B92972B2900001C00004004C7ACC2C7D58CF8C9F5E953993AF4"
+ "6CAC976635B42900001C00004005B64B190DFE7BDE8B9B1475EDE67B63D6"
+ "F1DBBF44290000080000402E290000100000402F00020003000400050000"
+ "000800004014";
final String ikeAuthCreateChildFailHex =
"46B8ECA1E0D72A182B300285DA19E6452E202320000000010000008C2400"
+ "0070386FC9CCC67495A17915D0544390A2963A769F4A42C6FA668CEEC07F"
+ "EC0C87D681DE34267023DD394F1401B5A563E71002C0CE0928D0ABC0C4570"
+ "E39C2EDEF820F870AB71BD70A3F3EB5C96CA294B6D3F01677690DCF9F8CFC"
+ "9584650957573502BA83E32F18207A9ADEB1FA";
// Open IKE Session
IkeSession ikeSession = openIkeSessionWithRemoteAddress(mRemoteAddress);
performSetupIkeAndFirstChildBlocking(ikeInitRespHex, ikeAuthCreateChildFailHex);
// Even though the child creation failed, the authentication succeeded, so the IKE Session's
// onOpened() callback is still expected
verifyIkeSessionSetupBlocking();
// Verify Child Creation failed
IkeProtocolException protocolException =
(IkeProtocolException) mFirstChildSessionCallback.awaitOnClosedException();
assertEquals(ERROR_TYPE_INTERNAL_ADDRESS_FAILURE, protocolException.getErrorType());
assertArrayEquals(EXPECTED_PROTOCOL_ERROR_DATA_NONE, protocolException.getErrorData());
ikeSession.kill();
mIkeSessionCallback.awaitOnClosed();
}
}

View File

@@ -0,0 +1,265 @@
/*
* Copyright (C) 2020 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 android.net.ipsec.ike.cts;
import static com.android.internal.util.HexDump.hexStringToByteArray;
import android.net.InetAddresses;
import android.net.LinkAddress;
import android.net.ipsec.ike.IkeFqdnIdentification;
import android.net.ipsec.ike.IkeSession;
import android.net.ipsec.ike.IkeSessionParams;
import android.net.ipsec.ike.IkeTrafficSelector;
import android.net.ipsec.ike.cts.IkeTunUtils.PortPair;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;
/**
* Explicitly test transport mode Child SA so that devices without FEATURE_IPSEC_TUNNELS can be test
* covered. Tunnel mode Child SA setup has been tested in IkeSessionPskTest. Rekeying process is
* independent from Child SA mode.
*/
@RunWith(AndroidJUnit4.class)
public class IkeSessionRekeyTest extends IkeSessionTestBase {
// This value is align with the test vectors hex that are generated in an IPv4 environment
private static final IkeTrafficSelector TRANSPORT_MODE_INBOUND_TS =
new IkeTrafficSelector(
MIN_PORT,
MAX_PORT,
InetAddresses.parseNumericAddress("172.58.35.40"),
InetAddresses.parseNumericAddress("172.58.35.40"));
private IkeSession openIkeSessionWithRemoteAddress(InetAddress remoteAddress) {
IkeSessionParams ikeParams =
new IkeSessionParams.Builder(sContext)
.setNetwork(mTunNetwork)
.setServerHostname(remoteAddress.getHostAddress())
.addSaProposal(SaProposalTest.buildIkeSaProposalWithNormalModeCipher())
.addSaProposal(SaProposalTest.buildIkeSaProposalWithCombinedModeCipher())
.setLocalIdentification(new IkeFqdnIdentification(LOCAL_HOSTNAME))
.setRemoteIdentification(new IkeFqdnIdentification(REMOTE_HOSTNAME))
.setAuthPsk(IKE_PSK)
.build();
return new IkeSession(
sContext,
ikeParams,
buildTransportModeChildParamsWithTs(
TRANSPORT_MODE_INBOUND_TS, TRANSPORT_MODE_OUTBOUND_TS),
mUserCbExecutor,
mIkeSessionCallback,
mFirstChildSessionCallback);
}
private byte[] buildInboundPkt(PortPair outPktSrcDestPortPair, String inboundDataHex)
throws Exception {
// Build inbound packet by flipping the outbound packet addresses and ports
return IkeTunUtils.buildIkePacket(
mRemoteAddress,
mLocalAddress,
outPktSrcDestPortPair.dstPort,
outPktSrcDestPortPair.srcPort,
true /* useEncap */,
hexStringToByteArray(inboundDataHex));
}
@Test
public void testRekeyIke() throws Exception {
final String ikeInitResp =
"46B8ECA1E0D72A1866B5248CF6C7472D21202220000000000000015022000030"
+ "0000002C010100040300000C0100000C800E0100030000080300000C03000008"
+ "0200000500000008040000022800008800020000920D3E830E7276908209212D"
+ "E5A7F2A48706CFEF1BE8CB6E3B173B8B4E0D8C2DC626271FF1B13A88619E569E"
+ "7B03C3ED2C127390749CDC7CDC711D0A8611E4457FFCBC4F0981B3288FBF58EA"
+ "3E8B70E27E76AE70117FBBCB753660ADDA37EB5EB3A81BED6A374CCB7E132C2A"
+ "94BFCE402DC76B19C158B533F6B1F2ABF01ACCC329000024B302CA2FB85B6CF4"
+ "02313381246E3C53828D787F6DFEA6BD62D6405254AEE6242900001C00004004"
+ "7A1682B06B58596533D00324886EF1F20EF276032900001C00004005BF633E31"
+ "F9984B29A62E370BB2770FC09BAEA665290000080000402E290000100000402F"
+ "00020003000400050000000800004014";
final String ikeAuthResp =
"46B8ECA1E0D72A1866B5248CF6C7472D2E20232000000001000000F0240000D4"
+ "10166CA8647F56123DE74C17FA5E256043ABF73216C812EE32EE1BB01EAF4A82"
+ "DC107AB3ADBFEE0DEA5EEE10BDD5D43178F4C975C7C775D252273BB037283C7F"
+ "236FE34A6BCE4833816897075DB2055B9FFD66DFA45A0A89A8F70AFB59431EED"
+ "A20602FB614369D12906D3355CF7298A5D25364ABBCC75A9D88E0E6581449FCD"
+ "4E361A39E00EFD1FD0A69651F63DB46C12470226AA21BA5EFF48FAF0B6DDF61C"
+ "B0A69392CE559495EEDB4D1C1D80688434D225D57210A424C213F7C993D8A456"
+ "38153FBD194C5E247B592D1D048DB4C8";
final String rekeyIkeCreateReq =
"46B8ECA1E0D72A1866B5248CF6C7472D2E202400000000000000013021000114"
+ "13743670039E308A8409BA5FD47B67F956B36FEE88AC3B70BB5D789B8218A135"
+ "1B3D83E260E87B3EDB1BF064F09D4DC2611AEDBC99951B4B2DE767BD4AA2ACC3"
+ "3653549CFC66B75869DF003CDC9A137A9CC27776AD5732B34203E74BE8CA4858"
+ "1D5C0D9C9CA52D680EB299B4B21C7FA25FFEE174D57015E0FF2EAED653AAD95C"
+ "071ABE269A8C2C9FBC1188E07550EB992F910D4CA9689E44BA66DE0FABB2BDF9"
+ "8DD377186DBB25EF9B68B027BB2A27981779D8303D88D7CE860010A42862D50B"
+ "1E0DBFD3D27C36F14809D7F493B2B96A65534CF98B0C32AD5219AD77F681AC04"
+ "9D5CB89A0230A91A243FA7F16251B0D9B4B65E7330BEEAC9663EF4578991EAC8"
+ "46C19EBB726E7D113F1D0D601102C05E";
final String rekeyIkeDeleteReq =
"46B8ECA1E0D72A1866B5248CF6C7472D2E20250000000001000000502A000034"
+ "02E40C0C7B1ED977729F705BB9B643FAC513A1070A6EB28ECD2AEA8A441ADC05"
+ "7841382A7967BBF116AE52496590B2AD";
final String deleteIkeReq =
"7D3DEDC65407D1FC9361C8CF8C47162A2E20250800000000000000502A000034"
+ "201915C9E4E9173AA9EE79F3E02FE2D4954B22085C66D164762C34D347C16E9F"
+ "FC5F7F114428C54D8D915860C57B1BC1";
final long newIkeDeterministicInitSpi = Long.parseLong("7D3DEDC65407D1FC", 16);
// Open IKE Session
IkeSession ikeSession = openIkeSessionWithRemoteAddress(mRemoteAddress);
PortPair localRemotePorts = performSetupIkeAndFirstChildBlocking(ikeInitResp, ikeAuthResp);
// Local request message ID starts from 2 because there is one IKE_INIT message and a single
// IKE_AUTH message.
int expectedReqMsgId = 2;
int expectedRespMsgId = 0;
verifyIkeSessionSetupBlocking();
verifyChildSessionSetupBlocking(
mFirstChildSessionCallback,
Arrays.asList(TRANSPORT_MODE_INBOUND_TS),
Arrays.asList(TRANSPORT_MODE_OUTBOUND_TS),
new ArrayList<LinkAddress>());
IpSecTransformCallRecord firstTransformRecordA =
mFirstChildSessionCallback.awaitNextCreatedIpSecTransform();
IpSecTransformCallRecord firstTransformRecordB =
mFirstChildSessionCallback.awaitNextCreatedIpSecTransform();
verifyCreateIpSecTransformPair(firstTransformRecordA, firstTransformRecordB);
// Inject rekey IKE requests
mTunUtils.injectPacket(buildInboundPkt(localRemotePorts, rekeyIkeCreateReq));
mTunUtils.awaitResp(
IKE_DETERMINISTIC_INITIATOR_SPI, expectedRespMsgId++, true /* expectedUseEncap */);
mTunUtils.injectPacket(buildInboundPkt(localRemotePorts, rekeyIkeDeleteReq));
mTunUtils.awaitResp(
IKE_DETERMINISTIC_INITIATOR_SPI, expectedRespMsgId++, true /* expectedUseEncap */);
// IKE has been rekeyed, reset message IDs
expectedReqMsgId = 0;
expectedRespMsgId = 0;
// Inject delete IKE request
mTunUtils.injectPacket(buildInboundPkt(localRemotePorts, deleteIkeReq));
mTunUtils.awaitResp(
newIkeDeterministicInitSpi, expectedRespMsgId++, true /* expectedUseEncap */);
verifyDeleteIpSecTransformPair(
mFirstChildSessionCallback, firstTransformRecordA, firstTransformRecordB);
mFirstChildSessionCallback.awaitOnClosed();
mIkeSessionCallback.awaitOnClosed();
}
@Test
public void testRekeyTransportModeChildSa() throws Exception {
final String ikeInitResp =
"46B8ECA1E0D72A18CECD871146CF83A121202220000000000000015022000030"
+ "0000002C010100040300000C0100000C800E0100030000080300000C03000008"
+ "0200000500000008040000022800008800020000C4904458957746BCF1C12972"
+ "1D4E19EB8A584F78DE673053396D167CE0F34552DBC69BA63FE7C673B4CF4A99"
+ "62481518EE985357876E8C47BAAA0DBE9C40AE47B12E52165874703586E8F786"
+ "045F72EEEB238C5D1823352BED44B71B3214609276ADC0B3D42DAC820168C4E2"
+ "660730DAAC92492403288805EBB9053F1AB060DA290000242D9364ACB93519FF"
+ "8F8B019BAA43A40D699F59714B327B8382216EF427ED52282900001C00004004"
+ "06D91438A0D6B734E152F76F5CC55A72A2E38A0A2900001C000040052EFF78B3"
+ "55B37F3CE75AFF26C721B050F892C0D6290000080000402E290000100000402F"
+ "00020003000400050000000800004014";
final String ikeAuthResp =
"46B8ECA1E0D72A18CECD871146CF83A12E20232000000001000000F0240000D4"
+ "A17BC258BA2714CF536663639DD5F665A60C75E93557CD5141990A8CEEDD2017"
+ "93F5B181C8569FBCD6C2A00198EC2B62D42BEFAC016B8B6BF6A7BC9CEDE3413A"
+ "6C495A6B8EC941864DC3E08F57D015EA6520C4B05884960B85478FCA53DA5F17"
+ "9628BB1097DA77461C71837207A9EB80720B3E6E661816EE4E14AC995B5E8441"
+ "A4C3F9097CC148142BA300076C94A23EC4ADE82B1DD2B121F7E9102860A8C3BF"
+ "58DDC207285A3176E924C44DE820322524E1AA438EFDFBA781B36084AED80846"
+ "3B77FCED9682B6E4E476408EF3F1037E";
final String rekeyChildCreateReq =
"46B8ECA1E0D72A18CECD871146CF83A12E202400000000000000015029000134"
+ "319D74B6B155B86942143CEC1D29D21F073F24B7BEDC9BFE0F0FDD8BDB5458C0"
+ "8DB93506E1A43DD0640FE7370C97F9B34FF4EC9B2DB7257A87B75632301FB68A"
+ "86B54871249534CA3D01C9BEB127B669F46470E1C8AAF72574C3CEEC15B901CF"
+ "5A0D6ADAE59C3CA64AC8C86689C860FAF9500E608DFE63F2DCD30510FD6FFCD5"
+ "A50838574132FD1D069BCACD4C7BAF45C9B1A7689FAD132E3F56DBCFAF905A8C"
+ "4145D4BA1B74A54762F8F43308D94DE05649C49D885121CE30681D51AC1E3E68"
+ "AB82F9A19B99579AFE257F32DBD1037814DA577379E4F42DEDAC84502E49C933"
+ "9EA83F6F5DB4401B660CB1681B023B8603D205DFDD1DE86AD8DE22B6B754F30D"
+ "05EAE81A709C2CEE81386133DC3DC7B5EF8F166E48E54A0722DD0C64F4D00638"
+ "40F272144C47F6ECED72A248180645DB";
final String rekeyChildDeleteReq =
"46B8ECA1E0D72A18CECD871146CF83A12E20250000000001000000502A000034"
+ "02D98DAF0432EBD991CA4F2D89C1E0EFABC6E91A3327A85D8914FB2F1485BE1B"
+ "8D3415D548F7CE0DC4224E7E9D0D3355";
final String deleteIkeReq =
"46B8ECA1E0D72A18CECD871146CF83A12E20250000000002000000502A000034"
+ "095041F4026B4634F04B0AB4F9349484F7BE9AEF03E3733EEE293330043B75D2"
+ "ABF5F965ED51127629585E1B1BBA787F";
// Open IKE Session
IkeSession ikeSession = openIkeSessionWithRemoteAddress(mRemoteAddress);
PortPair localRemotePorts = performSetupIkeAndFirstChildBlocking(ikeInitResp, ikeAuthResp);
// IKE INIT and IKE AUTH takes two exchanges. Local request message ID starts from 2
int expectedReqMsgId = 2;
int expectedRespMsgId = 0;
verifyIkeSessionSetupBlocking();
verifyChildSessionSetupBlocking(
mFirstChildSessionCallback,
Arrays.asList(TRANSPORT_MODE_INBOUND_TS),
Arrays.asList(TRANSPORT_MODE_OUTBOUND_TS),
new ArrayList<LinkAddress>());
IpSecTransformCallRecord oldTransformRecordA =
mFirstChildSessionCallback.awaitNextCreatedIpSecTransform();
IpSecTransformCallRecord oldTransformRecordB =
mFirstChildSessionCallback.awaitNextCreatedIpSecTransform();
verifyCreateIpSecTransformPair(oldTransformRecordA, oldTransformRecordB);
// Inject rekey Child requests
mTunUtils.injectPacket(buildInboundPkt(localRemotePorts, rekeyChildCreateReq));
mTunUtils.awaitResp(
IKE_DETERMINISTIC_INITIATOR_SPI, expectedRespMsgId++, true /* expectedUseEncap */);
mTunUtils.injectPacket(buildInboundPkt(localRemotePorts, rekeyChildDeleteReq));
mTunUtils.awaitResp(
IKE_DETERMINISTIC_INITIATOR_SPI, expectedRespMsgId++, true /* expectedUseEncap */);
// Verify IpSecTransforms are renewed
IpSecTransformCallRecord newTransformRecordA =
mFirstChildSessionCallback.awaitNextCreatedIpSecTransform();
IpSecTransformCallRecord newTransformRecordB =
mFirstChildSessionCallback.awaitNextCreatedIpSecTransform();
verifyCreateIpSecTransformPair(newTransformRecordA, newTransformRecordB);
verifyDeleteIpSecTransformPair(
mFirstChildSessionCallback, oldTransformRecordA, oldTransformRecordB);
// Inject delete IKE request
mTunUtils.injectPacket(buildInboundPkt(localRemotePorts, deleteIkeReq));
mTunUtils.awaitResp(
IKE_DETERMINISTIC_INITIATOR_SPI, expectedRespMsgId++, true /* expectedUseEncap */);
verifyDeleteIpSecTransformPair(
mFirstChildSessionCallback, newTransformRecordA, newTransformRecordB);
mFirstChildSessionCallback.awaitOnClosed();
mIkeSessionCallback.awaitOnClosed();
}
}

View File

@@ -15,7 +15,13 @@
package android.net.ipsec.ike.cts;
import static android.app.AppOpsManager.OP_MANAGE_IPSEC_TUNNELS;
import static android.net.ipsec.ike.IkeSessionConfiguration.EXTENSION_TYPE_FRAGMENTATION;
import static android.system.OsConstants.AF_INET;
import static android.system.OsConstants.AF_INET6;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import android.annotation.NonNull;
import android.app.AppOpsManager;
@@ -23,6 +29,7 @@ import android.content.Context;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.InetAddresses;
import android.net.IpSecManager;
import android.net.IpSecTransform;
import android.net.LinkAddress;
import android.net.Network;
@@ -33,7 +40,11 @@ import android.net.ipsec.ike.ChildSessionCallback;
import android.net.ipsec.ike.ChildSessionConfiguration;
import android.net.ipsec.ike.IkeSessionCallback;
import android.net.ipsec.ike.IkeSessionConfiguration;
import android.net.ipsec.ike.IkeSessionConnectionInfo;
import android.net.ipsec.ike.IkeTrafficSelector;
import android.net.ipsec.ike.TransportModeChildSessionParams;
import android.net.ipsec.ike.TunnelModeChildSessionParams;
import android.net.ipsec.ike.cts.IkeTunUtils.PortPair;
import android.net.ipsec.ike.cts.TestNetworkUtils.TestNetworkCallback;
import android.net.ipsec.ike.exceptions.IkeException;
import android.net.ipsec.ike.exceptions.IkeProtocolException;
@@ -55,6 +66,11 @@ import org.junit.runner.RunWith;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@@ -79,13 +95,39 @@ abstract class IkeSessionTestBase extends IkeTestBase {
// Package-wide common expected results that will be shared by all IKE/Child SA creation tests
static final String EXPECTED_REMOTE_APP_VERSION_EMPTY = "";
static final byte[] EXPECTED_PROTOCOL_ERROR_DATA_NONE = new byte[0];
static final InetAddress EXPECTED_DNS_SERVERS_ONE =
InetAddresses.parseNumericAddress("8.8.8.8");
static final InetAddress EXPECTED_DNS_SERVERS_TWO =
InetAddresses.parseNumericAddress("8.8.4.4");
static final InetAddress EXPECTED_INTERNAL_ADDR =
InetAddresses.parseNumericAddress("198.51.100.10");
static final LinkAddress EXPECTED_INTERNAL_LINK_ADDR =
new LinkAddress(EXPECTED_INTERNAL_ADDR, IP4_PREFIX_LEN);
static final IkeTrafficSelector EXPECTED_INBOUND_TS =
static final InetAddress EXPECTED_INTERNAL_ADDR_V6 =
InetAddresses.parseNumericAddress("2001:db8::2");
static final LinkAddress EXPECTED_INTERNAL_LINK_ADDR_V6 =
new LinkAddress(EXPECTED_INTERNAL_ADDR_V6, IP6_PREFIX_LEN);
static final IkeTrafficSelector TUNNEL_MODE_INBOUND_TS =
new IkeTrafficSelector(
MIN_PORT, MAX_PORT, EXPECTED_INTERNAL_ADDR, EXPECTED_INTERNAL_ADDR);
static final IkeTrafficSelector TUNNEL_MODE_OUTBOUND_TS = DEFAULT_V4_TS;
static final IkeTrafficSelector TUNNEL_MODE_INBOUND_TS_V6 =
new IkeTrafficSelector(
MIN_PORT, MAX_PORT, EXPECTED_INTERNAL_ADDR_V6, EXPECTED_INTERNAL_ADDR_V6);
static final IkeTrafficSelector TUNNEL_MODE_OUTBOUND_TS_V6 = DEFAULT_V6_TS;
// This value is align with the test vectors hex that are generated in an IPv4 environment
static final IkeTrafficSelector TRANSPORT_MODE_OUTBOUND_TS =
new IkeTrafficSelector(
MIN_PORT,
MAX_PORT,
InetAddresses.parseNumericAddress("10.138.0.2"),
InetAddresses.parseNumericAddress("10.138.0.2"));
static final long IKE_DETERMINISTIC_INITIATOR_SPI = Long.parseLong("46B8ECA1E0D72A18", 16);
// Static state to reduce setup/teardown
static Context sContext = InstrumentationRegistry.getContext();
@@ -124,19 +166,12 @@ abstract class IkeSessionTestBase extends IkeTestBase {
.getUiAutomation()
.adoptShellPermissionIdentity();
sTNM = sContext.getSystemService(TestNetworkManager.class);
// Under normal circumstances, the MANAGE_IPSEC_TUNNELS appop would be auto-granted, and
// a standard permission is insufficient. So we shell out the appop, to give us the
// right appop permissions.
setAppOp(OP_MANAGE_IPSEC_TUNNELS, true);
}
// This method is guaranteed to run in subclasses and will run after subclasses' @AfterClass
// methods.
@AfterClass
public static void tearDownPermissionAfterClass() throws Exception {
setAppOp(OP_MANAGE_IPSEC_TUNNELS, false);
InstrumentationRegistry.getInstrumentation()
.getUiAutomation()
.dropShellPermissionIdentity();
@@ -159,7 +194,7 @@ abstract class IkeSessionTestBase extends IkeTestBase {
}
void setUpTestNetwork(InetAddress localAddr) throws Exception {
int prefixLen = localAddr instanceof Inet4Address ? IP4_PREFIX_LEN : IP4_PREFIX_LEN;
int prefixLen = localAddr instanceof Inet4Address ? IP4_PREFIX_LEN : IP6_PREFIX_LEN;
TestNetworkInterface testIface =
sTNM.createTunInterface(new LinkAddress[] {new LinkAddress(localAddr, prefixLen)});
@@ -179,7 +214,7 @@ abstract class IkeSessionTestBase extends IkeTestBase {
mTunFd.close();
}
private static void setAppOp(int appop, boolean allow) {
static void setAppOp(int appop, boolean allow) {
String opName = AppOpsManager.opToName(appop);
for (String pkg : new String[] {"com.android.shell", sContext.getPackageName()}) {
String cmd =
@@ -231,6 +266,78 @@ abstract class IkeSessionTestBase extends IkeTestBase {
}
}
TransportModeChildSessionParams buildTransportModeChildParamsWithTs(
IkeTrafficSelector inboundTs, IkeTrafficSelector outboundTs) {
return new TransportModeChildSessionParams.Builder()
.addSaProposal(SaProposalTest.buildChildSaProposalWithCombinedModeCipher())
.addSaProposal(SaProposalTest.buildChildSaProposalWithNormalModeCipher())
.addInboundTrafficSelectors(inboundTs)
.addOutboundTrafficSelectors(outboundTs)
.build();
}
TunnelModeChildSessionParams buildTunnelModeChildSessionParams() {
return new TunnelModeChildSessionParams.Builder()
.addSaProposal(SaProposalTest.buildChildSaProposalWithNormalModeCipher())
.addSaProposal(SaProposalTest.buildChildSaProposalWithCombinedModeCipher())
.addInternalAddressRequest(AF_INET)
.addInternalAddressRequest(AF_INET6)
.build();
}
PortPair performSetupIkeAndFirstChildBlocking(String ikeInitRespHex, String... ikeAuthRespHexes)
throws Exception {
return performSetupIkeAndFirstChildBlocking(
ikeInitRespHex,
1 /* expectedAuthReqPktCnt */,
true /*expectedAuthUseEncap*/,
ikeAuthRespHexes);
}
PortPair performSetupIkeAndFirstChildBlocking(
String ikeInitRespHex, boolean expectedAuthUseEncap, String... ikeAuthRespHexes)
throws Exception {
return performSetupIkeAndFirstChildBlocking(
ikeInitRespHex,
1 /* expectedAuthReqPktCnt */,
expectedAuthUseEncap,
ikeAuthRespHexes);
}
PortPair performSetupIkeAndFirstChildBlocking(
String ikeInitRespHex,
int expectedAuthReqPktCnt,
boolean expectedAuthUseEncap,
String... ikeAuthRespHexes)
throws Exception {
mTunUtils.awaitReqAndInjectResp(
IKE_DETERMINISTIC_INITIATOR_SPI,
0 /* expectedMsgId */,
false /* expectedUseEncap */,
ikeInitRespHex);
byte[] ikeAuthReqPkt =
mTunUtils
.awaitReqAndInjectResp(
IKE_DETERMINISTIC_INITIATOR_SPI,
1 /* expectedMsgId */,
expectedAuthUseEncap,
expectedAuthReqPktCnt,
ikeAuthRespHexes)
.get(0);
return IkeTunUtils.getSrcDestPortPair(ikeAuthReqPkt);
}
void performCloseIkeBlocking(int expectedMsgId, String deleteIkeRespHex) throws Exception {
performCloseIkeBlocking(expectedMsgId, true /* expectedUseEncap*/, deleteIkeRespHex);
}
void performCloseIkeBlocking(
int expectedMsgId, boolean expectedUseEncap, String deleteIkeRespHex) throws Exception {
mTunUtils.awaitReqAndInjectResp(
IKE_DETERMINISTIC_INITIATOR_SPI, expectedMsgId, expectedUseEncap, deleteIkeRespHex);
}
/** Testing callback that allows caller to block current thread until a method get called */
static class TestIkeSessionCallback implements IkeSessionCallback {
private CompletableFuture<IkeSessionConfiguration> mFutureIkeConfig =
@@ -370,6 +477,109 @@ abstract class IkeSessionTestBase extends IkeTestBase {
this.ipSecTransform = ipSecTransform;
this.direction = direction;
}
@Override
public int hashCode() {
return Objects.hash(ipSecTransform, direction);
}
@Override
public boolean equals(Object o) {
if (!(o instanceof IpSecTransformCallRecord)) return false;
IpSecTransformCallRecord record = (IpSecTransformCallRecord) o;
return ipSecTransform.equals(record.ipSecTransform) && direction == record.direction;
}
}
void verifyIkeSessionSetupBlocking() throws Exception {
IkeSessionConfiguration ikeConfig = mIkeSessionCallback.awaitIkeConfig();
assertNotNull(ikeConfig);
assertEquals(EXPECTED_REMOTE_APP_VERSION_EMPTY, ikeConfig.getRemoteApplicationVersion());
assertTrue(ikeConfig.getRemoteVendorIds().isEmpty());
assertTrue(ikeConfig.getPcscfServers().isEmpty());
assertTrue(ikeConfig.isIkeExtensionEnabled(EXTENSION_TYPE_FRAGMENTATION));
IkeSessionConnectionInfo ikeConnectInfo = ikeConfig.getIkeSessionConnectionInfo();
assertNotNull(ikeConnectInfo);
assertEquals(mLocalAddress, ikeConnectInfo.getLocalAddress());
assertEquals(mRemoteAddress, ikeConnectInfo.getRemoteAddress());
assertEquals(mTunNetwork, ikeConnectInfo.getNetwork());
}
void verifyChildSessionSetupBlocking(
TestChildSessionCallback childCallback,
List<IkeTrafficSelector> expectedInboundTs,
List<IkeTrafficSelector> expectedOutboundTs,
List<LinkAddress> expectedInternalAddresses)
throws Exception {
verifyChildSessionSetupBlocking(
childCallback,
expectedInboundTs,
expectedOutboundTs,
expectedInternalAddresses,
new ArrayList<InetAddress>() /* expectedDnsServers */);
}
void verifyChildSessionSetupBlocking(
TestChildSessionCallback childCallback,
List<IkeTrafficSelector> expectedInboundTs,
List<IkeTrafficSelector> expectedOutboundTs,
List<LinkAddress> expectedInternalAddresses,
List<InetAddress> expectedDnsServers)
throws Exception {
ChildSessionConfiguration childConfig = childCallback.awaitChildConfig();
assertNotNull(childConfig);
assertEquals(expectedInboundTs, childConfig.getInboundTrafficSelectors());
assertEquals(expectedOutboundTs, childConfig.getOutboundTrafficSelectors());
assertEquals(expectedInternalAddresses, childConfig.getInternalAddresses());
assertEquals(expectedDnsServers, childConfig.getInternalDnsServers());
assertTrue(childConfig.getInternalSubnets().isEmpty());
assertTrue(childConfig.getInternalDhcpServers().isEmpty());
}
void verifyCloseIkeAndChildBlocking(
IpSecTransformCallRecord expectedTransformRecordA,
IpSecTransformCallRecord expectedTransformRecordB)
throws Exception {
verifyDeleteIpSecTransformPair(
mFirstChildSessionCallback, expectedTransformRecordA, expectedTransformRecordB);
mFirstChildSessionCallback.awaitOnClosed();
mIkeSessionCallback.awaitOnClosed();
}
static void verifyCreateIpSecTransformPair(
IpSecTransformCallRecord transformRecordA, IpSecTransformCallRecord transformRecordB) {
IpSecTransform transformA = transformRecordA.ipSecTransform;
IpSecTransform transformB = transformRecordB.ipSecTransform;
assertNotNull(transformA);
assertNotNull(transformB);
Set<Integer> expectedDirections = new HashSet<>();
expectedDirections.add(IpSecManager.DIRECTION_IN);
expectedDirections.add(IpSecManager.DIRECTION_OUT);
Set<Integer> resultDirections = new HashSet<>();
resultDirections.add(transformRecordA.direction);
resultDirections.add(transformRecordB.direction);
assertEquals(expectedDirections, resultDirections);
}
static void verifyDeleteIpSecTransformPair(
TestChildSessionCallback childCb,
IpSecTransformCallRecord expectedTransformRecordA,
IpSecTransformCallRecord expectedTransformRecordB) {
Set<IpSecTransformCallRecord> expectedTransforms = new HashSet<>();
expectedTransforms.add(expectedTransformRecordA);
expectedTransforms.add(expectedTransformRecordB);
Set<IpSecTransformCallRecord> resultTransforms = new HashSet<>();
resultTransforms.add(childCb.awaitNextDeletedIpSecTransform());
resultTransforms.add(childCb.awaitNextDeletedIpSecTransform());
assertEquals(expectedTransforms, resultTransforms);
}
/** Package private method to check if device has IPsec tunnels feature */
@@ -377,7 +587,5 @@ abstract class IkeSessionTestBase extends IkeTestBase {
return sContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS);
}
// TODO(b/148689509): Verify IKE Session setup using EAP and digital-signature-based auth
// TODO(b/148689509): Verify hostname based creation
}

View File

@@ -54,7 +54,7 @@ abstract class IkeTestBase {
static final int SUB_ID = 1;
static final byte[] EAP_IDENTITY = "test@android.net".getBytes();
static final String NETWORK_NAME = "android.net";
static final String EAP_MSCHAPV2_USERNAME = "username";
static final String EAP_MSCHAPV2_USERNAME = "mschapv2user";
static final String EAP_MSCHAPV2_PASSWORD = "password";
static final Inet4Address IPV4_ADDRESS_LOCAL =

View File

@@ -26,6 +26,8 @@ import static android.net.ipsec.ike.cts.PacketUtils.UDP_HDRLEN;
import static android.net.ipsec.ike.cts.PacketUtils.UdpHeader;
import static android.system.OsConstants.IPPROTO_UDP;
import static com.android.internal.util.HexDump.hexStringToByteArray;
import static org.junit.Assert.fail;
import android.os.ParcelFileDescriptor;
@@ -34,7 +36,10 @@ import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class IkeTunUtils extends TunUtils {
private static final int PORT_LEN = 2;
@@ -42,63 +47,132 @@ public class IkeTunUtils extends TunUtils {
private static final int NON_ESP_MARKER_LEN = 4;
private static final byte[] NON_ESP_MARKER = new byte[NON_ESP_MARKER_LEN];
private static final int IKE_HEADER_LEN = 28;
private static final int IKE_INIT_SPI_OFFSET = 0;
private static final int IKE_FIRST_PAYLOAD_OFFSET = 16;
private static final int IKE_IS_RESP_BYTE_OFFSET = 19;
private static final int IKE_MSG_ID_OFFSET = 20;
private static final int IKE_HEADER_LEN = 28;
private static final int IKE_FRAG_NUM_OFFSET = 32;
private static final int IKE_PAYLOAD_TYPE_SKF = 53;
private static final int RSP_FLAG_MASK = 0x20;
public IkeTunUtils(ParcelFileDescriptor tunFd) {
super(tunFd);
}
/**
* Await the expected IKE request and inject an IKE response.
* Await the expected IKE request inject an IKE response (or a list of response fragments)
*
* @param respIkePkt IKE response packet without IP/UDP headers or NON ESP MARKER.
* @param ikeRespDataFragmentsHex IKE response hex (or a list of response fragments) without
* IP/UDP headers or NON ESP MARKER.
*/
public byte[] awaitReqAndInjectResp(
long expectedInitIkeSpi, int expectedMsgId, boolean expectedUseEncap, byte[] respIkePkt)
long expectedInitIkeSpi,
int expectedMsgId,
boolean expectedUseEncap,
String... ikeRespDataFragmentsHex)
throws Exception {
byte[] request =
awaitIkePacket(
return awaitReqAndInjectResp(
expectedInitIkeSpi,
expectedMsgId,
false /* expectedResp */,
expectedUseEncap);
expectedUseEncap,
1 /* expectedReqPktCnt */,
ikeRespDataFragmentsHex)
.get(0);
}
/**
* Await the expected IKE request (or the list of IKE request fragments) and inject an IKE
* response (or a list of response fragments)
*
* @param ikeRespDataFragmentsHex IKE response hex (or a list of response fragments) without
* IP/UDP headers or NON ESP MARKER.
*/
public List<byte[]> awaitReqAndInjectResp(
long expectedInitIkeSpi,
int expectedMsgId,
boolean expectedUseEncap,
int expectedReqPktCnt,
String... ikeRespDataFragmentsHex)
throws Exception {
List<byte[]> reqList = new ArrayList<>(expectedReqPktCnt);
if (expectedReqPktCnt == 1) {
// Expecting one complete IKE packet
byte[] req =
awaitIkePacket(
(pkt) -> {
return isExpectedIkePkt(
pkt,
expectedInitIkeSpi,
expectedMsgId,
false /* expectedResp */,
expectedUseEncap);
});
reqList.add(req);
} else {
// Expecting "expectedReqPktCnt" number of request fragments
for (int i = 0; i < expectedReqPktCnt; i++) {
// IKE Fragment number always starts from 1
int expectedFragNum = i + 1;
byte[] req =
awaitIkePacket(
(pkt) -> {
return isExpectedIkeFragPkt(
pkt,
expectedInitIkeSpi,
expectedMsgId,
false /* expectedResp */,
expectedUseEncap,
expectedFragNum);
});
reqList.add(req);
}
}
// All request fragments have the same addresses and ports
byte[] request = reqList.get(0);
// Build response header by flipping address and port
InetAddress srcAddr = getAddress(request, false /* shouldGetSource */);
InetAddress dstAddr = getAddress(request, true /* shouldGetSource */);
int srcPort = getPort(request, false /* shouldGetSource */);
int dstPort = getPort(request, true /* shouldGetSource */);
for (String resp : ikeRespDataFragmentsHex) {
byte[] response =
buildIkePacket(
srcAddr,
dstAddr,
srcPort,
dstPort,
expectedUseEncap,
hexStringToByteArray(resp));
injectPacket(response);
}
byte[] response =
buildIkePacket(srcAddr, dstAddr, srcPort, dstPort, expectedUseEncap, respIkePkt);
injectPacket(response);
return request;
return reqList;
}
private byte[] awaitIkePacket(
long expectedInitIkeSpi,
int expectedMsgId,
boolean expectedResp,
boolean expectedUseEncap)
/** Await the expected IKE response */
public byte[] awaitResp(long expectedInitIkeSpi, int expectedMsgId, boolean expectedUseEncap)
throws Exception {
return awaitIkePacket(
(pkt) -> {
return isExpectedIkePkt(
pkt,
expectedInitIkeSpi,
expectedMsgId,
true /* expectedResp*/,
expectedUseEncap);
});
}
private byte[] awaitIkePacket(Predicate<byte[]> pktVerifier) throws Exception {
long endTime = System.currentTimeMillis() + TIMEOUT;
int startIndex = 0;
synchronized (mPackets) {
while (System.currentTimeMillis() < endTime) {
byte[] ikePkt =
getFirstMatchingPacket(
(pkt) -> {
return isIke(
pkt,
expectedInitIkeSpi,
expectedMsgId,
expectedResp,
expectedUseEncap);
},
startIndex);
byte[] ikePkt = getFirstMatchingPacket(pktVerifier, startIndex);
if (ikePkt != null) {
return ikePkt; // We've found the packet we're looking for.
}
@@ -112,51 +186,51 @@ public class IkeTunUtils extends TunUtils {
}
}
String direction = expectedResp ? "response" : "request";
fail(
"No such IKE "
+ direction
+ " found with Initiator SPI "
+ expectedInitIkeSpi
+ " and message ID "
+ expectedMsgId);
fail("No matching packet found");
}
throw new IllegalStateException(
"Hit an impossible case where fail() didn't throw an exception");
}
private static boolean isIke(
private static boolean isExpectedIkePkt(
byte[] pkt,
long expectedInitIkeSpi,
int expectedMsgId,
boolean expectedResp,
boolean expectedUseEncap) {
int ipProtocolOffset = 0;
int ikeOffset = 0;
if (isIpv6(pkt)) {
// IPv6 UDP expectedUseEncap not supported by kernels; assume non-expectedUseEncap.
ipProtocolOffset = IP6_PROTO_OFFSET;
ikeOffset = IP6_HDRLEN + UDP_HDRLEN;
} else {
// Use default IPv4 header length (assuming no options)
ipProtocolOffset = IP4_PROTO_OFFSET;
ikeOffset = IP4_HDRLEN + UDP_HDRLEN;
if (expectedUseEncap) {
if (hasNonEspMarker(pkt)) {
ikeOffset += NON_ESP_MARKER_LEN;
} else {
return false;
}
}
}
int ipProtocolOffset = isIpv6(pkt) ? IP6_PROTO_OFFSET : IP4_PROTO_OFFSET;
int ikeOffset = getIkeOffset(pkt, expectedUseEncap);
return pkt[ipProtocolOffset] == IPPROTO_UDP
&& areSpiAndMsgIdEqual(
&& expectedUseEncap == hasNonEspMarker(pkt)
&& isExpectedSpiAndMsgId(
pkt, ikeOffset, expectedInitIkeSpi, expectedMsgId, expectedResp);
}
private static boolean isExpectedIkeFragPkt(
byte[] pkt,
long expectedInitIkeSpi,
int expectedMsgId,
boolean expectedResp,
boolean expectedUseEncap,
int expectedFragNum) {
return isExpectedIkePkt(
pkt, expectedInitIkeSpi, expectedMsgId, expectedResp, expectedUseEncap)
&& isExpectedFragNum(pkt, getIkeOffset(pkt, expectedUseEncap), expectedFragNum);
}
private static int getIkeOffset(byte[] pkt, boolean useEncap) {
if (isIpv6(pkt)) {
// IPv6 UDP expectedUseEncap not supported by kernels; assume non-expectedUseEncap.
return IP6_HDRLEN + UDP_HDRLEN;
} else {
// Use default IPv4 header length (assuming no options)
int ikeOffset = IP4_HDRLEN + UDP_HDRLEN;
return useEncap ? ikeOffset + NON_ESP_MARKER_LEN : ikeOffset;
}
}
private static boolean hasNonEspMarker(byte[] pkt) {
ByteBuffer buffer = ByteBuffer.wrap(pkt);
int ikeOffset = IP4_HDRLEN + UDP_HDRLEN;
@@ -170,23 +244,81 @@ public class IkeTunUtils extends TunUtils {
return Arrays.equals(NON_ESP_MARKER, nonEspMarker);
}
private static boolean areSpiAndMsgIdEqual(
private static boolean isExpectedSpiAndMsgId(
byte[] pkt,
int ikeOffset,
long expectedIkeInitSpi,
long expectedInitIkeSpi,
int expectedMsgId,
boolean expectedResp) {
if (pkt.length <= ikeOffset + IKE_HEADER_LEN) return false;
ByteBuffer buffer = ByteBuffer.wrap(pkt);
buffer.get(new byte[ikeOffset]); // Skip IP, UDP header (and NON_ESP_MARKER)
buffer.mark(); // Mark this position so that later we can reset back here
// Check message ID.
// Check SPI
buffer.get(new byte[IKE_INIT_SPI_OFFSET]);
long initSpi = buffer.getLong();
if (expectedInitIkeSpi != initSpi) {
return false;
}
// Check direction
buffer.reset();
buffer.get(new byte[IKE_IS_RESP_BYTE_OFFSET]);
byte flagsByte = buffer.get();
boolean isResp = ((flagsByte & RSP_FLAG_MASK) != 0);
if (expectedResp != isResp) {
return false;
}
// Check message ID
buffer.reset();
buffer.get(new byte[IKE_MSG_ID_OFFSET]);
int msgId = buffer.getInt();
return expectedMsgId == msgId;
// TODO: Check SPI and packet direction
// Both the expected message ID and the packet's msgId are signed integers, so directly
// compare them.
int msgId = buffer.getInt();
if (expectedMsgId != msgId) {
return false;
}
return true;
}
private static boolean isExpectedFragNum(byte[] pkt, int ikeOffset, int expectedFragNum) {
ByteBuffer buffer = ByteBuffer.wrap(pkt);
buffer.get(new byte[ikeOffset]);
buffer.mark(); // Mark this position so that later we can reset back here
// Check if it is a fragment packet
buffer.get(new byte[IKE_FIRST_PAYLOAD_OFFSET]);
int firstPayload = Byte.toUnsignedInt(buffer.get());
if (firstPayload != IKE_PAYLOAD_TYPE_SKF) {
return false;
}
// Check fragment number
buffer.reset();
buffer.get(new byte[IKE_FRAG_NUM_OFFSET]);
int fragNum = Short.toUnsignedInt(buffer.getShort());
return expectedFragNum == fragNum;
}
public static class PortPair {
public final int srcPort;
public final int dstPort;
public PortPair(int sourcePort, int destinationPort) {
srcPort = sourcePort;
dstPort = destinationPort;
}
}
public static PortPair getSrcDestPortPair(byte[] outboundIkePkt) throws Exception {
return new PortPair(
getPort(outboundIkePkt, true /* shouldGetSource */),
getPort(outboundIkePkt, false /* shouldGetSource */));
}
private static InetAddress getAddress(byte[] pkt, boolean shouldGetSource) throws Exception {
@@ -210,7 +342,7 @@ public class IkeTunUtils extends TunUtils {
return Short.toUnsignedInt(buffer.getShort());
}
private static byte[] buildIkePacket(
public static byte[] buildIkePacket(
InetAddress srcAddr,
InetAddress dstAddr,
int srcPort,

View File

@@ -47,7 +47,7 @@ public class TunUtils {
private static final String TAG = TunUtils.class.getSimpleName();
private static final int DATA_BUFFER_LEN = 4096;
static final int TIMEOUT = 100;
static final int TIMEOUT = 500;
static final int IP4_PROTO_OFFSET = 9;
static final int IP6_PROTO_OFFSET = 6;

View File

@@ -0,0 +1,271 @@
/*
* Copyright (C) 2020 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 android.net.cts
import android.Manifest.permission.MANAGE_TEST_NETWORKS
import android.Manifest.permission.NETWORK_SETTINGS
import android.content.Context
import android.net.ConnectivityManager
import android.net.EthernetManager
import android.net.InetAddresses
import android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL
import android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED
import android.net.NetworkCapabilities.TRANSPORT_ETHERNET
import android.net.NetworkRequest
import android.net.TestNetworkInterface
import android.net.TestNetworkManager
import android.net.Uri
import android.net.dhcp.DhcpDiscoverPacket
import android.net.dhcp.DhcpPacket
import android.net.dhcp.DhcpPacket.DHCP_MESSAGE_TYPE
import android.net.dhcp.DhcpPacket.DHCP_MESSAGE_TYPE_DISCOVER
import android.net.dhcp.DhcpPacket.DHCP_MESSAGE_TYPE_REQUEST
import android.net.dhcp.DhcpRequestPacket
import android.net.shared.Inet4AddressUtils.getBroadcastAddress
import android.net.shared.Inet4AddressUtils.getPrefixMaskAsInet4Address
import android.os.Build
import android.os.HandlerThread
import android.platform.test.annotations.AppModeFull
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.runner.AndroidJUnit4
import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
import com.android.compatibility.common.util.ThrowingRunnable
import com.android.server.util.NetworkStackConstants.IPV4_ADDR_ANY
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DhcpClientPacketFilter
import com.android.testutils.DhcpOptionFilter
import com.android.testutils.RecorderCallback.CallbackEntry
import com.android.testutils.TapPacketReader
import com.android.testutils.TestableNetworkCallback
import fi.iki.elonen.NanoHTTPD
import org.junit.After
import org.junit.Assume.assumeFalse
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import java.net.Inet4Address
import java.util.concurrent.ArrayBlockingQueue
import java.util.concurrent.TimeUnit
import kotlin.reflect.KClass
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
import kotlin.test.fail
private const val MAX_PACKET_LENGTH = 1500
private const val TEST_TIMEOUT_MS = 10_000L
private const val TEST_LEASE_TIMEOUT_SECS = 3600 * 12
private const val TEST_PREFIX_LENGTH = 24
private const val TEST_LOGIN_URL = "https://login.capport.android.com"
private const val TEST_VENUE_INFO_URL = "https://venueinfo.capport.android.com"
private const val TEST_DOMAIN_NAME = "lan"
private const val TEST_MTU = 1500.toShort()
@AppModeFull(reason = "Instant apps cannot create test networks")
@RunWith(AndroidJUnit4::class)
class CaptivePortalApiTest {
@JvmField
@Rule
val ignoreRule = DevSdkIgnoreRule(ignoreClassUpTo = Build.VERSION_CODES.Q)
private val context by lazy { InstrumentationRegistry.getInstrumentation().context }
private val tnm by lazy { context.assertHasService(TestNetworkManager::class.java) }
private val eth by lazy { context.assertHasService(EthernetManager::class.java) }
private val cm by lazy { context.assertHasService(ConnectivityManager::class.java) }
private val handlerThread = HandlerThread(CaptivePortalApiTest::class.simpleName)
private val serverIpAddr = InetAddresses.parseNumericAddress("192.0.2.222") as Inet4Address
private val clientIpAddr = InetAddresses.parseNumericAddress("192.0.2.111") as Inet4Address
private val httpServer = HttpServer()
private val ethRequest = NetworkRequest.Builder()
// ETHERNET|TEST transport networks do not have NET_CAPABILITY_TRUSTED
.removeCapability(NET_CAPABILITY_TRUSTED)
.addTransportType(TRANSPORT_ETHERNET).build()
private val ethRequestCb = TestableNetworkCallback()
private lateinit var iface: TestNetworkInterface
private lateinit var reader: TapPacketReader
private lateinit var capportUrl: Uri
private var testSkipped = false
@Before
fun setUp() {
// This test requires using a tap interface as the default ethernet interface: skip if there
// is already an ethernet interface connected.
testSkipped = eth.isAvailable()
assumeFalse(testSkipped)
// Register a request so the network does not get torn down
cm.requestNetwork(ethRequest, ethRequestCb)
runAsShell(NETWORK_SETTINGS, MANAGE_TEST_NETWORKS) {
eth.setIncludeTestInterfaces(true)
// Keeping a reference to the test interface also makes sure the ParcelFileDescriptor
// does not go out of scope, which would cause it to close the underlying FileDescriptor
// in its finalizer.
iface = tnm.createTapInterface()
}
handlerThread.start()
reader = TapPacketReader(
handlerThread.threadHandler,
iface.fileDescriptor.fileDescriptor,
MAX_PACKET_LENGTH)
handlerThread.threadHandler.post { reader.start() }
httpServer.start()
// Pad the listening port to make sure it is always of length 5. This ensures the URL has
// always the same length so the test can use constant IP and UDP header lengths.
// The maximum port number is 65535 so a length of 5 is always enough.
capportUrl = Uri.parse("http://localhost:${httpServer.listeningPort}/testapi.html?par=val")
}
@After
fun tearDown() {
if (testSkipped) return
cm.unregisterNetworkCallback(ethRequestCb)
runAsShell(NETWORK_SETTINGS) { eth.setIncludeTestInterfaces(false) }
httpServer.stop()
handlerThread.threadHandler.post { reader.stop() }
handlerThread.quitSafely()
iface.fileDescriptor.close()
}
@Test
fun testApiCallbacks() {
// Handle the DHCP handshake that includes the capport API URL
val discover = reader.assertDhcpPacketReceived(
DhcpDiscoverPacket::class, TEST_TIMEOUT_MS, DHCP_MESSAGE_TYPE_DISCOVER)
reader.sendResponse(makeOfferPacket(discover.clientMac, discover.transactionId))
val request = reader.assertDhcpPacketReceived(
DhcpRequestPacket::class, TEST_TIMEOUT_MS, DHCP_MESSAGE_TYPE_REQUEST)
assertEquals(discover.transactionId, request.transactionId)
assertEquals(clientIpAddr, request.mRequestedIp)
reader.sendResponse(makeAckPacket(request.clientMac, request.transactionId))
// Expect a request to the capport API
val capportReq = httpServer.recordedRequests.poll(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS)
assertNotNull(capportReq, "The device did not fetch captive portal API data within timeout")
assertEquals(capportUrl.path, capportReq.uri)
assertEquals(capportUrl.query, capportReq.queryParameterString)
// Expect network callbacks with capport info
val testCb = TestableNetworkCallback(TEST_TIMEOUT_MS)
// LinkProperties do not contain captive portal info if the callback is registered without
// NETWORK_SETTINGS permissions.
val lp = runAsShell(NETWORK_SETTINGS) {
cm.registerNetworkCallback(ethRequest, testCb)
try {
val ncCb = testCb.eventuallyExpect<CallbackEntry.CapabilitiesChanged> {
it.caps.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL)
}
testCb.eventuallyExpect<CallbackEntry.LinkPropertiesChanged> {
it.network == ncCb.network && it.lp.captivePortalData != null
}.lp
} finally {
cm.unregisterNetworkCallback(testCb)
}
}
assertEquals(capportUrl, lp.captivePortalApiUrl)
with(lp.captivePortalData) {
assertNotNull(this)
assertTrue(isCaptive)
assertEquals(Uri.parse(TEST_LOGIN_URL), userPortalUrl)
assertEquals(Uri.parse(TEST_VENUE_INFO_URL), venueInfoUrl)
}
}
private fun makeOfferPacket(clientMac: ByteArray, transactionId: Int) =
DhcpPacket.buildOfferPacket(DhcpPacket.ENCAP_L2, transactionId,
false /* broadcast */, serverIpAddr, IPV4_ADDR_ANY /* relayIp */, clientIpAddr,
clientMac, TEST_LEASE_TIMEOUT_SECS,
getPrefixMaskAsInet4Address(TEST_PREFIX_LENGTH),
getBroadcastAddress(clientIpAddr, TEST_PREFIX_LENGTH),
listOf(serverIpAddr) /* gateways */, listOf(serverIpAddr) /* dnsServers */,
serverIpAddr, TEST_DOMAIN_NAME, null /* hostname */, true /* metered */,
TEST_MTU, capportUrl.toString())
private fun makeAckPacket(clientMac: ByteArray, transactionId: Int) =
DhcpPacket.buildAckPacket(DhcpPacket.ENCAP_L2, transactionId,
false /* broadcast */, serverIpAddr, IPV4_ADDR_ANY /* relayIp */, clientIpAddr,
clientIpAddr /* requestClientIp */, clientMac, TEST_LEASE_TIMEOUT_SECS,
getPrefixMaskAsInet4Address(TEST_PREFIX_LENGTH),
getBroadcastAddress(clientIpAddr, TEST_PREFIX_LENGTH),
listOf(serverIpAddr) /* gateways */, listOf(serverIpAddr) /* dnsServers */,
serverIpAddr, TEST_DOMAIN_NAME, null /* hostname */, true /* metered */,
TEST_MTU, false /* rapidCommit */, capportUrl.toString())
private fun parseDhcpPacket(bytes: ByteArray) = DhcpPacket.decodeFullPacket(
bytes, MAX_PACKET_LENGTH, DhcpPacket.ENCAP_L2)
}
/**
* A minimal HTTP server running on localhost (loopback), on a random available port.
*
* The server records each request in [recordedRequests] and will not serve any further request
* until the last one is removed from the queue for verification.
*/
private class HttpServer : NanoHTTPD("localhost", 0 /* auto-select the port */) {
val recordedRequests = ArrayBlockingQueue<IHTTPSession>(1 /* capacity */)
override fun serve(session: IHTTPSession): Response {
recordedRequests.offer(session)
return newFixedLengthResponse("""
|{
| "captive": true,
| "user-portal-url": "$TEST_LOGIN_URL",
| "venue-info-url": "$TEST_VENUE_INFO_URL"
|}
""".trimMargin())
}
}
private fun <T : DhcpPacket> TapPacketReader.assertDhcpPacketReceived(
packetType: KClass<T>,
timeoutMs: Long,
type: Byte
): T {
val packetBytes = popPacket(timeoutMs, DhcpClientPacketFilter()
.and(DhcpOptionFilter(DHCP_MESSAGE_TYPE, type)))
?: fail("${packetType.simpleName} not received within timeout")
val packet = DhcpPacket.decodeFullPacket(packetBytes, packetBytes.size, DhcpPacket.ENCAP_L2)
assertTrue(packetType.isInstance(packet),
"Expected ${packetType.simpleName} but got ${packet.javaClass.simpleName}")
return packetType.java.cast(packet)
}
private fun <T> Context.assertHasService(manager: Class<T>): T {
return getSystemService(manager) ?: fail("Service $manager not found")
}
/**
* Wrapper around runWithShellPermissionIdentity with kotlin-like syntax.
*/
private fun <T> runAsShell(vararg permissions: String, task: () -> T): T {
var ret: T? = null
runWithShellPermissionIdentity(ThrowingRunnable { ret = task() }, *permissions)
return ret ?: fail("ThrowingRunnable was not run")
}