1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * |
4 | * Generic part shared by ipv4 and ipv6 backends. |
5 | */ |
6 | |
7 | #include <linux/kernel.h> |
8 | #include <linux/init.h> |
9 | #include <linux/module.h> |
10 | #include <linux/netlink.h> |
11 | #include <linux/netfilter.h> |
12 | #include <linux/netfilter/nf_tables.h> |
13 | #include <net/netfilter/nf_tables_core.h> |
14 | #include <net/netfilter/nf_tables.h> |
15 | #include <net/netfilter/nft_fib.h> |
16 | |
17 | #define NFTA_FIB_F_ALL (NFTA_FIB_F_SADDR | NFTA_FIB_F_DADDR | \ |
18 | NFTA_FIB_F_MARK | NFTA_FIB_F_IIF | NFTA_FIB_F_OIF | \ |
19 | NFTA_FIB_F_PRESENT) |
20 | |
21 | const struct nla_policy nft_fib_policy[NFTA_FIB_MAX + 1] = { |
22 | [NFTA_FIB_DREG] = { .type = NLA_U32 }, |
23 | [NFTA_FIB_RESULT] = { .type = NLA_U32 }, |
24 | [NFTA_FIB_FLAGS] = |
25 | NLA_POLICY_MASK(NLA_BE32, NFTA_FIB_F_ALL), |
26 | }; |
27 | EXPORT_SYMBOL(nft_fib_policy); |
28 | |
29 | int nft_fib_validate(const struct nft_ctx *ctx, const struct nft_expr *expr, |
30 | const struct nft_data **data) |
31 | { |
32 | const struct nft_fib *priv = nft_expr_priv(expr); |
33 | unsigned int hooks; |
34 | |
35 | switch (priv->result) { |
36 | case NFT_FIB_RESULT_OIF: |
37 | case NFT_FIB_RESULT_OIFNAME: |
38 | hooks = (1 << NF_INET_PRE_ROUTING); |
39 | if (priv->flags & NFTA_FIB_F_IIF) { |
40 | hooks |= (1 << NF_INET_LOCAL_IN) | |
41 | (1 << NF_INET_FORWARD); |
42 | } |
43 | break; |
44 | case NFT_FIB_RESULT_ADDRTYPE: |
45 | if (priv->flags & NFTA_FIB_F_IIF) |
46 | hooks = (1 << NF_INET_PRE_ROUTING) | |
47 | (1 << NF_INET_LOCAL_IN) | |
48 | (1 << NF_INET_FORWARD); |
49 | else if (priv->flags & NFTA_FIB_F_OIF) |
50 | hooks = (1 << NF_INET_LOCAL_OUT) | |
51 | (1 << NF_INET_POST_ROUTING) | |
52 | (1 << NF_INET_FORWARD); |
53 | else |
54 | hooks = (1 << NF_INET_LOCAL_IN) | |
55 | (1 << NF_INET_LOCAL_OUT) | |
56 | (1 << NF_INET_FORWARD) | |
57 | (1 << NF_INET_PRE_ROUTING) | |
58 | (1 << NF_INET_POST_ROUTING); |
59 | |
60 | break; |
61 | default: |
62 | return -EINVAL; |
63 | } |
64 | |
65 | return nft_chain_validate_hooks(chain: ctx->chain, hook_flags: hooks); |
66 | } |
67 | EXPORT_SYMBOL_GPL(nft_fib_validate); |
68 | |
69 | int nft_fib_init(const struct nft_ctx *ctx, const struct nft_expr *expr, |
70 | const struct nlattr * const tb[]) |
71 | { |
72 | struct nft_fib *priv = nft_expr_priv(expr); |
73 | unsigned int len; |
74 | int err; |
75 | |
76 | if (!tb[NFTA_FIB_DREG] || !tb[NFTA_FIB_RESULT] || !tb[NFTA_FIB_FLAGS]) |
77 | return -EINVAL; |
78 | |
79 | priv->flags = ntohl(nla_get_be32(tb[NFTA_FIB_FLAGS])); |
80 | |
81 | if (priv->flags == 0) |
82 | return -EINVAL; |
83 | |
84 | if ((priv->flags & (NFTA_FIB_F_SADDR | NFTA_FIB_F_DADDR)) == |
85 | (NFTA_FIB_F_SADDR | NFTA_FIB_F_DADDR)) |
86 | return -EINVAL; |
87 | if ((priv->flags & (NFTA_FIB_F_IIF | NFTA_FIB_F_OIF)) == |
88 | (NFTA_FIB_F_IIF | NFTA_FIB_F_OIF)) |
89 | return -EINVAL; |
90 | if ((priv->flags & (NFTA_FIB_F_SADDR | NFTA_FIB_F_DADDR)) == 0) |
91 | return -EINVAL; |
92 | |
93 | priv->result = ntohl(nla_get_be32(tb[NFTA_FIB_RESULT])); |
94 | |
95 | switch (priv->result) { |
96 | case NFT_FIB_RESULT_OIF: |
97 | if (priv->flags & NFTA_FIB_F_OIF) |
98 | return -EINVAL; |
99 | len = sizeof(int); |
100 | break; |
101 | case NFT_FIB_RESULT_OIFNAME: |
102 | if (priv->flags & NFTA_FIB_F_OIF) |
103 | return -EINVAL; |
104 | len = IFNAMSIZ; |
105 | break; |
106 | case NFT_FIB_RESULT_ADDRTYPE: |
107 | len = sizeof(u32); |
108 | break; |
109 | default: |
110 | return -EINVAL; |
111 | } |
112 | |
113 | err = nft_parse_register_store(ctx, attr: tb[NFTA_FIB_DREG], dreg: &priv->dreg, |
114 | NULL, type: NFT_DATA_VALUE, len); |
115 | if (err < 0) |
116 | return err; |
117 | |
118 | return 0; |
119 | } |
120 | EXPORT_SYMBOL_GPL(nft_fib_init); |
121 | |
122 | int nft_fib_dump(struct sk_buff *skb, const struct nft_expr *expr, bool reset) |
123 | { |
124 | const struct nft_fib *priv = nft_expr_priv(expr); |
125 | |
126 | if (nft_dump_register(skb, attr: NFTA_FIB_DREG, reg: priv->dreg)) |
127 | return -1; |
128 | |
129 | if (nla_put_be32(skb, attrtype: NFTA_FIB_RESULT, htonl(priv->result))) |
130 | return -1; |
131 | |
132 | if (nla_put_be32(skb, attrtype: NFTA_FIB_FLAGS, htonl(priv->flags))) |
133 | return -1; |
134 | |
135 | return 0; |
136 | } |
137 | EXPORT_SYMBOL_GPL(nft_fib_dump); |
138 | |
139 | void nft_fib_store_result(void *reg, const struct nft_fib *priv, |
140 | const struct net_device *dev) |
141 | { |
142 | u32 *dreg = reg; |
143 | int index; |
144 | |
145 | switch (priv->result) { |
146 | case NFT_FIB_RESULT_OIF: |
147 | index = dev ? dev->ifindex : 0; |
148 | if (priv->flags & NFTA_FIB_F_PRESENT) |
149 | nft_reg_store8(dreg, val: !!index); |
150 | else |
151 | *dreg = index; |
152 | |
153 | break; |
154 | case NFT_FIB_RESULT_OIFNAME: |
155 | if (priv->flags & NFTA_FIB_F_PRESENT) |
156 | nft_reg_store8(dreg, val: !!dev); |
157 | else |
158 | strscpy_pad(reg, dev ? dev->name : "" , IFNAMSIZ); |
159 | break; |
160 | default: |
161 | WARN_ON_ONCE(1); |
162 | *dreg = 0; |
163 | break; |
164 | } |
165 | } |
166 | EXPORT_SYMBOL_GPL(nft_fib_store_result); |
167 | |
168 | bool nft_fib_reduce(struct nft_regs_track *track, |
169 | const struct nft_expr *expr) |
170 | { |
171 | const struct nft_fib *priv = nft_expr_priv(expr); |
172 | unsigned int len = NFT_REG32_SIZE; |
173 | const struct nft_fib *fib; |
174 | |
175 | switch (priv->result) { |
176 | case NFT_FIB_RESULT_OIF: |
177 | break; |
178 | case NFT_FIB_RESULT_OIFNAME: |
179 | if (priv->flags & NFTA_FIB_F_PRESENT) |
180 | len = NFT_REG32_SIZE; |
181 | else |
182 | len = IFNAMSIZ; |
183 | break; |
184 | case NFT_FIB_RESULT_ADDRTYPE: |
185 | break; |
186 | default: |
187 | WARN_ON_ONCE(1); |
188 | break; |
189 | } |
190 | |
191 | if (!nft_reg_track_cmp(track, expr, dreg: priv->dreg)) { |
192 | nft_reg_track_update(track, expr, dreg: priv->dreg, len); |
193 | return false; |
194 | } |
195 | |
196 | fib = nft_expr_priv(expr: track->regs[priv->dreg].selector); |
197 | if (priv->result != fib->result || |
198 | priv->flags != fib->flags) { |
199 | nft_reg_track_update(track, expr, dreg: priv->dreg, len); |
200 | return false; |
201 | } |
202 | |
203 | if (!track->regs[priv->dreg].bitwise) |
204 | return true; |
205 | |
206 | return false; |
207 | } |
208 | EXPORT_SYMBOL_GPL(nft_fib_reduce); |
209 | |
210 | MODULE_LICENSE("GPL" ); |
211 | MODULE_DESCRIPTION("Query routing table from nftables" ); |
212 | MODULE_AUTHOR("Florian Westphal <fw@strlen.de>" ); |
213 | |