#include <inttypes.h>
#include <stdlib.h>
#include "byte-order.h"
+#include "classifier.h"
#include "nx-match.h"
#include "ofp-util.h"
#include "ofpbuf.h"
* 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);
+/* 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 uint32_t
+static ovs_be32
alloc_xid(void)
{
static uint32_t next_xid = 1;
- return next_xid++;
+ return htonl(next_xid++);
+}
+\f
+/* 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;
+}
+\f
/* 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
*
* 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);
/* 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
*
* 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;
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
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;
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);
}
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)
{
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);
}
}
rq->version = OFP_VERSION;
rq->type = OFPT_ECHO_REQUEST;
rq->length = htons(sizeof *rq);
- rq->xid = 0;
+ rq->xid = htonl(0);
return out;
}
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)
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;
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 {
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