From 516b0adf6d8bbc6533ab58d7add96ec74d8be05a Mon Sep 17 00:00:00 2001 From: Nathan Skrzypczak Date: Thu, 25 Feb 2021 17:42:50 +0100 Subject: [PATCH] cnat: Add calico/k8s src policy This patch implements k8s-specific extensions to the cnat plugin. This could be done by exposing a richer semantic on srcNAT policies, but this might be too complex work at this point. Also k8s fits quite well as a 'cloud NAT' usecase. Type: feature Change-Id: I2266daf7b10a92e65f5ed430838a12ae826bd333 Signed-off-by: Aloys Augustin Signed-off-by: Nathan Skrzypczak --- src/plugins/cnat/cnat.api | 3 ++ src/plugins/cnat/cnat_snat_policy.c | 56 +++++++++++++++++++++++++++++++++++-- src/plugins/cnat/cnat_snat_policy.h | 2 ++ 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/plugins/cnat/cnat.api b/src/plugins/cnat/cnat.api index df57ea54896..e253084e74e 100644 --- a/src/plugins/cnat/cnat.api +++ b/src/plugins/cnat/cnat.api @@ -171,6 +171,7 @@ enum cnat_snat_policy_table:u8 { CNAT_POLICY_INCLUDE_V4 = 0, CNAT_POLICY_INCLUDE_V6 = 1, + CNAT_POLICY_POD = 2, }; autoreply define cnat_snat_policy_add_del_if @@ -190,6 +191,8 @@ enum cnat_snat_policies:u8 /* Filter by interface list : snat_policy_add_del_if * and prefix list : snat_policy_add_del_if */ CNAT_POLICY_IF_PFX = 1, + /* Kubernetes specific policy */ + CNAT_POLICY_K8S = 2, }; autoreply define cnat_set_snat_policy diff --git a/src/plugins/cnat/cnat_snat_policy.c b/src/plugins/cnat/cnat_snat_policy.c index 83a79c0544d..e2901631b28 100644 --- a/src/plugins/cnat/cnat_snat_policy.c +++ b/src/plugins/cnat/cnat_snat_policy.c @@ -27,6 +27,8 @@ unformat_cnat_snat_interface_map_type (unformat_input_t *input, va_list *args) *a = CNAT_SNAT_IF_MAP_INCLUDE_V4; else if (unformat (input, "include-v6")) *a = CNAT_SNAT_IF_MAP_INCLUDE_V6; + else if (unformat (input, "k8s")) + *a = CNAT_SNAT_IF_MAP_INCLUDE_POD; else return 0; return 1; @@ -44,6 +46,9 @@ format_cnat_snat_interface_map_type (u8 *s, va_list *args) case CNAT_SNAT_IF_MAP_INCLUDE_V6: s = format (s, "Included v6"); break; + case CNAT_SNAT_IF_MAP_INCLUDE_POD: + s = format (s, "k8s pod"); + break; default: s = format (s, "(unknown)"); break; @@ -135,7 +140,7 @@ cnat_snat_policy_add_del_if_command_fn (vlib_main_t *vm, VLIB_CLI_COMMAND (cnat_snat_policy_add_del_if_command, static) = { .path = "set cnat snat-policy if", .short_help = "set cnat snat-policy if [del]" - "[table [include-v4 include-v6]] [interface]", + "[table [include-v4 include-v6 k8s]] [interface]", .function = cnat_snat_policy_add_del_if_command_fn, }; @@ -280,6 +285,48 @@ cnat_snat_policy_if_pfx (vlib_buffer_t *b, cnat_session_t *session) return 0; } +int +cnat_snat_policy_k8s (vlib_buffer_t *b, cnat_session_t *session) +{ + cnat_snat_policy_main_t *cpm = &cnat_snat_policy_main; + ip_address_family_t af = session->key.cs_af; + + ip46_address_t *src_addr = &session->key.cs_ip[VLIB_RX]; + ip46_address_t *dst_addr = &session->key.cs_ip[VLIB_TX]; + u32 in_if = vnet_buffer (b)->sw_if_index[VLIB_RX]; + u32 out_if = vnet_buffer (b)->sw_if_index[VLIB_TX]; + + /* source nat for outgoing connections */ + if (cnat_snat_policy_interface_enabled (in_if, af)) + if (cnat_search_snat_prefix (dst_addr, af)) + /* Destination is not in the prefixes that don't require snat */ + return 1; + + /* source nat for translations that come from the outside: + src not not a pod interface, dst not a pod interface */ + if (!clib_bitmap_get (cpm->interface_maps[CNAT_SNAT_IF_MAP_INCLUDE_POD], + in_if) && + !clib_bitmap_get (cpm->interface_maps[CNAT_SNAT_IF_MAP_INCLUDE_POD], + out_if)) + { + if (AF_IP6 == af && + ip6_address_is_equal (&src_addr->ip6, + &ip_addr_v6 (&cpm->snat_ip6.ce_ip))) + return 0; + if (AF_IP4 == af && + ip4_address_is_equal (&src_addr->ip4, + &ip_addr_v4 (&cpm->snat_ip4.ce_ip))) + return 0; + return 1; + } + + /* handle the case where a container is connecting to itself via a service */ + if (ip46_address_is_equal (src_addr, dst_addr)) + return 1; + + return 0; +} + void cnat_set_snat (ip4_address_t *ip4, ip6_address_t *ip6, u32 sw_if_index) { @@ -434,6 +481,9 @@ cnat_set_snat_policy (cnat_snat_policy_type_t policy) case CNAT_SNAT_POLICY_IF_PFX: cpm->snat_policy = cnat_snat_policy_if_pfx; break; + case CNAT_SNAT_POLICY_K8S: + cpm->snat_policy = cnat_snat_policy_k8s; + break; default: return 1; } @@ -451,6 +501,8 @@ cnat_snat_policy_set_cmd_fn (vlib_main_t *vm, unformat_input_t *input, ; else if (unformat (input, "if-pfx")) policy = CNAT_SNAT_POLICY_IF_PFX; + else if (unformat (input, "k8s")) + policy = CNAT_SNAT_POLICY_K8S; else return clib_error_return (0, "unknown input '%U'", format_unformat_error, input); @@ -462,7 +514,7 @@ cnat_snat_policy_set_cmd_fn (vlib_main_t *vm, unformat_input_t *input, VLIB_CLI_COMMAND (cnat_snat_policy_set_cmd, static) = { .path = "set cnat snat-policy", - .short_help = "set cnat snat-policy [none][if-pfx]", + .short_help = "set cnat snat-policy [none][if-pfx][k8s]", .function = cnat_snat_policy_set_cmd_fn, }; diff --git a/src/plugins/cnat/cnat_snat_policy.h b/src/plugins/cnat/cnat_snat_policy.h index ff30d19c884..987ae494e16 100644 --- a/src/plugins/cnat/cnat_snat_policy.h +++ b/src/plugins/cnat/cnat_snat_policy.h @@ -44,6 +44,7 @@ typedef enum cnat_snat_interface_map_type_t_ { CNAT_SNAT_IF_MAP_INCLUDE_V4 = AF_IP4, CNAT_SNAT_IF_MAP_INCLUDE_V6 = AF_IP6, + CNAT_SNAT_IF_MAP_INCLUDE_POD, CNAT_N_SNAT_IF_MAP, } cnat_snat_interface_map_type_t; @@ -51,6 +52,7 @@ typedef enum cnat_snat_policy_type_t_ { CNAT_SNAT_POLICY_NONE = 0, CNAT_SNAT_POLICY_IF_PFX = 1, + CNAT_SNAT_POLICY_K8S = 2, } cnat_snat_policy_type_t; typedef struct cnat_snat_policy_main_t_ -- 2.16.6