8 from hashlib import blake2s
9 from scapy.packet import Packet
10 from scapy.packet import Raw
11 from scapy.layers.l2 import Ether, ARP
12 from scapy.layers.inet import IP, UDP
13 from scapy.layers.inet6 import IPv6
14 from scapy.contrib.wireguard import (
21 from cryptography.hazmat.primitives.asymmetric.x25519 import (
25 from cryptography.hazmat.primitives.serialization import (
31 from cryptography.hazmat.primitives.hashes import BLAKE2s, Hash
32 from cryptography.hazmat.primitives.hmac import HMAC
33 from cryptography.hazmat.backends import default_backend
34 from noise.connection import NoiseConnection, Keypair
36 from Crypto.Cipher import ChaCha20_Poly1305
37 from Crypto.Random import get_random_bytes
39 from vpp_ipip_tun_interface import VppIpIpTunInterface
40 from vpp_interface import VppInterface
41 from vpp_pg_interface import is_ipv6_misc
42 from vpp_ip_route import VppIpRoute, VppRoutePath
43 from vpp_object import VppObject
44 from vpp_papi import VppEnum
45 from framework import is_distro_ubuntu2204, is_distro_debian11, tag_fixme_vpp_debug
46 from framework import VppTestCase
47 from re import compile
50 """ TestWg is a subclass of VPPTestCase classes.
57 def private_key_bytes(k):
58 return k.private_bytes(Encoding.Raw, PrivateFormat.Raw, NoEncryption())
61 def public_key_bytes(k):
62 return k.public_bytes(Encoding.Raw, PublicFormat.Raw)
65 def get_field_bytes(pkt, name):
66 fld, val = pkt.getfield_and_val(name)
67 return fld.i2m(pkt, val)
70 class VppWgInterface(VppInterface):
72 VPP WireGuard interface
75 def __init__(self, test, src, port):
76 super(VppWgInterface, self).__init__(test)
80 self.private_key = X25519PrivateKey.generate()
81 self.public_key = self.private_key.public_key()
83 # cookie related params
84 self.cookie_key = blake2s(b"cookie--" + self.public_key_bytes()).digest()
86 def public_key_bytes(self):
87 return public_key_bytes(self.public_key)
89 def private_key_bytes(self):
90 return private_key_bytes(self.private_key)
92 def add_vpp_config(self):
93 r = self.test.vapi.wireguard_interface_create(
95 "user_instance": 0xFFFFFFFF,
98 "private_key": private_key_bytes(self.private_key),
99 "generate_key": False,
102 self.set_sw_if_index(r.sw_if_index)
103 self.test.registry.register(self, self.test.logger)
106 def remove_vpp_config(self):
107 self.test.vapi.wireguard_interface_delete(sw_if_index=self._sw_if_index)
109 def query_vpp_config(self):
110 ts = self.test.vapi.wireguard_interface_dump(sw_if_index=0xFFFFFFFF)
113 t.interface.sw_if_index == self._sw_if_index
114 and str(t.interface.src_ip) == self.src
115 and t.interface.port == self.port
116 and t.interface.private_key == private_key_bytes(self.private_key)
121 def want_events(self, peer_index=0xFFFFFFFF):
122 self.test.vapi.want_wireguard_peer_events(
125 sw_if_index=self._sw_if_index,
126 peer_index=peer_index,
129 def wait_events(self, expect, peers, timeout=5):
130 for i in range(len(peers)):
131 rv = self.test.vapi.wait_for_event(timeout, "wireguard_peer_event")
132 self.test.assertEqual(rv.peer_index, peers[i])
133 self.test.assertEqual(rv.flags, expect)
136 return self.object_id()
139 return "wireguard-%d" % self._sw_if_index
142 NOISE_HANDSHAKE_NAME = b"Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s"
143 NOISE_IDENTIFIER_NAME = b"WireGuard v1 zx2c4 Jason@zx2c4.com"
145 HANDSHAKE_COUNTING_INTERVAL = 0.5
146 UNDER_LOAD_INTERVAL = 1.0
147 HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD = 40
148 HANDSHAKE_NUM_BEFORE_RATELIMITING = 5
151 class VppWgPeer(VppObject):
152 def __init__(self, test, itf, endpoint, port, allowed_ips, persistent_keepalive=15):
155 self.endpoint = endpoint
157 self.allowed_ips = allowed_ips
158 self.persistent_keepalive = persistent_keepalive
160 # remote peer's public
161 self.private_key = X25519PrivateKey.generate()
162 self.public_key = self.private_key.public_key()
164 # cookie related params
165 self.cookie_key = blake2s(b"cookie--" + self.public_key_bytes()).digest()
166 self.last_sent_cookie = None
167 self.last_mac1 = None
168 self.last_received_cookie = None
170 self.noise = NoiseConnection.from_name(NOISE_HANDSHAKE_NAME)
172 def change_endpoint(self, endpoint, port):
173 self.endpoint = endpoint
176 def add_vpp_config(self):
177 rv = self._test.vapi.wireguard_peer_add(
179 "public_key": self.public_key_bytes(),
181 "endpoint": self.endpoint,
182 "n_allowed_ips": len(self.allowed_ips),
183 "allowed_ips": self.allowed_ips,
184 "sw_if_index": self.itf.sw_if_index,
185 "persistent_keepalive": self.persistent_keepalive,
188 self.index = rv.peer_index
189 self.receiver_index = self.index + 1
190 self._test.registry.register(self, self._test.logger)
193 def remove_vpp_config(self):
194 self._test.vapi.wireguard_peer_remove(peer_index=self.index)
197 return "wireguard-peer-%s" % self.index
199 def public_key_bytes(self):
200 return public_key_bytes(self.public_key)
202 def query_vpp_config(self):
203 peers = self._test.vapi.wireguard_peers_dump()
206 # "::" endpoint will be returned as "0.0.0.0" in peer's details
207 endpoint = "0.0.0.0" if self.endpoint == "::" else self.endpoint
209 p.peer.public_key == self.public_key_bytes()
210 and p.peer.port == self.port
211 and str(p.peer.endpoint) == endpoint
212 and p.peer.sw_if_index == self.itf.sw_if_index
213 and len(self.allowed_ips) == p.peer.n_allowed_ips
215 self.allowed_ips.sort()
216 p.peer.allowed_ips.sort()
218 for (a1, a2) in zip(self.allowed_ips, p.peer.allowed_ips):
219 if str(a1) != str(a2):
224 def mk_tunnel_header(self, tx_itf, is_ip6=False):
227 Ether(dst=tx_itf.local_mac, src=tx_itf.remote_mac)
228 / IP(src=self.endpoint, dst=self.itf.src)
229 / UDP(sport=self.port, dport=self.itf.port)
233 Ether(dst=tx_itf.local_mac, src=tx_itf.remote_mac)
234 / IPv6(src=self.endpoint, dst=self.itf.src)
235 / UDP(sport=self.port, dport=self.itf.port)
238 def noise_reset(self):
239 self.noise = NoiseConnection.from_name(NOISE_HANDSHAKE_NAME)
241 def noise_init(self, public_key=None):
242 self.noise.set_prologue(NOISE_IDENTIFIER_NAME)
243 self.noise.set_psks(psk=bytes(bytearray(32)))
246 public_key = self.itf.public_key
249 self.noise.set_keypair_from_private_bytes(
250 Keypair.STATIC, private_key_bytes(self.private_key)
253 self.noise.set_keypair_from_public_bytes(
254 Keypair.REMOTE_STATIC, public_key_bytes(public_key)
257 self.noise.start_handshake()
259 def mk_cookie(self, p, tx_itf, is_resp=False, is_ip6=False):
260 self.verify_header(p, is_ip6)
262 wg_pkt = Wireguard(p[Raw])
265 self._test.assertEqual(wg_pkt[Wireguard].message_type, 2)
266 self._test.assertEqual(wg_pkt[Wireguard].reserved_zero, 0)
267 self._test.assertEqual(wg_pkt[WireguardResponse].mac2, bytes([0] * 16))
269 self._test.assertEqual(wg_pkt[Wireguard].message_type, 1)
270 self._test.assertEqual(wg_pkt[Wireguard].reserved_zero, 0)
271 self._test.assertEqual(wg_pkt[WireguardInitiation].mac2, bytes([0] * 16))
273 # collect info from wg packet (initiation or response)
274 src = get_field_bytes(p[IPv6 if is_ip6 else IP], "src")
275 sport = p[UDP].sport.to_bytes(2, byteorder="big")
277 mac1 = wg_pkt[WireguardResponse].mac1
278 sender_index = wg_pkt[WireguardResponse].sender_index
280 mac1 = wg_pkt[WireguardInitiation].mac1
281 sender_index = wg_pkt[WireguardInitiation].sender_index
284 cookie_reply = Wireguard() / WireguardCookieReply()
285 cookie_reply[Wireguard].message_type = 3
286 cookie_reply[Wireguard].reserved_zero = 0
287 cookie_reply[WireguardCookieReply].receiver_index = sender_index
288 nonce = get_random_bytes(24)
289 cookie_reply[WireguardCookieReply].nonce = nonce
291 # generate cookie data
292 changing_secret = get_random_bytes(32)
293 self.last_sent_cookie = blake2s(
294 src + sport, digest_size=16, key=changing_secret
297 # encrypt cookie data
298 cipher = ChaCha20_Poly1305.new(key=self.cookie_key, nonce=nonce)
300 ciphertext, tag = cipher.encrypt_and_digest(self.last_sent_cookie)
301 cookie_reply[WireguardCookieReply].encrypted_cookie = ciphertext + tag
303 # prepare cookie reply to be sent
304 cookie_reply = self.mk_tunnel_header(tx_itf, is_ip6) / cookie_reply
308 def consume_cookie(self, p, is_ip6=False):
309 self.verify_header(p, is_ip6)
311 cookie_reply = Wireguard(p[Raw])
313 self._test.assertEqual(cookie_reply[Wireguard].message_type, 3)
314 self._test.assertEqual(cookie_reply[Wireguard].reserved_zero, 0)
315 self._test.assertEqual(
316 cookie_reply[WireguardCookieReply].receiver_index, self.receiver_index
319 # collect info from cookie reply
320 nonce = cookie_reply[WireguardCookieReply].nonce
321 encrypted_cookie = cookie_reply[WireguardCookieReply].encrypted_cookie
322 ciphertext, tag = encrypted_cookie[:16], encrypted_cookie[16:]
324 # decrypt cookie data
325 cipher = ChaCha20_Poly1305.new(key=self.itf.cookie_key, nonce=nonce)
326 cipher.update(self.last_mac1)
327 self.last_received_cookie = cipher.decrypt_and_verify(ciphertext, tag)
329 def mk_handshake(self, tx_itf, is_ip6=False, public_key=None):
330 self.noise.set_as_initiator()
331 self.noise_init(public_key)
333 p = Wireguard() / WireguardInitiation()
335 p[Wireguard].message_type = 1
336 p[Wireguard].reserved_zero = 0
337 p[WireguardInitiation].sender_index = self.receiver_index
339 # some random data for the message
340 # lifted from the noise protocol's wireguard example
341 now = datetime.datetime.now()
344 4611686018427387914 + int(now.timestamp()),
345 int(now.microsecond * 1e3),
347 b = self.noise.write_message(payload=tai)
349 # load noise into init message
350 p[WireguardInitiation].unencrypted_ephemeral = b[0:32]
351 p[WireguardInitiation].encrypted_static = b[32:80]
352 p[WireguardInitiation].encrypted_timestamp = b[80:108]
354 # generate the mac1 hash
355 mac_key = blake2s(b"mac1----" + self.itf.public_key_bytes()).digest()
356 mac1 = blake2s(bytes(p)[0:116], digest_size=16, key=mac_key).digest()
357 p[WireguardInitiation].mac1 = mac1
358 self.last_mac1 = mac1
360 # generate the mac2 hash
361 if self.last_received_cookie:
363 bytes(p)[0:132], digest_size=16, key=self.last_received_cookie
365 p[WireguardInitiation].mac2 = mac2
366 self.last_received_cookie = None
368 p[WireguardInitiation].mac2 = bytearray(16)
370 p = self.mk_tunnel_header(tx_itf, is_ip6) / p
374 def verify_header(self, p, is_ip6=False):
376 self._test.assertEqual(p[IP].src, self.itf.src)
377 self._test.assertEqual(p[IP].dst, self.endpoint)
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 def is_handshake_init(p):
499 wg_p = Wireguard(p[Raw])
501 return wg_p[Wireguard].message_type == 1
504 class TestWg(VppTestCase):
505 """Wireguard Test Case"""
507 error_str = compile(r"Error")
509 wg4_output_node_name = "/err/wg4-output-tun/"
510 wg4_input_node_name = "/err/wg4-input/"
511 wg6_output_node_name = "/err/wg6-output-tun/"
512 wg6_input_node_name = "/err/wg6-input/"
513 kp4_error = wg4_output_node_name + "Keypair error"
514 mac4_error = wg4_input_node_name + "Invalid MAC handshake"
515 peer4_in_err = wg4_input_node_name + "Peer error"
516 peer4_out_err = wg4_output_node_name + "Peer error"
517 kp6_error = wg6_output_node_name + "Keypair error"
518 mac6_error = wg6_input_node_name + "Invalid MAC handshake"
519 peer6_in_err = wg6_input_node_name + "Peer error"
520 peer6_out_err = wg6_output_node_name + "Peer error"
521 cookie_dec4_err = wg4_input_node_name + "Failed during Cookie decryption"
522 cookie_dec6_err = wg6_input_node_name + "Failed during Cookie decryption"
523 ratelimited4_err = wg4_input_node_name + "Handshake ratelimited"
524 ratelimited6_err = wg6_input_node_name + "Handshake ratelimited"
528 super(TestWg, cls).setUpClass()
529 if (is_distro_ubuntu2204 == True or is_distro_debian11 == True) and not hasattr(
534 cls.create_pg_interfaces(range(3))
535 for i in cls.pg_interfaces:
543 super(TestWg, cls).tearDownClass()
547 def tearDownClass(cls):
548 super(TestWg, cls).tearDownClass()
551 super(VppTestCase, self).setUp()
552 self.base_kp4_err = self.statistics.get_err_counter(self.kp4_error)
553 self.base_mac4_err = self.statistics.get_err_counter(self.mac4_error)
554 self.base_peer4_in_err = self.statistics.get_err_counter(self.peer4_in_err)
555 self.base_peer4_out_err = self.statistics.get_err_counter(self.peer4_out_err)
556 self.base_kp6_err = self.statistics.get_err_counter(self.kp6_error)
557 self.base_mac6_err = self.statistics.get_err_counter(self.mac6_error)
558 self.base_peer6_in_err = self.statistics.get_err_counter(self.peer6_in_err)
559 self.base_peer6_out_err = self.statistics.get_err_counter(self.peer6_out_err)
560 self.base_cookie_dec4_err = self.statistics.get_err_counter(
563 self.base_cookie_dec6_err = self.statistics.get_err_counter(
566 self.base_ratelimited4_err = self.statistics.get_err_counter(
567 self.ratelimited4_err
569 self.base_ratelimited6_err = self.statistics.get_err_counter(
570 self.ratelimited6_err
573 def send_and_assert_no_replies_ignoring_init(
574 self, intf, pkts, remark="", timeout=None
576 self.pg_send(intf, pkts)
578 def _filter_out_fn(p):
579 return is_ipv6_misc(p) or is_handshake_init(p)
584 for i in self.pg_interfaces:
585 i.assert_nothing_captured(
586 timeout=timeout, remark=remark, filter_out_fn=_filter_out_fn
592 def test_wg_interface(self):
593 """Simple interface creation"""
597 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
599 self.logger.info(self.vapi.cli("sh int"))
602 wg0.remove_vpp_config()
604 def test_handshake_hash(self):
605 """test hashing an init message"""
606 # a init packet generated by linux given the key below
629 b = bytearray.fromhex(h)
632 pubb = base64.b64decode("aRuHFTTxICIQNefp05oKWlJv3zgKxb8+WW7JJMh0jyM=")
633 pub = X25519PublicKey.from_public_bytes(pubb)
635 self.assertEqual(pubb, public_key_bytes(pub))
637 # strip the macs and build a new packet
639 mac_key = blake2s(b"mac1----" + public_key_bytes(pub)).digest()
640 init += blake2s(init, digest_size=16, key=mac_key).digest()
643 act = Wireguard(init)
645 self.assertEqual(tgt, act)
647 def _test_wg_send_cookie_tmpl(self, is_resp, is_ip6):
650 # create wg interface
652 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
656 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
660 self.pg_enable_capture(self.pg_interfaces)
666 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
670 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
672 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
675 # prepare and send a handshake initiation
676 # expect the peer to send a handshake response
677 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
678 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
680 # wait for the peer to send a handshake initiation
681 rxs = self.pg1.get_capture(1, timeout=2)
683 # prepare and send a wrong cookie reply
684 # expect no replies and the cookie error incremented
685 cookie = peer_1.mk_cookie(rxs[0], self.pg1, is_resp=is_resp, is_ip6=is_ip6)
686 cookie.nonce = b"1234567890"
687 self.send_and_assert_no_replies(self.pg1, [cookie], timeout=0.1)
690 self.base_cookie_dec6_err + 1,
691 self.statistics.get_err_counter(self.cookie_dec6_err),
695 self.base_cookie_dec4_err + 1,
696 self.statistics.get_err_counter(self.cookie_dec4_err),
699 # prepare and send a correct cookie reply
700 cookie = peer_1.mk_cookie(rxs[0], self.pg1, is_resp=is_resp, is_ip6=is_ip6)
701 self.pg_send(self.pg1, [cookie])
703 # wait for the peer to send a handshake initiation with mac2 set
704 rxs = self.pg1.get_capture(1, timeout=6)
706 # verify the initiation and its mac2
707 peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6, is_mac2=True)
710 peer_1.remove_vpp_config()
711 wg0.remove_vpp_config()
713 def test_wg_send_cookie_on_init_v4(self):
714 """Send cookie on handshake initiation (v4)"""
715 self._test_wg_send_cookie_tmpl(is_resp=False, is_ip6=False)
717 def test_wg_send_cookie_on_init_v6(self):
718 """Send cookie on handshake initiation (v6)"""
719 self._test_wg_send_cookie_tmpl(is_resp=False, is_ip6=True)
721 def test_wg_send_cookie_on_resp_v4(self):
722 """Send cookie on handshake response (v4)"""
723 self._test_wg_send_cookie_tmpl(is_resp=True, is_ip6=False)
725 def test_wg_send_cookie_on_resp_v6(self):
726 """Send cookie on handshake response (v6)"""
727 self._test_wg_send_cookie_tmpl(is_resp=True, is_ip6=True)
729 def _test_wg_receive_cookie_tmpl(self, is_resp, is_ip6):
732 # create wg interface
734 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
738 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
742 self.pg_enable_capture(self.pg_interfaces)
748 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
752 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
754 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
757 # wait for the peer to send a handshake initiation
758 rxs = self.pg1.get_capture(1, timeout=2)
759 # prepare and send a bunch of handshake responses
760 # expect to switch to under load state
761 resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6)
762 txs = [resp] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
763 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
764 # reset noise to be able to turn into initiator later
767 # prepare and send a bunch of handshake initiations
768 # expect to switch to under load state
769 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
770 txs = [init] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
771 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
773 # expect the peer to send a cookie reply
774 peer_1.consume_cookie(rxs[-1], is_ip6=is_ip6)
776 # prepare and send a handshake initiation with wrong mac2
777 # expect a cookie reply
778 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
779 init.mac2 = b"1234567890"
780 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
781 peer_1.consume_cookie(rxs[0], is_ip6=is_ip6)
783 # prepare and send a handshake initiation with correct mac2
784 # expect a handshake response
785 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
786 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
788 # verify the response
789 peer_1.consume_response(rxs[0], is_ip6=is_ip6)
791 # clear up under load state
792 self.sleep(UNDER_LOAD_INTERVAL)
795 peer_1.remove_vpp_config()
796 wg0.remove_vpp_config()
798 def test_wg_receive_cookie_on_init_v4(self):
799 """Receive cookie on handshake initiation (v4)"""
800 self._test_wg_receive_cookie_tmpl(is_resp=False, is_ip6=False)
802 def test_wg_receive_cookie_on_init_v6(self):
803 """Receive cookie on handshake initiation (v6)"""
804 self._test_wg_receive_cookie_tmpl(is_resp=False, is_ip6=True)
806 def test_wg_receive_cookie_on_resp_v4(self):
807 """Receive cookie on handshake response (v4)"""
808 self._test_wg_receive_cookie_tmpl(is_resp=True, is_ip6=False)
810 def test_wg_receive_cookie_on_resp_v6(self):
811 """Receive cookie on handshake response (v6)"""
812 self._test_wg_receive_cookie_tmpl(is_resp=True, is_ip6=True)
814 def test_wg_under_load_interval(self):
815 """Under load interval"""
818 # create wg interface
819 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
823 self.pg_enable_capture(self.pg_interfaces)
828 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
830 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
832 # prepare and send a bunch of handshake initiations
833 # expect to switch to under load state
834 init = peer_1.mk_handshake(self.pg1)
835 txs = [init] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
836 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
838 # expect the peer to send a cookie reply
839 peer_1.consume_cookie(rxs[-1])
841 # sleep till the next counting interval
842 # expect under load state is still active
843 self.sleep(HANDSHAKE_COUNTING_INTERVAL)
845 # prepare and send a handshake initiation with wrong mac2
846 # expect a cookie reply
847 init = peer_1.mk_handshake(self.pg1)
848 init.mac2 = b"1234567890"
849 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
850 peer_1.consume_cookie(rxs[0])
852 # sleep till the end of being under load
853 # expect under load state is over
854 self.sleep(UNDER_LOAD_INTERVAL - HANDSHAKE_COUNTING_INTERVAL)
856 # prepare and send a handshake initiation with wrong mac2
857 # expect a handshake response
858 init = peer_1.mk_handshake(self.pg1)
859 init.mac2 = b"1234567890"
860 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
862 # verify the response
863 peer_1.consume_response(rxs[0])
866 peer_1.remove_vpp_config()
867 wg0.remove_vpp_config()
869 def _test_wg_handshake_ratelimiting_tmpl(self, is_ip6):
872 # create wg interface
874 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
878 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
882 self.pg_enable_capture(self.pg_interfaces)
888 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
892 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
894 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
896 # prepare and send a bunch of handshake initiations
897 # expect to switch to under load state
898 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
899 txs = [init] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
900 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
902 # expect the peer to send a cookie reply
903 peer_1.consume_cookie(rxs[-1], is_ip6=is_ip6)
905 # prepare and send a bunch of handshake initiations with correct mac2
906 # expect a handshake response and then ratelimiting
908 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
909 txs = [init] * (HANDSHAKE_NUM_BEFORE_RATELIMITING + NUM_TO_REJECT)
910 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
914 self.base_ratelimited6_err + NUM_TO_REJECT,
915 self.statistics.get_err_counter(self.ratelimited6_err),
919 self.base_ratelimited4_err + NUM_TO_REJECT,
920 self.statistics.get_err_counter(self.ratelimited4_err),
923 # verify the response
924 peer_1.consume_response(rxs[0], is_ip6=is_ip6)
926 # clear up under load state
927 self.sleep(UNDER_LOAD_INTERVAL)
930 peer_1.remove_vpp_config()
931 wg0.remove_vpp_config()
933 def test_wg_handshake_ratelimiting_v4(self):
934 """Handshake ratelimiting (v4)"""
935 self._test_wg_handshake_ratelimiting_tmpl(is_ip6=False)
937 def test_wg_handshake_ratelimiting_v6(self):
938 """Handshake ratelimiting (v6)"""
939 self._test_wg_handshake_ratelimiting_tmpl(is_ip6=True)
941 def test_wg_handshake_ratelimiting_multi_peer(self):
942 """Handshake ratelimiting (multiple peer)"""
945 # create wg interface
946 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
950 self.pg_enable_capture(self.pg_interfaces)
955 self.pg1.generate_remote_hosts(NUM_PEERS)
956 self.pg1.configure_ipv4_neighbors()
959 self, wg0, self.pg1.remote_hosts[0].ip4, port + 1, ["10.11.3.0/24"]
962 self, wg0, self.pg1.remote_hosts[1].ip4, port + 1, ["10.11.4.0/24"]
964 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 2)
966 # (peer_1) prepare and send a bunch of handshake initiations
967 # expect not to switch to under load state
968 init_1 = peer_1.mk_handshake(self.pg1)
969 txs = [init_1] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
970 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
972 # (peer_1) expect the peer to send a handshake response
973 peer_1.consume_response(rxs[0])
976 # (peer_1) send another bunch of handshake initiations
977 # expect to switch to under load state
978 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
980 # (peer_1) expect the peer to send a cookie reply
981 peer_1.consume_cookie(rxs[-1])
983 # (peer_2) prepare and send a handshake initiation
984 # expect a cookie reply
985 init_2 = peer_2.mk_handshake(self.pg1)
986 rxs = self.send_and_expect(self.pg1, [init_2], self.pg1)
987 peer_2.consume_cookie(rxs[0])
989 # (peer_1) (peer_2) prepare and send a bunch of handshake initiations with correct mac2
990 # expect a handshake response and then ratelimiting
991 PEER_1_NUM_TO_REJECT = 2
992 PEER_2_NUM_TO_REJECT = 5
993 init_1 = peer_1.mk_handshake(self.pg1)
994 txs = [init_1] * (HANDSHAKE_NUM_BEFORE_RATELIMITING + PEER_1_NUM_TO_REJECT)
995 init_2 = peer_2.mk_handshake(self.pg1)
996 txs += [init_2] * (HANDSHAKE_NUM_BEFORE_RATELIMITING + PEER_2_NUM_TO_REJECT)
997 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
1000 self.base_ratelimited4_err + PEER_1_NUM_TO_REJECT
1001 < self.statistics.get_err_counter(self.ratelimited4_err)
1002 <= self.base_ratelimited4_err + PEER_1_NUM_TO_REJECT + PEER_2_NUM_TO_REJECT
1005 # (peer_1) (peer_2) verify the response
1006 peer_1.consume_response(rxs[0])
1007 peer_2.consume_response(rxs[1])
1009 # clear up under load state
1010 self.sleep(UNDER_LOAD_INTERVAL)
1013 peer_1.remove_vpp_config()
1014 peer_2.remove_vpp_config()
1015 wg0.remove_vpp_config()
1017 def _test_wg_peer_roaming_on_handshake_tmpl(self, is_endpoint_set, is_resp, is_ip6):
1020 # create wg interface
1022 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1026 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1030 self.pg_enable_capture(self.pg_interfaces)
1033 # create more remote hosts
1034 NUM_REMOTE_HOSTS = 2
1035 self.pg1.generate_remote_hosts(NUM_REMOTE_HOSTS)
1037 self.pg1.configure_ipv6_neighbors()
1039 self.pg1.configure_ipv4_neighbors()
1046 endpoint=self.pg1.remote_hosts[0].ip6 if is_endpoint_set else "::",
1047 port=port + 1 if is_endpoint_set else 0,
1048 allowed_ips=["1::3:0/112"],
1054 endpoint=self.pg1.remote_hosts[0].ip4 if is_endpoint_set else "0.0.0.0",
1055 port=port + 1 if is_endpoint_set else 0,
1056 allowed_ips=["10.11.3.0/24"],
1058 self.assertTrue(peer_1.query_vpp_config())
1061 # wait for the peer to send a handshake initiation
1062 rxs = self.pg1.get_capture(1, timeout=2)
1063 # prepare a handshake response
1064 resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6)
1067 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip6, port + 100)
1068 resp[IPv6].src, resp[UDP].sport = peer_1.endpoint, peer_1.port
1070 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip4, port + 100)
1071 resp[IP].src, resp[UDP].sport = peer_1.endpoint, peer_1.port
1072 # send the handshake response
1073 # expect a keepalive message sent to the new endpoint
1074 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1075 # verify the keepalive message
1076 b = peer_1.decrypt_transport(rxs[0], is_ip6=is_ip6)
1077 self.assertEqual(0, len(b))
1081 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip6, port + 100)
1083 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip4, port + 100)
1084 # prepare and send a handshake initiation
1085 # expect a handshake response sent to the new endpoint
1086 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
1087 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
1088 # verify the response
1089 peer_1.consume_response(rxs[0], is_ip6=is_ip6)
1090 self.assertTrue(peer_1.query_vpp_config())
1093 peer_1.remove_vpp_config()
1094 wg0.remove_vpp_config()
1096 def test_wg_peer_roaming_on_init_v4(self):
1097 """Peer roaming on handshake initiation (v4)"""
1098 self._test_wg_peer_roaming_on_handshake_tmpl(
1099 is_endpoint_set=False, is_resp=False, is_ip6=False
1102 def test_wg_peer_roaming_on_init_v6(self):
1103 """Peer roaming on handshake initiation (v6)"""
1104 self._test_wg_peer_roaming_on_handshake_tmpl(
1105 is_endpoint_set=False, is_resp=False, is_ip6=True
1108 def test_wg_peer_roaming_on_resp_v4(self):
1109 """Peer roaming on handshake response (v4)"""
1110 self._test_wg_peer_roaming_on_handshake_tmpl(
1111 is_endpoint_set=True, is_resp=True, is_ip6=False
1114 def test_wg_peer_roaming_on_resp_v6(self):
1115 """Peer roaming on handshake response (v6)"""
1116 self._test_wg_peer_roaming_on_handshake_tmpl(
1117 is_endpoint_set=True, is_resp=True, is_ip6=True
1120 def _test_wg_peer_roaming_on_data_tmpl(self, is_async, is_ip6):
1121 self.vapi.wg_set_async_mode(is_async)
1124 # create wg interface
1126 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1130 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1134 self.pg_enable_capture(self.pg_interfaces)
1137 # create more remote hosts
1138 NUM_REMOTE_HOSTS = 2
1139 self.pg1.generate_remote_hosts(NUM_REMOTE_HOSTS)
1141 self.pg1.configure_ipv6_neighbors()
1143 self.pg1.configure_ipv4_neighbors()
1148 self, wg0, self.pg1.remote_hosts[0].ip6, port + 1, ["1::3:0/112"]
1152 self, wg0, self.pg1.remote_hosts[0].ip4, port + 1, ["10.11.3.0/24"]
1154 self.assertTrue(peer_1.query_vpp_config())
1156 # create a route to rewrite traffic into the wg interface
1159 self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
1163 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1166 # wait for the peer to send a handshake initiation
1167 rxs = self.pg1.get_capture(1, timeout=2)
1169 # prepare and send a handshake response
1170 # expect a keepalive message
1171 resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6)
1172 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1174 # verify the keepalive message
1175 b = peer_1.decrypt_transport(rxs[0], is_ip6=is_ip6)
1176 self.assertEqual(0, len(b))
1180 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip6, port + 100)
1182 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip4, port + 100)
1184 # prepare and send a data packet
1185 # expect endpoint change
1187 ip_header = IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1189 ip_header = IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1191 peer_1.mk_tunnel_header(self.pg1, is_ip6=is_ip6)
1192 / Wireguard(message_type=4, reserved_zero=0)
1193 / WireguardTransport(
1194 receiver_index=peer_1.sender,
1196 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1197 ip_header / UDP(sport=222, dport=223) / Raw()
1201 rxs = self.send_and_expect(self.pg1, [data], self.pg0)
1203 self.assertEqual(rxs[0][IPv6].dst, self.pg0.remote_ip6)
1204 self.assertEqual(rxs[0][IPv6].hlim, 19)
1206 self.assertEqual(rxs[0][IP].dst, self.pg0.remote_ip4)
1207 self.assertEqual(rxs[0][IP].ttl, 19)
1208 self.assertTrue(peer_1.query_vpp_config())
1210 # prepare and send a packet that will be rewritten into the wg interface
1211 # expect a data packet sent to the new endpoint
1213 ip_header = IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1215 ip_header = IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1217 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1219 / UDP(sport=555, dport=556)
1222 rxs = self.send_and_expect(self.pg0, [p], self.pg1)
1224 # verify the data packet
1225 peer_1.validate_encapped(rxs, p, is_ip6=is_ip6)
1228 r1.remove_vpp_config()
1229 peer_1.remove_vpp_config()
1230 wg0.remove_vpp_config()
1232 def test_wg_peer_roaming_on_data_v4_sync(self):
1233 """Peer roaming on data packet (v4, sync)"""
1234 self._test_wg_peer_roaming_on_data_tmpl(is_async=False, is_ip6=False)
1236 def test_wg_peer_roaming_on_data_v6_sync(self):
1237 """Peer roaming on data packet (v6, sync)"""
1238 self._test_wg_peer_roaming_on_data_tmpl(is_async=False, is_ip6=True)
1240 def test_wg_peer_roaming_on_data_v4_async(self):
1241 """Peer roaming on data packet (v4, async)"""
1242 self._test_wg_peer_roaming_on_data_tmpl(is_async=True, is_ip6=False)
1244 def test_wg_peer_roaming_on_data_v6_async(self):
1245 """Peer roaming on data packet (v6, async)"""
1246 self._test_wg_peer_roaming_on_data_tmpl(is_async=True, is_ip6=True)
1248 def test_wg_peer_resp(self):
1249 """Send handshake response"""
1253 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1257 self.pg_enable_capture(self.pg_interfaces)
1261 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
1263 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1266 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1269 # wait for the peer to send a handshake
1270 rx = self.pg1.get_capture(1, timeout=2)
1272 # consume the handshake in the noise protocol and
1273 # generate the response
1274 resp = peer_1.consume_init(rx[0], self.pg1)
1276 # send the response, get keepalive
1277 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1280 b = peer_1.decrypt_transport(rx)
1281 self.assertEqual(0, len(b))
1283 # send a packets that are routed into the tunnel
1285 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1286 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1287 / UDP(sport=555, dport=556)
1291 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1293 peer_1.validate_encapped(rxs, p)
1295 # send packets into the tunnel, expect to receive them on
1299 peer_1.mk_tunnel_header(self.pg1)
1300 / Wireguard(message_type=4, reserved_zero=0)
1301 / WireguardTransport(
1302 receiver_index=peer_1.sender,
1304 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1306 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1307 / UDP(sport=222, dport=223)
1313 for ii in range(255)
1316 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1319 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1320 self.assertEqual(rx[IP].ttl, 19)
1322 r1.remove_vpp_config()
1323 peer_1.remove_vpp_config()
1324 wg0.remove_vpp_config()
1326 def test_wg_peer_v4o4(self):
1332 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1337 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
1339 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1342 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1345 self, "20.22.3.0", 24, [VppRoutePath("20.22.3.1", wg0.sw_if_index)]
1348 # route a packet into the wg interface
1349 # use the allowed-ip prefix
1350 # this is dropped because the peer is not initiated
1352 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1353 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1354 / UDP(sport=555, dport=556)
1357 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1359 self.base_kp4_err + 1, self.statistics.get_err_counter(self.kp4_error)
1362 # route a packet into the wg interface
1363 # use a not allowed-ip prefix
1364 # this is dropped because there is no matching peer
1366 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1367 / IP(src=self.pg0.remote_ip4, dst="20.22.3.2")
1368 / UDP(sport=555, dport=556)
1371 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1373 self.base_peer4_out_err + 1,
1374 self.statistics.get_err_counter(self.peer4_out_err),
1377 # send a handsake from the peer with an invalid MAC
1378 p = peer_1.mk_handshake(self.pg1)
1379 p[WireguardInitiation].mac1 = b"foobar"
1380 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1382 self.base_mac4_err + 1, self.statistics.get_err_counter(self.mac4_error)
1385 # send a handsake from the peer but signed by the wrong key.
1386 p = peer_1.mk_handshake(
1387 self.pg1, False, X25519PrivateKey.generate().public_key()
1389 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1391 self.base_peer4_in_err + 1,
1392 self.statistics.get_err_counter(self.peer4_in_err),
1395 # send a valid handsake init for which we expect a response
1396 p = peer_1.mk_handshake(self.pg1)
1398 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1400 peer_1.consume_response(rx[0])
1402 # route a packet into the wg interface
1403 # this is dropped because the peer is still not initiated
1405 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1406 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1407 / UDP(sport=555, dport=556)
1410 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1412 self.base_kp4_err + 2, self.statistics.get_err_counter(self.kp4_error)
1415 # send a data packet from the peer through the tunnel
1416 # this completes the handshake
1418 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1419 / UDP(sport=222, dport=223)
1422 d = peer_1.encrypt_transport(p)
1423 p = peer_1.mk_tunnel_header(self.pg1) / (
1424 Wireguard(message_type=4, reserved_zero=0)
1425 / WireguardTransport(
1426 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1429 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1432 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1433 self.assertEqual(rx[IP].ttl, 19)
1435 # send a packets that are routed into the tunnel
1437 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1438 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1439 / UDP(sport=555, dport=556)
1443 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1446 rx = IP(peer_1.decrypt_transport(rx))
1448 # check the original packet is present
1449 self.assertEqual(rx[IP].dst, p[IP].dst)
1450 self.assertEqual(rx[IP].ttl, p[IP].ttl - 1)
1452 # send packets into the tunnel, expect to receive them on
1456 peer_1.mk_tunnel_header(self.pg1)
1457 / Wireguard(message_type=4, reserved_zero=0)
1458 / WireguardTransport(
1459 receiver_index=peer_1.sender,
1461 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1463 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1464 / UDP(sport=222, dport=223)
1470 for ii in range(255)
1473 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1476 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1477 self.assertEqual(rx[IP].ttl, 19)
1479 r1.remove_vpp_config()
1480 r2.remove_vpp_config()
1481 peer_1.remove_vpp_config()
1482 wg0.remove_vpp_config()
1484 def test_wg_peer_v6o6(self):
1490 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1495 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
1497 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1500 self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
1503 self, "22::3:0", 112, [VppRoutePath("22::3:1", wg0.sw_if_index)]
1506 # route a packet into the wg interface
1507 # use the allowed-ip prefix
1508 # this is dropped because the peer is not initiated
1511 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1512 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1513 / UDP(sport=555, dport=556)
1516 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1519 self.base_kp6_err + 1, self.statistics.get_err_counter(self.kp6_error)
1522 # route a packet into the wg interface
1523 # use a not allowed-ip prefix
1524 # this is dropped because there is no matching peer
1526 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1527 / IPv6(src=self.pg0.remote_ip6, dst="22::3:2")
1528 / UDP(sport=555, dport=556)
1531 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1533 self.base_peer6_out_err + 1,
1534 self.statistics.get_err_counter(self.peer6_out_err),
1537 # send a handsake from the peer with an invalid MAC
1538 p = peer_1.mk_handshake(self.pg1, True)
1539 p[WireguardInitiation].mac1 = b"foobar"
1540 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1543 self.base_mac6_err + 1, self.statistics.get_err_counter(self.mac6_error)
1546 # send a handsake from the peer but signed by the wrong key.
1547 p = peer_1.mk_handshake(
1548 self.pg1, True, X25519PrivateKey.generate().public_key()
1550 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1552 self.base_peer6_in_err + 1,
1553 self.statistics.get_err_counter(self.peer6_in_err),
1556 # send a valid handsake init for which we expect a response
1557 p = peer_1.mk_handshake(self.pg1, True)
1559 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1561 peer_1.consume_response(rx[0], True)
1563 # route a packet into the wg interface
1564 # this is dropped because the peer is still not initiated
1566 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1567 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1568 / UDP(sport=555, dport=556)
1571 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1573 self.base_kp6_err + 2, self.statistics.get_err_counter(self.kp6_error)
1576 # send a data packet from the peer through the tunnel
1577 # this completes the handshake
1579 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1580 / UDP(sport=222, dport=223)
1583 d = peer_1.encrypt_transport(p)
1584 p = peer_1.mk_tunnel_header(self.pg1, True) / (
1585 Wireguard(message_type=4, reserved_zero=0)
1586 / WireguardTransport(
1587 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1590 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1593 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1594 self.assertEqual(rx[IPv6].hlim, 19)
1596 # send a packets that are routed into the tunnel
1598 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1599 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1600 / UDP(sport=555, dport=556)
1604 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1607 rx = IPv6(peer_1.decrypt_transport(rx, True))
1609 # check the original packet is present
1610 self.assertEqual(rx[IPv6].dst, p[IPv6].dst)
1611 self.assertEqual(rx[IPv6].hlim, p[IPv6].hlim - 1)
1613 # send packets into the tunnel, expect to receive them on
1617 peer_1.mk_tunnel_header(self.pg1, True)
1618 / Wireguard(message_type=4, reserved_zero=0)
1619 / WireguardTransport(
1620 receiver_index=peer_1.sender,
1622 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1624 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1625 / UDP(sport=222, dport=223)
1631 for ii in range(255)
1634 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1637 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1638 self.assertEqual(rx[IPv6].hlim, 19)
1640 r1.remove_vpp_config()
1641 r2.remove_vpp_config()
1642 peer_1.remove_vpp_config()
1643 wg0.remove_vpp_config()
1645 def test_wg_peer_v6o4(self):
1651 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1656 self, wg0, self.pg1.remote_ip4, port + 1, ["1::3:0/112"]
1658 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1661 self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
1664 # route a packet into the wg interface
1665 # use the allowed-ip prefix
1666 # this is dropped because the peer is not initiated
1668 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1669 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1670 / UDP(sport=555, dport=556)
1673 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1675 self.base_kp6_err + 1, self.statistics.get_err_counter(self.kp6_error)
1678 # send a handsake from the peer with an invalid MAC
1679 p = peer_1.mk_handshake(self.pg1)
1680 p[WireguardInitiation].mac1 = b"foobar"
1681 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1684 self.base_mac4_err + 1, self.statistics.get_err_counter(self.mac4_error)
1687 # send a handsake from the peer but signed by the wrong key.
1688 p = peer_1.mk_handshake(
1689 self.pg1, False, X25519PrivateKey.generate().public_key()
1691 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1693 self.base_peer4_in_err + 1,
1694 self.statistics.get_err_counter(self.peer4_in_err),
1697 # send a valid handsake init for which we expect a response
1698 p = peer_1.mk_handshake(self.pg1)
1700 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1702 peer_1.consume_response(rx[0])
1704 # route a packet into the wg interface
1705 # this is dropped because the peer is still not initiated
1707 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1708 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1709 / UDP(sport=555, dport=556)
1712 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1714 self.base_kp6_err + 2, self.statistics.get_err_counter(self.kp6_error)
1717 # send a data packet from the peer through the tunnel
1718 # this completes the handshake
1720 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1721 / UDP(sport=222, dport=223)
1724 d = peer_1.encrypt_transport(p)
1725 p = peer_1.mk_tunnel_header(self.pg1) / (
1726 Wireguard(message_type=4, reserved_zero=0)
1727 / WireguardTransport(
1728 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1731 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1734 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1735 self.assertEqual(rx[IPv6].hlim, 19)
1737 # send a packets that are routed into the tunnel
1739 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1740 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1741 / UDP(sport=555, dport=556)
1745 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1748 rx = IPv6(peer_1.decrypt_transport(rx))
1750 # check the original packet is present
1751 self.assertEqual(rx[IPv6].dst, p[IPv6].dst)
1752 self.assertEqual(rx[IPv6].hlim, p[IPv6].hlim - 1)
1754 # send packets into the tunnel, expect to receive them on
1758 peer_1.mk_tunnel_header(self.pg1)
1759 / Wireguard(message_type=4, reserved_zero=0)
1760 / WireguardTransport(
1761 receiver_index=peer_1.sender,
1763 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1765 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1766 / UDP(sport=222, dport=223)
1772 for ii in range(255)
1775 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1778 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1779 self.assertEqual(rx[IPv6].hlim, 19)
1781 r1.remove_vpp_config()
1782 peer_1.remove_vpp_config()
1783 wg0.remove_vpp_config()
1785 def test_wg_peer_v4o6(self):
1791 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1796 self, wg0, self.pg1.remote_ip6, port + 1, ["10.11.3.0/24"]
1798 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1801 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1804 # route a packet into the wg interface
1805 # use the allowed-ip prefix
1806 # this is dropped because the peer is not initiated
1808 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1809 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1810 / UDP(sport=555, dport=556)
1813 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1815 self.base_kp4_err + 1, self.statistics.get_err_counter(self.kp4_error)
1818 # send a handsake from the peer with an invalid MAC
1819 p = peer_1.mk_handshake(self.pg1, True)
1820 p[WireguardInitiation].mac1 = b"foobar"
1821 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1823 self.base_mac6_err + 1, self.statistics.get_err_counter(self.mac6_error)
1826 # send a handsake from the peer but signed by the wrong key.
1827 p = peer_1.mk_handshake(
1828 self.pg1, True, X25519PrivateKey.generate().public_key()
1830 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1832 self.base_peer6_in_err + 1,
1833 self.statistics.get_err_counter(self.peer6_in_err),
1836 # send a valid handsake init for which we expect a response
1837 p = peer_1.mk_handshake(self.pg1, True)
1839 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1841 peer_1.consume_response(rx[0], True)
1843 # route a packet into the wg interface
1844 # this is dropped because the peer is still not initiated
1846 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1847 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1848 / UDP(sport=555, dport=556)
1851 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1853 self.base_kp4_err + 2, self.statistics.get_err_counter(self.kp4_error)
1856 # send a data packet from the peer through the tunnel
1857 # this completes the handshake
1859 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1860 / UDP(sport=222, dport=223)
1863 d = peer_1.encrypt_transport(p)
1864 p = peer_1.mk_tunnel_header(self.pg1, True) / (
1865 Wireguard(message_type=4, reserved_zero=0)
1866 / WireguardTransport(
1867 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1870 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1873 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1874 self.assertEqual(rx[IP].ttl, 19)
1876 # send a packets that are routed into the tunnel
1878 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1879 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1880 / UDP(sport=555, dport=556)
1884 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1887 rx = IP(peer_1.decrypt_transport(rx, True))
1889 # check the original packet is present
1890 self.assertEqual(rx[IP].dst, p[IP].dst)
1891 self.assertEqual(rx[IP].ttl, p[IP].ttl - 1)
1893 # send packets into the tunnel, expect to receive them on
1897 peer_1.mk_tunnel_header(self.pg1, True)
1898 / Wireguard(message_type=4, reserved_zero=0)
1899 / WireguardTransport(
1900 receiver_index=peer_1.sender,
1902 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1904 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1905 / UDP(sport=222, dport=223)
1911 for ii in range(255)
1914 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1917 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1918 self.assertEqual(rx[IP].ttl, 19)
1920 r1.remove_vpp_config()
1921 peer_1.remove_vpp_config()
1922 wg0.remove_vpp_config()
1924 def test_wg_multi_peer(self):
1925 """multiple peer setup"""
1929 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1930 wg1 = VppWgInterface(self, self.pg2.local_ip4, port + 1).add_vpp_config()
1934 # Check peer counter
1935 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 0)
1937 self.pg_enable_capture(self.pg_interfaces)
1940 # Create many peers on sencond interface
1942 self.pg2.generate_remote_hosts(NUM_PEERS)
1943 self.pg2.configure_ipv4_neighbors()
1944 self.pg1.generate_remote_hosts(NUM_PEERS)
1945 self.pg1.configure_ipv4_neighbors()
1951 for i in range(NUM_PEERS):
1956 self.pg1.remote_hosts[i].ip4,
1958 ["10.0.%d.4/32" % i],
1966 [VppRoutePath(self.pg1.remote_hosts[i].ip4, wg0.sw_if_index)],
1974 self.pg2.remote_hosts[i].ip4,
1976 ["10.100.%d.4/32" % i],
1984 [VppRoutePath(self.pg2.remote_hosts[i].ip4, wg1.sw_if_index)],
1988 self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_PEERS * 2)
1990 self.logger.info(self.vapi.cli("show wireguard peer"))
1991 self.logger.info(self.vapi.cli("show wireguard interface"))
1992 self.logger.info(self.vapi.cli("show adj 37"))
1993 self.logger.info(self.vapi.cli("sh ip fib 172.16.3.17"))
1994 self.logger.info(self.vapi.cli("sh ip fib 10.11.3.0"))
1998 r.remove_vpp_config()
2000 r.remove_vpp_config()
2004 self.assertTrue(p.query_vpp_config())
2005 p.remove_vpp_config()
2007 self.assertTrue(p.query_vpp_config())
2008 p.remove_vpp_config()
2010 wg0.remove_vpp_config()
2011 wg1.remove_vpp_config()
2013 def test_wg_multi_interface(self):
2014 """Multi-tunnel on the same port"""
2017 # Create many wireguard interfaces
2019 self.pg1.generate_remote_hosts(NUM_IFS)
2020 self.pg1.configure_ipv4_neighbors()
2021 self.pg0.generate_remote_hosts(NUM_IFS)
2022 self.pg0.configure_ipv4_neighbors()
2024 # Create interfaces with a peer on each
2028 for i in range(NUM_IFS):
2029 # Use the same port for each interface
2030 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2038 self.pg1.remote_hosts[i].ip4,
2040 ["10.0.%d.0/24" % i],
2049 [VppRoutePath("10.0.%d.4" % i, wg0.sw_if_index)],
2053 self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_IFS)
2055 for i in range(NUM_IFS):
2056 # send a valid handsake init for which we expect a response
2057 p = peers[i].mk_handshake(self.pg1)
2058 rx = self.send_and_expect(self.pg1, [p], self.pg1)
2059 peers[i].consume_response(rx[0])
2061 # send a data packet from the peer through the tunnel
2062 # this completes the handshake
2064 IP(src="10.0.%d.4" % i, dst=self.pg0.remote_hosts[i].ip4, ttl=20)
2065 / UDP(sport=222, dport=223)
2068 d = peers[i].encrypt_transport(p)
2069 p = peers[i].mk_tunnel_header(self.pg1) / (
2070 Wireguard(message_type=4, reserved_zero=0)
2071 / WireguardTransport(
2072 receiver_index=peers[i].sender,
2074 encrypted_encapsulated_packet=d,
2077 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
2079 self.assertEqual(rx[IP].dst, self.pg0.remote_hosts[i].ip4)
2080 self.assertEqual(rx[IP].ttl, 19)
2082 # send a packets that are routed into the tunnel
2083 for i in range(NUM_IFS):
2085 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2086 / IP(src=self.pg0.remote_hosts[i].ip4, dst="10.0.%d.4" % i)
2087 / UDP(sport=555, dport=556)
2091 rxs = self.send_and_expect(self.pg0, p * 64, self.pg1)
2094 rx = IP(peers[i].decrypt_transport(rx))
2096 # check the oringial packet is present
2097 self.assertEqual(rx[IP].dst, p[IP].dst)
2098 self.assertEqual(rx[IP].ttl, p[IP].ttl - 1)
2100 # send packets into the tunnel
2101 for i in range(NUM_IFS):
2104 peers[i].mk_tunnel_header(self.pg1)
2105 / Wireguard(message_type=4, reserved_zero=0)
2106 / WireguardTransport(
2107 receiver_index=peers[i].sender,
2109 encrypted_encapsulated_packet=peers[i].encrypt_transport(
2112 src="10.0.%d.4" % i,
2113 dst=self.pg0.remote_hosts[i].ip4,
2116 / UDP(sport=222, dport=223)
2125 rxs = self.send_and_expect(self.pg1, p, self.pg0)
2128 self.assertEqual(rx[IP].dst, self.pg0.remote_hosts[i].ip4)
2129 self.assertEqual(rx[IP].ttl, 19)
2132 r.remove_vpp_config()
2134 p.remove_vpp_config()
2136 i.remove_vpp_config()
2138 def test_wg_event(self):
2141 ESTABLISHED_FLAG = (
2142 VppEnum.vl_api_wireguard_peer_flags_t.WIREGUARD_PEER_ESTABLISHED
2144 DEAD_FLAG = VppEnum.vl_api_wireguard_peer_flags_t.WIREGUARD_PEER_STATUS_DEAD
2147 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2148 wg1 = VppWgInterface(self, self.pg2.local_ip4, port + 1).add_vpp_config()
2152 # Check peer counter
2153 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 0)
2155 self.pg_enable_capture(self.pg_interfaces)
2160 self.pg2.generate_remote_hosts(NUM_PEERS)
2161 self.pg2.configure_ipv4_neighbors()
2162 self.pg1.generate_remote_hosts(NUM_PEERS)
2163 self.pg1.configure_ipv4_neighbors()
2169 for i in range(NUM_PEERS):
2174 self.pg1.remote_hosts[i].ip4,
2176 ["10.0.%d.4/32" % i],
2184 [VppRoutePath(self.pg1.remote_hosts[i].ip4, wg0.sw_if_index)],
2192 self.pg2.remote_hosts[i].ip4,
2194 ["10.100.%d.4/32" % i],
2202 [VppRoutePath(self.pg2.remote_hosts[i].ip4, wg1.sw_if_index)],
2206 self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_PEERS * 2)
2208 # Want events from the first perr of wg0
2209 # and from all wg1 peers
2210 peers_0[0].want_events()
2213 for i in range(NUM_PEERS):
2214 # send a valid handsake init for which we expect a response
2215 p = peers_0[i].mk_handshake(self.pg1)
2216 rx = self.send_and_expect(self.pg1, [p], self.pg1)
2217 peers_0[i].consume_response(rx[0])
2219 peers_0[0].wait_event(ESTABLISHED_FLAG)
2221 p = peers_1[i].mk_handshake(self.pg2)
2222 rx = self.send_and_expect(self.pg2, [p], self.pg2)
2223 peers_1[i].consume_response(rx[0])
2225 wg1.wait_events(ESTABLISHED_FLAG, [peers_1[0].index, peers_1[1].index])
2229 r.remove_vpp_config()
2231 r.remove_vpp_config()
2234 for i in range(NUM_PEERS):
2235 self.assertTrue(peers_0[i].query_vpp_config())
2236 peers_0[i].remove_vpp_config()
2238 peers_0[i].wait_event(0)
2239 peers_0[i].wait_event(DEAD_FLAG)
2241 self.assertTrue(p.query_vpp_config())
2242 p.remove_vpp_config()
2244 p.wait_event(DEAD_FLAG)
2246 wg0.remove_vpp_config()
2247 wg1.remove_vpp_config()
2249 def test_wg_sending_handshake_when_admin_down(self):
2250 """Sending handshake when admin down"""
2253 # create wg interface
2254 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2259 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
2261 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2263 self.pg_enable_capture(self.pg_interfaces)
2266 # wait for the peer to send a handshake initiation
2267 # expect no handshakes
2269 self.pg1.assert_nothing_captured(remark="handshake packet(s) sent")
2271 self.pg_enable_capture(self.pg_interfaces)
2274 # administratively enable the wg interface
2275 # expect the peer to send a handshake initiation
2277 rxs = self.pg1.get_capture(1, timeout=2)
2278 peer_1.consume_init(rxs[0], self.pg1)
2280 self.pg_enable_capture(self.pg_interfaces)
2283 # administratively disable the wg interface
2284 # expect no handshakes
2287 self.pg1.assert_nothing_captured(remark="handshake packet(s) sent")
2290 peer_1.remove_vpp_config()
2291 wg0.remove_vpp_config()
2293 def test_wg_sending_data_when_admin_down(self):
2294 """Sending data when admin down"""
2297 # create wg interface
2298 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2302 self.pg_enable_capture(self.pg_interfaces)
2307 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
2309 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2311 # create a route to rewrite traffic into the wg interface
2313 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
2316 # wait for the peer to send a handshake initiation
2317 rxs = self.pg1.get_capture(1, timeout=2)
2319 # prepare and send a handshake response
2320 # expect a keepalive message
2321 resp = peer_1.consume_init(rxs[0], self.pg1)
2322 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
2324 # verify the keepalive message
2325 b = peer_1.decrypt_transport(rxs[0])
2326 self.assertEqual(0, len(b))
2328 # prepare and send a packet that will be rewritten into the wg interface
2329 # expect a data packet sent
2331 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2332 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
2333 / UDP(sport=555, dport=556)
2336 rxs = self.send_and_expect(self.pg0, [p], self.pg1)
2338 # verify the data packet
2339 peer_1.validate_encapped(rxs, p)
2341 # administratively disable the wg interface
2344 # send a packet that will be rewritten into the wg interface
2345 # expect no data packets sent
2346 self.send_and_assert_no_replies(self.pg0, [p])
2348 # administratively enable the wg interface
2349 # expect the peer to send a handshake initiation
2351 peer_1.noise_reset()
2352 rxs = self.pg1.get_capture(1, timeout=2)
2353 resp = peer_1.consume_init(rxs[0], self.pg1)
2355 # send a packet that will be rewritten into the wg interface
2356 # expect no data packets sent because the peer is not initiated
2357 self.send_and_assert_no_replies(self.pg0, [p])
2359 self.base_kp4_err + 1, self.statistics.get_err_counter(self.kp4_error)
2362 # send a handshake response and expect a keepalive message
2363 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
2365 # verify the keepalive message
2366 b = peer_1.decrypt_transport(rxs[0])
2367 self.assertEqual(0, len(b))
2369 # send a packet that will be rewritten into the wg interface
2370 # expect a data packet sent
2371 rxs = self.send_and_expect(self.pg0, [p], self.pg1)
2373 # verify the data packet
2374 peer_1.validate_encapped(rxs, p)
2377 r1.remove_vpp_config()
2378 peer_1.remove_vpp_config()
2379 wg0.remove_vpp_config()
2382 @tag_fixme_vpp_debug
2383 class WireguardHandoffTests(TestWg):
2384 """Wireguard Tests in multi worker setup"""
2386 vpp_worker_count = 2
2388 def test_wg_peer_init(self):
2394 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2399 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.2.0/24", "10.11.3.0/24"]
2401 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2404 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
2407 # send a valid handsake init for which we expect a response
2408 p = peer_1.mk_handshake(self.pg1)
2410 rx = self.send_and_expect(self.pg1, [p], self.pg1)
2412 peer_1.consume_response(rx[0])
2414 # send a data packet from the peer through the tunnel
2415 # this completes the handshake and pins the peer to worker 0
2417 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
2418 / UDP(sport=222, dport=223)
2421 d = peer_1.encrypt_transport(p)
2422 p = peer_1.mk_tunnel_header(self.pg1) / (
2423 Wireguard(message_type=4, reserved_zero=0)
2424 / WireguardTransport(
2425 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
2428 rxs = self.send_and_expect(self.pg1, [p], self.pg0, worker=0)
2431 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
2432 self.assertEqual(rx[IP].ttl, 19)
2434 # send a packets that are routed into the tunnel
2435 # and pins the peer tp worker 1
2437 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2438 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
2439 / UDP(sport=555, dport=556)
2442 rxs = self.send_and_expect(self.pg0, pe * 255, self.pg1, worker=1)
2443 peer_1.validate_encapped(rxs, pe)
2445 # send packets into the tunnel, from the other worker
2448 peer_1.mk_tunnel_header(self.pg1)
2449 / Wireguard(message_type=4, reserved_zero=0)
2450 / WireguardTransport(
2451 receiver_index=peer_1.sender,
2453 encrypted_encapsulated_packet=peer_1.encrypt_transport(
2455 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
2456 / UDP(sport=222, dport=223)
2462 for ii in range(255)
2465 rxs = self.send_and_expect(self.pg1, p, self.pg0, worker=1)
2468 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
2469 self.assertEqual(rx[IP].ttl, 19)
2471 # send a packets that are routed into the tunnel
2473 rxs = self.send_and_expect(self.pg0, pe * 255, self.pg1, worker=0)
2475 peer_1.validate_encapped(rxs, pe)
2477 r1.remove_vpp_config()
2478 peer_1.remove_vpp_config()
2479 wg0.remove_vpp_config()
2481 @unittest.skip("test disabled")
2482 def test_wg_multi_interface(self):
2483 """Multi-tunnel on the same port"""
2486 class TestWgFIB(VppTestCase):
2487 """Wireguard FIB Test Case"""
2490 def setUpClass(cls):
2491 super(TestWgFIB, cls).setUpClass()
2494 def tearDownClass(cls):
2495 super(TestWgFIB, cls).tearDownClass()
2498 super(TestWgFIB, self).setUp()
2500 self.create_pg_interfaces(range(2))
2502 for i in self.pg_interfaces:
2507 for i in self.pg_interfaces:
2510 super(TestWgFIB, self).tearDown()
2512 def test_wg_fib_tracking(self):
2516 # create wg interface
2517 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2521 self.pg_enable_capture(self.pg_interfaces)
2526 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
2528 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2530 # create a route to rewrite traffic into the wg interface
2532 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
2535 # resolve ARP and expect the adjacency to update
2536 self.pg1.resolve_arp()
2538 # wait for the peer to send a handshake initiation
2539 rxs = self.pg1.get_capture(2, timeout=6)
2541 # prepare and send a handshake response
2542 # expect a keepalive message
2543 resp = peer_1.consume_init(rxs[1], self.pg1)
2544 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
2546 # verify the keepalive message
2547 b = peer_1.decrypt_transport(rxs[0])
2548 self.assertEqual(0, len(b))
2550 # prepare and send a packet that will be rewritten into the wg interface
2551 # expect a data packet sent
2553 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2554 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
2555 / UDP(sport=555, dport=556)
2558 rxs = self.send_and_expect(self.pg0, [p], self.pg1)
2560 # verify the data packet
2561 peer_1.validate_encapped(rxs, p)
2564 r1.remove_vpp_config()
2565 peer_1.remove_vpp_config()
2566 wg0.remove_vpp_config()