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