Initial commit of vpp code.
[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-gre/nsh_gre_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_GRE_O_BIT)
46       s = format (s, "O-set ");
47
48   if (h->ver_o_c & NSH_GRE_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_GRE_SPI_SHIFT) & NSH_GRE_SPI_MASK,
58               tmp & NSH_GRE_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   u32 last_tunnel_index = ~0;
99   u64 last_key = ~0ULL;
100   u32 pkts_decapsulated = 0;
101
102   from = vlib_frame_vector_args (from_frame);
103   n_left_from = from_frame->n_vectors;
104
105   next_index = node->cached_next_index;
106
107   while (n_left_from > 0)
108     {
109       u32 n_left_to_next;
110
111       vlib_get_next_frame (vm, node, next_index,
112                            to_next, n_left_to_next);
113
114       while (n_left_from >= 4 && n_left_to_next >= 2)
115         {
116           u32 bi0, bi1;
117           vlib_buffer_t * b0, * b1;
118           u32 next0, next1;
119           nsh_header_t * h0, * h1;
120           uword * p0, * p1;
121           u32 tunnel_index0, tunnel_index1;
122           nsh_gre_tunnel_t * t0, * t1;
123           u64 key0, key1;
124           u32 error0, error1;
125
126           /* Prefetch next iteration. */
127           {
128             vlib_buffer_t * p2, * p3;
129
130             p2 = vlib_get_buffer (vm, from[2]);
131             p3 = vlib_get_buffer (vm, from[3]);
132
133             vlib_prefetch_buffer_header (p2, LOAD);
134             vlib_prefetch_buffer_header (p3, LOAD);
135
136             CLIB_PREFETCH (p2->data, 2*CLIB_CACHE_LINE_BYTES, LOAD);
137             CLIB_PREFETCH (p3->data, 2*CLIB_CACHE_LINE_BYTES, LOAD);
138           }
139
140           bi0 = from[0];
141           bi1 = from[1];
142           to_next[0] = bi0;
143           to_next[1] = bi1;
144           from += 2;
145           to_next += 2;
146           n_left_to_next -= 2;
147           n_left_from -= 2;
148
149           b0 = vlib_get_buffer (vm, bi0);
150           b1 = vlib_get_buffer (vm, bi1);
151
152           h0 = vlib_buffer_get_current (b0);
153           h1 = vlib_buffer_get_current (b1);
154
155           /* gre stashed the src ip4 address for us... */
156           key0 = (((u64)(vnet_buffer(b0)->gre.src))<<32) | h0->spi_si;
157           key1 = (((u64)(vnet_buffer(b1)->gre.src))<<32) | h1->spi_si;
158
159           /* "pop" nsh header */
160           vlib_buffer_advance (b0, sizeof (*h0));
161           vlib_buffer_advance (b1, sizeof (*h1));
162
163           tunnel_index0 = ~0;
164           tunnel_index1 = ~0;
165           error0 = 0;
166           error1 = 0;
167           next0 = NSH_INPUT_NEXT_DROP;
168           next1 = NSH_INPUT_NEXT_DROP;
169
170           if (PREDICT_FALSE(key0 != last_key))
171             {
172               p0 = hash_get (ngm->nsh_gre_tunnel_by_src_address, key0);
173
174               if (p0 == 0)
175                 {
176                   error0 = NSH_GRE_ERROR_NO_SUCH_TUNNEL;
177                   goto trace0;
178                 }
179
180               last_key = key0;
181               tunnel_index0 = last_tunnel_index = p0[0];
182             }
183           else
184             tunnel_index0 = last_tunnel_index;
185
186           t0 = pool_elt_at_index (ngm->tunnels, tunnel_index0);
187
188           /* Required to make the l2 tag push / pop code work on l2 subifs */
189           vnet_update_l2_len (b0);
190
191           next0 = t0->decap_next_index;
192
193           /* ip[46] lookup in the configured FIB, otherwise an opaque */
194           vnet_buffer(b0)->sw_if_index[VLIB_TX] = t0->decap_fib_index;
195
196         trace0:
197           b0->error = error0 ? node->errors[error0] : 0;
198
199           if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) 
200             {
201               nsh_rx_trace_t *tr = vlib_add_trace (vm, node, 
202                                                    b0, sizeof (*tr));
203               tr->next_index = next0;
204               tr->error = error0;
205               tr->tunnel_index = tunnel_index0;
206               tr->h = h0[0];
207             }
208
209           if (PREDICT_FALSE(key1 != last_key))
210             {
211               p1 = hash_get (ngm->nsh_gre_tunnel_by_src_address, key1);
212
213               if (p1 == 0)
214                 {
215                   error1 = NSH_GRE_ERROR_NO_SUCH_TUNNEL;
216                   goto trace1;
217                 }
218
219               last_key = key1;
220               tunnel_index1 = last_tunnel_index = p1[0];
221             }
222           else
223             tunnel_index1 = last_tunnel_index;
224
225           t1 = pool_elt_at_index (ngm->tunnels, tunnel_index1);
226
227           /* Required to make the l2 tag push / pop code work on l2 subifs */
228           vnet_update_l2_len (b1);
229
230           next1 = t1->decap_next_index;
231
232           /* ip[46] lookup in the configured FIB, otherwise an opaque */
233           vnet_buffer(b1)->sw_if_index[VLIB_TX] = t1->decap_fib_index;
234
235           pkts_decapsulated +=2;
236
237         trace1:
238           b1->error = error1 ? node->errors[error1] : 0;
239
240           if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED)) 
241             {
242               nsh_rx_trace_t *tr = vlib_add_trace (vm, node, 
243                                                    b1, sizeof (*tr));
244               tr->next_index = next1;
245               tr->error = error1;
246               tr->tunnel_index = tunnel_index1;
247               tr->h = h1[0];
248             }
249
250           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
251                                            to_next, n_left_to_next,
252                                            bi0, bi1, next0, next1);
253         }
254     
255       while (n_left_from > 0 && n_left_to_next > 0)
256         {
257           u32 bi0;
258           vlib_buffer_t * b0;
259           u32 next0;
260           nsh_header_t * h0;
261           uword * p0;
262           u32 tunnel_index0;
263           nsh_gre_tunnel_t * t0;
264           u64 key0;
265           u32 error0;
266
267           bi0 = from[0];
268           to_next[0] = bi0;
269           from += 1;
270           to_next += 1;
271           n_left_from -= 1;
272           n_left_to_next -= 1;
273
274           b0 = vlib_get_buffer (vm, bi0);
275           h0 = vlib_buffer_get_current (b0);
276
277           /* gre stashed the src ip4 address for us... */
278           key0 = (((u64)(vnet_buffer(b0)->gre.src))<<32) | h0->spi_si;
279
280           /* "pop" nsh header */
281           vlib_buffer_advance (b0, sizeof (*h0));
282
283           tunnel_index0 = ~0;
284           error0 = 0;
285           next0 = NSH_INPUT_NEXT_DROP;
286
287           if (PREDICT_FALSE(key0 != last_key))
288             {
289               p0 = hash_get (ngm->nsh_gre_tunnel_by_src_address, key0);
290
291               if (p0 == 0)
292                 {
293                   error0 = NSH_GRE_ERROR_NO_SUCH_TUNNEL;
294                   goto trace00;
295                 }
296
297               last_key = key0;
298               tunnel_index0 = last_tunnel_index = p0[0];
299             }
300           else
301             tunnel_index0 = last_tunnel_index;
302
303           t0 = pool_elt_at_index (ngm->tunnels, tunnel_index0);
304
305           /* Required to make the l2 tag push / pop code work on l2 subifs */
306           vnet_update_l2_len (b0);
307
308           next0 = t0->decap_next_index;
309
310           /* ip[46] lookup in the configured FIB, otherwise an opaque */
311           vnet_buffer(b0)->sw_if_index[VLIB_TX] = t0->decap_fib_index;
312           pkts_decapsulated ++;
313
314         trace00:
315           b0->error = error0 ? node->errors[error0] : 0;
316
317           if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) 
318             {
319               nsh_rx_trace_t *tr = vlib_add_trace (vm, node, 
320                                                    b0, sizeof (*tr));
321               tr->next_index = next0;
322               tr->error = error0;
323               tr->tunnel_index = tunnel_index0;
324               tr->h = h0[0];
325             }
326           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
327                                            to_next, n_left_to_next,
328                                            bi0, next0);
329         }
330
331       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
332     }
333   vlib_node_increment_counter (vm, nsh_gre_input_node.index,
334                                NSH_GRE_ERROR_DECAPSULATED, 
335                                pkts_decapsulated);
336   return from_frame->n_vectors;
337 }
338
339 static char * nsh_error_strings[] = {
340 #define nsh_gre_error(n,s) s,
341 #include <vnet/nsh-gre/nsh_gre_error.def>
342 #undef nsh_gre_error
343 #undef _
344 };
345
346 VLIB_REGISTER_NODE (nsh_gre_input_node) = {
347   .function = nsh_gre_input,
348   .name = "nsh-gre-input",
349   /* Takes a vector of packets. */
350   .vector_size = sizeof (u32),
351
352   .n_errors = NSH_GRE_N_ERROR,
353   .error_strings = nsh_error_strings,
354
355   .n_next_nodes = NSH_INPUT_N_NEXT,
356   .next_nodes = {
357 #define _(s,n) [NSH_INPUT_NEXT_##s] = n,
358     foreach_nsh_gre_input_next
359 #undef _
360   },
361
362   .format_buffer = format_nsh_header_with_length,
363   .format_trace = format_nsh_rx_trace,
364   // $$$$ .unformat_buffer = unformat_nsh_gre_header,
365 };