diff options
| author | Joe Stringer <joe@wand.net.nz> | 2013-08-22 15:30:48 -0400 |
|---|---|---|
| committer | Jesse Gross <jesse@nicira.com> | 2013-08-26 17:03:13 -0400 |
| commit | a175a723301a8a4a9fedf9ce5b8ca586e7a97b40 (patch) | |
| tree | dcf29aa0043498e2e6ef231c38873345a21a7eff /net/openvswitch | |
| parent | 280c571e1a0f553d4f2384f1a8b643b5de26e0ec (diff) | |
openvswitch: Add SCTP support
This patch adds support for rewriting SCTP src,dst ports similar to the
functionality already available for TCP/UDP.
Rewriting SCTP ports is expensive due to double-recalculation of the
SCTP checksums; this is performed to ensure that packets traversing OVS
with invalid checksums will continue to the destination with any
checksum corruption intact.
Reviewed-by: Simon Horman <horms@verge.net.au>
Signed-off-by: Joe Stringer <joe@wand.net.nz>
Signed-off-by: Ben Pfaff <blp@nicira.com>
Signed-off-by: Jesse Gross <jesse@nicira.com>
Diffstat (limited to 'net/openvswitch')
| -rw-r--r-- | net/openvswitch/Kconfig | 1 | ||||
| -rw-r--r-- | net/openvswitch/actions.c | 39 | ||||
| -rw-r--r-- | net/openvswitch/datapath.c | 6 | ||||
| -rw-r--r-- | net/openvswitch/flow.c | 65 | ||||
| -rw-r--r-- | net/openvswitch/flow.h | 8 |
5 files changed, 115 insertions, 4 deletions
diff --git a/net/openvswitch/Kconfig b/net/openvswitch/Kconfig index bed30e69baa7..6ecf491ad509 100644 --- a/net/openvswitch/Kconfig +++ b/net/openvswitch/Kconfig | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | config OPENVSWITCH | 5 | config OPENVSWITCH |
| 6 | tristate "Open vSwitch" | 6 | tristate "Open vSwitch" |
| 7 | select LIBCRC32C | ||
| 7 | ---help--- | 8 | ---help--- |
| 8 | Open vSwitch is a multilayer Ethernet switch targeted at virtualized | 9 | Open vSwitch is a multilayer Ethernet switch targeted at virtualized |
| 9 | environments. In addition to supporting a variety of features | 10 | environments. In addition to supporting a variety of features |
diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 1f680222f4f7..65cfaa816075 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c | |||
| @@ -22,6 +22,7 @@ | |||
| 22 | #include <linux/in.h> | 22 | #include <linux/in.h> |
| 23 | #include <linux/ip.h> | 23 | #include <linux/ip.h> |
| 24 | #include <linux/openvswitch.h> | 24 | #include <linux/openvswitch.h> |
| 25 | #include <linux/sctp.h> | ||
| 25 | #include <linux/tcp.h> | 26 | #include <linux/tcp.h> |
| 26 | #include <linux/udp.h> | 27 | #include <linux/udp.h> |
| 27 | #include <linux/in6.h> | 28 | #include <linux/in6.h> |
| @@ -31,6 +32,7 @@ | |||
| 31 | #include <net/ipv6.h> | 32 | #include <net/ipv6.h> |
| 32 | #include <net/checksum.h> | 33 | #include <net/checksum.h> |
| 33 | #include <net/dsfield.h> | 34 | #include <net/dsfield.h> |
| 35 | #include <net/sctp/checksum.h> | ||
| 34 | 36 | ||
| 35 | #include "datapath.h" | 37 | #include "datapath.h" |
| 36 | #include "vport.h" | 38 | #include "vport.h" |
| @@ -352,6 +354,39 @@ static int set_tcp(struct sk_buff *skb, const struct ovs_key_tcp *tcp_port_key) | |||
| 352 | return 0; | 354 | return 0; |
| 353 | } | 355 | } |
| 354 | 356 | ||
| 357 | static int set_sctp(struct sk_buff *skb, | ||
| 358 | const struct ovs_key_sctp *sctp_port_key) | ||
| 359 | { | ||
| 360 | struct sctphdr *sh; | ||
| 361 | int err; | ||
| 362 | unsigned int sctphoff = skb_transport_offset(skb); | ||
| 363 | |||
| 364 | err = make_writable(skb, sctphoff + sizeof(struct sctphdr)); | ||
| 365 | if (unlikely(err)) | ||
| 366 | return err; | ||
| 367 | |||
| 368 | sh = sctp_hdr(skb); | ||
| 369 | if (sctp_port_key->sctp_src != sh->source || | ||
| 370 | sctp_port_key->sctp_dst != sh->dest) { | ||
| 371 | __le32 old_correct_csum, new_csum, old_csum; | ||
| 372 | |||
| 373 | old_csum = sh->checksum; | ||
| 374 | old_correct_csum = sctp_compute_cksum(skb, sctphoff); | ||
| 375 | |||
| 376 | sh->source = sctp_port_key->sctp_src; | ||
| 377 | sh->dest = sctp_port_key->sctp_dst; | ||
| 378 | |||
| 379 | new_csum = sctp_compute_cksum(skb, sctphoff); | ||
| 380 | |||
| 381 | /* Carry any checksum errors through. */ | ||
| 382 | sh->checksum = old_csum ^ old_correct_csum ^ new_csum; | ||
| 383 | |||
| 384 | skb->rxhash = 0; | ||
| 385 | } | ||
| 386 | |||
| 387 | return 0; | ||
| 388 | } | ||
| 389 | |||
| 355 | static int do_output(struct datapath *dp, struct sk_buff *skb, int out_port) | 390 | static int do_output(struct datapath *dp, struct sk_buff *skb, int out_port) |
| 356 | { | 391 | { |
| 357 | struct vport *vport; | 392 | struct vport *vport; |
| @@ -461,6 +496,10 @@ static int execute_set_action(struct sk_buff *skb, | |||
| 461 | case OVS_KEY_ATTR_UDP: | 496 | case OVS_KEY_ATTR_UDP: |
| 462 | err = set_udp(skb, nla_data(nested_attr)); | 497 | err = set_udp(skb, nla_data(nested_attr)); |
| 463 | break; | 498 | break; |
| 499 | |||
| 500 | case OVS_KEY_ATTR_SCTP: | ||
| 501 | err = set_sctp(skb, nla_data(nested_attr)); | ||
| 502 | break; | ||
| 464 | } | 503 | } |
| 465 | 504 | ||
| 466 | return err; | 505 | return err; |
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index d29cd9aa4a67..2aa13bd7f2b2 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c | |||
| @@ -712,6 +712,12 @@ static int validate_set(const struct nlattr *a, | |||
| 712 | 712 | ||
| 713 | return validate_tp_port(flow_key); | 713 | return validate_tp_port(flow_key); |
| 714 | 714 | ||
| 715 | case OVS_KEY_ATTR_SCTP: | ||
| 716 | if (flow_key->ip.proto != IPPROTO_SCTP) | ||
| 717 | return -EINVAL; | ||
| 718 | |||
| 719 | return validate_tp_port(flow_key); | ||
| 720 | |||
| 715 | default: | 721 | default: |
| 716 | return -EINVAL; | 722 | return -EINVAL; |
| 717 | } | 723 | } |
diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c index 1fceb9653598..2b4785590b56 100644 --- a/net/openvswitch/flow.c +++ b/net/openvswitch/flow.c | |||
| @@ -34,6 +34,7 @@ | |||
| 34 | #include <linux/if_arp.h> | 34 | #include <linux/if_arp.h> |
| 35 | #include <linux/ip.h> | 35 | #include <linux/ip.h> |
| 36 | #include <linux/ipv6.h> | 36 | #include <linux/ipv6.h> |
| 37 | #include <linux/sctp.h> | ||
| 37 | #include <linux/tcp.h> | 38 | #include <linux/tcp.h> |
| 38 | #include <linux/udp.h> | 39 | #include <linux/udp.h> |
| 39 | #include <linux/icmp.h> | 40 | #include <linux/icmp.h> |
| @@ -129,6 +130,7 @@ static bool ovs_match_validate(const struct sw_flow_match *match, | |||
| 129 | | (1 << OVS_KEY_ATTR_IPV6) | 130 | | (1 << OVS_KEY_ATTR_IPV6) |
| 130 | | (1 << OVS_KEY_ATTR_TCP) | 131 | | (1 << OVS_KEY_ATTR_TCP) |
| 131 | | (1 << OVS_KEY_ATTR_UDP) | 132 | | (1 << OVS_KEY_ATTR_UDP) |
| 133 | | (1 << OVS_KEY_ATTR_SCTP) | ||
| 132 | | (1 << OVS_KEY_ATTR_ICMP) | 134 | | (1 << OVS_KEY_ATTR_ICMP) |
| 133 | | (1 << OVS_KEY_ATTR_ICMPV6) | 135 | | (1 << OVS_KEY_ATTR_ICMPV6) |
| 134 | | (1 << OVS_KEY_ATTR_ARP) | 136 | | (1 << OVS_KEY_ATTR_ARP) |
| @@ -159,6 +161,12 @@ static bool ovs_match_validate(const struct sw_flow_match *match, | |||
| 159 | mask_allowed |= 1 << OVS_KEY_ATTR_UDP; | 161 | mask_allowed |= 1 << OVS_KEY_ATTR_UDP; |
| 160 | } | 162 | } |
| 161 | 163 | ||
| 164 | if (match->key->ip.proto == IPPROTO_SCTP) { | ||
| 165 | key_expected |= 1 << OVS_KEY_ATTR_SCTP; | ||
| 166 | if (match->mask && (match->mask->key.ip.proto == 0xff)) | ||
| 167 | mask_allowed |= 1 << OVS_KEY_ATTR_SCTP; | ||
| 168 | } | ||
| 169 | |||
| 162 | if (match->key->ip.proto == IPPROTO_TCP) { | 170 | if (match->key->ip.proto == IPPROTO_TCP) { |
| 163 | key_expected |= 1 << OVS_KEY_ATTR_TCP; | 171 | key_expected |= 1 << OVS_KEY_ATTR_TCP; |
| 164 | if (match->mask && (match->mask->key.ip.proto == 0xff)) | 172 | if (match->mask && (match->mask->key.ip.proto == 0xff)) |
| @@ -185,6 +193,12 @@ static bool ovs_match_validate(const struct sw_flow_match *match, | |||
| 185 | mask_allowed |= 1 << OVS_KEY_ATTR_UDP; | 193 | mask_allowed |= 1 << OVS_KEY_ATTR_UDP; |
| 186 | } | 194 | } |
| 187 | 195 | ||
| 196 | if (match->key->ip.proto == IPPROTO_SCTP) { | ||
| 197 | key_expected |= 1 << OVS_KEY_ATTR_SCTP; | ||
| 198 | if (match->mask && (match->mask->key.ip.proto == 0xff)) | ||
| 199 | mask_allowed |= 1 << OVS_KEY_ATTR_SCTP; | ||
| 200 | } | ||
| 201 | |||
| 188 | if (match->key->ip.proto == IPPROTO_TCP) { | 202 | if (match->key->ip.proto == IPPROTO_TCP) { |
| 189 | key_expected |= 1 << OVS_KEY_ATTR_TCP; | 203 | key_expected |= 1 << OVS_KEY_ATTR_TCP; |
| 190 | if (match->mask && (match->mask->key.ip.proto == 0xff)) | 204 | if (match->mask && (match->mask->key.ip.proto == 0xff)) |
| @@ -280,6 +294,12 @@ static bool udphdr_ok(struct sk_buff *skb) | |||
| 280 | sizeof(struct udphdr)); | 294 | sizeof(struct udphdr)); |
| 281 | } | 295 | } |
| 282 | 296 | ||
| 297 | static bool sctphdr_ok(struct sk_buff *skb) | ||
| 298 | { | ||
| 299 | return pskb_may_pull(skb, skb_transport_offset(skb) + | ||
| 300 | sizeof(struct sctphdr)); | ||
| 301 | } | ||
| 302 | |||
| 283 | static bool icmphdr_ok(struct sk_buff *skb) | 303 | static bool icmphdr_ok(struct sk_buff *skb) |
| 284 | { | 304 | { |
| 285 | return pskb_may_pull(skb, skb_transport_offset(skb) + | 305 | return pskb_may_pull(skb, skb_transport_offset(skb) + |
| @@ -891,6 +911,12 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key) | |||
| 891 | key->ipv4.tp.src = udp->source; | 911 | key->ipv4.tp.src = udp->source; |
| 892 | key->ipv4.tp.dst = udp->dest; | 912 | key->ipv4.tp.dst = udp->dest; |
| 893 | } | 913 | } |
| 914 | } else if (key->ip.proto == IPPROTO_SCTP) { | ||
| 915 | if (sctphdr_ok(skb)) { | ||
| 916 | struct sctphdr *sctp = sctp_hdr(skb); | ||
| 917 | key->ipv4.tp.src = sctp->source; | ||
| 918 | key->ipv4.tp.dst = sctp->dest; | ||
| 919 | } | ||
| 894 | } else if (key->ip.proto == IPPROTO_ICMP) { | 920 | } else if (key->ip.proto == IPPROTO_ICMP) { |
| 895 | if (icmphdr_ok(skb)) { | 921 | if (icmphdr_ok(skb)) { |
| 896 | struct icmphdr *icmp = icmp_hdr(skb); | 922 | struct icmphdr *icmp = icmp_hdr(skb); |
| @@ -953,6 +979,12 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key) | |||
| 953 | key->ipv6.tp.src = udp->source; | 979 | key->ipv6.tp.src = udp->source; |
| 954 | key->ipv6.tp.dst = udp->dest; | 980 | key->ipv6.tp.dst = udp->dest; |
| 955 | } | 981 | } |
| 982 | } else if (key->ip.proto == NEXTHDR_SCTP) { | ||
| 983 | if (sctphdr_ok(skb)) { | ||
| 984 | struct sctphdr *sctp = sctp_hdr(skb); | ||
| 985 | key->ipv6.tp.src = sctp->source; | ||
| 986 | key->ipv6.tp.dst = sctp->dest; | ||
| 987 | } | ||
| 956 | } else if (key->ip.proto == NEXTHDR_ICMP) { | 988 | } else if (key->ip.proto == NEXTHDR_ICMP) { |
| 957 | if (icmp6hdr_ok(skb)) { | 989 | if (icmp6hdr_ok(skb)) { |
| 958 | error = parse_icmpv6(skb, key, nh_len); | 990 | error = parse_icmpv6(skb, key, nh_len); |
| @@ -1087,6 +1119,7 @@ const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = { | |||
| 1087 | [OVS_KEY_ATTR_IPV6] = sizeof(struct ovs_key_ipv6), | 1119 | [OVS_KEY_ATTR_IPV6] = sizeof(struct ovs_key_ipv6), |
| 1088 | [OVS_KEY_ATTR_TCP] = sizeof(struct ovs_key_tcp), | 1120 | [OVS_KEY_ATTR_TCP] = sizeof(struct ovs_key_tcp), |
| 1089 | [OVS_KEY_ATTR_UDP] = sizeof(struct ovs_key_udp), | 1121 | [OVS_KEY_ATTR_UDP] = sizeof(struct ovs_key_udp), |
| 1122 | [OVS_KEY_ATTR_SCTP] = sizeof(struct ovs_key_sctp), | ||
| 1090 | [OVS_KEY_ATTR_ICMP] = sizeof(struct ovs_key_icmp), | 1123 | [OVS_KEY_ATTR_ICMP] = sizeof(struct ovs_key_icmp), |
| 1091 | [OVS_KEY_ATTR_ICMPV6] = sizeof(struct ovs_key_icmpv6), | 1124 | [OVS_KEY_ATTR_ICMPV6] = sizeof(struct ovs_key_icmpv6), |
| 1092 | [OVS_KEY_ATTR_ARP] = sizeof(struct ovs_key_arp), | 1125 | [OVS_KEY_ATTR_ARP] = sizeof(struct ovs_key_arp), |
| @@ -1500,6 +1533,24 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs, | |||
| 1500 | attrs &= ~(1 << OVS_KEY_ATTR_UDP); | 1533 | attrs &= ~(1 << OVS_KEY_ATTR_UDP); |
| 1501 | } | 1534 | } |
| 1502 | 1535 | ||
| 1536 | if (attrs & (1 << OVS_KEY_ATTR_SCTP)) { | ||
| 1537 | const struct ovs_key_sctp *sctp_key; | ||
| 1538 | |||
| 1539 | sctp_key = nla_data(a[OVS_KEY_ATTR_SCTP]); | ||
| 1540 | if (orig_attrs & (1 << OVS_KEY_ATTR_IPV4)) { | ||
| 1541 | SW_FLOW_KEY_PUT(match, ipv4.tp.src, | ||
| 1542 | sctp_key->sctp_src, is_mask); | ||
| 1543 | SW_FLOW_KEY_PUT(match, ipv4.tp.dst, | ||
| 1544 | sctp_key->sctp_dst, is_mask); | ||
| 1545 | } else { | ||
| 1546 | SW_FLOW_KEY_PUT(match, ipv6.tp.src, | ||
| 1547 | sctp_key->sctp_src, is_mask); | ||
| 1548 | SW_FLOW_KEY_PUT(match, ipv6.tp.dst, | ||
| 1549 | sctp_key->sctp_dst, is_mask); | ||
| 1550 | } | ||
| 1551 | attrs &= ~(1 << OVS_KEY_ATTR_SCTP); | ||
| 1552 | } | ||
| 1553 | |||
| 1503 | if (attrs & (1 << OVS_KEY_ATTR_ICMP)) { | 1554 | if (attrs & (1 << OVS_KEY_ATTR_ICMP)) { |
| 1504 | const struct ovs_key_icmp *icmp_key; | 1555 | const struct ovs_key_icmp *icmp_key; |
| 1505 | 1556 | ||
| @@ -1843,6 +1894,20 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, | |||
| 1843 | udp_key->udp_src = output->ipv6.tp.src; | 1894 | udp_key->udp_src = output->ipv6.tp.src; |
| 1844 | udp_key->udp_dst = output->ipv6.tp.dst; | 1895 | udp_key->udp_dst = output->ipv6.tp.dst; |
| 1845 | } | 1896 | } |
| 1897 | } else if (swkey->ip.proto == IPPROTO_SCTP) { | ||
| 1898 | struct ovs_key_sctp *sctp_key; | ||
| 1899 | |||
| 1900 | nla = nla_reserve(skb, OVS_KEY_ATTR_SCTP, sizeof(*sctp_key)); | ||
| 1901 | if (!nla) | ||
| 1902 | goto nla_put_failure; | ||
| 1903 | sctp_key = nla_data(nla); | ||
| 1904 | if (swkey->eth.type == htons(ETH_P_IP)) { | ||
| 1905 | sctp_key->sctp_src = swkey->ipv4.tp.src; | ||
| 1906 | sctp_key->sctp_dst = swkey->ipv4.tp.dst; | ||
| 1907 | } else if (swkey->eth.type == htons(ETH_P_IPV6)) { | ||
| 1908 | sctp_key->sctp_src = swkey->ipv6.tp.src; | ||
| 1909 | sctp_key->sctp_dst = swkey->ipv6.tp.dst; | ||
| 1910 | } | ||
| 1846 | } else if (swkey->eth.type == htons(ETH_P_IP) && | 1911 | } else if (swkey->eth.type == htons(ETH_P_IP) && |
| 1847 | swkey->ip.proto == IPPROTO_ICMP) { | 1912 | swkey->ip.proto == IPPROTO_ICMP) { |
| 1848 | struct ovs_key_icmp *icmp_key; | 1913 | struct ovs_key_icmp *icmp_key; |
diff --git a/net/openvswitch/flow.h b/net/openvswitch/flow.h index 9674e45f6969..d08dcf78dbf3 100644 --- a/net/openvswitch/flow.h +++ b/net/openvswitch/flow.h | |||
| @@ -99,8 +99,8 @@ struct sw_flow_key { | |||
| 99 | } addr; | 99 | } addr; |
| 100 | union { | 100 | union { |
| 101 | struct { | 101 | struct { |
| 102 | __be16 src; /* TCP/UDP source port. */ | 102 | __be16 src; /* TCP/UDP/SCTP source port. */ |
| 103 | __be16 dst; /* TCP/UDP destination port. */ | 103 | __be16 dst; /* TCP/UDP/SCTP destination port. */ |
| 104 | } tp; | 104 | } tp; |
| 105 | struct { | 105 | struct { |
| 106 | u8 sha[ETH_ALEN]; /* ARP source hardware address. */ | 106 | u8 sha[ETH_ALEN]; /* ARP source hardware address. */ |
| @@ -115,8 +115,8 @@ struct sw_flow_key { | |||
| 115 | } addr; | 115 | } addr; |
| 116 | __be32 label; /* IPv6 flow label. */ | 116 | __be32 label; /* IPv6 flow label. */ |
| 117 | struct { | 117 | struct { |
| 118 | __be16 src; /* TCP/UDP source port. */ | 118 | __be16 src; /* TCP/UDP/SCTP source port. */ |
| 119 | __be16 dst; /* TCP/UDP destination port. */ | 119 | __be16 dst; /* TCP/UDP/SCTP destination port. */ |
| 120 | } tp; | 120 | } tp; |
| 121 | struct { | 121 | struct { |
| 122 | struct in6_addr target; /* ND target address. */ | 122 | struct in6_addr target; /* ND target address. */ |
