b12e36c4fbc03d5fb68d74c594112c3d4458b17f
[vpp.git] / src / vnet / ipsec / ipsec_if_in.c
1 /*
2  * ipsec_if_in.c : IPSec interface input node
3  *
4  * Copyright (c) 2015 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 <vnet/vnet.h>
19 #include <vnet/api_errno.h>
20 #include <vnet/ip/ip.h>
21
22 #include <vnet/ipsec/ipsec.h>
23 #include <vnet/ipsec/esp.h>
24 #include <vnet/ipsec/ipsec_io.h>
25
26 /* Statistics (not really errors) */
27 #define foreach_ipsec_if_input_error                              \
28 _(RX, "good packets received")                                    \
29 _(DISABLED, "ipsec packets received on disabled interface")       \
30 _(NO_TUNNEL, "no matching tunnel")
31
32 static char *ipsec_if_input_error_strings[] = {
33 #define _(sym,string) string,
34   foreach_ipsec_if_input_error
35 #undef _
36 };
37
38 typedef enum
39 {
40 #define _(sym,str) IPSEC_IF_INPUT_ERROR_##sym,
41   foreach_ipsec_if_input_error
42 #undef _
43     IPSEC_IF_INPUT_N_ERROR,
44 } ipsec_if_input_error_t;
45
46
47 typedef struct
48 {
49   u32 spi;
50   u32 seq;
51 } ipsec_if_input_trace_t;
52
53 static u8 *
54 format_ipsec_if_input_trace (u8 * s, va_list * args)
55 {
56   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
57   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
58   ipsec_if_input_trace_t *t = va_arg (*args, ipsec_if_input_trace_t *);
59
60   s = format (s, "IPSec: spi %u seq %u", t->spi, t->seq);
61   return s;
62 }
63
64
65 always_inline uword
66 ipsec_if_input_inline (vlib_main_t * vm,
67                        vlib_node_runtime_t * node, vlib_frame_t * from_frame)
68 {
69   ipsec_main_t *im = &ipsec_main;
70   vnet_main_t *vnm = im->vnet_main;
71   vnet_interface_main_t *vim = &vnm->interface_main;
72
73   int is_trace = node->flags & VLIB_NODE_FLAG_TRACE;
74   u32 thread_index = vm->thread_index;
75
76   u32 n_left_from, *from;
77   u16 nexts[VLIB_FRAME_SIZE], *next;
78   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
79
80   from = vlib_frame_vector_args (from_frame);
81   n_left_from = from_frame->n_vectors;
82
83   vlib_get_buffers (vm, from, bufs, n_left_from);
84   b = bufs;
85   next = nexts;
86
87   clib_memset_u16 (nexts, im->esp4_decrypt_next_index, n_left_from);
88
89   u64 n_bytes = 0, n_packets = 0;
90   u32 n_disabled = 0, n_no_tunnel = 0;
91
92   u32 last_sw_if_index = ~0;
93   u32 last_tunnel_id = ~0;
94   u64 last_key = ~0;
95
96   vlib_combined_counter_main_t *rx_counter;
97   vlib_combined_counter_main_t *drop_counter;
98
99   rx_counter = vim->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX;
100   drop_counter = vim->combined_sw_if_counters + VNET_INTERFACE_COUNTER_DROP;
101
102   while (n_left_from >= 2)
103     {
104       u32 sw_if_index0, sw_if_index1;
105       ip4_header_t *ip0, *ip1;
106       esp_header_t *esp0, *esp1;
107       u32 len0, len1;
108       u16 buf_adv0, buf_adv1;
109       u32 tid0, tid1;
110       ipsec_tunnel_if_t *t0, *t1;
111       u64 key0, key1;
112
113       if (n_left_from >= 4)
114         {
115           CLIB_PREFETCH (b[2], CLIB_CACHE_LINE_BYTES, STORE);
116           CLIB_PREFETCH (b[2]->data, CLIB_CACHE_LINE_BYTES, LOAD);
117           CLIB_PREFETCH (b[3], CLIB_CACHE_LINE_BYTES, STORE);
118           CLIB_PREFETCH (b[3]->data, CLIB_CACHE_LINE_BYTES, LOAD);
119         }
120
121       ip0 = (ip4_header_t *) (b[0]->data + vnet_buffer (b[0])->l3_hdr_offset);
122       ip1 = (ip4_header_t *) (b[1]->data + vnet_buffer (b[1])->l3_hdr_offset);
123
124       /* NAT UDP port 4500 case, don't advance any more */
125       if (ip0->protocol == IP_PROTOCOL_UDP)
126         {
127           esp0 =
128             (esp_header_t *) ((u8 *) ip0 + ip4_header_bytes (ip0) +
129                               sizeof (udp_header_t));
130           buf_adv0 = 0;
131         }
132       else
133         {
134           esp0 = (esp_header_t *) ((u8 *) ip0 + ip4_header_bytes (ip0));
135           buf_adv0 = ip4_header_bytes (ip0);
136         }
137       if (ip1->protocol == IP_PROTOCOL_UDP)
138         {
139           esp1 =
140             (esp_header_t *) ((u8 *) ip1 + ip4_header_bytes (ip1) +
141                               sizeof (udp_header_t));
142           buf_adv1 = 0;
143         }
144       else
145         {
146           esp1 = (esp_header_t *) ((u8 *) ip1 + ip4_header_bytes (ip1));
147           buf_adv1 = ip4_header_bytes (ip1);
148         }
149
150       vlib_buffer_advance (b[0], buf_adv0);
151       vlib_buffer_advance (b[1], buf_adv1);
152
153       len0 = vlib_buffer_length_in_chain (vm, b[0]);
154       len1 = vlib_buffer_length_in_chain (vm, b[1]);
155
156       key0 = (u64) ip0->src_address.as_u32 << 32 | (u64) esp0->spi;
157       key1 = (u64) ip1->src_address.as_u32 << 32 | (u64) esp1->spi;
158
159       if (key0 == last_key)
160         {
161           tid0 = last_tunnel_id;
162         }
163       else
164         {
165           uword *p = hash_get (im->ipsec_if_pool_index_by_key, key0);
166           if (p)
167             {
168               tid0 = p[0];
169               last_tunnel_id = tid0;
170               last_key = key0;
171             }
172           else
173             {
174               n_no_tunnel++;
175               next[0] = IPSEC_INPUT_NEXT_DROP;
176               goto pkt1;
177             }
178         }
179
180       t0 = pool_elt_at_index (im->tunnel_interfaces, tid0);
181       vnet_buffer (b[0])->ipsec.sad_index = t0->input_sa_index;
182
183       if (PREDICT_TRUE (t0->hw_if_index != ~0))
184         {
185           vnet_buffer (b[0])->ipsec.flags = 0;
186           sw_if_index0 = t0->sw_if_index;
187           vnet_buffer (b[0])->sw_if_index[VLIB_RX] = sw_if_index0;
188
189           if (PREDICT_FALSE (!(t0->flags & VNET_HW_INTERFACE_FLAG_LINK_UP)))
190             {
191               vlib_increment_combined_counter
192                 (drop_counter, thread_index, sw_if_index0, 1, len0);
193               n_disabled++;
194               next[0] = IPSEC_INPUT_NEXT_DROP;
195               goto pkt1;
196             }
197
198           if (PREDICT_TRUE (sw_if_index0 == last_sw_if_index))
199             {
200               n_packets++;
201               n_bytes += len0;
202             }
203           else
204             {
205               if (n_packets)
206                 {
207                   vlib_increment_combined_counter
208                     (rx_counter, thread_index, last_sw_if_index,
209                      n_packets, n_bytes);
210                 }
211
212               last_sw_if_index = sw_if_index0;
213               n_packets = 1;
214               n_bytes = len0;
215             }
216         }
217       else
218         {
219           vnet_buffer (b[0])->ipsec.flags = IPSEC_FLAG_IPSEC_GRE_TUNNEL;
220         }
221
222     pkt1:
223       if (key1 == last_key)
224         {
225           tid1 = last_tunnel_id;
226         }
227       else
228         {
229           uword *p = hash_get (im->ipsec_if_pool_index_by_key, key1);
230           if (p)
231             {
232               tid1 = p[0];
233               last_tunnel_id = tid1;
234               last_key = key1;
235             }
236           else
237             {
238               n_no_tunnel++;
239               next[1] = IPSEC_INPUT_NEXT_DROP;
240               goto trace1;
241             }
242         }
243
244       t1 = pool_elt_at_index (im->tunnel_interfaces, tid1);
245       vnet_buffer (b[1])->ipsec.sad_index = t1->input_sa_index;
246
247       if (PREDICT_TRUE (t1->hw_if_index != ~0))
248         {
249           vnet_buffer (b[1])->ipsec.flags = 0;
250           sw_if_index1 = t1->sw_if_index;
251           vnet_buffer (b[1])->sw_if_index[VLIB_RX] = sw_if_index1;
252
253           if (PREDICT_FALSE (!(t1->flags & VNET_HW_INTERFACE_FLAG_LINK_UP)))
254             {
255               vlib_increment_combined_counter
256                 (drop_counter, thread_index, sw_if_index1, 1, len1);
257               n_disabled++;
258               next[1] = IPSEC_INPUT_NEXT_DROP;
259               goto trace1;
260             }
261
262           if (PREDICT_TRUE (sw_if_index1 == last_sw_if_index))
263             {
264               n_packets++;
265               n_bytes += len1;
266             }
267           else
268             {
269               if (n_packets)
270                 {
271                   vlib_increment_combined_counter
272                     (rx_counter, thread_index, last_sw_if_index,
273                      n_packets, n_bytes);
274                 }
275
276               last_sw_if_index = sw_if_index1;
277               n_packets = 1;
278               n_bytes = len1;
279             }
280         }
281       else
282         {
283           vnet_buffer (b[1])->ipsec.flags = IPSEC_FLAG_IPSEC_GRE_TUNNEL;
284         }
285
286     trace1:
287       if (is_trace)
288         {
289           if (PREDICT_FALSE (b[0]->flags & VLIB_BUFFER_IS_TRACED))
290             {
291               ipsec_if_input_trace_t *tr =
292                 vlib_add_trace (vm, node, b[0], sizeof (*tr));
293               tr->spi = clib_host_to_net_u32 (esp0->spi);
294               tr->seq = clib_host_to_net_u32 (esp0->seq);
295             }
296           if (PREDICT_FALSE (b[1]->flags & VLIB_BUFFER_IS_TRACED))
297             {
298               ipsec_if_input_trace_t *tr =
299                 vlib_add_trace (vm, node, b[1], sizeof (*tr));
300               tr->spi = clib_host_to_net_u32 (esp1->spi);
301               tr->seq = clib_host_to_net_u32 (esp1->seq);
302             }
303         }
304
305       /* next */
306       b += 2;
307       next += 2;
308       n_left_from -= 2;
309     }
310   while (n_left_from > 0)
311     {
312       u32 sw_if_index0;
313       ip4_header_t *ip0;
314       esp_header_t *esp0;
315       u32 len0;
316       u16 buf_adv0;
317       u32 tid0;
318       ipsec_tunnel_if_t *t0;
319       u64 key0;
320
321       ip0 = (ip4_header_t *) (b[0]->data + vnet_buffer (b[0])->l3_hdr_offset);
322
323       /* NAT UDP port 4500 case, don't advance any more */
324       if (ip0->protocol == IP_PROTOCOL_UDP)
325         {
326           esp0 =
327             (esp_header_t *) ((u8 *) ip0 + ip4_header_bytes (ip0) +
328                               sizeof (udp_header_t));
329           buf_adv0 = 0;
330         }
331       else
332         {
333           esp0 = (esp_header_t *) ((u8 *) ip0 + ip4_header_bytes (ip0));
334           buf_adv0 = ip4_header_bytes (ip0);
335         }
336
337       /* stats for the tunnel include all the data after the IP header
338          just like a norml IP-IP tunnel */
339       vlib_buffer_advance (b[0], buf_adv0);
340       len0 = vlib_buffer_length_in_chain (vm, b[0]);
341
342       key0 = (u64) ip0->src_address.as_u32 << 32 | (u64) esp0->spi;
343       if (key0 == last_key)
344         {
345           tid0 = last_tunnel_id;
346         }
347       else
348         {
349           uword *p = hash_get (im->ipsec_if_pool_index_by_key, key0);
350           if (p)
351             {
352               tid0 = p[0];
353               last_tunnel_id = tid0;
354               last_key = key0;
355             }
356           else
357             {
358               n_no_tunnel++;
359               next[0] = IPSEC_INPUT_NEXT_DROP;
360               goto trace00;
361             }
362         }
363
364       t0 = pool_elt_at_index (im->tunnel_interfaces, tid0);
365       vnet_buffer (b[0])->ipsec.sad_index = t0->input_sa_index;
366
367       if (PREDICT_TRUE (t0->hw_if_index != ~0))
368         {
369           vnet_buffer (b[0])->ipsec.flags = 0;
370           sw_if_index0 = t0->sw_if_index;
371           vnet_buffer (b[0])->sw_if_index[VLIB_RX] = sw_if_index0;
372
373           if (PREDICT_FALSE (!(t0->flags & VNET_HW_INTERFACE_FLAG_LINK_UP)))
374             {
375               vlib_increment_combined_counter
376                 (drop_counter, thread_index, sw_if_index0, 1, len0);
377               n_disabled++;
378               next[0] = IPSEC_INPUT_NEXT_DROP;
379               goto trace00;
380             }
381
382           if (PREDICT_TRUE (sw_if_index0 == last_sw_if_index))
383             {
384               n_packets++;
385               n_bytes += len0;
386             }
387           else
388             {
389               if (n_packets)
390                 {
391                   vlib_increment_combined_counter
392                     (rx_counter, thread_index, last_sw_if_index,
393                      n_packets, n_bytes);
394                 }
395
396               last_sw_if_index = sw_if_index0;
397               n_packets = 1;
398               n_bytes = len0;
399             }
400         }
401       else
402         {
403           vnet_buffer (b[0])->ipsec.flags = IPSEC_FLAG_IPSEC_GRE_TUNNEL;
404         }
405
406     trace00:
407       if (is_trace)
408         {
409           if (PREDICT_FALSE (b[0]->flags & VLIB_BUFFER_IS_TRACED))
410             {
411               ipsec_if_input_trace_t *tr =
412                 vlib_add_trace (vm, node, b[0], sizeof (*tr));
413               tr->spi = clib_host_to_net_u32 (esp0->spi);
414               tr->seq = clib_host_to_net_u32 (esp0->seq);
415             }
416         }
417
418       /* next */
419       b += 1;
420       next += 1;
421       n_left_from -= 1;
422     }
423
424   if (n_packets)
425     {
426       vlib_increment_combined_counter (rx_counter,
427                                        thread_index,
428                                        last_sw_if_index, n_packets, n_bytes);
429     }
430
431   vlib_node_increment_counter (vm, ipsec_if_input_node.index,
432                                IPSEC_IF_INPUT_ERROR_RX,
433                                from_frame->n_vectors - n_disabled);
434   vlib_node_increment_counter (vm, ipsec_if_input_node.index,
435                                IPSEC_IF_INPUT_ERROR_DISABLED, n_disabled);
436   vlib_node_increment_counter (vm, ipsec_if_input_node.index,
437                                IPSEC_IF_INPUT_ERROR_NO_TUNNEL, n_no_tunnel);
438
439   vlib_buffer_enqueue_to_next (vm, node, from, nexts, from_frame->n_vectors);
440
441   return from_frame->n_vectors;
442 }
443
444 VLIB_NODE_FN (ipsec_if_input_node) (vlib_main_t * vm,
445                                     vlib_node_runtime_t * node,
446                                     vlib_frame_t * from_frame)
447 {
448   return ipsec_if_input_inline (vm, node, from_frame);
449 }
450
451 /* *INDENT-OFF* */
452 VLIB_REGISTER_NODE (ipsec_if_input_node) = {
453   .name = "ipsec-if-input",
454   .vector_size = sizeof (u32),
455   .format_trace = format_ipsec_if_input_trace,
456   .type = VLIB_NODE_TYPE_INTERNAL,
457   .n_errors = ARRAY_LEN(ipsec_if_input_error_strings),
458   .error_strings = ipsec_if_input_error_strings,
459   .sibling_of = "ipsec4-input-feature",
460 };
461 /* *INDENT-ON* */
462
463 /*
464  * fd.io coding-style-patch-verification: ON
465  *
466  * Local Variables:
467  * eval: (c-set-style "gnu")
468  * End:
469  */