T-Rex: 2.82, core pin, 8 workers
[csit.git] / resources / libraries / python / GBP.py
1 # Copyright (c) 2020 Cisco 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 """GBP utilities library."""
15
16 from enum import IntEnum
17
18 from ipaddress import ip_address
19
20 from resources.libraries.python.IPAddress import IPAddress
21 from resources.libraries.python.L2Util import L2Util
22 from resources.libraries.python.PapiExecutor import PapiSocketExecutor
23
24
25 class GBPEndpointFlags(IntEnum):
26     """GBP Endpoint Flags."""
27     GBP_API_ENDPOINT_FLAG_NONE = 0
28     GBP_API_ENDPOINT_FLAG_BOUNCE = 1
29     GBP_API_ENDPOINT_FLAG_REMOTE = 2
30     GBP_API_ENDPOINT_FLAG_LEARNT = 4
31     GBP_API_ENDPOINT_FLAG_EXTERNAL = 8
32
33
34 class GBPBridgeDomainFlags(IntEnum):
35     """GBP Bridge Domain Flags."""
36     GBP_BD_API_FLAG_NONE = 0
37     GBP_BD_API_FLAG_DO_NOT_LEARN = 1
38     GBP_BD_API_FLAG_UU_FWD_DROP = 2
39     GBP_BD_API_FLAG_MCAST_DROP = 4
40     GBP_BD_API_FLAG_UCAST_ARP = 8
41
42
43 class GBPSubnetType(IntEnum):
44     """GBP Subnet Type."""
45     GBP_API_SUBNET_TRANSPORT = 1
46     # TODO: Names too long for pylint, fix in VPP.
47     GBP_API_SUBNET_STITCHED_INTERNAL = 2
48     GBP_API_SUBNET_STITCHED_EXTERNAL = 3
49     GBP_API_SUBNET_L3_OUT = 4
50     GBP_API_SUBNET_ANON_L3_OUT = 5
51
52
53 class GBPExtItfFlags(IntEnum):
54     """GBP External Interface Flags."""
55     GBP_API_EXT_ITF_F_NONE = 0
56     GBP_API_EXT_ITF_F_ANON = 1
57
58
59 class GBPRuleAction(IntEnum):
60     """GBP Rule Action."""
61     GBP_API_RULE_PERMIT = 1
62     GBP_API_RULE_DENY = 2
63     GBP_API_RULE_REDIRECT = 3
64
65
66 class GBPHashMode(IntEnum):
67     """GBP Hash Mode."""
68     GBP_API_HASH_MODE_SRC_IP = 1
69     GBP_API_HASH_MODE_DST_IP = 2
70     GBP_API_HASH_MODE_SYMETRIC = 3
71
72
73 class GBP:
74     """GBP utilities."""
75
76     @staticmethod
77     def gbp_route_domain_add(
78             node, rd_id=1, ip4_table_id=1, ip6_table_id=0,
79             ip4_uu_sw_if_index=0xffffffff, ip6_uu_sw_if_index=0xffffffff):
80         """Add GBP route domain.
81
82         :param node: Node to add GBP route domain on.
83         :param rd_id: GBP route domain ID.
84         :param ip4_table_id: IPv4 table.
85         :param ip6_table_id: IPv6 table.
86         :param ip4_uu_sw_if_index: IPv4 unicast interface index.
87         :param ip6_uu_sw_if_index: IPv6 unicast interface index.
88         :type node: dict
89         :type rd_id: int
90         :type ip4_table_id: int
91         :type ip6_table_id: int
92         :type ip4_uu_sw_if_index: int
93         :type ip6_uu_sw_if_index: int
94         """
95         cmd = u"gbp_route_domain_add"
96         err_msg = f"Failed to add GBP route domain on {node[u'host']}!"
97
98         args_in = dict(
99             rd=dict(
100                 rd_id=rd_id,
101                 ip4_table_id=ip4_table_id,
102                 ip6_table_id=ip6_table_id,
103                 ip4_uu_sw_if_index=ip4_uu_sw_if_index,
104                 ip6_uu_sw_if_index=ip6_uu_sw_if_index
105             )
106         )
107
108         with PapiSocketExecutor(node) as papi_exec:
109             papi_exec.add(cmd, **args_in).get_reply(err_msg)
110
111     @staticmethod
112     def gbp_bridge_domain_add(
113             node, bvi_sw_if_index, bd_id=1, rd_id=1,
114             uu_fwd_sw_if_index=0xffffffff, bm_flood_sw_if_index=0xffffffff):
115         """Add GBP bridge domain.
116
117         :param node: Node to add GBP bridge domain on.
118         :param bvi_sw_if_index: SW index of BVI/loopback interface.
119         :param bd_id: GBP bridge domain ID.
120         :param rd_id: GBP route domain ID.
121         :param uu_fwd_sw_if_index: Unicast forward interface index.
122         :param bm_flood_sw_if_index: Bcast/Mcast flood interface index.
123         :type node: dict
124         :type bvi_sw_if_index: int
125         :type bd_id: int
126         :type rd_id: int
127         :type uu_fwd_sw_if_index: int
128         :type bm_flood_sw_if_index: int
129         """
130         cmd = u"gbp_bridge_domain_add"
131         err_msg = f"Failed to add GBP bridge domain on {node[u'host']}!"
132
133         args_in = dict(
134             bd=dict(
135                 flags=getattr(
136                     GBPBridgeDomainFlags, u"GBP_BD_API_FLAG_NONE"
137                 ).value,
138                 bvi_sw_if_index=bvi_sw_if_index,
139                 uu_fwd_sw_if_index=uu_fwd_sw_if_index,
140                 bm_flood_sw_if_index=bm_flood_sw_if_index,
141                 bd_id=bd_id,
142                 rd_id=rd_id
143             )
144         )
145
146         with PapiSocketExecutor(node) as papi_exec:
147             papi_exec.add(cmd, **args_in).get_reply(err_msg)
148
149     @staticmethod
150     def gbp_endpoint_group_add(
151             node, sclass, bd_id=1, rd_id=1, vnid=1,
152             uplink_sw_if_index=0xffffffff, remote_ep_timeout=0xffffffff):
153         """Add GBP endpoint group.
154
155         :param node: Node to add GBP endpoint group on.
156         :param sclass: Source CLASS.
157         :param bd_id: GBP bridge domain ID.
158         :param rd_id: GBP route domain ID.
159         :param uplink_sw_if_index: Uplink interface index.
160         :param remote_ep_timeout: Remote endpoint interface index.
161         :param vnid: VNID.
162         :type node: dict
163         :type sclass: int
164         :type bd_id: int
165         :type rd_id: int
166         :type vnid: int
167         :type uplink_sw_if_index: int
168         :type remote_ep_timeout: int
169         """
170         cmd = u"gbp_endpoint_group_add"
171         err_msg = f"Failed to add GBP endpoint group on {node[u'host']}!"
172
173         args_in = dict(
174             epg=dict(
175                 uplink_sw_if_index=uplink_sw_if_index,
176                 bd_id=bd_id,
177                 rd_id=rd_id,
178                 vnid=vnid,
179                 sclass=sclass,
180                 retention=dict(
181                     remote_ep_timeout=remote_ep_timeout
182                 )
183             )
184         )
185
186         with PapiSocketExecutor(node) as papi_exec:
187             papi_exec.add(cmd, **args_in).get_reply(err_msg)
188
189     @staticmethod
190     def gbp_endpoint_add(node, sw_if_index, ip_addr, mac_addr, sclass):
191         """Add GBP endpoint.
192
193         :param node: Node to add GBP endpoint on.
194         :param sw_if_index: SW index of interface.
195         :param ip_addr: GBP route domain ID.
196         :param mac_addr: MAC address.
197         :param sclass: Source CLASS.
198         :type node: dict
199         :type sw_if_index: int
200         :type ip_addr: str
201         :type mac_addr: str
202         :type sclass: int
203         """
204         cmd = u"gbp_endpoint_add"
205         err_msg = f"Failed to add GBP endpoint on {node[u'host']}!"
206
207         ips = list()
208         ips.append(IPAddress.create_ip_address_object(ip_address(ip_addr)))
209         tun_src = IPAddress.create_ip_address_object(ip_address(u"0.0.0.0"))
210         tun_dst = IPAddress.create_ip_address_object(ip_address(u"0.0.0.0"))
211
212         args_in = dict(
213             endpoint=dict(
214                 sw_if_index=sw_if_index,
215                 ips=ips,
216                 n_ips=len(ips),
217                 mac=L2Util.mac_to_bin(mac_addr),
218                 sclass=sclass,
219                 flags=getattr(
220                     GBPEndpointFlags, u"GBP_API_ENDPOINT_FLAG_EXTERNAL"
221                 ).value,
222                 tun=dict(
223                     src=tun_src,
224                     dst=tun_dst
225                 )
226             )
227         )
228
229         with PapiSocketExecutor(node) as papi_exec:
230             papi_exec.add(cmd, **args_in).get_reply(err_msg)
231
232     @staticmethod
233     def gbp_ext_itf_add_del(node, sw_if_index, bd_id=1, rd_id=1):
234         """Add external interface to GBP.
235
236         :param node: Node to add external GBP interface on.
237         :param sw_if_index: SW index of interface.
238         :param bd_id: GBP bridge domain ID.
239         :param rd_id: GBP route domain ID.
240         :type node: dict
241         :type sw_if_index: int
242         :type bd_id: int
243         :type rd_id: int
244         """
245         cmd = u"gbp_ext_itf_add_del"
246         err_msg = u"Failed to add external GBP interface on {node[u'host']}!"
247
248         args_in = dict(
249             is_add=True,
250             ext_itf=dict(
251                 sw_if_index=sw_if_index,
252                 bd_id=bd_id,
253                 rd_id=rd_id,
254                 flags=getattr(GBPExtItfFlags, u"GBP_API_EXT_ITF_F_NONE").value
255             )
256         )
257
258         with PapiSocketExecutor(node) as papi_exec:
259             papi_exec.add(cmd, **args_in).get_reply(err_msg)
260
261     @staticmethod
262     def gbp_subnet_add_del(
263             node, address, subnet_length, sclass, rd_id=1,
264             sw_if_index=0xffffffff):
265         """Add external interface to GBP.
266
267         :param node: Node to add GBP subnet on.
268         :param address: IPv4 adddress.
269         :param subnet_length: IPv4 address subnet.
270         :param sclass: Source CLASS.
271         :param rd_id: GBP route domain ID.
272         :param sw_if_index: Interface index.
273         :type node: dict
274         :type address: int
275         :type subnet_length: int
276         :type sclass: int
277         :type rd_id: int
278         :type sw_if_index: int
279         """
280         cmd = u"gbp_subnet_add_del"
281         err_msg = f"Failed to add GBP subnet on {node[u'host']}!"
282
283         args_in = dict(
284             is_add=True,
285             subnet=dict(
286                 type=getattr(GBPSubnetType, u"GBP_API_SUBNET_L3_OUT").value,
287                 sw_if_index=sw_if_index,
288                 sclass=sclass,
289                 prefix=dict(
290                     address=IPAddress.create_ip_address_object(
291                         ip_address(address)
292                     ),
293                     len=int(subnet_length)
294                 ),
295                 rd_id=rd_id
296             )
297         )
298
299         with PapiSocketExecutor(node) as papi_exec:
300             papi_exec.add(cmd, **args_in).get_reply(err_msg)
301
302     @staticmethod
303     def gbp_contract_add_del(node, sclass, dclass, acl_index=0, hash_mode=None):
304         """Add GBP contract.
305
306         :param node: Node to add GBP contract on.
307         :param sclass: Source CLASS.
308         :param dclass: Destination CLASS.
309         :param acl_index: Index of ACL rule.
310         :param hash_mode: GBP contract hash mode.
311         :type node: dict
312         :type sclass: int
313         :type dclass: int
314         :type acl_index: int
315         :type hash_mode: str
316         """
317         cmd = u"gbp_contract_add_del"
318         err_msg = f"Failed to add GBP contract on {node[u'host']}!"
319
320         hash_mode = u"GBP_API_HASH_MODE_SRC_IP" if hash_mode is None \
321             else hash_mode
322         rule_permit = dict(
323             action=getattr(GBPRuleAction, u"GBP_API_RULE_PERMIT").value,
324             nh_set=dict(
325                 hash_mode=getattr(GBPHashMode, hash_mode).value,
326                 n_nhs=8,
327                 nhs=[dict()]*8,
328             )
329         )
330         rules = [rule_permit, rule_permit]
331
332         args_in = dict(
333             is_add=True,
334             contract=dict(
335                 acl_index=acl_index,
336                 sclass=sclass,
337                 dclass=dclass,
338                 n_rules=len(rules),
339                 rules=rules,
340                 n_ether_types=16,
341                 allowed_ethertypes=[0x800, 0x86dd] + [0]*14
342             )
343         )
344
345         with PapiSocketExecutor(node) as papi_exec:
346             papi_exec.add(cmd, **args_in).get_reply(err_msg)