diff options
author | Jesse Gross <jesse@nicira.com> | 2015-09-11 21:38:28 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-09-15 19:25:41 -0400 |
commit | 982b527004826b40de1e821b123c70f05b41496c (patch) | |
tree | 7c706b19116441b2aaeb4e6fa1737464b16f4d18 /net | |
parent | 892aa01df2ad67237f213c8f9d9b491e908aa910 (diff) |
openvswitch: Fix mask generation for nested attributes.
Masks were added to OVS flows in a way that was backwards compatible
with userspace programs that did not generate masks. As a result, it is
possible that we may receive flows that do not have a mask and we need
to synthesize one.
Generating a mask requires iterating over attributes and descending into
nested attributes. For each level we need to know the size to generate the
correct mask. We do this with a linked table of attribute types.
Although the logic to handle these nested attributes was there in concept,
there are a number of bugs in practice. Examples include incomplete links
between tables, variable length attributes being treated as nested and
missing sanity checks.
Signed-off-by: Jesse Gross <jesse@nicira.com>
Acked-by: Pravin B Shelar <pshelar@nicira.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/openvswitch/flow_netlink.c | 82 |
1 files changed, 59 insertions, 23 deletions
diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index c92d6a262bc5..5c030a4d7338 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c | |||
@@ -57,6 +57,7 @@ struct ovs_len_tbl { | |||
57 | }; | 57 | }; |
58 | 58 | ||
59 | #define OVS_ATTR_NESTED -1 | 59 | #define OVS_ATTR_NESTED -1 |
60 | #define OVS_ATTR_VARIABLE -2 | ||
60 | 61 | ||
61 | static void update_range(struct sw_flow_match *match, | 62 | static void update_range(struct sw_flow_match *match, |
62 | size_t offset, size_t size, bool is_mask) | 63 | size_t offset, size_t size, bool is_mask) |
@@ -304,6 +305,10 @@ size_t ovs_key_attr_size(void) | |||
304 | + nla_total_size(28); /* OVS_KEY_ATTR_ND */ | 305 | + nla_total_size(28); /* OVS_KEY_ATTR_ND */ |
305 | } | 306 | } |
306 | 307 | ||
308 | static const struct ovs_len_tbl ovs_vxlan_ext_key_lens[OVS_VXLAN_EXT_MAX + 1] = { | ||
309 | [OVS_VXLAN_EXT_GBP] = { .len = sizeof(u32) }, | ||
310 | }; | ||
311 | |||
307 | static const struct ovs_len_tbl ovs_tunnel_key_lens[OVS_TUNNEL_KEY_ATTR_MAX + 1] = { | 312 | static const struct ovs_len_tbl ovs_tunnel_key_lens[OVS_TUNNEL_KEY_ATTR_MAX + 1] = { |
308 | [OVS_TUNNEL_KEY_ATTR_ID] = { .len = sizeof(u64) }, | 313 | [OVS_TUNNEL_KEY_ATTR_ID] = { .len = sizeof(u64) }, |
309 | [OVS_TUNNEL_KEY_ATTR_IPV4_SRC] = { .len = sizeof(u32) }, | 314 | [OVS_TUNNEL_KEY_ATTR_IPV4_SRC] = { .len = sizeof(u32) }, |
@@ -315,8 +320,9 @@ static const struct ovs_len_tbl ovs_tunnel_key_lens[OVS_TUNNEL_KEY_ATTR_MAX + 1] | |||
315 | [OVS_TUNNEL_KEY_ATTR_TP_SRC] = { .len = sizeof(u16) }, | 320 | [OVS_TUNNEL_KEY_ATTR_TP_SRC] = { .len = sizeof(u16) }, |
316 | [OVS_TUNNEL_KEY_ATTR_TP_DST] = { .len = sizeof(u16) }, | 321 | [OVS_TUNNEL_KEY_ATTR_TP_DST] = { .len = sizeof(u16) }, |
317 | [OVS_TUNNEL_KEY_ATTR_OAM] = { .len = 0 }, | 322 | [OVS_TUNNEL_KEY_ATTR_OAM] = { .len = 0 }, |
318 | [OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS] = { .len = OVS_ATTR_NESTED }, | 323 | [OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS] = { .len = OVS_ATTR_VARIABLE }, |
319 | [OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS] = { .len = OVS_ATTR_NESTED }, | 324 | [OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS] = { .len = OVS_ATTR_NESTED, |
325 | .next = ovs_vxlan_ext_key_lens }, | ||
320 | }; | 326 | }; |
321 | 327 | ||
322 | /* The size of the argument for each %OVS_KEY_ATTR_* Netlink attribute. */ | 328 | /* The size of the argument for each %OVS_KEY_ATTR_* Netlink attribute. */ |
@@ -349,6 +355,13 @@ static const struct ovs_len_tbl ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = { | |||
349 | [OVS_KEY_ATTR_CT_LABEL] = { .len = sizeof(struct ovs_key_ct_label) }, | 355 | [OVS_KEY_ATTR_CT_LABEL] = { .len = sizeof(struct ovs_key_ct_label) }, |
350 | }; | 356 | }; |
351 | 357 | ||
358 | static bool check_attr_len(unsigned int attr_len, unsigned int expected_len) | ||
359 | { | ||
360 | return expected_len == attr_len || | ||
361 | expected_len == OVS_ATTR_NESTED || | ||
362 | expected_len == OVS_ATTR_VARIABLE; | ||
363 | } | ||
364 | |||
352 | static bool is_all_zero(const u8 *fp, size_t size) | 365 | static bool is_all_zero(const u8 *fp, size_t size) |
353 | { | 366 | { |
354 | int i; | 367 | int i; |
@@ -388,7 +401,7 @@ static int __parse_flow_nlattrs(const struct nlattr *attr, | |||
388 | } | 401 | } |
389 | 402 | ||
390 | expected_len = ovs_key_lens[type].len; | 403 | expected_len = ovs_key_lens[type].len; |
391 | if (nla_len(nla) != expected_len && expected_len != OVS_ATTR_NESTED) { | 404 | if (!check_attr_len(nla_len(nla), expected_len)) { |
392 | OVS_NLERR(log, "Key %d has unexpected len %d expected %d", | 405 | OVS_NLERR(log, "Key %d has unexpected len %d expected %d", |
393 | type, nla_len(nla), expected_len); | 406 | type, nla_len(nla), expected_len); |
394 | return -EINVAL; | 407 | return -EINVAL; |
@@ -473,29 +486,50 @@ static int genev_tun_opt_from_nlattr(const struct nlattr *a, | |||
473 | return 0; | 486 | return 0; |
474 | } | 487 | } |
475 | 488 | ||
476 | static const struct nla_policy vxlan_opt_policy[OVS_VXLAN_EXT_MAX + 1] = { | 489 | static int vxlan_tun_opt_from_nlattr(const struct nlattr *attr, |
477 | [OVS_VXLAN_EXT_GBP] = { .type = NLA_U32 }, | ||
478 | }; | ||
479 | |||
480 | static int vxlan_tun_opt_from_nlattr(const struct nlattr *a, | ||
481 | struct sw_flow_match *match, bool is_mask, | 490 | struct sw_flow_match *match, bool is_mask, |
482 | bool log) | 491 | bool log) |
483 | { | 492 | { |
484 | struct nlattr *tb[OVS_VXLAN_EXT_MAX+1]; | 493 | struct nlattr *a; |
494 | int rem; | ||
485 | unsigned long opt_key_offset; | 495 | unsigned long opt_key_offset; |
486 | struct vxlan_metadata opts; | 496 | struct vxlan_metadata opts; |
487 | int err; | ||
488 | 497 | ||
489 | BUILD_BUG_ON(sizeof(opts) > sizeof(match->key->tun_opts)); | 498 | BUILD_BUG_ON(sizeof(opts) > sizeof(match->key->tun_opts)); |
490 | 499 | ||
491 | err = nla_parse_nested(tb, OVS_VXLAN_EXT_MAX, a, vxlan_opt_policy); | ||
492 | if (err < 0) | ||
493 | return err; | ||
494 | |||
495 | memset(&opts, 0, sizeof(opts)); | 500 | memset(&opts, 0, sizeof(opts)); |
501 | nla_for_each_nested(a, attr, rem) { | ||
502 | int type = nla_type(a); | ||
496 | 503 | ||
497 | if (tb[OVS_VXLAN_EXT_GBP]) | 504 | if (type > OVS_VXLAN_EXT_MAX) { |
498 | opts.gbp = nla_get_u32(tb[OVS_VXLAN_EXT_GBP]); | 505 | OVS_NLERR(log, "VXLAN extension %d out of range max %d", |
506 | type, OVS_VXLAN_EXT_MAX); | ||
507 | return -EINVAL; | ||
508 | } | ||
509 | |||
510 | if (!check_attr_len(nla_len(a), | ||
511 | ovs_vxlan_ext_key_lens[type].len)) { | ||
512 | OVS_NLERR(log, "VXLAN extension %d has unexpected len %d expected %d", | ||
513 | type, nla_len(a), | ||
514 | ovs_vxlan_ext_key_lens[type].len); | ||
515 | return -EINVAL; | ||
516 | } | ||
517 | |||
518 | switch (type) { | ||
519 | case OVS_VXLAN_EXT_GBP: | ||
520 | opts.gbp = nla_get_u32(a); | ||
521 | break; | ||
522 | default: | ||
523 | OVS_NLERR(log, "Unknown VXLAN extension attribute %d", | ||
524 | type); | ||
525 | return -EINVAL; | ||
526 | } | ||
527 | } | ||
528 | if (rem) { | ||
529 | OVS_NLERR(log, "VXLAN extension message has %d unknown bytes.", | ||
530 | rem); | ||
531 | return -EINVAL; | ||
532 | } | ||
499 | 533 | ||
500 | if (!is_mask) | 534 | if (!is_mask) |
501 | SW_FLOW_KEY_PUT(match, tun_opts_len, sizeof(opts), false); | 535 | SW_FLOW_KEY_PUT(match, tun_opts_len, sizeof(opts), false); |
@@ -528,8 +562,8 @@ static int ipv4_tun_from_nlattr(const struct nlattr *attr, | |||
528 | return -EINVAL; | 562 | return -EINVAL; |
529 | } | 563 | } |
530 | 564 | ||
531 | if (ovs_tunnel_key_lens[type].len != nla_len(a) && | 565 | if (!check_attr_len(nla_len(a), |
532 | ovs_tunnel_key_lens[type].len != OVS_ATTR_NESTED) { | 566 | ovs_tunnel_key_lens[type].len)) { |
533 | OVS_NLERR(log, "Tunnel attr %d has unexpected len %d expected %d", | 567 | OVS_NLERR(log, "Tunnel attr %d has unexpected len %d expected %d", |
534 | type, nla_len(a), ovs_tunnel_key_lens[type].len); | 568 | type, nla_len(a), ovs_tunnel_key_lens[type].len); |
535 | return -EINVAL; | 569 | return -EINVAL; |
@@ -1052,10 +1086,13 @@ static void nlattr_set(struct nlattr *attr, u8 val, | |||
1052 | 1086 | ||
1053 | /* The nlattr stream should already have been validated */ | 1087 | /* The nlattr stream should already have been validated */ |
1054 | nla_for_each_nested(nla, attr, rem) { | 1088 | nla_for_each_nested(nla, attr, rem) { |
1055 | if (tbl && tbl[nla_type(nla)].len == OVS_ATTR_NESTED) | 1089 | if (tbl[nla_type(nla)].len == OVS_ATTR_NESTED) { |
1056 | nlattr_set(nla, val, tbl[nla_type(nla)].next); | 1090 | if (tbl[nla_type(nla)].next) |
1057 | else | 1091 | tbl = tbl[nla_type(nla)].next; |
1092 | nlattr_set(nla, val, tbl); | ||
1093 | } else { | ||
1058 | memset(nla_data(nla), val, nla_len(nla)); | 1094 | memset(nla_data(nla), val, nla_len(nla)); |
1095 | } | ||
1059 | } | 1096 | } |
1060 | } | 1097 | } |
1061 | 1098 | ||
@@ -1922,8 +1959,7 @@ static int validate_set(const struct nlattr *a, | |||
1922 | key_len /= 2; | 1959 | key_len /= 2; |
1923 | 1960 | ||
1924 | if (key_type > OVS_KEY_ATTR_MAX || | 1961 | if (key_type > OVS_KEY_ATTR_MAX || |
1925 | (ovs_key_lens[key_type].len != key_len && | 1962 | !check_attr_len(key_len, ovs_key_lens[key_type].len)) |
1926 | ovs_key_lens[key_type].len != OVS_ATTR_NESTED)) | ||
1927 | return -EINVAL; | 1963 | return -EINVAL; |
1928 | 1964 | ||
1929 | if (masked && !validate_masked(nla_data(ovs_key), key_len)) | 1965 | if (masked && !validate_masked(nla_data(ovs_key), key_len)) |