ofproto: Delete relevant flows on group del
[sliver-openvswitch.git] / lib / ofp-util.c
index 173b534..d435e99 100644 (file)
@@ -84,7 +84,7 @@ ofputil_netmask_to_wcbits(ovs_be32 netmask)
 void
 ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc)
 {
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 21);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 22);
 
     /* Initialize most of wc. */
     flow_wildcards_init_catchall(wc);
@@ -296,8 +296,8 @@ ofputil_pull_ofp11_match(struct ofpbuf *buf, struct match *match,
     }
 }
 
-/* Converts the ofp11_match in 'match' into a struct match in 'match.  Returns
- * 0 if successful, otherwise an OFPERR_* value. */
+/* Converts the ofp11_match in 'ofmatch' into a struct match in 'match'.
+ * Returns 0 if successful, otherwise an OFPERR_* value. */
 enum ofperr
 ofputil_match_from_ofp11_match(const struct ofp11_match *ofmatch,
                                struct match *match)
@@ -1504,7 +1504,8 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
             return error;
         }
 
-        error = ofpacts_pull_openflow11_instructions(&b, b.size, ofpacts);
+        error = ofpacts_pull_openflow11_instructions(&b, oh->version,
+                                                     b.size, ofpacts);
         if (error) {
             return error;
         }
@@ -2185,6 +2186,283 @@ ofputil_decode_nxst_flow_request(struct ofputil_flow_stats_request *fsr,
     return 0;
 }
 
+/* Constructs and returns an OFPT_QUEUE_GET_CONFIG request for the specified
+ * 'port', suitable for OpenFlow version 'version'. */
+struct ofpbuf *
+ofputil_encode_queue_get_config_request(enum ofp_version version,
+                                        ofp_port_t port)
+{
+    struct ofpbuf *request;
+
+    if (version == OFP10_VERSION) {
+        struct ofp10_queue_get_config_request *qgcr10;
+
+        request = ofpraw_alloc(OFPRAW_OFPT10_QUEUE_GET_CONFIG_REQUEST,
+                               version, 0);
+        qgcr10 = ofpbuf_put_zeros(request, sizeof *qgcr10);
+        qgcr10->port = htons(ofp_to_u16(port));
+    } else {
+        struct ofp11_queue_get_config_request *qgcr11;
+
+        request = ofpraw_alloc(OFPRAW_OFPT11_QUEUE_GET_CONFIG_REQUEST,
+                               version, 0);
+        qgcr11 = ofpbuf_put_zeros(request, sizeof *qgcr11);
+        qgcr11->port = ofputil_port_to_ofp11(port);
+    }
+
+    return request;
+}
+
+/* Parses OFPT_QUEUE_GET_CONFIG request 'oh', storing the port specified by the
+ * request into '*port'.  Returns 0 if successful, otherwise an OpenFlow error
+ * code. */
+enum ofperr
+ofputil_decode_queue_get_config_request(const struct ofp_header *oh,
+                                        ofp_port_t *port)
+{
+    const struct ofp10_queue_get_config_request *qgcr10;
+    const struct ofp11_queue_get_config_request *qgcr11;
+    enum ofpraw raw;
+    struct ofpbuf b;
+
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    raw = ofpraw_pull_assert(&b);
+
+    switch ((int) raw) {
+    case OFPRAW_OFPT10_QUEUE_GET_CONFIG_REQUEST:
+        qgcr10 = b.data;
+        *port = u16_to_ofp(ntohs(qgcr10->port));
+        return 0;
+
+    case OFPRAW_OFPT11_QUEUE_GET_CONFIG_REQUEST:
+        qgcr11 = b.data;
+        return ofputil_port_from_ofp11(qgcr11->port, port);
+    }
+
+    NOT_REACHED();
+}
+
+/* Constructs and returns the beginning of a reply to
+ * OFPT_QUEUE_GET_CONFIG_REQUEST 'oh'.  The caller may append information about
+ * individual queues with ofputil_append_queue_get_config_reply(). */
+struct ofpbuf *
+ofputil_encode_queue_get_config_reply(const struct ofp_header *oh)
+{
+    struct ofp10_queue_get_config_reply *qgcr10;
+    struct ofp11_queue_get_config_reply *qgcr11;
+    struct ofpbuf *reply;
+    enum ofperr error;
+    struct ofpbuf b;
+    enum ofpraw raw;
+    ofp_port_t port;
+
+    error = ofputil_decode_queue_get_config_request(oh, &port);
+    ovs_assert(!error);
+
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    raw = ofpraw_pull_assert(&b);
+
+    switch ((int) raw) {
+    case OFPRAW_OFPT10_QUEUE_GET_CONFIG_REQUEST:
+        reply = ofpraw_alloc_reply(OFPRAW_OFPT10_QUEUE_GET_CONFIG_REPLY,
+                                   oh, 0);
+        qgcr10 = ofpbuf_put_zeros(reply, sizeof *qgcr10);
+        qgcr10->port = htons(ofp_to_u16(port));
+        break;
+
+    case OFPRAW_OFPT11_QUEUE_GET_CONFIG_REQUEST:
+        reply = ofpraw_alloc_reply(OFPRAW_OFPT11_QUEUE_GET_CONFIG_REPLY,
+                                   oh, 0);
+        qgcr11 = ofpbuf_put_zeros(reply, sizeof *qgcr11);
+        qgcr11->port = ofputil_port_to_ofp11(port);
+        break;
+
+    default:
+        NOT_REACHED();
+    }
+
+    return reply;
+}
+
+static void
+put_queue_rate(struct ofpbuf *reply, enum ofp_queue_properties property,
+               uint16_t rate)
+{
+    if (rate != UINT16_MAX) {
+        struct ofp_queue_prop_rate *oqpr;
+
+        oqpr = ofpbuf_put_zeros(reply, sizeof *oqpr);
+        oqpr->prop_header.property = htons(property);
+        oqpr->prop_header.len = htons(sizeof *oqpr);
+        oqpr->rate = htons(rate);
+    }
+}
+
+/* Appends a queue description for 'queue_id' to the
+ * OFPT_QUEUE_GET_CONFIG_REPLY already in 'oh'. */
+void
+ofputil_append_queue_get_config_reply(struct ofpbuf *reply,
+                                      const struct ofputil_queue_config *oqc)
+{
+    const struct ofp_header *oh = reply->data;
+    size_t start_ofs, len_ofs;
+    ovs_be16 *len;
+
+    start_ofs = reply->size;
+    if (oh->version < OFP12_VERSION) {
+        struct ofp10_packet_queue *opq10;
+
+        opq10 = ofpbuf_put_zeros(reply, sizeof *opq10);
+        opq10->queue_id = htonl(oqc->queue_id);
+        len_ofs = (char *) &opq10->len - (char *) reply->data;
+    } else {
+        struct ofp11_queue_get_config_reply *qgcr11;
+        struct ofp12_packet_queue *opq12;
+        ovs_be32 port;
+
+        qgcr11 = reply->l3;
+        port = qgcr11->port;
+
+        opq12 = ofpbuf_put_zeros(reply, sizeof *opq12);
+        opq12->port = port;
+        opq12->queue_id = htonl(oqc->queue_id);
+        len_ofs = (char *) &opq12->len - (char *) reply->data;
+    }
+
+    put_queue_rate(reply, OFPQT_MIN_RATE, oqc->min_rate);
+    put_queue_rate(reply, OFPQT_MAX_RATE, oqc->max_rate);
+
+    len = ofpbuf_at(reply, len_ofs, sizeof *len);
+    *len = htons(reply->size - start_ofs);
+}
+
+/* Decodes the initial part of an OFPT_QUEUE_GET_CONFIG_REPLY from 'reply' and
+ * stores in '*port' the port that the reply is about.  The caller may call
+ * ofputil_pull_queue_get_config_reply() to obtain information about individual
+ * queues included in the reply.  Returns 0 if successful, otherwise an
+ * ofperr.*/
+enum ofperr
+ofputil_decode_queue_get_config_reply(struct ofpbuf *reply, ofp_port_t *port)
+{
+    const struct ofp10_queue_get_config_reply *qgcr10;
+    const struct ofp11_queue_get_config_reply *qgcr11;
+    enum ofpraw raw;
+
+    raw = ofpraw_pull_assert(reply);
+    switch ((int) raw) {
+    case OFPRAW_OFPT10_QUEUE_GET_CONFIG_REPLY:
+        qgcr10 = ofpbuf_pull(reply, sizeof *qgcr10);
+        *port = u16_to_ofp(ntohs(qgcr10->port));
+        return 0;
+
+    case OFPRAW_OFPT11_QUEUE_GET_CONFIG_REPLY:
+        qgcr11 = ofpbuf_pull(reply, sizeof *qgcr11);
+        return ofputil_port_from_ofp11(qgcr11->port, port);
+    }
+
+    NOT_REACHED();
+}
+
+static enum ofperr
+parse_queue_rate(const struct ofp_queue_prop_header *hdr, uint16_t *rate)
+{
+    const struct ofp_queue_prop_rate *oqpr;
+
+    if (hdr->len == htons(sizeof *oqpr)) {
+        oqpr = (const struct ofp_queue_prop_rate *) hdr;
+        *rate = ntohs(oqpr->rate);
+        return 0;
+    } else {
+        return OFPERR_OFPBRC_BAD_LEN;
+    }
+}
+
+/* Decodes information about a queue from the OFPT_QUEUE_GET_CONFIG_REPLY in
+ * 'reply' and stores it in '*queue'.  ofputil_decode_queue_get_config_reply()
+ * must already have pulled off the main header.
+ *
+ * This function returns EOF if the last queue has already been decoded, 0 if a
+ * queue was successfully decoded into '*queue', or an ofperr if there was a
+ * problem decoding 'reply'. */
+int
+ofputil_pull_queue_get_config_reply(struct ofpbuf *reply,
+                                    struct ofputil_queue_config *queue)
+{
+    const struct ofp_header *oh;
+    unsigned int opq_len;
+    unsigned int len;
+
+    if (!reply->size) {
+        return EOF;
+    }
+
+    queue->min_rate = UINT16_MAX;
+    queue->max_rate = UINT16_MAX;
+
+    oh = reply->l2;
+    if (oh->version < OFP12_VERSION) {
+        const struct ofp10_packet_queue *opq10;
+
+        opq10 = ofpbuf_try_pull(reply, sizeof *opq10);
+        if (!opq10) {
+            return OFPERR_OFPBRC_BAD_LEN;
+        }
+        queue->queue_id = ntohl(opq10->queue_id);
+        len = ntohs(opq10->len);
+        opq_len = sizeof *opq10;
+    } else {
+        const struct ofp12_packet_queue *opq12;
+
+        opq12 = ofpbuf_try_pull(reply, sizeof *opq12);
+        if (!opq12) {
+            return OFPERR_OFPBRC_BAD_LEN;
+        }
+        queue->queue_id = ntohl(opq12->queue_id);
+        len = ntohs(opq12->len);
+        opq_len = sizeof *opq12;
+    }
+
+    if (len < opq_len || len > reply->size + opq_len || len % 8) {
+        return OFPERR_OFPBRC_BAD_LEN;
+    }
+    len -= opq_len;
+
+    while (len > 0) {
+        const struct ofp_queue_prop_header *hdr;
+        unsigned int property;
+        unsigned int prop_len;
+        enum ofperr error = 0;
+
+        hdr = ofpbuf_at_assert(reply, 0, sizeof *hdr);
+        prop_len = ntohs(hdr->len);
+        if (prop_len < sizeof *hdr || prop_len > reply->size || prop_len % 8) {
+            return OFPERR_OFPBRC_BAD_LEN;
+        }
+
+        property = ntohs(hdr->property);
+        switch (property) {
+        case OFPQT_MIN_RATE:
+            error = parse_queue_rate(hdr, &queue->min_rate);
+            break;
+
+        case OFPQT_MAX_RATE:
+            error = parse_queue_rate(hdr, &queue->max_rate);
+            break;
+
+        default:
+            VLOG_INFO_RL(&bad_ofmsg_rl, "unknown queue property %u", property);
+            break;
+        }
+        if (error) {
+            return error;
+        }
+
+        ofpbuf_pull(reply, prop_len);
+        len -= prop_len;
+    }
+    return 0;
+}
+
 /* Converts an OFPST_FLOW, OFPST_AGGREGATE, NXST_FLOW, or NXST_AGGREGATE
  * request 'oh', into an abstract flow_stats_request in 'fsr'.  Returns 0 if
  * successful, otherwise an OpenFlow error code. */
@@ -2360,7 +2638,8 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
             return EINVAL;
         }
 
-        if (ofpacts_pull_openflow11_instructions(msg, length - sizeof *ofs -
+        if (ofpacts_pull_openflow11_instructions(msg, oh->version,
+                                                 length - sizeof *ofs -
                                                  padded_match_len, ofpacts)) {
             VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply bad instructions");
             return EINVAL;
@@ -2820,6 +3099,7 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin,
     struct ofpbuf b;
 
     memset(pin, 0, sizeof *pin);
+    pin->cookie = OVS_BE64_MAX;
 
     ofpbuf_use_const(&b, oh, ntohs(oh->length));
     raw = ofpraw_pull_assert(&b);
@@ -2938,7 +3218,6 @@ ofputil_encode_packet_in(const struct ofputil_packet_in *pin,
                          enum ofputil_protocol protocol,
                          enum nx_packet_in_format packet_in_format)
 {
-    size_t send_len = MIN(pin->send_len, pin->packet_len);
     struct ofpbuf *packet;
 
     /* Add OFPT_PACKET_IN. */
@@ -2964,11 +3243,11 @@ ofputil_encode_packet_in(const struct ofputil_packet_in *pin,
         /* The final argument is just an estimate of the space required. */
         packet = ofpraw_alloc_xid(packet_in_raw, packet_in_version,
                                   htonl(0), (sizeof(struct flow_metadata) * 2
-                                             + 2 + send_len));
+                                             + 2 + pin->packet_len));
         ofpbuf_put_zeros(packet, packet_in_size);
         oxm_put_match(packet, &match);
         ofpbuf_put_zeros(packet, 2);
-        ofpbuf_put(packet, pin->packet, send_len);
+        ofpbuf_put(packet, pin->packet, pin->packet_len);
 
         opi = packet->l3;
         opi->pi.buffer_id = htonl(pin->buffer_id);
@@ -2982,14 +3261,14 @@ ofputil_encode_packet_in(const struct ofputil_packet_in *pin,
         struct ofp10_packet_in *opi;
 
         packet = ofpraw_alloc_xid(OFPRAW_OFPT10_PACKET_IN, OFP10_VERSION,
-                                  htonl(0), send_len);
+                                  htonl(0), pin->packet_len);
         opi = ofpbuf_put_zeros(packet, offsetof(struct ofp10_packet_in, data));
         opi->total_len = htons(pin->total_len);
         opi->in_port = htons(ofp_to_u16(pin->fmd.in_port));
         opi->reason = pin->reason;
         opi->buffer_id = htonl(pin->buffer_id);
 
-        ofpbuf_put(packet, pin->packet, send_len);
+        ofpbuf_put(packet, pin->packet, pin->packet_len);
     } else if (packet_in_format == NXPIF_NXM) {
         struct nx_packet_in *npi;
         struct match match;
@@ -3000,11 +3279,11 @@ ofputil_encode_packet_in(const struct ofputil_packet_in *pin,
         /* The final argument is just an estimate of the space required. */
         packet = ofpraw_alloc_xid(OFPRAW_NXT_PACKET_IN, OFP10_VERSION,
                                   htonl(0), (sizeof(struct flow_metadata) * 2
-                                             + 2 + send_len));
+                                             + 2 + pin->packet_len));
         ofpbuf_put_zeros(packet, sizeof *npi);
         match_len = nx_put_match(packet, &match, 0, 0);
         ofpbuf_put_zeros(packet, 2);
-        ofpbuf_put(packet, pin->packet, send_len);
+        ofpbuf_put(packet, pin->packet, pin->packet_len);
 
         npi = packet->l3;
         npi->buffer_id = htonl(pin->buffer_id);
@@ -3092,7 +3371,8 @@ ofputil_decode_packet_out(struct ofputil_packet_out *po,
             return error;
         }
 
-        error = ofpacts_pull_openflow11_actions(&b, ntohs(opo->actions_len),
+        error = ofpacts_pull_openflow11_actions(&b, oh->version,
+                                                ntohs(opo->actions_len),
                                                 ofpacts);
         if (error) {
             return error;
@@ -3906,6 +4186,51 @@ ofputil_encode_role_reply(const struct ofp_header *request,
     return buf;
 }
 \f
+struct ofpbuf *
+ofputil_encode_role_status(const struct ofputil_role_status *status,
+                           enum ofputil_protocol protocol)
+{
+    struct ofpbuf *buf;
+    enum ofp_version version;
+    struct ofp14_role_status *rstatus;
+
+    version = ofputil_protocol_to_ofp_version(protocol);
+    buf = ofpraw_alloc_xid(OFPRAW_OFPT14_ROLE_STATUS, version, htonl(0), 0);
+    rstatus = ofpbuf_put_zeros(buf, sizeof *rstatus);
+    rstatus->role = htonl(status->role);
+    rstatus->reason = status->reason;
+    rstatus->generation_id = htonll(status->generation_id);
+
+    return buf;
+}
+
+enum ofperr
+ofputil_decode_role_status(const struct ofp_header *oh,
+                           struct ofputil_role_status *rs)
+{
+    struct ofpbuf b;
+    enum ofpraw raw;
+    const struct ofp14_role_status *r;
+
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    raw = ofpraw_pull_assert(&b);
+    ovs_assert(raw == OFPRAW_OFPT14_ROLE_STATUS);
+
+    r = b.l3;
+    if (r->role != htonl(OFPCR12_ROLE_NOCHANGE) &&
+        r->role != htonl(OFPCR12_ROLE_EQUAL) &&
+        r->role != htonl(OFPCR12_ROLE_MASTER) &&
+        r->role != htonl(OFPCR12_ROLE_SLAVE)) {
+        return OFPERR_OFPRRFC_BAD_ROLE;
+    }
+
+    rs->role = ntohl(r->role);
+    rs->generation_id = ntohll(r->generation_id);
+    rs->reason = r->reason;
+
+    return 0;
+}
+
 /* Table stats. */
 
 static void
@@ -5674,8 +5999,8 @@ ofputil_append_group_desc_reply(const struct ofputil_group_desc *gds,
 }
 
 static enum ofperr
-ofputil_pull_buckets(struct ofpbuf *msg, size_t buckets_length,
-                     struct list *buckets)
+ofputil_pull_buckets(struct ofpbuf *msg, enum ofp_version version,
+                     size_t buckets_length, struct list *buckets)
 {
     struct ofp11_bucket *ob;
 
@@ -5708,8 +6033,8 @@ ofputil_pull_buckets(struct ofpbuf *msg, size_t buckets_length,
         buckets_length -= ob_len;
 
         ofpbuf_init(&ofpacts, 0);
-        error = ofpacts_pull_openflow11_actions(msg, ob_len - sizeof *ob,
-                                                &ofpacts);
+        error = ofpacts_pull_openflow11_actions(msg, version,
+                                                ob_len - sizeof *ob, &ofpacts);
         if (error) {
             ofpbuf_uninit(&ofpacts);
             ofputil_bucket_list_destroy(buckets);
@@ -5745,7 +6070,7 @@ ofputil_pull_buckets(struct ofpbuf *msg, size_t buckets_length,
  * otherwise a positive errno value. */
 int
 ofputil_decode_group_desc_reply(struct ofputil_group_desc *gd,
-                                struct ofpbuf *msg)
+                                struct ofpbuf *msg, enum ofp_version version)
 {
     struct ofp11_group_desc_stats *ogds;
     size_t length;
@@ -5774,7 +6099,8 @@ ofputil_decode_group_desc_reply(struct ofputil_group_desc *gd,
         return OFPERR_OFPBRC_BAD_LEN;
     }
 
-    return ofputil_pull_buckets(msg, length - sizeof *ogds, &gd->buckets);
+    return ofputil_pull_buckets(msg, version, length - sizeof *ogds,
+                                &gd->buckets);
 }
 
 /* Converts abstract group mod 'gm' into a message for OpenFlow version
@@ -5857,7 +6183,7 @@ ofputil_decode_group_mod(const struct ofp_header *oh,
     gm->type = ogm->type;
     gm->group_id = ntohl(ogm->group_id);
 
-    return ofputil_pull_buckets(&msg, msg.size, &gm->buckets);
+    return ofputil_pull_buckets(&msg, oh->version, msg.size, &gm->buckets);
 }
 
 /* Parse a queue status request message into 'oqsr'.