Reviewed-by: Simon Horman <ho...@verge.net.au> Signed-off-by: Joe Stringer <j...@wand.net.nz> --- v3: Rebase v2: Fix broken test Use the correct packet pointer Calculate checksums as delta from incoming checksum --- include/linux/openvswitch.h | 6 +++++ include/sparse/netinet/in.h | 1 + lib/flow.c | 33 ++++++++++++++++++++++++--- lib/flow.h | 4 ++-- lib/odp-execute.c | 6 +++++ lib/odp-util.c | 53 +++++++++++++++++++++++++++++++++++++++++++ lib/packets.c | 22 ++++++++++++++++++ lib/packets.h | 10 ++++++++ 8 files changed, 130 insertions(+), 5 deletions(-)
diff --git a/include/linux/openvswitch.h b/include/linux/openvswitch.h index e890fd8..9f5aa82 100644 --- a/include/linux/openvswitch.h +++ b/include/linux/openvswitch.h @@ -282,6 +282,7 @@ enum ovs_key_attr { OVS_KEY_ATTR_ND, /* struct ovs_key_nd */ OVS_KEY_ATTR_SKB_MARK, /* u32 skb mark */ OVS_KEY_ATTR_TUNNEL, /* Nested set of ovs_tunnel attributes */ + OVS_KEY_ATTR_SCTP, /* struct ovs_key_sctp */ #ifdef __KERNEL__ OVS_KEY_ATTR_IPV4_TUNNEL, /* struct ovs_key_ipv4_tunnel */ @@ -364,6 +365,11 @@ struct ovs_key_udp { __be16 udp_dst; }; +struct ovs_key_sctp { + __be16 sctp_src; + __be16 sctp_dst; +}; + struct ovs_key_icmp { __u8 icmp_type; __u8 icmp_code; diff --git a/include/sparse/netinet/in.h b/include/sparse/netinet/in.h index b3924c3..6082932 100644 --- a/include/sparse/netinet/in.h +++ b/include/sparse/netinet/in.h @@ -59,6 +59,7 @@ extern const struct in6_addr in6addr_any; #define IPPROTO_ICMPV6 58 #define IPPROTO_NONE 59 #define IPPROTO_DSTOPTS 60 +#define IPPROTO_SCTP 132 /* All the IP options documented in Linux ip(7). */ #define IP_ADD_MEMBERSHIP 0 diff --git a/lib/flow.c b/lib/flow.c index d38e3ab..36efa20 100644 --- a/lib/flow.c +++ b/lib/flow.c @@ -81,6 +81,12 @@ pull_udp(struct ofpbuf *packet) return ofpbuf_try_pull(packet, UDP_HEADER_LEN); } +static struct sctp_header * +pull_sctp(struct ofpbuf *packet) +{ + return ofpbuf_try_pull(packet, SCTP_HEADER_LEN); +} + static struct icmp_header * pull_icmp(struct ofpbuf *packet) { @@ -265,6 +271,17 @@ parse_udp(struct ofpbuf *packet, struct ofpbuf *b, struct flow *flow) } } +static void +parse_sctp(struct ofpbuf *packet, struct ofpbuf *b, struct flow *flow) +{ + const struct sctp_header *sctp = pull_sctp(b); + if (sctp) { + flow->tp_src = sctp->sctp_src; + flow->tp_dst = sctp->sctp_dst; + packet->l7 = b->data; + } +} + static bool parse_icmpv6(struct ofpbuf *b, struct flow *flow) { @@ -352,7 +369,7 @@ invalid: * - packet->l4 to just past the IPv4 header, if one is present and has a * correct length, and otherwise NULL. * - * - packet->l7 to just past the TCP or UDP or ICMP header, if one is + * - packet->l7 to just past the TCP/UDP/SCTP/ICMP header, if one is * present and has a correct length, and otherwise NULL. */ void @@ -428,6 +445,8 @@ flow_extract(struct ofpbuf *packet, uint32_t skb_priority, uint32_t skb_mark, parse_tcp(packet, &b, flow); } else if (flow->nw_proto == IPPROTO_UDP) { parse_udp(packet, &b, flow); + } else if (flow->nw_proto == IPPROTO_SCTP) { + parse_sctp(packet, &b, flow); } else if (flow->nw_proto == IPPROTO_ICMP) { const struct icmp_header *icmp = pull_icmp(&b); if (icmp) { @@ -448,6 +467,8 @@ flow_extract(struct ofpbuf *packet, uint32_t skb_priority, uint32_t skb_mark, parse_tcp(packet, &b, flow); } else if (flow->nw_proto == IPPROTO_UDP) { parse_udp(packet, &b, flow); + } else if (flow->nw_proto == IPPROTO_SCTP) { + parse_sctp(packet, &b, flow); } else if (flow->nw_proto == IPPROTO_ICMPV6) { if (parse_icmpv6(&b, flow)) { packet->l7 = b.data; @@ -758,7 +779,7 @@ flow_hash_symmetric_l4(const struct flow *flow, uint32_t basis) if (fields.eth_type == htons(ETH_TYPE_IP)) { fields.ipv4_addr = flow->nw_src ^ flow->nw_dst; fields.ip_proto = flow->nw_proto; - if (fields.ip_proto == IPPROTO_TCP) { + if (fields.ip_proto == IPPROTO_TCP || fields.ip_proto == IPPROTO_SCTP) { fields.tp_port = flow->tp_src ^ flow->tp_dst; } } else if (fields.eth_type == htons(ETH_TYPE_IPV6)) { @@ -770,7 +791,7 @@ flow_hash_symmetric_l4(const struct flow *flow, uint32_t basis) ipv6_addr[i] = a[i] ^ b[i]; } fields.ip_proto = flow->nw_proto; - if (fields.ip_proto == IPPROTO_TCP) { + if (fields.ip_proto == IPPROTO_TCP || fields.ip_proto == IPPROTO_SCTP) { fields.tp_port = flow->tp_src ^ flow->tp_dst; } } @@ -989,6 +1010,12 @@ flow_compose(struct ofpbuf *b, const struct flow *flow) b->l4 = udp = ofpbuf_put_zeros(b, sizeof *udp); udp->udp_src = flow->tp_src; udp->udp_dst = flow->tp_dst; + } else if (flow->nw_proto == IPPROTO_SCTP) { + struct sctp_header *sctp; + + b->l4 = sctp = ofpbuf_put_zeros(b, sizeof *sctp); + sctp->sctp_src = flow->tp_src; + sctp->sctp_dst = flow->tp_dst; } else if (flow->nw_proto == IPPROTO_ICMP) { struct icmp_header *icmp; diff --git a/lib/flow.h b/lib/flow.h index b07b9ed..79f2380 100644 --- a/lib/flow.h +++ b/lib/flow.h @@ -95,8 +95,8 @@ struct flow { uint16_t mpls_depth; /* Depth of MPLS stack. */ ovs_be16 vlan_tci; /* If 802.1Q, TCI | VLAN_CFI; otherwise 0. */ ovs_be16 dl_type; /* Ethernet frame type. */ - ovs_be16 tp_src; /* TCP/UDP source port. */ - ovs_be16 tp_dst; /* TCP/UDP destination port. */ + ovs_be16 tp_src; /* TCP/UDP/SCTP source port. */ + ovs_be16 tp_dst; /* TCP/UDP/SCTP destination port. */ uint8_t dl_src[6]; /* Ethernet source address. */ uint8_t dl_dst[6]; /* Ethernet destination address. */ uint8_t nw_proto; /* IP protocol or low 8 bits of ARP opcode. */ diff --git a/lib/odp-execute.c b/lib/odp-execute.c index e6e8c91..e51a1cd 100644 --- a/lib/odp-execute.c +++ b/lib/odp-execute.c @@ -54,6 +54,7 @@ odp_execute_set_action(struct ofpbuf *packet, const struct nlattr *a, const struct ovs_key_ipv6 *ipv6_key; const struct ovs_key_tcp *tcp_key; const struct ovs_key_udp *udp_key; + const struct ovs_key_sctp *sctp_key; switch (type) { case OVS_KEY_ATTR_PRIORITY: @@ -96,6 +97,11 @@ odp_execute_set_action(struct ofpbuf *packet, const struct nlattr *a, packet_set_udp_port(packet, udp_key->udp_src, udp_key->udp_dst); break; + case OVS_KEY_ATTR_SCTP: + sctp_key = nl_attr_get_unspec(a, sizeof(struct ovs_key_sctp)); + packet_set_sctp_port(packet, sctp_key->sctp_src, sctp_key->sctp_dst); + break; + case OVS_KEY_ATTR_MPLS: set_mpls_lse(packet, nl_attr_get_be32(a)); break; diff --git a/lib/odp-util.c b/lib/odp-util.c index acd1a9d..986cdd5 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -107,6 +107,7 @@ ovs_key_attr_to_string(enum ovs_key_attr attr, char *namebuf, size_t bufsize) case OVS_KEY_ATTR_IPV6: return "ipv6"; case OVS_KEY_ATTR_TCP: return "tcp"; case OVS_KEY_ATTR_UDP: return "udp"; + case OVS_KEY_ATTR_SCTP: return "sctp"; case OVS_KEY_ATTR_ICMP: return "icmp"; case OVS_KEY_ATTR_ICMPV6: return "icmpv6"; case OVS_KEY_ATTR_ARP: return "arp"; @@ -724,6 +725,7 @@ odp_flow_key_attr_len(uint16_t type) case OVS_KEY_ATTR_IPV6: return sizeof(struct ovs_key_ipv6); case OVS_KEY_ATTR_TCP: return sizeof(struct ovs_key_tcp); case OVS_KEY_ATTR_UDP: return sizeof(struct ovs_key_udp); + case OVS_KEY_ATTR_SCTP: return sizeof(struct ovs_key_sctp); case OVS_KEY_ATTR_ICMP: return sizeof(struct ovs_key_icmp); case OVS_KEY_ATTR_ICMPV6: return sizeof(struct ovs_key_icmpv6); case OVS_KEY_ATTR_ARP: return sizeof(struct ovs_key_arp); @@ -883,6 +885,7 @@ format_odp_key_attr(const struct nlattr *a, struct ds *ds) const struct ovs_key_ipv6 *ipv6_key; const struct ovs_key_tcp *tcp_key; const struct ovs_key_udp *udp_key; + const struct ovs_key_sctp *sctp_key; const struct ovs_key_icmp *icmp_key; const struct ovs_key_icmpv6 *icmpv6_key; const struct ovs_key_arp *arp_key; @@ -1007,6 +1010,12 @@ format_odp_key_attr(const struct nlattr *a, struct ds *ds) ntohs(udp_key->udp_src), ntohs(udp_key->udp_dst)); break; + case OVS_KEY_ATTR_SCTP: + sctp_key = nl_attr_get(a); + ds_put_format(ds, "(src=%"PRIu16",dst=%"PRIu16")", + ntohs(sctp_key->sctp_src), ntohs(sctp_key->sctp_dst)); + break; + case OVS_KEY_ATTR_ICMP: icmp_key = nl_attr_get(a); ds_put_format(ds, "(type=%"PRIu8",code=%"PRIu8")", @@ -1384,6 +1393,23 @@ parse_odp_key_attr(const char *s, const struct simap *port_names, } { + int sctp_src; + int sctp_dst; + int n = -1; + + if (sscanf(s, "sctp(src=%i,dst=%i)%n", &sctp_src, &sctp_dst, &n) > 0 + && n > 0) { + struct ovs_key_sctp sctp_key; + + sctp_key.sctp_src = htons(sctp_src); + sctp_key.sctp_dst = htons(sctp_dst); + nl_msg_put_unspec(key, OVS_KEY_ATTR_SCTP, + &sctp_key, sizeof sctp_key); + return n; + } + } + + { int icmp_type; int icmp_code; int n = -1; @@ -1663,6 +1689,13 @@ odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow, sizeof *udp_key); udp_key->udp_src = flow->tp_src; udp_key->udp_dst = flow->tp_dst; + } else if (flow->nw_proto == IPPROTO_SCTP) { + struct ovs_key_sctp *sctp_key; + + sctp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_SCTP, + sizeof *sctp_key); + sctp_key->sctp_src = flow->tp_src; + sctp_key->sctp_dst = flow->tp_dst; } else if (flow->dl_type == htons(ETH_TYPE_IP) && flow->nw_proto == IPPROTO_ICMP) { struct ovs_key_icmp *icmp_key; @@ -1950,6 +1983,18 @@ parse_l2_5_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1], flow->tp_src = udp_key->udp_src; flow->tp_dst = udp_key->udp_dst; } + } else if (flow->nw_proto == IPPROTO_SCTP + && (flow->dl_type == htons(ETH_TYPE_IP) || + flow->dl_type == htons(ETH_TYPE_IPV6)) + && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) { + expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_SCTP; + if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_SCTP)) { + const struct ovs_key_sctp *sctp_key; + + sctp_key = nl_attr_get(attrs[OVS_KEY_ATTR_SCTP]); + flow->tp_src = sctp_key->sctp_src; + flow->tp_dst = sctp_key->sctp_dst; + } } else if (flow->nw_proto == IPPROTO_ICMP && flow->dl_type == htons(ETH_TYPE_IP) && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) { @@ -2420,6 +2465,14 @@ commit_set_port_action(const struct flow *flow, struct flow *base, commit_set_action(odp_actions, OVS_KEY_ATTR_UDP, &port_key, sizeof(port_key)); + } else if (flow->nw_proto == IPPROTO_SCTP) { + struct ovs_key_sctp port_key; + + port_key.sctp_src = base->tp_src = flow->tp_src; + port_key.sctp_dst = base->tp_dst = flow->tp_dst; + + commit_set_action(odp_actions, OVS_KEY_ATTR_SCTP, + &port_key, sizeof(port_key)); } } diff --git a/lib/packets.c b/lib/packets.c index 7fe6513..6093a67 100644 --- a/lib/packets.c +++ b/lib/packets.c @@ -23,6 +23,7 @@ #include <stdlib.h> #include "byte-order.h" #include "csum.h" +#include "crc32c.h" #include "flow.h" #include "hmap.h" #include "dynamic-string.h" @@ -879,6 +880,27 @@ packet_set_udp_port(struct ofpbuf *packet, ovs_be16 src, ovs_be16 dst) } } +/* Sets the SCTP source and destination port ('src' and 'dst' respectively) of + * the SCTP header contained in 'packet'. 'packet' must be a valid SCTP packet + * with its l4 marker properly populated. */ +void +packet_set_sctp_port(struct ofpbuf *packet, ovs_be16 src, ovs_be16 dst) +{ + struct sctp_header *sh = packet->l4; + ovs_be32 old_csum, old_correct_csum, new_csum; + uint16_t tp_len = packet->size - ((uint8_t*)sh - (uint8_t*)packet->data); + + old_csum = sh->sctp_csum; + sh->sctp_csum = 0; + old_correct_csum = crc32c(packet->l4, tp_len); + + sh->sctp_src = src; + sh->sctp_dst = dst; + + new_csum = crc32c(packet->l4, tp_len); + sh->sctp_csum = old_csum ^ old_correct_csum ^ new_csum; +} + /* If 'packet' is a TCP packet, returns the TCP flags. Otherwise, returns 0. * * 'flow' must be the flow corresponding to 'packet' and 'packet''s header diff --git a/lib/packets.h b/lib/packets.h index b73ff63..53d0214 100644 --- a/lib/packets.h +++ b/lib/packets.h @@ -454,6 +454,15 @@ struct icmp_header { }; BUILD_ASSERT_DECL(ICMP_HEADER_LEN == sizeof(struct icmp_header)); +#define SCTP_HEADER_LEN 12 +struct sctp_header { + ovs_be16 sctp_src; + ovs_be16 sctp_dst; + ovs_be32 sctp_vtag; + ovs_be32 sctp_csum; +}; +BUILD_ASSERT_DECL(SCTP_HEADER_LEN == sizeof(struct sctp_header)); + #define UDP_HEADER_LEN 8 struct udp_header { ovs_be16 udp_src; @@ -583,6 +592,7 @@ void packet_set_ipv6(struct ofpbuf *, uint8_t proto, const ovs_be32 src[4], ovs_be32 fl, uint8_t hlmit); void packet_set_tcp_port(struct ofpbuf *, ovs_be16 src, ovs_be16 dst); void packet_set_udp_port(struct ofpbuf *, ovs_be16 src, ovs_be16 dst); +void packet_set_sctp_port(struct ofpbuf *, ovs_be16 src, ovs_be16 dst); uint8_t packet_get_tcp_flags(const struct ofpbuf *, const struct flow *); void packet_format_tcp_flags(struct ds *, uint8_t); -- 1.7.10.4 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev