VPP-237: indent fixes in prep for checkstyle
[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
275                                               (udp0->src_port),
276                                               clib_net_to_host_u16
277                                               (udp0->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
295                                           (ip0->src_address.as_u32),
296                                           clib_net_to_host_u32
297                                           (ip0->dst_address.as_u32),
298                                           clib_net_to_host_u16
299                                           (udp0->src_port),
300                                           clib_net_to_host_u16
301                                           (udp0->dst_port));
302         }
303
304       if (PREDICT_TRUE (p0 != NULL))
305         {
306           if (p0->policy == IPSEC_POLICY_ACTION_PROTECT)
307             {
308               nc_protect++;
309               next_node_index = im->esp_encrypt_node_index;
310               vnet_buffer (b0)->output_features.ipsec_sad_index =
311                 p0->sa_index;
312               vlib_buffer_advance (b0, sizeof (ethernet_header_t));
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 if (p0->policy == IPSEC_POLICY_ACTION_BYPASS)
326             {
327               nc_bypass++;
328               next_node_index = get_next_output_feature_node_index (vnm, b0);
329               p0->counter.packets++;
330               if (is_ipv6)
331                 {
332                   p0->counter.bytes +=
333                     clib_net_to_host_u16 (ip6_0->payload_length);
334                   p0->counter.bytes += sizeof (ip6_header_t);
335                 }
336               else
337                 {
338                   p0->counter.bytes += clib_net_to_host_u16 (ip0->length);
339                 }
340             }
341           else
342             {
343               nc_discard++;
344               p0->counter.packets++;
345               if (is_ipv6)
346                 {
347                   p0->counter.bytes +=
348                     clib_net_to_host_u16 (ip6_0->payload_length);
349                   p0->counter.bytes += sizeof (ip6_header_t);
350                 }
351               else
352                 {
353                   p0->counter.bytes += clib_net_to_host_u16 (ip0->length);
354                 }
355               next_node_index = im->error_drop_node_index;
356             }
357         }
358       else
359         {
360           nc_nomatch++;
361           next_node_index = im->error_drop_node_index;
362         }
363
364     dispatch0:
365       from += 1;
366       n_left_from -= 1;
367
368       if (PREDICT_FALSE ((last_next_node_index != next_node_index)))
369         {
370           /* if this is not 1st frame */
371           if (f)
372             vlib_put_frame_to_node (vm, last_next_node_index, f);
373
374           last_next_node_index = next_node_index;
375
376           f = vlib_get_frame_to_node (vm, next_node_index);
377           to_next = vlib_frame_vector_args (f);
378         }
379
380       to_next[0] = bi0;
381       to_next += 1;
382       f->n_vectors++;
383
384       if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
385         {
386           ipsec_output_trace_t *tr =
387             vlib_add_trace (vm, node, b0, sizeof (*tr));
388           if (spd0)
389             tr->spd_id = spd0->id;
390         }
391     }
392
393   vlib_put_frame_to_node (vm, next_node_index, f);
394   vlib_node_increment_counter (vm, ipsec_output_node.index,
395                                IPSEC_OUTPUT_ERROR_POLICY_PROTECT, nc_protect);
396   vlib_node_increment_counter (vm, ipsec_output_node.index,
397                                IPSEC_OUTPUT_ERROR_POLICY_BYPASS, nc_bypass);
398   vlib_node_increment_counter (vm, ipsec_output_node.index,
399                                IPSEC_OUTPUT_ERROR_POLICY_DISCARD, nc_discard);
400   vlib_node_increment_counter (vm, ipsec_output_node.index,
401                                IPSEC_OUTPUT_ERROR_POLICY_NO_MATCH,
402                                nc_nomatch);
403   return from_frame->n_vectors;
404 }
405
406 /* *INDENT-OFF* */
407 VLIB_REGISTER_NODE (ipsec_output_node,static) = {
408   .function = ipsec_output_node_fn,
409   .name = "ipsec-output",
410   .vector_size = sizeof (u32),
411   .format_trace = format_ipsec_output_trace,
412   .type = VLIB_NODE_TYPE_INTERNAL,
413
414   .n_errors = ARRAY_LEN(ipsec_output_error_strings),
415   .error_strings = ipsec_output_error_strings,
416
417   .n_next_nodes = IPSEC_OUTPUT_N_NEXT,
418   .next_nodes = {
419 #define _(s,n) [IPSEC_OUTPUT_NEXT_##s] = n,
420     foreach_intf_output_feat
421     foreach_ipsec_output_next
422 #undef _
423   },
424 };
425 /* *INDENT-ON* */
426
427 VLIB_NODE_FUNCTION_MULTIARCH (ipsec_output_node, ipsec_output_node_fn)
428 #else /* IPSEC > 1 */
429
430 /* Dummy ipsec output node, in case when IPSec is disabled */
431
432 static uword
433 ipsec_output_node_fn (vlib_main_t * vm,
434                       vlib_node_runtime_t * node, vlib_frame_t * frame)
435 {
436   clib_warning ("IPSec disabled");
437   return 0;
438 }
439
440 /* *INDENT-OFF* */
441 VLIB_REGISTER_NODE (ipsec_output_node) = {
442   .vector_size = sizeof (u32),
443   .function = ipsec_output_node_fn,
444   .name = "ipsec-output",
445 };
446 /* *INDENT-ON* */
447 #endif
448
449 /*
450  * fd.io coding-style-patch-verification: ON
451  *
452  * Local Variables:
453  * eval: (c-set-style "gnu")
454  * End:
455  */