Skip to content

Commit

Permalink
tun: allow to attach ebpf socket filter
Browse files Browse the repository at this point in the history
This patch allows userspace to attach eBPF filter to tun. This will
allow to implement VM dataplane filtering in a more efficient way
compared to cBPF filter by allowing either qemu or libvirt to
attach eBPF filter to tun.

Signed-off-by: Jason Wang <jasowang@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Jason Wang authored and David S. Miller committed Jan 17, 2018
1 parent cd5681d commit aff3d70
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 4 deletions.
38 changes: 34 additions & 4 deletions drivers/net/tun.c
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,12 @@ struct tun_struct {
struct tun_pcpu_stats __percpu *pcpu_stats;
struct bpf_prog __rcu *xdp_prog;
struct tun_prog __rcu *steering_prog;
struct tun_prog __rcu *filter_prog;
};

struct veth {
__be16 h_vlan_proto;
__be16 h_vlan_TCI;
};

bool tun_is_xdp_buff(void *ptr)
Expand Down Expand Up @@ -1036,12 +1042,25 @@ static void tun_automq_xmit(struct tun_struct *tun, struct sk_buff *skb)
#endif
}

static unsigned int run_ebpf_filter(struct tun_struct *tun,
struct sk_buff *skb,
int len)
{
struct tun_prog *prog = rcu_dereference(tun->filter_prog);

if (prog)
len = bpf_prog_run_clear_cb(prog->prog, skb);

return len;
}

/* Net device start xmit */
static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct tun_struct *tun = netdev_priv(dev);
int txq = skb->queue_mapping;
struct tun_file *tfile;
int len = skb->len;

rcu_read_lock();
tfile = rcu_dereference(tun->tfiles[txq]);
Expand All @@ -1067,6 +1086,15 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev)
sk_filter(tfile->socket.sk, skb))
goto drop;

len = run_ebpf_filter(tun, skb, len);

/* Trim extra bytes since we may insert vlan proto & TCI
* in tun_put_user().
*/
len -= skb_vlan_tag_present(skb) ? sizeof(struct veth) : 0;
if (len <= 0 || pskb_trim(skb, len))
goto drop;

if (unlikely(skb_orphan_frags_rx(skb, GFP_ATOMIC)))
goto drop;

Expand Down Expand Up @@ -2054,10 +2082,7 @@ static ssize_t tun_put_user(struct tun_struct *tun,

if (vlan_hlen) {
int ret;
struct {
__be16 h_vlan_proto;
__be16 h_vlan_TCI;
} veth;
struct veth veth;

veth.h_vlan_proto = skb->vlan_proto;
veth.h_vlan_TCI = htons(skb_vlan_tag_get(skb));
Expand Down Expand Up @@ -2225,6 +2250,7 @@ static void tun_free_netdev(struct net_device *dev)
tun_flow_uninit(tun);
security_tun_dev_free_security(tun->security);
__tun_set_ebpf(tun, &tun->steering_prog, NULL);
__tun_set_ebpf(tun, &tun->filter_prog, NULL);
}

static void tun_setup(struct net_device *dev)
Expand Down Expand Up @@ -3019,6 +3045,10 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
ret = tun_set_ebpf(tun, &tun->steering_prog, argp);
break;

case TUNSETFILTEREBPF:
ret = tun_set_ebpf(tun, &tun->filter_prog, argp);
break;

default:
ret = -EINVAL;
break;
Expand Down
1 change: 1 addition & 0 deletions include/uapi/linux/if_tun.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
#define TUNSETVNETBE _IOW('T', 222, int)
#define TUNGETVNETBE _IOR('T', 223, int)
#define TUNSETSTEERINGEBPF _IOR('T', 224, int)
#define TUNSETFILTEREBPF _IOR('T', 225, int)

/* TUNSETIFF ifr flags */
#define IFF_TUN 0x0001
Expand Down

0 comments on commit aff3d70

Please sign in to comment.