1 From: Pablo Neira Ayuso <pablo@netfilter.org>
2 Date: Mon, 7 Dec 2020 20:31:44 +0100
3 Subject: [PATCH] netfilter: flowtable: add offload support for xmit path
6 When the flow tuple xmit_type is set to FLOW_OFFLOAD_XMIT_DIRECT, the
7 dst_cache pointer is not valid, and the h_source/h_dest/ifidx out fields
10 This patch also adds the FLOW_ACTION_VLAN_PUSH action to pass the VLAN
14 --- a/net/netfilter/nf_flow_table_offload.c
15 +++ b/net/netfilter/nf_flow_table_offload.c
16 @@ -175,28 +175,45 @@ static int flow_offload_eth_src(struct n
17 enum flow_offload_tuple_dir dir,
18 struct nf_flow_rule *flow_rule)
20 - const struct flow_offload_tuple *tuple = &flow->tuplehash[!dir].tuple;
21 struct flow_action_entry *entry0 = flow_action_entry_next(flow_rule);
22 struct flow_action_entry *entry1 = flow_action_entry_next(flow_rule);
23 - struct net_device *dev;
24 + const struct flow_offload_tuple *other_tuple, *this_tuple;
25 + struct net_device *dev = NULL;
26 + const unsigned char *addr;
30 - dev = dev_get_by_index(net, tuple->iifidx);
33 + this_tuple = &flow->tuplehash[dir].tuple;
35 + switch (this_tuple->xmit_type) {
36 + case FLOW_OFFLOAD_XMIT_DIRECT:
37 + addr = this_tuple->out.h_source;
39 + case FLOW_OFFLOAD_XMIT_NEIGH:
40 + other_tuple = &flow->tuplehash[!dir].tuple;
41 + dev = dev_get_by_index(net, other_tuple->iifidx);
45 + addr = dev->dev_addr;
52 - memcpy(&val16, dev->dev_addr, 2);
53 + memcpy(&val16, addr, 2);
55 flow_offload_mangle(entry0, FLOW_ACT_MANGLE_HDR_TYPE_ETH, 4,
59 - memcpy(&val, dev->dev_addr + 2, 4);
60 + memcpy(&val, addr + 2, 4);
61 flow_offload_mangle(entry1, FLOW_ACT_MANGLE_HDR_TYPE_ETH, 8,
70 @@ -208,27 +225,40 @@ static int flow_offload_eth_dst(struct n
72 struct flow_action_entry *entry0 = flow_action_entry_next(flow_rule);
73 struct flow_action_entry *entry1 = flow_action_entry_next(flow_rule);
74 - const void *daddr = &flow->tuplehash[!dir].tuple.src_v4;
75 + const struct flow_offload_tuple *other_tuple, *this_tuple;
76 const struct dst_entry *dst_cache;
77 unsigned char ha[ETH_ALEN];
84 - dst_cache = flow->tuplehash[dir].tuple.dst_cache;
85 - n = dst_neigh_lookup(dst_cache, daddr);
88 + this_tuple = &flow->tuplehash[dir].tuple;
90 - read_lock_bh(&n->lock);
91 - nud_state = n->nud_state;
92 - ether_addr_copy(ha, n->ha);
93 - read_unlock_bh(&n->lock);
94 + switch (this_tuple->xmit_type) {
95 + case FLOW_OFFLOAD_XMIT_DIRECT:
96 + ether_addr_copy(ha, this_tuple->out.h_dest);
98 + case FLOW_OFFLOAD_XMIT_NEIGH:
99 + other_tuple = &flow->tuplehash[!dir].tuple;
100 + daddr = &other_tuple->src_v4;
101 + dst_cache = this_tuple->dst_cache;
102 + n = dst_neigh_lookup(dst_cache, daddr);
106 - if (!(nud_state & NUD_VALID)) {
107 + read_lock_bh(&n->lock);
108 + nud_state = n->nud_state;
109 + ether_addr_copy(ha, n->ha);
110 + read_unlock_bh(&n->lock);
114 + if (!(nud_state & NUD_VALID))
118 + return -EOPNOTSUPP;
122 @@ -241,7 +271,6 @@ static int flow_offload_eth_dst(struct n
124 flow_offload_mangle(entry1, FLOW_ACT_MANGLE_HDR_TYPE_ETH, 4,
130 @@ -463,27 +492,52 @@ static void flow_offload_ipv4_checksum(s
134 -static void flow_offload_redirect(const struct flow_offload *flow,
135 +static void flow_offload_redirect(struct net *net,
136 + const struct flow_offload *flow,
137 enum flow_offload_tuple_dir dir,
138 struct nf_flow_rule *flow_rule)
140 - struct flow_action_entry *entry = flow_action_entry_next(flow_rule);
142 + const struct flow_offload_tuple *this_tuple, *other_tuple;
143 + struct flow_action_entry *entry;
144 + struct net_device *dev;
147 - rt = (struct rtable *)flow->tuplehash[dir].tuple.dst_cache;
148 + this_tuple = &flow->tuplehash[dir].tuple;
149 + switch (this_tuple->xmit_type) {
150 + case FLOW_OFFLOAD_XMIT_DIRECT:
151 + this_tuple = &flow->tuplehash[dir].tuple;
152 + ifindex = this_tuple->out.ifidx;
154 + case FLOW_OFFLOAD_XMIT_NEIGH:
155 + other_tuple = &flow->tuplehash[!dir].tuple;
156 + ifindex = other_tuple->iifidx;
162 + dev = dev_get_by_index(net, ifindex);
166 + entry = flow_action_entry_next(flow_rule);
167 entry->id = FLOW_ACTION_REDIRECT;
168 - entry->dev = rt->dst.dev;
169 - dev_hold(rt->dst.dev);
173 static void flow_offload_encap_tunnel(const struct flow_offload *flow,
174 enum flow_offload_tuple_dir dir,
175 struct nf_flow_rule *flow_rule)
177 + const struct flow_offload_tuple *this_tuple;
178 struct flow_action_entry *entry;
179 struct dst_entry *dst;
181 - dst = flow->tuplehash[dir].tuple.dst_cache;
182 + this_tuple = &flow->tuplehash[dir].tuple;
183 + if (this_tuple->xmit_type == FLOW_OFFLOAD_XMIT_DIRECT)
186 + dst = this_tuple->dst_cache;
187 if (dst && dst->lwtstate) {
188 struct ip_tunnel_info *tun_info;
190 @@ -500,10 +554,15 @@ static void flow_offload_decap_tunnel(co
191 enum flow_offload_tuple_dir dir,
192 struct nf_flow_rule *flow_rule)
194 + const struct flow_offload_tuple *other_tuple;
195 struct flow_action_entry *entry;
196 struct dst_entry *dst;
198 - dst = flow->tuplehash[!dir].tuple.dst_cache;
199 + other_tuple = &flow->tuplehash[!dir].tuple;
200 + if (other_tuple->xmit_type == FLOW_OFFLOAD_XMIT_DIRECT)
203 + dst = other_tuple->dst_cache;
204 if (dst && dst->lwtstate) {
205 struct ip_tunnel_info *tun_info;
207 @@ -515,10 +574,14 @@ static void flow_offload_decap_tunnel(co
211 -int nf_flow_rule_route_ipv4(struct net *net, const struct flow_offload *flow,
212 - enum flow_offload_tuple_dir dir,
213 - struct nf_flow_rule *flow_rule)
215 +nf_flow_rule_route_common(struct net *net, const struct flow_offload *flow,
216 + enum flow_offload_tuple_dir dir,
217 + struct nf_flow_rule *flow_rule)
219 + const struct flow_offload_tuple *other_tuple;
222 flow_offload_decap_tunnel(flow, dir, flow_rule);
223 flow_offload_encap_tunnel(flow, dir, flow_rule);
225 @@ -526,6 +589,26 @@ int nf_flow_rule_route_ipv4(struct net *
226 flow_offload_eth_dst(net, flow, dir, flow_rule) < 0)
229 + other_tuple = &flow->tuplehash[!dir].tuple;
231 + for (i = 0; i < other_tuple->in_vlan_num; i++) {
232 + struct flow_action_entry *entry = flow_action_entry_next(flow_rule);
234 + entry->id = FLOW_ACTION_VLAN_PUSH;
235 + entry->vlan.vid = other_tuple->in_vlan[i].id;
236 + entry->vlan.proto = other_tuple->in_vlan[i].proto;
242 +int nf_flow_rule_route_ipv4(struct net *net, const struct flow_offload *flow,
243 + enum flow_offload_tuple_dir dir,
244 + struct nf_flow_rule *flow_rule)
246 + if (nf_flow_rule_route_common(net, flow, dir, flow_rule) < 0)
249 if (test_bit(NF_FLOW_SNAT, &flow->flags)) {
250 flow_offload_ipv4_snat(net, flow, dir, flow_rule);
251 flow_offload_port_snat(net, flow, dir, flow_rule);
252 @@ -538,7 +621,7 @@ int nf_flow_rule_route_ipv4(struct net *
253 test_bit(NF_FLOW_DNAT, &flow->flags))
254 flow_offload_ipv4_checksum(net, flow, flow_rule);
256 - flow_offload_redirect(flow, dir, flow_rule);
257 + flow_offload_redirect(net, flow, dir, flow_rule);
261 @@ -548,11 +631,7 @@ int nf_flow_rule_route_ipv6(struct net *
262 enum flow_offload_tuple_dir dir,
263 struct nf_flow_rule *flow_rule)
265 - flow_offload_decap_tunnel(flow, dir, flow_rule);
266 - flow_offload_encap_tunnel(flow, dir, flow_rule);
268 - if (flow_offload_eth_src(net, flow, dir, flow_rule) < 0 ||
269 - flow_offload_eth_dst(net, flow, dir, flow_rule) < 0)
270 + if (nf_flow_rule_route_common(net, flow, dir, flow_rule) < 0)
273 if (test_bit(NF_FLOW_SNAT, &flow->flags)) {
274 @@ -564,7 +643,7 @@ int nf_flow_rule_route_ipv6(struct net *
275 flow_offload_port_dnat(net, flow, dir, flow_rule);
278 - flow_offload_redirect(flow, dir, flow_rule);
279 + flow_offload_redirect(net, flow, dir, flow_rule);
283 @@ -578,10 +657,10 @@ nf_flow_offload_rule_alloc(struct net *n
284 enum flow_offload_tuple_dir dir)
286 const struct nf_flowtable *flowtable = offload->flowtable;
287 + const struct flow_offload_tuple *tuple, *other_tuple;
288 const struct flow_offload *flow = offload->flow;
289 - const struct flow_offload_tuple *tuple;
290 + struct dst_entry *other_dst = NULL;
291 struct nf_flow_rule *flow_rule;
292 - struct dst_entry *other_dst;
295 flow_rule = kzalloc(sizeof(*flow_rule), GFP_KERNEL);
296 @@ -597,7 +676,10 @@ nf_flow_offload_rule_alloc(struct net *n
297 flow_rule->rule->match.key = &flow_rule->match.key;
299 tuple = &flow->tuplehash[dir].tuple;
300 - other_dst = flow->tuplehash[!dir].tuple.dst_cache;
301 + other_tuple = &flow->tuplehash[!dir].tuple;
302 + if (other_tuple->xmit_type == FLOW_OFFLOAD_XMIT_NEIGH)
303 + other_dst = other_tuple->dst_cache;
305 err = nf_flow_rule_match(&flow_rule->match, tuple, other_dst);