9 from hashlib import blake2s
10 from config import config
11 from scapy.packet import Raw
12 from scapy.layers.l2 import Ether
13 from scapy.layers.inet import IP, UDP
14 from scapy.layers.inet6 import IPv6
15 from scapy.layers.vxlan import VXLAN
16 from scapy.contrib.wireguard import (
23 from cryptography.hazmat.primitives.asymmetric.x25519 import (
27 from cryptography.hazmat.primitives.serialization import (
33 from noise.connection import NoiseConnection, Keypair
35 from Crypto.Cipher import ChaCha20_Poly1305
36 from Crypto.Random import get_random_bytes
38 from vpp_interface import VppInterface
39 from vpp_pg_interface import is_ipv6_misc
40 from vpp_ip_route import VppIpRoute, VppRoutePath
41 from vpp_l2 import VppBridgeDomain, VppBridgeDomainPort
42 from vpp_vxlan_tunnel import VppVxlanTunnel
43 from vpp_object import VppObject
44 from vpp_papi import VppEnum
45 from asfframework import tag_run_solo, 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
150 HANDSHAKE_JITTER = 0.5
153 class VppWgPeer(VppObject):
154 def __init__(self, test, itf, endpoint, port, allowed_ips, persistent_keepalive=15):
157 self.endpoint = endpoint
159 self.allowed_ips = allowed_ips
160 self.persistent_keepalive = persistent_keepalive
162 # remote peer's public
163 self.private_key = X25519PrivateKey.generate()
164 self.public_key = self.private_key.public_key()
166 # cookie related params
167 self.cookie_key = blake2s(b"cookie--" + self.public_key_bytes()).digest()
168 self.last_sent_cookie = None
169 self.last_mac1 = None
170 self.last_received_cookie = None
172 self.noise = NoiseConnection.from_name(NOISE_HANDSHAKE_NAME)
174 def change_endpoint(self, endpoint, port):
175 self.endpoint = endpoint
178 def add_vpp_config(self):
179 rv = self._test.vapi.wireguard_peer_add(
181 "public_key": self.public_key_bytes(),
183 "endpoint": self.endpoint,
184 "n_allowed_ips": len(self.allowed_ips),
185 "allowed_ips": self.allowed_ips,
186 "sw_if_index": self.itf.sw_if_index,
187 "persistent_keepalive": self.persistent_keepalive,
190 self.index = rv.peer_index
191 self.receiver_index = self.index + 1
192 self._test.registry.register(self, self._test.logger)
195 def remove_vpp_config(self):
196 self._test.vapi.wireguard_peer_remove(peer_index=self.index)
199 return "wireguard-peer-%s" % self.index
201 def public_key_bytes(self):
202 return public_key_bytes(self.public_key)
204 def query_vpp_config(self):
205 peers = self._test.vapi.wireguard_peers_dump()
208 # "::" endpoint will be returned as "0.0.0.0" in peer's details
209 endpoint = "0.0.0.0" if self.endpoint == "::" else self.endpoint
211 p.peer.public_key == self.public_key_bytes()
212 and p.peer.port == self.port
213 and str(p.peer.endpoint) == endpoint
214 and p.peer.sw_if_index == self.itf.sw_if_index
215 and len(self.allowed_ips) == p.peer.n_allowed_ips
217 self.allowed_ips.sort()
218 p.peer.allowed_ips.sort()
220 for a1, a2 in zip(self.allowed_ips, p.peer.allowed_ips):
221 if str(a1) != str(a2):
226 def mk_tunnel_header(self, tx_itf, is_ip6=False):
229 Ether(dst=tx_itf.local_mac, src=tx_itf.remote_mac)
230 / IP(src=self.endpoint, dst=self.itf.src)
231 / UDP(sport=self.port, dport=self.itf.port)
235 Ether(dst=tx_itf.local_mac, src=tx_itf.remote_mac)
236 / IPv6(src=self.endpoint, dst=self.itf.src)
237 / UDP(sport=self.port, dport=self.itf.port)
240 def noise_reset(self):
241 self.noise = NoiseConnection.from_name(NOISE_HANDSHAKE_NAME)
243 def noise_init(self, public_key=None):
244 self.noise.set_prologue(NOISE_IDENTIFIER_NAME)
245 self.noise.set_psks(psk=bytes(bytearray(32)))
248 public_key = self.itf.public_key
251 self.noise.set_keypair_from_private_bytes(
252 Keypair.STATIC, private_key_bytes(self.private_key)
255 self.noise.set_keypair_from_public_bytes(
256 Keypair.REMOTE_STATIC, public_key_bytes(public_key)
259 self.noise.start_handshake()
261 def mk_cookie(self, p, tx_itf, is_resp=False, is_ip6=False):
262 self.verify_header(p, is_ip6)
264 wg_pkt = Wireguard(p[Raw])
267 self._test.assertEqual(wg_pkt[Wireguard].message_type, 2)
268 self._test.assertEqual(wg_pkt[Wireguard].reserved_zero, 0)
269 self._test.assertEqual(wg_pkt[WireguardResponse].mac2, bytes([0] * 16))
271 self._test.assertEqual(wg_pkt[Wireguard].message_type, 1)
272 self._test.assertEqual(wg_pkt[Wireguard].reserved_zero, 0)
273 self._test.assertEqual(wg_pkt[WireguardInitiation].mac2, bytes([0] * 16))
275 # collect info from wg packet (initiation or response)
276 src = get_field_bytes(p[IPv6 if is_ip6 else IP], "src")
277 sport = p[UDP].sport.to_bytes(2, byteorder="big")
279 mac1 = wg_pkt[WireguardResponse].mac1
280 sender_index = wg_pkt[WireguardResponse].sender_index
282 mac1 = wg_pkt[WireguardInitiation].mac1
283 sender_index = wg_pkt[WireguardInitiation].sender_index
286 cookie_reply = Wireguard() / WireguardCookieReply()
287 cookie_reply[Wireguard].message_type = 3
288 cookie_reply[Wireguard].reserved_zero = 0
289 cookie_reply[WireguardCookieReply].receiver_index = sender_index
290 nonce = get_random_bytes(24)
291 cookie_reply[WireguardCookieReply].nonce = nonce
293 # generate cookie data
294 changing_secret = get_random_bytes(32)
295 self.last_sent_cookie = blake2s(
296 src + sport, digest_size=16, key=changing_secret
299 # encrypt cookie data
300 cipher = ChaCha20_Poly1305.new(key=self.cookie_key, nonce=nonce)
302 ciphertext, tag = cipher.encrypt_and_digest(self.last_sent_cookie)
303 cookie_reply[WireguardCookieReply].encrypted_cookie = ciphertext + tag
305 # prepare cookie reply to be sent
306 cookie_reply = self.mk_tunnel_header(tx_itf, is_ip6) / cookie_reply
310 def consume_cookie(self, p, is_ip6=False):
311 self.verify_header(p, is_ip6)
313 cookie_reply = Wireguard(p[Raw])
315 self._test.assertEqual(cookie_reply[Wireguard].message_type, 3)
316 self._test.assertEqual(cookie_reply[Wireguard].reserved_zero, 0)
317 self._test.assertEqual(
318 cookie_reply[WireguardCookieReply].receiver_index, self.receiver_index
321 # collect info from cookie reply
322 nonce = cookie_reply[WireguardCookieReply].nonce
323 encrypted_cookie = cookie_reply[WireguardCookieReply].encrypted_cookie
324 ciphertext, tag = encrypted_cookie[:16], encrypted_cookie[16:]
326 # decrypt cookie data
327 cipher = ChaCha20_Poly1305.new(key=self.itf.cookie_key, nonce=nonce)
328 cipher.update(self.last_mac1)
329 self.last_received_cookie = cipher.decrypt_and_verify(ciphertext, tag)
331 def mk_handshake(self, tx_itf, is_ip6=False, public_key=None):
332 self.noise.set_as_initiator()
333 self.noise_init(public_key)
335 p = Wireguard() / WireguardInitiation()
337 p[Wireguard].message_type = 1
338 p[Wireguard].reserved_zero = 0
339 p[WireguardInitiation].sender_index = self.receiver_index
341 # some random data for the message
342 # lifted from the noise protocol's wireguard example
343 now = datetime.datetime.now()
346 4611686018427387914 + int(now.timestamp()),
347 int(now.microsecond * 1e3),
349 b = self.noise.write_message(payload=tai)
351 # load noise into init message
352 p[WireguardInitiation].unencrypted_ephemeral = b[0:32]
353 p[WireguardInitiation].encrypted_static = b[32:80]
354 p[WireguardInitiation].encrypted_timestamp = b[80:108]
356 # generate the mac1 hash
357 mac_key = blake2s(b"mac1----" + self.itf.public_key_bytes()).digest()
358 mac1 = blake2s(bytes(p)[0:116], digest_size=16, key=mac_key).digest()
359 p[WireguardInitiation].mac1 = mac1
360 self.last_mac1 = mac1
362 # generate the mac2 hash
363 if self.last_received_cookie:
365 bytes(p)[0:132], digest_size=16, key=self.last_received_cookie
367 p[WireguardInitiation].mac2 = mac2
368 self.last_received_cookie = None
370 p[WireguardInitiation].mac2 = bytearray(16)
372 p = self.mk_tunnel_header(tx_itf, is_ip6) / p
376 def verify_header(self, p, is_ip6=False):
378 self._test.assertEqual(p[IP].src, self.itf.src)
379 self._test.assertEqual(p[IP].dst, self.endpoint)
380 self._test.assert_packet_checksums_valid(p)
382 self._test.assertEqual(p[IPv6].src, self.itf.src)
383 self._test.assertEqual(p[IPv6].dst, self.endpoint)
384 self._test.assert_packet_checksums_valid(p, False)
385 self._test.assertEqual(p[UDP].sport, self.itf.port)
386 self._test.assertEqual(p[UDP].dport, self.port)
388 def consume_init(self, p, tx_itf, is_ip6=False, is_mac2=False):
389 self.noise.set_as_responder()
390 self.noise_init(self.itf.public_key)
391 self.verify_header(p, is_ip6)
393 init = Wireguard(p[Raw])
395 self._test.assertEqual(init[Wireguard].message_type, 1)
396 self._test.assertEqual(init[Wireguard].reserved_zero, 0)
398 self.sender = init[WireguardInitiation].sender_index
400 # validate the mac1 hash
401 mac_key = blake2s(b"mac1----" + public_key_bytes(self.public_key)).digest()
402 mac1 = blake2s(bytes(init)[0:-32], digest_size=16, key=mac_key).digest()
403 self._test.assertEqual(init[WireguardInitiation].mac1, mac1)
405 # validate the mac2 hash
407 self._test.assertNotEqual(init[WireguardInitiation].mac2, bytes([0] * 16))
408 self._test.assertNotEqual(self.last_sent_cookie, None)
410 bytes(init)[0:-16], digest_size=16, key=self.last_sent_cookie
412 self._test.assertEqual(init[WireguardInitiation].mac2, mac2)
413 self.last_sent_cookie = None
415 self._test.assertEqual(init[WireguardInitiation].mac2, bytes([0] * 16))
417 # this passes only unencrypted_ephemeral, encrypted_static,
418 # encrypted_timestamp fields of the init
419 payload = self.noise.read_message(bytes(init)[8:-32])
422 b = self.noise.write_message()
423 mac_key = blake2s(b"mac1----" + public_key_bytes(self.itf.public_key)).digest()
424 resp = Wireguard(message_type=2, reserved_zero=0) / WireguardResponse(
425 sender_index=self.receiver_index,
426 receiver_index=self.sender,
427 unencrypted_ephemeral=b[0:32],
428 encrypted_nothing=b[32:],
430 mac1 = blake2s(bytes(resp)[:-32], digest_size=16, key=mac_key).digest()
431 resp[WireguardResponse].mac1 = mac1
432 self.last_mac1 = mac1
434 resp = self.mk_tunnel_header(tx_itf, is_ip6) / resp
435 self._test.assertTrue(self.noise.handshake_finished)
439 def consume_response(self, p, is_ip6=False):
440 self.verify_header(p, is_ip6)
442 resp = Wireguard(p[Raw])
444 self._test.assertEqual(resp[Wireguard].message_type, 2)
445 self._test.assertEqual(resp[Wireguard].reserved_zero, 0)
446 self._test.assertEqual(
447 resp[WireguardResponse].receiver_index, self.receiver_index
450 self.sender = resp[Wireguard].sender_index
452 payload = self.noise.read_message(bytes(resp)[12:60])
453 self._test.assertEqual(payload, b"")
454 self._test.assertTrue(self.noise.handshake_finished)
456 def decrypt_transport(self, p, is_ip6=False):
457 self.verify_header(p, is_ip6)
459 p = Wireguard(p[Raw])
460 self._test.assertEqual(p[Wireguard].message_type, 4)
461 self._test.assertEqual(p[Wireguard].reserved_zero, 0)
462 self._test.assertEqual(
463 p[WireguardTransport].receiver_index, self.receiver_index
466 d = self.noise.decrypt(p[WireguardTransport].encrypted_encapsulated_packet)
469 def encrypt_transport(self, p):
470 return self.noise.encrypt(bytes(p))
472 def validate_encapped(self, rxs, tx, is_tunnel_ip6=False, is_transport_ip6=False):
475 rx = self.decrypt_transport(rx, is_tunnel_ip6)
476 if is_transport_ip6 is False:
478 # check the original packet is present
479 self._test.assertEqual(rx[IP].dst, tx[IP].dst)
480 self._test.assertEqual(rx[IP].ttl, tx[IP].ttl - 1)
483 # check the original packet is present
484 self._test.assertEqual(rx[IPv6].dst, tx[IPv6].dst)
485 self._test.assertEqual(rx[IPv6].hlim, tx[IPv6].hlim - 1)
489 def want_events(self):
490 self._test.vapi.want_wireguard_peer_events(
493 peer_index=self.index,
494 sw_if_index=self.itf.sw_if_index,
497 def wait_event(self, expect, timeout=5):
498 rv = self._test.vapi.wait_for_event(timeout, "wireguard_peer_event")
499 self._test.assertEqual(rv.flags, expect)
500 self._test.assertEqual(rv.peer_index, self.index)
503 def is_handshake_init(p):
504 wg_p = Wireguard(p[Raw])
506 return wg_p[Wireguard].message_type == 1
510 "wireguard" in config.excluded_plugins, "Exclude Wireguard plugin tests"
513 class TestWg(VppTestCase):
514 """Wireguard Test Case"""
516 error_str = compile(r"Error")
518 wg4_output_node_name = "/err/wg4-output-tun/"
519 wg4_input_node_name = "/err/wg4-input/"
520 wg6_output_node_name = "/err/wg6-output-tun/"
521 wg6_input_node_name = "/err/wg6-input/"
522 kp4_error = wg4_output_node_name + "Keypair error"
523 mac4_error = wg4_input_node_name + "Invalid MAC handshake"
524 peer4_in_err = wg4_input_node_name + "Peer error"
525 peer4_out_err = wg4_output_node_name + "Peer error"
526 kp6_error = wg6_output_node_name + "Keypair error"
527 mac6_error = wg6_input_node_name + "Invalid MAC handshake"
528 peer6_in_err = wg6_input_node_name + "Peer error"
529 peer6_out_err = wg6_output_node_name + "Peer error"
530 cookie_dec4_err = wg4_input_node_name + "Failed during Cookie decryption"
531 cookie_dec6_err = wg6_input_node_name + "Failed during Cookie decryption"
532 ratelimited4_err = wg4_input_node_name + "Handshake ratelimited"
533 ratelimited6_err = wg6_input_node_name + "Handshake ratelimited"
537 super(TestWg, cls).setUpClass()
539 cls.create_pg_interfaces(range(3))
540 for i in cls.pg_interfaces:
548 super(TestWg, cls).tearDownClass()
552 def tearDownClass(cls):
553 super(TestWg, cls).tearDownClass()
556 super(VppTestCase, self).setUp()
557 self.base_kp4_err = self.statistics.get_err_counter(self.kp4_error)
558 self.base_mac4_err = self.statistics.get_err_counter(self.mac4_error)
559 self.base_peer4_in_err = self.statistics.get_err_counter(self.peer4_in_err)
560 self.base_peer4_out_err = self.statistics.get_err_counter(self.peer4_out_err)
561 self.base_kp6_err = self.statistics.get_err_counter(self.kp6_error)
562 self.base_mac6_err = self.statistics.get_err_counter(self.mac6_error)
563 self.base_peer6_in_err = self.statistics.get_err_counter(self.peer6_in_err)
564 self.base_peer6_out_err = self.statistics.get_err_counter(self.peer6_out_err)
565 self.base_cookie_dec4_err = self.statistics.get_err_counter(
568 self.base_cookie_dec6_err = self.statistics.get_err_counter(
571 self.base_ratelimited4_err = self.statistics.get_err_counter(
572 self.ratelimited4_err
574 self.base_ratelimited6_err = self.statistics.get_err_counter(
575 self.ratelimited6_err
578 def send_and_assert_no_replies_ignoring_init(
579 self, intf, pkts, remark="", timeout=None
581 self.pg_send(intf, pkts)
583 def _filter_out_fn(p):
584 return is_ipv6_misc(p) or is_handshake_init(p)
589 for i in self.pg_interfaces:
590 i.assert_nothing_captured(
591 timeout=timeout, remark=remark, filter_out_fn=_filter_out_fn
597 def test_wg_interface(self):
598 """Simple interface creation"""
602 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
604 self.logger.info(self.vapi.cli("sh int"))
607 wg0.remove_vpp_config()
609 def test_handshake_hash(self):
610 """test hashing an init message"""
611 # a init packet generated by linux given the key below
634 b = bytearray.fromhex(h)
637 pubb = base64.b64decode("aRuHFTTxICIQNefp05oKWlJv3zgKxb8+WW7JJMh0jyM=")
638 pub = X25519PublicKey.from_public_bytes(pubb)
640 self.assertEqual(pubb, public_key_bytes(pub))
642 # strip the macs and build a new packet
644 mac_key = blake2s(b"mac1----" + public_key_bytes(pub)).digest()
645 init += blake2s(init, digest_size=16, key=mac_key).digest()
648 act = Wireguard(init)
650 self.assertEqual(tgt, act)
652 def _test_wg_send_cookie_tmpl(self, is_resp, is_ip6):
655 # create wg interface
657 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
661 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
665 self.pg_enable_capture(self.pg_interfaces)
671 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
675 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
677 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
680 # skip the first automatic handshake
681 self.pg1.get_capture(1, timeout=HANDSHAKE_JITTER)
683 # prepare and send a handshake initiation
684 # expect the peer to send a handshake response
685 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
686 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
688 # wait for the peer to send a handshake initiation
689 rxs = self.pg1.get_capture(1, timeout=2)
691 # prepare and send a wrong cookie reply
692 # expect no replies and the cookie error incremented
693 cookie = peer_1.mk_cookie(rxs[0], self.pg1, is_resp=is_resp, is_ip6=is_ip6)
694 cookie.nonce = b"1234567890"
695 self.send_and_assert_no_replies(self.pg1, [cookie], timeout=0.1)
698 self.base_cookie_dec6_err + 1,
699 self.statistics.get_err_counter(self.cookie_dec6_err),
703 self.base_cookie_dec4_err + 1,
704 self.statistics.get_err_counter(self.cookie_dec4_err),
707 # prepare and send a correct cookie reply
708 cookie = peer_1.mk_cookie(rxs[0], self.pg1, is_resp=is_resp, is_ip6=is_ip6)
709 self.pg_send(self.pg1, [cookie])
711 # wait for the peer to send a handshake initiation with mac2 set
712 rxs = self.pg1.get_capture(1, timeout=6)
714 # verify the initiation and its mac2
715 peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6, is_mac2=True)
718 peer_1.remove_vpp_config()
719 wg0.remove_vpp_config()
721 def test_wg_send_cookie_on_init_v4(self):
722 """Send cookie on handshake initiation (v4)"""
723 self._test_wg_send_cookie_tmpl(is_resp=False, is_ip6=False)
725 def test_wg_send_cookie_on_init_v6(self):
726 """Send cookie on handshake initiation (v6)"""
727 self._test_wg_send_cookie_tmpl(is_resp=False, is_ip6=True)
729 def test_wg_send_cookie_on_resp_v4(self):
730 """Send cookie on handshake response (v4)"""
731 self._test_wg_send_cookie_tmpl(is_resp=True, is_ip6=False)
733 def test_wg_send_cookie_on_resp_v6(self):
734 """Send cookie on handshake response (v6)"""
735 self._test_wg_send_cookie_tmpl(is_resp=True, is_ip6=True)
737 def _test_wg_receive_cookie_tmpl(self, is_resp, is_ip6):
740 # create wg interface
742 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
746 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
750 self.pg_enable_capture(self.pg_interfaces)
756 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
760 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
762 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
765 # wait for the peer to send a handshake initiation
766 rxs = self.pg1.get_capture(1, timeout=2)
767 # prepare and send a bunch of handshake responses
768 # expect to switch to under load state
769 resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6)
770 txs = [resp] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
771 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
772 # reset noise to be able to turn into initiator later
775 # skip the first automatic handshake
776 self.pg1.get_capture(1, timeout=HANDSHAKE_JITTER)
778 # prepare and send a bunch of handshake initiations
779 # expect to switch to under load state
780 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
781 txs = [init] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
782 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
784 # expect the peer to send a cookie reply
785 peer_1.consume_cookie(rxs[-1], is_ip6=is_ip6)
787 # prepare and send a handshake initiation with wrong mac2
788 # expect a cookie reply
789 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
790 init.mac2 = b"1234567890"
791 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
792 peer_1.consume_cookie(rxs[0], is_ip6=is_ip6)
794 # prepare and send a handshake initiation with correct mac2
795 # expect a handshake response
796 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
797 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
799 # verify the response
800 peer_1.consume_response(rxs[0], is_ip6=is_ip6)
802 # clear up under load state
803 self.sleep(UNDER_LOAD_INTERVAL)
806 peer_1.remove_vpp_config()
807 wg0.remove_vpp_config()
809 def test_wg_receive_cookie_on_init_v4(self):
810 """Receive cookie on handshake initiation (v4)"""
811 self._test_wg_receive_cookie_tmpl(is_resp=False, is_ip6=False)
813 def test_wg_receive_cookie_on_init_v6(self):
814 """Receive cookie on handshake initiation (v6)"""
815 self._test_wg_receive_cookie_tmpl(is_resp=False, is_ip6=True)
817 def test_wg_receive_cookie_on_resp_v4(self):
818 """Receive cookie on handshake response (v4)"""
819 self._test_wg_receive_cookie_tmpl(is_resp=True, is_ip6=False)
821 def test_wg_receive_cookie_on_resp_v6(self):
822 """Receive cookie on handshake response (v6)"""
823 self._test_wg_receive_cookie_tmpl(is_resp=True, is_ip6=True)
825 def test_wg_under_load_interval(self):
826 """Under load interval"""
829 # create wg interface
830 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
834 self.pg_enable_capture(self.pg_interfaces)
839 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
841 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
843 # skip the first automatic handshake
844 self.pg1.get_capture(1, timeout=HANDSHAKE_JITTER)
846 # prepare and send a bunch of handshake initiations
847 # expect to switch to under load state
848 init = peer_1.mk_handshake(self.pg1)
849 txs = [init] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
850 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
852 # expect the peer to send a cookie reply
853 peer_1.consume_cookie(rxs[-1])
855 # sleep till the next counting interval
856 # expect under load state is still active
857 self.sleep(HANDSHAKE_COUNTING_INTERVAL)
859 # prepare and send a handshake initiation with wrong mac2
860 # expect a cookie reply
861 init = peer_1.mk_handshake(self.pg1)
862 init.mac2 = b"1234567890"
863 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
864 peer_1.consume_cookie(rxs[0])
866 # sleep till the end of being under load
867 # expect under load state is over
868 self.sleep(UNDER_LOAD_INTERVAL - HANDSHAKE_COUNTING_INTERVAL)
870 # prepare and send a handshake initiation with wrong mac2
871 # expect a handshake response
872 init = peer_1.mk_handshake(self.pg1)
873 init.mac2 = b"1234567890"
874 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
876 # verify the response
877 peer_1.consume_response(rxs[0])
880 peer_1.remove_vpp_config()
881 wg0.remove_vpp_config()
883 def _test_wg_handshake_ratelimiting_tmpl(self, is_ip6):
886 # create wg interface
888 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
892 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
896 self.pg_enable_capture(self.pg_interfaces)
902 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
906 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
908 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
910 # skip the first automatic handshake
911 self.pg1.get_capture(1, timeout=HANDSHAKE_JITTER)
913 # prepare and send a bunch of handshake initiations
914 # expect to switch to under load state
915 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
916 txs = [init] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
917 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
919 # expect the peer to send a cookie reply
920 peer_1.consume_cookie(rxs[-1], is_ip6=is_ip6)
922 # prepare and send a bunch of handshake initiations with correct mac2
923 # expect a handshake response and then ratelimiting
925 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
926 txs = [init] * (HANDSHAKE_NUM_BEFORE_RATELIMITING + NUM_TO_REJECT)
928 # TODO: Deterimine why no handshake response is sent back if test is
929 # not run in as part of the test suite. It fails only very occasionally
932 # Until then, if no response, don't fail trying to verify it.
933 # The error counter test still verifies that the correct number of
934 # handshake initiaions are ratelimited.
936 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
939 f"{self._testMethodDoc}: send_and_expect_some() failed to get any response packets."
946 self.base_ratelimited6_err + NUM_TO_REJECT,
947 self.statistics.get_err_counter(self.ratelimited6_err),
951 self.base_ratelimited4_err + NUM_TO_REJECT,
952 self.statistics.get_err_counter(self.ratelimited4_err),
955 # verify the response
957 peer_1.consume_response(rxs[0], is_ip6=is_ip6)
959 # clear up under load state
960 self.sleep(UNDER_LOAD_INTERVAL)
963 peer_1.remove_vpp_config()
964 wg0.remove_vpp_config()
966 def test_wg_handshake_ratelimiting_v4(self):
967 """Handshake ratelimiting (v4)"""
968 self._test_wg_handshake_ratelimiting_tmpl(is_ip6=False)
970 def test_wg_handshake_ratelimiting_v6(self):
971 """Handshake ratelimiting (v6)"""
972 self._test_wg_handshake_ratelimiting_tmpl(is_ip6=True)
974 def test_wg_handshake_ratelimiting_multi_peer(self):
975 """Handshake ratelimiting (multiple peer)"""
978 # create wg interface
979 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
983 self.pg_enable_capture(self.pg_interfaces)
988 self.pg1.generate_remote_hosts(NUM_PEERS)
989 self.pg1.configure_ipv4_neighbors()
992 self, wg0, self.pg1.remote_hosts[0].ip4, port + 1, ["10.11.3.0/24"]
995 self, wg0, self.pg1.remote_hosts[1].ip4, port + 1, ["10.11.4.0/24"]
997 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 2)
999 # skip the first automatic handshake
1000 self.pg1.get_capture(NUM_PEERS, timeout=HANDSHAKE_JITTER)
1002 # (peer_1) prepare and send a bunch of handshake initiations
1003 # expect not to switch to under load state
1004 init_1 = peer_1.mk_handshake(self.pg1)
1005 txs = [init_1] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
1006 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
1008 # (peer_1) expect the peer to send a handshake response
1009 peer_1.consume_response(rxs[0])
1010 peer_1.noise_reset()
1012 # (peer_1) send another bunch of handshake initiations
1013 # expect to switch to under load state
1014 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
1016 # (peer_1) expect the peer to send a cookie reply
1017 peer_1.consume_cookie(rxs[-1])
1019 # (peer_2) prepare and send a handshake initiation
1020 # expect a cookie reply
1021 init_2 = peer_2.mk_handshake(self.pg1)
1022 rxs = self.send_and_expect(self.pg1, [init_2], self.pg1)
1023 peer_2.consume_cookie(rxs[0])
1025 # (peer_1) (peer_2) prepare and send a bunch of handshake initiations with correct mac2
1026 # expect a handshake response and then ratelimiting
1027 PEER_1_NUM_TO_REJECT = 2
1028 PEER_2_NUM_TO_REJECT = 5
1029 init_1 = peer_1.mk_handshake(self.pg1)
1030 txs = [init_1] * (HANDSHAKE_NUM_BEFORE_RATELIMITING + PEER_1_NUM_TO_REJECT)
1031 init_2 = peer_2.mk_handshake(self.pg1)
1032 txs += [init_2] * (HANDSHAKE_NUM_BEFORE_RATELIMITING + PEER_2_NUM_TO_REJECT)
1033 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
1036 self.base_ratelimited4_err + PEER_1_NUM_TO_REJECT
1037 < self.statistics.get_err_counter(self.ratelimited4_err)
1038 <= self.base_ratelimited4_err + PEER_1_NUM_TO_REJECT + PEER_2_NUM_TO_REJECT
1041 # (peer_1) (peer_2) verify the response
1042 peer_1.consume_response(rxs[0])
1043 peer_2.consume_response(rxs[1])
1045 # clear up under load state
1046 self.sleep(UNDER_LOAD_INTERVAL)
1049 peer_1.remove_vpp_config()
1050 peer_2.remove_vpp_config()
1051 wg0.remove_vpp_config()
1053 def _test_wg_peer_roaming_on_handshake_tmpl(self, is_endpoint_set, is_resp, is_ip6):
1056 # create wg interface
1058 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1062 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1066 self.pg_enable_capture(self.pg_interfaces)
1069 # create more remote hosts
1070 NUM_REMOTE_HOSTS = 2
1071 self.pg1.generate_remote_hosts(NUM_REMOTE_HOSTS)
1073 self.pg1.configure_ipv6_neighbors()
1075 self.pg1.configure_ipv4_neighbors()
1082 endpoint=self.pg1.remote_hosts[0].ip6 if is_endpoint_set else "::",
1083 port=port + 1 if is_endpoint_set else 0,
1084 allowed_ips=["1::3:0/112"],
1090 endpoint=self.pg1.remote_hosts[0].ip4 if is_endpoint_set else "0.0.0.0",
1091 port=port + 1 if is_endpoint_set else 0,
1092 allowed_ips=["10.11.3.0/24"],
1094 self.assertTrue(peer_1.query_vpp_config())
1097 # wait for the peer to send a handshake initiation
1098 rxs = self.pg1.get_capture(1, timeout=2)
1099 # prepare a handshake response
1100 resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6)
1103 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip6, port + 100)
1104 resp[IPv6].src, resp[UDP].sport = peer_1.endpoint, peer_1.port
1106 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip4, port + 100)
1107 resp[IP].src, resp[UDP].sport = peer_1.endpoint, peer_1.port
1108 # send the handshake response
1109 # expect a keepalive message sent to the new endpoint
1110 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1111 # verify the keepalive message
1112 b = peer_1.decrypt_transport(rxs[0], is_ip6=is_ip6)
1113 self.assertEqual(0, len(b))
1117 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip6, port + 100)
1119 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip4, port + 100)
1120 # prepare and send a handshake initiation
1121 # expect a handshake response sent to the new endpoint
1122 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
1123 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
1124 # verify the response
1125 peer_1.consume_response(rxs[0], is_ip6=is_ip6)
1126 self.assertTrue(peer_1.query_vpp_config())
1129 peer_1.remove_vpp_config()
1130 wg0.remove_vpp_config()
1132 def test_wg_peer_roaming_on_init_v4(self):
1133 """Peer roaming on handshake initiation (v4)"""
1134 self._test_wg_peer_roaming_on_handshake_tmpl(
1135 is_endpoint_set=False, is_resp=False, is_ip6=False
1138 def test_wg_peer_roaming_on_init_v6(self):
1139 """Peer roaming on handshake initiation (v6)"""
1140 self._test_wg_peer_roaming_on_handshake_tmpl(
1141 is_endpoint_set=False, is_resp=False, is_ip6=True
1144 def test_wg_peer_roaming_on_resp_v4(self):
1145 """Peer roaming on handshake response (v4)"""
1146 self._test_wg_peer_roaming_on_handshake_tmpl(
1147 is_endpoint_set=True, is_resp=True, is_ip6=False
1150 def test_wg_peer_roaming_on_resp_v6(self):
1151 """Peer roaming on handshake response (v6)"""
1152 self._test_wg_peer_roaming_on_handshake_tmpl(
1153 is_endpoint_set=True, is_resp=True, is_ip6=True
1156 def _test_wg_peer_roaming_on_data_tmpl(self, is_async, is_ip6):
1157 self.vapi.wg_set_async_mode(is_async)
1160 # create wg interface
1162 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1166 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1170 self.pg_enable_capture(self.pg_interfaces)
1173 # create more remote hosts
1174 NUM_REMOTE_HOSTS = 2
1175 self.pg1.generate_remote_hosts(NUM_REMOTE_HOSTS)
1177 self.pg1.configure_ipv6_neighbors()
1179 self.pg1.configure_ipv4_neighbors()
1184 self, wg0, self.pg1.remote_hosts[0].ip6, port + 1, ["1::3:0/112"]
1188 self, wg0, self.pg1.remote_hosts[0].ip4, port + 1, ["10.11.3.0/24"]
1190 self.assertTrue(peer_1.query_vpp_config())
1192 # create a route to rewrite traffic into the wg interface
1195 self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
1199 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1202 # wait for the peer to send a handshake initiation
1203 rxs = self.pg1.get_capture(1, timeout=2)
1205 # prepare and send a handshake response
1206 # expect a keepalive message
1207 resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6)
1208 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1210 # verify the keepalive message
1211 b = peer_1.decrypt_transport(rxs[0], is_ip6=is_ip6)
1212 self.assertEqual(0, len(b))
1216 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip6, port + 100)
1218 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip4, port + 100)
1220 # prepare and send a data packet
1221 # expect endpoint change
1223 ip_header = IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1225 ip_header = IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1227 peer_1.mk_tunnel_header(self.pg1, is_ip6=is_ip6)
1228 / Wireguard(message_type=4, reserved_zero=0)
1229 / WireguardTransport(
1230 receiver_index=peer_1.sender,
1232 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1233 ip_header / UDP(sport=222, dport=223) / Raw()
1237 rxs = self.send_and_expect(self.pg1, [data], self.pg0)
1239 self.assertEqual(rxs[0][IPv6].dst, self.pg0.remote_ip6)
1240 self.assertEqual(rxs[0][IPv6].hlim, 19)
1242 self.assertEqual(rxs[0][IP].dst, self.pg0.remote_ip4)
1243 self.assertEqual(rxs[0][IP].ttl, 19)
1244 self.assertTrue(peer_1.query_vpp_config())
1246 # prepare and send a packet that will be rewritten into the wg interface
1247 # expect a data packet sent to the new endpoint
1249 ip_header = IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1251 ip_header = IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1253 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1255 / UDP(sport=555, dport=556)
1258 rxs = self.send_and_expect(self.pg0, [p], self.pg1)
1260 # verify the data packet
1261 peer_1.validate_encapped(rxs, p, is_tunnel_ip6=is_ip6, is_transport_ip6=is_ip6)
1264 r1.remove_vpp_config()
1265 peer_1.remove_vpp_config()
1266 wg0.remove_vpp_config()
1268 def test_wg_peer_roaming_on_data_v4_sync(self):
1269 """Peer roaming on data packet (v4, sync)"""
1270 self._test_wg_peer_roaming_on_data_tmpl(is_async=False, is_ip6=False)
1272 def test_wg_peer_roaming_on_data_v6_sync(self):
1273 """Peer roaming on data packet (v6, sync)"""
1274 self._test_wg_peer_roaming_on_data_tmpl(is_async=False, is_ip6=True)
1276 def test_wg_peer_roaming_on_data_v4_async(self):
1277 """Peer roaming on data packet (v4, async)"""
1278 self._test_wg_peer_roaming_on_data_tmpl(is_async=True, is_ip6=False)
1280 def test_wg_peer_roaming_on_data_v6_async(self):
1281 """Peer roaming on data packet (v6, async)"""
1282 self._test_wg_peer_roaming_on_data_tmpl(is_async=True, is_ip6=True)
1284 def test_wg_peer_resp(self):
1285 """Send handshake response IPv4 tunnel"""
1289 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1293 self.pg_enable_capture(self.pg_interfaces)
1297 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
1299 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1302 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1305 # wait for the peer to send a handshake
1306 rx = self.pg1.get_capture(1, timeout=2)
1308 # consume the handshake in the noise protocol and
1309 # generate the response
1310 resp = peer_1.consume_init(rx[0], self.pg1)
1312 # send the response, get keepalive
1313 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1316 b = peer_1.decrypt_transport(rx)
1317 self.assertEqual(0, len(b))
1319 # send a packets that are routed into the tunnel
1321 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1322 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1323 / UDP(sport=555, dport=556)
1327 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1329 peer_1.validate_encapped(rxs, p)
1331 # send packets into the tunnel, expect to receive them on
1335 peer_1.mk_tunnel_header(self.pg1)
1336 / Wireguard(message_type=4, reserved_zero=0)
1337 / WireguardTransport(
1338 receiver_index=peer_1.sender,
1340 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1342 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1343 / UDP(sport=222, dport=223)
1349 for ii in range(255)
1352 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1355 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1356 self.assertEqual(rx[IP].ttl, 19)
1358 r1.remove_vpp_config()
1359 peer_1.remove_vpp_config()
1360 wg0.remove_vpp_config()
1362 def test_wg_peer_resp_ipv6(self):
1363 """Send handshake response IPv6 tunnel"""
1367 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1371 self.pg_enable_capture(self.pg_interfaces)
1375 self, wg0, self.pg1.remote_ip6, port + 1, ["10.11.3.0/24"]
1377 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1380 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1383 # wait for the peer to send a handshake
1384 rx = self.pg1.get_capture(1, timeout=2)
1386 # consume the handshake in the noise protocol and
1387 # generate the response
1388 resp = peer_1.consume_init(rx[0], self.pg1, is_ip6=True)
1390 # send the response, get keepalive
1391 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1394 b = peer_1.decrypt_transport(rx, True)
1395 self.assertEqual(0, len(b))
1397 # send a packets that are routed into the tunnel
1399 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1400 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1401 / UDP(sport=555, dport=556)
1405 rxs = self.send_and_expect(self.pg0, p * 2, self.pg1)
1406 peer_1.validate_encapped(rxs, p, True)
1408 # send packets into the tunnel, expect to receive them on
1412 peer_1.mk_tunnel_header(self.pg1, True)
1413 / Wireguard(message_type=4, reserved_zero=0)
1414 / WireguardTransport(
1415 receiver_index=peer_1.sender,
1417 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1419 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1420 / UDP(sport=222, dport=223)
1426 for ii in range(255)
1429 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1432 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1433 self.assertEqual(rx[IP].ttl, 19)
1435 r1.remove_vpp_config()
1436 peer_1.remove_vpp_config()
1437 wg0.remove_vpp_config()
1439 def test_wg_peer_v4o4(self):
1445 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1450 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
1452 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1455 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1458 self, "20.22.3.0", 24, [VppRoutePath("20.22.3.1", wg0.sw_if_index)]
1461 # route a packet into the wg interface
1462 # use the allowed-ip prefix
1463 # this is dropped because the peer is not initiated
1465 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1466 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1467 / UDP(sport=555, dport=556)
1470 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1472 self.base_kp4_err + 1, self.statistics.get_err_counter(self.kp4_error)
1475 # route a packet into the wg interface
1476 # use a not allowed-ip prefix
1477 # this is dropped because there is no matching peer
1479 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1480 / IP(src=self.pg0.remote_ip4, dst="20.22.3.2")
1481 / UDP(sport=555, dport=556)
1484 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1486 self.base_peer4_out_err + 1,
1487 self.statistics.get_err_counter(self.peer4_out_err),
1490 # send a handsake from the peer with an invalid MAC
1491 p = peer_1.mk_handshake(self.pg1)
1492 p[WireguardInitiation].mac1 = b"foobar"
1493 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1495 self.base_mac4_err + 1, self.statistics.get_err_counter(self.mac4_error)
1498 # send a handsake from the peer but signed by the wrong key.
1499 p = peer_1.mk_handshake(
1500 self.pg1, False, X25519PrivateKey.generate().public_key()
1502 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1504 self.base_peer4_in_err + 1,
1505 self.statistics.get_err_counter(self.peer4_in_err),
1508 # send a valid handsake init for which we expect a response
1509 p = peer_1.mk_handshake(self.pg1)
1511 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1513 peer_1.consume_response(rx[0])
1515 # route a packet into the wg interface
1516 # this is dropped because the peer is still not initiated
1518 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1519 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1520 / UDP(sport=555, dport=556)
1523 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1525 self.base_kp4_err + 2, self.statistics.get_err_counter(self.kp4_error)
1528 # send a data packet from the peer through the tunnel
1529 # this completes the handshake
1531 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1532 / UDP(sport=222, dport=223)
1535 d = peer_1.encrypt_transport(p)
1536 p = peer_1.mk_tunnel_header(self.pg1) / (
1537 Wireguard(message_type=4, reserved_zero=0)
1538 / WireguardTransport(
1539 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1542 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1545 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1546 self.assertEqual(rx[IP].ttl, 19)
1548 # send a packets that are routed into the tunnel
1550 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1551 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1552 / UDP(sport=555, dport=556)
1556 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1559 rx = IP(peer_1.decrypt_transport(rx))
1561 # check the original packet is present
1562 self.assertEqual(rx[IP].dst, p[IP].dst)
1563 self.assertEqual(rx[IP].ttl, p[IP].ttl - 1)
1565 # send packets into the tunnel, expect to receive them on
1569 peer_1.mk_tunnel_header(self.pg1)
1570 / Wireguard(message_type=4, reserved_zero=0)
1571 / WireguardTransport(
1572 receiver_index=peer_1.sender,
1574 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1576 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1577 / UDP(sport=222, dport=223)
1583 for ii in range(255)
1586 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1589 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1590 self.assertEqual(rx[IP].ttl, 19)
1592 r1.remove_vpp_config()
1593 r2.remove_vpp_config()
1594 peer_1.remove_vpp_config()
1595 wg0.remove_vpp_config()
1597 def test_wg_peer_v6o6(self):
1603 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1608 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
1610 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1613 self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
1616 self, "22::3:0", 112, [VppRoutePath("22::3:1", wg0.sw_if_index)]
1619 # route a packet into the wg interface
1620 # use the allowed-ip prefix
1621 # this is dropped because the peer is not initiated
1624 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1625 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1626 / UDP(sport=555, dport=556)
1629 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1632 self.base_kp6_err + 1, self.statistics.get_err_counter(self.kp6_error)
1635 # route a packet into the wg interface
1636 # use a not allowed-ip prefix
1637 # this is dropped because there is no matching peer
1639 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1640 / IPv6(src=self.pg0.remote_ip6, dst="22::3:2")
1641 / UDP(sport=555, dport=556)
1644 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1646 self.base_peer6_out_err + 1,
1647 self.statistics.get_err_counter(self.peer6_out_err),
1650 # send a handsake from the peer with an invalid MAC
1651 p = peer_1.mk_handshake(self.pg1, True)
1652 p[WireguardInitiation].mac1 = b"foobar"
1653 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1656 self.base_mac6_err + 1, self.statistics.get_err_counter(self.mac6_error)
1659 # send a handsake from the peer but signed by the wrong key.
1660 p = peer_1.mk_handshake(
1661 self.pg1, True, X25519PrivateKey.generate().public_key()
1663 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1665 self.base_peer6_in_err + 1,
1666 self.statistics.get_err_counter(self.peer6_in_err),
1669 # send a valid handsake init for which we expect a response
1670 p = peer_1.mk_handshake(self.pg1, True)
1672 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1674 peer_1.consume_response(rx[0], True)
1676 # route a packet into the wg interface
1677 # this is dropped because the peer is still not initiated
1679 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1680 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1681 / UDP(sport=555, dport=556)
1684 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1686 self.base_kp6_err + 2, self.statistics.get_err_counter(self.kp6_error)
1689 # send a data packet from the peer through the tunnel
1690 # this completes the handshake
1692 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1693 / UDP(sport=222, dport=223)
1696 d = peer_1.encrypt_transport(p)
1697 p = peer_1.mk_tunnel_header(self.pg1, True) / (
1698 Wireguard(message_type=4, reserved_zero=0)
1699 / WireguardTransport(
1700 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1703 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1706 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1707 self.assertEqual(rx[IPv6].hlim, 19)
1709 # send a packets that are routed into the tunnel
1711 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1712 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1713 / UDP(sport=555, dport=556)
1717 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1720 rx = IPv6(peer_1.decrypt_transport(rx, True))
1722 # check the original packet is present
1723 self.assertEqual(rx[IPv6].dst, p[IPv6].dst)
1724 self.assertEqual(rx[IPv6].hlim, p[IPv6].hlim - 1)
1726 # send packets into the tunnel, expect to receive them on
1730 peer_1.mk_tunnel_header(self.pg1, True)
1731 / Wireguard(message_type=4, reserved_zero=0)
1732 / WireguardTransport(
1733 receiver_index=peer_1.sender,
1735 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1737 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1738 / UDP(sport=222, dport=223)
1744 for ii in range(255)
1747 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1750 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1751 self.assertEqual(rx[IPv6].hlim, 19)
1753 r1.remove_vpp_config()
1754 r2.remove_vpp_config()
1755 peer_1.remove_vpp_config()
1756 wg0.remove_vpp_config()
1758 def test_wg_peer_v6o4(self):
1764 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1769 self, wg0, self.pg1.remote_ip4, port + 1, ["1::3:0/112"]
1771 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1774 self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
1777 # route a packet into the wg interface
1778 # use the allowed-ip prefix
1779 # this is dropped because the peer is not initiated
1781 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1782 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1783 / UDP(sport=555, dport=556)
1786 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1788 self.base_kp6_err + 1, self.statistics.get_err_counter(self.kp6_error)
1791 # send a handsake from the peer with an invalid MAC
1792 p = peer_1.mk_handshake(self.pg1)
1793 p[WireguardInitiation].mac1 = b"foobar"
1794 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1797 self.base_mac4_err + 1, self.statistics.get_err_counter(self.mac4_error)
1800 # send a handsake from the peer but signed by the wrong key.
1801 p = peer_1.mk_handshake(
1802 self.pg1, False, X25519PrivateKey.generate().public_key()
1804 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1806 self.base_peer4_in_err + 1,
1807 self.statistics.get_err_counter(self.peer4_in_err),
1810 # send a valid handsake init for which we expect a response
1811 p = peer_1.mk_handshake(self.pg1)
1813 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1815 peer_1.consume_response(rx[0])
1817 # route a packet into the wg interface
1818 # this is dropped because the peer is still not initiated
1820 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1821 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1822 / UDP(sport=555, dport=556)
1825 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1827 self.base_kp6_err + 2, self.statistics.get_err_counter(self.kp6_error)
1830 # send a data packet from the peer through the tunnel
1831 # this completes the handshake
1833 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1834 / UDP(sport=222, dport=223)
1837 d = peer_1.encrypt_transport(p)
1838 p = peer_1.mk_tunnel_header(self.pg1) / (
1839 Wireguard(message_type=4, reserved_zero=0)
1840 / WireguardTransport(
1841 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1844 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1847 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1848 self.assertEqual(rx[IPv6].hlim, 19)
1850 # send a packets that are routed into the tunnel
1852 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1853 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1854 / UDP(sport=555, dport=556)
1858 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1861 rx = IPv6(peer_1.decrypt_transport(rx))
1863 # check the original packet is present
1864 self.assertEqual(rx[IPv6].dst, p[IPv6].dst)
1865 self.assertEqual(rx[IPv6].hlim, p[IPv6].hlim - 1)
1867 # send packets into the tunnel, expect to receive them on
1871 peer_1.mk_tunnel_header(self.pg1)
1872 / Wireguard(message_type=4, reserved_zero=0)
1873 / WireguardTransport(
1874 receiver_index=peer_1.sender,
1876 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1878 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1879 / UDP(sport=222, dport=223)
1885 for ii in range(255)
1888 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1891 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1892 self.assertEqual(rx[IPv6].hlim, 19)
1894 r1.remove_vpp_config()
1895 peer_1.remove_vpp_config()
1896 wg0.remove_vpp_config()
1898 def test_wg_peer_v4o6(self):
1904 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1909 self, wg0, self.pg1.remote_ip6, port + 1, ["10.11.3.0/24"]
1911 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1914 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1917 # route a packet into the wg interface
1918 # use the allowed-ip prefix
1919 # this is dropped because the peer is not initiated
1921 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1922 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1923 / UDP(sport=555, dport=556)
1926 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1928 self.base_kp4_err + 1, self.statistics.get_err_counter(self.kp4_error)
1931 # send a handsake from the peer with an invalid MAC
1932 p = peer_1.mk_handshake(self.pg1, True)
1933 p[WireguardInitiation].mac1 = b"foobar"
1934 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1936 self.base_mac6_err + 1, self.statistics.get_err_counter(self.mac6_error)
1939 # send a handsake from the peer but signed by the wrong key.
1940 p = peer_1.mk_handshake(
1941 self.pg1, True, X25519PrivateKey.generate().public_key()
1943 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1945 self.base_peer6_in_err + 1,
1946 self.statistics.get_err_counter(self.peer6_in_err),
1949 # send a valid handsake init for which we expect a response
1950 p = peer_1.mk_handshake(self.pg1, True)
1952 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1954 peer_1.consume_response(rx[0], True)
1956 # route a packet into the wg interface
1957 # this is dropped because the peer is still not initiated
1959 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1960 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1961 / UDP(sport=555, dport=556)
1964 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1966 self.base_kp4_err + 2, self.statistics.get_err_counter(self.kp4_error)
1969 # send a data packet from the peer through the tunnel
1970 # this completes the handshake
1972 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1973 / UDP(sport=222, dport=223)
1976 d = peer_1.encrypt_transport(p)
1977 p = peer_1.mk_tunnel_header(self.pg1, True) / (
1978 Wireguard(message_type=4, reserved_zero=0)
1979 / WireguardTransport(
1980 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1983 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1986 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1987 self.assertEqual(rx[IP].ttl, 19)
1989 # send a packets that are routed into the tunnel
1991 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1992 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1993 / UDP(sport=555, dport=556)
1997 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
2000 rx = IP(peer_1.decrypt_transport(rx, True))
2002 # check the original packet is present
2003 self.assertEqual(rx[IP].dst, p[IP].dst)
2004 self.assertEqual(rx[IP].ttl, p[IP].ttl - 1)
2006 # send packets into the tunnel, expect to receive them on
2010 peer_1.mk_tunnel_header(self.pg1, True)
2011 / Wireguard(message_type=4, reserved_zero=0)
2012 / WireguardTransport(
2013 receiver_index=peer_1.sender,
2015 encrypted_encapsulated_packet=peer_1.encrypt_transport(
2017 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
2018 / UDP(sport=222, dport=223)
2024 for ii in range(255)
2027 rxs = self.send_and_expect(self.pg1, p, self.pg0)
2030 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
2031 self.assertEqual(rx[IP].ttl, 19)
2033 r1.remove_vpp_config()
2034 peer_1.remove_vpp_config()
2035 wg0.remove_vpp_config()
2037 def test_wg_multi_peer(self):
2038 """multiple peer setup"""
2042 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2043 wg1 = VppWgInterface(self, self.pg2.local_ip4, port + 1).add_vpp_config()
2047 # Check peer counter
2048 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 0)
2050 self.pg_enable_capture(self.pg_interfaces)
2053 # Create many peers on sencond interface
2055 self.pg2.generate_remote_hosts(NUM_PEERS)
2056 self.pg2.configure_ipv4_neighbors()
2057 self.pg1.generate_remote_hosts(NUM_PEERS)
2058 self.pg1.configure_ipv4_neighbors()
2064 for i in range(NUM_PEERS):
2069 self.pg1.remote_hosts[i].ip4,
2071 ["10.0.%d.4/32" % i],
2079 [VppRoutePath(self.pg1.remote_hosts[i].ip4, wg0.sw_if_index)],
2087 self.pg2.remote_hosts[i].ip4,
2089 ["10.100.%d.4/32" % i],
2097 [VppRoutePath(self.pg2.remote_hosts[i].ip4, wg1.sw_if_index)],
2101 self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_PEERS * 2)
2103 self.logger.info(self.vapi.cli("show wireguard peer"))
2104 self.logger.info(self.vapi.cli("show wireguard interface"))
2105 self.logger.info(self.vapi.cli("show adj 37"))
2106 self.logger.info(self.vapi.cli("sh ip fib 172.16.3.17"))
2107 self.logger.info(self.vapi.cli("sh ip fib 10.11.3.0"))
2111 r.remove_vpp_config()
2113 r.remove_vpp_config()
2117 self.assertTrue(p.query_vpp_config())
2118 p.remove_vpp_config()
2120 self.assertTrue(p.query_vpp_config())
2121 p.remove_vpp_config()
2123 wg0.remove_vpp_config()
2124 wg1.remove_vpp_config()
2126 def test_wg_multi_interface(self):
2127 """Multi-tunnel on the same port"""
2130 # Create many wireguard interfaces
2132 self.pg1.generate_remote_hosts(NUM_IFS)
2133 self.pg1.configure_ipv4_neighbors()
2134 self.pg0.generate_remote_hosts(NUM_IFS)
2135 self.pg0.configure_ipv4_neighbors()
2137 self.pg_enable_capture(self.pg_interfaces)
2140 # Create interfaces with a peer on each
2144 for i in range(NUM_IFS):
2145 # Use the same port for each interface
2146 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2154 self.pg1.remote_hosts[i].ip4,
2156 ["10.0.%d.0/24" % i],
2165 [VppRoutePath("10.0.%d.4" % i, wg0.sw_if_index)],
2169 self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_IFS)
2171 # skip the first automatic handshake
2172 self.pg1.get_capture(NUM_IFS, timeout=HANDSHAKE_JITTER)
2174 for i in range(NUM_IFS):
2175 # send a valid handsake init for which we expect a response
2176 p = peers[i].mk_handshake(self.pg1)
2177 rx = self.send_and_expect(self.pg1, [p], self.pg1)
2178 peers[i].consume_response(rx[0])
2180 # send a data packet from the peer through the tunnel
2181 # this completes the handshake
2183 IP(src="10.0.%d.4" % i, dst=self.pg0.remote_hosts[i].ip4, ttl=20)
2184 / UDP(sport=222, dport=223)
2187 d = peers[i].encrypt_transport(p)
2188 p = peers[i].mk_tunnel_header(self.pg1) / (
2189 Wireguard(message_type=4, reserved_zero=0)
2190 / WireguardTransport(
2191 receiver_index=peers[i].sender,
2193 encrypted_encapsulated_packet=d,
2196 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
2198 self.assertEqual(rx[IP].dst, self.pg0.remote_hosts[i].ip4)
2199 self.assertEqual(rx[IP].ttl, 19)
2201 # send a packets that are routed into the tunnel
2202 for i in range(NUM_IFS):
2204 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2205 / IP(src=self.pg0.remote_hosts[i].ip4, dst="10.0.%d.4" % i)
2206 / UDP(sport=555, dport=556)
2210 rxs = self.send_and_expect(self.pg0, p * 64, self.pg1)
2213 rx = IP(peers[i].decrypt_transport(rx))
2215 # check the oringial packet is present
2216 self.assertEqual(rx[IP].dst, p[IP].dst)
2217 self.assertEqual(rx[IP].ttl, p[IP].ttl - 1)
2219 # send packets into the tunnel
2220 for i in range(NUM_IFS):
2223 peers[i].mk_tunnel_header(self.pg1)
2224 / Wireguard(message_type=4, reserved_zero=0)
2225 / WireguardTransport(
2226 receiver_index=peers[i].sender,
2228 encrypted_encapsulated_packet=peers[i].encrypt_transport(
2231 src="10.0.%d.4" % i,
2232 dst=self.pg0.remote_hosts[i].ip4,
2235 / UDP(sport=222, dport=223)
2244 rxs = self.send_and_expect(self.pg1, p, self.pg0)
2247 self.assertEqual(rx[IP].dst, self.pg0.remote_hosts[i].ip4)
2248 self.assertEqual(rx[IP].ttl, 19)
2251 r.remove_vpp_config()
2253 p.remove_vpp_config()
2255 i.remove_vpp_config()
2257 def test_wg_event(self):
2260 ESTABLISHED_FLAG = (
2261 VppEnum.vl_api_wireguard_peer_flags_t.WIREGUARD_PEER_ESTABLISHED
2263 DEAD_FLAG = VppEnum.vl_api_wireguard_peer_flags_t.WIREGUARD_PEER_STATUS_DEAD
2266 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2267 wg1 = VppWgInterface(self, self.pg2.local_ip4, port + 1).add_vpp_config()
2271 # Check peer counter
2272 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 0)
2274 self.pg_enable_capture(self.pg_interfaces)
2279 self.pg2.generate_remote_hosts(NUM_PEERS)
2280 self.pg2.configure_ipv4_neighbors()
2281 self.pg1.generate_remote_hosts(NUM_PEERS)
2282 self.pg1.configure_ipv4_neighbors()
2288 for i in range(NUM_PEERS):
2293 self.pg1.remote_hosts[i].ip4,
2295 ["10.0.%d.4/32" % i],
2303 [VppRoutePath(self.pg1.remote_hosts[i].ip4, wg0.sw_if_index)],
2311 self.pg2.remote_hosts[i].ip4,
2313 ["10.100.%d.4/32" % i],
2321 [VppRoutePath(self.pg2.remote_hosts[i].ip4, wg1.sw_if_index)],
2325 self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_PEERS * 2)
2327 # skip the first automatic handshake
2328 self.pg1.get_capture(NUM_PEERS, timeout=HANDSHAKE_JITTER)
2329 self.pg2.get_capture(NUM_PEERS, timeout=HANDSHAKE_JITTER)
2331 # Want events from the first perr of wg0
2332 # and from all wg1 peers
2333 peers_0[0].want_events()
2336 for i in range(NUM_PEERS):
2337 # wg0 peers: send a valid handsake init for which we expect a response
2338 p = peers_0[i].mk_handshake(self.pg1)
2339 rx = self.send_and_expect(self.pg1, [p], self.pg1)
2340 peers_0[i].consume_response(rx[0])
2342 # wg0 peers: send empty packet, it means successful connection (WIREGUARD_PEER_ESTABLISHED)
2343 keepalive = peers_0[i].encrypt_transport(0)
2344 p = peers_0[i].mk_tunnel_header(self.pg1) / (
2345 Wireguard(message_type=4, reserved_zero=0)
2346 / WireguardTransport(
2347 receiver_index=peers_0[i].sender,
2349 encrypted_encapsulated_packet=keepalive,
2352 # TODO: Figure out wny there are sometimes wg packets received here
2353 # self.send_and_assert_no_replies(self.pg1, [p])
2354 self.pg_send(self.pg1, [p])
2356 # wg0 peers: wait for established flag
2358 peers_0[0].wait_event(ESTABLISHED_FLAG)
2360 # wg1 peers: send a valid handsake init for which we expect a response
2361 p = peers_1[i].mk_handshake(self.pg2)
2362 rx = self.send_and_expect(self.pg2, [p], self.pg2)
2363 peers_1[i].consume_response(rx[0])
2365 # wg1 peers: send empty packet, it means successful connection (WIREGUARD_PEER_ESTABLISHED)
2366 keepalive = peers_1[i].encrypt_transport(0)
2367 p = peers_1[i].mk_tunnel_header(self.pg2) / (
2368 Wireguard(message_type=4, reserved_zero=0)
2369 / WireguardTransport(
2370 receiver_index=peers_1[i].sender,
2372 encrypted_encapsulated_packet=keepalive,
2375 self.send_and_assert_no_replies(self.pg2, [p])
2377 # wg1 peers: wait for established flag
2378 wg1.wait_events(ESTABLISHED_FLAG, [peers_1[0].index, peers_1[1].index])
2382 r.remove_vpp_config()
2384 r.remove_vpp_config()
2387 for i in range(NUM_PEERS):
2388 self.assertTrue(peers_0[i].query_vpp_config())
2389 peers_0[i].remove_vpp_config()
2391 peers_0[i].wait_event(0)
2392 peers_0[i].wait_event(DEAD_FLAG)
2394 self.assertTrue(p.query_vpp_config())
2395 p.remove_vpp_config()
2397 p.wait_event(DEAD_FLAG)
2399 wg0.remove_vpp_config()
2400 wg1.remove_vpp_config()
2402 def test_wg_sending_handshake_when_admin_down(self):
2403 """Sending handshake when admin down"""
2406 # create wg interface
2407 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2412 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
2414 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2416 self.pg_enable_capture(self.pg_interfaces)
2419 # wait for the peer to send a handshake initiation
2420 # expect no handshakes
2422 self.pg1.assert_nothing_captured(remark="handshake packet(s) sent")
2424 self.pg_enable_capture(self.pg_interfaces)
2427 # administratively enable the wg interface
2428 # expect the peer to send a handshake initiation
2430 rxs = self.pg1.get_capture(1, timeout=2)
2431 peer_1.consume_init(rxs[0], self.pg1)
2433 self.pg_enable_capture(self.pg_interfaces)
2436 # administratively disable the wg interface
2437 # expect no handshakes
2440 self.pg1.assert_nothing_captured(remark="handshake packet(s) sent")
2443 peer_1.remove_vpp_config()
2444 wg0.remove_vpp_config()
2446 def test_wg_sending_data_when_admin_down(self):
2447 """Sending data when admin down"""
2450 # create wg interface
2451 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2455 self.pg_enable_capture(self.pg_interfaces)
2460 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
2462 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2464 # create a route to rewrite traffic into the wg interface
2466 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
2469 # wait for the peer to send a handshake initiation
2470 rxs = self.pg1.get_capture(1, timeout=2)
2472 # prepare and send a handshake response
2473 # expect a keepalive message
2474 resp = peer_1.consume_init(rxs[0], self.pg1)
2475 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
2477 # verify the keepalive message
2478 b = peer_1.decrypt_transport(rxs[0])
2479 self.assertEqual(0, len(b))
2481 # prepare and send a packet that will be rewritten into the wg interface
2482 # expect a data packet sent
2484 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2485 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
2486 / UDP(sport=555, dport=556)
2489 rxs = self.send_and_expect(self.pg0, [p], self.pg1)
2491 # verify the data packet
2492 peer_1.validate_encapped(rxs, p)
2494 # administratively disable the wg interface
2497 # send a packet that will be rewritten into the wg interface
2498 # expect no data packets sent
2499 self.send_and_assert_no_replies(self.pg0, [p])
2501 # administratively enable the wg interface
2502 # expect the peer to send a handshake initiation
2504 peer_1.noise_reset()
2505 rxs = self.pg1.get_capture(1, timeout=2)
2506 resp = peer_1.consume_init(rxs[0], self.pg1)
2508 # send a packet that will be rewritten into the wg interface
2509 # expect no data packets sent because the peer is not initiated
2510 self.send_and_assert_no_replies(self.pg0, [p])
2512 self.base_kp4_err + 1, self.statistics.get_err_counter(self.kp4_error)
2515 # send a handshake response and expect a keepalive message
2516 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
2518 # verify the keepalive message
2519 b = peer_1.decrypt_transport(rxs[0])
2520 self.assertEqual(0, len(b))
2522 # send a packet that will be rewritten into the wg interface
2523 # expect a data packet sent
2524 rxs = self.send_and_expect(self.pg0, [p], self.pg1)
2526 # verify the data packet
2527 peer_1.validate_encapped(rxs, p)
2530 r1.remove_vpp_config()
2531 peer_1.remove_vpp_config()
2532 wg0.remove_vpp_config()
2534 def _test_wg_large_packet_tmpl(self, is_async, is_ip6):
2535 self.vapi.wg_set_async_mode(is_async)
2538 # create wg interface
2540 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
2544 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2548 self.pg_enable_capture(self.pg_interfaces)
2554 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
2558 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
2560 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2562 # create a route to rewrite traffic into the wg interface
2565 self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
2569 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
2572 # wait for the peer to send a handshake initiation
2573 rxs = self.pg1.get_capture(1, timeout=2)
2575 # prepare and send a handshake response
2576 # expect a keepalive message
2577 resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6)
2578 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
2580 # verify the keepalive message
2581 b = peer_1.decrypt_transport(rxs[0], is_ip6=is_ip6)
2582 self.assertEqual(0, len(b))
2584 # prepare and send data packets
2585 # expect to receive them decrypted
2587 ip_header = IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
2589 ip_header = IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
2593 4500, # three buffers
2594 1910 if is_ip6 else 1950, # auth tag is not contiguous
2597 for l in packet_len_opts:
2599 peer_1.mk_tunnel_header(self.pg1, is_ip6=is_ip6)
2600 / Wireguard(message_type=4, reserved_zero=0)
2601 / WireguardTransport(
2602 receiver_index=peer_1.sender,
2604 encrypted_encapsulated_packet=peer_1.encrypt_transport(
2605 ip_header / UDP(sport=222, dport=223) / Raw(b"\xfe" * l)
2609 rxs = self.send_and_expect(self.pg1, txs, self.pg0)
2611 # verify decrypted packets
2612 for i, l in enumerate(packet_len_opts):
2614 self.assertEqual(rxs[i][IPv6].dst, self.pg0.remote_ip6)
2615 self.assertEqual(rxs[i][IPv6].hlim, ip_header.hlim - 1)
2617 self.assertEqual(rxs[i][IP].dst, self.pg0.remote_ip4)
2618 self.assertEqual(rxs[i][IP].ttl, ip_header.ttl - 1)
2619 self.assertEqual(len(rxs[i][Raw]), l)
2620 self.assertEqual(bytes(rxs[i][Raw]), b"\xfe" * l)
2622 # prepare and send packets that will be rewritten into the wg interface
2623 # expect data packets sent
2625 ip_header = IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
2627 ip_header = IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
2631 4500, # three buffers
2632 1980 if is_ip6 else 2000, # no free space to write auth tag
2635 for l in packet_len_opts:
2637 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2639 / UDP(sport=555, dport=556)
2642 rxs = self.send_and_expect(self.pg0, txs, self.pg1)
2644 # verify the data packets
2645 rxs_decrypted = peer_1.validate_encapped(
2646 rxs, ip_header, is_tunnel_ip6=is_ip6, is_transport_ip6=is_ip6
2649 for i, l in enumerate(packet_len_opts):
2650 self.assertEqual(len(rxs_decrypted[i][Raw]), l)
2651 self.assertEqual(bytes(rxs_decrypted[i][Raw]), b"\xfe" * l)
2654 r1.remove_vpp_config()
2655 peer_1.remove_vpp_config()
2656 wg0.remove_vpp_config()
2658 def test_wg_large_packet_v4_sync(self):
2659 """Large packet (v4, sync)"""
2660 self._test_wg_large_packet_tmpl(is_async=False, is_ip6=False)
2662 def test_wg_large_packet_v6_sync(self):
2663 """Large packet (v6, sync)"""
2664 self._test_wg_large_packet_tmpl(is_async=False, is_ip6=True)
2666 def test_wg_large_packet_v4_async(self):
2667 """Large packet (v4, async)"""
2668 self._test_wg_large_packet_tmpl(is_async=True, is_ip6=False)
2670 def test_wg_large_packet_v6_async(self):
2671 """Large packet (v6, async)"""
2672 self._test_wg_large_packet_tmpl(is_async=True, is_ip6=True)
2674 def test_wg_lack_of_buf_headroom(self):
2675 """Lack of buffer's headroom (v6 vxlan over v6 wg)"""
2678 # create wg interface
2679 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
2683 self.pg_enable_capture(self.pg_interfaces)
2688 self, wg0, self.pg1.remote_ip6, port + 1, ["::/0"]
2690 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2692 # create a route to enable communication between wg interface addresses
2694 self, wg0.remote_ip6, 128, [VppRoutePath("0.0.0.0", wg0.sw_if_index)]
2697 # wait for the peer to send a handshake initiation
2698 rxs = self.pg1.get_capture(1, timeout=2)
2700 # prepare and send a handshake response
2701 # expect a keepalive message
2702 resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=True)
2703 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
2705 # verify the keepalive message
2706 b = peer_1.decrypt_transport(rxs[0], is_ip6=True)
2707 self.assertEqual(0, len(b))
2709 # create vxlan interface over the wg interface
2710 vxlan0 = VppVxlanTunnel(self, src=wg0.local_ip6, dst=wg0.remote_ip6, vni=1111)
2711 vxlan0.add_vpp_config()
2713 # create bridge domain
2714 bd1 = VppBridgeDomain(self, bd_id=1)
2715 bd1.add_vpp_config()
2717 # add the vxlan interface and pg0 to the bridge domain
2719 VppBridgeDomainPort(self, bd1, vxlan0).add_vpp_config(),
2720 VppBridgeDomainPort(self, bd1, self.pg0).add_vpp_config(),
2723 # prepare and send packets that will be rewritten into the vxlan interface
2724 # expect they to be rewritten into the wg interface then and data packets sent
2726 Ether(dst="00:00:00:00:00:01", src="00:00:00:00:00:02")
2727 / IPv6(src="::1", dst="::2", hlim=20)
2728 / UDP(sport=1111, dport=1112)
2729 / Raw(b"\xfe" * 1900)
2731 rxs = self.send_and_expect(self.pg0, [tx] * 5, self.pg1)
2733 # verify the data packet
2735 rx_decrypted = IPv6(peer_1.decrypt_transport(rx, is_ip6=True))
2737 self.assertEqual(rx_decrypted[VXLAN].vni, vxlan0.vni)
2738 inner = rx_decrypted[VXLAN].payload
2740 # check the original packet is present
2741 self.assertEqual(inner[IPv6].dst, tx[IPv6].dst)
2742 self.assertEqual(inner[IPv6].hlim, tx[IPv6].hlim)
2743 self.assertEqual(len(inner[Raw]), len(tx[Raw]))
2744 self.assertEqual(bytes(inner[Raw]), bytes(tx[Raw]))
2747 for bdp in bd1_ports:
2748 bdp.remove_vpp_config()
2749 bd1.remove_vpp_config()
2750 vxlan0.remove_vpp_config()
2751 r1.remove_vpp_config()
2752 peer_1.remove_vpp_config()
2753 wg0.remove_vpp_config()
2756 @tag_fixme_vpp_debug
2757 class WireguardHandoffTests(TestWg):
2758 """Wireguard Tests in multi worker setup"""
2760 vpp_worker_count = 2
2762 def test_wg_peer_init(self):
2768 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2772 self.pg_enable_capture(self.pg_interfaces)
2776 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.2.0/24", "10.11.3.0/24"]
2778 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2781 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
2784 # skip the first automatic handshake
2785 self.pg1.get_capture(1, timeout=HANDSHAKE_JITTER)
2787 # send a valid handsake init for which we expect a response
2788 p = peer_1.mk_handshake(self.pg1)
2790 rx = self.send_and_expect(self.pg1, [p], self.pg1)
2792 peer_1.consume_response(rx[0])
2794 # send a data packet from the peer through the tunnel
2795 # this completes the handshake and pins the peer to worker 0
2797 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
2798 / UDP(sport=222, dport=223)
2801 d = peer_1.encrypt_transport(p)
2802 p = peer_1.mk_tunnel_header(self.pg1) / (
2803 Wireguard(message_type=4, reserved_zero=0)
2804 / WireguardTransport(
2805 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
2808 rxs = self.send_and_expect(self.pg1, [p], self.pg0, worker=0)
2811 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
2812 self.assertEqual(rx[IP].ttl, 19)
2814 # send a packets that are routed into the tunnel
2815 # and pins the peer tp worker 1
2817 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2818 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
2819 / UDP(sport=555, dport=556)
2822 rxs = self.send_and_expect(self.pg0, pe * 255, self.pg1, worker=1)
2823 peer_1.validate_encapped(rxs, pe)
2825 # send packets into the tunnel, from the other worker
2828 peer_1.mk_tunnel_header(self.pg1)
2829 / Wireguard(message_type=4, reserved_zero=0)
2830 / WireguardTransport(
2831 receiver_index=peer_1.sender,
2833 encrypted_encapsulated_packet=peer_1.encrypt_transport(
2835 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
2836 / UDP(sport=222, dport=223)
2842 for ii in range(255)
2845 rxs = self.send_and_expect(self.pg1, p, self.pg0, worker=1)
2848 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
2849 self.assertEqual(rx[IP].ttl, 19)
2851 # send a packets that are routed into the tunnel
2853 rxs = self.send_and_expect(self.pg0, pe * 255, self.pg1, worker=0)
2855 peer_1.validate_encapped(rxs, pe)
2857 r1.remove_vpp_config()
2858 peer_1.remove_vpp_config()
2859 wg0.remove_vpp_config()
2861 @unittest.skip("test disabled")
2862 def test_wg_multi_interface(self):
2863 """Multi-tunnel on the same port"""
2867 "wireguard" in config.excluded_plugins, "Exclude Wireguard plugin tests"
2870 class TestWgFIB(VppTestCase):
2871 """Wireguard FIB Test Case"""
2874 def setUpClass(cls):
2875 super(TestWgFIB, cls).setUpClass()
2878 def tearDownClass(cls):
2879 super(TestWgFIB, cls).tearDownClass()
2882 super(TestWgFIB, self).setUp()
2884 self.create_pg_interfaces(range(2))
2886 for i in self.pg_interfaces:
2891 for i in self.pg_interfaces:
2894 super(TestWgFIB, self).tearDown()
2896 def test_wg_fib_tracking(self):
2900 # create wg interface
2901 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2905 self.pg_enable_capture(self.pg_interfaces)
2910 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
2912 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2914 # create a route to rewrite traffic into the wg interface
2916 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
2919 # resolve ARP and expect the adjacency to update
2920 self.pg1.resolve_arp()
2922 # wait for the peer to send a handshake initiation
2923 rxs = self.pg1.get_capture(2, timeout=6)
2925 # prepare and send a handshake response
2926 # expect a keepalive message
2927 resp = peer_1.consume_init(rxs[1], self.pg1)
2928 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
2930 # verify the keepalive message
2931 b = peer_1.decrypt_transport(rxs[0])
2932 self.assertEqual(0, len(b))
2934 # prepare and send a packet that will be rewritten into the wg interface
2935 # expect a data packet sent
2937 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2938 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
2939 / UDP(sport=555, dport=556)
2942 rxs = self.send_and_expect(self.pg0, [p], self.pg1)
2944 # verify the data packet
2945 peer_1.validate_encapped(rxs, p)
2948 r1.remove_vpp_config()
2949 peer_1.remove_vpp_config()
2950 wg0.remove_vpp_config()