FD.io VPP  v21.01
Vector Packet Processing
cnat_snat.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include <vnet/ip/ip.h>
17 #include <cnat/cnat_snat.h>
18 #include <cnat/cnat_translation.h>
19 
20 static void
22  table, ip_address_family_t af)
23 {
24  int i;
26  /* Note: bitmap reversed so this is in fact a longest prefix match */
27  /* *INDENT-OFF* */
29  {
30  int dst_address_length = 128 - i;
31  vec_add1 (table->meta[af].prefix_lengths_in_search_order, dst_address_length);
32  }
33  /* *INDENT-ON* */
34 }
35 
36 int
38 {
39  /* All packets destined to this prefix won't be source-NAT-ed */
42  ip6_address_t *mask;
43  u64 af = ip_prefix_version (pfx);;
44 
45  mask = &table->ip_masks[pfx->len];
46  if (AF_IP4 == af)
47  {
48  kv.key[0] = (u64) ip_prefix_v4 (pfx).as_u32 & mask->as_u64[0];
49  kv.key[1] = 0;
50  }
51  else
52  {
53  kv.key[0] = ip_prefix_v6 (pfx).as_u64[0] & mask->as_u64[0];
54  kv.key[1] = ip_prefix_v6 (pfx).as_u64[1] & mask->as_u64[1];
55  }
56  kv.key[2] = ((u64) af << 32) | pfx->len;
57  clib_bihash_add_del_24_8 (&table->ip_hash, &kv, 1 /* is_add */ );
58 
59  table->meta[af].dst_address_length_refcounts[pfx->len]++;
62  128 - pfx->len, 1);
64  return 0;
65 }
66 
67 int
69 {
71  clib_bihash_kv_24_8_t kv, val;
72  ip6_address_t *mask;
73  u64 af = ip_prefix_version (pfx);;
74 
75  mask = &table->ip_masks[pfx->len];
76  if (AF_IP4 == af)
77  {
78  kv.key[0] = (u64) ip_prefix_v4 (pfx).as_u32 & mask->as_u64[0];
79  kv.key[1] = 0;
80  }
81  else
82  {
83  kv.key[0] = ip_prefix_v6 (pfx).as_u64[0] & mask->as_u64[0];
84  kv.key[1] = ip_prefix_v6 (pfx).as_u64[1] & mask->as_u64[1];
85  }
86  kv.key[2] = ((u64) af << 32) | pfx->len;
87 
88  if (clib_bihash_search_24_8 (&table->ip_hash, &kv, &val))
89  {
90  return 1;
91  }
92  clib_bihash_add_del_24_8 (&table->ip_hash, &kv, 0 /* is_add */ );
93  /* refcount accounting */
94  ASSERT (table->meta[af].dst_address_length_refcounts[pfx->len] > 0);
95  if (--table->meta[af].dst_address_length_refcounts[pfx->len] == 0)
96  {
99  128 - pfx->len, 0);
101  }
102  return 0;
103 }
104 
105 int
107 {
108  /* Returns 0 if addr matches any of the listed prefixes */
110  clib_bihash_kv_24_8_t kv, val;
111  int i, n_p, rv;
112  n_p = vec_len (table->meta[af].prefix_lengths_in_search_order);
113  if (AF_IP4 == af)
114  {
115  kv.key[0] = addr->ip4.as_u32;
116  kv.key[1] = 0;
117  }
118  else
119  {
120  kv.key[0] = addr->as_u64[0];
121  kv.key[1] = addr->as_u64[1];
122  }
123 
124  /*
125  * start search from a mask length same length or shorter.
126  * we don't want matches longer than the mask passed
127  */
128  i = 0;
129  for (; i < n_p; i++)
130  {
131  int dst_address_length =
133  ip6_address_t *mask = &table->ip_masks[dst_address_length];
134 
135  ASSERT (dst_address_length >= 0 && dst_address_length <= 128);
136  /* As lengths are decreasing, masks are increasingly specific. */
137  kv.key[0] &= mask->as_u64[0];
138  kv.key[1] &= mask->as_u64[1];
139  kv.key[2] = ((u64) af << 32) | dst_address_length;
140  rv = clib_bihash_search_inline_2_24_8 (&table->ip_hash, &kv, &val);
141  if (rv == 0)
142  return 0;
143  }
144  return -1;
145 }
146 
147 u8 *
148 format_cnat_snat_prefix (u8 * s, va_list * args)
149 {
150  clib_bihash_kv_24_8_t *kv = va_arg (*args, clib_bihash_kv_24_8_t *);
151  CLIB_UNUSED (int verbose) = va_arg (*args, int);
152  u32 af = kv->key[2] >> 32;
153  u32 len = kv->key[2] & 0xffffffff;
154  if (AF_IP4 == af)
155  s = format (s, "%U/%d", format_ip4_address, &kv->key[0], len);
156  else
157  s = format (s, "%U/%d", format_ip6_address, &kv->key[0], len);
158  return (s);
159 }
160 
161 void
163 {
164  cnat_lazy_init ();
165 
167 
172 
179 }
180 
181 static clib_error_t *
183  unformat_input_t * input, vlib_cli_command_t * cmd)
184 {
185  unformat_input_t _line_input, *line_input = &_line_input;
186  vnet_main_t *vnm = vnet_get_main ();
187  ip4_address_t ip4 = { {0} };
188  ip6_address_t ip6 = { {0} };
189  clib_error_t *e = 0;
191 
192  cnat_lazy_init ();
193 
194  /* Get a line of input. */
195  if (!unformat_user (input, unformat_line_input, line_input))
196  return 0;
197 
198  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
199  {
200  if (unformat_user (line_input, unformat_ip4_address, &ip4))
201  ;
202  else if (unformat_user (line_input, unformat_ip6_address, &ip6))
203  ;
204  else if (unformat_user (line_input, unformat_vnet_sw_interface,
205  vnm, &sw_if_index))
206  ;
207  else
208  {
209  e = clib_error_return (0, "unknown input '%U'",
210  format_unformat_error, input);
211  goto done;
212  }
213  }
214 
215  cnat_set_snat (&ip4, &ip6, sw_if_index);
216 
217 done:
218  unformat_free (line_input);
219 
220  return (e);
221 }
222 
223 /* *INDENT-OFF* */
224 VLIB_CLI_COMMAND (cnat_set_snat_command, static) =
225 {
226  .path = "cnat snat with",
227  .short_help = "cnat snat with [<ip4-address>][<ip6-address>][sw_if_index]",
228  .function = cnat_set_snat_cli,
229 };
230 /* *INDENT-ON* */
231 
232 static clib_error_t *
234  unformat_input_t * input, vlib_cli_command_t * cmd)
235 {
236  ip_prefix_t pfx;
237  u8 is_add = 1;
238  int rv;
239 
241  {
242  if (unformat (input, "%U", unformat_ip_prefix, &pfx))
243  ;
244  else if (unformat (input, "del"))
245  is_add = 0;
246  else
247  return (clib_error_return (0, "unknown input '%U'",
248  format_unformat_error, input));
249  }
250 
251  if (is_add)
252  rv = cnat_add_snat_prefix (&pfx);
253  else
254  rv = cnat_del_snat_prefix (&pfx);
255 
256  if (rv)
257  {
258  return (clib_error_return (0, "error %d", rv, input));
259  }
260 
261  return (NULL);
262 }
263 
264 /* *INDENT-OFF* */
265 VLIB_CLI_COMMAND (cnat_snat_exclude_command, static) =
266 {
267  .path = "cnat snat exclude",
268  .short_help = "cnat snat exclude [ip]",
269  .function = cnat_snat_exclude,
270 };
271 /* *INDENT-ON* */
272 
273 static clib_error_t *
275  unformat_input_t * input, vlib_cli_command_t * cmd)
276 {
278  vlib_cli_output (vm, "Source NAT\nip4: %U\nip6: %U\n",
281  vlib_cli_output (vm, "Prefixes:\n%U\n",
282  format_bihash_24_8, &table->ip_hash, 1);
283  return (NULL);
284 }
285 
286 /* *INDENT-OFF* */
287 VLIB_CLI_COMMAND (cnat_show_snat_command, static) =
288 {
289  .path = "show cnat snat",
290  .short_help = "show cnat snat",
291  .function = cnat_show_snat,
292 };
293 /* *INDENT-ON* */
294 
295 static clib_error_t *
297 {
300  int i;
301  for (i = 0; i < ARRAY_LEN (table->ip_masks); i++)
302  {
303  u32 j, i0, i1;
304 
305  i0 = i / 32;
306  i1 = i % 32;
307 
308  for (j = 0; j < i0; j++)
309  table->ip_masks[i].as_u32[j] = ~0;
310 
311  if (i1)
312  table->ip_masks[i].as_u32[i0] =
313  clib_host_to_net_u32 (pow2_mask (i1) << (32 - i1));
314  }
315  clib_bihash_init_24_8 (&table->ip_hash, "snat prefixes",
317  clib_bihash_set_kvp_format_fn_24_8 (&table->ip_hash,
319 
320  return (NULL);
321 }
322 
324 
325 
326 /*
327  * fd.io coding-style-patch-verification: ON
328  *
329  * Local Variables:
330  * eval: (c-set-style "gnu")
331  * End:
332  */
#define CLIB_UNUSED(x)
Definition: clib.h:87
static void cnat_compute_prefix_lengths_in_search_order(cnat_snat_pfx_table_t *table, ip_address_family_t af)
Definition: cnat_snat.c:21
void ip_address_set(ip_address_t *dst, const void *src, u8 version)
Definition: ip_types.c:208
vnet_main_t * vnet_get_main(void)
Definition: misc.c:46
cnat_endpoint_t snat_ip6
Definition: cnat_types.h:134
unsigned long u64
Definition: types.h:89
#define clib_bitmap_foreach(i, ai)
Macro to iterate across set bits in a bitmap.
Definition: bitmap.h:361
#define vec_add1(V, E)
Add 1 element to end of vector (unspecified alignment).
Definition: vec.h:592
static clib_error_t * cnat_show_snat(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
Definition: cnat_snat.c:274
static uword * clib_bitmap_set(uword *ai, uword i, uword value)
Sets the ith bit of a bitmap to new_value Removes trailing zeros from the bitmap. ...
Definition: bitmap.h:167
uword unformat_user(unformat_input_t *input, unformat_function_t *func,...)
Definition: unformat.c:989
vlib_main_t * vm
Definition: in2out_ed.c:1578
unformat_function_t unformat_vnet_sw_interface
#define ip_prefix_v6(_a)
Definition: ip_types.h:126
int cnat_add_snat_prefix(ip_prefix_t *pfx)
Definition: cnat_snat.c:37
u16 mask
Definition: flow_types.api:52
#define ip_prefix_v4(_a)
Definition: ip_types.h:125
vhost_vring_addr_t addr
Definition: vhost_user.h:111
void cnat_set_snat(ip4_address_t *ip4, ip6_address_t *ip6, u32 sw_if_index)
Definition: cnat_snat.c:162
unsigned char u8
Definition: types.h:56
static clib_error_t * cnat_set_snat_cli(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
Definition: cnat_snat.c:182
#define vec_reset_length(v)
Reset vector length to zero NULL-pointer tolerant.
int cnat_del_snat_prefix(ip_prefix_t *pfx)
Definition: cnat_snat.c:68
format_function_t format_ip4_address
Definition: format.h:73
void cnat_translation_watch_addr(index_t cti, u64 opaque, cnat_endpoint_t *ep, cnat_addr_resol_type_t type)
Add an address resolution request.
unformat_function_t unformat_ip4_address
Definition: format.h:68
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:173
static uword pow2_mask(uword x)
Definition: clib.h:238
vl_api_ip6_address_t ip6
Definition: one.api:424
clib_bihash_24_8_t ip_hash
Definition: cnat_types.h:90
description fragment has unexpected format
Definition: map.api:433
#define clib_error_return(e, args...)
Definition: error.h:99
unsigned int u32
Definition: types.h:88
void cnat_translation_unwatch_addr(u32 cti, cnat_addr_resol_type_t type)
Cleanup matching addr resolution requests.
unformat_function_t unformat_line_input
Definition: format.h:282
vnet_crypto_main_t * cm
Definition: quic_crypto.c:53
static clib_error_t * cnat_snat_exclude(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
Definition: cnat_snat.c:233
struct _unformat_input_t unformat_input_t
u32 dst_address_length_refcounts[129]
Definition: cnat_types.h:82
u8 * format_cnat_endpoint(u8 *s, va_list *args)
Definition: cnat_types.c:123
int cnat_search_snat_prefix(ip46_address_t *addr, ip_address_family_t af)
Definition: cnat_snat.c:106
vl_api_ip4_address_t ip4
Definition: one.api:376
u8 len
Definition: ip_types.api:103
unformat_function_t unformat_ip6_address
Definition: format.h:89
#define UNFORMAT_END_OF_INPUT
Definition: format.h:144
format_function_t format_ip6_address
Definition: format.h:91
sll srl srl sll sra u16x4 i
Definition: vector_sse42.h:317
#define ARRAY_LEN(x)
Definition: clib.h:67
#define VLIB_CLI_COMMAND(x,...)
Definition: cli.h:158
#define ASSERT(truth)
void vlib_cli_output(vlib_main_t *vm, char *fmt,...)
Definition: cli.c:696
cnat_snat_pfx_table_t snat_pfx_table
Definition: cnat_types.h:137
enum ip_address_family_t_ ip_address_family_t
ip6_address_t ip_masks[129]
Definition: cnat_types.h:94
uword * non_empty_dst_address_length_bitmap
Definition: cnat_types.h:84
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
#define INDEX_INVALID
Invalid index - used when no index is known blazoned capitals INVALID speak volumes where ~0 does not...
Definition: dpo.h:47
static void unformat_free(unformat_input_t *i)
Definition: format.h:162
u8 * format_cnat_snat_prefix(u8 *s, va_list *args)
Definition: cnat_snat.c:148
cnat_main_t cnat_main
Definition: cnat_types.c:18
#define ip_prefix_version(_a)
Definition: ip_types.h:123
static clib_error_t * cnat_snat_init(vlib_main_t *vm)
Definition: cnat_snat.c:296
u8 * format_unformat_error(u8 *s, va_list *va)
Definition: unformat.c:91
ip_address_t ce_ip
Definition: cnat_types.h:62
cnat_snat_pfx_table_meta_t meta[2]
Definition: cnat_types.h:92
uword unformat_ip_prefix(unformat_input_t *input, va_list *args)
Definition: ip_types.c:64
cnat_endpoint_t snat_ip4
Definition: cnat_types.h:131
u8 cnat_resolve_ep(cnat_endpoint_t *ep)
Resolve endpoint address.
Definition: cnat_types.c:64
u32 snat_hash_buckets
Definition: cnat_types.h:115
void cnat_lazy_init()
Lazy initialization when first adding a translation or using snat.
Definition: cnat_types.c:168
vl_api_interface_index_t sw_if_index
Definition: wireguard.api:34
uword unformat(unformat_input_t *i, const char *fmt,...)
Definition: unformat.c:978
uword snat_hash_memory
Definition: cnat_types.h:112
static uword unformat_check_input(unformat_input_t *i)
Definition: format.h:170