ofproto: Implement support for registers in extended flow match.
authorBen Pfaff <blp@nicira.com>
Thu, 11 Nov 2010 18:41:33 +0000 (10:41 -0800)
committerBen Pfaff <blp@nicira.com>
Thu, 11 Nov 2010 18:41:33 +0000 (10:41 -0800)
include/openflow/nicira-ext.h
lib/classifier.c
lib/flow.c
lib/flow.h
lib/nx-match.c
lib/nx-match.def
lib/nx-match.h
lib/odp-util.c
lib/ofp-util.c
ofproto/ofproto.c
tests/ovs-ofctl.at

index a08ae14..3a4a694 100644 (file)
@@ -227,7 +227,9 @@ enum nx_action_subtype {
     NXAST_SET_TUNNEL,           /* struct nx_action_set_tunnel */
     NXAST_DROP_SPOOFED_ARP,     /* struct nx_action_drop_spoofed_arp */
     NXAST_SET_QUEUE,            /* struct nx_action_set_queue */
-    NXAST_POP_QUEUE             /* struct nx_action_pop_queue */
+    NXAST_POP_QUEUE,            /* struct nx_action_pop_queue */
+    NXAST_REG_MOVE,             /* struct nx_action_reg_move */
+    NXAST_REG_LOAD,             /* struct nx_action_reg_load */
 };
 
 /* Header for Nicira-defined actions. */
@@ -337,6 +339,109 @@ struct nx_action_pop_queue {
 };
 OFP_ASSERT(sizeof(struct nx_action_pop_queue) == 16);
 
+/* Action structure for NXAST_REG_MOVE.
+ *
+ * Copies src[src_ofs:src_ofs+n_bits] to dst[dst_ofs:dst_ofs+n_bits], where
+ * a[b:c] denotes the bits within 'a' numbered 'b' through 'c' (not including
+ * bit 'c').  Bit numbering starts at 0 for the least-significant bit, 1 for
+ * the next most significant bit, and so on.
+ *
+ * 'src' and 'dst' are nxm_header values with nxm_hasmask=0.  The following
+ * nxm_header values are potentially acceptable as 'src':
+ *
+ *   - NXM_OF_IN_PORT
+ *   - NXM_OF_ETH_DST
+ *   - NXM_OF_ETH_SRC
+ *   - NXM_OF_ETH_TYPE
+ *   - NXM_OF_VLAN_TCI
+ *   - NXM_OF_IP_TOS
+ *   - NXM_OF_IP_PROTO
+ *   - NXM_OF_IP_SRC
+ *   - NXM_OF_IP_DST
+ *   - NXM_OF_TCP_SRC
+ *   - NXM_OF_TCP_DST
+ *   - NXM_OF_UDP_SRC
+ *   - NXM_OF_UDP_DST
+ *   - NXM_OF_ICMP_TYPE
+ *   - NXM_OF_ICMP_CODE
+ *   - NXM_OF_ARP_OP
+ *   - NXM_OF_ARP_SPA
+ *   - NXM_OF_ARP_TPA
+ *   - NXM_NX_TUN_ID
+ *   - NXM_NX_REG(idx) for idx in the switch's accepted range.
+ *
+ * The following nxm_header values are potentially acceptable as 'dst':
+ *
+ *   - NXM_NX_REG(idx) for idx in the switch's accepted range.
+ *
+ *   - NXM_OF_VLAN_TCI.  Modifying this field's value has side effects on the
+ *     packet's 802.1Q header.  Setting a value with CFI=0 removes the 802.1Q
+ *     header (if any), ignoring the other bits.  Setting a value with CFI=1
+ *     adds or modifies the 802.1Q header appropriately, setting the TCI field
+ *     to the field's new value (with the CFI bit masked out).
+ *
+ *   - NXM_NX_TUN_ID.  Modifying this value modifies the tunnel ID used for the
+ *     packet's next tunnel encapsulation.
+ *
+ * A given nxm_header value may be used as 'src' or 'dst' only on a flow whose
+ * nx_match satisfies its prerequisites.  For example, NXM_OF_IP_TOS may be
+ * used only if the flow's nx_match includes an nxm_entry that specifies
+ * nxm_type=NXM_OF_ETH_TYPE, nxm_hasmask=0, and nxm_value=0x0800.
+ *
+ * The switch will reject actions for which src_ofs+n_bits is greater than the
+ * width of 'src' or dst_ofs+n_bits is greater than the width of 'dst' with
+ * error type OFPET_BAD_ACTION, code OFPBAC_BAD_ARGUMENT.
+ */
+struct nx_action_reg_move {
+    ovs_be16 type;                  /* OFPAT_VENDOR. */
+    ovs_be16 len;                   /* Length is 16. */
+    ovs_be32 vendor;                /* NX_VENDOR_ID. */
+    ovs_be16 subtype;               /* NXAST_REG_MOVE. */
+    ovs_be16 n_bits;                /* Number of bits. */
+    ovs_be16 src_ofs;               /* Starting bit offset in source. */
+    ovs_be16 dst_ofs;               /* Starting bit offset in destination. */
+    ovs_be32 src;                   /* Source register. */
+    ovs_be32 dst;                   /* Destination register. */
+};
+OFP_ASSERT(sizeof(struct nx_action_reg_move) == 24);
+
+/* Action structure for NXAST_REG_LOAD.
+ *
+ * Copies value[0:n_bits] to dst[ofs:ofs+n_bits], where a[b:c] denotes the bits
+ * within 'a' numbered 'b' through 'c' (not including bit 'c').  Bit numbering
+ * starts at 0 for the least-significant bit, 1 for the next most significant
+ * bit, and so on.
+ *
+ * 'dst' must be one of the following:
+ *
+ *   - NXM_NX_REG(idx) for idx in the switch's accepted range.
+ *
+ * The 'ofs' and 'n_bits' fields are combined into a single 'ofs_nbits' field
+ * to avoid enlarging the structure by another 8 bytes.  To allow 'n_bits' to
+ * take a value between 1 and 64 (inclusive) while taking up only 6 bits, it is
+ * also stored as one less than its true value:
+ *
+ *  15                           6 5                0
+ * +------------------------------+------------------+
+ * |              ofs             |    n_bits - 1    |
+ * +------------------------------+------------------+
+ *
+ * The switch will reject actions for which ofs+n_bits is greater than the
+ * width of 'dst', or in which any bits in 'value' with value 2**n_bits or
+ * greater are set to 1, with error type OFPET_BAD_ACTION, code
+ * OFPBAC_BAD_ARGUMENT.
+ */
+struct nx_action_reg_load {
+    ovs_be16 type;                  /* OFPAT_VENDOR. */
+    ovs_be16 len;                   /* Length is 16. */
+    ovs_be32 vendor;                /* NX_VENDOR_ID. */
+    ovs_be16 subtype;               /* NXAST_REG_LOAD. */
+    ovs_be16 ofs_nbits;             /* (ofs << 6) | (n_bits - 1). */
+    ovs_be32 dst;                   /* Destination register. */
+    ovs_be64 value;                 /* Immediate value. */
+};
+OFP_ASSERT(sizeof(struct nx_action_reg_load) == 24);
+
 /* Wildcard for tunnel ID. */
 #define NXFW_TUN_ID  (1 << 25)
 
@@ -721,6 +826,31 @@ OFP_ASSERT(sizeof(struct nx_action_pop_queue) == 16);
 /* ## Nicira match extensions. ## */
 /* ## ------------------------ ## */
 
+/* Metadata registers.
+ *
+ * Registers initially have value 0.  Actions allow register values to be
+ * manipulated.
+ *
+ * Prereqs: None.
+ *
+ * Format: Array of 32-bit integer registers.  Space is reserved for up to
+ *   NXM_NX_MAX_REGS registers, but switches may implement fewer.
+ *
+ * Masking: Arbitrary masks. */
+#define NXM_NX_MAX_REGS 16
+#define NXM_NX_REG(IDX)   NXM_HEADER  (0x0001, IDX, 4)
+#define NXM_NX_REG_W(IDX) NXM_HEADER_W(0x0001, IDX, 4)
+#define NXM_NX_REG_IDX(HEADER) NXM_FIELD(HEADER)
+#define NXM_IS_NX_REG(HEADER) (!((((HEADER) ^ NXM_NX_REG(0))) & 0xffffe0ff))
+#define NXM_NX_REG0       NXM_HEADER  (0x0001, 0, 4)
+#define NXM_NX_REG0_W     NXM_HEADER_W(0x0001, 0, 4)
+#define NXM_NX_REG1       NXM_HEADER  (0x0001, 1, 4)
+#define NXM_NX_REG1_W     NXM_HEADER_W(0x0001, 1, 4)
+#define NXM_NX_REG2       NXM_HEADER  (0x0001, 2, 4)
+#define NXM_NX_REG2_W     NXM_HEADER_W(0x0001, 2, 4)
+#define NXM_NX_REG3       NXM_HEADER  (0x0001, 3, 4)
+#define NXM_NX_REG3_W     NXM_HEADER_W(0x0001, 3, 4)
+
 /* Tunnel ID.
  *
  * For a packet received via GRE tunnel including a (32-bit) key, the key is
index 38c4d25..47a1bd7 100644 (file)
@@ -809,8 +809,15 @@ flow_equal_except(const struct flow *a, const struct flow *b,
                   const struct flow_wildcards *wildcards)
 {
     const uint32_t wc = wildcards->wildcards;
+    int i;
 
-    BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 37);
+    BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 37 + FLOW_N_REGS * 4);
+
+    for (i = 0; i < FLOW_N_REGS; i++) {
+        if ((a->regs[i] ^ b->regs[i]) & wildcards->reg_masks[i]) {
+            return false;
+        }
+    }
 
     return ((wc & NXFW_TUN_ID || a->tun_id == b->tun_id)
             && !((a->nw_src ^ b->nw_src) & wildcards->nw_src_mask)
@@ -831,9 +838,13 @@ static void
 zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards)
 {
     const uint32_t wc = wildcards->wildcards;
+    int i;
 
-    BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 37);
+    BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 37 + 4 * FLOW_N_REGS);
 
+    for (i = 0; i < FLOW_N_REGS; i++) {
+        flow->regs[i] &= wildcards->reg_masks[i];
+    }
     if (wc & NXFW_TUN_ID) {
         flow->tun_id = 0;
     }
index c85219e..7220210 100644 (file)
@@ -255,9 +255,8 @@ void
 flow_to_match(const struct flow *flow, uint32_t wildcards,
               int flow_format, struct ofp_match *match)
 {
-    if (flow_format != NXFF_TUN_ID_FROM_COOKIE) {
-        wildcards &= OFPFW_ALL;
-    }
+    wildcards &= (flow_format == NXFF_TUN_ID_FROM_COOKIE ? OVSFW_ALL
+                  : OFPFW_ALL);
     match->wildcards = htonl(wildcards);
 
     match->in_port = htons(flow->in_port == ODPP_LOCAL ? OFPP_LOCAL
@@ -282,14 +281,17 @@ flow_from_match(const struct ofp_match *match, int flow_format,
                 ovs_be64 cookie, struct flow *flow,
                 struct flow_wildcards *wc)
 {
-    flow_wildcards_init(wc, ntohl(match->wildcards));
-    if (flow_format == NXFF_TUN_ID_FROM_COOKIE
-        && !(wc->wildcards & NXFW_TUN_ID)) {
-        flow->tun_id = htonl(ntohll(cookie) >> 32);
+    uint32_t wildcards = ntohl(match->wildcards) & OVSFW_ALL;
+
+    flow->tun_id = 0;
+    if (flow_format != NXFF_TUN_ID_FROM_COOKIE) {
+        wildcards |= NXFW_TUN_ID;
     } else {
-        wc->wildcards |= NXFW_TUN_ID;
-        flow->tun_id = 0;
+        if (!(wildcards & NXFW_TUN_ID)) {
+            flow->tun_id = htonl(ntohll(cookie) >> 32);
+        }
     }
+    flow_wildcards_init(wc, wildcards);
 
     flow->nw_src = match->nw_src;
     flow->nw_dst = match->nw_dst;
@@ -386,13 +388,19 @@ flow_wildcards_normalize(uint32_t wildcards)
 }
 
 /* Initializes 'wc' from 'wildcards', which may be any combination of the
- * OFPFW_* and OVSFW_* wildcard bits. */
+ * OFPFW_* and OVSFW_* wildcard bits.
+ *
+ * All registers (NXM_NX_REG*) are always completely wildcarded, because
+ * 'wildcards' doesn't have enough bits to give the details on which
+ * particular bits should be wildcarded (if any).  The caller may use
+ * flow_wildcards_set_reg_mask() to update the register wildcard masks. */
 void
 flow_wildcards_init(struct flow_wildcards *wc, uint32_t wildcards)
 {
-    wc->wildcards = flow_wildcards_normalize(wildcards);
+    wc->wildcards = flow_wildcards_normalize(wildcards) | FWW_REGS;
     wc->nw_src_mask = flow_nw_bits_to_mask(wc->wildcards, OFPFW_NW_SRC_SHIFT);
     wc->nw_dst_mask = flow_nw_bits_to_mask(wc->wildcards, OFPFW_NW_DST_SHIFT);
+    memset(wc->reg_masks, 0, sizeof wc->reg_masks);
 }
 
 /* Initializes 'wc' as an exact-match set of wildcards; that is, 'wc' does not
@@ -400,7 +408,10 @@ flow_wildcards_init(struct flow_wildcards *wc, uint32_t wildcards)
 void
 flow_wildcards_init_exact(struct flow_wildcards *wc)
 {
-    flow_wildcards_init(wc, 0);
+    wc->wildcards = 0;
+    wc->nw_src_mask = htonl(UINT32_MAX);
+    wc->nw_dst_mask = htonl(UINT32_MAX);
+    memset(wc->reg_masks, 0xff, sizeof wc->reg_masks);
 }
 
 static inline uint32_t
@@ -421,12 +432,16 @@ flow_wildcards_combine(struct flow_wildcards *dst,
 {
     uint32_t wb1 = src1->wildcards;
     uint32_t wb2 = src2->wildcards;
+    int i;
 
     dst->wildcards = (wb1 | wb2) & ~(OFPFW_NW_SRC_MASK | OFPFW_NW_DST_MASK);
     dst->wildcards |= combine_nw_bits(wb1, wb2, OFPFW_NW_SRC_SHIFT);
     dst->wildcards |= combine_nw_bits(wb1, wb2, OFPFW_NW_DST_SHIFT);
     dst->nw_src_mask = src1->nw_src_mask & src2->nw_src_mask;
     dst->nw_dst_mask = src1->nw_dst_mask & src2->nw_dst_mask;
+    for (i = 0; i < FLOW_N_REGS; i++) {
+        dst->reg_masks[i] = src1->reg_masks[i] & src2->reg_masks[i];
+    }
 }
 
 /* Returns a hash of the wildcards in 'wc'. */
@@ -435,7 +450,11 @@ flow_wildcards_hash(const struct flow_wildcards *wc)
 {
     /* There is no need to include nw_src_mask or nw_dst_mask because they do
      * not add any information (they can be computed from wc->wildcards).  */
-    return hash_int(wc->wildcards, 0);
+    BUILD_ASSERT_DECL(sizeof wc->wildcards == 4);
+    BUILD_ASSERT_DECL(sizeof wc->reg_masks == 4 * FLOW_N_REGS);
+    BUILD_ASSERT_DECL(offsetof(struct flow_wildcards, wildcards) == 0);
+    BUILD_ASSERT_DECL(offsetof(struct flow_wildcards, reg_masks) == 4);
+    return hash_words((const uint32_t *) wc, 1 + FLOW_N_REGS, 0);
 }
 
 /* Returns true if 'a' and 'b' represent the same wildcards, false if they are
@@ -444,7 +463,19 @@ bool
 flow_wildcards_equal(const struct flow_wildcards *a,
                      const struct flow_wildcards *b)
 {
-    return a->wildcards == b->wildcards;
+    int i;
+
+    if (a->wildcards != b->wildcards) {
+        return false;
+    }
+
+    for (i = 0; i < FLOW_N_REGS; i++) {
+        if (a->reg_masks[i] != b->reg_masks[i]) {
+            return false;
+        }
+    }
+
+    return true;
 }
 
 /* Returns true if at least one bit or field is wildcarded in 'a' but not in
@@ -453,6 +484,14 @@ bool
 flow_wildcards_has_extra(const struct flow_wildcards *a,
                          const struct flow_wildcards *b)
 {
+    int i;
+
+    for (i = 0; i < FLOW_N_REGS; i++) {
+        if ((a->reg_masks[i] & b->reg_masks[i]) != b->reg_masks[i]) {
+            return true;
+        }
+    }
+
 #define OFPFW_NW_MASK (OFPFW_NW_SRC_MASK | OFPFW_NW_DST_MASK)
     return ((a->wildcards & ~(b->wildcards | OFPFW_NW_MASK))
             || (a->nw_src_mask & b->nw_src_mask) != b->nw_src_mask
@@ -507,3 +546,26 @@ flow_wildcards_set_nw_dst_mask(struct flow_wildcards *wc, ovs_be32 mask)
 {
     return set_nw_mask(wc, mask, &wc->nw_dst_mask, OFPFW_NW_DST_SHIFT);
 }
+
+/* Sets the wildcard mask for register 'idx' in 'wc' to 'mask'.
+ * (A 0-bit indicates a wildcard bit.) */
+void
+flow_wildcards_set_reg_mask(struct flow_wildcards *wc, int idx, uint32_t mask)
+{
+    if (mask != wc->reg_masks[idx]) {
+        wc->reg_masks[idx] = mask;
+        if (mask != UINT32_MAX) {
+            wc->wildcards |= FWW_REGS;
+        } else {
+            int i;
+
+            for (i = 0; i < FLOW_N_REGS; i++) {
+                if (wc->reg_masks[i] != UINT32_MAX) {
+                    wc->wildcards |= FWW_REGS;
+                    return;
+                }
+            }
+            wc->wildcards &= ~FWW_REGS;
+        }
+    }
+}
index 7a1084a..a87efc1 100644 (file)
@@ -32,7 +32,11 @@ struct flow_wildcards;
 struct ofp_match;
 struct ofpbuf;
 
+#define FLOW_N_REGS 3
+BUILD_ASSERT_DECL(FLOW_N_REGS <= NXM_NX_MAX_REGS);
+
 struct flow {
+    uint32_t regs[FLOW_N_REGS]; /* Registers. */
     ovs_be32 tun_id;            /* Encapsulating tunnel ID. */
     ovs_be32 nw_src;            /* IP source address. */
     ovs_be32 nw_dst;            /* IP destination address. */
@@ -50,7 +54,7 @@ struct flow {
 
 /* Assert that there are FLOW_SIG_SIZE bytes of significant data in "struct
  * flow", followed by FLOW_PAD_SIZE bytes of padding. */
-#define FLOW_SIG_SIZE 37
+#define FLOW_SIG_SIZE (37 + FLOW_N_REGS * 4)
 #define FLOW_PAD_SIZE 3
 BUILD_ASSERT_DECL(offsetof(struct flow, nw_tos) == FLOW_SIG_SIZE - 1);
 BUILD_ASSERT_DECL(sizeof(((struct flow *)0)->nw_tos) == 1);
@@ -89,6 +93,15 @@ flow_hash(const struct flow *flow, uint32_t basis)
     return hash_bytes(flow, FLOW_SIG_SIZE, basis);
 }
 
+/* Set to 1 in the 'wildcards' member of struct flow_wildcards if any bits in
+ * any of the reg_masks are wildcarded.  This maintains the invariant that
+ * 'wildcards' is nonzero if and only if any bits are wildcarded.
+ *
+ * This is used only internally to Open vSwitch--it never appears in the wire
+ * protocol. */
+#define FWW_REGS (1u << 31)
+BUILD_ASSERT_DECL(!(FWW_REGS & OVSFW_ALL)); /* Avoid collisions. */
+
 /* Information on wildcards for a flow, as a supplement to "struct flow".
  *
  * The flow_wildcards_*() functions below both depend on and maintain the
@@ -97,17 +110,25 @@ flow_hash(const struct flow *flow, uint32_t basis)
  * 1. 'wildcards' is nonzero if and only if at least one bit or field is
  *    wildcarded.
  *
- * 2. Bits in 'wildcards' not included in OVSFW_ALL are set to 0.  (This is a
- *    corollary to invariant #1.)
+ * 2. Bits in 'wildcards' not included in OVSFW_ALL or FWW_REGS are set to 0.
+ *    (This is a corollary to invariant #1.)
  *
  * 3. The fields in 'wildcards' masked by OFPFW_NW_SRC_MASK and
  *    OFPFW_NW_DST_MASK have values between 0 and 32, inclusive.
  *
  * 4. The fields masked by OFPFW_NW_SRC_MASK and OFPFW_NW_DST_MASK correspond
  *    correctly to the masks in 'nw_src_mask' and 'nw_dst_mask', respectively.
+ *
+ * 5. FWW_REGS is set to 1 in 'wildcards' if and only if at least one bit in
+ *    'reg_masks[]' is nonzero.  (This allows wildcarded 'reg_masks[]' to
+ *    satisfy invariant #1.)
+ *
+ * 6. If FWW_REGS is set to 0 in 'wildcards', then the values of all of the
+ *    other members can be correctly predicted based on 'wildcards' alone.
  */
 struct flow_wildcards {
     uint32_t wildcards;         /* enum ofp_flow_wildcards. */
+    uint32_t reg_masks[FLOW_N_REGS]; /* 1-bit in each significant regs bit. */
     ovs_be32 nw_src_mask;       /* 1-bit in each significant nw_src bit. */
     ovs_be32 nw_dst_mask;       /* 1-bit in each significant nw_dst bit. */
 };
@@ -118,6 +139,8 @@ void flow_wildcards_init_exact(struct flow_wildcards *);
 
 bool flow_wildcards_set_nw_src_mask(struct flow_wildcards *, ovs_be32);
 bool flow_wildcards_set_nw_dst_mask(struct flow_wildcards *, ovs_be32);
+void flow_wildcards_set_reg_mask(struct flow_wildcards *,
+                                 int idx, uint32_t mask);
 
 void flow_wildcards_combine(struct flow_wildcards *dst,
                             const struct flow_wildcards *src1,
index e2330fd..8e918eb 100644 (file)
@@ -119,6 +119,14 @@ nxm_field_bytes(uint32_t header)
     unsigned int length = NXM_LENGTH(header);
     return NXM_HASMASK(header) ? length / 2 : length;
 }
+
+/* Returns the width of the data for a field with the given 'header', in
+ * bits. */
+static int
+nxm_field_bits(uint32_t header)
+{
+    return nxm_field_bytes(header) * 8;
+}
 \f
 /* nx_pull_match() and helpers. */
 
@@ -133,6 +141,25 @@ parse_tci(struct cls_rule *rule, ovs_be16 tci, ovs_be16 mask)
     }
 }
 
+static int
+parse_nx_reg(const struct nxm_field *f,
+             struct flow *flow, struct flow_wildcards *wc,
+             const void *value, const void *maskp)
+{
+    int idx = NXM_NX_REG_IDX(f->header);
+    if (wc->reg_masks[idx]) {
+        return NXM_DUP_TYPE;
+    } else {
+        flow_wildcards_set_reg_mask(wc, idx,
+                                    (NXM_HASMASK(f->header)
+                                     ? ntohl(get_unaligned_u32(maskp))
+                                     : UINT32_MAX));
+        flow->regs[idx] = ntohl(get_unaligned_u32(value));
+        flow->regs[idx] &= wc->reg_masks[idx];
+        return 0;
+    }
+}
+
 static int
 parse_nxm_entry(struct cls_rule *rule, const struct nxm_field *f,
                 const void *value, const void *mask)
@@ -260,6 +287,26 @@ parse_nxm_entry(struct cls_rule *rule, const struct nxm_field *f,
         flow->tun_id = htonl(ntohll(get_unaligned_u64(value)));
         return 0;
 
+        /* Registers. */
+    case NFI_NXM_NX_REG0:
+    case NFI_NXM_NX_REG0_W:
+#if FLOW_N_REGS >= 2
+    case NFI_NXM_NX_REG1:
+    case NFI_NXM_NX_REG1_W:
+#endif
+#if FLOW_N_REGS >= 3
+    case NFI_NXM_NX_REG2:
+    case NFI_NXM_NX_REG2_W:
+#endif
+#if FLOW_N_REGS >= 4
+    case NFI_NXM_NX_REG3:
+    case NFI_NXM_NX_REG3_W:
+#endif
+#if FLOW_N_REGS > 4
+#error
+#endif
+        return parse_nx_reg(f, flow, wc, value, mask);
+
     case N_NXM_FIELDS:
         NOT_REACHED();
     }
@@ -450,6 +497,7 @@ nx_put_match(struct ofpbuf *b, const struct cls_rule *cr)
     const size_t start_len = b->size;
     ovs_be16 vid, pcp;
     int match_len;
+    int i;
 
     /* Metadata. */
     if (!(wc & OFPFW_IN_PORT)) {
@@ -554,6 +602,12 @@ nx_put_match(struct ofpbuf *b, const struct cls_rule *cr)
         nxm_put_64(b, NXM_NX_TUN_ID, htonll(ntohl(flow->tun_id)));
     }
 
+    /* Registers. */
+    for (i = 0; i < FLOW_N_REGS; i++) {
+        nxm_put_32m(b, NXM_NX_REG(i),
+                    htonl(flow->regs[i]), htonl(cr->wc.reg_masks[i]));
+    }
+
     match_len = b->size - start_len;
     ofpbuf_put_zeros(b, ROUND_UP(match_len, 8) - match_len);
     return match_len;
@@ -707,3 +761,214 @@ nx_match_from_string(const char *s, struct ofpbuf *b)
     ofpbuf_put_zeros(b, ROUND_UP(match_len, 8) - match_len);
     return match_len;
 }
+\f
+/* nxm_check_reg_move(), nxm_check_reg_load(). */
+
+static bool
+field_ok(const struct nxm_field *f, const struct flow *flow, int size)
+{
+    return (f && !NXM_HASMASK(f->header)
+            && nxm_prereqs_ok(f, flow) && size <= nxm_field_bits(f->header));
+}
+
+int
+nxm_check_reg_move(const struct nx_action_reg_move *action,
+                   const struct flow *flow)
+{
+    const struct nxm_field *src;
+    const struct nxm_field *dst;
+
+    if (action->n_bits == htons(0)) {
+        return BAD_ARGUMENT;
+    }
+
+    src = nxm_field_lookup(ntohl(action->src));
+    if (!field_ok(src, flow, ntohs(action->src_ofs) + ntohs(action->n_bits))) {
+        return BAD_ARGUMENT;
+    }
+
+    dst = nxm_field_lookup(ntohl(action->dst));
+    if (!field_ok(dst, flow, ntohs(action->dst_ofs) + ntohs(action->n_bits))) {
+        return BAD_ARGUMENT;
+    }
+
+    if (!NXM_IS_NX_REG(dst->header)
+        && dst->header != NXM_OF_VLAN_TCI
+        && dst->header != NXM_NX_TUN_ID) {
+        return BAD_ARGUMENT;
+    }
+
+    return 0;
+}
+
+int
+nxm_check_reg_load(const struct nx_action_reg_load *action,
+                   const struct flow *flow)
+{
+    const struct nxm_field *dst;
+    int ofs, n_bits;
+
+    ofs = ntohs(action->ofs_nbits) >> 6;
+    n_bits = (ntohs(action->ofs_nbits) & 0x3f) + 1;
+    dst = nxm_field_lookup(ntohl(action->dst));
+    if (!field_ok(dst, flow, ofs + n_bits)) {
+        return BAD_ARGUMENT;
+    }
+
+    /* Reject 'action' if a bit numbered 'n_bits' or higher is set to 1 in
+     * action->value. */
+    if (n_bits < 64 && ntohll(action->value) >> n_bits) {
+        return BAD_ARGUMENT;
+    }
+
+    if (!NXM_IS_NX_REG(dst->header)) {
+        return BAD_ARGUMENT;
+    }
+
+    return 0;
+}
+\f
+/* nxm_execute_reg_move(), nxm_execute_reg_load(). */
+
+static uint64_t
+nxm_read_field(const struct nxm_field *src, const struct flow *flow)
+{
+    switch (src->index) {
+    case NFI_NXM_OF_IN_PORT:
+        return flow->in_port == ODPP_LOCAL ? OFPP_LOCAL : flow->in_port;
+
+    case NFI_NXM_OF_ETH_DST:
+        return eth_addr_to_uint64(flow->dl_dst);
+
+    case NFI_NXM_OF_ETH_SRC:
+        return eth_addr_to_uint64(flow->dl_src);
+
+    case NFI_NXM_OF_ETH_TYPE:
+        return ntohs(flow->dl_type);
+
+    case NFI_NXM_OF_VLAN_TCI:
+        if (flow->dl_vlan == htons(OFP_VLAN_NONE)) {
+            return 0;
+        } else {
+            return (ntohs(flow->dl_vlan & htons(VLAN_VID_MASK))
+                    | ((flow->dl_vlan_pcp << VLAN_PCP_SHIFT) & VLAN_PCP_MASK)
+                    | VLAN_CFI);
+        }
+
+    case NFI_NXM_OF_IP_TOS:
+        return flow->nw_tos;
+
+    case NFI_NXM_OF_IP_PROTO:
+    case NFI_NXM_OF_ARP_OP:
+        return flow->nw_proto;
+
+    case NFI_NXM_OF_IP_SRC:
+    case NFI_NXM_OF_ARP_SPA:
+        return ntohl(flow->nw_src);
+
+    case NFI_NXM_OF_IP_DST:
+    case NFI_NXM_OF_ARP_TPA:
+        return ntohl(flow->nw_dst);
+
+    case NFI_NXM_OF_TCP_SRC:
+    case NFI_NXM_OF_UDP_SRC:
+        return ntohs(flow->tp_src);
+
+    case NFI_NXM_OF_TCP_DST:
+    case NFI_NXM_OF_UDP_DST:
+        return ntohs(flow->tp_dst);
+
+    case NFI_NXM_OF_ICMP_TYPE:
+        return ntohs(flow->tp_src) & 0xff;
+
+    case NFI_NXM_OF_ICMP_CODE:
+        return ntohs(flow->tp_dst) & 0xff;
+
+    case NFI_NXM_NX_TUN_ID:
+        return ntohl(flow->tun_id);
+
+#define NXM_READ_REGISTER(IDX)                  \
+    case NFI_NXM_NX_REG##IDX:                   \
+        return flow->regs[IDX];                 \
+    case NFI_NXM_NX_REG##IDX##_W:               \
+        NOT_REACHED();
+
+    NXM_READ_REGISTER(0);
+#if FLOW_N_REGS >= 2
+    NXM_READ_REGISTER(1);
+#endif
+#if FLOW_N_REGS >= 3
+    NXM_READ_REGISTER(2);
+#endif
+#if FLOW_N_REGS >= 4
+    NXM_READ_REGISTER(3);
+#endif
+#if FLOW_N_REGS > 4
+#error
+#endif
+
+    case NFI_NXM_OF_VLAN_TCI_W:
+    case NFI_NXM_OF_IP_SRC_W:
+    case NFI_NXM_OF_IP_DST_W:
+    case NFI_NXM_OF_ARP_SPA_W:
+    case NFI_NXM_OF_ARP_TPA_W:
+    case N_NXM_FIELDS:
+        NOT_REACHED();
+    }
+
+    NOT_REACHED();
+}
+
+void
+nxm_execute_reg_move(const struct nx_action_reg_move *action,
+                     struct flow *flow)
+{
+    /* Preparation. */
+    int n_bits = ntohs(action->n_bits);
+    uint64_t mask = n_bits == 64 ? UINT64_MAX : (UINT64_C(1) << n_bits) - 1;
+
+    /* Get the interesting bits of the source field. */
+    const struct nxm_field *src = nxm_field_lookup(ntohl(action->src));
+    int src_ofs = ntohs(action->src_ofs);
+    uint64_t src_data = nxm_read_field(src, flow) & (mask << src_ofs);
+
+    /* Get the remaining bits of the destination field. */
+    const struct nxm_field *dst = nxm_field_lookup(ntohl(action->dst));
+    int dst_ofs = ntohs(action->dst_ofs);
+    uint64_t dst_data = nxm_read_field(dst, flow) & ~(mask << dst_ofs);
+
+    /* Get the final value. */
+    uint64_t new_data = dst_data | ((src_data >> src_ofs) << dst_ofs);
+
+    /* Store the result. */
+    if (NXM_IS_NX_REG(dst->header)) {
+        flow->regs[NXM_NX_REG_IDX(dst->header)] = new_data;
+    } else if (dst->header == NXM_OF_VLAN_TCI) {
+        ovs_be16 vlan_tci = htons(new_data & VLAN_CFI ? new_data : 0);
+        flow->dl_vlan = htons(vlan_tci_to_vid(vlan_tci));
+        flow->dl_vlan_pcp = vlan_tci_to_pcp(vlan_tci);
+    } else if (dst->header == NXM_NX_TUN_ID) {
+        flow->tun_id = htonl(new_data);
+    } else {
+        NOT_REACHED();
+    }
+}
+
+void
+nxm_execute_reg_load(const struct nx_action_reg_load *action,
+                     struct flow *flow)
+{
+    /* Preparation. */
+    int n_bits = (ntohs(action->ofs_nbits) & 0x3f) + 1;
+    uint32_t mask = n_bits == 32 ? UINT32_MAX : (UINT32_C(1) << n_bits) - 1;
+    uint32_t *reg = &flow->regs[NXM_NX_REG_IDX(ntohl(action->dst))];
+
+    /* Get source data. */
+    uint32_t src_data = ntohll(action->value);
+
+    /* Get remaining bits of the destination field. */
+    int dst_ofs = ntohs(action->ofs_nbits) >> 6;
+    uint32_t dst_data = *reg & ~(mask << dst_ofs);
+
+    *reg = dst_data | (src_data << dst_ofs);
+}
index ec5fe9b..e70a1e8 100644 (file)
@@ -40,4 +40,18 @@ 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)
 
+DEFINE_FIELD_M(NX_REG0,      0,              0,            0)
+#if FLOW_N_REGS >= 2
+DEFINE_FIELD_M(NX_REG1,      0,              0,            0)
+#endif
+#if FLOW_N_REGS >= 3
+DEFINE_FIELD_M(NX_REG2,      0,              0,            0)
+#endif
+#if FLOW_N_REGS >= 4
+DEFINE_FIELD_M(NX_REG3,      0,              0,            0)
+#endif
+#if FLOW_N_REGS > 4
+#error
+#endif
+
 #undef DEFINE_FIELD
index 7bfea8c..e02e22b 100644 (file)
 #include <stdint.h>
 
 struct cls_rule;
+struct flow;
 struct ofpbuf;
+struct nx_action_reg_load;
+struct nx_action_reg_move;
 
 /* Nicira Extended Match (NXM) flexible flow match helper functions.
  *
@@ -34,6 +37,12 @@ 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 *);
 
+int nxm_check_reg_move(const struct nx_action_reg_move *, const struct flow *);
+int nxm_check_reg_load(const struct nx_action_reg_load *, const struct flow *);
+
+void nxm_execute_reg_move(const struct nx_action_reg_move *, struct flow *);
+void nxm_execute_reg_load(const struct nx_action_reg_load *, struct flow *);
+
 /* Upper bound on the length of an nx_match.  The longest nx_match (assuming
  * we implement 4 registers) would be:
  *
index 8367597..e57a7cb 100644 (file)
@@ -187,6 +187,7 @@ odp_flow_key_from_flow(struct odp_flow_key *key, const struct flow *flow)
 void
 odp_flow_key_to_flow(const struct odp_flow_key *key, struct flow *flow)
 {
+    memset(flow->regs, 0, sizeof flow->regs);
     flow->tun_id = key->tun_id;
     flow->nw_src = key->nw_src;
     flow->nw_dst = key->nw_dst;
index 838617c..81a5dad 100644 (file)
@@ -19,6 +19,7 @@
 #include <inttypes.h>
 #include <stdlib.h>
 #include "byte-order.h"
+#include "nx-match.h"
 #include "ofp-util.h"
 #include "ofpbuf.h"
 #include "packets.h"
@@ -527,9 +528,11 @@ check_enqueue_action(const union ofp_action *a, unsigned int len,
 }
 
 static int
-check_nicira_action(const union ofp_action *a, unsigned int len)
+check_nicira_action(const union ofp_action *a, unsigned int len,
+                    const struct flow *flow)
 {
     const struct nx_action_header *nah;
+    int error;
 
     if (len < 16) {
         VLOG_DBG_RL(&bad_ofmsg_rl,
@@ -545,13 +548,28 @@ check_nicira_action(const union ofp_action *a, unsigned int len)
     case NXAST_SET_QUEUE:
     case NXAST_POP_QUEUE:
         return check_action_exact_len(a, len, 16);
+    case NXAST_REG_MOVE:
+        error = check_action_exact_len(a, len,
+                                       sizeof(struct nx_action_reg_move));
+        if (error) {
+            return error;
+        }
+        return nxm_check_reg_move((const struct nx_action_reg_move *) a, flow);
+    case NXAST_REG_LOAD:
+        error = check_action_exact_len(a, len,
+                                       sizeof(struct nx_action_reg_load));
+        if (error) {
+            return error;
+        }
+        return nxm_check_reg_load((const struct nx_action_reg_load *) a, flow);
     default:
         return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR_TYPE);
     }
 }
 
 static int
-check_action(const union ofp_action *a, unsigned int len, int max_ports)
+check_action(const union ofp_action *a, unsigned int len,
+             const struct flow *flow, int max_ports)
 {
     int error;
 
@@ -597,7 +615,7 @@ check_action(const union ofp_action *a, unsigned int len, int max_ports)
 
     case OFPAT_VENDOR:
         return (a->vendor.vendor == htonl(NX_VENDOR_ID)
-                ? check_nicira_action(a, len)
+                ? check_nicira_action(a, len, flow)
                 : ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR));
 
     case OFPAT_ENQUEUE:
@@ -612,7 +630,7 @@ check_action(const union ofp_action *a, unsigned int len, int max_ports)
 
 int
 validate_actions(const union ofp_action *actions, size_t n_actions,
-                 const struct flow *flow OVS_UNUSED, int max_ports)
+                 const struct flow *flow, int max_ports)
 {
     size_t i;
 
@@ -637,7 +655,7 @@ validate_actions(const union ofp_action *actions, size_t n_actions,
             return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN);
         }
 
-        error = check_action(a, len, max_ports);
+        error = check_action(a, len, flow, max_ports);
         if (error) {
             return error;
         }
index e710c3d..dbde7c0 100644 (file)
@@ -2685,6 +2685,20 @@ xlate_set_dl_tci(struct action_xlate_ctx *ctx)
     }
 }
 
+static void
+xlate_reg_move_action(struct action_xlate_ctx *ctx,
+                      const struct nx_action_reg_move *narm)
+{
+    ovs_be16 old_vlan = ctx->flow.dl_vlan;
+    uint8_t old_pcp = ctx->flow.dl_vlan_pcp;
+
+    nxm_execute_reg_move(narm, &ctx->flow);
+
+    if (ctx->flow.dl_vlan != old_vlan || ctx->flow.dl_vlan_pcp != old_pcp) {
+        xlate_set_dl_tci(ctx);
+    }
+}
+
 static void
 xlate_nicira_action(struct action_xlate_ctx *ctx,
                     const struct nx_action_header *nah)
@@ -2723,6 +2737,15 @@ xlate_nicira_action(struct action_xlate_ctx *ctx,
         odp_actions_add(ctx->out, ODPAT_POP_PRIORITY);
         break;
 
+    case NXAST_REG_MOVE:
+        xlate_reg_move_action(ctx, (const struct nx_action_reg_move *) nah);
+        break;
+
+    case NXAST_REG_LOAD:
+        nxm_execute_reg_load((const struct nx_action_reg_load *) nah,
+                             &ctx->flow);
+        break;
+
     /* If you add a new action here that modifies flow data, don't forget to
      * update the flow key in ctx->flow at the same time. */
 
index 33a1734..2ef82a5 100644 (file)
@@ -122,6 +122,10 @@ NXM_OF_ARP_TPA_W(C0D80000/FFFF0000)
 
 # Tunnel ID.
 NXM_NX_TUN_ID(00000000abcdef01)
+
+# Register 0.
+NXM_NX_REG0(acebdf56)
+NXM_NX_REG0_W(a0e0d050/f0f0f0f0)
 ])
 AT_CHECK([ovs-ofctl parse-nx-match < nx-match.txt], [0], [stdout])
 AT_CHECK([cat stdout], [0], [dnl
@@ -219,5 +223,9 @@ nx_pull_match() returned error 44010104
 
 # Tunnel ID.
 NXM_NX_TUN_ID(00000000abcdef01)
+
+# Register 0.
+NXM_NX_REG0(acebdf56)
+NXM_NX_REG0_W(a0e0d050/f0f0f0f0)
 ])
 AT_CLEANUP