• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2#
3# xdp_macswap_count.py Swap Source and Destination MAC addresses on
4#                      incoming packets and transmit packets back on
5#                      same interface in XDP layer and count for which
6#                      protocol type
7#
8# Copyright (c) 2016 PLUMgrid
9# Copyright (c) 2016 Jan Ruth
10# Copyright (c) 2018 Andy Gospodarek
11# Licensed under the Apache License, Version 2.0 (the "License")
12
13from bcc import BPF
14import pyroute2
15import time
16import sys
17
18flags = 0
19def usage():
20    print("Usage: {0} [-S] <ifdev>".format(sys.argv[0]))
21    print("       -S: use skb mode\n")
22    print("e.g.: {0} eth0\n".format(sys.argv[0]))
23    exit(1)
24
25if len(sys.argv) < 2 or len(sys.argv) > 3:
26    usage()
27
28if len(sys.argv) == 2:
29    device = sys.argv[1]
30
31if len(sys.argv) == 3:
32    if "-S" in sys.argv:
33        # XDP_FLAGS_SKB_MODE
34        flags |= 2 << 0
35
36    if "-S" == sys.argv[1]:
37        device = sys.argv[2]
38    else:
39        device = sys.argv[1]
40
41mode = BPF.XDP
42#mode = BPF.SCHED_CLS
43
44if mode == BPF.XDP:
45    ret = "XDP_TX"
46    ctxtype = "xdp_md"
47else:
48    ret = "TC_ACT_SHOT"
49    ctxtype = "__sk_buff"
50
51# load BPF program
52b = BPF(text = """
53#define KBUILD_MODNAME "foo"
54#include <uapi/linux/bpf.h>
55#include <linux/in.h>
56#include <linux/if_ether.h>
57#include <linux/if_packet.h>
58#include <linux/if_vlan.h>
59#include <linux/ip.h>
60#include <linux/ipv6.h>
61
62
63BPF_TABLE("percpu_array", uint32_t, long, dropcnt, 256);
64
65static inline int parse_ipv4(void *data, u64 nh_off, void *data_end) {
66    struct iphdr *iph = data + nh_off;
67
68    if ((void*)&iph[1] > data_end)
69        return 0;
70    return iph->protocol;
71}
72
73static inline int parse_ipv6(void *data, u64 nh_off, void *data_end) {
74    struct ipv6hdr *ip6h = data + nh_off;
75
76    if ((void*)&ip6h[1] > data_end)
77        return 0;
78    return ip6h->nexthdr;
79}
80
81static void swap_src_dst_mac(void *data)
82{
83    unsigned short *p = data;
84    unsigned short dst[3];
85
86    dst[0] = p[0];
87    dst[1] = p[1];
88    dst[2] = p[2];
89    p[0] = p[3];
90    p[1] = p[4];
91    p[2] = p[5];
92    p[3] = dst[0];
93    p[4] = dst[1];
94    p[5] = dst[2];
95}
96
97int xdp_prog1(struct CTXTYPE *ctx) {
98
99    void* data_end = (void*)(long)ctx->data_end;
100    void* data = (void*)(long)ctx->data;
101
102    struct ethhdr *eth = data;
103
104    // drop packets
105    int rc = RETURNCODE; // let pass XDP_PASS or redirect to tx via XDP_TX
106    long *value;
107    uint16_t h_proto;
108    uint64_t nh_off = 0;
109    uint32_t index;
110
111    nh_off = sizeof(*eth);
112
113    if (data + nh_off  > data_end)
114        return rc;
115
116    h_proto = eth->h_proto;
117
118    if (h_proto == htons(ETH_P_8021Q) || h_proto == htons(ETH_P_8021AD)) {
119        struct vlan_hdr *vhdr;
120
121        vhdr = data + nh_off;
122        nh_off += sizeof(struct vlan_hdr);
123        if (data + nh_off > data_end)
124            return rc;
125            h_proto = vhdr->h_vlan_encapsulated_proto;
126    }
127    if (h_proto == htons(ETH_P_8021Q) || h_proto == htons(ETH_P_8021AD)) {
128        struct vlan_hdr *vhdr;
129
130        vhdr = data + nh_off;
131        nh_off += sizeof(struct vlan_hdr);
132        if (data + nh_off > data_end)
133            return rc;
134            h_proto = vhdr->h_vlan_encapsulated_proto;
135    }
136
137    if (h_proto == htons(ETH_P_IP))
138        index = parse_ipv4(data, nh_off, data_end);
139    else if (h_proto == htons(ETH_P_IPV6))
140       index = parse_ipv6(data, nh_off, data_end);
141    else
142        index = 0;
143
144    if (h_proto == IPPROTO_UDP) {
145        swap_src_dst_mac(data);
146        rc = XDP_TX;
147    }
148
149    value = dropcnt.lookup(&index);
150    if (value)
151        *value += 1;
152
153    return rc;
154}
155""", cflags=["-w", "-DRETURNCODE=%s" % ret, "-DCTXTYPE=%s" % ctxtype])
156
157fn = b.load_func("xdp_prog1", mode)
158
159if mode == BPF.XDP:
160    b.attach_xdp(device, fn, flags)
161else:
162    ip = pyroute2.IPRoute()
163    ipdb = pyroute2.IPDB(nl=ip)
164    idx = ipdb.interfaces[device].index
165    ip.tc("add", "clsact", idx)
166    ip.tc("add-filter", "bpf", idx, ":1", fd=fn.fd, name=fn.name,
167          parent="ffff:fff2", classid=1, direct_action=True)
168
169dropcnt = b.get_table("dropcnt")
170prev = [0] * 256
171print("Printing drops per IP protocol-number, hit CTRL+C to stop")
172while 1:
173    try:
174        for k in dropcnt.keys():
175            val = dropcnt.sum(k).value
176            i = k.value
177            if val:
178                delta = val - prev[i]
179                prev[i] = val
180                print("{}: {} pkt/s".format(i, delta))
181        time.sleep(1)
182    except KeyboardInterrupt:
183        print("Removing filter from device")
184        break;
185
186if mode == BPF.XDP:
187    b.remove_xdp(device, flags)
188else:
189    ip.tc("del", "clsact", idx)
190    ipdb.release()
191