This patch adds set-field operations for nd_target, nd_sll, and nd_tll fields, 
with and without masks, using Nicira extensions and OpenFlow 1.2 protocol. Unit 
tests are included.

This version performs all ND set-field processing in userspace. All ND 
set-field functions pass functional testing, including ICMPv6 checksum 
validation and regression test with IPv4/ARP traffic.  Tests were performed on 
CentOS 6.6, CentOS 7, and Ubuntu 14.04 LTS.

This version was developed against the master branch.  (Thanks for the pointer, 
Ben!)



Signed-off-by: Randall A Sharo <randall.sharo at navy.mil>



---



diff --git a/lib/csum.c b/lib/csum.c
index a9334fe..6743651 100644
--- a/lib/csum.c
+++ b/lib/csum.c
@@ -113,6 +113,26 @@ recalc_csum32(ovs_be16 old_csum, ovs_be32 old_u32, 
ovs_be32 new_u32)
 }

 /* Returns the new checksum for a packet in which the checksum field previously
+ * contained 'old_csum' and in which a field that contained the 6 bytes at
+ * 'old_bytes' was changed to contain the 6 bytes at 'new_bytes'. */
+ovs_be16
+recalc_csum48(ovs_be16 old_csum, const void *old_bytes,
+              const void *new_bytes)
+{
+    ovs_be16 new_csum = old_csum;
+    const uint16_t *p16_old = (const uint16_t *)old_bytes,
+                   *p16_new = (const uint16_t *)new_bytes;
+    int i;
+
+    for (i = 0; i < 3; ++i) {
+        new_csum = recalc_csum16(new_csum, get_unaligned_be16(&p16_old[i]),
+                                 get_unaligned_be16(&p16_new[i]));
+    }
+
+    return new_csum;
+}
+
+/* Returns the new checksum for a packet in which the checksum field previously
  * contained 'old_csum' and in which a field that contained 'old_u32[4]' was
  * changed to contain 'new_u32[4]'. */
 ovs_be16
diff --git a/lib/csum.h b/lib/csum.h
index df4b19d..ceff001 100644
--- a/lib/csum.h
+++ b/lib/csum.h
@@ -28,6 +28,8 @@ uint32_t csum_continue(uint32_t partial, const void *, 
size_t);
 ovs_be16 csum_finish(uint32_t partial);
 ovs_be16 recalc_csum16(ovs_be16 old_csum, ovs_be16 old_u16, ovs_be16 new_u16);
 ovs_be16 recalc_csum32(ovs_be16 old_csum, ovs_be32 old_u32, ovs_be32 new_u32);
+ovs_be16 recalc_csum48(ovs_be16 old_csum, const void *old_bytes,
+                       const void *new_bytes);
 ovs_be16 recalc_csum128(ovs_be16 old_csum, ovs_16aligned_be32 old_u32[4],
                         const ovs_be32 new_u32[4]);

diff --git a/lib/meta-flow.h b/lib/meta-flow.h
index 62e9c79..c0fd75e 100644
--- a/lib/meta-flow.h
+++ b/lib/meta-flow.h
@@ -1329,7 +1329,7 @@ enum OVS_PACKED_ENUM mf_field_id {
      * Maskable: bitwise.
      * Formatting: IPv6.
      * Prerequisites: ND.
-     * Access: read-only.
+     * Access: read/write.
      * NXM: NXM_NX_ND_TARGET(23) since v1.1.
      * OXM: OXM_OF_IPV6_ND_TARGET(31) since OF1.2 and v1.7.
      */
@@ -1343,7 +1343,7 @@ enum OVS_PACKED_ENUM mf_field_id {
      * Maskable: bitwise.
      * Formatting: Ethernet.
      * Prerequisites: ND solicit.
-     * Access: read-only.
+     * Access: read/write.
      * NXM: NXM_NX_ND_SLL(24) since v1.1.
      * OXM: OXM_OF_IPV6_ND_SLL(32) since OF1.2 and v1.7.
      */
@@ -1357,7 +1357,7 @@ enum OVS_PACKED_ENUM mf_field_id {
      * Maskable: bitwise.
      * Formatting: Ethernet.
      * Prerequisites: ND advert.
-     * Access: read-only.
+     * Access: read/write.
      * NXM: NXM_NX_ND_TLL(25) since v1.1.
      * OXM: OXM_OF_IPV6_ND_TLL(33) since OF1.2 and v1.7.
      */
diff --git a/lib/odp-execute.c b/lib/odp-execute.c
index 720d24e..c562055 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -19,6 +19,7 @@
 #include "odp-execute.h"
 #include <arpa/inet.h>
 #include <netinet/ip6.h>
+#include <netinet/icmp6.h>
 #include <stdlib.h>
 #include <string.h>

@@ -181,6 +182,55 @@ set_arp(struct ofpbuf *packet, const struct ovs_key_arp 
*key,
 }

 static void
+odp_set_nd(struct ofpbuf *packet, const struct ovs_key_nd *key,
+           const struct ovs_key_nd *mask)
+{
+    const struct nd_neighbor_solicit *ns
+        = (struct nd_neighbor_solicit *)ofpbuf_l4(packet);
+    const uint8_t *option = ofpbuf_get_nd_payload(packet);
+
+    if (OVS_LIKELY(ns && option)) {
+        int bytes_remain = (int)ofpbuf_l4_size(packet) - sizeof(*ns);
+        ovs_be32 tgt_buf[4];
+        uint8_t sll_buf[ETH_ADDR_LEN] = {0};
+        uint8_t tll_buf[ETH_ADDR_LEN] = {0};
+
+        /* ND sll and tll fields are 8 bytes, including option header */
+        while (bytes_remain >= 8) {
+            const struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *) option;
+            int opt_len_bytes = nd_opt->nd_opt_len * 8 - sizeof(*nd_opt);
+
+            option += sizeof(*nd_opt);
+            bytes_remain -= sizeof(*nd_opt);
+
+            if (nd_opt->nd_opt_type == ND_OPT_SOURCE_LINKADDR
+                && opt_len_bytes == ETH_ADDR_LEN) {
+                memcpy(sll_buf, option, ETH_ADDR_LEN);
+                ether_addr_copy_masked(sll_buf, key->nd_sll, mask->nd_sll);
+                /* A packet can only contain one SLL or TLL option */
+                break;
+            } else if (nd_opt->nd_opt_type == ND_OPT_TARGET_LINKADDR
+                       && opt_len_bytes == ETH_ADDR_LEN) {
+                memcpy(tll_buf, option, ETH_ADDR_LEN);
+                ether_addr_copy_masked(tll_buf, key->nd_tll, mask->nd_tll);
+                /* A packet can only contain one SLL or TLL option */
+                break;
+            }
+
+            option       += opt_len_bytes;
+            bytes_remain -= opt_len_bytes;
+        }
+
+        packet_set_nd(packet,
+                      mask_ipv6_addr((const ovs_16aligned_be32 *)
+                                     &ns->nd_ns_target,
+                                     key->nd_target, mask->nd_target, tgt_buf),
+                      sll_buf,
+                      tll_buf);
+    }
+}
+
+static void
 odp_execute_set_action(struct dpif_packet *packet, const struct nlattr *a)
 {
     enum ovs_key_attr type = nl_attr_type(a);
@@ -258,6 +308,15 @@ odp_execute_set_action(struct dpif_packet *packet, const 
struct nlattr *a)
         set_arp(&packet->ofpbuf, nl_attr_get(a), NULL);
         break;

+    case OVS_KEY_ATTR_ND:
+        if (OVS_LIKELY(ofpbuf_get_nd_payload(&packet->ofpbuf))) {
+            const struct ovs_key_nd *nd_key
+                   = nl_attr_get_unspec(a, sizeof(struct ovs_key_nd));
+            packet_set_nd(&packet->ofpbuf, nd_key->nd_target,
+                          nd_key->nd_sll, nd_key->nd_tll);
+        }
+        break;
+
     case OVS_KEY_ATTR_DP_HASH:
         md->dp_hash = nl_attr_get_u32(a);
         dpif_packet_set_dp_hash(packet, md->dp_hash);
@@ -274,7 +333,6 @@ odp_execute_set_action(struct dpif_packet *packet, const 
struct nlattr *a)
     case OVS_KEY_ATTR_VLAN:
     case OVS_KEY_ATTR_ICMP:
     case OVS_KEY_ATTR_ICMPV6:
-    case OVS_KEY_ATTR_ND:
     case OVS_KEY_ATTR_TCP_FLAGS:
     case __OVS_KEY_ATTR_MAX:
     default:
@@ -347,6 +405,11 @@ odp_execute_masked_set_action(struct dpif_packet *packet,
                 get_mask(a, struct ovs_key_arp));
         break;

+    case OVS_KEY_ATTR_ND:
+        odp_set_nd(&packet->ofpbuf, nl_attr_get(a),
+                   get_mask(a, struct ovs_key_nd));
+        break;
+
     case OVS_KEY_ATTR_DP_HASH:
         md->dp_hash = nl_attr_get_u32(a)
             | (dpif_packet_get_dp_hash(packet) & ~*get_mask(a, uint32_t));
@@ -366,7 +429,6 @@ odp_execute_masked_set_action(struct dpif_packet *packet,
     case OVS_KEY_ATTR_VLAN:
     case OVS_KEY_ATTR_ICMP:
     case OVS_KEY_ATTR_ICMPV6:
-    case OVS_KEY_ATTR_ND:
     case OVS_KEY_ATTR_TCP_FLAGS:
     case __OVS_KEY_ATTR_MAX:
     default:
diff --git a/lib/odp-util.c b/lib/odp-util.c
index 633919a..b5f8e79 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -2806,6 +2806,8 @@ static void put_ipv6_key(const struct ovs_key_ipv6 *, 
struct flow *,
                          bool is_mask);
 static void get_arp_key(const struct flow *, struct ovs_key_arp *);
 static void put_arp_key(const struct ovs_key_arp *, struct flow *);
+static void get_nd_key(const struct flow *, struct ovs_key_nd *);
+static void put_nd_key(const struct ovs_key_nd *, struct flow *);

 /* These share the same layout. */
 union ovs_key_tp {
@@ -4149,6 +4151,45 @@ commit_set_arp_action(const struct flow *flow, struct 
flow *base_flow,
     return 0;
 }

+static void
+get_nd_key(const struct flow *flow, struct ovs_key_nd *nd)
+{
+    memcpy(nd->nd_target, &flow->nd_target, sizeof flow->nd_target);
+    /* nd_sll and nd_tll are stored in arp_sha and arp_tha, respectively */
+    memcpy(nd->nd_sll, flow->arp_sha, ETH_ADDR_LEN);
+    memcpy(nd->nd_tll, flow->arp_tha, ETH_ADDR_LEN);
+}
+
+static void
+put_nd_key(const struct ovs_key_nd *nd, struct flow *flow)
+{
+    memcpy(&flow->nd_target, &flow->nd_target, sizeof flow->nd_target);
+    /* nd_sll and nd_tll are stored in arp_sha and arp_tha, respectively */
+    memcpy(flow->arp_sha, nd->nd_sll, ETH_ADDR_LEN);
+    memcpy(flow->arp_tha, nd->nd_tll, ETH_ADDR_LEN);
+}
+
+static enum slow_path_reason
+commit_set_nd_action(const struct flow *flow, struct flow *base_flow,
+                     struct ofpbuf *odp_actions,
+                     struct flow_wildcards *wc, bool use_masked)
+{
+    struct ovs_key_nd key, mask, base;
+
+    get_nd_key(flow, &key);
+    get_nd_key(base_flow, &base);
+    get_nd_key(&wc->masks, &mask);
+
+    if (commit(OVS_KEY_ATTR_ND, use_masked, &key, &base, &mask, sizeof key,
+               odp_actions)) {
+        put_nd_key(&base, base_flow);
+        put_nd_key(&mask, &wc->masks);
+        return SLOW_ACTION;
+    }
+
+    return 0;
+}
+
 static enum slow_path_reason
 commit_set_nw_action(const struct flow *flow, struct flow *base,
                      struct ofpbuf *odp_actions, struct flow_wildcards *wc,
@@ -4166,7 +4207,7 @@ commit_set_nw_action(const struct flow *flow, struct flow 
*base,

     case ETH_TYPE_IPV6:
         commit_set_ipv6_action(flow, base, odp_actions, wc, use_masked);
-        break;
+        return commit_set_nd_action(flow, base, odp_actions, wc, use_masked);

     case ETH_TYPE_ARP:
         return commit_set_arp_action(flow, base, odp_actions, wc);
diff --git a/lib/ofpbuf.h b/lib/ofpbuf.h
index ea03c9d..49c0a47 100644
--- a/lib/ofpbuf.h
+++ b/lib/ofpbuf.h
@@ -104,6 +104,7 @@ static inline const void *ofpbuf_get_tcp_payload(const 
struct ofpbuf *);
 static inline const void *ofpbuf_get_udp_payload(const struct ofpbuf *);
 static inline const void *ofpbuf_get_sctp_payload(const struct ofpbuf *);
 static inline const void *ofpbuf_get_icmp_payload(const struct ofpbuf *);
+static inline const void *ofpbuf_get_nd_payload(const struct ofpbuf *);

 void ofpbuf_use(struct ofpbuf *, void *, size_t);
 void ofpbuf_use_stack(struct ofpbuf *, void *, size_t);
@@ -374,6 +375,12 @@ static inline const void *ofpbuf_get_icmp_payload(const 
struct ofpbuf *b)
         ? (const char *)ofpbuf_l4(b) + ICMP_HEADER_LEN : NULL;
 }

+static inline const void *ofpbuf_get_nd_payload(const struct ofpbuf *b)
+{
+    return OVS_LIKELY(ofpbuf_l4_size(b) >= ND_HEADER_LEN)
+        ? (const char *)ofpbuf_l4(b) + ND_HEADER_LEN : NULL;
+}
+
 #ifdef DPDK_NETDEV
 BUILD_ASSERT_DECL(offsetof(struct ofpbuf, mbuf) == 0);

diff --git a/lib/packets.c b/lib/packets.c
index af99e3b..be89678 100644
--- a/lib/packets.c
+++ b/lib/packets.c
@@ -875,6 +875,62 @@ packet_set_sctp_port(struct ofpbuf *packet, ovs_be16 src, 
ovs_be16 dst)
     put_16aligned_be32(&sh->sctp_csum, old_csum ^ old_correct_csum ^ new_csum);
 }

+void
+packet_set_nd(struct ofpbuf *packet, const ovs_be32 target[4],
+              const uint8_t sll[ETH_ADDR_LEN],
+              const uint8_t tll[ETH_ADDR_LEN]) {
+    uint8_t *option                = ofpbuf_l4(packet);
+    int bytes_remain               = (int)ofpbuf_l4_size(packet);
+    struct nd_neighbor_solicit *ns = (struct nd_neighbor_solicit *)option;
+
+    if (OVS_UNLIKELY(bytes_remain < sizeof(*ns))) {
+        return;
+    }
+
+    if (memcmp(&ns->nd_ns_target, target, sizeof(ovs_be32[4]))) {
+        packet_set_ipv6_addr(packet, IPPROTO_ICMPV6,
+                             (ovs_16aligned_be32 *)&ns->nd_ns_target,
+                             target, true);
+    }
+
+    option       += sizeof(*ns);
+    bytes_remain -= sizeof(*ns);
+
+    /* ND sll and tll fields are 8 bytes, including option header */
+    while (bytes_remain >= 8) {
+        const struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *) option;
+        int opt_len_bytes = nd_opt->nd_opt_len * 8 - sizeof(*nd_opt);
+
+        option += sizeof(*nd_opt);
+        bytes_remain -= sizeof(*nd_opt);
+
+        if (nd_opt->nd_opt_type == ND_OPT_SOURCE_LINKADDR
+            && opt_len_bytes == ETH_ADDR_LEN) {
+            if (memcmp(option, sll, ETH_ADDR_LEN)) {
+                ovs_be16 *csum = &(ns->nd_ns_hdr.icmp6_cksum);
+                *csum = recalc_csum48(*csum, option, sll);
+                memcpy(option, sll, ETH_ADDR_LEN);
+            }
+
+            /* A packet can only contain one SLL or TLL option */
+            break;
+        } else if (nd_opt->nd_opt_type == ND_OPT_TARGET_LINKADDR
+                   && opt_len_bytes == ETH_ADDR_LEN) {
+            if (memcmp(option, tll, ETH_ADDR_LEN)) {
+                ovs_be16 *csum = &(ns->nd_ns_hdr.icmp6_cksum);
+                *csum = recalc_csum48(*csum, option, tll);
+                memcpy(option, tll, ETH_ADDR_LEN);
+            }
+
+            /* A packet can only contain one SLL or TLL option */
+            break;
+        }
+
+        option       += opt_len_bytes;
+        bytes_remain -= opt_len_bytes;
+    }
+}
+
 const char *
 packet_tcp_flag_to_string(uint32_t flag)
 {
diff --git a/lib/packets.h b/lib/packets.h
index 65d2274..7cee220 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -647,6 +647,13 @@ struct icmp6_header {
 };
 BUILD_ASSERT_DECL(ICMP6_HEADER_LEN == sizeof(struct icmp6_header));

+/* Minimum length of an IPv6 Neighbor Discovery message.
+ * This length includes the ICMP6 header, reserved word (if NS message),
+ * RSO flag word (if NA message), and target IPv6 address.  SLL and TLL
+ * options are not counted.
+ */
+#define ND_HEADER_LEN 24
+
 /* The IPv6 flow label is in the lower 20 bits of the first 32-bit word. */
 #define IPV6_LABEL_MASK 0x000fffff

@@ -758,6 +765,8 @@ void packet_set_ipv6(struct ofpbuf *, uint8_t proto, const 
ovs_be32 src[4],
 void packet_set_tcp_port(struct ofpbuf *, ovs_be16 src, ovs_be16 dst);
 void packet_set_udp_port(struct ofpbuf *, ovs_be16 src, ovs_be16 dst);
 void packet_set_sctp_port(struct ofpbuf *, ovs_be16 src, ovs_be16 dst);
+void packet_set_nd(struct ofpbuf *, const ovs_be32 target[4],
+                   const uint8_t sll[6], const uint8_t tll[6]);

 void packet_format_tcp_flags(struct ds *, uint16_t);
 const char *packet_tcp_flag_to_string(uint32_t flag);
diff --git a/tests/ofp-print.at b/tests/ofp-print.at
index c58ab7d..f25ce4b 100644
--- a/tests/ofp-print.at
+++ b/tests/ofp-print.at
@@ -934,6 +934,23 @@ OFPT_FLOW_MOD (OF1.2) (xid=0x52334507): ADD 
priority=255,dp_hash=0x1234567/0xfff
 ])
 AT_CLEANUP

+AT_SETUP([OFPT_FLOW_MOD - OF1.2 - set-field nd_target, nd_sll])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\
+03 0e 00 78 00 00 00 02 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff \
+ff ff ff ff ff ff ff ff ff ff ff ff 00 00 00 00 \
+00 01 00 14 80 00 0a 02 86 dd 80 00 14 01 3a 80 \
+00 3a 01 87 00 00 00 00 00 04 00 30 00 00 00 00 \
+00 19 00 18 80 00 3e 10 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 01 00 19 00 10 80 00 40 06 \
+aa aa aa aa aa aa 00 00
+" 2], [0], [dnl
+OFPT_FLOW_MOD (OF1.2) (xid=0x2): ADD priority=255,icmp6,icmp_type=135 
actions=set_field:::1->nd_target,set_field:aa:aa:aa:aa:aa:aa->nd_sll
+], [dnl
+])
+AT_CLEANUP
+
 dnl This triggered a buggy "instructions out of order" message earlier.
 AT_SETUP([OFPT_FLOW_MOD - OF1.3 - meter])
 AT_KEYWORDS([ofp-print])
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index 5116107..08df13c 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -198,6 +198,25 @@ Datapath actions: 
10,set(ipv4(src=192.168.3.91)),11,set(ipv4(src=192.168.3.90)),
 OVS_VSWITCHD_STOP
 AT_CLEANUP

+AT_SETUP([ofproto-dpif - modify IPv6 Neighbor Solitication (ND)])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [10], [11], [12], [13])
+AT_DATA([flows.txt], [dnl
+table=0 in_port=1,icmp6,icmpv6_type=135 
actions=output(10),write_actions(set_field:fe80::3->nd_target,set_field:aa:aa:aa:aa:aa:aa->nd_sll,output(12)),goto_table(1)
+table=1 icmp6 actions=write_actions(output(13)),goto_table(2)
+table=2 in_port=1,icmp6,icmpv6_type=135 
actions=set_field:fe80::4->nd_target,set_field:cc:cc:cc:cc:cc:cc->nd_sll,output(11)
+])
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flows br0 flows.txt])
+AT_CHECK([ovs-appctl ofproto/trace br0 
'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,icmp6,ipv6_src=fe80::1,ipv6_dst=fe80::2,nw_tos=0,nw_ttl=128,icmpv6_type=135,nd_target=fe80::2020,nd_sll=66:55:44:33:22:11'],
 [0], [stdout])
+AT_CHECK([tail -4 stdout], [0],
+  [Megaflow: 
recirc_id=0,icmp6,in_port=1,nw_frag=no,icmp_type=135,icmp_code=0x0/0xff,nd_target=fe80::2020,nd_sll=66:55:44:33:22:11
+Datapath actions: 
10,set(nd(target=fe80::4,sll=cc:cc:cc:cc:cc:cc)),11,set(nd(target=fe80::3,sll=aa:aa:aa:aa:aa:aa)),13
+This flow is handled by the userspace slow path because it:
+ - Uses action(s) not supported by datapath.
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([ofproto-dpif - clear actions])
 OVS_VSWITCHD_START
 ADD_OF_PORTS([br0], [1], [10], [11], [12])
diff --git a/tests/ofproto.at b/tests/ofproto.at
index 8cfecc6..ad47d6d 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -844,7 +844,9 @@ AT_SETUP([ofproto - set-field flow_mod commands (NXM)])
 OVS_VSWITCHD_START
 AT_CHECK([ovs-ofctl add-flow br0 ipv6,table=1,in_port=3,actions=drop])
 AT_CHECK([ovs-ofctl add-flow br0 
ipv6,table=1,in_port=3,actions=set_field:fe80:0123:4567:890a:a6ba:dbff:fefe:59fa-\>ipv6_src])
+AT_CHECK([ovs-ofctl add-flow br0 
icmp6,icmp_type=136,table=1,in_port=3,actions=set_field:fe80:8675:3097:890a:a6ba:dbff:f00d:59fa-\>nd_target,set_field:cc:dd:ee:ff:00:11-\>nd_tll])
 AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ table=1, icmp6,in_port=3,icmp_type=136 
actions=load:0xa6badbfff00d59fa->NXM_NX_ND_TARGET[[0..63]],load:0xfe8086753097890a->NXM_NX_ND_TARGET[[64..127]],load:0xccddeeff0011->NXM_NX_ND_TLL[[]]
  table=1, ipv6,in_port=3 
actions=load:0xa6badbfffefe59fa->NXM_NX_IPV6_SRC[[0..63]],load:0xfe8001234567890a->NXM_NX_IPV6_SRC[[64..127]]
 NXST_FLOW reply:
 ])
@@ -870,6 +872,19 @@ AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | 
ofctl_strip], [0], [OFPST_FLO
 OVS_VSWITCHD_STOP
 AT_CLEANUP

+AT_SETUP([ofproto - set-field flow_mod commands (OF1.2)])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 
ipv6,table=1,in_port=3,actions=drop])
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 
ipv6,table=1,in_port=3,actions=set_field:fe80:0123:4567:890a:a6ba:dbff:fefe:59fa-\>ipv6_src])
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 
icmp6,icmp_type=136,table=1,in_port=3,actions=set_field:fe80:8675:3097:890a:a6ba:dbff:f00d:59fa-\>nd_target,set_field:cc:dd:ee:ff:00:11-\>nd_tll])
+AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], 
[dnl
+ table=1, icmp6,in_port=3,icmp_type=136 
actions=set_field:fe80:8675:3097:890a:a6ba:dbff:f00d:59fa->nd_target,set_field:cc:dd:ee:ff:00:11->nd_tll
+ table=1, ipv6,in_port=3 
actions=set_field:fe80:123:4567:890a:a6ba:dbff:fefe:59fa->ipv6_src
+OFPST_FLOW reply (OF1.2):
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([ofproto - dump flows with cookie])
 OVS_VSWITCHD_START
 AT_CHECK([ovs-ofctl add-flow br0 cookie=0x1,in_port=1,actions=1])
@@ -1323,7 +1338,7 @@ OVS_VSWITCHD_START
       instructions: 
apply_actions,clear_actions,write_actions,write_metadata,goto_table
       Write-Actions and Apply-Actions features:
         actions: output group set_field strip_vlan push_vlan mod_nw_ttl 
dec_ttl set_mpls_ttl dec_mpls_ttl push_mpls pop_mpls set_queue
-        supported on Set-Field: metadata in_port_oxm eth_src eth_dst vlan_vid 
vlan_pcp mpls_label mpls_tc ip_src ip_dst ipv6_src ipv6_dst ip_dscp nw_ecn 
arp_op arp_spa arp_tpa arp_sha arp_tha tcp_src tcp_dst udp_src udp_dst sctp_src 
sctp_dst
+        supported on Set-Field: metadata in_port_oxm eth_src eth_dst vlan_vid 
vlan_pcp mpls_label mpls_tc ip_src ip_dst ipv6_src ipv6_dst ip_dscp nw_ecn 
arp_op arp_spa arp_tpa arp_sha arp_tha tcp_src tcp_dst udp_src udp_dst sctp_src 
sctp_dst nd_target nd_sll nd_tll
     matching:
       metadata: exact match or wildcard
       in_port_oxm: exact match or wildcard
@@ -1401,7 +1416,7 @@ OVS_VSWITCHD_START
       instructions: 
meter,apply_actions,clear_actions,write_actions,write_metadata,goto_table
       Write-Actions and Apply-Actions features:
         actions: output group set_field strip_vlan push_vlan mod_nw_ttl 
dec_ttl set_mpls_ttl dec_mpls_ttl push_mpls pop_mpls set_queue
-        supported on Set-Field: tun_id tun_src tun_dst metadata in_port 
in_port_oxm pkt_mark reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 xreg0 xreg1 xreg2 
xreg3 eth_src eth_dst vlan_tci vlan_vid vlan_pcp mpls_label mpls_tc ip_src 
ip_dst ipv6_src ipv6_dst nw_tos ip_dscp nw_ecn nw_ttl arp_op arp_spa arp_tpa 
arp_sha arp_tha tcp_src tcp_dst udp_src udp_dst sctp_src sctp_dst
+        supported on Set-Field: tun_id tun_src tun_dst metadata in_port 
in_port_oxm pkt_mark reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 xreg0 xreg1 xreg2 
xreg3 eth_src eth_dst vlan_tci vlan_vid vlan_pcp mpls_label mpls_tc ip_src 
ip_dst ipv6_src ipv6_dst nw_tos ip_dscp nw_ecn nw_ttl arp_op arp_spa arp_tpa 
arp_sha arp_tha tcp_src tcp_dst udp_src udp_dst sctp_src sctp_dst nd_target 
nd_sll nd_tll
     matching:
       dp_hash: arbitrary mask
       recirc_id: exact match or wildcard
_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to