1/* $Id$ */ 2 3/*** 4 This file is part of avahi. 5 6 avahi is free software; you can redistribute it and/or modify it 7 under the terms of the GNU Lesser General Public License as 8 published by the Free Software Foundation; either version 2.1 of the 9 License, or (at your option) any later version. 10 11 avahi is distributed in the hope that it will be useful, but WITHOUT 12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 13 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General 14 Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public 17 License along with avahi; if not, write to the Free Software 18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 19 USA. 20***/ 21 22#ifdef HAVE_CONFIG_H 23#include <config.h> 24#endif 25 26#include <avahi-common/malloc.h> 27 28#include <string.h> 29#include <unistd.h> 30#include <errno.h> 31 32#include <sys/types.h> 33#include <sys/socket.h> 34#include <sys/param.h> 35#ifdef HAVE_SYS_SYSCTL_H 36#include <sys/sysctl.h> 37#else 38#include <sys/sockio.h> 39#endif 40 41#include <net/route.h> 42#include <net/if.h> 43#include <net/if_dl.h> 44#include <netinet/in.h> 45 46#include "log.h" 47#include "iface.h" 48#include "iface-pfroute.h" 49#include "util.h" 50 51static int bitcount (unsigned int n) 52{ 53 int count=0 ; 54 while (n) 55 { 56 count++ ; 57 n &= (n - 1) ; 58 } 59 return count ; 60} 61 62static void rtm_info(struct rt_msghdr *rtm, AvahiInterfaceMonitor *m) 63{ 64 AvahiHwInterface *hw; 65 struct if_msghdr *ifm = (struct if_msghdr *)rtm; 66 struct sockaddr_dl *sdl = (struct sockaddr_dl *)(ifm + 1); 67 68 if (sdl->sdl_family != AF_LINK) 69 return; 70 71 if (ifm->ifm_addrs == 0 && ifm->ifm_index > 0) { 72 if (!(hw = avahi_interface_monitor_get_hw_interface(m, (AvahiIfIndex) ifm->ifm_index))) 73 return; 74 avahi_hw_interface_free(hw, 0); 75 return; 76 } 77 78 if (!(hw = avahi_interface_monitor_get_hw_interface(m, ifm->ifm_index))) 79 if (!(hw = avahi_hw_interface_new(m, (AvahiIfIndex) ifm->ifm_index))) 80 return; /* OOM */ 81 82 hw->flags_ok = 83 (ifm->ifm_flags & IFF_UP) && 84 (!m->server->config.use_iff_running || (ifm->ifm_flags & IFF_RUNNING)) && 85 !(ifm->ifm_flags & IFF_LOOPBACK) && 86 (ifm->ifm_flags & IFF_MULTICAST) && 87 (m->server->config.allow_point_to_point || !(ifm->ifm_flags & IFF_POINTOPOINT)); 88 89 avahi_free(hw->name); 90 hw->name = avahi_strndup(sdl->sdl_data, sdl->sdl_nlen); 91 92 hw->mtu = ifm->ifm_data.ifi_mtu; 93 94 hw->mac_address_size = sdl->sdl_alen; 95 if (hw->mac_address_size > AVAHI_MAC_ADDRESS_MAX) 96 hw->mac_address_size = AVAHI_MAC_ADDRESS_MAX; 97 98 memcpy(hw->mac_address, sdl->sdl_data + sdl->sdl_nlen, hw->mac_address_size); 99 100/* { */ 101/* char mac[256]; */ 102/* avahi_log_debug("======\n name: %s\n index:%d\n mtu:%d\n mac:%s\n flags_ok:%d\n======", */ 103/* hw->name, hw->index, */ 104/* hw->mtu, */ 105/* avahi_format_mac_address(mac, sizeof(mac), hw->mac_address, hw->mac_address_size), */ 106/* hw->flags_ok); */ 107/* } */ 108 109 avahi_hw_interface_check_relevant(hw); 110 avahi_hw_interface_update_rrs(hw, 0); 111} 112 113#define ROUNDUP(a) \ 114 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 115#ifdef HAVE_SYS_SYSCTL_H 116#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) 117#else 118#define ADVANCE(x, n) (x += ROUNDUP(sizeof(struct sockaddr))) 119#endif 120 121static void rtm_addr(struct rt_msghdr *rtm, AvahiInterfaceMonitor *m) 122{ 123 AvahiInterface *iface; 124 AvahiAddress raddr; 125 int raddr_valid = 0; 126 struct ifa_msghdr *ifam = (struct ifa_msghdr *) rtm; 127 char *cp = (char *)(ifam + 1); 128 char *cp0; 129 int i; 130 int prefixlen = 0; 131 struct sockaddr *sa =NULL; 132 133#if defined(__NetBSD__) || defined(__OpenBSD__) 134 if(((struct sockaddr *)cp)->sa_family == AF_UNSPEC) 135 ((struct sockaddr *)cp)->sa_family = AF_INET; 136#endif 137 138 for (cp0 = cp, i = 0; i < RTAX_MAX; i++) { 139 if (!(ifam->ifam_addrs & (1<<i))) 140 continue; 141 sa = (struct sockaddr *)cp; 142 if (i == RTAX_IFA) 143 break; 144#ifdef SA_SIZE 145 cp += SA_SIZE(sa); 146#else 147 ADVANCE(cp, sa); 148#endif 149 } 150 151 if(sa->sa_family != AF_INET && sa->sa_family != AF_INET6) 152 return; 153 154 if (!(iface = avahi_interface_monitor_get_interface(m, (AvahiIfIndex) ifam->ifam_index, avahi_af_to_proto(sa->sa_family)))) 155 return; 156 157 raddr.proto = avahi_af_to_proto(sa->sa_family); 158 159 for(cp = cp0, i = 0; i < RTAX_MAX; i++) 160 { 161 if (!(ifam->ifam_addrs & (1<<i))) 162 continue; 163 sa = (struct sockaddr *)cp; 164#ifdef HAVE_SYS_SYSCTL_H 165 if (sa->sa_len == 0) 166 continue; 167#endif 168 switch(sa->sa_family) { 169 case AF_INET: 170 switch (1<<i) { 171 case RTA_NETMASK: 172 prefixlen = bitcount((unsigned int)((struct sockaddr_in *)sa)->sin_addr.s_addr); 173 break; 174 case RTA_IFA: 175 memcpy(raddr.data.data, &((struct sockaddr_in *)sa)->sin_addr, sizeof(struct in_addr)); 176 raddr_valid = 1; 177 default: 178 break; 179 } 180 break; 181 case AF_INET6: 182 switch (1<<i) { 183 case RTA_NETMASK: 184 prefixlen = bitcount((unsigned int)((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr); 185 break; 186 case RTA_IFA: 187 memcpy(raddr.data.data, &((struct sockaddr_in6 *)sa)->sin6_addr, sizeof(struct in6_addr)); 188#ifdef __KAME__ 189 if (IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)raddr.data.data)) 190 { 191 ((struct in6_addr *)raddr.data.data)->s6_addr[2] = 0; 192 ((struct in6_addr *)raddr.data.data)->s6_addr[3] = 0; 193 } 194#endif 195 raddr_valid = 1; 196 default: 197 break; 198 } 199 break; 200 default: 201 break; 202 } 203#ifdef SA_SIZE 204 cp += SA_SIZE(sa); 205#else 206 ADVANCE(cp, sa); 207#endif 208 } 209 210 if (!raddr_valid) 211 return; 212 213 if(rtm->rtm_type == RTM_NEWADDR) 214 { 215 AvahiInterfaceAddress *addriface; 216 if (!(addriface = avahi_interface_monitor_get_address(m, iface, &raddr))) 217 if (!(addriface = avahi_interface_address_new(m, iface, &raddr, prefixlen))) 218 return; /* OOM */ 219 if (raddr.proto == AVAHI_PROTO_INET6) 220 { 221 addriface->global_scope = !(IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)raddr.data.data) || IN6_IS_ADDR_MULTICAST((struct in6_addr *)raddr.data.data)); 222 } 223 else 224 addriface->global_scope = 1; 225 } 226 else 227 { 228 AvahiInterfaceAddress *addriface; 229 assert(rtm->rtm_type == RTM_DELADDR); 230 if (!(addriface = avahi_interface_monitor_get_address(m, iface, &raddr))) 231 return; 232 avahi_interface_address_free(addriface); 233 } 234 235 avahi_interface_check_relevant(iface); 236 avahi_interface_update_rrs(iface, 0); 237} 238 239static void parse_rtmsg(struct rt_msghdr *rtm, AvahiInterfaceMonitor *m) 240{ 241 assert(m); 242 assert(rtm); 243 244 if (rtm->rtm_version != RTM_VERSION) { 245 avahi_log_warn("routing message version %d not understood", 246 rtm->rtm_version); 247 return; 248 } 249 250 switch (rtm->rtm_type) { 251 case RTM_IFINFO: 252 rtm_info(rtm,m); 253 break; 254 case RTM_NEWADDR: 255 case RTM_DELADDR: 256 rtm_addr(rtm,m); 257 break; 258 default: 259 break; 260 } 261} 262 263static void socket_event(AvahiWatch *w, int fd, AVAHI_GCC_UNUSED AvahiWatchEvent event,void *userdata) { 264 AvahiInterfaceMonitor *m = (AvahiInterfaceMonitor *)userdata; 265 AvahiPfRoute *nl = m->osdep.pfroute; 266 ssize_t bytes; 267 char msg[2048]; 268 269 assert(m); 270 assert(w); 271 assert(nl); 272 assert(fd == nl->fd); 273 274 do { 275 if((bytes = recv(nl->fd, msg, 2048, MSG_DONTWAIT)) < 0) { 276 if (errno == EAGAIN || errno == EINTR) 277 return; 278 avahi_log_error(__FILE__": recv() failed: %s", strerror(errno)); 279 return; 280 } 281 parse_rtmsg((struct rt_msghdr *)msg, m); 282 } 283 while (bytes > 0); 284} 285 286int avahi_interface_monitor_init_osdep(AvahiInterfaceMonitor *m) { 287 int fd = -1; 288 289 assert(m); 290 291 m->osdep.pfroute = NULL; 292 293 if ((fd = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC)) < 0) { 294 avahi_log_error(__FILE__": socket(PF_ROUTE): %s", strerror(errno)); 295 goto fail; 296 } 297 298 if (!(m->osdep.pfroute = avahi_new(AvahiPfRoute , 1))) { 299 avahi_log_error(__FILE__": avahi_new() failed."); 300 goto fail; 301 } 302 m->osdep.pfroute->fd = fd; 303 304 if (!(m->osdep.pfroute->watch = m->server->poll_api->watch_new(m->server->poll_api, 305 m->osdep.pfroute->fd, 306 AVAHI_WATCH_IN, 307 socket_event, 308 m))) { 309 avahi_log_error(__FILE__": Failed to create watch."); 310 goto fail; 311 } 312 313 return 0; 314 315fail: 316 317 if (m->osdep.pfroute) { 318 if (m->osdep.pfroute->watch) 319 m->server->poll_api->watch_free(m->osdep.pfroute->watch); 320 321 if (fd >= 0) 322 close(fd); 323 324 m->osdep.pfroute = NULL; 325 } 326 327 return -1; 328} 329 330void avahi_interface_monitor_free_osdep(AvahiInterfaceMonitor *m) { 331 assert(m); 332 333 if (m->osdep.pfroute) { 334 if (m->osdep.pfroute->watch) 335 m->server->poll_api->watch_free(m->osdep.pfroute->watch); 336 337 if (m->osdep.pfroute->fd >= 0) 338 close(m->osdep.pfroute->fd); 339 340 avahi_free(m->osdep.pfroute); 341 m->osdep.pfroute = NULL; 342 } 343} 344 345#if defined (SIOCGLIFNUM) && defined(HAVE_STRUCT_LIFCONF) /* Solaris 8 and later; Sol 7? */ 346/* 347 * I got this function from GNU zsbra 348 */ 349static int ip6_masklen (struct in6_addr netmask) { 350 int len = 0; 351 unsigned char val; 352 unsigned char *pnt; 353 354 pnt = (unsigned char *) & netmask; 355 356 while ((*pnt == 0xff) && len < 128) { 357 len += 8; 358 pnt++; 359 } 360 361 if (len < 128) { 362 val = *pnt; 363 while (val) { 364 len++; 365 val <<= 1; 366 } 367 } 368 return len; 369} 370 371static void if_add_interface(struct lifreq *lifreq, AvahiInterfaceMonitor *m, int fd, int count) 372{ 373 AvahiHwInterface *hw; 374 AvahiAddress addr; 375 struct lifreq lifrcopy; 376 unsigned int index; 377 int flags; 378 int mtu; 379 int prefixlen; 380 AvahiInterfaceAddress *addriface; 381 AvahiInterface *iface; 382 struct sockaddr_in mask; 383 struct sockaddr_in6 mask6; 384 char caddr[AVAHI_ADDRESS_STR_MAX]; 385 386 lifrcopy = *lifreq; 387 388 if (ioctl(fd, SIOCGLIFFLAGS, &lifrcopy) < 0) { 389 avahi_log_error(__FILE__": ioctl(SIOCGLIFFLAGS) %s", strerror(errno)); 390 return; 391 } 392 flags = lifrcopy.lifr_flags; 393 394 if (ioctl(fd, SIOCGLIFMTU, &lifrcopy) < 0) { 395 avahi_log_error(__FILE__": ioctl(SIOCGLIFMTU) %s", strerror(errno)); 396 return; 397 } 398 mtu = lifrcopy.lifr_metric; 399 400 if (ioctl(fd, SIOCGLIFADDR, &lifrcopy) < 0) { 401 avahi_log_error(__FILE__": ioctl(SIOCGLIFADDR) %s", strerror(errno)); 402 return; 403 } 404 addr.proto = avahi_af_to_proto(lifreq->lifr_addr.ss_family); 405 if (ioctl(fd, SIOCGLIFNETMASK, &lifrcopy) < 0) { 406 avahi_log_error(__FILE__": ioctl(SIOCGLIFNETMASK) %s", strerror(errno)); 407 return; 408 } 409 switch (lifreq->lifr_addr.ss_family) { 410 case AF_INET: 411 memcpy(addr.data.data, &((struct sockaddr_in *)&lifreq->lifr_addr)->sin_addr, sizeof(struct in_addr)); 412 memcpy(&mask, &((struct sockaddr_in *)&lifrcopy.lifr_addr)->sin_addr, sizeof(struct in_addr)); 413 prefixlen = bitcount((unsigned int) mask.sin_addr.s_addr); 414 break; 415 case AF_INET6: 416 memcpy(addr.data.data, &((struct sockaddr_in6 *)&lifreq->lifr_addr)->sin6_addr, sizeof(struct in6_addr)); 417 memcpy(&mask6, &((struct sockaddr_in6 *)&lifrcopy.lifr_addr)->sin6_addr, sizeof(struct in6_addr)); 418 prefixlen = lifrcopy.lifr_addrlen; 419 break; 420 default: 421 break; 422 } 423 index = if_nametoindex(lifreq->lifr_name); 424 425 if (!(hw = avahi_interface_monitor_get_hw_interface(m, (AvahiIfIndex) index))) { 426 if (!(hw = avahi_hw_interface_new(m, (AvahiIfIndex) index))) 427 return; /* OOM */ 428 429 hw->flags_ok = 430 (flags & IFF_UP) && 431 (!m->server->config.use_iff_running || (flags & IFF_RUNNING)) && 432 !(flags & IFF_LOOPBACK) && 433 (flags & IFF_MULTICAST) && 434 (m->server->config.allow_point_to_point || !(flags & IFF_POINTOPOINT)); 435 hw->name = avahi_strdup(lifreq->lifr_name); 436 hw->mtu = mtu; 437 /* TODO get mac address */ 438 } 439 440 if (!(iface = avahi_interface_monitor_get_interface(m, (AvahiIfIndex)index, addr.proto))) 441 return; 442 443 if (!(addriface = avahi_interface_monitor_get_address(m, iface, &addr))) 444 if (!(addriface = avahi_interface_address_new(m, iface, &addr, prefixlen))) 445 return; /* OOM */ 446 447 addriface->global_scope = 1; 448 449 avahi_hw_interface_check_relevant(hw); 450 avahi_hw_interface_update_rrs(hw, 0); 451} 452#endif 453 454void avahi_interface_monitor_sync(AvahiInterfaceMonitor *m) { 455#ifndef HAVE_STRUCT_LIFCONF 456 size_t needed; 457 int mib[6]; 458 char *buf, *lim, *next, count = 0; 459 struct rt_msghdr *rtm; 460 461 assert(m); 462 463 retry2: 464 mib[0] = CTL_NET; 465 mib[1] = PF_ROUTE; 466 mib[2] = 0; /* protocol */ 467 mib[3] = 0; /* wildcard address family */ 468 mib[4] = NET_RT_IFLIST; 469 mib[5] = 0; /* no flags */ 470 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) 471 { 472 avahi_log_error("sysctl failed: %s", strerror(errno)); 473 avahi_log_error("route-sysctl-estimate"); 474 return; 475 } 476 if ((buf = avahi_malloc(needed)) == NULL) 477 { 478 avahi_log_error("malloc failed in avahi_interface_monitor_sync"); 479 return; 480 } 481 if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) { 482 avahi_log_warn("sysctl failed: %s", strerror(errno)); 483 if (errno == ENOMEM && count++ < 10) { 484 avahi_log_warn("Routing table grew, retrying"); 485 sleep(1); 486 avahi_free(buf); 487 goto retry2; 488 } 489 } 490 lim = buf + needed; 491 for (next = buf; next < lim; next += rtm->rtm_msglen) { 492 rtm = (struct rt_msghdr *)next; 493 parse_rtmsg(rtm, m); 494 } 495 496 m->list_complete = 1; 497 avahi_interface_monitor_check_relevant(m); 498 avahi_interface_monitor_update_rrs(m, 0); 499 avahi_log_info("Network interface enumeration completed."); 500#elif defined (SIOCGLIFNUM) && defined(HAVE_STRUCT_LIFCONF) /* Solaris 8 and later; Sol 7? */ 501 int sockfd; 502 int ret; 503 int n; 504 struct lifnum lifn; 505 struct lifconf lifc; 506 struct lifreq *lifreq; 507 508 if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 509 avahi_log_error(__FILE__": socket(PFROUTE): %s", strerror(errno)); 510 return; 511 } 512 lifc.lifc_buf = NULL; 513 lifn.lifn_family = AF_UNSPEC; 514 lifn.lifn_flags = 0; 515 if (ioctl(sockfd, SIOCGLIFNUM, &lifn) < 0) { 516 avahi_log_error(__FILE__": ioctl(SIOCGLIFNUM): %s", strerror(errno)); 517 goto end; 518 } 519 lifc.lifc_len = lifn.lifn_count * sizeof (struct lifreq); 520 if ((lifc.lifc_buf = avahi_malloc(lifc.lifc_len)) == NULL) { 521 avahi_log_error("malloc failed in avahi_interface_monitor_sync"); 522 goto end; 523 } 524 lifc.lifc_family = NULL; 525 lifc.lifc_flags = 0; 526 if(ioctl(sockfd, SIOCGLIFCONF, &lifc) < 0) { 527 avahi_log_error(__FILE__": ioctl(SIOCGLIFCONF): %s", strerror(errno)); 528 goto end; 529 } 530 lifreq = lifc.lifc_req; 531 532 for (n = 0; n < lifc.lifc_len; n += sizeof(struct lifreq)) { 533 if_add_interface(lifreq, m, sockfd, lifn.lifn_count); 534 lifreq++; 535 } 536 m->list_complete = 1; 537 avahi_interface_monitor_check_relevant(m); 538 avahi_interface_monitor_update_rrs(m, 0); 539end: 540 close(sockfd); 541 avahi_free(lifc.lifc_buf); 542 543 avahi_log_info("Network interface enumeration completed."); 544#endif 545} 546