8 from hashlib import blake2s
9 from scapy.packet import Packet
10 from scapy.packet import Raw
11 from scapy.layers.l2 import Ether, ARP
12 from scapy.layers.inet import IP, UDP
13 from scapy.layers.inet6 import IPv6
14 from scapy.contrib.wireguard import (
21 from cryptography.hazmat.primitives.asymmetric.x25519 import (
25 from cryptography.hazmat.primitives.serialization import (
31 from cryptography.hazmat.primitives.hashes import BLAKE2s, Hash
32 from cryptography.hazmat.primitives.hmac import HMAC
33 from cryptography.hazmat.backends import default_backend
34 from noise.connection import NoiseConnection, Keypair
36 from Crypto.Cipher import ChaCha20_Poly1305
37 from Crypto.Random import get_random_bytes
39 from vpp_ipip_tun_interface import VppIpIpTunInterface
40 from vpp_interface import VppInterface
41 from vpp_pg_interface import is_ipv6_misc
42 from vpp_ip_route import VppIpRoute, VppRoutePath
43 from vpp_object import VppObject
44 from vpp_papi import VppEnum
45 from framework import is_distro_ubuntu2204, is_distro_debian11, tag_fixme_vpp_debug
46 from framework import VppTestCase
47 from re import compile
50 """ TestWg is a subclass of VPPTestCase classes.
57 def private_key_bytes(k):
58 return k.private_bytes(Encoding.Raw, PrivateFormat.Raw, NoEncryption())
61 def public_key_bytes(k):
62 return k.public_bytes(Encoding.Raw, PublicFormat.Raw)
65 def get_field_bytes(pkt, name):
66 fld, val = pkt.getfield_and_val(name)
67 return fld.i2m(pkt, val)
70 class VppWgInterface(VppInterface):
72 VPP WireGuard interface
75 def __init__(self, test, src, port):
76 super(VppWgInterface, self).__init__(test)
80 self.private_key = X25519PrivateKey.generate()
81 self.public_key = self.private_key.public_key()
83 # cookie related params
84 self.cookie_key = blake2s(b"cookie--" + self.public_key_bytes()).digest()
86 def public_key_bytes(self):
87 return public_key_bytes(self.public_key)
89 def private_key_bytes(self):
90 return private_key_bytes(self.private_key)
92 def add_vpp_config(self):
93 r = self.test.vapi.wireguard_interface_create(
95 "user_instance": 0xFFFFFFFF,
98 "private_key": private_key_bytes(self.private_key),
99 "generate_key": False,
102 self.set_sw_if_index(r.sw_if_index)
103 self.test.registry.register(self, self.test.logger)
106 def remove_vpp_config(self):
107 self.test.vapi.wireguard_interface_delete(sw_if_index=self._sw_if_index)
109 def query_vpp_config(self):
110 ts = self.test.vapi.wireguard_interface_dump(sw_if_index=0xFFFFFFFF)
113 t.interface.sw_if_index == self._sw_if_index
114 and str(t.interface.src_ip) == self.src
115 and t.interface.port == self.port
116 and t.interface.private_key == private_key_bytes(self.private_key)
121 def want_events(self, peer_index=0xFFFFFFFF):
122 self.test.vapi.want_wireguard_peer_events(
125 sw_if_index=self._sw_if_index,
126 peer_index=peer_index,
129 def wait_events(self, expect, peers, timeout=5):
130 for i in range(len(peers)):
131 rv = self.test.vapi.wait_for_event(timeout, "wireguard_peer_event")
132 self.test.assertEqual(rv.peer_index, peers[i])
133 self.test.assertEqual(rv.flags, expect)
136 return self.object_id()
139 return "wireguard-%d" % self._sw_if_index
142 NOISE_HANDSHAKE_NAME = b"Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s"
143 NOISE_IDENTIFIER_NAME = b"WireGuard v1 zx2c4 Jason@zx2c4.com"
145 HANDSHAKE_COUNTING_INTERVAL = 0.5
146 UNDER_LOAD_INTERVAL = 1.0
147 HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD = 40
148 HANDSHAKE_NUM_BEFORE_RATELIMITING = 5
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):
474 rx = self.decrypt_transport(rx, is_tunnel_ip6)
475 if is_transport_ip6 is False:
477 # check the original packet is present
478 self._test.assertEqual(rx[IP].dst, tx[IP].dst)
479 self._test.assertEqual(rx[IP].ttl, tx[IP].ttl - 1)
482 # check the original packet is present
483 self._test.assertEqual(rx[IPv6].dst, tx[IPv6].dst)
484 self._test.assertEqual(rx[IPv6].hlim, tx[IPv6].hlim - 1)
486 def want_events(self):
487 self._test.vapi.want_wireguard_peer_events(
490 peer_index=self.index,
491 sw_if_index=self.itf.sw_if_index,
494 def wait_event(self, expect, timeout=5):
495 rv = self._test.vapi.wait_for_event(timeout, "wireguard_peer_event")
496 self._test.assertEqual(rv.flags, expect)
497 self._test.assertEqual(rv.peer_index, self.index)
500 def is_handshake_init(p):
501 wg_p = Wireguard(p[Raw])
503 return wg_p[Wireguard].message_type == 1
506 class TestWg(VppTestCase):
507 """Wireguard Test Case"""
509 error_str = compile(r"Error")
511 wg4_output_node_name = "/err/wg4-output-tun/"
512 wg4_input_node_name = "/err/wg4-input/"
513 wg6_output_node_name = "/err/wg6-output-tun/"
514 wg6_input_node_name = "/err/wg6-input/"
515 kp4_error = wg4_output_node_name + "Keypair error"
516 mac4_error = wg4_input_node_name + "Invalid MAC handshake"
517 peer4_in_err = wg4_input_node_name + "Peer error"
518 peer4_out_err = wg4_output_node_name + "Peer error"
519 kp6_error = wg6_output_node_name + "Keypair error"
520 mac6_error = wg6_input_node_name + "Invalid MAC handshake"
521 peer6_in_err = wg6_input_node_name + "Peer error"
522 peer6_out_err = wg6_output_node_name + "Peer error"
523 cookie_dec4_err = wg4_input_node_name + "Failed during Cookie decryption"
524 cookie_dec6_err = wg6_input_node_name + "Failed during Cookie decryption"
525 ratelimited4_err = wg4_input_node_name + "Handshake ratelimited"
526 ratelimited6_err = wg6_input_node_name + "Handshake ratelimited"
530 super(TestWg, cls).setUpClass()
531 if (is_distro_ubuntu2204 == True or is_distro_debian11 == True) and not hasattr(
536 cls.create_pg_interfaces(range(3))
537 for i in cls.pg_interfaces:
545 super(TestWg, cls).tearDownClass()
549 def tearDownClass(cls):
550 super(TestWg, cls).tearDownClass()
553 super(VppTestCase, self).setUp()
554 self.base_kp4_err = self.statistics.get_err_counter(self.kp4_error)
555 self.base_mac4_err = self.statistics.get_err_counter(self.mac4_error)
556 self.base_peer4_in_err = self.statistics.get_err_counter(self.peer4_in_err)
557 self.base_peer4_out_err = self.statistics.get_err_counter(self.peer4_out_err)
558 self.base_kp6_err = self.statistics.get_err_counter(self.kp6_error)
559 self.base_mac6_err = self.statistics.get_err_counter(self.mac6_error)
560 self.base_peer6_in_err = self.statistics.get_err_counter(self.peer6_in_err)
561 self.base_peer6_out_err = self.statistics.get_err_counter(self.peer6_out_err)
562 self.base_cookie_dec4_err = self.statistics.get_err_counter(
565 self.base_cookie_dec6_err = self.statistics.get_err_counter(
568 self.base_ratelimited4_err = self.statistics.get_err_counter(
569 self.ratelimited4_err
571 self.base_ratelimited6_err = self.statistics.get_err_counter(
572 self.ratelimited6_err
575 def send_and_assert_no_replies_ignoring_init(
576 self, intf, pkts, remark="", timeout=None
578 self.pg_send(intf, pkts)
580 def _filter_out_fn(p):
581 return is_ipv6_misc(p) or is_handshake_init(p)
586 for i in self.pg_interfaces:
587 i.assert_nothing_captured(
588 timeout=timeout, remark=remark, filter_out_fn=_filter_out_fn
594 def test_wg_interface(self):
595 """Simple interface creation"""
599 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
601 self.logger.info(self.vapi.cli("sh int"))
604 wg0.remove_vpp_config()
606 def test_handshake_hash(self):
607 """test hashing an init message"""
608 # a init packet generated by linux given the key below
631 b = bytearray.fromhex(h)
634 pubb = base64.b64decode("aRuHFTTxICIQNefp05oKWlJv3zgKxb8+WW7JJMh0jyM=")
635 pub = X25519PublicKey.from_public_bytes(pubb)
637 self.assertEqual(pubb, public_key_bytes(pub))
639 # strip the macs and build a new packet
641 mac_key = blake2s(b"mac1----" + public_key_bytes(pub)).digest()
642 init += blake2s(init, digest_size=16, key=mac_key).digest()
645 act = Wireguard(init)
647 self.assertEqual(tgt, act)
649 def _test_wg_send_cookie_tmpl(self, is_resp, is_ip6):
652 # create wg interface
654 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
658 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
662 self.pg_enable_capture(self.pg_interfaces)
668 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
672 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
674 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
677 # skip the first automatic handshake
678 self.pg1.get_capture(1, timeout=HANDSHAKE_JITTER)
680 # prepare and send a handshake initiation
681 # expect the peer to send a handshake response
682 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
683 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
685 # wait for the peer to send a handshake initiation
686 rxs = self.pg1.get_capture(1, timeout=2)
688 # prepare and send a wrong cookie reply
689 # expect no replies and the cookie error incremented
690 cookie = peer_1.mk_cookie(rxs[0], self.pg1, is_resp=is_resp, is_ip6=is_ip6)
691 cookie.nonce = b"1234567890"
692 self.send_and_assert_no_replies(self.pg1, [cookie], timeout=0.1)
695 self.base_cookie_dec6_err + 1,
696 self.statistics.get_err_counter(self.cookie_dec6_err),
700 self.base_cookie_dec4_err + 1,
701 self.statistics.get_err_counter(self.cookie_dec4_err),
704 # prepare and send a correct cookie reply
705 cookie = peer_1.mk_cookie(rxs[0], self.pg1, is_resp=is_resp, is_ip6=is_ip6)
706 self.pg_send(self.pg1, [cookie])
708 # wait for the peer to send a handshake initiation with mac2 set
709 rxs = self.pg1.get_capture(1, timeout=6)
711 # verify the initiation and its mac2
712 peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6, is_mac2=True)
715 peer_1.remove_vpp_config()
716 wg0.remove_vpp_config()
718 def test_wg_send_cookie_on_init_v4(self):
719 """Send cookie on handshake initiation (v4)"""
720 self._test_wg_send_cookie_tmpl(is_resp=False, is_ip6=False)
722 def test_wg_send_cookie_on_init_v6(self):
723 """Send cookie on handshake initiation (v6)"""
724 self._test_wg_send_cookie_tmpl(is_resp=False, is_ip6=True)
726 def test_wg_send_cookie_on_resp_v4(self):
727 """Send cookie on handshake response (v4)"""
728 self._test_wg_send_cookie_tmpl(is_resp=True, is_ip6=False)
730 def test_wg_send_cookie_on_resp_v6(self):
731 """Send cookie on handshake response (v6)"""
732 self._test_wg_send_cookie_tmpl(is_resp=True, is_ip6=True)
734 def _test_wg_receive_cookie_tmpl(self, is_resp, is_ip6):
737 # create wg interface
739 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
743 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
747 self.pg_enable_capture(self.pg_interfaces)
753 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
757 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
759 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
762 # wait for the peer to send a handshake initiation
763 rxs = self.pg1.get_capture(1, timeout=2)
764 # prepare and send a bunch of handshake responses
765 # expect to switch to under load state
766 resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6)
767 txs = [resp] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
768 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
769 # reset noise to be able to turn into initiator later
772 # skip the first automatic handshake
773 self.pg1.get_capture(1, timeout=HANDSHAKE_JITTER)
775 # prepare and send a bunch of handshake initiations
776 # expect to switch to under load state
777 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
778 txs = [init] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
779 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
781 # expect the peer to send a cookie reply
782 peer_1.consume_cookie(rxs[-1], is_ip6=is_ip6)
784 # prepare and send a handshake initiation with wrong mac2
785 # expect a cookie reply
786 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
787 init.mac2 = b"1234567890"
788 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
789 peer_1.consume_cookie(rxs[0], is_ip6=is_ip6)
791 # prepare and send a handshake initiation with correct mac2
792 # expect a handshake response
793 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
794 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
796 # verify the response
797 peer_1.consume_response(rxs[0], is_ip6=is_ip6)
799 # clear up under load state
800 self.sleep(UNDER_LOAD_INTERVAL)
803 peer_1.remove_vpp_config()
804 wg0.remove_vpp_config()
806 def test_wg_receive_cookie_on_init_v4(self):
807 """Receive cookie on handshake initiation (v4)"""
808 self._test_wg_receive_cookie_tmpl(is_resp=False, is_ip6=False)
810 def test_wg_receive_cookie_on_init_v6(self):
811 """Receive cookie on handshake initiation (v6)"""
812 self._test_wg_receive_cookie_tmpl(is_resp=False, is_ip6=True)
814 def test_wg_receive_cookie_on_resp_v4(self):
815 """Receive cookie on handshake response (v4)"""
816 self._test_wg_receive_cookie_tmpl(is_resp=True, is_ip6=False)
818 def test_wg_receive_cookie_on_resp_v6(self):
819 """Receive cookie on handshake response (v6)"""
820 self._test_wg_receive_cookie_tmpl(is_resp=True, is_ip6=True)
822 def test_wg_under_load_interval(self):
823 """Under load interval"""
826 # create wg interface
827 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
831 self.pg_enable_capture(self.pg_interfaces)
836 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
838 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
840 # skip the first automatic handshake
841 self.pg1.get_capture(1, timeout=HANDSHAKE_JITTER)
843 # prepare and send a bunch of handshake initiations
844 # expect to switch to under load state
845 init = peer_1.mk_handshake(self.pg1)
846 txs = [init] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
847 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
849 # expect the peer to send a cookie reply
850 peer_1.consume_cookie(rxs[-1])
852 # sleep till the next counting interval
853 # expect under load state is still active
854 self.sleep(HANDSHAKE_COUNTING_INTERVAL)
856 # prepare and send a handshake initiation with wrong mac2
857 # expect a cookie reply
858 init = peer_1.mk_handshake(self.pg1)
859 init.mac2 = b"1234567890"
860 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
861 peer_1.consume_cookie(rxs[0])
863 # sleep till the end of being under load
864 # expect under load state is over
865 self.sleep(UNDER_LOAD_INTERVAL - HANDSHAKE_COUNTING_INTERVAL)
867 # prepare and send a handshake initiation with wrong mac2
868 # expect a handshake response
869 init = peer_1.mk_handshake(self.pg1)
870 init.mac2 = b"1234567890"
871 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
873 # verify the response
874 peer_1.consume_response(rxs[0])
877 peer_1.remove_vpp_config()
878 wg0.remove_vpp_config()
880 def _test_wg_handshake_ratelimiting_tmpl(self, is_ip6):
883 # create wg interface
885 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
889 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
893 self.pg_enable_capture(self.pg_interfaces)
899 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
903 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
905 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
907 # skip the first automatic handshake
908 self.pg1.get_capture(1, timeout=HANDSHAKE_JITTER)
910 # prepare and send a bunch of handshake initiations
911 # expect to switch to under load state
912 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
913 txs = [init] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
914 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
916 # expect the peer to send a cookie reply
917 peer_1.consume_cookie(rxs[-1], is_ip6=is_ip6)
919 # prepare and send a bunch of handshake initiations with correct mac2
920 # expect a handshake response and then ratelimiting
922 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
923 txs = [init] * (HANDSHAKE_NUM_BEFORE_RATELIMITING + NUM_TO_REJECT)
924 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
928 self.base_ratelimited6_err + NUM_TO_REJECT,
929 self.statistics.get_err_counter(self.ratelimited6_err),
933 self.base_ratelimited4_err + NUM_TO_REJECT,
934 self.statistics.get_err_counter(self.ratelimited4_err),
937 # verify the response
938 peer_1.consume_response(rxs[0], is_ip6=is_ip6)
940 # clear up under load state
941 self.sleep(UNDER_LOAD_INTERVAL)
944 peer_1.remove_vpp_config()
945 wg0.remove_vpp_config()
947 def test_wg_handshake_ratelimiting_v4(self):
948 """Handshake ratelimiting (v4)"""
949 self._test_wg_handshake_ratelimiting_tmpl(is_ip6=False)
951 def test_wg_handshake_ratelimiting_v6(self):
952 """Handshake ratelimiting (v6)"""
953 self._test_wg_handshake_ratelimiting_tmpl(is_ip6=True)
955 def test_wg_handshake_ratelimiting_multi_peer(self):
956 """Handshake ratelimiting (multiple peer)"""
959 # create wg interface
960 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
964 self.pg_enable_capture(self.pg_interfaces)
969 self.pg1.generate_remote_hosts(NUM_PEERS)
970 self.pg1.configure_ipv4_neighbors()
973 self, wg0, self.pg1.remote_hosts[0].ip4, port + 1, ["10.11.3.0/24"]
976 self, wg0, self.pg1.remote_hosts[1].ip4, port + 1, ["10.11.4.0/24"]
978 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 2)
980 # skip the first automatic handshake
981 self.pg1.get_capture(NUM_PEERS, timeout=HANDSHAKE_JITTER)
983 # (peer_1) prepare and send a bunch of handshake initiations
984 # expect not to switch to under load state
985 init_1 = peer_1.mk_handshake(self.pg1)
986 txs = [init_1] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
987 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
989 # (peer_1) expect the peer to send a handshake response
990 peer_1.consume_response(rxs[0])
993 # (peer_1) send another bunch of handshake initiations
994 # expect to switch to under load state
995 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
997 # (peer_1) expect the peer to send a cookie reply
998 peer_1.consume_cookie(rxs[-1])
1000 # (peer_2) prepare and send a handshake initiation
1001 # expect a cookie reply
1002 init_2 = peer_2.mk_handshake(self.pg1)
1003 rxs = self.send_and_expect(self.pg1, [init_2], self.pg1)
1004 peer_2.consume_cookie(rxs[0])
1006 # (peer_1) (peer_2) prepare and send a bunch of handshake initiations with correct mac2
1007 # expect a handshake response and then ratelimiting
1008 PEER_1_NUM_TO_REJECT = 2
1009 PEER_2_NUM_TO_REJECT = 5
1010 init_1 = peer_1.mk_handshake(self.pg1)
1011 txs = [init_1] * (HANDSHAKE_NUM_BEFORE_RATELIMITING + PEER_1_NUM_TO_REJECT)
1012 init_2 = peer_2.mk_handshake(self.pg1)
1013 txs += [init_2] * (HANDSHAKE_NUM_BEFORE_RATELIMITING + PEER_2_NUM_TO_REJECT)
1014 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
1017 self.base_ratelimited4_err + PEER_1_NUM_TO_REJECT
1018 < self.statistics.get_err_counter(self.ratelimited4_err)
1019 <= self.base_ratelimited4_err + PEER_1_NUM_TO_REJECT + PEER_2_NUM_TO_REJECT
1022 # (peer_1) (peer_2) verify the response
1023 peer_1.consume_response(rxs[0])
1024 peer_2.consume_response(rxs[1])
1026 # clear up under load state
1027 self.sleep(UNDER_LOAD_INTERVAL)
1030 peer_1.remove_vpp_config()
1031 peer_2.remove_vpp_config()
1032 wg0.remove_vpp_config()
1034 def _test_wg_peer_roaming_on_handshake_tmpl(self, is_endpoint_set, is_resp, is_ip6):
1037 # create wg interface
1039 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1043 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1047 self.pg_enable_capture(self.pg_interfaces)
1050 # create more remote hosts
1051 NUM_REMOTE_HOSTS = 2
1052 self.pg1.generate_remote_hosts(NUM_REMOTE_HOSTS)
1054 self.pg1.configure_ipv6_neighbors()
1056 self.pg1.configure_ipv4_neighbors()
1063 endpoint=self.pg1.remote_hosts[0].ip6 if is_endpoint_set else "::",
1064 port=port + 1 if is_endpoint_set else 0,
1065 allowed_ips=["1::3:0/112"],
1071 endpoint=self.pg1.remote_hosts[0].ip4 if is_endpoint_set else "0.0.0.0",
1072 port=port + 1 if is_endpoint_set else 0,
1073 allowed_ips=["10.11.3.0/24"],
1075 self.assertTrue(peer_1.query_vpp_config())
1078 # wait for the peer to send a handshake initiation
1079 rxs = self.pg1.get_capture(1, timeout=2)
1080 # prepare a handshake response
1081 resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6)
1084 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip6, port + 100)
1085 resp[IPv6].src, resp[UDP].sport = peer_1.endpoint, peer_1.port
1087 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip4, port + 100)
1088 resp[IP].src, resp[UDP].sport = peer_1.endpoint, peer_1.port
1089 # send the handshake response
1090 # expect a keepalive message sent to the new endpoint
1091 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1092 # verify the keepalive message
1093 b = peer_1.decrypt_transport(rxs[0], is_ip6=is_ip6)
1094 self.assertEqual(0, len(b))
1098 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip6, port + 100)
1100 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip4, port + 100)
1101 # prepare and send a handshake initiation
1102 # expect a handshake response sent to the new endpoint
1103 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
1104 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
1105 # verify the response
1106 peer_1.consume_response(rxs[0], is_ip6=is_ip6)
1107 self.assertTrue(peer_1.query_vpp_config())
1110 peer_1.remove_vpp_config()
1111 wg0.remove_vpp_config()
1113 def test_wg_peer_roaming_on_init_v4(self):
1114 """Peer roaming on handshake initiation (v4)"""
1115 self._test_wg_peer_roaming_on_handshake_tmpl(
1116 is_endpoint_set=False, is_resp=False, is_ip6=False
1119 def test_wg_peer_roaming_on_init_v6(self):
1120 """Peer roaming on handshake initiation (v6)"""
1121 self._test_wg_peer_roaming_on_handshake_tmpl(
1122 is_endpoint_set=False, is_resp=False, is_ip6=True
1125 def test_wg_peer_roaming_on_resp_v4(self):
1126 """Peer roaming on handshake response (v4)"""
1127 self._test_wg_peer_roaming_on_handshake_tmpl(
1128 is_endpoint_set=True, is_resp=True, is_ip6=False
1131 def test_wg_peer_roaming_on_resp_v6(self):
1132 """Peer roaming on handshake response (v6)"""
1133 self._test_wg_peer_roaming_on_handshake_tmpl(
1134 is_endpoint_set=True, is_resp=True, is_ip6=True
1137 def _test_wg_peer_roaming_on_data_tmpl(self, is_async, is_ip6):
1138 self.vapi.wg_set_async_mode(is_async)
1141 # create wg interface
1143 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1147 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1151 self.pg_enable_capture(self.pg_interfaces)
1154 # create more remote hosts
1155 NUM_REMOTE_HOSTS = 2
1156 self.pg1.generate_remote_hosts(NUM_REMOTE_HOSTS)
1158 self.pg1.configure_ipv6_neighbors()
1160 self.pg1.configure_ipv4_neighbors()
1165 self, wg0, self.pg1.remote_hosts[0].ip6, port + 1, ["1::3:0/112"]
1169 self, wg0, self.pg1.remote_hosts[0].ip4, port + 1, ["10.11.3.0/24"]
1171 self.assertTrue(peer_1.query_vpp_config())
1173 # create a route to rewrite traffic into the wg interface
1176 self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
1180 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1183 # wait for the peer to send a handshake initiation
1184 rxs = self.pg1.get_capture(1, timeout=2)
1186 # prepare and send a handshake response
1187 # expect a keepalive message
1188 resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6)
1189 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1191 # verify the keepalive message
1192 b = peer_1.decrypt_transport(rxs[0], is_ip6=is_ip6)
1193 self.assertEqual(0, len(b))
1197 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip6, port + 100)
1199 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip4, port + 100)
1201 # prepare and send a data packet
1202 # expect endpoint change
1204 ip_header = IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1206 ip_header = IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1208 peer_1.mk_tunnel_header(self.pg1, is_ip6=is_ip6)
1209 / Wireguard(message_type=4, reserved_zero=0)
1210 / WireguardTransport(
1211 receiver_index=peer_1.sender,
1213 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1214 ip_header / UDP(sport=222, dport=223) / Raw()
1218 rxs = self.send_and_expect(self.pg1, [data], self.pg0)
1220 self.assertEqual(rxs[0][IPv6].dst, self.pg0.remote_ip6)
1221 self.assertEqual(rxs[0][IPv6].hlim, 19)
1223 self.assertEqual(rxs[0][IP].dst, self.pg0.remote_ip4)
1224 self.assertEqual(rxs[0][IP].ttl, 19)
1225 self.assertTrue(peer_1.query_vpp_config())
1227 # prepare and send a packet that will be rewritten into the wg interface
1228 # expect a data packet sent to the new endpoint
1230 ip_header = IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1232 ip_header = IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1234 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1236 / UDP(sport=555, dport=556)
1239 rxs = self.send_and_expect(self.pg0, [p], self.pg1)
1241 # verify the data packet
1242 peer_1.validate_encapped(rxs, p, is_tunnel_ip6=is_ip6, is_transport_ip6=is_ip6)
1245 r1.remove_vpp_config()
1246 peer_1.remove_vpp_config()
1247 wg0.remove_vpp_config()
1249 def test_wg_peer_roaming_on_data_v4_sync(self):
1250 """Peer roaming on data packet (v4, sync)"""
1251 self._test_wg_peer_roaming_on_data_tmpl(is_async=False, is_ip6=False)
1253 def test_wg_peer_roaming_on_data_v6_sync(self):
1254 """Peer roaming on data packet (v6, sync)"""
1255 self._test_wg_peer_roaming_on_data_tmpl(is_async=False, is_ip6=True)
1257 def test_wg_peer_roaming_on_data_v4_async(self):
1258 """Peer roaming on data packet (v4, async)"""
1259 self._test_wg_peer_roaming_on_data_tmpl(is_async=True, is_ip6=False)
1261 def test_wg_peer_roaming_on_data_v6_async(self):
1262 """Peer roaming on data packet (v6, async)"""
1263 self._test_wg_peer_roaming_on_data_tmpl(is_async=True, is_ip6=True)
1265 def test_wg_peer_resp(self):
1266 """Send handshake response IPv4 tunnel"""
1270 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1274 self.pg_enable_capture(self.pg_interfaces)
1278 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
1280 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1283 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1286 # wait for the peer to send a handshake
1287 rx = self.pg1.get_capture(1, timeout=2)
1289 # consume the handshake in the noise protocol and
1290 # generate the response
1291 resp = peer_1.consume_init(rx[0], self.pg1)
1293 # send the response, get keepalive
1294 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1297 b = peer_1.decrypt_transport(rx)
1298 self.assertEqual(0, len(b))
1300 # send a packets that are routed into the tunnel
1302 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1303 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1304 / UDP(sport=555, dport=556)
1308 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1310 peer_1.validate_encapped(rxs, p)
1312 # send packets into the tunnel, expect to receive them on
1316 peer_1.mk_tunnel_header(self.pg1)
1317 / Wireguard(message_type=4, reserved_zero=0)
1318 / WireguardTransport(
1319 receiver_index=peer_1.sender,
1321 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1323 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1324 / UDP(sport=222, dport=223)
1330 for ii in range(255)
1333 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1336 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1337 self.assertEqual(rx[IP].ttl, 19)
1339 r1.remove_vpp_config()
1340 peer_1.remove_vpp_config()
1341 wg0.remove_vpp_config()
1343 def test_wg_peer_resp_ipv6(self):
1344 """Send handshake response IPv6 tunnel"""
1348 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1352 self.pg_enable_capture(self.pg_interfaces)
1356 self, wg0, self.pg1.remote_ip6, port + 1, ["10.11.3.0/24"]
1358 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1361 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1364 # wait for the peer to send a handshake
1365 rx = self.pg1.get_capture(1, timeout=2)
1367 # consume the handshake in the noise protocol and
1368 # generate the response
1369 resp = peer_1.consume_init(rx[0], self.pg1, is_ip6=True)
1371 # send the response, get keepalive
1372 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1375 b = peer_1.decrypt_transport(rx, True)
1376 self.assertEqual(0, len(b))
1378 # send a packets that are routed into the tunnel
1380 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1381 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1382 / UDP(sport=555, dport=556)
1386 rxs = self.send_and_expect(self.pg0, p * 2, self.pg1)
1387 peer_1.validate_encapped(rxs, p, True)
1389 # send packets into the tunnel, expect to receive them on
1393 peer_1.mk_tunnel_header(self.pg1, True)
1394 / Wireguard(message_type=4, reserved_zero=0)
1395 / WireguardTransport(
1396 receiver_index=peer_1.sender,
1398 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1400 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1401 / UDP(sport=222, dport=223)
1407 for ii in range(255)
1410 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1413 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1414 self.assertEqual(rx[IP].ttl, 19)
1416 r1.remove_vpp_config()
1417 peer_1.remove_vpp_config()
1418 wg0.remove_vpp_config()
1420 def test_wg_peer_v4o4(self):
1426 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1431 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
1433 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1436 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1439 self, "20.22.3.0", 24, [VppRoutePath("20.22.3.1", wg0.sw_if_index)]
1442 # route a packet into the wg interface
1443 # use the allowed-ip prefix
1444 # this is dropped because the peer is not initiated
1446 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1447 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1448 / UDP(sport=555, dport=556)
1451 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1453 self.base_kp4_err + 1, self.statistics.get_err_counter(self.kp4_error)
1456 # route a packet into the wg interface
1457 # use a not allowed-ip prefix
1458 # this is dropped because there is no matching peer
1460 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1461 / IP(src=self.pg0.remote_ip4, dst="20.22.3.2")
1462 / UDP(sport=555, dport=556)
1465 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1467 self.base_peer4_out_err + 1,
1468 self.statistics.get_err_counter(self.peer4_out_err),
1471 # send a handsake from the peer with an invalid MAC
1472 p = peer_1.mk_handshake(self.pg1)
1473 p[WireguardInitiation].mac1 = b"foobar"
1474 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1476 self.base_mac4_err + 1, self.statistics.get_err_counter(self.mac4_error)
1479 # send a handsake from the peer but signed by the wrong key.
1480 p = peer_1.mk_handshake(
1481 self.pg1, False, X25519PrivateKey.generate().public_key()
1483 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1485 self.base_peer4_in_err + 1,
1486 self.statistics.get_err_counter(self.peer4_in_err),
1489 # send a valid handsake init for which we expect a response
1490 p = peer_1.mk_handshake(self.pg1)
1492 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1494 peer_1.consume_response(rx[0])
1496 # route a packet into the wg interface
1497 # this is dropped because the peer is still not initiated
1499 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1500 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1501 / UDP(sport=555, dport=556)
1504 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1506 self.base_kp4_err + 2, self.statistics.get_err_counter(self.kp4_error)
1509 # send a data packet from the peer through the tunnel
1510 # this completes the handshake
1512 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1513 / UDP(sport=222, dport=223)
1516 d = peer_1.encrypt_transport(p)
1517 p = peer_1.mk_tunnel_header(self.pg1) / (
1518 Wireguard(message_type=4, reserved_zero=0)
1519 / WireguardTransport(
1520 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1523 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1526 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1527 self.assertEqual(rx[IP].ttl, 19)
1529 # send a packets that are routed into the tunnel
1531 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1532 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1533 / UDP(sport=555, dport=556)
1537 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1540 rx = IP(peer_1.decrypt_transport(rx))
1542 # check the original packet is present
1543 self.assertEqual(rx[IP].dst, p[IP].dst)
1544 self.assertEqual(rx[IP].ttl, p[IP].ttl - 1)
1546 # send packets into the tunnel, expect to receive them on
1550 peer_1.mk_tunnel_header(self.pg1)
1551 / Wireguard(message_type=4, reserved_zero=0)
1552 / WireguardTransport(
1553 receiver_index=peer_1.sender,
1555 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1557 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1558 / UDP(sport=222, dport=223)
1564 for ii in range(255)
1567 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1570 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1571 self.assertEqual(rx[IP].ttl, 19)
1573 r1.remove_vpp_config()
1574 r2.remove_vpp_config()
1575 peer_1.remove_vpp_config()
1576 wg0.remove_vpp_config()
1578 def test_wg_peer_v6o6(self):
1584 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1589 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
1591 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1594 self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
1597 self, "22::3:0", 112, [VppRoutePath("22::3:1", wg0.sw_if_index)]
1600 # route a packet into the wg interface
1601 # use the allowed-ip prefix
1602 # this is dropped because the peer is not initiated
1605 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1606 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1607 / UDP(sport=555, dport=556)
1610 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1613 self.base_kp6_err + 1, self.statistics.get_err_counter(self.kp6_error)
1616 # route a packet into the wg interface
1617 # use a not allowed-ip prefix
1618 # this is dropped because there is no matching peer
1620 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1621 / IPv6(src=self.pg0.remote_ip6, dst="22::3:2")
1622 / UDP(sport=555, dport=556)
1625 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1627 self.base_peer6_out_err + 1,
1628 self.statistics.get_err_counter(self.peer6_out_err),
1631 # send a handsake from the peer with an invalid MAC
1632 p = peer_1.mk_handshake(self.pg1, True)
1633 p[WireguardInitiation].mac1 = b"foobar"
1634 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1637 self.base_mac6_err + 1, self.statistics.get_err_counter(self.mac6_error)
1640 # send a handsake from the peer but signed by the wrong key.
1641 p = peer_1.mk_handshake(
1642 self.pg1, True, X25519PrivateKey.generate().public_key()
1644 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1646 self.base_peer6_in_err + 1,
1647 self.statistics.get_err_counter(self.peer6_in_err),
1650 # send a valid handsake init for which we expect a response
1651 p = peer_1.mk_handshake(self.pg1, True)
1653 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1655 peer_1.consume_response(rx[0], True)
1657 # route a packet into the wg interface
1658 # this is dropped because the peer is still not initiated
1660 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1661 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1662 / UDP(sport=555, dport=556)
1665 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1667 self.base_kp6_err + 2, self.statistics.get_err_counter(self.kp6_error)
1670 # send a data packet from the peer through the tunnel
1671 # this completes the handshake
1673 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1674 / UDP(sport=222, dport=223)
1677 d = peer_1.encrypt_transport(p)
1678 p = peer_1.mk_tunnel_header(self.pg1, True) / (
1679 Wireguard(message_type=4, reserved_zero=0)
1680 / WireguardTransport(
1681 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1684 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1687 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1688 self.assertEqual(rx[IPv6].hlim, 19)
1690 # send a packets that are routed into the tunnel
1692 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1693 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1694 / UDP(sport=555, dport=556)
1698 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1701 rx = IPv6(peer_1.decrypt_transport(rx, True))
1703 # check the original packet is present
1704 self.assertEqual(rx[IPv6].dst, p[IPv6].dst)
1705 self.assertEqual(rx[IPv6].hlim, p[IPv6].hlim - 1)
1707 # send packets into the tunnel, expect to receive them on
1711 peer_1.mk_tunnel_header(self.pg1, True)
1712 / Wireguard(message_type=4, reserved_zero=0)
1713 / WireguardTransport(
1714 receiver_index=peer_1.sender,
1716 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1718 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1719 / UDP(sport=222, dport=223)
1725 for ii in range(255)
1728 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1731 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1732 self.assertEqual(rx[IPv6].hlim, 19)
1734 r1.remove_vpp_config()
1735 r2.remove_vpp_config()
1736 peer_1.remove_vpp_config()
1737 wg0.remove_vpp_config()
1739 def test_wg_peer_v6o4(self):
1745 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1750 self, wg0, self.pg1.remote_ip4, port + 1, ["1::3:0/112"]
1752 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1755 self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
1758 # route a packet into the wg interface
1759 # use the allowed-ip prefix
1760 # this is dropped because the peer is not initiated
1762 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1763 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1764 / UDP(sport=555, dport=556)
1767 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1769 self.base_kp6_err + 1, self.statistics.get_err_counter(self.kp6_error)
1772 # send a handsake from the peer with an invalid MAC
1773 p = peer_1.mk_handshake(self.pg1)
1774 p[WireguardInitiation].mac1 = b"foobar"
1775 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1778 self.base_mac4_err + 1, self.statistics.get_err_counter(self.mac4_error)
1781 # send a handsake from the peer but signed by the wrong key.
1782 p = peer_1.mk_handshake(
1783 self.pg1, False, X25519PrivateKey.generate().public_key()
1785 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1787 self.base_peer4_in_err + 1,
1788 self.statistics.get_err_counter(self.peer4_in_err),
1791 # send a valid handsake init for which we expect a response
1792 p = peer_1.mk_handshake(self.pg1)
1794 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1796 peer_1.consume_response(rx[0])
1798 # route a packet into the wg interface
1799 # this is dropped because the peer is still not initiated
1801 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1802 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1803 / UDP(sport=555, dport=556)
1806 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1808 self.base_kp6_err + 2, self.statistics.get_err_counter(self.kp6_error)
1811 # send a data packet from the peer through the tunnel
1812 # this completes the handshake
1814 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1815 / UDP(sport=222, dport=223)
1818 d = peer_1.encrypt_transport(p)
1819 p = peer_1.mk_tunnel_header(self.pg1) / (
1820 Wireguard(message_type=4, reserved_zero=0)
1821 / WireguardTransport(
1822 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1825 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1828 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1829 self.assertEqual(rx[IPv6].hlim, 19)
1831 # send a packets that are routed into the tunnel
1833 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1834 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1835 / UDP(sport=555, dport=556)
1839 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1842 rx = IPv6(peer_1.decrypt_transport(rx))
1844 # check the original packet is present
1845 self.assertEqual(rx[IPv6].dst, p[IPv6].dst)
1846 self.assertEqual(rx[IPv6].hlim, p[IPv6].hlim - 1)
1848 # send packets into the tunnel, expect to receive them on
1852 peer_1.mk_tunnel_header(self.pg1)
1853 / Wireguard(message_type=4, reserved_zero=0)
1854 / WireguardTransport(
1855 receiver_index=peer_1.sender,
1857 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1859 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1860 / UDP(sport=222, dport=223)
1866 for ii in range(255)
1869 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1872 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1873 self.assertEqual(rx[IPv6].hlim, 19)
1875 r1.remove_vpp_config()
1876 peer_1.remove_vpp_config()
1877 wg0.remove_vpp_config()
1879 def test_wg_peer_v4o6(self):
1885 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1890 self, wg0, self.pg1.remote_ip6, port + 1, ["10.11.3.0/24"]
1892 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1895 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1898 # route a packet into the wg interface
1899 # use the allowed-ip prefix
1900 # this is dropped because the peer is not initiated
1902 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1903 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1904 / UDP(sport=555, dport=556)
1907 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1909 self.base_kp4_err + 1, self.statistics.get_err_counter(self.kp4_error)
1912 # send a handsake from the peer with an invalid MAC
1913 p = peer_1.mk_handshake(self.pg1, True)
1914 p[WireguardInitiation].mac1 = b"foobar"
1915 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1917 self.base_mac6_err + 1, self.statistics.get_err_counter(self.mac6_error)
1920 # send a handsake from the peer but signed by the wrong key.
1921 p = peer_1.mk_handshake(
1922 self.pg1, True, X25519PrivateKey.generate().public_key()
1924 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1926 self.base_peer6_in_err + 1,
1927 self.statistics.get_err_counter(self.peer6_in_err),
1930 # send a valid handsake init for which we expect a response
1931 p = peer_1.mk_handshake(self.pg1, True)
1933 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1935 peer_1.consume_response(rx[0], True)
1937 # route a packet into the wg interface
1938 # this is dropped because the peer is still not initiated
1940 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1941 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1942 / UDP(sport=555, dport=556)
1945 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1947 self.base_kp4_err + 2, self.statistics.get_err_counter(self.kp4_error)
1950 # send a data packet from the peer through the tunnel
1951 # this completes the handshake
1953 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1954 / UDP(sport=222, dport=223)
1957 d = peer_1.encrypt_transport(p)
1958 p = peer_1.mk_tunnel_header(self.pg1, True) / (
1959 Wireguard(message_type=4, reserved_zero=0)
1960 / WireguardTransport(
1961 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1964 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1967 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1968 self.assertEqual(rx[IP].ttl, 19)
1970 # send a packets that are routed into the tunnel
1972 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1973 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1974 / UDP(sport=555, dport=556)
1978 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1981 rx = IP(peer_1.decrypt_transport(rx, True))
1983 # check the original packet is present
1984 self.assertEqual(rx[IP].dst, p[IP].dst)
1985 self.assertEqual(rx[IP].ttl, p[IP].ttl - 1)
1987 # send packets into the tunnel, expect to receive them on
1991 peer_1.mk_tunnel_header(self.pg1, True)
1992 / Wireguard(message_type=4, reserved_zero=0)
1993 / WireguardTransport(
1994 receiver_index=peer_1.sender,
1996 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1998 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1999 / UDP(sport=222, dport=223)
2005 for ii in range(255)
2008 rxs = self.send_and_expect(self.pg1, p, self.pg0)
2011 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
2012 self.assertEqual(rx[IP].ttl, 19)
2014 r1.remove_vpp_config()
2015 peer_1.remove_vpp_config()
2016 wg0.remove_vpp_config()
2018 def test_wg_multi_peer(self):
2019 """multiple peer setup"""
2023 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2024 wg1 = VppWgInterface(self, self.pg2.local_ip4, port + 1).add_vpp_config()
2028 # Check peer counter
2029 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 0)
2031 self.pg_enable_capture(self.pg_interfaces)
2034 # Create many peers on sencond interface
2036 self.pg2.generate_remote_hosts(NUM_PEERS)
2037 self.pg2.configure_ipv4_neighbors()
2038 self.pg1.generate_remote_hosts(NUM_PEERS)
2039 self.pg1.configure_ipv4_neighbors()
2045 for i in range(NUM_PEERS):
2050 self.pg1.remote_hosts[i].ip4,
2052 ["10.0.%d.4/32" % i],
2060 [VppRoutePath(self.pg1.remote_hosts[i].ip4, wg0.sw_if_index)],
2068 self.pg2.remote_hosts[i].ip4,
2070 ["10.100.%d.4/32" % i],
2078 [VppRoutePath(self.pg2.remote_hosts[i].ip4, wg1.sw_if_index)],
2082 self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_PEERS * 2)
2084 self.logger.info(self.vapi.cli("show wireguard peer"))
2085 self.logger.info(self.vapi.cli("show wireguard interface"))
2086 self.logger.info(self.vapi.cli("show adj 37"))
2087 self.logger.info(self.vapi.cli("sh ip fib 172.16.3.17"))
2088 self.logger.info(self.vapi.cli("sh ip fib 10.11.3.0"))
2092 r.remove_vpp_config()
2094 r.remove_vpp_config()
2098 self.assertTrue(p.query_vpp_config())
2099 p.remove_vpp_config()
2101 self.assertTrue(p.query_vpp_config())
2102 p.remove_vpp_config()
2104 wg0.remove_vpp_config()
2105 wg1.remove_vpp_config()
2107 def test_wg_multi_interface(self):
2108 """Multi-tunnel on the same port"""
2111 # Create many wireguard interfaces
2113 self.pg1.generate_remote_hosts(NUM_IFS)
2114 self.pg1.configure_ipv4_neighbors()
2115 self.pg0.generate_remote_hosts(NUM_IFS)
2116 self.pg0.configure_ipv4_neighbors()
2118 self.pg_enable_capture(self.pg_interfaces)
2121 # Create interfaces with a peer on each
2125 for i in range(NUM_IFS):
2126 # Use the same port for each interface
2127 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2135 self.pg1.remote_hosts[i].ip4,
2137 ["10.0.%d.0/24" % i],
2146 [VppRoutePath("10.0.%d.4" % i, wg0.sw_if_index)],
2150 self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_IFS)
2152 # skip the first automatic handshake
2153 self.pg1.get_capture(NUM_IFS, timeout=HANDSHAKE_JITTER)
2155 for i in range(NUM_IFS):
2156 # send a valid handsake init for which we expect a response
2157 p = peers[i].mk_handshake(self.pg1)
2158 rx = self.send_and_expect(self.pg1, [p], self.pg1)
2159 peers[i].consume_response(rx[0])
2161 # send a data packet from the peer through the tunnel
2162 # this completes the handshake
2164 IP(src="10.0.%d.4" % i, dst=self.pg0.remote_hosts[i].ip4, ttl=20)
2165 / UDP(sport=222, dport=223)
2168 d = peers[i].encrypt_transport(p)
2169 p = peers[i].mk_tunnel_header(self.pg1) / (
2170 Wireguard(message_type=4, reserved_zero=0)
2171 / WireguardTransport(
2172 receiver_index=peers[i].sender,
2174 encrypted_encapsulated_packet=d,
2177 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
2179 self.assertEqual(rx[IP].dst, self.pg0.remote_hosts[i].ip4)
2180 self.assertEqual(rx[IP].ttl, 19)
2182 # send a packets that are routed into the tunnel
2183 for i in range(NUM_IFS):
2185 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2186 / IP(src=self.pg0.remote_hosts[i].ip4, dst="10.0.%d.4" % i)
2187 / UDP(sport=555, dport=556)
2191 rxs = self.send_and_expect(self.pg0, p * 64, self.pg1)
2194 rx = IP(peers[i].decrypt_transport(rx))
2196 # check the oringial packet is present
2197 self.assertEqual(rx[IP].dst, p[IP].dst)
2198 self.assertEqual(rx[IP].ttl, p[IP].ttl - 1)
2200 # send packets into the tunnel
2201 for i in range(NUM_IFS):
2204 peers[i].mk_tunnel_header(self.pg1)
2205 / Wireguard(message_type=4, reserved_zero=0)
2206 / WireguardTransport(
2207 receiver_index=peers[i].sender,
2209 encrypted_encapsulated_packet=peers[i].encrypt_transport(
2212 src="10.0.%d.4" % i,
2213 dst=self.pg0.remote_hosts[i].ip4,
2216 / UDP(sport=222, dport=223)
2225 rxs = self.send_and_expect(self.pg1, p, self.pg0)
2228 self.assertEqual(rx[IP].dst, self.pg0.remote_hosts[i].ip4)
2229 self.assertEqual(rx[IP].ttl, 19)
2232 r.remove_vpp_config()
2234 p.remove_vpp_config()
2236 i.remove_vpp_config()
2238 def test_wg_event(self):
2241 ESTABLISHED_FLAG = (
2242 VppEnum.vl_api_wireguard_peer_flags_t.WIREGUARD_PEER_ESTABLISHED
2244 DEAD_FLAG = VppEnum.vl_api_wireguard_peer_flags_t.WIREGUARD_PEER_STATUS_DEAD
2247 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2248 wg1 = VppWgInterface(self, self.pg2.local_ip4, port + 1).add_vpp_config()
2252 # Check peer counter
2253 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 0)
2255 self.pg_enable_capture(self.pg_interfaces)
2260 self.pg2.generate_remote_hosts(NUM_PEERS)
2261 self.pg2.configure_ipv4_neighbors()
2262 self.pg1.generate_remote_hosts(NUM_PEERS)
2263 self.pg1.configure_ipv4_neighbors()
2269 for i in range(NUM_PEERS):
2274 self.pg1.remote_hosts[i].ip4,
2276 ["10.0.%d.4/32" % i],
2284 [VppRoutePath(self.pg1.remote_hosts[i].ip4, wg0.sw_if_index)],
2292 self.pg2.remote_hosts[i].ip4,
2294 ["10.100.%d.4/32" % i],
2302 [VppRoutePath(self.pg2.remote_hosts[i].ip4, wg1.sw_if_index)],
2306 self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_PEERS * 2)
2308 # skip the first automatic handshake
2309 self.pg1.get_capture(NUM_PEERS, timeout=HANDSHAKE_JITTER)
2310 self.pg2.get_capture(NUM_PEERS, timeout=HANDSHAKE_JITTER)
2312 # Want events from the first perr of wg0
2313 # and from all wg1 peers
2314 peers_0[0].want_events()
2317 for i in range(NUM_PEERS):
2318 # wg0 peers: send a valid handsake init for which we expect a response
2319 p = peers_0[i].mk_handshake(self.pg1)
2320 rx = self.send_and_expect(self.pg1, [p], self.pg1)
2321 peers_0[i].consume_response(rx[0])
2323 # wg0 peers: send empty packet, it means successful connection (WIREGUARD_PEER_ESTABLISHED)
2324 keepalive = peers_0[i].encrypt_transport(0)
2325 p = peers_0[i].mk_tunnel_header(self.pg1) / (
2326 Wireguard(message_type=4, reserved_zero=0)
2327 / WireguardTransport(
2328 receiver_index=peers_0[i].sender,
2330 encrypted_encapsulated_packet=keepalive,
2333 self.send_and_assert_no_replies(self.pg1, [p])
2335 # wg0 peers: wait for established flag
2337 peers_0[0].wait_event(ESTABLISHED_FLAG)
2339 # wg1 peers: send a valid handsake init for which we expect a response
2340 p = peers_1[i].mk_handshake(self.pg2)
2341 rx = self.send_and_expect(self.pg2, [p], self.pg2)
2342 peers_1[i].consume_response(rx[0])
2344 # wg1 peers: send empty packet, it means successful connection (WIREGUARD_PEER_ESTABLISHED)
2345 keepalive = peers_1[i].encrypt_transport(0)
2346 p = peers_1[i].mk_tunnel_header(self.pg2) / (
2347 Wireguard(message_type=4, reserved_zero=0)
2348 / WireguardTransport(
2349 receiver_index=peers_1[i].sender,
2351 encrypted_encapsulated_packet=keepalive,
2354 self.send_and_assert_no_replies(self.pg2, [p])
2356 # wg1 peers: wait for established flag
2357 wg1.wait_events(ESTABLISHED_FLAG, [peers_1[0].index, peers_1[1].index])
2361 r.remove_vpp_config()
2363 r.remove_vpp_config()
2366 for i in range(NUM_PEERS):
2367 self.assertTrue(peers_0[i].query_vpp_config())
2368 peers_0[i].remove_vpp_config()
2370 peers_0[i].wait_event(0)
2371 peers_0[i].wait_event(DEAD_FLAG)
2373 self.assertTrue(p.query_vpp_config())
2374 p.remove_vpp_config()
2376 p.wait_event(DEAD_FLAG)
2378 wg0.remove_vpp_config()
2379 wg1.remove_vpp_config()
2381 def test_wg_sending_handshake_when_admin_down(self):
2382 """Sending handshake when admin down"""
2385 # create wg interface
2386 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2391 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
2393 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2395 self.pg_enable_capture(self.pg_interfaces)
2398 # wait for the peer to send a handshake initiation
2399 # expect no handshakes
2401 self.pg1.assert_nothing_captured(remark="handshake packet(s) sent")
2403 self.pg_enable_capture(self.pg_interfaces)
2406 # administratively enable the wg interface
2407 # expect the peer to send a handshake initiation
2409 rxs = self.pg1.get_capture(1, timeout=2)
2410 peer_1.consume_init(rxs[0], self.pg1)
2412 self.pg_enable_capture(self.pg_interfaces)
2415 # administratively disable the wg interface
2416 # expect no handshakes
2419 self.pg1.assert_nothing_captured(remark="handshake packet(s) sent")
2422 peer_1.remove_vpp_config()
2423 wg0.remove_vpp_config()
2425 def test_wg_sending_data_when_admin_down(self):
2426 """Sending data when admin down"""
2429 # create wg interface
2430 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2434 self.pg_enable_capture(self.pg_interfaces)
2439 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
2441 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2443 # create a route to rewrite traffic into the wg interface
2445 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
2448 # wait for the peer to send a handshake initiation
2449 rxs = self.pg1.get_capture(1, timeout=2)
2451 # prepare and send a handshake response
2452 # expect a keepalive message
2453 resp = peer_1.consume_init(rxs[0], self.pg1)
2454 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
2456 # verify the keepalive message
2457 b = peer_1.decrypt_transport(rxs[0])
2458 self.assertEqual(0, len(b))
2460 # prepare and send a packet that will be rewritten into the wg interface
2461 # expect a data packet sent
2463 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2464 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
2465 / UDP(sport=555, dport=556)
2468 rxs = self.send_and_expect(self.pg0, [p], self.pg1)
2470 # verify the data packet
2471 peer_1.validate_encapped(rxs, p)
2473 # administratively disable the wg interface
2476 # send a packet that will be rewritten into the wg interface
2477 # expect no data packets sent
2478 self.send_and_assert_no_replies(self.pg0, [p])
2480 # administratively enable the wg interface
2481 # expect the peer to send a handshake initiation
2483 peer_1.noise_reset()
2484 rxs = self.pg1.get_capture(1, timeout=2)
2485 resp = peer_1.consume_init(rxs[0], self.pg1)
2487 # send a packet that will be rewritten into the wg interface
2488 # expect no data packets sent because the peer is not initiated
2489 self.send_and_assert_no_replies(self.pg0, [p])
2491 self.base_kp4_err + 1, self.statistics.get_err_counter(self.kp4_error)
2494 # send a handshake response and expect a keepalive message
2495 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
2497 # verify the keepalive message
2498 b = peer_1.decrypt_transport(rxs[0])
2499 self.assertEqual(0, len(b))
2501 # send a packet that will be rewritten into the wg interface
2502 # expect a data packet sent
2503 rxs = self.send_and_expect(self.pg0, [p], self.pg1)
2505 # verify the data packet
2506 peer_1.validate_encapped(rxs, p)
2509 r1.remove_vpp_config()
2510 peer_1.remove_vpp_config()
2511 wg0.remove_vpp_config()
2514 @tag_fixme_vpp_debug
2515 class WireguardHandoffTests(TestWg):
2516 """Wireguard Tests in multi worker setup"""
2518 vpp_worker_count = 2
2520 def test_wg_peer_init(self):
2526 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2530 self.pg_enable_capture(self.pg_interfaces)
2534 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.2.0/24", "10.11.3.0/24"]
2536 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2539 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
2542 # skip the first automatic handshake
2543 self.pg1.get_capture(1, timeout=HANDSHAKE_JITTER)
2545 # send a valid handsake init for which we expect a response
2546 p = peer_1.mk_handshake(self.pg1)
2548 rx = self.send_and_expect(self.pg1, [p], self.pg1)
2550 peer_1.consume_response(rx[0])
2552 # send a data packet from the peer through the tunnel
2553 # this completes the handshake and pins the peer to worker 0
2555 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
2556 / UDP(sport=222, dport=223)
2559 d = peer_1.encrypt_transport(p)
2560 p = peer_1.mk_tunnel_header(self.pg1) / (
2561 Wireguard(message_type=4, reserved_zero=0)
2562 / WireguardTransport(
2563 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
2566 rxs = self.send_and_expect(self.pg1, [p], self.pg0, worker=0)
2569 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
2570 self.assertEqual(rx[IP].ttl, 19)
2572 # send a packets that are routed into the tunnel
2573 # and pins the peer tp worker 1
2575 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2576 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
2577 / UDP(sport=555, dport=556)
2580 rxs = self.send_and_expect(self.pg0, pe * 255, self.pg1, worker=1)
2581 peer_1.validate_encapped(rxs, pe)
2583 # send packets into the tunnel, from the other worker
2586 peer_1.mk_tunnel_header(self.pg1)
2587 / Wireguard(message_type=4, reserved_zero=0)
2588 / WireguardTransport(
2589 receiver_index=peer_1.sender,
2591 encrypted_encapsulated_packet=peer_1.encrypt_transport(
2593 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
2594 / UDP(sport=222, dport=223)
2600 for ii in range(255)
2603 rxs = self.send_and_expect(self.pg1, p, self.pg0, worker=1)
2606 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
2607 self.assertEqual(rx[IP].ttl, 19)
2609 # send a packets that are routed into the tunnel
2611 rxs = self.send_and_expect(self.pg0, pe * 255, self.pg1, worker=0)
2613 peer_1.validate_encapped(rxs, pe)
2615 r1.remove_vpp_config()
2616 peer_1.remove_vpp_config()
2617 wg0.remove_vpp_config()
2619 @unittest.skip("test disabled")
2620 def test_wg_multi_interface(self):
2621 """Multi-tunnel on the same port"""
2624 class TestWgFIB(VppTestCase):
2625 """Wireguard FIB Test Case"""
2628 def setUpClass(cls):
2629 super(TestWgFIB, cls).setUpClass()
2632 def tearDownClass(cls):
2633 super(TestWgFIB, cls).tearDownClass()
2636 super(TestWgFIB, self).setUp()
2638 self.create_pg_interfaces(range(2))
2640 for i in self.pg_interfaces:
2645 for i in self.pg_interfaces:
2648 super(TestWgFIB, self).tearDown()
2650 def test_wg_fib_tracking(self):
2654 # create wg interface
2655 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2659 self.pg_enable_capture(self.pg_interfaces)
2664 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
2666 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2668 # create a route to rewrite traffic into the wg interface
2670 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
2673 # resolve ARP and expect the adjacency to update
2674 self.pg1.resolve_arp()
2676 # wait for the peer to send a handshake initiation
2677 rxs = self.pg1.get_capture(2, timeout=6)
2679 # prepare and send a handshake response
2680 # expect a keepalive message
2681 resp = peer_1.consume_init(rxs[1], self.pg1)
2682 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
2684 # verify the keepalive message
2685 b = peer_1.decrypt_transport(rxs[0])
2686 self.assertEqual(0, len(b))
2688 # prepare and send a packet that will be rewritten into the wg interface
2689 # expect a data packet sent
2691 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2692 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
2693 / UDP(sport=555, dport=556)
2696 rxs = self.send_and_expect(self.pg0, [p], self.pg1)
2698 # verify the data packet
2699 peer_1.validate_encapped(rxs, p)
2702 r1.remove_vpp_config()
2703 peer_1.remove_vpp_config()
2704 wg0.remove_vpp_config()