X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fofp-util.c;h=ffdccda43ef1f0d5f7de19dbbe0fc6641137ae34;hb=eed8052c973dccdcf72a7e2aa7c6f8aa32737431;hp=a00939dcf1bed4fbf690314268871a2a646c596b;hpb=a1893da130e2bfadcd031476f00a365677a9ff61;p=sliver-openvswitch.git diff --git a/lib/ofp-util.c b/lib/ofp-util.c index a00939dcf..ffdccda43 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -29,6 +29,7 @@ #include "dynamic-string.h" #include "learn.h" #include "multipath.h" +#include "meta-flow.h" #include "nx-match.h" #include "ofp-errors.h" #include "ofp-util.h" @@ -77,9 +78,7 @@ ofputil_netmask_to_wcbits(ovs_be32 netmask) 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) + WC_INVARIANT_BIT(NW_PROTO) /* Verify that all of the invariant bits (as defined on WC_INVARIANT_LIST) * actually have the same names and values. */ @@ -101,7 +100,7 @@ static const flow_wildcards_t WC_INVARIANTS = 0 void ofputil_wildcard_from_openflow(uint32_t ofpfw, struct flow_wildcards *wc) { - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 7); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 8); /* Initialize most of rule->wc. */ flow_wildcards_init_catchall(wc); @@ -120,6 +119,13 @@ ofputil_wildcard_from_openflow(uint32_t ofpfw, struct flow_wildcards *wc) 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 (!(ofpfw & OFPFW_TP_SRC)) { + wc->tp_src_mask = htons(UINT16_MAX); + } + if (!(ofpfw & OFPFW_TP_DST)) { + wc->tp_dst_mask = htons(UINT16_MAX); + } + 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 @@ -199,6 +205,12 @@ ofputil_cls_rule_to_match(const struct cls_rule *rule, struct ofp_match *match) if (wc->wildcards & FWW_NW_DSCP) { ofpfw |= OFPFW_NW_TOS; } + if (!wc->tp_src_mask) { + ofpfw |= OFPFW_TP_SRC; + } + if (!wc->tp_dst_mask) { + ofpfw |= OFPFW_TP_DST; + } /* Translate VLANs. */ match->dl_vlan = htons(0); @@ -289,10 +301,10 @@ struct ofputil_msg_category { const char *name; /* e.g. "OpenFlow message" */ const struct ofputil_msg_type *types; size_t n_types; - int missing_error; /* ofp_mkerr() value for missing type. */ + enum ofperr missing_error; /* Error value for missing type. */ }; -static int +static enum ofperr ofputil_check_length(const struct ofputil_msg_type *type, unsigned int size) { switch (type->extra_multiple) { @@ -301,7 +313,7 @@ ofputil_check_length(const struct ofputil_msg_type *type, unsigned int size) VLOG_WARN_RL(&bad_ofmsg_rl, "received %s with incorrect " "length %u (expected length %u)", type->name, size, type->min_size); - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); + return OFPERR_OFPBRC_BAD_LEN; } return 0; @@ -310,7 +322,7 @@ ofputil_check_length(const struct ofputil_msg_type *type, unsigned int size) VLOG_WARN_RL(&bad_ofmsg_rl, "received %s with incorrect " "length %u (expected length at least %u bytes)", type->name, size, type->min_size); - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); + return OFPERR_OFPBRC_BAD_LEN; } return 0; @@ -322,13 +334,13 @@ ofputil_check_length(const struct ofputil_msg_type *type, unsigned int size) "by an integer multiple of %u bytes)", type->name, size, type->min_size, type->extra_multiple); - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); + return OFPERR_OFPBRC_BAD_LEN; } return 0; } } -static int +static enum ofperr ofputil_lookup_openflow_message(const struct ofputil_msg_category *cat, uint8_t version, uint32_t value, const struct ofputil_msg_type **typep) @@ -348,7 +360,7 @@ ofputil_lookup_openflow_message(const struct ofputil_msg_category *cat, return cat->missing_error; } -static int +static enum ofperr ofputil_decode_vendor(const struct ofp_header *oh, size_t length, const struct ofputil_msg_type **typep) { @@ -384,12 +396,20 @@ ofputil_decode_vendor(const struct ofp_header *oh, size_t length, { OFPUTIL_NXT_FLOW_MOD_TABLE_ID, OFP10_VERSION, NXT_FLOW_MOD_TABLE_ID, "NXT_FLOW_MOD_TABLE_ID", sizeof(struct nx_flow_mod_table_id), 0 }, + + { OFPUTIL_NXT_FLOW_AGE, OFP10_VERSION, + NXT_FLOW_AGE, "NXT_FLOW_AGE", + sizeof(struct nicira_header), 0 }, + + { OFPUTIL_NXT_SET_ASYNC_CONFIG, OFP10_VERSION, + NXT_SET_ASYNC_CONFIG, "NXT_SET_ASYNC_CONFIG", + sizeof(struct nx_async_config), 0 }, }; static const struct ofputil_msg_category nxt_category = { "Nicira extension message", nxt_messages, ARRAY_SIZE(nxt_messages), - OFP_MKERR(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE) + OFPERR_OFPBRC_BAD_SUBTYPE }; const struct ofp_vendor_header *ovh; @@ -399,14 +419,14 @@ ofputil_decode_vendor(const struct ofp_header *oh, size_t length, if (length == ntohs(oh->length)) { VLOG_WARN_RL(&bad_ofmsg_rl, "truncated vendor message"); } - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); + return OFPERR_OFPBRC_BAD_LEN; } ovh = (const struct ofp_vendor_header *) oh; if (ovh->vendor != htonl(NX_VENDOR_ID)) { VLOG_WARN_RL(&bad_ofmsg_rl, "received vendor message for unknown " "vendor %"PRIx32, ntohl(ovh->vendor)); - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_VENDOR); + return OFPERR_OFPBRC_BAD_VENDOR; } if (length < sizeof(struct nicira_header)) { @@ -416,7 +436,7 @@ ofputil_decode_vendor(const struct ofp_header *oh, size_t length, ntohs(ovh->header.length), sizeof(struct nicira_header)); } - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); + return OFPERR_OFPBRC_BAD_LEN; } nh = (const struct nicira_header *) oh; @@ -424,7 +444,7 @@ ofputil_decode_vendor(const struct ofp_header *oh, size_t length, ntohl(nh->subtype), typep); } -static int +static enum ofperr check_nxstats_msg(const struct ofp_header *oh, size_t length) { const struct ofp_stats_msg *osm = (const struct ofp_stats_msg *) oh; @@ -434,27 +454,27 @@ check_nxstats_msg(const struct ofp_header *oh, size_t length) if (length == ntohs(oh->length)) { VLOG_WARN_RL(&bad_ofmsg_rl, "truncated vendor stats message"); } - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); + return OFPERR_OFPBRC_BAD_LEN; } memcpy(&vendor, osm + 1, sizeof vendor); if (vendor != htonl(NX_VENDOR_ID)) { VLOG_WARN_RL(&bad_ofmsg_rl, "received vendor stats message for " "unknown vendor %"PRIx32, ntohl(vendor)); - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_VENDOR); + return OFPERR_OFPBRC_BAD_VENDOR; } if (length < sizeof(struct nicira_stats_msg)) { if (length == ntohs(osm->header.length)) { VLOG_WARN_RL(&bad_ofmsg_rl, "truncated Nicira stats message"); } - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); + return OFPERR_OFPBRC_BAD_LEN; } return 0; } -static int +static enum ofperr ofputil_decode_nxst_request(const struct ofp_header *oh, size_t length, const struct ofputil_msg_type **typep) { @@ -471,11 +491,11 @@ ofputil_decode_nxst_request(const struct ofp_header *oh, size_t length, static const struct ofputil_msg_category nxst_request_category = { "Nicira extension statistics request", nxst_requests, ARRAY_SIZE(nxst_requests), - OFP_MKERR(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE) + OFPERR_OFPBRC_BAD_SUBTYPE }; const struct nicira_stats_msg *nsm; - int error; + enum ofperr error; error = check_nxstats_msg(oh, length); if (error) { @@ -487,7 +507,7 @@ ofputil_decode_nxst_request(const struct ofp_header *oh, size_t length, ntohl(nsm->subtype), typep); } -static int +static enum ofperr ofputil_decode_nxst_reply(const struct ofp_header *oh, size_t length, const struct ofputil_msg_type **typep) { @@ -504,11 +524,11 @@ ofputil_decode_nxst_reply(const struct ofp_header *oh, size_t length, static const struct ofputil_msg_category nxst_reply_category = { "Nicira extension statistics reply", nxst_replies, ARRAY_SIZE(nxst_replies), - OFP_MKERR(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE) + OFPERR_OFPBRC_BAD_SUBTYPE }; const struct nicira_stats_msg *nsm; - int error; + enum ofperr error; error = check_nxstats_msg(oh, length); if (error) { @@ -520,20 +540,20 @@ ofputil_decode_nxst_reply(const struct ofp_header *oh, size_t length, ntohl(nsm->subtype), typep); } -static int +static enum ofperr check_stats_msg(const struct ofp_header *oh, size_t length) { if (length < sizeof(struct ofp_stats_msg)) { if (length == ntohs(oh->length)) { VLOG_WARN_RL(&bad_ofmsg_rl, "truncated stats message"); } - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); + return OFPERR_OFPBRC_BAD_LEN; } return 0; } -static int +static enum ofperr ofputil_decode_ofpst_request(const struct ofp_header *oh, size_t length, const struct ofputil_msg_type **typep) { @@ -570,11 +590,11 @@ ofputil_decode_ofpst_request(const struct ofp_header *oh, size_t length, static const struct ofputil_msg_category ofpst_request_category = { "OpenFlow statistics", ofpst_requests, ARRAY_SIZE(ofpst_requests), - OFP_MKERR(OFPET_BAD_REQUEST, OFPBRC_BAD_STAT) + OFPERR_OFPBRC_BAD_STAT }; const struct ofp_stats_msg *request = (const struct ofp_stats_msg *) oh; - int error; + enum ofperr error; error = check_stats_msg(oh, length); if (error) { @@ -590,7 +610,7 @@ ofputil_decode_ofpst_request(const struct ofp_header *oh, size_t length, return error; } -static int +static enum ofperr ofputil_decode_ofpst_reply(const struct ofp_header *oh, size_t length, const struct ofputil_msg_type **typep) { @@ -627,11 +647,11 @@ ofputil_decode_ofpst_reply(const struct ofp_header *oh, size_t length, static const struct ofputil_msg_category ofpst_reply_category = { "OpenFlow statistics", ofpst_replies, ARRAY_SIZE(ofpst_replies), - OFP_MKERR(OFPET_BAD_REQUEST, OFPBRC_BAD_STAT) + OFPERR_OFPBRC_BAD_STAT }; const struct ofp_stats_msg *reply = (const struct ofp_stats_msg *) oh; - int error; + enum ofperr error; error = check_stats_msg(oh, length); if (error) { @@ -646,7 +666,7 @@ ofputil_decode_ofpst_reply(const struct ofp_header *oh, size_t length, return error; } -static int +static enum ofperr ofputil_decode_msg_type__(const struct ofp_header *oh, size_t length, const struct ofputil_msg_type **typep) { @@ -655,7 +675,7 @@ ofputil_decode_msg_type__(const struct ofp_header *oh, size_t length, OFPT_HELLO, "OFPT_HELLO", sizeof(struct ofp_hello), 1 }, - { OFPUTIL_OFPT_ERROR, OFP10_VERSION, + { OFPUTIL_OFPT_ERROR, 0, OFPT_ERROR, "OFPT_ERROR", sizeof(struct ofp_error_msg), 1 }, @@ -735,10 +755,10 @@ ofputil_decode_msg_type__(const struct ofp_header *oh, size_t length, static const struct ofputil_msg_category ofpt_category = { "OpenFlow message", ofpt_messages, ARRAY_SIZE(ofpt_messages), - OFP_MKERR(OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE) + OFPERR_OFPBRC_BAD_TYPE }; - int error; + enum ofperr error; error = ofputil_lookup_openflow_message(&ofpt_category, oh->version, oh->type, typep); @@ -762,22 +782,21 @@ ofputil_decode_msg_type__(const struct ofp_header *oh, size_t length, return error; } -/* Decodes the message type represented by 'oh'. Returns 0 if successful or - * an OpenFlow error code constructed with ofp_mkerr() on failure. Either - * way, stores in '*typep' a type structure that can be inspected with the - * ofputil_msg_type_*() functions. +/* Decodes the message type represented by 'oh'. Returns 0 if successful or an + * OpenFlow error code on failure. Either way, stores in '*typep' a type + * structure that can be inspected with the ofputil_msg_type_*() functions. * * oh->length must indicate the correct length of the message (and must be at * least sizeof(struct ofp_header)). * * Success indicates that 'oh' is at least as long as the minimum-length * message of its type. */ -int +enum ofperr ofputil_decode_msg_type(const struct ofp_header *oh, const struct ofputil_msg_type **typep) { size_t length = ntohs(oh->length); - int error; + enum ofperr error; error = ofputil_decode_msg_type__(oh, length, typep); if (!error) { @@ -791,18 +810,17 @@ ofputil_decode_msg_type(const struct ofp_header *oh, /* Decodes the message type represented by 'oh', of which only the first * 'length' bytes are available. Returns 0 if successful or an OpenFlow error - * code constructed with ofp_mkerr() on failure. Either way, stores in - * '*typep' a type structure that can be inspected with the - * ofputil_msg_type_*() functions. */ -int + * code on failure. Either way, stores in '*typep' a type structure that can + * be inspected with the ofputil_msg_type_*() functions. */ +enum ofperr ofputil_decode_msg_type_partial(const struct ofp_header *oh, size_t length, const struct ofputil_msg_type **typep) { - int error; + enum ofperr error; error = (length >= sizeof *oh ? ofputil_decode_msg_type__(oh, length, typep) - : ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN)); + : OFPERR_OFPBRC_BAD_LEN); if (error) { *typep = &ofputil_invalid_type; } @@ -906,7 +924,7 @@ ofputil_min_flow_format(const struct cls_rule *rule) { const struct flow_wildcards *wc = &rule->wc; - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 7); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 8); /* Only NXM supports separately wildcards the Ethernet multicast bit. */ if (!(wc->wildcards & FWW_DL_DST) != !(wc->wildcards & FWW_ETH_MCAST)) { @@ -954,6 +972,12 @@ ofputil_min_flow_format(const struct cls_rule *rule) return NXFF_NXM; } + /* Only NXM supports bitwise matching on transport port. */ + if ((wc->tp_src_mask && wc->tp_src_mask != htons(UINT16_MAX)) || + (wc->tp_dst_mask && wc->tp_dst_mask != htons(UINT16_MAX))) { + return NXFF_NXM; + } + /* Other formats can express this rule. */ return NXFF_OPENFLOW10; } @@ -1005,7 +1029,7 @@ ofputil_make_flow_mod_table_id(bool flow_mod_table_id) * enabled, false otherwise. * * Does not validate the flow_mod actions. */ -int +enum ofperr ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, const struct ofp_header *oh, bool flow_mod_table_id) { @@ -1020,7 +1044,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, /* Standard OpenFlow flow_mod. */ const struct ofp_flow_mod *ofm; uint16_t priority; - int error; + enum ofperr error; /* Dissect the message. */ ofm = ofpbuf_pull(&b, sizeof *ofm); @@ -1054,7 +1078,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, } else if (ofputil_msg_type_code(type) == OFPUTIL_NXT_FLOW_MOD) { /* Nicira extended flow_mod. */ const struct nx_flow_mod *nfm; - int error; + enum ofperr error; /* Dissect the message. */ nfm = ofpbuf_pull(&b, sizeof *nfm); @@ -1075,7 +1099,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, /* The "NXM_NX_COOKIE*" matches are not valid for flow * additions. Additions must use the "cookie" field of * the "nx_flow_mod" structure. */ - return ofp_mkerr(OFPET_BAD_REQUEST, NXBRC_NXM_INVALID); + return OFPERR_NXBRC_NXM_INVALID; } else { fm->cookie = nfm->cookie; fm->cookie_mask = htonll(UINT64_MAX); @@ -1165,7 +1189,7 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm, return msg; } -static int +static enum ofperr ofputil_decode_ofpst_flow_request(struct ofputil_flow_stats_request *fsr, const struct ofp_header *oh, bool aggregate) @@ -1182,14 +1206,14 @@ ofputil_decode_ofpst_flow_request(struct ofputil_flow_stats_request *fsr, return 0; } -static int +static enum ofperr ofputil_decode_nxst_flow_request(struct ofputil_flow_stats_request *fsr, const struct ofp_header *oh, bool aggregate) { const struct nx_flow_stats_request *nfsr; struct ofpbuf b; - int error; + enum ofperr error; ofpbuf_use_const(&b, oh, ntohs(oh->length)); @@ -1200,7 +1224,7 @@ ofputil_decode_nxst_flow_request(struct ofputil_flow_stats_request *fsr, return error; } if (b.size) { - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); + return OFPERR_OFPBRC_BAD_LEN; } fsr->aggregate = aggregate; @@ -1213,7 +1237,7 @@ ofputil_decode_nxst_flow_request(struct ofputil_flow_stats_request *fsr, /* 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. */ -int +enum ofperr ofputil_decode_flow_stats_request(struct ofputil_flow_stats_request *fsr, const struct ofp_header *oh) { @@ -1291,11 +1315,18 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr, * iterates through the replies. The caller must initially leave 'msg''s layer * pointers null and not modify them between calls. * + * Most switches don't send the values needed to populate fs->idle_age and + * fs->hard_age, so those members will usually be set to 0. If the switch from + * which 'msg' originated is known to implement NXT_FLOW_AGE, then pass + * 'flow_age_extension' as true so that the contents of 'msg' determine the + * 'idle_age' and 'hard_age' members in 'fs'. + * * Returns 0 if successful, EOF if no replies were left in this 'msg', * otherwise a positive errno value. */ int ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs, - struct ofpbuf *msg) + struct ofpbuf *msg, + bool flow_age_extension) { const struct ofputil_msg_type *type; int code; @@ -1346,6 +1377,8 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs, fs->duration_nsec = ntohl(ofs->duration_nsec); fs->idle_timeout = ntohs(ofs->idle_timeout); fs->hard_timeout = ntohs(ofs->hard_timeout); + fs->idle_age = -1; + fs->hard_age = -1; fs->packet_count = ntohll(get_32aligned_be64(&ofs->packet_count)); fs->byte_count = ntohll(get_32aligned_be64(&ofs->byte_count)); } else if (code == OFPUTIL_NXST_FLOW_REPLY) { @@ -1383,6 +1416,16 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs, fs->duration_nsec = ntohl(nfs->duration_nsec); fs->idle_timeout = ntohs(nfs->idle_timeout); fs->hard_timeout = ntohs(nfs->hard_timeout); + fs->idle_age = -1; + fs->hard_age = -1; + if (flow_age_extension) { + if (nfs->idle_age) { + fs->idle_age = ntohs(nfs->idle_age) - 1; + } + if (nfs->hard_age) { + fs->hard_age = ntohs(nfs->hard_age) - 1; + } + } fs->packet_count = ntohll(nfs->packet_count); fs->byte_count = ntohll(nfs->byte_count); } else { @@ -1451,8 +1494,13 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs, nfs->priority = htons(fs->rule.priority); nfs->idle_timeout = htons(fs->idle_timeout); nfs->hard_timeout = htons(fs->hard_timeout); + nfs->idle_age = htons(fs->idle_age < 0 ? 0 + : fs->idle_age < UINT16_MAX ? fs->idle_age + 1 + : UINT16_MAX); + nfs->hard_age = htons(fs->hard_age < 0 ? 0 + : fs->hard_age < UINT16_MAX ? fs->hard_age + 1 + : UINT16_MAX); nfs->match_len = htons(nx_put_match(msg, &fs->rule, 0, 0)); - memset(nfs->pad2, 0, sizeof nfs->pad2); nfs->cookie = fs->cookie; nfs->packet_count = htonll(fs->packet_count); nfs->byte_count = htonll(fs->byte_count); @@ -1499,7 +1547,7 @@ ofputil_encode_aggregate_stats_reply( /* Converts an OFPT_FLOW_REMOVED or NXT_FLOW_REMOVED message 'oh' into an * abstract ofputil_flow_removed in 'fr'. Returns 0 if successful, otherwise * an OpenFlow error code. */ -int +enum ofperr ofputil_decode_flow_removed(struct ofputil_flow_removed *fr, const struct ofp_header *oh) { @@ -1535,7 +1583,7 @@ ofputil_decode_flow_removed(struct ofputil_flow_removed *fr, return error; } if (b.size) { - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); + return OFPERR_OFPBRC_BAD_LEN; } fr->cookie = nfr->cookie; @@ -1637,7 +1685,7 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin, } if (!ofpbuf_try_pull(&b, 2)) { - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); + return OFPERR_OFPBRC_BAD_LEN; } pin->packet = b.data; @@ -1736,6 +1784,70 @@ ofputil_encode_packet_in(const struct ofputil_packet_in *pin, return packet; } +enum ofperr +ofputil_decode_packet_out(struct ofputil_packet_out *po, + const struct ofp_packet_out *opo) +{ + enum ofperr error; + struct ofpbuf b; + + po->buffer_id = ntohl(opo->buffer_id); + po->in_port = ntohs(opo->in_port); + if (po->in_port >= OFPP_MAX && po->in_port != OFPP_LOCAL + && po->in_port != OFPP_NONE) { + VLOG_WARN_RL(&bad_ofmsg_rl, "packet-out has bad input port %#"PRIx16, + po->in_port); + return OFPERR_NXBRC_BAD_IN_PORT; + } + + ofpbuf_use_const(&b, opo, ntohs(opo->header.length)); + ofpbuf_pull(&b, sizeof *opo); + + error = ofputil_pull_actions(&b, ntohs(opo->actions_len), + &po->actions, &po->n_actions); + if (error) { + return error; + } + + if (po->buffer_id == UINT32_MAX) { + po->packet = b.data; + po->packet_len = b.size; + } else { + po->packet = NULL; + po->packet_len = 0; + } + + return 0; +} + +struct ofpbuf * +ofputil_encode_packet_out(const struct ofputil_packet_out *po) +{ + struct ofp_packet_out *opo; + size_t actions_len; + struct ofpbuf *msg; + size_t size; + + actions_len = po->n_actions * sizeof *po->actions; + size = sizeof *opo + actions_len; + if (po->buffer_id == UINT32_MAX) { + size += po->packet_len; + } + + msg = ofpbuf_new(size); + opo = put_openflow(sizeof *opo, OFPT_PACKET_OUT, msg); + opo->buffer_id = htonl(po->buffer_id); + opo->in_port = htons(po->in_port); + opo->actions_len = htons(actions_len); + ofpbuf_put(msg, po->actions, actions_len); + if (po->buffer_id == UINT32_MAX) { + ofpbuf_put(msg, po->packet, po->packet_len); + } + update_openflow_length(msg); + + return msg; +} + /* Returns a string representing the message type of 'type'. The string is the * enumeration constant for the type, e.g. "OFPT_HELLO". For statistics * messages, the constant is followed by "request" or "reply", @@ -2114,59 +2226,6 @@ make_packet_in(uint32_t buffer_id, uint16_t in_port, uint8_t reason, return buf; } -struct ofpbuf * -make_packet_out(const struct ofpbuf *packet, uint32_t buffer_id, - uint16_t in_port, - const struct ofp_action_header *actions, size_t n_actions) -{ - size_t actions_len = n_actions * sizeof *actions; - struct ofp_packet_out *opo; - size_t size = sizeof *opo + actions_len + (packet ? packet->size : 0); - struct ofpbuf *out = ofpbuf_new(size); - - opo = ofpbuf_put_uninit(out, sizeof *opo); - opo->header.version = OFP_VERSION; - opo->header.type = OFPT_PACKET_OUT; - opo->header.length = htons(size); - opo->header.xid = htonl(0); - opo->buffer_id = htonl(buffer_id); - opo->in_port = htons(in_port); - opo->actions_len = htons(actions_len); - ofpbuf_put(out, actions, actions_len); - if (packet) { - ofpbuf_put(out, packet->data, packet->size); - } - return out; -} - -struct ofpbuf * -make_unbuffered_packet_out(const struct ofpbuf *packet, - uint16_t in_port, uint16_t out_port) -{ - struct ofp_action_output action; - action.type = htons(OFPAT_OUTPUT); - action.len = htons(sizeof action); - action.port = htons(out_port); - return make_packet_out(packet, UINT32_MAX, in_port, - (struct ofp_action_header *) &action, 1); -} - -struct ofpbuf * -make_buffered_packet_out(uint32_t buffer_id, - uint16_t in_port, uint16_t out_port) -{ - if (out_port != OFPP_NONE) { - struct ofp_action_output action; - action.type = htons(OFPAT_OUTPUT); - action.len = htons(sizeof action); - action.port = htons(out_port); - return make_packet_out(NULL, buffer_id, in_port, - (struct ofp_action_header *) &action, 1); - } else { - return make_packet_out(NULL, buffer_id, in_port, NULL, 0); - } -} - /* Creates and returns an OFPT_ECHO_REQUEST message with an empty payload. */ struct ofpbuf * make_echo_request(void) @@ -2193,6 +2252,15 @@ make_echo_reply(const struct ofp_header *rq) return out; } +struct ofpbuf * +ofputil_encode_barrier_request(void) +{ + struct ofpbuf *msg; + + make_openflow(sizeof(struct ofp_header), OFPT_BARRIER_REQUEST, &msg); + return msg; +} + const char * ofputil_frag_handling_to_string(enum ofp_config_flags flags) { @@ -2225,8 +2293,8 @@ ofputil_frag_handling_from_string(const char *s, enum ofp_config_flags *flags) /* Checks that 'port' is a valid output port for the OFPAT_OUTPUT action, given * that the switch will never have more than 'max_ports' ports. Returns 0 if - * 'port' is valid, otherwise an ofp_mkerr() return code. */ -int + * 'port' is valid, otherwise an OpenFlow return code. */ +enum ofperr ofputil_check_output_port(uint16_t port, int max_ports) { switch (port) { @@ -2243,7 +2311,7 @@ ofputil_check_output_port(uint16_t port, int max_ports) if (port < max_ports) { return 0; } - return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_OUT_PORT); + return OFPERR_OFPBAC_BAD_OUT_PORT; } } @@ -2309,32 +2377,33 @@ ofputil_format_port(uint16_t port, struct ds *s) ds_put_cstr(s, name); } -static int +static enum ofperr check_resubmit_table(const struct nx_action_resubmit *nar) { if (nar->pad[0] || nar->pad[1] || nar->pad[2]) { - return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT); + return OFPERR_OFPBAC_BAD_ARGUMENT; } return 0; } -static int +static enum ofperr check_output_reg(const struct nx_action_output_reg *naor, const struct flow *flow) { + struct mf_subfield src; size_t i; for (i = 0; i < sizeof naor->zero; i++) { if (naor->zero[i]) { - return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT); + return OFPERR_OFPBAC_BAD_ARGUMENT; } } - return nxm_src_check(naor->src, nxm_decode_ofs(naor->ofs_nbits), - nxm_decode_n_bits(naor->ofs_nbits), flow); + nxm_decode(&src, naor->src, naor->ofs_nbits); + return mf_check_src(&src, flow); } -int +enum ofperr validate_actions(const union ofp_action *actions, size_t n_actions, const struct flow *flow, int max_ports) { @@ -2342,20 +2411,16 @@ validate_actions(const union ofp_action *actions, size_t n_actions, size_t left; OFPUTIL_ACTION_FOR_EACH (a, left, actions, n_actions) { + enum ofperr error; uint16_t port; - int error; int code; code = ofputil_decode_action(a); if (code < 0) { - char *msg; - error = -code; - msg = ofputil_error_to_string(error); VLOG_WARN_RL(&bad_ofmsg_rl, "action decoding error at offset %td (%s)", - (a - actions) * sizeof *a, msg); - free(msg); + (a - actions) * sizeof *a, ofperr_get_name(error)); return error; } @@ -2369,13 +2434,13 @@ validate_actions(const union ofp_action *actions, size_t n_actions, case OFPUTIL_OFPAT_SET_VLAN_VID: if (a->vlan_vid.vlan_vid & ~htons(0xfff)) { - error = ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT); + error = OFPERR_OFPBAC_BAD_ARGUMENT; } break; case OFPUTIL_OFPAT_SET_VLAN_PCP: if (a->vlan_pcp.vlan_pcp & ~7) { - error = ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT); + error = OFPERR_OFPBAC_BAD_ARGUMENT; } break; @@ -2383,7 +2448,7 @@ validate_actions(const union ofp_action *actions, size_t n_actions, port = ntohs(((const struct ofp_action_enqueue *) a)->port); if (port >= max_ports && port != OFPP_IN_PORT && port != OFPP_LOCAL) { - error = ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_OUT_PORT); + error = OFPERR_OFPBAC_BAD_OUT_PORT; } break; @@ -2442,21 +2507,21 @@ validate_actions(const union ofp_action *actions, size_t n_actions, case OFPUTIL_NXAST_NOTE: case OFPUTIL_NXAST_SET_TUNNEL64: case OFPUTIL_NXAST_EXIT: + case OFPUTIL_NXAST_DEC_TTL: + case OFPUTIL_NXAST_FIN_TIMEOUT: break; } if (error) { - char *msg = ofputil_error_to_string(error); VLOG_WARN_RL(&bad_ofmsg_rl, "bad action at offset %td (%s)", - (a - actions) * sizeof *a, msg); - free(msg); + (a - actions) * sizeof *a, ofperr_get_name(error)); return error; } } if (left) { VLOG_WARN_RL(&bad_ofmsg_rl, "bad action format at offset %zu", (n_actions - left) * sizeof *a); - return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN); + return OFPERR_OFPBAC_BAD_LEN; } return 0; } @@ -2468,11 +2533,11 @@ struct ofputil_action { }; static const struct ofputil_action action_bad_type - = { -OFP_MKERR(OFPET_BAD_ACTION, OFPBAC_BAD_TYPE), 0, UINT_MAX }; + = { -OFPERR_OFPBAC_BAD_TYPE, 0, UINT_MAX }; static const struct ofputil_action action_bad_len - = { -OFP_MKERR(OFPET_BAD_ACTION, OFPBAC_BAD_LEN), 0, UINT_MAX }; + = { -OFPERR_OFPBAC_BAD_LEN, 0, UINT_MAX }; static const struct ofputil_action action_bad_vendor - = { -OFP_MKERR(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR), 0, UINT_MAX }; + = { -OFPERR_OFPBAC_BAD_VENDOR, 0, UINT_MAX }; static const struct ofputil_action * ofputil_decode_ofpat_action(const union ofp_action *a) @@ -2523,8 +2588,8 @@ ofputil_decode_nxast_action(const union ofp_action *a) } /* Parses 'a' to determine its type. Returns a nonnegative OFPUTIL_OFPAT_* or - * OFPUTIL_NXAST_* constant if successful, otherwise a negative OpenFlow error - * code (as returned by ofp_mkerr()). + * OFPUTIL_NXAST_* constant if successful, otherwise a negative OFPERR_* error + * code. * * The caller must have already verified that 'a''s length is correct (that is, * a->header.len is nonzero and a multiple of sizeof(union ofp_action) and no @@ -2544,7 +2609,7 @@ ofputil_decode_action(const union ofp_action *a) switch (ntohl(a->vendor.vendor)) { case NX_VENDOR_ID: if (len < sizeof(struct nx_action_header)) { - return -ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN); + return -OFPERR_OFPBAC_BAD_LEN; } action = ofputil_decode_nxast_action(a); break; @@ -2556,7 +2621,7 @@ ofputil_decode_action(const union ofp_action *a) return (len >= action->min_len && len <= action->max_len ? action->code - : -ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN)); + : -OFPERR_OFPBAC_BAD_LEN); } /* Parses 'a' and returns its type as an OFPUTIL_OFPAT_* or OFPUTIL_NXAST_* @@ -2740,7 +2805,7 @@ ofputil_normalize_rule(struct cls_rule *rule, enum nx_flow_format flow_format) wc.nw_src_mask = wc.nw_dst_mask = htonl(0); } if (!(may_match & MAY_TP_ADDR)) { - wc.wildcards |= FWW_TP_SRC | FWW_TP_DST; + wc.tp_src_mask = wc.tp_dst_mask = htons(0); } if (!(may_match & MAY_NW_PROTO)) { wc.wildcards |= FWW_NW_PROTO; @@ -2783,207 +2848,6 @@ ofputil_normalize_rule(struct cls_rule *rule, enum nx_flow_format flow_format) } } -static uint32_t -vendor_code_to_id(uint8_t code) -{ - switch (code) { -#define OFPUTIL_VENDOR(NAME, VENDOR_ID) case NAME: return VENDOR_ID; - OFPUTIL_VENDORS -#undef OFPUTIL_VENDOR - default: - return UINT32_MAX; - } -} - -static int -vendor_id_to_code(uint32_t id) -{ - switch (id) { -#define OFPUTIL_VENDOR(NAME, VENDOR_ID) case VENDOR_ID: return NAME; - OFPUTIL_VENDORS -#undef OFPUTIL_VENDOR - default: - return -1; - } -} - -/* Creates and returns an OpenFlow message of type OFPT_ERROR with the error - * information taken from 'error', whose encoding must be as described in the - * large comment in ofp-util.h. If 'oh' is nonnull, then the error will use - * oh->xid as its transaction ID, and it will include up to the first 64 bytes - * of 'oh'. - * - * Returns NULL if 'error' is not an OpenFlow error code. */ -struct ofpbuf * -ofputil_encode_error_msg(int error, const struct ofp_header *oh) -{ - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); - - struct ofpbuf *buf; - const void *data; - size_t len; - uint8_t vendor; - uint16_t type; - uint16_t code; - ovs_be32 xid; - - if (!is_ofp_error(error)) { - /* We format 'error' with strerror() here since it seems likely to be - * a system errno value. */ - VLOG_WARN_RL(&rl, "invalid OpenFlow error code %d (%s)", - error, strerror(error)); - return NULL; - } - - if (oh) { - xid = oh->xid; - data = oh; - len = ntohs(oh->length); - if (len > 64) { - len = 64; - } - } else { - xid = 0; - data = NULL; - len = 0; - } - - vendor = get_ofp_err_vendor(error); - type = get_ofp_err_type(error); - code = get_ofp_err_code(error); - if (vendor == OFPUTIL_VENDOR_OPENFLOW) { - struct ofp_error_msg *oem; - - oem = make_openflow_xid(len + sizeof *oem, OFPT_ERROR, xid, &buf); - oem->type = htons(type); - oem->code = htons(code); - } else { - struct ofp_error_msg *oem; - struct nx_vendor_error *nve; - uint32_t vendor_id; - - vendor_id = vendor_code_to_id(vendor); - if (vendor_id == UINT32_MAX) { - VLOG_WARN_RL(&rl, "error %x contains invalid vendor code %d", - error, vendor); - return NULL; - } - - oem = make_openflow_xid(len + sizeof *oem + sizeof *nve, - OFPT_ERROR, xid, &buf); - oem->type = htons(NXET_VENDOR); - oem->code = htons(NXVC_VENDOR_ERROR); - - nve = (struct nx_vendor_error *)oem->data; - nve->vendor = htonl(vendor_id); - nve->type = htons(type); - nve->code = htons(code); - } - - if (len) { - buf->size -= len; - ofpbuf_put(buf, data, len); - } - - return buf; -} - -/* Decodes 'oh', which should be an OpenFlow OFPT_ERROR message, and returns an - * Open vSwitch internal error code in the format described in the large - * comment in ofp-util.h. - * - * If 'payload_ofs' is nonnull, on success '*payload_ofs' is set to the offset - * to the payload starting from 'oh' and on failure it is set to 0. */ -int -ofputil_decode_error_msg(const struct ofp_header *oh, size_t *payload_ofs) -{ - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); - - const struct ofp_error_msg *oem; - uint16_t type, code; - struct ofpbuf b; - int vendor; - - if (payload_ofs) { - *payload_ofs = 0; - } - if (oh->type != OFPT_ERROR) { - return EPROTO; - } - - ofpbuf_use_const(&b, oh, ntohs(oh->length)); - oem = ofpbuf_try_pull(&b, sizeof *oem); - if (!oem) { - return EPROTO; - } - - 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 EPROTO; - } - - vendor = vendor_id_to_code(ntohl(nve->vendor)); - if (vendor < 0) { - VLOG_WARN_RL(&rl, "error contains unknown vendor ID %#"PRIx32, - ntohl(nve->vendor)); - return EPROTO; - } - type = ntohs(nve->type); - code = ntohs(nve->code); - } else { - vendor = OFPUTIL_VENDOR_OPENFLOW; - } - - if (type >= 1024) { - VLOG_WARN_RL(&rl, "error contains type %"PRIu16" greater than " - "supported maximum value 1023", type); - return EPROTO; - } - - if (payload_ofs) { - *payload_ofs = (uint8_t *) b.data - (uint8_t *) oh; - } - return ofp_mkerr_vendor(vendor, type, code); -} - -void -ofputil_format_error(struct ds *s, int error) -{ - if (is_errno(error)) { - ds_put_cstr(s, strerror(error)); - } else { - uint16_t type = get_ofp_err_type(error); - uint16_t code = get_ofp_err_code(error); - const char *type_s = ofp_error_type_to_string(type); - const char *code_s = ofp_error_code_to_string(type, code); - - ds_put_format(s, "type "); - if (type_s) { - ds_put_cstr(s, type_s); - } else { - ds_put_format(s, "%"PRIu16, type); - } - - ds_put_cstr(s, ", code "); - if (code_s) { - ds_put_cstr(s, code_s); - } else { - ds_put_format(s, "%"PRIu16, code); - } - } -} - -char * -ofputil_error_to_string(int error) -{ - struct ds s = DS_EMPTY_INITIALIZER; - ofputil_format_error(&s, error); - return ds_steal_cstr(&s); -} - /* Attempts to pull 'actions_len' bytes from the front of 'b'. Returns 0 if * successful, otherwise an OpenFlow error. * @@ -2995,7 +2859,7 @@ ofputil_error_to_string(int error) * 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 +enum ofperr ofputil_pull_actions(struct ofpbuf *b, unsigned int actions_len, union ofp_action **actionsp, size_t *n_actionsp) { @@ -3019,7 +2883,7 @@ ofputil_pull_actions(struct ofpbuf *b, unsigned int actions_len, error: *actionsp = NULL; *n_actionsp = 0; - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); + return OFPERR_OFPBRC_BAD_LEN; } bool