VOM: NAT updates
[vpp.git] / src / vpp-api / vom / prefix.cpp
1 /*
2  * Copyright (c) 2017 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <boost/algorithm/string.hpp>
17 #include <sstream>
18
19 #include "vom/prefix.hpp"
20
21 namespace VOM {
22 /*
23  * Keep this in sync with VPP's fib_protocol_t
24  */
25 const l3_proto_t l3_proto_t::IPV4(0, "ipv4");
26 const l3_proto_t l3_proto_t::IPV6(1, "ipv6");
27 const l3_proto_t l3_proto_t::MPLS(2, "mpls");
28
29 l3_proto_t::l3_proto_t(int v, const std::string& s)
30   : enum_base<l3_proto_t>(v, s)
31 {
32 }
33
34 bool
35 l3_proto_t::is_ipv6()
36 {
37   return (*this == IPV6);
38 }
39
40 bool
41 l3_proto_t::is_ipv4()
42 {
43   return (*this == IPV4);
44 }
45
46 const l3_proto_t&
47 l3_proto_t::from_address(const boost::asio::ip::address& addr)
48 {
49   if (addr.is_v6()) {
50     return IPV6;
51   }
52
53   return IPV4;
54 }
55
56 std::ostream&
57 operator<<(std::ostream& os, const l3_proto_t& l3p)
58 {
59   os << l3p.to_string();
60   return os;
61 }
62
63 /*
64  * Keep this in sync with VPP's dpo_proto_t
65  */
66 const nh_proto_t nh_proto_t::IPV4(0, "ipv4");
67 const nh_proto_t nh_proto_t::IPV6(1, "ipv6");
68 const nh_proto_t nh_proto_t::MPLS(2, "mpls");
69 const nh_proto_t nh_proto_t::ETHERNET(3, "ethernet");
70
71 nh_proto_t::nh_proto_t(int v, const std::string& s)
72   : enum_base<nh_proto_t>(v, s)
73 {
74 }
75
76 const nh_proto_t&
77 nh_proto_t::from_address(const boost::asio::ip::address& addr)
78 {
79   if (addr.is_v6()) {
80     return IPV6;
81   }
82
83   return IPV4;
84 }
85
86 /**
87  * The all Zeros prefix
88  */
89 const route::prefix_t route::prefix_t::ZERO("0.0.0.0", 0);
90 const route::prefix_t route::prefix_t::ZEROv6("::", 0);
91
92 route::prefix_t::prefix_t(const boost::asio::ip::address& addr, uint8_t len)
93   : m_addr(addr)
94   , m_len(len)
95 {
96 }
97
98 route::prefix_t::prefix_t(const boost::asio::ip::address& addr)
99   : m_addr(addr)
100   , m_len(VOM::mask_width(addr))
101 {
102 }
103
104 route::prefix_t::prefix_t(const std::string& s, uint8_t len)
105   : m_addr(boost::asio::ip::address::from_string(s))
106   , m_len(len)
107 {
108 }
109
110 route::prefix_t::prefix_t(const prefix_t& o)
111   : m_addr(o.m_addr)
112   , m_len(o.m_len)
113 {
114 }
115
116 route::prefix_t::prefix_t()
117   : m_addr()
118   , m_len(0)
119 {
120 }
121
122 route::prefix_t::~prefix_t()
123 {
124 }
125
126 route::prefix_t&
127 route::prefix_t::operator=(const route::prefix_t& o)
128 {
129   m_addr = o.m_addr;
130   m_len = o.m_len;
131
132   return (*this);
133 }
134
135 const boost::asio::ip::address&
136 route::prefix_t::address() const
137 {
138   return (m_addr);
139 }
140
141 uint8_t
142 route::prefix_t::mask_width() const
143 {
144   return (m_len);
145 }
146
147 bool
148 route::prefix_t::operator<(const route::prefix_t& o) const
149 {
150   if (m_len == o.m_len) {
151     return (m_addr < o.m_addr);
152   } else {
153     return (m_len < o.m_len);
154   }
155 }
156
157 bool
158 route::prefix_t::operator==(const route::prefix_t& o) const
159 {
160   return (m_len == o.m_len && m_addr == o.m_addr);
161 }
162
163 bool
164 route::prefix_t::operator!=(const route::prefix_t& o) const
165 {
166   return (!(*this == o));
167 }
168
169 std::string
170 route::prefix_t::to_string() const
171 {
172   std::ostringstream s;
173
174   s << m_addr.to_string() << "/" << std::to_string(m_len);
175
176   return (s.str());
177 }
178
179 boost::asio::ip::address
180 from_bytes(uint8_t is_ip6, uint8_t* bytes)
181 {
182   boost::asio::ip::address addr;
183
184   if (is_ip6) {
185     std::array<uint8_t, 16> a;
186     std::copy(bytes, bytes + 16, std::begin(a));
187     boost::asio::ip::address_v6 v6(a);
188     addr = v6;
189   } else {
190     std::array<uint8_t, 4> a;
191     std::copy(bytes, bytes + 4, std::begin(a));
192     boost::asio::ip::address_v4 v4(a);
193     addr = v4;
194   }
195
196   return (addr);
197 }
198
199 route::prefix_t::prefix_t(uint8_t is_ip6, uint8_t* addr, uint8_t len)
200   : m_addr(from_bytes(is_ip6, addr))
201   , m_len(len)
202 {
203 }
204 void
205 to_bytes(const boost::asio::ip::address_v6& addr, uint8_t* array)
206 {
207   memcpy(array, addr.to_bytes().data(), 16);
208 }
209
210 void
211 to_bytes(const boost::asio::ip::address_v4& addr, uint8_t* array)
212 {
213   memcpy(array, addr.to_bytes().data(), 4);
214 }
215
216 void
217 to_bytes(const boost::asio::ip::address& addr, uint8_t* is_ip6, uint8_t* array)
218 {
219   if (addr.is_v6()) {
220     *is_ip6 = 1;
221     to_bytes(addr.to_v6(), array);
222   } else {
223     *is_ip6 = 0;
224     to_bytes(addr.to_v4(), array);
225   }
226 }
227
228 uint32_t
229 mask_width(const boost::asio::ip::address& addr)
230 {
231   if (addr.is_v6()) {
232     return 128;
233   }
234   return 32;
235 }
236
237 void
238 route::prefix_t::to_vpp(uint8_t* is_ip6, uint8_t* addr, uint8_t* len) const
239 {
240   *len = m_len;
241   to_bytes(m_addr, is_ip6, addr);
242 }
243
244 l3_proto_t
245 route::prefix_t::l3_proto() const
246 {
247   if (m_addr.is_v6()) {
248     return (l3_proto_t::IPV6);
249   } else {
250     return (l3_proto_t::IPV4);
251   }
252
253   return (l3_proto_t::IPV4);
254 }
255
256 std::ostream&
257 operator<<(std::ostream& os, const route::prefix_t& pfx)
258 {
259   os << pfx.to_string();
260
261   return (os);
262 }
263
264 boost::asio::ip::address_v4
265 operator|(const boost::asio::ip::address_v4& addr1,
266           const boost::asio::ip::address_v4& addr2)
267 {
268   uint32_t a;
269   a = addr1.to_ulong() | addr2.to_ulong();
270   boost::asio::ip::address_v4 addr(a);
271   return (addr);
272 }
273
274 boost::asio::ip::address_v4 operator&(const boost::asio::ip::address_v4& addr1,
275                                       const boost::asio::ip::address_v4& addr2)
276 {
277   uint32_t a;
278   a = addr1.to_ulong() & addr2.to_ulong();
279   boost::asio::ip::address_v4 addr(a);
280   return (addr);
281 }
282
283 boost::asio::ip::address_v4 operator~(const boost::asio::ip::address_v4& addr1)
284 {
285   uint32_t a;
286   a = ~addr1.to_ulong();
287   boost::asio::ip::address_v4 addr(a);
288   return (addr);
289 }
290
291 boost::asio::ip::address_v6
292 operator|(const boost::asio::ip::address_v6& addr1,
293           const boost::asio::ip::address_v6& addr2)
294 {
295   boost::asio::ip::address_v6::bytes_type b1 = addr1.to_bytes();
296   boost::asio::ip::address_v6::bytes_type b2 = addr2.to_bytes();
297
298   for (boost::asio::ip::address_v6::bytes_type::size_type ii = 0;
299        ii < b1.max_size(); ii++) {
300     b1[ii] |= b2[ii];
301   }
302
303   boost::asio::ip::address_v6 addr(b1);
304   return (addr);
305 }
306
307 boost::asio::ip::address_v6 operator&(const boost::asio::ip::address_v6& addr1,
308                                       const boost::asio::ip::address_v6& addr2)
309 {
310   boost::asio::ip::address_v6::bytes_type b1 = addr1.to_bytes();
311   boost::asio::ip::address_v6::bytes_type b2 = addr2.to_bytes();
312
313   for (boost::asio::ip::address_v6::bytes_type::size_type ii = 0;
314        ii < b1.max_size(); ii++) {
315     b1[ii] &= b2[ii];
316   }
317
318   boost::asio::ip::address_v6 addr(b1);
319   return (addr);
320 }
321
322 boost::asio::ip::address_v6 operator~(const boost::asio::ip::address_v6& addr1)
323 {
324   boost::asio::ip::address_v6::bytes_type b1 = addr1.to_bytes();
325
326   for (boost::asio::ip::address_v6::bytes_type::size_type ii = 0;
327        ii < b1.max_size(); ii++) {
328     b1[ii] = ~b1[ii];
329   }
330
331   boost::asio::ip::address_v6 addr(b1);
332   return (addr);
333 }
334 boost::asio::ip::address
335 operator|(const boost::asio::ip::address& addr1,
336           const boost::asio::ip::address& addr2)
337 {
338   if (addr1.is_v6())
339     return (addr1.to_v6() | addr2.to_v6());
340   else
341     return (addr1.to_v4() | addr2.to_v4());
342 }
343
344 boost::asio::ip::address operator&(const boost::asio::ip::address& addr1,
345                                    const boost::asio::ip::address& addr2)
346 {
347   if (addr1.is_v6())
348     return (addr1.to_v6() & addr2.to_v6());
349   else
350     return (addr1.to_v4() & addr2.to_v4());
351 }
352
353 boost::asio::ip::address operator~(const boost::asio::ip::address& addr1)
354 {
355   if (addr1.is_v6())
356     return ~(addr1.to_v6());
357   else
358     return ~(addr1.to_v4());
359 }
360
361 boost::asio::ip::address
362 route::prefix_t::mask() const
363 {
364   if (m_addr.is_v6()) {
365     boost::asio::ip::address_v6::bytes_type b =
366       boost::asio::ip::address_v6::any().to_bytes();
367
368     uint8_t n_bits = mask_width();
369
370     for (boost::asio::ip::address_v6::bytes_type::size_type ii = 0;
371          ii < b.max_size(); ii++) {
372       for (int8_t bit = 7; bit >= 0 && n_bits; bit--) {
373         b[ii] |= (1 << bit);
374         n_bits--;
375       }
376       if (!n_bits)
377         break;
378     }
379
380     return (boost::asio::ip::address_v6(b));
381   } else {
382     uint32_t a;
383
384     a = ~((1 << (32 - mask_width())) - 1);
385
386     return (boost::asio::ip::address_v4(a));
387   }
388 }
389
390 route::prefix_t
391 route::prefix_t::low() const
392 {
393   prefix_t pfx(*this);
394
395   pfx.m_addr = pfx.m_addr & pfx.mask();
396
397   return (pfx);
398 }
399
400 route::prefix_t
401 route::prefix_t::high() const
402 {
403   prefix_t pfx(*this);
404
405   pfx.m_addr = pfx.m_addr | ~pfx.mask();
406
407   return (pfx);
408 }
409
410 }; // namespace VOM
411
412 /*
413  * fd.io coding-style-patch-verification: ON
414  *
415  * Local Variables:
416  * eval: (c-set-style "mozilla")
417  * End:
418  */