130e103feSwenxu // SPDX-License-Identifier: GPL-2.0
230e103feSwenxu #include <linux/kernel.h>
330e103feSwenxu #include <linux/init.h>
430e103feSwenxu #include <linux/module.h>
530e103feSwenxu #include <linux/netlink.h>
630e103feSwenxu #include <linux/netfilter.h>
730e103feSwenxu #include <linux/netfilter/nf_tables.h>
830e103feSwenxu #include <net/netfilter/nf_tables.h>
930e103feSwenxu #include <net/netfilter/nft_meta.h>
10c54c7c68Swenxu #include <linux/if_bridge.h>
11*4386b921SSriram Yagnaraman #include <uapi/linux/netfilter_bridge.h> /* NF_BR_PRE_ROUTING */
12*4386b921SSriram Yagnaraman 
13*4386b921SSriram Yagnaraman #include "../br_private.h"
1430e103feSwenxu 
159d6a1ecdSwenxu static const struct net_device *
nft_meta_get_bridge(const struct net_device * dev)169d6a1ecdSwenxu nft_meta_get_bridge(const struct net_device *dev)
179d6a1ecdSwenxu {
189d6a1ecdSwenxu 	if (dev && netif_is_bridge_port(dev))
199d6a1ecdSwenxu 		return netdev_master_upper_dev_get_rcu((struct net_device *)dev);
209d6a1ecdSwenxu 
219d6a1ecdSwenxu 	return NULL;
229d6a1ecdSwenxu }
2330e103feSwenxu 
nft_meta_bridge_get_eval(const struct nft_expr * expr,struct nft_regs * regs,const struct nft_pktinfo * pkt)2430e103feSwenxu static void nft_meta_bridge_get_eval(const struct nft_expr *expr,
2530e103feSwenxu 				     struct nft_regs *regs,
2630e103feSwenxu 				     const struct nft_pktinfo *pkt)
2730e103feSwenxu {
2830e103feSwenxu 	const struct nft_meta *priv = nft_expr_priv(expr);
2930e103feSwenxu 	const struct net_device *in = nft_in(pkt), *out = nft_out(pkt);
3030e103feSwenxu 	u32 *dest = &regs->data[priv->dreg];
319d6a1ecdSwenxu 	const struct net_device *br_dev;
3230e103feSwenxu 
3330e103feSwenxu 	switch (priv->key) {
3430e103feSwenxu 	case NFT_META_BRI_IIFNAME:
359d6a1ecdSwenxu 		br_dev = nft_meta_get_bridge(in);
3630e103feSwenxu 		break;
3730e103feSwenxu 	case NFT_META_BRI_OIFNAME:
389d6a1ecdSwenxu 		br_dev = nft_meta_get_bridge(out);
3930e103feSwenxu 		break;
40c54c7c68Swenxu 	case NFT_META_BRI_IIFPVID: {
41c54c7c68Swenxu 		u16 p_pvid;
42c54c7c68Swenxu 
43c54c7c68Swenxu 		br_dev = nft_meta_get_bridge(in);
44c54c7c68Swenxu 		if (!br_dev || !br_vlan_enabled(br_dev))
45c54c7c68Swenxu 			goto err;
46c54c7c68Swenxu 
47c54c7c68Swenxu 		br_vlan_get_pvid_rcu(in, &p_pvid);
48c54c7c68Swenxu 		nft_reg_store16(dest, p_pvid);
49c54c7c68Swenxu 		return;
50c54c7c68Swenxu 	}
512a3a93efSwenxu 	case NFT_META_BRI_IIFVPROTO: {
522a3a93efSwenxu 		u16 p_proto;
532a3a93efSwenxu 
542a3a93efSwenxu 		br_dev = nft_meta_get_bridge(in);
552a3a93efSwenxu 		if (!br_dev || !br_vlan_enabled(br_dev))
562a3a93efSwenxu 			goto err;
572a3a93efSwenxu 
582a3a93efSwenxu 		br_vlan_get_proto(br_dev, &p_proto);
597278b3c1SFlorian Westphal 		nft_reg_store_be16(dest, htons(p_proto));
602a3a93efSwenxu 		return;
612a3a93efSwenxu 	}
6230e103feSwenxu 	default:
6367d86835SPhil Sutter 		return nft_meta_get_eval(expr, regs, pkt);
6430e103feSwenxu 	}
6530e103feSwenxu 
66cb81572eSPhil Sutter 	strncpy((char *)dest, br_dev ? br_dev->name : "", IFNAMSIZ);
6730e103feSwenxu 	return;
6830e103feSwenxu err:
6930e103feSwenxu 	regs->verdict.code = NFT_BREAK;
7030e103feSwenxu }
7130e103feSwenxu 
nft_meta_bridge_get_init(const struct nft_ctx * ctx,const struct nft_expr * expr,const struct nlattr * const tb[])7230e103feSwenxu static int nft_meta_bridge_get_init(const struct nft_ctx *ctx,
7330e103feSwenxu 				    const struct nft_expr *expr,
7430e103feSwenxu 				    const struct nlattr * const tb[])
7530e103feSwenxu {
7630e103feSwenxu 	struct nft_meta *priv = nft_expr_priv(expr);
7730e103feSwenxu 	unsigned int len;
7830e103feSwenxu 
7930e103feSwenxu 	priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY]));
8030e103feSwenxu 	switch (priv->key) {
8130e103feSwenxu 	case NFT_META_BRI_IIFNAME:
8230e103feSwenxu 	case NFT_META_BRI_OIFNAME:
8330e103feSwenxu 		len = IFNAMSIZ;
8430e103feSwenxu 		break;
85c54c7c68Swenxu 	case NFT_META_BRI_IIFPVID:
862a3a93efSwenxu 	case NFT_META_BRI_IIFVPROTO:
87c54c7c68Swenxu 		len = sizeof(u16);
88c54c7c68Swenxu 		break;
8930e103feSwenxu 	default:
9030e103feSwenxu 		return nft_meta_get_init(ctx, expr, tb);
9130e103feSwenxu 	}
9230e103feSwenxu 
9334cc9e52SPablo Neira Ayuso 	priv->len = len;
94345023b0SPablo Neira Ayuso 	return nft_parse_register_store(ctx, tb[NFTA_META_DREG], &priv->dreg,
95345023b0SPablo Neira Ayuso 					NULL, NFT_DATA_VALUE, len);
9630e103feSwenxu }
9730e103feSwenxu 
9830e103feSwenxu static struct nft_expr_type nft_meta_bridge_type;
9930e103feSwenxu static const struct nft_expr_ops nft_meta_bridge_get_ops = {
10030e103feSwenxu 	.type		= &nft_meta_bridge_type,
10130e103feSwenxu 	.size		= NFT_EXPR_SIZE(sizeof(struct nft_meta)),
10230e103feSwenxu 	.eval		= nft_meta_bridge_get_eval,
10330e103feSwenxu 	.init		= nft_meta_bridge_get_init,
10430e103feSwenxu 	.dump		= nft_meta_get_dump,
105aaa7b20bSFlorian Westphal 	.reduce		= nft_meta_get_reduce,
10630e103feSwenxu };
10730e103feSwenxu 
nft_meta_bridge_set_eval(const struct nft_expr * expr,struct nft_regs * regs,const struct nft_pktinfo * pkt)108*4386b921SSriram Yagnaraman static void nft_meta_bridge_set_eval(const struct nft_expr *expr,
109*4386b921SSriram Yagnaraman 				     struct nft_regs *regs,
110*4386b921SSriram Yagnaraman 				     const struct nft_pktinfo *pkt)
111*4386b921SSriram Yagnaraman {
112*4386b921SSriram Yagnaraman 	const struct nft_meta *meta = nft_expr_priv(expr);
113*4386b921SSriram Yagnaraman 	u32 *sreg = &regs->data[meta->sreg];
114*4386b921SSriram Yagnaraman 	struct sk_buff *skb = pkt->skb;
115*4386b921SSriram Yagnaraman 	u8 value8;
116*4386b921SSriram Yagnaraman 
117*4386b921SSriram Yagnaraman 	switch (meta->key) {
118*4386b921SSriram Yagnaraman 	case NFT_META_BRI_BROUTE:
119*4386b921SSriram Yagnaraman 		value8 = nft_reg_load8(sreg);
120*4386b921SSriram Yagnaraman 		BR_INPUT_SKB_CB(skb)->br_netfilter_broute = !!value8;
121*4386b921SSriram Yagnaraman 		break;
122*4386b921SSriram Yagnaraman 	default:
123*4386b921SSriram Yagnaraman 		nft_meta_set_eval(expr, regs, pkt);
124*4386b921SSriram Yagnaraman 	}
125*4386b921SSriram Yagnaraman }
126*4386b921SSriram Yagnaraman 
nft_meta_bridge_set_init(const struct nft_ctx * ctx,const struct nft_expr * expr,const struct nlattr * const tb[])127*4386b921SSriram Yagnaraman static int nft_meta_bridge_set_init(const struct nft_ctx *ctx,
128*4386b921SSriram Yagnaraman 				    const struct nft_expr *expr,
129*4386b921SSriram Yagnaraman 				    const struct nlattr * const tb[])
130*4386b921SSriram Yagnaraman {
131*4386b921SSriram Yagnaraman 	struct nft_meta *priv = nft_expr_priv(expr);
132*4386b921SSriram Yagnaraman 	unsigned int len;
133*4386b921SSriram Yagnaraman 	int err;
134*4386b921SSriram Yagnaraman 
135*4386b921SSriram Yagnaraman 	priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY]));
136*4386b921SSriram Yagnaraman 	switch (priv->key) {
137*4386b921SSriram Yagnaraman 	case NFT_META_BRI_BROUTE:
138*4386b921SSriram Yagnaraman 		len = sizeof(u8);
139*4386b921SSriram Yagnaraman 		break;
140*4386b921SSriram Yagnaraman 	default:
141*4386b921SSriram Yagnaraman 		return nft_meta_set_init(ctx, expr, tb);
142*4386b921SSriram Yagnaraman 	}
143*4386b921SSriram Yagnaraman 
144*4386b921SSriram Yagnaraman 	priv->len = len;
145*4386b921SSriram Yagnaraman 	err = nft_parse_register_load(tb[NFTA_META_SREG], &priv->sreg, len);
146*4386b921SSriram Yagnaraman 	if (err < 0)
147*4386b921SSriram Yagnaraman 		return err;
148*4386b921SSriram Yagnaraman 
149*4386b921SSriram Yagnaraman 	return 0;
150*4386b921SSriram Yagnaraman }
151*4386b921SSriram Yagnaraman 
nft_meta_bridge_set_reduce(struct nft_regs_track * track,const struct nft_expr * expr)1524a80e026SPablo Neira Ayuso static bool nft_meta_bridge_set_reduce(struct nft_regs_track *track,
1534a80e026SPablo Neira Ayuso 				       const struct nft_expr *expr)
1544a80e026SPablo Neira Ayuso {
1554a80e026SPablo Neira Ayuso 	int i;
1564a80e026SPablo Neira Ayuso 
1574a80e026SPablo Neira Ayuso 	for (i = 0; i < NFT_REG32_NUM; i++) {
1584a80e026SPablo Neira Ayuso 		if (!track->regs[i].selector)
1594a80e026SPablo Neira Ayuso 			continue;
1604a80e026SPablo Neira Ayuso 
1614a80e026SPablo Neira Ayuso 		if (track->regs[i].selector->ops != &nft_meta_bridge_get_ops)
1624a80e026SPablo Neira Ayuso 			continue;
1634a80e026SPablo Neira Ayuso 
16434cc9e52SPablo Neira Ayuso 		__nft_reg_track_cancel(track, i);
1654a80e026SPablo Neira Ayuso 	}
1664a80e026SPablo Neira Ayuso 
1674a80e026SPablo Neira Ayuso 	return false;
1684a80e026SPablo Neira Ayuso }
1694a80e026SPablo Neira Ayuso 
nft_meta_bridge_set_validate(const struct nft_ctx * ctx,const struct nft_expr * expr,const struct nft_data ** data)170*4386b921SSriram Yagnaraman static int nft_meta_bridge_set_validate(const struct nft_ctx *ctx,
171*4386b921SSriram Yagnaraman 					const struct nft_expr *expr,
172*4386b921SSriram Yagnaraman 					const struct nft_data **data)
173*4386b921SSriram Yagnaraman {
174*4386b921SSriram Yagnaraman 	struct nft_meta *priv = nft_expr_priv(expr);
175*4386b921SSriram Yagnaraman 	unsigned int hooks;
176*4386b921SSriram Yagnaraman 
177*4386b921SSriram Yagnaraman 	switch (priv->key) {
178*4386b921SSriram Yagnaraman 	case NFT_META_BRI_BROUTE:
179*4386b921SSriram Yagnaraman 		hooks = 1 << NF_BR_PRE_ROUTING;
180*4386b921SSriram Yagnaraman 		break;
181*4386b921SSriram Yagnaraman 	default:
182*4386b921SSriram Yagnaraman 		return nft_meta_set_validate(ctx, expr, data);
183*4386b921SSriram Yagnaraman 	}
184*4386b921SSriram Yagnaraman 
185*4386b921SSriram Yagnaraman 	return nft_chain_validate_hooks(ctx->chain, hooks);
186*4386b921SSriram Yagnaraman }
187*4386b921SSriram Yagnaraman 
18830e103feSwenxu static const struct nft_expr_ops nft_meta_bridge_set_ops = {
18930e103feSwenxu 	.type		= &nft_meta_bridge_type,
19030e103feSwenxu 	.size		= NFT_EXPR_SIZE(sizeof(struct nft_meta)),
191*4386b921SSriram Yagnaraman 	.eval		= nft_meta_bridge_set_eval,
192*4386b921SSriram Yagnaraman 	.init		= nft_meta_bridge_set_init,
19330e103feSwenxu 	.destroy	= nft_meta_set_destroy,
19430e103feSwenxu 	.dump		= nft_meta_set_dump,
1954a80e026SPablo Neira Ayuso 	.reduce		= nft_meta_bridge_set_reduce,
196*4386b921SSriram Yagnaraman 	.validate	= nft_meta_bridge_set_validate,
19730e103feSwenxu };
19830e103feSwenxu 
19930e103feSwenxu static const struct nft_expr_ops *
nft_meta_bridge_select_ops(const struct nft_ctx * ctx,const struct nlattr * const tb[])20030e103feSwenxu nft_meta_bridge_select_ops(const struct nft_ctx *ctx,
20130e103feSwenxu 			   const struct nlattr * const tb[])
20230e103feSwenxu {
20330e103feSwenxu 	if (tb[NFTA_META_KEY] == NULL)
20430e103feSwenxu 		return ERR_PTR(-EINVAL);
20530e103feSwenxu 
20630e103feSwenxu 	if (tb[NFTA_META_DREG] && tb[NFTA_META_SREG])
20730e103feSwenxu 		return ERR_PTR(-EINVAL);
20830e103feSwenxu 
20930e103feSwenxu 	if (tb[NFTA_META_DREG])
21030e103feSwenxu 		return &nft_meta_bridge_get_ops;
21130e103feSwenxu 
21230e103feSwenxu 	if (tb[NFTA_META_SREG])
21330e103feSwenxu 		return &nft_meta_bridge_set_ops;
21430e103feSwenxu 
21530e103feSwenxu 	return ERR_PTR(-EINVAL);
21630e103feSwenxu }
21730e103feSwenxu 
21830e103feSwenxu static struct nft_expr_type nft_meta_bridge_type __read_mostly = {
21930e103feSwenxu 	.family         = NFPROTO_BRIDGE,
22030e103feSwenxu 	.name           = "meta",
22130e103feSwenxu 	.select_ops     = nft_meta_bridge_select_ops,
22230e103feSwenxu 	.policy         = nft_meta_policy,
22330e103feSwenxu 	.maxattr        = NFTA_META_MAX,
22430e103feSwenxu 	.owner          = THIS_MODULE,
22530e103feSwenxu };
22630e103feSwenxu 
nft_meta_bridge_module_init(void)22730e103feSwenxu static int __init nft_meta_bridge_module_init(void)
22830e103feSwenxu {
22930e103feSwenxu 	return nft_register_expr(&nft_meta_bridge_type);
23030e103feSwenxu }
23130e103feSwenxu 
nft_meta_bridge_module_exit(void)23230e103feSwenxu static void __exit nft_meta_bridge_module_exit(void)
23330e103feSwenxu {
23430e103feSwenxu 	nft_unregister_expr(&nft_meta_bridge_type);
23530e103feSwenxu }
23630e103feSwenxu 
23730e103feSwenxu module_init(nft_meta_bridge_module_init);
23830e103feSwenxu module_exit(nft_meta_bridge_module_exit);
23930e103feSwenxu 
24030e103feSwenxu MODULE_LICENSE("GPL");
24130e103feSwenxu MODULE_AUTHOR("wenxu <wenxu@ucloud.cn>");
24230e103feSwenxu MODULE_ALIAS_NFT_AF_EXPR(AF_BRIDGE, "meta");
2434cacc395SRob Gill MODULE_DESCRIPTION("Support for bridge dedicated meta key");
244