kernel: bump 6.1 to 6.1.68
[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,698 @@
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 + if (flow_offload_add(&table->ft, flow) < 0)
578 + goto err_flow_add;
579 +
580 + xt_flowoffload_check_device(table, devs[0]);
581 + xt_flowoffload_check_device(table, devs[1]);
582 +
583 + dst_release(route.tuple[!dir].dst);
584 +
585 + return XT_CONTINUE;
586 +
587 +err_flow_add:
588 + flow_offload_free(flow);
589 +err_flow_alloc:
590 + dst_release(route.tuple[!dir].dst);
591 +err_flow_route:
592 + clear_bit(IPS_OFFLOAD_BIT, &ct->status);
593 +
594 + return XT_CONTINUE;
595 +}
596 +
597 +static int flowoffload_chk(const struct xt_tgchk_param *par)
598 +{
599 + struct xt_flowoffload_target_info *info = par->targinfo;
600 +
601 + if (info->flags & ~XT_FLOWOFFLOAD_MASK)
602 + return -EINVAL;
603 +
604 + return 0;
605 +}
606 +
607 +static struct xt_target offload_tg_reg __read_mostly = {
608 + .family = NFPROTO_UNSPEC,
609 + .name = "FLOWOFFLOAD",
610 + .revision = 0,
611 + .targetsize = sizeof(struct xt_flowoffload_target_info),
612 + .usersize = sizeof(struct xt_flowoffload_target_info),
613 + .checkentry = flowoffload_chk,
614 + .target = flowoffload_tg,
615 + .me = THIS_MODULE,
616 +};
617 +
618 +static int flow_offload_netdev_event(struct notifier_block *this,
619 + unsigned long event, void *ptr)
620 +{
621 + struct xt_flowoffload_hook *hook0, *hook1;
622 + struct net_device *dev = netdev_notifier_info_to_dev(ptr);
623 +
624 + if (event != NETDEV_UNREGISTER)
625 + return NOTIFY_DONE;
626 +
627 + spin_lock_bh(&hooks_lock);
628 + hook0 = flow_offload_lookup_hook(&flowtable[0], dev);
629 + if (hook0)
630 + hlist_del(&hook0->list);
631 +
632 + hook1 = flow_offload_lookup_hook(&flowtable[1], dev);
633 + if (hook1)
634 + hlist_del(&hook1->list);
635 + spin_unlock_bh(&hooks_lock);
636 +
637 + if (hook0) {
638 + nf_unregister_net_hook(hook0->net, &hook0->ops);
639 + kfree(hook0);
640 + }
641 +
642 + if (hook1) {
643 + nf_unregister_net_hook(hook1->net, &hook1->ops);
644 + kfree(hook1);
645 + }
646 +
647 + nf_flow_table_cleanup(dev);
648 +
649 + return NOTIFY_DONE;
650 +}
651 +
652 +static struct notifier_block flow_offload_netdev_notifier = {
653 + .notifier_call = flow_offload_netdev_event,
654 +};
655 +
656 +static int nf_flow_rule_route_inet(struct net *net,
657 + const struct flow_offload *flow,
658 + enum flow_offload_tuple_dir dir,
659 + struct nf_flow_rule *flow_rule)
660 +{
661 + const struct flow_offload_tuple *flow_tuple = &flow->tuplehash[dir].tuple;
662 + int err;
663 +
664 + switch (flow_tuple->l3proto) {
665 + case NFPROTO_IPV4:
666 + err = nf_flow_rule_route_ipv4(net, flow, dir, flow_rule);
667 + break;
668 + case NFPROTO_IPV6:
669 + err = nf_flow_rule_route_ipv6(net, flow, dir, flow_rule);
670 + break;
671 + default:
672 + err = -1;
673 + break;
674 + }
675 +
676 + return err;
677 +}
678 +
679 +static struct nf_flowtable_type flowtable_inet = {
680 + .family = NFPROTO_INET,
681 + .init = nf_flow_table_init,
682 + .setup = nf_flow_table_offload_setup,
683 + .action = nf_flow_rule_route_inet,
684 + .free = nf_flow_table_free,
685 + .hook = xt_flowoffload_net_hook,
686 + .owner = THIS_MODULE,
687 +};
688 +
689 +static int init_flowtable(struct xt_flowoffload_table *tbl)
690 +{
691 + INIT_DELAYED_WORK(&tbl->work, xt_flowoffload_hook_work);
692 + tbl->ft.type = &flowtable_inet;
693 + tbl->ft.flags = NF_FLOWTABLE_COUNTER;
694 +
695 + return nf_flow_table_init(&tbl->ft);
696 +}
697 +
698 +static int __init xt_flowoffload_tg_init(void)
699 +{
700 + int ret;
701 +
702 + register_netdevice_notifier(&flow_offload_netdev_notifier);
703 +
704 + ret = init_flowtable(&flowtable[0]);
705 + if (ret)
706 + return ret;
707 +
708 + ret = init_flowtable(&flowtable[1]);
709 + if (ret)
710 + goto cleanup;
711 +
712 + flowtable[1].ft.flags |= NF_FLOWTABLE_HW_OFFLOAD;
713 +
714 + ret = xt_register_target(&offload_tg_reg);
715 + if (ret)
716 + goto cleanup2;
717 +
718 + return 0;
719 +
720 +cleanup2:
721 + nf_flow_table_free(&flowtable[1].ft);
722 +cleanup:
723 + nf_flow_table_free(&flowtable[0].ft);
724 + return ret;
725 +}
726 +
727 +static void __exit xt_flowoffload_tg_exit(void)
728 +{
729 + xt_unregister_target(&offload_tg_reg);
730 + unregister_netdevice_notifier(&flow_offload_netdev_notifier);
731 + nf_flow_table_free(&flowtable[0].ft);
732 + nf_flow_table_free(&flowtable[1].ft);
733 +}
734 +
735 +MODULE_LICENSE("GPL");
736 +module_init(xt_flowoffload_tg_init);
737 +module_exit(xt_flowoffload_tg_exit);
738 --- a/net/netfilter/nf_flow_table_core.c
739 +++ b/net/netfilter/nf_flow_table_core.c
740 @@ -7,7 +7,6 @@
741 #include <linux/netdevice.h>
742 #include <net/ip.h>
743 #include <net/ip6_route.h>
744 -#include <net/netfilter/nf_tables.h>
745 #include <net/netfilter/nf_flow_table.h>
746 #include <net/netfilter/nf_conntrack.h>
747 #include <net/netfilter/nf_conntrack_core.h>
748 @@ -381,8 +380,7 @@ flow_offload_lookup(struct nf_flowtable
749 }
750 EXPORT_SYMBOL_GPL(flow_offload_lookup);
751
752 -static int
753 -nf_flow_table_iterate(struct nf_flowtable *flow_table,
754 +int nf_flow_table_iterate(struct nf_flowtable *flow_table,
755 void (*iter)(struct nf_flowtable *flowtable,
756 struct flow_offload *flow, void *data),
757 void *data)
758 @@ -436,6 +434,7 @@ static void nf_flow_offload_gc_step(stru
759 nf_flow_offload_stats(flow_table, flow);
760 }
761 }
762 +EXPORT_SYMBOL_GPL(nf_flow_table_iterate);
763
764 void nf_flow_table_gc_run(struct nf_flowtable *flow_table)
765 {
766 --- /dev/null
767 +++ b/include/uapi/linux/netfilter/xt_FLOWOFFLOAD.h
768 @@ -0,0 +1,17 @@
769 +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
770 +#ifndef _XT_FLOWOFFLOAD_H
771 +#define _XT_FLOWOFFLOAD_H
772 +
773 +#include <linux/types.h>
774 +
775 +enum {
776 + XT_FLOWOFFLOAD_HW = 1 << 0,
777 +
778 + XT_FLOWOFFLOAD_MASK = XT_FLOWOFFLOAD_HW
779 +};
780 +
781 +struct xt_flowoffload_target_info {
782 + __u32 flags;
783 +};
784 +
785 +#endif /* _XT_FLOWOFFLOAD_H */
786 --- a/include/net/netfilter/nf_flow_table.h
787 +++ b/include/net/netfilter/nf_flow_table.h
788 @@ -280,6 +280,11 @@ void nf_flow_table_free(struct nf_flowta
789
790 void flow_offload_teardown(struct flow_offload *flow);
791
792 +int nf_flow_table_iterate(struct nf_flowtable *flow_table,
793 + void (*iter)(struct nf_flowtable *flowtable,
794 + struct flow_offload *flow, void *data),
795 + void *data);
796 +
797 void nf_flow_snat_port(const struct flow_offload *flow,
798 struct sk_buff *skb, unsigned int thoff,
799 u8 protocol, enum flow_offload_tuple_dir dir);