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
157 class VppWgPeer(VppObject):
158 def __init__(self, test, itf, endpoint, port, allowed_ips, persistent_keepalive=15):
161 self.endpoint = endpoint
163 self.allowed_ips = allowed_ips
164 self.persistent_keepalive = persistent_keepalive
166 # remote peer's public
167 self.private_key = X25519PrivateKey.generate()
168 self.public_key = self.private_key.public_key()
170 # cookie related params
171 self.cookie_key = blake2s(b"cookie--" + self.public_key_bytes()).digest()
172 self.last_sent_cookie = None
173 self.last_mac1 = None
174 self.last_received_cookie = None
176 self.noise = NoiseConnection.from_name(NOISE_HANDSHAKE_NAME)
178 def add_vpp_config(self, is_ip6=False):
179 rv = self._test.vapi.wireguard_peer_add(
181 "public_key": self.public_key_bytes(),
183 "endpoint": self.endpoint,
184 "n_allowed_ips": len(self.allowed_ips),
185 "allowed_ips": self.allowed_ips,
186 "sw_if_index": self.itf.sw_if_index,
187 "persistent_keepalive": self.persistent_keepalive,
190 self.index = rv.peer_index
191 self.receiver_index = self.index + 1
192 self._test.registry.register(self, self._test.logger)
195 def remove_vpp_config(self):
196 self._test.vapi.wireguard_peer_remove(peer_index=self.index)
199 return "wireguard-peer-%s" % self.index
201 def public_key_bytes(self):
202 return public_key_bytes(self.public_key)
204 def query_vpp_config(self):
205 peers = self._test.vapi.wireguard_peers_dump()
209 p.peer.public_key == self.public_key_bytes()
210 and p.peer.port == self.port
211 and str(p.peer.endpoint) == self.endpoint
212 and p.peer.sw_if_index == self.itf.sw_if_index
213 and len(self.allowed_ips) == p.peer.n_allowed_ips
215 self.allowed_ips.sort()
216 p.peer.allowed_ips.sort()
218 for (a1, a2) in zip(self.allowed_ips, p.peer.allowed_ips):
219 if str(a1) != str(a2):
224 def mk_tunnel_header(self, tx_itf, is_ip6=False):
227 Ether(dst=tx_itf.local_mac, src=tx_itf.remote_mac)
228 / IP(src=self.endpoint, dst=self.itf.src)
229 / UDP(sport=self.port, dport=self.itf.port)
233 Ether(dst=tx_itf.local_mac, src=tx_itf.remote_mac)
234 / IPv6(src=self.endpoint, dst=self.itf.src)
235 / UDP(sport=self.port, dport=self.itf.port)
238 def noise_reset(self):
239 self.noise = NoiseConnection.from_name(NOISE_HANDSHAKE_NAME)
241 def noise_init(self, public_key=None):
242 self.noise.set_prologue(NOISE_IDENTIFIER_NAME)
243 self.noise.set_psks(psk=bytes(bytearray(32)))
246 public_key = self.itf.public_key
249 self.noise.set_keypair_from_private_bytes(
250 Keypair.STATIC, private_key_bytes(self.private_key)
253 self.noise.set_keypair_from_public_bytes(
254 Keypair.REMOTE_STATIC, public_key_bytes(public_key)
257 self.noise.start_handshake()
259 def mk_cookie(self, p, tx_itf, is_resp=False, is_ip6=False):
260 self.verify_header(p, is_ip6)
262 wg_pkt = Wireguard(p[Raw])
265 self._test.assertEqual(wg_pkt[Wireguard].message_type, 2)
266 self._test.assertEqual(wg_pkt[Wireguard].reserved_zero, 0)
267 self._test.assertEqual(wg_pkt[WireguardResponse].mac2, bytes([0] * 16))
269 self._test.assertEqual(wg_pkt[Wireguard].message_type, 1)
270 self._test.assertEqual(wg_pkt[Wireguard].reserved_zero, 0)
271 self._test.assertEqual(wg_pkt[WireguardInitiation].mac2, bytes([0] * 16))
273 # collect info from wg packet (initiation or response)
274 src = get_field_bytes(p[IPv6 if is_ip6 else IP], "src")
275 sport = p[UDP].sport.to_bytes(2, byteorder="big")
277 mac1 = wg_pkt[WireguardResponse].mac1
278 sender_index = wg_pkt[WireguardResponse].sender_index
280 mac1 = wg_pkt[WireguardInitiation].mac1
281 sender_index = wg_pkt[WireguardInitiation].sender_index
284 cookie_reply = Wireguard() / WireguardCookieReply()
285 cookie_reply[Wireguard].message_type = 3
286 cookie_reply[Wireguard].reserved_zero = 0
287 cookie_reply[WireguardCookieReply].receiver_index = sender_index
288 nonce = get_random_bytes(24)
289 cookie_reply[WireguardCookieReply].nonce = nonce
291 # generate cookie data
292 changing_secret = get_random_bytes(32)
293 self.last_sent_cookie = blake2s(
294 src + sport, digest_size=16, key=changing_secret
297 # encrypt cookie data
298 cipher = ChaCha20_Poly1305.new(key=self.cookie_key, nonce=nonce)
300 ciphertext, tag = cipher.encrypt_and_digest(self.last_sent_cookie)
301 cookie_reply[WireguardCookieReply].encrypted_cookie = ciphertext + tag
303 # prepare cookie reply to be sent
304 cookie_reply = self.mk_tunnel_header(tx_itf, is_ip6) / cookie_reply
308 def consume_cookie(self, p, is_ip6=False):
309 self.verify_header(p, is_ip6)
311 cookie_reply = Wireguard(p[Raw])
313 self._test.assertEqual(cookie_reply[Wireguard].message_type, 3)
314 self._test.assertEqual(cookie_reply[Wireguard].reserved_zero, 0)
315 self._test.assertEqual(
316 cookie_reply[WireguardCookieReply].receiver_index, self.receiver_index
319 # collect info from cookie reply
320 nonce = cookie_reply[WireguardCookieReply].nonce
321 encrypted_cookie = cookie_reply[WireguardCookieReply].encrypted_cookie
322 ciphertext, tag = encrypted_cookie[:16], encrypted_cookie[16:]
324 # decrypt cookie data
325 cipher = ChaCha20_Poly1305.new(key=self.itf.cookie_key, nonce=nonce)
326 cipher.update(self.last_mac1)
327 self.last_received_cookie = cipher.decrypt_and_verify(ciphertext, tag)
329 def mk_handshake(self, tx_itf, is_ip6=False, public_key=None):
330 self.noise.set_as_initiator()
331 self.noise_init(public_key)
333 p = Wireguard() / WireguardInitiation()
335 p[Wireguard].message_type = 1
336 p[Wireguard].reserved_zero = 0
337 p[WireguardInitiation].sender_index = self.receiver_index
339 # some random data for the message
340 # lifted from the noise protocol's wireguard example
341 now = datetime.datetime.now()
344 4611686018427387914 + int(now.timestamp()),
345 int(now.microsecond * 1e3),
347 b = self.noise.write_message(payload=tai)
349 # load noise into init message
350 p[WireguardInitiation].unencrypted_ephemeral = b[0:32]
351 p[WireguardInitiation].encrypted_static = b[32:80]
352 p[WireguardInitiation].encrypted_timestamp = b[80:108]
354 # generate the mac1 hash
355 mac_key = blake2s(b"mac1----" + self.itf.public_key_bytes()).digest()
356 mac1 = blake2s(bytes(p)[0:116], digest_size=16, key=mac_key).digest()
357 p[WireguardInitiation].mac1 = mac1
358 self.last_mac1 = mac1
360 # generate the mac2 hash
361 if self.last_received_cookie:
363 bytes(p)[0:132], digest_size=16, key=self.last_received_cookie
365 p[WireguardInitiation].mac2 = mac2
366 self.last_received_cookie = None
368 p[WireguardInitiation].mac2 = bytearray(16)
370 p = self.mk_tunnel_header(tx_itf, is_ip6) / p
374 def verify_header(self, p, is_ip6=False):
376 self._test.assertEqual(p[IP].src, self.itf.src)
377 self._test.assertEqual(p[IP].dst, self.endpoint)
379 self._test.assertEqual(p[IPv6].src, self.itf.src)
380 self._test.assertEqual(p[IPv6].dst, self.endpoint)
381 self._test.assertEqual(p[UDP].sport, self.itf.port)
382 self._test.assertEqual(p[UDP].dport, self.port)
383 self._test.assert_packet_checksums_valid(p)
385 def consume_init(self, p, tx_itf, is_ip6=False, is_mac2=False):
386 self.noise.set_as_responder()
387 self.noise_init(self.itf.public_key)
388 self.verify_header(p, is_ip6)
390 init = Wireguard(p[Raw])
392 self._test.assertEqual(init[Wireguard].message_type, 1)
393 self._test.assertEqual(init[Wireguard].reserved_zero, 0)
395 self.sender = init[WireguardInitiation].sender_index
397 # validate the mac1 hash
398 mac_key = blake2s(b"mac1----" + public_key_bytes(self.public_key)).digest()
399 mac1 = blake2s(bytes(init)[0:-32], digest_size=16, key=mac_key).digest()
400 self._test.assertEqual(init[WireguardInitiation].mac1, mac1)
402 # validate the mac2 hash
404 self._test.assertNotEqual(init[WireguardInitiation].mac2, bytes([0] * 16))
405 self._test.assertNotEqual(self.last_sent_cookie, None)
407 bytes(init)[0:-16], digest_size=16, key=self.last_sent_cookie
409 self._test.assertEqual(init[WireguardInitiation].mac2, mac2)
410 self.last_sent_cookie = None
412 self._test.assertEqual(init[WireguardInitiation].mac2, bytes([0] * 16))
414 # this passes only unencrypted_ephemeral, encrypted_static,
415 # encrypted_timestamp fields of the init
416 payload = self.noise.read_message(bytes(init)[8:-32])
419 b = self.noise.write_message()
420 mac_key = blake2s(b"mac1----" + public_key_bytes(self.itf.public_key)).digest()
421 resp = Wireguard(message_type=2, reserved_zero=0) / WireguardResponse(
422 sender_index=self.receiver_index,
423 receiver_index=self.sender,
424 unencrypted_ephemeral=b[0:32],
425 encrypted_nothing=b[32:],
427 mac1 = blake2s(bytes(resp)[:-32], digest_size=16, key=mac_key).digest()
428 resp[WireguardResponse].mac1 = mac1
429 self.last_mac1 = mac1
431 resp = self.mk_tunnel_header(tx_itf, is_ip6) / resp
432 self._test.assertTrue(self.noise.handshake_finished)
436 def consume_response(self, p, is_ip6=False):
437 self.verify_header(p, is_ip6)
439 resp = Wireguard(p[Raw])
441 self._test.assertEqual(resp[Wireguard].message_type, 2)
442 self._test.assertEqual(resp[Wireguard].reserved_zero, 0)
443 self._test.assertEqual(
444 resp[WireguardResponse].receiver_index, self.receiver_index
447 self.sender = resp[Wireguard].sender_index
449 payload = self.noise.read_message(bytes(resp)[12:60])
450 self._test.assertEqual(payload, b"")
451 self._test.assertTrue(self.noise.handshake_finished)
453 def decrypt_transport(self, p, is_ip6=False):
454 self.verify_header(p, is_ip6)
456 p = Wireguard(p[Raw])
457 self._test.assertEqual(p[Wireguard].message_type, 4)
458 self._test.assertEqual(p[Wireguard].reserved_zero, 0)
459 self._test.assertEqual(
460 p[WireguardTransport].receiver_index, self.receiver_index
463 d = self.noise.decrypt(p[WireguardTransport].encrypted_encapsulated_packet)
466 def encrypt_transport(self, p):
467 return self.noise.encrypt(bytes(p))
469 def validate_encapped(self, rxs, tx, is_ip6=False):
472 rx = IP(self.decrypt_transport(rx))
474 # chech the oringial packet is present
475 self._test.assertEqual(rx[IP].dst, tx[IP].dst)
476 self._test.assertEqual(rx[IP].ttl, tx[IP].ttl - 1)
478 rx = IPv6(self.decrypt_transport(rx))
480 # chech the oringial packet is present
481 self._test.assertEqual(rx[IPv6].dst, tx[IPv6].dst)
482 self._test.assertEqual(rx[IPv6].ttl, tx[IPv6].ttl - 1)
484 def want_events(self):
485 self._test.vapi.want_wireguard_peer_events(
488 peer_index=self.index,
489 sw_if_index=self.itf.sw_if_index,
492 def wait_event(self, expect, timeout=5):
493 rv = self._test.vapi.wait_for_event(timeout, "wireguard_peer_event")
494 self._test.assertEqual(rv.flags, expect)
495 self._test.assertEqual(rv.peer_index, self.index)
498 class TestWg(VppTestCase):
499 """Wireguard Test Case"""
501 error_str = compile(r"Error")
503 wg4_output_node_name = "/err/wg4-output-tun/"
504 wg4_input_node_name = "/err/wg4-input/"
505 wg6_output_node_name = "/err/wg6-output-tun/"
506 wg6_input_node_name = "/err/wg6-input/"
507 kp4_error = wg4_output_node_name + "Keypair error"
508 mac4_error = wg4_input_node_name + "Invalid MAC handshake"
509 peer4_in_err = wg4_input_node_name + "Peer error"
510 peer4_out_err = wg4_output_node_name + "Peer error"
511 kp6_error = wg6_output_node_name + "Keypair error"
512 mac6_error = wg6_input_node_name + "Invalid MAC handshake"
513 peer6_in_err = wg6_input_node_name + "Peer error"
514 peer6_out_err = wg6_output_node_name + "Peer error"
515 cookie_dec4_err = wg4_input_node_name + "Failed during Cookie decryption"
516 cookie_dec6_err = wg6_input_node_name + "Failed during Cookie decryption"
520 super(TestWg, cls).setUpClass()
522 cls.create_pg_interfaces(range(3))
523 for i in cls.pg_interfaces:
531 super(TestWg, cls).tearDownClass()
535 def tearDownClass(cls):
536 super(TestWg, cls).tearDownClass()
539 super(VppTestCase, self).setUp()
540 self.base_kp4_err = self.statistics.get_err_counter(self.kp4_error)
541 self.base_mac4_err = self.statistics.get_err_counter(self.mac4_error)
542 self.base_peer4_in_err = self.statistics.get_err_counter(self.peer4_in_err)
543 self.base_peer4_out_err = self.statistics.get_err_counter(self.peer4_out_err)
544 self.base_kp6_err = self.statistics.get_err_counter(self.kp6_error)
545 self.base_mac6_err = self.statistics.get_err_counter(self.mac6_error)
546 self.base_peer6_in_err = self.statistics.get_err_counter(self.peer6_in_err)
547 self.base_peer6_out_err = self.statistics.get_err_counter(self.peer6_out_err)
548 self.base_cookie_dec4_err = self.statistics.get_err_counter(
551 self.base_cookie_dec6_err = self.statistics.get_err_counter(
555 def test_wg_interface(self):
556 """Simple interface creation"""
560 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
562 self.logger.info(self.vapi.cli("sh int"))
565 wg0.remove_vpp_config()
567 def test_handshake_hash(self):
568 """test hashing an init message"""
569 # a init packet generated by linux given the key below
592 b = bytearray.fromhex(h)
595 pubb = base64.b64decode("aRuHFTTxICIQNefp05oKWlJv3zgKxb8+WW7JJMh0jyM=")
596 pub = X25519PublicKey.from_public_bytes(pubb)
598 self.assertEqual(pubb, public_key_bytes(pub))
600 # strip the macs and build a new packet
602 mac_key = blake2s(b"mac1----" + public_key_bytes(pub)).digest()
603 init += blake2s(init, digest_size=16, key=mac_key).digest()
606 act = Wireguard(init)
608 self.assertEqual(tgt, act)
610 def _test_wg_send_cookie_tmpl(self, is_resp, is_ip6):
613 # create wg interface
615 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
619 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
623 self.pg_enable_capture(self.pg_interfaces)
629 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
633 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
635 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
638 # prepare and send a handshake initiation
639 # expect the peer to send a handshake response
640 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
641 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
643 # wait for the peer to send a handshake initiation
644 rxs = self.pg1.get_capture(1, timeout=2)
646 # prepare and send a wrong cookie reply
647 # expect no replies and the cookie error incremented
648 cookie = peer_1.mk_cookie(rxs[0], self.pg1, is_resp=is_resp, is_ip6=is_ip6)
649 cookie.nonce = b"1234567890"
650 self.send_and_assert_no_replies(self.pg1, [cookie], timeout=0.1)
653 self.base_cookie_dec6_err + 1,
654 self.statistics.get_err_counter(self.cookie_dec6_err),
658 self.base_cookie_dec4_err + 1,
659 self.statistics.get_err_counter(self.cookie_dec4_err),
662 # prepare and send a correct cookie reply
663 cookie = peer_1.mk_cookie(rxs[0], self.pg1, is_resp=is_resp, is_ip6=is_ip6)
664 self.pg_send(self.pg1, [cookie])
666 # wait for the peer to send a handshake initiation with mac2 set
667 rxs = self.pg1.get_capture(1, timeout=6)
669 # verify the initiation and its mac2
670 peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6, is_mac2=True)
673 peer_1.remove_vpp_config()
674 wg0.remove_vpp_config()
676 def test_wg_send_cookie_on_init_v4(self):
677 """Send cookie on handshake initiation (v4)"""
678 self._test_wg_send_cookie_tmpl(is_resp=False, is_ip6=False)
680 def test_wg_send_cookie_on_init_v6(self):
681 """Send cookie on handshake initiation (v6)"""
682 self._test_wg_send_cookie_tmpl(is_resp=False, is_ip6=True)
684 def test_wg_send_cookie_on_resp_v4(self):
685 """Send cookie on handshake response (v4)"""
686 self._test_wg_send_cookie_tmpl(is_resp=True, is_ip6=False)
688 def test_wg_send_cookie_on_resp_v6(self):
689 """Send cookie on handshake response (v6)"""
690 self._test_wg_send_cookie_tmpl(is_resp=True, is_ip6=True)
692 def _test_wg_receive_cookie_tmpl(self, is_resp, is_ip6):
695 # create wg interface
697 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
701 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
705 self.pg_enable_capture(self.pg_interfaces)
711 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
715 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
717 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
720 # wait for the peer to send a handshake initiation
721 rxs = self.pg1.get_capture(1, timeout=2)
722 # prepare and send a bunch of handshake responses
723 # expect to switch to under load state
724 resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6)
725 txs = [resp] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
726 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
727 # reset noise to be able to turn into initiator later
730 # prepare and send a bunch of handshake initiations
731 # expect to switch to under load state
732 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
733 txs = [init] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
734 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
736 # expect the peer to send a cookie reply
737 peer_1.consume_cookie(rxs[-1], is_ip6=is_ip6)
739 # prepare and send a handshake initiation with wrong mac2
740 # expect a cookie reply
741 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
742 init.mac2 = b"1234567890"
743 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
744 peer_1.consume_cookie(rxs[0], is_ip6=is_ip6)
746 # prepare and send a handshake initiation with correct mac2
747 # expect a handshake response
748 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
749 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
751 # verify the response
752 peer_1.consume_response(rxs[0], is_ip6=is_ip6)
754 # clear up under load state
755 self.sleep(UNDER_LOAD_INTERVAL)
758 peer_1.remove_vpp_config()
759 wg0.remove_vpp_config()
761 def test_wg_receive_cookie_on_init_v4(self):
762 """Receive cookie on handshake initiation (v4)"""
763 self._test_wg_receive_cookie_tmpl(is_resp=False, is_ip6=False)
765 def test_wg_receive_cookie_on_init_v6(self):
766 """Receive cookie on handshake initiation (v6)"""
767 self._test_wg_receive_cookie_tmpl(is_resp=False, is_ip6=True)
769 def test_wg_receive_cookie_on_resp_v4(self):
770 """Receive cookie on handshake response (v4)"""
771 self._test_wg_receive_cookie_tmpl(is_resp=True, is_ip6=False)
773 def test_wg_receive_cookie_on_resp_v6(self):
774 """Receive cookie on handshake response (v6)"""
775 self._test_wg_receive_cookie_tmpl(is_resp=True, is_ip6=True)
777 def test_wg_under_load_interval(self):
778 """Under load interval"""
781 # create wg interface
782 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
786 self.pg_enable_capture(self.pg_interfaces)
791 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
793 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
795 # prepare and send a bunch of handshake initiations
796 # expect to switch to under load state
797 init = peer_1.mk_handshake(self.pg1)
798 txs = [init] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
799 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
801 # expect the peer to send a cookie reply
802 peer_1.consume_cookie(rxs[-1])
804 # sleep till the next counting interval
805 # expect under load state is still active
806 self.sleep(HANDSHAKE_COUNTING_INTERVAL)
808 # prepare and send a handshake initiation with wrong mac2
809 # expect a cookie reply
810 init = peer_1.mk_handshake(self.pg1)
811 init.mac2 = b"1234567890"
812 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
813 peer_1.consume_cookie(rxs[0])
815 # sleep till the end of being under load
816 # expect under load state is over
817 self.sleep(UNDER_LOAD_INTERVAL - HANDSHAKE_COUNTING_INTERVAL)
819 # prepare and send a handshake initiation with wrong mac2
820 # expect a handshake response
821 init = peer_1.mk_handshake(self.pg1)
822 init.mac2 = b"1234567890"
823 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
825 # verify the response
826 peer_1.consume_response(rxs[0])
829 peer_1.remove_vpp_config()
830 wg0.remove_vpp_config()
832 def test_wg_peer_resp(self):
833 """Send handshake response"""
837 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
841 self.pg_enable_capture(self.pg_interfaces)
845 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
847 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
850 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
853 # wait for the peer to send a handshake
854 rx = self.pg1.get_capture(1, timeout=2)
856 # consume the handshake in the noise protocol and
857 # generate the response
858 resp = peer_1.consume_init(rx[0], self.pg1)
860 # send the response, get keepalive
861 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
864 b = peer_1.decrypt_transport(rx)
865 self.assertEqual(0, len(b))
867 # send a packets that are routed into the tunnel
869 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
870 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
871 / UDP(sport=555, dport=556)
875 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
877 peer_1.validate_encapped(rxs, p)
879 # send packets into the tunnel, expect to receive them on
883 peer_1.mk_tunnel_header(self.pg1)
884 / Wireguard(message_type=4, reserved_zero=0)
885 / WireguardTransport(
886 receiver_index=peer_1.sender,
888 encrypted_encapsulated_packet=peer_1.encrypt_transport(
890 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
891 / UDP(sport=222, dport=223)
900 rxs = self.send_and_expect(self.pg1, p, self.pg0)
903 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
904 self.assertEqual(rx[IP].ttl, 19)
906 r1.remove_vpp_config()
907 peer_1.remove_vpp_config()
908 wg0.remove_vpp_config()
910 def test_wg_peer_v4o4(self):
916 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
921 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
923 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
926 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
929 self, "20.22.3.0", 24, [VppRoutePath("20.22.3.1", wg0.sw_if_index)]
932 # route a packet into the wg interface
933 # use the allowed-ip prefix
934 # this is dropped because the peer is not initiated
936 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
937 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
938 / UDP(sport=555, dport=556)
941 self.send_and_assert_no_replies(self.pg0, [p])
943 self.base_kp4_err + 1, self.statistics.get_err_counter(self.kp4_error)
946 # route a packet into the wg interface
947 # use a not allowed-ip prefix
948 # this is dropped because there is no matching peer
950 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
951 / IP(src=self.pg0.remote_ip4, dst="20.22.3.2")
952 / UDP(sport=555, dport=556)
955 self.send_and_assert_no_replies(self.pg0, [p])
957 self.base_peer4_out_err + 1,
958 self.statistics.get_err_counter(self.peer4_out_err),
961 # send a handsake from the peer with an invalid MAC
962 p = peer_1.mk_handshake(self.pg1)
963 p[WireguardInitiation].mac1 = b"foobar"
964 self.send_and_assert_no_replies(self.pg1, [p])
966 self.base_mac4_err + 1, self.statistics.get_err_counter(self.mac4_error)
969 # send a handsake from the peer but signed by the wrong key.
970 p = peer_1.mk_handshake(
971 self.pg1, False, X25519PrivateKey.generate().public_key()
973 self.send_and_assert_no_replies(self.pg1, [p])
975 self.base_peer4_in_err + 1,
976 self.statistics.get_err_counter(self.peer4_in_err),
979 # send a valid handsake init for which we expect a response
980 p = peer_1.mk_handshake(self.pg1)
982 rx = self.send_and_expect(self.pg1, [p], self.pg1)
984 peer_1.consume_response(rx[0])
986 # route a packet into the wg interface
987 # this is dropped because the peer is still not initiated
989 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
990 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
991 / UDP(sport=555, dport=556)
994 self.send_and_assert_no_replies(self.pg0, [p])
996 self.base_kp4_err + 2, self.statistics.get_err_counter(self.kp4_error)
999 # send a data packet from the peer through the tunnel
1000 # this completes the handshake
1002 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1003 / UDP(sport=222, dport=223)
1006 d = peer_1.encrypt_transport(p)
1007 p = peer_1.mk_tunnel_header(self.pg1) / (
1008 Wireguard(message_type=4, reserved_zero=0)
1009 / WireguardTransport(
1010 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1013 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1016 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1017 self.assertEqual(rx[IP].ttl, 19)
1019 # send a packets that are routed into the tunnel
1021 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1022 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1023 / UDP(sport=555, dport=556)
1027 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1030 rx = IP(peer_1.decrypt_transport(rx))
1032 # chech the oringial packet is present
1033 self.assertEqual(rx[IP].dst, p[IP].dst)
1034 self.assertEqual(rx[IP].ttl, p[IP].ttl - 1)
1036 # send packets into the tunnel, expect to receive them on
1040 peer_1.mk_tunnel_header(self.pg1)
1041 / Wireguard(message_type=4, reserved_zero=0)
1042 / WireguardTransport(
1043 receiver_index=peer_1.sender,
1045 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1047 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1048 / UDP(sport=222, dport=223)
1054 for ii in range(255)
1057 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1060 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1061 self.assertEqual(rx[IP].ttl, 19)
1063 r1.remove_vpp_config()
1064 r2.remove_vpp_config()
1065 peer_1.remove_vpp_config()
1066 wg0.remove_vpp_config()
1068 def test_wg_peer_v6o6(self):
1074 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1079 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
1080 ).add_vpp_config(True)
1081 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1084 self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
1087 self, "22::3:0", 112, [VppRoutePath("22::3:1", wg0.sw_if_index)]
1090 # route a packet into the wg interface
1091 # use the allowed-ip prefix
1092 # this is dropped because the peer is not initiated
1095 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1096 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1097 / UDP(sport=555, dport=556)
1100 self.send_and_assert_no_replies(self.pg0, [p])
1103 self.base_kp6_err + 1, self.statistics.get_err_counter(self.kp6_error)
1106 # route a packet into the wg interface
1107 # use a not allowed-ip prefix
1108 # this is dropped because there is no matching peer
1110 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1111 / IPv6(src=self.pg0.remote_ip6, dst="22::3:2")
1112 / UDP(sport=555, dport=556)
1115 self.send_and_assert_no_replies(self.pg0, [p])
1117 self.base_peer6_out_err + 1,
1118 self.statistics.get_err_counter(self.peer6_out_err),
1121 # send a handsake from the peer with an invalid MAC
1122 p = peer_1.mk_handshake(self.pg1, True)
1123 p[WireguardInitiation].mac1 = b"foobar"
1124 self.send_and_assert_no_replies(self.pg1, [p])
1127 self.base_mac6_err + 1, self.statistics.get_err_counter(self.mac6_error)
1130 # send a handsake from the peer but signed by the wrong key.
1131 p = peer_1.mk_handshake(
1132 self.pg1, True, X25519PrivateKey.generate().public_key()
1134 self.send_and_assert_no_replies(self.pg1, [p])
1136 self.base_peer6_in_err + 1,
1137 self.statistics.get_err_counter(self.peer6_in_err),
1140 # send a valid handsake init for which we expect a response
1141 p = peer_1.mk_handshake(self.pg1, True)
1143 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1145 peer_1.consume_response(rx[0], True)
1147 # route a packet into the wg interface
1148 # this is dropped because the peer is still not initiated
1150 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1151 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1152 / UDP(sport=555, dport=556)
1155 self.send_and_assert_no_replies(self.pg0, [p])
1157 self.base_kp6_err + 2, self.statistics.get_err_counter(self.kp6_error)
1160 # send a data packet from the peer through the tunnel
1161 # this completes the handshake
1163 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1164 / UDP(sport=222, dport=223)
1167 d = peer_1.encrypt_transport(p)
1168 p = peer_1.mk_tunnel_header(self.pg1, True) / (
1169 Wireguard(message_type=4, reserved_zero=0)
1170 / WireguardTransport(
1171 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1174 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1177 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1178 self.assertEqual(rx[IPv6].hlim, 19)
1180 # send a packets that are routed into the tunnel
1182 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1183 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1184 / UDP(sport=555, dport=556)
1188 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1191 rx = IPv6(peer_1.decrypt_transport(rx, True))
1193 # chech the oringial packet is present
1194 self.assertEqual(rx[IPv6].dst, p[IPv6].dst)
1195 self.assertEqual(rx[IPv6].hlim, p[IPv6].hlim - 1)
1197 # send packets into the tunnel, expect to receive them on
1201 peer_1.mk_tunnel_header(self.pg1, True)
1202 / Wireguard(message_type=4, reserved_zero=0)
1203 / WireguardTransport(
1204 receiver_index=peer_1.sender,
1206 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1208 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1209 / UDP(sport=222, dport=223)
1215 for ii in range(255)
1218 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1221 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1222 self.assertEqual(rx[IPv6].hlim, 19)
1224 r1.remove_vpp_config()
1225 r2.remove_vpp_config()
1226 peer_1.remove_vpp_config()
1227 wg0.remove_vpp_config()
1229 def test_wg_peer_v6o4(self):
1235 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1240 self, wg0, self.pg1.remote_ip4, port + 1, ["1::3:0/112"]
1241 ).add_vpp_config(True)
1242 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1245 self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
1248 # route a packet into the wg interface
1249 # use the allowed-ip prefix
1250 # this is dropped because the peer is not initiated
1252 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1253 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1254 / UDP(sport=555, dport=556)
1257 self.send_and_assert_no_replies(self.pg0, [p])
1259 self.base_kp6_err + 1, self.statistics.get_err_counter(self.kp6_error)
1262 # send a handsake from the peer with an invalid MAC
1263 p = peer_1.mk_handshake(self.pg1)
1264 p[WireguardInitiation].mac1 = b"foobar"
1265 self.send_and_assert_no_replies(self.pg1, [p])
1268 self.base_mac4_err + 1, self.statistics.get_err_counter(self.mac4_error)
1271 # send a handsake from the peer but signed by the wrong key.
1272 p = peer_1.mk_handshake(
1273 self.pg1, False, X25519PrivateKey.generate().public_key()
1275 self.send_and_assert_no_replies(self.pg1, [p])
1277 self.base_peer4_in_err + 1,
1278 self.statistics.get_err_counter(self.peer4_in_err),
1281 # send a valid handsake init for which we expect a response
1282 p = peer_1.mk_handshake(self.pg1)
1284 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1286 peer_1.consume_response(rx[0])
1288 # route a packet into the wg interface
1289 # this is dropped because the peer is still not initiated
1291 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1292 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1293 / UDP(sport=555, dport=556)
1296 self.send_and_assert_no_replies(self.pg0, [p])
1298 self.base_kp6_err + 2, self.statistics.get_err_counter(self.kp6_error)
1301 # send a data packet from the peer through the tunnel
1302 # this completes the handshake
1304 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1305 / UDP(sport=222, dport=223)
1308 d = peer_1.encrypt_transport(p)
1309 p = peer_1.mk_tunnel_header(self.pg1) / (
1310 Wireguard(message_type=4, reserved_zero=0)
1311 / WireguardTransport(
1312 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1315 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1318 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1319 self.assertEqual(rx[IPv6].hlim, 19)
1321 # send a packets that are routed into the tunnel
1323 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1324 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1325 / UDP(sport=555, dport=556)
1329 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1332 rx = IPv6(peer_1.decrypt_transport(rx))
1334 # chech the oringial packet is present
1335 self.assertEqual(rx[IPv6].dst, p[IPv6].dst)
1336 self.assertEqual(rx[IPv6].hlim, p[IPv6].hlim - 1)
1338 # send packets into the tunnel, expect to receive them on
1342 peer_1.mk_tunnel_header(self.pg1)
1343 / Wireguard(message_type=4, reserved_zero=0)
1344 / WireguardTransport(
1345 receiver_index=peer_1.sender,
1347 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1349 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1350 / UDP(sport=222, dport=223)
1356 for ii in range(255)
1359 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1362 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1363 self.assertEqual(rx[IPv6].hlim, 19)
1365 r1.remove_vpp_config()
1366 peer_1.remove_vpp_config()
1367 wg0.remove_vpp_config()
1369 def test_wg_peer_v4o6(self):
1375 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1380 self, wg0, self.pg1.remote_ip6, port + 1, ["10.11.3.0/24"]
1382 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1385 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1388 # route a packet into the wg interface
1389 # use the allowed-ip prefix
1390 # this is dropped because the peer is not initiated
1392 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1393 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1394 / UDP(sport=555, dport=556)
1397 self.send_and_assert_no_replies(self.pg0, [p])
1399 self.base_kp4_err + 1, self.statistics.get_err_counter(self.kp4_error)
1402 # send a handsake from the peer with an invalid MAC
1403 p = peer_1.mk_handshake(self.pg1, True)
1404 p[WireguardInitiation].mac1 = b"foobar"
1405 self.send_and_assert_no_replies(self.pg1, [p])
1407 self.base_mac6_err + 1, self.statistics.get_err_counter(self.mac6_error)
1410 # send a handsake from the peer but signed by the wrong key.
1411 p = peer_1.mk_handshake(
1412 self.pg1, True, X25519PrivateKey.generate().public_key()
1414 self.send_and_assert_no_replies(self.pg1, [p])
1416 self.base_peer6_in_err + 1,
1417 self.statistics.get_err_counter(self.peer6_in_err),
1420 # send a valid handsake init for which we expect a response
1421 p = peer_1.mk_handshake(self.pg1, True)
1423 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1425 peer_1.consume_response(rx[0], True)
1427 # route a packet into the wg interface
1428 # this is dropped because the peer is still not initiated
1430 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1431 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1432 / UDP(sport=555, dport=556)
1435 self.send_and_assert_no_replies(self.pg0, [p])
1437 self.base_kp4_err + 2, self.statistics.get_err_counter(self.kp4_error)
1440 # send a data packet from the peer through the tunnel
1441 # this completes the handshake
1443 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1444 / UDP(sport=222, dport=223)
1447 d = peer_1.encrypt_transport(p)
1448 p = peer_1.mk_tunnel_header(self.pg1, True) / (
1449 Wireguard(message_type=4, reserved_zero=0)
1450 / WireguardTransport(
1451 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1454 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1457 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1458 self.assertEqual(rx[IP].ttl, 19)
1460 # send a packets that are routed into the tunnel
1462 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1463 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1464 / UDP(sport=555, dport=556)
1468 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1471 rx = IP(peer_1.decrypt_transport(rx, True))
1473 # chech the oringial packet is present
1474 self.assertEqual(rx[IP].dst, p[IP].dst)
1475 self.assertEqual(rx[IP].ttl, p[IP].ttl - 1)
1477 # send packets into the tunnel, expect to receive them on
1481 peer_1.mk_tunnel_header(self.pg1, True)
1482 / Wireguard(message_type=4, reserved_zero=0)
1483 / WireguardTransport(
1484 receiver_index=peer_1.sender,
1486 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1488 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1489 / UDP(sport=222, dport=223)
1495 for ii in range(255)
1498 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1501 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1502 self.assertEqual(rx[IP].ttl, 19)
1504 r1.remove_vpp_config()
1505 peer_1.remove_vpp_config()
1506 wg0.remove_vpp_config()
1508 def test_wg_multi_peer(self):
1509 """multiple peer setup"""
1513 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1514 wg1 = VppWgInterface(self, self.pg2.local_ip4, port + 1).add_vpp_config()
1518 # Check peer counter
1519 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 0)
1521 self.pg_enable_capture(self.pg_interfaces)
1524 # Create many peers on sencond interface
1526 self.pg2.generate_remote_hosts(NUM_PEERS)
1527 self.pg2.configure_ipv4_neighbors()
1528 self.pg1.generate_remote_hosts(NUM_PEERS)
1529 self.pg1.configure_ipv4_neighbors()
1535 for i in range(NUM_PEERS):
1540 self.pg1.remote_hosts[i].ip4,
1542 ["10.0.%d.4/32" % i],
1550 [VppRoutePath(self.pg1.remote_hosts[i].ip4, wg0.sw_if_index)],
1558 self.pg2.remote_hosts[i].ip4,
1560 ["10.100.%d.4/32" % i],
1568 [VppRoutePath(self.pg2.remote_hosts[i].ip4, wg1.sw_if_index)],
1572 self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_PEERS * 2)
1574 self.logger.info(self.vapi.cli("show wireguard peer"))
1575 self.logger.info(self.vapi.cli("show wireguard interface"))
1576 self.logger.info(self.vapi.cli("show adj 37"))
1577 self.logger.info(self.vapi.cli("sh ip fib 172.16.3.17"))
1578 self.logger.info(self.vapi.cli("sh ip fib 10.11.3.0"))
1582 r.remove_vpp_config()
1584 r.remove_vpp_config()
1588 self.assertTrue(p.query_vpp_config())
1589 p.remove_vpp_config()
1591 self.assertTrue(p.query_vpp_config())
1592 p.remove_vpp_config()
1594 wg0.remove_vpp_config()
1595 wg1.remove_vpp_config()
1597 def test_wg_multi_interface(self):
1598 """Multi-tunnel on the same port"""
1601 # Create many wireguard interfaces
1603 self.pg1.generate_remote_hosts(NUM_IFS)
1604 self.pg1.configure_ipv4_neighbors()
1605 self.pg0.generate_remote_hosts(NUM_IFS)
1606 self.pg0.configure_ipv4_neighbors()
1608 # Create interfaces with a peer on each
1612 for i in range(NUM_IFS):
1613 # Use the same port for each interface
1614 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1622 self.pg1.remote_hosts[i].ip4,
1624 ["10.0.%d.0/24" % i],
1633 [VppRoutePath("10.0.%d.4" % i, wg0.sw_if_index)],
1637 self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_IFS)
1639 for i in range(NUM_IFS):
1640 # send a valid handsake init for which we expect a response
1641 p = peers[i].mk_handshake(self.pg1)
1642 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1643 peers[i].consume_response(rx[0])
1645 # send a data packet from the peer through the tunnel
1646 # this completes the handshake
1648 IP(src="10.0.%d.4" % i, dst=self.pg0.remote_hosts[i].ip4, ttl=20)
1649 / UDP(sport=222, dport=223)
1652 d = peers[i].encrypt_transport(p)
1653 p = peers[i].mk_tunnel_header(self.pg1) / (
1654 Wireguard(message_type=4, reserved_zero=0)
1655 / WireguardTransport(
1656 receiver_index=peers[i].sender,
1658 encrypted_encapsulated_packet=d,
1661 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1663 self.assertEqual(rx[IP].dst, self.pg0.remote_hosts[i].ip4)
1664 self.assertEqual(rx[IP].ttl, 19)
1666 # send a packets that are routed into the tunnel
1667 for i in range(NUM_IFS):
1669 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1670 / IP(src=self.pg0.remote_hosts[i].ip4, dst="10.0.%d.4" % i)
1671 / UDP(sport=555, dport=556)
1675 rxs = self.send_and_expect(self.pg0, p * 64, self.pg1)
1678 rx = IP(peers[i].decrypt_transport(rx))
1680 # check the oringial packet is present
1681 self.assertEqual(rx[IP].dst, p[IP].dst)
1682 self.assertEqual(rx[IP].ttl, p[IP].ttl - 1)
1684 # send packets into the tunnel
1685 for i in range(NUM_IFS):
1688 peers[i].mk_tunnel_header(self.pg1)
1689 / Wireguard(message_type=4, reserved_zero=0)
1690 / WireguardTransport(
1691 receiver_index=peers[i].sender,
1693 encrypted_encapsulated_packet=peers[i].encrypt_transport(
1696 src="10.0.%d.4" % i,
1697 dst=self.pg0.remote_hosts[i].ip4,
1700 / UDP(sport=222, dport=223)
1709 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1712 self.assertEqual(rx[IP].dst, self.pg0.remote_hosts[i].ip4)
1713 self.assertEqual(rx[IP].ttl, 19)
1716 r.remove_vpp_config()
1718 p.remove_vpp_config()
1720 i.remove_vpp_config()
1722 def test_wg_event(self):
1725 ESTABLISHED_FLAG = (
1726 VppEnum.vl_api_wireguard_peer_flags_t.WIREGUARD_PEER_ESTABLISHED
1728 DEAD_FLAG = VppEnum.vl_api_wireguard_peer_flags_t.WIREGUARD_PEER_STATUS_DEAD
1731 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1732 wg1 = VppWgInterface(self, self.pg2.local_ip4, port + 1).add_vpp_config()
1736 # Check peer counter
1737 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 0)
1739 self.pg_enable_capture(self.pg_interfaces)
1744 self.pg2.generate_remote_hosts(NUM_PEERS)
1745 self.pg2.configure_ipv4_neighbors()
1746 self.pg1.generate_remote_hosts(NUM_PEERS)
1747 self.pg1.configure_ipv4_neighbors()
1753 for i in range(NUM_PEERS):
1758 self.pg1.remote_hosts[i].ip4,
1760 ["10.0.%d.4/32" % i],
1768 [VppRoutePath(self.pg1.remote_hosts[i].ip4, wg0.sw_if_index)],
1776 self.pg2.remote_hosts[i].ip4,
1778 ["10.100.%d.4/32" % i],
1786 [VppRoutePath(self.pg2.remote_hosts[i].ip4, wg1.sw_if_index)],
1790 self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_PEERS * 2)
1792 # Want events from the first perr of wg0
1793 # and from all wg1 peers
1794 peers_0[0].want_events()
1797 for i in range(NUM_PEERS):
1798 # send a valid handsake init for which we expect a response
1799 p = peers_0[i].mk_handshake(self.pg1)
1800 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1801 peers_0[i].consume_response(rx[0])
1803 peers_0[0].wait_event(ESTABLISHED_FLAG)
1805 p = peers_1[i].mk_handshake(self.pg2)
1806 rx = self.send_and_expect(self.pg2, [p], self.pg2)
1807 peers_1[i].consume_response(rx[0])
1809 wg1.wait_events(ESTABLISHED_FLAG, [peers_1[0].index, peers_1[1].index])
1813 r.remove_vpp_config()
1815 r.remove_vpp_config()
1818 for i in range(NUM_PEERS):
1819 self.assertTrue(peers_0[i].query_vpp_config())
1820 peers_0[i].remove_vpp_config()
1822 peers_0[i].wait_event(0)
1823 peers_0[i].wait_event(DEAD_FLAG)
1825 self.assertTrue(p.query_vpp_config())
1826 p.remove_vpp_config()
1828 p.wait_event(DEAD_FLAG)
1830 wg0.remove_vpp_config()
1831 wg1.remove_vpp_config()
1834 class WireguardHandoffTests(TestWg):
1835 """Wireguard Tests in multi worker setup"""
1837 vpp_worker_count = 2
1839 def test_wg_peer_init(self):
1845 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1850 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.2.0/24", "10.11.3.0/24"]
1852 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1855 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1858 # send a valid handsake init for which we expect a response
1859 p = peer_1.mk_handshake(self.pg1)
1861 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1863 peer_1.consume_response(rx[0])
1865 # send a data packet from the peer through the tunnel
1866 # this completes the handshake and pins the peer to worker 0
1868 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1869 / UDP(sport=222, dport=223)
1872 d = peer_1.encrypt_transport(p)
1873 p = peer_1.mk_tunnel_header(self.pg1) / (
1874 Wireguard(message_type=4, reserved_zero=0)
1875 / WireguardTransport(
1876 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1879 rxs = self.send_and_expect(self.pg1, [p], self.pg0, worker=0)
1882 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1883 self.assertEqual(rx[IP].ttl, 19)
1885 # send a packets that are routed into the tunnel
1886 # and pins the peer tp worker 1
1888 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1889 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1890 / UDP(sport=555, dport=556)
1893 rxs = self.send_and_expect(self.pg0, pe * 255, self.pg1, worker=1)
1894 peer_1.validate_encapped(rxs, pe)
1896 # send packets into the tunnel, from the other worker
1899 peer_1.mk_tunnel_header(self.pg1)
1900 / Wireguard(message_type=4, reserved_zero=0)
1901 / WireguardTransport(
1902 receiver_index=peer_1.sender,
1904 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1906 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1907 / UDP(sport=222, dport=223)
1913 for ii in range(255)
1916 rxs = self.send_and_expect(self.pg1, p, self.pg0, worker=1)
1919 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1920 self.assertEqual(rx[IP].ttl, 19)
1922 # send a packets that are routed into the tunnel
1924 rxs = self.send_and_expect(self.pg0, pe * 255, self.pg1, worker=0)
1926 peer_1.validate_encapped(rxs, pe)
1928 r1.remove_vpp_config()
1929 peer_1.remove_vpp_config()
1930 wg0.remove_vpp_config()
1932 @unittest.skip("test disabled")
1933 def test_wg_multi_interface(self):
1934 """Multi-tunnel on the same port"""