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 <cursoragent@cursor.com>
This commit is contained in:
Robert McMahon 2026-02-13 13:44:28 -08:00
parent 122484463f
commit fa93fc26f1
3 changed files with 284 additions and 4 deletions

View File

@ -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) { int start_capture(const char *interface, packet_callback_t callback, void *user_data) {
char errbuf[PCAP_ERRBUF_SIZE]; char errbuf[PCAP_ERRBUF_SIZE];
(void)user_data; // Unused
if (capture_running) { if (capture_running) {
fprintf(stderr, "Capture already running\n"); fprintf(stderr, "Capture already running\n");
return -1; return -1;
} }
// Open interface for capture // Use pcap_create + pcap_set_rfmon + pcap_activate for better monitor mode support
pcap_handle = pcap_open_live(interface, 65535, 1, 1000, errbuf); pcap_handle = pcap_create(interface, errbuf);
if (!pcap_handle) { 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; 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) { if (pcap_setnonblock(pcap_handle, 0, errbuf) < 0) {
fprintf(stderr, "Warning: Failed to set blocking mode: %s\n", errbuf); fprintf(stderr, "Warning: Failed to set blocking mode: %s\n", errbuf);
} }

View File

@ -5,6 +5,7 @@
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/nl80211.h> #include <linux/nl80211.h>
#include <netlink/genl/genl.h> #include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.h> #include <netlink/genl/ctrl.h>
@ -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) { int set_monitor_mode(const char *interface, int channel) {
struct nl_msg *msg; struct nl_msg *msg;
struct nl_cb *cb; struct nl_cb *cb;
@ -66,6 +100,15 @@ int set_monitor_mode(const char *interface, int channel) {
return -1; 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 // Create message to set monitor mode
msg = nlmsg_alloc(); msg = nlmsg_alloc();
if (!msg) { if (!msg) {
@ -98,15 +141,44 @@ int set_monitor_mode(const char *interface, int channel) {
return -1; 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); nl_cb_put(cb);
nlmsg_free(msg); 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 // Set channel
if (set_channel(interface, channel) < 0) { if (set_channel(interface, channel) < 0) {
nl80211_cleanup(); nl80211_cleanup();
return -1; 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(); nl80211_cleanup();
return 0; return 0;
@ -209,6 +281,78 @@ static int get_interface_cb(struct nl_msg *msg, void *arg) {
return NL_OK; 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 // Get current channel from interface using netlink
int get_current_channel(const char *interface, uint32_t *freq_out) { int get_current_channel(const char *interface, uint32_t *freq_out) {
struct nl_msg *msg; struct nl_msg *msg;

99
test_monitor_tshark.sh Executable file
View File

@ -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)"