blob: ad544a919db37c354ab4cb03a4ad9eb273906e93 [file] [log] [blame]
Neale Rannsc87b66c2019-02-07 07:26:12 -08001/*
2 * ipsec_tun.h : IPSEC tunnel protection
3 *
4 * Copyright (c) 2015 Cisco and/or its affiliates.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at:
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#include <vnet/ipsec/ipsec_tun.h>
19#include <vnet/ipsec/esp.h>
20#include <vnet/udp/udp.h>
21
22/**
23 * Pool of tunnel protection objects
24 */
25ipsec_tun_protect_t *ipsec_protect_pool;
26
27/**
28 * DB of protected tunnels
29 */
30typedef struct ipsec_protect_db_t_
31{
32 u32 *tunnels;
33 u32 count;
34} ipsec_protect_db_t;
35
36static ipsec_protect_db_t ipsec_protect_db;
37
38static int
39ipsec_tun_protect_feature_set (ipsec_tun_protect_t * itp, u8 enable)
40{
41 u32 sai = itp->itp_out_sa;
Neale Rannsb3259832019-09-27 13:32:02 +000042 int rv;
Neale Rannsc87b66c2019-02-07 07:26:12 -080043
Neale Rannsb3259832019-09-27 13:32:02 +000044 const char *enc_node = (ip46_address_is_ip4 (&itp->itp_tun.src) ?
45 "esp4-encrypt-tun" : "esp6-encrypt-tun");
Neale Rannsc87b66c2019-02-07 07:26:12 -080046
Neale Rannsb3259832019-09-27 13:32:02 +000047 if (itp->itp_flags & IPSEC_PROTECT_L2)
Neale Rannsc87b66c2019-02-07 07:26:12 -080048 {
Neale Rannsb3259832019-09-27 13:32:02 +000049 rv = vnet_feature_enable_disable ("ethernet-output",
50 enc_node,
51 itp->itp_sw_if_index, enable,
52 &sai, sizeof (sai));
Neale Rannsc87b66c2019-02-07 07:26:12 -080053 }
54 else
55 {
Neale Rannsb3259832019-09-27 13:32:02 +000056 rv = vnet_feature_enable_disable ("ip4-output",
57 enc_node,
58 itp->itp_sw_if_index, enable,
59 &sai, sizeof (sai));
60 rv = vnet_feature_enable_disable ("ip6-output",
61 enc_node,
62 itp->itp_sw_if_index, enable,
63 &sai, sizeof (sai));
Neale Rannsc87b66c2019-02-07 07:26:12 -080064 }
Neale Rannsc87b66c2019-02-07 07:26:12 -080065 ASSERT (!rv);
66 return (rv);
67}
68
69static void
70ipsec_tun_protect_db_add (ipsec_main_t * im, const ipsec_tun_protect_t * itp)
71{
72 const ipsec_sa_t *sa;
73 u32 sai;
74
75 /* *INDENT-OFF* */
76 FOR_EACH_IPSEC_PROTECT_INPUT_SAI(itp, sai,
77 ({
78 sa = ipsec_sa_get (sai);
79
80 ipsec_tun_lkup_result_t res = {
81 .tun_index = itp - ipsec_protect_pool,
82 .sa_index = sai,
83 };
84
85 /*
86 * The key is formed from the tunnel's destination
87 * as the packet lookup is done from the packet's source
88 */
89 if (ip46_address_is_ip4 (&itp->itp_crypto.dst))
90 {
91 ipsec4_tunnel_key_t key = {
Neale Ranns41afb332019-07-16 06:19:35 -070092 .remote_ip = itp->itp_crypto.dst.ip4,
Neale Rannsc87b66c2019-02-07 07:26:12 -080093 .spi = clib_host_to_net_u32 (sa->spi),
94 };
95 hash_set (im->tun4_protect_by_key, key.as_u64, res.as_u64);
Neale Ranns41afb332019-07-16 06:19:35 -070096 if (1 == hash_elts(im->tun4_protect_by_key))
97 udp_register_dst_port (vlib_get_main(),
98 UDP_DST_PORT_ipsec,
99 ipsec4_tun_input_node.index, 1);
Neale Rannsc87b66c2019-02-07 07:26:12 -0800100 }
101 else
102 {
103 ipsec6_tunnel_key_t key = {
104 .remote_ip = itp->itp_crypto.dst.ip6,
105 .spi = clib_host_to_net_u32 (sa->spi),
106 };
107 hash_set_mem_alloc (&im->tun6_protect_by_key, &key, res.as_u64);
108 }
109 }))
110 /* *INDENT-ON* */
111}
112
113static void
114ipsec_tun_protect_db_remove (ipsec_main_t * im,
115 const ipsec_tun_protect_t * itp)
116{
117 const ipsec_sa_t *sa;
118
119 /* *INDENT-OFF* */
120 FOR_EACH_IPSEC_PROTECT_INPUT_SA(itp, sa,
121 ({
122 if (ip46_address_is_ip4 (&itp->itp_crypto.dst))
123 {
124 ipsec4_tunnel_key_t key = {
Neale Ranns41afb332019-07-16 06:19:35 -0700125 .remote_ip = itp->itp_crypto.dst.ip4,
Neale Rannsc87b66c2019-02-07 07:26:12 -0800126 .spi = clib_host_to_net_u32 (sa->spi),
127 };
128 hash_unset (im->tun4_protect_by_key, &key);
Neale Ranns41afb332019-07-16 06:19:35 -0700129 if (0 == hash_elts(im->tun4_protect_by_key))
130 udp_unregister_dst_port (vlib_get_main(),
131 UDP_DST_PORT_ipsec,
132 1);
Neale Rannsc87b66c2019-02-07 07:26:12 -0800133 }
134 else
135 {
136 ipsec6_tunnel_key_t key = {
137 .remote_ip = itp->itp_crypto.dst.ip6,
138 .spi = clib_host_to_net_u32 (sa->spi),
139 };
140 hash_unset_mem_free (&im->tun6_protect_by_key, &key);
141 }
142 }))
143 /* *INDENT-ON* */
144}
145
146static void
147ipsec_tun_protect_config (ipsec_main_t * im,
148 ipsec_tun_protect_t * itp, u32 sa_out, u32 * sas_in)
149{
150 ipsec_sa_t *sa;
151 u32 ii;
152
153 itp->itp_n_sa_in = vec_len (sas_in);
154 for (ii = 0; ii < itp->itp_n_sa_in; ii++)
155 itp->itp_in_sas[ii] = sas_in[ii];
156 itp->itp_out_sa = sa_out;
157
158 /* *INDENT-OFF* */
159 FOR_EACH_IPSEC_PROTECT_INPUT_SA(itp, sa,
160 ({
161 if (ipsec_sa_is_set_IS_TUNNEL (sa))
162 {
163 itp->itp_crypto.src = sa->tunnel_dst_addr;
164 itp->itp_crypto.dst = sa->tunnel_src_addr;
165 ipsec_sa_set_IS_PROTECT (sa);
166 itp->itp_flags |= IPSEC_PROTECT_ENCAPED;
167 }
168 else
169 {
170 itp->itp_crypto.src = itp->itp_tun.src;
171 itp->itp_crypto.dst = itp->itp_tun.dst;
172 itp->itp_flags &= ~IPSEC_PROTECT_ENCAPED;
173 }
174 }));
175 /* *INDENT-ON* */
176
177 /*
178 * add to the DB against each SA
179 */
180 ipsec_tun_protect_db_add (im, itp);
181
182 /*
183 * enable the encrypt feature for egress.
184 */
185 ipsec_tun_protect_feature_set (itp, 1);
186
187}
188
189static void
190ipsec_tun_protect_unconfig (ipsec_main_t * im, ipsec_tun_protect_t * itp)
191{
192 ipsec_sa_t *sa;
Neale Ranns495d7ff2019-07-12 09:15:26 +0000193 index_t sai;
Neale Rannsc87b66c2019-02-07 07:26:12 -0800194
195 ipsec_tun_protect_feature_set (itp, 0);
196
197 /* *INDENT-OFF* */
198 FOR_EACH_IPSEC_PROTECT_INPUT_SA(itp, sa,
199 ({
200 ipsec_sa_unset_IS_PROTECT (sa);
201 }));
Neale Rannsc87b66c2019-02-07 07:26:12 -0800202
203 ipsec_tun_protect_db_remove (im, itp);
Neale Ranns495d7ff2019-07-12 09:15:26 +0000204
205 ipsec_sa_unlock(itp->itp_out_sa);
206
207 FOR_EACH_IPSEC_PROTECT_INPUT_SAI(itp, sai,
208 ({
209 ipsec_sa_unlock(sai);
210 }));
211 /* *INDENT-ON* */
Neale Rannsc87b66c2019-02-07 07:26:12 -0800212}
213
214index_t
215ipsec_tun_protect_find (u32 sw_if_index)
216{
217 if (vec_len (ipsec_protect_db.tunnels) < sw_if_index)
218 return (INDEX_INVALID);
219
220 return (ipsec_protect_db.tunnels[sw_if_index]);
221}
222
223int
224ipsec_tun_protect_update (u32 sw_if_index, u32 sa_out, u32 * sas_in)
225{
226 u32 itpi, ii;
227 ipsec_tun_protect_t *itp;
228 ipsec_main_t *im;
229 int rv;
230
231 rv = 0;
232 im = &ipsec_main;
233 vec_validate_init_empty (ipsec_protect_db.tunnels, sw_if_index,
234 INDEX_INVALID);
235 itpi = ipsec_protect_db.tunnels[sw_if_index];
236
237 vec_foreach_index (ii, sas_in)
238 {
Neale Ranns495d7ff2019-07-12 09:15:26 +0000239 sas_in[ii] = ipsec_sa_find_and_lock (sas_in[ii]);
Neale Rannsc87b66c2019-02-07 07:26:12 -0800240 if (~0 == sas_in[ii])
241 {
242 rv = VNET_API_ERROR_INVALID_VALUE;
243 goto out;
244 }
245 }
246
Neale Ranns495d7ff2019-07-12 09:15:26 +0000247 sa_out = ipsec_sa_find_and_lock (sa_out);
Neale Rannsc87b66c2019-02-07 07:26:12 -0800248
249 if (~0 == sa_out)
250 {
251 rv = VNET_API_ERROR_INVALID_VALUE;
252 goto out;
253 }
254
255 if (INDEX_INVALID == itpi)
256 {
257 vnet_device_class_t *dev_class;
258 vnet_hw_interface_t *hi;
259 vnet_main_t *vnm;
260 u8 is_l2;
261
262 vnm = vnet_get_main ();
263 hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
264 dev_class = vnet_get_device_class (vnm, hi->dev_class_index);
265
266 if (NULL == dev_class->ip_tun_desc)
267 {
268 rv = VNET_API_ERROR_INVALID_SW_IF_INDEX;
269 goto out;
270 }
271
272 pool_get_zero (ipsec_protect_pool, itp);
273
274 itp->itp_sw_if_index = sw_if_index;
275 ipsec_protect_db.tunnels[sw_if_index] = itp - ipsec_protect_pool;
276 ipsec_protect_db.count++;
277
278 itp->itp_n_sa_in = vec_len (sas_in);
279 for (ii = 0; ii < itp->itp_n_sa_in; ii++)
280 itp->itp_in_sas[ii] = sas_in[ii];
281 itp->itp_out_sa = sa_out;
282
283 rv = dev_class->ip_tun_desc (sw_if_index,
284 &itp->itp_tun.src,
285 &itp->itp_tun.dst, &is_l2);
286
287 if (rv)
288 goto out;
289
290 if (is_l2)
291 itp->itp_flags |= IPSEC_PROTECT_L2;
292
293 /*
294 * add to the tunnel DB for ingress
295 * - if the SA is in trasnport mode, then the packates will arrivw
296 * with the IP src,dst of the protected tunnel, in which case we can
297 * simply strip the IP header and hand the payload to the protocol
298 * appropriate input handler
299 * - if the SA is in tunnel mode then there are two IP headers present
300 * one for the crytpo tunnel endpoints (described in the SA) and one
301 * for the tunnel endpoints. The outer IP headers in the srriving
302 * packets will have the crypto endpoints. So the DB needs to contain
303 * the crpto endpoint. Once the crypto header is stripped, revealing,
304 * the tunnel-IP we have 2 choices:
305 * 1) do a tunnel lookup based on the revealed header
306 * 2) skip the tunnel lookup and assume that the packet matches the
307 * one that is protected here.
308 * If we did 1) then we would allow our peer to use the SA for tunnel
309 * X to inject traffic onto tunnel Y, this is not good. If we do 2)
310 * then we don't verify that the peer is indeed using SA for tunnel
311 * X and addressing tunnel X. So we take a compromise, once the SA
312 * matches to tunnel X we veriy that the inner IP matches the value
313 * of the tunnel we are protecting, else it's dropped.
314 */
315 ipsec_tun_protect_config (im, itp, sa_out, sas_in);
316
317 if (1 == hash_elts (im->tun4_protect_by_key))
318 ip4_register_protocol (IP_PROTOCOL_IPSEC_ESP,
319 ipsec4_tun_input_node.index);
320 if (1 == hash_elts (im->tun6_protect_by_key))
321 ip6_register_protocol (IP_PROTOCOL_IPSEC_ESP,
322 ipsec6_tun_input_node.index);
323 }
324 else
325 {
326 /* updating SAs only */
327 itp = pool_elt_at_index (ipsec_protect_pool, itpi);
328
329 ipsec_tun_protect_unconfig (im, itp);
330 ipsec_tun_protect_config (im, itp, sa_out, sas_in);
331 }
332
333 vec_free (sas_in);
334out:
335 return (rv);
336}
337
338int
339ipsec_tun_protect_del (u32 sw_if_index)
340{
341 ipsec_tun_protect_t *itp;
342 ipsec_main_t *im;
343 index_t itpi;
344
345 im = &ipsec_main;
346
347 vec_validate_init_empty (ipsec_protect_db.tunnels, sw_if_index,
348 INDEX_INVALID);
349 itpi = ipsec_protect_db.tunnels[sw_if_index];
350
351 if (INDEX_INVALID == itpi)
352 return (VNET_API_ERROR_NO_SUCH_ENTRY);
353
354 itp = ipsec_tun_protect_get (itpi);
355 ipsec_tun_protect_unconfig (im, itp);
356
357 ipsec_protect_db.tunnels[itp->itp_sw_if_index] = INDEX_INVALID;
358
359 pool_put (ipsec_protect_pool, itp);
360
Neale Ranns41afb332019-07-16 06:19:35 -0700361 if (0 == hash_elts (im->tun4_protect_by_key))
362 ip4_unregister_protocol (IP_PROTOCOL_IPSEC_ESP);
363 if (0 == hash_elts (im->tun6_protect_by_key))
364 ip6_unregister_protocol (IP_PROTOCOL_IPSEC_ESP);
Neale Rannsc87b66c2019-02-07 07:26:12 -0800365
366 return (0);
367}
368
369void
370ipsec_tun_protect_walk (ipsec_tun_protect_walk_cb_t fn, void *ctx)
371{
372 index_t itpi;
373
374 /* *INDENT-OFF* */
375 pool_foreach_index(itpi, ipsec_protect_pool,
376 ({
377 fn (itpi, ctx);
378 }));
379 /* *INDENT-ON* */
380}
381
382clib_error_t *
383ipsec_tunnel_protect_init (vlib_main_t * vm)
384{
385 ipsec_main_t *im;
386
387 im = &ipsec_main;
388 im->tun6_protect_by_key = hash_create_mem (0,
389 sizeof (ipsec6_tunnel_key_t),
390 sizeof (u64));
391 im->tun4_protect_by_key = hash_create (0, sizeof (u64));
392
393 return 0;
394}
395
396VLIB_INIT_FUNCTION (ipsec_tunnel_protect_init);
397
398
399/*
400 * fd.io coding-style-patch-verification: ON
401 *
402 * Local Variables:
403 * eval: (c-set-style "gnu")
404 * End:
405 */