Fix bugs I introduced. Now
[metze/wireshark/wip.git] / iface_monitor.c
1 /* iface_monitor.c
2  * interface monitor by Pontus Fuchs <pontus.fuchs@gmail.com>
3  *
4  * $Id$
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23  */
24 #include "config.h"
25
26 #ifdef HAVE_LIBPCAP
27
28 #include "iface_monitor.h"
29
30 #if defined(HAVE_LIBNL)
31
32 /*
33  * Linux with libnl.
34  */
35
36 #include <stdio.h>
37 #include <strings.h>
38 #include <errno.h>
39
40 #if defined(HAVE_LIBNL1) || defined(HAVE_LIBNL2)
41 #include <net/if.h>
42 #endif
43
44 #include <netlink/msg.h>
45 #include <netlink/attr.h>
46 #include <netlink/route/link.h>
47
48 /* libnl 1.x compatibility code */
49 #ifdef HAVE_LIBNL1
50 #define nl_sock nl_handle
51 #define nl_socket_disable_seq_check nl_disable_sequence_check
52
53 static inline struct nl_handle *nl_socket_alloc(void)
54 {
55     return nl_handle_alloc();
56 }
57
58 static inline void nl_socket_free(struct nl_sock *h)
59 {
60     nl_handle_destroy(h);
61 }
62 #endif /* HAVE_LIBNL1 */
63
64 static struct nl_sock *iface_mon_sock;
65
66 static void
67 iface_mon_handler2(struct nl_object *obj, void *arg)
68 {
69     struct rtnl_link *filter;
70     struct rtnl_link *link_obj;
71     int flags, up;
72     char *ifname;
73     iface_mon_cb cb = arg;
74
75     filter = rtnl_link_alloc();
76     if (!filter) {
77         fprintf(stderr, "error allocating filter\n");
78         return;
79     }
80
81     if (nl_object_match_filter (obj, OBJ_CAST (filter)) == 0) {
82         rtnl_link_put(filter);
83         return;
84     }
85
86     link_obj = (struct rtnl_link *) obj;
87     flags = rtnl_link_get_flags (link_obj);
88     ifname = rtnl_link_get_name(link_obj);
89
90     /*
91      * You can't bind a PF_PACKET socket to an interface that's not
92      * up, so an interface going down is an "interface should be
93      * removed" indication.
94      *
95      * XXX - what indication, if any, do we get if the interface
96      * *completely goes away*?
97      *
98      * XXX - can we get events if an interface's link-layer or
99      * network addresses change?
100      */
101     up = (flags & IFF_UP) ? 1 : 0;
102
103     cb(ifname, up);
104
105     rtnl_link_put(filter);
106
107     return;
108 }
109
110 static int
111 iface_mon_handler(struct nl_msg *msg, void *arg)
112 {
113     nl_msg_parse (msg, &iface_mon_handler2, arg);
114     return 0;
115 }
116
117 void
118 iface_mon_event(void)
119 {
120     nl_recvmsgs_default(iface_mon_sock);
121 }
122
123 int
124 iface_mon_get_sock(void)
125 {
126     return nl_socket_get_fd(iface_mon_sock);
127 }
128
129 int
130 iface_mon_start(iface_mon_cb cb)
131 {
132     int err;
133
134     iface_mon_sock = nl_socket_alloc();
135     if (!iface_mon_sock) {
136         fprintf(stderr, "Failed to allocate netlink socket.\n");
137         return -ENOMEM;
138     }
139
140     nl_socket_disable_seq_check(iface_mon_sock);
141
142     nl_socket_modify_cb(iface_mon_sock, NL_CB_VALID, NL_CB_CUSTOM, iface_mon_handler, cb);
143
144     if (nl_connect(iface_mon_sock, NETLINK_ROUTE)) {
145         fprintf(stderr, "Failed to connect to generic netlink.\n");
146         err = -ENOLINK;
147         goto out_handle_destroy;
148     }
149
150     nl_socket_add_membership(iface_mon_sock, RTNLGRP_LINK);
151
152     return 0;
153
154 out_handle_destroy:
155     nl_socket_free(iface_mon_sock);
156     return err;
157 }
158
159 void
160 iface_mon_stop(void)
161 {
162     if(iface_mon_sock)
163         nl_socket_free(iface_mon_sock);
164     iface_mon_sock = NULL;
165 }
166
167 #elif defined(__APPLE__)
168
169 /*
170  * OS X.
171  */
172
173 #include <stddef.h>
174 #include <stdio.h>
175 #include <errno.h>
176 #include <unistd.h>
177
178 #include <sys/socket.h>
179 #include <sys/ioctl.h>
180 #include <sys/types.h>
181 #include <net/if.h>
182 #include <sys/kern_event.h>
183
184 #include <glib.h>
185
186 static int s;
187 static iface_mon_cb callback;
188
189 int
190 iface_mon_start(iface_mon_cb cb)
191 {
192     int ret;
193     struct kev_request key;
194
195     /* Create a socket of type PF_SYSTEM to listen for events. */
196     s = socket(PF_SYSTEM, SOCK_RAW, SYSPROTO_EVENT);
197     if (s == -1)
198         return -errno;
199
200     /*
201      * Ask for DLIL messages.
202      *
203      * XXX - also ask for KEV_INET_SUBCLASS and KEV_INET6_SUBCLASS,
204      * to detect new or changed network addresses, so those can be
205      * updated as well?  Can we specify multiple filters on a socket,
206      * or must we specify KEV_ANY_SUBCLASS and filter the events after
207      * receiving them?
208      */
209     key.vendor_code = KEV_VENDOR_APPLE;
210     key.kev_class = KEV_NETWORK_CLASS;
211     key.kev_subclass = KEV_DL_SUBCLASS;
212     if (ioctl(s, SIOCSKEVFILT, &key) == -1) {
213         ret = -errno;
214         close(s);
215         return ret;
216     }
217
218     callback = cb;
219     return 0;
220 }
221
222 void
223 iface_mon_stop(void)
224 {
225     close(s);
226 }
227
228 int
229 iface_mon_get_sock(void)
230 {
231     return s;
232 }
233
234 /*
235  * Size of buffer for kernel network event.
236  */
237 #define NET_EVENT_DATA_SIZE     (KEV_MSG_HEADER_SIZE + sizeof (struct net_event_data))
238
239 void
240 iface_mon_event(void)
241 {
242     char msg[NET_EVENT_DATA_SIZE];
243     ssize_t received;
244     struct kern_event_msg *kem;
245     struct net_event_data *evd;
246     int evd_len;
247     char ifr_name[IFNAMSIZ];
248
249     received = recv(s, msg, sizeof msg, 0);
250     if (received < 0) {
251         /* Error - ignore. */
252         return;
253     }
254     if ((size_t)received < sizeof msg) {
255         /* Short read - ignore. */
256         return;
257     }
258     kem = (struct kern_event_msg *)msg;
259     evd_len = kem->total_size - KEV_MSG_HEADER_SIZE;
260     if (evd_len != sizeof (struct net_event_data)) {
261         /* Length of the message is bogus. */
262         return;
263     }
264     evd = (struct net_event_data *)&kem->event_data[0];
265     g_snprintf(ifr_name, IFNAMSIZ, "%s%u", evd->if_name, evd->if_unit);
266
267     /*
268      * Check type of event.
269      *
270      * Note: if we also ask for KEV_INET_SUBCLASS, we will get
271      * events with keys
272      *
273      *    KEV_INET_NEW_ADDR
274      *    KEV_INET_CHANGED_ADDR
275      *    KEV_INET_CHANGED_ADDR
276      *    KEV_INET_SIFDSTADDR
277      *    KEV_INET_SIFBRDADDR
278      *    KEV_INET_SIFNETMASK
279      *
280      * reflecting network address changes, with the data being a
281      * struct kev_in_data rather than struct net_event_data, and
282      * if we also ask for KEV_INET6_SUBCLASS, we will get events
283      * with keys
284      *
285      *    KEV_INET6_NEW_LL_ADDR
286      *    KEV_INET6_NEW_USER_ADDR
287      *    KEV_INET6_NEW_RTADV_ADDR
288      *    KEV_INET6_ADDR_DELETED
289      *
290      * with the data being a struct kev_in6_data.
291      */
292     switch (kem->event_code) {
293
294     case KEV_DL_IF_ATTACHED:
295         /*
296          * A new interface has arrived.
297          *
298          * XXX - what we really want is "a new BPFable interface
299          * has arrived", but that's not available.  While we're
300          * asking for additional help from BPF, it'd also be
301          * nice if we could ask it for a list of all interfaces
302          * that have had bpfattach()/bpf_attach() done on them,
303          * so we don't have to try to open the device in order
304          * to see whether we should show it as something on
305          * which we can capture.
306          */
307         callback(ifr_name, 1);
308         break;
309
310     case KEV_DL_IF_DETACHED:
311         /*
312          * An existing interface has been removed.
313          *
314          * XXX - use KEV_DL_IF_DETACHING instead, as that's
315          * called shortly after bpfdetach() is called, and
316          * bpfdetach() makes an interface no longer BPFable,
317          * and that's what we *really* care about.
318          */
319         callback(ifr_name, 0);
320         break;
321
322     default:
323         /*
324          * Is there any reason to care about:
325          *
326          *    KEV_DL_LINK_ON
327          *    KEV_DL_LINK_OFF
328          *    KEV_DL_SIFFLAGS
329          *    KEV_DL_LINK_ADDRESS_CHANGED
330          *    KEV_DL_IFCAP_CHANGED
331          *
332          * or any of the other events?  On Snow Leopard and, I think,
333          * earlier releases, you can't attach a BPF device to an
334          * interface that's not up, so KEV_DL_SIFFLAGS might be
335          * worth listening to so that we only say "here's a new
336          * interface" when it goes up; on Lion (and possibly Mountain
337          * Lion), an interface doesn't have to be up in order to
338          * have a BPF device attached to it.
339          */
340         break;
341     }
342 }
343
344 #else /* don't have something we support */
345
346 int
347 iface_mon_start(iface_mon_cb cb _U_)
348 {
349     return -1;
350 }
351
352 void
353 iface_mon_stop(void)
354 {
355 }
356
357 int
358 iface_mon_get_sock(void)
359 {
360     return -1;
361 }
362
363 void
364 iface_mon_event(void)
365 {
366 }
367
368 #endif /* HAVE_LIBNL */
369
370 #endif /* HAVE_LIBPCAP */