FD.io VPP  v21.06-1-gbb7418cf9
Vector Packet Processing
pnat.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2021 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 "pnat.h"
17 #include <arpa/inet.h>
18 #include <stdbool.h>
19 #include <vlib/vlib.h>
20 #include <vnet/feature/feature.h>
21 #include <vnet/fib/fib_table.h>
22 #include <vnet/ip/format.h>
23 #include <vnet/ip/ip4.h>
24 #include <vnet/ip/ip4_packet.h>
26 #include <vppinfra/clib_error.h>
27 
28 /*
29  * This is the main control plane part of the PNAT (Policy 1:1 NAT) feature.
30  */
31 
33 
34 /*
35  * Do a lookup in the interface vector (interface_by_sw_if_index)
36  * and return pool entry.
37  */
39  pnat_main_t *pm = &pnat_main;
40 
41  if (!pm->interface_by_sw_if_index ||
42  sw_if_index > (vec_len(pm->interface_by_sw_if_index) - 1))
43  return 0;
45  if (index == ~0)
46  return 0;
47  if (pool_is_free_index(pm->interfaces, index))
48  return 0;
49  return pool_elt_at_index(pm->interfaces, index);
50 }
51 
53  pnat_mask_fast_t m = {0};
54 
55  if (lookup_mask & PNAT_SA)
56  m.as_u64[0] = 0xffffffff00000000;
57  if (lookup_mask & PNAT_DA)
58  m.as_u64[0] |= 0x00000000ffffffff;
59  m.as_u64[1] = 0xffffffff00000000;
60  if (lookup_mask & PNAT_SPORT)
61  m.as_u64[1] |= 0x00000000ffff0000;
62  if (lookup_mask & PNAT_DPORT)
63  m.as_u64[1] |= 0x000000000000ffff;
64  return m;
65 }
66 
67 /*
68  * Create new PNAT interface object and register the pnat feature in the
69  * corresponding feature chain.
70  * Also enable shallow virtual reassembly, to ensure that we have
71  * L4 ports available for all packets we receive.
72  */
74  pnat_attachment_point_t attachment,
75  pnat_mask_t mask) {
76  pnat_main_t *pm = &pnat_main;
77  pnat_interface_t *interface = pnat_interface_by_sw_if_index(sw_if_index);
78 
79  if (!interface) {
80  pool_get_zero(pm->interfaces, interface);
81  interface->sw_if_index = sw_if_index;
83  pm->interface_by_sw_if_index[sw_if_index] = interface - pm->interfaces;
84  }
85 
86  char *nodename;
87  char *arcname;
88  bool input = false;
89  switch (attachment) {
90  case PNAT_IP4_INPUT:
91  nodename = "pnat-input";
92  arcname = "ip4-unicast";
93  input = true;
94  break;
95 
96  case PNAT_IP4_OUTPUT:
97  nodename = "pnat-output";
98  arcname = "ip4-output";
99  break;
100  default:
101  return clib_error_return(0, "Unknown attachment point %u %u",
102  sw_if_index, attachment);
103  }
104 
105  if (!interface->enabled[attachment]) {
106  if (vnet_feature_enable_disable(arcname, nodename, sw_if_index, 1, 0,
107  0) != 0)
108  return clib_error_return(0, "PNAT feature enable failed on %u",
109  sw_if_index);
110 
111  if (input) {
112  /* TODO: Make shallow virtual reassembly configurable */
113  if (ip4_sv_reass_enable_disable_with_refcnt(sw_if_index, 1) != 0)
114  return clib_error_return(0, "PNAT SVR enable failed on %u",
115  sw_if_index);
116 
117  } else {
119  1) != 0)
120  return clib_error_return(0, "PNAT SVR enable failed on %u",
121  sw_if_index);
122  }
123 
124  interface->lookup_mask[attachment] = mask;
125  interface->lookup_mask_fast[attachment] = pnat_mask2fast(mask);
126  interface->enabled[attachment] = true;
127 
128  } else {
129  pnat_mask_t current_mask = interface->lookup_mask[attachment];
130  if (current_mask != mask) {
131  return clib_error_return(0,
132  "PNAT lookup mask must be consistent per "
133  "interface/direction %u",
134  sw_if_index);
135  }
136  }
137 
138  interface->refcount++;
139 
140  return 0;
141 }
142 
143 /*
144  * Delete interface object when no rules reference the interface.
145  */
147  pnat_attachment_point_t attachment) {
148  pnat_main_t *pm = &pnat_main;
149  pnat_interface_t *interface = pnat_interface_by_sw_if_index(sw_if_index);
150 
151  if (!interface)
152  return 0;
153  if (interface->refcount == 0)
154  return 0;
155 
156  if (interface->enabled[attachment] && attachment == PNAT_IP4_INPUT) {
157  if (ip4_sv_reass_enable_disable_with_refcnt(sw_if_index, 0) != 0)
158  return -1;
159  if (vnet_feature_enable_disable("ip4-unicast", "pnat-input",
160  sw_if_index, 0, 0, 0) != 0)
161  return -1;
162  }
163  if (interface->enabled[attachment] && attachment == PNAT_IP4_OUTPUT) {
164  if (ip4_sv_reass_output_enable_disable_with_refcnt(sw_if_index, 0) != 0)
165  return -1;
166  if (vnet_feature_enable_disable("ip4-output", "pnat-output",
167  sw_if_index, 0, 0, 0) != 0)
168  return -1;
169  }
170 
171  interface->lookup_mask[attachment] = 0;
172  interface->enabled[attachment] = false;
173 
174  interface->refcount--;
175  if (interface->refcount == 0) {
177  pool_put(pm->interfaces, interface);
178  }
179  return 0;
180 }
181 
182 /*
183  * From a 5-tuple (with mask) calculate the key used in the flow cache lookup.
184  */
186  pnat_attachment_point_t attachment,
187  pnat_match_tuple_t *match,
188  clib_bihash_kv_16_8_t *kv) {
189  pnat_mask_fast_t mask = pnat_mask2fast(match->mask);
191  clib_memcpy(&src, &match->src, 4);
192  clib_memcpy(&dst, &match->dst, 4);
193  pnat_calc_key(sw_if_index, attachment, src, dst, match->proto,
194  htons(match->sport), htons(match->dport), mask, kv);
195 }
196 
197 /*
198  * Map between the 5-tuple mask and the instruction set of the rewrite node.
199  */
202 
203  if (m & PNAT_SA)
205  if (m & PNAT_DA)
207  if (m & PNAT_SPORT)
209  if (m & PNAT_DPORT)
211  if (m & PNAT_COPY_BYTE)
213  if (m & PNAT_CLEAR_BYTE)
215  return i;
216 }
217 
218 /*
219  * "Init" the PNAT datastructures. Called upon first creation of a PNAT rule.
220  * TODO: Make number of buckets configurable.
221  */
222 static void pnat_enable(void) {
223  pnat_main_t *pm = &pnat_main;
224  if (pm->enabled)
225  return;
226 
227  /* Create new flow cache table */
228  clib_bihash_init_16_8(&pm->flowhash, "PNAT flow hash",
230 
231  pm->enabled = true;
232 }
233 static void pnat_disable(void) {
234  pnat_main_t *pm = &pnat_main;
235 
236  if (!pm->enabled)
237  return;
238  if (pool_elts(pm->translations))
239  return;
240 
241  /* Delete flow cache table */
242  clib_bihash_free_16_8(&pm->flowhash);
243 
244  pm->enabled = false;
245 }
246 
247 /*
248  * Ensure that a new rule lookup mask matches what's installed on interface
249  */
251  pnat_attachment_point_t attachment,
252  pnat_mask_t mask) {
253  pnat_interface_t *interface = pnat_interface_by_sw_if_index(sw_if_index);
254  if (!interface)
255  return 0;
256  if (!interface->enabled[attachment])
257  return 0;
258  if (interface->lookup_mask[attachment] != mask)
259  return -1;
260 
261  return 0;
262 }
263 
264 /*
265  * Add a binding to the binding table.
266  * Returns 0 on success.
267  * -1: Invalid mask
268  * -2: Only matches ports for UDP or TCP
269  *
270  */
272  u32 *index) {
273  pnat_main_t *pm = &pnat_main;
274 
275  *index = -1;
276 
277  /* If we aren't matching or rewriting, why are we here? */
278  if (match->mask == 0 || rewrite->mask == 0)
279  return -1;
280 
281  /* Check if protocol is set if ports are set */
282  if ((match->dport || match->sport) &&
283  (match->proto != IP_API_PROTO_UDP && match->proto != IP_API_PROTO_TCP))
284  return -2;
285 
286  /* Create pool entry */
288  pool_get_zero(pm->translations, t);
289  memcpy(&t->post_da, &rewrite->dst, 4);
290  memcpy(&t->post_sa, &rewrite->src, 4);
291  t->post_sp = rewrite->sport;
292  t->post_dp = rewrite->dport;
293  t->from_offset = rewrite->from_offset;
294  t->to_offset = rewrite->to_offset;
295  t->clear_offset = rewrite->clear_offset;
296  t->instructions = pnat_instructions_from_mask(rewrite->mask);
297 
298  /* These are only used for show commands and trace */
299  t->match = *match;
300  t->rewrite = *rewrite;
301 
302  *index = t - pm->translations;
303 
304  return 0;
305 }
306 
307 /*
308  * Looks a match flow in the flow cache, returns the index in the binding table
309  */
311  pnat_match_tuple_t *match) {
312  pnat_main_t *pm = &pnat_main;
314  pnat_calc_key_from_5tuple(sw_if_index, attachment, match, &kv);
315  if (clib_bihash_search_16_8(&pm->flowhash, &kv, &value) == 0) {
316  return value.value;
317  }
318  return ~0;
319 }
320 
321 /*
322  * Attach a binding to an interface / direction.
323  * Returns 0 on success.
324  * -1: Binding does not exist
325  * -2: Interface mask does not match
326  * -3: Existing match entry in flow table
327  * -4: Adding flow table entry failed
328  */
330  u32 binding_index) {
331  pnat_main_t *pm = &pnat_main;
332 
333  if (!pm->translations ||
334  pool_is_free_index(pm->translations, binding_index))
335  return -1;
336 
337  pnat_translation_t *t = pool_elt_at_index(pm->translations, binding_index);
338 
339  if (pnat_interface_check_mask(sw_if_index, attachment, t->match.mask) != 0)
340  return -2;
341 
342  pnat_enable();
343 
344  /* Verify non-duplicate */
346  pnat_calc_key_from_5tuple(sw_if_index, attachment, &t->match, &kv);
347  if (clib_bihash_search_16_8(&pm->flowhash, &kv, &value) == 0) {
348  return -3;
349  }
350 
351  /* Create flow cache */
352  kv.value = binding_index;
353  if (clib_bihash_add_del_16_8(&pm->flowhash, &kv, 1)) {
354  pool_put(pm->translations, t);
355  return -4;
356  }
357 
358  /* Register interface */
359  pnat_enable_interface(sw_if_index, attachment, t->match.mask);
360 
361  return 0;
362 }
363 
365  u32 binding_index) {
366  pnat_main_t *pm = &pnat_main;
367 
368  if (!pm->translations ||
369  pool_is_free_index(pm->translations, binding_index))
370  return -1;
371 
372  pnat_translation_t *t = pool_elt_at_index(pm->translations, binding_index);
373 
374  /* Verify non-duplicate */
376  pnat_calc_key_from_5tuple(sw_if_index, attachment, &t->match, &kv);
377  if (clib_bihash_add_del_16_8(&pm->flowhash, &kv, 0)) {
378  return -2;
379  }
380 
381  /* Deregister interface */
382  pnat_disable_interface(sw_if_index, attachment);
383 
384  pnat_disable();
385 
386  return 0;
387 }
388 
389 /*
390  * Delete a translation using the index returned from pnat_add_translation.
391  */
393  pnat_main_t *pm = &pnat_main;
394 
395  if (pool_is_free_index(pm->translations, index)) {
396  clib_warning("Binding delete: translation does not exist: %d", index);
397  return -1;
398  }
399 
401  pool_put(pm->translations, t);
402 
403  return 0;
404 }
u32 pnat_flow_lookup(u32 sw_if_index, pnat_attachment_point_t attachment, pnat_match_tuple_t *match)
Definition: pnat.c:310
int ip4_sv_reass_output_enable_disable_with_refcnt(u32 sw_if_index, int is_enable)
#define pool_get_zero(P, E)
Allocate an object E from a pool P and zero it.
Definition: pool.h:258
pnat_rewrite_tuple_t rewrite
Definition: pnat.h:66
pnat_mask_t lookup_mask[PNAT_ATTACHMENT_POINT_MAX]
Definition: pnat.h:72
ip4_address_t post_sa
Definition: pnat.h:53
static void pnat_enable(void)
Definition: pnat.c:222
vl_api_address_t src
Definition: gre.api:54
static void pnat_disable(void)
Definition: pnat.c:233
static int pnat_disable_interface(u32 sw_if_index, pnat_attachment_point_t attachment)
Definition: pnat.c:146
unsigned int u32
Definition: types.h:88
#define clib_memcpy(d, s, n)
Definition: string.h:197
int pnat_binding_detach(u32 sw_if_index, pnat_attachment_point_t attachment, u32 binding_index)
Definition: pnat.c:364
pnat_instructions_t pnat_instructions_from_mask(pnat_mask_t m)
Definition: pnat.c:200
clib_bihash_16_8_t flowhash
Definition: pnat.h:85
#define clib_error_return(e, args...)
Definition: error.h:99
pnat_instructions_t instructions
Definition: pnat.h:50
vl_api_pnat_attachment_point_t pnat_attachment_point_t
Definition: pnat.h:30
int ip4_sv_reass_enable_disable_with_refcnt(u32 sw_if_index, int is_enable)
Definition: pnat.api:23
vl_api_pnat_mask_t pnat_mask_t
Definition: pnat.h:29
pnat_translation_t * translations
Definition: pnat.h:92
#define pool_elt_at_index(p, i)
Returns pointer to element at given index.
Definition: pool.h:553
vl_api_interface_index_t sw_if_index
Definition: wireguard.api:34
IPv4 shallow virtual reassembly.
#define pool_put(P, E)
Free an object E in pool P.
Definition: pool.h:305
int vnet_feature_enable_disable(const char *arc_name, const char *node_name, u32 sw_if_index, int enable_disable, void *feature_config, u32 n_feature_config_bytes)
pnat_interface_t * pnat_interface_by_sw_if_index(u32 sw_if_index)
Definition: pnat.c:38
static void pnat_calc_key_from_5tuple(u32 sw_if_index, pnat_attachment_point_t attachment, pnat_match_tuple_t *match, clib_bihash_kv_16_8_t *kv)
Definition: pnat.c:185
vl_api_pnat_rewrite_tuple_t pnat_rewrite_tuple_t
Definition: pnat.h:28
static int pnat_interface_check_mask(u32 sw_if_index, pnat_attachment_point_t attachment, pnat_mask_t mask)
Definition: pnat.c:250
#define PNAT_FLOW_HASH_BUCKETS
Definition: pnat.h:23
sll srl srl sll sra u16x4 i
Definition: vector_sse42.h:261
vl_api_pnat_mask_t mask
Definition: pnat.api:45
u32 index
Definition: flow_types.api:221
#define clib_warning(format, args...)
Definition: error.h:59
#define pool_is_free_index(P, I)
Use free bitmap to query whether given index is free.
Definition: pool.h:302
pnat_match_tuple_t match
Definition: pnat.h:65
static clib_error_t * pnat_enable_interface(u32 sw_if_index, pnat_attachment_point_t attachment, pnat_mask_t mask)
Definition: pnat.c:73
vl_api_pnat_match_tuple_t pnat_match_tuple_t
Definition: pnat.h:27
u8 value
Definition: qos.api:54
int pnat_binding_del(u32 index)
Definition: pnat.c:392
int pnat_binding_add(pnat_match_tuple_t *match, pnat_rewrite_tuple_t *rewrite, u32 *index)
Definition: pnat.c:271
static void pnat_calc_key(u32 sw_if_index, pnat_attachment_point_t attachment, ip4_address_t src, ip4_address_t dst, u8 protocol, u16 sport, u16 dport, pnat_mask_fast_t mask, clib_bihash_kv_16_8_t *kv)
Definition: pnat.h:118
ip4_address_t post_da
Definition: pnat.h:54
Definition: pnat.api:24
pnat_instructions_t
Definition: pnat.h:33
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
static pnat_mask_fast_t pnat_mask2fast(pnat_mask_t lookup_mask)
Definition: pnat.c:52
vl_api_ip4_address_t dst
Definition: pnat.api:41
pnat_interface_t * interfaces
Definition: pnat.h:88
#define vec_validate_init_empty(V, I, INIT)
Make sure vector is long enough for given index and initialize empty space (no header, unspecified alignment)
Definition: vec.h:571
bool enabled
Definition: pnat.h:83
u64 as_u64[2]
Definition: pnat.h:44
u32 * interface_by_sw_if_index
Definition: pnat.h:89
pnat_main_t pnat_main
Definition: pnat.c:32
rewrite
Definition: pnat.api:158
int pnat_binding_attach(u32 sw_if_index, pnat_attachment_point_t attachment, u32 binding_index)
Definition: pnat.c:329
static uword pool_elts(void *v)
Number of active elements in a pool.
Definition: pool.h:127