X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=lib%2Fofp-util.c;h=579be73f1c21e564f2d3f6698b1c641f2f9d5f85;hb=299016266ed1;hp=37eb073e13aedfb1676cc0db4f35bee704cf1da4;hpb=18ddadc2e96279c69b1960af57da7af9332fbb21;p=openvswitch diff --git a/lib/ofp-util.c b/lib/ofp-util.c index 37eb073e..579be73f 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -21,6 +21,7 @@ #include #include #include "autopath.h" +#include "bundle.h" #include "byte-order.h" #include "classifier.h" #include "dynamic-string.h" @@ -725,8 +726,8 @@ ofputil_decode_msg_type(const struct ofp_header *oh, } if (error) { static const struct ofputil_msg_type ofputil_invalid_type = { - OFPUTIL_INVALID, - 0, "OFPUTIL_INVALID", + OFPUTIL_MSG_INVALID, + 0, "OFPUTIL_MSG_INVALID", 0, 0 }; @@ -974,7 +975,7 @@ ofputil_encode_flow_mod(const struct flow_mod *fm, ofm = put_openflow(sizeof *ofm, OFPT_FLOW_MOD, msg); ofputil_cls_rule_to_match(&fm->cr, &ofm->match); ofm->cookie = fm->cookie; - ofm->command = htons(fm->command); + ofm->command = htons(command); ofm->idle_timeout = htons(fm->idle_timeout); ofm->hard_timeout = htons(fm->hard_timeout); ofm->priority = htons(fm->cr.priority); @@ -1231,6 +1232,16 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs, return 0; } +/* Returns 'count' unchanged except that UINT64_MAX becomes 0. + * + * We use this in situations where OVS internally uses UINT64_MAX to mean + * "value unknown" but OpenFlow 1.0 does not define any unknown value. */ +static uint64_t +unknown_to_zero(uint64_t count) +{ + return count != UINT64_MAX ? count : 0; +} + /* Appends an OFPST_FLOW or NXST_FLOW reply that contains the data in 'fs' to * those already present in the list of ofpbufs in 'replies'. 'replies' should * have been initialized with ofputil_start_stats_reply(). */ @@ -1258,8 +1269,10 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs, ofs->hard_timeout = htons(fs->hard_timeout); memset(ofs->pad2, 0, sizeof ofs->pad2); put_32aligned_be64(&ofs->cookie, fs->cookie); - put_32aligned_be64(&ofs->packet_count, htonll(fs->packet_count)); - put_32aligned_be64(&ofs->byte_count, htonll(fs->byte_count)); + put_32aligned_be64(&ofs->packet_count, + htonll(unknown_to_zero(fs->packet_count))); + put_32aligned_be64(&ofs->byte_count, + htonll(unknown_to_zero(fs->byte_count))); memcpy(ofs->actions, fs->actions, act_len); } else if (osm->type == htons(OFPST_VENDOR)) { struct nx_flow_stats *nfs; @@ -1303,8 +1316,10 @@ ofputil_encode_aggregate_stats_reply( struct ofp_aggregate_stats_reply *asr; asr = ofputil_make_stats_reply(sizeof *asr, request, &msg); - put_32aligned_be64(&asr->packet_count, htonll(stats->packet_count)); - put_32aligned_be64(&asr->byte_count, htonll(stats->byte_count)); + put_32aligned_be64(&asr->packet_count, + htonll(unknown_to_zero(stats->packet_count))); + put_32aligned_be64(&asr->byte_count, + htonll(unknown_to_zero(stats->byte_count))); asr->flow_count = htonl(stats->flow_count); } else if (request->type == htons(OFPST_VENDOR)) { struct nx_aggregate_stats_reply *nasr; @@ -1398,8 +1413,8 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr, ofr->duration_sec = htonl(fr->duration_sec); ofr->duration_nsec = htonl(fr->duration_nsec); ofr->idle_timeout = htons(fr->idle_timeout); - ofr->packet_count = htonll(fr->packet_count); - ofr->byte_count = htonll(fr->byte_count); + ofr->packet_count = htonll(unknown_to_zero(fr->packet_count)); + ofr->byte_count = htonll(unknown_to_zero(fr->byte_count)); } else if (flow_format == NXFF_NXM) { struct nx_flow_removed *nfr; int match_len; @@ -1927,38 +1942,11 @@ make_echo_reply(const struct ofp_header *rq) return out; } -static int -check_action_exact_len(const union ofp_action *a, unsigned int len, - unsigned int required_len) -{ - if (len != required_len) { - VLOG_WARN_RL(&bad_ofmsg_rl, "action %"PRIu16" has invalid length " - "%"PRIu16" (must be %u)\n", - ntohs(a->type), ntohs(a->header.len), required_len); - return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN); - } - return 0; -} - -static int -check_nx_action_exact_len(const struct nx_action_header *a, - unsigned int len, unsigned int required_len) -{ - if (len != required_len) { - VLOG_WARN_RL(&bad_ofmsg_rl, - "Nicira action %"PRIu16" has invalid length %"PRIu16" " - "(must be %u)\n", - ntohs(a->subtype), ntohs(a->len), required_len); - return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN); - } - return 0; -} - /* 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. */ -static int -check_output_port(uint16_t port, int max_ports) +int +ofputil_check_output_port(uint16_t port, int max_ports) { switch (port) { case OFPP_IN_PORT: @@ -1974,208 +1962,272 @@ check_output_port(uint16_t port, int max_ports) if (port < max_ports) { return 0; } - VLOG_WARN_RL(&bad_ofmsg_rl, "unknown output port %x", port); return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_OUT_PORT); } } -/* Checks that 'action' is a valid OFPAT_ENQUEUE 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. */ static int -check_enqueue_action(const union ofp_action *a, unsigned int len, - int max_ports) +check_resubmit_table(const struct nx_action_resubmit *nar) { - const struct ofp_action_enqueue *oae; - uint16_t port; - int error; - - error = check_action_exact_len(a, len, 16); - if (error) { - return error; - } - - oae = (const struct ofp_action_enqueue *) a; - port = ntohs(oae->port); - if (port < max_ports || port == OFPP_IN_PORT) { - return 0; + if (nar->pad[0] || nar->pad[1] || nar->pad[2]) { + return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT); } - VLOG_WARN_RL(&bad_ofmsg_rl, "unknown enqueue port %x", port); - return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_OUT_PORT); + return 0; } -static int -check_nicira_action(const union ofp_action *a, unsigned int len, - const struct flow *flow) +int +validate_actions(const union ofp_action *actions, size_t n_actions, + const struct flow *flow, int max_ports) { - const struct nx_action_header *nah; - int subtype; - int error; + const union ofp_action *a; + size_t left; - if (len < 16) { - VLOG_WARN_RL(&bad_ofmsg_rl, - "Nicira vendor action only %u bytes", len); - return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN); - } - nah = (const struct nx_action_header *) a; + OFPUTIL_ACTION_FOR_EACH (a, left, actions, n_actions) { + uint16_t port; + int error; + int code; - subtype = ntohs(nah->subtype); - if (subtype > TYPE_MAXIMUM(enum nx_action_subtype)) { - /* This is necessary because enum nx_action_subtype may be an - * 8-bit type, so the cast below throws away the top 8 bits. */ - return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR_TYPE); - } + code = ofputil_decode_action(a); + if (code < 0) { + char *msg; - switch ((enum nx_action_subtype) subtype) { - case NXAST_RESUBMIT: - case NXAST_SET_TUNNEL: - case NXAST_SET_QUEUE: - case NXAST_POP_QUEUE: - return check_nx_action_exact_len(nah, len, 16); + 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); - case NXAST_REG_MOVE: - error = check_nx_action_exact_len(nah, len, - sizeof(struct nx_action_reg_move)); - if (error) { return error; } - return nxm_check_reg_move((const struct nx_action_reg_move *) a, flow); - case NXAST_REG_LOAD: - error = check_nx_action_exact_len(nah, len, - sizeof(struct nx_action_reg_load)); - if (error) { - return error; - } - return nxm_check_reg_load((const struct nx_action_reg_load *) a, flow); + error = 0; + switch ((enum ofputil_action_code) code) { + case OFPUTIL_OFPAT_OUTPUT: + error = ofputil_check_output_port(ntohs(a->output.port), + max_ports); + break; - case NXAST_NOTE: - return 0; + case OFPUTIL_OFPAT_SET_VLAN_VID: + if (a->vlan_vid.vlan_vid & ~htons(0xfff)) { + error = ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT); + } + break; - case NXAST_SET_TUNNEL64: - return check_nx_action_exact_len( - nah, len, sizeof(struct nx_action_set_tunnel64)); + case OFPUTIL_OFPAT_SET_VLAN_PCP: + if (a->vlan_pcp.vlan_pcp & ~7) { + error = ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT); + } + break; - case NXAST_MULTIPATH: - error = check_nx_action_exact_len( - nah, len, sizeof(struct nx_action_multipath)); - if (error) { - return error; + case OFPUTIL_OFPAT_ENQUEUE: + port = ntohs(((const struct ofp_action_enqueue *) a)->port); + if (port >= max_ports && port != OFPP_IN_PORT) { + error = ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_OUT_PORT); + } + break; + + case OFPUTIL_NXAST_REG_MOVE: + error = nxm_check_reg_move((const struct nx_action_reg_move *) a, + flow); + break; + + case OFPUTIL_NXAST_REG_LOAD: + error = nxm_check_reg_load((const struct nx_action_reg_load *) a, + flow); + break; + + case OFPUTIL_NXAST_MULTIPATH: + error = multipath_check((const struct nx_action_multipath *) a, + flow); + break; + + case OFPUTIL_NXAST_AUTOPATH: + error = autopath_check((const struct nx_action_autopath *) a, + flow); + break; + + case OFPUTIL_NXAST_BUNDLE: + case OFPUTIL_NXAST_BUNDLE_LOAD: + error = bundle_check((const struct nx_action_bundle *) a, + max_ports, flow); + break; + + case OFPUTIL_NXAST_RESUBMIT_TABLE: + error = check_resubmit_table( + (const struct nx_action_resubmit *) a); + break; + + case OFPUTIL_OFPAT_STRIP_VLAN: + case OFPUTIL_OFPAT_SET_NW_SRC: + case OFPUTIL_OFPAT_SET_NW_DST: + case OFPUTIL_OFPAT_SET_NW_TOS: + case OFPUTIL_OFPAT_SET_TP_SRC: + case OFPUTIL_OFPAT_SET_TP_DST: + case OFPUTIL_OFPAT_SET_DL_SRC: + case OFPUTIL_OFPAT_SET_DL_DST: + case OFPUTIL_NXAST_RESUBMIT: + case OFPUTIL_NXAST_SET_TUNNEL: + case OFPUTIL_NXAST_SET_QUEUE: + case OFPUTIL_NXAST_POP_QUEUE: + case OFPUTIL_NXAST_NOTE: + case OFPUTIL_NXAST_SET_TUNNEL64: + break; } - return multipath_check((const struct nx_action_multipath *) a); - case NXAST_AUTOPATH: - error = check_nx_action_exact_len( - nah, len, sizeof(struct nx_action_autopath)); 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); return error; } - return autopath_check((const struct nx_action_autopath *) a); - - case NXAST_SNAT__OBSOLETE: - case NXAST_DROP_SPOOFED_ARP__OBSOLETE: - default: - VLOG_WARN_RL(&bad_ofmsg_rl, - "unknown Nicira vendor action subtype %d", subtype); - return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR_TYPE); } + 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 0; } -static int -check_action(const union ofp_action *a, unsigned int len, - const struct flow *flow, int max_ports) +struct ofputil_action { + int code; + unsigned int min_len; + unsigned int max_len; +}; + +static const struct ofputil_action action_bad_type + = { -OFP_MKERR(OFPET_BAD_ACTION, 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 }; +static const struct ofputil_action action_bad_vendor + = { -OFP_MKERR(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR), 0, UINT_MAX }; + +static const struct ofputil_action * +ofputil_decode_ofpat_action(const union ofp_action *a) { enum ofp_action_type type = ntohs(a->type); - int error; switch (type) { - case OFPAT_OUTPUT: - error = check_action_exact_len(a, len, 8); - if (error) { - return error; - } - return check_output_port(ntohs(a->output.port), max_ports); - - case OFPAT_SET_VLAN_VID: - error = check_action_exact_len(a, len, 8); - if (error) { - return error; +#define OFPAT_ACTION(ENUM, TYPE) \ + case ENUM: { \ + static const struct ofputil_action action = { \ + OFPUTIL_##ENUM, sizeof(TYPE), sizeof(TYPE) \ + }; \ + return &action; \ } - if (a->vlan_vid.vlan_vid & ~htons(0xfff)) { - return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT); - } - return 0; - - case OFPAT_SET_VLAN_PCP: - error = check_action_exact_len(a, len, 8); - if (error) { - return error; - } - if (a->vlan_pcp.vlan_pcp & ~7) { - return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT); - } - return 0; - - case OFPAT_STRIP_VLAN: - case OFPAT_SET_NW_SRC: - case OFPAT_SET_NW_DST: - case OFPAT_SET_NW_TOS: - case OFPAT_SET_TP_SRC: - case OFPAT_SET_TP_DST: - return check_action_exact_len(a, len, 8); - - case OFPAT_SET_DL_SRC: - case OFPAT_SET_DL_DST: - return check_action_exact_len(a, len, 16); + OFPAT_ACTION(OFPAT_OUTPUT, struct ofp_action_output); + OFPAT_ACTION(OFPAT_SET_VLAN_VID, struct ofp_action_vlan_vid); + OFPAT_ACTION(OFPAT_SET_VLAN_PCP, struct ofp_action_vlan_pcp); + OFPAT_ACTION(OFPAT_STRIP_VLAN, struct ofp_action_header); + OFPAT_ACTION(OFPAT_SET_DL_SRC, struct ofp_action_dl_addr); + OFPAT_ACTION(OFPAT_SET_DL_DST, struct ofp_action_dl_addr); + OFPAT_ACTION(OFPAT_SET_NW_SRC, struct ofp_action_nw_addr); + OFPAT_ACTION(OFPAT_SET_NW_DST, struct ofp_action_nw_addr); + OFPAT_ACTION(OFPAT_SET_NW_TOS, struct ofp_action_nw_tos); + OFPAT_ACTION(OFPAT_SET_TP_SRC, struct ofp_action_tp_port); + OFPAT_ACTION(OFPAT_SET_TP_DST, struct ofp_action_tp_port); + OFPAT_ACTION(OFPAT_ENQUEUE, struct ofp_action_enqueue); +#undef OFPAT_ACTION case OFPAT_VENDOR: - return (a->vendor.vendor == htonl(NX_VENDOR_ID) - ? check_nicira_action(a, len, flow) - : ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR)); + default: + return &action_bad_type; + } +} - case OFPAT_ENQUEUE: - return check_enqueue_action(a, len, max_ports); +static const struct ofputil_action * +ofputil_decode_nxast_action(const union ofp_action *a) +{ + const struct nx_action_header *nah = (const struct nx_action_header *) a; + enum nx_action_subtype subtype = ntohs(nah->subtype); + + switch (subtype) { +#define NXAST_ACTION(ENUM, TYPE, EXTENSIBLE) \ + case ENUM: { \ + static const struct ofputil_action action = { \ + OFPUTIL_##ENUM, \ + sizeof(TYPE), \ + EXTENSIBLE ? UINT_MAX : sizeof(TYPE) \ + }; \ + return &action; \ + } + NXAST_ACTION(NXAST_RESUBMIT, struct nx_action_resubmit, false); + NXAST_ACTION(NXAST_SET_TUNNEL, struct nx_action_set_tunnel, false); + NXAST_ACTION(NXAST_SET_QUEUE, struct nx_action_set_queue, false); + NXAST_ACTION(NXAST_POP_QUEUE, struct nx_action_pop_queue, false); + NXAST_ACTION(NXAST_REG_MOVE, struct nx_action_reg_move, false); + NXAST_ACTION(NXAST_REG_LOAD, struct nx_action_reg_load, false); + NXAST_ACTION(NXAST_NOTE, struct nx_action_note, true); + NXAST_ACTION(NXAST_SET_TUNNEL64, struct nx_action_set_tunnel64, false); + NXAST_ACTION(NXAST_MULTIPATH, struct nx_action_multipath, false); + NXAST_ACTION(NXAST_AUTOPATH, struct nx_action_autopath, false); + NXAST_ACTION(NXAST_BUNDLE, struct nx_action_bundle, true); + NXAST_ACTION(NXAST_BUNDLE_LOAD, struct nx_action_bundle, true); + NXAST_ACTION(NXAST_RESUBMIT_TABLE, struct nx_action_resubmit, false); +#undef NXAST_ACTION + case NXAST_SNAT__OBSOLETE: + case NXAST_DROP_SPOOFED_ARP__OBSOLETE: default: - VLOG_WARN_RL(&bad_ofmsg_rl, "unknown action type %d", (int) type); - return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_TYPE); + return &action_bad_type; } } +/* 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()). + * + * 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 + * longer than the amount of space allocated to 'a'). + * + * This function verifies that 'a''s length is correct for the type of action + * that it represents. */ int -validate_actions(const union ofp_action *actions, size_t n_actions, - const struct flow *flow, int max_ports) +ofputil_decode_action(const union ofp_action *a) { - size_t i; - - for (i = 0; i < n_actions; ) { - const union ofp_action *a = &actions[i]; - unsigned int len = ntohs(a->header.len); - unsigned int n_slots = len / OFP_ACTION_ALIGN; - unsigned int slots_left = &actions[n_actions] - a; - int error; + const struct ofputil_action *action; + uint16_t len = ntohs(a->header.len); - if (n_slots > slots_left) { - VLOG_WARN_RL(&bad_ofmsg_rl, - "action requires %u slots but only %u remain", - n_slots, slots_left); - return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN); - } else if (!len) { - VLOG_WARN_RL(&bad_ofmsg_rl, "action has invalid length 0"); - return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN); - } else if (len % OFP_ACTION_ALIGN) { - VLOG_WARN_RL(&bad_ofmsg_rl, "action length %u is not a multiple " - "of %d", len, OFP_ACTION_ALIGN); - return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN); + if (a->type != htons(OFPAT_VENDOR)) { + action = ofputil_decode_ofpat_action(a); + } else { + 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); + } + action = ofputil_decode_nxast_action(a); + break; + default: + action = &action_bad_vendor; + break; } + } - error = check_action(a, len, flow, max_ports); - if (error) { - return error; - } - i += n_slots; + return (len >= action->min_len && len <= action->max_len + ? action->code + : -ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN)); +} + +/* Parses 'a' and returns its type as an OFPUTIL_OFPAT_* or OFPUTIL_NXAST_* + * constant. The caller must have already validated that 'a' is a valid action + * understood by Open vSwitch (e.g. by a previous successful call to + * ofputil_decode_action()). */ +enum ofputil_action_code +ofputil_decode_action_unsafe(const union ofp_action *a) +{ + const struct ofputil_action *action; + + if (a->type != htons(OFPAT_VENDOR)) { + action = ofputil_decode_ofpat_action(a); + } else { + action = ofputil_decode_nxast_action(a); } - return 0; + + return action->code; } /* Returns true if 'action' outputs to 'port', false otherwise. */ @@ -2192,30 +2244,6 @@ action_outputs_to_port(const union ofp_action *action, ovs_be16 port) } } -/* The set of actions must either come from a trusted source or have been - * previously validated with validate_actions(). */ -const union ofp_action * -actions_first(struct actions_iterator *iter, - const union ofp_action *oa, size_t n_actions) -{ - iter->pos = oa; - iter->end = oa + n_actions; - return actions_next(iter); -} - -const union ofp_action * -actions_next(struct actions_iterator *iter) -{ - if (iter->pos != iter->end) { - const union ofp_action *a = iter->pos; - unsigned int len = ntohs(a->header.len); - iter->pos += len / OFP_ACTION_ALIGN; - return a; - } else { - return NULL; - } -} - /* "Normalizes" the wildcards in 'rule'. That means: * * 1. If the type of level N is known, then only the valid fields for that