netfilter: add netfilter hooks to SRv6 data plane
This patch introduces netfilter hooks for solving the problem that conntrack couldn't record both inner flows and outer flows. This patch also introduces a new sysctl toggle for enabling lightweight tunnel netfilter hooks. Signed-off-by: Ryoga Saito <contact@proelbtn.com> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
Родитель
7bc416f147
Коммит
7a3f5b0de3
|
@ -184,6 +184,13 @@ nf_conntrack_gre_timeout_stream - INTEGER (seconds)
|
||||||
This extended timeout will be used in case there is an GRE stream
|
This extended timeout will be used in case there is an GRE stream
|
||||||
detected.
|
detected.
|
||||||
|
|
||||||
|
nf_hooks_lwtunnel - BOOLEAN
|
||||||
|
- 0 - disabled (default)
|
||||||
|
- not 0 - enabled
|
||||||
|
|
||||||
|
If this option is enabled, the lightweight tunnel netfilter hooks are
|
||||||
|
enabled. This option cannot be disabled once it is enabled.
|
||||||
|
|
||||||
nf_flowtable_tcp_timeout - INTEGER (seconds)
|
nf_flowtable_tcp_timeout - INTEGER (seconds)
|
||||||
default 30
|
default 30
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,9 @@ struct lwtunnel_encap_ops {
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_LWTUNNEL
|
#ifdef CONFIG_LWTUNNEL
|
||||||
|
|
||||||
|
DECLARE_STATIC_KEY_FALSE(nf_hooks_lwtunnel_enabled);
|
||||||
|
|
||||||
void lwtstate_free(struct lwtunnel_state *lws);
|
void lwtstate_free(struct lwtunnel_state *lws);
|
||||||
|
|
||||||
static inline struct lwtunnel_state *
|
static inline struct lwtunnel_state *
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
#include <linux/sysctl.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
#ifdef CONFIG_SYSCTL
|
||||||
|
int nf_hooks_lwtunnel_sysctl_handler(struct ctl_table *table, int write,
|
||||||
|
void *buffer, size_t *lenp, loff_t *ppos);
|
||||||
|
#endif
|
|
@ -23,6 +23,9 @@
|
||||||
#include <net/ip6_fib.h>
|
#include <net/ip6_fib.h>
|
||||||
#include <net/rtnh.h>
|
#include <net/rtnh.h>
|
||||||
|
|
||||||
|
DEFINE_STATIC_KEY_FALSE(nf_hooks_lwtunnel_enabled);
|
||||||
|
EXPORT_SYMBOL_GPL(nf_hooks_lwtunnel_enabled);
|
||||||
|
|
||||||
#ifdef CONFIG_MODULES
|
#ifdef CONFIG_MODULES
|
||||||
|
|
||||||
static const char *lwtunnel_encap_str(enum lwtunnel_encap_types encap_type)
|
static const char *lwtunnel_encap_str(enum lwtunnel_encap_types encap_type)
|
||||||
|
|
|
@ -26,6 +26,8 @@
|
||||||
#ifdef CONFIG_IPV6_SEG6_HMAC
|
#ifdef CONFIG_IPV6_SEG6_HMAC
|
||||||
#include <net/seg6_hmac.h>
|
#include <net/seg6_hmac.h>
|
||||||
#endif
|
#endif
|
||||||
|
#include <net/lwtunnel.h>
|
||||||
|
#include <linux/netfilter.h>
|
||||||
|
|
||||||
static size_t seg6_lwt_headroom(struct seg6_iptunnel_encap *tuninfo)
|
static size_t seg6_lwt_headroom(struct seg6_iptunnel_encap *tuninfo)
|
||||||
{
|
{
|
||||||
|
@ -295,11 +297,19 @@ static int seg6_do_srh(struct sk_buff *skb)
|
||||||
|
|
||||||
ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
|
ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
|
||||||
skb_set_transport_header(skb, sizeof(struct ipv6hdr));
|
skb_set_transport_header(skb, sizeof(struct ipv6hdr));
|
||||||
|
nf_reset_ct(skb);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int seg6_input(struct sk_buff *skb)
|
static int seg6_input_finish(struct net *net, struct sock *sk,
|
||||||
|
struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
return dst_input(skb);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int seg6_input_core(struct net *net, struct sock *sk,
|
||||||
|
struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct dst_entry *orig_dst = skb_dst(skb);
|
struct dst_entry *orig_dst = skb_dst(skb);
|
||||||
struct dst_entry *dst = NULL;
|
struct dst_entry *dst = NULL;
|
||||||
|
@ -337,10 +347,41 @@ static int seg6_input(struct sk_buff *skb)
|
||||||
if (unlikely(err))
|
if (unlikely(err))
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
return dst_input(skb);
|
if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled))
|
||||||
|
return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT,
|
||||||
|
dev_net(skb->dev), NULL, skb, NULL,
|
||||||
|
skb_dst(skb)->dev, seg6_input_finish);
|
||||||
|
|
||||||
|
return seg6_input_finish(dev_net(skb->dev), NULL, skb);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int seg6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
|
static int seg6_input_nf(struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
struct net_device *dev = skb_dst(skb)->dev;
|
||||||
|
struct net *net = dev_net(skb->dev);
|
||||||
|
|
||||||
|
switch (skb->protocol) {
|
||||||
|
case htons(ETH_P_IP):
|
||||||
|
return NF_HOOK(NFPROTO_IPV4, NF_INET_POST_ROUTING, net, NULL,
|
||||||
|
skb, NULL, dev, seg6_input_core);
|
||||||
|
case htons(ETH_P_IPV6):
|
||||||
|
return NF_HOOK(NFPROTO_IPV6, NF_INET_POST_ROUTING, net, NULL,
|
||||||
|
skb, NULL, dev, seg6_input_core);
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int seg6_input(struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled))
|
||||||
|
return seg6_input_nf(skb);
|
||||||
|
|
||||||
|
return seg6_input_core(dev_net(skb->dev), NULL, skb);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int seg6_output_core(struct net *net, struct sock *sk,
|
||||||
|
struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct dst_entry *orig_dst = skb_dst(skb);
|
struct dst_entry *orig_dst = skb_dst(skb);
|
||||||
struct dst_entry *dst = NULL;
|
struct dst_entry *dst = NULL;
|
||||||
|
@ -387,12 +428,40 @@ static int seg6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
|
||||||
if (unlikely(err))
|
if (unlikely(err))
|
||||||
goto drop;
|
goto drop;
|
||||||
|
|
||||||
|
if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled))
|
||||||
|
return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, net, sk, skb,
|
||||||
|
NULL, skb_dst(skb)->dev, dst_output);
|
||||||
|
|
||||||
return dst_output(net, sk, skb);
|
return dst_output(net, sk, skb);
|
||||||
drop:
|
drop:
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int seg6_output_nf(struct net *net, struct sock *sk, struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
struct net_device *dev = skb_dst(skb)->dev;
|
||||||
|
|
||||||
|
switch (skb->protocol) {
|
||||||
|
case htons(ETH_P_IP):
|
||||||
|
return NF_HOOK(NFPROTO_IPV4, NF_INET_POST_ROUTING, net, sk, skb,
|
||||||
|
NULL, dev, seg6_output_core);
|
||||||
|
case htons(ETH_P_IPV6):
|
||||||
|
return NF_HOOK(NFPROTO_IPV6, NF_INET_POST_ROUTING, net, sk, skb,
|
||||||
|
NULL, dev, seg6_output_core);
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int seg6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled))
|
||||||
|
return seg6_output_nf(net, sk, skb);
|
||||||
|
|
||||||
|
return seg6_output_core(net, sk, skb);
|
||||||
|
}
|
||||||
|
|
||||||
static int seg6_build_state(struct net *net, struct nlattr *nla,
|
static int seg6_build_state(struct net *net, struct nlattr *nla,
|
||||||
unsigned int family, const void *cfg,
|
unsigned int family, const void *cfg,
|
||||||
struct lwtunnel_state **ts,
|
struct lwtunnel_state **ts,
|
||||||
|
|
|
@ -30,6 +30,8 @@
|
||||||
#include <net/seg6_local.h>
|
#include <net/seg6_local.h>
|
||||||
#include <linux/etherdevice.h>
|
#include <linux/etherdevice.h>
|
||||||
#include <linux/bpf.h>
|
#include <linux/bpf.h>
|
||||||
|
#include <net/lwtunnel.h>
|
||||||
|
#include <linux/netfilter.h>
|
||||||
|
|
||||||
#define SEG6_F_ATTR(i) BIT(i)
|
#define SEG6_F_ATTR(i) BIT(i)
|
||||||
|
|
||||||
|
@ -413,12 +415,33 @@ drop:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int input_action_end_dx6_finish(struct net *net, struct sock *sk,
|
||||||
|
struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
struct dst_entry *orig_dst = skb_dst(skb);
|
||||||
|
struct in6_addr *nhaddr = NULL;
|
||||||
|
struct seg6_local_lwt *slwt;
|
||||||
|
|
||||||
|
slwt = seg6_local_lwtunnel(orig_dst->lwtstate);
|
||||||
|
|
||||||
|
/* The inner packet is not associated to any local interface,
|
||||||
|
* so we do not call netif_rx().
|
||||||
|
*
|
||||||
|
* If slwt->nh6 is set to ::, then lookup the nexthop for the
|
||||||
|
* inner packet's DA. Otherwise, use the specified nexthop.
|
||||||
|
*/
|
||||||
|
if (!ipv6_addr_any(&slwt->nh6))
|
||||||
|
nhaddr = &slwt->nh6;
|
||||||
|
|
||||||
|
seg6_lookup_nexthop(skb, nhaddr, 0);
|
||||||
|
|
||||||
|
return dst_input(skb);
|
||||||
|
}
|
||||||
|
|
||||||
/* decapsulate and forward to specified nexthop */
|
/* decapsulate and forward to specified nexthop */
|
||||||
static int input_action_end_dx6(struct sk_buff *skb,
|
static int input_action_end_dx6(struct sk_buff *skb,
|
||||||
struct seg6_local_lwt *slwt)
|
struct seg6_local_lwt *slwt)
|
||||||
{
|
{
|
||||||
struct in6_addr *nhaddr = NULL;
|
|
||||||
|
|
||||||
/* this function accepts IPv6 encapsulated packets, with either
|
/* this function accepts IPv6 encapsulated packets, with either
|
||||||
* an SRH with SL=0, or no SRH.
|
* an SRH with SL=0, or no SRH.
|
||||||
*/
|
*/
|
||||||
|
@ -429,40 +452,30 @@ static int input_action_end_dx6(struct sk_buff *skb,
|
||||||
if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
|
if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
|
||||||
goto drop;
|
goto drop;
|
||||||
|
|
||||||
/* The inner packet is not associated to any local interface,
|
|
||||||
* so we do not call netif_rx().
|
|
||||||
*
|
|
||||||
* If slwt->nh6 is set to ::, then lookup the nexthop for the
|
|
||||||
* inner packet's DA. Otherwise, use the specified nexthop.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (!ipv6_addr_any(&slwt->nh6))
|
|
||||||
nhaddr = &slwt->nh6;
|
|
||||||
|
|
||||||
skb_set_transport_header(skb, sizeof(struct ipv6hdr));
|
skb_set_transport_header(skb, sizeof(struct ipv6hdr));
|
||||||
|
nf_reset_ct(skb);
|
||||||
|
|
||||||
seg6_lookup_nexthop(skb, nhaddr, 0);
|
if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled))
|
||||||
|
return NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING,
|
||||||
|
dev_net(skb->dev), NULL, skb, NULL,
|
||||||
|
skb_dst(skb)->dev, input_action_end_dx6_finish);
|
||||||
|
|
||||||
return dst_input(skb);
|
return input_action_end_dx6_finish(dev_net(skb->dev), NULL, skb);
|
||||||
drop:
|
drop:
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int input_action_end_dx4(struct sk_buff *skb,
|
static int input_action_end_dx4_finish(struct net *net, struct sock *sk,
|
||||||
struct seg6_local_lwt *slwt)
|
struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
|
struct dst_entry *orig_dst = skb_dst(skb);
|
||||||
|
struct seg6_local_lwt *slwt;
|
||||||
struct iphdr *iph;
|
struct iphdr *iph;
|
||||||
__be32 nhaddr;
|
__be32 nhaddr;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (!decap_and_validate(skb, IPPROTO_IPIP))
|
slwt = seg6_local_lwtunnel(orig_dst->lwtstate);
|
||||||
goto drop;
|
|
||||||
|
|
||||||
if (!pskb_may_pull(skb, sizeof(struct iphdr)))
|
|
||||||
goto drop;
|
|
||||||
|
|
||||||
skb->protocol = htons(ETH_P_IP);
|
|
||||||
|
|
||||||
iph = ip_hdr(skb);
|
iph = ip_hdr(skb);
|
||||||
|
|
||||||
|
@ -470,14 +483,34 @@ static int input_action_end_dx4(struct sk_buff *skb,
|
||||||
|
|
||||||
skb_dst_drop(skb);
|
skb_dst_drop(skb);
|
||||||
|
|
||||||
skb_set_transport_header(skb, sizeof(struct iphdr));
|
|
||||||
|
|
||||||
err = ip_route_input(skb, nhaddr, iph->saddr, 0, skb->dev);
|
err = ip_route_input(skb, nhaddr, iph->saddr, 0, skb->dev);
|
||||||
if (err)
|
if (err) {
|
||||||
goto drop;
|
kfree_skb(skb);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
return dst_input(skb);
|
return dst_input(skb);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int input_action_end_dx4(struct sk_buff *skb,
|
||||||
|
struct seg6_local_lwt *slwt)
|
||||||
|
{
|
||||||
|
if (!decap_and_validate(skb, IPPROTO_IPIP))
|
||||||
|
goto drop;
|
||||||
|
|
||||||
|
if (!pskb_may_pull(skb, sizeof(struct iphdr)))
|
||||||
|
goto drop;
|
||||||
|
|
||||||
|
skb->protocol = htons(ETH_P_IP);
|
||||||
|
skb_set_transport_header(skb, sizeof(struct iphdr));
|
||||||
|
nf_reset_ct(skb);
|
||||||
|
|
||||||
|
if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled))
|
||||||
|
return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING,
|
||||||
|
dev_net(skb->dev), NULL, skb, NULL,
|
||||||
|
skb_dst(skb)->dev, input_action_end_dx4_finish);
|
||||||
|
|
||||||
|
return input_action_end_dx4_finish(dev_net(skb->dev), NULL, skb);
|
||||||
drop:
|
drop:
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -645,6 +678,7 @@ static struct sk_buff *end_dt_vrf_core(struct sk_buff *skb,
|
||||||
skb_dst_drop(skb);
|
skb_dst_drop(skb);
|
||||||
|
|
||||||
skb_set_transport_header(skb, hdrlen);
|
skb_set_transport_header(skb, hdrlen);
|
||||||
|
nf_reset_ct(skb);
|
||||||
|
|
||||||
return end_dt_vrf_rcv(skb, family, vrf);
|
return end_dt_vrf_rcv(skb, family, vrf);
|
||||||
|
|
||||||
|
@ -1078,7 +1112,8 @@ static void seg6_local_update_counters(struct seg6_local_lwt *slwt,
|
||||||
u64_stats_update_end(&pcounters->syncp);
|
u64_stats_update_end(&pcounters->syncp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int seg6_local_input(struct sk_buff *skb)
|
static int seg6_local_input_core(struct net *net, struct sock *sk,
|
||||||
|
struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct dst_entry *orig_dst = skb_dst(skb);
|
struct dst_entry *orig_dst = skb_dst(skb);
|
||||||
struct seg6_action_desc *desc;
|
struct seg6_action_desc *desc;
|
||||||
|
@ -1086,11 +1121,6 @@ static int seg6_local_input(struct sk_buff *skb)
|
||||||
unsigned int len = skb->len;
|
unsigned int len = skb->len;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if (skb->protocol != htons(ETH_P_IPV6)) {
|
|
||||||
kfree_skb(skb);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
slwt = seg6_local_lwtunnel(orig_dst->lwtstate);
|
slwt = seg6_local_lwtunnel(orig_dst->lwtstate);
|
||||||
desc = slwt->desc;
|
desc = slwt->desc;
|
||||||
|
|
||||||
|
@ -1104,6 +1134,21 @@ static int seg6_local_input(struct sk_buff *skb)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int seg6_local_input(struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
if (skb->protocol != htons(ETH_P_IPV6)) {
|
||||||
|
kfree_skb(skb);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled))
|
||||||
|
return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_IN,
|
||||||
|
dev_net(skb->dev), NULL, skb, skb->dev, NULL,
|
||||||
|
seg6_local_input_core);
|
||||||
|
|
||||||
|
return seg6_local_input_core(dev_net(skb->dev), NULL, skb);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct nla_policy seg6_local_policy[SEG6_LOCAL_MAX + 1] = {
|
static const struct nla_policy seg6_local_policy[SEG6_LOCAL_MAX + 1] = {
|
||||||
[SEG6_LOCAL_ACTION] = { .type = NLA_U32 },
|
[SEG6_LOCAL_ACTION] = { .type = NLA_U32 },
|
||||||
[SEG6_LOCAL_SRH] = { .type = NLA_BINARY },
|
[SEG6_LOCAL_SRH] = { .type = NLA_BINARY },
|
||||||
|
|
|
@ -212,3 +212,6 @@ obj-$(CONFIG_IP_SET) += ipset/
|
||||||
|
|
||||||
# IPVS
|
# IPVS
|
||||||
obj-$(CONFIG_IP_VS) += ipvs/
|
obj-$(CONFIG_IP_VS) += ipvs/
|
||||||
|
|
||||||
|
# lwtunnel
|
||||||
|
obj-$(CONFIG_LWTUNNEL) += nf_hooks_lwtunnel.o
|
||||||
|
|
|
@ -22,6 +22,9 @@
|
||||||
#include <net/netfilter/nf_conntrack_acct.h>
|
#include <net/netfilter/nf_conntrack_acct.h>
|
||||||
#include <net/netfilter/nf_conntrack_zones.h>
|
#include <net/netfilter/nf_conntrack_zones.h>
|
||||||
#include <net/netfilter/nf_conntrack_timestamp.h>
|
#include <net/netfilter/nf_conntrack_timestamp.h>
|
||||||
|
#ifdef CONFIG_LWTUNNEL
|
||||||
|
#include <net/netfilter/nf_hooks_lwtunnel.h>
|
||||||
|
#endif
|
||||||
#include <linux/rculist_nulls.h>
|
#include <linux/rculist_nulls.h>
|
||||||
|
|
||||||
static bool enable_hooks __read_mostly;
|
static bool enable_hooks __read_mostly;
|
||||||
|
@ -612,6 +615,9 @@ enum nf_ct_sysctl_index {
|
||||||
NF_SYSCTL_CT_PROTO_TIMEOUT_GRE,
|
NF_SYSCTL_CT_PROTO_TIMEOUT_GRE,
|
||||||
NF_SYSCTL_CT_PROTO_TIMEOUT_GRE_STREAM,
|
NF_SYSCTL_CT_PROTO_TIMEOUT_GRE_STREAM,
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef CONFIG_LWTUNNEL
|
||||||
|
NF_SYSCTL_CT_LWTUNNEL,
|
||||||
|
#endif
|
||||||
|
|
||||||
__NF_SYSCTL_CT_LAST_SYSCTL,
|
__NF_SYSCTL_CT_LAST_SYSCTL,
|
||||||
};
|
};
|
||||||
|
@ -958,6 +964,15 @@ static struct ctl_table nf_ct_sysctl_table[] = {
|
||||||
.mode = 0644,
|
.mode = 0644,
|
||||||
.proc_handler = proc_dointvec_jiffies,
|
.proc_handler = proc_dointvec_jiffies,
|
||||||
},
|
},
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_LWTUNNEL
|
||||||
|
[NF_SYSCTL_CT_LWTUNNEL] = {
|
||||||
|
.procname = "nf_hooks_lwtunnel",
|
||||||
|
.data = NULL,
|
||||||
|
.maxlen = sizeof(int),
|
||||||
|
.mode = 0644,
|
||||||
|
.proc_handler = nf_hooks_lwtunnel_sysctl_handler,
|
||||||
|
},
|
||||||
#endif
|
#endif
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
#include <linux/sysctl.h>
|
||||||
|
#include <net/lwtunnel.h>
|
||||||
|
#include <net/netfilter/nf_hooks_lwtunnel.h>
|
||||||
|
|
||||||
|
static inline int nf_hooks_lwtunnel_get(void)
|
||||||
|
{
|
||||||
|
if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled))
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int nf_hooks_lwtunnel_set(int enable)
|
||||||
|
{
|
||||||
|
if (static_branch_unlikely(&nf_hooks_lwtunnel_enabled)) {
|
||||||
|
if (!enable)
|
||||||
|
return -EBUSY;
|
||||||
|
} else if (enable) {
|
||||||
|
static_branch_enable(&nf_hooks_lwtunnel_enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_SYSCTL
|
||||||
|
int nf_hooks_lwtunnel_sysctl_handler(struct ctl_table *table, int write,
|
||||||
|
void *buffer, size_t *lenp, loff_t *ppos)
|
||||||
|
{
|
||||||
|
int proc_nf_hooks_lwtunnel_enabled = 0;
|
||||||
|
struct ctl_table tmp = {
|
||||||
|
.procname = table->procname,
|
||||||
|
.data = &proc_nf_hooks_lwtunnel_enabled,
|
||||||
|
.maxlen = sizeof(int),
|
||||||
|
.mode = table->mode,
|
||||||
|
.extra1 = SYSCTL_ZERO,
|
||||||
|
.extra2 = SYSCTL_ONE,
|
||||||
|
};
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!write)
|
||||||
|
proc_nf_hooks_lwtunnel_enabled = nf_hooks_lwtunnel_get();
|
||||||
|
|
||||||
|
ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
|
||||||
|
|
||||||
|
if (write && ret == 0)
|
||||||
|
ret = nf_hooks_lwtunnel_set(proc_nf_hooks_lwtunnel_enabled);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(nf_hooks_lwtunnel_sysctl_handler);
|
||||||
|
#endif /* CONFIG_SYSCTL */
|
Загрузка…
Ссылка в новой задаче