From aec83994fea80aec0965bc9ecc95fe0139be3160 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 16 Dec 2007 21:03:02 +0000 Subject: [PATCH] fix detection whether an interface has a routable address assigned on BSD. Patch from zml. Closes #166 git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1591 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe --- avahi-autoipd/iface-bsd.c | 564 ++++++++++++++++++++------------------ avahi-core/socket.c | 6 +- 2 files changed, 299 insertions(+), 271 deletions(-) diff --git a/avahi-autoipd/iface-bsd.c b/avahi-autoipd/iface-bsd.c index 23c02dd..6a1085c 100644 --- a/avahi-autoipd/iface-bsd.c +++ b/avahi-autoipd/iface-bsd.c @@ -3,17 +3,17 @@ /*** This file is part of avahi. - + avahi is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. - + avahi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - + You should have received a copy of the GNU Lesser General Public License along with avahi; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 @@ -51,42 +51,42 @@ #include "iface.h" #ifndef IN_LINKLOCAL -#define IN_LINKLOCAL(i) (((u_int32_t)(i) & (0xffff0000)) == (0xa9fe0000)) +#define IN_LINKLOCAL(i) (((u_int32_t)(i) & (0xffff0000)) == (0xa9fe0000)) #endif #ifndef elementsof -#define elementsof(array) (sizeof(array)/sizeof(array[0])) +#define elementsof(array) (sizeof(array)/sizeof(array[0])) #endif #ifndef so_set_nonblock #define so_set_nonblock(s, val) \ - do { \ - int __flags; \ - __flags = fcntl((s), F_GETFL); \ - if (__flags == -1) \ - break; \ - if (val != 0) \ - __flags |= O_NONBLOCK; \ - else \ - __flags &= ~O_NONBLOCK; \ - (void)fcntl((s), F_SETFL, __flags); \ - } while (0) + do { \ + int __flags; \ + __flags = fcntl((s), F_GETFL); \ + if (__flags == -1) \ + break; \ + if (val != 0) \ + __flags |= O_NONBLOCK; \ + else \ + __flags &= ~O_NONBLOCK; \ + (void)fcntl((s), F_SETFL, __flags); \ + } while (0) #endif #define MAX_RTMSG_SIZE 2048 struct rtm_dispinfo { - u_char *di_buf; - ssize_t di_buflen; - ssize_t di_len; + u_char *di_buf; + ssize_t di_buflen; + ssize_t di_len; }; union rtmunion { - struct rt_msghdr rtm; - struct if_msghdr ifm; - struct ifa_msghdr ifam; - struct ifma_msghdr ifmam; - struct if_announcemsghdr ifan; + struct rt_msghdr rtm; + struct if_msghdr ifm; + struct ifa_msghdr ifam; + struct ifma_msghdr ifmam; + struct if_announcemsghdr ifan; }; typedef union rtmunion rtmunion_t; @@ -94,8 +94,8 @@ struct Address; typedef struct Address Address; struct Address { - in_addr_t address; - AVAHI_LLIST_FIELDS(Address, addresses); + in_addr_t address; + AVAHI_LLIST_FIELDS(Address, addresses); }; static int rtm_dispatch(void); @@ -111,115 +111,115 @@ int iface_init(int idx) { - fd = socket(PF_ROUTE, SOCK_RAW, 0); - if (fd == -1) { - daemon_log(LOG_ERR, "socket(PF_ROUTE): %s", strerror(errno)); - return (-1); - } + fd = socket(PF_ROUTE, SOCK_RAW, AF_INET); + if (fd == -1) { + daemon_log(LOG_ERR, "socket(PF_ROUTE): %s", strerror(errno)); + return (-1); + } - so_set_nonblock(fd, 1); + so_set_nonblock(fd, 1); - ifindex = idx; + ifindex = idx; - return (fd); + return (fd); } int iface_get_initial_state(State *state) { - int mib[6]; - char *buf; - struct if_msghdr *ifm; - struct ifa_msghdr *ifam; - char *lim; - char *next; - struct sockaddr *sa; - size_t len; - int naddrs; - - assert(state != NULL); - assert(fd != -1); - - naddrs = 0; - - mib[0] = CTL_NET; - mib[1] = PF_ROUTE; - mib[2] = 0; - mib[3] = 0; - mib[4] = NET_RT_IFLIST; - mib[5] = ifindex; - - if (sysctl(mib, elementsof(mib), NULL, &len, NULL, 0) != 0) { - daemon_log(LOG_ERR, "sysctl(NET_RT_IFLIST): %s", - strerror(errno)); - return (-1); - } - - buf = malloc(len); - if (buf == NULL) { - daemon_log(LOG_ERR, "malloc(%d): %s", len, strerror(errno)); - return (-1); - } - - if (sysctl(mib, elementsof(mib), buf, &len, NULL, 0) != 0) { - daemon_log(LOG_ERR, "sysctl(NET_RT_IFLIST): %s", - strerror(errno)); - free(buf); - return (-1); - } - - lim = buf + len; - for (next = buf; next < lim; next += ifm->ifm_msglen) { - ifm = (struct if_msghdr *)next; - if (ifm->ifm_type == RTM_NEWADDR) { - ifam = (struct ifa_msghdr *)next; - sa = (struct sockaddr *)(ifam + 1); - if (sa->sa_family != AF_INET) - continue; - ++naddrs; - } - } - free(buf); - - *state = (naddrs > 0) ? STATE_SLEEPING : STATE_START; - - return (0); + int mib[6]; + char *buf; + struct if_msghdr *ifm; + struct ifa_msghdr *ifam; + char *lim; + char *next; + struct sockaddr *sa; + size_t len; + int naddrs; + + assert(state != NULL); + assert(fd != -1); + + naddrs = 0; + + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; + mib[3] = 0; + mib[4] = NET_RT_IFLIST; + mib[5] = ifindex; + + if (sysctl(mib, elementsof(mib), NULL, &len, NULL, 0) != 0) { + daemon_log(LOG_ERR, "sysctl(NET_RT_IFLIST): %s", + strerror(errno)); + return (-1); + } + + buf = malloc(len); + if (buf == NULL) { + daemon_log(LOG_ERR, "malloc(%d): %s", len, strerror(errno)); + return (-1); + } + + if (sysctl(mib, elementsof(mib), buf, &len, NULL, 0) != 0) { + daemon_log(LOG_ERR, "sysctl(NET_RT_IFLIST): %s", + strerror(errno)); + free(buf); + return (-1); + } + + lim = buf + len; + for (next = buf; next < lim; next += ifm->ifm_msglen) { + ifm = (struct if_msghdr *)next; + if (ifm->ifm_type == RTM_NEWADDR) { + ifam = (struct ifa_msghdr *)next; + sa = (struct sockaddr *)(ifam + 1); + if (sa->sa_family != AF_INET) + continue; + ++naddrs; + } + } + free(buf); + + *state = (naddrs > 0) ? STATE_SLEEPING : STATE_START; + + return (0); } int iface_process(Event *event) { - int b; + int routable; - assert(fd != -1); + assert(fd != -1); - b = !!addresses; + routable = !!addresses; - if (rtm_dispatch() == -1) - return (-1); + if (rtm_dispatch() == -1) + return (-1); - if (b && !addresses) - *event = EVENT_ROUTABLE_ADDR_UNCONFIGURED; - else if (!b && addresses) - *event = EVENT_ROUTABLE_ADDR_CONFIGURED; + if (routable && !addresses) + *event = EVENT_ROUTABLE_ADDR_UNCONFIGURED; + else if (!routable && addresses) + *event = EVENT_ROUTABLE_ADDR_CONFIGURED; - return (0); + return (0); } void iface_done(void) { - Address *a; + Address *a; - if (fd != -1) { - close(fd); - fd = -1; - } + if (fd != -1) { + close(fd); + fd = -1; + } - while ((a = addresses) != NULL) { - AVAHI_LLIST_REMOVE(Address, addresses, addresses, a); - avahi_free(a); - } + while ((a = addresses) != NULL) { + AVAHI_LLIST_REMOVE(Address, addresses, addresses, a); + avahi_free(a); + } } /* @@ -228,189 +228,213 @@ iface_done(void) static int rtm_dispatch(void) { - struct msghdr mh; - struct iovec iov[1]; - struct rt_msghdr *rtm; - struct rtm_dispinfo *di; - ssize_t len; - int retval; - - di = malloc(sizeof(*di)); - if (di == NULL) { - daemon_log(LOG_ERR, "malloc(%d): %s", sizeof(*di), - strerror(errno)); - return (-1); - } - di->di_buflen = MAX_RTMSG_SIZE; - di->di_buf = calloc(MAX_RTMSG_SIZE, 1); - if (di->di_buf == NULL) { - free(di); - daemon_log(LOG_ERR, "calloc(%d): %s", MAX_RTMSG_SIZE, - strerror(errno)); - return (-1); - } - - memset(&mh, 0, sizeof(mh)); - iov[0].iov_base = di->di_buf; - iov[0].iov_len = di->di_buflen; - mh.msg_iov = iov; - mh.msg_iovlen = 1; - - retval = 0; - for (;;) { - len = recvmsg(fd, &mh, MSG_DONTWAIT); - if (len == -1) { - if (errno == EWOULDBLOCK) - break; - else { - daemon_log(LOG_ERR, "recvmsg(): %s", - strerror(errno)); - retval = -1; - break; - } - } - - rtm = (void *)di->di_buf; - if (rtm->rtm_version != RTM_VERSION) { - daemon_log(LOG_ERR, - "unknown routing socket message (version %d)\n", - rtm->rtm_version); - /* this is non-fatal; just ignore it for now. */ - continue; - } - - switch (rtm->rtm_type) { - case RTM_NEWADDR: - case RTM_DELADDR: - retval = rtm_dispatch_newdeladdr(di); - break; - case RTM_IFANNOUNCE: - retval = rtm_dispatch_ifannounce(di); - break; - default: - break; - } - - /* - * If we got an error; assume our position on the call - * stack is enclosed by a level-triggered event loop, - * and signal the error condition. - */ - if (retval != 0) - break; - } - free(di->di_buf); - free(di); - - return (retval); + struct msghdr mh; + struct iovec iov[1]; + struct rt_msghdr *rtm; + struct rtm_dispinfo *di; + ssize_t len; + int retval; + + di = malloc(sizeof(*di)); + if (di == NULL) { + daemon_log(LOG_ERR, "malloc(%d): %s", sizeof(*di), + strerror(errno)); + return (-1); + } + di->di_buflen = MAX_RTMSG_SIZE; + di->di_buf = calloc(MAX_RTMSG_SIZE, 1); + if (di->di_buf == NULL) { + free(di); + daemon_log(LOG_ERR, "calloc(%d): %s", MAX_RTMSG_SIZE, + strerror(errno)); + return (-1); + } + + memset(&mh, 0, sizeof(mh)); + iov[0].iov_base = di->di_buf; + iov[0].iov_len = di->di_buflen; + mh.msg_iov = iov; + mh.msg_iovlen = 1; + + retval = 0; + for (;;) { + len = recvmsg(fd, &mh, MSG_DONTWAIT); + if (len == -1) { + if (errno == EWOULDBLOCK) + break; + else { + daemon_log(LOG_ERR, "recvmsg(): %s", + strerror(errno)); + retval = -1; + break; + } + } + + rtm = (void *)di->di_buf; + if (rtm->rtm_version != RTM_VERSION) { + daemon_log(LOG_ERR, + "unknown routing socket message (version %d)\n", + rtm->rtm_version); + /* this is non-fatal; just ignore it for now. */ + continue; + } + + switch (rtm->rtm_type) { + case RTM_NEWADDR: + case RTM_DELADDR: + retval = rtm_dispatch_newdeladdr(di); + break; + case RTM_IFANNOUNCE: + retval = rtm_dispatch_ifannounce(di); + break; + default: + daemon_log(LOG_DEBUG, "%s: rtm_type %d ignored", __func__, rtm->rtm_type); + break; + } + + /* + * If we got an error; assume our position on the call + * stack is enclosed by a level-triggered event loop, + * and signal the error condition. + */ + if (retval != 0) + break; + } + free(di->di_buf); + free(di); + + return (retval); } /* handle link coming or going away */ static int rtm_dispatch_ifannounce(struct rtm_dispinfo *di) { - rtmunion_t *rtm = (void *)di->di_buf; + rtmunion_t *rtm = (void *)di->di_buf; - assert(rtm->rtm.rtm_type == RTM_IFANNOUNCE); + assert(rtm->rtm.rtm_type == RTM_IFANNOUNCE); - switch (rtm->ifan.ifan_what) { - case IFAN_ARRIVAL: - if (rtm->ifan.ifan_index == ifindex) { - daemon_log(LOG_ERR, + daemon_log(LOG_DEBUG, "%s: IFANNOUNCE for ifindex %d", + __func__, rtm->ifan.ifan_index); + + switch (rtm->ifan.ifan_what) { + case IFAN_ARRIVAL: + if (rtm->ifan.ifan_index == ifindex) { + daemon_log(LOG_ERR, "RTM_IFANNOUNCE IFAN_ARRIVAL, for ifindex %d, which we already manage.", - ifindex); - return (-1); - } - break; - case IFAN_DEPARTURE: - if (rtm->ifan.ifan_index == ifindex) { - daemon_log(LOG_ERR, "Interface vanished."); - return (-1); - } - break; - default: - /* ignore */ - break; - } - - return (0); + ifindex); + return (-1); + } + break; + case IFAN_DEPARTURE: + if (rtm->ifan.ifan_index == ifindex) { + daemon_log(LOG_ERR, "Interface vanished."); + return (-1); + } + break; + default: + /* ignore */ + break; + } + + return (0); } static struct sockaddr * next_sa(struct sockaddr *sa) { - void *p; - size_t sa_size; + void *p; + size_t sa_size; + +#ifdef SA_SIZE + sa_size = SA_SIZE(sa); +#else + /* This is not foolproof, kernel may round. */ + sa_size = sa->sa_len; + if (sa_size < sizeof(u_long)) + sa_size = sizeof(u_long); +#endif - sa_size = sa->sa_len; - if (sa_size < sizeof(u_long)) - sa_size = sizeof(u_long); - p = ((char *)sa) + sa_size; + p = ((char *)sa) + sa_size; - return (struct sockaddr *)p; + return (struct sockaddr *)p; } /* handle address coming or going away */ static int rtm_dispatch_newdeladdr(struct rtm_dispinfo *di) { - Address *ap; - rtmunion_t *rtm; - struct sockaddr *sa; - struct sockaddr_in *sin; + Address *ap; + struct ifa_msghdr *ifam; + struct sockaddr *sa; + struct sockaddr_in *sin; + int link_local; /* macro to skip to next RTA; has side-effects */ -#define SKIPRTA(rtmsgp, rta, sa) \ - do { \ - if ((rtmsgp)->rtm_addrs & (rta)) \ - (sa) = next_sa((sa)); \ - } while (0) - - rtm = (void *)di->di_buf; - - assert(rtm->rtm.rtm_type == RTM_NEWADDR || - rtm->rtm.rtm_type == RTM_DELADDR); - - if (rtm->rtm.rtm_index != ifindex) - return (0); - - if (!(rtm->rtm.rtm_addrs & RTA_IFA)) { - daemon_log(LOG_ERR, "ifa msg has no RTA_IFA."); - return (0); - } - - /* skip over rtmsg padding correctly */ - sa = (struct sockaddr *)((&rtm->ifam) + 1); - SKIPRTA(&rtm->rtm, RTA_DST, sa); - SKIPRTA(&rtm->rtm, RTA_GATEWAY, sa); - SKIPRTA(&rtm->rtm, RTA_NETMASK, sa); - SKIPRTA(&rtm->rtm, RTA_GENMASK, sa); - SKIPRTA(&rtm->rtm, RTA_IFP, sa); - - /* - * sa now points to RTA_IFA sockaddr; we are only interested - * in updates for link-local addresses. - */ - if (sa->sa_family != AF_INET) - return (0); - sin = (struct sockaddr_in *)sa; - if (!IN_LINKLOCAL(ntohl(sin->sin_addr.s_addr))) - return (0); - - for (ap = addresses; ap; ap = ap->addresses_next) { - if (ap->address == sin->sin_addr.s_addr) - break; - } - if (rtm->rtm.rtm_type == RTM_DELADDR && ap != NULL) { - AVAHI_LLIST_REMOVE(Address, addresses, addresses, ap); - avahi_free(ap); - } - if (rtm->rtm.rtm_type == RTM_NEWADDR && ap == NULL) { - ap = avahi_new(Address, 1); - ap->address = sin->sin_addr.s_addr; - AVAHI_LLIST_PREPEND(Address, addresses, addresses, ap); - } - - return (0); +#define SKIPRTA(ifamsgp, rta, sa) \ + do { \ + if ((ifamsgp)->ifam_addrs & (rta)) \ + (sa) = next_sa((sa)); \ + } while (0) + + ifam = &((rtmunion_t *)di->di_buf)->ifam; + + assert(ifam->ifam_type == RTM_NEWADDR || + ifam->ifam_type == RTM_DELADDR); + + daemon_log(LOG_DEBUG, "%s: %s for iface %d (%s)", __func__, + ifam->ifam_type == RTM_NEWADDR ? "NEWADDR" : "DELADDR", + ifam->ifam_index, (ifam->ifam_index == ifindex) ? "ours" : "not ours"); + + if (ifam->ifam_index != ifindex) + return (0); + + if (!(ifam->ifam_addrs & RTA_IFA)) { + daemon_log(LOG_ERR, "ifa msg has no RTA_IFA."); + return (0); + } + + /* skip over rtmsg padding correctly */ + sa = (struct sockaddr *)(ifam + 1); + SKIPRTA(ifam, RTA_DST, sa); + SKIPRTA(ifam, RTA_GATEWAY, sa); + SKIPRTA(ifam, RTA_NETMASK, sa); + SKIPRTA(ifam, RTA_GENMASK, sa); + SKIPRTA(ifam, RTA_IFP, sa); + + /* + * sa now points to RTA_IFA sockaddr; we are only interested + * in updates for routable addresses. + */ + if (sa->sa_family != AF_INET) { + daemon_log(LOG_DEBUG, "%s: RTA_IFA family not AF_INET (=%d)", __func__, sa->sa_family); + return (0); + } + + sin = (struct sockaddr_in *)sa; + link_local = IN_LINKLOCAL(ntohl(sin->sin_addr.s_addr)); + + daemon_log(LOG_DEBUG, "%s: %s for %s (%s)", __func__, + ifam->ifam_type == RTM_NEWADDR ? "NEWADDR" : "DELADDR", + inet_ntoa(sin->sin_addr), link_local ? "link local" : "routable"); + + if (link_local) + return (0); + + for (ap = addresses; ap; ap = ap->addresses_next) { + if (ap->address == sin->sin_addr.s_addr) + break; + } + if (ifam->ifam_type == RTM_DELADDR && ap != NULL) { + AVAHI_LLIST_REMOVE(Address, addresses, addresses, ap); + avahi_free(ap); + } + if (ifam->ifam_type == RTM_NEWADDR && ap == NULL) { + ap = avahi_new(Address, 1); + ap->address = sin->sin_addr.s_addr; + AVAHI_LLIST_PREPEND(Address, addresses, addresses, ap); + } + + return (0); #undef SKIPRTA } diff --git a/avahi-core/socket.c b/avahi-core/socket.c index 265536d..4146d5a 100644 --- a/avahi-core/socket.c +++ b/avahi-core/socket.c @@ -451,7 +451,11 @@ static int sendmsg_loop(int fd, struct msghdr *msg, int flags) { break; if (errno != EAGAIN) { - avahi_log_debug("sendmsg() failed: %s", strerror(errno)); + char where[64]; + struct sockaddr_in *sin = msg->msg_name; + + inet_ntop(sin->sin_family, &sin->sin_addr, where, sizeof(where)); + avahi_log_debug("sendmsg() to %s failed: %s", where, strerror(errno)); return -1; } -- 2.39.5