1 # Copyright (c) 2016 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 :return: 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.
113 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
114 +-----------+-----------+-------+
115 Ports in | A | PSID | j |
116 the CE port set | > 0 | | |
117 +-----------+-----------+-------+
118 | a bits | k bits |m bits |
121 :param port: Port to compute PSID from.
122 :param psid_len: PSID length.
123 :param psid_offset: PSID offset.
126 :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 :return: Number representing EA bit field of destination IPv6 address.
155 v4_suffix_len = ipv4_net._max_prefixlen - ipv4_net.prefixlen
156 v4_suffix = ipv4_net.network_address._ip ^ ipv4_host._ip
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 :return: 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 = rule_net.network_address._ip ^ dst_ip._ip
191 ea_bits = v4_suffix >> (v4_suffix_len - ea_bit_len)
192 address = rule_net.network_address._ip >> v4_suffix_len
193 address <<= ea_bit_len
195 address <<= 32 - rule_net.prefixlen - ea_bit_len
198 # address = address | psid_field
199 elif rule_net.prefixlen + ea_bit_len == 32:
200 address = dst_ip._ip << 16
202 # address = address | psid_field
204 address = dst_ip._ip << 16
211 def compute_ipv6_map_destination_address(ipv4_pfx, ipv6_pfx, ea_bit_len,
212 psid_offset, psid_len, ipv4_dst,
214 """Compute IPv6 destination address from IPv4 address for MAP algorithm.
217 | n bits | o bits | s bits | 128-n-o-s bits |
218 +--------------------+-----------+---------+-----------------------+
219 | Rule IPv6 prefix | EA bits |subnet ID| interface ID |
220 +--------------------+-----------+---------+-----------------------+
221 |<--- End-user IPv6 prefix --->|
224 :param ipv4_pfx: Domain IPv4 preffix.
225 :param ipv6_pfx: Domain IPv6 preffix.
226 :param ea_bit_len: Domain EA bits length.
227 :param psid_offset: Domain PSID offset.
228 :param psid_len: Domain PSID length.
229 :param ipv4_dst: Destination IPv4 address.
230 :param dst_port: Destination port number or ICMP ID.
233 :type ea_bit_len: int
234 :type psid_offset: int
238 :return: Computed IPv6 address.
241 ipv6_net = ipaddress.ip_network(unicode(ipv6_pfx))
242 ipv4_net = ipaddress.ip_network(unicode(ipv4_pfx))
243 ipv4_host = ipaddress.ip_address(unicode(ipv4_dst))
245 ipv6_host_len = ipv6_net._max_prefixlen - ipv6_net.prefixlen
246 ipv4_host_len = ipv4_net._max_prefixlen - ipv4_net.prefixlen
247 end_user_v6_pfx_len = ipv6_net.prefixlen + ea_bit_len
248 psid = Map.get_psid_from_port(dst_port, psid_len, psid_offset)
250 rule_v6_pfx = ipv6_net.network_address._ip >> ipv6_host_len
251 ea_bits = Map._make_ea_bits(ipv4_net, ipv4_host, ea_bit_len, psid_len,
254 interface_id = Map._make_interface_id(ipv4_net, ipv4_host, ea_bit_len,
257 address = rule_v6_pfx << ea_bit_len
258 address |= ea_bits # add EA bits
260 if end_user_v6_pfx_len > 64:
261 # If the End-user IPv6 prefix length is larger than 64,
262 # the most significant parts of the interface identifier are
263 # overwritten by the prefix.
264 mask = (2**128-1) >> end_user_v6_pfx_len
266 address <<= (128 - end_user_v6_pfx_len)
267 address |= interface_id # add Interface ID bits
269 return str(ipaddress.ip_address(address))
272 def compute_ipv6_map_source_address(ipv6_pfx, ipv4_src):
273 """Compute IPv6 source address from IPv4 address for MAP-T algorithm.
275 :param ipv6_pfx: 96 bit long IPv6 prefix.
276 :param ipv4_src: IPv4 source address
279 :return: IPv6 address, combination of IPv6 prefix and IPv4 address.
282 ipv6_net = ipaddress.ip_network(unicode(ipv6_pfx))
283 ipv4_host = ipaddress.ip_address(unicode(ipv4_src))
285 address = ipv6_net.network_address._ip
286 address |= ipv4_host._ip
288 return str(ipaddress.ip_address(address))