]> git.meshlink.io Git - catta/blob - avahi-autoipd/iface-bsd.c
23c02ddf436b030721e713d3054b2483025f83db
[catta] / avahi-autoipd / iface-bsd.c
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
111 iface_init(int idx)
112 {
113
114         fd = socket(PF_ROUTE, SOCK_RAW, 0);
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
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
190 iface_process(Event *event)
191 {
192         int b;
193
194         assert(fd != -1);
195
196         b = !!addresses;
197
198         if (rtm_dispatch() == -1)
199                 return (-1);
200
201         if (b && !addresses)
202                 *event = EVENT_ROUTABLE_ADDR_UNCONFIGURED;
203         else if (!b && addresses)
204                 *event = EVENT_ROUTABLE_ADDR_CONFIGURED;
205
206         return (0);
207 }
208
209 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
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                         break;
292                 }
293
294                 /*
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.
298                  */
299                 if (retval != 0)
300                         break;
301         }
302         free(di->di_buf);
303         free(di);
304
305         return (retval);
306 }
307
308 /* handle link coming or going away */
309 static int
310 rtm_dispatch_ifannounce(struct rtm_dispinfo *di)
311 {
312         rtmunion_t *rtm = (void *)di->di_buf;
313
314         assert(rtm->rtm.rtm_type == RTM_IFANNOUNCE);
315
316         switch (rtm->ifan.ifan_what) {
317         case IFAN_ARRIVAL:
318                 if (rtm->ifan.ifan_index == ifindex) {
319                         daemon_log(LOG_ERR,
320 "RTM_IFANNOUNCE IFAN_ARRIVAL, for ifindex %d, which we already manage.",
321                             ifindex);
322                         return (-1);
323                 }
324                 break;
325         case IFAN_DEPARTURE:
326                 if (rtm->ifan.ifan_index == ifindex) {
327                         daemon_log(LOG_ERR, "Interface vanished.");
328                         return (-1);
329                 }
330                 break;
331         default:
332                 /* ignore */
333                 break;
334         }
335
336         return (0);
337 }
338
339 static struct sockaddr *
340 next_sa(struct sockaddr *sa)
341 {
342         void            *p;
343         size_t           sa_size;
344
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;
349
350         return (struct sockaddr *)p;
351 }
352
353 /* handle address coming or going away */
354 static int
355 rtm_dispatch_newdeladdr(struct rtm_dispinfo *di)
356 {
357         Address                 *ap;
358         rtmunion_t              *rtm;
359         struct sockaddr         *sa;
360         struct sockaddr_in      *sin;
361
362 /* macro to skip to next RTA; has side-effects */
363 #define SKIPRTA(rtmsgp, rta, sa)                                        \
364         do {                                                            \
365                 if ((rtmsgp)->rtm_addrs & (rta))                        \
366                         (sa) = next_sa((sa));                           \
367         } while (0)
368
369         rtm = (void *)di->di_buf;
370
371         assert(rtm->rtm.rtm_type == RTM_NEWADDR ||
372                rtm->rtm.rtm_type == RTM_DELADDR);
373
374         if (rtm->rtm.rtm_index != ifindex)
375                 return (0);
376
377         if (!(rtm->rtm.rtm_addrs & RTA_IFA)) {
378                 daemon_log(LOG_ERR, "ifa msg has no RTA_IFA.");
379                 return (0);
380         }
381
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);
389
390         /*
391          * sa now points to RTA_IFA sockaddr; we are only interested
392          * in updates for link-local addresses.
393          */
394         if (sa->sa_family != AF_INET)
395                 return (0);
396         sin = (struct sockaddr_in *)sa;
397         if (!IN_LINKLOCAL(ntohl(sin->sin_addr.s_addr)))
398                 return (0);
399
400         for (ap = addresses; ap; ap = ap->addresses_next) {
401                 if (ap->address == sin->sin_addr.s_addr)
402                         break;
403         }
404         if (rtm->rtm.rtm_type == RTM_DELADDR && ap != NULL) {
405                 AVAHI_LLIST_REMOVE(Address, addresses, addresses, ap);
406                 avahi_free(ap);
407         }
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);
412         }
413
414         return (0);
415 #undef SKIPRTA
416 }