Skip to content

Instantly share code, notes, and snippets.

@pldubouilh
Last active August 10, 2023 17:05
Show Gist options
  • Select an option

  • Save pldubouilh/5a278f650f6400473d2848f8ab25f87f to your computer and use it in GitHub Desktop.

Select an option

Save pldubouilh/5a278f650f6400473d2848f8ab25f87f to your computer and use it in GitHub Desktop.

Revisions

  1. pldubouilh revised this gist Jun 20, 2019. 1 changed file with 32 additions and 18 deletions.
    50 changes: 32 additions & 18 deletions xdp.py
    Original file line number Diff line number Diff line change
    @@ -4,7 +4,7 @@
    # this example adds connections to ::1 ::1 to a an eBPF map
    # depends on `bpftool` for now, as bcc is missing map pinning
    #
    # the map is spinned by the XDP program, and this python handler
    # the map is spinned by the XDP program, and this python handler
    # will call `bpftool` to pin the map to a path to allow system-wide
    # access. the path will be removed when the program quits.
    #
    @@ -60,11 +60,16 @@ def pin_map():
    #include <linux/bpf.h>
    struct keyType {
    u_int8_t ipa[16];
    u_int8_t ipb[16];
    u_int16_t porta;
    u_int16_t portb;
    u_int8_t family;
    struct {
    uint8_t ip[16];
    uint16_t port;
    } source;
    struct {
    uint8_t ip[16];
    uint16_t port;
    } destination;
    uint8_t family;
    uint8_t protocol;
    };
    #define htons(x) ((__be16)___constant_swab16((x)))
    @@ -96,22 +101,21 @@ def pin_map():
    char record = 0;
    unsigned char prot = 0;
    // extract family
    // extract family - protocol
    key.family = is_v6 ? 6 : 4;
    // extract ip (only tcp/udp)
    if (is_v4) {
    struct iphdr *iph = data + eth_sze;
    check_hdr();
    prot = iph->protocol;
    memcpy(&key.ipa[0], &iph->saddr, sizeof(iph->saddr));
    memcpy(&key.ipb[0], &iph->daddr, sizeof(iph->daddr));
    memcpy(&key.destination.ip[0], &iph->saddr, sizeof(iph->saddr));
    memcpy(&key.source.ip[0], &iph->daddr, sizeof(iph->daddr));
    } else if (is_v6) {
    struct ipv6hdr *ip6h = data + eth_sze;
    check_hdr();
    prot = ip6h->nexthdr;
    memcpy(&key.ipa[0], ip6h->saddr.s6_addr, sizeof(key.ipa));
    memcpy(&key.ipb[0], ip6h->daddr.s6_addr, sizeof(key.ipa));
    memcpy(&key.destination.ip[0], ip6h->saddr.s6_addr, sizeof(ip6h->saddr.s6_addr));
    memcpy(&key.source.ip[0], ip6h->daddr.s6_addr, sizeof(ip6h->daddr.s6_addr));
    // only record ::1 ::1 for now
    if (ip6h->saddr.s6_addr[15] == ip6h->daddr.s6_addr[15] == 1) record = 1;
    @@ -121,13 +125,15 @@ def pin_map():
    if (is_udp) {
    struct udphdr *udph = data + eth_sze + iph_sze;
    check_udp_hdr();
    key.porta = udph->source;
    key.portb = udph->dest;
    key.destination.port = udph->source;
    key.source.port = udph->dest;
    key.protocol = SOCK_DGRAM;
    } else if (is_tcp) {
    struct tcphdr *tcph = data + eth_sze + iph_sze;
    check_tcp_hdr();
    key.porta = tcph->source;
    key.portb = tcph->dest;
    key.destination.port = tcph->source;
    key.source.port = tcph->dest;
    key.protocol = SOCK_STREAM;
    }
    if (record) h2o_map.insert(&key, &val);
    @@ -148,8 +154,16 @@ def pin_map():
    try:
    time.sleep(1)
    except KeyboardInterrupt:
    print("removed filter from device, cleanup map")
    break

    run("sudo rm -rf" + PATH_MAP)
    print("remove xdp program from device...\r\ncleanup map and wait for h2o to disconnect...")
    b.remove_xdp(in_if, 0)
    run("sudo rm -rf" + PATH_MAP)

    fds = "1"
    while fds != "0":
    fds = run('sudo ls -la /proc/`pgrep -o h2o`/fd | grep "bpf-map" | wc -l').rstrip()
    print("{} fds left...".format(fds))
    time.sleep(1)

    print("cleanup done!")
  2. pldubouilh revised this gist Jun 2, 2019. 1 changed file with 9 additions and 3 deletions.
    12 changes: 9 additions & 3 deletions xdp.py
    Original file line number Diff line number Diff line change
    @@ -62,11 +62,14 @@ def pin_map():
    struct keyType {
    u_int8_t ipa[16];
    u_int8_t ipb[16];
    long porta;
    long portb;
    u_int16_t porta;
    u_int16_t portb;
    u_int8_t family;
    };
    #define htons(x) ((__be16)___constant_swab16((x)))
    #define htonl(x) ((__be32)___constant_swab32((x)))
    #define is_v4 (eth->h_proto == htons(ETH_P_IP))
    #define is_v6 (eth->h_proto == htons(ETH_P_IPV6))
    #define iph_sze (is_v4 ? sizeof(struct iphdr) : sizeof(struct ipv6hdr))
    @@ -93,6 +96,9 @@ def pin_map():
    char record = 0;
    unsigned char prot = 0;
    // extract family
    key.family = is_v6 ? 6 : 4;
    // extract ip (only tcp/udp)
    if (is_v4) {
    struct iphdr *iph = data + eth_sze;
    @@ -107,7 +113,7 @@ def pin_map():
    memcpy(&key.ipa[0], ip6h->saddr.s6_addr, sizeof(key.ipa));
    memcpy(&key.ipb[0], ip6h->daddr.s6_addr, sizeof(key.ipa));
    // only push ::1 ::1 to map
    // only record ::1 ::1 for now
    if (ip6h->saddr.s6_addr[15] == ip6h->daddr.s6_addr[15] == 1) record = 1;
    }
  3. pldubouilh revised this gist May 19, 2019. 1 changed file with 1 addition and 3 deletions.
    4 changes: 1 addition & 3 deletions xdp.py
    Original file line number Diff line number Diff line change
    @@ -67,8 +67,6 @@ def pin_map():
    };
    #define htons(x) ((__be16)___constant_swab16((x)))
    #define htonl(x) ((__be32)___constant_swab32((x)))
    #define is_v4 (eth->h_proto == htons(ETH_P_IP))
    #define is_v6 (eth->h_proto == htons(ETH_P_IPV6))
    #define iph_sze (is_v4 ? sizeof(struct iphdr) : sizeof(struct ipv6hdr))
    @@ -109,7 +107,7 @@ def pin_map():
    memcpy(&key.ipa[0], ip6h->saddr.s6_addr, sizeof(key.ipa));
    memcpy(&key.ipb[0], ip6h->daddr.s6_addr, sizeof(key.ipa));
    // only record ::1 ::1 for now
    // only push ::1 ::1 to map
    if (ip6h->saddr.s6_addr[15] == ip6h->daddr.s6_addr[15] == 1) record = 1;
    }
  4. pldubouilh revised this gist May 19, 2019. 1 changed file with 8 additions and 8 deletions.
    16 changes: 8 additions & 8 deletions xdp.py
    Original file line number Diff line number Diff line change
    @@ -83,10 +83,10 @@ def pin_map():
    BPF_TABLE("lru_percpu_hash", struct keyType, char, h2o_map, 1000);
    int xdp_block(struct xdp_md *ctx) {
    void *data = (void *)(long)ctx->data;
    void *data_end = (void *)(long)ctx->data_end;
    struct ethhdr *eth = data;
    uint64_t eth_sze = sizeof(*eth);
    void *data = (void *)(long)ctx->data;
    void *data_end = (void *)(long)ctx->data_end;
    struct ethhdr *eth = data;
    uint64_t eth_sze = sizeof(*eth);
    check_eth();
    struct keyType key;
    @@ -96,14 +96,14 @@ def pin_map():
    unsigned char prot = 0;
    // extract ip (only tcp/udp)
    if (is_v4) {
    struct iphdr *iph = data + eth_sze;
    if (is_v4) {
    struct iphdr *iph = data + eth_sze;
    check_hdr();
    prot = iph->protocol;
    memcpy(&key.ipa[0], &iph->saddr, sizeof(iph->saddr));
    memcpy(&key.ipb[0], &iph->daddr, sizeof(iph->daddr));
    } else if (is_v6) {
    struct ipv6hdr *ip6h = data + eth_sze;
    } else if (is_v6) {
    struct ipv6hdr *ip6h = data + eth_sze;
    check_hdr();
    prot = ip6h->nexthdr;
    memcpy(&key.ipa[0], ip6h->saddr.s6_addr, sizeof(key.ipa));
  5. pldubouilh revised this gist May 19, 2019. No changes.
  6. pldubouilh revised this gist May 19, 2019. 1 changed file with 4 additions and 0 deletions.
    4 changes: 4 additions & 0 deletions xdp.py
    Original file line number Diff line number Diff line change
    @@ -4,6 +4,10 @@
    # this example adds connections to ::1 ::1 to a an eBPF map
    # depends on `bpftool` for now, as bcc is missing map pinning
    #
    # the map is spinned by the XDP program, and this python handler
    # will call `bpftool` to pin the map to a path to allow system-wide
    # access. the path will be removed when the program quits.
    #
    # example usage
    # 1. start h2o listening on ::1 (optionally adding logging in h2o_trace_check_map)
    # 2. attach usdt probe for connection tracing (see cmd below for sample)
  7. pldubouilh created this gist May 19, 2019.
    147 changes: 147 additions & 0 deletions xdp.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,147 @@
    #!/usr/bin/python
    #
    # simple XDP eBPF program to flag connections using an eBPF map
    # this example adds connections to ::1 ::1 to a an eBPF map
    # depends on `bpftool` for now, as bcc is missing map pinning
    #
    # example usage
    # 1. start h2o listening on ::1 (optionally adding logging in h2o_trace_check_map)
    # 2. attach usdt probe for connection tracing (see cmd below for sample)
    # 3. start map monitoring (optional, below)
    # 4. start this script, attaching to lo interface
    # 5. curl h2o on ::1
    # 6. observe map key/values and optionally, logging
    #
    # useful monitoring commands :
    # enable h2o conn tracing:
    # % sudo ./trace.py -T -p `pgrep -o h2o` u:/home/joe/code/h2o/h2o:h2o_conn_tracing
    # list maps and dump:
    # % watch sudo bpftool map show
    # % watch sudo bpftool map dump pinned /sys/fs/bpf/h2o_map

    from bcc import BPF
    import time
    import sys
    import ctypes as ct
    import json
    import subprocess

    if len(sys.argv) != 2:
    print("Usage: {0} <in ifdev>".format(sys.argv[0]))
    print("e.g.: {0} eth0\n".format(sys.argv[0]))
    exit(1)

    MAP_NAME = "h2o_map"
    PATH_MAP = " /sys/fs/bpf/" + MAP_NAME

    def run(cmd):
    return subprocess.check_output(cmd, shell=True).decode("utf-8")

    # TODO: integrate this into BCC
    def pin_map():
    for m in json.loads(run('sudo bpftool -j map list')):
    if 'name' in m and m['name'] == MAP_NAME:
    print('pinning ' + str(m['id']) + ' to path' + PATH_MAP)
    run('bpftool -j map pin id ' + str(m['id']) + PATH_MAP)


    # load BPF program
    b = BPF(text = """
    #define KBUILD_MODNAME "foo"
    #include <linux/in.h>
    #include <linux/if_ether.h>
    #include <linux/ip.h>
    #include <linux/ipv6.h>
    #include <linux/unistd.h>
    #include <linux/bpf.h>
    struct keyType {
    u_int8_t ipa[16];
    u_int8_t ipb[16];
    long porta;
    long portb;
    };
    #define htons(x) ((__be16)___constant_swab16((x)))
    #define htonl(x) ((__be32)___constant_swab32((x)))
    #define is_v4 (eth->h_proto == htons(ETH_P_IP))
    #define is_v6 (eth->h_proto == htons(ETH_P_IPV6))
    #define iph_sze (is_v4 ? sizeof(struct iphdr) : sizeof(struct ipv6hdr))
    #define is_udp (prot == IPPROTO_UDP)
    #define is_tcp (prot == IPPROTO_TCP)
    #define check_eth() ({ if (data + eth_sze > data_end) return XDP_PASS; })
    #define check_hdr() ({ if (data + eth_sze + iph_sze > data_end) return XDP_PASS; })
    #define check_udp_hdr() ({ if (udph + 1 > (struct udphdr *)data_end) return XDP_PASS; })
    #define check_tcp_hdr() ({ if (tcph + 1 > (struct tcphdr *)data_end) return XDP_PASS; })
    BPF_TABLE("lru_percpu_hash", struct keyType, char, h2o_map, 1000);
    int xdp_block(struct xdp_md *ctx) {
    void *data = (void *)(long)ctx->data;
    void *data_end = (void *)(long)ctx->data_end;
    struct ethhdr *eth = data;
    uint64_t eth_sze = sizeof(*eth);
    check_eth();
    struct keyType key;
    memset(&key, 0, sizeof(key));
    char val = 1;
    char record = 0;
    unsigned char prot = 0;
    // extract ip (only tcp/udp)
    if (is_v4) {
    struct iphdr *iph = data + eth_sze;
    check_hdr();
    prot = iph->protocol;
    memcpy(&key.ipa[0], &iph->saddr, sizeof(iph->saddr));
    memcpy(&key.ipb[0], &iph->daddr, sizeof(iph->daddr));
    } else if (is_v6) {
    struct ipv6hdr *ip6h = data + eth_sze;
    check_hdr();
    prot = ip6h->nexthdr;
    memcpy(&key.ipa[0], ip6h->saddr.s6_addr, sizeof(key.ipa));
    memcpy(&key.ipb[0], ip6h->daddr.s6_addr, sizeof(key.ipa));
    // only record ::1 ::1 for now
    if (ip6h->saddr.s6_addr[15] == ip6h->daddr.s6_addr[15] == 1) record = 1;
    }
    // extract port (only tcp/udp)
    if (is_udp) {
    struct udphdr *udph = data + eth_sze + iph_sze;
    check_udp_hdr();
    key.porta = udph->source;
    key.portb = udph->dest;
    } else if (is_tcp) {
    struct tcphdr *tcph = data + eth_sze + iph_sze;
    check_tcp_hdr();
    key.porta = tcph->source;
    key.portb = tcph->dest;
    }
    if (record) h2o_map.insert(&key, &val);
    return XDP_PASS;
    }
    """, cflags=["-w"], debug=0)

    in_if = sys.argv[1]
    in_fn = b.load_func("xdp_block", BPF.XDP)
    b.attach_xdp(in_if, in_fn, 0)

    time.sleep(1)
    pin_map()

    print("XDP program running, hit CTRL+C to stop")
    while 1:
    try:
    time.sleep(1)
    except KeyboardInterrupt:
    print("removed filter from device, cleanup map")
    break

    run("sudo rm -rf" + PATH_MAP)
    b.remove_xdp(in_if, 0)