X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;ds=sidebyside;f=lib%2Fofp-util.c;h=11836ccdd94a73e91a4394865e96abc340182833;hb=60d5e0d859c2c04b24cf4283bc764d37911871da;hp=208eabb5c56844efbaf9921f2ae1301806a74ac9;hpb=daa68e9f291f94fb40a58843ddbe8a9cc8c99ec4;p=openvswitch diff --git a/lib/ofp-util.c b/lib/ofp-util.c index 208eabb5..11836ccd 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -72,7 +72,6 @@ ofputil_netmask_to_wcbits(ovs_be32 netmask) * name. */ #define WC_INVARIANT_LIST \ WC_INVARIANT_BIT(IN_PORT) \ - WC_INVARIANT_BIT(DL_VLAN) \ WC_INVARIANT_BIT(DL_SRC) \ WC_INVARIANT_BIT(DL_DST) \ WC_INVARIANT_BIT(DL_TYPE) \ @@ -104,11 +103,13 @@ enum { * wildcarded. */ void ofputil_cls_rule_from_match(const struct ofp_match *match, - unsigned int priority, int flow_format, + 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); @@ -117,9 +118,6 @@ ofputil_cls_rule_from_match(const struct ofp_match *match, /* Initialize most of rule->wc. */ wc->wildcards = ofpfw & WC_INVARIANTS; - if (ofpfw & OFPFW_DL_VLAN_PCP) { - wc->wildcards |= FWW_DL_VLAN_PCP; - } if (ofpfw & OFPFW_NW_TOS) { wc->wildcards |= FWW_NW_TOS; } @@ -127,7 +125,7 @@ ofputil_cls_rule_from_match(const struct ofp_match *match, 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 (!(ofpfw & NXFW_TUN_ID)) { + 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; @@ -141,13 +139,11 @@ ofputil_cls_rule_from_match(const struct ofp_match *match, wc->wildcards |= FWW_ETH_MCAST; } - /* Initialize rule->flow. */ + /* 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_vlan = match->dl_vlan; - rule->flow.dl_vlan_pcp = match->dl_vlan_pcp; rule->flow.dl_type = match->dl_type; rule->flow.tp_src = match->tp_src; rule->flow.tp_dst = match->tp_dst; @@ -156,6 +152,49 @@ ofputil_cls_rule_from_match(const struct ofp_match *match, 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); } @@ -167,19 +206,17 @@ ofputil_cls_rule_from_match(const struct ofp_match *match, * 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, int flow_format, +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 OpenFlow wildcards. */ + /* 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_DL_VLAN_PCP) { - ofpfw |= OFPFW_DL_VLAN_PCP; - } if (wc->wildcards & FWW_NW_TOS) { ofpfw |= OFPFW_NW_TOS; } @@ -187,12 +224,32 @@ ofputil_cls_rule_to_match(const struct cls_rule *rule, int flow_format, ofpfw |= NXFW_TUN_ID; } - /* Compose match structure. */ + /* 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); - match->dl_vlan = rule->flow.dl_vlan; - match->dl_vlan_pcp = rule->flow.dl_vlan_pcp; 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; @@ -213,7 +270,502 @@ alloc_xid(void) 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 @@ -269,11 +821,8 @@ 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; + *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 @@ -318,6 +867,28 @@ put_openflow_xid(size_t openflow_len, uint8_t type, ovs_be32 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 @@ -327,6 +898,55 @@ 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 cls_rule *rule, size_t actions_len) @@ -487,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)