X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=lib%2Fofp-util.c;h=d7bc0ee450d43b4465f40f480a354c917f547de1;hb=eb5f3e933b0922d369ad839b2afa1cd7ab764b6e;hp=d8d3ced58cebd15ab632e4ee58dd482284c63cb6;hpb=ae412e7dd89489911a32c6723d4109c6d1de3a38;p=openvswitch diff --git a/lib/ofp-util.c b/lib/ofp-util.c index d8d3ced5..d7bc0ee4 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -18,14 +18,15 @@ #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 "vlog.h" -#include "xtoxll.h" -VLOG_DEFINE_THIS_MODULE(ofp_util) +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. */ @@ -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 @@ -80,6 +89,19 @@ make_openflow_xid(size_t openflow_len, uint8_t type, uint32_t xid, 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, uint32_t 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. @@ -394,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) @@ -483,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) @@ -550,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, @@ -568,13 +545,34 @@ check_nicira_action(const union ofp_action *a, unsigned int len) case NXAST_SET_QUEUE: case NXAST_POP_QUEUE: return check_action_exact_len(a, len, 16); + + case NXAST_REG_MOVE: + error = check_action_exact_len(a, len, + sizeof(struct nx_action_reg_move)); + if (error) { + return error; + } + return nxm_check_reg_move((const struct nx_action_reg_move *) a, flow); + + case NXAST_REG_LOAD: + error = check_action_exact_len(a, len, + sizeof(struct nx_action_reg_load)); + if (error) { + return error; + } + return nxm_check_reg_load((const struct nx_action_reg_load *) a, flow); + + case NXAST_NOTE: + return 0; + default: return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR_TYPE); } } static int -check_action(const union ofp_action *a, unsigned int len, int max_ports) +check_action(const union ofp_action *a, unsigned int len, + const struct flow *flow, int max_ports) { int error; @@ -587,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: @@ -602,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: @@ -617,14 +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) { size_t i; for (i = 0; i < n_actions; ) { const union ofp_action *a = &actions[i]; unsigned int len = ntohs(a->header.len); - unsigned int n_slots = len / ACTION_ALIGNMENT; + unsigned int n_slots = len / OFP_ACTION_ALIGN; unsigned int slots_left = &actions[n_actions] - a; int error; @@ -636,13 +652,13 @@ validate_actions(const union ofp_action *actions, size_t n_actions, } else if (!len) { VLOG_DBG_RL(&bad_ofmsg_rl, "action has invalid length 0"); return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN); - } else if (len % ACTION_ALIGNMENT) { + } else if (len % OFP_ACTION_ALIGN) { VLOG_DBG_RL(&bad_ofmsg_rl, "action length %u is not a multiple " - "of %d", len, ACTION_ALIGNMENT); + "of %d", len, OFP_ACTION_ALIGN); return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN); } - error = check_action(a, len, max_ports); + error = check_action(a, len, flow, max_ports); if (error) { return error; } @@ -683,7 +699,7 @@ actions_next(struct actions_iterator *iter) if (iter->pos != iter->end) { const union ofp_action *a = iter->pos; unsigned int len = ntohs(a->header.len); - iter->pos += len / ACTION_ALIGNMENT; + iter->pos += len / OFP_ACTION_ALIGN; return a; } else { return NULL; @@ -693,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; @@ -804,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; + uint32_t 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); +}