Commit 9a255a06 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 updates for net-next

The following batch contains Netfilter updates for net-next:

1) Split flowtable workqueues per events, from Oz Shlomo.

2) fall-through warnings for clang, from Gustavo A. R. Silva

3) Remove unused declaration in conntrack, from YueHaibing.

4) Consolidate skb_try_make_writable() in flowtable datapath,
   simplify some of the existing codebase.

5) Call dst_check() to fall back to static classic forwarding path.

6) Update table flags from commit phase.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents add2d736 0ce7cf41
......@@ -4,7 +4,4 @@
extern const struct nf_conntrack_l4proto nf_conntrack_l4proto_icmpv6;
#include <linux/sysctl.h>
extern struct ctl_table nf_ct_ipv6_sysctl_table[];
#endif /* _NF_CONNTRACK_IPV6_H*/
......@@ -86,8 +86,8 @@ static inline bool nf_flowtable_hw_offload(struct nf_flowtable *flowtable)
enum flow_offload_tuple_dir {
FLOW_OFFLOAD_DIR_ORIGINAL = IP_CT_DIR_ORIGINAL,
FLOW_OFFLOAD_DIR_REPLY = IP_CT_DIR_REPLY,
FLOW_OFFLOAD_DIR_MAX = IP_CT_DIR_MAX
};
#define FLOW_OFFLOAD_DIR_MAX IP_CT_DIR_MAX
struct flow_offload_tuple {
union {
......@@ -229,10 +229,10 @@ void nf_flow_table_free(struct nf_flowtable *flow_table);
void flow_offload_teardown(struct flow_offload *flow);
int nf_flow_snat_port(const struct flow_offload *flow,
void nf_flow_snat_port(const struct flow_offload *flow,
struct sk_buff *skb, unsigned int thoff,
u8 protocol, enum flow_offload_tuple_dir dir);
int nf_flow_dnat_port(const struct flow_offload *flow,
void nf_flow_dnat_port(const struct flow_offload *flow,
struct sk_buff *skb, unsigned int thoff,
u8 protocol, enum flow_offload_tuple_dir dir);
......
......@@ -1498,13 +1498,16 @@ struct nft_trans_chain {
struct nft_trans_table {
bool update;
bool enable;
u8 state;
u32 flags;
};
#define nft_trans_table_update(trans) \
(((struct nft_trans_table *)trans->data)->update)
#define nft_trans_table_enable(trans) \
(((struct nft_trans_table *)trans->data)->enable)
#define nft_trans_table_state(trans) \
(((struct nft_trans_table *)trans->data)->state)
#define nft_trans_table_flags(trans) \
(((struct nft_trans_table *)trans->data)->flags)
struct nft_trans_elem {
struct nft_set *set;
......
......@@ -397,6 +397,7 @@ dccp_new(struct nf_conn *ct, const struct sk_buff *skb,
msg = "not picking up existing connection ";
goto out_invalid;
}
break;
case CT_DCCP_REQUEST:
break;
case CT_DCCP_INVALID:
......
......@@ -389,29 +389,20 @@ static void nf_flow_offload_work_gc(struct work_struct *work)
queue_delayed_work(system_power_efficient_wq, &flow_table->gc_work, HZ);
}
static int nf_flow_nat_port_tcp(struct sk_buff *skb, unsigned int thoff,
static void nf_flow_nat_port_tcp(struct sk_buff *skb, unsigned int thoff,
__be16 port, __be16 new_port)
{
struct tcphdr *tcph;
if (skb_try_make_writable(skb, thoff + sizeof(*tcph)))
return -1;
tcph = (void *)(skb_network_header(skb) + thoff);
inet_proto_csum_replace2(&tcph->check, skb, port, new_port, false);
return 0;
}
static int nf_flow_nat_port_udp(struct sk_buff *skb, unsigned int thoff,
static void nf_flow_nat_port_udp(struct sk_buff *skb, unsigned int thoff,
__be16 port, __be16 new_port)
{
struct udphdr *udph;
if (skb_try_make_writable(skb, thoff + sizeof(*udph)))
return -1;
udph = (void *)(skb_network_header(skb) + thoff);
if (udph->check || skb->ip_summed == CHECKSUM_PARTIAL) {
inet_proto_csum_replace2(&udph->check, skb, port,
......@@ -419,37 +410,28 @@ static int nf_flow_nat_port_udp(struct sk_buff *skb, unsigned int thoff,
if (!udph->check)
udph->check = CSUM_MANGLED_0;
}
return 0;
}
static int nf_flow_nat_port(struct sk_buff *skb, unsigned int thoff,
static void nf_flow_nat_port(struct sk_buff *skb, unsigned int thoff,
u8 protocol, __be16 port, __be16 new_port)
{
switch (protocol) {
case IPPROTO_TCP:
if (nf_flow_nat_port_tcp(skb, thoff, port, new_port) < 0)
return NF_DROP;
nf_flow_nat_port_tcp(skb, thoff, port, new_port);
break;
case IPPROTO_UDP:
if (nf_flow_nat_port_udp(skb, thoff, port, new_port) < 0)
return NF_DROP;
nf_flow_nat_port_udp(skb, thoff, port, new_port);
break;
}
return 0;
}
int nf_flow_snat_port(const struct flow_offload *flow,
void nf_flow_snat_port(const struct flow_offload *flow,
struct sk_buff *skb, unsigned int thoff,
u8 protocol, enum flow_offload_tuple_dir dir)
{
struct flow_ports *hdr;
__be16 port, new_port;
if (skb_try_make_writable(skb, thoff + sizeof(*hdr)))
return -1;
hdr = (void *)(skb_network_header(skb) + thoff);
switch (dir) {
......@@ -463,24 +445,19 @@ int nf_flow_snat_port(const struct flow_offload *flow,
new_port = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.src_port;
hdr->dest = new_port;
break;
default:
return -1;
}
return nf_flow_nat_port(skb, thoff, protocol, port, new_port);
nf_flow_nat_port(skb, thoff, protocol, port, new_port);
}
EXPORT_SYMBOL_GPL(nf_flow_snat_port);
int nf_flow_dnat_port(const struct flow_offload *flow,
struct sk_buff *skb, unsigned int thoff,
u8 protocol, enum flow_offload_tuple_dir dir)
void nf_flow_dnat_port(const struct flow_offload *flow, struct sk_buff *skb,
unsigned int thoff, u8 protocol,
enum flow_offload_tuple_dir dir)
{
struct flow_ports *hdr;
__be16 port, new_port;
if (skb_try_make_writable(skb, thoff + sizeof(*hdr)))
return -1;
hdr = (void *)(skb_network_header(skb) + thoff);
switch (dir) {
......@@ -494,11 +471,9 @@ int nf_flow_dnat_port(const struct flow_offload *flow,
new_port = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.dst_port;
hdr->source = new_port;
break;
default:
return -1;
}
return nf_flow_nat_port(skb, thoff, protocol, port, new_port);
nf_flow_nat_port(skb, thoff, protocol, port, new_port);
}
EXPORT_SYMBOL_GPL(nf_flow_dnat_port);
......
......@@ -34,28 +34,20 @@ static int nf_flow_state_check(struct flow_offload *flow, int proto,
return 0;
}
static int nf_flow_nat_ip_tcp(struct sk_buff *skb, unsigned int thoff,
static void nf_flow_nat_ip_tcp(struct sk_buff *skb, unsigned int thoff,
__be32 addr, __be32 new_addr)
{
struct tcphdr *tcph;
if (skb_try_make_writable(skb, thoff + sizeof(*tcph)))
return -1;
tcph = (void *)(skb_network_header(skb) + thoff);
inet_proto_csum_replace4(&tcph->check, skb, addr, new_addr, true);
return 0;
}
static int nf_flow_nat_ip_udp(struct sk_buff *skb, unsigned int thoff,
static void nf_flow_nat_ip_udp(struct sk_buff *skb, unsigned int thoff,
__be32 addr, __be32 new_addr)
{
struct udphdr *udph;
if (skb_try_make_writable(skb, thoff + sizeof(*udph)))
return -1;
udph = (void *)(skb_network_header(skb) + thoff);
if (udph->check || skb->ip_summed == CHECKSUM_PARTIAL) {
inet_proto_csum_replace4(&udph->check, skb, addr,
......@@ -63,31 +55,25 @@ static int nf_flow_nat_ip_udp(struct sk_buff *skb, unsigned int thoff,
if (!udph->check)
udph->check = CSUM_MANGLED_0;
}
return 0;
}
static int nf_flow_nat_ip_l4proto(struct sk_buff *skb, struct iphdr *iph,
static void nf_flow_nat_ip_l4proto(struct sk_buff *skb, struct iphdr *iph,
unsigned int thoff, __be32 addr,
__be32 new_addr)
{
switch (iph->protocol) {
case IPPROTO_TCP:
if (nf_flow_nat_ip_tcp(skb, thoff, addr, new_addr) < 0)
return NF_DROP;
nf_flow_nat_ip_tcp(skb, thoff, addr, new_addr);
break;
case IPPROTO_UDP:
if (nf_flow_nat_ip_udp(skb, thoff, addr, new_addr) < 0)
return NF_DROP;
nf_flow_nat_ip_udp(skb, thoff, addr, new_addr);
break;
}
return 0;
}
static int nf_flow_snat_ip(const struct flow_offload *flow, struct sk_buff *skb,
struct iphdr *iph, unsigned int thoff,
enum flow_offload_tuple_dir dir)
static void nf_flow_snat_ip(const struct flow_offload *flow,
struct sk_buff *skb, struct iphdr *iph,
unsigned int thoff, enum flow_offload_tuple_dir dir)
{
__be32 addr, new_addr;
......@@ -102,17 +88,15 @@ static int nf_flow_snat_ip(const struct flow_offload *flow, struct sk_buff *skb,
new_addr = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.src_v4.s_addr;
iph->daddr = new_addr;
break;
default:
return -1;
}
csum_replace4(&iph->check, addr, new_addr);
return nf_flow_nat_ip_l4proto(skb, iph, thoff, addr, new_addr);
nf_flow_nat_ip_l4proto(skb, iph, thoff, addr, new_addr);
}
static int nf_flow_dnat_ip(const struct flow_offload *flow, struct sk_buff *skb,
struct iphdr *iph, unsigned int thoff,
enum flow_offload_tuple_dir dir)
static void nf_flow_dnat_ip(const struct flow_offload *flow,
struct sk_buff *skb, struct iphdr *iph,
unsigned int thoff, enum flow_offload_tuple_dir dir)
{
__be32 addr, new_addr;
......@@ -127,31 +111,24 @@ static int nf_flow_dnat_ip(const struct flow_offload *flow, struct sk_buff *skb,
new_addr = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.dst_v4.s_addr;
iph->saddr = new_addr;
break;
default:
return -1;
}
csum_replace4(&iph->check, addr, new_addr);
return nf_flow_nat_ip_l4proto(skb, iph, thoff, addr, new_addr);
nf_flow_nat_ip_l4proto(skb, iph, thoff, addr, new_addr);
}
static int nf_flow_nat_ip(const struct flow_offload *flow, struct sk_buff *skb,
unsigned int thoff, enum flow_offload_tuple_dir dir)
static void nf_flow_nat_ip(const struct flow_offload *flow, struct sk_buff *skb,
unsigned int thoff, enum flow_offload_tuple_dir dir,
struct iphdr *iph)
{
struct iphdr *iph = ip_hdr(skb);
if (test_bit(NF_FLOW_SNAT, &flow->flags) &&
(nf_flow_snat_port(flow, skb, thoff, iph->protocol, dir) < 0 ||
nf_flow_snat_ip(flow, skb, ip_hdr(skb), thoff, dir) < 0))
return -1;
iph = ip_hdr(skb);
if (test_bit(NF_FLOW_DNAT, &flow->flags) &&
(nf_flow_dnat_port(flow, skb, thoff, iph->protocol, dir) < 0 ||
nf_flow_dnat_ip(flow, skb, ip_hdr(skb), thoff, dir) < 0))
return -1;
return 0;
if (test_bit(NF_FLOW_SNAT, &flow->flags)) {
nf_flow_snat_port(flow, skb, thoff, iph->protocol, dir);
nf_flow_snat_ip(flow, skb, iph, thoff, dir);
}
if (test_bit(NF_FLOW_DNAT, &flow->flags)) {
nf_flow_dnat_port(flow, skb, thoff, iph->protocol, dir);
nf_flow_dnat_ip(flow, skb, iph, thoff, dir);
}
}
static bool ip_has_options(unsigned int thoff)
......@@ -160,10 +137,10 @@ static bool ip_has_options(unsigned int thoff)
}
static int nf_flow_tuple_ip(struct sk_buff *skb, const struct net_device *dev,
struct flow_offload_tuple *tuple)
struct flow_offload_tuple *tuple, u32 *hdrsize)
{
unsigned int thoff, hdrsize;
struct flow_ports *ports;
unsigned int thoff;
struct iphdr *iph;
if (!pskb_may_pull(skb, sizeof(*iph)))
......@@ -178,10 +155,10 @@ static int nf_flow_tuple_ip(struct sk_buff *skb, const struct net_device *dev,
switch (iph->protocol) {
case IPPROTO_TCP:
hdrsize = sizeof(struct tcphdr);
*hdrsize = sizeof(struct tcphdr);
break;
case IPPROTO_UDP:
hdrsize = sizeof(struct udphdr);
*hdrsize = sizeof(struct udphdr);
break;
default:
return -1;
......@@ -191,7 +168,7 @@ static int nf_flow_tuple_ip(struct sk_buff *skb, const struct net_device *dev,
return -1;
thoff = iph->ihl * 4;
if (!pskb_may_pull(skb, thoff + hdrsize))
if (!pskb_may_pull(skb, thoff + *hdrsize))
return -1;
iph = ip_hdr(skb);
......@@ -220,14 +197,6 @@ static bool nf_flow_exceeds_mtu(const struct sk_buff *skb, unsigned int mtu)
return true;
}
static int nf_flow_offload_dst_check(struct dst_entry *dst)
{
if (unlikely(dst_xfrm(dst)))
return dst_check(dst, 0) ? 0 : -1;
return 0;
}
static unsigned int nf_flow_xmit_xfrm(struct sk_buff *skb,
const struct nf_hook_state *state,
struct dst_entry *dst)
......@@ -252,11 +221,12 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
unsigned int thoff;
struct iphdr *iph;
__be32 nexthop;
u32 hdrsize;
if (skb->protocol != htons(ETH_P_IP))
return NF_ACCEPT;
if (nf_flow_tuple_ip(skb, state->in, &tuple) < 0)
if (nf_flow_tuple_ip(skb, state->in, &tuple, &hdrsize) < 0)
return NF_ACCEPT;
tuplehash = flow_offload_lookup(flow_table, &tuple);
......@@ -271,24 +241,24 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
if (unlikely(nf_flow_exceeds_mtu(skb, flow->tuplehash[dir].tuple.mtu)))
return NF_ACCEPT;
if (skb_try_make_writable(skb, sizeof(*iph)))
return NF_DROP;
thoff = ip_hdr(skb)->ihl * 4;
if (nf_flow_state_check(flow, ip_hdr(skb)->protocol, skb, thoff))
iph = ip_hdr(skb);
thoff = iph->ihl * 4;
if (nf_flow_state_check(flow, iph->protocol, skb, thoff))
return NF_ACCEPT;
flow_offload_refresh(flow_table, flow);
if (nf_flow_offload_dst_check(&rt->dst)) {
if (!dst_check(&rt->dst, 0)) {
flow_offload_teardown(flow);
return NF_ACCEPT;
}
if (nf_flow_nat_ip(flow, skb, thoff, dir) < 0)
if (skb_try_make_writable(skb, thoff + hdrsize))
return NF_DROP;
flow_offload_refresh(flow_table, flow);
iph = ip_hdr(skb);
nf_flow_nat_ip(flow, skb, thoff, dir, iph);
ip_decrease_ttl(iph);
skb->tstamp = 0;
......@@ -311,31 +281,24 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
}
EXPORT_SYMBOL_GPL(nf_flow_offload_ip_hook);
static int nf_flow_nat_ipv6_tcp(struct sk_buff *skb, unsigned int thoff,
static void nf_flow_nat_ipv6_tcp(struct sk_buff *skb, unsigned int thoff,
struct in6_addr *addr,
struct in6_addr *new_addr)
struct in6_addr *new_addr,
struct ipv6hdr *ip6h)
{
struct tcphdr *tcph;
if (skb_try_make_writable(skb, thoff + sizeof(*tcph)))
return -1;
tcph = (void *)(skb_network_header(skb) + thoff);
inet_proto_csum_replace16(&tcph->check, skb, addr->s6_addr32,
new_addr->s6_addr32, true);
return 0;
}
static int nf_flow_nat_ipv6_udp(struct sk_buff *skb, unsigned int thoff,
static void nf_flow_nat_ipv6_udp(struct sk_buff *skb, unsigned int thoff,
struct in6_addr *addr,
struct in6_addr *new_addr)
{
struct udphdr *udph;
if (skb_try_make_writable(skb, thoff + sizeof(*udph)))
return -1;
udph = (void *)(skb_network_header(skb) + thoff);
if (udph->check || skb->ip_summed == CHECKSUM_PARTIAL) {
inet_proto_csum_replace16(&udph->check, skb, addr->s6_addr32,
......@@ -343,29 +306,23 @@ static int nf_flow_nat_ipv6_udp(struct sk_buff *skb, unsigned int thoff,
if (!udph->check)
udph->check = CSUM_MANGLED_0;
}
return 0;
}
static int nf_flow_nat_ipv6_l4proto(struct sk_buff *skb, struct ipv6hdr *ip6h,
static void nf_flow_nat_ipv6_l4proto(struct sk_buff *skb, struct ipv6hdr *ip6h,
unsigned int thoff, struct in6_addr *addr,
struct in6_addr *new_addr)
{
switch (ip6h->nexthdr) {
case IPPROTO_TCP:
if (nf_flow_nat_ipv6_tcp(skb, thoff, addr, new_addr) < 0)
return NF_DROP;
nf_flow_nat_ipv6_tcp(skb, thoff, addr, new_addr, ip6h);
break;
case IPPROTO_UDP:
if (nf_flow_nat_ipv6_udp(skb, thoff, addr, new_addr) < 0)
return NF_DROP;
nf_flow_nat_ipv6_udp(skb, thoff, addr, new_addr);
break;
}
return 0;
}
static int nf_flow_snat_ipv6(const struct flow_offload *flow,
static void nf_flow_snat_ipv6(const struct flow_offload *flow,
struct sk_buff *skb, struct ipv6hdr *ip6h,
unsigned int thoff,
enum flow_offload_tuple_dir dir)
......@@ -383,14 +340,12 @@ static int nf_flow_snat_ipv6(const struct flow_offload *flow,
new_addr = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.src_v6;
ip6h->daddr = new_addr;
break;
default:
return -1;
}
return nf_flow_nat_ipv6_l4proto(skb, ip6h, thoff, &addr, &new_addr);
nf_flow_nat_ipv6_l4proto(skb, ip6h, thoff, &addr, &new_addr);
}
static int nf_flow_dnat_ipv6(const struct flow_offload *flow,
static void nf_flow_dnat_ipv6(const struct flow_offload *flow,
struct sk_buff *skb, struct ipv6hdr *ip6h,
unsigned int thoff,
enum flow_offload_tuple_dir dir)
......@@ -408,40 +363,34 @@ static int nf_flow_dnat_ipv6(const struct flow_offload *flow,
new_addr = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.dst_v6;
ip6h->saddr = new_addr;
break;
default:
return -1;
}
return nf_flow_nat_ipv6_l4proto(skb, ip6h, thoff, &addr, &new_addr);
nf_flow_nat_ipv6_l4proto(skb, ip6h, thoff, &addr, &new_addr);
}
static int nf_flow_nat_ipv6(const struct flow_offload *flow,
static void nf_flow_nat_ipv6(const struct flow_offload *flow,
struct sk_buff *skb,
enum flow_offload_tuple_dir dir)
enum flow_offload_tuple_dir dir,
struct ipv6hdr *ip6h)
{
struct ipv6hdr *ip6h = ipv6_hdr(skb);
unsigned int thoff = sizeof(*ip6h);
if (test_bit(NF_FLOW_SNAT, &flow->flags) &&
(nf_flow_snat_port(flow, skb, thoff, ip6h->nexthdr, dir) < 0 ||
nf_flow_snat_ipv6(flow, skb, ipv6_hdr(skb), thoff, dir) < 0))
return -1;
ip6h = ipv6_hdr(skb);
if (test_bit(NF_FLOW_DNAT, &flow->flags) &&
(nf_flow_dnat_port(flow, skb, thoff, ip6h->nexthdr, dir) < 0 ||
nf_flow_dnat_ipv6(flow, skb, ipv6_hdr(skb), thoff, dir) < 0))
return -1;
return 0;
if (test_bit(NF_FLOW_SNAT, &flow->flags)) {
nf_flow_snat_port(flow, skb, thoff, ip6h->nexthdr, dir);
nf_flow_snat_ipv6(flow, skb, ip6h, thoff, dir);
}
if (test_bit(NF_FLOW_DNAT, &flow->flags)) {
nf_flow_dnat_port(flow, skb, thoff, ip6h->nexthdr, dir);
nf_flow_dnat_ipv6(flow, skb, ip6h, thoff, dir);
}
}
static int nf_flow_tuple_ipv6(struct sk_buff *skb, const struct net_device *dev,
struct flow_offload_tuple *tuple)
struct flow_offload_tuple *tuple, u32 *hdrsize)
{
unsigned int thoff, hdrsize;
struct flow_ports *ports;
struct ipv6hdr *ip6h;
unsigned int thoff;
if (!pskb_may_pull(skb, sizeof(*ip6h)))
return -1;
......@@ -450,10 +399,10 @@ static int nf_flow_tuple_ipv6(struct sk_buff *skb, const struct net_device *dev,
switch (ip6h->nexthdr) {
case IPPROTO_TCP:
hdrsize = sizeof(struct tcphdr);
*hdrsize = sizeof(struct tcphdr);
break;
case IPPROTO_UDP:
hdrsize = sizeof(struct udphdr);
*hdrsize = sizeof(struct udphdr);
break;
default:
return -1;
......@@ -463,7 +412,7 @@ static int nf_flow_tuple_ipv6(struct sk_buff *skb, const struct net_device *dev,
return -1;
thoff = sizeof(*ip6h);
if (!pskb_may_pull(skb, thoff + hdrsize))
if (!pskb_may_pull(skb, thoff + *hdrsize))
return -1;
ip6h = ipv6_hdr(skb);
......@@ -493,11 +442,12 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
struct net_device *outdev;
struct ipv6hdr *ip6h;
struct rt6_info *rt;
u32 hdrsize;
if (skb->protocol != htons(ETH_P_IPV6))
return NF_ACCEPT;
if (nf_flow_tuple_ipv6(skb, state->in, &tuple) < 0)
if (nf_flow_tuple_ipv6(skb, state->in, &tuple, &hdrsize) < 0)
return NF_ACCEPT;
tuplehash = flow_offload_lookup(flow_table, &tuple);
......@@ -516,20 +466,19 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
sizeof(*ip6h)))
return NF_ACCEPT;
flow_offload_refresh(flow_table, flow);
if (nf_flow_offload_dst_check(&rt->dst)) {
if (!dst_check(&rt->dst, 0)) {
flow_offload_teardown(flow);
return NF_ACCEPT;
}
if (skb_try_make_writable(skb, sizeof(*ip6h)))
if (skb_try_make_writable(skb, sizeof(*ip6h) + hdrsize))
return NF_DROP;
if (nf_flow_nat_ipv6(flow, skb, dir) < 0)
return NF_DROP;
flow_offload_refresh(flow_table, flow);
ip6h = ipv6_hdr(skb);
nf_flow_nat_ipv6(flow, skb, dir, ip6h);
ip6h->hop_limit--;
skb->tstamp = 0;
......
......@@ -13,7 +13,9 @@
#include <net/netfilter/nf_conntrack_core.h>
#include <net/netfilter/nf_conntrack_tuple.h>
static struct workqueue_struct *nf_flow_offload_wq;
static struct workqueue_struct *nf_flow_offload_add_wq;
static struct workqueue_struct *nf_flow_offload_del_wq;
static struct workqueue_struct *nf_flow_offload_stats_wq;
struct flow_offload_work {
struct list_head list;
......@@ -826,7 +828,12 @@ static void flow_offload_work_handler(struct work_struct *work)
static void flow_offload_queue_work(struct flow_offload_work *offload)
{
queue_work(nf_flow_offload_wq, &offload->work);
if (offload->cmd == FLOW_CLS_REPLACE)
queue_work(nf_flow_offload_add_wq, &offload->work);
else if (offload->cmd == FLOW_CLS_DESTROY)
queue_work(nf_flow_offload_del_wq, &offload->work);
else
queue_work(nf_flow_offload_stats_wq, &offload->work);
}
static struct flow_offload_work *
......@@ -898,8 +905,11 @@ void nf_flow_offload_stats(struct nf_flowtable *flowtable,
void nf_flow_table_offload_flush(struct nf_flowtable *flowtable)
{
if (nf_flowtable_hw_offload(flowtable))
flush_workqueue(nf_flow_offload_wq);
if (nf_flowtable_hw_offload(flowtable)) {
flush_workqueue(nf_flow_offload_add_wq);
flush_workqueue(nf_flow_offload_del_wq);
flush_workqueue(nf_flow_offload_stats_wq);
}
}
static int nf_flow_table_block_setup(struct nf_flowtable *flowtable,
......@@ -1011,15 +1021,33 @@ EXPORT_SYMBOL_GPL(nf_flow_table_offload_setup);
int nf_flow_table_offload_init(void)
{
nf_flow_offload_wq = alloc_workqueue("nf_flow_table_offload",
WQ_UNBOUND, 0);
if (!nf_flow_offload_wq)
nf_flow_offload_add_wq = alloc_workqueue("nf_ft_offload_add",
WQ_UNBOUND | WQ_SYSFS, 0);
if (!nf_flow_offload_add_wq)
return -ENOMEM;
nf_flow_offload_del_wq = alloc_workqueue("nf_ft_offload_del",
WQ_UNBOUND | WQ_SYSFS, 0);
if (!nf_flow_offload_del_wq)
goto err_del_wq;
nf_flow_offload_stats_wq = alloc_workqueue("nf_ft_offload_stats",
WQ_UNBOUND | WQ_SYSFS, 0);
if (!nf_flow_offload_stats_wq)
goto err_stats_wq;
return 0;
err_stats_wq:
destroy_workqueue(nf_flow_offload_del_wq);
err_del_wq:
destroy_workqueue(nf_flow_offload_add_wq);
return -ENOMEM;
}
void nf_flow_table_offload_exit(void)
{
destroy_workqueue(nf_flow_offload_wq);
destroy_workqueue(nf_flow_offload_add_wq);
destroy_workqueue(nf_flow_offload_del_wq);
destroy_workqueue(nf_flow_offload_stats_wq);
}
......@@ -900,6 +900,12 @@ static void nf_tables_table_disable(struct net *net, struct nft_table *table)
nft_table_disable(net, table, 0);
}
enum {
NFT_TABLE_STATE_UNCHANGED = 0,
NFT_TABLE_STATE_DORMANT,
NFT_TABLE_STATE_WAKEUP
};
static int nf_tables_updtable(struct nft_ctx *ctx)
{
struct nft_trans *trans;
......@@ -929,19 +935,17 @@ static int nf_tables_updtable(struct nft_ctx *ctx)
if ((flags & NFT_TABLE_F_DORMANT) &&
!(ctx->table->flags & NFT_TABLE_F_DORMANT)) {
nft_trans_table_enable(trans) = false;
nft_trans_table_state(trans) = NFT_TABLE_STATE_DORMANT;
} else if (!(flags & NFT_TABLE_F_DORMANT) &&
ctx->table->flags & NFT_TABLE_F_DORMANT) {
ctx->table->flags &= ~NFT_TABLE_F_DORMANT;
ret = nf_tables_table_enable(ctx->net, ctx->table);
if (ret >= 0)
nft_trans_table_enable(trans) = true;
else
ctx->table->flags |= NFT_TABLE_F_DORMANT;
nft_trans_table_state(trans) = NFT_TABLE_STATE_WAKEUP;
}
if (ret < 0)
goto err;
nft_trans_table_flags(trans) = flags;
nft_trans_table_update(trans) = true;
list_add_tail(&trans->list, &ctx->net->nft.commit_list);
return 0;
......@@ -8068,11 +8072,10 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
switch (trans->msg_type) {
case NFT_MSG_NEWTABLE:
if (nft_trans_table_update(trans)) {
if (!nft_trans_table_enable(trans)) {
nf_tables_table_disable(net,
trans->ctx.table);
trans->ctx.table->flags |= NFT_TABLE_F_DORMANT;
}
if (nft_trans_table_state(trans) == NFT_TABLE_STATE_DORMANT)
nf_tables_table_disable(net, trans->ctx.table);
trans->ctx.table->flags = nft_trans_table_flags(trans);
} else {
nft_clear(net, trans->ctx.table);
}
......@@ -8283,11 +8286,9 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
switch (trans->msg_type) {
case NFT_MSG_NEWTABLE:
if (nft_trans_table_update(trans)) {
if (nft_trans_table_enable(trans)) {
nf_tables_table_disable(net,
trans->ctx.table);
trans->ctx.table->flags |= NFT_TABLE_F_DORMANT;
}
if (nft_trans_table_state(trans) == NFT_TABLE_STATE_WAKEUP)
nf_tables_table_disable(net, trans->ctx.table);
nft_trans_destroy(trans);
} else {
list_del_rcu(&trans->ctx.table->list);
......@@ -8557,6 +8558,7 @@ static int nf_tables_check_loops(const struct nft_ctx *ctx,
data->verdict.chain);
if (err < 0)
return err;
break;
default:
break;
}
......
......@@ -527,6 +527,7 @@ static void __nft_ct_set_destroy(const struct nft_ctx *ctx, struct nft_ct *priv)
case NFT_CT_ZONE:
if (--nft_ct_pcpu_template_refcnt == 0)
nft_ct_tmpl_put_pcpu();
break;
#endif
default:
break;
......
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