1// SPDX-License-Identifier: GPL-2.0
2#include <linux/kernel.h>
3#include <linux/init.h>
4#include <linux/module.h>
5#include <linux/netlink.h>
6#include <linux/netfilter.h>
7#include <linux/netfilter/nf_tables.h>
8#include <net/netfilter/nf_tables.h>
9#include <net/netfilter/nft_meta.h>
10#include <linux/if_bridge.h>
11#include <uapi/linux/netfilter_bridge.h> /* NF_BR_PRE_ROUTING */
12
13#include "../br_private.h"
14
15static const struct net_device *
16nft_meta_get_bridge(const struct net_device *dev)
17{
18	if (dev && netif_is_bridge_port(dev))
19		return netdev_master_upper_dev_get_rcu((struct net_device *)dev);
20
21	return NULL;
22}
23
24static void nft_meta_bridge_get_eval(const struct nft_expr *expr,
25				     struct nft_regs *regs,
26				     const struct nft_pktinfo *pkt)
27{
28	const struct nft_meta *priv = nft_expr_priv(expr);
29	const struct net_device *in = nft_in(pkt), *out = nft_out(pkt);
30	u32 *dest = &regs->data[priv->dreg];
31	const struct net_device *br_dev;
32
33	switch (priv->key) {
34	case NFT_META_BRI_IIFNAME:
35		br_dev = nft_meta_get_bridge(in);
36		break;
37	case NFT_META_BRI_OIFNAME:
38		br_dev = nft_meta_get_bridge(out);
39		break;
40	case NFT_META_BRI_IIFPVID: {
41		u16 p_pvid;
42
43		br_dev = nft_meta_get_bridge(in);
44		if (!br_dev || !br_vlan_enabled(br_dev))
45			goto err;
46
47		br_vlan_get_pvid_rcu(in, &p_pvid);
48		nft_reg_store16(dest, p_pvid);
49		return;
50	}
51	case NFT_META_BRI_IIFVPROTO: {
52		u16 p_proto;
53
54		br_dev = nft_meta_get_bridge(in);
55		if (!br_dev || !br_vlan_enabled(br_dev))
56			goto err;
57
58		br_vlan_get_proto(br_dev, &p_proto);
59		nft_reg_store_be16(dest, htons(p_proto));
60		return;
61	}
62	default:
63		return nft_meta_get_eval(expr, regs, pkt);
64	}
65
66	strncpy((char *)dest, br_dev ? br_dev->name : "", IFNAMSIZ);
67	return;
68err:
69	regs->verdict.code = NFT_BREAK;
70}
71
72static int nft_meta_bridge_get_init(const struct nft_ctx *ctx,
73				    const struct nft_expr *expr,
74				    const struct nlattr * const tb[])
75{
76	struct nft_meta *priv = nft_expr_priv(expr);
77	unsigned int len;
78
79	priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY]));
80	switch (priv->key) {
81	case NFT_META_BRI_IIFNAME:
82	case NFT_META_BRI_OIFNAME:
83		len = IFNAMSIZ;
84		break;
85	case NFT_META_BRI_IIFPVID:
86	case NFT_META_BRI_IIFVPROTO:
87		len = sizeof(u16);
88		break;
89	default:
90		return nft_meta_get_init(ctx, expr, tb);
91	}
92
93	priv->len = len;
94	return nft_parse_register_store(ctx, tb[NFTA_META_DREG], &priv->dreg,
95					NULL, NFT_DATA_VALUE, len);
96}
97
98static struct nft_expr_type nft_meta_bridge_type;
99static const struct nft_expr_ops nft_meta_bridge_get_ops = {
100	.type		= &nft_meta_bridge_type,
101	.size		= NFT_EXPR_SIZE(sizeof(struct nft_meta)),
102	.eval		= nft_meta_bridge_get_eval,
103	.init		= nft_meta_bridge_get_init,
104	.dump		= nft_meta_get_dump,
105	.reduce		= nft_meta_get_reduce,
106};
107
108static void nft_meta_bridge_set_eval(const struct nft_expr *expr,
109				     struct nft_regs *regs,
110				     const struct nft_pktinfo *pkt)
111{
112	const struct nft_meta *meta = nft_expr_priv(expr);
113	u32 *sreg = &regs->data[meta->sreg];
114	struct sk_buff *skb = pkt->skb;
115	u8 value8;
116
117	switch (meta->key) {
118	case NFT_META_BRI_BROUTE:
119		value8 = nft_reg_load8(sreg);
120		BR_INPUT_SKB_CB(skb)->br_netfilter_broute = !!value8;
121		break;
122	default:
123		nft_meta_set_eval(expr, regs, pkt);
124	}
125}
126
127static int nft_meta_bridge_set_init(const struct nft_ctx *ctx,
128				    const struct nft_expr *expr,
129				    const struct nlattr * const tb[])
130{
131	struct nft_meta *priv = nft_expr_priv(expr);
132	unsigned int len;
133	int err;
134
135	priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY]));
136	switch (priv->key) {
137	case NFT_META_BRI_BROUTE:
138		len = sizeof(u8);
139		break;
140	default:
141		return nft_meta_set_init(ctx, expr, tb);
142	}
143
144	priv->len = len;
145	err = nft_parse_register_load(tb[NFTA_META_SREG], &priv->sreg, len);
146	if (err < 0)
147		return err;
148
149	return 0;
150}
151
152static bool nft_meta_bridge_set_reduce(struct nft_regs_track *track,
153				       const struct nft_expr *expr)
154{
155	int i;
156
157	for (i = 0; i < NFT_REG32_NUM; i++) {
158		if (!track->regs[i].selector)
159			continue;
160
161		if (track->regs[i].selector->ops != &nft_meta_bridge_get_ops)
162			continue;
163
164		__nft_reg_track_cancel(track, i);
165	}
166
167	return false;
168}
169
170static int nft_meta_bridge_set_validate(const struct nft_ctx *ctx,
171					const struct nft_expr *expr,
172					const struct nft_data **data)
173{
174	struct nft_meta *priv = nft_expr_priv(expr);
175	unsigned int hooks;
176
177	switch (priv->key) {
178	case NFT_META_BRI_BROUTE:
179		hooks = 1 << NF_BR_PRE_ROUTING;
180		break;
181	default:
182		return nft_meta_set_validate(ctx, expr, data);
183	}
184
185	return nft_chain_validate_hooks(ctx->chain, hooks);
186}
187
188static const struct nft_expr_ops nft_meta_bridge_set_ops = {
189	.type		= &nft_meta_bridge_type,
190	.size		= NFT_EXPR_SIZE(sizeof(struct nft_meta)),
191	.eval		= nft_meta_bridge_set_eval,
192	.init		= nft_meta_bridge_set_init,
193	.destroy	= nft_meta_set_destroy,
194	.dump		= nft_meta_set_dump,
195	.reduce		= nft_meta_bridge_set_reduce,
196	.validate	= nft_meta_bridge_set_validate,
197};
198
199static const struct nft_expr_ops *
200nft_meta_bridge_select_ops(const struct nft_ctx *ctx,
201			   const struct nlattr * const tb[])
202{
203	if (tb[NFTA_META_KEY] == NULL)
204		return ERR_PTR(-EINVAL);
205
206	if (tb[NFTA_META_DREG] && tb[NFTA_META_SREG])
207		return ERR_PTR(-EINVAL);
208
209	if (tb[NFTA_META_DREG])
210		return &nft_meta_bridge_get_ops;
211
212	if (tb[NFTA_META_SREG])
213		return &nft_meta_bridge_set_ops;
214
215	return ERR_PTR(-EINVAL);
216}
217
218static struct nft_expr_type nft_meta_bridge_type __read_mostly = {
219	.family         = NFPROTO_BRIDGE,
220	.name           = "meta",
221	.select_ops     = nft_meta_bridge_select_ops,
222	.policy         = nft_meta_policy,
223	.maxattr        = NFTA_META_MAX,
224	.owner          = THIS_MODULE,
225};
226
227static int __init nft_meta_bridge_module_init(void)
228{
229	return nft_register_expr(&nft_meta_bridge_type);
230}
231
232static void __exit nft_meta_bridge_module_exit(void)
233{
234	nft_unregister_expr(&nft_meta_bridge_type);
235}
236
237module_init(nft_meta_bridge_module_init);
238module_exit(nft_meta_bridge_module_exit);
239
240MODULE_LICENSE("GPL");
241MODULE_AUTHOR("wenxu <wenxu@ucloud.cn>");
242MODULE_ALIAS_NFT_AF_EXPR(AF_BRIDGE, "meta");
243MODULE_DESCRIPTION("Support for bridge dedicated meta key");
244