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