From af897c5e3fa76180fbe0634052bde98b4b3c34d7 Mon Sep 17 00:00:00 2001 From: Nathan Skrzypczak Date: Mon, 21 Sep 2020 19:14:08 +0200 Subject: cnat: Add DHCP support Type: feature Change-Id: I4bd50fd672ac35cf14ebda2b0b10ec0b9a208628 Signed-off-by: Nathan Skrzypczak --- src/plugins/cnat/cnat_translation.c | 343 ++++++++++++++++++++++++++++++++---- 1 file changed, 305 insertions(+), 38 deletions(-) (limited to 'src/plugins/cnat/cnat_translation.c') diff --git a/src/plugins/cnat/cnat_translation.c b/src/plugins/cnat/cnat_translation.c index e453b8a0fda..b128679cded 100644 --- a/src/plugins/cnat/cnat_translation.c +++ b/src/plugins/cnat/cnat_translation.c @@ -25,6 +25,11 @@ cnat_translation_t *cnat_translation_pool; clib_bihash_8_8_t cnat_translation_db; +addr_resolution_t *tr_resolutions; + +typedef void (*cnat_if_addr_add_cb_t) (addr_resolution_t * ar, + ip_address_t * address, u8 is_del); +cnat_if_addr_add_cb_t *cnat_if_addr_add_cbs; static fib_node_type_t cnat_translation_fib_node_type; @@ -33,25 +38,68 @@ vlib_combined_counter_main_t cnat_translation_counters = { .stat_segment_name = "/net/cnat-translation", }; +void +cnat_translation_watch_addr (index_t cti, u64 opaque, cnat_endpoint_t * ep, + cnat_addr_resol_type_t type) +{ + addr_resolution_t *ar; + + if (INDEX_INVALID == ep->ce_sw_if_index) + return; + + pool_get (tr_resolutions, ar); + ar->af = ep->ce_ip.version; + ar->sw_if_index = ep->ce_sw_if_index; + ar->type = type; + ar->opaque = opaque; + ar->cti = cti; +} + +static void +cnat_resolve_ep_tuple (cnat_endpoint_tuple_t * path) +{ + cnat_resolve_ep (&path->src_ep); + cnat_resolve_ep (&path->dst_ep); +} + +void +cnat_translation_unwatch_addr (u32 cti, cnat_addr_resol_type_t type) +{ + /* Delete tr resolution entries matching translation index */ + addr_resolution_t *ar; + index_t *indexes = 0, *ari; + /* *INDENT-OFF* */ + pool_foreach (ar, tr_resolutions, ({ + if ((cti == INDEX_INVALID || ar->cti == cti) && + (ar->type == type || CNAT_RESOLV_ADDR_ANY == type)) + vec_add1(indexes, ar - tr_resolutions); + })); + /* *INDENT-ON* */ + vec_foreach (ari, indexes) pool_put_index (tr_resolutions, *ari); + + vec_free (indexes); +} + static void cnat_tracker_release (cnat_ep_trk_t * trk) { + /* We only track fully resolved endpoints */ + if (!trk->is_active) + return; fib_entry_untrack (trk->ct_fei, trk->ct_sibling); } static void -cnat_tracker_track (index_t cti, - const cnat_endpoint_tuple_t * path, cnat_ep_trk_t * trk) +cnat_tracker_track (index_t cti, cnat_ep_trk_t * trk) { fib_prefix_t pfx; + /* We only track fully resolved endpoints */ + trk->is_active = trk->ct_ep[VLIB_TX].ce_flags & CNAT_EP_FLAG_RESOLVED + && trk->ct_ep[VLIB_RX].ce_flags & CNAT_EP_FLAG_RESOLVED; + if (!trk->is_active) + return; - ip_address_to_fib_prefix (&path->dst_ep.ce_ip, &pfx); - - clib_memcpy (&trk->ct_ep[VLIB_TX], &path->dst_ep, - sizeof (trk->ct_ep[VLIB_TX])); - clib_memcpy (&trk->ct_ep[VLIB_RX], &path->src_ep, - sizeof (trk->ct_ep[VLIB_RX])); - + ip_address_to_fib_prefix (&trk->ct_ep[VLIB_TX].ce_ip, &pfx); trk->ct_fei = fib_entry_track (CNAT_FIB_TABLE, &pfx, cnat_translation_fib_node_type, @@ -62,15 +110,32 @@ cnat_tracker_track (index_t cti, (pfx.fp_proto), &trk->ct_dpo); } -void -cnat_add_translation_to_db (index_t cci, u16 port, ip_protocol_t proto, - index_t cti) +/** + * Add a translation to the bihash + * + * @param cci the ID of the parent client (invalid if vip not resolved) + * @param vip the translation endpoint + * @param proto the translation proto + * @param cti the translation index to be used as value + */ +static void +cnat_add_translation_to_db (index_t cci, cnat_endpoint_t * vip, + ip_protocol_t proto, index_t cti) { clib_bihash_kv_8_8_t bkey; u64 key; - - key = (proto << 16) | port; - key = key << 32 | (u32) cci; + if (INDEX_INVALID == cci) + { + key = proto << 8 | 0x80 | vip->ce_ip.version; + key = key << 16 | vip->ce_port; + key = key << 32 | (u32) vip->ce_sw_if_index; + } + else + { + key = proto << 8; + key = key << 16 | vip->ce_port; + key = key << 32 | (u32) cci; + } bkey.key = key; bkey.value = cti; @@ -78,14 +143,31 @@ cnat_add_translation_to_db (index_t cci, u16 port, ip_protocol_t proto, clib_bihash_add_del_8_8 (&cnat_translation_db, &bkey, 1); } -void -cnat_remove_translation_from_db (index_t cci, u16 port, ip_protocol_t proto) +/** + * Remove a translation from the bihash + * + * @param cci the ID of the parent client + * @param vip the translation endpoint + * @param proto the translation proto + */ +static void +cnat_remove_translation_from_db (index_t cci, cnat_endpoint_t * vip, + ip_protocol_t proto) { clib_bihash_kv_8_8_t bkey; u64 key; - - key = (proto << 16) | port; - key = key << 32 | (u32) cci; + if (INDEX_INVALID == cci) + { + key = proto << 8 | 0x80 | vip->ce_ip.version; + key = key << 16 | vip->ce_port; + key = key << 32 | (u32) vip->ce_sw_if_index; + } + else + { + key = proto << 8; + key = key << 16 | vip->ce_port; + key = key << 32 | (u32) cci; + } bkey.key = key; @@ -98,16 +180,21 @@ cnat_translation_stack (cnat_translation_t * ct) fib_protocol_t fproto; cnat_ep_trk_t *trk; dpo_proto_t dproto; + u8 ep_idx = 0; index_t lbi; fproto = ip_address_family_to_fib_proto (ct->ct_vip.ce_ip.version); dproto = fib_proto_to_dpo (fproto); - lbi = load_balance_create (vec_len (ct->ct_paths), - fib_proto_to_dpo (fproto), IP_FLOW_HASH_DEFAULT); + vec_foreach (trk, ct->ct_paths) if (trk->is_active) + ep_idx++; - vec_foreach (trk, ct->ct_paths) - load_balance_set_bucket (lbi, trk - ct->ct_paths, &trk->ct_dpo); + lbi = load_balance_create (ep_idx, fib_proto_to_dpo (fproto), + IP_FLOW_HASH_DEFAULT); + + ep_idx = 0; + vec_foreach (trk, ct->ct_paths) if (trk->is_active) + load_balance_set_bucket (lbi, ep_idx++, &trk->ct_dpo); dpo_set (&ct->ct_lb, DPO_LOAD_BALANCE, dproto, lbi); dpo_stack (cnat_client_dpo, dproto, &ct->ct_lb, &ct->ct_lb); @@ -128,32 +215,40 @@ cnat_translation_delete (u32 id) vec_foreach (trk, ct->ct_paths) cnat_tracker_release (trk); - cnat_remove_translation_from_db (ct->ct_cci, ct->ct_vip.ce_port, - ct->ct_proto); + cnat_remove_translation_from_db (ct->ct_cci, &ct->ct_vip, ct->ct_proto); cnat_client_translation_deleted (ct->ct_cci); + cnat_translation_unwatch_addr (id, CNAT_RESOLV_ADDR_ANY); pool_put (cnat_translation_pool, ct); return (0); } u32 -cnat_translation_update (const cnat_endpoint_t * vip, +cnat_translation_update (cnat_endpoint_t * vip, ip_protocol_t proto, - const cnat_endpoint_tuple_t * paths, u8 flags) + cnat_endpoint_tuple_t * paths, u8 flags) { - const cnat_endpoint_tuple_t *path; + cnat_endpoint_tuple_t *path; const cnat_client_t *cc; cnat_translation_t *ct; cnat_ep_trk_t *trk; index_t cci; cnat_lazy_init (); + if (cnat_resolve_ep (vip)) + { + /* vip only contains a sw_if_index for now */ + ct = cnat_find_translation (vip->ce_sw_if_index, vip->ce_port, proto); + cci = INDEX_INVALID; + } + else + { + /* do we know of this ep's vip */ + cci = cnat_client_add (&vip->ce_ip, flags); + cc = cnat_client_get (cci); - /* do we know of this ep's vip */ - cci = cnat_client_add (&vip->ce_ip, flags); - cc = cnat_client_get (cci); - - ct = cnat_find_translation (cc->parent_cci, vip->ce_port, proto); + ct = cnat_find_translation (cc->parent_cci, vip->ce_port, proto); + } if (NULL == ct) { @@ -164,8 +259,7 @@ cnat_translation_update (const cnat_endpoint_t * vip, ct->ct_cci = cci; ct->index = ct - cnat_translation_pool; - cnat_add_translation_to_db (cci, ct->ct_vip.ce_port, ct->ct_proto, - ct->index); + cnat_add_translation_to_db (cci, vip, proto, ct->index); cnat_client_translation_added (cci); vlib_validate_combined_counter (&cnat_translation_counters, ct->index); @@ -173,6 +267,10 @@ cnat_translation_update (const cnat_endpoint_t * vip, } ct->flags = flags; + cnat_translation_unwatch_addr (ct->index, CNAT_RESOLV_ADDR_ANY); + cnat_translation_watch_addr (ct->index, 0, vip, + CNAT_RESOLV_ADDR_TRANSLATION); + vec_foreach (trk, ct->ct_paths) { cnat_tracker_release (trk); @@ -180,11 +278,26 @@ cnat_translation_update (const cnat_endpoint_t * vip, vec_reset_length (ct->ct_paths); + u64 path_idx = 0; vec_foreach (path, paths) { + cnat_resolve_ep_tuple (path); + cnat_translation_watch_addr (ct->index, + path_idx << 32 | VLIB_RX, &path->src_ep, + CNAT_RESOLV_ADDR_BACKEND); + cnat_translation_watch_addr (ct->index, + path_idx << 32 | VLIB_TX, &path->dst_ep, + CNAT_RESOLV_ADDR_BACKEND); + path_idx++; + vec_add2 (ct->ct_paths, trk, 1); - cnat_tracker_track (ct->index, path, trk); + clib_memcpy (&trk->ct_ep[VLIB_TX], &path->dst_ep, + sizeof (trk->ct_ep[VLIB_TX])); + clib_memcpy (&trk->ct_ep[VLIB_RX], &path->src_ep, + sizeof (trk->ct_ep[VLIB_RX])); + + cnat_tracker_track (ct->index, trk); } cnat_translation_stack (ct); @@ -404,14 +517,154 @@ cnat_translation_cli_add_del (vlib_main_t * vm, VLIB_CLI_COMMAND (cnat_translation_cli_add_del_command, static) = { .path = "cnat translation", - .short_help = "cnat translation [add|del] proto [TCP|UDP] [vip|real] [ip] [port] [to [ip] [port]->[ip] [port]]", + .short_help = "cnat translation [add|del] proto [TCP|UDP] [vip|real] [ip|sw_if_index [v6]] [port] [to [ip|sw_if_index [v6]] [port]->[ip|sw_if_index [v6]] [port]]", .function = cnat_translation_cli_add_del, }; /* *INDENT-ON* */ +static void +cnat_if_addr_add_del_translation_cb (addr_resolution_t * ar, + ip_address_t * address, u8 is_del) +{ + cnat_translation_t *ct; + ct = cnat_translation_get (ar->cti); + if (!is_del && ct->ct_vip.ce_flags & CNAT_EP_FLAG_RESOLVED) + return; + + cnat_remove_translation_from_db (ct->ct_cci, &ct->ct_vip, ct->ct_proto); + + if (is_del) + { + ct->ct_vip.ce_flags &= ~CNAT_EP_FLAG_RESOLVED; + ct->ct_cci = INDEX_INVALID; + cnat_client_translation_deleted (ct->ct_cci); + /* Are there remaining addresses ? */ + if (0 == cnat_resolve_addr (ar->sw_if_index, ar->af, address)) + is_del = 0; + } + + if (!is_del) + { + ct->ct_cci = cnat_client_add (address, ct->flags); + cnat_client_translation_added (ct->ct_cci); + ip_address_copy (&ct->ct_vip.ce_ip, address); + ct->ct_vip.ce_flags |= CNAT_EP_FLAG_RESOLVED; + } + + cnat_add_translation_to_db (ct->ct_cci, &ct->ct_vip, ct->ct_proto, + ct->index); +} + +static void +cnat_if_addr_add_del_backend_cb (addr_resolution_t * ar, + ip_address_t * address, u8 is_del) +{ + cnat_translation_t *ct; + cnat_ep_trk_t *trk; + cnat_endpoint_t *ep; + + u8 direction = ar->opaque & 0xf; + u32 path_idx = ar->opaque >> 32; + + ct = cnat_translation_get (ar->cti); + + trk = &ct->ct_paths[path_idx]; + ep = &trk->ct_ep[direction]; + + if (!is_del && ep->ce_flags & CNAT_EP_FLAG_RESOLVED) + return; + + ASSERT (ep->ce_sw_if_index == ar->sw_if_index); + + if (is_del) + { + ep->ce_flags &= ~CNAT_EP_FLAG_RESOLVED; + /* Are there remaining addresses ? */ + if (0 == cnat_resolve_addr (ar->sw_if_index, ar->af, address)) + is_del = 0; + } + + if (!is_del) + { + ip_address_copy (&ep->ce_ip, address); + ep->ce_flags |= CNAT_EP_FLAG_RESOLVED; + } + cnat_tracker_track (ar->cti, trk); + + cnat_translation_stack (ct); +} + +static void +cnat_if_addr_add_del_snat_cb (addr_resolution_t * ar, ip_address_t * address, + u8 is_del) +{ + cnat_endpoint_t *ep; + ep = AF_IP4 == ar->af ? &cnat_main.snat_ip4 : &cnat_main.snat_ip6; + + if (!is_del && ep->ce_flags & CNAT_EP_FLAG_RESOLVED) + return; + + if (is_del) + { + ep->ce_flags &= ~CNAT_EP_FLAG_RESOLVED; + /* Are there remaining addresses ? */ + if (0 == cnat_resolve_addr (ar->sw_if_index, ar->af, address)) + is_del = 0; + } + + if (!is_del) + { + ip_address_copy (&ep->ce_ip, address); + ep->ce_flags |= CNAT_EP_FLAG_RESOLVED; + } + +} + +static void +cnat_if_addr_add_del_callback (u32 sw_if_index, ip_address_t * address, + u8 is_del) +{ + addr_resolution_t *ar; + /* *INDENT-OFF* */ + pool_foreach (ar, tr_resolutions, ({ + if (ar->sw_if_index != sw_if_index) + continue; + if (ar->af != ip_addr_version (address)) + continue; + cnat_if_addr_add_cbs[ar->type] (ar, address, is_del); + })); + /* *INDENT-ON* */ +} + +static void +cnat_ip6_if_addr_add_del_callback (struct ip6_main_t *im, + uword opaque, u32 sw_if_index, + ip6_address_t * address, + u32 address_length, u32 if_address_index, + u32 is_del) +{ + ip_address_t addr; + ip_address_set (&addr, address, AF_IP6); + cnat_if_addr_add_del_callback (sw_if_index, &addr, is_del); +} + +static void +cnat_ip4_if_addr_add_del_callback (struct ip4_main_t *im, + uword opaque, u32 sw_if_index, + ip4_address_t * address, + u32 address_length, u32 if_address_index, + u32 is_del) +{ + ip_address_t addr; + ip_address_set (&addr, address, AF_IP4); + cnat_if_addr_add_del_callback (sw_if_index, &addr, is_del); +} + static clib_error_t * cnat_translation_init (vlib_main_t * vm) { + ip4_main_t *i4m = &ip4_main; + ip6_main_t *i6m = &ip6_main; cnat_main_t *cm = &cnat_main; cnat_translation_fib_node_type = fib_node_register_new_type (&cnat_translation_vft); @@ -420,6 +673,20 @@ cnat_translation_init (vlib_main_t * vm) cm->translation_hash_buckets, cm->translation_hash_memory); + ip4_add_del_interface_address_callback_t cb4; + cb4.function = cnat_ip4_if_addr_add_del_callback; + vec_add1 (i4m->add_del_interface_address_callbacks, cb4); + + ip6_add_del_interface_address_callback_t cb6; + cb6.function = cnat_ip6_if_addr_add_del_callback; + vec_add1 (i6m->add_del_interface_address_callbacks, cb6); + + vec_validate (cnat_if_addr_add_cbs, CNAT_ADDR_N_RESOLUTIONS); + cnat_if_addr_add_cbs[CNAT_RESOLV_ADDR_BACKEND] = + cnat_if_addr_add_del_backend_cb; + cnat_if_addr_add_cbs[CNAT_RESOLV_ADDR_SNAT] = cnat_if_addr_add_del_snat_cb; + cnat_if_addr_add_cbs[CNAT_RESOLV_ADDR_TRANSLATION] = + cnat_if_addr_add_del_translation_cb; return (NULL); } -- cgit 1.2.3-korg