/* $NetBSD: grabmyaddr.c,v 1.4.6.3 2008/06/18 07:30:18 mgrooms Exp $ */ /* Id: grabmyaddr.c,v 1.27 2006/04/06 16:27:05 manubsd Exp */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #include <sys/types.h> #include <sys/param.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <net/if.h> #if defined(__FreeBSD__) && __FreeBSD__ >= 3 #include <net/if_var.h> #endif #if defined(__NetBSD__) || defined(__FreeBSD__) || \ (defined(__APPLE__) && defined(__MACH__)) #include <netinet/in.h> #include <netinet6/in6_var.h> #endif #include <net/route.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <errno.h> #ifdef HAVE_UNISTD_H #include <unistd.h> #endif #include <netdb.h> #ifdef HAVE_GETIFADDRS #include <ifaddrs.h> #include <net/if.h> #endif #include "var.h" #include "misc.h" #include "vmbuf.h" #include "plog.h" #include "sockmisc.h" #include "debug.h" #include "localconf.h" #include "handler.h" #include "grabmyaddr.h" #include "sockmisc.h" #include "isakmp_var.h" #include "gcmalloc.h" #include "nattraversal.h" #ifdef __linux__ #include <linux/types.h> #include <linux/rtnetlink.h> #ifndef HAVE_GETIFADDRS #define HAVE_GETIFADDRS #define NEED_LINUX_GETIFADDRS #endif #endif #ifdef ANDROID_CHANGES #include "NetdClient.h" #endif #ifndef HAVE_GETIFADDRS static unsigned int if_maxindex __P((void)); #endif static struct myaddrs *find_myaddr __P((struct myaddrs *, struct myaddrs *)); static int suitable_ifaddr __P((const char *, const struct sockaddr *)); #ifdef INET6 static int suitable_ifaddr6 __P((const char *, const struct sockaddr *)); #endif #ifdef NEED_LINUX_GETIFADDRS /* We could do this _much_ better. kame racoon in its current form * will esentially die at frequent changes of address configuration. */ struct ifaddrs { struct ifaddrs *ifa_next; char ifa_name[16]; int ifa_ifindex; struct sockaddr *ifa_addr; struct sockaddr_storage ifa_addrbuf; }; static int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len) { while (RTA_OK(rta, len)) { if (rta->rta_type <= max) tb[rta->rta_type] = rta; rta = RTA_NEXT(rta,len); } return 0; } static void recvaddrs(int fd, struct ifaddrs **ifa, __u32 seq) { char buf[8192]; struct sockaddr_nl nladdr; struct iovec iov = { buf, sizeof(buf) }; struct ifaddrmsg *m; struct rtattr * rta_tb[IFA_MAX+1]; struct ifaddrs *I; while (1) { int status; struct nlmsghdr *h; struct msghdr msg = { (void*)&nladdr, sizeof(nladdr), &iov, 1, NULL, 0, 0 }; status = recvmsg(fd, &msg, 0); if (status < 0) continue; if (status == 0) return; if (nladdr.nl_pid) /* Message not from kernel */ continue; h = (struct nlmsghdr*)buf; while (NLMSG_OK(h, status)) { if (h->nlmsg_seq != seq) goto skip_it; if (h->nlmsg_type == NLMSG_DONE) return; if (h->nlmsg_type == NLMSG_ERROR) return; if (h->nlmsg_type != RTM_NEWADDR) goto skip_it; m = NLMSG_DATA(h); if (m->ifa_family != AF_INET && m->ifa_family != AF_INET6) goto skip_it; if (m->ifa_flags&IFA_F_TENTATIVE) goto skip_it; memset(rta_tb, 0, sizeof(rta_tb)); parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(m), h->nlmsg_len - NLMSG_LENGTH(sizeof(*m))); if (rta_tb[IFA_LOCAL] == NULL) rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS]; if (rta_tb[IFA_LOCAL] == NULL) goto skip_it; I = malloc(sizeof(struct ifaddrs)); if (!I) return; memset(I, 0, sizeof(*I)); I->ifa_ifindex = m->ifa_index; I->ifa_addr = (struct sockaddr*)&I->ifa_addrbuf; I->ifa_addr->sa_family = m->ifa_family; if (m->ifa_family == AF_INET) { struct sockaddr_in *sin = (void*)I->ifa_addr; memcpy(&sin->sin_addr, RTA_DATA(rta_tb[IFA_LOCAL]), 4); } else { struct sockaddr_in6 *sin = (void*)I->ifa_addr; memcpy(&sin->sin6_addr, RTA_DATA(rta_tb[IFA_LOCAL]), 16); if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr)) sin->sin6_scope_id = I->ifa_ifindex; } I->ifa_next = *ifa; *ifa = I; skip_it: h = NLMSG_NEXT(h, status); } if (msg.msg_flags & MSG_TRUNC) continue; } return; } static int getifaddrs(struct ifaddrs **ifa0) { struct { struct nlmsghdr nlh; struct rtgenmsg g; } req; struct sockaddr_nl nladdr; static __u32 seq; struct ifaddrs *i; int fd; fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (fd < 0) return -1; memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; req.nlh.nlmsg_len = sizeof(req); req.nlh.nlmsg_type = RTM_GETADDR; req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST; req.nlh.nlmsg_pid = 0; req.nlh.nlmsg_seq = ++seq; req.g.rtgen_family = AF_UNSPEC; if (sendto(fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr)) < 0) { close(fd); return -1; } *ifa0 = NULL; recvaddrs(fd, ifa0, seq); close(fd); fd = socket(AF_INET, SOCK_DGRAM, 0); for (i=*ifa0; i; i = i->ifa_next) { struct ifreq ifr; ifr.ifr_ifindex = i->ifa_ifindex; ioctl(fd, SIOCGIFNAME, (void*)&ifr); memcpy(i->ifa_name, ifr.ifr_name, 16); } close(fd); return 0; } static void freeifaddrs(struct ifaddrs *ifa0) { struct ifaddrs *i; while (ifa0) { i = ifa0; ifa0 = i->ifa_next; free(i); } } #endif #ifndef HAVE_GETIFADDRS static unsigned int if_maxindex() { struct if_nameindex *p, *p0; unsigned int max = 0; p0 = if_nameindex(); for (p = p0; p && p->if_index && p->if_name; p++) { if (max < p->if_index) max = p->if_index; } if_freenameindex(p0); return max; } #endif void clear_myaddr(db) struct myaddrs **db; { struct myaddrs *p; while (*db) { p = (*db)->next; delmyaddr(*db); *db = p; } } static struct myaddrs * find_myaddr(db, p) struct myaddrs *db; struct myaddrs *p; { struct myaddrs *q; char h1[NI_MAXHOST], h2[NI_MAXHOST]; if (getnameinfo(p->addr, sysdep_sa_len(p->addr), h1, sizeof(h1), NULL, 0, NI_NUMERICHOST | niflags) != 0) return NULL; for (q = db; q; q = q->next) { if (p->addr->sa_family != q->addr->sa_family) continue; if (getnameinfo(q->addr, sysdep_sa_len(q->addr), h2, sizeof(h2), NULL, 0, NI_NUMERICHOST | niflags) != 0) return NULL; if (strcmp(h1, h2) == 0) return q; } return NULL; } void grab_myaddrs() { #ifdef HAVE_GETIFADDRS struct myaddrs *p, *q, *old; struct ifaddrs *ifa0, *ifap; #ifdef INET6 struct sockaddr_in6 *sin6; #endif char addr1[NI_MAXHOST]; if (getifaddrs(&ifa0)) { plog(LLV_ERROR, LOCATION, NULL, "getifaddrs failed: %s\n", strerror(errno)); exit(1); /*NOTREACHED*/ } old = lcconf->myaddrs; for (ifap = ifa0; ifap; ifap = ifap->ifa_next) { if (! ifap->ifa_addr) continue; if (ifap->ifa_addr->sa_family != AF_INET #ifdef INET6 && ifap->ifa_addr->sa_family != AF_INET6 #endif ) continue; if (!suitable_ifaddr(ifap->ifa_name, ifap->ifa_addr)) { plog(LLV_ERROR, LOCATION, NULL, "unsuitable address: %s %s\n", ifap->ifa_name, saddrwop2str(ifap->ifa_addr)); continue; } p = newmyaddr(); if (p == NULL) { exit(1); /*NOTREACHED*/ } p->addr = dupsaddr(ifap->ifa_addr); if (p->addr == NULL) { exit(1); /*NOTREACHED*/ } #ifdef INET6 #ifdef __KAME__ if (ifap->ifa_addr->sa_family == AF_INET6) { sin6 = (struct sockaddr_in6 *)p->addr; if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) || IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr)) { sin6->sin6_scope_id = ntohs(*(u_int16_t *)&sin6->sin6_addr.s6_addr[2]); sin6->sin6_addr.s6_addr[2] = 0; sin6->sin6_addr.s6_addr[3] = 0; } } #else /* !__KAME__ */ if (ifap->ifa_addr->sa_family == AF_INET6) { sin6 = (struct sockaddr_in6 *)p->addr; if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) || IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr)) { sin6->sin6_scope_id = if_nametoindex(ifap->ifa_name); } } #endif #endif if (getnameinfo(p->addr, sysdep_sa_len(p->addr), addr1, sizeof(addr1), NULL, 0, NI_NUMERICHOST | niflags)) strlcpy(addr1, "(invalid)", sizeof(addr1)); plog(LLV_DEBUG, LOCATION, NULL, "my interface: %s (%s)\n", addr1, ifap->ifa_name); q = find_myaddr(old, p); #ifdef ANDROID_CHANGES if (q) { protectFromVpn(q->sock); } #endif if (q) p->sock = q->sock; else p->sock = -1; p->next = lcconf->myaddrs; lcconf->myaddrs = p; } freeifaddrs(ifa0); clear_myaddr(&old); #else /*!HAVE_GETIFADDRS*/ int s; unsigned int maxif; int len; struct ifreq *iflist; struct ifconf ifconf; struct ifreq *ifr, *ifr_end; struct myaddrs *p, *q, *old; #ifdef INET6 #ifdef __KAME__ struct sockaddr_in6 *sin6; #endif #endif char addr1[NI_MAXHOST]; maxif = if_maxindex() + 1; len = maxif * sizeof(struct sockaddr_storage) * 4; /* guess guess */ iflist = (struct ifreq *)racoon_malloc(len); if (!iflist) { plog(LLV_ERROR, LOCATION, NULL, "failed to allocate buffer\n"); exit(1); /*NOTREACHED*/ } if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { plog(LLV_ERROR, LOCATION, NULL, "socket(SOCK_DGRAM) failed: %s\n", strerror(errno)); exit(1); /*NOTREACHED*/ } #ifdef ANDROID_CHANGES protectFromVpn(s); #endif memset(&ifconf, 0, sizeof(ifconf)); ifconf.ifc_req = iflist; ifconf.ifc_len = len; if (ioctl(s, SIOCGIFCONF, &ifconf) < 0) { close(s); plog(LLV_ERROR, LOCATION, NULL, "ioctl(SIOCGIFCONF) failed: %s\n", strerror(errno)); exit(1); /*NOTREACHED*/ } close(s); old = lcconf->myaddrs; /* Look for this interface in the list */ ifr_end = (struct ifreq *) (ifconf.ifc_buf + ifconf.ifc_len); #define _IFREQ_LEN(p) \ (sizeof((p)->ifr_name) + sysdep_sa_len(&(p)->ifr_addr) > sizeof(struct ifreq) \ ? sizeof((p)->ifr_name) + sysdep_sa_len(&(p)->ifr_addr) : sizeof(struct ifreq)) for (ifr = ifconf.ifc_req; ifr < ifr_end; ifr = (struct ifreq *)((caddr_t)ifr + _IFREQ_LEN(ifr))) { switch (ifr->ifr_addr.sa_family) { case AF_INET: #ifdef INET6 case AF_INET6: #endif if (!suitable_ifaddr(ifr->ifr_name, &ifr->ifr_addr)) { plog(LLV_ERROR, LOCATION, NULL, "unsuitable address: %s %s\n", ifr->ifr_name, saddrwop2str(&ifr->ifr_addr)); continue; } p = newmyaddr(); if (p == NULL) { exit(1); /*NOTREACHED*/ } p->addr = dupsaddr(&ifr->ifr_addr); if (p->addr == NULL) { exit(1); /*NOTREACHED*/ } #ifdef INET6 #ifdef __KAME__ sin6 = (struct sockaddr_in6 *)p->addr; if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) || IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr)) { sin6->sin6_scope_id = ntohs(*(u_int16_t *)&sin6->sin6_addr.s6_addr[2]); sin6->sin6_addr.s6_addr[2] = 0; sin6->sin6_addr.s6_addr[3] = 0; } #endif #endif if (getnameinfo(p->addr, sysdep_sa_len(p->addr), addr1, sizeof(addr1), NULL, 0, NI_NUMERICHOST | niflags)) strlcpy(addr1, "(invalid)", sizeof(addr1)); plog(LLV_DEBUG, LOCATION, NULL, "my interface: %s (%s)\n", addr1, ifr->ifr_name); q = find_myaddr(old, p); #ifdef ANDROID_CHANGES if (q) { protectFromVpn(q->sock); } #endif if (q) p->sock = q->sock; else p->sock = -1; p->next = lcconf->myaddrs; lcconf->myaddrs = p; break; default: break; } } clear_myaddr(&old); racoon_free(iflist); #endif /*HAVE_GETIFADDRS*/ } /* * check the interface is suitable or not */ static int suitable_ifaddr(ifname, ifaddr) const char *ifname; const struct sockaddr *ifaddr; { #ifdef ENABLE_HYBRID /* Exclude any address we got through ISAKMP mode config */ if (exclude_cfg_addr(ifaddr) == 0) return 0; #endif switch(ifaddr->sa_family) { case AF_INET: return 1; #ifdef INET6 case AF_INET6: return suitable_ifaddr6(ifname, ifaddr); #endif default: return 0; } /*NOTREACHED*/ } #ifdef INET6 static int suitable_ifaddr6(ifname, ifaddr) const char *ifname; const struct sockaddr *ifaddr; { #ifndef __linux__ struct in6_ifreq ifr6; int s; #endif if (ifaddr->sa_family != AF_INET6) return 0; #ifndef __linux__ s = socket(PF_INET6, SOCK_DGRAM, 0); if (s == -1) { plog(LLV_ERROR, LOCATION, NULL, "socket(SOCK_DGRAM) failed:%s\n", strerror(errno)); return 0; } #ifdef ANDROID_CHANGES protectFromVpn(s); #endif memset(&ifr6, 0, sizeof(ifr6)); strncpy(ifr6.ifr_name, ifname, strlen(ifname)); ifr6.ifr_addr = *(const struct sockaddr_in6 *)ifaddr; if (ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) < 0) { plog(LLV_ERROR, LOCATION, NULL, "ioctl(SIOCGIFAFLAG_IN6) failed:%s\n", strerror(errno)); close(s); return 0; } close(s); if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DUPLICATED || ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DETACHED || ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_ANYCAST) return 0; #endif /* suitable */ return 1; } #endif int update_myaddrs() { #ifdef __linux__ char msg[BUFSIZ]; int len; struct nlmsghdr *h = (void*)msg; len = read(lcconf->rtsock, msg, sizeof(msg)); if (len < 0) return errno == ENOBUFS; if (len < sizeof(*h)) return 0; if (h->nlmsg_pid) /* not from kernel! */ return 0; if (h->nlmsg_type == RTM_NEWLINK) return 0; plog(LLV_DEBUG, LOCATION, NULL, "netlink signals update interface address list\n"); return 1; #else char msg[BUFSIZ]; int len; struct rt_msghdr *rtm; len = read(lcconf->rtsock, msg, sizeof(msg)); if (len < 0) { plog(LLV_ERROR, LOCATION, NULL, "read(PF_ROUTE) failed: %s\n", strerror(errno)); return 0; } rtm = (struct rt_msghdr *)msg; if (len < rtm->rtm_msglen) { plog(LLV_ERROR, LOCATION, NULL, "read(PF_ROUTE) short read\n"); return 0; } if (rtm->rtm_version != RTM_VERSION) { plog(LLV_ERROR, LOCATION, NULL, "routing socket version mismatch\n"); close(lcconf->rtsock); lcconf->rtsock = -1; return 0; } switch (rtm->rtm_type) { case RTM_NEWADDR: case RTM_DELADDR: case RTM_DELETE: case RTM_IFINFO: break; case RTM_MISS: /* ignore this message silently */ return 0; default: plog(LLV_DEBUG, LOCATION, NULL, "msg %d not interesting\n", rtm->rtm_type); return 0; } /* XXX more filters here? */ plog(LLV_DEBUG, LOCATION, NULL, "caught rtm:%d, need update interface address list\n", rtm->rtm_type); return 1; #endif /* __linux__ */ } /* * initialize default port for ISAKMP to send, if no "listen" * directive is specified in config file. * * DO NOT listen to wildcard addresses. if you receive packets to * wildcard address, you'll be in trouble (DoS attack possible by * broadcast storm). */ int autoconf_myaddrsport() { struct myaddrs *p; int n; plog(LLV_DEBUG, LOCATION, NULL, "configuring default isakmp port.\n"); #ifdef ENABLE_NATT if (natt_enabled_in_rmconf ()) { plog(LLV_NOTIFY, LOCATION, NULL, "NAT-T is enabled, autoconfiguring ports\n"); for (p = lcconf->myaddrs; p; p = p->next) { struct myaddrs *new; if (! p->udp_encap) { new = dupmyaddr(p); new->udp_encap = 1; } } } #endif for (p = lcconf->myaddrs, n = 0; p; p = p->next, n++) { set_port (p->addr, p->udp_encap ? lcconf->port_isakmp_natt : lcconf->port_isakmp); } plog(LLV_DEBUG, LOCATION, NULL, "%d addrs are configured successfully\n", n); return 0; } /* * get a port number to which racoon binded. */ u_short getmyaddrsport(local) struct sockaddr *local; { struct myaddrs *p, *bestmatch = NULL; u_short bestmatch_port = PORT_ISAKMP; /* get a relative port */ for (p = lcconf->myaddrs; p; p = p->next) { if (!p->addr) continue; if (cmpsaddrwop(local, p->addr)) continue; /* use first matching address regardless of port */ if (!bestmatch) { bestmatch = p; continue; } /* matching address with port PORT_ISAKMP */ if (extract_port(p->addr) == PORT_ISAKMP) { bestmatch = p; bestmatch_port = PORT_ISAKMP; } } return bestmatch_port; } struct myaddrs * newmyaddr() { struct myaddrs *new; new = racoon_calloc(1, sizeof(*new)); if (new == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to allocate buffer for myaddrs.\n"); return NULL; } new->next = NULL; new->addr = NULL; return new; } struct myaddrs * dupmyaddr(struct myaddrs *old) { struct myaddrs *new; new = racoon_calloc(1, sizeof(*new)); if (new == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to allocate buffer for myaddrs.\n"); return NULL; } /* Copy the whole structure and set the differences. */ memcpy (new, old, sizeof (*new)); new->addr = dupsaddr (old->addr); if (new->addr == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to allocate buffer for myaddrs.\n"); racoon_free(new); return NULL; } new->next = old->next; old->next = new; return new; } void insmyaddr(new, head) struct myaddrs *new; struct myaddrs **head; { new->next = *head; *head = new; } void delmyaddr(myaddr) struct myaddrs *myaddr; { if (myaddr->addr) racoon_free(myaddr->addr); racoon_free(myaddr); } int initmyaddr() { /* initialize routing socket */ lcconf->rtsock = socket(PF_ROUTE, SOCK_RAW, PF_UNSPEC); if (lcconf->rtsock < 0) { plog(LLV_ERROR, LOCATION, NULL, "socket(PF_ROUTE) failed: %s", strerror(errno)); return -1; } #ifdef __linux__ { struct sockaddr_nl nl; u_int addr_len; memset(&nl, 0, sizeof(nl)); nl.nl_family = AF_NETLINK; nl.nl_groups = RTMGRP_IPV4_IFADDR|RTMGRP_LINK|RTMGRP_IPV6_IFADDR; if (bind(lcconf->rtsock, (struct sockaddr*)&nl, sizeof(nl)) < 0) { plog(LLV_ERROR, LOCATION, NULL, "bind(PF_NETLINK) failed: %s\n", strerror(errno)); return -1; } addr_len = sizeof(nl); if (getsockname(lcconf->rtsock, (struct sockaddr*)&nl, &addr_len) < 0) { plog(LLV_ERROR, LOCATION, NULL, "getsockname(PF_NETLINK) failed: %s\n", strerror(errno)); return -1; } } #endif if (lcconf->myaddrs == NULL && lcconf->autograbaddr == 1) { grab_myaddrs(); if (autoconf_myaddrsport() < 0) return -1; } return 0; } /* select the socket to be sent */ /* should implement other method. */ int getsockmyaddr(my) struct sockaddr *my; { struct myaddrs *p, *lastresort = NULL; #if defined(INET6) && defined(__linux__) struct myaddrs *match_wo_scope_id = NULL; int check_wo_scope_id = (my->sa_family == AF_INET6) && IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)my)->sin6_addr); #endif for (p = lcconf->myaddrs; p; p = p->next) { if (p->addr == NULL) continue; if (my->sa_family == p->addr->sa_family) { lastresort = p; } else continue; if (sysdep_sa_len(my) == sysdep_sa_len(p->addr) && memcmp(my, p->addr, sysdep_sa_len(my)) == 0) { break; } #if defined(INET6) && defined(__linux__) if (check_wo_scope_id && IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)p->addr)->sin6_addr) && /* XXX: this depends on sin6_scope_id to be last * item in struct sockaddr_in6 */ memcmp(my, p->addr, sysdep_sa_len(my) - sizeof(uint32_t)) == 0) { match_wo_scope_id = p; } #endif } #if defined(INET6) && defined(__linux__) if (!p) p = match_wo_scope_id; #endif if (!p) p = lastresort; if (!p) { plog(LLV_ERROR, LOCATION, NULL, "no socket matches address family %d\n", my->sa_family); return -1; } return p->sock; }