From 09246b99d1601e2ba7ff85bb26f9b0235632a76d Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Tue, 9 Nov 2010 17:00:59 -0800 Subject: [PATCH] ofproto: Implement Nicira Extended Match flexible flow match (NXM). --- include/openflow/nicira-ext.h | 612 ++++++++++++++++++++++++++++- lib/automake.mk | 2 + lib/nx-match.c | 709 ++++++++++++++++++++++++++++++++++ lib/nx-match.def | 43 +++ lib/nx-match.h | 65 ++++ lib/util.c | 5 +- lib/vlog-modules.def | 1 + ofproto/ofproto.c | 277 ++++++++++++- tests/ovs-ofctl.at | 197 ++++++++++ utilities/ovs-ofctl.c | 73 +++- 10 files changed, 1959 insertions(+), 25 deletions(-) create mode 100644 lib/nx-match.c create mode 100644 lib/nx-match.def create mode 100644 lib/nx-match.h diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h index e13c2aa4..a08ae14a 100644 --- a/include/openflow/nicira-ext.h +++ b/include/openflow/nicira-ext.h @@ -77,18 +77,56 @@ struct nx_vendor_error { * so we're using the existing 'type' values to avoid having to invent new ones * that duplicate the current ones' meanings. */ +/* Additional "code" values for OFPET_BAD_REQUEST. */ +enum { +/* Nicira Extended Match (NXM) errors. */ + + /* Generic error code used when there is an error in an NXM sent to the + * switch. The switch may use one of the more specific error codes below, + * if there is an appropriate one, to simplify debugging, but it is not + * required to do so. */ + NXBRC_NXM_INVALID = 0x100, + + /* The nxm_type, or nxm_type taken in combination with nxm_hasmask or + * nxm_length or both, is invalid or not implemented. */ + NXBRC_NXM_BAD_TYPE = 0x101, + + /* Invalid nxm_value. */ + NXBRC_NXM_BAD_VALUE = 0x102, + + /* Invalid nxm_mask. */ + NXBRC_NXM_BAD_MASK = 0x103, + + /* A prerequisite was not met. */ + NXBRC_NXM_BAD_PREREQ = 0x104, + + /* A given nxm_type was specified more than once. */ + NXBRC_NXM_DUP_TYPE = 0x105 +}; + /* Additional "code" values for OFPET_FLOW_MOD_FAILED. */ enum { /* Generic hardware error. */ NXFMFC_HARDWARE = 0x100, /* A nonexistent table ID was specified in the "command" field of struct - * ofp_flow_mod, when the nxt_flow_mod_table_id extension is enabled. */ - NXFMFC_BAD_TABLE_ID + * ofp_flow_mod, when the nxt_flow_mod_table_id extension is enabled. + * (This extension is not yet implemented on this branch of Open + * vSwitch.) */ + NXFMFC_BAD_TABLE_ID = 0x101 }; /* Nicira vendor requests and replies. */ +/* Header for Nicira vendor requests and replies. */ +struct nicira_header { + struct ofp_header header; + ovs_be32 vendor; /* NX_VENDOR_ID. */ + ovs_be32 subtype; /* One of NXT_* below. */ +}; +OFP_ASSERT(sizeof(struct nicira_header) == 16); + +/* Values for the 'subtype' member of struct nicira_header. */ enum nicira_type { /* Switch status request. The request body is an ASCII string that * specifies a prefix of the key names to include in the output; if it is @@ -115,21 +153,33 @@ enum nicira_type { /* Controller role support. The request body is struct nx_role_request. * The reply echos the request. */ NXT_ROLE_REQUEST, - NXT_ROLE_REPLY + NXT_ROLE_REPLY, + + /* Flexible flow specification (aka NXM = Nicira Extended Match). */ + NXT_SET_FLOW_FORMAT, /* Set flow format. */ + NXT_FLOW_MOD, /* Analogous to OFPT_FLOW_MOD. */ + NXT_FLOW_REMOVED /* Analogous to OFPT_FLOW_REMOVED. */ }; -struct nicira_header { - struct ofp_header header; - uint32_t vendor; /* NX_VENDOR_ID. */ - uint32_t subtype; /* One of NXT_* above. */ +/* Header for Nicira vendor stats request and reply messages. */ +struct nicira_stats_msg { + struct ofp_header header; /* OFPT_STATS_REQUEST or OFPT_STATS_REPLY. */ + ovs_be16 type; /* OFPST_VENDOR. */ + ovs_be16 flags; /* OFPSF_{REQ,REPLY}_*. */ + ovs_be32 vendor; /* NX_VENDOR_ID. */ + ovs_be32 subtype; /* One of NXST_* below. */ + uint8_t pad[4]; /* Align to 64-bits. */ }; -OFP_ASSERT(sizeof(struct nicira_header) == 16); +OFP_ASSERT(sizeof(struct nicira_stats_msg) == 24); -enum { - NXFF_OPENFLOW10 = 0, /* Standard OpenFlow 1.0 compatible. */ - NXFF_TUN_ID_FROM_COOKIE = 1 /* Obtain tunnel ID from cookie. */ +/* Values for the 'subtype' member of struct nicira_stats_msg. */ +enum nicira_stats_type { + /* Flexible flow specification (aka NXM = Nicira Extended Match). */ + NXST_FLOW, /* Analogous to OFPST_FLOW. */ + NXST_AGGREGATE /* Analogous to OFPST_AGGREGATE. */ }; +/* NXT_TUN_ID_FROM_COOKIE request. */ struct nxt_tun_id_cookie { struct ofp_header header; uint32_t vendor; /* NX_VENDOR_ID. */ @@ -292,5 +342,545 @@ OFP_ASSERT(sizeof(struct nx_action_pop_queue) == 16); #define NXFW_ALL NXFW_TUN_ID #define OVSFW_ALL (OFPFW_ALL | NXFW_ALL) + +/* Flexible flow specifications (aka NXM = Nicira Extended Match). + * + * OpenFlow 1.0 has "struct ofp_match" for specifying flow matches. This + * structure is fixed-length and hence difficult to extend. This section + * describes a more flexible, variable-length flow match, called "nx_match" for + * short, that is also supported by Open vSwitch. This section also defines a + * replacement for each OpenFlow message that includes struct ofp_match. + * + * + * Format + * ====== + * + * An nx_match is a sequence of zero or more "nxm_entry"s, which are + * type-length-value (TLV) entries, each 5 to 259 (inclusive) bytes long. + * "nxm_entry"s are not aligned on or padded to any multibyte boundary. The + * first 4 bytes of an nxm_entry are its "header", followed by the entry's + * "body". + * + * An nxm_entry's header is interpreted as a 32-bit word in network byte order: + * + * |<-------------------- nxm_type ------------------>| + * | | + * |31 16 15 9| 8 7 0 + * +----------------------------------+---------------+--+------------------+ + * | nxm_vendor | nxm_field |hm| nxm_length | + * +----------------------------------+---------------+--+------------------+ + * + * The most-significant 23 bits of the header are collectively "nxm_type". + * Bits 16...31 are "nxm_vendor", one of the NXM_VENDOR_* values below. Bits + * 9...15 are "nxm_field", which is a vendor-specific value. nxm_type normally + * designates a protocol header, such as the Ethernet type, but it can also + * refer to packet metadata, such as the switch port on which a packet arrived. + * + * Bit 8 is "nxm_hasmask" (labeled "hm" above for space reasons). The meaning + * of this bit is explained later. + * + * The least-significant 8 bits are "nxm_length", a positive integer. The + * length of the nxm_entry, including the header, is exactly 4 + nxm_length + * bytes. + * + * For a given nxm_vendor, nxm_field, and nxm_hasmask value, nxm_length is a + * constant. It is included only to allow software to minimally parse + * "nxm_entry"s of unknown types. (Similarly, for a given nxm_vendor, + * nxm_field, and nxm_length, nxm_hasmask is a constant.) + * + * + * Semantics + * ========= + * + * A zero-length nx_match (one with no "nxm_entry"s) matches every packet. + * + * An nxm_entry places a constraint on the packets matched by the nx_match: + * + * - If nxm_hasmask is 0, the nxm_entry's body contains a value for the + * field, called "nxm_value". The nx_match matches only packets in which + * the field equals nxm_value. + * + * - If nxm_hasmask is 1, then the nxm_entry's body contains a value for the + * field (nxm_value), followed by a bitmask of the same length as the + * value, called "nxm_mask". For each 1-bit in position J in nxm_mask, the + * nx_match matches only packets for which bit J in the given field's value + * matches bit J in nxm_value. A 0-bit in nxm_mask causes the + * corresponding bits in nxm_value and the field's value to be ignored. + * (The sense of the nxm_mask bits is the opposite of that used by the + * "wildcards" member of struct ofp_match.) + * + * When nxm_hasmask is 1, nxm_length is always even. + * + * An all-zero-bits nxm_mask is equivalent to omitting the nxm_entry + * entirely. An all-one-bits nxm_mask is equivalent to specifying 0 for + * nxm_hasmask. + * + * When there are multiple "nxm_entry"s, all of the constraints must be met. + * + * + * Mask Restrictions + * ================= + * + * Masks may be restricted: + * + * - Some nxm_types may not support masked wildcards, that is, nxm_hasmask + * must always be 0 when these fields are specified. For example, the + * field that identifies the port on which a packet was received may not be + * masked. + * + * - Some nxm_types that do support masked wildcards may only support certain + * nxm_mask patterns. For example, fields that have IPv4 address values + * may be restricted to CIDR masks. + * + * These restrictions should be noted in specifications for individual fields. + * A switch may accept an nxm_hasmask or nxm_mask value that the specification + * disallows, if the switch correctly implements support for that nxm_hasmask + * or nxm_mask value. A switch must reject an attempt to set up a flow that + * contains a nxm_hasmask or nxm_mask value that it does not support. + * + * + * Prerequisite Restrictions + * ========================= + * + * The presence of an nxm_entry with a given nxm_type may be restricted based + * on the presence of or values of other "nxm_entry"s. For example: + * + * - An nxm_entry for nxm_type=NXM_OF_IP_TOS is allowed only if it is + * preceded by another entry with nxm_type=NXM_OF_ETH_TYPE, nxm_hasmask=0, + * and nxm_value=0x0800. That is, matching on the IP source address is + * allowed only if the Ethernet type is explicitly set to IP. + * + * - An nxm_entry for nxm_type=NXM_OF_TCP_SRC is allowed only if it is preced + * by an entry with nxm_type=NXM_OF_ETH_TYPE, nxm_hasmask=0, + * nxm_value=0x0800 and another with nxm_type=NXM_OF_IP_PROTO, + * nxm_hasmask=0, nxm_value=6, in that order. That is, matching on the TCP + * source port is allowed only if the Ethernet type is IP and the IP + * protocol is TCP. + * + * These restrictions should be noted in specifications for individual fields. + * A switch may implement relaxed versions of these restrictions. A switch + * must reject an attempt to set up a flow that violates its restrictions. + * + * + * Ordering Restrictions + * ===================== + * + * An nxm_entry that has prerequisite restrictions must appear after the + * "nxm_entry"s for its prerequisites. Ordering of "nxm_entry"s within an + * nx_match is not otherwise constrained. + * + * Any given nxm_type may appear in an nx_match at most once. + * + * + * nxm_entry Examples + * ================== + * + * These examples show the format of a single nxm_entry with particular + * nxm_hasmask and nxm_length values. The diagrams are labeled with field + * numbers and byte indexes. + * + * + * 8-bit nxm_value, nxm_hasmask=1, nxm_length=1: + * + * 0 3 4 5 + * +------------+---+---+ + * | header | v | m | + * +------------+---+---+ + * + * + * 16-bit nxm_value, nxm_hasmask=0, nxm_length=2: + * + * 0 3 4 5 + * +------------+------+ + * | header | value| + * +------------+------+ + * + * + * 32-bit nxm_value, nxm_hasmask=0, nxm_length=4: + * + * 0 3 4 7 + * +------------+-------------+ + * | header | nxm_value | + * +------------+-------------+ + * + * + * 48-bit nxm_value, nxm_hasmask=0, nxm_length=6: + * + * 0 3 4 9 + * +------------+------------------+ + * | header | nxm_value | + * +------------+------------------+ + * + * + * 48-bit nxm_value, nxm_hasmask=1, nxm_length=12: + * + * 0 3 4 9 10 15 + * +------------+------------------+------------------+ + * | header | nxm_value | nxm_mask | + * +------------+------------------+------------------+ + * + * + * Error Reporting + * =============== + * + * A switch should report an error in an nx_match using error type + * OFPET_BAD_REQUEST and one of the NXBRC_NXM_* codes. Ideally the switch + * should report a specific error code, if one is assigned for the particular + * problem, but NXBRC_NXM_INVALID is also available to report a generic + * nx_match error. + */ + +#define NXM_HEADER__(VENDOR, FIELD, HASMASK, LENGTH) \ + (((VENDOR) << 16) | ((FIELD) << 9) | ((HASMASK) << 8) | (LENGTH)) +#define NXM_HEADER(VENDOR, FIELD, LENGTH) \ + NXM_HEADER__(VENDOR, FIELD, 0, LENGTH) +#define NXM_HEADER_W(VENDOR, FIELD, LENGTH) \ + NXM_HEADER__(VENDOR, FIELD, 1, (LENGTH) * 2) +#define NXM_VENDOR(HEADER) ((HEADER) >> 16) +#define NXM_FIELD(HEADER) (((HEADER) >> 9) & 0x7f) +#define NXM_TYPE(HEADER) (((HEADER) >> 9) & 0x7fffff) +#define NXM_HASMASK(HEADER) (((HEADER) >> 8) & 1) +#define NXM_LENGTH(HEADER) ((HEADER) & 0xff) + +#define NXM_MAKE_WILD_HEADER(HEADER) \ + NXM_HEADER_W(NXM_VENDOR(HEADER), NXM_FIELD(HEADER), NXM_LENGTH(HEADER)) + +/* ## ------------------------------- ## */ +/* ## OpenFlow 1.0-compatible fields. ## */ +/* ## ------------------------------- ## */ + +/* Physical or virtual port on which the packet was received. + * + * Prereqs: None. + * + * Format: 16-bit integer in network byte order. + * + * Masking: Not maskable. */ +#define NXM_OF_IN_PORT NXM_HEADER (0x0000, 0, 2) + +/* Source or destination address in Ethernet header. + * + * Prereqs: None. + * + * Format: 48-bit Ethernet MAC address. + * + * Masking: Not maskable. */ +#define NXM_OF_ETH_DST NXM_HEADER (0x0000, 1, 6) +#define NXM_OF_ETH_SRC NXM_HEADER (0x0000, 2, 6) + +/* Packet's Ethernet type. + * + * For an Ethernet II packet this is taken from the Ethernet header. For an + * 802.2 LLC+SNAP header with OUI 00-00-00 this is taken from the SNAP header. + * A packet that has neither format has value 0x05ff + * (OFP_DL_TYPE_NOT_ETH_TYPE). + * + * For a packet with an 802.1Q header, this is the type of the encapsulated + * frame. + * + * Prereqs: None. + * + * Format: 16-bit integer in network byte order. + * + * Masking: Not maskable. */ +#define NXM_OF_ETH_TYPE NXM_HEADER (0x0000, 3, 2) + +/* 802.1Q TCI. + * + * For a packet with an 802.1Q header, this is the Tag Control Information + * (TCI) field, with the CFI bit forced to 1. For a packet with no 802.1Q + * header, this has value 0. + * + * Prereqs: None. + * + * Format: 16-bit integer in network byte order. + * + * Masking: Arbitrary masks. + * + * This field can be used in various ways: + * + * - If it is not constrained at all, the nx_match matches packets without + * an 802.1Q header or with an 802.1Q header that has any TCI value. + * + * - Testing for an exact match with 0 matches only packets without an + * 802.1Q header. + * + * - Testing for an exact match with a TCI value with CFI=1 matches packets + * that have an 802.1Q header with a specified VID and PCP. + * + * - Testing for an exact match with a nonzero TCI value with CFI=0 does + * not make sense. The switch may reject this combination. + * + * - Testing with a specific VID and CFI=1, with nxm_mask=0x1fff, matches + * packets that have an 802.1Q header with that VID (and any PCP). + * + * - Testing with a specific PCP and CFI=1, with nxm_mask=0xf000, matches + * packets that have an 802.1Q header with that PCP (and any VID). + * + * - Testing with nxm_value=0, nxm_mask=0xe000 matches packets with no 802.1Q + * header or with an 802.1Q header with a VID of 0. + */ +#define NXM_OF_VLAN_TCI NXM_HEADER (0x0000, 4, 2) +#define NXM_OF_VLAN_TCI_W NXM_HEADER_W(0x0000, 4, 2) + +/* The "type of service" byte of the IP header, with the ECN bits forced to 0. + * + * Prereqs: NXM_OF_ETH_TYPE must match 0x0800 exactly. + * + * Format: 8-bit integer with 2 least-significant bits forced to 0. + * + * Masking: Not maskable. */ +#define NXM_OF_IP_TOS NXM_HEADER (0x0000, 5, 1) + +/* The "protocol" byte in the IP header. + * + * Prereqs: NXM_OF_ETH_TYPE must match 0x0800 exactly. + * + * Format: 8-bit integer. + * + * Masking: Not maskable. */ +#define NXM_OF_IP_PROTO NXM_HEADER (0x0000, 6, 1) + +/* The source or destination address in the IP header. + * + * Prereqs: NXM_OF_ETH_TYPE must match 0x0800 exactly. + * + * Format: 32-bit integer in network byte order. + * + * Masking: Only CIDR masks are allowed, that is, masks that consist of N + * high-order bits set to 1 and the other 32-N bits set to 0. */ +#define NXM_OF_IP_SRC NXM_HEADER (0x0000, 7, 4) +#define NXM_OF_IP_SRC_W NXM_HEADER_W(0x0000, 7, 4) +#define NXM_OF_IP_DST NXM_HEADER (0x0000, 8, 4) +#define NXM_OF_IP_DST_W NXM_HEADER_W(0x0000, 8, 4) + +/* The source or destination port in the TCP header. + * + * Prereqs: + * NXM_OF_ETH_TYPE must match 0x0800 exactly. + * NXM_OF_IP_PROTO must match 6 exactly. + * + * Format: 16-bit integer in network byte order. + * + * Masking: Not maskable. */ +#define NXM_OF_TCP_SRC NXM_HEADER (0x0000, 9, 2) +#define NXM_OF_TCP_DST NXM_HEADER (0x0000, 10, 2) + +/* The source or destination port in the UDP header. + * + * Prereqs: + * NXM_OF_ETH_TYPE must match 0x0800 exactly. + * NXM_OF_IP_PROTO must match 17 exactly. + * + * Format: 16-bit integer in network byte order. + * + * Masking: Not maskable. */ +#define NXM_OF_UDP_SRC NXM_HEADER (0x0000, 11, 2) +#define NXM_OF_UDP_DST NXM_HEADER (0x0000, 12, 2) + +/* The type or code in the ICMP header. + * + * Prereqs: + * NXM_OF_ETH_TYPE must match 0x0800 exactly. + * NXM_OF_IP_PROTO must match 1 exactly. + * + * Format: 8-bit integer. + * + * Masking: Not maskable. */ +#define NXM_OF_ICMP_TYPE NXM_HEADER (0x0000, 13, 1) +#define NXM_OF_ICMP_CODE NXM_HEADER (0x0000, 14, 1) + +/* ARP opcode. + * + * For an Ethernet+IP ARP packet, the opcode in the ARP header. Always 0 + * otherwise. Only ARP opcodes between 1 and 255 should be specified for + * matching. + * + * Prereqs: NXM_OF_ETH_TYPE must match 0x0806 exactly. + * + * Format: 16-bit integer in network byte order. + * + * Masking: Not maskable. */ +#define NXM_OF_ARP_OP NXM_HEADER (0x0000, 15, 2) + +/* For an Ethernet+IP ARP packet, the source or target protocol address + * in the ARP header. Always 0 otherwise. + * + * Prereqs: NXM_OF_ETH_TYPE must match 0x0806 exactly. + * + * Format: 32-bit integer in network byte order. + * + * Masking: Only CIDR masks are allowed, that is, masks that consist of N + * high-order bits set to 1 and the other 32-N bits set to 0. */ +#define NXM_OF_ARP_SPA NXM_HEADER (0x0000, 16, 4) +#define NXM_OF_ARP_SPA_W NXM_HEADER_W(0x0000, 16, 4) +#define NXM_OF_ARP_TPA NXM_HEADER (0x0000, 17, 4) +#define NXM_OF_ARP_TPA_W NXM_HEADER_W(0x0000, 17, 4) + +/* ## ------------------------ ## */ +/* ## Nicira match extensions. ## */ +/* ## ------------------------ ## */ + +/* Tunnel ID. + * + * For a packet received via GRE tunnel including a (32-bit) key, the key is + * stored in the low 32-bits and the high bits are zeroed. For other packets, + * the value is 0. + * + * Prereqs: None. + * + * Format: 64-bit integer in network byte order. + * + * Masking: Arbitrary masks. */ +#define NXM_NX_TUN_ID NXM_HEADER (0x0001, 16, 8) +#define NXM_NX_TUN_ID_W NXM_HEADER_W(0x0001, 16, 8) + +/* ## --------------------- ## */ +/* ## Requests and replies. ## */ +/* ## --------------------- ## */ + +enum { + NXFF_OPENFLOW10 = 0, /* Standard OpenFlow 1.0 compatible. */ + NXFF_TUN_ID_FROM_COOKIE = 1, /* OpenFlow 1.0, plus obtain tunnel ID from + * cookie. */ + NXFF_NXM = 2 /* Nicira extended match. */ +}; + +/* NXT_SET_FLOW_FORMAT request. */ +struct nxt_set_flow_format { + struct ofp_header header; + ovs_be32 vendor; /* NX_VENDOR_ID. */ + ovs_be32 subtype; /* NXT_SET_FLOW_FORMAT. */ + ovs_be32 format; /* One of NXFF_*. */ +}; +OFP_ASSERT(sizeof(struct nxt_set_flow_format) == 20); + +/* NXT_FLOW_MOD (analogous to OFPT_FLOW_MOD). */ +struct nx_flow_mod { + struct nicira_header nxh; + ovs_be64 cookie; /* Opaque controller-issued identifier. */ + ovs_be16 command; /* One of OFPFC_*. */ + ovs_be16 idle_timeout; /* Idle time before discarding (seconds). */ + ovs_be16 hard_timeout; /* Max time before discarding (seconds). */ + ovs_be16 priority; /* Priority level of flow entry. */ + ovs_be32 buffer_id; /* Buffered packet to apply to (or -1). + Not meaningful for OFPFC_DELETE*. */ + ovs_be16 out_port; /* For OFPFC_DELETE* commands, require + matching entries to include this as an + output port. A value of OFPP_NONE + indicates no restriction. */ + ovs_be16 flags; /* One of OFPFF_*. */ + ovs_be16 match_len; /* Size of nx_match. */ + uint8_t pad[6]; /* Align to 64-bits. */ + /* Followed by: + * - Exactly match_len (possibly 0) bytes containing the nx_match, then + * - Exactly (match_len + 7)/8*8 - match_len (between 0 and 7) bytes of + * all-zero bytes, then + * - Actions to fill out the remainder of the message length (always a + * multiple of 8). + */ +}; +OFP_ASSERT(sizeof(struct nx_flow_mod) == 48); + +/* NXT_FLOW_REMOVED (analogous to OFPT_FLOW_REMOVED). */ +struct nx_flow_removed { + struct nicira_header nxh; + ovs_be64 cookie; /* Opaque controller-issued identifier. */ + ovs_be16 priority; /* Priority level of flow entry. */ + uint8_t reason; /* One of OFPRR_*. */ + uint8_t pad[1]; /* Align to 32-bits. */ + ovs_be32 duration_sec; /* Time flow was alive in seconds. */ + ovs_be32 duration_nsec; /* Time flow was alive in nanoseconds beyond + duration_sec. */ + ovs_be16 idle_timeout; /* Idle timeout from original flow mod. */ + ovs_be16 match_len; /* Size of nx_match. */ + ovs_be64 packet_count; + ovs_be64 byte_count; + /* Followed by: + * - Exactly match_len (possibly 0) bytes containing the nx_match, then + * - Exactly (match_len + 7)/8*8 - match_len (between 0 and 7) bytes of + * all-zero bytes. */ +}; +OFP_ASSERT(sizeof(struct nx_flow_removed) == 56); + +/* Nicira vendor stats request of type NXST_FLOW (analogous to OFPST_FLOW + * request). */ +struct nx_flow_stats_request { + struct nicira_stats_msg nsm; + ovs_be16 out_port; /* Require matching entries to include this + as an output port. A value of OFPP_NONE + indicates no restriction. */ + ovs_be16 match_len; /* Length of nx_match. */ + uint8_t table_id; /* ID of table to read (from ofp_table_stats) + or 0xff for all tables. */ + uint8_t pad[3]; /* Align to 64 bits. */ + /* Followed by: + * - Exactly match_len (possibly 0) bytes containing the nx_match, then + * - Exactly (match_len + 7)/8*8 - match_len (between 0 and 7) bytes of + * all-zero bytes, which must also exactly fill out the length of the + * message. + */ +}; +OFP_ASSERT(sizeof(struct nx_flow_stats_request) == 32); + +/* Body for Nicira vendor stats reply of type NXST_FLOW (analogous to + * OFPST_FLOW reply). */ +struct nx_flow_stats { + ovs_be16 length; /* Length of this entry. */ + uint8_t table_id; /* ID of table flow came from. */ + uint8_t pad; + ovs_be32 duration_sec; /* Time flow has been alive in seconds. */ + ovs_be32 duration_nsec; /* Time flow has been alive in nanoseconds + beyond duration_sec. */ + ovs_be16 priority; /* Priority of the entry. Only meaningful + when this is not an exact-match entry. */ + ovs_be16 idle_timeout; /* Number of seconds idle before expiration. */ + ovs_be16 hard_timeout; /* Number of seconds before expiration. */ + ovs_be16 match_len; /* Length of nx_match. */ + uint8_t pad2[4]; /* Align to 64 bits. */ + ovs_be64 cookie; /* Opaque controller-issued identifier. */ + ovs_be64 packet_count; /* Number of packets in flow. */ + ovs_be64 byte_count; /* Number of bytes in flow. */ + /* Followed by: + * - Exactly match_len (possibly 0) bytes containing the nx_match, then + * - Exactly (match_len + 7)/8*8 - match_len (between 0 and 7) bytes of + * all-zero bytes, then + * - Actions to fill out the remainder 'length' bytes (always a multiple + * of 8). + */ +}; +OFP_ASSERT(sizeof(struct nx_flow_stats) == 48); + +/* Nicira vendor stats request of type NXST_AGGREGATE (analogous to + * OFPST_AGGREGATE request). */ +struct nx_aggregate_stats_request { + struct nicira_stats_msg nsm; + ovs_be16 out_port; /* Require matching entries to include this + as an output port. A value of OFPP_NONE + indicates no restriction. */ + ovs_be16 match_len; /* Length of nx_match. */ + uint8_t table_id; /* ID of table to read (from ofp_table_stats) + or 0xff for all tables. */ + uint8_t pad[3]; /* Align to 64 bits. */ + /* Followed by: + * - Exactly match_len (possibly 0) bytes containing the nx_match, then + * - Exactly (match_len + 7)/8*8 - match_len (between 0 and 7) bytes of + * all-zero bytes, which must also exactly fill out the length of the + * message. + */ +}; +OFP_ASSERT(sizeof(struct nx_aggregate_stats_request) == 32); + +/* Body for nicira_stats_msg reply of type NXST_AGGREGATE (analogous to + * OFPST_AGGREGATE reply). + * + * ofp_aggregate_stats_reply does not contain an ofp_match structure, so we + * reuse it entirely. (It would be very odd to use OFPST_AGGREGATE to reply to + * an NXST_AGGREGATE request, so we don't do that.) */ +struct nx_aggregate_stats_reply { + struct nicira_stats_msg nsm; + struct ofp_aggregate_stats_reply asr; +}; +OFP_ASSERT(sizeof(struct nx_aggregate_stats_reply) == 48); #endif /* openflow/nicira-ext.h */ diff --git a/lib/automake.mk b/lib/automake.mk index 5cfac240..31af3c9d 100644 --- a/lib/automake.mk +++ b/lib/automake.mk @@ -68,6 +68,8 @@ lib_libopenvswitch_a_SOURCES = \ lib/netdev-provider.h \ lib/netdev.c \ lib/netdev.h \ + lib/nx-match.c \ + lib/nx-match.h \ lib/odp-util.c \ lib/odp-util.h \ lib/ofp-parse.c \ diff --git a/lib/nx-match.c b/lib/nx-match.c new file mode 100644 index 00000000..e2330fd5 --- /dev/null +++ b/lib/nx-match.c @@ -0,0 +1,709 @@ +/* + * Copyright (c) 2010 Nicira Networks. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "nx-match.h" + +#include "classifier.h" +#include "dynamic-string.h" +#include "ofp-util.h" +#include "ofpbuf.h" +#include "openflow/nicira-ext.h" +#include "packets.h" +#include "unaligned.h" +#include "vlog.h" + +VLOG_DEFINE_THIS_MODULE(nx_match); + +/* Rate limit for nx_match parse errors. These always indicate a bug in the + * peer and so there's not much point in showing a lot of them. */ +static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + +enum { + NXM_INVALID = OFP_MKERR_NICIRA(OFPET_BAD_REQUEST, NXBRC_NXM_INVALID), + NXM_BAD_TYPE = OFP_MKERR_NICIRA(OFPET_BAD_REQUEST, NXBRC_NXM_BAD_TYPE), + NXM_BAD_VALUE = OFP_MKERR_NICIRA(OFPET_BAD_REQUEST, NXBRC_NXM_BAD_VALUE), + NXM_BAD_MASK = OFP_MKERR_NICIRA(OFPET_BAD_REQUEST, NXBRC_NXM_BAD_MASK), + NXM_BAD_PREREQ = OFP_MKERR_NICIRA(OFPET_BAD_REQUEST, NXBRC_NXM_BAD_PREREQ), + NXM_DUP_TYPE = OFP_MKERR_NICIRA(OFPET_BAD_REQUEST, NXBRC_NXM_DUP_TYPE), + BAD_ARGUMENT = OFP_MKERR(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT) +}; + +/* For each NXM_* field, define NFI_NXM_* as consecutive integers starting from + * zero. */ +enum nxm_field_index { +#define DEFINE_FIELD(HEADER, WILDCARD, DL_TYPE, NW_PROTO) NFI_NXM_##HEADER, +#include "nx-match.def" + N_NXM_FIELDS +}; + +struct nxm_field { + struct hmap_node hmap_node; + enum nxm_field_index index; /* NFI_* value. */ + uint32_t header; /* NXM_* value. */ + uint32_t wildcard; /* Wildcard bit, if exactly one. */ + ovs_be16 dl_type; /* dl_type prerequisite, if nonzero. */ + uint8_t nw_proto; /* nw_proto prerequisite, if nonzero. */ + const char *name; /* "NXM_*" string. */ +}; + +/* All the known fields. */ +static struct nxm_field nxm_fields[N_NXM_FIELDS] = { +#define DEFINE_FIELD(HEADER, WILDCARD, DL_TYPE, NW_PROTO) \ + { HMAP_NODE_NULL_INITIALIZER, NFI_NXM_##HEADER, NXM_##HEADER, WILDCARD, \ + CONSTANT_HTONS(DL_TYPE), NW_PROTO, "NXM_" #HEADER }, +#include "nx-match.def" +}; + +/* Hash table of 'nxm_fields'. */ +static struct hmap all_nxm_fields = HMAP_INITIALIZER(&all_nxm_fields); + +static void +nxm_init(void) +{ + if (hmap_is_empty(&all_nxm_fields)) { + int i; + + for (i = 0; i < N_NXM_FIELDS; i++) { + struct nxm_field *f = &nxm_fields[i]; + hmap_insert(&all_nxm_fields, &f->hmap_node, + hash_int(f->header, 0)); + } + + /* Verify that the header values are unique (duplicate "case" values + * cause a compile error). */ + switch (0) { +#define DEFINE_FIELD(HEADER, WILDCARD, DL_TYPE, NW_PROTO) \ + case NXM_##HEADER: break; +#include "nx-match.def" + } + } +} + +static const struct nxm_field * +nxm_field_lookup(uint32_t header) +{ + struct nxm_field *f; + + nxm_init(); + + HMAP_FOR_EACH_WITH_HASH (f, hmap_node, hash_int(header, 0), + &all_nxm_fields) { + if (f->header == header) { + return f; + } + } + + return NULL; +} + +/* Returns the width of the data for a field with the given 'header', in + * bytes. */ +static int +nxm_field_bytes(uint32_t header) +{ + unsigned int length = NXM_LENGTH(header); + return NXM_HASMASK(header) ? length / 2 : length; +} + +/* nx_pull_match() and helpers. */ + +static int +parse_tci(struct cls_rule *rule, ovs_be16 tci, ovs_be16 mask) +{ + enum { OFPFW_DL_TCI = OFPFW_DL_VLAN | OFPFW_DL_VLAN_PCP }; + if ((rule->wc.wildcards & OFPFW_DL_TCI) != OFPFW_DL_TCI) { + return NXM_DUP_TYPE; + } else { + return cls_rule_set_dl_tci_masked(rule, tci, mask) ? 0 : NXM_INVALID; + } +} + +static int +parse_nxm_entry(struct cls_rule *rule, const struct nxm_field *f, + const void *value, const void *mask) +{ + struct flow_wildcards *wc = &rule->wc; + struct flow *flow = &rule->flow; + + switch (f->index) { + /* Metadata. */ + case NFI_NXM_OF_IN_PORT: + flow->in_port = ntohs(get_unaligned_u16(value)); + if (flow->in_port == OFPP_LOCAL) { + flow->in_port = ODPP_LOCAL; + } + return 0; + + /* Ethernet header. */ + case NFI_NXM_OF_ETH_DST: + memcpy(flow->dl_dst, value, ETH_ADDR_LEN); + return 0; + case NFI_NXM_OF_ETH_SRC: + memcpy(flow->dl_src, value, ETH_ADDR_LEN); + return 0; + case NFI_NXM_OF_ETH_TYPE: + flow->dl_type = get_unaligned_u16(value); + return 0; + + /* 802.1Q header. */ + case NFI_NXM_OF_VLAN_TCI: + return parse_tci(rule, get_unaligned_u16(value), htons(UINT16_MAX)); + + case NFI_NXM_OF_VLAN_TCI_W: + return parse_tci(rule, get_unaligned_u16(value), + get_unaligned_u16(mask)); + + /* IP header. */ + case NFI_NXM_OF_IP_TOS: + if (*(uint8_t *) value & 0x03) { + return NXM_BAD_VALUE; + } else { + flow->nw_tos = *(uint8_t *) value; + return 0; + } + case NFI_NXM_OF_IP_PROTO: + flow->nw_proto = *(uint8_t *) value; + return 0; + + /* IP addresses in IP and ARP headers. */ + case NFI_NXM_OF_IP_SRC: + case NFI_NXM_OF_ARP_SPA: + if (wc->nw_src_mask) { + return NXM_DUP_TYPE; + } else { + cls_rule_set_nw_src(rule, get_unaligned_u32(value)); + return 0; + } + case NFI_NXM_OF_IP_SRC_W: + case NFI_NXM_OF_ARP_SPA_W: + if (wc->nw_src_mask) { + return NXM_DUP_TYPE; + } else { + ovs_be32 ip = get_unaligned_u32(value); + ovs_be32 netmask = get_unaligned_u32(mask); + if (!cls_rule_set_nw_src_masked(rule, ip, netmask)) { + return NXM_BAD_MASK; + } + return 0; + } + case NFI_NXM_OF_IP_DST: + case NFI_NXM_OF_ARP_TPA: + if (wc->nw_dst_mask) { + return NXM_DUP_TYPE; + } else { + cls_rule_set_nw_dst(rule, get_unaligned_u32(value)); + return 0; + } + case NFI_NXM_OF_IP_DST_W: + case NFI_NXM_OF_ARP_TPA_W: + if (wc->nw_dst_mask) { + return NXM_DUP_TYPE; + } else { + ovs_be32 ip = get_unaligned_u32(value); + ovs_be32 netmask = get_unaligned_u32(mask); + if (!cls_rule_set_nw_dst_masked(rule, ip, netmask)) { + return NXM_BAD_MASK; + } + return 0; + } + + /* TCP header. */ + case NFI_NXM_OF_TCP_SRC: + flow->tp_src = get_unaligned_u16(value); + return 0; + case NFI_NXM_OF_TCP_DST: + flow->tp_dst = get_unaligned_u16(value); + return 0; + + /* UDP header. */ + case NFI_NXM_OF_UDP_SRC: + flow->tp_src = get_unaligned_u16(value); + return 0; + case NFI_NXM_OF_UDP_DST: + flow->tp_dst = get_unaligned_u16(value); + return 0; + + /* ICMP header. */ + case NFI_NXM_OF_ICMP_TYPE: + flow->tp_src = htons(*(uint8_t *) value); + return 0; + case NFI_NXM_OF_ICMP_CODE: + flow->tp_dst = htons(*(uint8_t *) value); + return 0; + + /* ARP header. */ + case NFI_NXM_OF_ARP_OP: + if (ntohs(get_unaligned_u16(value)) > 255) { + return NXM_BAD_VALUE; + } else { + flow->nw_proto = ntohs(get_unaligned_u16(value)); + return 0; + } + + /* Tunnel ID. */ + case NFI_NXM_NX_TUN_ID: + flow->tun_id = htonl(ntohll(get_unaligned_u64(value))); + return 0; + + case N_NXM_FIELDS: + NOT_REACHED(); + } + NOT_REACHED(); +} + +static bool +nxm_prereqs_ok(const struct nxm_field *field, const struct flow *flow) +{ + return (!field->dl_type + || (field->dl_type == flow->dl_type + && (!field->nw_proto || field->nw_proto == flow->nw_proto))); +} + +static uint32_t +nx_entry_ok(const void *p, unsigned int match_len) +{ + unsigned int payload_len; + ovs_be32 header_be; + uint32_t header; + + if (match_len < 4) { + if (match_len) { + VLOG_DBG_RL(&rl, "nx_match ends with partial nxm_header"); + } + return 0; + } + memcpy(&header_be, p, 4); + header = ntohl(header_be); + + payload_len = NXM_LENGTH(header); + if (!payload_len) { + VLOG_DBG_RL(&rl, "nxm_entry %08"PRIx32" has invalid payload " + "length 0", header); + return 0; + } + if (match_len < payload_len + 4) { + VLOG_DBG_RL(&rl, "%"PRIu32"-byte nxm_entry but only " + "%u bytes left in nx_match", payload_len + 4, match_len); + return 0; + } + + return header; +} + +int +nx_pull_match(struct ofpbuf *b, unsigned int match_len, uint16_t priority, + struct cls_rule *rule) +{ + uint32_t header; + uint8_t *p; + + p = ofpbuf_try_pull(b, ROUND_UP(match_len, 8)); + if (!p) { + VLOG_DBG_RL(&rl, "nx_match length %zu, rounded up to a " + "multiple of 8, is longer than space in message (max " + "length %zu)", match_len, b->size); + return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); + } + + cls_rule_init_catchall(rule, priority); + while ((header = nx_entry_ok(p, match_len)) != 0) { + unsigned length = NXM_LENGTH(header); + const struct nxm_field *f; + int error; + + f = nxm_field_lookup(header); + if (!f) { + error = NXM_BAD_TYPE; + } else if (!nxm_prereqs_ok(f, &rule->flow)) { + error = NXM_BAD_PREREQ; + } else if (f->wildcard && !(rule->wc.wildcards & f->wildcard)) { + error = NXM_DUP_TYPE; + } else { + /* 'hasmask' and 'length' are known to be correct at this point + * because they are included in 'header' and nxm_field_lookup() + * checked them already. */ + rule->wc.wildcards &= ~f->wildcard; + error = parse_nxm_entry(rule, f, p + 4, p + 4 + length / 2); + } + if (error) { + VLOG_DBG_RL(&rl, "bad nxm_entry with vendor=%"PRIu32", " + "field=%"PRIu32", hasmask=%"PRIu32", type=%"PRIu32" " + "(error %x)", + NXM_VENDOR(header), NXM_FIELD(header), + NXM_HASMASK(header), NXM_TYPE(header), + error); + return error; + } + + + p += 4 + length; + match_len -= 4 + length; + } + + return match_len ? NXM_INVALID : 0; +} + +/* nx_put_match() and helpers. + * + * 'put' functions whose names end in 'w' add a wildcarded field. + * 'put' functions whose names end in 'm' add a field that might be wildcarded. + * Other 'put' functions add exact-match fields. + */ + +static void +nxm_put_header(struct ofpbuf *b, uint32_t header) +{ + ovs_be32 n_header = htonl(header); + ofpbuf_put(b, &n_header, sizeof n_header); +} + +static void +nxm_put_8(struct ofpbuf *b, uint32_t header, uint8_t value) +{ + nxm_put_header(b, header); + ofpbuf_put(b, &value, sizeof value); +} + +static void +nxm_put_16(struct ofpbuf *b, uint32_t header, ovs_be16 value) +{ + nxm_put_header(b, header); + ofpbuf_put(b, &value, sizeof value); +} + +static void +nxm_put_16w(struct ofpbuf *b, uint32_t header, ovs_be16 value, ovs_be16 mask) +{ + nxm_put_header(b, header); + ofpbuf_put(b, &value, sizeof value); + ofpbuf_put(b, &mask, sizeof mask); +} + +static void +nxm_put_32(struct ofpbuf *b, uint32_t header, ovs_be32 value) +{ + nxm_put_header(b, header); + ofpbuf_put(b, &value, sizeof value); +} + +static void +nxm_put_32w(struct ofpbuf *b, uint32_t header, ovs_be32 value, ovs_be32 mask) +{ + nxm_put_header(b, header); + ofpbuf_put(b, &value, sizeof value); + ofpbuf_put(b, &mask, sizeof mask); +} + +static void +nxm_put_32m(struct ofpbuf *b, uint32_t header, ovs_be32 value, ovs_be32 mask) +{ + switch (mask) { + case 0: + break; + + case UINT32_MAX: + nxm_put_32(b, header, value); + break; + + default: + nxm_put_32w(b, NXM_MAKE_WILD_HEADER(header), value, mask); + break; + } +} + +static void +nxm_put_64(struct ofpbuf *b, uint32_t header, ovs_be64 value) +{ + nxm_put_header(b, header); + ofpbuf_put(b, &value, sizeof value); +} + + +static void +nxm_put_eth(struct ofpbuf *b, uint32_t header, + const uint8_t value[ETH_ADDR_LEN]) +{ + nxm_put_header(b, header); + ofpbuf_put(b, value, ETH_ADDR_LEN); +} + +int +nx_put_match(struct ofpbuf *b, const struct cls_rule *cr) +{ + const uint32_t wc = cr->wc.wildcards; + const struct flow *flow = &cr->flow; + const size_t start_len = b->size; + ovs_be16 vid, pcp; + int match_len; + + /* Metadata. */ + if (!(wc & OFPFW_IN_PORT)) { + uint16_t in_port = flow->in_port; + if (in_port == ODPP_LOCAL) { + in_port = OFPP_LOCAL; + } + nxm_put_16(b, NXM_OF_IN_PORT, htons(in_port)); + } + + /* Ethernet. */ + if (!(wc & OFPFW_DL_DST)) { + nxm_put_eth(b, NXM_OF_ETH_DST, flow->dl_dst); + } + if (!(wc & OFPFW_DL_SRC)) { + nxm_put_eth(b, NXM_OF_ETH_SRC, flow->dl_src); + } + if (!(wc & OFPFW_DL_TYPE)) { + nxm_put_16(b, NXM_OF_ETH_TYPE, flow->dl_type); + } + + /* 802.1Q. */ + vid = flow->dl_vlan & htons(VLAN_VID_MASK); + pcp = htons((flow->dl_vlan_pcp << VLAN_PCP_SHIFT) & VLAN_PCP_MASK); + switch (wc & (OFPFW_DL_VLAN | OFPFW_DL_VLAN_PCP)) { + case OFPFW_DL_VLAN | OFPFW_DL_VLAN_PCP: + break; + case OFPFW_DL_VLAN: + nxm_put_16w(b, NXM_OF_VLAN_TCI_W, pcp | htons(VLAN_CFI), + htons(VLAN_PCP_MASK | VLAN_CFI)); + break; + case OFPFW_DL_VLAN_PCP: + if (flow->dl_vlan == htons(OFP_VLAN_NONE)) { + nxm_put_16(b, NXM_OF_VLAN_TCI, 0); + } else { + nxm_put_16w(b, NXM_OF_VLAN_TCI_W, vid | htons(VLAN_CFI), + htons(VLAN_VID_MASK | VLAN_CFI)); + } + break; + case 0: + if (flow->dl_vlan == htons(OFP_VLAN_NONE)) { + nxm_put_16(b, NXM_OF_VLAN_TCI, 0); + } else { + nxm_put_16(b, NXM_OF_VLAN_TCI, vid | pcp | htons(VLAN_CFI)); + } + break; + } + + if (!(wc & OFPFW_DL_TYPE) && flow->dl_type == htons(ETH_TYPE_IP)) { + /* IP. */ + if (!(wc & OFPFW_NW_TOS)) { + nxm_put_8(b, NXM_OF_IP_TOS, flow->nw_tos & 0xfc); + } + nxm_put_32m(b, NXM_OF_IP_SRC, flow->nw_src, cr->wc.nw_src_mask); + nxm_put_32m(b, NXM_OF_IP_DST, flow->nw_dst, cr->wc.nw_dst_mask); + + if (!(wc & OFPFW_NW_PROTO)) { + nxm_put_8(b, NXM_OF_IP_PROTO, flow->nw_proto); + switch (flow->nw_proto) { + /* TCP. */ + case IP_TYPE_TCP: + if (!(wc & OFPFW_TP_SRC)) { + nxm_put_16(b, NXM_OF_TCP_SRC, flow->tp_src); + } + if (!(wc & OFPFW_TP_DST)) { + nxm_put_16(b, NXM_OF_TCP_DST, flow->tp_dst); + } + break; + + /* UDP. */ + case IP_TYPE_UDP: + if (!(wc & OFPFW_TP_SRC)) { + nxm_put_16(b, NXM_OF_UDP_SRC, flow->tp_src); + } + if (!(wc & OFPFW_TP_DST)) { + nxm_put_16(b, NXM_OF_UDP_DST, flow->tp_dst); + } + break; + + /* ICMP. */ + case IP_TYPE_ICMP: + if (!(wc & OFPFW_TP_SRC)) { + nxm_put_8(b, NXM_OF_ICMP_TYPE, ntohs(flow->tp_src)); + } + if (!(wc & OFPFW_TP_DST)) { + nxm_put_8(b, NXM_OF_ICMP_CODE, ntohs(flow->tp_dst)); + } + break; + } + } + } else if (!(wc & OFPFW_DL_TYPE) && flow->dl_type == htons(ETH_TYPE_ARP)) { + /* ARP. */ + if (!(wc & OFPFW_NW_PROTO)) { + nxm_put_16(b, NXM_OF_ARP_OP, htons(flow->nw_proto)); + } + nxm_put_32m(b, NXM_OF_ARP_SPA, flow->nw_src, cr->wc.nw_src_mask); + nxm_put_32m(b, NXM_OF_ARP_TPA, flow->nw_dst, cr->wc.nw_dst_mask); + } + + /* Tunnel ID. */ + if (!(wc & NXFW_TUN_ID)) { + nxm_put_64(b, NXM_NX_TUN_ID, htonll(ntohl(flow->tun_id))); + } + + match_len = b->size - start_len; + ofpbuf_put_zeros(b, ROUND_UP(match_len, 8) - match_len); + return match_len; +} + +/* nx_match_to_string() and helpers. */ + +char * +nx_match_to_string(const uint8_t *p, unsigned int match_len) +{ + uint32_t header; + struct ds s; + + if (!match_len) { + return xstrdup(""); + } + + ds_init(&s); + while ((header = nx_entry_ok(p, match_len)) != 0) { + unsigned int length = NXM_LENGTH(header); + unsigned int value_len = nxm_field_bytes(header); + const uint8_t *value = p + 4; + const uint8_t *mask = value + value_len; + const struct nxm_field *f; + unsigned int i; + + if (s.length) { + ds_put_cstr(&s, ", "); + } + + f = nxm_field_lookup(header); + if (f) { + ds_put_cstr(&s, f->name); + } else { + ds_put_format(&s, "%d:%d", NXM_VENDOR(header), NXM_FIELD(header)); + } + + ds_put_char(&s, '('); + + for (i = 0; i < value_len; i++) { + ds_put_format(&s, "%02x", value[i]); + } + if (NXM_HASMASK(header)) { + ds_put_char(&s, '/'); + for (i = 0; i < value_len; i++) { + ds_put_format(&s, "%02x", mask[i]); + } + } + ds_put_char(&s, ')'); + + p += 4 + length; + match_len -= 4 + length; + } + + if (match_len) { + if (s.length) { + ds_put_cstr(&s, ", "); + } + + ds_put_format(&s, "<%u invalid bytes>", match_len); + } + + return ds_steal_cstr(&s); +} + +static const struct nxm_field * +lookup_nxm_field(const char *name, int name_len) +{ + const struct nxm_field *f; + + for (f = nxm_fields; f < &nxm_fields[ARRAY_SIZE(nxm_fields)]; f++) { + if (!strncmp(f->name, name, name_len) && f->name[name_len] == '\0') { + return f; + } + } + + return NULL; +} + +static const char * +parse_hex_bytes(struct ofpbuf *b, const char *s, unsigned int n) +{ + while (n--) { + int low, high; + uint8_t byte; + + s += strspn(s, " "); + low = hexit_value(*s); + high = low < 0 ? low : hexit_value(s[1]); + if (low < 0 || high < 0) { + ovs_fatal(0, "%.2s: hex digits expected", s); + } + + byte = 16 * low + high; + ofpbuf_put(b, &byte, 1); + s += 2; + } + return s; +} + +/* nx_match_from_string(). */ + +int +nx_match_from_string(const char *s, struct ofpbuf *b) +{ + const char *full_s = s; + const size_t start_len = b->size; + int match_len; + + if (!strcmp(s, "")) { + /* Ensure that 'b->data' isn't actually null. */ + ofpbuf_prealloc_tailroom(b, 1); + return 0; + } + + for (s += strspn(s, ", "); *s; s += strspn(s, ", ")) { + const struct nxm_field *f; + int name_len; + + name_len = strcspn(s, "("); + if (s[name_len] != '(') { + ovs_fatal(0, "%s: missing ( at end of nx_match", full_s); + } + + f = lookup_nxm_field(s, name_len); + if (!f) { + ovs_fatal(0, "%s: unknown field `%.*s'", full_s, name_len, s); + } + + s += name_len + 1; + + nxm_put_header(b, f->header); + s = parse_hex_bytes(b, s, nxm_field_bytes(f->header)); + if (NXM_HASMASK(f->header)) { + s += strspn(s, " "); + if (*s != '/') { + ovs_fatal(0, "%s: missing / in masked field %s", + full_s, f->name); + } + s = parse_hex_bytes(b, s + 1, nxm_field_bytes(f->header)); + } + + s += strspn(s, " "); + if (*s != ')') { + ovs_fatal(0, "%s: missing ) following field %s", full_s, f->name); + } + s++; + } + + match_len = b->size - start_len; + ofpbuf_put_zeros(b, ROUND_UP(match_len, 8) - match_len); + return match_len; +} diff --git a/lib/nx-match.def b/lib/nx-match.def new file mode 100644 index 00000000..ec5fe9b3 --- /dev/null +++ b/lib/nx-match.def @@ -0,0 +1,43 @@ +/* -*- c -*- + * Copyright (c) 2008, 2009, 2010 Nicira Networks. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define DEFINE_FIELD_M(HEADER, WILDCARD, DL_TYPE, NW_PROTO) \ + DEFINE_FIELD(HEADER, WILDCARD, DL_TYPE, NW_PROTO) \ + DEFINE_FIELD(HEADER##_W, WILDCARD, DL_TYPE, NW_PROTO) + +/* NXM_ bit OFPFW_* bit dl_type nw_proto */ +/* ------------ -------------- ----------- ------------- */ +DEFINE_FIELD (OF_IN_PORT, OFPFW_IN_PORT, 0, 0) +DEFINE_FIELD (OF_ETH_DST, OFPFW_DL_DST, 0, 0) +DEFINE_FIELD (OF_ETH_SRC, OFPFW_DL_SRC, 0, 0) +DEFINE_FIELD (OF_ETH_TYPE, OFPFW_DL_TYPE, 0, 0) +DEFINE_FIELD_M(OF_VLAN_TCI, 0, 0, 0) +DEFINE_FIELD (OF_IP_TOS, OFPFW_NW_TOS, ETH_TYPE_IP, 0) +DEFINE_FIELD (OF_IP_PROTO, OFPFW_NW_PROTO, ETH_TYPE_IP, 0) +DEFINE_FIELD_M(OF_IP_SRC, 0, ETH_TYPE_IP, 0) +DEFINE_FIELD_M(OF_IP_DST, 0, ETH_TYPE_IP, 0) +DEFINE_FIELD (OF_TCP_SRC, OFPFW_TP_SRC, ETH_TYPE_IP, IP_TYPE_TCP) +DEFINE_FIELD (OF_TCP_DST, OFPFW_TP_DST, ETH_TYPE_IP, IP_TYPE_TCP) +DEFINE_FIELD (OF_UDP_SRC, OFPFW_TP_SRC, ETH_TYPE_IP, IP_TYPE_UDP) +DEFINE_FIELD (OF_UDP_DST, OFPFW_TP_DST, ETH_TYPE_IP, IP_TYPE_UDP) +DEFINE_FIELD (OF_ICMP_TYPE, OFPFW_TP_SRC, ETH_TYPE_IP, IP_TYPE_ICMP) +DEFINE_FIELD (OF_ICMP_CODE, OFPFW_TP_DST, ETH_TYPE_IP, IP_TYPE_ICMP) +DEFINE_FIELD (OF_ARP_OP, OFPFW_NW_PROTO, ETH_TYPE_ARP, 0) +DEFINE_FIELD_M(OF_ARP_SPA, 0, ETH_TYPE_ARP, 0) +DEFINE_FIELD_M(OF_ARP_TPA, 0, ETH_TYPE_ARP, 0) +DEFINE_FIELD (NX_TUN_ID, NXFW_TUN_ID, 0, 0) + +#undef DEFINE_FIELD diff --git a/lib/nx-match.h b/lib/nx-match.h new file mode 100644 index 00000000..7bfea8c8 --- /dev/null +++ b/lib/nx-match.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2010 Nicira Networks. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NX_MATCH_H +#define NX_MATCH_H 1 + +#include + +struct cls_rule; +struct ofpbuf; + +/* Nicira Extended Match (NXM) flexible flow match helper functions. + * + * See include/openflow/nicira-ext.h for NXM specification. + */ + +int nx_pull_match(struct ofpbuf *, unsigned int match_len, uint16_t priority, + struct cls_rule *); +int nx_put_match(struct ofpbuf *, const struct cls_rule *); + +char *nx_match_to_string(const uint8_t *, unsigned int match_len); +int nx_match_from_string(const char *, struct ofpbuf *); + +/* Upper bound on the length of an nx_match. The longest nx_match (assuming + * we implement 4 registers) would be: + * + * header value mask total + * ------ ----- ---- ----- + * NXM_OF_IN_PORT 4 2 -- 6 + * NXM_OF_ETH_DST_W 4 6 6 16 + * NXM_OF_ETH_SRC 4 6 -- 10 + * NXM_OF_ETH_TYPE 4 2 -- 6 + * NXM_OF_VLAN_TCI 4 2 2 8 + * NXM_OF_IP_TOS 4 1 -- 5 + * NXM_OF_IP_PROTO 4 2 -- 6 + * NXM_OF_IP_SRC_W 4 4 4 12 + * NXM_OF_IP_DST_W 4 4 4 12 + * NXM_OF_TCP_SRC 4 2 -- 6 + * NXM_OF_TCP_DST 4 2 -- 6 + * NXM_NX_REG_W(0) 4 4 4 12 + * NXM_NX_REG_W(1) 4 4 4 12 + * NXM_NX_REG_W(2) 4 4 4 12 + * NXM_NX_REG_W(3) 4 4 4 12 + * NXM_NX_TUN_ID_W 4 8 8 20 + * ------------------------------------------- + * total 161 + * + * So this value is conservative. + */ +#define NXM_MAX_LEN 192 + +#endif /* nx-match.h */ diff --git a/lib/util.c b/lib/util.c index 39ca3b54..328cfac4 100644 --- a/lib/util.c +++ b/lib/util.c @@ -358,9 +358,10 @@ hexit_value(int c) case 'f': case 'F': return 0xf; - } - NOT_REACHED(); + default: + return -1; + } } /* Returns the current working directory as a malloc()'d string, or a null diff --git a/lib/vlog-modules.def b/lib/vlog-modules.def index e4f037f7..7e629948 100644 --- a/lib/vlog-modules.def +++ b/lib/vlog-modules.def @@ -44,6 +44,7 @@ VLOG_MODULE(netdev_linux) VLOG_MODULE(netdev_vport) VLOG_MODULE(netflow) VLOG_MODULE(netlink) +VLOG_MODULE(nx_match) VLOG_MODULE(ofctl) VLOG_MODULE(ofp_parse) VLOG_MODULE(ofp_util) diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index fb274652..618e8a56 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -37,6 +37,7 @@ #include "mac-learning.h" #include "netdev.h" #include "netflow.h" +#include "nx-match.h" #include "odp-util.h" #include "ofp-print.h" #include "ofp-util.h" @@ -3014,6 +3015,42 @@ append_ofp_stats_reply(size_t nbytes, struct ofconn *ofconn, return ofpbuf_put_uninit(*msgp, nbytes); } +static struct ofpbuf * +make_nxstats_reply(ovs_be32 xid, ovs_be32 subtype, size_t body_len) +{ + struct nicira_stats_msg *nsm; + struct ofpbuf *msg; + + msg = ofpbuf_new(MIN(sizeof *nsm + body_len, UINT16_MAX)); + nsm = put_openflow_xid(sizeof *nsm, OFPT_STATS_REPLY, xid, msg); + nsm->type = htons(OFPST_VENDOR); + nsm->flags = htons(0); + nsm->vendor = htonl(NX_VENDOR_ID); + nsm->subtype = htonl(subtype); + return msg; +} + +static struct ofpbuf * +start_nxstats_reply(const struct nicira_stats_msg *request, size_t body_len) +{ + return make_nxstats_reply(request->header.xid, request->subtype, body_len); +} + +static void +append_nxstats_reply(size_t nbytes, struct ofconn *ofconn, + struct ofpbuf **msgp) +{ + struct ofpbuf *msg = *msgp; + assert(nbytes <= UINT16_MAX - sizeof(struct nicira_stats_msg)); + if (nbytes + msg->size > UINT16_MAX) { + struct nicira_stats_msg *reply = msg->data; + reply->flags = htons(OFPSF_REPLY_MORE); + *msgp = make_nxstats_reply(reply->header.xid, reply->subtype, nbytes); + queue_tx(msg, ofconn, ofconn->reply_counter); + } + ofpbuf_prealloc_tailroom(*msgp, nbytes); +} + static int handle_desc_stats_request(struct ofconn *ofconn, struct ofp_stats_request *request) @@ -3266,6 +3303,73 @@ handle_flow_stats_request(struct ofconn *ofconn, return 0; } +static void +nx_flow_stats_cb(struct cls_rule *rule_, void *cbdata_) +{ + struct rule *rule = rule_from_cls_rule(rule_); + struct flow_stats_cbdata *cbdata = cbdata_; + struct nx_flow_stats *nfs; + uint64_t packet_count, byte_count; + size_t act_len, start_len; + + if (rule_is_hidden(rule) || !rule_has_out_port(rule, cbdata->out_port)) { + return; + } + + query_stats(cbdata->ofconn->ofproto, rule, &packet_count, &byte_count); + + act_len = sizeof *rule->actions * rule->n_actions; + + start_len = cbdata->msg->size; + append_nxstats_reply(sizeof *nfs + NXM_MAX_LEN + act_len, + cbdata->ofconn, &cbdata->msg); + nfs = ofpbuf_put_uninit(cbdata->msg, sizeof *nfs); + nfs->table_id = 0; + nfs->pad = 0; + calc_flow_duration(rule->created, &nfs->duration_sec, &nfs->duration_nsec); + nfs->cookie = rule->flow_cookie; + nfs->priority = htons(rule->cr.priority); + nfs->idle_timeout = htons(rule->idle_timeout); + nfs->hard_timeout = htons(rule->hard_timeout); + nfs->match_len = htons(nx_put_match(cbdata->msg, &rule->cr)); + memset(nfs->pad2, 0, sizeof nfs->pad2); + nfs->packet_count = htonll(packet_count); + nfs->byte_count = htonll(byte_count); + if (rule->n_actions > 0) { + ofpbuf_put(cbdata->msg, rule->actions, act_len); + } + nfs->length = htons(cbdata->msg->size - start_len); +} + +static int +handle_nxst_flow(struct ofconn *ofconn, struct ofpbuf *b) +{ + struct nx_flow_stats_request *nfsr; + struct flow_stats_cbdata cbdata; + struct cls_rule target; + int error; + + /* Dissect the message. */ + nfsr = ofpbuf_try_pull(b, sizeof *nfsr); + if (!nfsr) { + return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); + } + error = nx_pull_match(b, ntohs(nfsr->match_len), 0, &target); + if (error) { + return error; + } + + COVERAGE_INC(ofproto_flows_req); + cbdata.ofconn = ofconn; + cbdata.out_port = nfsr->out_port; + cbdata.msg = start_nxstats_reply(&nfsr->nsm, 1024); + classifier_for_each_match(&ofconn->ofproto->cls, &target, + table_id_to_include(nfsr->table_id), + nx_flow_stats_cb, &cbdata); + queue_tx(cbdata.msg, ofconn, ofconn->reply_counter); + return 0; +} + struct flow_stats_ds_cbdata { struct ofproto *ofproto; struct ds *results; @@ -3398,6 +3502,36 @@ handle_aggregate_stats_request(struct ofconn *ofconn, return 0; } +static int +handle_nxst_aggregate(struct ofconn *ofconn, struct ofpbuf *b) +{ + struct nx_aggregate_stats_request *request; + struct ofp_aggregate_stats_reply *reply; + struct cls_rule target; + struct ofpbuf *buf; + int error; + + /* Dissect the message. */ + request = ofpbuf_try_pull(b, sizeof *request); + if (!request) { + return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); + } + error = nx_pull_match(b, ntohs(request->match_len), 0, &target); + if (error) { + return error; + } + + /* Reply. */ + COVERAGE_INC(ofproto_flows_req); + buf = start_nxstats_reply(&request->nsm, sizeof *reply); + reply = ofpbuf_put_uninit(buf, sizeof *reply); + query_aggregate_stats(ofconn->ofproto, &target, request->out_port, + request->table_id, reply); + queue_tx(buf, ofconn, ofconn->reply_counter); + + return 0; +} + struct queue_stats_cbdata { struct ofconn *ofconn; struct ofport *ofport; @@ -3488,6 +3622,44 @@ handle_queue_stats_request(struct ofconn *ofconn, return 0; } +static int +handle_vendor_stats_request(struct ofconn *ofconn, + struct ofp_stats_request *osr, size_t arg_size) +{ + struct nicira_stats_msg *nsm; + struct ofpbuf b; + ovs_be32 vendor; + + if (arg_size < 4) { + VLOG_WARN_RL(&rl, "truncated vendor stats request body"); + return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); + } + + memcpy(&vendor, osr->body, sizeof vendor); + if (vendor != htonl(NX_VENDOR_ID)) { + return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_VENDOR); + } + + if (ntohs(nsm->header.length) < sizeof(struct nicira_stats_msg)) { + VLOG_WARN_RL(&rl, "truncated Nicira stats request"); + return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); + } + + nsm = (struct nicira_stats_msg *) osr; + b.data = nsm; + b.size = ntohs(nsm->header.length); + switch (ntohl(nsm->subtype)) { + case NXST_FLOW: + return handle_nxst_flow(ofconn, &b); + + case NXST_AGGREGATE: + return handle_nxst_aggregate(ofconn, &b); + + default: + return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE); + } +} + static int handle_stats_request(struct ofconn *ofconn, struct ofp_header *oh) { @@ -3522,7 +3694,7 @@ handle_stats_request(struct ofconn *ofconn, struct ofp_header *oh) return handle_queue_stats_request(ofconn, osr, arg_size); case OFPST_VENDOR: - return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_VENDOR); + return handle_vendor_stats_request(ofconn, osr, arg_size); default: return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_STAT); @@ -3863,7 +4035,7 @@ flow_mod_core(struct ofconn *ofconn, struct flow_mod *fm) } static int -handle_flow_mod(struct ofconn *ofconn, struct ofp_header *oh) +handle_ofpt_flow_mod(struct ofconn *ofconn, struct ofp_header *oh) { struct ofp_match orig_match; struct ofp_flow_mod *ofm; @@ -3918,6 +4090,45 @@ handle_flow_mod(struct ofconn *ofconn, struct ofp_header *oh) return flow_mod_core(ofconn, &fm); } +static int +handle_nxt_flow_mod(struct ofconn *ofconn, struct ofp_header *oh) +{ + struct nx_flow_mod *nfm; + struct flow_mod fm; + struct ofpbuf b; + int error; + + b.data = oh; + b.size = ntohs(oh->length); + + /* Dissect the message. */ + nfm = ofpbuf_try_pull(&b, sizeof *nfm); + if (!nfm) { + return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN); + } + error = nx_pull_match(&b, ntohs(nfm->match_len), ntohs(nfm->priority), + &fm.cr); + if (error) { + return error; + } + error = ofputil_pull_actions(&b, b.size, &fm.actions, &fm.n_actions); + if (error) { + return error; + } + + /* Translate the message. */ + fm.cookie = nfm->cookie; + fm.command = ntohs(nfm->command); + fm.idle_timeout = ntohs(nfm->idle_timeout); + fm.hard_timeout = ntohs(nfm->hard_timeout); + fm.buffer_id = ntohl(nfm->buffer_id); + fm.out_port = ntohs(nfm->out_port); + fm.flags = ntohs(nfm->flags); + + /* Execute the command. */ + return flow_mod_core(ofconn, &fm); +} + static int handle_tun_id_from_cookie(struct ofconn *ofconn, struct nxt_tun_id_cookie *msg) { @@ -3981,6 +4192,29 @@ handle_role_request(struct ofconn *ofconn, struct nicira_header *msg) return 0; } +static int +handle_nxt_set_flow_format(struct ofconn *ofconn, + struct nxt_set_flow_format *msg) +{ + uint32_t format; + int error; + + error = check_ofp_message(&msg->header, OFPT_VENDOR, sizeof *msg); + if (error) { + return error; + } + + format = ntohl(msg->format); + if (format == NXFF_OPENFLOW10 + || format == NXFF_TUN_ID_FROM_COOKIE + || format == NXFF_NXM) { + ofconn->flow_format = format; + return 0; + } else { + return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_EPERM); + } +} + static int handle_vendor(struct ofconn *ofconn, void *msg) { @@ -4015,6 +4249,12 @@ handle_vendor(struct ofconn *ofconn, void *msg) case NXT_ROLE_REQUEST: return handle_role_request(ofconn, msg); + + case NXT_SET_FLOW_FORMAT: + return handle_nxt_set_flow_format(ofconn, msg); + + case NXT_FLOW_MOD: + return handle_nxt_flow_mod(ofconn, &ovh->header); } return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE); @@ -4070,7 +4310,7 @@ handle_openflow(struct ofconn *ofconn, struct ofpbuf *ofp_msg) break; case OFPT_FLOW_MOD: - error = handle_flow_mod(ofconn, ofp_msg->data); + error = handle_ofpt_flow_mod(ofconn, ofp_msg->data); break; case OFPT_STATS_REQUEST: @@ -4533,8 +4773,8 @@ revalidate_rule(struct ofproto *p, struct rule *rule) } static struct ofpbuf * -compose_flow_removed(struct ofconn *ofconn, const struct rule *rule, - uint8_t reason) +compose_ofp_flow_removed(struct ofconn *ofconn, const struct rule *rule, + uint8_t reason) { struct ofp_flow_removed *ofr; struct ofpbuf *buf; @@ -4553,6 +4793,29 @@ compose_flow_removed(struct ofconn *ofconn, const struct rule *rule, return buf; } +static struct ofpbuf * +compose_nx_flow_removed(const struct rule *rule, uint8_t reason) +{ + struct nx_flow_removed *nfr; + struct ofpbuf *buf; + int match_len; + + nfr = make_nxmsg(sizeof *nfr, NXT_FLOW_REMOVED, &buf); + + match_len = nx_put_match(buf, &rule->cr); + + nfr->cookie = rule->flow_cookie; + nfr->priority = htons(rule->cr.priority); + nfr->reason = reason; + calc_flow_duration(rule->created, &nfr->duration_sec, &nfr->duration_nsec); + nfr->idle_timeout = htons(rule->idle_timeout); + nfr->match_len = htons(match_len); + nfr->packet_count = htonll(rule->packet_count); + nfr->byte_count = htonll(rule->byte_count); + + return buf; +} + static void send_flow_removed(struct ofproto *p, struct rule *rule, uint8_t reason) { @@ -4570,7 +4833,9 @@ send_flow_removed(struct ofproto *p, struct rule *rule, uint8_t reason) continue; } - msg = compose_flow_removed(ofconn, rule, reason); + msg = (ofconn->flow_format == NXFF_NXM + ? compose_nx_flow_removed(rule, reason) + : compose_ofp_flow_removed(ofconn, rule, reason)); /* Account flow expirations under ofconn->reply_counter, the counter * for replies to OpenFlow requests. That works because preventing diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at index f6a5cd81..33a17347 100644 --- a/tests/ovs-ofctl.at +++ b/tests/ovs-ofctl.at @@ -24,3 +24,200 @@ flow_mod: ADD: cookie:0x123456789abcdef hard:10 pri:60000 actions=CONTROLLER:655 flow_mod: ADD: actions=drop ]) AT_CLEANUP + +AT_SETUP([ovs-ofctl parse-nx-match]) +AT_KEYWORDS([nx-match]) +AT_DATA([nx-match.txt], [dnl + + +# in port +NXM_OF_IN_PORT(0000) +NXM_OF_IN_PORT(fffe) + +# eth dst +NXM_OF_ETH_DST(0002e30f80a4) + +# eth src +NXM_OF_ETH_SRC(020898456ddb) + +# eth type +NXM_OF_ETH_TYPE(0800) +NXM_OF_ETH_TYPE(0800) NXM_OF_IN_PORT(0012) + +# vlan tci +NXM_OF_VLAN_TCI(f009) +NXM_OF_VLAN_TCI(f009) NXM_OF_VLAN_TCI(f009) +NXM_OF_VLAN_TCI(0000) # Packets without 802.1Q header. +NXM_OF_VLAN_TCI(3123) # Packets with VID=123, PCP=1. +NXM_OF_VLAN_TCI(0123) # Does not make sense. +NXM_OF_VLAN_TCI_W(1123/1fff) # Packets with VID=123, any PCP. +NXM_OF_VLAN_TCI_W(f000/f000) # Packets with any VID, PCP=7. +NXM_OF_VLAN_TCI_W(0000/e000) # No 802.1Q or with VID=0 (not yet supported) + +# IP TOS +NXM_OF_ETH_TYPE(0800) NXM_OF_IP_TOS(f0) +NXM_OF_IP_TOS(f0) + +# IP protocol +NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(01) +NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(05) +NXM_OF_IP_PROTO(05) + +# IP source +NXM_OF_ETH_TYPE(0800) NXM_OF_IP_SRC(ac100014) +NXM_OF_ETH_TYPE(0800) NXM_OF_IP_SRC_W(C0a80000/FFFF0000) +NXM_OF_ETH_TYPE(0806) NXM_OF_IP_SRC(ac100014) +NXM_OF_IP_SRC_W(C0D80000/FFFF0000) + +# IP destination +NXM_OF_ETH_TYPE(0800) NXM_OF_IP_DST(ac100014) +NXM_OF_ETH_TYPE(0800) NXM_OF_IP_DST_W(C0a80000/FFFF0000) +NXM_OF_IP_DST(ac100014) +NXM_OF_ETH_TYPE(0806) NXM_OF_IP_DST_W(C0D80000/FFFF0000) + +# TCP source port +NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_OF_TCP_SRC(4231) +NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(07) NXM_OF_TCP_SRC(4231) + +# TCP destination port +NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_OF_TCP_DST(4231) +NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(07) NXM_OF_TCP_DST(4231) + +# UDP source port +NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(11) NXM_OF_UDP_SRC(8732) +NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_OF_UDP_SRC(7823) + +# UDP destination port +NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(11) NXM_OF_UDP_DST(1782) +NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(02) NXM_OF_UDP_DST(1293) + +# ICMP type +NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(01) NXM_OF_ICMP_TYPE(12) +NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(00) NXM_OF_ICMP_TYPE(10) + +# ICMP code +NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(01) NXM_OF_ICMP_CODE(12) +NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(00) NXM_OF_ICMP_CODE(10) +NXM_OF_ETH_TYPE(0800) NXM_OF_ICMP_CODE(10) +NXM_OF_ICMP_CODE(00) + +# ARP opcode +NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_OP(0001) +NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_OP(1111) +NXM_OF_ETH_TYPE(0000) NXM_OF_ARP_OP(0001) +NXM_OF_ARP_OP(0001) +NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_OP(0001) NXM_OF_ARP_OP(0001) + +# ARP source +NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_SPA(ac100014) +NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_SPA_W(C0a81200/FFFFFF00) +NXM_OF_ETH_TYPE(0800) NXM_OF_ARP_SPA(ac100014) +NXM_OF_ARP_SPA_W(C0D80000/FFFF0000) + +# ARP destination +NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_TPA(ac100014) +NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_TPA_W(C0a81200/FFFFFF00) +NXM_OF_ETH_TYPE(0800) NXM_OF_ARP_TPA(ac100014) +NXM_OF_ARP_TPA_W(C0D80000/FFFF0000) + +# Tunnel ID. +NXM_NX_TUN_ID(00000000abcdef01) +]) +AT_CHECK([ovs-ofctl parse-nx-match < nx-match.txt], [0], [stdout]) +AT_CHECK([cat stdout], [0], [dnl + + +# in port +NXM_OF_IN_PORT(fffe) +NXM_OF_IN_PORT(fffe) + +# eth dst +NXM_OF_ETH_DST(0002e30f80a4) + +# eth src +NXM_OF_ETH_SRC(020898456ddb) + +# eth type +NXM_OF_ETH_TYPE(0800) +NXM_OF_IN_PORT(0012), NXM_OF_ETH_TYPE(0800) + +# vlan tci +NXM_OF_VLAN_TCI(f009) +nx_pull_match() returned error 44010105 +NXM_OF_VLAN_TCI(0000) +NXM_OF_VLAN_TCI(3123) +nx_pull_match() returned error 44010100 +NXM_OF_VLAN_TCI_W(1123/1fff) +NXM_OF_VLAN_TCI_W(f000/f000) +nx_pull_match() returned error 44010100 + +# IP TOS +NXM_OF_ETH_TYPE(0800), NXM_OF_IP_TOS(f0) +nx_pull_match() returned error 44010104 + +# IP protocol +NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(01) +NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(05) +nx_pull_match() returned error 44010104 + +# IP source +NXM_OF_ETH_TYPE(0800), NXM_OF_IP_SRC(ac100014) +NXM_OF_ETH_TYPE(0800), NXM_OF_IP_SRC_W(c0a80000/ffff0000) +nx_pull_match() returned error 44010104 +nx_pull_match() returned error 44010104 + +# IP destination +NXM_OF_ETH_TYPE(0800), NXM_OF_IP_DST(ac100014) +NXM_OF_ETH_TYPE(0800), NXM_OF_IP_DST_W(c0a80000/ffff0000) +nx_pull_match() returned error 44010104 +nx_pull_match() returned error 44010104 + +# TCP source port +NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_SRC(4231) +nx_pull_match() returned error 44010104 + +# TCP destination port +NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_DST(4231) +nx_pull_match() returned error 44010104 + +# UDP source port +NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11), NXM_OF_UDP_SRC(8732) +nx_pull_match() returned error 44010104 + +# UDP destination port +NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11), NXM_OF_UDP_DST(1782) +nx_pull_match() returned error 44010104 + +# ICMP type +NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(01), NXM_OF_ICMP_TYPE(12) +nx_pull_match() returned error 44010104 + +# ICMP code +NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(01), NXM_OF_ICMP_CODE(12) +nx_pull_match() returned error 44010104 +nx_pull_match() returned error 44010104 +nx_pull_match() returned error 44010104 + +# ARP opcode +NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_OP(0001) +nx_pull_match() returned error 44010102 +nx_pull_match() returned error 44010104 +nx_pull_match() returned error 44010104 +nx_pull_match() returned error 44010105 + +# ARP source +NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_SPA(ac100014) +NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_SPA_W(c0a81200/ffffff00) +nx_pull_match() returned error 44010104 +nx_pull_match() returned error 44010104 + +# ARP destination +NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_TPA(ac100014) +NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_TPA_W(c0a81200/ffffff00) +nx_pull_match() returned error 44010104 +nx_pull_match() returned error 44010104 + +# Tunnel ID. +NXM_NX_TUN_ID(00000000abcdef01) +]) +AT_CLEANUP diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c index ff430581..73a8fca4 100644 --- a/utilities/ovs-ofctl.c +++ b/utilities/ovs-ofctl.c @@ -27,11 +27,14 @@ #include #include "byte-order.h" +#include "classifier.h" #include "command-line.h" #include "compiler.h" #include "dirs.h" #include "dpif.h" +#include "dynamic-string.h" #include "netlink.h" +#include "nx-match.h" #include "odp-util.h" #include "ofp-parse.h" #include "ofp-print.h" @@ -855,8 +858,14 @@ do_benchmark(int argc OVS_UNUSED, char *argv[]) count * message_size / (duration / 1000.0)); } -/* This command is really only useful for testing the flow parser (ofp_parse), - * so it is undocumented. */ +static void +do_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) +{ + usage(); +} + +/* Undocumented commands for unit testing. */ + static void do_parse_flows(int argc OVS_UNUSED, char *argv[]) { @@ -876,9 +885,57 @@ do_parse_flows(int argc OVS_UNUSED, char *argv[]) } static void -do_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) -{ - usage(); +do_parse_nx_match(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) +{ + struct ds in; + + ds_init(&in); + while (!ds_get_line(&in, stdin)) { + struct ofpbuf nx_match; + struct cls_rule rule; + int match_len; + int error; + char *s; + + /* Delete comments, skip blank lines. */ + s = ds_cstr(&in); + if (*s == '#') { + puts(s); + continue; + } + if (strchr(s, '#')) { + *strchr(s, '#') = '\0'; + } + if (s[strspn(s, " ")] == '\0') { + putchar('\n'); + continue; + } + + /* Convert string to nx_match. */ + ofpbuf_init(&nx_match, 0); + match_len = nx_match_from_string(ds_cstr(&in), &nx_match); + + /* Convert nx_match to cls_rule. */ + error = nx_pull_match(&nx_match, match_len, 0, &rule); + if (!error) { + char *out; + + /* Convert cls_rule back to nx_match. */ + ofpbuf_uninit(&nx_match); + ofpbuf_init(&nx_match, 0); + match_len = nx_put_match(&nx_match, &rule); + + /* Convert nx_match to string. */ + out = nx_match_to_string(nx_match.data, match_len); + puts(out); + free(out); + } else { + printf("nx_pull_match() returned error %x\n", error); + } + + ofpbuf_uninit(&nx_match); + } + ds_destroy(&in); } static const struct command all_commands[] = { @@ -901,7 +958,11 @@ static const struct command all_commands[] = { { "probe", 1, 1, do_probe }, { "ping", 1, 2, do_ping }, { "benchmark", 3, 3, do_benchmark }, - { "parse-flows", 1, 1, do_parse_flows }, { "help", 0, INT_MAX, do_help }, + + /* Undocumented commands for testing. */ + { "parse-flows", 1, 1, do_parse_flows }, + { "parse-nx-match", 0, 0, do_parse_nx_match }, + { NULL, 0, 0, NULL }, }; -- 2.30.2