Commit 355a8b13 authored by Pablo Neira Ayuso's avatar Pablo Neira Ayuso

netfilter: flowtable: use atomic bitwise operations for flow flags

Originally, all flow flag bits were set on only from the workqueue. With
the introduction of the flow teardown state and hardware offload this is
no longer true. Let's be safe and use atomic bitwise operation to
operation with flow flags.

Fixes: 59c466dd ("netfilter: nf_flow_table: add a new flow state for tearing down offloading")
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent 445db8d0
...@@ -83,12 +83,14 @@ struct flow_offload_tuple_rhash { ...@@ -83,12 +83,14 @@ struct flow_offload_tuple_rhash {
struct flow_offload_tuple tuple; struct flow_offload_tuple tuple;
}; };
#define FLOW_OFFLOAD_SNAT 0x1 enum nf_flow_flags {
#define FLOW_OFFLOAD_DNAT 0x2 NF_FLOW_SNAT,
#define FLOW_OFFLOAD_TEARDOWN 0x8 NF_FLOW_DNAT,
#define FLOW_OFFLOAD_HW 0x10 NF_FLOW_TEARDOWN,
#define FLOW_OFFLOAD_HW_DYING 0x20 NF_FLOW_HW,
#define FLOW_OFFLOAD_HW_DEAD 0x40 NF_FLOW_HW_DYING,
NF_FLOW_HW_DEAD,
};
enum flow_offload_type { enum flow_offload_type {
NF_FLOW_OFFLOAD_UNSPEC = 0, NF_FLOW_OFFLOAD_UNSPEC = 0,
...@@ -98,7 +100,7 @@ enum flow_offload_type { ...@@ -98,7 +100,7 @@ enum flow_offload_type {
struct flow_offload { struct flow_offload {
struct flow_offload_tuple_rhash tuplehash[FLOW_OFFLOAD_DIR_MAX]; struct flow_offload_tuple_rhash tuplehash[FLOW_OFFLOAD_DIR_MAX];
struct nf_conn *ct; struct nf_conn *ct;
u16 flags; unsigned long flags;
u16 type; u16 type;
u32 timeout; u32 timeout;
struct rcu_head rcu_head; struct rcu_head rcu_head;
......
...@@ -61,9 +61,9 @@ struct flow_offload *flow_offload_alloc(struct nf_conn *ct) ...@@ -61,9 +61,9 @@ struct flow_offload *flow_offload_alloc(struct nf_conn *ct)
flow_offload_fill_dir(flow, FLOW_OFFLOAD_DIR_REPLY); flow_offload_fill_dir(flow, FLOW_OFFLOAD_DIR_REPLY);
if (ct->status & IPS_SRC_NAT) if (ct->status & IPS_SRC_NAT)
flow->flags |= FLOW_OFFLOAD_SNAT; __set_bit(NF_FLOW_SNAT, &flow->flags);
if (ct->status & IPS_DST_NAT) if (ct->status & IPS_DST_NAT)
flow->flags |= FLOW_OFFLOAD_DNAT; __set_bit(NF_FLOW_DNAT, &flow->flags);
return flow; return flow;
...@@ -269,7 +269,7 @@ static void flow_offload_del(struct nf_flowtable *flow_table, ...@@ -269,7 +269,7 @@ static void flow_offload_del(struct nf_flowtable *flow_table,
if (nf_flow_has_expired(flow)) if (nf_flow_has_expired(flow))
flow_offload_fixup_ct(flow->ct); flow_offload_fixup_ct(flow->ct);
else if (flow->flags & FLOW_OFFLOAD_TEARDOWN) else if (test_bit(NF_FLOW_TEARDOWN, &flow->flags))
flow_offload_fixup_ct_timeout(flow->ct); flow_offload_fixup_ct_timeout(flow->ct);
flow_offload_free(flow); flow_offload_free(flow);
...@@ -277,7 +277,7 @@ static void flow_offload_del(struct nf_flowtable *flow_table, ...@@ -277,7 +277,7 @@ static void flow_offload_del(struct nf_flowtable *flow_table,
void flow_offload_teardown(struct flow_offload *flow) void flow_offload_teardown(struct flow_offload *flow)
{ {
flow->flags |= FLOW_OFFLOAD_TEARDOWN; set_bit(NF_FLOW_TEARDOWN, &flow->flags);
flow_offload_fixup_ct_state(flow->ct); flow_offload_fixup_ct_state(flow->ct);
} }
...@@ -298,7 +298,7 @@ flow_offload_lookup(struct nf_flowtable *flow_table, ...@@ -298,7 +298,7 @@ flow_offload_lookup(struct nf_flowtable *flow_table,
dir = tuplehash->tuple.dir; dir = tuplehash->tuple.dir;
flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]); flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
if (flow->flags & FLOW_OFFLOAD_TEARDOWN) if (test_bit(NF_FLOW_TEARDOWN, &flow->flags))
return NULL; return NULL;
if (unlikely(nf_ct_is_dying(flow->ct))) if (unlikely(nf_ct_is_dying(flow->ct)))
...@@ -347,16 +347,16 @@ static void nf_flow_offload_gc_step(struct flow_offload *flow, void *data) ...@@ -347,16 +347,16 @@ static void nf_flow_offload_gc_step(struct flow_offload *flow, void *data)
struct nf_flowtable *flow_table = data; struct nf_flowtable *flow_table = data;
if (nf_flow_has_expired(flow) || nf_ct_is_dying(flow->ct) || if (nf_flow_has_expired(flow) || nf_ct_is_dying(flow->ct) ||
(flow->flags & FLOW_OFFLOAD_TEARDOWN)) { test_bit(NF_FLOW_TEARDOWN, &flow->flags)) {
if (flow->flags & FLOW_OFFLOAD_HW) { if (test_bit(NF_FLOW_HW, &flow->flags)) {
if (!(flow->flags & FLOW_OFFLOAD_HW_DYING)) if (!test_bit(NF_FLOW_HW_DYING, &flow->flags))
nf_flow_offload_del(flow_table, flow); nf_flow_offload_del(flow_table, flow);
else if (flow->flags & FLOW_OFFLOAD_HW_DEAD) else if (test_bit(NF_FLOW_HW_DEAD, &flow->flags))
flow_offload_del(flow_table, flow); flow_offload_del(flow_table, flow);
} else { } else {
flow_offload_del(flow_table, flow); flow_offload_del(flow_table, flow);
} }
} else if (flow->flags & FLOW_OFFLOAD_HW) { } else if (test_bit(NF_FLOW_HW, &flow->flags)) {
nf_flow_offload_stats(flow_table, flow); nf_flow_offload_stats(flow_table, flow);
} }
} }
......
...@@ -144,11 +144,11 @@ static int nf_flow_nat_ip(const struct flow_offload *flow, struct sk_buff *skb, ...@@ -144,11 +144,11 @@ static int nf_flow_nat_ip(const struct flow_offload *flow, struct sk_buff *skb,
{ {
struct iphdr *iph = ip_hdr(skb); struct iphdr *iph = ip_hdr(skb);
if (flow->flags & FLOW_OFFLOAD_SNAT && if (test_bit(NF_FLOW_SNAT, &flow->flags) &&
(nf_flow_snat_port(flow, skb, thoff, iph->protocol, dir) < 0 || (nf_flow_snat_port(flow, skb, thoff, iph->protocol, dir) < 0 ||
nf_flow_snat_ip(flow, skb, iph, thoff, dir) < 0)) nf_flow_snat_ip(flow, skb, iph, thoff, dir) < 0))
return -1; return -1;
if (flow->flags & FLOW_OFFLOAD_DNAT && if (test_bit(NF_FLOW_DNAT, &flow->flags) &&
(nf_flow_dnat_port(flow, skb, thoff, iph->protocol, dir) < 0 || (nf_flow_dnat_port(flow, skb, thoff, iph->protocol, dir) < 0 ||
nf_flow_dnat_ip(flow, skb, iph, thoff, dir) < 0)) nf_flow_dnat_ip(flow, skb, iph, thoff, dir) < 0))
return -1; return -1;
...@@ -414,11 +414,11 @@ static int nf_flow_nat_ipv6(const struct flow_offload *flow, ...@@ -414,11 +414,11 @@ static int nf_flow_nat_ipv6(const struct flow_offload *flow,
struct ipv6hdr *ip6h = ipv6_hdr(skb); struct ipv6hdr *ip6h = ipv6_hdr(skb);
unsigned int thoff = sizeof(*ip6h); unsigned int thoff = sizeof(*ip6h);
if (flow->flags & FLOW_OFFLOAD_SNAT && if (test_bit(NF_FLOW_SNAT, &flow->flags) &&
(nf_flow_snat_port(flow, skb, thoff, ip6h->nexthdr, dir) < 0 || (nf_flow_snat_port(flow, skb, thoff, ip6h->nexthdr, dir) < 0 ||
nf_flow_snat_ipv6(flow, skb, ip6h, thoff, dir) < 0)) nf_flow_snat_ipv6(flow, skb, ip6h, thoff, dir) < 0))
return -1; return -1;
if (flow->flags & FLOW_OFFLOAD_DNAT && if (test_bit(NF_FLOW_DNAT, &flow->flags) &&
(nf_flow_dnat_port(flow, skb, thoff, ip6h->nexthdr, dir) < 0 || (nf_flow_dnat_port(flow, skb, thoff, ip6h->nexthdr, dir) < 0 ||
nf_flow_dnat_ipv6(flow, skb, ip6h, thoff, dir) < 0)) nf_flow_dnat_ipv6(flow, skb, ip6h, thoff, dir) < 0))
return -1; return -1;
......
...@@ -450,16 +450,16 @@ int nf_flow_rule_route_ipv4(struct net *net, const struct flow_offload *flow, ...@@ -450,16 +450,16 @@ int nf_flow_rule_route_ipv4(struct net *net, const struct flow_offload *flow,
flow_offload_eth_dst(net, flow, dir, flow_rule) < 0) flow_offload_eth_dst(net, flow, dir, flow_rule) < 0)
return -1; return -1;
if (flow->flags & FLOW_OFFLOAD_SNAT) { if (test_bit(NF_FLOW_SNAT, &flow->flags)) {
flow_offload_ipv4_snat(net, flow, dir, flow_rule); flow_offload_ipv4_snat(net, flow, dir, flow_rule);
flow_offload_port_snat(net, flow, dir, flow_rule); flow_offload_port_snat(net, flow, dir, flow_rule);
} }
if (flow->flags & FLOW_OFFLOAD_DNAT) { if (test_bit(NF_FLOW_DNAT, &flow->flags)) {
flow_offload_ipv4_dnat(net, flow, dir, flow_rule); flow_offload_ipv4_dnat(net, flow, dir, flow_rule);
flow_offload_port_dnat(net, flow, dir, flow_rule); flow_offload_port_dnat(net, flow, dir, flow_rule);
} }
if (flow->flags & FLOW_OFFLOAD_SNAT || if (test_bit(NF_FLOW_SNAT, &flow->flags) ||
flow->flags & FLOW_OFFLOAD_DNAT) test_bit(NF_FLOW_DNAT, &flow->flags))
flow_offload_ipv4_checksum(net, flow, flow_rule); flow_offload_ipv4_checksum(net, flow, flow_rule);
flow_offload_redirect(flow, dir, flow_rule); flow_offload_redirect(flow, dir, flow_rule);
...@@ -476,11 +476,11 @@ int nf_flow_rule_route_ipv6(struct net *net, const struct flow_offload *flow, ...@@ -476,11 +476,11 @@ int nf_flow_rule_route_ipv6(struct net *net, const struct flow_offload *flow,
flow_offload_eth_dst(net, flow, dir, flow_rule) < 0) flow_offload_eth_dst(net, flow, dir, flow_rule) < 0)
return -1; return -1;
if (flow->flags & FLOW_OFFLOAD_SNAT) { if (test_bit(NF_FLOW_SNAT, &flow->flags)) {
flow_offload_ipv6_snat(net, flow, dir, flow_rule); flow_offload_ipv6_snat(net, flow, dir, flow_rule);
flow_offload_port_snat(net, flow, dir, flow_rule); flow_offload_port_snat(net, flow, dir, flow_rule);
} }
if (flow->flags & FLOW_OFFLOAD_DNAT) { if (test_bit(NF_FLOW_DNAT, &flow->flags)) {
flow_offload_ipv6_dnat(net, flow, dir, flow_rule); flow_offload_ipv6_dnat(net, flow, dir, flow_rule);
flow_offload_port_dnat(net, flow, dir, flow_rule); flow_offload_port_dnat(net, flow, dir, flow_rule);
} }
...@@ -636,7 +636,7 @@ static void flow_offload_tuple_del(struct flow_offload_work *offload, ...@@ -636,7 +636,7 @@ static void flow_offload_tuple_del(struct flow_offload_work *offload,
list_for_each_entry(block_cb, &flowtable->flow_block.cb_list, list) list_for_each_entry(block_cb, &flowtable->flow_block.cb_list, list)
block_cb->cb(TC_SETUP_CLSFLOWER, &cls_flow, block_cb->cb_priv); block_cb->cb(TC_SETUP_CLSFLOWER, &cls_flow, block_cb->cb_priv);
offload->flow->flags |= FLOW_OFFLOAD_HW_DEAD; set_bit(NF_FLOW_HW_DEAD, &offload->flow->flags);
} }
static int flow_offload_rule_add(struct flow_offload_work *offload, static int flow_offload_rule_add(struct flow_offload_work *offload,
...@@ -723,7 +723,7 @@ static void flow_offload_work_handler(struct work_struct *work) ...@@ -723,7 +723,7 @@ static void flow_offload_work_handler(struct work_struct *work)
case FLOW_CLS_REPLACE: case FLOW_CLS_REPLACE:
ret = flow_offload_work_add(offload); ret = flow_offload_work_add(offload);
if (ret < 0) if (ret < 0)
offload->flow->flags &= ~FLOW_OFFLOAD_HW; __clear_bit(NF_FLOW_HW, &offload->flow->flags);
break; break;
case FLOW_CLS_DESTROY: case FLOW_CLS_DESTROY:
flow_offload_work_del(offload); flow_offload_work_del(offload);
...@@ -776,7 +776,7 @@ void nf_flow_offload_add(struct nf_flowtable *flowtable, ...@@ -776,7 +776,7 @@ void nf_flow_offload_add(struct nf_flowtable *flowtable,
if (!offload) if (!offload)
return; return;
flow->flags |= FLOW_OFFLOAD_HW; __set_bit(NF_FLOW_HW, &flow->flags);
flow_offload_queue_work(offload); flow_offload_queue_work(offload);
} }
...@@ -789,7 +789,7 @@ void nf_flow_offload_del(struct nf_flowtable *flowtable, ...@@ -789,7 +789,7 @@ void nf_flow_offload_del(struct nf_flowtable *flowtable,
if (!offload) if (!offload)
return; return;
flow->flags |= FLOW_OFFLOAD_HW_DYING; set_bit(NF_FLOW_HW_DYING, &flow->flags);
flow_offload_queue_work(offload); flow_offload_queue_work(offload);
} }
......
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