FD.io VPP  v19.04.4-rc0-5-ge88582fac
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_msg_enum.h>
28 
29 /* define message structures */
30 #define vl_typedefs
31 #include <ct6/ct6_all_api_h.h>
32 #undef vl_typedefs
33 
34 /* define generated endian-swappers */
35 #define vl_endianfun
36 #include <ct6/ct6_all_api_h.h>
37 #undef vl_endianfun
38 
39 /* instantiate all the print functions we know about */
40 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
41 #define vl_printfun
42 #include <ct6/ct6_all_api_h.h>
43 #undef vl_printfun
44 
45 /* Get the API version number */
46 #define vl_api_version(n,v) static u32 api_version=(v);
47 #include <ct6/ct6_all_api_h.h>
48 #undef vl_api_version
49 
50 #define REPLY_MSG_ID_BASE cmp->msg_id_base
52 
54 
55 /* List of message types that this plugin understands */
56 
57 #define foreach_ct6_plugin_api_msg \
58 _(CT6_ENABLE_DISABLE, ct6_enable_disable)
59 
60 /* Action function shared between message handler and debug CLI */
61 
62 static void
64 {
65  u32 nworkers = vlib_num_workers ();
66 
67  if (cmp->feature_initialized)
68  return;
69 
70  clib_bihash_init_48_8 (&cmp->session_hash, "ct6 session table",
72  cmp->feature_initialized = 1;
73  vec_validate (cmp->sessions, nworkers);
74  vec_validate_init_empty (cmp->first_index, nworkers, ~0);
75  vec_validate_init_empty (cmp->last_index, nworkers, ~0);
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 ("interface-output", "ct6-in2out",
98  sw_if_index, enable_disable, 0, 0);
99 
100  return rv;
101 }
102 
103 int
105  int enable_disable)
106 {
108  int rv = 0;
109 
110  ct6_feature_init (cmp);
111 
112  /* Utterly wrong? */
114  sw_if_index))
115  return VNET_API_ERROR_INVALID_SW_IF_INDEX;
116 
117  /* Not a physical port? */
118  sw = vnet_get_sw_interface (cmp->vnet_main, sw_if_index);
120  return VNET_API_ERROR_INVALID_SW_IF_INDEX;
121 
122  vnet_feature_enable_disable ("ip6-unicast", "ct6-out2in",
123  sw_if_index, enable_disable, 0, 0);
124 
125  return rv;
126 }
127 
128 static clib_error_t *
130  unformat_input_t * input,
131  vlib_cli_command_t * cmd)
132 {
133  ct6_main_t *cmp = &ct6_main;
134  u32 sw_if_index = ~0;
135  int enable_disable = 1;
136  u32 inside = ~0;
137  int rv;
138 
140  {
141  if (unformat (input, "disable"))
142  enable_disable = 0;
143  else if (unformat (input, "%U", unformat_vnet_sw_interface,
144  cmp->vnet_main, &sw_if_index))
145  ;
146  else if (unformat (input, "inside") || unformat (input, "in"))
147  inside = 1;
148  else if (unformat (input, "outside") || unformat (input, "out"))
149  inside = 0;
150  else
151  break;
152  }
153 
154  if (inside == ~0)
155  return clib_error_return (0, "Please specify inside or outside");
156 
157  if (sw_if_index == ~0)
158  return clib_error_return (0, "Please specify an interface...");
159 
160  if (inside == 1)
161  rv = ct6_in2out_enable_disable (cmp, sw_if_index, enable_disable);
162  else
163  rv = ct6_out2in_enable_disable (cmp, sw_if_index, enable_disable);
164 
165  switch (rv)
166  {
167  case 0:
168  break;
169 
170  case VNET_API_ERROR_INVALID_SW_IF_INDEX:
171  return clib_error_return
172  (0, "Invalid interface, only works on physical ports");
173  break;
174 
175  default:
176  return clib_error_return (0, "ct6_enable_disable returned %d", rv);
177  }
178  return 0;
179 }
180 
181 /* *INDENT-OFF* */
182 VLIB_CLI_COMMAND (set_ct6_command, static) =
183 {
184  .path = "set ct6",
185  .short_help =
186  "set ct6 [inside|outside] <interface-name> [disable]",
188 };
189 /* *INDENT-ON* */
190 
191 /* API message handler */
194 {
196  ct6_main_t *cmp = &ct6_main;
197  int rv;
198 
200 
201  if (mp->is_inside)
202  rv = ct6_in2out_enable_disable (cmp, ntohl (mp->sw_if_index),
203  (int) (mp->enable_disable));
204  else
205  rv = ct6_out2in_enable_disable (cmp, ntohl (mp->sw_if_index),
206  (int) (mp->enable_disable));
207 
209  REPLY_MACRO (VL_API_CT6_ENABLE_DISABLE_REPLY);
210 }
211 
212 /* Set up the API message handling tables */
213 static clib_error_t *
215 {
216  ct6_main_t *cmp = &ct6_main;
217 #define _(N,n) \
218  vl_msg_api_set_handlers((VL_API_##N + cmp->msg_id_base), \
219  #n, \
220  vl_api_##n##_t_handler, \
221  vl_noop_handler, \
222  vl_api_##n##_t_endian, \
223  vl_api_##n##_t_print, \
224  sizeof(vl_api_##n##_t), 1);
226 #undef _
227 
228  return 0;
229 }
230 
231 #define vl_msg_name_crc_list
232 #include <ct6/ct6_all_api_h.h>
233 #undef vl_msg_name_crc_list
234 
235 static void
237 {
238 #define _(id,n,crc) vl_msg_api_add_msg_name_crc (am, #n #crc, id + cmp->msg_id_base);
239  foreach_vl_msg_name_crc_ct6;
240 #undef _
241 }
242 
243 static clib_error_t *
245 {
246  ct6_main_t *cmp = &ct6_main;
247  clib_error_t *error = 0;
248  u8 *name;
249 
250  cmp->vlib_main = vm;
251  cmp->vnet_main = vnet_get_main ();
252 
253  name = format (0, "ct6_%08x%c", api_version, 0);
254 
255  /* Ask for a correctly-sized block of API message decode slots */
257  ((char *) name, VL_MSG_FIRST_AVAILABLE);
258 
259  error = ct6_plugin_api_hookup (vm);
260 
261  /* Add our API messages to the global name_crc hash table */
263 
264  vec_free (name);
265 
266  /*
267  * Set default parameters...
268  * 256K sessions
269  * 64K buckets
270  * 2 minute inactivity timer
271  * 10000 concurrent sessions
272  */
273  cmp->session_hash_memory = 16ULL << 20;
274  cmp->session_hash_buckets = 64 << 10;
275  cmp->session_timeout_interval = 120.0;
276  cmp->max_sessions_per_worker = 10000;
277 
278  /* ... so the packet generator can feed the in2out node ... */
280  return error;
281 }
282 
284 
285 /* *INDENT-OFF* */
286 VNET_FEATURE_INIT (ct6out2in, static) =
287 {
288  .arc_name = "ip6-unicast",
289  .node_name = "ct6-out2in",
290  .runs_before = VNET_FEATURES ("ip6-lookup"),
291 };
292 /* *INDENT-ON */
293 
294 /* *INDENT-OFF* */
295 VNET_FEATURE_INIT (ct6in2out, static) =
296 {
297  .arc_name = "interface-output",
298  .node_name = "ct6-in2out",
299  .runs_before = VNET_FEATURES ("interface-tx"),
300 };
301 /* *INDENT-ON */
302 
303 /* *INDENT-OFF* */
305 {
306  .version = VPP_BUILD_VER,
307  .description = "IPv6 Connection Tracker",
308 };
309 /* *INDENT-ON* */
310 
311 u8 *
312 format_ct6_session (u8 * s, va_list * args)
313 {
314  ct6_main_t *cmp = va_arg (*args, ct6_main_t *);
315  int i = va_arg (*args, int);
316  ct6_session_t *s0 = va_arg (*args, ct6_session_t *);
317  int verbose = va_arg (*args, int);
319 
320  if (s0 == 0)
321  {
322  s = format (s, "\n%6s%6s%40s%6s%40s%6s",
323  "Sess", "Prot", "Src", "Sport", "Dst", "Dport");
324  return s;
325  }
326 
327  s = format (s, "\n%6d%6d%40U%6u%40U%6u",
328  s0 - cmp->sessions[i], s0->key.proto,
329  format_ip6_address, &s0->key.src,
330  clib_net_to_host_u16 (s0->key.sport),
331  format_ip6_address, &s0->key.dst,
332  clib_net_to_host_u16 (s0->key.dport));
333 
334  clib_memcpy_fast (&kvp0, s0, sizeof (ct6_session_key_t));
335 
336  if (clib_bihash_search_48_8 (&cmp->session_hash, &kvp0, &kvp0) < 0)
337  {
338  s = format (s, " LOOKUP FAIL!");
339  }
340  else
341  {
342  if (kvp0.value == s0 - cmp->sessions[s0->thread_index])
343  {
344  s = format (s, " OK");
345  if (verbose > 1)
346  {
347  s = format (s, " next %d prev %d", s0->next_index,
348  s0->prev_index);
349  s = format (s, " hits %d expires %.2f", s0->hits, s0->expires);
350  }
351  }
352  else
353  s = format (s, " BOGUS LOOKUP RESULT!");
354  }
355 
356  return s;
357 }
358 
359 static clib_error_t *
361  unformat_input_t * input,
362  vlib_cli_command_t * cmd)
363 {
364  ct6_main_t *cmp = &ct6_main;
365  ct6_session_t *s0;
366  int verbose = 0;
367  u8 *s = 0;
368  int i;
369 
370  if (!cmp->feature_initialized)
371  return clib_error_return (0, "ip6 connection tracking not enabled...");
372 
373  if (unformat (input, "verbose %d", &verbose))
374  ;
375  else if (unformat (input, "verbose"))
376  verbose = 1;
377 
378  for (i = 0; i < vec_len (cmp->sessions); i++)
379  {
380  s = format (s, "Thread %d: %d sessions\n", i,
381  pool_elts (cmp->sessions[i]));
382 
383  if (verbose == 0)
384  continue;
385 
386  s =
387  format (s, "%U", format_ct6_session, cmp,
388  0 /* pool */ , 0 /* header */ , verbose);
389 
390  /* *INDENT-OFF* */
391  pool_foreach (s0, cmp->sessions[i],
392  ({
393  s = format (s, "%U", format_ct6_session, cmp, i, s0, verbose);
394  }));
395  /* *INDENT-ON* */
396  }
397  vlib_cli_output (cmp->vlib_main, "%v", s);
398  vec_free (s);
399  return 0;
400 }
401 
402 /* *INDENT-OFF* */
403 VLIB_CLI_COMMAND (show_ct6_command_fn_command, static) =
404 {
405  .path = "show ip6 connection-tracker",
406  .short_help = "show ip6 connection-tracker",
407  .function = show_ct6_command_fn_command_fn,
408 };
409 /* *INDENT-ON* */
410 
411 static void
413 {
414  u64 v0, v1;
415 
416  v0 = clib_net_to_host_u64 (a->as_u64[0]);
417  v1 = clib_net_to_host_u64 (a->as_u64[1]);
418 
419  v1 += 1;
420  if (v1 == 0)
421  v0 += 1;
422  a->as_u64[0] = clib_net_to_host_u64 (v0);
423  a->as_u64[1] = clib_net_to_host_u64 (v1);
424 }
425 
426 
427 static clib_error_t *
429  unformat_input_t * input,
430  vlib_cli_command_t * cmd)
431 {
432  ct6_main_t *cmp = &ct6_main;
434  ct6_session_key_t *key0;
435  ct6_session_t *s0;
436  u8 src[16], dst[16];
437  u32 recycled = 0, created = 0;
438  int i, num_sessions = 5;
439  u32 midpt_index;
440  u8 *s = 0;
441 
442  cmp->max_sessions_per_worker = 4;
443 
445  {
446  if (unformat (input, "num-sessions %d", &num_sessions))
447  ;
448  else
449  if (unformat
450  (input, "max-sessions %d", &cmp->max_sessions_per_worker))
451  ;
452  else
453  break;
454  }
455 
456  ct6_feature_init (cmp);
457 
458  /* Set up starting src/dst addresses */
459  memset (src, 0, sizeof (src));
460  memset (dst, 0, sizeof (dst));
461 
462  src[0] = 0xdb;
463  dst[0] = 0xbe;
464 
465  src[15] = 1;
466  dst[15] = 1;
467 
468  /*
469  * See if we know about this flow.
470  * Key set up for the out2in path, the performant case
471  */
472  key0 = (ct6_session_key_t *) & kvp0;
473  memset (&kvp0, 0, sizeof (kvp0));
474 
475  for (i = 0; i < num_sessions; i++)
476  {
477  clib_memcpy_fast (&key0->src, src, sizeof (src));
478  clib_memcpy_fast (&key0->dst, dst, sizeof (dst));
479  key0->as_u64[4] = 0;
480  key0->as_u64[5] = 0;
481  key0->sport = clib_host_to_net_u16 (1234);
482  key0->dport = clib_host_to_net_u16 (4321);
483  key0->proto = 17; /* udp, fwiw */
484 
486  (cmp, &kvp0, 3.0 /* now */ , 0 /* thread index */ ,
487  &recycled, &created);
488 
489  s = format (s, "%U (%d, %d)", format_ct6_session, cmp,
490  0 /* thread index */ , s0, 1 /* verbose */ ,
491  recycled, created);
492  vlib_cli_output (vm, "%v", s);
493  vec_free (s);
495  recycled = 0;
496  created = 0;
497  }
498 
499  /* *INDENT-OFF* */
500  pool_foreach (s0, cmp->sessions[0],
501  ({
502  s = format (s, "%U", format_ct6_session, cmp, 0, s0, 1 /* verbose */);
503  }));
504  /* *INDENT-ON* */
505 
506  vlib_cli_output (vm, "\nEnd state: first index %d last index %d\n%v",
507  cmp->first_index[0], cmp->last_index[0], s);
508 
509  vec_free (s);
510 
511  midpt_index = cmp->max_sessions_per_worker / 3;
512 
513  s0 = pool_elt_at_index (cmp->sessions[0], midpt_index);
514  vlib_cli_output (vm, "\nSimulate LRU hit on session %d",
515  s0 - cmp->sessions[0]);
516 
517  ct6_update_session_hit (cmp, s0, 234.0);
518 
519  /* *INDENT-OFF* */
520  pool_foreach (s0, cmp->sessions[0],
521  ({
522  s = format (s, "%U", format_ct6_session, cmp, 0, s0, 1 /* verbose */);
523  }));
524  /* *INDENT-ON* */
525 
526  vlib_cli_output (vm, "\nEnd state: first index %d last index %d\n%v",
527  cmp->first_index[0], cmp->last_index[0], s);
528 
529  vec_free (s);
530 
531  return 0;
532 }
533 
534 /* *INDENT-OFF* */
535 VLIB_CLI_COMMAND (test_ct6_command_fn_command, static) =
536 {
537  .path = "test ip6 connection-tracker",
538  .short_help = "test ip6 connection-tracker",
539  .function = test_ct6_command_fn_command_fn,
540 };
541 /* *INDENT-ON* */
542 
543 static clib_error_t *
545 {
546  ct6_main_t *cmp = &ct6_main;
547 
549  {
550  if (unformat (input, "session-hash-buckets %u",
551  &cmp->session_hash_buckets))
552  ;
553  else if (unformat (input, "session-hash-memory %U",
555  ;
556  else if (unformat (input, "session-timeout %f",
558  ;
559  else
560  {
561  return clib_error_return (0, "unknown input '%U'",
562  format_unformat_error, input);
563  }
564  }
565  return 0;
566 }
567 
569 
570 /*
571  * fd.io coding-style-patch-verification: ON
572  *
573  * Local Variables:
574  * eval: (c-set-style "gnu")
575  * End:
576  */
#define vec_validate(V, I)
Make sure vector is long enough for given index (no header, unspecified alignment) ...
Definition: vec.h:439
u32 sw_if_index
Definition: ipsec_gre.api:37
VLIB_PLUGIN_REGISTER()
a
Definition: bitmap.h:538
vnet_main_t * vnet_get_main(void)
Definition: misc.c:47
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:428
vnet_interface_main_t interface_main
Definition: vnet.h:56
vnet_main_t * vnet_main
Definition: ct6.h:80
u64 as_u64[2]
Definition: ip6_packet.h:51
unsigned long u64
Definition: types.h:89
#define clib_memcpy_fast(a, b, c)
Definition: string.h:81
ct6_main_t ct6_main
Definition: ct6.c:53
static void setup_message_id_table(ct6_main_t *cmp, api_main_t *am)
Definition: ct6.c:236
int i
static vnet_sw_interface_t * vnet_get_sw_interface(vnet_main_t *vnm, u32 sw_if_index)
u8 * format(u8 *s, const char *fmt,...)
Definition: format.c:424
unformat_function_t unformat_vnet_sw_interface
vl_api_ip4_address_t dst
Definition: ipsec_gre.api:39
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:79
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:493
vlib_main_t * vlib_main
Definition: ct6.h:79
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:163
static void vl_api_ct6_enable_disable_t_handler(vl_api_ct6_enable_disable_t *mp)
Definition: ct6.c:193
#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:244
static clib_error_t * ct6_plugin_api_hookup(vlib_main_t *vm)
Definition: ct6.c:214
#define pool_elt_at_index(p, i)
Returns pointer to element at given index.
Definition: pool.h:514
VNET_FEATURE_INIT(ct6out2in, static)
u8 * format_ct6_session(u8 *s, va_list *args)
Definition: ct6.c:312
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:172
#define REPLY_MACRO(t)
vl_api_ip4_address_t src
Definition: ipsec_gre.api:38
u8 name[64]
Definition: memclnt.api:152
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
API main structure, used by both vpp and binary API clients.
Definition: api_common.h:202
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:129
#define BAD_SW_IF_INDEX_LABEL
#define UNFORMAT_END_OF_INPUT
Definition: format.h:144
format_function_t format_ip6_address
Definition: format.h:93
vlib_main_t * vm
Definition: buffer.c:312
#define vec_free(V)
Free vector&#39;s memory (no header).
Definition: vec.h:341
u32 * first_index
Definition: ct6.h:69
static clib_error_t * ct6_config(vlib_main_t *vm, unformat_input_t *input)
Definition: ct6.c:544
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:283
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:104
#define VLIB_CLI_COMMAND(x,...)
Definition: cli.h:155
u32 * last_index
Definition: ct6.h:70
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:360
#define foreach_ct6_plugin_api_msg
Definition: ct6.c:57
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:435
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:815
clib_bihash_48_8_t session_hash
Definition: ct6.h:64
unformat_function_t unformat_memory_size
Definition: format.h:295
static void ct6_feature_init(ct6_main_t *cmp)
Definition: ct6.c:63
u8 * format_unformat_error(u8 *s, va_list *va)
Definition: unformat.c:91
vnet_sw_interface_type_t type
Definition: interface.h:682
static u32 vlib_num_workers()
Definition: threads.h:366
static void increment_v6_address(ip6_address_t *a)
Definition: ct6.c:412
#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:486
u32 max_sessions_per_worker
Definition: ct6.h:76
api_main_t api_main
Definition: api_shared.c:35
void vlib_cli_output(vlib_main_t *vm, char *fmt,...)
Definition: cli.c:762
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:365
uword unformat(unformat_input_t *i, const char *fmt,...)
Definition: unformat.c:972
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:274
static uword unformat_check_input(unformat_input_t *i)
Definition: format.h:170
#define VALIDATE_SW_IF_INDEX(mp)
u16 vl_msg_api_get_msg_ids(const char *name, int n)
Definition: api_shared.c:880
static uword pool_elts(void *v)
Number of active elements in a pool.
Definition: pool.h:128