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