2 /* Original author: Bruce M. Simpson <bms@FreeBSD.org> */
5 This file is part of avahi.
7 avahi is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as
9 published by the Free Software Foundation; either version 2.1 of the
10 License, or (at your option) any later version.
12 avahi is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
15 Public License for more details.
17 You should have received a copy of the GNU Lesser General Public
18 License along with avahi; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
27 #include <sys/param.h>
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <sys/sysctl.h>
33 #include <net/route.h>
34 #include <netinet/in.h>
35 #include <arpa/inet.h>
46 #include <libdaemon/dlog.h>
48 #include <avahi-common/llist.h>
49 #include <avahi-common/malloc.h>
54 #define IN_LINKLOCAL(i) (((u_int32_t)(i) & (0xffff0000)) == (0xa9fe0000))
58 #define elementsof(array) (sizeof(array)/sizeof(array[0]))
61 #ifndef so_set_nonblock
62 #define so_set_nonblock(s, val) \
65 __flags = fcntl((s), F_GETFL); \
69 __flags |= O_NONBLOCK; \
71 __flags &= ~O_NONBLOCK; \
72 (void)fcntl((s), F_SETFL, __flags); \
76 #define MAX_RTMSG_SIZE 2048
87 struct ifa_msghdr ifam;
88 struct ifma_msghdr ifmam;
89 struct if_announcemsghdr ifan;
91 typedef union rtmunion rtmunion_t;
94 typedef struct Address Address;
98 AVAHI_LLIST_FIELDS(Address, addresses);
101 static int rtm_dispatch(void);
102 static int rtm_dispatch_newdeladdr(struct rtm_dispinfo *di);
103 static int rtm_dispatch_ifannounce(struct rtm_dispinfo *di);
104 static struct sockaddr *next_sa(struct sockaddr *sa);
107 static int ifindex = -1;
108 static AVAHI_LLIST_HEAD(Address, addresses) = NULL;
114 fd = socket(PF_ROUTE, SOCK_RAW, 0);
116 daemon_log(LOG_ERR, "socket(PF_ROUTE): %s", strerror(errno));
120 so_set_nonblock(fd, 1);
128 iface_get_initial_state(State *state)
132 struct if_msghdr *ifm;
133 struct ifa_msghdr *ifam;
140 assert(state != NULL);
149 mib[4] = NET_RT_IFLIST;
152 if (sysctl(mib, elementsof(mib), NULL, &len, NULL, 0) != 0) {
153 daemon_log(LOG_ERR, "sysctl(NET_RT_IFLIST): %s",
160 daemon_log(LOG_ERR, "malloc(%d): %s", len, strerror(errno));
164 if (sysctl(mib, elementsof(mib), buf, &len, NULL, 0) != 0) {
165 daemon_log(LOG_ERR, "sysctl(NET_RT_IFLIST): %s",
172 for (next = buf; next < lim; next += ifm->ifm_msglen) {
173 ifm = (struct if_msghdr *)next;
174 if (ifm->ifm_type == RTM_NEWADDR) {
175 ifam = (struct ifa_msghdr *)next;
176 sa = (struct sockaddr *)(ifam + 1);
177 if (sa->sa_family != AF_INET)
184 *state = (naddrs > 0) ? STATE_SLEEPING : STATE_START;
190 iface_process(Event *event)
198 if (rtm_dispatch() == -1)
202 *event = EVENT_ROUTABLE_ADDR_UNCONFIGURED;
203 else if (!b && addresses)
204 *event = EVENT_ROUTABLE_ADDR_CONFIGURED;
219 while ((a = addresses) != NULL) {
220 AVAHI_LLIST_REMOVE(Address, addresses, addresses, a);
226 * Dispatch kernel routing socket messages.
233 struct rt_msghdr *rtm;
234 struct rtm_dispinfo *di;
238 di = malloc(sizeof(*di));
240 daemon_log(LOG_ERR, "malloc(%d): %s", sizeof(*di),
244 di->di_buflen = MAX_RTMSG_SIZE;
245 di->di_buf = calloc(MAX_RTMSG_SIZE, 1);
246 if (di->di_buf == NULL) {
248 daemon_log(LOG_ERR, "calloc(%d): %s", MAX_RTMSG_SIZE,
253 memset(&mh, 0, sizeof(mh));
254 iov[0].iov_base = di->di_buf;
255 iov[0].iov_len = di->di_buflen;
261 len = recvmsg(fd, &mh, MSG_DONTWAIT);
263 if (errno == EWOULDBLOCK)
266 daemon_log(LOG_ERR, "recvmsg(): %s",
273 rtm = (void *)di->di_buf;
274 if (rtm->rtm_version != RTM_VERSION) {
276 "unknown routing socket message (version %d)\n",
278 /* this is non-fatal; just ignore it for now. */
282 switch (rtm->rtm_type) {
285 retval = rtm_dispatch_newdeladdr(di);
288 retval = rtm_dispatch_ifannounce(di);
295 * If we got an error; assume our position on the call
296 * stack is enclosed by a level-triggered event loop,
297 * and signal the error condition.
308 /* handle link coming or going away */
310 rtm_dispatch_ifannounce(struct rtm_dispinfo *di)
312 rtmunion_t *rtm = (void *)di->di_buf;
314 assert(rtm->rtm.rtm_type == RTM_IFANNOUNCE);
316 switch (rtm->ifan.ifan_what) {
318 if (rtm->ifan.ifan_index == ifindex) {
320 "RTM_IFANNOUNCE IFAN_ARRIVAL, for ifindex %d, which we already manage.",
326 if (rtm->ifan.ifan_index == ifindex) {
327 daemon_log(LOG_ERR, "Interface vanished.");
339 static struct sockaddr *
340 next_sa(struct sockaddr *sa)
345 sa_size = sa->sa_len;
346 if (sa_size < sizeof(u_long))
347 sa_size = sizeof(u_long);
348 p = ((char *)sa) + sa_size;
350 return (struct sockaddr *)p;
353 /* handle address coming or going away */
355 rtm_dispatch_newdeladdr(struct rtm_dispinfo *di)
360 struct sockaddr_in *sin;
362 /* macro to skip to next RTA; has side-effects */
363 #define SKIPRTA(rtmsgp, rta, sa) \
365 if ((rtmsgp)->rtm_addrs & (rta)) \
366 (sa) = next_sa((sa)); \
369 rtm = (void *)di->di_buf;
371 assert(rtm->rtm.rtm_type == RTM_NEWADDR ||
372 rtm->rtm.rtm_type == RTM_DELADDR);
374 if (rtm->rtm.rtm_index != ifindex)
377 if (!(rtm->rtm.rtm_addrs & RTA_IFA)) {
378 daemon_log(LOG_ERR, "ifa msg has no RTA_IFA.");
382 /* skip over rtmsg padding correctly */
383 sa = (struct sockaddr *)((&rtm->ifam) + 1);
384 SKIPRTA(&rtm->rtm, RTA_DST, sa);
385 SKIPRTA(&rtm->rtm, RTA_GATEWAY, sa);
386 SKIPRTA(&rtm->rtm, RTA_NETMASK, sa);
387 SKIPRTA(&rtm->rtm, RTA_GENMASK, sa);
388 SKIPRTA(&rtm->rtm, RTA_IFP, sa);
391 * sa now points to RTA_IFA sockaddr; we are only interested
392 * in updates for link-local addresses.
394 if (sa->sa_family != AF_INET)
396 sin = (struct sockaddr_in *)sa;
397 if (!IN_LINKLOCAL(ntohl(sin->sin_addr.s_addr)))
400 for (ap = addresses; ap; ap = ap->addresses_next) {
401 if (ap->address == sin->sin_addr.s_addr)
404 if (rtm->rtm.rtm_type == RTM_DELADDR && ap != NULL) {
405 AVAHI_LLIST_REMOVE(Address, addresses, addresses, ap);
408 if (rtm->rtm.rtm_type == RTM_NEWADDR && ap == NULL) {
409 ap = avahi_new(Address, 1);
410 ap->address = sin->sin_addr.s_addr;
411 AVAHI_LLIST_PREPEND(Address, addresses, addresses, ap);