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(dev: (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 = ®s->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(dev: in); |
36 | break; |
37 | case NFT_META_BRI_OIFNAME: |
38 | br_dev = nft_meta_get_bridge(dev: out); |
39 | break; |
40 | case NFT_META_BRI_IIFPVID: { |
41 | u16 p_pvid; |
42 | |
43 | br_dev = nft_meta_get_bridge(dev: in); |
44 | if (!br_dev || !br_vlan_enabled(dev: br_dev)) |
45 | goto err; |
46 | |
47 | br_vlan_get_pvid_rcu(dev: in, p_pvid: &p_pvid); |
48 | nft_reg_store16(dreg: dest, val: p_pvid); |
49 | return; |
50 | } |
51 | case NFT_META_BRI_IIFVPROTO: { |
52 | u16 p_proto; |
53 | |
54 | br_dev = nft_meta_get_bridge(dev: in); |
55 | if (!br_dev || !br_vlan_enabled(dev: br_dev)) |
56 | goto err; |
57 | |
58 | br_vlan_get_proto(dev: br_dev, p_proto: &p_proto); |
59 | nft_reg_store_be16(dreg: dest, htons(p_proto)); |
60 | return; |
61 | } |
62 | default: |
63 | return nft_meta_get_eval(expr, regs, pkt); |
64 | } |
65 | |
66 | strncpy(p: (char *)dest, q: 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, attr: tb[NFTA_META_DREG], dreg: &priv->dreg, |
95 | NULL, type: 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 = ®s->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(attr: tb[NFTA_META_SREG], 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, dreg: 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(chain: ctx->chain, hook_flags: 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(error: -EINVAL); |
205 | |
206 | if (tb[NFTA_META_DREG] && tb[NFTA_META_SREG]) |
207 | return ERR_PTR(error: -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(error: -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" ); |
244 | |