Revert "UNUSED enc_key_id_{equal,hash}"
[wireshark-sm.git] / capture / iface_monitor.c
blob1c74cba944f696a91baa0b1daa998c2fecfb9dda
1 /* iface_monitor.c
2 * interface monitor by Pontus Fuchs <pontus.fuchs@gmail.com>
4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <gerald@wireshark.org>
6 * Copyright 1998 Gerald Combs
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
11 #include "config.h"
12 #include "iface_monitor.h"
14 #ifdef HAVE_LIBPCAP
16 #if defined(HAVE_LIBNL)
19 * Linux with libnl.
21 * Use Netlink to get indications of new/removed intrfaces.
24 #include <stdio.h>
25 #include <string.h>
26 #include <errno.h>
28 DIAG_OFF_PEDANTIC
29 #include <netlink/msg.h>
30 DIAG_ON_PEDANTIC
31 #include <netlink/attr.h>
32 DIAG_OFF_PEDANTIC
33 #include <netlink/route/link.h>
34 DIAG_ON_PEDANTIC
36 #ifndef IFF_UP
38 * Apparently, some versions of libnl drag in headers that define IFF_UP
39 * and others don't. Include <net/if.h> iff IFF_UP isn't already defined,
40 * so that if <linux/if.h> has been included by some or all of the
41 * netlink headers, we don't include <net/if.h> and get a bunch of
42 * complaints about various structures being redefined.
44 #include <net/if.h>
45 #endif
47 /* libnl 1.x compatibility code */
48 #ifdef HAVE_LIBNL1
49 #define nl_sock nl_handle
50 #define nl_socket_disable_seq_check nl_disable_sequence_check
52 static inline struct nl_handle *nl_socket_alloc(void)
54 return nl_handle_alloc();
57 static inline void nl_socket_free(struct nl_sock *h)
59 nl_handle_destroy(h);
61 #endif /* HAVE_LIBNL1 */
63 static struct nl_sock *iface_mon_sock;
65 static void
66 iface_mon_handler2(struct nl_object *obj, void *arg)
68 struct rtnl_link *filter;
69 struct rtnl_link *link_obj;
70 int flags, up;
71 char *ifname;
72 iface_mon_cb cb = (iface_mon_cb)arg;
74 filter = rtnl_link_alloc();
75 if (!filter) {
76 fprintf(stderr, "error allocating filter\n");
77 return;
80 if (nl_object_match_filter (obj, OBJ_CAST (filter)) == 0) {
81 rtnl_link_put(filter);
82 return;
85 link_obj = (struct rtnl_link *) obj;
86 flags = rtnl_link_get_flags (link_obj);
87 ifname = rtnl_link_get_name(link_obj);
90 * You can't bind a PF_PACKET socket to an interface that's not
91 * up, so an interface going down is an "interface should be
92 * removed" indication.
94 * XXX - what indication, if any, do we get if the interface
95 * *completely goes away*?
97 * XXX - can we get events if an interface's link-layer or
98 * network addresses change?
100 up = (flags & IFF_UP) ? 1 : 0;
102 #ifdef HAVE_LIBNL1
103 cb(ifname, 0, up);
104 #else
105 int msg_type = nl_object_get_msgtype(obj);
107 switch (msg_type) {
108 case RTM_NEWLINK:
109 cb(ifname, 1, up);
110 break;
111 case RTM_DELLINK:
112 cb(ifname, 0, 0);
113 break;
114 default:
115 /* Ignore other events */
116 break;
118 #endif
120 rtnl_link_put(filter);
122 return;
125 static int
126 iface_mon_handler(struct nl_msg *msg, void *arg)
128 nl_msg_parse (msg, &iface_mon_handler2, arg);
129 return 0;
132 void
133 iface_mon_event(void)
135 nl_recvmsgs_default(iface_mon_sock);
139 iface_mon_get_sock(void)
141 return nl_socket_get_fd(iface_mon_sock);
145 iface_mon_start(iface_mon_cb cb)
147 int err;
149 iface_mon_sock = nl_socket_alloc();
150 if (!iface_mon_sock) {
151 fprintf(stderr, "Failed to allocate netlink socket.\n");
152 return -ENOMEM;
155 nl_socket_disable_seq_check(iface_mon_sock);
157 nl_socket_modify_cb(iface_mon_sock, NL_CB_VALID, NL_CB_CUSTOM, iface_mon_handler, (void *)cb);
159 if (nl_connect(iface_mon_sock, NETLINK_ROUTE)) {
160 fprintf(stderr, "Failed to connect to generic netlink.\n");
161 err = -ENOLINK;
162 goto out_handle_destroy;
165 nl_socket_add_membership(iface_mon_sock, RTNLGRP_LINK);
167 return 0;
169 out_handle_destroy:
170 nl_socket_free(iface_mon_sock);
171 return err;
174 void
175 iface_mon_stop(void)
177 if(iface_mon_sock)
178 nl_socket_free(iface_mon_sock);
179 iface_mon_sock = NULL;
182 void
183 iface_mon_enable(bool enable)
185 if (!iface_mon_sock)
186 return;
188 if (enable) {
189 nl_socket_add_membership(iface_mon_sock, RTNLGRP_LINK);
190 } else {
191 nl_socket_drop_membership(iface_mon_sock, RTNLGRP_LINK);
195 #elif defined(__APPLE__)
198 * macOS.
200 * Use a PF_SYSTEM socket to get indications of new/removed intrfaces.
203 #include <stddef.h>
204 #include <stdio.h>
205 #include <errno.h>
206 #include <unistd.h>
208 #include <sys/socket.h>
209 #include <sys/ioctl.h>
210 #include <sys/types.h>
211 #include <net/if.h>
212 #include <sys/kern_event.h>
214 #include <glib.h>
216 static int s;
217 static iface_mon_cb callback;
220 iface_mon_start(iface_mon_cb cb)
222 int ret;
223 struct kev_request key;
225 /* Create a socket of type PF_SYSTEM to listen for events. */
226 s = socket(PF_SYSTEM, SOCK_RAW, SYSPROTO_EVENT);
227 if (s == -1)
228 return -errno;
231 * Ask for DLIL messages.
233 * XXX - also ask for KEV_INET_SUBCLASS and KEV_INET6_SUBCLASS,
234 * to detect new or changed network addresses, so those can be
235 * updated as well? Can we specify multiple filters on a socket,
236 * or must we specify KEV_ANY_SUBCLASS and filter the events after
237 * receiving them?
239 key.vendor_code = KEV_VENDOR_APPLE;
240 key.kev_class = KEV_NETWORK_CLASS;
241 key.kev_subclass = KEV_DL_SUBCLASS;
242 if (ioctl(s, SIOCSKEVFILT, &key) == -1) {
243 ret = -errno;
244 close(s);
245 return ret;
248 callback = cb;
249 return 0;
252 void
253 iface_mon_stop(void)
255 close(s);
259 iface_mon_get_sock(void)
261 return s;
265 * Size of buffer for kernel network event.
267 #define NET_EVENT_DATA_SIZE (KEV_MSG_HEADER_SIZE + sizeof (struct net_event_data))
269 void
270 iface_mon_event(void)
272 char msg[NET_EVENT_DATA_SIZE];
273 ssize_t received;
274 struct kern_event_msg *kem;
275 struct net_event_data *evd;
276 size_t evd_len;
277 char ifr_name[IFNAMSIZ];
279 received = recv(s, msg, sizeof msg, 0);
280 if (received < 0) {
281 /* Error - ignore. */
282 return;
284 if ((size_t)received < sizeof msg) {
285 /* Short read - ignore. */
286 return;
288 kem = (struct kern_event_msg *)msg;
289 evd_len = kem->total_size - KEV_MSG_HEADER_SIZE;
290 if (evd_len != sizeof (struct net_event_data)) {
291 /* Length of the message is bogus. */
292 return;
294 evd = (struct net_event_data *)&kem->event_data[0];
295 snprintf(ifr_name, IFNAMSIZ, "%s%u", evd->if_name, evd->if_unit);
298 * Check type of event.
300 * Note: if we also ask for KEV_INET_SUBCLASS, we will get
301 * events with keys
303 * KEV_INET_NEW_ADDR
304 * KEV_INET_CHANGED_ADDR
305 * KEV_INET_CHANGED_ADDR
306 * KEV_INET_SIFDSTADDR
307 * KEV_INET_SIFBRDADDR
308 * KEV_INET_SIFNETMASK
310 * reflecting network address changes, with the data being a
311 * struct kev_in_data rather than struct net_event_data, and
312 * if we also ask for KEV_INET6_SUBCLASS, we will get events
313 * with keys
315 * KEV_INET6_NEW_LL_ADDR
316 * KEV_INET6_NEW_USER_ADDR
317 * KEV_INET6_NEW_RTADV_ADDR
318 * KEV_INET6_ADDR_DELETED
320 * with the data being a struct kev_in6_data.
322 switch (kem->event_code) {
324 case KEV_DL_IF_ATTACHED:
326 * A new interface has arrived.
328 * XXX - what we really want is "a new BPFable interface
329 * has arrived", but that's not available. While we're
330 * asking for additional help from BPF, it'd also be
331 * nice if we could ask it for a list of all interfaces
332 * that have had bpfattach()/bpf_attach() done on them,
333 * so we don't have to try to open the device in order
334 * to see whether we should show it as something on
335 * which we can capture.
337 callback(ifr_name, 1, 1);
338 break;
340 case KEV_DL_IF_DETACHED:
342 * An existing interface has been removed.
344 * XXX - use KEV_DL_IF_DETACHING instead, as that's
345 * called shortly after bpfdetach() is called, and
346 * bpfdetach() makes an interface no longer BPFable,
347 * and that's what we *really* care about.
349 callback(ifr_name, 0, 0);
350 break;
352 default:
354 * Is there any reason to care about:
356 * KEV_DL_LINK_ON
357 * KEV_DL_LINK_OFF
358 * KEV_DL_SIFFLAGS
359 * KEV_DL_LINK_ADDRESS_CHANGED
360 * KEV_DL_IFCAP_CHANGED
362 * or any of the other events? On Snow Leopard and, I think,
363 * earlier releases, you can't attach a BPF device to an
364 * interface that's not up, so KEV_DL_SIFFLAGS might be
365 * worth listening to so that we only say "here's a new
366 * interface" when it goes up; on Lion (and possibly Mountain
367 * Lion), an interface doesn't have to be up in order to
368 * have a BPF device attached to it.
370 break;
374 void
375 iface_mon_enable(bool enable _U_)
379 #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
382 * FreeBSD, NetBSD, OpenBSD, DragonFly BSD.
384 * Use a PF_ROUTE socket to get indications of new/removed intrfaces.
386 #include <stdio.h>
387 #include <stdlib.h>
388 #include <string.h>
389 #include <errno.h>
390 #include <unistd.h>
392 #include <sys/types.h>
393 #include <sys/socket.h>
394 #include <net/if.h>
395 #include <net/route.h>
396 #include <net/if_dl.h>
398 #include <netinet/in.h>
399 #include <netinet/in_var.h>
401 static int s;
402 static iface_mon_cb callback;
405 iface_mon_start(iface_mon_cb cb)
407 #ifdef RO_MSGFILTER
408 unsigned char msgfilter[] = {
409 RTM_IFANNOUNCE,
411 #endif
413 /* Create a socket of type PF_ROUTE to listen for events. */
414 s = socket(PF_ROUTE, SOCK_RAW, 0);
415 if (s == -1)
416 return -errno;
418 #ifdef RO_MSGFILTER
420 * This OS supports filtering PF_ROUTE sockets for specific
421 * events; we only want interface announcement events.
423 * If this fails, we just live with extra events that we ignore,
424 * which we also do on platforms that don't support filtering.
426 (void) setsockopt(s, PF_ROUTE, RO_MSGFILTER, &msgfilter, sizeof msgfilter);
427 #endif
428 #ifdef SO_RERROR
430 * This OS supports getting error reports from recvmsg() if a
431 * receive buffer overflow occurs. If that happens, it means
432 * that we may have lost interface reports, so we should
433 * probably just refetch all interface data.
435 * If we can't get those error reports, we're out of luck.
437 int n = 1;
438 (void) setsockopt(s, SOL_SOCKET, SO_RERROR, &n, sizeof(n));
439 #endif
441 callback = cb;
442 return 0;
445 void
446 iface_mon_stop(void)
448 close(s);
452 iface_mon_get_sock(void)
454 return s;
457 void
458 iface_mon_event(void)
460 union msgbuf {
461 char buf[2048];
462 struct rt_msghdr hd;
463 struct if_announcemsghdr ifan;
464 } msgbuf;
465 struct iovec iov[1];
466 struct msghdr msg;
467 bool message_seen = false;
468 ssize_t received;
470 iov[0].iov_base = &msgbuf;
471 iov[0].iov_len = sizeof msgbuf;
472 memset(&msg, 0, sizeof msg);
473 msg.msg_iov = iov;
474 msg.msg_iovlen = 1;
476 while (!message_seen) {
477 received = recvmsg(s, &msg, 0);
478 if (received == -1) {
479 if (errno == ENOBUFS) {
481 * Receive buffer overflow. Keep reading,
482 * to get any messages in the socket buffer.
484 * XXX - that means we may have lost indications;
485 * should there be a callback that indicates that
486 * all interface data should be refreshed?
488 continue;
489 } else {
491 * Other error - just ignore.
493 return;
498 * We've seen a message.
500 message_seen = true;
502 if (received != 0) {
504 * XXX - should we check received, to make sure it's large
505 * enough?
507 if (msgbuf.hd.rtm_version != RTM_VERSION)
508 return;
510 switch (msgbuf.hd.rtm_type) {
512 case RTM_IFANNOUNCE:
513 switch (msgbuf.ifan.ifan_what) {
515 case IFAN_ARRIVAL:
517 * A new interface has arrived.
519 * XXX - see comment about interface arrivals in the
520 * macOS section; it applies here as well.
522 callback(msgbuf.ifan.ifan_name, 1, 1);
523 break;
525 case IFAN_DEPARTURE:
527 * An existing interface has been removed.
529 callback(msgbuf.ifan.ifan_name, 0, 0);
530 break;
532 default:
534 * Ignore other notifications.
536 break;
538 break;
540 default:
542 * Ignore other messages.
544 break;
549 void
550 iface_mon_enable(bool enable _U_)
554 #else /* don't have something we support */
557 iface_mon_start(iface_mon_cb cb _U_)
559 return -1;
562 void
563 iface_mon_stop(void)
568 iface_mon_get_sock(void)
570 return -1;
573 void
574 iface_mon_event(void)
578 void
579 iface_mon_enable(bool enable _U_)
583 #endif /* HAVE_LIBNL */
585 #endif /* HAVE_LIBPCAP */
588 * Editor modelines - https://www.wireshark.org/tools/modelines.html
590 * Local variables:
591 * c-basic-offset: 4
592 * tab-width: 8
593 * indent-tabs-mode: nil
594 * End:
596 * vi: set shiftwidth=4 tabstop=8 expandtab:
597 * :indentSize=4:tabSize=8:noTabs=true: