X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=lib%2Fofp-util.c;h=d58f7ea5ce2627f4c4ba1ce36e5f23d7714d07bb;hb=4029c21acb33723ecc3f621c1c75819651889b58;hp=261c67ad076c62752520a43d4ae5d00e0445bd1f;hpb=217f48c61bbfe1422f00830f36d2d255d3622446;p=openvswitch diff --git a/lib/ofp-util.c b/lib/ofp-util.c index 261c67ad..d58f7ea5 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -19,6 +19,8 @@ #include #include #include "byte-order.h" +#include "classifier.h" +#include "nx-match.h" #include "ofp-util.h" #include "ofpbuf.h" #include "packets.h" @@ -31,12 +33,240 @@ VLOG_DEFINE_THIS_MODULE(ofp_util); * in the peer and so there's not much point in showing a lot of them. */ static struct vlog_rate_limit bad_ofmsg_rl = VLOG_RATE_LIMIT_INIT(1, 5); -/* XXX we should really use consecutive xids to avoid probabilistic - * failures. */ -static inline uint32_t +/* Given the wildcard bit count in the least-significant 6 of 'wcbits', returns + * an IP netmask with a 1 in each bit that must match and a 0 in each bit that + * is wildcarded. + * + * The bits in 'wcbits' are in the format used in enum ofp_flow_wildcards: 0 + * is exact match, 1 ignores the LSB, 2 ignores the 2 least-significant bits, + * ..., 32 and higher wildcard the entire field. This is the *opposite* of the + * usual convention where e.g. /24 indicates that 8 bits (not 24 bits) are + * wildcarded. */ +ovs_be32 +ofputil_wcbits_to_netmask(int wcbits) +{ + wcbits &= 0x3f; + return wcbits < 32 ? htonl(~((1u << wcbits) - 1)) : 0; +} + +/* Given the IP netmask 'netmask', returns the number of bits of the IP address + * that it wildcards. 'netmask' must be a CIDR netmask (see ip_is_cidr()). */ +int +ofputil_netmask_to_wcbits(ovs_be32 netmask) +{ + assert(ip_is_cidr(netmask)); +#if __GNUC__ >= 4 + return netmask == htonl(0) ? 32 : __builtin_ctz(ntohl(netmask)); +#else + int wcbits; + + for (wcbits = 32; netmask; wcbits--) { + netmask &= netmask - 1; + } + + return wcbits; +#endif +} + +/* A list of the FWW_* and OFPFW_ bits that have the same value, meaning, and + * name. */ +#define WC_INVARIANT_LIST \ + WC_INVARIANT_BIT(IN_PORT) \ + WC_INVARIANT_BIT(DL_SRC) \ + WC_INVARIANT_BIT(DL_DST) \ + WC_INVARIANT_BIT(DL_TYPE) \ + WC_INVARIANT_BIT(NW_PROTO) \ + WC_INVARIANT_BIT(TP_SRC) \ + WC_INVARIANT_BIT(TP_DST) + +/* Verify that all of the invariant bits (as defined on WC_INVARIANT_LIST) + * actually have the same names and values. */ +#define WC_INVARIANT_BIT(NAME) BUILD_ASSERT_DECL(FWW_##NAME == OFPFW_##NAME); + WC_INVARIANT_LIST +#undef WC_INVARIANT_BIT + +/* WC_INVARIANTS is the invariant bits (as defined on WC_INVARIANT_LIST) all + * OR'd together. */ +enum { + WC_INVARIANTS = 0 +#define WC_INVARIANT_BIT(NAME) | FWW_##NAME + WC_INVARIANT_LIST +#undef WC_INVARIANT_BIT +}; + +/* Converts the ofp_match in 'match' into a cls_rule in 'rule', with the given + * 'priority'. + * + * 'flow_format' must either NXFF_OPENFLOW10 or NXFF_TUN_ID_FROM_COOKIE. In + * the latter case only, 'flow''s tun_id field will be taken from the high bits + * of 'cookie', if 'match''s wildcards do not indicate that tun_id is + * wildcarded. */ +void +ofputil_cls_rule_from_match(const struct ofp_match *match, + unsigned int priority, int flow_format, + uint64_t cookie, struct cls_rule *rule) +{ + struct flow_wildcards *wc = &rule->wc; + unsigned int ofpfw; + ovs_be16 vid, pcp; + + /* Initialize rule->priority. */ + ofpfw = ntohl(match->wildcards); + ofpfw &= flow_format == NXFF_TUN_ID_FROM_COOKIE ? OVSFW_ALL : OFPFW_ALL; + rule->priority = !ofpfw ? UINT16_MAX : priority; + + /* Initialize most of rule->wc. */ + wc->wildcards = ofpfw & WC_INVARIANTS; + if (ofpfw & OFPFW_NW_TOS) { + wc->wildcards |= FWW_NW_TOS; + } + memset(wc->reg_masks, 0, sizeof wc->reg_masks); + wc->nw_src_mask = ofputil_wcbits_to_netmask(ofpfw >> OFPFW_NW_SRC_SHIFT); + wc->nw_dst_mask = ofputil_wcbits_to_netmask(ofpfw >> OFPFW_NW_DST_SHIFT); + + if (flow_format == NXFF_TUN_ID_FROM_COOKIE && !(ofpfw & NXFW_TUN_ID)) { + rule->flow.tun_id = htonl(ntohll(cookie) >> 32); + } else { + wc->wildcards |= FWW_TUN_ID; + rule->flow.tun_id = 0; + } + + if (ofpfw & OFPFW_DL_DST) { + /* OpenFlow 1.0 OFPFW_DL_DST covers the whole Ethernet destination, but + * Open vSwitch breaks the Ethernet destination into bits as FWW_DL_DST + * and FWW_ETH_MCAST. */ + wc->wildcards |= FWW_ETH_MCAST; + } + + /* Initialize most of rule->flow. */ + rule->flow.nw_src = match->nw_src; + rule->flow.nw_dst = match->nw_dst; + rule->flow.in_port = (match->in_port == htons(OFPP_LOCAL) ? ODPP_LOCAL + : ntohs(match->in_port)); + rule->flow.dl_type = match->dl_type; + rule->flow.tp_src = match->tp_src; + rule->flow.tp_dst = match->tp_dst; + memcpy(rule->flow.dl_src, match->dl_src, ETH_ADDR_LEN); + memcpy(rule->flow.dl_dst, match->dl_dst, ETH_ADDR_LEN); + rule->flow.nw_tos = match->nw_tos; + rule->flow.nw_proto = match->nw_proto; + + /* Translate VLANs. */ + vid = match->dl_vlan & htons(VLAN_VID_MASK); + pcp = htons((match->dl_vlan_pcp << VLAN_PCP_SHIFT) & VLAN_PCP_MASK); + switch (ofpfw & (OFPFW_DL_VLAN | OFPFW_DL_VLAN_PCP)) { + case OFPFW_DL_VLAN | OFPFW_DL_VLAN_PCP: + /* Wildcard everything. */ + rule->flow.vlan_tci = htons(0); + rule->wc.vlan_tci_mask = htons(0); + break; + + case OFPFW_DL_VLAN_PCP: + if (match->dl_vlan == htons(OFP_VLAN_NONE)) { + /* Match only packets without 802.1Q header. */ + rule->flow.vlan_tci = htons(0); + rule->wc.vlan_tci_mask = htons(0xffff); + } else { + /* Wildcard PCP, specific VID. */ + rule->flow.vlan_tci = vid | htons(VLAN_CFI); + rule->wc.vlan_tci_mask = htons(VLAN_VID_MASK | VLAN_CFI); + } + break; + + case OFPFW_DL_VLAN: + /* Wildcard VID, specific PCP. */ + rule->flow.vlan_tci = pcp | htons(VLAN_CFI); + rule->wc.vlan_tci_mask = htons(VLAN_PCP_MASK | VLAN_CFI); + break; + + case 0: + if (match->dl_vlan == htons(OFP_VLAN_NONE)) { + /* This case is odd, since we can't have a specific PCP without an + * 802.1Q header. However, older versions of OVS treated this as + * matching packets withut an 802.1Q header, so we do here too. */ + rule->flow.vlan_tci = htons(0); + rule->wc.vlan_tci_mask = htons(0xffff); + } else { + /* Specific VID and PCP. */ + rule->flow.vlan_tci = vid | pcp | htons(VLAN_CFI); + rule->wc.vlan_tci_mask = htons(0xffff); + } + break; + } + + /* Clean up. */ + cls_rule_zero_wildcarded_fields(rule); +} + +/* Extract 'flow' with 'wildcards' into the OpenFlow match structure + * 'match'. + * + * 'flow_format' must either NXFF_OPENFLOW10 or NXFF_TUN_ID_FROM_COOKIE. In + * the latter case only, 'match''s NXFW_TUN_ID bit will be filled in; otherwise + * it is always set to 0. */ +void +ofputil_cls_rule_to_match(const struct cls_rule *rule, int flow_format, + struct ofp_match *match) +{ + const struct flow_wildcards *wc = &rule->wc; + unsigned int ofpfw; + + /* Figure out most OpenFlow wildcards. */ + ofpfw = wc->wildcards & WC_INVARIANTS; + ofpfw |= ofputil_netmask_to_wcbits(wc->nw_src_mask) << OFPFW_NW_SRC_SHIFT; + ofpfw |= ofputil_netmask_to_wcbits(wc->nw_dst_mask) << OFPFW_NW_DST_SHIFT; + if (wc->wildcards & FWW_NW_TOS) { + ofpfw |= OFPFW_NW_TOS; + } + if (flow_format == NXFF_TUN_ID_FROM_COOKIE && wc->wildcards & FWW_TUN_ID) { + ofpfw |= NXFW_TUN_ID; + } + + /* Translate VLANs. */ + match->dl_vlan = htons(0); + match->dl_vlan_pcp = 0; + if (rule->wc.vlan_tci_mask == htons(0)) { + ofpfw |= OFPFW_DL_VLAN | OFPFW_DL_VLAN_PCP; + } else if (rule->wc.vlan_tci_mask & htons(VLAN_CFI) + && !(rule->flow.vlan_tci & htons(VLAN_CFI))) { + match->dl_vlan = htons(OFP_VLAN_NONE); + } else { + if (!(rule->wc.vlan_tci_mask & htons(VLAN_VID_MASK))) { + ofpfw |= OFPFW_DL_VLAN; + } else { + match->dl_vlan = htons(vlan_tci_to_vid(rule->flow.vlan_tci)); + } + + if (!(rule->wc.vlan_tci_mask & htons(VLAN_PCP_MASK))) { + ofpfw |= OFPFW_DL_VLAN_PCP; + } else { + match->dl_vlan_pcp = vlan_tci_to_pcp(rule->flow.vlan_tci); + } + } + + /* Compose most of the match structure. */ + match->wildcards = htonl(ofpfw); + match->in_port = htons(rule->flow.in_port == ODPP_LOCAL ? OFPP_LOCAL + : rule->flow.in_port); + memcpy(match->dl_src, rule->flow.dl_src, ETH_ADDR_LEN); + memcpy(match->dl_dst, rule->flow.dl_dst, ETH_ADDR_LEN); + match->dl_type = rule->flow.dl_type; + match->nw_src = rule->flow.nw_src; + match->nw_dst = rule->flow.nw_dst; + match->nw_tos = rule->flow.nw_tos; + match->nw_proto = rule->flow.nw_proto; + match->tp_src = rule->flow.tp_src; + match->tp_dst = rule->flow.tp_dst; + memset(match->pad1, '\0', sizeof match->pad1); + memset(match->pad2, '\0', sizeof match->pad2); +} + +/* Returns a transaction ID to use for an outgoing OpenFlow message. */ +static ovs_be32 alloc_xid(void) { - return random_uint32(); + static uint32_t next_xid = 1; + return htonl(next_xid++); } /* Allocates and stores in '*bufferp' a new ofpbuf with a size of @@ -59,6 +289,14 @@ make_openflow(size_t openflow_len, uint8_t type, struct ofpbuf **bufferp) return put_openflow_xid(openflow_len, type, alloc_xid(), *bufferp); } +/* Similar to make_openflow() but creates a Nicira vendor extension message + * with the specific 'subtype'. 'subtype' should be in host byte order. */ +void * +make_nxmsg(size_t openflow_len, uint32_t subtype, struct ofpbuf **bufferp) +{ + return make_nxmsg_xid(openflow_len, subtype, alloc_xid(), bufferp); +} + /* Allocates and stores in '*bufferp' a new ofpbuf with a size of * 'openflow_len', starting with an OpenFlow header with the given 'type' and * transaction id 'xid'. Allocated bytes beyond the header, if any, are @@ -73,13 +311,26 @@ make_openflow(size_t openflow_len, uint8_t type, struct ofpbuf **bufferp) * * Returns the header. */ void * -make_openflow_xid(size_t openflow_len, uint8_t type, uint32_t xid, +make_openflow_xid(size_t openflow_len, uint8_t type, ovs_be32 xid, struct ofpbuf **bufferp) { *bufferp = ofpbuf_new(openflow_len); return put_openflow_xid(openflow_len, type, xid, *bufferp); } +/* Similar to make_openflow_xid() but creates a Nicira vendor extension message + * with the specific 'subtype'. 'subtype' should be in host byte order. */ +void * +make_nxmsg_xid(size_t openflow_len, uint32_t subtype, ovs_be32 xid, + struct ofpbuf **bufferp) +{ + struct nicira_header *nxh = make_openflow_xid(openflow_len, OFPT_VENDOR, + xid, bufferp); + nxh->vendor = htonl(NX_VENDOR_ID); + nxh->subtype = htonl(subtype); + return nxh; +} + /* Appends 'openflow_len' bytes to 'buffer', starting with an OpenFlow header * with the given 'type' and an arbitrary transaction id. Allocated bytes * beyond the header, if any, are zeroed. @@ -105,7 +356,7 @@ put_openflow(size_t openflow_len, uint8_t type, struct ofpbuf *buffer) * * Returns the header. */ void * -put_openflow_xid(size_t openflow_len, uint8_t type, uint32_t xid, +put_openflow_xid(size_t openflow_len, uint8_t type, ovs_be32 xid, struct ofpbuf *buffer) { struct ofp_header *oh; @@ -132,7 +383,8 @@ update_openflow_length(struct ofpbuf *buffer) } struct ofpbuf * -make_flow_mod(uint16_t command, const struct flow *flow, size_t actions_len) +make_flow_mod(uint16_t command, const struct cls_rule *rule, + size_t actions_len) { struct ofp_flow_mod *ofm; size_t size = sizeof *ofm + actions_len; @@ -142,29 +394,17 @@ make_flow_mod(uint16_t command, const struct flow *flow, size_t actions_len) ofm->header.type = OFPT_FLOW_MOD; ofm->header.length = htons(size); ofm->cookie = 0; - ofm->match.wildcards = htonl(0); - ofm->match.in_port = htons(flow->in_port == ODPP_LOCAL ? OFPP_LOCAL - : flow->in_port); - memcpy(ofm->match.dl_src, flow->dl_src, sizeof ofm->match.dl_src); - memcpy(ofm->match.dl_dst, flow->dl_dst, sizeof ofm->match.dl_dst); - ofm->match.dl_vlan = flow->dl_vlan; - ofm->match.dl_vlan_pcp = flow->dl_vlan_pcp; - ofm->match.dl_type = flow->dl_type; - ofm->match.nw_src = flow->nw_src; - ofm->match.nw_dst = flow->nw_dst; - ofm->match.nw_proto = flow->nw_proto; - ofm->match.nw_tos = flow->nw_tos; - ofm->match.tp_src = flow->tp_src; - ofm->match.tp_dst = flow->tp_dst; + ofm->priority = htons(MIN(rule->priority, UINT16_MAX)); + ofputil_cls_rule_to_match(rule, NXFF_OPENFLOW10, &ofm->match); ofm->command = htons(command); return out; } struct ofpbuf * -make_add_flow(const struct flow *flow, uint32_t buffer_id, +make_add_flow(const struct cls_rule *rule, uint32_t buffer_id, uint16_t idle_timeout, size_t actions_len) { - struct ofpbuf *out = make_flow_mod(OFPFC_ADD, flow, actions_len); + struct ofpbuf *out = make_flow_mod(OFPFC_ADD, rule, actions_len); struct ofp_flow_mod *ofm = out->data; ofm->idle_timeout = htons(idle_timeout); ofm->hard_timeout = htons(OFP_FLOW_PERMANENT); @@ -173,16 +413,16 @@ make_add_flow(const struct flow *flow, uint32_t buffer_id, } struct ofpbuf * -make_del_flow(const struct flow *flow) +make_del_flow(const struct cls_rule *rule) { - struct ofpbuf *out = make_flow_mod(OFPFC_DELETE_STRICT, flow, 0); + struct ofpbuf *out = make_flow_mod(OFPFC_DELETE_STRICT, rule, 0); struct ofp_flow_mod *ofm = out->data; ofm->out_port = htons(OFPP_NONE); return out; } struct ofpbuf * -make_add_simple_flow(const struct flow *flow, +make_add_simple_flow(const struct cls_rule *rule, uint32_t buffer_id, uint16_t out_port, uint16_t idle_timeout) { @@ -190,14 +430,14 @@ make_add_simple_flow(const struct flow *flow, struct ofp_action_output *oao; struct ofpbuf *buffer; - buffer = make_add_flow(flow, buffer_id, idle_timeout, sizeof *oao); + buffer = make_add_flow(rule, buffer_id, idle_timeout, sizeof *oao); oao = ofpbuf_put_zeros(buffer, sizeof *oao); oao->type = htons(OFPAT_OUTPUT); oao->len = htons(sizeof *oao); oao->port = htons(out_port); return buffer; } else { - return make_add_flow(flow, buffer_id, idle_timeout, 0); + return make_add_flow(rule, buffer_id, idle_timeout, 0); } } @@ -286,7 +526,7 @@ make_echo_request(void) rq->version = OFP_VERSION; rq->type = OFPT_ECHO_REQUEST; rq->length = htons(sizeof *rq); - rq->xid = 0; + rq->xid = htonl(0); return out; } @@ -394,50 +634,6 @@ check_ofp_message_array(const struct ofp_header *msg, uint8_t type, return 0; } -int -check_ofp_packet_out(const struct ofp_header *oh, struct ofpbuf *data, - int *n_actionsp, int max_ports) -{ - const struct ofp_packet_out *opo; - unsigned int actions_len, n_actions; - size_t extra; - int error; - - *n_actionsp = 0; - error = check_ofp_message_array(oh, OFPT_PACKET_OUT, - sizeof *opo, 1, &extra); - if (error) { - return error; - } - opo = (const struct ofp_packet_out *) oh; - - actions_len = ntohs(opo->actions_len); - if (actions_len > extra) { - VLOG_WARN_RL(&bad_ofmsg_rl, "packet-out claims %u bytes of actions " - "but message has room for only %zu bytes", - actions_len, extra); - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); - } - if (actions_len % sizeof(union ofp_action)) { - VLOG_WARN_RL(&bad_ofmsg_rl, "packet-out claims %u bytes of actions, " - "which is not a multiple of %zu", - actions_len, sizeof(union ofp_action)); - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); - } - - n_actions = actions_len / sizeof(union ofp_action); - error = validate_actions((const union ofp_action *) opo->actions, - n_actions, max_ports); - if (error) { - return error; - } - - data->data = (void *) &opo->actions[n_actions]; - data->size = extra - actions_len; - *n_actionsp = n_actions; - return 0; -} - const struct ofp_flow_stats * flow_stats_first(struct flow_stats_iterator *iter, const struct ofp_stats_reply *osr) @@ -483,9 +679,6 @@ flow_stats_next(struct flow_stats_iterator *iter) return fs; } -/* Alignment of ofp_actions. */ -#define ACTION_ALIGNMENT 8 - static int check_action_exact_len(const union ofp_action *a, unsigned int len, unsigned int required_len) @@ -550,9 +743,11 @@ check_enqueue_action(const union ofp_action *a, unsigned int len, } static int -check_nicira_action(const union ofp_action *a, unsigned int len) +check_nicira_action(const union ofp_action *a, unsigned int len, + const struct flow *flow) { const struct nx_action_header *nah; + int error; if (len < 16) { VLOG_DBG_RL(&bad_ofmsg_rl, @@ -568,13 +763,34 @@ check_nicira_action(const union ofp_action *a, unsigned int len) case NXAST_SET_QUEUE: case NXAST_POP_QUEUE: return check_action_exact_len(a, len, 16); + + case NXAST_REG_MOVE: + error = check_action_exact_len(a, len, + sizeof(struct nx_action_reg_move)); + if (error) { + return error; + } + return nxm_check_reg_move((const struct nx_action_reg_move *) a, flow); + + case NXAST_REG_LOAD: + error = check_action_exact_len(a, len, + sizeof(struct nx_action_reg_load)); + if (error) { + return error; + } + return nxm_check_reg_load((const struct nx_action_reg_load *) a, flow); + + case NXAST_NOTE: + return 0; + default: return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR_TYPE); } } static int -check_action(const union ofp_action *a, unsigned int len, int max_ports) +check_action(const union ofp_action *a, unsigned int len, + const struct flow *flow, int max_ports) { int error; @@ -620,7 +836,7 @@ check_action(const union ofp_action *a, unsigned int len, int max_ports) case OFPAT_VENDOR: return (a->vendor.vendor == htonl(NX_VENDOR_ID) - ? check_nicira_action(a, len) + ? check_nicira_action(a, len, flow) : ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR)); case OFPAT_ENQUEUE: @@ -635,14 +851,14 @@ check_action(const union ofp_action *a, unsigned int len, int max_ports) int validate_actions(const union ofp_action *actions, size_t n_actions, - int max_ports) + const struct flow *flow, int max_ports) { size_t i; for (i = 0; i < n_actions; ) { const union ofp_action *a = &actions[i]; unsigned int len = ntohs(a->header.len); - unsigned int n_slots = len / ACTION_ALIGNMENT; + unsigned int n_slots = len / OFP_ACTION_ALIGN; unsigned int slots_left = &actions[n_actions] - a; int error; @@ -654,13 +870,13 @@ validate_actions(const union ofp_action *actions, size_t n_actions, } else if (!len) { VLOG_DBG_RL(&bad_ofmsg_rl, "action has invalid length 0"); return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN); - } else if (len % ACTION_ALIGNMENT) { + } else if (len % OFP_ACTION_ALIGN) { VLOG_DBG_RL(&bad_ofmsg_rl, "action length %u is not a multiple " - "of %d", len, ACTION_ALIGNMENT); + "of %d", len, OFP_ACTION_ALIGN); return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN); } - error = check_action(a, len, max_ports); + error = check_action(a, len, flow, max_ports); if (error) { return error; } @@ -701,7 +917,7 @@ actions_next(struct actions_iterator *iter) if (iter->pos != iter->end) { const union ofp_action *a = iter->pos; unsigned int len = ntohs(a->header.len); - iter->pos += len / ACTION_ALIGNMENT; + iter->pos += len / OFP_ACTION_ALIGN; return a; } else { return NULL; @@ -749,10 +965,10 @@ normalize_match(struct ofp_match *m) m->tp_src = m->tp_dst = 0; } if (wc & OFPFW_NW_SRC_MASK) { - m->nw_src &= flow_nw_bits_to_mask(wc, OFPFW_NW_SRC_SHIFT); + m->nw_src &= ofputil_wcbits_to_netmask(wc >> OFPFW_NW_SRC_SHIFT); } if (wc & OFPFW_NW_DST_MASK) { - m->nw_dst &= flow_nw_bits_to_mask(wc, OFPFW_NW_DST_SHIFT); + m->nw_dst &= ofputil_wcbits_to_netmask(wc >> OFPFW_NW_DST_SHIFT); } if (wc & OFPFW_NW_TOS) { m->nw_tos = 0; @@ -764,10 +980,10 @@ normalize_match(struct ofp_match *m) m->nw_proto = 0; } if (wc & OFPFW_NW_SRC_MASK) { - m->nw_src &= flow_nw_bits_to_mask(wc, OFPFW_NW_SRC_SHIFT); + m->nw_src &= ofputil_wcbits_to_netmask(wc >> OFPFW_NW_SRC_SHIFT); } if (wc & OFPFW_NW_DST_MASK) { - m->nw_dst &= flow_nw_bits_to_mask(wc, OFPFW_NW_DST_SHIFT); + m->nw_dst &= ofputil_wcbits_to_netmask(wc >> OFPFW_NW_DST_SHIFT); } m->tp_src = m->tp_dst = m->nw_tos = 0; } else { @@ -854,7 +1070,7 @@ make_ofp_error_msg(int error, const struct ofp_header *oh) uint8_t vendor; uint16_t type; uint16_t code; - uint32_t xid; + ovs_be32 xid; if (!is_ofp_error(error)) { /* We format 'error' with strerror() here since it seems likely to be @@ -915,3 +1131,41 @@ make_ofp_error_msg(int error, const struct ofp_header *oh) return buf; } + +/* Attempts to pull 'actions_len' bytes from the front of 'b'. Returns 0 if + * successful, otherwise an OpenFlow error. + * + * If successful, the first action is stored in '*actionsp' and the number of + * "union ofp_action" size elements into '*n_actionsp'. Otherwise NULL and 0 + * are stored, respectively. + * + * This function does not check that the actions are valid (the caller should + * do so, with validate_actions()). The caller is also responsible for making + * sure that 'b->data' is initially aligned appropriately for "union + * ofp_action". */ +int +ofputil_pull_actions(struct ofpbuf *b, unsigned int actions_len, + union ofp_action **actionsp, size_t *n_actionsp) +{ + if (actions_len % OFP_ACTION_ALIGN != 0) { + VLOG_DBG_RL(&bad_ofmsg_rl, "OpenFlow message actions length %u " + "is not a multiple of %d", actions_len, OFP_ACTION_ALIGN); + goto error; + } + + *actionsp = ofpbuf_try_pull(b, actions_len); + if (*actionsp == NULL) { + VLOG_DBG_RL(&bad_ofmsg_rl, "OpenFlow message actions length %u " + "exceeds remaining message length (%zu)", + actions_len, b->size); + goto error; + } + + *n_actionsp = actions_len / OFP_ACTION_ALIGN; + return 0; + +error: + *actionsp = NULL; + *n_actionsp = 0; + return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); +}