wireguard: add peers roaming support
[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     WireguardCookieReply,
20 )
21 from cryptography.hazmat.primitives.asymmetric.x25519 import (
22     X25519PrivateKey,
23     X25519PublicKey,
24 )
25 from cryptography.hazmat.primitives.serialization import (
26     Encoding,
27     PrivateFormat,
28     PublicFormat,
29     NoEncryption,
30 )
31 from cryptography.hazmat.primitives.hashes import BLAKE2s, Hash
32 from cryptography.hazmat.primitives.hmac import HMAC
33 from cryptography.hazmat.backends import default_backend
34 from noise.connection import NoiseConnection, Keypair
35
36 from Crypto.Cipher import ChaCha20_Poly1305
37 from Crypto.Random import get_random_bytes
38
39 from vpp_ipip_tun_interface import VppIpIpTunInterface
40 from vpp_interface import VppInterface
41 from vpp_ip_route import VppIpRoute, VppRoutePath
42 from vpp_object import VppObject
43 from vpp_papi import VppEnum
44 from framework import VppTestCase
45 from re import compile
46 import unittest
47
48 """ TestWg is a subclass of  VPPTestCase classes.
49
50 Wg test.
51
52 """
53
54
55 def private_key_bytes(k):
56     return k.private_bytes(Encoding.Raw, PrivateFormat.Raw, NoEncryption())
57
58
59 def public_key_bytes(k):
60     return k.public_bytes(Encoding.Raw, PublicFormat.Raw)
61
62
63 def get_field_bytes(pkt, name):
64     fld, val = pkt.getfield_and_val(name)
65     return fld.i2m(pkt, val)
66
67
68 class VppWgInterface(VppInterface):
69     """
70     VPP WireGuard interface
71     """
72
73     def __init__(self, test, src, port):
74         super(VppWgInterface, self).__init__(test)
75
76         self.port = port
77         self.src = src
78         self.private_key = X25519PrivateKey.generate()
79         self.public_key = self.private_key.public_key()
80
81         # cookie related params
82         self.cookie_key = blake2s(b"cookie--" + self.public_key_bytes()).digest()
83
84     def public_key_bytes(self):
85         return public_key_bytes(self.public_key)
86
87     def private_key_bytes(self):
88         return private_key_bytes(self.private_key)
89
90     def add_vpp_config(self):
91         r = self.test.vapi.wireguard_interface_create(
92             interface={
93                 "user_instance": 0xFFFFFFFF,
94                 "port": self.port,
95                 "src_ip": self.src,
96                 "private_key": private_key_bytes(self.private_key),
97                 "generate_key": False,
98             }
99         )
100         self.set_sw_if_index(r.sw_if_index)
101         self.test.registry.register(self, self.test.logger)
102         return self
103
104     def remove_vpp_config(self):
105         self.test.vapi.wireguard_interface_delete(sw_if_index=self._sw_if_index)
106
107     def query_vpp_config(self):
108         ts = self.test.vapi.wireguard_interface_dump(sw_if_index=0xFFFFFFFF)
109         for t in ts:
110             if (
111                 t.interface.sw_if_index == self._sw_if_index
112                 and str(t.interface.src_ip) == self.src
113                 and t.interface.port == self.port
114                 and t.interface.private_key == private_key_bytes(self.private_key)
115             ):
116                 return True
117         return False
118
119     def want_events(self, peer_index=0xFFFFFFFF):
120         self.test.vapi.want_wireguard_peer_events(
121             enable_disable=1,
122             pid=os.getpid(),
123             sw_if_index=self._sw_if_index,
124             peer_index=peer_index,
125         )
126
127     def wait_events(self, expect, peers, timeout=5):
128         for i in range(len(peers)):
129             rv = self.test.vapi.wait_for_event(timeout, "wireguard_peer_event")
130             self.test.assertEqual(rv.peer_index, peers[i])
131             self.test.assertEqual(rv.flags, expect)
132
133     def __str__(self):
134         return self.object_id()
135
136     def object_id(self):
137         return "wireguard-%d" % self._sw_if_index
138
139
140 NOISE_HANDSHAKE_NAME = b"Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s"
141 NOISE_IDENTIFIER_NAME = b"WireGuard v1 zx2c4 Jason@zx2c4.com"
142
143 HANDSHAKE_COUNTING_INTERVAL = 0.5
144 UNDER_LOAD_INTERVAL = 1.0
145 HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD = 40
146 HANDSHAKE_NUM_BEFORE_RATELIMITING = 5
147
148
149 class VppWgPeer(VppObject):
150     def __init__(self, test, itf, endpoint, port, allowed_ips, persistent_keepalive=15):
151         self._test = test
152         self.itf = itf
153         self.endpoint = endpoint
154         self.port = port
155         self.allowed_ips = allowed_ips
156         self.persistent_keepalive = persistent_keepalive
157
158         # remote peer's public
159         self.private_key = X25519PrivateKey.generate()
160         self.public_key = self.private_key.public_key()
161
162         # cookie related params
163         self.cookie_key = blake2s(b"cookie--" + self.public_key_bytes()).digest()
164         self.last_sent_cookie = None
165         self.last_mac1 = None
166         self.last_received_cookie = None
167
168         self.noise = NoiseConnection.from_name(NOISE_HANDSHAKE_NAME)
169
170     def change_endpoint(self, endpoint, port):
171         self.endpoint = endpoint
172         self.port = port
173
174     def add_vpp_config(self, is_ip6=False):
175         rv = self._test.vapi.wireguard_peer_add(
176             peer={
177                 "public_key": self.public_key_bytes(),
178                 "port": self.port,
179                 "endpoint": self.endpoint,
180                 "n_allowed_ips": len(self.allowed_ips),
181                 "allowed_ips": self.allowed_ips,
182                 "sw_if_index": self.itf.sw_if_index,
183                 "persistent_keepalive": self.persistent_keepalive,
184             }
185         )
186         self.index = rv.peer_index
187         self.receiver_index = self.index + 1
188         self._test.registry.register(self, self._test.logger)
189         return self
190
191     def remove_vpp_config(self):
192         self._test.vapi.wireguard_peer_remove(peer_index=self.index)
193
194     def object_id(self):
195         return "wireguard-peer-%s" % self.index
196
197     def public_key_bytes(self):
198         return public_key_bytes(self.public_key)
199
200     def query_vpp_config(self):
201         peers = self._test.vapi.wireguard_peers_dump()
202
203         for p in peers:
204             # "::" endpoint will be returned as "0.0.0.0" in peer's details
205             endpoint = "0.0.0.0" if self.endpoint == "::" else self.endpoint
206             if (
207                 p.peer.public_key == self.public_key_bytes()
208                 and p.peer.port == self.port
209                 and str(p.peer.endpoint) == endpoint
210                 and p.peer.sw_if_index == self.itf.sw_if_index
211                 and len(self.allowed_ips) == p.peer.n_allowed_ips
212             ):
213                 self.allowed_ips.sort()
214                 p.peer.allowed_ips.sort()
215
216                 for (a1, a2) in zip(self.allowed_ips, p.peer.allowed_ips):
217                     if str(a1) != str(a2):
218                         return False
219                 return True
220         return False
221
222     def mk_tunnel_header(self, tx_itf, is_ip6=False):
223         if is_ip6 is False:
224             return (
225                 Ether(dst=tx_itf.local_mac, src=tx_itf.remote_mac)
226                 / IP(src=self.endpoint, dst=self.itf.src)
227                 / UDP(sport=self.port, dport=self.itf.port)
228             )
229         else:
230             return (
231                 Ether(dst=tx_itf.local_mac, src=tx_itf.remote_mac)
232                 / IPv6(src=self.endpoint, dst=self.itf.src)
233                 / UDP(sport=self.port, dport=self.itf.port)
234             )
235
236     def noise_reset(self):
237         self.noise = NoiseConnection.from_name(NOISE_HANDSHAKE_NAME)
238
239     def noise_init(self, public_key=None):
240         self.noise.set_prologue(NOISE_IDENTIFIER_NAME)
241         self.noise.set_psks(psk=bytes(bytearray(32)))
242
243         if not public_key:
244             public_key = self.itf.public_key
245
246         # local/this private
247         self.noise.set_keypair_from_private_bytes(
248             Keypair.STATIC, private_key_bytes(self.private_key)
249         )
250         # remote's public
251         self.noise.set_keypair_from_public_bytes(
252             Keypair.REMOTE_STATIC, public_key_bytes(public_key)
253         )
254
255         self.noise.start_handshake()
256
257     def mk_cookie(self, p, tx_itf, is_resp=False, is_ip6=False):
258         self.verify_header(p, is_ip6)
259
260         wg_pkt = Wireguard(p[Raw])
261
262         if is_resp:
263             self._test.assertEqual(wg_pkt[Wireguard].message_type, 2)
264             self._test.assertEqual(wg_pkt[Wireguard].reserved_zero, 0)
265             self._test.assertEqual(wg_pkt[WireguardResponse].mac2, bytes([0] * 16))
266         else:
267             self._test.assertEqual(wg_pkt[Wireguard].message_type, 1)
268             self._test.assertEqual(wg_pkt[Wireguard].reserved_zero, 0)
269             self._test.assertEqual(wg_pkt[WireguardInitiation].mac2, bytes([0] * 16))
270
271         # collect info from wg packet (initiation or response)
272         src = get_field_bytes(p[IPv6 if is_ip6 else IP], "src")
273         sport = p[UDP].sport.to_bytes(2, byteorder="big")
274         if is_resp:
275             mac1 = wg_pkt[WireguardResponse].mac1
276             sender_index = wg_pkt[WireguardResponse].sender_index
277         else:
278             mac1 = wg_pkt[WireguardInitiation].mac1
279             sender_index = wg_pkt[WireguardInitiation].sender_index
280
281         # make cookie reply
282         cookie_reply = Wireguard() / WireguardCookieReply()
283         cookie_reply[Wireguard].message_type = 3
284         cookie_reply[Wireguard].reserved_zero = 0
285         cookie_reply[WireguardCookieReply].receiver_index = sender_index
286         nonce = get_random_bytes(24)
287         cookie_reply[WireguardCookieReply].nonce = nonce
288
289         # generate cookie data
290         changing_secret = get_random_bytes(32)
291         self.last_sent_cookie = blake2s(
292             src + sport, digest_size=16, key=changing_secret
293         ).digest()
294
295         # encrypt cookie data
296         cipher = ChaCha20_Poly1305.new(key=self.cookie_key, nonce=nonce)
297         cipher.update(mac1)
298         ciphertext, tag = cipher.encrypt_and_digest(self.last_sent_cookie)
299         cookie_reply[WireguardCookieReply].encrypted_cookie = ciphertext + tag
300
301         # prepare cookie reply to be sent
302         cookie_reply = self.mk_tunnel_header(tx_itf, is_ip6) / cookie_reply
303
304         return cookie_reply
305
306     def consume_cookie(self, p, is_ip6=False):
307         self.verify_header(p, is_ip6)
308
309         cookie_reply = Wireguard(p[Raw])
310
311         self._test.assertEqual(cookie_reply[Wireguard].message_type, 3)
312         self._test.assertEqual(cookie_reply[Wireguard].reserved_zero, 0)
313         self._test.assertEqual(
314             cookie_reply[WireguardCookieReply].receiver_index, self.receiver_index
315         )
316
317         # collect info from cookie reply
318         nonce = cookie_reply[WireguardCookieReply].nonce
319         encrypted_cookie = cookie_reply[WireguardCookieReply].encrypted_cookie
320         ciphertext, tag = encrypted_cookie[:16], encrypted_cookie[16:]
321
322         # decrypt cookie data
323         cipher = ChaCha20_Poly1305.new(key=self.itf.cookie_key, nonce=nonce)
324         cipher.update(self.last_mac1)
325         self.last_received_cookie = cipher.decrypt_and_verify(ciphertext, tag)
326
327     def mk_handshake(self, tx_itf, is_ip6=False, public_key=None):
328         self.noise.set_as_initiator()
329         self.noise_init(public_key)
330
331         p = Wireguard() / WireguardInitiation()
332
333         p[Wireguard].message_type = 1
334         p[Wireguard].reserved_zero = 0
335         p[WireguardInitiation].sender_index = self.receiver_index
336
337         # some random data for the message
338         #  lifted from the noise protocol's wireguard example
339         now = datetime.datetime.now()
340         tai = struct.pack(
341             "!qi",
342             4611686018427387914 + int(now.timestamp()),
343             int(now.microsecond * 1e3),
344         )
345         b = self.noise.write_message(payload=tai)
346
347         # load noise into init message
348         p[WireguardInitiation].unencrypted_ephemeral = b[0:32]
349         p[WireguardInitiation].encrypted_static = b[32:80]
350         p[WireguardInitiation].encrypted_timestamp = b[80:108]
351
352         # generate the mac1 hash
353         mac_key = blake2s(b"mac1----" + self.itf.public_key_bytes()).digest()
354         mac1 = blake2s(bytes(p)[0:116], digest_size=16, key=mac_key).digest()
355         p[WireguardInitiation].mac1 = mac1
356         self.last_mac1 = mac1
357
358         # generate the mac2 hash
359         if self.last_received_cookie:
360             mac2 = blake2s(
361                 bytes(p)[0:132], digest_size=16, key=self.last_received_cookie
362             ).digest()
363             p[WireguardInitiation].mac2 = mac2
364             self.last_received_cookie = None
365         else:
366             p[WireguardInitiation].mac2 = bytearray(16)
367
368         p = self.mk_tunnel_header(tx_itf, is_ip6) / p
369
370         return p
371
372     def verify_header(self, p, is_ip6=False):
373         if is_ip6 is False:
374             self._test.assertEqual(p[IP].src, self.itf.src)
375             self._test.assertEqual(p[IP].dst, self.endpoint)
376         else:
377             self._test.assertEqual(p[IPv6].src, self.itf.src)
378             self._test.assertEqual(p[IPv6].dst, self.endpoint)
379         self._test.assertEqual(p[UDP].sport, self.itf.port)
380         self._test.assertEqual(p[UDP].dport, self.port)
381         self._test.assert_packet_checksums_valid(p)
382
383     def consume_init(self, p, tx_itf, is_ip6=False, is_mac2=False):
384         self.noise.set_as_responder()
385         self.noise_init(self.itf.public_key)
386         self.verify_header(p, is_ip6)
387
388         init = Wireguard(p[Raw])
389
390         self._test.assertEqual(init[Wireguard].message_type, 1)
391         self._test.assertEqual(init[Wireguard].reserved_zero, 0)
392
393         self.sender = init[WireguardInitiation].sender_index
394
395         # validate the mac1 hash
396         mac_key = blake2s(b"mac1----" + public_key_bytes(self.public_key)).digest()
397         mac1 = blake2s(bytes(init)[0:-32], digest_size=16, key=mac_key).digest()
398         self._test.assertEqual(init[WireguardInitiation].mac1, mac1)
399
400         # validate the mac2 hash
401         if is_mac2:
402             self._test.assertNotEqual(init[WireguardInitiation].mac2, bytes([0] * 16))
403             self._test.assertNotEqual(self.last_sent_cookie, None)
404             mac2 = blake2s(
405                 bytes(init)[0:-16], digest_size=16, key=self.last_sent_cookie
406             ).digest()
407             self._test.assertEqual(init[WireguardInitiation].mac2, mac2)
408             self.last_sent_cookie = None
409         else:
410             self._test.assertEqual(init[WireguardInitiation].mac2, bytes([0] * 16))
411
412         # this passes only unencrypted_ephemeral, encrypted_static,
413         # encrypted_timestamp fields of the init
414         payload = self.noise.read_message(bytes(init)[8:-32])
415
416         # build the response
417         b = self.noise.write_message()
418         mac_key = blake2s(b"mac1----" + public_key_bytes(self.itf.public_key)).digest()
419         resp = Wireguard(message_type=2, reserved_zero=0) / WireguardResponse(
420             sender_index=self.receiver_index,
421             receiver_index=self.sender,
422             unencrypted_ephemeral=b[0:32],
423             encrypted_nothing=b[32:],
424         )
425         mac1 = blake2s(bytes(resp)[:-32], digest_size=16, key=mac_key).digest()
426         resp[WireguardResponse].mac1 = mac1
427         self.last_mac1 = mac1
428
429         resp = self.mk_tunnel_header(tx_itf, is_ip6) / resp
430         self._test.assertTrue(self.noise.handshake_finished)
431
432         return resp
433
434     def consume_response(self, p, is_ip6=False):
435         self.verify_header(p, is_ip6)
436
437         resp = Wireguard(p[Raw])
438
439         self._test.assertEqual(resp[Wireguard].message_type, 2)
440         self._test.assertEqual(resp[Wireguard].reserved_zero, 0)
441         self._test.assertEqual(
442             resp[WireguardResponse].receiver_index, self.receiver_index
443         )
444
445         self.sender = resp[Wireguard].sender_index
446
447         payload = self.noise.read_message(bytes(resp)[12:60])
448         self._test.assertEqual(payload, b"")
449         self._test.assertTrue(self.noise.handshake_finished)
450
451     def decrypt_transport(self, p, is_ip6=False):
452         self.verify_header(p, is_ip6)
453
454         p = Wireguard(p[Raw])
455         self._test.assertEqual(p[Wireguard].message_type, 4)
456         self._test.assertEqual(p[Wireguard].reserved_zero, 0)
457         self._test.assertEqual(
458             p[WireguardTransport].receiver_index, self.receiver_index
459         )
460
461         d = self.noise.decrypt(p[WireguardTransport].encrypted_encapsulated_packet)
462         return d
463
464     def encrypt_transport(self, p):
465         return self.noise.encrypt(bytes(p))
466
467     def validate_encapped(self, rxs, tx, is_ip6=False):
468         for rx in rxs:
469             if is_ip6 is False:
470                 rx = IP(self.decrypt_transport(rx, is_ip6=is_ip6))
471
472                 # check the original packet is present
473                 self._test.assertEqual(rx[IP].dst, tx[IP].dst)
474                 self._test.assertEqual(rx[IP].ttl, tx[IP].ttl - 1)
475             else:
476                 rx = IPv6(self.decrypt_transport(rx, is_ip6=is_ip6))
477
478                 # check the original packet is present
479                 self._test.assertEqual(rx[IPv6].dst, tx[IPv6].dst)
480                 self._test.assertEqual(rx[IPv6].hlim, tx[IPv6].hlim - 1)
481
482     def want_events(self):
483         self._test.vapi.want_wireguard_peer_events(
484             enable_disable=1,
485             pid=os.getpid(),
486             peer_index=self.index,
487             sw_if_index=self.itf.sw_if_index,
488         )
489
490     def wait_event(self, expect, timeout=5):
491         rv = self._test.vapi.wait_for_event(timeout, "wireguard_peer_event")
492         self._test.assertEqual(rv.flags, expect)
493         self._test.assertEqual(rv.peer_index, self.index)
494
495
496 class TestWg(VppTestCase):
497     """Wireguard Test Case"""
498
499     error_str = compile(r"Error")
500
501     wg4_output_node_name = "/err/wg4-output-tun/"
502     wg4_input_node_name = "/err/wg4-input/"
503     wg6_output_node_name = "/err/wg6-output-tun/"
504     wg6_input_node_name = "/err/wg6-input/"
505     kp4_error = wg4_output_node_name + "Keypair error"
506     mac4_error = wg4_input_node_name + "Invalid MAC handshake"
507     peer4_in_err = wg4_input_node_name + "Peer error"
508     peer4_out_err = wg4_output_node_name + "Peer error"
509     kp6_error = wg6_output_node_name + "Keypair error"
510     mac6_error = wg6_input_node_name + "Invalid MAC handshake"
511     peer6_in_err = wg6_input_node_name + "Peer error"
512     peer6_out_err = wg6_output_node_name + "Peer error"
513     cookie_dec4_err = wg4_input_node_name + "Failed during Cookie decryption"
514     cookie_dec6_err = wg6_input_node_name + "Failed during Cookie decryption"
515     ratelimited4_err = wg4_input_node_name + "Handshake ratelimited"
516     ratelimited6_err = wg6_input_node_name + "Handshake ratelimited"
517
518     @classmethod
519     def setUpClass(cls):
520         super(TestWg, cls).setUpClass()
521         try:
522             cls.create_pg_interfaces(range(3))
523             for i in cls.pg_interfaces:
524                 i.admin_up()
525                 i.config_ip4()
526                 i.config_ip6()
527                 i.resolve_arp()
528                 i.resolve_ndp()
529
530         except Exception:
531             super(TestWg, cls).tearDownClass()
532             raise
533
534     @classmethod
535     def tearDownClass(cls):
536         super(TestWg, cls).tearDownClass()
537
538     def setUp(self):
539         super(VppTestCase, self).setUp()
540         self.base_kp4_err = self.statistics.get_err_counter(self.kp4_error)
541         self.base_mac4_err = self.statistics.get_err_counter(self.mac4_error)
542         self.base_peer4_in_err = self.statistics.get_err_counter(self.peer4_in_err)
543         self.base_peer4_out_err = self.statistics.get_err_counter(self.peer4_out_err)
544         self.base_kp6_err = self.statistics.get_err_counter(self.kp6_error)
545         self.base_mac6_err = self.statistics.get_err_counter(self.mac6_error)
546         self.base_peer6_in_err = self.statistics.get_err_counter(self.peer6_in_err)
547         self.base_peer6_out_err = self.statistics.get_err_counter(self.peer6_out_err)
548         self.base_cookie_dec4_err = self.statistics.get_err_counter(
549             self.cookie_dec4_err
550         )
551         self.base_cookie_dec6_err = self.statistics.get_err_counter(
552             self.cookie_dec6_err
553         )
554         self.base_ratelimited4_err = self.statistics.get_err_counter(
555             self.ratelimited4_err
556         )
557         self.base_ratelimited6_err = self.statistics.get_err_counter(
558             self.ratelimited6_err
559         )
560
561     def test_wg_interface(self):
562         """Simple interface creation"""
563         port = 12312
564
565         # Create interface
566         wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
567
568         self.logger.info(self.vapi.cli("sh int"))
569
570         # delete interface
571         wg0.remove_vpp_config()
572
573     def test_handshake_hash(self):
574         """test hashing an init message"""
575         # a init packet generated by linux given the key below
576         h = (
577             "0100000098b9032b"
578             "55cc4b39e73c3d24"
579             "a2a1ab884b524a81"
580             "1808bb86640fb70d"
581             "e93154fec1879125"
582             "ab012624a27f0b75"
583             "c0a2582f438ddb5f"
584             "8e768af40b4ab444"
585             "02f9ff473e1b797e"
586             "80d39d93c5480c82"
587             "a3d4510f70396976"
588             "586fb67300a5167b"
589             "ae6ca3ff3dfd00eb"
590             "59be198810f5aa03"
591             "6abc243d2155ee4f"
592             "2336483900aef801"
593             "08752cd700000000"
594             "0000000000000000"
595             "00000000"
596         )
597
598         b = bytearray.fromhex(h)
599         tgt = Wireguard(b)
600
601         pubb = base64.b64decode("aRuHFTTxICIQNefp05oKWlJv3zgKxb8+WW7JJMh0jyM=")
602         pub = X25519PublicKey.from_public_bytes(pubb)
603
604         self.assertEqual(pubb, public_key_bytes(pub))
605
606         # strip the macs and build a new packet
607         init = b[0:-32]
608         mac_key = blake2s(b"mac1----" + public_key_bytes(pub)).digest()
609         init += blake2s(init, digest_size=16, key=mac_key).digest()
610         init += b"\x00" * 16
611
612         act = Wireguard(init)
613
614         self.assertEqual(tgt, act)
615
616     def _test_wg_send_cookie_tmpl(self, is_resp, is_ip6):
617         port = 12323
618
619         # create wg interface
620         if is_ip6:
621             wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
622             wg0.admin_up()
623             wg0.config_ip6()
624         else:
625             wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
626             wg0.admin_up()
627             wg0.config_ip4()
628
629         self.pg_enable_capture(self.pg_interfaces)
630         self.pg_start()
631
632         # create a peer
633         if is_ip6:
634             peer_1 = VppWgPeer(
635                 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
636             ).add_vpp_config()
637         else:
638             peer_1 = VppWgPeer(
639                 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
640             ).add_vpp_config()
641         self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
642
643         if is_resp:
644             # prepare and send a handshake initiation
645             # expect the peer to send a handshake response
646             init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
647             rxs = self.send_and_expect(self.pg1, [init], self.pg1)
648         else:
649             # wait for the peer to send a handshake initiation
650             rxs = self.pg1.get_capture(1, timeout=2)
651
652         # prepare and send a wrong cookie reply
653         # expect no replies and the cookie error incremented
654         cookie = peer_1.mk_cookie(rxs[0], self.pg1, is_resp=is_resp, is_ip6=is_ip6)
655         cookie.nonce = b"1234567890"
656         self.send_and_assert_no_replies(self.pg1, [cookie], timeout=0.1)
657         if is_ip6:
658             self.assertEqual(
659                 self.base_cookie_dec6_err + 1,
660                 self.statistics.get_err_counter(self.cookie_dec6_err),
661             )
662         else:
663             self.assertEqual(
664                 self.base_cookie_dec4_err + 1,
665                 self.statistics.get_err_counter(self.cookie_dec4_err),
666             )
667
668         # prepare and send a correct cookie reply
669         cookie = peer_1.mk_cookie(rxs[0], self.pg1, is_resp=is_resp, is_ip6=is_ip6)
670         self.pg_send(self.pg1, [cookie])
671
672         # wait for the peer to send a handshake initiation with mac2 set
673         rxs = self.pg1.get_capture(1, timeout=6)
674
675         # verify the initiation and its mac2
676         peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6, is_mac2=True)
677
678         # remove configs
679         peer_1.remove_vpp_config()
680         wg0.remove_vpp_config()
681
682     def test_wg_send_cookie_on_init_v4(self):
683         """Send cookie on handshake initiation (v4)"""
684         self._test_wg_send_cookie_tmpl(is_resp=False, is_ip6=False)
685
686     def test_wg_send_cookie_on_init_v6(self):
687         """Send cookie on handshake initiation (v6)"""
688         self._test_wg_send_cookie_tmpl(is_resp=False, is_ip6=True)
689
690     def test_wg_send_cookie_on_resp_v4(self):
691         """Send cookie on handshake response (v4)"""
692         self._test_wg_send_cookie_tmpl(is_resp=True, is_ip6=False)
693
694     def test_wg_send_cookie_on_resp_v6(self):
695         """Send cookie on handshake response (v6)"""
696         self._test_wg_send_cookie_tmpl(is_resp=True, is_ip6=True)
697
698     def _test_wg_receive_cookie_tmpl(self, is_resp, is_ip6):
699         port = 12323
700
701         # create wg interface
702         if is_ip6:
703             wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
704             wg0.admin_up()
705             wg0.config_ip6()
706         else:
707             wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
708             wg0.admin_up()
709             wg0.config_ip4()
710
711         self.pg_enable_capture(self.pg_interfaces)
712         self.pg_start()
713
714         # create a peer
715         if is_ip6:
716             peer_1 = VppWgPeer(
717                 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
718             ).add_vpp_config()
719         else:
720             peer_1 = VppWgPeer(
721                 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
722             ).add_vpp_config()
723         self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
724
725         if is_resp:
726             # wait for the peer to send a handshake initiation
727             rxs = self.pg1.get_capture(1, timeout=2)
728             # prepare and send a bunch of handshake responses
729             # expect to switch to under load state
730             resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6)
731             txs = [resp] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
732             rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
733             # reset noise to be able to turn into initiator later
734             peer_1.noise_reset()
735         else:
736             # prepare and send a bunch of handshake initiations
737             # expect to switch to under load state
738             init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
739             txs = [init] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
740             rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
741
742         # expect the peer to send a cookie reply
743         peer_1.consume_cookie(rxs[-1], is_ip6=is_ip6)
744
745         # prepare and send a handshake initiation with wrong mac2
746         # expect a cookie reply
747         init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
748         init.mac2 = b"1234567890"
749         rxs = self.send_and_expect(self.pg1, [init], self.pg1)
750         peer_1.consume_cookie(rxs[0], is_ip6=is_ip6)
751
752         # prepare and send a handshake initiation with correct mac2
753         # expect a handshake response
754         init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
755         rxs = self.send_and_expect(self.pg1, [init], self.pg1)
756
757         # verify the response
758         peer_1.consume_response(rxs[0], is_ip6=is_ip6)
759
760         # clear up under load state
761         self.sleep(UNDER_LOAD_INTERVAL)
762
763         # remove configs
764         peer_1.remove_vpp_config()
765         wg0.remove_vpp_config()
766
767     def test_wg_receive_cookie_on_init_v4(self):
768         """Receive cookie on handshake initiation (v4)"""
769         self._test_wg_receive_cookie_tmpl(is_resp=False, is_ip6=False)
770
771     def test_wg_receive_cookie_on_init_v6(self):
772         """Receive cookie on handshake initiation (v6)"""
773         self._test_wg_receive_cookie_tmpl(is_resp=False, is_ip6=True)
774
775     def test_wg_receive_cookie_on_resp_v4(self):
776         """Receive cookie on handshake response (v4)"""
777         self._test_wg_receive_cookie_tmpl(is_resp=True, is_ip6=False)
778
779     def test_wg_receive_cookie_on_resp_v6(self):
780         """Receive cookie on handshake response (v6)"""
781         self._test_wg_receive_cookie_tmpl(is_resp=True, is_ip6=True)
782
783     def test_wg_under_load_interval(self):
784         """Under load interval"""
785         port = 12323
786
787         # create wg interface
788         wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
789         wg0.admin_up()
790         wg0.config_ip4()
791
792         self.pg_enable_capture(self.pg_interfaces)
793         self.pg_start()
794
795         # create a peer
796         peer_1 = VppWgPeer(
797             self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
798         ).add_vpp_config()
799         self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
800
801         # prepare and send a bunch of handshake initiations
802         # expect to switch to under load state
803         init = peer_1.mk_handshake(self.pg1)
804         txs = [init] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
805         rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
806
807         # expect the peer to send a cookie reply
808         peer_1.consume_cookie(rxs[-1])
809
810         # sleep till the next counting interval
811         # expect under load state is still active
812         self.sleep(HANDSHAKE_COUNTING_INTERVAL)
813
814         # prepare and send a handshake initiation with wrong mac2
815         # expect a cookie reply
816         init = peer_1.mk_handshake(self.pg1)
817         init.mac2 = b"1234567890"
818         rxs = self.send_and_expect(self.pg1, [init], self.pg1)
819         peer_1.consume_cookie(rxs[0])
820
821         # sleep till the end of being under load
822         # expect under load state is over
823         self.sleep(UNDER_LOAD_INTERVAL - HANDSHAKE_COUNTING_INTERVAL)
824
825         # prepare and send a handshake initiation with wrong mac2
826         # expect a handshake response
827         init = peer_1.mk_handshake(self.pg1)
828         init.mac2 = b"1234567890"
829         rxs = self.send_and_expect(self.pg1, [init], self.pg1)
830
831         # verify the response
832         peer_1.consume_response(rxs[0])
833
834         # remove configs
835         peer_1.remove_vpp_config()
836         wg0.remove_vpp_config()
837
838     def _test_wg_handshake_ratelimiting_tmpl(self, is_ip6):
839         port = 12323
840
841         # create wg interface
842         if is_ip6:
843             wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
844             wg0.admin_up()
845             wg0.config_ip6()
846         else:
847             wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
848             wg0.admin_up()
849             wg0.config_ip4()
850
851         self.pg_enable_capture(self.pg_interfaces)
852         self.pg_start()
853
854         # create a peer
855         if is_ip6:
856             peer_1 = VppWgPeer(
857                 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
858             ).add_vpp_config()
859         else:
860             peer_1 = VppWgPeer(
861                 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
862             ).add_vpp_config()
863         self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
864
865         # prepare and send a bunch of handshake initiations
866         # expect to switch to under load state
867         init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
868         txs = [init] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
869         rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
870
871         # expect the peer to send a cookie reply
872         peer_1.consume_cookie(rxs[-1], is_ip6=is_ip6)
873
874         # prepare and send a bunch of handshake initiations with correct mac2
875         # expect a handshake response and then ratelimiting
876         NUM_TO_REJECT = 10
877         init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
878         txs = [init] * (HANDSHAKE_NUM_BEFORE_RATELIMITING + NUM_TO_REJECT)
879         rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
880
881         if is_ip6:
882             self.assertEqual(
883                 self.base_ratelimited6_err + NUM_TO_REJECT,
884                 self.statistics.get_err_counter(self.ratelimited6_err),
885             )
886         else:
887             self.assertEqual(
888                 self.base_ratelimited4_err + NUM_TO_REJECT,
889                 self.statistics.get_err_counter(self.ratelimited4_err),
890             )
891
892         # verify the response
893         peer_1.consume_response(rxs[0], is_ip6=is_ip6)
894
895         # clear up under load state
896         self.sleep(UNDER_LOAD_INTERVAL)
897
898         # remove configs
899         peer_1.remove_vpp_config()
900         wg0.remove_vpp_config()
901
902     def test_wg_handshake_ratelimiting_v4(self):
903         """Handshake ratelimiting (v4)"""
904         self._test_wg_handshake_ratelimiting_tmpl(is_ip6=False)
905
906     def test_wg_handshake_ratelimiting_v6(self):
907         """Handshake ratelimiting (v6)"""
908         self._test_wg_handshake_ratelimiting_tmpl(is_ip6=True)
909
910     def test_wg_handshake_ratelimiting_multi_peer(self):
911         """Handshake ratelimiting (multiple peer)"""
912         port = 12323
913
914         # create wg interface
915         wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
916         wg0.admin_up()
917         wg0.config_ip4()
918
919         self.pg_enable_capture(self.pg_interfaces)
920         self.pg_start()
921
922         # create two peers
923         NUM_PEERS = 2
924         self.pg1.generate_remote_hosts(NUM_PEERS)
925         self.pg1.configure_ipv4_neighbors()
926
927         peer_1 = VppWgPeer(
928             self, wg0, self.pg1.remote_hosts[0].ip4, port + 1, ["10.11.3.0/24"]
929         ).add_vpp_config()
930         peer_2 = VppWgPeer(
931             self, wg0, self.pg1.remote_hosts[1].ip4, port + 1, ["10.11.4.0/24"]
932         ).add_vpp_config()
933         self.assertEqual(len(self.vapi.wireguard_peers_dump()), 2)
934
935         # (peer_1) prepare and send a bunch of handshake initiations
936         # expect not to switch to under load state
937         init_1 = peer_1.mk_handshake(self.pg1)
938         txs = [init_1] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
939         rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
940
941         # (peer_1) expect the peer to send a handshake response
942         peer_1.consume_response(rxs[0])
943         peer_1.noise_reset()
944
945         # (peer_1) send another bunch of handshake initiations
946         # expect to switch to under load state
947         rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
948
949         # (peer_1) expect the peer to send a cookie reply
950         peer_1.consume_cookie(rxs[-1])
951
952         # (peer_2) prepare and send a handshake initiation
953         # expect a cookie reply
954         init_2 = peer_2.mk_handshake(self.pg1)
955         rxs = self.send_and_expect(self.pg1, [init_2], self.pg1)
956         peer_2.consume_cookie(rxs[0])
957
958         # (peer_1) prepare and send a bunch of handshake initiations with correct mac2
959         # expect no ratelimiting and a handshake response
960         init_1 = peer_1.mk_handshake(self.pg1)
961         txs = [init_1] * HANDSHAKE_NUM_BEFORE_RATELIMITING
962         rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
963         self.assertEqual(
964             self.base_ratelimited4_err,
965             self.statistics.get_err_counter(self.ratelimited4_err),
966         )
967
968         # (peer_1) verify the response
969         peer_1.consume_response(rxs[0])
970         peer_1.noise_reset()
971
972         # (peer_1) send another two handshake initiations with correct mac2
973         # expect ratelimiting
974         # (peer_2) prepare and send a handshake initiation with correct mac2
975         # expect no ratelimiting and a handshake response
976         init_2 = peer_2.mk_handshake(self.pg1)
977         txs = [init_1, init_2, init_1]
978         rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
979
980         # (peer_1) verify ratelimiting
981         self.assertEqual(
982             self.base_ratelimited4_err + 2,
983             self.statistics.get_err_counter(self.ratelimited4_err),
984         )
985
986         # (peer_2) verify the response
987         peer_2.consume_response(rxs[0])
988
989         # clear up under load state
990         self.sleep(UNDER_LOAD_INTERVAL)
991
992         # remove configs
993         peer_1.remove_vpp_config()
994         peer_2.remove_vpp_config()
995         wg0.remove_vpp_config()
996
997     def _test_wg_peer_roaming_on_handshake_tmpl(self, is_endpoint_set, is_resp, is_ip6):
998         port = 12323
999
1000         # create wg interface
1001         if is_ip6:
1002             wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1003             wg0.admin_up()
1004             wg0.config_ip6()
1005         else:
1006             wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1007             wg0.admin_up()
1008             wg0.config_ip4()
1009
1010         self.pg_enable_capture(self.pg_interfaces)
1011         self.pg_start()
1012
1013         # create more remote hosts
1014         NUM_REMOTE_HOSTS = 2
1015         self.pg1.generate_remote_hosts(NUM_REMOTE_HOSTS)
1016         if is_ip6:
1017             self.pg1.configure_ipv6_neighbors()
1018         else:
1019             self.pg1.configure_ipv4_neighbors()
1020
1021         # create a peer
1022         if is_ip6:
1023             peer_1 = VppWgPeer(
1024                 test=self,
1025                 itf=wg0,
1026                 endpoint=self.pg1.remote_hosts[0].ip6 if is_endpoint_set else "::",
1027                 port=port + 1 if is_endpoint_set else 0,
1028                 allowed_ips=["1::3:0/112"],
1029             ).add_vpp_config()
1030         else:
1031             peer_1 = VppWgPeer(
1032                 test=self,
1033                 itf=wg0,
1034                 endpoint=self.pg1.remote_hosts[0].ip4 if is_endpoint_set else "0.0.0.0",
1035                 port=port + 1 if is_endpoint_set else 0,
1036                 allowed_ips=["10.11.3.0/24"],
1037             ).add_vpp_config()
1038         self.assertTrue(peer_1.query_vpp_config())
1039
1040         if is_resp:
1041             # wait for the peer to send a handshake initiation
1042             rxs = self.pg1.get_capture(1, timeout=2)
1043             # prepare a handshake response
1044             resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6)
1045             # change endpoint
1046             if is_ip6:
1047                 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip6, port + 100)
1048                 resp[IPv6].src, resp[UDP].sport = peer_1.endpoint, peer_1.port
1049             else:
1050                 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip4, port + 100)
1051                 resp[IP].src, resp[UDP].sport = peer_1.endpoint, peer_1.port
1052             # send the handshake response
1053             # expect a keepalive message sent to the new endpoint
1054             rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1055             # verify the keepalive message
1056             b = peer_1.decrypt_transport(rxs[0], is_ip6=is_ip6)
1057             self.assertEqual(0, len(b))
1058         else:
1059             # change endpoint
1060             if is_ip6:
1061                 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip6, port + 100)
1062             else:
1063                 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip4, port + 100)
1064             # prepare and send a handshake initiation
1065             # expect a handshake response sent to the new endpoint
1066             init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
1067             rxs = self.send_and_expect(self.pg1, [init], self.pg1)
1068             # verify the response
1069             peer_1.consume_response(rxs[0], is_ip6=is_ip6)
1070         self.assertTrue(peer_1.query_vpp_config())
1071
1072         # remove configs
1073         peer_1.remove_vpp_config()
1074         wg0.remove_vpp_config()
1075
1076     def test_wg_peer_roaming_on_init_v4(self):
1077         """Peer roaming on handshake initiation (v4)"""
1078         self._test_wg_peer_roaming_on_handshake_tmpl(
1079             is_endpoint_set=False, is_resp=False, is_ip6=False
1080         )
1081
1082     def test_wg_peer_roaming_on_init_v6(self):
1083         """Peer roaming on handshake initiation (v6)"""
1084         self._test_wg_peer_roaming_on_handshake_tmpl(
1085             is_endpoint_set=False, is_resp=False, is_ip6=True
1086         )
1087
1088     def test_wg_peer_roaming_on_resp_v4(self):
1089         """Peer roaming on handshake response (v4)"""
1090         self._test_wg_peer_roaming_on_handshake_tmpl(
1091             is_endpoint_set=True, is_resp=True, is_ip6=False
1092         )
1093
1094     def test_wg_peer_roaming_on_resp_v6(self):
1095         """Peer roaming on handshake response (v6)"""
1096         self._test_wg_peer_roaming_on_handshake_tmpl(
1097             is_endpoint_set=True, is_resp=True, is_ip6=True
1098         )
1099
1100     def _test_wg_peer_roaming_on_data_tmpl(self, is_async, is_ip6):
1101         self.vapi.wg_set_async_mode(is_async)
1102         port = 12323
1103
1104         # create wg interface
1105         if is_ip6:
1106             wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1107             wg0.admin_up()
1108             wg0.config_ip6()
1109         else:
1110             wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1111             wg0.admin_up()
1112             wg0.config_ip4()
1113
1114         self.pg_enable_capture(self.pg_interfaces)
1115         self.pg_start()
1116
1117         # create more remote hosts
1118         NUM_REMOTE_HOSTS = 2
1119         self.pg1.generate_remote_hosts(NUM_REMOTE_HOSTS)
1120         if is_ip6:
1121             self.pg1.configure_ipv6_neighbors()
1122         else:
1123             self.pg1.configure_ipv4_neighbors()
1124
1125         # create a peer
1126         if is_ip6:
1127             peer_1 = VppWgPeer(
1128                 self, wg0, self.pg1.remote_hosts[0].ip6, port + 1, ["1::3:0/112"]
1129             ).add_vpp_config()
1130         else:
1131             peer_1 = VppWgPeer(
1132                 self, wg0, self.pg1.remote_hosts[0].ip4, port + 1, ["10.11.3.0/24"]
1133             ).add_vpp_config()
1134         self.assertTrue(peer_1.query_vpp_config())
1135
1136         # create a route to rewrite traffic into the wg interface
1137         if is_ip6:
1138             r1 = VppIpRoute(
1139                 self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
1140             ).add_vpp_config()
1141         else:
1142             r1 = VppIpRoute(
1143                 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1144             ).add_vpp_config()
1145
1146         # wait for the peer to send a handshake initiation
1147         rxs = self.pg1.get_capture(1, timeout=2)
1148
1149         # prepare and send a handshake response
1150         # expect a keepalive message
1151         resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6)
1152         rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1153
1154         # verify the keepalive message
1155         b = peer_1.decrypt_transport(rxs[0], is_ip6=is_ip6)
1156         self.assertEqual(0, len(b))
1157
1158         # change endpoint
1159         if is_ip6:
1160             peer_1.change_endpoint(self.pg1.remote_hosts[1].ip6, port + 100)
1161         else:
1162             peer_1.change_endpoint(self.pg1.remote_hosts[1].ip4, port + 100)
1163
1164         # prepare and send a data packet
1165         # expect endpoint change
1166         if is_ip6:
1167             ip_header = IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1168         else:
1169             ip_header = IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1170         data = (
1171             peer_1.mk_tunnel_header(self.pg1, is_ip6=is_ip6)
1172             / Wireguard(message_type=4, reserved_zero=0)
1173             / WireguardTransport(
1174                 receiver_index=peer_1.sender,
1175                 counter=0,
1176                 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1177                     ip_header / UDP(sport=222, dport=223) / Raw()
1178                 ),
1179             )
1180         )
1181         rxs = self.send_and_expect(self.pg1, [data], self.pg0)
1182         if is_ip6:
1183             self.assertEqual(rxs[0][IPv6].dst, self.pg0.remote_ip6)
1184             self.assertEqual(rxs[0][IPv6].hlim, 19)
1185         else:
1186             self.assertEqual(rxs[0][IP].dst, self.pg0.remote_ip4)
1187             self.assertEqual(rxs[0][IP].ttl, 19)
1188         self.assertTrue(peer_1.query_vpp_config())
1189
1190         # prepare and send a packet that will be rewritten into the wg interface
1191         # expect a data packet sent to the new endpoint
1192         if is_ip6:
1193             ip_header = IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1194         else:
1195             ip_header = IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1196         p = (
1197             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1198             / ip_header
1199             / UDP(sport=555, dport=556)
1200             / Raw()
1201         )
1202         rxs = self.send_and_expect(self.pg0, [p], self.pg1)
1203
1204         # verify the data packet
1205         peer_1.validate_encapped(rxs, p, is_ip6=is_ip6)
1206
1207         # remove configs
1208         r1.remove_vpp_config()
1209         peer_1.remove_vpp_config()
1210         wg0.remove_vpp_config()
1211
1212     def test_wg_peer_roaming_on_data_v4_sync(self):
1213         """Peer roaming on data packet (v4, sync)"""
1214         self._test_wg_peer_roaming_on_data_tmpl(is_async=False, is_ip6=False)
1215
1216     def test_wg_peer_roaming_on_data_v6_sync(self):
1217         """Peer roaming on data packet (v6, sync)"""
1218         self._test_wg_peer_roaming_on_data_tmpl(is_async=False, is_ip6=True)
1219
1220     def test_wg_peer_roaming_on_data_v4_async(self):
1221         """Peer roaming on data packet (v4, async)"""
1222         self._test_wg_peer_roaming_on_data_tmpl(is_async=True, is_ip6=False)
1223
1224     def test_wg_peer_roaming_on_data_v6_async(self):
1225         """Peer roaming on data packet (v6, async)"""
1226         self._test_wg_peer_roaming_on_data_tmpl(is_async=True, is_ip6=True)
1227
1228     def test_wg_peer_resp(self):
1229         """Send handshake response"""
1230         port = 12323
1231
1232         # Create interfaces
1233         wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1234         wg0.admin_up()
1235         wg0.config_ip4()
1236
1237         self.pg_enable_capture(self.pg_interfaces)
1238         self.pg_start()
1239
1240         peer_1 = VppWgPeer(
1241             self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
1242         ).add_vpp_config()
1243         self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1244
1245         r1 = VppIpRoute(
1246             self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1247         ).add_vpp_config()
1248
1249         # wait for the peer to send a handshake
1250         rx = self.pg1.get_capture(1, timeout=2)
1251
1252         # consume the handshake in the noise protocol and
1253         # generate the response
1254         resp = peer_1.consume_init(rx[0], self.pg1)
1255
1256         # send the response, get keepalive
1257         rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1258
1259         for rx in rxs:
1260             b = peer_1.decrypt_transport(rx)
1261             self.assertEqual(0, len(b))
1262
1263         # send a packets that are routed into the tunnel
1264         p = (
1265             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1266             / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1267             / UDP(sport=555, dport=556)
1268             / Raw(b"\x00" * 80)
1269         )
1270
1271         rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1272
1273         peer_1.validate_encapped(rxs, p)
1274
1275         # send packets into the tunnel, expect to receive them on
1276         # the other side
1277         p = [
1278             (
1279                 peer_1.mk_tunnel_header(self.pg1)
1280                 / Wireguard(message_type=4, reserved_zero=0)
1281                 / WireguardTransport(
1282                     receiver_index=peer_1.sender,
1283                     counter=ii,
1284                     encrypted_encapsulated_packet=peer_1.encrypt_transport(
1285                         (
1286                             IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1287                             / UDP(sport=222, dport=223)
1288                             / Raw()
1289                         )
1290                     ),
1291                 )
1292             )
1293             for ii in range(255)
1294         ]
1295
1296         rxs = self.send_and_expect(self.pg1, p, self.pg0)
1297
1298         for rx in rxs:
1299             self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1300             self.assertEqual(rx[IP].ttl, 19)
1301
1302         r1.remove_vpp_config()
1303         peer_1.remove_vpp_config()
1304         wg0.remove_vpp_config()
1305
1306     def test_wg_peer_v4o4(self):
1307         """Test v4o4"""
1308
1309         port = 12333
1310
1311         # Create interfaces
1312         wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1313         wg0.admin_up()
1314         wg0.config_ip4()
1315
1316         peer_1 = VppWgPeer(
1317             self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
1318         ).add_vpp_config()
1319         self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1320
1321         r1 = VppIpRoute(
1322             self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1323         ).add_vpp_config()
1324         r2 = VppIpRoute(
1325             self, "20.22.3.0", 24, [VppRoutePath("20.22.3.1", wg0.sw_if_index)]
1326         ).add_vpp_config()
1327
1328         # route a packet into the wg interface
1329         #  use the allowed-ip prefix
1330         #  this is dropped because the peer is not initiated
1331         p = (
1332             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1333             / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1334             / UDP(sport=555, dport=556)
1335             / Raw()
1336         )
1337         self.send_and_assert_no_replies(self.pg0, [p])
1338         self.assertEqual(
1339             self.base_kp4_err + 1, self.statistics.get_err_counter(self.kp4_error)
1340         )
1341
1342         # route a packet into the wg interface
1343         #  use a not allowed-ip prefix
1344         #  this is dropped because there is no matching peer
1345         p = (
1346             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1347             / IP(src=self.pg0.remote_ip4, dst="20.22.3.2")
1348             / UDP(sport=555, dport=556)
1349             / Raw()
1350         )
1351         self.send_and_assert_no_replies(self.pg0, [p])
1352         self.assertEqual(
1353             self.base_peer4_out_err + 1,
1354             self.statistics.get_err_counter(self.peer4_out_err),
1355         )
1356
1357         # send a handsake from the peer with an invalid MAC
1358         p = peer_1.mk_handshake(self.pg1)
1359         p[WireguardInitiation].mac1 = b"foobar"
1360         self.send_and_assert_no_replies(self.pg1, [p])
1361         self.assertEqual(
1362             self.base_mac4_err + 1, self.statistics.get_err_counter(self.mac4_error)
1363         )
1364
1365         # send a handsake from the peer but signed by the wrong key.
1366         p = peer_1.mk_handshake(
1367             self.pg1, False, X25519PrivateKey.generate().public_key()
1368         )
1369         self.send_and_assert_no_replies(self.pg1, [p])
1370         self.assertEqual(
1371             self.base_peer4_in_err + 1,
1372             self.statistics.get_err_counter(self.peer4_in_err),
1373         )
1374
1375         # send a valid handsake init for which we expect a response
1376         p = peer_1.mk_handshake(self.pg1)
1377
1378         rx = self.send_and_expect(self.pg1, [p], self.pg1)
1379
1380         peer_1.consume_response(rx[0])
1381
1382         # route a packet into the wg interface
1383         #  this is dropped because the peer is still not initiated
1384         p = (
1385             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1386             / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1387             / UDP(sport=555, dport=556)
1388             / Raw()
1389         )
1390         self.send_and_assert_no_replies(self.pg0, [p])
1391         self.assertEqual(
1392             self.base_kp4_err + 2, self.statistics.get_err_counter(self.kp4_error)
1393         )
1394
1395         # send a data packet from the peer through the tunnel
1396         # this completes the handshake
1397         p = (
1398             IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1399             / UDP(sport=222, dport=223)
1400             / Raw()
1401         )
1402         d = peer_1.encrypt_transport(p)
1403         p = peer_1.mk_tunnel_header(self.pg1) / (
1404             Wireguard(message_type=4, reserved_zero=0)
1405             / WireguardTransport(
1406                 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1407             )
1408         )
1409         rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1410
1411         for rx in rxs:
1412             self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1413             self.assertEqual(rx[IP].ttl, 19)
1414
1415         # send a packets that are routed into the tunnel
1416         p = (
1417             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1418             / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1419             / UDP(sport=555, dport=556)
1420             / Raw(b"\x00" * 80)
1421         )
1422
1423         rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1424
1425         for rx in rxs:
1426             rx = IP(peer_1.decrypt_transport(rx))
1427
1428             # check the original packet is present
1429             self.assertEqual(rx[IP].dst, p[IP].dst)
1430             self.assertEqual(rx[IP].ttl, p[IP].ttl - 1)
1431
1432         # send packets into the tunnel, expect to receive them on
1433         # the other side
1434         p = [
1435             (
1436                 peer_1.mk_tunnel_header(self.pg1)
1437                 / Wireguard(message_type=4, reserved_zero=0)
1438                 / WireguardTransport(
1439                     receiver_index=peer_1.sender,
1440                     counter=ii + 1,
1441                     encrypted_encapsulated_packet=peer_1.encrypt_transport(
1442                         (
1443                             IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1444                             / UDP(sport=222, dport=223)
1445                             / Raw()
1446                         )
1447                     ),
1448                 )
1449             )
1450             for ii in range(255)
1451         ]
1452
1453         rxs = self.send_and_expect(self.pg1, p, self.pg0)
1454
1455         for rx in rxs:
1456             self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1457             self.assertEqual(rx[IP].ttl, 19)
1458
1459         r1.remove_vpp_config()
1460         r2.remove_vpp_config()
1461         peer_1.remove_vpp_config()
1462         wg0.remove_vpp_config()
1463
1464     def test_wg_peer_v6o6(self):
1465         """Test v6o6"""
1466
1467         port = 12343
1468
1469         # Create interfaces
1470         wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1471         wg0.admin_up()
1472         wg0.config_ip6()
1473
1474         peer_1 = VppWgPeer(
1475             self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
1476         ).add_vpp_config(True)
1477         self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1478
1479         r1 = VppIpRoute(
1480             self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
1481         ).add_vpp_config()
1482         r2 = VppIpRoute(
1483             self, "22::3:0", 112, [VppRoutePath("22::3:1", wg0.sw_if_index)]
1484         ).add_vpp_config()
1485
1486         # route a packet into the wg interface
1487         #  use the allowed-ip prefix
1488         #  this is dropped because the peer is not initiated
1489
1490         p = (
1491             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1492             / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1493             / UDP(sport=555, dport=556)
1494             / Raw()
1495         )
1496         self.send_and_assert_no_replies(self.pg0, [p])
1497
1498         self.assertEqual(
1499             self.base_kp6_err + 1, self.statistics.get_err_counter(self.kp6_error)
1500         )
1501
1502         # route a packet into the wg interface
1503         #  use a not allowed-ip prefix
1504         #  this is dropped because there is no matching peer
1505         p = (
1506             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1507             / IPv6(src=self.pg0.remote_ip6, dst="22::3:2")
1508             / UDP(sport=555, dport=556)
1509             / Raw()
1510         )
1511         self.send_and_assert_no_replies(self.pg0, [p])
1512         self.assertEqual(
1513             self.base_peer6_out_err + 1,
1514             self.statistics.get_err_counter(self.peer6_out_err),
1515         )
1516
1517         # send a handsake from the peer with an invalid MAC
1518         p = peer_1.mk_handshake(self.pg1, True)
1519         p[WireguardInitiation].mac1 = b"foobar"
1520         self.send_and_assert_no_replies(self.pg1, [p])
1521
1522         self.assertEqual(
1523             self.base_mac6_err + 1, self.statistics.get_err_counter(self.mac6_error)
1524         )
1525
1526         # send a handsake from the peer but signed by the wrong key.
1527         p = peer_1.mk_handshake(
1528             self.pg1, True, X25519PrivateKey.generate().public_key()
1529         )
1530         self.send_and_assert_no_replies(self.pg1, [p])
1531         self.assertEqual(
1532             self.base_peer6_in_err + 1,
1533             self.statistics.get_err_counter(self.peer6_in_err),
1534         )
1535
1536         # send a valid handsake init for which we expect a response
1537         p = peer_1.mk_handshake(self.pg1, True)
1538
1539         rx = self.send_and_expect(self.pg1, [p], self.pg1)
1540
1541         peer_1.consume_response(rx[0], True)
1542
1543         # route a packet into the wg interface
1544         #  this is dropped because the peer is still not initiated
1545         p = (
1546             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1547             / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1548             / UDP(sport=555, dport=556)
1549             / Raw()
1550         )
1551         self.send_and_assert_no_replies(self.pg0, [p])
1552         self.assertEqual(
1553             self.base_kp6_err + 2, self.statistics.get_err_counter(self.kp6_error)
1554         )
1555
1556         # send a data packet from the peer through the tunnel
1557         # this completes the handshake
1558         p = (
1559             IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1560             / UDP(sport=222, dport=223)
1561             / Raw()
1562         )
1563         d = peer_1.encrypt_transport(p)
1564         p = peer_1.mk_tunnel_header(self.pg1, True) / (
1565             Wireguard(message_type=4, reserved_zero=0)
1566             / WireguardTransport(
1567                 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1568             )
1569         )
1570         rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1571
1572         for rx in rxs:
1573             self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1574             self.assertEqual(rx[IPv6].hlim, 19)
1575
1576         # send a packets that are routed into the tunnel
1577         p = (
1578             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1579             / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1580             / UDP(sport=555, dport=556)
1581             / Raw(b"\x00" * 80)
1582         )
1583
1584         rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1585
1586         for rx in rxs:
1587             rx = IPv6(peer_1.decrypt_transport(rx, True))
1588
1589             # check the original packet is present
1590             self.assertEqual(rx[IPv6].dst, p[IPv6].dst)
1591             self.assertEqual(rx[IPv6].hlim, p[IPv6].hlim - 1)
1592
1593         # send packets into the tunnel, expect to receive them on
1594         # the other side
1595         p = [
1596             (
1597                 peer_1.mk_tunnel_header(self.pg1, True)
1598                 / Wireguard(message_type=4, reserved_zero=0)
1599                 / WireguardTransport(
1600                     receiver_index=peer_1.sender,
1601                     counter=ii + 1,
1602                     encrypted_encapsulated_packet=peer_1.encrypt_transport(
1603                         (
1604                             IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1605                             / UDP(sport=222, dport=223)
1606                             / Raw()
1607                         )
1608                     ),
1609                 )
1610             )
1611             for ii in range(255)
1612         ]
1613
1614         rxs = self.send_and_expect(self.pg1, p, self.pg0)
1615
1616         for rx in rxs:
1617             self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1618             self.assertEqual(rx[IPv6].hlim, 19)
1619
1620         r1.remove_vpp_config()
1621         r2.remove_vpp_config()
1622         peer_1.remove_vpp_config()
1623         wg0.remove_vpp_config()
1624
1625     def test_wg_peer_v6o4(self):
1626         """Test v6o4"""
1627
1628         port = 12353
1629
1630         # Create interfaces
1631         wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1632         wg0.admin_up()
1633         wg0.config_ip6()
1634
1635         peer_1 = VppWgPeer(
1636             self, wg0, self.pg1.remote_ip4, port + 1, ["1::3:0/112"]
1637         ).add_vpp_config(True)
1638         self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1639
1640         r1 = VppIpRoute(
1641             self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
1642         ).add_vpp_config()
1643
1644         # route a packet into the wg interface
1645         #  use the allowed-ip prefix
1646         #  this is dropped because the peer is not initiated
1647         p = (
1648             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1649             / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1650             / UDP(sport=555, dport=556)
1651             / Raw()
1652         )
1653         self.send_and_assert_no_replies(self.pg0, [p])
1654         self.assertEqual(
1655             self.base_kp6_err + 1, self.statistics.get_err_counter(self.kp6_error)
1656         )
1657
1658         # send a handsake from the peer with an invalid MAC
1659         p = peer_1.mk_handshake(self.pg1)
1660         p[WireguardInitiation].mac1 = b"foobar"
1661         self.send_and_assert_no_replies(self.pg1, [p])
1662
1663         self.assertEqual(
1664             self.base_mac4_err + 1, self.statistics.get_err_counter(self.mac4_error)
1665         )
1666
1667         # send a handsake from the peer but signed by the wrong key.
1668         p = peer_1.mk_handshake(
1669             self.pg1, False, X25519PrivateKey.generate().public_key()
1670         )
1671         self.send_and_assert_no_replies(self.pg1, [p])
1672         self.assertEqual(
1673             self.base_peer4_in_err + 1,
1674             self.statistics.get_err_counter(self.peer4_in_err),
1675         )
1676
1677         # send a valid handsake init for which we expect a response
1678         p = peer_1.mk_handshake(self.pg1)
1679
1680         rx = self.send_and_expect(self.pg1, [p], self.pg1)
1681
1682         peer_1.consume_response(rx[0])
1683
1684         # route a packet into the wg interface
1685         #  this is dropped because the peer is still not initiated
1686         p = (
1687             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1688             / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1689             / UDP(sport=555, dport=556)
1690             / Raw()
1691         )
1692         self.send_and_assert_no_replies(self.pg0, [p])
1693         self.assertEqual(
1694             self.base_kp6_err + 2, self.statistics.get_err_counter(self.kp6_error)
1695         )
1696
1697         # send a data packet from the peer through the tunnel
1698         # this completes the handshake
1699         p = (
1700             IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1701             / UDP(sport=222, dport=223)
1702             / Raw()
1703         )
1704         d = peer_1.encrypt_transport(p)
1705         p = peer_1.mk_tunnel_header(self.pg1) / (
1706             Wireguard(message_type=4, reserved_zero=0)
1707             / WireguardTransport(
1708                 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1709             )
1710         )
1711         rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1712
1713         for rx in rxs:
1714             self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1715             self.assertEqual(rx[IPv6].hlim, 19)
1716
1717         # send a packets that are routed into the tunnel
1718         p = (
1719             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1720             / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1721             / UDP(sport=555, dport=556)
1722             / Raw(b"\x00" * 80)
1723         )
1724
1725         rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1726
1727         for rx in rxs:
1728             rx = IPv6(peer_1.decrypt_transport(rx))
1729
1730             # check the original packet is present
1731             self.assertEqual(rx[IPv6].dst, p[IPv6].dst)
1732             self.assertEqual(rx[IPv6].hlim, p[IPv6].hlim - 1)
1733
1734         # send packets into the tunnel, expect to receive them on
1735         # the other side
1736         p = [
1737             (
1738                 peer_1.mk_tunnel_header(self.pg1)
1739                 / Wireguard(message_type=4, reserved_zero=0)
1740                 / WireguardTransport(
1741                     receiver_index=peer_1.sender,
1742                     counter=ii + 1,
1743                     encrypted_encapsulated_packet=peer_1.encrypt_transport(
1744                         (
1745                             IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1746                             / UDP(sport=222, dport=223)
1747                             / Raw()
1748                         )
1749                     ),
1750                 )
1751             )
1752             for ii in range(255)
1753         ]
1754
1755         rxs = self.send_and_expect(self.pg1, p, self.pg0)
1756
1757         for rx in rxs:
1758             self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1759             self.assertEqual(rx[IPv6].hlim, 19)
1760
1761         r1.remove_vpp_config()
1762         peer_1.remove_vpp_config()
1763         wg0.remove_vpp_config()
1764
1765     def test_wg_peer_v4o6(self):
1766         """Test v4o6"""
1767
1768         port = 12363
1769
1770         # Create interfaces
1771         wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1772         wg0.admin_up()
1773         wg0.config_ip4()
1774
1775         peer_1 = VppWgPeer(
1776             self, wg0, self.pg1.remote_ip6, port + 1, ["10.11.3.0/24"]
1777         ).add_vpp_config()
1778         self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1779
1780         r1 = VppIpRoute(
1781             self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1782         ).add_vpp_config()
1783
1784         # route a packet into the wg interface
1785         #  use the allowed-ip prefix
1786         #  this is dropped because the peer is not initiated
1787         p = (
1788             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1789             / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1790             / UDP(sport=555, dport=556)
1791             / Raw()
1792         )
1793         self.send_and_assert_no_replies(self.pg0, [p])
1794         self.assertEqual(
1795             self.base_kp4_err + 1, self.statistics.get_err_counter(self.kp4_error)
1796         )
1797
1798         # send a handsake from the peer with an invalid MAC
1799         p = peer_1.mk_handshake(self.pg1, True)
1800         p[WireguardInitiation].mac1 = b"foobar"
1801         self.send_and_assert_no_replies(self.pg1, [p])
1802         self.assertEqual(
1803             self.base_mac6_err + 1, self.statistics.get_err_counter(self.mac6_error)
1804         )
1805
1806         # send a handsake from the peer but signed by the wrong key.
1807         p = peer_1.mk_handshake(
1808             self.pg1, True, X25519PrivateKey.generate().public_key()
1809         )
1810         self.send_and_assert_no_replies(self.pg1, [p])
1811         self.assertEqual(
1812             self.base_peer6_in_err + 1,
1813             self.statistics.get_err_counter(self.peer6_in_err),
1814         )
1815
1816         # send a valid handsake init for which we expect a response
1817         p = peer_1.mk_handshake(self.pg1, True)
1818
1819         rx = self.send_and_expect(self.pg1, [p], self.pg1)
1820
1821         peer_1.consume_response(rx[0], True)
1822
1823         # route a packet into the wg interface
1824         #  this is dropped because the peer is still not initiated
1825         p = (
1826             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1827             / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1828             / UDP(sport=555, dport=556)
1829             / Raw()
1830         )
1831         self.send_and_assert_no_replies(self.pg0, [p])
1832         self.assertEqual(
1833             self.base_kp4_err + 2, self.statistics.get_err_counter(self.kp4_error)
1834         )
1835
1836         # send a data packet from the peer through the tunnel
1837         # this completes the handshake
1838         p = (
1839             IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1840             / UDP(sport=222, dport=223)
1841             / Raw()
1842         )
1843         d = peer_1.encrypt_transport(p)
1844         p = peer_1.mk_tunnel_header(self.pg1, True) / (
1845             Wireguard(message_type=4, reserved_zero=0)
1846             / WireguardTransport(
1847                 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1848             )
1849         )
1850         rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1851
1852         for rx in rxs:
1853             self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1854             self.assertEqual(rx[IP].ttl, 19)
1855
1856         # send a packets that are routed into the tunnel
1857         p = (
1858             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1859             / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1860             / UDP(sport=555, dport=556)
1861             / Raw(b"\x00" * 80)
1862         )
1863
1864         rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1865
1866         for rx in rxs:
1867             rx = IP(peer_1.decrypt_transport(rx, True))
1868
1869             # check the original packet is present
1870             self.assertEqual(rx[IP].dst, p[IP].dst)
1871             self.assertEqual(rx[IP].ttl, p[IP].ttl - 1)
1872
1873         # send packets into the tunnel, expect to receive them on
1874         # the other side
1875         p = [
1876             (
1877                 peer_1.mk_tunnel_header(self.pg1, True)
1878                 / Wireguard(message_type=4, reserved_zero=0)
1879                 / WireguardTransport(
1880                     receiver_index=peer_1.sender,
1881                     counter=ii + 1,
1882                     encrypted_encapsulated_packet=peer_1.encrypt_transport(
1883                         (
1884                             IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1885                             / UDP(sport=222, dport=223)
1886                             / Raw()
1887                         )
1888                     ),
1889                 )
1890             )
1891             for ii in range(255)
1892         ]
1893
1894         rxs = self.send_and_expect(self.pg1, p, self.pg0)
1895
1896         for rx in rxs:
1897             self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1898             self.assertEqual(rx[IP].ttl, 19)
1899
1900         r1.remove_vpp_config()
1901         peer_1.remove_vpp_config()
1902         wg0.remove_vpp_config()
1903
1904     def test_wg_multi_peer(self):
1905         """multiple peer setup"""
1906         port = 12373
1907
1908         # Create interfaces
1909         wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1910         wg1 = VppWgInterface(self, self.pg2.local_ip4, port + 1).add_vpp_config()
1911         wg0.admin_up()
1912         wg1.admin_up()
1913
1914         # Check peer counter
1915         self.assertEqual(len(self.vapi.wireguard_peers_dump()), 0)
1916
1917         self.pg_enable_capture(self.pg_interfaces)
1918         self.pg_start()
1919
1920         # Create many peers on sencond interface
1921         NUM_PEERS = 16
1922         self.pg2.generate_remote_hosts(NUM_PEERS)
1923         self.pg2.configure_ipv4_neighbors()
1924         self.pg1.generate_remote_hosts(NUM_PEERS)
1925         self.pg1.configure_ipv4_neighbors()
1926
1927         peers_1 = []
1928         peers_2 = []
1929         routes_1 = []
1930         routes_2 = []
1931         for i in range(NUM_PEERS):
1932             peers_1.append(
1933                 VppWgPeer(
1934                     self,
1935                     wg0,
1936                     self.pg1.remote_hosts[i].ip4,
1937                     port + 1 + i,
1938                     ["10.0.%d.4/32" % i],
1939                 ).add_vpp_config()
1940             )
1941             routes_1.append(
1942                 VppIpRoute(
1943                     self,
1944                     "10.0.%d.4" % i,
1945                     32,
1946                     [VppRoutePath(self.pg1.remote_hosts[i].ip4, wg0.sw_if_index)],
1947                 ).add_vpp_config()
1948             )
1949
1950             peers_2.append(
1951                 VppWgPeer(
1952                     self,
1953                     wg1,
1954                     self.pg2.remote_hosts[i].ip4,
1955                     port + 100 + i,
1956                     ["10.100.%d.4/32" % i],
1957                 ).add_vpp_config()
1958             )
1959             routes_2.append(
1960                 VppIpRoute(
1961                     self,
1962                     "10.100.%d.4" % i,
1963                     32,
1964                     [VppRoutePath(self.pg2.remote_hosts[i].ip4, wg1.sw_if_index)],
1965                 ).add_vpp_config()
1966             )
1967
1968         self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_PEERS * 2)
1969
1970         self.logger.info(self.vapi.cli("show wireguard peer"))
1971         self.logger.info(self.vapi.cli("show wireguard interface"))
1972         self.logger.info(self.vapi.cli("show adj 37"))
1973         self.logger.info(self.vapi.cli("sh ip fib 172.16.3.17"))
1974         self.logger.info(self.vapi.cli("sh ip fib 10.11.3.0"))
1975
1976         # remove routes
1977         for r in routes_1:
1978             r.remove_vpp_config()
1979         for r in routes_2:
1980             r.remove_vpp_config()
1981
1982         # remove peers
1983         for p in peers_1:
1984             self.assertTrue(p.query_vpp_config())
1985             p.remove_vpp_config()
1986         for p in peers_2:
1987             self.assertTrue(p.query_vpp_config())
1988             p.remove_vpp_config()
1989
1990         wg0.remove_vpp_config()
1991         wg1.remove_vpp_config()
1992
1993     def test_wg_multi_interface(self):
1994         """Multi-tunnel on the same port"""
1995         port = 12500
1996
1997         # Create many wireguard interfaces
1998         NUM_IFS = 4
1999         self.pg1.generate_remote_hosts(NUM_IFS)
2000         self.pg1.configure_ipv4_neighbors()
2001         self.pg0.generate_remote_hosts(NUM_IFS)
2002         self.pg0.configure_ipv4_neighbors()
2003
2004         # Create interfaces with a peer on each
2005         peers = []
2006         routes = []
2007         wg_ifs = []
2008         for i in range(NUM_IFS):
2009             # Use the same port for each interface
2010             wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2011             wg0.admin_up()
2012             wg0.config_ip4()
2013             wg_ifs.append(wg0)
2014             peers.append(
2015                 VppWgPeer(
2016                     self,
2017                     wg0,
2018                     self.pg1.remote_hosts[i].ip4,
2019                     port + 1 + i,
2020                     ["10.0.%d.0/24" % i],
2021                 ).add_vpp_config()
2022             )
2023
2024             routes.append(
2025                 VppIpRoute(
2026                     self,
2027                     "10.0.%d.0" % i,
2028                     24,
2029                     [VppRoutePath("10.0.%d.4" % i, wg0.sw_if_index)],
2030                 ).add_vpp_config()
2031             )
2032
2033         self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_IFS)
2034
2035         for i in range(NUM_IFS):
2036             # send a valid handsake init for which we expect a response
2037             p = peers[i].mk_handshake(self.pg1)
2038             rx = self.send_and_expect(self.pg1, [p], self.pg1)
2039             peers[i].consume_response(rx[0])
2040
2041             # send a data packet from the peer through the tunnel
2042             # this completes the handshake
2043             p = (
2044                 IP(src="10.0.%d.4" % i, dst=self.pg0.remote_hosts[i].ip4, ttl=20)
2045                 / UDP(sport=222, dport=223)
2046                 / Raw()
2047             )
2048             d = peers[i].encrypt_transport(p)
2049             p = peers[i].mk_tunnel_header(self.pg1) / (
2050                 Wireguard(message_type=4, reserved_zero=0)
2051                 / WireguardTransport(
2052                     receiver_index=peers[i].sender,
2053                     counter=0,
2054                     encrypted_encapsulated_packet=d,
2055                 )
2056             )
2057             rxs = self.send_and_expect(self.pg1, [p], self.pg0)
2058             for rx in rxs:
2059                 self.assertEqual(rx[IP].dst, self.pg0.remote_hosts[i].ip4)
2060                 self.assertEqual(rx[IP].ttl, 19)
2061
2062         # send a packets that are routed into the tunnel
2063         for i in range(NUM_IFS):
2064             p = (
2065                 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2066                 / IP(src=self.pg0.remote_hosts[i].ip4, dst="10.0.%d.4" % i)
2067                 / UDP(sport=555, dport=556)
2068                 / Raw(b"\x00" * 80)
2069             )
2070
2071             rxs = self.send_and_expect(self.pg0, p * 64, self.pg1)
2072
2073             for rx in rxs:
2074                 rx = IP(peers[i].decrypt_transport(rx))
2075
2076                 # check the oringial packet is present
2077                 self.assertEqual(rx[IP].dst, p[IP].dst)
2078                 self.assertEqual(rx[IP].ttl, p[IP].ttl - 1)
2079
2080         # send packets into the tunnel
2081         for i in range(NUM_IFS):
2082             p = [
2083                 (
2084                     peers[i].mk_tunnel_header(self.pg1)
2085                     / Wireguard(message_type=4, reserved_zero=0)
2086                     / WireguardTransport(
2087                         receiver_index=peers[i].sender,
2088                         counter=ii + 1,
2089                         encrypted_encapsulated_packet=peers[i].encrypt_transport(
2090                             (
2091                                 IP(
2092                                     src="10.0.%d.4" % i,
2093                                     dst=self.pg0.remote_hosts[i].ip4,
2094                                     ttl=20,
2095                                 )
2096                                 / UDP(sport=222, dport=223)
2097                                 / Raw()
2098                             )
2099                         ),
2100                     )
2101                 )
2102                 for ii in range(64)
2103             ]
2104
2105             rxs = self.send_and_expect(self.pg1, p, self.pg0)
2106
2107             for rx in rxs:
2108                 self.assertEqual(rx[IP].dst, self.pg0.remote_hosts[i].ip4)
2109                 self.assertEqual(rx[IP].ttl, 19)
2110
2111         for r in routes:
2112             r.remove_vpp_config()
2113         for p in peers:
2114             p.remove_vpp_config()
2115         for i in wg_ifs:
2116             i.remove_vpp_config()
2117
2118     def test_wg_event(self):
2119         """Test events"""
2120         port = 12600
2121         ESTABLISHED_FLAG = (
2122             VppEnum.vl_api_wireguard_peer_flags_t.WIREGUARD_PEER_ESTABLISHED
2123         )
2124         DEAD_FLAG = VppEnum.vl_api_wireguard_peer_flags_t.WIREGUARD_PEER_STATUS_DEAD
2125
2126         # Create interfaces
2127         wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2128         wg1 = VppWgInterface(self, self.pg2.local_ip4, port + 1).add_vpp_config()
2129         wg0.admin_up()
2130         wg1.admin_up()
2131
2132         # Check peer counter
2133         self.assertEqual(len(self.vapi.wireguard_peers_dump()), 0)
2134
2135         self.pg_enable_capture(self.pg_interfaces)
2136         self.pg_start()
2137
2138         # Create peers
2139         NUM_PEERS = 2
2140         self.pg2.generate_remote_hosts(NUM_PEERS)
2141         self.pg2.configure_ipv4_neighbors()
2142         self.pg1.generate_remote_hosts(NUM_PEERS)
2143         self.pg1.configure_ipv4_neighbors()
2144
2145         peers_0 = []
2146         peers_1 = []
2147         routes_0 = []
2148         routes_1 = []
2149         for i in range(NUM_PEERS):
2150             peers_0.append(
2151                 VppWgPeer(
2152                     self,
2153                     wg0,
2154                     self.pg1.remote_hosts[i].ip4,
2155                     port + 1 + i,
2156                     ["10.0.%d.4/32" % i],
2157                 ).add_vpp_config()
2158             )
2159             routes_0.append(
2160                 VppIpRoute(
2161                     self,
2162                     "10.0.%d.4" % i,
2163                     32,
2164                     [VppRoutePath(self.pg1.remote_hosts[i].ip4, wg0.sw_if_index)],
2165                 ).add_vpp_config()
2166             )
2167
2168             peers_1.append(
2169                 VppWgPeer(
2170                     self,
2171                     wg1,
2172                     self.pg2.remote_hosts[i].ip4,
2173                     port + 100 + i,
2174                     ["10.100.%d.4/32" % i],
2175                 ).add_vpp_config()
2176             )
2177             routes_1.append(
2178                 VppIpRoute(
2179                     self,
2180                     "10.100.%d.4" % i,
2181                     32,
2182                     [VppRoutePath(self.pg2.remote_hosts[i].ip4, wg1.sw_if_index)],
2183                 ).add_vpp_config()
2184             )
2185
2186         self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_PEERS * 2)
2187
2188         # Want events from the first perr of wg0
2189         # and from all wg1 peers
2190         peers_0[0].want_events()
2191         wg1.want_events()
2192
2193         for i in range(NUM_PEERS):
2194             # send a valid handsake init for which we expect a response
2195             p = peers_0[i].mk_handshake(self.pg1)
2196             rx = self.send_and_expect(self.pg1, [p], self.pg1)
2197             peers_0[i].consume_response(rx[0])
2198             if i == 0:
2199                 peers_0[0].wait_event(ESTABLISHED_FLAG)
2200
2201             p = peers_1[i].mk_handshake(self.pg2)
2202             rx = self.send_and_expect(self.pg2, [p], self.pg2)
2203             peers_1[i].consume_response(rx[0])
2204
2205         wg1.wait_events(ESTABLISHED_FLAG, [peers_1[0].index, peers_1[1].index])
2206
2207         # remove routes
2208         for r in routes_0:
2209             r.remove_vpp_config()
2210         for r in routes_1:
2211             r.remove_vpp_config()
2212
2213         # remove peers
2214         for i in range(NUM_PEERS):
2215             self.assertTrue(peers_0[i].query_vpp_config())
2216             peers_0[i].remove_vpp_config()
2217             if i == 0:
2218                 peers_0[i].wait_event(0)
2219                 peers_0[i].wait_event(DEAD_FLAG)
2220         for p in peers_1:
2221             self.assertTrue(p.query_vpp_config())
2222             p.remove_vpp_config()
2223             p.wait_event(0)
2224             p.wait_event(DEAD_FLAG)
2225
2226         wg0.remove_vpp_config()
2227         wg1.remove_vpp_config()
2228
2229
2230 class WireguardHandoffTests(TestWg):
2231     """Wireguard Tests in multi worker setup"""
2232
2233     vpp_worker_count = 2
2234
2235     def test_wg_peer_init(self):
2236         """Handoff"""
2237
2238         port = 12383
2239
2240         # Create interfaces
2241         wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2242         wg0.admin_up()
2243         wg0.config_ip4()
2244
2245         peer_1 = VppWgPeer(
2246             self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.2.0/24", "10.11.3.0/24"]
2247         ).add_vpp_config()
2248         self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2249
2250         r1 = VppIpRoute(
2251             self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
2252         ).add_vpp_config()
2253
2254         # send a valid handsake init for which we expect a response
2255         p = peer_1.mk_handshake(self.pg1)
2256
2257         rx = self.send_and_expect(self.pg1, [p], self.pg1)
2258
2259         peer_1.consume_response(rx[0])
2260
2261         # send a data packet from the peer through the tunnel
2262         # this completes the handshake and pins the peer to worker 0
2263         p = (
2264             IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
2265             / UDP(sport=222, dport=223)
2266             / Raw()
2267         )
2268         d = peer_1.encrypt_transport(p)
2269         p = peer_1.mk_tunnel_header(self.pg1) / (
2270             Wireguard(message_type=4, reserved_zero=0)
2271             / WireguardTransport(
2272                 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
2273             )
2274         )
2275         rxs = self.send_and_expect(self.pg1, [p], self.pg0, worker=0)
2276
2277         for rx in rxs:
2278             self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
2279             self.assertEqual(rx[IP].ttl, 19)
2280
2281         # send a packets that are routed into the tunnel
2282         # and pins the peer tp worker 1
2283         pe = (
2284             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2285             / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
2286             / UDP(sport=555, dport=556)
2287             / Raw(b"\x00" * 80)
2288         )
2289         rxs = self.send_and_expect(self.pg0, pe * 255, self.pg1, worker=1)
2290         peer_1.validate_encapped(rxs, pe)
2291
2292         # send packets into the tunnel, from the other worker
2293         p = [
2294             (
2295                 peer_1.mk_tunnel_header(self.pg1)
2296                 / Wireguard(message_type=4, reserved_zero=0)
2297                 / WireguardTransport(
2298                     receiver_index=peer_1.sender,
2299                     counter=ii + 1,
2300                     encrypted_encapsulated_packet=peer_1.encrypt_transport(
2301                         (
2302                             IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
2303                             / UDP(sport=222, dport=223)
2304                             / Raw()
2305                         )
2306                     ),
2307                 )
2308             )
2309             for ii in range(255)
2310         ]
2311
2312         rxs = self.send_and_expect(self.pg1, p, self.pg0, worker=1)
2313
2314         for rx in rxs:
2315             self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
2316             self.assertEqual(rx[IP].ttl, 19)
2317
2318         # send a packets that are routed into the tunnel
2319         # from owrker 0
2320         rxs = self.send_and_expect(self.pg0, pe * 255, self.pg1, worker=0)
2321
2322         peer_1.validate_encapped(rxs, pe)
2323
2324         r1.remove_vpp_config()
2325         peer_1.remove_vpp_config()
2326         wg0.remove_vpp_config()
2327
2328     @unittest.skip("test disabled")
2329     def test_wg_multi_interface(self):
2330         """Multi-tunnel on the same port"""