diff options
Diffstat (limited to 'net/openvswitch')
-rw-r--r-- | net/openvswitch/Kconfig | 3 | ||||
-rw-r--r-- | net/openvswitch/conntrack.c | 8 | ||||
-rw-r--r-- | net/openvswitch/datapath.c | 4 | ||||
-rw-r--r-- | net/openvswitch/flow_netlink.c | 82 | ||||
-rw-r--r-- | net/openvswitch/flow_table.c | 23 | ||||
-rw-r--r-- | net/openvswitch/flow_table.h | 2 |
6 files changed, 81 insertions, 41 deletions
diff --git a/net/openvswitch/Kconfig b/net/openvswitch/Kconfig index 2a071f470d57..d143aa9f6654 100644 --- a/net/openvswitch/Kconfig +++ b/net/openvswitch/Kconfig | |||
@@ -5,7 +5,8 @@ | |||
5 | config OPENVSWITCH | 5 | config OPENVSWITCH |
6 | tristate "Open vSwitch" | 6 | tristate "Open vSwitch" |
7 | depends on INET | 7 | depends on INET |
8 | depends on (!NF_CONNTRACK || NF_CONNTRACK) | 8 | depends on !NF_CONNTRACK || \ |
9 | (NF_CONNTRACK && (!NF_DEFRAG_IPV6 || NF_DEFRAG_IPV6)) | ||
9 | select LIBCRC32C | 10 | select LIBCRC32C |
10 | select MPLS | 11 | select MPLS |
11 | select NET_MPLS_GSO | 12 | select NET_MPLS_GSO |
diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c index e8e524ad8a01..002a755fa07e 100644 --- a/net/openvswitch/conntrack.c +++ b/net/openvswitch/conntrack.c | |||
@@ -275,13 +275,15 @@ static int ovs_ct_helper(struct sk_buff *skb, u16 proto) | |||
275 | case NFPROTO_IPV6: { | 275 | case NFPROTO_IPV6: { |
276 | u8 nexthdr = ipv6_hdr(skb)->nexthdr; | 276 | u8 nexthdr = ipv6_hdr(skb)->nexthdr; |
277 | __be16 frag_off; | 277 | __be16 frag_off; |
278 | int ofs; | ||
278 | 279 | ||
279 | protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), | 280 | ofs = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr, |
280 | &nexthdr, &frag_off); | 281 | &frag_off); |
281 | if (protoff < 0 || (frag_off & htons(~0x7)) != 0) { | 282 | if (ofs < 0 || (frag_off & htons(~0x7)) != 0) { |
282 | pr_debug("proto header not found\n"); | 283 | pr_debug("proto header not found\n"); |
283 | return NF_ACCEPT; | 284 | return NF_ACCEPT; |
284 | } | 285 | } |
286 | protoff = ofs; | ||
285 | break; | 287 | break; |
286 | } | 288 | } |
287 | default: | 289 | default: |
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 6fbd2decb19e..b816ff871528 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c | |||
@@ -952,7 +952,7 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info) | |||
952 | if (error) | 952 | if (error) |
953 | goto err_kfree_flow; | 953 | goto err_kfree_flow; |
954 | 954 | ||
955 | ovs_flow_mask_key(&new_flow->key, &key, &mask); | 955 | ovs_flow_mask_key(&new_flow->key, &key, true, &mask); |
956 | 956 | ||
957 | /* Extract flow identifier. */ | 957 | /* Extract flow identifier. */ |
958 | error = ovs_nla_get_identifier(&new_flow->id, a[OVS_FLOW_ATTR_UFID], | 958 | error = ovs_nla_get_identifier(&new_flow->id, a[OVS_FLOW_ATTR_UFID], |
@@ -1080,7 +1080,7 @@ static struct sw_flow_actions *get_flow_actions(struct net *net, | |||
1080 | struct sw_flow_key masked_key; | 1080 | struct sw_flow_key masked_key; |
1081 | int error; | 1081 | int error; |
1082 | 1082 | ||
1083 | ovs_flow_mask_key(&masked_key, key, mask); | 1083 | ovs_flow_mask_key(&masked_key, key, true, mask); |
1084 | error = ovs_nla_copy_actions(net, a, &masked_key, &acts, log); | 1084 | error = ovs_nla_copy_actions(net, a, &masked_key, &acts, log); |
1085 | if (error) { | 1085 | if (error) { |
1086 | OVS_NLERR(log, | 1086 | OVS_NLERR(log, |
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)) |
diff --git a/net/openvswitch/flow_table.c b/net/openvswitch/flow_table.c index d22d8e948d0f..f2ea83ba4763 100644 --- a/net/openvswitch/flow_table.c +++ b/net/openvswitch/flow_table.c | |||
@@ -57,20 +57,21 @@ static u16 range_n_bytes(const struct sw_flow_key_range *range) | |||
57 | } | 57 | } |
58 | 58 | ||
59 | void ovs_flow_mask_key(struct sw_flow_key *dst, const struct sw_flow_key *src, | 59 | void ovs_flow_mask_key(struct sw_flow_key *dst, const struct sw_flow_key *src, |
60 | const struct sw_flow_mask *mask) | 60 | bool full, const struct sw_flow_mask *mask) |
61 | { | 61 | { |
62 | const long *m = (const long *)((const u8 *)&mask->key + | 62 | int start = full ? 0 : mask->range.start; |
63 | mask->range.start); | 63 | int len = full ? sizeof *dst : range_n_bytes(&mask->range); |
64 | const long *s = (const long *)((const u8 *)src + | 64 | const long *m = (const long *)((const u8 *)&mask->key + start); |
65 | mask->range.start); | 65 | const long *s = (const long *)((const u8 *)src + start); |
66 | long *d = (long *)((u8 *)dst + mask->range.start); | 66 | long *d = (long *)((u8 *)dst + start); |
67 | int i; | 67 | int i; |
68 | 68 | ||
69 | /* The memory outside of the 'mask->range' are not set since | 69 | /* If 'full' is true then all of 'dst' is fully initialized. Otherwise, |
70 | * further operations on 'dst' only uses contents within | 70 | * if 'full' is false the memory outside of the 'mask->range' is left |
71 | * 'mask->range'. | 71 | * uninitialized. This can be used as an optimization when further |
72 | * operations on 'dst' only use contents within 'mask->range'. | ||
72 | */ | 73 | */ |
73 | for (i = 0; i < range_n_bytes(&mask->range); i += sizeof(long)) | 74 | for (i = 0; i < len; i += sizeof(long)) |
74 | *d++ = *s++ & *m++; | 75 | *d++ = *s++ & *m++; |
75 | } | 76 | } |
76 | 77 | ||
@@ -475,7 +476,7 @@ static struct sw_flow *masked_flow_lookup(struct table_instance *ti, | |||
475 | u32 hash; | 476 | u32 hash; |
476 | struct sw_flow_key masked_key; | 477 | struct sw_flow_key masked_key; |
477 | 478 | ||
478 | ovs_flow_mask_key(&masked_key, unmasked, mask); | 479 | ovs_flow_mask_key(&masked_key, unmasked, false, mask); |
479 | hash = flow_hash(&masked_key, &mask->range); | 480 | hash = flow_hash(&masked_key, &mask->range); |
480 | head = find_bucket(ti, hash); | 481 | head = find_bucket(ti, hash); |
481 | hlist_for_each_entry_rcu(flow, head, flow_table.node[ti->node_ver]) { | 482 | hlist_for_each_entry_rcu(flow, head, flow_table.node[ti->node_ver]) { |
diff --git a/net/openvswitch/flow_table.h b/net/openvswitch/flow_table.h index 616eda10d955..2dd9900f533d 100644 --- a/net/openvswitch/flow_table.h +++ b/net/openvswitch/flow_table.h | |||
@@ -86,5 +86,5 @@ struct sw_flow *ovs_flow_tbl_lookup_ufid(struct flow_table *, | |||
86 | bool ovs_flow_cmp(const struct sw_flow *, const struct sw_flow_match *); | 86 | bool ovs_flow_cmp(const struct sw_flow *, const struct sw_flow_match *); |
87 | 87 | ||
88 | void ovs_flow_mask_key(struct sw_flow_key *dst, const struct sw_flow_key *src, | 88 | void ovs_flow_mask_key(struct sw_flow_key *dst, const struct sw_flow_key *src, |
89 | const struct sw_flow_mask *mask); | 89 | bool full, const struct sw_flow_mask *mask); |
90 | #endif /* flow_table.h */ | 90 | #endif /* flow_table.h */ |