/***
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
#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;
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);
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);
+ }
}
/*
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
}