From fa93fc26f1f0090ad553c002ee6d657f86c98660 Mon Sep 17 00:00:00 2001 From: Robert McMahon Date: Fri, 13 Feb 2026 13:44:28 -0800 Subject: [PATCH] Add tshark test script and fix monitor mode setup - Add test_monitor_tshark.sh to verify monitor mode works with tshark - Fix set_monitor_mode to wait for Netlink response - Improve libpcap initialization with pcap_create/pcap_set_rfmon/pcap_activate - Add interface up/down control before/after setting monitor mode - Add verification step to confirm monitor mode was set correctly Co-authored-by: Cursor --- src/capture.c | 45 +++++++++++-- src/monitor.c | 144 +++++++++++++++++++++++++++++++++++++++++ test_monitor_tshark.sh | 99 ++++++++++++++++++++++++++++ 3 files changed, 284 insertions(+), 4 deletions(-) create mode 100755 test_monitor_tshark.sh diff --git a/src/capture.c b/src/capture.c index f8fbae9..7954bd9 100644 --- a/src/capture.c +++ b/src/capture.c @@ -22,19 +22,56 @@ static void packet_handler(u_char *user, const struct pcap_pkthdr *h, const u_ch int start_capture(const char *interface, packet_callback_t callback, void *user_data) { char errbuf[PCAP_ERRBUF_SIZE]; + (void)user_data; // Unused + if (capture_running) { fprintf(stderr, "Capture already running\n"); return -1; } - // Open interface for capture - pcap_handle = pcap_open_live(interface, 65535, 1, 1000, errbuf); + // Use pcap_create + pcap_set_rfmon + pcap_activate for better monitor mode support + pcap_handle = pcap_create(interface, errbuf); if (!pcap_handle) { - fprintf(stderr, "Failed to open interface %s: %s\n", interface, errbuf); + fprintf(stderr, "Failed to create pcap handle for %s: %s\n", interface, errbuf); return -1; } - // Set non-blocking mode + // Set monitor mode (promiscuous mode) + if (pcap_set_rfmon(pcap_handle, 1) != 0) { + fprintf(stderr, "Warning: Failed to set monitor mode: %s\n", pcap_geterr(pcap_handle)); + // Continue anyway - interface might already be in monitor mode + } + + // Set buffer size + if (pcap_set_buffer_size(pcap_handle, 65535) != 0) { + fprintf(stderr, "Warning: Failed to set buffer size: %s\n", pcap_geterr(pcap_handle)); + } + + // Set timeout + if (pcap_set_timeout(pcap_handle, 1000) != 0) { + fprintf(stderr, "Warning: Failed to set timeout: %s\n", pcap_geterr(pcap_handle)); + } + + // Activate the handle + int activate_result = pcap_activate(pcap_handle); + if (activate_result < 0) { + if (activate_result == PCAP_ERROR_ACTIVATED) { + fprintf(stderr, "Error: Handle already activated\n"); + } else if (activate_result == PCAP_ERROR_NO_SUCH_DEVICE) { + fprintf(stderr, "Error: Interface %s not found\n", interface); + } else if (activate_result == PCAP_ERROR_PERM_DENIED) { + fprintf(stderr, "Error: Permission denied. Try running with sudo\n"); + } else { + fprintf(stderr, "Failed to activate pcap handle: %s\n", pcap_geterr(pcap_handle)); + } + pcap_close(pcap_handle); + pcap_handle = NULL; + return -1; + } else if (activate_result > 0) { + fprintf(stderr, "Warning: pcap_activate returned warning: %s\n", pcap_geterr(pcap_handle)); + } + + // Set non-blocking mode (0 = blocking) if (pcap_setnonblock(pcap_handle, 0, errbuf) < 0) { fprintf(stderr, "Warning: Failed to set blocking mode: %s\n", errbuf); } diff --git a/src/monitor.c b/src/monitor.c index 7f1ca1e..10d406b 100644 --- a/src/monitor.c +++ b/src/monitor.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -49,6 +50,39 @@ static void nl80211_cleanup(void) { } } +// Helper function to bring interface up/down using ioctl +static int set_interface_up(const char *interface, int up) { + int sockfd; + struct ifreq ifr; + + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) { + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, interface, IFNAMSIZ - 1); + + if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) { + close(sockfd); + return -1; + } + + if (up) { + ifr.ifr_flags |= IFF_UP; + } else { + ifr.ifr_flags &= ~IFF_UP; + } + + if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) { + close(sockfd); + return -1; + } + + close(sockfd); + return 0; +} + int set_monitor_mode(const char *interface, int channel) { struct nl_msg *msg; struct nl_cb *cb; @@ -66,6 +100,15 @@ int set_monitor_mode(const char *interface, int channel) { return -1; } + // Bring interface down before changing mode + if (set_interface_up(interface, 0) < 0) { + fprintf(stderr, "Warning: Failed to bring interface down: %s\n", strerror(errno)); + // Continue anyway - might already be down + } + + // Small delay to let interface settle + usleep(500000); // 500ms + // Create message to set monitor mode msg = nlmsg_alloc(); if (!msg) { @@ -98,15 +141,44 @@ int set_monitor_mode(const char *interface, int channel) { return -1; } + // Wait for response to confirm monitor mode was set + err = nl_recvmsgs(nl_sock, cb); + if (err < 0) { + fprintf(stderr, "Failed to receive netlink response: %s\n", nl_geterror(err)); + nl_cb_put(cb); + nlmsg_free(msg); + nl80211_cleanup(); + return -1; + } + nl_cb_put(cb); nlmsg_free(msg); + // Small delay after setting monitor mode + usleep(500000); // 500ms + + // Bring interface up + if (set_interface_up(interface, 1) < 0) { + fprintf(stderr, "Warning: Failed to bring interface up: %s\n", strerror(errno)); + // Continue anyway - channel setting might still work + } + + // Small delay before setting channel + usleep(500000); // 500ms + // Set channel if (set_channel(interface, channel) < 0) { nl80211_cleanup(); return -1; } + // Verify monitor mode was set correctly + if (verify_monitor_mode(interface) < 0) { + fprintf(stderr, "Warning: Interface may not be in monitor mode after setup\n"); + fprintf(stderr, "Try: sudo iw dev %s info\n", interface); + // Don't fail - let libpcap check catch it + } + nl80211_cleanup(); return 0; @@ -209,6 +281,78 @@ static int get_interface_cb(struct nl_msg *msg, void *arg) { return NL_OK; } +// Callback to extract interface type from GET_INTERFACE response +static int get_interface_type_cb(struct nl_msg *msg, void *arg) { + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = (struct genlmsghdr *)nlmsg_data(nlmsg_hdr(msg)); + uint32_t *iftype_out = (uint32_t *)arg; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (tb[NL80211_ATTR_IFTYPE]) { + *iftype_out = nla_get_u32(tb[NL80211_ATTR_IFTYPE]); + } + + return NL_OK; +} + +// Verify interface is in monitor mode +static int verify_monitor_mode(const char *interface) { + struct nl_msg *msg; + struct nl_cb *cb; + int ifindex; + int err = 0; + uint32_t iftype = 0; + + if (nl80211_init() < 0) { + return -1; + } + + ifindex = if_nametoindex(interface); + if (ifindex == 0) { + nl80211_cleanup(); + return -1; + } + + msg = nlmsg_alloc(); + if (!msg) { + nl80211_cleanup(); + return -1; + } + + genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, nl80211_id, 0, + NLM_F_REQUEST, NL80211_CMD_GET_INTERFACE, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); + + cb = nl_cb_alloc(NL_CB_DEFAULT); + if (!cb) { + nlmsg_free(msg); + nl80211_cleanup(); + return -1; + } + + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, get_interface_type_cb, &iftype); + + err = nl_send_auto_complete(nl_sock, msg); + if (err < 0) { + nl_cb_put(cb); + nlmsg_free(msg); + nl80211_cleanup(); + return -1; + } + + err = nl_recvmsgs(nl_sock, cb); + + nl_cb_put(cb); + nlmsg_free(msg); + + nl80211_cleanup(); + + return (iftype == NL80211_IFTYPE_MONITOR) ? 0 : -1; +} + // Get current channel from interface using netlink int get_current_channel(const char *interface, uint32_t *freq_out) { struct nl_msg *msg; diff --git a/test_monitor_tshark.sh b/test_monitor_tshark.sh new file mode 100755 index 0000000..3de97f6 --- /dev/null +++ b/test_monitor_tshark.sh @@ -0,0 +1,99 @@ +#!/bin/bash +# Test script to verify monitor mode works with tshark + +set -e + +INTERFACE="${1:-wlan0}" +CHANNEL="${2:-36}" + +echo "=== Testing Monitor Mode with tshark ===" +echo "Interface: $INTERFACE" +echo "Channel: $CHANNEL" +echo "" + +# Check if running as root +if [ "$EUID" -ne 0 ]; then + echo "Please run as root (use sudo)" + exit 1 +fi + +# Check if tshark is installed +if ! command -v tshark &> /dev/null; then + echo "tshark is not installed. Installing..." + if command -v apt-get &> /dev/null; then + sudo apt-get update + sudo apt-get install -y tshark + elif command -v dnf &> /dev/null; then + sudo dnf install -y wireshark-cli + else + echo "Please install tshark manually" + exit 1 + fi +fi + +# Unmanage from NetworkManager +if command -v nmcli &> /dev/null; then + echo "Unmanaging interface from NetworkManager..." + nmcli device set "$INTERFACE" managed no 2>/dev/null || true +fi + +# Unblock WiFi +rfkill unblock wifi 2>/dev/null || true + +# Bring down interface +echo "Bringing down interface..." +ip link set "$INTERFACE" down 2>/dev/null || true +sleep 0.5 + +# Set monitor mode +echo "Setting monitor mode..." +if ! iw dev "$INTERFACE" set type monitor; then + echo "Error: Failed to set monitor mode" + exit 1 +fi + +sleep 0.5 + +# Bring up interface +echo "Bringing up interface..." +ip link set "$INTERFACE" up || echo "Warning: Failed to bring interface up" + +sleep 0.5 + +# Set channel +echo "Setting channel to $CHANNEL..." +iw dev "$INTERFACE" set channel "$CHANNEL" || echo "Warning: Failed to set channel" + +# Verify monitor mode +echo "" +echo "Verifying monitor mode..." +iw dev "$INTERFACE" info | grep -E "(type|channel)" || echo "Could not verify" + +# Check DLT with tshark +echo "" +echo "Checking Data Link Type..." +tshark -i "$INTERFACE" -T fields -e frame.number -c 1 2>&1 | head -5 || true + +echo "" +echo "=== Starting tshark capture (10 seconds) ===" +echo "Press Ctrl+C to stop early" +echo "" + +# Capture for 10 seconds +timeout 10 tshark -i "$INTERFACE" -n -T fields \ + -e frame.number \ + -e frame.time \ + -e wlan.sa \ + -e wlan.da \ + -e wlan.type \ + -e wlan.fc.type_subtype \ + 2>&1 | head -20 + +echo "" +echo "=== Capture complete ===" +echo "" +echo "If you saw packets above, monitor mode is working!" +echo "If not, check:" +echo " 1. Is there WiFi traffic on channel $CHANNEL?" +echo " 2. Is the interface actually in monitor mode? (iw dev $INTERFACE info)" +echo " 3. Try a different channel (e.g., 1, 6, 11 for 2.4GHz)"