Adds ability to attach a BPF program to a pktgen thread. The program
should fill in the contents of each packet from scratch, including
checksum.

The context passed in is currently in the form of xdp_md, with a fresh
skb underlying the data pointer and headroom equivalent to the pktgen
pktsize setting. The BPF program should make appropriate calls to
bpf_xdp_adjust_head() to increase the size of the packet into the
headroom (initially data == data_end).

One concern is that the type of the program should probably be made
unique, since not all helpers of XDP would be expected to work, this was
just a convenient baseline for this initial version.

Attaching a program is done by writing the bpf fd from a user process
into /proc/net/pktgen/$DEV@$THREAD with the format ("bpf %d\n", fd).
Detaching a program is done with ("bpf\n")

In tests, observed approximately 1Mpps xmit per core on a ixgbe 10G nic.
Burst was set to 1 due to each packet being unique. This included the
soft bpf checksum calculation of a valid tcp syn packet.

Signed-off-by: Brenden Blanco <[email protected]>
---
 net/core/pktgen.c | 105 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 104 insertions(+), 1 deletion(-)

diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index 6e1e10f..a492534 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -146,6 +146,8 @@
 #include <linux/inet.h>
 #include <linux/inetdevice.h>
 #include <linux/rtnetlink.h>
+#include <linux/bpf.h>
+#include <linux/filter.h>
 #include <linux/if_arp.h>
 #include <linux/if_vlan.h>
 #include <linux/in.h>
@@ -203,6 +205,7 @@
 #define F_NODE          (1<<15)        /* Node memory alloc*/
 #define F_UDPCSUM       (1<<16)        /* Include UDP checksum */
 #define F_NO_TIMESTAMP  (1<<17)        /* Don't timestamp packets (default TS) 
*/
+#define F_BPF           (1<<18)        /* Fill packet via BPF */
 
 /* Thread control flag bits */
 #define T_STOP        (1<<0)   /* Stop run */
@@ -394,6 +397,7 @@ struct pktgen_dev {
        __u32 skb_priority;     /* skb priority field */
        unsigned int burst;     /* number of duplicated packets to burst */
        int node;               /* Memory node */
+       struct bpf_prog *prog;
 
 #ifdef CONFIG_XFRM
        __u8    ipsmode;                /* IPSEC mode (config) */
@@ -695,6 +699,9 @@ static int pktgen_if_show(struct seq_file *seq, void *v)
        if (pkt_dev->flags & F_NODE)
                seq_puts(seq, "NODE_ALLOC  ");
 
+       if (pkt_dev->flags & F_BPF)
+               seq_puts(seq, "BPF  ");
+
        seq_puts(seq, "\n");
 
        /* not really stopped, more like last-running-at */
@@ -1794,6 +1801,39 @@ static ssize_t pktgen_if_write(struct file *file,
                return count;
        }
 
+       if (!strcmp(name, "bpf")) {
+               struct bpf_prog *prog, *old_prog;
+
+               len = num_arg(&user_buffer[i], 9, &value);
+               if (len < 0)
+                       return len;
+
+               i += len;
+               if (len == 0)
+                       prog = NULL;
+               else
+                       prog = bpf_prog_get_type(value, BPF_PROG_TYPE_XDP);
+
+               if (IS_ERR(prog)) {
+                       sprintf(pg_result, "ERROR: invalid bpf fd");
+                       return count;
+               }
+
+               old_prog = pkt_dev->prog;
+
+               pkt_dev->prog = prog;
+               if (prog)
+                       pkt_dev->flags |= F_BPF;
+               else
+                       pkt_dev->flags &= ~F_BPF;
+
+               if (old_prog)
+                       bpf_prog_put(old_prog);
+
+               sprintf(pg_result, "OK: bpf=%s", prog ? "running" : "cleared");
+               return count;
+       }
+
        sprintf(pkt_dev->result, "No such parameter \"%s\"", name);
        return -EINVAL;
 }
@@ -2806,6 +2846,65 @@ static struct sk_buff *pktgen_alloc_skb(struct 
net_device *dev,
        return skb;
 }
 
+static struct sk_buff *fill_packet_bpf(struct net_device *odev,
+                                      struct pktgen_dev *pkt_dev)
+{
+       struct bpf_prog *prog = pkt_dev->prog;
+       struct sk_buff *skb = NULL;
+       struct xdp_buff xdp;
+       void *orig_data;
+       u16 queue_map;
+       u32 act;
+
+       set_cur_queue_map(pkt_dev);
+       queue_map = pkt_dev->cur_queue_map;
+
+       skb = pktgen_alloc_skb(odev, pkt_dev);
+       if (!skb) {
+               sprintf(pkt_dev->result, "No memory");
+               return NULL;
+       }
+
+       prefetchw(skb->data);
+       skb_reserve(skb, 64);
+
+       xdp.data_hard_start = skb->head;
+       xdp.data = skb->data;
+       xdp.data_end = skb->data;
+       orig_data = xdp.data;
+
+       local_bh_disable();
+       rcu_read_lock();
+       act = bpf_prog_run_xdp(prog, &xdp);
+       rcu_read_unlock();
+       local_bh_enable();
+
+       switch (act) {
+       case XDP_TX:
+               break;
+       default:
+               goto drop;
+       }
+
+       if (xdp.data != orig_data)
+               skb_push(skb, orig_data - xdp.data);
+
+       skb_set_mac_header(skb, 0);
+
+       skb_set_queue_mapping(skb, queue_map);
+       skb->priority = pkt_dev->skb_priority;
+       skb->dev = odev;
+       skb->pkt_type = PACKET_HOST;
+       skb->ip_summed = CHECKSUM_NONE;
+
+       return skb;
+
+drop:
+       if (skb)
+               kfree_skb(skb);
+       return NULL;
+}
+
 static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
                                        struct pktgen_dev *pkt_dev)
 {
@@ -3067,7 +3166,9 @@ static struct sk_buff *fill_packet_ipv6(struct net_device 
*odev,
 static struct sk_buff *fill_packet(struct net_device *odev,
                                   struct pktgen_dev *pkt_dev)
 {
-       if (pkt_dev->flags & F_IPV6)
+       if (pkt_dev->flags & F_BPF)
+               return fill_packet_bpf(odev, pkt_dev);
+       else if (pkt_dev->flags & F_IPV6)
                return fill_packet_ipv6(odev, pkt_dev);
        else
                return fill_packet_ipv4(odev, pkt_dev);
@@ -3854,6 +3955,8 @@ static int pktgen_remove_device(struct pktgen_thread *t,
        vfree(pkt_dev->flows);
        if (pkt_dev->page)
                put_page(pkt_dev->page);
+       if (pkt_dev->prog)
+               bpf_prog_put(pkt_dev->prog);
        kfree_rcu(pkt_dev, rcu);
        return 0;
 }
-- 
2.7.4

_______________________________________________
iovisor-dev mailing list
[email protected]
https://lists.iovisor.org/mailman/listinfo/iovisor-dev

Reply via email to