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