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 """Map utilities library."""
19 from resources.libraries.python.VatExecutor import VatExecutor
23 """Utilities for manipulating MAP feature in VPP."""
26 def map_add_domain(vpp_node, ip4_pfx, ip6_pfx, ip6_src, ea_bits_len,
27 psid_offset, psid_len, map_t=False):
28 """Add map domain on node.
30 :param vpp_node: VPP node to add map domain on.
31 :param ip4_pfx: Rule IPv4 prefix.
32 :param ip6_pfx: Rule IPv6 prefix.
33 :param ip6_src: MAP domain IPv6 BR address / Tunnel source.
34 :param ea_bits_len: Embedded Address bits length.
35 :param psid_offset: Port Set Identifier (PSID) offset.
36 :param psid_len: Port Set Identifier (PSID) length.
37 :param map_t: Mapping using translation instead of encapsulation.
43 :type ea_bits_len: int
44 :type psid_offset: int
47 :returns: Index of created map domain.
49 :raises RuntimeError: If unable to add map domain.
51 translate = 'map-t' if map_t else ''
53 output = VatExecutor.cmd_from_template(vpp_node, "map_add_domain.vat",
57 ea_bits_len=ea_bits_len,
58 psid_offset=psid_offset,
61 if output[0]["retval"] == 0:
62 return output[0]["index"]
64 raise RuntimeError('Unable to add map domain on node {}'
65 .format(vpp_node['host']))
68 def map_add_rule(vpp_node, index, psid, dst, delete=False):
69 """Add or delete map rule on node.
71 :param vpp_node: VPP node to add map rule on.
72 :param index: Map domain index to add rule to.
73 :param psid: Port Set Identifier.
74 :param dst: MAP CE IPv6 address.
75 :param delete: If set to True, delete rule. Default False.
81 :raises RuntimeError: If unable to add map rule.
83 output = VatExecutor.cmd_from_template(vpp_node, "map_add_del_rule.vat",
87 delete='del' if delete else '')
89 if output[0]["retval"] != 0:
90 raise RuntimeError('Unable to add map rule on node {}'
91 .format(vpp_node['host']))
94 def map_del_domain(vpp_node, index):
95 """Delete map domain on node.
97 :param vpp_node: VPP node to delete map domain on.
98 :param index: Index of the map domain.
101 :raises RuntimeError: If unable to delete map domain.
103 output = VatExecutor.cmd_from_template(vpp_node, "map_del_domain.vat",
105 if output[0]["retval"] != 0:
106 raise RuntimeError('Unable to delete map domain {} on node {}'
107 .format(index, vpp_node['host']))
110 def get_psid_from_port(port, psid_len, psid_offset):
111 """Return PSID from port.::
114 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
115 +-----------+-----------+-------+
116 Ports in | A | PSID | j |
117 the CE port set | > 0 | | |
118 +-----------+-----------+-------+
119 | a bits | k bits |m bits |
122 :param port: Port to compute PSID from.
123 :param psid_len: PSID length.
124 :param psid_offset: PSID offset.
127 :type psid_offset: int
132 mask = ones >> (16 - psid_len)
133 psid = port >> (16 - psid_len - psid_offset)
138 def _make_ea_bits(ipv4_net, ipv4_host, ea_bit_len, psid_len, psid):
140 _note_: host(or prefix) part of destination ip in rule prefix, + psid
142 :param ipv4_net: IPv4 domain prefix.
143 :param ipv4_host: Destination IPv4 address.
144 :param ea_bit_len: EA bit length.
145 :param psid_len: PSID length.
147 :type ipv4_net: ipaddress.IPv4Network
148 :type ipv4_host: ipaddress.IPv4Address
149 :type ea_bit_len: int
152 :returns: Number representing EA bit field of destination IPv6 address.
155 v4_suffix_len = ipv4_net.max_prefixlen - ipv4_net.prefixlen
156 v4_suffix = int(ipv4_net.network_address) ^ int(ipv4_host)
158 if ipv4_net.prefixlen + ea_bit_len <= 32:
159 ea_bits = v4_suffix >> (v4_suffix_len - ea_bit_len)
162 q_len = ea_bit_len - v4_suffix_len
163 # p_bits = v4_suffix << q_len # option 1: psid right padded
164 p_bits = v4_suffix << psid_len # option 2: psid left padded
166 raise Exception("invalid configuration: q_len < psid_len")
167 ea_bits = p_bits | psid
168 ea_bits <<= q_len - psid_len # option 2: psid left padded
172 def _make_interface_id(rule_net, dst_ip, ea_bit_len, psid):
174 _note_: if prefix or complete ip (<= 32), psid is 0
176 :param rule_net: IPv4 domain prefix.
177 :param dst_ip: Destination IPv4 address.
178 :param ea_bit_len: EA bit length.
180 :type rule_net: ipaddress.IPv4Network
181 :type dst_ip: ipaddress.IPv4Address
182 :type ea_bit_len: int
184 :returns: Number representing interface id field of destination IPv6
188 if rule_net.prefixlen + ea_bit_len < 32:
189 v4_suffix_len = rule_net.max_prefixlen - rule_net.prefixlen
190 v4_suffix = int(rule_net.network_address) ^ int(dst_ip)
191 ea_bits = v4_suffix >> (v4_suffix_len - ea_bit_len)
192 address = int(rule_net.network_address) >> v4_suffix_len
193 address <<= ea_bit_len
195 address <<= 32 - rule_net.prefixlen - ea_bit_len
197 elif rule_net.prefixlen + ea_bit_len == 32:
198 address = int(dst_ip) << 16
200 address = int(dst_ip) << 16
207 def compute_ipv6_map_destination_address(ipv4_pfx, ipv6_pfx, ea_bit_len,
208 psid_offset, psid_len, ipv4_dst,
210 """Compute IPv6 destination address from IPv4 address for MAP algorithm.
213 | n bits | o bits | s bits | 128-n-o-s bits |
214 +--------------------+-----------+---------+-----------------------+
215 | Rule IPv6 prefix | EA bits |subnet ID| interface ID |
216 +--------------------+-----------+---------+-----------------------+
217 |<--- End-user IPv6 prefix --->|
219 :param ipv4_pfx: Domain IPv4 preffix.
220 :param ipv6_pfx: Domain IPv6 preffix.
221 :param ea_bit_len: Domain EA bits length.
222 :param psid_offset: Domain PSID offset.
223 :param psid_len: Domain PSID length.
224 :param ipv4_dst: Destination IPv4 address.
225 :param dst_port: Destination port number or ICMP ID.
228 :type ea_bit_len: int
229 :type psid_offset: int
233 :returns: Computed IPv6 address.
236 ipv6_net = ipaddress.ip_network(unicode(ipv6_pfx))
237 ipv4_net = ipaddress.ip_network(unicode(ipv4_pfx))
238 ipv4_host = ipaddress.ip_address(unicode(ipv4_dst))
240 ipv6_host_len = ipv6_net.max_prefixlen - ipv6_net.prefixlen
241 end_user_v6_pfx_len = ipv6_net.prefixlen + ea_bit_len
242 psid = Map.get_psid_from_port(dst_port, psid_len, psid_offset)
244 rule_v6_pfx = int(ipv6_net.network_address) >> ipv6_host_len
245 ea_bits = Map._make_ea_bits(ipv4_net, ipv4_host, ea_bit_len, psid_len,
247 interface_id = Map._make_interface_id(ipv4_net, ipv4_host, ea_bit_len,
250 address = rule_v6_pfx << ea_bit_len
251 address |= ea_bits # add EA bits
253 if end_user_v6_pfx_len > 64:
254 # If the End-user IPv6 prefix length is larger than 64,
255 # the most significant parts of the interface identifier are
256 # overwritten by the prefix.
257 mask = (2**128-1) >> end_user_v6_pfx_len
259 address <<= (128 - end_user_v6_pfx_len)
260 address |= interface_id # add Interface ID bits
262 return str(ipaddress.ip_address(address))
265 def compute_ipv6_map_source_address(ipv6_pfx, ipv4_src):
266 """Compute IPv6 source address from IPv4 address for MAP-T algorithm.
268 :param ipv6_pfx: 96 bit long IPv6 prefix.
269 :param ipv4_src: IPv4 source address
272 :returns: IPv6 address, combination of IPv6 prefix and IPv4 address.
275 ipv6_net = ipaddress.ip_network(unicode(ipv6_pfx))
276 ipv4_host = ipaddress.ip_address(unicode(ipv4_src))
278 address = int(ipv6_net.network_address)
279 address |= int(ipv4_host)
281 return str(ipaddress.ip_address(address))