X-Git-Url: http://git.cascardo.eti.br/?a=blobdiff_plain;f=lib%2Fofp-util.c;h=de7cf3cea308801d38b8987863c64293865574ce;hb=600766e877efa2713b9c87d127f7190d8ab48da9;hp=1e3d5b2c4226d0f10e0446a41a5e43c257df9a9b;hpb=5876b4dbf6887a9c6c419c130a8ef9996ac52012;p=cascardo%2Fovs.git diff --git a/lib/ofp-util.c b/lib/ofp-util.c index 1e3d5b2c4..de7cf3cea 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -41,6 +41,7 @@ #include "ofpbuf.h" #include "openflow/netronome-ext.h" #include "packets.h" +#include "pktbuf.h" #include "random.h" #include "tun-metadata.h" #include "unaligned.h" @@ -1124,8 +1125,9 @@ bool ofputil_packet_in_format_is_valid(enum nx_packet_in_format packet_in_format) { switch (packet_in_format) { - case NXPIF_OPENFLOW10: - case NXPIF_NXM: + case NXPIF_STANDARD: + case NXPIF_NXT_PACKET_IN: + case NXPIF_NXT_PACKET_IN2: return true; } @@ -1136,10 +1138,12 @@ const char * ofputil_packet_in_format_to_string(enum nx_packet_in_format packet_in_format) { switch (packet_in_format) { - case NXPIF_OPENFLOW10: - return "openflow10"; - case NXPIF_NXM: - return "nxm"; + case NXPIF_STANDARD: + return "standard"; + case NXPIF_NXT_PACKET_IN: + return "nxt_packet_in"; + case NXPIF_NXT_PACKET_IN2: + return "nxt_packet_in2"; default: OVS_NOT_REACHED(); } @@ -1148,8 +1152,12 @@ ofputil_packet_in_format_to_string(enum nx_packet_in_format packet_in_format) int ofputil_packet_in_format_from_string(const char *s) { - return (!strcmp(s, "openflow10") ? NXPIF_OPENFLOW10 - : !strcmp(s, "nxm") ? NXPIF_NXM + return (!strcmp(s, "standard") || !strcmp(s, "openflow10") + ? NXPIF_STANDARD + : !strcmp(s, "nxt_packet_in") || !strcmp(s, "nxm") + ? NXPIF_NXT_PACKET_IN + : !strcmp(s, "nxt_packet_in2") + ? NXPIF_NXT_PACKET_IN2 : -1); } @@ -1243,13 +1251,12 @@ version_bitmap_from_version(uint8_t ofp_version) bool ofputil_decode_hello(const struct ofp_header *oh, uint32_t *allowed_versions) { - struct ofpbuf msg; - bool ok = true; - - ofpbuf_use_const(&msg, oh, ntohs(oh->length)); + struct ofpbuf msg = ofpbuf_const_initializer(oh, ntohs(oh->length)); ofpbuf_pull(&msg, sizeof *oh); *allowed_versions = version_bitmap_from_version(oh->version); + + bool ok = true; while (msg.size) { const struct ofp_hello_elem_header *oheh; unsigned int len; @@ -1544,14 +1551,12 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, { ovs_be16 raw_flags; enum ofperr error; - struct ofpbuf b; - enum ofpraw raw; /* Ignored for non-delete actions */ fm->delete_reason = OFPRR_DELETE; - ofpbuf_use_const(&b, oh, ntohs(oh->length)); - raw = ofpraw_pull_assert(&b); + struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length)); + enum ofpraw raw = ofpraw_pull_assert(&b); if (raw == OFPRAW_OFPT11_FLOW_MOD) { /* Standard OpenFlow 1.1+ flow_mod. */ const struct ofp11_flow_mod *ofm; @@ -1772,12 +1777,9 @@ ofputil_decode_meter_mod(const struct ofp_header *oh, struct ofputil_meter_mod *mm, struct ofpbuf *bands) { - const struct ofp13_meter_mod *omm; - struct ofpbuf b; - - ofpbuf_use_const(&b, oh, ntohs(oh->length)); + struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length)); ofpraw_pull_assert(&b); - omm = ofpbuf_pull(&b, sizeof *omm); + const struct ofp13_meter_mod *omm = ofpbuf_pull(&b, sizeof *omm); /* Translate the message. */ mm->command = ntohs(omm->command); @@ -2341,11 +2343,8 @@ ofputil_decode_queue_get_config_request(const struct ofp_header *oh, const struct ofp10_queue_get_config_request *qgcr10; const struct ofp11_queue_get_config_request *qgcr11; const struct ofp14_queue_desc_request *qdr14; - enum ofpraw raw; - struct ofpbuf b; - - ofpbuf_use_const(&b, oh, ntohs(oh->length)); - raw = ofpraw_pull_assert(&b); + struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length)); + enum ofpraw raw = ofpraw_pull_assert(&b); switch ((int) raw) { case OFPRAW_OFPT10_QUEUE_GET_CONFIG_REQUEST: @@ -2613,8 +2612,8 @@ ofputil_pull_queue_get_config_reply14(struct ofpbuf *msg, } len -= sizeof *oqd14; - struct ofpbuf properties; - ofpbuf_use_const(&properties, ofpbuf_pull(msg, len), len); + struct ofpbuf properties = ofpbuf_const_initializer(ofpbuf_pull(msg, len), + len); while (properties.size > 0) { struct ofpbuf payload; uint64_t type; @@ -2694,11 +2693,8 @@ enum ofperr ofputil_decode_flow_stats_request(struct ofputil_flow_stats_request *fsr, const struct ofp_header *oh) { - enum ofpraw raw; - struct ofpbuf b; - - ofpbuf_use_const(&b, oh, ntohs(oh->length)); - raw = ofpraw_pull_assert(&b); + struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length)); + enum ofpraw raw = ofpraw_pull_assert(&b); switch ((int) raw) { case OFPRAW_OFPST10_FLOW_REQUEST: return ofputil_decode_ofpst10_flow_request(fsr, b.data, false); @@ -3126,13 +3122,10 @@ enum ofperr ofputil_decode_aggregate_stats_reply(struct ofputil_aggregate_stats *stats, const struct ofp_header *reply) { - struct ofp_aggregate_stats_reply *asr; - struct ofpbuf msg; - - ofpbuf_use_const(&msg, reply, ntohs(reply->length)); + struct ofpbuf msg = ofpbuf_const_initializer(reply, ntohs(reply->length)); ofpraw_pull_assert(&msg); - asr = msg.msg; + struct ofp_aggregate_stats_reply *asr = msg.msg; stats->packet_count = ntohll(get_32aligned_be64(&asr->packet_count)); stats->byte_count = ntohll(get_32aligned_be64(&asr->byte_count)); stats->flow_count = ntohl(asr->flow_count); @@ -3147,11 +3140,8 @@ enum ofperr ofputil_decode_flow_removed(struct ofputil_flow_removed *fr, const struct ofp_header *oh) { - enum ofpraw raw; - struct ofpbuf b; - - ofpbuf_use_const(&b, oh, ntohs(oh->length)); - raw = ofpraw_pull_assert(&b); + struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length)); + enum ofpraw raw = ofpraw_pull_assert(&b); if (raw == OFPRAW_OFPT11_FLOW_REMOVED) { const struct ofp12_flow_removed *ofr; enum ofperr error; @@ -3311,31 +3301,142 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr, return msg; } +/* The caller has done basic initialization of '*pin'; the other output + * arguments needs to be initialized. */ +static enum ofperr +decode_nx_packet_in2(const struct ofp_header *oh, bool loose, + struct ofputil_packet_in *pin, + size_t *total_len, uint32_t *buffer_id, + struct ofpbuf *continuation) +{ + *total_len = 0; + *buffer_id = UINT32_MAX; + + struct ofpbuf properties; + ofpbuf_use_const(&properties, oh, ntohs(oh->length)); + ofpraw_pull_assert(&properties); + + while (properties.size > 0) { + struct ofpbuf payload; + uint64_t type; + + enum ofperr error = ofpprop_pull(&properties, &payload, &type); + if (error) { + return error; + } + + switch (type) { + case NXPINT_PACKET: + pin->packet = payload.msg; + pin->packet_len = ofpbuf_msgsize(&payload); + break; + + case NXPINT_FULL_LEN: { + uint32_t u32; + error = ofpprop_parse_u32(&payload, &u32); + *total_len = u32; + break; + } + + case NXPINT_BUFFER_ID: + error = ofpprop_parse_u32(&payload, buffer_id); + break; + + case NXPINT_TABLE_ID: + error = ofpprop_parse_u8(&payload, &pin->table_id); + break; + + case NXPINT_COOKIE: + error = ofpprop_parse_be64(&payload, &pin->cookie); + break; + + case NXPINT_REASON: { + uint8_t reason; + error = ofpprop_parse_u8(&payload, &reason); + pin->reason = reason; + break; + } + + case NXPINT_METADATA: + error = oxm_decode_match(payload.msg, ofpbuf_msgsize(&payload), + &pin->flow_metadata); + break; + + case NXPINT_USERDATA: + pin->userdata = payload.msg; + pin->userdata_len = ofpbuf_msgsize(&payload); + break; + + case NXPINT_CONTINUATION: + if (continuation) { + error = ofpprop_parse_nested(&payload, continuation); + } + break; + + default: + error = OFPPROP_UNKNOWN(loose, "NX_PACKET_IN2", type); + break; + } + if (error) { + return error; + } + } + + if (!pin->packet_len) { + VLOG_WARN_RL(&bad_ofmsg_rl, "NXT_PACKET_IN2 lacks packet"); + return OFPERR_OFPBRC_BAD_LEN; + } else if (!*total_len) { + *total_len = pin->packet_len; + } else if (*total_len < pin->packet_len) { + VLOG_WARN_RL(&bad_ofmsg_rl, "NXT_PACKET_IN2 claimed full_len < len"); + return OFPERR_OFPBRC_BAD_LEN; + } + + return 0; +} + +/* Decodes the packet-in message starting at 'oh' into '*pin'. Populates + * 'pin->packet' and 'pin->packet_len' with the part of the packet actually + * included in the message. If 'total_lenp' is nonnull, populates + * '*total_lenp' with the original length of the packet (which is larger than + * 'packet->len' if only part of the packet was included). If 'buffer_idp' is + * nonnull, stores the packet's buffer ID in '*buffer_idp' (UINT32_MAX if it + * was not buffered). + * + * Populates 'continuation', if nonnull, with the continuation data from the + * packet-in (an empty buffer, if 'oh' did not contain continuation data). The + * format of this data is supposed to be opaque to anything other than + * ovs-vswitchd, so that in any other process the only reasonable use of this + * data is to be copied into an NXT_RESUME message via ofputil_encode_resume(). + * + * This function points 'pin->packet' into 'oh', so the caller should not free + * it separately from the original OpenFlow message. This is also true for + * 'pin->userdata' (which could also end up NULL if there is no userdata). + * + * Returns 0 if successful, otherwise an OpenFlow error code. */ enum ofperr -ofputil_decode_packet_in(struct ofputil_packet_in *pin, - const struct ofp_header *oh) +ofputil_decode_packet_in(const struct ofp_header *oh, bool loose, + struct ofputil_packet_in *pin, + size_t *total_lenp, uint32_t *buffer_idp, + struct ofpbuf *continuation) { - enum ofpraw raw; - struct ofpbuf b; + uint32_t buffer_id; + size_t total_len; memset(pin, 0, sizeof *pin); pin->cookie = OVS_BE64_MAX; + if (continuation) { + ofpbuf_use_const(continuation, NULL, 0); + } - ofpbuf_use_const(&b, oh, ntohs(oh->length)); - raw = ofpraw_pull_assert(&b); + struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length)); + enum ofpraw raw = ofpraw_pull_assert(&b); if (raw == OFPRAW_OFPT13_PACKET_IN || raw == OFPRAW_OFPT12_PACKET_IN) { - const struct ofp13_packet_in *opi; - int error; - size_t packet_in_size; - - if (raw == OFPRAW_OFPT12_PACKET_IN) { - packet_in_size = sizeof (struct ofp12_packet_in); - } else { - packet_in_size = sizeof (struct ofp13_packet_in); - } - - opi = ofpbuf_pull(&b, packet_in_size); - error = oxm_pull_match_loose(&b, &pin->flow_metadata); + const struct ofp12_packet_in *opi = ofpbuf_pull(&b, sizeof *opi); + const ovs_be64 *cookie = (raw == OFPRAW_OFPT13_PACKET_IN + ? ofpbuf_pull(&b, sizeof *cookie) + : NULL); + enum ofperr error = oxm_pull_match_loose(&b, &pin->flow_metadata); if (error) { return error; } @@ -3344,13 +3445,12 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin, return OFPERR_OFPBRC_BAD_LEN; } - pin->reason = opi->pi.reason; - pin->table_id = opi->pi.table_id; - pin->buffer_id = ntohl(opi->pi.buffer_id); - pin->total_len = ntohs(opi->pi.total_len); - - if (raw == OFPRAW_OFPT13_PACKET_IN) { - pin->cookie = opi->cookie; + pin->reason = opi->reason; + pin->table_id = opi->table_id; + buffer_id = ntohl(opi->buffer_id); + total_len = ntohs(opi->total_len); + if (cookie) { + pin->cookie = *cookie; } pin->packet = b.data; @@ -3360,14 +3460,15 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin, opi = ofpbuf_pull(&b, offsetof(struct ofp10_packet_in, data)); - pin->packet = opi->data; + pin->packet = CONST_CAST(uint8_t *, opi->data); pin->packet_len = b.size; match_init_catchall(&pin->flow_metadata); - match_set_in_port(&pin->flow_metadata, u16_to_ofp(ntohs(opi->in_port))); + match_set_in_port(&pin->flow_metadata, + u16_to_ofp(ntohs(opi->in_port))); pin->reason = opi->reason; - pin->buffer_id = ntohl(opi->buffer_id); - pin->total_len = ntohs(opi->total_len); + buffer_id = ntohl(opi->buffer_id); + total_len = ntohs(opi->total_len); } else if (raw == OFPRAW_OFPT11_PACKET_IN) { const struct ofp11_packet_in *opi; ofp_port_t in_port; @@ -3378,14 +3479,14 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin, pin->packet = b.data; pin->packet_len = b.size; - pin->buffer_id = ntohl(opi->buffer_id); + buffer_id = ntohl(opi->buffer_id); error = ofputil_port_from_ofp11(opi->in_port, &in_port); if (error) { return error; } match_init_catchall(&pin->flow_metadata); match_set_in_port(&pin->flow_metadata, in_port); - pin->total_len = ntohs(opi->total_len); + total_len = ntohs(opi->total_len); pin->reason = opi->reason; pin->table_id = opi->table_id; } else if (raw == OFPRAW_NXT_PACKET_IN) { @@ -3407,161 +3508,411 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin, pin->table_id = npi->table_id; pin->cookie = npi->cookie; - pin->buffer_id = ntohl(npi->buffer_id); - pin->total_len = ntohs(npi->total_len); + buffer_id = ntohl(npi->buffer_id); + total_len = ntohs(npi->total_len); pin->packet = b.data; pin->packet_len = b.size; + } else if (raw == OFPRAW_NXT_PACKET_IN2 || raw == OFPRAW_NXT_RESUME) { + enum ofperr error = decode_nx_packet_in2(oh, loose, pin, &total_len, + &buffer_id, continuation); + if (error) { + return error; + } } else { OVS_NOT_REACHED(); } + if (total_lenp) { + *total_lenp = total_len; + } + if (buffer_idp) { + *buffer_idp = buffer_id; + } + return 0; } +static int +encode_packet_in_reason(enum ofp_packet_in_reason reason, + enum ofp_version version) +{ + switch (reason) { + case OFPR_NO_MATCH: + case OFPR_ACTION: + case OFPR_INVALID_TTL: + return reason; + + case OFPR_ACTION_SET: + case OFPR_GROUP: + case OFPR_PACKET_OUT: + return version < OFP14_VERSION ? OFPR_ACTION : reason; + + case OFPR_EXPLICIT_MISS: + return version < OFP13_VERSION ? OFPR_ACTION : OFPR_NO_MATCH; + + case OFPR_IMPLICIT_MISS: + return OFPR_NO_MATCH; + + case OFPR_N_REASONS: + default: + OVS_NOT_REACHED(); + } +} + +/* Only NXT_PACKET_IN2 (not NXT_RESUME) should include NXCPT_USERDATA, so this + * function omits it. The caller can add it itself if desired. */ +static void +ofputil_put_packet_in(const struct ofputil_packet_in *pin, + enum ofp_version version, uint32_t buffer_id, + size_t include_bytes, struct ofpbuf *msg) +{ + /* Add packet properties. */ + ofpprop_put(msg, NXPINT_PACKET, pin->packet, include_bytes); + if (include_bytes != pin->packet_len) { + ofpprop_put_u32(msg, NXPINT_FULL_LEN, pin->packet_len); + } + if (buffer_id != UINT32_MAX) { + ofpprop_put_u32(msg, NXPINT_BUFFER_ID, buffer_id); + } + + /* Add flow properties. */ + ofpprop_put_u8(msg, NXPINT_TABLE_ID, pin->table_id); + if (pin->cookie != OVS_BE64_MAX) { + ofpprop_put_be64(msg, NXPINT_COOKIE, pin->cookie); + } + + /* Add other properties. */ + ofpprop_put_u8(msg, NXPINT_REASON, + encode_packet_in_reason(pin->reason, version)); + + size_t start = ofpprop_start(msg, NXPINT_METADATA); + oxm_put_raw(msg, &pin->flow_metadata, version); + ofpprop_end(msg, start); +} + +static void +put_actions_property(struct ofpbuf *msg, uint64_t prop_type, + enum ofp_version version, + const struct ofpact *actions, size_t actions_len) +{ + if (actions_len) { + size_t start = ofpprop_start_nested(msg, prop_type); + ofpacts_put_openflow_actions(actions, actions_len, msg, version); + ofpprop_end(msg, start); + } +} + +enum nx_continuation_prop_type { + NXCPT_BRIDGE = 0x8000, + NXCPT_STACK, + NXCPT_MIRRORS, + NXCPT_CONNTRACKED, + NXCPT_TABLE_ID, + NXCPT_COOKIE, + NXCPT_ACTIONS, + NXCPT_ACTION_SET, +}; + +/* Only NXT_PACKET_IN2 (not NXT_RESUME) should include NXCPT_USERDATA, so this + * function omits it. The caller can add it itself if desired. */ +static void +ofputil_put_packet_in_private(const struct ofputil_packet_in_private *pin, + enum ofp_version version, uint32_t buffer_id, + size_t include_bytes, struct ofpbuf *msg) +{ + ofputil_put_packet_in(&pin->public, version, buffer_id, + include_bytes, msg); + + size_t continuation_ofs = ofpprop_start_nested(msg, NXPINT_CONTINUATION); + size_t inner_ofs = msg->size; + + if (!uuid_is_zero(&pin->bridge)) { + ofpprop_put_uuid(msg, NXCPT_BRIDGE, &pin->bridge); + } + + for (size_t i = 0; i < pin->n_stack; i++) { + const union mf_subvalue *s = &pin->stack[i]; + size_t ofs; + for (ofs = 0; ofs < sizeof *s; ofs++) { + if (s->u8[ofs]) { + break; + } + } + + ofpprop_put(msg, NXCPT_STACK, &s->u8[ofs], sizeof *s - ofs); + } + + if (pin->mirrors) { + ofpprop_put_u32(msg, NXCPT_MIRRORS, pin->mirrors); + } + + if (pin->conntracked) { + ofpprop_put_flag(msg, NXCPT_CONNTRACKED); + } + + if (pin->actions_len) { + /* Divide 'pin->actions' into groups that begins with an + * unroll_xlate action. For each group, emit a NXCPT_TABLE_ID and + * NXCPT_COOKIE property (if either has changed; each is initially + * assumed 0), then a NXCPT_ACTIONS property with the grouped + * actions. + * + * The alternative is to make OFPACT_UNROLL_XLATE public. We can + * always do that later, since this is a private property. */ + const struct ofpact *const end = ofpact_end(pin->actions, + pin->actions_len); + const struct ofpact_unroll_xlate *unroll = NULL; + uint8_t table_id = 0; + ovs_be64 cookie = 0; + + const struct ofpact *a; + for (a = pin->actions; ; a = ofpact_next(a)) { + if (a == end || a->type == OFPACT_UNROLL_XLATE) { + if (unroll) { + if (table_id != unroll->rule_table_id) { + ofpprop_put_u8(msg, NXCPT_TABLE_ID, + unroll->rule_table_id); + table_id = unroll->rule_table_id; + } + if (cookie != unroll->rule_cookie) { + ofpprop_put_be64(msg, NXCPT_COOKIE, + unroll->rule_cookie); + cookie = unroll->rule_cookie; + } + } + + const struct ofpact *start + = unroll ? ofpact_next(&unroll->ofpact) : pin->actions; + put_actions_property(msg, NXCPT_ACTIONS, version, + start, (a - start) * sizeof *a); + + if (a == end) { + break; + } + unroll = ofpact_get_UNROLL_XLATE(a); + } + } + } + + if (pin->action_set_len) { + size_t start = ofpprop_start_nested(msg, NXCPT_ACTION_SET); + ofpacts_put_openflow_actions(pin->action_set, + pin->action_set_len, msg, version); + ofpprop_end(msg, start); + } + + if (msg->size > inner_ofs) { + ofpprop_end(msg, continuation_ofs); + } else { + msg->size = continuation_ofs; + } +} + static struct ofpbuf * -ofputil_encode_ofp10_packet_in(const struct ofputil_packet_in *pin) +ofputil_encode_ofp10_packet_in(const struct ofputil_packet_in *pin, + uint32_t buffer_id) { struct ofp10_packet_in *opi; - struct ofpbuf *packet; + struct ofpbuf *msg; - packet = ofpraw_alloc_xid(OFPRAW_OFPT10_PACKET_IN, OFP10_VERSION, - htonl(0), pin->packet_len); - opi = ofpbuf_put_zeros(packet, offsetof(struct ofp10_packet_in, data)); - opi->total_len = htons(pin->total_len); + msg = ofpraw_alloc_xid(OFPRAW_OFPT10_PACKET_IN, OFP10_VERSION, + htonl(0), pin->packet_len); + opi = ofpbuf_put_zeros(msg, offsetof(struct ofp10_packet_in, data)); + opi->total_len = htons(pin->packet_len); opi->in_port = htons(ofp_to_u16(pin->flow_metadata.flow.in_port.ofp_port)); - opi->reason = pin->reason; - opi->buffer_id = htonl(pin->buffer_id); + opi->reason = encode_packet_in_reason(pin->reason, OFP10_VERSION); + opi->buffer_id = htonl(buffer_id); - ofpbuf_put(packet, pin->packet, pin->packet_len); - - return packet; + return msg; } static struct ofpbuf * -ofputil_encode_nx_packet_in(const struct ofputil_packet_in *pin) +ofputil_encode_nx_packet_in(const struct ofputil_packet_in *pin, + enum ofp_version version, uint32_t buffer_id) { struct nx_packet_in *npi; - struct ofpbuf *packet; + struct ofpbuf *msg; size_t match_len; /* The final argument is just an estimate of the space required. */ - packet = ofpraw_alloc_xid(OFPRAW_NXT_PACKET_IN, OFP10_VERSION, - htonl(0), NXM_TYPICAL_LEN + 2 + pin->packet_len); - ofpbuf_put_zeros(packet, sizeof *npi); - match_len = nx_put_match(packet, &pin->flow_metadata, 0, 0); - ofpbuf_put_zeros(packet, 2); - ofpbuf_put(packet, pin->packet, pin->packet_len); - - npi = packet->msg; - npi->buffer_id = htonl(pin->buffer_id); - npi->total_len = htons(pin->total_len); - npi->reason = pin->reason; + msg = ofpraw_alloc_xid(OFPRAW_NXT_PACKET_IN, version, + htonl(0), NXM_TYPICAL_LEN + 2 + pin->packet_len); + ofpbuf_put_zeros(msg, sizeof *npi); + match_len = nx_put_match(msg, &pin->flow_metadata, 0, 0); + ofpbuf_put_zeros(msg, 2); + + npi = msg->msg; + npi->buffer_id = htonl(buffer_id); + npi->total_len = htons(pin->packet_len); + npi->reason = encode_packet_in_reason(pin->reason, version); npi->table_id = pin->table_id; npi->cookie = pin->cookie; npi->match_len = htons(match_len); - return packet; + return msg; } static struct ofpbuf * -ofputil_encode_ofp11_packet_in(const struct ofputil_packet_in *pin) +ofputil_encode_nx_packet_in2(const struct ofputil_packet_in_private *pin, + enum ofp_version version, uint32_t buffer_id, + size_t include_bytes) +{ + /* 'extra' is just an estimate of the space required. */ + size_t extra = (pin->public.packet_len + + NXM_TYPICAL_LEN /* flow_metadata */ + + pin->n_stack * 16 + + pin->actions_len + + pin->action_set_len + + 256); /* fudge factor */ + struct ofpbuf *msg = ofpraw_alloc_xid(OFPRAW_NXT_PACKET_IN2, version, + htonl(0), extra); + + ofputil_put_packet_in_private(pin, version, buffer_id, include_bytes, msg); + if (pin->public.userdata_len) { + ofpprop_put(msg, NXPINT_USERDATA, pin->public.userdata, + pin->public.userdata_len); + } + + ofpmsg_update_length(msg); + return msg; +} + +static struct ofpbuf * +ofputil_encode_ofp11_packet_in(const struct ofputil_packet_in *pin, + uint32_t buffer_id) { struct ofp11_packet_in *opi; - struct ofpbuf *packet; + struct ofpbuf *msg; - packet = ofpraw_alloc_xid(OFPRAW_OFPT11_PACKET_IN, OFP11_VERSION, - htonl(0), pin->packet_len); - opi = ofpbuf_put_zeros(packet, sizeof *opi); - opi->buffer_id = htonl(pin->buffer_id); - opi->in_port = ofputil_port_to_ofp11(pin->flow_metadata.flow.in_port.ofp_port); + msg = ofpraw_alloc_xid(OFPRAW_OFPT11_PACKET_IN, OFP11_VERSION, + htonl(0), pin->packet_len); + opi = ofpbuf_put_zeros(msg, sizeof *opi); + opi->buffer_id = htonl(buffer_id); + opi->in_port = ofputil_port_to_ofp11( + pin->flow_metadata.flow.in_port.ofp_port); opi->in_phy_port = opi->in_port; - opi->total_len = htons(pin->total_len); - opi->reason = pin->reason; + opi->total_len = htons(pin->packet_len); + opi->reason = encode_packet_in_reason(pin->reason, OFP11_VERSION); opi->table_id = pin->table_id; - ofpbuf_put(packet, pin->packet, pin->packet_len); - - return packet; + return msg; } static struct ofpbuf * ofputil_encode_ofp12_packet_in(const struct ofputil_packet_in *pin, - enum ofputil_protocol protocol) -{ - struct ofp13_packet_in *opi; - enum ofpraw packet_in_raw; - enum ofp_version packet_in_version; - size_t packet_in_size; - struct ofpbuf *packet; - - if (protocol == OFPUTIL_P_OF12_OXM) { - packet_in_raw = OFPRAW_OFPT12_PACKET_IN; - packet_in_version = OFP12_VERSION; - packet_in_size = sizeof (struct ofp12_packet_in); - } else { - packet_in_raw = OFPRAW_OFPT13_PACKET_IN; - packet_in_version = ofputil_protocol_to_ofp_version(protocol); - packet_in_size = sizeof (struct ofp13_packet_in); - } + enum ofp_version version, + uint32_t buffer_id) +{ + enum ofpraw raw = (version >= OFP13_VERSION + ? OFPRAW_OFPT13_PACKET_IN + : OFPRAW_OFPT12_PACKET_IN); + struct ofpbuf *msg; /* The final argument is just an estimate of the space required. */ - packet = ofpraw_alloc_xid(packet_in_raw, packet_in_version, - htonl(0), NXM_TYPICAL_LEN + 2 + pin->packet_len); - ofpbuf_put_zeros(packet, packet_in_size); - oxm_put_match(packet, &pin->flow_metadata, - ofputil_protocol_to_ofp_version(protocol)); - ofpbuf_put_zeros(packet, 2); - ofpbuf_put(packet, pin->packet, pin->packet_len); + msg = ofpraw_alloc_xid(raw, version, + htonl(0), NXM_TYPICAL_LEN + 2 + pin->packet_len); + + struct ofp12_packet_in *opi = ofpbuf_put_zeros(msg, sizeof *opi); + opi->buffer_id = htonl(buffer_id); + opi->total_len = htons(pin->packet_len); + opi->reason = encode_packet_in_reason(pin->reason, version); + opi->table_id = pin->table_id; - opi = packet->msg; - opi->pi.buffer_id = htonl(pin->buffer_id); - opi->pi.total_len = htons(pin->total_len); - opi->pi.reason = pin->reason; - opi->pi.table_id = pin->table_id; - if (protocol != OFPUTIL_P_OF12_OXM) { - opi->cookie = pin->cookie; + if (version >= OFP13_VERSION) { + ovs_be64 cookie = pin->cookie; + ofpbuf_put(msg, &cookie, sizeof cookie); } - return packet; + oxm_put_match(msg, &pin->flow_metadata, version); + ofpbuf_put_zeros(msg, 2); + + return msg; } -/* Converts abstract ofputil_packet_in 'pin' into a PACKET_IN message - * in the format specified by 'packet_in_format'. */ +/* Converts abstract ofputil_packet_in_private 'pin' into a PACKET_IN message + * for 'protocol', using the packet-in format specified by 'packet_in_format'. + * + * If 'pkt_buf' is nonnull and 'max_len' allows the packet to be buffered, this + * function will attempt to obtain a buffer ID from 'pktbuf' and truncate the + * packet to 'max_len' bytes. Otherwise, or if 'pktbuf' doesn't have a free + * buffer, it will send the whole packet without buffering. + * + * This function is really meant only for use by ovs-vswitchd. To any other + * code, the "continuation" data, i.e. the data that is in struct + * ofputil_packet_in_private but not in struct ofputil_packet_in, is supposed + * to be opaque (and it might change from one OVS version to another). Thus, + * if any other code wants to encode a packet-in, it should use a non-"private" + * version of this function. (Such a version doesn't currently exist because + * only ovs-vswitchd currently wants to encode packet-ins. If you need one, + * write it...) */ struct ofpbuf * -ofputil_encode_packet_in(const struct ofputil_packet_in *pin, - enum ofputil_protocol protocol, - enum nx_packet_in_format packet_in_format) +ofputil_encode_packet_in_private(const struct ofputil_packet_in_private *pin, + enum ofputil_protocol protocol, + enum nx_packet_in_format packet_in_format, + uint16_t max_len, struct pktbuf *pktbuf) { - struct ofpbuf *packet; + enum ofp_version version = ofputil_protocol_to_ofp_version(protocol); - switch (protocol) { - case OFPUTIL_P_OF10_STD: - case OFPUTIL_P_OF10_STD_TID: - case OFPUTIL_P_OF10_NXM: - case OFPUTIL_P_OF10_NXM_TID: - packet = (packet_in_format == NXPIF_NXM - ? ofputil_encode_nx_packet_in(pin) - : ofputil_encode_ofp10_packet_in(pin)); - break; + /* Get buffer ID. */ + ofp_port_t in_port = pin->public.flow_metadata.flow.in_port.ofp_port; + uint32_t buffer_id = (max_len != OFPCML12_NO_BUFFER && pktbuf + ? pktbuf_save(pktbuf, pin->public.packet, + pin->public.packet_len, in_port) + : UINT32_MAX); - case OFPUTIL_P_OF11_STD: - packet = ofputil_encode_ofp11_packet_in(pin); + /* Calculate the number of bytes of the packet to include in the + * packet-in: + * + * - If not buffered, the whole thing. + * + * - Otherwise, no more than 'max_len' bytes. */ + size_t include_bytes = (buffer_id == UINT32_MAX + ? pin->public.packet_len + : MIN(max_len, pin->public.packet_len)); + + struct ofpbuf *msg; + switch (packet_in_format) { + case NXPIF_STANDARD: + switch (protocol) { + case OFPUTIL_P_OF10_STD: + case OFPUTIL_P_OF10_STD_TID: + case OFPUTIL_P_OF10_NXM: + case OFPUTIL_P_OF10_NXM_TID: + msg = ofputil_encode_ofp10_packet_in(&pin->public, buffer_id); + break; + + case OFPUTIL_P_OF11_STD: + msg = ofputil_encode_ofp11_packet_in(&pin->public, buffer_id); + break; + + case OFPUTIL_P_OF12_OXM: + case OFPUTIL_P_OF13_OXM: + case OFPUTIL_P_OF14_OXM: + case OFPUTIL_P_OF15_OXM: + msg = ofputil_encode_ofp12_packet_in(&pin->public, version, buffer_id); + break; + + default: + OVS_NOT_REACHED(); + } break; - case OFPUTIL_P_OF12_OXM: - case OFPUTIL_P_OF13_OXM: - case OFPUTIL_P_OF14_OXM: - case OFPUTIL_P_OF15_OXM: - packet = ofputil_encode_ofp12_packet_in(pin, protocol); + case NXPIF_NXT_PACKET_IN: + msg = ofputil_encode_nx_packet_in(&pin->public, version, buffer_id); break; + case NXPIF_NXT_PACKET_IN2: + return ofputil_encode_nx_packet_in2(pin, version, buffer_id, + include_bytes); + default: OVS_NOT_REACHED(); } - ofpmsg_update_length(packet); - return packet; + ofpbuf_put(msg, pin->public.packet, include_bytes); + ofpmsg_update_length(msg); + return msg; } /* Returns a string form of 'reason'. The return value is either a statically @@ -3584,6 +3935,9 @@ ofputil_packet_in_reason_to_string(enum ofp_packet_in_reason reason, return "group"; case OFPR_PACKET_OUT: return "packet_out"; + case OFPR_EXPLICIT_MISS: + case OFPR_IMPLICIT_MISS: + return ""; case OFPR_N_REASONS: default: @@ -3612,6 +3966,171 @@ ofputil_packet_in_reason_from_string(const char *s, return false; } +/* Returns a newly allocated NXT_RESUME message for 'pin', with the given + * 'continuation', for 'protocol'. This message is suitable for resuming the + * pipeline traveral of the packet represented by 'pin', if sent to the switch + * from which 'pin' was received. */ +struct ofpbuf * +ofputil_encode_resume(const struct ofputil_packet_in *pin, + const struct ofpbuf *continuation, + enum ofputil_protocol protocol) +{ + enum ofp_version version = ofputil_protocol_to_ofp_version(protocol); + size_t extra = pin->packet_len + NXM_TYPICAL_LEN + continuation->size; + struct ofpbuf *msg = ofpraw_alloc_xid(OFPRAW_NXT_RESUME, version, + 0, extra); + ofputil_put_packet_in(pin, version, UINT32_MAX, pin->packet_len, msg); + ofpprop_put_nested(msg, NXPINT_CONTINUATION, continuation); + ofpmsg_update_length(msg); + return msg; +} + +static enum ofperr +parse_subvalue_prop(const struct ofpbuf *property, union mf_subvalue *sv) +{ + unsigned int len = ofpbuf_msgsize(property); + if (len > sizeof *sv) { + VLOG_WARN_RL(&bad_ofmsg_rl, "NXCPT_STACK property has bad length %u", + len); + return OFPERR_OFPBPC_BAD_LEN; + } + memset(sv, 0, sizeof *sv); + memcpy(&sv->u8[sizeof *sv - len], property->msg, len); + return 0; +} + +static enum ofperr +parse_actions_property(struct ofpbuf *property, enum ofp_version version, + struct ofpbuf *ofpacts) +{ + if (!ofpbuf_try_pull(property, ROUND_UP(ofpbuf_headersize(property), 8))) { + VLOG_WARN_RL(&bad_ofmsg_rl, "actions property has bad length %"PRIu32, + property->size); + return OFPERR_OFPBPC_BAD_LEN; + } + + return ofpacts_pull_openflow_actions(property, property->size, + version, ofpacts); +} + +/* This is like ofputil_decode_packet_in(), except that it decodes the + * continuation data into 'pin'. The format of this data is supposed to be + * opaque to any process other than ovs-vswitchd, so this function should not + * be used outside ovs-vswitchd. + * + * When successful, 'pin' contains some dynamically allocated data. Call + * ofputil_packet_in_private_destroy() to free this data. */ +enum ofperr +ofputil_decode_packet_in_private(const struct ofp_header *oh, bool loose, + struct ofputil_packet_in_private *pin, + size_t *total_len, uint32_t *buffer_id) +{ + memset(pin, 0, sizeof *pin); + + struct ofpbuf continuation; + enum ofperr error; + error = ofputil_decode_packet_in(oh, loose, &pin->public, total_len, + buffer_id, &continuation); + if (error) { + return error; + } + + struct ofpbuf actions, action_set; + ofpbuf_init(&actions, 0); + ofpbuf_init(&action_set, 0); + + uint8_t table_id = 0; + ovs_be64 cookie = 0; + + size_t allocated_stack = 0; + + while (continuation.size > 0) { + struct ofpbuf payload; + uint64_t type; + + error = ofpprop_pull(&continuation, &payload, &type); + ovs_assert(!error); + + switch (type) { + case NXCPT_BRIDGE: + error = ofpprop_parse_uuid(&payload, &pin->bridge); + break; + + case NXCPT_STACK: + if (pin->n_stack >= allocated_stack) { + pin->stack = x2nrealloc(pin->stack, &allocated_stack, + sizeof *pin->stack); + } + error = parse_subvalue_prop(&payload, + &pin->stack[pin->n_stack++]); + break; + + case NXCPT_MIRRORS: + error = ofpprop_parse_u32(&payload, &pin->mirrors); + break; + + case NXCPT_CONNTRACKED: + pin->conntracked = true; + break; + + case NXCPT_TABLE_ID: + error = ofpprop_parse_u8(&payload, &table_id); + break; + + case NXCPT_COOKIE: + error = ofpprop_parse_be64(&payload, &cookie); + break; + + case NXCPT_ACTIONS: { + struct ofpact_unroll_xlate *unroll + = ofpact_put_UNROLL_XLATE(&actions); + unroll->rule_table_id = table_id; + unroll->rule_cookie = cookie; + error = parse_actions_property(&payload, oh->version, &actions); + break; + } + + case NXCPT_ACTION_SET: + error = parse_actions_property(&payload, oh->version, &action_set); + break; + + default: + error = OFPPROP_UNKNOWN(loose, "continuation", type); + break; + } + if (error) { + break; + } + } + + pin->actions_len = actions.size; + pin->actions = ofpbuf_steal_data(&actions); + pin->action_set_len = action_set.size; + pin->action_set = ofpbuf_steal_data(&action_set); + + if (error) { + ofputil_packet_in_private_destroy(pin); + } + + return 0; +} + +/* Frees data in 'pin' that is dynamically allocated by + * ofputil_decode_packet_in_private(). + * + * 'pin->public' contains some pointer members that + * ofputil_decode_packet_in_private() doesn't initialize to newly allocated + * data, so this function doesn't free those. */ +void +ofputil_packet_in_private_destroy(struct ofputil_packet_in_private *pin) +{ + if (pin) { + free(pin->stack); + free(pin->actions); + free(pin->action_set); + } +} + /* Converts an OFPT_PACKET_OUT in 'opo' into an abstract ofputil_packet_out in * 'po'. * @@ -3625,11 +4144,8 @@ ofputil_decode_packet_out(struct ofputil_packet_out *po, const struct ofp_header *oh, struct ofpbuf *ofpacts) { - enum ofpraw raw; - struct ofpbuf b; - - ofpbuf_use_const(&b, oh, ntohs(oh->length)); - raw = ofpraw_pull_assert(&b); + struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length)); + enum ofpraw raw = ofpraw_pull_assert(&b); ofpbuf_clear(ofpacts); if (raw == OFPRAW_OFPT11_PACKET_OUT) { @@ -3818,24 +4334,18 @@ parse_ofp14_port_ethernet_property(const struct ofpbuf *payload, static enum ofperr ofputil_pull_ofp14_port(struct ofputil_phy_port *pp, struct ofpbuf *msg) { - struct ofpbuf properties; - struct ofp14_port *op; - enum ofperr error; - size_t len; - - op = ofpbuf_try_pull(msg, sizeof *op); + struct ofp14_port *op = ofpbuf_try_pull(msg, sizeof *op); if (!op) { return OFPERR_OFPBRC_BAD_LEN; } - len = ntohs(op->length); + size_t len = ntohs(op->length); if (len < sizeof *op || len - sizeof *op > msg->size) { return OFPERR_OFPBRC_BAD_LEN; } len -= sizeof *op; - ofpbuf_use_const(&properties, ofpbuf_pull(msg, len), len); - error = ofputil_port_from_ofp11(op->port_no, &pp->port_no); + enum ofperr error = ofputil_port_from_ofp11(op->port_no, &pp->port_no); if (error) { return error; } @@ -3845,6 +4355,8 @@ ofputil_pull_ofp14_port(struct ofputil_phy_port *pp, struct ofpbuf *msg) pp->config = ntohl(op->config) & OFPPC11_ALL; pp->state = ntohl(op->state) & OFPPS11_ALL; + struct ofpbuf properties = ofpbuf_const_initializer(ofpbuf_pull(msg, len), + len); while (properties.size > 0) { struct ofpbuf payload; enum ofperr error; @@ -3973,11 +4485,9 @@ enum ofperr ofputil_decode_port_desc_stats_request(const struct ofp_header *request, ofp_port_t *port) { - struct ofpbuf b; - enum ofpraw raw; - - ofpbuf_use_const(&b, request, ntohs(request->length)); - raw = ofpraw_pull_assert(&b); + struct ofpbuf b = ofpbuf_const_initializer(request, + ntohs(request->length)); + enum ofpraw raw = ofpraw_pull_assert(&b); if (raw == OFPRAW_OFPST10_PORT_DESC_REQUEST) { *port = OFPP_ANY; return 0; @@ -4041,13 +4551,10 @@ static bool ofputil_decode_switch_config(const struct ofp_header *oh, struct ofputil_switch_config *config) { - const struct ofp_switch_config *osc; - struct ofpbuf b; - - ofpbuf_use_const(&b, oh, ntohs(oh->length)); + struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length)); ofpraw_pull_assert(&b); - osc = ofpbuf_pull(&b, sizeof *osc); + const struct ofp_switch_config *osc = ofpbuf_pull(&b, sizeof *osc); config->frag = ntohs(osc->flags) & OFPC_FRAG_MASK; config->miss_send_len = ntohs(osc->miss_send_len); @@ -4140,23 +4647,18 @@ ofputil_capabilities_mask(enum ofp_version ofp_version) } } -/* Decodes an OpenFlow 1.0 or 1.1 "switch_features" structure 'osf' into an - * abstract representation in '*features'. Initializes '*b' to iterate over - * the OpenFlow port structures following 'osf' with later calls to - * ofputil_pull_phy_port(). Returns 0 if successful, otherwise an - * OFPERR_* value. */ +/* Pulls an OpenFlow "switch_features" structure from 'b' and decodes it into + * an abstract representation in '*features', readying 'b' to iterate over the + * OpenFlow port structures following 'osf' with later calls to + * ofputil_pull_phy_port(). Returns 0 if successful, otherwise an OFPERR_* + * value. */ enum ofperr -ofputil_decode_switch_features(const struct ofp_header *oh, - struct ofputil_switch_features *features, - struct ofpbuf *b) +ofputil_pull_switch_features(struct ofpbuf *b, + struct ofputil_switch_features *features) { - const struct ofp_switch_features *osf; - enum ofpraw raw; - - ofpbuf_use_const(b, oh, ntohs(oh->length)); - raw = ofpraw_pull_assert(b); - - osf = ofpbuf_pull(b, sizeof *osf); + const struct ofp_header *oh = b->data; + enum ofpraw raw = ofpraw_pull_assert(b); + const struct ofp_switch_features *osf = ofpbuf_pull(b, sizeof *osf); features->datapath_id = ntohll(osf->datapath_id); features->n_buffers = ntohl(osf->n_buffers); features->n_tables = osf->n_tables; @@ -4321,14 +4823,10 @@ enum ofperr ofputil_decode_port_status(const struct ofp_header *oh, struct ofputil_port_status *ps) { - const struct ofp_port_status *ops; - struct ofpbuf b; - int retval; - - ofpbuf_use_const(&b, oh, ntohs(oh->length)); + struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length)); ofpraw_pull_assert(&b); - ops = ofpbuf_pull(&b, sizeof *ops); + const struct ofp_port_status *ops = ofpbuf_pull(&b, sizeof *ops); if (ops->reason != OFPPR_ADD && ops->reason != OFPPR_DELETE && ops->reason != OFPPR_MODIFY) { @@ -4336,7 +4834,7 @@ ofputil_decode_port_status(const struct ofp_header *oh, } ps->reason = ops->reason; - retval = ofputil_pull_phy_port(oh->version, &b, &ps->desc); + int retval = ofputil_pull_phy_port(oh->version, &b, &ps->desc); ovs_assert(retval != EOF); return retval; } @@ -4404,12 +4902,8 @@ enum ofperr ofputil_decode_port_mod(const struct ofp_header *oh, struct ofputil_port_mod *pm, bool loose) { - enum ofpraw raw; - struct ofpbuf b; - - ofpbuf_use_const(&b, oh, ntohs(oh->length)); - raw = ofpraw_pull_assert(&b); - + struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length)); + enum ofpraw raw = ofpraw_pull_assert(&b); if (raw == OFPRAW_OFPT10_PORT_MOD) { const struct ofp10_port_mod *opm = b.data; @@ -4680,33 +5174,26 @@ int ofputil_decode_table_features(struct ofpbuf *msg, struct ofputil_table_features *tf, bool loose) { - const struct ofp_header *oh; - struct ofp13_table_features *otf; - struct ofpbuf properties; - unsigned int len; - memset(tf, 0, sizeof *tf); if (!msg->header) { ofpraw_pull_assert(msg); } - oh = msg->header; if (!msg->size) { return EOF; } + const struct ofp_header *oh = msg->header; + struct ofp13_table_features *otf = msg->data; if (msg->size < sizeof *otf) { return OFPERR_OFPBPC_BAD_LEN; } - otf = msg->data; - len = ntohs(otf->length); + unsigned int len = ntohs(otf->length); if (len < sizeof *otf || len % 8 || len > msg->size) { return OFPERR_OFPBPC_BAD_LEN; } - ofpbuf_use_const(&properties, ofpbuf_pull(msg, len), len); - ofpbuf_pull(&properties, sizeof *otf); tf->table_id = otf->table_id; if (tf->table_id == OFPTT_ALL) { @@ -4727,6 +5214,9 @@ ofputil_decode_table_features(struct ofpbuf *msg, } tf->max_entries = ntohl(otf->max_entries); + struct ofpbuf properties = ofpbuf_const_initializer(ofpbuf_pull(msg, len), + len); + ofpbuf_pull(&properties, sizeof *otf); while (properties.size > 0) { struct ofpbuf payload; enum ofperr error; @@ -4971,10 +5461,6 @@ ofputil_decode_table_desc(struct ofpbuf *msg, struct ofputil_table_desc *td, enum ofp_version version) { - struct ofp14_table_desc *otd; - struct ofpbuf properties; - size_t length; - memset(td, 0, sizeof *td); if (!msg->header) { @@ -4985,7 +5471,7 @@ ofputil_decode_table_desc(struct ofpbuf *msg, return EOF; } - otd = ofpbuf_try_pull(msg, sizeof *otd); + struct ofp14_table_desc *otd = ofpbuf_try_pull(msg, sizeof *otd); if (!otd) { VLOG_WARN_RL(&bad_ofmsg_rl, "OFP14_TABLE_DESC reply has %"PRIu32" " "leftover bytes at end", msg->size); @@ -4993,19 +5479,20 @@ ofputil_decode_table_desc(struct ofpbuf *msg, } td->table_id = otd->table_id; - length = ntohs(otd->length); + size_t length = ntohs(otd->length); if (length < sizeof *otd || length - sizeof *otd > msg->size) { VLOG_WARN_RL(&bad_ofmsg_rl, "OFP14_TABLE_DESC reply claims invalid " "length %"PRIuSIZE, length); return OFPERR_OFPBRC_BAD_LEN; } length -= sizeof *otd; - ofpbuf_use_const(&properties, ofpbuf_pull(msg, length), length); td->eviction = ofputil_decode_table_eviction(otd->config, version); td->vacancy = ofputil_decode_table_vacancy(otd->config, version); td->eviction_flags = UINT32_MAX; + struct ofpbuf properties = ofpbuf_const_initializer( + ofpbuf_pull(msg, length), length); while (properties.size > 0) { struct ofpbuf payload; enum ofperr error; @@ -5249,17 +5736,14 @@ enum ofperr ofputil_decode_table_mod(const struct ofp_header *oh, struct ofputil_table_mod *pm) { - enum ofpraw raw; - struct ofpbuf b; - memset(pm, 0, sizeof *pm); pm->miss = OFPUTIL_TABLE_MISS_DEFAULT; pm->eviction = OFPUTIL_TABLE_EVICTION_DEFAULT; pm->eviction_flags = UINT32_MAX; pm->vacancy = OFPUTIL_TABLE_VACANCY_DEFAULT; - ofpbuf_use_const(&b, oh, ntohs(oh->length)); - raw = ofpraw_pull_assert(&b); + struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length)); + enum ofpraw raw = ofpraw_pull_assert(&b); if (raw == OFPRAW_OFPT11_TABLE_MOD) { const struct ofp11_table_mod *otm = b.data; @@ -5373,12 +5857,8 @@ enum ofperr ofputil_decode_role_message(const struct ofp_header *oh, struct ofputil_role_request *rr) { - struct ofpbuf b; - enum ofpraw raw; - - ofpbuf_use_const(&b, oh, ntohs(oh->length)); - raw = ofpraw_pull_assert(&b); - + struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length)); + enum ofpraw raw = ofpraw_pull_assert(&b); if (raw == OFPRAW_OFPT12_ROLE_REQUEST || raw == OFPRAW_OFPT12_ROLE_REPLY) { const struct ofp12_role_request *orr = b.msg; @@ -5492,15 +5972,11 @@ 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); + struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length)); + enum ofpraw raw = ofpraw_pull_assert(&b); ovs_assert(raw == OFPRAW_OFPT14_ROLE_STATUS); - r = b.msg; + const struct ofp14_role_status *r = b.msg; if (r->role != htonl(OFPCR12_ROLE_NOCHANGE) && r->role != htonl(OFPCR12_ROLE_EQUAL) && r->role != htonl(OFPCR12_ROLE_MASTER) && @@ -5559,10 +6035,7 @@ enum ofperr ofputil_decode_requestforward(const struct ofp_header *outer, struct ofputil_requestforward *rf) { - struct ofpbuf b; - enum ofperr error; - - ofpbuf_use_const(&b, outer, ntohs(outer->length)); + struct ofpbuf b = ofpbuf_const_initializer(outer, ntohs(outer->length)); /* Skip past outer message. */ enum ofpraw outer_raw = ofpraw_pull_assert(&b); @@ -5583,7 +6056,7 @@ ofputil_decode_requestforward(const struct ofp_header *outer, /* Parse inner message. */ enum ofptype type; - error = ofptype_decode(&type, inner); + enum ofperr error = ofptype_decode(&type, inner); if (error) { return error; } @@ -6393,13 +6866,11 @@ make_echo_request(enum ofp_version ofp_version) struct ofpbuf * make_echo_reply(const struct ofp_header *rq) { - struct ofpbuf rq_buf; - struct ofpbuf *reply; - - ofpbuf_use_const(&rq_buf, rq, ntohs(rq->length)); + struct ofpbuf rq_buf = ofpbuf_const_initializer(rq, ntohs(rq->length)); ofpraw_pull_assert(&rq_buf); - reply = ofpraw_alloc_reply(OFPRAW_OFPT_ECHO_REPLY, rq, rq_buf.size); + struct ofpbuf *reply = ofpraw_alloc_reply(OFPRAW_OFPT_ECHO_REPLY, + rq, rq_buf.size); ofpbuf_put(reply, rq_buf.data, rq_buf.size); return reply; } @@ -7148,24 +7619,18 @@ static enum ofperr ofputil_pull_ofp14_port_stats(struct ofputil_port_stats *ops, struct ofpbuf *msg) { - const struct ofp14_port_stats *ps14; - struct ofpbuf properties; - enum ofperr error; - size_t len; - - ps14 = ofpbuf_try_pull(msg, sizeof *ps14); + const struct ofp14_port_stats *ps14 = ofpbuf_try_pull(msg, sizeof *ps14); if (!ps14) { return OFPERR_OFPBRC_BAD_LEN; } - len = ntohs(ps14->length); + size_t len = ntohs(ps14->length); if (len < sizeof *ps14 || len - sizeof *ps14 > msg->size) { return OFPERR_OFPBRC_BAD_LEN; } len -= sizeof *ps14; - ofpbuf_use_const(&properties, ofpbuf_pull(msg, len), len); - error = ofputil_port_from_ofp11(ps14->port_no, &ops->port_no); + enum ofperr error = ofputil_port_from_ofp11(ps14->port_no, &ops->port_no); if (error) { return error; } @@ -7185,6 +7650,8 @@ ofputil_pull_ofp14_port_stats(struct ofputil_port_stats *ops, ops->stats.rx_crc_errors = UINT64_MAX; ops->stats.collisions = UINT64_MAX; + struct ofpbuf properties = ofpbuf_const_initializer(ofpbuf_pull(msg, len), + len); while (properties.size > 0) { struct ofpbuf payload; enum ofperr error; @@ -7218,16 +7685,15 @@ ofputil_pull_ofp14_port_stats(struct ofputil_port_stats *ops, size_t ofputil_count_port_stats(const struct ofp_header *oh) { - struct ofputil_port_stats ps; - struct ofpbuf b; - size_t n = 0; - - ofpbuf_use_const(&b, oh, ntohs(oh->length)); + struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length)); ofpraw_pull_assert(&b); - while (!ofputil_decode_port_stats(&ps, &b)) { - n++; + + for (size_t n = 0; ; n++) { + struct ofputil_port_stats ps; + if (ofputil_decode_port_stats(&ps, &b)) { + return n; + } } - return n; } /* Converts an OFPST_PORT_STATS reply in 'msg' into an abstract @@ -7477,11 +7943,8 @@ ofputil_uninit_group_desc(struct ofputil_group_desc *gd) uint32_t ofputil_decode_group_desc_request(const struct ofp_header *oh) { - struct ofpbuf request; - enum ofpraw raw; - - ofpbuf_use_const(&request, oh, ntohs(oh->length)); - raw = ofpraw_pull_assert(&request); + struct ofpbuf request = ofpbuf_const_initializer(oh, ntohs(oh->length)); + enum ofpraw raw = ofpraw_pull_assert(&request); if (raw == OFPRAW_OFPST11_GROUP_DESC_REQUEST) { return OFPG_ALL; } else if (raw == OFPRAW_OFPST15_GROUP_DESC_REQUEST) { @@ -8011,7 +8474,6 @@ ofputil_pull_ofp15_buckets(struct ofpbuf *msg, size_t buckets_length, struct ofputil_bucket *bucket = NULL; struct ofpbuf ofpacts; enum ofperr err = OFPERR_OFPGMFC_BAD_BUCKET; - struct ofpbuf properties; size_t ob_len, actions_len, properties_len; ovs_be32 watch_port = ofputil_port_to_ofp11(OFPP_ANY); ovs_be32 watch_group = htonl(OFPG_ANY); @@ -8054,9 +8516,8 @@ ofputil_pull_ofp15_buckets(struct ofpbuf *msg, size_t buckets_length, } properties_len = ob_len - sizeof *ob - actions_len; - ofpbuf_use_const(&properties, ofpbuf_pull(msg, properties_len), - properties_len); - + struct ofpbuf properties = ofpbuf_const_initializer( + ofpbuf_pull(msg, properties_len), properties_len); while (properties.size > 0) { struct ofpbuf payload; uint64_t type; @@ -8228,11 +8689,8 @@ parse_ofp15_group_properties(struct ofpbuf *msg, struct ofputil_group_props *gp, size_t properties_len) { - struct ofpbuf properties; - - ofpbuf_use_const(&properties, ofpbuf_pull(msg, properties_len), - properties_len); - + struct ofpbuf properties = ofpbuf_const_initializer( + ofpbuf_pull(msg, properties_len), properties_len); while (properties.size > 0) { struct ofpbuf payload; enum ofperr error; @@ -8651,16 +9109,13 @@ enum ofperr ofputil_decode_group_mod(const struct ofp_header *oh, struct ofputil_group_mod *gm) { - enum ofp_version ofp_version = oh->version; - struct ofpbuf msg; - struct ofputil_bucket *bucket; - enum ofperr err; + ofputil_init_group_properties(&gm->props); - ofpbuf_use_const(&msg, oh, ntohs(oh->length)); + enum ofp_version ofp_version = oh->version; + struct ofpbuf msg = ofpbuf_const_initializer(oh, ntohs(oh->length)); ofpraw_pull_assert(&msg); - ofputil_init_group_properties(&gm->props); - + enum ofperr err; switch (ofp_version) { case OFP11_VERSION: @@ -8678,7 +9133,6 @@ ofputil_decode_group_mod(const struct ofp_header *oh, default: OVS_NOT_REACHED(); } - if (err) { return err; } @@ -8712,6 +9166,7 @@ ofputil_decode_group_mod(const struct ofp_header *oh, return OFPERR_OFPGMFC_BAD_COMMAND; } + struct ofputil_bucket *bucket; LIST_FOR_EACH (bucket, list_node, &gm->buckets) { if (bucket->weight && gm->type != OFPGT11_SELECT) { return OFPERR_OFPGMFC_INVALID_GROUP; @@ -8818,16 +9273,15 @@ ofputil_encode_queue_stats_request(enum ofp_version ofp_version, size_t ofputil_count_queue_stats(const struct ofp_header *oh) { - struct ofputil_queue_stats qs; - struct ofpbuf b; - size_t n = 0; - - ofpbuf_use_const(&b, oh, ntohs(oh->length)); + struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length)); ofpraw_pull_assert(&b); - while (!ofputil_decode_queue_stats(&qs, &b)) { - n++; + + for (size_t n = 0; ; n++) { + struct ofputil_queue_stats qs; + if (ofputil_decode_queue_stats(&qs, &b)) { + return n; + } } - return n; } static enum ofperr @@ -9048,15 +9502,11 @@ enum ofperr ofputil_decode_bundle_ctrl(const struct ofp_header *oh, struct ofputil_bundle_ctrl_msg *msg) { - struct ofpbuf b; - enum ofpraw raw; - const struct ofp14_bundle_ctrl_msg *m; - - ofpbuf_use_const(&b, oh, ntohs(oh->length)); - raw = ofpraw_pull_assert(&b); + struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length)); + enum ofpraw raw = ofpraw_pull_assert(&b); ovs_assert(raw == OFPRAW_OFPT14_BUNDLE_CONTROL); - m = b.msg; + const struct ofp14_bundle_ctrl_msg *m = b.msg; msg->bundle_id = ntohl(m->bundle_id); msg->type = ntohs(m->type); msg->flags = ntohs(m->flags); @@ -9195,8 +9645,10 @@ ofputil_is_bundlable(enum ofptype type) case OFPTYPE_TABLE_DESC_REPLY: case OFPTYPE_ROLE_STATUS: case OFPTYPE_REQUESTFORWARD: + case OFPTYPE_TABLE_STATUS: case OFPTYPE_NXT_TLV_TABLE_REQUEST: case OFPTYPE_NXT_TLV_TABLE_REPLY: + case OFPTYPE_NXT_RESUME: break; } @@ -9206,20 +9658,13 @@ ofputil_is_bundlable(enum ofptype type) enum ofperr ofputil_decode_bundle_add(const struct ofp_header *oh, struct ofputil_bundle_add_msg *msg, - enum ofptype *type_ptr) + enum ofptype *typep) { - const struct ofp14_bundle_ctrl_msg *m; - struct ofpbuf b; - enum ofpraw raw; - size_t inner_len; - enum ofperr error; - enum ofptype type; - - ofpbuf_use_const(&b, oh, ntohs(oh->length)); - raw = ofpraw_pull_assert(&b); + struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length)); + enum ofpraw raw = ofpraw_pull_assert(&b); ovs_assert(raw == OFPRAW_OFPT14_BUNDLE_ADD_MESSAGE); - m = ofpbuf_pull(&b, sizeof *m); + const struct ofp14_bundle_ctrl_msg *m = ofpbuf_pull(&b, sizeof *m); msg->bundle_id = ntohl(m->bundle_id); msg->flags = ntohs(m->flags); @@ -9227,7 +9672,7 @@ ofputil_decode_bundle_add(const struct ofp_header *oh, if (msg->msg->version != oh->version) { return OFPERR_NXBFC_BAD_VERSION; } - inner_len = ntohs(msg->msg->length); + size_t inner_len = ntohs(msg->msg->length); if (inner_len < sizeof(struct ofp_header) || inner_len > b.size) { return OFPERR_OFPBFC_MSG_BAD_LEN; } @@ -9236,21 +9681,22 @@ ofputil_decode_bundle_add(const struct ofp_header *oh, } /* Reject unbundlable messages. */ - if (!type_ptr) { - type_ptr = &type; - } - error = ofptype_decode(type_ptr, msg->msg); + enum ofptype type; + enum ofperr error = ofptype_decode(&type, msg->msg); if (error) { VLOG_WARN_RL(&bad_ofmsg_rl, "OFPT14_BUNDLE_ADD_MESSAGE contained " "message is unparsable (%s)", ofperr_get_name(error)); return OFPERR_OFPBFC_MSG_UNSUP; /* 'error' would be confusing. */ } - if (!ofputil_is_bundlable(*type_ptr)) { + if (!ofputil_is_bundlable(type)) { VLOG_WARN_RL(&bad_ofmsg_rl, "%s message not allowed inside " - "OFPT14_BUNDLE_ADD_MESSAGE", ofptype_get_name(*type_ptr)); + "OFPT14_BUNDLE_ADD_MESSAGE", ofptype_get_name(type)); return OFPERR_OFPBFC_MSG_UNSUP; } + if (typep) { + *typep = type; + } return 0; } @@ -9348,13 +9794,10 @@ enum ofperr ofputil_decode_tlv_table_mod(const struct ofp_header *oh, struct ofputil_tlv_table_mod *ttm) { - struct ofpbuf msg; - struct nx_tlv_table_mod *nx_ttm; - - ofpbuf_use_const(&msg, oh, ntohs(oh->length)); + struct ofpbuf msg = ofpbuf_const_initializer(oh, ntohs(oh->length)); ofpraw_pull_assert(&msg); - nx_ttm = ofpbuf_pull(&msg, sizeof *nx_ttm); + struct nx_tlv_table_mod *nx_ttm = ofpbuf_pull(&msg, sizeof *nx_ttm); ttm->command = ntohs(nx_ttm->command); if (ttm->command > NXTTMC_CLEAR) { VLOG_WARN_RL(&bad_ofmsg_rl, @@ -9394,13 +9837,10 @@ enum ofperr ofputil_decode_tlv_table_reply(const struct ofp_header *oh, struct ofputil_tlv_table_reply *ttr) { - struct ofpbuf msg; - struct nx_tlv_table_reply *nx_ttr; - - ofpbuf_use_const(&msg, oh, ntohs(oh->length)); + struct ofpbuf msg = ofpbuf_const_initializer(oh, ntohs(oh->length)); ofpraw_pull_assert(&msg); - nx_ttr = ofpbuf_pull(&msg, sizeof *nx_ttr); + struct nx_tlv_table_reply *nx_ttr = ofpbuf_pull(&msg, sizeof *nx_ttr); ttr->max_option_space = ntohl(nx_ttr->max_option_space); ttr->max_fields = ntohs(nx_ttr->max_fields); @@ -9515,6 +9955,15 @@ decode_async_mask(ovs_be32 src, } } + if (ap->oam == OAM_PACKET_IN) { + if (mask & (1u << OFPR_NO_MATCH)) { + mask |= 1u << OFPR_EXPLICIT_MISS; + if (version < OFP13_VERSION) { + mask |= 1u << OFPR_IMPLICIT_MISS; + } + } + } + uint32_t *array = ap->master ? dst->master : dst->slave; array[ap->oam] = mask; return 0; @@ -9595,11 +10044,8 @@ ofputil_decode_set_async_config(const struct ofp_header *oh, bool loose, const struct ofputil_async_cfg *basis, struct ofputil_async_cfg *ac) { - enum ofpraw raw; - struct ofpbuf b; - - ofpbuf_use_const(&b, oh, ntohs(oh->length)); - raw = ofpraw_pull_assert(&b); + struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length)); + enum ofpraw raw = ofpraw_pull_assert(&b); if (raw == OFPRAW_OFPT13_SET_ASYNC || raw == OFPRAW_NXT_SET_ASYNC_CONFIG || @@ -9614,7 +10060,8 @@ ofputil_decode_set_async_config(const struct ofp_header *oh, bool loose, decode_legacy_async_masks(msg->flow_removed_mask, OAM_FLOW_REMOVED, oh->version, ac); } else if (raw == OFPRAW_OFPT14_SET_ASYNC || - raw == OFPRAW_OFPT14_GET_ASYNC_REPLY) { + raw == OFPRAW_OFPT14_GET_ASYNC_REPLY || + raw == OFPRAW_NXT_SET_ASYNC_CONFIG2) { *ac = *basis; while (b.size > 0) { struct ofpbuf property; @@ -9695,8 +10142,8 @@ ofputil_put_async_config__(const struct ofputil_async_cfg *ac, /* Encodes and returns a reply to the OFPT_GET_ASYNC_REQUEST in 'oh' that * states that the asynchronous message configuration is 'ac'. */ struct ofpbuf * -ofputil_encode_get_async_config(const struct ofp_header *oh, - const struct ofputil_async_cfg *ac) +ofputil_encode_get_async_reply(const struct ofp_header *oh, + const struct ofputil_async_cfg *ac) { struct ofpbuf *buf; @@ -9712,13 +10159,47 @@ ofputil_encode_get_async_config(const struct ofp_header *oh, return buf; } +/* Encodes and returns a message, in a format appropriate for OpenFlow version + * 'ofp_version', that sets the asynchronous message configuration to 'ac'. + * + * Specify 'oams' as a bitmap of OAM_* that indicate the asynchronous messages + * to configure. OF1.0 through OF1.3 can't natively configure a subset of + * messages, so more messages than requested may be configured. OF1.0 through + * OF1.3 also can't configure OVS extension OAM_* values, so if 'oam' includes + * any extensions then this function encodes an Open vSwitch extension message + * that does support configuring OVS extension OAM_*. */ +struct ofpbuf * +ofputil_encode_set_async_config(const struct ofputil_async_cfg *ac, + uint32_t oams, enum ofp_version ofp_version) +{ + enum ofpraw raw = (ofp_version >= OFP14_VERSION ? OFPRAW_OFPT14_SET_ASYNC + : oams & OAM_EXTENSIONS ? OFPRAW_NXT_SET_ASYNC_CONFIG2 + : ofp_version >= OFP13_VERSION ? OFPRAW_OFPT13_SET_ASYNC + : OFPRAW_NXT_SET_ASYNC_CONFIG); + struct ofpbuf *request = ofpraw_alloc(raw, ofp_version, 0); + ofputil_put_async_config__(ac, request, + (raw == OFPRAW_OFPT14_SET_ASYNC || + raw == OFPRAW_NXT_SET_ASYNC_CONFIG2), + ofp_version, oams); + return request; +} + struct ofputil_async_cfg ofputil_async_cfg_default(enum ofp_version version) { + /* We enable all of the OF1.4 reasons regardless of 'version' because the + * reasons added in OF1.4 just are just refinements of the OFPR_ACTION + * introduced in OF1.0, breaking it into more specific categories. When we + * encode these for earlier OpenFlow versions, we translate them into + * OFPR_ACTION. */ + uint32_t pin = OFPR14_BITS & ~(1u << OFPR_INVALID_TTL); + pin |= 1u << OFPR_EXPLICIT_MISS; + if (version <= OFP12_VERSION) { + pin |= 1u << OFPR_IMPLICIT_MISS; + } + return (struct ofputil_async_cfg) { - .master[OAM_PACKET_IN] - = ((version >= OFP14_VERSION ? OFPR14_BITS : OFPR10_BITS) - & ~(1u << OFPR_INVALID_TTL)), + .master[OAM_PACKET_IN] = pin, .master[OAM_FLOW_REMOVED] = (version >= OFP14_VERSION ? OFPRR14_BITS : OFPRR10_BITS), @@ -9727,3 +10208,89 @@ ofputil_async_cfg_default(enum ofp_version version) .slave[OAM_PORT_STATUS] = OFPPR_BITS, }; } + +static void +ofputil_put_ofp14_table_desc(const struct ofputil_table_desc *td, + struct ofpbuf *b, enum ofp_version version) +{ + struct ofp14_table_desc *otd; + struct ofp14_table_mod_prop_vacancy *otv; + size_t start_otd; + + start_otd = b->size; + ofpbuf_put_zeros(b, sizeof *otd); + + ofpprop_put_u32(b, OFPTMPT14_EVICTION, td->eviction_flags); + + otv = ofpbuf_put_zeros(b, sizeof *otv); + otv->type = htons(OFPTMPT14_VACANCY); + otv->length = htons(sizeof *otv); + otv->vacancy_down = td->table_vacancy.vacancy_down; + otv->vacancy_up = td->table_vacancy.vacancy_up; + otv->vacancy = td->table_vacancy.vacancy; + + otd = ofpbuf_at_assert(b, start_otd, sizeof *otd); + otd->length = htons(b->size - start_otd); + otd->table_id = td->table_id; + otd->config = ofputil_encode_table_config(OFPUTIL_TABLE_MISS_DEFAULT, + td->eviction, td->vacancy, + version); +} + +/* Converts the abstract form of a "table status" message in '*ts' into an + * OpenFlow message suitable for 'protocol', and returns that encoded form in + * a buffer owned by the caller. */ +struct ofpbuf * +ofputil_encode_table_status(const struct ofputil_table_status *ts, + enum ofputil_protocol protocol) +{ + enum ofp_version version; + struct ofpbuf *b; + + version = ofputil_protocol_to_ofp_version(protocol); + if (version >= OFP14_VERSION) { + enum ofpraw raw; + struct ofp14_table_status *ots; + + raw = OFPRAW_OFPT14_TABLE_STATUS; + b = ofpraw_alloc_xid(raw, version, htonl(0), 0); + ots = ofpbuf_put_zeros(b, sizeof *ots); + ots->reason = ts->reason; + ofputil_put_ofp14_table_desc(&ts->desc, b, version); + ofpmsg_update_length(b); + return b; + } else { + return NULL; + } +} + +/* Decodes the OpenFlow "table status" message in '*ots' into an abstract form + * in '*ts'. Returns 0 if successful, otherwise an OFPERR_* value. */ +enum ofperr +ofputil_decode_table_status(const struct ofp_header *oh, + struct ofputil_table_status *ts) +{ + const struct ofp14_table_status *ots; + struct ofpbuf b; + enum ofperr error; + enum ofpraw raw; + + ofpbuf_use_const(&b, oh, ntohs(oh->length)); + raw = ofpraw_pull_assert(&b); + ots = ofpbuf_pull(&b, sizeof *ots); + + if (raw == OFPRAW_OFPT14_TABLE_STATUS) { + if (ots->reason != OFPTR_VACANCY_DOWN + && ots->reason != OFPTR_VACANCY_UP) { + return OFPERR_OFPBPC_BAD_VALUE; + } + ts->reason = ots->reason; + + error = ofputil_decode_table_desc(&b, &ts->desc, oh->version); + return error; + } else { + return OFPERR_OFPBRC_BAD_VERSION; + } + + return 0; +}