Socket/Protocols/INet: Add 'host' and 'subnet' methods to INet4Network
g0dil [Tue, 7 Aug 2007 08:23:23 +0000 (08:23 +0000)]
Socket/Protocols/INet: Implement INet6Network

git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@381 270642c3-0616-0410-b53a-bc976706d245

Socket/Protocols/INet/INet4Address.cci
Socket/Protocols/INet/INet4Address.hh
Socket/Protocols/INet/INet4Address.test.cc
Socket/Protocols/INet/INet6Address.cc
Socket/Protocols/INet/INet6Address.cci
Socket/Protocols/INet/INet6Address.ct
Socket/Protocols/INet/INet6Address.hh
Socket/Protocols/INet/INet6Address.ih
Socket/Protocols/INet/INet6Address.test.cc

index ebf5615..a840eaf 100644 (file)
@@ -122,8 +122,17 @@ prefix_ bool senf::INet4Network::match(INet4Address addr)
 prefix_ bool senf::INet4Network::match(INet4Network net)
     const
 {
-    return net.prefix_len() >= prefix_len() &&
-        (net.address().address() & mask()) == address_.address();
+    return net.prefix_len() >= prefix_len() && match(net.address());
+}
+
+prefix_ senf::INet4Address senf::INet4Network::host(boost::uint32_t number)
+{
+    return INet4Address(address_.address() | (number & ~mask()));
+}
+
+prefix_ senf::INet4Network senf::INet4Network::subnet(boost::uint32_t net, unsigned prefix_len)
+{
+    return INet4Network(host(net << (32-prefix_len)),prefix_len);
 }
 
 ////////////////////////////////////////
index 818ffbe..b638448 100644 (file)
@@ -45,7 +45,7 @@ namespace senf {
         INet4Address represents a simple IP address. It is modelled as a fixed-size
         container/sequence of 4 bytes.
 
-        \see CheckINet4Network
+        \see CheckINet4Network \n INet4Network
 
         \implementation We awkwardly need to use static named constructors (<tt>from_</tt> members)
             instead of ordinarily overloaded constructors for one simple reason: <tt>char *</tt>
@@ -205,9 +205,6 @@ namespace senf {
     {
     public:
         ///////////////////////////////////////////////////////////////////////////
-        // Types
-
-        ///////////////////////////////////////////////////////////////////////////
         ///\name Structors and default members
         ///@{
 
@@ -231,6 +228,26 @@ namespace senf {
                                         /**< The is true, if \a net is sub-network (or the same as)
                                              \c this. */
 
+        INet4Address host(boost::uint32_t number); ///< Return the host with the given number
+                                        /**< Returns the host with the given number within the
+                                             network. If the number is larger than the maximum
+                                             host number in the network, it is truncated. So \c
+                                             host(0) is the networks own address, \c host(1)
+                                             customarily is the default router and \c host(-1) is
+                                             the broadcast address. */
+
+        INet4Network subnet(boost::uint32_t net, unsigned prefix_len);
+                                        ///< Return the given subnet of \c this
+                                        /**< The returned INet4Network will be a subnet of \c this
+                                             with the given network number. The network number is
+                                             comprised by the bits above \a prefix_len:
+                                             \code
+                                             INet4Network("192.168.0.0/16").subnet(111u,24u) == INet4Network("192.168.111.0/24")
+                                             INet4Network("192.168.111.0/24").subnet(1u,28u) == INet4Network("192.168.111.16/28")
+                                             \endcode 
+                                             \param[in] net network number
+                                             \param[in] prefix_len length of subnet prefix */
+
     protected:
 
     private:
index c1a8211..aaf58f8 100644 (file)
@@ -89,6 +89,9 @@ BOOST_AUTO_UNIT_TEST(inet4Network)
     BOOST_CHECK( ! net2.match(senf::INet4Network("192.0.0.0/15")) );
 
     BOOST_CHECK_EQUAL( boost::lexical_cast<std::string>(net2), "192.0.0.0/16" );
+
+    BOOST_CHECK_EQUAL( net2.host(-1), senf::INet4Address::from_string("192.0.255.255") );
+    BOOST_CHECK_EQUAL( boost::lexical_cast<std::string>(net2.subnet(2u,24u)), "192.0.2.0/24" );
 }
 
 ///////////////////////////////cc.e////////////////////////////////////////
index 7dfa642..8ece248 100644 (file)
@@ -32,6 +32,7 @@
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <netdb.h>
+#include <boost/lexical_cast.hpp>
 
 //#include "INet6Address.mpp"
 #define prefix_
@@ -102,6 +103,25 @@ senf::INet6Address const senf::INet6Address::Loopback   (0u,0u,0u,0u,0u,0u,0u,1u
 senf::INet6Address const senf::INet6Address::AllNodes   (0xFF02u,0u,0u,0u,0u,0u,0u,1u);
 senf::INet6Address const senf::INet6Address::AllRouters (0xFF02u,0u,0u,0u,0u,0u,0u,2u);
 
+///////////////////////////////////////////////////////////////////////////
+// senf::INet6Network
+
+prefix_ senf::INet6Network::INet6Network(std::string s)
+{
+    using boost::lambda::_1;
+    using boost::lambda::_2;
+    std::string::size_type i (s.find('/'));
+    if (i == std::string::npos)
+        throw INet6Address::SyntaxException();
+    try {
+        prefix_len_ = boost::lexical_cast<unsigned>(std::string(s,i+1));
+    } catch (boost::bad_lexical_cast const &) {
+        throw INet6Address::SyntaxException();
+    }
+    address_ = INet6Address::from_string(std::string(s, 0, i));
+    detail::apply_mask(prefix_len_, address_.begin(), address_.end(), _1 &= _2);
+}
+
 ///////////////////////////////cc.e////////////////////////////////////////
 #undef prefix_
 //#include "INet6Address.mpp"
index c3072b0..5c09acc 100644 (file)
@@ -23,6 +23,8 @@
 /** \file
     \brief INet6Address inline non-template implementation */
 
+#include "INet6Address.ih"
+
 // Custom includes
 #include <algorithm>
 #include <boost/lambda/lambda.hpp>
@@ -216,6 +218,97 @@ prefix_ void senf::INet6Address::id(boost::uint64_t id)
     (*this)[15] = id      ;
 }
 
+///////////////////////////////////////////////////////////////////////////
+// senf::INet6Network
+
+prefix_ senf::INet6Network::INet6Network()
+    : prefix_len_(), address_()
+{}
+
+prefix_ senf::INet6Network::INet6Network(INet6Address address, unsigned prefix_len)
+    : prefix_len_(prefix_len), address_(address)
+{
+    using boost::lambda::_1;
+    using boost::lambda::_2;
+    detail::apply_mask(prefix_len_, address_.begin(), address_.end(), _1 &= _2);
+}
+
+prefix_ senf::INet6Address const & senf::INet6Network::address()
+    const
+{
+    return address_;
+}
+
+prefix_ unsigned senf::INet6Network::prefix_len()
+    const
+{
+    return prefix_len_;
+}
+
+prefix_ bool senf::INet6Network::boolean_test()
+    const
+{
+    return prefix_len() && address();
+}
+
+prefix_ bool senf::INet6Network::operator==(INet6Network const & other)
+    const
+{
+    return prefix_len() == other.prefix_len() && address() == other.address();
+}
+
+prefix_ bool senf::INet6Network::match(INet6Address addr)
+    const
+{
+    using boost::lambda::_1;
+    using boost::lambda::_2;
+    using boost::lambda::_3;
+    return detail::find_if_mask(prefix_len_, address_.begin(), address_.end(), addr.begin(),
+                                _1 != (_2 & _3)) == address_.end();
+}
+
+prefix_ bool senf::INet6Network::match(INet6Network net)
+    const
+{
+    return net.prefix_len() >= prefix_len() && match(net.address());
+}
+
+prefix_ senf::INet6Address senf::INet6Network::host(boost::uint64_t id)
+{
+    INet6Address addr (address());
+    addr.id(id);
+    return addr;
+}
+
+prefix_ senf::INet6Network senf::INet6Network::subnet(boost::uint64_t net, unsigned prefix_len)
+{
+    using boost::lambda::_1;
+    using boost::lambda::_2;
+    using boost::lambda::_3;
+    using boost::lambda::var;
+    using boost::lambda::ret;
+    INet6Address addr (address());
+    net <<= (64-prefix_len);
+    detail::apply_mask(prefix_len, addr.begin(), addr.end(),
+                       ( ( _1 |= ret<boost::uint8_t>((var(net) >> 56) & _2) ),
+                         ( var(net) <<= 8 ) ));
+    return INet6Network(addr, prefix_len);
+}
+
+prefix_ std::ostream & senf::operator<<(std::ostream & os, INet6Network const & addr)
+{
+    os << addr.address() << '/' << addr.prefix_len();
+    return os;
+}
+
+///////////////////////////////////////////////////////////////////////////
+// namespace senf::detail members
+
+prefix_ boost::uint8_t senf::detail::low_bits_mask(unsigned bits)
+{
+    return ((1<<bits)-1);
+}
+
 ///////////////////////////////cci.e///////////////////////////////////////
 #undef prefix_
 
index 5e19703..fa63745 100644 (file)
@@ -44,6 +44,46 @@ prefix_ senf::INet6Address senf::INet6Address::from_data(InputIterator i)
     return addr;
 }
 
+///////////////////////////////////////////////////////////////////////////
+// namespace senf::detail members
+
+template <class ForwardIterator, class Function>
+prefix_ void senf::detail::apply_mask(unsigned bits, ForwardIterator b, ForwardIterator e,
+                                      Function fn)
+{
+    for(; b != e; ++b) {
+        boost::uint8_t mask (0);
+        if (bits > 8) {
+            mask = 0xFFu;
+            bits -= 8;
+        } else if (bits > 0) {
+            mask = ~ low_bits_mask(8-bits);
+            bits = 0;
+        }
+        fn(*b,mask);
+    }
+}
+
+template <class ForwardIterator1, class ForwardIterator2, class Function>
+prefix_ ForwardIterator1 senf::detail::find_if_mask(unsigned bits, ForwardIterator1 b1,
+                                                    ForwardIterator1 e1, ForwardIterator2 b2, 
+                                                    Function fn)
+{
+    for(; b1 != e1; ++b1, ++b2) {
+        boost::uint8_t mask (0);
+        if (bits > 8) {
+            mask = 0xFFu;
+            bits -= 8;
+        } else if (bits > 0) {
+            mask = ~ low_bits_mask(8-bits);
+            bits = 0;
+        }
+        if (fn(*b1,*b2,mask))
+            return b1;
+    }
+    return e1;
+}
+
 ///////////////////////////////ct.e////////////////////////////////////////
 #undef prefix_
 
index 4ef43e7..2032aff 100644 (file)
@@ -32,6 +32,7 @@
 #include <boost/cstdint.hpp>
 #include <boost/function.hpp>
 #include <boost/array.hpp>
+#include <boost/operators.hpp>
 #include "Utils/SafeBool.hh"
 #include "INet4Address.hh"
 
@@ -86,7 +87,7 @@ namespace senf {
         The INet6Address class is based on \c boost::array and is built as a fixed-size sequence of
         16 bytes.
 
-        \see CheckINet6Network Helper to check address against an arbitrary fixed network prefix
+        \see CheckINet6Network \n INet6Network
         \ingroup addr_group
 
         \implementation We awkwardly need to use static named constructors (<tt>from_</tt> members)
@@ -302,6 +303,66 @@ namespace senf {
         : public detail::CheckINet6Network_impl<a0,a1,a2,a3,a4,a5,a6,a7,a8>
     {};
 
+    /** \brief IpV6 network prefix
+
+        This class represents an IpV6 network prefix in CIDR notation. 
+      */
+    class INet6Network
+        : public boost::equality_comparable<INet6Network>, 
+          public ComparableSafeBool<INet6Network>
+    {
+    public:
+        ///////////////////////////////////////////////////////////////////////////
+        ///\name Structors and default members
+        ///@{
+
+        INet6Network();                 ///< Construct empty (::/0) network
+        INet6Network(INet6Address address, unsigned prefix_len);
+                                        ///< Construct network from given address and prefix length
+        explicit INet6Network(std::string s); ///< Construct network from CIDR notation
+
+        ///@}
+        ///////////////////////////////////////////////////////////////////////////
+
+        INet6Address const & address() const; ///< Get the network address
+        unsigned prefix_len() const;    ///< Get the network prefix length
+
+        bool boolean_test() const;      ///< \c true, if INet6Network is non-empty
+        bool operator==(INet6Network const & other) const;
+                                        ///< Compare two networks for equality
+        
+        bool match(INet6Address addr) const; ///< \c true, if the network includes \a addr
+        bool match(INet6Network net) const; ///< \c true, if the network includes \a net
+                                        /**< The is true, if \a net is sub-network (or the same as)
+                                             \c this. */
+        INet6Address host(boost::uint64_t id); ///< Return the host with the given id
+                                        /**< Returns the host with the given number within the
+                                             network. This call replaces the lower 64 bits of the
+                                             network address with the given id. */
+
+        INet6Network subnet(boost::uint64_t net, unsigned prefix_len);
+                                        ///< Return the given subnet of \c this
+                                        /**< The returned INet6Network will be a subnet of \c this
+                                             with the given network number. The network number is
+                                             comprised by the bits above \a prefix_len:
+                                             \code
+                                             INet6Network("2001:db8::/32").subnet(0x12u,40u) == INet6Network("2001:db8:1200::/40")
+                                             INet6Network("2001:db8:1200::/40").subnet(0x2345,64u) == INet6Network("2001:db8:1200:2345::/64")
+                                             \endcode 
+                                             \param[in] net network number
+                                             \param[in] prefix_len length of subnet prefix */
+
+    protected:
+
+    private:
+        unsigned prefix_len_;
+        INet6Address address_;
+    };
+
+    /** \brief Output INet6Network instance as it's string representation
+        \related INet6Network
+     */
+    std::ostream & operator<<(std::ostream & os, INet6Network const & addr);
 }
 
 ///////////////////////////////hh.e////////////////////////////////////////
index 782880d..a41e8c6 100644 (file)
@@ -182,12 +182,18 @@ namespace detail {
         : public CheckINet6Network_impl2<a0,0,0,0,0,0,0,0,a1>
     {};
 
-    template <boost::uint64_t net, boost::uint64_t id, unsigned bits, unsigned bytes>
-    struct IsINet6Network_impl
-    {};
-
 #   endif
 
+    boost::uint8_t low_bits_mask(unsigned bits);
+
+    template <class ForwardIterator, class Function>
+    void apply_mask(unsigned bits, ForwardIterator b, ForwardIterator e, Function fn);
+
+    template <class ForwardIterator1, class ForwardIterator2, class Function>
+    ForwardIterator1 find_if_mask(unsigned bits, 
+                                  ForwardIterator1 b1, ForwardIterator1 e1, ForwardIterator2 b2,
+                                  Function fn);
+
 }}
 
 ///////////////////////////////ih.e////////////////////////////////////////
index 240e1d4..253c214 100644 (file)
@@ -138,6 +138,32 @@ BOOST_AUTO_UNIT_TEST(inet6Address)
     }
 }
 
+BOOST_AUTO_UNIT_TEST(inet6Network)
+{
+    senf::INet6Network net (senf::INet6Address(0xFF14u,0x1234u),32u);
+    BOOST_CHECK_EQUAL( boost::lexical_cast<std::string>(net.address()), "ff14:1234::");
+    BOOST_CHECK_EQUAL( net.prefix_len(), 32u );
+    BOOST_CHECK( net );
+    BOOST_CHECK( ! senf::INet6Network() );
+    
+    senf::INet6Network net2 ("2001:db8:1234::/44");
+    BOOST_CHECK_EQUAL( net2.address(), senf::INet6Address::from_string("2001:db8:1230::") );
+    BOOST_CHECK_EQUAL( net2.prefix_len(), 44u );
+
+    BOOST_CHECK( net != net2 );
+    BOOST_CHECK( net.match(senf::INet6Address::from_string("ff14:1234::1")) );
+    BOOST_CHECK( ! net2.match(senf::INet6Address::from_string("ff13:1234::1")) );
+    BOOST_CHECK( ! net.match(net2) );
+    BOOST_CHECK( net2.match(senf::INet6Network("2001:db8:1234::/48")) );
+    BOOST_CHECK( ! net2.match(senf::INet6Network("2001:db8:1234::/32")) );
+
+    BOOST_CHECK_EQUAL( boost::lexical_cast<std::string>(net2), "2001:db8:1230::/44" );
+
+    BOOST_CHECK_EQUAL( net2.host(0x1234u), senf::INet6Address::from_string("2001:db8:1230::1234") );
+    BOOST_CHECK_EQUAL( boost::lexical_cast<std::string>(net2.subnet(2u,48u)), 
+                       "2001:db8:1232::/48" );
+}
+
 ///////////////////////////////cc.e////////////////////////////////////////
 #undef prefix_