kernel: bump 6.1 to 6.1.80
[openwrt/staging/nbd.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,702 @@
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 + if (!dst_hold_safe(this_dst))
486 + return -ENOENT;
487 +
488 + nf_route(xt_net(par), &other_dst, &fl, false, xt_family(par));
489 + if (!other_dst) {
490 + dst_release(this_dst);
491 + return -ENOENT;
492 + }
493 +
494 + nf_default_forward_path(route, this_dst, dir, devs);
495 + nf_default_forward_path(route, other_dst, !dir, devs);
496 +
497 + if (route->tuple[dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH &&
498 + route->tuple[!dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH) {
499 + nf_dev_forward_path(route, ct, dir, devs);
500 + nf_dev_forward_path(route, ct, !dir, devs);
501 + }
502 +
503 + return 0;
504 +}
505 +
506 +static unsigned int
507 +flowoffload_tg(struct sk_buff *skb, const struct xt_action_param *par)
508 +{
509 + struct xt_flowoffload_table *table;
510 + const struct xt_flowoffload_target_info *info = par->targinfo;
511 + struct tcphdr _tcph, *tcph = NULL;
512 + enum ip_conntrack_info ctinfo;
513 + enum ip_conntrack_dir dir;
514 + struct nf_flow_route route = {};
515 + struct flow_offload *flow = NULL;
516 + struct net_device *devs[2] = {};
517 + struct nf_conn *ct;
518 + struct net *net;
519 +
520 + if (xt_flowoffload_skip(skb, xt_family(par)))
521 + return XT_CONTINUE;
522 +
523 + ct = nf_ct_get(skb, &ctinfo);
524 + if (ct == NULL)
525 + return XT_CONTINUE;
526 +
527 + switch (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum) {
528 + case IPPROTO_TCP:
529 + if (ct->proto.tcp.state != TCP_CONNTRACK_ESTABLISHED)
530 + return XT_CONTINUE;
531 +
532 + tcph = skb_header_pointer(skb, par->thoff,
533 + sizeof(_tcph), &_tcph);
534 + if (unlikely(!tcph || tcph->fin || tcph->rst))
535 + return XT_CONTINUE;
536 + break;
537 + case IPPROTO_UDP:
538 + break;
539 + default:
540 + return XT_CONTINUE;
541 + }
542 +
543 + if (nf_ct_ext_exist(ct, NF_CT_EXT_HELPER) ||
544 + ct->status & (IPS_SEQ_ADJUST | IPS_NAT_CLASH))
545 + return XT_CONTINUE;
546 +
547 + if (!nf_ct_is_confirmed(ct))
548 + return XT_CONTINUE;
549 +
550 + devs[dir] = xt_out(par);
551 + devs[!dir] = xt_in(par);
552 +
553 + if (!devs[dir] || !devs[!dir])
554 + return XT_CONTINUE;
555 +
556 + if (test_and_set_bit(IPS_OFFLOAD_BIT, &ct->status))
557 + return XT_CONTINUE;
558 +
559 + dir = CTINFO2DIR(ctinfo);
560 +
561 + if (xt_flowoffload_route(skb, ct, par, &route, dir, devs) < 0)
562 + goto err_flow_route;
563 +
564 + flow = flow_offload_alloc(ct);
565 + if (!flow)
566 + goto err_flow_alloc;
567 +
568 + flow_offload_route_init(flow, &route);
569 +
570 + if (tcph) {
571 + ct->proto.tcp.seen[0].flags |= IP_CT_TCP_FLAG_BE_LIBERAL;
572 + ct->proto.tcp.seen[1].flags |= IP_CT_TCP_FLAG_BE_LIBERAL;
573 + }
574 +
575 + table = &flowtable[!!(info->flags & XT_FLOWOFFLOAD_HW)];
576 +
577 + net = read_pnet(&table->ft.net);
578 + if (!net)
579 + write_pnet(&table->ft.net, xt_net(par));
580 +
581 + __set_bit(NF_FLOW_HW_BIDIRECTIONAL, &flow->flags);
582 + if (flow_offload_add(&table->ft, flow) < 0)
583 + goto err_flow_add;
584 +
585 + xt_flowoffload_check_device(table, devs[0]);
586 + xt_flowoffload_check_device(table, devs[1]);
587 +
588 + return XT_CONTINUE;
589 +
590 +err_flow_add:
591 + flow_offload_free(flow);
592 +err_flow_alloc:
593 + dst_release(route.tuple[dir].dst);
594 + dst_release(route.tuple[!dir].dst);
595 +err_flow_route:
596 + clear_bit(IPS_OFFLOAD_BIT, &ct->status);
597 +
598 + return XT_CONTINUE;
599 +}
600 +
601 +static int flowoffload_chk(const struct xt_tgchk_param *par)
602 +{
603 + struct xt_flowoffload_target_info *info = par->targinfo;
604 +
605 + if (info->flags & ~XT_FLOWOFFLOAD_MASK)
606 + return -EINVAL;
607 +
608 + return 0;
609 +}
610 +
611 +static struct xt_target offload_tg_reg __read_mostly = {
612 + .family = NFPROTO_UNSPEC,
613 + .name = "FLOWOFFLOAD",
614 + .revision = 0,
615 + .targetsize = sizeof(struct xt_flowoffload_target_info),
616 + .usersize = sizeof(struct xt_flowoffload_target_info),
617 + .checkentry = flowoffload_chk,
618 + .target = flowoffload_tg,
619 + .me = THIS_MODULE,
620 +};
621 +
622 +static int flow_offload_netdev_event(struct notifier_block *this,
623 + unsigned long event, void *ptr)
624 +{
625 + struct xt_flowoffload_hook *hook0, *hook1;
626 + struct net_device *dev = netdev_notifier_info_to_dev(ptr);
627 +
628 + if (event != NETDEV_UNREGISTER)
629 + return NOTIFY_DONE;
630 +
631 + spin_lock_bh(&hooks_lock);
632 + hook0 = flow_offload_lookup_hook(&flowtable[0], dev);
633 + if (hook0)
634 + hlist_del(&hook0->list);
635 +
636 + hook1 = flow_offload_lookup_hook(&flowtable[1], dev);
637 + if (hook1)
638 + hlist_del(&hook1->list);
639 + spin_unlock_bh(&hooks_lock);
640 +
641 + if (hook0) {
642 + nf_unregister_net_hook(hook0->net, &hook0->ops);
643 + kfree(hook0);
644 + }
645 +
646 + if (hook1) {
647 + nf_unregister_net_hook(hook1->net, &hook1->ops);
648 + kfree(hook1);
649 + }
650 +
651 + nf_flow_table_cleanup(dev);
652 +
653 + return NOTIFY_DONE;
654 +}
655 +
656 +static struct notifier_block flow_offload_netdev_notifier = {
657 + .notifier_call = flow_offload_netdev_event,
658 +};
659 +
660 +static int nf_flow_rule_route_inet(struct net *net,
661 + struct flow_offload *flow,
662 + enum flow_offload_tuple_dir dir,
663 + struct nf_flow_rule *flow_rule)
664 +{
665 + const struct flow_offload_tuple *flow_tuple = &flow->tuplehash[dir].tuple;
666 + int err;
667 +
668 + switch (flow_tuple->l3proto) {
669 + case NFPROTO_IPV4:
670 + err = nf_flow_rule_route_ipv4(net, flow, dir, flow_rule);
671 + break;
672 + case NFPROTO_IPV6:
673 + err = nf_flow_rule_route_ipv6(net, flow, dir, flow_rule);
674 + break;
675 + default:
676 + err = -1;
677 + break;
678 + }
679 +
680 + return err;
681 +}
682 +
683 +static struct nf_flowtable_type flowtable_inet = {
684 + .family = NFPROTO_INET,
685 + .init = nf_flow_table_init,
686 + .setup = nf_flow_table_offload_setup,
687 + .action = nf_flow_rule_route_inet,
688 + .free = nf_flow_table_free,
689 + .hook = xt_flowoffload_net_hook,
690 + .owner = THIS_MODULE,
691 +};
692 +
693 +static int init_flowtable(struct xt_flowoffload_table *tbl)
694 +{
695 + INIT_DELAYED_WORK(&tbl->work, xt_flowoffload_hook_work);
696 + tbl->ft.type = &flowtable_inet;
697 + tbl->ft.flags = NF_FLOWTABLE_COUNTER;
698 +
699 + return nf_flow_table_init(&tbl->ft);
700 +}
701 +
702 +static int __init xt_flowoffload_tg_init(void)
703 +{
704 + int ret;
705 +
706 + register_netdevice_notifier(&flow_offload_netdev_notifier);
707 +
708 + ret = init_flowtable(&flowtable[0]);
709 + if (ret)
710 + return ret;
711 +
712 + ret = init_flowtable(&flowtable[1]);
713 + if (ret)
714 + goto cleanup;
715 +
716 + flowtable[1].ft.flags |= NF_FLOWTABLE_HW_OFFLOAD;
717 +
718 + ret = xt_register_target(&offload_tg_reg);
719 + if (ret)
720 + goto cleanup2;
721 +
722 + return 0;
723 +
724 +cleanup2:
725 + nf_flow_table_free(&flowtable[1].ft);
726 +cleanup:
727 + nf_flow_table_free(&flowtable[0].ft);
728 + return ret;
729 +}
730 +
731 +static void __exit xt_flowoffload_tg_exit(void)
732 +{
733 + xt_unregister_target(&offload_tg_reg);
734 + unregister_netdevice_notifier(&flow_offload_netdev_notifier);
735 + nf_flow_table_free(&flowtable[0].ft);
736 + nf_flow_table_free(&flowtable[1].ft);
737 +}
738 +
739 +MODULE_LICENSE("GPL");
740 +module_init(xt_flowoffload_tg_init);
741 +module_exit(xt_flowoffload_tg_exit);
742 --- a/net/netfilter/nf_flow_table_core.c
743 +++ b/net/netfilter/nf_flow_table_core.c
744 @@ -7,7 +7,6 @@
745 #include <linux/netdevice.h>
746 #include <net/ip.h>
747 #include <net/ip6_route.h>
748 -#include <net/netfilter/nf_tables.h>
749 #include <net/netfilter/nf_flow_table.h>
750 #include <net/netfilter/nf_conntrack.h>
751 #include <net/netfilter/nf_conntrack_core.h>
752 @@ -374,8 +373,7 @@ flow_offload_lookup(struct nf_flowtable
753 }
754 EXPORT_SYMBOL_GPL(flow_offload_lookup);
755
756 -static int
757 -nf_flow_table_iterate(struct nf_flowtable *flow_table,
758 +int nf_flow_table_iterate(struct nf_flowtable *flow_table,
759 void (*iter)(struct nf_flowtable *flowtable,
760 struct flow_offload *flow, void *data),
761 void *data)
762 @@ -436,6 +434,7 @@ static void nf_flow_offload_gc_step(stru
763 nf_flow_offload_stats(flow_table, flow);
764 }
765 }
766 +EXPORT_SYMBOL_GPL(nf_flow_table_iterate);
767
768 void nf_flow_table_gc_run(struct nf_flowtable *flow_table)
769 {
770 --- /dev/null
771 +++ b/include/uapi/linux/netfilter/xt_FLOWOFFLOAD.h
772 @@ -0,0 +1,17 @@
773 +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
774 +#ifndef _XT_FLOWOFFLOAD_H
775 +#define _XT_FLOWOFFLOAD_H
776 +
777 +#include <linux/types.h>
778 +
779 +enum {
780 + XT_FLOWOFFLOAD_HW = 1 << 0,
781 +
782 + XT_FLOWOFFLOAD_MASK = XT_FLOWOFFLOAD_HW
783 +};
784 +
785 +struct xt_flowoffload_target_info {
786 + __u32 flags;
787 +};
788 +
789 +#endif /* _XT_FLOWOFFLOAD_H */
790 --- a/include/net/netfilter/nf_flow_table.h
791 +++ b/include/net/netfilter/nf_flow_table.h
792 @@ -293,6 +293,11 @@ void nf_flow_table_free(struct nf_flowta
793
794 void flow_offload_teardown(struct flow_offload *flow);
795
796 +int nf_flow_table_iterate(struct nf_flowtable *flow_table,
797 + void (*iter)(struct nf_flowtable *flowtable,
798 + struct flow_offload *flow, void *data),
799 + void *data);
800 +
801 void nf_flow_snat_port(const struct flow_offload *flow,
802 struct sk_buff *skb, unsigned int thoff,
803 u8 protocol, enum flow_offload_tuple_dir dir);