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:
parent
122484463f
commit
fa93fc26f1
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
144
src/monitor.c
144
src/monitor.c
|
|
@ -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,8 +141,30 @@ 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);
|
nl_cb_put(cb);
|
||||||
nlmsg_free(msg);
|
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
|
// Set channel
|
||||||
if (set_channel(interface, channel) < 0) {
|
if (set_channel(interface, channel) < 0) {
|
||||||
|
|
@ -107,6 +172,13 @@ int set_monitor_mode(const char *interface, int channel) {
|
||||||
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;
|
||||||
|
|
|
||||||
|
|
@ -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)"
|
||||||
Loading…
Reference in New Issue