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