summaryrefslogtreecommitdiffstats
path: root/src/vnet/ip/icmp4.c
blob: 48bcf67ac4efbb258022957f14cfff5355c28c78 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
/*
 * Copyright (c) 2015 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * ip/icmp4.c: ipv4 icmp
 *
 * Copyright (c) 2008 Eliot Dresselhaus
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include <vlib/vlib.h>
#include <vnet/ip/ip.h>
#include <vnet/pg/pg.h>

static char *icmp_error_strings[] = {
#define _(f,s) s,
  foreach_icmp4_error
#undef _
};

static u8 *
format_ip4_icmp_type_and_code (u8 * s, va_list * args)
{
  icmp4_type_t type = va_arg (*args, int);
  u8 code = va_arg (*args, int);
  char *t = 0;

#define _(n,f) case n: t = #f; break;

  switch (type)
    {
      foreach_icmp4_type;

    default:
      break;
    }

#undef _

  if (!t)
    return format (s, "unknown 0x%x", type);

  s = format (s, "%s", t);

  t = 0;
  switch ((type << 8) | code)
    {
#define _(a,n,f) case (ICMP4_##a << 8) | (n): t = #f; break;

      foreach_icmp4_code;

#undef _
    }

  if (t)
    s = format (s, " %s", t);

  return s;
}

static u8 *
format_ip4_icmp_header (u8 * s, va_list * args)
{
  icmp46_header_t *icmp = va_arg (*args, icmp46_header_t *);
  u32 max_header_bytes = va_arg (*args, u32);

  /* Nothing to do. */
  if (max_header_bytes < sizeof (icmp[0]))
    return format (s, "ICMP header truncated");

  s = format (s, "ICMP %U checksum 0x%x",
	      format_ip4_icmp_type_and_code, icmp->type, icmp->code,
	      clib_net_to_host_u16 (icmp->checksum));

  return s;
}

static u8 *
format_icmp_input_trace (u8 * s, va_list * va)
{
  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
  CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
  icmp_input_trace_t *t = va_arg (*va, icmp_input_trace_t *);

  s = format (s, "%U",
	      format_ip4_header, t->packet_data, sizeof (t->packet_data));

  return s;
}

typedef enum
{
  ICMP_INPUT_NEXT_ERROR,
  ICMP_INPUT_N_NEXT,
} icmp_input_next_t;

typedef struct
{
  uword *type_and_code_by_name;

  uword *type_by_name;

  /* Vector dispatch table indexed by [icmp type]. */
  u8 ip4_input_next_index_by_type[256];
} icmp4_main_t;

icmp4_main_t icmp4_main;

static uword
ip4_icmp_input (vlib_main_t * vm,
		vlib_node_runtime_t * node, vlib_frame_t * frame)
{
  icmp4_main_t *im = &icmp4_main;
  uword n_packets = frame->n_vectors;
  u32 *from, *to_next;
  u32 n_left_from, n_left_to_next, next;

  from = vlib_frame_vector_args (frame);
  n_left_from = n_packets;
  next = node->cached_next_index;

  if (node->flags & VLIB_NODE_FLAG_TRACE)
    vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors,
				   /* stride */ 1,
				   sizeof (icmp_input_trace_t));

  while (n_left_from > 0)
    {
      vlib_get_next_frame (vm, node, next, to_next, n_left_to_next);

      while (n_left_from > 0 && n_left_to_next > 0)
	{
	  vlib_buffer_t *p0;
	  ip4_header_t *ip0;
	  icmp46_header_t *icmp0;
	  icmp4_type_t type0;
	  u32 bi0, next0;

	  if (PREDICT_TRUE (n_left_from > 2))
	    {
	      vlib_prefetch_buffer_with_index (vm, from[2], LOAD);
	      p0 = vlib_get_buffer (vm, from[1]);
	      ip0 = vlib_buffer_get_current (p0);
	      CLIB_PREFETCH (ip0, CLIB_CACHE_LINE_BYTES, LOAD);
	    }

	  bi0 = to_next[0] = from[0];

	  from += 1;
	  n_left_from -= 1;
	  to_next += 1;
	  n_left_to_next -= 1;

	  p0 = vlib_get_buffer (vm, bi0);
	  ip0 = vlib_buffer_get_current (p0);
	  icmp0 = ip4_next_header (ip0);
	  type0 = icmp0->type;
	  next0 = im->ip4_input_next_index_by_type[type0];

	  p0->error = node->errors[ICMP4_ERROR_UNKNOWN_TYPE];

	  /* Verify speculative enqueue, maybe switch current next frame */
	  vlib_validate_buffer_enqueue_x1 (vm, node, next, to_next,
					   n_left_to_next, bi0, next0);
	}

      vlib_put_next_frame (vm, node, next, n_left_to_next);
    }

  return frame->n_vectors;
}

/* *INDENT-OFF* */
VLIB_REGISTER_NODE (ip4_icmp_input_node) = {
  .function = ip4_icmp_input,
  .name = "ip4-icmp-input",

  .vector_size = sizeof (u32),

  .format_trace = format_icmp_input_trace,

  .n_errors = ARRAY_LEN (icmp_error_strings),
  .error_strings = icmp_error_strings,

  .n_next_nodes = 1,
  .next_nodes = {
    [ICMP_INPUT_NEXT_ERROR] = "ip4-punt",
  },
};
/* *INDENT-ON* */

typedef enum
{
  IP4_ICMP_ERROR_NEXT_DROP,
  IP4_ICMP_ERROR_NEXT_LOOKUP,
  IP4_ICMP_ERROR_N_NEXT,
} ip4_icmp_error_next_t;

static u8
icmp4_icmp_type_to_error (u8 type)
{
  switch (type)
    {
    case ICMP4_destination_unreachable:
      return ICMP4_ERROR_DEST_UNREACH_SENT;
    case ICMP4_time_exceeded:
      return ICMP4_ERROR_TTL_EXPIRE_SENT;
    case ICMP4_parameter_problem:
      return ICMP4_ERROR_PARAM_PROBLEM_SENT;
    default:
      return ICMP4_ERROR_DROP;
    }
}

static uword
ip4_icmp_error (vlib_main_t * vm,
		vlib_node_runtime_t * node, vlib_frame_t * frame)
{
  u32 *from, *to_next;
  uword n_left_from, n_left_to_next;
  ip4_icmp_error_next_t next_index;
  ip4_main_t *im = &ip4_main;
  ip_lookup_main_t *lm = &im->lookup_main;

  from = vlib_frame_vector_args (frame);
  n_left_from = frame->n_vectors;
  next_index = node->cached_next_index;

  if (node->flags & VLIB_NODE_FLAG_TRACE)
    vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors,
				   /* stride */ 1,
				   sizeof (icmp_input_trace_t));

  while (n_left_from > 0)
    {
      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);

      while (n_left_from > 0 && n_left_to_next > 0)
	{
	  /*
	   * Duplicate first buffer and free the original chain.  Keep
	   * as much of the original packet as possible, within the
	   * minimum MTU. We chat "a little" here by keeping whatever
	   * is available in the first buffer.
	   */

	  u32 pi0 = ~0;
	  u32 org_pi0 = from[0];
	  u32 next0 = IP4_ICMP_ERROR_NEXT_LOOKUP;
	  u8 error0 = ICMP4_ERROR_NONE;
	  vlib_buffer_t *p0, *org_p0;
	  ip4_header_t *ip0, *out_ip0;
	  icmp46_header_t *icmp0;
	  u32 sw_if_index0, if_add_index0;
	  ip_csum_t sum;

	  org_p0 = vlib_get_buffer (vm, org_pi0);
	  p0 = vlib_buffer_copy_no_chain (vm, org_p0, &pi0);
	  if (!p0 || pi0 == ~0)	/* Out of buffers */
	    continue;

	  /* Speculatively enqueue p0 to the current next frame */
	  to_next[0] = pi0;
	  from += 1;
	  to_next += 1;
	  n_left_from -= 1;
	  n_left_to_next -= 1;

	  ip0 = vlib_buffer_get_current (p0);
	  sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];

	  /* Add IP header and ICMPv4 header including a 4 byte data field */
	  vlib_buffer_advance (p0,
			       -sizeof (ip4_header_t) -
			       sizeof (icmp46_header_t) - 4);

	  p0->current_length =
	    p0->current_length > 576 ? 576 : p0->current_length;
	  out_ip0 = vlib_buffer_get_current (p0);
	  icmp0 = (icmp46_header_t *) & out_ip0[1];

	  /* Fill ip header fields */
	  out_ip0->ip_version_and_header_length = 0x45;
	  out_ip0->tos = 0;
	  out_ip0->length = clib_host_to_net_u16 (p0->current_length);
	  out_ip0->fragment_id = 0;
	  out_ip0->flags_and_fragment_offset = 0;
	  out_ip0->ttl = 0xff;
	  out_ip0->protocol = IP_PROTOCOL_ICMP;
	  out_ip0->dst_address = ip0->src_address;
	  if_add_index0 = ~0;
	  if (PREDICT_TRUE (vec_len (lm->if_address_pool_index_by_sw_if_index)
			    > sw_if_index0))
	    if_add_index0 =
	      lm->if_address_pool_index_by_sw_if_index[sw_if_index0];
	  if (PREDICT_TRUE (if_add_index0 != ~0))
	    {
	      ip_interface_address_t *if_add =
		pool_elt_at_index (lm->if_address_pool, if_add_index0);
	      ip4_address_t *if_ip =
		ip_interface_address_get_address (lm, if_add);
	      out_ip0->src_address = *if_ip;
	    }
	  else
	    {
	      /* interface has no IP4 address - should not happen */
	      next0 = IP4_ICMP_ERROR_NEXT_DROP;
	      error0 = ICMP4_ERROR_DROP;
	    }
	  out_ip0->checksum = ip4_header_checksum (out_ip0);

	  /* Fill icmp header fields */
	  icmp0->type = vnet_buffer (p0)->ip.icmp.type;
	  icmp0->code = vnet_buffer (p0)->ip.icmp.code;
	  *((u32 *) (icmp0 + 1)) =
	    clib_host_to_net_u32 (vnet_buffer (p0)->ip.icmp.data);
	  icmp0->checksum = 0;
	  sum =
	    ip_incremental_checksum (0, icmp0,
				     p0->current_length -
				     sizeof (ip4_header_t));
	  icmp0->checksum = ~ip_csum_fold (sum);

	  /* Update error status */
	  if (error0 == ICMP4_ERROR_NONE)
	    error0 = icmp4_icmp_type_to_error (icmp0->type);

	  vlib_error_count (vm, node->node_index, error0, 1);

	  /* Verify speculative enqueue, maybe switch current next frame */
	  vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
					   to_next, n_left_to_next,
					   pi0, next0);
	}
      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
    }

  /*
   * push the original buffers to error-drop, so that
   * they can get the error counters handled, then freed
   */
  vlib_buffer_enqueue_to_single_next (vm, node,
				      vlib_frame_vector_args (frame),
				      IP4_ICMP_ERROR_NEXT_DROP,
				      frame->n_vectors);

  return frame->n_vectors;
}

/* *INDENT-OFF* */
VLIB_REGISTER_NODE (ip4_icmp_error_node) = {
  .function = ip4_icmp_error,
  .name = "ip4-icmp-error",
  .vector_size = sizeof (u32),

  .n_errors = ARRAY_LEN (icmp_error_strings),
  .error_strings = icmp_error_strings,

  .n_next_nodes = IP4_ICMP_ERROR_N_NEXT,
  .next_nodes = {
    [IP4_ICMP_ERROR_NEXT_DROP] = "ip4-drop",
    [IP4_ICMP_ERROR_NEXT_LOOKUP] = "ip4-lookup",
  },

  .format_trace = format_icmp_input_trace,
};
/* *INDENT-ON* */


static uword
unformat_icmp_type_and_code (unformat_input_t * input, va_list * args)
{
  icmp46_header_t *h = va_arg (*args, icmp46_header_t *);
  icmp4_main_t *cm = &icmp4_main;
  u32 i;

  if (unformat_user (input, unformat_vlib_number_by_name,
		     cm->type_and_code_by_name, &i))
    {
      h->type = (i >> 8) & 0xff;
      h->code = (i >> 0) & 0xff;
    }
  else if (unformat_user (input, unformat_vlib_number_by_name,
			  cm->type_by_name, &i))
    {
      h->type = i;
      h->code = 0;
    }
  else
    return 0;

  return 1;
}

static void
icmp4_pg_edit_function (pg_main_t * pg,
			pg_stream_t * s,
			pg_edit_group_t * g, u32 * packets, u32 n_packets)
{
  vlib_main_t *vm = vlib_get_main ();
  u32 ip_offset, icmp_offset;

  icmp_offset = g->start_byte_offset;
  ip_offset = (g - 1)->start_byte_offset;

  while (n_packets >= 1)
    {
      vlib_buffer_t *p0;
      ip4_header_t *ip0;
      icmp46_header_t *icmp0;
      u32 len0;

      p0 = vlib_get_buffer (vm, packets[0]);
      n_packets -= 1;
      packets += 1;

      ASSERT (p0->current_data == 0);
      ip0 = (void *) (p0->data + ip_offset);
      icmp0 = (void *) (p0->data + icmp_offset);

      /* if IP length has been specified, then calculate the length based on buffer */
      if (ip0->length == 0)
	len0 = vlib_buffer_length_in_chain (vm, p0) - icmp_offset;
      else
	len0 = clib_net_to_host_u16 (ip0->length) - icmp_offset;

      icmp0->checksum =
	~ip_csum_fold (ip_incremental_checksum (0, icmp0, len0));
    }
}

typedef struct
{
  pg_edit_t type, code;
  pg_edit_t checksum;
} pg_icmp46_header_t;

always_inline void
pg_icmp_header_init (pg_icmp46_header_t * p)
{
  /* Initialize fields that are not bit fields in the IP header. */
#define _(f) pg_edit_init (&p->f, icmp46_header_t, f);
  _(type);
  _(code);
  _(checksum);
#undef _
}

static uword
unformat_pg_icmp_header (unformat_input_t * input, va_list * args)
{
  pg_stream_t *s = va_arg (*args, pg_stream_t *);
  pg_icmp46_header_t *p;
  u32 group_index;

  p = pg_create_edit_group (s, sizeof (p[0]), sizeof (icmp46_header_t),
			    &group_index);
  pg_icmp_header_init (p);

  p->checksum.type = PG_EDIT_UNSPECIFIED;

  {
    icmp46_header_t tmp;

    if (!unformat (input, "ICMP %U", unformat_icmp_type_and_code, &tmp))
      goto error;

    pg_edit_set_fixed (&p->type, tmp.type);
    pg_edit_set_fixed (&p->code, tmp.code);
  }

  /* Parse options. */
  while (1)
    {
      if (unformat (input, "checksum %U",
		    unformat_pg_edit, unformat_pg_number, &p->checksum))
	;

      /* Can't parse input: try next protocol level. */
      else
	break;
    }

  if (!unformat_user (input, unformat_pg_payload, s))
    goto error;

  if (p->checksum.type == PG_EDIT_UNSPECIFIED)
    {
      pg_edit_group_t *g = pg_stream_get_group (s, group_index);
      g->edit_function = icmp4_pg_edit_function;
      g->edit_function_opaque = 0;
    }

  return 1;

error:
  /* Free up any edits we may have added. */
  pg_free_edit_group (s);
  return 0;
}

void
ip4_icmp_register_type (vlib_main_t * vm, icmp4_type_t type, u32 node_index)
{
  icmp4_main_t *im = &icmp4_main;
  u32 old_next_index;

  ASSERT ((int) type < ARRAY_LEN (im->ip4_input_next_index_by_type));
  old_next_index = im->ip4_input_next_index_by_type[type];

  im->ip4_input_next_index_by_type[type]
    = vlib_node_add_next (vm, ip4_icmp_input_node.index, node_index);

  if (old_next_index &&
      (old_next_index != im->ip4_input_next_index_by_type[type]))
    clib_warning ("WARNING: changed next_by_type[%d]", (int) type);
}

static clib_error_t *
icmp4_init (vlib_main_t * vm)
{
  ip_main_t *im = &ip_main;
  ip_protocol_info_t *pi;
  icmp4_main_t *cm = &icmp4_main;
  clib_error_t *error;

  error = vlib_call_init_function (vm, ip_main_init);

  if (error)
    return error;

  pi = ip_get_protocol_info (im, IP_PROTOCOL_ICMP);
  pi->format_header = format_ip4_icmp_header;
  pi->unformat_pg_edit = unformat_pg_icmp_header;

  cm->type_by_name = hash_create_string (0, sizeof (uword));
#define _(n,t) hash_set_mem (cm->type_by_name, #t, (n));
  foreach_icmp4_type;
#undef _

  cm->type_and_code_by_name = hash_create_string (0, sizeof (uword));
#define _(a,n,t) hash_set_mem (cm->type_by_name, #t, (n) | (ICMP4_##a << 8));
  foreach_icmp4_code;
#undef _

  clib_memset (cm->ip4_input_next_index_by_type,
	       ICMP_INPUT_NEXT_ERROR,
	       sizeof (cm->ip4_input_next_index_by_type));

  return 0;
}

VLIB_INIT_FUNCTION (icmp4_init);

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */
dst_addr = '' addr_incr = 1 << (128 - 96) if src_addr.version == 6 \ else 1 << (32 - 24) if int(n_entries) > 10: tmp_filename = '/tmp/ipsec_sad_{0}_add_del_entry.script'.\ format(sad_id) with open(tmp_filename, 'w') as tmp_file: for i in xrange(n_entries): integ = ( 'integ-alg {integ_alg} integ-key {integ_key}'.format( integ_alg=integ_alg.alg_name, integ_key=integ_key.encode('hex')) if integ_alg else '') tunnel = ( 'tunnel-src {laddr} tunnel-dst {raddr}'.format( laddr=src_addr + i * addr_incr, raddr=dst_addr + i * addr_incr) if tunnel_src and tunnel_dst else '') conf = ( 'exec ipsec sa add {sad_id} esp spi {spi} ' 'crypto-alg {crypto_alg} crypto-key {crypto_key} ' '{integ} {tunnel}\n'.format( sad_id=sad_id + i, spi=spi + i, crypto_alg=crypto_alg.alg_name, crypto_key=crypto_key.encode('hex'), integ=integ, tunnel=tunnel)) tmp_file.write(conf) vat = VatExecutor() vat.execute_script(tmp_filename, node, timeout=300, json_out=False, copy_on_execute=True) os.remove(tmp_filename) return ckey = dict( length=len(crypto_key), data=crypto_key ) ikey = dict( length=len(integ_key), data=integ_key if integ_key else 0 ) flags = int(IPsecSadFlags.IPSEC_API_SAD_FLAG_NONE) if tunnel_src and tunnel_dst: flags = flags | int(IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL) if src_addr.version == 6: flags = flags | int( IPsecSadFlags.IPSEC_API_SAD_FLAG_IS_TUNNEL_V6) cmd = 'ipsec_sad_entry_add_del' err_msg = 'Failed to add Security Association Database entry on ' \ 'host {host}'.format(host=node['host']) sad_entry = dict( sad_id=int(sad_id), spi=int(spi), crypto_algorithm=crypto_alg.alg_int_repr, crypto_key=ckey, integrity_algorithm=integ_alg.alg_int_repr if integ_alg else 0, integrity_key=ikey, flags=flags, tunnel_src=str(src_addr), tunnel_dst=str(dst_addr), protocol=int(IPsecProto.ESP) ) args = dict( is_add=1, entry=sad_entry ) with PapiSocketExecutor(node) as papi_exec: for i in xrange(n_entries): args['entry']['sad_id'] = int(sad_id) + i args['entry']['spi'] = int(spi) + i args['entry']['tunnel_src'] = str(src_addr + i * addr_incr) \ if tunnel_src and tunnel_dst else src_addr args['entry']['tunnel_dst'] = str(dst_addr + i * addr_incr) \ if tunnel_src and tunnel_dst else dst_addr history = False if 1 < i < n_entries - 1 else True papi_exec.add(cmd, history=history, **args) papi_exec.get_replies(err_msg) @staticmethod def vpp_ipsec_set_ip_route( node, n_tunnels, tunnel_src, traffic_addr, tunnel_dst, interface, raddr_range): """Set IP address and route on interface. :param node: VPP node to add config on. :param n_tunnels: Number of tunnels to create. :param tunnel_src: Tunnel header source IPv4 or IPv6 address. :param traffic_addr: Traffic destination IP address to route. :param tunnel_dst: Tunnel header destination IPv4 or IPv6 address. :param interface: Interface key on node 1. :param raddr_range: Mask specifying range of Policy selector Remote IP addresses. Valid values are from 1 to 32 in case of IPv4 and to 128 in case of IPv6. :type node: dict :type n_tunnels: int :type tunnel_src: str :type traffic_addr: str :type tunnel_dst: str :type interface: str :type raddr_range: int """ laddr = ip_address(unicode(tunnel_src)) raddr = ip_address(unicode(tunnel_dst)) taddr = ip_address(unicode(traffic_addr)) addr_incr = 1 << (128 - raddr_range) if laddr.version == 6 \ else 1 << (32 - raddr_range) if int(n_tunnels) > 10: tmp_filename = '/tmp/ipsec_set_ip.script' with open(tmp_filename, 'w') as tmp_file: for i in xrange(n_tunnels): conf = ( 'exec set interface ip address {interface} ' '{laddr}/{laddr_l}\n' 'exec ip route add {taddr}/{taddr_l} via {raddr} ' '{interface}\n'.format( interface=Topology.get_interface_name( node, interface), laddr=laddr + i * addr_incr, laddr_l=raddr_range, raddr=raddr + i * addr_incr, taddr=taddr + i, taddr_l=128 if taddr.version == 6 else 32)) tmp_file.write(conf) vat = VatExecutor() vat.execute_script(tmp_filename, node, timeout=300, json_out=False, copy_on_execute=True) os.remove(tmp_filename) return cmd1 = 'sw_interface_add_del_address' args1 = dict( sw_if_index=InterfaceUtil.get_interface_index(node, interface), is_add=1, is_ipv6=1 if laddr.version == 6 else 0, del_all=0, address_length=raddr_range, address=None ) cmd2 = 'ip_route_add_del' route = IPUtil.compose_vpp_route_structure( node, taddr, prefix_len=128 if taddr.version == 6 else 32, interface=interface, gateway=tunnel_dst ) args2 = dict( is_add=1, is_multipath=0, route=route ) err_msg = 'Failed to configure IP addresses and IP routes on ' \ 'interface {ifc} on host {host}'.\ format(ifc=interface, host=node['host']) with PapiSocketExecutor(node) as papi_exec: for i in xrange(n_tunnels): args1['address'] = getattr(laddr + i * addr_incr, 'packed') args2['route']['prefix']['address']['un'] = \ IPUtil.union_addr(taddr + i) args2['route']['paths'][0]['nh']['address'] = \ IPUtil.union_addr(raddr + i * addr_incr) history = False if 1 < i < n_tunnels - 1 else True papi_exec.add(cmd1, history=history, **args1).\ add(cmd2, history=history, **args2) papi_exec.get_replies(err_msg) @staticmethod def vpp_ipsec_add_spd(node, spd_id): """Create Security Policy Database on the VPP node. :param node: VPP node to add SPD on. :param spd_id: SPD ID. :type node: dict :type spd_id: int """ cmd = 'ipsec_spd_add_del' err_msg = 'Failed to add Security Policy Database on host {host}'.\ format(host=node['host']) args = dict( is_add=1, spd_id=int(spd_id) ) with PapiSocketExecutor(node) as papi_exec: papi_exec.add(cmd, **args).get_reply(err_msg) @staticmethod def vpp_ipsec_spd_add_if(node, spd_id, interface): """Add interface to the Security Policy Database. :param node: VPP node. :param spd_id: SPD ID to add interface on. :param interface: Interface name or sw_if_index. :type node: dict :type spd_id: int :type interface: str or int """ cmd = 'ipsec_interface_add_del_spd' err_msg = 'Failed to add interface {ifc} to Security Policy Database ' \ '{spd} on host {host}'.\ format(ifc=interface, spd=spd_id, host=node['host']) args = dict( is_add=1, sw_if_index=InterfaceUtil.get_interface_index(node, interface), spd_id=int(spd_id) ) with PapiSocketExecutor(node) as papi_exec: papi_exec.add(cmd, **args).get_reply(err_msg) @staticmethod def vpp_ipsec_policy_add( node, spd_id, priority, action, inbound=True, sa_id=None, laddr_range=None, raddr_range=None, proto=None, lport_range=None, rport_range=None, is_ipv6=False): """Create Security Policy Database entry on the VPP node. :param node: VPP node to add SPD entry on. :param spd_id: SPD ID to add entry on. :param priority: SPD entry priority, higher number = higher priority. :param action: Policy action. :param inbound: If True policy is for inbound traffic, otherwise outbound. :param sa_id: SAD entry ID for protect action. :param laddr_range: Policy selector local IPv4 or IPv6 address range in format IP/prefix or IP/mask. If no mask is provided, it's considered to be /32. :param raddr_range: Policy selector remote IPv4 or IPv6 address range in format IP/prefix or IP/mask. If no mask is provided, it's considered to be /32. :param proto: Policy selector next layer protocol number. :param lport_range: Policy selector local TCP/UDP port range in format <port_start>-<port_end>. :param rport_range: Policy selector remote TCP/UDP port range in format <port_start>-<port_end>. :param is_ipv6: True in case of IPv6 policy when IPv6 address range is not defined so it will default to address ::/0, otherwise False. :type node: dict :type spd_id: int :type priority: int :type action: PolicyAction :type inbound: bool :type sa_id: int :type laddr_range: string :type raddr_range: string :type proto: int :type lport_range: string :type rport_range: string :type is_ipv6: bool """ if laddr_range is None: laddr_range = '::/0' if is_ipv6 else '0.0.0.0/0' if raddr_range is None: raddr_range = '::/0' if is_ipv6 else '0.0.0.0/0' cmd = 'ipsec_spd_entry_add_del' err_msg = 'Failed to add entry to Security Policy Database ' \ '{spd} on host {host}'.format(spd=spd_id, host=node['host']) spd_entry = dict( spd_id=int(spd_id), priority=int(priority), is_outbound=0 if inbound else 1, sa_id=int(sa_id) if sa_id else 0, policy=action.policy_int_repr, protocol=int(proto) if proto else 0, remote_address_start=IPUtil.create_ip_address_object( ip_network(unicode(raddr_range), strict=False).network_address), remote_address_stop=IPUtil.create_ip_address_object( ip_network( unicode(raddr_range), strict=False).broadcast_address), local_address_start=IPUtil.create_ip_address_object( ip_network( unicode(laddr_range), strict=False).network_address), local_address_stop=IPUtil.create_ip_address_object( ip_network( unicode(laddr_range), strict=False).broadcast_address), remote_port_start=int(rport_range.split('-')[0]) if rport_range else 0, remote_port_stop=int(rport_range.split('-')[1]) if rport_range else 65535, local_port_start=int(lport_range.split('-')[0]) if lport_range else 0, local_port_stop=int(lport_range.split('-')[1]) if rport_range else 65535 ) args = dict( is_add=1, entry=spd_entry ) with PapiSocketExecutor(node) as papi_exec: papi_exec.add(cmd, **args).get_reply(err_msg) @staticmethod def vpp_ipsec_spd_add_entries( node, n_entries, spd_id, priority, inbound, sa_id, raddr_ip): """Create multiple Security Policy Database entries on the VPP node. :param node: VPP node to add SPD entries on. :param n_entries: Number of SPD entries to be added. :param spd_id: SPD ID to add entries on. :param priority: SPD entries priority, higher number = higher priority. :param inbound: If True policy is for inbound traffic, otherwise outbound. :param sa_id: SAD entry ID for first entry. Each subsequent entry will SAD entry ID incremented by 1. :param raddr_ip: Policy selector remote IPv4 start address for the first entry. Remote IPv4 end address will be calculated depending on raddr_range parameter. Each subsequent entry will have start address next after IPv4 end address of previous entry. :type node: dict :type n_entries: int :type spd_id: int :type priority: int :type inbound: bool :type sa_id: int :type raddr_ip: string """ if int(n_entries) > 10: tmp_filename = '/tmp/ipsec_spd_{0}_add_del_entry.script'.\ format(sa_id) with open(tmp_filename, 'w') as tmp_file: for i in xrange(n_entries): raddr_s = ip_address(unicode(raddr_ip)) + i raddr_e = ip_address(unicode(raddr_ip)) + (i + 1) - 1 tunnel = ( 'exec ipsec policy add spd {spd_id} ' 'priority {priority} {direction} action protect ' 'sa {sa_id} remote-ip-range {raddr_s} - {raddr_e} ' 'local-ip-range 0.0.0.0 - 255.255.255.255\n'. format( spd_id=spd_id, priority=priority, direction='inbound' if inbound else 'outbound', sa_id=sa_id+i, raddr_s=raddr_s, raddr_e=raddr_e)) tmp_file.write(tunnel) VatExecutor().execute_script( tmp_filename, node, timeout=300, json_out=False, copy_on_execute=True) os.remove(tmp_filename) return raddr_ip = ip_address(unicode(raddr_ip)) laddr_range = '::/0' if raddr_ip.version == 6 else '0.0.0.0/0' cmd = 'ipsec_spd_entry_add_del' err_msg = 'Failed to add entry to Security Policy Database ' \ '{spd} on host {host}'.format(spd=spd_id, host=node['host']) spd_entry = dict( spd_id=int(spd_id), priority=int(priority), is_outbound=0 if inbound else 1, sa_id=int(sa_id) if sa_id else 0, policy=IPsecUtil.policy_action_protect().policy_int_repr, protocol=0, remote_address_start=IPUtil.create_ip_address_object(raddr_ip), remote_address_stop=IPUtil.create_ip_address_object(raddr_ip), local_address_start=IPUtil.create_ip_address_object( ip_network(unicode(laddr_range), strict=False).network_address), local_address_stop=IPUtil.create_ip_address_object( ip_network( unicode(laddr_range), strict=False).broadcast_address), remote_port_start=0, remote_port_stop=65535, local_port_start=0, local_port_stop=65535 ) args = dict( is_add=1, entry=spd_entry ) with PapiSocketExecutor(node) as papi_exec: for i in xrange(n_entries): args['entry']['remote_address_start']['un'] = \ IPUtil.union_addr(raddr_ip + i) args['entry']['remote_address_stop']['un'] = \ IPUtil.union_addr(raddr_ip + i) history = False if 1 < i < n_entries - 1 else True papi_exec.add(cmd, history=history, **args) papi_exec.get_replies(err_msg) @staticmethod def vpp_ipsec_create_tunnel_interfaces( nodes, if1_ip_addr, if2_ip_addr, if1_key, if2_key, n_tunnels, crypto_alg, integ_alg, raddr_ip1, raddr_ip2, raddr_range): """Create multiple IPsec tunnel interfaces between two VPP nodes. :param nodes: VPP nodes to create tunnel interfaces. :param if1_ip_addr: VPP node 1 interface IPv4/IPv6 address. :param if2_ip_addr: VPP node 2 interface IPv4/IPv6 address. :param if1_key: VPP node 1 interface key from topology file. :param if2_key: VPP node 2 interface key from topology file. :param n_tunnels: Number of tunnell interfaces to create. :param crypto_alg: The encryption algorithm name. :param integ_alg: The integrity algorithm name. :param raddr_ip1: Policy selector remote IPv4/IPv6 start address for the first tunnel in direction node1->node2. :param raddr_ip2: Policy selector remote IPv4/IPv6 start address for the first tunnel in direction node2->node1. :param raddr_range: Mask specifying range of Policy selector Remote IPv4/IPv6 addresses. Valid values are from 1 to 32 in case of IPv4 and to 128 in case of IPv6. :type nodes: dict :type if1_ip_addr: str :type if2_ip_addr: str :type if1_key: str :type if2_key: str :type n_tunnels: int :type crypto_alg: CryptoAlg :type integ_alg: IntegAlg :type raddr_ip1: string :type raddr_ip2: string :type raddr_range: int """ n_tunnels = int(n_tunnels) spi_1 = 100000 spi_2 = 200000 if1_ip = ip_address(unicode(if1_ip_addr)) if2_ip = ip_address(unicode(if2_ip_addr)) raddr_ip1 = ip_address(unicode(raddr_ip1)) raddr_ip2 = ip_address(unicode(raddr_ip2)) addr_incr = 1 << (128 - raddr_range) if if1_ip.version == 6 \ else 1 << (32 - raddr_range) if n_tunnels > 1: tmp_fn1 = '/tmp/ipsec_create_tunnel_dut1.config' tmp_fn2 = '/tmp/ipsec_create_tunnel_dut2.config' vat = VatExecutor() with open(tmp_fn1, 'w') as tmp_f1, open(tmp_fn2, 'w') as tmp_f2: tmp_f1.write( 'exec create loopback interface\n' 'exec set interface state loop0 up\n' 'exec set interface ip address {uifc} {iaddr}/{mask}\n' .format( iaddr=if2_ip - 1, uifc=Topology.get_interface_name( nodes['DUT1'], if1_key), mask=96 if if2_ip.version == 6 else 24)) tmp_f2.write( 'exec set interface ip address {uifc} {iaddr}/{mask}\n' .format( iaddr=if2_ip, uifc=Topology.get_interface_name( nodes['DUT2'], if2_key), mask=96 if if2_ip.version == 6 else 24)) for i in xrange(n_tunnels): ckey = gen_key(IPsecUtil.get_crypto_alg_key_len( crypto_alg)).encode('hex') if integ_alg: ikey = gen_key(IPsecUtil.get_integ_alg_key_len( integ_alg)).encode('hex') integ = ( 'integ_alg {integ_alg} ' 'local_integ_key {local_integ_key} ' 'remote_integ_key {remote_integ_key} ' .format( integ_alg=integ_alg.alg_name, local_integ_key=ikey, remote_integ_key=ikey)) else: integ = '' tmp_f1.write( 'exec set interface ip address loop0 {laddr}/32\n' 'ipsec_tunnel_if_add_del ' 'local_spi {local_spi} ' 'remote_spi {remote_spi} ' 'crypto_alg {crypto_alg} ' 'local_crypto_key {local_crypto_key} ' 'remote_crypto_key {remote_crypto_key} ' '{integ} ' 'local_ip {laddr} ' 'remote_ip {raddr}\n' .format( local_spi=spi_1 + i, remote_spi=spi_2 + i, crypto_alg=crypto_alg.alg_name, local_crypto_key=ckey, remote_crypto_key=ckey, integ=integ, laddr=if1_ip + i * addr_incr, raddr=if2_ip)) tmp_f2.write( 'ipsec_tunnel_if_add_del ' 'local_spi {local_spi} ' 'remote_spi {remote_spi} ' 'crypto_alg {crypto_alg} ' 'local_crypto_key {local_crypto_key} ' 'remote_crypto_key {remote_crypto_key} ' '{integ} ' 'local_ip {laddr} ' 'remote_ip {raddr}\n' .format( local_spi=spi_2 + i, remote_spi=spi_1 + i, crypto_alg=crypto_alg.alg_name, local_crypto_key=ckey, remote_crypto_key=ckey, integ=integ, laddr=if2_ip, raddr=if1_ip + i * addr_incr)) vat.execute_script( tmp_fn1, nodes['DUT1'], timeout=1800, json_out=False, copy_on_execute=True, history=False if n_tunnels > 100 else True) vat.execute_script( tmp_fn2, nodes['DUT2'], timeout=1800, json_out=False, copy_on_execute=True, history=False if n_tunnels > 100 else True) os.remove(tmp_fn1) os.remove(tmp_fn2) with open(tmp_fn1, 'w') as tmp_f1, open(tmp_fn2, 'w') as tmp_f2: tmp_f2.write( 'exec ip route add {raddr} via {uifc} {iaddr}\n' .format( raddr=ip_network(unicode(if1_ip_addr+'/8'), False), iaddr=if2_ip - 1, uifc=Topology.get_interface_name( nodes['DUT2'], if2_key))) for i in xrange(n_tunnels): tmp_f1.write( 'exec set interface unnumbered ipsec{i} use {uifc}\n' 'exec set interface state ipsec{i} up\n' 'exec ip route add {taddr}/{mask} via ipsec{i}\n' .format( taddr=raddr_ip2 + i, i=i, uifc=Topology.get_interface_name(nodes['DUT1'], if1_key), mask=128 if if2_ip.version == 6 else 32)) tmp_f2.write( 'exec set interface unnumbered ipsec{i} use {uifc}\n' 'exec set interface state ipsec{i} up\n' 'exec ip route add {taddr}/{mask} via ipsec{i}\n' .format( taddr=raddr_ip1 + i, i=i, uifc=Topology.get_interface_name(nodes['DUT2'], if2_key), mask=128 if if2_ip.version == 6 else 32)) vat.execute_script( tmp_fn1, nodes['DUT1'], timeout=1800, json_out=False, copy_on_execute=True, history=False if n_tunnels > 100 else True) vat.execute_script( tmp_fn2, nodes['DUT2'], timeout=1800, json_out=False, copy_on_execute=True, history=False if n_tunnels > 100 else True) os.remove(tmp_fn1) os.remove(tmp_fn2) return with PapiSocketExecutor(nodes['DUT1']) as papi_exec: # Create loopback interface on DUT1, set it to up state cmd1 = 'create_loopback' args1 = dict(mac_address=0) err_msg = 'Failed to create loopback interface on host {host}'.\ format(host=nodes['DUT1']['host']) loop_sw_if_idx = papi_exec.add(cmd1, **args1).\ get_sw_if_index(err_msg) cmd1 = 'sw_interface_set_flags' args1 = dict( sw_if_index=loop_sw_if_idx, admin_up_down=1) err_msg = 'Failed to set loopback interface state up on host ' \ '{host}'.format(host=nodes['DUT1']['host']) papi_exec.add(cmd1, **args1).get_reply(err_msg) # Set IP address on VPP node 1 interface cmd1 = 'sw_interface_add_del_address' args1 = dict( sw_if_index=InterfaceUtil.get_interface_index( nodes['DUT1'], if1_key), is_add=1, is_ipv6=1 if if2_ip.version == 6 else 0, del_all=0, address_length=96 if if2_ip.version == 6 else 24, address=getattr(if2_ip - 1, 'packed')) err_msg = 'Failed to set IP address on interface {ifc} on host ' \ '{host}'.format(ifc=if1_key, host=nodes['DUT1']['host']) papi_exec.add(cmd1, **args1).get_reply(err_msg) # Configure IPsec tunnel interfaces args1 = dict( sw_if_index=loop_sw_if_idx, is_add=1, is_ipv6=1 if if1_ip.version == 6 else 0, del_all=0, address_length=128 if if1_ip.version == 6 else 32, address='') cmd2 = 'ipsec_tunnel_if_add_del' args2 = dict( is_add=1, local_ip=None, remote_ip=None, local_spi=0, remote_spi=0, crypto_alg=crypto_alg.alg_int_repr, local_crypto_key_len=0, local_crypto_key=None, remote_crypto_key_len=0, remote_crypto_key=None, integ_alg=integ_alg.alg_int_repr if integ_alg else 0, local_integ_key_len=0, local_integ_key=None, remote_integ_key_len=0, remote_integ_key=None, tx_table_id=0 ) err_msg = 'Failed to add IPsec tunnel interfaces on host {host}'.\ format(host=nodes['DUT1']['host']) ipsec_tunnels = list() ckeys = list() ikeys = list() for i in xrange(n_tunnels): ckeys.append( gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg))) if integ_alg: ikeys.append( gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg))) args1['address'] = getattr(if1_ip + i * addr_incr, 'packed') args2['local_spi'] = spi_1 + i args2['remote_spi'] = spi_2 + i args2['local_ip'] = IPUtil.create_ip_address_object( if1_ip + i * addr_incr) args2['remote_ip'] = IPUtil.create_ip_address_object(if2_ip) args2['local_crypto_key_len'] = len(ckeys[i]) args2['local_crypto_key'] = ckeys[i] args2['remote_crypto_key_len'] = len(ckeys[i]) args2['remote_crypto_key'] = ckeys[i] if integ_alg: args2['local_integ_key_len'] = len(ikeys[i]) args2['local_integ_key'] = ikeys[i] args2['remote_integ_key_len'] = len(ikeys[i]) args2['remote_integ_key'] = ikeys[i] history = False if 1 < i < n_tunnels - 1 else True papi_exec.add(cmd1, history=history, **args1).\ add(cmd2, history=history, **args2) replies = papi_exec.get_replies(err_msg) for reply in replies: if 'sw_if_index' in reply: ipsec_tunnels.append(reply["sw_if_index"]) # Configure IP routes cmd1 = 'sw_interface_set_unnumbered' args1 = dict( is_add=1, sw_if_index=InterfaceUtil.get_interface_index( nodes['DUT1'], if1_key), unnumbered_sw_if_index=0 ) cmd2 = 'sw_interface_set_flags' args2 = dict( sw_if_index=0, admin_up_down=1) cmd3 = 'ip_route_add_del' route = IPUtil.compose_vpp_route_structure( nodes['DUT1'], raddr_ip2.compressed, prefix_len=128 if raddr_ip2.version == 6 else 32, interface=0 ) args3 = dict( is_add=1, is_multipath=0, route=route ) err_msg = 'Failed to add IP routes on host {host}'.format( host=nodes['DUT1']['host']) for i in xrange(n_tunnels): args1['unnumbered_sw_if_index'] = ipsec_tunnels[i] args2['sw_if_index'] = ipsec_tunnels[i] args3['route']['prefix']['address']['un'] = \ IPUtil.union_addr(raddr_ip2 + i) args3['route']['paths'][0]['sw_if_index'] = \ ipsec_tunnels[i] history = False if 1 < i < n_tunnels - 1 else True papi_exec.add(cmd1, history=history, **args1).\ add(cmd2, history=history, **args2).\ add(cmd3, history=history, **args3) papi_exec.get_replies(err_msg) with PapiSocketExecutor(nodes['DUT2']) as papi_exec: # Set IP address on VPP node 2 interface cmd1 = 'sw_interface_add_del_address' args1 = dict( sw_if_index=InterfaceUtil.get_interface_index( nodes['DUT2'], if2_key), is_add=1, is_ipv6=1 if if2_ip.version == 6 else 0, del_all=0, address_length=96 if if2_ip.version == 6 else 24, address=if2_ip.packed) err_msg = 'Failed to set IP address on interface {ifc} on host ' \ '{host}'.format(ifc=if2_key, host=nodes['DUT2']['host']) papi_exec.add(cmd1, **args1).get_reply(err_msg) # Configure IPsec tunnel interfaces cmd2 = 'ipsec_tunnel_if_add_del' args2 = dict( is_add=1, local_ip=IPUtil.create_ip_address_object(if2_ip), remote_ip=None, local_spi=0, remote_spi=0, crypto_alg=crypto_alg.alg_int_repr, local_crypto_key_len=0, local_crypto_key=None, remote_crypto_key_len=0, remote_crypto_key=None, integ_alg=integ_alg.alg_int_repr if integ_alg else 0, local_integ_key_len=0, local_integ_key=None, remote_integ_key_len=0, remote_integ_key=None, tx_table_id=0 ) err_msg = 'Failed to add IPsec tunnel interfaces on host {host}'. \ format(host=nodes['DUT2']['host']) ipsec_tunnels = list() for i in xrange(n_tunnels): args2['local_spi'] = spi_2 + i args2['remote_spi'] = spi_1 + i args2['local_ip'] = IPUtil.create_ip_address_object(if2_ip) args2['remote_ip'] = IPUtil.create_ip_address_object( if1_ip + i * addr_incr) args2['local_crypto_key_len'] = len(ckeys[i]) args2['local_crypto_key'] = ckeys[i] args2['remote_crypto_key_len'] = len(ckeys[i]) args2['remote_crypto_key'] = ckeys[i] if integ_alg: args2['local_integ_key_len'] = len(ikeys[i]) args2['local_integ_key'] = ikeys[i] args2['remote_integ_key_len'] = len(ikeys[i]) args2['remote_integ_key'] = ikeys[i] history = False if 1 < i < n_tunnels - 1 else True papi_exec.add(cmd2, history=history, **args2) replies = papi_exec.get_replies(err_msg) for reply in replies: if 'sw_if_index' in reply: ipsec_tunnels.append(reply["sw_if_index"]) # Configure IP routes cmd1 = 'ip_route_add_del' route = IPUtil.compose_vpp_route_structure( nodes['DUT2'], if1_ip.compressed, prefix_len=32 if if1_ip.version == 6 else 8, interface=if2_key, gateway=(if2_ip - 1).compressed ) args1 = dict( is_add=1, is_multipath=0, route=route ) papi_exec.add(cmd1, **args1) cmd1 = 'sw_interface_set_unnumbered' args1 = dict( is_add=1, sw_if_index=InterfaceUtil.get_interface_index( nodes['DUT2'], if2_key), unnumbered_sw_if_index=0 ) cmd2 = 'sw_interface_set_flags' args2 = dict( sw_if_index=0, admin_up_down=1) cmd3 = 'ip_route_add_del' route = IPUtil.compose_vpp_route_structure( nodes['DUT2'], raddr_ip1.compressed, prefix_len=128 if raddr_ip1.version == 6 else 32, interface=0 ) args3 = dict( is_add=1, is_multipath=0, route=route ) err_msg = 'Failed to add IP routes on host {host}'.format( host=nodes['DUT2']['host']) for i in xrange(n_tunnels): args1['unnumbered_sw_if_index'] = ipsec_tunnels[i] args2['sw_if_index'] = ipsec_tunnels[i] args3['route']['prefix']['address']['un'] = \ IPUtil.union_addr(raddr_ip1 + i) args3['route']['paths'][0]['sw_if_index'] = \ ipsec_tunnels[i] history = False if 1 < i < n_tunnels - 1 else True papi_exec.add(cmd1, history=history, **args1). \ add(cmd2, history=history, **args2). \ add(cmd3, history=history, **args3) papi_exec.get_replies(err_msg) @staticmethod def vpp_ipsec_add_multiple_tunnels( nodes, interface1, interface2, n_tunnels, crypto_alg, integ_alg, tunnel_ip1, tunnel_ip2, raddr_ip1, raddr_ip2, raddr_range): """Create multiple IPsec tunnels between two VPP nodes. :param nodes: VPP nodes to create tunnels. :param interface1: Interface name or sw_if_index on node 1. :param interface2: Interface name or sw_if_index on node 2. :param n_tunnels: Number of tunnels to create. :param crypto_alg: The encryption algorithm name. :param integ_alg: The integrity algorithm name. :param tunnel_ip1: Tunnel node1 IPv4 address. :param tunnel_ip2: Tunnel node2 IPv4 address. :param raddr_ip1: Policy selector remote IPv4 start address for the first tunnel in direction node1->node2. :param raddr_ip2: Policy selector remote IPv4 start address for the first tunnel in direction node2->node1. :param raddr_range: Mask specifying range of Policy selector Remote IPv4 addresses. Valid values are from 1 to 32. :type nodes: dict :type interface1: str or int :type interface2: str or int :type n_tunnels: int :type crypto_alg: CryptoAlg :type integ_alg: IntegAlg :type tunnel_ip1: str :type tunnel_ip2: str :type raddr_ip1: string :type raddr_ip2: string :type raddr_range: int """ spd_id = 1 p_hi = 100 p_lo = 10 sa_id_1 = 100000 sa_id_2 = 200000 spi_1 = 300000 spi_2 = 400000 crypto_key = gen_key(IPsecUtil.get_crypto_alg_key_len(crypto_alg)) integ_key = gen_key(IPsecUtil.get_integ_alg_key_len(integ_alg)) \ if integ_alg else '' IPsecUtil.vpp_ipsec_set_ip_route( nodes['DUT1'], n_tunnels, tunnel_ip1, raddr_ip2, tunnel_ip2, interface1, raddr_range) IPsecUtil.vpp_ipsec_set_ip_route( nodes['DUT2'], n_tunnels, tunnel_ip2, raddr_ip1, tunnel_ip1, interface2, raddr_range) IPsecUtil.vpp_ipsec_add_spd( nodes['DUT1'], spd_id) IPsecUtil.vpp_ipsec_spd_add_if( nodes['DUT1'], spd_id, interface1) IPsecUtil.vpp_ipsec_policy_add( nodes['DUT1'], spd_id, p_hi, PolicyAction.BYPASS, inbound=False, proto=50, laddr_range='100.0.0.0/8', raddr_range='100.0.0.0/8') IPsecUtil.vpp_ipsec_policy_add( nodes['DUT1'], spd_id, p_hi, PolicyAction.BYPASS, inbound=True, proto=50, laddr_range='100.0.0.0/8', raddr_range='100.0.0.0/8') IPsecUtil.vpp_ipsec_add_spd( nodes['DUT2'], spd_id) IPsecUtil.vpp_ipsec_spd_add_if( nodes['DUT2'], spd_id, interface2) IPsecUtil.vpp_ipsec_policy_add( nodes['DUT2'], spd_id, p_hi, PolicyAction.BYPASS, inbound=False, proto=50, laddr_range='100.0.0.0/8', raddr_range='100.0.0.0/8') IPsecUtil.vpp_ipsec_policy_add( nodes['DUT2'], spd_id, p_hi, PolicyAction.BYPASS, inbound=True, proto=50, laddr_range='100.0.0.0/8', raddr_range='100.0.0.0/8') IPsecUtil.vpp_ipsec_add_sad_entries( nodes['DUT1'], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key, integ_alg, integ_key, tunnel_ip1, tunnel_ip2) IPsecUtil.vpp_ipsec_spd_add_entries( nodes['DUT1'], n_tunnels, spd_id, p_lo, False, sa_id_1, raddr_ip2) IPsecUtil.vpp_ipsec_add_sad_entries( nodes['DUT2'], n_tunnels, sa_id_1, spi_1, crypto_alg, crypto_key, integ_alg, integ_key, tunnel_ip1, tunnel_ip2) IPsecUtil.vpp_ipsec_spd_add_entries( nodes['DUT2'], n_tunnels, spd_id, p_lo, True, sa_id_1, raddr_ip2) IPsecUtil.vpp_ipsec_add_sad_entries( nodes['DUT2'], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key, integ_alg, integ_key, tunnel_ip2, tunnel_ip1) IPsecUtil.vpp_ipsec_spd_add_entries( nodes['DUT2'], n_tunnels, spd_id, p_lo, False, sa_id_2, raddr_ip1) IPsecUtil.vpp_ipsec_add_sad_entries( nodes['DUT1'], n_tunnels, sa_id_2, spi_2, crypto_alg, crypto_key, integ_alg, integ_key, tunnel_ip2, tunnel_ip1) IPsecUtil.vpp_ipsec_spd_add_entries( nodes['DUT1'], n_tunnels, spd_id, p_lo, True, sa_id_2, raddr_ip1) @staticmethod def vpp_ipsec_show(node): """Run "show ipsec" debug CLI command. :param node: Node to run command on. :type node: dict """ PapiSocketExecutor.run_cli_cmd(node, 'show ipsec')