Home | History | Annotate | Download | only in avahi-core
      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