From f10b82b26456f44d03d0c28d93c5118b859d3891 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 12 Apr 2007 18:57:31 +0000 Subject: port avahi-autoipd to FreeBSD (original patch from Bruce M Simpson) git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1402 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe --- avahi-autoipd/iface-bsd.c | 416 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 416 insertions(+) create mode 100644 avahi-autoipd/iface-bsd.c (limited to 'avahi-autoipd/iface-bsd.c') diff --git a/avahi-autoipd/iface-bsd.c b/avahi-autoipd/iface-bsd.c new file mode 100644 index 0000000..23c02dd --- /dev/null +++ b/avahi-autoipd/iface-bsd.c @@ -0,0 +1,416 @@ +/* rcs tags go here */ +/* Original author: Bruce M. Simpson */ + +/*** + 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 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include "iface.h" + +#ifndef IN_LINKLOCAL +#define IN_LINKLOCAL(i) (((u_int32_t)(i) & (0xffff0000)) == (0xa9fe0000)) +#endif + +#ifndef elementsof +#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) +#endif + +#define MAX_RTMSG_SIZE 2048 + +struct rtm_dispinfo { + 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; +}; +typedef union rtmunion rtmunion_t; + +struct Address; +typedef struct Address Address; + +struct Address { + in_addr_t address; + AVAHI_LLIST_FIELDS(Address, addresses); +}; + +static int rtm_dispatch(void); +static int rtm_dispatch_newdeladdr(struct rtm_dispinfo *di); +static int rtm_dispatch_ifannounce(struct rtm_dispinfo *di); +static struct sockaddr *next_sa(struct sockaddr *sa); + +static int fd = -1; +static int ifindex = -1; +static AVAHI_LLIST_HEAD(Address, addresses) = NULL; + +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); + } + + so_set_nonblock(fd, 1); + + ifindex = idx; + + 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 +iface_process(Event *event) +{ + int b; + + assert(fd != -1); + + b = !!addresses; + + if (rtm_dispatch() == -1) + return (-1); + + if (b && !addresses) + *event = EVENT_ROUTABLE_ADDR_UNCONFIGURED; + else if (!b && addresses) + *event = EVENT_ROUTABLE_ADDR_CONFIGURED; + + return (0); +} + +void +iface_done(void) +{ + Address *a; + + if (fd != -1) { + close(fd); + fd = -1; + } + + while ((a = addresses) != NULL) { + AVAHI_LLIST_REMOVE(Address, addresses, addresses, a); + avahi_free(a); + } +} + +/* + * Dispatch kernel routing socket messages. + */ +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); +} + +/* handle link coming or going away */ +static int +rtm_dispatch_ifannounce(struct rtm_dispinfo *di) +{ + rtmunion_t *rtm = (void *)di->di_buf; + + 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, +"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); +} + +static struct sockaddr * +next_sa(struct sockaddr *sa) +{ + void *p; + size_t sa_size; + + sa_size = sa->sa_len; + if (sa_size < sizeof(u_long)) + sa_size = sizeof(u_long); + p = ((char *)sa) + sa_size; + + 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; + +/* 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); +#undef SKIPRTA +} -- cgit