Commit 47cfb904 authored by David S. Miller's avatar David S. Miller

Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf-next

Pablo Neira Ayuso says:

====================
Netfilter/IPVS updates for net-next

The following patchset contains Netfilter/IPVS updates for net-next:

1) Move bridge keys in nft_meta to nft_meta_bridge, from wenxu.

2) Support for bridge pvid matching, from wenxu.

3) Support for bridge vlan protocol matching, also from wenxu.

4) Add br_vlan_get_pvid_rcu(), to fetch the bridge port pvid
   from packet path.

5) Prefer specific family extension in nf_tables.

6) Autoload specific family extension in case it is missing.

7) Add synproxy support to nf_tables, from Fernando Fernandez Mancera.

8) Support for GRE encapsulation in IPVS, from Vadim Fedorenko.

9) ICMP handling for GRE encapsulation, from Julian Anastasov.

10) Remove unused parameter in nf_queue, from Florian Westphal.

11) Replace seq_printf() by seq_puts() in nf_log, from Markus Elfring.

12) Rename nf_SYNPROXY.h => nf_synproxy.h before this header becomes
    public.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents bfb20412 0ef1efd1
......@@ -88,6 +88,8 @@ static inline bool br_multicast_router(const struct net_device *dev)
#if IS_ENABLED(CONFIG_BRIDGE) && IS_ENABLED(CONFIG_BRIDGE_VLAN_FILTERING)
bool br_vlan_enabled(const struct net_device *dev);
int br_vlan_get_pvid(const struct net_device *dev, u16 *p_pvid);
int br_vlan_get_pvid_rcu(const struct net_device *dev, u16 *p_pvid);
int br_vlan_get_proto(const struct net_device *dev, u16 *p_proto);
int br_vlan_get_info(const struct net_device *dev, u16 vid,
struct bridge_vlan_info *p_vinfo);
#else
......@@ -101,6 +103,16 @@ static inline int br_vlan_get_pvid(const struct net_device *dev, u16 *p_pvid)
return -EINVAL;
}
static inline int br_vlan_get_proto(const struct net_device *dev, u16 *p_proto)
{
return -EINVAL;
}
static inline int br_vlan_get_pvid_rcu(const struct net_device *dev, u16 *p_pvid)
{
return -EINVAL;
}
static inline int br_vlan_get_info(const struct net_device *dev, u16 vid,
struct bridge_vlan_info *p_vinfo)
{
......
......@@ -2,6 +2,7 @@
#ifndef _NF_CONNTRACK_SYNPROXY_H
#define _NF_CONNTRACK_SYNPROXY_H
#include <net/netfilter/nf_conntrack_seqadj.h>
#include <net/netns/generic.h>
struct nf_conn_synproxy {
......
......@@ -120,6 +120,5 @@ nfqueue_hash(const struct sk_buff *skb, u16 queue, u16 queues_total, u8 family,
}
int nf_queue(struct sk_buff *skb, struct nf_hook_state *state,
const struct nf_hook_entries *entries, unsigned int index,
unsigned int verdict);
unsigned int index, unsigned int verdict);
#endif /* _NF_QUEUE_H */
......@@ -39,6 +39,11 @@ unsigned int ipv6_synproxy_hook(void *priv, struct sk_buff *skb,
const struct nf_hook_state *nhs);
int nf_synproxy_ipv6_init(struct synproxy_net *snet, struct net *net);
void nf_synproxy_ipv6_fini(struct synproxy_net *snet, struct net *net);
#else
static inline int
nf_synproxy_ipv6_init(struct synproxy_net *snet, struct net *net) { return 0; }
static inline void
nf_synproxy_ipv6_fini(struct synproxy_net *snet, struct net *net) {};
#endif /* CONFIG_IPV6 */
#endif /* _NF_SYNPROXY_SHARED_H */
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _NFT_META_H_
#define _NFT_META_H_
struct nft_meta {
enum nft_meta_keys key:8;
union {
enum nft_registers dreg:8;
enum nft_registers sreg:8;
};
};
extern const struct nla_policy nft_meta_policy[];
int nft_meta_get_init(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[]);
int nft_meta_set_init(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[]);
int nft_meta_get_dump(struct sk_buff *skb,
const struct nft_expr *expr);
int nft_meta_set_dump(struct sk_buff *skb,
const struct nft_expr *expr);
void nft_meta_get_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt);
void nft_meta_set_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt);
void nft_meta_set_destroy(const struct nft_ctx *ctx,
const struct nft_expr *expr);
int nft_meta_set_validate(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nft_data **data);
#endif
......@@ -128,6 +128,7 @@
enum {
IP_VS_CONN_F_TUNNEL_TYPE_IPIP = 0, /* IPIP */
IP_VS_CONN_F_TUNNEL_TYPE_GUE, /* GUE */
IP_VS_CONN_F_TUNNEL_TYPE_GRE, /* GRE */
IP_VS_CONN_F_TUNNEL_TYPE_MAX,
};
......
......@@ -9,6 +9,10 @@
#define NF_SYNPROXY_OPT_SACK_PERM 0x04
#define NF_SYNPROXY_OPT_TIMESTAMP 0x08
#define NF_SYNPROXY_OPT_ECN 0x10
#define NF_SYNPROXY_OPT_MASK (NF_SYNPROXY_OPT_MSS | \
NF_SYNPROXY_OPT_WSCALE | \
NF_SYNPROXY_OPT_SACK_PERM | \
NF_SYNPROXY_OPT_TIMESTAMP)
struct nf_synproxy_info {
__u8 options;
......
......@@ -795,6 +795,8 @@ enum nft_exthdr_attributes {
* @NFT_META_SECPATH: boolean, secpath_exists (!!skb->sp)
* @NFT_META_IIFKIND: packet input interface kind name (dev->rtnl_link_ops->kind)
* @NFT_META_OIFKIND: packet output interface kind name (dev->rtnl_link_ops->kind)
* @NFT_META_BRI_IIFPVID: packet input bridge port pvid
* @NFT_META_BRI_IIFVPROTO: packet input bridge vlan proto
*/
enum nft_meta_keys {
NFT_META_LEN,
......@@ -825,6 +827,8 @@ enum nft_meta_keys {
NFT_META_SECPATH,
NFT_META_IIFKIND,
NFT_META_OIFKIND,
NFT_META_BRI_IIFPVID,
NFT_META_BRI_IIFVPROTO,
};
/**
......@@ -1551,6 +1555,22 @@ enum nft_osf_flags {
NFT_OSF_F_VERSION = (1 << 0),
};
/**
* enum nft_synproxy_attributes - nf_tables synproxy expression netlink attributes
*
* @NFTA_SYNPROXY_MSS: mss value sent to the backend (NLA_U16)
* @NFTA_SYNPROXY_WSCALE: wscale value sent to the backend (NLA_U8)
* @NFTA_SYNPROXY_FLAGS: flags (NLA_U32)
*/
enum nft_synproxy_attributes {
NFTA_SYNPROXY_UNSPEC,
NFTA_SYNPROXY_MSS,
NFTA_SYNPROXY_WSCALE,
NFTA_SYNPROXY_FLAGS,
__NFTA_SYNPROXY_MAX,
};
#define NFTA_SYNPROXY_MAX (__NFTA_SYNPROXY_MAX - 1)
/**
* enum nft_device_attributes - nf_tables device netlink attributes
*
......
......@@ -2,7 +2,7 @@
#ifndef _XT_SYNPROXY_H
#define _XT_SYNPROXY_H
#include <linux/netfilter/nf_SYNPROXY.h>
#include <linux/netfilter/nf_synproxy.h>
#define XT_SYNPROXY_OPT_MSS NF_SYNPROXY_OPT_MSS
#define XT_SYNPROXY_OPT_WSCALE NF_SYNPROXY_OPT_WSCALE
......
......@@ -234,7 +234,7 @@ static int nf_hook_bridge_pre(struct sk_buff *skb, struct sk_buff **pskb)
kfree_skb(skb);
return RX_HANDLER_CONSUMED;
case NF_QUEUE:
ret = nf_queue(skb, &state, e, i, verdict);
ret = nf_queue(skb, &state, i, verdict);
if (ret == 1)
continue;
return RX_HANDLER_CONSUMED;
......
......@@ -797,6 +797,16 @@ bool br_vlan_enabled(const struct net_device *dev)
}
EXPORT_SYMBOL_GPL(br_vlan_enabled);
int br_vlan_get_proto(const struct net_device *dev, u16 *p_proto)
{
struct net_bridge *br = netdev_priv(dev);
*p_proto = ntohs(br->vlan_proto);
return 0;
}
EXPORT_SYMBOL_GPL(br_vlan_get_proto);
int __br_vlan_set_proto(struct net_bridge *br, __be16 proto)
{
int err = 0;
......@@ -1227,13 +1237,11 @@ void br_vlan_get_stats(const struct net_bridge_vlan *v,
}
}
int br_vlan_get_pvid(const struct net_device *dev, u16 *p_pvid)
static int __br_vlan_get_pvid(const struct net_device *dev,
struct net_bridge_port *p, u16 *p_pvid)
{
struct net_bridge_vlan_group *vg;
struct net_bridge_port *p;
ASSERT_RTNL();
p = br_port_get_check_rtnl(dev);
if (p)
vg = nbp_vlan_group(p);
else if (netif_is_bridge_master(dev))
......@@ -1244,8 +1252,21 @@ int br_vlan_get_pvid(const struct net_device *dev, u16 *p_pvid)
*p_pvid = br_get_pvid(vg);
return 0;
}
int br_vlan_get_pvid(const struct net_device *dev, u16 *p_pvid)
{
ASSERT_RTNL();
return __br_vlan_get_pvid(dev, br_port_get_check_rtnl(dev), p_pvid);
}
EXPORT_SYMBOL_GPL(br_vlan_get_pvid);
int br_vlan_get_pvid_rcu(const struct net_device *dev, u16 *p_pvid)
{
return __br_vlan_get_pvid(dev, br_port_get_check_rcu(dev), p_pvid);
}
EXPORT_SYMBOL_GPL(br_vlan_get_pvid_rcu);
int br_vlan_get_info(const struct net_device *dev, u16 vid,
struct bridge_vlan_info *p_vinfo)
{
......
......@@ -9,6 +9,12 @@ menuconfig NF_TABLES_BRIDGE
bool "Ethernet Bridge nf_tables support"
if NF_TABLES_BRIDGE
config NFT_BRIDGE_META
tristate "Netfilter nf_table bridge meta support"
help
Add support for bridge dedicated meta key.
config NFT_BRIDGE_REJECT
tristate "Netfilter nf_tables bridge reject support"
depends on NFT_REJECT && NFT_REJECT_IPV4 && NFT_REJECT_IPV6
......
......@@ -3,6 +3,7 @@
# Makefile for the netfilter modules for Link Layer filtering on a bridge.
#
obj-$(CONFIG_NFT_BRIDGE_META) += nft_meta_bridge.o
obj-$(CONFIG_NFT_BRIDGE_REJECT) += nft_reject_bridge.o
# connection tracking
......
// SPDX-License-Identifier: GPL-2.0
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/netlink.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables.h>
#include <net/netfilter/nft_meta.h>
#include <linux/if_bridge.h>
static const struct net_device *
nft_meta_get_bridge(const struct net_device *dev)
{
if (dev && netif_is_bridge_port(dev))
return netdev_master_upper_dev_get_rcu((struct net_device *)dev);
return NULL;
}
static void nft_meta_bridge_get_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
{
const struct nft_meta *priv = nft_expr_priv(expr);
const struct net_device *in = nft_in(pkt), *out = nft_out(pkt);
u32 *dest = &regs->data[priv->dreg];
const struct net_device *br_dev;
switch (priv->key) {
case NFT_META_BRI_IIFNAME:
br_dev = nft_meta_get_bridge(in);
if (!br_dev)
goto err;
break;
case NFT_META_BRI_OIFNAME:
br_dev = nft_meta_get_bridge(out);
if (!br_dev)
goto err;
break;
case NFT_META_BRI_IIFPVID: {
u16 p_pvid;
br_dev = nft_meta_get_bridge(in);
if (!br_dev || !br_vlan_enabled(br_dev))
goto err;
br_vlan_get_pvid_rcu(in, &p_pvid);
nft_reg_store16(dest, p_pvid);
return;
}
case NFT_META_BRI_IIFVPROTO: {
u16 p_proto;
br_dev = nft_meta_get_bridge(in);
if (!br_dev || !br_vlan_enabled(br_dev))
goto err;
br_vlan_get_proto(br_dev, &p_proto);
nft_reg_store16(dest, p_proto);
return;
}
default:
goto out;
}
strncpy((char *)dest, br_dev->name, IFNAMSIZ);
return;
out:
return nft_meta_get_eval(expr, regs, pkt);
err:
regs->verdict.code = NFT_BREAK;
}
static int nft_meta_bridge_get_init(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[])
{
struct nft_meta *priv = nft_expr_priv(expr);
unsigned int len;
priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY]));
switch (priv->key) {
case NFT_META_BRI_IIFNAME:
case NFT_META_BRI_OIFNAME:
len = IFNAMSIZ;
break;
case NFT_META_BRI_IIFPVID:
case NFT_META_BRI_IIFVPROTO:
len = sizeof(u16);
break;
default:
return nft_meta_get_init(ctx, expr, tb);
}
priv->dreg = nft_parse_register(tb[NFTA_META_DREG]);
return nft_validate_register_store(ctx, priv->dreg, NULL,
NFT_DATA_VALUE, len);
}
static struct nft_expr_type nft_meta_bridge_type;
static const struct nft_expr_ops nft_meta_bridge_get_ops = {
.type = &nft_meta_bridge_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_meta)),
.eval = nft_meta_bridge_get_eval,
.init = nft_meta_bridge_get_init,
.dump = nft_meta_get_dump,
};
static const struct nft_expr_ops nft_meta_bridge_set_ops = {
.type = &nft_meta_bridge_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_meta)),
.eval = nft_meta_set_eval,
.init = nft_meta_set_init,
.destroy = nft_meta_set_destroy,
.dump = nft_meta_set_dump,
.validate = nft_meta_set_validate,
};
static const struct nft_expr_ops *
nft_meta_bridge_select_ops(const struct nft_ctx *ctx,
const struct nlattr * const tb[])
{
if (tb[NFTA_META_KEY] == NULL)
return ERR_PTR(-EINVAL);
if (tb[NFTA_META_DREG] && tb[NFTA_META_SREG])
return ERR_PTR(-EINVAL);
if (tb[NFTA_META_DREG])
return &nft_meta_bridge_get_ops;
if (tb[NFTA_META_SREG])
return &nft_meta_bridge_set_ops;
return ERR_PTR(-EINVAL);
}
static struct nft_expr_type nft_meta_bridge_type __read_mostly = {
.family = NFPROTO_BRIDGE,
.name = "meta",
.select_ops = nft_meta_bridge_select_ops,
.policy = nft_meta_policy,
.maxattr = NFTA_META_MAX,
.owner = THIS_MODULE,
};
static int __init nft_meta_bridge_module_init(void)
{
return nft_register_expr(&nft_meta_bridge_type);
}
static void __exit nft_meta_bridge_module_exit(void)
{
nft_unregister_expr(&nft_meta_bridge_type);
}
module_init(nft_meta_bridge_module_init);
module_exit(nft_meta_bridge_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("wenxu <wenxu@ucloud.cn>");
MODULE_ALIAS_NFT_AF_EXPR(AF_BRIDGE, "meta");
......@@ -651,6 +651,17 @@ config NFT_TPROXY
help
This makes transparent proxy support available in nftables.
config NFT_SYNPROXY
tristate "Netfilter nf_tables SYNPROXY expression support"
depends on NF_CONNTRACK && NETFILTER_ADVANCED
select NETFILTER_SYNPROXY
select SYN_COOKIES
help
The SYNPROXY expression allows you to intercept TCP connections and
establish them using syncookies before they are passed on to the
server. This allows to avoid conntrack and server resource usage
during SYN-flood attacks.
if NF_TABLES_NETDEV
config NF_DUP_NETDEV
......
......@@ -110,6 +110,7 @@ obj-$(CONFIG_NFT_SOCKET) += nft_socket.o
obj-$(CONFIG_NFT_OSF) += nft_osf.o
obj-$(CONFIG_NFT_TPROXY) += nft_tproxy.o
obj-$(CONFIG_NFT_XFRM) += nft_xfrm.o
obj-$(CONFIG_NFT_SYNPROXY) += nft_synproxy.o
obj-$(CONFIG_NFT_NAT) += nft_chain_nat.o
......
......@@ -520,7 +520,7 @@ int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state,
ret = -EPERM;
return ret;
case NF_QUEUE:
ret = nf_queue(skb, state, e, s, verdict);
ret = nf_queue(skb, state, s, verdict);
if (ret == 1)
continue;
return ret;
......
......@@ -35,6 +35,7 @@
#include <net/udp.h>
#include <net/icmp.h> /* for icmp_send */
#include <net/gue.h>
#include <net/gre.h>
#include <net/route.h>
#include <net/ip6_checksum.h>
#include <net/netns/generic.h> /* net_generic() */
......@@ -1610,6 +1611,38 @@ static int ipvs_udp_decap(struct netns_ipvs *ipvs, struct sk_buff *skb,
return 0;
}
/* Check the GRE tunnel and return its header length */
static int ipvs_gre_decap(struct netns_ipvs *ipvs, struct sk_buff *skb,
unsigned int offset, __u16 af,
const union nf_inet_addr *daddr, __u8 *proto)
{
struct gre_base_hdr _greh, *greh;
struct ip_vs_dest *dest;
greh = skb_header_pointer(skb, offset, sizeof(_greh), &_greh);
if (!greh)
goto unk;
dest = ip_vs_find_tunnel(ipvs, af, daddr, 0);
if (!dest)
goto unk;
if (dest->tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GRE) {
__be16 type;
/* Only support version 0 and C (csum) */
if ((greh->flags & ~GRE_CSUM) != 0)
goto unk;
type = greh->protocol;
/* Later we can support also IPPROTO_IPV6 */
if (type != htons(ETH_P_IP))
goto unk;
*proto = IPPROTO_IPIP;
return gre_calc_hlen(gre_flags_to_tnl_flags(greh->flags));
}
unk:
return 0;
}
/*
* Handle ICMP messages in the outside-to-inside direction (incoming).
* Find any that might be relevant, check against existing connections,
......@@ -1689,7 +1722,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
if (cih == NULL)
return NF_ACCEPT; /* The packet looks wrong, ignore */
ipip = true;
} else if (cih->protocol == IPPROTO_UDP && /* Can be UDP encap */
} else if ((cih->protocol == IPPROTO_UDP || /* Can be UDP encap */
cih->protocol == IPPROTO_GRE) && /* Can be GRE encap */
/* Error for our tunnel must arrive at LOCAL_IN */
(skb_rtable(skb)->rt_flags & RTCF_LOCAL)) {
__u8 iproto;
......@@ -1699,10 +1733,14 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
if (unlikely(cih->frag_off & htons(IP_OFFSET)))
return NF_ACCEPT;
offset2 = offset + cih->ihl * 4;
ulen = ipvs_udp_decap(ipvs, skb, offset2, AF_INET, raddr,
&iproto);
if (cih->protocol == IPPROTO_UDP)
ulen = ipvs_udp_decap(ipvs, skb, offset2, AF_INET,
raddr, &iproto);
else
ulen = ipvs_gre_decap(ipvs, skb, offset2, AF_INET,
raddr, &iproto);
if (ulen > 0) {
/* Skip IP and UDP tunnel headers */
/* Skip IP and UDP/GRE tunnel headers */
offset = offset2 + ulen;
/* Now we should be at the original IP header */
cih = skb_header_pointer(skb, offset, sizeof(_ciph),
......
......@@ -525,6 +525,7 @@ static void ip_vs_rs_hash(struct netns_ipvs *ipvs, struct ip_vs_dest *dest)
port = dest->tun_port;
break;
case IP_VS_CONN_F_TUNNEL_TYPE_IPIP:
case IP_VS_CONN_F_TUNNEL_TYPE_GRE:
port = 0;
break;
default:
......
......@@ -29,6 +29,7 @@
#include <linux/tcp.h> /* for tcphdr */
#include <net/ip.h>
#include <net/gue.h>
#include <net/gre.h>
#include <net/tcp.h> /* for csum_tcpudp_magic */
#include <net/udp.h>
#include <net/icmp.h> /* for icmp_send */
......@@ -388,6 +389,12 @@ __ip_vs_get_out_rt(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb,
IP_VS_TUNNEL_ENCAP_FLAG_REMCSUM) &&
skb->ip_summed == CHECKSUM_PARTIAL)
mtu -= GUE_PLEN_REMCSUM + GUE_LEN_PRIV;
} else if (dest->tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GRE) {
__be16 tflags = 0;
if (dest->tun_flags & IP_VS_TUNNEL_ENCAP_FLAG_CSUM)
tflags |= TUNNEL_CSUM;
mtu -= gre_calc_hlen(tflags);
}
if (mtu < 68) {
IP_VS_DBG_RL("%s(): mtu less than 68\n", __func__);
......@@ -548,6 +555,12 @@ __ip_vs_get_out_rt_v6(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb,
IP_VS_TUNNEL_ENCAP_FLAG_REMCSUM) &&
skb->ip_summed == CHECKSUM_PARTIAL)
mtu -= GUE_PLEN_REMCSUM + GUE_LEN_PRIV;
} else if (dest->tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GRE) {
__be16 tflags = 0;
if (dest->tun_flags & IP_VS_TUNNEL_ENCAP_FLAG_CSUM)
tflags |= TUNNEL_CSUM;
mtu -= gre_calc_hlen(tflags);
}
if (mtu < IPV6_MIN_MTU) {
IP_VS_DBG_RL("%s(): mtu less than %d\n", __func__,
......@@ -1079,6 +1092,24 @@ ipvs_gue_encap(struct net *net, struct sk_buff *skb,
return 0;
}
static void
ipvs_gre_encap(struct net *net, struct sk_buff *skb,
struct ip_vs_conn *cp, __u8 *next_protocol)
{
__be16 proto = *next_protocol == IPPROTO_IPIP ?
htons(ETH_P_IP) : htons(ETH_P_IPV6);
__be16 tflags = 0;
size_t hdrlen;
if (cp->dest->tun_flags & IP_VS_TUNNEL_ENCAP_FLAG_CSUM)
tflags |= TUNNEL_CSUM;
hdrlen = gre_calc_hlen(tflags);
gre_build_header(skb, hdrlen, tflags, proto, 0, 0);
*next_protocol = IPPROTO_GRE;
}
/*
* IP Tunneling transmitter
*
......@@ -1151,6 +1182,15 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
gue_hdrlen = sizeof(struct guehdr) + gue_optlen;
max_headroom += sizeof(struct udphdr) + gue_hdrlen;
} else if (tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GRE) {
size_t gre_hdrlen;
__be16 tflags = 0;
if (tun_flags & IP_VS_TUNNEL_ENCAP_FLAG_CSUM)
tflags |= TUNNEL_CSUM;
gre_hdrlen = gre_calc_hlen(tflags);
max_headroom += gre_hdrlen;
}
/* We only care about the df field if sysctl_pmtu_disc(ipvs) is set */
......@@ -1172,6 +1212,11 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
skb->ip_summed == CHECKSUM_PARTIAL) {
gso_type |= SKB_GSO_TUNNEL_REMCSUM;
}
} else if (tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GRE) {
if (tun_flags & IP_VS_TUNNEL_ENCAP_FLAG_CSUM)
gso_type |= SKB_GSO_GRE_CSUM;
else
gso_type |= SKB_GSO_GRE;
}
if (iptunnel_handle_offloads(skb, gso_type))
......@@ -1192,8 +1237,8 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
check = true;
udp_set_csum(!check, skb, saddr, cp->daddr.ip, skb->len);
}
} else if (tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GRE)
ipvs_gre_encap(net, skb, cp, &next_protocol);
skb_push(skb, sizeof(struct iphdr));
skb_reset_network_header(skb);
......@@ -1287,6 +1332,15 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
gue_hdrlen = sizeof(struct guehdr) + gue_optlen;
max_headroom += sizeof(struct udphdr) + gue_hdrlen;
} else if (tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GRE) {
size_t gre_hdrlen;
__be16 tflags = 0;
if (tun_flags & IP_VS_TUNNEL_ENCAP_FLAG_CSUM)
tflags |= TUNNEL_CSUM;
gre_hdrlen = gre_calc_hlen(tflags);
max_headroom += gre_hdrlen;
}
skb = ip_vs_prepare_tunneled_skb(skb, cp->af, max_headroom,
......@@ -1306,6 +1360,11 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
skb->ip_summed == CHECKSUM_PARTIAL) {
gso_type |= SKB_GSO_TUNNEL_REMCSUM;
}
} else if (tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GRE) {
if (tun_flags & IP_VS_TUNNEL_ENCAP_FLAG_CSUM)
gso_type |= SKB_GSO_GRE_CSUM;
else
gso_type |= SKB_GSO_GRE;
}
if (iptunnel_handle_offloads(skb, gso_type))
......@@ -1326,7 +1385,8 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
check = true;
udp6_set_csum(!check, skb, &saddr, &cp->daddr.in6, skb->len);
}
} else if (tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GRE)
ipvs_gre_encap(net, skb, cp, &next_protocol);
skb_push(skb, sizeof(struct ipv6hdr));
skb_reset_network_header(skb);
......
......@@ -374,7 +374,7 @@ static int seq_show(struct seq_file *s, void *v)
continue;
logger = nft_log_dereference(loggers[*pos][i]);
seq_printf(s, "%s", logger->name);
seq_puts(s, logger->name);
if (i == 0 && loggers[*pos][i + 1] != NULL)
seq_puts(s, ",");
......
......@@ -156,7 +156,6 @@ static void nf_ip6_saveroute(const struct sk_buff *skb,
}
static int __nf_queue(struct sk_buff *skb, const struct nf_hook_state *state,
const struct nf_hook_entries *entries,
unsigned int index, unsigned int queuenum)
{
int status = -ENOENT;
......@@ -225,12 +224,11 @@ static int __nf_queue(struct sk_buff *skb, const struct nf_hook_state *state,
/* Packets leaving via this function must come back through nf_reinject(). */
int nf_queue(struct sk_buff *skb, struct nf_hook_state *state,
const struct nf_hook_entries *entries, unsigned int index,
unsigned int verdict)
unsigned int index, unsigned int verdict)
{
int ret;
ret = __nf_queue(skb, state, entries, index, verdict >> NF_VERDICT_QBITS);
ret = __nf_queue(skb, state, index, verdict >> NF_VERDICT_QBITS);
if (ret < 0) {
if (ret == -ESRCH &&
(verdict & NF_VERDICT_FLAG_QUEUE_BYPASS))
......@@ -336,7 +334,7 @@ void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict)
local_bh_enable();
break;
case NF_QUEUE:
err = nf_queue(skb, &entry->state, hooks, i, verdict);
err = nf_queue(skb, &entry->state, i, verdict);
if (err == 1)
goto next_hook;
break;
......
......@@ -11,7 +11,7 @@
#include <linux/proc_fs.h>
#include <linux/netfilter_ipv6.h>
#include <linux/netfilter/nf_SYNPROXY.h>
#include <linux/netfilter/nf_synproxy.h>
#include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nf_conntrack_ecache.h>
......
......@@ -2009,16 +2009,32 @@ EXPORT_SYMBOL_GPL(nft_unregister_expr);
static const struct nft_expr_type *__nft_expr_type_get(u8 family,
struct nlattr *nla)
{
const struct nft_expr_type *type;
const struct nft_expr_type *type, *candidate = NULL;
list_for_each_entry(type, &nf_tables_expressions, list) {
if (!nla_strcmp(nla, type->name) &&
(!type->family || type->family == family))
return type;
if (!nla_strcmp(nla, type->name)) {
if (!type->family && !candidate)
candidate = type;
else if (type->family == family)
candidate = type;
}
return NULL;
}
return candidate;
}
#ifdef CONFIG_MODULES
static int nft_expr_type_request_module(struct net *net, u8 family,
struct nlattr *nla)
{
nft_request_module(net, "nft-expr-%u-%.*s", family,
nla_len(nla), (char *)nla_data(nla));
if (__nft_expr_type_get(family, nla))
return -EAGAIN;
return 0;
}
#endif
static const struct nft_expr_type *nft_expr_type_get(struct net *net,
u8 family,
struct nlattr *nla)
......@@ -2035,9 +2051,7 @@ static const struct nft_expr_type *nft_expr_type_get(struct net *net,
lockdep_nfnl_nft_mutex_not_held();
#ifdef CONFIG_MODULES
if (type == NULL) {
nft_request_module(net, "nft-expr-%u-%.*s", family,
nla_len(nla), (char *)nla_data(nla));
if (__nft_expr_type_get(family, nla))
if (nft_expr_type_request_module(net, family, nla) == -EAGAIN)
return ERR_PTR(-EAGAIN);
nft_request_module(net, "nft-expr-%.*s",
......@@ -2130,6 +2144,12 @@ static int nf_tables_expr_parse(const struct nft_ctx *ctx,
(const struct nlattr * const *)info->tb);
if (IS_ERR(ops)) {
err = PTR_ERR(ops);
#ifdef CONFIG_MODULES
if (err == -EAGAIN)
nft_expr_type_request_module(ctx->net,
ctx->family,
tb[NFTA_EXPR_NAME]);
#endif
goto err1;
}
} else
......
......@@ -19,6 +19,7 @@
#include <net/netfilter/nf_tables_core.h>
#include <net/netfilter/nf_tables.h>
#include <net/netfilter/nf_log.h>
#include <net/netfilter/nft_meta.h>
static noinline void __nft_trace_packet(struct nft_traceinfo *info,
const struct nft_chain *chain,
......
......@@ -21,23 +21,12 @@
#include <net/tcp_states.h> /* for TCP_TIME_WAIT */
#include <net/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables_core.h>
#include <net/netfilter/nft_meta.h>
#include <uapi/linux/netfilter_bridge.h> /* NF_BR_PRE_ROUTING */
struct nft_meta {
enum nft_meta_keys key:8;
union {
enum nft_registers dreg:8;
enum nft_registers sreg:8;
};
};
static DEFINE_PER_CPU(struct rnd_state, nft_prandom_state);
#ifdef CONFIG_NF_TABLES_BRIDGE
#include "../bridge/br_private.h"
#endif
void nft_meta_get_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
......@@ -47,9 +36,6 @@ void nft_meta_get_eval(const struct nft_expr *expr,
const struct net_device *in = nft_in(pkt), *out = nft_out(pkt);
struct sock *sk;
u32 *dest = &regs->data[priv->dreg];
#ifdef CONFIG_NF_TABLES_BRIDGE
const struct net_bridge_port *p;
#endif
switch (priv->key) {
case NFT_META_LEN:
......@@ -228,18 +214,6 @@ void nft_meta_get_eval(const struct nft_expr *expr,
case NFT_META_SECPATH:
nft_reg_store8(dest, secpath_exists(skb));
break;
#endif
#ifdef CONFIG_NF_TABLES_BRIDGE
case NFT_META_BRI_IIFNAME:
if (in == NULL || (p = br_port_get_rcu(in)) == NULL)
goto err;
strncpy((char *)dest, p->br->dev->name, IFNAMSIZ);
return;
case NFT_META_BRI_OIFNAME:
if (out == NULL || (p = br_port_get_rcu(out)) == NULL)
goto err;
strncpy((char *)dest, p->br->dev->name, IFNAMSIZ);
return;
#endif
case NFT_META_IIFKIND:
if (in == NULL || in->rtnl_link_ops == NULL)
......@@ -260,8 +234,9 @@ void nft_meta_get_eval(const struct nft_expr *expr,
err:
regs->verdict.code = NFT_BREAK;
}
EXPORT_SYMBOL_GPL(nft_meta_get_eval);
static void nft_meta_set_eval(const struct nft_expr *expr,
void nft_meta_set_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
{
......@@ -300,14 +275,16 @@ static void nft_meta_set_eval(const struct nft_expr *expr,
WARN_ON(1);
}
}
EXPORT_SYMBOL_GPL(nft_meta_set_eval);
static const struct nla_policy nft_meta_policy[NFTA_META_MAX + 1] = {
const struct nla_policy nft_meta_policy[NFTA_META_MAX + 1] = {
[NFTA_META_DREG] = { .type = NLA_U32 },
[NFTA_META_KEY] = { .type = NLA_U32 },
[NFTA_META_SREG] = { .type = NLA_U32 },
};
EXPORT_SYMBOL_GPL(nft_meta_policy);
static int nft_meta_get_init(const struct nft_ctx *ctx,
int nft_meta_get_init(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[])
{
......@@ -359,14 +336,6 @@ static int nft_meta_get_init(const struct nft_ctx *ctx,
case NFT_META_SECPATH:
len = sizeof(u8);
break;
#endif
#ifdef CONFIG_NF_TABLES_BRIDGE
case NFT_META_BRI_IIFNAME:
case NFT_META_BRI_OIFNAME:
if (ctx->family != NFPROTO_BRIDGE)
return -EOPNOTSUPP;
len = IFNAMSIZ;
break;
#endif
default:
return -EOPNOTSUPP;
......@@ -376,6 +345,7 @@ static int nft_meta_get_init(const struct nft_ctx *ctx,
return nft_validate_register_store(ctx, priv->dreg, NULL,
NFT_DATA_VALUE, len);
}
EXPORT_SYMBOL_GPL(nft_meta_get_init);
static int nft_meta_get_validate(const struct nft_ctx *ctx,
const struct nft_expr *expr,
......@@ -409,7 +379,7 @@ static int nft_meta_get_validate(const struct nft_ctx *ctx,
#endif
}
static int nft_meta_set_validate(const struct nft_ctx *ctx,
int nft_meta_set_validate(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nft_data **data)
{
......@@ -437,8 +407,9 @@ static int nft_meta_set_validate(const struct nft_ctx *ctx,
return nft_chain_validate_hooks(ctx->chain, hooks);
}
EXPORT_SYMBOL_GPL(nft_meta_set_validate);
static int nft_meta_set_init(const struct nft_ctx *ctx,
int nft_meta_set_init(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[])
{
......@@ -475,8 +446,9 @@ static int nft_meta_set_init(const struct nft_ctx *ctx,
return 0;
}
EXPORT_SYMBOL_GPL(nft_meta_set_init);
static int nft_meta_get_dump(struct sk_buff *skb,
int nft_meta_get_dump(struct sk_buff *skb,
const struct nft_expr *expr)
{
const struct nft_meta *priv = nft_expr_priv(expr);
......@@ -490,8 +462,9 @@ static int nft_meta_get_dump(struct sk_buff *skb,
nla_put_failure:
return -1;
}
EXPORT_SYMBOL_GPL(nft_meta_get_dump);
static int nft_meta_set_dump(struct sk_buff *skb, const struct nft_expr *expr)
int nft_meta_set_dump(struct sk_buff *skb, const struct nft_expr *expr)
{
const struct nft_meta *priv = nft_expr_priv(expr);
......@@ -505,8 +478,9 @@ static int nft_meta_set_dump(struct sk_buff *skb, const struct nft_expr *expr)
nla_put_failure:
return -1;
}
EXPORT_SYMBOL_GPL(nft_meta_set_dump);
static void nft_meta_set_destroy(const struct nft_ctx *ctx,
void nft_meta_set_destroy(const struct nft_ctx *ctx,
const struct nft_expr *expr)
{
const struct nft_meta *priv = nft_expr_priv(expr);
......@@ -514,6 +488,7 @@ static void nft_meta_set_destroy(const struct nft_ctx *ctx,
if (priv->key == NFT_META_NFTRACE)
static_branch_dec(&nft_trace_enabled);
}
EXPORT_SYMBOL_GPL(nft_meta_set_destroy);
static const struct nft_expr_ops nft_meta_get_ops = {
.type = &nft_meta_type,
......@@ -544,6 +519,10 @@ nft_meta_select_ops(const struct nft_ctx *ctx,
if (tb[NFTA_META_DREG] && tb[NFTA_META_SREG])
return ERR_PTR(-EINVAL);
#ifdef CONFIG_NF_TABLES_BRIDGE
if (ctx->family == NFPROTO_BRIDGE)
return ERR_PTR(-EAGAIN);
#endif
if (tb[NFTA_META_DREG])
return &nft_meta_get_ops;
......
// SPDX-License-Identifier: GPL-2.0
#include <linux/types.h>
#include <net/ip.h>
#include <net/tcp.h>
#include <net/netlink.h>
#include <net/netfilter/nf_tables.h>
#include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nf_conntrack_synproxy.h>
#include <net/netfilter/nf_synproxy.h>
#include <linux/netfilter/nf_tables.h>
#include <linux/netfilter/nf_synproxy.h>
struct nft_synproxy {
struct nf_synproxy_info info;
};
static const struct nla_policy nft_synproxy_policy[NFTA_SYNPROXY_MAX + 1] = {
[NFTA_SYNPROXY_MSS] = { .type = NLA_U16 },
[NFTA_SYNPROXY_WSCALE] = { .type = NLA_U8 },
[NFTA_SYNPROXY_FLAGS] = { .type = NLA_U32 },
};
static void nft_synproxy_tcp_options(struct synproxy_options *opts,
const struct tcphdr *tcp,
struct synproxy_net *snet,
struct nf_synproxy_info *info,
struct nft_synproxy *priv)
{
this_cpu_inc(snet->stats->syn_received);
if (tcp->ece && tcp->cwr)
opts->options |= NF_SYNPROXY_OPT_ECN;
opts->options &= priv->info.options;
if (opts->options & NF_SYNPROXY_OPT_TIMESTAMP)
synproxy_init_timestamp_cookie(info, opts);
else
opts->options &= ~(NF_SYNPROXY_OPT_WSCALE |
NF_SYNPROXY_OPT_SACK_PERM |
NF_SYNPROXY_OPT_ECN);
}
static void nft_synproxy_eval_v4(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt,
const struct tcphdr *tcp,
struct tcphdr *_tcph,
struct synproxy_options *opts)
{
struct nft_synproxy *priv = nft_expr_priv(expr);
struct nf_synproxy_info info = priv->info;
struct net *net = nft_net(pkt);
struct synproxy_net *snet = synproxy_pernet(net);
struct sk_buff *skb = pkt->skb;
if (tcp->syn) {
/* Initial SYN from client */
nft_synproxy_tcp_options(opts, tcp, snet, &info, priv);
synproxy_send_client_synack(net, skb, tcp, opts);
consume_skb(skb);
regs->verdict.code = NF_STOLEN;
} else if (tcp->ack) {
/* ACK from client */
if (synproxy_recv_client_ack(net, skb, tcp, opts,
ntohl(tcp->seq))) {
consume_skb(skb);
regs->verdict.code = NF_STOLEN;
} else {
regs->verdict.code = NF_DROP;
}
}
}
#if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
static void nft_synproxy_eval_v6(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt,
const struct tcphdr *tcp,
struct tcphdr *_tcph,
struct synproxy_options *opts)
{
struct nft_synproxy *priv = nft_expr_priv(expr);
struct nf_synproxy_info info = priv->info;
struct net *net = nft_net(pkt);
struct synproxy_net *snet = synproxy_pernet(net);
struct sk_buff *skb = pkt->skb;
if (tcp->syn) {
/* Initial SYN from client */
nft_synproxy_tcp_options(opts, tcp, snet, &info, priv);
synproxy_send_client_synack_ipv6(net, skb, tcp, opts);
consume_skb(skb);
regs->verdict.code = NF_STOLEN;
} else if (tcp->ack) {
/* ACK from client */
if (synproxy_recv_client_ack_ipv6(net, skb, tcp, opts,
ntohl(tcp->seq))) {
consume_skb(skb);
regs->verdict.code = NF_STOLEN;
} else {
regs->verdict.code = NF_DROP;
}
}
}
#endif /* CONFIG_NF_TABLES_IPV6*/
static void nft_synproxy_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
{
struct synproxy_options opts = {};
struct sk_buff *skb = pkt->skb;
int thoff = pkt->xt.thoff;
const struct tcphdr *tcp;
struct tcphdr _tcph;
if (pkt->tprot != IPPROTO_TCP) {
regs->verdict.code = NFT_BREAK;
return;
}
if (nf_ip_checksum(skb, nft_hook(pkt), thoff, IPPROTO_TCP)) {
regs->verdict.code = NF_DROP;
return;
}
tcp = skb_header_pointer(skb, pkt->xt.thoff,
sizeof(struct tcphdr),
&_tcph);
if (!tcp) {
regs->verdict.code = NF_DROP;
return;
}
if (!synproxy_parse_options(skb, thoff, tcp, &opts)) {
regs->verdict.code = NF_DROP;
return;
}
switch (skb->protocol) {
case htons(ETH_P_IP):
nft_synproxy_eval_v4(expr, regs, pkt, tcp, &_tcph, &opts);
return;
#if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
case htons(ETH_P_IPV6):
nft_synproxy_eval_v6(expr, regs, pkt, tcp, &_tcph, &opts);
return;
#endif
}
regs->verdict.code = NFT_BREAK;
}
static int nft_synproxy_init(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[])
{
struct synproxy_net *snet = synproxy_pernet(ctx->net);
struct nft_synproxy *priv = nft_expr_priv(expr);
u32 flags;
int err;
if (tb[NFTA_SYNPROXY_MSS])
priv->info.mss = ntohs(nla_get_be16(tb[NFTA_SYNPROXY_MSS]));
if (tb[NFTA_SYNPROXY_WSCALE])
priv->info.wscale = nla_get_u8(tb[NFTA_SYNPROXY_WSCALE]);
if (tb[NFTA_SYNPROXY_FLAGS]) {
flags = ntohl(nla_get_be32(tb[NFTA_SYNPROXY_FLAGS]));
if (flags & ~NF_SYNPROXY_OPT_MASK)
return -EOPNOTSUPP;
priv->info.options = flags;
}
err = nf_ct_netns_get(ctx->net, ctx->family);
if (err)
return err;
switch (ctx->family) {
case NFPROTO_IPV4:
err = nf_synproxy_ipv4_init(snet, ctx->net);
if (err)
goto nf_ct_failure;
break;
#if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
case NFPROTO_IPV6:
err = nf_synproxy_ipv6_init(snet, ctx->net);
if (err)
goto nf_ct_failure;
break;
#endif
case NFPROTO_INET:
case NFPROTO_BRIDGE:
err = nf_synproxy_ipv4_init(snet, ctx->net);
if (err)
goto nf_ct_failure;
err = nf_synproxy_ipv6_init(snet, ctx->net);
if (err)
goto nf_ct_failure;
break;
}
return 0;
nf_ct_failure:
nf_ct_netns_put(ctx->net, ctx->family);
return err;
}
static void nft_synproxy_destroy(const struct nft_ctx *ctx,
const struct nft_expr *expr)
{
struct synproxy_net *snet = synproxy_pernet(ctx->net);
switch (ctx->family) {
case NFPROTO_IPV4:
nf_synproxy_ipv4_fini(snet, ctx->net);
break;
#if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
case NFPROTO_IPV6:
nf_synproxy_ipv6_fini(snet, ctx->net);
break;
#endif
case NFPROTO_INET:
case NFPROTO_BRIDGE:
nf_synproxy_ipv4_fini(snet, ctx->net);
nf_synproxy_ipv6_fini(snet, ctx->net);
break;
}
nf_ct_netns_put(ctx->net, ctx->family);
}
static int nft_synproxy_dump(struct sk_buff *skb, const struct nft_expr *expr)
{
const struct nft_synproxy *priv = nft_expr_priv(expr);
if (nla_put_be16(skb, NFTA_SYNPROXY_MSS, htons(priv->info.mss)) ||
nla_put_u8(skb, NFTA_SYNPROXY_WSCALE, priv->info.wscale) ||
nla_put_be32(skb, NFTA_SYNPROXY_FLAGS, htonl(priv->info.options)))
goto nla_put_failure;
return 0;
nla_put_failure:
return -1;
}
static int nft_synproxy_validate(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nft_data **data)
{
return nft_chain_validate_hooks(ctx->chain, (1 << NF_INET_LOCAL_IN) |
(1 << NF_INET_FORWARD));
}
static struct nft_expr_type nft_synproxy_type;
static const struct nft_expr_ops nft_synproxy_ops = {
.eval = nft_synproxy_eval,
.size = NFT_EXPR_SIZE(sizeof(struct nft_synproxy)),
.init = nft_synproxy_init,
.destroy = nft_synproxy_destroy,
.dump = nft_synproxy_dump,
.type = &nft_synproxy_type,
.validate = nft_synproxy_validate,
};
static struct nft_expr_type nft_synproxy_type __read_mostly = {
.ops = &nft_synproxy_ops,
.name = "synproxy",
.owner = THIS_MODULE,
.policy = nft_synproxy_policy,
.maxattr = NFTA_SYNPROXY_MAX,
};
static int __init nft_synproxy_module_init(void)
{
return nft_register_expr(&nft_synproxy_type);
}
static void __exit nft_synproxy_module_exit(void)
{
return nft_unregister_expr(&nft_synproxy_type);
}
module_init(nft_synproxy_module_init);
module_exit(nft_synproxy_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Fernando Fernandez <ffmancera@riseup.net>");
MODULE_ALIAS_NFT_EXPR("synproxy");
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