]> git.meshlink.io Git - catta/blob - avahi-core/iface.c
* abstract MTU stuff for packet generation
[catta] / avahi-core / iface.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 <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
40 static 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
58 static 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
67 static 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
77 static 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
90 static 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
114 static 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
128 static 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
142 static 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
161 static 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
184 static 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
216 static 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
226 static 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
396 AvahiInterfaceMonitor *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
416 fail:
417     avahi_interface_monitor_free(m);
418     return NULL;
419 }
420
421 void 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
430 void 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
449 AvahiInterface* 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
467 AvahiHwInterface* 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
475 void 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
496 void 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
503 gboolean 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
513 gboolean 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
523 gboolean 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
533 void 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
546 gboolean 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
557 gboolean avahi_interface_address_relevant(AvahiInterfaceAddress *a) { 
558     g_assert(a);
559
560     return a->scope == RT_SCOPE_UNIVERSE;
561 }
562
563
564 gboolean 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
577 void 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 }