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