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 Length + Next 55 * Header). Note: Payload data, Pad Length and Next Header will need to be padded to be multiple 56 * of the block size of a cipher, and at the same time be aligned on a 4-byte boundary. 57 */ 58 private static final int GENERIC_ESP_OVERHEAD_MAX_V4 = 78; 59 60 /** 61 * Max ESP overhead possible 62 * 63 * <p>40 (Outer IPv6) + 4 (SPI) + 4 (Seq) + 2 (Pad Length + Next Header). Note: Payload data, 64 * Pad Length and Next Header will need to be padded to be multiple of the block size of a 65 * cipher, and at the same time be aligned on a 4-byte boundary. 66 */ 67 private static final int GENERIC_ESP_OVERHEAD_MAX_V6 = 50; 68 69 /** Maximum overheads of authentication algorithms, keyed on IANA-defined constants */ 70 private static final Map<Integer, Integer> AUTH_ALGORITHM_OVERHEAD; 71 72 static { 73 final Map<Integer, Integer> map = new ArrayMap<>(); map.put(INTEGRITY_ALGORITHM_NONE, 0)74 map.put(INTEGRITY_ALGORITHM_NONE, 0); map.put(INTEGRITY_ALGORITHM_HMAC_SHA1_96, 12)75 map.put(INTEGRITY_ALGORITHM_HMAC_SHA1_96, 12); map.put(INTEGRITY_ALGORITHM_AES_XCBC_96, 12)76 map.put(INTEGRITY_ALGORITHM_AES_XCBC_96, 12); map.put(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128, 32)77 map.put(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128, 32); map.put(INTEGRITY_ALGORITHM_HMAC_SHA2_384_192, 48)78 map.put(INTEGRITY_ALGORITHM_HMAC_SHA2_384_192, 48); map.put(INTEGRITY_ALGORITHM_HMAC_SHA2_512_256, 64)79 map.put(INTEGRITY_ALGORITHM_HMAC_SHA2_512_256, 64); map.put(INTEGRITY_ALGORITHM_AES_CMAC_96, 12)80 map.put(INTEGRITY_ALGORITHM_AES_CMAC_96, 12); 81 82 AUTH_ALGORITHM_OVERHEAD = unmodifiableMap(map); 83 } 84 85 /** Maximum overheads of encryption algorithms, keyed on IANA-defined constants */ 86 private static final Map<Integer, Integer> CRYPT_ALGORITHM_OVERHEAD; 87 88 static { 89 final Map<Integer, Integer> map = new ArrayMap<>(); map.put(ENCRYPTION_ALGORITHM_3DES, 15)90 map.put(ENCRYPTION_ALGORITHM_3DES, 15); // 8 (IV) + 7 (Max pad) map.put(ENCRYPTION_ALGORITHM_AES_CBC, 31)91 map.put(ENCRYPTION_ALGORITHM_AES_CBC, 31); // 16 (IV) + 15 (Max pad) map.put(ENCRYPTION_ALGORITHM_AES_CTR, 11)92 map.put(ENCRYPTION_ALGORITHM_AES_CTR, 11); // 8 (IV) + 3 (Max pad) 93 94 CRYPT_ALGORITHM_OVERHEAD = unmodifiableMap(map); 95 } 96 97 /** Maximum overheads of combined mode algorithms, keyed on IANA-defined constants */ 98 private static final Map<Integer, Integer> AUTHCRYPT_ALGORITHM_OVERHEAD; 99 100 static { 101 final Map<Integer, Integer> map = new ArrayMap<>(); map.put(ENCRYPTION_ALGORITHM_AES_GCM_8, 19)102 map.put(ENCRYPTION_ALGORITHM_AES_GCM_8, 19); // 8 (IV) + 3 (Max pad) + 8 (ICV) map.put(ENCRYPTION_ALGORITHM_AES_GCM_12, 23)103 map.put(ENCRYPTION_ALGORITHM_AES_GCM_12, 23); // 8 (IV) + 3 (Max pad) + 12 (ICV) map.put(ENCRYPTION_ALGORITHM_AES_GCM_16, 27)104 map.put(ENCRYPTION_ALGORITHM_AES_GCM_16, 27); // 8 (IV) + 3 (Max pad) + 16 (ICV) map.put(ENCRYPTION_ALGORITHM_CHACHA20_POLY1305, 27)105 map.put(ENCRYPTION_ALGORITHM_CHACHA20_POLY1305, 27); // 8 (IV) + 3 (Max pad) + 16 (ICV) 106 107 AUTHCRYPT_ALGORITHM_OVERHEAD = unmodifiableMap(map); 108 } 109 110 /** 111 * Calculates the MTU of the inner interface based on the parameters provided 112 * 113 * <p>The MTU of the inner interface will be the minimum of the following: 114 * 115 * <ul> 116 * <li>The MTU of the outer interface, minus the greatest ESP overhead (based on proposed 117 * algorithms). 118 * <li>The maximum MTU as provided in the arguments. 119 * </ul> 120 */ getMtu( @onNull List<ChildSaProposal> childProposals, int maxMtu, int underlyingMtu, boolean isIpv4)121 public static int getMtu( 122 @NonNull List<ChildSaProposal> childProposals, 123 int maxMtu, 124 int underlyingMtu, 125 boolean isIpv4) { 126 if (underlyingMtu <= 0) { 127 return IPV6_MIN_MTU; 128 } 129 130 int maxAuthOverhead = 0; 131 int maxCryptOverhead = 0; 132 int maxAuthCryptOverhead = 0; 133 134 for (ChildSaProposal proposal : childProposals) { 135 for (Pair<Integer, Integer> encryptionAlgoPair : proposal.getEncryptionAlgorithms()) { 136 final int algo = encryptionAlgoPair.first; 137 138 if (AUTHCRYPT_ALGORITHM_OVERHEAD.containsKey(algo)) { 139 maxAuthCryptOverhead = 140 max(maxAuthCryptOverhead, AUTHCRYPT_ALGORITHM_OVERHEAD.get(algo)); 141 continue; 142 } else if (CRYPT_ALGORITHM_OVERHEAD.containsKey(algo)) { 143 maxCryptOverhead = max(maxCryptOverhead, CRYPT_ALGORITHM_OVERHEAD.get(algo)); 144 continue; 145 } 146 147 Slog.wtf(TAG, "Unknown encryption algorithm requested: " + algo); 148 return IPV6_MIN_MTU; 149 } 150 151 for (int algo : proposal.getIntegrityAlgorithms()) { 152 if (AUTH_ALGORITHM_OVERHEAD.containsKey(algo)) { 153 maxAuthOverhead = max(maxAuthOverhead, AUTH_ALGORITHM_OVERHEAD.get(algo)); 154 continue; 155 } 156 157 Slog.wtf(TAG, "Unknown integrity algorithm requested: " + algo); 158 return IPV6_MIN_MTU; 159 } 160 } 161 162 final int genericEspOverheadMax = 163 isIpv4 ? GENERIC_ESP_OVERHEAD_MAX_V4 : GENERIC_ESP_OVERHEAD_MAX_V6; 164 165 // Return minimum of maxMtu, and the adjusted MTUs based on algorithms. 166 final int combinedModeMtu = underlyingMtu - maxAuthCryptOverhead - genericEspOverheadMax; 167 final int normalModeMtu = 168 underlyingMtu - maxCryptOverhead - maxAuthOverhead - genericEspOverheadMax; 169 return Math.min(Math.min(maxMtu, combinedModeMtu), normalModeMtu); 170 } 171 } 172