Move java,lua api and remaining plugins to src/
[vpp.git] / src / plugins / acl / l2sess_node.c
1 /*
2  * Copyright (c) 2016 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 <netinet/in.h>
16 #include <vlib/vlib.h>
17 #include <vnet/vnet.h>
18 #include <vnet/pg/pg.h>
19 #include <vppinfra/error.h>
20 #include <acl/l2sess.h>
21 #include <vnet/l2/l2_classify.h>
22
23
24 typedef struct
25 {
26   u32 next_index;
27   u32 sw_if_index;
28   u32 trace_flags;
29   u32 session_tables[2];
30   u32 session_nexts[2];
31   u8 l4_proto;
32 } l2sess_trace_t;
33
34 /* packet trace format function */
35
36 #define _(node_name, node_var, is_out, is_ip6, is_track) \
37 static u8 * format_## node_var ##_trace (u8 * s, va_list * args)      \
38 {  \
39   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); \
40   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); \
41   l2sess_trace_t * t = va_arg (*args, l2sess_trace_t *); \
42  \
43   s = format (s, node_name ": sw_if_index %d, next index %d trace_flags %08x L4 proto %d\n" \
44                            "                 tables [ %d, %d ] nexts [ %d, %d ]", \
45               t->sw_if_index, t->next_index, t->trace_flags, t->l4_proto, \
46               t->session_tables[0], t->session_tables[1], \
47               t->session_nexts[0], t->session_nexts[1]); \
48   return s; \
49 }
50 foreach_l2sess_node
51 #undef _
52 #define foreach_l2sess_error \
53 _(SWAPPED, "Mac swap packets processed")
54   typedef enum
55 {
56 #define _(sym,str) L2SESS_ERROR_##sym,
57   foreach_l2sess_error
58 #undef _
59     L2SESS_N_ERROR,
60 } l2sess_error_t;
61
62 static char *l2sess_error_strings[] = {
63 #define _(sym,string) string,
64   foreach_l2sess_error
65 #undef _
66 };
67
68 typedef enum
69 {
70   L2SESS_NEXT_DROP,
71   L2SESS_N_NEXT,
72 } l2sess_next_t;
73
74 u8
75 l2sess_get_l4_proto (vlib_buffer_t * b0, int node_is_ip6)
76 {
77   u8 proto;
78   int proto_offset;
79   if (node_is_ip6)
80     {
81       proto_offset = 20;
82     }
83   else
84     {
85       proto_offset = 23;
86     }
87   proto = *((u8 *) vlib_buffer_get_current (b0) + proto_offset);
88   return proto;
89 }
90
91
92 u8
93 l2sess_get_tcp_flags (vlib_buffer_t * b0, int node_is_ip6)
94 {
95   u8 flags;
96   int flags_offset;
97   if (node_is_ip6)
98     {
99       flags_offset = 14 + 40 + 13;      /* FIXME: no extension headers assumed */
100     }
101   else
102     {
103       flags_offset = 14 + 20 + 13;
104     }
105   flags = *((u8 *) vlib_buffer_get_current (b0) + flags_offset);
106   return flags;
107 }
108
109 static inline int
110 l4_tcp_or_udp (u8 proto)
111 {
112   return ((proto == 6) || (proto == 17));
113 }
114
115 void
116 l2sess_get_session_tables (l2sess_main_t * sm, u32 sw_if_index,
117                            int node_is_out, int node_is_ip6, u8 l4_proto,
118                            u32 * session_tables)
119 {
120 /*
121  * Based on the direction, l3 and l4 protocol, fill a u32[2] array:
122  * [0] is index for the "direct match" path, [1] is for "mirrored match".
123  * Store the indices of the tables to add the session to in session_tables[]
124  */
125   l2_output_classify_main_t *l2om = &l2_output_classify_main;
126   l2_input_classify_main_t *l2im = &l2_input_classify_main;
127
128   u32 output_table_index;
129   u32 input_table_index;
130
131   if (!l4_tcp_or_udp (l4_proto))
132     {
133       return;
134     }
135
136   if (node_is_ip6)
137     {
138       vec_validate_init_empty (l2im->
139                                classify_table_index_by_sw_if_index
140                                [L2_INPUT_CLASSIFY_TABLE_IP6], sw_if_index,
141                                ~0);
142       input_table_index =
143         l2im->
144         classify_table_index_by_sw_if_index[L2_INPUT_CLASSIFY_TABLE_IP6]
145         [sw_if_index];
146       vec_validate_init_empty (l2om->
147                                classify_table_index_by_sw_if_index
148                                [L2_OUTPUT_CLASSIFY_TABLE_IP6], sw_if_index,
149                                ~0);
150       output_table_index =
151         l2om->
152         classify_table_index_by_sw_if_index[L2_OUTPUT_CLASSIFY_TABLE_IP6]
153         [sw_if_index];
154     }
155   else
156     {
157       vec_validate_init_empty (l2im->
158                                classify_table_index_by_sw_if_index
159                                [L2_INPUT_CLASSIFY_TABLE_IP4], sw_if_index,
160                                ~0);
161       input_table_index =
162         l2im->
163         classify_table_index_by_sw_if_index[L2_INPUT_CLASSIFY_TABLE_IP4]
164         [sw_if_index];
165       vec_validate_init_empty (l2om->
166                                classify_table_index_by_sw_if_index
167                                [L2_OUTPUT_CLASSIFY_TABLE_IP4], sw_if_index,
168                                ~0);
169       output_table_index =
170         l2om->
171         classify_table_index_by_sw_if_index[L2_OUTPUT_CLASSIFY_TABLE_IP4]
172         [sw_if_index];
173     }
174
175   if (node_is_out)
176     {
177       session_tables[0] = output_table_index;
178       session_tables[1] = input_table_index;
179     }
180   else
181     {
182       session_tables[0] = input_table_index;
183       session_tables[1] = output_table_index;
184     }
185 }
186
187 void
188 l2sess_get_session_nexts (l2sess_main_t * sm, u32 sw_if_index,
189                           int node_is_out, int node_is_ip6, u8 l4_proto,
190                           u32 * session_nexts)
191 {
192 /*
193  * Based on the direction, l3 and l4 protocol, fill a u32[2] array:
194  * [0] is the index for the "direct match" path, [1] is for "mirrored match".
195  * Store the match_next_index in session_nexts[] for a new session entry which is being added to session tables.
196  */
197   u32 input_node_index;
198   u32 output_node_index;
199
200   if (!l4_tcp_or_udp (l4_proto))
201     {
202       return;
203     }
204
205   input_node_index =
206     sm->next_slot_track_node_by_is_ip6_is_out[node_is_ip6][0];
207   output_node_index =
208     sm->next_slot_track_node_by_is_ip6_is_out[node_is_ip6][1];
209
210   if (node_is_out)
211     {
212       session_nexts[0] = output_node_index;
213       session_nexts[1] = input_node_index;
214     }
215   else
216     {
217       session_nexts[0] = input_node_index;
218       session_nexts[1] = output_node_index;
219     }
220 }
221
222
223 static inline void
224 swap_bytes (vlib_buffer_t * b0, int off_a, int off_b, int nbytes)
225 {
226   u8 tmp;
227   u8 *pa = vlib_buffer_get_current (b0) + off_a;
228   u8 *pb = vlib_buffer_get_current (b0) + off_b;
229   while (nbytes--)
230     {
231       tmp = *pa;
232       *pa++ = *pb;
233       *pb++ = tmp;
234     }
235 }
236
237 /*
238  * This quite pro[bv]ably is a terrible idea performance wise. Moreso doing it twice.
239  * Would having a long (ish) chunk of memory work better for this ?
240  * We will see when we get to the performance of this.
241  */
242 void
243 l2sess_flip_l3l4_fields (vlib_buffer_t * b0, int node_is_ip6, u8 l4_proto)
244 {
245   if (!l4_tcp_or_udp (l4_proto))
246     {
247       return;
248     }
249   if (node_is_ip6)
250     {
251       swap_bytes (b0, 22, 38, 16);      /* L3 */
252       swap_bytes (b0, 54, 56, 2);       /* L4 (when no EH!) */
253     }
254   else
255     {
256       swap_bytes (b0, 26, 30, 4);       /* L3 */
257       swap_bytes (b0, 34, 36, 2);       /* L4 */
258     }
259 }
260
261 void
262 l2sess_add_session (vlib_buffer_t * b0, int node_is_out, int node_is_ip6,
263                     u32 session_table, u32 session_match_next,
264                     u32 opaque_index)
265 {
266   vnet_classify_main_t *cm = &vnet_classify_main;
267   u32 action = 0;
268   u32 metadata = 0;
269
270 #ifdef DEBUG_SESSIONS
271   printf ("Adding session to table %d with next %d\n", session_table,
272           session_match_next);
273 #endif
274   vnet_classify_add_del_session (cm, session_table,
275                                  vlib_buffer_get_current (b0),
276                                  session_match_next, opaque_index, 0, action,
277                                  metadata, 1);
278 }
279
280
281
282 static void *
283 get_ptr_to_offset (vlib_buffer_t * b0, int offset)
284 {
285   u8 *p = vlib_buffer_get_current (b0) + offset;
286   return p;
287 }
288
289
290 /*
291  * FIXME: Hardcoded offsets are ugly, although if casting to structs one
292  * would need to take care about alignment.. So let's for now be naive and simple.
293  */
294
295 void
296 session_store_ip4_l3l4_info (vlib_buffer_t * b0, l2s_session_t * sess,
297                              int node_is_out)
298 {
299   clib_memcpy (&sess->side[1 - node_is_out].addr.ip4,
300                get_ptr_to_offset (b0, 26), 4);
301   clib_memcpy (&sess->side[node_is_out].addr.ip4, get_ptr_to_offset (b0, 30),
302                4);
303   sess->side[1 - node_is_out].port =
304     ntohs (*(u16 *) get_ptr_to_offset (b0, 34));
305   sess->side[node_is_out].port = ntohs (*(u16 *) get_ptr_to_offset (b0, 36));
306 }
307
308 void
309 session_store_ip6_l3l4_info (vlib_buffer_t * b0, l2s_session_t * sess,
310                              int node_is_out)
311 {
312   clib_memcpy (&sess->side[1 - node_is_out].addr.ip6,
313                get_ptr_to_offset (b0, 22), 16);
314   clib_memcpy (&sess->side[node_is_out].addr.ip4, get_ptr_to_offset (b0, 38),
315                16);
316   sess->side[1 - node_is_out].port =
317     ntohs (*(u16 *) get_ptr_to_offset (b0, 54));
318   sess->side[node_is_out].port = ntohs (*(u16 *) get_ptr_to_offset (b0, 56));
319 }
320
321 static void
322 build_match_from_session (l2sess_main_t * sm, u8 * match,
323                           l2s_session_t * sess, int is_out)
324 {
325   if (sess->is_ip6)
326     {
327       match[20] = sess->l4_proto;
328       clib_memcpy (&match[22], &sess->side[1 - is_out].addr.ip6, 16);
329       clib_memcpy (&match[38], &sess->side[is_out].addr.ip4, 16);
330       *(u16 *) & match[54] = htons (sess->side[1 - is_out].port);
331       *(u16 *) & match[56] = htons (sess->side[is_out].port);
332     }
333   else
334     {
335       match[23] = sess->l4_proto;
336       clib_memcpy (&match[26], &sess->side[1 - is_out].addr.ip6, 4);
337       clib_memcpy (&match[30], &sess->side[is_out].addr.ip4, 4);
338       *(u16 *) & match[34] = htons (sess->side[1 - is_out].port);
339       *(u16 *) & match[36] = htons (sess->side[is_out].port);
340     }
341 }
342
343 static void
344 delete_session (l2sess_main_t * sm, u32 sw_if_index, u32 session_index)
345 {
346   vnet_classify_main_t *cm = &vnet_classify_main;
347   u8 match[5 * 16];             /* For building the mock of the packet to delete the classifier session */
348   u32 session_tables[2] = { ~0, ~0 };
349   l2s_session_t *sess = sm->sessions + session_index;
350   if (pool_is_free (sm->sessions, sess))
351     {
352       sm->counter_attempted_delete_free_session++;
353       return;
354     }
355   l2sess_get_session_tables (sm, sw_if_index, 0, sess->is_ip6, sess->l4_proto,
356                              session_tables);
357   if (session_tables[1] != ~0)
358     {
359       build_match_from_session (sm, match, sess, 1);
360       vnet_classify_add_del_session (cm, session_tables[1], match, 0, 0, 0, 0,
361                                      0, 0);
362     }
363   if (session_tables[1] != ~0)
364     {
365       build_match_from_session (sm, match, sess, 1);
366       vnet_classify_add_del_session (cm, session_tables[1], match, 0, 0, 0, 0,
367                                      0, 0);
368     }
369   pool_put (sm->sessions, sess);
370 }
371
372 static void
373 udp_session_account_buffer (vlib_buffer_t * b0, l2s_session_t * s,
374                             int which_side, u64 now)
375 {
376   l2s_session_side_t *ss = &s->side[which_side];
377   ss->active_time = now;
378   ss->n_packets++;
379   ss->n_bytes += b0->current_data + b0->current_length;
380 }
381
382 static inline u64
383 udp_session_get_timeout (l2sess_main_t * sm, l2s_session_t * sess, u64 now)
384 {
385   return (sm->udp_session_idle_timeout);
386 }
387
388 static void
389 tcp_session_account_buffer (vlib_buffer_t * b0, l2s_session_t * s,
390                             int which_side, u64 now)
391 {
392   l2s_session_side_t *ss = &s->side[which_side];
393   ss->active_time = now;
394   ss->n_packets++;
395   ss->n_bytes += b0->current_data + b0->current_length;
396   /* Very very lightweight TCP state tracking: just record which flags were seen */
397   s->tcp_flags_seen |=
398     l2sess_get_tcp_flags (b0, s->is_ip6) << (8 * which_side);
399 }
400
401 /*
402  * Since we are tracking for the purposes of timing the sessions out,
403  * we mostly care about two states: established (maximize the idle timeouts)
404  * and transient (halfopen/halfclosed/reset) - we need to have a reasonably short timeout to
405  * quickly get rid of sessions but not short enough to violate the TCP specs.
406  */
407
408 static inline u64
409 tcp_session_get_timeout (l2sess_main_t * sm, l2s_session_t * sess, u64 now)
410 {
411   /* seen both SYNs and ACKs but not FINs means we are in establshed state */
412   u16 masked_flags =
413     sess->tcp_flags_seen & ((TCP_FLAGS_RSTFINACKSYN << 8) +
414                             TCP_FLAGS_RSTFINACKSYN);
415   if (((TCP_FLAGS_ACKSYN << 8) + TCP_FLAGS_ACKSYN) == masked_flags)
416     {
417       return (sm->tcp_session_idle_timeout);
418     }
419   else
420     {
421       return (sm->tcp_session_transient_timeout);
422     }
423 }
424
425 static inline u64
426 session_get_timeout (l2sess_main_t * sm, l2s_session_t * sess, u64 now)
427 {
428   u64 timeout;
429
430   switch (sess->l4_proto)
431     {
432     case 6:
433       timeout = tcp_session_get_timeout (sm, sess, now);
434       break;
435     case 17:
436       timeout = udp_session_get_timeout (sm, sess, now);
437       break;
438     default:
439       timeout = 0;
440     }
441
442   return timeout;
443 }
444
445 static inline u64
446 get_session_last_active_time(l2s_session_t * sess)
447 {
448   u64 last_active =
449     sess->side[0].active_time >
450     sess->side[1].active_time ? sess->side[0].active_time : sess->side[1].
451     active_time;
452   return last_active;
453 }
454
455 static int
456 session_is_alive (l2sess_main_t * sm, l2s_session_t * sess, u64 now, u64 *last_active_cache)
457 {
458   u64 last_active = get_session_last_active_time(sess);
459   u64 timeout = session_get_timeout (sm, sess, now);
460   int is_alive = ((now - last_active) < timeout);
461   if (last_active_cache)
462     *last_active_cache = last_active;
463   return is_alive;
464 }
465
466 static void
467 check_idle_sessions (l2sess_main_t * sm, u32 sw_if_index, u64 now)
468 {
469   sm->timer_wheel_next_expiring_time = 0;
470   sm->data_from_advancing_timing_wheel
471     =
472     timing_wheel_advance (&sm->timing_wheel, now,
473                           sm->data_from_advancing_timing_wheel,
474                           &sm->timer_wheel_next_expiring_time);
475 #ifdef DEBUG_SESSIONS_VERBOSE
476   {
477     clib_time_t *ct = &sm->vlib_main->clib_time;
478     f64 ctime;
479     ctime = now * ct->seconds_per_clock;
480     clib_warning ("Now        : %U", format_time_interval, "h:m:s:u", ctime);
481     ctime = sm->timer_wheel_next_expiring_time * ct->seconds_per_clock;
482     clib_warning ("Next expire: %U", format_time_interval, "h:m:s:u", ctime);
483     clib_warning ("Expired items: %d",
484                   (int) vec_len (sm->data_from_advancing_timing_wheel));
485   }
486 #endif
487
488   sm->timer_wheel_next_expiring_time = now + sm->timer_wheel_tick;
489   if (PREDICT_FALSE ( 0 == sm->data_from_advancing_timing_wheel )) {
490     return;
491   }
492
493   if (PREDICT_FALSE (_vec_len (sm->data_from_advancing_timing_wheel) > 0))
494     {
495       uword i;
496       for (i = 0; i < _vec_len (sm->data_from_advancing_timing_wheel); i++)
497         {
498           u32 session_index = sm->data_from_advancing_timing_wheel[i];
499           if (!pool_is_free_index (sm->sessions, session_index))
500             {
501               l2s_session_t *sess = sm->sessions + session_index;
502               u64 last_active;
503               if (session_is_alive (sm, sess, now, &last_active))
504                 {
505 #ifdef DEBUG_SESSIONS
506               clib_warning ("Restarting timer for session %d", (int) session_index);
507 #endif
508                     /* Pretend we did this in the past, at last_active moment */
509                     timing_wheel_insert (&sm->timing_wheel,
510                                          last_active + session_get_timeout (sm, sess,
511                                                                     last_active),
512                                          session_index);
513                 }
514               else
515                 {
516 #ifdef DEBUG_SESSIONS
517               clib_warning ("Deleting session %d", (int) session_index);
518 #endif
519               delete_session (sm, sw_if_index, session_index);
520                 }
521             }
522         }
523       _vec_len (sm->data_from_advancing_timing_wheel) = 0;
524     }
525 }
526
527 static uword
528 l2sess_node_fn (vlib_main_t * vm,
529                 vlib_node_runtime_t * node, vlib_frame_t * frame)
530 {
531   u32 n_left_from, *from, *to_next;
532   l2sess_next_t next_index;
533   u32 pkts_swapped = 0;
534   u32 cached_sw_if_index = (u32) ~ 0;
535   u32 cached_next_index = (u32) ~ 0;
536   u32 feature_bitmap0;
537   u32 trace_flags0;
538
539   l2sess_main_t *sm = &l2sess_main;
540
541   from = vlib_frame_vector_args (frame);
542   n_left_from = frame->n_vectors;
543   next_index = node->cached_next_index;
544
545   while (n_left_from > 0)
546     {
547       u32 n_left_to_next;
548
549       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
550
551       /* Only a single loop for now for simplicity */
552
553       while (n_left_from > 0 && n_left_to_next > 0)
554         {
555           u32 bi0;
556           vlib_buffer_t *b0;
557           u32 next0 = L2SESS_NEXT_DROP;
558           u32 sw_if_index0;
559           //ethernet_header_t *en0;
560
561           /* speculatively enqueue b0 to the current next frame */
562           bi0 = from[0];
563           to_next[0] = bi0;
564           from += 1;
565           to_next += 1;
566           n_left_from -= 1;
567           n_left_to_next -= 1;
568
569           b0 = vlib_get_buffer (vm, bi0);
570           //en0 = vlib_buffer_get_current (b0);
571
572 /*
573  * The non-boilerplate is in the block below.
574  * Note first a magic macro block that sets up the behavior qualifiers:
575  *     node_is_out : 1 = is output, 0 = is input
576  *     node_is_ip6 : 1 = is ip6, 0 = is ip4
577  *     node_is_track : 1 = is a state tracking node, 0 - is a session addition node
578  *
579  * Subsequently the code adjusts its behavior depending on these variables.
580  * It's most probably not great performance wise but much easier to work with.
581  *
582  */
583           {
584             int node_is_out = -1;
585             CLIB_UNUSED (int node_is_ip6) = -1;
586             CLIB_UNUSED (int node_is_track) = -1;
587             u32 node_index = 0;
588             u32 session_tables[2] = { ~0, ~0 };
589             u32 session_nexts[2] = { ~0, ~0 };
590             l2_output_next_nodes_st *next_nodes = 0;
591             u32 *input_feat_next_node_index;
592             u8 l4_proto;
593             u64 now = clib_cpu_time_now ();
594
595 /* 
596  * Set the variables according to which of the 8 nodes we are.
597  * Hopefully the compiler is smart enough to eliminate the extraneous.
598  */
599 #define _(node_name, node_var, is_out, is_ip6, is_track)                 \
600 if(node_var.index == node->node_index)                                   \
601   {                                                                      \
602     node_is_out = is_out;                                                \
603     node_is_ip6 = is_ip6;                                                \
604     node_is_track = is_track;                                            \
605     node_index = node_var.index;                                         \
606     next_nodes = &sm->node_var ## _next_nodes;                           \
607     input_feat_next_node_index = sm->node_var ## _input_next_node_index; \
608   }
609             foreach_l2sess_node
610 #undef _
611               trace_flags0 = 0;
612             if (node_is_out)
613               {
614                 sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_TX];
615               }
616             else
617               {
618                 sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
619               }
620             /* potentially also remove the nodes here */
621             feature_bitmap0 = vnet_buffer (b0)->l2.feature_bitmap;
622
623             if (node_is_track)
624               {
625                 u32 sess_index = vnet_buffer (b0)->l2_classify.opaque_index;
626                 l2s_session_t *sess = sm->sessions + sess_index;
627                 l4_proto = sess->l4_proto;
628
629                 if (session_is_alive (sm, sess, now, 0))
630                   {
631                     if (6 == l4_proto)
632                       {
633                         tcp_session_account_buffer (b0, sess, node_is_out,
634                                                     now);
635                       }
636                     else
637                       {
638                         udp_session_account_buffer (b0, sess, node_is_out,
639                                                     now);
640                       }
641                   }
642                 else
643                   {
644                     timing_wheel_delete (&sm->timing_wheel, sess_index);
645                     delete_session (sm, sw_if_index0, sess_index);
646                     /* FIXME: drop the packet that hit the obsolete node, for now. We really ought to recycle it. */
647                     next0 = 0;
648                   }
649               }
650             else
651               {
652                 /*
653                  * "-add" node: take l2opaque which arrived to us, and deduce
654                  * the tables out of that. ~0 means the topmost classifier table
655                  * applied for this AF on the RX(for input)/TX(for output)) sw_if_index.
656                  * Also add the mirrored session to the paired table.
657                  */
658                 l2s_session_t *sess;
659                 u32 sess_index;
660
661                 l4_proto = l2sess_get_l4_proto (b0, node_is_ip6);
662
663                 pool_get (sm->sessions, sess);
664                 sess_index = sess - sm->sessions;
665                 sess->create_time = now;
666                 sess->side[node_is_out].active_time = now;
667                 sess->side[1 - node_is_out].active_time = now;
668                 sess->l4_proto = l4_proto;
669                 sess->is_ip6 = node_is_ip6;
670                 if (node_is_ip6)
671                   {
672                     session_store_ip6_l3l4_info (b0, sess, node_is_out);
673                   }
674                 else
675                   {
676                     session_store_ip4_l3l4_info (b0, sess, node_is_out);
677                   }
678
679                 l2sess_get_session_tables (sm, sw_if_index0, node_is_out,
680                                            node_is_ip6, l4_proto,
681                                            session_tables);
682                 l2sess_get_session_nexts (sm, sw_if_index0, node_is_out,
683                                           node_is_ip6, l4_proto,
684                                           session_nexts);
685                 l2sess_flip_l3l4_fields (b0, node_is_ip6, l4_proto);
686                 if (session_tables[1] != ~0)
687                   {
688                     l2sess_add_session (b0, node_is_out, node_is_ip6,
689                                         session_tables[1], session_nexts[1],
690                                         sess_index);
691                   }
692                 l2sess_flip_l3l4_fields (b0, node_is_ip6, l4_proto);
693                 if (session_tables[0] != ~0)
694                   {
695                     l2sess_add_session (b0, node_is_out, node_is_ip6,
696                                         session_tables[0], session_nexts[0],
697                                         sess_index);
698                   }
699                 if (6 == sess->l4_proto)
700                   {
701                     tcp_session_account_buffer (b0, sess, node_is_out, now);
702                   }
703                 else
704                   {
705                     udp_session_account_buffer (b0, sess, node_is_out, now);
706                   }
707                 timing_wheel_insert (&sm->timing_wheel,
708                                      now + session_get_timeout (sm, sess,
709                                                                 now),
710                                      sess_index);
711               }
712
713             if (now >= sm->timer_wheel_next_expiring_time)
714               {
715                 check_idle_sessions (sm, sw_if_index0, now);
716               }
717
718             if (node_is_out)
719               {
720                 if (feature_bitmap0)
721                   {
722                     trace_flags0 |= 0x10;
723                   }
724                 if (sw_if_index0 == cached_sw_if_index)
725                   {
726                     trace_flags0 |= 0x20;
727                   }
728                 l2_output_dispatch (sm->vlib_main,
729                                     sm->vnet_main,
730                                     node,
731                                     node_index,
732                                     &cached_sw_if_index,
733                                     &cached_next_index,
734                                     next_nodes,
735                                     b0, sw_if_index0, feature_bitmap0,
736                                     &next0);
737                 trace_flags0 |= 2;
738
739               }
740             else
741               {
742                 next0 =
743                   feat_bitmap_get_next_node_index (input_feat_next_node_index,
744                                                    feature_bitmap0);
745                 trace_flags0 |= 4;
746
747               }
748
749
750
751             if (next0 >= node->n_next_nodes)
752               {
753                 trace_flags0 |= 1;
754               }
755
756             if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
757                                && (b0->flags & VLIB_BUFFER_IS_TRACED)))
758               {
759                 l2sess_trace_t *t =
760                   vlib_add_trace (vm, node, b0, sizeof (*t));
761                 t->sw_if_index = sw_if_index0;
762                 t->next_index = next0;
763                 t->trace_flags = trace_flags0;
764                 t->l4_proto = l4_proto;
765                 t->session_tables[0] = session_tables[0];
766                 t->session_tables[1] = session_tables[1];
767                 t->session_nexts[0] = session_nexts[0];
768                 t->session_nexts[1] = session_nexts[1];
769               }
770
771           }
772           pkts_swapped += 1;
773           if (next0 >= node->n_next_nodes)
774             {
775               next0 = 0;
776             }
777
778           /* verify speculative enqueue, maybe switch current next frame */
779           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
780                                            to_next, n_left_to_next,
781                                            bi0, next0);
782         }
783
784       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
785     }
786   vlib_node_increment_counter (vm, node->node_index,
787                                L2SESS_ERROR_SWAPPED, pkts_swapped);
788   return frame->n_vectors;
789 }
790
791
792 #define _(node_name, node_var, is_out, is_ip6, is_track) \
793 static uword                                             \
794 node_var ## node_fn (vlib_main_t * vm,                   \
795                   vlib_node_runtime_t * node,            \
796                   vlib_frame_t * frame)                  \
797 {                                                        \
798   return l2sess_node_fn(vm, node, frame);                \
799 }                                                        \
800 VLIB_REGISTER_NODE (node_var) = {                        \
801   .function = node_var ## node_fn,                       \
802   .name = node_name,                                     \
803   .vector_size = sizeof (u32),                           \
804   .format_trace = format_ ## node_var ## _trace,         \
805   .type = VLIB_NODE_TYPE_INTERNAL,                       \
806                                                          \
807   .n_errors = ARRAY_LEN(l2sess_error_strings),           \
808   .error_strings = l2sess_error_strings,                 \
809                                                          \
810   .n_next_nodes = L2SESS_N_NEXT,                         \
811   .next_nodes = {                                        \
812         [L2SESS_NEXT_DROP] = "error-drop",               \
813   },                                                     \
814 };
815 foreach_l2sess_node
816 #undef _