FD.io VPP  v20.09-64-g4f7b92f0a
Vector Packet Processing
ct6.c
Go to the documentation of this file.
1 /*
2  * ct6.c - skeleton vpp engine plug-in
3  *
4  * Copyright (c) <current-year> <your-organization>
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at:
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 #include <vnet/vnet.h>
19 #include <vnet/plugin/plugin.h>
20 #include <ct6/ct6.h>
21 
22 #include <vlibapi/api.h>
23 #include <vlibmemory/api.h>
24 #include <vpp/app/version.h>
25 
26 /* define message IDs */
27 #include <ct6/ct6.api_enum.h>
28 #include <ct6/ct6.api_types.h>
29 
30 #define REPLY_MSG_ID_BASE cmp->msg_id_base
32 
34 
35 /* Action function shared between message handler and debug CLI */
36 
37 static void
39 {
40  u32 nworkers = vlib_num_workers ();
41 
42  if (cmp->feature_initialized)
43  return;
44 
45  clib_bihash_init_48_8 (&cmp->session_hash, "ct6 session table",
47  cmp->feature_initialized = 1;
48  vec_validate (cmp->sessions, nworkers);
49  vec_validate_init_empty (cmp->first_index, nworkers, ~0);
50  vec_validate_init_empty (cmp->last_index, nworkers, ~0);
51 }
52 
53 int
55  int enable_disable)
56 {
58  int rv = 0;
59 
60  ct6_feature_init (cmp);
61 
62  /* Utterly wrong? */
64  sw_if_index))
65  return VNET_API_ERROR_INVALID_SW_IF_INDEX;
66 
67  /* Not a physical port? */
68  sw = vnet_get_sw_interface (cmp->vnet_main, sw_if_index);
70  return VNET_API_ERROR_INVALID_SW_IF_INDEX;
71 
72  vnet_feature_enable_disable ("interface-output", "ct6-in2out",
73  sw_if_index, enable_disable, 0, 0);
74 
75  return rv;
76 }
77 
78 int
80  int enable_disable)
81 {
83  int rv = 0;
84 
85  ct6_feature_init (cmp);
86 
87  /* Utterly wrong? */
89  sw_if_index))
90  return VNET_API_ERROR_INVALID_SW_IF_INDEX;
91 
92  /* Not a physical port? */
93  sw = vnet_get_sw_interface (cmp->vnet_main, sw_if_index);
95  return VNET_API_ERROR_INVALID_SW_IF_INDEX;
96 
97  vnet_feature_enable_disable ("ip6-unicast", "ct6-out2in",
98  sw_if_index, enable_disable, 0, 0);
99 
100  return rv;
101 }
102 
103 static clib_error_t *
105  unformat_input_t * input,
106  vlib_cli_command_t * cmd)
107 {
108  ct6_main_t *cmp = &ct6_main;
109  u32 sw_if_index = ~0;
110  int enable_disable = 1;
111  u32 inside = ~0;
112  int rv;
113 
115  {
116  if (unformat (input, "disable"))
117  enable_disable = 0;
118  else if (unformat (input, "%U", unformat_vnet_sw_interface,
119  cmp->vnet_main, &sw_if_index))
120  ;
121  else if (unformat (input, "inside") || unformat (input, "in"))
122  inside = 1;
123  else if (unformat (input, "outside") || unformat (input, "out"))
124  inside = 0;
125  else
126  break;
127  }
128 
129  if (inside == ~0)
130  return clib_error_return (0, "Please specify inside or outside");
131 
132  if (sw_if_index == ~0)
133  return clib_error_return (0, "Please specify an interface...");
134 
135  if (inside == 1)
136  rv = ct6_in2out_enable_disable (cmp, sw_if_index, enable_disable);
137  else
138  rv = ct6_out2in_enable_disable (cmp, sw_if_index, enable_disable);
139 
140  switch (rv)
141  {
142  case 0:
143  break;
144 
145  case VNET_API_ERROR_INVALID_SW_IF_INDEX:
146  return clib_error_return
147  (0, "Invalid interface, only works on physical ports");
148  break;
149 
150  default:
151  return clib_error_return (0, "ct6_enable_disable returned %d", rv);
152  }
153  return 0;
154 }
155 
156 /* *INDENT-OFF* */
157 VLIB_CLI_COMMAND (set_ct6_command, static) =
158 {
159  .path = "set ct6",
160  .short_help =
161  "set ct6 [inside|outside] <interface-name> [disable]",
163 };
164 /* *INDENT-ON* */
165 
166 /* API message handler */
169 {
170  vl_api_ct6_enable_disable_reply_t *rmp;
171  ct6_main_t *cmp = &ct6_main;
172  int rv;
173 
175 
176  if (mp->is_inside)
177  rv = ct6_in2out_enable_disable (cmp, ntohl (mp->sw_if_index),
178  (int) (mp->enable_disable));
179  else
180  rv = ct6_out2in_enable_disable (cmp, ntohl (mp->sw_if_index),
181  (int) (mp->enable_disable));
182 
184  REPLY_MACRO (VL_API_CT6_ENABLE_DISABLE_REPLY);
185 }
186 
187 #include <ct6/ct6.api.c>
188 static clib_error_t *
190 {
191  ct6_main_t *cmp = &ct6_main;
192  clib_error_t *error = 0;
193 
194  cmp->vlib_main = vm;
195  cmp->vnet_main = vnet_get_main ();
196 
197  /* Ask for a correctly-sized block of API message decode slots */
199 
200  /*
201  * Set default parameters...
202  * 256K sessions
203  * 64K buckets
204  * 2 minute inactivity timer
205  * 10000 concurrent sessions
206  */
207  cmp->session_hash_memory = 16ULL << 20;
208  cmp->session_hash_buckets = 64 << 10;
209  cmp->session_timeout_interval = 120.0;
210  cmp->max_sessions_per_worker = 10000;
211 
212  /* ... so the packet generator can feed the in2out node ... */
214  return error;
215 }
216 
218 
219 /* *INDENT-OFF* */
220 VNET_FEATURE_INIT (ct6out2in, static) =
221 {
222  .arc_name = "ip6-unicast",
223  .node_name = "ct6-out2in",
224  .runs_before = VNET_FEATURES ("ip6-lookup"),
225 };
226 /* *INDENT-ON */
227 
228 /* *INDENT-OFF* */
229 VNET_FEATURE_INIT (ct6in2out, static) =
230 {
231  .arc_name = "interface-output",
232  .node_name = "ct6-in2out",
233  .runs_before = VNET_FEATURES ("interface-tx"),
234 };
235 /* *INDENT-ON */
236 
237 /* *INDENT-OFF* */
239 {
240  .version = VPP_BUILD_VER,
241  .description = "IPv6 Connection Tracker",
242 };
243 /* *INDENT-ON* */
244 
245 u8 *
246 format_ct6_session (u8 * s, va_list * args)
247 {
248  ct6_main_t *cmp = va_arg (*args, ct6_main_t *);
249  int i = va_arg (*args, int);
250  ct6_session_t *s0 = va_arg (*args, ct6_session_t *);
251  int verbose = va_arg (*args, int);
253 
254  if (s0 == 0)
255  {
256  s = format (s, "\n%6s%6s%40s%6s%40s%6s",
257  "Sess", "Prot", "Src", "Sport", "Dst", "Dport");
258  return s;
259  }
260 
261  s = format (s, "\n%6d%6d%40U%6u%40U%6u",
262  s0 - cmp->sessions[i], s0->key.proto,
263  format_ip6_address, &s0->key.src,
264  clib_net_to_host_u16 (s0->key.sport),
265  format_ip6_address, &s0->key.dst,
266  clib_net_to_host_u16 (s0->key.dport));
267 
268  clib_memcpy_fast (&kvp0, s0, sizeof (ct6_session_key_t));
269 
270  if (clib_bihash_search_48_8 (&cmp->session_hash, &kvp0, &kvp0) < 0)
271  {
272  s = format (s, " LOOKUP FAIL!");
273  }
274  else
275  {
276  if (kvp0.value == s0 - cmp->sessions[s0->thread_index])
277  {
278  s = format (s, " OK");
279  if (verbose > 1)
280  {
281  s = format (s, " next %d prev %d", s0->next_index,
282  s0->prev_index);
283  s = format (s, " hits %d expires %.2f", s0->hits, s0->expires);
284  }
285  }
286  else
287  s = format (s, " BOGUS LOOKUP RESULT!");
288  }
289 
290  return s;
291 }
292 
293 static clib_error_t *
295  unformat_input_t * input,
296  vlib_cli_command_t * cmd)
297 {
298  ct6_main_t *cmp = &ct6_main;
299  ct6_session_t *s0;
300  int verbose = 0;
301  u8 *s = 0;
302  int i;
303 
304  if (!cmp->feature_initialized)
305  return clib_error_return (0, "ip6 connection tracking not enabled...");
306 
307  if (unformat (input, "verbose %d", &verbose))
308  ;
309  else if (unformat (input, "verbose"))
310  verbose = 1;
311 
312  for (i = 0; i < vec_len (cmp->sessions); i++)
313  {
314  s = format (s, "Thread %d: %d sessions\n", i,
315  pool_elts (cmp->sessions[i]));
316 
317  if (verbose == 0)
318  continue;
319 
320  s =
321  format (s, "%U", format_ct6_session, cmp,
322  0 /* pool */ , 0 /* header */ , verbose);
323 
324  /* *INDENT-OFF* */
325  pool_foreach (s0, cmp->sessions[i],
326  ({
327  s = format (s, "%U", format_ct6_session, cmp, i, s0, verbose);
328  }));
329  /* *INDENT-ON* */
330  }
331  vlib_cli_output (cmp->vlib_main, "%v", s);
332  vec_free (s);
333  return 0;
334 }
335 
336 /* *INDENT-OFF* */
337 VLIB_CLI_COMMAND (show_ct6_command_fn_command, static) =
338 {
339  .path = "show ip6 connection-tracker",
340  .short_help = "show ip6 connection-tracker",
341  .function = show_ct6_command_fn_command_fn,
342 };
343 /* *INDENT-ON* */
344 
345 static void
346 increment_v6_address (ip6_address_t * a)
347 {
348  u64 v0, v1;
349 
350  v0 = clib_net_to_host_u64 (a->as_u64[0]);
351  v1 = clib_net_to_host_u64 (a->as_u64[1]);
352 
353  v1 += 1;
354  if (v1 == 0)
355  v0 += 1;
356  a->as_u64[0] = clib_net_to_host_u64 (v0);
357  a->as_u64[1] = clib_net_to_host_u64 (v1);
358 }
359 
360 
361 static clib_error_t *
363  unformat_input_t * input,
364  vlib_cli_command_t * cmd)
365 {
366  ct6_main_t *cmp = &ct6_main;
368  ct6_session_key_t *key0;
369  ct6_session_t *s0;
370  u8 src[16], dst[16];
371  u32 recycled = 0, created = 0;
372  int i, num_sessions = 5;
373  u32 midpt_index;
374  u8 *s = 0;
375 
376  cmp->max_sessions_per_worker = 4;
377 
379  {
380  if (unformat (input, "num-sessions %d", &num_sessions))
381  ;
382  else
383  if (unformat
384  (input, "max-sessions %d", &cmp->max_sessions_per_worker))
385  ;
386  else
387  break;
388  }
389 
390  ct6_feature_init (cmp);
391 
392  /* Set up starting src/dst addresses */
393  memset (src, 0, sizeof (src));
394  memset (dst, 0, sizeof (dst));
395 
396  src[0] = 0xdb;
397  dst[0] = 0xbe;
398 
399  src[15] = 1;
400  dst[15] = 1;
401 
402  /*
403  * See if we know about this flow.
404  * Key set up for the out2in path, the performant case
405  */
406  key0 = (ct6_session_key_t *) & kvp0;
407  memset (&kvp0, 0, sizeof (kvp0));
408 
409  for (i = 0; i < num_sessions; i++)
410  {
411  clib_memcpy_fast (&key0->src, src, sizeof (src));
412  clib_memcpy_fast (&key0->dst, dst, sizeof (dst));
413  key0->as_u64[4] = 0;
414  key0->as_u64[5] = 0;
415  key0->sport = clib_host_to_net_u16 (1234);
416  key0->dport = clib_host_to_net_u16 (4321);
417  key0->proto = 17; /* udp, fwiw */
418 
420  (cmp, &kvp0, 3.0 /* now */ , 0 /* thread index */ ,
421  &recycled, &created);
422 
423  s = format (s, "%U (%d, %d)", format_ct6_session, cmp,
424  0 /* thread index */ , s0, 1 /* verbose */ ,
425  recycled, created);
426  vlib_cli_output (vm, "%v", s);
427  vec_free (s);
428  increment_v6_address ((ip6_address_t *) src);
429  recycled = 0;
430  created = 0;
431  }
432 
433  /* *INDENT-OFF* */
434  pool_foreach (s0, cmp->sessions[0],
435  ({
436  s = format (s, "%U", format_ct6_session, cmp, 0, s0, 1 /* verbose */);
437  }));
438  /* *INDENT-ON* */
439 
440  vlib_cli_output (vm, "\nEnd state: first index %d last index %d\n%v",
441  cmp->first_index[0], cmp->last_index[0], s);
442 
443  vec_free (s);
444 
445  midpt_index = cmp->max_sessions_per_worker / 3;
446 
447  s0 = pool_elt_at_index (cmp->sessions[0], midpt_index);
448  vlib_cli_output (vm, "\nSimulate LRU hit on session %d",
449  s0 - cmp->sessions[0]);
450 
451  ct6_update_session_hit (cmp, s0, 234.0);
452 
453  /* *INDENT-OFF* */
454  pool_foreach (s0, cmp->sessions[0],
455  ({
456  s = format (s, "%U", format_ct6_session, cmp, 0, s0, 1 /* verbose */);
457  }));
458  /* *INDENT-ON* */
459 
460  vlib_cli_output (vm, "\nEnd state: first index %d last index %d\n%v",
461  cmp->first_index[0], cmp->last_index[0], s);
462 
463  vec_free (s);
464 
465  return 0;
466 }
467 
468 /* *INDENT-OFF* */
469 VLIB_CLI_COMMAND (test_ct6_command_fn_command, static) =
470 {
471  .path = "test ip6 connection-tracker",
472  .short_help = "test ip6 connection-tracker",
473  .function = test_ct6_command_fn_command_fn,
474 };
475 /* *INDENT-ON* */
476 
477 static clib_error_t *
479 {
480  ct6_main_t *cmp = &ct6_main;
481 
483  {
484  if (unformat (input, "session-hash-buckets %u",
485  &cmp->session_hash_buckets))
486  ;
487  else if (unformat (input, "session-hash-memory %U",
489  ;
490  else if (unformat (input, "session-timeout %f",
492  ;
493  else
494  {
495  return clib_error_return (0, "unknown input '%U'",
496  format_unformat_error, input);
497  }
498  }
499  return 0;
500 }
501 
503 
504 /*
505  * fd.io coding-style-patch-verification: ON
506  *
507  * Local Variables:
508  * eval: (c-set-style "gnu")
509  * End:
510  */
#define vec_validate(V, I)
Make sure vector is long enough for given index (no header, unspecified alignment) ...
Definition: vec.h:509
VLIB_PLUGIN_REGISTER()
a
Definition: bitmap.h:538
vnet_main_t * vnet_get_main(void)
Definition: misc.c:46
u32 session_hash_buckets
Definition: ct6.h:75
static clib_error_t * test_ct6_command_fn_command_fn(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
Definition: ct6.c:362
vnet_interface_main_t interface_main
Definition: vnet.h:59
vnet_main_t * vnet_main
Definition: ct6.h:80
unsigned long u64
Definition: types.h:89
vl_api_interface_index_t sw_if_index
Definition: ct6.api:20
#define clib_memcpy_fast(a, b, c)
Definition: string.h:81
ct6_main_t ct6_main
Definition: ct6.c:33
vl_api_address_t src
Definition: gre.api:54
static vnet_sw_interface_t * vnet_get_sw_interface(vnet_main_t *vnm, u32 sw_if_index)
vlib_main_t * vm
Definition: in2out_ed.c:1582
u8 * format(u8 *s, const char *fmt,...)
Definition: format.c:424
unformat_function_t unformat_vnet_sw_interface
unsigned char u8
Definition: types.h:56
Definition: ct6.h:58
int ct6_in2out_enable_disable(ct6_main_t *cmp, u32 sw_if_index, int enable_disable)
Definition: ct6.c:54
f64 expires
Definition: ct6.h:55
ct6_session_t ** sessions
Definition: ct6.h:68
#define pool_foreach(VAR, POOL, BODY)
Iterate through pool.
Definition: pool.h:513
vlib_main_t * vlib_main
Definition: ct6.h:79
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:173
static void vl_api_ct6_enable_disable_t_handler(vl_api_ct6_enable_disable_t *mp)
Definition: ct6.c:168
#define clib_error_return(e, args...)
Definition: error.h:99
unsigned int u32
Definition: types.h:88
static clib_error_t * ct6_init(vlib_main_t *vm)
Definition: ct6.c:189
#define pool_elt_at_index(p, i)
Returns pointer to element at given index.
Definition: pool.h:534
VNET_FEATURE_INIT(ct6out2in, static)
u8 * format_ct6_session(u8 *s, va_list *args)
Definition: ct6.c:246
struct _unformat_input_t unformat_input_t
u16 msg_id_base
Definition: ct6.h:61
uword session_hash_memory
Definition: ct6.h:74
#define VLIB_CONFIG_FUNCTION(x, n,...)
Definition: init.h:182
#define REPLY_MACRO(t)
vl_api_address_t dst
Definition: gre.api:55
ct6_session_t * ct6_create_or_recycle_session(ct6_main_t *cmp, clib_bihash_kv_48_8_t *kvpp, f64 now, u32 my_thread_index, u32 *recyclep, u32 *createp)
Definition: ct6_in2out.c:78
static clib_error_t * set_ct6_enable_disable_command_fn(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
Definition: ct6.c:104
#define BAD_SW_IF_INDEX_LABEL
#define UNFORMAT_END_OF_INPUT
Definition: format.h:145
format_function_t format_ip6_address
Definition: format.h:91
sll srl srl sll sra u16x4 i
Definition: vector_sse42.h:317
#define vec_free(V)
Free vector&#39;s memory (no header).
Definition: vec.h:380
u32 * first_index
Definition: ct6.h:69
static clib_error_t * ct6_config(vlib_main_t *vm, unformat_input_t *input)
Definition: ct6.c:478
u8 feature_initialized
Definition: ct6.h:65
#define pool_is_free_index(P, I)
Use free bitmap to query whether given index is free.
Definition: pool.h:299
u32 thread_index
Definition: ct6.h:51
u32 next_index
Definition: ct6.h:52
int ct6_out2in_enable_disable(ct6_main_t *cmp, u32 sw_if_index, int enable_disable)
Definition: ct6.c:79
#define VLIB_CLI_COMMAND(x,...)
Definition: cli.h:158
u32 * last_index
Definition: ct6.h:70
void vlib_cli_output(vlib_main_t *vm, char *fmt,...)
Definition: cli.c:696
static void ct6_update_session_hit(ct6_main_t *cmp, ct6_session_t *s0, f64 now)
Definition: ct6.h:165
static clib_error_t * show_ct6_command_fn_command_fn(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
Definition: ct6.c:294
vlib_node_registration_t ct6_in2out_node
(constructor) VLIB_REGISTER_NODE (ct6_in2out_node)
Definition: ct6_in2out.c:350
#define VNET_FEATURES(...)
Definition: feature.h:470
u32 prev_index
Definition: ct6.h:53
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
vnet_sw_interface_t * sw_interfaces
Definition: interface.h:872
clib_bihash_48_8_t session_hash
Definition: ct6.h:64
unformat_function_t unformat_memory_size
Definition: format.h:296
static void ct6_feature_init(ct6_main_t *cmp)
Definition: ct6.c:38
u8 * format_unformat_error(u8 *s, va_list *va)
Definition: unformat.c:91
vnet_sw_interface_type_t type
Definition: interface.h:736
static u32 vlib_num_workers()
Definition: threads.h:377
static void setup_message_id_table(snat_main_t *sm, api_main_t *am)
Definition: nat_api.c:2804
static void increment_v6_address(ip6_address_t *a)
Definition: ct6.c:346
#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:556
u32 max_sessions_per_worker
Definition: ct6.h:76
u32 hits
Definition: ct6.h:54
ct6_session_key_t key
Definition: ct6.h:50
f64 session_timeout_interval
Definition: ct6.h:73
static void ethernet_setup_node(vlib_main_t *vm, u32 node_index)
Definition: ethernet.h:397
vl_api_interface_index_t sw_if_index
Definition: wireguard.api:33
uword unformat(unformat_input_t *i, const char *fmt,...)
Definition: unformat.c:978
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)
Definition: feature.c:303
static uword unformat_check_input(unformat_input_t *i)
Definition: format.h:171
#define VALIDATE_SW_IF_INDEX(mp)
static uword pool_elts(void *v)
Number of active elements in a pool.
Definition: pool.h:128