]> git.meshlink.io Git - catta/blob - avahi-autoipd/iface-bsd.c
CVE-2009-0758: Reflector creates packet storm on legacy unicast traffic
[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, 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
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 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
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                         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
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 *
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
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 }