VOM: prefix bit fiddling
[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 /*
57  * Keep this in sync with VPP's dpo_proto_t
58  */
59 const nh_proto_t nh_proto_t::IPV4(0, "ipv4");
60 const nh_proto_t nh_proto_t::IPV6(1, "ipv6");
61 const nh_proto_t nh_proto_t::MPLS(2, "mpls");
62 const nh_proto_t nh_proto_t::ETHERNET(3, "ethernet");
63
64 nh_proto_t::nh_proto_t(int v, const std::string& s)
65   : enum_base<nh_proto_t>(v, s)
66 {
67 }
68
69 const nh_proto_t&
70 nh_proto_t::from_address(const boost::asio::ip::address& addr)
71 {
72   if (addr.is_v6()) {
73     return IPV6;
74   }
75
76   return IPV4;
77 }
78
79 /**
80  * The all Zeros prefix
81  */
82 const route::prefix_t route::prefix_t::ZERO("0.0.0.0", 0);
83 const route::prefix_t route::prefix_t::ZEROv6("::", 0);
84
85 route::prefix_t::prefix_t(const boost::asio::ip::address& addr, uint8_t len)
86   : m_addr(addr)
87   , m_len(len)
88 {
89 }
90
91 route::prefix_t::prefix_t(const boost::asio::ip::address& addr)
92   : m_addr(addr)
93   , m_len(VOM::mask_width(addr))
94 {
95 }
96
97 route::prefix_t::prefix_t(const std::string& s, uint8_t len)
98   : m_addr(boost::asio::ip::address::from_string(s))
99   , m_len(len)
100 {
101 }
102
103 route::prefix_t::prefix_t(const prefix_t& o)
104   : m_addr(o.m_addr)
105   , m_len(o.m_len)
106 {
107 }
108
109 route::prefix_t::prefix_t()
110   : m_addr()
111   , m_len(0)
112 {
113 }
114
115 route::prefix_t::~prefix_t()
116 {
117 }
118
119 route::prefix_t&
120 route::prefix_t::operator=(const route::prefix_t& o)
121 {
122   m_addr = o.m_addr;
123   m_len = o.m_len;
124
125   return (*this);
126 }
127
128 const boost::asio::ip::address&
129 route::prefix_t::address() const
130 {
131   return (m_addr);
132 }
133
134 uint8_t
135 route::prefix_t::mask_width() const
136 {
137   return (m_len);
138 }
139
140 bool
141 route::prefix_t::operator<(const route::prefix_t& o) const
142 {
143   if (m_len == o.m_len) {
144     return (m_addr < o.m_addr);
145   } else {
146     return (m_len < o.m_len);
147   }
148 }
149
150 bool
151 route::prefix_t::operator==(const route::prefix_t& o) const
152 {
153   return (m_len == o.m_len && m_addr == o.m_addr);
154 }
155
156 bool
157 route::prefix_t::operator!=(const route::prefix_t& o) const
158 {
159   return (!(*this == o));
160 }
161
162 std::string
163 route::prefix_t::to_string() const
164 {
165   std::ostringstream s;
166
167   s << m_addr.to_string() << "/" << std::to_string(m_len);
168
169   return (s.str());
170 }
171
172 boost::asio::ip::address
173 from_bytes(uint8_t is_ip6, uint8_t* bytes)
174 {
175   boost::asio::ip::address addr;
176
177   if (is_ip6) {
178     std::array<uint8_t, 16> a;
179     std::copy(bytes, bytes + 16, std::begin(a));
180     boost::asio::ip::address_v6 v6(a);
181     addr = v6;
182   } else {
183     std::array<uint8_t, 4> a;
184     std::copy(bytes, bytes + 4, std::begin(a));
185     boost::asio::ip::address_v4 v4(a);
186     addr = v4;
187   }
188
189   return (addr);
190 }
191
192 route::prefix_t::prefix_t(uint8_t is_ip6, uint8_t* addr, uint8_t len)
193   : m_addr(from_bytes(is_ip6, addr))
194   , m_len(len)
195 {
196 }
197 void
198 to_bytes(const boost::asio::ip::address_v6& addr, uint8_t* array)
199 {
200   memcpy(array, addr.to_bytes().data(), 16);
201 }
202
203 void
204 to_bytes(const boost::asio::ip::address_v4& addr, uint8_t* array)
205 {
206   memcpy(array, addr.to_bytes().data(), 4);
207 }
208
209 void
210 to_bytes(const boost::asio::ip::address& addr, uint8_t* is_ip6, uint8_t* array)
211 {
212   if (addr.is_v6()) {
213     *is_ip6 = 1;
214     to_bytes(addr.to_v6(), array);
215   } else {
216     *is_ip6 = 0;
217     to_bytes(addr.to_v4(), array);
218   }
219 }
220
221 uint32_t
222 mask_width(const boost::asio::ip::address& addr)
223 {
224   if (addr.is_v6()) {
225     return 128;
226   }
227   return 32;
228 }
229
230 void
231 route::prefix_t::to_vpp(uint8_t* is_ip6, uint8_t* addr, uint8_t* len) const
232 {
233   *len = m_len;
234   to_bytes(m_addr, is_ip6, addr);
235 }
236
237 l3_proto_t
238 route::prefix_t::l3_proto() const
239 {
240   if (m_addr.is_v6()) {
241     return (l3_proto_t::IPV6);
242   } else {
243     return (l3_proto_t::IPV4);
244   }
245
246   return (l3_proto_t::IPV4);
247 }
248
249 std::ostream&
250 operator<<(std::ostream& os, const route::prefix_t& pfx)
251 {
252   os << pfx.to_string();
253
254   return (os);
255 }
256
257 boost::asio::ip::address_v4
258 operator|(const boost::asio::ip::address_v4& addr1,
259           const boost::asio::ip::address_v4& addr2)
260 {
261   uint32_t a;
262   a = addr1.to_ulong() | addr2.to_ulong();
263   boost::asio::ip::address_v4 addr(a);
264   return (addr);
265 }
266
267 boost::asio::ip::address_v4 operator&(const boost::asio::ip::address_v4& addr1,
268                                       const boost::asio::ip::address_v4& addr2)
269 {
270   uint32_t a;
271   a = addr1.to_ulong() & addr2.to_ulong();
272   boost::asio::ip::address_v4 addr(a);
273   return (addr);
274 }
275
276 boost::asio::ip::address_v4 operator~(const boost::asio::ip::address_v4& addr1)
277 {
278   uint32_t a;
279   a = ~addr1.to_ulong();
280   boost::asio::ip::address_v4 addr(a);
281   return (addr);
282 }
283
284 boost::asio::ip::address_v6
285 operator|(const boost::asio::ip::address_v6& addr1,
286           const boost::asio::ip::address_v6& addr2)
287 {
288   boost::asio::ip::address_v6::bytes_type b1 = addr1.to_bytes();
289   boost::asio::ip::address_v6::bytes_type b2 = addr2.to_bytes();
290
291   for (boost::asio::ip::address_v6::bytes_type::size_type ii = 0;
292        ii < b1.max_size(); ii++) {
293     b1[ii] |= b2[ii];
294   }
295
296   boost::asio::ip::address_v6 addr(b1);
297   return (addr);
298 }
299
300 boost::asio::ip::address_v6 operator&(const boost::asio::ip::address_v6& addr1,
301                                       const boost::asio::ip::address_v6& addr2)
302 {
303   boost::asio::ip::address_v6::bytes_type b1 = addr1.to_bytes();
304   boost::asio::ip::address_v6::bytes_type b2 = addr2.to_bytes();
305
306   for (boost::asio::ip::address_v6::bytes_type::size_type ii = 0;
307        ii < b1.max_size(); ii++) {
308     b1[ii] &= b2[ii];
309   }
310
311   boost::asio::ip::address_v6 addr(b1);
312   return (addr);
313 }
314
315 boost::asio::ip::address_v6 operator~(const boost::asio::ip::address_v6& addr1)
316 {
317   boost::asio::ip::address_v6::bytes_type b1 = addr1.to_bytes();
318
319   for (boost::asio::ip::address_v6::bytes_type::size_type ii = 0;
320        ii < b1.max_size(); ii++) {
321     b1[ii] = ~b1[ii];
322   }
323
324   boost::asio::ip::address_v6 addr(b1);
325   return (addr);
326 }
327 boost::asio::ip::address
328 operator|(const boost::asio::ip::address& addr1,
329           const boost::asio::ip::address& addr2)
330 {
331   if (addr1.is_v6())
332     return (addr1.to_v6() | addr2.to_v6());
333   else
334     return (addr1.to_v4() | addr2.to_v4());
335 }
336
337 boost::asio::ip::address operator&(const boost::asio::ip::address& addr1,
338                                    const boost::asio::ip::address& addr2)
339 {
340   if (addr1.is_v6())
341     return (addr1.to_v6() & addr2.to_v6());
342   else
343     return (addr1.to_v4() & addr2.to_v4());
344 }
345
346 boost::asio::ip::address operator~(const boost::asio::ip::address& addr1)
347 {
348   if (addr1.is_v6())
349     return ~(addr1.to_v6());
350   else
351     return ~(addr1.to_v4());
352 }
353
354 boost::asio::ip::address
355 route::prefix_t::mask() const
356 {
357   if (m_addr.is_v6()) {
358     boost::asio::ip::address_v6::bytes_type b =
359       boost::asio::ip::address_v6::any().to_bytes();
360
361     uint8_t n_bits = mask_width();
362
363     for (boost::asio::ip::address_v6::bytes_type::size_type ii = 0;
364          ii < b.max_size(); ii++) {
365       for (int8_t bit = 7; bit >= 0 && n_bits; bit--) {
366         b[ii] |= (1 << bit);
367         n_bits--;
368       }
369       if (!n_bits)
370         break;
371     }
372
373     return (boost::asio::ip::address_v6(b));
374   } else {
375     uint32_t a;
376
377     a = ~((1 << (32 - mask_width())) - 1);
378
379     return (boost::asio::ip::address_v4(a));
380   }
381 }
382
383 route::prefix_t
384 route::prefix_t::low() const
385 {
386   prefix_t pfx(*this);
387
388   pfx.m_addr = pfx.m_addr & pfx.mask();
389
390   return (pfx);
391 }
392
393 route::prefix_t
394 route::prefix_t::high() const
395 {
396   prefix_t pfx(*this);
397
398   pfx.m_addr = pfx.m_addr | ~pfx.mask();
399
400   return (pfx);
401 }
402
403 }; // namespace VOM
404
405 /*
406  * fd.io coding-style-patch-verification: ON
407  *
408  * Local Variables:
409  * eval: (c-set-style "mozilla")
410  * End:
411  */