X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=lib%2Fofp-util.c;h=11836ccdd94a73e91a4394865e96abc340182833;hb=60d5e0d859c2c04b24cf4283bc764d37911871da;hp=4d632efecd47c2691817ff0c81e1d338f542381a;hpb=69b6be193fcb99f87fb0f6747b3b708edd4305a0;p=openvswitch diff --git a/lib/ofp-util.c b/lib/ofp-util.c index 4d632efe..11836ccd 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -19,6 +19,7 @@ #include #include #include "byte-order.h" +#include "classifier.h" #include "nx-match.h" #include "ofp-util.h" #include "ofpbuf.h" @@ -32,14 +33,739 @@ VLOG_DEFINE_THIS_MODULE(ofp_util); * 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 +/* Given the wildcard bit count in the least-significant 6 of 'wcbits', returns + * an IP netmask with a 1 in each bit that must match and a 0 in each bit that + * is wildcarded. + * + * The bits in 'wcbits' are in the format used in enum ofp_flow_wildcards: 0 + * is exact match, 1 ignores the LSB, 2 ignores the 2 least-significant bits, + * ..., 32 and higher wildcard the entire field. This is the *opposite* of the + * usual convention where e.g. /24 indicates that 8 bits (not 24 bits) are + * wildcarded. */ +ovs_be32 +ofputil_wcbits_to_netmask(int wcbits) +{ + wcbits &= 0x3f; + return wcbits < 32 ? htonl(~((1u << wcbits) - 1)) : 0; +} + +/* Given the IP netmask 'netmask', returns the number of bits of the IP address + * that it wildcards. 'netmask' must be a CIDR netmask (see ip_is_cidr()). */ +int +ofputil_netmask_to_wcbits(ovs_be32 netmask) +{ + assert(ip_is_cidr(netmask)); +#if __GNUC__ >= 4 + return netmask == htonl(0) ? 32 : __builtin_ctz(ntohl(netmask)); +#else + int wcbits; + + for (wcbits = 32; netmask; wcbits--) { + netmask &= netmask - 1; + } + + return wcbits; +#endif +} + +/* A list of the FWW_* and OFPFW_ bits that have the same value, meaning, and + * name. */ +#define WC_INVARIANT_LIST \ + WC_INVARIANT_BIT(IN_PORT) \ + WC_INVARIANT_BIT(DL_SRC) \ + WC_INVARIANT_BIT(DL_DST) \ + WC_INVARIANT_BIT(DL_TYPE) \ + WC_INVARIANT_BIT(NW_PROTO) \ + WC_INVARIANT_BIT(TP_SRC) \ + WC_INVARIANT_BIT(TP_DST) + +/* Verify that all of the invariant bits (as defined on WC_INVARIANT_LIST) + * actually have the same names and values. */ +#define WC_INVARIANT_BIT(NAME) BUILD_ASSERT_DECL(FWW_##NAME == OFPFW_##NAME); + WC_INVARIANT_LIST +#undef WC_INVARIANT_BIT + +/* WC_INVARIANTS is the invariant bits (as defined on WC_INVARIANT_LIST) all + * OR'd together. */ +enum { + WC_INVARIANTS = 0 +#define WC_INVARIANT_BIT(NAME) | FWW_##NAME + WC_INVARIANT_LIST +#undef WC_INVARIANT_BIT +}; + +/* Converts the ofp_match in 'match' into a cls_rule in 'rule', with the given + * 'priority'. + * + * 'flow_format' must either NXFF_OPENFLOW10 or NXFF_TUN_ID_FROM_COOKIE. In + * the latter case only, 'flow''s tun_id field will be taken from the high bits + * of 'cookie', if 'match''s wildcards do not indicate that tun_id is + * wildcarded. */ +void +ofputil_cls_rule_from_match(const struct ofp_match *match, + unsigned int priority, + enum nx_flow_format flow_format, + uint64_t cookie, struct cls_rule *rule) +{ + struct flow_wildcards *wc = &rule->wc; + unsigned int ofpfw; + ovs_be16 vid, pcp; + + /* Initialize rule->priority. */ + ofpfw = ntohl(match->wildcards); + ofpfw &= flow_format == NXFF_TUN_ID_FROM_COOKIE ? OVSFW_ALL : OFPFW_ALL; + rule->priority = !ofpfw ? UINT16_MAX : priority; + + /* Initialize most of rule->wc. */ + wc->wildcards = ofpfw & WC_INVARIANTS; + if (ofpfw & OFPFW_NW_TOS) { + wc->wildcards |= FWW_NW_TOS; + } + memset(wc->reg_masks, 0, sizeof wc->reg_masks); + wc->nw_src_mask = ofputil_wcbits_to_netmask(ofpfw >> OFPFW_NW_SRC_SHIFT); + wc->nw_dst_mask = ofputil_wcbits_to_netmask(ofpfw >> OFPFW_NW_DST_SHIFT); + + if (flow_format == NXFF_TUN_ID_FROM_COOKIE && !(ofpfw & NXFW_TUN_ID)) { + rule->flow.tun_id = htonl(ntohll(cookie) >> 32); + } else { + wc->wildcards |= FWW_TUN_ID; + rule->flow.tun_id = 0; + } + + if (ofpfw & OFPFW_DL_DST) { + /* OpenFlow 1.0 OFPFW_DL_DST covers the whole Ethernet destination, but + * Open vSwitch breaks the Ethernet destination into bits as FWW_DL_DST + * and FWW_ETH_MCAST. */ + wc->wildcards |= FWW_ETH_MCAST; + } + + /* Initialize most of rule->flow. */ + rule->flow.nw_src = match->nw_src; + 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.tp_src = match->tp_src; + rule->flow.tp_dst = match->tp_dst; + memcpy(rule->flow.dl_src, match->dl_src, ETH_ADDR_LEN); + memcpy(rule->flow.dl_dst, match->dl_dst, ETH_ADDR_LEN); + rule->flow.nw_tos = match->nw_tos; + rule->flow.nw_proto = match->nw_proto; + + /* Translate VLANs. */ + vid = match->dl_vlan & htons(VLAN_VID_MASK); + pcp = htons((match->dl_vlan_pcp << VLAN_PCP_SHIFT) & VLAN_PCP_MASK); + switch (ofpfw & (OFPFW_DL_VLAN | OFPFW_DL_VLAN_PCP)) { + case OFPFW_DL_VLAN | OFPFW_DL_VLAN_PCP: + /* Wildcard everything. */ + rule->flow.vlan_tci = htons(0); + rule->wc.vlan_tci_mask = htons(0); + break; + + case OFPFW_DL_VLAN_PCP: + if (match->dl_vlan == htons(OFP_VLAN_NONE)) { + /* Match only packets without 802.1Q header. */ + rule->flow.vlan_tci = htons(0); + rule->wc.vlan_tci_mask = htons(0xffff); + } else { + /* Wildcard PCP, specific VID. */ + rule->flow.vlan_tci = vid | htons(VLAN_CFI); + rule->wc.vlan_tci_mask = htons(VLAN_VID_MASK | VLAN_CFI); + } + break; + + case OFPFW_DL_VLAN: + /* Wildcard VID, specific PCP. */ + rule->flow.vlan_tci = pcp | htons(VLAN_CFI); + rule->wc.vlan_tci_mask = htons(VLAN_PCP_MASK | VLAN_CFI); + break; + + case 0: + if (match->dl_vlan == htons(OFP_VLAN_NONE)) { + /* This case is odd, since we can't have a specific PCP without an + * 802.1Q header. However, older versions of OVS treated this as + * matching packets withut an 802.1Q header, so we do here too. */ + rule->flow.vlan_tci = htons(0); + rule->wc.vlan_tci_mask = htons(0xffff); + } else { + /* Specific VID and PCP. */ + rule->flow.vlan_tci = vid | pcp | htons(VLAN_CFI); + rule->wc.vlan_tci_mask = htons(0xffff); + } + break; + } + + /* Clean up. */ + cls_rule_zero_wildcarded_fields(rule); +} + +/* Extract 'flow' with 'wildcards' into the OpenFlow match structure + * 'match'. + * + * 'flow_format' must either NXFF_OPENFLOW10 or NXFF_TUN_ID_FROM_COOKIE. In + * the latter case only, 'match''s NXFW_TUN_ID bit will be filled in; otherwise + * it is always set to 0. */ +void +ofputil_cls_rule_to_match(const struct cls_rule *rule, + enum nx_flow_format flow_format, + struct ofp_match *match) +{ + const struct flow_wildcards *wc = &rule->wc; + unsigned int ofpfw; + + /* Figure out most OpenFlow wildcards. */ + ofpfw = wc->wildcards & WC_INVARIANTS; + ofpfw |= ofputil_netmask_to_wcbits(wc->nw_src_mask) << OFPFW_NW_SRC_SHIFT; + ofpfw |= ofputil_netmask_to_wcbits(wc->nw_dst_mask) << OFPFW_NW_DST_SHIFT; + if (wc->wildcards & FWW_NW_TOS) { + ofpfw |= OFPFW_NW_TOS; + } + if (flow_format == NXFF_TUN_ID_FROM_COOKIE && wc->wildcards & FWW_TUN_ID) { + ofpfw |= NXFW_TUN_ID; + } + + /* Translate VLANs. */ + match->dl_vlan = htons(0); + match->dl_vlan_pcp = 0; + if (rule->wc.vlan_tci_mask == htons(0)) { + ofpfw |= OFPFW_DL_VLAN | OFPFW_DL_VLAN_PCP; + } else if (rule->wc.vlan_tci_mask & htons(VLAN_CFI) + && !(rule->flow.vlan_tci & htons(VLAN_CFI))) { + match->dl_vlan = htons(OFP_VLAN_NONE); + } else { + if (!(rule->wc.vlan_tci_mask & htons(VLAN_VID_MASK))) { + ofpfw |= OFPFW_DL_VLAN; + } else { + match->dl_vlan = htons(vlan_tci_to_vid(rule->flow.vlan_tci)); + } + + if (!(rule->wc.vlan_tci_mask & htons(VLAN_PCP_MASK))) { + ofpfw |= OFPFW_DL_VLAN_PCP; + } else { + match->dl_vlan_pcp = vlan_tci_to_pcp(rule->flow.vlan_tci); + } + } + + /* Compose most of the match structure. */ + match->wildcards = htonl(ofpfw); + match->in_port = htons(rule->flow.in_port == ODPP_LOCAL ? OFPP_LOCAL + : 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->nw_src = rule->flow.nw_src; + match->nw_dst = rule->flow.nw_dst; + match->nw_tos = rule->flow.nw_tos; + match->nw_proto = rule->flow.nw_proto; + match->tp_src = rule->flow.tp_src; + match->tp_dst = rule->flow.tp_dst; + memset(match->pad1, '\0', sizeof match->pad1); + memset(match->pad2, '\0', sizeof match->pad2); +} + +/* 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++); +} + +/* Basic parsing of OpenFlow messages. */ + +struct ofputil_msg_type { + enum ofputil_msg_code code; /* OFPUTIL_*. */ + uint32_t value; /* OFPT_*, OFPST_*, NXT_*, or NXST_*. */ + const char *name; /* e.g. "OFPT_FLOW_REMOVED". */ + unsigned int min_size; /* Minimum total message size in bytes. */ + /* 0 if 'min_size' is the exact size that the message must be. Otherwise, + * the message may exceed 'min_size' by an even multiple of this value. */ + unsigned int extra_multiple; +}; + +struct ofputil_msg_category { + const char *name; /* e.g. "OpenFlow message" */ + const struct ofputil_msg_type *types; + size_t n_types; + int missing_error; /* ofp_mkerr() value for missing type. */ +}; + +static bool +ofputil_length_ok(const struct ofputil_msg_category *cat, + const struct ofputil_msg_type *type, + unsigned int size) +{ + switch (type->extra_multiple) { + case 0: + if (size != type->min_size) { + VLOG_WARN_RL(&bad_ofmsg_rl, "received %s %s with incorrect " + "length %u (expected length %u)", + cat->name, type->name, size, type->min_size); + return false; + } + return true; + + case 1: + if (size < type->min_size) { + VLOG_WARN_RL(&bad_ofmsg_rl, "received %s %s with incorrect " + "length %u (expected length at least %u bytes)", + cat->name, type->name, size, type->min_size); + return false; + } + return true; + + default: + if (size < type->min_size + || (size - type->min_size) % type->extra_multiple) { + VLOG_WARN_RL(&bad_ofmsg_rl, "received %s %s with incorrect " + "length %u (must be exactly %u bytes or longer " + "by an integer multiple of %u bytes)", + cat->name, type->name, size, + type->min_size, type->extra_multiple); + return false; + } + return true; + } +} + +static int +ofputil_lookup_openflow_message(const struct ofputil_msg_category *cat, + uint32_t value, unsigned int size, + const struct ofputil_msg_type **typep) +{ + const struct ofputil_msg_type *type; + + for (type = cat->types; type < &cat->types[cat->n_types]; type++) { + if (type->value == value) { + if (!ofputil_length_ok(cat, type, size)) { + return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); + } + *typep = type; + return 0; + } + } + + VLOG_WARN_RL(&bad_ofmsg_rl, "received %s of unknown type %u", + cat->name, value); + return cat->missing_error; +} + +static int +ofputil_decode_vendor(const struct ofp_header *oh, + const struct ofputil_msg_type **typep) +{ + static const struct ofputil_msg_type nxt_messages[] = { + { OFPUTIL_NXT_STATUS_REQUEST, + NXT_STATUS_REQUEST, "NXT_STATUS_REQUEST", + sizeof(struct nicira_header), 1 }, + + { OFPUTIL_NXT_STATUS_REPLY, + NXT_STATUS_REPLY, "NXT_STATUS_REPLY", + sizeof(struct nicira_header), 1 }, + + { OFPUTIL_NXT_TUN_ID_FROM_COOKIE, + NXT_TUN_ID_FROM_COOKIE, "NXT_TUN_ID_FROM_COOKIE", + sizeof(struct nxt_tun_id_cookie), 0 }, + + { OFPUTIL_NXT_ROLE_REQUEST, + NXT_ROLE_REQUEST, "NXT_ROLE_REQUEST", + sizeof(struct nx_role_request), 0 }, + + { OFPUTIL_NXT_ROLE_REPLY, + NXT_ROLE_REPLY, "NXT_ROLE_REPLY", + sizeof(struct nx_role_request), 0 }, + + { OFPUTIL_NXT_SET_FLOW_FORMAT, + NXT_SET_FLOW_FORMAT, "NXT_SET_FLOW_FORMAT", + sizeof(struct nxt_set_flow_format), 0 }, + + { OFPUTIL_NXT_FLOW_MOD, + NXT_FLOW_MOD, "NXT_FLOW_MOD", + sizeof(struct nx_flow_mod), 8 }, + + { OFPUTIL_NXT_FLOW_REMOVED, + NXT_FLOW_REMOVED, "NXT_FLOW_REMOVED", + sizeof(struct nx_flow_removed), 8 }, + }; + + static const struct ofputil_msg_category nxt_category = { + "Nicira extension message", + nxt_messages, ARRAY_SIZE(nxt_messages), + OFP_MKERR(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE) + }; + + const struct ofp_vendor_header *ovh; + const struct nicira_header *nh; + + ovh = (const struct ofp_vendor_header *) oh; + if (ovh->vendor != htonl(NX_VENDOR_ID)) { + VLOG_WARN_RL(&bad_ofmsg_rl, "received vendor message for unknown " + "vendor %"PRIx32, ntohl(ovh->vendor)); + return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_VENDOR); + } + + if (ntohs(ovh->header.length) < sizeof(struct nicira_header)) { + VLOG_WARN_RL(&bad_ofmsg_rl, "received Nicira vendor message of " + "length %u (expected at least %zu)", + ntohs(ovh->header.length), sizeof(struct nicira_header)); + return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); + } + + nh = (const struct nicira_header *) oh; + return ofputil_lookup_openflow_message(&nxt_category, ntohl(nh->subtype), + ntohs(oh->length), typep); +} + +static int +check_nxstats_msg(const struct ofp_header *oh) +{ + const struct ofp_stats_request *osr; + ovs_be32 vendor; + + osr = (const struct ofp_stats_request *) oh; + + memcpy(&vendor, osr->body, sizeof vendor); + if (vendor != htonl(NX_VENDOR_ID)) { + VLOG_WARN_RL(&bad_ofmsg_rl, "received vendor stats message for " + "unknown vendor %"PRIx32, ntohl(vendor)); + return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_VENDOR); + } + + if (ntohs(osr->header.length) < sizeof(struct nicira_stats_msg)) { + VLOG_WARN_RL(&bad_ofmsg_rl, "truncated Nicira stats message"); + return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); + } + + return 0; +} + +static int +ofputil_decode_nxst_request(const struct ofp_header *oh, + const struct ofputil_msg_type **typep) +{ + static const struct ofputil_msg_type nxst_requests[] = { + { OFPUTIL_NXST_FLOW_REQUEST, + NXST_FLOW, "NXST_FLOW request", + sizeof(struct nx_flow_stats_request), 8 }, + + { OFPUTIL_NXST_AGGREGATE_REQUEST, + NXST_AGGREGATE, "NXST_AGGREGATE request", + sizeof(struct nx_aggregate_stats_request), 8 }, + }; + + static const struct ofputil_msg_category nxst_request_category = { + "Nicira extension statistics", + nxst_requests, ARRAY_SIZE(nxst_requests), + OFP_MKERR(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE) + }; + + const struct nicira_stats_msg *nsm; + int error; + + error = check_nxstats_msg(oh); + if (error) { + return error; + } + + nsm = (struct nicira_stats_msg *) oh; + return ofputil_lookup_openflow_message(&nxst_request_category, + ntohl(nsm->subtype), + ntohs(oh->length), typep); +} + +static int +ofputil_decode_nxst_reply(const struct ofp_header *oh, + const struct ofputil_msg_type **typep) +{ + static const struct ofputil_msg_type nxst_replies[] = { + { OFPUTIL_NXST_FLOW_REPLY, + NXST_FLOW, "NXST_FLOW reply", + sizeof(struct nicira_stats_msg), 8 }, + + { OFPUTIL_NXST_AGGREGATE_REPLY, + NXST_AGGREGATE, "NXST_AGGREGATE reply", + sizeof(struct nx_aggregate_stats_reply), 0 }, + }; + + static const struct ofputil_msg_category nxst_reply_category = { + "Nicira extension statistics", + nxst_replies, ARRAY_SIZE(nxst_replies), + OFP_MKERR(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE) + }; + + const struct nicira_stats_msg *nsm; + int error; + + error = check_nxstats_msg(oh); + if (error) { + return error; + } + + nsm = (struct nicira_stats_msg *) oh; + return ofputil_lookup_openflow_message(&nxst_reply_category, + ntohl(nsm->subtype), + ntohs(oh->length), typep); +} + +static int +ofputil_decode_ofpst_request(const struct ofp_header *oh, + const struct ofputil_msg_type **typep) +{ + enum { OSR_SIZE = sizeof(struct ofp_stats_request) }; + static const struct ofputil_msg_type ofpst_requests[] = { + { OFPUTIL_OFPST_DESC_REQUEST, + OFPST_DESC, "OFPST_DESC request", + OSR_SIZE, 0 }, + + { OFPUTIL_OFPST_FLOW_REQUEST, + OFPST_FLOW, "OFPST_FLOW request", + OSR_SIZE + sizeof(struct ofp_flow_stats_request), 0 }, + + { OFPUTIL_OFPST_AGGREGATE_REQUEST, + OFPST_AGGREGATE, "OFPST_AGGREGATE request", + OSR_SIZE + sizeof(struct ofp_aggregate_stats_request), 0 }, + + { OFPUTIL_OFPST_TABLE_REQUEST, + OFPST_TABLE, "OFPST_TABLE request", + OSR_SIZE, 0 }, + + { OFPUTIL_OFPST_PORT_REQUEST, + OFPST_PORT, "OFPST_PORT request", + OSR_SIZE + sizeof(struct ofp_port_stats_request), 0 }, + + { OFPUTIL_OFPST_QUEUE_REQUEST, + OFPST_QUEUE, "OFPST_QUEUE request", + OSR_SIZE + sizeof(struct ofp_queue_stats_request), 0 }, + + { 0, + OFPST_VENDOR, "OFPST_VENDOR request", + OSR_SIZE + sizeof(uint32_t), 1 }, + }; + + static const struct ofputil_msg_category ofpst_request_category = { + "OpenFlow statistics", + ofpst_requests, ARRAY_SIZE(ofpst_requests), + OFP_MKERR(OFPET_BAD_REQUEST, OFPBRC_BAD_STAT) + }; + + const struct ofp_stats_request *osr; + int error; + + osr = (const struct ofp_stats_request *) oh; + error = ofputil_lookup_openflow_message(&ofpst_request_category, + ntohs(osr->type), + ntohs(oh->length), typep); + if (!error && osr->type == htons(OFPST_VENDOR)) { + error = ofputil_decode_nxst_request(oh, typep); + } + return error; +} + +static int +ofputil_decode_ofpst_reply(const struct ofp_header *oh, + const struct ofputil_msg_type **typep) +{ + enum { OSR_SIZE = sizeof(struct ofp_stats_reply) }; + static const struct ofputil_msg_type ofpst_replies[] = { + { OFPUTIL_OFPST_DESC_REPLY, + OFPST_DESC, "OFPST_DESC reply", + OSR_SIZE + sizeof(struct ofp_desc_stats), 0 }, + + { OFPUTIL_OFPST_FLOW_REPLY, + OFPST_FLOW, "OFPST_FLOW reply", + OSR_SIZE, 1 }, + + { OFPUTIL_OFPST_AGGREGATE_REPLY, + OFPST_AGGREGATE, "OFPST_AGGREGATE reply", + OSR_SIZE + sizeof(struct ofp_aggregate_stats_reply), 0 }, + + { OFPUTIL_OFPST_TABLE_REPLY, + OFPST_TABLE, "OFPST_TABLE reply", + OSR_SIZE, sizeof(struct ofp_table_stats) }, + + { OFPUTIL_OFPST_PORT_REPLY, + OFPST_PORT, "OFPST_PORT reply", + OSR_SIZE, sizeof(struct ofp_port_stats) }, + + { OFPUTIL_OFPST_QUEUE_REPLY, + OFPST_QUEUE, "OFPST_QUEUE reply", + OSR_SIZE, sizeof(struct ofp_queue_stats) }, + + { 0, + OFPST_VENDOR, "OFPST_VENDOR reply", + OSR_SIZE + sizeof(uint32_t), 1 }, + }; + + static const struct ofputil_msg_category ofpst_reply_category = { + "OpenFlow statistics", + ofpst_replies, ARRAY_SIZE(ofpst_replies), + OFP_MKERR(OFPET_BAD_REQUEST, OFPBRC_BAD_STAT) + }; + + const struct ofp_stats_reply *osr = (const struct ofp_stats_reply *) oh; + int error; + + error = ofputil_lookup_openflow_message(&ofpst_reply_category, + ntohs(osr->type), + ntohs(oh->length), typep); + if (!error && osr->type == htons(OFPST_VENDOR)) { + error = ofputil_decode_nxst_reply(oh, typep); + } + return error; } +/* Decodes the message type represented by 'oh'. Returns 0 if successful or + * an OpenFlow error code constructed with ofp_mkerr() on failure. Either + * way, stores in '*typep' a type structure that can be inspected with the + * ofputil_msg_type_*() functions. + * + * oh->length must indicate the correct length of the message (and must be at + * least sizeof(struct ofp_header)). + * + * Success indicates that 'oh' is at least as long as the minimum-length + * message of its type. */ +int +ofputil_decode_msg_type(const struct ofp_header *oh, + const struct ofputil_msg_type **typep) +{ + static const struct ofputil_msg_type ofpt_messages[] = { + { OFPUTIL_OFPT_HELLO, + OFPT_HELLO, "OFPT_HELLO", + sizeof(struct ofp_hello), 1 }, + + { OFPUTIL_OFPT_ERROR, + OFPT_ERROR, "OFPT_ERROR", + sizeof(struct ofp_error_msg), 1 }, + + { OFPUTIL_OFPT_ECHO_REQUEST, + OFPT_ECHO_REQUEST, "OFPT_ECHO_REQUEST", + sizeof(struct ofp_header), 1 }, + + { OFPUTIL_OFPT_ECHO_REPLY, + OFPT_ECHO_REPLY, "OFPT_ECHO_REPLY", + sizeof(struct ofp_header), 1 }, + + { OFPUTIL_OFPT_FEATURES_REQUEST, + OFPT_FEATURES_REQUEST, "OFPT_FEATURES_REQUEST", + sizeof(struct ofp_header), 0 }, + + { OFPUTIL_OFPT_FEATURES_REPLY, + OFPT_FEATURES_REPLY, "OFPT_FEATURES_REPLY", + sizeof(struct ofp_switch_features), sizeof(struct ofp_phy_port) }, + + { OFPUTIL_OFPT_GET_CONFIG_REQUEST, + OFPT_GET_CONFIG_REQUEST, "OFPT_GET_CONFIG_REQUEST", + sizeof(struct ofp_header), 0 }, + + { OFPUTIL_OFPT_GET_CONFIG_REPLY, + OFPT_GET_CONFIG_REPLY, "OFPT_GET_CONFIG_REPLY", + sizeof(struct ofp_switch_config), 0 }, + + { OFPUTIL_OFPT_SET_CONFIG, + OFPT_SET_CONFIG, "OFPT_SET_CONFIG", + sizeof(struct ofp_switch_config), 0 }, + + { OFPUTIL_OFPT_PACKET_IN, + OFPT_PACKET_IN, "OFPT_PACKET_IN", + offsetof(struct ofp_packet_in, data), 1 }, + + { OFPUTIL_OFPT_FLOW_REMOVED, + OFPT_FLOW_REMOVED, "OFPT_FLOW_REMOVED", + sizeof(struct ofp_flow_removed), 0 }, + + { OFPUTIL_OFPT_PORT_STATUS, + OFPT_PORT_STATUS, "OFPT_PORT_STATUS", + sizeof(struct ofp_port_status), 0 }, + + { OFPUTIL_OFPT_PACKET_OUT, + OFPT_PACKET_OUT, "OFPT_PACKET_OUT", + sizeof(struct ofp_packet_out), 1 }, + + { OFPUTIL_OFPT_FLOW_MOD, + OFPT_FLOW_MOD, "OFPT_FLOW_MOD", + sizeof(struct ofp_flow_mod), 1 }, + + { OFPUTIL_OFPT_PORT_MOD, + OFPT_PORT_MOD, "OFPT_PORT_MOD", + sizeof(struct ofp_port_mod), 0 }, + + { 0, + OFPT_STATS_REQUEST, "OFPT_STATS_REQUEST", + sizeof(struct ofp_stats_request), 1 }, + + { 0, + OFPT_STATS_REPLY, "OFPT_STATS_REPLY", + sizeof(struct ofp_stats_reply), 1 }, + + { OFPUTIL_OFPT_BARRIER_REQUEST, + OFPT_BARRIER_REQUEST, "OFPT_BARRIER_REQUEST", + sizeof(struct ofp_header), 0 }, + + { OFPUTIL_OFPT_BARRIER_REPLY, + OFPT_BARRIER_REPLY, "OFPT_BARRIER_REPLY", + sizeof(struct ofp_header), 0 }, + + { 0, + OFPT_VENDOR, "OFPT_VENDOR", + sizeof(struct ofp_vendor_header), 1 }, + }; + + static const struct ofputil_msg_category ofpt_category = { + "OpenFlow message", + ofpt_messages, ARRAY_SIZE(ofpt_messages), + OFP_MKERR(OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE) + }; + + int error; + + error = ofputil_lookup_openflow_message(&ofpt_category, oh->type, + ntohs(oh->length), typep); + if (!error) { + switch (oh->type) { + case OFPT_VENDOR: + error = ofputil_decode_vendor(oh, typep); + break; + + case OFPT_STATS_REQUEST: + error = ofputil_decode_ofpst_request(oh, typep); + break; + + case OFPT_STATS_REPLY: + error = ofputil_decode_ofpst_reply(oh, typep); + + default: + break; + } + } + if (error) { + static const struct ofputil_msg_type ofputil_invalid_type = { + OFPUTIL_INVALID, + 0, "OFPUTIL_INVALID", + 0, 0 + }; + + *typep = &ofputil_invalid_type; + } + return error; +} + +/* Returns an OFPUTIL_* message type code for 'type'. */ +enum ofputil_msg_code +ofputil_msg_type_code(const struct ofputil_msg_type *type) +{ + return type->code; +} + +/* Returns a string representing the message type of 'type'. The string is the + * enumeration constant for the type, e.g. "OFPT_HELLO". For statistics + * messages, the constant is followed by "request" or "reply", + * e.g. "OFPST_AGGREGATE reply". */ +const char * +ofputil_msg_type_name(const struct ofputil_msg_type *type) +{ + return type->name; +} + /* Allocates and stores in '*bufferp' a new ofpbuf with a size of * 'openflow_len', starting with an OpenFlow header with the given 'type' and * an arbitrary transaction id. Allocated bytes beyond the header, if any, are @@ -82,7 +808,7 @@ make_nxmsg(size_t openflow_len, uint32_t subtype, 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); @@ -92,14 +818,11 @@ make_openflow_xid(size_t openflow_len, uint8_t type, uint32_t xid, /* 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, +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; + *bufferp = ofpbuf_new(openflow_len); + return put_nxmsg_xid(openflow_len, subtype, xid, *bufferp); } /* Appends 'openflow_len' bytes to 'buffer', starting with an OpenFlow header @@ -127,7 +850,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; @@ -144,6 +867,28 @@ put_openflow_xid(size_t openflow_len, uint8_t type, uint32_t xid, return oh; } +/* Similar to put_openflow() but append a Nicira vendor extension message with + * the specific 'subtype'. 'subtype' should be in host byte order. */ +void * +put_nxmsg(size_t openflow_len, uint32_t subtype, struct ofpbuf *buffer) +{ + return put_nxmsg_xid(openflow_len, subtype, alloc_xid(), buffer); +} + +/* Similar to put_openflow_xid() but append a Nicira vendor extension message + * with the specific 'subtype'. 'subtype' should be in host byte order. */ +void * +put_nxmsg_xid(size_t openflow_len, uint32_t subtype, ovs_be32 xid, + struct ofpbuf *buffer) +{ + struct nicira_header *nxh; + + nxh = put_openflow_xid(openflow_len, OFPT_VENDOR, xid, buffer); + nxh->vendor = htonl(NX_VENDOR_ID); + nxh->subtype = htonl(subtype); + return nxh; +} + /* Updates the 'length' field of the OpenFlow message in 'buffer' to * 'buffer->size'. */ void @@ -153,8 +898,58 @@ update_openflow_length(struct ofpbuf *buffer) oh->length = htons(buffer->size); } +/* Creates an ofp_stats_request with the given 'type' and 'body_len' bytes of + * space allocated for the 'body' member. Returns the first byte of the 'body' + * member. */ +void * +ofputil_make_stats_request(size_t body_len, uint16_t type, + struct ofpbuf **bufferp) +{ + struct ofp_stats_request *osr; + osr = make_openflow((offsetof(struct ofp_stats_request, body) + + body_len), OFPT_STATS_REQUEST, bufferp); + osr->type = htons(type); + osr->flags = htons(0); + return osr->body; +} + +/* Creates a stats request message with Nicira as vendor and the given + * 'subtype', of total length 'openflow_len'. Returns the message. */ +void * +ofputil_make_nxstats_request(size_t openflow_len, uint32_t subtype, + struct ofpbuf **bufferp) +{ + struct nicira_stats_msg *nsm; + + nsm = make_openflow(openflow_len, OFPT_STATS_REQUEST, bufferp); + nsm->type = htons(OFPST_VENDOR); + nsm->flags = htons(0); + nsm->vendor = htonl(NX_VENDOR_ID); + nsm->subtype = htonl(subtype); + return nsm; +} + +/* Returns the first byte of the 'body' member of the ofp_stats_request or + * ofp_stats_reply in 'oh'. */ +const void * +ofputil_stats_body(const struct ofp_header *oh) +{ + assert(oh->type == OFPT_STATS_REQUEST || oh->type == OFPT_STATS_REPLY); + return ((const struct ofp_stats_request *) oh)->body; +} + +/* Returns the length of the 'body' member of the ofp_stats_request or + * ofp_stats_reply in 'oh'. */ +size_t +ofputil_stats_body_len(const struct ofp_header *oh) +{ + assert(oh->type == OFPT_STATS_REQUEST || oh->type == OFPT_STATS_REPLY); + return ntohs(oh->length) - sizeof(struct ofp_stats_request); +} + struct ofpbuf * -make_flow_mod(uint16_t command, const struct flow *flow, size_t actions_len) +make_flow_mod(uint16_t command, const struct cls_rule *rule, + size_t actions_len) { struct ofp_flow_mod *ofm; size_t size = sizeof *ofm + actions_len; @@ -164,29 +959,17 @@ make_flow_mod(uint16_t command, const struct flow *flow, size_t actions_len) ofm->header.type = OFPT_FLOW_MOD; ofm->header.length = htons(size); ofm->cookie = 0; - ofm->match.wildcards = htonl(0); - ofm->match.in_port = htons(flow->in_port == ODPP_LOCAL ? OFPP_LOCAL - : flow->in_port); - memcpy(ofm->match.dl_src, flow->dl_src, sizeof ofm->match.dl_src); - memcpy(ofm->match.dl_dst, flow->dl_dst, sizeof ofm->match.dl_dst); - ofm->match.dl_vlan = flow->dl_vlan; - ofm->match.dl_vlan_pcp = flow->dl_vlan_pcp; - ofm->match.dl_type = flow->dl_type; - ofm->match.nw_src = flow->nw_src; - ofm->match.nw_dst = flow->nw_dst; - ofm->match.nw_proto = flow->nw_proto; - ofm->match.nw_tos = flow->nw_tos; - ofm->match.tp_src = flow->tp_src; - ofm->match.tp_dst = flow->tp_dst; + ofm->priority = htons(MIN(rule->priority, UINT16_MAX)); + ofputil_cls_rule_to_match(rule, NXFF_OPENFLOW10, &ofm->match); ofm->command = htons(command); return out; } struct ofpbuf * -make_add_flow(const struct flow *flow, uint32_t buffer_id, +make_add_flow(const struct cls_rule *rule, uint32_t buffer_id, uint16_t idle_timeout, size_t actions_len) { - struct ofpbuf *out = make_flow_mod(OFPFC_ADD, flow, actions_len); + struct ofpbuf *out = make_flow_mod(OFPFC_ADD, rule, actions_len); struct ofp_flow_mod *ofm = out->data; ofm->idle_timeout = htons(idle_timeout); ofm->hard_timeout = htons(OFP_FLOW_PERMANENT); @@ -195,16 +978,16 @@ make_add_flow(const struct flow *flow, uint32_t buffer_id, } struct ofpbuf * -make_del_flow(const struct flow *flow) +make_del_flow(const struct cls_rule *rule) { - struct ofpbuf *out = make_flow_mod(OFPFC_DELETE_STRICT, flow, 0); + struct ofpbuf *out = make_flow_mod(OFPFC_DELETE_STRICT, rule, 0); struct ofp_flow_mod *ofm = out->data; ofm->out_port = htons(OFPP_NONE); return out; } struct ofpbuf * -make_add_simple_flow(const struct flow *flow, +make_add_simple_flow(const struct cls_rule *rule, uint32_t buffer_id, uint16_t out_port, uint16_t idle_timeout) { @@ -212,14 +995,14 @@ make_add_simple_flow(const struct flow *flow, struct ofp_action_output *oao; struct ofpbuf *buffer; - buffer = make_add_flow(flow, buffer_id, idle_timeout, sizeof *oao); + buffer = make_add_flow(rule, 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); + return make_add_flow(rule, buffer_id, idle_timeout, 0); } } @@ -308,7 +1091,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; } @@ -324,98 +1107,6 @@ make_echo_reply(const struct ofp_header *rq) return out; } -static int -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); - char *got_type_name = ofp_message_type_to_string(got_type); - VLOG_WARN_RL(&bad_ofmsg_rl, - "received bad message type %s (expected %s)", - got_type_name, want_type_name); - free(want_type_name); - free(got_type_name); - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE); - } - return 0; -} - -/* Checks that 'msg' has type 'type' and that it is exactly 'size' bytes long. - * Returns 0 if the checks pass, otherwise an OpenFlow error code (produced - * with ofp_mkerr()). */ -int -check_ofp_message(const struct ofp_header *msg, uint8_t type, size_t size) -{ - size_t got_size; - int error; - - error = check_message_type(msg->type, type); - if (error) { - return error; - } - - got_size = ntohs(msg->length); - if (got_size != size) { - char *type_name = ofp_message_type_to_string(type); - VLOG_WARN_RL(&bad_ofmsg_rl, - "received %s message of length %zu (expected %zu)", - type_name, got_size, size); - free(type_name); - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); - } - - return 0; -} - -/* Checks that 'msg' has type 'type' and that 'msg' is 'size' plus a - * nonnegative integer multiple of 'array_elt_size' bytes long. Returns 0 if - * the checks pass, otherwise an OpenFlow error code (produced with - * ofp_mkerr()). - * - * If 'n_array_elts' is nonnull, then '*n_array_elts' is set to the number of - * 'array_elt_size' blocks in 'msg' past the first 'min_size' bytes, when - * successful. */ -int -check_ofp_message_array(const struct ofp_header *msg, uint8_t type, - size_t min_size, size_t array_elt_size, - size_t *n_array_elts) -{ - size_t got_size; - int error; - - assert(array_elt_size); - - error = check_message_type(msg->type, type); - if (error) { - return error; - } - - got_size = ntohs(msg->length); - if (got_size < min_size) { - char *type_name = ofp_message_type_to_string(type); - VLOG_WARN_RL(&bad_ofmsg_rl, "received %s message of length %zu " - "(expected at least %zu)", - type_name, got_size, min_size); - free(type_name); - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); - } - if ((got_size - min_size) % array_elt_size) { - char *type_name = ofp_message_type_to_string(type); - VLOG_WARN_RL(&bad_ofmsg_rl, - "received %s message of bad length %zu: the " - "excess over %zu (%zu) is not evenly divisible by %zu " - "(remainder is %zu)", - type_name, got_size, min_size, got_size - min_size, - array_elt_size, (got_size - min_size) % array_elt_size); - free(type_name); - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); - } - if (n_array_elts) { - *n_array_elts = (got_size - min_size) / array_elt_size; - } - return 0; -} - const struct ofp_flow_stats * flow_stats_first(struct flow_stats_iterator *iter, const struct ofp_stats_reply *osr) @@ -545,6 +1236,7 @@ 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)); @@ -552,6 +1244,7 @@ check_nicira_action(const union ofp_action *a, unsigned int len, 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)); @@ -559,6 +1252,10 @@ check_nicira_action(const union ofp_action *a, unsigned int len, 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); } @@ -741,10 +1438,10 @@ normalize_match(struct ofp_match *m) m->tp_src = m->tp_dst = 0; } if (wc & OFPFW_NW_SRC_MASK) { - m->nw_src &= flow_nw_bits_to_mask(wc, OFPFW_NW_SRC_SHIFT); + m->nw_src &= ofputil_wcbits_to_netmask(wc >> OFPFW_NW_SRC_SHIFT); } if (wc & OFPFW_NW_DST_MASK) { - m->nw_dst &= flow_nw_bits_to_mask(wc, OFPFW_NW_DST_SHIFT); + m->nw_dst &= ofputil_wcbits_to_netmask(wc >> OFPFW_NW_DST_SHIFT); } if (wc & OFPFW_NW_TOS) { m->nw_tos = 0; @@ -756,10 +1453,10 @@ normalize_match(struct ofp_match *m) m->nw_proto = 0; } if (wc & OFPFW_NW_SRC_MASK) { - m->nw_src &= flow_nw_bits_to_mask(wc, OFPFW_NW_SRC_SHIFT); + m->nw_src &= ofputil_wcbits_to_netmask(wc >> OFPFW_NW_SRC_SHIFT); } if (wc & OFPFW_NW_DST_MASK) { - m->nw_dst &= flow_nw_bits_to_mask(wc, OFPFW_NW_DST_SHIFT); + m->nw_dst &= ofputil_wcbits_to_netmask(wc >> OFPFW_NW_DST_SHIFT); } m->tp_src = m->tp_dst = m->nw_tos = 0; } else { @@ -846,7 +1543,7 @@ make_ofp_error_msg(int error, const struct ofp_header *oh) uint8_t vendor; uint16_t type; uint16_t code; - uint32_t xid; + ovs_be32 xid; if (!is_ofp_error(error)) { /* We format 'error' with strerror() here since it seems likely to be