c10b11b30706cff457b09656181435cee4f57d1b
[vpp.git] / vnet / vnet / nsh-gre / decap.c
1 /*
2  * nsh.c: nsh packet processing
3  *
4  * Copyright (c) 2013 Cisco and/or its affiliates.
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 <vlib/vlib.h>
19 #include <vnet/pg/pg.h>
20 #include <vnet/nsh-gre/nsh_gre.h>
21 #include <vnet/nsh/nsh_packet.h>
22
23 vlib_node_registration_t nsh_input_node;
24
25 typedef struct {
26   u32 next_index;
27   u32 tunnel_index;
28   u32 error;
29   nsh_header_t h;
30 } nsh_rx_trace_t;
31
32
33 u8 * format_nsh_header_with_length (u8 * s, va_list * args)
34 {
35   nsh_header_t * h = va_arg (*args, nsh_header_t *);
36   u32 max_header_bytes = va_arg (*args, u32);
37   u32 tmp, header_bytes;
38
39   header_bytes = sizeof (h[0]);
40   if (max_header_bytes != 0 && header_bytes > max_header_bytes)
41     return format (s, "gre-nsh header truncated");
42
43   s = format (s, "ver %d ", h->ver_o_c>>6);
44
45   if (h->ver_o_c & NSH_O_BIT)
46       s = format (s, "O-set ");
47
48   if (h->ver_o_c & NSH_C_BIT)
49       s = format (s, "C-set ");
50
51   s = format (s, "len %d (%d bytes) md_type %d next_protocol %d\n",
52               h->length, h->length * 4, h->md_type, h->next_protocol);
53   
54   tmp = clib_net_to_host_u32 (h->spi_si);
55
56   s = format (s, "  spi %d si %d ",
57               (tmp>>NSH_SPI_SHIFT) & NSH_SPI_MASK,
58               tmp & NSH_SINDEX_MASK);
59
60   s = format (s, "c1 %u c2 %u c3 %u c4 %u",
61               clib_net_to_host_u32 (h->c1),
62               clib_net_to_host_u32 (h->c2),
63               clib_net_to_host_u32 (h->c3),
64               clib_net_to_host_u32 (h->c4));
65
66   return s;
67 }
68
69
70 u8 * format_nsh_rx_trace (u8 * s, va_list * args)
71 {
72   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
73   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
74   nsh_rx_trace_t * t = va_arg (*args, nsh_rx_trace_t *);
75
76   if (t->tunnel_index != ~0)
77     {
78       s = format (s, "NSH: tunnel %d next %d error %d", t->tunnel_index, 
79                   t->next_index, t->error);
80     }
81   else
82     {
83       s = format (s, "NSH: no tunnel next %d error %d\n", t->next_index, 
84                   t->error);
85     }
86   s = format (s, "\n  %U", format_nsh_header_with_length, &t->h, 
87               (u32) sizeof (t->h) /* max size */);
88   return s;
89 }
90
91 static uword
92 nsh_gre_input (vlib_main_t * vm,
93                vlib_node_runtime_t * node,
94                vlib_frame_t * from_frame)
95 {
96   u32 n_left_from, next_index, * from, * to_next;
97   nsh_gre_main_t * ngm = &nsh_gre_main;
98   vnet_main_t * vnm = ngm->vnet_main;
99   vnet_interface_main_t * im = &vnm->interface_main;
100   u32 last_tunnel_index = ~0;
101   u64 last_key = ~0ULL;
102   u32 pkts_decapsulated = 0;
103   u32 cpu_index = os_get_cpu_number();
104   u32 stats_sw_if_index, stats_n_packets, stats_n_bytes;
105
106   from = vlib_frame_vector_args (from_frame);
107   n_left_from = from_frame->n_vectors;
108
109   next_index = node->cached_next_index;
110   stats_sw_if_index = node->runtime_data[0];
111   stats_n_packets = stats_n_bytes = 0;
112
113   while (n_left_from > 0)
114     {
115       u32 n_left_to_next;
116
117       vlib_get_next_frame (vm, node, next_index,
118                            to_next, n_left_to_next);
119
120       while (n_left_from >= 4 && n_left_to_next >= 2)
121         {
122           u32 bi0, bi1;
123           vlib_buffer_t * b0, * b1;
124           u32 next0, next1;
125           nsh_header_t * h0, * h1;
126           uword * p0, * p1;
127           u32 tunnel_index0, tunnel_index1;
128           nsh_gre_tunnel_t * t0, * t1;
129           u64 key0, key1;
130           u32 error0, error1;
131           u32 sw_if_index0, sw_if_index1, len0, len1;
132
133           /* Prefetch next iteration. */
134           {
135             vlib_buffer_t * p2, * p3;
136
137             p2 = vlib_get_buffer (vm, from[2]);
138             p3 = vlib_get_buffer (vm, from[3]);
139
140             vlib_prefetch_buffer_header (p2, LOAD);
141             vlib_prefetch_buffer_header (p3, LOAD);
142
143             CLIB_PREFETCH (p2->data, 2*CLIB_CACHE_LINE_BYTES, LOAD);
144             CLIB_PREFETCH (p3->data, 2*CLIB_CACHE_LINE_BYTES, LOAD);
145           }
146
147           bi0 = from[0];
148           bi1 = from[1];
149           to_next[0] = bi0;
150           to_next[1] = bi1;
151           from += 2;
152           to_next += 2;
153           n_left_to_next -= 2;
154           n_left_from -= 2;
155
156           b0 = vlib_get_buffer (vm, bi0);
157           b1 = vlib_get_buffer (vm, bi1);
158
159           h0 = vlib_buffer_get_current (b0);
160           h1 = vlib_buffer_get_current (b1);
161
162           /* gre stashed the src ip4 address for us... */
163           key0 = (((u64)(vnet_buffer(b0)->gre.src))<<32) | h0->spi_si;
164           key1 = (((u64)(vnet_buffer(b1)->gre.src))<<32) | h1->spi_si;
165
166           /* "pop" nsh header */
167           vlib_buffer_advance (b0, sizeof (*h0));
168           vlib_buffer_advance (b1, sizeof (*h1));
169
170           tunnel_index0 = ~0;
171           tunnel_index1 = ~0;
172           error0 = 0;
173           error1 = 0;
174           next0 = NSH_GRE_INPUT_NEXT_DROP;
175           next1 = NSH_GRE_INPUT_NEXT_DROP;
176
177           if (PREDICT_FALSE(key0 != last_key))
178             {
179               p0 = hash_get (ngm->nsh_gre_tunnel_by_src_address, key0);
180
181               if (p0 == 0)
182                 {
183                   error0 = NSH_GRE_ERROR_NO_SUCH_TUNNEL;
184                   goto trace0;
185                 }
186
187               last_key = key0;
188               tunnel_index0 = last_tunnel_index = p0[0];
189             }
190           else
191             tunnel_index0 = last_tunnel_index;
192
193           t0 = pool_elt_at_index (ngm->tunnels, tunnel_index0);
194
195           next0 = t0->decap_next_index;
196           sw_if_index0 = t0->sw_if_index;
197           len0 = vlib_buffer_length_in_chain(vm, b0);
198
199           /* Required to make the l2 tag push / pop code work on l2 subifs */
200           vnet_update_l2_len (b0);
201
202           next0 = t0->decap_next_index;
203
204           /* ip[46] lookup in the configured FIB, otherwise an opaque */
205           vnet_buffer(b0)->sw_if_index[VLIB_TX] = t0->decap_fib_index;
206
207           pkts_decapsulated++;
208           stats_n_packets += 1;
209           stats_n_bytes += len0;
210
211           if (PREDICT_FALSE(sw_if_index0 != stats_sw_if_index))
212           {
213             stats_n_packets -= 1;
214             stats_n_bytes -= len0;
215             if (stats_n_packets)
216               vlib_increment_combined_counter(
217                   im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX,
218                   cpu_index, stats_sw_if_index, stats_n_packets, stats_n_bytes);
219             stats_n_packets = 1;
220             stats_n_bytes = len0;
221             stats_sw_if_index = sw_if_index0;
222           }
223
224         trace0:
225           b0->error = error0 ? node->errors[error0] : 0;
226
227           if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) 
228             {
229               nsh_rx_trace_t *tr = vlib_add_trace (vm, node, 
230                                                    b0, sizeof (*tr));
231               tr->next_index = next0;
232               tr->error = error0;
233               tr->tunnel_index = tunnel_index0;
234               tr->h = h0[0];
235             }
236
237           if (PREDICT_FALSE(key1 != last_key))
238             {
239               p1 = hash_get (ngm->nsh_gre_tunnel_by_src_address, key1);
240
241               if (p1 == 0)
242                 {
243                   error1 = NSH_GRE_ERROR_NO_SUCH_TUNNEL;
244                   goto trace1;
245                 }
246
247               last_key = key1;
248               tunnel_index1 = last_tunnel_index = p1[0];
249             }
250           else
251             tunnel_index1 = last_tunnel_index;
252
253           t1 = pool_elt_at_index (ngm->tunnels, tunnel_index1);
254
255           next1 = t1->decap_next_index;
256           sw_if_index1 = t1->sw_if_index;
257           len1 = vlib_buffer_length_in_chain(vm, b1);
258
259           /* Required to make the l2 tag push / pop code work on l2 subifs */
260           vnet_update_l2_len (b1);
261
262           next1 = t1->decap_next_index;
263
264           /* ip[46] lookup in the configured FIB, otherwise an opaque */
265           vnet_buffer(b1)->sw_if_index[VLIB_TX] = t1->decap_fib_index;
266
267           pkts_decapsulated++;
268           stats_n_packets += 1;
269           stats_n_bytes += len1;
270           /* Batch stats increment on the same nsh-gre tunnel so counter
271            is not incremented per packet */
272           if (PREDICT_FALSE(sw_if_index1 != stats_sw_if_index))
273           {
274             stats_n_packets -= 1;
275             stats_n_bytes -= len1;
276             if (stats_n_packets)
277               vlib_increment_combined_counter(
278                   im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX,
279                   cpu_index, stats_sw_if_index, stats_n_packets, stats_n_bytes);
280             stats_n_packets = 1;
281             stats_n_bytes = len1;
282             stats_sw_if_index = sw_if_index1;
283           }
284           vnet_buffer(b1)->sw_if_index[VLIB_TX] = t1->decap_fib_index;
285
286         trace1:
287           b1->error = error1 ? node->errors[error1] : 0;
288
289           if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED)) 
290             {
291               nsh_rx_trace_t *tr = vlib_add_trace (vm, node, 
292                                                    b1, sizeof (*tr));
293               tr->next_index = next1;
294               tr->error = error1;
295               tr->tunnel_index = tunnel_index1;
296               tr->h = h1[0];
297             }
298
299           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
300                                            to_next, n_left_to_next,
301                                            bi0, bi1, next0, next1);
302         }
303     
304       while (n_left_from > 0 && n_left_to_next > 0)
305         {
306           u32 bi0;
307           vlib_buffer_t * b0;
308           u32 next0;
309           nsh_header_t * h0;
310           uword * p0;
311           u32 tunnel_index0;
312           nsh_gre_tunnel_t * t0;
313           u64 key0;
314           u32 error0;
315           u32 sw_if_index0, len0;
316
317           bi0 = from[0];
318           to_next[0] = bi0;
319           from += 1;
320           to_next += 1;
321           n_left_from -= 1;
322           n_left_to_next -= 1;
323
324           b0 = vlib_get_buffer (vm, bi0);
325           h0 = vlib_buffer_get_current (b0);
326
327           /* gre stashed the src ip4 address for us... */
328           key0 = (((u64)(vnet_buffer(b0)->gre.src))<<32) | h0->spi_si;
329
330           /* "pop" nsh header */
331           vlib_buffer_advance (b0, sizeof (*h0));
332
333           tunnel_index0 = ~0;
334           error0 = 0;
335           next0 = NSH_GRE_INPUT_NEXT_DROP;
336
337           if (PREDICT_FALSE(key0 != last_key))
338             {
339               p0 = hash_get (ngm->nsh_gre_tunnel_by_src_address, key0);
340
341               if (p0 == 0)
342                 {
343                   error0 = NSH_GRE_ERROR_NO_SUCH_TUNNEL;
344                   goto trace00;
345                 }
346
347               last_key = key0;
348               tunnel_index0 = last_tunnel_index = p0[0];
349             }
350           else
351             tunnel_index0 = last_tunnel_index;
352
353           t0 = pool_elt_at_index (ngm->tunnels, tunnel_index0);
354
355           next0 = t0->decap_next_index;
356           sw_if_index0 = t0->sw_if_index;
357           len0 = vlib_buffer_length_in_chain(vm, b0);
358
359           /* Required to make the l2 tag push / pop code work on l2 subifs */
360           vnet_update_l2_len (b0);
361
362           next0 = t0->decap_next_index;
363
364           /* ip[46] lookup in the configured FIB, otherwise an opaque */
365           vnet_buffer(b0)->sw_if_index[VLIB_TX] = t0->decap_fib_index;
366           pkts_decapsulated ++;
367
368           stats_n_packets += 1;
369           stats_n_bytes += len0;
370
371           /* Batch stats increment on the same nsh-gre tunnel so counter
372            is not incremented per packet */
373           if (PREDICT_FALSE(sw_if_index0 != stats_sw_if_index))
374           {
375             stats_n_packets -= 1;
376             stats_n_bytes -= len0;
377             if (stats_n_packets)
378               vlib_increment_combined_counter(
379                   im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX,
380                   cpu_index, stats_sw_if_index, stats_n_packets, stats_n_bytes);
381             stats_n_packets = 1;
382             stats_n_bytes = len0;
383             stats_sw_if_index = sw_if_index0;
384           }
385
386         trace00:
387           b0->error = error0 ? node->errors[error0] : 0;
388
389           if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) 
390             {
391               nsh_rx_trace_t *tr = vlib_add_trace (vm, node, 
392                                                    b0, sizeof (*tr));
393               tr->next_index = next0;
394               tr->error = error0;
395               tr->tunnel_index = tunnel_index0;
396               tr->h = h0[0];
397             }
398           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
399                                            to_next, n_left_to_next,
400                                            bi0, next0);
401         }
402
403       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
404     }
405   vlib_node_increment_counter (vm, nsh_gre_input_node.index,
406                                NSH_GRE_ERROR_DECAPSULATED, 
407                                pkts_decapsulated);
408   /* Increment any remaining batch stats */
409   if (stats_n_packets)
410   {
411     vlib_increment_combined_counter(
412         im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX, cpu_index,
413         stats_sw_if_index, stats_n_packets, stats_n_bytes);
414     node->runtime_data[0] = stats_sw_if_index;
415   }
416   return from_frame->n_vectors;
417 }
418
419 static char * nsh_error_strings[] = {
420 #define nsh_gre_error(n,s) s,
421 #include <vnet/nsh/nsh_error.def>
422 #undef nsh_gre_error
423 #undef _
424 };
425
426 VLIB_REGISTER_NODE (nsh_gre_input_node) = {
427   .function = nsh_gre_input,
428   .name = "nsh-gre-input",
429   /* Takes a vector of packets. */
430   .vector_size = sizeof (u32),
431
432   .n_errors = NSH_GRE_N_ERROR,
433   .error_strings = nsh_error_strings,
434
435   .n_next_nodes = NSH_GRE_INPUT_N_NEXT,
436   .next_nodes = {
437 #define _(s,n) [NSH_GRE_INPUT_NEXT_##s] = n,
438     foreach_nsh_gre_input_next
439 #undef _
440   },
441
442   .format_buffer = format_nsh_header_with_length,
443   .format_trace = format_nsh_rx_trace,
444   // $$$$ .unformat_buffer = unformat_nsh_gre_header,
445 };