cnat: Destination based NAT

Type: feature

Signed-off-by: Neale Ranns <nranns@cisco.com>
Change-Id: I64a99a4fbc674212944247793fd5c1fb701408cb
This commit is contained in:
Neale Ranns
2020-05-19 07:17:19 +00:00
committed by Andrew Yourtchenko
parent 133c91c1c0
commit 29f3c7d2ec
25 changed files with 4991 additions and 35 deletions

View File

@ -682,6 +682,12 @@ I: urpf
M: Neale Ranns <nranns@cisco.com>
F: src/plugins/urpf
Plugin - CNat
I: cnat
M: Nathan Skrzypczak <nathan.skrzypczak@gmail.com>
M: Neale Ranns <nranns@cisco.com>
F: src/plugins/cnat
VPP Config Tooling
I: vpp_config
M: John DeNisco <jdenisco@cisco.com>

View File

@ -0,0 +1,28 @@
# Copyright (c) 2018 Cisco and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
add_vpp_plugin(cnat
SOURCES
cnat_api.c
cnat_client.c
cnat_node_snat.c
cnat_node_vip.c
cnat_scanner.c
cnat_session.c
cnat_translation.c
cnat_types.c
cnat_snat.c
API_FILES
cnat.api
)

View File

@ -0,0 +1,17 @@
---
name: Cloud NAT
maintainer: Nathan Skrzypczak <nathan.skrzypczak@gmail.com>
features:
- Destination based address/port translation
- Conditional sourceNATing based on prefix exclusions
description: "This plugin is intended to complement the VPP's plugin_nat for
Cloud use-cases. It allows for source/destination address/port
translation based on multiple criterias. It is intended to be modular
enough so that one could write a use-case optimised translation function
without having to deal with actually re-writing packets or maintining
sessions.
This plugin supports multithreading. Workers share a unique bihash where
sessions are stored."
state: development
properties: [API, CLI, MULTITHREAD]

View File

@ -0,0 +1,112 @@
/*
* Copyright (c) 2016 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#undef BIHASH_TYPE
#undef BIHASH_KVP_PER_PAGE
#undef BIHASH_32_64_SVM
#undef BIHASH_ENABLE_STATS
#undef BIHASH_KVP_AT_BUCKET_LEVEL
#undef BIHASH_LAZY_INSTANTIATE
#undef BIHASH_BUCKET_PREFETCH_CACHE_LINES
#define BIHASH_TYPE _40_48
#define BIHASH_KVP_PER_PAGE 2
#define BIHASH_KVP_AT_BUCKET_LEVEL 1
#define BIHASH_LAZY_INSTANTIATE 1
#define BIHASH_BUCKET_PREFETCH_CACHE_LINES 2
#ifndef __included_bihash_40_48_h__
#define __included_bihash_40_48_h__
#include <vppinfra/crc32.h>
#include <vppinfra/heap.h>
#include <vppinfra/format.h>
#include <vppinfra/pool.h>
#include <vppinfra/xxhash.h>
typedef struct
{
u64 key[5];
u64 value[6];
} clib_bihash_kv_40_48_t;
static inline int
clib_bihash_is_free_40_48 (const clib_bihash_kv_40_48_t * v)
{
/* Free values are clib_memset to 0xff, check a bit... */
if (v->key[0] == ~0ULL && v->value[0] == ~0ULL)
return 1;
return 0;
}
static inline u64
clib_bihash_hash_40_48 (const clib_bihash_kv_40_48_t * v)
{
#ifdef clib_crc32c_uses_intrinsics
return clib_crc32c ((u8 *) v->key, 40);
#else
u64 tmp = v->key[0] ^ v->key[1] ^ v->key[2] ^ v->key[3] ^ v->key[4];
return clib_xxhash (tmp);
#endif
}
static inline u8 *
format_bihash_kvp_40_48 (u8 * s, va_list * args)
{
clib_bihash_kv_40_48_t *v = va_arg (*args, clib_bihash_kv_40_48_t *);
s =
format (s,
"key %llu %llu %llu %llu %llu value %llu %llu %llu %llu %llu %u",
v->key[0], v->key[1], v->key[2], v->key[3], v->key[4],
v->value[0], v->value[1], v->value[2], v->value[3], v->value[4],
v->value[5]);
return s;
}
static inline int
clib_bihash_key_compare_40_48 (u64 * a, u64 * b)
{
#if defined (CLIB_HAVE_VEC512)
u64x8 v;
v = u64x8_load_unaligned (a) ^ u64x8_load_unaligned (b);
return (u64x8_is_zero_mask (v) & 0x1f) == 0;
#elif defined (CLIB_HAVE_VEC256)
u64x4 v = { a[4] ^ b[4], 0, 0, 0 };
v |= u64x4_load_unaligned (a) ^ u64x4_load_unaligned (b);
return u64x4_is_all_zero (v);
#elif defined(CLIB_HAVE_VEC128) && defined(CLIB_HAVE_VEC128_UNALIGNED_LOAD_STORE)
u64x2 v = { a[4] ^ b[4], 0 };
v |= u64x2_load_unaligned (a) ^ u64x2_load_unaligned (b);
v |= u64x2_load_unaligned (a + 2) ^ u64x2_load_unaligned (b + 2);
return u64x2_is_all_zero (v);
#else
return ((a[0] ^ b[0]) | (a[1] ^ b[1]) | (a[2] ^ b[2]) | (a[3] ^ b[3])
| (a[4] ^ b[4])) == 0;
#endif
}
#undef __included_bihash_template_h__
#include <vppinfra/bihash_template.h>
#endif /* __included_bihash_40_48_h__ */
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/

136
src/plugins/cnat/cnat.api Normal file
View File

@ -0,0 +1,136 @@
/* Hey Emacs use -*- mode: C -*- */
/*
* Copyright (c) 2016 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/** \file
This file defines the vpp control-plane API messages
used to control the ABF plugin
*/
option version = "0.1.0";
import "vnet/ip/ip_types.api";
import "vnet/fib/fib_types.api";
enum cnat_translation_flags:u8
{
CNAT_TRANSLATION_ALLOC_PORT = 1,
};
typedef cnat_endpoint
{
vl_api_address_t addr;
u16 port;
};
typedef cnat_endpoint_tuple
{
vl_api_cnat_endpoint_t dst_ep;
vl_api_cnat_endpoint_t src_ep;
};
typedef cnat_translation
{
vl_api_cnat_endpoint_t vip;
u32 id;
vl_api_ip_proto_t ip_proto;
u8 is_real_ip;
u8 flags;
u8 n_paths;
vl_api_cnat_endpoint_tuple_t paths[n_paths];
};
define cnat_translation_update
{
u32 client_index;
u32 context;
vl_api_cnat_translation_t translation;
};
define cnat_translation_update_reply
{
u32 context;
i32 retval;
u32 id;
};
autoreply define cnat_translation_del
{
u32 client_index;
u32 context;
u32 id;
};
define cnat_translation_details
{
u32 context;
vl_api_cnat_translation_t translation;
};
define cnat_translation_dump
{
u32 client_index;
u32 context;
};
autoreply define cnat_session_purge
{
u32 client_index;
u32 context;
};
typedef cnat_session
{
vl_api_cnat_endpoint_t src;
vl_api_cnat_endpoint_t dst;
vl_api_cnat_endpoint_t new;
vl_api_ip_proto_t ip_proto;
f64 timestamp;
};
define cnat_session_details
{
u32 context;
vl_api_cnat_session_t session;
};
define cnat_session_dump
{
u32 client_index;
u32 context;
};
autoreply define cnat_set_snat_addresses
{
u32 client_index;
u32 context;
vl_api_ip4_address_t snat_ip4;
vl_api_ip6_address_t snat_ip6;
};
autoreply define cnat_add_del_snat_prefix
{
u32 client_index;
u32 context;
u8 is_add;
vl_api_prefix_t prefix;
};
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/

81
src/plugins/cnat/cnat.rst Normal file
View File

@ -0,0 +1,81 @@
.. _dev_cnat:
.. toctree::
Cloud NAT
=========
Overview
________
This plugin covers specific NAT use-cases that come mostly
from the container networking world. On the contraty of the
NAT concepts used for e.g. a home gateway, there is no notion
of 'outside' and 'inside'. We handle Virtual (or Real) IPs and
translations of the packets destined to them
Terminology & Usage
___________________
Setting up the NAT will consist in the creation of a translation
that has several backends. A translation is 3-tuple containing :
a fully qualified IP address a port and a protocol. All packets
destined to it (ip, port) will then choose one of the backends,
and follow its rewrite rules.
A backend consists of four rewrites components (source & destination
address, source & destination port) that shall be applied to packets
on the way in, and reverted on the way back.
Backends are equally load-balanced with a flow hash. The choice
of a backend for a flow will trigger the creation of a NAT session,
that will store the packet rewrite to do and the one to undo
until the flow is reset or a timeout is reached
Translating Addresses
---------------------
In this example, all packets destined to 30.0.0.2:80 will be
rewritten so that their destination IP is 20.0.0.1 and destination
port 8080. Here 30.0.0.2 has to be a virtual IP, it cannot be
assigned to an interface
.. code-block:: console
cnat translation add proto TCP vip 30.0.0.2 80 to ->20.0.0.1 8080
If 30.0.0.2 is the address of an interface, we can use the following
to do the same translation, and additionnaly change the source.
address with 1.2.3.4
.. code-block:: console
cnat translation add proto TCP real 30.0.0.2 80 to 1.2.3.4->20.0.0.1 8080
To show existing translations and sessions you can use
.. code-block:: console
cnat show session verbose
cant show translation
SourceNATing outgoing traffic
-----------------------------
A independant part of the plugin allows changing the source address
of outgoing traffic on a per-interface basis.
.. code-block:: console
cnat snat with 30::1
cnat snat exclude 20::/100
ex_ctl _calico_master cnat snat exclude 10::/100
ex_ctl _calico_master set interface feature tap0 ip6-cnat-snat arc ip6-unicast
Extending the NAT
_________________

317
src/plugins/cnat/cnat_api.c Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,226 @@
/*
* Copyright (c) 2020 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __CNAT_CLIENT_H__
#define __CNAT_CLIENT_H__
#include <cnat/cnat_types.h>
/**
* A client is a representation of an IP address behind the NAT.
* A client thus sends packet to a VIP.
* Clients are learned in the Data-plane when they send packets,
* but, since they make additions to the FIB they must be programmed
* in the main thread. They are aged out when they become idle.
*
* A client interposes in the FIB graph for the prefix corresponding
* to the client (e.g. client's-IP/32). As a result this client object
* is cloned as the interpose DPO. The clones are removed when the lock
* count drops to zero. The originals are removed when the client ages.
* At forwarding time the client preforms the reverse translation and
* then ships the packet to where the FIB would send it.
*/
typedef struct cnat_client_t_
{
CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
/**
* the client's IP address
*/
ip_address_t cc_ip;
/**
* How to send packets to this client post translation
*/
dpo_id_t cc_parent;
/**
* the FIB entry this client sources
*/
fib_node_index_t cc_fei;
/**
* number of DPO locks
*/
u32 cc_locks;
/**
* Translations refcount for cleanup
*/
u32 tr_refcnt;
/**
* Session refcount for cleanup
*/
u32 session_refcnt;
/**
* Parent cnat_client index if cloned via interpose
* or own index if vanilla client.
* Used to get translations & update session_refcnt
*/
index_t parent_cci;
/**
* Client flags
*/
u8 flags;
} cnat_client_t;
extern u8 *format_cnat_client (u8 * s, va_list * args);
extern void cnat_client_free_by_ip (ip46_address_t * addr, u8 af);
extern cnat_client_t *cnat_client_pool;
extern dpo_type_t cnat_client_dpo;
#define CC_INDEX_INVALID ((u32)(~0))
static_always_inline cnat_client_t *
cnat_client_get (index_t i)
{
return (pool_elt_at_index (cnat_client_pool, i));
}
typedef struct cnat_learn_arg_t_
{
ip_address_t addr;
} cnat_learn_arg_t;
/**
* A translation that references this VIP was deleted
*/
extern void cnat_client_translation_deleted (index_t cci);
/**
* A translation that references this VIP was added
*/
extern void cnat_client_translation_added (index_t cci);
/**
* Called in the main thread by RPC from the workers to learn a
* new client
*/
extern void cnat_client_learn (const cnat_learn_arg_t * l);
extern index_t cnat_client_add (const ip_address_t * ip, u8 flags);
/**
* Check all the clients were purged by translation & session purge
*/
extern int cnat_client_purge (void);
/**
* CNat Client (dpo) flags
*/
typedef enum
{
/* IP already present in the FIB, need to interpose dpo */
CNAT_FLAG_EXCLUSIVE = (1 << 1),
/* Prune this entry */
CNAT_FLAG_EXPIRES = (1 << 2),
} cnat_entry_flag_t;
extern void cnat_client_throttle_pool_process ();
/**
* DB of clients
*/
typedef struct cnat_client_db_t_
{
uword *crd_cip4;
uword *crd_cip6;
/* Pool of addresses that have been throttled
and need to be refcounted before calling
cnat_client_free_by_ip */
ip_address_t **throttle_pool;
clib_spinlock_t *throttle_pool_lock;
} cnat_client_db_t;
extern cnat_client_db_t cnat_client_db;
/**
* Find a client from an IP4 address
*/
static_always_inline cnat_client_t *
cnat_client_ip4_find (const ip4_address_t * ip)
{
uword *p;
p = hash_get (cnat_client_db.crd_cip4, ip->as_u32);
if (p)
return (pool_elt_at_index (cnat_client_pool, p[0]));
return (NULL);
}
static_always_inline u32
cnat_client_ip4_find_index (const ip4_address_t * ip)
{
uword *p;
p = hash_get (cnat_client_db.crd_cip4, ip->as_u32);
if (p)
return p[0];
return -1;
}
/**
* Find a client from an IP6 address
*/
static_always_inline cnat_client_t *
cnat_client_ip6_find (const ip6_address_t * ip)
{
uword *p;
p = hash_get_mem (cnat_client_db.crd_cip6, ip);
if (p)
return (pool_elt_at_index (cnat_client_pool, p[0]));
return (NULL);
}
/**
* Add a session refcnt to this client
*/
static_always_inline u32
cnat_client_cnt_session (cnat_client_t * cc)
{
cnat_client_t *ccp = cnat_client_get (cc->parent_cci);
return clib_atomic_add_fetch (&ccp->session_refcnt, 1);
}
/**
* Del a session refcnt to this client
*/
static_always_inline u32
cnat_client_uncnt_session (cnat_client_t * cc)
{
cnat_client_t *ccp = cnat_client_get (cc->parent_cci);
return clib_atomic_sub_fetch (&ccp->session_refcnt, 1);
}
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/
#endif

View File

@ -0,0 +1,19 @@
/*
* Copyright (c) 2020 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
cnat_error (NONE, "no error")
cnat_error (EXHAUSTED_PORTS, "no more free ports")

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,237 @@
/*
* Copyright (c) 2020 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <vlibmemory/api.h>
#include <cnat/cnat_node.h>
#include <cnat/cnat_snat.h>
typedef enum cnat_snat_next_
{
CNAT_SNAT_NEXT_DROP,
CNAT_SNAT_N_NEXT,
} cnat_snat_next_t;
typedef struct cnat_snat_trace_
{
u32 found;
cnat_session_t session;
} cnat_snat_trace_t;
vlib_node_registration_t cnat_snat_ip4_node;
vlib_node_registration_t cnat_snat_ip6_node;
static u8 *
format_cnat_snat_trace (u8 * s, va_list * args)
{
CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
cnat_snat_trace_t *t = va_arg (*args, cnat_snat_trace_t *);
if (t->found)
s = format (s, "found: %U", format_cnat_session, &t->session, 1);
else
s = format (s, "not found");
return s;
}
/* CNat sub for source NAT as a feature arc on ip[46]-unicast
This node's sub shouldn't apply to the same flows as
cnat_vip_inline */
always_inline uword
cnat_snat_inline (vlib_main_t * vm,
vlib_node_runtime_t * node,
vlib_buffer_t * b,
cnat_node_ctx_t * ctx, int rv, cnat_session_t * session)
{
cnat_main_t *cm = &cnat_main;
ip4_header_t *ip4;
ip_protocol_t iproto;
ip6_header_t *ip6;
udp_header_t *udp0;
u32 arc_next0;
u16 next0;
u16 sport;
if (AF_IP4 == ctx->af)
{
ip4 = vlib_buffer_get_current (b);
iproto = ip4->protocol;
udp0 = (udp_header_t *) (ip4 + 1);
}
else
{
ip6 = vlib_buffer_get_current (b);
iproto = ip6->protocol;
udp0 = (udp_header_t *) (ip6 + 1);
}
/* By default don't follow previous next0 */
vnet_feature_next (&arc_next0, b);
next0 = arc_next0;
if (iproto != IP_PROTOCOL_UDP && iproto != IP_PROTOCOL_TCP)
{
/* Dont translate */
goto trace;
}
if (!rv)
{
/* session table hit */
cnat_timestamp_update (session->value.cs_ts_index, ctx->now);
}
else
{
ip46_address_t ip46_dst_address;
if (AF_IP4 == ctx->af)
ip46_address_set_ip4 (&ip46_dst_address, &ip4->dst_address);
else
ip46_address_set_ip6 (&ip46_dst_address, &ip6->dst_address);
rv = cnat_search_snat_prefix (&ip46_dst_address, ctx->af);
if (!rv)
{
/* Prefix table hit, we shouldn't source NAT */
goto trace;
}
/* New flow, create the sessions if necessary. session will be a snat
session, and rsession will be a dnat session
Note: packet going through this path are going to the outside,
so they will never hit the NAT again (they are not going towards
a VIP) */
if (AF_IP4 == ctx->af)
{
ip46_address_set_ip4 (&session->value.cs_ip[VLIB_RX],
&cm->snat_ip4);
ip46_address_set_ip4 (&session->value.cs_ip[VLIB_TX],
&ip4->dst_address);
}
else
{
ip46_address_set_ip6 (&session->value.cs_ip[VLIB_RX],
&cm->snat_ip6);
ip46_address_set_ip6 (&session->value.cs_ip[VLIB_TX],
&ip6->dst_address);
}
/* Port allocation, first try to use the original port, allocate one
if it is already used */
sport = udp0->src_port;
rv = cnat_allocate_port (cm, &sport);
if (rv)
{
vlib_node_increment_counter (vm, cnat_snat_ip4_node.index,
CNAT_ERROR_EXHAUSTED_PORTS, 1);
next0 = CNAT_SNAT_NEXT_DROP;
goto trace;
}
session->value.cs_port[VLIB_RX] = sport;
session->value.cs_port[VLIB_TX] = udp0->dst_port;
session->value.cs_lbi = INDEX_INVALID;
session->value.flags =
CNAT_SESSION_FLAG_NO_CLIENT | CNAT_SESSION_FLAG_ALLOC_PORT;
cnat_session_create (session, ctx, CNAT_SESSION_FLAG_HAS_SNAT);
}
if (AF_IP4 == ctx->af)
cnat_translation_ip4 (session, ip4, udp0);
else
cnat_translation_ip6 (session, ip6, udp0);
trace:
if (PREDICT_FALSE (b->flags & VLIB_BUFFER_IS_TRACED))
{
cnat_snat_trace_t *t;
t = vlib_add_trace (vm, node, b, sizeof (*t));
if (NULL != session)
clib_memcpy (&t->session, session, sizeof (t->session));
}
return next0;
}
VLIB_NODE_FN (cnat_snat_ip4_node) (vlib_main_t * vm,
vlib_node_runtime_t * node,
vlib_frame_t * frame)
{
if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
return cnat_node_inline (vm, node, frame, cnat_snat_inline, AF_IP4,
1 /* do_trace */ );
return cnat_node_inline (vm, node, frame, cnat_snat_inline, AF_IP4,
0 /* do_trace */ );
}
VLIB_NODE_FN (cnat_snat_ip6_node) (vlib_main_t * vm,
vlib_node_runtime_t * node,
vlib_frame_t * frame)
{
if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
return cnat_node_inline (vm, node, frame, cnat_snat_inline, AF_IP6,
1 /* do_trace */ );
return cnat_node_inline (vm, node, frame, cnat_snat_inline, AF_IP6,
0 /* do_trace */ );
}
/* *INDENT-OFF* */
VLIB_REGISTER_NODE (cnat_snat_ip4_node) =
{
.name = "ip4-cnat-snat",
.vector_size = sizeof (u32),
.format_trace = format_cnat_snat_trace,
.type = VLIB_NODE_TYPE_INTERNAL,
.n_errors = CNAT_N_ERROR,
.error_strings = cnat_error_strings,
.n_next_nodes = CNAT_SNAT_N_NEXT,
.next_nodes =
{
[CNAT_SNAT_NEXT_DROP] = "ip4-drop",
}
};
VLIB_REGISTER_NODE (cnat_snat_ip6_node) =
{
.name = "ip6-cnat-snat",
.vector_size = sizeof (u32),
.format_trace = format_cnat_snat_trace,
.type = VLIB_NODE_TYPE_INTERNAL,
.n_errors = CNAT_N_ERROR,
.error_strings = cnat_error_strings,
.n_next_nodes = CNAT_SNAT_N_NEXT,
.next_nodes =
{
[CNAT_SNAT_NEXT_DROP] = "ip6-drop",
}
};
/* *INDENT-ON* */
VNET_FEATURE_INIT (cnat_snat_ip4_node, static) =
{
.arc_name = "ip4-unicast",.node_name = "ip4-cnat-snat",};
VNET_FEATURE_INIT (cnat_snat_ip6_node, static) =
{
.arc_name = "ip6-unicast",.node_name = "ip6-cnat-snat",};
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,113 @@
/*
* Copyright (c) 2020 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <cnat/cnat_session.h>
#include <cnat/cnat_client.h>
typedef enum cnat_scanner_cmd_t_
{
CNAT_SCANNER_OFF,
CNAT_SCANNER_ON,
} cnat_scanner_cmd_t;
static uword
cnat_scanner_process (vlib_main_t * vm,
vlib_node_runtime_t * rt, vlib_frame_t * f)
{
uword event_type, *event_data = 0;
cnat_main_t *cm = &cnat_main;
f64 start_time;
int enabled = 1, i = 0;
while (1)
{
if (enabled)
vlib_process_wait_for_event_or_clock (vm, cm->scanner_timeout);
else
vlib_process_wait_for_event (vm);
event_type = vlib_process_get_events (vm, &event_data);
vec_reset_length (event_data);
start_time = vlib_time_now (vm);
switch (event_type)
{
/* timer expired */
case ~0:
break;
case CNAT_SCANNER_OFF:
enabled = 0;
break;
case CNAT_SCANNER_ON:
enabled = 1;
break;
default:
ASSERT (0);
}
cnat_client_throttle_pool_process ();
i = cnat_session_scan (vm, start_time, i);
}
return 0;
}
/* *INDENT-OFF* */
VLIB_REGISTER_NODE (cnat_scanner_process_node) = {
.function = cnat_scanner_process,
.type = VLIB_NODE_TYPE_PROCESS,
.name = "cnat-scanner-process",
};
/* *INDENT-ON* */
static clib_error_t *
cnat_scanner_cmd (vlib_main_t * vm,
unformat_input_t * input, vlib_cli_command_t * c)
{
cnat_scanner_cmd_t cmd;
cmd = CNAT_SCANNER_ON;
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
{
if (unformat (input, "on"))
cmd = CNAT_SCANNER_ON;
else if (unformat (input, "off"))
cmd = CNAT_SCANNER_OFF;
else
return (clib_error_return (0, "unknown input '%U'",
format_unformat_error, input));
}
vlib_process_signal_event (vm, cnat_scanner_process_node.index, cmd, 0);
return (NULL);
}
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (cnat_scanner_cmd_node, static) = {
.path = "test cnat scanner",
.function = cnat_scanner_cmd,
.short_help = "test cnat scanner",
};
/* *INDENT-ON* */
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/

View File

@ -0,0 +1,269 @@
/*
* Copyright (c) 2020 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <vnet/ip/ip.h>
#include <cnat/cnat_session.h>
#include <vppinfra/bihash_template.h>
#include <vppinfra/bihash_template.c>
clib_bihash_40_48_t cnat_session_db;
typedef struct cnat_session_walk_ctx_t_
{
cnat_session_walk_cb_t cb;
void *ctx;
} cnat_session_walk_ctx_t;
static int
cnat_session_walk_cb (BVT (clib_bihash_kv) * kv, void *arg)
{
cnat_session_t *session = (cnat_session_t *) kv;
cnat_session_walk_ctx_t *ctx = arg;
ctx->cb (session, ctx->ctx);
return (BIHASH_WALK_CONTINUE);
}
void
cnat_session_walk (cnat_session_walk_cb_t cb, void *ctx)
{
cnat_session_walk_ctx_t wctx = {
.cb = cb,
.ctx = ctx,
};
BV (clib_bihash_foreach_key_value_pair) (&cnat_session_db,
cnat_session_walk_cb, &wctx);
}
typedef struct cnat_session_purge_walk_t_
{
clib_bihash_kv_40_48_t *keys;
} cnat_session_purge_walk_ctx_t;
static int
cnat_session_purge_walk (BVT (clib_bihash_kv) * key, void *arg)
{
cnat_session_purge_walk_ctx_t *ctx = arg;
vec_add1 (ctx->keys, *key);
return (BIHASH_WALK_CONTINUE);
}
u8 *
format_cnat_session (u8 * s, va_list * args)
{
cnat_session_t *sess = va_arg (*args, cnat_session_t *);
CLIB_UNUSED (int verbose) = va_arg (*args, int);
f64 ts = 0;
if (!pool_is_free_index (cnat_timestamps, sess->value.cs_ts_index))
ts = cnat_timestamp_exp (sess->value.cs_ts_index);
s =
format (s,
"session:[%U;%d -> %U;%d, %U] => %U;%d -> %U;%d lb:%d age:%f",
format_ip46_address, &sess->key.cs_ip[VLIB_RX], IP46_TYPE_ANY,
clib_host_to_net_u16 (sess->key.cs_port[VLIB_RX]),
format_ip46_address, &sess->key.cs_ip[VLIB_TX], IP46_TYPE_ANY,
clib_host_to_net_u16 (sess->key.cs_port[VLIB_TX]),
format_ip_protocol, sess->key.cs_proto, format_ip46_address,
&sess->value.cs_ip[VLIB_RX], IP46_TYPE_ANY,
clib_host_to_net_u16 (sess->value.cs_port[VLIB_RX]),
format_ip46_address, &sess->value.cs_ip[VLIB_TX], IP46_TYPE_ANY,
clib_host_to_net_u16 (sess->value.cs_port[VLIB_TX]),
sess->value.cs_lbi, ts);
return (s);
}
static clib_error_t *
cnat_session_show (vlib_main_t * vm,
unformat_input_t * input, vlib_cli_command_t * cmd)
{
u8 verbose = 0;
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
{
if (unformat (input, "verbose"))
verbose = 1;
else
return (clib_error_return (0, "unknown input '%U'",
format_unformat_error, input));
}
vlib_cli_output (vm, "CNat Sessions: now:%f\n%U\n",
vlib_time_now (vm),
BV (format_bihash), &cnat_session_db, verbose);
return (NULL);
}
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (cnat_session_show_cmd_node, static) = {
.path = "show cnat session",
.function = cnat_session_show,
.short_help = "show cnat session",
.is_mp_safe = 1,
};
/* *INDENT-ON* */
void
cnat_session_free (cnat_session_t * session)
{
clib_bihash_kv_40_48_t *bkey = (clib_bihash_kv_40_48_t *) session;
/* age it */
if (session->value.flags & CNAT_SESSION_FLAG_ALLOC_PORT)
cnat_free_port (session->value.cs_port[VLIB_RX]);
if (!(session->value.flags & CNAT_SESSION_FLAG_NO_CLIENT))
cnat_client_free_by_ip (&session->key.cs_ip[VLIB_TX], session->key.cs_af);
cnat_timestamp_free (session->value.cs_ts_index);
clib_bihash_add_del_40_48 (&cnat_session_db, bkey, 0 /* is_add */ );
}
int
cnat_session_purge (void)
{
/* flush all the session from the DB */
cnat_session_purge_walk_ctx_t ctx = { };
clib_bihash_kv_40_48_t *key;
BV (clib_bihash_foreach_key_value_pair) (&cnat_session_db,
cnat_session_purge_walk, &ctx);
vec_foreach (key, ctx.keys) cnat_session_free ((cnat_session_t *) key);
vec_free (ctx.keys);
return (0);
}
u64
cnat_session_scan (vlib_main_t * vm, f64 start_time, int i)
{
BVT (clib_bihash) * h = &cnat_session_db;
int j, k;
/* Don't scan the l2 fib if it hasn't been instantiated yet */
if (alloc_arena (h) == 0)
return 0.0;
for (i = 0; i < h->nbuckets; i++)
{
/* allow no more than 100us without a pause */
if ((vlib_time_now (vm) - start_time) > 10e-5)
return (i);
if (i < (h->nbuckets - 3))
{
BVT (clib_bihash_bucket) * b =
BV (clib_bihash_get_bucket) (h, i + 3);
CLIB_PREFETCH (b, CLIB_CACHE_LINE_BYTES, LOAD);
b = BV (clib_bihash_get_bucket) (h, i + 1);
if (!BV (clib_bihash_bucket_is_empty) (b))
{
BVT (clib_bihash_value) * v =
BV (clib_bihash_get_value) (h, b->offset);
CLIB_PREFETCH (v, CLIB_CACHE_LINE_BYTES, LOAD);
}
}
BVT (clib_bihash_bucket) * b = BV (clib_bihash_get_bucket) (h, i);
if (BV (clib_bihash_bucket_is_empty) (b))
continue;
BVT (clib_bihash_value) * v = BV (clib_bihash_get_value) (h, b->offset);
for (j = 0; j < (1 << b->log2_pages); j++)
{
for (k = 0; k < BIHASH_KVP_PER_PAGE; k++)
{
if (v->kvp[k].key[0] == ~0ULL && v->kvp[k].value[0] == ~0ULL)
continue;
cnat_session_t *session = (cnat_session_t *) & v->kvp[k];
if (start_time >
cnat_timestamp_exp (session->value.cs_ts_index))
{
/* age it */
cnat_session_free (session);
/*
* Note: we may have just freed the bucket's backing
* storage, so check right here...
*/
if (BV (clib_bihash_bucket_is_empty) (b))
goto doublebreak;
}
}
v++;
}
doublebreak:
;
}
/* start again */
return (0);
}
static clib_error_t *
cnat_session_init (vlib_main_t * vm)
{
cnat_main_t *cm = &cnat_main;
BV (clib_bihash_init) (&cnat_session_db,
"CNat Session DB", cm->session_hash_buckets,
cm->session_hash_memory);
BV (clib_bihash_set_kvp_format_fn) (&cnat_session_db, format_cnat_session);
return (NULL);
}
VLIB_INIT_FUNCTION (cnat_session_init);
static clib_error_t *
cnat_timestamp_show (vlib_main_t * vm,
unformat_input_t * input, vlib_cli_command_t * cmd)
{
cnat_timestamp_t *ts;
clib_rwlock_reader_lock (&cnat_main.ts_lock);
/* *INDENT-OFF* */
pool_foreach (ts, cnat_timestamps, ({
vlib_cli_output (vm, "[%d] last_seen:%f lifetime:%u ref:%u",
ts - cnat_timestamps,
ts->last_seen, ts->lifetime, ts->refcnt);
}));
/* *INDENT-ON* */
clib_rwlock_reader_unlock (&cnat_main.ts_lock);
return (NULL);
}
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (cnat_timestamp_show_cmd, static) = {
.path = "show cnat timestamp",
.function = cnat_timestamp_show,
.short_help = "show cnat timestamp",
.is_mp_safe = 1,
};
/* *INDENT-ON* */
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/

View File

@ -0,0 +1,157 @@
/*
* Copyright (c) 2020 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __CNAT_SESSION_H__
#define __CNAT_SESSION_H__
#include <vnet/udp/udp.h>
#include <cnat/cnat_types.h>
#include <cnat/cnat_client.h>
#include <cnat/bihash_40_48.h>
/**
* A session represents the memory of a translation.
* In the tx direction (from behind to in front of the NAT), the
* session is preserved so subsequent packets follow the same path
* even if the translation has been updated. In the tx direction
* the session represents the swap from the VIP to the server address
* In the RX direction the swap is from the server address/port to VIP.
*
* A session exists only as key and value in the bihash, there is no
* pool for this object. If there were a pool, one would need to be
* concerned about what worker is using it.
*/
typedef struct cnat_session_t_
{
/**
* this key sits in the same memory location a 'key' in the bihash kvp
*/
struct
{
/**
* IP 4/6 address in the rx/tx direction
*/
ip46_address_t cs_ip[VLIB_N_DIR];
/**
* ports in rx/tx
*/
u16 cs_port[VLIB_N_DIR];
/**
* The IP protocol TCP or UDP only supported
*/
ip_protocol_t cs_proto;
/**
* The address family describing the IP addresses
*/
u8 cs_af;
/**
* spare space
*/
u8 __cs_pad[2];
} key;
/**
* this value sits in the same memory location a 'value' in the bihash kvp
*/
struct
{
/**
* The IP address to translate to.
*/
ip46_address_t cs_ip[VLIB_N_DIR];
/**
* the port to translate to.
*/
u16 cs_port[VLIB_N_DIR];
/**
* The load balance object to use to forward
*/
index_t cs_lbi;
/**
* Timestamp index this session was last used
*/
u32 cs_ts_index;
/**
* Indicates a return path session that was source NATed
* on the way in.
*/
u32 flags;
} value;
} cnat_session_t;
typedef enum cnat_session_flag_t_
{
CNAT_SESSION_FLAG_HAS_SNAT = (1 << 0),
CNAT_SESSION_FLAG_ALLOC_PORT = (1 << 1),
CNAT_SESSION_FLAG_NO_CLIENT = (1 << 2),
} cnat_session_flag_t;
extern u8 *format_cnat_session (u8 * s, va_list * args);
/**
* Ensure the session object correctly overlays the bihash key/value pair
*/
STATIC_ASSERT (STRUCT_OFFSET_OF (cnat_session_t, key) ==
STRUCT_OFFSET_OF (clib_bihash_kv_40_48_t, key),
"key overlaps");
STATIC_ASSERT (STRUCT_OFFSET_OF (cnat_session_t, value) ==
STRUCT_OFFSET_OF (clib_bihash_kv_40_48_t, value),
"value overlaps");
STATIC_ASSERT (sizeof (cnat_session_t) == sizeof (clib_bihash_kv_40_48_t),
"session kvp");
/**
* The DB of sessions
*/
extern clib_bihash_40_48_t cnat_session_db;
/**
* Callback function invoked during a walk of all translations
*/
typedef walk_rc_t (*cnat_session_walk_cb_t) (const cnat_session_t *
session, void *ctx);
/**
* Walk/visit each of the cnat session
*/
extern void cnat_session_walk (cnat_session_walk_cb_t cb, void *ctx);
/**
* Scan the session DB for expired sessions
*/
extern u64 cnat_session_scan (vlib_main_t * vm, f64 start_time, int i);
/**
* Purge all the sessions
*/
extern int cnat_session_purge (void);
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/
#endif

View File

@ -0,0 +1,252 @@
/*
* Copyright (c) 2020 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <vnet/ip/ip.h>
#include <cnat/cnat_snat.h>
static void
cnat_compute_prefix_lengths_in_search_order (cnat_snat_pfx_table_t *
table, ip_address_family_t af)
{
int i;
vec_reset_length (table->meta[af].prefix_lengths_in_search_order);
/* Note: bitmap reversed so this is in fact a longest prefix match */
/* *INDENT-OFF* */
clib_bitmap_foreach (i, table->meta[af].non_empty_dst_address_length_bitmap,
({
int dst_address_length = 128 - i;
vec_add1 (table->meta[af].prefix_lengths_in_search_order, dst_address_length);
}));
/* *INDENT-ON* */
}
int
cnat_add_snat_prefix (ip_prefix_t * pfx)
{
/* All packets destined to this prefix won't be source-NAT-ed */
cnat_snat_pfx_table_t *table = &cnat_main.snat_pfx_table;
clib_bihash_kv_24_8_t kv;
ip6_address_t *mask;
u64 af = ip_prefix_version (pfx);;
mask = &table->ip_masks[pfx->len];
if (AF_IP4 == af)
{
kv.key[0] = (u64) ip_prefix_v4 (pfx).as_u32 & mask->as_u64[0];
kv.key[1] = 0;
}
else
{
kv.key[0] = ip_prefix_v6 (pfx).as_u64[0] & mask->as_u64[0];
kv.key[1] = ip_prefix_v6 (pfx).as_u64[1] & mask->as_u64[1];
}
kv.key[2] = ((u64) af << 32) | pfx->len;
clib_bihash_add_del_24_8 (&table->ip_hash, &kv, 1 /* is_add */ );
table->meta[af].dst_address_length_refcounts[pfx->len]++;
table->meta[af].non_empty_dst_address_length_bitmap =
clib_bitmap_set (table->meta[af].non_empty_dst_address_length_bitmap,
128 - pfx->len, 1);
cnat_compute_prefix_lengths_in_search_order (table, af);
return 0;
}
int
cnat_del_snat_prefix (ip_prefix_t * pfx)
{
cnat_snat_pfx_table_t *table = &cnat_main.snat_pfx_table;
clib_bihash_kv_24_8_t kv, val;
ip6_address_t *mask;
u64 af = ip_prefix_version (pfx);;
mask = &table->ip_masks[pfx->len];
if (AF_IP4 == af)
{
kv.key[0] = (u64) ip_prefix_v4 (pfx).as_u32 & mask->as_u64[0];
kv.key[1] = 0;
}
else
{
kv.key[0] = ip_prefix_v6 (pfx).as_u64[0] & mask->as_u64[0];
kv.key[1] = ip_prefix_v6 (pfx).as_u64[1] & mask->as_u64[1];
}
kv.key[2] = ((u64) af << 32) | pfx->len;
if (clib_bihash_search_24_8 (&table->ip_hash, &kv, &val))
{
return 1;
}
clib_bihash_add_del_24_8 (&table->ip_hash, &kv, 0 /* is_add */ );
/* refcount accounting */
ASSERT (table->meta[af].dst_address_length_refcounts[pfx->len] > 0);
if (--table->meta[af].dst_address_length_refcounts[pfx->len] == 0)
{
table->meta[af].non_empty_dst_address_length_bitmap =
clib_bitmap_set (table->meta[af].non_empty_dst_address_length_bitmap,
128 - pfx->len, 0);
cnat_compute_prefix_lengths_in_search_order (table, af);
}
return 0;
}
u8 *
format_cnat_snat_prefix (u8 * s, va_list * args)
{
clib_bihash_kv_24_8_t *kv = va_arg (*args, clib_bihash_kv_24_8_t *);
CLIB_UNUSED (int verbose) = va_arg (*args, int);
u32 af = kv->key[2] >> 32;
u32 len = kv->key[2] & 0xffffffff;
if (AF_IP4 == af)
s = format (s, "%U/%d", format_ip4_address, &kv->key[0], len);
else
s = format (s, "%U/%d", format_ip6_address, &kv->key[0], len);
return (s);
}
static clib_error_t *
cnat_set_snat (vlib_main_t * vm,
unformat_input_t * input, vlib_cli_command_t * cmd)
{
ip_address_t addr;
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
{
if (unformat (input, "%U", unformat_ip_address, &addr))
{
if (ip_addr_version (&addr) == AF_IP4)
clib_memcpy (&cnat_main.snat_ip4, &ip_addr_v4 (&addr),
sizeof (ip4_address_t));
else
clib_memcpy (&cnat_main.snat_ip6, &ip_addr_v6 (&addr),
sizeof (ip6_address_t));
}
else
return (clib_error_return (0, "unknown input '%U'",
format_unformat_error, input));
}
return (NULL);
}
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (cnat_set_snat_command, static) =
{
.path = "cnat snat with",
.short_help = "cnat snat with [ip]",
.function = cnat_set_snat,
};
/* *INDENT-ON* */
static clib_error_t *
cnat_snat_exclude (vlib_main_t * vm,
unformat_input_t * input, vlib_cli_command_t * cmd)
{
ip_prefix_t pfx;
u8 is_add = 1;
int rv;
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
{
if (unformat (input, "%U", unformat_ip_prefix, &pfx))
;
else if (unformat (input, "del"))
is_add = 0;
else
return (clib_error_return (0, "unknown input '%U'",
format_unformat_error, input));
}
if (is_add)
rv = cnat_add_snat_prefix (&pfx);
else
rv = cnat_del_snat_prefix (&pfx);
if (rv)
{
return (clib_error_return (0, "error %d", rv, input));
}
return (NULL);
}
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (cnat_snat_exclude_command, static) =
{
.path = "cnat snat exclude",
.short_help = "cnat snat exclude [ip]",
.function = cnat_snat_exclude,
};
/* *INDENT-ON* */
static clib_error_t *
cnat_show_snat (vlib_main_t * vm,
unformat_input_t * input, vlib_cli_command_t * cmd)
{
cnat_snat_pfx_table_t *table = &cnat_main.snat_pfx_table;
vlib_cli_output (vm, "Source NAT\nip4: %U\nip6: %U\n",
format_ip4_address, &cnat_main.snat_ip4,
format_ip6_address, &cnat_main.snat_ip6);
vlib_cli_output (vm, "Prefixes:\n%U\n",
format_bihash_24_8, &table->ip_hash, 1);
return (NULL);
}
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (cnat_show_snat_command, static) =
{
.path = "show cnat snat",
.short_help = "show cnat snat",
.function = cnat_show_snat,
};
/* *INDENT-ON* */
static clib_error_t *
cnat_snat_init (vlib_main_t * vm)
{
cnat_snat_pfx_table_t *table = &cnat_main.snat_pfx_table;
cnat_main_t *cm = &cnat_main;
int i;
for (i = 0; i < ARRAY_LEN (table->ip_masks); i++)
{
u32 j, i0, i1;
i0 = i / 32;
i1 = i % 32;
for (j = 0; j < i0; j++)
table->ip_masks[i].as_u32[j] = ~0;
if (i1)
table->ip_masks[i].as_u32[i0] =
clib_host_to_net_u32 (pow2_mask (i1) << (32 - i1));
}
clib_bihash_init_24_8 (&table->ip_hash, "snat prefixes",
cm->snat_hash_buckets, cm->snat_hash_memory);
clib_bihash_set_kvp_format_fn_24_8 (&table->ip_hash,
format_cnat_snat_prefix);
return (NULL);
}
VLIB_INIT_FUNCTION (cnat_snat_init);
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/

View File

@ -0,0 +1,74 @@
/*
* Copyright (c) 2020 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __CNAT_SNAT_H__
#define __CNAT_SNAT_H__
#include <cnat/cnat_types.h>
always_inline int
cnat_search_snat_prefix (ip46_address_t * addr, ip_address_family_t af)
{
/* Returns 0 if addr matches any of the listed prefixes */
cnat_snat_pfx_table_t *table = &cnat_main.snat_pfx_table;
clib_bihash_kv_24_8_t kv, val;
int i, n_p, rv;
n_p = vec_len (table->meta[af].prefix_lengths_in_search_order);
if (AF_IP4 == af)
{
kv.key[0] = addr->ip4.as_u32;
kv.key[1] = 0;
}
else
{
kv.key[0] = addr->as_u64[0];
kv.key[1] = addr->as_u64[1];
}
/*
* start search from a mask length same length or shorter.
* we don't want matches longer than the mask passed
*/
i = 0;
for (; i < n_p; i++)
{
int dst_address_length =
table->meta[af].prefix_lengths_in_search_order[i];
ip6_address_t *mask = &table->ip_masks[dst_address_length];
ASSERT (dst_address_length >= 0 && dst_address_length <= 128);
/* As lengths are decreasing, masks are increasingly specific. */
kv.key[0] &= mask->as_u64[0];
kv.key[1] &= mask->as_u64[1];
kv.key[2] = ((u64) af << 32) | dst_address_length;
rv = clib_bihash_search_inline_2_24_8 (&table->ip_hash, &kv, &val);
if (rv == 0)
return 0;
}
return -1;
}
extern int cnat_add_snat_prefix (ip_prefix_t * pfx);
extern int cnat_del_snat_prefix (ip_prefix_t * pfx);
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,204 @@
/*
* Copyright (c) 2020 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __CNAT_TRANSLATION_H__
#define __CNAT_TRANSLATION_H__
#include <cnat/cnat_types.h>
#include <vnet/ip/ip_types.h>
#include <vppinfra/bihash_8_8.h>
/**
* Counters for each translation
*/
extern vlib_combined_counter_main_t cnat_translation_counters;
/**
* Data used to track an EP in the FIB
*/
typedef struct cnat_ep_trk_t_
{
/**
* The EP being tracked
*/
cnat_endpoint_t ct_ep[VLIB_N_DIR];
/**
* The FIB entry for the EP
*/
fib_node_index_t ct_fei;
/**
* The sibling on the entry's child list
*/
u32 ct_sibling;
/**
* The forwarding contributed by the entry
*/
dpo_id_t ct_dpo;
} cnat_ep_trk_t;
typedef enum cnat_translation_flag_t_
{
CNAT_TRANSLATION_FLAG_ALLOCATE_PORT = (1 << 0),
} cnat_translation_flag_t;
/**
* A Translation represents the translation of a VEP to one of a set
* of real server addresses
*/
typedef struct cnat_translation_t_
{
/**
* Linkage into the FIB graph
*/
fib_node_t ct_node;
/**
* The LB used to forward to the backends
*/
dpo_id_t ct_lb;
/**
* The Virtual end point
*/
cnat_endpoint_t ct_vip;
/**
* The vector of tracked back-ends
*/
cnat_ep_trk_t *ct_paths;
/**
* The ip protocol for the translation
*/
ip_protocol_t ct_proto;
/**
* The client object this translation belongs on
*/
index_t ct_cci;
/**
* Own index (if copied for trace)
*/
index_t index;
/**
* Translation flags
*/
u8 flags;
} cnat_translation_t;
extern cnat_translation_t *cnat_translation_pool;
extern u8 *format_cnat_translation (u8 * s, va_list * args);
/**
* create or update a translation
*
* @param vip The Virtual Endpoint
* @param ip_proto The ip protocol to translate
* @param backends the backends to choose from
*
* @return the ID of the translation. used to delete and gather stats
*/
extern u32 cnat_translation_update (const cnat_endpoint_t * vip,
ip_protocol_t ip_proto,
const cnat_endpoint_tuple_t *
backends, u8 flags);
/**
* Add a translation to the bihash
*
* @param cci the ID of the parent client
* @param port the translation port
* @param proto the translation proto
* @param cti the translation index to be used as value
*/
extern void cnat_add_translation_to_db (index_t cci, u16 port,
ip_protocol_t proto, index_t cti);
/**
* Remove a translation from the bihash
*
* @param cci the ID of the parent client
* @param port the translation port
* @param proto the translation proto
*/
extern void cnat_remove_translation_from_db (index_t cci, u16 port,
ip_protocol_t proto);
/**
* Delete a translation
*
* @param id the ID as returned from the create
*/
extern int cnat_translation_delete (u32 id);
/**
* Callback function invoked during a walk of all translations
*/
typedef walk_rc_t (*cnat_translation_walk_cb_t) (index_t index, void *ctx);
/**
* Walk/visit each of the translations
*/
extern void cnat_translation_walk (cnat_translation_walk_cb_t cb, void *ctx);
/**
* Purge all the trahslations
*/
extern int cnat_translation_purge (void);
/*
* Data plane functions
*/
extern clib_bihash_8_8_t cnat_translation_db;
static_always_inline cnat_translation_t *
cnat_translation_get (index_t cti)
{
return (pool_elt_at_index (cnat_translation_pool, cti));
}
static_always_inline cnat_translation_t *
cnat_find_translation (index_t cti, u16 port, ip_protocol_t proto)
{
clib_bihash_kv_8_8_t bkey, bvalue;
u64 key;
int rv;
key = (proto << 16) | port;
key = key << 32 | (u32) cti;
bkey.key = key;
rv = clib_bihash_search_inline_2_8_8 (&cnat_translation_db, &bkey, &bvalue);
if (!rv)
return (pool_elt_at_index (cnat_translation_pool, bvalue.value));
return (NULL);
}
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/
#endif

View File

@ -0,0 +1,149 @@
/*
* Copyright (c) 2020 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <cnat/cnat_types.h>
cnat_main_t cnat_main;
fib_source_t cnat_fib_source;
cnat_timestamp_t *cnat_timestamps;
throttle_t cnat_throttle;
char *cnat_error_strings[] = {
#define cnat_error(n,s) s,
#include <cnat/cnat_error.def>
#undef cnat_error
};
uword
unformat_cnat_ep (unformat_input_t * input, va_list * args)
{
cnat_endpoint_t *a = va_arg (*args, cnat_endpoint_t *);
int port = 0;
clib_memset (a, 0, sizeof (*a));
if (unformat (input, "%U %d", unformat_ip_address, &a->ce_ip, &port))
;
else if (unformat_user (input, unformat_ip_address, &a->ce_ip))
;
else if (unformat (input, "%d", &port))
;
else
return 0;
a->ce_port = (u16) port;
return 1;
}
uword
unformat_cnat_ep_tuple (unformat_input_t * input, va_list * args)
{
cnat_endpoint_tuple_t *a = va_arg (*args, cnat_endpoint_tuple_t *);
if (unformat (input, "%U->%U", unformat_cnat_ep, &a->src_ep,
unformat_cnat_ep, &a->dst_ep))
;
else if (unformat (input, "->%U", unformat_cnat_ep, &a->dst_ep))
;
else if (unformat (input, "%U->", unformat_cnat_ep, &a->src_ep))
;
else
return 0;
return 1;
}
u8 *
format_cnat_endpoint (u8 * s, va_list * args)
{
cnat_endpoint_t *cep = va_arg (*args, cnat_endpoint_t *);
s = format (s, "%U;%d", format_ip_address, &cep->ce_ip, cep->ce_port);
return (s);
}
static clib_error_t *
cnat_types_init (vlib_main_t * vm)
{
vlib_thread_main_t *tm = &vlib_thread_main;
u32 n_vlib_mains = tm->n_vlib_mains;
cnat_fib_source = fib_source_allocate ("cnat",
CNAT_FIB_SOURCE_PRIORITY,
FIB_SOURCE_BH_SIMPLE);
clib_rwlock_init (&cnat_main.ts_lock);
clib_spinlock_init (&cnat_main.src_ports_lock);
clib_bitmap_validate (cnat_main.src_ports, UINT16_MAX);
throttle_init (&cnat_throttle, n_vlib_mains, 1e-3);
return (NULL);
}
static clib_error_t *
cnat_config (vlib_main_t * vm, unformat_input_t * input)
{
cnat_main_t *cm = &cnat_main;
cm->session_hash_memory = CNAT_DEFAULT_SESSION_MEMORY;
cm->session_hash_buckets = CNAT_DEFAULT_SESSION_BUCKETS;
cm->translation_hash_memory = CNAT_DEFAULT_TRANSLATION_MEMORY;
cm->translation_hash_buckets = CNAT_DEFAULT_TRANSLATION_BUCKETS;
cm->snat_hash_memory = CNAT_DEFAULT_SNAT_MEMORY;
cm->snat_hash_buckets = CNAT_DEFAULT_SNAT_BUCKETS;
cm->scanner_timeout = CNAT_DEFAULT_SCANNER_TIMEOUT;
cm->session_max_age = CNAT_DEFAULT_SESSION_MAX_AGE;
cm->tcp_max_age = CNAT_DEFAULT_TCP_MAX_AGE;
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
{
if (unformat
(input, "session-db-buckets %u", &cm->session_hash_buckets))
;
else if (unformat (input, "session-db-memory %U",
unformat_memory_size, &cm->session_hash_memory))
;
else if (unformat (input, "translation-db-buckets %u",
&cm->translation_hash_buckets))
;
else if (unformat (input, "translation-db-memory %U",
unformat_memory_size, &cm->translation_hash_memory))
;
else if (unformat (input, "snat-db-buckets %u", &cm->snat_hash_buckets))
;
else if (unformat (input, "snat-db-memory %U",
unformat_memory_size, &cm->snat_hash_memory))
;
else if (unformat (input, "session-cleanup-timeout %f",
&cm->scanner_timeout))
;
else if (unformat (input, "session-max-age %u", &cm->session_max_age))
;
else if (unformat (input, "tcp-max-age %u", &cm->tcp_max_age))
;
else
return clib_error_return (0, "unknown input '%U'",
format_unformat_error, input);
}
return 0;
}
VLIB_EARLY_CONFIG_FUNCTION (cnat_config, "cnat");
VLIB_INIT_FUNCTION (cnat_types_init);
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/

View File

@ -0,0 +1,281 @@
/*
* Copyright (c) 2020 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __CNAT_TYPES_H__
#define __CNAT_TYPES_H__
#include <vnet/fib/fib_node.h>
#include <vnet/fib/fib_source.h>
#include <vnet/ip/ip_types.h>
#include <vnet/ip/ip.h>
/* only in the default table for v4 and v6 */
#define CNAT_FIB_TABLE 0
/* default lifetime of NAT sessions (seconds) */
#define CNAT_DEFAULT_SESSION_MAX_AGE 30
/* lifetime of TCP conn NAT sessions after SYNACK (seconds) */
#define CNAT_DEFAULT_TCP_MAX_AGE 3600
/* lifetime of TCP conn NAT sessions after RST/FIN (seconds) */
#define CNAT_DEFAULT_TCP_RST_TIMEOUT 5
#define CNAT_DEFAULT_SCANNER_TIMEOUT (1.0)
#define CNAT_DEFAULT_SESSION_BUCKETS 1024
#define CNAT_DEFAULT_TRANSLATION_BUCKETS 1024
#define CNAT_DEFAULT_SNAT_BUCKETS 1024
#define CNAT_DEFAULT_SESSION_MEMORY (1 << 20)
#define CNAT_DEFAULT_TRANSLATION_MEMORY (256 << 10)
#define CNAT_DEFAULT_SNAT_MEMORY (64 << 20)
/* This should be strictly lower than FIB_SOURCE_INTERFACE
* from fib_source.h */
#define CNAT_FIB_SOURCE_PRIORITY 0x02
/* Initial refcnt for timestamps (2 : session & rsession) */
#define CNAT_TIMESTAMP_INIT_REFCNT 2
#define MIN_SRC_PORT ((u16) 0xC000)
typedef struct cnat_endpoint_t_
{
ip_address_t ce_ip;
u16 ce_port;
} cnat_endpoint_t;
typedef struct cnat_endpoint_tuple_t_
{
cnat_endpoint_t dst_ep;
cnat_endpoint_t src_ep;
} cnat_endpoint_tuple_t;
typedef struct
{
u32 dst_address_length_refcounts[129];
u16 *prefix_lengths_in_search_order;
uword *non_empty_dst_address_length_bitmap;
} cnat_snat_pfx_table_meta_t;
typedef struct
{
/* Stores (ip family, prefix & mask) */
clib_bihash_24_8_t ip_hash;
/* family dependant cache */
cnat_snat_pfx_table_meta_t meta[2];
/* Precomputed ip masks (ip4 & ip6) */
ip6_address_t ip_masks[129];
} cnat_snat_pfx_table_t;
typedef struct cnat_main_
{
/* Memory size of the session bihash */
uword session_hash_memory;
/* Number of buckets of the session bihash */
u32 session_hash_buckets;
/* Memory size of the translation bihash */
uword translation_hash_memory;
/* Number of buckets of the translation bihash */
u32 translation_hash_buckets;
/* Memory size of the source NAT prefix bihash */
uword snat_hash_memory;
/* Number of buckets of the source NAT prefix bihash */
u32 snat_hash_buckets;
/* Timeout after which to clear sessions (in seconds) */
u32 session_max_age;
/* Timeout after which to clear an established TCP
* session (in seconds) */
u32 tcp_max_age;
/* delay in seconds between two scans of session/clients tables */
f64 scanner_timeout;
/* Lock for the timestamp pool */
clib_rwlock_t ts_lock;
/* Source ports bitmap for snat */
clib_bitmap_t *src_ports;
/* Lock for src_ports access */
clib_spinlock_t src_ports_lock;
/* Ip4 Address to use for source NATing */
ip4_address_t snat_ip4;
/* Ip6 Address to use for source NATing */
ip6_address_t snat_ip6;
/* Longest prefix Match table for source NATing */
cnat_snat_pfx_table_t snat_pfx_table;
} cnat_main_t;
typedef struct cnat_timestamp_t_
{
/* Last time said session was seen */
f64 last_seen;
/* expire after N seconds */
u16 lifetime;
/* Users refcount, initially 3 (session, rsession, dpo) */
u16 refcnt;
} cnat_timestamp_t;
typedef struct cnat_node_ctx_t_
{
f64 now;
u64 seed;
u32 thread_index;
ip_address_family_t af;
u8 do_trace;
} cnat_node_ctx_t;
extern u8 *format_cnat_endpoint (u8 * s, va_list * args);
extern uword unformat_cnat_ep_tuple (unformat_input_t * input,
va_list * args);
extern uword unformat_cnat_ep (unformat_input_t * input, va_list * args);
extern cnat_timestamp_t *cnat_timestamps;
extern fib_source_t cnat_fib_source;
extern cnat_main_t cnat_main;
extern throttle_t cnat_throttle;
extern char *cnat_error_strings[];
typedef enum
{
#define cnat_error(n,s) CNAT_ERROR_##n,
#include <cnat/cnat_error.def>
#undef cnat_error
CNAT_N_ERROR,
} cnat_error_t;
/*
Dataplane functions
*/
always_inline u32
cnat_timestamp_new (f64 t)
{
u32 index;
cnat_timestamp_t *ts;
clib_rwlock_writer_lock (&cnat_main.ts_lock);
pool_get (cnat_timestamps, ts);
ts->last_seen = t;
ts->lifetime = cnat_main.session_max_age;
ts->refcnt = CNAT_TIMESTAMP_INIT_REFCNT;
index = ts - cnat_timestamps;
clib_rwlock_writer_unlock (&cnat_main.ts_lock);
return index;
}
always_inline void
cnat_timestamp_inc_refcnt (u32 index)
{
clib_rwlock_reader_lock (&cnat_main.ts_lock);
cnat_timestamp_t *ts = pool_elt_at_index (cnat_timestamps, index);
ts->refcnt++;
clib_rwlock_reader_unlock (&cnat_main.ts_lock);
}
always_inline void
cnat_timestamp_update (u32 index, f64 t)
{
return;
clib_rwlock_reader_lock (&cnat_main.ts_lock);
cnat_timestamp_t *ts = pool_elt_at_index (cnat_timestamps, index);
ts->last_seen = t;
clib_rwlock_reader_unlock (&cnat_main.ts_lock);
}
always_inline void
cnat_timestamp_set_lifetime (u32 index, u16 lifetime)
{
clib_rwlock_reader_lock (&cnat_main.ts_lock);
cnat_timestamp_t *ts = pool_elt_at_index (cnat_timestamps, index);
ts->lifetime = lifetime;
clib_rwlock_reader_unlock (&cnat_main.ts_lock);
}
always_inline f64
cnat_timestamp_exp (u32 index)
{
f64 t;
if (INDEX_INVALID == index)
return -1;
clib_rwlock_reader_lock (&cnat_main.ts_lock);
cnat_timestamp_t *ts = pool_elt_at_index (cnat_timestamps, index);
t = ts->last_seen + (f64) ts->lifetime;
clib_rwlock_reader_unlock (&cnat_main.ts_lock);
return t;
}
always_inline void
cnat_timestamp_free (u32 index)
{
if (INDEX_INVALID == index)
return;
clib_rwlock_writer_lock (&cnat_main.ts_lock);
cnat_timestamp_t *ts = pool_elt_at_index (cnat_timestamps, index);
ts->refcnt--;
if (0 == ts->refcnt)
pool_put (cnat_timestamps, ts);
clib_rwlock_writer_unlock (&cnat_main.ts_lock);
}
always_inline void
cnat_free_port (u16 port)
{
cnat_main_t *cm = &cnat_main;
clib_spinlock_lock (&cm->src_ports_lock);
clib_bitmap_set_no_check (cm->src_ports, port, 0);
clib_spinlock_unlock (&cm->src_ports_lock);
}
always_inline int
cnat_allocate_port (cnat_main_t * cm, u16 * port)
{
*port = clib_net_to_host_u16 (*port);
if (*port == 0)
*port = MIN_SRC_PORT;
clib_spinlock_lock (&cm->src_ports_lock);
if (clib_bitmap_get_no_check (cm->src_ports, *port))
{
*port = clib_bitmap_next_clear (cm->src_ports, *port);
if (PREDICT_FALSE (*port >= UINT16_MAX))
*port = clib_bitmap_next_clear (cm->src_ports, MIN_SRC_PORT);
if (PREDICT_FALSE (*port >= UINT16_MAX))
return -1;
}
clib_bitmap_set_no_check (cm->src_ports, *port, 1);
*port = clib_host_to_net_u16 (*port);
clib_spinlock_unlock (&cm->src_ports_lock);
return 0;
}
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/
#endif

File diff suppressed because it is too large Load Diff

View File

@ -261,6 +261,30 @@ ip_address_from_46 (const ip46_address_t * nh,
ip_addr_version (ip) = ip_address_family_from_fib_proto (fproto);
}
/**
* convert from a IP address to a FIB prefix
*/
void
ip_address_to_fib_prefix (const ip_address_t * addr, fib_prefix_t * prefix)
{
if (addr->version == AF_IP4)
{
prefix->fp_len = 32;
prefix->fp_proto = FIB_PROTOCOL_IP4;
clib_memset (&prefix->fp_addr.pad, 0, sizeof (prefix->fp_addr.pad));
memcpy (&prefix->fp_addr.ip4, &addr->ip.ip4,
sizeof (prefix->fp_addr.ip4));
}
else
{
prefix->fp_len = 128;
prefix->fp_proto = FIB_PROTOCOL_IP6;
memcpy (&prefix->fp_addr.ip6, &addr->ip.ip6,
sizeof (prefix->fp_addr.ip6));
}
prefix->___fp___pad = 0;
}
static void
ip_prefix_normalize_ip4 (ip4_address_t * ip4, u8 preflen)
{
@ -364,6 +388,17 @@ ip_prefix_cmp (ip_prefix_t * p1, ip_prefix_t * p2)
return cmp;
}
/**
* convert from a LISP to a FIB prefix
*/
void
ip_prefix_to_fib_prefix (const ip_prefix_t * ip_prefix,
fib_prefix_t * fib_prefix)
{
ip_address_to_fib_prefix (&ip_prefix->addr, fib_prefix);
fib_prefix->fp_len = ip_prefix->len;
}
static bool
ip4_prefix_validate (const ip_prefix_t * ip)
{

View File

@ -123,41 +123,6 @@ ip_interface_get_first_ip_address (lisp_cp_main_t * lcm, u32 sw_if_index,
return 1;
}
/**
* convert from a LISP address to a FIB prefix
*/
void
ip_address_to_fib_prefix (const ip_address_t * addr, fib_prefix_t * prefix)
{
if (addr->version == AF_IP4)
{
prefix->fp_len = 32;
prefix->fp_proto = FIB_PROTOCOL_IP4;
clib_memset (&prefix->fp_addr.pad, 0, sizeof (prefix->fp_addr.pad));
memcpy (&prefix->fp_addr.ip4, &addr->ip.ip4,
sizeof (prefix->fp_addr.ip4));
}
else
{
prefix->fp_len = 128;
prefix->fp_proto = FIB_PROTOCOL_IP6;
memcpy (&prefix->fp_addr.ip6, &addr->ip.ip6,
sizeof (prefix->fp_addr.ip6));
}
prefix->___fp___pad = 0;
}
/**
* convert from a LISP to a FIB prefix
*/
void
ip_prefix_to_fib_prefix (const ip_prefix_t * ip_prefix,
fib_prefix_t * fib_prefix)
{
ip_address_to_fib_prefix (&ip_prefix->addr, fib_prefix);
fib_prefix->fp_len = ip_prefix->len;
}
/**
* Find the sw_if_index of the interface that would be used to egress towards
* dst.