Initial commit of vpp code.
[vpp.git] / vpp / app / sticky_hash.c
1 /*
2  * Copyright (c) 2015 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 #include <vnet/l2/l2_classify.h>
16
17 #include <vlib/vlib.h>
18 #include <vnet/vnet.h>
19 #include <vnet/pg/pg.h>
20 #include <vnet/ip/ip.h>
21 #include <vnet/ip/ip_packet.h>
22 #include <vnet/ip/ip4_packet.h>
23 #include <vnet/ip/ip6_packet.h>
24 #include <vppinfra/error.h>
25
26 typedef struct {
27   u32 fwd_entry_index;
28   u32 rev_entry_index;
29   /* Not strictly needed, for show command */
30   u32 fib_index;
31 } sticky_hash_session_t;
32
33 typedef struct {
34   u32 cached_next_index;
35
36   /* next index added to l2_classify */
37   u32 fwd_miss_next_index;
38
39   /* session pool */
40   sticky_hash_session_t * sessions;
41
42   /* Forward and reverse data session setup buffers */
43   u8 fdata[3 * sizeof (u32x4)];
44   u8 rdata[3 * sizeof (u32x4)];
45
46   /* convenience variables */
47   vlib_main_t * vlib_main;
48   vnet_main_t * vnet_main;
49   vnet_classify_main_t * vnet_classify_main;
50   l2_classify_main_t * l2_classify_main;
51 } sticky_hash_main_t;
52
53 typedef struct {
54   /* $$$$ fill in with per-pkt trace data */ 
55   u32 next_index;
56   u32 sw_if_index;
57 } sticky_hash_miss_trace_t;
58
59 /* packet trace format function */
60 static u8 * format_sticky_hash_miss_trace (u8 * s, va_list * args)
61 {
62   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
63   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
64   sticky_hash_miss_trace_t * t = va_arg (*args, sticky_hash_miss_trace_t *);
65   
66   s = format (s, "STICKY_HASH_MISS: sw_if_index %d",
67       t->sw_if_index);
68   return s;
69 }
70
71 typedef CLIB_PACKED(struct {
72   ethernet_header_t eh;
73   ip4_header_t ip;
74 }) classify_data_or_mask_t;
75
76 sticky_hash_main_t sticky_hash_main;
77
78 vlib_node_registration_t sticky_hash_miss_node;
79
80 #define foreach_sticky_hash_miss_error \
81 _(MISSES, "forward flow classify misses")
82
83 typedef enum {
84 #define _(sym,str) STICKY_HASH_MISS_ERROR_##sym,
85   foreach_sticky_hash_miss_error
86 #undef _
87   STICKY_HASH_MISS_N_ERROR,
88 } sticky_hash_miss_error_t;
89
90 static char * sticky_hash_miss_error_strings[] = {
91 #define _(sym,string) string,
92   foreach_sticky_hash_miss_error
93 #undef _
94 };
95
96 /* 
97  * To drop a pkt and increment one of the previous counters:
98  * 
99  * set b0->error = error_node->errors[STICKY_HASH_MISS_ERROR_EXAMPLE];
100  * set next0 to a disposition index bound to "error-drop".
101  *
102  * To manually increment the specific counter STICKY_HASH_MISS_ERROR_EXAMPLE:
103  *
104  *  vlib_node_t *n = vlib_get_node (vm, sticky_hash_miss.index);
105  *  u32 node_counter_base_index = n->error_heap_index;
106  *  vlib_error_main_t * em = &vm->error_main;
107  *  em->counters[node_counter_base_index + STICKY_HASH_MISS_ERROR_EXAMPLE] += 1;
108  * 
109  */
110
111 typedef enum {
112   STICKY_HASH_MISS_NEXT_IP4_INPUT,
113   STICKY_HASH_MISS_N_NEXT,
114 } sticky_hash_miss_next_t;
115
116 static uword
117 sticky_hash_miss_node_fn (vlib_main_t * vm,
118                   vlib_node_runtime_t * node,
119                   vlib_frame_t * frame)
120 {
121   u32 n_left_from, * from, * to_next;
122   sticky_hash_miss_next_t next_index;
123   sticky_hash_main_t * mp = &sticky_hash_main;
124   vlib_node_t *n = vlib_get_node (vm, sticky_hash_miss_node.index);
125   u32 node_counter_base_index = n->error_heap_index;
126   vlib_error_main_t * em = &vm->error_main;
127   vnet_classify_main_t * cm = mp->vnet_classify_main;
128   ip4_main_t * im = &ip4_main;
129
130   from = vlib_frame_vector_args (frame);
131   n_left_from = frame->n_vectors;
132   next_index = node->cached_next_index;
133
134   while (n_left_from > 0)
135     {
136       u32 n_left_to_next;
137
138       vlib_get_next_frame (vm, node, next_index,
139                            to_next, n_left_to_next);
140       
141       while (n_left_from > 0 && n_left_to_next > 0)
142         {
143           u32 bi0;
144           vlib_buffer_t * b0;
145           u32 next0;
146           u32 sw_if_index0;
147           u32 fib_index0, ft_index0, rt_index0;
148           vnet_classify_table_3_t * ft0, * rt0;
149           vnet_classify_entry_3_t * fe0, * re0;
150           classify_data_or_mask_t * h0;
151           u8 was_found0;
152           ip4_fib_t * fib0;
153           sticky_hash_session_t * s;
154           u32 tmp;
155           
156           /* speculatively enqueue b0 to the current next frame */
157           bi0 = from[0];
158           to_next[0] = bi0;
159           from += 1;
160           to_next += 1;
161           n_left_from -= 1;
162           n_left_to_next -= 1;
163
164           b0 = vlib_get_buffer (vm, bi0);
165
166           sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
167           next0 = mp->cached_next_index;
168
169           h0 = vlib_buffer_get_current (b0);
170
171           /* Add forward and reverse entries for this flow */
172           memcpy (mp->fdata, h0, sizeof (mp->fdata));
173           memcpy (mp->rdata, h0, sizeof (mp->rdata));
174
175           h0 = (classify_data_or_mask_t *)(mp->rdata);
176
177           /* swap src + dst addresses to form reverse data */
178           tmp = h0->ip.src_address.as_u32;
179           h0->ip.src_address.as_u32 = h0->ip.dst_address.as_u32;
180           h0->ip.dst_address.as_u32 = tmp;
181
182           /* dig up fwd + rev tables */
183           fib_index0 = vec_elt (im->fib_index_by_sw_if_index, sw_if_index0);
184           fib0 = vec_elt_at_index (im->fibs, fib_index0);  
185
186           ft_index0 = fib0->fwd_classify_table_index;
187           rt_index0 = fib0->rev_classify_table_index;
188           
189           ft0 = (vnet_classify_table_3_t *)
190               pool_elt_at_index (cm->tables, ft_index0);
191           rt0 = (vnet_classify_table_3_t *)
192               pool_elt_at_index (cm->tables, rt_index0);
193
194           fe0 = vnet_classify_find_or_add_entry_3 (ft0, mp->fdata, &was_found0);
195           fe0->next_index = L2_CLASSIFY_NEXT_IP4_INPUT;
196           fe0->advance = sizeof (ethernet_header_t);
197           
198           re0 = vnet_classify_find_or_add_entry_3 (rt0, mp->rdata, 0);
199           re0->next_index = L2_CLASSIFY_NEXT_IP4_INPUT; /* $$$ FIXME */
200           re0->advance = sizeof (ethernet_header_t);
201
202           /* Note: we could get a whole vector of misses for the same sess */
203           if (was_found0 == 0)
204             {
205               pool_get (mp->sessions, s);
206               
207               fe0->opaque_index = s - mp->sessions;
208               re0->opaque_index = s - mp->sessions;
209               
210               s->fwd_entry_index = fe0 - ft0->entries;
211               s->rev_entry_index = re0 - rt0->entries;
212               s->fib_index = fib_index0;
213             }
214
215           if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) 
216                             && (b0->flags & VLIB_BUFFER_IS_TRACED))) {
217             sticky_hash_miss_trace_t *t = 
218                vlib_add_trace (vm, node, b0, sizeof (*t));
219             t->sw_if_index = sw_if_index0;
220             t->next_index = next0;
221             }
222             
223           em->counters[node_counter_base_index + STICKY_HASH_MISS_ERROR_MISSES]
224             += 1;
225
226           vlib_buffer_advance (b0, sizeof (ethernet_header_t));
227
228           /* verify speculative enqueue, maybe switch current next frame */
229           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
230                                            to_next, n_left_to_next,
231                                            bi0, next0);
232         }
233
234       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
235     }
236
237   return frame->n_vectors;
238 }
239
240 VLIB_REGISTER_NODE (sticky_hash_miss_node) = {
241   .function = sticky_hash_miss_node_fn,
242   .name = "sticky-hash-miss",
243   .vector_size = sizeof (u32),
244   .format_trace = format_sticky_hash_miss_trace,
245   .type = VLIB_NODE_TYPE_INTERNAL,
246   
247   .n_errors = ARRAY_LEN(sticky_hash_miss_error_strings),
248   .error_strings = sticky_hash_miss_error_strings,
249
250   .n_next_nodes = STICKY_HASH_MISS_N_NEXT,
251
252   /* edit / add dispositions here */
253   .next_nodes = {
254         [STICKY_HASH_MISS_NEXT_IP4_INPUT] = "ip4-input",
255   },
256 };
257
258 clib_error_t *sticky_hash_miss_init (vlib_main_t *vm)
259 {
260   sticky_hash_main_t * mp = &sticky_hash_main;
261     
262   mp->vlib_main = vm;
263   mp->vnet_main = vnet_get_main();
264   mp->vnet_classify_main = &vnet_classify_main;
265   mp->l2_classify_main = &l2_classify_main;
266
267   return 0;
268 }
269
270 VLIB_INIT_FUNCTION (sticky_hash_miss_init);
271
272 static int ip4_sticky_hash_enable_disable 
273 (sticky_hash_main_t * mp,
274  u32 fwd_sw_if_index, u8 * fwd_mask, 
275  u32 rev_sw_if_index, u8 * rev_mask, 
276  u32 nbuckets, int enable_disable)
277 {
278   ip4_main_t * im = &ip4_main;
279   u32 fib_index;
280   ip4_fib_t * fib;
281   vnet_classify_main_t * cm = mp->vnet_classify_main;
282   l2_classify_main_t * l2cm = mp->l2_classify_main;
283   vnet_classify_table_3_t * ft, * rt;
284   
285   fib_index = vec_elt (im->fib_index_by_sw_if_index, fwd_sw_if_index);
286   fib = vec_elt_at_index (im->fibs, fib_index);  
287   
288   if (fib->fwd_classify_table_index == ~0)
289     {
290       /* Set up forward table */
291       ft = (vnet_classify_table_3_t *) 
292         vnet_classify_new_table (cm, fwd_mask, nbuckets, 
293                                  0 /* skip */, 3 /* match */);
294       fib->fwd_classify_table_index 
295         = ft - (vnet_classify_table_3_t *) cm->tables;
296       mp->fwd_miss_next_index = 
297         vlib_node_add_next (mp->vlib_main, l2_classify_node.index,
298                             sticky_hash_miss_node.index);
299       ft->miss_next_index = mp->fwd_miss_next_index;
300
301       /* Set up reverse table */
302       rt = (vnet_classify_table_3_t *) 
303         vnet_classify_new_table (cm, rev_mask, nbuckets,
304                                  0 /* skip */, 3 /* match */);
305       fib->rev_classify_table_index 
306         = rt - (vnet_classify_table_3_t *) cm->tables;
307     }
308
309   vec_validate 
310     (l2cm->classify_table_index_by_sw_if_index[L2_CLASSIFY_TABLE_IP4], 
311      fwd_sw_if_index);
312   
313   vec_validate 
314     (l2cm->classify_table_index_by_sw_if_index[L2_CLASSIFY_TABLE_IP6], 
315      fwd_sw_if_index);
316   
317   vec_validate 
318     (l2cm->classify_table_index_by_sw_if_index[L2_CLASSIFY_TABLE_OTHER], 
319      fwd_sw_if_index);
320   
321   l2cm->classify_table_index_by_sw_if_index[L2_CLASSIFY_TABLE_IP4]
322     [fwd_sw_if_index] = fib->fwd_classify_table_index;
323
324   l2cm->classify_table_index_by_sw_if_index[L2_CLASSIFY_TABLE_IP6]
325     [fwd_sw_if_index] = ~0;
326   
327   l2cm->classify_table_index_by_sw_if_index[L2_CLASSIFY_TABLE_OTHER]
328     [fwd_sw_if_index] = ~0;
329   
330
331   vec_validate 
332     (l2cm->classify_table_index_by_sw_if_index[L2_CLASSIFY_TABLE_IP4], 
333      rev_sw_if_index);
334   
335   vec_validate 
336     (l2cm->classify_table_index_by_sw_if_index[L2_CLASSIFY_TABLE_IP6], 
337      rev_sw_if_index);
338   
339   vec_validate 
340     (l2cm->classify_table_index_by_sw_if_index[L2_CLASSIFY_TABLE_OTHER], 
341      rev_sw_if_index);
342
343   
344   l2cm->classify_table_index_by_sw_if_index[L2_CLASSIFY_TABLE_IP4]
345     [rev_sw_if_index] = fib->rev_classify_table_index;
346   
347   l2cm->classify_table_index_by_sw_if_index[L2_CLASSIFY_TABLE_IP6]
348     [rev_sw_if_index] = ~0;
349   
350   l2cm->classify_table_index_by_sw_if_index[L2_CLASSIFY_TABLE_OTHER]
351     [rev_sw_if_index] = ~0;
352   
353   vnet_l2_classify_enable_disable (fwd_sw_if_index, enable_disable);
354   vnet_l2_classify_enable_disable (rev_sw_if_index, enable_disable);
355   return 0;
356 }
357
358 static clib_error_t *
359 ip4_sticky_hash_init_command_fn (vlib_main_t * vm,
360                                  unformat_input_t * input,
361                                  vlib_cli_command_t * cmd)
362 {
363   u32 fwd_sw_if_index = ~0, rev_sw_if_index = ~0;
364   int enable_disable = 1;
365   u32 nbuckets = 2;
366   int rv;
367   sticky_hash_main_t * mp = &sticky_hash_main;
368   classify_data_or_mask_t fwd_mask, rev_mask;
369   u8 * fm = 0, * rm = 0;
370   
371   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
372     if (unformat (input, "fwd %U", unformat_vnet_sw_interface, mp->vnet_main,
373                   &fwd_sw_if_index))
374       ;
375     if (unformat (input, "rev %U", unformat_vnet_sw_interface, mp->vnet_main,
376                   &rev_sw_if_index))
377       ;
378     else if (unformat (input, "nbuckets %d", &nbuckets))
379       ;
380     else if (unformat (input, "disable"))
381       enable_disable = 0;
382
383     else break;
384   }
385
386   nbuckets = 1<<max_log2(nbuckets);
387
388   if (fwd_sw_if_index == ~0)
389     return clib_error_return (0, "fwd interface not set");
390
391   if (rev_sw_if_index == ~0)
392     return clib_error_return (0, "rev interface not set");
393
394   if (!is_pow2(nbuckets))
395     return clib_error_return (0, "nbuckets %d not a power of 2", nbuckets);
396   
397   ASSERT(sizeof(fwd_mask) <= 3 * sizeof (u32x4));
398
399   /* Mask on src/dst address, depending on direction */
400   memset(&fwd_mask, 0, sizeof (fwd_mask));
401   memset (&fwd_mask.ip.src_address, 0xff, 4);
402
403   memset(&rev_mask, 0, sizeof (rev_mask));
404   memset (&rev_mask.ip.dst_address, 0xff, 4);
405
406   vec_validate (fm, 3 * sizeof(u32x4) - 1);
407   vec_validate (rm, 3 * sizeof(u32x4) - 1);
408
409   memcpy (fm, &fwd_mask, sizeof (fwd_mask));
410   memcpy (rm, &rev_mask, sizeof (rev_mask));
411   
412   rv = ip4_sticky_hash_enable_disable (mp, fwd_sw_if_index, fm, 
413                                        rev_sw_if_index, rm,
414                                        nbuckets, enable_disable);
415
416   vec_free (fm);
417   vec_free (rm);
418   switch (rv)
419     {
420     case 0:
421       return 0;
422
423     default:
424       return clib_error_return (0, "ip4_sticky_hash_enable_disable returned %d",
425                                 rv);
426     }
427
428   return 0;
429 }
430
431 VLIB_CLI_COMMAND (sticky_hash_init_command, static) = {
432     .path = "ip sticky classify",
433     .short_help = "ip sticky classify fwd <intfc> rev <intfc> " 
434     "[nbuckets <nn>][disable]",
435     .function = ip4_sticky_hash_init_command_fn,
436 };
437
438
439 u8 * format_sticky_hash_session (u8 * s, va_list * args)
440 {
441   sticky_hash_main_t * mp = va_arg(*args, sticky_hash_main_t *);
442   sticky_hash_session_t * session = va_arg(*args, sticky_hash_session_t *);
443   vnet_classify_table_3_t * t;
444   vnet_classify_entry_3_t * e;
445   ip4_main_t * im = &ip4_main;
446   vnet_classify_main_t * cm = mp->vnet_classify_main;
447   ip4_fib_t * fib;
448   classify_data_or_mask_t * match;
449
450   fib = vec_elt_at_index (im->fibs, session->fib_index);
451
452   t = (vnet_classify_table_3_t *) 
453     pool_elt_at_index (cm->tables, fib->fwd_classify_table_index);
454   e = pool_elt_at_index (t->entries, session->fwd_entry_index);
455   match = (classify_data_or_mask_t *)(e->key);
456
457   s = format 
458       (s, 
459        "[%6d] fwd src %U next index %d session %d fib %d\n"
460        "         hits %lld last-heard %.6f\n",
461        e - t->entries, 
462        format_ip4_address, &match->ip.src_address,
463        e->next_index, e->opaque_index, fib->table_id,
464        e->hits, e->last_heard);
465   
466   if (e->opaque_index != session - mp->sessions)
467     s = format (s, "WARNING: forward session index mismatch!\n");
468
469   t = (vnet_classify_table_3_t *)
470     pool_elt_at_index (cm->tables, fib->rev_classify_table_index);
471   e = pool_elt_at_index (t->entries, session->rev_entry_index);
472   match = (classify_data_or_mask_t *)(e->key);
473   
474   s = format 
475       (s, 
476        "[%6d] rev dst %U next index %d session %d\n"
477        "         hits %lld last-heard %.6f\n",
478        e - t->entries, 
479        format_ip4_address, &match->ip.dst_address,
480        e->next_index, e->opaque_index, e->hits, e->last_heard);
481
482   if (e->opaque_index != session - mp->sessions)
483     s = format (s, "WARNING: reverse session index mismatch!\n");
484   s = format (s, "---------\n");
485
486   return s;
487 }
488
489 static clib_error_t *
490 show_ip4_sticky_hash_command_fn (vlib_main_t * vm,
491                                  unformat_input_t * input,
492                                  vlib_cli_command_t * cmd)
493 {
494   sticky_hash_main_t * mp = &sticky_hash_main;
495   sticky_hash_session_t * s;
496   int verbose = 0;
497   int dump_classifier_tables = 0;
498   ip4_fib_t * fib;
499   ip4_main_t * im4 = &ip4_main;
500   vnet_classify_main_t * cm = mp->vnet_classify_main;
501   
502   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
503     if (unformat (input, "verbose"))
504       verbose = 1;
505     else if (unformat (input, "dump-tables")
506              || unformat (input, "dump-classifier-tables"))
507       dump_classifier_tables = 1;
508     else
509       break;
510   }
511
512   if (pool_elts (mp->sessions) == 0)
513     vlib_cli_output (vm, "No ip sticky hash sessions");
514   
515
516   vlib_cli_output (vm, "%d active sessions\n",
517                    pool_elts (mp->sessions));
518
519   vec_foreach (fib, im4->fibs)
520     {
521       if (fib->fwd_classify_table_index != ~0)
522           vlib_cli_output (vm, "fib %d fwd table: \n%U", 
523                            fib->table_id, 
524                            format_classify_table, 
525                            cm,
526                            pool_elt_at_index 
527                            (cm->tables, fib->fwd_classify_table_index),
528                            dump_classifier_tables);
529       if (fib->rev_classify_table_index != ~0)
530           vlib_cli_output (vm, "fib %d rev table: \n%U", 
531                            fib->table_id, 
532                            format_classify_table, 
533                            cm,
534                            pool_elt_at_index 
535                            (cm->tables, fib->rev_classify_table_index),
536                            dump_classifier_tables);
537     }
538
539   if (verbose)
540     {
541       pool_foreach (s, mp->sessions, 
542       ({
543         vlib_cli_output (vm, "%U", format_sticky_hash_session, mp, s);
544       }));
545     }
546   return 0;
547 }
548
549 VLIB_CLI_COMMAND (show_sticky_hash_command, static) = {
550     .path = "show sticky classify",
551     .short_help = "Display sticky classifier tables", 
552     .function = show_ip4_sticky_hash_command_fn,
553 };
554