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