[ovs-dev] [PATCH v2] ofproto: New action TTL decrement.

Pravin B Shelar pshelar at nicira.com
Fri Jan 13 02:04:47 UTC 2012


Fixed according to comments from Ben.

v1-v2:
        - Fixed man page
        - Added invalid-ttl arg for ofctl-monitor.
        - Added invalid-ttl packet-in test case.

--8<--------------------------cut here-------------------------->8-
Following patch implements dec_ttl as vendor action with similar
semantics as OpenFlow 1.2. If TTL reaches zero while procession
actions in current table, the remaining actions in previous tables
are processed. An configuration parameter is added to make TTL
decrement to zero generate packet in.

Feature #8758
---
 NEWS                          |    1 +
 include/openflow/nicira-ext.h |    3 +-
 include/openflow/openflow.h   |    9 ++++++-
 lib/ofp-parse.c               |    4 +++
 lib/ofp-print.c               |   24 ++++++++++++++++++--
 lib/ofp-util.c                |    1 +
 lib/ofp-util.def              |    1 +
 lib/ofp-util.h                |    2 +-
 ofproto/connmgr.c             |   46 ++++++++++++++++++++++++++++++++++++----
 ofproto/connmgr.h             |    3 ++
 ofproto/ofproto-dpif.c        |   36 +++++++++++++++++++++++++++++--
 ofproto/ofproto.c             |    9 +++++++-
 tests/ofp-print.at            |    2 +-
 tests/ofproto-dpif.at         |   38 +++++++++++++++++++++++++++++++--
 utilities/ovs-ofctl.8.in      |   10 +++++++++
 utilities/ovs-ofctl.c         |   36 +++++++++++++++++++++++++++++++-
 16 files changed, 204 insertions(+), 21 deletions(-)

diff --git a/NEWS b/NEWS
index b628e29..4f29e19 100644
--- a/NEWS
+++ b/NEWS
@@ -8,6 +8,7 @@ v1.5.0 - xx xxx xxxx
         - Added support for querying, modifying, and deleting flows
           based on flow cookie when using NXM.
         - Added new NXM_PACKET_IN format.
+        - Added new NXAST_DEC_TTL action.
     - ovs-ofctl:
         - Added daemonization support to the monitor and snoop commands.
     - ovs-vsctl:
diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h
index f00f994..e17b313 100644
--- a/include/openflow/nicira-ext.h
+++ b/include/openflow/nicira-ext.h
@@ -361,7 +361,8 @@ enum nx_action_subtype {
     NXAST_RESUBMIT_TABLE,       /* struct nx_action_resubmit */
     NXAST_OUTPUT_REG,           /* struct nx_action_output_reg */
     NXAST_LEARN,                /* struct nx_action_learn */
-    NXAST_EXIT                  /* struct nx_action_header */
+    NXAST_EXIT,                 /* struct nx_action_header */
+    NXAST_DEC_TTL,              /* struct nx_action_header */
 };
 
 /* Header for Nicira-defined actions. */
diff --git a/include/openflow/openflow.h b/include/openflow/openflow.h
index f68a140..cd30d32 100644
--- a/include/openflow/openflow.h
+++ b/include/openflow/openflow.h
@@ -136,7 +136,11 @@ enum ofp_config_flags {
     OFPC_FRAG_DROP     = 1,  /* Drop fragments. */
     OFPC_FRAG_REASM    = 2,  /* Reassemble (only if OFPC_IP_REASM set). */
     OFPC_FRAG_NX_MATCH = 3,  /* Make first fragments available for matching. */
-    OFPC_FRAG_MASK     = 3
+    OFPC_FRAG_MASK     = 3,
+
+    /* TTL processing - applicable for IP and MPLS packets. */
+    OFPC_INVALID_TTL_TO_CONTROLLER = 1 << 2, /* Send packets with invalid TTL
+                                                to the controller. */
 };
 
 /* Switch configuration. */
@@ -289,7 +293,8 @@ OFP_ASSERT(sizeof(struct ofp_port_mod) == 32);
 /* Why is this packet being sent to the controller? */
 enum ofp_packet_in_reason {
     OFPR_NO_MATCH,          /* No matching flow. */
-    OFPR_ACTION             /* Action explicitly output to controller. */
+    OFPR_ACTION,            /* Action explicitly output to controller. */
+    OFPR_INVALID_TTL        /* Packet has invalid TTL. */
 };
 
 /* Packet received on port (datapath -> controller). */
diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
index 38c3dab..5321364 100644
--- a/lib/ofp-parse.c
+++ b/lib/ofp-parse.c
@@ -364,6 +364,10 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow,
     case OFPUTIL_NXAST_EXIT:
         ofputil_put_NXAST_EXIT(b);
         break;
+
+    case OFPUTIL_NXAST_DEC_TTL:
+        ofputil_put_NXAST_DEC_TTL(b);
+        break;
     }
 }
 
diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index aff12b6..0775ea6 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -120,10 +120,19 @@ ofp_print_packet_in(struct ds *string, const struct ofp_header *oh,
         }
     }
 
-    if (pin.reason == OFPR_ACTION) {
+    switch (pin.reason) {
+    case OFPR_NO_MATCH:
+        ds_put_cstr(string, " (via no_match)");
+        break;
+    case OFPR_ACTION:
         ds_put_cstr(string, " (via action)");
-    } else if (pin.reason != OFPR_NO_MATCH) {
+        break;
+    case OFPR_INVALID_TTL:
+        ds_put_cstr(string, " (via invalid_ttl)");
+        break;
+      default:
         ds_put_format(string, " (***reason %"PRIu8"***)", pin.reason);
+        break;
     }
 
     ds_put_format(string, " data_len=%zu", pin.packet_len);
@@ -333,6 +342,10 @@ ofp_print_action(struct ds *s, const union ofp_action *a,
         learn_format((const struct nx_action_learn *) a, s);
         break;
 
+    case OFPUTIL_NXAST_DEC_TTL:
+        ds_put_cstr(s, "dec_ttl");
+        break;
+
     case OFPUTIL_NXAST_EXIT:
         ds_put_cstr(s, "exit");
         break;
@@ -599,13 +612,18 @@ ofp_print_switch_features(struct ds *string,
 static void
 ofp_print_switch_config(struct ds *string, const struct ofp_switch_config *osc)
 {
-    uint16_t flags;
+    enum ofp_config_flags flags;
 
     flags = ntohs(osc->flags);
 
     ds_put_format(string, " frags=%s", ofputil_frag_handling_to_string(flags));
     flags &= ~OFPC_FRAG_MASK;
 
+    if (flags & OFPC_INVALID_TTL_TO_CONTROLLER) {
+        ds_put_format(string, " invalid_ttl_to_controller");
+        flags &= ~OFPC_INVALID_TTL_TO_CONTROLLER;
+    }
+
     if (flags) {
         ds_put_format(string, " ***unknown flags 0x%04"PRIx16"***", flags);
     }
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index b20d3fb..6fe1611 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -2436,6 +2436,7 @@ validate_actions(const union ofp_action *actions, size_t n_actions,
         case OFPUTIL_NXAST_NOTE:
         case OFPUTIL_NXAST_SET_TUNNEL64:
         case OFPUTIL_NXAST_EXIT:
+        case OFPUTIL_NXAST_DEC_TTL:
             break;
         }
 
diff --git a/lib/ofp-util.def b/lib/ofp-util.def
index 2958eb6..d05ec9d 100644
--- a/lib/ofp-util.def
+++ b/lib/ofp-util.def
@@ -36,4 +36,5 @@ NXAST_ACTION(NXAST_RESUBMIT_TABLE, nx_action_resubmit,     0, NULL)
 NXAST_ACTION(NXAST_OUTPUT_REG,     nx_action_output_reg,   0, NULL)
 NXAST_ACTION(NXAST_LEARN,          nx_action_learn,        1, "learn")
 NXAST_ACTION(NXAST_EXIT,           nx_action_header,       0, "exit")
+NXAST_ACTION(NXAST_DEC_TTL,        nx_action_header,       0, "dec_ttl")
 #undef NXAST_ACTION
diff --git a/lib/ofp-util.h b/lib/ofp-util.h
index d01e17a..422c14a 100644
--- a/lib/ofp-util.h
+++ b/lib/ofp-util.h
@@ -227,7 +227,7 @@ struct ofputil_packet_in {
     const void *packet;
     size_t packet_len;
 
-    uint8_t reason;             /* One of OFPR_*. */
+    enum ofp_packet_in_reason reason;    /* One of OFPRR_*. */
     uint8_t table_id;
     ovs_be64 cookie;
 
diff --git a/ofproto/connmgr.c b/ofproto/connmgr.c
index 28d9488..887080a 100644
--- a/ofproto/connmgr.c
+++ b/ofproto/connmgr.c
@@ -71,6 +71,8 @@ struct ofconn {
 
     /* type == OFCONN_PRIMARY only. */
     enum nx_role role;           /* Role. */
+    bool invalid_ttl_to_controller; /* Send packets with invalid TTL
+                                       to the controller. */
     struct hmap_node hmap_node;  /* In struct connmgr's "controllers" map. */
     enum ofproto_band band;      /* In-band or out-of-band? */
 };
@@ -754,6 +756,18 @@ ofconn_set_role(struct ofconn *ofconn, enum nx_role role)
     ofconn->role = role;
 }
 
+void
+ofconn_set_invalid_ttl_to_controller(struct ofconn *ofconn, bool val)
+{
+    ofconn->invalid_ttl_to_controller = val;
+}
+
+bool
+ofconn_get_invalid_ttl_to_controller(struct ofconn *ofconn)
+{
+    return ofconn->invalid_ttl_to_controller;
+}
+
 /* Returns the currently configured flow format for 'ofconn', one of NXFF_*.
  *
  * The default, if no other format has been set, is NXFF_OPENFLOW10. */
@@ -931,6 +945,7 @@ ofconn_create(struct connmgr *mgr, struct rconn *rconn, enum ofconn_type type)
     ofconn->pktbuf = NULL;
     ofconn->miss_send_len = 0;
     ofconn->reply_counter = rconn_packet_counter_create ();
+    ofconn->invalid_ttl_to_controller = false;
     return ofconn;
 }
 
@@ -1054,11 +1069,9 @@ ofconn_wait(struct ofconn *ofconn, bool handling_openflow)
 
 /* Returns true if 'ofconn' should receive asynchronous messages. */
 static bool
-ofconn_receives_async_msgs(const struct ofconn *ofconn)
+ofconn_receives_async_msgs__(const struct ofconn *ofconn)
 {
-    if (!rconn_is_connected(ofconn->rconn)) {
-        return false;
-    } else if (ofconn->type == OFCONN_PRIMARY) {
+    if (ofconn->type == OFCONN_PRIMARY) {
         /* Primary controllers always get asynchronous messages unless they
          * have configured themselves as "slaves".  */
         return ofconn->role != NX_ROLE_SLAVE;
@@ -1069,6 +1082,29 @@ ofconn_receives_async_msgs(const struct ofconn *ofconn)
     }
 }
 
+static bool
+ofconn_receives_async_msgs(const struct ofconn *ofconn)
+{
+    if (!rconn_is_connected(ofconn->rconn)) {
+        return false;
+    } else {
+        return ofconn_receives_async_msgs__(ofconn);
+    }
+}
+
+static bool
+ofconn_interested_in_packet(const struct ofconn *ofconn,
+                            const struct ofputil_packet_in *pin)
+{
+    if (!rconn_is_connected(ofconn->rconn)) {
+        return false;
+    } else if (pin->reason == OFPR_INVALID_TTL) {
+        return ofconn->invalid_ttl_to_controller;
+    } else {
+        return ofconn_receives_async_msgs__(ofconn);
+    }
+}
+
 /* Returns a human-readable name for an OpenFlow connection between 'mgr' and
  * 'target', suitable for use in log messages for identifying the connection.
  *
@@ -1178,7 +1214,7 @@ connmgr_send_packet_in(struct connmgr *mgr,
     struct ofconn *ofconn;
 
     LIST_FOR_EACH (ofconn, node, &mgr->all_conns) {
-        if (ofconn_receives_async_msgs(ofconn)) {
+        if (ofconn_interested_in_packet(ofconn, pin)) {
             schedule_packet_in(ofconn, *pin, flow);
         }
     }
diff --git a/ofproto/connmgr.h b/ofproto/connmgr.h
index bbee7f4..8ff89f3 100644
--- a/ofproto/connmgr.h
+++ b/ofproto/connmgr.h
@@ -92,6 +92,9 @@ void ofconn_set_packet_in_format(struct ofconn *, enum nx_packet_in_format);
 bool ofconn_get_flow_mod_table_id(const struct ofconn *);
 void ofconn_set_flow_mod_table_id(struct ofconn *, bool enable);
 
+void ofconn_set_invalid_ttl_to_controller(struct ofconn *, bool);
+bool ofconn_get_invalid_ttl_to_controller(struct ofconn *);
+
 int ofconn_get_miss_send_len(const struct ofconn *);
 void ofconn_set_miss_send_len(struct ofconn *, int miss_send_len);
 
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index 6ecf71b..567240d 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -4291,7 +4291,8 @@ flood_packets(struct action_xlate_ctx *ctx, bool all)
 }
 
 static void
-execute_controller_action(struct action_xlate_ctx *ctx, int len)
+execute_controller_action(struct action_xlate_ctx *ctx, int len,
+                          enum ofp_packet_in_reason reason)
 {
     struct ofputil_packet_in pin;
     struct ofpbuf *packet;
@@ -4336,7 +4337,7 @@ execute_controller_action(struct action_xlate_ctx *ctx, int len)
 
     pin.packet = packet->data;
     pin.packet_len = packet->size;
-    pin.reason = OFPR_ACTION;
+    pin.reason = reason;
     pin.table_id = ctx->table_id;
     pin.cookie = ctx->cookie;
 
@@ -4349,6 +4350,25 @@ execute_controller_action(struct action_xlate_ctx *ctx, int len)
     ofpbuf_delete(packet);
 }
 
+static bool
+compose_dec_ttl(struct action_xlate_ctx *ctx)
+{
+    if (ctx->flow.dl_type != htons(ETH_TYPE_IP) &&
+        ctx->flow.dl_type != htons(ETH_TYPE_IPV6)) {
+        return false;
+    }
+
+    if (ctx->flow.nw_ttl > 1) {
+        ctx->flow.nw_ttl--;
+        return false;
+    } else {
+        execute_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL);
+
+        /* Stop processing for current table. */
+        return true;
+    }
+}
+
 static void
 xlate_output_action__(struct action_xlate_ctx *ctx,
                       uint16_t port, uint16_t max_len)
@@ -4374,7 +4394,7 @@ xlate_output_action__(struct action_xlate_ctx *ctx,
         flood_packets(ctx, true);
         break;
     case OFPP_CONTROLLER:
-        execute_controller_action(ctx, max_len);
+        execute_controller_action(ctx, max_len, OFPR_ACTION);
         break;
     case OFPP_LOCAL:
         compose_output_action(ctx, OFPP_LOCAL);
@@ -4730,12 +4750,19 @@ do_xlate_actions(const union ofp_action *in, size_t n_in,
             }
             break;
 
+        case OFPUTIL_NXAST_DEC_TTL:
+            if (compose_dec_ttl(ctx)) {
+                goto out;
+            }
+            break;
+
         case OFPUTIL_NXAST_EXIT:
             ctx->exit = true;
             break;
         }
     }
 
+out:
     /* We've let OFPP_NORMAL and the learning action look at the packet,
      * so drop it now if forwarding is disabled. */
     if (port && !stp_forward_in_state(port->stp_state)) {
@@ -4799,6 +4826,9 @@ xlate_actions(struct action_xlate_ctx *ctx,
         case OFPC_FRAG_NX_MATCH:
             /* Nothing to do. */
             break;
+
+        case OFPC_INVALID_TTL_TO_CONTROLLER:
+            NOT_REACHED();
         }
     }
 
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index 29259e4..0504026 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -1746,11 +1746,16 @@ handle_get_config_request(struct ofconn *ofconn, const struct ofp_header *oh)
 {
     struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
     struct ofp_switch_config *osc;
+    enum ofp_config_flags flags;
     struct ofpbuf *buf;
 
     /* Send reply. */
     osc = make_openflow_xid(sizeof *osc, OFPT_GET_CONFIG_REPLY, oh->xid, &buf);
-    osc->flags = htons(ofproto->frag_handling);
+    flags = ofproto->frag_handling;
+    if (ofconn_get_invalid_ttl_to_controller(ofconn)) {
+        flags |= OFPC_INVALID_TTL_TO_CONTROLLER;
+    }
+    osc->flags = htons(flags);
     osc->miss_send_len = htons(ofconn_get_miss_send_len(ofconn));
     ofconn_send_reply(ofconn, buf);
 
@@ -1779,6 +1784,8 @@ handle_set_config(struct ofconn *ofconn, const struct ofp_switch_config *osc)
             }
         }
     }
+    ofconn_set_invalid_ttl_to_controller(ofconn,
+			 (flags & OFPC_INVALID_TTL_TO_CONTROLLER));
 
     ofconn_set_miss_send_len(ofconn, ntohs(osc->miss_send_len));
 
diff --git a/tests/ofp-print.at b/tests/ofp-print.at
index 0619e98..85562b6 100644
--- a/tests/ofp-print.at
+++ b/tests/ofp-print.at
@@ -269,7 +269,7 @@ AT_CHECK([ovs-ofctl ofp-print "\
 c0 a8 00 02 27 2f 00 00 78 50 cc 5b 57 af 42 1e \
 50 00 02 00 26 e8 00 00 00 00 00 00 00 00 \
 "], [0], [dnl
-OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=3 data_len=60 buffer=0x00000111
+OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=3 (via no_match) data_len=60 buffer=0x00000111
 priority:0,tunnel:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:06) type:0800 proto:6 tos:0 ttl:64 ip(192.168.0.1->192.168.0.2) port(10031->0) tcp_csum:26e8
 ])
 AT_CLEANUP
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index 2c4e7c4..bb00714 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -62,6 +62,38 @@ AT_CHECK([tail -1 stdout], [0],
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([ofproto-dpif - dec_ttl])
+OVS_VSWITCHD_START
+AT_DATA([flows.txt], [dnl
+table=0 in_port=1 action=dec_ttl,output:2,resubmit(1,1),output:4
+table=1 in_port=1 action=dec_ttl,output:3
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=2,frag=no)' -generate], [0], [stdout])
+AT_CHECK([tail -2 stdout], [0],
+  [Datapath actions: set(ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=1,frag=no)),2,4
+This flow is not cachable.
+])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=3,frag=no)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: set(ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=2,frag=no)),2,set(ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=1,frag=no)),3,4
+])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=10,tclass=0x70,hlimit=128,frag=no)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: set(ipv6(src=::1,dst=::2,label=0,proto=10,tclass=0x70,hlimit=127,frag=no)),2,set(ipv6(src=::1,dst=::2,label=0,proto=10,tclass=0x70,hlimit=126,frag=no)),3,4
+])
+
+AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl --detach --pidfile 2> ofctl_monitor.log])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=2,frag=no)' -generate], [0], [stdout])
+OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
+AT_CHECK([cat ofctl_monitor.log], [0], [dnl
+NXT_PACKET_IN (xid=0x0): table_id=1 total_len=42 in_port=1 tun_id=0x0 reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 (via invalid_ttl) data_len=42 (unbuffered)
+priority:0,tunnel:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:07) type:0800 proto:1 tos:0 ttl:1 ip(192.168.0.1->192.168.0.2)
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+
 AT_SETUP([ofproto-dpif - output, OFPP_NONE ingress port])
 OVS_VSWITCHD_START(
        [add-port br0 p1 -- set Interface p1 type=dummy --\
@@ -209,13 +241,13 @@ done
 
 OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
-OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 data_len=60 (unbuffered)
+OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via no_match) data_len=60 (unbuffered)
 priority:0,tunnel:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->9) tcp_csum:0
 dnl
-OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 data_len=60 (unbuffered)
+OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via no_match) data_len=60 (unbuffered)
 priority:0,tunnel:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->9) tcp_csum:0
 dnl
-OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 data_len=60 (unbuffered)
+OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via no_match) data_len=60 (unbuffered)
 priority:0,tunnel:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->9) tcp_csum:0
 ])
 
diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
index 4bfd543..9203370 100644
--- a/utilities/ovs-ofctl.8.in
+++ b/utilities/ovs-ofctl.8.in
@@ -778,6 +778,16 @@ OpenFlow implementations do not support queuing at all.
 Restores the queue to the value it was before any \fBset_queue\fR
 actions were applied.
 .
+.IP \fBdec_ttl\fR
+Decrement TTL of IPv4 packet or hop limit of IPv6 packet.  If the
+TTL or hop limit is initially zero, no decrement occurs.  Instead,
+a ``packet-in'' message with reason code \fBOFPR_INVALID_TTL\fR is
+sent to each connected controller that has enabled receiving them,
+if any.  Processing the current 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 \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
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index 6219f94..8a8b8b2 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -794,6 +794,35 @@ set_packet_in_format(struct vconn *vconn,
              ofputil_packet_in_format_to_string(packet_in_format));
 }
 
+static int
+monitor_set_invalid_ttl_to_controller(struct vconn *vconn)
+{
+    struct ofp_switch_config config;
+    enum ofp_config_flags flags;
+
+    fetch_switch_config(vconn, &config);
+    flags = ntohs(config.flags);
+    if (!(flags & OFPC_INVALID_TTL_TO_CONTROLLER)) {
+        /* Set the invalid ttl config. */
+        flags |= OFPC_INVALID_TTL_TO_CONTROLLER;
+
+        config.flags = htons(flags);
+        set_switch_config(vconn, &config);
+
+        /* Then retrieve the configuration to see if it really took.  OpenFlow
+         * doesn't define error reporting for bad modes, so this is all we can
+         * do. */
+        fetch_switch_config(vconn, &config);
+        flags = ntohs(config.flags);
+        if (!(flags & OFPC_INVALID_TTL_TO_CONTROLLER)) {
+            ovs_fatal(0, "setting invalid_ttl_to_controller failed (this "
+                      "switch probably doesn't support mode)");
+            return -EOPNOTSUPP;
+        }
+    }
+    return 0;
+}
+
 static void
 monitor_vconn(struct vconn *vconn)
 {
@@ -876,6 +905,11 @@ do_monitor(int argc, char *argv[])
         config.miss_send_len = htons(atoi(argv[2]));
         set_switch_config(vconn, &config);
     }
+    if (argc > 3) {
+        if (!strcmp(argv[3], "invalid_ttl")) {
+            monitor_set_invalid_ttl_to_controller(vconn);
+        }
+    }
     monitor_vconn(vconn);
 }
 
@@ -1634,7 +1668,7 @@ do_ofp_print(int argc, char *argv[])
 
 static const struct command all_commands[] = {
     { "show", 1, 1, do_show },
-    { "monitor", 1, 2, do_monitor },
+    { "monitor", 1, 3, do_monitor },
     { "snoop", 1, 1, do_snoop },
     { "dump-desc", 1, 1, do_dump_desc },
     { "dump-tables", 1, 1, do_dump_tables },
-- 
1.7.1




More information about the dev mailing list