This adds support for the OpenFlow 1.1+ copy_ttl_out action.
And also adds an NX copy_ttl_out action.

The implementation does not support copying in the case where the outermost
header is IP as it is unclear to me that Open vSwtich has a notion of an
inner IP header to copy the TLL from.

The handling of the TTL modification is entirely handled in userspace.

Signed-off-by: Simon Horman <ho...@verge.net.au>

---

v2.6
* Non-trivial rebase

v2.5
* First post
---
 include/openflow/nicira-ext.h |    1 +
 lib/flow.c                    |    7 +++++--
 lib/flow.h                    |    8 +++++---
 lib/match.c                   |    2 +-
 lib/nx-match.c                |    2 +-
 lib/ofp-actions.c             |   20 +++++++++++++++++++
 lib/ofp-actions.h             |    1 +
 lib/ofp-parse.c               |    5 +++++
 lib/ofp-util.c                |    4 ++--
 lib/ofp-util.def              |    2 ++
 ofproto/ofproto-dpif.c        |   23 +++++++++++++++++++++
 tests/ofproto-dpif.at         |   44 ++++++++++++++++++++++++++++++++++++++++-
 tests/test-bundle.c           |    1 +
 tests/test-multipath.c        |    1 +
 utilities/ovs-ofctl.8.in      |    7 +++++++
 15 files changed, 118 insertions(+), 10 deletions(-)

diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h
index e1093cb..0ff906f 100644
--- a/include/openflow/nicira-ext.h
+++ b/include/openflow/nicira-ext.h
@@ -306,6 +306,7 @@ enum nx_action_subtype {
     NXAST_WRITE_METADATA,       /* struct nx_action_write_metadata */
     NXAST_PUSH_MPLS,            /* struct nx_action_push_mpls */
     NXAST_POP_MPLS,             /* struct nx_action_pop_mpls */
+    NXAST_COPY_TTL_OUT,         /* struct nx_action_header */
     NXAST_SET_MPLS_TTL,         /* struct nx_action_ttl */
     NXAST_DEC_MPLS_TTL,         /* struct nx_action_header */
 };
diff --git a/lib/flow.c b/lib/flow.c
index 54e332b..70290b5 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -100,7 +100,9 @@ parse_remaining_mpls(struct ofpbuf *b, struct flow *flow)
     struct mpls_hdr *mh;
     while ((mh = ofpbuf_try_pull(b, sizeof *mh)) &&
            !(mh->mpls_lse & htonl(MPLS_BOS_MASK))) {
-        flow->mpls_depth++;
+        if (flow->mpls_depth++ == 1) {
+            flow->inner_mpls_lse = mh->mpls_lse;
+        }
     }
 }
 
@@ -511,7 +513,7 @@ flow_zero_wildcards(struct flow *flow, const struct 
flow_wildcards *wildcards)
 void
 flow_get_metadata(const struct flow *flow, struct flow_metadata *fmd)
 {
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 18);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 19);
 
     fmd->tun_id = flow->tunnel.tun_id;
     fmd->metadata = flow->metadata;
@@ -559,6 +561,7 @@ void
 flow_wildcards_init_exact(struct flow_wildcards *wc)
 {
     memset(&wc->masks, 0xff, sizeof wc->masks);
+    memset(wc->masks.zeros, 0, sizeof wc->masks.zeros);
 }
 
 /* Returns true if 'wc' matches every packet, false if 'wc' fixes any bits or
diff --git a/lib/flow.h b/lib/flow.h
index 65a9353..5f99257 100644
--- a/lib/flow.h
+++ b/lib/flow.h
@@ -36,7 +36,7 @@ struct ofpbuf;
 /* This sequence number should be incremented whenever anything involving flows
  * or the wildcarding of flows changes.  This will cause build assertion
  * failures in places which likely need to be updated. */
-#define FLOW_WC_SEQ 18
+#define FLOW_WC_SEQ 19
 
 #define FLOW_N_REGS 8
 BUILD_ASSERT_DECL(FLOW_N_REGS <= NXM_NX_MAX_REGS);
@@ -88,6 +88,7 @@ struct flow {
                                    unless in DPIF code, in which case it
                                    is the datapath port number. */
     ovs_be32 mpls_lse;          /* MPLS label stack entry. */
+    ovs_be32 inner_mpls_lse;    /* Inner MPLS label stack entry. */
     uint16_t mpls_depth;        /* Depth of MPLS stack. */
     ovs_be16 vlan_tci;          /* If 802.1Q, TCI | VLAN_CFI; otherwise 0. */
     ovs_be16 dl_type;           /* Ethernet frame type. */
@@ -102,14 +103,15 @@ struct flow {
     uint8_t arp_tha[6];         /* ARP/ND target hardware address. */
     uint8_t nw_ttl;             /* IP TTL/Hop Limit. */
     uint8_t nw_frag;            /* FLOW_FRAG_* flags. */
+    uint8_t zeros[4];           /* Must be zero. */
 };
 BUILD_ASSERT_DECL(sizeof(struct flow) % 4 == 0);
 
 #define FLOW_U32S (sizeof(struct flow) / 4)
 
 /* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */
-BUILD_ASSERT_DECL(sizeof(struct flow) == sizeof(struct flow_tnl) + 152 &&
-                  FLOW_WC_SEQ == 18);
+BUILD_ASSERT_DECL(sizeof(struct flow) == sizeof(struct flow_tnl) + 160 &&
+                  FLOW_WC_SEQ == 19);
 
 /* Represents the metadata fields of struct flow. */
 struct flow_metadata {
diff --git a/lib/match.c b/lib/match.c
index d04e862..1db5586 100644
--- a/lib/match.c
+++ b/lib/match.c
@@ -724,7 +724,7 @@ match_format(const struct match *match, struct ds *s, 
unsigned int priority)
 
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 18);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 19);
 
     if (priority != OFP_DEFAULT_PRIORITY) {
         ds_put_format(s, "priority=%u,", priority);
diff --git a/lib/nx-match.c b/lib/nx-match.c
index ab5639c..47b9a97 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -547,7 +547,7 @@ nx_put_raw(struct ofpbuf *b, bool oxm, const struct match 
*match,
     int match_len;
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 18);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 19);
 
     /* Metadata. */
     if (match->wc.masks.in_port) {
diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index 8487baa..5d8e68d 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -412,6 +412,10 @@ ofpact_from_nxast(const union ofp_action *a, enum 
ofputil_action_code code,
         break;
     }
 
+    case OFPUTIL_NXAST_COPY_TTL_OUT:
+        ofpact_put_COPY_TTL_OUT(out);
+        break;
+
     case OFPUTIL_NXAST_SET_MPLS_TTL: {
         struct nx_action_mpls_ttl *nxamt = (struct nx_action_mpls_ttl *)a;
         ofpact_put_SET_MPLS_TTL(out)->ttl = nxamt->ttl;
@@ -798,6 +802,10 @@ ofpact_from_openflow11(const union ofp_action *a, struct 
ofpbuf *out)
         return nxm_reg_load_from_openflow12_set_field(
             (const struct ofp12_action_set_field *)a, out);
 
+    case OFPUTIL_OFPAT11_COPY_TTL_OUT:
+        ofpact_put_COPY_TTL_OUT(out);
+        break;
+
     case OFPUTIL_OFPAT11_SET_MPLS_TTL: {
         struct ofp11_action_mpls_ttl *oamt = (struct ofp11_action_mpls_ttl *)a;
         ofpact_put_SET_MPLS_TTL(out)->ttl = oamt->mpls_ttl;
@@ -1155,6 +1163,7 @@ ofpact_check__(const struct ofpact *a, const struct flow 
*flow, int max_ports,
     }
 
     case OFPACT_DEC_TTL:
+    case OFPACT_COPY_TTL_OUT:
     case OFPACT_SET_MPLS_TTL:
     case OFPACT_DEC_MPLS_TTL:
     case OFPACT_SET_TUNNEL:
@@ -1394,6 +1403,10 @@ ofpact_to_nxast(const struct ofpact *a, struct ofpbuf 
*out)
         ofpact_dec_ttl_to_nxast(ofpact_get_DEC_TTL(a), out);
         break;
 
+    case OFPACT_COPY_TTL_OUT:
+        ofputil_put_NXAST_COPY_TTL_OUT(out);
+        break;
+
     case OFPACT_SET_MPLS_TTL:
         ofputil_put_NXAST_SET_MPLS_TTL(out)->ttl
             = ofpact_get_SET_MPLS_TTL(a)->ttl;
@@ -1574,6 +1587,7 @@ ofpact_to_openflow10(const struct ofpact *a, struct 
ofpbuf *out)
     case OFPACT_REG_MOVE:
     case OFPACT_REG_LOAD:
     case OFPACT_DEC_TTL:
+    case OFPACT_COPY_TTL_OUT:
     case OFPACT_SET_MPLS_TTL:
     case OFPACT_DEC_MPLS_TTL:
     case OFPACT_SET_TUNNEL:
@@ -1728,6 +1742,7 @@ ofpact_to_openflow11(const struct ofpact *a, struct 
ofpbuf *out)
     case OFPACT_BUNDLE:
     case OFPACT_REG_MOVE:
     case OFPACT_REG_LOAD:
+    case OFPACT_COPY_TTL_OUT:
     case OFPACT_SET_MPLS_TTL:
     case OFPACT_DEC_MPLS_TTL:
     case OFPACT_SET_TUNNEL:
@@ -1852,6 +1867,7 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, 
uint16_t port)
     case OFPACT_REG_MOVE:
     case OFPACT_REG_LOAD:
     case OFPACT_DEC_TTL:
+    case OFPACT_COPY_TTL_OUT:
     case OFPACT_SET_MPLS_TTL:
     case OFPACT_DEC_MPLS_TTL:
     case OFPACT_SET_TUNNEL:
@@ -2078,6 +2094,10 @@ ofpact_format(const struct ofpact *a, struct ds *s)
         print_dec_ttl(ofpact_get_DEC_TTL(a), s);
         break;
 
+    case OFPACT_COPY_TTL_OUT:
+        ds_put_cstr(s, "copy_ttl_out");
+        break;
+
     case OFPACT_SET_MPLS_TTL:
         ds_put_format(s, "set_mpls_ttl(%"PRIu8")",
                       ofpact_get_SET_MPLS_TTL(a)->ttl);
diff --git a/lib/ofp-actions.h b/lib/ofp-actions.h
index 90ff06b..381d9f6 100644
--- a/lib/ofp-actions.h
+++ b/lib/ofp-actions.h
@@ -73,6 +73,7 @@
     DEFINE_OFPACT(DEC_TTL,         ofpact_cnt_ids,       cnt_ids)   \
     DEFINE_OFPACT(SET_MPLS_TTL,    ofpact_mpls_ttl,      ofpact)    \
     DEFINE_OFPACT(DEC_MPLS_TTL,    ofpact_null,          ofpact)    \
+    DEFINE_OFPACT(COPY_TTL_OUT,    ofpact_null,          ofpact)    \
     DEFINE_OFPACT(PUSH_MPLS,       ofpact_push,          ofpact)    \
     DEFINE_OFPACT(POP_MPLS,        ofpact_pop_mpls,      ofpact)    \
                                                                     \
diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
index 65611c2..1e733a6 100644
--- a/lib/ofp-parse.c
+++ b/lib/ofp-parse.c
@@ -572,6 +572,11 @@ parse_named_action(enum ofputil_action_code code, const 
struct flow *flow,
         ofpact_put_DEC_MPLS_TTL(ofpacts);
         break;
 
+    case OFPUTIL_OFPAT11_COPY_TTL_OUT:
+    case OFPUTIL_NXAST_COPY_TTL_OUT:
+        ofpact_put_COPY_TTL_OUT(ofpacts);
+        break;
+
     case OFPUTIL_NXAST_FIN_TIMEOUT:
         parse_fin_timeout(ofpacts, arg);
         break;
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index d19625b..eb50731 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -84,7 +84,7 @@ ofputil_netmask_to_wcbits(ovs_be32 netmask)
 void
 ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc)
 {
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 18);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 19);
 
     /* Initialize most of wc. */
     flow_wildcards_init_catchall(wc);
@@ -901,7 +901,7 @@ ofputil_usable_protocols(const struct match *match)
 {
     const struct flow_wildcards *wc = &match->wc;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 18);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 19);
 
     /* NXM and OF1.1+ supports bitwise matching on ethernet addresses. */
     if (!eth_mask_is_exact(wc->masks.dl_src)
diff --git a/lib/ofp-util.def b/lib/ofp-util.def
index d403bcb..c278d14 100644
--- a/lib/ofp-util.def
+++ b/lib/ofp-util.def
@@ -30,6 +30,7 @@ OFPAT11_ACTION(OFPAT11_SET_NW_TOS,   ofp_action_nw_tos,   0, 
"mod_nw_tos")
 //OFPAT11_ACTION(OFPAT11_SET_NW_ECN,   ofp11_action_nw_ecn, "0, mod_nw_ecn")
 OFPAT11_ACTION(OFPAT11_SET_TP_SRC,   ofp_action_tp_port,  0, "mod_tp_src")
 OFPAT11_ACTION(OFPAT11_SET_TP_DST,   ofp_action_tp_port,  0, "mod_tp_dst")
+OFPAT11_ACTION(OFPAT11_COPY_TTL_OUT, ofp_action_header,   0, "copy_ttl_out")
 OFPAT11_ACTION(OFPAT11_SET_MPLS_TTL, ofp11_action_mpls_ttl, 0, "set_mpls_ttl")
 OFPAT11_ACTION(OFPAT11_DEC_MPLS_TTL, ofp_action_header,   0, "dec_mpls_ttl")
 OFPAT11_ACTION(OFPAT11_PUSH_VLAN,    ofp11_action_push,   0, "push_vlan")
@@ -66,6 +67,7 @@ NXAST_ACTION(NXAST_CONTROLLER,      nx_action_controller,   
0, "controller")
 NXAST_ACTION(NXAST_DEC_TTL_CNT_IDS, nx_action_cnt_ids,      1, NULL)
 NXAST_ACTION(NXAST_WRITE_METADATA,  nx_action_write_metadata, 0,
              "write_metadata")
+NXAST_ACTION(NXAST_COPY_TTL_OUT,    nx_action_header,       0, "copy_ttl_out")
 NXAST_ACTION(NXAST_SET_MPLS_TTL,    nx_action_mpls_ttl,     0, "set_mpls_ttl")
 NXAST_ACTION(NXAST_DEC_MPLS_TTL,    nx_action_header,       0, "dec_mpls_ttl")
 NXAST_ACTION(NXAST_PUSH_MPLS,       nx_action_push,         0, "push_mpls")
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index d9e5823..51c2b57 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -5671,6 +5671,25 @@ compose_dec_ttl(struct action_xlate_ctx *ctx, struct 
ofpact_cnt_ids *ids)
 }
 
 static void
+compose_copy_ttl_out_action(struct action_xlate_ctx *ctx)
+{
+    if (ctx->flow.dl_type != htons(ETH_TYPE_MPLS) &&
+        ctx->flow.dl_type != htons(ETH_TYPE_MPLS_MCAST)) {
+        /* Copying TTL to IP is not supported */
+        return;
+    }
+
+    if (ctx->flow.mpls_depth > 1) {
+        /* MPLS -> MPLS */
+        set_mpls_lse_ttl(&ctx->flow.mpls_lse,
+                         mpls_lse_to_ttl(ctx->flow.inner_mpls_lse));
+    } else {
+        /* IP -> MPLS */
+        set_mpls_lse_ttl(&ctx->flow.mpls_lse, ctx->flow.nw_ttl);
+    }
+}
+
+static void
 compose_set_mpls_ttl_action(struct action_xlate_ctx *ctx, uint8_t ttl)
 {
     if (ctx->flow.dl_type == htons(ETH_TYPE_MPLS) ||
@@ -6071,6 +6090,10 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t 
ofpacts_len,
             compose_mpls_pop_action(ctx, ofpact_get_POP_MPLS(a)->ethertype);
             break;
 
+        case OFPACT_COPY_TTL_OUT:
+            compose_copy_ttl_out_action(ctx);
+            break;
+
         case OFPACT_SET_MPLS_TTL:
             compose_set_mpls_ttl_action(ctx, ofpact_get_SET_MPLS_TTL(a)->ttl);
             break;
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index c14e755..fe554ad 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -254,7 +254,9 @@ cookie=0xa dl_src=40:44:44:44:44:45 
actions=push_mpls:0x8847,load:10->OXM_OF_MPL
 cookie=0xa dl_src=40:44:44:44:44:46 
actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],set_mpls_ttl(10),controller
 cookie=0xa dl_src=40:44:44:44:44:47 
actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],dec_mpls_ttl,set_mpls_ttl(10),controller
 cookie=0xa dl_src=40:44:44:44:44:48 
actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],set_mpls_ttl(10),dec_mpls_ttl,controller
+cookie=0xa dl_src=40:44:44:44:44:49 
actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],set_mpls_ttl(10),dec_mpls_ttl,copy_ttl_out,controller
 cookie=0xb dl_src=50:55:55:55:55:55 dl_type=0x8847 
actions=load:1000->OXM_OF_MPLS_LABEL[[]],controller
+cookie=0xb dl_src=50:55:55:55:55:56 dl_type=0x8847 
actions=load:1000->OXM_OF_MPLS_LABEL[[]],copy_ttl_out,controller
 cookie=0xd dl_src=60:66:66:66:66:66 actions=pop_mpls:0x0800,controller
 cookie=0xc dl_src=70:77:77:77:77:77 
actions=push_mpls:0x8848,load:1000->OXM_OF_MPLS_LABEL[[]],load:7->OXM_OF_MPLS_TC[[]],controller
 ])
@@ -397,6 +399,26 @@ dnl Modified MPLS controller action.
 AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> 
ofctl_monitor.log])
 
 for i in 1 2 3; do
+    ovs-appctl netdev-dummy/receive p1 
'in_port(1),eth(src=40:44:44:44:44:49,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)'
+done
+
+OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
+AT_CHECK([cat ofctl_monitor.log], [0], [dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) 
data_len=64 (unbuffered)
+priority=0,mpls(label:10,tc:3,ttl:64,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:49,dl_dst=50:54:00:00:00:07
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) 
data_len=64 (unbuffered)
+priority=0,mpls(label:10,tc:3,ttl:64,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:49,dl_dst=50:54:00:00:00:07
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) 
data_len=64 (unbuffered)
+priority=0,mpls(label:10,tc:3,ttl:64,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:49,dl_dst=50:54:00:00:00:07
+])
+
+
+dnl Modified MPLS controller action.
+AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> 
ofctl_monitor.log])
+
+for i in 1 2 3; do
     ovs-appctl netdev-dummy/receive p1 
'in_port(1),eth(src=40:44:44:44:44:48,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)'
 done
 
@@ -431,6 +453,25 @@ NXT_PACKET_IN (xid=0x0): cookie=0xb total_len=60 in_port=1 
(via action) data_len
 
priority=0,mpls(label:1000,tc:7,ttl:64,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:55:55:55:55:55,dl_dst=50:54:00:00:00:07
 ])
 
+dnl Modified MPLS actions.
+AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> 
ofctl_monitor.log])
+
+for i in 1 2 3; do
+    ovs-appctl netdev-dummy/receive p1 
'in_port(1),eth(src=50:55:55:55:55:56,dst=50:54:00:00:00:07),eth_type(0x8847),mpls(label=100,tc=7,ttl=60,bos=1),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)'
+done
+
+OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
+AT_CHECK([cat ofctl_monitor.log], [0], [dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xb total_len=60 in_port=1 (via action) 
data_len=60 (unbuffered)
+priority=0,mpls(label:1000,tc:7,ttl:64,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:55:55:55:55:56,dl_dst=50:54:00:00:00:07
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xb total_len=60 in_port=1 (via action) 
data_len=60 (unbuffered)
+priority=0,mpls(label:1000,tc:7,ttl:64,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:55:55:55:55:56,dl_dst=50:54:00:00:00:07
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xb total_len=60 in_port=1 (via action) 
data_len=60 (unbuffered)
+priority=0,mpls(label:1000,tc:7,ttl:64,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:55:55:55:55:56,dl_dst=50:54:00:00:00:07
+])
+
 dnl Modified MPLS ipv6 controller action.
 AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> 
ofctl_monitor.log])
 
@@ -450,7 +491,6 @@ NXT_PACKET_IN (xid=0x0): cookie=0xc total_len=64 in_port=1 
(via action) data_len
 
priority=0,mplsm(label:1000,tc:7,ttl:64,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=70:77:77:77:77:77,dl_dst=50:54:00:00:00:07
 ])
 
-
 dnl Modified MPLS pop action.
 AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> 
ofctl_monitor.log])
 
@@ -559,7 +599,9 @@ AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], 
[0], [dnl
  cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:44:46 
actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],set_mpls_ttl(10),CONTROLLER:65535
  cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:44:47 
actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],dec_mpls_ttl,set_mpls_ttl(10),CONTROLLER:65535
  cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:44:48 
actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],set_mpls_ttl(10),dec_mpls_ttl,CONTROLLER:65535
+ cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:44:49 
actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],set_mpls_ttl(10),dec_mpls_ttl,copy_ttl_out,CONTROLLER:65535
  cookie=0xb, n_packets=3, n_bytes=180, dl_src=50:55:55:55:55:55,dl_type=0x8847 
actions=load:0x3e8->OXM_OF_MPLS_LABEL[[]],CONTROLLER:65535
+ cookie=0xb, n_packets=3, n_bytes=180, dl_src=50:55:55:55:55:56,dl_type=0x8847 
actions=load:0x3e8->OXM_OF_MPLS_LABEL[[]],copy_ttl_out,CONTROLLER:65535
  cookie=0xc, n_packets=3, n_bytes=180, dl_src=70:77:77:77:77:77 
actions=push_mpls:0x8848,load:0x3e8->OXM_OF_MPLS_LABEL[[]],load:0x7->OXM_OF_MPLS_TC[[]],CONTROLLER:65535
  cookie=0xd, n_packets=3, n_bytes=180, dl_src=60:66:66:66:66:66 
actions=pop_mpls:0x0800,CONTROLLER:65535
  n_packets=3, n_bytes=180, dl_src=10:11:11:11:11:11 actions=CONTROLLER:65535
diff --git a/tests/test-bundle.c b/tests/test-bundle.c
index 53f3873..f5b24b4 100644
--- a/tests/test-bundle.c
+++ b/tests/test-bundle.c
@@ -136,6 +136,7 @@ main(int argc, char *argv[])
     flows = xmalloc(N_FLOWS * sizeof *flows);
     for (i = 0; i < N_FLOWS; i++) {
         random_bytes(&flows[i], sizeof flows[i]);
+        memset(flows[i].zeros, 0, sizeof flows[i].zeros);
         flows[i].mpls_depth = 0;
         flows[i].regs[0] = OFPP_NONE;
     }
diff --git a/tests/test-multipath.c b/tests/test-multipath.c
index d01e51f..8442bc2 100644
--- a/tests/test-multipath.c
+++ b/tests/test-multipath.c
@@ -60,6 +60,7 @@ main(int argc, char *argv[])
             struct flow flow;
 
             random_bytes(&flow, sizeof flow);
+            memset(flow.zeros, 0, sizeof flow.zeros);
             flow.mpls_depth = 0;
 
             mp.max_link = n - 1;
diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
index 8e5a8ab..4d097b7 100644
--- a/utilities/ovs-ofctl.8.in
+++ b/utilities/ovs-ofctl.8.in
@@ -1022,6 +1022,13 @@ set of actions then stops.  However, if the current set 
of actions was
 reached through ``resubmit'' then remaining actions in outer levels resume
 processing.
 .
+.IP \fBcopy_ttl_out\fR
+Copy the TTL from the next-to-outermost to the outermost header with TTL.
+The implementation supports copying to an outer MPLS header from either the
+next-to-outermost MPLS header, or if the MPLS stack only has a depth of one
+the TTL of the outer-most IP header - the TTL of an IPv4 header or hop limit
+of an IPv6 header.
+.
 .IP \fBnote:\fR[\fIhh\fR]...
 Does nothing at all.  Any number of bytes represented as hex digits
 \fIhh\fR may be included.  Pairs of hex digits may be separated by
-- 
1.7.10.4

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to