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 Wireguard, WireguardResponse, \
15 WireguardInitiation, WireguardTransport
16 from cryptography.hazmat.primitives.asymmetric.x25519 import \
17 X25519PrivateKey, X25519PublicKey
18 from cryptography.hazmat.primitives.serialization import Encoding, \
19 PrivateFormat, PublicFormat, NoEncryption
20 from cryptography.hazmat.primitives.hashes import BLAKE2s, Hash
21 from cryptography.hazmat.primitives.hmac import HMAC
22 from cryptography.hazmat.backends import default_backend
23 from noise.connection import NoiseConnection, Keypair
25 from vpp_ipip_tun_interface import VppIpIpTunInterface
26 from vpp_interface import VppInterface
27 from vpp_ip_route import VppIpRoute, VppRoutePath
28 from vpp_object import VppObject
29 from vpp_papi import VppEnum
30 from framework import VppTestCase
31 from re import compile
34 """ TestWg is a subclass of VPPTestCase classes.
41 def private_key_bytes(k):
42 return k.private_bytes(Encoding.Raw,
47 def public_key_bytes(k):
48 return k.public_bytes(Encoding.Raw,
52 class VppWgInterface(VppInterface):
54 VPP WireGuard interface
57 def __init__(self, test, src, port):
58 super(VppWgInterface, self).__init__(test)
62 self.private_key = X25519PrivateKey.generate()
63 self.public_key = self.private_key.public_key()
65 def public_key_bytes(self):
66 return public_key_bytes(self.public_key)
68 def private_key_bytes(self):
69 return private_key_bytes(self.private_key)
71 def add_vpp_config(self):
72 r = self.test.vapi.wireguard_interface_create(interface={
73 'user_instance': 0xffffffff,
76 'private_key': private_key_bytes(self.private_key),
79 self.set_sw_if_index(r.sw_if_index)
80 self.test.registry.register(self, self.test.logger)
83 def remove_vpp_config(self):
84 self.test.vapi.wireguard_interface_delete(
85 sw_if_index=self._sw_if_index)
87 def query_vpp_config(self):
88 ts = self.test.vapi.wireguard_interface_dump(sw_if_index=0xffffffff)
90 if t.interface.sw_if_index == self._sw_if_index and \
91 str(t.interface.src_ip) == self.src and \
92 t.interface.port == self.port and \
93 t.interface.private_key == private_key_bytes(self.private_key):
97 def want_events(self, peer_index=0xffffffff):
98 self.test.vapi.want_wireguard_peer_events(
101 sw_if_index=self._sw_if_index,
102 peer_index=peer_index)
104 def wait_events(self, expect, peers, timeout=5):
105 for i in range(len(peers)):
106 rv = self.test.vapi.wait_for_event(timeout, "wireguard_peer_event")
107 self.test.assertEqual(rv.peer_index, peers[i])
108 self.test.assertEqual(rv.flags, expect)
111 return self.object_id()
114 return "wireguard-%d" % self._sw_if_index
117 def find_route(test, prefix, is_ip6, table_id=0):
118 routes = test.vapi.ip_route_dump(table_id, is_ip6)
121 if table_id == e.route.table_id \
122 and str(e.route.prefix) == str(prefix):
127 NOISE_HANDSHAKE_NAME = b"Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s"
128 NOISE_IDENTIFIER_NAME = b"WireGuard v1 zx2c4 Jason@zx2c4.com"
131 class VppWgPeer(VppObject):
139 persistent_keepalive=15):
142 self.endpoint = endpoint
144 self.allowed_ips = allowed_ips
145 self.persistent_keepalive = persistent_keepalive
147 # remote peer's public
148 self.private_key = X25519PrivateKey.generate()
149 self.public_key = self.private_key.public_key()
151 self.noise = NoiseConnection.from_name(NOISE_HANDSHAKE_NAME)
153 def add_vpp_config(self, is_ip6=False):
154 rv = self._test.vapi.wireguard_peer_add(
156 'public_key': self.public_key_bytes(),
158 'endpoint': self.endpoint,
159 'n_allowed_ips': len(self.allowed_ips),
160 'allowed_ips': self.allowed_ips,
161 'sw_if_index': self.itf.sw_if_index,
162 'persistent_keepalive': self.persistent_keepalive})
163 self.index = rv.peer_index
164 self.receiver_index = self.index + 1
165 self._test.registry.register(self, self._test.logger)
168 def remove_vpp_config(self):
169 self._test.vapi.wireguard_peer_remove(peer_index=self.index)
172 return ("wireguard-peer-%s" % self.index)
174 def public_key_bytes(self):
175 return public_key_bytes(self.public_key)
177 def query_vpp_config(self):
178 peers = self._test.vapi.wireguard_peers_dump()
181 if p.peer.public_key == self.public_key_bytes() and \
182 p.peer.port == self.port and \
183 str(p.peer.endpoint) == self.endpoint and \
184 p.peer.sw_if_index == self.itf.sw_if_index and \
185 len(self.allowed_ips) == p.peer.n_allowed_ips:
186 self.allowed_ips.sort()
187 p.peer.allowed_ips.sort()
189 for (a1, a2) in zip(self.allowed_ips, p.peer.allowed_ips):
190 if str(a1) != str(a2):
195 def set_responder(self):
196 self.noise.set_as_responder()
198 def mk_tunnel_header(self, tx_itf, is_ip6=False):
200 return (Ether(dst=tx_itf.local_mac, src=tx_itf.remote_mac) /
201 IP(src=self.endpoint, dst=self.itf.src) /
202 UDP(sport=self.port, dport=self.itf.port))
204 return (Ether(dst=tx_itf.local_mac, src=tx_itf.remote_mac) /
205 IPv6(src=self.endpoint, dst=self.itf.src) /
206 UDP(sport=self.port, dport=self.itf.port))
208 def noise_init(self, public_key=None):
209 self.noise.set_prologue(NOISE_IDENTIFIER_NAME)
210 self.noise.set_psks(psk=bytes(bytearray(32)))
213 public_key = self.itf.public_key
216 self.noise.set_keypair_from_private_bytes(
218 private_key_bytes(self.private_key))
220 self.noise.set_keypair_from_public_bytes(
221 Keypair.REMOTE_STATIC,
222 public_key_bytes(public_key))
224 self.noise.start_handshake()
226 def mk_handshake(self, tx_itf, is_ip6=False, public_key=None):
227 self.noise.set_as_initiator()
228 self.noise_init(public_key)
230 p = (Wireguard() / WireguardInitiation())
232 p[Wireguard].message_type = 1
233 p[Wireguard].reserved_zero = 0
234 p[WireguardInitiation].sender_index = self.receiver_index
236 # some random data for the message
237 # lifted from the noise protocol's wireguard example
238 now = datetime.datetime.now()
239 tai = struct.pack('!qi', 4611686018427387914 + int(now.timestamp()),
240 int(now.microsecond * 1e3))
241 b = self.noise.write_message(payload=tai)
243 # load noise into init message
244 p[WireguardInitiation].unencrypted_ephemeral = b[0:32]
245 p[WireguardInitiation].encrypted_static = b[32:80]
246 p[WireguardInitiation].encrypted_timestamp = b[80:108]
248 # generate the mac1 hash
249 mac_key = blake2s(b'mac1----' +
250 self.itf.public_key_bytes()).digest()
251 p[WireguardInitiation].mac1 = blake2s(bytes(p)[0:116],
253 key=mac_key).digest()
254 p[WireguardInitiation].mac2 = bytearray(16)
256 p = (self.mk_tunnel_header(tx_itf, is_ip6) / p)
260 def verify_header(self, p, is_ip6=False):
262 self._test.assertEqual(p[IP].src, self.itf.src)
263 self._test.assertEqual(p[IP].dst, self.endpoint)
265 self._test.assertEqual(p[IPv6].src, self.itf.src)
266 self._test.assertEqual(p[IPv6].dst, self.endpoint)
267 self._test.assertEqual(p[UDP].sport, self.itf.port)
268 self._test.assertEqual(p[UDP].dport, self.port)
269 self._test.assert_packet_checksums_valid(p)
271 def consume_init(self, p, tx_itf, is_ip6=False):
272 self.noise.set_as_responder()
273 self.noise_init(self.itf.public_key)
274 self.verify_header(p, is_ip6)
276 init = Wireguard(p[Raw])
278 self._test.assertEqual(init[Wireguard].message_type, 1)
279 self._test.assertEqual(init[Wireguard].reserved_zero, 0)
281 self.sender = init[WireguardInitiation].sender_index
284 mac_key = blake2s(b'mac1----' +
285 public_key_bytes(self.public_key)).digest()
286 mac1 = blake2s(bytes(init)[0:-32],
288 key=mac_key).digest()
289 self._test.assertEqual(init[WireguardInitiation].mac1, mac1)
291 # this passes only unencrypted_ephemeral, encrypted_static,
292 # encrypted_timestamp fields of the init
293 payload = self.noise.read_message(bytes(init)[8:-32])
296 b = self.noise.write_message()
297 mac_key = blake2s(b'mac1----' +
298 public_key_bytes(self.itf.public_key)).digest()
299 resp = (Wireguard(message_type=2, reserved_zero=0) /
300 WireguardResponse(sender_index=self.receiver_index,
301 receiver_index=self.sender,
302 unencrypted_ephemeral=b[0:32],
303 encrypted_nothing=b[32:]))
304 mac1 = blake2s(bytes(resp)[:-32],
306 key=mac_key).digest()
307 resp[WireguardResponse].mac1 = mac1
309 resp = (self.mk_tunnel_header(tx_itf, is_ip6) / resp)
310 self._test.assertTrue(self.noise.handshake_finished)
314 def consume_response(self, p, is_ip6=False):
315 self.verify_header(p, is_ip6)
317 resp = Wireguard(p[Raw])
319 self._test.assertEqual(resp[Wireguard].message_type, 2)
320 self._test.assertEqual(resp[Wireguard].reserved_zero, 0)
321 self._test.assertEqual(resp[WireguardResponse].receiver_index,
324 self.sender = resp[Wireguard].sender_index
326 payload = self.noise.read_message(bytes(resp)[12:60])
327 self._test.assertEqual(payload, b'')
328 self._test.assertTrue(self.noise.handshake_finished)
330 def decrypt_transport(self, p, is_ip6=False):
331 self.verify_header(p, is_ip6)
333 p = Wireguard(p[Raw])
334 self._test.assertEqual(p[Wireguard].message_type, 4)
335 self._test.assertEqual(p[Wireguard].reserved_zero, 0)
336 self._test.assertEqual(p[WireguardTransport].receiver_index,
339 d = self.noise.decrypt(
340 p[WireguardTransport].encrypted_encapsulated_packet)
343 def encrypt_transport(self, p):
344 return self.noise.encrypt(bytes(p))
346 def validate_encapped(self, rxs, tx, is_ip6=False):
349 rx = IP(self.decrypt_transport(rx))
351 # chech the oringial packet is present
352 self._test.assertEqual(rx[IP].dst, tx[IP].dst)
353 self._test.assertEqual(rx[IP].ttl, tx[IP].ttl-1)
355 rx = IPv6(self.decrypt_transport(rx))
357 # chech the oringial packet is present
358 self._test.assertEqual(rx[IPv6].dst, tx[IPv6].dst)
359 self._test.assertEqual(rx[IPv6].ttl, tx[IPv6].ttl-1)
361 def want_events(self):
362 self._test.vapi.want_wireguard_peer_events(
365 peer_index=self.index,
366 sw_if_index=self.itf.sw_if_index)
368 def wait_event(self, expect, timeout=5):
369 rv = self._test.vapi.wait_for_event(timeout, "wireguard_peer_event")
370 self._test.assertEqual(rv.flags, expect)
371 self._test.assertEqual(rv.peer_index, self.index)
374 class TestWg(VppTestCase):
375 """ Wireguard Test Case """
377 error_str = compile(r"Error")
379 wg4_output_node_name = '/err/wg4-output-tun/'
380 wg4_input_node_name = '/err/wg4-input/'
381 wg6_output_node_name = '/err/wg6-output-tun/'
382 wg6_input_node_name = '/err/wg6-input/'
383 kp4_error = wg4_output_node_name + "Keypair error"
384 mac4_error = wg4_input_node_name + "Invalid MAC handshake"
385 peer4_error = wg4_input_node_name + "Peer error"
386 kp6_error = wg6_output_node_name + "Keypair error"
387 mac6_error = wg6_input_node_name + "Invalid MAC handshake"
388 peer6_error = wg6_input_node_name + "Peer error"
392 super(TestWg, cls).setUpClass()
394 cls.create_pg_interfaces(range(3))
395 for i in cls.pg_interfaces:
403 super(TestWg, cls).tearDownClass()
407 def tearDownClass(cls):
408 super(TestWg, cls).tearDownClass()
411 super(VppTestCase, self).setUp()
412 self.base_kp4_err = self.statistics.get_err_counter(self.kp4_error)
413 self.base_mac4_err = self.statistics.get_err_counter(self.mac4_error)
414 self.base_peer4_err = self.statistics.get_err_counter(self.peer4_error)
415 self.base_kp6_err = self.statistics.get_err_counter(self.kp6_error)
416 self.base_mac6_err = self.statistics.get_err_counter(self.mac6_error)
417 self.base_peer6_err = self.statistics.get_err_counter(self.peer6_error)
419 def test_wg_interface(self):
420 """ Simple interface creation """
424 wg0 = VppWgInterface(self,
426 port).add_vpp_config()
428 self.logger.info(self.vapi.cli("sh int"))
431 wg0.remove_vpp_config()
433 def test_handshake_hash(self):
434 """ test hashing an init message """
435 # a init packet generated by linux given the key below
436 h = "0100000098b9032b" \
456 b = bytearray.fromhex(h)
459 pubb = base64.b64decode("aRuHFTTxICIQNefp05oKWlJv3zgKxb8+WW7JJMh0jyM=")
460 pub = X25519PublicKey.from_public_bytes(pubb)
462 self.assertEqual(pubb, public_key_bytes(pub))
464 # strip the macs and build a new packet
466 mac_key = blake2s(b'mac1----' + public_key_bytes(pub)).digest()
467 init += blake2s(init,
469 key=mac_key).digest()
472 act = Wireguard(init)
474 self.assertEqual(tgt, act)
476 def test_wg_peer_resp(self):
477 """ Send handshake response """
481 wg0 = VppWgInterface(self,
483 port).add_vpp_config()
487 self.pg_enable_capture(self.pg_interfaces)
490 peer_1 = VppWgPeer(self,
494 ["10.11.3.0/24"]).add_vpp_config()
495 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
497 r1 = VppIpRoute(self, "10.11.3.0", 24,
498 [VppRoutePath("10.11.3.1",
499 wg0.sw_if_index)]).add_vpp_config()
501 # wait for the peer to send a handshake
502 rx = self.pg1.get_capture(1, timeout=2)
504 # consume the handshake in the noise protocol and
505 # generate the response
506 resp = peer_1.consume_init(rx[0], self.pg1)
508 # send the response, get keepalive
509 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
512 b = peer_1.decrypt_transport(rx)
513 self.assertEqual(0, len(b))
515 # send a packets that are routed into the tunnel
516 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
517 IP(src=self.pg0.remote_ip4, dst="10.11.3.2") /
518 UDP(sport=555, dport=556) /
521 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
523 peer_1.validate_encapped(rxs, p)
525 # send packets into the tunnel, expect to receive them on
527 p = [(peer_1.mk_tunnel_header(self.pg1) /
528 Wireguard(message_type=4, reserved_zero=0) /
530 receiver_index=peer_1.sender,
532 encrypted_encapsulated_packet=peer_1.encrypt_transport(
533 (IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20) /
534 UDP(sport=222, dport=223) /
535 Raw())))) for ii in range(255)]
537 rxs = self.send_and_expect(self.pg1, p, self.pg0)
540 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
541 self.assertEqual(rx[IP].ttl, 19)
543 r1.remove_vpp_config()
544 peer_1.remove_vpp_config()
545 wg0.remove_vpp_config()
547 def test_wg_peer_v4o4(self):
553 wg0 = VppWgInterface(self,
555 port).add_vpp_config()
559 peer_1 = VppWgPeer(self,
563 ["10.11.3.0/24"]).add_vpp_config()
564 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
566 r1 = VppIpRoute(self, "10.11.3.0", 24,
567 [VppRoutePath("10.11.3.1",
568 wg0.sw_if_index)]).add_vpp_config()
570 # route a packet into the wg interface
571 # use the allowed-ip prefix
572 # this is dropped because the peer is not initiated
573 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
574 IP(src=self.pg0.remote_ip4, dst="10.11.3.2") /
575 UDP(sport=555, dport=556) /
577 self.send_and_assert_no_replies(self.pg0, [p])
578 self.assertEqual(self.base_kp4_err + 1,
579 self.statistics.get_err_counter(self.kp4_error))
581 # send a handsake from the peer with an invalid MAC
582 p = peer_1.mk_handshake(self.pg1)
583 p[WireguardInitiation].mac1 = b'foobar'
584 self.send_and_assert_no_replies(self.pg1, [p])
585 self.assertEqual(self.base_mac4_err + 1,
586 self.statistics.get_err_counter(self.mac4_error))
588 # send a handsake from the peer but signed by the wrong key.
589 p = peer_1.mk_handshake(self.pg1,
591 X25519PrivateKey.generate().public_key())
592 self.send_and_assert_no_replies(self.pg1, [p])
593 self.assertEqual(self.base_peer4_err + 1,
594 self.statistics.get_err_counter(self.peer4_error))
596 # send a valid handsake init for which we expect a response
597 p = peer_1.mk_handshake(self.pg1)
599 rx = self.send_and_expect(self.pg1, [p], self.pg1)
601 peer_1.consume_response(rx[0])
603 # route a packet into the wg interface
604 # this is dropped because the peer is still not initiated
605 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
606 IP(src=self.pg0.remote_ip4, dst="10.11.3.2") /
607 UDP(sport=555, dport=556) /
609 self.send_and_assert_no_replies(self.pg0, [p])
610 self.assertEqual(self.base_kp4_err + 2,
611 self.statistics.get_err_counter(self.kp4_error))
613 # send a data packet from the peer through the tunnel
614 # this completes the handshake
615 p = (IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20) /
616 UDP(sport=222, dport=223) /
618 d = peer_1.encrypt_transport(p)
619 p = (peer_1.mk_tunnel_header(self.pg1) /
620 (Wireguard(message_type=4, reserved_zero=0) /
621 WireguardTransport(receiver_index=peer_1.sender,
623 encrypted_encapsulated_packet=d)))
624 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
627 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
628 self.assertEqual(rx[IP].ttl, 19)
630 # send a packets that are routed into the tunnel
631 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
632 IP(src=self.pg0.remote_ip4, dst="10.11.3.2") /
633 UDP(sport=555, dport=556) /
636 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
639 rx = IP(peer_1.decrypt_transport(rx))
641 # chech the oringial packet is present
642 self.assertEqual(rx[IP].dst, p[IP].dst)
643 self.assertEqual(rx[IP].ttl, p[IP].ttl-1)
645 # send packets into the tunnel, expect to receive them on
647 p = [(peer_1.mk_tunnel_header(self.pg1) /
648 Wireguard(message_type=4, reserved_zero=0) /
650 receiver_index=peer_1.sender,
652 encrypted_encapsulated_packet=peer_1.encrypt_transport(
653 (IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20) /
654 UDP(sport=222, dport=223) /
655 Raw())))) for ii in range(255)]
657 rxs = self.send_and_expect(self.pg1, p, self.pg0)
660 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
661 self.assertEqual(rx[IP].ttl, 19)
663 r1.remove_vpp_config()
664 peer_1.remove_vpp_config()
665 wg0.remove_vpp_config()
667 def test_wg_peer_v6o6(self):
673 wg0 = VppWgInterface(self,
675 port).add_vpp_config()
679 peer_1 = VppWgPeer(self,
683 ["1::3:0/112"]).add_vpp_config(True)
684 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
686 r1 = VppIpRoute(self, "1::3:0", 112,
687 [VppRoutePath("1::3:1",
688 wg0.sw_if_index)]).add_vpp_config()
690 # route a packet into the wg interface
691 # use the allowed-ip prefix
692 # this is dropped because the peer is not initiated
694 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
695 IPv6(src=self.pg0.remote_ip6, dst="1::3:2") /
696 UDP(sport=555, dport=556) /
698 self.send_and_assert_no_replies(self.pg0, [p])
700 self.assertEqual(self.base_kp6_err + 1,
701 self.statistics.get_err_counter(self.kp6_error))
703 # send a handsake from the peer with an invalid MAC
704 p = peer_1.mk_handshake(self.pg1, True)
705 p[WireguardInitiation].mac1 = b'foobar'
706 self.send_and_assert_no_replies(self.pg1, [p])
708 self.assertEqual(self.base_mac6_err + 1,
709 self.statistics.get_err_counter(self.mac6_error))
711 # send a handsake from the peer but signed by the wrong key.
712 p = peer_1.mk_handshake(self.pg1,
714 X25519PrivateKey.generate().public_key())
715 self.send_and_assert_no_replies(self.pg1, [p])
716 self.assertEqual(self.base_peer6_err + 1,
717 self.statistics.get_err_counter(self.peer6_error))
719 # send a valid handsake init for which we expect a response
720 p = peer_1.mk_handshake(self.pg1, True)
722 rx = self.send_and_expect(self.pg1, [p], self.pg1)
724 peer_1.consume_response(rx[0], True)
726 # route a packet into the wg interface
727 # this is dropped because the peer is still not initiated
728 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
729 IPv6(src=self.pg0.remote_ip6, dst="1::3:2") /
730 UDP(sport=555, dport=556) /
732 self.send_and_assert_no_replies(self.pg0, [p])
733 self.assertEqual(self.base_kp6_err + 2,
734 self.statistics.get_err_counter(self.kp6_error))
736 # send a data packet from the peer through the tunnel
737 # this completes the handshake
738 p = (IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20) /
739 UDP(sport=222, dport=223) /
741 d = peer_1.encrypt_transport(p)
742 p = (peer_1.mk_tunnel_header(self.pg1, True) /
743 (Wireguard(message_type=4, reserved_zero=0) /
744 WireguardTransport(receiver_index=peer_1.sender,
746 encrypted_encapsulated_packet=d)))
747 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
750 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
751 self.assertEqual(rx[IPv6].hlim, 19)
753 # send a packets that are routed into the tunnel
754 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
755 IPv6(src=self.pg0.remote_ip6, dst="1::3:2") /
756 UDP(sport=555, dport=556) /
759 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
762 rx = IPv6(peer_1.decrypt_transport(rx, True))
764 # chech the oringial packet is present
765 self.assertEqual(rx[IPv6].dst, p[IPv6].dst)
766 self.assertEqual(rx[IPv6].hlim, p[IPv6].hlim-1)
768 # send packets into the tunnel, expect to receive them on
770 p = [(peer_1.mk_tunnel_header(self.pg1, True) /
771 Wireguard(message_type=4, reserved_zero=0) /
773 receiver_index=peer_1.sender,
775 encrypted_encapsulated_packet=peer_1.encrypt_transport(
776 (IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20) /
777 UDP(sport=222, dport=223) /
778 Raw())))) for ii in range(255)]
780 rxs = self.send_and_expect(self.pg1, p, self.pg0)
783 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
784 self.assertEqual(rx[IPv6].hlim, 19)
786 r1.remove_vpp_config()
787 peer_1.remove_vpp_config()
788 wg0.remove_vpp_config()
790 def test_wg_peer_v6o4(self):
796 wg0 = VppWgInterface(self,
798 port).add_vpp_config()
802 peer_1 = VppWgPeer(self,
806 ["1::3:0/112"]).add_vpp_config(True)
807 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
809 r1 = VppIpRoute(self, "1::3:0", 112,
810 [VppRoutePath("1::3:1",
811 wg0.sw_if_index)]).add_vpp_config()
813 # route a packet into the wg interface
814 # use the allowed-ip prefix
815 # this is dropped because the peer is not initiated
816 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
817 IPv6(src=self.pg0.remote_ip6, dst="1::3:2") /
818 UDP(sport=555, dport=556) /
820 self.send_and_assert_no_replies(self.pg0, [p])
821 self.assertEqual(self.base_kp6_err + 1,
822 self.statistics.get_err_counter(self.kp6_error))
824 # send a handsake from the peer with an invalid MAC
825 p = peer_1.mk_handshake(self.pg1)
826 p[WireguardInitiation].mac1 = b'foobar'
827 self.send_and_assert_no_replies(self.pg1, [p])
829 self.assertEqual(self.base_mac4_err + 1,
830 self.statistics.get_err_counter(self.mac4_error))
832 # send a handsake from the peer but signed by the wrong key.
833 p = peer_1.mk_handshake(self.pg1,
835 X25519PrivateKey.generate().public_key())
836 self.send_and_assert_no_replies(self.pg1, [p])
837 self.assertEqual(self.base_peer4_err + 1,
838 self.statistics.get_err_counter(self.peer4_error))
840 # send a valid handsake init for which we expect a response
841 p = peer_1.mk_handshake(self.pg1)
843 rx = self.send_and_expect(self.pg1, [p], self.pg1)
845 peer_1.consume_response(rx[0])
847 # route a packet into the wg interface
848 # this is dropped because the peer is still not initiated
849 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
850 IPv6(src=self.pg0.remote_ip6, dst="1::3:2") /
851 UDP(sport=555, dport=556) /
853 self.send_and_assert_no_replies(self.pg0, [p])
854 self.assertEqual(self.base_kp6_err + 2,
855 self.statistics.get_err_counter(self.kp6_error))
857 # send a data packet from the peer through the tunnel
858 # this completes the handshake
859 p = (IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20) /
860 UDP(sport=222, dport=223) /
862 d = peer_1.encrypt_transport(p)
863 p = (peer_1.mk_tunnel_header(self.pg1) /
864 (Wireguard(message_type=4, reserved_zero=0) /
865 WireguardTransport(receiver_index=peer_1.sender,
867 encrypted_encapsulated_packet=d)))
868 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
871 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
872 self.assertEqual(rx[IPv6].hlim, 19)
874 # send a packets that are routed into the tunnel
875 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
876 IPv6(src=self.pg0.remote_ip6, dst="1::3:2") /
877 UDP(sport=555, dport=556) /
880 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
883 rx = IPv6(peer_1.decrypt_transport(rx))
885 # chech the oringial packet is present
886 self.assertEqual(rx[IPv6].dst, p[IPv6].dst)
887 self.assertEqual(rx[IPv6].hlim, p[IPv6].hlim-1)
889 # send packets into the tunnel, expect to receive them on
891 p = [(peer_1.mk_tunnel_header(self.pg1) /
892 Wireguard(message_type=4, reserved_zero=0) /
894 receiver_index=peer_1.sender,
896 encrypted_encapsulated_packet=peer_1.encrypt_transport(
897 (IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20) /
898 UDP(sport=222, dport=223) /
899 Raw())))) for ii in range(255)]
901 rxs = self.send_and_expect(self.pg1, p, self.pg0)
904 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
905 self.assertEqual(rx[IPv6].hlim, 19)
907 r1.remove_vpp_config()
908 peer_1.remove_vpp_config()
909 wg0.remove_vpp_config()
911 def test_wg_peer_v4o6(self):
917 wg0 = VppWgInterface(self,
919 port).add_vpp_config()
923 peer_1 = VppWgPeer(self,
927 ["10.11.3.0/24"]).add_vpp_config()
928 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
930 r1 = VppIpRoute(self, "10.11.3.0", 24,
931 [VppRoutePath("10.11.3.1",
932 wg0.sw_if_index)]).add_vpp_config()
934 # route a packet into the wg interface
935 # use the allowed-ip prefix
936 # this is dropped because the peer is not initiated
937 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
938 IP(src=self.pg0.remote_ip4, dst="10.11.3.2") /
939 UDP(sport=555, dport=556) /
941 self.send_and_assert_no_replies(self.pg0, [p])
942 self.assertEqual(self.base_kp4_err + 1,
943 self.statistics.get_err_counter(self.kp4_error))
945 # send a handsake from the peer with an invalid MAC
946 p = peer_1.mk_handshake(self.pg1, True)
947 p[WireguardInitiation].mac1 = b'foobar'
948 self.send_and_assert_no_replies(self.pg1, [p])
949 self.assertEqual(self.base_mac6_err + 1,
950 self.statistics.get_err_counter(self.mac6_error))
952 # send a handsake from the peer but signed by the wrong key.
953 p = peer_1.mk_handshake(self.pg1,
955 X25519PrivateKey.generate().public_key())
956 self.send_and_assert_no_replies(self.pg1, [p])
957 self.assertEqual(self.base_peer6_err + 1,
958 self.statistics.get_err_counter(self.peer6_error))
960 # send a valid handsake init for which we expect a response
961 p = peer_1.mk_handshake(self.pg1, True)
963 rx = self.send_and_expect(self.pg1, [p], self.pg1)
965 peer_1.consume_response(rx[0], True)
967 # route a packet into the wg interface
968 # this is dropped because the peer is still not initiated
969 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
970 IP(src=self.pg0.remote_ip4, dst="10.11.3.2") /
971 UDP(sport=555, dport=556) /
973 self.send_and_assert_no_replies(self.pg0, [p])
974 self.assertEqual(self.base_kp4_err + 2,
975 self.statistics.get_err_counter(self.kp4_error))
977 # send a data packet from the peer through the tunnel
978 # this completes the handshake
979 p = (IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20) /
980 UDP(sport=222, dport=223) /
982 d = peer_1.encrypt_transport(p)
983 p = (peer_1.mk_tunnel_header(self.pg1, True) /
984 (Wireguard(message_type=4, reserved_zero=0) /
985 WireguardTransport(receiver_index=peer_1.sender,
987 encrypted_encapsulated_packet=d)))
988 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
991 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
992 self.assertEqual(rx[IP].ttl, 19)
994 # send a packets that are routed into the tunnel
995 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
996 IP(src=self.pg0.remote_ip4, dst="10.11.3.2") /
997 UDP(sport=555, dport=556) /
1000 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1003 rx = IP(peer_1.decrypt_transport(rx, True))
1005 # chech the oringial packet is present
1006 self.assertEqual(rx[IP].dst, p[IP].dst)
1007 self.assertEqual(rx[IP].ttl, p[IP].ttl-1)
1009 # send packets into the tunnel, expect to receive them on
1011 p = [(peer_1.mk_tunnel_header(self.pg1, True) /
1012 Wireguard(message_type=4, reserved_zero=0) /
1014 receiver_index=peer_1.sender,
1016 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1017 (IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20) /
1018 UDP(sport=222, dport=223) /
1019 Raw())))) for ii in range(255)]
1021 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1024 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1025 self.assertEqual(rx[IP].ttl, 19)
1027 r1.remove_vpp_config()
1028 peer_1.remove_vpp_config()
1029 wg0.remove_vpp_config()
1031 def test_wg_multi_peer(self):
1032 """ multiple peer setup """
1036 wg0 = VppWgInterface(self,
1038 port).add_vpp_config()
1039 wg1 = VppWgInterface(self,
1041 port+1).add_vpp_config()
1045 # Check peer counter
1046 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 0)
1048 self.pg_enable_capture(self.pg_interfaces)
1051 # Create many peers on sencond interface
1053 self.pg2.generate_remote_hosts(NUM_PEERS)
1054 self.pg2.configure_ipv4_neighbors()
1055 self.pg1.generate_remote_hosts(NUM_PEERS)
1056 self.pg1.configure_ipv4_neighbors()
1062 for i in range(NUM_PEERS):
1063 peers_1.append(VppWgPeer(self,
1065 self.pg1.remote_hosts[i].ip4,
1067 ["10.0.%d.4/32" % i]).add_vpp_config())
1068 routes_1.append(VppIpRoute(self, "10.0.%d.4" % i, 32,
1069 [VppRoutePath(self.pg1.remote_hosts[i].ip4,
1070 wg0.sw_if_index)]).add_vpp_config())
1072 peers_2.append(VppWgPeer(self,
1074 self.pg2.remote_hosts[i].ip4,
1076 ["10.100.%d.4/32" % i]).add_vpp_config())
1077 routes_2.append(VppIpRoute(self, "10.100.%d.4" % i, 32,
1078 [VppRoutePath(self.pg2.remote_hosts[i].ip4,
1079 wg1.sw_if_index)]).add_vpp_config())
1081 self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_PEERS*2)
1083 self.logger.info(self.vapi.cli("show wireguard peer"))
1084 self.logger.info(self.vapi.cli("show wireguard interface"))
1085 self.logger.info(self.vapi.cli("show adj 37"))
1086 self.logger.info(self.vapi.cli("sh ip fib 172.16.3.17"))
1087 self.logger.info(self.vapi.cli("sh ip fib 10.11.3.0"))
1091 r.remove_vpp_config()
1093 r.remove_vpp_config()
1097 self.assertTrue(p.query_vpp_config())
1098 p.remove_vpp_config()
1100 self.assertTrue(p.query_vpp_config())
1101 p.remove_vpp_config()
1103 wg0.remove_vpp_config()
1104 wg1.remove_vpp_config()
1106 def test_wg_multi_interface(self):
1107 """ Multi-tunnel on the same port """
1110 # Create many wireguard interfaces
1112 self.pg1.generate_remote_hosts(NUM_IFS)
1113 self.pg1.configure_ipv4_neighbors()
1114 self.pg0.generate_remote_hosts(NUM_IFS)
1115 self.pg0.configure_ipv4_neighbors()
1117 # Create interfaces with a peer on each
1121 for i in range(NUM_IFS):
1122 # Use the same port for each interface
1123 wg0 = VppWgInterface(self,
1125 port).add_vpp_config()
1129 peers.append(VppWgPeer(self,
1131 self.pg1.remote_hosts[i].ip4,
1133 ["10.0.%d.0/24" % i]).add_vpp_config())
1135 routes.append(VppIpRoute(self, "10.0.%d.0" % i, 24,
1136 [VppRoutePath("10.0.%d.4" % i,
1137 wg0.sw_if_index)]).add_vpp_config())
1139 self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_IFS)
1141 for i in range(NUM_IFS):
1142 # send a valid handsake init for which we expect a response
1143 p = peers[i].mk_handshake(self.pg1)
1144 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1145 peers[i].consume_response(rx[0])
1147 # send a data packet from the peer through the tunnel
1148 # this completes the handshake
1149 p = (IP(src="10.0.%d.4" % i,
1150 dst=self.pg0.remote_hosts[i].ip4, ttl=20) /
1151 UDP(sport=222, dport=223) /
1153 d = peers[i].encrypt_transport(p)
1154 p = (peers[i].mk_tunnel_header(self.pg1) /
1155 (Wireguard(message_type=4, reserved_zero=0) /
1156 WireguardTransport(receiver_index=peers[i].sender,
1158 encrypted_encapsulated_packet=d)))
1159 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1161 self.assertEqual(rx[IP].dst, self.pg0.remote_hosts[i].ip4)
1162 self.assertEqual(rx[IP].ttl, 19)
1164 # send a packets that are routed into the tunnel
1165 for i in range(NUM_IFS):
1166 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
1167 IP(src=self.pg0.remote_hosts[i].ip4, dst="10.0.%d.4" % i) /
1168 UDP(sport=555, dport=556) /
1171 rxs = self.send_and_expect(self.pg0, p * 64, self.pg1)
1174 rx = IP(peers[i].decrypt_transport(rx))
1176 # check the oringial packet is present
1177 self.assertEqual(rx[IP].dst, p[IP].dst)
1178 self.assertEqual(rx[IP].ttl, p[IP].ttl-1)
1180 # send packets into the tunnel
1181 for i in range(NUM_IFS):
1182 p = [(peers[i].mk_tunnel_header(self.pg1) /
1183 Wireguard(message_type=4, reserved_zero=0) /
1185 receiver_index=peers[i].sender,
1187 encrypted_encapsulated_packet=peers[i].encrypt_transport(
1188 (IP(src="10.0.%d.4" % i,
1189 dst=self.pg0.remote_hosts[i].ip4, ttl=20) /
1190 UDP(sport=222, dport=223) /
1191 Raw())))) for ii in range(64)]
1193 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1196 self.assertEqual(rx[IP].dst, self.pg0.remote_hosts[i].ip4)
1197 self.assertEqual(rx[IP].ttl, 19)
1200 r.remove_vpp_config()
1202 p.remove_vpp_config()
1204 i.remove_vpp_config()
1206 def test_wg_event(self):
1209 ESTABLISHED_FLAG = VppEnum.\
1210 vl_api_wireguard_peer_flags_t.\
1211 WIREGUARD_PEER_ESTABLISHED
1212 DEAD_FLAG = VppEnum.\
1213 vl_api_wireguard_peer_flags_t.\
1214 WIREGUARD_PEER_STATUS_DEAD
1217 wg0 = VppWgInterface(self,
1219 port).add_vpp_config()
1220 wg1 = VppWgInterface(self,
1222 port+1).add_vpp_config()
1226 # Check peer counter
1227 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 0)
1229 self.pg_enable_capture(self.pg_interfaces)
1234 self.pg2.generate_remote_hosts(NUM_PEERS)
1235 self.pg2.configure_ipv4_neighbors()
1236 self.pg1.generate_remote_hosts(NUM_PEERS)
1237 self.pg1.configure_ipv4_neighbors()
1243 for i in range(NUM_PEERS):
1244 peers_0.append(VppWgPeer(self,
1246 self.pg1.remote_hosts[i].ip4,
1248 ["10.0.%d.4/32" % i]).add_vpp_config())
1249 routes_0.append(VppIpRoute(self, "10.0.%d.4" % i, 32,
1250 [VppRoutePath(self.pg1.remote_hosts[i].ip4,
1251 wg0.sw_if_index)]).add_vpp_config())
1253 peers_1.append(VppWgPeer(self,
1255 self.pg2.remote_hosts[i].ip4,
1257 ["10.100.%d.4/32" % i]).add_vpp_config())
1258 routes_1.append(VppIpRoute(self, "10.100.%d.4" % i, 32,
1259 [VppRoutePath(self.pg2.remote_hosts[i].ip4,
1260 wg1.sw_if_index)]).add_vpp_config())
1262 self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_PEERS*2)
1264 # Want events from the first perr of wg0
1265 # and from all wg1 peers
1266 peers_0[0].want_events()
1269 for i in range(NUM_PEERS):
1270 # send a valid handsake init for which we expect a response
1271 p = peers_0[i].mk_handshake(self.pg1)
1272 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1273 peers_0[i].consume_response(rx[0])
1275 peers_0[0].wait_event(ESTABLISHED_FLAG)
1277 p = peers_1[i].mk_handshake(self.pg2)
1278 rx = self.send_and_expect(self.pg2, [p], self.pg2)
1279 peers_1[i].consume_response(rx[0])
1283 [peers_1[0].index, peers_1[1].index])
1287 r.remove_vpp_config()
1289 r.remove_vpp_config()
1292 for i in range(NUM_PEERS):
1293 self.assertTrue(peers_0[i].query_vpp_config())
1294 peers_0[i].remove_vpp_config()
1296 peers_0[i].wait_event(0)
1297 peers_0[i].wait_event(DEAD_FLAG)
1299 self.assertTrue(p.query_vpp_config())
1300 p.remove_vpp_config()
1302 p.wait_event(DEAD_FLAG)
1304 wg0.remove_vpp_config()
1305 wg1.remove_vpp_config()
1308 class WireguardHandoffTests(TestWg):
1309 """ Wireguard Tests in multi worker setup """
1310 vpp_worker_count = 2
1312 def test_wg_peer_init(self):
1318 wg0 = VppWgInterface(self,
1320 port).add_vpp_config()
1324 peer_1 = VppWgPeer(self,
1326 self.pg1.remote_ip4,
1329 "10.11.3.0/24"]).add_vpp_config()
1330 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1332 r1 = VppIpRoute(self, "10.11.3.0", 24,
1333 [VppRoutePath("10.11.3.1",
1334 wg0.sw_if_index)]).add_vpp_config()
1336 # send a valid handsake init for which we expect a response
1337 p = peer_1.mk_handshake(self.pg1)
1339 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1341 peer_1.consume_response(rx[0])
1343 # send a data packet from the peer through the tunnel
1344 # this completes the handshake and pins the peer to worker 0
1345 p = (IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20) /
1346 UDP(sport=222, dport=223) /
1348 d = peer_1.encrypt_transport(p)
1349 p = (peer_1.mk_tunnel_header(self.pg1) /
1350 (Wireguard(message_type=4, reserved_zero=0) /
1351 WireguardTransport(receiver_index=peer_1.sender,
1353 encrypted_encapsulated_packet=d)))
1354 rxs = self.send_and_expect(self.pg1, [p], self.pg0,
1358 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1359 self.assertEqual(rx[IP].ttl, 19)
1361 # send a packets that are routed into the tunnel
1362 # and pins the peer tp worker 1
1363 pe = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
1364 IP(src=self.pg0.remote_ip4, dst="10.11.3.2") /
1365 UDP(sport=555, dport=556) /
1367 rxs = self.send_and_expect(self.pg0, pe * 255, self.pg1, worker=1)
1368 peer_1.validate_encapped(rxs, pe)
1370 # send packets into the tunnel, from the other worker
1371 p = [(peer_1.mk_tunnel_header(self.pg1) /
1372 Wireguard(message_type=4, reserved_zero=0) /
1374 receiver_index=peer_1.sender,
1376 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1377 (IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20) /
1378 UDP(sport=222, dport=223) /
1379 Raw())))) for ii in range(255)]
1381 rxs = self.send_and_expect(self.pg1, p, self.pg0, worker=1)
1384 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1385 self.assertEqual(rx[IP].ttl, 19)
1387 # send a packets that are routed into the tunnel
1389 rxs = self.send_and_expect(self.pg0, pe * 255, self.pg1, worker=0)
1391 peer_1.validate_encapped(rxs, pe)
1393 r1.remove_vpp_config()
1394 peer_1.remove_vpp_config()
1395 wg0.remove_vpp_config()
1397 @unittest.skip("test disabled")
1398 def test_wg_multi_interface(self):
1399 """ Multi-tunnel on the same port """