Signed-off-by: Joe Stringer <[email protected]>
---
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/dpif-netdev.c | 6 +++++
lib/flow.c | 33 ++++++++++++++++++++++++--
lib/flow.h | 4 +-
lib/odp-util.c | 53 +++++++++++++++++++++++++++++++++++++++++++
lib/packets.c | 23 ++++++++++++++++++
lib/packets.h | 10 ++++++++
ofproto/ofproto-dpif.c | 3 ++
9 files changed, 134 insertions(+), 5 deletions(-)
diff --git a/include/linux/openvswitch.h b/include/linux/openvswitch.h
index 56b9785..57db339 100644
--- a/include/linux/openvswitch.h
+++ b/include/linux/openvswitch.h
@@ -283,6 +283,7 @@ enum ovs_key_attr {
OVS_KEY_ATTR_ND, /* struct ovs_key_nd */
OVS_KEY_ATTR_SKB_MARK, /* u32 skb mark */
OVS_KEY_ATTR_IPV4_TUNNEL, /* struct ovs_key_ipv4_tunnel */
+ OVS_KEY_ATTR_SCTP, /* struct ovs_key_sctp */
OVS_KEY_ATTR_TUN_ID = 63, /* be64 tunnel ID */
__OVS_KEY_ATTR_MAX
};
@@ -341,6 +342,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/dpif-netdev.c b/lib/dpif-netdev.c
index 682dfc9..cef03ef 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -1188,6 +1188,7 @@ 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_TUN_ID:
@@ -1225,6 +1226,11 @@ 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_UNSPEC:
case OVS_KEY_ATTR_ENCAP:
case OVS_KEY_ATTR_ETHERTYPE:
diff --git a/lib/flow.c b/lib/flow.c
index ce9e6c6..1e0fe2d 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)
{
@@ -246,6 +252,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)
{
@@ -331,7 +348,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
@@ -400,6 +417,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) {
@@ -420,6 +439,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;
@@ -683,7 +704,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)) {
@@ -695,7 +716,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;
}
}
@@ -839,6 +860,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 ca41439..86d2686 100644
--- a/lib/flow.h
+++ b/lib/flow.h
@@ -93,8 +93,8 @@ struct flow {
uint32_t skb_mark; /* Packet mark. */
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-util.c b/lib/odp-util.c
index f1075e3..2ecbf95 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -104,6 +104,7 @@ ovs_key_attr_to_string(enum ovs_key_attr attr)
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";
@@ -626,6 +627,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);
@@ -695,6 +697,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;
@@ -810,6 +813,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")",
@@ -1176,6 +1185,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;
@@ -1450,6 +1476,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;
@@ -1719,6 +1752,18 @@ parse_l3_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)) {
@@ -2110,6 +2155,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 812d1af..58dbebc 100644
--- a/lib/packets.c
+++ b/lib/packets.c
@@ -24,6 +24,7 @@
#include <stdlib.h>
#include "byte-order.h"
#include "csum.h"
+#include "crc32c.h"
#include "flow.h"
#include "hmap.h"
#include "dynamic-string.h"
@@ -704,6 +705,28 @@ 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(0, packet->l4, tp_len);
+
+ sh->sctp_src = src;
+ sh->sctp_dst = dst;
+ sh->sctp_csum = 0;
+
+ new_csum = crc32c(0, 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 7e2d4e9..9a14f54 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -373,6 +373,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;
@@ -491,6 +500,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);
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index ca0a065..3cffd45 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -5581,6 +5581,9 @@ execute_controller_action(struct action_xlate_ctx *ctx,
int len,
} else if (ctx->flow.nw_proto == IPPROTO_UDP) {
packet_set_udp_port(packet, ctx->flow.tp_src,
ctx->flow.tp_dst);
+ } else if (ctx->flow.nw_proto == IPPROTO_SCTP) {
+ packet_set_sctp_port(packet, ctx->flow.tp_src,
+ ctx->flow.tp_dst);
}
}
}
--
1.7.2.5
_______________________________________________
dev mailing list
[email protected]
http://openvswitch.org/mailman/listinfo/dev