1 # Copyright (c) 2018 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:
6 # http://www.apache.org/licenses/LICENSE-2.0
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.
14 """Classify utilities library."""
16 from robot.api import logger
18 from resources.libraries.python.VatExecutor import VatExecutor, VatTerminal
19 from resources.libraries.python.topology import Topology
22 class Classify(object):
23 """Classify utilities."""
26 def vpp_creates_classify_table_l3(node, ip_version, direction):
27 """Create classify table for IP address filtering.
29 :param node: VPP node to create classify table.
30 :param ip_version: Version of IP protocol.
31 :param direction: Direction of traffic - src/dst.
35 :returns: (table_index, skip_n, match_n)
36 table_index: Classify table index.
37 skip_n: Number of skip vectors.
38 match_n: Number of match vectors.
39 :rtype: tuple(int, int, int)
40 :raises RuntimeError: If VPP can't create table.
43 output = VatExecutor.cmd_from_template(node, "classify_add_table.vat",
44 ip_version=ip_version,
47 if output[0]["retval"] == 0:
48 table_index = output[0]["new_table_index"]
49 skip_n = output[0]["skip_n_vectors"]
50 match_n = output[0]["match_n_vectors"]
51 logger.trace('Classify table with table_index {} created on node {}'
52 .format(table_index, node['host']))
54 raise RuntimeError('Unable to create classify table on node {}'
55 .format(node['host']))
57 return table_index, skip_n, match_n
60 def vpp_creates_classify_table_l2(node, direction):
61 """Create classify table for MAC address filtering.
63 :param node: VPP node to create classify table.
64 :param direction: Direction of traffic - src/dst.
67 :returns: (table_index, skip_n, match_n)
68 table_index: Classify table index.
69 skip_n: Number of skip vectors.
70 match_n: Number of match vectors.
71 :rtype: tuple(int, int, int)
72 :raises RuntimeError: If VPP can't create table.
74 output = VatExecutor.cmd_from_template(node,
75 "classify_add_table_l2.vat",
78 if output[0]["retval"] == 0:
79 table_index = output[0]["new_table_index"]
80 skip_n = output[0]["skip_n_vectors"]
81 match_n = output[0]["match_n_vectors"]
82 logger.trace('Classify table with table_index {} created on node {}'
83 .format(table_index, node['host']))
85 raise RuntimeError('Unable to create classify table on node {}'
86 .format(node['host']))
88 return table_index, skip_n, match_n
91 def vpp_creates_classify_table_hex(node, hex_mask):
92 """Create classify table with hex mask.
94 :param node: VPP node to create classify table based on hex mask.
95 :param hex_mask: Classify hex mask.
98 :returns: (table_index, skip_n, match_n)
99 table_index: Classify table index.
100 skip_n: Number of skip vectors.
101 match_n: Number of match vectors.
102 :rtype: tuple(int, int, int)
103 :raises RuntimeError: If VPP can't create table.
105 output = VatExecutor.cmd_from_template(node,
106 "classify_add_table_hex.vat",
109 if output[0]["retval"] == 0:
110 table_index = output[0]["new_table_index"]
111 skip_n = output[0]["skip_n_vectors"]
112 match_n = output[0]["match_n_vectors"]
113 logger.trace('Classify table with table_index {} created on node {}'
114 .format(table_index, node['host']))
116 raise RuntimeError('Unable to create classify table on node {}'
117 .format(node['host']))
119 return table_index, skip_n, match_n
122 def vpp_configures_classify_session_l3(node, acl_method, table_index,
123 skip_n, match_n, ip_version,
125 """Configuration of classify session for IP address filtering.
127 :param node: VPP node to setup classify session.
128 :param acl_method: ACL method - deny/permit.
129 :param table_index: Classify table index.
130 :param skip_n: Number of skip vectors based on mask.
131 :param match_n: Number of match vectors based on mask.
132 :param ip_version: Version of IP protocol.
133 :param direction: Direction of traffic - src/dst.
134 :param address: IPv4 or IPv6 address.
136 :type acl_method: str
137 :type table_index: int
140 :type ip_version: str
144 with VatTerminal(node) as vat:
145 vat.vat_terminal_exec_cmd_from_template("classify_add_session.vat",
146 acl_method=acl_method,
147 table_index=table_index,
150 ip_version=ip_version,
155 def vpp_configures_classify_session_l2(node, acl_method, table_index,
156 skip_n, match_n, direction, address):
157 """Configuration of classify session for MAC address filtering.
159 :param node: VPP node to setup classify session.
160 :param acl_method: ACL method - deny/permit.
161 :param table_index: Classify table index.
162 :param skip_n: Number of skip vectors based on mask.
163 :param match_n: Number of match vectors based on mask.
164 :param direction: Direction of traffic - src/dst.
165 :param address: IPv4 or IPv6 address.
167 :type acl_method: str
168 :type table_index: int
174 with VatTerminal(node) as vat:
175 vat.vat_terminal_exec_cmd_from_template(
176 "classify_add_session_l2.vat",
177 acl_method=acl_method,
178 table_index=table_index,
185 def vpp_configures_classify_session_hex(node, acl_method, table_index,
186 skip_n, match_n, hex_value):
187 """Configuration of classify session with hex value.
189 :param node: VPP node to setup classify session.
190 :param acl_method: ACL method - deny/permit.
191 :param table_index: Classify table index.
192 :param skip_n: Number of skip vectors based on mask.
193 :param match_n: Number of match vectors based on mask.
194 :param hex_value: Classify hex value.
196 :type acl_method: str
197 :type table_index: int
202 with VatTerminal(node) as vat:
203 vat.vat_terminal_exec_cmd_from_template(
204 "classify_add_session_hex.vat",
205 acl_method=acl_method,
206 table_index=table_index,
212 def vpp_configures_classify_session_generic(node, session_type, table_index,
213 skip_n, match_n, match,
215 """Configuration of classify session.
217 :param node: VPP node to setup classify session.
218 :param session_type: Session type - hit-next, l2-hit-next, acl-hit-next
219 or policer-hit-next, and their respective parameters.
220 :param table_index: Classify table index.
221 :param skip_n: Number of skip vectors based on mask.
222 :param match_n: Number of match vectors based on mask.
223 :param match: Match value - l2, l3, l4 or hex, and their
224 respective parameters.
225 :param match2: Additional match values, to avoid using overly long
226 variables in RobotFramework.
228 :type session_type: str
229 :type table_index: int
236 match = ' '.join((match, match2))
238 with VatTerminal(node) as vat:
239 vat.vat_terminal_exec_cmd_from_template(
240 "classify_add_session_generic.vat",
242 table_index=table_index,
249 def compute_classify_hex_mask(ip_version, protocol, direction):
250 """Compute classify hex mask for TCP or UDP packet matching.
252 :param ip_version: Version of IP protocol.
253 :param protocol: Type of protocol.
254 :param direction: Traffic direction.
255 :type ip_version: str
258 :returns: Classify hex mask.
260 :raises ValueError: If protocol is not TCP or UDP.
261 :raises ValueError: If direction is not source or destination or
262 source + destination.
264 if protocol == 'TCP' or protocol == 'UDP':
265 base_mask = Classify._compute_base_mask(ip_version)
267 if direction == 'source':
268 return base_mask + 'FFFF0000'
269 elif direction == 'destination':
270 return base_mask + '0000FFFF'
271 elif direction == 'source + destination':
272 return base_mask + 'FFFFFFFF'
274 raise ValueError("Invalid direction!")
276 raise ValueError("Invalid protocol!")
279 def compute_classify_hex_value(hex_mask, source_port, destination_port):
280 """Compute classify hex value for TCP or UDP packet matching.
282 :param hex_mask: Classify hex mask.
283 :param source_port: Source TCP/UDP port.
284 :param destination_port: Destination TCP/UDP port.
286 :type source_port: str
287 :type destination_port: str
288 :returns: Classify hex value.
291 source_port_hex = Classify._port_convert(source_port)
292 destination_port_hex = Classify._port_convert(destination_port)
294 return hex_mask[:-8] + source_port_hex + destination_port_hex
297 def _port_convert(port):
298 """Convert port number for classify hex table format.
300 :param port: TCP/UDP port number.
302 :returns: TCP/UDP port number in 4-digit hexadecimal format.
305 return '{0:04x}'.format(int(port))
308 def _compute_base_mask(ip_version):
309 """Compute base classify hex mask based on IP version.
311 :param ip_version: Version of IP protocol.
312 :type ip_version: str
313 :returns: Base hex mask.
316 if ip_version == 'ip4':
318 # base value of classify hex table for IPv4 TCP/UDP ports
319 elif ip_version == 'ip6':
321 # base value of classify hex table for IPv6 TCP/UDP ports
323 raise ValueError("Invalid IP version!")
326 def get_classify_table_data(node, table_index):
327 """Retrieve settings for classify table by ID.
329 :param node: VPP node to retrieve classify data from.
330 :param table_index: Index of a specific classify table.
332 :type table_index: int
333 :returns: Classify table settings.
336 with VatTerminal(node) as vat:
337 data = vat.vat_terminal_exec_cmd_from_template(
338 "classify_table_info.vat",
344 def get_classify_session_data(node, table_index, session_index=None):
345 """Retrieve settings for all classify sessions in a table,
346 or for a specific classify session.
348 :param node: VPP node to retrieve classify data from.
349 :param table_index: Index of a classify table.
350 :param session_index: Index of a specific classify session. (Optional)
352 :type table_index: int
353 :type session_index: int
354 :returns: List of classify session settings, or a dictionary of settings
355 for a specific classify session.
358 with VatTerminal(node) as vat:
359 data = vat.vat_terminal_exec_cmd_from_template(
360 "classify_session_dump.vat",
363 if session_index is not None:
364 return data[0][session_index]
368 def vpp_log_plugin_acl_settings(node):
369 """Retrieve configured settings from the ACL plugin
370 and write to robot log.
372 :param node: VPP node.
376 VatExecutor.cmd_from_template(
377 node, "acl_plugin/acl_dump.vat")
378 except (ValueError, RuntimeError):
379 # Fails to parse JSON data in response, but it is still logged
383 def vpp_log_plugin_acl_interface_assignment(node):
384 """Retrieve interface assignment from the ACL plugin
385 and write to robot log.
387 :param node: VPP node.
391 VatExecutor.cmd_from_template(
392 node, "acl_plugin/acl_interface_dump.vat", json_out=False)
394 # Fails to parse response, but it is still logged
398 def set_acl_list_for_interface(node, interface, acl_type, acl_idx=None):
399 """Set the list of input or output ACLs applied to the interface. It
400 unapplies any previously applied ACLs.
402 :param node: VPP node to set ACL on.
403 :param interface: Interface name or sw_if_index.
404 :param acl_type: Type of ACL(s) - input or output.
405 :param acl_idx: Index(ies) of ACLs to be applied on the interface.
407 :type interface: str or int
410 :raises RuntimeError: If unable to set ACL list for the interface.
412 if isinstance(interface, basestring):
413 sw_if_index = Topology.get_interface_sw_index(node, interface)
415 sw_if_index = interface
417 acl_list = acl_type + ' ' + ' '.join(str(idx) for idx in acl_idx) \
418 if acl_idx else acl_type
421 with VatTerminal(node, json_param=False) as vat:
422 vat.vat_terminal_exec_cmd_from_template(
423 "acl_plugin/acl_interface_set_acl_list.vat",
424 interface=sw_if_index, acl_list=acl_list)
426 raise RuntimeError("Setting of ACL list for interface {0} failed "
427 "on node {1}".format(interface, node['host']))
430 def add_replace_acl(node, acl_idx=None, ip_ver="ipv4", action="permit",
431 src=None, dst=None, sport=None, dport=None, proto=None,
432 tcpflg_val=None, tcpflg_mask=None):
433 """Add a new ACL or replace the existing one. To replace an existing
434 ACL, pass the ID of this ACL.
436 :param node: VPP node to set ACL on.
437 :param acl_idx: ID of ACL. (Optional)
438 :param ip_ver: IP version. (Optional)
439 :param action: ACL action. (Optional)
440 :param src: Source IP in format IP/plen. (Optional)
441 :param dst: Destination IP in format IP/plen. (Optional)
442 :param sport: Source port or ICMP4/6 type - range format X-Y allowed.
444 :param dport: Destination port or ICMP4/6 code - range format X-Y
446 :param proto: L4 protocol (http://www.iana.org/assignments/protocol-
447 numbers/protocol-numbers.xhtml). (Optional)
448 :param tcpflg_val: TCP flags value. (Optional)
449 :param tcpflg_mask: TCP flags mask. (Optional)
456 :type sport: str or int
457 :type dport: str or int
459 :type tcpflg_val: int
460 :type tcpflg_mask: int
461 :raises RuntimeError: If unable to add or replace ACL.
463 acl_idx = '{0}'.format(acl_idx) if acl_idx else ''
465 src = 'src {0}'.format(src) if src else ''
467 dst = 'dst {0}'.format(dst) if dst else ''
469 sport = 'sport {0}'.format(sport) if sport else ''
471 dport = 'dport {0}'.format(dport) if dport else ''
473 proto = 'proto {0}'.format(proto) if proto else ''
475 tcpflags = 'tcpflags {0} {1}'.format(tcpflg_val, tcpflg_mask) \
476 if tcpflg_val and tcpflg_mask else ''
479 with VatTerminal(node, json_param=False) as vat:
480 vat.vat_terminal_exec_cmd_from_template(
481 "acl_plugin/acl_add_replace.vat", acl_idx=acl_idx,
482 ip_ver=ip_ver, action=action, src=src, dst=dst, sport=sport,
483 dport=dport, proto=proto, tcpflags=tcpflags)
485 raise RuntimeError("Adding or replacing of ACL failed on "
486 "node {0}".format(node['host']))
489 def add_replace_acl_multi_entries(node, acl_idx=None, rules=None):
490 """Add a new ACL or replace the existing one. To replace an existing
491 ACL, pass the ID of this ACL.
493 :param node: VPP node to set ACL on.
494 :param acl_idx: ID of ACL. (Optional)
495 :param rules: Required rules. (Optional)
499 :raises RuntimeError: If unable to add or replace ACL.
501 acl_idx = '{0}'.format(acl_idx) if acl_idx else ''
503 rules = '{0}'.format(rules) if rules else ''
506 with VatTerminal(node, json_param=False) as vat:
507 vat.vat_terminal_exec_cmd_from_template(
508 "acl_plugin/acl_add_replace.vat", acl_idx=acl_idx,
509 ip_ver=rules, action='', src='', dst='', sport='',
510 dport='', proto='', tcpflags='')
512 raise RuntimeError("Adding or replacing of ACL failed on "
513 "node {0}".format(node['host']))
516 def delete_acl(node, idx):
517 """Delete required ACL.
519 :param node: VPP node to delete ACL on.
520 :param idx: Index of ACL to be deleted.
522 :type idx: int or str
523 :raises RuntimeError: If unable to delete ACL.
526 with VatTerminal(node, json_param=False) as vat:
527 vat.vat_terminal_exec_cmd_from_template(
528 "acl_plugin/acl_delete.vat", idx=idx)
530 raise RuntimeError("Deletion of ACL failed on node {0}".
531 format(node['host']))
534 def cli_show_acl(node, acl_idx=None):
537 :param node: VPP node to show ACL on.
538 :param acl_idx: Index of ACL to be shown.
540 :type acl_idx: int or str
541 :raises RuntimeError: If unable to delete ACL.
543 acl_idx = '{0}'.format(acl_idx) if acl_idx else ''
546 with VatTerminal(node, json_param=False) as vat:
547 vat.vat_terminal_exec_cmd_from_template(
548 "acl_plugin/show_acl.vat", idx=acl_idx)
550 raise RuntimeError("Failed to show ACL on node {0}".
551 format(node['host']))
554 def add_macip_acl(node, ip_ver="ipv4", action="permit", src_ip=None,
555 src_mac=None, src_mac_mask=None):
556 """Add a new MACIP ACL.
558 :param node: VPP node to set MACIP ACL on.
559 :param ip_ver: IP version. (Optional)
560 :param action: ACL action. (Optional)
561 :param src_ip: Source IP in format IP/plen. (Optional)
562 :param src_mac: Source MAC address in format with colons. (Optional)
563 :param src_mac_mask: Source MAC address mask in format with colons.
564 00:00:00:00:00:00 is a wildcard mask. (Optional)
570 :type src_mac_mask: str
571 :raises RuntimeError: If unable to add MACIP ACL.
573 src_ip = 'ip {0}'.format(src_ip) if src_ip else ''
575 src_mac = 'mac {0}'.format(src_mac) if src_mac else ''
577 src_mac_mask = 'mask {0}'.format(src_mac_mask) if src_mac_mask else ''
580 with VatTerminal(node, json_param=False) as vat:
581 vat.vat_terminal_exec_cmd_from_template(
582 "acl_plugin/macip_acl_add.vat", ip_ver=ip_ver,
583 action=action, src_ip=src_ip, src_mac=src_mac,
584 src_mac_mask=src_mac_mask)
586 raise RuntimeError("Adding of MACIP ACL failed on node {0}".
587 format(node['host']))
590 def add_macip_acl_multi_entries(node, rules=None):
591 """Add a new MACIP ACL.
593 :param node: VPP node to set MACIP ACL on.
594 :param rules: Required MACIP rules. (Optional)
597 :raises RuntimeError: If unable to add MACIP ACL.
599 rules = '{0}'.format(rules) if rules else ''
602 with VatTerminal(node, json_param=False) as vat:
603 vat.vat_terminal_exec_cmd_from_template(
604 "acl_plugin/macip_acl_add.vat", ip_ver=rules, action='',
605 src_ip='', src_mac='', src_mac_mask='')
607 raise RuntimeError("Adding of MACIP ACL failed on node {0}".
608 format(node['host']))
611 def delete_macip_acl(node, idx):
612 """Delete required MACIP ACL.
614 :param node: VPP node to delete MACIP ACL on.
615 :param idx: Index of ACL to be deleted.
617 :type idx: int or str
618 :raises RuntimeError: If unable to delete MACIP ACL.
621 with VatTerminal(node, json_param=False) as vat:
622 vat.vat_terminal_exec_cmd_from_template(
623 "acl_plugin/macip_acl_delete.vat", idx=idx)
625 raise RuntimeError("Deletion of MACIP ACL failed on node {0}".
626 format(node['host']))
629 def vpp_log_macip_acl_settings(node):
630 """Retrieve configured MACIP settings from the ACL plugin
631 and write to robot log.
633 :param node: VPP node.
637 VatExecutor.cmd_from_template(
638 node, "acl_plugin/macip_acl_dump.vat")
639 except (ValueError, RuntimeError):
640 # Fails to parse JSON data in response, but it is still logged
644 def add_del_macip_acl_interface(node, interface, action, acl_idx):
645 """Apply/un-apply the MACIP ACL to/from a given interface.
647 :param node: VPP node to set MACIP ACL on.
648 :param interface: Interface name or sw_if_index.
649 :param action: Required action - add or del.
650 :param acl_idx: ACL index to be applied on the interface.
652 :type interface: str or int
654 :type acl_idx: str or int
655 :raises RuntimeError: If unable to set MACIP ACL for the interface.
657 if isinstance(interface, basestring):
658 sw_if_index = Topology.get_interface_sw_index(node, interface)
660 sw_if_index = interface
663 with VatTerminal(node, json_param=False) as vat:
664 vat.vat_terminal_exec_cmd_from_template(
665 "acl_plugin/macip_acl_interface_add_del.vat",
666 sw_if_index=sw_if_index, action=action, acl_idx=acl_idx)
668 raise RuntimeError("Setting of MACIP ACL index for interface {0} "
669 "failed on node {1}".
670 format(interface, node['host']))
673 def vpp_log_macip_acl_interface_assignment(node):
674 """Get interface list and associated MACIP ACLs and write to robot log.
676 :param node: VPP node.
680 VatExecutor.cmd_from_template(
681 node, "acl_plugin/macip_acl_interface_get.vat", json_out=False)
683 # Fails to parse response, but it is still logged