lib/socket: let query_iface_speed_from_name() initialize memory passed to syscalls
[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         ZERO_STRUCT(edata);
153         edata.cmd = ETHTOOL_GLINK;
154         ret = ioctl(fd, SIOCETHTOOL, &ifr);
155         if (ret == -1) {
156                 goto done;
157         }
158         if (edata.data == 0) {
159                 /* no link detected */
160                 *speed = 0;
161                 goto done;
162         }
163
164         ifr.ifr_data = (void *)&ecmd;
165         ZERO_STRUCT(ecmd);
166         ecmd.cmd = ETHTOOL_GSET;
167         ret = ioctl(fd, SIOCETHTOOL, &ifr);
168         if (ret == -1) {
169                 goto done;
170         }
171         *speed = ((uint64_t)ethtool_cmd_speed(&ecmd)) * 1000 * 1000;
172
173 done:
174         (void)close(fd);
175 }
176 #endif
177
178 /****************************************************************************
179  Try the "standard" getifaddrs/freeifaddrs interfaces.
180  Also gets IPv6 interfaces.
181 ****************************************************************************/
182
183 /****************************************************************************
184  Get the netmask address for a local interface.
185 ****************************************************************************/
186
187 static int _get_interfaces(TALLOC_CTX *mem_ctx, struct iface_struct **pifaces)
188 {
189         struct iface_struct *ifaces;
190         struct ifaddrs *iflist = NULL;
191         struct ifaddrs *ifptr = NULL;
192         int count;
193         int total = 0;
194         size_t copy_size;
195
196         if (getifaddrs(&iflist) < 0) {
197                 return -1;
198         }
199
200         count = 0;
201         for (ifptr = iflist; ifptr != NULL; ifptr = ifptr->ifa_next) {
202                 if (!ifptr->ifa_addr || !ifptr->ifa_netmask) {
203                         continue;
204                 }
205                 if (!(ifptr->ifa_flags & IFF_UP)) {
206                         continue;
207                 }
208                 count += 1;
209         }
210
211         ifaces = talloc_array(mem_ctx, struct iface_struct, count);
212         if (ifaces == NULL) {
213                 errno = ENOMEM;
214                 return -1;
215         }
216
217         /* Loop through interfaces, looking for given IP address */
218         for (ifptr = iflist; ifptr != NULL; ifptr = ifptr->ifa_next) {
219                 uint64_t if_speed = 1000 * 1000 * 1000; /* 1Gbps */
220
221                 if (!ifptr->ifa_addr || !ifptr->ifa_netmask) {
222                         continue;
223                 }
224
225                 /* Check the interface is up. */
226                 if (!(ifptr->ifa_flags & IFF_UP)) {
227                         continue;
228                 }
229
230                 memset(&ifaces[total], '\0', sizeof(ifaces[total]));
231
232                 copy_size = sizeof(struct sockaddr_in);
233
234                 ifaces[total].flags = ifptr->ifa_flags;
235
236 #if defined(HAVE_IPV6)
237                 if (ifptr->ifa_addr->sa_family == AF_INET6) {
238                         copy_size = sizeof(struct sockaddr_in6);
239                 }
240 #endif
241
242                 memcpy(&ifaces[total].ip, ifptr->ifa_addr, copy_size);
243                 memcpy(&ifaces[total].netmask, ifptr->ifa_netmask, copy_size);
244
245                 /* calculate broadcast address */
246 #if defined(HAVE_IPV6)
247                 if (ifptr->ifa_addr->sa_family == AF_INET6) {
248                         struct sockaddr_in6 *sin6 =
249                                 (struct sockaddr_in6 *)ifptr->ifa_addr;
250                         struct in6_addr *in6 =
251                                 (struct in6_addr *)&sin6->sin6_addr;
252
253                         if (IN6_IS_ADDR_LINKLOCAL(in6) || IN6_IS_ADDR_V4COMPAT(in6)) {
254                                 continue;
255                         }
256                         /* IPv6 does not have broadcast it uses multicast. */
257                         memset(&ifaces[total].bcast, '\0', copy_size);
258                 } else
259 #endif
260                 if (ifaces[total].flags & (IFF_BROADCAST|IFF_LOOPBACK)) {
261                         make_bcast(&ifaces[total].bcast,
262                                 &ifaces[total].ip,
263                                 &ifaces[total].netmask);
264                 } else if ((ifaces[total].flags & IFF_POINTOPOINT) &&
265                                ifptr->ifa_dstaddr ) {
266                         memcpy(&ifaces[total].bcast,
267                                 ifptr->ifa_dstaddr,
268                                 copy_size);
269                 } else {
270                         continue;
271                 }
272
273                 ifaces[total].if_index = if_nametoindex(ifptr->ifa_name);
274                 if (ifaces[total].if_index == 0) {
275                         DBG_ERR("Failed to retrieve interface index for '%s': "
276                                 "%s\n", ifptr->ifa_name, strerror(errno));
277                 }
278
279 #ifdef HAVE_ETHTOOL
280                 query_iface_speed_from_name(ifptr->ifa_name, &if_speed);
281 #endif
282                 ifaces[total].linkspeed = if_speed;
283                 ifaces[total].capability = FSCTL_NET_IFACE_NONE_CAPABLE;
284
285                 if (strlcpy(ifaces[total].name, ifptr->ifa_name,
286                         sizeof(ifaces[total].name)) >=
287                                 sizeof(ifaces[total].name)) {
288                         /* Truncation ! Ignore. */
289                         continue;
290                 }
291                 total++;
292         }
293
294         freeifaddrs(iflist);
295
296         *pifaces = ifaces;
297         return total;
298 }
299
300 static int iface_comp(struct iface_struct *i1, struct iface_struct *i2)
301 {
302         int r;
303
304 #if defined(HAVE_IPV6)
305         /*
306          * If we have IPv6 - sort these interfaces lower
307          * than any IPv4 ones.
308          */
309         if (i1->ip.ss_family == AF_INET6 &&
310                         i2->ip.ss_family == AF_INET) {
311                 return -1;
312         } else if (i1->ip.ss_family == AF_INET &&
313                         i2->ip.ss_family == AF_INET6) {
314                 return 1;
315         }
316
317         if (i1->ip.ss_family == AF_INET6) {
318                 struct sockaddr_in6 *s1 = (struct sockaddr_in6 *)&i1->ip;
319                 struct sockaddr_in6 *s2 = (struct sockaddr_in6 *)&i2->ip;
320
321                 r = memcmp(&s1->sin6_addr,
322                                 &s2->sin6_addr,
323                                 sizeof(struct in6_addr));
324                 if (r) {
325                         return r;
326                 }
327
328                 s1 = (struct sockaddr_in6 *)&i1->netmask;
329                 s2 = (struct sockaddr_in6 *)&i2->netmask;
330
331                 r = memcmp(&s1->sin6_addr,
332                                 &s2->sin6_addr,
333                                 sizeof(struct in6_addr));
334                 if (r) {
335                         return r;
336                 }
337         }
338 #endif
339
340         /* AIX uses __ss_family instead of ss_family inside of
341            sockaddr_storage. Instead of trying to figure out which field to
342            use, we can just cast it to a sockaddr.
343          */
344
345         if (((struct sockaddr *)&i1->ip)->sa_family == AF_INET) {
346                 struct sockaddr_in *s1 = (struct sockaddr_in *)&i1->ip;
347                 struct sockaddr_in *s2 = (struct sockaddr_in *)&i2->ip;
348
349                 r = ntohl(s1->sin_addr.s_addr) -
350                         ntohl(s2->sin_addr.s_addr);
351                 if (r) {
352                         return r;
353                 }
354
355                 s1 = (struct sockaddr_in *)&i1->netmask;
356                 s2 = (struct sockaddr_in *)&i2->netmask;
357
358                 return ntohl(s1->sin_addr.s_addr) -
359                         ntohl(s2->sin_addr.s_addr);
360         }
361         return 0;
362 }
363
364 /* this wrapper is used to remove duplicates from the interface list generated
365    above */
366 int get_interfaces(TALLOC_CTX *mem_ctx, struct iface_struct **pifaces)
367 {
368         struct iface_struct *ifaces = NULL;
369         int total, i, j;
370
371         total = _get_interfaces(mem_ctx, &ifaces);
372         /* If we have an error, no interface or just one we can leave */
373         if (total <= 1) {
374                 *pifaces = ifaces;
375                 return total;
376         }
377
378         /* now we need to remove duplicates */
379         TYPESAFE_QSORT(ifaces, total, iface_comp);
380
381         for (i=1;i<total;) {
382                 if (iface_comp(&ifaces[i-1], &ifaces[i]) == 0) {
383                         for (j=i-1;j<total-1;j++) {
384                                 ifaces[j] = ifaces[j+1];
385                         }
386                         total--;
387                 } else {
388                         i++;
389                 }
390         }
391
392         *pifaces = ifaces;
393         return total;
394 }