Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

attach_raw_socket function does not work well on Qualcomm modem #4984

Closed
fourcolor opened this issue May 6, 2024 · 1 comment
Closed

attach_raw_socket function does not work well on Qualcomm modem #4984

fourcolor opened this issue May 6, 2024 · 1 comment

Comments

@fourcolor
Copy link

fourcolor commented May 6, 2024

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
@fourcolor
Copy link
Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant