s3:utils: Fix Inherit-Only flag being automatically propagated to children
[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 address 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.\n");
140                 return;
141         }
142
143         if (strlen(name) >= IF_NAMESIZE) {
144                 DBG_ERR("Interface name too long.\n");
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
177 static void query_iface_rx_queues_from_name(const char *name,
178                                             uint64_t *rx_queues)
179 {
180         int ret = 0;
181         struct ethtool_rxnfc rxcmd;
182         struct ifreq ifr;
183         int fd;
184
185         fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
186         if (fd == -1) {
187                 DBG_ERR("Failed to open socket.\n");
188                 return;
189         }
190
191         if (strlen(name) >= IF_NAMESIZE) {
192                 DBG_ERR("Interface name too long.\n");
193                 goto done;
194         }
195
196         ZERO_STRUCT(ifr);
197         strlcpy(ifr.ifr_name, name, IF_NAMESIZE);
198
199         ifr.ifr_data = (void *)&rxcmd;
200         ZERO_STRUCT(rxcmd);
201         rxcmd.cmd = ETHTOOL_GRXRINGS;
202         ret = ioctl(fd, SIOCETHTOOL, &ifr);
203         if (ret == -1) {
204                 goto done;
205         }
206
207         *rx_queues = rxcmd.data;
208
209 done:
210         (void)close(fd);
211 }
212 #endif
213
214 /****************************************************************************
215  Try the "standard" getifaddrs/freeifaddrs interfaces.
216  Also gets IPv6 interfaces.
217 ****************************************************************************/
218
219 /****************************************************************************
220  Get the netmask address for a local interface.
221 ****************************************************************************/
222
223 static int _get_interfaces(TALLOC_CTX *mem_ctx, struct iface_struct **pifaces)
224 {
225         struct iface_struct *ifaces;
226         struct ifaddrs *iflist = NULL;
227         struct ifaddrs *ifptr = NULL;
228         int count;
229         int total = 0;
230         size_t copy_size;
231
232         if (getifaddrs(&iflist) < 0) {
233                 return -1;
234         }
235
236         count = 0;
237         for (ifptr = iflist; ifptr != NULL; ifptr = ifptr->ifa_next) {
238                 if (!ifptr->ifa_addr || !ifptr->ifa_netmask) {
239                         continue;
240                 }
241                 if (!(ifptr->ifa_flags & IFF_UP)) {
242                         continue;
243                 }
244                 count += 1;
245         }
246
247         ifaces = talloc_array(mem_ctx, struct iface_struct, count);
248         if (ifaces == NULL) {
249                 errno = ENOMEM;
250                 return -1;
251         }
252
253         /* Loop through interfaces, looking for given IP address */
254         for (ifptr = iflist; ifptr != NULL; ifptr = ifptr->ifa_next) {
255                 uint64_t if_speed = 1000 * 1000 * 1000; /* 1Gbps */
256                 uint64_t rx_queues = 1;
257
258                 if (!ifptr->ifa_addr || !ifptr->ifa_netmask) {
259                         continue;
260                 }
261
262                 /* Check the interface is up. */
263                 if (!(ifptr->ifa_flags & IFF_UP)) {
264                         continue;
265                 }
266
267                 memset(&ifaces[total], '\0', sizeof(ifaces[total]));
268
269                 copy_size = sizeof(struct sockaddr_in);
270
271                 ifaces[total].flags = ifptr->ifa_flags;
272
273 #if defined(HAVE_IPV6)
274                 if (ifptr->ifa_addr->sa_family == AF_INET6) {
275                         copy_size = sizeof(struct sockaddr_in6);
276                 }
277 #endif
278
279                 memcpy(&ifaces[total].ip, ifptr->ifa_addr, copy_size);
280                 memcpy(&ifaces[total].netmask, ifptr->ifa_netmask, copy_size);
281
282                 /* calculate broadcast address */
283 #if defined(HAVE_IPV6)
284                 if (ifptr->ifa_addr->sa_family == AF_INET6) {
285                         struct sockaddr_in6 *sin6 =
286                                 (struct sockaddr_in6 *)ifptr->ifa_addr;
287                         struct in6_addr *in6 =
288                                 (struct in6_addr *)&sin6->sin6_addr;
289
290                         if (IN6_IS_ADDR_LINKLOCAL(in6) || IN6_IS_ADDR_V4COMPAT(in6)) {
291                                 continue;
292                         }
293                         /* IPv6 does not have broadcast it uses multicast. */
294                         memset(&ifaces[total].bcast, '\0', copy_size);
295                 } else
296 #endif
297                 if (ifaces[total].flags & (IFF_BROADCAST|IFF_LOOPBACK)) {
298                         make_bcast(&ifaces[total].bcast,
299                                 &ifaces[total].ip,
300                                 &ifaces[total].netmask);
301                 } else if ((ifaces[total].flags & IFF_POINTOPOINT) &&
302                                ifptr->ifa_dstaddr ) {
303                         memcpy(&ifaces[total].bcast,
304                                 ifptr->ifa_dstaddr,
305                                 copy_size);
306                 } else {
307                         continue;
308                 }
309
310                 ifaces[total].if_index = if_nametoindex(ifptr->ifa_name);
311                 if (ifaces[total].if_index == 0) {
312                         DBG_ERR("Failed to retrieve interface index for '%s': "
313                                 "%s\n", ifptr->ifa_name, strerror(errno));
314                 }
315
316 #ifdef HAVE_ETHTOOL
317                 query_iface_speed_from_name(ifptr->ifa_name, &if_speed);
318                 query_iface_rx_queues_from_name(ifptr->ifa_name, &rx_queues);
319 #endif
320                 ifaces[total].linkspeed = if_speed;
321                 ifaces[total].capability = FSCTL_NET_IFACE_NONE_CAPABLE;
322                 if (rx_queues > 1) {
323                         ifaces[total].capability |= FSCTL_NET_IFACE_RSS_CAPABLE;
324                 }
325
326                 if (strlcpy(ifaces[total].name, ifptr->ifa_name,
327                         sizeof(ifaces[total].name)) >=
328                                 sizeof(ifaces[total].name)) {
329                         /* Truncation ! Ignore. */
330                         continue;
331                 }
332                 total++;
333         }
334
335         freeifaddrs(iflist);
336
337         *pifaces = ifaces;
338         return total;
339 }
340
341 static int iface_comp(struct iface_struct *i1, struct iface_struct *i2)
342 {
343         int r;
344
345 #if defined(HAVE_IPV6)
346         /*
347          * If we have IPv6 - sort these interfaces lower
348          * than any IPv4 ones.
349          */
350         if (i1->ip.ss_family == AF_INET6 &&
351                         i2->ip.ss_family == AF_INET) {
352                 return -1;
353         } else if (i1->ip.ss_family == AF_INET &&
354                         i2->ip.ss_family == AF_INET6) {
355                 return 1;
356         }
357
358         if (i1->ip.ss_family == AF_INET6) {
359                 struct sockaddr_in6 *s1 = (struct sockaddr_in6 *)&i1->ip;
360                 struct sockaddr_in6 *s2 = (struct sockaddr_in6 *)&i2->ip;
361
362                 r = memcmp(&s1->sin6_addr,
363                                 &s2->sin6_addr,
364                                 sizeof(struct in6_addr));
365                 if (r) {
366                         return r;
367                 }
368
369                 s1 = (struct sockaddr_in6 *)&i1->netmask;
370                 s2 = (struct sockaddr_in6 *)&i2->netmask;
371
372                 r = memcmp(&s1->sin6_addr,
373                                 &s2->sin6_addr,
374                                 sizeof(struct in6_addr));
375                 if (r) {
376                         return r;
377                 }
378         }
379 #endif
380
381         /* AIX uses __ss_family instead of ss_family inside of
382            sockaddr_storage. Instead of trying to figure out which field to
383            use, we can just cast it to a sockaddr.
384          */
385
386         if (((struct sockaddr *)&i1->ip)->sa_family == AF_INET) {
387                 struct sockaddr_in *s1 = (struct sockaddr_in *)&i1->ip;
388                 struct sockaddr_in *s2 = (struct sockaddr_in *)&i2->ip;
389                 uint32_t a1 = ntohl(s1->sin_addr.s_addr);
390                 uint32_t a2 = ntohl(s2->sin_addr.s_addr);
391                 r = NUMERIC_CMP(a1, a2);
392                 if (r == 0) {
393                         /* compare netmasks as a tiebreaker */
394                         s1 = (struct sockaddr_in *)&i1->netmask;
395                         s2 = (struct sockaddr_in *)&i2->netmask;
396                         a1 = ntohl(s1->sin_addr.s_addr);
397                         a2 = ntohl(s2->sin_addr.s_addr);
398                         r = NUMERIC_CMP(a1, a2);
399                 }
400                 return r;
401         }
402         return 0;
403 }
404
405 /* this wrapper is used to remove duplicates from the interface list generated
406    above */
407 int get_interfaces(TALLOC_CTX *mem_ctx, struct iface_struct **pifaces)
408 {
409         struct iface_struct *ifaces = NULL;
410         int total, i, j;
411
412         total = _get_interfaces(mem_ctx, &ifaces);
413         /* If we have an error, no interface or just one we can leave */
414         if (total <= 1) {
415                 *pifaces = ifaces;
416                 return total;
417         }
418
419         /* now we need to remove duplicates */
420         TYPESAFE_QSORT(ifaces, total, iface_comp);
421
422         for (i=1;i<total;) {
423                 if (iface_comp(&ifaces[i-1], &ifaces[i]) == 0) {
424                         for (j=i-1;j<total-1;j++) {
425                                 ifaces[j] = ifaces[j+1];
426                         }
427                         total--;
428                 } else {
429                         i++;
430                 }
431         }
432
433         *pifaces = ifaces;
434         return total;
435 }