8 from hashlib import blake2s
9 from scapy.packet import Packet
10 from scapy.packet import Raw
11 from scapy.layers.l2 import Ether, ARP
12 from scapy.layers.inet import IP, UDP
13 from scapy.layers.inet6 import IPv6
14 from scapy.contrib.wireguard import (
21 from cryptography.hazmat.primitives.asymmetric.x25519 import (
25 from cryptography.hazmat.primitives.serialization import (
31 from cryptography.hazmat.primitives.hashes import BLAKE2s, Hash
32 from cryptography.hazmat.primitives.hmac import HMAC
33 from cryptography.hazmat.backends import default_backend
34 from noise.connection import NoiseConnection, Keypair
36 from Crypto.Cipher import ChaCha20_Poly1305
37 from Crypto.Random import get_random_bytes
39 from vpp_ipip_tun_interface import VppIpIpTunInterface
40 from vpp_interface import VppInterface
41 from vpp_ip_route import VppIpRoute, VppRoutePath
42 from vpp_object import VppObject
43 from vpp_papi import VppEnum
44 from framework import VppTestCase
45 from re import compile
48 """ TestWg is a subclass of VPPTestCase classes.
55 def private_key_bytes(k):
56 return k.private_bytes(Encoding.Raw, PrivateFormat.Raw, NoEncryption())
59 def public_key_bytes(k):
60 return k.public_bytes(Encoding.Raw, PublicFormat.Raw)
63 def get_field_bytes(pkt, name):
64 fld, val = pkt.getfield_and_val(name)
65 return fld.i2m(pkt, val)
68 class VppWgInterface(VppInterface):
70 VPP WireGuard interface
73 def __init__(self, test, src, port):
74 super(VppWgInterface, self).__init__(test)
78 self.private_key = X25519PrivateKey.generate()
79 self.public_key = self.private_key.public_key()
81 # cookie related params
82 self.cookie_key = blake2s(b"cookie--" + self.public_key_bytes()).digest()
84 def public_key_bytes(self):
85 return public_key_bytes(self.public_key)
87 def private_key_bytes(self):
88 return private_key_bytes(self.private_key)
90 def add_vpp_config(self):
91 r = self.test.vapi.wireguard_interface_create(
93 "user_instance": 0xFFFFFFFF,
96 "private_key": private_key_bytes(self.private_key),
97 "generate_key": False,
100 self.set_sw_if_index(r.sw_if_index)
101 self.test.registry.register(self, self.test.logger)
104 def remove_vpp_config(self):
105 self.test.vapi.wireguard_interface_delete(sw_if_index=self._sw_if_index)
107 def query_vpp_config(self):
108 ts = self.test.vapi.wireguard_interface_dump(sw_if_index=0xFFFFFFFF)
111 t.interface.sw_if_index == self._sw_if_index
112 and str(t.interface.src_ip) == self.src
113 and t.interface.port == self.port
114 and t.interface.private_key == private_key_bytes(self.private_key)
119 def want_events(self, peer_index=0xFFFFFFFF):
120 self.test.vapi.want_wireguard_peer_events(
123 sw_if_index=self._sw_if_index,
124 peer_index=peer_index,
127 def wait_events(self, expect, peers, timeout=5):
128 for i in range(len(peers)):
129 rv = self.test.vapi.wait_for_event(timeout, "wireguard_peer_event")
130 self.test.assertEqual(rv.peer_index, peers[i])
131 self.test.assertEqual(rv.flags, expect)
134 return self.object_id()
137 return "wireguard-%d" % self._sw_if_index
140 def find_route(test, prefix, is_ip6, table_id=0):
141 routes = test.vapi.ip_route_dump(table_id, is_ip6)
144 if table_id == e.route.table_id and str(e.route.prefix) == str(prefix):
149 NOISE_HANDSHAKE_NAME = b"Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s"
150 NOISE_IDENTIFIER_NAME = b"WireGuard v1 zx2c4 Jason@zx2c4.com"
152 HANDSHAKE_COUNTING_INTERVAL = 0.5
153 UNDER_LOAD_INTERVAL = 1.0
154 HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD = 40
155 HANDSHAKE_NUM_BEFORE_RATELIMITING = 5
158 class VppWgPeer(VppObject):
159 def __init__(self, test, itf, endpoint, port, allowed_ips, persistent_keepalive=15):
162 self.endpoint = endpoint
164 self.allowed_ips = allowed_ips
165 self.persistent_keepalive = persistent_keepalive
167 # remote peer's public
168 self.private_key = X25519PrivateKey.generate()
169 self.public_key = self.private_key.public_key()
171 # cookie related params
172 self.cookie_key = blake2s(b"cookie--" + self.public_key_bytes()).digest()
173 self.last_sent_cookie = None
174 self.last_mac1 = None
175 self.last_received_cookie = None
177 self.noise = NoiseConnection.from_name(NOISE_HANDSHAKE_NAME)
179 def add_vpp_config(self, is_ip6=False):
180 rv = self._test.vapi.wireguard_peer_add(
182 "public_key": self.public_key_bytes(),
184 "endpoint": self.endpoint,
185 "n_allowed_ips": len(self.allowed_ips),
186 "allowed_ips": self.allowed_ips,
187 "sw_if_index": self.itf.sw_if_index,
188 "persistent_keepalive": self.persistent_keepalive,
191 self.index = rv.peer_index
192 self.receiver_index = self.index + 1
193 self._test.registry.register(self, self._test.logger)
196 def remove_vpp_config(self):
197 self._test.vapi.wireguard_peer_remove(peer_index=self.index)
200 return "wireguard-peer-%s" % self.index
202 def public_key_bytes(self):
203 return public_key_bytes(self.public_key)
205 def query_vpp_config(self):
206 peers = self._test.vapi.wireguard_peers_dump()
210 p.peer.public_key == self.public_key_bytes()
211 and p.peer.port == self.port
212 and str(p.peer.endpoint) == self.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)
380 self._test.assertEqual(p[IPv6].src, self.itf.src)
381 self._test.assertEqual(p[IPv6].dst, self.endpoint)
382 self._test.assertEqual(p[UDP].sport, self.itf.port)
383 self._test.assertEqual(p[UDP].dport, self.port)
384 self._test.assert_packet_checksums_valid(p)
386 def consume_init(self, p, tx_itf, is_ip6=False, is_mac2=False):
387 self.noise.set_as_responder()
388 self.noise_init(self.itf.public_key)
389 self.verify_header(p, is_ip6)
391 init = Wireguard(p[Raw])
393 self._test.assertEqual(init[Wireguard].message_type, 1)
394 self._test.assertEqual(init[Wireguard].reserved_zero, 0)
396 self.sender = init[WireguardInitiation].sender_index
398 # validate the mac1 hash
399 mac_key = blake2s(b"mac1----" + public_key_bytes(self.public_key)).digest()
400 mac1 = blake2s(bytes(init)[0:-32], digest_size=16, key=mac_key).digest()
401 self._test.assertEqual(init[WireguardInitiation].mac1, mac1)
403 # validate the mac2 hash
405 self._test.assertNotEqual(init[WireguardInitiation].mac2, bytes([0] * 16))
406 self._test.assertNotEqual(self.last_sent_cookie, None)
408 bytes(init)[0:-16], digest_size=16, key=self.last_sent_cookie
410 self._test.assertEqual(init[WireguardInitiation].mac2, mac2)
411 self.last_sent_cookie = None
413 self._test.assertEqual(init[WireguardInitiation].mac2, bytes([0] * 16))
415 # this passes only unencrypted_ephemeral, encrypted_static,
416 # encrypted_timestamp fields of the init
417 payload = self.noise.read_message(bytes(init)[8:-32])
420 b = self.noise.write_message()
421 mac_key = blake2s(b"mac1----" + public_key_bytes(self.itf.public_key)).digest()
422 resp = Wireguard(message_type=2, reserved_zero=0) / WireguardResponse(
423 sender_index=self.receiver_index,
424 receiver_index=self.sender,
425 unencrypted_ephemeral=b[0:32],
426 encrypted_nothing=b[32:],
428 mac1 = blake2s(bytes(resp)[:-32], digest_size=16, key=mac_key).digest()
429 resp[WireguardResponse].mac1 = mac1
430 self.last_mac1 = mac1
432 resp = self.mk_tunnel_header(tx_itf, is_ip6) / resp
433 self._test.assertTrue(self.noise.handshake_finished)
437 def consume_response(self, p, is_ip6=False):
438 self.verify_header(p, is_ip6)
440 resp = Wireguard(p[Raw])
442 self._test.assertEqual(resp[Wireguard].message_type, 2)
443 self._test.assertEqual(resp[Wireguard].reserved_zero, 0)
444 self._test.assertEqual(
445 resp[WireguardResponse].receiver_index, self.receiver_index
448 self.sender = resp[Wireguard].sender_index
450 payload = self.noise.read_message(bytes(resp)[12:60])
451 self._test.assertEqual(payload, b"")
452 self._test.assertTrue(self.noise.handshake_finished)
454 def decrypt_transport(self, p, is_ip6=False):
455 self.verify_header(p, is_ip6)
457 p = Wireguard(p[Raw])
458 self._test.assertEqual(p[Wireguard].message_type, 4)
459 self._test.assertEqual(p[Wireguard].reserved_zero, 0)
460 self._test.assertEqual(
461 p[WireguardTransport].receiver_index, self.receiver_index
464 d = self.noise.decrypt(p[WireguardTransport].encrypted_encapsulated_packet)
467 def encrypt_transport(self, p):
468 return self.noise.encrypt(bytes(p))
470 def validate_encapped(self, rxs, tx, is_ip6=False):
473 rx = IP(self.decrypt_transport(rx))
475 # chech the oringial packet is present
476 self._test.assertEqual(rx[IP].dst, tx[IP].dst)
477 self._test.assertEqual(rx[IP].ttl, tx[IP].ttl - 1)
479 rx = IPv6(self.decrypt_transport(rx))
481 # chech the oringial packet is present
482 self._test.assertEqual(rx[IPv6].dst, tx[IPv6].dst)
483 self._test.assertEqual(rx[IPv6].ttl, tx[IPv6].ttl - 1)
485 def want_events(self):
486 self._test.vapi.want_wireguard_peer_events(
489 peer_index=self.index,
490 sw_if_index=self.itf.sw_if_index,
493 def wait_event(self, expect, timeout=5):
494 rv = self._test.vapi.wait_for_event(timeout, "wireguard_peer_event")
495 self._test.assertEqual(rv.flags, expect)
496 self._test.assertEqual(rv.peer_index, self.index)
499 class TestWg(VppTestCase):
500 """Wireguard Test Case"""
502 error_str = compile(r"Error")
504 wg4_output_node_name = "/err/wg4-output-tun/"
505 wg4_input_node_name = "/err/wg4-input/"
506 wg6_output_node_name = "/err/wg6-output-tun/"
507 wg6_input_node_name = "/err/wg6-input/"
508 kp4_error = wg4_output_node_name + "Keypair error"
509 mac4_error = wg4_input_node_name + "Invalid MAC handshake"
510 peer4_in_err = wg4_input_node_name + "Peer error"
511 peer4_out_err = wg4_output_node_name + "Peer error"
512 kp6_error = wg6_output_node_name + "Keypair error"
513 mac6_error = wg6_input_node_name + "Invalid MAC handshake"
514 peer6_in_err = wg6_input_node_name + "Peer error"
515 peer6_out_err = wg6_output_node_name + "Peer error"
516 cookie_dec4_err = wg4_input_node_name + "Failed during Cookie decryption"
517 cookie_dec6_err = wg6_input_node_name + "Failed during Cookie decryption"
518 ratelimited4_err = wg4_input_node_name + "Handshake ratelimited"
519 ratelimited6_err = wg6_input_node_name + "Handshake ratelimited"
523 super(TestWg, cls).setUpClass()
525 cls.create_pg_interfaces(range(3))
526 for i in cls.pg_interfaces:
534 super(TestWg, cls).tearDownClass()
538 def tearDownClass(cls):
539 super(TestWg, cls).tearDownClass()
542 super(VppTestCase, self).setUp()
543 self.base_kp4_err = self.statistics.get_err_counter(self.kp4_error)
544 self.base_mac4_err = self.statistics.get_err_counter(self.mac4_error)
545 self.base_peer4_in_err = self.statistics.get_err_counter(self.peer4_in_err)
546 self.base_peer4_out_err = self.statistics.get_err_counter(self.peer4_out_err)
547 self.base_kp6_err = self.statistics.get_err_counter(self.kp6_error)
548 self.base_mac6_err = self.statistics.get_err_counter(self.mac6_error)
549 self.base_peer6_in_err = self.statistics.get_err_counter(self.peer6_in_err)
550 self.base_peer6_out_err = self.statistics.get_err_counter(self.peer6_out_err)
551 self.base_cookie_dec4_err = self.statistics.get_err_counter(
554 self.base_cookie_dec6_err = self.statistics.get_err_counter(
557 self.base_ratelimited4_err = self.statistics.get_err_counter(
558 self.ratelimited4_err
560 self.base_ratelimited6_err = self.statistics.get_err_counter(
561 self.ratelimited6_err
564 def test_wg_interface(self):
565 """Simple interface creation"""
569 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
571 self.logger.info(self.vapi.cli("sh int"))
574 wg0.remove_vpp_config()
576 def test_handshake_hash(self):
577 """test hashing an init message"""
578 # a init packet generated by linux given the key below
601 b = bytearray.fromhex(h)
604 pubb = base64.b64decode("aRuHFTTxICIQNefp05oKWlJv3zgKxb8+WW7JJMh0jyM=")
605 pub = X25519PublicKey.from_public_bytes(pubb)
607 self.assertEqual(pubb, public_key_bytes(pub))
609 # strip the macs and build a new packet
611 mac_key = blake2s(b"mac1----" + public_key_bytes(pub)).digest()
612 init += blake2s(init, digest_size=16, key=mac_key).digest()
615 act = Wireguard(init)
617 self.assertEqual(tgt, act)
619 def _test_wg_send_cookie_tmpl(self, is_resp, is_ip6):
622 # create wg interface
624 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
628 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
632 self.pg_enable_capture(self.pg_interfaces)
638 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
642 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
644 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
647 # prepare and send a handshake initiation
648 # expect the peer to send a handshake response
649 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
650 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
652 # wait for the peer to send a handshake initiation
653 rxs = self.pg1.get_capture(1, timeout=2)
655 # prepare and send a wrong cookie reply
656 # expect no replies and the cookie error incremented
657 cookie = peer_1.mk_cookie(rxs[0], self.pg1, is_resp=is_resp, is_ip6=is_ip6)
658 cookie.nonce = b"1234567890"
659 self.send_and_assert_no_replies(self.pg1, [cookie], timeout=0.1)
662 self.base_cookie_dec6_err + 1,
663 self.statistics.get_err_counter(self.cookie_dec6_err),
667 self.base_cookie_dec4_err + 1,
668 self.statistics.get_err_counter(self.cookie_dec4_err),
671 # prepare and send a correct cookie reply
672 cookie = peer_1.mk_cookie(rxs[0], self.pg1, is_resp=is_resp, is_ip6=is_ip6)
673 self.pg_send(self.pg1, [cookie])
675 # wait for the peer to send a handshake initiation with mac2 set
676 rxs = self.pg1.get_capture(1, timeout=6)
678 # verify the initiation and its mac2
679 peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6, is_mac2=True)
682 peer_1.remove_vpp_config()
683 wg0.remove_vpp_config()
685 def test_wg_send_cookie_on_init_v4(self):
686 """Send cookie on handshake initiation (v4)"""
687 self._test_wg_send_cookie_tmpl(is_resp=False, is_ip6=False)
689 def test_wg_send_cookie_on_init_v6(self):
690 """Send cookie on handshake initiation (v6)"""
691 self._test_wg_send_cookie_tmpl(is_resp=False, is_ip6=True)
693 def test_wg_send_cookie_on_resp_v4(self):
694 """Send cookie on handshake response (v4)"""
695 self._test_wg_send_cookie_tmpl(is_resp=True, is_ip6=False)
697 def test_wg_send_cookie_on_resp_v6(self):
698 """Send cookie on handshake response (v6)"""
699 self._test_wg_send_cookie_tmpl(is_resp=True, is_ip6=True)
701 def _test_wg_receive_cookie_tmpl(self, is_resp, is_ip6):
704 # create wg interface
706 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
710 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
714 self.pg_enable_capture(self.pg_interfaces)
720 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
724 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
726 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
729 # wait for the peer to send a handshake initiation
730 rxs = self.pg1.get_capture(1, timeout=2)
731 # prepare and send a bunch of handshake responses
732 # expect to switch to under load state
733 resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6)
734 txs = [resp] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
735 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
736 # reset noise to be able to turn into initiator later
739 # prepare and send a bunch of handshake initiations
740 # expect to switch to under load state
741 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
742 txs = [init] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
743 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
745 # expect the peer to send a cookie reply
746 peer_1.consume_cookie(rxs[-1], is_ip6=is_ip6)
748 # prepare and send a handshake initiation with wrong mac2
749 # expect a cookie reply
750 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
751 init.mac2 = b"1234567890"
752 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
753 peer_1.consume_cookie(rxs[0], is_ip6=is_ip6)
755 # prepare and send a handshake initiation with correct mac2
756 # expect a handshake response
757 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
758 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
760 # verify the response
761 peer_1.consume_response(rxs[0], is_ip6=is_ip6)
763 # clear up under load state
764 self.sleep(UNDER_LOAD_INTERVAL)
767 peer_1.remove_vpp_config()
768 wg0.remove_vpp_config()
770 def test_wg_receive_cookie_on_init_v4(self):
771 """Receive cookie on handshake initiation (v4)"""
772 self._test_wg_receive_cookie_tmpl(is_resp=False, is_ip6=False)
774 def test_wg_receive_cookie_on_init_v6(self):
775 """Receive cookie on handshake initiation (v6)"""
776 self._test_wg_receive_cookie_tmpl(is_resp=False, is_ip6=True)
778 def test_wg_receive_cookie_on_resp_v4(self):
779 """Receive cookie on handshake response (v4)"""
780 self._test_wg_receive_cookie_tmpl(is_resp=True, is_ip6=False)
782 def test_wg_receive_cookie_on_resp_v6(self):
783 """Receive cookie on handshake response (v6)"""
784 self._test_wg_receive_cookie_tmpl(is_resp=True, is_ip6=True)
786 def test_wg_under_load_interval(self):
787 """Under load interval"""
790 # create wg interface
791 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
795 self.pg_enable_capture(self.pg_interfaces)
800 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
802 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
804 # prepare and send a bunch of handshake initiations
805 # expect to switch to under load state
806 init = peer_1.mk_handshake(self.pg1)
807 txs = [init] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
808 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
810 # expect the peer to send a cookie reply
811 peer_1.consume_cookie(rxs[-1])
813 # sleep till the next counting interval
814 # expect under load state is still active
815 self.sleep(HANDSHAKE_COUNTING_INTERVAL)
817 # prepare and send a handshake initiation with wrong mac2
818 # expect a cookie reply
819 init = peer_1.mk_handshake(self.pg1)
820 init.mac2 = b"1234567890"
821 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
822 peer_1.consume_cookie(rxs[0])
824 # sleep till the end of being under load
825 # expect under load state is over
826 self.sleep(UNDER_LOAD_INTERVAL - HANDSHAKE_COUNTING_INTERVAL)
828 # prepare and send a handshake initiation with wrong mac2
829 # expect a handshake response
830 init = peer_1.mk_handshake(self.pg1)
831 init.mac2 = b"1234567890"
832 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
834 # verify the response
835 peer_1.consume_response(rxs[0])
838 peer_1.remove_vpp_config()
839 wg0.remove_vpp_config()
841 def _test_wg_handshake_ratelimiting_tmpl(self, is_ip6):
844 # create wg interface
846 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
850 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
854 self.pg_enable_capture(self.pg_interfaces)
860 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
864 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
866 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
868 # prepare and send a bunch of handshake initiations
869 # expect to switch to under load state
870 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
871 txs = [init] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
872 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
874 # expect the peer to send a cookie reply
875 peer_1.consume_cookie(rxs[-1], is_ip6=is_ip6)
877 # prepare and send a bunch of handshake initiations with correct mac2
878 # expect a handshake response and then ratelimiting
880 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
881 txs = [init] * (HANDSHAKE_NUM_BEFORE_RATELIMITING + NUM_TO_REJECT)
882 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
886 self.base_ratelimited6_err + NUM_TO_REJECT,
887 self.statistics.get_err_counter(self.ratelimited6_err),
891 self.base_ratelimited4_err + NUM_TO_REJECT,
892 self.statistics.get_err_counter(self.ratelimited4_err),
895 # verify the response
896 peer_1.consume_response(rxs[0], is_ip6=is_ip6)
898 # clear up under load state
899 self.sleep(UNDER_LOAD_INTERVAL)
902 peer_1.remove_vpp_config()
903 wg0.remove_vpp_config()
905 def test_wg_handshake_ratelimiting_v4(self):
906 """Handshake ratelimiting (v4)"""
907 self._test_wg_handshake_ratelimiting_tmpl(is_ip6=False)
909 def test_wg_handshake_ratelimiting_v6(self):
910 """Handshake ratelimiting (v6)"""
911 self._test_wg_handshake_ratelimiting_tmpl(is_ip6=True)
913 def test_wg_handshake_ratelimiting_multi_peer(self):
914 """Handshake ratelimiting (multiple peer)"""
917 # create wg interface
918 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
922 self.pg_enable_capture(self.pg_interfaces)
927 self.pg1.generate_remote_hosts(NUM_PEERS)
928 self.pg1.configure_ipv4_neighbors()
931 self, wg0, self.pg1.remote_hosts[0].ip4, port + 1, ["10.11.3.0/24"]
934 self, wg0, self.pg1.remote_hosts[1].ip4, port + 1, ["10.11.4.0/24"]
936 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 2)
938 # (peer_1) prepare and send a bunch of handshake initiations
939 # expect not to switch to under load state
940 init_1 = peer_1.mk_handshake(self.pg1)
941 txs = [init_1] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
942 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
944 # (peer_1) expect the peer to send a handshake response
945 peer_1.consume_response(rxs[0])
948 # (peer_1) send another bunch of handshake initiations
949 # expect to switch to under load state
950 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
952 # (peer_1) expect the peer to send a cookie reply
953 peer_1.consume_cookie(rxs[-1])
955 # (peer_2) prepare and send a handshake initiation
956 # expect a cookie reply
957 init_2 = peer_2.mk_handshake(self.pg1)
958 rxs = self.send_and_expect(self.pg1, [init_2], self.pg1)
959 peer_2.consume_cookie(rxs[0])
961 # (peer_1) prepare and send a bunch of handshake initiations with correct mac2
962 # expect no ratelimiting and a handshake response
963 init_1 = peer_1.mk_handshake(self.pg1)
964 txs = [init_1] * HANDSHAKE_NUM_BEFORE_RATELIMITING
965 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
967 self.base_ratelimited4_err,
968 self.statistics.get_err_counter(self.ratelimited4_err),
971 # (peer_1) verify the response
972 peer_1.consume_response(rxs[0])
975 # (peer_1) send another two handshake initiations with correct mac2
976 # expect ratelimiting
977 # (peer_2) prepare and send a handshake initiation with correct mac2
978 # expect no ratelimiting and a handshake response
979 init_2 = peer_2.mk_handshake(self.pg1)
980 txs = [init_1, init_2, init_1]
981 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
983 # (peer_1) verify ratelimiting
985 self.base_ratelimited4_err + 2,
986 self.statistics.get_err_counter(self.ratelimited4_err),
989 # (peer_2) verify the response
990 peer_2.consume_response(rxs[0])
992 # clear up under load state
993 self.sleep(UNDER_LOAD_INTERVAL)
996 peer_1.remove_vpp_config()
997 peer_2.remove_vpp_config()
998 wg0.remove_vpp_config()
1000 def test_wg_peer_resp(self):
1001 """Send handshake response"""
1005 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1009 self.pg_enable_capture(self.pg_interfaces)
1013 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
1015 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1018 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1021 # wait for the peer to send a handshake
1022 rx = self.pg1.get_capture(1, timeout=2)
1024 # consume the handshake in the noise protocol and
1025 # generate the response
1026 resp = peer_1.consume_init(rx[0], self.pg1)
1028 # send the response, get keepalive
1029 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1032 b = peer_1.decrypt_transport(rx)
1033 self.assertEqual(0, len(b))
1035 # send a packets that are routed into the tunnel
1037 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1038 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1039 / UDP(sport=555, dport=556)
1043 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1045 peer_1.validate_encapped(rxs, p)
1047 # send packets into the tunnel, expect to receive them on
1051 peer_1.mk_tunnel_header(self.pg1)
1052 / Wireguard(message_type=4, reserved_zero=0)
1053 / WireguardTransport(
1054 receiver_index=peer_1.sender,
1056 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1058 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1059 / UDP(sport=222, dport=223)
1065 for ii in range(255)
1068 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1071 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1072 self.assertEqual(rx[IP].ttl, 19)
1074 r1.remove_vpp_config()
1075 peer_1.remove_vpp_config()
1076 wg0.remove_vpp_config()
1078 def test_wg_peer_v4o4(self):
1084 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1089 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
1091 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1094 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1097 self, "20.22.3.0", 24, [VppRoutePath("20.22.3.1", wg0.sw_if_index)]
1100 # route a packet into the wg interface
1101 # use the allowed-ip prefix
1102 # this is dropped because the peer is not initiated
1104 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1105 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1106 / UDP(sport=555, dport=556)
1109 self.send_and_assert_no_replies(self.pg0, [p])
1111 self.base_kp4_err + 1, self.statistics.get_err_counter(self.kp4_error)
1114 # route a packet into the wg interface
1115 # use a not allowed-ip prefix
1116 # this is dropped because there is no matching peer
1118 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1119 / IP(src=self.pg0.remote_ip4, dst="20.22.3.2")
1120 / UDP(sport=555, dport=556)
1123 self.send_and_assert_no_replies(self.pg0, [p])
1125 self.base_peer4_out_err + 1,
1126 self.statistics.get_err_counter(self.peer4_out_err),
1129 # send a handsake from the peer with an invalid MAC
1130 p = peer_1.mk_handshake(self.pg1)
1131 p[WireguardInitiation].mac1 = b"foobar"
1132 self.send_and_assert_no_replies(self.pg1, [p])
1134 self.base_mac4_err + 1, self.statistics.get_err_counter(self.mac4_error)
1137 # send a handsake from the peer but signed by the wrong key.
1138 p = peer_1.mk_handshake(
1139 self.pg1, False, X25519PrivateKey.generate().public_key()
1141 self.send_and_assert_no_replies(self.pg1, [p])
1143 self.base_peer4_in_err + 1,
1144 self.statistics.get_err_counter(self.peer4_in_err),
1147 # send a valid handsake init for which we expect a response
1148 p = peer_1.mk_handshake(self.pg1)
1150 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1152 peer_1.consume_response(rx[0])
1154 # route a packet into the wg interface
1155 # this is dropped because the peer is still not initiated
1157 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1158 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1159 / UDP(sport=555, dport=556)
1162 self.send_and_assert_no_replies(self.pg0, [p])
1164 self.base_kp4_err + 2, self.statistics.get_err_counter(self.kp4_error)
1167 # send a data packet from the peer through the tunnel
1168 # this completes the handshake
1170 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1171 / UDP(sport=222, dport=223)
1174 d = peer_1.encrypt_transport(p)
1175 p = peer_1.mk_tunnel_header(self.pg1) / (
1176 Wireguard(message_type=4, reserved_zero=0)
1177 / WireguardTransport(
1178 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1181 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1184 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1185 self.assertEqual(rx[IP].ttl, 19)
1187 # send a packets that are routed into the tunnel
1189 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1190 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1191 / UDP(sport=555, dport=556)
1195 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1198 rx = IP(peer_1.decrypt_transport(rx))
1200 # chech the oringial packet is present
1201 self.assertEqual(rx[IP].dst, p[IP].dst)
1202 self.assertEqual(rx[IP].ttl, p[IP].ttl - 1)
1204 # send packets into the tunnel, expect to receive them on
1208 peer_1.mk_tunnel_header(self.pg1)
1209 / Wireguard(message_type=4, reserved_zero=0)
1210 / WireguardTransport(
1211 receiver_index=peer_1.sender,
1213 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1215 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1216 / UDP(sport=222, dport=223)
1222 for ii in range(255)
1225 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1228 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1229 self.assertEqual(rx[IP].ttl, 19)
1231 r1.remove_vpp_config()
1232 r2.remove_vpp_config()
1233 peer_1.remove_vpp_config()
1234 wg0.remove_vpp_config()
1236 def test_wg_peer_v6o6(self):
1242 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1247 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
1248 ).add_vpp_config(True)
1249 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1252 self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
1255 self, "22::3:0", 112, [VppRoutePath("22::3:1", wg0.sw_if_index)]
1258 # route a packet into the wg interface
1259 # use the allowed-ip prefix
1260 # this is dropped because the peer is not initiated
1263 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1264 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1265 / UDP(sport=555, dport=556)
1268 self.send_and_assert_no_replies(self.pg0, [p])
1271 self.base_kp6_err + 1, self.statistics.get_err_counter(self.kp6_error)
1274 # route a packet into the wg interface
1275 # use a not allowed-ip prefix
1276 # this is dropped because there is no matching peer
1278 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1279 / IPv6(src=self.pg0.remote_ip6, dst="22::3:2")
1280 / UDP(sport=555, dport=556)
1283 self.send_and_assert_no_replies(self.pg0, [p])
1285 self.base_peer6_out_err + 1,
1286 self.statistics.get_err_counter(self.peer6_out_err),
1289 # send a handsake from the peer with an invalid MAC
1290 p = peer_1.mk_handshake(self.pg1, True)
1291 p[WireguardInitiation].mac1 = b"foobar"
1292 self.send_and_assert_no_replies(self.pg1, [p])
1295 self.base_mac6_err + 1, self.statistics.get_err_counter(self.mac6_error)
1298 # send a handsake from the peer but signed by the wrong key.
1299 p = peer_1.mk_handshake(
1300 self.pg1, True, X25519PrivateKey.generate().public_key()
1302 self.send_and_assert_no_replies(self.pg1, [p])
1304 self.base_peer6_in_err + 1,
1305 self.statistics.get_err_counter(self.peer6_in_err),
1308 # send a valid handsake init for which we expect a response
1309 p = peer_1.mk_handshake(self.pg1, True)
1311 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1313 peer_1.consume_response(rx[0], True)
1315 # route a packet into the wg interface
1316 # this is dropped because the peer is still not initiated
1318 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1319 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1320 / UDP(sport=555, dport=556)
1323 self.send_and_assert_no_replies(self.pg0, [p])
1325 self.base_kp6_err + 2, self.statistics.get_err_counter(self.kp6_error)
1328 # send a data packet from the peer through the tunnel
1329 # this completes the handshake
1331 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1332 / UDP(sport=222, dport=223)
1335 d = peer_1.encrypt_transport(p)
1336 p = peer_1.mk_tunnel_header(self.pg1, True) / (
1337 Wireguard(message_type=4, reserved_zero=0)
1338 / WireguardTransport(
1339 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1342 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1345 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1346 self.assertEqual(rx[IPv6].hlim, 19)
1348 # send a packets that are routed into the tunnel
1350 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1351 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1352 / UDP(sport=555, dport=556)
1356 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1359 rx = IPv6(peer_1.decrypt_transport(rx, True))
1361 # chech the oringial packet is present
1362 self.assertEqual(rx[IPv6].dst, p[IPv6].dst)
1363 self.assertEqual(rx[IPv6].hlim, p[IPv6].hlim - 1)
1365 # send packets into the tunnel, expect to receive them on
1369 peer_1.mk_tunnel_header(self.pg1, True)
1370 / Wireguard(message_type=4, reserved_zero=0)
1371 / WireguardTransport(
1372 receiver_index=peer_1.sender,
1374 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1376 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1377 / UDP(sport=222, dport=223)
1383 for ii in range(255)
1386 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1389 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1390 self.assertEqual(rx[IPv6].hlim, 19)
1392 r1.remove_vpp_config()
1393 r2.remove_vpp_config()
1394 peer_1.remove_vpp_config()
1395 wg0.remove_vpp_config()
1397 def test_wg_peer_v6o4(self):
1403 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1408 self, wg0, self.pg1.remote_ip4, port + 1, ["1::3:0/112"]
1409 ).add_vpp_config(True)
1410 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1413 self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
1416 # route a packet into the wg interface
1417 # use the allowed-ip prefix
1418 # this is dropped because the peer is not initiated
1420 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1421 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1422 / UDP(sport=555, dport=556)
1425 self.send_and_assert_no_replies(self.pg0, [p])
1427 self.base_kp6_err + 1, self.statistics.get_err_counter(self.kp6_error)
1430 # send a handsake from the peer with an invalid MAC
1431 p = peer_1.mk_handshake(self.pg1)
1432 p[WireguardInitiation].mac1 = b"foobar"
1433 self.send_and_assert_no_replies(self.pg1, [p])
1436 self.base_mac4_err + 1, self.statistics.get_err_counter(self.mac4_error)
1439 # send a handsake from the peer but signed by the wrong key.
1440 p = peer_1.mk_handshake(
1441 self.pg1, False, X25519PrivateKey.generate().public_key()
1443 self.send_and_assert_no_replies(self.pg1, [p])
1445 self.base_peer4_in_err + 1,
1446 self.statistics.get_err_counter(self.peer4_in_err),
1449 # send a valid handsake init for which we expect a response
1450 p = peer_1.mk_handshake(self.pg1)
1452 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1454 peer_1.consume_response(rx[0])
1456 # route a packet into the wg interface
1457 # this is dropped because the peer is still not initiated
1459 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1460 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1461 / UDP(sport=555, dport=556)
1464 self.send_and_assert_no_replies(self.pg0, [p])
1466 self.base_kp6_err + 2, self.statistics.get_err_counter(self.kp6_error)
1469 # send a data packet from the peer through the tunnel
1470 # this completes the handshake
1472 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1473 / UDP(sport=222, dport=223)
1476 d = peer_1.encrypt_transport(p)
1477 p = peer_1.mk_tunnel_header(self.pg1) / (
1478 Wireguard(message_type=4, reserved_zero=0)
1479 / WireguardTransport(
1480 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1483 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1486 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1487 self.assertEqual(rx[IPv6].hlim, 19)
1489 # send a packets that are routed into the tunnel
1491 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1492 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1493 / UDP(sport=555, dport=556)
1497 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1500 rx = IPv6(peer_1.decrypt_transport(rx))
1502 # chech the oringial packet is present
1503 self.assertEqual(rx[IPv6].dst, p[IPv6].dst)
1504 self.assertEqual(rx[IPv6].hlim, p[IPv6].hlim - 1)
1506 # send packets into the tunnel, expect to receive them on
1510 peer_1.mk_tunnel_header(self.pg1)
1511 / Wireguard(message_type=4, reserved_zero=0)
1512 / WireguardTransport(
1513 receiver_index=peer_1.sender,
1515 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1517 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1518 / UDP(sport=222, dport=223)
1524 for ii in range(255)
1527 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1530 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1531 self.assertEqual(rx[IPv6].hlim, 19)
1533 r1.remove_vpp_config()
1534 peer_1.remove_vpp_config()
1535 wg0.remove_vpp_config()
1537 def test_wg_peer_v4o6(self):
1543 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1548 self, wg0, self.pg1.remote_ip6, port + 1, ["10.11.3.0/24"]
1550 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1553 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1556 # route a packet into the wg interface
1557 # use the allowed-ip prefix
1558 # this is dropped because the peer is not initiated
1560 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1561 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1562 / UDP(sport=555, dport=556)
1565 self.send_and_assert_no_replies(self.pg0, [p])
1567 self.base_kp4_err + 1, self.statistics.get_err_counter(self.kp4_error)
1570 # send a handsake from the peer with an invalid MAC
1571 p = peer_1.mk_handshake(self.pg1, True)
1572 p[WireguardInitiation].mac1 = b"foobar"
1573 self.send_and_assert_no_replies(self.pg1, [p])
1575 self.base_mac6_err + 1, self.statistics.get_err_counter(self.mac6_error)
1578 # send a handsake from the peer but signed by the wrong key.
1579 p = peer_1.mk_handshake(
1580 self.pg1, True, X25519PrivateKey.generate().public_key()
1582 self.send_and_assert_no_replies(self.pg1, [p])
1584 self.base_peer6_in_err + 1,
1585 self.statistics.get_err_counter(self.peer6_in_err),
1588 # send a valid handsake init for which we expect a response
1589 p = peer_1.mk_handshake(self.pg1, True)
1591 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1593 peer_1.consume_response(rx[0], True)
1595 # route a packet into the wg interface
1596 # this is dropped because the peer is still not initiated
1598 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1599 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1600 / UDP(sport=555, dport=556)
1603 self.send_and_assert_no_replies(self.pg0, [p])
1605 self.base_kp4_err + 2, self.statistics.get_err_counter(self.kp4_error)
1608 # send a data packet from the peer through the tunnel
1609 # this completes the handshake
1611 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1612 / UDP(sport=222, dport=223)
1615 d = peer_1.encrypt_transport(p)
1616 p = peer_1.mk_tunnel_header(self.pg1, True) / (
1617 Wireguard(message_type=4, reserved_zero=0)
1618 / WireguardTransport(
1619 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1622 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1625 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1626 self.assertEqual(rx[IP].ttl, 19)
1628 # send a packets that are routed into the tunnel
1630 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1631 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1632 / UDP(sport=555, dport=556)
1636 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1639 rx = IP(peer_1.decrypt_transport(rx, True))
1641 # chech the oringial packet is present
1642 self.assertEqual(rx[IP].dst, p[IP].dst)
1643 self.assertEqual(rx[IP].ttl, p[IP].ttl - 1)
1645 # send packets into the tunnel, expect to receive them on
1649 peer_1.mk_tunnel_header(self.pg1, True)
1650 / Wireguard(message_type=4, reserved_zero=0)
1651 / WireguardTransport(
1652 receiver_index=peer_1.sender,
1654 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1656 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1657 / UDP(sport=222, dport=223)
1663 for ii in range(255)
1666 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1669 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1670 self.assertEqual(rx[IP].ttl, 19)
1672 r1.remove_vpp_config()
1673 peer_1.remove_vpp_config()
1674 wg0.remove_vpp_config()
1676 def test_wg_multi_peer(self):
1677 """multiple peer setup"""
1681 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1682 wg1 = VppWgInterface(self, self.pg2.local_ip4, port + 1).add_vpp_config()
1686 # Check peer counter
1687 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 0)
1689 self.pg_enable_capture(self.pg_interfaces)
1692 # Create many peers on sencond interface
1694 self.pg2.generate_remote_hosts(NUM_PEERS)
1695 self.pg2.configure_ipv4_neighbors()
1696 self.pg1.generate_remote_hosts(NUM_PEERS)
1697 self.pg1.configure_ipv4_neighbors()
1703 for i in range(NUM_PEERS):
1708 self.pg1.remote_hosts[i].ip4,
1710 ["10.0.%d.4/32" % i],
1718 [VppRoutePath(self.pg1.remote_hosts[i].ip4, wg0.sw_if_index)],
1726 self.pg2.remote_hosts[i].ip4,
1728 ["10.100.%d.4/32" % i],
1736 [VppRoutePath(self.pg2.remote_hosts[i].ip4, wg1.sw_if_index)],
1740 self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_PEERS * 2)
1742 self.logger.info(self.vapi.cli("show wireguard peer"))
1743 self.logger.info(self.vapi.cli("show wireguard interface"))
1744 self.logger.info(self.vapi.cli("show adj 37"))
1745 self.logger.info(self.vapi.cli("sh ip fib 172.16.3.17"))
1746 self.logger.info(self.vapi.cli("sh ip fib 10.11.3.0"))
1750 r.remove_vpp_config()
1752 r.remove_vpp_config()
1756 self.assertTrue(p.query_vpp_config())
1757 p.remove_vpp_config()
1759 self.assertTrue(p.query_vpp_config())
1760 p.remove_vpp_config()
1762 wg0.remove_vpp_config()
1763 wg1.remove_vpp_config()
1765 def test_wg_multi_interface(self):
1766 """Multi-tunnel on the same port"""
1769 # Create many wireguard interfaces
1771 self.pg1.generate_remote_hosts(NUM_IFS)
1772 self.pg1.configure_ipv4_neighbors()
1773 self.pg0.generate_remote_hosts(NUM_IFS)
1774 self.pg0.configure_ipv4_neighbors()
1776 # Create interfaces with a peer on each
1780 for i in range(NUM_IFS):
1781 # Use the same port for each interface
1782 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1790 self.pg1.remote_hosts[i].ip4,
1792 ["10.0.%d.0/24" % i],
1801 [VppRoutePath("10.0.%d.4" % i, wg0.sw_if_index)],
1805 self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_IFS)
1807 for i in range(NUM_IFS):
1808 # send a valid handsake init for which we expect a response
1809 p = peers[i].mk_handshake(self.pg1)
1810 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1811 peers[i].consume_response(rx[0])
1813 # send a data packet from the peer through the tunnel
1814 # this completes the handshake
1816 IP(src="10.0.%d.4" % i, dst=self.pg0.remote_hosts[i].ip4, ttl=20)
1817 / UDP(sport=222, dport=223)
1820 d = peers[i].encrypt_transport(p)
1821 p = peers[i].mk_tunnel_header(self.pg1) / (
1822 Wireguard(message_type=4, reserved_zero=0)
1823 / WireguardTransport(
1824 receiver_index=peers[i].sender,
1826 encrypted_encapsulated_packet=d,
1829 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1831 self.assertEqual(rx[IP].dst, self.pg0.remote_hosts[i].ip4)
1832 self.assertEqual(rx[IP].ttl, 19)
1834 # send a packets that are routed into the tunnel
1835 for i in range(NUM_IFS):
1837 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1838 / IP(src=self.pg0.remote_hosts[i].ip4, dst="10.0.%d.4" % i)
1839 / UDP(sport=555, dport=556)
1843 rxs = self.send_and_expect(self.pg0, p * 64, self.pg1)
1846 rx = IP(peers[i].decrypt_transport(rx))
1848 # check the oringial packet is present
1849 self.assertEqual(rx[IP].dst, p[IP].dst)
1850 self.assertEqual(rx[IP].ttl, p[IP].ttl - 1)
1852 # send packets into the tunnel
1853 for i in range(NUM_IFS):
1856 peers[i].mk_tunnel_header(self.pg1)
1857 / Wireguard(message_type=4, reserved_zero=0)
1858 / WireguardTransport(
1859 receiver_index=peers[i].sender,
1861 encrypted_encapsulated_packet=peers[i].encrypt_transport(
1864 src="10.0.%d.4" % i,
1865 dst=self.pg0.remote_hosts[i].ip4,
1868 / UDP(sport=222, dport=223)
1877 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1880 self.assertEqual(rx[IP].dst, self.pg0.remote_hosts[i].ip4)
1881 self.assertEqual(rx[IP].ttl, 19)
1884 r.remove_vpp_config()
1886 p.remove_vpp_config()
1888 i.remove_vpp_config()
1890 def test_wg_event(self):
1893 ESTABLISHED_FLAG = (
1894 VppEnum.vl_api_wireguard_peer_flags_t.WIREGUARD_PEER_ESTABLISHED
1896 DEAD_FLAG = VppEnum.vl_api_wireguard_peer_flags_t.WIREGUARD_PEER_STATUS_DEAD
1899 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1900 wg1 = VppWgInterface(self, self.pg2.local_ip4, port + 1).add_vpp_config()
1904 # Check peer counter
1905 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 0)
1907 self.pg_enable_capture(self.pg_interfaces)
1912 self.pg2.generate_remote_hosts(NUM_PEERS)
1913 self.pg2.configure_ipv4_neighbors()
1914 self.pg1.generate_remote_hosts(NUM_PEERS)
1915 self.pg1.configure_ipv4_neighbors()
1921 for i in range(NUM_PEERS):
1926 self.pg1.remote_hosts[i].ip4,
1928 ["10.0.%d.4/32" % i],
1936 [VppRoutePath(self.pg1.remote_hosts[i].ip4, wg0.sw_if_index)],
1944 self.pg2.remote_hosts[i].ip4,
1946 ["10.100.%d.4/32" % i],
1954 [VppRoutePath(self.pg2.remote_hosts[i].ip4, wg1.sw_if_index)],
1958 self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_PEERS * 2)
1960 # Want events from the first perr of wg0
1961 # and from all wg1 peers
1962 peers_0[0].want_events()
1965 for i in range(NUM_PEERS):
1966 # send a valid handsake init for which we expect a response
1967 p = peers_0[i].mk_handshake(self.pg1)
1968 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1969 peers_0[i].consume_response(rx[0])
1971 peers_0[0].wait_event(ESTABLISHED_FLAG)
1973 p = peers_1[i].mk_handshake(self.pg2)
1974 rx = self.send_and_expect(self.pg2, [p], self.pg2)
1975 peers_1[i].consume_response(rx[0])
1977 wg1.wait_events(ESTABLISHED_FLAG, [peers_1[0].index, peers_1[1].index])
1981 r.remove_vpp_config()
1983 r.remove_vpp_config()
1986 for i in range(NUM_PEERS):
1987 self.assertTrue(peers_0[i].query_vpp_config())
1988 peers_0[i].remove_vpp_config()
1990 peers_0[i].wait_event(0)
1991 peers_0[i].wait_event(DEAD_FLAG)
1993 self.assertTrue(p.query_vpp_config())
1994 p.remove_vpp_config()
1996 p.wait_event(DEAD_FLAG)
1998 wg0.remove_vpp_config()
1999 wg1.remove_vpp_config()
2002 class WireguardHandoffTests(TestWg):
2003 """Wireguard Tests in multi worker setup"""
2005 vpp_worker_count = 2
2007 def test_wg_peer_init(self):
2013 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2018 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.2.0/24", "10.11.3.0/24"]
2020 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2023 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
2026 # send a valid handsake init for which we expect a response
2027 p = peer_1.mk_handshake(self.pg1)
2029 rx = self.send_and_expect(self.pg1, [p], self.pg1)
2031 peer_1.consume_response(rx[0])
2033 # send a data packet from the peer through the tunnel
2034 # this completes the handshake and pins the peer to worker 0
2036 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
2037 / UDP(sport=222, dport=223)
2040 d = peer_1.encrypt_transport(p)
2041 p = peer_1.mk_tunnel_header(self.pg1) / (
2042 Wireguard(message_type=4, reserved_zero=0)
2043 / WireguardTransport(
2044 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
2047 rxs = self.send_and_expect(self.pg1, [p], self.pg0, worker=0)
2050 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
2051 self.assertEqual(rx[IP].ttl, 19)
2053 # send a packets that are routed into the tunnel
2054 # and pins the peer tp worker 1
2056 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2057 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
2058 / UDP(sport=555, dport=556)
2061 rxs = self.send_and_expect(self.pg0, pe * 255, self.pg1, worker=1)
2062 peer_1.validate_encapped(rxs, pe)
2064 # send packets into the tunnel, from the other worker
2067 peer_1.mk_tunnel_header(self.pg1)
2068 / Wireguard(message_type=4, reserved_zero=0)
2069 / WireguardTransport(
2070 receiver_index=peer_1.sender,
2072 encrypted_encapsulated_packet=peer_1.encrypt_transport(
2074 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
2075 / UDP(sport=222, dport=223)
2081 for ii in range(255)
2084 rxs = self.send_and_expect(self.pg1, p, self.pg0, worker=1)
2087 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
2088 self.assertEqual(rx[IP].ttl, 19)
2090 # send a packets that are routed into the tunnel
2092 rxs = self.send_and_expect(self.pg0, pe * 255, self.pg1, worker=0)
2094 peer_1.validate_encapped(rxs, pe)
2096 r1.remove_vpp_config()
2097 peer_1.remove_vpp_config()
2098 wg0.remove_vpp_config()
2100 @unittest.skip("test disabled")
2101 def test_wg_multi_interface(self):
2102 """Multi-tunnel on the same port"""