[ovs-dev] [PATCH 2/4] dpif-netdev: Add SCTP support

Joe Stringer joe at wand.net.nz
Fri Aug 31 10:44:20 UTC 2012


Signed-off-by: Joe Stringer <joe at wand.net.nz>
---
It seems that this commit breaks the following test, I'm not sure quite why:-

    504: ofproto-dpif - NetFlow flow expiration

---
 include/linux/openvswitch.h |    6 +++++
 include/sparse/netinet/in.h |    1 +
 lib/dpif-netdev.c           |    6 +++++
 lib/flow.c                  |   34 +++++++++++++++++++++++++--
 lib/flow.h                  |    4 +-
 lib/odp-util.c              |   52 +++++++++++++++++++++++++++++++++++++++++++
 lib/packets.c               |   20 ++++++++++++++++
 lib/packets.h               |   10 ++++++++
 ofproto/ofproto-dpif.c      |    3 ++
 9 files changed, 131 insertions(+), 5 deletions(-)

diff --git a/include/linux/openvswitch.h b/include/linux/openvswitch.h
index f5c9cca..63c83cb 100644
--- a/include/linux/openvswitch.h
+++ b/include/linux/openvswitch.h
@@ -274,6 +274,7 @@ enum ovs_key_attr {
 	OVS_KEY_ATTR_IPV6,      /* struct ovs_key_ipv6 */
 	OVS_KEY_ATTR_TCP,       /* struct ovs_key_tcp */
 	OVS_KEY_ATTR_UDP,       /* struct ovs_key_udp */
+	OVS_KEY_ATTR_SCTP,       /* struct ovs_key_sctp */
 	OVS_KEY_ATTR_ICMP,      /* struct ovs_key_icmp */
 	OVS_KEY_ATTR_ICMPV6,    /* struct ovs_key_icmpv6 */
 	OVS_KEY_ATTR_ARP,       /* struct ovs_key_arp */
@@ -336,6 +337,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 144b6b6..219f017 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -1175,6 +1175,7 @@ execute_set_action(struct ofpbuf *packet, const struct nlattr *a)
     const struct ovs_key_ipv4 *ipv4_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:
@@ -1204,6 +1205,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 f4446c9..754ad2f 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -77,6 +77,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)
 {
@@ -242,6 +248,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)
 {
@@ -327,7 +344,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
@@ -390,6 +407,8 @@ flow_extract(struct ofpbuf *packet, uint32_t skb_priority, ovs_be64 tun_id,
                     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) {
@@ -410,6 +429,8 @@ flow_extract(struct ofpbuf *packet, uint32_t skb_priority, ovs_be64 tun_id,
             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;
@@ -899,7 +920,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)) {
@@ -911,7 +932,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;
         }
     }
@@ -1054,6 +1075,13 @@ 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;
+                /* XXX: Do we need to set the csum here? */
             } else if (flow->nw_proto == IPPROTO_ICMP) {
                 struct icmp_header *icmp;
 
diff --git a/lib/flow.h b/lib/flow.h
index 568e291..7348672 100644
--- a/lib/flow.h
+++ b/lib/flow.h
@@ -65,8 +65,8 @@ struct flow {
     uint16_t in_port;           /* OpenFlow port number of input port. */
     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 901dac3..019f3a6 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -101,6 +101,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";
@@ -610,6 +611,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);
@@ -664,6 +666,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;
@@ -760,6 +763,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")",
@@ -1080,6 +1089,22 @@ 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;
@@ -1345,6 +1370,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;
@@ -1613,6 +1645,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)) {
@@ -2002,6 +2046,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 16f4fe6..de4906b 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"
@@ -467,6 +468,10 @@ packet_set_ipv4_addr(struct ofpbuf *packet, ovs_be32 *addr, ovs_be32 new_addr)
                 uh->udp_csum = htons(0xffff);
             }
         }
+    } else if (nh->ip_proto == IPPROTO_SCTP && packet->l7) {
+        struct sctp_header *sh = packet->l4;
+        sh->sctp_csum = 0;
+        sh->sctp_csum = crc32c(0, packet->data, packet->size);
     }
     nh->ip_csum = recalc_csum32(nh->ip_csum, *addr, new_addr);
     *addr = new_addr;
@@ -549,6 +554,21 @@ 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;
+
+    sh->sctp_src = src;
+    sh->sctp_dst = dst;
+
+    sh->sctp_csum = 0;
+    sh->sctp_csum = crc32c(0, packet->data, packet->size);
+}
+
 /* 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 e5be1cb..34f246b 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -371,6 +371,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;
@@ -486,6 +495,7 @@ void packet_set_ipv4(struct ofpbuf *, ovs_be32 src, ovs_be32 dst, uint8_t tos,
                      uint8_t ttl);
 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 78cb186..8da4cca 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -5164,6 +5164,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




More information about the dev mailing list