+++ /dev/null
-/* rcs tags go here */
-/* Original author: Bruce M. Simpson <bms@FreeBSD.org> */
-
-/***
- 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 <config.h>
-#endif
-
-#include <sys/param.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/sysctl.h>
-
-#include <net/if.h>
-#include <net/route.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-#include <stdarg.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <string.h>
-
-#include <unistd.h>
-
-#include <libdaemon/dlog.h>
-
-#include <avahi-common/llist.h>
-#include <avahi-common/malloc.h>
-
-#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, AF_INET);
- 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 routable;
-
- assert(fd != -1);
-
- routable = !!addresses;
-
- if (rtm_dispatch() == -1)
- return (-1);
-
- if (routable && !addresses)
- *event = EVENT_ROUTABLE_ADDR_UNCONFIGURED;
- else if (!routable && 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:
- 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;
-
- assert(rtm->rtm.rtm_type == RTM_IFANNOUNCE);
-
- 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);
-}
-
-static struct sockaddr *
-next_sa(struct sockaddr *sa)
-{
- 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
-
- 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;
- 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(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
-}