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
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.
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.
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/>.
24 #include "system/network.h"
25 #include "interfaces.h"
26 #include "lib/util/tsort.h"
27 #include "librpc/gen_ndr/ioctl.h"
30 #include "linux/sockios.h"
31 #include "linux/ethtool.h"
34 /****************************************************************************
35 Create a struct sockaddr_storage with the netmask bits set to 1.
36 ****************************************************************************/
38 bool make_netmask(struct sockaddr_storage
*pss_out
,
39 const struct sockaddr_storage
*pss_in
,
40 unsigned long masklen
)
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
;
52 for (i
= 0; masklen
>= 8; masklen
-= 8, i
++) {
55 /* Deal with the partial byte. */
56 *p
++ &= (0xff & ~(0xff>>masklen
));
58 for (;i
< sizeof(struct in6_addr
); i
++) {
64 if (pss_in
->ss_family
== AF_INET
) {
68 ((struct sockaddr_in
*)pss_out
)->sin_addr
.s_addr
=
69 htonl(((0xFFFFFFFFL
>> masklen
) ^ 0xFFFFFFFFL
));
75 /****************************************************************************
76 Create a struct sockaddr_storage set to the broadcast or network address from
77 an incoming sockaddr_storage.
78 ****************************************************************************/
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
,
85 unsigned int i
= 0, len
= 0;
86 const char *pmask
= NULL
;
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
;
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
;
104 for (i
= 0; i
< len
; i
++, p
++, pmask
++) {
106 *p
= (*p
& *pmask
) | (*pmask
^ 0xff);
114 void make_bcast(struct sockaddr_storage
*pss_out
,
115 const struct sockaddr_storage
*pss_in
,
116 const struct sockaddr_storage
*nmask
)
118 make_bcast_or_net(pss_out
, pss_in
, nmask
, true);
121 void make_net(struct sockaddr_storage
*pss_out
,
122 const struct sockaddr_storage
*pss_in
,
123 const struct sockaddr_storage
*nmask
)
125 make_bcast_or_net(pss_out
, pss_in
, nmask
, false);
129 static void query_iface_speed_from_name(const char *name
, uint64_t *speed
)
132 struct ethtool_cmd ecmd
;
133 struct ethtool_value edata
;
137 fd
= socket(AF_INET
, SOCK_DGRAM
, IPPROTO_IP
);
139 DBG_ERR("Failed to open socket.\n");
143 if (strlen(name
) >= IF_NAMESIZE
) {
144 DBG_ERR("Interface name too long.\n");
149 strlcpy(ifr
.ifr_name
, name
, IF_NAMESIZE
);
151 ifr
.ifr_data
= (void *)&edata
;
153 edata
.cmd
= ETHTOOL_GLINK
;
154 ret
= ioctl(fd
, SIOCETHTOOL
, &ifr
);
158 if (edata
.data
== 0) {
159 /* no link detected */
164 ifr
.ifr_data
= (void *)&ecmd
;
166 ecmd
.cmd
= ETHTOOL_GSET
;
167 ret
= ioctl(fd
, SIOCETHTOOL
, &ifr
);
171 *speed
= ((uint64_t)ethtool_cmd_speed(&ecmd
)) * 1000 * 1000;
177 static void query_iface_rx_queues_from_name(const char *name
,
181 struct ethtool_rxnfc rxcmd
;
185 fd
= socket(AF_INET
, SOCK_DGRAM
, IPPROTO_IP
);
187 DBG_ERR("Failed to open socket.\n");
191 if (strlen(name
) >= IF_NAMESIZE
) {
192 DBG_ERR("Interface name too long.\n");
197 strlcpy(ifr
.ifr_name
, name
, IF_NAMESIZE
);
199 ifr
.ifr_data
= (void *)&rxcmd
;
201 rxcmd
.cmd
= ETHTOOL_GRXRINGS
;
202 ret
= ioctl(fd
, SIOCETHTOOL
, &ifr
);
207 *rx_queues
= rxcmd
.data
;
214 /****************************************************************************
215 Try the "standard" getifaddrs/freeifaddrs interfaces.
216 Also gets IPv6 interfaces.
217 ****************************************************************************/
219 /****************************************************************************
220 Get the netmask address for a local interface.
221 ****************************************************************************/
223 static int _get_interfaces(TALLOC_CTX
*mem_ctx
, struct iface_struct
**pifaces
)
225 struct iface_struct
*ifaces
;
226 struct ifaddrs
*iflist
= NULL
;
227 struct ifaddrs
*ifptr
= NULL
;
232 if (getifaddrs(&iflist
) < 0) {
237 for (ifptr
= iflist
; ifptr
!= NULL
; ifptr
= ifptr
->ifa_next
) {
238 if (!ifptr
->ifa_addr
|| !ifptr
->ifa_netmask
) {
241 if (!(ifptr
->ifa_flags
& IFF_UP
)) {
247 ifaces
= talloc_array(mem_ctx
, struct iface_struct
, count
);
248 if (ifaces
== NULL
) {
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;
258 if (!ifptr
->ifa_addr
|| !ifptr
->ifa_netmask
) {
262 /* Check the interface is up. */
263 if (!(ifptr
->ifa_flags
& IFF_UP
)) {
267 memset(&ifaces
[total
], '\0', sizeof(ifaces
[total
]));
269 copy_size
= sizeof(struct sockaddr_in
);
271 ifaces
[total
].flags
= ifptr
->ifa_flags
;
273 #if defined(HAVE_IPV6)
274 if (ifptr
->ifa_addr
->sa_family
== AF_INET6
) {
275 copy_size
= sizeof(struct sockaddr_in6
);
279 memcpy(&ifaces
[total
].ip
, ifptr
->ifa_addr
, copy_size
);
280 memcpy(&ifaces
[total
].netmask
, ifptr
->ifa_netmask
, copy_size
);
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
;
290 if (IN6_IS_ADDR_LINKLOCAL(in6
) || IN6_IS_ADDR_V4COMPAT(in6
)) {
293 /* IPv6 does not have broadcast it uses multicast. */
294 memset(&ifaces
[total
].bcast
, '\0', copy_size
);
297 if (ifaces
[total
].flags
& (IFF_BROADCAST
|IFF_LOOPBACK
)) {
298 make_bcast(&ifaces
[total
].bcast
,
300 &ifaces
[total
].netmask
);
301 } else if ((ifaces
[total
].flags
& IFF_POINTOPOINT
) &&
302 ifptr
->ifa_dstaddr
) {
303 memcpy(&ifaces
[total
].bcast
,
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
));
317 query_iface_speed_from_name(ifptr
->ifa_name
, &if_speed
);
318 query_iface_rx_queues_from_name(ifptr
->ifa_name
, &rx_queues
);
320 ifaces
[total
].linkspeed
= if_speed
;
321 ifaces
[total
].capability
= FSCTL_NET_IFACE_NONE_CAPABLE
;
323 ifaces
[total
].capability
|= FSCTL_NET_IFACE_RSS_CAPABLE
;
326 if (strlcpy(ifaces
[total
].name
, ifptr
->ifa_name
,
327 sizeof(ifaces
[total
].name
)) >=
328 sizeof(ifaces
[total
].name
)) {
329 /* Truncation ! Ignore. */
341 static int iface_comp(struct iface_struct
*i1
, struct iface_struct
*i2
)
345 #if defined(HAVE_IPV6)
347 * If we have IPv6 - sort these interfaces lower
348 * than any IPv4 ones.
350 if (i1
->ip
.ss_family
== AF_INET6
&&
351 i2
->ip
.ss_family
== AF_INET
) {
353 } else if (i1
->ip
.ss_family
== AF_INET
&&
354 i2
->ip
.ss_family
== AF_INET6
) {
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
;
362 r
= memcmp(&s1
->sin6_addr
,
364 sizeof(struct in6_addr
));
369 s1
= (struct sockaddr_in6
*)&i1
->netmask
;
370 s2
= (struct sockaddr_in6
*)&i2
->netmask
;
372 r
= memcmp(&s1
->sin6_addr
,
374 sizeof(struct in6_addr
));
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.
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
);
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
);
405 /* this wrapper is used to remove duplicates from the interface list generated
407 int get_interfaces(TALLOC_CTX
*mem_ctx
, struct iface_struct
**pifaces
)
409 struct iface_struct
*ifaces
= NULL
;
412 total
= _get_interfaces(mem_ctx
, &ifaces
);
413 /* If we have an error, no interface or just one we can leave */
419 /* now we need to remove duplicates */
420 TYPESAFE_QSORT(ifaces
, total
, iface_comp
);
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];