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/avahi-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