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