|
11 | 11 | #include <net/ip_tunnels.h>
|
12 | 12 | #include <net/vxlan.h>
|
13 | 13 | #include <net/erspan.h>
|
| 14 | +#include <net/geneve.h> |
14 | 15 |
|
15 | 16 | struct nft_tunnel {
|
16 | 17 | enum nft_tunnel_keys key:8;
|
@@ -144,6 +145,7 @@ struct nft_tunnel_opts {
|
144 | 145 | union {
|
145 | 146 | struct vxlan_metadata vxlan;
|
146 | 147 | struct erspan_metadata erspan;
|
| 148 | + u8 data[IP_TUNNEL_OPTS_MAX]; |
147 | 149 | } u;
|
148 | 150 | u32 len;
|
149 | 151 | __be16 flags;
|
@@ -301,32 +303,97 @@ static int nft_tunnel_obj_erspan_init(const struct nlattr *attr,
|
301 | 303 | return 0;
|
302 | 304 | }
|
303 | 305 |
|
| 306 | +static const struct nla_policy nft_tunnel_opts_geneve_policy[NFTA_TUNNEL_KEY_GENEVE_MAX + 1] = { |
| 307 | + [NFTA_TUNNEL_KEY_GENEVE_CLASS] = { .type = NLA_U16 }, |
| 308 | + [NFTA_TUNNEL_KEY_GENEVE_TYPE] = { .type = NLA_U8 }, |
| 309 | + [NFTA_TUNNEL_KEY_GENEVE_DATA] = { .type = NLA_BINARY, .len = 128 }, |
| 310 | +}; |
| 311 | + |
| 312 | +static int nft_tunnel_obj_geneve_init(const struct nlattr *attr, |
| 313 | + struct nft_tunnel_opts *opts) |
| 314 | +{ |
| 315 | + struct geneve_opt *opt = (struct geneve_opt *)opts->u.data + opts->len; |
| 316 | + struct nlattr *tb[NFTA_TUNNEL_KEY_GENEVE_MAX + 1]; |
| 317 | + int err, data_len; |
| 318 | + |
| 319 | + err = nla_parse_nested(tb, NFTA_TUNNEL_KEY_GENEVE_MAX, attr, |
| 320 | + nft_tunnel_opts_geneve_policy, NULL); |
| 321 | + if (err < 0) |
| 322 | + return err; |
| 323 | + |
| 324 | + if (!tb[NFTA_TUNNEL_KEY_GENEVE_CLASS] || |
| 325 | + !tb[NFTA_TUNNEL_KEY_GENEVE_TYPE] || |
| 326 | + !tb[NFTA_TUNNEL_KEY_GENEVE_DATA]) |
| 327 | + return -EINVAL; |
| 328 | + |
| 329 | + attr = tb[NFTA_TUNNEL_KEY_GENEVE_DATA]; |
| 330 | + data_len = nla_len(attr); |
| 331 | + if (data_len % 4) |
| 332 | + return -EINVAL; |
| 333 | + |
| 334 | + opts->len += sizeof(*opt) + data_len; |
| 335 | + if (opts->len > IP_TUNNEL_OPTS_MAX) |
| 336 | + return -EINVAL; |
| 337 | + |
| 338 | + memcpy(opt->opt_data, nla_data(attr), data_len); |
| 339 | + opt->length = data_len / 4; |
| 340 | + opt->opt_class = nla_get_be16(tb[NFTA_TUNNEL_KEY_GENEVE_CLASS]); |
| 341 | + opt->type = nla_get_u8(tb[NFTA_TUNNEL_KEY_GENEVE_TYPE]); |
| 342 | + opts->flags = TUNNEL_GENEVE_OPT; |
| 343 | + |
| 344 | + return 0; |
| 345 | +} |
| 346 | + |
304 | 347 | static const struct nla_policy nft_tunnel_opts_policy[NFTA_TUNNEL_KEY_OPTS_MAX + 1] = {
|
| 348 | + [NFTA_TUNNEL_KEY_OPTS_UNSPEC] = { |
| 349 | + .strict_start_type = NFTA_TUNNEL_KEY_OPTS_GENEVE }, |
305 | 350 | [NFTA_TUNNEL_KEY_OPTS_VXLAN] = { .type = NLA_NESTED, },
|
306 | 351 | [NFTA_TUNNEL_KEY_OPTS_ERSPAN] = { .type = NLA_NESTED, },
|
| 352 | + [NFTA_TUNNEL_KEY_OPTS_GENEVE] = { .type = NLA_NESTED, }, |
307 | 353 | };
|
308 | 354 |
|
309 | 355 | static int nft_tunnel_obj_opts_init(const struct nft_ctx *ctx,
|
310 | 356 | const struct nlattr *attr,
|
311 | 357 | struct ip_tunnel_info *info,
|
312 | 358 | struct nft_tunnel_opts *opts)
|
313 | 359 | {
|
314 |
| - struct nlattr *tb[NFTA_TUNNEL_KEY_OPTS_MAX + 1]; |
315 |
| - int err; |
| 360 | + int err, rem, type = 0; |
| 361 | + struct nlattr *nla; |
316 | 362 |
|
317 |
| - err = nla_parse_nested_deprecated(tb, NFTA_TUNNEL_KEY_OPTS_MAX, attr, |
318 |
| - nft_tunnel_opts_policy, NULL); |
| 363 | + err = nla_validate_nested_deprecated(attr, NFTA_TUNNEL_KEY_OPTS_MAX, |
| 364 | + nft_tunnel_opts_policy, NULL); |
319 | 365 | if (err < 0)
|
320 | 366 | return err;
|
321 | 367 |
|
322 |
| - if (tb[NFTA_TUNNEL_KEY_OPTS_VXLAN]) { |
323 |
| - err = nft_tunnel_obj_vxlan_init(tb[NFTA_TUNNEL_KEY_OPTS_VXLAN], |
324 |
| - opts); |
325 |
| - } else if (tb[NFTA_TUNNEL_KEY_OPTS_ERSPAN]) { |
326 |
| - err = nft_tunnel_obj_erspan_init(tb[NFTA_TUNNEL_KEY_OPTS_ERSPAN], |
327 |
| - opts); |
328 |
| - } else { |
329 |
| - return -EOPNOTSUPP; |
| 368 | + nla_for_each_attr(nla, nla_data(attr), nla_len(attr), rem) { |
| 369 | + switch (nla_type(nla)) { |
| 370 | + case NFTA_TUNNEL_KEY_OPTS_VXLAN: |
| 371 | + if (type) |
| 372 | + return -EINVAL; |
| 373 | + err = nft_tunnel_obj_vxlan_init(nla, opts); |
| 374 | + if (err) |
| 375 | + return err; |
| 376 | + type = TUNNEL_VXLAN_OPT; |
| 377 | + break; |
| 378 | + case NFTA_TUNNEL_KEY_OPTS_ERSPAN: |
| 379 | + if (type) |
| 380 | + return -EINVAL; |
| 381 | + err = nft_tunnel_obj_erspan_init(nla, opts); |
| 382 | + if (err) |
| 383 | + return err; |
| 384 | + type = TUNNEL_ERSPAN_OPT; |
| 385 | + break; |
| 386 | + case NFTA_TUNNEL_KEY_OPTS_GENEVE: |
| 387 | + if (type && type != TUNNEL_GENEVE_OPT) |
| 388 | + return -EINVAL; |
| 389 | + err = nft_tunnel_obj_geneve_init(nla, opts); |
| 390 | + if (err) |
| 391 | + return err; |
| 392 | + type = TUNNEL_GENEVE_OPT; |
| 393 | + break; |
| 394 | + default: |
| 395 | + return -EOPNOTSUPP; |
| 396 | + } |
330 | 397 | }
|
331 | 398 |
|
332 | 399 | return err;
|
@@ -518,6 +585,25 @@ static int nft_tunnel_opts_dump(struct sk_buff *skb,
|
518 | 585 | break;
|
519 | 586 | }
|
520 | 587 | nla_nest_end(skb, inner);
|
| 588 | + } else if (opts->flags & TUNNEL_GENEVE_OPT) { |
| 589 | + struct geneve_opt *opt; |
| 590 | + int offset = 0; |
| 591 | + |
| 592 | + inner = nla_nest_start_noflag(skb, NFTA_TUNNEL_KEY_OPTS_GENEVE); |
| 593 | + if (!inner) |
| 594 | + goto failure; |
| 595 | + while (opts->len > offset) { |
| 596 | + opt = (struct geneve_opt *)opts->u.data + offset; |
| 597 | + if (nla_put_be16(skb, NFTA_TUNNEL_KEY_GENEVE_CLASS, |
| 598 | + opt->opt_class) || |
| 599 | + nla_put_u8(skb, NFTA_TUNNEL_KEY_GENEVE_TYPE, |
| 600 | + opt->type) || |
| 601 | + nla_put(skb, NFTA_TUNNEL_KEY_GENEVE_DATA, |
| 602 | + opt->length * 4, opt->opt_data)) |
| 603 | + goto inner_failure; |
| 604 | + offset += sizeof(*opt) + opt->length * 4; |
| 605 | + } |
| 606 | + nla_nest_end(skb, inner); |
521 | 607 | }
|
522 | 608 | nla_nest_end(skb, nest);
|
523 | 609 | return 0;
|
|
0 commit comments