979778991322ca6b7c73eee0f22cf087c13c5ce4
[vpp.git] / vnet / vnet / ipsec / ipsec_output.c
1 /*
2  * ipsec_output.c : IPSec output 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
24 #if DPDK_CRYPTO==1
25 #define ESP_NODE "dpdk-esp-encrypt"
26 #else
27 #define ESP_NODE "esp-encrypt"
28 #endif
29
30 #if IPSEC > 0
31
32 #define foreach_ipsec_output_next                \
33 _(DROP, "error-drop")                            \
34 _(ESP_ENCRYPT, ESP_NODE)
35
36 #define _(v, s) IPSEC_OUTPUT_NEXT_##v,
37 typedef enum
38 {
39   foreach_ipsec_output_next
40 #undef _
41     IPSEC_OUTPUT_N_NEXT,
42 } ipsec_output_next_t;
43
44
45 #define foreach_ipsec_output_error                   \
46  _(RX_PKTS, "IPSec pkts received")                   \
47  _(POLICY_DISCARD, "IPSec policy discard")           \
48  _(POLICY_NO_MATCH, "IPSec policy (no match)")       \
49  _(POLICY_PROTECT, "IPSec policy protect")           \
50  _(POLICY_BYPASS, "IPSec policy bypass")             \
51  _(ENCAPS_FAILED, "IPSec encapsulation failed")
52
53
54 typedef enum
55 {
56 #define _(sym,str) IPSEC_OUTPUT_ERROR_##sym,
57   foreach_ipsec_output_error
58 #undef _
59     IPSEC_DECAP_N_ERROR,
60 } ipsec_output_error_t;
61
62 static char *ipsec_output_error_strings[] = {
63 #define _(sym,string) string,
64   foreach_ipsec_output_error
65 #undef _
66 };
67
68 static vlib_node_registration_t ipsec_output_ip4_node;
69 static vlib_node_registration_t ipsec_output_ip6_node;
70
71 typedef struct
72 {
73   u32 spd_id;
74 } ipsec_output_trace_t;
75
76 /* packet trace format function */
77 static u8 *
78 format_ipsec_output_trace (u8 * s, va_list * args)
79 {
80   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
81   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
82   ipsec_output_trace_t *t = va_arg (*args, ipsec_output_trace_t *);
83
84   if (t->spd_id != ~0)
85     {
86       s = format (s, "spd %u ", t->spd_id);
87     }
88   else
89     {
90       s = format (s, "no spd");
91     }
92   return s;
93 }
94
95 always_inline ipsec_policy_t *
96 ipsec_output_policy_match (ipsec_spd_t * spd, u8 pr, u32 la, u32 ra, u16 lp,
97                            u16 rp)
98 {
99   ipsec_policy_t *p;
100   u32 *i;
101
102   if (!spd)
103     return 0;
104
105   vec_foreach (i, spd->ipv4_outbound_policies)
106   {
107     p = pool_elt_at_index (spd->policies, *i);
108     if (PREDICT_FALSE (p->protocol && (p->protocol != pr)))
109       continue;
110
111     if (la < clib_net_to_host_u32 (p->laddr.start.ip4.as_u32))
112       continue;
113
114     if (la > clib_net_to_host_u32 (p->laddr.stop.ip4.as_u32))
115       continue;
116
117     if (ra < clib_net_to_host_u32 (p->raddr.start.ip4.as_u32))
118       continue;
119
120     if (ra > clib_net_to_host_u32 (p->raddr.stop.ip4.as_u32))
121       continue;
122
123     if (PREDICT_FALSE ((pr != IP_PROTOCOL_TCP) && (pr != IP_PROTOCOL_UDP)))
124       return p;
125
126     if (lp < p->lport.start)
127       continue;
128
129     if (lp > p->lport.stop)
130       continue;
131
132     if (rp < p->rport.start)
133       continue;
134
135     if (rp > p->rport.stop)
136       continue;
137
138     return p;
139   }
140   return 0;
141 }
142
143 always_inline uword
144 ip6_addr_match_range (ip6_address_t * a, ip6_address_t * la,
145                       ip6_address_t * ua)
146 {
147   if ((memcmp (a->as_u64, la->as_u64, 2 * sizeof (u64)) >= 0) &&
148       (memcmp (a->as_u64, ua->as_u64, 2 * sizeof (u64)) <= 0))
149     return 1;
150   return 0;
151 }
152
153 always_inline ipsec_policy_t *
154 ipsec_output_ip6_policy_match (ipsec_spd_t * spd,
155                                ip6_address_t * la,
156                                ip6_address_t * ra, u16 lp, u16 rp, u8 pr)
157 {
158   ipsec_policy_t *p;
159   u32 *i;
160
161   if (!spd)
162     return 0;
163
164   vec_foreach (i, spd->ipv6_outbound_policies)
165   {
166     p = pool_elt_at_index (spd->policies, *i);
167     if (PREDICT_FALSE (p->protocol && (p->protocol != pr)))
168       continue;
169
170     if (!ip6_addr_match_range (ra, &p->raddr.start.ip6, &p->raddr.stop.ip6))
171       continue;
172
173     if (!ip6_addr_match_range (la, &p->laddr.start.ip6, &p->laddr.stop.ip6))
174       continue;
175
176     if (PREDICT_FALSE ((pr != IP_PROTOCOL_TCP) && (pr != IP_PROTOCOL_UDP)))
177       return p;
178
179     if (lp < p->lport.start)
180       continue;
181
182     if (lp > p->lport.stop)
183       continue;
184
185     if (rp < p->rport.start)
186       continue;
187
188     if (rp > p->rport.stop)
189       continue;
190
191     return p;
192   }
193
194   return 0;
195 }
196
197 static inline uword
198 ipsec_output_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
199                      vlib_frame_t * from_frame, int is_ipv6)
200 {
201   ipsec_main_t *im = &ipsec_main;
202
203   u32 *from, *to_next = 0;
204   u32 n_left_from, sw_if_index0, last_sw_if_index = (u32) ~ 0;
205   u32 next_node_index = (u32) ~ 0, last_next_node_index = (u32) ~ 0;
206   vlib_frame_t *f = 0;
207   u32 spd_index0 = ~0;
208   ipsec_spd_t *spd0 = 0;
209   u64 nc_protect = 0, nc_bypass = 0, nc_discard = 0, nc_nomatch = 0;
210
211   from = vlib_frame_vector_args (from_frame);
212   n_left_from = from_frame->n_vectors;
213
214   while (n_left_from > 0)
215     {
216       u32 bi0;
217       vlib_buffer_t *b0;
218       ipsec_policy_t *p0;
219       ip4_header_t *ip0;
220       ip6_header_t *ip6_0 = 0;
221       udp_header_t *udp0;
222       u32 iph_offset = 0;
223
224       bi0 = from[0];
225       b0 = vlib_get_buffer (vm, bi0);
226       sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_TX];
227       iph_offset = vnet_buffer (b0)->ip.save_rewrite_length;
228       ip0 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b0)
229                               + iph_offset);
230
231       /* lookup for SPD only if sw_if_index is changed */
232       if (PREDICT_FALSE (last_sw_if_index != sw_if_index0))
233         {
234           uword *p = hash_get (im->spd_index_by_sw_if_index, sw_if_index0);
235           ASSERT (p);
236           spd_index0 = p[0];
237           spd0 = pool_elt_at_index (im->spds, spd_index0);
238           last_sw_if_index = sw_if_index0;
239         }
240
241       if (is_ipv6)
242         {
243           ip6_0 = (ip6_header_t *) ((u8 *) vlib_buffer_get_current (b0)
244                                     + iph_offset);
245
246           udp0 = ip6_next_header (ip6_0);
247 #if 0
248           clib_warning
249             ("packet received from %U port %u to %U port %u spd_id %u",
250              format_ip6_address, &ip6_0->src_address,
251              clib_net_to_host_u16 (udp0->src_port), format_ip6_address,
252              &ip6_0->dst_address, clib_net_to_host_u16 (udp0->dst_port),
253              spd0->id);
254 #endif
255
256           p0 = ipsec_output_ip6_policy_match (spd0,
257                                               &ip6_0->src_address,
258                                               &ip6_0->dst_address,
259                                               clib_net_to_host_u16
260                                               (udp0->src_port),
261                                               clib_net_to_host_u16
262                                               (udp0->dst_port),
263                                               ip6_0->protocol);
264         }
265       else
266         {
267           udp0 = (udp_header_t *) ((u8 *) ip0 + ip4_header_bytes (ip0));
268
269 #if 0
270           clib_warning ("packet received from %U to %U port %u",
271                         format_ip4_address, ip0->src_address.as_u8,
272                         format_ip4_address, ip0->dst_address.as_u8,
273                         clib_net_to_host_u16 (udp0->dst_port));
274           clib_warning ("sw_if_index0 %u spd_index0 %u spd_id %u",
275                         sw_if_index0, spd_index0, spd0->id);
276 #endif
277
278           p0 = ipsec_output_policy_match (spd0, ip0->protocol,
279                                           clib_net_to_host_u32
280                                           (ip0->src_address.as_u32),
281                                           clib_net_to_host_u32
282                                           (ip0->dst_address.as_u32),
283                                           clib_net_to_host_u16
284                                           (udp0->src_port),
285                                           clib_net_to_host_u16
286                                           (udp0->dst_port));
287         }
288
289       if (PREDICT_TRUE (p0 != NULL))
290         {
291           if (p0->policy == IPSEC_POLICY_ACTION_PROTECT)
292             {
293               nc_protect++;
294               next_node_index = im->esp_encrypt_node_index;
295               vnet_buffer (b0)->ipsec.sad_index = p0->sa_index;
296               vlib_buffer_advance (b0, iph_offset);
297               p0->counter.packets++;
298               if (is_ipv6)
299                 {
300                   p0->counter.bytes +=
301                     clib_net_to_host_u16 (ip6_0->payload_length);
302                   p0->counter.bytes += sizeof (ip6_header_t);
303                 }
304               else
305                 {
306                   p0->counter.bytes += clib_net_to_host_u16 (ip0->length);
307                 }
308             }
309           else if (p0->policy == IPSEC_POLICY_ACTION_BYPASS)
310             {
311               nc_bypass++;
312               next_node_index = get_next_output_feature_node_index (b0, node);
313               p0->counter.packets++;
314               if (is_ipv6)
315                 {
316                   p0->counter.bytes +=
317                     clib_net_to_host_u16 (ip6_0->payload_length);
318                   p0->counter.bytes += sizeof (ip6_header_t);
319                 }
320               else
321                 {
322                   p0->counter.bytes += clib_net_to_host_u16 (ip0->length);
323                 }
324             }
325           else
326             {
327               nc_discard++;
328               p0->counter.packets++;
329               if (is_ipv6)
330                 {
331                   p0->counter.bytes +=
332                     clib_net_to_host_u16 (ip6_0->payload_length);
333                   p0->counter.bytes += sizeof (ip6_header_t);
334                 }
335               else
336                 {
337                   p0->counter.bytes += clib_net_to_host_u16 (ip0->length);
338                 }
339               next_node_index = im->error_drop_node_index;
340             }
341         }
342       else
343         {
344           nc_nomatch++;
345           next_node_index = im->error_drop_node_index;
346         }
347
348       from += 1;
349       n_left_from -= 1;
350
351       if (PREDICT_FALSE ((last_next_node_index != next_node_index) || f == 0))
352         {
353           /* if this is not 1st frame */
354           if (f)
355             vlib_put_frame_to_node (vm, last_next_node_index, f);
356
357           last_next_node_index = next_node_index;
358
359           f = vlib_get_frame_to_node (vm, next_node_index);
360           to_next = vlib_frame_vector_args (f);
361         }
362
363       to_next[0] = bi0;
364       to_next += 1;
365       f->n_vectors++;
366
367       if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
368         {
369           ipsec_output_trace_t *tr =
370             vlib_add_trace (vm, node, b0, sizeof (*tr));
371           if (spd0)
372             tr->spd_id = spd0->id;
373         }
374     }
375
376   vlib_put_frame_to_node (vm, next_node_index, f);
377   vlib_node_increment_counter (vm, node->node_index,
378                                IPSEC_OUTPUT_ERROR_POLICY_PROTECT, nc_protect);
379   vlib_node_increment_counter (vm, node->node_index,
380                                IPSEC_OUTPUT_ERROR_POLICY_BYPASS, nc_bypass);
381   vlib_node_increment_counter (vm, node->node_index,
382                                IPSEC_OUTPUT_ERROR_POLICY_DISCARD, nc_discard);
383   vlib_node_increment_counter (vm, node->node_index,
384                                IPSEC_OUTPUT_ERROR_POLICY_NO_MATCH,
385                                nc_nomatch);
386   return from_frame->n_vectors;
387 }
388
389 static uword
390 ipsec_output_ip4_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
391                           vlib_frame_t * frame)
392 {
393   return ipsec_output_inline (vm, node, frame, 0);
394 }
395
396 /* *INDENT-OFF* */
397 VLIB_REGISTER_NODE (ipsec_output_ip4_node,static) = {
398   .function = ipsec_output_ip4_node_fn,
399   .name = "ipsec-output-ip4",
400   .vector_size = sizeof (u32),
401   .format_trace = format_ipsec_output_trace,
402   .type = VLIB_NODE_TYPE_INTERNAL,
403
404   .n_errors = ARRAY_LEN(ipsec_output_error_strings),
405   .error_strings = ipsec_output_error_strings,
406
407   .n_next_nodes = IPSEC_OUTPUT_N_NEXT,
408   .next_nodes = {
409 #define _(s,n) [IPSEC_OUTPUT_NEXT_##s] = n,
410     foreach_ipsec_output_next
411 #undef _
412   },
413 };
414 /* *INDENT-ON* */
415
416 VLIB_NODE_FUNCTION_MULTIARCH (ipsec_output_ip4_node, ipsec_output_ip4_node_fn)
417      static uword
418        ipsec_output_ip6_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
419                                  vlib_frame_t * frame)
420 {
421   return ipsec_output_inline (vm, node, frame, 1);
422 }
423
424 /* *INDENT-OFF* */
425 VLIB_REGISTER_NODE (ipsec_output_ip6_node,static) = {
426   .function = ipsec_output_ip6_node_fn,
427   .name = "ipsec-output-ip6",
428   .vector_size = sizeof (u32),
429   .format_trace = format_ipsec_output_trace,
430   .type = VLIB_NODE_TYPE_INTERNAL,
431
432   .n_errors = ARRAY_LEN(ipsec_output_error_strings),
433   .error_strings = ipsec_output_error_strings,
434
435   .n_next_nodes = IPSEC_OUTPUT_N_NEXT,
436   .next_nodes = {
437 #define _(s,n) [IPSEC_OUTPUT_NEXT_##s] = n,
438     foreach_ipsec_output_next
439 #undef _
440   },
441 };
442 /* *INDENT-ON* */
443
444 VLIB_NODE_FUNCTION_MULTIARCH (ipsec_output_ip6_node, ipsec_output_ip6_node_fn)
445 #else /* IPSEC > 1 */
446
447 /* Dummy ipsec output node, in case when IPSec is disabled */
448
449 static uword
450 ipsec_output_node_fn (vlib_main_t * vm,
451                       vlib_node_runtime_t * node, vlib_frame_t * frame)
452 {
453   clib_warning ("IPSec disabled");
454   return 0;
455 }
456
457 /* *INDENT-OFF* */
458 VLIB_REGISTER_NODE (ipsec_output_node) = {
459   .vector_size = sizeof (u32),
460   .function = ipsec_output_node_fn,
461   .name = "ipsec-output-ip4",
462 };
463
464 VLIB_REGISTER_NODE (ipsec_output_node) = {
465   .vector_size = sizeof (u32),
466   .function = ipsec_output_node_fn,
467   .name = "ipsec-output-ip6",
468 };
469 /* *INDENT-ON* */
470 #endif
471
472 /*
473  * fd.io coding-style-patch-verification: ON
474  *
475  * Local Variables:
476  * eval: (c-set-style "gnu")
477  * End:
478  */