lib:socket: set defaults for linkspeed and capability in get_interfaces()
[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 #include "includes.h"
24 #include "system/network.h"
25 #include "interfaces.h"
26 #include "lib/util/tsort.h"
27 #include "librpc/gen_ndr/ioctl.h"
28
29 /****************************************************************************
30  Create a struct sockaddr_storage with the netmask bits set to 1.
31 ****************************************************************************/
32
33 bool make_netmask(struct sockaddr_storage *pss_out,
34                         const struct sockaddr_storage *pss_in,
35                         unsigned long masklen)
36 {
37         *pss_out = *pss_in;
38         /* Now apply masklen bits of mask. */
39 #if defined(HAVE_IPV6)
40         if (pss_in->ss_family == AF_INET6) {
41                 char *p = (char *)&((struct sockaddr_in6 *)pss_out)->sin6_addr;
42                 unsigned int i;
43
44                 if (masklen > 128) {
45                         return false;
46                 }
47                 for (i = 0; masklen >= 8; masklen -= 8, i++) {
48                         *p++ = 0xff;
49                 }
50                 /* Deal with the partial byte. */
51                 *p++ &= (0xff & ~(0xff>>masklen));
52                 i++;
53                 for (;i < sizeof(struct in6_addr); i++) {
54                         *p++ = '\0';
55                 }
56                 return true;
57         }
58 #endif
59         if (pss_in->ss_family == AF_INET) {
60                 if (masklen > 32) {
61                         return false;
62                 }
63                 ((struct sockaddr_in *)pss_out)->sin_addr.s_addr =
64                         htonl(((0xFFFFFFFFL >> masklen) ^ 0xFFFFFFFFL));
65                 return true;
66         }
67         return false;
68 }
69
70 /****************************************************************************
71  Create a struct sockaddr_storage set to the broadcast or network adress from
72  an incoming sockaddr_storage.
73 ****************************************************************************/
74
75 static void make_bcast_or_net(struct sockaddr_storage *pss_out,
76                         const struct sockaddr_storage *pss_in,
77                         const struct sockaddr_storage *nmask,
78                         bool make_bcast_p)
79 {
80         unsigned int i = 0, len = 0;
81         const char *pmask = NULL;
82         char *p = NULL;
83         *pss_out = *pss_in;
84
85         /* Set all zero netmask bits to 1. */
86 #if defined(HAVE_IPV6)
87         if (pss_in->ss_family == AF_INET6) {
88                 p = (char *)&((struct sockaddr_in6 *)pss_out)->sin6_addr;
89                 pmask = (const char *)&((const struct sockaddr_in6 *)nmask)->sin6_addr;
90                 len = 16;
91         }
92 #endif
93         if (pss_in->ss_family == AF_INET) {
94                 p = (char *)&((struct sockaddr_in *)pss_out)->sin_addr;
95                 pmask = (const char *)&((const struct sockaddr_in *)nmask)->sin_addr;
96                 len = 4;
97         }
98
99         for (i = 0; i < len; i++, p++, pmask++) {
100                 if (make_bcast_p) {
101                         *p = (*p & *pmask) | (*pmask ^ 0xff);
102                 } else {
103                         /* make_net */
104                         *p = (*p & *pmask);
105                 }
106         }
107 }
108
109 void make_bcast(struct sockaddr_storage *pss_out,
110                         const struct sockaddr_storage *pss_in,
111                         const struct sockaddr_storage *nmask)
112 {
113         make_bcast_or_net(pss_out, pss_in, nmask, true);
114 }
115
116 void make_net(struct sockaddr_storage *pss_out,
117                         const struct sockaddr_storage *pss_in,
118                         const struct sockaddr_storage *nmask)
119 {
120         make_bcast_or_net(pss_out, pss_in, nmask, false);
121 }
122
123
124 /****************************************************************************
125  Try the "standard" getifaddrs/freeifaddrs interfaces.
126  Also gets IPv6 interfaces.
127 ****************************************************************************/
128
129 /****************************************************************************
130  Get the netmask address for a local interface.
131 ****************************************************************************/
132
133 static int _get_interfaces(TALLOC_CTX *mem_ctx, struct iface_struct **pifaces)
134 {
135         struct iface_struct *ifaces;
136         struct ifaddrs *iflist = NULL;
137         struct ifaddrs *ifptr = NULL;
138         int count;
139         int total = 0;
140         size_t copy_size;
141         uint64_t if_speed = 1000 * 1000 * 1000; /* 1GBit */
142
143         if (getifaddrs(&iflist) < 0) {
144                 return -1;
145         }
146
147         count = 0;
148         for (ifptr = iflist; ifptr != NULL; ifptr = ifptr->ifa_next) {
149                 if (!ifptr->ifa_addr || !ifptr->ifa_netmask) {
150                         continue;
151                 }
152                 if (!(ifptr->ifa_flags & IFF_UP)) {
153                         continue;
154                 }
155                 count += 1;
156         }
157
158         ifaces = talloc_array(mem_ctx, struct iface_struct, count);
159         if (ifaces == NULL) {
160                 errno = ENOMEM;
161                 return -1;
162         }
163
164         /* Loop through interfaces, looking for given IP address */
165         for (ifptr = iflist; ifptr != NULL; ifptr = ifptr->ifa_next) {
166
167                 if (!ifptr->ifa_addr || !ifptr->ifa_netmask) {
168                         continue;
169                 }
170
171                 /* Check the interface is up. */
172                 if (!(ifptr->ifa_flags & IFF_UP)) {
173                         continue;
174                 }
175
176                 memset(&ifaces[total], '\0', sizeof(ifaces[total]));
177
178                 copy_size = sizeof(struct sockaddr_in);
179
180                 ifaces[total].flags = ifptr->ifa_flags;
181
182 #if defined(HAVE_IPV6)
183                 if (ifptr->ifa_addr->sa_family == AF_INET6) {
184                         copy_size = sizeof(struct sockaddr_in6);
185                 }
186 #endif
187
188                 memcpy(&ifaces[total].ip, ifptr->ifa_addr, copy_size);
189                 memcpy(&ifaces[total].netmask, ifptr->ifa_netmask, copy_size);
190
191                 /* calculate broadcast address */
192 #if defined(HAVE_IPV6)
193                 if (ifptr->ifa_addr->sa_family == AF_INET6) {
194                         struct sockaddr_in6 *sin6 =
195                                 (struct sockaddr_in6 *)ifptr->ifa_addr;
196                         struct in6_addr *in6 =
197                                 (struct in6_addr *)&sin6->sin6_addr;
198
199                         if (IN6_IS_ADDR_LINKLOCAL(in6) || IN6_IS_ADDR_V4COMPAT(in6)) {
200                                 continue;
201                         }
202                         /* IPv6 does not have broadcast it uses multicast. */
203                         memset(&ifaces[total].bcast, '\0', copy_size);
204                 } else
205 #endif
206                 if (ifaces[total].flags & (IFF_BROADCAST|IFF_LOOPBACK)) {
207                         make_bcast(&ifaces[total].bcast,
208                                 &ifaces[total].ip,
209                                 &ifaces[total].netmask);
210                 } else if ((ifaces[total].flags & IFF_POINTOPOINT) &&
211                                ifptr->ifa_dstaddr ) {
212                         memcpy(&ifaces[total].bcast,
213                                 ifptr->ifa_dstaddr,
214                                 copy_size);
215                 } else {
216                         continue;
217                 }
218
219                 ifaces[total].if_index = if_nametoindex(ifptr->ifa_name);
220                 if (ifaces[total].if_index == 0) {
221                         DBG_ERR("Failed to retrieve interface index for '%s': "
222                                 "%s\n", ifptr->ifa_name, strerror(errno));
223                 }
224
225                 ifaces[total].linkspeed = if_speed;
226                 ifaces[total].capability = FSCTL_NET_IFACE_NONE_CAPABLE;
227
228                 if (strlcpy(ifaces[total].name, ifptr->ifa_name,
229                         sizeof(ifaces[total].name)) >=
230                                 sizeof(ifaces[total].name)) {
231                         /* Truncation ! Ignore. */
232                         continue;
233                 }
234                 total++;
235         }
236
237         freeifaddrs(iflist);
238
239         *pifaces = ifaces;
240         return total;
241 }
242
243 static int iface_comp(struct iface_struct *i1, struct iface_struct *i2)
244 {
245         int r;
246
247 #if defined(HAVE_IPV6)
248         /*
249          * If we have IPv6 - sort these interfaces lower
250          * than any IPv4 ones.
251          */
252         if (i1->ip.ss_family == AF_INET6 &&
253                         i2->ip.ss_family == AF_INET) {
254                 return -1;
255         } else if (i1->ip.ss_family == AF_INET &&
256                         i2->ip.ss_family == AF_INET6) {
257                 return 1;
258         }
259
260         if (i1->ip.ss_family == AF_INET6) {
261                 struct sockaddr_in6 *s1 = (struct sockaddr_in6 *)&i1->ip;
262                 struct sockaddr_in6 *s2 = (struct sockaddr_in6 *)&i2->ip;
263
264                 r = memcmp(&s1->sin6_addr,
265                                 &s2->sin6_addr,
266                                 sizeof(struct in6_addr));
267                 if (r) {
268                         return r;
269                 }
270
271                 s1 = (struct sockaddr_in6 *)&i1->netmask;
272                 s2 = (struct sockaddr_in6 *)&i2->netmask;
273
274                 r = memcmp(&s1->sin6_addr,
275                                 &s2->sin6_addr,
276                                 sizeof(struct in6_addr));
277                 if (r) {
278                         return r;
279                 }
280         }
281 #endif
282
283         /* AIX uses __ss_family instead of ss_family inside of
284            sockaddr_storage. Instead of trying to figure out which field to
285            use, we can just cast it to a sockaddr.
286          */
287
288         if (((struct sockaddr *)&i1->ip)->sa_family == AF_INET) {
289                 struct sockaddr_in *s1 = (struct sockaddr_in *)&i1->ip;
290                 struct sockaddr_in *s2 = (struct sockaddr_in *)&i2->ip;
291
292                 r = ntohl(s1->sin_addr.s_addr) -
293                         ntohl(s2->sin_addr.s_addr);
294                 if (r) {
295                         return r;
296                 }
297
298                 s1 = (struct sockaddr_in *)&i1->netmask;
299                 s2 = (struct sockaddr_in *)&i2->netmask;
300
301                 return ntohl(s1->sin_addr.s_addr) -
302                         ntohl(s2->sin_addr.s_addr);
303         }
304         return 0;
305 }
306
307 /* this wrapper is used to remove duplicates from the interface list generated
308    above */
309 int get_interfaces(TALLOC_CTX *mem_ctx, struct iface_struct **pifaces)
310 {
311         struct iface_struct *ifaces;
312         int total, i, j;
313
314         total = _get_interfaces(mem_ctx, &ifaces);
315         if (total <= 0) return total;
316
317         /* now we need to remove duplicates */
318         TYPESAFE_QSORT(ifaces, total, iface_comp);
319
320         for (i=1;i<total;) {
321                 if (iface_comp(&ifaces[i-1], &ifaces[i]) == 0) {
322                         for (j=i-1;j<total-1;j++) {
323                                 ifaces[j] = ifaces[j+1];
324                         }
325                         total--;
326                 } else {
327                         i++;
328                 }
329         }
330
331         *pifaces = ifaces;
332         return total;
333 }