diff options
Diffstat (limited to 'resources/libraries/python/Classify.py')
-rw-r--r-- | resources/libraries/python/Classify.py | 347 |
1 files changed, 113 insertions, 234 deletions
diff --git a/resources/libraries/python/Classify.py b/resources/libraries/python/Classify.py index 6f05a6157d..6d4b84c1cc 100644 --- a/resources/libraries/python/Classify.py +++ b/resources/libraries/python/Classify.py @@ -20,6 +20,7 @@ from ipaddress import ip_address from robot.api import logger +from resources.libraries.python.Constants import Constants from resources.libraries.python.topology import Topology from resources.libraries.python.PapiExecutor import PapiSocketExecutor @@ -40,16 +41,10 @@ class Classify(object): :returns MAC ACL mask in hexstring format. :rtype: str """ - if ether_type: - end = 28 - elif src_mac: - end = 24 - else: - end = 12 return ('{!s:0>12}{!s:0>12}{!s:0>4}'.format( dst_mac.replace(':', ''), src_mac.replace(':', ''), - ether_type))[0:end] + ether_type)).decode('hex').rstrip('\0') @staticmethod def _build_ip_mask(proto='', src_ip='', dst_ip='', src_port='', @@ -69,19 +64,10 @@ class Classify(object): :returns: IP mask in hexstring format. :rtype: str """ - if dst_port: - end = 48 - elif src_port: - end = 44 - elif dst_ip: - end = 40 - elif src_ip: - end = 32 - else: - end = 20 return ('{!s:0>20}{!s:0>12}{!s:0>8}{!s:0>4}{!s:0>4}'.format( - proto, src_ip, dst_ip, src_port, dst_port))[0:end] + proto, src_ip, dst_ip, src_port, dst_port)).decode('hex').\ + rstrip('\0') @staticmethod def _build_ip6_mask(next_hdr='', src_ip='', dst_ip='', src_port='', @@ -101,19 +87,10 @@ class Classify(object): :returns: IPv6 ACL mask in hexstring format. :rtype: str """ - if dst_port: - end = 88 - elif src_port: - end = 84 - elif dst_ip: - end = 80 - elif src_ip: - end = 48 - else: - end = 14 return ('{!s:0>14}{!s:0>34}{!s:0>32}{!s:0>4}{!s:0>4}'.format( - next_hdr, src_ip, dst_ip, src_port, dst_port))[0:end] + next_hdr, src_ip, dst_ip, src_port, dst_port)).decode('hex').\ + rstrip('\0') @staticmethod def _build_mac_match(dst_mac='', src_mac='', ether_type=''): @@ -128,24 +105,18 @@ class Classify(object): :returns: MAC ACL match data in hexstring format. :rtype: str """ - if ether_type: - end = 28 - elif src_mac: - end = 24 - else: - end = 12 return ('{!s:0>12}{!s:0>12}{!s:0>4}'.format( dst_mac.replace(':', ''), src_mac.replace(':', ''), - ether_type))[0:end] + ether_type)).decode('hex').rstrip('\0') @staticmethod def _build_ip_match(proto=0, src_ip='', dst_ip='', src_port=0, dst_port=0): - """Build IP ACL match data in hexstring format. + """Build IP ACL match data in byte-string format. :param proto: Protocol number with valid option "x". - :param src_ip: Source ip address with format of "x.x.x.x". - :param dst_ip: Destination ip address with format of "x.x.x.x". + :param src_ip: Source ip address in packed format. + :param dst_ip: Destination ip address in packed format. :param src_port: Source port number "x". :param dst_port: Destination port number "x". :type proto: int @@ -153,37 +124,22 @@ class Classify(object): :type dst_ip: str :type src_port: int :type dst_port: int - :returns: IP ACL match data in hexstring format. + :returns: IP ACL match data in byte-string format. :rtype: str """ - if src_ip: - src_ip = binascii.hexlify(ip_address(unicode(src_ip)).packed) - if dst_ip: - dst_ip = binascii.hexlify(ip_address(unicode(dst_ip)).packed) - if dst_port: - end = 48 - elif src_port: - end = 44 - elif dst_ip: - end = 40 - elif src_ip: - end = 32 - else: - end = 20 return ('{!s:0>20}{!s:0>12}{!s:0>8}{!s:0>4}{!s:0>4}'.format( hex(proto)[2:], src_ip, dst_ip, hex(src_port)[2:], - hex(dst_port)[2:]))[0:end] + hex(dst_port)[2:])).decode('hex').rstrip('\0') @staticmethod def _build_ip6_match(next_hdr=0, src_ip='', dst_ip='', src_port=0, dst_port=0): - """Build IPv6 ACL match data in hexstring format. + """Build IPv6 ACL match data in byte-string format. :param next_hdr: Next header number with valid option "x". - :param src_ip: Source ip6 address with format of "xxxx:xxxx::xxxx". - :param dst_ip: Destination ip6 address with format of - "xxxx:xxxx::xxxx". + :param src_ip: Source ip6 address in packed format. + :param dst_ip: Destination ip6 address in packed format. :param src_port: Source port number "x". :param dst_port: Destination port number "x". :type next_hdr: int @@ -191,52 +147,36 @@ class Classify(object): :type dst_ip: str :type src_port: int :type dst_port: int - :returns: IPv6 ACL match data in hexstring format. + :returns: IPv6 ACL match data in byte-string format. :rtype: str """ - if src_ip: - src_ip = binascii.hexlify(ip_address(unicode(src_ip)).packed) - if dst_ip: - dst_ip = binascii.hexlify(ip_address(unicode(dst_ip)).packed) - if dst_port: - end = 88 - elif src_port: - end = 84 - elif dst_ip: - end = 80 - elif src_ip: - end = 48 - else: - end = 14 return ('{!s:0>14}{!s:0>34}{!s:0>32}{!s:0>4}{!s:0>4}'.format( hex(next_hdr)[2:], src_ip, dst_ip, hex(src_port)[2:], - hex(dst_port)[2:]))[0:end] + hex(dst_port)[2:])).decode('hex').rstrip('\0') @staticmethod - def _classify_add_del_table(node, is_add, mask, match_n_vectors=1, - table_index=0xFFFFFFFF, nbuckets=2, - memory_size=2097152, skip_n_vectors=0, - next_table_index=0xFFFFFFFF, - miss_next_index=0xFFFFFFFF, current_data_flag=0, - current_data_offset=0): + def _classify_add_del_table( + node, is_add, mask, match_n_vectors=Constants.BITWISE_NON_ZERO, + table_index=Constants.BITWISE_NON_ZERO, nbuckets=2, + memory_size=2097152, skip_n_vectors=Constants.BITWISE_NON_ZERO, + next_table_index=Constants.BITWISE_NON_ZERO, + miss_next_index=Constants.BITWISE_NON_ZERO, + current_data_flag=0, current_data_offset=0): """Add or delete a classify table. :param node: VPP node to create classify table. :param is_add: If 1 the table is added, if 0 the table is deleted. :param mask: ACL mask in hexstring format. - :param match_n_vectors: Number of vectors to match (Default value = 1). - :param table_index: Index of the classify table. - (Default value = 0xFFFFFFFF) + :param match_n_vectors: Number of vectors to match (Default value = ~0). + :param table_index: Index of the classify table. (Default value = ~0) :param nbuckets: Number of buckets when adding a table. (Default value = 2) :param memory_size: Memory size when adding a table. (Default value = 2097152) - :param skip_n_vectors: Number of skip vectors (Default value = 0). - :param next_table_index: Index of next table. - (Default value = 0xFFFFFFFF) - :param miss_next_index: Index of miss table. - (Default value = 0xFFFFFFFF) + :param skip_n_vectors: Number of skip vectors (Default value = ~0). + :param next_table_index: Index of next table. (Default value = ~0) + :param miss_next_index: Index of miss table. (Default value = ~0) :param current_data_flag: Option to use current node's packet payload as the starting point from where packets are classified. This option is only valid for L2/L3 input ACL for now. @@ -267,9 +207,7 @@ class Classify(object): match_n: Number of match vectors. :rtype: tuple(int, int, int) """ - mask_len = ((len(mask) - 1) / 16 + 1) * 16 - mask = mask + '\0' * (mask_len - len(mask)) - + cmd = 'classify_add_del_table' args = dict( is_add=is_add, table_index=table_index, @@ -281,11 +219,9 @@ class Classify(object): miss_next_index=miss_next_index, current_data_flag=current_data_flag, current_data_offset=current_data_offset, - mask_len=mask_len, + mask_len=len(mask), mask=mask ) - - cmd = 'classify_add_del_table' err_msg = "Failed to create a classify table on host {host}".format( host=node['host']) @@ -296,10 +232,11 @@ class Classify(object): int(reply["match_n_vectors"]) @staticmethod - def _classify_add_del_session(node, is_add, table_index, match, - opaque_index=0xFFFFFFFF, - hit_next_index=0xFFFFFFFF, advance=0, - action=0, metadata=0): + def _classify_add_del_session( + node, is_add, table_index, match, + opaque_index=Constants.BITWISE_NON_ZERO, + hit_next_index=Constants.BITWISE_NON_ZERO, advance=0, + action=0, metadata=0): """Add or delete a classify session. :param node: VPP node to create classify session. @@ -309,9 +246,9 @@ class Classify(object): include bytes in front with length of skip_n_vectors of target table times sizeof (u32x4) (values of those bytes will be ignored). :param opaque_index: For add, opaque_index of new session. - (Default value = 0xFFFFFFFF) + (Default value = ~0) :param hit_next_index: For add, hit_next_index of new session. - (Default value = 0xFFFFFFFF) + (Default value = ~0) :param advance: For add, advance value for session. (Default value = 0) :param action: 0: No action (by default) metadata is not used. 1: Classified IP packets will be looked up from the specified ipv4 @@ -337,9 +274,7 @@ class Classify(object): :type action: int :type metadata: int """ - - match_len = ((len(match) - 1) / 16 + 1) * 16 - match = match + '\0' * (match_len - len(match)) + cmd = 'classify_add_del_session' args = dict( is_add=is_add, table_index=table_index, @@ -348,10 +283,9 @@ class Classify(object): advance=advance, action=action, metadata=metadata, - match_len=match_len, + match_len=len(match), match=match ) - cmd = 'classify_add_del_session' err_msg = "Failed to create a classify session on host {host}".format( host=node['host']) @@ -438,18 +372,18 @@ class Classify(object): papi_exec.add(cmd, **args).get_reply(err_msg) @staticmethod - def vpp_creates_classify_table_l3(node, ip_version, direction, ip_addr): + def vpp_creates_classify_table_l3(node, ip_version, direction, netmask): """Create classify table for IP address filtering. :param node: VPP node to create classify table. :param ip_version: Version of IP protocol. :param direction: Direction of traffic - src/dst. - :param ip_addr: IPv4 or Ipv6 (depending on the parameter 'ip_version') - address. + :param netmask: IPv4 or Ipv6 (depending on the parameter 'ip_version') + netmask (decimal, e.g. 255.255.255.255). :type node: dict :type ip_version: str :type direction: str - :type ip_addr: str + :type netmask: str :returns: (table_index, skip_n, match_n) table_index: Classify table index. skip_n: Number of skip vectors. @@ -462,181 +396,114 @@ class Classify(object): ip4=Classify._build_ip_mask, ip6=Classify._build_ip6_mask ) + if ip_version == "ip4" or ip_version == "ip6": - ip_addr = binascii.hexlify(ip_address(unicode(ip_addr)).packed) + netmask = binascii.hexlify(ip_address(unicode(netmask)).packed) else: - raise ValueError("IP version {ver} is not supported.". - format(ver=ip_version)) + raise ValueError("IP version {ver} is not supported.".format( + ver=ip_version)) if direction == "src": - mask = mask_f[ip_version](src_ip=ip_addr) + mask = mask_f[ip_version](src_ip=netmask) elif direction == "dst": - mask = mask_f[ip_version](dst_ip=ip_addr) + mask = mask_f[ip_version](dst_ip=netmask) else: - raise ValueError("Direction {dir} is not supported.". - format(dir=direction)) + raise ValueError("Direction {dir} is not supported.".format( + dir=direction)) - return Classify._classify_add_del_table( - node, - is_add=1, - mask=binascii.unhexlify(mask), - match_n_vectors=(len(mask) - 1) // 32 + 1 - ) + # Add l2 ethernet header to mask + mask = 14 * '\0' + mask - @staticmethod - def vpp_creates_classify_table_l2(node, direction, mac=""): - """Create classify table for MAC address filtering. + # Get index of the first significant mask octet + i = len(mask) - len(mask.lstrip('\0')) - :param node: VPP node to create classify table. - :param direction: Direction of traffic - src/dst. - :param mac: Source or destination (depending on the parameter - 'direction') MAC address. - :type node: dict - :type direction: str - :type mac: str - :returns: (table_index, skip_n, match_n) - table_index: Classify table index. - skip_n: Number of skip vectors. - match_n: Number of match vectors. - :rtype: tuple(int, int, int) - :raises ValueError: If the parameter 'direction' has incorrect value. - """ - if direction == "src": - mask = Classify._build_mac_mask(src_mac=mac) - elif direction == "dst": - mask = Classify._build_mac_mask(dst_mac=mac) - else: - raise ValueError("Direction {dir} is not supported.". - format(dir=direction)) - - return Classify._classify_add_del_table( - node, - is_add=1, - mask=binascii.unhexlify(mask), - match_n_vectors=(len(mask) - 1) // 32 + 1 - ) - - @staticmethod - def vpp_creates_classify_table_hex(node, hex_mask): - """Create classify table with hex mask. + # Compute skip_n parameter + skip_n = i // 16 + # Remove octets to be skipped from the mask + mask = mask[skip_n*16:] + # Pad mask to an even multiple of the vector size + mask = mask + (16 - len(mask) % 16 if len(mask) % 16 else 0) * '\0' + # Compute match_n parameter + match_n = len(mask) // 16 - :param node: VPP node to create classify table based on hex mask. - :param hex_mask: Classify hex mask. - :type node: dict - :type hex_mask: str - :returns: (table_index, skip_n, match_n) - table_index: Classify table index. - skip_n: Number of skip vectors. - match_n: Number of match vectors. - :rtype: tuple(int, int, int) - """ return Classify._classify_add_del_table( node, is_add=1, - mask=binascii.unhexlify(hex_mask), - match_n_vectors=(len(hex_mask) - 1) // 32 + 1 + mask=mask, + match_n_vectors=match_n, + skip_n_vectors=skip_n ) @staticmethod - def vpp_configures_classify_session_l3(node, acl_method, table_index, - ip_version, direction, address): + def vpp_configures_classify_session_l3( + node, acl_method, table_index, skip_n, match_n, ip_version, + direction, address, hit_next_index=Constants.BITWISE_NON_ZERO, + opaque_index=Constants.BITWISE_NON_ZERO): """Configuration of classify session for IP address filtering. :param node: VPP node to setup classify session. :param acl_method: ACL method - deny/permit. :param table_index: Classify table index. + :param skip_n: Number of skip vectors. + :param match_n: Number of vectors to match. :param ip_version: Version of IP protocol. :param direction: Direction of traffic - src/dst. :param address: IPv4 or IPv6 address. + :param hit_next_index: hit_next_index of new session. + (Default value = ~0) + :param opaque_index: opaque_index of new session. (Default value = ~0) :type node: dict :type acl_method: str :type table_index: int + :type skip_n: int + :type match_n: int :type ip_version: str :type direction: str :type address: str + :type hit_next_index: int + :type opaque_index: int :raises ValueError: If the parameter 'direction' has incorrect value. """ match_f = dict( ip4=Classify._build_ip_match, ip6=Classify._build_ip6_match ) - if direction == "src": - match = match_f[ip_version](src_ip=address) - elif direction == "dst": - match = match_f[ip_version](dst_ip=address) - else: - raise ValueError("Direction {dir} is not supported.". - format(dir=direction)) action = dict( permit=0, deny=1 ) - Classify._classify_add_del_session( - node, - is_add=1, - table_index=table_index, - match=binascii.unhexlify(match), - action=action[acl_method]) - @staticmethod - def vpp_configures_classify_session_l2(node, acl_method, table_index, - direction, address): - """Configuration of classify session for MAC address filtering. + if ip_version == "ip4" or ip_version == "ip6": + address = binascii.hexlify(ip_address(unicode(address)).packed) + else: + raise ValueError("IP version {ver} is not supported.".format( + ver=ip_version)) - :param node: VPP node to setup classify session. - :param acl_method: ACL method - deny/permit. - :param table_index: Classify table index. - :param direction: Direction of traffic - src/dst. - :param address: MAC address. - :type node: dict - :type acl_method: str - :type table_index: int - :type direction: str - :type address: str - :raises ValueError: If the parameter 'direction' has incorrect value. - """ if direction == "src": - match = Classify._build_mac_match(src_mac=address) + match = match_f[ip_version](src_ip=address) elif direction == "dst": - match = Classify._build_mac_match(dst_mac=address) + match = match_f[ip_version](dst_ip=address) else: - raise ValueError("Direction {dir} is not supported.". - format(dir=direction)) - action = dict( - permit=0, - deny=1 - ) - Classify._classify_add_del_session( - node, - is_add=1, - table_index=table_index, - match=binascii.unhexlify(match), - action=action[acl_method]) + raise ValueError("Direction {dir} is not supported.".format( + dir=direction)) - @staticmethod - def vpp_configures_classify_session_hex(node, acl_method, table_index, - hex_value): - """Configuration of classify session with hex value. + # Prepend match with l2 ethernet header part + match = 14 * '\0' + match + + # Pad match to match skip_n_vector + match_n_vector size + match = match + ((match_n + skip_n) * 16 - len(match) + if len(match) < (match_n + skip_n) * 16 + else 0) * '\0' - :param node: VPP node to setup classify session. - :param acl_method: ACL method - deny/permit. - :param table_index: Classify table index. - :param hex_value: Classify hex value. - :type node: dict - :type acl_method: str - :type table_index: int - :type hex_value: str - """ - action = dict( - permit=0, - deny=1 - ) Classify._classify_add_del_session( node, is_add=1, table_index=table_index, - match=binascii.unhexlify(hex_value), - action=action[acl_method]) + hit_next_index=hit_next_index, + opaque_index=opaque_index, + match=match, + action=action[acl_method] + ) @staticmethod def compute_classify_hex_mask(ip_version, protocol, direction): @@ -757,6 +624,18 @@ class Classify(object): return details @staticmethod + def show_classify_tables_verbose(node): + """Show classify tables verbose. + + :param node: Topology node. + :type node: dict + :returns: Classify tables verbose data. + :rtype: str + """ + return PapiSocketExecutor.run_cli_cmd( + node, "show classify tables verbose") + + @staticmethod def vpp_log_plugin_acl_settings(node): """Retrieve configured settings from the ACL plugin and write to robot log. |