libpcap disable monitor mode (C, macOS)

71 Views Asked by At

I have a function that enables monitor mode for a WiFi device on macOS using libpcap. This works, but currently the only way to set the WiFi device back to managed mode is to exit the process. I would like to set the WiFi device to managed mode while allowing the process to continue. As far as I can tell, this can't be done with libpcap. How would I set the WiFi device back to managed mode (potentially using the CoreWLAN API in Objective-C?)?

pcap_t *enable_monitor(char *dev) {
    wifi_disassociate();

    char error_buffer[PCAP_ERRBUF_SIZE];
    pcap_t *handle;
    int result;

    handle = pcap_create(dev, error_buffer);
    if (handle == NULL) {
        printf("failed to create a handle: %s\n",
               error_buffer);
        return NULL;
    }
    result = pcap_set_rfmon(handle, 1);
    if (result != 0) {
        printf("failed to set pcap rfmon: %s (%s)\n",
               pcap_statustostr(result),
               pcap_geterr(handle));
        return NULL;
    }
    result = pcap_activate(handle);
    if (result != 0) {
        printf("failed to activate handle: %s (%s)\n",
               pcap_statustostr(result),
               pcap_geterr(handle));
        return NULL;
    }
    //pcap_close(handle);
    return handle;
}

Edit: Setting the second argument to cap_set_rfmon to 0 does not seem to set the device to managed mode, it just doesn't set it to monitor mode.

int disable_monitor(pcap_t *handle) {
    int result;
    result = pcap_set_rfmon(handle, 0);
    if (result != 0) {
        printf("failed to set pcap rfmon: %s (%s)\n",
               pcap_statustostr(result),
               pcap_geterr(handle));
        return 1;
    }
    pcap_close(handle);
    return 0;
}

int main() {
    char *dev = find_wifi_device();
    pcap_t *handle = enable_monitor(dev);
    sleep(5);
    int status = disable_monitor(handle);
    printf("Status: %d\n", status);
    for (;;)
        sleep(100);
}

This results in:

failed to set pcap rfmon: The setting can't be changed after the pcap_t is activated (can't perform  operation on activated capture)
Status: 1
2

There are 2 best solutions below

0
Craig Estey On BEST ANSWER

Prefaced by my top comments ...

From man pcap_set_rfmon, it sets the mode. But, it only takes effect "when activated" (i.e. pcap_activate).

Further, if called on an already activated handle, the call returns the PCAP_ERROR_ACTIVATED error.

I've looked through the libpcap source code and ...

Once a connection has been activated, I can find no pcap_* call that will "deactivate" it. So, we may have to close the handle and open a fresh instance.

Also, AFAICT, do pcap_set_rfmon with 0 clears a flag (e.g. pcap->opt.rfmon). But, with a new instance (i.e. before calling pcap_activate), this should already be clear, so this is an effective no-op.

The interface is already in a given mode from the first pcap_activate call. Generally, it doesn't "reset" if we call pcap_close (Note: For some interfaces, the interface will be marked to reset upon pcap_close but we probably can't rely on that in general).

What is the default mode? I'm guessing promiscuous mode, so to reset monitor mode, we may have to use pcap_set_promisc instead.

That worked. Thank you. pcap_set_promisc works without a second pcap_set_rfmon. – Benjamin Mickler

0
user16139739 On

If you only care about macOS, then:

The way to enable monitor mode on macOS is to use the BIOCSDLT ioctl on the file descriptor for the raw BPF device (which you can get using the pcap_fileno() call on the pcap_t *) to set the link-layer type to DLT_IEEE802_11_RADIO (which, as far as I know, all Wi-Fi adapter drivers on macOS support), and to disable it is to use the same ioctl to set it to DLT_EN10MB. (That's what libpcap uses.)

See the bpf(4) man page for details on that ioctl.

Note that this will NOT work on any other operating system. They all have their own different ways to set monitor mode.

Note that if you're capturing traffic on the BPF device, once you change the link-layer type, the packets that arrive after the change will have different link-layer headers.

Note also that, at least with drivers for the adapters on newer Macs, and with newer versions of macOS, attempting to go into monitor mode will prevent packets from being captured unless you somehow force the adapter to disconnect from the network. (That's what the "Sniffer" menu item in "Wireless Diagnostics" does - it disconnects you from your network, and may do other magic, and then fires up tcpdump with the -I option to capture in monitor mode, writing out capture files.)