wireguard: add ipv6 support
[vpp.git] / test / test_wireguard.py
1 #!/usr/bin/env python3
2 """ Wg tests """
3
4 import datetime
5 import base64
6
7 from hashlib import blake2s
8 from scapy.packet import Packet
9 from scapy.packet import Raw
10 from scapy.layers.l2 import Ether, ARP
11 from scapy.layers.inet import IP, UDP
12 from scapy.layers.inet6 import IPv6
13 from scapy.contrib.wireguard import Wireguard, WireguardResponse, \
14     WireguardInitiation, WireguardTransport
15 from cryptography.hazmat.primitives.asymmetric.x25519 import \
16     X25519PrivateKey, X25519PublicKey
17 from cryptography.hazmat.primitives.serialization import Encoding, \
18     PrivateFormat, PublicFormat, NoEncryption
19 from cryptography.hazmat.primitives.hashes import BLAKE2s, Hash
20 from cryptography.hazmat.primitives.hmac import HMAC
21 from cryptography.hazmat.backends import default_backend
22 from noise.connection import NoiseConnection, Keypair
23
24 from vpp_ipip_tun_interface import VppIpIpTunInterface
25 from vpp_interface import VppInterface
26 from vpp_ip_route import VppIpRoute, VppRoutePath
27 from vpp_object import VppObject
28 from framework import VppTestCase
29 from re import compile
30 import unittest
31
32 """ TestWg is a subclass of  VPPTestCase classes.
33
34 Wg test.
35
36 """
37
38
39 def private_key_bytes(k):
40     return k.private_bytes(Encoding.Raw,
41                            PrivateFormat.Raw,
42                            NoEncryption())
43
44
45 def public_key_bytes(k):
46     return k.public_bytes(Encoding.Raw,
47                           PublicFormat.Raw)
48
49
50 class VppWgInterface(VppInterface):
51     """
52     VPP WireGuard interface
53     """
54
55     def __init__(self, test, src, port):
56         super(VppWgInterface, self).__init__(test)
57
58         self.port = port
59         self.src = src
60         self.private_key = X25519PrivateKey.generate()
61         self.public_key = self.private_key.public_key()
62
63     def public_key_bytes(self):
64         return public_key_bytes(self.public_key)
65
66     def private_key_bytes(self):
67         return private_key_bytes(self.private_key)
68
69     def add_vpp_config(self):
70         r = self.test.vapi.wireguard_interface_create(interface={
71             'user_instance': 0xffffffff,
72             'port': self.port,
73             'src_ip': self.src,
74             'private_key': private_key_bytes(self.private_key),
75             'generate_key': False
76         })
77         self.set_sw_if_index(r.sw_if_index)
78         self.test.registry.register(self, self.test.logger)
79         return self
80
81     def remove_vpp_config(self):
82         self.test.vapi.wireguard_interface_delete(
83             sw_if_index=self._sw_if_index)
84
85     def query_vpp_config(self):
86         ts = self.test.vapi.wireguard_interface_dump(sw_if_index=0xffffffff)
87         for t in ts:
88             if t.interface.sw_if_index == self._sw_if_index and \
89                str(t.interface.src_ip) == self.src and \
90                t.interface.port == self.port and \
91                t.interface.private_key == private_key_bytes(self.private_key):
92                 return True
93         return False
94
95     def __str__(self):
96         return self.object_id()
97
98     def object_id(self):
99         return "wireguard-%d" % self._sw_if_index
100
101
102 def find_route(test, prefix, is_ip6, table_id=0):
103     routes = test.vapi.ip_route_dump(table_id, is_ip6)
104
105     for e in routes:
106         if table_id == e.route.table_id \
107            and str(e.route.prefix) == str(prefix):
108             return True
109     return False
110
111
112 NOISE_HANDSHAKE_NAME = b"Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s"
113 NOISE_IDENTIFIER_NAME = b"WireGuard v1 zx2c4 Jason@zx2c4.com"
114
115
116 class VppWgPeer(VppObject):
117
118     def __init__(self,
119                  test,
120                  itf,
121                  endpoint,
122                  port,
123                  allowed_ips,
124                  persistent_keepalive=15):
125         self._test = test
126         self.itf = itf
127         self.endpoint = endpoint
128         self.port = port
129         self.allowed_ips = allowed_ips
130         self.persistent_keepalive = persistent_keepalive
131
132         # remote peer's public
133         self.private_key = X25519PrivateKey.generate()
134         self.public_key = self.private_key.public_key()
135
136         self.noise = NoiseConnection.from_name(NOISE_HANDSHAKE_NAME)
137
138     def add_vpp_config(self, is_ip6=False):
139         rv = self._test.vapi.wireguard_peer_add(
140             peer={
141                 'public_key': self.public_key_bytes(),
142                 'port': self.port,
143                 'endpoint': self.endpoint,
144                 'n_allowed_ips': len(self.allowed_ips),
145                 'allowed_ips': self.allowed_ips,
146                 'sw_if_index': self.itf.sw_if_index,
147                 'persistent_keepalive': self.persistent_keepalive})
148         self.index = rv.peer_index
149         self.receiver_index = self.index + 1
150         self._test.registry.register(self, self._test.logger)
151         return self
152
153     def remove_vpp_config(self):
154         self._test.vapi.wireguard_peer_remove(peer_index=self.index)
155
156     def object_id(self):
157         return ("wireguard-peer-%s" % self.index)
158
159     def public_key_bytes(self):
160         return public_key_bytes(self.public_key)
161
162     def query_vpp_config(self):
163         peers = self._test.vapi.wireguard_peers_dump()
164
165         for p in peers:
166             if p.peer.public_key == self.public_key_bytes() and \
167                p.peer.port == self.port and \
168                str(p.peer.endpoint) == self.endpoint and \
169                p.peer.sw_if_index == self.itf.sw_if_index and \
170                len(self.allowed_ips) == p.peer.n_allowed_ips:
171                 self.allowed_ips.sort()
172                 p.peer.allowed_ips.sort()
173
174                 for (a1, a2) in zip(self.allowed_ips, p.peer.allowed_ips):
175                     if str(a1) != str(a2):
176                         return False
177                 return True
178         return False
179
180     def set_responder(self):
181         self.noise.set_as_responder()
182
183     def mk_tunnel_header(self, tx_itf, is_ip6=False):
184         if is_ip6 is False:
185             return (Ether(dst=tx_itf.local_mac, src=tx_itf.remote_mac) /
186                     IP(src=self.endpoint, dst=self.itf.src) /
187                     UDP(sport=self.port, dport=self.itf.port))
188         else:
189             return (Ether(dst=tx_itf.local_mac, src=tx_itf.remote_mac) /
190                     IPv6(src=self.endpoint, dst=self.itf.src) /
191                     UDP(sport=self.port, dport=self.itf.port))
192
193     def noise_init(self, public_key=None):
194         self.noise.set_prologue(NOISE_IDENTIFIER_NAME)
195         self.noise.set_psks(psk=bytes(bytearray(32)))
196
197         if not public_key:
198             public_key = self.itf.public_key
199
200         # local/this private
201         self.noise.set_keypair_from_private_bytes(
202             Keypair.STATIC,
203             private_key_bytes(self.private_key))
204         # remote's public
205         self.noise.set_keypair_from_public_bytes(
206             Keypair.REMOTE_STATIC,
207             public_key_bytes(public_key))
208
209         self.noise.start_handshake()
210
211     def mk_handshake(self, tx_itf, is_ip6=False, public_key=None):
212         self.noise.set_as_initiator()
213         self.noise_init(public_key)
214
215         p = (Wireguard() / WireguardInitiation())
216
217         p[Wireguard].message_type = 1
218         p[Wireguard].reserved_zero = 0
219         p[WireguardInitiation].sender_index = self.receiver_index
220
221         # some random data for the message
222         #  lifted from the noise protocol's wireguard example
223         now = datetime.datetime.now()
224         tai = struct.pack('!qi', 4611686018427387914 + int(now.timestamp()),
225                           int(now.microsecond * 1e3))
226         b = self.noise.write_message(payload=tai)
227
228         # load noise into init message
229         p[WireguardInitiation].unencrypted_ephemeral = b[0:32]
230         p[WireguardInitiation].encrypted_static = b[32:80]
231         p[WireguardInitiation].encrypted_timestamp = b[80:108]
232
233         # generate the mac1 hash
234         mac_key = blake2s(b'mac1----' +
235                           self.itf.public_key_bytes()).digest()
236         p[WireguardInitiation].mac1 = blake2s(bytes(p)[0:116],
237                                               digest_size=16,
238                                               key=mac_key).digest()
239         p[WireguardInitiation].mac2 = bytearray(16)
240
241         p = (self.mk_tunnel_header(tx_itf, is_ip6) / p)
242
243         return p
244
245     def verify_header(self, p, is_ip6=False):
246         if is_ip6 is False:
247             self._test.assertEqual(p[IP].src, self.itf.src)
248             self._test.assertEqual(p[IP].dst, self.endpoint)
249         else:
250             self._test.assertEqual(p[IPv6].src, self.itf.src)
251             self._test.assertEqual(p[IPv6].dst, self.endpoint)
252         self._test.assertEqual(p[UDP].sport, self.itf.port)
253         self._test.assertEqual(p[UDP].dport, self.port)
254         self._test.assert_packet_checksums_valid(p)
255
256     def consume_init(self, p, tx_itf, is_ip6=False):
257         self.noise.set_as_responder()
258         self.noise_init(self.itf.public_key)
259         self.verify_header(p, is_ip6)
260
261         init = Wireguard(p[Raw])
262
263         self._test.assertEqual(init[Wireguard].message_type, 1)
264         self._test.assertEqual(init[Wireguard].reserved_zero, 0)
265
266         self.sender = init[WireguardInitiation].sender_index
267
268         # validate the hash
269         mac_key = blake2s(b'mac1----' +
270                           public_key_bytes(self.public_key)).digest()
271         mac1 = blake2s(bytes(init)[0:-32],
272                        digest_size=16,
273                        key=mac_key).digest()
274         self._test.assertEqual(init[WireguardInitiation].mac1, mac1)
275
276         # this passes only unencrypted_ephemeral, encrypted_static,
277         # encrypted_timestamp fields of the init
278         payload = self.noise.read_message(bytes(init)[8:-32])
279
280         # build the response
281         b = self.noise.write_message()
282         mac_key = blake2s(b'mac1----' +
283                           public_key_bytes(self.itf.public_key)).digest()
284         resp = (Wireguard(message_type=2, reserved_zero=0) /
285                 WireguardResponse(sender_index=self.receiver_index,
286                                   receiver_index=self.sender,
287                                   unencrypted_ephemeral=b[0:32],
288                                   encrypted_nothing=b[32:]))
289         mac1 = blake2s(bytes(resp)[:-32],
290                        digest_size=16,
291                        key=mac_key).digest()
292         resp[WireguardResponse].mac1 = mac1
293
294         resp = (self.mk_tunnel_header(tx_itf, is_ip6) / resp)
295         self._test.assertTrue(self.noise.handshake_finished)
296
297         return resp
298
299     def consume_response(self, p, is_ip6=False):
300         self.verify_header(p, is_ip6)
301
302         resp = Wireguard(p[Raw])
303
304         self._test.assertEqual(resp[Wireguard].message_type, 2)
305         self._test.assertEqual(resp[Wireguard].reserved_zero, 0)
306         self._test.assertEqual(resp[WireguardResponse].receiver_index,
307                                self.receiver_index)
308
309         self.sender = resp[Wireguard].sender_index
310
311         payload = self.noise.read_message(bytes(resp)[12:60])
312         self._test.assertEqual(payload, b'')
313         self._test.assertTrue(self.noise.handshake_finished)
314
315     def decrypt_transport(self, p, is_ip6=False):
316         self.verify_header(p, is_ip6)
317
318         p = Wireguard(p[Raw])
319         self._test.assertEqual(p[Wireguard].message_type, 4)
320         self._test.assertEqual(p[Wireguard].reserved_zero, 0)
321         self._test.assertEqual(p[WireguardTransport].receiver_index,
322                                self.receiver_index)
323
324         d = self.noise.decrypt(
325             p[WireguardTransport].encrypted_encapsulated_packet)
326         return d
327
328     def encrypt_transport(self, p):
329         return self.noise.encrypt(bytes(p))
330
331     def validate_encapped(self, rxs, tx, is_ip6=False):
332         for rx in rxs:
333             if is_ip6 is False:
334                 rx = IP(self.decrypt_transport(rx))
335
336                 # chech the oringial packet is present
337                 self._test.assertEqual(rx[IP].dst, tx[IP].dst)
338                 self._test.assertEqual(rx[IP].ttl, tx[IP].ttl-1)
339             else:
340                 rx = IPv6(self.decrypt_transport(rx))
341
342                 # chech the oringial packet is present
343                 self._test.assertEqual(rx[IPv6].dst, tx[IPv6].dst)
344                 self._test.assertEqual(rx[IPv6].ttl, tx[IPv6].ttl-1)
345
346
347 class TestWg(VppTestCase):
348     """ Wireguard Test Case """
349
350     error_str = compile(r"Error")
351
352     wg4_output_node_name = '/err/wg4-output-tun/'
353     wg4_input_node_name = '/err/wg4-input/'
354     wg6_output_node_name = '/err/wg6-output-tun/'
355     wg6_input_node_name = '/err/wg6-input/'
356     kp4_error = wg4_output_node_name + "Keypair error"
357     mac4_error = wg4_input_node_name + "Invalid MAC handshake"
358     peer4_error = wg4_input_node_name + "Peer error"
359     kp6_error = wg6_output_node_name + "Keypair error"
360     mac6_error = wg6_input_node_name + "Invalid MAC handshake"
361     peer6_error = wg6_input_node_name + "Peer error"
362
363     @classmethod
364     def setUpClass(cls):
365         super(TestWg, cls).setUpClass()
366         try:
367             cls.create_pg_interfaces(range(3))
368             for i in cls.pg_interfaces:
369                 i.admin_up()
370                 i.config_ip4()
371                 i.config_ip6()
372                 i.resolve_arp()
373                 i.resolve_ndp()
374
375         except Exception:
376             super(TestWg, cls).tearDownClass()
377             raise
378
379     @classmethod
380     def tearDownClass(cls):
381         super(TestWg, cls).tearDownClass()
382
383     def setUp(self):
384         super(VppTestCase, self).setUp()
385         self.base_kp4_err = self.statistics.get_err_counter(self.kp4_error)
386         self.base_mac4_err = self.statistics.get_err_counter(self.mac4_error)
387         self.base_peer4_err = self.statistics.get_err_counter(self.peer4_error)
388         self.base_kp6_err = self.statistics.get_err_counter(self.kp6_error)
389         self.base_mac6_err = self.statistics.get_err_counter(self.mac6_error)
390         self.base_peer6_err = self.statistics.get_err_counter(self.peer6_error)
391
392     def test_wg_interface(self):
393         """ Simple interface creation """
394         port = 12312
395
396         # Create interface
397         wg0 = VppWgInterface(self,
398                              self.pg1.local_ip4,
399                              port).add_vpp_config()
400
401         self.logger.info(self.vapi.cli("sh int"))
402
403         # delete interface
404         wg0.remove_vpp_config()
405
406     def test_handshake_hash(self):
407         """ test hashing an init message """
408         # a init packet generated by linux given the key below
409         h = "0100000098b9032b" \
410             "55cc4b39e73c3d24" \
411             "a2a1ab884b524a81" \
412             "1808bb86640fb70d" \
413             "e93154fec1879125" \
414             "ab012624a27f0b75" \
415             "c0a2582f438ddb5f" \
416             "8e768af40b4ab444" \
417             "02f9ff473e1b797e" \
418             "80d39d93c5480c82" \
419             "a3d4510f70396976" \
420             "586fb67300a5167b" \
421             "ae6ca3ff3dfd00eb" \
422             "59be198810f5aa03" \
423             "6abc243d2155ee4f" \
424             "2336483900aef801" \
425             "08752cd700000000" \
426             "0000000000000000" \
427             "00000000"
428
429         b = bytearray.fromhex(h)
430         tgt = Wireguard(b)
431
432         pubb = base64.b64decode("aRuHFTTxICIQNefp05oKWlJv3zgKxb8+WW7JJMh0jyM=")
433         pub = X25519PublicKey.from_public_bytes(pubb)
434
435         self.assertEqual(pubb, public_key_bytes(pub))
436
437         # strip the macs and build a new packet
438         init = b[0:-32]
439         mac_key = blake2s(b'mac1----' + public_key_bytes(pub)).digest()
440         init += blake2s(init,
441                         digest_size=16,
442                         key=mac_key).digest()
443         init += b'\x00' * 16
444
445         act = Wireguard(init)
446
447         self.assertEqual(tgt, act)
448
449     def test_wg_peer_resp(self):
450         """ Send handshake response """
451         port = 12323
452
453         # Create interfaces
454         wg0 = VppWgInterface(self,
455                              self.pg1.local_ip4,
456                              port).add_vpp_config()
457         wg0.admin_up()
458         wg0.config_ip4()
459
460         self.pg_enable_capture(self.pg_interfaces)
461         self.pg_start()
462
463         peer_1 = VppWgPeer(self,
464                            wg0,
465                            self.pg1.remote_ip4,
466                            port+1,
467                            ["10.11.3.0/24"]).add_vpp_config()
468         self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
469
470         r1 = VppIpRoute(self, "10.11.3.0", 24,
471                         [VppRoutePath("10.11.3.1",
472                                       wg0.sw_if_index)]).add_vpp_config()
473
474         # wait for the peer to send a handshake
475         rx = self.pg1.get_capture(1, timeout=2)
476
477         # consume the handshake in the noise protocol and
478         # generate the response
479         resp = peer_1.consume_init(rx[0], self.pg1)
480
481         # send the response, get keepalive
482         rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
483
484         for rx in rxs:
485             b = peer_1.decrypt_transport(rx)
486             self.assertEqual(0, len(b))
487
488         # send a packets that are routed into the tunnel
489         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
490              IP(src=self.pg0.remote_ip4, dst="10.11.3.2") /
491              UDP(sport=555, dport=556) /
492              Raw(b'\x00' * 80))
493
494         rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
495
496         peer_1.validate_encapped(rxs, p)
497
498         # send packets into the tunnel, expect to receive them on
499         # the other side
500         p = [(peer_1.mk_tunnel_header(self.pg1) /
501               Wireguard(message_type=4, reserved_zero=0) /
502               WireguardTransport(
503                   receiver_index=peer_1.sender,
504                   counter=ii,
505                   encrypted_encapsulated_packet=peer_1.encrypt_transport(
506                       (IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20) /
507                        UDP(sport=222, dport=223) /
508                        Raw())))) for ii in range(255)]
509
510         rxs = self.send_and_expect(self.pg1, p, self.pg0)
511
512         for rx in rxs:
513             self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
514             self.assertEqual(rx[IP].ttl, 19)
515
516         r1.remove_vpp_config()
517         peer_1.remove_vpp_config()
518         wg0.remove_vpp_config()
519
520     def test_wg_peer_v4o4(self):
521         """ Test v4o4"""
522
523         port = 12333
524
525         # Create interfaces
526         wg0 = VppWgInterface(self,
527                              self.pg1.local_ip4,
528                              port).add_vpp_config()
529         wg0.admin_up()
530         wg0.config_ip4()
531
532         peer_1 = VppWgPeer(self,
533                            wg0,
534                            self.pg1.remote_ip4,
535                            port+1,
536                            ["10.11.3.0/24"]).add_vpp_config()
537         self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
538
539         r1 = VppIpRoute(self, "10.11.3.0", 24,
540                         [VppRoutePath("10.11.3.1",
541                                       wg0.sw_if_index)]).add_vpp_config()
542
543         # route a packet into the wg interface
544         #  use the allowed-ip prefix
545         #  this is dropped because the peer is not initiated
546         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
547              IP(src=self.pg0.remote_ip4, dst="10.11.3.2") /
548              UDP(sport=555, dport=556) /
549              Raw())
550         self.send_and_assert_no_replies(self.pg0, [p])
551         self.assertEqual(self.base_kp4_err + 1,
552                          self.statistics.get_err_counter(self.kp4_error))
553
554         # send a handsake from the peer with an invalid MAC
555         p = peer_1.mk_handshake(self.pg1)
556         p[WireguardInitiation].mac1 = b'foobar'
557         self.send_and_assert_no_replies(self.pg1, [p])
558         self.assertEqual(self.base_mac4_err + 1,
559                          self.statistics.get_err_counter(self.mac4_error))
560
561         # send a handsake from the peer but signed by the wrong key.
562         p = peer_1.mk_handshake(self.pg1,
563                                 False,
564                                 X25519PrivateKey.generate().public_key())
565         self.send_and_assert_no_replies(self.pg1, [p])
566         self.assertEqual(self.base_peer4_err + 1,
567                          self.statistics.get_err_counter(self.peer4_error))
568
569         # send a valid handsake init for which we expect a response
570         p = peer_1.mk_handshake(self.pg1)
571
572         rx = self.send_and_expect(self.pg1, [p], self.pg1)
573
574         peer_1.consume_response(rx[0])
575
576         # route a packet into the wg interface
577         #  this is dropped because the peer is still not initiated
578         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
579              IP(src=self.pg0.remote_ip4, dst="10.11.3.2") /
580              UDP(sport=555, dport=556) /
581              Raw())
582         self.send_and_assert_no_replies(self.pg0, [p])
583         self.assertEqual(self.base_kp4_err + 2,
584                          self.statistics.get_err_counter(self.kp4_error))
585
586         # send a data packet from the peer through the tunnel
587         # this completes the handshake
588         p = (IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20) /
589              UDP(sport=222, dport=223) /
590              Raw())
591         d = peer_1.encrypt_transport(p)
592         p = (peer_1.mk_tunnel_header(self.pg1) /
593              (Wireguard(message_type=4, reserved_zero=0) /
594               WireguardTransport(receiver_index=peer_1.sender,
595                                  counter=0,
596                                  encrypted_encapsulated_packet=d)))
597         rxs = self.send_and_expect(self.pg1, [p], self.pg0)
598
599         for rx in rxs:
600             self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
601             self.assertEqual(rx[IP].ttl, 19)
602
603         # send a packets that are routed into the tunnel
604         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
605              IP(src=self.pg0.remote_ip4, dst="10.11.3.2") /
606              UDP(sport=555, dport=556) /
607              Raw(b'\x00' * 80))
608
609         rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
610
611         for rx in rxs:
612             rx = IP(peer_1.decrypt_transport(rx))
613
614             # chech the oringial packet is present
615             self.assertEqual(rx[IP].dst, p[IP].dst)
616             self.assertEqual(rx[IP].ttl, p[IP].ttl-1)
617
618         # send packets into the tunnel, expect to receive them on
619         # the other side
620         p = [(peer_1.mk_tunnel_header(self.pg1) /
621               Wireguard(message_type=4, reserved_zero=0) /
622               WireguardTransport(
623                   receiver_index=peer_1.sender,
624                   counter=ii+1,
625                   encrypted_encapsulated_packet=peer_1.encrypt_transport(
626                       (IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20) /
627                        UDP(sport=222, dport=223) /
628                        Raw())))) for ii in range(255)]
629
630         rxs = self.send_and_expect(self.pg1, p, self.pg0)
631
632         for rx in rxs:
633             self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
634             self.assertEqual(rx[IP].ttl, 19)
635
636         r1.remove_vpp_config()
637         peer_1.remove_vpp_config()
638         wg0.remove_vpp_config()
639
640     def test_wg_peer_v6o6(self):
641         """ Test v6o6"""
642
643         port = 12343
644
645         # Create interfaces
646         wg0 = VppWgInterface(self,
647                              self.pg1.local_ip6,
648                              port).add_vpp_config()
649         wg0.admin_up()
650         wg0.config_ip6()
651
652         peer_1 = VppWgPeer(self,
653                            wg0,
654                            self.pg1.remote_ip6,
655                            port+1,
656                            ["1::3:0/112"]).add_vpp_config(True)
657         self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
658
659         r1 = VppIpRoute(self, "1::3:0", 112,
660                         [VppRoutePath("1::3:1",
661                                       wg0.sw_if_index)]).add_vpp_config()
662
663         # route a packet into the wg interface
664         #  use the allowed-ip prefix
665         #  this is dropped because the peer is not initiated
666
667         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
668              IPv6(src=self.pg0.remote_ip6, dst="1::3:2") /
669              UDP(sport=555, dport=556) /
670              Raw())
671         self.send_and_assert_no_replies(self.pg0, [p])
672
673         self.assertEqual(self.base_kp6_err + 1,
674                          self.statistics.get_err_counter(self.kp6_error))
675
676         # send a handsake from the peer with an invalid MAC
677         p = peer_1.mk_handshake(self.pg1, True)
678         p[WireguardInitiation].mac1 = b'foobar'
679         self.send_and_assert_no_replies(self.pg1, [p])
680
681         self.assertEqual(self.base_mac6_err + 1,
682                          self.statistics.get_err_counter(self.mac6_error))
683
684         # send a handsake from the peer but signed by the wrong key.
685         p = peer_1.mk_handshake(self.pg1,
686                                 True,
687                                 X25519PrivateKey.generate().public_key())
688         self.send_and_assert_no_replies(self.pg1, [p])
689         self.assertEqual(self.base_peer6_err + 1,
690                          self.statistics.get_err_counter(self.peer6_error))
691
692         # send a valid handsake init for which we expect a response
693         p = peer_1.mk_handshake(self.pg1, True)
694
695         rx = self.send_and_expect(self.pg1, [p], self.pg1)
696
697         peer_1.consume_response(rx[0], True)
698
699         # route a packet into the wg interface
700         #  this is dropped because the peer is still not initiated
701         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
702              IPv6(src=self.pg0.remote_ip6, dst="1::3:2") /
703              UDP(sport=555, dport=556) /
704              Raw())
705         self.send_and_assert_no_replies(self.pg0, [p])
706         self.assertEqual(self.base_kp6_err + 2,
707                          self.statistics.get_err_counter(self.kp6_error))
708
709         # send a data packet from the peer through the tunnel
710         # this completes the handshake
711         p = (IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20) /
712              UDP(sport=222, dport=223) /
713              Raw())
714         d = peer_1.encrypt_transport(p)
715         p = (peer_1.mk_tunnel_header(self.pg1, True) /
716              (Wireguard(message_type=4, reserved_zero=0) /
717               WireguardTransport(receiver_index=peer_1.sender,
718                                  counter=0,
719                                  encrypted_encapsulated_packet=d)))
720         rxs = self.send_and_expect(self.pg1, [p], self.pg0)
721
722         for rx in rxs:
723             self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
724             self.assertEqual(rx[IPv6].hlim, 19)
725
726         # send a packets that are routed into the tunnel
727         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
728              IPv6(src=self.pg0.remote_ip6, dst="1::3:2") /
729              UDP(sport=555, dport=556) /
730              Raw(b'\x00' * 80))
731
732         rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
733
734         for rx in rxs:
735             rx = IPv6(peer_1.decrypt_transport(rx, True))
736
737             # chech the oringial packet is present
738             self.assertEqual(rx[IPv6].dst, p[IPv6].dst)
739             self.assertEqual(rx[IPv6].hlim, p[IPv6].hlim-1)
740
741         # send packets into the tunnel, expect to receive them on
742         # the other side
743         p = [(peer_1.mk_tunnel_header(self.pg1, True) /
744               Wireguard(message_type=4, reserved_zero=0) /
745               WireguardTransport(
746                   receiver_index=peer_1.sender,
747                   counter=ii+1,
748                   encrypted_encapsulated_packet=peer_1.encrypt_transport(
749                       (IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20) /
750                        UDP(sport=222, dport=223) /
751                        Raw())))) for ii in range(255)]
752
753         rxs = self.send_and_expect(self.pg1, p, self.pg0)
754
755         for rx in rxs:
756             self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
757             self.assertEqual(rx[IPv6].hlim, 19)
758
759         r1.remove_vpp_config()
760         peer_1.remove_vpp_config()
761         wg0.remove_vpp_config()
762
763     def test_wg_peer_v6o4(self):
764         """ Test v6o4"""
765
766         port = 12353
767
768         # Create interfaces
769         wg0 = VppWgInterface(self,
770                              self.pg1.local_ip4,
771                              port).add_vpp_config()
772         wg0.admin_up()
773         wg0.config_ip6()
774
775         peer_1 = VppWgPeer(self,
776                            wg0,
777                            self.pg1.remote_ip4,
778                            port+1,
779                            ["1::3:0/112"]).add_vpp_config(True)
780         self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
781
782         r1 = VppIpRoute(self, "1::3:0", 112,
783                         [VppRoutePath("1::3:1",
784                                       wg0.sw_if_index)]).add_vpp_config()
785
786         # route a packet into the wg interface
787         #  use the allowed-ip prefix
788         #  this is dropped because the peer is not initiated
789         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
790              IPv6(src=self.pg0.remote_ip6, dst="1::3:2") /
791              UDP(sport=555, dport=556) /
792              Raw())
793         self.send_and_assert_no_replies(self.pg0, [p])
794         self.assertEqual(self.base_kp6_err + 1,
795                          self.statistics.get_err_counter(self.kp6_error))
796
797         # send a handsake from the peer with an invalid MAC
798         p = peer_1.mk_handshake(self.pg1)
799         p[WireguardInitiation].mac1 = b'foobar'
800         self.send_and_assert_no_replies(self.pg1, [p])
801
802         self.assertEqual(self.base_mac4_err + 1,
803                          self.statistics.get_err_counter(self.mac4_error))
804
805         # send a handsake from the peer but signed by the wrong key.
806         p = peer_1.mk_handshake(self.pg1,
807                                 False,
808                                 X25519PrivateKey.generate().public_key())
809         self.send_and_assert_no_replies(self.pg1, [p])
810         self.assertEqual(self.base_peer4_err + 1,
811                          self.statistics.get_err_counter(self.peer4_error))
812
813         # send a valid handsake init for which we expect a response
814         p = peer_1.mk_handshake(self.pg1)
815
816         rx = self.send_and_expect(self.pg1, [p], self.pg1)
817
818         peer_1.consume_response(rx[0])
819
820         # route a packet into the wg interface
821         #  this is dropped because the peer is still not initiated
822         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
823              IPv6(src=self.pg0.remote_ip6, dst="1::3:2") /
824              UDP(sport=555, dport=556) /
825              Raw())
826         self.send_and_assert_no_replies(self.pg0, [p])
827         self.assertEqual(self.base_kp6_err + 2,
828                          self.statistics.get_err_counter(self.kp6_error))
829
830         # send a data packet from the peer through the tunnel
831         # this completes the handshake
832         p = (IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20) /
833              UDP(sport=222, dport=223) /
834              Raw())
835         d = peer_1.encrypt_transport(p)
836         p = (peer_1.mk_tunnel_header(self.pg1) /
837              (Wireguard(message_type=4, reserved_zero=0) /
838               WireguardTransport(receiver_index=peer_1.sender,
839                                  counter=0,
840                                  encrypted_encapsulated_packet=d)))
841         rxs = self.send_and_expect(self.pg1, [p], self.pg0)
842
843         for rx in rxs:
844             self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
845             self.assertEqual(rx[IPv6].hlim, 19)
846
847         # send a packets that are routed into the tunnel
848         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
849              IPv6(src=self.pg0.remote_ip6, dst="1::3:2") /
850              UDP(sport=555, dport=556) /
851              Raw(b'\x00' * 80))
852
853         rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
854
855         for rx in rxs:
856             rx = IPv6(peer_1.decrypt_transport(rx))
857
858             # chech the oringial packet is present
859             self.assertEqual(rx[IPv6].dst, p[IPv6].dst)
860             self.assertEqual(rx[IPv6].hlim, p[IPv6].hlim-1)
861
862         # send packets into the tunnel, expect to receive them on
863         # the other side
864         p = [(peer_1.mk_tunnel_header(self.pg1) /
865               Wireguard(message_type=4, reserved_zero=0) /
866               WireguardTransport(
867                   receiver_index=peer_1.sender,
868                   counter=ii+1,
869                   encrypted_encapsulated_packet=peer_1.encrypt_transport(
870                       (IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20) /
871                        UDP(sport=222, dport=223) /
872                        Raw())))) for ii in range(255)]
873
874         rxs = self.send_and_expect(self.pg1, p, self.pg0)
875
876         for rx in rxs:
877             self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
878             self.assertEqual(rx[IPv6].hlim, 19)
879
880         r1.remove_vpp_config()
881         peer_1.remove_vpp_config()
882         wg0.remove_vpp_config()
883
884     def test_wg_peer_v4o6(self):
885         """ Test v4o6"""
886
887         port = 12363
888
889         # Create interfaces
890         wg0 = VppWgInterface(self,
891                              self.pg1.local_ip6,
892                              port).add_vpp_config()
893         wg0.admin_up()
894         wg0.config_ip4()
895
896         peer_1 = VppWgPeer(self,
897                            wg0,
898                            self.pg1.remote_ip6,
899                            port+1,
900                            ["10.11.3.0/24"]).add_vpp_config()
901         self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
902
903         r1 = VppIpRoute(self, "10.11.3.0", 24,
904                         [VppRoutePath("10.11.3.1",
905                                       wg0.sw_if_index)]).add_vpp_config()
906
907         # route a packet into the wg interface
908         #  use the allowed-ip prefix
909         #  this is dropped because the peer is not initiated
910         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
911              IP(src=self.pg0.remote_ip4, dst="10.11.3.2") /
912              UDP(sport=555, dport=556) /
913              Raw())
914         self.send_and_assert_no_replies(self.pg0, [p])
915         self.assertEqual(self.base_kp4_err + 1,
916                          self.statistics.get_err_counter(self.kp4_error))
917
918         # send a handsake from the peer with an invalid MAC
919         p = peer_1.mk_handshake(self.pg1, True)
920         p[WireguardInitiation].mac1 = b'foobar'
921         self.send_and_assert_no_replies(self.pg1, [p])
922         self.assertEqual(self.base_mac6_err + 1,
923                          self.statistics.get_err_counter(self.mac6_error))
924
925         # send a handsake from the peer but signed by the wrong key.
926         p = peer_1.mk_handshake(self.pg1,
927                                 True,
928                                 X25519PrivateKey.generate().public_key())
929         self.send_and_assert_no_replies(self.pg1, [p])
930         self.assertEqual(self.base_peer6_err + 1,
931                          self.statistics.get_err_counter(self.peer6_error))
932
933         # send a valid handsake init for which we expect a response
934         p = peer_1.mk_handshake(self.pg1, True)
935
936         rx = self.send_and_expect(self.pg1, [p], self.pg1)
937
938         peer_1.consume_response(rx[0], True)
939
940         # route a packet into the wg interface
941         #  this is dropped because the peer is still not initiated
942         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
943              IP(src=self.pg0.remote_ip4, dst="10.11.3.2") /
944              UDP(sport=555, dport=556) /
945              Raw())
946         self.send_and_assert_no_replies(self.pg0, [p])
947         self.assertEqual(self.base_kp4_err + 2,
948                          self.statistics.get_err_counter(self.kp4_error))
949
950         # send a data packet from the peer through the tunnel
951         # this completes the handshake
952         p = (IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20) /
953              UDP(sport=222, dport=223) /
954              Raw())
955         d = peer_1.encrypt_transport(p)
956         p = (peer_1.mk_tunnel_header(self.pg1, True) /
957              (Wireguard(message_type=4, reserved_zero=0) /
958               WireguardTransport(receiver_index=peer_1.sender,
959                                  counter=0,
960                                  encrypted_encapsulated_packet=d)))
961         rxs = self.send_and_expect(self.pg1, [p], self.pg0)
962
963         for rx in rxs:
964             self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
965             self.assertEqual(rx[IP].ttl, 19)
966
967         # send a packets that are routed into the tunnel
968         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
969              IP(src=self.pg0.remote_ip4, dst="10.11.3.2") /
970              UDP(sport=555, dport=556) /
971              Raw(b'\x00' * 80))
972
973         rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
974
975         for rx in rxs:
976             rx = IP(peer_1.decrypt_transport(rx, True))
977
978             # chech the oringial packet is present
979             self.assertEqual(rx[IP].dst, p[IP].dst)
980             self.assertEqual(rx[IP].ttl, p[IP].ttl-1)
981
982         # send packets into the tunnel, expect to receive them on
983         # the other side
984         p = [(peer_1.mk_tunnel_header(self.pg1, True) /
985               Wireguard(message_type=4, reserved_zero=0) /
986               WireguardTransport(
987                   receiver_index=peer_1.sender,
988                   counter=ii+1,
989                   encrypted_encapsulated_packet=peer_1.encrypt_transport(
990                       (IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20) /
991                        UDP(sport=222, dport=223) /
992                        Raw())))) for ii in range(255)]
993
994         rxs = self.send_and_expect(self.pg1, p, self.pg0)
995
996         for rx in rxs:
997             self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
998             self.assertEqual(rx[IP].ttl, 19)
999
1000         r1.remove_vpp_config()
1001         peer_1.remove_vpp_config()
1002         wg0.remove_vpp_config()
1003
1004     def test_wg_multi_peer(self):
1005         """ multiple peer setup """
1006         port = 12373
1007
1008         # Create interfaces
1009         wg0 = VppWgInterface(self,
1010                              self.pg1.local_ip4,
1011                              port).add_vpp_config()
1012         wg1 = VppWgInterface(self,
1013                              self.pg2.local_ip4,
1014                              port+1).add_vpp_config()
1015         wg0.admin_up()
1016         wg1.admin_up()
1017
1018         # Check peer counter
1019         self.assertEqual(len(self.vapi.wireguard_peers_dump()), 0)
1020
1021         self.pg_enable_capture(self.pg_interfaces)
1022         self.pg_start()
1023
1024         # Create many peers on sencond interface
1025         NUM_PEERS = 16
1026         self.pg2.generate_remote_hosts(NUM_PEERS)
1027         self.pg2.configure_ipv4_neighbors()
1028         self.pg1.generate_remote_hosts(NUM_PEERS)
1029         self.pg1.configure_ipv4_neighbors()
1030
1031         peers_1 = []
1032         peers_2 = []
1033         routes_1 = []
1034         routes_2 = []
1035         for i in range(NUM_PEERS):
1036             peers_1.append(VppWgPeer(self,
1037                                      wg0,
1038                                      self.pg1.remote_hosts[i].ip4,
1039                                      port+1+i,
1040                                      ["10.0.%d.4/32" % i]).add_vpp_config())
1041             routes_1.append(VppIpRoute(self, "10.0.%d.4" % i, 32,
1042                             [VppRoutePath(self.pg1.remote_hosts[i].ip4,
1043                                           wg0.sw_if_index)]).add_vpp_config())
1044
1045             peers_2.append(VppWgPeer(self,
1046                                      wg1,
1047                                      self.pg2.remote_hosts[i].ip4,
1048                                      port+100+i,
1049                                      ["10.100.%d.4/32" % i]).add_vpp_config())
1050             routes_2.append(VppIpRoute(self, "10.100.%d.4" % i, 32,
1051                             [VppRoutePath(self.pg2.remote_hosts[i].ip4,
1052                                           wg1.sw_if_index)]).add_vpp_config())
1053
1054         self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_PEERS*2)
1055
1056         self.logger.info(self.vapi.cli("show wireguard peer"))
1057         self.logger.info(self.vapi.cli("show wireguard interface"))
1058         self.logger.info(self.vapi.cli("show adj 37"))
1059         self.logger.info(self.vapi.cli("sh ip fib 172.16.3.17"))
1060         self.logger.info(self.vapi.cli("sh ip fib 10.11.3.0"))
1061
1062         # remove routes
1063         for r in routes_1:
1064             r.remove_vpp_config()
1065         for r in routes_2:
1066             r.remove_vpp_config()
1067
1068         # remove peers
1069         for p in peers_1:
1070             self.assertTrue(p.query_vpp_config())
1071             p.remove_vpp_config()
1072         for p in peers_2:
1073             self.assertTrue(p.query_vpp_config())
1074             p.remove_vpp_config()
1075
1076         wg0.remove_vpp_config()
1077         wg1.remove_vpp_config()
1078
1079     def test_wg_multi_interface(self):
1080         """ Multi-tunnel on the same port """
1081         port = 12500
1082
1083         # Create many wireguard interfaces
1084         NUM_IFS = 4
1085         self.pg1.generate_remote_hosts(NUM_IFS)
1086         self.pg1.configure_ipv4_neighbors()
1087         self.pg0.generate_remote_hosts(NUM_IFS)
1088         self.pg0.configure_ipv4_neighbors()
1089
1090         # Create interfaces with a peer on each
1091         peers = []
1092         routes = []
1093         wg_ifs = []
1094         for i in range(NUM_IFS):
1095             # Use the same port for each interface
1096             wg0 = VppWgInterface(self,
1097                                  self.pg1.local_ip4,
1098                                  port).add_vpp_config()
1099             wg0.admin_up()
1100             wg0.config_ip4()
1101             wg_ifs.append(wg0)
1102             peers.append(VppWgPeer(self,
1103                                    wg0,
1104                                    self.pg1.remote_hosts[i].ip4,
1105                                    port+1+i,
1106                                    ["10.0.%d.0/24" % i]).add_vpp_config())
1107
1108             routes.append(VppIpRoute(self, "10.0.%d.0" % i, 24,
1109                           [VppRoutePath("10.0.%d.4" % i,
1110                                         wg0.sw_if_index)]).add_vpp_config())
1111
1112         self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_IFS)
1113
1114         for i in range(NUM_IFS):
1115             # send a valid handsake init for which we expect a response
1116             p = peers[i].mk_handshake(self.pg1)
1117             rx = self.send_and_expect(self.pg1, [p], self.pg1)
1118             peers[i].consume_response(rx[0])
1119
1120             # send a data packet from the peer through the tunnel
1121             # this completes the handshake
1122             p = (IP(src="10.0.%d.4" % i,
1123                     dst=self.pg0.remote_hosts[i].ip4, ttl=20) /
1124                  UDP(sport=222, dport=223) /
1125                  Raw())
1126             d = peers[i].encrypt_transport(p)
1127             p = (peers[i].mk_tunnel_header(self.pg1) /
1128                  (Wireguard(message_type=4, reserved_zero=0) /
1129                   WireguardTransport(receiver_index=peers[i].sender,
1130                                      counter=0,
1131                                      encrypted_encapsulated_packet=d)))
1132             rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1133             for rx in rxs:
1134                 self.assertEqual(rx[IP].dst, self.pg0.remote_hosts[i].ip4)
1135                 self.assertEqual(rx[IP].ttl, 19)
1136
1137         # send a packets that are routed into the tunnel
1138         for i in range(NUM_IFS):
1139             p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
1140                  IP(src=self.pg0.remote_hosts[i].ip4, dst="10.0.%d.4" % i) /
1141                  UDP(sport=555, dport=556) /
1142                  Raw(b'\x00' * 80))
1143
1144             rxs = self.send_and_expect(self.pg0, p * 64, self.pg1)
1145
1146             for rx in rxs:
1147                 rx = IP(peers[i].decrypt_transport(rx))
1148
1149                 # check the oringial packet is present
1150                 self.assertEqual(rx[IP].dst, p[IP].dst)
1151                 self.assertEqual(rx[IP].ttl, p[IP].ttl-1)
1152
1153         # send packets into the tunnel
1154         for i in range(NUM_IFS):
1155             p = [(peers[i].mk_tunnel_header(self.pg1) /
1156                   Wireguard(message_type=4, reserved_zero=0) /
1157                   WireguardTransport(
1158                       receiver_index=peers[i].sender,
1159                       counter=ii+1,
1160                       encrypted_encapsulated_packet=peers[i].encrypt_transport(
1161                           (IP(src="10.0.%d.4" % i,
1162                               dst=self.pg0.remote_hosts[i].ip4, ttl=20) /
1163                            UDP(sport=222, dport=223) /
1164                            Raw())))) for ii in range(64)]
1165
1166             rxs = self.send_and_expect(self.pg1, p, self.pg0)
1167
1168             for rx in rxs:
1169                 self.assertEqual(rx[IP].dst, self.pg0.remote_hosts[i].ip4)
1170                 self.assertEqual(rx[IP].ttl, 19)
1171
1172         for r in routes:
1173             r.remove_vpp_config()
1174         for p in peers:
1175             p.remove_vpp_config()
1176         for i in wg_ifs:
1177             i.remove_vpp_config()
1178
1179
1180 class WireguardHandoffTests(TestWg):
1181     """ Wireguard Tests in multi worker setup """
1182     vpp_worker_count = 2
1183
1184     def test_wg_peer_init(self):
1185         """ Handoff """
1186
1187         port = 12383
1188
1189         # Create interfaces
1190         wg0 = VppWgInterface(self,
1191                              self.pg1.local_ip4,
1192                              port).add_vpp_config()
1193         wg0.admin_up()
1194         wg0.config_ip4()
1195
1196         peer_1 = VppWgPeer(self,
1197                            wg0,
1198                            self.pg1.remote_ip4,
1199                            port+1,
1200                            ["10.11.2.0/24",
1201                             "10.11.3.0/24"]).add_vpp_config()
1202         self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1203
1204         r1 = VppIpRoute(self, "10.11.3.0", 24,
1205                         [VppRoutePath("10.11.3.1",
1206                                       wg0.sw_if_index)]).add_vpp_config()
1207
1208         # send a valid handsake init for which we expect a response
1209         p = peer_1.mk_handshake(self.pg1)
1210
1211         rx = self.send_and_expect(self.pg1, [p], self.pg1)
1212
1213         peer_1.consume_response(rx[0])
1214
1215         # send a data packet from the peer through the tunnel
1216         # this completes the handshake and pins the peer to worker 0
1217         p = (IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20) /
1218              UDP(sport=222, dport=223) /
1219              Raw())
1220         d = peer_1.encrypt_transport(p)
1221         p = (peer_1.mk_tunnel_header(self.pg1) /
1222              (Wireguard(message_type=4, reserved_zero=0) /
1223               WireguardTransport(receiver_index=peer_1.sender,
1224                                  counter=0,
1225                                  encrypted_encapsulated_packet=d)))
1226         rxs = self.send_and_expect(self.pg1, [p], self.pg0,
1227                                    worker=0)
1228
1229         for rx in rxs:
1230             self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1231             self.assertEqual(rx[IP].ttl, 19)
1232
1233         # send a packets that are routed into the tunnel
1234         # and pins the peer tp worker 1
1235         pe = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
1236               IP(src=self.pg0.remote_ip4, dst="10.11.3.2") /
1237               UDP(sport=555, dport=556) /
1238               Raw(b'\x00' * 80))
1239         rxs = self.send_and_expect(self.pg0, pe * 255, self.pg1, worker=1)
1240         peer_1.validate_encapped(rxs, pe)
1241
1242         # send packets into the tunnel, from the other worker
1243         p = [(peer_1.mk_tunnel_header(self.pg1) /
1244              Wireguard(message_type=4, reserved_zero=0) /
1245              WireguardTransport(
1246                  receiver_index=peer_1.sender,
1247                  counter=ii+1,
1248                  encrypted_encapsulated_packet=peer_1.encrypt_transport(
1249                      (IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20) /
1250                       UDP(sport=222, dport=223) /
1251                       Raw())))) for ii in range(255)]
1252
1253         rxs = self.send_and_expect(self.pg1, p, self.pg0, worker=1)
1254
1255         for rx in rxs:
1256             self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1257             self.assertEqual(rx[IP].ttl, 19)
1258
1259         # send a packets that are routed into the tunnel
1260         # from owrker 0
1261         rxs = self.send_and_expect(self.pg0, pe * 255, self.pg1, worker=0)
1262
1263         peer_1.validate_encapped(rxs, pe)
1264
1265         r1.remove_vpp_config()
1266         peer_1.remove_vpp_config()
1267         wg0.remove_vpp_config()
1268
1269     @unittest.skip("test disabled")
1270     def test_wg_multi_interface(self):
1271         """ Multi-tunnel on the same port """