]> git.meshlink.io Git - catta/blob - avahi-core/iface-linux.c
Split off linux specific parts of iface.[ch] into iface-linux.[ch]. This should
[catta] / avahi-core / iface-linux.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 <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 static int netlink_list_items(AvahiNetlink *nl, uint16_t type, unsigned *ret_seq) {
38     struct nlmsghdr *n;
39     struct rtgenmsg *gen;
40     uint8_t req[1024];
41     
42     memset(&req, 0, sizeof(req));
43     n = (struct nlmsghdr*) req;
44     n->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
45     n->nlmsg_type = type;
46     n->nlmsg_flags = NLM_F_ROOT|NLM_F_REQUEST;
47     n->nlmsg_pid = 0;
48
49     gen = NLMSG_DATA(n);
50     memset(gen, 0, sizeof(struct rtgenmsg));
51     gen->rtgen_family = AF_UNSPEC;
52
53     return avahi_netlink_send(nl, n, ret_seq);
54 }
55
56 static void netlink_callback(AvahiNetlink *nl, struct nlmsghdr *n, void* userdata) {
57     AvahiInterfaceMonitor *m = userdata;
58     
59     assert(m);
60     assert(n);
61     assert(m->osdep.netlink == nl);
62
63     if (n->nlmsg_type == RTM_NEWLINK) {
64         struct ifinfomsg *ifinfomsg = NLMSG_DATA(n);
65         AvahiHwInterface *hw;
66         struct rtattr *a = NULL;
67         size_t l;
68         
69         if (ifinfomsg->ifi_family != AF_UNSPEC)
70             return;
71
72         if (!(hw = avahi_interface_monitor_get_hw_interface(m, ifinfomsg->ifi_index)))
73             if (!(hw = avahi_hw_interface_new(m, (AvahiIfIndex) ifinfomsg->ifi_index)))
74                 return; /* OOM */
75
76         /* Check whether the flags of this interface are OK for us */
77         hw->flags_ok =
78             (ifinfomsg->ifi_flags & IFF_UP) &&
79             (!m->server->config.use_iff_running || (ifinfomsg->ifi_flags & IFF_RUNNING)) &&
80             !(ifinfomsg->ifi_flags & IFF_LOOPBACK) &&
81             (ifinfomsg->ifi_flags & IFF_MULTICAST) &&
82             !(ifinfomsg->ifi_flags & IFF_POINTOPOINT);
83             
84
85         l = NLMSG_PAYLOAD(n, sizeof(struct ifinfomsg));
86         a = IFLA_RTA(ifinfomsg);
87
88         while (RTA_OK(a, l)) {
89             switch(a->rta_type) {
90                 case IFLA_IFNAME:
91                     avahi_free(hw->name);
92                     hw->name = avahi_strndup(RTA_DATA(a), RTA_PAYLOAD(a));
93                     break;
94
95                 case IFLA_MTU:
96                     assert(RTA_PAYLOAD(a) == sizeof(unsigned int));
97                     hw->mtu = *((unsigned int*) RTA_DATA(a));
98                     break;
99
100                 case IFLA_ADDRESS: {
101                     hw->mac_address_size = RTA_PAYLOAD(a);
102                     if (hw->mac_address_size > AVAHI_MAX_MAC_ADDRESS)
103                         hw->mac_address_size = AVAHI_MAX_MAC_ADDRESS;
104                     
105                     memcpy(hw->mac_address, RTA_DATA(a), hw->mac_address_size);
106                     break;
107                 }
108                     
109                 default:
110                     ;
111             }
112
113             a = RTA_NEXT(a, l);
114         }
115
116         avahi_hw_interface_check_relevant(hw);
117         avahi_hw_interface_update_rrs(hw, 0);
118         
119     } else if (n->nlmsg_type == RTM_DELLINK) {
120         struct ifinfomsg *ifinfomsg = NLMSG_DATA(n);
121         AvahiHwInterface *hw;
122
123         if (ifinfomsg->ifi_family != AF_UNSPEC)
124             return;
125         
126         if (!(hw = avahi_interface_monitor_get_hw_interface(m, (AvahiIfIndex) ifinfomsg->ifi_index)))
127             return;
128
129         avahi_hw_interface_free(hw, 0);
130         
131     } else if (n->nlmsg_type == RTM_NEWADDR || n->nlmsg_type == RTM_DELADDR) {
132
133         struct ifaddrmsg *ifaddrmsg = NLMSG_DATA(n);
134         AvahiInterface *i;
135         struct rtattr *a = NULL;
136         size_t l;
137         AvahiAddress raddr;
138         int raddr_valid = 0;
139
140         if (ifaddrmsg->ifa_family != AF_INET && ifaddrmsg->ifa_family != AF_INET6)
141             return;
142
143         if (!(i = avahi_interface_monitor_get_interface(m, (AvahiIfIndex) ifaddrmsg->ifa_index, avahi_af_to_proto(ifaddrmsg->ifa_family))))
144             return;
145
146         raddr.proto = avahi_af_to_proto(ifaddrmsg->ifa_family);
147
148         l = NLMSG_PAYLOAD(n, sizeof(struct ifaddrmsg));
149         a = IFA_RTA(ifaddrmsg);
150
151         while (RTA_OK(a, l)) {
152
153             switch(a->rta_type) {
154                 case IFA_ADDRESS:
155                     if ((raddr.proto == AVAHI_PROTO_INET6 && RTA_PAYLOAD(a) != 16) ||
156                         (raddr.proto == AVAHI_PROTO_INET && RTA_PAYLOAD(a) != 4))
157                         return;
158
159                     memcpy(raddr.data.data, RTA_DATA(a), RTA_PAYLOAD(a));
160                     raddr_valid = 1;
161
162                     break;
163
164                 default:
165                     ;
166             }
167             
168             a = RTA_NEXT(a, l);
169         }
170         
171         if (!raddr_valid)
172             return;
173
174         if (n->nlmsg_type == RTM_NEWADDR) {
175             AvahiInterfaceAddress *addr;
176             
177             if (!(addr = avahi_interface_monitor_get_address(m, i, &raddr)))
178                 if (!(addr = avahi_interface_address_new(m, i, &raddr, ifaddrmsg->ifa_prefixlen)))
179                     return; /* OOM */
180             
181             addr->global_scope = ifaddrmsg->ifa_scope == RT_SCOPE_UNIVERSE || ifaddrmsg->ifa_scope == RT_SCOPE_SITE;
182         } else {
183             AvahiInterfaceAddress *addr;
184             
185             if (!(addr = avahi_interface_monitor_get_address(m, i, &raddr)))
186                 return;
187
188             avahi_interface_address_free(addr);
189         }
190
191         avahi_interface_check_relevant(i);
192         avahi_interface_update_rrs(i, 0);
193         
194     } else if (n->nlmsg_type == NLMSG_DONE) {
195         
196         if (m->osdep.list == LIST_IFACE) {
197             
198             if (netlink_list_items(m->osdep.netlink, RTM_GETADDR, &m->osdep.query_addr_seq) < 0) {
199                 avahi_log_warn("NETLINK: Failed to list addrs: %s", strerror(errno));
200                 m->osdep.list = LIST_DONE;
201             } else
202                 m->osdep.list = LIST_ADDR;
203
204         } else
205             /* We're through */
206             m->osdep.list = LIST_DONE;
207
208         if (m->osdep.list == LIST_DONE) {
209             m->list_complete = 1;
210             avahi_interface_monitor_check_relevant(m);
211             avahi_interface_monitor_update_rrs(m, 0);
212             avahi_log_info("Network interface enumeration completed.");
213         }
214         
215     } else if (n->nlmsg_type == NLMSG_ERROR &&
216                (n->nlmsg_seq == m->osdep.query_link_seq || n->nlmsg_seq == m->osdep.query_addr_seq)) {
217         struct nlmsgerr *e = NLMSG_DATA (n);
218                     
219         if (e->error)
220             avahi_log_warn("NETLINK: Failed to browse: %s", strerror(-e->error));
221     }
222 }
223
224 int avahi_interface_monitor_init_osdep(AvahiInterfaceMonitor *m) {
225     assert(m);
226
227     m->osdep.netlink = NULL;
228     m->osdep.query_addr_seq = m->osdep.query_link_seq = 0;
229     
230     if (!(m->osdep.netlink = avahi_netlink_new(m->server->poll_api, RTMGRP_LINK|RTMGRP_IPV4_IFADDR|RTMGRP_IPV6_IFADDR, netlink_callback, m)))
231         goto fail;
232
233     if (netlink_list_items(m->osdep.netlink, RTM_GETLINK, &m->osdep.query_link_seq) < 0)
234         goto fail;
235
236     m->osdep.list = LIST_IFACE;
237
238     return 0;
239
240 fail:
241
242     if (m->osdep.netlink) {
243         avahi_netlink_free(m->osdep.netlink);
244         m->osdep.netlink = NULL;
245     }
246
247     return -1;
248 }
249
250 void avahi_interface_monitor_free_osdep(AvahiInterfaceMonitor *m) {
251     assert(m);
252
253     if (m->osdep.netlink) {
254         avahi_netlink_free(m->osdep.netlink);
255         m->osdep.netlink = NULL;
256     }
257 }
258
259 void avahi_interface_monitor_sync(AvahiInterfaceMonitor *m) {
260     assert(m);
261     
262     while (m->osdep.list != LIST_DONE) {
263         if (!avahi_netlink_work(m->osdep.netlink, 1))
264             break;
265     } 
266 }