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):
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.
41 :type ea_bits_len: int
42 :type psid_offset: int
44 :return: Index of created map domain.
46 :raises RuntimeError: If unable to add map domain.
48 output = VatExecutor.cmd_from_template(vpp_node, "map_add_domain.vat",
52 ea_bits_len=ea_bits_len,
53 psid_offset=psid_offset,
55 if output[0]["retval"] == 0:
56 return output[0]["index"]
58 raise RuntimeError('Unable to add map domain on node {}'
59 .format(vpp_node['host']))
62 def map_add_rule(vpp_node, index, psid, dst, delete=False):
63 """Add or delete map rule on node.
65 :param vpp_node: VPP node to add map rule on.
66 :param index: Map domain index to add rule to.
67 :param psid: Port Set Identifier.
68 :param dst: MAP CE IPv6 address.
69 :param delete: If set to True, delete rule. Default False.
75 :raises RuntimeError: If unable to add map rule.
77 output = VatExecutor.cmd_from_template(vpp_node, "map_add_del_rule.vat",
81 delete='del' if delete else '')
83 if output[0]["retval"] != 0:
84 raise RuntimeError('Unable to add map rule on node {}'
85 .format(vpp_node['host']))
88 def map_del_domain(vpp_node, index):
89 """Delete map domain on node.
91 :param vpp_node: VPP node to delete map domain on.
92 :param index: Index of the map domain.
95 :raises RuntimeError: If unable to delete map domain.
97 output = VatExecutor.cmd_from_template(vpp_node, "map_del_domain.vat",
99 if output[0]["retval"] != 0:
100 raise RuntimeError('Unable to delete map domain {} on node {}'
101 .format(index, vpp_node['host']))
104 def get_psid_from_port(port, psid_len, psid_offset):
105 """Return PSID from port.
107 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
108 +-----------+-----------+-------+
109 Ports in | A | PSID | j |
110 the CE port set | > 0 | | |
111 +-----------+-----------+-------+
112 | a bits | k bits |m bits |
115 :param port: Port to compute PSID from.
116 :param psid_len: PSID length.
117 :param psid_offset: PSID offset.
120 :type psid_offset: int
126 mask = ones >> (16 - psid_len)
127 psid = port >> (16 - psid_len - psid_offset)
132 def _make_ea_bits(ipv4_net, ipv4_host, ea_bit_len, psid_len, psid):
134 _note_: host(or prefix) part of destination ip in rule prefix, + psid
136 :param ipv4_net: IPv4 domain prefix.
137 :param ipv4_host: Destination IPv4 address.
138 :param ea_bit_len: EA bit length.
139 :param psid_len: PSID length.
141 :type ipv4_net: ipaddress.IPv4Network
142 :type ipv4_host: ipaddress.IPv4Address
143 :type ea_bit_len: int
146 :return: Number representing EA bit field of destination IPv6 address.
149 v4_suffix_len = ipv4_net._max_prefixlen - ipv4_net.prefixlen
150 v4_suffix = ipv4_net.network_address._ip ^ ipv4_host._ip
152 if ipv4_net.prefixlen + ea_bit_len <= 32:
153 ea_bits = v4_suffix >> (v4_suffix_len - ea_bit_len)
156 q_len = ea_bit_len - v4_suffix_len
157 # p_bits = v4_suffix << q_len # option 1: psid right padded
158 p_bits = v4_suffix << psid_len # option 2: psid left padded
160 raise Exception("invalid configuration: q_len < psid_len")
161 ea_bits = p_bits | psid
162 ea_bits <<= q_len - psid_len # option 2: psid left padded
166 def _make_interface_id(rule_net, dst_ip, ea_bit_len, psid):
168 _note_: if prefix or complete ip (<= 32), psid is 0
170 :param rule_net: IPv4 domain prefix.
171 :param dst_ip: Destination IPv4 address.
172 :param ea_bit_len: EA bit length.
174 :type rule_net: ipaddress.IPv4Network
175 :type dst_ip: ipaddress.IPv4Address
176 :type ea_bit_len: int
178 :return: Number representing interface id field of destination IPv6
182 if rule_net.prefixlen + ea_bit_len < 32:
183 v4_suffix_len = rule_net._max_prefixlen - rule_net.prefixlen
184 v4_suffix = rule_net.network_address._ip ^ dst_ip._ip
185 ea_bits = v4_suffix >> (v4_suffix_len - ea_bit_len)
186 address = rule_net.network_address._ip >> v4_suffix_len
187 address <<= ea_bit_len
189 address <<= 32 - rule_net.prefixlen - ea_bit_len
192 # address = address | psid_field
193 elif rule_net.prefixlen + ea_bit_len == 32:
194 address = dst_ip._ip << 16
196 # address = address | psid_field
198 address = dst_ip._ip << 16
205 def compute_ipv6_map_destination_address(ipv4_pfx, ipv6_pfx, ea_bit_len,
206 psid_offset, psid_len, ipv4_dst,
208 """Compute IPv6 destination address from IPv4 address for MAP algorithm.
211 | n bits | o bits | s bits | 128-n-o-s bits |
212 +--------------------+-----------+---------+-----------------------+
213 | Rule IPv6 prefix | EA bits |subnet ID| interface ID |
214 +--------------------+-----------+---------+-----------------------+
215 |<--- End-user IPv6 prefix --->|
218 :param ipv4_pfx: Domain IPv4 preffix.
219 :param ipv6_pfx: Domain IPv6 preffix.
220 :param ea_bit_len: Domain EA bits length.
221 :param psid_offset: Domain PSID offset.
222 :param psid_len: Domain PSID length.
223 :param ipv4_dst: Destination IPv4 address.
224 :param dst_port: Destination port number or ICMP ID.
227 :type ea_bit_len: int
228 :type psid_offset: int
232 :return: Computed IPv6 address.
235 ipv6_net = ipaddress.ip_network(unicode(ipv6_pfx))
236 ipv4_net = ipaddress.ip_network(unicode(ipv4_pfx))
237 ipv4_host = ipaddress.ip_address(unicode(ipv4_dst))
239 ipv6_host_len = ipv6_net._max_prefixlen - ipv6_net.prefixlen
240 ipv4_host_len = ipv4_net._max_prefixlen - ipv4_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 = ipv6_net.network_address._ip >> ipv6_host_len
245 ea_bits = Map._make_ea_bits(ipv4_net, ipv4_host, ea_bit_len, psid_len,
248 interface_id = Map._make_interface_id(ipv4_net, ipv4_host, ea_bit_len,
251 address = rule_v6_pfx << ea_bit_len
252 address |= ea_bits # add EA bits
254 if end_user_v6_pfx_len > 64:
255 # If the End-user IPv6 prefix length is larger than 64,
256 # the most significant parts of the interface identifier are
257 # overwritten by the prefix.
258 mask = (2**128-1) >> end_user_v6_pfx_len
260 address <<= (128 - end_user_v6_pfx_len)
261 address |= interface_id # add Interface ID bits
263 return str(ipaddress.ip_address(address))