]> git.meshlink.io Git - catta/blob - avahi-core/iface-linux.c
slightly more comments
[catta] / avahi-core / iface-linux.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 <string.h>
27 #include <net/if.h>
28 #include <errno.h>
29 #include <string.h>
30
31 #include <avahi-common/malloc.h>
32
33 #include "log.h"
34 #include "iface.h"
35 #include "iface-linux.h"
36
37 static int netlink_list_items(AvahiNetlink *nl, uint16_t type, unsigned *ret_seq) {
38     struct nlmsghdr *n;
39     struct rtgenmsg *gen;
40     uint8_t req[1024];
41
42     /* Issue a wild dump NETLINK request */
43     
44     memset(&req, 0, sizeof(req));
45     n = (struct nlmsghdr*) req;
46     n->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
47     n->nlmsg_type = type;
48     n->nlmsg_flags = NLM_F_ROOT|NLM_F_REQUEST;
49     n->nlmsg_pid = 0;
50
51     gen = NLMSG_DATA(n);
52     memset(gen, 0, sizeof(struct rtgenmsg));
53     gen->rtgen_family = AF_UNSPEC;
54
55     return avahi_netlink_send(nl, n, ret_seq);
56 }
57
58 static void netlink_callback(AvahiNetlink *nl, struct nlmsghdr *n, void* userdata) {
59     AvahiInterfaceMonitor *m = userdata;
60
61     /* This routine is called for every RTNETLINK response packet */
62     
63     assert(m);
64     assert(n);
65     assert(m->osdep.netlink == nl);
66
67     if (n->nlmsg_type == RTM_NEWLINK) {
68
69         /* A new interface appeared or an existing one has been modified */
70         
71         struct ifinfomsg *ifinfomsg = NLMSG_DATA(n);
72         AvahiHwInterface *hw;
73         struct rtattr *a = NULL;
74         size_t l;
75
76         /* A (superfluous?) sanity check */
77         if (ifinfomsg->ifi_family != AF_UNSPEC)
78             return;
79
80         /* Check whether there already is an AvahiHwInterface object
81          * for this link, so that we can update its data. Note that
82          * Netlink sends us an RTM_NEWLINK not only when a new
83          * interface appears, but when it changes, too */
84
85         if (!(hw = avahi_interface_monitor_get_hw_interface(m, ifinfomsg->ifi_index)))
86             
87             /* No object found, so let's create a new
88              * one. avahi_hw_interface_new() will call
89              * avahi_interface_new() internally twice for IPv4 and
90              * IPv6, so there is no need for us to do that
91              * ourselves */
92             if (!(hw = avahi_hw_interface_new(m, (AvahiIfIndex) ifinfomsg->ifi_index)))
93                 return; /* OOM */
94
95         /* Check whether the flags of this interface are OK for us */
96         hw->flags_ok =
97             (ifinfomsg->ifi_flags & IFF_UP) &&
98             (!m->server->config.use_iff_running || (ifinfomsg->ifi_flags & IFF_RUNNING)) &&
99             !(ifinfomsg->ifi_flags & IFF_LOOPBACK) &&
100             (ifinfomsg->ifi_flags & IFF_MULTICAST) &&
101             !(ifinfomsg->ifi_flags & IFF_POINTOPOINT);
102
103         /* Handle interface attributes */
104         l = NLMSG_PAYLOAD(n, sizeof(struct ifinfomsg));
105         a = IFLA_RTA(ifinfomsg);
106
107         while (RTA_OK(a, l)) {
108             switch(a->rta_type) {
109                 case IFLA_IFNAME:
110
111                     /* Fill in interface name */
112                     avahi_free(hw->name);
113                     hw->name = avahi_strndup(RTA_DATA(a), RTA_PAYLOAD(a));
114                     break;
115
116                 case IFLA_MTU:
117
118                     /* Fill in MTU */
119                     assert(RTA_PAYLOAD(a) == sizeof(unsigned int));
120                     hw->mtu = *((unsigned int*) RTA_DATA(a));
121                     break;
122
123                 case IFLA_ADDRESS:
124
125                     /* Fill in hardware (MAC) address */
126                     hw->mac_address_size = RTA_PAYLOAD(a);
127                     if (hw->mac_address_size > AVAHI_MAX_MAC_ADDRESS)
128                         hw->mac_address_size = AVAHI_MAX_MAC_ADDRESS;
129                     
130                     memcpy(hw->mac_address, RTA_DATA(a), hw->mac_address_size);
131                     break;
132                     
133                 default:
134                     ;
135             }
136
137             a = RTA_NEXT(a, l);
138         }
139
140         /* Check whether this interface is now "relevant" for us. If
141          * it is Avahi will start to announce its records on this
142          * interface and send out queries for subscribed records on
143          * it */
144         avahi_hw_interface_check_relevant(hw);
145
146         /* Update any associated RRs of this interface. (i.e. the
147          * _workstation._tcp record containing the MAC address) */
148         avahi_hw_interface_update_rrs(hw, 0);
149         
150     } else if (n->nlmsg_type == RTM_DELLINK) {
151
152         /* An interface has been removed */
153
154         struct ifinfomsg *ifinfomsg = NLMSG_DATA(n);
155         AvahiHwInterface *hw;
156
157         /* A (superfluous?) sanity check */
158         if (ifinfomsg->ifi_family != AF_UNSPEC)
159             return;
160
161         /* Get a reference to our AvahiHwInterface object of this interface */
162         if (!(hw = avahi_interface_monitor_get_hw_interface(m, (AvahiIfIndex) ifinfomsg->ifi_index)))
163             return;
164
165         /* Free our object */
166         avahi_hw_interface_free(hw, 0);
167         
168     } else if (n->nlmsg_type == RTM_NEWADDR || n->nlmsg_type == RTM_DELADDR) {
169
170         /* An address has been added, modified or removed */
171         
172         struct ifaddrmsg *ifaddrmsg = NLMSG_DATA(n);
173         AvahiInterface *i;
174         struct rtattr *a = NULL;
175         size_t l;
176         AvahiAddress raddr;
177         int raddr_valid = 0;
178
179         /* We are only interested in IPv4 and IPv6 */
180         if (ifaddrmsg->ifa_family != AF_INET && ifaddrmsg->ifa_family != AF_INET6)
181             return;
182
183         /* Try to get a reference to our AvahiInterface object for the
184          * interface this address is assigned to. If ther is no object
185          * for this interface, we ignore this address. */
186         if (!(i = avahi_interface_monitor_get_interface(m, (AvahiIfIndex) ifaddrmsg->ifa_index, avahi_af_to_proto(ifaddrmsg->ifa_family))))
187             return;
188
189         /* Fill in address family for our new address */
190         raddr.proto = avahi_af_to_proto(ifaddrmsg->ifa_family);
191
192         l = NLMSG_PAYLOAD(n, sizeof(struct ifaddrmsg));
193         a = IFA_RTA(ifaddrmsg);
194
195         while (RTA_OK(a, l)) {
196
197             switch(a->rta_type) {
198                 case IFA_ADDRESS:
199                     /* Fill in address data */
200                     
201                     if ((raddr.proto == AVAHI_PROTO_INET6 && RTA_PAYLOAD(a) != 16) ||
202                         (raddr.proto == AVAHI_PROTO_INET && RTA_PAYLOAD(a) != 4))
203                         return;
204
205                     memcpy(raddr.data.data, RTA_DATA(a), RTA_PAYLOAD(a));
206                     raddr_valid = 1;
207
208                     break;
209
210                 default:
211                     ;
212             }
213             
214             a = RTA_NEXT(a, l);
215         }
216
217         /* If there was no adress attached to this message, let's quit. */
218         if (!raddr_valid)
219             return;
220
221         if (n->nlmsg_type == RTM_NEWADDR) {
222             AvahiInterfaceAddress *addr;
223
224             /* This address is new or has been modified, so let's get an object for it */
225             if (!(addr = avahi_interface_monitor_get_address(m, i, &raddr)))
226
227                 /* Mmm, no object existing yet, so let's create a new one */
228                 if (!(addr = avahi_interface_address_new(m, i, &raddr, ifaddrmsg->ifa_prefixlen)))
229                     return; /* OOM */
230
231             /* Update the scope field for the address */
232             addr->global_scope = ifaddrmsg->ifa_scope == RT_SCOPE_UNIVERSE || ifaddrmsg->ifa_scope == RT_SCOPE_SITE;
233         } else {
234             AvahiInterfaceAddress *addr;
235             assert(n->nlmsg_type == RTM_DELADDR);
236
237             /* Try to get a reference to our AvahiInterfaceAddress object for this address */
238             if (!(addr = avahi_interface_monitor_get_address(m, i, &raddr)))
239                 return;
240
241             /* And free it */
242             avahi_interface_address_free(addr);
243         }
244
245         /* Avahi only considers interfaces with at least one address
246          * attached relevant. Since we migh have added or removed an
247          * address, let's have it check again whether the interface is
248          * now relevant */
249         avahi_interface_check_relevant(i);
250
251         /* Update any associated RRs, like A or AAAA for our new/removed address */
252         avahi_interface_update_rrs(i, 0);
253         
254     } else if (n->nlmsg_type == NLMSG_DONE) {
255
256         /* This wild dump request ended, so let's see what we do next */
257         
258         if (m->osdep.list == LIST_IFACE) {
259
260             /* Mmmm, interfaces have been wild dumped already, so
261              * let's go on with wild dumping the addresses */
262             
263             if (netlink_list_items(m->osdep.netlink, RTM_GETADDR, &m->osdep.query_addr_seq) < 0) {
264                 avahi_log_warn("NETLINK: Failed to list addrs: %s", strerror(errno));
265                 m->osdep.list = LIST_DONE;
266             } else
267
268                 /* Update state information */
269                 m->osdep.list = LIST_ADDR;
270
271         } else
272             /* We're done. Tell avahi_interface_monitor_sync() to finish. */
273             m->osdep.list = LIST_DONE;
274
275         if (m->osdep.list == LIST_DONE) {
276
277             /* Only after this boolean variable has been set, Avahi
278              * will start to announce or browse on all interfaces. It
279              * is originaly set to 0, which means that relevancy
280              * checks and RR updates are disabled during the wild
281              * dumps. */
282             m->list_complete = 1;
283
284             /* So let's check if any interfaces are relevant now */
285             avahi_interface_monitor_check_relevant(m);
286
287             /* And update all RRs attached to any interface */
288             avahi_interface_monitor_update_rrs(m, 0);
289
290             /* Tell the user that the wild dump is complete */
291             avahi_log_info("Network interface enumeration completed.");
292         }
293         
294     } else if (n->nlmsg_type == NLMSG_ERROR &&
295                (n->nlmsg_seq == m->osdep.query_link_seq || n->nlmsg_seq == m->osdep.query_addr_seq)) {
296         struct nlmsgerr *e = NLMSG_DATA (n);
297
298         /* Some kind of error happened. Let's just tell the user and
299          * ignore it otherwise */
300         
301         if (e->error)
302             avahi_log_warn("NETLINK: Failed to browse: %s", strerror(-e->error));
303     }
304 }
305
306 int avahi_interface_monitor_init_osdep(AvahiInterfaceMonitor *m) {
307     assert(m);
308
309     /* Initialize our own data */
310     
311     m->osdep.netlink = NULL;
312     m->osdep.query_addr_seq = m->osdep.query_link_seq = 0;
313
314     /* Create a netlink object for us. It abstracts some things and
315      * makes netlink easier to use. It will attach to the main loop
316      * for us and call netlink_callback() whenever an event
317      * happens. */
318     if (!(m->osdep.netlink = avahi_netlink_new(m->server->poll_api, RTMGRP_LINK|RTMGRP_IPV4_IFADDR|RTMGRP_IPV6_IFADDR, netlink_callback, m)))
319         goto fail;
320
321     /* Set the initial state. */
322     m->osdep.list = LIST_IFACE;
323
324     /* Start the wild dump for the interfaces */
325     if (netlink_list_items(m->osdep.netlink, RTM_GETLINK, &m->osdep.query_link_seq) < 0)
326         goto fail;
327
328     return 0;
329
330 fail:
331
332     if (m->osdep.netlink) {
333         avahi_netlink_free(m->osdep.netlink);
334         m->osdep.netlink = NULL;
335     }
336
337     return -1;
338 }
339
340 void avahi_interface_monitor_free_osdep(AvahiInterfaceMonitor *m) {
341     assert(m);
342
343     if (m->osdep.netlink) {
344         avahi_netlink_free(m->osdep.netlink);
345         m->osdep.netlink = NULL;
346     }
347 }
348
349 void avahi_interface_monitor_sync(AvahiInterfaceMonitor *m) {
350     assert(m);
351
352     /* Let's handle netlink events until we are done with wild
353      * dumping */
354     
355     while (m->osdep.list != LIST_DONE) {
356         if (!avahi_netlink_work(m->osdep.netlink, 1) == 0)
357             break;
358     }
359
360     /* At this point Avahi knows about all local interfaces and
361      * addresses in existance. */
362 }