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