Line data Source code
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 879112 : bool make_netmask(struct sockaddr_storage *pss_out,
39 : const struct sockaddr_storage *pss_in,
40 : unsigned long masklen)
41 : {
42 879112 : *pss_out = *pss_in;
43 : /* Now apply masklen bits of mask. */
44 : #if defined(HAVE_IPV6)
45 879112 : if (pss_in->ss_family == AF_INET6) {
46 167401 : char *p = (char *)&((struct sockaddr_in6 *)pss_out)->sin6_addr;
47 2851 : unsigned int i;
48 :
49 167401 : if (masklen > 128) {
50 0 : return false;
51 : }
52 1506609 : for (i = 0; masklen >= 8; masklen -= 8, i++) {
53 1339208 : *p++ = 0xff;
54 : }
55 : /* Deal with the partial byte. */
56 167401 : *p++ &= (0xff & ~(0xff>>masklen));
57 167401 : i++;
58 1339208 : for (;i < sizeof(struct in6_addr); i++) {
59 1171807 : *p++ = '\0';
60 : }
61 164550 : return true;
62 : }
63 : #endif
64 711711 : if (pss_in->ss_family == AF_INET) {
65 711711 : if (masklen > 32) {
66 0 : return false;
67 : }
68 711711 : ((struct sockaddr_in *)pss_out)->sin_addr.s_addr =
69 711711 : htonl(((0xFFFFFFFFL >> masklen) ^ 0xFFFFFFFFL));
70 711711 : return true;
71 : }
72 0 : 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 2094754 : 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 2094754 : unsigned int i = 0, len = 0;
86 2094754 : const char *pmask = NULL;
87 2094754 : char *p = NULL;
88 2094754 : *pss_out = *pss_in;
89 :
90 : /* Set all zero netmask bits to 1. */
91 : #if defined(HAVE_IPV6)
92 2094754 : if (pss_in->ss_family == AF_INET6) {
93 334802 : p = (char *)&((struct sockaddr_in6 *)pss_out)->sin6_addr;
94 334802 : pmask = (const char *)&((const struct sockaddr_in6 *)nmask)->sin6_addr;
95 334802 : len = 16;
96 : }
97 : #endif
98 2094754 : if (pss_in->ss_family == AF_INET) {
99 1759952 : p = (char *)&((struct sockaddr_in *)pss_out)->sin_addr;
100 1759952 : pmask = (const char *)&((const struct sockaddr_in *)nmask)->sin_addr;
101 1759952 : len = 4;
102 : }
103 :
104 14491394 : for (i = 0; i < len; i++, p++, pmask++) {
105 12396640 : if (make_bcast_p) {
106 6871380 : *p = (*p & *pmask) | (*pmask ^ 0xff);
107 : } else {
108 : /* make_net */
109 5525260 : *p = (*p & *pmask);
110 : }
111 : }
112 2094754 : }
113 :
114 1215642 : void make_bcast(struct sockaddr_storage *pss_out,
115 : const struct sockaddr_storage *pss_in,
116 : const struct sockaddr_storage *nmask)
117 : {
118 1215642 : make_bcast_or_net(pss_out, pss_in, nmask, true);
119 1215642 : }
120 :
121 879112 : void make_net(struct sockaddr_storage *pss_out,
122 : const struct sockaddr_storage *pss_in,
123 : const struct sockaddr_storage *nmask)
124 : {
125 879112 : make_bcast_or_net(pss_out, pss_in, nmask, false);
126 879112 : }
127 :
128 : #ifdef HAVE_ETHTOOL
129 673060 : static void query_iface_speed_from_name(const char *name, uint64_t *speed)
130 : {
131 673060 : int ret = 0;
132 11472 : struct ethtool_cmd ecmd;
133 11472 : struct ethtool_value edata;
134 11472 : struct ifreq ifr;
135 11472 : int fd;
136 :
137 673060 : fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
138 673060 : if (fd == -1) {
139 0 : DBG_ERR("Failed to open socket.\n");
140 0 : return;
141 : }
142 :
143 673060 : if (strlen(name) >= IF_NAMESIZE) {
144 0 : DBG_ERR("Interface name too long.\n");
145 0 : goto done;
146 : }
147 :
148 673060 : ZERO_STRUCT(ifr);
149 673060 : strlcpy(ifr.ifr_name, name, IF_NAMESIZE);
150 :
151 673060 : ifr.ifr_data = (void *)&edata;
152 673060 : ZERO_STRUCT(edata);
153 673060 : edata.cmd = ETHTOOL_GLINK;
154 673060 : ret = ioctl(fd, SIOCETHTOOL, &ifr);
155 673060 : if (ret == -1) {
156 0 : goto done;
157 : }
158 673060 : if (edata.data == 0) {
159 : /* no link detected */
160 0 : *speed = 0;
161 0 : goto done;
162 : }
163 :
164 673060 : ifr.ifr_data = (void *)&ecmd;
165 673060 : ZERO_STRUCT(ecmd);
166 673060 : ecmd.cmd = ETHTOOL_GSET;
167 673060 : ret = ioctl(fd, SIOCETHTOOL, &ifr);
168 673060 : if (ret == -1) {
169 336530 : goto done;
170 : }
171 336530 : *speed = ((uint64_t)ethtool_cmd_speed(&ecmd)) * 1000 * 1000;
172 :
173 673060 : done:
174 673060 : (void)close(fd);
175 : }
176 :
177 673060 : static void query_iface_rx_queues_from_name(const char *name,
178 : uint64_t *rx_queues)
179 : {
180 673060 : int ret = 0;
181 11472 : struct ethtool_rxnfc rxcmd;
182 11472 : struct ifreq ifr;
183 11472 : int fd;
184 :
185 673060 : fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
186 673060 : if (fd == -1) {
187 0 : DBG_ERR("Failed to open socket.\n");
188 0 : return;
189 : }
190 :
191 673060 : if (strlen(name) >= IF_NAMESIZE) {
192 0 : DBG_ERR("Interface name too long.\n");
193 0 : goto done;
194 : }
195 :
196 673060 : ZERO_STRUCT(ifr);
197 673060 : strlcpy(ifr.ifr_name, name, IF_NAMESIZE);
198 :
199 673060 : ifr.ifr_data = (void *)&rxcmd;
200 673060 : ZERO_STRUCT(rxcmd);
201 673060 : rxcmd.cmd = ETHTOOL_GRXRINGS;
202 673060 : ret = ioctl(fd, SIOCETHTOOL, &ifr);
203 673060 : if (ret == -1) {
204 673060 : goto done;
205 : }
206 :
207 0 : *rx_queues = rxcmd.data;
208 :
209 673060 : done:
210 673060 : (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 168265 : static int _get_interfaces(TALLOC_CTX *mem_ctx, struct iface_struct **pifaces)
224 : {
225 2868 : struct iface_struct *ifaces;
226 168265 : struct ifaddrs *iflist = NULL;
227 168265 : struct ifaddrs *ifptr = NULL;
228 2868 : int count;
229 168265 : int total = 0;
230 2868 : size_t copy_size;
231 :
232 168265 : if (getifaddrs(&iflist) < 0) {
233 0 : return -1;
234 : }
235 :
236 168265 : count = 0;
237 1346120 : for (ifptr = iflist; ifptr != NULL; ifptr = ifptr->ifa_next) {
238 1177855 : if (!ifptr->ifa_addr || !ifptr->ifa_netmask) {
239 336530 : continue;
240 : }
241 841325 : if (!(ifptr->ifa_flags & IFF_UP)) {
242 0 : continue;
243 : }
244 841325 : count += 1;
245 : }
246 :
247 168265 : ifaces = talloc_array(mem_ctx, struct iface_struct, count);
248 168265 : if (ifaces == NULL) {
249 0 : errno = ENOMEM;
250 0 : return -1;
251 : }
252 :
253 : /* Loop through interfaces, looking for given IP address */
254 1346120 : for (ifptr = iflist; ifptr != NULL; ifptr = ifptr->ifa_next) {
255 1177855 : uint64_t if_speed = 1000 * 1000 * 1000; /* 1Gbps */
256 1177855 : uint64_t rx_queues = 1;
257 :
258 1177855 : if (!ifptr->ifa_addr || !ifptr->ifa_netmask) {
259 504795 : continue;
260 : }
261 :
262 : /* Check the interface is up. */
263 841325 : if (!(ifptr->ifa_flags & IFF_UP)) {
264 0 : continue;
265 : }
266 :
267 841325 : memset(&ifaces[total], '\0', sizeof(ifaces[total]));
268 :
269 841325 : copy_size = sizeof(struct sockaddr_in);
270 :
271 841325 : ifaces[total].flags = ifptr->ifa_flags;
272 :
273 : #if defined(HAVE_IPV6)
274 841325 : if (ifptr->ifa_addr->sa_family == AF_INET6) {
275 504795 : copy_size = sizeof(struct sockaddr_in6);
276 : }
277 : #endif
278 :
279 841325 : memcpy(&ifaces[total].ip, ifptr->ifa_addr, copy_size);
280 841325 : memcpy(&ifaces[total].netmask, ifptr->ifa_netmask, copy_size);
281 :
282 : /* calculate broadcast address */
283 : #if defined(HAVE_IPV6)
284 841325 : if (ifptr->ifa_addr->sa_family == AF_INET6) {
285 504795 : struct sockaddr_in6 *sin6 =
286 : (struct sockaddr_in6 *)ifptr->ifa_addr;
287 504795 : struct in6_addr *in6 =
288 : (struct in6_addr *)&sin6->sin6_addr;
289 :
290 504795 : if (IN6_IS_ADDR_LINKLOCAL(in6) || IN6_IS_ADDR_V4COMPAT(in6)) {
291 168265 : continue;
292 : }
293 : /* IPv6 does not have broadcast it uses multicast. */
294 336530 : memset(&ifaces[total].bcast, '\0', copy_size);
295 : } else
296 : #endif
297 336530 : if (ifaces[total].flags & (IFF_BROADCAST|IFF_LOOPBACK)) {
298 336530 : make_bcast(&ifaces[total].bcast,
299 330794 : &ifaces[total].ip,
300 330794 : &ifaces[total].netmask);
301 0 : } else if ((ifaces[total].flags & IFF_POINTOPOINT) &&
302 0 : ifptr->ifa_dstaddr ) {
303 0 : memcpy(&ifaces[total].bcast,
304 0 : ifptr->ifa_dstaddr,
305 : copy_size);
306 : } else {
307 0 : continue;
308 : }
309 :
310 673060 : ifaces[total].if_index = if_nametoindex(ifptr->ifa_name);
311 673060 : if (ifaces[total].if_index == 0) {
312 0 : DBG_ERR("Failed to retrieve interface index for '%s': "
313 : "%s\n", ifptr->ifa_name, strerror(errno));
314 : }
315 :
316 : #ifdef HAVE_ETHTOOL
317 673060 : query_iface_speed_from_name(ifptr->ifa_name, &if_speed);
318 673060 : query_iface_rx_queues_from_name(ifptr->ifa_name, &rx_queues);
319 : #endif
320 673060 : ifaces[total].linkspeed = if_speed;
321 673060 : ifaces[total].capability = FSCTL_NET_IFACE_NONE_CAPABLE;
322 673060 : if (rx_queues > 1) {
323 0 : ifaces[total].capability |= FSCTL_NET_IFACE_RSS_CAPABLE;
324 : }
325 :
326 673060 : if (strlcpy(ifaces[total].name, ifptr->ifa_name,
327 : sizeof(ifaces[total].name)) >=
328 : sizeof(ifaces[total].name)) {
329 : /* Truncation ! Ignore. */
330 0 : continue;
331 : }
332 673060 : total++;
333 : }
334 :
335 168265 : freeifaddrs(iflist);
336 :
337 168265 : *pifaces = ifaces;
338 168265 : return total;
339 : }
340 :
341 1346120 : static int iface_comp(struct iface_struct *i1, struct iface_struct *i2)
342 : {
343 22944 : 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 1346120 : if (i1->ip.ss_family == AF_INET6 &&
351 673060 : i2->ip.ss_family == AF_INET) {
352 165397 : return -1;
353 1177855 : } else if (i1->ip.ss_family == AF_INET &&
354 673060 : i2->ip.ss_family == AF_INET6) {
355 330794 : return 1;
356 : }
357 :
358 841325 : if (i1->ip.ss_family == AF_INET6) {
359 504795 : struct sockaddr_in6 *s1 = (struct sockaddr_in6 *)&i1->ip;
360 504795 : struct sockaddr_in6 *s2 = (struct sockaddr_in6 *)&i2->ip;
361 :
362 504795 : r = memcmp(&s1->sin6_addr,
363 504795 : &s2->sin6_addr,
364 : sizeof(struct in6_addr));
365 504795 : if (r) {
366 496191 : return r;
367 : }
368 :
369 0 : s1 = (struct sockaddr_in6 *)&i1->netmask;
370 0 : s2 = (struct sockaddr_in6 *)&i2->netmask;
371 :
372 0 : r = memcmp(&s1->sin6_addr,
373 0 : &s2->sin6_addr,
374 : sizeof(struct in6_addr));
375 0 : if (r) {
376 0 : 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 336530 : if (((struct sockaddr *)&i1->ip)->sa_family == AF_INET) {
387 336530 : struct sockaddr_in *s1 = (struct sockaddr_in *)&i1->ip;
388 336530 : struct sockaddr_in *s2 = (struct sockaddr_in *)&i2->ip;
389 :
390 336530 : r = ntohl(s1->sin_addr.s_addr) -
391 336530 : ntohl(s2->sin_addr.s_addr);
392 336530 : if (r) {
393 330794 : return r;
394 : }
395 :
396 0 : s1 = (struct sockaddr_in *)&i1->netmask;
397 0 : s2 = (struct sockaddr_in *)&i2->netmask;
398 :
399 0 : return ntohl(s1->sin_addr.s_addr) -
400 0 : ntohl(s2->sin_addr.s_addr);
401 : }
402 0 : return 0;
403 : }
404 :
405 : /* this wrapper is used to remove duplicates from the interface list generated
406 : above */
407 168265 : int get_interfaces(TALLOC_CTX *mem_ctx, struct iface_struct **pifaces)
408 : {
409 168265 : struct iface_struct *ifaces = NULL;
410 2868 : int total, i, j;
411 :
412 168265 : total = _get_interfaces(mem_ctx, &ifaces);
413 : /* If we have an error, no interface or just one we can leave */
414 168265 : if (total <= 1) {
415 0 : *pifaces = ifaces;
416 0 : return total;
417 : }
418 :
419 : /* now we need to remove duplicates */
420 168265 : TYPESAFE_QSORT(ifaces, total, iface_comp);
421 :
422 673060 : for (i=1;i<total;) {
423 504795 : if (iface_comp(&ifaces[i-1], &ifaces[i]) == 0) {
424 0 : for (j=i-1;j<total-1;j++) {
425 0 : ifaces[j] = ifaces[j+1];
426 : }
427 0 : total--;
428 : } else {
429 504795 : i++;
430 : }
431 : }
432 :
433 168265 : *pifaces = ifaces;
434 168265 : return total;
435 : }
|