Commit | Line | Data |
---|---|---|
af308b94 PNA |
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/seqlock.h> | |
6 | #include <linux/netlink.h> | |
7 | #include <linux/netfilter.h> | |
8 | #include <linux/netfilter/nf_tables.h> | |
9 | #include <net/netfilter/nf_tables.h> | |
10 | #include <net/dst_metadata.h> | |
11 | #include <net/ip_tunnels.h> | |
12 | #include <net/vxlan.h> | |
13 | #include <net/erspan.h> | |
14 | ||
aaecfdb5 PNA |
15 | struct nft_tunnel { |
16 | enum nft_tunnel_keys key:8; | |
17 | enum nft_registers dreg:8; | |
a46c52d9 | 18 | enum nft_tunnel_mode mode:8; |
aaecfdb5 PNA |
19 | }; |
20 | ||
21 | static void nft_tunnel_get_eval(const struct nft_expr *expr, | |
22 | struct nft_regs *regs, | |
23 | const struct nft_pktinfo *pkt) | |
24 | { | |
25 | const struct nft_tunnel *priv = nft_expr_priv(expr); | |
26 | u32 *dest = ®s->data[priv->dreg]; | |
27 | struct ip_tunnel_info *tun_info; | |
28 | ||
29 | tun_info = skb_tunnel_info(pkt->skb); | |
30 | ||
31 | switch (priv->key) { | |
32 | case NFT_TUNNEL_PATH: | |
a46c52d9 | 33 | if (!tun_info) { |
34 | nft_reg_store8(dest, false); | |
35 | return; | |
36 | } | |
37 | if (priv->mode == NFT_TUNNEL_MODE_NONE || | |
38 | (priv->mode == NFT_TUNNEL_MODE_RX && | |
39 | !(tun_info->mode & IP_TUNNEL_INFO_TX)) || | |
40 | (priv->mode == NFT_TUNNEL_MODE_TX && | |
41 | (tun_info->mode & IP_TUNNEL_INFO_TX))) | |
42 | nft_reg_store8(dest, true); | |
43 | else | |
44 | nft_reg_store8(dest, false); | |
aaecfdb5 PNA |
45 | break; |
46 | case NFT_TUNNEL_ID: | |
47 | if (!tun_info) { | |
48 | regs->verdict.code = NFT_BREAK; | |
49 | return; | |
50 | } | |
a46c52d9 | 51 | if (priv->mode == NFT_TUNNEL_MODE_NONE || |
52 | (priv->mode == NFT_TUNNEL_MODE_RX && | |
53 | !(tun_info->mode & IP_TUNNEL_INFO_TX)) || | |
54 | (priv->mode == NFT_TUNNEL_MODE_TX && | |
55 | (tun_info->mode & IP_TUNNEL_INFO_TX))) | |
56 | *dest = ntohl(tunnel_id_to_key32(tun_info->key.tun_id)); | |
57 | else | |
58 | regs->verdict.code = NFT_BREAK; | |
aaecfdb5 PNA |
59 | break; |
60 | default: | |
61 | WARN_ON(1); | |
62 | regs->verdict.code = NFT_BREAK; | |
63 | } | |
64 | } | |
65 | ||
66 | static const struct nla_policy nft_tunnel_policy[NFTA_TUNNEL_MAX + 1] = { | |
67 | [NFTA_TUNNEL_KEY] = { .type = NLA_U32 }, | |
68 | [NFTA_TUNNEL_DREG] = { .type = NLA_U32 }, | |
a46c52d9 | 69 | [NFTA_TUNNEL_MODE] = { .type = NLA_U32 }, |
aaecfdb5 PNA |
70 | }; |
71 | ||
72 | static int nft_tunnel_get_init(const struct nft_ctx *ctx, | |
73 | const struct nft_expr *expr, | |
74 | const struct nlattr * const tb[]) | |
75 | { | |
76 | struct nft_tunnel *priv = nft_expr_priv(expr); | |
77 | u32 len; | |
78 | ||
79 | if (!tb[NFTA_TUNNEL_KEY] && | |
80 | !tb[NFTA_TUNNEL_DREG]) | |
81 | return -EINVAL; | |
82 | ||
83 | priv->key = ntohl(nla_get_be32(tb[NFTA_TUNNEL_KEY])); | |
84 | switch (priv->key) { | |
85 | case NFT_TUNNEL_PATH: | |
86 | len = sizeof(u8); | |
87 | break; | |
88 | case NFT_TUNNEL_ID: | |
89 | len = sizeof(u32); | |
90 | break; | |
91 | default: | |
92 | return -EOPNOTSUPP; | |
93 | } | |
94 | ||
95 | priv->dreg = nft_parse_register(tb[NFTA_TUNNEL_DREG]); | |
96 | ||
a46c52d9 | 97 | if (tb[NFTA_TUNNEL_MODE]) { |
98 | priv->mode = ntohl(nla_get_be32(tb[NFTA_TUNNEL_MODE])); | |
99 | if (priv->mode > NFT_TUNNEL_MODE_MAX) | |
100 | return -EOPNOTSUPP; | |
101 | } else { | |
102 | priv->mode = NFT_TUNNEL_MODE_NONE; | |
103 | } | |
104 | ||
aaecfdb5 PNA |
105 | return nft_validate_register_store(ctx, priv->dreg, NULL, |
106 | NFT_DATA_VALUE, len); | |
107 | } | |
108 | ||
109 | static int nft_tunnel_get_dump(struct sk_buff *skb, | |
110 | const struct nft_expr *expr) | |
111 | { | |
112 | const struct nft_tunnel *priv = nft_expr_priv(expr); | |
113 | ||
114 | if (nla_put_be32(skb, NFTA_TUNNEL_KEY, htonl(priv->key))) | |
115 | goto nla_put_failure; | |
116 | if (nft_dump_register(skb, NFTA_TUNNEL_DREG, priv->dreg)) | |
117 | goto nla_put_failure; | |
a46c52d9 | 118 | if (nla_put_be32(skb, NFTA_TUNNEL_MODE, htonl(priv->mode))) |
119 | goto nla_put_failure; | |
aaecfdb5 PNA |
120 | return 0; |
121 | ||
122 | nla_put_failure: | |
123 | return -1; | |
124 | } | |
125 | ||
126 | static struct nft_expr_type nft_tunnel_type; | |
127 | static const struct nft_expr_ops nft_tunnel_get_ops = { | |
128 | .type = &nft_tunnel_type, | |
129 | .size = NFT_EXPR_SIZE(sizeof(struct nft_tunnel)), | |
130 | .eval = nft_tunnel_get_eval, | |
131 | .init = nft_tunnel_get_init, | |
132 | .dump = nft_tunnel_get_dump, | |
133 | }; | |
134 | ||
135 | static struct nft_expr_type nft_tunnel_type __read_mostly = { | |
136 | .name = "tunnel", | |
137 | .ops = &nft_tunnel_get_ops, | |
138 | .policy = nft_tunnel_policy, | |
139 | .maxattr = NFTA_TUNNEL_MAX, | |
140 | .owner = THIS_MODULE, | |
141 | }; | |
142 | ||
af308b94 PNA |
143 | struct nft_tunnel_opts { |
144 | union { | |
145 | struct vxlan_metadata vxlan; | |
146 | struct erspan_metadata erspan; | |
147 | } u; | |
148 | u32 len; | |
483f3fdc | 149 | __be16 flags; |
af308b94 PNA |
150 | }; |
151 | ||
152 | struct nft_tunnel_obj { | |
153 | struct metadata_dst *md; | |
154 | struct nft_tunnel_opts opts; | |
155 | }; | |
156 | ||
157 | static const struct nla_policy nft_tunnel_ip_policy[NFTA_TUNNEL_KEY_IP_MAX + 1] = { | |
158 | [NFTA_TUNNEL_KEY_IP_SRC] = { .type = NLA_U32 }, | |
159 | [NFTA_TUNNEL_KEY_IP_DST] = { .type = NLA_U32 }, | |
160 | }; | |
161 | ||
162 | static int nft_tunnel_obj_ip_init(const struct nft_ctx *ctx, | |
163 | const struct nlattr *attr, | |
164 | struct ip_tunnel_info *info) | |
165 | { | |
166 | struct nlattr *tb[NFTA_TUNNEL_KEY_IP_MAX + 1]; | |
167 | int err; | |
168 | ||
8cb08174 JB |
169 | err = nla_parse_nested_deprecated(tb, NFTA_TUNNEL_KEY_IP_MAX, attr, |
170 | nft_tunnel_ip_policy, NULL); | |
af308b94 PNA |
171 | if (err < 0) |
172 | return err; | |
173 | ||
174 | if (!tb[NFTA_TUNNEL_KEY_IP_DST]) | |
175 | return -EINVAL; | |
176 | ||
177 | if (tb[NFTA_TUNNEL_KEY_IP_SRC]) | |
178 | info->key.u.ipv4.src = nla_get_be32(tb[NFTA_TUNNEL_KEY_IP_SRC]); | |
179 | if (tb[NFTA_TUNNEL_KEY_IP_DST]) | |
180 | info->key.u.ipv4.dst = nla_get_be32(tb[NFTA_TUNNEL_KEY_IP_DST]); | |
181 | ||
182 | return 0; | |
183 | } | |
184 | ||
185 | static const struct nla_policy nft_tunnel_ip6_policy[NFTA_TUNNEL_KEY_IP6_MAX + 1] = { | |
186 | [NFTA_TUNNEL_KEY_IP6_SRC] = { .len = sizeof(struct in6_addr), }, | |
187 | [NFTA_TUNNEL_KEY_IP6_DST] = { .len = sizeof(struct in6_addr), }, | |
188 | [NFTA_TUNNEL_KEY_IP6_FLOWLABEL] = { .type = NLA_U32, } | |
189 | }; | |
190 | ||
191 | static int nft_tunnel_obj_ip6_init(const struct nft_ctx *ctx, | |
192 | const struct nlattr *attr, | |
193 | struct ip_tunnel_info *info) | |
194 | { | |
195 | struct nlattr *tb[NFTA_TUNNEL_KEY_IP6_MAX + 1]; | |
196 | int err; | |
197 | ||
8cb08174 JB |
198 | err = nla_parse_nested_deprecated(tb, NFTA_TUNNEL_KEY_IP6_MAX, attr, |
199 | nft_tunnel_ip6_policy, NULL); | |
af308b94 PNA |
200 | if (err < 0) |
201 | return err; | |
202 | ||
203 | if (!tb[NFTA_TUNNEL_KEY_IP6_DST]) | |
204 | return -EINVAL; | |
205 | ||
206 | if (tb[NFTA_TUNNEL_KEY_IP6_SRC]) { | |
207 | memcpy(&info->key.u.ipv6.src, | |
208 | nla_data(tb[NFTA_TUNNEL_KEY_IP6_SRC]), | |
209 | sizeof(struct in6_addr)); | |
210 | } | |
211 | if (tb[NFTA_TUNNEL_KEY_IP6_DST]) { | |
212 | memcpy(&info->key.u.ipv6.dst, | |
213 | nla_data(tb[NFTA_TUNNEL_KEY_IP6_DST]), | |
214 | sizeof(struct in6_addr)); | |
215 | } | |
216 | if (tb[NFTA_TUNNEL_KEY_IP6_FLOWLABEL]) | |
217 | info->key.label = nla_get_be32(tb[NFTA_TUNNEL_KEY_IP6_FLOWLABEL]); | |
218 | ||
219 | info->mode |= IP_TUNNEL_INFO_IPV6; | |
220 | ||
221 | return 0; | |
222 | } | |
223 | ||
224 | static const struct nla_policy nft_tunnel_opts_vxlan_policy[NFTA_TUNNEL_KEY_VXLAN_MAX + 1] = { | |
225 | [NFTA_TUNNEL_KEY_VXLAN_GBP] = { .type = NLA_U32 }, | |
226 | }; | |
227 | ||
228 | static int nft_tunnel_obj_vxlan_init(const struct nlattr *attr, | |
229 | struct nft_tunnel_opts *opts) | |
230 | { | |
231 | struct nlattr *tb[NFTA_TUNNEL_KEY_VXLAN_MAX + 1]; | |
232 | int err; | |
233 | ||
8cb08174 JB |
234 | err = nla_parse_nested_deprecated(tb, NFTA_TUNNEL_KEY_VXLAN_MAX, attr, |
235 | nft_tunnel_opts_vxlan_policy, NULL); | |
af308b94 PNA |
236 | if (err < 0) |
237 | return err; | |
238 | ||
239 | if (!tb[NFTA_TUNNEL_KEY_VXLAN_GBP]) | |
240 | return -EINVAL; | |
241 | ||
242 | opts->u.vxlan.gbp = ntohl(nla_get_be32(tb[NFTA_TUNNEL_KEY_VXLAN_GBP])); | |
243 | ||
244 | opts->len = sizeof(struct vxlan_metadata); | |
245 | opts->flags = TUNNEL_VXLAN_OPT; | |
246 | ||
247 | return 0; | |
248 | } | |
249 | ||
250 | static const struct nla_policy nft_tunnel_opts_erspan_policy[NFTA_TUNNEL_KEY_ERSPAN_MAX + 1] = { | |
251 | [NFTA_TUNNEL_KEY_ERSPAN_V1_INDEX] = { .type = NLA_U32 }, | |
252 | [NFTA_TUNNEL_KEY_ERSPAN_V2_DIR] = { .type = NLA_U8 }, | |
253 | [NFTA_TUNNEL_KEY_ERSPAN_V2_HWID] = { .type = NLA_U8 }, | |
254 | }; | |
255 | ||
256 | static int nft_tunnel_obj_erspan_init(const struct nlattr *attr, | |
257 | struct nft_tunnel_opts *opts) | |
258 | { | |
259 | struct nlattr *tb[NFTA_TUNNEL_KEY_ERSPAN_MAX + 1]; | |
260 | uint8_t hwid, dir; | |
261 | int err, version; | |
262 | ||
8cb08174 JB |
263 | err = nla_parse_nested_deprecated(tb, NFTA_TUNNEL_KEY_ERSPAN_MAX, |
264 | attr, nft_tunnel_opts_erspan_policy, | |
265 | NULL); | |
af308b94 PNA |
266 | if (err < 0) |
267 | return err; | |
268 | ||
269 | version = ntohl(nla_get_be32(tb[NFTA_TUNNEL_KEY_ERSPAN_VERSION])); | |
270 | switch (version) { | |
271 | case ERSPAN_VERSION: | |
272 | if (!tb[NFTA_TUNNEL_KEY_ERSPAN_V1_INDEX]) | |
273 | return -EINVAL; | |
274 | ||
275 | opts->u.erspan.u.index = | |
276 | nla_get_be32(tb[NFTA_TUNNEL_KEY_ERSPAN_V1_INDEX]); | |
277 | break; | |
278 | case ERSPAN_VERSION2: | |
279 | if (!tb[NFTA_TUNNEL_KEY_ERSPAN_V2_DIR] || | |
280 | !tb[NFTA_TUNNEL_KEY_ERSPAN_V2_HWID]) | |
281 | return -EINVAL; | |
282 | ||
283 | hwid = nla_get_u8(tb[NFTA_TUNNEL_KEY_ERSPAN_V2_HWID]); | |
284 | dir = nla_get_u8(tb[NFTA_TUNNEL_KEY_ERSPAN_V2_DIR]); | |
285 | ||
286 | set_hwid(&opts->u.erspan.u.md2, hwid); | |
287 | opts->u.erspan.u.md2.dir = dir; | |
288 | break; | |
289 | default: | |
290 | return -EOPNOTSUPP; | |
291 | } | |
292 | opts->u.erspan.version = version; | |
293 | ||
294 | opts->len = sizeof(struct erspan_metadata); | |
295 | opts->flags = TUNNEL_ERSPAN_OPT; | |
296 | ||
297 | return 0; | |
298 | } | |
299 | ||
300 | static const struct nla_policy nft_tunnel_opts_policy[NFTA_TUNNEL_KEY_OPTS_MAX + 1] = { | |
301 | [NFTA_TUNNEL_KEY_OPTS_VXLAN] = { .type = NLA_NESTED, }, | |
302 | [NFTA_TUNNEL_KEY_OPTS_ERSPAN] = { .type = NLA_NESTED, }, | |
303 | }; | |
304 | ||
305 | static int nft_tunnel_obj_opts_init(const struct nft_ctx *ctx, | |
306 | const struct nlattr *attr, | |
307 | struct ip_tunnel_info *info, | |
308 | struct nft_tunnel_opts *opts) | |
309 | { | |
310 | struct nlattr *tb[NFTA_TUNNEL_KEY_OPTS_MAX + 1]; | |
311 | int err; | |
312 | ||
8cb08174 JB |
313 | err = nla_parse_nested_deprecated(tb, NFTA_TUNNEL_KEY_OPTS_MAX, attr, |
314 | nft_tunnel_opts_policy, NULL); | |
af308b94 PNA |
315 | if (err < 0) |
316 | return err; | |
317 | ||
318 | if (tb[NFTA_TUNNEL_KEY_OPTS_VXLAN]) { | |
319 | err = nft_tunnel_obj_vxlan_init(tb[NFTA_TUNNEL_KEY_OPTS_VXLAN], | |
320 | opts); | |
321 | } else if (tb[NFTA_TUNNEL_KEY_OPTS_ERSPAN]) { | |
322 | err = nft_tunnel_obj_erspan_init(tb[NFTA_TUNNEL_KEY_OPTS_ERSPAN], | |
323 | opts); | |
324 | } else { | |
325 | return -EOPNOTSUPP; | |
326 | } | |
327 | ||
328 | return err; | |
329 | } | |
330 | ||
331 | static const struct nla_policy nft_tunnel_key_policy[NFTA_TUNNEL_KEY_MAX + 1] = { | |
332 | [NFTA_TUNNEL_KEY_IP] = { .type = NLA_NESTED, }, | |
333 | [NFTA_TUNNEL_KEY_IP6] = { .type = NLA_NESTED, }, | |
334 | [NFTA_TUNNEL_KEY_ID] = { .type = NLA_U32, }, | |
335 | [NFTA_TUNNEL_KEY_FLAGS] = { .type = NLA_U32, }, | |
336 | [NFTA_TUNNEL_KEY_TOS] = { .type = NLA_U8, }, | |
337 | [NFTA_TUNNEL_KEY_TTL] = { .type = NLA_U8, }, | |
338 | [NFTA_TUNNEL_KEY_OPTS] = { .type = NLA_NESTED, }, | |
339 | }; | |
340 | ||
341 | static int nft_tunnel_obj_init(const struct nft_ctx *ctx, | |
342 | const struct nlattr * const tb[], | |
343 | struct nft_object *obj) | |
344 | { | |
345 | struct nft_tunnel_obj *priv = nft_obj_data(obj); | |
346 | struct ip_tunnel_info info; | |
347 | struct metadata_dst *md; | |
348 | int err; | |
349 | ||
350 | if (!tb[NFTA_TUNNEL_KEY_ID]) | |
351 | return -EINVAL; | |
352 | ||
353 | memset(&info, 0, sizeof(info)); | |
354 | info.mode = IP_TUNNEL_INFO_TX; | |
355 | info.key.tun_id = key32_to_tunnel_id(nla_get_be32(tb[NFTA_TUNNEL_KEY_ID])); | |
356 | info.key.tun_flags = TUNNEL_KEY | TUNNEL_CSUM | TUNNEL_NOCACHE; | |
357 | ||
358 | if (tb[NFTA_TUNNEL_KEY_IP]) { | |
359 | err = nft_tunnel_obj_ip_init(ctx, tb[NFTA_TUNNEL_KEY_IP], &info); | |
360 | if (err < 0) | |
361 | return err; | |
362 | } else if (tb[NFTA_TUNNEL_KEY_IP6]) { | |
363 | err = nft_tunnel_obj_ip6_init(ctx, tb[NFTA_TUNNEL_KEY_IP6], &info); | |
364 | if (err < 0) | |
365 | return err; | |
366 | } else { | |
367 | return -EINVAL; | |
368 | } | |
369 | ||
370 | if (tb[NFTA_TUNNEL_KEY_SPORT]) { | |
483f3fdc | 371 | info.key.tp_src = nla_get_be16(tb[NFTA_TUNNEL_KEY_SPORT]); |
af308b94 PNA |
372 | } |
373 | if (tb[NFTA_TUNNEL_KEY_DPORT]) { | |
483f3fdc | 374 | info.key.tp_dst = nla_get_be16(tb[NFTA_TUNNEL_KEY_DPORT]); |
af308b94 PNA |
375 | } |
376 | ||
377 | if (tb[NFTA_TUNNEL_KEY_FLAGS]) { | |
378 | u32 tun_flags; | |
379 | ||
380 | tun_flags = ntohl(nla_get_be32(tb[NFTA_TUNNEL_KEY_FLAGS])); | |
381 | if (tun_flags & ~NFT_TUNNEL_F_MASK) | |
382 | return -EOPNOTSUPP; | |
383 | ||
384 | if (tun_flags & NFT_TUNNEL_F_ZERO_CSUM_TX) | |
385 | info.key.tun_flags &= ~TUNNEL_CSUM; | |
386 | if (tun_flags & NFT_TUNNEL_F_DONT_FRAGMENT) | |
387 | info.key.tun_flags |= TUNNEL_DONT_FRAGMENT; | |
388 | if (tun_flags & NFT_TUNNEL_F_SEQ_NUMBER) | |
389 | info.key.tun_flags |= TUNNEL_SEQ; | |
390 | } | |
391 | if (tb[NFTA_TUNNEL_KEY_TOS]) | |
392 | info.key.tos = nla_get_u8(tb[NFTA_TUNNEL_KEY_TOS]); | |
393 | if (tb[NFTA_TUNNEL_KEY_TTL]) | |
394 | info.key.ttl = nla_get_u8(tb[NFTA_TUNNEL_KEY_TTL]); | |
395 | else | |
396 | info.key.ttl = U8_MAX; | |
397 | ||
398 | if (tb[NFTA_TUNNEL_KEY_OPTS]) { | |
399 | err = nft_tunnel_obj_opts_init(ctx, tb[NFTA_TUNNEL_KEY_OPTS], | |
400 | &info, &priv->opts); | |
401 | if (err < 0) | |
402 | return err; | |
403 | } | |
404 | ||
405 | md = metadata_dst_alloc(priv->opts.len, METADATA_IP_TUNNEL, GFP_KERNEL); | |
406 | if (!md) | |
407 | return -ENOMEM; | |
408 | ||
409 | memcpy(&md->u.tun_info, &info, sizeof(info)); | |
3e511d56 | 410 | #ifdef CONFIG_DST_CACHE |
411 | err = dst_cache_init(&md->u.tun_info.dst_cache, GFP_KERNEL); | |
412 | if (err < 0) { | |
413 | metadata_dst_free(md); | |
414 | return err; | |
415 | } | |
416 | #endif | |
af308b94 PNA |
417 | ip_tunnel_info_opts_set(&md->u.tun_info, &priv->opts.u, priv->opts.len, |
418 | priv->opts.flags); | |
419 | priv->md = md; | |
420 | ||
421 | return 0; | |
422 | } | |
423 | ||
424 | static inline void nft_tunnel_obj_eval(struct nft_object *obj, | |
425 | struct nft_regs *regs, | |
426 | const struct nft_pktinfo *pkt) | |
427 | { | |
428 | struct nft_tunnel_obj *priv = nft_obj_data(obj); | |
429 | struct sk_buff *skb = pkt->skb; | |
430 | ||
431 | skb_dst_drop(skb); | |
432 | dst_hold((struct dst_entry *) priv->md); | |
433 | skb_dst_set(skb, (struct dst_entry *) priv->md); | |
434 | } | |
435 | ||
436 | static int nft_tunnel_ip_dump(struct sk_buff *skb, struct ip_tunnel_info *info) | |
437 | { | |
438 | struct nlattr *nest; | |
439 | ||
440 | if (info->mode & IP_TUNNEL_INFO_IPV6) { | |
ae0be8de | 441 | nest = nla_nest_start_noflag(skb, NFTA_TUNNEL_KEY_IP6); |
af308b94 PNA |
442 | if (!nest) |
443 | return -1; | |
444 | ||
445 | if (nla_put_in6_addr(skb, NFTA_TUNNEL_KEY_IP6_SRC, &info->key.u.ipv6.src) < 0 || | |
446 | nla_put_in6_addr(skb, NFTA_TUNNEL_KEY_IP6_DST, &info->key.u.ipv6.dst) < 0 || | |
447 | nla_put_be32(skb, NFTA_TUNNEL_KEY_IP6_FLOWLABEL, info->key.label)) | |
448 | return -1; | |
449 | ||
450 | nla_nest_end(skb, nest); | |
451 | } else { | |
ae0be8de | 452 | nest = nla_nest_start_noflag(skb, NFTA_TUNNEL_KEY_IP); |
af308b94 PNA |
453 | if (!nest) |
454 | return -1; | |
455 | ||
456 | if (nla_put_in_addr(skb, NFTA_TUNNEL_KEY_IP_SRC, info->key.u.ipv4.src) < 0 || | |
457 | nla_put_in_addr(skb, NFTA_TUNNEL_KEY_IP_DST, info->key.u.ipv4.dst) < 0) | |
458 | return -1; | |
459 | ||
460 | nla_nest_end(skb, nest); | |
461 | } | |
462 | ||
463 | return 0; | |
464 | } | |
465 | ||
466 | static int nft_tunnel_opts_dump(struct sk_buff *skb, | |
467 | struct nft_tunnel_obj *priv) | |
468 | { | |
469 | struct nft_tunnel_opts *opts = &priv->opts; | |
470 | struct nlattr *nest; | |
471 | ||
ae0be8de | 472 | nest = nla_nest_start_noflag(skb, NFTA_TUNNEL_KEY_OPTS); |
af308b94 PNA |
473 | if (!nest) |
474 | return -1; | |
475 | ||
476 | if (opts->flags & TUNNEL_VXLAN_OPT) { | |
477 | if (nla_put_be32(skb, NFTA_TUNNEL_KEY_VXLAN_GBP, | |
478 | htonl(opts->u.vxlan.gbp))) | |
479 | return -1; | |
480 | } else if (opts->flags & TUNNEL_ERSPAN_OPT) { | |
481 | switch (opts->u.erspan.version) { | |
482 | case ERSPAN_VERSION: | |
483 | if (nla_put_be32(skb, NFTA_TUNNEL_KEY_ERSPAN_V1_INDEX, | |
484 | opts->u.erspan.u.index)) | |
485 | return -1; | |
486 | break; | |
487 | case ERSPAN_VERSION2: | |
488 | if (nla_put_u8(skb, NFTA_TUNNEL_KEY_ERSPAN_V2_HWID, | |
489 | get_hwid(&opts->u.erspan.u.md2)) || | |
490 | nla_put_u8(skb, NFTA_TUNNEL_KEY_ERSPAN_V2_DIR, | |
491 | opts->u.erspan.u.md2.dir)) | |
492 | return -1; | |
493 | break; | |
494 | } | |
495 | } | |
496 | nla_nest_end(skb, nest); | |
497 | ||
498 | return 0; | |
499 | } | |
500 | ||
501 | static int nft_tunnel_ports_dump(struct sk_buff *skb, | |
502 | struct ip_tunnel_info *info) | |
503 | { | |
504 | if (nla_put_be16(skb, NFTA_TUNNEL_KEY_SPORT, htons(info->key.tp_src)) < 0 || | |
505 | nla_put_be16(skb, NFTA_TUNNEL_KEY_DPORT, htons(info->key.tp_dst)) < 0) | |
506 | return -1; | |
507 | ||
508 | return 0; | |
509 | } | |
510 | ||
511 | static int nft_tunnel_flags_dump(struct sk_buff *skb, | |
512 | struct ip_tunnel_info *info) | |
513 | { | |
514 | u32 flags = 0; | |
515 | ||
516 | if (info->key.tun_flags & TUNNEL_DONT_FRAGMENT) | |
517 | flags |= NFT_TUNNEL_F_DONT_FRAGMENT; | |
518 | if (!(info->key.tun_flags & TUNNEL_CSUM)) | |
519 | flags |= NFT_TUNNEL_F_ZERO_CSUM_TX; | |
520 | if (info->key.tun_flags & TUNNEL_SEQ) | |
521 | flags |= NFT_TUNNEL_F_SEQ_NUMBER; | |
522 | ||
523 | if (nla_put_be32(skb, NFTA_TUNNEL_KEY_FLAGS, htonl(flags)) < 0) | |
524 | return -1; | |
525 | ||
526 | return 0; | |
527 | } | |
528 | ||
529 | static int nft_tunnel_obj_dump(struct sk_buff *skb, | |
530 | struct nft_object *obj, bool reset) | |
531 | { | |
532 | struct nft_tunnel_obj *priv = nft_obj_data(obj); | |
533 | struct ip_tunnel_info *info = &priv->md->u.tun_info; | |
534 | ||
535 | if (nla_put_be32(skb, NFTA_TUNNEL_KEY_ID, | |
536 | tunnel_id_to_key32(info->key.tun_id)) || | |
537 | nft_tunnel_ip_dump(skb, info) < 0 || | |
538 | nft_tunnel_ports_dump(skb, info) < 0 || | |
539 | nft_tunnel_flags_dump(skb, info) < 0 || | |
540 | nla_put_u8(skb, NFTA_TUNNEL_KEY_TOS, info->key.tos) || | |
541 | nla_put_u8(skb, NFTA_TUNNEL_KEY_TTL, info->key.ttl) || | |
542 | nft_tunnel_opts_dump(skb, priv) < 0) | |
543 | goto nla_put_failure; | |
544 | ||
545 | return 0; | |
546 | ||
547 | nla_put_failure: | |
548 | return -1; | |
549 | } | |
550 | ||
551 | static void nft_tunnel_obj_destroy(const struct nft_ctx *ctx, | |
552 | struct nft_object *obj) | |
553 | { | |
554 | struct nft_tunnel_obj *priv = nft_obj_data(obj); | |
555 | ||
556 | metadata_dst_free(priv->md); | |
557 | } | |
558 | ||
559 | static struct nft_object_type nft_tunnel_obj_type; | |
560 | static const struct nft_object_ops nft_tunnel_obj_ops = { | |
561 | .type = &nft_tunnel_obj_type, | |
562 | .size = sizeof(struct nft_tunnel_obj), | |
563 | .eval = nft_tunnel_obj_eval, | |
564 | .init = nft_tunnel_obj_init, | |
565 | .destroy = nft_tunnel_obj_destroy, | |
566 | .dump = nft_tunnel_obj_dump, | |
567 | }; | |
568 | ||
569 | static struct nft_object_type nft_tunnel_obj_type __read_mostly = { | |
570 | .type = NFT_OBJECT_TUNNEL, | |
571 | .ops = &nft_tunnel_obj_ops, | |
572 | .maxattr = NFTA_TUNNEL_KEY_MAX, | |
573 | .policy = nft_tunnel_key_policy, | |
574 | .owner = THIS_MODULE, | |
575 | }; | |
576 | ||
577 | static int __init nft_tunnel_module_init(void) | |
578 | { | |
aaecfdb5 PNA |
579 | int err; |
580 | ||
581 | err = nft_register_expr(&nft_tunnel_type); | |
582 | if (err < 0) | |
583 | return err; | |
584 | ||
585 | err = nft_register_obj(&nft_tunnel_obj_type); | |
586 | if (err < 0) | |
587 | nft_unregister_expr(&nft_tunnel_type); | |
588 | ||
589 | return err; | |
af308b94 PNA |
590 | } |
591 | ||
592 | static void __exit nft_tunnel_module_exit(void) | |
593 | { | |
594 | nft_unregister_obj(&nft_tunnel_obj_type); | |
aaecfdb5 | 595 | nft_unregister_expr(&nft_tunnel_type); |
af308b94 PNA |
596 | } |
597 | ||
598 | module_init(nft_tunnel_module_init); | |
599 | module_exit(nft_tunnel_module_exit); | |
600 | ||
601 | MODULE_LICENSE("GPL"); | |
602 | MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>"); | |
aaecfdb5 | 603 | MODULE_ALIAS_NFT_EXPR("tunnel"); |
af308b94 | 604 | MODULE_ALIAS_NFT_OBJ(NFT_OBJECT_TUNNEL); |