]> git.meshlink.io Git - catta/blob - avahi-core/iface-pfroute.c
Patch from Padraig O'Briain <Padraig.Obriain@sun.com> to build avahi on Solaris
[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 #ifdef HAVE_SYS_SYSCTL_H
36 #include <sys/sysctl.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   int addrs = ifam->ifam_addrs;
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   if(((struct sockaddr *)cp)->sa_family != AF_INET && ((struct sockaddr *)cp)->sa_family != AF_INET6)
137     return;
138
139   if (!(iface = avahi_interface_monitor_get_interface(m, (AvahiIfIndex) ifam->ifam_index, avahi_af_to_proto(((struct sockaddr *)cp)->sa_family))))
140     return;
141
142   raddr.proto = avahi_af_to_proto(((struct sockaddr *)cp)->sa_family);
143   
144   for(i = 0; addrs != 0 && i < RTAX_MAX; addrs &= ~(1<<i), i++)
145     {
146       if (!(addrs & 1<<i))
147         continue;
148       sa = (struct sockaddr *)cp;
149 #ifdef HAVE_SYS_SYSCTL_H
150       if (sa->sa_len == 0) 
151         continue;
152 #endif
153       switch(sa->sa_family) {
154       case AF_INET:
155         switch (1<<i) {
156         case RTA_NETMASK:
157           prefixlen = bitcount((unsigned int)((struct sockaddr_in *)sa)->sin_addr.s_addr);
158           break;
159         case RTA_IFA:
160           memcpy(raddr.data.data, &((struct sockaddr_in *)sa)->sin_addr,  sizeof(struct in_addr));
161           raddr_valid = 1;
162         default:
163           break;
164         }
165         break;
166       case AF_INET6:
167         switch (1<<i) {
168         case RTA_NETMASK:
169           prefixlen = bitcount((unsigned int)((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr);
170           break;
171         case RTA_IFA:
172           memcpy(raddr.data.data, &((struct sockaddr_in6 *)sa)->sin6_addr,  sizeof(struct in6_addr));
173           raddr_valid = 1;
174         default:
175           break;
176         }
177         break;
178       default:
179         break;
180       }
181 #ifdef SA_SIZE
182       cp += SA_SIZE(sa);
183 #else
184       ADVANCE(cp, sa);
185 #endif
186     }
187
188   if (!raddr_valid)
189     return;
190
191   if(rtm->rtm_type == RTM_NEWADDR)
192     {
193       AvahiInterfaceAddress *addriface;
194       if (!(addriface = avahi_interface_monitor_get_address(m, iface, &raddr)))
195         if (!(addriface = avahi_interface_address_new(m, iface, &raddr, prefixlen)))
196           return; /* OOM */
197       /*       FIXME */
198       /*       addriface->global_scope = ifaddrmsg->ifa_scope == RT_SCOPE_UNIVERSE || ifaddrmsg->ifa_scope == RT_SCOPE_SITE; */
199       addriface->global_scope = 1;
200     }
201   else
202     {
203       AvahiInterfaceAddress *addriface;
204       assert(rtm->rtm_type == RTM_DELADDR);
205       if (!(addriface = avahi_interface_monitor_get_address(m, iface, &raddr)))
206         return;
207       avahi_interface_address_free(addriface);
208     }
209   
210   avahi_interface_check_relevant(iface);
211   avahi_interface_update_rrs(iface, 0);
212 }
213
214 static void parse_rtmsg(struct rt_msghdr *rtm, AvahiInterfaceMonitor *m)
215 {
216   assert(m);
217   assert(rtm);
218   
219   if (rtm->rtm_version != RTM_VERSION) {
220     avahi_log_warn("routing message version %d not understood",
221                    rtm->rtm_version);
222     return;
223   }
224
225   switch (rtm->rtm_type) {
226   case RTM_IFINFO:
227     rtm_info(rtm,m);
228     break;
229   case RTM_NEWADDR:
230   case RTM_DELADDR:
231     rtm_addr(rtm,m);
232     break;
233   default:
234     break;
235   }
236 }
237
238 static void socket_event(AvahiWatch *w, int fd, AVAHI_GCC_UNUSED AvahiWatchEvent event,void *userdata) {
239   AvahiInterfaceMonitor *m = (AvahiInterfaceMonitor *)userdata;
240   AvahiPfRoute *nl = m->osdep.pfroute;
241     ssize_t bytes;
242     char msg[2048];
243
244     assert(m);
245     assert(w);
246     assert(nl);
247     assert(fd == nl->fd);
248
249     do {
250       if((bytes = recv(nl->fd, msg, 2048, MSG_DONTWAIT)) < 0) {
251         if (errno == EAGAIN || errno == EINTR)
252           return;
253         avahi_log_error(__FILE__": recv() failed: %s", strerror(errno));
254         return;
255       }
256       parse_rtmsg((struct rt_msghdr *)msg, m);
257     }
258     while (bytes > 0);
259 }
260
261 int avahi_interface_monitor_init_osdep(AvahiInterfaceMonitor *m) {
262     int fd = -1;
263     m->osdep.pfroute = NULL;
264
265     assert(m);
266
267     if ((fd = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC)) < 0) {
268         avahi_log_error(__FILE__": socket(PF_ROUTE): %s", strerror(errno));
269         goto fail;
270     }
271
272     if (!(m->osdep.pfroute = avahi_new(AvahiPfRoute , 1))) {
273         avahi_log_error(__FILE__": avahi_new() failed.");
274         goto fail;
275     }
276     m->osdep.pfroute->fd = fd;
277
278     if (!(m->osdep.pfroute->watch = m->server->poll_api->watch_new(m->server->poll_api, 
279                                                                    m->osdep.pfroute->fd, 
280                                                                    AVAHI_WATCH_IN, 
281                                                                    socket_event, 
282                                                                    m))) {
283       avahi_log_error(__FILE__": Failed to create watch.");
284       goto fail;
285     }
286     
287     return 0;
288
289 fail:
290
291     if (m->osdep.pfroute) {
292       if (m->osdep.pfroute->watch)
293         m->server->poll_api->watch_free(m->osdep.pfroute->watch);
294       
295       if (fd >= 0)
296         close(fd);
297       
298       m->osdep.pfroute = NULL;
299     }
300
301     return -1;
302 }
303
304 void avahi_interface_monitor_free_osdep(AvahiInterfaceMonitor *m) {
305     assert(m);
306
307     if (m->osdep.pfroute) {
308       if (m->osdep.pfroute->watch)
309         m->server->poll_api->watch_free(m->osdep.pfroute->watch);
310       
311       if (m->osdep.pfroute->fd >= 0)
312         close(m->osdep.pfroute->fd);
313
314       avahi_free(m->osdep.pfroute);
315       m->osdep.pfroute = NULL;
316     }
317 }
318
319 void avahi_interface_monitor_sync(AvahiInterfaceMonitor *m) {
320   size_t needed;
321   int mib[6];
322   char *buf, *lim, *next, count = 0;
323   struct rt_msghdr *rtm;
324
325   assert(m);
326   
327  retry2:
328 #ifdef HAVE_SYS_SYSCTL_H
329   mib[0] = CTL_NET;
330   mib[1] = PF_ROUTE;
331   mib[2] = 0;             /* protocol */
332   mib[3] = 0;             /* wildcard address family */
333   mib[4] = NET_RT_IFLIST;
334   mib[5] = 0;             /* no flags */
335   if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
336     {
337       avahi_log_error("sysctl failed: %s", strerror(errno));
338       avahi_log_error("route-sysctl-estimate");
339       return;
340     }
341 #endif
342   if ((buf = avahi_malloc(needed)) == NULL)
343     {
344       avahi_log_error("malloc failed in avahi_interface_monitor_sync");
345       return;
346     }
347 #ifdef HAVE_SYS_SYSCTL_H
348   if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
349     avahi_log_warn("sysctl failed: %s", strerror(errno));
350     if (errno == ENOMEM && count++ < 10) {
351       avahi_log_warn("Routing table grew, retrying");
352       sleep(1);
353       avahi_free(buf);
354       goto retry2;
355     }
356   }
357 #endif
358   lim = buf + needed;
359   for (next = buf; next < lim; next += rtm->rtm_msglen) {
360     rtm = (struct rt_msghdr *)next;
361     parse_rtmsg(rtm, m);
362   }
363   
364   m->list_complete = 1;
365   avahi_interface_monitor_check_relevant(m);
366   avahi_interface_monitor_update_rrs(m, 0);
367   avahi_log_info("Network interface enumeration completed.");
368 }