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 = ®s->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 = ®s->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