| /* |
| * Copyright (c) 2017 Cisco and/or its affiliates. |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at: |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <boost/algorithm/string.hpp> |
| #include <sstream> |
| |
| #include "vom/prefix.hpp" |
| |
| namespace VOM { |
| /* |
| * Keep this in sync with VPP's fib_protocol_t |
| */ |
| const l3_proto_t l3_proto_t::IPV4(0, "ipv4"); |
| const l3_proto_t l3_proto_t::IPV6(1, "ipv6"); |
| const l3_proto_t l3_proto_t::MPLS(2, "mpls"); |
| |
| l3_proto_t::l3_proto_t(int v, const std::string& s) |
| : enum_base<l3_proto_t>(v, s) |
| { |
| } |
| |
| bool |
| l3_proto_t::is_ipv6() |
| { |
| return (*this == IPV6); |
| } |
| |
| bool |
| l3_proto_t::is_ipv4() |
| { |
| return (*this == IPV4); |
| } |
| |
| const l3_proto_t& |
| l3_proto_t::from_address(const boost::asio::ip::address& addr) |
| { |
| if (addr.is_v6()) { |
| return IPV6; |
| } |
| |
| return IPV4; |
| } |
| |
| std::ostream& |
| operator<<(std::ostream& os, const l3_proto_t& l3p) |
| { |
| os << l3p.to_string(); |
| return os; |
| } |
| |
| /* |
| * Keep this in sync with VPP's dpo_proto_t |
| */ |
| const nh_proto_t nh_proto_t::IPV4(0, "ipv4"); |
| const nh_proto_t nh_proto_t::IPV6(1, "ipv6"); |
| const nh_proto_t nh_proto_t::MPLS(2, "mpls"); |
| const nh_proto_t nh_proto_t::ETHERNET(3, "ethernet"); |
| |
| nh_proto_t::nh_proto_t(int v, const std::string& s) |
| : enum_base<nh_proto_t>(v, s) |
| { |
| } |
| |
| const nh_proto_t& |
| nh_proto_t::from_address(const boost::asio::ip::address& addr) |
| { |
| if (addr.is_v6()) { |
| return IPV6; |
| } |
| |
| return IPV4; |
| } |
| |
| /** |
| * The all Zeros prefix |
| */ |
| const route::prefix_t route::prefix_t::ZERO("0.0.0.0", 0); |
| const route::prefix_t route::prefix_t::ZEROv6("::", 0); |
| |
| route::prefix_t::prefix_t(const boost::asio::ip::address& addr, uint8_t len) |
| : m_addr(addr) |
| , m_len(len) |
| { |
| } |
| |
| route::prefix_t::prefix_t(const boost::asio::ip::address& addr) |
| : m_addr(addr) |
| , m_len(VOM::mask_width(addr)) |
| { |
| } |
| |
| route::prefix_t::prefix_t(const std::string& s, uint8_t len) |
| : m_addr(boost::asio::ip::address::from_string(s)) |
| , m_len(len) |
| { |
| } |
| |
| route::prefix_t::prefix_t(const prefix_t& o) |
| : m_addr(o.m_addr) |
| , m_len(o.m_len) |
| { |
| } |
| |
| route::prefix_t::prefix_t() |
| : m_addr() |
| , m_len(0) |
| { |
| } |
| |
| route::prefix_t::~prefix_t() |
| { |
| } |
| |
| route::prefix_t& |
| route::prefix_t::operator=(const route::prefix_t& o) |
| { |
| m_addr = o.m_addr; |
| m_len = o.m_len; |
| |
| return (*this); |
| } |
| |
| const boost::asio::ip::address& |
| route::prefix_t::address() const |
| { |
| return (m_addr); |
| } |
| |
| uint8_t |
| route::prefix_t::mask_width() const |
| { |
| return (m_len); |
| } |
| |
| bool |
| route::prefix_t::operator<(const route::prefix_t& o) const |
| { |
| if (m_len == o.m_len) { |
| return (m_addr < o.m_addr); |
| } else { |
| return (m_len < o.m_len); |
| } |
| } |
| |
| bool |
| route::prefix_t::operator==(const route::prefix_t& o) const |
| { |
| return (m_len == o.m_len && m_addr == o.m_addr); |
| } |
| |
| bool |
| route::prefix_t::operator!=(const route::prefix_t& o) const |
| { |
| return (!(*this == o)); |
| } |
| |
| std::string |
| route::prefix_t::to_string() const |
| { |
| std::ostringstream s; |
| |
| s << m_addr.to_string() << "/" << std::to_string(m_len); |
| |
| return (s.str()); |
| } |
| |
| boost::asio::ip::address |
| from_bytes(uint8_t is_ip6, uint8_t* bytes) |
| { |
| boost::asio::ip::address addr; |
| |
| if (is_ip6) { |
| std::array<uint8_t, 16> a; |
| std::copy(bytes, bytes + 16, std::begin(a)); |
| boost::asio::ip::address_v6 v6(a); |
| addr = v6; |
| } else { |
| std::array<uint8_t, 4> a; |
| std::copy(bytes, bytes + 4, std::begin(a)); |
| boost::asio::ip::address_v4 v4(a); |
| addr = v4; |
| } |
| |
| return (addr); |
| } |
| |
| route::prefix_t::prefix_t(uint8_t is_ip6, uint8_t* addr, uint8_t len) |
| : m_addr(from_bytes(is_ip6, addr)) |
| , m_len(len) |
| { |
| } |
| void |
| to_bytes(const boost::asio::ip::address_v6& addr, uint8_t* array) |
| { |
| memcpy(array, addr.to_bytes().data(), 16); |
| } |
| |
| void |
| to_bytes(const boost::asio::ip::address_v4& addr, uint8_t* array) |
| { |
| memcpy(array, addr.to_bytes().data(), 4); |
| } |
| |
| void |
| to_bytes(const boost::asio::ip::address& addr, uint8_t* is_ip6, uint8_t* array) |
| { |
| if (addr.is_v6()) { |
| *is_ip6 = 1; |
| to_bytes(addr.to_v6(), array); |
| } else { |
| *is_ip6 = 0; |
| to_bytes(addr.to_v4(), array); |
| } |
| } |
| |
| uint32_t |
| mask_width(const boost::asio::ip::address& addr) |
| { |
| if (addr.is_v6()) { |
| return 128; |
| } |
| return 32; |
| } |
| |
| void |
| route::prefix_t::to_vpp(uint8_t* is_ip6, uint8_t* addr, uint8_t* len) const |
| { |
| *len = m_len; |
| to_bytes(m_addr, is_ip6, addr); |
| } |
| |
| l3_proto_t |
| route::prefix_t::l3_proto() const |
| { |
| if (m_addr.is_v6()) { |
| return (l3_proto_t::IPV6); |
| } else { |
| return (l3_proto_t::IPV4); |
| } |
| |
| return (l3_proto_t::IPV4); |
| } |
| |
| std::ostream& |
| operator<<(std::ostream& os, const route::prefix_t& pfx) |
| { |
| os << pfx.to_string(); |
| |
| return (os); |
| } |
| |
| boost::asio::ip::address_v4 |
| operator|(const boost::asio::ip::address_v4& addr1, |
| const boost::asio::ip::address_v4& addr2) |
| { |
| uint32_t a; |
| a = addr1.to_ulong() | addr2.to_ulong(); |
| boost::asio::ip::address_v4 addr(a); |
| return (addr); |
| } |
| |
| boost::asio::ip::address_v4 operator&(const boost::asio::ip::address_v4& addr1, |
| const boost::asio::ip::address_v4& addr2) |
| { |
| uint32_t a; |
| a = addr1.to_ulong() & addr2.to_ulong(); |
| boost::asio::ip::address_v4 addr(a); |
| return (addr); |
| } |
| |
| boost::asio::ip::address_v4 operator~(const boost::asio::ip::address_v4& addr1) |
| { |
| uint32_t a; |
| a = ~addr1.to_ulong(); |
| boost::asio::ip::address_v4 addr(a); |
| return (addr); |
| } |
| |
| boost::asio::ip::address_v6 |
| operator|(const boost::asio::ip::address_v6& addr1, |
| const boost::asio::ip::address_v6& addr2) |
| { |
| boost::asio::ip::address_v6::bytes_type b1 = addr1.to_bytes(); |
| boost::asio::ip::address_v6::bytes_type b2 = addr2.to_bytes(); |
| |
| for (boost::asio::ip::address_v6::bytes_type::size_type ii = 0; |
| ii < b1.max_size(); ii++) { |
| b1[ii] |= b2[ii]; |
| } |
| |
| boost::asio::ip::address_v6 addr(b1); |
| return (addr); |
| } |
| |
| boost::asio::ip::address_v6 operator&(const boost::asio::ip::address_v6& addr1, |
| const boost::asio::ip::address_v6& addr2) |
| { |
| boost::asio::ip::address_v6::bytes_type b1 = addr1.to_bytes(); |
| boost::asio::ip::address_v6::bytes_type b2 = addr2.to_bytes(); |
| |
| for (boost::asio::ip::address_v6::bytes_type::size_type ii = 0; |
| ii < b1.max_size(); ii++) { |
| b1[ii] &= b2[ii]; |
| } |
| |
| boost::asio::ip::address_v6 addr(b1); |
| return (addr); |
| } |
| |
| boost::asio::ip::address_v6 operator~(const boost::asio::ip::address_v6& addr1) |
| { |
| boost::asio::ip::address_v6::bytes_type b1 = addr1.to_bytes(); |
| |
| for (boost::asio::ip::address_v6::bytes_type::size_type ii = 0; |
| ii < b1.max_size(); ii++) { |
| b1[ii] = ~b1[ii]; |
| } |
| |
| boost::asio::ip::address_v6 addr(b1); |
| return (addr); |
| } |
| boost::asio::ip::address |
| operator|(const boost::asio::ip::address& addr1, |
| const boost::asio::ip::address& addr2) |
| { |
| if (addr1.is_v6()) |
| return (addr1.to_v6() | addr2.to_v6()); |
| else |
| return (addr1.to_v4() | addr2.to_v4()); |
| } |
| |
| boost::asio::ip::address operator&(const boost::asio::ip::address& addr1, |
| const boost::asio::ip::address& addr2) |
| { |
| if (addr1.is_v6()) |
| return (addr1.to_v6() & addr2.to_v6()); |
| else |
| return (addr1.to_v4() & addr2.to_v4()); |
| } |
| |
| boost::asio::ip::address operator~(const boost::asio::ip::address& addr1) |
| { |
| if (addr1.is_v6()) |
| return ~(addr1.to_v6()); |
| else |
| return ~(addr1.to_v4()); |
| } |
| |
| boost::asio::ip::address |
| route::prefix_t::mask() const |
| { |
| if (m_addr.is_v6()) { |
| boost::asio::ip::address_v6::bytes_type b = |
| boost::asio::ip::address_v6::any().to_bytes(); |
| |
| uint8_t n_bits = mask_width(); |
| |
| for (boost::asio::ip::address_v6::bytes_type::size_type ii = 0; |
| ii < b.max_size(); ii++) { |
| for (int8_t bit = 7; bit >= 0 && n_bits; bit--) { |
| b[ii] |= (1 << bit); |
| n_bits--; |
| } |
| if (!n_bits) |
| break; |
| } |
| |
| return (boost::asio::ip::address_v6(b)); |
| } else { |
| uint32_t a; |
| |
| a = ~((1 << (32 - mask_width())) - 1); |
| |
| return (boost::asio::ip::address_v4(a)); |
| } |
| } |
| |
| route::prefix_t |
| route::prefix_t::low() const |
| { |
| prefix_t pfx(*this); |
| |
| pfx.m_addr = pfx.m_addr & pfx.mask(); |
| |
| return (pfx); |
| } |
| |
| route::prefix_t |
| route::prefix_t::high() const |
| { |
| prefix_t pfx(*this); |
| |
| pfx.m_addr = pfx.m_addr | ~pfx.mask(); |
| |
| return (pfx); |
| } |
| |
| }; // namespace VOM |
| |
| /* |
| * fd.io coding-style-patch-verification: ON |
| * |
| * Local Variables: |
| * eval: (c-set-style "mozilla") |
| * End: |
| */ |