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