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.layers.vxlan import VXLAN
15 from scapy.contrib.wireguard import (
22 from cryptography.hazmat.primitives.asymmetric.x25519 import (
26 from cryptography.hazmat.primitives.serialization import (
32 from cryptography.hazmat.primitives.hashes import BLAKE2s, Hash
33 from cryptography.hazmat.primitives.hmac import HMAC
34 from cryptography.hazmat.backends import default_backend
35 from noise.connection import NoiseConnection, Keypair
37 from Crypto.Cipher import ChaCha20_Poly1305
38 from Crypto.Random import get_random_bytes
40 from vpp_ipip_tun_interface import VppIpIpTunInterface
41 from vpp_interface import VppInterface
42 from vpp_pg_interface import is_ipv6_misc
43 from vpp_ip_route import VppIpRoute, VppRoutePath
44 from vpp_l2 import VppBridgeDomain, VppBridgeDomainPort
45 from vpp_vxlan_tunnel import VppVxlanTunnel
46 from vpp_object import VppObject
47 from vpp_papi import VppEnum
48 from framework import is_distro_ubuntu2204, is_distro_debian11, tag_fixme_vpp_debug
49 from framework import VppTestCase
50 from re import compile
53 """ TestWg is a subclass of VPPTestCase classes.
60 def private_key_bytes(k):
61 return k.private_bytes(Encoding.Raw, PrivateFormat.Raw, NoEncryption())
64 def public_key_bytes(k):
65 return k.public_bytes(Encoding.Raw, PublicFormat.Raw)
68 def get_field_bytes(pkt, name):
69 fld, val = pkt.getfield_and_val(name)
70 return fld.i2m(pkt, val)
73 class VppWgInterface(VppInterface):
75 VPP WireGuard interface
78 def __init__(self, test, src, port):
79 super(VppWgInterface, self).__init__(test)
83 self.private_key = X25519PrivateKey.generate()
84 self.public_key = self.private_key.public_key()
86 # cookie related params
87 self.cookie_key = blake2s(b"cookie--" + self.public_key_bytes()).digest()
89 def public_key_bytes(self):
90 return public_key_bytes(self.public_key)
92 def private_key_bytes(self):
93 return private_key_bytes(self.private_key)
95 def add_vpp_config(self):
96 r = self.test.vapi.wireguard_interface_create(
98 "user_instance": 0xFFFFFFFF,
101 "private_key": private_key_bytes(self.private_key),
102 "generate_key": False,
105 self.set_sw_if_index(r.sw_if_index)
106 self.test.registry.register(self, self.test.logger)
109 def remove_vpp_config(self):
110 self.test.vapi.wireguard_interface_delete(sw_if_index=self._sw_if_index)
112 def query_vpp_config(self):
113 ts = self.test.vapi.wireguard_interface_dump(sw_if_index=0xFFFFFFFF)
116 t.interface.sw_if_index == self._sw_if_index
117 and str(t.interface.src_ip) == self.src
118 and t.interface.port == self.port
119 and t.interface.private_key == private_key_bytes(self.private_key)
124 def want_events(self, peer_index=0xFFFFFFFF):
125 self.test.vapi.want_wireguard_peer_events(
128 sw_if_index=self._sw_if_index,
129 peer_index=peer_index,
132 def wait_events(self, expect, peers, timeout=5):
133 for i in range(len(peers)):
134 rv = self.test.vapi.wait_for_event(timeout, "wireguard_peer_event")
135 self.test.assertEqual(rv.peer_index, peers[i])
136 self.test.assertEqual(rv.flags, expect)
139 return self.object_id()
142 return "wireguard-%d" % self._sw_if_index
145 NOISE_HANDSHAKE_NAME = b"Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s"
146 NOISE_IDENTIFIER_NAME = b"WireGuard v1 zx2c4 Jason@zx2c4.com"
148 HANDSHAKE_COUNTING_INTERVAL = 0.5
149 UNDER_LOAD_INTERVAL = 1.0
150 HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD = 40
151 HANDSHAKE_NUM_BEFORE_RATELIMITING = 5
153 HANDSHAKE_JITTER = 0.5
156 class VppWgPeer(VppObject):
157 def __init__(self, test, itf, endpoint, port, allowed_ips, persistent_keepalive=15):
160 self.endpoint = endpoint
162 self.allowed_ips = allowed_ips
163 self.persistent_keepalive = persistent_keepalive
165 # remote peer's public
166 self.private_key = X25519PrivateKey.generate()
167 self.public_key = self.private_key.public_key()
169 # cookie related params
170 self.cookie_key = blake2s(b"cookie--" + self.public_key_bytes()).digest()
171 self.last_sent_cookie = None
172 self.last_mac1 = None
173 self.last_received_cookie = None
175 self.noise = NoiseConnection.from_name(NOISE_HANDSHAKE_NAME)
177 def change_endpoint(self, endpoint, port):
178 self.endpoint = endpoint
181 def add_vpp_config(self):
182 rv = self._test.vapi.wireguard_peer_add(
184 "public_key": self.public_key_bytes(),
186 "endpoint": self.endpoint,
187 "n_allowed_ips": len(self.allowed_ips),
188 "allowed_ips": self.allowed_ips,
189 "sw_if_index": self.itf.sw_if_index,
190 "persistent_keepalive": self.persistent_keepalive,
193 self.index = rv.peer_index
194 self.receiver_index = self.index + 1
195 self._test.registry.register(self, self._test.logger)
198 def remove_vpp_config(self):
199 self._test.vapi.wireguard_peer_remove(peer_index=self.index)
202 return "wireguard-peer-%s" % self.index
204 def public_key_bytes(self):
205 return public_key_bytes(self.public_key)
207 def query_vpp_config(self):
208 peers = self._test.vapi.wireguard_peers_dump()
211 # "::" endpoint will be returned as "0.0.0.0" in peer's details
212 endpoint = "0.0.0.0" if self.endpoint == "::" else self.endpoint
214 p.peer.public_key == self.public_key_bytes()
215 and p.peer.port == self.port
216 and str(p.peer.endpoint) == endpoint
217 and p.peer.sw_if_index == self.itf.sw_if_index
218 and len(self.allowed_ips) == p.peer.n_allowed_ips
220 self.allowed_ips.sort()
221 p.peer.allowed_ips.sort()
223 for a1, a2 in zip(self.allowed_ips, p.peer.allowed_ips):
224 if str(a1) != str(a2):
229 def mk_tunnel_header(self, tx_itf, is_ip6=False):
232 Ether(dst=tx_itf.local_mac, src=tx_itf.remote_mac)
233 / IP(src=self.endpoint, dst=self.itf.src)
234 / UDP(sport=self.port, dport=self.itf.port)
238 Ether(dst=tx_itf.local_mac, src=tx_itf.remote_mac)
239 / IPv6(src=self.endpoint, dst=self.itf.src)
240 / UDP(sport=self.port, dport=self.itf.port)
243 def noise_reset(self):
244 self.noise = NoiseConnection.from_name(NOISE_HANDSHAKE_NAME)
246 def noise_init(self, public_key=None):
247 self.noise.set_prologue(NOISE_IDENTIFIER_NAME)
248 self.noise.set_psks(psk=bytes(bytearray(32)))
251 public_key = self.itf.public_key
254 self.noise.set_keypair_from_private_bytes(
255 Keypair.STATIC, private_key_bytes(self.private_key)
258 self.noise.set_keypair_from_public_bytes(
259 Keypair.REMOTE_STATIC, public_key_bytes(public_key)
262 self.noise.start_handshake()
264 def mk_cookie(self, p, tx_itf, is_resp=False, is_ip6=False):
265 self.verify_header(p, is_ip6)
267 wg_pkt = Wireguard(p[Raw])
270 self._test.assertEqual(wg_pkt[Wireguard].message_type, 2)
271 self._test.assertEqual(wg_pkt[Wireguard].reserved_zero, 0)
272 self._test.assertEqual(wg_pkt[WireguardResponse].mac2, bytes([0] * 16))
274 self._test.assertEqual(wg_pkt[Wireguard].message_type, 1)
275 self._test.assertEqual(wg_pkt[Wireguard].reserved_zero, 0)
276 self._test.assertEqual(wg_pkt[WireguardInitiation].mac2, bytes([0] * 16))
278 # collect info from wg packet (initiation or response)
279 src = get_field_bytes(p[IPv6 if is_ip6 else IP], "src")
280 sport = p[UDP].sport.to_bytes(2, byteorder="big")
282 mac1 = wg_pkt[WireguardResponse].mac1
283 sender_index = wg_pkt[WireguardResponse].sender_index
285 mac1 = wg_pkt[WireguardInitiation].mac1
286 sender_index = wg_pkt[WireguardInitiation].sender_index
289 cookie_reply = Wireguard() / WireguardCookieReply()
290 cookie_reply[Wireguard].message_type = 3
291 cookie_reply[Wireguard].reserved_zero = 0
292 cookie_reply[WireguardCookieReply].receiver_index = sender_index
293 nonce = get_random_bytes(24)
294 cookie_reply[WireguardCookieReply].nonce = nonce
296 # generate cookie data
297 changing_secret = get_random_bytes(32)
298 self.last_sent_cookie = blake2s(
299 src + sport, digest_size=16, key=changing_secret
302 # encrypt cookie data
303 cipher = ChaCha20_Poly1305.new(key=self.cookie_key, nonce=nonce)
305 ciphertext, tag = cipher.encrypt_and_digest(self.last_sent_cookie)
306 cookie_reply[WireguardCookieReply].encrypted_cookie = ciphertext + tag
308 # prepare cookie reply to be sent
309 cookie_reply = self.mk_tunnel_header(tx_itf, is_ip6) / cookie_reply
313 def consume_cookie(self, p, is_ip6=False):
314 self.verify_header(p, is_ip6)
316 cookie_reply = Wireguard(p[Raw])
318 self._test.assertEqual(cookie_reply[Wireguard].message_type, 3)
319 self._test.assertEqual(cookie_reply[Wireguard].reserved_zero, 0)
320 self._test.assertEqual(
321 cookie_reply[WireguardCookieReply].receiver_index, self.receiver_index
324 # collect info from cookie reply
325 nonce = cookie_reply[WireguardCookieReply].nonce
326 encrypted_cookie = cookie_reply[WireguardCookieReply].encrypted_cookie
327 ciphertext, tag = encrypted_cookie[:16], encrypted_cookie[16:]
329 # decrypt cookie data
330 cipher = ChaCha20_Poly1305.new(key=self.itf.cookie_key, nonce=nonce)
331 cipher.update(self.last_mac1)
332 self.last_received_cookie = cipher.decrypt_and_verify(ciphertext, tag)
334 def mk_handshake(self, tx_itf, is_ip6=False, public_key=None):
335 self.noise.set_as_initiator()
336 self.noise_init(public_key)
338 p = Wireguard() / WireguardInitiation()
340 p[Wireguard].message_type = 1
341 p[Wireguard].reserved_zero = 0
342 p[WireguardInitiation].sender_index = self.receiver_index
344 # some random data for the message
345 # lifted from the noise protocol's wireguard example
346 now = datetime.datetime.now()
349 4611686018427387914 + int(now.timestamp()),
350 int(now.microsecond * 1e3),
352 b = self.noise.write_message(payload=tai)
354 # load noise into init message
355 p[WireguardInitiation].unencrypted_ephemeral = b[0:32]
356 p[WireguardInitiation].encrypted_static = b[32:80]
357 p[WireguardInitiation].encrypted_timestamp = b[80:108]
359 # generate the mac1 hash
360 mac_key = blake2s(b"mac1----" + self.itf.public_key_bytes()).digest()
361 mac1 = blake2s(bytes(p)[0:116], digest_size=16, key=mac_key).digest()
362 p[WireguardInitiation].mac1 = mac1
363 self.last_mac1 = mac1
365 # generate the mac2 hash
366 if self.last_received_cookie:
368 bytes(p)[0:132], digest_size=16, key=self.last_received_cookie
370 p[WireguardInitiation].mac2 = mac2
371 self.last_received_cookie = None
373 p[WireguardInitiation].mac2 = bytearray(16)
375 p = self.mk_tunnel_header(tx_itf, is_ip6) / p
379 def verify_header(self, p, is_ip6=False):
381 self._test.assertEqual(p[IP].src, self.itf.src)
382 self._test.assertEqual(p[IP].dst, self.endpoint)
383 self._test.assert_packet_checksums_valid(p)
385 self._test.assertEqual(p[IPv6].src, self.itf.src)
386 self._test.assertEqual(p[IPv6].dst, self.endpoint)
387 self._test.assert_packet_checksums_valid(p, False)
388 self._test.assertEqual(p[UDP].sport, self.itf.port)
389 self._test.assertEqual(p[UDP].dport, self.port)
391 def consume_init(self, p, tx_itf, is_ip6=False, is_mac2=False):
392 self.noise.set_as_responder()
393 self.noise_init(self.itf.public_key)
394 self.verify_header(p, is_ip6)
396 init = Wireguard(p[Raw])
398 self._test.assertEqual(init[Wireguard].message_type, 1)
399 self._test.assertEqual(init[Wireguard].reserved_zero, 0)
401 self.sender = init[WireguardInitiation].sender_index
403 # validate the mac1 hash
404 mac_key = blake2s(b"mac1----" + public_key_bytes(self.public_key)).digest()
405 mac1 = blake2s(bytes(init)[0:-32], digest_size=16, key=mac_key).digest()
406 self._test.assertEqual(init[WireguardInitiation].mac1, mac1)
408 # validate the mac2 hash
410 self._test.assertNotEqual(init[WireguardInitiation].mac2, bytes([0] * 16))
411 self._test.assertNotEqual(self.last_sent_cookie, None)
413 bytes(init)[0:-16], digest_size=16, key=self.last_sent_cookie
415 self._test.assertEqual(init[WireguardInitiation].mac2, mac2)
416 self.last_sent_cookie = None
418 self._test.assertEqual(init[WireguardInitiation].mac2, bytes([0] * 16))
420 # this passes only unencrypted_ephemeral, encrypted_static,
421 # encrypted_timestamp fields of the init
422 payload = self.noise.read_message(bytes(init)[8:-32])
425 b = self.noise.write_message()
426 mac_key = blake2s(b"mac1----" + public_key_bytes(self.itf.public_key)).digest()
427 resp = Wireguard(message_type=2, reserved_zero=0) / WireguardResponse(
428 sender_index=self.receiver_index,
429 receiver_index=self.sender,
430 unencrypted_ephemeral=b[0:32],
431 encrypted_nothing=b[32:],
433 mac1 = blake2s(bytes(resp)[:-32], digest_size=16, key=mac_key).digest()
434 resp[WireguardResponse].mac1 = mac1
435 self.last_mac1 = mac1
437 resp = self.mk_tunnel_header(tx_itf, is_ip6) / resp
438 self._test.assertTrue(self.noise.handshake_finished)
442 def consume_response(self, p, is_ip6=False):
443 self.verify_header(p, is_ip6)
445 resp = Wireguard(p[Raw])
447 self._test.assertEqual(resp[Wireguard].message_type, 2)
448 self._test.assertEqual(resp[Wireguard].reserved_zero, 0)
449 self._test.assertEqual(
450 resp[WireguardResponse].receiver_index, self.receiver_index
453 self.sender = resp[Wireguard].sender_index
455 payload = self.noise.read_message(bytes(resp)[12:60])
456 self._test.assertEqual(payload, b"")
457 self._test.assertTrue(self.noise.handshake_finished)
459 def decrypt_transport(self, p, is_ip6=False):
460 self.verify_header(p, is_ip6)
462 p = Wireguard(p[Raw])
463 self._test.assertEqual(p[Wireguard].message_type, 4)
464 self._test.assertEqual(p[Wireguard].reserved_zero, 0)
465 self._test.assertEqual(
466 p[WireguardTransport].receiver_index, self.receiver_index
469 d = self.noise.decrypt(p[WireguardTransport].encrypted_encapsulated_packet)
472 def encrypt_transport(self, p):
473 return self.noise.encrypt(bytes(p))
475 def validate_encapped(self, rxs, tx, is_tunnel_ip6=False, is_transport_ip6=False):
478 rx = self.decrypt_transport(rx, is_tunnel_ip6)
479 if is_transport_ip6 is False:
481 # check the original packet is present
482 self._test.assertEqual(rx[IP].dst, tx[IP].dst)
483 self._test.assertEqual(rx[IP].ttl, tx[IP].ttl - 1)
486 # check the original packet is present
487 self._test.assertEqual(rx[IPv6].dst, tx[IPv6].dst)
488 self._test.assertEqual(rx[IPv6].hlim, tx[IPv6].hlim - 1)
492 def want_events(self):
493 self._test.vapi.want_wireguard_peer_events(
496 peer_index=self.index,
497 sw_if_index=self.itf.sw_if_index,
500 def wait_event(self, expect, timeout=5):
501 rv = self._test.vapi.wait_for_event(timeout, "wireguard_peer_event")
502 self._test.assertEqual(rv.flags, expect)
503 self._test.assertEqual(rv.peer_index, self.index)
506 def is_handshake_init(p):
507 wg_p = Wireguard(p[Raw])
509 return wg_p[Wireguard].message_type == 1
512 class TestWg(VppTestCase):
513 """Wireguard Test Case"""
515 error_str = compile(r"Error")
517 wg4_output_node_name = "/err/wg4-output-tun/"
518 wg4_input_node_name = "/err/wg4-input/"
519 wg6_output_node_name = "/err/wg6-output-tun/"
520 wg6_input_node_name = "/err/wg6-input/"
521 kp4_error = wg4_output_node_name + "Keypair error"
522 mac4_error = wg4_input_node_name + "Invalid MAC handshake"
523 peer4_in_err = wg4_input_node_name + "Peer error"
524 peer4_out_err = wg4_output_node_name + "Peer error"
525 kp6_error = wg6_output_node_name + "Keypair error"
526 mac6_error = wg6_input_node_name + "Invalid MAC handshake"
527 peer6_in_err = wg6_input_node_name + "Peer error"
528 peer6_out_err = wg6_output_node_name + "Peer error"
529 cookie_dec4_err = wg4_input_node_name + "Failed during Cookie decryption"
530 cookie_dec6_err = wg6_input_node_name + "Failed during Cookie decryption"
531 ratelimited4_err = wg4_input_node_name + "Handshake ratelimited"
532 ratelimited6_err = wg6_input_node_name + "Handshake ratelimited"
536 super(TestWg, cls).setUpClass()
537 if (is_distro_ubuntu2204 == True or is_distro_debian11 == True) and not hasattr(
542 cls.create_pg_interfaces(range(3))
543 for i in cls.pg_interfaces:
551 super(TestWg, cls).tearDownClass()
555 def tearDownClass(cls):
556 super(TestWg, cls).tearDownClass()
559 super(VppTestCase, self).setUp()
560 self.base_kp4_err = self.statistics.get_err_counter(self.kp4_error)
561 self.base_mac4_err = self.statistics.get_err_counter(self.mac4_error)
562 self.base_peer4_in_err = self.statistics.get_err_counter(self.peer4_in_err)
563 self.base_peer4_out_err = self.statistics.get_err_counter(self.peer4_out_err)
564 self.base_kp6_err = self.statistics.get_err_counter(self.kp6_error)
565 self.base_mac6_err = self.statistics.get_err_counter(self.mac6_error)
566 self.base_peer6_in_err = self.statistics.get_err_counter(self.peer6_in_err)
567 self.base_peer6_out_err = self.statistics.get_err_counter(self.peer6_out_err)
568 self.base_cookie_dec4_err = self.statistics.get_err_counter(
571 self.base_cookie_dec6_err = self.statistics.get_err_counter(
574 self.base_ratelimited4_err = self.statistics.get_err_counter(
575 self.ratelimited4_err
577 self.base_ratelimited6_err = self.statistics.get_err_counter(
578 self.ratelimited6_err
581 def send_and_assert_no_replies_ignoring_init(
582 self, intf, pkts, remark="", timeout=None
584 self.pg_send(intf, pkts)
586 def _filter_out_fn(p):
587 return is_ipv6_misc(p) or is_handshake_init(p)
592 for i in self.pg_interfaces:
593 i.assert_nothing_captured(
594 timeout=timeout, remark=remark, filter_out_fn=_filter_out_fn
600 def test_wg_interface(self):
601 """Simple interface creation"""
605 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
607 self.logger.info(self.vapi.cli("sh int"))
610 wg0.remove_vpp_config()
612 def test_handshake_hash(self):
613 """test hashing an init message"""
614 # a init packet generated by linux given the key below
637 b = bytearray.fromhex(h)
640 pubb = base64.b64decode("aRuHFTTxICIQNefp05oKWlJv3zgKxb8+WW7JJMh0jyM=")
641 pub = X25519PublicKey.from_public_bytes(pubb)
643 self.assertEqual(pubb, public_key_bytes(pub))
645 # strip the macs and build a new packet
647 mac_key = blake2s(b"mac1----" + public_key_bytes(pub)).digest()
648 init += blake2s(init, digest_size=16, key=mac_key).digest()
651 act = Wireguard(init)
653 self.assertEqual(tgt, act)
655 def _test_wg_send_cookie_tmpl(self, is_resp, is_ip6):
658 # create wg interface
660 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
664 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
668 self.pg_enable_capture(self.pg_interfaces)
674 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
678 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
680 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
683 # skip the first automatic handshake
684 self.pg1.get_capture(1, timeout=HANDSHAKE_JITTER)
686 # prepare and send a handshake initiation
687 # expect the peer to send a handshake response
688 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
689 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
691 # wait for the peer to send a handshake initiation
692 rxs = self.pg1.get_capture(1, timeout=2)
694 # prepare and send a wrong cookie reply
695 # expect no replies and the cookie error incremented
696 cookie = peer_1.mk_cookie(rxs[0], self.pg1, is_resp=is_resp, is_ip6=is_ip6)
697 cookie.nonce = b"1234567890"
698 self.send_and_assert_no_replies(self.pg1, [cookie], timeout=0.1)
701 self.base_cookie_dec6_err + 1,
702 self.statistics.get_err_counter(self.cookie_dec6_err),
706 self.base_cookie_dec4_err + 1,
707 self.statistics.get_err_counter(self.cookie_dec4_err),
710 # prepare and send a correct cookie reply
711 cookie = peer_1.mk_cookie(rxs[0], self.pg1, is_resp=is_resp, is_ip6=is_ip6)
712 self.pg_send(self.pg1, [cookie])
714 # wait for the peer to send a handshake initiation with mac2 set
715 rxs = self.pg1.get_capture(1, timeout=6)
717 # verify the initiation and its mac2
718 peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6, is_mac2=True)
721 peer_1.remove_vpp_config()
722 wg0.remove_vpp_config()
724 def test_wg_send_cookie_on_init_v4(self):
725 """Send cookie on handshake initiation (v4)"""
726 self._test_wg_send_cookie_tmpl(is_resp=False, is_ip6=False)
728 def test_wg_send_cookie_on_init_v6(self):
729 """Send cookie on handshake initiation (v6)"""
730 self._test_wg_send_cookie_tmpl(is_resp=False, is_ip6=True)
732 def test_wg_send_cookie_on_resp_v4(self):
733 """Send cookie on handshake response (v4)"""
734 self._test_wg_send_cookie_tmpl(is_resp=True, is_ip6=False)
736 def test_wg_send_cookie_on_resp_v6(self):
737 """Send cookie on handshake response (v6)"""
738 self._test_wg_send_cookie_tmpl(is_resp=True, is_ip6=True)
740 def _test_wg_receive_cookie_tmpl(self, is_resp, is_ip6):
743 # create wg interface
745 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
749 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
753 self.pg_enable_capture(self.pg_interfaces)
759 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
763 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
765 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
768 # wait for the peer to send a handshake initiation
769 rxs = self.pg1.get_capture(1, timeout=2)
770 # prepare and send a bunch of handshake responses
771 # expect to switch to under load state
772 resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6)
773 txs = [resp] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
774 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
775 # reset noise to be able to turn into initiator later
778 # skip the first automatic handshake
779 self.pg1.get_capture(1, timeout=HANDSHAKE_JITTER)
781 # prepare and send a bunch of handshake initiations
782 # expect to switch to under load state
783 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
784 txs = [init] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
785 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
787 # expect the peer to send a cookie reply
788 peer_1.consume_cookie(rxs[-1], is_ip6=is_ip6)
790 # prepare and send a handshake initiation with wrong mac2
791 # expect a cookie reply
792 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
793 init.mac2 = b"1234567890"
794 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
795 peer_1.consume_cookie(rxs[0], is_ip6=is_ip6)
797 # prepare and send a handshake initiation with correct mac2
798 # expect a handshake response
799 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
800 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
802 # verify the response
803 peer_1.consume_response(rxs[0], is_ip6=is_ip6)
805 # clear up under load state
806 self.sleep(UNDER_LOAD_INTERVAL)
809 peer_1.remove_vpp_config()
810 wg0.remove_vpp_config()
812 def test_wg_receive_cookie_on_init_v4(self):
813 """Receive cookie on handshake initiation (v4)"""
814 self._test_wg_receive_cookie_tmpl(is_resp=False, is_ip6=False)
816 def test_wg_receive_cookie_on_init_v6(self):
817 """Receive cookie on handshake initiation (v6)"""
818 self._test_wg_receive_cookie_tmpl(is_resp=False, is_ip6=True)
820 def test_wg_receive_cookie_on_resp_v4(self):
821 """Receive cookie on handshake response (v4)"""
822 self._test_wg_receive_cookie_tmpl(is_resp=True, is_ip6=False)
824 def test_wg_receive_cookie_on_resp_v6(self):
825 """Receive cookie on handshake response (v6)"""
826 self._test_wg_receive_cookie_tmpl(is_resp=True, is_ip6=True)
828 def test_wg_under_load_interval(self):
829 """Under load interval"""
832 # create wg interface
833 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
837 self.pg_enable_capture(self.pg_interfaces)
842 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
844 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
846 # skip the first automatic handshake
847 self.pg1.get_capture(1, timeout=HANDSHAKE_JITTER)
849 # prepare and send a bunch of handshake initiations
850 # expect to switch to under load state
851 init = peer_1.mk_handshake(self.pg1)
852 txs = [init] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
853 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
855 # expect the peer to send a cookie reply
856 peer_1.consume_cookie(rxs[-1])
858 # sleep till the next counting interval
859 # expect under load state is still active
860 self.sleep(HANDSHAKE_COUNTING_INTERVAL)
862 # prepare and send a handshake initiation with wrong mac2
863 # expect a cookie reply
864 init = peer_1.mk_handshake(self.pg1)
865 init.mac2 = b"1234567890"
866 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
867 peer_1.consume_cookie(rxs[0])
869 # sleep till the end of being under load
870 # expect under load state is over
871 self.sleep(UNDER_LOAD_INTERVAL - HANDSHAKE_COUNTING_INTERVAL)
873 # prepare and send a handshake initiation with wrong mac2
874 # expect a handshake response
875 init = peer_1.mk_handshake(self.pg1)
876 init.mac2 = b"1234567890"
877 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
879 # verify the response
880 peer_1.consume_response(rxs[0])
883 peer_1.remove_vpp_config()
884 wg0.remove_vpp_config()
886 def _test_wg_handshake_ratelimiting_tmpl(self, is_ip6):
889 # create wg interface
891 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
895 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
899 self.pg_enable_capture(self.pg_interfaces)
905 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
909 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
911 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
913 # skip the first automatic handshake
914 self.pg1.get_capture(1, timeout=HANDSHAKE_JITTER)
916 # prepare and send a bunch of handshake initiations
917 # expect to switch to under load state
918 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
919 txs = [init] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
920 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
922 # expect the peer to send a cookie reply
923 peer_1.consume_cookie(rxs[-1], is_ip6=is_ip6)
925 # prepare and send a bunch of handshake initiations with correct mac2
926 # expect a handshake response and then ratelimiting
928 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
929 txs = [init] * (HANDSHAKE_NUM_BEFORE_RATELIMITING + NUM_TO_REJECT)
930 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
934 self.base_ratelimited6_err + NUM_TO_REJECT,
935 self.statistics.get_err_counter(self.ratelimited6_err),
939 self.base_ratelimited4_err + NUM_TO_REJECT,
940 self.statistics.get_err_counter(self.ratelimited4_err),
943 # verify the response
944 peer_1.consume_response(rxs[0], is_ip6=is_ip6)
946 # clear up under load state
947 self.sleep(UNDER_LOAD_INTERVAL)
950 peer_1.remove_vpp_config()
951 wg0.remove_vpp_config()
953 def test_wg_handshake_ratelimiting_v4(self):
954 """Handshake ratelimiting (v4)"""
955 self._test_wg_handshake_ratelimiting_tmpl(is_ip6=False)
957 def test_wg_handshake_ratelimiting_v6(self):
958 """Handshake ratelimiting (v6)"""
959 self._test_wg_handshake_ratelimiting_tmpl(is_ip6=True)
961 def test_wg_handshake_ratelimiting_multi_peer(self):
962 """Handshake ratelimiting (multiple peer)"""
965 # create wg interface
966 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
970 self.pg_enable_capture(self.pg_interfaces)
975 self.pg1.generate_remote_hosts(NUM_PEERS)
976 self.pg1.configure_ipv4_neighbors()
979 self, wg0, self.pg1.remote_hosts[0].ip4, port + 1, ["10.11.3.0/24"]
982 self, wg0, self.pg1.remote_hosts[1].ip4, port + 1, ["10.11.4.0/24"]
984 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 2)
986 # skip the first automatic handshake
987 self.pg1.get_capture(NUM_PEERS, timeout=HANDSHAKE_JITTER)
989 # (peer_1) prepare and send a bunch of handshake initiations
990 # expect not to switch to under load state
991 init_1 = peer_1.mk_handshake(self.pg1)
992 txs = [init_1] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
993 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
995 # (peer_1) expect the peer to send a handshake response
996 peer_1.consume_response(rxs[0])
999 # (peer_1) send another bunch of handshake initiations
1000 # expect to switch to under load state
1001 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
1003 # (peer_1) expect the peer to send a cookie reply
1004 peer_1.consume_cookie(rxs[-1])
1006 # (peer_2) prepare and send a handshake initiation
1007 # expect a cookie reply
1008 init_2 = peer_2.mk_handshake(self.pg1)
1009 rxs = self.send_and_expect(self.pg1, [init_2], self.pg1)
1010 peer_2.consume_cookie(rxs[0])
1012 # (peer_1) (peer_2) prepare and send a bunch of handshake initiations with correct mac2
1013 # expect a handshake response and then ratelimiting
1014 PEER_1_NUM_TO_REJECT = 2
1015 PEER_2_NUM_TO_REJECT = 5
1016 init_1 = peer_1.mk_handshake(self.pg1)
1017 txs = [init_1] * (HANDSHAKE_NUM_BEFORE_RATELIMITING + PEER_1_NUM_TO_REJECT)
1018 init_2 = peer_2.mk_handshake(self.pg1)
1019 txs += [init_2] * (HANDSHAKE_NUM_BEFORE_RATELIMITING + PEER_2_NUM_TO_REJECT)
1020 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
1023 self.base_ratelimited4_err + PEER_1_NUM_TO_REJECT
1024 < self.statistics.get_err_counter(self.ratelimited4_err)
1025 <= self.base_ratelimited4_err + PEER_1_NUM_TO_REJECT + PEER_2_NUM_TO_REJECT
1028 # (peer_1) (peer_2) verify the response
1029 peer_1.consume_response(rxs[0])
1030 peer_2.consume_response(rxs[1])
1032 # clear up under load state
1033 self.sleep(UNDER_LOAD_INTERVAL)
1036 peer_1.remove_vpp_config()
1037 peer_2.remove_vpp_config()
1038 wg0.remove_vpp_config()
1040 def _test_wg_peer_roaming_on_handshake_tmpl(self, is_endpoint_set, is_resp, is_ip6):
1043 # create wg interface
1045 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1049 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1053 self.pg_enable_capture(self.pg_interfaces)
1056 # create more remote hosts
1057 NUM_REMOTE_HOSTS = 2
1058 self.pg1.generate_remote_hosts(NUM_REMOTE_HOSTS)
1060 self.pg1.configure_ipv6_neighbors()
1062 self.pg1.configure_ipv4_neighbors()
1069 endpoint=self.pg1.remote_hosts[0].ip6 if is_endpoint_set else "::",
1070 port=port + 1 if is_endpoint_set else 0,
1071 allowed_ips=["1::3:0/112"],
1077 endpoint=self.pg1.remote_hosts[0].ip4 if is_endpoint_set else "0.0.0.0",
1078 port=port + 1 if is_endpoint_set else 0,
1079 allowed_ips=["10.11.3.0/24"],
1081 self.assertTrue(peer_1.query_vpp_config())
1084 # wait for the peer to send a handshake initiation
1085 rxs = self.pg1.get_capture(1, timeout=2)
1086 # prepare a handshake response
1087 resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6)
1090 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip6, port + 100)
1091 resp[IPv6].src, resp[UDP].sport = peer_1.endpoint, peer_1.port
1093 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip4, port + 100)
1094 resp[IP].src, resp[UDP].sport = peer_1.endpoint, peer_1.port
1095 # send the handshake response
1096 # expect a keepalive message sent to the new endpoint
1097 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1098 # verify the keepalive message
1099 b = peer_1.decrypt_transport(rxs[0], is_ip6=is_ip6)
1100 self.assertEqual(0, len(b))
1104 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip6, port + 100)
1106 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip4, port + 100)
1107 # prepare and send a handshake initiation
1108 # expect a handshake response sent to the new endpoint
1109 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
1110 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
1111 # verify the response
1112 peer_1.consume_response(rxs[0], is_ip6=is_ip6)
1113 self.assertTrue(peer_1.query_vpp_config())
1116 peer_1.remove_vpp_config()
1117 wg0.remove_vpp_config()
1119 def test_wg_peer_roaming_on_init_v4(self):
1120 """Peer roaming on handshake initiation (v4)"""
1121 self._test_wg_peer_roaming_on_handshake_tmpl(
1122 is_endpoint_set=False, is_resp=False, is_ip6=False
1125 def test_wg_peer_roaming_on_init_v6(self):
1126 """Peer roaming on handshake initiation (v6)"""
1127 self._test_wg_peer_roaming_on_handshake_tmpl(
1128 is_endpoint_set=False, is_resp=False, is_ip6=True
1131 def test_wg_peer_roaming_on_resp_v4(self):
1132 """Peer roaming on handshake response (v4)"""
1133 self._test_wg_peer_roaming_on_handshake_tmpl(
1134 is_endpoint_set=True, is_resp=True, is_ip6=False
1137 def test_wg_peer_roaming_on_resp_v6(self):
1138 """Peer roaming on handshake response (v6)"""
1139 self._test_wg_peer_roaming_on_handshake_tmpl(
1140 is_endpoint_set=True, is_resp=True, is_ip6=True
1143 def _test_wg_peer_roaming_on_data_tmpl(self, is_async, is_ip6):
1144 self.vapi.wg_set_async_mode(is_async)
1147 # create wg interface
1149 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1153 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1157 self.pg_enable_capture(self.pg_interfaces)
1160 # create more remote hosts
1161 NUM_REMOTE_HOSTS = 2
1162 self.pg1.generate_remote_hosts(NUM_REMOTE_HOSTS)
1164 self.pg1.configure_ipv6_neighbors()
1166 self.pg1.configure_ipv4_neighbors()
1171 self, wg0, self.pg1.remote_hosts[0].ip6, port + 1, ["1::3:0/112"]
1175 self, wg0, self.pg1.remote_hosts[0].ip4, port + 1, ["10.11.3.0/24"]
1177 self.assertTrue(peer_1.query_vpp_config())
1179 # create a route to rewrite traffic into the wg interface
1182 self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
1186 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1189 # wait for the peer to send a handshake initiation
1190 rxs = self.pg1.get_capture(1, timeout=2)
1192 # prepare and send a handshake response
1193 # expect a keepalive message
1194 resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6)
1195 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1197 # verify the keepalive message
1198 b = peer_1.decrypt_transport(rxs[0], is_ip6=is_ip6)
1199 self.assertEqual(0, len(b))
1203 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip6, port + 100)
1205 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip4, port + 100)
1207 # prepare and send a data packet
1208 # expect endpoint change
1210 ip_header = IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1212 ip_header = IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1214 peer_1.mk_tunnel_header(self.pg1, is_ip6=is_ip6)
1215 / Wireguard(message_type=4, reserved_zero=0)
1216 / WireguardTransport(
1217 receiver_index=peer_1.sender,
1219 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1220 ip_header / UDP(sport=222, dport=223) / Raw()
1224 rxs = self.send_and_expect(self.pg1, [data], self.pg0)
1226 self.assertEqual(rxs[0][IPv6].dst, self.pg0.remote_ip6)
1227 self.assertEqual(rxs[0][IPv6].hlim, 19)
1229 self.assertEqual(rxs[0][IP].dst, self.pg0.remote_ip4)
1230 self.assertEqual(rxs[0][IP].ttl, 19)
1231 self.assertTrue(peer_1.query_vpp_config())
1233 # prepare and send a packet that will be rewritten into the wg interface
1234 # expect a data packet sent to the new endpoint
1236 ip_header = IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1238 ip_header = IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1240 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1242 / UDP(sport=555, dport=556)
1245 rxs = self.send_and_expect(self.pg0, [p], self.pg1)
1247 # verify the data packet
1248 peer_1.validate_encapped(rxs, p, is_tunnel_ip6=is_ip6, is_transport_ip6=is_ip6)
1251 r1.remove_vpp_config()
1252 peer_1.remove_vpp_config()
1253 wg0.remove_vpp_config()
1255 def test_wg_peer_roaming_on_data_v4_sync(self):
1256 """Peer roaming on data packet (v4, sync)"""
1257 self._test_wg_peer_roaming_on_data_tmpl(is_async=False, is_ip6=False)
1259 def test_wg_peer_roaming_on_data_v6_sync(self):
1260 """Peer roaming on data packet (v6, sync)"""
1261 self._test_wg_peer_roaming_on_data_tmpl(is_async=False, is_ip6=True)
1263 def test_wg_peer_roaming_on_data_v4_async(self):
1264 """Peer roaming on data packet (v4, async)"""
1265 self._test_wg_peer_roaming_on_data_tmpl(is_async=True, is_ip6=False)
1267 def test_wg_peer_roaming_on_data_v6_async(self):
1268 """Peer roaming on data packet (v6, async)"""
1269 self._test_wg_peer_roaming_on_data_tmpl(is_async=True, is_ip6=True)
1271 def test_wg_peer_resp(self):
1272 """Send handshake response IPv4 tunnel"""
1276 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1280 self.pg_enable_capture(self.pg_interfaces)
1284 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
1286 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1289 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1292 # wait for the peer to send a handshake
1293 rx = self.pg1.get_capture(1, timeout=2)
1295 # consume the handshake in the noise protocol and
1296 # generate the response
1297 resp = peer_1.consume_init(rx[0], self.pg1)
1299 # send the response, get keepalive
1300 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1303 b = peer_1.decrypt_transport(rx)
1304 self.assertEqual(0, len(b))
1306 # send a packets that are routed into the tunnel
1308 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1309 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1310 / UDP(sport=555, dport=556)
1314 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1316 peer_1.validate_encapped(rxs, p)
1318 # send packets into the tunnel, expect to receive them on
1322 peer_1.mk_tunnel_header(self.pg1)
1323 / Wireguard(message_type=4, reserved_zero=0)
1324 / WireguardTransport(
1325 receiver_index=peer_1.sender,
1327 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1329 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1330 / UDP(sport=222, dport=223)
1336 for ii in range(255)
1339 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1342 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1343 self.assertEqual(rx[IP].ttl, 19)
1345 r1.remove_vpp_config()
1346 peer_1.remove_vpp_config()
1347 wg0.remove_vpp_config()
1349 def test_wg_peer_resp_ipv6(self):
1350 """Send handshake response IPv6 tunnel"""
1354 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1358 self.pg_enable_capture(self.pg_interfaces)
1362 self, wg0, self.pg1.remote_ip6, port + 1, ["10.11.3.0/24"]
1364 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1367 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1370 # wait for the peer to send a handshake
1371 rx = self.pg1.get_capture(1, timeout=2)
1373 # consume the handshake in the noise protocol and
1374 # generate the response
1375 resp = peer_1.consume_init(rx[0], self.pg1, is_ip6=True)
1377 # send the response, get keepalive
1378 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1381 b = peer_1.decrypt_transport(rx, True)
1382 self.assertEqual(0, len(b))
1384 # send a packets that are routed into the tunnel
1386 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1387 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1388 / UDP(sport=555, dport=556)
1392 rxs = self.send_and_expect(self.pg0, p * 2, self.pg1)
1393 peer_1.validate_encapped(rxs, p, True)
1395 # send packets into the tunnel, expect to receive them on
1399 peer_1.mk_tunnel_header(self.pg1, True)
1400 / Wireguard(message_type=4, reserved_zero=0)
1401 / WireguardTransport(
1402 receiver_index=peer_1.sender,
1404 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1406 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1407 / UDP(sport=222, dport=223)
1413 for ii in range(255)
1416 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1419 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1420 self.assertEqual(rx[IP].ttl, 19)
1422 r1.remove_vpp_config()
1423 peer_1.remove_vpp_config()
1424 wg0.remove_vpp_config()
1426 def test_wg_peer_v4o4(self):
1432 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1437 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
1439 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1442 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1445 self, "20.22.3.0", 24, [VppRoutePath("20.22.3.1", wg0.sw_if_index)]
1448 # route a packet into the wg interface
1449 # use the allowed-ip prefix
1450 # this is dropped because the peer is not initiated
1452 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1453 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1454 / UDP(sport=555, dport=556)
1457 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1459 self.base_kp4_err + 1, self.statistics.get_err_counter(self.kp4_error)
1462 # route a packet into the wg interface
1463 # use a not allowed-ip prefix
1464 # this is dropped because there is no matching peer
1466 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1467 / IP(src=self.pg0.remote_ip4, dst="20.22.3.2")
1468 / UDP(sport=555, dport=556)
1471 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1473 self.base_peer4_out_err + 1,
1474 self.statistics.get_err_counter(self.peer4_out_err),
1477 # send a handsake from the peer with an invalid MAC
1478 p = peer_1.mk_handshake(self.pg1)
1479 p[WireguardInitiation].mac1 = b"foobar"
1480 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1482 self.base_mac4_err + 1, self.statistics.get_err_counter(self.mac4_error)
1485 # send a handsake from the peer but signed by the wrong key.
1486 p = peer_1.mk_handshake(
1487 self.pg1, False, X25519PrivateKey.generate().public_key()
1489 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1491 self.base_peer4_in_err + 1,
1492 self.statistics.get_err_counter(self.peer4_in_err),
1495 # send a valid handsake init for which we expect a response
1496 p = peer_1.mk_handshake(self.pg1)
1498 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1500 peer_1.consume_response(rx[0])
1502 # route a packet into the wg interface
1503 # this is dropped because the peer is still not initiated
1505 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1506 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1507 / UDP(sport=555, dport=556)
1510 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1512 self.base_kp4_err + 2, self.statistics.get_err_counter(self.kp4_error)
1515 # send a data packet from the peer through the tunnel
1516 # this completes the handshake
1518 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1519 / UDP(sport=222, dport=223)
1522 d = peer_1.encrypt_transport(p)
1523 p = peer_1.mk_tunnel_header(self.pg1) / (
1524 Wireguard(message_type=4, reserved_zero=0)
1525 / WireguardTransport(
1526 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1529 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1532 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1533 self.assertEqual(rx[IP].ttl, 19)
1535 # send a packets that are routed into the tunnel
1537 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1538 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1539 / UDP(sport=555, dport=556)
1543 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1546 rx = IP(peer_1.decrypt_transport(rx))
1548 # check the original packet is present
1549 self.assertEqual(rx[IP].dst, p[IP].dst)
1550 self.assertEqual(rx[IP].ttl, p[IP].ttl - 1)
1552 # send packets into the tunnel, expect to receive them on
1556 peer_1.mk_tunnel_header(self.pg1)
1557 / Wireguard(message_type=4, reserved_zero=0)
1558 / WireguardTransport(
1559 receiver_index=peer_1.sender,
1561 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1563 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1564 / UDP(sport=222, dport=223)
1570 for ii in range(255)
1573 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1576 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1577 self.assertEqual(rx[IP].ttl, 19)
1579 r1.remove_vpp_config()
1580 r2.remove_vpp_config()
1581 peer_1.remove_vpp_config()
1582 wg0.remove_vpp_config()
1584 def test_wg_peer_v6o6(self):
1590 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1595 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
1597 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1600 self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
1603 self, "22::3:0", 112, [VppRoutePath("22::3:1", wg0.sw_if_index)]
1606 # route a packet into the wg interface
1607 # use the allowed-ip prefix
1608 # this is dropped because the peer is not initiated
1611 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1612 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1613 / UDP(sport=555, dport=556)
1616 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1619 self.base_kp6_err + 1, self.statistics.get_err_counter(self.kp6_error)
1622 # route a packet into the wg interface
1623 # use a not allowed-ip prefix
1624 # this is dropped because there is no matching peer
1626 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1627 / IPv6(src=self.pg0.remote_ip6, dst="22::3:2")
1628 / UDP(sport=555, dport=556)
1631 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1633 self.base_peer6_out_err + 1,
1634 self.statistics.get_err_counter(self.peer6_out_err),
1637 # send a handsake from the peer with an invalid MAC
1638 p = peer_1.mk_handshake(self.pg1, True)
1639 p[WireguardInitiation].mac1 = b"foobar"
1640 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1643 self.base_mac6_err + 1, self.statistics.get_err_counter(self.mac6_error)
1646 # send a handsake from the peer but signed by the wrong key.
1647 p = peer_1.mk_handshake(
1648 self.pg1, True, X25519PrivateKey.generate().public_key()
1650 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1652 self.base_peer6_in_err + 1,
1653 self.statistics.get_err_counter(self.peer6_in_err),
1656 # send a valid handsake init for which we expect a response
1657 p = peer_1.mk_handshake(self.pg1, True)
1659 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1661 peer_1.consume_response(rx[0], True)
1663 # route a packet into the wg interface
1664 # this is dropped because the peer is still not initiated
1666 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1667 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1668 / UDP(sport=555, dport=556)
1671 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1673 self.base_kp6_err + 2, self.statistics.get_err_counter(self.kp6_error)
1676 # send a data packet from the peer through the tunnel
1677 # this completes the handshake
1679 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1680 / UDP(sport=222, dport=223)
1683 d = peer_1.encrypt_transport(p)
1684 p = peer_1.mk_tunnel_header(self.pg1, True) / (
1685 Wireguard(message_type=4, reserved_zero=0)
1686 / WireguardTransport(
1687 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1690 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1693 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1694 self.assertEqual(rx[IPv6].hlim, 19)
1696 # send a packets that are routed into the tunnel
1698 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1699 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1700 / UDP(sport=555, dport=556)
1704 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1707 rx = IPv6(peer_1.decrypt_transport(rx, True))
1709 # check the original packet is present
1710 self.assertEqual(rx[IPv6].dst, p[IPv6].dst)
1711 self.assertEqual(rx[IPv6].hlim, p[IPv6].hlim - 1)
1713 # send packets into the tunnel, expect to receive them on
1717 peer_1.mk_tunnel_header(self.pg1, True)
1718 / Wireguard(message_type=4, reserved_zero=0)
1719 / WireguardTransport(
1720 receiver_index=peer_1.sender,
1722 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1724 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1725 / UDP(sport=222, dport=223)
1731 for ii in range(255)
1734 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1737 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1738 self.assertEqual(rx[IPv6].hlim, 19)
1740 r1.remove_vpp_config()
1741 r2.remove_vpp_config()
1742 peer_1.remove_vpp_config()
1743 wg0.remove_vpp_config()
1745 def test_wg_peer_v6o4(self):
1751 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1756 self, wg0, self.pg1.remote_ip4, port + 1, ["1::3:0/112"]
1758 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1761 self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
1764 # route a packet into the wg interface
1765 # use the allowed-ip prefix
1766 # this is dropped because the peer is not initiated
1768 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1769 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1770 / UDP(sport=555, dport=556)
1773 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1775 self.base_kp6_err + 1, self.statistics.get_err_counter(self.kp6_error)
1778 # send a handsake from the peer with an invalid MAC
1779 p = peer_1.mk_handshake(self.pg1)
1780 p[WireguardInitiation].mac1 = b"foobar"
1781 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1784 self.base_mac4_err + 1, self.statistics.get_err_counter(self.mac4_error)
1787 # send a handsake from the peer but signed by the wrong key.
1788 p = peer_1.mk_handshake(
1789 self.pg1, False, X25519PrivateKey.generate().public_key()
1791 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1793 self.base_peer4_in_err + 1,
1794 self.statistics.get_err_counter(self.peer4_in_err),
1797 # send a valid handsake init for which we expect a response
1798 p = peer_1.mk_handshake(self.pg1)
1800 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1802 peer_1.consume_response(rx[0])
1804 # route a packet into the wg interface
1805 # this is dropped because the peer is still not initiated
1807 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1808 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1809 / UDP(sport=555, dport=556)
1812 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1814 self.base_kp6_err + 2, self.statistics.get_err_counter(self.kp6_error)
1817 # send a data packet from the peer through the tunnel
1818 # this completes the handshake
1820 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1821 / UDP(sport=222, dport=223)
1824 d = peer_1.encrypt_transport(p)
1825 p = peer_1.mk_tunnel_header(self.pg1) / (
1826 Wireguard(message_type=4, reserved_zero=0)
1827 / WireguardTransport(
1828 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1831 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1834 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1835 self.assertEqual(rx[IPv6].hlim, 19)
1837 # send a packets that are routed into the tunnel
1839 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1840 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1841 / UDP(sport=555, dport=556)
1845 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1848 rx = IPv6(peer_1.decrypt_transport(rx))
1850 # check the original packet is present
1851 self.assertEqual(rx[IPv6].dst, p[IPv6].dst)
1852 self.assertEqual(rx[IPv6].hlim, p[IPv6].hlim - 1)
1854 # send packets into the tunnel, expect to receive them on
1858 peer_1.mk_tunnel_header(self.pg1)
1859 / Wireguard(message_type=4, reserved_zero=0)
1860 / WireguardTransport(
1861 receiver_index=peer_1.sender,
1863 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1865 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1866 / UDP(sport=222, dport=223)
1872 for ii in range(255)
1875 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1878 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1879 self.assertEqual(rx[IPv6].hlim, 19)
1881 r1.remove_vpp_config()
1882 peer_1.remove_vpp_config()
1883 wg0.remove_vpp_config()
1885 def test_wg_peer_v4o6(self):
1891 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1896 self, wg0, self.pg1.remote_ip6, port + 1, ["10.11.3.0/24"]
1898 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1901 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1904 # route a packet into the wg interface
1905 # use the allowed-ip prefix
1906 # this is dropped because the peer is not initiated
1908 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1909 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1910 / UDP(sport=555, dport=556)
1913 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1915 self.base_kp4_err + 1, self.statistics.get_err_counter(self.kp4_error)
1918 # send a handsake from the peer with an invalid MAC
1919 p = peer_1.mk_handshake(self.pg1, True)
1920 p[WireguardInitiation].mac1 = b"foobar"
1921 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1923 self.base_mac6_err + 1, self.statistics.get_err_counter(self.mac6_error)
1926 # send a handsake from the peer but signed by the wrong key.
1927 p = peer_1.mk_handshake(
1928 self.pg1, True, X25519PrivateKey.generate().public_key()
1930 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1932 self.base_peer6_in_err + 1,
1933 self.statistics.get_err_counter(self.peer6_in_err),
1936 # send a valid handsake init for which we expect a response
1937 p = peer_1.mk_handshake(self.pg1, True)
1939 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1941 peer_1.consume_response(rx[0], True)
1943 # route a packet into the wg interface
1944 # this is dropped because the peer is still not initiated
1946 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1947 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1948 / UDP(sport=555, dport=556)
1951 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1953 self.base_kp4_err + 2, self.statistics.get_err_counter(self.kp4_error)
1956 # send a data packet from the peer through the tunnel
1957 # this completes the handshake
1959 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1960 / UDP(sport=222, dport=223)
1963 d = peer_1.encrypt_transport(p)
1964 p = peer_1.mk_tunnel_header(self.pg1, True) / (
1965 Wireguard(message_type=4, reserved_zero=0)
1966 / WireguardTransport(
1967 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1970 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1973 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1974 self.assertEqual(rx[IP].ttl, 19)
1976 # send a packets that are routed into the tunnel
1978 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1979 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1980 / UDP(sport=555, dport=556)
1984 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1987 rx = IP(peer_1.decrypt_transport(rx, True))
1989 # check the original packet is present
1990 self.assertEqual(rx[IP].dst, p[IP].dst)
1991 self.assertEqual(rx[IP].ttl, p[IP].ttl - 1)
1993 # send packets into the tunnel, expect to receive them on
1997 peer_1.mk_tunnel_header(self.pg1, True)
1998 / Wireguard(message_type=4, reserved_zero=0)
1999 / WireguardTransport(
2000 receiver_index=peer_1.sender,
2002 encrypted_encapsulated_packet=peer_1.encrypt_transport(
2004 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
2005 / UDP(sport=222, dport=223)
2011 for ii in range(255)
2014 rxs = self.send_and_expect(self.pg1, p, self.pg0)
2017 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
2018 self.assertEqual(rx[IP].ttl, 19)
2020 r1.remove_vpp_config()
2021 peer_1.remove_vpp_config()
2022 wg0.remove_vpp_config()
2024 def test_wg_multi_peer(self):
2025 """multiple peer setup"""
2029 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2030 wg1 = VppWgInterface(self, self.pg2.local_ip4, port + 1).add_vpp_config()
2034 # Check peer counter
2035 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 0)
2037 self.pg_enable_capture(self.pg_interfaces)
2040 # Create many peers on sencond interface
2042 self.pg2.generate_remote_hosts(NUM_PEERS)
2043 self.pg2.configure_ipv4_neighbors()
2044 self.pg1.generate_remote_hosts(NUM_PEERS)
2045 self.pg1.configure_ipv4_neighbors()
2051 for i in range(NUM_PEERS):
2056 self.pg1.remote_hosts[i].ip4,
2058 ["10.0.%d.4/32" % i],
2066 [VppRoutePath(self.pg1.remote_hosts[i].ip4, wg0.sw_if_index)],
2074 self.pg2.remote_hosts[i].ip4,
2076 ["10.100.%d.4/32" % i],
2084 [VppRoutePath(self.pg2.remote_hosts[i].ip4, wg1.sw_if_index)],
2088 self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_PEERS * 2)
2090 self.logger.info(self.vapi.cli("show wireguard peer"))
2091 self.logger.info(self.vapi.cli("show wireguard interface"))
2092 self.logger.info(self.vapi.cli("show adj 37"))
2093 self.logger.info(self.vapi.cli("sh ip fib 172.16.3.17"))
2094 self.logger.info(self.vapi.cli("sh ip fib 10.11.3.0"))
2098 r.remove_vpp_config()
2100 r.remove_vpp_config()
2104 self.assertTrue(p.query_vpp_config())
2105 p.remove_vpp_config()
2107 self.assertTrue(p.query_vpp_config())
2108 p.remove_vpp_config()
2110 wg0.remove_vpp_config()
2111 wg1.remove_vpp_config()
2113 def test_wg_multi_interface(self):
2114 """Multi-tunnel on the same port"""
2117 # Create many wireguard interfaces
2119 self.pg1.generate_remote_hosts(NUM_IFS)
2120 self.pg1.configure_ipv4_neighbors()
2121 self.pg0.generate_remote_hosts(NUM_IFS)
2122 self.pg0.configure_ipv4_neighbors()
2124 self.pg_enable_capture(self.pg_interfaces)
2127 # Create interfaces with a peer on each
2131 for i in range(NUM_IFS):
2132 # Use the same port for each interface
2133 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2141 self.pg1.remote_hosts[i].ip4,
2143 ["10.0.%d.0/24" % i],
2152 [VppRoutePath("10.0.%d.4" % i, wg0.sw_if_index)],
2156 self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_IFS)
2158 # skip the first automatic handshake
2159 self.pg1.get_capture(NUM_IFS, timeout=HANDSHAKE_JITTER)
2161 for i in range(NUM_IFS):
2162 # send a valid handsake init for which we expect a response
2163 p = peers[i].mk_handshake(self.pg1)
2164 rx = self.send_and_expect(self.pg1, [p], self.pg1)
2165 peers[i].consume_response(rx[0])
2167 # send a data packet from the peer through the tunnel
2168 # this completes the handshake
2170 IP(src="10.0.%d.4" % i, dst=self.pg0.remote_hosts[i].ip4, ttl=20)
2171 / UDP(sport=222, dport=223)
2174 d = peers[i].encrypt_transport(p)
2175 p = peers[i].mk_tunnel_header(self.pg1) / (
2176 Wireguard(message_type=4, reserved_zero=0)
2177 / WireguardTransport(
2178 receiver_index=peers[i].sender,
2180 encrypted_encapsulated_packet=d,
2183 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
2185 self.assertEqual(rx[IP].dst, self.pg0.remote_hosts[i].ip4)
2186 self.assertEqual(rx[IP].ttl, 19)
2188 # send a packets that are routed into the tunnel
2189 for i in range(NUM_IFS):
2191 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2192 / IP(src=self.pg0.remote_hosts[i].ip4, dst="10.0.%d.4" % i)
2193 / UDP(sport=555, dport=556)
2197 rxs = self.send_and_expect(self.pg0, p * 64, self.pg1)
2200 rx = IP(peers[i].decrypt_transport(rx))
2202 # check the oringial packet is present
2203 self.assertEqual(rx[IP].dst, p[IP].dst)
2204 self.assertEqual(rx[IP].ttl, p[IP].ttl - 1)
2206 # send packets into the tunnel
2207 for i in range(NUM_IFS):
2210 peers[i].mk_tunnel_header(self.pg1)
2211 / Wireguard(message_type=4, reserved_zero=0)
2212 / WireguardTransport(
2213 receiver_index=peers[i].sender,
2215 encrypted_encapsulated_packet=peers[i].encrypt_transport(
2218 src="10.0.%d.4" % i,
2219 dst=self.pg0.remote_hosts[i].ip4,
2222 / UDP(sport=222, dport=223)
2231 rxs = self.send_and_expect(self.pg1, p, self.pg0)
2234 self.assertEqual(rx[IP].dst, self.pg0.remote_hosts[i].ip4)
2235 self.assertEqual(rx[IP].ttl, 19)
2238 r.remove_vpp_config()
2240 p.remove_vpp_config()
2242 i.remove_vpp_config()
2244 def test_wg_event(self):
2247 ESTABLISHED_FLAG = (
2248 VppEnum.vl_api_wireguard_peer_flags_t.WIREGUARD_PEER_ESTABLISHED
2250 DEAD_FLAG = VppEnum.vl_api_wireguard_peer_flags_t.WIREGUARD_PEER_STATUS_DEAD
2253 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2254 wg1 = VppWgInterface(self, self.pg2.local_ip4, port + 1).add_vpp_config()
2258 # Check peer counter
2259 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 0)
2261 self.pg_enable_capture(self.pg_interfaces)
2266 self.pg2.generate_remote_hosts(NUM_PEERS)
2267 self.pg2.configure_ipv4_neighbors()
2268 self.pg1.generate_remote_hosts(NUM_PEERS)
2269 self.pg1.configure_ipv4_neighbors()
2275 for i in range(NUM_PEERS):
2280 self.pg1.remote_hosts[i].ip4,
2282 ["10.0.%d.4/32" % i],
2290 [VppRoutePath(self.pg1.remote_hosts[i].ip4, wg0.sw_if_index)],
2298 self.pg2.remote_hosts[i].ip4,
2300 ["10.100.%d.4/32" % i],
2308 [VppRoutePath(self.pg2.remote_hosts[i].ip4, wg1.sw_if_index)],
2312 self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_PEERS * 2)
2314 # skip the first automatic handshake
2315 self.pg1.get_capture(NUM_PEERS, timeout=HANDSHAKE_JITTER)
2316 self.pg2.get_capture(NUM_PEERS, timeout=HANDSHAKE_JITTER)
2318 # Want events from the first perr of wg0
2319 # and from all wg1 peers
2320 peers_0[0].want_events()
2323 for i in range(NUM_PEERS):
2324 # wg0 peers: send a valid handsake init for which we expect a response
2325 p = peers_0[i].mk_handshake(self.pg1)
2326 rx = self.send_and_expect(self.pg1, [p], self.pg1)
2327 peers_0[i].consume_response(rx[0])
2329 # wg0 peers: send empty packet, it means successful connection (WIREGUARD_PEER_ESTABLISHED)
2330 keepalive = peers_0[i].encrypt_transport(0)
2331 p = peers_0[i].mk_tunnel_header(self.pg1) / (
2332 Wireguard(message_type=4, reserved_zero=0)
2333 / WireguardTransport(
2334 receiver_index=peers_0[i].sender,
2336 encrypted_encapsulated_packet=keepalive,
2339 self.send_and_assert_no_replies(self.pg1, [p])
2341 # wg0 peers: wait for established flag
2343 peers_0[0].wait_event(ESTABLISHED_FLAG)
2345 # wg1 peers: send a valid handsake init for which we expect a response
2346 p = peers_1[i].mk_handshake(self.pg2)
2347 rx = self.send_and_expect(self.pg2, [p], self.pg2)
2348 peers_1[i].consume_response(rx[0])
2350 # wg1 peers: send empty packet, it means successful connection (WIREGUARD_PEER_ESTABLISHED)
2351 keepalive = peers_1[i].encrypt_transport(0)
2352 p = peers_1[i].mk_tunnel_header(self.pg2) / (
2353 Wireguard(message_type=4, reserved_zero=0)
2354 / WireguardTransport(
2355 receiver_index=peers_1[i].sender,
2357 encrypted_encapsulated_packet=keepalive,
2360 self.send_and_assert_no_replies(self.pg2, [p])
2362 # wg1 peers: wait for established flag
2363 wg1.wait_events(ESTABLISHED_FLAG, [peers_1[0].index, peers_1[1].index])
2367 r.remove_vpp_config()
2369 r.remove_vpp_config()
2372 for i in range(NUM_PEERS):
2373 self.assertTrue(peers_0[i].query_vpp_config())
2374 peers_0[i].remove_vpp_config()
2376 peers_0[i].wait_event(0)
2377 peers_0[i].wait_event(DEAD_FLAG)
2379 self.assertTrue(p.query_vpp_config())
2380 p.remove_vpp_config()
2382 p.wait_event(DEAD_FLAG)
2384 wg0.remove_vpp_config()
2385 wg1.remove_vpp_config()
2387 def test_wg_sending_handshake_when_admin_down(self):
2388 """Sending handshake when admin down"""
2391 # create wg interface
2392 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2397 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
2399 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2401 self.pg_enable_capture(self.pg_interfaces)
2404 # wait for the peer to send a handshake initiation
2405 # expect no handshakes
2407 self.pg1.assert_nothing_captured(remark="handshake packet(s) sent")
2409 self.pg_enable_capture(self.pg_interfaces)
2412 # administratively enable the wg interface
2413 # expect the peer to send a handshake initiation
2415 rxs = self.pg1.get_capture(1, timeout=2)
2416 peer_1.consume_init(rxs[0], self.pg1)
2418 self.pg_enable_capture(self.pg_interfaces)
2421 # administratively disable the wg interface
2422 # expect no handshakes
2425 self.pg1.assert_nothing_captured(remark="handshake packet(s) sent")
2428 peer_1.remove_vpp_config()
2429 wg0.remove_vpp_config()
2431 def test_wg_sending_data_when_admin_down(self):
2432 """Sending data when admin down"""
2435 # create wg interface
2436 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2440 self.pg_enable_capture(self.pg_interfaces)
2445 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
2447 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2449 # create a route to rewrite traffic into the wg interface
2451 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
2454 # wait for the peer to send a handshake initiation
2455 rxs = self.pg1.get_capture(1, timeout=2)
2457 # prepare and send a handshake response
2458 # expect a keepalive message
2459 resp = peer_1.consume_init(rxs[0], self.pg1)
2460 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
2462 # verify the keepalive message
2463 b = peer_1.decrypt_transport(rxs[0])
2464 self.assertEqual(0, len(b))
2466 # prepare and send a packet that will be rewritten into the wg interface
2467 # expect a data packet sent
2469 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2470 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
2471 / UDP(sport=555, dport=556)
2474 rxs = self.send_and_expect(self.pg0, [p], self.pg1)
2476 # verify the data packet
2477 peer_1.validate_encapped(rxs, p)
2479 # administratively disable the wg interface
2482 # send a packet that will be rewritten into the wg interface
2483 # expect no data packets sent
2484 self.send_and_assert_no_replies(self.pg0, [p])
2486 # administratively enable the wg interface
2487 # expect the peer to send a handshake initiation
2489 peer_1.noise_reset()
2490 rxs = self.pg1.get_capture(1, timeout=2)
2491 resp = peer_1.consume_init(rxs[0], self.pg1)
2493 # send a packet that will be rewritten into the wg interface
2494 # expect no data packets sent because the peer is not initiated
2495 self.send_and_assert_no_replies(self.pg0, [p])
2497 self.base_kp4_err + 1, self.statistics.get_err_counter(self.kp4_error)
2500 # send a handshake response and expect a keepalive message
2501 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
2503 # verify the keepalive message
2504 b = peer_1.decrypt_transport(rxs[0])
2505 self.assertEqual(0, len(b))
2507 # send a packet that will be rewritten into the wg interface
2508 # expect a data packet sent
2509 rxs = self.send_and_expect(self.pg0, [p], self.pg1)
2511 # verify the data packet
2512 peer_1.validate_encapped(rxs, p)
2515 r1.remove_vpp_config()
2516 peer_1.remove_vpp_config()
2517 wg0.remove_vpp_config()
2519 def _test_wg_large_packet_tmpl(self, is_async, is_ip6):
2520 self.vapi.wg_set_async_mode(is_async)
2523 # create wg interface
2525 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
2529 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2533 self.pg_enable_capture(self.pg_interfaces)
2539 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
2543 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
2545 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2547 # create a route to rewrite traffic into the wg interface
2550 self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
2554 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
2557 # wait for the peer to send a handshake initiation
2558 rxs = self.pg1.get_capture(1, timeout=2)
2560 # prepare and send a handshake response
2561 # expect a keepalive message
2562 resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6)
2563 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
2565 # verify the keepalive message
2566 b = peer_1.decrypt_transport(rxs[0], is_ip6=is_ip6)
2567 self.assertEqual(0, len(b))
2569 # prepare and send data packets
2570 # expect to receive them decrypted
2572 ip_header = IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
2574 ip_header = IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
2578 4500, # three buffers
2579 1910 if is_ip6 else 1950, # auth tag is not contiguous
2582 for l in packet_len_opts:
2584 peer_1.mk_tunnel_header(self.pg1, is_ip6=is_ip6)
2585 / Wireguard(message_type=4, reserved_zero=0)
2586 / WireguardTransport(
2587 receiver_index=peer_1.sender,
2589 encrypted_encapsulated_packet=peer_1.encrypt_transport(
2590 ip_header / UDP(sport=222, dport=223) / Raw(b"\xfe" * l)
2594 rxs = self.send_and_expect(self.pg1, txs, self.pg0)
2596 # verify decrypted packets
2597 for i, l in enumerate(packet_len_opts):
2599 self.assertEqual(rxs[i][IPv6].dst, self.pg0.remote_ip6)
2600 self.assertEqual(rxs[i][IPv6].hlim, ip_header.hlim - 1)
2602 self.assertEqual(rxs[i][IP].dst, self.pg0.remote_ip4)
2603 self.assertEqual(rxs[i][IP].ttl, ip_header.ttl - 1)
2604 self.assertEqual(len(rxs[i][Raw]), l)
2605 self.assertEqual(bytes(rxs[i][Raw]), b"\xfe" * l)
2607 # prepare and send packets that will be rewritten into the wg interface
2608 # expect data packets sent
2610 ip_header = IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
2612 ip_header = IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
2616 4500, # three buffers
2617 1980 if is_ip6 else 2000, # no free space to write auth tag
2620 for l in packet_len_opts:
2622 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2624 / UDP(sport=555, dport=556)
2627 rxs = self.send_and_expect(self.pg0, txs, self.pg1)
2629 # verify the data packets
2630 rxs_decrypted = peer_1.validate_encapped(
2631 rxs, ip_header, is_tunnel_ip6=is_ip6, is_transport_ip6=is_ip6
2634 for i, l in enumerate(packet_len_opts):
2635 self.assertEqual(len(rxs_decrypted[i][Raw]), l)
2636 self.assertEqual(bytes(rxs_decrypted[i][Raw]), b"\xfe" * l)
2639 r1.remove_vpp_config()
2640 peer_1.remove_vpp_config()
2641 wg0.remove_vpp_config()
2643 def test_wg_large_packet_v4_sync(self):
2644 """Large packet (v4, sync)"""
2645 self._test_wg_large_packet_tmpl(is_async=False, is_ip6=False)
2647 def test_wg_large_packet_v6_sync(self):
2648 """Large packet (v6, sync)"""
2649 self._test_wg_large_packet_tmpl(is_async=False, is_ip6=True)
2651 def test_wg_large_packet_v4_async(self):
2652 """Large packet (v4, async)"""
2653 self._test_wg_large_packet_tmpl(is_async=True, is_ip6=False)
2655 def test_wg_large_packet_v6_async(self):
2656 """Large packet (v6, async)"""
2657 self._test_wg_large_packet_tmpl(is_async=True, is_ip6=True)
2659 def test_wg_lack_of_buf_headroom(self):
2660 """Lack of buffer's headroom (v6 vxlan over v6 wg)"""
2663 # create wg interface
2664 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
2668 self.pg_enable_capture(self.pg_interfaces)
2673 self, wg0, self.pg1.remote_ip6, port + 1, ["::/0"]
2675 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2677 # create a route to enable communication between wg interface addresses
2679 self, wg0.remote_ip6, 128, [VppRoutePath("0.0.0.0", wg0.sw_if_index)]
2682 # wait for the peer to send a handshake initiation
2683 rxs = self.pg1.get_capture(1, timeout=2)
2685 # prepare and send a handshake response
2686 # expect a keepalive message
2687 resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=True)
2688 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
2690 # verify the keepalive message
2691 b = peer_1.decrypt_transport(rxs[0], is_ip6=True)
2692 self.assertEqual(0, len(b))
2694 # create vxlan interface over the wg interface
2695 vxlan0 = VppVxlanTunnel(self, src=wg0.local_ip6, dst=wg0.remote_ip6, vni=1111)
2696 vxlan0.add_vpp_config()
2698 # create bridge domain
2699 bd1 = VppBridgeDomain(self, bd_id=1)
2700 bd1.add_vpp_config()
2702 # add the vxlan interface and pg0 to the bridge domain
2704 VppBridgeDomainPort(self, bd1, vxlan0).add_vpp_config(),
2705 VppBridgeDomainPort(self, bd1, self.pg0).add_vpp_config(),
2708 # prepare and send packets that will be rewritten into the vxlan interface
2709 # expect they to be rewritten into the wg interface then and data packets sent
2711 Ether(dst="00:00:00:00:00:01", src="00:00:00:00:00:02")
2712 / IPv6(src="::1", dst="::2", hlim=20)
2713 / UDP(sport=1111, dport=1112)
2714 / Raw(b"\xfe" * 1900)
2716 rxs = self.send_and_expect(self.pg0, [tx] * 5, self.pg1)
2718 # verify the data packet
2720 rx_decrypted = IPv6(peer_1.decrypt_transport(rx, is_ip6=True))
2722 self.assertEqual(rx_decrypted[VXLAN].vni, vxlan0.vni)
2723 inner = rx_decrypted[VXLAN].payload
2725 # check the original packet is present
2726 self.assertEqual(inner[IPv6].dst, tx[IPv6].dst)
2727 self.assertEqual(inner[IPv6].hlim, tx[IPv6].hlim)
2728 self.assertEqual(len(inner[Raw]), len(tx[Raw]))
2729 self.assertEqual(bytes(inner[Raw]), bytes(tx[Raw]))
2732 for bdp in bd1_ports:
2733 bdp.remove_vpp_config()
2734 bd1.remove_vpp_config()
2735 vxlan0.remove_vpp_config()
2736 r1.remove_vpp_config()
2737 peer_1.remove_vpp_config()
2738 wg0.remove_vpp_config()
2741 @tag_fixme_vpp_debug
2742 class WireguardHandoffTests(TestWg):
2743 """Wireguard Tests in multi worker setup"""
2745 vpp_worker_count = 2
2747 def test_wg_peer_init(self):
2753 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2757 self.pg_enable_capture(self.pg_interfaces)
2761 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.2.0/24", "10.11.3.0/24"]
2763 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2766 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
2769 # skip the first automatic handshake
2770 self.pg1.get_capture(1, timeout=HANDSHAKE_JITTER)
2772 # send a valid handsake init for which we expect a response
2773 p = peer_1.mk_handshake(self.pg1)
2775 rx = self.send_and_expect(self.pg1, [p], self.pg1)
2777 peer_1.consume_response(rx[0])
2779 # send a data packet from the peer through the tunnel
2780 # this completes the handshake and pins the peer to worker 0
2782 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
2783 / UDP(sport=222, dport=223)
2786 d = peer_1.encrypt_transport(p)
2787 p = peer_1.mk_tunnel_header(self.pg1) / (
2788 Wireguard(message_type=4, reserved_zero=0)
2789 / WireguardTransport(
2790 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
2793 rxs = self.send_and_expect(self.pg1, [p], self.pg0, worker=0)
2796 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
2797 self.assertEqual(rx[IP].ttl, 19)
2799 # send a packets that are routed into the tunnel
2800 # and pins the peer tp worker 1
2802 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2803 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
2804 / UDP(sport=555, dport=556)
2807 rxs = self.send_and_expect(self.pg0, pe * 255, self.pg1, worker=1)
2808 peer_1.validate_encapped(rxs, pe)
2810 # send packets into the tunnel, from the other worker
2813 peer_1.mk_tunnel_header(self.pg1)
2814 / Wireguard(message_type=4, reserved_zero=0)
2815 / WireguardTransport(
2816 receiver_index=peer_1.sender,
2818 encrypted_encapsulated_packet=peer_1.encrypt_transport(
2820 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
2821 / UDP(sport=222, dport=223)
2827 for ii in range(255)
2830 rxs = self.send_and_expect(self.pg1, p, self.pg0, worker=1)
2833 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
2834 self.assertEqual(rx[IP].ttl, 19)
2836 # send a packets that are routed into the tunnel
2838 rxs = self.send_and_expect(self.pg0, pe * 255, self.pg1, worker=0)
2840 peer_1.validate_encapped(rxs, pe)
2842 r1.remove_vpp_config()
2843 peer_1.remove_vpp_config()
2844 wg0.remove_vpp_config()
2846 @unittest.skip("test disabled")
2847 def test_wg_multi_interface(self):
2848 """Multi-tunnel on the same port"""
2851 class TestWgFIB(VppTestCase):
2852 """Wireguard FIB Test Case"""
2855 def setUpClass(cls):
2856 super(TestWgFIB, cls).setUpClass()
2859 def tearDownClass(cls):
2860 super(TestWgFIB, cls).tearDownClass()
2863 super(TestWgFIB, self).setUp()
2865 self.create_pg_interfaces(range(2))
2867 for i in self.pg_interfaces:
2872 for i in self.pg_interfaces:
2875 super(TestWgFIB, self).tearDown()
2877 def test_wg_fib_tracking(self):
2881 # create wg interface
2882 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2886 self.pg_enable_capture(self.pg_interfaces)
2891 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
2893 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2895 # create a route to rewrite traffic into the wg interface
2897 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
2900 # resolve ARP and expect the adjacency to update
2901 self.pg1.resolve_arp()
2903 # wait for the peer to send a handshake initiation
2904 rxs = self.pg1.get_capture(2, timeout=6)
2906 # prepare and send a handshake response
2907 # expect a keepalive message
2908 resp = peer_1.consume_init(rxs[1], self.pg1)
2909 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
2911 # verify the keepalive message
2912 b = peer_1.decrypt_transport(rxs[0])
2913 self.assertEqual(0, len(b))
2915 # prepare and send a packet that will be rewritten into the wg interface
2916 # expect a data packet sent
2918 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2919 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
2920 / UDP(sport=555, dport=556)
2923 rxs = self.send_and_expect(self.pg0, [p], self.pg1)
2925 # verify the data packet
2926 peer_1.validate_encapped(rxs, p)
2929 r1.remove_vpp_config()
2930 peer_1.remove_vpp_config()
2931 wg0.remove_vpp_config()