2 This file is part of catta.
4 catta 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.
9 catta 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.
14 You should have received a copy of the GNU Lesser General Public
15 License along with catta; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
29 #include <catta/malloc.h>
31 #include <catta/log.h>
33 #include "iface-linux.h"
36 #include <linux/if_addr.h>
37 #define IFLA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
41 #include <linux/if_addr.h>
42 #define IFA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
45 static int netlink_list_items(CattaNetlink *nl, uint16_t type, unsigned *ret_seq) {
50 /* Issue a wild dump NETLINK request */
52 memset(&req, 0, sizeof(req));
53 n = (struct nlmsghdr*) req;
54 n->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
56 n->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP;
60 memset(gen, 0, sizeof(struct rtgenmsg));
61 gen->rtgen_family = AF_UNSPEC;
63 return catta_netlink_send(nl, n, ret_seq);
66 static void netlink_callback(CattaNetlink *nl, struct nlmsghdr *n, void* userdata) {
67 CattaInterfaceMonitor *m = userdata;
69 /* This routine is called for every RTNETLINK response packet */
73 assert(m->osdep.netlink == nl);
75 if (n->nlmsg_type == RTM_NEWLINK) {
77 /* A new interface appeared or an existing one has been modified */
79 struct ifinfomsg *ifinfomsg = NLMSG_DATA(n);
81 struct rtattr *a = NULL;
84 /* A (superfluous?) sanity check */
85 if (ifinfomsg->ifi_family != AF_UNSPEC)
88 /* Check whether there already is an CattaHwInterface 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 */
93 if (!(hw = catta_interface_monitor_get_hw_interface(m, ifinfomsg->ifi_index)))
95 /* No object found, so let's create a new
96 * one. catta_hw_interface_new() will call
97 * catta_interface_new() internally twice for IPv4 and
98 * IPv6, so there is no need for us to do that
100 if (!(hw = catta_hw_interface_new(m, (CattaIfIndex) ifinfomsg->ifi_index)))
103 /* Check whether the flags of this interface are OK for us */
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));
111 /* Handle interface attributes */
112 l = NLMSG_PAYLOAD(n, sizeof(struct ifinfomsg));
113 a = IFLA_RTA(ifinfomsg);
115 while (RTA_OK(a, l)) {
116 switch(a->rta_type) {
119 /* Fill in interface name */
120 catta_free(hw->name);
121 hw->name = catta_strndup(RTA_DATA(a), RTA_PAYLOAD(a));
127 assert(RTA_PAYLOAD(a) == sizeof(unsigned int));
128 hw->mtu = *((unsigned int*) RTA_DATA(a));
133 /* Fill in hardware (MAC) address */
134 hw->mac_address_size = RTA_PAYLOAD(a);
135 if (hw->mac_address_size > CATTA_MAC_ADDRESS_MAX)
136 hw->mac_address_size = CATTA_MAC_ADDRESS_MAX;
138 memcpy(hw->mac_address, RTA_DATA(a), hw->mac_address_size);
148 /* Check whether this interface is now "relevant" for us. If
149 * it is Catta will start to announce its records on this
150 * interface and send out queries for subscribed records on
152 catta_hw_interface_check_relevant(hw);
154 /* Update any associated RRs of this interface. (i.e. the
155 * _workstation._tcp record containing the MAC address) */
156 catta_hw_interface_update_rrs(hw, 0);
158 } else if (n->nlmsg_type == RTM_DELLINK) {
160 /* An interface has been removed */
162 struct ifinfomsg *ifinfomsg = NLMSG_DATA(n);
163 CattaHwInterface *hw;
165 /* A (superfluous?) sanity check */
166 if (ifinfomsg->ifi_family != AF_UNSPEC)
169 /* Get a reference to our CattaHwInterface object of this interface */
170 if (!(hw = catta_interface_monitor_get_hw_interface(m, (CattaIfIndex) ifinfomsg->ifi_index)))
173 /* Free our object */
174 catta_hw_interface_free(hw, 0);
176 } else if (n->nlmsg_type == RTM_NEWADDR || n->nlmsg_type == RTM_DELADDR) {
178 /* An address has been added, modified or removed */
180 struct ifaddrmsg *ifaddrmsg = NLMSG_DATA(n);
182 struct rtattr *a = NULL;
184 CattaAddress raddr, rlocal, *r;
185 int raddr_valid = 0, rlocal_valid = 0;
187 /* We are only interested in IPv4 and IPv6 */
188 if (ifaddrmsg->ifa_family != AF_INET && ifaddrmsg->ifa_family != AF_INET6)
191 /* Try to get a reference to our CattaInterface object for the
192 * interface this address is assigned to. If ther is no object
193 * for this interface, we ignore this address. */
194 if (!(i = catta_interface_monitor_get_interface(m, (CattaIfIndex) ifaddrmsg->ifa_index, catta_af_to_proto(ifaddrmsg->ifa_family))))
197 /* Fill in address family for our new address */
198 rlocal.proto = raddr.proto = catta_af_to_proto(ifaddrmsg->ifa_family);
200 l = NLMSG_PAYLOAD(n, sizeof(struct ifaddrmsg));
201 a = IFA_RTA(ifaddrmsg);
203 while (RTA_OK(a, l)) {
205 switch(a->rta_type) {
209 if ((rlocal.proto == CATTA_PROTO_INET6 && RTA_PAYLOAD(a) != 16) ||
210 (rlocal.proto == CATTA_PROTO_INET && RTA_PAYLOAD(a) != 4))
213 memcpy(rlocal.data.data, RTA_DATA(a), RTA_PAYLOAD(a));
220 /* Fill in local address data. Usually this is
221 * preferable over IFA_ADDRESS if both are set,
222 * since this refers to the local address of a PPP
223 * link while IFA_ADDRESS refers to the other
226 if ((raddr.proto == CATTA_PROTO_INET6 && RTA_PAYLOAD(a) != 16) ||
227 (raddr.proto == CATTA_PROTO_INET && RTA_PAYLOAD(a) != 4))
230 memcpy(raddr.data.data, RTA_DATA(a), RTA_PAYLOAD(a));
242 /* If there was no adress attached to this message, let's quit. */
245 else if (raddr_valid)
250 if (n->nlmsg_type == RTM_NEWADDR) {
251 CattaInterfaceAddress *addr;
253 /* This address is new or has been modified, so let's get an object for it */
254 if (!(addr = catta_interface_monitor_get_address(m, i, r)))
256 /* Mmm, no object existing yet, so let's create a new one */
257 if (!(addr = catta_interface_address_new(m, i, r, ifaddrmsg->ifa_prefixlen)))
260 /* Update the scope field for the address */
261 addr->global_scope = ifaddrmsg->ifa_scope == RT_SCOPE_UNIVERSE || ifaddrmsg->ifa_scope == RT_SCOPE_SITE;
262 addr->deprecated = !!(ifaddrmsg->ifa_flags & IFA_F_DEPRECATED);
264 CattaInterfaceAddress *addr;
265 assert(n->nlmsg_type == RTM_DELADDR);
267 /* Try to get a reference to our CattaInterfaceAddress object for this address */
268 if (!(addr = catta_interface_monitor_get_address(m, i, r)))
272 catta_interface_address_free(addr);
275 /* Catta only considers interfaces with at least one address
276 * attached relevant. Since we migh have added or removed an
277 * address, let's have it check again whether the interface is
279 catta_interface_check_relevant(i);
281 /* Update any associated RRs, like A or AAAA for our new/removed address */
282 catta_interface_update_rrs(i, 0);
284 } else if (n->nlmsg_type == NLMSG_DONE) {
286 /* This wild dump request ended, so let's see what we do next */
288 if (m->osdep.list == LIST_IFACE) {
290 /* Mmmm, interfaces have been wild dumped already, so
291 * let's go on with wild dumping the addresses */
293 if (netlink_list_items(m->osdep.netlink, RTM_GETADDR, &m->osdep.query_addr_seq) < 0) {
294 catta_log_warn("NETLINK: Failed to list addrs: %s", strerror(errno));
295 m->osdep.list = LIST_DONE;
298 /* Update state information */
299 m->osdep.list = LIST_ADDR;
302 /* We're done. Tell catta_interface_monitor_sync() to finish. */
303 m->osdep.list = LIST_DONE;
305 if (m->osdep.list == LIST_DONE) {
307 /* Only after this boolean variable has been set, Catta
308 * will start to announce or browse on all interfaces. It
309 * is originaly set to 0, which means that relevancy
310 * checks and RR updates are disabled during the wild
312 m->list_complete = 1;
314 /* So let's check if any interfaces are relevant now */
315 catta_interface_monitor_check_relevant(m);
317 /* And update all RRs attached to any interface */
318 catta_interface_monitor_update_rrs(m, 0);
320 /* Tell the user that the wild dump is complete */
321 catta_log_info("Network interface enumeration completed.");
324 } else if (n->nlmsg_type == NLMSG_ERROR &&
325 (n->nlmsg_seq == m->osdep.query_link_seq || n->nlmsg_seq == m->osdep.query_addr_seq)) {
326 struct nlmsgerr *e = NLMSG_DATA (n);
328 /* Some kind of error happened. Let's just tell the user and
329 * ignore it otherwise */
332 catta_log_warn("NETLINK: Failed to browse: %s", strerror(-e->error));
336 int catta_interface_monitor_init_osdep(CattaInterfaceMonitor *m) {
339 /* Initialize our own data */
341 m->osdep.netlink = NULL;
342 m->osdep.query_addr_seq = m->osdep.query_link_seq = 0;
344 /* Create a netlink object for us. It abstracts some things and
345 * makes netlink easier to use. It will attach to the main loop
346 * for us and call netlink_callback() whenever an event
348 if (!(m->osdep.netlink = catta_netlink_new(m->server->poll_api, RTMGRP_LINK|RTMGRP_IPV4_IFADDR|RTMGRP_IPV6_IFADDR, netlink_callback, m)))
351 /* Set the initial state. */
352 m->osdep.list = LIST_IFACE;
354 /* Start the wild dump for the interfaces */
355 if (netlink_list_items(m->osdep.netlink, RTM_GETLINK, &m->osdep.query_link_seq) < 0)
362 if (m->osdep.netlink) {
363 catta_netlink_free(m->osdep.netlink);
364 m->osdep.netlink = NULL;
370 void catta_interface_monitor_free_osdep(CattaInterfaceMonitor *m) {
373 if (m->osdep.netlink) {
374 catta_netlink_free(m->osdep.netlink);
375 m->osdep.netlink = NULL;
379 void catta_interface_monitor_sync(CattaInterfaceMonitor *m) {
382 /* Let's handle netlink events until we are done with wild
385 while (!m->list_complete)
386 if (!catta_netlink_work(m->osdep.netlink, 1) == 0)
389 /* At this point Catta knows about all local interfaces and
390 * addresses in existance. */