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. */ |