wireguard: add events for peer
[vpp.git] / test / test_wireguard.py
1 #!/usr/bin/env python3
2 """ Wg tests """
3
4 import datetime
5 import base64
6 import os
7
8 from hashlib import blake2s
9 from scapy.packet import Packet
10 from scapy.packet import Raw
11 from scapy.layers.l2 import Ether, ARP
12 from scapy.layers.inet import IP, UDP
13 from scapy.layers.inet6 import IPv6
14 from scapy.contrib.wireguard import Wireguard, WireguardResponse, \
15     WireguardInitiation, WireguardTransport
16 from cryptography.hazmat.primitives.asymmetric.x25519 import \
17     X25519PrivateKey, X25519PublicKey
18 from cryptography.hazmat.primitives.serialization import Encoding, \
19     PrivateFormat, PublicFormat, NoEncryption
20 from cryptography.hazmat.primitives.hashes import BLAKE2s, Hash
21 from cryptography.hazmat.primitives.hmac import HMAC
22 from cryptography.hazmat.backends import default_backend
23 from noise.connection import NoiseConnection, Keypair
24
25 from vpp_ipip_tun_interface import VppIpIpTunInterface
26 from vpp_interface import VppInterface
27 from vpp_ip_route import VppIpRoute, VppRoutePath
28 from vpp_object import VppObject
29 from vpp_papi import VppEnum
30 from framework import VppTestCase
31 from re import compile
32 import unittest
33
34 """ TestWg is a subclass of  VPPTestCase classes.
35
36 Wg test.
37
38 """
39
40
41 def private_key_bytes(k):
42     return k.private_bytes(Encoding.Raw,
43                            PrivateFormat.Raw,
44                            NoEncryption())
45
46
47 def public_key_bytes(k):
48     return k.public_bytes(Encoding.Raw,
49                           PublicFormat.Raw)
50
51
52 class VppWgInterface(VppInterface):
53     """
54     VPP WireGuard interface
55     """
56
57     def __init__(self, test, src, port):
58         super(VppWgInterface, self).__init__(test)
59
60         self.port = port
61         self.src = src
62         self.private_key = X25519PrivateKey.generate()
63         self.public_key = self.private_key.public_key()
64
65     def public_key_bytes(self):
66         return public_key_bytes(self.public_key)
67
68     def private_key_bytes(self):
69         return private_key_bytes(self.private_key)
70
71     def add_vpp_config(self):
72         r = self.test.vapi.wireguard_interface_create(interface={
73             'user_instance': 0xffffffff,
74             'port': self.port,
75             'src_ip': self.src,
76             'private_key': private_key_bytes(self.private_key),
77             'generate_key': False
78         })
79         self.set_sw_if_index(r.sw_if_index)
80         self.test.registry.register(self, self.test.logger)
81         return self
82
83     def remove_vpp_config(self):
84         self.test.vapi.wireguard_interface_delete(
85             sw_if_index=self._sw_if_index)
86
87     def query_vpp_config(self):
88         ts = self.test.vapi.wireguard_interface_dump(sw_if_index=0xffffffff)
89         for t in ts:
90             if t.interface.sw_if_index == self._sw_if_index and \
91                str(t.interface.src_ip) == self.src and \
92                t.interface.port == self.port and \
93                t.interface.private_key == private_key_bytes(self.private_key):
94                 return True
95         return False
96
97     def want_events(self, peer_index=0xffffffff):
98         self.test.vapi.want_wireguard_peer_events(
99             enable_disable=1,
100             pid=os.getpid(),
101             sw_if_index=self._sw_if_index,
102             peer_index=peer_index)
103
104     def wait_events(self, expect, peers, timeout=5):
105         for i in range(len(peers)):
106             rv = self.test.vapi.wait_for_event(timeout, "wireguard_peer_event")
107             self.test.assertEqual(rv.peer_index, peers[i])
108             self.test.assertEqual(rv.flags, expect)
109
110     def __str__(self):
111         return self.object_id()
112
113     def object_id(self):
114         return "wireguard-%d" % self._sw_if_index
115
116
117 def find_route(test, prefix, is_ip6, table_id=0):
118     routes = test.vapi.ip_route_dump(table_id, is_ip6)
119
120     for e in routes:
121         if table_id == e.route.table_id \
122            and str(e.route.prefix) == str(prefix):
123             return True
124     return False
125
126
127 NOISE_HANDSHAKE_NAME = b"Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s"
128 NOISE_IDENTIFIER_NAME = b"WireGuard v1 zx2c4 Jason@zx2c4.com"
129
130
131 class VppWgPeer(VppObject):
132
133     def __init__(self,
134                  test,
135                  itf,
136                  endpoint,
137                  port,
138                  allowed_ips,
139                  persistent_keepalive=15):
140         self._test = test
141         self.itf = itf
142         self.endpoint = endpoint
143         self.port = port
144         self.allowed_ips = allowed_ips
145         self.persistent_keepalive = persistent_keepalive
146
147         # remote peer's public
148         self.private_key = X25519PrivateKey.generate()
149         self.public_key = self.private_key.public_key()
150
151         self.noise = NoiseConnection.from_name(NOISE_HANDSHAKE_NAME)
152
153     def add_vpp_config(self, is_ip6=False):
154         rv = self._test.vapi.wireguard_peer_add(
155             peer={
156                 'public_key': self.public_key_bytes(),
157                 'port': self.port,
158                 'endpoint': self.endpoint,
159                 'n_allowed_ips': len(self.allowed_ips),
160                 'allowed_ips': self.allowed_ips,
161                 'sw_if_index': self.itf.sw_if_index,
162                 'persistent_keepalive': self.persistent_keepalive})
163         self.index = rv.peer_index
164         self.receiver_index = self.index + 1
165         self._test.registry.register(self, self._test.logger)
166         return self
167
168     def remove_vpp_config(self):
169         self._test.vapi.wireguard_peer_remove(peer_index=self.index)
170
171     def object_id(self):
172         return ("wireguard-peer-%s" % self.index)
173
174     def public_key_bytes(self):
175         return public_key_bytes(self.public_key)
176
177     def query_vpp_config(self):
178         peers = self._test.vapi.wireguard_peers_dump()
179
180         for p in peers:
181             if p.peer.public_key == self.public_key_bytes() and \
182                p.peer.port == self.port and \
183                str(p.peer.endpoint) == self.endpoint and \
184                p.peer.sw_if_index == self.itf.sw_if_index and \
185                len(self.allowed_ips) == p.peer.n_allowed_ips:
186                 self.allowed_ips.sort()
187                 p.peer.allowed_ips.sort()
188
189                 for (a1, a2) in zip(self.allowed_ips, p.peer.allowed_ips):
190                     if str(a1) != str(a2):
191                         return False
192                 return True
193         return False
194
195     def set_responder(self):
196         self.noise.set_as_responder()
197
198     def mk_tunnel_header(self, tx_itf, is_ip6=False):
199         if is_ip6 is False:
200             return (Ether(dst=tx_itf.local_mac, src=tx_itf.remote_mac) /
201                     IP(src=self.endpoint, dst=self.itf.src) /
202                     UDP(sport=self.port, dport=self.itf.port))
203         else:
204             return (Ether(dst=tx_itf.local_mac, src=tx_itf.remote_mac) /
205                     IPv6(src=self.endpoint, dst=self.itf.src) /
206                     UDP(sport=self.port, dport=self.itf.port))
207
208     def noise_init(self, public_key=None):
209         self.noise.set_prologue(NOISE_IDENTIFIER_NAME)
210         self.noise.set_psks(psk=bytes(bytearray(32)))
211
212         if not public_key:
213             public_key = self.itf.public_key
214
215         # local/this private
216         self.noise.set_keypair_from_private_bytes(
217             Keypair.STATIC,
218             private_key_bytes(self.private_key))
219         # remote's public
220         self.noise.set_keypair_from_public_bytes(
221             Keypair.REMOTE_STATIC,
222             public_key_bytes(public_key))
223
224         self.noise.start_handshake()
225
226     def mk_handshake(self, tx_itf, is_ip6=False, public_key=None):
227         self.noise.set_as_initiator()
228         self.noise_init(public_key)
229
230         p = (Wireguard() / WireguardInitiation())
231
232         p[Wireguard].message_type = 1
233         p[Wireguard].reserved_zero = 0
234         p[WireguardInitiation].sender_index = self.receiver_index
235
236         # some random data for the message
237         #  lifted from the noise protocol's wireguard example
238         now = datetime.datetime.now()
239         tai = struct.pack('!qi', 4611686018427387914 + int(now.timestamp()),
240                           int(now.microsecond * 1e3))
241         b = self.noise.write_message(payload=tai)
242
243         # load noise into init message
244         p[WireguardInitiation].unencrypted_ephemeral = b[0:32]
245         p[WireguardInitiation].encrypted_static = b[32:80]
246         p[WireguardInitiation].encrypted_timestamp = b[80:108]
247
248         # generate the mac1 hash
249         mac_key = blake2s(b'mac1----' +
250                           self.itf.public_key_bytes()).digest()
251         p[WireguardInitiation].mac1 = blake2s(bytes(p)[0:116],
252                                               digest_size=16,
253                                               key=mac_key).digest()
254         p[WireguardInitiation].mac2 = bytearray(16)
255
256         p = (self.mk_tunnel_header(tx_itf, is_ip6) / p)
257
258         return p
259
260     def verify_header(self, p, is_ip6=False):
261         if is_ip6 is False:
262             self._test.assertEqual(p[IP].src, self.itf.src)
263             self._test.assertEqual(p[IP].dst, self.endpoint)
264         else:
265             self._test.assertEqual(p[IPv6].src, self.itf.src)
266             self._test.assertEqual(p[IPv6].dst, self.endpoint)
267         self._test.assertEqual(p[UDP].sport, self.itf.port)
268         self._test.assertEqual(p[UDP].dport, self.port)
269         self._test.assert_packet_checksums_valid(p)
270
271     def consume_init(self, p, tx_itf, is_ip6=False):
272         self.noise.set_as_responder()
273         self.noise_init(self.itf.public_key)
274         self.verify_header(p, is_ip6)
275
276         init = Wireguard(p[Raw])
277
278         self._test.assertEqual(init[Wireguard].message_type, 1)
279         self._test.assertEqual(init[Wireguard].reserved_zero, 0)
280
281         self.sender = init[WireguardInitiation].sender_index
282
283         # validate the hash
284         mac_key = blake2s(b'mac1----' +
285                           public_key_bytes(self.public_key)).digest()
286         mac1 = blake2s(bytes(init)[0:-32],
287                        digest_size=16,
288                        key=mac_key).digest()
289         self._test.assertEqual(init[WireguardInitiation].mac1, mac1)
290
291         # this passes only unencrypted_ephemeral, encrypted_static,
292         # encrypted_timestamp fields of the init
293         payload = self.noise.read_message(bytes(init)[8:-32])
294
295         # build the response
296         b = self.noise.write_message()
297         mac_key = blake2s(b'mac1----' +
298                           public_key_bytes(self.itf.public_key)).digest()
299         resp = (Wireguard(message_type=2, reserved_zero=0) /
300                 WireguardResponse(sender_index=self.receiver_index,
301                                   receiver_index=self.sender,
302                                   unencrypted_ephemeral=b[0:32],
303                                   encrypted_nothing=b[32:]))
304         mac1 = blake2s(bytes(resp)[:-32],
305                        digest_size=16,
306                        key=mac_key).digest()
307         resp[WireguardResponse].mac1 = mac1
308
309         resp = (self.mk_tunnel_header(tx_itf, is_ip6) / resp)
310         self._test.assertTrue(self.noise.handshake_finished)
311
312         return resp
313
314     def consume_response(self, p, is_ip6=False):
315         self.verify_header(p, is_ip6)
316
317         resp = Wireguard(p[Raw])
318
319         self._test.assertEqual(resp[Wireguard].message_type, 2)
320         self._test.assertEqual(resp[Wireguard].reserved_zero, 0)
321         self._test.assertEqual(resp[WireguardResponse].receiver_index,
322                                self.receiver_index)
323
324         self.sender = resp[Wireguard].sender_index
325
326         payload = self.noise.read_message(bytes(resp)[12:60])
327         self._test.assertEqual(payload, b'')
328         self._test.assertTrue(self.noise.handshake_finished)
329
330     def decrypt_transport(self, p, is_ip6=False):
331         self.verify_header(p, is_ip6)
332
333         p = Wireguard(p[Raw])
334         self._test.assertEqual(p[Wireguard].message_type, 4)
335         self._test.assertEqual(p[Wireguard].reserved_zero, 0)
336         self._test.assertEqual(p[WireguardTransport].receiver_index,
337                                self.receiver_index)
338
339         d = self.noise.decrypt(
340             p[WireguardTransport].encrypted_encapsulated_packet)
341         return d
342
343     def encrypt_transport(self, p):
344         return self.noise.encrypt(bytes(p))
345
346     def validate_encapped(self, rxs, tx, is_ip6=False):
347         for rx in rxs:
348             if is_ip6 is False:
349                 rx = IP(self.decrypt_transport(rx))
350
351                 # chech the oringial packet is present
352                 self._test.assertEqual(rx[IP].dst, tx[IP].dst)
353                 self._test.assertEqual(rx[IP].ttl, tx[IP].ttl-1)
354             else:
355                 rx = IPv6(self.decrypt_transport(rx))
356
357                 # chech the oringial packet is present
358                 self._test.assertEqual(rx[IPv6].dst, tx[IPv6].dst)
359                 self._test.assertEqual(rx[IPv6].ttl, tx[IPv6].ttl-1)
360
361     def want_events(self):
362         self._test.vapi.want_wireguard_peer_events(
363             enable_disable=1,
364             pid=os.getpid(),
365             peer_index=self.index,
366             sw_if_index=self.itf.sw_if_index)
367
368     def wait_event(self, expect, timeout=5):
369         rv = self._test.vapi.wait_for_event(timeout, "wireguard_peer_event")
370         self._test.assertEqual(rv.flags, expect)
371         self._test.assertEqual(rv.peer_index, self.index)
372
373
374 class TestWg(VppTestCase):
375     """ Wireguard Test Case """
376
377     error_str = compile(r"Error")
378
379     wg4_output_node_name = '/err/wg4-output-tun/'
380     wg4_input_node_name = '/err/wg4-input/'
381     wg6_output_node_name = '/err/wg6-output-tun/'
382     wg6_input_node_name = '/err/wg6-input/'
383     kp4_error = wg4_output_node_name + "Keypair error"
384     mac4_error = wg4_input_node_name + "Invalid MAC handshake"
385     peer4_error = wg4_input_node_name + "Peer error"
386     kp6_error = wg6_output_node_name + "Keypair error"
387     mac6_error = wg6_input_node_name + "Invalid MAC handshake"
388     peer6_error = wg6_input_node_name + "Peer error"
389
390     @classmethod
391     def setUpClass(cls):
392         super(TestWg, cls).setUpClass()
393         try:
394             cls.create_pg_interfaces(range(3))
395             for i in cls.pg_interfaces:
396                 i.admin_up()
397                 i.config_ip4()
398                 i.config_ip6()
399                 i.resolve_arp()
400                 i.resolve_ndp()
401
402         except Exception:
403             super(TestWg, cls).tearDownClass()
404             raise
405
406     @classmethod
407     def tearDownClass(cls):
408         super(TestWg, cls).tearDownClass()
409
410     def setUp(self):
411         super(VppTestCase, self).setUp()
412         self.base_kp4_err = self.statistics.get_err_counter(self.kp4_error)
413         self.base_mac4_err = self.statistics.get_err_counter(self.mac4_error)
414         self.base_peer4_err = self.statistics.get_err_counter(self.peer4_error)
415         self.base_kp6_err = self.statistics.get_err_counter(self.kp6_error)
416         self.base_mac6_err = self.statistics.get_err_counter(self.mac6_error)
417         self.base_peer6_err = self.statistics.get_err_counter(self.peer6_error)
418
419     def test_wg_interface(self):
420         """ Simple interface creation """
421         port = 12312
422
423         # Create interface
424         wg0 = VppWgInterface(self,
425                              self.pg1.local_ip4,
426                              port).add_vpp_config()
427
428         self.logger.info(self.vapi.cli("sh int"))
429
430         # delete interface
431         wg0.remove_vpp_config()
432
433     def test_handshake_hash(self):
434         """ test hashing an init message """
435         # a init packet generated by linux given the key below
436         h = "0100000098b9032b" \
437             "55cc4b39e73c3d24" \
438             "a2a1ab884b524a81" \
439             "1808bb86640fb70d" \
440             "e93154fec1879125" \
441             "ab012624a27f0b75" \
442             "c0a2582f438ddb5f" \
443             "8e768af40b4ab444" \
444             "02f9ff473e1b797e" \
445             "80d39d93c5480c82" \
446             "a3d4510f70396976" \
447             "586fb67300a5167b" \
448             "ae6ca3ff3dfd00eb" \
449             "59be198810f5aa03" \
450             "6abc243d2155ee4f" \
451             "2336483900aef801" \
452             "08752cd700000000" \
453             "0000000000000000" \
454             "00000000"
455
456         b = bytearray.fromhex(h)
457         tgt = Wireguard(b)
458
459         pubb = base64.b64decode("aRuHFTTxICIQNefp05oKWlJv3zgKxb8+WW7JJMh0jyM=")
460         pub = X25519PublicKey.from_public_bytes(pubb)
461
462         self.assertEqual(pubb, public_key_bytes(pub))
463
464         # strip the macs and build a new packet
465         init = b[0:-32]
466         mac_key = blake2s(b'mac1----' + public_key_bytes(pub)).digest()
467         init += blake2s(init,
468                         digest_size=16,
469                         key=mac_key).digest()
470         init += b'\x00' * 16
471
472         act = Wireguard(init)
473
474         self.assertEqual(tgt, act)
475
476     def test_wg_peer_resp(self):
477         """ Send handshake response """
478         port = 12323
479
480         # Create interfaces
481         wg0 = VppWgInterface(self,
482                              self.pg1.local_ip4,
483                              port).add_vpp_config()
484         wg0.admin_up()
485         wg0.config_ip4()
486
487         self.pg_enable_capture(self.pg_interfaces)
488         self.pg_start()
489
490         peer_1 = VppWgPeer(self,
491                            wg0,
492                            self.pg1.remote_ip4,
493                            port+1,
494                            ["10.11.3.0/24"]).add_vpp_config()
495         self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
496
497         r1 = VppIpRoute(self, "10.11.3.0", 24,
498                         [VppRoutePath("10.11.3.1",
499                                       wg0.sw_if_index)]).add_vpp_config()
500
501         # wait for the peer to send a handshake
502         rx = self.pg1.get_capture(1, timeout=2)
503
504         # consume the handshake in the noise protocol and
505         # generate the response
506         resp = peer_1.consume_init(rx[0], self.pg1)
507
508         # send the response, get keepalive
509         rxs = self.send_and_expect(self.pg1, [resp], self.pg1)
510
511         for rx in rxs:
512             b = peer_1.decrypt_transport(rx)
513             self.assertEqual(0, len(b))
514
515         # send a packets that are routed into the tunnel
516         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
517              IP(src=self.pg0.remote_ip4, dst="10.11.3.2") /
518              UDP(sport=555, dport=556) /
519              Raw(b'\x00' * 80))
520
521         rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
522
523         peer_1.validate_encapped(rxs, p)
524
525         # send packets into the tunnel, expect to receive them on
526         # the other side
527         p = [(peer_1.mk_tunnel_header(self.pg1) /
528               Wireguard(message_type=4, reserved_zero=0) /
529               WireguardTransport(
530                   receiver_index=peer_1.sender,
531                   counter=ii,
532                   encrypted_encapsulated_packet=peer_1.encrypt_transport(
533                       (IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20) /
534                        UDP(sport=222, dport=223) /
535                        Raw())))) for ii in range(255)]
536
537         rxs = self.send_and_expect(self.pg1, p, self.pg0)
538
539         for rx in rxs:
540             self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
541             self.assertEqual(rx[IP].ttl, 19)
542
543         r1.remove_vpp_config()
544         peer_1.remove_vpp_config()
545         wg0.remove_vpp_config()
546
547     def test_wg_peer_v4o4(self):
548         """ Test v4o4"""
549
550         port = 12333
551
552         # Create interfaces
553         wg0 = VppWgInterface(self,
554                              self.pg1.local_ip4,
555                              port).add_vpp_config()
556         wg0.admin_up()
557         wg0.config_ip4()
558
559         peer_1 = VppWgPeer(self,
560                            wg0,
561                            self.pg1.remote_ip4,
562                            port+1,
563                            ["10.11.3.0/24"]).add_vpp_config()
564         self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
565
566         r1 = VppIpRoute(self, "10.11.3.0", 24,
567                         [VppRoutePath("10.11.3.1",
568                                       wg0.sw_if_index)]).add_vpp_config()
569
570         # route a packet into the wg interface
571         #  use the allowed-ip prefix
572         #  this is dropped because the peer is not initiated
573         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
574              IP(src=self.pg0.remote_ip4, dst="10.11.3.2") /
575              UDP(sport=555, dport=556) /
576              Raw())
577         self.send_and_assert_no_replies(self.pg0, [p])
578         self.assertEqual(self.base_kp4_err + 1,
579                          self.statistics.get_err_counter(self.kp4_error))
580
581         # send a handsake from the peer with an invalid MAC
582         p = peer_1.mk_handshake(self.pg1)
583         p[WireguardInitiation].mac1 = b'foobar'
584         self.send_and_assert_no_replies(self.pg1, [p])
585         self.assertEqual(self.base_mac4_err + 1,
586                          self.statistics.get_err_counter(self.mac4_error))
587
588         # send a handsake from the peer but signed by the wrong key.
589         p = peer_1.mk_handshake(self.pg1,
590                                 False,
591                                 X25519PrivateKey.generate().public_key())
592         self.send_and_assert_no_replies(self.pg1, [p])
593         self.assertEqual(self.base_peer4_err + 1,
594                          self.statistics.get_err_counter(self.peer4_error))
595
596         # send a valid handsake init for which we expect a response
597         p = peer_1.mk_handshake(self.pg1)
598
599         rx = self.send_and_expect(self.pg1, [p], self.pg1)
600
601         peer_1.consume_response(rx[0])
602
603         # route a packet into the wg interface
604         #  this is dropped because the peer is still not initiated
605         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
606              IP(src=self.pg0.remote_ip4, dst="10.11.3.2") /
607              UDP(sport=555, dport=556) /
608              Raw())
609         self.send_and_assert_no_replies(self.pg0, [p])
610         self.assertEqual(self.base_kp4_err + 2,
611                          self.statistics.get_err_counter(self.kp4_error))
612
613         # send a data packet from the peer through the tunnel
614         # this completes the handshake
615         p = (IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20) /
616              UDP(sport=222, dport=223) /
617              Raw())
618         d = peer_1.encrypt_transport(p)
619         p = (peer_1.mk_tunnel_header(self.pg1) /
620              (Wireguard(message_type=4, reserved_zero=0) /
621               WireguardTransport(receiver_index=peer_1.sender,
622                                  counter=0,
623                                  encrypted_encapsulated_packet=d)))
624         rxs = self.send_and_expect(self.pg1, [p], self.pg0)
625
626         for rx in rxs:
627             self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
628             self.assertEqual(rx[IP].ttl, 19)
629
630         # send a packets that are routed into the tunnel
631         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
632              IP(src=self.pg0.remote_ip4, dst="10.11.3.2") /
633              UDP(sport=555, dport=556) /
634              Raw(b'\x00' * 80))
635
636         rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
637
638         for rx in rxs:
639             rx = IP(peer_1.decrypt_transport(rx))
640
641             # chech the oringial packet is present
642             self.assertEqual(rx[IP].dst, p[IP].dst)
643             self.assertEqual(rx[IP].ttl, p[IP].ttl-1)
644
645         # send packets into the tunnel, expect to receive them on
646         # the other side
647         p = [(peer_1.mk_tunnel_header(self.pg1) /
648               Wireguard(message_type=4, reserved_zero=0) /
649               WireguardTransport(
650                   receiver_index=peer_1.sender,
651                   counter=ii+1,
652                   encrypted_encapsulated_packet=peer_1.encrypt_transport(
653                       (IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20) /
654                        UDP(sport=222, dport=223) /
655                        Raw())))) for ii in range(255)]
656
657         rxs = self.send_and_expect(self.pg1, p, self.pg0)
658
659         for rx in rxs:
660             self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
661             self.assertEqual(rx[IP].ttl, 19)
662
663         r1.remove_vpp_config()
664         peer_1.remove_vpp_config()
665         wg0.remove_vpp_config()
666
667     def test_wg_peer_v6o6(self):
668         """ Test v6o6"""
669
670         port = 12343
671
672         # Create interfaces
673         wg0 = VppWgInterface(self,
674                              self.pg1.local_ip6,
675                              port).add_vpp_config()
676         wg0.admin_up()
677         wg0.config_ip6()
678
679         peer_1 = VppWgPeer(self,
680                            wg0,
681                            self.pg1.remote_ip6,
682                            port+1,
683                            ["1::3:0/112"]).add_vpp_config(True)
684         self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
685
686         r1 = VppIpRoute(self, "1::3:0", 112,
687                         [VppRoutePath("1::3:1",
688                                       wg0.sw_if_index)]).add_vpp_config()
689
690         # route a packet into the wg interface
691         #  use the allowed-ip prefix
692         #  this is dropped because the peer is not initiated
693
694         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
695              IPv6(src=self.pg0.remote_ip6, dst="1::3:2") /
696              UDP(sport=555, dport=556) /
697              Raw())
698         self.send_and_assert_no_replies(self.pg0, [p])
699
700         self.assertEqual(self.base_kp6_err + 1,
701                          self.statistics.get_err_counter(self.kp6_error))
702
703         # send a handsake from the peer with an invalid MAC
704         p = peer_1.mk_handshake(self.pg1, True)
705         p[WireguardInitiation].mac1 = b'foobar'
706         self.send_and_assert_no_replies(self.pg1, [p])
707
708         self.assertEqual(self.base_mac6_err + 1,
709                          self.statistics.get_err_counter(self.mac6_error))
710
711         # send a handsake from the peer but signed by the wrong key.
712         p = peer_1.mk_handshake(self.pg1,
713                                 True,
714                                 X25519PrivateKey.generate().public_key())
715         self.send_and_assert_no_replies(self.pg1, [p])
716         self.assertEqual(self.base_peer6_err + 1,
717                          self.statistics.get_err_counter(self.peer6_error))
718
719         # send a valid handsake init for which we expect a response
720         p = peer_1.mk_handshake(self.pg1, True)
721
722         rx = self.send_and_expect(self.pg1, [p], self.pg1)
723
724         peer_1.consume_response(rx[0], True)
725
726         # route a packet into the wg interface
727         #  this is dropped because the peer is still not initiated
728         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
729              IPv6(src=self.pg0.remote_ip6, dst="1::3:2") /
730              UDP(sport=555, dport=556) /
731              Raw())
732         self.send_and_assert_no_replies(self.pg0, [p])
733         self.assertEqual(self.base_kp6_err + 2,
734                          self.statistics.get_err_counter(self.kp6_error))
735
736         # send a data packet from the peer through the tunnel
737         # this completes the handshake
738         p = (IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20) /
739              UDP(sport=222, dport=223) /
740              Raw())
741         d = peer_1.encrypt_transport(p)
742         p = (peer_1.mk_tunnel_header(self.pg1, True) /
743              (Wireguard(message_type=4, reserved_zero=0) /
744               WireguardTransport(receiver_index=peer_1.sender,
745                                  counter=0,
746                                  encrypted_encapsulated_packet=d)))
747         rxs = self.send_and_expect(self.pg1, [p], self.pg0)
748
749         for rx in rxs:
750             self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
751             self.assertEqual(rx[IPv6].hlim, 19)
752
753         # send a packets that are routed into the tunnel
754         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
755              IPv6(src=self.pg0.remote_ip6, dst="1::3:2") /
756              UDP(sport=555, dport=556) /
757              Raw(b'\x00' * 80))
758
759         rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
760
761         for rx in rxs:
762             rx = IPv6(peer_1.decrypt_transport(rx, True))
763
764             # chech the oringial packet is present
765             self.assertEqual(rx[IPv6].dst, p[IPv6].dst)
766             self.assertEqual(rx[IPv6].hlim, p[IPv6].hlim-1)
767
768         # send packets into the tunnel, expect to receive them on
769         # the other side
770         p = [(peer_1.mk_tunnel_header(self.pg1, True) /
771               Wireguard(message_type=4, reserved_zero=0) /
772               WireguardTransport(
773                   receiver_index=peer_1.sender,
774                   counter=ii+1,
775                   encrypted_encapsulated_packet=peer_1.encrypt_transport(
776                       (IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20) /
777                        UDP(sport=222, dport=223) /
778                        Raw())))) for ii in range(255)]
779
780         rxs = self.send_and_expect(self.pg1, p, self.pg0)
781
782         for rx in rxs:
783             self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
784             self.assertEqual(rx[IPv6].hlim, 19)
785
786         r1.remove_vpp_config()
787         peer_1.remove_vpp_config()
788         wg0.remove_vpp_config()
789
790     def test_wg_peer_v6o4(self):
791         """ Test v6o4"""
792
793         port = 12353
794
795         # Create interfaces
796         wg0 = VppWgInterface(self,
797                              self.pg1.local_ip4,
798                              port).add_vpp_config()
799         wg0.admin_up()
800         wg0.config_ip6()
801
802         peer_1 = VppWgPeer(self,
803                            wg0,
804                            self.pg1.remote_ip4,
805                            port+1,
806                            ["1::3:0/112"]).add_vpp_config(True)
807         self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
808
809         r1 = VppIpRoute(self, "1::3:0", 112,
810                         [VppRoutePath("1::3:1",
811                                       wg0.sw_if_index)]).add_vpp_config()
812
813         # route a packet into the wg interface
814         #  use the allowed-ip prefix
815         #  this is dropped because the peer is not initiated
816         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
817              IPv6(src=self.pg0.remote_ip6, dst="1::3:2") /
818              UDP(sport=555, dport=556) /
819              Raw())
820         self.send_and_assert_no_replies(self.pg0, [p])
821         self.assertEqual(self.base_kp6_err + 1,
822                          self.statistics.get_err_counter(self.kp6_error))
823
824         # send a handsake from the peer with an invalid MAC
825         p = peer_1.mk_handshake(self.pg1)
826         p[WireguardInitiation].mac1 = b'foobar'
827         self.send_and_assert_no_replies(self.pg1, [p])
828
829         self.assertEqual(self.base_mac4_err + 1,
830                          self.statistics.get_err_counter(self.mac4_error))
831
832         # send a handsake from the peer but signed by the wrong key.
833         p = peer_1.mk_handshake(self.pg1,
834                                 False,
835                                 X25519PrivateKey.generate().public_key())
836         self.send_and_assert_no_replies(self.pg1, [p])
837         self.assertEqual(self.base_peer4_err + 1,
838                          self.statistics.get_err_counter(self.peer4_error))
839
840         # send a valid handsake init for which we expect a response
841         p = peer_1.mk_handshake(self.pg1)
842
843         rx = self.send_and_expect(self.pg1, [p], self.pg1)
844
845         peer_1.consume_response(rx[0])
846
847         # route a packet into the wg interface
848         #  this is dropped because the peer is still not initiated
849         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
850              IPv6(src=self.pg0.remote_ip6, dst="1::3:2") /
851              UDP(sport=555, dport=556) /
852              Raw())
853         self.send_and_assert_no_replies(self.pg0, [p])
854         self.assertEqual(self.base_kp6_err + 2,
855                          self.statistics.get_err_counter(self.kp6_error))
856
857         # send a data packet from the peer through the tunnel
858         # this completes the handshake
859         p = (IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20) /
860              UDP(sport=222, dport=223) /
861              Raw())
862         d = peer_1.encrypt_transport(p)
863         p = (peer_1.mk_tunnel_header(self.pg1) /
864              (Wireguard(message_type=4, reserved_zero=0) /
865               WireguardTransport(receiver_index=peer_1.sender,
866                                  counter=0,
867                                  encrypted_encapsulated_packet=d)))
868         rxs = self.send_and_expect(self.pg1, [p], self.pg0)
869
870         for rx in rxs:
871             self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
872             self.assertEqual(rx[IPv6].hlim, 19)
873
874         # send a packets that are routed into the tunnel
875         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
876              IPv6(src=self.pg0.remote_ip6, dst="1::3:2") /
877              UDP(sport=555, dport=556) /
878              Raw(b'\x00' * 80))
879
880         rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
881
882         for rx in rxs:
883             rx = IPv6(peer_1.decrypt_transport(rx))
884
885             # chech the oringial packet is present
886             self.assertEqual(rx[IPv6].dst, p[IPv6].dst)
887             self.assertEqual(rx[IPv6].hlim, p[IPv6].hlim-1)
888
889         # send packets into the tunnel, expect to receive them on
890         # the other side
891         p = [(peer_1.mk_tunnel_header(self.pg1) /
892               Wireguard(message_type=4, reserved_zero=0) /
893               WireguardTransport(
894                   receiver_index=peer_1.sender,
895                   counter=ii+1,
896                   encrypted_encapsulated_packet=peer_1.encrypt_transport(
897                       (IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20) /
898                        UDP(sport=222, dport=223) /
899                        Raw())))) for ii in range(255)]
900
901         rxs = self.send_and_expect(self.pg1, p, self.pg0)
902
903         for rx in rxs:
904             self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6)
905             self.assertEqual(rx[IPv6].hlim, 19)
906
907         r1.remove_vpp_config()
908         peer_1.remove_vpp_config()
909         wg0.remove_vpp_config()
910
911     def test_wg_peer_v4o6(self):
912         """ Test v4o6"""
913
914         port = 12363
915
916         # Create interfaces
917         wg0 = VppWgInterface(self,
918                              self.pg1.local_ip6,
919                              port).add_vpp_config()
920         wg0.admin_up()
921         wg0.config_ip4()
922
923         peer_1 = VppWgPeer(self,
924                            wg0,
925                            self.pg1.remote_ip6,
926                            port+1,
927                            ["10.11.3.0/24"]).add_vpp_config()
928         self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
929
930         r1 = VppIpRoute(self, "10.11.3.0", 24,
931                         [VppRoutePath("10.11.3.1",
932                                       wg0.sw_if_index)]).add_vpp_config()
933
934         # route a packet into the wg interface
935         #  use the allowed-ip prefix
936         #  this is dropped because the peer is not initiated
937         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
938              IP(src=self.pg0.remote_ip4, dst="10.11.3.2") /
939              UDP(sport=555, dport=556) /
940              Raw())
941         self.send_and_assert_no_replies(self.pg0, [p])
942         self.assertEqual(self.base_kp4_err + 1,
943                          self.statistics.get_err_counter(self.kp4_error))
944
945         # send a handsake from the peer with an invalid MAC
946         p = peer_1.mk_handshake(self.pg1, True)
947         p[WireguardInitiation].mac1 = b'foobar'
948         self.send_and_assert_no_replies(self.pg1, [p])
949         self.assertEqual(self.base_mac6_err + 1,
950                          self.statistics.get_err_counter(self.mac6_error))
951
952         # send a handsake from the peer but signed by the wrong key.
953         p = peer_1.mk_handshake(self.pg1,
954                                 True,
955                                 X25519PrivateKey.generate().public_key())
956         self.send_and_assert_no_replies(self.pg1, [p])
957         self.assertEqual(self.base_peer6_err + 1,
958                          self.statistics.get_err_counter(self.peer6_error))
959
960         # send a valid handsake init for which we expect a response
961         p = peer_1.mk_handshake(self.pg1, True)
962
963         rx = self.send_and_expect(self.pg1, [p], self.pg1)
964
965         peer_1.consume_response(rx[0], True)
966
967         # route a packet into the wg interface
968         #  this is dropped because the peer is still not initiated
969         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
970              IP(src=self.pg0.remote_ip4, dst="10.11.3.2") /
971              UDP(sport=555, dport=556) /
972              Raw())
973         self.send_and_assert_no_replies(self.pg0, [p])
974         self.assertEqual(self.base_kp4_err + 2,
975                          self.statistics.get_err_counter(self.kp4_error))
976
977         # send a data packet from the peer through the tunnel
978         # this completes the handshake
979         p = (IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20) /
980              UDP(sport=222, dport=223) /
981              Raw())
982         d = peer_1.encrypt_transport(p)
983         p = (peer_1.mk_tunnel_header(self.pg1, True) /
984              (Wireguard(message_type=4, reserved_zero=0) /
985               WireguardTransport(receiver_index=peer_1.sender,
986                                  counter=0,
987                                  encrypted_encapsulated_packet=d)))
988         rxs = self.send_and_expect(self.pg1, [p], self.pg0)
989
990         for rx in rxs:
991             self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
992             self.assertEqual(rx[IP].ttl, 19)
993
994         # send a packets that are routed into the tunnel
995         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
996              IP(src=self.pg0.remote_ip4, dst="10.11.3.2") /
997              UDP(sport=555, dport=556) /
998              Raw(b'\x00' * 80))
999
1000         rxs = self.send_and_expect(self.pg0, p * 255, self.pg1)
1001
1002         for rx in rxs:
1003             rx = IP(peer_1.decrypt_transport(rx, True))
1004
1005             # chech the oringial packet is present
1006             self.assertEqual(rx[IP].dst, p[IP].dst)
1007             self.assertEqual(rx[IP].ttl, p[IP].ttl-1)
1008
1009         # send packets into the tunnel, expect to receive them on
1010         # the other side
1011         p = [(peer_1.mk_tunnel_header(self.pg1, True) /
1012               Wireguard(message_type=4, reserved_zero=0) /
1013               WireguardTransport(
1014                   receiver_index=peer_1.sender,
1015                   counter=ii+1,
1016                   encrypted_encapsulated_packet=peer_1.encrypt_transport(
1017                       (IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20) /
1018                        UDP(sport=222, dport=223) /
1019                        Raw())))) for ii in range(255)]
1020
1021         rxs = self.send_and_expect(self.pg1, p, self.pg0)
1022
1023         for rx in rxs:
1024             self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1025             self.assertEqual(rx[IP].ttl, 19)
1026
1027         r1.remove_vpp_config()
1028         peer_1.remove_vpp_config()
1029         wg0.remove_vpp_config()
1030
1031     def test_wg_multi_peer(self):
1032         """ multiple peer setup """
1033         port = 12373
1034
1035         # Create interfaces
1036         wg0 = VppWgInterface(self,
1037                              self.pg1.local_ip4,
1038                              port).add_vpp_config()
1039         wg1 = VppWgInterface(self,
1040                              self.pg2.local_ip4,
1041                              port+1).add_vpp_config()
1042         wg0.admin_up()
1043         wg1.admin_up()
1044
1045         # Check peer counter
1046         self.assertEqual(len(self.vapi.wireguard_peers_dump()), 0)
1047
1048         self.pg_enable_capture(self.pg_interfaces)
1049         self.pg_start()
1050
1051         # Create many peers on sencond interface
1052         NUM_PEERS = 16
1053         self.pg2.generate_remote_hosts(NUM_PEERS)
1054         self.pg2.configure_ipv4_neighbors()
1055         self.pg1.generate_remote_hosts(NUM_PEERS)
1056         self.pg1.configure_ipv4_neighbors()
1057
1058         peers_1 = []
1059         peers_2 = []
1060         routes_1 = []
1061         routes_2 = []
1062         for i in range(NUM_PEERS):
1063             peers_1.append(VppWgPeer(self,
1064                                      wg0,
1065                                      self.pg1.remote_hosts[i].ip4,
1066                                      port+1+i,
1067                                      ["10.0.%d.4/32" % i]).add_vpp_config())
1068             routes_1.append(VppIpRoute(self, "10.0.%d.4" % i, 32,
1069                             [VppRoutePath(self.pg1.remote_hosts[i].ip4,
1070                                           wg0.sw_if_index)]).add_vpp_config())
1071
1072             peers_2.append(VppWgPeer(self,
1073                                      wg1,
1074                                      self.pg2.remote_hosts[i].ip4,
1075                                      port+100+i,
1076                                      ["10.100.%d.4/32" % i]).add_vpp_config())
1077             routes_2.append(VppIpRoute(self, "10.100.%d.4" % i, 32,
1078                             [VppRoutePath(self.pg2.remote_hosts[i].ip4,
1079                                           wg1.sw_if_index)]).add_vpp_config())
1080
1081         self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_PEERS*2)
1082
1083         self.logger.info(self.vapi.cli("show wireguard peer"))
1084         self.logger.info(self.vapi.cli("show wireguard interface"))
1085         self.logger.info(self.vapi.cli("show adj 37"))
1086         self.logger.info(self.vapi.cli("sh ip fib 172.16.3.17"))
1087         self.logger.info(self.vapi.cli("sh ip fib 10.11.3.0"))
1088
1089         # remove routes
1090         for r in routes_1:
1091             r.remove_vpp_config()
1092         for r in routes_2:
1093             r.remove_vpp_config()
1094
1095         # remove peers
1096         for p in peers_1:
1097             self.assertTrue(p.query_vpp_config())
1098             p.remove_vpp_config()
1099         for p in peers_2:
1100             self.assertTrue(p.query_vpp_config())
1101             p.remove_vpp_config()
1102
1103         wg0.remove_vpp_config()
1104         wg1.remove_vpp_config()
1105
1106     def test_wg_multi_interface(self):
1107         """ Multi-tunnel on the same port """
1108         port = 12500
1109
1110         # Create many wireguard interfaces
1111         NUM_IFS = 4
1112         self.pg1.generate_remote_hosts(NUM_IFS)
1113         self.pg1.configure_ipv4_neighbors()
1114         self.pg0.generate_remote_hosts(NUM_IFS)
1115         self.pg0.configure_ipv4_neighbors()
1116
1117         # Create interfaces with a peer on each
1118         peers = []
1119         routes = []
1120         wg_ifs = []
1121         for i in range(NUM_IFS):
1122             # Use the same port for each interface
1123             wg0 = VppWgInterface(self,
1124                                  self.pg1.local_ip4,
1125                                  port).add_vpp_config()
1126             wg0.admin_up()
1127             wg0.config_ip4()
1128             wg_ifs.append(wg0)
1129             peers.append(VppWgPeer(self,
1130                                    wg0,
1131                                    self.pg1.remote_hosts[i].ip4,
1132                                    port+1+i,
1133                                    ["10.0.%d.0/24" % i]).add_vpp_config())
1134
1135             routes.append(VppIpRoute(self, "10.0.%d.0" % i, 24,
1136                           [VppRoutePath("10.0.%d.4" % i,
1137                                         wg0.sw_if_index)]).add_vpp_config())
1138
1139         self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_IFS)
1140
1141         for i in range(NUM_IFS):
1142             # send a valid handsake init for which we expect a response
1143             p = peers[i].mk_handshake(self.pg1)
1144             rx = self.send_and_expect(self.pg1, [p], self.pg1)
1145             peers[i].consume_response(rx[0])
1146
1147             # send a data packet from the peer through the tunnel
1148             # this completes the handshake
1149             p = (IP(src="10.0.%d.4" % i,
1150                     dst=self.pg0.remote_hosts[i].ip4, ttl=20) /
1151                  UDP(sport=222, dport=223) /
1152                  Raw())
1153             d = peers[i].encrypt_transport(p)
1154             p = (peers[i].mk_tunnel_header(self.pg1) /
1155                  (Wireguard(message_type=4, reserved_zero=0) /
1156                   WireguardTransport(receiver_index=peers[i].sender,
1157                                      counter=0,
1158                                      encrypted_encapsulated_packet=d)))
1159             rxs = self.send_and_expect(self.pg1, [p], self.pg0)
1160             for rx in rxs:
1161                 self.assertEqual(rx[IP].dst, self.pg0.remote_hosts[i].ip4)
1162                 self.assertEqual(rx[IP].ttl, 19)
1163
1164         # send a packets that are routed into the tunnel
1165         for i in range(NUM_IFS):
1166             p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
1167                  IP(src=self.pg0.remote_hosts[i].ip4, dst="10.0.%d.4" % i) /
1168                  UDP(sport=555, dport=556) /
1169                  Raw(b'\x00' * 80))
1170
1171             rxs = self.send_and_expect(self.pg0, p * 64, self.pg1)
1172
1173             for rx in rxs:
1174                 rx = IP(peers[i].decrypt_transport(rx))
1175
1176                 # check the oringial packet is present
1177                 self.assertEqual(rx[IP].dst, p[IP].dst)
1178                 self.assertEqual(rx[IP].ttl, p[IP].ttl-1)
1179
1180         # send packets into the tunnel
1181         for i in range(NUM_IFS):
1182             p = [(peers[i].mk_tunnel_header(self.pg1) /
1183                   Wireguard(message_type=4, reserved_zero=0) /
1184                   WireguardTransport(
1185                       receiver_index=peers[i].sender,
1186                       counter=ii+1,
1187                       encrypted_encapsulated_packet=peers[i].encrypt_transport(
1188                           (IP(src="10.0.%d.4" % i,
1189                               dst=self.pg0.remote_hosts[i].ip4, ttl=20) /
1190                            UDP(sport=222, dport=223) /
1191                            Raw())))) for ii in range(64)]
1192
1193             rxs = self.send_and_expect(self.pg1, p, self.pg0)
1194
1195             for rx in rxs:
1196                 self.assertEqual(rx[IP].dst, self.pg0.remote_hosts[i].ip4)
1197                 self.assertEqual(rx[IP].ttl, 19)
1198
1199         for r in routes:
1200             r.remove_vpp_config()
1201         for p in peers:
1202             p.remove_vpp_config()
1203         for i in wg_ifs:
1204             i.remove_vpp_config()
1205
1206     def test_wg_event(self):
1207         """ Test events """
1208         port = 12600
1209         ESTABLISHED_FLAG = VppEnum.\
1210             vl_api_wireguard_peer_flags_t.\
1211             WIREGUARD_PEER_ESTABLISHED
1212         DEAD_FLAG = VppEnum.\
1213             vl_api_wireguard_peer_flags_t.\
1214             WIREGUARD_PEER_STATUS_DEAD
1215
1216         # Create interfaces
1217         wg0 = VppWgInterface(self,
1218                              self.pg1.local_ip4,
1219                              port).add_vpp_config()
1220         wg1 = VppWgInterface(self,
1221                              self.pg2.local_ip4,
1222                              port+1).add_vpp_config()
1223         wg0.admin_up()
1224         wg1.admin_up()
1225
1226         # Check peer counter
1227         self.assertEqual(len(self.vapi.wireguard_peers_dump()), 0)
1228
1229         self.pg_enable_capture(self.pg_interfaces)
1230         self.pg_start()
1231
1232         # Create peers
1233         NUM_PEERS = 2
1234         self.pg2.generate_remote_hosts(NUM_PEERS)
1235         self.pg2.configure_ipv4_neighbors()
1236         self.pg1.generate_remote_hosts(NUM_PEERS)
1237         self.pg1.configure_ipv4_neighbors()
1238
1239         peers_0 = []
1240         peers_1 = []
1241         routes_0 = []
1242         routes_1 = []
1243         for i in range(NUM_PEERS):
1244             peers_0.append(VppWgPeer(self,
1245                                      wg0,
1246                                      self.pg1.remote_hosts[i].ip4,
1247                                      port+1+i,
1248                                      ["10.0.%d.4/32" % i]).add_vpp_config())
1249             routes_0.append(VppIpRoute(self, "10.0.%d.4" % i, 32,
1250                             [VppRoutePath(self.pg1.remote_hosts[i].ip4,
1251                                           wg0.sw_if_index)]).add_vpp_config())
1252
1253             peers_1.append(VppWgPeer(self,
1254                                      wg1,
1255                                      self.pg2.remote_hosts[i].ip4,
1256                                      port+100+i,
1257                                      ["10.100.%d.4/32" % i]).add_vpp_config())
1258             routes_1.append(VppIpRoute(self, "10.100.%d.4" % i, 32,
1259                             [VppRoutePath(self.pg2.remote_hosts[i].ip4,
1260                                           wg1.sw_if_index)]).add_vpp_config())
1261
1262         self.assertEqual(len(self.vapi.wireguard_peers_dump()), NUM_PEERS*2)
1263
1264         # Want events from the first perr of wg0
1265         # and from all wg1 peers
1266         peers_0[0].want_events()
1267         wg1.want_events()
1268
1269         for i in range(NUM_PEERS):
1270             # send a valid handsake init for which we expect a response
1271             p = peers_0[i].mk_handshake(self.pg1)
1272             rx = self.send_and_expect(self.pg1, [p], self.pg1)
1273             peers_0[i].consume_response(rx[0])
1274             if (i == 0):
1275                 peers_0[0].wait_event(ESTABLISHED_FLAG)
1276
1277             p = peers_1[i].mk_handshake(self.pg2)
1278             rx = self.send_and_expect(self.pg2, [p], self.pg2)
1279             peers_1[i].consume_response(rx[0])
1280
1281         wg1.wait_events(
1282             ESTABLISHED_FLAG,
1283             [peers_1[0].index, peers_1[1].index])
1284
1285         # remove routes
1286         for r in routes_0:
1287             r.remove_vpp_config()
1288         for r in routes_1:
1289             r.remove_vpp_config()
1290
1291         # remove peers
1292         for i in range(NUM_PEERS):
1293             self.assertTrue(peers_0[i].query_vpp_config())
1294             peers_0[i].remove_vpp_config()
1295             if (i == 0):
1296                 peers_0[i].wait_event(0)
1297                 peers_0[i].wait_event(DEAD_FLAG)
1298         for p in peers_1:
1299             self.assertTrue(p.query_vpp_config())
1300             p.remove_vpp_config()
1301             p.wait_event(0)
1302             p.wait_event(DEAD_FLAG)
1303
1304         wg0.remove_vpp_config()
1305         wg1.remove_vpp_config()
1306
1307
1308 class WireguardHandoffTests(TestWg):
1309     """ Wireguard Tests in multi worker setup """
1310     vpp_worker_count = 2
1311
1312     def test_wg_peer_init(self):
1313         """ Handoff """
1314
1315         port = 12383
1316
1317         # Create interfaces
1318         wg0 = VppWgInterface(self,
1319                              self.pg1.local_ip4,
1320                              port).add_vpp_config()
1321         wg0.admin_up()
1322         wg0.config_ip4()
1323
1324         peer_1 = VppWgPeer(self,
1325                            wg0,
1326                            self.pg1.remote_ip4,
1327                            port+1,
1328                            ["10.11.2.0/24",
1329                             "10.11.3.0/24"]).add_vpp_config()
1330         self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
1331
1332         r1 = VppIpRoute(self, "10.11.3.0", 24,
1333                         [VppRoutePath("10.11.3.1",
1334                                       wg0.sw_if_index)]).add_vpp_config()
1335
1336         # send a valid handsake init for which we expect a response
1337         p = peer_1.mk_handshake(self.pg1)
1338
1339         rx = self.send_and_expect(self.pg1, [p], self.pg1)
1340
1341         peer_1.consume_response(rx[0])
1342
1343         # send a data packet from the peer through the tunnel
1344         # this completes the handshake and pins the peer to worker 0
1345         p = (IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20) /
1346              UDP(sport=222, dport=223) /
1347              Raw())
1348         d = peer_1.encrypt_transport(p)
1349         p = (peer_1.mk_tunnel_header(self.pg1) /
1350              (Wireguard(message_type=4, reserved_zero=0) /
1351               WireguardTransport(receiver_index=peer_1.sender,
1352                                  counter=0,
1353                                  encrypted_encapsulated_packet=d)))
1354         rxs = self.send_and_expect(self.pg1, [p], self.pg0,
1355                                    worker=0)
1356
1357         for rx in rxs:
1358             self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1359             self.assertEqual(rx[IP].ttl, 19)
1360
1361         # send a packets that are routed into the tunnel
1362         # and pins the peer tp worker 1
1363         pe = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
1364               IP(src=self.pg0.remote_ip4, dst="10.11.3.2") /
1365               UDP(sport=555, dport=556) /
1366               Raw(b'\x00' * 80))
1367         rxs = self.send_and_expect(self.pg0, pe * 255, self.pg1, worker=1)
1368         peer_1.validate_encapped(rxs, pe)
1369
1370         # send packets into the tunnel, from the other worker
1371         p = [(peer_1.mk_tunnel_header(self.pg1) /
1372               Wireguard(message_type=4, reserved_zero=0) /
1373               WireguardTransport(
1374                     receiver_index=peer_1.sender,
1375                     counter=ii+1,
1376                     encrypted_encapsulated_packet=peer_1.encrypt_transport(
1377                         (IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20) /
1378                          UDP(sport=222, dport=223) /
1379                          Raw())))) for ii in range(255)]
1380
1381         rxs = self.send_and_expect(self.pg1, p, self.pg0, worker=1)
1382
1383         for rx in rxs:
1384             self.assertEqual(rx[IP].dst, self.pg0.remote_ip4)
1385             self.assertEqual(rx[IP].ttl, 19)
1386
1387         # send a packets that are routed into the tunnel
1388         # from owrker 0
1389         rxs = self.send_and_expect(self.pg0, pe * 255, self.pg1, worker=0)
1390
1391         peer_1.validate_encapped(rxs, pe)
1392
1393         r1.remove_vpp_config()
1394         peer_1.remove_vpp_config()
1395         wg0.remove_vpp_config()
1396
1397     @unittest.skip("test disabled")
1398     def test_wg_multi_interface(self):
1399         """ Multi-tunnel on the same port """