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