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