s4-lib/socket Samba4 is not IPv6 compatible
[samba.git] / lib / socket / interfaces.c
1 /*
2    Unix SMB/CIFS implementation.
3    return a list of network interfaces
4    Copyright (C) Andrew Tridgell 1998
5    Copyright (C) Jeremy Allison 2007
6    Copyright (C) Jelmer Vernooij 2007
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22
23 /* working out the interfaces for a OS is an incredibly non-portable
24    thing. We have several possible implementations below, and autoconf
25    tries each of them to see what works
26
27    Note that this file does _not_ include includes.h. That is so this code
28    can be called directly from the autoconf tests. That also means
29    this code cannot use any of the normal Samba debug stuff or defines.
30    This is standalone code.
31
32 */
33
34 #include "includes.h"
35 #include "system/network.h"
36 #include "interfaces.h"
37 #include "lib/util/tsort.h"
38
39 /****************************************************************************
40  Create a struct sockaddr_storage with the netmask bits set to 1.
41 ****************************************************************************/
42
43 bool make_netmask(struct sockaddr_storage *pss_out,
44                         const struct sockaddr_storage *pss_in,
45                         unsigned long masklen)
46 {
47         *pss_out = *pss_in;
48         /* Now apply masklen bits of mask. */
49 #if defined(HAVE_IPV6)
50         if (pss_in->ss_family == AF_INET6) {
51                 char *p = (char *)&((struct sockaddr_in6 *)pss_out)->sin6_addr;
52                 unsigned int i;
53
54                 if (masklen > 128) {
55                         return false;
56                 }
57                 for (i = 0; masklen >= 8; masklen -= 8, i++) {
58                         *p++ = 0xff;
59                 }
60                 /* Deal with the partial byte. */
61                 *p++ &= (0xff & ~(0xff>>masklen));
62                 i++;
63                 for (;i < sizeof(struct in6_addr); i++) {
64                         *p++ = '\0';
65                 }
66                 return true;
67         }
68 #endif
69         if (pss_in->ss_family == AF_INET) {
70                 if (masklen > 32) {
71                         return false;
72                 }
73                 ((struct sockaddr_in *)pss_out)->sin_addr.s_addr =
74                         htonl(((0xFFFFFFFFL >> masklen) ^ 0xFFFFFFFFL));
75                 return true;
76         }
77         return false;
78 }
79
80 /****************************************************************************
81  Create a struct sockaddr_storage set to the broadcast or network adress from
82  an incoming sockaddr_storage.
83 ****************************************************************************/
84
85 static void make_bcast_or_net(struct sockaddr_storage *pss_out,
86                         const struct sockaddr_storage *pss_in,
87                         const struct sockaddr_storage *nmask,
88                         bool make_bcast_p)
89 {
90         unsigned int i = 0, len = 0;
91         char *pmask = NULL;
92         char *p = NULL;
93         *pss_out = *pss_in;
94
95         /* Set all zero netmask bits to 1. */
96 #if defined(HAVE_IPV6)
97         if (pss_in->ss_family == AF_INET6) {
98                 p = (char *)&((struct sockaddr_in6 *)pss_out)->sin6_addr;
99                 pmask = discard_const_p(char, &((struct sockaddr_in6 *)nmask)->sin6_addr);
100                 len = 16;
101         }
102 #endif
103         if (pss_in->ss_family == AF_INET) {
104                 p = (char *)&((struct sockaddr_in *)pss_out)->sin_addr;
105                 pmask = discard_const_p(char, &((struct sockaddr_in *)nmask)->sin_addr);
106                 len = 4;
107         }
108
109         for (i = 0; i < len; i++, p++, pmask++) {
110                 if (make_bcast_p) {
111                         *p = (*p & *pmask) | (*pmask ^ 0xff);
112                 } else {
113                         /* make_net */
114                         *p = (*p & *pmask);
115                 }
116         }
117 }
118
119 void make_bcast(struct sockaddr_storage *pss_out,
120                         const struct sockaddr_storage *pss_in,
121                         const struct sockaddr_storage *nmask)
122 {
123         make_bcast_or_net(pss_out, pss_in, nmask, true);
124 }
125
126 void make_net(struct sockaddr_storage *pss_out,
127                         const struct sockaddr_storage *pss_in,
128                         const struct sockaddr_storage *nmask)
129 {
130         make_bcast_or_net(pss_out, pss_in, nmask, false);
131 }
132
133
134 /****************************************************************************
135  Try the "standard" getifaddrs/freeifaddrs interfaces.
136  Also gets IPv6 interfaces.
137 ****************************************************************************/
138
139 /****************************************************************************
140  Get the netmask address for a local interface.
141 ****************************************************************************/
142
143 static int _get_interfaces(TALLOC_CTX *mem_ctx, struct iface_struct **pifaces)
144 {
145         struct iface_struct *ifaces;
146         struct ifaddrs *iflist = NULL;
147         struct ifaddrs *ifptr = NULL;
148         int count;
149         int total = 0;
150         size_t copy_size;
151
152         if (getifaddrs(&iflist) < 0) {
153                 return -1;
154         }
155
156         count = 0;
157         for (ifptr = iflist; ifptr != NULL; ifptr = ifptr->ifa_next) {
158                 if (!ifptr->ifa_addr || !ifptr->ifa_netmask) {
159                         continue;
160                 }
161                 if (!(ifptr->ifa_flags & IFF_UP)) {
162                         continue;
163                 }
164                 count += 1;
165         }
166
167         ifaces = talloc_array(mem_ctx, struct iface_struct, count);
168         if (ifaces == NULL) {
169                 errno = ENOMEM;
170                 return -1;
171         }
172
173         /* Loop through interfaces, looking for given IP address */
174         for (ifptr = iflist; ifptr != NULL; ifptr = ifptr->ifa_next) {
175
176                 if (!ifptr->ifa_addr || !ifptr->ifa_netmask) {
177                         continue;
178                 }
179
180                 /* Check the interface is up. */
181                 if (!(ifptr->ifa_flags & IFF_UP)) {
182                         continue;
183                 }
184
185                 memset(&ifaces[total], '\0', sizeof(ifaces[total]));
186
187                 copy_size = sizeof(struct sockaddr_in);
188
189                 ifaces[total].flags = ifptr->ifa_flags;
190
191 #if defined(HAVE_IPV6)
192                 if (ifptr->ifa_addr->sa_family == AF_INET6) {
193                         copy_size = sizeof(struct sockaddr_in6);
194                 }
195 #endif
196
197                 memcpy(&ifaces[total].ip, ifptr->ifa_addr, copy_size);
198                 memcpy(&ifaces[total].netmask, ifptr->ifa_netmask, copy_size);
199
200                 if (ifaces[total].flags & (IFF_BROADCAST|IFF_LOOPBACK)) {
201                         make_bcast(&ifaces[total].bcast,
202                                 &ifaces[total].ip,
203                                 &ifaces[total].netmask);
204                 } else if ((ifaces[total].flags & IFF_POINTOPOINT) &&
205                                ifptr->ifa_dstaddr ) {
206                         memcpy(&ifaces[total].bcast,
207                                 ifptr->ifa_dstaddr,
208                                 copy_size);
209                 } else {
210                         continue;
211                 }
212
213                 strlcpy(ifaces[total].name, ifptr->ifa_name,
214                         sizeof(ifaces[total].name));
215                 total++;
216         }
217
218         freeifaddrs(iflist);
219
220         *pifaces = ifaces;
221         return total;
222 }
223
224 static int iface_comp(struct iface_struct *i1, struct iface_struct *i2)
225 {
226         int r;
227
228 #if defined(HAVE_IPV6)
229         /*
230          * If we have IPv6 - sort these interfaces lower
231          * than any IPv4 ones.
232          */
233         if (i1->ip.ss_family == AF_INET6 &&
234                         i2->ip.ss_family == AF_INET) {
235                 return -1;
236         } else if (i1->ip.ss_family == AF_INET &&
237                         i2->ip.ss_family == AF_INET6) {
238                 return 1;
239         }
240
241         if (i1->ip.ss_family == AF_INET6) {
242                 struct sockaddr_in6 *s1 = (struct sockaddr_in6 *)&i1->ip;
243                 struct sockaddr_in6 *s2 = (struct sockaddr_in6 *)&i2->ip;
244
245                 r = memcmp(&s1->sin6_addr,
246                                 &s2->sin6_addr,
247                                 sizeof(struct in6_addr));
248                 if (r) {
249                         return r;
250                 }
251
252                 s1 = (struct sockaddr_in6 *)&i1->netmask;
253                 s2 = (struct sockaddr_in6 *)&i2->netmask;
254
255                 r = memcmp(&s1->sin6_addr,
256                                 &s2->sin6_addr,
257                                 sizeof(struct in6_addr));
258                 if (r) {
259                         return r;
260                 }
261         }
262 #endif
263
264         /* AIX uses __ss_family instead of ss_family inside of
265            sockaddr_storage. Instead of trying to figure out which field to
266            use, we can just cast it to a sockaddr.
267          */
268
269         if (((struct sockaddr *)&i1->ip)->sa_family == AF_INET) {
270                 struct sockaddr_in *s1 = (struct sockaddr_in *)&i1->ip;
271                 struct sockaddr_in *s2 = (struct sockaddr_in *)&i2->ip;
272
273                 r = ntohl(s1->sin_addr.s_addr) -
274                         ntohl(s2->sin_addr.s_addr);
275                 if (r) {
276                         return r;
277                 }
278
279                 s1 = (struct sockaddr_in *)&i1->netmask;
280                 s2 = (struct sockaddr_in *)&i2->netmask;
281
282                 return ntohl(s1->sin_addr.s_addr) -
283                         ntohl(s2->sin_addr.s_addr);
284         }
285         return 0;
286 }
287
288 /* this wrapper is used to remove duplicates from the interface list generated
289    above */
290 int get_interfaces(TALLOC_CTX *mem_ctx, struct iface_struct **pifaces)
291 {
292         struct iface_struct *ifaces;
293         int total, i, j;
294
295         total = _get_interfaces(mem_ctx, &ifaces);
296         if (total <= 0) return total;
297
298         /* now we need to remove duplicates */
299         TYPESAFE_QSORT(ifaces, total, iface_comp);
300
301         for (i=1;i<total;) {
302                 if (iface_comp(&ifaces[i-1], &ifaces[i]) == 0) {
303                         for (j=i-1;j<total-1;j++) {
304                                 ifaces[j] = ifaces[j+1];
305                         }
306                         total--;
307                 } else {
308                         i++;
309                 }
310         }
311
312         *pifaces = ifaces;
313         return total;
314 }