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