blob: b9713f6fc080a8cf24149de01a4f77e7457dcd06 [file] [log] [blame]
Artem Glazychevedca1322020-08-31 17:12:30 +07001#!/usr/bin/env python3
2""" Wg tests """
3
Neale Rannsd75a2d12020-09-10 08:49:10 +00004import datetime
5import base64
Artem Glazychevdd630d12021-06-11 00:10:00 +07006import os
Neale Rannsd75a2d12020-09-10 08:49:10 +00007
8from hashlib import blake2s
Artem Glazychevedca1322020-08-31 17:12:30 +07009from scapy.packet import Packet
10from scapy.packet import Raw
Neale Rannsd75a2d12020-09-10 08:49:10 +000011from scapy.layers.l2 import Ether, ARP
Artem Glazychevedca1322020-08-31 17:12:30 +070012from scapy.layers.inet import IP, UDP
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +070013from scapy.layers.inet6 import IPv6
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020014from scapy.contrib.wireguard import (
15 Wireguard,
16 WireguardResponse,
17 WireguardInitiation,
18 WireguardTransport,
Alexander Chernavin44ec8462022-07-20 10:48:56 +000019 WireguardCookieReply,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020020)
21from cryptography.hazmat.primitives.asymmetric.x25519 import (
22 X25519PrivateKey,
23 X25519PublicKey,
24)
25from cryptography.hazmat.primitives.serialization import (
26 Encoding,
27 PrivateFormat,
28 PublicFormat,
29 NoEncryption,
30)
Neale Rannsd75a2d12020-09-10 08:49:10 +000031from cryptography.hazmat.primitives.hashes import BLAKE2s, Hash
32from cryptography.hazmat.primitives.hmac import HMAC
33from cryptography.hazmat.backends import default_backend
34from noise.connection import NoiseConnection, Keypair
Artem Glazychevedca1322020-08-31 17:12:30 +070035
Alexander Chernavin44ec8462022-07-20 10:48:56 +000036from Crypto.Cipher import ChaCha20_Poly1305
37from Crypto.Random import get_random_bytes
38
Artem Glazychevedca1322020-08-31 17:12:30 +070039from vpp_ipip_tun_interface import VppIpIpTunInterface
40from vpp_interface import VppInterface
Alexander Chernavin522a5b32022-09-26 15:11:27 +000041from vpp_pg_interface import is_ipv6_misc
Artem Glazychevde3caf32021-05-20 12:33:52 +070042from vpp_ip_route import VppIpRoute, VppRoutePath
Artem Glazychevedca1322020-08-31 17:12:30 +070043from vpp_object import VppObject
Artem Glazychevdd630d12021-06-11 00:10:00 +070044from vpp_papi import VppEnum
Dave Wallace8a0a9d22022-10-04 22:02:49 -040045from framework import is_distro_ubuntu2204, is_distro_debian11, tag_fixme_vpp_debug
Artem Glazychevedca1322020-08-31 17:12:30 +070046from framework import VppTestCase
47from re import compile
48import unittest
49
50""" TestWg is a subclass of VPPTestCase classes.
51
52Wg test.
53
54"""
55
56
Neale Rannsd75a2d12020-09-10 08:49:10 +000057def private_key_bytes(k):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020058 return k.private_bytes(Encoding.Raw, PrivateFormat.Raw, NoEncryption())
Neale Rannsd75a2d12020-09-10 08:49:10 +000059
60
61def public_key_bytes(k):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020062 return k.public_bytes(Encoding.Raw, PublicFormat.Raw)
Neale Rannsd75a2d12020-09-10 08:49:10 +000063
64
Alexander Chernavin44ec8462022-07-20 10:48:56 +000065def get_field_bytes(pkt, name):
66 fld, val = pkt.getfield_and_val(name)
67 return fld.i2m(pkt, val)
68
69
Artem Glazychevedca1322020-08-31 17:12:30 +070070class VppWgInterface(VppInterface):
71 """
72 VPP WireGuard interface
73 """
74
Neale Rannsd75a2d12020-09-10 08:49:10 +000075 def __init__(self, test, src, port):
Artem Glazychevedca1322020-08-31 17:12:30 +070076 super(VppWgInterface, self).__init__(test)
77
Artem Glazychevedca1322020-08-31 17:12:30 +070078 self.port = port
79 self.src = src
Neale Rannsd75a2d12020-09-10 08:49:10 +000080 self.private_key = X25519PrivateKey.generate()
81 self.public_key = self.private_key.public_key()
82
Alexander Chernavince91af82022-07-20 12:43:42 +000083 # cookie related params
84 self.cookie_key = blake2s(b"cookie--" + self.public_key_bytes()).digest()
85
Neale Rannsd75a2d12020-09-10 08:49:10 +000086 def public_key_bytes(self):
87 return public_key_bytes(self.public_key)
88
89 def private_key_bytes(self):
90 return private_key_bytes(self.private_key)
Artem Glazychevedca1322020-08-31 17:12:30 +070091
92 def add_vpp_config(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020093 r = self.test.vapi.wireguard_interface_create(
94 interface={
95 "user_instance": 0xFFFFFFFF,
96 "port": self.port,
97 "src_ip": self.src,
98 "private_key": private_key_bytes(self.private_key),
99 "generate_key": False,
100 }
101 )
Artem Glazychevedca1322020-08-31 17:12:30 +0700102 self.set_sw_if_index(r.sw_if_index)
103 self.test.registry.register(self, self.test.logger)
104 return self
105
Artem Glazychevedca1322020-08-31 17:12:30 +0700106 def remove_vpp_config(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200107 self.test.vapi.wireguard_interface_delete(sw_if_index=self._sw_if_index)
Artem Glazychevedca1322020-08-31 17:12:30 +0700108
109 def query_vpp_config(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200110 ts = self.test.vapi.wireguard_interface_dump(sw_if_index=0xFFFFFFFF)
Artem Glazychevedca1322020-08-31 17:12:30 +0700111 for t in ts:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200112 if (
113 t.interface.sw_if_index == self._sw_if_index
114 and str(t.interface.src_ip) == self.src
115 and t.interface.port == self.port
116 and t.interface.private_key == private_key_bytes(self.private_key)
117 ):
Artem Glazychevedca1322020-08-31 17:12:30 +0700118 return True
119 return False
120
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200121 def want_events(self, peer_index=0xFFFFFFFF):
Artem Glazychevdd630d12021-06-11 00:10:00 +0700122 self.test.vapi.want_wireguard_peer_events(
123 enable_disable=1,
124 pid=os.getpid(),
125 sw_if_index=self._sw_if_index,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200126 peer_index=peer_index,
127 )
Artem Glazychevdd630d12021-06-11 00:10:00 +0700128
129 def wait_events(self, expect, peers, timeout=5):
130 for i in range(len(peers)):
131 rv = self.test.vapi.wait_for_event(timeout, "wireguard_peer_event")
132 self.test.assertEqual(rv.peer_index, peers[i])
133 self.test.assertEqual(rv.flags, expect)
134
Artem Glazychevedca1322020-08-31 17:12:30 +0700135 def __str__(self):
136 return self.object_id()
137
138 def object_id(self):
139 return "wireguard-%d" % self._sw_if_index
140
141
Neale Rannsd75a2d12020-09-10 08:49:10 +0000142NOISE_HANDSHAKE_NAME = b"Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s"
143NOISE_IDENTIFIER_NAME = b"WireGuard v1 zx2c4 Jason@zx2c4.com"
144
Alexander Chernavince91af82022-07-20 12:43:42 +0000145HANDSHAKE_COUNTING_INTERVAL = 0.5
146UNDER_LOAD_INTERVAL = 1.0
147HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD = 40
Alexander Chernavina6328e52022-07-20 13:01:42 +0000148HANDSHAKE_NUM_BEFORE_RATELIMITING = 5
Alexander Chernavince91af82022-07-20 12:43:42 +0000149
Artem Glazychev53badfc2023-01-24 16:10:29 +0700150HANDSHAKE_JITTER = 0.5
151
Neale Rannsd75a2d12020-09-10 08:49:10 +0000152
Artem Glazychevedca1322020-08-31 17:12:30 +0700153class VppWgPeer(VppObject):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200154 def __init__(self, test, itf, endpoint, port, allowed_ips, persistent_keepalive=15):
Artem Glazychevedca1322020-08-31 17:12:30 +0700155 self._test = test
156 self.itf = itf
157 self.endpoint = endpoint
158 self.port = port
159 self.allowed_ips = allowed_ips
160 self.persistent_keepalive = persistent_keepalive
Neale Rannsd75a2d12020-09-10 08:49:10 +0000161
162 # remote peer's public
Artem Glazychevedca1322020-08-31 17:12:30 +0700163 self.private_key = X25519PrivateKey.generate()
164 self.public_key = self.private_key.public_key()
Neale Rannsd75a2d12020-09-10 08:49:10 +0000165
Alexander Chernavin44ec8462022-07-20 10:48:56 +0000166 # cookie related params
167 self.cookie_key = blake2s(b"cookie--" + self.public_key_bytes()).digest()
168 self.last_sent_cookie = None
Alexander Chernavince91af82022-07-20 12:43:42 +0000169 self.last_mac1 = None
170 self.last_received_cookie = None
Alexander Chernavin44ec8462022-07-20 10:48:56 +0000171
Neale Rannsd75a2d12020-09-10 08:49:10 +0000172 self.noise = NoiseConnection.from_name(NOISE_HANDSHAKE_NAME)
Artem Glazychevedca1322020-08-31 17:12:30 +0700173
Alexander Chernavinfee98532022-08-04 08:11:57 +0000174 def change_endpoint(self, endpoint, port):
175 self.endpoint = endpoint
176 self.port = port
177
Alexander Chernavin522a5b32022-09-26 15:11:27 +0000178 def add_vpp_config(self):
Artem Glazychevedca1322020-08-31 17:12:30 +0700179 rv = self._test.vapi.wireguard_peer_add(
180 peer={
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200181 "public_key": self.public_key_bytes(),
182 "port": self.port,
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,
188 }
189 )
Artem Glazychevedca1322020-08-31 17:12:30 +0700190 self.index = rv.peer_index
Neale Rannsd75a2d12020-09-10 08:49:10 +0000191 self.receiver_index = self.index + 1
Artem Glazychevedca1322020-08-31 17:12:30 +0700192 self._test.registry.register(self, self._test.logger)
Artem Glazychevedca1322020-08-31 17:12:30 +0700193 return self
194
195 def remove_vpp_config(self):
196 self._test.vapi.wireguard_peer_remove(peer_index=self.index)
Artem Glazychevedca1322020-08-31 17:12:30 +0700197
198 def object_id(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200199 return "wireguard-peer-%s" % self.index
Artem Glazychevedca1322020-08-31 17:12:30 +0700200
201 def public_key_bytes(self):
Neale Rannsd75a2d12020-09-10 08:49:10 +0000202 return public_key_bytes(self.public_key)
Artem Glazychevedca1322020-08-31 17:12:30 +0700203
204 def query_vpp_config(self):
205 peers = self._test.vapi.wireguard_peers_dump()
206
207 for p in peers:
Alexander Chernavinfee98532022-08-04 08:11:57 +0000208 # "::" endpoint will be returned as "0.0.0.0" in peer's details
209 endpoint = "0.0.0.0" if self.endpoint == "::" else self.endpoint
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200210 if (
211 p.peer.public_key == self.public_key_bytes()
212 and p.peer.port == self.port
Alexander Chernavinfee98532022-08-04 08:11:57 +0000213 and str(p.peer.endpoint) == endpoint
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200214 and p.peer.sw_if_index == self.itf.sw_if_index
215 and len(self.allowed_ips) == p.peer.n_allowed_ips
216 ):
Artem Glazychevedca1322020-08-31 17:12:30 +0700217 self.allowed_ips.sort()
218 p.peer.allowed_ips.sort()
219
220 for (a1, a2) in zip(self.allowed_ips, p.peer.allowed_ips):
221 if str(a1) != str(a2):
222 return False
223 return True
224 return False
225
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +0700226 def mk_tunnel_header(self, tx_itf, is_ip6=False):
227 if is_ip6 is False:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200228 return (
229 Ether(dst=tx_itf.local_mac, src=tx_itf.remote_mac)
230 / IP(src=self.endpoint, dst=self.itf.src)
231 / UDP(sport=self.port, dport=self.itf.port)
232 )
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +0700233 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200234 return (
235 Ether(dst=tx_itf.local_mac, src=tx_itf.remote_mac)
236 / IPv6(src=self.endpoint, dst=self.itf.src)
237 / UDP(sport=self.port, dport=self.itf.port)
238 )
Neale Rannsd75a2d12020-09-10 08:49:10 +0000239
Alexander Chernavince91af82022-07-20 12:43:42 +0000240 def noise_reset(self):
241 self.noise = NoiseConnection.from_name(NOISE_HANDSHAKE_NAME)
242
Neale Rannsd75a2d12020-09-10 08:49:10 +0000243 def noise_init(self, public_key=None):
244 self.noise.set_prologue(NOISE_IDENTIFIER_NAME)
245 self.noise.set_psks(psk=bytes(bytearray(32)))
246
247 if not public_key:
248 public_key = self.itf.public_key
249
250 # local/this private
251 self.noise.set_keypair_from_private_bytes(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200252 Keypair.STATIC, private_key_bytes(self.private_key)
253 )
Neale Rannsd75a2d12020-09-10 08:49:10 +0000254 # remote's public
255 self.noise.set_keypair_from_public_bytes(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200256 Keypair.REMOTE_STATIC, public_key_bytes(public_key)
257 )
Neale Rannsd75a2d12020-09-10 08:49:10 +0000258
259 self.noise.start_handshake()
260
Alexander Chernavin44ec8462022-07-20 10:48:56 +0000261 def mk_cookie(self, p, tx_itf, is_resp=False, is_ip6=False):
262 self.verify_header(p, is_ip6)
263
264 wg_pkt = Wireguard(p[Raw])
265
266 if is_resp:
267 self._test.assertEqual(wg_pkt[Wireguard].message_type, 2)
268 self._test.assertEqual(wg_pkt[Wireguard].reserved_zero, 0)
269 self._test.assertEqual(wg_pkt[WireguardResponse].mac2, bytes([0] * 16))
270 else:
271 self._test.assertEqual(wg_pkt[Wireguard].message_type, 1)
272 self._test.assertEqual(wg_pkt[Wireguard].reserved_zero, 0)
273 self._test.assertEqual(wg_pkt[WireguardInitiation].mac2, bytes([0] * 16))
274
275 # collect info from wg packet (initiation or response)
276 src = get_field_bytes(p[IPv6 if is_ip6 else IP], "src")
277 sport = p[UDP].sport.to_bytes(2, byteorder="big")
278 if is_resp:
279 mac1 = wg_pkt[WireguardResponse].mac1
280 sender_index = wg_pkt[WireguardResponse].sender_index
281 else:
282 mac1 = wg_pkt[WireguardInitiation].mac1
283 sender_index = wg_pkt[WireguardInitiation].sender_index
284
285 # make cookie reply
286 cookie_reply = Wireguard() / WireguardCookieReply()
287 cookie_reply[Wireguard].message_type = 3
288 cookie_reply[Wireguard].reserved_zero = 0
289 cookie_reply[WireguardCookieReply].receiver_index = sender_index
290 nonce = get_random_bytes(24)
291 cookie_reply[WireguardCookieReply].nonce = nonce
292
293 # generate cookie data
294 changing_secret = get_random_bytes(32)
295 self.last_sent_cookie = blake2s(
296 src + sport, digest_size=16, key=changing_secret
297 ).digest()
298
299 # encrypt cookie data
300 cipher = ChaCha20_Poly1305.new(key=self.cookie_key, nonce=nonce)
301 cipher.update(mac1)
302 ciphertext, tag = cipher.encrypt_and_digest(self.last_sent_cookie)
303 cookie_reply[WireguardCookieReply].encrypted_cookie = ciphertext + tag
304
305 # prepare cookie reply to be sent
306 cookie_reply = self.mk_tunnel_header(tx_itf, is_ip6) / cookie_reply
307
308 return cookie_reply
309
Alexander Chernavince91af82022-07-20 12:43:42 +0000310 def consume_cookie(self, p, is_ip6=False):
311 self.verify_header(p, is_ip6)
312
313 cookie_reply = Wireguard(p[Raw])
314
315 self._test.assertEqual(cookie_reply[Wireguard].message_type, 3)
316 self._test.assertEqual(cookie_reply[Wireguard].reserved_zero, 0)
317 self._test.assertEqual(
318 cookie_reply[WireguardCookieReply].receiver_index, self.receiver_index
319 )
320
321 # collect info from cookie reply
322 nonce = cookie_reply[WireguardCookieReply].nonce
323 encrypted_cookie = cookie_reply[WireguardCookieReply].encrypted_cookie
324 ciphertext, tag = encrypted_cookie[:16], encrypted_cookie[16:]
325
326 # decrypt cookie data
327 cipher = ChaCha20_Poly1305.new(key=self.itf.cookie_key, nonce=nonce)
328 cipher.update(self.last_mac1)
329 self.last_received_cookie = cipher.decrypt_and_verify(ciphertext, tag)
330
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +0700331 def mk_handshake(self, tx_itf, is_ip6=False, public_key=None):
Neale Rannsd75a2d12020-09-10 08:49:10 +0000332 self.noise.set_as_initiator()
333 self.noise_init(public_key)
334
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200335 p = Wireguard() / WireguardInitiation()
Neale Rannsd75a2d12020-09-10 08:49:10 +0000336
337 p[Wireguard].message_type = 1
338 p[Wireguard].reserved_zero = 0
339 p[WireguardInitiation].sender_index = self.receiver_index
340
341 # some random data for the message
342 # lifted from the noise protocol's wireguard example
343 now = datetime.datetime.now()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200344 tai = struct.pack(
345 "!qi",
346 4611686018427387914 + int(now.timestamp()),
347 int(now.microsecond * 1e3),
348 )
Neale Rannsd75a2d12020-09-10 08:49:10 +0000349 b = self.noise.write_message(payload=tai)
350
351 # load noise into init message
352 p[WireguardInitiation].unencrypted_ephemeral = b[0:32]
353 p[WireguardInitiation].encrypted_static = b[32:80]
354 p[WireguardInitiation].encrypted_timestamp = b[80:108]
355
356 # generate the mac1 hash
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200357 mac_key = blake2s(b"mac1----" + self.itf.public_key_bytes()).digest()
Alexander Chernavince91af82022-07-20 12:43:42 +0000358 mac1 = blake2s(bytes(p)[0:116], digest_size=16, key=mac_key).digest()
359 p[WireguardInitiation].mac1 = mac1
360 self.last_mac1 = mac1
361
362 # generate the mac2 hash
363 if self.last_received_cookie:
364 mac2 = blake2s(
365 bytes(p)[0:132], digest_size=16, key=self.last_received_cookie
366 ).digest()
367 p[WireguardInitiation].mac2 = mac2
368 self.last_received_cookie = None
369 else:
370 p[WireguardInitiation].mac2 = bytearray(16)
Neale Rannsd75a2d12020-09-10 08:49:10 +0000371
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200372 p = self.mk_tunnel_header(tx_itf, is_ip6) / p
Neale Rannsd75a2d12020-09-10 08:49:10 +0000373
374 return p
375
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +0700376 def verify_header(self, p, is_ip6=False):
377 if is_ip6 is False:
378 self._test.assertEqual(p[IP].src, self.itf.src)
379 self._test.assertEqual(p[IP].dst, self.endpoint)
Artem Glazychevb9e391e2022-10-25 18:48:40 +0700380 self._test.assert_packet_checksums_valid(p)
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +0700381 else:
382 self._test.assertEqual(p[IPv6].src, self.itf.src)
383 self._test.assertEqual(p[IPv6].dst, self.endpoint)
Artem Glazychevb9e391e2022-10-25 18:48:40 +0700384 self._test.assert_packet_checksums_valid(p, False)
Neale Rannsd75a2d12020-09-10 08:49:10 +0000385 self._test.assertEqual(p[UDP].sport, self.itf.port)
386 self._test.assertEqual(p[UDP].dport, self.port)
Neale Rannsd75a2d12020-09-10 08:49:10 +0000387
Alexander Chernavin44ec8462022-07-20 10:48:56 +0000388 def consume_init(self, p, tx_itf, is_ip6=False, is_mac2=False):
Neale Rannsd75a2d12020-09-10 08:49:10 +0000389 self.noise.set_as_responder()
390 self.noise_init(self.itf.public_key)
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +0700391 self.verify_header(p, is_ip6)
Neale Rannsd75a2d12020-09-10 08:49:10 +0000392
393 init = Wireguard(p[Raw])
394
395 self._test.assertEqual(init[Wireguard].message_type, 1)
396 self._test.assertEqual(init[Wireguard].reserved_zero, 0)
397
398 self.sender = init[WireguardInitiation].sender_index
399
Alexander Chernavin44ec8462022-07-20 10:48:56 +0000400 # validate the mac1 hash
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200401 mac_key = blake2s(b"mac1----" + public_key_bytes(self.public_key)).digest()
402 mac1 = blake2s(bytes(init)[0:-32], digest_size=16, key=mac_key).digest()
Neale Rannsd75a2d12020-09-10 08:49:10 +0000403 self._test.assertEqual(init[WireguardInitiation].mac1, mac1)
404
Alexander Chernavin44ec8462022-07-20 10:48:56 +0000405 # validate the mac2 hash
406 if is_mac2:
407 self._test.assertNotEqual(init[WireguardInitiation].mac2, bytes([0] * 16))
408 self._test.assertNotEqual(self.last_sent_cookie, None)
409 mac2 = blake2s(
410 bytes(init)[0:-16], digest_size=16, key=self.last_sent_cookie
411 ).digest()
412 self._test.assertEqual(init[WireguardInitiation].mac2, mac2)
413 self.last_sent_cookie = None
414 else:
415 self._test.assertEqual(init[WireguardInitiation].mac2, bytes([0] * 16))
416
Neale Rannsd75a2d12020-09-10 08:49:10 +0000417 # this passes only unencrypted_ephemeral, encrypted_static,
418 # encrypted_timestamp fields of the init
419 payload = self.noise.read_message(bytes(init)[8:-32])
420
421 # build the response
422 b = self.noise.write_message()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200423 mac_key = blake2s(b"mac1----" + public_key_bytes(self.itf.public_key)).digest()
424 resp = Wireguard(message_type=2, reserved_zero=0) / WireguardResponse(
425 sender_index=self.receiver_index,
426 receiver_index=self.sender,
427 unencrypted_ephemeral=b[0:32],
428 encrypted_nothing=b[32:],
429 )
430 mac1 = blake2s(bytes(resp)[:-32], digest_size=16, key=mac_key).digest()
Neale Rannsd75a2d12020-09-10 08:49:10 +0000431 resp[WireguardResponse].mac1 = mac1
Alexander Chernavince91af82022-07-20 12:43:42 +0000432 self.last_mac1 = mac1
Neale Rannsd75a2d12020-09-10 08:49:10 +0000433
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200434 resp = self.mk_tunnel_header(tx_itf, is_ip6) / resp
Neale Rannsd75a2d12020-09-10 08:49:10 +0000435 self._test.assertTrue(self.noise.handshake_finished)
436
437 return resp
438
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +0700439 def consume_response(self, p, is_ip6=False):
440 self.verify_header(p, is_ip6)
Neale Rannsd75a2d12020-09-10 08:49:10 +0000441
442 resp = Wireguard(p[Raw])
443
444 self._test.assertEqual(resp[Wireguard].message_type, 2)
445 self._test.assertEqual(resp[Wireguard].reserved_zero, 0)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200446 self._test.assertEqual(
447 resp[WireguardResponse].receiver_index, self.receiver_index
448 )
Neale Rannsd75a2d12020-09-10 08:49:10 +0000449
450 self.sender = resp[Wireguard].sender_index
451
452 payload = self.noise.read_message(bytes(resp)[12:60])
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200453 self._test.assertEqual(payload, b"")
Neale Rannsd75a2d12020-09-10 08:49:10 +0000454 self._test.assertTrue(self.noise.handshake_finished)
455
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +0700456 def decrypt_transport(self, p, is_ip6=False):
457 self.verify_header(p, is_ip6)
Neale Rannsd75a2d12020-09-10 08:49:10 +0000458
459 p = Wireguard(p[Raw])
460 self._test.assertEqual(p[Wireguard].message_type, 4)
461 self._test.assertEqual(p[Wireguard].reserved_zero, 0)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200462 self._test.assertEqual(
463 p[WireguardTransport].receiver_index, self.receiver_index
464 )
Neale Rannsd75a2d12020-09-10 08:49:10 +0000465
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200466 d = self.noise.decrypt(p[WireguardTransport].encrypted_encapsulated_packet)
Neale Rannsd75a2d12020-09-10 08:49:10 +0000467 return d
468
469 def encrypt_transport(self, p):
470 return self.noise.encrypt(bytes(p))
471
Artem Glazychevb9e391e2022-10-25 18:48:40 +0700472 def validate_encapped(self, rxs, tx, is_tunnel_ip6=False, is_transport_ip6=False):
Artem Glazychev8eb69402020-09-14 11:36:01 +0700473 for rx in rxs:
Artem Glazychevb9e391e2022-10-25 18:48:40 +0700474 rx = self.decrypt_transport(rx, is_tunnel_ip6)
475 if is_transport_ip6 is False:
476 rx = IP(rx)
Alexander Chernavinfee98532022-08-04 08:11:57 +0000477 # check the original packet is present
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +0700478 self._test.assertEqual(rx[IP].dst, tx[IP].dst)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200479 self._test.assertEqual(rx[IP].ttl, tx[IP].ttl - 1)
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +0700480 else:
Artem Glazychevb9e391e2022-10-25 18:48:40 +0700481 rx = IPv6(rx)
Alexander Chernavinfee98532022-08-04 08:11:57 +0000482 # check the original packet is present
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +0700483 self._test.assertEqual(rx[IPv6].dst, tx[IPv6].dst)
Alexander Chernavinfee98532022-08-04 08:11:57 +0000484 self._test.assertEqual(rx[IPv6].hlim, tx[IPv6].hlim - 1)
Artem Glazychev8eb69402020-09-14 11:36:01 +0700485
Artem Glazychevdd630d12021-06-11 00:10:00 +0700486 def want_events(self):
487 self._test.vapi.want_wireguard_peer_events(
488 enable_disable=1,
489 pid=os.getpid(),
490 peer_index=self.index,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200491 sw_if_index=self.itf.sw_if_index,
492 )
Artem Glazychevdd630d12021-06-11 00:10:00 +0700493
494 def wait_event(self, expect, timeout=5):
495 rv = self._test.vapi.wait_for_event(timeout, "wireguard_peer_event")
496 self._test.assertEqual(rv.flags, expect)
497 self._test.assertEqual(rv.peer_index, self.index)
498
Artem Glazychevedca1322020-08-31 17:12:30 +0700499
Alexander Chernavin522a5b32022-09-26 15:11:27 +0000500def is_handshake_init(p):
501 wg_p = Wireguard(p[Raw])
502
503 return wg_p[Wireguard].message_type == 1
504
505
Artem Glazychevedca1322020-08-31 17:12:30 +0700506class TestWg(VppTestCase):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200507 """Wireguard Test Case"""
Artem Glazychevedca1322020-08-31 17:12:30 +0700508
509 error_str = compile(r"Error")
510
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200511 wg4_output_node_name = "/err/wg4-output-tun/"
512 wg4_input_node_name = "/err/wg4-input/"
513 wg6_output_node_name = "/err/wg6-output-tun/"
514 wg6_input_node_name = "/err/wg6-input/"
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +0700515 kp4_error = wg4_output_node_name + "Keypair error"
516 mac4_error = wg4_input_node_name + "Invalid MAC handshake"
Alexander Chernavin1477c722022-06-02 09:55:37 +0000517 peer4_in_err = wg4_input_node_name + "Peer error"
518 peer4_out_err = wg4_output_node_name + "Peer error"
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +0700519 kp6_error = wg6_output_node_name + "Keypair error"
520 mac6_error = wg6_input_node_name + "Invalid MAC handshake"
Alexander Chernavin1477c722022-06-02 09:55:37 +0000521 peer6_in_err = wg6_input_node_name + "Peer error"
522 peer6_out_err = wg6_output_node_name + "Peer error"
Alexander Chernavin44ec8462022-07-20 10:48:56 +0000523 cookie_dec4_err = wg4_input_node_name + "Failed during Cookie decryption"
524 cookie_dec6_err = wg6_input_node_name + "Failed during Cookie decryption"
Alexander Chernavina6328e52022-07-20 13:01:42 +0000525 ratelimited4_err = wg4_input_node_name + "Handshake ratelimited"
526 ratelimited6_err = wg6_input_node_name + "Handshake ratelimited"
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +0700527
Artem Glazychevedca1322020-08-31 17:12:30 +0700528 @classmethod
529 def setUpClass(cls):
530 super(TestWg, cls).setUpClass()
Dave Wallace670724c2022-09-20 21:52:18 -0400531 if (is_distro_ubuntu2204 == True or is_distro_debian11 == True) and not hasattr(
532 cls, "vpp"
533 ):
534 return
Artem Glazychevedca1322020-08-31 17:12:30 +0700535 try:
536 cls.create_pg_interfaces(range(3))
537 for i in cls.pg_interfaces:
538 i.admin_up()
539 i.config_ip4()
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +0700540 i.config_ip6()
Artem Glazychevedca1322020-08-31 17:12:30 +0700541 i.resolve_arp()
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +0700542 i.resolve_ndp()
Artem Glazychevedca1322020-08-31 17:12:30 +0700543
544 except Exception:
545 super(TestWg, cls).tearDownClass()
546 raise
547
548 @classmethod
549 def tearDownClass(cls):
550 super(TestWg, cls).tearDownClass()
551
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +0700552 def setUp(self):
553 super(VppTestCase, self).setUp()
554 self.base_kp4_err = self.statistics.get_err_counter(self.kp4_error)
555 self.base_mac4_err = self.statistics.get_err_counter(self.mac4_error)
Alexander Chernavin1477c722022-06-02 09:55:37 +0000556 self.base_peer4_in_err = self.statistics.get_err_counter(self.peer4_in_err)
557 self.base_peer4_out_err = self.statistics.get_err_counter(self.peer4_out_err)
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +0700558 self.base_kp6_err = self.statistics.get_err_counter(self.kp6_error)
559 self.base_mac6_err = self.statistics.get_err_counter(self.mac6_error)
Alexander Chernavin1477c722022-06-02 09:55:37 +0000560 self.base_peer6_in_err = self.statistics.get_err_counter(self.peer6_in_err)
561 self.base_peer6_out_err = self.statistics.get_err_counter(self.peer6_out_err)
Alexander Chernavin44ec8462022-07-20 10:48:56 +0000562 self.base_cookie_dec4_err = self.statistics.get_err_counter(
563 self.cookie_dec4_err
564 )
565 self.base_cookie_dec6_err = self.statistics.get_err_counter(
566 self.cookie_dec6_err
567 )
Alexander Chernavina6328e52022-07-20 13:01:42 +0000568 self.base_ratelimited4_err = self.statistics.get_err_counter(
569 self.ratelimited4_err
570 )
571 self.base_ratelimited6_err = self.statistics.get_err_counter(
572 self.ratelimited6_err
573 )
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +0700574
Alexander Chernavin522a5b32022-09-26 15:11:27 +0000575 def send_and_assert_no_replies_ignoring_init(
576 self, intf, pkts, remark="", timeout=None
577 ):
578 self.pg_send(intf, pkts)
579
580 def _filter_out_fn(p):
581 return is_ipv6_misc(p) or is_handshake_init(p)
582
583 try:
584 if not timeout:
585 timeout = 1
586 for i in self.pg_interfaces:
587 i.assert_nothing_captured(
588 timeout=timeout, remark=remark, filter_out_fn=_filter_out_fn
589 )
590 timeout = 0.1
591 finally:
592 pass
593
Artem Glazychevedca1322020-08-31 17:12:30 +0700594 def test_wg_interface(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200595 """Simple interface creation"""
Artem Glazychevedca1322020-08-31 17:12:30 +0700596 port = 12312
597
598 # Create interface
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200599 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
Artem Glazychevedca1322020-08-31 17:12:30 +0700600
601 self.logger.info(self.vapi.cli("sh int"))
602
603 # delete interface
604 wg0.remove_vpp_config()
605
Neale Rannsd75a2d12020-09-10 08:49:10 +0000606 def test_handshake_hash(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200607 """test hashing an init message"""
Neale Rannsd75a2d12020-09-10 08:49:10 +0000608 # a init packet generated by linux given the key below
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200609 h = (
610 "0100000098b9032b"
611 "55cc4b39e73c3d24"
612 "a2a1ab884b524a81"
613 "1808bb86640fb70d"
614 "e93154fec1879125"
615 "ab012624a27f0b75"
616 "c0a2582f438ddb5f"
617 "8e768af40b4ab444"
618 "02f9ff473e1b797e"
619 "80d39d93c5480c82"
620 "a3d4510f70396976"
621 "586fb67300a5167b"
622 "ae6ca3ff3dfd00eb"
623 "59be198810f5aa03"
624 "6abc243d2155ee4f"
625 "2336483900aef801"
626 "08752cd700000000"
627 "0000000000000000"
Neale Rannsd75a2d12020-09-10 08:49:10 +0000628 "00000000"
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200629 )
Neale Rannsd75a2d12020-09-10 08:49:10 +0000630
631 b = bytearray.fromhex(h)
632 tgt = Wireguard(b)
633
634 pubb = base64.b64decode("aRuHFTTxICIQNefp05oKWlJv3zgKxb8+WW7JJMh0jyM=")
635 pub = X25519PublicKey.from_public_bytes(pubb)
636
637 self.assertEqual(pubb, public_key_bytes(pub))
638
639 # strip the macs and build a new packet
640 init = b[0:-32]
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200641 mac_key = blake2s(b"mac1----" + public_key_bytes(pub)).digest()
642 init += blake2s(init, digest_size=16, key=mac_key).digest()
643 init += b"\x00" * 16
Neale Rannsd75a2d12020-09-10 08:49:10 +0000644
645 act = Wireguard(init)
646
647 self.assertEqual(tgt, act)
648
Alexander Chernavin44ec8462022-07-20 10:48:56 +0000649 def _test_wg_send_cookie_tmpl(self, is_resp, is_ip6):
650 port = 12323
651
652 # create wg interface
653 if is_ip6:
654 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
655 wg0.admin_up()
656 wg0.config_ip6()
657 else:
658 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
659 wg0.admin_up()
660 wg0.config_ip4()
661
662 self.pg_enable_capture(self.pg_interfaces)
663 self.pg_start()
664
665 # create a peer
666 if is_ip6:
667 peer_1 = VppWgPeer(
668 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
669 ).add_vpp_config()
670 else:
671 peer_1 = VppWgPeer(
672 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
673 ).add_vpp_config()
674 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
675
676 if is_resp:
Artem Glazychev53badfc2023-01-24 16:10:29 +0700677 # skip the first automatic handshake
678 self.pg1.get_capture(1, timeout=HANDSHAKE_JITTER)
679
Alexander Chernavin44ec8462022-07-20 10:48:56 +0000680 # prepare and send a handshake initiation
681 # expect the peer to send a handshake response
682 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
683 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
684 else:
685 # wait for the peer to send a handshake initiation
686 rxs = self.pg1.get_capture(1, timeout=2)
687
688 # prepare and send a wrong cookie reply
689 # expect no replies and the cookie error incremented
690 cookie = peer_1.mk_cookie(rxs[0], self.pg1, is_resp=is_resp, is_ip6=is_ip6)
691 cookie.nonce = b"1234567890"
692 self.send_and_assert_no_replies(self.pg1, [cookie], timeout=0.1)
693 if is_ip6:
694 self.assertEqual(
695 self.base_cookie_dec6_err + 1,
696 self.statistics.get_err_counter(self.cookie_dec6_err),
697 )
698 else:
699 self.assertEqual(
700 self.base_cookie_dec4_err + 1,
701 self.statistics.get_err_counter(self.cookie_dec4_err),
702 )
703
704 # prepare and send a correct cookie reply
705 cookie = peer_1.mk_cookie(rxs[0], self.pg1, is_resp=is_resp, is_ip6=is_ip6)
706 self.pg_send(self.pg1, [cookie])
707
708 # wait for the peer to send a handshake initiation with mac2 set
709 rxs = self.pg1.get_capture(1, timeout=6)
710
711 # verify the initiation and its mac2
712 peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6, is_mac2=True)
713
714 # remove configs
715 peer_1.remove_vpp_config()
716 wg0.remove_vpp_config()
717
718 def test_wg_send_cookie_on_init_v4(self):
719 """Send cookie on handshake initiation (v4)"""
720 self._test_wg_send_cookie_tmpl(is_resp=False, is_ip6=False)
721
722 def test_wg_send_cookie_on_init_v6(self):
723 """Send cookie on handshake initiation (v6)"""
724 self._test_wg_send_cookie_tmpl(is_resp=False, is_ip6=True)
725
726 def test_wg_send_cookie_on_resp_v4(self):
727 """Send cookie on handshake response (v4)"""
728 self._test_wg_send_cookie_tmpl(is_resp=True, is_ip6=False)
729
730 def test_wg_send_cookie_on_resp_v6(self):
731 """Send cookie on handshake response (v6)"""
732 self._test_wg_send_cookie_tmpl(is_resp=True, is_ip6=True)
733
Alexander Chernavince91af82022-07-20 12:43:42 +0000734 def _test_wg_receive_cookie_tmpl(self, is_resp, is_ip6):
735 port = 12323
736
737 # create wg interface
738 if is_ip6:
739 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
740 wg0.admin_up()
741 wg0.config_ip6()
742 else:
743 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
744 wg0.admin_up()
745 wg0.config_ip4()
746
747 self.pg_enable_capture(self.pg_interfaces)
748 self.pg_start()
749
750 # create a peer
751 if is_ip6:
752 peer_1 = VppWgPeer(
753 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
754 ).add_vpp_config()
755 else:
756 peer_1 = VppWgPeer(
757 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
758 ).add_vpp_config()
759 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
760
761 if is_resp:
762 # wait for the peer to send a handshake initiation
763 rxs = self.pg1.get_capture(1, timeout=2)
764 # prepare and send a bunch of handshake responses
765 # expect to switch to under load state
766 resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6)
767 txs = [resp] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
768 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
769 # reset noise to be able to turn into initiator later
770 peer_1.noise_reset()
771 else:
Artem Glazychev53badfc2023-01-24 16:10:29 +0700772 # skip the first automatic handshake
773 self.pg1.get_capture(1, timeout=HANDSHAKE_JITTER)
774
Alexander Chernavince91af82022-07-20 12:43:42 +0000775 # prepare and send a bunch of handshake initiations
776 # expect to switch to under load state
777 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
778 txs = [init] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
779 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
780
781 # expect the peer to send a cookie reply
782 peer_1.consume_cookie(rxs[-1], is_ip6=is_ip6)
783
784 # prepare and send a handshake initiation with wrong mac2
785 # expect a cookie reply
786 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
787 init.mac2 = b"1234567890"
788 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
789 peer_1.consume_cookie(rxs[0], is_ip6=is_ip6)
790
791 # prepare and send a handshake initiation with correct mac2
792 # expect a handshake response
793 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
794 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
795
796 # verify the response
797 peer_1.consume_response(rxs[0], is_ip6=is_ip6)
798
799 # clear up under load state
800 self.sleep(UNDER_LOAD_INTERVAL)
801
802 # remove configs
803 peer_1.remove_vpp_config()
804 wg0.remove_vpp_config()
805
806 def test_wg_receive_cookie_on_init_v4(self):
807 """Receive cookie on handshake initiation (v4)"""
808 self._test_wg_receive_cookie_tmpl(is_resp=False, is_ip6=False)
809
810 def test_wg_receive_cookie_on_init_v6(self):
811 """Receive cookie on handshake initiation (v6)"""
812 self._test_wg_receive_cookie_tmpl(is_resp=False, is_ip6=True)
813
814 def test_wg_receive_cookie_on_resp_v4(self):
815 """Receive cookie on handshake response (v4)"""
816 self._test_wg_receive_cookie_tmpl(is_resp=True, is_ip6=False)
817
818 def test_wg_receive_cookie_on_resp_v6(self):
819 """Receive cookie on handshake response (v6)"""
820 self._test_wg_receive_cookie_tmpl(is_resp=True, is_ip6=True)
821
822 def test_wg_under_load_interval(self):
823 """Under load interval"""
824 port = 12323
825
826 # create wg interface
827 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
828 wg0.admin_up()
829 wg0.config_ip4()
830
831 self.pg_enable_capture(self.pg_interfaces)
832 self.pg_start()
833
834 # create a peer
835 peer_1 = VppWgPeer(
836 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
837 ).add_vpp_config()
838 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
839
Artem Glazychev53badfc2023-01-24 16:10:29 +0700840 # skip the first automatic handshake
841 self.pg1.get_capture(1, timeout=HANDSHAKE_JITTER)
842
Alexander Chernavince91af82022-07-20 12:43:42 +0000843 # prepare and send a bunch of handshake initiations
844 # expect to switch to under load state
845 init = peer_1.mk_handshake(self.pg1)
846 txs = [init] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
847 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
848
849 # expect the peer to send a cookie reply
850 peer_1.consume_cookie(rxs[-1])
851
852 # sleep till the next counting interval
853 # expect under load state is still active
854 self.sleep(HANDSHAKE_COUNTING_INTERVAL)
855
856 # prepare and send a handshake initiation with wrong mac2
857 # expect a cookie reply
858 init = peer_1.mk_handshake(self.pg1)
859 init.mac2 = b"1234567890"
860 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
861 peer_1.consume_cookie(rxs[0])
862
863 # sleep till the end of being under load
864 # expect under load state is over
865 self.sleep(UNDER_LOAD_INTERVAL - HANDSHAKE_COUNTING_INTERVAL)
866
867 # prepare and send a handshake initiation with wrong mac2
868 # expect a handshake response
869 init = peer_1.mk_handshake(self.pg1)
870 init.mac2 = b"1234567890"
871 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
872
873 # verify the response
874 peer_1.consume_response(rxs[0])
875
876 # remove configs
877 peer_1.remove_vpp_config()
878 wg0.remove_vpp_config()
879
Alexander Chernavina6328e52022-07-20 13:01:42 +0000880 def _test_wg_handshake_ratelimiting_tmpl(self, is_ip6):
881 port = 12323
882
883 # create wg interface
884 if is_ip6:
885 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
886 wg0.admin_up()
887 wg0.config_ip6()
888 else:
889 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
890 wg0.admin_up()
891 wg0.config_ip4()
892
893 self.pg_enable_capture(self.pg_interfaces)
894 self.pg_start()
895
896 # create a peer
897 if is_ip6:
898 peer_1 = VppWgPeer(
899 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
900 ).add_vpp_config()
901 else:
902 peer_1 = VppWgPeer(
903 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
904 ).add_vpp_config()
905 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
906
Artem Glazychev53badfc2023-01-24 16:10:29 +0700907 # skip the first automatic handshake
908 self.pg1.get_capture(1, timeout=HANDSHAKE_JITTER)
909
Alexander Chernavina6328e52022-07-20 13:01:42 +0000910 # prepare and send a bunch of handshake initiations
911 # expect to switch to under load state
912 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
913 txs = [init] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
914 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
915
916 # expect the peer to send a cookie reply
917 peer_1.consume_cookie(rxs[-1], is_ip6=is_ip6)
918
919 # prepare and send a bunch of handshake initiations with correct mac2
920 # expect a handshake response and then ratelimiting
921 NUM_TO_REJECT = 10
922 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
923 txs = [init] * (HANDSHAKE_NUM_BEFORE_RATELIMITING + NUM_TO_REJECT)
924 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
925
926 if is_ip6:
927 self.assertEqual(
928 self.base_ratelimited6_err + NUM_TO_REJECT,
929 self.statistics.get_err_counter(self.ratelimited6_err),
930 )
931 else:
932 self.assertEqual(
933 self.base_ratelimited4_err + NUM_TO_REJECT,
934 self.statistics.get_err_counter(self.ratelimited4_err),
935 )
936
937 # verify the response
938 peer_1.consume_response(rxs[0], is_ip6=is_ip6)
939
940 # clear up under load state
941 self.sleep(UNDER_LOAD_INTERVAL)
942
943 # remove configs
944 peer_1.remove_vpp_config()
945 wg0.remove_vpp_config()
946
947 def test_wg_handshake_ratelimiting_v4(self):
948 """Handshake ratelimiting (v4)"""
949 self._test_wg_handshake_ratelimiting_tmpl(is_ip6=False)
950
951 def test_wg_handshake_ratelimiting_v6(self):
952 """Handshake ratelimiting (v6)"""
953 self._test_wg_handshake_ratelimiting_tmpl(is_ip6=True)
954
955 def test_wg_handshake_ratelimiting_multi_peer(self):
956 """Handshake ratelimiting (multiple peer)"""
957 port = 12323
958
959 # create wg interface
960 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
961 wg0.admin_up()
962 wg0.config_ip4()
963
964 self.pg_enable_capture(self.pg_interfaces)
965 self.pg_start()
966
967 # create two peers
968 NUM_PEERS = 2
969 self.pg1.generate_remote_hosts(NUM_PEERS)
970 self.pg1.configure_ipv4_neighbors()
971
972 peer_1 = VppWgPeer(
973 self, wg0, self.pg1.remote_hosts[0].ip4, port + 1, ["10.11.3.0/24"]
974 ).add_vpp_config()
975 peer_2 = VppWgPeer(
976 self, wg0, self.pg1.remote_hosts[1].ip4, port + 1, ["10.11.4.0/24"]
977 ).add_vpp_config()
978 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 2)
979
Artem Glazychev53badfc2023-01-24 16:10:29 +0700980 # skip the first automatic handshake
981 self.pg1.get_capture(NUM_PEERS, timeout=HANDSHAKE_JITTER)
982
Alexander Chernavina6328e52022-07-20 13:01:42 +0000983 # (peer_1) prepare and send a bunch of handshake initiations
984 # expect not to switch to under load state
985 init_1 = peer_1.mk_handshake(self.pg1)
986 txs = [init_1] * HANDSHAKE_NUM_PER_PEER_UNTIL_UNDER_LOAD
987 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
988
989 # (peer_1) expect the peer to send a handshake response
990 peer_1.consume_response(rxs[0])
991 peer_1.noise_reset()
992
993 # (peer_1) send another bunch of handshake initiations
994 # expect to switch to under load state
995 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
996
997 # (peer_1) expect the peer to send a cookie reply
998 peer_1.consume_cookie(rxs[-1])
999
1000 # (peer_2) prepare and send a handshake initiation
1001 # expect a cookie reply
1002 init_2 = peer_2.mk_handshake(self.pg1)
1003 rxs = self.send_and_expect(self.pg1, [init_2], self.pg1)
1004 peer_2.consume_cookie(rxs[0])
1005
Alexander Chernavincf9144e2022-09-23 12:41:31 +00001006 # (peer_1) (peer_2) prepare and send a bunch of handshake initiations with correct mac2
1007 # expect a handshake response and then ratelimiting
1008 PEER_1_NUM_TO_REJECT = 2
1009 PEER_2_NUM_TO_REJECT = 5
Alexander Chernavina6328e52022-07-20 13:01:42 +00001010 init_1 = peer_1.mk_handshake(self.pg1)
Alexander Chernavincf9144e2022-09-23 12:41:31 +00001011 txs = [init_1] * (HANDSHAKE_NUM_BEFORE_RATELIMITING + PEER_1_NUM_TO_REJECT)
Alexander Chernavina6328e52022-07-20 13:01:42 +00001012 init_2 = peer_2.mk_handshake(self.pg1)
Alexander Chernavincf9144e2022-09-23 12:41:31 +00001013 txs += [init_2] * (HANDSHAKE_NUM_BEFORE_RATELIMITING + PEER_2_NUM_TO_REJECT)
Alexander Chernavina6328e52022-07-20 13:01:42 +00001014 rxs = self.send_and_expect_some(self.pg1, txs, self.pg1)
1015
Alexander Chernavincf9144e2022-09-23 12:41:31 +00001016 self.assertTrue(
1017 self.base_ratelimited4_err + PEER_1_NUM_TO_REJECT
1018 < self.statistics.get_err_counter(self.ratelimited4_err)
1019 <= self.base_ratelimited4_err + PEER_1_NUM_TO_REJECT + PEER_2_NUM_TO_REJECT
Alexander Chernavina6328e52022-07-20 13:01:42 +00001020 )
1021
Alexander Chernavincf9144e2022-09-23 12:41:31 +00001022 # (peer_1) (peer_2) verify the response
1023 peer_1.consume_response(rxs[0])
1024 peer_2.consume_response(rxs[1])
Alexander Chernavina6328e52022-07-20 13:01:42 +00001025
1026 # clear up under load state
1027 self.sleep(UNDER_LOAD_INTERVAL)
1028
1029 # remove configs
1030 peer_1.remove_vpp_config()
1031 peer_2.remove_vpp_config()
1032 wg0.remove_vpp_config()
1033
Alexander Chernavinfee98532022-08-04 08:11:57 +00001034 def _test_wg_peer_roaming_on_handshake_tmpl(self, is_endpoint_set, is_resp, is_ip6):
1035 port = 12323
1036
1037 # create wg interface
1038 if is_ip6:
1039 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1040 wg0.admin_up()
1041 wg0.config_ip6()
1042 else:
1043 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1044 wg0.admin_up()
1045 wg0.config_ip4()
1046
1047 self.pg_enable_capture(self.pg_interfaces)
1048 self.pg_start()
1049
1050 # create more remote hosts
1051 NUM_REMOTE_HOSTS = 2
1052 self.pg1.generate_remote_hosts(NUM_REMOTE_HOSTS)
1053 if is_ip6:
1054 self.pg1.configure_ipv6_neighbors()
1055 else:
1056 self.pg1.configure_ipv4_neighbors()
1057
1058 # create a peer
1059 if is_ip6:
1060 peer_1 = VppWgPeer(
1061 test=self,
1062 itf=wg0,
1063 endpoint=self.pg1.remote_hosts[0].ip6 if is_endpoint_set else "::",
1064 port=port + 1 if is_endpoint_set else 0,
1065 allowed_ips=["1::3:0/112"],
1066 ).add_vpp_config()
1067 else:
1068 peer_1 = VppWgPeer(
1069 test=self,
1070 itf=wg0,
1071 endpoint=self.pg1.remote_hosts[0].ip4 if is_endpoint_set else "0.0.0.0",
1072 port=port + 1 if is_endpoint_set else 0,
1073 allowed_ips=["10.11.3.0/24"],
1074 ).add_vpp_config()
1075 self.assertTrue(peer_1.query_vpp_config())
1076
1077 if is_resp:
1078 # wait for the peer to send a handshake initiation
1079 rxs = self.pg1.get_capture(1, timeout=2)
1080 # prepare a handshake response
1081 resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6)
1082 # change endpoint
1083 if is_ip6:
1084 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip6, port + 100)
1085 resp[IPv6].src, resp[UDP].sport = peer_1.endpoint, peer_1.port
1086 else:
1087 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip4, port + 100)
1088 resp[IP].src, resp[UDP].sport = peer_1.endpoint, peer_1.port
1089 # send the handshake response
1090 # expect a keepalive message sent to the new endpoint
1091 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1092 # verify the keepalive message
1093 b = peer_1.decrypt_transport(rxs[0], is_ip6=is_ip6)
1094 self.assertEqual(0, len(b))
1095 else:
1096 # change endpoint
1097 if is_ip6:
1098 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip6, port + 100)
1099 else:
1100 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip4, port + 100)
1101 # prepare and send a handshake initiation
1102 # expect a handshake response sent to the new endpoint
1103 init = peer_1.mk_handshake(self.pg1, is_ip6=is_ip6)
1104 rxs = self.send_and_expect(self.pg1, [init], self.pg1)
1105 # verify the response
1106 peer_1.consume_response(rxs[0], is_ip6=is_ip6)
1107 self.assertTrue(peer_1.query_vpp_config())
1108
1109 # remove configs
1110 peer_1.remove_vpp_config()
1111 wg0.remove_vpp_config()
1112
1113 def test_wg_peer_roaming_on_init_v4(self):
1114 """Peer roaming on handshake initiation (v4)"""
1115 self._test_wg_peer_roaming_on_handshake_tmpl(
1116 is_endpoint_set=False, is_resp=False, is_ip6=False
1117 )
1118
1119 def test_wg_peer_roaming_on_init_v6(self):
1120 """Peer roaming on handshake initiation (v6)"""
1121 self._test_wg_peer_roaming_on_handshake_tmpl(
1122 is_endpoint_set=False, is_resp=False, is_ip6=True
1123 )
1124
1125 def test_wg_peer_roaming_on_resp_v4(self):
1126 """Peer roaming on handshake response (v4)"""
1127 self._test_wg_peer_roaming_on_handshake_tmpl(
1128 is_endpoint_set=True, is_resp=True, is_ip6=False
1129 )
1130
1131 def test_wg_peer_roaming_on_resp_v6(self):
1132 """Peer roaming on handshake response (v6)"""
1133 self._test_wg_peer_roaming_on_handshake_tmpl(
1134 is_endpoint_set=True, is_resp=True, is_ip6=True
1135 )
1136
1137 def _test_wg_peer_roaming_on_data_tmpl(self, is_async, is_ip6):
1138 self.vapi.wg_set_async_mode(is_async)
1139 port = 12323
1140
1141 # create wg interface
1142 if is_ip6:
1143 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1144 wg0.admin_up()
1145 wg0.config_ip6()
1146 else:
1147 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
1148 wg0.admin_up()
1149 wg0.config_ip4()
1150
1151 self.pg_enable_capture(self.pg_interfaces)
1152 self.pg_start()
1153
1154 # create more remote hosts
1155 NUM_REMOTE_HOSTS = 2
1156 self.pg1.generate_remote_hosts(NUM_REMOTE_HOSTS)
1157 if is_ip6:
1158 self.pg1.configure_ipv6_neighbors()
1159 else:
1160 self.pg1.configure_ipv4_neighbors()
1161
1162 # create a peer
1163 if is_ip6:
1164 peer_1 = VppWgPeer(
1165 self, wg0, self.pg1.remote_hosts[0].ip6, port + 1, ["1::3:0/112"]
1166 ).add_vpp_config()
1167 else:
1168 peer_1 = VppWgPeer(
1169 self, wg0, self.pg1.remote_hosts[0].ip4, port + 1, ["10.11.3.0/24"]
1170 ).add_vpp_config()
1171 self.assertTrue(peer_1.query_vpp_config())
1172
1173 # create a route to rewrite traffic into the wg interface
1174 if is_ip6:
1175 r1 = VppIpRoute(
1176 self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
1177 ).add_vpp_config()
1178 else:
1179 r1 = VppIpRoute(
1180 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1181 ).add_vpp_config()
1182
1183 # wait for the peer to send a handshake initiation
1184 rxs = self.pg1.get_capture(1, timeout=2)
1185
1186 # prepare and send a handshake response
1187 # expect a keepalive message
1188 resp = peer_1.consume_init(rxs[0], self.pg1, is_ip6=is_ip6)
1189 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1190
1191 # verify the keepalive message
1192 b = peer_1.decrypt_transport(rxs[0], is_ip6=is_ip6)
1193 self.assertEqual(0, len(b))
1194
1195 # change endpoint
1196 if is_ip6:
1197 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip6, port + 100)
1198 else:
1199 peer_1.change_endpoint(self.pg1.remote_hosts[1].ip4, port + 100)
1200
1201 # prepare and send a data packet
1202 # expect endpoint change
1203 if is_ip6:
1204 ip_header = IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1205 else:
1206 ip_header = IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1207 data = (
1208 peer_1.mk_tunnel_header(self.pg1, is_ip6=is_ip6)
1209 / Wireguard(message_type=4, reserved_zero=0)
1210 / WireguardTransport(
1211 receiver_index=peer_1.sender,
1212 counter=0,
1213 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1214 ip_header / UDP(sport=222, dport=223) / Raw()
1215 ),
1216 )
1217 )
1218 rxs = self.send_and_expect(self.pg1, [data], self.pg0)
1219 if is_ip6:
1220 self.assertEqual(rxs[0][IPv6].dst, self.pg0.remote_ip6)
1221 self.assertEqual(rxs[0][IPv6].hlim, 19)
1222 else:
1223 self.assertEqual(rxs[0][IP].dst, self.pg0.remote_ip4)
1224 self.assertEqual(rxs[0][IP].ttl, 19)
1225 self.assertTrue(peer_1.query_vpp_config())
1226
1227 # prepare and send a packet that will be rewritten into the wg interface
1228 # expect a data packet sent to the new endpoint
1229 if is_ip6:
1230 ip_header = IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1231 else:
1232 ip_header = IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1233 p = (
1234 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1235 / ip_header
1236 / UDP(sport=555, dport=556)
1237 / Raw()
1238 )
1239 rxs = self.send_and_expect(self.pg0, [p], self.pg1)
1240
1241 # verify the data packet
Artem Glazychevb9e391e2022-10-25 18:48:40 +07001242 peer_1.validate_encapped(rxs, p, is_tunnel_ip6=is_ip6, is_transport_ip6=is_ip6)
Alexander Chernavinfee98532022-08-04 08:11:57 +00001243
1244 # remove configs
1245 r1.remove_vpp_config()
1246 peer_1.remove_vpp_config()
1247 wg0.remove_vpp_config()
1248
1249 def test_wg_peer_roaming_on_data_v4_sync(self):
1250 """Peer roaming on data packet (v4, sync)"""
1251 self._test_wg_peer_roaming_on_data_tmpl(is_async=False, is_ip6=False)
1252
1253 def test_wg_peer_roaming_on_data_v6_sync(self):
1254 """Peer roaming on data packet (v6, sync)"""
1255 self._test_wg_peer_roaming_on_data_tmpl(is_async=False, is_ip6=True)
1256
1257 def test_wg_peer_roaming_on_data_v4_async(self):
1258 """Peer roaming on data packet (v4, async)"""
1259 self._test_wg_peer_roaming_on_data_tmpl(is_async=True, is_ip6=False)
1260
1261 def test_wg_peer_roaming_on_data_v6_async(self):
1262 """Peer roaming on data packet (v6, async)"""
1263 self._test_wg_peer_roaming_on_data_tmpl(is_async=True, is_ip6=True)
1264
Neale Rannsd75a2d12020-09-10 08:49:10 +00001265 def test_wg_peer_resp(self):
Artem Glazychevb9e391e2022-10-25 18:48:40 +07001266 """Send handshake response IPv4 tunnel"""
Artem Glazychevedca1322020-08-31 17:12:30 +07001267 port = 12323
1268
1269 # Create interfaces
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001270 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
Artem Glazychevedca1322020-08-31 17:12:30 +07001271 wg0.admin_up()
Neale Rannsd75a2d12020-09-10 08:49:10 +00001272 wg0.config_ip4()
Artem Glazychevedca1322020-08-31 17:12:30 +07001273
1274 self.pg_enable_capture(self.pg_interfaces)
1275 self.pg_start()
1276
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001277 peer_1 = VppWgPeer(
1278 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
1279 ).add_vpp_config()
Artem Glazychevedca1322020-08-31 17:12:30 +07001280 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1281
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001282 r1 = VppIpRoute(
1283 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1284 ).add_vpp_config()
Artem Glazychevde3caf32021-05-20 12:33:52 +07001285
Artem Glazychevedca1322020-08-31 17:12:30 +07001286 # wait for the peer to send a handshake
Neale Rannsd75a2d12020-09-10 08:49:10 +00001287 rx = self.pg1.get_capture(1, timeout=2)
Artem Glazychevedca1322020-08-31 17:12:30 +07001288
Neale Rannsd75a2d12020-09-10 08:49:10 +00001289 # consume the handshake in the noise protocol and
1290 # generate the response
1291 resp = peer_1.consume_init(rx[0], self.pg1)
1292
1293 # send the response, get keepalive
1294 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1295
1296 for rx in rxs:
1297 b = peer_1.decrypt_transport(rx)
1298 self.assertEqual(0, len(b))
1299
1300 # send a packets that are routed into the tunnel
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001301 p = (
1302 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1303 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1304 / UDP(sport=555, dport=556)
1305 / Raw(b"\x00" * 80)
1306 )
Neale Rannsd75a2d12020-09-10 08:49:10 +00001307
1308 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1309
Artem Glazychev8eb69402020-09-14 11:36:01 +07001310 peer_1.validate_encapped(rxs, p)
Neale Rannsd75a2d12020-09-10 08:49:10 +00001311
1312 # send packets into the tunnel, expect to receive them on
1313 # the other side
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001314 p = [
1315 (
1316 peer_1.mk_tunnel_header(self.pg1)
1317 / Wireguard(message_type=4, reserved_zero=0)
1318 / WireguardTransport(
1319 receiver_index=peer_1.sender,
1320 counter=ii,
1321 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1322 (
1323 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1324 / UDP(sport=222, dport=223)
1325 / Raw()
1326 )
1327 ),
1328 )
1329 )
1330 for ii in range(255)
1331 ]
Neale Rannsd75a2d12020-09-10 08:49:10 +00001332
1333 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1334
1335 for rx in rxs:
1336 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1337 self.assertEqual(rx[IP].ttl, 19)
1338
Artem Glazychevde3caf32021-05-20 12:33:52 +07001339 r1.remove_vpp_config()
1340 peer_1.remove_vpp_config()
1341 wg0.remove_vpp_config()
1342
Artem Glazychevb9e391e2022-10-25 18:48:40 +07001343 def test_wg_peer_resp_ipv6(self):
1344 """Send handshake response IPv6 tunnel"""
1345 port = 12323
1346
1347 # Create interfaces
1348 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
1349 wg0.admin_up()
1350 wg0.config_ip4()
1351
1352 self.pg_enable_capture(self.pg_interfaces)
1353 self.pg_start()
1354
1355 peer_1 = VppWgPeer(
1356 self, wg0, self.pg1.remote_ip6, port + 1, ["10.11.3.0/24"]
1357 ).add_vpp_config()
1358 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1359
1360 r1 = VppIpRoute(
1361 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1362 ).add_vpp_config()
1363
1364 # wait for the peer to send a handshake
1365 rx = self.pg1.get_capture(1, timeout=2)
1366
1367 # consume the handshake in the noise protocol and
1368 # generate the response
1369 resp = peer_1.consume_init(rx[0], self.pg1, is_ip6=True)
1370
1371 # send the response, get keepalive
1372 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
1373
1374 for rx in rxs:
1375 b = peer_1.decrypt_transport(rx, True)
1376 self.assertEqual(0, len(b))
1377
1378 # send a packets that are routed into the tunnel
1379 p = (
1380 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1381 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1382 / UDP(sport=555, dport=556)
1383 / Raw(b"\x00" * 80)
1384 )
1385
1386 rxs = self.send_and_expect(self.pg0, p * 2, self.pg1)
1387 peer_1.validate_encapped(rxs, p, True)
1388
1389 # send packets into the tunnel, expect to receive them on
1390 # the other side
1391 p = [
1392 (
1393 peer_1.mk_tunnel_header(self.pg1, True)
1394 / Wireguard(message_type=4, reserved_zero=0)
1395 / WireguardTransport(
1396 receiver_index=peer_1.sender,
1397 counter=ii,
1398 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1399 (
1400 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1401 / UDP(sport=222, dport=223)
1402 / Raw()
1403 )
1404 ),
1405 )
1406 )
1407 for ii in range(255)
1408 ]
1409
1410 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1411
1412 for rx in rxs:
1413 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1414 self.assertEqual(rx[IP].ttl, 19)
1415
1416 r1.remove_vpp_config()
1417 peer_1.remove_vpp_config()
1418 wg0.remove_vpp_config()
1419
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001420 def test_wg_peer_v4o4(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001421 """Test v4o4"""
Neale Rannsd75a2d12020-09-10 08:49:10 +00001422
Artem Glazychev124d5e02020-09-30 01:07:46 +07001423 port = 12333
Neale Rannsd75a2d12020-09-10 08:49:10 +00001424
1425 # Create interfaces
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001426 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
Neale Rannsd75a2d12020-09-10 08:49:10 +00001427 wg0.admin_up()
1428 wg0.config_ip4()
1429
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001430 peer_1 = VppWgPeer(
1431 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
1432 ).add_vpp_config()
Neale Rannsd75a2d12020-09-10 08:49:10 +00001433 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
Artem Glazychevedca1322020-08-31 17:12:30 +07001434
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001435 r1 = VppIpRoute(
1436 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1437 ).add_vpp_config()
Alexander Chernavin1477c722022-06-02 09:55:37 +00001438 r2 = VppIpRoute(
1439 self, "20.22.3.0", 24, [VppRoutePath("20.22.3.1", wg0.sw_if_index)]
1440 ).add_vpp_config()
Artem Glazychevde3caf32021-05-20 12:33:52 +07001441
Artem Glazychevedca1322020-08-31 17:12:30 +07001442 # route a packet into the wg interface
1443 # use the allowed-ip prefix
Neale Rannsd75a2d12020-09-10 08:49:10 +00001444 # this is dropped because the peer is not initiated
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001445 p = (
1446 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1447 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1448 / UDP(sport=555, dport=556)
1449 / Raw()
1450 )
Alexander Chernavin522a5b32022-09-26 15:11:27 +00001451 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001452 self.assertEqual(
1453 self.base_kp4_err + 1, self.statistics.get_err_counter(self.kp4_error)
1454 )
Neale Rannsd75a2d12020-09-10 08:49:10 +00001455
Alexander Chernavin1477c722022-06-02 09:55:37 +00001456 # route a packet into the wg interface
1457 # use a not allowed-ip prefix
1458 # this is dropped because there is no matching peer
1459 p = (
1460 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1461 / IP(src=self.pg0.remote_ip4, dst="20.22.3.2")
1462 / UDP(sport=555, dport=556)
1463 / Raw()
1464 )
Alexander Chernavin522a5b32022-09-26 15:11:27 +00001465 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
Alexander Chernavin1477c722022-06-02 09:55:37 +00001466 self.assertEqual(
1467 self.base_peer4_out_err + 1,
1468 self.statistics.get_err_counter(self.peer4_out_err),
1469 )
1470
Neale Rannsd75a2d12020-09-10 08:49:10 +00001471 # send a handsake from the peer with an invalid MAC
1472 p = peer_1.mk_handshake(self.pg1)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001473 p[WireguardInitiation].mac1 = b"foobar"
Alexander Chernavin522a5b32022-09-26 15:11:27 +00001474 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001475 self.assertEqual(
1476 self.base_mac4_err + 1, self.statistics.get_err_counter(self.mac4_error)
1477 )
Neale Rannsd75a2d12020-09-10 08:49:10 +00001478
1479 # send a handsake from the peer but signed by the wrong key.
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001480 p = peer_1.mk_handshake(
1481 self.pg1, False, X25519PrivateKey.generate().public_key()
1482 )
Alexander Chernavin522a5b32022-09-26 15:11:27 +00001483 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001484 self.assertEqual(
Alexander Chernavin1477c722022-06-02 09:55:37 +00001485 self.base_peer4_in_err + 1,
1486 self.statistics.get_err_counter(self.peer4_in_err),
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001487 )
Neale Rannsd75a2d12020-09-10 08:49:10 +00001488
1489 # send a valid handsake init for which we expect a response
1490 p = peer_1.mk_handshake(self.pg1)
1491
1492 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1493
1494 peer_1.consume_response(rx[0])
1495
1496 # route a packet into the wg interface
1497 # this is dropped because the peer is still not initiated
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001498 p = (
1499 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1500 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1501 / UDP(sport=555, dport=556)
1502 / Raw()
1503 )
Alexander Chernavin522a5b32022-09-26 15:11:27 +00001504 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001505 self.assertEqual(
1506 self.base_kp4_err + 2, self.statistics.get_err_counter(self.kp4_error)
1507 )
Neale Rannsd75a2d12020-09-10 08:49:10 +00001508
1509 # send a data packet from the peer through the tunnel
1510 # this completes the handshake
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001511 p = (
1512 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1513 / UDP(sport=222, dport=223)
1514 / Raw()
1515 )
Neale Rannsd75a2d12020-09-10 08:49:10 +00001516 d = peer_1.encrypt_transport(p)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001517 p = peer_1.mk_tunnel_header(self.pg1) / (
1518 Wireguard(message_type=4, reserved_zero=0)
1519 / WireguardTransport(
1520 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1521 )
1522 )
Neale Rannsd75a2d12020-09-10 08:49:10 +00001523 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1524
1525 for rx in rxs:
1526 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1527 self.assertEqual(rx[IP].ttl, 19)
1528
1529 # send a packets that are routed into the tunnel
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001530 p = (
1531 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1532 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1533 / UDP(sport=555, dport=556)
1534 / Raw(b"\x00" * 80)
1535 )
Neale Rannsd75a2d12020-09-10 08:49:10 +00001536
1537 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1538
1539 for rx in rxs:
1540 rx = IP(peer_1.decrypt_transport(rx))
1541
Alexander Chernavinfee98532022-08-04 08:11:57 +00001542 # check the original packet is present
Neale Rannsd75a2d12020-09-10 08:49:10 +00001543 self.assertEqual(rx[IP].dst, p[IP].dst)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001544 self.assertEqual(rx[IP].ttl, p[IP].ttl - 1)
Neale Rannsd75a2d12020-09-10 08:49:10 +00001545
1546 # send packets into the tunnel, expect to receive them on
1547 # the other side
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001548 p = [
1549 (
1550 peer_1.mk_tunnel_header(self.pg1)
1551 / Wireguard(message_type=4, reserved_zero=0)
1552 / WireguardTransport(
1553 receiver_index=peer_1.sender,
1554 counter=ii + 1,
1555 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1556 (
1557 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1558 / UDP(sport=222, dport=223)
1559 / Raw()
1560 )
1561 ),
1562 )
1563 )
1564 for ii in range(255)
1565 ]
Neale Rannsd75a2d12020-09-10 08:49:10 +00001566
1567 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1568
1569 for rx in rxs:
1570 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1571 self.assertEqual(rx[IP].ttl, 19)
1572
Artem Glazychevde3caf32021-05-20 12:33:52 +07001573 r1.remove_vpp_config()
Alexander Chernavin1477c722022-06-02 09:55:37 +00001574 r2.remove_vpp_config()
Neale Rannsd75a2d12020-09-10 08:49:10 +00001575 peer_1.remove_vpp_config()
1576 wg0.remove_vpp_config()
1577
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001578 def test_wg_peer_v6o6(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001579 """Test v6o6"""
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001580
1581 port = 12343
1582
1583 # Create interfaces
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001584 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001585 wg0.admin_up()
1586 wg0.config_ip6()
1587
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001588 peer_1 = VppWgPeer(
1589 self, wg0, self.pg1.remote_ip6, port + 1, ["1::3:0/112"]
Alexander Chernavin522a5b32022-09-26 15:11:27 +00001590 ).add_vpp_config()
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001591 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1592
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001593 r1 = VppIpRoute(
1594 self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
1595 ).add_vpp_config()
Alexander Chernavin1477c722022-06-02 09:55:37 +00001596 r2 = VppIpRoute(
1597 self, "22::3:0", 112, [VppRoutePath("22::3:1", wg0.sw_if_index)]
1598 ).add_vpp_config()
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001599
1600 # route a packet into the wg interface
1601 # use the allowed-ip prefix
1602 # this is dropped because the peer is not initiated
1603
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001604 p = (
1605 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1606 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1607 / UDP(sport=555, dport=556)
1608 / Raw()
1609 )
Alexander Chernavin522a5b32022-09-26 15:11:27 +00001610 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001611
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001612 self.assertEqual(
1613 self.base_kp6_err + 1, self.statistics.get_err_counter(self.kp6_error)
1614 )
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001615
Alexander Chernavin1477c722022-06-02 09:55:37 +00001616 # route a packet into the wg interface
1617 # use a not allowed-ip prefix
1618 # this is dropped because there is no matching peer
1619 p = (
1620 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1621 / IPv6(src=self.pg0.remote_ip6, dst="22::3:2")
1622 / UDP(sport=555, dport=556)
1623 / Raw()
1624 )
Alexander Chernavin522a5b32022-09-26 15:11:27 +00001625 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
Alexander Chernavin1477c722022-06-02 09:55:37 +00001626 self.assertEqual(
1627 self.base_peer6_out_err + 1,
1628 self.statistics.get_err_counter(self.peer6_out_err),
1629 )
1630
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001631 # send a handsake from the peer with an invalid MAC
1632 p = peer_1.mk_handshake(self.pg1, True)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001633 p[WireguardInitiation].mac1 = b"foobar"
Alexander Chernavin522a5b32022-09-26 15:11:27 +00001634 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001635
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001636 self.assertEqual(
1637 self.base_mac6_err + 1, self.statistics.get_err_counter(self.mac6_error)
1638 )
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001639
1640 # send a handsake from the peer but signed by the wrong key.
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001641 p = peer_1.mk_handshake(
1642 self.pg1, True, X25519PrivateKey.generate().public_key()
1643 )
Alexander Chernavin522a5b32022-09-26 15:11:27 +00001644 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001645 self.assertEqual(
Alexander Chernavin1477c722022-06-02 09:55:37 +00001646 self.base_peer6_in_err + 1,
1647 self.statistics.get_err_counter(self.peer6_in_err),
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001648 )
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001649
1650 # send a valid handsake init for which we expect a response
1651 p = peer_1.mk_handshake(self.pg1, True)
1652
1653 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1654
1655 peer_1.consume_response(rx[0], True)
1656
1657 # route a packet into the wg interface
1658 # this is dropped because the peer is still not initiated
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001659 p = (
1660 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1661 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1662 / UDP(sport=555, dport=556)
1663 / Raw()
1664 )
Alexander Chernavin522a5b32022-09-26 15:11:27 +00001665 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001666 self.assertEqual(
1667 self.base_kp6_err + 2, self.statistics.get_err_counter(self.kp6_error)
1668 )
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001669
1670 # send a data packet from the peer through the tunnel
1671 # this completes the handshake
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001672 p = (
1673 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1674 / UDP(sport=222, dport=223)
1675 / Raw()
1676 )
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001677 d = peer_1.encrypt_transport(p)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001678 p = peer_1.mk_tunnel_header(self.pg1, True) / (
1679 Wireguard(message_type=4, reserved_zero=0)
1680 / WireguardTransport(
1681 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1682 )
1683 )
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001684 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1685
1686 for rx in rxs:
1687 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1688 self.assertEqual(rx[IPv6].hlim, 19)
1689
1690 # send a packets that are routed into the tunnel
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001691 p = (
1692 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1693 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1694 / UDP(sport=555, dport=556)
1695 / Raw(b"\x00" * 80)
1696 )
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001697
1698 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1699
1700 for rx in rxs:
1701 rx = IPv6(peer_1.decrypt_transport(rx, True))
1702
Alexander Chernavinfee98532022-08-04 08:11:57 +00001703 # check the original packet is present
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001704 self.assertEqual(rx[IPv6].dst, p[IPv6].dst)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001705 self.assertEqual(rx[IPv6].hlim, p[IPv6].hlim - 1)
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001706
1707 # send packets into the tunnel, expect to receive them on
1708 # the other side
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001709 p = [
1710 (
1711 peer_1.mk_tunnel_header(self.pg1, True)
1712 / Wireguard(message_type=4, reserved_zero=0)
1713 / WireguardTransport(
1714 receiver_index=peer_1.sender,
1715 counter=ii + 1,
1716 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1717 (
1718 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1719 / UDP(sport=222, dport=223)
1720 / Raw()
1721 )
1722 ),
1723 )
1724 )
1725 for ii in range(255)
1726 ]
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001727
1728 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1729
1730 for rx in rxs:
1731 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1732 self.assertEqual(rx[IPv6].hlim, 19)
1733
1734 r1.remove_vpp_config()
Alexander Chernavin1477c722022-06-02 09:55:37 +00001735 r2.remove_vpp_config()
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001736 peer_1.remove_vpp_config()
1737 wg0.remove_vpp_config()
1738
1739 def test_wg_peer_v6o4(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001740 """Test v6o4"""
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001741
1742 port = 12353
1743
1744 # Create interfaces
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001745 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001746 wg0.admin_up()
1747 wg0.config_ip6()
1748
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001749 peer_1 = VppWgPeer(
1750 self, wg0, self.pg1.remote_ip4, port + 1, ["1::3:0/112"]
Alexander Chernavin522a5b32022-09-26 15:11:27 +00001751 ).add_vpp_config()
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001752 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1753
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001754 r1 = VppIpRoute(
1755 self, "1::3:0", 112, [VppRoutePath("1::3:1", wg0.sw_if_index)]
1756 ).add_vpp_config()
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001757
1758 # route a packet into the wg interface
1759 # use the allowed-ip prefix
1760 # this is dropped because the peer is not initiated
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001761 p = (
1762 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1763 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1764 / UDP(sport=555, dport=556)
1765 / Raw()
1766 )
Alexander Chernavin522a5b32022-09-26 15:11:27 +00001767 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001768 self.assertEqual(
1769 self.base_kp6_err + 1, self.statistics.get_err_counter(self.kp6_error)
1770 )
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001771
1772 # send a handsake from the peer with an invalid MAC
1773 p = peer_1.mk_handshake(self.pg1)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001774 p[WireguardInitiation].mac1 = b"foobar"
Alexander Chernavin522a5b32022-09-26 15:11:27 +00001775 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001776
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001777 self.assertEqual(
1778 self.base_mac4_err + 1, self.statistics.get_err_counter(self.mac4_error)
1779 )
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001780
1781 # send a handsake from the peer but signed by the wrong key.
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001782 p = peer_1.mk_handshake(
1783 self.pg1, False, X25519PrivateKey.generate().public_key()
1784 )
Alexander Chernavin522a5b32022-09-26 15:11:27 +00001785 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001786 self.assertEqual(
Alexander Chernavin1477c722022-06-02 09:55:37 +00001787 self.base_peer4_in_err + 1,
1788 self.statistics.get_err_counter(self.peer4_in_err),
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001789 )
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001790
1791 # send a valid handsake init for which we expect a response
1792 p = peer_1.mk_handshake(self.pg1)
1793
1794 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1795
1796 peer_1.consume_response(rx[0])
1797
1798 # route a packet into the wg interface
1799 # this is dropped because the peer is still not initiated
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001800 p = (
1801 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1802 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1803 / UDP(sport=555, dport=556)
1804 / Raw()
1805 )
Alexander Chernavin522a5b32022-09-26 15:11:27 +00001806 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001807 self.assertEqual(
1808 self.base_kp6_err + 2, self.statistics.get_err_counter(self.kp6_error)
1809 )
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001810
1811 # send a data packet from the peer through the tunnel
1812 # this completes the handshake
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001813 p = (
1814 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1815 / UDP(sport=222, dport=223)
1816 / Raw()
1817 )
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001818 d = peer_1.encrypt_transport(p)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001819 p = peer_1.mk_tunnel_header(self.pg1) / (
1820 Wireguard(message_type=4, reserved_zero=0)
1821 / WireguardTransport(
1822 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1823 )
1824 )
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001825 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1826
1827 for rx in rxs:
1828 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1829 self.assertEqual(rx[IPv6].hlim, 19)
1830
1831 # send a packets that are routed into the tunnel
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001832 p = (
1833 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1834 / IPv6(src=self.pg0.remote_ip6, dst="1::3:2")
1835 / UDP(sport=555, dport=556)
1836 / Raw(b"\x00" * 80)
1837 )
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001838
1839 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1840
1841 for rx in rxs:
1842 rx = IPv6(peer_1.decrypt_transport(rx))
1843
Alexander Chernavinfee98532022-08-04 08:11:57 +00001844 # check the original packet is present
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001845 self.assertEqual(rx[IPv6].dst, p[IPv6].dst)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001846 self.assertEqual(rx[IPv6].hlim, p[IPv6].hlim - 1)
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001847
1848 # send packets into the tunnel, expect to receive them on
1849 # the other side
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001850 p = [
1851 (
1852 peer_1.mk_tunnel_header(self.pg1)
1853 / Wireguard(message_type=4, reserved_zero=0)
1854 / WireguardTransport(
1855 receiver_index=peer_1.sender,
1856 counter=ii + 1,
1857 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1858 (
1859 IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20)
1860 / UDP(sport=222, dport=223)
1861 / Raw()
1862 )
1863 ),
1864 )
1865 )
1866 for ii in range(255)
1867 ]
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001868
1869 rxs = self.send_and_expect(self.pg1, p, self.pg0)
1870
1871 for rx in rxs:
1872 self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
1873 self.assertEqual(rx[IPv6].hlim, 19)
1874
1875 r1.remove_vpp_config()
1876 peer_1.remove_vpp_config()
1877 wg0.remove_vpp_config()
1878
1879 def test_wg_peer_v4o6(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001880 """Test v4o6"""
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001881
1882 port = 12363
1883
1884 # Create interfaces
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001885 wg0 = VppWgInterface(self, self.pg1.local_ip6, port).add_vpp_config()
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001886 wg0.admin_up()
1887 wg0.config_ip4()
1888
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001889 peer_1 = VppWgPeer(
1890 self, wg0, self.pg1.remote_ip6, port + 1, ["10.11.3.0/24"]
1891 ).add_vpp_config()
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001892 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1893
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001894 r1 = VppIpRoute(
1895 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
1896 ).add_vpp_config()
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001897
1898 # route a packet into the wg interface
1899 # use the allowed-ip prefix
1900 # this is dropped because the peer is not initiated
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001901 p = (
1902 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1903 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1904 / UDP(sport=555, dport=556)
1905 / Raw()
1906 )
Alexander Chernavin522a5b32022-09-26 15:11:27 +00001907 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001908 self.assertEqual(
1909 self.base_kp4_err + 1, self.statistics.get_err_counter(self.kp4_error)
1910 )
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001911
1912 # send a handsake from the peer with an invalid MAC
1913 p = peer_1.mk_handshake(self.pg1, True)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001914 p[WireguardInitiation].mac1 = b"foobar"
Alexander Chernavin522a5b32022-09-26 15:11:27 +00001915 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001916 self.assertEqual(
1917 self.base_mac6_err + 1, self.statistics.get_err_counter(self.mac6_error)
1918 )
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001919
1920 # send a handsake from the peer but signed by the wrong key.
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001921 p = peer_1.mk_handshake(
1922 self.pg1, True, X25519PrivateKey.generate().public_key()
1923 )
Alexander Chernavin522a5b32022-09-26 15:11:27 +00001924 self.send_and_assert_no_replies_ignoring_init(self.pg1, [p])
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001925 self.assertEqual(
Alexander Chernavin1477c722022-06-02 09:55:37 +00001926 self.base_peer6_in_err + 1,
1927 self.statistics.get_err_counter(self.peer6_in_err),
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001928 )
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001929
1930 # send a valid handsake init for which we expect a response
1931 p = peer_1.mk_handshake(self.pg1, True)
1932
1933 rx = self.send_and_expect(self.pg1, [p], self.pg1)
1934
1935 peer_1.consume_response(rx[0], True)
1936
1937 # route a packet into the wg interface
1938 # this is dropped because the peer is still not initiated
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001939 p = (
1940 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1941 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1942 / UDP(sport=555, dport=556)
1943 / Raw()
1944 )
Alexander Chernavin522a5b32022-09-26 15:11:27 +00001945 self.send_and_assert_no_replies_ignoring_init(self.pg0, [p])
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001946 self.assertEqual(
1947 self.base_kp4_err + 2, self.statistics.get_err_counter(self.kp4_error)
1948 )
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001949
1950 # send a data packet from the peer through the tunnel
1951 # this completes the handshake
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001952 p = (
1953 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1954 / UDP(sport=222, dport=223)
1955 / Raw()
1956 )
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001957 d = peer_1.encrypt_transport(p)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001958 p = peer_1.mk_tunnel_header(self.pg1, True) / (
1959 Wireguard(message_type=4, reserved_zero=0)
1960 / WireguardTransport(
1961 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
1962 )
1963 )
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001964 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1965
1966 for rx in rxs:
1967 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1968 self.assertEqual(rx[IP].ttl, 19)
1969
1970 # send a packets that are routed into the tunnel
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001971 p = (
1972 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1973 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
1974 / UDP(sport=555, dport=556)
1975 / Raw(b"\x00" * 80)
1976 )
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001977
1978 rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1979
1980 for rx in rxs:
1981 rx = IP(peer_1.decrypt_transport(rx, True))
1982
Alexander Chernavinfee98532022-08-04 08:11:57 +00001983 # check the original packet is present
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001984 self.assertEqual(rx[IP].dst, p[IP].dst)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001985 self.assertEqual(rx[IP].ttl, p[IP].ttl - 1)
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07001986
1987 # send packets into the tunnel, expect to receive them on
1988 # the other side
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001989 p = [
1990 (
1991 peer_1.mk_tunnel_header(self.pg1, True)
1992 / Wireguard(message_type=4, reserved_zero=0)
1993 / WireguardTransport(
1994 receiver_index=peer_1.sender,
1995 counter=ii + 1,
1996 encrypted_encapsulated_packet=peer_1.encrypt_transport(
1997 (
1998 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
1999 / UDP(sport=222, dport=223)
2000 / Raw()
2001 )
2002 ),
2003 )
2004 )
2005 for ii in range(255)
2006 ]
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07002007
2008 rxs = self.send_and_expect(self.pg1, p, self.pg0)
2009
2010 for rx in rxs:
2011 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
2012 self.assertEqual(rx[IP].ttl, 19)
2013
2014 r1.remove_vpp_config()
2015 peer_1.remove_vpp_config()
2016 wg0.remove_vpp_config()
2017
Neale Rannsd75a2d12020-09-10 08:49:10 +00002018 def test_wg_multi_peer(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002019 """multiple peer setup"""
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07002020 port = 12373
Neale Rannsd75a2d12020-09-10 08:49:10 +00002021
2022 # Create interfaces
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002023 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2024 wg1 = VppWgInterface(self, self.pg2.local_ip4, port + 1).add_vpp_config()
Neale Rannsd75a2d12020-09-10 08:49:10 +00002025 wg0.admin_up()
2026 wg1.admin_up()
2027
2028 # Check peer counter
2029 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 0)
2030
2031 self.pg_enable_capture(self.pg_interfaces)
2032 self.pg_start()
Artem Glazychevedca1322020-08-31 17:12:30 +07002033
2034 # Create many peers on sencond interface
2035 NUM_PEERS = 16
2036 self.pg2.generate_remote_hosts(NUM_PEERS)
2037 self.pg2.configure_ipv4_neighbors()
Neale Rannsd75a2d12020-09-10 08:49:10 +00002038 self.pg1.generate_remote_hosts(NUM_PEERS)
2039 self.pg1.configure_ipv4_neighbors()
Artem Glazychevedca1322020-08-31 17:12:30 +07002040
Neale Rannsd75a2d12020-09-10 08:49:10 +00002041 peers_1 = []
2042 peers_2 = []
Artem Glazychevde3caf32021-05-20 12:33:52 +07002043 routes_1 = []
2044 routes_2 = []
Artem Glazychevedca1322020-08-31 17:12:30 +07002045 for i in range(NUM_PEERS):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002046 peers_1.append(
2047 VppWgPeer(
2048 self,
2049 wg0,
2050 self.pg1.remote_hosts[i].ip4,
2051 port + 1 + i,
2052 ["10.0.%d.4/32" % i],
2053 ).add_vpp_config()
2054 )
2055 routes_1.append(
2056 VppIpRoute(
2057 self,
2058 "10.0.%d.4" % i,
2059 32,
2060 [VppRoutePath(self.pg1.remote_hosts[i].ip4, wg0.sw_if_index)],
2061 ).add_vpp_config()
2062 )
Artem Glazychevde3caf32021-05-20 12:33:52 +07002063
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002064 peers_2.append(
2065 VppWgPeer(
2066 self,
2067 wg1,
2068 self.pg2.remote_hosts[i].ip4,
2069 port + 100 + i,
2070 ["10.100.%d.4/32" % i],
2071 ).add_vpp_config()
2072 )
2073 routes_2.append(
2074 VppIpRoute(
2075 self,
2076 "10.100.%d.4" % i,
2077 32,
2078 [VppRoutePath(self.pg2.remote_hosts[i].ip4, wg1.sw_if_index)],
2079 ).add_vpp_config()
2080 )
Neale Rannsd75a2d12020-09-10 08:49:10 +00002081
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002082 self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_PEERS * 2)
Artem Glazychevedca1322020-08-31 17:12:30 +07002083
2084 self.logger.info(self.vapi.cli("show wireguard peer"))
2085 self.logger.info(self.vapi.cli("show wireguard interface"))
2086 self.logger.info(self.vapi.cli("show adj 37"))
2087 self.logger.info(self.vapi.cli("sh ip fib 172.16.3.17"))
2088 self.logger.info(self.vapi.cli("sh ip fib 10.11.3.0"))
2089
Artem Glazychevde3caf32021-05-20 12:33:52 +07002090 # remove routes
2091 for r in routes_1:
2092 r.remove_vpp_config()
2093 for r in routes_2:
2094 r.remove_vpp_config()
2095
Artem Glazychevedca1322020-08-31 17:12:30 +07002096 # remove peers
Neale Rannsd75a2d12020-09-10 08:49:10 +00002097 for p in peers_1:
Artem Glazychevedca1322020-08-31 17:12:30 +07002098 self.assertTrue(p.query_vpp_config())
2099 p.remove_vpp_config()
Neale Rannsd75a2d12020-09-10 08:49:10 +00002100 for p in peers_2:
2101 self.assertTrue(p.query_vpp_config())
2102 p.remove_vpp_config()
Artem Glazychevedca1322020-08-31 17:12:30 +07002103
2104 wg0.remove_vpp_config()
Neale Rannsd75a2d12020-09-10 08:49:10 +00002105 wg1.remove_vpp_config()
Artem Glazychev8eb69402020-09-14 11:36:01 +07002106
Artem Glazychev9e24f7e2021-05-25 12:06:42 +07002107 def test_wg_multi_interface(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002108 """Multi-tunnel on the same port"""
Artem Glazychev9e24f7e2021-05-25 12:06:42 +07002109 port = 12500
2110
2111 # Create many wireguard interfaces
2112 NUM_IFS = 4
2113 self.pg1.generate_remote_hosts(NUM_IFS)
2114 self.pg1.configure_ipv4_neighbors()
2115 self.pg0.generate_remote_hosts(NUM_IFS)
2116 self.pg0.configure_ipv4_neighbors()
2117
Artem Glazychev53badfc2023-01-24 16:10:29 +07002118 self.pg_enable_capture(self.pg_interfaces)
2119 self.pg_start()
2120
Artem Glazychev9e24f7e2021-05-25 12:06:42 +07002121 # Create interfaces with a peer on each
2122 peers = []
2123 routes = []
2124 wg_ifs = []
2125 for i in range(NUM_IFS):
2126 # Use the same port for each interface
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002127 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
Artem Glazychev9e24f7e2021-05-25 12:06:42 +07002128 wg0.admin_up()
2129 wg0.config_ip4()
2130 wg_ifs.append(wg0)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002131 peers.append(
2132 VppWgPeer(
2133 self,
2134 wg0,
2135 self.pg1.remote_hosts[i].ip4,
2136 port + 1 + i,
2137 ["10.0.%d.0/24" % i],
2138 ).add_vpp_config()
2139 )
Artem Glazychev9e24f7e2021-05-25 12:06:42 +07002140
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002141 routes.append(
2142 VppIpRoute(
2143 self,
2144 "10.0.%d.0" % i,
2145 24,
2146 [VppRoutePath("10.0.%d.4" % i, wg0.sw_if_index)],
2147 ).add_vpp_config()
2148 )
Artem Glazychev9e24f7e2021-05-25 12:06:42 +07002149
2150 self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_IFS)
2151
Artem Glazychev53badfc2023-01-24 16:10:29 +07002152 # skip the first automatic handshake
2153 self.pg1.get_capture(NUM_IFS, timeout=HANDSHAKE_JITTER)
2154
Artem Glazychev9e24f7e2021-05-25 12:06:42 +07002155 for i in range(NUM_IFS):
2156 # send a valid handsake init for which we expect a response
2157 p = peers[i].mk_handshake(self.pg1)
2158 rx = self.send_and_expect(self.pg1, [p], self.pg1)
2159 peers[i].consume_response(rx[0])
2160
2161 # send a data packet from the peer through the tunnel
2162 # this completes the handshake
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002163 p = (
2164 IP(src="10.0.%d.4" % i, dst=self.pg0.remote_hosts[i].ip4, ttl=20)
2165 / UDP(sport=222, dport=223)
2166 / Raw()
2167 )
Artem Glazychev9e24f7e2021-05-25 12:06:42 +07002168 d = peers[i].encrypt_transport(p)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002169 p = peers[i].mk_tunnel_header(self.pg1) / (
2170 Wireguard(message_type=4, reserved_zero=0)
2171 / WireguardTransport(
2172 receiver_index=peers[i].sender,
2173 counter=0,
2174 encrypted_encapsulated_packet=d,
2175 )
2176 )
Artem Glazychev9e24f7e2021-05-25 12:06:42 +07002177 rxs = self.send_and_expect(self.pg1, [p], self.pg0)
2178 for rx in rxs:
2179 self.assertEqual(rx[IP].dst, self.pg0.remote_hosts[i].ip4)
2180 self.assertEqual(rx[IP].ttl, 19)
2181
2182 # send a packets that are routed into the tunnel
2183 for i in range(NUM_IFS):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002184 p = (
2185 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2186 / IP(src=self.pg0.remote_hosts[i].ip4, dst="10.0.%d.4" % i)
2187 / UDP(sport=555, dport=556)
2188 / Raw(b"\x00" * 80)
2189 )
Artem Glazychev9e24f7e2021-05-25 12:06:42 +07002190
2191 rxs = self.send_and_expect(self.pg0, p * 64, self.pg1)
2192
2193 for rx in rxs:
2194 rx = IP(peers[i].decrypt_transport(rx))
2195
2196 # check the oringial packet is present
2197 self.assertEqual(rx[IP].dst, p[IP].dst)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002198 self.assertEqual(rx[IP].ttl, p[IP].ttl - 1)
Artem Glazychev9e24f7e2021-05-25 12:06:42 +07002199
2200 # send packets into the tunnel
2201 for i in range(NUM_IFS):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002202 p = [
2203 (
2204 peers[i].mk_tunnel_header(self.pg1)
2205 / Wireguard(message_type=4, reserved_zero=0)
2206 / WireguardTransport(
2207 receiver_index=peers[i].sender,
2208 counter=ii + 1,
2209 encrypted_encapsulated_packet=peers[i].encrypt_transport(
2210 (
2211 IP(
2212 src="10.0.%d.4" % i,
2213 dst=self.pg0.remote_hosts[i].ip4,
2214 ttl=20,
2215 )
2216 / UDP(sport=222, dport=223)
2217 / Raw()
2218 )
2219 ),
2220 )
2221 )
2222 for ii in range(64)
2223 ]
Artem Glazychev9e24f7e2021-05-25 12:06:42 +07002224
2225 rxs = self.send_and_expect(self.pg1, p, self.pg0)
2226
2227 for rx in rxs:
2228 self.assertEqual(rx[IP].dst, self.pg0.remote_hosts[i].ip4)
2229 self.assertEqual(rx[IP].ttl, 19)
2230
2231 for r in routes:
2232 r.remove_vpp_config()
2233 for p in peers:
2234 p.remove_vpp_config()
2235 for i in wg_ifs:
2236 i.remove_vpp_config()
2237
Artem Glazychevdd630d12021-06-11 00:10:00 +07002238 def test_wg_event(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002239 """Test events"""
Artem Glazychevdd630d12021-06-11 00:10:00 +07002240 port = 12600
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002241 ESTABLISHED_FLAG = (
2242 VppEnum.vl_api_wireguard_peer_flags_t.WIREGUARD_PEER_ESTABLISHED
2243 )
2244 DEAD_FLAG = VppEnum.vl_api_wireguard_peer_flags_t.WIREGUARD_PEER_STATUS_DEAD
Artem Glazychevdd630d12021-06-11 00:10:00 +07002245
2246 # Create interfaces
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002247 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2248 wg1 = VppWgInterface(self, self.pg2.local_ip4, port + 1).add_vpp_config()
Artem Glazychevdd630d12021-06-11 00:10:00 +07002249 wg0.admin_up()
2250 wg1.admin_up()
2251
2252 # Check peer counter
2253 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 0)
2254
2255 self.pg_enable_capture(self.pg_interfaces)
2256 self.pg_start()
2257
2258 # Create peers
2259 NUM_PEERS = 2
2260 self.pg2.generate_remote_hosts(NUM_PEERS)
2261 self.pg2.configure_ipv4_neighbors()
2262 self.pg1.generate_remote_hosts(NUM_PEERS)
2263 self.pg1.configure_ipv4_neighbors()
2264
2265 peers_0 = []
2266 peers_1 = []
2267 routes_0 = []
2268 routes_1 = []
2269 for i in range(NUM_PEERS):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002270 peers_0.append(
2271 VppWgPeer(
2272 self,
2273 wg0,
2274 self.pg1.remote_hosts[i].ip4,
2275 port + 1 + i,
2276 ["10.0.%d.4/32" % i],
2277 ).add_vpp_config()
2278 )
2279 routes_0.append(
2280 VppIpRoute(
2281 self,
2282 "10.0.%d.4" % i,
2283 32,
2284 [VppRoutePath(self.pg1.remote_hosts[i].ip4, wg0.sw_if_index)],
2285 ).add_vpp_config()
2286 )
Artem Glazychevdd630d12021-06-11 00:10:00 +07002287
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002288 peers_1.append(
2289 VppWgPeer(
2290 self,
2291 wg1,
2292 self.pg2.remote_hosts[i].ip4,
2293 port + 100 + i,
2294 ["10.100.%d.4/32" % i],
2295 ).add_vpp_config()
2296 )
2297 routes_1.append(
2298 VppIpRoute(
2299 self,
2300 "10.100.%d.4" % i,
2301 32,
2302 [VppRoutePath(self.pg2.remote_hosts[i].ip4, wg1.sw_if_index)],
2303 ).add_vpp_config()
2304 )
Artem Glazychevdd630d12021-06-11 00:10:00 +07002305
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002306 self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_PEERS * 2)
Artem Glazychevdd630d12021-06-11 00:10:00 +07002307
Artem Glazychev53badfc2023-01-24 16:10:29 +07002308 # skip the first automatic handshake
2309 self.pg1.get_capture(NUM_PEERS, timeout=HANDSHAKE_JITTER)
2310 self.pg2.get_capture(NUM_PEERS, timeout=HANDSHAKE_JITTER)
2311
Artem Glazychevdd630d12021-06-11 00:10:00 +07002312 # Want events from the first perr of wg0
2313 # and from all wg1 peers
2314 peers_0[0].want_events()
2315 wg1.want_events()
2316
2317 for i in range(NUM_PEERS):
Artem Glazychev4d290c32023-01-24 15:34:00 +07002318 # wg0 peers: send a valid handsake init for which we expect a response
Artem Glazychevdd630d12021-06-11 00:10:00 +07002319 p = peers_0[i].mk_handshake(self.pg1)
2320 rx = self.send_and_expect(self.pg1, [p], self.pg1)
2321 peers_0[i].consume_response(rx[0])
Artem Glazychev4d290c32023-01-24 15:34:00 +07002322
2323 # wg0 peers: send empty packet, it means successful connection (WIREGUARD_PEER_ESTABLISHED)
2324 keepalive = peers_0[i].encrypt_transport(0)
2325 p = peers_0[i].mk_tunnel_header(self.pg1) / (
2326 Wireguard(message_type=4, reserved_zero=0)
2327 / WireguardTransport(
2328 receiver_index=peers_0[i].sender,
2329 counter=0,
2330 encrypted_encapsulated_packet=keepalive,
2331 )
2332 )
2333 self.send_and_assert_no_replies(self.pg1, [p])
2334
2335 # wg0 peers: wait for established flag
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002336 if i == 0:
Artem Glazychevdd630d12021-06-11 00:10:00 +07002337 peers_0[0].wait_event(ESTABLISHED_FLAG)
2338
Artem Glazychev4d290c32023-01-24 15:34:00 +07002339 # wg1 peers: send a valid handsake init for which we expect a response
Artem Glazychevdd630d12021-06-11 00:10:00 +07002340 p = peers_1[i].mk_handshake(self.pg2)
2341 rx = self.send_and_expect(self.pg2, [p], self.pg2)
2342 peers_1[i].consume_response(rx[0])
2343
Artem Glazychev4d290c32023-01-24 15:34:00 +07002344 # wg1 peers: send empty packet, it means successful connection (WIREGUARD_PEER_ESTABLISHED)
2345 keepalive = peers_1[i].encrypt_transport(0)
2346 p = peers_1[i].mk_tunnel_header(self.pg2) / (
2347 Wireguard(message_type=4, reserved_zero=0)
2348 / WireguardTransport(
2349 receiver_index=peers_1[i].sender,
2350 counter=0,
2351 encrypted_encapsulated_packet=keepalive,
2352 )
2353 )
2354 self.send_and_assert_no_replies(self.pg2, [p])
2355
2356 # wg1 peers: wait for established flag
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002357 wg1.wait_events(ESTABLISHED_FLAG, [peers_1[0].index, peers_1[1].index])
Artem Glazychevdd630d12021-06-11 00:10:00 +07002358
2359 # remove routes
2360 for r in routes_0:
2361 r.remove_vpp_config()
2362 for r in routes_1:
2363 r.remove_vpp_config()
2364
2365 # remove peers
2366 for i in range(NUM_PEERS):
2367 self.assertTrue(peers_0[i].query_vpp_config())
2368 peers_0[i].remove_vpp_config()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002369 if i == 0:
Artem Glazychevdd630d12021-06-11 00:10:00 +07002370 peers_0[i].wait_event(0)
2371 peers_0[i].wait_event(DEAD_FLAG)
2372 for p in peers_1:
2373 self.assertTrue(p.query_vpp_config())
2374 p.remove_vpp_config()
2375 p.wait_event(0)
2376 p.wait_event(DEAD_FLAG)
2377
2378 wg0.remove_vpp_config()
2379 wg1.remove_vpp_config()
2380
Alexander Chernavin31ce1a62022-09-01 13:42:56 +00002381 def test_wg_sending_handshake_when_admin_down(self):
2382 """Sending handshake when admin down"""
2383 port = 12323
2384
2385 # create wg interface
2386 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2387 wg0.config_ip4()
2388
2389 # create a peer
2390 peer_1 = VppWgPeer(
2391 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
2392 ).add_vpp_config()
2393 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2394
2395 self.pg_enable_capture(self.pg_interfaces)
2396 self.pg_start()
2397
2398 # wait for the peer to send a handshake initiation
2399 # expect no handshakes
2400 for i in range(2):
2401 self.pg1.assert_nothing_captured(remark="handshake packet(s) sent")
2402
2403 self.pg_enable_capture(self.pg_interfaces)
2404 self.pg_start()
2405
2406 # administratively enable the wg interface
2407 # expect the peer to send a handshake initiation
2408 wg0.admin_up()
2409 rxs = self.pg1.get_capture(1, timeout=2)
2410 peer_1.consume_init(rxs[0], self.pg1)
2411
2412 self.pg_enable_capture(self.pg_interfaces)
2413 self.pg_start()
2414
2415 # administratively disable the wg interface
2416 # expect no handshakes
2417 wg0.admin_down()
2418 for i in range(6):
2419 self.pg1.assert_nothing_captured(remark="handshake packet(s) sent")
2420
2421 # remove configs
2422 peer_1.remove_vpp_config()
2423 wg0.remove_vpp_config()
2424
2425 def test_wg_sending_data_when_admin_down(self):
2426 """Sending data when admin down"""
2427 port = 12323
2428
2429 # create wg interface
2430 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2431 wg0.admin_up()
2432 wg0.config_ip4()
2433
2434 self.pg_enable_capture(self.pg_interfaces)
2435 self.pg_start()
2436
2437 # create a peer
2438 peer_1 = VppWgPeer(
2439 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
2440 ).add_vpp_config()
2441 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2442
2443 # create a route to rewrite traffic into the wg interface
2444 r1 = VppIpRoute(
2445 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
2446 ).add_vpp_config()
2447
2448 # wait for the peer to send a handshake initiation
2449 rxs = self.pg1.get_capture(1, timeout=2)
2450
2451 # prepare and send a handshake response
2452 # expect a keepalive message
2453 resp = peer_1.consume_init(rxs[0], self.pg1)
2454 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
2455
2456 # verify the keepalive message
2457 b = peer_1.decrypt_transport(rxs[0])
2458 self.assertEqual(0, len(b))
2459
2460 # prepare and send a packet that will be rewritten into the wg interface
2461 # expect a data packet sent
2462 p = (
2463 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2464 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
2465 / UDP(sport=555, dport=556)
2466 / Raw()
2467 )
2468 rxs = self.send_and_expect(self.pg0, [p], self.pg1)
2469
2470 # verify the data packet
2471 peer_1.validate_encapped(rxs, p)
2472
2473 # administratively disable the wg interface
2474 wg0.admin_down()
2475
2476 # send a packet that will be rewritten into the wg interface
2477 # expect no data packets sent
2478 self.send_and_assert_no_replies(self.pg0, [p])
2479
2480 # administratively enable the wg interface
2481 # expect the peer to send a handshake initiation
2482 wg0.admin_up()
2483 peer_1.noise_reset()
2484 rxs = self.pg1.get_capture(1, timeout=2)
2485 resp = peer_1.consume_init(rxs[0], self.pg1)
2486
2487 # send a packet that will be rewritten into the wg interface
2488 # expect no data packets sent because the peer is not initiated
2489 self.send_and_assert_no_replies(self.pg0, [p])
2490 self.assertEqual(
2491 self.base_kp4_err + 1, self.statistics.get_err_counter(self.kp4_error)
2492 )
2493
2494 # send a handshake response and expect a keepalive message
2495 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
2496
2497 # verify the keepalive message
2498 b = peer_1.decrypt_transport(rxs[0])
2499 self.assertEqual(0, len(b))
2500
2501 # send a packet that will be rewritten into the wg interface
2502 # expect a data packet sent
2503 rxs = self.send_and_expect(self.pg0, [p], self.pg1)
2504
2505 # verify the data packet
2506 peer_1.validate_encapped(rxs, p)
2507
2508 # remove configs
2509 r1.remove_vpp_config()
2510 peer_1.remove_vpp_config()
2511 wg0.remove_vpp_config()
2512
Artem Glazychev8eb69402020-09-14 11:36:01 +07002513
Dave Wallace8a0a9d22022-10-04 22:02:49 -04002514@tag_fixme_vpp_debug
Artem Glazychev8eb69402020-09-14 11:36:01 +07002515class WireguardHandoffTests(TestWg):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002516 """Wireguard Tests in multi worker setup"""
2517
Klement Sekera8d815022021-03-15 16:58:10 +01002518 vpp_worker_count = 2
Artem Glazychev8eb69402020-09-14 11:36:01 +07002519
2520 def test_wg_peer_init(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002521 """Handoff"""
Artem Glazychev8eb69402020-09-14 11:36:01 +07002522
Artem Glazychev7dd3b5b2021-06-03 20:11:54 +07002523 port = 12383
Artem Glazychev8eb69402020-09-14 11:36:01 +07002524
2525 # Create interfaces
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002526 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
Artem Glazychev8eb69402020-09-14 11:36:01 +07002527 wg0.admin_up()
2528 wg0.config_ip4()
2529
Artem Glazychev53badfc2023-01-24 16:10:29 +07002530 self.pg_enable_capture(self.pg_interfaces)
2531 self.pg_start()
2532
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002533 peer_1 = VppWgPeer(
2534 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.2.0/24", "10.11.3.0/24"]
2535 ).add_vpp_config()
Artem Glazychev8eb69402020-09-14 11:36:01 +07002536 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2537
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002538 r1 = VppIpRoute(
2539 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
2540 ).add_vpp_config()
Artem Glazychevde3caf32021-05-20 12:33:52 +07002541
Artem Glazychev53badfc2023-01-24 16:10:29 +07002542 # skip the first automatic handshake
2543 self.pg1.get_capture(1, timeout=HANDSHAKE_JITTER)
2544
Artem Glazychev8eb69402020-09-14 11:36:01 +07002545 # send a valid handsake init for which we expect a response
2546 p = peer_1.mk_handshake(self.pg1)
2547
2548 rx = self.send_and_expect(self.pg1, [p], self.pg1)
2549
2550 peer_1.consume_response(rx[0])
2551
2552 # send a data packet from the peer through the tunnel
2553 # this completes the handshake and pins the peer to worker 0
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002554 p = (
2555 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
2556 / UDP(sport=222, dport=223)
2557 / Raw()
2558 )
Artem Glazychev8eb69402020-09-14 11:36:01 +07002559 d = peer_1.encrypt_transport(p)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002560 p = peer_1.mk_tunnel_header(self.pg1) / (
2561 Wireguard(message_type=4, reserved_zero=0)
2562 / WireguardTransport(
2563 receiver_index=peer_1.sender, counter=0, encrypted_encapsulated_packet=d
2564 )
2565 )
2566 rxs = self.send_and_expect(self.pg1, [p], self.pg0, worker=0)
Artem Glazychev8eb69402020-09-14 11:36:01 +07002567
2568 for rx in rxs:
2569 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
2570 self.assertEqual(rx[IP].ttl, 19)
2571
2572 # send a packets that are routed into the tunnel
2573 # and pins the peer tp worker 1
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002574 pe = (
2575 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2576 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
2577 / UDP(sport=555, dport=556)
2578 / Raw(b"\x00" * 80)
2579 )
Artem Glazychev8eb69402020-09-14 11:36:01 +07002580 rxs = self.send_and_expect(self.pg0, pe * 255, self.pg1, worker=1)
2581 peer_1.validate_encapped(rxs, pe)
2582
2583 # send packets into the tunnel, from the other worker
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002584 p = [
2585 (
2586 peer_1.mk_tunnel_header(self.pg1)
2587 / Wireguard(message_type=4, reserved_zero=0)
2588 / WireguardTransport(
Artem Glazychevdd630d12021-06-11 00:10:00 +07002589 receiver_index=peer_1.sender,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002590 counter=ii + 1,
Artem Glazychevdd630d12021-06-11 00:10:00 +07002591 encrypted_encapsulated_packet=peer_1.encrypt_transport(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002592 (
2593 IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20)
2594 / UDP(sport=222, dport=223)
2595 / Raw()
2596 )
2597 ),
2598 )
2599 )
2600 for ii in range(255)
2601 ]
Artem Glazychev8eb69402020-09-14 11:36:01 +07002602
2603 rxs = self.send_and_expect(self.pg1, p, self.pg0, worker=1)
2604
2605 for rx in rxs:
2606 self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
2607 self.assertEqual(rx[IP].ttl, 19)
2608
2609 # send a packets that are routed into the tunnel
Alexander Chernavin522a5b32022-09-26 15:11:27 +00002610 # from worker 0
Artem Glazychev8eb69402020-09-14 11:36:01 +07002611 rxs = self.send_and_expect(self.pg0, pe * 255, self.pg1, worker=0)
2612
2613 peer_1.validate_encapped(rxs, pe)
2614
Artem Glazychevde3caf32021-05-20 12:33:52 +07002615 r1.remove_vpp_config()
Artem Glazychev8eb69402020-09-14 11:36:01 +07002616 peer_1.remove_vpp_config()
2617 wg0.remove_vpp_config()
Artem Glazychev9e24f7e2021-05-25 12:06:42 +07002618
2619 @unittest.skip("test disabled")
2620 def test_wg_multi_interface(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02002621 """Multi-tunnel on the same port"""
Alexander Chernavinae605382022-08-17 08:30:43 +00002622
2623
2624class TestWgFIB(VppTestCase):
2625 """Wireguard FIB Test Case"""
2626
2627 @classmethod
2628 def setUpClass(cls):
2629 super(TestWgFIB, cls).setUpClass()
2630
2631 @classmethod
2632 def tearDownClass(cls):
2633 super(TestWgFIB, cls).tearDownClass()
2634
2635 def setUp(self):
2636 super(TestWgFIB, self).setUp()
2637
2638 self.create_pg_interfaces(range(2))
2639
2640 for i in self.pg_interfaces:
2641 i.admin_up()
2642 i.config_ip4()
2643
2644 def tearDown(self):
2645 for i in self.pg_interfaces:
2646 i.unconfig_ip4()
2647 i.admin_down()
2648 super(TestWgFIB, self).tearDown()
2649
2650 def test_wg_fib_tracking(self):
2651 """FIB tracking"""
2652 port = 12323
2653
2654 # create wg interface
2655 wg0 = VppWgInterface(self, self.pg1.local_ip4, port).add_vpp_config()
2656 wg0.admin_up()
2657 wg0.config_ip4()
2658
2659 self.pg_enable_capture(self.pg_interfaces)
2660 self.pg_start()
2661
2662 # create a peer
2663 peer_1 = VppWgPeer(
2664 self, wg0, self.pg1.remote_ip4, port + 1, ["10.11.3.0/24"]
2665 ).add_vpp_config()
2666 self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
2667
2668 # create a route to rewrite traffic into the wg interface
2669 r1 = VppIpRoute(
2670 self, "10.11.3.0", 24, [VppRoutePath("10.11.3.1", wg0.sw_if_index)]
2671 ).add_vpp_config()
2672
2673 # resolve ARP and expect the adjacency to update
2674 self.pg1.resolve_arp()
2675
2676 # wait for the peer to send a handshake initiation
2677 rxs = self.pg1.get_capture(2, timeout=6)
2678
2679 # prepare and send a handshake response
2680 # expect a keepalive message
2681 resp = peer_1.consume_init(rxs[1], self.pg1)
2682 rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
2683
2684 # verify the keepalive message
2685 b = peer_1.decrypt_transport(rxs[0])
2686 self.assertEqual(0, len(b))
2687
2688 # prepare and send a packet that will be rewritten into the wg interface
Alexander Chernavin31ce1a62022-09-01 13:42:56 +00002689 # expect a data packet sent
Alexander Chernavinae605382022-08-17 08:30:43 +00002690 p = (
2691 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
2692 / IP(src=self.pg0.remote_ip4, dst="10.11.3.2")
2693 / UDP(sport=555, dport=556)
2694 / Raw()
2695 )
2696 rxs = self.send_and_expect(self.pg0, [p], self.pg1)
2697
2698 # verify the data packet
2699 peer_1.validate_encapped(rxs, p)
2700
2701 # remove configs
2702 r1.remove_vpp_config()
2703 peer_1.remove_vpp_config()
2704 wg0.remove_vpp_config()