cnat: Destination based NAT
Type: feature Signed-off-by: Neale Ranns <nranns@cisco.com> Change-Id: I64a99a4fbc674212944247793fd5c1fb701408cb
This commit is contained in:

committed by
Andrew Yourtchenko

parent
133c91c1c0
commit
29f3c7d2ec
@ -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>
|
||||
|
28
src/plugins/cnat/CMakeLists.txt
Normal file
28
src/plugins/cnat/CMakeLists.txt
Normal 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
|
||||
)
|
17
src/plugins/cnat/FEATURE.yaml
Normal file
17
src/plugins/cnat/FEATURE.yaml
Normal 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]
|
112
src/plugins/cnat/bihash_40_48.h
Normal file
112
src/plugins/cnat/bihash_40_48.h
Normal 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
136
src/plugins/cnat/cnat.api
Normal 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
81
src/plugins/cnat/cnat.rst
Normal 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
317
src/plugins/cnat/cnat_api.c
Normal file
File diff suppressed because it is too large
Load Diff
407
src/plugins/cnat/cnat_client.c
Normal file
407
src/plugins/cnat/cnat_client.c
Normal file
File diff suppressed because it is too large
Load Diff
226
src/plugins/cnat/cnat_client.h
Normal file
226
src/plugins/cnat/cnat_client.h
Normal 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
|
19
src/plugins/cnat/cnat_error.def
Normal file
19
src/plugins/cnat/cnat_error.def
Normal 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")
|
||||
|
||||
|
535
src/plugins/cnat/cnat_node.h
Normal file
535
src/plugins/cnat/cnat_node.h
Normal file
File diff suppressed because it is too large
Load Diff
237
src/plugins/cnat/cnat_node_snat.c
Normal file
237
src/plugins/cnat/cnat_node_snat.c
Normal 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:
|
||||
*/
|
308
src/plugins/cnat/cnat_node_vip.c
Normal file
308
src/plugins/cnat/cnat_node_vip.c
Normal file
File diff suppressed because it is too large
Load Diff
113
src/plugins/cnat/cnat_scanner.c
Normal file
113
src/plugins/cnat/cnat_scanner.c
Normal 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:
|
||||
*/
|
269
src/plugins/cnat/cnat_session.c
Normal file
269
src/plugins/cnat/cnat_session.c
Normal 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:
|
||||
*/
|
157
src/plugins/cnat/cnat_session.h
Normal file
157
src/plugins/cnat/cnat_session.h
Normal 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
|
252
src/plugins/cnat/cnat_snat.c
Normal file
252
src/plugins/cnat/cnat_snat.c
Normal 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:
|
||||
*/
|
74
src/plugins/cnat/cnat_snat.h
Normal file
74
src/plugins/cnat/cnat_snat.h
Normal 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
|
432
src/plugins/cnat/cnat_translation.c
Normal file
432
src/plugins/cnat/cnat_translation.c
Normal file
File diff suppressed because it is too large
Load Diff
204
src/plugins/cnat/cnat_translation.h
Normal file
204
src/plugins/cnat/cnat_translation.h
Normal 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
|
149
src/plugins/cnat/cnat_types.c
Normal file
149
src/plugins/cnat/cnat_types.c
Normal 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:
|
||||
*/
|
281
src/plugins/cnat/cnat_types.h
Normal file
281
src/plugins/cnat/cnat_types.h
Normal 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
|
596
src/plugins/cnat/test/test_cnat.py
Normal file
596
src/plugins/cnat/test/test_cnat.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -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)
|
||||
{
|
||||
|
@ -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.
|
||||
|
Reference in New Issue
Block a user