Reviewed-by: Simon Horman <ho...@verge.net.au> Signed-off-by: Joe Stringer <j...@wand.net.nz> --- v4: Rebase against megaflow Move openvswitch.h header changes to datapath patch v3: Rebase v2: Fix broken test Use the correct packet pointer Calculate checksums as delta from incoming checksum --- include/sparse/netinet/in.h | 1 + lib/flow.c | 33 +++++++++++++++-- lib/flow.h | 4 +- lib/odp-execute.c | 6 +++ lib/odp-util.c | 85 +++++++++++++++++++++++++++++++++++++++++++ lib/packets.c | 22 +++++++++++ lib/packets.h | 10 +++++ 7 files changed, 156 insertions(+), 5 deletions(-)
diff --git a/include/sparse/netinet/in.h b/include/sparse/netinet/in.h index 87d48d6..781358b 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 d899d26..f373956 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 @@ -430,6 +447,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) { @@ -450,6 +469,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; @@ -760,7 +781,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)) { @@ -772,7 +793,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; } } @@ -998,6 +1019,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 7c3654b..a4ab7aa 100644 --- a/lib/flow.h +++ b/lib/flow.h @@ -101,8 +101,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 3c3063d..cc4acc0 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -108,6 +108,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"; @@ -745,6 +746,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); @@ -1198,6 +1200,23 @@ format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma, } break; + case OVS_KEY_ATTR_SCTP: + if (ma) { + const struct ovs_key_sctp *sctp_mask = nl_attr_get(ma); + const struct ovs_key_sctp *sctp_key = nl_attr_get(a); + + ds_put_format(ds, "src=%"PRIu16"/%#"PRIx16 + ",dst=%"PRIu16"/%#"PRIx16, + ntohs(sctp_key->sctp_src), ntohs(sctp_mask->sctp_src), + ntohs(sctp_key->sctp_dst), ntohs(sctp_mask->sctp_dst)); + } else { + const struct ovs_key_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: if (!is_exact) { const struct ovs_key_icmp *icmp_mask = nl_attr_get(ma); @@ -2008,6 +2027,45 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names, } { + int sctp_src; + int sctp_dst; + int sctp_src_mask; + int sctp_dst_mask; + int n = -1; + + if (mask && sscanf(s, "sctp(src=%i/%i,dst=%i/%i)%n", + &sctp_src, &sctp_src_mask, + &sctp_dst, &sctp_dst_mask, &n) > 0 && n > 0) { + struct ovs_key_sctp sctp_key; + struct ovs_key_sctp sctp_mask; + + 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); + + sctp_mask.sctp_src = htons(sctp_src_mask); + sctp_mask.sctp_dst = htons(sctp_dst_mask); + nl_msg_put_unspec(mask, OVS_KEY_ATTR_SCTP, + &sctp_mask, sizeof sctp_mask); + return n; + } + 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); + + if (mask) { + memset(&sctp_key, 0xff, sizeof sctp_key); + nl_msg_put_unspec(mask, OVS_KEY_ATTR_SCTP, &sctp_key, sizeof sctp_key); + } + return n; + } + } + + { int icmp_type; int icmp_code; int icmp_type_mask; @@ -2441,6 +2499,13 @@ odp_flow_key_from_flow__(struct ofpbuf *buf, const struct flow *data, sizeof *udp_key); udp_key->udp_src = data->tp_src; udp_key->udp_dst = data->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 = data->tp_src; + sctp_key->sctp_dst = data->tp_dst; } else if (flow->dl_type == htons(ETH_TYPE_IP) && flow->nw_proto == IPPROTO_ICMP) { struct ovs_key_icmp *icmp_key; @@ -2759,6 +2824,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)) { @@ -3256,6 +3333,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 b95e1e0..253184f 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" @@ -885,6 +886,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 e852761..6aa70f0 100644 --- a/lib/packets.h +++ b/lib/packets.h @@ -468,6 +468,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; @@ -598,6 +607,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