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