Closed fourcolor closed 4 months ago
I use eBPF to capture packets, and I found that it works fine on regular network cards, but it doesn't function properly when I use it on a Qualcomm modem. I expect my code to execute as follows:
$ sudo python3 udp_sniffer.py -i enp43s0 -p 26425,26426 timestamp,saddr,daddr,sport,dport,sequence,epoch,microseconds,direction 2024-05-06 12:56:35.059705,140.112.20.182,140.112.20.183,47003,26425,10394,1714971395,59455,0, 2024-05-06 12:56:35.060625,140.112.20.183,140.112.20.182,26426,34069,10396,1714971395,23433,2, 2024-05-06 12:56:35.061726,140.112.20.182,140.112.20.183,47003,26425,10395,1714971395,61455,0, 2024-05-06 12:56:35.062661,140.112.20.183,140.112.20.182,26426,34069,10397,1714971395,25434,2, 2024-05-06 12:56:35.063719,140.112.20.182,140.112.20.183,47003,26425,10396,1714971395,63455,0, 2024-05-06 12:56:35.064900,140.112.20.183,140.112.20.182,26426,34069,10398,1714971395,27434,2, 2024-05-06 12:56:35.065643,140.112.20.182,140.112.20.183,47003,26425,10397,1714971395,65455,0,
When I use the Qualcomm modem as a network interface, however, nothing is displayed.
$ sudo python3 udp_sniffer.py -i qc01 -p 26425,26426 timestamp,saddr,daddr,sport,dport,sequence,epoch,microseconds,direction
And I have used ss --bpf --packet -p to confirm, and the Recv-Q of qc01 has data.
$ ss --bpf --packet -p Netid Recv-Q Send-Q Local Address:Port Peer Address:Port Process bpf filter (0): p_raw 213504 0 *:qc01 * bpf filter (0):
Below is a snippet of the eBPF code I executed
#!/usr/bin/python from bcc import BPF import time import sys import argparse PACKET_SIZE = 250 parser = argparse.ArgumentParser() parser.add_argument("-i", "--device", help="net_interface") parser.add_argument("-p", "--ports", help="comma-separated list of ports to trace.") parser.add_argument("-w", "--file", help="output file") args = parser.parse_args() bpf_text = """ #include <uapi/linux/ptrace.h> #include <net/sock.h> #include <bcc/proto.h> #include <linux/bpf.h> #define IP_TCP 6 #define IP_UDP 17 #define IP_ICMP 1 #define ETH_HLEN 14 BPF_PERF_OUTPUT(skb_events); struct perf_data { int len; int dir; }; int packet_monitor(struct __sk_buff *skb) { u8 *cursor = 0; u32 saddr, daddr; u32 sport, dport; u32 udp_header_length = 0; u32 ip_header_length = 0; u32 payload_offset = 0; u32 payload_length = 0; struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet)); struct ip_t *ip = cursor_advance(cursor, sizeof(*ip)); if (ip->ver != 4) goto KEEP; ip_header_length = ip->hlen << 2; // SHL 2 -> *4 multiply /* check ip header length against minimum */ if (ip_header_length < sizeof(*ip)) goto KEEP; if (ip->nextp == IP_TCP) goto KEEP; if (ip->nextp == IP_ICMP) goto KEEP; if (ip -> nextp == IP_UDP){ struct udp_t *udp = cursor_advance(cursor, sizeof(*udp)); payload_offset = ETH_HLEN + ip_header_length + 8; payload_length = ip->tlen - ip_header_length - 8; dport = udp->dport; sport = udp->sport; saddr = ip -> src; daddr = ip -> dst; int len = payload_length; FILTER_PORT struct perf_data data = {.len = len, .dir = skb->ingress_ifindex}; skb_events.perf_submit_skb(skb, skb->len, &data, sizeof(data)); } /* keep the packet and send it to userspace returning -1 */ KEEP: return -1; /* drop the packet returning 0 */ DROP: return 0; } """ from ctypes import * import ctypes as ct import sys import socket import os import struct import ipaddress import ctypes from datetime import datetime from struct import unpack out = sys.stdout if args.file: out = open(args.file,"w+") if args.ports: dports = [int(port) for port in args.ports.split(',')] dports_if = ' && '.join([f'dport != {port} && sport != {port}' for port in dports]) bpf_text = bpf_text.replace('FILTER_PORT', 'if (%s) { goto KEEP; }' % dports_if) else: bpf_text = bpf_text.replace('FILTER_PORT',"") bpf = BPF(text=bpf_text) function_skb_matching = bpf.load_func("packet_monitor", BPF.SOCKET_FILTER) BPF.attach_raw_socket(function_skb_matching, args.device) def payload_info(cpu, data, size): class SkbEvent(ct.Structure): _fields_ = [ ("len", ct.c_uint32), ("dir", ct.c_uint32), ("raw", ct.c_ubyte * (size - 2*ct.sizeof(ct.c_uint32))), ] try: ...... print(f"{ts},{saddr},{daddr},{sport},{dport},{seq},{epoch},{microseconds},{dir},",file=out,flush=True) except ValueError: return "Invalid input" try: bpf["skb_events"].open_perf_buffer(payload_info) print("timestamp,saddr,daddr,sport,dport,sequence,epoch,microseconds,direction",file=out,flush=True) while True : bpf.perf_buffer_poll() except KeyboardInterrupt: sys.stdout.close() pass
I found that the device I'm using operates on Raw IP level sockets, so parsing should start from the IP header. This issue can be closed now.
I use eBPF to capture packets, and I found that it works fine on regular network cards, but it doesn't function properly when I use it on a Qualcomm modem. I expect my code to execute as follows:
When I use the Qualcomm modem as a network interface, however, nothing is displayed.
And I have used ss --bpf --packet -p to confirm, and the Recv-Q of qc01 has data.
Below is a snippet of the eBPF code I executed