cnat: Prepare extended snat policies
[vpp.git] / src / plugins / cnat / cnat_snat_policy.c
1 /*
2  * Copyright (c) 2020 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <vnet/ip/ip.h>
17 #include <cnat/cnat_snat_policy.h>
18 #include <cnat/cnat_translation.h>
19
20 cnat_snat_policy_main_t cnat_snat_policy_main;
21
22 uword
23 unformat_cnat_snat_interface_map_type (unformat_input_t *input, va_list *args)
24 {
25   u8 *a = va_arg (*args, u8 *);
26   if (unformat (input, "include-v4"))
27     *a = CNAT_SNAT_IF_MAP_INCLUDE_V4;
28   else if (unformat (input, "include-v6"))
29     *a = CNAT_SNAT_IF_MAP_INCLUDE_V6;
30   else
31     return 0;
32   return 1;
33 }
34
35 u8 *
36 format_cnat_snat_interface_map_type (u8 *s, va_list *args)
37 {
38   cnat_snat_interface_map_type_t mtype = va_arg (*args, int);
39   switch (mtype)
40     {
41     case CNAT_SNAT_IF_MAP_INCLUDE_V4:
42       s = format (s, "Included v4");
43       break;
44     case CNAT_SNAT_IF_MAP_INCLUDE_V6:
45       s = format (s, "Included v6");
46       break;
47     default:
48       s = format (s, "(unknown)");
49       break;
50     }
51   return (s);
52 }
53
54 u8 *
55 format_cnat_snat_prefix (u8 *s, va_list *args)
56 {
57   clib_bihash_kv_24_8_t *kv = va_arg (*args, clib_bihash_kv_24_8_t *);
58   CLIB_UNUSED (int verbose) = va_arg (*args, int);
59   u32 af = kv->key[2] >> 32;
60   u32 len = kv->key[2] & 0xffffffff;
61   if (AF_IP4 == af)
62     s = format (s, "%U/%d", format_ip4_address, &kv->key[0], len);
63   else
64     s = format (s, "%U/%d", format_ip6_address, &kv->key[0], len);
65   return (s);
66 }
67
68 static void
69 cnat_compute_prefix_lengths_in_search_order (
70   cnat_snat_exclude_pfx_table_t *table, ip_address_family_t af)
71 {
72   int i;
73   vec_reset_length (table->meta[af].prefix_lengths_in_search_order);
74   /* Note: bitmap reversed so this is in fact a longest prefix match */
75   clib_bitmap_foreach (i, table->meta[af].non_empty_dst_address_length_bitmap)
76     {
77       int dst_address_length = 128 - i;
78       vec_add1 (table->meta[af].prefix_lengths_in_search_order,
79                 dst_address_length);
80     }
81 }
82
83 int
84 cnat_snat_policy_add_del_if (u32 sw_if_index, u8 is_add,
85                              cnat_snat_interface_map_type_t table)
86 {
87   cnat_snat_policy_main_t *cpm = &cnat_snat_policy_main;
88
89   if (table > ARRAY_LEN (cpm->interface_maps))
90     return VNET_API_ERROR_INVALID_VALUE;
91
92   clib_bitmap_t **map = &cpm->interface_maps[table];
93
94   *map = clib_bitmap_set (*map, sw_if_index, is_add);
95   return 0;
96 }
97
98 static clib_error_t *
99 cnat_snat_policy_add_del_if_command_fn (vlib_main_t *vm,
100                                         unformat_input_t *input,
101                                         vlib_cli_command_t *cmd)
102 {
103   vnet_main_t *vnm = vnet_get_main ();
104   int is_add = 1;
105   u32 sw_if_index = ~0;
106   u32 table;
107   int rv;
108
109   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
110     {
111       if (unformat (input, "del"))
112         is_add = 0;
113       else if (unformat (input, "table %U",
114                          unformat_cnat_snat_interface_map_type, &table))
115         ;
116       else if (unformat (input, "%U", unformat_vnet_sw_interface, vnm,
117                          &sw_if_index))
118         ;
119       else
120         return clib_error_return (0, "unknown input '%U'",
121                                   format_unformat_error, input);
122     }
123
124   if (sw_if_index == ~0)
125     return clib_error_return (0, "Interface not specified");
126
127   rv = cnat_snat_policy_add_del_if (sw_if_index, is_add, table);
128
129   if (rv)
130     return clib_error_return (0, "Error %d", rv);
131
132   return NULL;
133 }
134
135 VLIB_CLI_COMMAND (cnat_snat_policy_add_del_if_command, static) = {
136   .path = "set cnat snat-policy if",
137   .short_help = "set cnat snat-policy if [del]"
138                 "[table [include-v4 include-v6]] [interface]",
139   .function = cnat_snat_policy_add_del_if_command_fn,
140 };
141
142 int
143 cnat_snat_policy_add_pfx (ip_prefix_t *pfx)
144 {
145   /* All packets destined to this prefix won't be source-NAT-ed */
146   cnat_snat_exclude_pfx_table_t *table = &cnat_snat_policy_main.excluded_pfx;
147   clib_bihash_kv_24_8_t kv;
148   ip6_address_t *mask;
149   u64 af = ip_prefix_version (pfx);
150   ;
151
152   mask = &table->ip_masks[pfx->len];
153   if (AF_IP4 == af)
154     {
155       kv.key[0] = (u64) ip_prefix_v4 (pfx).as_u32 & mask->as_u64[0];
156       kv.key[1] = 0;
157     }
158   else
159     {
160       kv.key[0] = ip_prefix_v6 (pfx).as_u64[0] & mask->as_u64[0];
161       kv.key[1] = ip_prefix_v6 (pfx).as_u64[1] & mask->as_u64[1];
162     }
163   kv.key[2] = ((u64) af << 32) | pfx->len;
164   clib_bihash_add_del_24_8 (&table->ip_hash, &kv, 1 /* is_add */);
165
166   table->meta[af].dst_address_length_refcounts[pfx->len]++;
167   table->meta[af].non_empty_dst_address_length_bitmap = clib_bitmap_set (
168     table->meta[af].non_empty_dst_address_length_bitmap, 128 - pfx->len, 1);
169   cnat_compute_prefix_lengths_in_search_order (table, af);
170   return 0;
171 }
172
173 int
174 cnat_snat_policy_del_pfx (ip_prefix_t *pfx)
175 {
176   cnat_snat_exclude_pfx_table_t *table = &cnat_snat_policy_main.excluded_pfx;
177   clib_bihash_kv_24_8_t kv, val;
178   ip6_address_t *mask;
179   u64 af = ip_prefix_version (pfx);
180   ;
181
182   mask = &table->ip_masks[pfx->len];
183   if (AF_IP4 == af)
184     {
185       kv.key[0] = (u64) ip_prefix_v4 (pfx).as_u32 & mask->as_u64[0];
186       kv.key[1] = 0;
187     }
188   else
189     {
190       kv.key[0] = ip_prefix_v6 (pfx).as_u64[0] & mask->as_u64[0];
191       kv.key[1] = ip_prefix_v6 (pfx).as_u64[1] & mask->as_u64[1];
192     }
193   kv.key[2] = ((u64) af << 32) | pfx->len;
194
195   if (clib_bihash_search_24_8 (&table->ip_hash, &kv, &val))
196     {
197       return 1;
198     }
199   clib_bihash_add_del_24_8 (&table->ip_hash, &kv, 0 /* is_add */);
200   /* refcount accounting */
201   ASSERT (table->meta[af].dst_address_length_refcounts[pfx->len] > 0);
202   if (--table->meta[af].dst_address_length_refcounts[pfx->len] == 0)
203     {
204       table->meta[af].non_empty_dst_address_length_bitmap =
205         clib_bitmap_set (table->meta[af].non_empty_dst_address_length_bitmap,
206                          128 - pfx->len, 0);
207       cnat_compute_prefix_lengths_in_search_order (table, af);
208     }
209   return 0;
210 }
211
212 int
213 cnat_search_snat_prefix (ip46_address_t *addr, ip_address_family_t af)
214 {
215   /* Returns 0 if addr matches any of the listed prefixes */
216   cnat_snat_exclude_pfx_table_t *table = &cnat_snat_policy_main.excluded_pfx;
217   clib_bihash_kv_24_8_t kv, val;
218   int i, n_p, rv;
219   n_p = vec_len (table->meta[af].prefix_lengths_in_search_order);
220   if (AF_IP4 == af)
221     {
222       kv.key[0] = addr->ip4.as_u32;
223       kv.key[1] = 0;
224     }
225   else
226     {
227       kv.key[0] = addr->as_u64[0];
228       kv.key[1] = addr->as_u64[1];
229     }
230
231   /*
232    * start search from a mask length same length or shorter.
233    * we don't want matches longer than the mask passed
234    */
235   i = 0;
236   for (; i < n_p; i++)
237     {
238       int dst_address_length =
239         table->meta[af].prefix_lengths_in_search_order[i];
240       ip6_address_t *mask = &table->ip_masks[dst_address_length];
241
242       ASSERT (dst_address_length >= 0 && dst_address_length <= 128);
243       /* As lengths are decreasing, masks are increasingly specific. */
244       kv.key[0] &= mask->as_u64[0];
245       kv.key[1] &= mask->as_u64[1];
246       kv.key[2] = ((u64) af << 32) | dst_address_length;
247       rv = clib_bihash_search_inline_2_24_8 (&table->ip_hash, &kv, &val);
248       if (rv == 0)
249         return 0;
250     }
251   return -1;
252 }
253
254 static_always_inline int
255 cnat_snat_policy_interface_enabled (u32 sw_if_index, ip_address_family_t af)
256 {
257   cnat_snat_policy_main_t *cpm = &cnat_snat_policy_main;
258   return clib_bitmap_get (cpm->interface_maps[af], sw_if_index);
259 }
260
261 int
262 cnat_snat_policy_none (vlib_buffer_t *b, cnat_session_t *session)
263 {
264   /* srcNAT everything by default */
265   return 1;
266 }
267
268 int
269 cnat_snat_policy_if_pfx (vlib_buffer_t *b, cnat_session_t *session)
270 {
271   ip46_address_t *dst_addr = &session->key.cs_ip[VLIB_TX];
272   u32 in_if = vnet_buffer (b)->sw_if_index[VLIB_RX];
273   ip_address_family_t af = session->key.cs_af;
274
275   /* source nat for outgoing connections */
276   if (cnat_snat_policy_interface_enabled (in_if, af))
277     if (cnat_search_snat_prefix (dst_addr, af))
278       /* Destination is not in the prefixes that don't require snat */
279       return 1;
280   return 0;
281 }
282
283 void
284 cnat_set_snat (ip4_address_t *ip4, ip6_address_t *ip6, u32 sw_if_index)
285 {
286   cnat_snat_policy_main_t *cpm = &cnat_snat_policy_main;
287
288   cnat_lazy_init ();
289
290   cnat_translation_unwatch_addr (INDEX_INVALID, CNAT_RESOLV_ADDR_SNAT);
291
292   ip_address_set (&cpm->snat_ip4.ce_ip, ip4, AF_IP4);
293   ip_address_set (&cpm->snat_ip6.ce_ip, ip6, AF_IP6);
294   cpm->snat_ip4.ce_sw_if_index = sw_if_index;
295   cpm->snat_ip6.ce_sw_if_index = sw_if_index;
296
297   cnat_resolve_ep (&cpm->snat_ip4);
298   cnat_resolve_ep (&cpm->snat_ip6);
299   cnat_translation_watch_addr (INDEX_INVALID, 0, &cpm->snat_ip4,
300                                CNAT_RESOLV_ADDR_SNAT);
301   cnat_translation_watch_addr (INDEX_INVALID, 0, &cpm->snat_ip6,
302                                CNAT_RESOLV_ADDR_SNAT);
303 }
304
305 static clib_error_t *
306 cnat_set_snat_cli (vlib_main_t *vm, unformat_input_t *input,
307                    vlib_cli_command_t *cmd)
308 {
309   unformat_input_t _line_input, *line_input = &_line_input;
310   vnet_main_t *vnm = vnet_get_main ();
311   ip4_address_t ip4 = { { 0 } };
312   ip6_address_t ip6 = { { 0 } };
313   clib_error_t *e = 0;
314   u32 sw_if_index = INDEX_INVALID;
315
316   cnat_lazy_init ();
317
318   /* Get a line of input. */
319   if (!unformat_user (input, unformat_line_input, line_input))
320     return 0;
321
322   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
323     {
324       if (unformat_user (line_input, unformat_ip4_address, &ip4))
325         ;
326       else if (unformat_user (line_input, unformat_ip6_address, &ip6))
327         ;
328       else if (unformat_user (line_input, unformat_vnet_sw_interface, vnm,
329                               &sw_if_index))
330         ;
331       else
332         {
333           e = clib_error_return (0, "unknown input '%U'",
334                                  format_unformat_error, input);
335           goto done;
336         }
337     }
338
339   cnat_set_snat (&ip4, &ip6, sw_if_index);
340
341 done:
342   unformat_free (line_input);
343
344   return (e);
345 }
346
347 VLIB_CLI_COMMAND (cnat_set_snat_command, static) = {
348   .path = "set cnat snat-policy addr",
349   .short_help =
350     "set cnat snat-policy addr [<ip4-address>][<ip6-address>][sw_if_index]",
351   .function = cnat_set_snat_cli,
352 };
353
354 static clib_error_t *
355 cnat_snat_policy_add_del_pfx_command_fn (vlib_main_t *vm,
356                                          unformat_input_t *input,
357                                          vlib_cli_command_t *cmd)
358 {
359   ip_prefix_t pfx;
360   u8 is_add = 1;
361   int rv;
362
363   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
364     {
365       if (unformat (input, "%U", unformat_ip_prefix, &pfx))
366         ;
367       else if (unformat (input, "del"))
368         is_add = 0;
369       else
370         return (clib_error_return (0, "unknown input '%U'",
371                                    format_unformat_error, input));
372     }
373
374   if (is_add)
375     rv = cnat_snat_policy_add_pfx (&pfx);
376   else
377     rv = cnat_snat_policy_del_pfx (&pfx);
378
379   if (rv)
380     return (clib_error_return (0, "error %d", rv, input));
381
382   return (NULL);
383 }
384
385 VLIB_CLI_COMMAND (cnat_snat_policy_add_del_pfx_command, static) = {
386   .path = "set cnat snat-policy prefix",
387   .short_help = "set cnat snat-policy prefix [del] [prefix]",
388   .function = cnat_snat_policy_add_del_pfx_command_fn,
389 };
390
391 static clib_error_t *
392 cnat_show_snat (vlib_main_t *vm, unformat_input_t *input,
393                 vlib_cli_command_t *cmd)
394 {
395   cnat_snat_exclude_pfx_table_t *excluded_pfx =
396     &cnat_snat_policy_main.excluded_pfx;
397   cnat_snat_policy_main_t *cpm = &cnat_snat_policy_main;
398   vnet_main_t *vnm = vnet_get_main ();
399   u32 sw_if_index;
400
401   vlib_cli_output (vm, "Source NAT\n  ip4: %U\n  ip6: %U\n\n",
402                    format_cnat_endpoint, &cpm->snat_ip4, format_cnat_endpoint,
403                    &cpm->snat_ip6);
404   vlib_cli_output (vm, "Excluded prefixes:\n  %U\n", format_bihash_24_8,
405                    &excluded_pfx->ip_hash, 1);
406
407   for (int i = 0; i < CNAT_N_SNAT_IF_MAP; i++)
408     {
409       vlib_cli_output (vm, "\n%U interfaces:\n",
410                        format_cnat_snat_interface_map_type, i);
411       clib_bitmap_foreach (sw_if_index, cpm->interface_maps[i])
412         vlib_cli_output (vm, "  %U\n", format_vnet_sw_if_index_name, vnm,
413                          sw_if_index);
414     }
415
416   return (NULL);
417 }
418
419 VLIB_CLI_COMMAND (cnat_show_snat_command, static) = {
420   .path = "show cnat snat-policy",
421   .short_help = "show cnat snat-policy",
422   .function = cnat_show_snat,
423 };
424
425 int
426 cnat_set_snat_policy (cnat_snat_policy_type_t policy)
427 {
428   cnat_snat_policy_main_t *cpm = &cnat_snat_policy_main;
429   switch (policy)
430     {
431     case CNAT_SNAT_POLICY_NONE:
432       cpm->snat_policy = cnat_snat_policy_none;
433       break;
434     case CNAT_SNAT_POLICY_IF_PFX:
435       cpm->snat_policy = cnat_snat_policy_if_pfx;
436       break;
437     default:
438       return 1;
439     }
440   return 0;
441 }
442
443 static clib_error_t *
444 cnat_snat_policy_set_cmd_fn (vlib_main_t *vm, unformat_input_t *input,
445                              vlib_cli_command_t *cmd)
446 {
447   cnat_snat_policy_type_t policy = CNAT_SNAT_POLICY_NONE;
448   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
449     {
450       if (unformat (input, "none"))
451         ;
452       else if (unformat (input, "if-pfx"))
453         policy = CNAT_SNAT_POLICY_IF_PFX;
454       else
455         return clib_error_return (0, "unknown input '%U'",
456                                   format_unformat_error, input);
457     }
458
459   cnat_set_snat_policy (policy);
460   return NULL;
461 }
462
463 VLIB_CLI_COMMAND (cnat_snat_policy_set_cmd, static) = {
464   .path = "set cnat snat-policy",
465   .short_help = "set cnat snat-policy [none][if-pfx]",
466   .function = cnat_snat_policy_set_cmd_fn,
467 };
468
469 static void
470 cnat_if_addr_add_del_snat_cb (addr_resolution_t *ar, ip_address_t *address,
471                               u8 is_del)
472 {
473   cnat_snat_policy_main_t *cpm = &cnat_snat_policy_main;
474   cnat_endpoint_t *ep;
475
476   ep = AF_IP4 == ar->af ? &cpm->snat_ip4 : &cpm->snat_ip6;
477
478   if (!is_del && ep->ce_flags & CNAT_EP_FLAG_RESOLVED)
479     return;
480
481   if (is_del)
482     {
483       ep->ce_flags &= ~CNAT_EP_FLAG_RESOLVED;
484       /* Are there remaining addresses ? */
485       if (0 == cnat_resolve_addr (ar->sw_if_index, ar->af, address))
486         is_del = 0;
487     }
488
489   if (!is_del)
490     {
491       ip_address_copy (&ep->ce_ip, address);
492       ep->ce_flags |= CNAT_EP_FLAG_RESOLVED;
493     }
494 }
495
496 static clib_error_t *
497 cnat_snat_init (vlib_main_t *vm)
498 {
499   cnat_snat_policy_main_t *cpm = &cnat_snat_policy_main;
500   cnat_main_t *cm = &cnat_main;
501   cnat_snat_exclude_pfx_table_t *excluded_pfx = &cpm->excluded_pfx;
502
503   int i;
504   for (i = 0; i < ARRAY_LEN (excluded_pfx->ip_masks); i++)
505     {
506       u32 j, i0, i1;
507
508       i0 = i / 32;
509       i1 = i % 32;
510
511       for (j = 0; j < i0; j++)
512         excluded_pfx->ip_masks[i].as_u32[j] = ~0;
513
514       if (i1)
515         excluded_pfx->ip_masks[i].as_u32[i0] =
516           clib_host_to_net_u32 (pow2_mask (i1) << (32 - i1));
517     }
518   clib_bihash_init_24_8 (&excluded_pfx->ip_hash, "snat prefixes",
519                          cm->snat_hash_buckets, cm->snat_hash_memory);
520   clib_bihash_set_kvp_format_fn_24_8 (&excluded_pfx->ip_hash,
521                                       format_cnat_snat_prefix);
522
523   for (int i = 0; i < CNAT_N_SNAT_IF_MAP; i++)
524     clib_bitmap_validate (cpm->interface_maps[i], cm->snat_if_map_length);
525
526   cnat_translation_register_addr_add_cb (CNAT_RESOLV_ADDR_SNAT,
527                                          cnat_if_addr_add_del_snat_cb);
528
529   cpm->snat_policy = cnat_snat_policy_none;
530
531   return (NULL);
532 }
533
534 VLIB_INIT_FUNCTION (cnat_snat_init);
535
536 /*
537  * fd.io coding-style-patch-verification: ON
538  *
539  * Local Variables:
540  * eval: (c-set-style "gnu")
541  * End:
542  */