acl: The ACL plugin.
[vpp.git] / plugins / acl-plugin / 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 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
490   if (PREDICT_FALSE (_vec_len (sm->data_from_advancing_timing_wheel) > 0))
491     {
492       uword i;
493       for (i = 0; i < vec_len (sm->data_from_advancing_timing_wheel); i++)
494         {
495           u32 session_index = sm->data_from_advancing_timing_wheel[i];
496           if (!pool_is_free_index (sm->sessions, session_index))
497             {
498               l2s_session_t *sess = sm->sessions + session_index;
499               u64 last_active;
500               if (session_is_alive (sm, sess, now, &last_active))
501                 {
502 #ifdef DEBUG_SESSIONS
503               clib_warning ("Restarting timer for session %d", (int) session_index);
504 #endif
505                     /* Pretend we did this in the past, at last_active moment */
506                     timing_wheel_insert (&sm->timing_wheel,
507                                          last_active + session_get_timeout (sm, sess,
508                                                                     last_active),
509                                          session_index);
510                 }
511               else
512                 {
513 #ifdef DEBUG_SESSIONS
514               clib_warning ("Deleting session %d", (int) session_index);
515 #endif
516               delete_session (sm, sw_if_index, session_index);
517                 }
518             }
519         }
520       _vec_len (sm->data_from_advancing_timing_wheel) = 0;
521     }
522 }
523
524 static uword
525 l2sess_node_fn (vlib_main_t * vm,
526                 vlib_node_runtime_t * node, vlib_frame_t * frame)
527 {
528   u32 n_left_from, *from, *to_next;
529   l2sess_next_t next_index;
530   u32 pkts_swapped = 0;
531   u32 cached_sw_if_index = (u32) ~ 0;
532   u32 cached_next_index = (u32) ~ 0;
533   u32 feature_bitmap0;
534   u32 trace_flags0;
535
536   l2sess_main_t *sm = &l2sess_main;
537
538   from = vlib_frame_vector_args (frame);
539   n_left_from = frame->n_vectors;
540   next_index = node->cached_next_index;
541
542   while (n_left_from > 0)
543     {
544       u32 n_left_to_next;
545
546       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
547
548       /* Only a single loop for now for simplicity */
549
550       while (n_left_from > 0 && n_left_to_next > 0)
551         {
552           u32 bi0;
553           vlib_buffer_t *b0;
554           u32 next0 = L2SESS_NEXT_DROP;
555           u32 sw_if_index0;
556           //ethernet_header_t *en0;
557
558           /* speculatively enqueue b0 to the current next frame */
559           bi0 = from[0];
560           to_next[0] = bi0;
561           from += 1;
562           to_next += 1;
563           n_left_from -= 1;
564           n_left_to_next -= 1;
565
566           b0 = vlib_get_buffer (vm, bi0);
567           //en0 = vlib_buffer_get_current (b0);
568
569 /*
570  * The non-boilerplate is in the block below.
571  * Note first a magic macro block that sets up the behavior qualifiers:
572  *     node_is_out : 1 = is output, 0 = is input
573  *     node_is_ip6 : 1 = is ip6, 0 = is ip4
574  *     node_is_track : 1 = is a state tracking node, 0 - is a session addition node
575  *
576  * Subsequently the code adjusts its behavior depending on these variables.
577  * It's most probably not great performance wise but much easier to work with.
578  *
579  */
580           {
581             int node_is_out = -1;
582             CLIB_UNUSED (int node_is_ip6) = -1;
583             CLIB_UNUSED (int node_is_track) = -1;
584             u32 node_index = 0;
585             u32 session_tables[2] = { ~0, ~0 };
586             u32 session_nexts[2] = { ~0, ~0 };
587             l2_output_next_nodes_st *next_nodes = 0;
588             u32 *input_feat_next_node_index;
589             u8 l4_proto;
590             u64 now = clib_cpu_time_now ();
591
592 /* 
593  * Set the variables according to which of the 8 nodes we are.
594  * Hopefully the compiler is smart enough to eliminate the extraneous.
595  */
596 #define _(node_name, node_var, is_out, is_ip6, is_track)                 \
597 if(node_var.index == node->node_index)                                   \
598   {                                                                      \
599     node_is_out = is_out;                                                \
600     node_is_ip6 = is_ip6;                                                \
601     node_is_track = is_track;                                            \
602     node_index = node_var.index;                                         \
603     next_nodes = &sm->node_var ## _next_nodes;                           \
604     input_feat_next_node_index = sm->node_var ## _input_next_node_index; \
605   }
606             foreach_l2sess_node
607 #undef _
608               trace_flags0 = 0;
609             if (node_is_out)
610               {
611                 sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_TX];
612               }
613             else
614               {
615                 sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
616               }
617             /* potentially also remove the nodes here */
618             feature_bitmap0 = vnet_buffer (b0)->l2.feature_bitmap;
619
620             if (node_is_track)
621               {
622                 u32 sess_index = vnet_buffer (b0)->l2_classify.opaque_index;
623                 l2s_session_t *sess = sm->sessions + sess_index;
624                 l4_proto = sess->l4_proto;
625
626                 if (session_is_alive (sm, sess, now, 0))
627                   {
628                     if (6 == l4_proto)
629                       {
630                         tcp_session_account_buffer (b0, sess, node_is_out,
631                                                     now);
632                       }
633                     else
634                       {
635                         udp_session_account_buffer (b0, sess, node_is_out,
636                                                     now);
637                       }
638                   }
639                 else
640                   {
641                     timing_wheel_delete (&sm->timing_wheel, sess_index);
642                     delete_session (sm, sw_if_index0, sess_index);
643                     /* FIXME: drop the packet that hit the obsolete node, for now. We really ought to recycle it. */
644                     next0 = 0;
645                   }
646               }
647             else
648               {
649                 /*
650                  * "-add" node: take l2opaque which arrived to us, and deduce
651                  * the tables out of that. ~0 means the topmost classifier table
652                  * applied for this AF on the RX(for input)/TX(for output)) sw_if_index.
653                  * Also add the mirrored session to the paired table.
654                  */
655                 l2s_session_t *sess;
656                 u32 sess_index;
657
658                 l4_proto = l2sess_get_l4_proto (b0, node_is_ip6);
659
660                 pool_get (sm->sessions, sess);
661                 sess_index = sess - sm->sessions;
662                 sess->create_time = now;
663                 sess->side[node_is_out].active_time = now;
664                 sess->side[1 - node_is_out].active_time = now;
665                 sess->l4_proto = l4_proto;
666                 sess->is_ip6 = node_is_ip6;
667                 if (node_is_ip6)
668                   {
669                     session_store_ip6_l3l4_info (b0, sess, node_is_out);
670                   }
671                 else
672                   {
673                     session_store_ip4_l3l4_info (b0, sess, node_is_out);
674                   }
675
676                 l2sess_get_session_tables (sm, sw_if_index0, node_is_out,
677                                            node_is_ip6, l4_proto,
678                                            session_tables);
679                 l2sess_get_session_nexts (sm, sw_if_index0, node_is_out,
680                                           node_is_ip6, l4_proto,
681                                           session_nexts);
682                 l2sess_flip_l3l4_fields (b0, node_is_ip6, l4_proto);
683                 if (session_tables[1] != ~0)
684                   {
685                     l2sess_add_session (b0, node_is_out, node_is_ip6,
686                                         session_tables[1], session_nexts[1],
687                                         sess_index);
688                   }
689                 l2sess_flip_l3l4_fields (b0, node_is_ip6, l4_proto);
690                 if (session_tables[0] != ~0)
691                   {
692                     l2sess_add_session (b0, node_is_out, node_is_ip6,
693                                         session_tables[0], session_nexts[0],
694                                         sess_index);
695                   }
696                 if (6 == sess->l4_proto)
697                   {
698                     tcp_session_account_buffer (b0, sess, node_is_out, now);
699                   }
700                 else
701                   {
702                     udp_session_account_buffer (b0, sess, node_is_out, now);
703                   }
704                 timing_wheel_insert (&sm->timing_wheel,
705                                      now + session_get_timeout (sm, sess,
706                                                                 now),
707                                      sess_index);
708               }
709
710             if (now >= sm->timer_wheel_next_expiring_time)
711               {
712                 check_idle_sessions (sm, sw_if_index0, now);
713               }
714
715             if (node_is_out)
716               {
717                 if (feature_bitmap0)
718                   {
719                     trace_flags0 |= 0x10;
720                   }
721                 if (sw_if_index0 == cached_sw_if_index)
722                   {
723                     trace_flags0 |= 0x20;
724                   }
725                 l2_output_dispatch (sm->vlib_main,
726                                     sm->vnet_main,
727                                     node,
728                                     node_index,
729                                     &cached_sw_if_index,
730                                     &cached_next_index,
731                                     next_nodes,
732                                     b0, sw_if_index0, feature_bitmap0,
733                                     &next0);
734                 trace_flags0 |= 2;
735
736               }
737             else
738               {
739                 next0 =
740                   feat_bitmap_get_next_node_index (input_feat_next_node_index,
741                                                    feature_bitmap0);
742                 trace_flags0 |= 4;
743
744               }
745
746
747
748             if (next0 >= node->n_next_nodes)
749               {
750                 trace_flags0 |= 1;
751               }
752
753             if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
754                                && (b0->flags & VLIB_BUFFER_IS_TRACED)))
755               {
756                 l2sess_trace_t *t =
757                   vlib_add_trace (vm, node, b0, sizeof (*t));
758                 t->sw_if_index = sw_if_index0;
759                 t->next_index = next0;
760                 t->trace_flags = trace_flags0;
761                 t->l4_proto = l4_proto;
762                 t->session_tables[0] = session_tables[0];
763                 t->session_tables[1] = session_tables[1];
764                 t->session_nexts[0] = session_nexts[0];
765                 t->session_nexts[1] = session_nexts[1];
766               }
767
768           }
769           pkts_swapped += 1;
770           if (next0 >= node->n_next_nodes)
771             {
772               next0 = 0;
773             }
774
775           /* verify speculative enqueue, maybe switch current next frame */
776           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
777                                            to_next, n_left_to_next,
778                                            bi0, next0);
779         }
780
781       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
782     }
783   vlib_node_increment_counter (vm, node->node_index,
784                                L2SESS_ERROR_SWAPPED, pkts_swapped);
785   return frame->n_vectors;
786 }
787
788
789 #define _(node_name, node_var, is_out, is_ip6, is_track) \
790 static uword                                             \
791 node_var ## node_fn (vlib_main_t * vm,                   \
792                   vlib_node_runtime_t * node,            \
793                   vlib_frame_t * frame)                  \
794 {                                                        \
795   return l2sess_node_fn(vm, node, frame);                \
796 }                                                        \
797 VLIB_REGISTER_NODE (node_var) = {                        \
798   .function = node_var ## node_fn,                       \
799   .name = node_name,                                     \
800   .vector_size = sizeof (u32),                           \
801   .format_trace = format_ ## node_var ## _trace,         \
802   .type = VLIB_NODE_TYPE_INTERNAL,                       \
803                                                          \
804   .n_errors = ARRAY_LEN(l2sess_error_strings),           \
805   .error_strings = l2sess_error_strings,                 \
806                                                          \
807   .n_next_nodes = L2SESS_N_NEXT,                         \
808   .next_nodes = {                                        \
809         [L2SESS_NEXT_DROP] = "error-drop",               \
810   },                                                     \
811 };
812 foreach_l2sess_node
813 #undef _