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