2 * Copyright (c) 2005 Red Hat Inc
3 * Copyright (c) 2006 Sun Microsystems, Inc.
7 * Author: Patrick Caulfield (pcaulfie@redhat.com)
9 * This software licensed under BSD license, the text of which follows:
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions are met:
14 * - Redistributions of source code must retain the above copyright notice,
15 * this list of conditions and the following disclaimer.
16 * - Redistributions in binary form must reproduce the above copyright notice,
17 * this list of conditions and the following disclaimer in the documentation
18 * and/or other materials provided with the distribution.
19 * - Neither the name of the MontaVista Software, Inc. nor the names of its
20 * contributors may be used to endorse or promote products derived from this
21 * software without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
33 * THE POSSIBILITY OF SUCH DAMAGE.
36 /* IPv4/6 abstraction */
38 #include <sys/ioctl.h>
39 #include <sys/types.h>
40 #include <sys/socket.h>
41 #include <arpa/inet.h>
42 #include <netinet/in.h>
43 #include <arpa/inet.h>
45 #if defined(OPENAIS_BSD) || defined(OPENAIS_DARWIN) || defined(OPENAIS_SOLARIS)
46 #include <sys/sockio.h>
48 #ifndef OPENAIS_SOLARIS
49 #include <net/if_var.h>
51 #include <netinet/in_var.h>
60 #if defined(OPENAIS_LINUX)
63 /* ARGH!! I hate netlink */
64 #include <asm/types.h>
65 #include <linux/rtnetlink.h>
67 /* this should catch 2.6.19 headers */
69 #include <linux/if_addr.h>
71 /* redefine macro that disappeared in 2.6.19 */
73 #define IFA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
78 #if ! defined(OPENAIS_SOLARIS) && ! defined(s6_addr16)
79 #define s6_addr16 __u6_addr.__u6_addr16
85 #define LOCALHOST_IPV4 "127.0.0.1"
86 #define LOCALHOST_IPV6 "::1"
88 #define NETLINK_BUFSIZE 16384
91 void totemip_nosigpipe(int s
)
94 setsockopt(s
, SOL_SOCKET
, SO_NOSIGPIPE
, (void *)&on
, sizeof(on
));
98 /* Compare two addresses */
99 int totemip_equal(struct totem_ip_address
*addr1
, struct totem_ip_address
*addr2
)
103 if (addr1
->family
!= addr2
->family
)
106 if (addr1
->family
== AF_INET
) {
107 addrlen
= sizeof(struct in_addr
);
109 if (addr1
->family
== AF_INET6
) {
110 addrlen
= sizeof(struct in6_addr
);
114 if (memcmp(addr1
->addr
, addr2
->addr
, addrlen
) == 0)
121 /* Copy a totem_ip_address */
122 void totemip_copy(struct totem_ip_address
*addr1
, struct totem_ip_address
*addr2
)
124 memcpy(addr1
, addr2
, sizeof(struct totem_ip_address
));
127 void totemip_copy_endian_convert(struct totem_ip_address
*addr1
, struct totem_ip_address
*addr2
)
129 addr1
->nodeid
= swab32(addr2
->nodeid
);
130 addr1
->family
= swab16(addr2
->family
);
131 if (addr1
!= addr2
) {
132 memcpy(addr1
->addr
, addr2
->addr
, TOTEMIP_ADDRLEN
);
136 /* For sorting etc. params are void * for qsort's benefit */
137 int totemip_compare(const void *a
, const void *b
)
140 struct totem_ip_address
*totemip_a
= (struct totem_ip_address
*)a
;
141 struct totem_ip_address
*totemip_b
= (struct totem_ip_address
*)b
;
142 struct in_addr ipv4_a1
;
143 struct in_addr ipv4_a2
;
144 struct in6_addr ipv6_a1
;
145 struct in6_addr ipv6_a2
;
146 unsigned short family
;
149 * Use memcpy to align since totem_ip_address is unaligned on various archs
151 memcpy (&family
, &totemip_a
->family
, sizeof (unsigned short));
153 if (family
== AF_INET
) {
154 memcpy (&ipv4_a1
, totemip_a
->addr
, sizeof (struct in_addr
));
155 memcpy (&ipv4_a2
, totemip_b
->addr
, sizeof (struct in_addr
));
156 if (ipv4_a1
.s_addr
== ipv4_a2
.s_addr
) {
159 if (htonl(ipv4_a1
.s_addr
) < htonl(ipv4_a2
.s_addr
)) {
165 if (family
== AF_INET6
) {
167 * Compare 16 bits at a time the ipv6 address
169 memcpy (&ipv6_a1
, totemip_a
->addr
, sizeof (struct in6_addr
));
170 memcpy (&ipv6_a2
, totemip_b
->addr
, sizeof (struct in6_addr
));
171 for (i
= 0; i
< 8; i
++) {
172 #ifndef OPENAIS_SOLARIS
173 int res
= htons(ipv6_a1
.s6_addr16
[i
]) -
174 htons(ipv6_a2
.s6_addr16
[i
]);
176 int res
= htons(((uint16_t *)ipv6_a1
.s6_addr
)[i
]) -
177 htons(((uint16_t *)ipv6_a2
.s6_addr
)[i
]);
186 * Family not set, should be!
193 /* Build a localhost totem_ip_address */
194 int totemip_localhost(int family
, struct totem_ip_address
*localhost
)
199 memset (localhost
, 0, sizeof (struct totem_ip_address
));
201 if (family
== AF_INET
) {
202 addr_text
= LOCALHOST_IPV4
;
203 if (inet_pton(family
, addr_text
, (char *)&nodeid
) <= 0) {
206 localhost
->nodeid
= ntohl(nodeid
);
208 addr_text
= LOCALHOST_IPV6
;
211 if (inet_pton(family
, addr_text
, (char *)localhost
->addr
) <= 0)
214 localhost
->family
= family
;
219 int totemip_localhost_check(struct totem_ip_address
*addr
)
221 struct totem_ip_address localhost
;
223 if (totemip_localhost(addr
->family
, &localhost
))
225 return totemip_equal(addr
, &localhost
);
228 const char *totemip_print(struct totem_ip_address
*addr
)
230 static char buf
[INET6_ADDRSTRLEN
];
232 return inet_ntop(addr
->family
, addr
->addr
, buf
, sizeof(buf
));
235 /* Make a totem_ip_address into a usable sockaddr_storage */
236 int totemip_totemip_to_sockaddr_convert(struct totem_ip_address
*ip_addr
,
237 uint16_t port
, struct sockaddr_storage
*saddr
, int *addrlen
)
241 if (ip_addr
->family
== AF_INET
) {
242 struct sockaddr_in
*sin
= (struct sockaddr_in
*)saddr
;
244 memset(sin
, 0, sizeof(struct sockaddr_in
));
245 #if defined(OPENAIS_BSD) || defined(OPENAIS_DARWIN)
246 sin
->sin_len
= sizeof(struct sockaddr_in
);
248 sin
->sin_family
= ip_addr
->family
;
249 sin
->sin_port
= htons (port
);
250 memcpy(&sin
->sin_addr
, ip_addr
->addr
, sizeof(struct in_addr
));
251 *addrlen
= sizeof(struct sockaddr_in
);
255 if (ip_addr
->family
== AF_INET6
) {
256 struct sockaddr_in6
*sin
= (struct sockaddr_in6
*)saddr
;
258 memset(sin
, 0, sizeof(struct sockaddr_in6
));
259 #if defined(OPENAIS_BSD) || defined(OPENAIS_DARWIN)
260 sin
->sin6_len
= sizeof(struct sockaddr_in6
);
262 sin
->sin6_family
= ip_addr
->family
;
263 sin
->sin6_port
= htons (port
);
264 sin
->sin6_scope_id
= 2;
265 memcpy(&sin
->sin6_addr
, ip_addr
->addr
, sizeof(struct in6_addr
));
267 *addrlen
= sizeof(struct sockaddr_in6
);
274 /* Converts an address string string into a totem_ip_address.
275 * family can be AF_INET, AF_INET6 or 0 (for "don't care")
277 int totemip_parse(struct totem_ip_address
*totemip
, char *addr
, int family
)
279 struct addrinfo
*ainfo
;
280 struct addrinfo ahints
;
281 struct sockaddr_in
*sa
;
282 struct sockaddr_in6
*sa6
;
285 memset(&ahints
, 0, sizeof(ahints
));
286 ahints
.ai_socktype
= SOCK_DGRAM
;
287 ahints
.ai_protocol
= IPPROTO_UDP
;
288 ahints
.ai_family
= family
;
290 /* Lookup the nodename address */
291 ret
= getaddrinfo(addr
, NULL
, &ahints
, &ainfo
);
295 sa
= (struct sockaddr_in
*)ainfo
->ai_addr
;
296 sa6
= (struct sockaddr_in6
*)ainfo
->ai_addr
;
297 totemip
->family
= ainfo
->ai_family
;
299 if (ainfo
->ai_family
== AF_INET
)
300 memcpy(totemip
->addr
, &sa
->sin_addr
, sizeof(struct in_addr
));
302 memcpy(totemip
->addr
, &sa6
->sin6_addr
, sizeof(struct in6_addr
));
309 /* Make a sockaddr_* into a totem_ip_address */
310 int totemip_sockaddr_to_totemip_convert(struct sockaddr_storage
*saddr
, struct totem_ip_address
*ip_addr
)
314 ip_addr
->family
= saddr
->ss_family
;
317 if (saddr
->ss_family
== AF_INET
) {
318 struct sockaddr_in
*sin
= (struct sockaddr_in
*)saddr
;
320 memcpy(ip_addr
->addr
, &sin
->sin_addr
, sizeof(struct in_addr
));
324 if (saddr
->ss_family
== AF_INET6
) {
325 struct sockaddr_in6
*sin
= (struct sockaddr_in6
*)saddr
;
327 memcpy(ip_addr
->addr
, &sin
->sin6_addr
, sizeof(struct in6_addr
));
334 #if defined(OPENAIS_BSD) || defined(OPENAIS_DARWIN) || defined(OPENAIS_SOLARIS)
335 int totemip_iface_check(struct totem_ip_address
*bindnet
,
336 struct totem_ip_address
*boundto
,
340 #ifndef OPENAIS_SOLARIS
341 #define NEXT_IFR(a) ((struct ifreq *)((u_char *)&(a)->ifr_addr +\
342 ((a)->ifr_addr.sa_len ? (a)->ifr_addr.sa_len : sizeof((a)->ifr_addr))))
344 #define NEXT_IFR(a) ((struct ifreq *)((u_char *)&(a)->ifr_addr +\
345 sizeof((a)->ifr_addr)))
348 struct sockaddr_in
*intf_addr_mask
;
349 struct sockaddr_storage bindnet_ss
, intf_addr_ss
;
350 struct sockaddr_in
*intf_addr_sin
= (struct sockaddr_in
*)&intf_addr_ss
;
351 struct sockaddr_in
*bindnet_sin
= (struct sockaddr_in
*)&bindnet_ss
;
352 struct ifreq
*ifr
, *lifr
;
363 totemip_totemip_to_sockaddr_convert(bindnet
,
364 0, &bindnet_ss
, &addrlen
);
367 * Generate list of local interfaces in ifc.ifc_req structure
369 id_fd
= socket (AF_INET
, SOCK_DGRAM
, 0);
374 ifc
.ifc_len
= sizeof (struct ifreq
) * numreqs
;
375 ifc_buf_tmp
= realloc (ifc
.ifc_buf
, ifc
.ifc_len
);
376 if (ifc_buf_tmp
== NULL
) {
378 if (ifc
.ifc_buf
!= NULL
) {
383 ifc
.ifc_buf
= ifc_buf_tmp
;
384 res
= ioctl (id_fd
, SIOCGIFCONF
, &ifc
);
390 } while (ifc
.ifc_len
== sizeof (struct ifreq
) * numreqs
);
394 * Find interface address to bind to
396 lifr
= (struct ifreq
*)ifc
.ifc_buf
+ (ifc
.ifc_len
/ sizeof(*lifr
));
398 for (ifr
= ifc
.ifc_req
; ifr
< lifr
; ifr
= NEXT_IFR(ifr
)) {
399 strcpy(ifrb
.ifr_name
, ifr
->ifr_name
);
401 /* Skip if no address set
403 if (ioctl(id_fd
, SIOCGIFADDR
, &ifrb
) < 0)
406 memcpy(&intf_addr_ss
, &ifrb
.ifr_addr
, sizeof(intf_addr_ss
));
407 if (intf_addr_sin
->sin_family
== AF_INET
) {
410 if (ioctl(id_fd
, SIOCGIFNETMASK
, &ifrb
) < 0) {
413 intf_addr_mask
= (struct sockaddr_in
*)&ifrb
.ifr_addr
;
415 if ( bindnet_sin
->sin_family
== AF_INET
&&
416 (intf_addr_sin
->sin_addr
.s_addr
& intf_addr_mask
->sin_addr
.s_addr
) ==
417 (bindnet_sin
->sin_addr
.s_addr
& intf_addr_mask
->sin_addr
.s_addr
)) {
419 totemip_copy(boundto
, bindnet
);
420 memcpy(boundto
->addr
, &intf_addr_sin
->sin_addr
, sizeof(intf_addr_sin
->sin_addr
));
422 /* Get inteface state
424 if (ioctl(id_fd
, SIOCGIFFLAGS
, &ifrb
) < 0) {
427 *interface_up
= ifrb
.ifr_flags
& IFF_UP
;
429 /* Get interface index
432 if (ioctl(id_fd
, SIOCGIFINDEX
, &ifrb
) < 0) {
435 *interface_num
= ifrb
.ifr_index
;
437 *interface_num
= if_nametoindex(ifrb
.ifr_name
);
444 if (ifc
.ifc_buf
!= NULL
) {
451 #elif defined(OPENAIS_LINUX)
453 static void parse_rtattr(struct rtattr
*tb
[], int max
, struct rtattr
*rta
, int len
)
455 while (RTA_OK(rta
, len
)) {
456 if (rta
->rta_type
<= max
)
457 tb
[rta
->rta_type
] = rta
;
458 rta
= RTA_NEXT(rta
,len
);
462 int totemip_iface_check(struct totem_ip_address
*bindnet
,
463 struct totem_ip_address
*boundto
,
472 struct sockaddr_nl nladdr
;
473 struct totem_ip_address ipaddr
;
474 static char rcvbuf
[NETLINK_BUFSIZE
];
478 memset(&ipaddr
, 0, sizeof(ipaddr
));
480 /* Make sure we preserve these */
481 ipaddr
.family
= bindnet
->family
;
482 ipaddr
.nodeid
= bindnet
->nodeid
;
484 /* Ask netlink for a list of interface addresses */
485 fd
= socket(AF_NETLINK
, SOCK_RAW
, NETLINK_ROUTE
);
489 setsockopt(fd
,SOL_SOCKET
,SO_RCVBUF
,&rcvbuf
,sizeof(rcvbuf
));
491 memset(&nladdr
, 0, sizeof(nladdr
));
492 nladdr
.nl_family
= AF_NETLINK
;
494 req
.nlh
.nlmsg_len
= sizeof(req
);
495 req
.nlh
.nlmsg_type
= RTM_GETADDR
;
496 req
.nlh
.nlmsg_flags
= NLM_F_ROOT
|NLM_F_MATCH
|NLM_F_REQUEST
;
497 req
.nlh
.nlmsg_pid
= 0;
498 req
.nlh
.nlmsg_seq
= 1;
499 req
.g
.rtgen_family
= bindnet
->family
;
501 if (sendto(fd
, (void *)&req
, sizeof(req
), 0,
502 (struct sockaddr
*)&nladdr
, sizeof(nladdr
)) < 0) {
507 /* Look through the return buffer for our address */
512 struct iovec iov
= { rcvbuf
, sizeof(rcvbuf
) };
513 struct msghdr msg
= {
514 (void*)&nladdr
, sizeof(nladdr
),
520 status
= recvmsg(fd
, &msg
, 0);
526 h
= (struct nlmsghdr
*)rcvbuf
;
527 if (h
->nlmsg_type
== NLMSG_DONE
)
530 if (h
->nlmsg_type
== NLMSG_ERROR
) {
535 while (NLMSG_OK(h
, status
)) {
536 if (h
->nlmsg_type
== RTM_NEWADDR
) {
537 struct ifaddrmsg
*ifa
= NLMSG_DATA(h
);
538 struct rtattr
*tb
[IFA_MAX
+1];
539 int len
= h
->nlmsg_len
- NLMSG_LENGTH(sizeof(*ifa
));
542 memset(tb
, 0, sizeof(tb
));
544 parse_rtattr(tb
, IFA_MAX
, IFA_RTA(ifa
), len
);
546 memcpy(ipaddr
.addr
, RTA_DATA(tb
[IFA_ADDRESS
]), TOTEMIP_ADDRLEN
);
547 if (totemip_equal(&ipaddr
, bindnet
))
550 /* If the address we have is an IPv4 network address, then
551 substitute the actual IP address of this interface */
552 if (!found_if
&& tb
[IFA_BROADCAST
] && ifa
->ifa_family
== AF_INET
) {
555 uint32_t netmask
= htonl(~((1<<(32-ifa
->ifa_prefixlen
))-1));
557 memcpy(&network
, RTA_DATA(tb
[IFA_BROADCAST
]), sizeof(uint32_t));
558 memcpy(&addr
, bindnet
->addr
, sizeof(uint32_t));
560 if (addr
== (network
& netmask
)) {
561 memcpy(ipaddr
.addr
, RTA_DATA(tb
[IFA_ADDRESS
]), TOTEMIP_ADDRLEN
);
568 /* Found it - check I/F is UP */
570 int ioctl_fd
; /* Can't do ioctls on netlink FDs */
572 ioctl_fd
= socket(AF_INET
, SOCK_STREAM
, 0);
577 memset(&ifr
, 0, sizeof(ifr
));
578 ifr
.ifr_ifindex
= ifa
->ifa_index
;
580 /* SIOCGIFFLAGS needs an interface name */
581 status
= ioctl(ioctl_fd
, SIOCGIFNAME
, &ifr
);
582 status
= ioctl(ioctl_fd
, SIOCGIFFLAGS
, &ifr
);
589 if (ifr
.ifr_flags
& IFF_UP
)
592 *interface_num
= ifa
->ifa_index
;
598 h
= NLMSG_NEXT(h
, status
);
602 totemip_copy (boundto
, &ipaddr
);
606 #endif /* OPENAIS_LINUX */