1 /* rcs tags go here */
2 /* Original author: Bruce M. Simpson <bms@FreeBSD.org> */
3 
4 /***
5   This file is part of avahi.
6 
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.
11 
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.
16 
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
20   USA.
21 ***/
22 
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 
27 #include <sys/param.h>
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <sys/sysctl.h>
31 
32 #include <net/if.h>
33 #include <net/route.h>
34 #include <netinet/in.h>
35 #include <arpa/inet.h>
36 
37 #include <stdarg.h>
38 #include <stdlib.h>
39 #include <stdio.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <string.h>
43 
44 #include <unistd.h>
45 
46 #include <libdaemon/dlog.h>
47 
48 #include <avahi-common/llist.h>
49 #include <avahi-common/malloc.h>
50 
51 #include "iface.h"
52 
53 #ifndef IN_LINKLOCAL
54 #define IN_LINKLOCAL(i) (((u_int32_t)(i) & (0xffff0000)) == (0xa9fe0000))
55 #endif
56 
57 #ifndef elementsof
58 #define elementsof(array)       (sizeof(array)/sizeof(array[0]))
59 #endif
60 
61 #ifndef so_set_nonblock
62 #define so_set_nonblock(s, val) \
63         do {                                            \
64                 int __flags;                            \
65                 __flags = fcntl((s), F_GETFL);          \
66                 if (__flags == -1)                      \
67                         break;                          \
68                 if (val != 0)                           \
69                         __flags |= O_NONBLOCK;          \
70                 else                                    \
71                         __flags &= ~O_NONBLOCK;         \
72                 (void)fcntl((s), F_SETFL, __flags);     \
73         } while (0)
74 #endif
75 
76 #define MAX_RTMSG_SIZE 2048
77 
78 struct rtm_dispinfo {
79         u_char          *di_buf;
80         ssize_t          di_buflen;
81         ssize_t          di_len;
82 };
83 
84 union rtmunion {
85         struct rt_msghdr                 rtm;
86         struct if_msghdr                 ifm;
87         struct ifa_msghdr                ifam;
88         struct ifma_msghdr               ifmam;
89         struct if_announcemsghdr         ifan;
90 };
91 typedef union rtmunion rtmunion_t;
92 
93 struct Address;
94 typedef struct Address Address;
95 
96 struct Address {
97         in_addr_t       address;
98         AVAHI_LLIST_FIELDS(Address, addresses);
99 };
100 
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);
105 
106 static int fd = -1;
107 static int ifindex = -1;
108 static AVAHI_LLIST_HEAD(Address, addresses) = NULL;
109 
110 int
iface_init(int idx)111 iface_init(int idx)
112 {
113 
114         fd = socket(PF_ROUTE, SOCK_RAW, AF_INET);
115         if (fd == -1) {
116                 daemon_log(LOG_ERR, "socket(PF_ROUTE): %s", strerror(errno));
117                 return (-1);
118         }
119 
120         so_set_nonblock(fd, 1);
121 
122         ifindex = idx;
123 
124         return (fd);
125 }
126 
127 int
iface_get_initial_state(State * state)128 iface_get_initial_state(State *state)
129 {
130         int                      mib[6];
131         char                    *buf;
132         struct if_msghdr        *ifm;
133         struct ifa_msghdr       *ifam;
134         char                    *lim;
135         char                    *next;
136         struct sockaddr         *sa;
137         size_t                   len;
138         int                      naddrs;
139 
140         assert(state != NULL);
141         assert(fd != -1);
142 
143         naddrs = 0;
144 
145         mib[0] = CTL_NET;
146         mib[1] = PF_ROUTE;
147         mib[2] = 0;
148         mib[3] = 0;
149         mib[4] = NET_RT_IFLIST;
150         mib[5] = ifindex;
151 
152         if (sysctl(mib, elementsof(mib), NULL, &len, NULL, 0) != 0) {
153                 daemon_log(LOG_ERR, "sysctl(NET_RT_IFLIST): %s",
154                     strerror(errno));
155                 return (-1);
156         }
157 
158         buf = malloc(len);
159         if (buf == NULL) {
160                 daemon_log(LOG_ERR, "malloc(%d): %s", len, strerror(errno));
161                 return (-1);
162         }
163 
164         if (sysctl(mib, elementsof(mib), buf, &len, NULL, 0) != 0) {
165                 daemon_log(LOG_ERR, "sysctl(NET_RT_IFLIST): %s",
166                     strerror(errno));
167                 free(buf);
168                 return (-1);
169         }
170 
171         lim = buf + len;
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)
178                                 continue;
179                         ++naddrs;
180                 }
181         }
182         free(buf);
183 
184         *state = (naddrs > 0) ? STATE_SLEEPING : STATE_START;
185 
186         return (0);
187 }
188 
189 int
iface_process(Event * event)190 iface_process(Event *event)
191 {
192         int routable;
193 
194         assert(fd != -1);
195 
196         routable = !!addresses;
197 
198         if (rtm_dispatch() == -1)
199                 return (-1);
200 
201         if (routable && !addresses)
202                 *event = EVENT_ROUTABLE_ADDR_UNCONFIGURED;
203         else if (!routable && addresses)
204                 *event = EVENT_ROUTABLE_ADDR_CONFIGURED;
205 
206         return (0);
207 }
208 
209 void
iface_done(void)210 iface_done(void)
211 {
212         Address *a;
213 
214         if (fd != -1) {
215                 close(fd);
216                 fd = -1;
217         }
218 
219         while ((a = addresses) != NULL) {
220                 AVAHI_LLIST_REMOVE(Address, addresses, addresses, a);
221                 avahi_free(a);
222         }
223 }
224 
225 /*
226  * Dispatch kernel routing socket messages.
227  */
228 static int
rtm_dispatch(void)229 rtm_dispatch(void)
230 {
231         struct msghdr mh;
232         struct iovec iov[1];
233         struct rt_msghdr *rtm;
234         struct rtm_dispinfo *di;
235         ssize_t len;
236         int retval;
237 
238         di = malloc(sizeof(*di));
239         if (di == NULL) {
240                 daemon_log(LOG_ERR, "malloc(%d): %s", sizeof(*di),
241                     strerror(errno));
242                 return (-1);
243         }
244         di->di_buflen = MAX_RTMSG_SIZE;
245         di->di_buf = calloc(MAX_RTMSG_SIZE, 1);
246         if (di->di_buf == NULL) {
247                 free(di);
248                 daemon_log(LOG_ERR, "calloc(%d): %s", MAX_RTMSG_SIZE,
249                     strerror(errno));
250                 return (-1);
251         }
252 
253         memset(&mh, 0, sizeof(mh));
254         iov[0].iov_base = di->di_buf;
255         iov[0].iov_len = di->di_buflen;
256         mh.msg_iov = iov;
257         mh.msg_iovlen = 1;
258 
259         retval = 0;
260         for (;;) {
261                 len = recvmsg(fd, &mh, MSG_DONTWAIT);
262                 if (len == -1) {
263                         if (errno == EWOULDBLOCK)
264                                 break;
265                         else {
266                                 daemon_log(LOG_ERR, "recvmsg(): %s",
267                                     strerror(errno));
268                                 retval = -1;
269                                 break;
270                         }
271                 }
272 
273                 rtm = (void *)di->di_buf;
274                 if (rtm->rtm_version != RTM_VERSION) {
275                         daemon_log(LOG_ERR,
276                             "unknown routing socket message (version %d)\n",
277                             rtm->rtm_version);
278                         /* this is non-fatal; just ignore it for now. */
279                         continue;
280                 }
281 
282                 switch (rtm->rtm_type) {
283                 case RTM_NEWADDR:
284                 case RTM_DELADDR:
285                         retval = rtm_dispatch_newdeladdr(di);
286                         break;
287                 case RTM_IFANNOUNCE:
288                         retval = rtm_dispatch_ifannounce(di);
289                         break;
290                 default:
291                         daemon_log(LOG_DEBUG, "%s: rtm_type %d ignored", __func__, rtm->rtm_type);
292                         break;
293                 }
294 
295                 /*
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.
299                  */
300                 if (retval != 0)
301                         break;
302         }
303         free(di->di_buf);
304         free(di);
305 
306         return (retval);
307 }
308 
309 /* handle link coming or going away */
310 static int
rtm_dispatch_ifannounce(struct rtm_dispinfo * di)311 rtm_dispatch_ifannounce(struct rtm_dispinfo *di)
312 {
313         rtmunion_t *rtm = (void *)di->di_buf;
314 
315         assert(rtm->rtm.rtm_type == RTM_IFANNOUNCE);
316 
317         daemon_log(LOG_DEBUG, "%s: IFANNOUNCE for ifindex %d",
318             __func__, rtm->ifan.ifan_index);
319 
320         switch (rtm->ifan.ifan_what) {
321         case IFAN_ARRIVAL:
322                 if (rtm->ifan.ifan_index == ifindex) {
323                         daemon_log(LOG_ERR,
324 "RTM_IFANNOUNCE IFAN_ARRIVAL, for ifindex %d, which we already manage.",
325                             ifindex);
326                         return (-1);
327                 }
328                 break;
329         case IFAN_DEPARTURE:
330                 if (rtm->ifan.ifan_index == ifindex) {
331                         daemon_log(LOG_ERR, "Interface vanished.");
332                         return (-1);
333                 }
334                 break;
335         default:
336                 /* ignore */
337                 break;
338         }
339 
340         return (0);
341 }
342 
343 static struct sockaddr *
next_sa(struct sockaddr * sa)344 next_sa(struct sockaddr *sa)
345 {
346         void            *p;
347         size_t           sa_size;
348 
349 #ifdef SA_SIZE
350         sa_size = SA_SIZE(sa);
351 #else
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);
356 #endif
357 
358         p = ((char *)sa) + sa_size;
359 
360         return (struct sockaddr *)p;
361 }
362 
363 /* handle address coming or going away */
364 static int
rtm_dispatch_newdeladdr(struct rtm_dispinfo * di)365 rtm_dispatch_newdeladdr(struct rtm_dispinfo *di)
366 {
367         Address                 *ap;
368         struct ifa_msghdr       *ifam;
369         struct sockaddr         *sa;
370         struct sockaddr_in      *sin;
371         int                     link_local;
372 
373 /* macro to skip to next RTA; has side-effects */
374 #define SKIPRTA(ifamsgp, rta, sa)                                       \
375         do {                                                            \
376                 if ((ifamsgp)->ifam_addrs & (rta))                      \
377                         (sa) = next_sa((sa));                           \
378         } while (0)
379 
380         ifam = &((rtmunion_t *)di->di_buf)->ifam;
381 
382         assert(ifam->ifam_type == RTM_NEWADDR ||
383                ifam->ifam_type == RTM_DELADDR);
384 
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");
388 
389         if (ifam->ifam_index != ifindex)
390                 return (0);
391 
392         if (!(ifam->ifam_addrs & RTA_IFA)) {
393                 daemon_log(LOG_ERR, "ifa msg has no RTA_IFA.");
394                 return (0);
395         }
396 
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);
404 
405         /*
406          * sa now points to RTA_IFA sockaddr; we are only interested
407          * in updates for routable addresses.
408          */
409         if (sa->sa_family != AF_INET) {
410                 daemon_log(LOG_DEBUG, "%s: RTA_IFA family not AF_INET (=%d)", __func__, sa->sa_family);
411                 return (0);
412         }
413 
414         sin = (struct sockaddr_in *)sa;
415         link_local = IN_LINKLOCAL(ntohl(sin->sin_addr.s_addr));
416 
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");
420 
421         if (link_local)
422                 return (0);
423 
424         for (ap = addresses; ap; ap = ap->addresses_next) {
425                 if (ap->address == sin->sin_addr.s_addr)
426                         break;
427         }
428         if (ifam->ifam_type == RTM_DELADDR && ap != NULL) {
429                 AVAHI_LLIST_REMOVE(Address, addresses, addresses, ap);
430                 avahi_free(ap);
431         }
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);
436         }
437 
438         return (0);
439 #undef SKIPRTA
440 }
441