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