fix(Pylint): Small fixes
[csit.git] / resources / libraries / python / FlowUtil.py
1 # copyright (c) 2021 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 """Flow Utilities Library."""
15
16 from ipaddress import ip_address
17
18 from resources.libraries.python.topology import Topology
19 from resources.libraries.python.ssh import exec_cmd_no_error
20 from resources.libraries.python.PapiExecutor import PapiSocketExecutor
21
22 from vpp_papi import VppEnum
23
24 class FlowUtil:
25     """Utilities for flow configuration."""
26
27     @staticmethod
28     def vpp_create_ip4_n_tuple_flow(
29             node, src_ip, dst_ip, src_port, dst_port,
30             proto, action, value=0):
31         """Create IP4_N_TUPLE flow.
32
33         :param node: DUT node.
34         :param src_ip: Source IP4 address.
35         :param dst_ip: Destination IP4 address.
36         :param src_port: Source port.
37         :param dst_port: Destination port.
38         :param proto: TCP or UDP.
39         :param action: Mark, drop or redirect-to-queue.
40         :param value: Action value.
41
42         :type node: dict
43         :type src_ip: str
44         :type dst_ip: str
45         :type src_port: int
46         :type dst_port: int
47         :type proto: str
48         :type action: str
49         :type value: int
50         :returns: flow_index.
51         :rtype: int
52         """
53         flow = u"ip4_n_tuple"
54         flow_type = VppEnum.vl_api_flow_type_t.FLOW_TYPE_IP4_N_TUPLE
55
56         if proto == u"TCP":
57             flow_proto = VppEnum.vl_api_ip_proto_t.IP_API_PROTO_TCP
58         elif proto == u"UDP":
59             flow_proto = VppEnum.vl_api_ip_proto_t.IP_API_PROTO_UDP
60         else:
61             raise ValueError(f"proto error: {proto}")
62
63         pattern = {
64             u'src_addr': {u'addr': src_ip, u'mask': u"255.255.255.255"},
65             u'dst_addr': {u'addr': dst_ip, u'mask': u"255.255.255.255"},
66             u'src_port': {u'port': src_port, u'mask': 0xFFFF},
67             u'dst_port': {u'port': dst_port, u'mask': 0xFFFF},
68             u'protocol': {u'prot': flow_proto}
69         }
70
71         flow_index = FlowUtil.vpp_flow_add(
72             node, flow, flow_type, pattern, action, value)
73
74         return flow_index
75
76     @staticmethod
77     def vpp_create_ip6_n_tuple_flow(
78             node, src_ip, dst_ip, src_port, dst_port,
79             proto, action, value=0):
80         """Create IP6_N_TUPLE flow.
81
82         :param node: DUT node.
83         :param src_ip: Source IP6 address.
84         :param dst_ip: Destination IP6 address.
85         :param src_port: Source port.
86         :param dst_port: Destination port.
87         :param proto: TCP or UDP.
88         :param action: Mark, drop or redirect-to-queue.
89         :param value: Action value.
90
91         :type node: dict
92         :type src_ip: str
93         :type dst_ip: str
94         :type src_port: int
95         :type dst_port: int
96         :type proto: str
97         :type action: str
98         :type value: int
99         :returns: flow_index.
100         :rtype: int
101         """
102         flow = u"ip6_n_tuple"
103         flow_type = VppEnum.vl_api_flow_type_t.FLOW_TYPE_IP6_N_TUPLE
104
105         if proto == u"TCP":
106             flow_proto = VppEnum.vl_api_ip_proto_t.IP_API_PROTO_TCP
107         elif proto == u"UDP":
108             flow_proto = VppEnum.vl_api_ip_proto_t.IP_API_PROTO_UDP
109         else:
110             raise ValueError(f"proto error: {proto}")
111
112         pattern = {
113             u'src_addr': {u'addr': src_ip, \
114                 u'mask': u"FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF"},
115             u'dst_addr': {u'addr': dst_ip, \
116                 u'mask': u"FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF"},
117             u'src_port': {u'port': src_port, u'mask': 0xFFFF},
118             u'dst_port': {u'port': dst_port, u'mask': 0xFFFF},
119             u'protocol': {u'prot': flow_proto}
120         }
121
122         flow_index = FlowUtil.vpp_flow_add(
123             node, flow, flow_type, pattern, action, value)
124
125         return flow_index
126
127     @staticmethod
128     def vpp_create_ip4_flow(
129             node, src_ip, dst_ip, proto, action, value=0):
130         """Create IP4 flow.
131
132         :param node: DUT node.
133         :param src_ip: Source IP4 address.
134         :param dst_ip: Destination IP4 address.
135         :param proto: TCP or UDP.
136         :param action: Mark, drop or redirect-to-queue.
137         :param value: Action value.
138
139         :type node: dict
140         :type src_ip: str
141         :type dst_ip: str
142         :type proto: str
143         :type action: str
144         :type value: int
145         :returns: flow_index.
146         :rtype: int
147         """
148         flow = u"ip4"
149         flow_type = VppEnum.vl_api_flow_type_t.FLOW_TYPE_IP4
150
151         if proto == u"TCP":
152             flow_proto = VppEnum.vl_api_ip_proto_t.IP_API_PROTO_TCP
153         elif proto == u"UDP":
154             flow_proto = VppEnum.vl_api_ip_proto_t.IP_API_PROTO_UDP
155         else:
156             raise ValueError(f"proto error: {proto}")
157
158         pattern = {
159             u'src_addr': {u'addr': src_ip, u'mask': u"255.255.255.255"},
160             u'dst_addr': {u'addr': dst_ip, u'mask': u"255.255.255.255"},
161             u'protocol': {u'prot': flow_proto}
162         }
163
164         flow_index = FlowUtil.vpp_flow_add(
165             node, flow, flow_type, pattern, action, value)
166
167         return flow_index
168
169     @staticmethod
170     def vpp_create_ip6_flow(
171             node, src_ip, dst_ip, proto, action, value=0):
172         """Create IP6 flow.
173
174         :param node: DUT node.
175         :param src_ip: Source IP6 address.
176         :param dst_ip: Destination IP6 address.
177         :param proto: TCP or UDP.
178         :param action: Mark, drop or redirect-to-queue.
179         :param value: Action value.
180
181         :type node: dict
182         :type src_ip: str
183         :type dst_ip: str
184         :type proto: str
185         :type action: str
186         :type value: int
187         :returns: flow_index.
188         :rtype: int
189         """
190         flow = u"ip6"
191         flow_type = VppEnum.vl_api_flow_type_t.FLOW_TYPE_IP6
192
193         if proto == u"TCP":
194             flow_proto = VppEnum.vl_api_ip_proto_t.IP_API_PROTO_TCP
195         elif proto == u"UDP":
196             flow_proto = VppEnum.vl_api_ip_proto_t.IP_API_PROTO_UDP
197         else:
198             raise ValueError(f"proto error: {proto}")
199
200         pattern = {
201             u'src_addr': {u'addr': src_ip, \
202                 u'mask': u"FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF"},
203             u'dst_addr': {'addr': dst_ip, \
204                 u'mask': u"FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF"},
205             u'protocol': {u'prot': flow_proto}
206         }
207
208         flow_index = FlowUtil.vpp_flow_add(
209             node, flow, flow_type, pattern, action, value)
210
211         return flow_index
212
213     @staticmethod
214     def vpp_create_ip4_gtpu_flow(
215             node, src_ip, dst_ip, teid, action, value=0):
216         """Create IP4_GTPU flow.
217
218         :param node: DUT node.
219         :param src_ip: Source IP4 address.
220         :param dst_ip: Destination IP4 address.
221         :param teid: Tunnel endpoint identifier.
222         :param action: Mark, drop or redirect-to-queue.
223         :param value: Action value.
224
225         :type node: dict
226         :type src_ip: str
227         :type dst_ip: str
228         :type teid: int
229         :type action: str
230         :type value: int
231         :returns: flow_index.
232         :rtype: int
233         """
234         flow = u"ip4_gtpu"
235         flow_type = VppEnum.vl_api_flow_type_t.FLOW_TYPE_IP4_GTPU
236         flow_proto = VppEnum.vl_api_ip_proto_t.IP_API_PROTO_UDP
237
238         pattern = {
239             u'src_addr': {u'addr': src_ip, u'mask': u"255.255.255.255"},
240             u'dst_addr': {u'addr': dst_ip, u'mask': u"255.255.255.255"},
241             u'protocol': {u'prot': flow_proto},
242             u'teid': teid
243         }
244
245         flow_index = FlowUtil.vpp_flow_add(
246             node, flow, flow_type, pattern, action, value)
247
248         return flow_index
249
250     @staticmethod
251     def vpp_create_ip4_ipsec_flow(node, proto, spi, action, value=0):
252         """Create IP4_IPSEC flow.
253
254         :param node: DUT node.
255         :param proto: TCP or UDP.
256         :param spi: Security Parameters Index.
257         :param action: Mark, drop or redirect-to-queue.
258         :param value: Action value.
259
260         :type node: dict
261         :type proto: str
262         :type spi: int
263         :type action: str
264         :type value: int
265         :returns: flow_index.
266         :rtype: int
267         """
268         if proto == u"ESP":
269             flow = u"ip4_ipsec_esp"
270             flow_proto = VppEnum.vl_api_ip_proto_t.IP_API_PROTO_ESP
271             flow_type = VppEnum.vl_api_flow_type_t.FLOW_TYPE_IP4_IPSEC_ESP
272         elif proto == u"AH":
273             flow = u"ip4_ipsec_ah"
274             flow_proto = VppEnum.vl_api_ip_proto_t.IP_API_PROTO_AH
275             flow_type = VppEnum.vl_api_flow_type_t.FLOW_TYPE_IP4_IPSEC_AH
276         else:
277             raise ValueError(f"proto error: {proto}")
278
279         pattern = {
280             u'protocol': {u'prot': flow_proto},
281             u'spi': spi
282         }
283
284         flow_index = FlowUtil.vpp_flow_add(
285             node, flow, flow_type, pattern, action, value)
286
287         return flow_index
288
289     @staticmethod
290     def vpp_create_ip4_l2tp_flow(node, session_id, action, value=0):
291         """Create IP4_L2TPV3OIP flow.
292
293         :param node: DUT node.
294         :param session_id: PPPoE session ID
295         :param action: Mark, drop or redirect-to-queue.
296         :param value: Action value.
297
298         :type node: dict
299         :type session_id: int
300         :type action: str
301         :type value: int
302         :returns: flow_index.
303         :rtype: int
304         """
305         flow = u"ip4_l2tpv3oip"
306         flow_proto = 115    # IP_API_PROTO_L2TP
307         flow_type = VppEnum.vl_api_flow_type_t.FLOW_TYPE_IP4_L2TPV3OIP
308
309         pattern = {
310             u'protocol': {u'prot': flow_proto},
311             u'session_id': session_id
312         }
313
314         flow_index = FlowUtil.vpp_flow_add(
315             node, flow, flow_type, pattern, action, value)
316
317         return flow_index
318
319     @staticmethod
320     def vpp_create_ip4_vxlan_flow(node, src_ip, dst_ip, vni, action, value=0):
321         """Create IP4_VXLAN flow.
322
323         :param node: DUT node.
324         :param src_ip: Source IP4 address.
325         :param dst_ip: Destination IP4 address.
326         :param vni: Virtual network instance.
327         :param action: Mark, drop or redirect-to-queue.
328         :param value: Action value.
329
330         :type node: dict
331         :type src_ip: str
332         :type dst_ip: str
333         :type vni: int
334         :type action: str
335         :type value: int
336         :returns: flow_index.
337         """
338         flow = u"ip4_vxlan"
339         flow_type = VppEnum.vl_api_flow_type_t.FLOW_TYPE_IP4_VXLAN
340         flow_proto = VppEnum.vl_api_ip_proto_t.IP_API_PROTO_UDP
341
342         pattern = {
343             u'src_addr': {u'addr': src_ip, u'mask': u"255.255.255.255"},
344             u'dst_addr': {u'addr': dst_ip, u'mask': u"255.255.255.255"},
345             u'dst_port': {u'port': 4789, 'mask': 0xFFFF},
346             u'protocol': {u'prot': flow_proto},
347             u'vni': vni
348         }
349
350         flow_index = FlowUtil.vpp_flow_add(
351             node, flow, flow_type, pattern, action, value)
352
353         return flow_index
354
355     @staticmethod
356     def vpp_flow_add(node, flow, flow_type, pattern, action, value=0):
357         """Flow add.
358
359         :param node: DUT node.
360         :param flow: Name of flow.
361         :param flow_type: Type of flow.
362         :param pattern: Pattern of flow.
363         :param action: Mark, drop or redirect-to-queue.
364         :param value: Action value.
365
366         :type node: dict
367         :type node: str
368         :type flow_type: str
369         :type pattern: dict
370         :type action: str
371         :type value: int
372         :returns: flow_index.
373         :rtype: int
374         :raises ValueError: If action type is not supported.
375         """
376         cmd = u"flow_add"
377
378         if action == u"redirect-to-queue":
379             flow_rule = {
380                 u'type': flow_type,
381                 u'actions': VppEnum.vl_api_flow_action_t.\
382                             FLOW_ACTION_REDIRECT_TO_QUEUE,
383                 u'redirect_queue': value,
384                 u'flow': {flow : pattern}
385             }
386         elif action == u"mark":
387             flow_rule = {
388                 u'type': flow_type,
389                 u'actions': VppEnum.vl_api_flow_action_t.FLOW_ACTION_MARK,
390                 u'mark_flow_id': value,
391                 u'flow': {flow : pattern}
392             }
393         elif action == u"drop":
394             flow_rule = {
395                 u'type': flow_type,
396                 u'actions': VppEnum.vl_api_flow_action_t.FLOW_ACTION_DROP,
397                 u'flow': {flow : pattern}
398             }
399         else:
400             raise ValueError(f"Unsupported action type: {action}")
401
402         err_msg = f"Failed to create {flow} flow on host {node[u'host']}."
403         args = dict(flow=flow_rule)
404         flow_index = -1
405         with PapiSocketExecutor(node) as papi_exec:
406             reply = papi_exec.add(cmd, **args).get_reply(err_msg)
407             flow_index = reply[u"flow_index"]
408
409         return flow_index
410
411     @staticmethod
412     def vpp_flow_enable(node, interface, flow_index=0):
413         """Flow enable.
414
415         :param node: DUT node.
416         :param interface: Interface sw_if_index.
417         :param flow_index: Flow index.
418
419         :type node: dict
420         :type interface: int
421         :type flow_index: int
422         :returns: Nothing.
423         """
424         cmd = u"flow_enable"
425         sw_if_index = Topology.get_interface_sw_index(node, interface)
426         args = dict(
427             flow_index=int(flow_index),
428             hw_if_index=int(sw_if_index)
429         )
430
431         err_msg = u"Failed to enable flow on host {node[u'host']}"
432         with PapiSocketExecutor(node) as papi_exec:
433             papi_exec.add(cmd, **args).get_reply(err_msg)
434
435     @staticmethod
436     def vpp_flow_disable(node, interface, flow_index=0):
437         """Flow disable.
438
439         :param node: DUT node.
440         :param interface: Interface sw_if_index.
441         :param flow_index: Flow index.
442
443         :type node: dict
444         :type interface: int
445         :type flow_index: int
446         :returns: Nothing.
447         """
448         cmd = u"flow_disable"
449         sw_if_index = Topology.get_interface_sw_index(node, interface)
450         args = dict(
451             flow_index=int(flow_index),
452             hw_if_index=int(sw_if_index)
453         )
454
455         err_msg = u"Failed to disable flow on host {node[u'host']}"
456         with PapiSocketExecutor(node) as papi_exec:
457             papi_exec.add(cmd, **args).get_reply(err_msg)
458
459     @staticmethod
460     def vpp_flow_del(node, flow_index=0):
461         """Flow delete.
462
463         :param node: DUT node.
464         :param flow_index: Flow index.
465
466         :type node: dict
467         :type flow_index: int
468         :returns: Nothing.
469         """
470         cmd = u"flow_del"
471         args = dict(
472             flow_index=int(flow_index)
473         )
474
475         err_msg = u"Failed to delete flow on host {node[u'host']}"
476         with PapiSocketExecutor(node) as papi_exec:
477             papi_exec.add(cmd, **args).get_reply(err_msg)
478
479     @staticmethod
480     def vpp_show_flow_entry(node):
481         """Show flow entry.
482
483         :param node: DUT node.
484
485         :type node: dict
486         :returns: flow entry.
487         :rtype: str
488         """
489         cmd = u"vppctl show flow entry"
490
491         err_msg = u"Failed to show flow on host {node[u'host']}"
492         stdout, _ = exec_cmd_no_error(
493             node, cmd, sudo=False, message=err_msg, retries=120
494             )
495
496         return stdout.strip()
497
498     @staticmethod
499     def vpp_verify_flow_action(
500             node, action, value,
501             src_mac=u"11:22:33:44:55:66", dst_mac=u"11:22:33:44:55:66",
502             src_ip=None, dst_ip=None):
503         """Verify the correctness of the flow action.
504
505         :param node: DUT node.
506         :param action: Action.
507         :param value: Action value.
508         :param src_mac: Source mac address.
509         :param dst_mac: Destination mac address.
510         :param src_ip: Source IP address.
511         :param dst_ip: Destination IP address.
512
513         :type node: dict
514         :type action: str
515         :type value: int
516         :type src_mac: str
517         :type dst_mac: str
518         :type src_ip: str
519         :type dst_ip: str
520         :returns: Nothing.
521         :raises RuntimeError: If the verification of flow action fails.
522         :raises ValueError: If action type is not supported.
523         """
524         err_msg = f"Failed to show trace on host {node[u'host']}"
525         cmd = u"vppctl show trace"
526         stdout, _ = exec_cmd_no_error(
527             node, cmd, sudo=False, message=err_msg, retries=120
528         )
529
530         err_info = f"Verify flow {action} failed"
531
532         if src_ip is None:
533             expected_str = f"{src_mac} -> {dst_mac}"
534         else:
535             src_ip = ip_address(src_ip)
536             dst_ip = ip_address(dst_ip)
537             expected_str = f"{src_ip} -> {dst_ip}"
538
539         if action == u"drop":
540             if expected_str in stdout:
541                 raise RuntimeError(err_info)
542         elif action == u"redirect-to-queue":
543             if f"queue {value}" not in stdout \
544                     and f"qid {value}" not in stdout:
545                 raise RuntimeError(err_info)
546             if expected_str not in stdout:
547                 raise RuntimeError(err_info)
548         elif action == u"mark":
549             if u"PKT_RX_FDIR" not in stdout and  u"flow-id 1" not in stdout:
550                 raise RuntimeError(err_info)
551             if expected_str not in stdout:
552                 raise RuntimeError(err_info)
553         else:
554             raise ValueError(f"Unsupported action type: {action}")