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/ipv6/nf_dup_ipv6.h> |
14 | |
15 | struct nft_dup_ipv6 { |
16 | u8 sreg_addr; |
17 | u8 sreg_dev; |
18 | }; |
19 | |
20 | static void nft_dup_ipv6_eval(const struct nft_expr *expr, |
21 | struct nft_regs *regs, |
22 | const struct nft_pktinfo *pkt) |
23 | { |
24 | struct nft_dup_ipv6 *priv = nft_expr_priv(expr); |
25 | struct in6_addr *gw = (struct in6_addr *)®s->data[priv->sreg_addr]; |
26 | int oif = priv->sreg_dev ? regs->data[priv->sreg_dev] : -1; |
27 | |
28 | nf_dup_ipv6(net: nft_net(pkt), skb: pkt->skb, hooknum: nft_hook(pkt), gw, oif); |
29 | } |
30 | |
31 | static int nft_dup_ipv6_init(const struct nft_ctx *ctx, |
32 | const struct nft_expr *expr, |
33 | const struct nlattr * const tb[]) |
34 | { |
35 | struct nft_dup_ipv6 *priv = nft_expr_priv(expr); |
36 | int err; |
37 | |
38 | if (tb[NFTA_DUP_SREG_ADDR] == NULL) |
39 | return -EINVAL; |
40 | |
41 | err = nft_parse_register_load(attr: tb[NFTA_DUP_SREG_ADDR], sreg: &priv->sreg_addr, |
42 | len: sizeof(struct in6_addr)); |
43 | if (err < 0) |
44 | return err; |
45 | |
46 | if (tb[NFTA_DUP_SREG_DEV]) |
47 | err = nft_parse_register_load(attr: tb[NFTA_DUP_SREG_DEV], |
48 | sreg: &priv->sreg_dev, len: sizeof(int)); |
49 | |
50 | return err; |
51 | } |
52 | |
53 | static int nft_dup_ipv6_dump(struct sk_buff *skb, |
54 | const struct nft_expr *expr, bool reset) |
55 | { |
56 | struct nft_dup_ipv6 *priv = nft_expr_priv(expr); |
57 | |
58 | if (nft_dump_register(skb, attr: NFTA_DUP_SREG_ADDR, reg: priv->sreg_addr)) |
59 | goto nla_put_failure; |
60 | if (priv->sreg_dev && |
61 | nft_dump_register(skb, attr: NFTA_DUP_SREG_DEV, reg: priv->sreg_dev)) |
62 | goto nla_put_failure; |
63 | |
64 | return 0; |
65 | |
66 | nla_put_failure: |
67 | return -1; |
68 | } |
69 | |
70 | static struct nft_expr_type nft_dup_ipv6_type; |
71 | static const struct nft_expr_ops nft_dup_ipv6_ops = { |
72 | .type = &nft_dup_ipv6_type, |
73 | .size = NFT_EXPR_SIZE(sizeof(struct nft_dup_ipv6)), |
74 | .eval = nft_dup_ipv6_eval, |
75 | .init = nft_dup_ipv6_init, |
76 | .dump = nft_dup_ipv6_dump, |
77 | .reduce = NFT_REDUCE_READONLY, |
78 | }; |
79 | |
80 | static const struct nla_policy nft_dup_ipv6_policy[NFTA_DUP_MAX + 1] = { |
81 | [NFTA_DUP_SREG_ADDR] = { .type = NLA_U32 }, |
82 | [NFTA_DUP_SREG_DEV] = { .type = NLA_U32 }, |
83 | }; |
84 | |
85 | static struct nft_expr_type nft_dup_ipv6_type __read_mostly = { |
86 | .family = NFPROTO_IPV6, |
87 | .name = "dup" , |
88 | .ops = &nft_dup_ipv6_ops, |
89 | .policy = nft_dup_ipv6_policy, |
90 | .maxattr = NFTA_DUP_MAX, |
91 | .owner = THIS_MODULE, |
92 | }; |
93 | |
94 | static int __init nft_dup_ipv6_module_init(void) |
95 | { |
96 | return nft_register_expr(&nft_dup_ipv6_type); |
97 | } |
98 | |
99 | static void __exit nft_dup_ipv6_module_exit(void) |
100 | { |
101 | nft_unregister_expr(&nft_dup_ipv6_type); |
102 | } |
103 | |
104 | module_init(nft_dup_ipv6_module_init); |
105 | module_exit(nft_dup_ipv6_module_exit); |
106 | |
107 | MODULE_LICENSE("GPL" ); |
108 | MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>" ); |
109 | MODULE_ALIAS_NFT_AF_EXPR(AF_INET6, "dup" ); |
110 | MODULE_DESCRIPTION("IPv6 nftables packet duplication support" ); |
111 | |