FD.io VPP  v19.08.3-2-gbabecb413
Vector Packet Processing
ipsec_tun_in.c
Go to the documentation of this file.
1 /*
2  * ipsec_tun_protect_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 #include <vnet/ipsec/ipsec_punt.h>
26 #include <vnet/ipsec/ipsec_tun.h>
27 #include <vnet/ip/ip4_input.h>
28 
29 /* Statistics (not really errors) */
30 #define foreach_ipsec_tun_protect_input_error \
31  _(RX, "good packets received") \
32  _(DISABLED, "ipsec packets received on disabled interface") \
33  _(NO_TUNNEL, "no matching tunnel") \
34  _(TUNNEL_MISMATCH, "SPI-tunnel mismatch") \
35  _(NAT_KEEPALIVE, "NAT Keepalive") \
36  _(TOO_SHORT, "Too Short") \
37  _(SPI_0, "SPI 0")
38 
40 #define _(sym,string) string,
42 #undef _
43 };
44 
45 typedef enum
46 {
47 #define _(sym,str) IPSEC_TUN_PROTECT_INPUT_ERROR_##sym,
49 #undef _
52 
53 typedef enum ipsec_tun_next_t_
54 {
55 #define _(v, s) IPSEC_TUN_PROTECT_NEXT_##v,
57 #undef _
61 
62 typedef struct
63 {
64  union
65  {
66  ipsec4_tunnel_key_t key4;
67  ipsec6_tunnel_key_t key6;
68  };
72 
73 static u8 *
75 {
76  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
77  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
79  va_arg (*args, ipsec_tun_protect_input_trace_t *);
80 
81  if (t->is_ip6)
82  s = format (s, "IPSec: %U seq %u",
84  else
85  s = format (s, "IPSec: %U seq %u",
87  return s;
88 }
89 
92  vlib_buffer_t * b,
93  const esp_header_t * esp, const ip4_header_t * ip4)
94 {
95  if (PREDICT_FALSE (0 == esp->spi))
96  {
97  b->error = node->errors[IPSEC_TUN_PROTECT_INPUT_ERROR_SPI_0];
98  b->punt_reason = ipsec_punt_reason[(ip4->protocol == IP_PROTOCOL_UDP ?
99  IPSEC_PUNT_IP4_SPI_UDP_0 :
100  IPSEC_PUNT_IP4_NO_SUCH_TUNNEL)];
101  }
102  else
103  {
104  b->error = node->errors[IPSEC_TUN_PROTECT_INPUT_ERROR_NO_TUNNEL];
105  b->punt_reason = ipsec_punt_reason[IPSEC_PUNT_IP4_NO_SUCH_TUNNEL];
106  }
107  return IPSEC_INPUT_NEXT_PUNT;
108 }
109 
112  vlib_buffer_t * b, const esp_header_t * esp)
113 {
114  b->error = node->errors[IPSEC_TUN_PROTECT_INPUT_ERROR_NO_TUNNEL];
115  b->punt_reason = ipsec_punt_reason[IPSEC_PUNT_IP6_NO_SUCH_TUNNEL];
116 
117  return (IPSEC_INPUT_NEXT_PUNT);
118 }
119 
122  vlib_frame_t * from_frame, int is_ip6)
123 {
124  ipsec_main_t *im = &ipsec_main;
125  vnet_main_t *vnm = im->vnet_main;
127 
128  int is_trace = node->flags & VLIB_NODE_FLAG_TRACE;
129  u32 thread_index = vm->thread_index;
130 
131  u32 n_left_from, *from;
132  u16 nexts[VLIB_FRAME_SIZE], *next;
133  vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
134 
135  from = vlib_frame_vector_args (from_frame);
136  n_left_from = from_frame->n_vectors;
137 
138  vlib_get_buffers (vm, from, bufs, n_left_from);
139  b = bufs;
140  next = nexts;
141 
142  clib_memset_u16 (nexts, im->esp4_decrypt_next_index, n_left_from);
143 
144  u64 n_bytes = 0, n_packets = 0;
145  u32 n_disabled = 0, n_no_tunnel = 0;
146 
147  u32 last_sw_if_index = ~0;
148  ipsec_tun_lkup_result_t last_result = {
149  .tun_index = ~0
150  };
151  ipsec4_tunnel_key_t last_key4;
152  ipsec6_tunnel_key_t last_key6;
153 
154  vlib_combined_counter_main_t *rx_counter;
155  vlib_combined_counter_main_t *drop_counter;
156  ipsec_tun_protect_t *itp0;
157 
158  if (is_ip6)
159  clib_memset (&last_key6, 0xff, sizeof (last_key6));
160  else
161  last_key4.as_u64 = ~0;
162 
165 
166  while (n_left_from > 0)
167  {
168  u32 sw_if_index0, len0, hdr_sz0;
170  ipsec4_tunnel_key_t key40;
171  ipsec6_tunnel_key_t key60;
172  ip4_header_t *ip40;
173  ip6_header_t *ip60;
174  esp_header_t *esp0;
175  u16 buf_rewind0;
176 
177  ip40 =
178  (ip4_header_t *) (b[0]->data + vnet_buffer (b[0])->l3_hdr_offset);
179 
180  if (is_ip6)
181  {
182  ip60 = (ip6_header_t *) ip40;
183  esp0 = (esp_header_t *) (ip60 + 1);
184  hdr_sz0 = sizeof (ip6_header_t);
185  }
186  else
187  {
188  /* NAT UDP port 4500 case, don't advance any more */
189  if (ip40->protocol == IP_PROTOCOL_UDP)
190  {
191  esp0 =
192  (esp_header_t *) ((u8 *) ip40 + ip4_header_bytes (ip40) +
193  sizeof (udp_header_t));
194  hdr_sz0 = 0;
195  buf_rewind0 = ip4_header_bytes (ip40) + sizeof (udp_header_t);
196  }
197  else
198  {
199  esp0 = (esp_header_t *) ((u8 *) ip40 + ip4_header_bytes (ip40));
200  buf_rewind0 = hdr_sz0 = ip4_header_bytes (ip40);
201  }
202  }
203 
204  /* stats for the tunnel include all the data after the IP header
205  just like a norml IP-IP tunnel */
206  vlib_buffer_advance (b[0], hdr_sz0);
207  len0 = vlib_buffer_length_in_chain (vm, b[0]);
208 
209  if (len0 < sizeof (esp_header_t))
210  {
211  if (esp0->spi_bytes[0] == 0xff)
212  b[0]->error =
213  node->errors[IPSEC_TUN_PROTECT_INPUT_ERROR_NAT_KEEPALIVE];
214  else
215  b[0]->error =
216  node->errors[IPSEC_TUN_PROTECT_INPUT_ERROR_TOO_SHORT];
217 
218  next[0] = IPSEC_INPUT_NEXT_DROP;
219  goto trace00;
220  }
221 
222  if (is_ip6)
223  {
224  key60.remote_ip = ip60->src_address;
225  key60.spi = esp0->spi;
226 
227  if (memcmp (&key60, &last_key6, sizeof (last_key6)) == 0)
228  {
229  itr0 = last_result;
230  }
231  else
232  {
233  uword *p = hash_get_mem (im->tun6_protect_by_key, &key60);
234  if (p)
235  {
236  itr0.as_u64 = p[0];
237  last_result = itr0;
238  clib_memcpy_fast (&last_key6, &key60, sizeof (key60));
239  }
240  else
241  {
242  next[0] = ipsec_ip6_if_no_tunnel (node, b[0], esp0);
243  n_no_tunnel++;
244  goto trace00;
245  }
246  }
247  }
248  else
249  {
250  key40.remote_ip = ip40->src_address;
251  key40.spi = esp0->spi;
252 
253  if (key40.as_u64 == last_key4.as_u64)
254  {
255  itr0 = last_result;
256  }
257  else
258  {
259  uword *p = hash_get (im->tun4_protect_by_key, key40.as_u64);
260  if (p)
261  {
262  itr0.as_u64 = p[0];
263  last_result = itr0;
264  last_key4.as_u64 = key40.as_u64;
265  }
266  else
267  {
268  next[0] = ipsec_ip4_if_no_tunnel (node, b[0], esp0, ip40);
269  vlib_buffer_advance (b[0], -buf_rewind0);
270  n_no_tunnel++;
271  goto trace00;
272  }
273  }
274  }
275 
277  vnet_buffer (b[0])->ipsec.sad_index = itr0.sa_index;
278  vnet_buffer (b[0])->ipsec.protect_index = itr0.tun_index;
279 
280  sw_if_index0 = itp0->itp_sw_if_index;
281  vnet_buffer (b[0])->sw_if_index[VLIB_RX] = sw_if_index0;
282 
283  if (PREDICT_FALSE (!vnet_sw_interface_is_admin_up (vnm, sw_if_index0)))
284  {
286  (drop_counter, thread_index, sw_if_index0, 1, len0);
287  n_disabled++;
288  b[0]->error = node->errors[IPSEC_TUN_PROTECT_INPUT_ERROR_DISABLED];
289  next[0] = IPSEC_INPUT_NEXT_DROP;
290  goto trace00;
291  }
292  else
293  {
294  if (PREDICT_TRUE (sw_if_index0 == last_sw_if_index))
295  {
296  n_packets++;
297  n_bytes += len0;
298  }
299  else
300  {
301  if (n_packets && !(itp0->itp_flags & IPSEC_PROTECT_ENCAPED))
302  {
304  (rx_counter, thread_index, last_sw_if_index,
305  n_packets, n_bytes);
306  }
307 
308  last_sw_if_index = sw_if_index0;
309  n_packets = 1;
310  n_bytes = len0;
311  }
312 
313  /*
314  * compare the packet's outer IP headers to that of the tunnels
315  */
316  if (is_ip6)
317  {
318  if (PREDICT_FALSE
320  (&itp0->itp_crypto.dst, &ip60->src_address)
322  &ip60->dst_address)))
323  {
324  b[0]->error =
325  node->errors
326  [IPSEC_TUN_PROTECT_INPUT_ERROR_TUNNEL_MISMATCH];
327  next[0] = IPSEC_INPUT_NEXT_DROP;
328  goto trace00;
329  }
330  }
331  else
332  {
333  if (PREDICT_FALSE
335  (&itp0->itp_crypto.dst, &ip40->src_address)
337  &ip40->dst_address)))
338  {
339  b[0]->error =
340  node->errors
341  [IPSEC_TUN_PROTECT_INPUT_ERROR_TUNNEL_MISMATCH];
342  next[0] = IPSEC_INPUT_NEXT_DROP;
343  goto trace00;
344  }
345  }
346 
347  /*
348  * There are two encap possibilities
349  * 1) the tunnel and ths SA are prodiving encap, i.e. it's
350  * MAC | SA-IP | TUN-IP | ESP | PAYLOAD
351  * implying the SA is in tunnel mode (on a tunnel interface)
352  * 2) only the tunnel provides encap
353  * MAC | TUN-IP | ESP | PAYLOAD
354  * implying the SA is in transport mode.
355  *
356  * For 2) we need only strip the tunnel encap and we're good.
357  * since the tunnel and crypto ecnap (int the tun=protect
358  * object) are the same and we verified above that these match
359  * for 1) we need to strip the SA-IP outer headers, to
360  * reveal the tunnel IP and then check that this matches
361  * the configured tunnel. this we can;t do here since it
362  * involves a lookup in the per-tunnel-type DB - so ship
363  * the packet to the tunnel-types provided node to do that
364  */
366  }
367  trace00:
368  if (PREDICT_FALSE (is_trace))
369  {
370  if (b[0]->flags & VLIB_BUFFER_IS_TRACED)
371  {
373  vlib_add_trace (vm, node, b[0], sizeof (*tr));
374  if (is_ip6)
375  clib_memcpy (&tr->key6, &key60, sizeof (tr->key6));
376  else
377  clib_memcpy (&tr->key4, &key40, sizeof (tr->key4));
378  tr->is_ip6 = is_ip6;
379  tr->seq =
380  len0 >=
381  sizeof (*esp0) ? clib_host_to_net_u32 (esp0->seq) : ~0;
382  }
383  }
384 
385  /* next */
386  b += 1;
387  next += 1;
388  n_left_from -= 1;
389  }
390 
391  if (n_packets && !(itp0->itp_flags & IPSEC_PROTECT_ENCAPED))
392  {
394  thread_index,
395  last_sw_if_index, n_packets, n_bytes);
396  }
397 
399  IPSEC_TUN_PROTECT_INPUT_ERROR_RX,
400  from_frame->n_vectors - (n_disabled +
401  n_no_tunnel));
402 
403  vlib_buffer_enqueue_to_next (vm, node, from, nexts, from_frame->n_vectors);
404 
405  return from_frame->n_vectors;
406 }
407 
409  vlib_node_runtime_t * node,
410  vlib_frame_t * from_frame)
411 {
412  return ipsec_tun_protect_input_inline (vm, node, from_frame,
413  0 /* is_ip6 */ );
414 }
415 
416 /* *INDENT-OFF* */
418  .name = "ipsec4-tun-input",
419  .vector_size = sizeof (u32),
420  .format_trace = format_ipsec_tun_protect_input_trace,
423  .error_strings = ipsec_tun_protect_input_error_strings,
424  .n_next_nodes = IPSEC_TUN_PROTECT_N_NEXT,
425  .next_nodes = {
426  [IPSEC_TUN_PROTECT_NEXT_DROP] = "ip4-drop",
427  [IPSEC_TUN_PROTECT_NEXT_PUNT] = "punt-dispatch",
428  [IPSEC_TUN_PROTECT_NEXT_DECRYPT] = "esp4-decrypt-tun",
429  }
430 };
431 /* *INDENT-ON* */
432 
434  vlib_node_runtime_t * node,
435  vlib_frame_t * from_frame)
436 {
437  return ipsec_tun_protect_input_inline (vm, node, from_frame,
438  1 /* is_ip6 */ );
439 }
440 
441 /* *INDENT-OFF* */
443  .name = "ipsec6-tun-input",
444  .vector_size = sizeof (u32),
445  .format_trace = format_ipsec_tun_protect_input_trace,
448  .error_strings = ipsec_tun_protect_input_error_strings,
449  .n_next_nodes = IPSEC_TUN_PROTECT_N_NEXT,
450  .next_nodes = {
451  [IPSEC_TUN_PROTECT_NEXT_DROP] = "ip6-drop",
452  [IPSEC_TUN_PROTECT_NEXT_PUNT] = "punt-dispatch",
453  [IPSEC_TUN_PROTECT_NEXT_DECRYPT] = "esp6-decrypt-tun",
454  }
455 };
456 /* *INDENT-ON* */
457 
458 /*
459  * fd.io coding-style-patch-verification: ON
460  *
461  * Local Variables:
462  * eval: (c-set-style "gnu")
463  * End:
464  */
static u16 ipsec_ip6_if_no_tunnel(vlib_node_runtime_t *node, vlib_buffer_t *b, const esp_header_t *esp)
Definition: ipsec_tun_in.c:111
u32 flags
Definition: vhost_user.h:141
#define CLIB_UNUSED(x)
Definition: clib.h:83
uword * tun6_protect_by_key
Definition: ipsec.h:119
static void vlib_increment_combined_counter(vlib_combined_counter_main_t *cm, u32 thread_index, u32 index, u64 n_packets, u64 n_bytes)
Increment a combined counter.
Definition: counter.h:220
ip4_address_t src_address
Definition: ip4_packet.h:170
ipsec_tun_protect_input_error_t
Definition: ipsec_tun_in.c:45
ipsec6_tunnel_key_t key6
Definition: ipsec_tun_in.c:67
vnet_interface_main_t interface_main
Definition: vnet.h:56
#define PREDICT_TRUE(x)
Definition: clib.h:113
unsigned long u64
Definition: types.h:89
#define clib_memcpy_fast(a, b, c)
Definition: string.h:81
clib_memset(h->entries, 0, sizeof(h->entries[0]) *entries)
ipsec_tun_next_t_
Definition: ipsec_tun_in.c:53
u32 thread_index
Definition: main.h:218
u8 * format_ipsec6_tunnel_key(u8 *s, va_list *args)
Definition: ipsec_format.c:419
u8 * format(u8 *s, const char *fmt,...)
Definition: format.c:424
u8 data[128]
Definition: ipsec.api:251
#define VLIB_NODE_FN(node)
Definition: node.h:202
vlib_error_t * errors
Vector of errors for this node.
Definition: node.h:470
static uword vlib_buffer_length_in_chain(vlib_main_t *vm, vlib_buffer_t *b)
Get length in bytes of the buffer chain.
Definition: buffer_funcs.h:366
ip6_address_t src_address
Definition: ip6_packet.h:383
static u16 ipsec_ip4_if_no_tunnel(vlib_node_runtime_t *node, vlib_buffer_t *b, const esp_header_t *esp, const ip4_header_t *ip4)
Definition: ipsec_tun_in.c:91
unsigned char u8
Definition: types.h:56
ipsec4_tunnel_key_t key4
Definition: ipsec_tun_in.c:66
#define clib_memcpy(d, s, n)
Definition: string.h:180
ip46_address_t src
Definition: ipsec_tun.h:28
ipsec_main_t ipsec_main
Definition: ipsec.c:28
#define always_inline
Definition: clib.h:99
ip4_address_t dst_address
Definition: ip4_packet.h:170
vlib_combined_counter_main_t * combined_sw_if_counters
Definition: interface.h:846
u32 esp4_decrypt_next_index
Definition: ipsec.h:133
vlib_node_registration_t ipsec4_tun_input_node
(constructor) VLIB_REGISTER_NODE (ipsec4_tun_input_node)
Definition: ipsec_tun_in.c:417
unsigned int u32
Definition: types.h:88
#define VLIB_FRAME_SIZE
Definition: node.h:378
vl_api_fib_path_type_t type
Definition: fib_types.api:123
vlib_error_t error
Error code for buffers to be enqueued to error handler.
Definition: buffer.h:136
#define hash_get(h, key)
Definition: hash.h:249
#define pool_elt_at_index(p, i)
Returns pointer to element at given index.
Definition: pool.h:514
static_always_inline int ip46_address_is_equal_v6(const ip46_address_t *ip46, const ip6_address_t *ip6)
Definition: ip6_packet.h:106
vnet_main_t * vnet_main
Definition: ipsec.h:108
unsigned short u16
Definition: types.h:57
#define PREDICT_FALSE(x)
Definition: clib.h:112
u32 node_index
Node index.
Definition: node.h:496
static void vlib_node_increment_counter(vlib_main_t *vm, u32 node_index, u32 counter_index, u64 increment)
Definition: node_funcs.h:1150
u32 punt_reason
Definition: buffer.h:149
vlib_punt_reason_t ipsec_punt_reason[IPSEC_PUNT_N_REASONS]
Definition: ipsec_punt.c:23
#define VLIB_REGISTER_NODE(x,...)
Definition: node.h:169
u16 n_vectors
Definition: node.h:397
vlib_main_t * vm
Definition: buffer.c:323
static_always_inline void vlib_buffer_enqueue_to_next(vlib_main_t *vm, vlib_node_runtime_t *node, u32 *buffers, u16 *nexts, uword count)
Definition: buffer_node.h:332
u8 spi_bytes[4]
Definition: esp.h:27
enum ipsec_tun_next_t_ ipsec_tun_next_t
#define ARRAY_LEN(x)
Definition: clib.h:63
ip46_address_t dst
Definition: ipsec_tun.h:29
ipsec_tun_protect_t * ipsec_protect_pool
Pool of tunnel protection objects.
Definition: ipsec_tun.c:25
u32 seq
Definition: esp.h:29
static void vlib_buffer_advance(vlib_buffer_t *b, word l)
Advance current data pointer by the supplied (signed!) amount.
Definition: buffer.h:248
static uword ipsec_tun_protect_input_inline(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *from_frame, int is_ip6)
Definition: ipsec_tun_in.c:121
static char * ipsec_tun_protect_input_error_strings[]
Definition: ipsec_tun_in.c:39
u32 spi
Definition: esp.h:26
static uword vnet_sw_interface_is_admin_up(vnet_main_t *vnm, u32 sw_if_index)
static void * vlib_add_trace(vlib_main_t *vm, vlib_node_runtime_t *r, vlib_buffer_t *b, u32 n_data_bytes)
Definition: trace_funcs.h:55
static_always_inline void clib_memset_u16(void *p, u16 val, uword count)
Definition: string.h:378
ipsec_ep_t itp_crypto
Definition: ipsec_tun.h:44
VLIB buffer representation.
Definition: buffer.h:102
u64 uword
Definition: types.h:112
#define foreach_ipsec_tun_protect_input_error
Definition: ipsec_tun_in.c:30
static void * vlib_frame_vector_args(vlib_frame_t *f)
Get pointer to frame vector data.
Definition: node_funcs.h:244
ipsec_protect_flags_t itp_flags
Definition: ipsec_tun.h:46
vlib_node_registration_t ipsec6_tun_input_node
(constructor) VLIB_REGISTER_NODE (ipsec6_tun_input_node)
Definition: ipsec_tun_in.c:442
A collection of combined counters.
Definition: counter.h:188
#define hash_get_mem(h, key)
Definition: hash.h:269
#define vnet_buffer(b)
Definition: buffer.h:365
static_always_inline int ip46_address_is_equal_v4(const ip46_address_t *ip46, const ip4_address_t *ip4)
Definition: ip6_packet.h:99
u16 flags
Copy of main node flags.
Definition: node.h:509
static int ip4_header_bytes(const ip4_header_t *i)
Definition: ip4_packet.h:235
u8 * format_ipsec4_tunnel_key(u8 *s, va_list *args)
Definition: ipsec_format.c:406
static_always_inline void vlib_get_buffers(vlib_main_t *vm, u32 *bi, vlib_buffer_t **b, int count)
Translate array of buffer indices into buffer pointers.
Definition: buffer_funcs.h:244
#define VLIB_NODE_FLAG_TRACE
Definition: node.h:302
static u8 * format_ipsec_tun_protect_input_trace(u8 *s, va_list *args)
Definition: ipsec_tun_in.c:74
#define foreach_ipsec_input_next
Definition: ipsec_io.h:29
Definition: defs.h:46
ip6_address_t dst_address
Definition: ip6_packet.h:383
uword * tun4_protect_by_key
Definition: ipsec.h:118