X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=lib%2Fofp-util.c;h=1125b83fc17752de195b11ee6479136da958fb45;hb=e868fb3d322f5c46385f1fc6db5bb1ab33f90305;hp=f0e0c69eb9cb8e8df457accf786701be6e4e3296;hpb=5c47debbf0518135d75a3426eb136b1e01eb0845;p=openvswitch diff --git a/lib/ofp-util.c b/lib/ofp-util.c index f0e0c69e..1125b83f 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, 2010 Nicira Networks. + * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +16,15 @@ #include #include "ofp-print.h" +#include #include #include #include "byte-order.h" #include "classifier.h" +#include "dynamic-string.h" +#include "multipath.h" #include "nx-match.h" +#include "ofp-errors.h" #include "ofp-util.h" #include "ofpbuf.h" #include "packets.h" @@ -120,6 +124,10 @@ ofputil_cls_rule_from_match(const struct ofp_match *match, /* Initialize most of rule->wc. */ flow_wildcards_init_catchall(wc); wc->wildcards = ofpfw & WC_INVARIANTS; + + /* Wildcard fields that aren't defined by ofp_match or tun_id. */ + wc->wildcards |= (FWW_ARP_SHA | FWW_ARP_THA | FWW_ND_TARGET); + if (ofpfw & OFPFW_NW_TOS) { wc->wildcards |= FWW_NW_TOS; } @@ -128,9 +136,6 @@ ofputil_cls_rule_from_match(const struct ofp_match *match, if (flow_format == NXFF_TUN_ID_FROM_COOKIE && !(ofpfw & NXFW_TUN_ID)) { rule->flow.tun_id = htonll(ntohll(cookie) >> 32); - } else { - wc->wildcards |= FWW_TUN_ID; - rule->flow.tun_id = htonll(0); } if (ofpfw & OFPFW_DL_DST) { @@ -145,7 +150,7 @@ ofputil_cls_rule_from_match(const struct ofp_match *match, rule->flow.nw_dst = match->nw_dst; rule->flow.in_port = (match->in_port == htons(OFPP_LOCAL) ? ODPP_LOCAL : ntohs(match->in_port)); - rule->flow.dl_type = match->dl_type; + rule->flow.dl_type = ofputil_dl_type_from_openflow(match->dl_type); rule->flow.tp_src = match->tp_src; rule->flow.tp_dst = match->tp_dst; memcpy(rule->flow.dl_src, match->dl_src, ETH_ADDR_LEN); @@ -229,7 +234,7 @@ ofputil_cls_rule_to_match(const struct cls_rule *rule, /* Tunnel ID. */ if (flow_format == NXFF_TUN_ID_FROM_COOKIE) { - if (wc->wildcards & FWW_TUN_ID) { + if (wc->tun_id_mask == htonll(0)) { ofpfw |= NXFW_TUN_ID; } else { uint32_t cookie_lo = ntohll(cookie_in); @@ -269,7 +274,7 @@ ofputil_cls_rule_to_match(const struct cls_rule *rule, : rule->flow.in_port); memcpy(match->dl_src, rule->flow.dl_src, ETH_ADDR_LEN); memcpy(match->dl_dst, rule->flow.dl_dst, ETH_ADDR_LEN); - match->dl_type = rule->flow.dl_type; + match->dl_type = ofputil_dl_type_to_openflow(rule->flow.dl_type); match->nw_src = rule->flow.nw_src; match->nw_dst = rule->flow.nw_dst; match->nw_tos = rule->flow.nw_tos; @@ -280,6 +285,27 @@ ofputil_cls_rule_to_match(const struct cls_rule *rule, memset(match->pad2, '\0', sizeof match->pad2); } +/* Given a 'dl_type' value in the format used in struct flow, returns the + * corresponding 'dl_type' value for use in an OpenFlow ofp_match structure. */ +ovs_be16 +ofputil_dl_type_to_openflow(ovs_be16 flow_dl_type) +{ + return (flow_dl_type == htons(FLOW_DL_TYPE_NONE) + ? htons(OFP_DL_TYPE_NOT_ETH_TYPE) + : flow_dl_type); +} + +/* Given a 'dl_type' value in the format used in an OpenFlow ofp_match + * structure, returns the corresponding 'dl_type' value for use in struct + * flow. */ +ovs_be16 +ofputil_dl_type_from_openflow(ovs_be16 ofp_dl_type) +{ + return (ofp_dl_type == htons(OFP_DL_TYPE_NOT_ETH_TYPE) + ? htons(FLOW_DL_TYPE_NONE) + : ofp_dl_type); +} + /* Returns a transaction ID to use for an outgoing OpenFlow message. */ static ovs_be32 alloc_xid(void) @@ -471,7 +497,7 @@ ofputil_decode_nxst_request(const struct ofp_header *oh, }; static const struct ofputil_msg_category nxst_request_category = { - "Nicira extension statistics", + "Nicira extension statistics request", nxst_requests, ARRAY_SIZE(nxst_requests), OFP_MKERR(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE) }; @@ -505,7 +531,7 @@ ofputil_decode_nxst_reply(const struct ofp_header *oh, }; static const struct ofputil_msg_category nxst_reply_category = { - "Nicira extension statistics", + "Nicira extension statistics reply", nxst_replies, ARRAY_SIZE(nxst_replies), OFP_MKERR(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE) }; @@ -825,6 +851,58 @@ regs_fully_wildcarded(const struct flow_wildcards *wc) return true; } +static inline bool +is_nxm_required(const struct cls_rule *rule, bool cookie_support, + ovs_be64 cookie) +{ + const struct flow_wildcards *wc = &rule->wc; + ovs_be32 cookie_hi; + + /* Only NXM supports separately wildcards the Ethernet multicast bit. */ + if (!(wc->wildcards & FWW_DL_DST) != !(wc->wildcards & FWW_ETH_MCAST)) { + return true; + } + + /* Only NXM supports matching ARP hardware addresses. */ + if (!(wc->wildcards & FWW_ARP_SHA) || !(wc->wildcards & FWW_ARP_THA)) { + return true; + } + + /* Only NXM supports matching IPv6 traffic. */ + if (!(wc->wildcards & FWW_DL_TYPE) + && (rule->flow.dl_type == htons(ETH_TYPE_IPV6))) { + return true; + } + + /* Only NXM supports matching registers. */ + if (!regs_fully_wildcarded(wc)) { + return true; + } + + switch (wc->tun_id_mask) { + case CONSTANT_HTONLL(0): + /* Other formats can fully wildcard tun_id. */ + break; + + case CONSTANT_HTONLL(UINT64_MAX): + /* Only NXM supports matching tunnel ID, unless there is a cookie and + * the top 32 bits of the cookie are the desired tunnel ID value. */ + cookie_hi = htonl(ntohll(cookie) >> 32); + if (!cookie_support + || (cookie_hi && cookie_hi != ntohll(rule->flow.tun_id))) { + return true; + } + break; + + default: + /* Only NXM supports partial matches on tunnel ID. */ + return true; + } + + /* Other formats can express this rule. */ + return false; +} + /* Returns the minimum nx_flow_format to use for sending 'rule' to a switch * (e.g. to add or remove a flow). 'cookie_support' should be true if the * command to be sent includes a flow cookie (as OFPT_FLOW_MOD does, for @@ -849,16 +927,9 @@ enum nx_flow_format ofputil_min_flow_format(const struct cls_rule *rule, bool cookie_support, ovs_be64 cookie) { - const struct flow_wildcards *wc = &rule->wc; - ovs_be32 cookie_hi = htonl(ntohll(cookie) >> 32); - - if (!(wc->wildcards & FWW_DL_DST) != !(wc->wildcards & FWW_ETH_MCAST) - || !regs_fully_wildcarded(wc) - || (!(wc->wildcards & FWW_TUN_ID) - && (!cookie_support - || (cookie_hi && cookie_hi != ntohll(rule->flow.tun_id))))) { + if (is_nxm_required(rule, cookie_support, cookie)) { return NXFF_NXM; - } else if (!(wc->wildcards & FWW_TUN_ID)) { + } else if (rule->wc.tun_id_mask != htonll(0)) { return NXFF_TUN_ID_FROM_COOKIE; } else { return NXFF_OPENFLOW10; @@ -1636,9 +1707,23 @@ check_action_exact_len(const union ofp_action *a, unsigned int len, unsigned int required_len) { if (len != required_len) { - VLOG_DBG_RL(&bad_ofmsg_rl, - "action %u has invalid length %"PRIu16" (must be %u)\n", - a->type, ntohs(a->header.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; @@ -1703,8 +1788,8 @@ check_nicira_action(const union ofp_action *a, unsigned int len, int error; if (len < 16) { - VLOG_DBG_RL(&bad_ofmsg_rl, - "Nicira vendor action only %u bytes", len); + 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; @@ -1722,19 +1807,19 @@ check_nicira_action(const union ofp_action *a, unsigned int len, case NXAST_DROP_SPOOFED_ARP: case NXAST_SET_QUEUE: case NXAST_POP_QUEUE: - return check_action_exact_len(a, len, 16); + return check_nx_action_exact_len(nah, len, 16); case NXAST_REG_MOVE: - error = check_action_exact_len(a, len, - sizeof(struct nx_action_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_action_exact_len(a, len, - sizeof(struct nx_action_reg_load)); + error = check_nx_action_exact_len(nah, len, + sizeof(struct nx_action_reg_load)); if (error) { return error; } @@ -1744,11 +1829,22 @@ check_nicira_action(const union ofp_action *a, unsigned int len, return 0; case NXAST_SET_TUNNEL64: - return check_action_exact_len(a, len, - sizeof(struct nx_action_set_tunnel64)); + return check_nx_action_exact_len( + nah, len, sizeof(struct nx_action_set_tunnel64)); + + case NXAST_MULTIPATH: + error = check_nx_action_exact_len( + nah, len, sizeof(struct nx_action_multipath)); + if (error) { + return error; + } + return multipath_check((const struct nx_action_multipath *) a); case NXAST_SNAT__OBSOLETE: default: + VLOG_WARN_RL(&bad_ofmsg_rl, + "unknown Nicira vendor action subtype %"PRIu16, + ntohs(nah->subtype)); return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR_TYPE); } } @@ -1828,16 +1924,16 @@ validate_actions(const union ofp_action *actions, size_t n_actions, int error; if (n_slots > slots_left) { - VLOG_DBG_RL(&bad_ofmsg_rl, - "action requires %u slots but only %u remain", - 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_DBG_RL(&bad_ofmsg_rl, "action has invalid length 0"); + 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_DBG_RL(&bad_ofmsg_rl, "action length %u is not a multiple " - "of %d", 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); } @@ -1951,6 +2047,9 @@ normalize_match(struct ofp_match *m) m->nw_dst &= ofputil_wcbits_to_netmask(wc >> OFPFW_NW_DST_SHIFT); } m->tp_src = m->tp_dst = m->nw_tos = 0; + } else if (m->dl_type == htons(ETH_TYPE_IPV6)) { + /* Don't normalize IPv6 traffic, since OpenFlow doesn't have a + * way to express it. */ } else { /* Network and transport layer fields will always be extracted as * zeros, so we can do an exact-match on those values. */ @@ -2017,6 +2116,18 @@ vendor_code_to_id(uint8_t code) } } +static int +vendor_id_to_code(uint32_t id) +{ + switch (id) { +#define OFPUTIL_VENDOR(NAME, VENDOR_ID) case VENDOR_ID: return NAME; + OFPUTIL_VENDORS +#undef OFPUTIL_VENDOR + default: + return -1; + } +} + /* Creates and returns an OpenFlow message of type OFPT_ERROR with the error * information taken from 'error', whose encoding must be as described in the * large comment in ofp-util.h. If 'oh' is nonnull, then the error will use @@ -2025,7 +2136,7 @@ vendor_code_to_id(uint8_t code) * * Returns NULL if 'error' is not an OpenFlow error code. */ struct ofpbuf * -make_ofp_error_msg(int error, const struct ofp_header *oh) +ofputil_encode_error_msg(int error, const struct ofp_header *oh) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); @@ -2098,6 +2209,102 @@ make_ofp_error_msg(int error, const struct ofp_header *oh) return buf; } +/* Decodes 'oh', which should be an OpenFlow OFPT_ERROR message, and returns an + * Open vSwitch internal error code in the format described in the large + * comment in ofp-util.h. + * + * If 'payload_ofs' is nonnull, on success '*payload_ofs' is set to the offset + * to the payload starting from 'oh' and on failure it is set to 0. */ +int +ofputil_decode_error_msg(const struct ofp_header *oh, size_t *payload_ofs) +{ + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + + const struct ofp_error_msg *oem; + uint16_t type, code; + struct ofpbuf b; + int vendor; + + if (payload_ofs) { + *payload_ofs = 0; + } + if (oh->type != OFPT_ERROR) { + return EPROTO; + } + + ofpbuf_use_const(&b, oh, ntohs(oh->length)); + oem = ofpbuf_try_pull(&b, sizeof *oem); + if (!oem) { + return EPROTO; + } + + type = ntohs(oem->type); + code = ntohs(oem->code); + if (type == NXET_VENDOR && code == NXVC_VENDOR_ERROR) { + const struct nx_vendor_error *nve = ofpbuf_try_pull(&b, sizeof *nve); + if (!nve) { + return EPROTO; + } + + vendor = vendor_id_to_code(ntohl(nve->vendor)); + if (vendor < 0) { + VLOG_WARN_RL(&rl, "error contains unknown vendor ID %#"PRIx32, + ntohl(nve->vendor)); + return EPROTO; + } + type = ntohs(nve->type); + code = ntohs(nve->code); + } else { + vendor = OFPUTIL_VENDOR_OPENFLOW; + } + + if (type >= 1024) { + VLOG_WARN_RL(&rl, "error contains type %"PRIu16" greater than " + "supported maximum value 1023", type); + return EPROTO; + } + + if (payload_ofs) { + *payload_ofs = (uint8_t *) b.data - (uint8_t *) oh; + } + return ofp_mkerr_vendor(vendor, type, code); +} + +void +ofputil_format_error(struct ds *s, int error) +{ + if (is_errno(error)) { + ds_put_cstr(s, strerror(error)); + } else { + uint16_t type = get_ofp_err_type(error); + uint16_t code = get_ofp_err_code(error); + const char *type_s = ofp_error_type_to_string(type); + const char *code_s = ofp_error_code_to_string(type, code); + + ds_put_format(s, "type "); + if (type_s) { + ds_put_cstr(s, type_s); + } else { + ds_put_format(s, "%"PRIu16, type); + } + + ds_put_cstr(s, ", code "); + if (code_s) { + ds_put_cstr(s, code_s); + } else { + ds_put_format(s, "%"PRIu16, code); + } + } +} + +char * +ofputil_error_to_string(int error) +{ + struct ds s = DS_EMPTY_INITIALIZER; + ofputil_format_error(&s, error); + return ds_steal_cstr(&s); +} + /* Attempts to pull 'actions_len' bytes from the front of 'b'. Returns 0 if * successful, otherwise an OpenFlow error. * @@ -2114,16 +2321,16 @@ 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); + VLOG_WARN_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); + VLOG_WARN_RL(&bad_ofmsg_rl, "OpenFlow message actions length %u " + "exceeds remaining message length (%zu)", + actions_len, b->size); goto error; }