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