diff options
author | Matej Klotton <mklotton@cisco.com> | 2016-07-25 17:52:22 +0200 |
---|---|---|
committer | Matej Klotton <mklotton@cisco.com> | 2016-08-15 14:36:47 +0200 |
commit | 517ee7fd3eb28ecf030c5d50be09fcdabe508922 (patch) | |
tree | c6ac1ce632b96157b962f43c74f9c11cefca2ebe /resources/libraries/python | |
parent | bc4e345605abe0772043892fcc99fce9aa768edb (diff) |
CSIT-197: Add basic mapping rule tests for MAP-E
- IPv4 prefix length + ea bits length < 32
- IPv4 prefix length + ea bits length == 32
- IPv4 prefix length + ea bits length > 32
- End user IPv6 prefix is 64
- IPv4 preffix is 0
Change-Id: Ib8d76abfebfb206fbbaa3c1422b2d321a3ed8712
Signed-off-by: Matej Klotton <mklotton@cisco.com>
Diffstat (limited to 'resources/libraries/python')
-rw-r--r-- | resources/libraries/python/IPUtil.py | 23 | ||||
-rw-r--r-- | resources/libraries/python/Map.py | 180 | ||||
-rw-r--r-- | resources/libraries/python/Trace.py | 8 |
3 files changed, 209 insertions, 2 deletions
diff --git a/resources/libraries/python/IPUtil.py b/resources/libraries/python/IPUtil.py index 323c75bda4..5011708c39 100644 --- a/resources/libraries/python/IPUtil.py +++ b/resources/libraries/python/IPUtil.py @@ -12,13 +12,15 @@ # limitations under the License. """Common IP utilities library.""" -from ipaddress import IPv4Network + +from ipaddress import IPv4Network, ip_address from resources.libraries.python.ssh import SSH from resources.libraries.python.constants import Constants from resources.libraries.python.topology import Topology + class IPUtil(object): """Common IP utilities""" @@ -45,7 +47,7 @@ class IPUtil(object): elif if_type == "name": iface_name = interface else: - raise ValueError("if_type unknown: {}".format(if_type)) + raise ValueError("if_type unknown: {0}".format(if_type)) cmd = "{c}".format(c=Constants.VAT_BIN_NAME) cmd_input = 'exec ip probe {dev} {ip}'.format(dev=iface_name, ip=addr) @@ -54,6 +56,23 @@ class IPUtil(object): raise Exception('VPP ip probe {dev} {ip} failed on {h}'.format( dev=iface_name, ip=addr, h=node['host'])) + @staticmethod + def ip_addresses_should_be_equal(ip1, ip2): + """Fails if the given IP addresses are unequal. + + :param ip1: IPv4 or IPv6 address. + :param ip2: IPv4 or IPv6 address. + :type ip1: str + :type ip2: str + """ + + addr1 = ip_address(unicode(ip1)) + addr2 = ip_address(unicode(ip2)) + + if addr1 != addr2: + raise AssertionError('IP addresses are not equal: {0} != {1}'. + format(ip1, ip2)) + def convert_ipv4_netmask_prefix(network): """Convert network mask to equivalent network prefix length or vice versa. diff --git a/resources/libraries/python/Map.py b/resources/libraries/python/Map.py index 7d48c20d2e..b98d488e73 100644 --- a/resources/libraries/python/Map.py +++ b/resources/libraries/python/Map.py @@ -14,6 +14,8 @@ """Map utilities library.""" +import ipaddress + from resources.libraries.python.VatExecutor import VatExecutor @@ -81,3 +83,181 @@ class Map(object): if output[0]["retval"] != 0: raise RuntimeError('Unable to add map rule on node {}' .format(vpp_node['host'])) + + @staticmethod + def map_del_domain(vpp_node, index): + """Delete map domain on node. + + :param vpp_node: VPP node to delete map domain on. + :param index: Index of the map domain. + :type vpp_node: dict + :type index: int + :raises RuntimeError: If unable to delete map domain. + """ + output = VatExecutor.cmd_from_template(vpp_node, "map_del_domain.vat", + index=index) + if output[0]["retval"] != 0: + raise RuntimeError('Unable to delete map domain {} on node {}' + .format(index, vpp_node['host'])) + + @staticmethod + def get_psid_from_port(port, psid_len, psid_offset): + """Return PSID from port. + 0 1 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + +-----------+-----------+-------+ + Ports in | A | PSID | j | + the CE port set | > 0 | | | + +-----------+-----------+-------+ + | a bits | k bits |m bits | + + + :param port: Port to compute PSID from. + :param psid_len: PSID length. + :param psid_offset: PSID offset. + :type port: int + :type psid_len: int + :type psid_offset: int + + :return: PSID. + :rtype: int + """ + ones = 2**16-1 + mask = ones >> (16 - psid_len) + psid = port >> (16 - psid_len - psid_offset) + psid &= mask + return psid + + @staticmethod + def _make_ea_bits(ipv4_net, ipv4_host, ea_bit_len, psid_len, psid): + """ + _note_: host(or prefix) part of destination ip in rule prefix, + psid + + :param ipv4_net: IPv4 domain prefix. + :param ipv4_host: Destination IPv4 address. + :param ea_bit_len: EA bit length. + :param psid_len: PSID length. + :param psid: PSID. + :type ipv4_net: ipaddress.IPv4Network + :type ipv4_host: ipaddress.IPv4Address + :type ea_bit_len: int + :type psid_len: int + :type psid: int + :return: Number representing EA bit field of destination IPv6 address. + :rtype: int + """ + v4_suffix_len = ipv4_net._max_prefixlen - ipv4_net.prefixlen + v4_suffix = ipv4_net.network_address._ip ^ ipv4_host._ip + + if ipv4_net.prefixlen + ea_bit_len <= 32: + ea_bits = v4_suffix >> (v4_suffix_len - ea_bit_len) + return ea_bits + else: + q_len = ea_bit_len - v4_suffix_len + # p_bits = v4_suffix << q_len # option 1: psid right padded + p_bits = v4_suffix << psid_len # option 2: psid left padded + if q_len < psid_len: + raise Exception("invalid configuration: q_len < psid_len") + ea_bits = p_bits | psid + ea_bits <<= q_len - psid_len # option 2: psid left padded + return ea_bits + + @staticmethod + def _make_interface_id(rule_net, dst_ip, ea_bit_len, psid): + """ + _note_: if prefix or complete ip (<= 32), psid is 0 + + :param rule_net: IPv4 domain prefix. + :param dst_ip: Destination IPv4 address. + :param ea_bit_len: EA bit length. + :param psid: PSID. + :type rule_net: ipaddress.IPv4Network + :type dst_ip: ipaddress.IPv4Address + :type ea_bit_len: int + :type psid: int + :return: Number representing interface id field of destination IPv6 + address. + :rtype: int + """ + if rule_net.prefixlen + ea_bit_len < 32: + v4_suffix_len = rule_net._max_prefixlen - rule_net.prefixlen + v4_suffix = rule_net.network_address._ip ^ dst_ip._ip + ea_bits = v4_suffix >> (v4_suffix_len - ea_bit_len) + address = rule_net.network_address._ip >> v4_suffix_len + address <<= ea_bit_len + address |= ea_bits + address <<= 32 - rule_net.prefixlen - ea_bit_len + address <<= 16 + # psid_field = 0 + # address = address | psid_field + elif rule_net.prefixlen + ea_bit_len == 32: + address = dst_ip._ip << 16 + # psid_field = 0 + # address = address | psid_field + else: + address = dst_ip._ip << 16 + address |= psid + return address + + return address + + @staticmethod + def compute_ipv6_map_destination_address(ipv4_pfx, ipv6_pfx, ea_bit_len, + psid_offset, psid_len, ipv4_dst, + dst_port): + """Compute IPv6 destination address from IPv4 address for MAP algorithm. + (RFC 7597) + + | n bits | o bits | s bits | 128-n-o-s bits | + +--------------------+-----------+---------+-----------------------+ + | Rule IPv6 prefix | EA bits |subnet ID| interface ID | + +--------------------+-----------+---------+-----------------------+ + |<--- End-user IPv6 prefix --->| + + + :param ipv4_pfx: Domain IPv4 preffix. + :param ipv6_pfx: Domain IPv6 preffix. + :param ea_bit_len: Domain EA bits length. + :param psid_offset: Domain PSID offset. + :param psid_len: Domain PSID length. + :param ipv4_dst: Destination IPv4 address. + :param dst_port: Destination port number or ICMP ID. + :type ipv4_pfx: str + :type ipv6_pfx: str + :type ea_bit_len: int + :type psid_offset: int + :type psid_len: int + :type ipv4_dst: str + :type dst_port: int + :return: Computed IPv6 address. + :rtype: str + """ + ipv6_net = ipaddress.ip_network(unicode(ipv6_pfx)) + ipv4_net = ipaddress.ip_network(unicode(ipv4_pfx)) + ipv4_host = ipaddress.ip_address(unicode(ipv4_dst)) + + ipv6_host_len = ipv6_net._max_prefixlen - ipv6_net.prefixlen + ipv4_host_len = ipv4_net._max_prefixlen - ipv4_net.prefixlen + end_user_v6_pfx_len = ipv6_net.prefixlen + ea_bit_len + psid = Map.get_psid_from_port(dst_port, psid_len, psid_offset) + + rule_v6_pfx = ipv6_net.network_address._ip >> ipv6_host_len + ea_bits = Map._make_ea_bits(ipv4_net, ipv4_host, ea_bit_len, psid_len, + psid) + subnet_id = 0 + interface_id = Map._make_interface_id(ipv4_net, ipv4_host, ea_bit_len, + psid) + + address = rule_v6_pfx << ea_bit_len + address |= ea_bits # add EA bits + + if end_user_v6_pfx_len > 64: + # If the End-user IPv6 prefix length is larger than 64, + # the most significant parts of the interface identifier are + # overwritten by the prefix. + mask = (2**128-1) >> end_user_v6_pfx_len + interface_id &= mask + address <<= (128 - end_user_v6_pfx_len) + address |= interface_id # add Interface ID bits + + return str(ipaddress.ip_address(address)) diff --git a/resources/libraries/python/Trace.py b/resources/libraries/python/Trace.py index 1a251a61b6..49b39cbdfb 100644 --- a/resources/libraries/python/Trace.py +++ b/resources/libraries/python/Trace.py @@ -23,3 +23,11 @@ class Trace(object): if node['type'] == NodeType.DUT: vat = VatExecutor() vat.execute_script("show_trace.vat", node, json_out=False) + + @staticmethod + def clear_packet_trace_on_all_duts(nodes): + for node in nodes.values(): + if node['type'] == NodeType.DUT: + vat = VatExecutor() + vat.execute_script("clear_trace.vat", node, json_out=False) + |