]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
common/ipaddr: Find a configured IP address in given subnet.
authorTommi Virtanen <tommi.virtanen@dreamhost.com>
Wed, 16 Nov 2011 21:39:29 +0000 (13:39 -0800)
committerTommi Virtanen <tv@eagain.net>
Mon, 21 Nov 2011 22:27:44 +0000 (14:27 -0800)
Signed-off-by: Tommi Virtanen <tommi.virtanen@dreamhost.com>
src/Makefile.am
src/common/ipaddr.cc [new file with mode: 0644]
src/include/ipaddr.h [new file with mode: 0644]
src/test/test_ipaddr.cc [new file with mode: 0644]

index 2800ff13eb6fd3df6741366b3ab382fddeda2841..011ac58bb80dc87e8ad7d3f99654346fecfa905b 100644 (file)
@@ -618,6 +618,11 @@ unittest_interval_tree_LDADD = ${UNITTEST_LDADD} $(LIBGLOBAL_LDA)
 unittest_interval_tree_CXXFLAGS = ${AM_CXXFLAGS} ${UNITTEST_CXXFLAGS}
 check_PROGRAMS += unittest_interval_tree
 
+unittest_ipaddr_SOURCES = test/test_ipaddr.cc
+unittest_ipaddr_LDADD = ${UNITTEST_LDADD} $(LIBGLOBAL_LDA)
+unittest_ipaddr_CXXFLAGS = ${AM_CXXFLAGS} ${UNITTEST_CXXFLAGS}
+check_PROGRAMS += unittest_ipaddr
+
 test_librbd_SOURCES = test/test_librbd.cc
 test_librbd_LDADD =  librbd.la librados.la ${UNITTEST_STATIC_LDADD}
 test_librbd_CXXFLAGS = ${AM_CXXFLAGS} ${UNITTEST_CXXFLAGS}
@@ -910,6 +915,7 @@ libcommon_files = \
        common/hex.cc \
        common/entity_name.cc \
        common/ceph_crypto.cc \
+       common/ipaddr.cc \
        include/addr_parsing.c \
        $(crush_files)
 
diff --git a/src/common/ipaddr.cc b/src/common/ipaddr.cc
new file mode 100644 (file)
index 0000000..a2cdc92
--- /dev/null
@@ -0,0 +1,100 @@
+#include "include/ipaddr.h"
+
+#include <string.h>
+
+
+static void netmask_ipv4(const struct in_addr *addr,
+                        unsigned int prefix_len,
+                        struct in_addr *out) {
+  uint32_t mask;
+
+  if (prefix_len >= 32) {
+    // also handle 32 in this branch, because >>32 is not defined by
+    // the C standards
+    mask = ~uint32_t(0);
+  } else {
+    mask = htonl(~(~uint32_t(0) >> prefix_len));
+  }
+  out->s_addr = addr->s_addr & mask;
+}
+
+
+const struct sockaddr *find_ipv4_in_subnet(const struct ifaddrs *addrs,
+                                          const struct sockaddr_in *net,
+                                          unsigned int prefix_len) {
+  struct in_addr want, temp;
+
+  netmask_ipv4(&net->sin_addr, prefix_len, &want);
+
+  for (; addrs != NULL; addrs = addrs->ifa_next) {
+
+    if (addrs->ifa_addr == NULL)
+      continue;
+
+    if (addrs->ifa_addr->sa_family != net->sin_family)
+      continue;
+
+    struct in_addr *cur = &((struct sockaddr_in*)addrs->ifa_addr)->sin_addr;
+    netmask_ipv4(cur, prefix_len, &temp);
+
+    if (temp.s_addr == want.s_addr) {
+      return addrs->ifa_addr;
+    }
+  }
+
+  return NULL;
+}
+
+
+static void netmask_ipv6(const struct in6_addr *addr,
+                        unsigned int prefix_len,
+                        struct in6_addr *out) {
+  if (prefix_len > 128)
+    prefix_len = 128;
+
+  memcpy(out->s6_addr, addr->s6_addr, prefix_len/8);
+  out->s6_addr[prefix_len/8] = addr->s6_addr[prefix_len/8] & ~( 0xFF >> (prefix_len % 8) );
+  if (prefix_len/8 < 15)
+    memset(out->s6_addr+prefix_len/8+1, 0, 16-prefix_len/8-1);
+}
+
+
+const struct sockaddr *find_ipv6_in_subnet(const struct ifaddrs *addrs,
+                                          const struct sockaddr_in6 *net,
+                                          unsigned int prefix_len) {
+  struct in6_addr want, temp;
+
+  netmask_ipv6(&net->sin6_addr, prefix_len, &want);
+
+  for (; addrs != NULL; addrs = addrs->ifa_next) {
+
+    if (addrs->ifa_addr == NULL)
+      continue;
+
+    if (addrs->ifa_addr->sa_family != net->sin6_family)
+      continue;
+
+    struct in6_addr *cur = &((struct sockaddr_in6*)addrs->ifa_addr)->sin6_addr;
+    netmask_ipv6(cur, prefix_len, &temp);
+
+    if (memcmp(temp.s6_addr32, want.s6_addr32, sizeof(temp)) == 0)
+      return addrs->ifa_addr;
+  }
+
+  return NULL;
+}
+
+
+const struct sockaddr *find_ip_in_subnet(const struct ifaddrs *addrs,
+                                        const struct sockaddr *net,
+                                        unsigned int prefix_len) {
+  switch (net->sa_family) {
+    case AF_INET:
+      return find_ipv4_in_subnet(addrs, (struct sockaddr_in*)net, prefix_len);
+
+    case AF_INET6:
+      return find_ipv6_in_subnet(addrs, (struct sockaddr_in6*)net, prefix_len);
+    }
+
+  return NULL;
+}
diff --git a/src/include/ipaddr.h b/src/include/ipaddr.h
new file mode 100644 (file)
index 0000000..a255b38
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef CEPH_IPADDR_H
+#define CEPH_IPADDR_H
+
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <ifaddrs.h>
+
+/*
+  Find an IP address that is in the wanted subnet.
+
+  If there are multiple matches, the first one is returned; this order
+  is system-dependent and should not be relied on.
+ */
+const struct sockaddr *find_ip_in_subnet(const struct ifaddrs *addrs,
+                                        const struct sockaddr *net,
+                                        unsigned int prefix_len);
+
+#endif
diff --git a/src/test/test_ipaddr.cc b/src/test/test_ipaddr.cc
new file mode 100644 (file)
index 0000000..907e3a5
--- /dev/null
@@ -0,0 +1,208 @@
+#include "include/ipaddr.h"
+#include "gtest/gtest.h"
+
+#include <arpa/inet.h>
+
+static void ipv4(struct sockaddr_in *addr, const char *s) {
+  int err;
+
+  addr->sin_family = AF_INET;
+  err = inet_pton(AF_INET, s, &addr->sin_addr);
+  ASSERT_EQ(1, err);
+}
+
+static void ipv6(struct sockaddr_in6 *addr, const char *s) {
+  int err;
+
+  addr->sin6_family = AF_INET6;
+  err = inet_pton(AF_INET6, s, &addr->sin6_addr);
+  ASSERT_EQ(1, err);
+}
+
+TEST(CommonIPAddr, TestNotFound)
+{
+  struct ifaddrs one, two;
+  struct sockaddr_in a_one;
+  struct sockaddr_in6 a_two;
+  struct sockaddr_in net;
+  const struct sockaddr *result;
+
+  one.ifa_next = &two;
+  one.ifa_addr = (struct sockaddr*)&a_one;
+
+  two.ifa_next = NULL;
+  two.ifa_addr = (struct sockaddr*)&a_two;
+
+  ipv4(&a_one, "10.11.12.13");
+  ipv6(&a_two, "2001:1234:5678:90ab::cdef");
+  ipv4(&net, "10.11.234.56");
+
+  result = find_ip_in_subnet(&one, (struct sockaddr*)&net, 24);
+  ASSERT_EQ(0, result);
+}
+
+TEST(CommonIPAddr, TestV4_Simple)
+{
+  struct ifaddrs one, two;
+  struct sockaddr_in a_one;
+  struct sockaddr_in6 a_two;
+  struct sockaddr_in net;
+  const struct sockaddr *result;
+
+  one.ifa_next = &two;
+  one.ifa_addr = (struct sockaddr*)&a_one;
+
+  two.ifa_next = NULL;
+  two.ifa_addr = (struct sockaddr*)&a_two;
+
+  ipv4(&a_one, "10.11.12.13");
+  ipv6(&a_two, "2001:1234:5678:90ab::cdef");
+  ipv4(&net, "10.11.12.42");
+
+  result = find_ip_in_subnet(&one, (struct sockaddr*)&net, 24);
+  ASSERT_EQ((struct sockaddr*)&a_one, result);
+}
+
+TEST(CommonIPAddr, TestV4_Prefix25)
+{
+  struct ifaddrs one, two;
+  struct sockaddr_in a_one;
+  struct sockaddr_in a_two;
+  struct sockaddr_in net;
+  const struct sockaddr *result;
+
+  one.ifa_next = &two;
+  one.ifa_addr = (struct sockaddr*)&a_one;
+
+  two.ifa_next = NULL;
+  two.ifa_addr = (struct sockaddr*)&a_two;
+
+  ipv4(&a_one, "10.11.12.13");
+  ipv4(&a_two, "10.11.12.129");
+  ipv4(&net, "10.11.12.128");
+
+  result = find_ip_in_subnet(&one, (struct sockaddr*)&net, 25);
+  ASSERT_EQ((struct sockaddr*)&a_two, result);
+}
+
+TEST(CommonIPAddr, TestV4_PrefixTooLong)
+{
+  struct ifaddrs one;
+  struct sockaddr_in a_one;
+  struct sockaddr_in net;
+  const struct sockaddr *result;
+
+  one.ifa_next = NULL;
+  one.ifa_addr = (struct sockaddr*)&a_one;
+
+  ipv4(&a_one, "10.11.12.13");
+  ipv4(&net, "10.11.12.12");
+
+  result = find_ip_in_subnet(&one, (struct sockaddr*)&net, 42);
+  ASSERT_EQ(0, result);
+}
+
+TEST(CommonIPAddr, TestV4_PrefixZero)
+{
+  struct ifaddrs one, two;
+  struct sockaddr_in6 a_one;
+  struct sockaddr_in a_two;
+  struct sockaddr_in net;
+  const struct sockaddr *result;
+
+  one.ifa_next = &two;
+  one.ifa_addr = (struct sockaddr*)&a_one;
+
+  two.ifa_next = NULL;
+  two.ifa_addr = (struct sockaddr*)&a_two;
+
+  ipv6(&a_one, "2001:1234:5678:900F::cdef");
+  ipv4(&a_two, "10.1.2.3");
+  ipv4(&net, "255.0.1.2");
+
+  result = find_ip_in_subnet(&one, (struct sockaddr*)&net, 0);
+  ASSERT_EQ((struct sockaddr*)&a_two, result);
+}
+
+TEST(CommonIPAddr, TestV6_Simple)
+{
+  struct ifaddrs one, two;
+  struct sockaddr_in a_one;
+  struct sockaddr_in6 a_two;
+  struct sockaddr_in6 net;
+  const struct sockaddr *result;
+
+  one.ifa_next = &two;
+  one.ifa_addr = (struct sockaddr*)&a_one;
+
+  two.ifa_next = NULL;
+  two.ifa_addr = (struct sockaddr*)&a_two;
+
+  ipv4(&a_one, "10.11.12.13");
+  ipv6(&a_two, "2001:1234:5678:90ab::cdef");
+  ipv6(&net, "2001:1234:5678:90ab::dead:beef");
+
+  result = find_ip_in_subnet(&one, (struct sockaddr*)&net, 64);
+  ASSERT_EQ((struct sockaddr*)&a_two, result);
+}
+
+TEST(CommonIPAddr, TestV6_Prefix57)
+{
+  struct ifaddrs one, two;
+  struct sockaddr_in6 a_one;
+  struct sockaddr_in6 a_two;
+  struct sockaddr_in6 net;
+  const struct sockaddr *result;
+
+  one.ifa_next = &two;
+  one.ifa_addr = (struct sockaddr*)&a_one;
+
+  two.ifa_next = NULL;
+  two.ifa_addr = (struct sockaddr*)&a_two;
+
+  ipv6(&a_one, "2001:1234:5678:900F::cdef");
+  ipv6(&a_two, "2001:1234:5678:90ab::cdef");
+  ipv6(&net, "2001:1234:5678:90ab::dead:beef");
+
+  result = find_ip_in_subnet(&one, (struct sockaddr*)&net, 57);
+  ASSERT_EQ((struct sockaddr*)&a_two, result);
+}
+
+TEST(CommonIPAddr, TestV6_PrefixTooLong)
+{
+  struct ifaddrs one;
+  struct sockaddr_in6 a_one;
+  struct sockaddr_in6 net;
+  const struct sockaddr *result;
+
+  one.ifa_next = NULL;
+  one.ifa_addr = (struct sockaddr*)&a_one;
+
+  ipv6(&a_one, "2001:1234:5678:900F::cdef");
+  ipv6(&net, "2001:1234:5678:900F::cdee");
+
+  result = find_ip_in_subnet(&one, (struct sockaddr*)&net, 9000);
+  ASSERT_EQ(0, result);
+}
+
+TEST(CommonIPAddr, TestV6_PrefixZero)
+{
+  struct ifaddrs one, two;
+  struct sockaddr_in a_one;
+  struct sockaddr_in6 a_two;
+  struct sockaddr_in6 net;
+  const struct sockaddr *result;
+
+  one.ifa_next = &two;
+  one.ifa_addr = (struct sockaddr*)&a_one;
+
+  two.ifa_next = NULL;
+  two.ifa_addr = (struct sockaddr*)&a_two;
+
+  ipv4(&a_one, "10.2.3.4");
+  ipv6(&a_two, "2001:f00b::1");
+  ipv6(&net, "ff00::1");
+
+  result = find_ip_in_subnet(&one, (struct sockaddr*)&net, 0);
+  ASSERT_EQ((struct sockaddr*)&a_two, result);
+}