1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2015 Pablo Neira Ayuso <pablo@netfilter.org> |
4 | */ |
5 | |
6 | #include <linux/kernel.h> |
7 | #include <linux/init.h> |
8 | #include <linux/module.h> |
9 | #include <linux/netlink.h> |
10 | #include <linux/netfilter.h> |
11 | #include <linux/netfilter/nf_tables.h> |
12 | #include <net/netfilter/nf_tables.h> |
13 | #include <net/netfilter/nf_tables_offload.h> |
14 | #include <net/netfilter/nf_dup_netdev.h> |
15 | |
16 | #define NF_RECURSION_LIMIT 2 |
17 | |
18 | static DEFINE_PER_CPU(u8, nf_dup_skb_recursion); |
19 | |
20 | static void nf_do_netdev_egress(struct sk_buff *skb, struct net_device *dev, |
21 | enum nf_dev_hooks hook) |
22 | { |
23 | if (__this_cpu_read(nf_dup_skb_recursion) > NF_RECURSION_LIMIT) |
24 | goto err; |
25 | |
26 | if (hook == NF_NETDEV_INGRESS && skb_mac_header_was_set(skb)) { |
27 | if (skb_cow_head(skb, headroom: skb->mac_len)) |
28 | goto err; |
29 | |
30 | skb_push(skb, len: skb->mac_len); |
31 | } |
32 | |
33 | skb->dev = dev; |
34 | skb_clear_tstamp(skb); |
35 | __this_cpu_inc(nf_dup_skb_recursion); |
36 | dev_queue_xmit(skb); |
37 | __this_cpu_dec(nf_dup_skb_recursion); |
38 | return; |
39 | err: |
40 | kfree_skb(skb); |
41 | } |
42 | |
43 | void nf_fwd_netdev_egress(const struct nft_pktinfo *pkt, int oif) |
44 | { |
45 | struct net_device *dev; |
46 | |
47 | dev = dev_get_by_index_rcu(net: nft_net(pkt), ifindex: oif); |
48 | if (!dev) { |
49 | kfree_skb(skb: pkt->skb); |
50 | return; |
51 | } |
52 | |
53 | nf_do_netdev_egress(skb: pkt->skb, dev, hook: nft_hook(pkt)); |
54 | } |
55 | EXPORT_SYMBOL_GPL(nf_fwd_netdev_egress); |
56 | |
57 | void nf_dup_netdev_egress(const struct nft_pktinfo *pkt, int oif) |
58 | { |
59 | struct net_device *dev; |
60 | struct sk_buff *skb; |
61 | |
62 | dev = dev_get_by_index_rcu(net: nft_net(pkt), ifindex: oif); |
63 | if (dev == NULL) |
64 | return; |
65 | |
66 | skb = skb_clone(skb: pkt->skb, GFP_ATOMIC); |
67 | if (skb) |
68 | nf_do_netdev_egress(skb, dev, hook: nft_hook(pkt)); |
69 | } |
70 | EXPORT_SYMBOL_GPL(nf_dup_netdev_egress); |
71 | |
72 | int nft_fwd_dup_netdev_offload(struct nft_offload_ctx *ctx, |
73 | struct nft_flow_rule *flow, |
74 | enum flow_action_id id, int oif) |
75 | { |
76 | struct flow_action_entry *entry; |
77 | struct net_device *dev; |
78 | |
79 | /* nft_flow_rule_destroy() releases the reference on this device. */ |
80 | dev = dev_get_by_index(net: ctx->net, ifindex: oif); |
81 | if (!dev) |
82 | return -EOPNOTSUPP; |
83 | |
84 | entry = &flow->rule->action.entries[ctx->num_actions++]; |
85 | entry->id = id; |
86 | entry->dev = dev; |
87 | |
88 | return 0; |
89 | } |
90 | EXPORT_SYMBOL_GPL(nft_fwd_dup_netdev_offload); |
91 | |
92 | MODULE_LICENSE("GPL" ); |
93 | MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>" ); |
94 | MODULE_DESCRIPTION("Netfilter packet duplication support" ); |
95 | |