1938688900caa00b027752dd24de0d8cc29a4e78
[csit.git] / resources / libraries / python / Classify.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 """Classify utilities library."""
15
16 import binascii
17 import re
18
19 from socket import AF_INET, AF_INET6, inet_aton, inet_pton
20
21 from robot.api import logger
22
23 from resources.libraries.python.topology import Topology
24 from resources.libraries.python.PapiExecutor import PapiExecutor
25
26
27 class Classify(object):
28     """Classify utilities."""
29
30     @staticmethod
31     def _build_mac_mask(dst_mac='', src_mac='', ether_type=''):
32         """Build MAC ACL mask data in hexstring format.
33
34         :param dst_mac: Source MAC address <0-ffffffffffff>.
35         :param src_mac: Destination MAC address <0-ffffffffffff>.
36         :param ether_type: Ethernet type <0-ffff>.
37         :type dst_mac: str
38         :type src_mac: str
39         :type ether_type: str
40         :returns MAC ACL mask in hexstring format.
41         :rtype: str
42         """
43         return ('{!s:0>12}{!s:0>12}{!s:0>4}'.format(
44             dst_mac.replace(':', ''), src_mac.replace(':', ''), ether_type)).\
45             rstrip('0')
46
47     @staticmethod
48     def _build_ip_mask(proto='', src_ip='', dst_ip='', src_port='',
49                        dst_port=''):
50         """Build IP ACL mask data in hexstring format.
51
52         :param proto: Protocol number <0-ff>.
53         :param src_ip: Source ip address <0-ffffffff>.
54         :param dst_ip: Destination ip address <0-ffffffff>.
55         :param src_port: Source port number <0-ffff>.
56         :param str dst_port: Destination port number <0-ffff>.
57         :type proto: str
58         :type src_ip: str
59         :type dst_ip: str
60         :type src_port: str
61         :type dst_port:src
62         :returns: IP mask in hexstring format.
63         :rtype: str
64         """
65         return ('{!s:0>20}{!s:0>12}{!s:0>8}{!s:0>4}{!s:0>4}'.format(
66             proto, src_ip, dst_ip, src_port, dst_port)).rstrip('0')
67
68     @staticmethod
69     def _build_ip6_mask(next_hdr='', src_ip='', dst_ip='', src_port='',
70                         dst_port=''):
71         """Build IPv6 ACL mask data in hexstring format.
72
73         :param next_hdr: Next header number <0-ff>.
74         :param src_ip: Source ip address <0-ffffffff>.
75         :param dst_ip: Destination ip address <0-ffffffff>.
76         :param src_port: Source port number <0-ffff>.
77         :param dst_port: Destination port number <0-ffff>.
78         :type next_hdr: str
79         :type src_ip: str
80         :type dst_ip: str
81         :type src_port: str
82         :type dst_port: str
83         :returns: IPv6 ACL mask in hexstring format.
84         :rtype: str
85         """
86         return ('{!s:0>14}{!s:0>34}{!s:0>32}{!s:0>4}{!s:0>4}'.format(
87             next_hdr, src_ip, dst_ip, src_port, dst_port)).rstrip('0')
88
89     @staticmethod
90     def _build_mac_match(dst_mac='', src_mac='', ether_type=''):
91         """Build MAC ACL match data in  hexstring format.
92
93         :param dst_mac: Source MAC address <x:x:x:x:x:x>.
94         :param src_mac: Destination MAC address <x:x:x:x:x:x>.
95         :param ether_type: Ethernet type <0-ffff>.
96         :type dst_mac: str
97         :type src_mac: str
98         :type ether_type: str
99         :returns: MAC ACL match data in hexstring format.
100         :rtype: str
101         """
102         if dst_mac:
103             dst_mac = dst_mac.replace(':', '')
104         if src_mac:
105             src_mac = src_mac.replace(':', '')
106
107         return ('{!s:0>12}{!s:0>12}{!s:0>4}'.format(
108             dst_mac, src_mac, ether_type)).rstrip('0')
109
110     @staticmethod
111     def _build_ip_match(proto=0, src_ip='', dst_ip='', src_port=0, dst_port=0):
112         """Build IP ACL match data in hexstring format.
113
114         :param proto: Protocol number with valid option "x".
115         :param src_ip: Source ip address with format of "x.x.x.x".
116         :param dst_ip: Destination ip address with format of "x.x.x.x".
117         :param src_port: Source port number "x".
118         :param dst_port: Destination port number "x".
119         :type proto: int
120         :type src_ip: str
121         :type dst_ip: str
122         :type src_port: int
123         :type dst_port: int
124         :returns: IP ACL match data in hexstring format.
125         :rtype: str
126         """
127         if src_ip:
128             src_ip = binascii.hexlify(inet_aton(src_ip))
129         if dst_ip:
130             dst_ip = binascii.hexlify(inet_aton(dst_ip))
131
132         return ('{!s:0>20}{!s:0>12}{!s:0>8}{!s:0>4}{!s:0>4}'.format(
133             hex(proto)[2:], src_ip, dst_ip, hex(src_port)[2:],
134             hex(dst_port)[2:])).rstrip('0')
135
136     @staticmethod
137     def _build_ip6_match(next_hdr=0, src_ip='', dst_ip='', src_port=0,
138                          dst_port=0):
139         """Build IPv6 ACL match data in hexstring format.
140
141         :param next_hdr: Next header number with valid option "x".
142         :param src_ip: Source ip6 address with format of "xxx:xxxx::xxxx".
143         :param dst_ip: Destination ip6 address with format of
144             "xxx:xxxx::xxxx".
145         :param src_port: Source port number "x".
146         :param dst_port: Destination port number "x".
147         :type next_hdr: int
148         :type src_ip: str
149         :type dst_ip: str
150         :type src_port: int
151         :type dst_port: int
152         :returns: IPv6 ACL match data in hexstring format.
153         :rtype: str
154         """
155         if src_ip:
156             src_ip = binascii.hexlify(inet_pton(AF_INET6, src_ip))
157         if dst_ip:
158             dst_ip = binascii.hexlify(inet_pton(AF_INET6, dst_ip))
159
160         return ('{!s:0>14}{!s:0>34}{!s:0>32}{!s:0>4}{!s:0>4}'.format(
161             hex(next_hdr)[2:], src_ip, dst_ip, hex(src_port)[2:],
162             hex(dst_port)[2:])).rstrip('0')
163
164     @staticmethod
165     def _classify_add_del_table(node, is_add, mask, match_n_vectors=1,
166                                 table_index=0xFFFFFFFF, nbuckets=2,
167                                 memory_size=2097152, skip_n_vectors=0,
168                                 next_table_index=0xFFFFFFFF,
169                                 miss_next_index=0xFFFFFFFF, current_data_flag=0,
170                                 current_data_offset=0):
171         """Add or delete a classify table.
172
173         :param node: VPP node to create classify table.
174         :param is_add: If 1 the table is added, if 0 the table is deleted.
175         :param mask: ACL mask in hexstring format.
176         :param match_n_vectors: Number of vectors to match (Default value = 1).
177         :param table_index: Index of the classify table.
178             (Default value = 0xFFFFFFFF)
179         :param nbuckets: Number of buckets when adding a table.
180             (Default value = 2)
181         :param memory_size: Memory size when adding a table.
182             (Default value = 2097152)
183         :param skip_n_vectors: Number of skip vectors (Default value = 0).
184         :param next_table_index: Index of next table.
185             (Default value = 0xFFFFFFFF)
186         :param miss_next_index: Index of miss table.
187             (Default value = 0xFFFFFFFF)
188         :param current_data_flag: Option to use current node's packet payload
189             as the starting point from where packets are classified.
190             This option is only valid for L2/L3 input ACL for now.
191             0: by default, classify data from the buffer's start location
192             1: classify packets from VPP node's current data pointer.
193         :param current_data_offset: A signed value to shift the start location
194             of the packet to be classified.
195             For example, if input IP ACL node is used, L2 header's first byte
196             can be accessible by configuring current_data_offset to -14
197             if there is no vlan tag.
198             This is valid only if current_data_flag is set to 1.
199             (Default value = 0)
200         :type node: dict
201         :type is_add: int
202         :type mask: str
203         :type match_n_vectors: int
204         :type table_index: int
205         :type nbuckets: int
206         :type memory_size: int
207         :type skip_n_vectors: int
208         :type next_table_index: int
209         :type miss_next_index: int
210         :type current_data_flag: int
211         :type current_data_offset: int
212         :returns: (table_index, skip_n, match_n)
213             table_index: Classify table index.
214             skip_n: Number of skip vectors.
215             match_n: Number of match vectors.
216         :rtype: tuple(int, int, int)
217         """
218         mask_len = ((len(mask) - 1) / 16 + 1) * 16
219         mask = mask + '\0' * (mask_len - len(mask))
220
221         args = dict(
222             is_add=is_add,
223             table_index=table_index,
224             nbuckets=nbuckets,
225             memory_size=memory_size,
226             skip_n_vectors=skip_n_vectors,
227             match_n_vectors=match_n_vectors,
228             next_table_index=next_table_index,
229             miss_next_index=miss_next_index,
230             current_data_flag=current_data_flag,
231             current_data_offset=current_data_offset,
232             mask_len=mask_len,
233             mask=mask
234         )
235
236         cmd = 'classify_add_del_table'
237         err_msg = "Failed to create a classify table on host {host}".format(
238             host=node['host'])
239
240         with PapiExecutor(node) as papi_exec:
241             data = papi_exec.add(cmd, **args).get_replies(err_msg).\
242                 verify_reply(err_msg=err_msg)
243
244         return int(data["new_table_index"]), int(data["skip_n_vectors"]),\
245             int(data["match_n_vectors"])
246
247     @staticmethod
248     def _classify_add_del_session(node, is_add, table_index, match,
249                                   opaque_index=0xFFFFFFFF,
250                                   hit_next_index=0xFFFFFFFF, advance=0,
251                                   action=0, metadata=0):
252         """Add or delete a classify session.
253
254         :param node: VPP node to create classify session.
255         :param is_add: If 1 the session is added, if 0 the session is deleted.
256         :param table_index: Index of the table to add/del the session.
257         :param match: For add, match value for session, required, needs to
258             include bytes in front with length of skip_n_vectors of target table
259             times sizeof (u32x4) (values of those bytes will be ignored).
260         :param opaque_index: For add, opaque_index of new session.
261             (Default value = 0xFFFFFFFF)
262         :param hit_next_index: For add, hit_next_index of new session.
263             (Default value = 0xFFFFFFFF)
264         :param advance: For add, advance value for session. (Default value = 0)
265         :param action: 0: No action (by default) metadata is not used.
266             1: Classified IP packets will be looked up from the specified ipv4
267                fib table (configured by metadata as VRF id).
268                Only valid for L3 input ACL node
269             2: Classified IP packets will be looked up from the specified ipv6
270                fib table (configured by metadata as VRF id).
271                Only valid for L3 input ACL node
272             3: Classified packet will be steered to source routig policy of
273                given index (in metadata).
274                This is only valid for IPv6 packets redirected to a source
275                routing node.
276         :param metadata: Valid only if action != 0
277             VRF id if action is 1 or 2. SR policy index if action is 3.
278             (Default value = 0)
279         :type node: dict
280         :type is_add: int
281         :type table_index: int
282         :type match: str
283         :type opaque_index: int
284         :type hit_next_index: int
285         :type advance: int
286         :type action: int
287         :type metadata: int
288         """
289
290         match_len = ((len(match) - 1) / 16 + 1) * 16
291         match = match + '\0' * (match_len - len(match))
292         args = dict(
293             is_add=is_add,
294             table_index=table_index,
295             hit_next_index=hit_next_index,
296             opaque_index=opaque_index,
297             advance=advance,
298             action=action,
299             metadata=metadata,
300             match_len=match_len,
301             match=match
302         )
303         cmd = 'classify_add_del_session'
304         err_msg = "Failed to create a classify session on host {host}".format(
305             host=node['host'])
306
307         with PapiExecutor(node) as papi_exec:
308             papi_exec.add(cmd, **args).get_replies(err_msg). \
309                 verify_reply(err_msg=err_msg)
310
311     @staticmethod
312     def _macip_acl_add(node, rules, tag=""):
313         """Add MACIP ACL.
314
315         :param node: VPP node to add MACIP ACL.
316         :param rules: List of rules for given ACL.
317         :param tag: ACL tag.
318         :type node: dict
319         :type rules: list
320         :type tag: str
321         """
322         cmd = "macip_acl_add"
323         args = dict(
324             r=rules,
325             count=len(rules),
326             tag=tag
327         )
328
329         err_msg = "Failed to create a classify session on host {host}".format(
330             host=node['host'])
331
332         with PapiExecutor(node) as papi_exec:
333             papi_exec.add(cmd, **args).get_replies(err_msg). \
334                 verify_reply(err_msg=err_msg)
335
336     @staticmethod
337     def _acl_interface_set_acl_list(node, sw_if_index, acl_type, acls):
338         """Set ACL list for interface.
339
340         :param node: VPP node to set ACL list for interface.
341         :param sw_if_index: sw_if_index of the used interface.
342         :param acl_type: Type of ACL(s) - input or output.
343         :param acls: List of ACLs.
344         :type node: dict
345         :type sw_if_index: int
346         :type acl_type: str
347         :type acls: list
348         """
349         cmd = "acl_interface_set_acl_list"
350         n_input = len(acls) if acl_type == "input" else 0
351         args = dict(
352             sw_if_index=sw_if_index,
353             acls=acls,
354             n_input=n_input,
355             count=len(acls)
356         )
357
358         err_msg = "Failed to set acl list for interface {idx} on host {host}".\
359             format(idx=sw_if_index, host=node['host'])
360
361         with PapiExecutor(node) as papi_exec:
362             papi_exec.add(cmd, **args).get_replies(err_msg). \
363                 verify_reply(err_msg=err_msg)
364
365     @staticmethod
366     def _acl_add_replace(node, acl_idx, rules, tag=""):
367         """ Add/replace ACLs.
368
369         :param node: VPP node to add MACIP ACL.
370         :param acl_idx: ACL index.
371         :param rules: List of rules for given ACL.
372         :param tag: ACL tag.
373         :type node: dict
374         :type acl_idx: int
375         :type rules: list
376         :type tag: str
377         """
378         cmd = "acl_add_replace"
379         args = dict(
380             tag=tag,
381             acl_index=4294967295 if acl_idx is None else acl_idx,
382             count=len(rules),
383             r=rules
384         )
385
386         err_msg = "Failed to add/replace acls on host {host}".format(
387             host=node['host'])
388
389         with PapiExecutor(node) as papi_exec:
390             papi_exec.add(cmd, **args).get_replies(err_msg). \
391                 verify_reply(err_msg=err_msg)
392
393     @staticmethod
394     def vpp_creates_classify_table_l3(node, ip_version, direction, ip_addr):
395         """Create classify table for IP address filtering.
396
397         :param node: VPP node to create classify table.
398         :param ip_version: Version of IP protocol.
399         :param direction: Direction of traffic - src/dst.
400         :param ip_addr: IPv4 or Ipv6 (depending on the parameter 'ip_version')
401             address.
402         :type node: dict
403         :type ip_version: str
404         :type direction: str
405         :type ip_addr: str
406         :returns: (table_index, skip_n, match_n)
407             table_index: Classify table index.
408             skip_n: Number of skip vectors.
409             match_n: Number of match vectors.
410         :rtype: tuple(int, int, int)
411         :raises ValueError: If the parameters 'ip_version' or 'direction' have
412             incorrect values.
413         """
414         mask_f = dict(
415             ip4=Classify._build_ip_mask,
416             ip6=Classify._build_ip6_mask
417         )
418         if ip_version == "ip4":
419             ip_addr = binascii.hexlify(inet_aton(ip_addr))
420         elif ip_version == "ip6":
421             ip_addr = binascii.hexlify(inet_pton(AF_INET6, ip_addr))
422         else:
423             raise ValueError("IP version {ver} is not supported.".
424                              format(ver=ip_version))
425
426         if direction == "src":
427             mask = mask_f[ip_version](src_ip=ip_addr)
428         elif direction == "dst":
429             mask = mask_f[ip_version](dst_ip=ip_addr)
430         else:
431             raise ValueError("Direction {dir} is not supported.".
432                              format(dir=direction))
433
434         return Classify._classify_add_del_table(
435             node,
436             is_add=1,
437             mask=binascii.unhexlify(mask),
438             match_n_vectors=(len(mask) - 1) // 32 + 1
439         )
440
441     @staticmethod
442     def vpp_creates_classify_table_l2(node, direction, mac=""):
443         """Create classify table for MAC address filtering.
444
445         :param node: VPP node to create classify table.
446         :param direction: Direction of traffic - src/dst.
447         :param mac: Source or destination (depending on the parameter
448             'direction') MAC address.
449         :type node: dict
450         :type direction: str
451         :type mac: str
452         :returns: (table_index, skip_n, match_n)
453             table_index: Classify table index.
454             skip_n: Number of skip vectors.
455             match_n: Number of match vectors.
456         :rtype: tuple(int, int, int)
457         :raises ValueError: If the parameter 'direction' has incorrect value.
458         """
459         if direction == "src":
460             mask = Classify._build_mac_mask(src_mac=mac)
461         elif direction == "dst":
462             mask = Classify._build_mac_mask(dst_mac=mac)
463         else:
464             raise ValueError("Direction {dir} is not supported.".
465                              format(dir=direction))
466
467         return Classify._classify_add_del_table(
468             node,
469             is_add=1,
470             mask=binascii.unhexlify(mask),
471             match_n_vectors=(len(mask) - 1) // 32 + 1
472         )
473
474     @staticmethod
475     def vpp_creates_classify_table_hex(node, hex_mask):
476         """Create classify table with hex mask.
477
478         :param node: VPP node to create classify table based on hex mask.
479         :param hex_mask: Classify hex mask.
480         :type node: dict
481         :type hex_mask: str
482         :returns: (table_index, skip_n, match_n)
483             table_index: Classify table index.
484             skip_n: Number of skip vectors.
485             match_n: Number of match vectors.
486         :rtype: tuple(int, int, int)
487         """
488         return Classify._classify_add_del_table(
489             node,
490             is_add=1,
491             mask=binascii.unhexlify(hex_mask),
492             match_n_vectors=(len(hex_mask) - 1) // 32 + 1
493         )
494
495     @staticmethod
496     def vpp_configures_classify_session_l3(node, acl_method, table_index,
497                                            ip_version, direction, address):
498         """Configuration of classify session for IP address filtering.
499
500         :param node: VPP node to setup classify session.
501         :param acl_method: ACL method - deny/permit.
502         :param table_index: Classify table index.
503         :param ip_version: Version of IP protocol.
504         :param direction: Direction of traffic - src/dst.
505         :param address: IPv4 or IPv6 address.
506         :type node: dict
507         :type acl_method: str
508         :type table_index: int
509         :type ip_version: str
510         :type direction: str
511         :type address: str
512         :raises ValueError: If the parameter 'direction' has incorrect value.
513         """
514         match_f = dict(
515             ip4=Classify._build_ip_match,
516             ip6=Classify._build_ip6_match
517         )
518         if direction == "src":
519             match = match_f[ip_version](src_ip=address)
520         elif direction == "dst":
521             match = match_f[ip_version](dst_ip=address)
522         else:
523             raise ValueError("Direction {dir} is not supported.".
524                              format(dir=direction))
525         action = dict(
526             permit=0,
527             deny=1
528         )
529         Classify._classify_add_del_session(
530             node,
531             is_add=1,
532             table_index=table_index,
533             match=binascii.unhexlify(match),
534             action=action[acl_method])
535
536     @staticmethod
537     def vpp_configures_classify_session_l2(node, acl_method, table_index,
538                                            direction, address):
539         """Configuration of classify session for MAC address filtering.
540
541         :param node: VPP node to setup classify session.
542         :param acl_method: ACL method - deny/permit.
543         :param table_index: Classify table index.
544         :param direction: Direction of traffic - src/dst.
545         :param address: MAC address.
546         :type node: dict
547         :type acl_method: str
548         :type table_index: int
549         :type direction: str
550         :type address: str
551         :raises ValueError: If the parameter 'direction' has incorrect value.
552         """
553         if direction == "src":
554             match = Classify._build_mac_match(src_mac=address)
555         elif direction == "dst":
556             match = Classify._build_mac_match(dst_mac=address)
557         else:
558             raise ValueError("Direction {dir} is not supported.".
559                              format(dir=direction))
560         action = dict(
561             permit=0,
562             deny=1
563         )
564         Classify._classify_add_del_session(
565             node,
566             is_add=1,
567             table_index=table_index,
568             match=binascii.unhexlify(match),
569             action=action[acl_method])
570
571     @staticmethod
572     def vpp_configures_classify_session_hex(node, acl_method, table_index,
573                                             hex_value):
574         """Configuration of classify session with hex value.
575
576         :param node: VPP node to setup classify session.
577         :param acl_method: ACL method - deny/permit.
578         :param table_index: Classify table index.
579         :param hex_value: Classify hex value.
580         :type node: dict
581         :type acl_method: str
582         :type table_index: int
583         :type hex_value: str
584         """
585         action = dict(
586             permit=0,
587             deny=1
588         )
589         Classify._classify_add_del_session(
590             node,
591             is_add=1,
592             table_index=table_index,
593             match=binascii.unhexlify(hex_value),
594             action=action[acl_method])
595
596     @staticmethod
597     def compute_classify_hex_mask(ip_version, protocol, direction):
598         """Compute classify hex mask for TCP or UDP packet matching.
599
600         :param ip_version: Version of IP protocol.
601         :param protocol: Type of protocol.
602         :param direction: Traffic direction.
603         :type ip_version: str
604         :type protocol: str
605         :type direction: str
606         :returns: Classify hex mask.
607         :rtype: str
608         :raises ValueError: If protocol is not TCP or UDP.
609         :raises ValueError: If direction is not source or destination or
610             source + destination.
611         """
612         if protocol in ('TCP', 'UDP'):
613             base_mask = Classify._compute_base_mask(ip_version)
614
615             if direction == 'source':
616                 return base_mask + 'FFFF0000'
617             elif direction == 'destination':
618                 return base_mask + '0000FFFF'
619             elif direction == 'source + destination':
620                 return base_mask + 'FFFFFFFF'
621             else:
622                 raise ValueError("Invalid direction!")
623         else:
624             raise ValueError("Invalid protocol!")
625
626     @staticmethod
627     def compute_classify_hex_value(hex_mask, source_port, destination_port):
628         """Compute classify hex value for TCP or UDP packet matching.
629
630         :param hex_mask: Classify hex mask.
631         :param source_port: Source TCP/UDP port.
632         :param destination_port: Destination TCP/UDP port.
633         :type hex_mask: str
634         :type source_port: str
635         :type destination_port: str
636         :returns: Classify hex value.
637         :rtype: str
638         """
639         source_port_hex = Classify._port_convert(source_port)
640         destination_port_hex = Classify._port_convert(destination_port)
641
642         return hex_mask[:-8] + source_port_hex + destination_port_hex
643
644     @staticmethod
645     def _port_convert(port):
646         """Convert port number for classify hex table format.
647
648         :param port: TCP/UDP port number.
649         :type port: str
650         :returns: TCP/UDP port number in 4-digit hexadecimal format.
651         :rtype: str
652         """
653         return '{0:04x}'.format(int(port))
654
655     @staticmethod
656     def _compute_base_mask(ip_version):
657         """Compute base classify hex mask based on IP version.
658
659         :param ip_version: Version of IP protocol.
660         :type ip_version: str
661         :returns: Base hex mask.
662         :rtype: str
663         """
664         if ip_version == 'ip4':
665             return 68 * '0'
666             # base value of classify hex table for IPv4 TCP/UDP ports
667         elif ip_version == 'ip6':
668             return 108 * '0'
669             # base value of classify hex table for IPv6 TCP/UDP ports
670         else:
671             raise ValueError("Invalid IP version!")
672
673     @staticmethod
674     def get_classify_table_data(node, table_index):
675         """Retrieve settings for classify table by ID.
676
677         :param node: VPP node to retrieve classify data from.
678         :param table_index: Index of a specific classify table.
679         :type node: dict
680         :type table_index: int
681         :returns: Classify table settings.
682         :rtype: dict
683         """
684         cmd = 'classify_table_info'
685         err_msg = "Failed to get 'classify_table_info' on host {host}".format(
686             host=node['host'])
687         args = dict(
688             table_id=int(table_index)
689         )
690         with PapiExecutor(node) as papi_exec:
691             data = papi_exec.add(cmd, **args).get_replies(err_msg).\
692                 verify_reply(err_msg=err_msg)
693
694         return data
695
696     @staticmethod
697     def get_classify_session_data(node, table_index):
698         """Retrieve settings for all classify sessions in a table.
699
700         :param node: VPP node to retrieve classify data from.
701         :param table_index: Index of a classify table.
702         :type node: dict
703         :type table_index: int
704         :returns: List of classify session settings.
705         :rtype: list or dict
706         """
707         args = dict(
708             table_id=int(table_index)
709         )
710         with PapiExecutor(node) as papi_exec:
711             dump = papi_exec.add("classify_session_dump", **args).\
712                 get_dump().reply[0]["api_reply"]["classify_session_details"]
713
714         return dump
715
716     @staticmethod
717     def vpp_log_plugin_acl_settings(node):
718         """Retrieve configured settings from the ACL plugin and write to robot
719         log.
720
721         :param node: VPP node.
722         :type node: dict
723         """
724         PapiExecutor.dump_and_log(node, ["acl_dump", ])
725
726     @staticmethod
727     def vpp_log_plugin_acl_interface_assignment(node):
728         """Retrieve interface assignment from the ACL plugin and write to robot
729         log.
730
731         :param node: VPP node.
732         :type node: dict
733         """
734         PapiExecutor.dump_and_log(node, ["acl_interface_list_dump", ])
735
736     @staticmethod
737     def set_acl_list_for_interface(node, interface, acl_type, acl_idx=None):
738         """Set the list of input or output ACLs applied to the interface. It
739         unapplies any previously applied ACLs.
740
741         :param node: VPP node to set ACL on.
742         :param interface: Interface name or sw_if_index.
743         :param acl_type: Type of ACL(s) - input or output.
744         :param acl_idx: Index(ies) of ACLs to be applied on the interface.
745         :type node: dict
746         :type interface: str or int
747         :type acl_type: str
748         :type acl_idx: list
749         """
750         if isinstance(interface, basestring):
751             sw_if_index = Topology.get_interface_sw_index(node, interface)
752         else:
753             sw_if_index = int(interface)
754
755         acls = acl_idx if isinstance(acl_idx, list) else list()
756
757         Classify._acl_interface_set_acl_list(node=node,
758                                              sw_if_index=sw_if_index,
759                                              acl_type=acl_type,
760                                              acls=acls)
761
762     @staticmethod
763     def add_replace_acl_multi_entries(node, acl_idx=None, rules=None):
764         """Add a new ACL or replace the existing one. To replace an existing
765         ACL, pass the ID of this ACL.
766
767         :param node: VPP node to set ACL on.
768         :param acl_idx: ID of ACL. (Optional)
769         :param rules: Required rules. (Optional)
770         :type node: dict
771         :type acl_idx: int
772         :type rules: str
773         """
774         reg_ex_src_ip = re.compile(r'(src [0-9a-fA-F.:/\d{1,2}]*)')
775         reg_ex_dst_ip = re.compile(r'(dst [0-9a-fA-F.:/\d{1,2}]*)')
776         reg_ex_sport = re.compile(r'(sport \d{1,5})')
777         reg_ex_dport = re.compile(r'(dport \d{1,5})')
778
779         acl_rules = list()
780         for rule in rules.split(", "):
781             acl_rule = dict()
782             acl_rule["is_permit"] = 1 if "permit" in rule else 0
783             acl_rule["is_ipv6"] = 1 if "ipv6" in rule else 0
784
785             groups = re.search(reg_ex_src_ip, rule)
786             if groups:
787                 grp = groups.group(1).split(' ')[1].split('/')
788                 acl_rule["src_ip_addr"] = str(inet_pton(
789                     AF_INET6 if acl_rule["is_ipv6"] else AF_INET, grp[0]))
790                 acl_rule["src_ip_prefix_len"] = int(grp[1])
791
792             groups = re.search(reg_ex_dst_ip, rule)
793             if groups:
794                 grp = groups.group(1).split(' ')[1].split('/')
795                 acl_rule["dst_ip_addr"] = str(inet_pton(
796                     AF_INET6 if acl_rule["is_ipv6"] else AF_INET, grp[0]))
797                 acl_rule["dst_ip_prefix_len"] = int(grp[1])
798
799             groups = re.search(reg_ex_sport, rule)
800             if groups:
801                 port = int(groups.group(1).split(' ')[1])
802                 acl_rule["srcport_or_icmptype_first"] = port
803                 acl_rule["srcport_or_icmptype_last"] = port
804
805             groups = re.search(reg_ex_dport, rule)
806             if groups:
807                 port = int(groups.group(1).split(' ')[1])
808                 acl_rule["dstport_or_icmpcode_first"] = port
809                 acl_rule["dstport_or_icmpcode_last"] = port
810
811             acl_rule["proto"] = 0
812
813             acl_rules.append(acl_rule)
814
815         Classify._acl_add_replace(node, acl_idx=acl_idx, rules=acl_rules)
816
817     @staticmethod
818     def add_macip_acl_multi_entries(node, rules=""):
819         """Add a new MACIP ACL.
820
821         :param node: VPP node to set MACIP ACL on.
822         :param rules: Required MACIP rules.
823         :type node: dict
824         :type rules: str
825         """
826         reg_ex_ip = re.compile(r'(ip [0-9a-fA-F.:/\d{1,2}]*)')
827         reg_ex_mac = re.compile(r'(mac \S\S:\S\S:\S\S:\S\S:\S\S:\S\S)')
828         reg_ex_mask = re.compile(r'(mask \S\S:\S\S:\S\S:\S\S:\S\S:\S\S)')
829
830         acl_rules = list()
831         for rule in rules.split(", "):
832             acl_rule = dict()
833             acl_rule["is_permit"] = 1 if "permit" in rule else 0
834             acl_rule["is_ipv6"] = 1 if "ipv6" in rule else 0
835
836             groups = re.search(reg_ex_mac, rule)
837             if groups:
838                 mac = groups.group(1).split(' ')[1].replace(':', '')
839                 acl_rule["src_mac"] = unicode(mac)
840
841             groups = re.search(reg_ex_mask, rule)
842             if groups:
843                 mask = groups.group(1).split(' ')[1].replace(':', '')
844                 acl_rule["src_mac_mask"] = unicode(mask)
845
846             groups = re.search(reg_ex_ip, rule)
847             if groups:
848                 grp = groups.group(1).split(' ')[1].split('/')
849                 acl_rule["src_ip_addr"] = str(inet_pton(
850                     AF_INET6 if acl_rule["is_ipv6"] else AF_INET, grp[0]))
851                 acl_rule["src_ip_prefix_len"] = int(grp[1])
852
853             acl_rules.append(acl_rule)
854
855         Classify._macip_acl_add(node=node, rules=acl_rules)
856
857     @staticmethod
858     def vpp_log_macip_acl_settings(node):
859         """Retrieve configured MACIP settings from the ACL plugin and write to
860         robot log.
861
862         :param node: VPP node.
863         :type node: dict
864         """
865         PapiExecutor.dump_and_log(node, ["macip_acl_dump", ])
866
867     @staticmethod
868     def add_del_macip_acl_interface(node, interface, action, acl_idx):
869         """Apply/un-apply the MACIP ACL to/from a given interface.
870
871         :param node: VPP node to set MACIP ACL on.
872         :param interface: Interface name or sw_if_index.
873         :param action: Required action - add or del.
874         :param acl_idx: ACL index to be applied on the interface.
875         :type node: dict
876         :type interface: str or int
877         :type action: str
878         :type acl_idx: str or int
879         :raises RuntimeError: If unable to set MACIP ACL for the interface.
880         """
881         if isinstance(interface, basestring):
882             sw_if_index = Topology.get_interface_sw_index(node, interface)
883         else:
884             sw_if_index = interface
885
886         is_add = 1 if action == "add" else 0
887
888         cmd = 'macip_acl_interface_add_del'
889         err_msg = "Failed to get 'macip_acl_interface' on host {host}".format(
890             host=node['host'])
891         args = dict(
892             is_add=is_add,
893             sw_if_index=int(sw_if_index),
894             acl_index=int(acl_idx)
895         )
896         with PapiExecutor(node) as papi_exec:
897             papi_exec.add(cmd, **args).get_replies(err_msg).\
898                 verify_reply(err_msg=err_msg)
899
900     @staticmethod
901     def vpp_log_macip_acl_interface_assignment(node):
902         """Get interface list and associated MACIP ACLs and write to robot log.
903
904         :param node: VPP node.
905         :type node: dict
906         """
907         cmd = 'macip_acl_interface_get'
908         err_msg = "Failed to get 'macip_acl_interface' on host {host}".format(
909             host=node['host'])
910         with PapiExecutor(node) as papi_exec:
911             rpl = papi_exec.add(cmd).get_replies(err_msg).reply[0]["api_reply"]
912         logger.info(rpl["macip_acl_interface_get_reply"])