iface.c revision 602a2b6481587b7da2594db39151ec9380f276df
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 <sys/socket.h> 28#include <asm/types.h> 29#include <linux/netlink.h> 30#include <linux/rtnetlink.h> 31#include <errno.h> 32#include <net/if.h> 33 34#include "iface.h" 35#include "netlink.h" 36#include "dns.h" 37#include "socket.h" 38#include "announce.h" 39 40static void update_address_rr(AvahiInterfaceMonitor *m, AvahiInterfaceAddress *a, int remove) { 41 g_assert(m); 42 g_assert(a); 43 44 if (!avahi_interface_address_relevant(a) || remove) { 45 if (a->entry_group) { 46 avahi_entry_group_free(a->entry_group); 47 a->entry_group = NULL; 48 } 49 } else { 50 if (!a->entry_group) { 51 a->entry_group = avahi_entry_group_new(m->server, NULL, NULL); 52 avahi_server_add_address(m->server, a->entry_group, a->interface->hardware->index, AF_UNSPEC, 0, NULL, &a->address); 53 avahi_entry_group_commit(a->entry_group); 54 } 55 } 56} 57 58static void update_interface_rr(AvahiInterfaceMonitor *m, AvahiInterface *i, int remove) { 59 AvahiInterfaceAddress *a; 60 g_assert(m); 61 g_assert(i); 62 63 for (a = i->addresses; a; a = a->address_next) 64 update_address_rr(m, a, remove); 65} 66 67static void update_hw_interface_rr(AvahiInterfaceMonitor *m, AvahiHwInterface *hw, int remove) { 68 AvahiInterface *i; 69 70 g_assert(m); 71 g_assert(hw); 72 73 for (i = hw->interfaces; i; i = i->by_hardware_next) 74 update_interface_rr(m, i, remove); 75} 76 77static void free_address(AvahiInterfaceMonitor *m, AvahiInterfaceAddress *a) { 78 g_assert(m); 79 g_assert(a); 80 g_assert(a->interface); 81 82 AVAHI_LLIST_REMOVE(AvahiInterfaceAddress, address, a->interface->addresses, a); 83 84 if (a->entry_group) 85 avahi_entry_group_free(a->entry_group); 86 87 g_free(a); 88} 89 90static void free_interface(AvahiInterfaceMonitor *m, AvahiInterface *i, gboolean send_goodbye) { 91 g_assert(m); 92 g_assert(i); 93 94 g_message("removing interface %s.%i", i->hardware->name, i->protocol); 95 avahi_goodbye_interface(m->server, i, send_goodbye); 96 g_message("flushing..."); 97 avahi_packet_scheduler_flush_responses(i->scheduler); 98 g_message("done"); 99 100 g_assert(!i->announcements); 101 102 while (i->addresses) 103 free_address(m, i->addresses); 104 105 avahi_packet_scheduler_free(i->scheduler); 106 avahi_cache_free(i->cache); 107 108 AVAHI_LLIST_REMOVE(AvahiInterface, interface, m->interfaces, i); 109 AVAHI_LLIST_REMOVE(AvahiInterface, by_hardware, i->hardware->interfaces, i); 110 111 g_free(i); 112} 113 114static void free_hw_interface(AvahiInterfaceMonitor *m, AvahiHwInterface *hw, gboolean send_goodbye) { 115 g_assert(m); 116 g_assert(hw); 117 118 while (hw->interfaces) 119 free_interface(m, hw->interfaces, send_goodbye); 120 121 AVAHI_LLIST_REMOVE(AvahiHwInterface, hardware, m->hw_interfaces, hw); 122 g_hash_table_remove(m->hash_table, &hw->index); 123 124 g_free(hw->name); 125 g_free(hw); 126} 127 128static AvahiInterfaceAddress* get_address(AvahiInterfaceMonitor *m, AvahiInterface *i, const AvahiAddress *raddr) { 129 AvahiInterfaceAddress *ia; 130 131 g_assert(m); 132 g_assert(i); 133 g_assert(raddr); 134 135 for (ia = i->addresses; ia; ia = ia->address_next) 136 if (avahi_address_cmp(&ia->address, raddr) == 0) 137 return ia; 138 139 return NULL; 140} 141 142static int netlink_list_items(AvahiNetlink *nl, guint16 type, guint *ret_seq) { 143 struct nlmsghdr *n; 144 struct rtgenmsg *gen; 145 guint8 req[1024]; 146 147 memset(&req, 0, sizeof(req)); 148 n = (struct nlmsghdr*) req; 149 n->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)); 150 n->nlmsg_type = type; 151 n->nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST; 152 n->nlmsg_pid = 0; 153 154 gen = NLMSG_DATA(n); 155 memset(gen, 0, sizeof(struct rtgenmsg)); 156 gen->rtgen_family = AF_UNSPEC; 157 158 return avahi_netlink_send(nl, n, ret_seq); 159} 160 161static void new_interface(AvahiInterfaceMonitor *m, AvahiHwInterface *hw, guchar protocol) { 162 AvahiInterface *i; 163 164 g_assert(m); 165 g_assert(hw); 166 g_assert(protocol != AF_UNSPEC); 167 168 i = g_new(AvahiInterface, 1); 169 i->monitor = m; 170 i->hardware = hw; 171 i->protocol = protocol; 172 i->announcing = FALSE; 173 174 AVAHI_LLIST_HEAD_INIT(AvahiInterfaceAddress, i->addresses); 175 AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, i->announcements); 176 177 i->cache = avahi_cache_new(m->server, i); 178 i->scheduler = avahi_packet_scheduler_new(m->server, i); 179 180 AVAHI_LLIST_PREPEND(AvahiInterface, by_hardware, hw->interfaces, i); 181 AVAHI_LLIST_PREPEND(AvahiInterface, interface, m->interfaces, i); 182} 183 184static void check_interface_relevant(AvahiInterfaceMonitor *m, AvahiInterface *i) { 185 gboolean b; 186 187 g_assert(m); 188 g_assert(i); 189 190 b = avahi_interface_relevant(i); 191 192 if (b && !i->announcing) { 193 g_message("New relevant interface %s.%i", i->hardware->name, i->protocol); 194 195 if (i->protocol == AF_INET) 196 avahi_mdns_mcast_join_ipv4 (i->hardware->index, m->server->fd_ipv4); 197 if (i->protocol == AF_INET6) 198 avahi_mdns_mcast_join_ipv6 (i->hardware->index, m->server->fd_ipv6); 199 200 i->announcing = TRUE; 201 avahi_announce_interface(m->server, i); 202 } else if (!b && i->announcing) { 203 g_message("Interface %s.%i no longer relevant", i->hardware->name, i->protocol); 204 205 avahi_goodbye_interface(m->server, i, FALSE); 206 207 if (i->protocol == AF_INET) 208 avahi_mdns_mcast_leave_ipv4 (i->hardware->index, m->server->fd_ipv4); 209 if (i->protocol == AF_INET6) 210 avahi_mdns_mcast_leave_ipv6 (i->hardware->index, m->server->fd_ipv6); 211 212 i->announcing = FALSE; 213 } 214} 215 216static void check_hw_interface_relevant(AvahiInterfaceMonitor *m, AvahiHwInterface *hw) { 217 AvahiInterface *i; 218 219 g_assert(m); 220 g_assert(hw); 221 222 for (i = hw->interfaces; i; i = i->by_hardware_next) 223 check_interface_relevant(m, i); 224} 225 226static void callback(AvahiNetlink *nl, struct nlmsghdr *n, gpointer userdata) { 227 AvahiInterfaceMonitor *m = userdata; 228 229 g_assert(m); 230 g_assert(n); 231 g_assert(m->netlink == nl); 232 233 if (n->nlmsg_type == RTM_NEWLINK) { 234 struct ifinfomsg *ifinfomsg = NLMSG_DATA(n); 235 AvahiHwInterface *hw; 236 struct rtattr *a = NULL; 237 size_t l; 238 239 if (ifinfomsg->ifi_family != AF_UNSPEC) 240 return; 241 242 if (!(hw = g_hash_table_lookup(m->hash_table, &ifinfomsg->ifi_index))) { 243 hw = g_new(AvahiHwInterface, 1); 244 hw->monitor = m; 245 hw->name = NULL; 246 hw->flags = 0; 247 hw->mtu = 1500; 248 hw->index = ifinfomsg->ifi_index; 249 250 AVAHI_LLIST_HEAD_INIT(AvahiInterface, hw->interfaces); 251 AVAHI_LLIST_PREPEND(AvahiHwInterface, hardware, m->hw_interfaces, hw); 252 253 g_hash_table_insert(m->hash_table, &hw->index, hw); 254 255 if (m->server->fd_ipv4 >= 0) 256 new_interface(m, hw, AF_INET); 257 if (m->server->fd_ipv6 >= 0) 258 new_interface(m, hw, AF_INET6); 259 } 260 261 hw->flags = ifinfomsg->ifi_flags; 262 263 l = NLMSG_PAYLOAD(n, sizeof(struct ifinfomsg)); 264 a = IFLA_RTA(ifinfomsg); 265 266 while (RTA_OK(a, l)) { 267 switch(a->rta_type) { 268 case IFLA_IFNAME: 269 g_free(hw->name); 270 hw->name = g_strndup(RTA_DATA(a), RTA_PAYLOAD(a)); 271 break; 272 273 case IFLA_MTU: 274 g_assert(RTA_PAYLOAD(a) == sizeof(unsigned int)); 275 hw->mtu = *((unsigned int*) RTA_DATA(a)); 276 break; 277 278 default: 279 ; 280 } 281 282 a = RTA_NEXT(a, l); 283 } 284 285 update_hw_interface_rr(m, hw, FALSE); 286 check_hw_interface_relevant(m, hw); 287 288 } else if (n->nlmsg_type == RTM_DELLINK) { 289 struct ifinfomsg *ifinfomsg = NLMSG_DATA(n); 290 AvahiHwInterface *hw; 291 292 if (ifinfomsg->ifi_family != AF_UNSPEC) 293 return; 294 295 if (!(hw = avahi_interface_monitor_get_hw_interface(m, ifinfomsg->ifi_index))) 296 return; 297 298 update_hw_interface_rr(m, hw, TRUE); 299 free_hw_interface(m, hw, FALSE); 300 301 } else if (n->nlmsg_type == RTM_NEWADDR || n->nlmsg_type == RTM_DELADDR) { 302 303 struct ifaddrmsg *ifaddrmsg = NLMSG_DATA(n); 304 AvahiInterface *i; 305 struct rtattr *a = NULL; 306 size_t l; 307 AvahiAddress raddr; 308 int raddr_valid = 0; 309 310 if (ifaddrmsg->ifa_family != AF_INET && ifaddrmsg->ifa_family != AF_INET6) 311 return; 312 313 if (!(i = (AvahiInterface*) avahi_interface_monitor_get_interface(m, ifaddrmsg->ifa_index, ifaddrmsg->ifa_family))) 314 return; 315 316 raddr.family = ifaddrmsg->ifa_family; 317 318 l = NLMSG_PAYLOAD(n, sizeof(struct ifinfomsg)); 319 a = IFA_RTA(ifaddrmsg); 320 321 while (RTA_OK(a, l)) { 322 switch(a->rta_type) { 323 case IFA_ADDRESS: 324 if ((raddr.family == AF_INET6 && RTA_PAYLOAD(a) != 16) || 325 (raddr.family == AF_INET && RTA_PAYLOAD(a) != 4)) 326 return; 327 328 memcpy(raddr.data.data, RTA_DATA(a), RTA_PAYLOAD(a)); 329 raddr_valid = 1; 330 331 break; 332 333 default: 334 ; 335 } 336 337 a = RTA_NEXT(a, l); 338 } 339 340 341 if (!raddr_valid) 342 return; 343 344 if (n->nlmsg_type == RTM_NEWADDR) { 345 AvahiInterfaceAddress *addr; 346 347 if (!(addr = get_address(m, i, &raddr))) { 348 addr = g_new(AvahiInterfaceAddress, 1); 349 addr->monitor = m; 350 addr->address = raddr; 351 addr->interface = i; 352 addr->entry_group = NULL; 353 354 AVAHI_LLIST_PREPEND(AvahiInterfaceAddress, address, i->addresses, addr); 355 } 356 357 addr->flags = ifaddrmsg->ifa_flags; 358 addr->scope = ifaddrmsg->ifa_scope; 359 360 update_address_rr(m, addr, FALSE); 361 check_interface_relevant(m, i); 362 } else { 363 AvahiInterfaceAddress *addr; 364 365 if (!(addr = get_address(m, i, &raddr))) 366 return; 367 368 update_address_rr(m, addr, TRUE); 369 free_address(m, addr); 370 371 check_interface_relevant(m, i); 372 } 373 374 } else if (n->nlmsg_type == NLMSG_DONE) { 375 376 if (m->list == LIST_IFACE) { 377 m->list = LIST_DONE; 378 379 if (netlink_list_items(m->netlink, RTM_GETADDR, &m->query_addr_seq) < 0) 380 g_warning("NETLINK: Failed to list addrs: %s", strerror(errno)); 381 else 382 m->list = LIST_ADDR; 383 } else { 384 m->list = LIST_DONE; 385 g_message("Enumeration complete"); 386 } 387 388 } else if (n->nlmsg_type == NLMSG_ERROR && (n->nlmsg_seq == m->query_link_seq || n->nlmsg_seq == m->query_addr_seq)) { 389 struct nlmsgerr *e = NLMSG_DATA (n); 390 391 if (e->error) 392 g_warning("NETLINK: Failed to browse: %s", strerror(-e->error)); 393 } 394} 395 396AvahiInterfaceMonitor *avahi_interface_monitor_new(AvahiServer *s) { 397 AvahiInterfaceMonitor *m = NULL; 398 399 m = g_new0(AvahiInterfaceMonitor, 1); 400 m->server = s; 401 if (!(m->netlink = avahi_netlink_new(s->context, G_PRIORITY_DEFAULT-10, RTMGRP_LINK|RTMGRP_IPV4_IFADDR|RTMGRP_IPV6_IFADDR, callback, m))) 402 goto fail; 403 404 m->hash_table = g_hash_table_new(g_int_hash, g_int_equal); 405 406 AVAHI_LLIST_HEAD_INIT(AvahiInterface, m->interfaces); 407 AVAHI_LLIST_HEAD_INIT(AvahiHwInterface, m->hw_interfaces); 408 409 if (netlink_list_items(m->netlink, RTM_GETLINK, &m->query_link_seq) < 0) 410 goto fail; 411 412 m->list = LIST_IFACE; 413 414 return m; 415 416fail: 417 avahi_interface_monitor_free(m); 418 return NULL; 419} 420 421void avahi_interface_monitor_sync(AvahiInterfaceMonitor *m) { 422 g_assert(m); 423 424 while (m->list != LIST_DONE) { 425 if (!avahi_netlink_work(m->netlink, TRUE)) 426 break; 427 } 428} 429 430void avahi_interface_monitor_free(AvahiInterfaceMonitor *m) { 431 g_assert(m); 432 433 while (m->hw_interfaces) 434 free_hw_interface(m, m->hw_interfaces, TRUE); 435 436 g_assert(!m->interfaces); 437 438 439 if (m->netlink) 440 avahi_netlink_free(m->netlink); 441 442 if (m->hash_table) 443 g_hash_table_destroy(m->hash_table); 444 445 g_free(m); 446} 447 448 449AvahiInterface* avahi_interface_monitor_get_interface(AvahiInterfaceMonitor *m, gint index, guchar protocol) { 450 AvahiHwInterface *hw; 451 AvahiInterface *i; 452 453 g_assert(m); 454 g_assert(index > 0); 455 g_assert(protocol != AF_UNSPEC); 456 457 if (!(hw = avahi_interface_monitor_get_hw_interface(m, index))) 458 return NULL; 459 460 for (i = hw->interfaces; i; i = i->by_hardware_next) 461 if (i->protocol == protocol) 462 return i; 463 464 return NULL; 465} 466 467AvahiHwInterface* avahi_interface_monitor_get_hw_interface(AvahiInterfaceMonitor *m, gint index) { 468 g_assert(m); 469 g_assert(index > 0); 470 471 return g_hash_table_lookup(m->hash_table, &index); 472} 473 474 475void avahi_interface_send_packet_unicast(AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, guint16 port) { 476 g_assert(i); 477 g_assert(p); 478 char t[64]; 479 480 if (!avahi_interface_relevant(i)) 481 return; 482 483 g_assert(!a || a->family == i->protocol); 484 485 if (a) 486 g_message("unicast sending on '%s.%i' to %s:%u", i->hardware->name, i->protocol, avahi_address_snprint(t, sizeof(t), a), port); 487 else 488 g_message("multicast sending on '%s.%i'", i->hardware->name, i->protocol); 489 490 if (i->protocol == AF_INET && i->monitor->server->fd_ipv4 >= 0) 491 avahi_send_dns_packet_ipv4(i->monitor->server->fd_ipv4, i->hardware->index, p, a ? &a->data.ipv4 : NULL, port); 492 else if (i->protocol == AF_INET6 && i->monitor->server->fd_ipv6 >= 0) 493 avahi_send_dns_packet_ipv6(i->monitor->server->fd_ipv6, i->hardware->index, p, a ? &a->data.ipv6 : NULL, port); 494} 495 496void avahi_interface_send_packet(AvahiInterface *i, AvahiDnsPacket *p) { 497 g_assert(i); 498 g_assert(p); 499 500 avahi_interface_send_packet_unicast(i, p, NULL, 0); 501} 502 503gboolean avahi_interface_post_query(AvahiInterface *i, AvahiKey *key, gboolean immediately) { 504 g_assert(i); 505 g_assert(key); 506 507 if (avahi_interface_relevant(i)) 508 return avahi_packet_scheduler_post_query(i->scheduler, key, immediately); 509 510 return FALSE; 511} 512 513gboolean avahi_interface_post_response(AvahiInterface *i, const AvahiAddress *a, AvahiRecord *record, gboolean flush_cache, gboolean immediately) { 514 g_assert(i); 515 g_assert(record); 516 517 if (avahi_interface_relevant(i)) 518 return avahi_packet_scheduler_post_response(i->scheduler, a, record, flush_cache, immediately); 519 520 return FALSE; 521} 522 523gboolean avahi_interface_post_probe(AvahiInterface *i, AvahiRecord *record, gboolean immediately) { 524 g_assert(i); 525 g_assert(record); 526 527 if (avahi_interface_relevant(i)) 528 return avahi_packet_scheduler_post_probe(i->scheduler, record, immediately); 529 530 return FALSE; 531} 532 533void avahi_dump_caches(AvahiInterfaceMonitor *m, FILE *f) { 534 AvahiInterface *i; 535 g_assert(m); 536 537 for (i = m->interfaces; i; i = i->interface_next) { 538 if (avahi_interface_relevant(i)) { 539 fprintf(f, "\n;;; INTERFACE %s.%i ;;;\n", i->hardware->name, i->protocol); 540 avahi_cache_dump(i->cache, f); 541 } 542 } 543 fprintf(f, "\n"); 544} 545 546gboolean avahi_interface_relevant(AvahiInterface *i) { 547 g_assert(i); 548 549 return 550 (i->hardware->flags & IFF_UP) && 551 (i->hardware->flags & IFF_RUNNING) && 552 !(i->hardware->flags & IFF_LOOPBACK) && 553 (i->hardware->flags & IFF_MULTICAST) && 554 i->addresses; 555} 556 557gboolean avahi_interface_address_relevant(AvahiInterfaceAddress *a) { 558 g_assert(a); 559 560 return a->scope == RT_SCOPE_UNIVERSE; 561} 562 563 564gboolean avahi_interface_match(AvahiInterface *i, gint index, guchar protocol) { 565 g_assert(i); 566 567 if (index > 0 && index != i->hardware->index) 568 return FALSE; 569 570 if (protocol != AF_UNSPEC && protocol != i->protocol) 571 return FALSE; 572 573 return TRUE; 574} 575 576 577void avahi_interface_monitor_walk(AvahiInterfaceMonitor *m, gint interface, guchar protocol, AvahiInterfaceMonitorWalkCallback callback, gpointer userdata) { 578 g_assert(m); 579 g_assert(callback); 580 581 if (interface > 0) { 582 if (protocol != AF_UNSPEC) { 583 AvahiInterface *i; 584 585 if ((i = avahi_interface_monitor_get_interface(m, interface, protocol))) 586 callback(m, i, userdata); 587 588 } else { 589 AvahiHwInterface *hw; 590 AvahiInterface *i; 591 592 if ((hw = avahi_interface_monitor_get_hw_interface(m, interface))) 593 for (i = hw->interfaces; i; i = i->by_hardware_next) 594 if (avahi_interface_match(i, interface, protocol)) 595 callback(m, i, userdata); 596 } 597 598 } else { 599 AvahiInterface *i; 600 601 for (i = m->interfaces; i; i = i->interface_next) 602 if (avahi_interface_match(i, interface, protocol)) 603 callback(m, i, userdata); 604 } 605} 606