Revert "fix(jobspec): Delete ipsec nfv density tests"
[csit.git] / resources / libraries / python / WireGuardUtil.py
1 # Copyright (c) 2022 Intel and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
5 #
6 #     http://www.apache.org/licenses/LICENSE-2.0
7 #
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
13
14 """WireGuard utilities library."""
15
16 from ipaddress import ip_address
17 from cryptography.hazmat.primitives.serialization import Encoding, \
18     PrivateFormat, PublicFormat, NoEncryption
19 from cryptography.hazmat.primitives.asymmetric.x25519 import \
20     X25519PrivateKey
21
22 from resources.libraries.python.InterfaceUtil import InterfaceUtil
23 from resources.libraries.python.IPUtil import IPUtil
24 from resources.libraries.python.PapiExecutor import PapiSocketExecutor
25
26 class WireGuardUtil:
27     """This class defines the methods to set WireGuard."""
28
29     @staticmethod
30     def public_key_bytes(k):
31         """Return the public key as byte.
32
33         :param k: Generated public key.
34         :type: x25519._X25519PublicKey object
35         :returns: Public key.
36         :rtype: bytes
37         """
38         return k.public_bytes(Encoding.Raw, PublicFormat.Raw)
39
40     @staticmethod
41     def private_key_bytes(k):
42         """Return the private key as byte.
43
44         :param k: Generated private key.
45         :type: x25519._X25519PrivateKey object
46         :returns: Private key.
47         :rtype: bytes
48         """
49         return k.private_bytes(Encoding.Raw, PrivateFormat.Raw, NoEncryption())
50
51     @staticmethod
52     def generate_wireguard_privatekey_and_pubkey():
53         """Generate a pair of WireGuard Private key and Public key.
54
55         :returns: A pair of privatekey and publickey
56         :rtype: x25519._X25519PublicKey object
57         """
58         privatekey = X25519PrivateKey.generate()
59         pubkey = privatekey.public_key()
60         private_key = WireGuardUtil.private_key_bytes(privatekey)
61         public_key = WireGuardUtil.public_key_bytes(pubkey)
62         return private_key, public_key
63
64     @staticmethod
65     def vpp_wireguard_create_interface(
66             node, listen_port, wg_src, private_key):
67         """Create WireGuard interface.
68
69         :param node: VPP node to add config on.
70         :param listen_port: WireGuard interface listen port.
71         :param wg_src: WireGuard source IPv4.
72         :param private_key: WireGuard interface private key
73         :type node: dict
74         :type listen_port: int
75         :type wg_src: str
76         :type private_key: bytes
77         :returns: Wireguard interface sw_if_index.
78         :rtype: int
79         """
80         cmd = u"wireguard_interface_create"
81         err_msg = f"Failed to create wireguard interface" \
82             f"on host {node[u'host']}"
83         src_ip = ip_address(wg_src)
84         args = dict(
85             interface=dict(
86                 port=int(listen_port),
87                 src_ip=src_ip,
88                 private_key=private_key,
89                 generate_key=False
90             )
91         )
92         with PapiSocketExecutor(node) as papi_exec:
93             wg_sw_index = \
94                 papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
95             return wg_sw_index
96
97     @staticmethod
98     def vpp_wireguard_add_peer(
99             node, interface, peer_pubkey, endpoint_ip,
100             allowed_ips, n_allowed_ips, dst_port, keepalive_time):
101         """Add a peer for WireGuard interface.
102
103         :param node: VPP node to add config on.
104         :param interface: WireGuard interface sw_if_index.
105         :param peer_pubkey: Public key of wireguard interface peer.
106         :param endpoint_ip: Peer source IPv4.
107         :param allowed_ips: WireGuard interface allowed ips list.
108         :param n_allowed_ips: Number of allowed ips.
109         :param dst_port: WireGuard destination port.
110         :param keepaliva time: WireGuard persistent keepalive time.
111         :type node: dict
112         :type interface: int
113         :type peer_pubkey: bytes
114         :type endpoint_ip: str
115         :type allowed_ips: list
116         :type n_allowed_ips: int
117         :type dst_port: int
118         :type keepalive_time: int
119         """
120         endpoint_ip = ip_address(endpoint_ip)
121         cmd = u"wireguard_peer_add"
122         err_msg = f"Failed to add peer of wireguard interface" \
123             f"{interface} on host {node[u'host']}"
124         args = dict(
125             peer=dict(
126                 public_key=peer_pubkey,
127                 port=int(dst_port),
128                 endpoint=endpoint_ip,
129                 sw_if_index=interface,
130                 persistent_keepalive=int(keepalive_time),
131                 n_allowed_ips=int(n_allowed_ips),
132                 allowed_ips=allowed_ips
133             )
134         )
135         with PapiSocketExecutor(node) as papi_exec:
136             papi_exec.add(cmd, **args).get_reply(err_msg)
137
138     @staticmethod
139     def vpp_wireguard_set_async_mode(node, async_enable=1):
140         """Set wireguard async mode on or off.
141
142         :param node: VPP node to set wireguard async mode.
143         :param async_enable: Async mode on or off.
144         :type node: dict
145         :type async_enable: int
146         """
147         cmd = u"wg_set_async_mode"
148         err_msg = f"Failed to set wireguard async mode on host {node[u'host']}"
149         args = dict(
150             async_enable=async_enable
151         )
152         with PapiSocketExecutor(node) as papi_exec:
153             papi_exec.add(cmd, **args).get_reply(err_msg)
154
155     @staticmethod
156     def _wireguard_create_tunnel_interface_on_dut(
157             node, if1_key, if2_mac_addr, src_ip, peer_endpoint_ip,
158             peer_allowed_ips, peer_n_allowed_ips, dut_wg_ip, port,
159             keepalive_time, dut_private_key, peer_pubkey):
160         """Create WireGuard tunnel interface on one DUT node using PAPI.
161
162         :param node: VPP node as DUT to create tunnel interface.
163         :param if1_key: VPP node as DUT interface key from topology file.
164         :param if2_mac_addr: Vpp node on the other end/ TG node
165             (in case of 2-node topology) interface mac address.
166         :param src_ip: WireGuard source IPv4 address.
167         :param peer_endpoint_ip: Peer source IPv4 address.
168         :param peer_allowed_ips: WireGuard peer interface allowed ip list.
169         :param peer_n_allowed ips: Number of peer allowed ips.
170         :param dut_wg_ip: WireGuard interface ip address on DUT.
171         :param port: WireGuard interface listen port or
172             Peer interface destination port.
173         :param keepalive_time: WireGuard persistent keepalive time.
174         :param dut_private_key: WireGuard interface private key of DUT.
175         :param peer_pubkey: WireGuard Peer interface public key.
176         :type nodes: dict
177         :type if1_key: str
178         :type if2_mac_addr: str
179         :type src_ip: str
180         :type peer_endpoint_ip: str
181         :type peer_allowed_ips: list
182         :type peer_n_allowed_ips: int
183         :type dut_wg_ip: str
184         :type port: int
185         :type keepalive_time: int
186         :type dut_private_key: bytes
187         :type peer_pubkey: bytes
188         """
189         #Set IP address on VPP node interface
190         IPUtil.vpp_interface_set_ip_address(node, if1_key, src_ip, 24)
191         IPUtil.vpp_add_ip_neighbor(
192             node, if1_key, peer_endpoint_ip, if2_mac_addr
193         )
194         #Create Wireguard interface on DUT
195         dut_wg_sw_index = WireGuardUtil.vpp_wireguard_create_interface(
196             node, port, src_ip, dut_private_key
197         )
198         #Add wireguard peer
199         WireGuardUtil.vpp_wireguard_add_peer(
200             node, dut_wg_sw_index, peer_pubkey, peer_endpoint_ip,
201             peer_allowed_ips, peer_n_allowed_ips, port, keepalive_time
202         )
203         #Set wireguard interface up
204         InterfaceUtil.set_interface_state(node, dut_wg_sw_index, state=u'up')
205         #Set wireguard interface IP address
206         cmd = u'sw_interface_add_del_address'
207         args = dict(
208             sw_if_index=dut_wg_sw_index,
209             is_add=True,
210             del_all=False,
211             prefix=IPUtil.create_prefix_object(ip_address(dut_wg_ip), 24)
212         )
213         err_msg = f"Failed to set IP address on wg interface " \
214             f"on host {node[u'host']}"
215         with PapiSocketExecutor(node) as papi_exec:
216             papi_exec.add(cmd, **args).get_reply(err_msg)
217         #Set route on VPP node as DUT wg interface
218         for allowed_ip in peer_allowed_ips:
219             traffic_addr = ip_address(
220                 allowed_ip[u'address'][u'un'][u'ip4']
221             )
222             prefix_len = allowed_ip[u'len']
223             IPUtil.vpp_route_add(
224                 node, traffic_addr, prefix_len,
225                 gateway=(traffic_addr+1).compressed,
226                 interface=dut_wg_sw_index
227             )
228
229     @staticmethod
230     def vpp_wireguard_create_tunnel_interfaces_on_duts(
231             nodes, if1_key, if2_key, if1_ip_addr, if2_ip_addr,
232             if1_mac_addr, if2_mac_addr, wg_if1_ip_addr, wg_if2_ip_addr,
233             n_tunnels, port, keepalive_time, raddr_ip1, raddr_ip2):
234         """Create WireGuard tunnel interfaces between two VPP nodes.
235
236         :param nodes: VPP nodes to create tunnel interfaces.
237         :param if1_key: VPP node 1 interface key from topology file.
238         :param if2_key: VPP node 2 / TG node (in case of 2-node topology)
239         :param if1_ip_addr: VPP node 1 interface IPv4/IPv6 address.
240         :param if2_ip_addr: VPP node 2 / TG node
241             (in case of 2-node topology) interface IPv4/IPv6 address.
242         :param if1_mac_addr: VPP node1 interface mac address.
243         :param if2_mac_addr: VPP node2 interface mac address.
244         :param wg_if1_ip_addr: VPP node 1 WireGuard interface IPv4 address.
245         :param wg_if2_ip_addr: VPP node 2 WireGuard interface IPv4 address.
246         :param n_tunnels: Number of wireguard tunnels.
247         :param port: WireGuard interface listen port or
248             Peer interface destination port.
249         :param keepalive_time: WireGuard persistent keepalive time.
250         :param raddr_ip1: Policy selector remote IPv4/IPv6 start address
251             for the first tunnel in direction node1->node2.
252         :param raddr_ip2: Policy selector remote IPv4/IPv6 start address
253             for the first tunnel in direction node2->node1.
254         :type nodes: dict
255         :type if1_key: str
256         :type if2_key: str
257         :type if1_ip_addr: str
258         :type if2_ip_addr: str
259         :type if1_mac_addr: str
260         :type if2_mac_addr: str
261         :type wg_if1_ip_addr: str
262         :type wg_if2_ip_addr: str
263         :type n_tunnels: int
264         :type port: int
265         :type keepalive_time: int
266         :type raddr_ip1: str
267         :type raddr_ip2: str
268         """
269         for i in range(n_tunnels):
270             if1_ipaddr = str(ip_address(if1_ip_addr) + i*256)
271             if2_ipaddr = str(ip_address(if2_ip_addr) + i*256)
272             wg_if1_ipaddr = str(ip_address(wg_if1_ip_addr) + i*256)
273             wg_if2_ipaddr = str(ip_address(wg_if2_ip_addr) + i*256)
274
275             allowed_ipaddr1 = ip_address(raddr_ip1) + i*256
276             allowed_ipaddr2 = ip_address(raddr_ip2) + i*256
277             dut1_allowed_ips = \
278                 [IPUtil.create_prefix_object(allowed_ipaddr2, 24),]
279             dut2_allowed_ips = \
280                 [IPUtil.create_prefix_object(allowed_ipaddr1, 24),]
281
282             dut1_privatekey, dut1_pubkey = \
283                 WireGuardUtil.generate_wireguard_privatekey_and_pubkey()
284             dut2_privatekey, dut2_pubkey = \
285                 WireGuardUtil.generate_wireguard_privatekey_and_pubkey()
286
287             #Configure WireGuard interface on DUT1
288             WireGuardUtil._wireguard_create_tunnel_interface_on_dut(
289                 nodes[u'DUT1'], if1_key, if2_mac_addr, if1_ipaddr, if2_ipaddr,
290                 dut1_allowed_ips, 1, wg_if1_ipaddr, port,
291                 keepalive_time, dut1_privatekey, dut2_pubkey
292             )
293             #Configure WireGuard interface on DUT2
294             WireGuardUtil._wireguard_create_tunnel_interface_on_dut(
295                 nodes[u'DUT2'], if2_key, if1_mac_addr, if2_ipaddr, if1_ipaddr,
296                 dut2_allowed_ips, 1, wg_if2_ipaddr, port,
297                 keepalive_time, dut2_privatekey, dut1_pubkey
298             )