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
78struct rtm_dispinfo {
79        u_char          *di_buf;
80        ssize_t          di_buflen;
81        ssize_t          di_len;
82};
83
84union 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};
91typedef union rtmunion rtmunion_t;
92
93struct Address;
94typedef struct Address Address;
95
96struct Address {
97        in_addr_t       address;
98        AVAHI_LLIST_FIELDS(Address, addresses);
99};
100
101static int rtm_dispatch(void);
102static int rtm_dispatch_newdeladdr(struct rtm_dispinfo *di);
103static int rtm_dispatch_ifannounce(struct rtm_dispinfo *di);
104static struct sockaddr *next_sa(struct sockaddr *sa);
105
106static int fd = -1;
107static int ifindex = -1;
108static AVAHI_LLIST_HEAD(Address, addresses) = NULL;
109
110int
111iface_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
127int
128iface_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
189int
190iface_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
209void
210iface_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 */
228static int
229rtm_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 */
310static int
311rtm_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
343static struct sockaddr *
344next_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 */
364static int
365rtm_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