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