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_ip_route import VppIpRoute, VppRoutePath
42 from vpp_object import VppObject
43 from vpp_papi import VppEnum
44 from framework import tag_fixme_ubuntu2204, tag_fixme_debian11
45 from framework import is_distro_ubuntu2204, is_distro_debian11
46 from framework import VppTestCase
47 from re import compile
50 """ TestWg is a subclass of VPPTestCase classes.
57 def private_key_bytes(k):
58 return k.private_bytes(Encoding.Raw, PrivateFormat.Raw, NoEncryption())
61 def public_key_bytes(k):
62 return k.public_bytes(Encoding.Raw, PublicFormat.Raw)
65 def get_field_bytes(pkt, name):
66 fld, val = pkt.getfield_and_val(name)
67 return fld.i2m(pkt, val)
70 class VppWgInterface(VppInterface):
72 VPP WireGuard interface
75 def __init__(self, test, src, port):
76 super(VppWgInterface, self).__init__(test)
80 self.private_key = X25519PrivateKey.generate()
81 self.public_key = self.private_key.public_key()
83 # cookie related params
84 self.cookie_key = blake2s(b"cookie--" + self.public_key_bytes()).digest()
86 def public_key_bytes(self):
87 return public_key_bytes(self.public_key)
89 def private_key_bytes(self):
90 return private_key_bytes(self.private_key)
92 def add_vpp_config(self):
93 r = self.test.vapi.wireguard_interface_create(
95 "user_instance": 0xFFFFFFFF,
98 "private_key": private_key_bytes(self.private_key),
99 "generate_key": False,
102 self.set_sw_if_index(r.sw_if_index)
103 self.test.registry.register(self, self.test.logger)
106 def remove_vpp_config(self):
107 self.test.vapi.wireguard_interface_delete(sw_if_index=self._sw_if_index)
109 def query_vpp_config(self):
110 ts = self.test.vapi.wireguard_interface_dump(sw_if_index=0xFFFFFFFF)
113 t.interface.sw_if_index == self._sw_if_index
114 and str(t.interface.src_ip) == self.src
115 and t.interface.port == self.port
116 and t.interface.private_key == private_key_bytes(self.private_key)
121 def want_events(self, peer_index=0xFFFFFFFF):
122 self.test.vapi.want_wireguard_peer_events(
125 sw_if_index=self._sw_if_index,
126 peer_index=peer_index,
129 def wait_events(self, expect, peers, timeout=5):
130 for i in range(len(peers)):
131 rv = self.test.vapi.wait_for_event(timeout, "wireguard_peer_event")
132 self.test.assertEqual(rv.peer_index, peers[i])
133 self.test.assertEqual(rv.flags, expect)
136 return self.object_id()
139 return "wireguard-%d" % self._sw_if_index
142 NOISE_HANDSHAKE_NAME = b"Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s"
143 NOISE_IDENTIFIER_NAME = b"WireGuard v1 zx2c4 Jason@zx2c4.com"
145 HANDSHAKE_COUNTING_INTERVAL = 0.5
146 UNDER_LOAD_INTERVAL = 1.0
147 HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD = 40
148 HANDSHAKE_NUM_BEFORE_RATELIMITING = 5
151 class VppWgPeer(VppObject):
152 def __init__(self, test, itf, endpoint, port, allowed_ips, persistent_keepalive=15):
155 self.endpoint = endpoint
157 self.allowed_ips = allowed_ips
158 self.persistent_keepalive = persistent_keepalive
160 # remote peer's public
161 self.private_key = X25519PrivateKey.generate()
162 self.public_key = self.private_key.public_key()
164 # cookie related params
165 self.cookie_key = blake2s(b"cookie--" + self.public_key_bytes()).digest()
166 self.last_sent_cookie = None
167 self.last_mac1 = None
168 self.last_received_cookie = None
170 self.noise = NoiseConnection.from_name(NOISE_HANDSHAKE_NAME)
172 def change_endpoint(self, endpoint, port):
173 self.endpoint = endpoint
176 def add_vpp_config(self, is_ip6=False):
177 rv = self._test.vapi.wireguard_peer_add(
179 "public_key": self.public_key_bytes(),
181 "endpoint": self.endpoint,
182 "n_allowed_ips": len(self.allowed_ips),
183 "allowed_ips": self.allowed_ips,
184 "sw_if_index": self.itf.sw_if_index,
185 "persistent_keepalive": self.persistent_keepalive,
188 self.index = rv.peer_index
189 self.receiver_index = self.index + 1
190 self._test.registry.register(self, self._test.logger)
193 def remove_vpp_config(self):
194 self._test.vapi.wireguard_peer_remove(peer_index=self.index)
197 return "wireguard-peer-%s" % self.index
199 def public_key_bytes(self):
200 return public_key_bytes(self.public_key)
202 def query_vpp_config(self):
203 peers = self._test.vapi.wireguard_peers_dump()
206 # "::" endpoint will be returned as "0.0.0.0" in peer's details
207 endpoint = "0.0.0.0" if self.endpoint == "::" else self.endpoint
209 p.peer.public_key == self.public_key_bytes()
210 and p.peer.port == self.port
211 and str(p.peer.endpoint) == endpoint
212 and p.peer.sw_if_index == self.itf.sw_if_index
213 and len(self.allowed_ips) == p.peer.n_allowed_ips
215 self.allowed_ips.sort()
216 p.peer.allowed_ips.sort()
218 for (a1, a2) in zip(self.allowed_ips, p.peer.allowed_ips):
219 if str(a1) != str(a2):
224 def mk_tunnel_header(self, tx_itf, is_ip6=False):
227 Ether(dst=tx_itf.local_mac, src=tx_itf.remote_mac)
228 / IP(src=self.endpoint, dst=self.itf.src)
229 / UDP(sport=self.port, dport=self.itf.port)
233 Ether(dst=tx_itf.local_mac, src=tx_itf.remote_mac)
234 / IPv6(src=self.endpoint, dst=self.itf.src)
235 / UDP(sport=self.port, dport=self.itf.port)
238 def noise_reset(self):
239 self.noise = NoiseConnection.from_name(NOISE_HANDSHAKE_NAME)
241 def noise_init(self, public_key=None):
242 self.noise.set_prologue(NOISE_IDENTIFIER_NAME)
243 self.noise.set_psks(psk=bytes(bytearray(32)))
246 public_key = self.itf.public_key
249 self.noise.set_keypair_from_private_bytes(
250 Keypair.STATIC, private_key_bytes(self.private_key)
253 self.noise.set_keypair_from_public_bytes(
254 Keypair.REMOTE_STATIC, public_key_bytes(public_key)
257 self.noise.start_handshake()
259 def mk_cookie(self, p, tx_itf, is_resp=False, is_ip6=False):
260 self.verify_header(p, is_ip6)
262 wg_pkt = Wireguard(p[Raw])
265 self._test.assertEqual(wg_pkt[Wireguard].message_type, 2)
266 self._test.assertEqual(wg_pkt[Wireguard].reserved_zero, 0)
267 self._test.assertEqual(wg_pkt[WireguardResponse].mac2, bytes([0] * 16))
269 self._test.assertEqual(wg_pkt[Wireguard].message_type, 1)
270 self._test.assertEqual(wg_pkt[Wireguard].reserved_zero, 0)
271 self._test.assertEqual(wg_pkt[WireguardInitiation].mac2, bytes([0] * 16))
273 # collect info from wg packet (initiation or response)
274 src = get_field_bytes(p[IPv6 if is_ip6 else IP], "src")
275 sport = p[UDP].sport.to_bytes(2, byteorder="big")
277 mac1 = wg_pkt[WireguardResponse].mac1
278 sender_index = wg_pkt[WireguardResponse].sender_index
280 mac1 = wg_pkt[WireguardInitiation].mac1
281 sender_index = wg_pkt[WireguardInitiation].sender_index
284 cookie_reply = Wireguard() / WireguardCookieReply()
285 cookie_reply[Wireguard].message_type = 3
286 cookie_reply[Wireguard].reserved_zero = 0
287 cookie_reply[WireguardCookieReply].receiver_index = sender_index
288 nonce = get_random_bytes(24)
289 cookie_reply[WireguardCookieReply].nonce = nonce
291 # generate cookie data
292 changing_secret = get_random_bytes(32)
293 self.last_sent_cookie = blake2s(
294 src + sport, digest_size=16, key=changing_secret
297 # encrypt cookie data
298 cipher = ChaCha20_Poly1305.new(key=self.cookie_key, nonce=nonce)
300 ciphertext, tag = cipher.encrypt_and_digest(self.last_sent_cookie)
301 cookie_reply[WireguardCookieReply].encrypted_cookie = ciphertext + tag
303 # prepare cookie reply to be sent
304 cookie_reply = self.mk_tunnel_header(tx_itf, is_ip6) / cookie_reply
308 def consume_cookie(self, p, is_ip6=False):
309 self.verify_header(p, is_ip6)
311 cookie_reply = Wireguard(p[Raw])
313 self._test.assertEqual(cookie_reply[Wireguard].message_type, 3)
314 self._test.assertEqual(cookie_reply[Wireguard].reserved_zero, 0)
315 self._test.assertEqual(
316 cookie_reply[WireguardCookieReply].receiver_index, self.receiver_index
319 # collect info from cookie reply
320 nonce = cookie_reply[WireguardCookieReply].nonce
321 encrypted_cookie = cookie_reply[WireguardCookieReply].encrypted_cookie
322 ciphertext, tag = encrypted_cookie[:16], encrypted_cookie[16:]
324 # decrypt cookie data
325 cipher = ChaCha20_Poly1305.new(key=self.itf.cookie_key, nonce=nonce)
326 cipher.update(self.last_mac1)
327 self.last_received_cookie = cipher.decrypt_and_verify(ciphertext, tag)
329 def mk_handshake(self, tx_itf, is_ip6=False, public_key=None):
330 self.noise.set_as_initiator()
331 self.noise_init(public_key)
333 p = Wireguard() / WireguardInitiation()
335 p[Wireguard].message_type = 1
336 p[Wireguard].reserved_zero = 0
337 p[WireguardInitiation].sender_index = self.receiver_index
339 # some random data for the message
340 # lifted from the noise protocol's wireguard example
341 now = datetime.datetime.now()
344 4611686018427387914 + int(now.timestamp()),
345 int(now.microsecond * 1e3),
347 b = self.noise.write_message(payload=tai)
349 # load noise into init message
350 p[WireguardInitiation].unencrypted_ephemeral = b[0:32]
351 p[WireguardInitiation].encrypted_static = b[32:80]
352 p[WireguardInitiation].encrypted_timestamp = b[80:108]
354 # generate the mac1 hash
355 mac_key = blake2s(b"mac1----" + self.itf.public_key_bytes()).digest()
356 mac1 = blake2s(bytes(p)[0:116], digest_size=16, key=mac_key).digest()
357 p[WireguardInitiation].mac1 = mac1
358 self.last_mac1 = mac1
360 # generate the mac2 hash
361 if self.last_received_cookie:
363 bytes(p)[0:132], digest_size=16, key=self.last_received_cookie
365 p[WireguardInitiation].mac2 = mac2
366 self.last_received_cookie = None
368 p[WireguardInitiation].mac2 = bytearray(16)
370 p = self.mk_tunnel_header(tx_itf, is_ip6) / p
374 def verify_header(self, p, is_ip6=False):
376 self._test.assertEqual(p[IP].src, self.itf.src)
377 self._test.assertEqual(p[IP].dst, self.endpoint)
379 self._test.assertEqual(p[IPv6].src, self.itf.src)
380 self._test.assertEqual(p[IPv6].dst, self.endpoint)
381 self._test.assertEqual(p[UDP].sport, self.itf.port)
382 self._test.assertEqual(p[UDP].dport, self.port)
383 self._test.assert_packet_checksums_valid(p)
385 def consume_init(self, p, tx_itf, is_ip6=False, is_mac2=False):
386 self.noise.set_as_responder()
387 self.noise_init(self.itf.public_key)
388 self.verify_header(p, is_ip6)
390 init = Wireguard(p[Raw])
392 self._test.assertEqual(init[Wireguard].message_type, 1)
393 self._test.assertEqual(init[Wireguard].reserved_zero, 0)
395 self.sender = init[WireguardInitiation].sender_index
397 # validate the mac1 hash
398 mac_key = blake2s(b"mac1----" + public_key_bytes(self.public_key)).digest()
399 mac1 = blake2s(bytes(init)[0:-32], digest_size=16, key=mac_key).digest()
400 self._test.assertEqual(init[WireguardInitiation].mac1, mac1)
402 # validate the mac2 hash
404 self._test.assertNotEqual(init[WireguardInitiation].mac2, bytes([0] * 16))
405 self._test.assertNotEqual(self.last_sent_cookie, None)
407 bytes(init)[0:-16], digest_size=16, key=self.last_sent_cookie
409 self._test.assertEqual(init[WireguardInitiation].mac2, mac2)
410 self.last_sent_cookie = None
412 self._test.assertEqual(init[WireguardInitiation].mac2, bytes([0] * 16))
414 # this passes only unencrypted_ephemeral, encrypted_static,
415 # encrypted_timestamp fields of the init
416 payload = self.noise.read_message(bytes(init)[8:-32])
419 b = self.noise.write_message()
420 mac_key = blake2s(b"mac1----" + public_key_bytes(self.itf.public_key)).digest()
421 resp = Wireguard(message_type=2, reserved_zero=0) / WireguardResponse(
422 sender_index=self.receiver_index,
423 receiver_index=self.sender,
424 unencrypted_ephemeral=b[0:32],
425 encrypted_nothing=b[32:],
427 mac1 = blake2s(bytes(resp)[:-32], digest_size=16, key=mac_key).digest()
428 resp[WireguardResponse].mac1 = mac1
429 self.last_mac1 = mac1
431 resp = self.mk_tunnel_header(tx_itf, is_ip6) / resp
432 self._test.assertTrue(self.noise.handshake_finished)
436 def consume_response(self, p, is_ip6=False):
437 self.verify_header(p, is_ip6)
439 resp = Wireguard(p[Raw])
441 self._test.assertEqual(resp[Wireguard].message_type, 2)
442 self._test.assertEqual(resp[Wireguard].reserved_zero, 0)
443 self._test.assertEqual(
444 resp[WireguardResponse].receiver_index, self.receiver_index
447 self.sender = resp[Wireguard].sender_index
449 payload = self.noise.read_message(bytes(resp)[12:60])
450 self._test.assertEqual(payload, b"")
451 self._test.assertTrue(self.noise.handshake_finished)
453 def decrypt_transport(self, p, is_ip6=False):
454 self.verify_header(p, is_ip6)
456 p = Wireguard(p[Raw])
457 self._test.assertEqual(p[Wireguard].message_type, 4)
458 self._test.assertEqual(p[Wireguard].reserved_zero, 0)
459 self._test.assertEqual(
460 p[WireguardTransport].receiver_index, self.receiver_index
463 d = self.noise.decrypt(p[WireguardTransport].encrypted_encapsulated_packet)
466 def encrypt_transport(self, p):
467 return self.noise.encrypt(bytes(p))
469 def validate_encapped(self, rxs, tx, is_ip6=False):
472 rx = IP(self.decrypt_transport(rx, is_ip6=is_ip6))
474 # check the original packet is present
475 self._test.assertEqual(rx[IP].dst, tx[IP].dst)
476 self._test.assertEqual(rx[IP].ttl, tx[IP].ttl - 1)
478 rx = IPv6(self.decrypt_transport(rx, is_ip6=is_ip6))
480 # check the original packet is present
481 self._test.assertEqual(rx[IPv6].dst, tx[IPv6].dst)
482 self._test.assertEqual(rx[IPv6].hlim, tx[IPv6].hlim - 1)
484 def want_events(self):
485 self._test.vapi.want_wireguard_peer_events(
488 peer_index=self.index,
489 sw_if_index=self.itf.sw_if_index,
492 def wait_event(self, expect, timeout=5):
493 rv = self._test.vapi.wait_for_event(timeout, "wireguard_peer_event")
494 self._test.assertEqual(rv.flags, expect)
495 self._test.assertEqual(rv.peer_index, self.index)
498 @tag_fixme_ubuntu2204
500 class TestWg(VppTestCase):
501 """Wireguard Test Case"""
503 error_str = compile(r"Error")
505 wg4_output_node_name = "/err/wg4-output-tun/"
506 wg4_input_node_name = "/err/wg4-input/"
507 wg6_output_node_name = "/err/wg6-output-tun/"
508 wg6_input_node_name = "/err/wg6-input/"
509 kp4_error = wg4_output_node_name + "Keypair error"
510 mac4_error = wg4_input_node_name + "Invalid MAC handshake"
511 peer4_in_err = wg4_input_node_name + "Peer error"
512 peer4_out_err = wg4_output_node_name + "Peer error"
513 kp6_error = wg6_output_node_name + "Keypair error"
514 mac6_error = wg6_input_node_name + "Invalid MAC handshake"
515 peer6_in_err = wg6_input_node_name + "Peer error"
516 peer6_out_err = wg6_output_node_name + "Peer error"
517 cookie_dec4_err = wg4_input_node_name + "Failed during Cookie decryption"
518 cookie_dec6_err = wg6_input_node_name + "Failed during Cookie decryption"
519 ratelimited4_err = wg4_input_node_name + "Handshake ratelimited"
520 ratelimited6_err = wg6_input_node_name + "Handshake ratelimited"
524 super(TestWg, cls).setUpClass()
525 if (is_distro_ubuntu2204 == True or is_distro_debian11 == True) and not hasattr(
530 cls.create_pg_interfaces(range(3))
531 for i in cls.pg_interfaces:
539 super(TestWg, cls).tearDownClass()
543 def tearDownClass(cls):
544 super(TestWg, cls).tearDownClass()
547 super(VppTestCase, self).setUp()
548 self.base_kp4_err = self.statistics.get_err_counter(self.kp4_error)
549 self.base_mac4_err = self.statistics.get_err_counter(self.mac4_error)
550 self.base_peer4_in_err = self.statistics.get_err_counter(self.peer4_in_err)
551 self.base_peer4_out_err = self.statistics.get_err_counter(self.peer4_out_err)
552 self.base_kp6_err = self.statistics.get_err_counter(self.kp6_error)
553 self.base_mac6_err = self.statistics.get_err_counter(self.mac6_error)
554 self.base_peer6_in_err = self.statistics.get_err_counter(self.peer6_in_err)
555 self.base_peer6_out_err = self.statistics.get_err_counter(self.peer6_out_err)
556 self.base_cookie_dec4_err = self.statistics.get_err_counter(
559 self.base_cookie_dec6_err = self.statistics.get_err_counter(
562 self.base_ratelimited4_err = self.statistics.get_err_counter(
563 self.ratelimited4_err
565 self.base_ratelimited6_err = self.statistics.get_err_counter(
566 self.ratelimited6_err
569 def test_wg_interface(self):
570 """Simple interface creation"""
574 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
576 self.logger.info(self.vapi.cli("sh int"))
579 wg0.remove_vpp_config()
581 def test_handshake_hash(self):
582 """test hashing an init message"""
583 # a init packet generated by linux given the key below
606 b = bytearray.fromhex(h)
609 pubb = base64.b64decode("aRuHFTTxICIQNefp05oKWlJv3zgKxb8+WW7JJMh0jyM=")
610 pub = X25519PublicKey.from_public_bytes(pubb)
612 self.assertEqual(pubb, public_key_bytes(pub))
614 # strip the macs and build a new packet
616 mac_key = blake2s(b"mac1----" + public_key_bytes(pub)).digest()
617 init += blake2s(init, digest_size=16, key=mac_key).digest()
620 act = Wireguard(init)
622 self.assertEqual(tgt, act)
624 def _test_wg_send_cookie_tmpl(self, is_resp, is_ip6):
627 # create wg interface
629 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
633 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
637 self.pg_enable_capture(self.pg_interfaces)
643 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
647 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
649 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
652 # prepare and send a handshake initiation
653 # expect the peer to send a handshake response
654 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
655 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
657 # wait for the peer to send a handshake initiation
658 rxs = self.pg1.get_capture(1, timeout=2)
660 # prepare and send a wrong cookie reply
661 # expect no replies and the cookie error incremented
662 cookie = peer_1.mk_cookie(rxs[0], self.pg1, is_resp=is_resp, is_ip6=is_ip6)
663 cookie.nonce = b"1234567890"
664 self.send_and_assert_no_replies(self.pg1, [cookie], timeout=0.1)
667 self.base_cookie_dec6_err + 1,
668 self.statistics.get_err_counter(self.cookie_dec6_err),
672 self.base_cookie_dec4_err + 1,
673 self.statistics.get_err_counter(self.cookie_dec4_err),
676 # prepare and send a correct cookie reply
677 cookie = peer_1.mk_cookie(rxs[0], self.pg1, is_resp=is_resp, is_ip6=is_ip6)
678 self.pg_send(self.pg1, [cookie])
680 # wait for the peer to send a handshake initiation with mac2 set
681 rxs = self.pg1.get_capture(1, timeout=6)
683 # verify the initiation and its mac2
684 peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6, is_mac2=True)
687 peer_1.remove_vpp_config()
688 wg0.remove_vpp_config()
690 def test_wg_send_cookie_on_init_v4(self):
691 """Send cookie on handshake initiation (v4)"""
692 self._test_wg_send_cookie_tmpl(is_resp=False, is_ip6=False)
694 def test_wg_send_cookie_on_init_v6(self):
695 """Send cookie on handshake initiation (v6)"""
696 self._test_wg_send_cookie_tmpl(is_resp=False, is_ip6=True)
698 def test_wg_send_cookie_on_resp_v4(self):
699 """Send cookie on handshake response (v4)"""
700 self._test_wg_send_cookie_tmpl(is_resp=True, is_ip6=False)
702 def test_wg_send_cookie_on_resp_v6(self):
703 """Send cookie on handshake response (v6)"""
704 self._test_wg_send_cookie_tmpl(is_resp=True, is_ip6=True)
706 def _test_wg_receive_cookie_tmpl(self, is_resp, is_ip6):
709 # create wg interface
711 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
715 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
719 self.pg_enable_capture(self.pg_interfaces)
725 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
729 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
731 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
734 # wait for the peer to send a handshake initiation
735 rxs = self.pg1.get_capture(1, timeout=2)
736 # prepare and send a bunch of handshake responses
737 # expect to switch to under load state
738 resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6)
739 txs = [resp] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
740 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
741 # reset noise to be able to turn into initiator later
744 # prepare and send a bunch of handshake initiations
745 # expect to switch to under load state
746 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
747 txs = [init] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
748 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
750 # expect the peer to send a cookie reply
751 peer_1.consume_cookie(rxs[-1], is_ip6=is_ip6)
753 # prepare and send a handshake initiation with wrong mac2
754 # expect a cookie reply
755 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
756 init.mac2 = b"1234567890"
757 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
758 peer_1.consume_cookie(rxs[0], is_ip6=is_ip6)
760 # prepare and send a handshake initiation with correct mac2
761 # expect a handshake response
762 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
763 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
765 # verify the response
766 peer_1.consume_response(rxs[0], is_ip6=is_ip6)
768 # clear up under load state
769 self.sleep(UNDER_LOAD_INTERVAL)
772 peer_1.remove_vpp_config()
773 wg0.remove_vpp_config()
775 def test_wg_receive_cookie_on_init_v4(self):
776 """Receive cookie on handshake initiation (v4)"""
777 self._test_wg_receive_cookie_tmpl(is_resp=False, is_ip6=False)
779 def test_wg_receive_cookie_on_init_v6(self):
780 """Receive cookie on handshake initiation (v6)"""
781 self._test_wg_receive_cookie_tmpl(is_resp=False, is_ip6=True)
783 def test_wg_receive_cookie_on_resp_v4(self):
784 """Receive cookie on handshake response (v4)"""
785 self._test_wg_receive_cookie_tmpl(is_resp=True, is_ip6=False)
787 def test_wg_receive_cookie_on_resp_v6(self):
788 """Receive cookie on handshake response (v6)"""
789 self._test_wg_receive_cookie_tmpl(is_resp=True, is_ip6=True)
791 def test_wg_under_load_interval(self):
792 """Under load interval"""
795 # create wg interface
796 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
800 self.pg_enable_capture(self.pg_interfaces)
805 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
807 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
809 # prepare and send a bunch of handshake initiations
810 # expect to switch to under load state
811 init = peer_1.mk_handshake(self.pg1)
812 txs = [init] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
813 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
815 # expect the peer to send a cookie reply
816 peer_1.consume_cookie(rxs[-1])
818 # sleep till the next counting interval
819 # expect under load state is still active
820 self.sleep(HANDSHAKE_COUNTING_INTERVAL)
822 # prepare and send a handshake initiation with wrong mac2
823 # expect a cookie reply
824 init = peer_1.mk_handshake(self.pg1)
825 init.mac2 = b"1234567890"
826 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
827 peer_1.consume_cookie(rxs[0])
829 # sleep till the end of being under load
830 # expect under load state is over
831 self.sleep(UNDER_LOAD_INTERVAL - HANDSHAKE_COUNTING_INTERVAL)
833 # prepare and send a handshake initiation with wrong mac2
834 # expect a handshake response
835 init = peer_1.mk_handshake(self.pg1)
836 init.mac2 = b"1234567890"
837 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
839 # verify the response
840 peer_1.consume_response(rxs[0])
843 peer_1.remove_vpp_config()
844 wg0.remove_vpp_config()
846 def _test_wg_handshake_ratelimiting_tmpl(self, is_ip6):
849 # create wg interface
851 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
855 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
859 self.pg_enable_capture(self.pg_interfaces)
865 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
869 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
871 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
873 # prepare and send a bunch of handshake initiations
874 # expect to switch to under load state
875 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
876 txs = [init] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
877 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
879 # expect the peer to send a cookie reply
880 peer_1.consume_cookie(rxs[-1], is_ip6=is_ip6)
882 # prepare and send a bunch of handshake initiations with correct mac2
883 # expect a handshake response and then ratelimiting
885 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
886 txs = [init] * (HANDSHAKE_NUM_BEFORE_RATELIMITING + NUM_TO_REJECT)
887 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
891 self.base_ratelimited6_err + NUM_TO_REJECT,
892 self.statistics.get_err_counter(self.ratelimited6_err),
896 self.base_ratelimited4_err + NUM_TO_REJECT,
897 self.statistics.get_err_counter(self.ratelimited4_err),
900 # verify the response
901 peer_1.consume_response(rxs[0], is_ip6=is_ip6)
903 # clear up under load state
904 self.sleep(UNDER_LOAD_INTERVAL)
907 peer_1.remove_vpp_config()
908 wg0.remove_vpp_config()
910 def test_wg_handshake_ratelimiting_v4(self):
911 """Handshake ratelimiting (v4)"""
912 self._test_wg_handshake_ratelimiting_tmpl(is_ip6=False)
914 def test_wg_handshake_ratelimiting_v6(self):
915 """Handshake ratelimiting (v6)"""
916 self._test_wg_handshake_ratelimiting_tmpl(is_ip6=True)
918 def test_wg_handshake_ratelimiting_multi_peer(self):
919 """Handshake ratelimiting (multiple peer)"""
922 # create wg interface
923 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
927 self.pg_enable_capture(self.pg_interfaces)
932 self.pg1.generate_remote_hosts(NUM_PEERS)
933 self.pg1.configure_ipv4_neighbors()
936 self, wg0, self.pg1.remote_hosts[0].ip4, port + 1, ["10.11.3.0/24"]
939 self, wg0, self.pg1.remote_hosts[1].ip4, port + 1, ["10.11.4.0/24"]
941 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 2)
943 # (peer_1) prepare and send a bunch of handshake initiations
944 # expect not to switch to under load state
945 init_1 = peer_1.mk_handshake(self.pg1)
946 txs = [init_1] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
947 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
949 # (peer_1) expect the peer to send a handshake response
950 peer_1.consume_response(rxs[0])
953 # (peer_1) send another bunch of handshake initiations
954 # expect to switch to under load state
955 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
957 # (peer_1) expect the peer to send a cookie reply
958 peer_1.consume_cookie(rxs[-1])
960 # (peer_2) prepare and send a handshake initiation
961 # expect a cookie reply
962 init_2 = peer_2.mk_handshake(self.pg1)
963 rxs = self.send_and_expect(self.pg1, [init_2], self.pg1)
964 peer_2.consume_cookie(rxs[0])
966 # (peer_1) prepare and send a bunch of handshake initiations with correct mac2
967 # expect no ratelimiting and a handshake response
968 init_1 = peer_1.mk_handshake(self.pg1)
969 txs = [init_1] * HANDSHAKE_NUM_BEFORE_RATELIMITING
970 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
972 self.base_ratelimited4_err,
973 self.statistics.get_err_counter(self.ratelimited4_err),
976 # (peer_1) verify the response
977 peer_1.consume_response(rxs[0])
980 # (peer_1) send another two handshake initiations with correct mac2
981 # expect ratelimiting
982 # (peer_2) prepare and send a handshake initiation with correct mac2
983 # expect no ratelimiting and a handshake response
984 init_2 = peer_2.mk_handshake(self.pg1)
985 txs = [init_1, init_2, init_1]
986 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
988 # (peer_1) verify ratelimiting
990 self.base_ratelimited4_err + 2,
991 self.statistics.get_err_counter(self.ratelimited4_err),
994 # (peer_2) verify the response
995 peer_2.consume_response(rxs[0])
997 # clear up under load state
998 self.sleep(UNDER_LOAD_INTERVAL)
1001 peer_1.remove_vpp_config()
1002 peer_2.remove_vpp_config()
1003 wg0.remove_vpp_config()
1005 def _test_wg_peer_roaming_on_handshake_tmpl(self, is_endpoint_set, is_resp, is_ip6):
1008 # create wg interface
1010 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1014 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1018 self.pg_enable_capture(self.pg_interfaces)
1021 # create more remote hosts
1022 NUM_REMOTE_HOSTS = 2
1023 self.pg1.generate_remote_hosts(NUM_REMOTE_HOSTS)
1025 self.pg1.configure_ipv6_neighbors()
1027 self.pg1.configure_ipv4_neighbors()
1034 endpoint=self.pg1.remote_hosts[0].ip6 if is_endpoint_set else "::",
1035 port=port + 1 if is_endpoint_set else 0,
1036 allowed_ips=["1::3:0/112"],
1042 endpoint=self.pg1.remote_hosts[0].ip4 if is_endpoint_set else "0.0.0.0",
1043 port=port + 1 if is_endpoint_set else 0,
1044 allowed_ips=["10.11.3.0/24"],
1046 self.assertTrue(peer_1.query_vpp_config())
1049 # wait for the peer to send a handshake initiation
1050 rxs = self.pg1.get_capture(1, timeout=2)
1051 # prepare a handshake response
1052 resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6)
1055 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip6, port + 100)
1056 resp[IPv6].src, resp[UDP].sport = peer_1.endpoint, peer_1.port
1058 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip4, port + 100)
1059 resp[IP].src, resp[UDP].sport = peer_1.endpoint, peer_1.port
1060 # send the handshake response
1061 # expect a keepalive message sent to the new endpoint
1062 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1063 # verify the keepalive message
1064 b = peer_1.decrypt_transport(rxs[0], is_ip6=is_ip6)
1065 self.assertEqual(0, len(b))
1069 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip6, port + 100)
1071 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip4, port + 100)
1072 # prepare and send a handshake initiation
1073 # expect a handshake response sent to the new endpoint
1074 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
1075 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
1076 # verify the response
1077 peer_1.consume_response(rxs[0], is_ip6=is_ip6)
1078 self.assertTrue(peer_1.query_vpp_config())
1081 peer_1.remove_vpp_config()
1082 wg0.remove_vpp_config()
1084 def test_wg_peer_roaming_on_init_v4(self):
1085 """Peer roaming on handshake initiation (v4)"""
1086 self._test_wg_peer_roaming_on_handshake_tmpl(
1087 is_endpoint_set=False, is_resp=False, is_ip6=False
1090 def test_wg_peer_roaming_on_init_v6(self):
1091 """Peer roaming on handshake initiation (v6)"""
1092 self._test_wg_peer_roaming_on_handshake_tmpl(
1093 is_endpoint_set=False, is_resp=False, is_ip6=True
1096 def test_wg_peer_roaming_on_resp_v4(self):
1097 """Peer roaming on handshake response (v4)"""
1098 self._test_wg_peer_roaming_on_handshake_tmpl(
1099 is_endpoint_set=True, is_resp=True, is_ip6=False
1102 def test_wg_peer_roaming_on_resp_v6(self):
1103 """Peer roaming on handshake response (v6)"""
1104 self._test_wg_peer_roaming_on_handshake_tmpl(
1105 is_endpoint_set=True, is_resp=True, is_ip6=True
1108 def _test_wg_peer_roaming_on_data_tmpl(self, is_async, is_ip6):
1109 self.vapi.wg_set_async_mode(is_async)
1112 # create wg interface
1114 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1118 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1122 self.pg_enable_capture(self.pg_interfaces)
1125 # create more remote hosts
1126 NUM_REMOTE_HOSTS = 2
1127 self.pg1.generate_remote_hosts(NUM_REMOTE_HOSTS)
1129 self.pg1.configure_ipv6_neighbors()
1131 self.pg1.configure_ipv4_neighbors()
1136 self, wg0, self.pg1.remote_hosts[0].ip6, port + 1, ["1::3:0/112"]
1140 self, wg0, self.pg1.remote_hosts[0].ip4, port + 1, ["10.11.3.0/24"]
1142 self.assertTrue(peer_1.query_vpp_config())
1144 # create a route to rewrite traffic into the wg interface
1147 self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
1151 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1154 # wait for the peer to send a handshake initiation
1155 rxs = self.pg1.get_capture(1, timeout=2)
1157 # prepare and send a handshake response
1158 # expect a keepalive message
1159 resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6)
1160 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1162 # verify the keepalive message
1163 b = peer_1.decrypt_transport(rxs[0], is_ip6=is_ip6)
1164 self.assertEqual(0, len(b))
1168 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip6, port + 100)
1170 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip4, port + 100)
1172 # prepare and send a data packet
1173 # expect endpoint change
1175 ip_header = IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1177 ip_header = IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1179 peer_1.mk_tunnel_header(self.pg1, is_ip6=is_ip6)
1180 / Wireguard(message_type=4, reserved_zero=0)
1181 / WireguardTransport(
1182 receiver_index=peer_1.sender,
1184 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1185 ip_header / UDP(sport=222, dport=223) / Raw()
1189 rxs = self.send_and_expect(self.pg1, [data], self.pg0)
1191 self.assertEqual(rxs[0][IPv6].dst, self.pg0.remote_ip6)
1192 self.assertEqual(rxs[0][IPv6].hlim, 19)
1194 self.assertEqual(rxs[0][IP].dst, self.pg0.remote_ip4)
1195 self.assertEqual(rxs[0][IP].ttl, 19)
1196 self.assertTrue(peer_1.query_vpp_config())
1198 # prepare and send a packet that will be rewritten into the wg interface
1199 # expect a data packet sent to the new endpoint
1201 ip_header = IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1203 ip_header = IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1205 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1207 / UDP(sport=555, dport=556)
1210 rxs = self.send_and_expect(self.pg0, [p], self.pg1)
1212 # verify the data packet
1213 peer_1.validate_encapped(rxs, p, is_ip6=is_ip6)
1216 r1.remove_vpp_config()
1217 peer_1.remove_vpp_config()
1218 wg0.remove_vpp_config()
1220 def test_wg_peer_roaming_on_data_v4_sync(self):
1221 """Peer roaming on data packet (v4, sync)"""
1222 self._test_wg_peer_roaming_on_data_tmpl(is_async=False, is_ip6=False)
1224 def test_wg_peer_roaming_on_data_v6_sync(self):
1225 """Peer roaming on data packet (v6, sync)"""
1226 self._test_wg_peer_roaming_on_data_tmpl(is_async=False, is_ip6=True)
1228 def test_wg_peer_roaming_on_data_v4_async(self):
1229 """Peer roaming on data packet (v4, async)"""
1230 self._test_wg_peer_roaming_on_data_tmpl(is_async=True, is_ip6=False)
1232 def test_wg_peer_roaming_on_data_v6_async(self):
1233 """Peer roaming on data packet (v6, async)"""
1234 self._test_wg_peer_roaming_on_data_tmpl(is_async=True, is_ip6=True)
1236 def test_wg_peer_resp(self):
1237 """Send handshake response"""
1241 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1245 self.pg_enable_capture(self.pg_interfaces)
1249 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
1251 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1254 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1257 # wait for the peer to send a handshake
1258 rx = self.pg1.get_capture(1, timeout=2)
1260 # consume the handshake in the noise protocol and
1261 # generate the response
1262 resp = peer_1.consume_init(rx[0], self.pg1)
1264 # send the response, get keepalive
1265 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1268 b = peer_1.decrypt_transport(rx)
1269 self.assertEqual(0, len(b))
1271 # send a packets that are routed into the tunnel
1273 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1274 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1275 / UDP(sport=555, dport=556)
1279 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1281 peer_1.validate_encapped(rxs, p)
1283 # send packets into the tunnel, expect to receive them on
1287 peer_1.mk_tunnel_header(self.pg1)
1288 / Wireguard(message_type=4, reserved_zero=0)
1289 / WireguardTransport(
1290 receiver_index=peer_1.sender,
1292 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1294 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1295 / UDP(sport=222, dport=223)
1301 for ii in range(255)
1304 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1307 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1308 self.assertEqual(rx[IP].ttl, 19)
1310 r1.remove_vpp_config()
1311 peer_1.remove_vpp_config()
1312 wg0.remove_vpp_config()
1314 def test_wg_peer_v4o4(self):
1320 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1325 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
1327 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1330 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1333 self, "20.22.3.0", 24, [VppRoutePath("20.22.3.1", wg0.sw_if_index)]
1336 # route a packet into the wg interface
1337 # use the allowed-ip prefix
1338 # this is dropped because the peer is not initiated
1340 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1341 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1342 / UDP(sport=555, dport=556)
1345 self.send_and_assert_no_replies(self.pg0, [p])
1347 self.base_kp4_err + 1, self.statistics.get_err_counter(self.kp4_error)
1350 # route a packet into the wg interface
1351 # use a not allowed-ip prefix
1352 # this is dropped because there is no matching peer
1354 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1355 / IP(src=self.pg0.remote_ip4, dst="20.22.3.2")
1356 / UDP(sport=555, dport=556)
1359 self.send_and_assert_no_replies(self.pg0, [p])
1361 self.base_peer4_out_err + 1,
1362 self.statistics.get_err_counter(self.peer4_out_err),
1365 # send a handsake from the peer with an invalid MAC
1366 p = peer_1.mk_handshake(self.pg1)
1367 p[WireguardInitiation].mac1 = b"foobar"
1368 self.send_and_assert_no_replies(self.pg1, [p])
1370 self.base_mac4_err + 1, self.statistics.get_err_counter(self.mac4_error)
1373 # send a handsake from the peer but signed by the wrong key.
1374 p = peer_1.mk_handshake(
1375 self.pg1, False, X25519PrivateKey.generate().public_key()
1377 self.send_and_assert_no_replies(self.pg1, [p])
1379 self.base_peer4_in_err + 1,
1380 self.statistics.get_err_counter(self.peer4_in_err),
1383 # send a valid handsake init for which we expect a response
1384 p = peer_1.mk_handshake(self.pg1)
1386 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1388 peer_1.consume_response(rx[0])
1390 # route a packet into the wg interface
1391 # this is dropped because the peer is still not initiated
1393 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1394 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1395 / UDP(sport=555, dport=556)
1398 self.send_and_assert_no_replies(self.pg0, [p])
1400 self.base_kp4_err + 2, self.statistics.get_err_counter(self.kp4_error)
1403 # send a data packet from the peer through the tunnel
1404 # this completes the handshake
1406 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1407 / UDP(sport=222, dport=223)
1410 d = peer_1.encrypt_transport(p)
1411 p = peer_1.mk_tunnel_header(self.pg1) / (
1412 Wireguard(message_type=4, reserved_zero=0)
1413 / WireguardTransport(
1414 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1417 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1420 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1421 self.assertEqual(rx[IP].ttl, 19)
1423 # send a packets that are routed into the tunnel
1425 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1426 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1427 / UDP(sport=555, dport=556)
1431 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1434 rx = IP(peer_1.decrypt_transport(rx))
1436 # check the original packet is present
1437 self.assertEqual(rx[IP].dst, p[IP].dst)
1438 self.assertEqual(rx[IP].ttl, p[IP].ttl - 1)
1440 # send packets into the tunnel, expect to receive them on
1444 peer_1.mk_tunnel_header(self.pg1)
1445 / Wireguard(message_type=4, reserved_zero=0)
1446 / WireguardTransport(
1447 receiver_index=peer_1.sender,
1449 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1451 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1452 / UDP(sport=222, dport=223)
1458 for ii in range(255)
1461 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1464 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1465 self.assertEqual(rx[IP].ttl, 19)
1467 r1.remove_vpp_config()
1468 r2.remove_vpp_config()
1469 peer_1.remove_vpp_config()
1470 wg0.remove_vpp_config()
1472 def test_wg_peer_v6o6(self):
1478 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1483 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
1484 ).add_vpp_config(True)
1485 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1488 self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
1491 self, "22::3:0", 112, [VppRoutePath("22::3:1", wg0.sw_if_index)]
1494 # route a packet into the wg interface
1495 # use the allowed-ip prefix
1496 # this is dropped because the peer is not initiated
1499 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1500 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1501 / UDP(sport=555, dport=556)
1504 self.send_and_assert_no_replies(self.pg0, [p])
1507 self.base_kp6_err + 1, self.statistics.get_err_counter(self.kp6_error)
1510 # route a packet into the wg interface
1511 # use a not allowed-ip prefix
1512 # this is dropped because there is no matching peer
1514 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1515 / IPv6(src=self.pg0.remote_ip6, dst="22::3:2")
1516 / UDP(sport=555, dport=556)
1519 self.send_and_assert_no_replies(self.pg0, [p])
1521 self.base_peer6_out_err + 1,
1522 self.statistics.get_err_counter(self.peer6_out_err),
1525 # send a handsake from the peer with an invalid MAC
1526 p = peer_1.mk_handshake(self.pg1, True)
1527 p[WireguardInitiation].mac1 = b"foobar"
1528 self.send_and_assert_no_replies(self.pg1, [p])
1531 self.base_mac6_err + 1, self.statistics.get_err_counter(self.mac6_error)
1534 # send a handsake from the peer but signed by the wrong key.
1535 p = peer_1.mk_handshake(
1536 self.pg1, True, X25519PrivateKey.generate().public_key()
1538 self.send_and_assert_no_replies(self.pg1, [p])
1540 self.base_peer6_in_err + 1,
1541 self.statistics.get_err_counter(self.peer6_in_err),
1544 # send a valid handsake init for which we expect a response
1545 p = peer_1.mk_handshake(self.pg1, True)
1547 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1549 peer_1.consume_response(rx[0], True)
1551 # route a packet into the wg interface
1552 # this is dropped because the peer is still not initiated
1554 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1555 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1556 / UDP(sport=555, dport=556)
1559 self.send_and_assert_no_replies(self.pg0, [p])
1561 self.base_kp6_err + 2, self.statistics.get_err_counter(self.kp6_error)
1564 # send a data packet from the peer through the tunnel
1565 # this completes the handshake
1567 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1568 / UDP(sport=222, dport=223)
1571 d = peer_1.encrypt_transport(p)
1572 p = peer_1.mk_tunnel_header(self.pg1, True) / (
1573 Wireguard(message_type=4, reserved_zero=0)
1574 / WireguardTransport(
1575 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1578 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1581 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1582 self.assertEqual(rx[IPv6].hlim, 19)
1584 # send a packets that are routed into the tunnel
1586 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1587 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1588 / UDP(sport=555, dport=556)
1592 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1595 rx = IPv6(peer_1.decrypt_transport(rx, True))
1597 # check the original packet is present
1598 self.assertEqual(rx[IPv6].dst, p[IPv6].dst)
1599 self.assertEqual(rx[IPv6].hlim, p[IPv6].hlim - 1)
1601 # send packets into the tunnel, expect to receive them on
1605 peer_1.mk_tunnel_header(self.pg1, True)
1606 / Wireguard(message_type=4, reserved_zero=0)
1607 / WireguardTransport(
1608 receiver_index=peer_1.sender,
1610 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1612 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1613 / UDP(sport=222, dport=223)
1619 for ii in range(255)
1622 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1625 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1626 self.assertEqual(rx[IPv6].hlim, 19)
1628 r1.remove_vpp_config()
1629 r2.remove_vpp_config()
1630 peer_1.remove_vpp_config()
1631 wg0.remove_vpp_config()
1633 def test_wg_peer_v6o4(self):
1639 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1644 self, wg0, self.pg1.remote_ip4, port + 1, ["1::3:0/112"]
1645 ).add_vpp_config(True)
1646 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1649 self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
1652 # route a packet into the wg interface
1653 # use the allowed-ip prefix
1654 # this is dropped because the peer is not initiated
1656 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1657 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1658 / UDP(sport=555, dport=556)
1661 self.send_and_assert_no_replies(self.pg0, [p])
1663 self.base_kp6_err + 1, self.statistics.get_err_counter(self.kp6_error)
1666 # send a handsake from the peer with an invalid MAC
1667 p = peer_1.mk_handshake(self.pg1)
1668 p[WireguardInitiation].mac1 = b"foobar"
1669 self.send_and_assert_no_replies(self.pg1, [p])
1672 self.base_mac4_err + 1, self.statistics.get_err_counter(self.mac4_error)
1675 # send a handsake from the peer but signed by the wrong key.
1676 p = peer_1.mk_handshake(
1677 self.pg1, False, X25519PrivateKey.generate().public_key()
1679 self.send_and_assert_no_replies(self.pg1, [p])
1681 self.base_peer4_in_err + 1,
1682 self.statistics.get_err_counter(self.peer4_in_err),
1685 # send a valid handsake init for which we expect a response
1686 p = peer_1.mk_handshake(self.pg1)
1688 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1690 peer_1.consume_response(rx[0])
1692 # route a packet into the wg interface
1693 # this is dropped because the peer is still not initiated
1695 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1696 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1697 / UDP(sport=555, dport=556)
1700 self.send_and_assert_no_replies(self.pg0, [p])
1702 self.base_kp6_err + 2, self.statistics.get_err_counter(self.kp6_error)
1705 # send a data packet from the peer through the tunnel
1706 # this completes the handshake
1708 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1709 / UDP(sport=222, dport=223)
1712 d = peer_1.encrypt_transport(p)
1713 p = peer_1.mk_tunnel_header(self.pg1) / (
1714 Wireguard(message_type=4, reserved_zero=0)
1715 / WireguardTransport(
1716 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1719 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1722 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1723 self.assertEqual(rx[IPv6].hlim, 19)
1725 # send a packets that are routed into the tunnel
1727 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1728 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1729 / UDP(sport=555, dport=556)
1733 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1736 rx = IPv6(peer_1.decrypt_transport(rx))
1738 # check the original packet is present
1739 self.assertEqual(rx[IPv6].dst, p[IPv6].dst)
1740 self.assertEqual(rx[IPv6].hlim, p[IPv6].hlim - 1)
1742 # send packets into the tunnel, expect to receive them on
1746 peer_1.mk_tunnel_header(self.pg1)
1747 / Wireguard(message_type=4, reserved_zero=0)
1748 / WireguardTransport(
1749 receiver_index=peer_1.sender,
1751 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1753 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1754 / UDP(sport=222, dport=223)
1760 for ii in range(255)
1763 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1766 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1767 self.assertEqual(rx[IPv6].hlim, 19)
1769 r1.remove_vpp_config()
1770 peer_1.remove_vpp_config()
1771 wg0.remove_vpp_config()
1773 def test_wg_peer_v4o6(self):
1779 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1784 self, wg0, self.pg1.remote_ip6, port + 1, ["10.11.3.0/24"]
1786 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1789 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1792 # route a packet into the wg interface
1793 # use the allowed-ip prefix
1794 # this is dropped because the peer is not initiated
1796 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1797 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1798 / UDP(sport=555, dport=556)
1801 self.send_and_assert_no_replies(self.pg0, [p])
1803 self.base_kp4_err + 1, self.statistics.get_err_counter(self.kp4_error)
1806 # send a handsake from the peer with an invalid MAC
1807 p = peer_1.mk_handshake(self.pg1, True)
1808 p[WireguardInitiation].mac1 = b"foobar"
1809 self.send_and_assert_no_replies(self.pg1, [p])
1811 self.base_mac6_err + 1, self.statistics.get_err_counter(self.mac6_error)
1814 # send a handsake from the peer but signed by the wrong key.
1815 p = peer_1.mk_handshake(
1816 self.pg1, True, X25519PrivateKey.generate().public_key()
1818 self.send_and_assert_no_replies(self.pg1, [p])
1820 self.base_peer6_in_err + 1,
1821 self.statistics.get_err_counter(self.peer6_in_err),
1824 # send a valid handsake init for which we expect a response
1825 p = peer_1.mk_handshake(self.pg1, True)
1827 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1829 peer_1.consume_response(rx[0], True)
1831 # route a packet into the wg interface
1832 # this is dropped because the peer is still not initiated
1834 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1835 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1836 / UDP(sport=555, dport=556)
1839 self.send_and_assert_no_replies(self.pg0, [p])
1841 self.base_kp4_err + 2, self.statistics.get_err_counter(self.kp4_error)
1844 # send a data packet from the peer through the tunnel
1845 # this completes the handshake
1847 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1848 / UDP(sport=222, dport=223)
1851 d = peer_1.encrypt_transport(p)
1852 p = peer_1.mk_tunnel_header(self.pg1, True) / (
1853 Wireguard(message_type=4, reserved_zero=0)
1854 / WireguardTransport(
1855 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1858 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1861 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1862 self.assertEqual(rx[IP].ttl, 19)
1864 # send a packets that are routed into the tunnel
1866 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1867 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1868 / UDP(sport=555, dport=556)
1872 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1875 rx = IP(peer_1.decrypt_transport(rx, True))
1877 # check the original packet is present
1878 self.assertEqual(rx[IP].dst, p[IP].dst)
1879 self.assertEqual(rx[IP].ttl, p[IP].ttl - 1)
1881 # send packets into the tunnel, expect to receive them on
1885 peer_1.mk_tunnel_header(self.pg1, True)
1886 / Wireguard(message_type=4, reserved_zero=0)
1887 / WireguardTransport(
1888 receiver_index=peer_1.sender,
1890 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1892 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1893 / UDP(sport=222, dport=223)
1899 for ii in range(255)
1902 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1905 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1906 self.assertEqual(rx[IP].ttl, 19)
1908 r1.remove_vpp_config()
1909 peer_1.remove_vpp_config()
1910 wg0.remove_vpp_config()
1912 def test_wg_multi_peer(self):
1913 """multiple peer setup"""
1917 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1918 wg1 = VppWgInterface(self, self.pg2.local_ip4, port + 1).add_vpp_config()
1922 # Check peer counter
1923 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 0)
1925 self.pg_enable_capture(self.pg_interfaces)
1928 # Create many peers on sencond interface
1930 self.pg2.generate_remote_hosts(NUM_PEERS)
1931 self.pg2.configure_ipv4_neighbors()
1932 self.pg1.generate_remote_hosts(NUM_PEERS)
1933 self.pg1.configure_ipv4_neighbors()
1939 for i in range(NUM_PEERS):
1944 self.pg1.remote_hosts[i].ip4,
1946 ["10.0.%d.4/32" % i],
1954 [VppRoutePath(self.pg1.remote_hosts[i].ip4, wg0.sw_if_index)],
1962 self.pg2.remote_hosts[i].ip4,
1964 ["10.100.%d.4/32" % i],
1972 [VppRoutePath(self.pg2.remote_hosts[i].ip4, wg1.sw_if_index)],
1976 self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_PEERS * 2)
1978 self.logger.info(self.vapi.cli("show wireguard peer"))
1979 self.logger.info(self.vapi.cli("show wireguard interface"))
1980 self.logger.info(self.vapi.cli("show adj 37"))
1981 self.logger.info(self.vapi.cli("sh ip fib 172.16.3.17"))
1982 self.logger.info(self.vapi.cli("sh ip fib 10.11.3.0"))
1986 r.remove_vpp_config()
1988 r.remove_vpp_config()
1992 self.assertTrue(p.query_vpp_config())
1993 p.remove_vpp_config()
1995 self.assertTrue(p.query_vpp_config())
1996 p.remove_vpp_config()
1998 wg0.remove_vpp_config()
1999 wg1.remove_vpp_config()
2001 def test_wg_multi_interface(self):
2002 """Multi-tunnel on the same port"""
2005 # Create many wireguard interfaces
2007 self.pg1.generate_remote_hosts(NUM_IFS)
2008 self.pg1.configure_ipv4_neighbors()
2009 self.pg0.generate_remote_hosts(NUM_IFS)
2010 self.pg0.configure_ipv4_neighbors()
2012 # Create interfaces with a peer on each
2016 for i in range(NUM_IFS):
2017 # Use the same port for each interface
2018 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2026 self.pg1.remote_hosts[i].ip4,
2028 ["10.0.%d.0/24" % i],
2037 [VppRoutePath("10.0.%d.4" % i, wg0.sw_if_index)],
2041 self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_IFS)
2043 for i in range(NUM_IFS):
2044 # send a valid handsake init for which we expect a response
2045 p = peers[i].mk_handshake(self.pg1)
2046 rx = self.send_and_expect(self.pg1, [p], self.pg1)
2047 peers[i].consume_response(rx[0])
2049 # send a data packet from the peer through the tunnel
2050 # this completes the handshake
2052 IP(src="10.0.%d.4" % i, dst=self.pg0.remote_hosts[i].ip4, ttl=20)
2053 / UDP(sport=222, dport=223)
2056 d = peers[i].encrypt_transport(p)
2057 p = peers[i].mk_tunnel_header(self.pg1) / (
2058 Wireguard(message_type=4, reserved_zero=0)
2059 / WireguardTransport(
2060 receiver_index=peers[i].sender,
2062 encrypted_encapsulated_packet=d,
2065 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
2067 self.assertEqual(rx[IP].dst, self.pg0.remote_hosts[i].ip4)
2068 self.assertEqual(rx[IP].ttl, 19)
2070 # send a packets that are routed into the tunnel
2071 for i in range(NUM_IFS):
2073 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2074 / IP(src=self.pg0.remote_hosts[i].ip4, dst="10.0.%d.4" % i)
2075 / UDP(sport=555, dport=556)
2079 rxs = self.send_and_expect(self.pg0, p * 64, self.pg1)
2082 rx = IP(peers[i].decrypt_transport(rx))
2084 # check the oringial packet is present
2085 self.assertEqual(rx[IP].dst, p[IP].dst)
2086 self.assertEqual(rx[IP].ttl, p[IP].ttl - 1)
2088 # send packets into the tunnel
2089 for i in range(NUM_IFS):
2092 peers[i].mk_tunnel_header(self.pg1)
2093 / Wireguard(message_type=4, reserved_zero=0)
2094 / WireguardTransport(
2095 receiver_index=peers[i].sender,
2097 encrypted_encapsulated_packet=peers[i].encrypt_transport(
2100 src="10.0.%d.4" % i,
2101 dst=self.pg0.remote_hosts[i].ip4,
2104 / UDP(sport=222, dport=223)
2113 rxs = self.send_and_expect(self.pg1, p, self.pg0)
2116 self.assertEqual(rx[IP].dst, self.pg0.remote_hosts[i].ip4)
2117 self.assertEqual(rx[IP].ttl, 19)
2120 r.remove_vpp_config()
2122 p.remove_vpp_config()
2124 i.remove_vpp_config()
2126 def test_wg_event(self):
2129 ESTABLISHED_FLAG = (
2130 VppEnum.vl_api_wireguard_peer_flags_t.WIREGUARD_PEER_ESTABLISHED
2132 DEAD_FLAG = VppEnum.vl_api_wireguard_peer_flags_t.WIREGUARD_PEER_STATUS_DEAD
2135 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2136 wg1 = VppWgInterface(self, self.pg2.local_ip4, port + 1).add_vpp_config()
2140 # Check peer counter
2141 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 0)
2143 self.pg_enable_capture(self.pg_interfaces)
2148 self.pg2.generate_remote_hosts(NUM_PEERS)
2149 self.pg2.configure_ipv4_neighbors()
2150 self.pg1.generate_remote_hosts(NUM_PEERS)
2151 self.pg1.configure_ipv4_neighbors()
2157 for i in range(NUM_PEERS):
2162 self.pg1.remote_hosts[i].ip4,
2164 ["10.0.%d.4/32" % i],
2172 [VppRoutePath(self.pg1.remote_hosts[i].ip4, wg0.sw_if_index)],
2180 self.pg2.remote_hosts[i].ip4,
2182 ["10.100.%d.4/32" % i],
2190 [VppRoutePath(self.pg2.remote_hosts[i].ip4, wg1.sw_if_index)],
2194 self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_PEERS * 2)
2196 # Want events from the first perr of wg0
2197 # and from all wg1 peers
2198 peers_0[0].want_events()
2201 for i in range(NUM_PEERS):
2202 # send a valid handsake init for which we expect a response
2203 p = peers_0[i].mk_handshake(self.pg1)
2204 rx = self.send_and_expect(self.pg1, [p], self.pg1)
2205 peers_0[i].consume_response(rx[0])
2207 peers_0[0].wait_event(ESTABLISHED_FLAG)
2209 p = peers_1[i].mk_handshake(self.pg2)
2210 rx = self.send_and_expect(self.pg2, [p], self.pg2)
2211 peers_1[i].consume_response(rx[0])
2213 wg1.wait_events(ESTABLISHED_FLAG, [peers_1[0].index, peers_1[1].index])
2217 r.remove_vpp_config()
2219 r.remove_vpp_config()
2222 for i in range(NUM_PEERS):
2223 self.assertTrue(peers_0[i].query_vpp_config())
2224 peers_0[i].remove_vpp_config()
2226 peers_0[i].wait_event(0)
2227 peers_0[i].wait_event(DEAD_FLAG)
2229 self.assertTrue(p.query_vpp_config())
2230 p.remove_vpp_config()
2232 p.wait_event(DEAD_FLAG)
2234 wg0.remove_vpp_config()
2235 wg1.remove_vpp_config()
2238 @tag_fixme_ubuntu2204
2240 class WireguardHandoffTests(TestWg):
2241 """Wireguard Tests in multi worker setup"""
2243 vpp_worker_count = 2
2245 def test_wg_peer_init(self):
2251 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2256 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.2.0/24", "10.11.3.0/24"]
2258 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2261 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
2264 # send a valid handsake init for which we expect a response
2265 p = peer_1.mk_handshake(self.pg1)
2267 rx = self.send_and_expect(self.pg1, [p], self.pg1)
2269 peer_1.consume_response(rx[0])
2271 # send a data packet from the peer through the tunnel
2272 # this completes the handshake and pins the peer to worker 0
2274 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
2275 / UDP(sport=222, dport=223)
2278 d = peer_1.encrypt_transport(p)
2279 p = peer_1.mk_tunnel_header(self.pg1) / (
2280 Wireguard(message_type=4, reserved_zero=0)
2281 / WireguardTransport(
2282 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
2285 rxs = self.send_and_expect(self.pg1, [p], self.pg0, worker=0)
2288 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
2289 self.assertEqual(rx[IP].ttl, 19)
2291 # send a packets that are routed into the tunnel
2292 # and pins the peer tp worker 1
2294 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2295 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
2296 / UDP(sport=555, dport=556)
2299 rxs = self.send_and_expect(self.pg0, pe * 255, self.pg1, worker=1)
2300 peer_1.validate_encapped(rxs, pe)
2302 # send packets into the tunnel, from the other worker
2305 peer_1.mk_tunnel_header(self.pg1)
2306 / Wireguard(message_type=4, reserved_zero=0)
2307 / WireguardTransport(
2308 receiver_index=peer_1.sender,
2310 encrypted_encapsulated_packet=peer_1.encrypt_transport(
2312 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
2313 / UDP(sport=222, dport=223)
2319 for ii in range(255)
2322 rxs = self.send_and_expect(self.pg1, p, self.pg0, worker=1)
2325 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
2326 self.assertEqual(rx[IP].ttl, 19)
2328 # send a packets that are routed into the tunnel
2330 rxs = self.send_and_expect(self.pg0, pe * 255, self.pg1, worker=0)
2332 peer_1.validate_encapped(rxs, pe)
2334 r1.remove_vpp_config()
2335 peer_1.remove_vpp_config()
2336 wg0.remove_vpp_config()
2338 @unittest.skip("test disabled")
2339 def test_wg_multi_interface(self):
2340 """Multi-tunnel on the same port"""
2343 class TestWgFIB(VppTestCase):
2344 """Wireguard FIB Test Case"""
2347 def setUpClass(cls):
2348 super(TestWgFIB, cls).setUpClass()
2351 def tearDownClass(cls):
2352 super(TestWgFIB, cls).tearDownClass()
2355 super(TestWgFIB, self).setUp()
2357 self.create_pg_interfaces(range(2))
2359 for i in self.pg_interfaces:
2364 for i in self.pg_interfaces:
2367 super(TestWgFIB, self).tearDown()
2369 def test_wg_fib_tracking(self):
2373 # create wg interface
2374 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2378 self.pg_enable_capture(self.pg_interfaces)
2383 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
2385 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2387 # create a route to rewrite traffic into the wg interface
2389 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
2392 # resolve ARP and expect the adjacency to update
2393 self.pg1.resolve_arp()
2395 # wait for the peer to send a handshake initiation
2396 rxs = self.pg1.get_capture(2, timeout=6)
2398 # prepare and send a handshake response
2399 # expect a keepalive message
2400 resp = peer_1.consume_init(rxs[1], self.pg1)
2401 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
2403 # verify the keepalive message
2404 b = peer_1.decrypt_transport(rxs[0])
2405 self.assertEqual(0, len(b))
2407 # prepare and send a packet that will be rewritten into the wg interface
2408 # expect a data packet sent to the new endpoint
2410 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2411 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
2412 / UDP(sport=555, dport=556)
2415 rxs = self.send_and_expect(self.pg0, [p], self.pg1)
2417 # verify the data packet
2418 peer_1.validate_encapped(rxs, p)
2421 r1.remove_vpp_config()
2422 peer_1.remove_vpp_config()
2423 wg0.remove_vpp_config()