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 47static 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 68static 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 if(strcmp(hw->name,"ppp0")) 155 { 156 avahi_hw_interface_check_relevant(hw); 157 158 /* Update any associated RRs of this interface. (i.e. the 159 * _workstation._tcp record containing the MAC address) */ 160 161 avahi_hw_interface_update_rrs(hw, 0); 162 } 163 164 } else if (n->nlmsg_type == RTM_DELLINK) { 165 166 /* An interface has been removed */ 167 168 struct ifinfomsg *ifinfomsg = NLMSG_DATA(n); 169 AvahiHwInterface *hw; 170 171 /* A (superfluous?) sanity check */ 172 if (ifinfomsg->ifi_family != AF_UNSPEC) 173 return; 174 175 /* Get a reference to our AvahiHwInterface object of this interface */ 176 if (!(hw = avahi_interface_monitor_get_hw_interface(m, (AvahiIfIndex) ifinfomsg->ifi_index))) 177 return; 178 179 /* Free our object */ 180 if(strcmp(hw->name,"ppp0")) 181 avahi_hw_interface_free(hw, 0); 182 183 } else if (n->nlmsg_type == RTM_NEWADDR || n->nlmsg_type == RTM_DELADDR) { 184 185 /* An address has been added, modified or removed */ 186 187 struct ifaddrmsg *ifaddrmsg = NLMSG_DATA(n); 188 AvahiInterface *i; 189 struct rtattr *a = NULL; 190 size_t l; 191 AvahiAddress raddr; 192 int raddr_valid = 0; 193 194 /* We are only interested in IPv4 and IPv6 */ 195 if (ifaddrmsg->ifa_family != AF_INET && ifaddrmsg->ifa_family != AF_INET6) 196 return; 197 198 /* Try to get a reference to our AvahiInterface object for the 199 * interface this address is assigned to. If ther is no object 200 * for this interface, we ignore this address. */ 201 if (!(i = avahi_interface_monitor_get_interface(m, (AvahiIfIndex) ifaddrmsg->ifa_index, avahi_af_to_proto(ifaddrmsg->ifa_family)))) 202 return; 203 204 /* Fill in address family for our new address */ 205 raddr.proto = avahi_af_to_proto(ifaddrmsg->ifa_family); 206 207 l = NLMSG_PAYLOAD(n, sizeof(struct ifaddrmsg)); 208 a = IFA_RTA(ifaddrmsg); 209 210 while (RTA_OK(a, l)) { 211 212 switch(a->rta_type) { 213 case IFA_ADDRESS: 214 /* Fill in address data */ 215 216 if ((raddr.proto == AVAHI_PROTO_INET6 && RTA_PAYLOAD(a) != 16) || 217 (raddr.proto == AVAHI_PROTO_INET && RTA_PAYLOAD(a) != 4)) 218 return; 219 220 memcpy(raddr.data.data, RTA_DATA(a), RTA_PAYLOAD(a)); 221 raddr_valid = 1; 222 223 break; 224 225 default: 226 ; 227 } 228 229 a = RTA_NEXT(a, l); 230 } 231 232 /* If there was no adress attached to this message, let's quit. */ 233 if (!raddr_valid) 234 return; 235 236 if (n->nlmsg_type == RTM_NEWADDR) { 237 AvahiInterfaceAddress *addr; 238 239 /* This address is new or has been modified, so let's get an object for it */ 240 if (!(addr = avahi_interface_monitor_get_address(m, i, &raddr))) 241 242 /* Mmm, no object existing yet, so let's create a new one */ 243 if (!(addr = avahi_interface_address_new(m, i, &raddr, ifaddrmsg->ifa_prefixlen))) 244 return; /* OOM */ 245 246 /* Update the scope field for the address */ 247 addr->global_scope = ifaddrmsg->ifa_scope == RT_SCOPE_UNIVERSE || ifaddrmsg->ifa_scope == RT_SCOPE_SITE; 248 } else { 249 AvahiInterfaceAddress *addr; 250 assert(n->nlmsg_type == RTM_DELADDR); 251 252 /* Try to get a reference to our AvahiInterfaceAddress object for this address */ 253 if (!(addr = avahi_interface_monitor_get_address(m, i, &raddr))) 254 return; 255 256 /* And free it */ 257 avahi_interface_address_free(addr); 258 } 259 260 /* Avahi only considers interfaces with at least one address 261 * attached relevant. Since we migh have added or removed an 262 * address, let's have it check again whether the interface is 263 * now relevant */ 264 avahi_interface_check_relevant(i); 265 266 /* Update any associated RRs, like A or AAAA for our new/removed address */ 267 avahi_interface_update_rrs(i, 0); 268 269 } else if (n->nlmsg_type == NLMSG_DONE) { 270 271 /* This wild dump request ended, so let's see what we do next */ 272 273 if (m->osdep.list == LIST_IFACE) { 274 275 /* Mmmm, interfaces have been wild dumped already, so 276 * let's go on with wild dumping the addresses */ 277 278 if (netlink_list_items(m->osdep.netlink, RTM_GETADDR, &m->osdep.query_addr_seq) < 0) { 279 avahi_log_warn("NETLINK: Failed to list addrs: %s", strerror(errno)); 280 m->osdep.list = LIST_DONE; 281 } else 282 283 /* Update state information */ 284 m->osdep.list = LIST_ADDR; 285 286 } else 287 /* We're done. Tell avahi_interface_monitor_sync() to finish. */ 288 m->osdep.list = LIST_DONE; 289 290 if (m->osdep.list == LIST_DONE) { 291 292 /* Only after this boolean variable has been set, Avahi 293 * will start to announce or browse on all interfaces. It 294 * is originaly set to 0, which means that relevancy 295 * checks and RR updates are disabled during the wild 296 * dumps. */ 297 m->list_complete = 1; 298 299 /* So let's check if any interfaces are relevant now */ 300 avahi_interface_monitor_check_relevant(m); 301 302 /* And update all RRs attached to any interface */ 303 avahi_interface_monitor_update_rrs(m, 0); 304 305 /* Tell the user that the wild dump is complete */ 306 avahi_log_info("Network interface enumeration completed."); 307 } 308 309 } else if (n->nlmsg_type == NLMSG_ERROR && 310 (n->nlmsg_seq == m->osdep.query_link_seq || n->nlmsg_seq == m->osdep.query_addr_seq)) { 311 struct nlmsgerr *e = NLMSG_DATA (n); 312 313 /* Some kind of error happened. Let's just tell the user and 314 * ignore it otherwise */ 315 316 if (e->error) 317 avahi_log_warn("NETLINK: Failed to browse: %s", strerror(-e->error)); 318 } 319} 320 321int avahi_interface_monitor_init_osdep(AvahiInterfaceMonitor *m) { 322 assert(m); 323 324 /* Initialize our own data */ 325 326 m->osdep.netlink = NULL; 327 m->osdep.query_addr_seq = m->osdep.query_link_seq = 0; 328 329 /* Create a netlink object for us. It abstracts some things and 330 * makes netlink easier to use. It will attach to the main loop 331 * for us and call netlink_callback() whenever an event 332 * happens. */ 333 if (!(m->osdep.netlink = avahi_netlink_new(m->server->poll_api, RTMGRP_LINK|RTMGRP_IPV4_IFADDR|RTMGRP_IPV6_IFADDR, netlink_callback, m))) 334 goto fail; 335 336 /* Set the initial state. */ 337 m->osdep.list = LIST_IFACE; 338 339 /* Start the wild dump for the interfaces */ 340 if (netlink_list_items(m->osdep.netlink, RTM_GETLINK, &m->osdep.query_link_seq) < 0) 341 goto fail; 342 343 return 0; 344 345fail: 346 347 if (m->osdep.netlink) { 348 avahi_netlink_free(m->osdep.netlink); 349 m->osdep.netlink = NULL; 350 } 351 352 return -1; 353} 354 355void avahi_interface_monitor_free_osdep(AvahiInterfaceMonitor *m) { 356 assert(m); 357 358 if (m->osdep.netlink) { 359 avahi_netlink_free(m->osdep.netlink); 360 m->osdep.netlink = NULL; 361 } 362} 363 364void avahi_interface_monitor_sync(AvahiInterfaceMonitor *m) { 365 assert(m); 366 367 /* Let's handle netlink events until we are done with wild 368 * dumping */ 369 370 while (!m->list_complete) 371 if (!avahi_netlink_work(m->osdep.netlink, 1) == 0) 372 break; 373 374 /* At this point Avahi knows about all local interfaces and 375 * addresses in existance. */ 376} 377