+#include <algorithm>
+#include <stdexcept>
+#include <arpa/inet.h>
+#include <netinet/ether.h>
+#include "address.h"
+
+using namespace std;
+
+Address::Address():
+ type(0),
+ length(0),
+ mask_bits(0)
+{ }
+
+Address::Address(uint32_t a):
+ type(ETH_P_IP),
+ length(4),
+ mask_bits(32)
+{
+ for(unsigned i=0; i<4; ++i)
+ data[i] = a>>(24-i*8);
+}
+
+Address::Address(unsigned short t, const unsigned char *d, unsigned char l):
+ type(t),
+ length(l)
+{
+ copy(d, d+l, data);
+}
+
+void Address::set_mask(const Address &mask)
+{
+ if(mask.length!=length)
+ throw invalid_argument("Address::set_mask");
+
+ mask_bits = 0;
+ for(unsigned i=0; (i<length && mask.data[i]); ++i)
+ {
+ mask_bits += 8;
+ if(mask.data[i]!=0xFF)
+ {
+ for(unsigned d=mask.data[i]; !(d&1); d>>=1)
+ --mask_bits;
+ break;
+ }
+ }
+}
+
+string Address::str() const
+{
+ if(type==ETH_P_IP)
+ {
+ char buf[INET_ADDRSTRLEN];
+ return inet_ntop(AF_INET, data, buf, INET_ADDRSTRLEN);
+ }
+ else
+ return "<unknown>";
+}
+
+unsigned Address::common_prefix_length(const Address &other) const
+{
+ if(length!=other.length)
+ return 0;
+
+ unsigned prefix = 0;
+ for(unsigned i=0; i<length; ++i)
+ {
+ prefix += 8;
+ if(data[i]!=other.data[i])
+ {
+ unsigned mismatch = data[i]^other.data[i];
+ for(; mismatch; mismatch>>=1)
+ --prefix;
+ break;
+ }
+ }
+
+ return prefix;
+}
+
+bool Address::masked_match(const Address &other) const
+{
+ unsigned prefix = common_prefix_length(other);
+ unsigned mask = min(mask_bits, other.mask_bits);
+ return prefix>=mask;
+}
+
+void Address::to_sockaddr(sockaddr_storage &sa) const
+{
+ if(type==ETH_P_IP)
+ {
+ sockaddr_in &sa_in = reinterpret_cast<sockaddr_in &>(sa);
+ sa_in.sin_family = AF_INET;
+ sa_in.sin_port = 0;
+ sa_in.sin_addr.s_addr = *(uint32_t *)data;
+ }
+ else
+ throw runtime_error("Unknown type");
+}
+
+bool Address::operator<(const Address &other) const
+{
+ if(length!=other.length)
+ return length<other.length;
+ for(unsigned i=0; i<length; ++i)
+ if(data[i]!=other.data[i])
+ return data[i]<other.data[i];
+ return false;
+}