xref: /openbmc/linux/net/netfilter/nft_fib.c (revision ae9e39a2)
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 			(1 << NF_INET_LOCAL_IN) |
40 			(1 << NF_INET_FORWARD);
41 		break;
42 	case NFT_FIB_RESULT_ADDRTYPE:
43 		if (priv->flags & NFTA_FIB_F_IIF)
44 			hooks = (1 << NF_INET_PRE_ROUTING) |
45 				(1 << NF_INET_LOCAL_IN) |
46 				(1 << NF_INET_FORWARD);
47 		else if (priv->flags & NFTA_FIB_F_OIF)
48 			hooks = (1 << NF_INET_LOCAL_OUT) |
49 				(1 << NF_INET_POST_ROUTING) |
50 				(1 << NF_INET_FORWARD);
51 		else
52 			hooks = (1 << NF_INET_LOCAL_IN) |
53 				(1 << NF_INET_LOCAL_OUT) |
54 				(1 << NF_INET_FORWARD) |
55 				(1 << NF_INET_PRE_ROUTING) |
56 				(1 << NF_INET_POST_ROUTING);
57 
58 		break;
59 	default:
60 		return -EINVAL;
61 	}
62 
63 	return nft_chain_validate_hooks(ctx->chain, hooks);
64 }
65 EXPORT_SYMBOL_GPL(nft_fib_validate);
66 
67 int nft_fib_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
68 		 const struct nlattr * const tb[])
69 {
70 	struct nft_fib *priv = nft_expr_priv(expr);
71 	unsigned int len;
72 	int err;
73 
74 	if (!tb[NFTA_FIB_DREG] || !tb[NFTA_FIB_RESULT] || !tb[NFTA_FIB_FLAGS])
75 		return -EINVAL;
76 
77 	priv->flags = ntohl(nla_get_be32(tb[NFTA_FIB_FLAGS]));
78 
79 	if (priv->flags == 0)
80 		return -EINVAL;
81 
82 	if ((priv->flags & (NFTA_FIB_F_SADDR | NFTA_FIB_F_DADDR)) ==
83 			   (NFTA_FIB_F_SADDR | NFTA_FIB_F_DADDR))
84 		return -EINVAL;
85 	if ((priv->flags & (NFTA_FIB_F_IIF | NFTA_FIB_F_OIF)) ==
86 			   (NFTA_FIB_F_IIF | NFTA_FIB_F_OIF))
87 		return -EINVAL;
88 	if ((priv->flags & (NFTA_FIB_F_SADDR | NFTA_FIB_F_DADDR)) == 0)
89 		return -EINVAL;
90 
91 	priv->result = ntohl(nla_get_be32(tb[NFTA_FIB_RESULT]));
92 
93 	switch (priv->result) {
94 	case NFT_FIB_RESULT_OIF:
95 		if (priv->flags & NFTA_FIB_F_OIF)
96 			return -EINVAL;
97 		len = sizeof(int);
98 		break;
99 	case NFT_FIB_RESULT_OIFNAME:
100 		if (priv->flags & NFTA_FIB_F_OIF)
101 			return -EINVAL;
102 		len = IFNAMSIZ;
103 		break;
104 	case NFT_FIB_RESULT_ADDRTYPE:
105 		len = sizeof(u32);
106 		break;
107 	default:
108 		return -EINVAL;
109 	}
110 
111 	err = nft_parse_register_store(ctx, tb[NFTA_FIB_DREG], &priv->dreg,
112 				       NULL, NFT_DATA_VALUE, len);
113 	if (err < 0)
114 		return err;
115 
116 	return 0;
117 }
118 EXPORT_SYMBOL_GPL(nft_fib_init);
119 
120 int nft_fib_dump(struct sk_buff *skb, const struct nft_expr *expr, bool reset)
121 {
122 	const struct nft_fib *priv = nft_expr_priv(expr);
123 
124 	if (nft_dump_register(skb, NFTA_FIB_DREG, priv->dreg))
125 		return -1;
126 
127 	if (nla_put_be32(skb, NFTA_FIB_RESULT, htonl(priv->result)))
128 		return -1;
129 
130 	if (nla_put_be32(skb, NFTA_FIB_FLAGS, htonl(priv->flags)))
131 		return -1;
132 
133 	return 0;
134 }
135 EXPORT_SYMBOL_GPL(nft_fib_dump);
136 
137 void nft_fib_store_result(void *reg, const struct nft_fib *priv,
138 			  const struct net_device *dev)
139 {
140 	u32 *dreg = reg;
141 	int index;
142 
143 	switch (priv->result) {
144 	case NFT_FIB_RESULT_OIF:
145 		index = dev ? dev->ifindex : 0;
146 		if (priv->flags & NFTA_FIB_F_PRESENT)
147 			nft_reg_store8(dreg, !!index);
148 		else
149 			*dreg = index;
150 
151 		break;
152 	case NFT_FIB_RESULT_OIFNAME:
153 		if (priv->flags & NFTA_FIB_F_PRESENT)
154 			nft_reg_store8(dreg, !!dev);
155 		else
156 			strscpy_pad(reg, dev ? dev->name : "", IFNAMSIZ);
157 		break;
158 	default:
159 		WARN_ON_ONCE(1);
160 		*dreg = 0;
161 		break;
162 	}
163 }
164 EXPORT_SYMBOL_GPL(nft_fib_store_result);
165 
166 bool nft_fib_reduce(struct nft_regs_track *track,
167 		    const struct nft_expr *expr)
168 {
169 	const struct nft_fib *priv = nft_expr_priv(expr);
170 	unsigned int len = NFT_REG32_SIZE;
171 	const struct nft_fib *fib;
172 
173 	switch (priv->result) {
174 	case NFT_FIB_RESULT_OIF:
175 		break;
176 	case NFT_FIB_RESULT_OIFNAME:
177 		if (priv->flags & NFTA_FIB_F_PRESENT)
178 			len = NFT_REG32_SIZE;
179 		else
180 			len = IFNAMSIZ;
181 		break;
182 	case NFT_FIB_RESULT_ADDRTYPE:
183 	     break;
184 	default:
185 		WARN_ON_ONCE(1);
186 		break;
187 	}
188 
189 	if (!nft_reg_track_cmp(track, expr, priv->dreg)) {
190 		nft_reg_track_update(track, expr, priv->dreg, len);
191 		return false;
192 	}
193 
194 	fib = nft_expr_priv(track->regs[priv->dreg].selector);
195 	if (priv->result != fib->result ||
196 	    priv->flags != fib->flags) {
197 		nft_reg_track_update(track, expr, priv->dreg, len);
198 		return false;
199 	}
200 
201 	if (!track->regs[priv->dreg].bitwise)
202 		return true;
203 
204 	return false;
205 }
206 EXPORT_SYMBOL_GPL(nft_fib_reduce);
207 
208 MODULE_LICENSE("GPL");
209 MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
210