8 from hashlib import blake2s
9 from config import config
10 from scapy.packet import Raw
11 from scapy.layers.l2 import Ether
12 from scapy.layers.inet import IP, UDP
13 from scapy.layers.inet6 import IPv6
14 from scapy.layers.vxlan import VXLAN
15 from scapy.contrib.wireguard import (
22 from cryptography.hazmat.primitives.asymmetric.x25519 import (
26 from cryptography.hazmat.primitives.serialization import (
32 from noise.connection import NoiseConnection, Keypair
34 from Crypto.Cipher import ChaCha20_Poly1305
35 from Crypto.Random import get_random_bytes
37 from vpp_interface import VppInterface
38 from vpp_pg_interface import is_ipv6_misc
39 from vpp_ip_route import VppIpRoute, VppRoutePath
40 from vpp_l2 import VppBridgeDomain, VppBridgeDomainPort
41 from vpp_vxlan_tunnel import VppVxlanTunnel
42 from vpp_object import VppObject
43 from vpp_papi import VppEnum
44 from asfframework import tag_run_solo, tag_fixme_vpp_debug
45 from framework import VppTestCase
46 from re import compile
49 """ TestWg is a subclass of VPPTestCase classes.
56 def private_key_bytes(k):
57 return k.private_bytes(Encoding.Raw, PrivateFormat.Raw, NoEncryption())
60 def public_key_bytes(k):
61 return k.public_bytes(Encoding.Raw, PublicFormat.Raw)
64 def get_field_bytes(pkt, name):
65 fld, val = pkt.getfield_and_val(name)
66 return fld.i2m(pkt, val)
69 class VppWgInterface(VppInterface):
71 VPP WireGuard interface
74 def __init__(self, test, src, port):
75 super(VppWgInterface, self).__init__(test)
79 self.private_key = X25519PrivateKey.generate()
80 self.public_key = self.private_key.public_key()
82 # cookie related params
83 self.cookie_key = blake2s(b"cookie--" + self.public_key_bytes()).digest()
85 def public_key_bytes(self):
86 return public_key_bytes(self.public_key)
88 def private_key_bytes(self):
89 return private_key_bytes(self.private_key)
91 def add_vpp_config(self):
92 r = self.test.vapi.wireguard_interface_create(
94 "user_instance": 0xFFFFFFFF,
97 "private_key": private_key_bytes(self.private_key),
98 "generate_key": False,
101 self.set_sw_if_index(r.sw_if_index)
102 self.test.registry.register(self, self.test.logger)
105 def remove_vpp_config(self):
106 self.test.vapi.wireguard_interface_delete(sw_if_index=self._sw_if_index)
108 def query_vpp_config(self):
109 ts = self.test.vapi.wireguard_interface_dump(sw_if_index=0xFFFFFFFF)
112 t.interface.sw_if_index == self._sw_if_index
113 and str(t.interface.src_ip) == self.src
114 and t.interface.port == self.port
115 and t.interface.private_key == private_key_bytes(self.private_key)
120 def want_events(self, peer_index=0xFFFFFFFF):
121 self.test.vapi.want_wireguard_peer_events(
124 sw_if_index=self._sw_if_index,
125 peer_index=peer_index,
128 def wait_events(self, expect, peers, timeout=5):
129 for i in range(len(peers)):
130 rv = self.test.vapi.wait_for_event(timeout, "wireguard_peer_event")
131 self.test.assertEqual(rv.peer_index, peers[i])
132 self.test.assertEqual(rv.flags, expect)
135 return self.object_id()
138 return "wireguard-%d" % self._sw_if_index
141 NOISE_HANDSHAKE_NAME = b"Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s"
142 NOISE_IDENTIFIER_NAME = b"WireGuard v1 zx2c4 Jason@zx2c4.com"
144 HANDSHAKE_COUNTING_INTERVAL = 0.5
145 UNDER_LOAD_INTERVAL = 1.0
146 HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD = 40
147 HANDSHAKE_NUM_BEFORE_RATELIMITING = 5
149 HANDSHAKE_JITTER = 0.5
152 class VppWgPeer(VppObject):
153 def __init__(self, test, itf, endpoint, port, allowed_ips, persistent_keepalive=15):
156 self.endpoint = endpoint
158 self.allowed_ips = allowed_ips
159 self.persistent_keepalive = persistent_keepalive
161 # remote peer's public
162 self.private_key = X25519PrivateKey.generate()
163 self.public_key = self.private_key.public_key()
165 # cookie related params
166 self.cookie_key = blake2s(b"cookie--" + self.public_key_bytes()).digest()
167 self.last_sent_cookie = None
168 self.last_mac1 = None
169 self.last_received_cookie = None
171 self.noise = NoiseConnection.from_name(NOISE_HANDSHAKE_NAME)
173 def change_endpoint(self, endpoint, port):
174 self.endpoint = endpoint
177 def add_vpp_config(self):
178 rv = self._test.vapi.wireguard_peer_add(
180 "public_key": self.public_key_bytes(),
182 "endpoint": self.endpoint,
183 "n_allowed_ips": len(self.allowed_ips),
184 "allowed_ips": self.allowed_ips,
185 "sw_if_index": self.itf.sw_if_index,
186 "persistent_keepalive": self.persistent_keepalive,
189 self.index = rv.peer_index
190 self.receiver_index = self.index + 1
191 self._test.registry.register(self, self._test.logger)
194 def remove_vpp_config(self):
195 self._test.vapi.wireguard_peer_remove(peer_index=self.index)
198 return "wireguard-peer-%s" % self.index
200 def public_key_bytes(self):
201 return public_key_bytes(self.public_key)
203 def query_vpp_config(self):
204 peers = self._test.vapi.wireguard_peers_dump()
207 # "::" endpoint will be returned as "0.0.0.0" in peer's details
208 endpoint = "0.0.0.0" if self.endpoint == "::" else self.endpoint
210 p.peer.public_key == self.public_key_bytes()
211 and p.peer.port == self.port
212 and str(p.peer.endpoint) == endpoint
213 and p.peer.sw_if_index == self.itf.sw_if_index
214 and len(self.allowed_ips) == p.peer.n_allowed_ips
216 self.allowed_ips.sort()
217 p.peer.allowed_ips.sort()
219 for a1, a2 in zip(self.allowed_ips, p.peer.allowed_ips):
220 if str(a1) != str(a2):
225 def mk_tunnel_header(self, tx_itf, is_ip6=False):
228 Ether(dst=tx_itf.local_mac, src=tx_itf.remote_mac)
229 / IP(src=self.endpoint, dst=self.itf.src)
230 / UDP(sport=self.port, dport=self.itf.port)
234 Ether(dst=tx_itf.local_mac, src=tx_itf.remote_mac)
235 / IPv6(src=self.endpoint, dst=self.itf.src)
236 / UDP(sport=self.port, dport=self.itf.port)
239 def noise_reset(self):
240 self.noise = NoiseConnection.from_name(NOISE_HANDSHAKE_NAME)
242 def noise_init(self, public_key=None):
243 self.noise.set_prologue(NOISE_IDENTIFIER_NAME)
244 self.noise.set_psks(psk=bytes(bytearray(32)))
247 public_key = self.itf.public_key
250 self.noise.set_keypair_from_private_bytes(
251 Keypair.STATIC, private_key_bytes(self.private_key)
254 self.noise.set_keypair_from_public_bytes(
255 Keypair.REMOTE_STATIC, public_key_bytes(public_key)
258 self.noise.start_handshake()
260 def mk_cookie(self, p, tx_itf, is_resp=False, is_ip6=False):
261 self.verify_header(p, is_ip6)
263 wg_pkt = Wireguard(p[Raw])
266 self._test.assertEqual(wg_pkt[Wireguard].message_type, 2)
267 self._test.assertEqual(wg_pkt[Wireguard].reserved_zero, 0)
268 self._test.assertEqual(wg_pkt[WireguardResponse].mac2, bytes([0] * 16))
270 self._test.assertEqual(wg_pkt[Wireguard].message_type, 1)
271 self._test.assertEqual(wg_pkt[Wireguard].reserved_zero, 0)
272 self._test.assertEqual(wg_pkt[WireguardInitiation].mac2, bytes([0] * 16))
274 # collect info from wg packet (initiation or response)
275 src = get_field_bytes(p[IPv6 if is_ip6 else IP], "src")
276 sport = p[UDP].sport.to_bytes(2, byteorder="big")
278 mac1 = wg_pkt[WireguardResponse].mac1
279 sender_index = wg_pkt[WireguardResponse].sender_index
281 mac1 = wg_pkt[WireguardInitiation].mac1
282 sender_index = wg_pkt[WireguardInitiation].sender_index
285 cookie_reply = Wireguard() / WireguardCookieReply()
286 cookie_reply[Wireguard].message_type = 3
287 cookie_reply[Wireguard].reserved_zero = 0
288 cookie_reply[WireguardCookieReply].receiver_index = sender_index
289 nonce = get_random_bytes(24)
290 cookie_reply[WireguardCookieReply].nonce = nonce
292 # generate cookie data
293 changing_secret = get_random_bytes(32)
294 self.last_sent_cookie = blake2s(
295 src + sport, digest_size=16, key=changing_secret
298 # encrypt cookie data
299 cipher = ChaCha20_Poly1305.new(key=self.cookie_key, nonce=nonce)
301 ciphertext, tag = cipher.encrypt_and_digest(self.last_sent_cookie)
302 cookie_reply[WireguardCookieReply].encrypted_cookie = ciphertext + tag
304 # prepare cookie reply to be sent
305 cookie_reply = self.mk_tunnel_header(tx_itf, is_ip6) / cookie_reply
309 def consume_cookie(self, p, is_ip6=False):
310 self.verify_header(p, is_ip6)
312 cookie_reply = Wireguard(p[Raw])
314 self._test.assertEqual(cookie_reply[Wireguard].message_type, 3)
315 self._test.assertEqual(cookie_reply[Wireguard].reserved_zero, 0)
316 self._test.assertEqual(
317 cookie_reply[WireguardCookieReply].receiver_index, self.receiver_index
320 # collect info from cookie reply
321 nonce = cookie_reply[WireguardCookieReply].nonce
322 encrypted_cookie = cookie_reply[WireguardCookieReply].encrypted_cookie
323 ciphertext, tag = encrypted_cookie[:16], encrypted_cookie[16:]
325 # decrypt cookie data
326 cipher = ChaCha20_Poly1305.new(key=self.itf.cookie_key, nonce=nonce)
327 cipher.update(self.last_mac1)
328 self.last_received_cookie = cipher.decrypt_and_verify(ciphertext, tag)
330 def mk_handshake(self, tx_itf, is_ip6=False, public_key=None):
331 self.noise.set_as_initiator()
332 self.noise_init(public_key)
334 p = Wireguard() / WireguardInitiation()
336 p[Wireguard].message_type = 1
337 p[Wireguard].reserved_zero = 0
338 p[WireguardInitiation].sender_index = self.receiver_index
340 # some random data for the message
341 # lifted from the noise protocol's wireguard example
342 now = datetime.datetime.now()
345 4611686018427387914 + int(now.timestamp()),
346 int(now.microsecond * 1e3),
348 b = self.noise.write_message(payload=tai)
350 # load noise into init message
351 p[WireguardInitiation].unencrypted_ephemeral = b[0:32]
352 p[WireguardInitiation].encrypted_static = b[32:80]
353 p[WireguardInitiation].encrypted_timestamp = b[80:108]
355 # generate the mac1 hash
356 mac_key = blake2s(b"mac1----" + self.itf.public_key_bytes()).digest()
357 mac1 = blake2s(bytes(p)[0:116], digest_size=16, key=mac_key).digest()
358 p[WireguardInitiation].mac1 = mac1
359 self.last_mac1 = mac1
361 # generate the mac2 hash
362 if self.last_received_cookie:
364 bytes(p)[0:132], digest_size=16, key=self.last_received_cookie
366 p[WireguardInitiation].mac2 = mac2
367 self.last_received_cookie = None
369 p[WireguardInitiation].mac2 = bytearray(16)
371 p = self.mk_tunnel_header(tx_itf, is_ip6) / p
375 def verify_header(self, p, is_ip6=False):
377 self._test.assertEqual(p[IP].src, self.itf.src)
378 self._test.assertEqual(p[IP].dst, self.endpoint)
379 self._test.assert_packet_checksums_valid(p)
381 self._test.assertEqual(p[IPv6].src, self.itf.src)
382 self._test.assertEqual(p[IPv6].dst, self.endpoint)
383 self._test.assert_packet_checksums_valid(p, False)
384 self._test.assertEqual(p[UDP].sport, self.itf.port)
385 self._test.assertEqual(p[UDP].dport, self.port)
387 def consume_init(self, p, tx_itf, is_ip6=False, is_mac2=False):
388 self.noise.set_as_responder()
389 self.noise_init(self.itf.public_key)
390 self.verify_header(p, is_ip6)
392 init = Wireguard(p[Raw])
394 self._test.assertEqual(init[Wireguard].message_type, 1)
395 self._test.assertEqual(init[Wireguard].reserved_zero, 0)
397 self.sender = init[WireguardInitiation].sender_index
399 # validate the mac1 hash
400 mac_key = blake2s(b"mac1----" + public_key_bytes(self.public_key)).digest()
401 mac1 = blake2s(bytes(init)[0:-32], digest_size=16, key=mac_key).digest()
402 self._test.assertEqual(init[WireguardInitiation].mac1, mac1)
404 # validate the mac2 hash
406 self._test.assertNotEqual(init[WireguardInitiation].mac2, bytes([0] * 16))
407 self._test.assertNotEqual(self.last_sent_cookie, None)
409 bytes(init)[0:-16], digest_size=16, key=self.last_sent_cookie
411 self._test.assertEqual(init[WireguardInitiation].mac2, mac2)
412 self.last_sent_cookie = None
414 self._test.assertEqual(init[WireguardInitiation].mac2, bytes([0] * 16))
416 # this passes only unencrypted_ephemeral, encrypted_static,
417 # encrypted_timestamp fields of the init
418 payload = self.noise.read_message(bytes(init)[8:-32])
421 b = self.noise.write_message()
422 mac_key = blake2s(b"mac1----" + public_key_bytes(self.itf.public_key)).digest()
423 resp = Wireguard(message_type=2, reserved_zero=0) / WireguardResponse(
424 sender_index=self.receiver_index,
425 receiver_index=self.sender,
426 unencrypted_ephemeral=b[0:32],
427 encrypted_nothing=b[32:],
429 mac1 = blake2s(bytes(resp)[:-32], digest_size=16, key=mac_key).digest()
430 resp[WireguardResponse].mac1 = mac1
431 self.last_mac1 = mac1
433 resp = self.mk_tunnel_header(tx_itf, is_ip6) / resp
434 self._test.assertTrue(self.noise.handshake_finished)
438 def consume_response(self, p, is_ip6=False):
439 self.verify_header(p, is_ip6)
441 resp = Wireguard(p[Raw])
443 self._test.assertEqual(resp[Wireguard].message_type, 2)
444 self._test.assertEqual(resp[Wireguard].reserved_zero, 0)
445 self._test.assertEqual(
446 resp[WireguardResponse].receiver_index, self.receiver_index
449 self.sender = resp[Wireguard].sender_index
451 payload = self.noise.read_message(bytes(resp)[12:60])
452 self._test.assertEqual(payload, b"")
453 self._test.assertTrue(self.noise.handshake_finished)
455 def decrypt_transport(self, p, is_ip6=False):
456 self.verify_header(p, is_ip6)
458 p = Wireguard(p[Raw])
459 self._test.assertEqual(p[Wireguard].message_type, 4)
460 self._test.assertEqual(p[Wireguard].reserved_zero, 0)
461 self._test.assertEqual(
462 p[WireguardTransport].receiver_index, self.receiver_index
465 d = self.noise.decrypt(p[WireguardTransport].encrypted_encapsulated_packet)
468 def encrypt_transport(self, p):
469 return self.noise.encrypt(bytes(p))
471 def validate_encapped(self, rxs, tx, is_tunnel_ip6=False, is_transport_ip6=False):
474 rx = self.decrypt_transport(rx, is_tunnel_ip6)
475 if is_transport_ip6 is False:
477 # check the original packet is present
478 self._test.assertEqual(rx[IP].dst, tx[IP].dst)
479 self._test.assertEqual(rx[IP].ttl, tx[IP].ttl - 1)
482 # check the original packet is present
483 self._test.assertEqual(rx[IPv6].dst, tx[IPv6].dst)
484 self._test.assertEqual(rx[IPv6].hlim, tx[IPv6].hlim - 1)
488 def want_events(self):
489 self._test.vapi.want_wireguard_peer_events(
492 peer_index=self.index,
493 sw_if_index=self.itf.sw_if_index,
496 def wait_event(self, expect, timeout=5):
497 rv = self._test.vapi.wait_for_event(timeout, "wireguard_peer_event")
498 self._test.assertEqual(rv.flags, expect)
499 self._test.assertEqual(rv.peer_index, self.index)
502 def is_handshake_init(p):
503 wg_p = Wireguard(p[Raw])
505 return wg_p[Wireguard].message_type == 1
509 "wireguard" in config.excluded_plugins, "Exclude Wireguard plugin tests"
512 class TestWg(VppTestCase):
513 """Wireguard Test Case"""
515 error_str = compile(r"Error")
517 wg4_output_node_name = "/err/wg4-output-tun/"
518 wg4_input_node_name = "/err/wg4-input/"
519 wg6_output_node_name = "/err/wg6-output-tun/"
520 wg6_input_node_name = "/err/wg6-input/"
521 kp4_error = wg4_output_node_name + "Keypair error"
522 mac4_error = wg4_input_node_name + "Invalid MAC handshake"
523 peer4_in_err = wg4_input_node_name + "Peer error"
524 peer4_out_err = wg4_output_node_name + "Peer error"
525 kp6_error = wg6_output_node_name + "Keypair error"
526 mac6_error = wg6_input_node_name + "Invalid MAC handshake"
527 peer6_in_err = wg6_input_node_name + "Peer error"
528 peer6_out_err = wg6_output_node_name + "Peer error"
529 cookie_dec4_err = wg4_input_node_name + "Failed during Cookie decryption"
530 cookie_dec6_err = wg6_input_node_name + "Failed during Cookie decryption"
531 ratelimited4_err = wg4_input_node_name + "Handshake ratelimited"
532 ratelimited6_err = wg6_input_node_name + "Handshake ratelimited"
536 super(TestWg, cls).setUpClass()
538 cls.create_pg_interfaces(range(3))
539 for i in cls.pg_interfaces:
547 super(TestWg, cls).tearDownClass()
551 def tearDownClass(cls):
552 super(TestWg, cls).tearDownClass()
555 super(VppTestCase, self).setUp()
556 self.base_kp4_err = self.statistics.get_err_counter(self.kp4_error)
557 self.base_mac4_err = self.statistics.get_err_counter(self.mac4_error)
558 self.base_peer4_in_err = self.statistics.get_err_counter(self.peer4_in_err)
559 self.base_peer4_out_err = self.statistics.get_err_counter(self.peer4_out_err)
560 self.base_kp6_err = self.statistics.get_err_counter(self.kp6_error)
561 self.base_mac6_err = self.statistics.get_err_counter(self.mac6_error)
562 self.base_peer6_in_err = self.statistics.get_err_counter(self.peer6_in_err)
563 self.base_peer6_out_err = self.statistics.get_err_counter(self.peer6_out_err)
564 self.base_cookie_dec4_err = self.statistics.get_err_counter(
567 self.base_cookie_dec6_err = self.statistics.get_err_counter(
570 self.base_ratelimited4_err = self.statistics.get_err_counter(
571 self.ratelimited4_err
573 self.base_ratelimited6_err = self.statistics.get_err_counter(
574 self.ratelimited6_err
577 def send_and_assert_no_replies_ignoring_init(
578 self, intf, pkts, remark="", timeout=None
580 self.pg_send(intf, pkts)
582 def _filter_out_fn(p):
583 return is_ipv6_misc(p) or is_handshake_init(p)
588 for i in self.pg_interfaces:
589 i.assert_nothing_captured(
590 timeout=timeout, remark=remark, filter_out_fn=_filter_out_fn
596 def test_wg_interface(self):
597 """Simple interface creation"""
601 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
603 self.logger.info(self.vapi.cli("sh int"))
606 wg0.remove_vpp_config()
608 def test_handshake_hash(self):
609 """test hashing an init message"""
610 # a init packet generated by linux given the key below
633 b = bytearray.fromhex(h)
636 pubb = base64.b64decode("aRuHFTTxICIQNefp05oKWlJv3zgKxb8+WW7JJMh0jyM=")
637 pub = X25519PublicKey.from_public_bytes(pubb)
639 self.assertEqual(pubb, public_key_bytes(pub))
641 # strip the macs and build a new packet
643 mac_key = blake2s(b"mac1----" + public_key_bytes(pub)).digest()
644 init += blake2s(init, digest_size=16, key=mac_key).digest()
647 act = Wireguard(init)
649 self.assertEqual(tgt, act)
651 def _test_wg_send_cookie_tmpl(self, is_resp, is_ip6):
654 # create wg interface
656 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
660 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
664 self.pg_enable_capture(self.pg_interfaces)
670 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
674 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
676 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
679 # skip the first automatic handshake
680 self.pg1.get_capture(1, timeout=HANDSHAKE_JITTER)
682 # prepare and send a handshake initiation
683 # expect the peer to send a handshake response
684 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
685 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
687 # wait for the peer to send a handshake initiation
688 rxs = self.pg1.get_capture(1, timeout=2)
690 # prepare and send a wrong cookie reply
691 # expect no replies and the cookie error incremented
692 cookie = peer_1.mk_cookie(rxs[0], self.pg1, is_resp=is_resp, is_ip6=is_ip6)
693 cookie.nonce = b"1234567890"
694 self.send_and_assert_no_replies(self.pg1, [cookie], timeout=0.1)
697 self.base_cookie_dec6_err + 1,
698 self.statistics.get_err_counter(self.cookie_dec6_err),
702 self.base_cookie_dec4_err + 1,
703 self.statistics.get_err_counter(self.cookie_dec4_err),
706 # prepare and send a correct cookie reply
707 cookie = peer_1.mk_cookie(rxs[0], self.pg1, is_resp=is_resp, is_ip6=is_ip6)
708 self.pg_send(self.pg1, [cookie])
710 # wait for the peer to send a handshake initiation with mac2 set
711 rxs = self.pg1.get_capture(1, timeout=6)
713 # verify the initiation and its mac2
714 peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6, is_mac2=True)
717 peer_1.remove_vpp_config()
718 wg0.remove_vpp_config()
720 def test_wg_send_cookie_on_init_v4(self):
721 """Send cookie on handshake initiation (v4)"""
722 self._test_wg_send_cookie_tmpl(is_resp=False, is_ip6=False)
724 def test_wg_send_cookie_on_init_v6(self):
725 """Send cookie on handshake initiation (v6)"""
726 self._test_wg_send_cookie_tmpl(is_resp=False, is_ip6=True)
728 def test_wg_send_cookie_on_resp_v4(self):
729 """Send cookie on handshake response (v4)"""
730 self._test_wg_send_cookie_tmpl(is_resp=True, is_ip6=False)
732 def test_wg_send_cookie_on_resp_v6(self):
733 """Send cookie on handshake response (v6)"""
734 self._test_wg_send_cookie_tmpl(is_resp=True, is_ip6=True)
736 def _test_wg_receive_cookie_tmpl(self, is_resp, is_ip6):
739 # create wg interface
741 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
745 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
749 self.pg_enable_capture(self.pg_interfaces)
755 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
759 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
761 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
764 # wait for the peer to send a handshake initiation
765 rxs = self.pg1.get_capture(1, timeout=2)
766 # prepare and send a bunch of handshake responses
767 # expect to switch to under load state
768 resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6)
769 txs = [resp] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
770 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
771 # reset noise to be able to turn into initiator later
774 # skip the first automatic handshake
775 self.pg1.get_capture(1, timeout=HANDSHAKE_JITTER)
777 # prepare and send a bunch of handshake initiations
778 # expect to switch to under load state
779 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
780 txs = [init] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
781 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
783 # expect the peer to send a cookie reply
784 peer_1.consume_cookie(rxs[-1], is_ip6=is_ip6)
786 # prepare and send a handshake initiation with wrong mac2
787 # expect a cookie reply
788 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
789 init.mac2 = b"1234567890"
790 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
791 peer_1.consume_cookie(rxs[0], is_ip6=is_ip6)
793 # prepare and send a handshake initiation with correct mac2
794 # expect a handshake response
795 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
796 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
798 # verify the response
799 peer_1.consume_response(rxs[0], is_ip6=is_ip6)
801 # clear up under load state
802 self.sleep(UNDER_LOAD_INTERVAL)
805 peer_1.remove_vpp_config()
806 wg0.remove_vpp_config()
808 def test_wg_receive_cookie_on_init_v4(self):
809 """Receive cookie on handshake initiation (v4)"""
810 self._test_wg_receive_cookie_tmpl(is_resp=False, is_ip6=False)
812 def test_wg_receive_cookie_on_init_v6(self):
813 """Receive cookie on handshake initiation (v6)"""
814 self._test_wg_receive_cookie_tmpl(is_resp=False, is_ip6=True)
816 def test_wg_receive_cookie_on_resp_v4(self):
817 """Receive cookie on handshake response (v4)"""
818 self._test_wg_receive_cookie_tmpl(is_resp=True, is_ip6=False)
820 def test_wg_receive_cookie_on_resp_v6(self):
821 """Receive cookie on handshake response (v6)"""
822 self._test_wg_receive_cookie_tmpl(is_resp=True, is_ip6=True)
824 def test_wg_under_load_interval(self):
825 """Under load interval"""
828 # create wg interface
829 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
833 self.pg_enable_capture(self.pg_interfaces)
838 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
840 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
842 # skip the first automatic handshake
843 self.pg1.get_capture(1, timeout=HANDSHAKE_JITTER)
845 # prepare and send a bunch of handshake initiations
846 # expect to switch to under load state
847 init = peer_1.mk_handshake(self.pg1)
848 txs = [init] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
849 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
851 # expect the peer to send a cookie reply
852 peer_1.consume_cookie(rxs[-1])
854 # sleep till the next counting interval
855 # expect under load state is still active
856 self.sleep(HANDSHAKE_COUNTING_INTERVAL)
858 # prepare and send a handshake initiation with wrong mac2
859 # expect a cookie reply
860 init = peer_1.mk_handshake(self.pg1)
861 init.mac2 = b"1234567890"
862 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
863 peer_1.consume_cookie(rxs[0])
865 # sleep till the end of being under load
866 # expect under load state is over
867 self.sleep(UNDER_LOAD_INTERVAL - HANDSHAKE_COUNTING_INTERVAL)
869 # prepare and send a handshake initiation with wrong mac2
870 # expect a handshake response
871 init = peer_1.mk_handshake(self.pg1)
872 init.mac2 = b"1234567890"
873 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
875 # verify the response
876 peer_1.consume_response(rxs[0])
879 peer_1.remove_vpp_config()
880 wg0.remove_vpp_config()
882 def _test_wg_handshake_ratelimiting_tmpl(self, is_ip6):
885 # create wg interface
887 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
891 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
895 self.pg_enable_capture(self.pg_interfaces)
901 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
905 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
907 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
909 # skip the first automatic handshake
910 self.pg1.get_capture(1, timeout=HANDSHAKE_JITTER)
912 # prepare and send a bunch of handshake initiations
913 # expect to switch to under load state
914 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
915 txs = [init] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
916 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
918 # expect the peer to send a cookie reply
919 peer_1.consume_cookie(rxs[-1], is_ip6=is_ip6)
921 # prepare and send a bunch of handshake initiations with correct mac2
922 # expect a handshake response and then ratelimiting
924 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
925 txs = [init] * (HANDSHAKE_NUM_BEFORE_RATELIMITING + NUM_TO_REJECT)
927 # TODO: Deterimine why no handshake response is sent back if test is
928 # not run in as part of the test suite. It fails only very occasionally
931 # Until then, if no response, don't fail trying to verify it.
932 # The error counter test still verifies that the correct number of
933 # handshake initiaions are ratelimited.
935 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
938 f"{self._testMethodDoc}: send_and_expect_some() failed to get any response packets."
945 self.base_ratelimited6_err + NUM_TO_REJECT,
946 self.statistics.get_err_counter(self.ratelimited6_err),
950 self.base_ratelimited4_err + NUM_TO_REJECT,
951 self.statistics.get_err_counter(self.ratelimited4_err),
954 # verify the response
956 peer_1.consume_response(rxs[0], is_ip6=is_ip6)
958 # clear up under load state
959 self.sleep(UNDER_LOAD_INTERVAL)
962 peer_1.remove_vpp_config()
963 wg0.remove_vpp_config()
965 def test_wg_handshake_ratelimiting_v4(self):
966 """Handshake ratelimiting (v4)"""
967 self._test_wg_handshake_ratelimiting_tmpl(is_ip6=False)
969 def test_wg_handshake_ratelimiting_v6(self):
970 """Handshake ratelimiting (v6)"""
971 self._test_wg_handshake_ratelimiting_tmpl(is_ip6=True)
973 def test_wg_handshake_ratelimiting_multi_peer(self):
974 """Handshake ratelimiting (multiple peer)"""
977 # create wg interface
978 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
982 self.pg_enable_capture(self.pg_interfaces)
987 self.pg1.generate_remote_hosts(NUM_PEERS)
988 self.pg1.configure_ipv4_neighbors()
991 self, wg0, self.pg1.remote_hosts[0].ip4, port + 1, ["10.11.3.0/24"]
994 self, wg0, self.pg1.remote_hosts[1].ip4, port + 1, ["10.11.4.0/24"]
996 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 2)
998 # skip the first automatic handshake
999 self.pg1.get_capture(NUM_PEERS, timeout=HANDSHAKE_JITTER)
1001 # (peer_1) prepare and send a bunch of handshake initiations
1002 # expect not to switch to under load state
1003 init_1 = peer_1.mk_handshake(self.pg1)
1004 txs = [init_1] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
1005 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
1007 # (peer_1) expect the peer to send a handshake response
1008 peer_1.consume_response(rxs[0])
1009 peer_1.noise_reset()
1011 # (peer_1) send another bunch of handshake initiations
1012 # expect to switch to under load state
1013 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
1015 # (peer_1) expect the peer to send a cookie reply
1016 peer_1.consume_cookie(rxs[-1])
1018 # (peer_2) prepare and send a handshake initiation
1019 # expect a cookie reply
1020 init_2 = peer_2.mk_handshake(self.pg1)
1021 rxs = self.send_and_expect(self.pg1, [init_2], self.pg1)
1022 peer_2.consume_cookie(rxs[0])
1024 # (peer_1) (peer_2) prepare and send a bunch of handshake initiations with correct mac2
1025 # expect a handshake response and then ratelimiting
1026 PEER_1_NUM_TO_REJECT = 2
1027 PEER_2_NUM_TO_REJECT = 5
1028 init_1 = peer_1.mk_handshake(self.pg1)
1029 txs = [init_1] * (HANDSHAKE_NUM_BEFORE_RATELIMITING + PEER_1_NUM_TO_REJECT)
1030 init_2 = peer_2.mk_handshake(self.pg1)
1031 txs += [init_2] * (HANDSHAKE_NUM_BEFORE_RATELIMITING + PEER_2_NUM_TO_REJECT)
1032 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
1035 self.base_ratelimited4_err + PEER_1_NUM_TO_REJECT
1036 < self.statistics.get_err_counter(self.ratelimited4_err)
1037 <= self.base_ratelimited4_err + PEER_1_NUM_TO_REJECT + PEER_2_NUM_TO_REJECT
1040 # (peer_1) (peer_2) verify the response
1041 peer_1.consume_response(rxs[0])
1042 peer_2.consume_response(rxs[1])
1044 # clear up under load state
1045 self.sleep(UNDER_LOAD_INTERVAL)
1048 peer_1.remove_vpp_config()
1049 peer_2.remove_vpp_config()
1050 wg0.remove_vpp_config()
1052 def _test_wg_peer_roaming_on_handshake_tmpl(self, is_endpoint_set, is_resp, is_ip6):
1055 # create wg interface
1057 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1061 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1065 self.pg_enable_capture(self.pg_interfaces)
1068 # create more remote hosts
1069 NUM_REMOTE_HOSTS = 2
1070 self.pg1.generate_remote_hosts(NUM_REMOTE_HOSTS)
1072 self.pg1.configure_ipv6_neighbors()
1074 self.pg1.configure_ipv4_neighbors()
1081 endpoint=self.pg1.remote_hosts[0].ip6 if is_endpoint_set else "::",
1082 port=port + 1 if is_endpoint_set else 0,
1083 allowed_ips=["1::3:0/112"],
1089 endpoint=self.pg1.remote_hosts[0].ip4 if is_endpoint_set else "0.0.0.0",
1090 port=port + 1 if is_endpoint_set else 0,
1091 allowed_ips=["10.11.3.0/24"],
1093 self.assertTrue(peer_1.query_vpp_config())
1096 # wait for the peer to send a handshake initiation
1097 rxs = self.pg1.get_capture(1, timeout=2)
1098 # prepare a handshake response
1099 resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6)
1102 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip6, port + 100)
1103 resp[IPv6].src, resp[UDP].sport = peer_1.endpoint, peer_1.port
1105 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip4, port + 100)
1106 resp[IP].src, resp[UDP].sport = peer_1.endpoint, peer_1.port
1107 # send the handshake response
1108 # expect a keepalive message sent to the new endpoint
1109 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1110 # verify the keepalive message
1111 b = peer_1.decrypt_transport(rxs[0], is_ip6=is_ip6)
1112 self.assertEqual(0, len(b))
1116 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip6, port + 100)
1118 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip4, port + 100)
1119 # prepare and send a handshake initiation
1120 # expect a handshake response sent to the new endpoint
1121 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
1122 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
1123 # verify the response
1124 peer_1.consume_response(rxs[0], is_ip6=is_ip6)
1125 self.assertTrue(peer_1.query_vpp_config())
1128 peer_1.remove_vpp_config()
1129 wg0.remove_vpp_config()
1131 def test_wg_peer_roaming_on_init_v4(self):
1132 """Peer roaming on handshake initiation (v4)"""
1133 self._test_wg_peer_roaming_on_handshake_tmpl(
1134 is_endpoint_set=False, is_resp=False, is_ip6=False
1137 def test_wg_peer_roaming_on_init_v6(self):
1138 """Peer roaming on handshake initiation (v6)"""
1139 self._test_wg_peer_roaming_on_handshake_tmpl(
1140 is_endpoint_set=False, is_resp=False, is_ip6=True
1143 def test_wg_peer_roaming_on_resp_v4(self):
1144 """Peer roaming on handshake response (v4)"""
1145 self._test_wg_peer_roaming_on_handshake_tmpl(
1146 is_endpoint_set=True, is_resp=True, is_ip6=False
1149 def test_wg_peer_roaming_on_resp_v6(self):
1150 """Peer roaming on handshake response (v6)"""
1151 self._test_wg_peer_roaming_on_handshake_tmpl(
1152 is_endpoint_set=True, is_resp=True, is_ip6=True
1155 def _test_wg_peer_roaming_on_data_tmpl(self, is_async, is_ip6):
1156 self.vapi.wg_set_async_mode(is_async)
1159 # create wg interface
1161 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1165 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1169 self.pg_enable_capture(self.pg_interfaces)
1172 # create more remote hosts
1173 NUM_REMOTE_HOSTS = 2
1174 self.pg1.generate_remote_hosts(NUM_REMOTE_HOSTS)
1176 self.pg1.configure_ipv6_neighbors()
1178 self.pg1.configure_ipv4_neighbors()
1183 self, wg0, self.pg1.remote_hosts[0].ip6, port + 1, ["1::3:0/112"]
1187 self, wg0, self.pg1.remote_hosts[0].ip4, port + 1, ["10.11.3.0/24"]
1189 self.assertTrue(peer_1.query_vpp_config())
1191 # create a route to rewrite traffic into the wg interface
1194 self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
1198 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1201 # wait for the peer to send a handshake initiation
1202 rxs = self.pg1.get_capture(1, timeout=2)
1204 # prepare and send a handshake response
1205 # expect a keepalive message
1206 resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6)
1207 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1209 # verify the keepalive message
1210 b = peer_1.decrypt_transport(rxs[0], is_ip6=is_ip6)
1211 self.assertEqual(0, len(b))
1215 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip6, port + 100)
1217 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip4, port + 100)
1219 # prepare and send a data packet
1220 # expect endpoint change
1222 ip_header = IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1224 ip_header = IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1226 peer_1.mk_tunnel_header(self.pg1, is_ip6=is_ip6)
1227 / Wireguard(message_type=4, reserved_zero=0)
1228 / WireguardTransport(
1229 receiver_index=peer_1.sender,
1231 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1232 ip_header / UDP(sport=222, dport=223) / Raw()
1236 rxs = self.send_and_expect(self.pg1, [data], self.pg0)
1238 self.assertEqual(rxs[0][IPv6].dst, self.pg0.remote_ip6)
1239 self.assertEqual(rxs[0][IPv6].hlim, 19)
1241 self.assertEqual(rxs[0][IP].dst, self.pg0.remote_ip4)
1242 self.assertEqual(rxs[0][IP].ttl, 19)
1243 self.assertTrue(peer_1.query_vpp_config())
1245 # prepare and send a packet that will be rewritten into the wg interface
1246 # expect a data packet sent to the new endpoint
1248 ip_header = IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1250 ip_header = IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1252 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1254 / UDP(sport=555, dport=556)
1257 rxs = self.send_and_expect(self.pg0, [p], self.pg1)
1259 # verify the data packet
1260 peer_1.validate_encapped(rxs, p, is_tunnel_ip6=is_ip6, is_transport_ip6=is_ip6)
1263 r1.remove_vpp_config()
1264 peer_1.remove_vpp_config()
1265 wg0.remove_vpp_config()
1267 def test_wg_peer_roaming_on_data_v4_sync(self):
1268 """Peer roaming on data packet (v4, sync)"""
1269 self._test_wg_peer_roaming_on_data_tmpl(is_async=False, is_ip6=False)
1271 def test_wg_peer_roaming_on_data_v6_sync(self):
1272 """Peer roaming on data packet (v6, sync)"""
1273 self._test_wg_peer_roaming_on_data_tmpl(is_async=False, is_ip6=True)
1275 def test_wg_peer_roaming_on_data_v4_async(self):
1276 """Peer roaming on data packet (v4, async)"""
1277 self._test_wg_peer_roaming_on_data_tmpl(is_async=True, is_ip6=False)
1279 def test_wg_peer_roaming_on_data_v6_async(self):
1280 """Peer roaming on data packet (v6, async)"""
1281 self._test_wg_peer_roaming_on_data_tmpl(is_async=True, is_ip6=True)
1283 def test_wg_peer_resp(self):
1284 """Send handshake response IPv4 tunnel"""
1288 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1292 self.pg_enable_capture(self.pg_interfaces)
1296 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
1298 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1301 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1304 # wait for the peer to send a handshake
1305 rx = self.pg1.get_capture(1, timeout=2)
1307 # consume the handshake in the noise protocol and
1308 # generate the response
1309 resp = peer_1.consume_init(rx[0], self.pg1)
1311 # send the response, get keepalive
1312 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1315 b = peer_1.decrypt_transport(rx)
1316 self.assertEqual(0, len(b))
1318 # send a packets that are routed into the tunnel
1320 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1321 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1322 / UDP(sport=555, dport=556)
1326 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1328 peer_1.validate_encapped(rxs, p)
1330 # send packets into the tunnel, expect to receive them on
1334 peer_1.mk_tunnel_header(self.pg1)
1335 / Wireguard(message_type=4, reserved_zero=0)
1336 / WireguardTransport(
1337 receiver_index=peer_1.sender,
1339 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1341 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1342 / UDP(sport=222, dport=223)
1348 for ii in range(255)
1351 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1354 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1355 self.assertEqual(rx[IP].ttl, 19)
1357 r1.remove_vpp_config()
1358 peer_1.remove_vpp_config()
1359 wg0.remove_vpp_config()
1361 def test_wg_peer_resp_ipv6(self):
1362 """Send handshake response IPv6 tunnel"""
1366 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1370 self.pg_enable_capture(self.pg_interfaces)
1374 self, wg0, self.pg1.remote_ip6, port + 1, ["10.11.3.0/24"]
1376 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1379 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1382 # wait for the peer to send a handshake
1383 rx = self.pg1.get_capture(1, timeout=2)
1385 # consume the handshake in the noise protocol and
1386 # generate the response
1387 resp = peer_1.consume_init(rx[0], self.pg1, is_ip6=True)
1389 # send the response, get keepalive
1390 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1393 b = peer_1.decrypt_transport(rx, True)
1394 self.assertEqual(0, len(b))
1396 # send a packets that are routed into the tunnel
1398 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1399 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1400 / UDP(sport=555, dport=556)
1404 rxs = self.send_and_expect(self.pg0, p * 2, self.pg1)
1405 peer_1.validate_encapped(rxs, p, True)
1407 # send packets into the tunnel, expect to receive them on
1411 peer_1.mk_tunnel_header(self.pg1, True)
1412 / Wireguard(message_type=4, reserved_zero=0)
1413 / WireguardTransport(
1414 receiver_index=peer_1.sender,
1416 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1418 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1419 / UDP(sport=222, dport=223)
1425 for ii in range(255)
1428 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1431 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1432 self.assertEqual(rx[IP].ttl, 19)
1434 r1.remove_vpp_config()
1435 peer_1.remove_vpp_config()
1436 wg0.remove_vpp_config()
1438 def test_wg_peer_v4o4(self):
1444 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1449 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
1451 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1454 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1457 self, "20.22.3.0", 24, [VppRoutePath("20.22.3.1", wg0.sw_if_index)]
1460 # route a packet into the wg interface
1461 # use the allowed-ip prefix
1462 # this is dropped because the peer is not initiated
1464 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1465 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1466 / UDP(sport=555, dport=556)
1469 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1471 self.base_kp4_err + 1, self.statistics.get_err_counter(self.kp4_error)
1474 # route a packet into the wg interface
1475 # use a not allowed-ip prefix
1476 # this is dropped because there is no matching peer
1478 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1479 / IP(src=self.pg0.remote_ip4, dst="20.22.3.2")
1480 / UDP(sport=555, dport=556)
1483 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1485 self.base_peer4_out_err + 1,
1486 self.statistics.get_err_counter(self.peer4_out_err),
1489 # send a handsake from the peer with an invalid MAC
1490 p = peer_1.mk_handshake(self.pg1)
1491 p[WireguardInitiation].mac1 = b"foobar"
1492 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1494 self.base_mac4_err + 1, self.statistics.get_err_counter(self.mac4_error)
1497 # send a handsake from the peer but signed by the wrong key.
1498 p = peer_1.mk_handshake(
1499 self.pg1, False, X25519PrivateKey.generate().public_key()
1501 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1503 self.base_peer4_in_err + 1,
1504 self.statistics.get_err_counter(self.peer4_in_err),
1507 # send a valid handsake init for which we expect a response
1508 p = peer_1.mk_handshake(self.pg1)
1510 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1512 peer_1.consume_response(rx[0])
1514 # route a packet into the wg interface
1515 # this is dropped because the peer is still not initiated
1517 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1518 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1519 / UDP(sport=555, dport=556)
1522 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1524 self.base_kp4_err + 2, self.statistics.get_err_counter(self.kp4_error)
1527 # send a data packet from the peer through the tunnel
1528 # this completes the handshake
1530 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1531 / UDP(sport=222, dport=223)
1534 d = peer_1.encrypt_transport(p)
1535 p = peer_1.mk_tunnel_header(self.pg1) / (
1536 Wireguard(message_type=4, reserved_zero=0)
1537 / WireguardTransport(
1538 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1541 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1544 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1545 self.assertEqual(rx[IP].ttl, 19)
1547 # send a packets that are routed into the tunnel
1549 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1550 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1551 / UDP(sport=555, dport=556)
1555 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1558 rx = IP(peer_1.decrypt_transport(rx))
1560 # check the original packet is present
1561 self.assertEqual(rx[IP].dst, p[IP].dst)
1562 self.assertEqual(rx[IP].ttl, p[IP].ttl - 1)
1564 # send packets into the tunnel, expect to receive them on
1568 peer_1.mk_tunnel_header(self.pg1)
1569 / Wireguard(message_type=4, reserved_zero=0)
1570 / WireguardTransport(
1571 receiver_index=peer_1.sender,
1573 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1575 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1576 / UDP(sport=222, dport=223)
1582 for ii in range(255)
1585 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1588 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1589 self.assertEqual(rx[IP].ttl, 19)
1591 r1.remove_vpp_config()
1592 r2.remove_vpp_config()
1593 peer_1.remove_vpp_config()
1594 wg0.remove_vpp_config()
1596 def test_wg_peer_v6o6(self):
1602 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1607 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
1609 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1612 self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
1615 self, "22::3:0", 112, [VppRoutePath("22::3:1", wg0.sw_if_index)]
1618 # route a packet into the wg interface
1619 # use the allowed-ip prefix
1620 # this is dropped because the peer is not initiated
1623 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1624 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1625 / UDP(sport=555, dport=556)
1628 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1631 self.base_kp6_err + 1, self.statistics.get_err_counter(self.kp6_error)
1634 # route a packet into the wg interface
1635 # use a not allowed-ip prefix
1636 # this is dropped because there is no matching peer
1638 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1639 / IPv6(src=self.pg0.remote_ip6, dst="22::3:2")
1640 / UDP(sport=555, dport=556)
1643 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1645 self.base_peer6_out_err + 1,
1646 self.statistics.get_err_counter(self.peer6_out_err),
1649 # send a handsake from the peer with an invalid MAC
1650 p = peer_1.mk_handshake(self.pg1, True)
1651 p[WireguardInitiation].mac1 = b"foobar"
1652 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1655 self.base_mac6_err + 1, self.statistics.get_err_counter(self.mac6_error)
1658 # send a handsake from the peer but signed by the wrong key.
1659 p = peer_1.mk_handshake(
1660 self.pg1, True, X25519PrivateKey.generate().public_key()
1662 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1664 self.base_peer6_in_err + 1,
1665 self.statistics.get_err_counter(self.peer6_in_err),
1668 # send a valid handsake init for which we expect a response
1669 p = peer_1.mk_handshake(self.pg1, True)
1671 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1673 peer_1.consume_response(rx[0], True)
1675 # route a packet into the wg interface
1676 # this is dropped because the peer is still not initiated
1678 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1679 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1680 / UDP(sport=555, dport=556)
1683 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1685 self.base_kp6_err + 2, self.statistics.get_err_counter(self.kp6_error)
1688 # send a data packet from the peer through the tunnel
1689 # this completes the handshake
1691 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1692 / UDP(sport=222, dport=223)
1695 d = peer_1.encrypt_transport(p)
1696 p = peer_1.mk_tunnel_header(self.pg1, True) / (
1697 Wireguard(message_type=4, reserved_zero=0)
1698 / WireguardTransport(
1699 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1702 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1705 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1706 self.assertEqual(rx[IPv6].hlim, 19)
1708 # send a packets that are routed into the tunnel
1710 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1711 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1712 / UDP(sport=555, dport=556)
1716 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1719 rx = IPv6(peer_1.decrypt_transport(rx, True))
1721 # check the original packet is present
1722 self.assertEqual(rx[IPv6].dst, p[IPv6].dst)
1723 self.assertEqual(rx[IPv6].hlim, p[IPv6].hlim - 1)
1725 # send packets into the tunnel, expect to receive them on
1729 peer_1.mk_tunnel_header(self.pg1, True)
1730 / Wireguard(message_type=4, reserved_zero=0)
1731 / WireguardTransport(
1732 receiver_index=peer_1.sender,
1734 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1736 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1737 / UDP(sport=222, dport=223)
1743 for ii in range(255)
1746 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1749 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1750 self.assertEqual(rx[IPv6].hlim, 19)
1752 r1.remove_vpp_config()
1753 r2.remove_vpp_config()
1754 peer_1.remove_vpp_config()
1755 wg0.remove_vpp_config()
1757 def test_wg_peer_v6o4(self):
1763 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1768 self, wg0, self.pg1.remote_ip4, port + 1, ["1::3:0/112"]
1770 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1773 self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
1776 # route a packet into the wg interface
1777 # use the allowed-ip prefix
1778 # this is dropped because the peer is not initiated
1780 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1781 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1782 / UDP(sport=555, dport=556)
1785 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1787 self.base_kp6_err + 1, self.statistics.get_err_counter(self.kp6_error)
1790 # send a handsake from the peer with an invalid MAC
1791 p = peer_1.mk_handshake(self.pg1)
1792 p[WireguardInitiation].mac1 = b"foobar"
1793 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1796 self.base_mac4_err + 1, self.statistics.get_err_counter(self.mac4_error)
1799 # send a handsake from the peer but signed by the wrong key.
1800 p = peer_1.mk_handshake(
1801 self.pg1, False, X25519PrivateKey.generate().public_key()
1803 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1805 self.base_peer4_in_err + 1,
1806 self.statistics.get_err_counter(self.peer4_in_err),
1809 # send a valid handsake init for which we expect a response
1810 p = peer_1.mk_handshake(self.pg1)
1812 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1814 peer_1.consume_response(rx[0])
1816 # route a packet into the wg interface
1817 # this is dropped because the peer is still not initiated
1819 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1820 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1821 / UDP(sport=555, dport=556)
1824 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1826 self.base_kp6_err + 2, self.statistics.get_err_counter(self.kp6_error)
1829 # send a data packet from the peer through the tunnel
1830 # this completes the handshake
1832 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1833 / UDP(sport=222, dport=223)
1836 d = peer_1.encrypt_transport(p)
1837 p = peer_1.mk_tunnel_header(self.pg1) / (
1838 Wireguard(message_type=4, reserved_zero=0)
1839 / WireguardTransport(
1840 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1843 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1846 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1847 self.assertEqual(rx[IPv6].hlim, 19)
1849 # send a packets that are routed into the tunnel
1851 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1852 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1853 / UDP(sport=555, dport=556)
1857 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1860 rx = IPv6(peer_1.decrypt_transport(rx))
1862 # check the original packet is present
1863 self.assertEqual(rx[IPv6].dst, p[IPv6].dst)
1864 self.assertEqual(rx[IPv6].hlim, p[IPv6].hlim - 1)
1866 # send packets into the tunnel, expect to receive them on
1870 peer_1.mk_tunnel_header(self.pg1)
1871 / Wireguard(message_type=4, reserved_zero=0)
1872 / WireguardTransport(
1873 receiver_index=peer_1.sender,
1875 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1877 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1878 / UDP(sport=222, dport=223)
1884 for ii in range(255)
1887 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1890 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1891 self.assertEqual(rx[IPv6].hlim, 19)
1893 r1.remove_vpp_config()
1894 peer_1.remove_vpp_config()
1895 wg0.remove_vpp_config()
1897 def test_wg_peer_v4o6(self):
1903 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1908 self, wg0, self.pg1.remote_ip6, port + 1, ["10.11.3.0/24"]
1910 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1913 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1916 # route a packet into the wg interface
1917 # use the allowed-ip prefix
1918 # this is dropped because the peer is not initiated
1920 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1921 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1922 / UDP(sport=555, dport=556)
1925 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1927 self.base_kp4_err + 1, self.statistics.get_err_counter(self.kp4_error)
1930 # send a handsake from the peer with an invalid MAC
1931 p = peer_1.mk_handshake(self.pg1, True)
1932 p[WireguardInitiation].mac1 = b"foobar"
1933 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1935 self.base_mac6_err + 1, self.statistics.get_err_counter(self.mac6_error)
1938 # send a handsake from the peer but signed by the wrong key.
1939 p = peer_1.mk_handshake(
1940 self.pg1, True, X25519PrivateKey.generate().public_key()
1942 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
1944 self.base_peer6_in_err + 1,
1945 self.statistics.get_err_counter(self.peer6_in_err),
1948 # send a valid handsake init for which we expect a response
1949 p = peer_1.mk_handshake(self.pg1, True)
1951 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1953 peer_1.consume_response(rx[0], True)
1955 # route a packet into the wg interface
1956 # this is dropped because the peer is still not initiated
1958 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1959 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1960 / UDP(sport=555, dport=556)
1963 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
1965 self.base_kp4_err + 2, self.statistics.get_err_counter(self.kp4_error)
1968 # send a data packet from the peer through the tunnel
1969 # this completes the handshake
1971 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1972 / UDP(sport=222, dport=223)
1975 d = peer_1.encrypt_transport(p)
1976 p = peer_1.mk_tunnel_header(self.pg1, True) / (
1977 Wireguard(message_type=4, reserved_zero=0)
1978 / WireguardTransport(
1979 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1982 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1985 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1986 self.assertEqual(rx[IP].ttl, 19)
1988 # send a packets that are routed into the tunnel
1990 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1991 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1992 / UDP(sport=555, dport=556)
1996 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1999 rx = IP(peer_1.decrypt_transport(rx, True))
2001 # check the original packet is present
2002 self.assertEqual(rx[IP].dst, p[IP].dst)
2003 self.assertEqual(rx[IP].ttl, p[IP].ttl - 1)
2005 # send packets into the tunnel, expect to receive them on
2009 peer_1.mk_tunnel_header(self.pg1, True)
2010 / Wireguard(message_type=4, reserved_zero=0)
2011 / WireguardTransport(
2012 receiver_index=peer_1.sender,
2014 encrypted_encapsulated_packet=peer_1.encrypt_transport(
2016 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
2017 / UDP(sport=222, dport=223)
2023 for ii in range(255)
2026 rxs = self.send_and_expect(self.pg1, p, self.pg0)
2029 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
2030 self.assertEqual(rx[IP].ttl, 19)
2032 r1.remove_vpp_config()
2033 peer_1.remove_vpp_config()
2034 wg0.remove_vpp_config()
2036 def test_wg_multi_peer(self):
2037 """multiple peer setup"""
2041 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2042 wg1 = VppWgInterface(self, self.pg2.local_ip4, port + 1).add_vpp_config()
2046 # Check peer counter
2047 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 0)
2049 self.pg_enable_capture(self.pg_interfaces)
2052 # Create many peers on sencond interface
2054 self.pg2.generate_remote_hosts(NUM_PEERS)
2055 self.pg2.configure_ipv4_neighbors()
2056 self.pg1.generate_remote_hosts(NUM_PEERS)
2057 self.pg1.configure_ipv4_neighbors()
2063 for i in range(NUM_PEERS):
2068 self.pg1.remote_hosts[i].ip4,
2070 ["10.0.%d.4/32" % i],
2078 [VppRoutePath(self.pg1.remote_hosts[i].ip4, wg0.sw_if_index)],
2086 self.pg2.remote_hosts[i].ip4,
2088 ["10.100.%d.4/32" % i],
2096 [VppRoutePath(self.pg2.remote_hosts[i].ip4, wg1.sw_if_index)],
2100 self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_PEERS * 2)
2102 self.logger.info(self.vapi.cli("show wireguard peer"))
2103 self.logger.info(self.vapi.cli("show wireguard interface"))
2104 self.logger.info(self.vapi.cli("show adj 37"))
2105 self.logger.info(self.vapi.cli("sh ip fib 172.16.3.17"))
2106 self.logger.info(self.vapi.cli("sh ip fib 10.11.3.0"))
2110 r.remove_vpp_config()
2112 r.remove_vpp_config()
2116 self.assertTrue(p.query_vpp_config())
2117 p.remove_vpp_config()
2119 self.assertTrue(p.query_vpp_config())
2120 p.remove_vpp_config()
2122 wg0.remove_vpp_config()
2123 wg1.remove_vpp_config()
2125 def test_wg_multi_interface(self):
2126 """Multi-tunnel on the same port"""
2129 # Create many wireguard interfaces
2131 self.pg1.generate_remote_hosts(NUM_IFS)
2132 self.pg1.configure_ipv4_neighbors()
2133 self.pg0.generate_remote_hosts(NUM_IFS)
2134 self.pg0.configure_ipv4_neighbors()
2136 self.pg_enable_capture(self.pg_interfaces)
2139 # Create interfaces with a peer on each
2143 for i in range(NUM_IFS):
2144 # Use the same port for each interface
2145 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2153 self.pg1.remote_hosts[i].ip4,
2155 ["10.0.%d.0/24" % i],
2164 [VppRoutePath("10.0.%d.4" % i, wg0.sw_if_index)],
2168 self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_IFS)
2170 # skip the first automatic handshake
2171 self.pg1.get_capture(NUM_IFS, timeout=HANDSHAKE_JITTER)
2173 for i in range(NUM_IFS):
2174 # send a valid handsake init for which we expect a response
2175 p = peers[i].mk_handshake(self.pg1)
2176 rx = self.send_and_expect(self.pg1, [p], self.pg1)
2177 peers[i].consume_response(rx[0])
2179 # send a data packet from the peer through the tunnel
2180 # this completes the handshake
2182 IP(src="10.0.%d.4" % i, dst=self.pg0.remote_hosts[i].ip4, ttl=20)
2183 / UDP(sport=222, dport=223)
2186 d = peers[i].encrypt_transport(p)
2187 p = peers[i].mk_tunnel_header(self.pg1) / (
2188 Wireguard(message_type=4, reserved_zero=0)
2189 / WireguardTransport(
2190 receiver_index=peers[i].sender,
2192 encrypted_encapsulated_packet=d,
2195 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
2197 self.assertEqual(rx[IP].dst, self.pg0.remote_hosts[i].ip4)
2198 self.assertEqual(rx[IP].ttl, 19)
2200 # send a packets that are routed into the tunnel
2201 for i in range(NUM_IFS):
2203 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2204 / IP(src=self.pg0.remote_hosts[i].ip4, dst="10.0.%d.4" % i)
2205 / UDP(sport=555, dport=556)
2209 rxs = self.send_and_expect(self.pg0, p * 64, self.pg1)
2212 rx = IP(peers[i].decrypt_transport(rx))
2214 # check the oringial packet is present
2215 self.assertEqual(rx[IP].dst, p[IP].dst)
2216 self.assertEqual(rx[IP].ttl, p[IP].ttl - 1)
2218 # send packets into the tunnel
2219 for i in range(NUM_IFS):
2222 peers[i].mk_tunnel_header(self.pg1)
2223 / Wireguard(message_type=4, reserved_zero=0)
2224 / WireguardTransport(
2225 receiver_index=peers[i].sender,
2227 encrypted_encapsulated_packet=peers[i].encrypt_transport(
2230 src="10.0.%d.4" % i,
2231 dst=self.pg0.remote_hosts[i].ip4,
2234 / UDP(sport=222, dport=223)
2243 rxs = self.send_and_expect(self.pg1, p, self.pg0)
2246 self.assertEqual(rx[IP].dst, self.pg0.remote_hosts[i].ip4)
2247 self.assertEqual(rx[IP].ttl, 19)
2250 r.remove_vpp_config()
2252 p.remove_vpp_config()
2254 i.remove_vpp_config()
2256 def test_wg_event(self):
2259 ESTABLISHED_FLAG = (
2260 VppEnum.vl_api_wireguard_peer_flags_t.WIREGUARD_PEER_ESTABLISHED
2262 DEAD_FLAG = VppEnum.vl_api_wireguard_peer_flags_t.WIREGUARD_PEER_STATUS_DEAD
2265 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2266 wg1 = VppWgInterface(self, self.pg2.local_ip4, port + 1).add_vpp_config()
2270 # Check peer counter
2271 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 0)
2273 self.pg_enable_capture(self.pg_interfaces)
2278 self.pg2.generate_remote_hosts(NUM_PEERS)
2279 self.pg2.configure_ipv4_neighbors()
2280 self.pg1.generate_remote_hosts(NUM_PEERS)
2281 self.pg1.configure_ipv4_neighbors()
2287 for i in range(NUM_PEERS):
2292 self.pg1.remote_hosts[i].ip4,
2294 ["10.0.%d.4/32" % i],
2302 [VppRoutePath(self.pg1.remote_hosts[i].ip4, wg0.sw_if_index)],
2310 self.pg2.remote_hosts[i].ip4,
2312 ["10.100.%d.4/32" % i],
2320 [VppRoutePath(self.pg2.remote_hosts[i].ip4, wg1.sw_if_index)],
2324 self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_PEERS * 2)
2326 # skip the first automatic handshake
2327 self.pg1.get_capture(NUM_PEERS, timeout=HANDSHAKE_JITTER)
2328 self.pg2.get_capture(NUM_PEERS, timeout=HANDSHAKE_JITTER)
2330 # Want events from the first perr of wg0
2331 # and from all wg1 peers
2332 peers_0[0].want_events()
2335 for i in range(NUM_PEERS):
2336 # wg0 peers: send a valid handsake init for which we expect a response
2337 p = peers_0[i].mk_handshake(self.pg1)
2338 rx = self.send_and_expect(self.pg1, [p], self.pg1)
2339 peers_0[i].consume_response(rx[0])
2341 # wg0 peers: send empty packet, it means successful connection (WIREGUARD_PEER_ESTABLISHED)
2342 keepalive = peers_0[i].encrypt_transport(0)
2343 p = peers_0[i].mk_tunnel_header(self.pg1) / (
2344 Wireguard(message_type=4, reserved_zero=0)
2345 / WireguardTransport(
2346 receiver_index=peers_0[i].sender,
2348 encrypted_encapsulated_packet=keepalive,
2351 # TODO: Figure out wny there are sometimes wg packets received here
2352 # self.send_and_assert_no_replies(self.pg1, [p])
2353 self.pg_send(self.pg1, [p])
2355 # wg0 peers: wait for established flag
2357 peers_0[0].wait_event(ESTABLISHED_FLAG)
2359 # wg1 peers: send a valid handsake init for which we expect a response
2360 p = peers_1[i].mk_handshake(self.pg2)
2361 rx = self.send_and_expect(self.pg2, [p], self.pg2)
2362 peers_1[i].consume_response(rx[0])
2364 # wg1 peers: send empty packet, it means successful connection (WIREGUARD_PEER_ESTABLISHED)
2365 keepalive = peers_1[i].encrypt_transport(0)
2366 p = peers_1[i].mk_tunnel_header(self.pg2) / (
2367 Wireguard(message_type=4, reserved_zero=0)
2368 / WireguardTransport(
2369 receiver_index=peers_1[i].sender,
2371 encrypted_encapsulated_packet=keepalive,
2374 self.send_and_assert_no_replies(self.pg2, [p])
2376 # wg1 peers: wait for established flag
2377 wg1.wait_events(ESTABLISHED_FLAG, [peers_1[0].index, peers_1[1].index])
2381 r.remove_vpp_config()
2383 r.remove_vpp_config()
2386 for i in range(NUM_PEERS):
2387 self.assertTrue(peers_0[i].query_vpp_config())
2388 peers_0[i].remove_vpp_config()
2390 peers_0[i].wait_event(0)
2391 peers_0[i].wait_event(DEAD_FLAG)
2393 self.assertTrue(p.query_vpp_config())
2394 p.remove_vpp_config()
2396 p.wait_event(DEAD_FLAG)
2398 wg0.remove_vpp_config()
2399 wg1.remove_vpp_config()
2401 def test_wg_sending_handshake_when_admin_down(self):
2402 """Sending handshake when admin down"""
2405 # create wg interface
2406 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2411 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
2413 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2415 self.pg_enable_capture(self.pg_interfaces)
2418 # wait for the peer to send a handshake initiation
2419 # expect no handshakes
2421 self.pg1.assert_nothing_captured(remark="handshake packet(s) sent")
2423 self.pg_enable_capture(self.pg_interfaces)
2426 # administratively enable the wg interface
2427 # expect the peer to send a handshake initiation
2429 rxs = self.pg1.get_capture(1, timeout=2)
2430 peer_1.consume_init(rxs[0], self.pg1)
2432 self.pg_enable_capture(self.pg_interfaces)
2435 # administratively disable the wg interface
2436 # expect no handshakes
2439 self.pg1.assert_nothing_captured(remark="handshake packet(s) sent")
2442 peer_1.remove_vpp_config()
2443 wg0.remove_vpp_config()
2445 def test_wg_sending_data_when_admin_down(self):
2446 """Sending data when admin down"""
2449 # create wg interface
2450 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2454 self.pg_enable_capture(self.pg_interfaces)
2459 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
2461 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2463 # create a route to rewrite traffic into the wg interface
2465 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
2468 # wait for the peer to send a handshake initiation
2469 rxs = self.pg1.get_capture(1, timeout=2)
2471 # prepare and send a handshake response
2472 # expect a keepalive message
2473 resp = peer_1.consume_init(rxs[0], self.pg1)
2474 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
2476 # verify the keepalive message
2477 b = peer_1.decrypt_transport(rxs[0])
2478 self.assertEqual(0, len(b))
2480 # prepare and send a packet that will be rewritten into the wg interface
2481 # expect a data packet sent
2483 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2484 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
2485 / UDP(sport=555, dport=556)
2488 rxs = self.send_and_expect(self.pg0, [p], self.pg1)
2490 # verify the data packet
2491 peer_1.validate_encapped(rxs, p)
2493 # administratively disable the wg interface
2496 # send a packet that will be rewritten into the wg interface
2497 # expect no data packets sent
2498 self.send_and_assert_no_replies(self.pg0, [p])
2500 # administratively enable the wg interface
2501 # expect the peer to send a handshake initiation
2503 peer_1.noise_reset()
2504 rxs = self.pg1.get_capture(1, timeout=2)
2505 resp = peer_1.consume_init(rxs[0], self.pg1)
2507 # send a packet that will be rewritten into the wg interface
2508 # expect no data packets sent because the peer is not initiated
2509 self.send_and_assert_no_replies(self.pg0, [p])
2511 self.base_kp4_err + 1, self.statistics.get_err_counter(self.kp4_error)
2514 # send a handshake response and expect a keepalive message
2515 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
2517 # verify the keepalive message
2518 b = peer_1.decrypt_transport(rxs[0])
2519 self.assertEqual(0, len(b))
2521 # send a packet that will be rewritten into the wg interface
2522 # expect a data packet sent
2523 rxs = self.send_and_expect(self.pg0, [p], self.pg1)
2525 # verify the data packet
2526 peer_1.validate_encapped(rxs, p)
2529 r1.remove_vpp_config()
2530 peer_1.remove_vpp_config()
2531 wg0.remove_vpp_config()
2533 def _test_wg_large_packet_tmpl(self, is_async, is_ip6):
2534 self.vapi.wg_set_async_mode(is_async)
2537 # create wg interface
2539 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
2543 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2547 self.pg_enable_capture(self.pg_interfaces)
2553 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
2557 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
2559 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2561 # create a route to rewrite traffic into the wg interface
2564 self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
2568 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
2571 # wait for the peer to send a handshake initiation
2572 rxs = self.pg1.get_capture(1, timeout=2)
2574 # prepare and send a handshake response
2575 # expect a keepalive message
2576 resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6)
2577 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
2579 # verify the keepalive message
2580 b = peer_1.decrypt_transport(rxs[0], is_ip6=is_ip6)
2581 self.assertEqual(0, len(b))
2583 # prepare and send data packets
2584 # expect to receive them decrypted
2586 ip_header = IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
2588 ip_header = IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
2592 4500, # three buffers
2593 1910 if is_ip6 else 1950, # auth tag is not contiguous
2596 for l in packet_len_opts:
2598 peer_1.mk_tunnel_header(self.pg1, is_ip6=is_ip6)
2599 / Wireguard(message_type=4, reserved_zero=0)
2600 / WireguardTransport(
2601 receiver_index=peer_1.sender,
2603 encrypted_encapsulated_packet=peer_1.encrypt_transport(
2604 ip_header / UDP(sport=222, dport=223) / Raw(b"\xfe" * l)
2608 rxs = self.send_and_expect(self.pg1, txs, self.pg0)
2610 # verify decrypted packets
2611 for i, l in enumerate(packet_len_opts):
2613 self.assertEqual(rxs[i][IPv6].dst, self.pg0.remote_ip6)
2614 self.assertEqual(rxs[i][IPv6].hlim, ip_header.hlim - 1)
2616 self.assertEqual(rxs[i][IP].dst, self.pg0.remote_ip4)
2617 self.assertEqual(rxs[i][IP].ttl, ip_header.ttl - 1)
2618 self.assertEqual(len(rxs[i][Raw]), l)
2619 self.assertEqual(bytes(rxs[i][Raw]), b"\xfe" * l)
2621 # prepare and send packets that will be rewritten into the wg interface
2622 # expect data packets sent
2624 ip_header = IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
2626 ip_header = IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
2630 4500, # three buffers
2631 1980 if is_ip6 else 2000, # no free space to write auth tag
2634 for l in packet_len_opts:
2636 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2638 / UDP(sport=555, dport=556)
2641 rxs = self.send_and_expect(self.pg0, txs, self.pg1)
2643 # verify the data packets
2644 rxs_decrypted = peer_1.validate_encapped(
2645 rxs, ip_header, is_tunnel_ip6=is_ip6, is_transport_ip6=is_ip6
2648 for i, l in enumerate(packet_len_opts):
2649 self.assertEqual(len(rxs_decrypted[i][Raw]), l)
2650 self.assertEqual(bytes(rxs_decrypted[i][Raw]), b"\xfe" * l)
2653 r1.remove_vpp_config()
2654 peer_1.remove_vpp_config()
2655 wg0.remove_vpp_config()
2657 def test_wg_large_packet_v4_sync(self):
2658 """Large packet (v4, sync)"""
2659 self._test_wg_large_packet_tmpl(is_async=False, is_ip6=False)
2661 def test_wg_large_packet_v6_sync(self):
2662 """Large packet (v6, sync)"""
2663 self._test_wg_large_packet_tmpl(is_async=False, is_ip6=True)
2665 def test_wg_large_packet_v4_async(self):
2666 """Large packet (v4, async)"""
2667 self._test_wg_large_packet_tmpl(is_async=True, is_ip6=False)
2669 def test_wg_large_packet_v6_async(self):
2670 """Large packet (v6, async)"""
2671 self._test_wg_large_packet_tmpl(is_async=True, is_ip6=True)
2673 def test_wg_lack_of_buf_headroom(self):
2674 """Lack of buffer's headroom (v6 vxlan over v6 wg)"""
2677 # create wg interface
2678 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
2682 self.pg_enable_capture(self.pg_interfaces)
2687 self, wg0, self.pg1.remote_ip6, port + 1, ["::/0"]
2689 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2691 # create a route to enable communication between wg interface addresses
2693 self, wg0.remote_ip6, 128, [VppRoutePath("0.0.0.0", wg0.sw_if_index)]
2696 # wait for the peer to send a handshake initiation
2697 rxs = self.pg1.get_capture(1, timeout=2)
2699 # prepare and send a handshake response
2700 # expect a keepalive message
2701 resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=True)
2702 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
2704 # verify the keepalive message
2705 b = peer_1.decrypt_transport(rxs[0], is_ip6=True)
2706 self.assertEqual(0, len(b))
2708 # create vxlan interface over the wg interface
2709 vxlan0 = VppVxlanTunnel(self, src=wg0.local_ip6, dst=wg0.remote_ip6, vni=1111)
2710 vxlan0.add_vpp_config()
2712 # create bridge domain
2713 bd1 = VppBridgeDomain(self, bd_id=1)
2714 bd1.add_vpp_config()
2716 # add the vxlan interface and pg0 to the bridge domain
2718 VppBridgeDomainPort(self, bd1, vxlan0).add_vpp_config(),
2719 VppBridgeDomainPort(self, bd1, self.pg0).add_vpp_config(),
2722 # prepare and send packets that will be rewritten into the vxlan interface
2723 # expect they to be rewritten into the wg interface then and data packets sent
2725 Ether(dst="00:00:00:00:00:01", src="00:00:00:00:00:02")
2726 / IPv6(src="::1", dst="::2", hlim=20)
2727 / UDP(sport=1111, dport=1112)
2728 / Raw(b"\xfe" * 1900)
2730 rxs = self.send_and_expect(self.pg0, [tx] * 5, self.pg1)
2732 # verify the data packet
2734 rx_decrypted = IPv6(peer_1.decrypt_transport(rx, is_ip6=True))
2736 self.assertEqual(rx_decrypted[VXLAN].vni, vxlan0.vni)
2737 inner = rx_decrypted[VXLAN].payload
2739 # check the original packet is present
2740 self.assertEqual(inner[IPv6].dst, tx[IPv6].dst)
2741 self.assertEqual(inner[IPv6].hlim, tx[IPv6].hlim)
2742 self.assertEqual(len(inner[Raw]), len(tx[Raw]))
2743 self.assertEqual(bytes(inner[Raw]), bytes(tx[Raw]))
2746 for bdp in bd1_ports:
2747 bdp.remove_vpp_config()
2748 bd1.remove_vpp_config()
2749 vxlan0.remove_vpp_config()
2750 r1.remove_vpp_config()
2751 peer_1.remove_vpp_config()
2752 wg0.remove_vpp_config()
2755 @tag_fixme_vpp_debug
2756 class WireguardHandoffTests(TestWg):
2757 """Wireguard Tests in multi worker setup"""
2759 vpp_worker_count = 2
2761 def test_wg_peer_init(self):
2767 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2771 self.pg_enable_capture(self.pg_interfaces)
2775 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.2.0/24", "10.11.3.0/24"]
2777 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2780 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
2783 # skip the first automatic handshake
2784 self.pg1.get_capture(1, timeout=HANDSHAKE_JITTER)
2786 # send a valid handsake init for which we expect a response
2787 p = peer_1.mk_handshake(self.pg1)
2789 rx = self.send_and_expect(self.pg1, [p], self.pg1)
2791 peer_1.consume_response(rx[0])
2793 # send a data packet from the peer through the tunnel
2794 # this completes the handshake and pins the peer to worker 0
2796 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
2797 / UDP(sport=222, dport=223)
2800 d = peer_1.encrypt_transport(p)
2801 p = peer_1.mk_tunnel_header(self.pg1) / (
2802 Wireguard(message_type=4, reserved_zero=0)
2803 / WireguardTransport(
2804 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
2807 rxs = self.send_and_expect(self.pg1, [p], self.pg0, worker=0)
2810 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
2811 self.assertEqual(rx[IP].ttl, 19)
2813 # send a packets that are routed into the tunnel
2814 # and pins the peer tp worker 1
2816 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2817 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
2818 / UDP(sport=555, dport=556)
2821 rxs = self.send_and_expect(self.pg0, pe * 255, self.pg1, worker=1)
2822 peer_1.validate_encapped(rxs, pe)
2824 # send packets into the tunnel, from the other worker
2827 peer_1.mk_tunnel_header(self.pg1)
2828 / Wireguard(message_type=4, reserved_zero=0)
2829 / WireguardTransport(
2830 receiver_index=peer_1.sender,
2832 encrypted_encapsulated_packet=peer_1.encrypt_transport(
2834 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
2835 / UDP(sport=222, dport=223)
2841 for ii in range(255)
2844 rxs = self.send_and_expect(self.pg1, p, self.pg0, worker=1)
2847 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
2848 self.assertEqual(rx[IP].ttl, 19)
2850 # send a packets that are routed into the tunnel
2852 rxs = self.send_and_expect(self.pg0, pe * 255, self.pg1, worker=0)
2854 peer_1.validate_encapped(rxs, pe)
2856 r1.remove_vpp_config()
2857 peer_1.remove_vpp_config()
2858 wg0.remove_vpp_config()
2860 @unittest.skip("test disabled")
2861 def test_wg_multi_interface(self):
2862 """Multi-tunnel on the same port"""
2866 "wireguard" in config.excluded_plugins, "Exclude Wireguard plugin tests"
2869 class TestWgFIB(VppTestCase):
2870 """Wireguard FIB Test Case"""
2873 def setUpClass(cls):
2874 super(TestWgFIB, cls).setUpClass()
2877 def tearDownClass(cls):
2878 super(TestWgFIB, cls).tearDownClass()
2881 super(TestWgFIB, self).setUp()
2883 self.create_pg_interfaces(range(2))
2885 for i in self.pg_interfaces:
2890 for i in self.pg_interfaces:
2893 super(TestWgFIB, self).tearDown()
2895 def test_wg_fib_tracking(self):
2899 # create wg interface
2900 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2904 self.pg_enable_capture(self.pg_interfaces)
2909 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
2911 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2913 # create a route to rewrite traffic into the wg interface
2915 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
2918 # resolve ARP and expect the adjacency to update
2919 self.pg1.resolve_arp()
2921 # wait for the peer to send a handshake initiation
2922 rxs = self.pg1.get_capture(2, timeout=6)
2924 # prepare and send a handshake response
2925 # expect a keepalive message
2926 resp = peer_1.consume_init(rxs[1], self.pg1)
2927 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
2929 # verify the keepalive message
2930 b = peer_1.decrypt_transport(rxs[0])
2931 self.assertEqual(0, len(b))
2933 # prepare and send a packet that will be rewritten into the wg interface
2934 # expect a data packet sent
2936 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2937 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
2938 / UDP(sport=555, dport=556)
2941 rxs = self.send_and_expect(self.pg0, [p], self.pg1)
2943 # verify the data packet
2944 peer_1.validate_encapped(rxs, p)
2947 r1.remove_vpp_config()
2948 peer_1.remove_vpp_config()
2949 wg0.remove_vpp_config()