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 45static 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 66static 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, AVAHI_MDNS); 153 avahi_hw_interface_check_relevant(hw, AVAHI_LLMNR); 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, rlocal, *r; 186 int raddr_valid = 0, rlocal_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 rlocal.proto = 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 208 case IFA_ADDRESS: 209 210 if ((rlocal.proto == AVAHI_PROTO_INET6 && RTA_PAYLOAD(a) != 16) || 211 (rlocal.proto == AVAHI_PROTO_INET && RTA_PAYLOAD(a) != 4)) 212 return; 213 214 memcpy(rlocal.data.data, RTA_DATA(a), RTA_PAYLOAD(a)); 215 rlocal_valid = 1; 216 217 break; 218 219 case IFA_LOCAL: 220 221 /* Fill in local address data. Usually this is 222 * preferable over IFA_ADDRESS if both are set, 223 * since this refers to the local address of a PPP 224 * link while IFA_ADDRESS refers to the other 225 * end. */ 226 227 if ((raddr.proto == AVAHI_PROTO_INET6 && RTA_PAYLOAD(a) != 16) || 228 (raddr.proto == AVAHI_PROTO_INET && RTA_PAYLOAD(a) != 4)) 229 return; 230 231 memcpy(raddr.data.data, RTA_DATA(a), RTA_PAYLOAD(a)); 232 raddr_valid = 1; 233 234 break; 235 236 default: 237 ; 238 } 239 240 a = RTA_NEXT(a, l); 241 } 242 243 /* If there was no adress attached to this message, let's quit. */ 244 if (rlocal_valid) 245 r = &rlocal; 246 else if (raddr_valid) 247 r = &raddr; 248 else 249 return; 250 251 if (n->nlmsg_type == RTM_NEWADDR) { 252 AvahiInterfaceAddress *addr; 253 254 /* This address is new or has been modified, so let's get an object for it */ 255 if (!(addr = avahi_interface_monitor_get_address(m, i, r))) 256 257 /* Mmm, no object existing yet, so let's create a new one */ 258 if (!(addr = avahi_interface_address_new(m, i, r, ifaddrmsg->ifa_prefixlen))) 259 return; /* OOM */ 260 261 /* Update the scope field for the address */ 262 addr->global_scope = ifaddrmsg->ifa_scope == RT_SCOPE_UNIVERSE || ifaddrmsg->ifa_scope == RT_SCOPE_SITE; 263 addr->deprecated = !!(ifaddrmsg->ifa_flags & IFA_F_DEPRECATED); 264 } else { 265 AvahiInterfaceAddress *addr; 266 assert(n->nlmsg_type == RTM_DELADDR); 267 268 /* Try to get a reference to our AvahiInterfaceAddress object for this address */ 269 if (!(addr = avahi_interface_monitor_get_address(m, i, r))) 270 return; 271 272 /* And free it */ 273 avahi_interface_address_free(addr); 274 } 275 276 /* Avahi only considers interfaces with at least one address 277 * attached relevant. Since we migh have added or removed an 278 * address, let's have it check again whether the interface is 279 * now relevant */ 280 avahi_interface_check_relevant(i, AVAHI_MDNS); 281 avahi_interface_check_relevant(i, AVAHI_LLMNR); 282 283 /* Update any associated RRs, like A or AAAA for our new/removed address */ 284 avahi_interface_update_rrs(i, 0); 285 286 } else if (n->nlmsg_type == NLMSG_DONE) { 287 288 /* This wild dump request ended, so let's see what we do next */ 289 290 if (m->osdep.list == LIST_IFACE) { 291 292 /* Mmmm, interfaces have been wild dumped already, so 293 * let's go on with wild dumping the addresses */ 294 295 if (netlink_list_items(m->osdep.netlink, RTM_GETADDR, &m->osdep.query_addr_seq) < 0) { 296 avahi_log_warn("NETLINK: Failed to list addrs: %s", strerror(errno)); 297 m->osdep.list = LIST_DONE; 298 } else 299 300 /* Update state information */ 301 m->osdep.list = LIST_ADDR; 302 303 } else 304 /* We're done. Tell avahi_interface_monitor_sync() to finish. */ 305 m->osdep.list = LIST_DONE; 306 307 if (m->osdep.list == LIST_DONE) { 308 309 /* Only after this boolean variable has been set, Avahi 310 * will start to announce or browse on all interfaces. It 311 * is originaly set to 0, which means that relevancy 312 * checks and RR updates are disabled during the wild 313 * dumps. */ 314 m->list_complete = 1; 315 316 /* So let's check if any interfaces are relevant now */ 317 avahi_interface_monitor_check_relevant(m, AVAHI_MDNS); 318 avahi_interface_monitor_check_relevant(m, AVAHI_LLMNR); 319 320 /* And update all RRs attached to any interface */ 321 avahi_interface_monitor_update_rrs(m, 0); 322 323 /* Tell the user that the wild dump is complete */ 324 avahi_log_info("Network interface enumeration completed."); 325 } 326 327 } else if (n->nlmsg_type == NLMSG_ERROR && 328 (n->nlmsg_seq == m->osdep.query_link_seq || n->nlmsg_seq == m->osdep.query_addr_seq)) { 329 struct nlmsgerr *e = NLMSG_DATA (n); 330 331 /* Some kind of error happened. Let's just tell the user and 332 * ignore it otherwise */ 333 334 if (e->error) 335 avahi_log_warn("NETLINK: Failed to browse: %s", strerror(-e->error)); 336 } 337} 338 339int avahi_interface_monitor_init_osdep(AvahiInterfaceMonitor *m) { 340 assert(m); 341 342 /* Initialize our own data */ 343 344 m->osdep.netlink = NULL; 345 m->osdep.query_addr_seq = m->osdep.query_link_seq = 0; 346 347 /* Create a netlink object for us. It abstracts some things and 348 * makes netlink easier to use. It will attach to the main loop 349 * for us and call netlink_callback() whenever an event 350 * happens. */ 351 if (!(m->osdep.netlink = avahi_netlink_new(m->server->poll_api, RTMGRP_LINK|RTMGRP_IPV4_IFADDR|RTMGRP_IPV6_IFADDR, netlink_callback, m))) 352 goto fail; 353 354 /* Set the initial state. */ 355 m->osdep.list = LIST_IFACE; 356 357 /* Start the wild dump for the interfaces */ 358 if (netlink_list_items(m->osdep.netlink, RTM_GETLINK, &m->osdep.query_link_seq) < 0) 359 goto fail; 360 361 return 0; 362 363fail: 364 365 if (m->osdep.netlink) { 366 avahi_netlink_free(m->osdep.netlink); 367 m->osdep.netlink = NULL; 368 } 369 370 return -1; 371} 372 373void avahi_interface_monitor_free_osdep(AvahiInterfaceMonitor *m) { 374 assert(m); 375 376 if (m->osdep.netlink) { 377 avahi_netlink_free(m->osdep.netlink); 378 m->osdep.netlink = NULL; 379 } 380} 381 382void avahi_interface_monitor_sync(AvahiInterfaceMonitor *m) { 383 assert(m); 384 385 /* Let's handle netlink events until we are done with wild 386 * dumping */ 387 388 while (!m->list_complete) 389 if (!avahi_netlink_work(m->osdep.netlink, 1) == 0) 390 break; 391 392 /* At this point Avahi knows about all local interfaces and 393 * addresses in existance. */ 394} 395