iface-pfroute.c revision 3daec6014638cccfe205529f829bb6ef6e9383db
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/sysctl.h> 35 36#include <net/route.h> 37#include <net/if.h> 38#include <net/if_dl.h> 39#include <netinet/in.h> 40 41#include "log.h" 42#include "iface.h" 43#include "iface-pfroute.h" 44#include "util.h" 45 46static int bitcount (unsigned int n) 47{ 48 int count=0 ; 49 while (n) 50 { 51 count++ ; 52 n &= (n - 1) ; 53 } 54 return count ; 55} 56 57static void rtm_info(struct rt_msghdr *rtm, AvahiInterfaceMonitor *m) 58{ 59 AvahiHwInterface *hw; 60 struct if_msghdr *ifm = (struct if_msghdr *)rtm; 61 struct sockaddr_dl *sdl = (struct sockaddr_dl *)(ifm + 1); 62 63 if (sdl->sdl_family != AF_LINK) 64 return; 65 66 if (ifm->ifm_addrs == 0 && ifm->ifm_index > 0) { 67 if (!(hw = avahi_interface_monitor_get_hw_interface(m, (AvahiIfIndex) ifm->ifm_index))) 68 return; 69 avahi_hw_interface_free(hw, 0); 70 return; 71 } 72 73 if (!(hw = avahi_interface_monitor_get_hw_interface(m, ifm->ifm_index))) 74 if (!(hw = avahi_hw_interface_new(m, (AvahiIfIndex) ifm->ifm_index))) 75 return; /* OOM */ 76 77 hw->flags_ok = 78 (ifm->ifm_flags & IFF_UP) && 79 (!m->server->config.use_iff_running || (ifm->ifm_flags & IFF_RUNNING)) && 80 !(ifm->ifm_flags & IFF_LOOPBACK) && 81 (ifm->ifm_flags & IFF_MULTICAST) && 82 !(ifm->ifm_flags & IFF_POINTOPOINT); 83 84 avahi_free(hw->name); 85 hw->name = avahi_strndup(sdl->sdl_data, sdl->sdl_nlen); 86 87 hw->mtu = ifm->ifm_data.ifi_mtu; 88 89 hw->mac_address_size = sdl->sdl_alen; 90 if (hw->mac_address_size > AVAHI_MAX_MAC_ADDRESS) 91 hw->mac_address_size = AVAHI_MAX_MAC_ADDRESS; 92 93 memcpy(hw->mac_address, sdl->sdl_data + sdl->sdl_nlen, hw->mac_address_size); 94 95 avahi_log_debug("======\n name: %s\n index:%d\n mtu:%d\n mac:%s\n flags_ok:%d\n======", 96 hw->name, hw->index, 97 hw->mtu, 98 avahi_format_mac_address(hw->mac_address, hw->mac_address_size), 99 hw->flags_ok); 100 101 avahi_hw_interface_check_relevant(hw); 102 avahi_hw_interface_update_rrs(hw, 0); 103} 104 105#define ROUNDUP(a) \ 106 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 107#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) 108 109static void rtm_addr(struct rt_msghdr *rtm, AvahiInterfaceMonitor *m) 110{ 111 AvahiInterface *iface; 112 AvahiAddress raddr; 113 int raddr_valid = 0; 114 struct ifa_msghdr *ifam = (struct ifa_msghdr *) rtm; 115 char *cp = (char *)(ifam + 1); 116 int addrs = ifam->ifam_addrs; 117 int i; 118 int prefixlen = 0; 119 struct sockaddr *sa =NULL; 120 121 if(((struct sockaddr *)cp)->sa_family != AF_INET && ((struct sockaddr *)cp)->sa_family != AF_INET6) 122 return; 123 124 if (!(iface = avahi_interface_monitor_get_interface(m, (AvahiIfIndex) ifam->ifam_index, avahi_af_to_proto(((struct sockaddr *)cp)->sa_family)))) 125 return; 126 127 raddr.proto = avahi_af_to_proto(((struct sockaddr *)cp)->sa_family); 128 129 for(i = 0; addrs != 0 && i < RTAX_MAX; addrs &= ~(1<<i), i++) 130 { 131 if (!(addrs & 1<<i)) 132 continue; 133 sa = (struct sockaddr *)cp; 134 if (sa->sa_len == 0) 135 continue; 136 switch(sa->sa_family) { 137 case AF_INET: 138 switch (1<<i) { 139 case RTA_NETMASK: 140 prefixlen = bitcount(((struct sockaddr_in *)sa)->sin_addr.s_addr); 141 break; 142 case RTA_IFA: 143 memcpy(raddr.data.data, &((struct sockaddr_in *)sa)->sin_addr, sizeof(struct in_addr)); 144 raddr_valid = 1; 145 default: 146 break; 147 } 148 break; 149 case AF_INET6: 150 switch (1<<i) { 151 case RTA_NETMASK: 152 prefixlen = bitcount(((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr); 153 break; 154 case RTA_IFA: 155 memcpy(raddr.data.data, &((struct sockaddr_in6 *)sa)->sin6_addr, sizeof(struct in6_addr)); 156 raddr_valid = 1; 157 default: 158 break; 159 } 160 break; 161 default: 162 break; 163 } 164#ifdef SA_SIZE 165 cp += SA_SIZE(sa); 166#else 167 ADVANCE(cp, sa); 168#endif 169 } 170 171 if (!raddr_valid) 172 return; 173 174 if(rtm->rtm_type == RTM_NEWADDR) 175 { 176 AvahiInterfaceAddress *addriface; 177 if (!(addriface = avahi_interface_monitor_get_address(m, iface, &raddr))) 178 if (!(addriface = avahi_interface_address_new(m, iface, &raddr, prefixlen))) 179 return; /* OOM */ 180 /* FIXME */ 181 /* addriface->global_scope = ifaddrmsg->ifa_scope == RT_SCOPE_UNIVERSE || ifaddrmsg->ifa_scope == RT_SCOPE_SITE; */ 182 addriface->global_scope = 1; 183 } 184 else 185 { 186 AvahiInterfaceAddress *addriface; 187 assert(rtm->rtm_type == RTM_DELADDR); 188 if (!(addriface = avahi_interface_monitor_get_address(m, iface, &raddr))) 189 return; 190 avahi_interface_address_free(addriface); 191 } 192 193 avahi_interface_check_relevant(iface); 194 avahi_interface_update_rrs(iface, 0); 195} 196 197static void parse_rtmsg(struct rt_msghdr *rtm, int msglen, AvahiInterfaceMonitor *m) 198{ 199 assert(m); 200 assert(rtm); 201 202 if (rtm->rtm_version != RTM_VERSION) { 203 avahi_log_warn("routing message version %d not understood", 204 rtm->rtm_version); 205 return; 206 } 207 208 switch (rtm->rtm_type) { 209 case RTM_IFINFO: 210 rtm_info(rtm,m); 211 break; 212 case RTM_NEWADDR: 213 case RTM_DELADDR: 214 rtm_addr(rtm,m); 215 break; 216 default: 217 break; 218 } 219} 220 221static void socket_event(AvahiWatch *w, int fd, AvahiWatchEvent event,void *userdata) { 222 AvahiInterfaceMonitor *m = (AvahiInterfaceMonitor *)userdata; 223 AvahiPfRoute *nl = m->osdep.pfroute; 224 ssize_t bytes; 225 char msg[2048]; 226 227 assert(m); 228 assert(w); 229 assert(nl); 230 assert(fd == nl->fd); 231 232 do { 233 time_t now = time(NULL); 234 if((bytes = recv(nl->fd, msg, 2048, MSG_DONTWAIT)) < 0) { 235 if (errno == EAGAIN || errno == EINTR) 236 return; 237 avahi_log_error(__FILE__": recv() failed: %s", strerror(errno)); 238 return; 239 } 240 241 avahi_log_debug("socket_event: got message of size %d on %s", (int)bytes, ctime(&now)); 242 parse_rtmsg((struct rt_msghdr *)msg, bytes ,m); 243 } 244 while (bytes > 0); 245} 246 247int avahi_interface_monitor_init_osdep(AvahiInterfaceMonitor *m) { 248 int fd = -1; 249 m->osdep.pfroute = NULL; 250 251 assert(m); 252 253 if ((fd = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC)) < 0) { 254 avahi_log_error(__FILE__": socket(PF_ROUTE): %s", strerror(errno)); 255 goto fail; 256 } 257 258 if (!(m->osdep.pfroute = avahi_new(AvahiPfRoute , 1))) { 259 avahi_log_error(__FILE__": avahi_new() failed."); 260 goto fail; 261 } 262 m->osdep.pfroute->fd = fd; 263 264 if (!(m->osdep.pfroute->watch = m->server->poll_api->watch_new(m->server->poll_api, 265 m->osdep.pfroute->fd, 266 AVAHI_WATCH_IN, 267 socket_event, 268 m))) { 269 avahi_log_error(__FILE__": Failed to create watch."); 270 goto fail; 271 } 272 273 return 0; 274 275fail: 276 277 if (m->osdep.pfroute) { 278 if (m->osdep.pfroute->watch) 279 m->server->poll_api->watch_free(m->osdep.pfroute->watch); 280 281 if (fd >= 0) 282 close(fd); 283 284 m->osdep.pfroute = NULL; 285 } 286 287 return -1; 288} 289 290void avahi_interface_monitor_free_osdep(AvahiInterfaceMonitor *m) { 291 assert(m); 292 293 if (m->osdep.pfroute) { 294 if (m->osdep.pfroute->watch) 295 m->server->poll_api->watch_free(m->osdep.pfroute->watch); 296 297 if (m->osdep.pfroute->fd >= 0) 298 close(m->osdep.pfroute->fd); 299 300 avahi_free(m->osdep.pfroute); 301 m->osdep.pfroute = NULL; 302 } 303} 304 305void avahi_interface_monitor_sync(AvahiInterfaceMonitor *m) { 306 size_t needed; 307 int mib[6]; 308 char *buf, *lim, *next, count = 0; 309 struct rt_msghdr *rtm; 310 311 assert(m); 312 313 retry2: 314 mib[0] = CTL_NET; 315 mib[1] = PF_ROUTE; 316 mib[2] = 0; /* protocol */ 317 mib[3] = 0; /* wildcard address family */ 318 mib[4] = NET_RT_IFLIST; 319 mib[5] = 0; /* no flags */ 320 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) 321 return; 322 if ((buf = avahi_malloc(needed)) == NULL) 323 return; 324 if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) { 325 if (errno == ENOMEM && count++ < 10) { 326 sleep(1); 327 avahi_free(buf); 328 goto retry2; 329 } 330 } 331 lim = buf + needed; 332 for (next = buf; next < lim; next += rtm->rtm_msglen) { 333 rtm = (struct rt_msghdr *)next; 334 parse_rtmsg(rtm, rtm->rtm_msglen, m); 335 } 336 337 m->list_complete = 1; 338 avahi_interface_monitor_check_relevant(m); 339 avahi_interface_monitor_update_rrs(m, 0); 340 avahi_log_info("Network interface enumeration completed."); 341} 342