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, AF_INET);
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)
196 routable = !!addresses;
198 if (rtm_dispatch() == -1)
201 if (routable && !addresses)
202 *event = EVENT_ROUTABLE_ADDR_UNCONFIGURED;
203 else if (!routable && 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);
291 daemon_log(LOG_DEBUG, "%s: rtm_type %d ignored", __func__, rtm->rtm_type);
296 * If we got an error; assume our position on the call
297 * stack is enclosed by a level-triggered event loop,
298 * and signal the error condition.
309 /* handle link coming or going away */
311 rtm_dispatch_ifannounce(struct rtm_dispinfo *di)
313 rtmunion_t *rtm = (void *)di->di_buf;
315 assert(rtm->rtm.rtm_type == RTM_IFANNOUNCE);
317 daemon_log(LOG_DEBUG, "%s: IFANNOUNCE for ifindex %d",
318 __func__, rtm->ifan.ifan_index);
320 switch (rtm->ifan.ifan_what) {
322 if (rtm->ifan.ifan_index == ifindex) {
324 "RTM_IFANNOUNCE IFAN_ARRIVAL, for ifindex %d, which we already manage.",
330 if (rtm->ifan.ifan_index == ifindex) {
331 daemon_log(LOG_ERR, "Interface vanished.");
343 static struct sockaddr *
344 next_sa(struct sockaddr *sa)
350 sa_size = SA_SIZE(sa);
352 /* This is not foolproof, kernel may round. */
353 sa_size = sa->sa_len;
354 if (sa_size < sizeof(u_long))
355 sa_size = sizeof(u_long);
358 p = ((char *)sa) + sa_size;
360 return (struct sockaddr *)p;
363 /* handle address coming or going away */
365 rtm_dispatch_newdeladdr(struct rtm_dispinfo *di)
368 struct ifa_msghdr *ifam;
370 struct sockaddr_in *sin;
373 /* macro to skip to next RTA; has side-effects */
374 #define SKIPRTA(ifamsgp, rta, sa) \
376 if ((ifamsgp)->ifam_addrs & (rta)) \
377 (sa) = next_sa((sa)); \
380 ifam = &((rtmunion_t *)di->di_buf)->ifam;
382 assert(ifam->ifam_type == RTM_NEWADDR ||
383 ifam->ifam_type == RTM_DELADDR);
385 daemon_log(LOG_DEBUG, "%s: %s for iface %d (%s)", __func__,
386 ifam->ifam_type == RTM_NEWADDR ? "NEWADDR" : "DELADDR",
387 ifam->ifam_index, (ifam->ifam_index == ifindex) ? "ours" : "not ours");
389 if (ifam->ifam_index != ifindex)
392 if (!(ifam->ifam_addrs & RTA_IFA)) {
393 daemon_log(LOG_ERR, "ifa msg has no RTA_IFA.");
397 /* skip over rtmsg padding correctly */
398 sa = (struct sockaddr *)(ifam + 1);
399 SKIPRTA(ifam, RTA_DST, sa);
400 SKIPRTA(ifam, RTA_GATEWAY, sa);
401 SKIPRTA(ifam, RTA_NETMASK, sa);
402 SKIPRTA(ifam, RTA_GENMASK, sa);
403 SKIPRTA(ifam, RTA_IFP, sa);
406 * sa now points to RTA_IFA sockaddr; we are only interested
407 * in updates for routable addresses.
409 if (sa->sa_family != AF_INET) {
410 daemon_log(LOG_DEBUG, "%s: RTA_IFA family not AF_INET (=%d)", __func__, sa->sa_family);
414 sin = (struct sockaddr_in *)sa;
415 link_local = IN_LINKLOCAL(ntohl(sin->sin_addr.s_addr));
417 daemon_log(LOG_DEBUG, "%s: %s for %s (%s)", __func__,
418 ifam->ifam_type == RTM_NEWADDR ? "NEWADDR" : "DELADDR",
419 inet_ntoa(sin->sin_addr), link_local ? "link local" : "routable");
424 for (ap = addresses; ap; ap = ap->addresses_next) {
425 if (ap->address == sin->sin_addr.s_addr)
428 if (ifam->ifam_type == RTM_DELADDR && ap != NULL) {
429 AVAHI_LLIST_REMOVE(Address, addresses, addresses, ap);
432 if (ifam->ifam_type == RTM_NEWADDR && ap == NULL) {
433 ap = avahi_new(Address, 1);
434 ap->address = sin->sin_addr.s_addr;
435 AVAHI_LLIST_PREPEND(Address, addresses, addresses, ap);