1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.vcn.util; 18 19 import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_3DES; 20 import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_CBC; 21 import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_CTR; 22 import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12; 23 import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16; 24 import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8; 25 import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_CHACHA20_POLY1305; 26 import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_AES_CMAC_96; 27 import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96; 28 import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96; 29 import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128; 30 import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192; 31 import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256; 32 import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_NONE; 33 34 import static com.android.net.module.util.NetworkStackConstants.IPV6_MIN_MTU; 35 36 import static java.lang.Math.max; 37 import static java.util.Collections.unmodifiableMap; 38 39 import android.annotation.NonNull; 40 import android.net.ipsec.ike.ChildSaProposal; 41 import android.util.ArrayMap; 42 import android.util.Pair; 43 import android.util.Slog; 44 45 import java.util.List; 46 import java.util.Map; 47 48 /** @hide */ 49 public class MtuUtils { 50 private static final String TAG = MtuUtils.class.getSimpleName(); 51 /** 52 * Max ESP overhead possible 53 * 54 * <p>60 (Outer IPv4 + options) + 8 (UDP encap) + 4 (SPI) + 4 (Seq) + 2 (Pad + NextHeader) 55 */ 56 private static final int GENERIC_ESP_OVERHEAD_MAX = 78; 57 58 /** Maximum overheads of authentication algorithms, keyed on IANA-defined constants */ 59 private static final Map<Integer, Integer> AUTH_ALGORITHM_OVERHEAD; 60 61 static { 62 final Map<Integer, Integer> map = new ArrayMap<>(); map.put(INTEGRITY_ALGORITHM_NONE, 0)63 map.put(INTEGRITY_ALGORITHM_NONE, 0); map.put(INTEGRITY_ALGORITHM_HMAC_SHA1_96, 12)64 map.put(INTEGRITY_ALGORITHM_HMAC_SHA1_96, 12); map.put(INTEGRITY_ALGORITHM_AES_XCBC_96, 12)65 map.put(INTEGRITY_ALGORITHM_AES_XCBC_96, 12); map.put(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128, 32)66 map.put(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128, 32); map.put(INTEGRITY_ALGORITHM_HMAC_SHA2_384_192, 48)67 map.put(INTEGRITY_ALGORITHM_HMAC_SHA2_384_192, 48); map.put(INTEGRITY_ALGORITHM_HMAC_SHA2_512_256, 64)68 map.put(INTEGRITY_ALGORITHM_HMAC_SHA2_512_256, 64); map.put(INTEGRITY_ALGORITHM_AES_CMAC_96, 12)69 map.put(INTEGRITY_ALGORITHM_AES_CMAC_96, 12); 70 71 AUTH_ALGORITHM_OVERHEAD = unmodifiableMap(map); 72 } 73 74 /** Maximum overheads of encryption algorithms, keyed on IANA-defined constants */ 75 private static final Map<Integer, Integer> CRYPT_ALGORITHM_OVERHEAD; 76 77 static { 78 final Map<Integer, Integer> map = new ArrayMap<>(); map.put(ENCRYPTION_ALGORITHM_3DES, 15)79 map.put(ENCRYPTION_ALGORITHM_3DES, 15); // 8 (IV) + 7 (Max pad) map.put(ENCRYPTION_ALGORITHM_AES_CBC, 31)80 map.put(ENCRYPTION_ALGORITHM_AES_CBC, 31); // 16 (IV) + 15 (Max pad) map.put(ENCRYPTION_ALGORITHM_AES_CTR, 11)81 map.put(ENCRYPTION_ALGORITHM_AES_CTR, 11); // 8 (IV) + 3 (Max pad) 82 83 CRYPT_ALGORITHM_OVERHEAD = unmodifiableMap(map); 84 } 85 86 /** Maximum overheads of combined mode algorithms, keyed on IANA-defined constants */ 87 private static final Map<Integer, Integer> AUTHCRYPT_ALGORITHM_OVERHEAD; 88 89 static { 90 final Map<Integer, Integer> map = new ArrayMap<>(); map.put(ENCRYPTION_ALGORITHM_AES_GCM_8, 19)91 map.put(ENCRYPTION_ALGORITHM_AES_GCM_8, 19); // 8 (IV) + 3 (Max pad) + 8 (ICV) map.put(ENCRYPTION_ALGORITHM_AES_GCM_12, 23)92 map.put(ENCRYPTION_ALGORITHM_AES_GCM_12, 23); // 8 (IV) + 3 (Max pad) + 12 (ICV) map.put(ENCRYPTION_ALGORITHM_AES_GCM_16, 27)93 map.put(ENCRYPTION_ALGORITHM_AES_GCM_16, 27); // 8 (IV) + 3 (Max pad) + 16 (ICV) map.put(ENCRYPTION_ALGORITHM_CHACHA20_POLY1305, 27)94 map.put(ENCRYPTION_ALGORITHM_CHACHA20_POLY1305, 27); // 8 (IV) + 3 (Max pad) + 16 (ICV) 95 96 AUTHCRYPT_ALGORITHM_OVERHEAD = unmodifiableMap(map); 97 } 98 99 /** 100 * Calculates the MTU of the inner interface based on the parameters provided 101 * 102 * <p>The MTU of the inner interface will be the minimum of the following: 103 * 104 * <ul> 105 * <li>The MTU of the outer interface, minus the greatest ESP overhead (based on proposed 106 * algorithms). 107 * <li>The maximum MTU as provided in the arguments. 108 * </ul> 109 */ getMtu( @onNull List<ChildSaProposal> childProposals, int maxMtu, int underlyingMtu)110 public static int getMtu( 111 @NonNull List<ChildSaProposal> childProposals, int maxMtu, int underlyingMtu) { 112 if (underlyingMtu <= 0) { 113 return IPV6_MIN_MTU; 114 } 115 116 int maxAuthOverhead = 0; 117 int maxCryptOverhead = 0; 118 int maxAuthCryptOverhead = 0; 119 120 for (ChildSaProposal proposal : childProposals) { 121 for (Pair<Integer, Integer> encryptionAlgoPair : proposal.getEncryptionAlgorithms()) { 122 final int algo = encryptionAlgoPair.first; 123 124 if (AUTHCRYPT_ALGORITHM_OVERHEAD.containsKey(algo)) { 125 maxAuthCryptOverhead = 126 max(maxAuthCryptOverhead, AUTHCRYPT_ALGORITHM_OVERHEAD.get(algo)); 127 continue; 128 } else if (CRYPT_ALGORITHM_OVERHEAD.containsKey(algo)) { 129 maxCryptOverhead = max(maxCryptOverhead, CRYPT_ALGORITHM_OVERHEAD.get(algo)); 130 continue; 131 } 132 133 Slog.wtf(TAG, "Unknown encryption algorithm requested: " + algo); 134 return IPV6_MIN_MTU; 135 } 136 137 for (int algo : proposal.getIntegrityAlgorithms()) { 138 if (AUTH_ALGORITHM_OVERHEAD.containsKey(algo)) { 139 maxAuthOverhead = max(maxAuthOverhead, AUTH_ALGORITHM_OVERHEAD.get(algo)); 140 continue; 141 } 142 143 Slog.wtf(TAG, "Unknown integrity algorithm requested: " + algo); 144 return IPV6_MIN_MTU; 145 } 146 } 147 148 // Return minimum of maxMtu, and the adjusted MTUs based on algorithms. 149 final int combinedModeMtu = underlyingMtu - maxAuthCryptOverhead - GENERIC_ESP_OVERHEAD_MAX; 150 final int normalModeMtu = 151 underlyingMtu - maxCryptOverhead - maxAuthOverhead - GENERIC_ESP_OVERHEAD_MAX; 152 return Math.min(Math.min(maxMtu, combinedModeMtu), normalModeMtu); 153 } 154 } 155