8 from hashlib import blake2s
9 from config import config
10 from scapy.packet import Packet
11 from scapy.packet import Raw
12 from scapy.layers.l2 import Ether, ARP
13 from scapy.layers.inet import IP, UDP
14 from scapy.layers.inet6 import IPv6
15 from scapy.layers.vxlan import VXLAN
16 from scapy.contrib.wireguard import (
23 from cryptography.hazmat.primitives.asymmetric.x25519 import (
27 from cryptography.hazmat.primitives.serialization import (
33 from cryptography.hazmat.primitives.hashes import BLAKE2s, Hash
34 from cryptography.hazmat.primitives.hmac import HMAC
35 from cryptography.hazmat.backends import default_backend
36 from noise.connection import NoiseConnection, Keypair
38 from Crypto.Cipher import ChaCha20_Poly1305
39 from Crypto.Random import get_random_bytes
41 from vpp_ipip_tun_interface import VppIpIpTunInterface
42 from vpp_interface import VppInterface
43 from vpp_pg_interface import is_ipv6_misc
44 from vpp_ip_route import VppIpRoute, VppRoutePath
45 from vpp_l2 import VppBridgeDomain, VppBridgeDomainPort
46 from vpp_vxlan_tunnel import VppVxlanTunnel
47 from vpp_object import VppObject
48 from vpp_papi import VppEnum
49 from framework import is_distro_ubuntu2204, is_distro_debian11, tag_fixme_vpp_debug
50 from framework import VppTestCase
51 from re import compile
54 """ TestWg is a subclass of VPPTestCase classes.
61 def private_key_bytes(k):
62 return k.private_bytes(Encoding.Raw, PrivateFormat.Raw, NoEncryption())
65 def public_key_bytes(k):
66 return k.public_bytes(Encoding.Raw, PublicFormat.Raw)
69 def get_field_bytes(pkt, name):
70 fld, val = pkt.getfield_and_val(name)
71 return fld.i2m(pkt, val)
74 class VppWgInterface(VppInterface):
76 VPP WireGuard interface
79 def __init__(self, test, src, port):
80 super(VppWgInterface, self).__init__(test)
84 self.private_key = X25519PrivateKey.generate()
85 self.public_key = self.private_key.public_key()
87 # cookie related params
88 self.cookie_key = blake2s(b"cookie--" + self.public_key_bytes()).digest()
90 def public_key_bytes(self):
91 return public_key_bytes(self.public_key)
93 def private_key_bytes(self):
94 return private_key_bytes(self.private_key)
96 def add_vpp_config(self):
97 r = self.test.vapi.wireguard_interface_create(
99 "user_instance": 0xFFFFFFFF,
102 "private_key": private_key_bytes(self.private_key),
103 "generate_key": False,
106 self.set_sw_if_index(r.sw_if_index)
107 self.test.registry.register(self, self.test.logger)
110 def remove_vpp_config(self):
111 self.test.vapi.wireguard_interface_delete(sw_if_index=self._sw_if_index)
113 def query_vpp_config(self):
114 ts = self.test.vapi.wireguard_interface_dump(sw_if_index=0xFFFFFFFF)
117 t.interface.sw_if_index == self._sw_if_index
118 and str(t.interface.src_ip) == self.src
119 and t.interface.port == self.port
120 and t.interface.private_key == private_key_bytes(self.private_key)
125 def want_events(self, peer_index=0xFFFFFFFF):
126 self.test.vapi.want_wireguard_peer_events(
129 sw_if_index=self._sw_if_index,
130 peer_index=peer_index,
133 def wait_events(self, expect, peers, timeout=5):
134 for i in range(len(peers)):
135 rv = self.test.vapi.wait_for_event(timeout, "wireguard_peer_event")
136 self.test.assertEqual(rv.peer_index, peers[i])
137 self.test.assertEqual(rv.flags, expect)
140 return self.object_id()
143 return "wireguard-%d" % self._sw_if_index
146 NOISE_HANDSHAKE_NAME = b"Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s"
147 NOISE_IDENTIFIER_NAME = b"WireGuard v1 zx2c4 Jason@zx2c4.com"
149 HANDSHAKE_COUNTING_INTERVAL = 0.5
150 UNDER_LOAD_INTERVAL = 1.0
151 HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD = 40
152 HANDSHAKE_NUM_BEFORE_RATELIMITING = 5
154 HANDSHAKE_JITTER = 0.5
157 class VppWgPeer(VppObject):
158 def __init__(self, test, itf, endpoint, port, allowed_ips, persistent_keepalive=15):
161 self.endpoint = endpoint
163 self.allowed_ips = allowed_ips
164 self.persistent_keepalive = persistent_keepalive
166 # remote peer's public
167 self.private_key = X25519PrivateKey.generate()
168 self.public_key = self.private_key.public_key()
170 # cookie related params
171 self.cookie_key = blake2s(b"cookie--" + self.public_key_bytes()).digest()
172 self.last_sent_cookie = None
173 self.last_mac1 = None
174 self.last_received_cookie = None
176 self.noise = NoiseConnection.from_name(NOISE_HANDSHAKE_NAME)
178 def change_endpoint(self, endpoint, port):
179 self.endpoint = endpoint
182 def add_vpp_config(self):
183 rv = self._test.vapi.wireguard_peer_add(
185 "public_key": self.public_key_bytes(),
187 "endpoint": self.endpoint,
188 "n_allowed_ips": len(self.allowed_ips),
189 "allowed_ips": self.allowed_ips,
190 "sw_if_index": self.itf.sw_if_index,
191 "persistent_keepalive": self.persistent_keepalive,
194 self.index = rv.peer_index
195 self.receiver_index = self.index + 1
196 self._test.registry.register(self, self._test.logger)
199 def remove_vpp_config(self):
200 self._test.vapi.wireguard_peer_remove(peer_index=self.index)
203 return "wireguard-peer-%s" % self.index
205 def public_key_bytes(self):
206 return public_key_bytes(self.public_key)
208 def query_vpp_config(self):
209 peers = self._test.vapi.wireguard_peers_dump()
212 # "::" endpoint will be returned as "0.0.0.0" in peer's details
213 endpoint = "0.0.0.0" if self.endpoint == "::" else self.endpoint
215 p.peer.public_key == self.public_key_bytes()
216 and p.peer.port == self.port
217 and str(p.peer.endpoint) == endpoint
218 and p.peer.sw_if_index == self.itf.sw_if_index
219 and len(self.allowed_ips) == p.peer.n_allowed_ips
221 self.allowed_ips.sort()
222 p.peer.allowed_ips.sort()
224 for a1, a2 in zip(self.allowed_ips, p.peer.allowed_ips):
225 if str(a1) != str(a2):
230 def mk_tunnel_header(self, tx_itf, is_ip6=False):
233 Ether(dst=tx_itf.local_mac, src=tx_itf.remote_mac)
234 / IP(src=self.endpoint, dst=self.itf.src)
235 / UDP(sport=self.port, dport=self.itf.port)
239 Ether(dst=tx_itf.local_mac, src=tx_itf.remote_mac)
240 / IPv6(src=self.endpoint, dst=self.itf.src)
241 / UDP(sport=self.port, dport=self.itf.port)
244 def noise_reset(self):
245 self.noise = NoiseConnection.from_name(NOISE_HANDSHAKE_NAME)
247 def noise_init(self, public_key=None):
248 self.noise.set_prologue(NOISE_IDENTIFIER_NAME)
249 self.noise.set_psks(psk=bytes(bytearray(32)))
252 public_key = self.itf.public_key
255 self.noise.set_keypair_from_private_bytes(
256 Keypair.STATIC, private_key_bytes(self.private_key)
259 self.noise.set_keypair_from_public_bytes(
260 Keypair.REMOTE_STATIC, public_key_bytes(public_key)
263 self.noise.start_handshake()
265 def mk_cookie(self, p, tx_itf, is_resp=False, is_ip6=False):
266 self.verify_header(p, is_ip6)
268 wg_pkt = Wireguard(p[Raw])
271 self._test.assertEqual(wg_pkt[Wireguard].message_type, 2)
272 self._test.assertEqual(wg_pkt[Wireguard].reserved_zero, 0)
273 self._test.assertEqual(wg_pkt[WireguardResponse].mac2, bytes([0] * 16))
275 self._test.assertEqual(wg_pkt[Wireguard].message_type, 1)
276 self._test.assertEqual(wg_pkt[Wireguard].reserved_zero, 0)
277 self._test.assertEqual(wg_pkt[WireguardInitiation].mac2, bytes([0] * 16))
279 # collect info from wg packet (initiation or response)
280 src = get_field_bytes(p[IPv6 if is_ip6 else IP], "src")
281 sport = p[UDP].sport.to_bytes(2, byteorder="big")
283 mac1 = wg_pkt[WireguardResponse].mac1
284 sender_index = wg_pkt[WireguardResponse].sender_index
286 mac1 = wg_pkt[WireguardInitiation].mac1
287 sender_index = wg_pkt[WireguardInitiation].sender_index
290 cookie_reply = Wireguard() / WireguardCookieReply()
291 cookie_reply[Wireguard].message_type = 3
292 cookie_reply[Wireguard].reserved_zero = 0
293 cookie_reply[WireguardCookieReply].receiver_index = sender_index
294 nonce = get_random_bytes(24)
295 cookie_reply[WireguardCookieReply].nonce = nonce
297 # generate cookie data
298 changing_secret = get_random_bytes(32)
299 self.last_sent_cookie = blake2s(
300 src + sport, digest_size=16, key=changing_secret
303 # encrypt cookie data
304 cipher = ChaCha20_Poly1305.new(key=self.cookie_key, nonce=nonce)
306 ciphertext, tag = cipher.encrypt_and_digest(self.last_sent_cookie)
307 cookie_reply[WireguardCookieReply].encrypted_cookie = ciphertext + tag
309 # prepare cookie reply to be sent
310 cookie_reply = self.mk_tunnel_header(tx_itf, is_ip6) / cookie_reply
314 def consume_cookie(self, p, is_ip6=False):
315 self.verify_header(p, is_ip6)
317 cookie_reply = Wireguard(p[Raw])
319 self._test.assertEqual(cookie_reply[Wireguard].message_type, 3)
320 self._test.assertEqual(cookie_reply[Wireguard].reserved_zero, 0)
321 self._test.assertEqual(
322 cookie_reply[WireguardCookieReply].receiver_index, self.receiver_index
325 # collect info from cookie reply
326 nonce = cookie_reply[WireguardCookieReply].nonce
327 encrypted_cookie = cookie_reply[WireguardCookieReply].encrypted_cookie
328 ciphertext, tag = encrypted_cookie[:16], encrypted_cookie[16:]
330 # decrypt cookie data
331 cipher = ChaCha20_Poly1305.new(key=self.itf.cookie_key, nonce=nonce)
332 cipher.update(self.last_mac1)
333 self.last_received_cookie = cipher.decrypt_and_verify(ciphertext, tag)
335 def mk_handshake(self, tx_itf, is_ip6=False, public_key=None):
336 self.noise.set_as_initiator()
337 self.noise_init(public_key)
339 p = Wireguard() / WireguardInitiation()
341 p[Wireguard].message_type = 1
342 p[Wireguard].reserved_zero = 0
343 p[WireguardInitiation].sender_index = self.receiver_index
345 # some random data for the message
346 # lifted from the noise protocol's wireguard example
347 now = datetime.datetime.now()
350 4611686018427387914 + int(now.timestamp()),
351 int(now.microsecond * 1e3),
353 b = self.noise.write_message(payload=tai)
355 # load noise into init message
356 p[WireguardInitiation].unencrypted_ephemeral = b[0:32]
357 p[WireguardInitiation].encrypted_static = b[32:80]
358 p[WireguardInitiation].encrypted_timestamp = b[80:108]
360 # generate the mac1 hash
361 mac_key = blake2s(b"mac1----" + self.itf.public_key_bytes()).digest()
362 mac1 = blake2s(bytes(p)[0:116], digest_size=16, key=mac_key).digest()
363 p[WireguardInitiation].mac1 = mac1
364 self.last_mac1 = mac1
366 # generate the mac2 hash
367 if self.last_received_cookie:
369 bytes(p)[0:132], digest_size=16, key=self.last_received_cookie
371 p[WireguardInitiation].mac2 = mac2
372 self.last_received_cookie = None
374 p[WireguardInitiation].mac2 = bytearray(16)
376 p = self.mk_tunnel_header(tx_itf, is_ip6) / p
380 def verify_header(self, p, is_ip6=False):
382 self._test.assertEqual(p[IP].src, self.itf.src)
383 self._test.assertEqual(p[IP].dst, self.endpoint)
384 self._test.assert_packet_checksums_valid(p)
386 self._test.assertEqual(p[IPv6].src, self.itf.src)
387 self._test.assertEqual(p[IPv6].dst, self.endpoint)
388 self._test.assert_packet_checksums_valid(p, False)
389 self._test.assertEqual(p[UDP].sport, self.itf.port)
390 self._test.assertEqual(p[UDP].dport, self.port)
392 def consume_init(self, p, tx_itf, is_ip6=False, is_mac2=False):
393 self.noise.set_as_responder()
394 self.noise_init(self.itf.public_key)
395 self.verify_header(p, is_ip6)
397 init = Wireguard(p[Raw])
399 self._test.assertEqual(init[Wireguard].message_type, 1)
400 self._test.assertEqual(init[Wireguard].reserved_zero, 0)
402 self.sender = init[WireguardInitiation].sender_index
404 # validate the mac1 hash
405 mac_key = blake2s(b"mac1----" + public_key_bytes(self.public_key)).digest()
406 mac1 = blake2s(bytes(init)[0:-32], digest_size=16, key=mac_key).digest()
407 self._test.assertEqual(init[WireguardInitiation].mac1, mac1)
409 # validate the mac2 hash
411 self._test.assertNotEqual(init[WireguardInitiation].mac2, bytes([0] * 16))
412 self._test.assertNotEqual(self.last_sent_cookie, None)
414 bytes(init)[0:-16], digest_size=16, key=self.last_sent_cookie
416 self._test.assertEqual(init[WireguardInitiation].mac2, mac2)
417 self.last_sent_cookie = None
419 self._test.assertEqual(init[WireguardInitiation].mac2, bytes([0] * 16))
421 # this passes only unencrypted_ephemeral, encrypted_static,
422 # encrypted_timestamp fields of the init
423 payload = self.noise.read_message(bytes(init)[8:-32])
426 b = self.noise.write_message()
427 mac_key = blake2s(b"mac1----" + public_key_bytes(self.itf.public_key)).digest()
428 resp = Wireguard(message_type=2, reserved_zero=0) / WireguardResponse(
429 sender_index=self.receiver_index,
430 receiver_index=self.sender,
431 unencrypted_ephemeral=b[0:32],
432 encrypted_nothing=b[32:],
434 mac1 = blake2s(bytes(resp)[:-32], digest_size=16, key=mac_key).digest()
435 resp[WireguardResponse].mac1 = mac1
436 self.last_mac1 = mac1
438 resp = self.mk_tunnel_header(tx_itf, is_ip6) / resp
439 self._test.assertTrue(self.noise.handshake_finished)
443 def consume_response(self, p, is_ip6=False):
444 self.verify_header(p, is_ip6)
446 resp = Wireguard(p[Raw])
448 self._test.assertEqual(resp[Wireguard].message_type, 2)
449 self._test.assertEqual(resp[Wireguard].reserved_zero, 0)
450 self._test.assertEqual(
451 resp[WireguardResponse].receiver_index, self.receiver_index
454 self.sender = resp[Wireguard].sender_index
456 payload = self.noise.read_message(bytes(resp)[12:60])
457 self._test.assertEqual(payload, b"")
458 self._test.assertTrue(self.noise.handshake_finished)
460 def decrypt_transport(self, p, is_ip6=False):
461 self.verify_header(p, is_ip6)
463 p = Wireguard(p[Raw])
464 self._test.assertEqual(p[Wireguard].message_type, 4)
465 self._test.assertEqual(p[Wireguard].reserved_zero, 0)
466 self._test.assertEqual(
467 p[WireguardTransport].receiver_index, self.receiver_index
470 d = self.noise.decrypt(p[WireguardTransport].encrypted_encapsulated_packet)
473 def encrypt_transport(self, p):
474 return self.noise.encrypt(bytes(p))
476 def validate_encapped(self, rxs, tx, is_tunnel_ip6=False, is_transport_ip6=False):
479 rx = self.decrypt_transport(rx, is_tunnel_ip6)
480 if is_transport_ip6 is False:
482 # check the original packet is present
483 self._test.assertEqual(rx[IP].dst, tx[IP].dst)
484 self._test.assertEqual(rx[IP].ttl, tx[IP].ttl - 1)
487 # check the original packet is present
488 self._test.assertEqual(rx[IPv6].dst, tx[IPv6].dst)
489 self._test.assertEqual(rx[IPv6].hlim, tx[IPv6].hlim - 1)
493 def want_events(self):
494 self._test.vapi.want_wireguard_peer_events(
497 peer_index=self.index,
498 sw_if_index=self.itf.sw_if_index,
501 def wait_event(self, expect, timeout=5):
502 rv = self._test.vapi.wait_for_event(timeout, "wireguard_peer_event")
503 self._test.assertEqual(rv.flags, expect)
504 self._test.assertEqual(rv.peer_index, self.index)
507 def is_handshake_init(p):
508 wg_p = Wireguard(p[Raw])
510 return wg_p[Wireguard].message_type == 1
514 "wireguard" in config.excluded_plugins, "Exclude Wireguard plugin tests"
516 class TestWg(VppTestCase):
517 """Wireguard Test Case"""
519 error_str = compile(r"Error")
521 wg4_output_node_name = "/err/wg4-output-tun/"
522 wg4_input_node_name = "/err/wg4-input/"
523 wg6_output_node_name = "/err/wg6-output-tun/"
524 wg6_input_node_name = "/err/wg6-input/"
525 kp4_error = wg4_output_node_name + "Keypair error"
526 mac4_error = wg4_input_node_name + "Invalid MAC handshake"
527 peer4_in_err = wg4_input_node_name + "Peer error"
528 peer4_out_err = wg4_output_node_name + "Peer error"
529 kp6_error = wg6_output_node_name + "Keypair error"
530 mac6_error = wg6_input_node_name + "Invalid MAC handshake"
531 peer6_in_err = wg6_input_node_name + "Peer error"
532 peer6_out_err = wg6_output_node_name + "Peer error"
533 cookie_dec4_err = wg4_input_node_name + "Failed during Cookie decryption"
534 cookie_dec6_err = wg6_input_node_name + "Failed during Cookie decryption"
535 ratelimited4_err = wg4_input_node_name + "Handshake ratelimited"
536 ratelimited6_err = wg6_input_node_name + "Handshake ratelimited"
540 super(TestWg, cls).setUpClass()
541 if (is_distro_ubuntu2204 == True or is_distro_debian11 == True) and not hasattr(
546 cls.create_pg_interfaces(range(3))
547 for i in cls.pg_interfaces:
555 super(TestWg, cls).tearDownClass()
559 def tearDownClass(cls):
560 super(TestWg, cls).tearDownClass()
563 super(VppTestCase, self).setUp()
564 self.base_kp4_err = self.statistics.get_err_counter(self.kp4_error)
565 self.base_mac4_err = self.statistics.get_err_counter(self.mac4_error)
566 self.base_peer4_in_err = self.statistics.get_err_counter(self.peer4_in_err)
567 self.base_peer4_out_err = self.statistics.get_err_counter(self.peer4_out_err)
568 self.base_kp6_err = self.statistics.get_err_counter(self.kp6_error)
569 self.base_mac6_err = self.statistics.get_err_counter(self.mac6_error)
570 self.base_peer6_in_err = self.statistics.get_err_counter(self.peer6_in_err)
571 self.base_peer6_out_err = self.statistics.get_err_counter(self.peer6_out_err)
572 self.base_cookie_dec4_err = self.statistics.get_err_counter(
575 self.base_cookie_dec6_err = self.statistics.get_err_counter(
578 self.base_ratelimited4_err = self.statistics.get_err_counter(
579 self.ratelimited4_err
581 self.base_ratelimited6_err = self.statistics.get_err_counter(
582 self.ratelimited6_err
585 def send_and_assert_no_replies_ignoring_init(
586 self, intf, pkts, remark="", timeout=None
588 self.pg_send(intf, pkts)
590 def _filter_out_fn(p):
591 return is_ipv6_misc(p) or is_handshake_init(p)
596 for i in self.pg_interfaces:
597 i.assert_nothing_captured(
598 timeout=timeout, remark=remark, filter_out_fn=_filter_out_fn
604 def test_wg_interface(self):
605 """Simple interface creation"""
609 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
611 self.logger.info(self.vapi.cli("sh int"))
614 wg0.remove_vpp_config()
616 def test_handshake_hash(self):
617 """test hashing an init message"""
618 # a init packet generated by linux given the key below
641 b = bytearray.fromhex(h)
644 pubb = base64.b64decode("aRuHFTTxICIQNefp05oKWlJv3zgKxb8+WW7JJMh0jyM=")
645 pub = X25519PublicKey.from_public_bytes(pubb)
647 self.assertEqual(pubb, public_key_bytes(pub))
649 # strip the macs and build a new packet
651 mac_key = blake2s(b"mac1----" + public_key_bytes(pub)).digest()
652 init += blake2s(init, digest_size=16, key=mac_key).digest()
655 act = Wireguard(init)
657 self.assertEqual(tgt, act)
659 def _test_wg_send_cookie_tmpl(self, is_resp, is_ip6):
662 # create wg interface
664 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
668 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
672 self.pg_enable_capture(self.pg_interfaces)
678 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
682 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
684 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
687 # skip the first automatic handshake
688 self.pg1.get_capture(1, timeout=HANDSHAKE_JITTER)
690 # prepare and send a handshake initiation
691 # expect the peer to send a handshake response
692 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
693 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
695 # wait for the peer to send a handshake initiation
696 rxs = self.pg1.get_capture(1, timeout=2)
698 # prepare and send a wrong cookie reply
699 # expect no replies and the cookie error incremented
700 cookie = peer_1.mk_cookie(rxs[0], self.pg1, is_resp=is_resp, is_ip6=is_ip6)
701 cookie.nonce = b"1234567890"
702 self.send_and_assert_no_replies(self.pg1, [cookie], timeout=0.1)
705 self.base_cookie_dec6_err + 1,
706 self.statistics.get_err_counter(self.cookie_dec6_err),
710 self.base_cookie_dec4_err + 1,
711 self.statistics.get_err_counter(self.cookie_dec4_err),
714 # prepare and send a correct cookie reply
715 cookie = peer_1.mk_cookie(rxs[0], self.pg1, is_resp=is_resp, is_ip6=is_ip6)
716 self.pg_send(self.pg1, [cookie])
718 # wait for the peer to send a handshake initiation with mac2 set
719 rxs = self.pg1.get_capture(1, timeout=6)
721 # verify the initiation and its mac2
722 peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6, is_mac2=True)
725 peer_1.remove_vpp_config()
726 wg0.remove_vpp_config()
728 def test_wg_send_cookie_on_init_v4(self):
729 """Send cookie on handshake initiation (v4)"""
730 self._test_wg_send_cookie_tmpl(is_resp=False, is_ip6=False)
732 def test_wg_send_cookie_on_init_v6(self):
733 """Send cookie on handshake initiation (v6)"""
734 self._test_wg_send_cookie_tmpl(is_resp=False, is_ip6=True)
736 def test_wg_send_cookie_on_resp_v4(self):
737 """Send cookie on handshake response (v4)"""
738 self._test_wg_send_cookie_tmpl(is_resp=True, is_ip6=False)
740 def test_wg_send_cookie_on_resp_v6(self):
741 """Send cookie on handshake response (v6)"""
742 self._test_wg_send_cookie_tmpl(is_resp=True, is_ip6=True)
744 def _test_wg_receive_cookie_tmpl(self, is_resp, is_ip6):
747 # create wg interface
749 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
753 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
757 self.pg_enable_capture(self.pg_interfaces)
763 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
767 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
769 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
772 # wait for the peer to send a handshake initiation
773 rxs = self.pg1.get_capture(1, timeout=2)
774 # prepare and send a bunch of handshake responses
775 # expect to switch to under load state
776 resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6)
777 txs = [resp] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
778 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
779 # reset noise to be able to turn into initiator later
782 # skip the first automatic handshake
783 self.pg1.get_capture(1, timeout=HANDSHAKE_JITTER)
785 # prepare and send a bunch of handshake initiations
786 # expect to switch to under load state
787 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
788 txs = [init] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
789 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
791 # expect the peer to send a cookie reply
792 peer_1.consume_cookie(rxs[-1], is_ip6=is_ip6)
794 # prepare and send a handshake initiation with wrong mac2
795 # expect a cookie reply
796 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
797 init.mac2 = b"1234567890"
798 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
799 peer_1.consume_cookie(rxs[0], is_ip6=is_ip6)
801 # prepare and send a handshake initiation with correct mac2
802 # expect a handshake response
803 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
804 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
806 # verify the response
807 peer_1.consume_response(rxs[0], is_ip6=is_ip6)
809 # clear up under load state
810 self.sleep(UNDER_LOAD_INTERVAL)
813 peer_1.remove_vpp_config()
814 wg0.remove_vpp_config()
816 def test_wg_receive_cookie_on_init_v4(self):
817 """Receive cookie on handshake initiation (v4)"""
818 self._test_wg_receive_cookie_tmpl(is_resp=False, is_ip6=False)
820 def test_wg_receive_cookie_on_init_v6(self):
821 """Receive cookie on handshake initiation (v6)"""
822 self._test_wg_receive_cookie_tmpl(is_resp=False, is_ip6=True)
824 def test_wg_receive_cookie_on_resp_v4(self):
825 """Receive cookie on handshake response (v4)"""
826 self._test_wg_receive_cookie_tmpl(is_resp=True, is_ip6=False)
828 def test_wg_receive_cookie_on_resp_v6(self):
829 """Receive cookie on handshake response (v6)"""
830 self._test_wg_receive_cookie_tmpl(is_resp=True, is_ip6=True)
832 def test_wg_under_load_interval(self):
833 """Under load interval"""
836 # create wg interface
837 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
841 self.pg_enable_capture(self.pg_interfaces)
846 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
848 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
850 # skip the first automatic handshake
851 self.pg1.get_capture(1, timeout=HANDSHAKE_JITTER)
853 # prepare and send a bunch of handshake initiations
854 # expect to switch to under load state
855 init = peer_1.mk_handshake(self.pg1)
856 txs = [init] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
857 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
859 # expect the peer to send a cookie reply
860 peer_1.consume_cookie(rxs[-1])
862 # sleep till the next counting interval
863 # expect under load state is still active
864 self.sleep(HANDSHAKE_COUNTING_INTERVAL)
866 # prepare and send a handshake initiation with wrong mac2
867 # expect a cookie reply
868 init = peer_1.mk_handshake(self.pg1)
869 init.mac2 = b"1234567890"
870 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
871 peer_1.consume_cookie(rxs[0])
873 # sleep till the end of being under load
874 # expect under load state is over
875 self.sleep(UNDER_LOAD_INTERVAL - HANDSHAKE_COUNTING_INTERVAL)
877 # prepare and send a handshake initiation with wrong mac2
878 # expect a handshake response
879 init = peer_1.mk_handshake(self.pg1)
880 init.mac2 = b"1234567890"
881 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
883 # verify the response
884 peer_1.consume_response(rxs[0])
887 peer_1.remove_vpp_config()
888 wg0.remove_vpp_config()
890 def _test_wg_handshake_ratelimiting_tmpl(self, is_ip6):
893 # create wg interface
895 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
899 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
903 self.pg_enable_capture(self.pg_interfaces)
909 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
913 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
915 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
917 # skip the first automatic handshake
918 self.pg1.get_capture(1, timeout=HANDSHAKE_JITTER)
920 # prepare and send a bunch of handshake initiations
921 # expect to switch to under load state
922 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
923 txs = [init] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
924 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
926 # expect the peer to send a cookie reply
927 peer_1.consume_cookie(rxs[-1], is_ip6=is_ip6)
929 # prepare and send a bunch of handshake initiations with correct mac2
930 # expect a handshake response and then ratelimiting
932 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
933 txs = [init] * (HANDSHAKE_NUM_BEFORE_RATELIMITING + NUM_TO_REJECT)
934 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
938 self.base_ratelimited6_err + NUM_TO_REJECT,
939 self.statistics.get_err_counter(self.ratelimited6_err),
943 self.base_ratelimited4_err + NUM_TO_REJECT,
944 self.statistics.get_err_counter(self.ratelimited4_err),
947 # verify the response
948 peer_1.consume_response(rxs[0], is_ip6=is_ip6)
950 # clear up under load state
951 self.sleep(UNDER_LOAD_INTERVAL)
954 peer_1.remove_vpp_config()
955 wg0.remove_vpp_config()
957 def test_wg_handshake_ratelimiting_v4(self):
958 """Handshake ratelimiting (v4)"""
959 self._test_wg_handshake_ratelimiting_tmpl(is_ip6=False)
961 def test_wg_handshake_ratelimiting_v6(self):
962 """Handshake ratelimiting (v6)"""
963 self._test_wg_handshake_ratelimiting_tmpl(is_ip6=True)
965 def test_wg_handshake_ratelimiting_multi_peer(self):
966 """Handshake ratelimiting (multiple peer)"""
969 # create wg interface
970 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
974 self.pg_enable_capture(self.pg_interfaces)
979 self.pg1.generate_remote_hosts(NUM_PEERS)
980 self.pg1.configure_ipv4_neighbors()
983 self, wg0, self.pg1.remote_hosts[0].ip4, port + 1, ["10.11.3.0/24"]
986 self, wg0, self.pg1.remote_hosts[1].ip4, port + 1, ["10.11.4.0/24"]
988 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 2)
990 # skip the first automatic handshake
991 self.pg1.get_capture(NUM_PEERS, timeout=HANDSHAKE_JITTER)
993 # (peer_1) prepare and send a bunch of handshake initiations
994 # expect not to switch to under load state
995 init_1 = peer_1.mk_handshake(self.pg1)
996 txs = [init_1] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
997 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
999 # (peer_1) expect the peer to send a handshake response
1000 peer_1.consume_response(rxs[0])
1001 peer_1.noise_reset()
1003 # (peer_1) send another bunch of handshake initiations
1004 # expect to switch to under load state
1005 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
1007 # (peer_1) expect the peer to send a cookie reply
1008 peer_1.consume_cookie(rxs[-1])
1010 # (peer_2) prepare and send a handshake initiation
1011 # expect a cookie reply
1012 init_2 = peer_2.mk_handshake(self.pg1)
1013 rxs = self.send_and_expect(self.pg1, [init_2], self.pg1)
1014 peer_2.consume_cookie(rxs[0])
1016 # (peer_1) (peer_2) prepare and send a bunch of handshake initiations with correct mac2
1017 # expect a handshake response and then ratelimiting
1018 PEER_1_NUM_TO_REJECT = 2
1019 PEER_2_NUM_TO_REJECT = 5
1020 init_1 = peer_1.mk_handshake(self.pg1)
1021 txs = [init_1] * (HANDSHAKE_NUM_BEFORE_RATELIMITING + PEER_1_NUM_TO_REJECT)
1022 init_2 = peer_2.mk_handshake(self.pg1)
1023 txs += [init_2] * (HANDSHAKE_NUM_BEFORE_RATELIMITING + PEER_2_NUM_TO_REJECT)
1024 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
1027 self.base_ratelimited4_err + PEER_1_NUM_TO_REJECT
1028 < self.statistics.get_err_counter(self.ratelimited4_err)
1029 <= self.base_ratelimited4_err + PEER_1_NUM_TO_REJECT + PEER_2_NUM_TO_REJECT
1032 # (peer_1) (peer_2) verify the response
1033 peer_1.consume_response(rxs[0])
1034 peer_2.consume_response(rxs[1])
1036 # clear up under load state
1037 self.sleep(UNDER_LOAD_INTERVAL)
1040 peer_1.remove_vpp_config()
1041 peer_2.remove_vpp_config()
1042 wg0.remove_vpp_config()
1044 def _test_wg_peer_roaming_on_handshake_tmpl(self, is_endpoint_set, is_resp, is_ip6):
1047 # create wg interface
1049 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1053 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1057 self.pg_enable_capture(self.pg_interfaces)
1060 # create more remote hosts
1061 NUM_REMOTE_HOSTS = 2
1062 self.pg1.generate_remote_hosts(NUM_REMOTE_HOSTS)
1064 self.pg1.configure_ipv6_neighbors()
1066 self.pg1.configure_ipv4_neighbors()
1073 endpoint=self.pg1.remote_hosts[0].ip6 if is_endpoint_set else "::",
1074 port=port + 1 if is_endpoint_set else 0,
1075 allowed_ips=["1::3:0/112"],
1081 endpoint=self.pg1.remote_hosts[0].ip4 if is_endpoint_set else "0.0.0.0",
1082 port=port + 1 if is_endpoint_set else 0,
1083 allowed_ips=["10.11.3.0/24"],
1085 self.assertTrue(peer_1.query_vpp_config())
1088 # wait for the peer to send a handshake initiation
1089 rxs = self.pg1.get_capture(1, timeout=2)
1090 # prepare a handshake response
1091 resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6)
1094 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip6, port + 100)
1095 resp[IPv6].src, resp[UDP].sport = peer_1.endpoint, peer_1.port
1097 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip4, port + 100)
1098 resp[IP].src, resp[UDP].sport = peer_1.endpoint, peer_1.port
1099 # send the handshake response
1100 # expect a keepalive message sent to the new endpoint
1101 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1102 # verify the keepalive message
1103 b = peer_1.decrypt_transport(rxs[0], is_ip6=is_ip6)
1104 self.assertEqual(0, len(b))
1108 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip6, port + 100)
1110 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip4, port + 100)
1111 # prepare and send a handshake initiation
1112 # expect a handshake response sent to the new endpoint
1113 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
1114 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
1115 # verify the response
1116 peer_1.consume_response(rxs[0], is_ip6=is_ip6)
1117 self.assertTrue(peer_1.query_vpp_config())
1120 peer_1.remove_vpp_config()
1121 wg0.remove_vpp_config()
1123 def test_wg_peer_roaming_on_init_v4(self):
1124 """Peer roaming on handshake initiation (v4)"""
1125 self._test_wg_peer_roaming_on_handshake_tmpl(
1126 is_endpoint_set=False, is_resp=False, is_ip6=False
1129 def test_wg_peer_roaming_on_init_v6(self):
1130 """Peer roaming on handshake initiation (v6)"""
1131 self._test_wg_peer_roaming_on_handshake_tmpl(
1132 is_endpoint_set=False, is_resp=False, is_ip6=True
1135 def test_wg_peer_roaming_on_resp_v4(self):
1136 """Peer roaming on handshake response (v4)"""
1137 self._test_wg_peer_roaming_on_handshake_tmpl(
1138 is_endpoint_set=True, is_resp=True, is_ip6=False
1141 def test_wg_peer_roaming_on_resp_v6(self):
1142 """Peer roaming on handshake response (v6)"""
1143 self._test_wg_peer_roaming_on_handshake_tmpl(
1144 is_endpoint_set=True, is_resp=True, is_ip6=True
1147 def _test_wg_peer_roaming_on_data_tmpl(self, is_async, is_ip6):
1148 self.vapi.wg_set_async_mode(is_async)
1151 # create wg interface
1153 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1157 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1161 self.pg_enable_capture(self.pg_interfaces)
1164 # create more remote hosts
1165 NUM_REMOTE_HOSTS = 2
1166 self.pg1.generate_remote_hosts(NUM_REMOTE_HOSTS)
1168 self.pg1.configure_ipv6_neighbors()
1170 self.pg1.configure_ipv4_neighbors()
1175 self, wg0, self.pg1.remote_hosts[0].ip6, port + 1, ["1::3:0/112"]
1179 self, wg0, self.pg1.remote_hosts[0].ip4, port + 1, ["10.11.3.0/24"]
1181 self.assertTrue(peer_1.query_vpp_config())
1183 # create a route to rewrite traffic into the wg interface
1186 self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
1190 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1193 # wait for the peer to send a handshake initiation
1194 rxs = self.pg1.get_capture(1, timeout=2)
1196 # prepare and send a handshake response
1197 # expect a keepalive message
1198 resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6)
1199 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1201 # verify the keepalive message
1202 b = peer_1.decrypt_transport(rxs[0], is_ip6=is_ip6)
1203 self.assertEqual(0, len(b))
1207 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip6, port + 100)
1209 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip4, port + 100)
1211 # prepare and send a data packet
1212 # expect endpoint change
1214 ip_header = IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1216 ip_header = IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1218 peer_1.mk_tunnel_header(self.pg1, is_ip6=is_ip6)
1219 / Wireguard(message_type=4, reserved_zero=0)
1220 / WireguardTransport(
1221 receiver_index=peer_1.sender,
1223 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1224 ip_header / UDP(sport=222, dport=223) / Raw()
1228 rxs = self.send_and_expect(self.pg1, [data], self.pg0)
1230 self.assertEqual(rxs[0][IPv6].dst, self.pg0.remote_ip6)
1231 self.assertEqual(rxs[0][IPv6].hlim, 19)
1233 self.assertEqual(rxs[0][IP].dst, self.pg0.remote_ip4)
1234 self.assertEqual(rxs[0][IP].ttl, 19)
1235 self.assertTrue(peer_1.query_vpp_config())
1237 # prepare and send a packet that will be rewritten into the wg interface
1238 # expect a data packet sent to the new endpoint
1240 ip_header = IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1242 ip_header = IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1244 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1246 / UDP(sport=555, dport=556)
1249 rxs = self.send_and_expect(self.pg0, [p], self.pg1)
1251 # verify the data packet
1252 peer_1.validate_encapped(rxs, p, is_tunnel_ip6=is_ip6, is_transport_ip6=is_ip6)
1255 r1.remove_vpp_config()
1256 peer_1.remove_vpp_config()
1257 wg0.remove_vpp_config()
1259 def test_wg_peer_roaming_on_data_v4_sync(self):
1260 """Peer roaming on data packet (v4, sync)"""
1261 self._test_wg_peer_roaming_on_data_tmpl(is_async=False, is_ip6=False)
1263 def test_wg_peer_roaming_on_data_v6_sync(self):
1264 """Peer roaming on data packet (v6, sync)"""
1265 self._test_wg_peer_roaming_on_data_tmpl(is_async=False, is_ip6=True)
1267 def test_wg_peer_roaming_on_data_v4_async(self):
1268 """Peer roaming on data packet (v4, async)"""
1269 self._test_wg_peer_roaming_on_data_tmpl(is_async=True, is_ip6=False)
1271 def test_wg_peer_roaming_on_data_v6_async(self):
1272 """Peer roaming on data packet (v6, async)"""
1273 self._test_wg_peer_roaming_on_data_tmpl(is_async=True, is_ip6=True)
1275 def test_wg_peer_resp(self):
1276 """Send handshake response IPv4 tunnel"""
1280 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1284 self.pg_enable_capture(self.pg_interfaces)
1288 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
1290 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1293 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1296 # wait for the peer to send a handshake
1297 rx = self.pg1.get_capture(1, timeout=2)
1299 # consume the handshake in the noise protocol and
1300 # generate the response
1301 resp = peer_1.consume_init(rx[0], self.pg1)
1303 # send the response, get keepalive
1304 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1307 b = peer_1.decrypt_transport(rx)
1308 self.assertEqual(0, len(b))
1310 # send a packets that are routed into the tunnel
1312 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1313 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1314 / UDP(sport=555, dport=556)
1318 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1320 peer_1.validate_encapped(rxs, p)
1322 # send packets into the tunnel, expect to receive them on
1326 peer_1.mk_tunnel_header(self.pg1)
1327 / Wireguard(message_type=4, reserved_zero=0)
1328 / WireguardTransport(
1329 receiver_index=peer_1.sender,
1331 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1333 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1334 / UDP(sport=222, dport=223)
1340 for ii in range(255)
1343 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1346 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1347 self.assertEqual(rx[IP].ttl, 19)
1349 r1.remove_vpp_config()
1350 peer_1.remove_vpp_config()
1351 wg0.remove_vpp_config()
1353 def test_wg_peer_resp_ipv6(self):
1354 """Send handshake response IPv6 tunnel"""
1358 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1362 self.pg_enable_capture(self.pg_interfaces)
1366 self, wg0, self.pg1.remote_ip6, port + 1, ["10.11.3.0/24"]
1368 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1371 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1374 # wait for the peer to send a handshake
1375 rx = self.pg1.get_capture(1, timeout=2)
1377 # consume the handshake in the noise protocol and
1378 # generate the response
1379 resp = peer_1.consume_init(rx[0], self.pg1, is_ip6=True)
1381 # send the response, get keepalive
1382 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1385 b = peer_1.decrypt_transport(rx, True)
1386 self.assertEqual(0, len(b))
1388 # send a packets that are routed into the tunnel
1390 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1391 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1392 / UDP(sport=555, dport=556)
1396 rxs = self.send_and_expect(self.pg0, p * 2, self.pg1)
1397 peer_1.validate_encapped(rxs, p, True)
1399 # send packets into the tunnel, expect to receive them on
1403 peer_1.mk_tunnel_header(self.pg1, True)
1404 / Wireguard(message_type=4, reserved_zero=0)
1405 / WireguardTransport(
1406 receiver_index=peer_1.sender,
1408 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1410 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1411 / UDP(sport=222, dport=223)
1417 for ii in range(255)
1420 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1423 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1424 self.assertEqual(rx[IP].ttl, 19)
1426 r1.remove_vpp_config()
1427 peer_1.remove_vpp_config()
1428 wg0.remove_vpp_config()
1430 def test_wg_peer_v4o4(self):
1436 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1441 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
1443 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1446 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1449 self, "20.22.3.0", 24, [VppRoutePath("20.22.3.1", wg0.sw_if_index)]
1452 # route a packet into the wg interface
1453 # use the allowed-ip prefix
1454 # this is dropped because the peer is not initiated
1456 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1457 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1458 / UDP(sport=555, dport=556)
1461 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1463 self.base_kp4_err + 1, self.statistics.get_err_counter(self.kp4_error)
1466 # route a packet into the wg interface
1467 # use a not allowed-ip prefix
1468 # this is dropped because there is no matching peer
1470 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1471 / IP(src=self.pg0.remote_ip4, dst="20.22.3.2")
1472 / UDP(sport=555, dport=556)
1475 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1477 self.base_peer4_out_err + 1,
1478 self.statistics.get_err_counter(self.peer4_out_err),
1481 # send a handsake from the peer with an invalid MAC
1482 p = peer_1.mk_handshake(self.pg1)
1483 p[WireguardInitiation].mac1 = b"foobar"
1484 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1486 self.base_mac4_err + 1, self.statistics.get_err_counter(self.mac4_error)
1489 # send a handsake from the peer but signed by the wrong key.
1490 p = peer_1.mk_handshake(
1491 self.pg1, False, X25519PrivateKey.generate().public_key()
1493 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1495 self.base_peer4_in_err + 1,
1496 self.statistics.get_err_counter(self.peer4_in_err),
1499 # send a valid handsake init for which we expect a response
1500 p = peer_1.mk_handshake(self.pg1)
1502 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1504 peer_1.consume_response(rx[0])
1506 # route a packet into the wg interface
1507 # this is dropped because the peer is still not initiated
1509 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1510 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1511 / UDP(sport=555, dport=556)
1514 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1516 self.base_kp4_err + 2, self.statistics.get_err_counter(self.kp4_error)
1519 # send a data packet from the peer through the tunnel
1520 # this completes the handshake
1522 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1523 / UDP(sport=222, dport=223)
1526 d = peer_1.encrypt_transport(p)
1527 p = peer_1.mk_tunnel_header(self.pg1) / (
1528 Wireguard(message_type=4, reserved_zero=0)
1529 / WireguardTransport(
1530 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1533 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1536 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1537 self.assertEqual(rx[IP].ttl, 19)
1539 # send a packets that are routed into the tunnel
1541 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1542 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1543 / UDP(sport=555, dport=556)
1547 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1550 rx = IP(peer_1.decrypt_transport(rx))
1552 # check the original packet is present
1553 self.assertEqual(rx[IP].dst, p[IP].dst)
1554 self.assertEqual(rx[IP].ttl, p[IP].ttl - 1)
1556 # send packets into the tunnel, expect to receive them on
1560 peer_1.mk_tunnel_header(self.pg1)
1561 / Wireguard(message_type=4, reserved_zero=0)
1562 / WireguardTransport(
1563 receiver_index=peer_1.sender,
1565 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1567 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1568 / UDP(sport=222, dport=223)
1574 for ii in range(255)
1577 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1580 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1581 self.assertEqual(rx[IP].ttl, 19)
1583 r1.remove_vpp_config()
1584 r2.remove_vpp_config()
1585 peer_1.remove_vpp_config()
1586 wg0.remove_vpp_config()
1588 def test_wg_peer_v6o6(self):
1594 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1599 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
1601 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1604 self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
1607 self, "22::3:0", 112, [VppRoutePath("22::3:1", wg0.sw_if_index)]
1610 # route a packet into the wg interface
1611 # use the allowed-ip prefix
1612 # this is dropped because the peer is not initiated
1615 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1616 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1617 / UDP(sport=555, dport=556)
1620 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1623 self.base_kp6_err + 1, self.statistics.get_err_counter(self.kp6_error)
1626 # route a packet into the wg interface
1627 # use a not allowed-ip prefix
1628 # this is dropped because there is no matching peer
1630 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1631 / IPv6(src=self.pg0.remote_ip6, dst="22::3:2")
1632 / UDP(sport=555, dport=556)
1635 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1637 self.base_peer6_out_err + 1,
1638 self.statistics.get_err_counter(self.peer6_out_err),
1641 # send a handsake from the peer with an invalid MAC
1642 p = peer_1.mk_handshake(self.pg1, True)
1643 p[WireguardInitiation].mac1 = b"foobar"
1644 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1647 self.base_mac6_err + 1, self.statistics.get_err_counter(self.mac6_error)
1650 # send a handsake from the peer but signed by the wrong key.
1651 p = peer_1.mk_handshake(
1652 self.pg1, True, X25519PrivateKey.generate().public_key()
1654 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1656 self.base_peer6_in_err + 1,
1657 self.statistics.get_err_counter(self.peer6_in_err),
1660 # send a valid handsake init for which we expect a response
1661 p = peer_1.mk_handshake(self.pg1, True)
1663 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1665 peer_1.consume_response(rx[0], True)
1667 # route a packet into the wg interface
1668 # this is dropped because the peer is still not initiated
1670 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1671 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1672 / UDP(sport=555, dport=556)
1675 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1677 self.base_kp6_err + 2, self.statistics.get_err_counter(self.kp6_error)
1680 # send a data packet from the peer through the tunnel
1681 # this completes the handshake
1683 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1684 / UDP(sport=222, dport=223)
1687 d = peer_1.encrypt_transport(p)
1688 p = peer_1.mk_tunnel_header(self.pg1, True) / (
1689 Wireguard(message_type=4, reserved_zero=0)
1690 / WireguardTransport(
1691 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1694 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1697 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1698 self.assertEqual(rx[IPv6].hlim, 19)
1700 # send a packets that are routed into the tunnel
1702 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1703 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1704 / UDP(sport=555, dport=556)
1708 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1711 rx = IPv6(peer_1.decrypt_transport(rx, True))
1713 # check the original packet is present
1714 self.assertEqual(rx[IPv6].dst, p[IPv6].dst)
1715 self.assertEqual(rx[IPv6].hlim, p[IPv6].hlim - 1)
1717 # send packets into the tunnel, expect to receive them on
1721 peer_1.mk_tunnel_header(self.pg1, True)
1722 / Wireguard(message_type=4, reserved_zero=0)
1723 / WireguardTransport(
1724 receiver_index=peer_1.sender,
1726 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1728 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1729 / UDP(sport=222, dport=223)
1735 for ii in range(255)
1738 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1741 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1742 self.assertEqual(rx[IPv6].hlim, 19)
1744 r1.remove_vpp_config()
1745 r2.remove_vpp_config()
1746 peer_1.remove_vpp_config()
1747 wg0.remove_vpp_config()
1749 def test_wg_peer_v6o4(self):
1755 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1760 self, wg0, self.pg1.remote_ip4, port + 1, ["1::3:0/112"]
1762 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1765 self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
1768 # route a packet into the wg interface
1769 # use the allowed-ip prefix
1770 # this is dropped because the peer is not initiated
1772 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1773 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1774 / UDP(sport=555, dport=556)
1777 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1779 self.base_kp6_err + 1, self.statistics.get_err_counter(self.kp6_error)
1782 # send a handsake from the peer with an invalid MAC
1783 p = peer_1.mk_handshake(self.pg1)
1784 p[WireguardInitiation].mac1 = b"foobar"
1785 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1788 self.base_mac4_err + 1, self.statistics.get_err_counter(self.mac4_error)
1791 # send a handsake from the peer but signed by the wrong key.
1792 p = peer_1.mk_handshake(
1793 self.pg1, False, X25519PrivateKey.generate().public_key()
1795 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1797 self.base_peer4_in_err + 1,
1798 self.statistics.get_err_counter(self.peer4_in_err),
1801 # send a valid handsake init for which we expect a response
1802 p = peer_1.mk_handshake(self.pg1)
1804 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1806 peer_1.consume_response(rx[0])
1808 # route a packet into the wg interface
1809 # this is dropped because the peer is still not initiated
1811 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1812 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1813 / UDP(sport=555, dport=556)
1816 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1818 self.base_kp6_err + 2, self.statistics.get_err_counter(self.kp6_error)
1821 # send a data packet from the peer through the tunnel
1822 # this completes the handshake
1824 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1825 / UDP(sport=222, dport=223)
1828 d = peer_1.encrypt_transport(p)
1829 p = peer_1.mk_tunnel_header(self.pg1) / (
1830 Wireguard(message_type=4, reserved_zero=0)
1831 / WireguardTransport(
1832 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1835 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1838 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1839 self.assertEqual(rx[IPv6].hlim, 19)
1841 # send a packets that are routed into the tunnel
1843 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1844 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1845 / UDP(sport=555, dport=556)
1849 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1852 rx = IPv6(peer_1.decrypt_transport(rx))
1854 # check the original packet is present
1855 self.assertEqual(rx[IPv6].dst, p[IPv6].dst)
1856 self.assertEqual(rx[IPv6].hlim, p[IPv6].hlim - 1)
1858 # send packets into the tunnel, expect to receive them on
1862 peer_1.mk_tunnel_header(self.pg1)
1863 / Wireguard(message_type=4, reserved_zero=0)
1864 / WireguardTransport(
1865 receiver_index=peer_1.sender,
1867 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1869 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1870 / UDP(sport=222, dport=223)
1876 for ii in range(255)
1879 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1882 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1883 self.assertEqual(rx[IPv6].hlim, 19)
1885 r1.remove_vpp_config()
1886 peer_1.remove_vpp_config()
1887 wg0.remove_vpp_config()
1889 def test_wg_peer_v4o6(self):
1895 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1900 self, wg0, self.pg1.remote_ip6, port + 1, ["10.11.3.0/24"]
1902 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1905 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1908 # route a packet into the wg interface
1909 # use the allowed-ip prefix
1910 # this is dropped because the peer is not initiated
1912 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1913 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1914 / UDP(sport=555, dport=556)
1917 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1919 self.base_kp4_err + 1, self.statistics.get_err_counter(self.kp4_error)
1922 # send a handsake from the peer with an invalid MAC
1923 p = peer_1.mk_handshake(self.pg1, True)
1924 p[WireguardInitiation].mac1 = b"foobar"
1925 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1927 self.base_mac6_err + 1, self.statistics.get_err_counter(self.mac6_error)
1930 # send a handsake from the peer but signed by the wrong key.
1931 p = peer_1.mk_handshake(
1932 self.pg1, True, X25519PrivateKey.generate().public_key()
1934 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1936 self.base_peer6_in_err + 1,
1937 self.statistics.get_err_counter(self.peer6_in_err),
1940 # send a valid handsake init for which we expect a response
1941 p = peer_1.mk_handshake(self.pg1, True)
1943 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1945 peer_1.consume_response(rx[0], True)
1947 # route a packet into the wg interface
1948 # this is dropped because the peer is still not initiated
1950 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1951 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1952 / UDP(sport=555, dport=556)
1955 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1957 self.base_kp4_err + 2, self.statistics.get_err_counter(self.kp4_error)
1960 # send a data packet from the peer through the tunnel
1961 # this completes the handshake
1963 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1964 / UDP(sport=222, dport=223)
1967 d = peer_1.encrypt_transport(p)
1968 p = peer_1.mk_tunnel_header(self.pg1, True) / (
1969 Wireguard(message_type=4, reserved_zero=0)
1970 / WireguardTransport(
1971 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1974 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1977 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1978 self.assertEqual(rx[IP].ttl, 19)
1980 # send a packets that are routed into the tunnel
1982 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1983 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1984 / UDP(sport=555, dport=556)
1988 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1991 rx = IP(peer_1.decrypt_transport(rx, True))
1993 # check the original packet is present
1994 self.assertEqual(rx[IP].dst, p[IP].dst)
1995 self.assertEqual(rx[IP].ttl, p[IP].ttl - 1)
1997 # send packets into the tunnel, expect to receive them on
2001 peer_1.mk_tunnel_header(self.pg1, True)
2002 / Wireguard(message_type=4, reserved_zero=0)
2003 / WireguardTransport(
2004 receiver_index=peer_1.sender,
2006 encrypted_encapsulated_packet=peer_1.encrypt_transport(
2008 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
2009 / UDP(sport=222, dport=223)
2015 for ii in range(255)
2018 rxs = self.send_and_expect(self.pg1, p, self.pg0)
2021 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
2022 self.assertEqual(rx[IP].ttl, 19)
2024 r1.remove_vpp_config()
2025 peer_1.remove_vpp_config()
2026 wg0.remove_vpp_config()
2028 def test_wg_multi_peer(self):
2029 """multiple peer setup"""
2033 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2034 wg1 = VppWgInterface(self, self.pg2.local_ip4, port + 1).add_vpp_config()
2038 # Check peer counter
2039 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 0)
2041 self.pg_enable_capture(self.pg_interfaces)
2044 # Create many peers on sencond interface
2046 self.pg2.generate_remote_hosts(NUM_PEERS)
2047 self.pg2.configure_ipv4_neighbors()
2048 self.pg1.generate_remote_hosts(NUM_PEERS)
2049 self.pg1.configure_ipv4_neighbors()
2055 for i in range(NUM_PEERS):
2060 self.pg1.remote_hosts[i].ip4,
2062 ["10.0.%d.4/32" % i],
2070 [VppRoutePath(self.pg1.remote_hosts[i].ip4, wg0.sw_if_index)],
2078 self.pg2.remote_hosts[i].ip4,
2080 ["10.100.%d.4/32" % i],
2088 [VppRoutePath(self.pg2.remote_hosts[i].ip4, wg1.sw_if_index)],
2092 self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_PEERS * 2)
2094 self.logger.info(self.vapi.cli("show wireguard peer"))
2095 self.logger.info(self.vapi.cli("show wireguard interface"))
2096 self.logger.info(self.vapi.cli("show adj 37"))
2097 self.logger.info(self.vapi.cli("sh ip fib 172.16.3.17"))
2098 self.logger.info(self.vapi.cli("sh ip fib 10.11.3.0"))
2102 r.remove_vpp_config()
2104 r.remove_vpp_config()
2108 self.assertTrue(p.query_vpp_config())
2109 p.remove_vpp_config()
2111 self.assertTrue(p.query_vpp_config())
2112 p.remove_vpp_config()
2114 wg0.remove_vpp_config()
2115 wg1.remove_vpp_config()
2117 def test_wg_multi_interface(self):
2118 """Multi-tunnel on the same port"""
2121 # Create many wireguard interfaces
2123 self.pg1.generate_remote_hosts(NUM_IFS)
2124 self.pg1.configure_ipv4_neighbors()
2125 self.pg0.generate_remote_hosts(NUM_IFS)
2126 self.pg0.configure_ipv4_neighbors()
2128 self.pg_enable_capture(self.pg_interfaces)
2131 # Create interfaces with a peer on each
2135 for i in range(NUM_IFS):
2136 # Use the same port for each interface
2137 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2145 self.pg1.remote_hosts[i].ip4,
2147 ["10.0.%d.0/24" % i],
2156 [VppRoutePath("10.0.%d.4" % i, wg0.sw_if_index)],
2160 self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_IFS)
2162 # skip the first automatic handshake
2163 self.pg1.get_capture(NUM_IFS, timeout=HANDSHAKE_JITTER)
2165 for i in range(NUM_IFS):
2166 # send a valid handsake init for which we expect a response
2167 p = peers[i].mk_handshake(self.pg1)
2168 rx = self.send_and_expect(self.pg1, [p], self.pg1)
2169 peers[i].consume_response(rx[0])
2171 # send a data packet from the peer through the tunnel
2172 # this completes the handshake
2174 IP(src="10.0.%d.4" % i, dst=self.pg0.remote_hosts[i].ip4, ttl=20)
2175 / UDP(sport=222, dport=223)
2178 d = peers[i].encrypt_transport(p)
2179 p = peers[i].mk_tunnel_header(self.pg1) / (
2180 Wireguard(message_type=4, reserved_zero=0)
2181 / WireguardTransport(
2182 receiver_index=peers[i].sender,
2184 encrypted_encapsulated_packet=d,
2187 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
2189 self.assertEqual(rx[IP].dst, self.pg0.remote_hosts[i].ip4)
2190 self.assertEqual(rx[IP].ttl, 19)
2192 # send a packets that are routed into the tunnel
2193 for i in range(NUM_IFS):
2195 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2196 / IP(src=self.pg0.remote_hosts[i].ip4, dst="10.0.%d.4" % i)
2197 / UDP(sport=555, dport=556)
2201 rxs = self.send_and_expect(self.pg0, p * 64, self.pg1)
2204 rx = IP(peers[i].decrypt_transport(rx))
2206 # check the oringial packet is present
2207 self.assertEqual(rx[IP].dst, p[IP].dst)
2208 self.assertEqual(rx[IP].ttl, p[IP].ttl - 1)
2210 # send packets into the tunnel
2211 for i in range(NUM_IFS):
2214 peers[i].mk_tunnel_header(self.pg1)
2215 / Wireguard(message_type=4, reserved_zero=0)
2216 / WireguardTransport(
2217 receiver_index=peers[i].sender,
2219 encrypted_encapsulated_packet=peers[i].encrypt_transport(
2222 src="10.0.%d.4" % i,
2223 dst=self.pg0.remote_hosts[i].ip4,
2226 / UDP(sport=222, dport=223)
2235 rxs = self.send_and_expect(self.pg1, p, self.pg0)
2238 self.assertEqual(rx[IP].dst, self.pg0.remote_hosts[i].ip4)
2239 self.assertEqual(rx[IP].ttl, 19)
2242 r.remove_vpp_config()
2244 p.remove_vpp_config()
2246 i.remove_vpp_config()
2248 def test_wg_event(self):
2251 ESTABLISHED_FLAG = (
2252 VppEnum.vl_api_wireguard_peer_flags_t.WIREGUARD_PEER_ESTABLISHED
2254 DEAD_FLAG = VppEnum.vl_api_wireguard_peer_flags_t.WIREGUARD_PEER_STATUS_DEAD
2257 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2258 wg1 = VppWgInterface(self, self.pg2.local_ip4, port + 1).add_vpp_config()
2262 # Check peer counter
2263 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 0)
2265 self.pg_enable_capture(self.pg_interfaces)
2270 self.pg2.generate_remote_hosts(NUM_PEERS)
2271 self.pg2.configure_ipv4_neighbors()
2272 self.pg1.generate_remote_hosts(NUM_PEERS)
2273 self.pg1.configure_ipv4_neighbors()
2279 for i in range(NUM_PEERS):
2284 self.pg1.remote_hosts[i].ip4,
2286 ["10.0.%d.4/32" % i],
2294 [VppRoutePath(self.pg1.remote_hosts[i].ip4, wg0.sw_if_index)],
2302 self.pg2.remote_hosts[i].ip4,
2304 ["10.100.%d.4/32" % i],
2312 [VppRoutePath(self.pg2.remote_hosts[i].ip4, wg1.sw_if_index)],
2316 self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_PEERS * 2)
2318 # skip the first automatic handshake
2319 self.pg1.get_capture(NUM_PEERS, timeout=HANDSHAKE_JITTER)
2320 self.pg2.get_capture(NUM_PEERS, timeout=HANDSHAKE_JITTER)
2322 # Want events from the first perr of wg0
2323 # and from all wg1 peers
2324 peers_0[0].want_events()
2327 for i in range(NUM_PEERS):
2328 # wg0 peers: send a valid handsake init for which we expect a response
2329 p = peers_0[i].mk_handshake(self.pg1)
2330 rx = self.send_and_expect(self.pg1, [p], self.pg1)
2331 peers_0[i].consume_response(rx[0])
2333 # wg0 peers: send empty packet, it means successful connection (WIREGUARD_PEER_ESTABLISHED)
2334 keepalive = peers_0[i].encrypt_transport(0)
2335 p = peers_0[i].mk_tunnel_header(self.pg1) / (
2336 Wireguard(message_type=4, reserved_zero=0)
2337 / WireguardTransport(
2338 receiver_index=peers_0[i].sender,
2340 encrypted_encapsulated_packet=keepalive,
2343 self.send_and_assert_no_replies(self.pg1, [p])
2345 # wg0 peers: wait for established flag
2347 peers_0[0].wait_event(ESTABLISHED_FLAG)
2349 # wg1 peers: send a valid handsake init for which we expect a response
2350 p = peers_1[i].mk_handshake(self.pg2)
2351 rx = self.send_and_expect(self.pg2, [p], self.pg2)
2352 peers_1[i].consume_response(rx[0])
2354 # wg1 peers: send empty packet, it means successful connection (WIREGUARD_PEER_ESTABLISHED)
2355 keepalive = peers_1[i].encrypt_transport(0)
2356 p = peers_1[i].mk_tunnel_header(self.pg2) / (
2357 Wireguard(message_type=4, reserved_zero=0)
2358 / WireguardTransport(
2359 receiver_index=peers_1[i].sender,
2361 encrypted_encapsulated_packet=keepalive,
2364 self.send_and_assert_no_replies(self.pg2, [p])
2366 # wg1 peers: wait for established flag
2367 wg1.wait_events(ESTABLISHED_FLAG, [peers_1[0].index, peers_1[1].index])
2371 r.remove_vpp_config()
2373 r.remove_vpp_config()
2376 for i in range(NUM_PEERS):
2377 self.assertTrue(peers_0[i].query_vpp_config())
2378 peers_0[i].remove_vpp_config()
2380 peers_0[i].wait_event(0)
2381 peers_0[i].wait_event(DEAD_FLAG)
2383 self.assertTrue(p.query_vpp_config())
2384 p.remove_vpp_config()
2386 p.wait_event(DEAD_FLAG)
2388 wg0.remove_vpp_config()
2389 wg1.remove_vpp_config()
2391 def test_wg_sending_handshake_when_admin_down(self):
2392 """Sending handshake when admin down"""
2395 # create wg interface
2396 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2401 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
2403 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2405 self.pg_enable_capture(self.pg_interfaces)
2408 # wait for the peer to send a handshake initiation
2409 # expect no handshakes
2411 self.pg1.assert_nothing_captured(remark="handshake packet(s) sent")
2413 self.pg_enable_capture(self.pg_interfaces)
2416 # administratively enable the wg interface
2417 # expect the peer to send a handshake initiation
2419 rxs = self.pg1.get_capture(1, timeout=2)
2420 peer_1.consume_init(rxs[0], self.pg1)
2422 self.pg_enable_capture(self.pg_interfaces)
2425 # administratively disable the wg interface
2426 # expect no handshakes
2429 self.pg1.assert_nothing_captured(remark="handshake packet(s) sent")
2432 peer_1.remove_vpp_config()
2433 wg0.remove_vpp_config()
2435 def test_wg_sending_data_when_admin_down(self):
2436 """Sending data when admin down"""
2439 # create wg interface
2440 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2444 self.pg_enable_capture(self.pg_interfaces)
2449 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
2451 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2453 # create a route to rewrite traffic into the wg interface
2455 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
2458 # wait for the peer to send a handshake initiation
2459 rxs = self.pg1.get_capture(1, timeout=2)
2461 # prepare and send a handshake response
2462 # expect a keepalive message
2463 resp = peer_1.consume_init(rxs[0], self.pg1)
2464 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
2466 # verify the keepalive message
2467 b = peer_1.decrypt_transport(rxs[0])
2468 self.assertEqual(0, len(b))
2470 # prepare and send a packet that will be rewritten into the wg interface
2471 # expect a data packet sent
2473 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2474 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
2475 / UDP(sport=555, dport=556)
2478 rxs = self.send_and_expect(self.pg0, [p], self.pg1)
2480 # verify the data packet
2481 peer_1.validate_encapped(rxs, p)
2483 # administratively disable the wg interface
2486 # send a packet that will be rewritten into the wg interface
2487 # expect no data packets sent
2488 self.send_and_assert_no_replies(self.pg0, [p])
2490 # administratively enable the wg interface
2491 # expect the peer to send a handshake initiation
2493 peer_1.noise_reset()
2494 rxs = self.pg1.get_capture(1, timeout=2)
2495 resp = peer_1.consume_init(rxs[0], self.pg1)
2497 # send a packet that will be rewritten into the wg interface
2498 # expect no data packets sent because the peer is not initiated
2499 self.send_and_assert_no_replies(self.pg0, [p])
2501 self.base_kp4_err + 1, self.statistics.get_err_counter(self.kp4_error)
2504 # send a handshake response and expect a keepalive message
2505 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
2507 # verify the keepalive message
2508 b = peer_1.decrypt_transport(rxs[0])
2509 self.assertEqual(0, len(b))
2511 # send a packet that will be rewritten into the wg interface
2512 # expect a data packet sent
2513 rxs = self.send_and_expect(self.pg0, [p], self.pg1)
2515 # verify the data packet
2516 peer_1.validate_encapped(rxs, p)
2519 r1.remove_vpp_config()
2520 peer_1.remove_vpp_config()
2521 wg0.remove_vpp_config()
2523 def _test_wg_large_packet_tmpl(self, is_async, is_ip6):
2524 self.vapi.wg_set_async_mode(is_async)
2527 # create wg interface
2529 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
2533 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2537 self.pg_enable_capture(self.pg_interfaces)
2543 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
2547 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
2549 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2551 # create a route to rewrite traffic into the wg interface
2554 self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
2558 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
2561 # wait for the peer to send a handshake initiation
2562 rxs = self.pg1.get_capture(1, timeout=2)
2564 # prepare and send a handshake response
2565 # expect a keepalive message
2566 resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6)
2567 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
2569 # verify the keepalive message
2570 b = peer_1.decrypt_transport(rxs[0], is_ip6=is_ip6)
2571 self.assertEqual(0, len(b))
2573 # prepare and send data packets
2574 # expect to receive them decrypted
2576 ip_header = IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
2578 ip_header = IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
2582 4500, # three buffers
2583 1910 if is_ip6 else 1950, # auth tag is not contiguous
2586 for l in packet_len_opts:
2588 peer_1.mk_tunnel_header(self.pg1, is_ip6=is_ip6)
2589 / Wireguard(message_type=4, reserved_zero=0)
2590 / WireguardTransport(
2591 receiver_index=peer_1.sender,
2593 encrypted_encapsulated_packet=peer_1.encrypt_transport(
2594 ip_header / UDP(sport=222, dport=223) / Raw(b"\xfe" * l)
2598 rxs = self.send_and_expect(self.pg1, txs, self.pg0)
2600 # verify decrypted packets
2601 for i, l in enumerate(packet_len_opts):
2603 self.assertEqual(rxs[i][IPv6].dst, self.pg0.remote_ip6)
2604 self.assertEqual(rxs[i][IPv6].hlim, ip_header.hlim - 1)
2606 self.assertEqual(rxs[i][IP].dst, self.pg0.remote_ip4)
2607 self.assertEqual(rxs[i][IP].ttl, ip_header.ttl - 1)
2608 self.assertEqual(len(rxs[i][Raw]), l)
2609 self.assertEqual(bytes(rxs[i][Raw]), b"\xfe" * l)
2611 # prepare and send packets that will be rewritten into the wg interface
2612 # expect data packets sent
2614 ip_header = IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
2616 ip_header = IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
2620 4500, # three buffers
2621 1980 if is_ip6 else 2000, # no free space to write auth tag
2624 for l in packet_len_opts:
2626 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2628 / UDP(sport=555, dport=556)
2631 rxs = self.send_and_expect(self.pg0, txs, self.pg1)
2633 # verify the data packets
2634 rxs_decrypted = peer_1.validate_encapped(
2635 rxs, ip_header, is_tunnel_ip6=is_ip6, is_transport_ip6=is_ip6
2638 for i, l in enumerate(packet_len_opts):
2639 self.assertEqual(len(rxs_decrypted[i][Raw]), l)
2640 self.assertEqual(bytes(rxs_decrypted[i][Raw]), b"\xfe" * l)
2643 r1.remove_vpp_config()
2644 peer_1.remove_vpp_config()
2645 wg0.remove_vpp_config()
2647 def test_wg_large_packet_v4_sync(self):
2648 """Large packet (v4, sync)"""
2649 self._test_wg_large_packet_tmpl(is_async=False, is_ip6=False)
2651 def test_wg_large_packet_v6_sync(self):
2652 """Large packet (v6, sync)"""
2653 self._test_wg_large_packet_tmpl(is_async=False, is_ip6=True)
2655 def test_wg_large_packet_v4_async(self):
2656 """Large packet (v4, async)"""
2657 self._test_wg_large_packet_tmpl(is_async=True, is_ip6=False)
2659 def test_wg_large_packet_v6_async(self):
2660 """Large packet (v6, async)"""
2661 self._test_wg_large_packet_tmpl(is_async=True, is_ip6=True)
2663 def test_wg_lack_of_buf_headroom(self):
2664 """Lack of buffer's headroom (v6 vxlan over v6 wg)"""
2667 # create wg interface
2668 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
2672 self.pg_enable_capture(self.pg_interfaces)
2677 self, wg0, self.pg1.remote_ip6, port + 1, ["::/0"]
2679 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2681 # create a route to enable communication between wg interface addresses
2683 self, wg0.remote_ip6, 128, [VppRoutePath("0.0.0.0", wg0.sw_if_index)]
2686 # wait for the peer to send a handshake initiation
2687 rxs = self.pg1.get_capture(1, timeout=2)
2689 # prepare and send a handshake response
2690 # expect a keepalive message
2691 resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=True)
2692 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
2694 # verify the keepalive message
2695 b = peer_1.decrypt_transport(rxs[0], is_ip6=True)
2696 self.assertEqual(0, len(b))
2698 # create vxlan interface over the wg interface
2699 vxlan0 = VppVxlanTunnel(self, src=wg0.local_ip6, dst=wg0.remote_ip6, vni=1111)
2700 vxlan0.add_vpp_config()
2702 # create bridge domain
2703 bd1 = VppBridgeDomain(self, bd_id=1)
2704 bd1.add_vpp_config()
2706 # add the vxlan interface and pg0 to the bridge domain
2708 VppBridgeDomainPort(self, bd1, vxlan0).add_vpp_config(),
2709 VppBridgeDomainPort(self, bd1, self.pg0).add_vpp_config(),
2712 # prepare and send packets that will be rewritten into the vxlan interface
2713 # expect they to be rewritten into the wg interface then and data packets sent
2715 Ether(dst="00:00:00:00:00:01", src="00:00:00:00:00:02")
2716 / IPv6(src="::1", dst="::2", hlim=20)
2717 / UDP(sport=1111, dport=1112)
2718 / Raw(b"\xfe" * 1900)
2720 rxs = self.send_and_expect(self.pg0, [tx] * 5, self.pg1)
2722 # verify the data packet
2724 rx_decrypted = IPv6(peer_1.decrypt_transport(rx, is_ip6=True))
2726 self.assertEqual(rx_decrypted[VXLAN].vni, vxlan0.vni)
2727 inner = rx_decrypted[VXLAN].payload
2729 # check the original packet is present
2730 self.assertEqual(inner[IPv6].dst, tx[IPv6].dst)
2731 self.assertEqual(inner[IPv6].hlim, tx[IPv6].hlim)
2732 self.assertEqual(len(inner[Raw]), len(tx[Raw]))
2733 self.assertEqual(bytes(inner[Raw]), bytes(tx[Raw]))
2736 for bdp in bd1_ports:
2737 bdp.remove_vpp_config()
2738 bd1.remove_vpp_config()
2739 vxlan0.remove_vpp_config()
2740 r1.remove_vpp_config()
2741 peer_1.remove_vpp_config()
2742 wg0.remove_vpp_config()
2745 @tag_fixme_vpp_debug
2746 class WireguardHandoffTests(TestWg):
2747 """Wireguard Tests in multi worker setup"""
2749 vpp_worker_count = 2
2751 def test_wg_peer_init(self):
2757 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2761 self.pg_enable_capture(self.pg_interfaces)
2765 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.2.0/24", "10.11.3.0/24"]
2767 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2770 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
2773 # skip the first automatic handshake
2774 self.pg1.get_capture(1, timeout=HANDSHAKE_JITTER)
2776 # send a valid handsake init for which we expect a response
2777 p = peer_1.mk_handshake(self.pg1)
2779 rx = self.send_and_expect(self.pg1, [p], self.pg1)
2781 peer_1.consume_response(rx[0])
2783 # send a data packet from the peer through the tunnel
2784 # this completes the handshake and pins the peer to worker 0
2786 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
2787 / UDP(sport=222, dport=223)
2790 d = peer_1.encrypt_transport(p)
2791 p = peer_1.mk_tunnel_header(self.pg1) / (
2792 Wireguard(message_type=4, reserved_zero=0)
2793 / WireguardTransport(
2794 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
2797 rxs = self.send_and_expect(self.pg1, [p], self.pg0, worker=0)
2800 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
2801 self.assertEqual(rx[IP].ttl, 19)
2803 # send a packets that are routed into the tunnel
2804 # and pins the peer tp worker 1
2806 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2807 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
2808 / UDP(sport=555, dport=556)
2811 rxs = self.send_and_expect(self.pg0, pe * 255, self.pg1, worker=1)
2812 peer_1.validate_encapped(rxs, pe)
2814 # send packets into the tunnel, from the other worker
2817 peer_1.mk_tunnel_header(self.pg1)
2818 / Wireguard(message_type=4, reserved_zero=0)
2819 / WireguardTransport(
2820 receiver_index=peer_1.sender,
2822 encrypted_encapsulated_packet=peer_1.encrypt_transport(
2824 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
2825 / UDP(sport=222, dport=223)
2831 for ii in range(255)
2834 rxs = self.send_and_expect(self.pg1, p, self.pg0, worker=1)
2837 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
2838 self.assertEqual(rx[IP].ttl, 19)
2840 # send a packets that are routed into the tunnel
2842 rxs = self.send_and_expect(self.pg0, pe * 255, self.pg1, worker=0)
2844 peer_1.validate_encapped(rxs, pe)
2846 r1.remove_vpp_config()
2847 peer_1.remove_vpp_config()
2848 wg0.remove_vpp_config()
2850 @unittest.skip("test disabled")
2851 def test_wg_multi_interface(self):
2852 """Multi-tunnel on the same port"""
2856 "wireguard" in config.excluded_plugins, "Exclude Wireguard plugin tests"
2858 class TestWgFIB(VppTestCase):
2859 """Wireguard FIB Test Case"""
2862 def setUpClass(cls):
2863 super(TestWgFIB, cls).setUpClass()
2866 def tearDownClass(cls):
2867 super(TestWgFIB, cls).tearDownClass()
2870 super(TestWgFIB, self).setUp()
2872 self.create_pg_interfaces(range(2))
2874 for i in self.pg_interfaces:
2879 for i in self.pg_interfaces:
2882 super(TestWgFIB, self).tearDown()
2884 def test_wg_fib_tracking(self):
2888 # create wg interface
2889 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2893 self.pg_enable_capture(self.pg_interfaces)
2898 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
2900 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2902 # create a route to rewrite traffic into the wg interface
2904 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
2907 # resolve ARP and expect the adjacency to update
2908 self.pg1.resolve_arp()
2910 # wait for the peer to send a handshake initiation
2911 rxs = self.pg1.get_capture(2, timeout=6)
2913 # prepare and send a handshake response
2914 # expect a keepalive message
2915 resp = peer_1.consume_init(rxs[1], self.pg1)
2916 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
2918 # verify the keepalive message
2919 b = peer_1.decrypt_transport(rxs[0])
2920 self.assertEqual(0, len(b))
2922 # prepare and send a packet that will be rewritten into the wg interface
2923 # expect a data packet sent
2925 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2926 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
2927 / UDP(sport=555, dport=556)
2930 rxs = self.send_and_expect(self.pg0, [p], self.pg1)
2932 # verify the data packet
2933 peer_1.validate_encapped(rxs, p)
2936 r1.remove_vpp_config()
2937 peer_1.remove_vpp_config()
2938 wg0.remove_vpp_config()