fib: fib api updates
[vpp.git] / src / vnet / ipsec-gre / 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 /**
16  * @file
17  * @brief L2-GRE over IPSec packet processing.
18  *
19  * Removes GRE header from the packet and sends it to the l2-input node.
20 */
21
22 #include <vlib/vlib.h>
23 #include <vnet/pg/pg.h>
24 #include <vnet/ipsec-gre/ipsec_gre.h>
25 #include <vppinfra/sparse_vec.h>
26
27 #define foreach_ipsec_gre_input_next            \
28 _(PUNT, "error-punt")                           \
29 _(DROP, "error-drop")                           \
30 _(L2_INPUT, "l2-input")
31
32 typedef enum {
33 #define _(s,n) IPSEC_GRE_INPUT_NEXT_##s,
34   foreach_ipsec_gre_input_next
35 #undef _
36   IPSEC_GRE_INPUT_N_NEXT,
37 } ipsec_gre_input_next_t;
38
39 typedef struct {
40   u32 tunnel_id;
41   u32 length;
42   ip4_address_t src;
43   ip4_address_t dst;
44 } ipsec_gre_rx_trace_t;
45
46 static u8 * format_ipsec_gre_rx_trace (u8 * s, va_list * args)
47 {
48   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
49   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
50   ipsec_gre_rx_trace_t * t = va_arg (*args, ipsec_gre_rx_trace_t *);
51
52   s = format (s, "GRE: tunnel %d len %d src %U dst %U",
53               t->tunnel_id, clib_net_to_host_u16(t->length),
54               format_ip4_address, &t->src.as_u8,
55               format_ip4_address, &t->dst.as_u8);
56   return s;
57 }
58
59 /**
60  * @brief L2-GRE over IPSec input node.
61  * @node ipsec-gre-input
62  *
63  * This node remove GRE header.
64  *
65  * @param vm         vlib_main_t corresponding to the current thread.
66  * @param node       vlib_node_runtime_t data for this node.
67  * @param from_frame vlib_frame_t whose contents should be dispatched.
68  *
69  * @par Graph mechanics: buffer metadata, next index usage
70  *
71  * <em>Uses:</em>
72  * - <code>ip->src_address</code> and <code>ip->dst_address</code>
73  *     - Match tunnel by source and destination addresses in GRE IP header.
74  *
75  * <em>Sets:</em>
76  * - <code>vnet_buffer(b)->gre.src</code>
77  *     - Save tunnel source IPv4 address.
78  * - <code>vnet_buffer(b)->gre.dst</code>
79  *     - Save tunnel destination IPv4 address.
80  * - <code>vnet_buffer(b)->sw_if_index[VLIB_RX]</code>
81  *     - Set input sw_if_index to IPSec-GRE tunnel for learning.
82  *
83  * <em>Next Index:</em>
84  * - Dispatches the packet to the l2-input node.
85 */
86 VLIB_NODE_FN (ipsec_gre_input_node) (vlib_main_t * vm,
87                  vlib_node_runtime_t * node,
88                  vlib_frame_t * from_frame)
89 {
90   ipsec_gre_main_t * igm = &ipsec_gre_main;
91   u32 n_left_from, next_index, * from, * to_next;
92   u64 cached_tunnel_key = (u64) ~0;
93   u32 cached_tunnel_sw_if_index = 0, tunnel_sw_if_index;
94   u32 tun_src0, tun_dst0;
95   u32 tun_src1, tun_dst1;
96
97   from = vlib_frame_vector_args (from_frame);
98   n_left_from = from_frame->n_vectors;
99
100   next_index = node->cached_next_index;
101
102   while (n_left_from > 0)
103     {
104       u32 n_left_to_next;
105
106       vlib_get_next_frame (vm, node, next_index,
107                            to_next, n_left_to_next);
108
109       while (n_left_from >= 4 && n_left_to_next >= 2)
110         {
111           u32 bi0, bi1;
112           vlib_buffer_t * b0, * b1;
113           gre_header_t * h0, * h1;
114           u16 version0, version1, protocol0, protocol1;
115           int verr0, verr1;
116           u32 next0, next1;
117           ip4_header_t *ip0, *ip1;
118
119           /* Prefetch next iteration. */
120           {
121             vlib_buffer_t * p2, * p3;
122
123             p2 = vlib_get_buffer (vm, from[2]);
124             p3 = vlib_get_buffer (vm, from[3]);
125
126             vlib_prefetch_buffer_header (p2, LOAD);
127             vlib_prefetch_buffer_header (p3, LOAD);
128
129             CLIB_PREFETCH (p2->data, sizeof (h0[0]), LOAD);
130             CLIB_PREFETCH (p3->data, sizeof (h1[0]), LOAD);
131           }
132
133           bi0 = from[0];
134           bi1 = from[1];
135           to_next[0] = bi0;
136           to_next[1] = bi1;
137           from += 2;
138           to_next += 2;
139           n_left_to_next -= 2;
140           n_left_from -= 2;
141
142           b0 = vlib_get_buffer (vm, bi0);
143           b1 = vlib_get_buffer (vm, bi1);
144
145           /* ip4_local hands us the ip header, not the gre header */
146           ip0 = vlib_buffer_get_current (b0);
147           ip1 = vlib_buffer_get_current (b1);
148
149           /* Save src + dst ip4 address */
150           tun_src0 = ip0->src_address.as_u32;
151           tun_dst0 = ip0->dst_address.as_u32;
152           tun_src1 = ip1->src_address.as_u32;
153           tun_dst1 = ip1->dst_address.as_u32;
154
155           vlib_buffer_advance (b0, sizeof (*ip0));
156           vlib_buffer_advance (b1, sizeof (*ip1));
157
158           h0 = vlib_buffer_get_current (b0);
159           h1 = vlib_buffer_get_current (b1);
160
161           protocol0 = clib_net_to_host_u16 (h0->protocol);
162           protocol1 = clib_net_to_host_u16 (h1->protocol);
163           if (PREDICT_TRUE(protocol0 == GRE_PROTOCOL_teb))
164             {
165               next0 = IPSEC_GRE_INPUT_NEXT_L2_INPUT;
166               b0->error = node->errors[IPSEC_GRE_ERROR_NONE];
167             }
168           else
169             {
170               b0->error = node->errors[IPSEC_GRE_ERROR_UNKNOWN_PROTOCOL];
171               next0 = IPSEC_GRE_INPUT_NEXT_DROP;
172             }
173           if (PREDICT_TRUE(protocol1 == GRE_PROTOCOL_teb))
174             {
175               next1 = IPSEC_GRE_INPUT_NEXT_L2_INPUT;
176               b1->error = node->errors[IPSEC_GRE_ERROR_NONE];
177             }
178           else
179             {
180               b1->error = node->errors[IPSEC_GRE_ERROR_UNKNOWN_PROTOCOL];
181               next1 = IPSEC_GRE_INPUT_NEXT_DROP;
182             }
183
184           version0 = clib_net_to_host_u16 (h0->flags_and_version);
185           verr0 =  version0 & GRE_VERSION_MASK;
186           version1 = clib_net_to_host_u16 (h1->flags_and_version);
187           verr1 =  version1 & GRE_VERSION_MASK;
188
189           b0->error = verr0 ? node->errors[IPSEC_GRE_ERROR_UNSUPPORTED_VERSION]
190               : b0->error;
191           next0 = verr0 ? IPSEC_GRE_INPUT_NEXT_DROP : next0;
192           b1->error = verr1 ? node->errors[IPSEC_GRE_ERROR_UNSUPPORTED_VERSION]
193               : b1->error;
194           next1 = verr1 ? IPSEC_GRE_INPUT_NEXT_DROP : next1;
195
196           /* For L2 payload set input sw_if_index to GRE tunnel for learning */
197           if (PREDICT_TRUE(next0 == IPSEC_GRE_INPUT_NEXT_L2_INPUT))
198             {
199               u64 key = ((u64)(tun_dst0) << 32) | (u64)(tun_src0);
200
201               if (cached_tunnel_key != key)
202                 {
203                   vnet_hw_interface_t * hi;
204                   ipsec_gre_tunnel_t * t;
205                   uword * p;
206
207                   p = hash_get (igm->tunnel_by_key, key);
208                   if (!p)
209                     {
210                       next0 = IPSEC_GRE_INPUT_NEXT_DROP;
211                       b0->error = node->errors[IPSEC_GRE_ERROR_NO_SUCH_TUNNEL];
212                       goto drop0;
213                     }
214                   t = pool_elt_at_index (igm->tunnels, p[0]);
215                   hi = vnet_get_hw_interface (igm->vnet_main,
216                             t->hw_if_index);
217                   tunnel_sw_if_index = hi->sw_if_index;
218                   cached_tunnel_sw_if_index = tunnel_sw_if_index;
219                 }
220               else
221                 {
222                   tunnel_sw_if_index = cached_tunnel_sw_if_index;
223                 }
224               vnet_buffer(b0)->sw_if_index[VLIB_RX] = tunnel_sw_if_index;
225             }
226
227 drop0:
228           /* For L2 payload set input sw_if_index to GRE tunnel for learning */
229           if (PREDICT_TRUE(next1 == IPSEC_GRE_INPUT_NEXT_L2_INPUT))
230             {
231               u64 key = ((u64)(tun_dst1) << 32) | (u64)(tun_src1);
232
233               if (cached_tunnel_key != key)
234                 {
235                   vnet_hw_interface_t * hi;
236                   ipsec_gre_tunnel_t * t;
237                   uword * p;
238
239                   p = hash_get (igm->tunnel_by_key, key);
240                   if (!p)
241                     {
242                       next1 = IPSEC_GRE_INPUT_NEXT_DROP;
243                       b1->error = node->errors[IPSEC_GRE_ERROR_NO_SUCH_TUNNEL];
244                       goto drop1;
245                     }
246                   t = pool_elt_at_index (igm->tunnels, p[0]);
247                   hi = vnet_get_hw_interface (igm->vnet_main,
248                             t->hw_if_index);
249                   tunnel_sw_if_index = hi->sw_if_index;
250                   cached_tunnel_sw_if_index = tunnel_sw_if_index;
251                 }
252               else
253                 {
254                   tunnel_sw_if_index = cached_tunnel_sw_if_index;
255                 }
256               vnet_buffer(b1)->sw_if_index[VLIB_RX] = tunnel_sw_if_index;
257             }
258
259 drop1:
260           if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
261             {
262               ipsec_gre_rx_trace_t *tr = vlib_add_trace (vm, node,
263                                                    b0, sizeof (*tr));
264               tr->tunnel_id = ~0;
265               tr->length = ip0->length;
266               tr->src.as_u32 = ip0->src_address.as_u32;
267               tr->dst.as_u32 = ip0->dst_address.as_u32;
268             }
269
270           if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED))
271             {
272               ipsec_gre_rx_trace_t *tr = vlib_add_trace (vm, node,
273                                                    b1, sizeof (*tr));
274               tr->tunnel_id = ~0;
275               tr->length = ip1->length;
276               tr->src.as_u32 = ip1->src_address.as_u32;
277               tr->dst.as_u32 = ip1->dst_address.as_u32;
278             }
279
280           vlib_buffer_advance (b0, sizeof (*h0));
281           vlib_buffer_advance (b1, sizeof (*h1));
282
283           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
284                                            to_next, n_left_to_next,
285                                            bi0, bi1, next0, next1);
286         }
287
288       while (n_left_from > 0 && n_left_to_next > 0)
289         {
290           u32 bi0;
291           vlib_buffer_t * b0;
292           gre_header_t * h0;
293           ip4_header_t * ip0;
294           u16 version0, protocol0;
295           int verr0;
296           u32 next0;
297           u32 tun_src0, tun_dst0;
298
299           bi0 = from[0];
300           to_next[0] = bi0;
301           from += 1;
302           to_next += 1;
303           n_left_from -= 1;
304           n_left_to_next -= 1;
305
306           b0 = vlib_get_buffer (vm, bi0);
307           ip0 = vlib_buffer_get_current (b0);
308
309           tun_src0 = ip0->src_address.as_u32;
310           tun_dst0 = ip0->dst_address.as_u32;
311
312           vlib_buffer_advance (b0, sizeof (*ip0));
313
314           h0 = vlib_buffer_get_current (b0);
315
316           protocol0 = clib_net_to_host_u16 (h0->protocol);
317           if (PREDICT_TRUE(protocol0 == GRE_PROTOCOL_teb))
318             {
319               next0 = IPSEC_GRE_INPUT_NEXT_L2_INPUT;
320               b0->error = node->errors[IPSEC_GRE_ERROR_NONE];
321             }
322           else
323             {
324               b0->error = node->errors[IPSEC_GRE_ERROR_UNKNOWN_PROTOCOL];
325               next0 = IPSEC_GRE_INPUT_NEXT_DROP;
326             }
327
328           version0 = clib_net_to_host_u16 (h0->flags_and_version);
329           verr0 =  version0 & GRE_VERSION_MASK;
330           b0->error = verr0 ? node->errors[IPSEC_GRE_ERROR_UNSUPPORTED_VERSION]
331               : b0->error;
332           next0 = verr0 ? IPSEC_GRE_INPUT_NEXT_DROP : next0;
333
334           /* For L2 payload set input sw_if_index to GRE tunnel for learning */
335           if (PREDICT_TRUE(next0 == IPSEC_GRE_INPUT_NEXT_L2_INPUT))
336             {
337               u64 key = ((u64)(tun_dst0) << 32) | (u64)(tun_src0);
338
339               if (cached_tunnel_key != key)
340                 {
341                   vnet_hw_interface_t * hi;
342                   ipsec_gre_tunnel_t * t;
343                   uword * p;
344
345                   p = hash_get (igm->tunnel_by_key, key);
346                   if (!p)
347                     {
348                       next0 = IPSEC_GRE_INPUT_NEXT_DROP;
349                       b0->error = node->errors[IPSEC_GRE_ERROR_NO_SUCH_TUNNEL];
350                       goto drop;
351                     }
352                   t = pool_elt_at_index (igm->tunnels, p[0]);
353                   hi = vnet_get_hw_interface (igm->vnet_main,
354                             t->hw_if_index);
355                   tunnel_sw_if_index = hi->sw_if_index;
356                   cached_tunnel_sw_if_index = tunnel_sw_if_index;
357                 }
358               else
359                 {
360                   tunnel_sw_if_index = cached_tunnel_sw_if_index;
361                 }
362               vnet_buffer(b0)->sw_if_index[VLIB_RX] = tunnel_sw_if_index;
363             }
364
365 drop:
366           if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
367             {
368               ipsec_gre_rx_trace_t *tr = vlib_add_trace (vm, node,
369                                                    b0, sizeof (*tr));
370               tr->tunnel_id = ~0;
371               tr->length = ip0->length;
372               tr->src.as_u32 = ip0->src_address.as_u32;
373               tr->dst.as_u32 = ip0->dst_address.as_u32;
374             }
375
376           vlib_buffer_advance (b0, sizeof (*h0));
377
378           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
379                                            to_next, n_left_to_next,
380                                            bi0, next0);
381         }
382
383       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
384     }
385   vlib_node_increment_counter (vm, ipsec_gre_input_node.index,
386                                IPSEC_GRE_ERROR_PKTS_DECAP, from_frame->n_vectors);
387   return from_frame->n_vectors;
388 }
389
390 static char * ipsec_gre_error_strings[] = {
391 #define ipsec_gre_error(n,s) s,
392 #include "error.def"
393 #undef ipsec_gre_error
394 };
395
396 VLIB_REGISTER_NODE (ipsec_gre_input_node) = {
397   .name = "ipsec-gre-input",
398   /* Takes a vector of packets. */
399   .vector_size = sizeof (u32),
400
401   .n_errors = IPSEC_GRE_N_ERROR,
402   .error_strings = ipsec_gre_error_strings,
403
404   .n_next_nodes = IPSEC_GRE_INPUT_N_NEXT,
405   .next_nodes = {
406 #define _(s,n) [IPSEC_GRE_INPUT_NEXT_##s] = n,
407     foreach_ipsec_gre_input_next
408 #undef _
409   },
410
411   .format_trace = format_ipsec_gre_rx_trace,
412 };
413
414 static clib_error_t * ipsec_gre_input_init (vlib_main_t * vm)
415 {
416   {
417     clib_error_t * error;
418     error = vlib_call_init_function (vm, ipsec_gre_init);
419     if (error)
420       clib_error_report (error);
421   }
422
423   return 0;
424 }
425
426 VLIB_INIT_FUNCTION (ipsec_gre_input_init);