Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / common / ipaddr.cc
1
2 #include <arpa/inet.h>
3 #include <ifaddrs.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #if defined(__FreeBSD__)
7 #include <sys/types.h>
8 #include <sys/socket.h>
9 #include <netinet/in.h>
10 #endif
11
12 #include "include/ipaddr.h"
13
14 static void netmask_ipv4(const struct in_addr *addr,
15                          unsigned int prefix_len,
16                          struct in_addr *out) {
17   uint32_t mask;
18
19   if (prefix_len >= 32) {
20     // also handle 32 in this branch, because >>32 is not defined by
21     // the C standards
22     mask = ~uint32_t(0);
23   } else {
24     mask = htonl(~(~uint32_t(0) >> prefix_len));
25   }
26   out->s_addr = addr->s_addr & mask;
27 }
28
29
30 const struct ifaddrs *find_ipv4_in_subnet(const struct ifaddrs *addrs,
31                                            const struct sockaddr_in *net,
32                                            unsigned int prefix_len) {
33   struct in_addr want, temp;
34
35   netmask_ipv4(&net->sin_addr, prefix_len, &want);
36
37   for (; addrs != NULL; addrs = addrs->ifa_next) {
38
39     if (addrs->ifa_addr == NULL)
40       continue;
41
42     if (strcmp(addrs->ifa_name, "lo") == 0)
43       continue;
44
45     if (addrs->ifa_addr->sa_family != net->sin_family)
46       continue;
47
48     struct in_addr *cur = &((struct sockaddr_in*)addrs->ifa_addr)->sin_addr;
49     netmask_ipv4(cur, prefix_len, &temp);
50
51     if (temp.s_addr == want.s_addr) {
52       return addrs;
53     }
54   }
55
56   return NULL;
57 }
58
59
60 static void netmask_ipv6(const struct in6_addr *addr,
61                          unsigned int prefix_len,
62                          struct in6_addr *out) {
63   if (prefix_len > 128)
64     prefix_len = 128;
65
66   memcpy(out->s6_addr, addr->s6_addr, prefix_len/8);
67   if (prefix_len < 128)
68     out->s6_addr[prefix_len/8] = addr->s6_addr[prefix_len/8] & ~( 0xFF >> (prefix_len % 8) );
69   if (prefix_len/8 < 15)
70     memset(out->s6_addr+prefix_len/8+1, 0, 16-prefix_len/8-1);
71 }
72
73
74 const struct ifaddrs *find_ipv6_in_subnet(const struct ifaddrs *addrs,
75                                            const struct sockaddr_in6 *net,
76                                            unsigned int prefix_len) {
77   struct in6_addr want, temp;
78
79   netmask_ipv6(&net->sin6_addr, prefix_len, &want);
80
81   for (; addrs != NULL; addrs = addrs->ifa_next) {
82
83     if (addrs->ifa_addr == NULL)
84       continue;
85
86     if (strcmp(addrs->ifa_name, "lo") == 0)
87       continue;
88
89     if (addrs->ifa_addr->sa_family != net->sin6_family)
90       continue;
91
92     struct in6_addr *cur = &((struct sockaddr_in6*)addrs->ifa_addr)->sin6_addr;
93     netmask_ipv6(cur, prefix_len, &temp);
94
95     if (IN6_ARE_ADDR_EQUAL(&temp, &want))
96       return addrs;
97   }
98
99   return NULL;
100 }
101
102
103 const struct ifaddrs *find_ip_in_subnet(const struct ifaddrs *addrs,
104                                          const struct sockaddr *net,
105                                          unsigned int prefix_len) {
106   switch (net->sa_family) {
107     case AF_INET:
108       return find_ipv4_in_subnet(addrs, (struct sockaddr_in*)net, prefix_len);
109
110     case AF_INET6:
111       return find_ipv6_in_subnet(addrs, (struct sockaddr_in6*)net, prefix_len);
112     }
113
114   return NULL;
115 }
116
117
118 bool parse_network(const char *s, struct sockaddr_storage *network, unsigned int *prefix_len) {
119   char *slash = strchr((char*)s, '/');
120   if (!slash) {
121     // no slash
122     return false;
123   }
124   if (*(slash+1) == '\0') {
125     // slash is the last character
126     return false;
127   }
128
129   char *end;
130   long int num = strtol(slash+1, &end, 10);
131   if (*end != '\0') {
132     // junk after the prefix_len
133     return false;
134   }
135   if (num < 0) {
136     return false;
137   }
138   *prefix_len = num;
139
140   // copy the part before slash to get nil termination
141   char *addr = (char*)alloca(slash-s + 1);
142   strncpy(addr, s, slash-s);
143   addr[slash-s] = '\0';
144
145   // caller expects ports etc to be zero
146   memset(network, 0, sizeof(*network));
147
148   // try parsing as ipv4
149   int ok;
150   ok = inet_pton(AF_INET, addr, &((struct sockaddr_in*)network)->sin_addr);
151   if (ok) {
152     network->ss_family = AF_INET;
153     return true;
154   }
155
156   // try parsing as ipv6
157   ok = inet_pton(AF_INET6, addr, &((struct sockaddr_in6*)network)->sin6_addr);
158   if (ok) {
159     network->ss_family = AF_INET6;
160     return true;
161   }
162
163   return false;
164 }