Пример #1
0
/* Tries to decode 'oh', which should be an OpenFlow OFPT_ERROR message.
 * Returns an OFPERR_* constant on success, 0 on failure.
 *
 * If 'payload' is nonnull, on success '*payload' is initialized with a copy of
 * the error's payload (copying is required because the payload is not properly
 * aligned).  The caller must free the payload (with ofpbuf_uninit()) when it
 * is no longer needed.  On failure, '*payload' is cleared. */
enum ofperr
ofperr_decode_msg(const struct ofp_header *oh, struct ofpbuf *payload)
{
    const struct ofp_error_msg *oem;
    enum ofpraw raw;
    uint16_t type, code;
    enum ofperr error;
    uint32_t vendor;
    struct ofpbuf b;

    if (payload) {
        memset(payload, 0, sizeof *payload);
    }

    /* Pull off the error message. */
    ofpbuf_use_const(&b, oh, ntohs(oh->length));
    error = ofpraw_pull(&raw, &b);
    if (error) {
        return 0;
    }
    oem = ofpbuf_pull(&b, sizeof *oem);

    /* Get the error type and code. */
    vendor = 0;
    type = ntohs(oem->type);
    code = ntohs(oem->code);
    if (type == NXET_VENDOR && code == NXVC_VENDOR_ERROR) {
        const struct nx_vendor_error *nve = ofpbuf_try_pull(&b, sizeof *nve);
        if (!nve) {
            return 0;
        }

        vendor = ntohl(nve->vendor);
        type = ntohs(nve->type);
        code = ntohs(nve->code);
    } else if (type == OFPET12_EXPERIMENTER) {
        const ovs_be32 *vendorp = ofpbuf_try_pull(&b, sizeof *vendorp);
        if (!vendorp) {
            return 0;
        }

        vendor = ntohl(*vendorp);
        type = code;
        code = 0;
    }

    /* Translate the error type and code into an ofperr. */
    error = ofperr_decode(oh->version, vendor, type, code);
    if (error && payload) {
        ofpbuf_init(payload, b.size);
        ofpbuf_push(payload, b.data, b.size);
    }
    return error;
}
Пример #2
0
/* Converts an OFPST_QUEUE_STATS reply in 'msg' into an abstract
 * ofputil_queue_stats in 'qs'.
 *
 * Multiple OFPST_QUEUE_STATS replies can be packed into a single OpenFlow
 * message.  Calling this function multiple times for a single 'msg' iterates
 * through the replies.  The caller must initially leave 'msg''s layer pointers
 * null and not modify them between calls.
 *
 * Returns 0 if successful, EOF if no replies were left in this 'msg',
 * otherwise a positive errno value. */
int
ofputil_decode_queue_stats(struct ofputil_queue_stats *qs, struct ofpbuf *msg)
{
    enum ofperr error;
    enum ofpraw raw;

    error = (msg->header ? ofpraw_decode(&raw, msg->header)
             : ofpraw_pull(&raw, msg));
    if (error) {
        return error;
    }

    if (!msg->size) {
        return EOF;
    } else if (raw == OFPRAW_OFPST14_QUEUE_REPLY) {
        return ofputil_pull_ofp14_queue_stats(qs, msg);
    } else if (raw == OFPRAW_OFPST13_QUEUE_REPLY) {
        const struct ofp13_queue_stats *qs13;

        qs13 = ofpbuf_try_pull(msg, sizeof *qs13);
        if (!qs13) {
            goto bad_len;
        }
        return ofputil_queue_stats_from_ofp13(qs, qs13);
    } else if (raw == OFPRAW_OFPST11_QUEUE_REPLY) {
        const struct ofp11_queue_stats *qs11;

        qs11 = ofpbuf_try_pull(msg, sizeof *qs11);
        if (!qs11) {
            goto bad_len;
        }
        return ofputil_queue_stats_from_ofp11(qs, qs11);
    } else if (raw == OFPRAW_OFPST10_QUEUE_REPLY) {
        const struct ofp10_queue_stats *qs10;

        qs10 = ofpbuf_try_pull(msg, sizeof *qs10);
        if (!qs10) {
            goto bad_len;
        }
        return ofputil_queue_stats_from_ofp10(qs, qs10);
    } else {
        OVS_NOT_REACHED();
    }

 bad_len:
    VLOG_WARN_RL(&rl, "OFPST_QUEUE reply has %"PRIu32" leftover "
                 "bytes at end", msg->size);
    return OFPERR_OFPBRC_BAD_LEN;
}
Пример #3
0
void
handle_acl_log(const struct flow *headers, struct ofpbuf *userdata)
{
    if (!VLOG_IS_INFO_ENABLED()) {
        return;
    }

    struct log_pin_header *lph = ofpbuf_try_pull(userdata, sizeof *lph);
    if (!lph) {
        VLOG_WARN("log data missing");
        return;
    }

    size_t name_len = userdata->size;
    char *name = name_len ? xmemdup0(userdata->data, name_len) : NULL;

    struct ds ds = DS_EMPTY_INITIALIZER;
    ds_put_cstr(&ds, "name=");
    json_string_escape(name_len ? name : "<unnamed>", &ds);
    ds_put_format(&ds, ", verdict=%s, severity=%s: ",
                  log_verdict_to_string(lph->verdict),
                  log_severity_to_string(lph->severity));
    flow_format(&ds, headers, NULL);

    VLOG_INFO("%s", ds_cstr(&ds));
    ds_destroy(&ds);
    free(name);
}
Пример #4
0
static enum ofperr
oxm_pull_match__(struct ofpbuf *b, bool strict, struct match *match)
{
    struct ofp11_match_header *omh = ofpbuf_data(b);
    uint8_t *p;
    uint16_t match_len;

    if (ofpbuf_size(b) < sizeof *omh) {
        return OFPERR_OFPBMC_BAD_LEN;
    }

    match_len = ntohs(omh->length);
    if (match_len < sizeof *omh) {
        return OFPERR_OFPBMC_BAD_LEN;
    }

    if (omh->type != htons(OFPMT_OXM)) {
        return OFPERR_OFPBMC_BAD_TYPE;
    }

    p = ofpbuf_try_pull(b, ROUND_UP(match_len, 8));
    if (!p) {
        VLOG_DBG_RL(&rl, "oxm length %u, rounded up to a "
                    "multiple of 8, is longer than space in message (max "
                    "length %"PRIu32")", match_len, ofpbuf_size(b));
        return OFPERR_OFPBMC_BAD_LEN;
    }

    return nx_pull_raw(p + sizeof *omh, match_len - sizeof *omh,
                       strict, match, NULL, NULL);
}
Пример #5
0
static int
ofputil_pull_queue_get_config_reply14(struct ofpbuf *msg,
                                      struct ofputil_queue_config *queue)
{
    struct ofp14_queue_desc *oqd14 = ofpbuf_try_pull(msg, sizeof *oqd14);
    if (!oqd14) {
        return OFPERR_OFPBRC_BAD_LEN;
    }
    enum ofperr error = ofputil_port_from_ofp11(oqd14->port_no, &queue->port);
    if (error) {
        return error;
    }
    queue->queue = ntohl(oqd14->queue_id);

    /* Length check. */
    unsigned int len = ntohs(oqd14->len);
    if (len < sizeof *oqd14 || len > msg->size + sizeof *oqd14 || len % 8) {
        return OFPERR_OFPBRC_BAD_LEN;
    }
    len -= sizeof *oqd14;

    struct ofpbuf properties = ofpbuf_const_initializer(ofpbuf_pull(msg, len),
                                                        len);
    while (properties.size > 0) {
        struct ofpbuf payload;
        uint64_t type;

        error = ofpprop_pull(&properties, &payload, &type);
        if (error) {
            return error;
        }

        switch (type) {
        case OFPQDPT14_MIN_RATE:
            error = ofpprop_parse_u16(&payload, &queue->min_rate);
            break;

        case OFPQDPT14_MAX_RATE:
            error = ofpprop_parse_u16(&payload, &queue->max_rate);
            break;

        default:
            error = OFPPROP_UNKNOWN(true, "queue desc", type);
            break;
        }

        if (error) {
            return error;
        }
    }

    return 0;
}
Пример #6
0
static void
parse_mpls(struct ofpbuf *b, struct flow *flow)
{
    struct mpls_hdr *mh;

    while ((mh = ofpbuf_try_pull(b, sizeof *mh))) {
        if (flow->mpls_depth++ == 0) {
            flow->mpls_lse = mh->mpls_lse;
        }
        if (mh->mpls_lse & htonl(MPLS_BOS_MASK)) {
            break;
        }
    }
}
Пример #7
0
static void
parse_mpls(struct ofpbuf *b, struct flow *flow)
{
    struct mpls_hdr *mh;
    bool top = true;

    while ((mh = ofpbuf_try_pull(b, sizeof *mh))) {
        if (top) {
            top = false;
            flow->mpls_lse = mh->mpls_lse;
        }
        if (mh->mpls_lse & htonl(MPLS_BOS_MASK)) {
            break;
        }
    }
}
Пример #8
0
static enum ofperr
nx_pull_match__(struct ofpbuf *b, unsigned int match_len, bool strict,
                struct match *match,
                ovs_be64 *cookie, ovs_be64 *cookie_mask)
{
    uint8_t *p = NULL;

    if (match_len) {
        p = ofpbuf_try_pull(b, ROUND_UP(match_len, 8));
        if (!p) {
            VLOG_DBG_RL(&rl, "nx_match length %u, rounded up to a "
                        "multiple of 8, is longer than space in message (max "
                        "length %"PRIu32")", match_len, ofpbuf_size(b));
            return OFPERR_OFPBMC_BAD_LEN;
        }
    }

    return nx_pull_raw(p, match_len, strict, match, cookie, cookie_mask);
}
Пример #9
0
static enum ofperr
ofputil_pull_ofp14_queue_stats(struct ofputil_queue_stats *oqs,
                               struct ofpbuf *msg)
{
    const struct ofp14_queue_stats *qs14;
    size_t len;

    qs14 = ofpbuf_try_pull(msg, sizeof *qs14);
    if (!qs14) {
        return OFPERR_OFPBRC_BAD_LEN;
    }

    len = ntohs(qs14->length);
    if (len < sizeof *qs14 || len - sizeof *qs14 > msg->size) {
        return OFPERR_OFPBRC_BAD_LEN;
    }
    ofpbuf_pull(msg, len - sizeof *qs14);

    /* No properties yet defined, so ignore them for now. */

    return ofputil_queue_stats_from_ofp13(oqs, &qs14->qs);
}
Пример #10
0
static int
parse_ipv6(struct ofpbuf *packet, struct flow *flow)
{
    const struct ovs_16aligned_ip6_hdr *nh;
    ovs_be32 tc_flow;
    int nexthdr;

    nh = ofpbuf_try_pull(packet, sizeof *nh);
    if (!nh) {
        return EINVAL;
    }

    nexthdr = nh->ip6_nxt;

    memcpy(&flow->ipv6_src, &nh->ip6_src, sizeof flow->ipv6_src);
    memcpy(&flow->ipv6_dst, &nh->ip6_dst, sizeof flow->ipv6_dst);

    tc_flow = get_16aligned_be32(&nh->ip6_flow);
    flow->nw_tos = ntohl(tc_flow) >> 20;
    flow->ipv6_label = tc_flow & htonl(IPV6_LABEL_MASK);
    flow->nw_ttl = nh->ip6_hlim;
    flow->nw_proto = IPPROTO_NONE;

    while (1) {
        if ((nexthdr != IPPROTO_HOPOPTS)
                && (nexthdr != IPPROTO_ROUTING)
                && (nexthdr != IPPROTO_DSTOPTS)
                && (nexthdr != IPPROTO_AH)
                && (nexthdr != IPPROTO_FRAGMENT)) {
            /* It's either a terminal header (e.g., TCP, UDP) or one we
             * don't understand.  In either case, we're done with the
             * packet, so use it to fill in 'nw_proto'. */
            break;
        }

        /* We only verify that at least 8 bytes of the next header are
         * available, but many of these headers are longer.  Ensure that
         * accesses within the extension header are within those first 8
         * bytes. All extension headers are required to be at least 8
         * bytes. */
        if (packet->size < 8) {
            return EINVAL;
        }

        if ((nexthdr == IPPROTO_HOPOPTS)
                || (nexthdr == IPPROTO_ROUTING)
                || (nexthdr == IPPROTO_DSTOPTS)) {
            /* These headers, while different, have the fields we care about
             * in the same location and with the same interpretation. */
            const struct ip6_ext *ext_hdr = packet->data;
            nexthdr = ext_hdr->ip6e_nxt;
            if (!ofpbuf_try_pull(packet, (ext_hdr->ip6e_len + 1) * 8)) {
                return EINVAL;
            }
        } else if (nexthdr == IPPROTO_AH) {
            /* A standard AH definition isn't available, but the fields
             * we care about are in the same location as the generic
             * option header--only the header length is calculated
             * differently. */
            const struct ip6_ext *ext_hdr = packet->data;
            nexthdr = ext_hdr->ip6e_nxt;
            if (!ofpbuf_try_pull(packet, (ext_hdr->ip6e_len + 2) * 4)) {
               return EINVAL;
            }
        } else if (nexthdr == IPPROTO_FRAGMENT) {
            const struct ovs_16aligned_ip6_frag *frag_hdr = packet->data;

            nexthdr = frag_hdr->ip6f_nxt;
            if (!ofpbuf_try_pull(packet, sizeof *frag_hdr)) {
                return EINVAL;
            }

            /* We only process the first fragment. */
            if (frag_hdr->ip6f_offlg != htons(0)) {
                flow->nw_frag = FLOW_NW_FRAG_ANY;
                if ((frag_hdr->ip6f_offlg & IP6F_OFF_MASK) != htons(0)) {
                    flow->nw_frag |= FLOW_NW_FRAG_LATER;
                    nexthdr = IPPROTO_FRAGMENT;
                    break;
                }
            }
        }
    }

    flow->nw_proto = nexthdr;
    return 0;
}
Пример #11
0
static void
print_netflow(struct ofpbuf *buf)
{
    const struct netflow_v5_header *hdr;
    int i;

    hdr = ofpbuf_try_pull(buf, sizeof *hdr);
    if (!hdr) {
        printf("truncated NetFlow packet header\n");
        return;
    }
    printf("header: v%"PRIu16", "
           "uptime %"PRIu32", "
           "now %"PRIu32".%09"PRIu32", "
           "seq %"PRIu32", "
           "engine %"PRIu8",%"PRIu8,
           ntohs(hdr->version),
           ntohl(hdr->sysuptime),
           ntohl(hdr->unix_secs), ntohl(hdr->unix_nsecs),
           ntohl(hdr->flow_seq),
           hdr->engine_type, hdr->engine_id);
    if (hdr->sampling_interval != htons(0)) {
        printf(", interval %"PRIu16, ntohs(hdr->sampling_interval));
    }
    putchar('\n');

    for (i = 0; i < ntohs(hdr->count); i++) {
        struct netflow_v5_record *rec;

        rec = ofpbuf_try_pull(buf, sizeof *rec);
        if (!rec) {
            printf("truncated NetFlow records\n");
            return;
        }

        printf("seq %"PRIu32": "IP_FMT" > "IP_FMT, ntohl(hdr->flow_seq),
               IP_ARGS(rec->src_addr), IP_ARGS(rec->dst_addr));

        printf(", if %"PRIu16" > %"PRIu16,
               ntohs(rec->input), ntohs(rec->output));

        printf(", %"PRIu32" pkts, %"PRIu32" bytes",
               ntohl(rec->packet_count), ntohl(rec->byte_count));

        switch (rec->ip_proto) {
        case IPPROTO_TCP:
            printf(", TCP %"PRIu16" > %"PRIu16,
                   ntohs(rec->src_port), ntohs(rec->dst_port));
            if (rec->tcp_flags) {
                struct ds s = DS_EMPTY_INITIALIZER;
                packet_format_tcp_flags(&s, rec->tcp_flags);
                printf(" %s", ds_cstr(&s));
                ds_destroy(&s);
            }
            break;

        case IPPROTO_UDP:
            printf(", UDP %"PRIu16" > %"PRIu16,
                   ntohs(rec->src_port), ntohs(rec->dst_port));
            break;

        case IPPROTO_ICMP:
            printf(", ICMP %"PRIu16":%"PRIu16,
                   ntohs(rec->dst_port) >> 8,
                   ntohs(rec->dst_port) & 0xff);
            if (rec->src_port != htons(0)) {
                printf(", src_port=%"PRIu16, ntohs(rec->src_port));
            }
            break;

        default:
            printf(", proto %"PRIu8, rec->ip_proto);
            break;
        }

        if (rec->ip_proto != IPPROTO_TCP && rec->tcp_flags != 0) {
            printf(", flags %"PRIx8, rec->tcp_flags);
        }

        if (rec->ip_proto != IPPROTO_TCP &&
            rec->ip_proto != IPPROTO_UDP &&
            rec->ip_proto != IPPROTO_ICMP) {
            if (rec->src_port != htons(0)) {
                printf(", src_port %"PRIu16, ntohs(rec->src_port));
            }
            if (rec->dst_port != htons(0)) {
                printf(", dst_port %"PRIu16, ntohs(rec->dst_port));
            }
        }

        if (rec->ip_tos) {
            printf(", TOS %"PRIx8, rec->ip_tos);
        }

        printf(", time %"PRIu32"...%"PRIu32,
               ntohl(rec->init_time), ntohl(rec->used_time));

        if (rec->nexthop != htonl(0)) {
            printf(", nexthop "IP_FMT, IP_ARGS(rec->nexthop));
        }
        if (rec->src_as != htons(0) || rec->dst_as != htons(0)) {
            printf(", AS %"PRIu16" > %"PRIu16,
                   ntohs(rec->src_as), ntohs(rec->dst_as));
        }
        if (rec->src_mask != 0 || rec->dst_mask != 0) {
            printf(", mask %"PRIu8" > %"PRIu8, rec->src_mask, rec->dst_mask);
        }
        if (rec->pad1) {
            printf(", pad1 %"PRIu8, rec->pad1);
        }
        if (rec->pad[0] || rec->pad[1]) {
            printf(", pad %"PRIu8", %"PRIu8, rec->pad[0], rec->pad[1]);
        }
        putchar('\n');
    }

    if (buf->size) {
        printf("%zu extra bytes after last record\n", buf->size);
    }
}
Пример #12
0
static struct icmp6_hdr *
pull_icmpv6(struct ofpbuf *packet)
{
    return ofpbuf_try_pull(packet, sizeof(struct icmp6_hdr));
}
Пример #13
0
static struct icmp_header *
pull_icmp(struct ofpbuf *packet)
{
    return ofpbuf_try_pull(packet, ICMP_HEADER_LEN);
}
Пример #14
0
static struct sctp_header *
pull_sctp(struct ofpbuf *packet)
{
    return ofpbuf_try_pull(packet, SCTP_HEADER_LEN);
}
Пример #15
0
static struct udp_header *
pull_udp(struct ofpbuf *packet)
{
    return ofpbuf_try_pull(packet, UDP_HEADER_LEN);
}
Пример #16
0
static struct arp_eth_header *
pull_arp(struct ofpbuf *packet)
{
    return ofpbuf_try_pull(packet, ARP_ETH_HEADER_LEN);
}
Пример #17
0
static bool
parse_icmpv6(struct ofpbuf *b, struct flow *flow)
{
    const struct icmp6_hdr *icmp = pull_icmpv6(b);

    if (!icmp) {
        return false;
    }

    /* The ICMPv6 type and code fields use the 16-bit transport port
     * fields, so we need to store them in 16-bit network byte order. */
    flow->tp_src = htons(icmp->icmp6_type);
    flow->tp_dst = htons(icmp->icmp6_code);

    if (icmp->icmp6_code == 0 &&
        (icmp->icmp6_type == ND_NEIGHBOR_SOLICIT ||
         icmp->icmp6_type == ND_NEIGHBOR_ADVERT)) {
        const struct in6_addr *nd_target;

        nd_target = ofpbuf_try_pull(b, sizeof *nd_target);
        if (!nd_target) {
            return false;
        }
        flow->nd_target = *nd_target;

        while (b->size >= 8) {
            /* The minimum size of an option is 8 bytes, which also is
             * the size of Ethernet link-layer options. */
            const struct nd_opt_hdr *nd_opt = b->data;
            int opt_len = nd_opt->nd_opt_len * 8;

            if (!opt_len || opt_len > b->size) {
                goto invalid;
            }

            /* Store the link layer address if the appropriate option is
             * provided.  It is considered an error if the same link
             * layer option is specified twice. */
            if (nd_opt->nd_opt_type == ND_OPT_SOURCE_LINKADDR
                    && opt_len == 8) {
                if (eth_addr_is_zero(flow->arp_sha)) {
                    memcpy(flow->arp_sha, nd_opt + 1, ETH_ADDR_LEN);
                } else {
                    goto invalid;
                }
            } else if (nd_opt->nd_opt_type == ND_OPT_TARGET_LINKADDR
                    && opt_len == 8) {
                if (eth_addr_is_zero(flow->arp_tha)) {
                    memcpy(flow->arp_tha, nd_opt + 1, ETH_ADDR_LEN);
                } else {
                    goto invalid;
                }
            }

            if (!ofpbuf_try_pull(b, opt_len)) {
                goto invalid;
            }
        }
    }

    return true;

invalid:
    memset(&flow->nd_target, 0, sizeof(flow->nd_target));
    memset(flow->arp_sha, 0, sizeof(flow->arp_sha));
    memset(flow->arp_tha, 0, sizeof(flow->arp_tha));

    return false;

}
Пример #18
0
static struct vlan_header *
pull_vlan(struct ofpbuf *packet)
{
    return ofpbuf_try_pull(packet, VLAN_HEADER_LEN);
}
Пример #19
0
static struct eth_header *
pull_eth(struct ofpbuf *packet) 
{
    return ofpbuf_try_pull(packet, ETH_HEADER_LEN);
}
Пример #20
0
netdev_windows_netdev_from_ofpbuf(struct netdev_windows_netdev_info *info,
                                  struct ofpbuf *buf)
{
    static const struct nl_policy ovs_netdev_policy[] = {
        [OVS_WIN_NETDEV_ATTR_PORT_NO] = { .type = NL_A_U32 },
        [OVS_WIN_NETDEV_ATTR_TYPE] = { .type = NL_A_U32 },
        [OVS_WIN_NETDEV_ATTR_NAME] = { .type = NL_A_STRING, .max_len = IFNAMSIZ },
        [OVS_WIN_NETDEV_ATTR_MAC_ADDR] = { NL_POLICY_FOR(info->mac_address) },
        [OVS_WIN_NETDEV_ATTR_MTU] = { .type = NL_A_U32 },
        [OVS_WIN_NETDEV_ATTR_IF_FLAGS] = { .type = NL_A_U32 },
    };

    netdev_windows_info_init(info);

    struct ofpbuf b = ofpbuf_const_initializer(buf->data, buf->size);
    struct nlmsghdr *nlmsg = ofpbuf_try_pull(&b, sizeof *nlmsg);
    struct genlmsghdr *genl = ofpbuf_try_pull(&b, sizeof *genl);
    struct ovs_header *ovs_header = ofpbuf_try_pull(&b, sizeof *ovs_header);

    struct nlattr *a[ARRAY_SIZE(ovs_netdev_policy)];
    if (!nlmsg || !genl || !ovs_header
        || nlmsg->nlmsg_type != ovs_win_netdev_family
        || !nl_policy_parse(&b, 0, ovs_netdev_policy, a,
                            ARRAY_SIZE(ovs_netdev_policy))) {
        return EINVAL;
    }

    info->cmd = genl->cmd;
    info->dp_ifindex = ovs_header->dp_ifindex;
    info->port_no = nl_attr_get_odp_port(a[OVS_WIN_NETDEV_ATTR_PORT_NO]);
    info->ovs_type = nl_attr_get_u32(a[OVS_WIN_NETDEV_ATTR_TYPE]);
Пример #21
0
static int
ofputil_pull_queue_get_config_reply10(struct ofpbuf *msg,
                                      struct ofputil_queue_config *queue)
{
    const struct ofp_header *oh = msg->header;
    unsigned int opq_len;       /* Length of protocol-specific queue header. */
    unsigned int len;           /* Total length of queue + properties. */

    /* Obtain the port number from the message header. */
    if (oh->version == OFP10_VERSION) {
        const struct ofp10_queue_get_config_reply *oqgcr10 = msg->msg;
        queue->port = u16_to_ofp(ntohs(oqgcr10->port));
    } else {
        const struct ofp11_queue_get_config_reply *oqgcr11 = msg->msg;
        enum ofperr error = ofputil_port_from_ofp11(oqgcr11->port,
                                                    &queue->port);
        if (error) {
            return error;
        }
    }

    /* Pull off the queue header and get the queue number and length. */
    if (oh->version < OFP12_VERSION) {
        const struct ofp10_packet_queue *opq10;
        opq10 = ofpbuf_try_pull(msg, sizeof *opq10);
        if (!opq10) {
            return OFPERR_OFPBRC_BAD_LEN;
        }
        queue->queue = ntohl(opq10->queue_id);
        len = ntohs(opq10->len);
        opq_len = sizeof *opq10;
    } else {
        const struct ofp12_packet_queue *opq12;
        opq12 = ofpbuf_try_pull(msg, sizeof *opq12);
        if (!opq12) {
            return OFPERR_OFPBRC_BAD_LEN;
        }
        queue->queue = ntohl(opq12->queue_id);
        len = ntohs(opq12->len);
        opq_len = sizeof *opq12;
    }

    /* Length check. */
    if (len < opq_len || len > msg->size + opq_len || len % 8) {
        return OFPERR_OFPBRC_BAD_LEN;
    }
    len -= opq_len;

    /* Pull properties.  The format of these properties differs from used in
     * OF1.4+ so we can't use the common property functions. */
    while (len > 0) {
        const struct ofp10_queue_prop_header *hdr;
        unsigned int property;
        unsigned int prop_len;
        enum ofperr error = 0;

        hdr = ofpbuf_at_assert(msg, 0, sizeof *hdr);
        prop_len = ntohs(hdr->len);
        if (prop_len < sizeof *hdr || prop_len > len || prop_len % 8) {
            return OFPERR_OFPBRC_BAD_LEN;
        }

        property = ntohs(hdr->property);
        switch (property) {
        case OFPQT10_MIN_RATE:
            error = parse_ofp10_queue_rate(hdr, &queue->min_rate);
            break;

        case OFPQT11_MAX_RATE:
            error = parse_ofp10_queue_rate(hdr, &queue->max_rate);
            break;

        default:
            VLOG_INFO_RL(&rl, "unknown queue property %u", property);
            break;
        }
        if (error) {
            return error;
        }

        ofpbuf_pull(msg, prop_len);
        len -= prop_len;
    }
    return 0;
}