How do I activate a BCC eBPF program that modifies outgoing/egress network traffic?

237 Views Asked by At

I used the BPF Compiler Collection (BCC) to write an eBPF program to modify outgoing UDP packets.

I used the program type BPF_PROG_TYPE_SCHED_ACT, which I hope is the right one. (https://ebpf-docs.dylanreimerink.nl/linux/program-type/).

But having loaded it, I don't know how to "activate" it. Attaching it to eth0 or something does not seem to work. I was guessing I need the tc command, but I don't understand why (because I don't know what that is, exactly).

Here is my Python script:

import sys
import time

from bcc import BPF

def main():
    src = open("my_example.c").read()

    b = BPF(text=src, cflags=["-Wno-macro-redefined"])  # 3

    # fn = b.load_func("my_example", BPF.SK_SKB)
    fn = b.load_func("my_example", BPF.SCHED_CLS)

    b.attach_raw_socket(fn, "eth0") # this doesn't work

if __name__ == "__main__":
    main()

While the eBPF Program itself is this:

#include <bcc/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/udp.h>

int my_example(struct __sk_buff *skb)
{
    if(skb->protocol != htons(ETH_P_IP)) return TC_ACT_OK;
    if(skb->pkt_type != PACKET_OUTGOING) return TC_ACT_OK;

    if(skb->wire_len<176) return TC_ACT_OK;
    if(skb->len<176) return TC_ACT_OK;

    bpf_skb_pull_data(skb, 176);

    u8* packet = (u8*)(long)skb->data;
    u8* packet_end = (u8*)(long)skb->data_end;
    if(packet_end<packet+176) return TC_ACT_OK;

    if(packet[94] != 0x54) return TC_ACT_OK;

    bpf_skb_change_tail(skb, 30, 0);

    packet=(u8*)(long)skb->data;
    packet_end=(u8*)(long)skb->data_end;
    if (packet_end<packet+206) return TC_ACT_OK;

    packet[205] = packet[205-30];
    packet[94] = 0x56;
    // ...

    // more code for length and checksums ...

    return TC_ACT_RECLASSIFY;
}

How do I "activate" this program?

1

There are 1 best solutions below

2
pchaigno On

TL;DR. You can attach your program to the tc hook of interface eth0 with the Python pyroute2 library. See the code example at the end of my post.


fn = b.load_func("my_example", BPF.SCHED_CLS)
b.attach_raw_socket(fn, "eth0") # this doesn't work

This doesn't work because you declared your program as being of type SCHED_CLS and you then try to attach it to a different hook.

Given you are using bcc, you can check example program https://github.com/iovisor/bcc/blob/master/examples/networking/xdp/xdp_drop_count.py (that program handles both XDP and tc). In particular you can attach your tc-bpf program with:

import pyroute2

fn = b.load_func("my_example", BPF.SCHED_CLS)

ip = pyroute2.IPRoute()
ipdb = pyroute2.IPDB(nl=ip)
idx = ipdb.interfaces["eth0"].index
ip.tc("add", "clsact", idx)
ip.tc("add-filter", "bpf", idx, ":1", fd=fn.fd, name=fn.name,
      parent="ffff:fff2", classid=1, direct_action=True)