f753b590e70b9c055973962dda6120d16bb83779
[openwrt/openwrt.git] / target / linux / generic / hack-6.1 / 650-netfilter-add-xt_FLOWOFFLOAD-target.patch
1 From: Felix Fietkau <nbd@nbd.name>
2 Date: Tue, 20 Feb 2018 15:56:02 +0100
3 Subject: [PATCH] netfilter: add xt_FLOWOFFLOAD target
4
5 Signed-off-by: Felix Fietkau <nbd@nbd.name>
6 ---
7 create mode 100644 net/netfilter/xt_OFFLOAD.c
8
9 --- a/net/netfilter/Kconfig
10 +++ b/net/netfilter/Kconfig
11 @@ -1023,6 +1023,15 @@ config NETFILTER_XT_TARGET_NOTRACK
12 depends on NETFILTER_ADVANCED
13 select NETFILTER_XT_TARGET_CT
14
15 +config NETFILTER_XT_TARGET_FLOWOFFLOAD
16 + tristate '"FLOWOFFLOAD" target support'
17 + depends on NF_FLOW_TABLE
18 + depends on NETFILTER_INGRESS
19 + help
20 + This option adds a `FLOWOFFLOAD' target, which uses the nf_flow_offload
21 + module to speed up processing of packets by bypassing the usual
22 + netfilter chains
23 +
24 config NETFILTER_XT_TARGET_RATEEST
25 tristate '"RATEEST" target support'
26 depends on NETFILTER_ADVANCED
27 --- a/net/netfilter/Makefile
28 +++ b/net/netfilter/Makefile
29 @@ -154,6 +154,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_CLASSIF
30 obj-$(CONFIG_NETFILTER_XT_TARGET_CONNSECMARK) += xt_CONNSECMARK.o
31 obj-$(CONFIG_NETFILTER_XT_TARGET_CT) += xt_CT.o
32 obj-$(CONFIG_NETFILTER_XT_TARGET_DSCP) += xt_DSCP.o
33 +obj-$(CONFIG_NETFILTER_XT_TARGET_FLOWOFFLOAD) += xt_FLOWOFFLOAD.o
34 obj-$(CONFIG_NETFILTER_XT_TARGET_HL) += xt_HL.o
35 obj-$(CONFIG_NETFILTER_XT_TARGET_HMARK) += xt_HMARK.o
36 obj-$(CONFIG_NETFILTER_XT_TARGET_LED) += xt_LED.o
37 --- /dev/null
38 +++ b/net/netfilter/xt_FLOWOFFLOAD.c
39 @@ -0,0 +1,699 @@
40 +/*
41 + * Copyright (C) 2018-2021 Felix Fietkau <nbd@nbd.name>
42 + *
43 + * This program is free software; you can redistribute it and/or modify
44 + * it under the terms of the GNU General Public License version 2 as
45 + * published by the Free Software Foundation.
46 + */
47 +#include <linux/module.h>
48 +#include <linux/init.h>
49 +#include <linux/netfilter.h>
50 +#include <linux/netfilter/xt_FLOWOFFLOAD.h>
51 +#include <linux/if_vlan.h>
52 +#include <net/ip.h>
53 +#include <net/netfilter/nf_conntrack.h>
54 +#include <net/netfilter/nf_conntrack_extend.h>
55 +#include <net/netfilter/nf_conntrack_helper.h>
56 +#include <net/netfilter/nf_flow_table.h>
57 +
58 +struct xt_flowoffload_hook {
59 + struct hlist_node list;
60 + struct nf_hook_ops ops;
61 + struct net *net;
62 + bool registered;
63 + bool used;
64 +};
65 +
66 +struct xt_flowoffload_table {
67 + struct nf_flowtable ft;
68 + struct hlist_head hooks;
69 + struct delayed_work work;
70 +};
71 +
72 +struct nf_forward_info {
73 + const struct net_device *indev;
74 + const struct net_device *outdev;
75 + const struct net_device *hw_outdev;
76 + struct id {
77 + __u16 id;
78 + __be16 proto;
79 + } encap[NF_FLOW_TABLE_ENCAP_MAX];
80 + u8 num_encaps;
81 + u8 ingress_vlans;
82 + u8 h_source[ETH_ALEN];
83 + u8 h_dest[ETH_ALEN];
84 + enum flow_offload_xmit_type xmit_type;
85 +};
86 +
87 +static DEFINE_SPINLOCK(hooks_lock);
88 +
89 +struct xt_flowoffload_table flowtable[2];
90 +
91 +static unsigned int
92 +xt_flowoffload_net_hook(void *priv, struct sk_buff *skb,
93 + const struct nf_hook_state *state)
94 +{
95 + struct vlan_ethhdr *veth;
96 + __be16 proto;
97 +
98 + switch (skb->protocol) {
99 + case htons(ETH_P_8021Q):
100 + veth = (struct vlan_ethhdr *)skb_mac_header(skb);
101 + proto = veth->h_vlan_encapsulated_proto;
102 + break;
103 + case htons(ETH_P_PPP_SES):
104 + proto = nf_flow_pppoe_proto(skb);
105 + break;
106 + default:
107 + proto = skb->protocol;
108 + break;
109 + }
110 +
111 + switch (proto) {
112 + case htons(ETH_P_IP):
113 + return nf_flow_offload_ip_hook(priv, skb, state);
114 + case htons(ETH_P_IPV6):
115 + return nf_flow_offload_ipv6_hook(priv, skb, state);
116 + }
117 +
118 + return NF_ACCEPT;
119 +}
120 +
121 +static int
122 +xt_flowoffload_create_hook(struct xt_flowoffload_table *table,
123 + struct net_device *dev)
124 +{
125 + struct xt_flowoffload_hook *hook;
126 + struct nf_hook_ops *ops;
127 +
128 + hook = kzalloc(sizeof(*hook), GFP_ATOMIC);
129 + if (!hook)
130 + return -ENOMEM;
131 +
132 + ops = &hook->ops;
133 + ops->pf = NFPROTO_NETDEV;
134 + ops->hooknum = NF_NETDEV_INGRESS;
135 + ops->priority = 10;
136 + ops->priv = &table->ft;
137 + ops->hook = xt_flowoffload_net_hook;
138 + ops->dev = dev;
139 +
140 + hlist_add_head(&hook->list, &table->hooks);
141 + mod_delayed_work(system_power_efficient_wq, &table->work, 0);
142 +
143 + return 0;
144 +}
145 +
146 +static struct xt_flowoffload_hook *
147 +flow_offload_lookup_hook(struct xt_flowoffload_table *table,
148 + struct net_device *dev)
149 +{
150 + struct xt_flowoffload_hook *hook;
151 +
152 + hlist_for_each_entry(hook, &table->hooks, list) {
153 + if (hook->ops.dev == dev)
154 + return hook;
155 + }
156 +
157 + return NULL;
158 +}
159 +
160 +static void
161 +xt_flowoffload_check_device(struct xt_flowoffload_table *table,
162 + struct net_device *dev)
163 +{
164 + struct xt_flowoffload_hook *hook;
165 +
166 + if (!dev)
167 + return;
168 +
169 + spin_lock_bh(&hooks_lock);
170 + hook = flow_offload_lookup_hook(table, dev);
171 + if (hook)
172 + hook->used = true;
173 + else
174 + xt_flowoffload_create_hook(table, dev);
175 + spin_unlock_bh(&hooks_lock);
176 +}
177 +
178 +static void
179 +xt_flowoffload_register_hooks(struct xt_flowoffload_table *table)
180 +{
181 + struct xt_flowoffload_hook *hook;
182 +
183 +restart:
184 + hlist_for_each_entry(hook, &table->hooks, list) {
185 + if (hook->registered)
186 + continue;
187 +
188 + hook->registered = true;
189 + hook->net = dev_net(hook->ops.dev);
190 + spin_unlock_bh(&hooks_lock);
191 + nf_register_net_hook(hook->net, &hook->ops);
192 + if (table->ft.flags & NF_FLOWTABLE_HW_OFFLOAD)
193 + table->ft.type->setup(&table->ft, hook->ops.dev,
194 + FLOW_BLOCK_BIND);
195 + spin_lock_bh(&hooks_lock);
196 + goto restart;
197 + }
198 +
199 +}
200 +
201 +static bool
202 +xt_flowoffload_cleanup_hooks(struct xt_flowoffload_table *table)
203 +{
204 + struct xt_flowoffload_hook *hook;
205 + bool active = false;
206 +
207 +restart:
208 + spin_lock_bh(&hooks_lock);
209 + hlist_for_each_entry(hook, &table->hooks, list) {
210 + if (hook->used || !hook->registered) {
211 + active = true;
212 + continue;
213 + }
214 +
215 + hlist_del(&hook->list);
216 + spin_unlock_bh(&hooks_lock);
217 + if (table->ft.flags & NF_FLOWTABLE_HW_OFFLOAD)
218 + table->ft.type->setup(&table->ft, hook->ops.dev,
219 + FLOW_BLOCK_UNBIND);
220 + nf_unregister_net_hook(hook->net, &hook->ops);
221 + kfree(hook);
222 + goto restart;
223 + }
224 + spin_unlock_bh(&hooks_lock);
225 +
226 + return active;
227 +}
228 +
229 +static void
230 +xt_flowoffload_check_hook(struct nf_flowtable *flowtable,
231 + struct flow_offload *flow, void *data)
232 +{
233 + struct xt_flowoffload_table *table;
234 + struct flow_offload_tuple *tuple0 = &flow->tuplehash[0].tuple;
235 + struct flow_offload_tuple *tuple1 = &flow->tuplehash[1].tuple;
236 + struct xt_flowoffload_hook *hook;
237 +
238 + table = container_of(flowtable, struct xt_flowoffload_table, ft);
239 +
240 + spin_lock_bh(&hooks_lock);
241 + hlist_for_each_entry(hook, &table->hooks, list) {
242 + if (hook->ops.dev->ifindex != tuple0->iifidx &&
243 + hook->ops.dev->ifindex != tuple1->iifidx)
244 + continue;
245 +
246 + hook->used = true;
247 + }
248 + spin_unlock_bh(&hooks_lock);
249 +}
250 +
251 +static void
252 +xt_flowoffload_hook_work(struct work_struct *work)
253 +{
254 + struct xt_flowoffload_table *table;
255 + struct xt_flowoffload_hook *hook;
256 + int err;
257 +
258 + table = container_of(work, struct xt_flowoffload_table, work.work);
259 +
260 + spin_lock_bh(&hooks_lock);
261 + xt_flowoffload_register_hooks(table);
262 + hlist_for_each_entry(hook, &table->hooks, list)
263 + hook->used = false;
264 + spin_unlock_bh(&hooks_lock);
265 +
266 + err = nf_flow_table_iterate(&table->ft, xt_flowoffload_check_hook,
267 + NULL);
268 + if (err && err != -EAGAIN)
269 + goto out;
270 +
271 + if (!xt_flowoffload_cleanup_hooks(table))
272 + return;
273 +
274 +out:
275 + queue_delayed_work(system_power_efficient_wq, &table->work, HZ);
276 +}
277 +
278 +static bool
279 +xt_flowoffload_skip(struct sk_buff *skb, int family)
280 +{
281 + if (skb_sec_path(skb))
282 + return true;
283 +
284 + if (family == NFPROTO_IPV4) {
285 + const struct ip_options *opt = &(IPCB(skb)->opt);
286 +
287 + if (unlikely(opt->optlen))
288 + return true;
289 + }
290 +
291 + return false;
292 +}
293 +
294 +static enum flow_offload_xmit_type nf_xmit_type(struct dst_entry *dst)
295 +{
296 + if (dst_xfrm(dst))
297 + return FLOW_OFFLOAD_XMIT_XFRM;
298 +
299 + return FLOW_OFFLOAD_XMIT_NEIGH;
300 +}
301 +
302 +static void nf_default_forward_path(struct nf_flow_route *route,
303 + struct dst_entry *dst_cache,
304 + enum ip_conntrack_dir dir,
305 + struct net_device **dev)
306 +{
307 + dev[!dir] = dst_cache->dev;
308 + route->tuple[!dir].in.ifindex = dst_cache->dev->ifindex;
309 + route->tuple[dir].dst = dst_cache;
310 + route->tuple[dir].xmit_type = nf_xmit_type(dst_cache);
311 +}
312 +
313 +static bool nf_is_valid_ether_device(const struct net_device *dev)
314 +{
315 + if (!dev || (dev->flags & IFF_LOOPBACK) || dev->type != ARPHRD_ETHER ||
316 + dev->addr_len != ETH_ALEN || !is_valid_ether_addr(dev->dev_addr))
317 + return false;
318 +
319 + return true;
320 +}
321 +
322 +static void nf_dev_path_info(const struct net_device_path_stack *stack,
323 + struct nf_forward_info *info,
324 + unsigned char *ha)
325 +{
326 + const struct net_device_path *path;
327 + int i;
328 +
329 + memcpy(info->h_dest, ha, ETH_ALEN);
330 +
331 + for (i = 0; i < stack->num_paths; i++) {
332 + path = &stack->path[i];
333 + switch (path->type) {
334 + case DEV_PATH_ETHERNET:
335 + case DEV_PATH_DSA:
336 + case DEV_PATH_VLAN:
337 + case DEV_PATH_PPPOE:
338 + info->indev = path->dev;
339 + if (is_zero_ether_addr(info->h_source))
340 + memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN);
341 +
342 + if (path->type == DEV_PATH_ETHERNET)
343 + break;
344 + if (path->type == DEV_PATH_DSA) {
345 + i = stack->num_paths;
346 + break;
347 + }
348 +
349 + /* DEV_PATH_VLAN and DEV_PATH_PPPOE */
350 + if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX) {
351 + info->indev = NULL;
352 + break;
353 + }
354 + if (!info->outdev)
355 + info->outdev = path->dev;
356 + info->encap[info->num_encaps].id = path->encap.id;
357 + info->encap[info->num_encaps].proto = path->encap.proto;
358 + info->num_encaps++;
359 + if (path->type == DEV_PATH_PPPOE)
360 + memcpy(info->h_dest, path->encap.h_dest, ETH_ALEN);
361 + break;
362 + case DEV_PATH_BRIDGE:
363 + if (is_zero_ether_addr(info->h_source))
364 + memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN);
365 +
366 + switch (path->bridge.vlan_mode) {
367 + case DEV_PATH_BR_VLAN_UNTAG_HW:
368 + info->ingress_vlans |= BIT(info->num_encaps - 1);
369 + break;
370 + case DEV_PATH_BR_VLAN_TAG:
371 + info->encap[info->num_encaps].id = path->bridge.vlan_id;
372 + info->encap[info->num_encaps].proto = path->bridge.vlan_proto;
373 + info->num_encaps++;
374 + break;
375 + case DEV_PATH_BR_VLAN_UNTAG:
376 + info->num_encaps--;
377 + break;
378 + case DEV_PATH_BR_VLAN_KEEP:
379 + break;
380 + }
381 + break;
382 + default:
383 + info->indev = NULL;
384 + break;
385 + }
386 + }
387 + if (!info->outdev)
388 + info->outdev = info->indev;
389 +
390 + info->hw_outdev = info->indev;
391 +
392 + if (nf_is_valid_ether_device(info->indev))
393 + info->xmit_type = FLOW_OFFLOAD_XMIT_DIRECT;
394 +}
395 +
396 +static int nf_dev_fill_forward_path(const struct nf_flow_route *route,
397 + const struct dst_entry *dst_cache,
398 + const struct nf_conn *ct,
399 + enum ip_conntrack_dir dir, u8 *ha,
400 + struct net_device_path_stack *stack)
401 +{
402 + const void *daddr = &ct->tuplehash[!dir].tuple.src.u3;
403 + struct net_device *dev = dst_cache->dev;
404 + struct neighbour *n;
405 + u8 nud_state;
406 +
407 + if (!nf_is_valid_ether_device(dev))
408 + goto out;
409 +
410 + n = dst_neigh_lookup(dst_cache, daddr);
411 + if (!n)
412 + return -1;
413 +
414 + read_lock_bh(&n->lock);
415 + nud_state = n->nud_state;
416 + ether_addr_copy(ha, n->ha);
417 + read_unlock_bh(&n->lock);
418 + neigh_release(n);
419 +
420 + if (!(nud_state & NUD_VALID))
421 + return -1;
422 +
423 +out:
424 + return dev_fill_forward_path(dev, ha, stack);
425 +}
426 +
427 +static void nf_dev_forward_path(struct nf_flow_route *route,
428 + const struct nf_conn *ct,
429 + enum ip_conntrack_dir dir,
430 + struct net_device **devs)
431 +{
432 + const struct dst_entry *dst = route->tuple[dir].dst;
433 + struct net_device_path_stack stack;
434 + struct nf_forward_info info = {};
435 + unsigned char ha[ETH_ALEN];
436 + int i;
437 +
438 + if (nf_dev_fill_forward_path(route, dst, ct, dir, ha, &stack) >= 0)
439 + nf_dev_path_info(&stack, &info, ha);
440 +
441 + devs[!dir] = (struct net_device *)info.indev;
442 + if (!info.indev)
443 + return;
444 +
445 + route->tuple[!dir].in.ifindex = info.indev->ifindex;
446 + for (i = 0; i < info.num_encaps; i++) {
447 + route->tuple[!dir].in.encap[i].id = info.encap[i].id;
448 + route->tuple[!dir].in.encap[i].proto = info.encap[i].proto;
449 + }
450 + route->tuple[!dir].in.num_encaps = info.num_encaps;
451 + route->tuple[!dir].in.ingress_vlans = info.ingress_vlans;
452 +
453 + if (info.xmit_type == FLOW_OFFLOAD_XMIT_DIRECT) {
454 + memcpy(route->tuple[dir].out.h_source, info.h_source, ETH_ALEN);
455 + memcpy(route->tuple[dir].out.h_dest, info.h_dest, ETH_ALEN);
456 + route->tuple[dir].out.ifindex = info.outdev->ifindex;
457 + route->tuple[dir].out.hw_ifindex = info.hw_outdev->ifindex;
458 + route->tuple[dir].xmit_type = info.xmit_type;
459 + }
460 +}
461 +
462 +static int
463 +xt_flowoffload_route(struct sk_buff *skb, const struct nf_conn *ct,
464 + const struct xt_action_param *par,
465 + struct nf_flow_route *route, enum ip_conntrack_dir dir,
466 + struct net_device **devs)
467 +{
468 + struct dst_entry *this_dst = skb_dst(skb);
469 + struct dst_entry *other_dst = NULL;
470 + struct flowi fl;
471 +
472 + memset(&fl, 0, sizeof(fl));
473 + switch (xt_family(par)) {
474 + case NFPROTO_IPV4:
475 + fl.u.ip4.daddr = ct->tuplehash[dir].tuple.src.u3.ip;
476 + fl.u.ip4.flowi4_oif = xt_in(par)->ifindex;
477 + break;
478 + case NFPROTO_IPV6:
479 + fl.u.ip6.saddr = ct->tuplehash[!dir].tuple.dst.u3.in6;
480 + fl.u.ip6.daddr = ct->tuplehash[dir].tuple.src.u3.in6;
481 + fl.u.ip6.flowi6_oif = xt_in(par)->ifindex;
482 + break;
483 + }
484 +
485 + nf_route(xt_net(par), &other_dst, &fl, false, xt_family(par));
486 + if (!other_dst)
487 + return -ENOENT;
488 +
489 + nf_default_forward_path(route, this_dst, dir, devs);
490 + nf_default_forward_path(route, other_dst, !dir, devs);
491 +
492 + if (route->tuple[dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH &&
493 + route->tuple[!dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH) {
494 + nf_dev_forward_path(route, ct, dir, devs);
495 + nf_dev_forward_path(route, ct, !dir, devs);
496 + }
497 +
498 + return 0;
499 +}
500 +
501 +static unsigned int
502 +flowoffload_tg(struct sk_buff *skb, const struct xt_action_param *par)
503 +{
504 + struct xt_flowoffload_table *table;
505 + const struct xt_flowoffload_target_info *info = par->targinfo;
506 + struct tcphdr _tcph, *tcph = NULL;
507 + enum ip_conntrack_info ctinfo;
508 + enum ip_conntrack_dir dir;
509 + struct nf_flow_route route = {};
510 + struct flow_offload *flow = NULL;
511 + struct net_device *devs[2] = {};
512 + struct nf_conn *ct;
513 + struct net *net;
514 +
515 + if (xt_flowoffload_skip(skb, xt_family(par)))
516 + return XT_CONTINUE;
517 +
518 + ct = nf_ct_get(skb, &ctinfo);
519 + if (ct == NULL)
520 + return XT_CONTINUE;
521 +
522 + switch (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum) {
523 + case IPPROTO_TCP:
524 + if (ct->proto.tcp.state != TCP_CONNTRACK_ESTABLISHED)
525 + return XT_CONTINUE;
526 +
527 + tcph = skb_header_pointer(skb, par->thoff,
528 + sizeof(_tcph), &_tcph);
529 + if (unlikely(!tcph || tcph->fin || tcph->rst))
530 + return XT_CONTINUE;
531 + break;
532 + case IPPROTO_UDP:
533 + break;
534 + default:
535 + return XT_CONTINUE;
536 + }
537 +
538 + if (nf_ct_ext_exist(ct, NF_CT_EXT_HELPER) ||
539 + ct->status & (IPS_SEQ_ADJUST | IPS_NAT_CLASH))
540 + return XT_CONTINUE;
541 +
542 + if (!nf_ct_is_confirmed(ct))
543 + return XT_CONTINUE;
544 +
545 + devs[dir] = xt_out(par);
546 + devs[!dir] = xt_in(par);
547 +
548 + if (!devs[dir] || !devs[!dir])
549 + return XT_CONTINUE;
550 +
551 + if (test_and_set_bit(IPS_OFFLOAD_BIT, &ct->status))
552 + return XT_CONTINUE;
553 +
554 + dir = CTINFO2DIR(ctinfo);
555 +
556 + if (xt_flowoffload_route(skb, ct, par, &route, dir, devs) < 0)
557 + goto err_flow_route;
558 +
559 + flow = flow_offload_alloc(ct);
560 + if (!flow)
561 + goto err_flow_alloc;
562 +
563 + if (flow_offload_route_init(flow, &route) < 0)
564 + goto err_flow_add;
565 +
566 + if (tcph) {
567 + ct->proto.tcp.seen[0].flags |= IP_CT_TCP_FLAG_BE_LIBERAL;
568 + ct->proto.tcp.seen[1].flags |= IP_CT_TCP_FLAG_BE_LIBERAL;
569 + }
570 +
571 + table = &flowtable[!!(info->flags & XT_FLOWOFFLOAD_HW)];
572 +
573 + net = read_pnet(&table->ft.net);
574 + if (!net)
575 + write_pnet(&table->ft.net, xt_net(par));
576 +
577 + __set_bit(NF_FLOW_HW_BIDIRECTIONAL, &flow->flags);
578 + if (flow_offload_add(&table->ft, flow) < 0)
579 + goto err_flow_add;
580 +
581 + xt_flowoffload_check_device(table, devs[0]);
582 + xt_flowoffload_check_device(table, devs[1]);
583 +
584 + dst_release(route.tuple[!dir].dst);
585 +
586 + return XT_CONTINUE;
587 +
588 +err_flow_add:
589 + flow_offload_free(flow);
590 +err_flow_alloc:
591 + dst_release(route.tuple[!dir].dst);
592 +err_flow_route:
593 + clear_bit(IPS_OFFLOAD_BIT, &ct->status);
594 +
595 + return XT_CONTINUE;
596 +}
597 +
598 +static int flowoffload_chk(const struct xt_tgchk_param *par)
599 +{
600 + struct xt_flowoffload_target_info *info = par->targinfo;
601 +
602 + if (info->flags & ~XT_FLOWOFFLOAD_MASK)
603 + return -EINVAL;
604 +
605 + return 0;
606 +}
607 +
608 +static struct xt_target offload_tg_reg __read_mostly = {
609 + .family = NFPROTO_UNSPEC,
610 + .name = "FLOWOFFLOAD",
611 + .revision = 0,
612 + .targetsize = sizeof(struct xt_flowoffload_target_info),
613 + .usersize = sizeof(struct xt_flowoffload_target_info),
614 + .checkentry = flowoffload_chk,
615 + .target = flowoffload_tg,
616 + .me = THIS_MODULE,
617 +};
618 +
619 +static int flow_offload_netdev_event(struct notifier_block *this,
620 + unsigned long event, void *ptr)
621 +{
622 + struct xt_flowoffload_hook *hook0, *hook1;
623 + struct net_device *dev = netdev_notifier_info_to_dev(ptr);
624 +
625 + if (event != NETDEV_UNREGISTER)
626 + return NOTIFY_DONE;
627 +
628 + spin_lock_bh(&hooks_lock);
629 + hook0 = flow_offload_lookup_hook(&flowtable[0], dev);
630 + if (hook0)
631 + hlist_del(&hook0->list);
632 +
633 + hook1 = flow_offload_lookup_hook(&flowtable[1], dev);
634 + if (hook1)
635 + hlist_del(&hook1->list);
636 + spin_unlock_bh(&hooks_lock);
637 +
638 + if (hook0) {
639 + nf_unregister_net_hook(hook0->net, &hook0->ops);
640 + kfree(hook0);
641 + }
642 +
643 + if (hook1) {
644 + nf_unregister_net_hook(hook1->net, &hook1->ops);
645 + kfree(hook1);
646 + }
647 +
648 + nf_flow_table_cleanup(dev);
649 +
650 + return NOTIFY_DONE;
651 +}
652 +
653 +static struct notifier_block flow_offload_netdev_notifier = {
654 + .notifier_call = flow_offload_netdev_event,
655 +};
656 +
657 +static int nf_flow_rule_route_inet(struct net *net,
658 + struct flow_offload *flow,
659 + enum flow_offload_tuple_dir dir,
660 + struct nf_flow_rule *flow_rule)
661 +{
662 + const struct flow_offload_tuple *flow_tuple = &flow->tuplehash[dir].tuple;
663 + int err;
664 +
665 + switch (flow_tuple->l3proto) {
666 + case NFPROTO_IPV4:
667 + err = nf_flow_rule_route_ipv4(net, flow, dir, flow_rule);
668 + break;
669 + case NFPROTO_IPV6:
670 + err = nf_flow_rule_route_ipv6(net, flow, dir, flow_rule);
671 + break;
672 + default:
673 + err = -1;
674 + break;
675 + }
676 +
677 + return err;
678 +}
679 +
680 +static struct nf_flowtable_type flowtable_inet = {
681 + .family = NFPROTO_INET,
682 + .init = nf_flow_table_init,
683 + .setup = nf_flow_table_offload_setup,
684 + .action = nf_flow_rule_route_inet,
685 + .free = nf_flow_table_free,
686 + .hook = xt_flowoffload_net_hook,
687 + .owner = THIS_MODULE,
688 +};
689 +
690 +static int init_flowtable(struct xt_flowoffload_table *tbl)
691 +{
692 + INIT_DELAYED_WORK(&tbl->work, xt_flowoffload_hook_work);
693 + tbl->ft.type = &flowtable_inet;
694 + tbl->ft.flags = NF_FLOWTABLE_COUNTER;
695 +
696 + return nf_flow_table_init(&tbl->ft);
697 +}
698 +
699 +static int __init xt_flowoffload_tg_init(void)
700 +{
701 + int ret;
702 +
703 + register_netdevice_notifier(&flow_offload_netdev_notifier);
704 +
705 + ret = init_flowtable(&flowtable[0]);
706 + if (ret)
707 + return ret;
708 +
709 + ret = init_flowtable(&flowtable[1]);
710 + if (ret)
711 + goto cleanup;
712 +
713 + flowtable[1].ft.flags |= NF_FLOWTABLE_HW_OFFLOAD;
714 +
715 + ret = xt_register_target(&offload_tg_reg);
716 + if (ret)
717 + goto cleanup2;
718 +
719 + return 0;
720 +
721 +cleanup2:
722 + nf_flow_table_free(&flowtable[1].ft);
723 +cleanup:
724 + nf_flow_table_free(&flowtable[0].ft);
725 + return ret;
726 +}
727 +
728 +static void __exit xt_flowoffload_tg_exit(void)
729 +{
730 + xt_unregister_target(&offload_tg_reg);
731 + unregister_netdevice_notifier(&flow_offload_netdev_notifier);
732 + nf_flow_table_free(&flowtable[0].ft);
733 + nf_flow_table_free(&flowtable[1].ft);
734 +}
735 +
736 +MODULE_LICENSE("GPL");
737 +module_init(xt_flowoffload_tg_init);
738 +module_exit(xt_flowoffload_tg_exit);
739 --- a/net/netfilter/nf_flow_table_core.c
740 +++ b/net/netfilter/nf_flow_table_core.c
741 @@ -7,7 +7,6 @@
742 #include <linux/netdevice.h>
743 #include <net/ip.h>
744 #include <net/ip6_route.h>
745 -#include <net/netfilter/nf_tables.h>
746 #include <net/netfilter/nf_flow_table.h>
747 #include <net/netfilter/nf_conntrack.h>
748 #include <net/netfilter/nf_conntrack_core.h>
749 @@ -381,8 +380,7 @@ flow_offload_lookup(struct nf_flowtable
750 }
751 EXPORT_SYMBOL_GPL(flow_offload_lookup);
752
753 -static int
754 -nf_flow_table_iterate(struct nf_flowtable *flow_table,
755 +int nf_flow_table_iterate(struct nf_flowtable *flow_table,
756 void (*iter)(struct nf_flowtable *flowtable,
757 struct flow_offload *flow, void *data),
758 void *data)
759 @@ -443,6 +441,7 @@ static void nf_flow_offload_gc_step(stru
760 nf_flow_offload_stats(flow_table, flow);
761 }
762 }
763 +EXPORT_SYMBOL_GPL(nf_flow_table_iterate);
764
765 void nf_flow_table_gc_run(struct nf_flowtable *flow_table)
766 {
767 --- /dev/null
768 +++ b/include/uapi/linux/netfilter/xt_FLOWOFFLOAD.h
769 @@ -0,0 +1,17 @@
770 +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
771 +#ifndef _XT_FLOWOFFLOAD_H
772 +#define _XT_FLOWOFFLOAD_H
773 +
774 +#include <linux/types.h>
775 +
776 +enum {
777 + XT_FLOWOFFLOAD_HW = 1 << 0,
778 +
779 + XT_FLOWOFFLOAD_MASK = XT_FLOWOFFLOAD_HW
780 +};
781 +
782 +struct xt_flowoffload_target_info {
783 + __u32 flags;
784 +};
785 +
786 +#endif /* _XT_FLOWOFFLOAD_H */
787 --- a/include/net/netfilter/nf_flow_table.h
788 +++ b/include/net/netfilter/nf_flow_table.h
789 @@ -293,6 +293,11 @@ void nf_flow_table_free(struct nf_flowta
790
791 void flow_offload_teardown(struct flow_offload *flow);
792
793 +int nf_flow_table_iterate(struct nf_flowtable *flow_table,
794 + void (*iter)(struct nf_flowtable *flowtable,
795 + struct flow_offload *flow, void *data),
796 + void *data);
797 +
798 void nf_flow_snat_port(const struct flow_offload *flow,
799 struct sk_buff *skb, unsigned int thoff,
800 u8 protocol, enum flow_offload_tuple_dir dir);