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 (
21 from cryptography.hazmat.primitives.asymmetric.x25519 import (
25 from cryptography.hazmat.primitives.serialization import (
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
36 from Crypto.Cipher import ChaCha20_Poly1305
37 from Crypto.Random import get_random_bytes
39 from vpp_ipip_tun_interface import VppIpIpTunInterface
40 from vpp_interface import VppInterface
41 from vpp_pg_interface import is_ipv6_misc
42 from vpp_ip_route import VppIpRoute, VppRoutePath
43 from vpp_object import VppObject
44 from vpp_papi import VppEnum
45 from framework import is_distro_ubuntu2204, is_distro_debian11, tag_fixme_vpp_debug
46 from framework import VppTestCase
47 from re import compile
50 """ TestWg is a subclass of VPPTestCase classes.
57 def private_key_bytes(k):
58 return k.private_bytes(Encoding.Raw, PrivateFormat.Raw, NoEncryption())
61 def public_key_bytes(k):
62 return k.public_bytes(Encoding.Raw, PublicFormat.Raw)
65 def get_field_bytes(pkt, name):
66 fld, val = pkt.getfield_and_val(name)
67 return fld.i2m(pkt, val)
70 class VppWgInterface(VppInterface):
72 VPP WireGuard interface
75 def __init__(self, test, src, port):
76 super(VppWgInterface, self).__init__(test)
80 self.private_key = X25519PrivateKey.generate()
81 self.public_key = self.private_key.public_key()
83 # cookie related params
84 self.cookie_key = blake2s(b"cookie--" + self.public_key_bytes()).digest()
86 def public_key_bytes(self):
87 return public_key_bytes(self.public_key)
89 def private_key_bytes(self):
90 return private_key_bytes(self.private_key)
92 def add_vpp_config(self):
93 r = self.test.vapi.wireguard_interface_create(
95 "user_instance": 0xFFFFFFFF,
98 "private_key": private_key_bytes(self.private_key),
99 "generate_key": False,
102 self.set_sw_if_index(r.sw_if_index)
103 self.test.registry.register(self, self.test.logger)
106 def remove_vpp_config(self):
107 self.test.vapi.wireguard_interface_delete(sw_if_index=self._sw_if_index)
109 def query_vpp_config(self):
110 ts = self.test.vapi.wireguard_interface_dump(sw_if_index=0xFFFFFFFF)
113 t.interface.sw_if_index == self._sw_if_index
114 and str(t.interface.src_ip) == self.src
115 and t.interface.port == self.port
116 and t.interface.private_key == private_key_bytes(self.private_key)
121 def want_events(self, peer_index=0xFFFFFFFF):
122 self.test.vapi.want_wireguard_peer_events(
125 sw_if_index=self._sw_if_index,
126 peer_index=peer_index,
129 def wait_events(self, expect, peers, timeout=5):
130 for i in range(len(peers)):
131 rv = self.test.vapi.wait_for_event(timeout, "wireguard_peer_event")
132 self.test.assertEqual(rv.peer_index, peers[i])
133 self.test.assertEqual(rv.flags, expect)
136 return self.object_id()
139 return "wireguard-%d" % self._sw_if_index
142 NOISE_HANDSHAKE_NAME = b"Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s"
143 NOISE_IDENTIFIER_NAME = b"WireGuard v1 zx2c4 Jason@zx2c4.com"
145 HANDSHAKE_COUNTING_INTERVAL = 0.5
146 UNDER_LOAD_INTERVAL = 1.0
147 HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD = 40
148 HANDSHAKE_NUM_BEFORE_RATELIMITING = 5
151 class VppWgPeer(VppObject):
152 def __init__(self, test, itf, endpoint, port, allowed_ips, persistent_keepalive=15):
155 self.endpoint = endpoint
157 self.allowed_ips = allowed_ips
158 self.persistent_keepalive = persistent_keepalive
160 # remote peer's public
161 self.private_key = X25519PrivateKey.generate()
162 self.public_key = self.private_key.public_key()
164 # cookie related params
165 self.cookie_key = blake2s(b"cookie--" + self.public_key_bytes()).digest()
166 self.last_sent_cookie = None
167 self.last_mac1 = None
168 self.last_received_cookie = None
170 self.noise = NoiseConnection.from_name(NOISE_HANDSHAKE_NAME)
172 def change_endpoint(self, endpoint, port):
173 self.endpoint = endpoint
176 def add_vpp_config(self):
177 rv = self._test.vapi.wireguard_peer_add(
179 "public_key": self.public_key_bytes(),
181 "endpoint": self.endpoint,
182 "n_allowed_ips": len(self.allowed_ips),
183 "allowed_ips": self.allowed_ips,
184 "sw_if_index": self.itf.sw_if_index,
185 "persistent_keepalive": self.persistent_keepalive,
188 self.index = rv.peer_index
189 self.receiver_index = self.index + 1
190 self._test.registry.register(self, self._test.logger)
193 def remove_vpp_config(self):
194 self._test.vapi.wireguard_peer_remove(peer_index=self.index)
197 return "wireguard-peer-%s" % self.index
199 def public_key_bytes(self):
200 return public_key_bytes(self.public_key)
202 def query_vpp_config(self):
203 peers = self._test.vapi.wireguard_peers_dump()
206 # "::" endpoint will be returned as "0.0.0.0" in peer's details
207 endpoint = "0.0.0.0" if self.endpoint == "::" else self.endpoint
209 p.peer.public_key == self.public_key_bytes()
210 and p.peer.port == self.port
211 and str(p.peer.endpoint) == endpoint
212 and p.peer.sw_if_index == self.itf.sw_if_index
213 and len(self.allowed_ips) == p.peer.n_allowed_ips
215 self.allowed_ips.sort()
216 p.peer.allowed_ips.sort()
218 for (a1, a2) in zip(self.allowed_ips, p.peer.allowed_ips):
219 if str(a1) != str(a2):
224 def mk_tunnel_header(self, tx_itf, is_ip6=False):
227 Ether(dst=tx_itf.local_mac, src=tx_itf.remote_mac)
228 / IP(src=self.endpoint, dst=self.itf.src)
229 / UDP(sport=self.port, dport=self.itf.port)
233 Ether(dst=tx_itf.local_mac, src=tx_itf.remote_mac)
234 / IPv6(src=self.endpoint, dst=self.itf.src)
235 / UDP(sport=self.port, dport=self.itf.port)
238 def noise_reset(self):
239 self.noise = NoiseConnection.from_name(NOISE_HANDSHAKE_NAME)
241 def noise_init(self, public_key=None):
242 self.noise.set_prologue(NOISE_IDENTIFIER_NAME)
243 self.noise.set_psks(psk=bytes(bytearray(32)))
246 public_key = self.itf.public_key
249 self.noise.set_keypair_from_private_bytes(
250 Keypair.STATIC, private_key_bytes(self.private_key)
253 self.noise.set_keypair_from_public_bytes(
254 Keypair.REMOTE_STATIC, public_key_bytes(public_key)
257 self.noise.start_handshake()
259 def mk_cookie(self, p, tx_itf, is_resp=False, is_ip6=False):
260 self.verify_header(p, is_ip6)
262 wg_pkt = Wireguard(p[Raw])
265 self._test.assertEqual(wg_pkt[Wireguard].message_type, 2)
266 self._test.assertEqual(wg_pkt[Wireguard].reserved_zero, 0)
267 self._test.assertEqual(wg_pkt[WireguardResponse].mac2, bytes([0] * 16))
269 self._test.assertEqual(wg_pkt[Wireguard].message_type, 1)
270 self._test.assertEqual(wg_pkt[Wireguard].reserved_zero, 0)
271 self._test.assertEqual(wg_pkt[WireguardInitiation].mac2, bytes([0] * 16))
273 # collect info from wg packet (initiation or response)
274 src = get_field_bytes(p[IPv6 if is_ip6 else IP], "src")
275 sport = p[UDP].sport.to_bytes(2, byteorder="big")
277 mac1 = wg_pkt[WireguardResponse].mac1
278 sender_index = wg_pkt[WireguardResponse].sender_index
280 mac1 = wg_pkt[WireguardInitiation].mac1
281 sender_index = wg_pkt[WireguardInitiation].sender_index
284 cookie_reply = Wireguard() / WireguardCookieReply()
285 cookie_reply[Wireguard].message_type = 3
286 cookie_reply[Wireguard].reserved_zero = 0
287 cookie_reply[WireguardCookieReply].receiver_index = sender_index
288 nonce = get_random_bytes(24)
289 cookie_reply[WireguardCookieReply].nonce = nonce
291 # generate cookie data
292 changing_secret = get_random_bytes(32)
293 self.last_sent_cookie = blake2s(
294 src + sport, digest_size=16, key=changing_secret
297 # encrypt cookie data
298 cipher = ChaCha20_Poly1305.new(key=self.cookie_key, nonce=nonce)
300 ciphertext, tag = cipher.encrypt_and_digest(self.last_sent_cookie)
301 cookie_reply[WireguardCookieReply].encrypted_cookie = ciphertext + tag
303 # prepare cookie reply to be sent
304 cookie_reply = self.mk_tunnel_header(tx_itf, is_ip6) / cookie_reply
308 def consume_cookie(self, p, is_ip6=False):
309 self.verify_header(p, is_ip6)
311 cookie_reply = Wireguard(p[Raw])
313 self._test.assertEqual(cookie_reply[Wireguard].message_type, 3)
314 self._test.assertEqual(cookie_reply[Wireguard].reserved_zero, 0)
315 self._test.assertEqual(
316 cookie_reply[WireguardCookieReply].receiver_index, self.receiver_index
319 # collect info from cookie reply
320 nonce = cookie_reply[WireguardCookieReply].nonce
321 encrypted_cookie = cookie_reply[WireguardCookieReply].encrypted_cookie
322 ciphertext, tag = encrypted_cookie[:16], encrypted_cookie[16:]
324 # decrypt cookie data
325 cipher = ChaCha20_Poly1305.new(key=self.itf.cookie_key, nonce=nonce)
326 cipher.update(self.last_mac1)
327 self.last_received_cookie = cipher.decrypt_and_verify(ciphertext, tag)
329 def mk_handshake(self, tx_itf, is_ip6=False, public_key=None):
330 self.noise.set_as_initiator()
331 self.noise_init(public_key)
333 p = Wireguard() / WireguardInitiation()
335 p[Wireguard].message_type = 1
336 p[Wireguard].reserved_zero = 0
337 p[WireguardInitiation].sender_index = self.receiver_index
339 # some random data for the message
340 # lifted from the noise protocol's wireguard example
341 now = datetime.datetime.now()
344 4611686018427387914 + int(now.timestamp()),
345 int(now.microsecond * 1e3),
347 b = self.noise.write_message(payload=tai)
349 # load noise into init message
350 p[WireguardInitiation].unencrypted_ephemeral = b[0:32]
351 p[WireguardInitiation].encrypted_static = b[32:80]
352 p[WireguardInitiation].encrypted_timestamp = b[80:108]
354 # generate the mac1 hash
355 mac_key = blake2s(b"mac1----" + self.itf.public_key_bytes()).digest()
356 mac1 = blake2s(bytes(p)[0:116], digest_size=16, key=mac_key).digest()
357 p[WireguardInitiation].mac1 = mac1
358 self.last_mac1 = mac1
360 # generate the mac2 hash
361 if self.last_received_cookie:
363 bytes(p)[0:132], digest_size=16, key=self.last_received_cookie
365 p[WireguardInitiation].mac2 = mac2
366 self.last_received_cookie = None
368 p[WireguardInitiation].mac2 = bytearray(16)
370 p = self.mk_tunnel_header(tx_itf, is_ip6) / p
374 def verify_header(self, p, is_ip6=False):
376 self._test.assertEqual(p[IP].src, self.itf.src)
377 self._test.assertEqual(p[IP].dst, self.endpoint)
378 self._test.assert_packet_checksums_valid(p)
380 self._test.assertEqual(p[IPv6].src, self.itf.src)
381 self._test.assertEqual(p[IPv6].dst, self.endpoint)
382 self._test.assert_packet_checksums_valid(p, False)
383 self._test.assertEqual(p[UDP].sport, self.itf.port)
384 self._test.assertEqual(p[UDP].dport, self.port)
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)
391 init = Wireguard(p[Raw])
393 self._test.assertEqual(init[Wireguard].message_type, 1)
394 self._test.assertEqual(init[Wireguard].reserved_zero, 0)
396 self.sender = init[WireguardInitiation].sender_index
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)
403 # validate the mac2 hash
405 self._test.assertNotEqual(init[WireguardInitiation].mac2, bytes([0] * 16))
406 self._test.assertNotEqual(self.last_sent_cookie, None)
408 bytes(init)[0:-16], digest_size=16, key=self.last_sent_cookie
410 self._test.assertEqual(init[WireguardInitiation].mac2, mac2)
411 self.last_sent_cookie = None
413 self._test.assertEqual(init[WireguardInitiation].mac2, bytes([0] * 16))
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])
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:],
428 mac1 = blake2s(bytes(resp)[:-32], digest_size=16, key=mac_key).digest()
429 resp[WireguardResponse].mac1 = mac1
430 self.last_mac1 = mac1
432 resp = self.mk_tunnel_header(tx_itf, is_ip6) / resp
433 self._test.assertTrue(self.noise.handshake_finished)
437 def consume_response(self, p, is_ip6=False):
438 self.verify_header(p, is_ip6)
440 resp = Wireguard(p[Raw])
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
448 self.sender = resp[Wireguard].sender_index
450 payload = self.noise.read_message(bytes(resp)[12:60])
451 self._test.assertEqual(payload, b"")
452 self._test.assertTrue(self.noise.handshake_finished)
454 def decrypt_transport(self, p, is_ip6=False):
455 self.verify_header(p, is_ip6)
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
464 d = self.noise.decrypt(p[WireguardTransport].encrypted_encapsulated_packet)
467 def encrypt_transport(self, p):
468 return self.noise.encrypt(bytes(p))
470 def validate_encapped(self, rxs, tx, is_tunnel_ip6=False, is_transport_ip6=False):
472 rx = self.decrypt_transport(rx, is_tunnel_ip6)
473 if is_transport_ip6 is False:
475 # check the original packet is present
476 self._test.assertEqual(rx[IP].dst, tx[IP].dst)
477 self._test.assertEqual(rx[IP].ttl, tx[IP].ttl - 1)
480 # check the original packet is present
481 self._test.assertEqual(rx[IPv6].dst, tx[IPv6].dst)
482 self._test.assertEqual(rx[IPv6].hlim, tx[IPv6].hlim - 1)
484 def want_events(self):
485 self._test.vapi.want_wireguard_peer_events(
488 peer_index=self.index,
489 sw_if_index=self.itf.sw_if_index,
492 def wait_event(self, expect, timeout=5):
493 rv = self._test.vapi.wait_for_event(timeout, "wireguard_peer_event")
494 self._test.assertEqual(rv.flags, expect)
495 self._test.assertEqual(rv.peer_index, self.index)
498 def is_handshake_init(p):
499 wg_p = Wireguard(p[Raw])
501 return wg_p[Wireguard].message_type == 1
504 class TestWg(VppTestCase):
505 """Wireguard Test Case"""
507 error_str = compile(r"Error")
509 wg4_output_node_name = "/err/wg4-output-tun/"
510 wg4_input_node_name = "/err/wg4-input/"
511 wg6_output_node_name = "/err/wg6-output-tun/"
512 wg6_input_node_name = "/err/wg6-input/"
513 kp4_error = wg4_output_node_name + "Keypair error"
514 mac4_error = wg4_input_node_name + "Invalid MAC handshake"
515 peer4_in_err = wg4_input_node_name + "Peer error"
516 peer4_out_err = wg4_output_node_name + "Peer error"
517 kp6_error = wg6_output_node_name + "Keypair error"
518 mac6_error = wg6_input_node_name + "Invalid MAC handshake"
519 peer6_in_err = wg6_input_node_name + "Peer error"
520 peer6_out_err = wg6_output_node_name + "Peer error"
521 cookie_dec4_err = wg4_input_node_name + "Failed during Cookie decryption"
522 cookie_dec6_err = wg6_input_node_name + "Failed during Cookie decryption"
523 ratelimited4_err = wg4_input_node_name + "Handshake ratelimited"
524 ratelimited6_err = wg6_input_node_name + "Handshake ratelimited"
528 super(TestWg, cls).setUpClass()
529 if (is_distro_ubuntu2204 == True or is_distro_debian11 == True) and not hasattr(
534 cls.create_pg_interfaces(range(3))
535 for i in cls.pg_interfaces:
543 super(TestWg, cls).tearDownClass()
547 def tearDownClass(cls):
548 super(TestWg, cls).tearDownClass()
551 super(VppTestCase, self).setUp()
552 self.base_kp4_err = self.statistics.get_err_counter(self.kp4_error)
553 self.base_mac4_err = self.statistics.get_err_counter(self.mac4_error)
554 self.base_peer4_in_err = self.statistics.get_err_counter(self.peer4_in_err)
555 self.base_peer4_out_err = self.statistics.get_err_counter(self.peer4_out_err)
556 self.base_kp6_err = self.statistics.get_err_counter(self.kp6_error)
557 self.base_mac6_err = self.statistics.get_err_counter(self.mac6_error)
558 self.base_peer6_in_err = self.statistics.get_err_counter(self.peer6_in_err)
559 self.base_peer6_out_err = self.statistics.get_err_counter(self.peer6_out_err)
560 self.base_cookie_dec4_err = self.statistics.get_err_counter(
563 self.base_cookie_dec6_err = self.statistics.get_err_counter(
566 self.base_ratelimited4_err = self.statistics.get_err_counter(
567 self.ratelimited4_err
569 self.base_ratelimited6_err = self.statistics.get_err_counter(
570 self.ratelimited6_err
573 def send_and_assert_no_replies_ignoring_init(
574 self, intf, pkts, remark="", timeout=None
576 self.pg_send(intf, pkts)
578 def _filter_out_fn(p):
579 return is_ipv6_misc(p) or is_handshake_init(p)
584 for i in self.pg_interfaces:
585 i.assert_nothing_captured(
586 timeout=timeout, remark=remark, filter_out_fn=_filter_out_fn
592 def test_wg_interface(self):
593 """Simple interface creation"""
597 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
599 self.logger.info(self.vapi.cli("sh int"))
602 wg0.remove_vpp_config()
604 def test_handshake_hash(self):
605 """test hashing an init message"""
606 # a init packet generated by linux given the key below
629 b = bytearray.fromhex(h)
632 pubb = base64.b64decode("aRuHFTTxICIQNefp05oKWlJv3zgKxb8+WW7JJMh0jyM=")
633 pub = X25519PublicKey.from_public_bytes(pubb)
635 self.assertEqual(pubb, public_key_bytes(pub))
637 # strip the macs and build a new packet
639 mac_key = blake2s(b"mac1----" + public_key_bytes(pub)).digest()
640 init += blake2s(init, digest_size=16, key=mac_key).digest()
643 act = Wireguard(init)
645 self.assertEqual(tgt, act)
647 def _test_wg_send_cookie_tmpl(self, is_resp, is_ip6):
650 # create wg interface
652 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
656 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
660 self.pg_enable_capture(self.pg_interfaces)
666 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
670 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
672 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
675 # prepare and send a handshake initiation
676 # expect the peer to send a handshake response
677 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
678 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
680 # wait for the peer to send a handshake initiation
681 rxs = self.pg1.get_capture(1, timeout=2)
683 # prepare and send a wrong cookie reply
684 # expect no replies and the cookie error incremented
685 cookie = peer_1.mk_cookie(rxs[0], self.pg1, is_resp=is_resp, is_ip6=is_ip6)
686 cookie.nonce = b"1234567890"
687 self.send_and_assert_no_replies(self.pg1, [cookie], timeout=0.1)
690 self.base_cookie_dec6_err + 1,
691 self.statistics.get_err_counter(self.cookie_dec6_err),
695 self.base_cookie_dec4_err + 1,
696 self.statistics.get_err_counter(self.cookie_dec4_err),
699 # prepare and send a correct cookie reply
700 cookie = peer_1.mk_cookie(rxs[0], self.pg1, is_resp=is_resp, is_ip6=is_ip6)
701 self.pg_send(self.pg1, [cookie])
703 # wait for the peer to send a handshake initiation with mac2 set
704 rxs = self.pg1.get_capture(1, timeout=6)
706 # verify the initiation and its mac2
707 peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6, is_mac2=True)
710 peer_1.remove_vpp_config()
711 wg0.remove_vpp_config()
713 def test_wg_send_cookie_on_init_v4(self):
714 """Send cookie on handshake initiation (v4)"""
715 self._test_wg_send_cookie_tmpl(is_resp=False, is_ip6=False)
717 def test_wg_send_cookie_on_init_v6(self):
718 """Send cookie on handshake initiation (v6)"""
719 self._test_wg_send_cookie_tmpl(is_resp=False, is_ip6=True)
721 def test_wg_send_cookie_on_resp_v4(self):
722 """Send cookie on handshake response (v4)"""
723 self._test_wg_send_cookie_tmpl(is_resp=True, is_ip6=False)
725 def test_wg_send_cookie_on_resp_v6(self):
726 """Send cookie on handshake response (v6)"""
727 self._test_wg_send_cookie_tmpl(is_resp=True, is_ip6=True)
729 def _test_wg_receive_cookie_tmpl(self, is_resp, is_ip6):
732 # create wg interface
734 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
738 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
742 self.pg_enable_capture(self.pg_interfaces)
748 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
752 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
754 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
757 # wait for the peer to send a handshake initiation
758 rxs = self.pg1.get_capture(1, timeout=2)
759 # prepare and send a bunch of handshake responses
760 # expect to switch to under load state
761 resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6)
762 txs = [resp] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
763 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
764 # reset noise to be able to turn into initiator later
767 # prepare and send a bunch of handshake initiations
768 # expect to switch to under load state
769 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
770 txs = [init] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
771 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
773 # expect the peer to send a cookie reply
774 peer_1.consume_cookie(rxs[-1], is_ip6=is_ip6)
776 # prepare and send a handshake initiation with wrong mac2
777 # expect a cookie reply
778 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
779 init.mac2 = b"1234567890"
780 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
781 peer_1.consume_cookie(rxs[0], is_ip6=is_ip6)
783 # prepare and send a handshake initiation with correct mac2
784 # expect a handshake response
785 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
786 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
788 # verify the response
789 peer_1.consume_response(rxs[0], is_ip6=is_ip6)
791 # clear up under load state
792 self.sleep(UNDER_LOAD_INTERVAL)
795 peer_1.remove_vpp_config()
796 wg0.remove_vpp_config()
798 def test_wg_receive_cookie_on_init_v4(self):
799 """Receive cookie on handshake initiation (v4)"""
800 self._test_wg_receive_cookie_tmpl(is_resp=False, is_ip6=False)
802 def test_wg_receive_cookie_on_init_v6(self):
803 """Receive cookie on handshake initiation (v6)"""
804 self._test_wg_receive_cookie_tmpl(is_resp=False, is_ip6=True)
806 def test_wg_receive_cookie_on_resp_v4(self):
807 """Receive cookie on handshake response (v4)"""
808 self._test_wg_receive_cookie_tmpl(is_resp=True, is_ip6=False)
810 def test_wg_receive_cookie_on_resp_v6(self):
811 """Receive cookie on handshake response (v6)"""
812 self._test_wg_receive_cookie_tmpl(is_resp=True, is_ip6=True)
814 def test_wg_under_load_interval(self):
815 """Under load interval"""
818 # create wg interface
819 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
823 self.pg_enable_capture(self.pg_interfaces)
828 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
830 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
832 # prepare and send a bunch of handshake initiations
833 # expect to switch to under load state
834 init = peer_1.mk_handshake(self.pg1)
835 txs = [init] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
836 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
838 # expect the peer to send a cookie reply
839 peer_1.consume_cookie(rxs[-1])
841 # sleep till the next counting interval
842 # expect under load state is still active
843 self.sleep(HANDSHAKE_COUNTING_INTERVAL)
845 # prepare and send a handshake initiation with wrong mac2
846 # expect a cookie reply
847 init = peer_1.mk_handshake(self.pg1)
848 init.mac2 = b"1234567890"
849 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
850 peer_1.consume_cookie(rxs[0])
852 # sleep till the end of being under load
853 # expect under load state is over
854 self.sleep(UNDER_LOAD_INTERVAL - HANDSHAKE_COUNTING_INTERVAL)
856 # prepare and send a handshake initiation with wrong mac2
857 # expect a handshake response
858 init = peer_1.mk_handshake(self.pg1)
859 init.mac2 = b"1234567890"
860 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
862 # verify the response
863 peer_1.consume_response(rxs[0])
866 peer_1.remove_vpp_config()
867 wg0.remove_vpp_config()
869 def _test_wg_handshake_ratelimiting_tmpl(self, is_ip6):
872 # create wg interface
874 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
878 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
882 self.pg_enable_capture(self.pg_interfaces)
888 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
892 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
894 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
896 # prepare and send a bunch of handshake initiations
897 # expect to switch to under load state
898 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
899 txs = [init] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
900 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
902 # expect the peer to send a cookie reply
903 peer_1.consume_cookie(rxs[-1], is_ip6=is_ip6)
905 # prepare and send a bunch of handshake initiations with correct mac2
906 # expect a handshake response and then ratelimiting
908 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
909 txs = [init] * (HANDSHAKE_NUM_BEFORE_RATELIMITING + NUM_TO_REJECT)
910 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
914 self.base_ratelimited6_err + NUM_TO_REJECT,
915 self.statistics.get_err_counter(self.ratelimited6_err),
919 self.base_ratelimited4_err + NUM_TO_REJECT,
920 self.statistics.get_err_counter(self.ratelimited4_err),
923 # verify the response
924 peer_1.consume_response(rxs[0], is_ip6=is_ip6)
926 # clear up under load state
927 self.sleep(UNDER_LOAD_INTERVAL)
930 peer_1.remove_vpp_config()
931 wg0.remove_vpp_config()
933 def test_wg_handshake_ratelimiting_v4(self):
934 """Handshake ratelimiting (v4)"""
935 self._test_wg_handshake_ratelimiting_tmpl(is_ip6=False)
937 def test_wg_handshake_ratelimiting_v6(self):
938 """Handshake ratelimiting (v6)"""
939 self._test_wg_handshake_ratelimiting_tmpl(is_ip6=True)
941 def test_wg_handshake_ratelimiting_multi_peer(self):
942 """Handshake ratelimiting (multiple peer)"""
945 # create wg interface
946 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
950 self.pg_enable_capture(self.pg_interfaces)
955 self.pg1.generate_remote_hosts(NUM_PEERS)
956 self.pg1.configure_ipv4_neighbors()
959 self, wg0, self.pg1.remote_hosts[0].ip4, port + 1, ["10.11.3.0/24"]
962 self, wg0, self.pg1.remote_hosts[1].ip4, port + 1, ["10.11.4.0/24"]
964 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 2)
966 # (peer_1) prepare and send a bunch of handshake initiations
967 # expect not to switch to under load state
968 init_1 = peer_1.mk_handshake(self.pg1)
969 txs = [init_1] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
970 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
972 # (peer_1) expect the peer to send a handshake response
973 peer_1.consume_response(rxs[0])
976 # (peer_1) send another bunch of handshake initiations
977 # expect to switch to under load state
978 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
980 # (peer_1) expect the peer to send a cookie reply
981 peer_1.consume_cookie(rxs[-1])
983 # (peer_2) prepare and send a handshake initiation
984 # expect a cookie reply
985 init_2 = peer_2.mk_handshake(self.pg1)
986 rxs = self.send_and_expect(self.pg1, [init_2], self.pg1)
987 peer_2.consume_cookie(rxs[0])
989 # (peer_1) (peer_2) prepare and send a bunch of handshake initiations with correct mac2
990 # expect a handshake response and then ratelimiting
991 PEER_1_NUM_TO_REJECT = 2
992 PEER_2_NUM_TO_REJECT = 5
993 init_1 = peer_1.mk_handshake(self.pg1)
994 txs = [init_1] * (HANDSHAKE_NUM_BEFORE_RATELIMITING + PEER_1_NUM_TO_REJECT)
995 init_2 = peer_2.mk_handshake(self.pg1)
996 txs += [init_2] * (HANDSHAKE_NUM_BEFORE_RATELIMITING + PEER_2_NUM_TO_REJECT)
997 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
1000 self.base_ratelimited4_err + PEER_1_NUM_TO_REJECT
1001 < self.statistics.get_err_counter(self.ratelimited4_err)
1002 <= self.base_ratelimited4_err + PEER_1_NUM_TO_REJECT + PEER_2_NUM_TO_REJECT
1005 # (peer_1) (peer_2) verify the response
1006 peer_1.consume_response(rxs[0])
1007 peer_2.consume_response(rxs[1])
1009 # clear up under load state
1010 self.sleep(UNDER_LOAD_INTERVAL)
1013 peer_1.remove_vpp_config()
1014 peer_2.remove_vpp_config()
1015 wg0.remove_vpp_config()
1017 def _test_wg_peer_roaming_on_handshake_tmpl(self, is_endpoint_set, is_resp, is_ip6):
1020 # create wg interface
1022 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1026 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1030 self.pg_enable_capture(self.pg_interfaces)
1033 # create more remote hosts
1034 NUM_REMOTE_HOSTS = 2
1035 self.pg1.generate_remote_hosts(NUM_REMOTE_HOSTS)
1037 self.pg1.configure_ipv6_neighbors()
1039 self.pg1.configure_ipv4_neighbors()
1046 endpoint=self.pg1.remote_hosts[0].ip6 if is_endpoint_set else "::",
1047 port=port + 1 if is_endpoint_set else 0,
1048 allowed_ips=["1::3:0/112"],
1054 endpoint=self.pg1.remote_hosts[0].ip4 if is_endpoint_set else "0.0.0.0",
1055 port=port + 1 if is_endpoint_set else 0,
1056 allowed_ips=["10.11.3.0/24"],
1058 self.assertTrue(peer_1.query_vpp_config())
1061 # wait for the peer to send a handshake initiation
1062 rxs = self.pg1.get_capture(1, timeout=2)
1063 # prepare a handshake response
1064 resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6)
1067 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip6, port + 100)
1068 resp[IPv6].src, resp[UDP].sport = peer_1.endpoint, peer_1.port
1070 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip4, port + 100)
1071 resp[IP].src, resp[UDP].sport = peer_1.endpoint, peer_1.port
1072 # send the handshake response
1073 # expect a keepalive message sent to the new endpoint
1074 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1075 # verify the keepalive message
1076 b = peer_1.decrypt_transport(rxs[0], is_ip6=is_ip6)
1077 self.assertEqual(0, len(b))
1081 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip6, port + 100)
1083 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip4, port + 100)
1084 # prepare and send a handshake initiation
1085 # expect a handshake response sent to the new endpoint
1086 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
1087 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
1088 # verify the response
1089 peer_1.consume_response(rxs[0], is_ip6=is_ip6)
1090 self.assertTrue(peer_1.query_vpp_config())
1093 peer_1.remove_vpp_config()
1094 wg0.remove_vpp_config()
1096 def test_wg_peer_roaming_on_init_v4(self):
1097 """Peer roaming on handshake initiation (v4)"""
1098 self._test_wg_peer_roaming_on_handshake_tmpl(
1099 is_endpoint_set=False, is_resp=False, is_ip6=False
1102 def test_wg_peer_roaming_on_init_v6(self):
1103 """Peer roaming on handshake initiation (v6)"""
1104 self._test_wg_peer_roaming_on_handshake_tmpl(
1105 is_endpoint_set=False, is_resp=False, is_ip6=True
1108 def test_wg_peer_roaming_on_resp_v4(self):
1109 """Peer roaming on handshake response (v4)"""
1110 self._test_wg_peer_roaming_on_handshake_tmpl(
1111 is_endpoint_set=True, is_resp=True, is_ip6=False
1114 def test_wg_peer_roaming_on_resp_v6(self):
1115 """Peer roaming on handshake response (v6)"""
1116 self._test_wg_peer_roaming_on_handshake_tmpl(
1117 is_endpoint_set=True, is_resp=True, is_ip6=True
1120 def _test_wg_peer_roaming_on_data_tmpl(self, is_async, is_ip6):
1121 self.vapi.wg_set_async_mode(is_async)
1124 # create wg interface
1126 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1130 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1134 self.pg_enable_capture(self.pg_interfaces)
1137 # create more remote hosts
1138 NUM_REMOTE_HOSTS = 2
1139 self.pg1.generate_remote_hosts(NUM_REMOTE_HOSTS)
1141 self.pg1.configure_ipv6_neighbors()
1143 self.pg1.configure_ipv4_neighbors()
1148 self, wg0, self.pg1.remote_hosts[0].ip6, port + 1, ["1::3:0/112"]
1152 self, wg0, self.pg1.remote_hosts[0].ip4, port + 1, ["10.11.3.0/24"]
1154 self.assertTrue(peer_1.query_vpp_config())
1156 # create a route to rewrite traffic into the wg interface
1159 self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
1163 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1166 # wait for the peer to send a handshake initiation
1167 rxs = self.pg1.get_capture(1, timeout=2)
1169 # prepare and send a handshake response
1170 # expect a keepalive message
1171 resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6)
1172 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1174 # verify the keepalive message
1175 b = peer_1.decrypt_transport(rxs[0], is_ip6=is_ip6)
1176 self.assertEqual(0, len(b))
1180 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip6, port + 100)
1182 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip4, port + 100)
1184 # prepare and send a data packet
1185 # expect endpoint change
1187 ip_header = IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1189 ip_header = IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1191 peer_1.mk_tunnel_header(self.pg1, is_ip6=is_ip6)
1192 / Wireguard(message_type=4, reserved_zero=0)
1193 / WireguardTransport(
1194 receiver_index=peer_1.sender,
1196 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1197 ip_header / UDP(sport=222, dport=223) / Raw()
1201 rxs = self.send_and_expect(self.pg1, [data], self.pg0)
1203 self.assertEqual(rxs[0][IPv6].dst, self.pg0.remote_ip6)
1204 self.assertEqual(rxs[0][IPv6].hlim, 19)
1206 self.assertEqual(rxs[0][IP].dst, self.pg0.remote_ip4)
1207 self.assertEqual(rxs[0][IP].ttl, 19)
1208 self.assertTrue(peer_1.query_vpp_config())
1210 # prepare and send a packet that will be rewritten into the wg interface
1211 # expect a data packet sent to the new endpoint
1213 ip_header = IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1215 ip_header = IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1217 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1219 / UDP(sport=555, dport=556)
1222 rxs = self.send_and_expect(self.pg0, [p], self.pg1)
1224 # verify the data packet
1225 peer_1.validate_encapped(rxs, p, is_tunnel_ip6=is_ip6, is_transport_ip6=is_ip6)
1228 r1.remove_vpp_config()
1229 peer_1.remove_vpp_config()
1230 wg0.remove_vpp_config()
1232 def test_wg_peer_roaming_on_data_v4_sync(self):
1233 """Peer roaming on data packet (v4, sync)"""
1234 self._test_wg_peer_roaming_on_data_tmpl(is_async=False, is_ip6=False)
1236 def test_wg_peer_roaming_on_data_v6_sync(self):
1237 """Peer roaming on data packet (v6, sync)"""
1238 self._test_wg_peer_roaming_on_data_tmpl(is_async=False, is_ip6=True)
1240 def test_wg_peer_roaming_on_data_v4_async(self):
1241 """Peer roaming on data packet (v4, async)"""
1242 self._test_wg_peer_roaming_on_data_tmpl(is_async=True, is_ip6=False)
1244 def test_wg_peer_roaming_on_data_v6_async(self):
1245 """Peer roaming on data packet (v6, async)"""
1246 self._test_wg_peer_roaming_on_data_tmpl(is_async=True, is_ip6=True)
1248 def test_wg_peer_resp(self):
1249 """Send handshake response IPv4 tunnel"""
1253 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1257 self.pg_enable_capture(self.pg_interfaces)
1261 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
1263 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1266 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1269 # wait for the peer to send a handshake
1270 rx = self.pg1.get_capture(1, timeout=2)
1272 # consume the handshake in the noise protocol and
1273 # generate the response
1274 resp = peer_1.consume_init(rx[0], self.pg1)
1276 # send the response, get keepalive
1277 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1280 b = peer_1.decrypt_transport(rx)
1281 self.assertEqual(0, len(b))
1283 # send a packets that are routed into the tunnel
1285 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1286 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1287 / UDP(sport=555, dport=556)
1291 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1293 peer_1.validate_encapped(rxs, p)
1295 # send packets into the tunnel, expect to receive them on
1299 peer_1.mk_tunnel_header(self.pg1)
1300 / Wireguard(message_type=4, reserved_zero=0)
1301 / WireguardTransport(
1302 receiver_index=peer_1.sender,
1304 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1306 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1307 / UDP(sport=222, dport=223)
1313 for ii in range(255)
1316 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1319 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1320 self.assertEqual(rx[IP].ttl, 19)
1322 r1.remove_vpp_config()
1323 peer_1.remove_vpp_config()
1324 wg0.remove_vpp_config()
1326 def test_wg_peer_resp_ipv6(self):
1327 """Send handshake response IPv6 tunnel"""
1331 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1335 self.pg_enable_capture(self.pg_interfaces)
1339 self, wg0, self.pg1.remote_ip6, port + 1, ["10.11.3.0/24"]
1341 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1344 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1347 # wait for the peer to send a handshake
1348 rx = self.pg1.get_capture(1, timeout=2)
1350 # consume the handshake in the noise protocol and
1351 # generate the response
1352 resp = peer_1.consume_init(rx[0], self.pg1, is_ip6=True)
1354 # send the response, get keepalive
1355 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1358 b = peer_1.decrypt_transport(rx, True)
1359 self.assertEqual(0, len(b))
1361 # send a packets that are routed into the tunnel
1363 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1364 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1365 / UDP(sport=555, dport=556)
1369 rxs = self.send_and_expect(self.pg0, p * 2, self.pg1)
1370 peer_1.validate_encapped(rxs, p, True)
1372 # send packets into the tunnel, expect to receive them on
1376 peer_1.mk_tunnel_header(self.pg1, True)
1377 / Wireguard(message_type=4, reserved_zero=0)
1378 / WireguardTransport(
1379 receiver_index=peer_1.sender,
1381 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1383 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1384 / UDP(sport=222, dport=223)
1390 for ii in range(255)
1393 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1396 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1397 self.assertEqual(rx[IP].ttl, 19)
1399 r1.remove_vpp_config()
1400 peer_1.remove_vpp_config()
1401 wg0.remove_vpp_config()
1403 def test_wg_peer_v4o4(self):
1409 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1414 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
1416 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1419 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1422 self, "20.22.3.0", 24, [VppRoutePath("20.22.3.1", wg0.sw_if_index)]
1425 # route a packet into the wg interface
1426 # use the allowed-ip prefix
1427 # this is dropped because the peer is not initiated
1429 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1430 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1431 / UDP(sport=555, dport=556)
1434 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1436 self.base_kp4_err + 1, self.statistics.get_err_counter(self.kp4_error)
1439 # route a packet into the wg interface
1440 # use a not allowed-ip prefix
1441 # this is dropped because there is no matching peer
1443 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1444 / IP(src=self.pg0.remote_ip4, dst="20.22.3.2")
1445 / UDP(sport=555, dport=556)
1448 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1450 self.base_peer4_out_err + 1,
1451 self.statistics.get_err_counter(self.peer4_out_err),
1454 # send a handsake from the peer with an invalid MAC
1455 p = peer_1.mk_handshake(self.pg1)
1456 p[WireguardInitiation].mac1 = b"foobar"
1457 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1459 self.base_mac4_err + 1, self.statistics.get_err_counter(self.mac4_error)
1462 # send a handsake from the peer but signed by the wrong key.
1463 p = peer_1.mk_handshake(
1464 self.pg1, False, X25519PrivateKey.generate().public_key()
1466 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1468 self.base_peer4_in_err + 1,
1469 self.statistics.get_err_counter(self.peer4_in_err),
1472 # send a valid handsake init for which we expect a response
1473 p = peer_1.mk_handshake(self.pg1)
1475 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1477 peer_1.consume_response(rx[0])
1479 # route a packet into the wg interface
1480 # this is dropped because the peer is still not initiated
1482 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1483 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1484 / UDP(sport=555, dport=556)
1487 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1489 self.base_kp4_err + 2, self.statistics.get_err_counter(self.kp4_error)
1492 # send a data packet from the peer through the tunnel
1493 # this completes the handshake
1495 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1496 / UDP(sport=222, dport=223)
1499 d = peer_1.encrypt_transport(p)
1500 p = peer_1.mk_tunnel_header(self.pg1) / (
1501 Wireguard(message_type=4, reserved_zero=0)
1502 / WireguardTransport(
1503 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1506 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1509 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1510 self.assertEqual(rx[IP].ttl, 19)
1512 # send a packets that are routed into the tunnel
1514 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1515 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1516 / UDP(sport=555, dport=556)
1520 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1523 rx = IP(peer_1.decrypt_transport(rx))
1525 # check the original packet is present
1526 self.assertEqual(rx[IP].dst, p[IP].dst)
1527 self.assertEqual(rx[IP].ttl, p[IP].ttl - 1)
1529 # send packets into the tunnel, expect to receive them on
1533 peer_1.mk_tunnel_header(self.pg1)
1534 / Wireguard(message_type=4, reserved_zero=0)
1535 / WireguardTransport(
1536 receiver_index=peer_1.sender,
1538 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1540 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1541 / UDP(sport=222, dport=223)
1547 for ii in range(255)
1550 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1553 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1554 self.assertEqual(rx[IP].ttl, 19)
1556 r1.remove_vpp_config()
1557 r2.remove_vpp_config()
1558 peer_1.remove_vpp_config()
1559 wg0.remove_vpp_config()
1561 def test_wg_peer_v6o6(self):
1567 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1572 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
1574 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1577 self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
1580 self, "22::3:0", 112, [VppRoutePath("22::3:1", wg0.sw_if_index)]
1583 # route a packet into the wg interface
1584 # use the allowed-ip prefix
1585 # this is dropped because the peer is not initiated
1588 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1589 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1590 / UDP(sport=555, dport=556)
1593 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1596 self.base_kp6_err + 1, self.statistics.get_err_counter(self.kp6_error)
1599 # route a packet into the wg interface
1600 # use a not allowed-ip prefix
1601 # this is dropped because there is no matching peer
1603 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1604 / IPv6(src=self.pg0.remote_ip6, dst="22::3:2")
1605 / UDP(sport=555, dport=556)
1608 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1610 self.base_peer6_out_err + 1,
1611 self.statistics.get_err_counter(self.peer6_out_err),
1614 # send a handsake from the peer with an invalid MAC
1615 p = peer_1.mk_handshake(self.pg1, True)
1616 p[WireguardInitiation].mac1 = b"foobar"
1617 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1620 self.base_mac6_err + 1, self.statistics.get_err_counter(self.mac6_error)
1623 # send a handsake from the peer but signed by the wrong key.
1624 p = peer_1.mk_handshake(
1625 self.pg1, True, X25519PrivateKey.generate().public_key()
1627 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1629 self.base_peer6_in_err + 1,
1630 self.statistics.get_err_counter(self.peer6_in_err),
1633 # send a valid handsake init for which we expect a response
1634 p = peer_1.mk_handshake(self.pg1, True)
1636 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1638 peer_1.consume_response(rx[0], True)
1640 # route a packet into the wg interface
1641 # this is dropped because the peer is still not initiated
1643 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1644 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1645 / UDP(sport=555, dport=556)
1648 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1650 self.base_kp6_err + 2, self.statistics.get_err_counter(self.kp6_error)
1653 # send a data packet from the peer through the tunnel
1654 # this completes the handshake
1656 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1657 / UDP(sport=222, dport=223)
1660 d = peer_1.encrypt_transport(p)
1661 p = peer_1.mk_tunnel_header(self.pg1, True) / (
1662 Wireguard(message_type=4, reserved_zero=0)
1663 / WireguardTransport(
1664 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1667 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1670 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1671 self.assertEqual(rx[IPv6].hlim, 19)
1673 # send a packets that are routed into the tunnel
1675 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1676 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1677 / UDP(sport=555, dport=556)
1681 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1684 rx = IPv6(peer_1.decrypt_transport(rx, True))
1686 # check the original packet is present
1687 self.assertEqual(rx[IPv6].dst, p[IPv6].dst)
1688 self.assertEqual(rx[IPv6].hlim, p[IPv6].hlim - 1)
1690 # send packets into the tunnel, expect to receive them on
1694 peer_1.mk_tunnel_header(self.pg1, True)
1695 / Wireguard(message_type=4, reserved_zero=0)
1696 / WireguardTransport(
1697 receiver_index=peer_1.sender,
1699 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1701 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1702 / UDP(sport=222, dport=223)
1708 for ii in range(255)
1711 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1714 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1715 self.assertEqual(rx[IPv6].hlim, 19)
1717 r1.remove_vpp_config()
1718 r2.remove_vpp_config()
1719 peer_1.remove_vpp_config()
1720 wg0.remove_vpp_config()
1722 def test_wg_peer_v6o4(self):
1728 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1733 self, wg0, self.pg1.remote_ip4, port + 1, ["1::3:0/112"]
1735 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1738 self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
1741 # route a packet into the wg interface
1742 # use the allowed-ip prefix
1743 # this is dropped because the peer is not initiated
1745 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1746 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1747 / UDP(sport=555, dport=556)
1750 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1752 self.base_kp6_err + 1, self.statistics.get_err_counter(self.kp6_error)
1755 # send a handsake from the peer with an invalid MAC
1756 p = peer_1.mk_handshake(self.pg1)
1757 p[WireguardInitiation].mac1 = b"foobar"
1758 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1761 self.base_mac4_err + 1, self.statistics.get_err_counter(self.mac4_error)
1764 # send a handsake from the peer but signed by the wrong key.
1765 p = peer_1.mk_handshake(
1766 self.pg1, False, X25519PrivateKey.generate().public_key()
1768 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1770 self.base_peer4_in_err + 1,
1771 self.statistics.get_err_counter(self.peer4_in_err),
1774 # send a valid handsake init for which we expect a response
1775 p = peer_1.mk_handshake(self.pg1)
1777 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1779 peer_1.consume_response(rx[0])
1781 # route a packet into the wg interface
1782 # this is dropped because the peer is still not initiated
1784 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1785 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1786 / UDP(sport=555, dport=556)
1789 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1791 self.base_kp6_err + 2, self.statistics.get_err_counter(self.kp6_error)
1794 # send a data packet from the peer through the tunnel
1795 # this completes the handshake
1797 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1798 / UDP(sport=222, dport=223)
1801 d = peer_1.encrypt_transport(p)
1802 p = peer_1.mk_tunnel_header(self.pg1) / (
1803 Wireguard(message_type=4, reserved_zero=0)
1804 / WireguardTransport(
1805 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1808 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1811 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1812 self.assertEqual(rx[IPv6].hlim, 19)
1814 # send a packets that are routed into the tunnel
1816 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1817 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1818 / UDP(sport=555, dport=556)
1822 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1825 rx = IPv6(peer_1.decrypt_transport(rx))
1827 # check the original packet is present
1828 self.assertEqual(rx[IPv6].dst, p[IPv6].dst)
1829 self.assertEqual(rx[IPv6].hlim, p[IPv6].hlim - 1)
1831 # send packets into the tunnel, expect to receive them on
1835 peer_1.mk_tunnel_header(self.pg1)
1836 / Wireguard(message_type=4, reserved_zero=0)
1837 / WireguardTransport(
1838 receiver_index=peer_1.sender,
1840 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1842 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1843 / UDP(sport=222, dport=223)
1849 for ii in range(255)
1852 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1855 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1856 self.assertEqual(rx[IPv6].hlim, 19)
1858 r1.remove_vpp_config()
1859 peer_1.remove_vpp_config()
1860 wg0.remove_vpp_config()
1862 def test_wg_peer_v4o6(self):
1868 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1873 self, wg0, self.pg1.remote_ip6, port + 1, ["10.11.3.0/24"]
1875 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1878 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1881 # route a packet into the wg interface
1882 # use the allowed-ip prefix
1883 # this is dropped because the peer is not initiated
1885 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1886 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1887 / UDP(sport=555, dport=556)
1890 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1892 self.base_kp4_err + 1, self.statistics.get_err_counter(self.kp4_error)
1895 # send a handsake from the peer with an invalid MAC
1896 p = peer_1.mk_handshake(self.pg1, True)
1897 p[WireguardInitiation].mac1 = b"foobar"
1898 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1900 self.base_mac6_err + 1, self.statistics.get_err_counter(self.mac6_error)
1903 # send a handsake from the peer but signed by the wrong key.
1904 p = peer_1.mk_handshake(
1905 self.pg1, True, X25519PrivateKey.generate().public_key()
1907 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1909 self.base_peer6_in_err + 1,
1910 self.statistics.get_err_counter(self.peer6_in_err),
1913 # send a valid handsake init for which we expect a response
1914 p = peer_1.mk_handshake(self.pg1, True)
1916 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1918 peer_1.consume_response(rx[0], True)
1920 # route a packet into the wg interface
1921 # this is dropped because the peer is still not initiated
1923 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1924 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1925 / UDP(sport=555, dport=556)
1928 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1930 self.base_kp4_err + 2, self.statistics.get_err_counter(self.kp4_error)
1933 # send a data packet from the peer through the tunnel
1934 # this completes the handshake
1936 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1937 / UDP(sport=222, dport=223)
1940 d = peer_1.encrypt_transport(p)
1941 p = peer_1.mk_tunnel_header(self.pg1, True) / (
1942 Wireguard(message_type=4, reserved_zero=0)
1943 / WireguardTransport(
1944 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1947 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1950 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1951 self.assertEqual(rx[IP].ttl, 19)
1953 # send a packets that are routed into the tunnel
1955 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1956 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1957 / UDP(sport=555, dport=556)
1961 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1964 rx = IP(peer_1.decrypt_transport(rx, True))
1966 # check the original packet is present
1967 self.assertEqual(rx[IP].dst, p[IP].dst)
1968 self.assertEqual(rx[IP].ttl, p[IP].ttl - 1)
1970 # send packets into the tunnel, expect to receive them on
1974 peer_1.mk_tunnel_header(self.pg1, True)
1975 / Wireguard(message_type=4, reserved_zero=0)
1976 / WireguardTransport(
1977 receiver_index=peer_1.sender,
1979 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1981 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1982 / UDP(sport=222, dport=223)
1988 for ii in range(255)
1991 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1994 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1995 self.assertEqual(rx[IP].ttl, 19)
1997 r1.remove_vpp_config()
1998 peer_1.remove_vpp_config()
1999 wg0.remove_vpp_config()
2001 def test_wg_multi_peer(self):
2002 """multiple peer setup"""
2006 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2007 wg1 = VppWgInterface(self, self.pg2.local_ip4, port + 1).add_vpp_config()
2011 # Check peer counter
2012 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 0)
2014 self.pg_enable_capture(self.pg_interfaces)
2017 # Create many peers on sencond interface
2019 self.pg2.generate_remote_hosts(NUM_PEERS)
2020 self.pg2.configure_ipv4_neighbors()
2021 self.pg1.generate_remote_hosts(NUM_PEERS)
2022 self.pg1.configure_ipv4_neighbors()
2028 for i in range(NUM_PEERS):
2033 self.pg1.remote_hosts[i].ip4,
2035 ["10.0.%d.4/32" % i],
2043 [VppRoutePath(self.pg1.remote_hosts[i].ip4, wg0.sw_if_index)],
2051 self.pg2.remote_hosts[i].ip4,
2053 ["10.100.%d.4/32" % i],
2061 [VppRoutePath(self.pg2.remote_hosts[i].ip4, wg1.sw_if_index)],
2065 self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_PEERS * 2)
2067 self.logger.info(self.vapi.cli("show wireguard peer"))
2068 self.logger.info(self.vapi.cli("show wireguard interface"))
2069 self.logger.info(self.vapi.cli("show adj 37"))
2070 self.logger.info(self.vapi.cli("sh ip fib 172.16.3.17"))
2071 self.logger.info(self.vapi.cli("sh ip fib 10.11.3.0"))
2075 r.remove_vpp_config()
2077 r.remove_vpp_config()
2081 self.assertTrue(p.query_vpp_config())
2082 p.remove_vpp_config()
2084 self.assertTrue(p.query_vpp_config())
2085 p.remove_vpp_config()
2087 wg0.remove_vpp_config()
2088 wg1.remove_vpp_config()
2090 def test_wg_multi_interface(self):
2091 """Multi-tunnel on the same port"""
2094 # Create many wireguard interfaces
2096 self.pg1.generate_remote_hosts(NUM_IFS)
2097 self.pg1.configure_ipv4_neighbors()
2098 self.pg0.generate_remote_hosts(NUM_IFS)
2099 self.pg0.configure_ipv4_neighbors()
2101 # Create interfaces with a peer on each
2105 for i in range(NUM_IFS):
2106 # Use the same port for each interface
2107 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2115 self.pg1.remote_hosts[i].ip4,
2117 ["10.0.%d.0/24" % i],
2126 [VppRoutePath("10.0.%d.4" % i, wg0.sw_if_index)],
2130 self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_IFS)
2132 for i in range(NUM_IFS):
2133 # send a valid handsake init for which we expect a response
2134 p = peers[i].mk_handshake(self.pg1)
2135 rx = self.send_and_expect(self.pg1, [p], self.pg1)
2136 peers[i].consume_response(rx[0])
2138 # send a data packet from the peer through the tunnel
2139 # this completes the handshake
2141 IP(src="10.0.%d.4" % i, dst=self.pg0.remote_hosts[i].ip4, ttl=20)
2142 / UDP(sport=222, dport=223)
2145 d = peers[i].encrypt_transport(p)
2146 p = peers[i].mk_tunnel_header(self.pg1) / (
2147 Wireguard(message_type=4, reserved_zero=0)
2148 / WireguardTransport(
2149 receiver_index=peers[i].sender,
2151 encrypted_encapsulated_packet=d,
2154 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
2156 self.assertEqual(rx[IP].dst, self.pg0.remote_hosts[i].ip4)
2157 self.assertEqual(rx[IP].ttl, 19)
2159 # send a packets that are routed into the tunnel
2160 for i in range(NUM_IFS):
2162 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2163 / IP(src=self.pg0.remote_hosts[i].ip4, dst="10.0.%d.4" % i)
2164 / UDP(sport=555, dport=556)
2168 rxs = self.send_and_expect(self.pg0, p * 64, self.pg1)
2171 rx = IP(peers[i].decrypt_transport(rx))
2173 # check the oringial packet is present
2174 self.assertEqual(rx[IP].dst, p[IP].dst)
2175 self.assertEqual(rx[IP].ttl, p[IP].ttl - 1)
2177 # send packets into the tunnel
2178 for i in range(NUM_IFS):
2181 peers[i].mk_tunnel_header(self.pg1)
2182 / Wireguard(message_type=4, reserved_zero=0)
2183 / WireguardTransport(
2184 receiver_index=peers[i].sender,
2186 encrypted_encapsulated_packet=peers[i].encrypt_transport(
2189 src="10.0.%d.4" % i,
2190 dst=self.pg0.remote_hosts[i].ip4,
2193 / UDP(sport=222, dport=223)
2202 rxs = self.send_and_expect(self.pg1, p, self.pg0)
2205 self.assertEqual(rx[IP].dst, self.pg0.remote_hosts[i].ip4)
2206 self.assertEqual(rx[IP].ttl, 19)
2209 r.remove_vpp_config()
2211 p.remove_vpp_config()
2213 i.remove_vpp_config()
2215 def test_wg_event(self):
2218 ESTABLISHED_FLAG = (
2219 VppEnum.vl_api_wireguard_peer_flags_t.WIREGUARD_PEER_ESTABLISHED
2221 DEAD_FLAG = VppEnum.vl_api_wireguard_peer_flags_t.WIREGUARD_PEER_STATUS_DEAD
2224 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2225 wg1 = VppWgInterface(self, self.pg2.local_ip4, port + 1).add_vpp_config()
2229 # Check peer counter
2230 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 0)
2232 self.pg_enable_capture(self.pg_interfaces)
2237 self.pg2.generate_remote_hosts(NUM_PEERS)
2238 self.pg2.configure_ipv4_neighbors()
2239 self.pg1.generate_remote_hosts(NUM_PEERS)
2240 self.pg1.configure_ipv4_neighbors()
2246 for i in range(NUM_PEERS):
2251 self.pg1.remote_hosts[i].ip4,
2253 ["10.0.%d.4/32" % i],
2261 [VppRoutePath(self.pg1.remote_hosts[i].ip4, wg0.sw_if_index)],
2269 self.pg2.remote_hosts[i].ip4,
2271 ["10.100.%d.4/32" % i],
2279 [VppRoutePath(self.pg2.remote_hosts[i].ip4, wg1.sw_if_index)],
2283 self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_PEERS * 2)
2285 # Want events from the first perr of wg0
2286 # and from all wg1 peers
2287 peers_0[0].want_events()
2290 for i in range(NUM_PEERS):
2291 # send a valid handsake init for which we expect a response
2292 p = peers_0[i].mk_handshake(self.pg1)
2293 rx = self.send_and_expect(self.pg1, [p], self.pg1)
2294 peers_0[i].consume_response(rx[0])
2296 peers_0[0].wait_event(ESTABLISHED_FLAG)
2298 p = peers_1[i].mk_handshake(self.pg2)
2299 rx = self.send_and_expect(self.pg2, [p], self.pg2)
2300 peers_1[i].consume_response(rx[0])
2302 wg1.wait_events(ESTABLISHED_FLAG, [peers_1[0].index, peers_1[1].index])
2306 r.remove_vpp_config()
2308 r.remove_vpp_config()
2311 for i in range(NUM_PEERS):
2312 self.assertTrue(peers_0[i].query_vpp_config())
2313 peers_0[i].remove_vpp_config()
2315 peers_0[i].wait_event(0)
2316 peers_0[i].wait_event(DEAD_FLAG)
2318 self.assertTrue(p.query_vpp_config())
2319 p.remove_vpp_config()
2321 p.wait_event(DEAD_FLAG)
2323 wg0.remove_vpp_config()
2324 wg1.remove_vpp_config()
2326 def test_wg_sending_handshake_when_admin_down(self):
2327 """Sending handshake when admin down"""
2330 # create wg interface
2331 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2336 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
2338 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2340 self.pg_enable_capture(self.pg_interfaces)
2343 # wait for the peer to send a handshake initiation
2344 # expect no handshakes
2346 self.pg1.assert_nothing_captured(remark="handshake packet(s) sent")
2348 self.pg_enable_capture(self.pg_interfaces)
2351 # administratively enable the wg interface
2352 # expect the peer to send a handshake initiation
2354 rxs = self.pg1.get_capture(1, timeout=2)
2355 peer_1.consume_init(rxs[0], self.pg1)
2357 self.pg_enable_capture(self.pg_interfaces)
2360 # administratively disable the wg interface
2361 # expect no handshakes
2364 self.pg1.assert_nothing_captured(remark="handshake packet(s) sent")
2367 peer_1.remove_vpp_config()
2368 wg0.remove_vpp_config()
2370 def test_wg_sending_data_when_admin_down(self):
2371 """Sending data when admin down"""
2374 # create wg interface
2375 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2379 self.pg_enable_capture(self.pg_interfaces)
2384 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
2386 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2388 # create a route to rewrite traffic into the wg interface
2390 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
2393 # wait for the peer to send a handshake initiation
2394 rxs = self.pg1.get_capture(1, timeout=2)
2396 # prepare and send a handshake response
2397 # expect a keepalive message
2398 resp = peer_1.consume_init(rxs[0], self.pg1)
2399 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
2401 # verify the keepalive message
2402 b = peer_1.decrypt_transport(rxs[0])
2403 self.assertEqual(0, len(b))
2405 # prepare and send a packet that will be rewritten into the wg interface
2406 # expect a data packet sent
2408 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2409 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
2410 / UDP(sport=555, dport=556)
2413 rxs = self.send_and_expect(self.pg0, [p], self.pg1)
2415 # verify the data packet
2416 peer_1.validate_encapped(rxs, p)
2418 # administratively disable the wg interface
2421 # send a packet that will be rewritten into the wg interface
2422 # expect no data packets sent
2423 self.send_and_assert_no_replies(self.pg0, [p])
2425 # administratively enable the wg interface
2426 # expect the peer to send a handshake initiation
2428 peer_1.noise_reset()
2429 rxs = self.pg1.get_capture(1, timeout=2)
2430 resp = peer_1.consume_init(rxs[0], self.pg1)
2432 # send a packet that will be rewritten into the wg interface
2433 # expect no data packets sent because the peer is not initiated
2434 self.send_and_assert_no_replies(self.pg0, [p])
2436 self.base_kp4_err + 1, self.statistics.get_err_counter(self.kp4_error)
2439 # send a handshake response and expect a keepalive message
2440 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
2442 # verify the keepalive message
2443 b = peer_1.decrypt_transport(rxs[0])
2444 self.assertEqual(0, len(b))
2446 # send a packet that will be rewritten into the wg interface
2447 # expect a data packet sent
2448 rxs = self.send_and_expect(self.pg0, [p], self.pg1)
2450 # verify the data packet
2451 peer_1.validate_encapped(rxs, p)
2454 r1.remove_vpp_config()
2455 peer_1.remove_vpp_config()
2456 wg0.remove_vpp_config()
2459 @tag_fixme_vpp_debug
2460 class WireguardHandoffTests(TestWg):
2461 """Wireguard Tests in multi worker setup"""
2463 vpp_worker_count = 2
2465 def test_wg_peer_init(self):
2471 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2476 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.2.0/24", "10.11.3.0/24"]
2478 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2481 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
2484 # send a valid handsake init for which we expect a response
2485 p = peer_1.mk_handshake(self.pg1)
2487 rx = self.send_and_expect(self.pg1, [p], self.pg1)
2489 peer_1.consume_response(rx[0])
2491 # send a data packet from the peer through the tunnel
2492 # this completes the handshake and pins the peer to worker 0
2494 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
2495 / UDP(sport=222, dport=223)
2498 d = peer_1.encrypt_transport(p)
2499 p = peer_1.mk_tunnel_header(self.pg1) / (
2500 Wireguard(message_type=4, reserved_zero=0)
2501 / WireguardTransport(
2502 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
2505 rxs = self.send_and_expect(self.pg1, [p], self.pg0, worker=0)
2508 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
2509 self.assertEqual(rx[IP].ttl, 19)
2511 # send a packets that are routed into the tunnel
2512 # and pins the peer tp worker 1
2514 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2515 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
2516 / UDP(sport=555, dport=556)
2519 rxs = self.send_and_expect(self.pg0, pe * 255, self.pg1, worker=1)
2520 peer_1.validate_encapped(rxs, pe)
2522 # send packets into the tunnel, from the other worker
2525 peer_1.mk_tunnel_header(self.pg1)
2526 / Wireguard(message_type=4, reserved_zero=0)
2527 / WireguardTransport(
2528 receiver_index=peer_1.sender,
2530 encrypted_encapsulated_packet=peer_1.encrypt_transport(
2532 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
2533 / UDP(sport=222, dport=223)
2539 for ii in range(255)
2542 rxs = self.send_and_expect(self.pg1, p, self.pg0, worker=1)
2545 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
2546 self.assertEqual(rx[IP].ttl, 19)
2548 # send a packets that are routed into the tunnel
2550 rxs = self.send_and_expect(self.pg0, pe * 255, self.pg1, worker=0)
2552 peer_1.validate_encapped(rxs, pe)
2554 r1.remove_vpp_config()
2555 peer_1.remove_vpp_config()
2556 wg0.remove_vpp_config()
2558 @unittest.skip("test disabled")
2559 def test_wg_multi_interface(self):
2560 """Multi-tunnel on the same port"""
2563 class TestWgFIB(VppTestCase):
2564 """Wireguard FIB Test Case"""
2567 def setUpClass(cls):
2568 super(TestWgFIB, cls).setUpClass()
2571 def tearDownClass(cls):
2572 super(TestWgFIB, cls).tearDownClass()
2575 super(TestWgFIB, self).setUp()
2577 self.create_pg_interfaces(range(2))
2579 for i in self.pg_interfaces:
2584 for i in self.pg_interfaces:
2587 super(TestWgFIB, self).tearDown()
2589 def test_wg_fib_tracking(self):
2593 # create wg interface
2594 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2598 self.pg_enable_capture(self.pg_interfaces)
2603 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
2605 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2607 # create a route to rewrite traffic into the wg interface
2609 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
2612 # resolve ARP and expect the adjacency to update
2613 self.pg1.resolve_arp()
2615 # wait for the peer to send a handshake initiation
2616 rxs = self.pg1.get_capture(2, timeout=6)
2618 # prepare and send a handshake response
2619 # expect a keepalive message
2620 resp = peer_1.consume_init(rxs[1], self.pg1)
2621 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
2623 # verify the keepalive message
2624 b = peer_1.decrypt_transport(rxs[0])
2625 self.assertEqual(0, len(b))
2627 # prepare and send a packet that will be rewritten into the wg interface
2628 # expect a data packet sent
2630 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2631 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
2632 / UDP(sport=555, dport=556)
2635 rxs = self.send_and_expect(self.pg0, [p], self.pg1)
2637 # verify the data packet
2638 peer_1.validate_encapped(rxs, p)
2641 r1.remove_vpp_config()
2642 peer_1.remove_vpp_config()
2643 wg0.remove_vpp_config()