Commit 33b48679 authored by Daniel Mack's avatar Daniel Mack Committed by David S. Miller

net: ipv4, ipv6: run cgroup eBPF egress programs

If the cgroup associated with the receiving socket has an eBPF
programs installed, run them from ip_output(), ip6_output() and
ip_mc_output(). From mentioned functions we have two socket contexts
as per 7026b1dd ("netfilter: Pass socket pointer down through
okfn()."). We explicitly need to use sk instead of skb->sk here,
since otherwise the same program would run multiple times on egress
when encap devices are involved, which is not desired in our case.

eBPF programs used in this context are expected to either return 1 to
let the packet pass, or != 1 to drop them. The programs have access to
the skb through bpf_skb_load_bytes(), and the payload starts at the
network headers (L3).

Note that cgroup_bpf_run_filter() is stubbed out as static inline nop
for !CONFIG_CGROUP_BPF, and is otherwise guarded by a static key if
the feature is unused.
Signed-off-by: default avatarDaniel Mack <daniel@zonque.org>
Acked-by: default avatarAlexei Starovoitov <ast@kernel.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c11cd3a6
...@@ -74,6 +74,7 @@ ...@@ -74,6 +74,7 @@
#include <net/checksum.h> #include <net/checksum.h>
#include <net/inetpeer.h> #include <net/inetpeer.h>
#include <net/lwtunnel.h> #include <net/lwtunnel.h>
#include <linux/bpf-cgroup.h>
#include <linux/igmp.h> #include <linux/igmp.h>
#include <linux/netfilter_ipv4.h> #include <linux/netfilter_ipv4.h>
#include <linux/netfilter_bridge.h> #include <linux/netfilter_bridge.h>
...@@ -285,6 +286,13 @@ static int ip_finish_output_gso(struct net *net, struct sock *sk, ...@@ -285,6 +286,13 @@ static int ip_finish_output_gso(struct net *net, struct sock *sk,
static int ip_finish_output(struct net *net, struct sock *sk, struct sk_buff *skb) static int ip_finish_output(struct net *net, struct sock *sk, struct sk_buff *skb)
{ {
unsigned int mtu; unsigned int mtu;
int ret;
ret = BPF_CGROUP_RUN_PROG_INET_EGRESS(sk, skb);
if (ret) {
kfree_skb(skb);
return ret;
}
#if defined(CONFIG_NETFILTER) && defined(CONFIG_XFRM) #if defined(CONFIG_NETFILTER) && defined(CONFIG_XFRM)
/* Policy lookup after SNAT yielded a new policy */ /* Policy lookup after SNAT yielded a new policy */
...@@ -303,6 +311,20 @@ static int ip_finish_output(struct net *net, struct sock *sk, struct sk_buff *sk ...@@ -303,6 +311,20 @@ static int ip_finish_output(struct net *net, struct sock *sk, struct sk_buff *sk
return ip_finish_output2(net, sk, skb); return ip_finish_output2(net, sk, skb);
} }
static int ip_mc_finish_output(struct net *net, struct sock *sk,
struct sk_buff *skb)
{
int ret;
ret = BPF_CGROUP_RUN_PROG_INET_EGRESS(sk, skb);
if (ret) {
kfree_skb(skb);
return ret;
}
return dev_loopback_xmit(net, sk, skb);
}
int ip_mc_output(struct net *net, struct sock *sk, struct sk_buff *skb) int ip_mc_output(struct net *net, struct sock *sk, struct sk_buff *skb)
{ {
struct rtable *rt = skb_rtable(skb); struct rtable *rt = skb_rtable(skb);
...@@ -340,7 +362,7 @@ int ip_mc_output(struct net *net, struct sock *sk, struct sk_buff *skb) ...@@ -340,7 +362,7 @@ int ip_mc_output(struct net *net, struct sock *sk, struct sk_buff *skb)
if (newskb) if (newskb)
NF_HOOK(NFPROTO_IPV4, NF_INET_POST_ROUTING, NF_HOOK(NFPROTO_IPV4, NF_INET_POST_ROUTING,
net, sk, newskb, NULL, newskb->dev, net, sk, newskb, NULL, newskb->dev,
dev_loopback_xmit); ip_mc_finish_output);
} }
/* Multicasts with ttl 0 must not go beyond the host */ /* Multicasts with ttl 0 must not go beyond the host */
...@@ -356,7 +378,7 @@ int ip_mc_output(struct net *net, struct sock *sk, struct sk_buff *skb) ...@@ -356,7 +378,7 @@ int ip_mc_output(struct net *net, struct sock *sk, struct sk_buff *skb)
if (newskb) if (newskb)
NF_HOOK(NFPROTO_IPV4, NF_INET_POST_ROUTING, NF_HOOK(NFPROTO_IPV4, NF_INET_POST_ROUTING,
net, sk, newskb, NULL, newskb->dev, net, sk, newskb, NULL, newskb->dev,
dev_loopback_xmit); ip_mc_finish_output);
} }
return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING,
......
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/bpf-cgroup.h>
#include <linux/netfilter.h> #include <linux/netfilter.h>
#include <linux/netfilter_ipv6.h> #include <linux/netfilter_ipv6.h>
...@@ -131,6 +132,14 @@ static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff * ...@@ -131,6 +132,14 @@ static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *
static int ip6_finish_output(struct net *net, struct sock *sk, struct sk_buff *skb) static int ip6_finish_output(struct net *net, struct sock *sk, struct sk_buff *skb)
{ {
int ret;
ret = BPF_CGROUP_RUN_PROG_INET_EGRESS(sk, skb);
if (ret) {
kfree_skb(skb);
return ret;
}
if ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) || if ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) ||
dst_allfrag(skb_dst(skb)) || dst_allfrag(skb_dst(skb)) ||
(IP6CB(skb)->frag_max_size && skb->len > IP6CB(skb)->frag_max_size)) (IP6CB(skb)->frag_max_size && skb->len > IP6CB(skb)->frag_max_size))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment