Linux 6.7-rc7
[linux-modified.git] / net / bridge / netfilter / nft_meta_bridge.c
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
15 static const struct net_device *
16 nft_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
24 static 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;
68 err:
69         regs->verdict.code = NFT_BREAK;
70 }
71
72 static 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
98 static struct nft_expr_type nft_meta_bridge_type;
99 static 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
108 static 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
127 static 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
152 static 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
170 static 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
188 static 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
199 static const struct nft_expr_ops *
200 nft_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
218 static 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
227 static int __init nft_meta_bridge_module_init(void)
228 {
229         return nft_register_expr(&nft_meta_bridge_type);
230 }
231
232 static void __exit nft_meta_bridge_module_exit(void)
233 {
234         nft_unregister_expr(&nft_meta_bridge_type);
235 }
236
237 module_init(nft_meta_bridge_module_init);
238 module_exit(nft_meta_bridge_module_exit);
239
240 MODULE_LICENSE("GPL");
241 MODULE_AUTHOR("wenxu <wenxu@ucloud.cn>");
242 MODULE_ALIAS_NFT_AF_EXPR(AF_BRIDGE, "meta");
243 MODULE_DESCRIPTION("Support for bridge dedicated meta key");