X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=lib%2Fofp-util.c;h=5c4336a812feadadce42cef4e1feb6918ea06fb8;hb=8321fb9c570bd25bb8925e7b5116ab90662113b5;hp=16a3eeeeb20ecee9b5136f4031fc047b0e9caae2;hpb=3f09c339f7e5e3d74bb06600b7f05c8efccbf132;p=openvswitch diff --git a/lib/ofp-util.c b/lib/ofp-util.c index 16a3eeee..5c4336a8 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -18,25 +18,26 @@ #include "ofp-print.h" #include #include +#include "byte-order.h" +#include "nx-match.h" #include "ofp-util.h" #include "ofpbuf.h" #include "packets.h" #include "random.h" -#include "xtoxll.h" - -#define THIS_MODULE VLM_ofp_util #include "vlog.h" +VLOG_DEFINE_THIS_MODULE(ofp_util); + /* Rate limit for OpenFlow message parse errors. These always indicate a bug * in the peer and so there's not much point in showing a lot of them. */ static struct vlog_rate_limit bad_ofmsg_rl = VLOG_RATE_LIMIT_INIT(1, 5); -/* XXX we should really use consecutive xids to avoid probabilistic - * failures. */ -static inline uint32_t +/* Returns a transaction ID to use for an outgoing OpenFlow message. */ +static ovs_be32 alloc_xid(void) { - return random_uint32(); + static uint32_t next_xid = 1; + return htonl(next_xid++); } /* Allocates and stores in '*bufferp' a new ofpbuf with a size of @@ -59,6 +60,14 @@ make_openflow(size_t openflow_len, uint8_t type, struct ofpbuf **bufferp) return put_openflow_xid(openflow_len, type, alloc_xid(), *bufferp); } +/* Similar to make_openflow() but creates a Nicira vendor extension message + * with the specific 'subtype'. 'subtype' should be in host byte order. */ +void * +make_nxmsg(size_t openflow_len, uint32_t subtype, struct ofpbuf **bufferp) +{ + return make_nxmsg_xid(openflow_len, subtype, alloc_xid(), bufferp); +} + /* Allocates and stores in '*bufferp' a new ofpbuf with a size of * 'openflow_len', starting with an OpenFlow header with the given 'type' and * transaction id 'xid'. Allocated bytes beyond the header, if any, are @@ -73,13 +82,26 @@ make_openflow(size_t openflow_len, uint8_t type, struct ofpbuf **bufferp) * * Returns the header. */ void * -make_openflow_xid(size_t openflow_len, uint8_t type, uint32_t xid, +make_openflow_xid(size_t openflow_len, uint8_t type, ovs_be32 xid, struct ofpbuf **bufferp) { *bufferp = ofpbuf_new(openflow_len); return put_openflow_xid(openflow_len, type, xid, *bufferp); } +/* Similar to make_openflow_xid() but creates a Nicira vendor extension message + * with the specific 'subtype'. 'subtype' should be in host byte order. */ +void * +make_nxmsg_xid(size_t openflow_len, uint32_t subtype, ovs_be32 xid, + struct ofpbuf **bufferp) +{ + struct nicira_header *nxh = make_openflow_xid(openflow_len, OFPT_VENDOR, + xid, bufferp); + nxh->vendor = htonl(NX_VENDOR_ID); + nxh->subtype = htonl(subtype); + return nxh; +} + /* Appends 'openflow_len' bytes to 'buffer', starting with an OpenFlow header * with the given 'type' and an arbitrary transaction id. Allocated bytes * beyond the header, if any, are zeroed. @@ -105,7 +127,7 @@ put_openflow(size_t openflow_len, uint8_t type, struct ofpbuf *buffer) * * Returns the header. */ void * -put_openflow_xid(size_t openflow_len, uint8_t type, uint32_t xid, +put_openflow_xid(size_t openflow_len, uint8_t type, ovs_be32 xid, struct ofpbuf *buffer) { struct ofp_header *oh; @@ -125,14 +147,14 @@ put_openflow_xid(size_t openflow_len, uint8_t type, uint32_t xid, /* Updates the 'length' field of the OpenFlow message in 'buffer' to * 'buffer->size'. */ void -update_openflow_length(struct ofpbuf *buffer) +update_openflow_length(struct ofpbuf *buffer) { struct ofp_header *oh = ofpbuf_at_assert(buffer, 0, sizeof *oh); - oh->length = htons(buffer->size); + oh->length = htons(buffer->size); } struct ofpbuf * -make_flow_mod(uint16_t command, const flow_t *flow, size_t actions_len) +make_flow_mod(uint16_t command, const struct flow *flow, size_t actions_len) { struct ofp_flow_mod *ofm; size_t size = sizeof *ofm + actions_len; @@ -161,7 +183,7 @@ make_flow_mod(uint16_t command, const flow_t *flow, size_t actions_len) } struct ofpbuf * -make_add_flow(const flow_t *flow, uint32_t buffer_id, +make_add_flow(const struct flow *flow, uint32_t buffer_id, uint16_t idle_timeout, size_t actions_len) { struct ofpbuf *out = make_flow_mod(OFPFC_ADD, flow, actions_len); @@ -173,7 +195,7 @@ make_add_flow(const flow_t *flow, uint32_t buffer_id, } struct ofpbuf * -make_del_flow(const flow_t *flow) +make_del_flow(const struct flow *flow) { struct ofpbuf *out = make_flow_mod(OFPFC_DELETE_STRICT, flow, 0); struct ofp_flow_mod *ofm = out->data; @@ -182,18 +204,23 @@ make_del_flow(const flow_t *flow) } struct ofpbuf * -make_add_simple_flow(const flow_t *flow, +make_add_simple_flow(const struct flow *flow, uint32_t buffer_id, uint16_t out_port, uint16_t idle_timeout) { - struct ofp_action_output *oao; - struct ofpbuf *buffer = make_add_flow(flow, buffer_id, idle_timeout, - sizeof *oao); - oao = ofpbuf_put_zeros(buffer, sizeof *oao); - oao->type = htons(OFPAT_OUTPUT); - oao->len = htons(sizeof *oao); - oao->port = htons(out_port); - return buffer; + if (out_port != OFPP_NONE) { + struct ofp_action_output *oao; + struct ofpbuf *buffer; + + buffer = make_add_flow(flow, buffer_id, idle_timeout, sizeof *oao); + oao = ofpbuf_put_zeros(buffer, sizeof *oao); + oao->type = htons(OFPAT_OUTPUT); + oao->len = htons(sizeof *oao); + oao->port = htons(out_port); + return buffer; + } else { + return make_add_flow(flow, buffer_id, idle_timeout, 0); + } } struct ofpbuf * @@ -259,12 +286,16 @@ struct ofpbuf * make_buffered_packet_out(uint32_t buffer_id, 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(NULL, buffer_id, in_port, - (struct ofp_action_header *) &action, 1); + 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. */ @@ -277,7 +308,7 @@ make_echo_request(void) rq->version = OFP_VERSION; rq->type = OFPT_ECHO_REQUEST; rq->length = htons(sizeof *rq); - rq->xid = 0; + rq->xid = htonl(0); return out; } @@ -294,7 +325,7 @@ make_echo_reply(const struct ofp_header *rq) } static int -check_message_type(uint8_t got_type, uint8_t want_type) +check_message_type(uint8_t got_type, uint8_t want_type) { if (got_type != want_type) { char *want_type_name = ofp_message_type_to_string(want_type); @@ -385,50 +416,6 @@ check_ofp_message_array(const struct ofp_header *msg, uint8_t type, return 0; } -int -check_ofp_packet_out(const struct ofp_header *oh, struct ofpbuf *data, - int *n_actionsp, int max_ports) -{ - const struct ofp_packet_out *opo; - unsigned int actions_len, n_actions; - size_t extra; - int error; - - *n_actionsp = 0; - error = check_ofp_message_array(oh, OFPT_PACKET_OUT, - sizeof *opo, 1, &extra); - if (error) { - return error; - } - opo = (const struct ofp_packet_out *) oh; - - actions_len = ntohs(opo->actions_len); - if (actions_len > extra) { - VLOG_WARN_RL(&bad_ofmsg_rl, "packet-out claims %u bytes of actions " - "but message has room for only %zu bytes", - actions_len, extra); - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); - } - if (actions_len % sizeof(union ofp_action)) { - VLOG_WARN_RL(&bad_ofmsg_rl, "packet-out claims %u bytes of actions, " - "which is not a multiple of %zu", - actions_len, sizeof(union ofp_action)); - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); - } - - n_actions = actions_len / sizeof(union ofp_action); - error = validate_actions((const union ofp_action *) opo->actions, - n_actions, max_ports); - if (error) { - return error; - } - - data->data = (void *) &opo->actions[n_actions]; - data->size = extra - actions_len; - *n_actionsp = n_actions; - return 0; -} - const struct ofp_flow_stats * flow_stats_first(struct flow_stats_iterator *iter, const struct ofp_stats_reply *osr) @@ -474,9 +461,6 @@ flow_stats_next(struct flow_stats_iterator *iter) return fs; } -/* Alignment of ofp_actions. */ -#define ACTION_ALIGNMENT 8 - static int check_action_exact_len(const union ofp_action *a, unsigned int len, unsigned int required_len) @@ -541,9 +525,11 @@ check_enqueue_action(const union ofp_action *a, unsigned int len, } static int -check_nicira_action(const union ofp_action *a, unsigned int len) +check_nicira_action(const union ofp_action *a, unsigned int len, + const struct flow *flow) { const struct nx_action_header *nah; + int error; if (len < 16) { VLOG_DBG_RL(&bad_ofmsg_rl, @@ -555,14 +541,38 @@ check_nicira_action(const union ofp_action *a, unsigned int len) switch (ntohs(nah->subtype)) { case NXAST_RESUBMIT: case NXAST_SET_TUNNEL: + case NXAST_DROP_SPOOFED_ARP: + case NXAST_SET_QUEUE: + case NXAST_POP_QUEUE: return check_action_exact_len(a, len, 16); + + case NXAST_REG_MOVE: + error = check_action_exact_len(a, len, + sizeof(struct nx_action_reg_move)); + if (error) { + return error; + } + return nxm_check_reg_move((const struct nx_action_reg_move *) a, flow); + + case NXAST_REG_LOAD: + error = check_action_exact_len(a, len, + sizeof(struct nx_action_reg_load)); + if (error) { + return error; + } + return nxm_check_reg_load((const struct nx_action_reg_load *) a, flow); + + case NXAST_NOTE: + return 0; + default: return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR_TYPE); } } static int -check_action(const union ofp_action *a, unsigned int len, int max_ports) +check_action(const union ofp_action *a, unsigned int len, + const struct flow *flow, int max_ports) { int error; @@ -575,7 +585,25 @@ check_action(const union ofp_action *a, unsigned int len, int max_ports) 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; + } + 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_vid.vlan_vid & ~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: @@ -590,7 +618,7 @@ check_action(const union ofp_action *a, unsigned int len, int max_ports) case OFPAT_VENDOR: return (a->vendor.vendor == htonl(NX_VENDOR_ID) - ? check_nicira_action(a, len) + ? check_nicira_action(a, len, flow) : ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR)); case OFPAT_ENQUEUE: @@ -605,13 +633,14 @@ check_action(const union ofp_action *a, unsigned int len, int max_ports) int validate_actions(const union ofp_action *actions, size_t n_actions, - int max_ports) + const struct flow *flow, int max_ports) { - const union ofp_action *a; + size_t i; - for (a = actions; a < &actions[n_actions]; ) { + for (i = 0; i < n_actions; ) { + const union ofp_action *a = &actions[i]; unsigned int len = ntohs(a->header.len); - unsigned int n_slots = len / ACTION_ALIGNMENT; + unsigned int n_slots = len / OFP_ACTION_ALIGN; unsigned int slots_left = &actions[n_actions] - a; int error; @@ -623,17 +652,17 @@ validate_actions(const union ofp_action *actions, size_t n_actions, } else if (!len) { VLOG_DBG_RL(&bad_ofmsg_rl, "action has invalid length 0"); return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN); - } else if (len % ACTION_ALIGNMENT) { + } else if (len % OFP_ACTION_ALIGN) { VLOG_DBG_RL(&bad_ofmsg_rl, "action length %u is not a multiple " - "of %d", len, ACTION_ALIGNMENT); + "of %d", len, OFP_ACTION_ALIGN); return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN); } - error = check_action(a, len, max_ports); + error = check_action(a, len, flow, max_ports); if (error) { return error; } - a += n_slots; + i += n_slots; } return 0; } @@ -667,10 +696,10 @@ actions_first(struct actions_iterator *iter, const union ofp_action * actions_next(struct actions_iterator *iter) { - if (iter->pos < iter->end) { + if (iter->pos != iter->end) { const union ofp_action *a = iter->pos; unsigned int len = ntohs(a->header.len); - iter->pos += len / ACTION_ALIGNMENT; + iter->pos += len / OFP_ACTION_ALIGN; return a; } else { return NULL; @@ -680,7 +709,8 @@ actions_next(struct actions_iterator *iter) void normalize_match(struct ofp_match *m) { - enum { OFPFW_NW = OFPFW_NW_SRC_MASK | OFPFW_NW_DST_MASK | OFPFW_NW_PROTO }; + enum { OFPFW_NW = (OFPFW_NW_SRC_MASK | OFPFW_NW_DST_MASK | OFPFW_NW_PROTO + | OFPFW_NW_TOS) }; enum { OFPFW_TP = OFPFW_TP_SRC | OFPFW_TP_DST }; uint32_t wc; @@ -791,3 +821,133 @@ ofp_match_to_literal_string(const struct ofp_match *match) ntohs(match->tp_src), ntohs(match->tp_dst)); } + +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; + } +} + +/* 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 * +make_ofp_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 = ofpbuf_put_uninit(buf, sizeof *nve); + nve->vendor = htonl(vendor_id); + nve->type = htons(type); + nve->code = htons(code); + } + + if (len) { + ofpbuf_put(buf, data, len); + } + + return buf; +} + +/* Attempts to pull 'actions_len' bytes from the front of 'b'. Returns 0 if + * successful, otherwise an OpenFlow error. + * + * If successful, the first action is stored in '*actionsp' and the number of + * "union ofp_action" size elements into '*n_actionsp'. Otherwise NULL and 0 + * are stored, respectively. + * + * This function does not check that the actions are valid (the caller should + * do so, with validate_actions()). The caller is also responsible for making + * sure that 'b->data' is initially aligned appropriately for "union + * ofp_action". */ +int +ofputil_pull_actions(struct ofpbuf *b, unsigned int actions_len, + union ofp_action **actionsp, size_t *n_actionsp) +{ + if (actions_len % OFP_ACTION_ALIGN != 0) { + VLOG_DBG_RL(&bad_ofmsg_rl, "OpenFlow message actions length %u " + "is not a multiple of %d", actions_len, OFP_ACTION_ALIGN); + goto error; + } + + *actionsp = ofpbuf_try_pull(b, actions_len); + if (*actionsp == NULL) { + VLOG_DBG_RL(&bad_ofmsg_rl, "OpenFlow message actions length %u " + "exceeds remaining message length (%zu)", + actions_len, b->size); + goto error; + } + + *n_actionsp = actions_len / OFP_ACTION_ALIGN; + return 0; + +error: + *actionsp = NULL; + *n_actionsp = 0; + return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); +}