ofproto: New action TTL decrement.
authorPravin B Shelar <pshelar@nicira.com>
Sat, 14 Jan 2012 01:54:04 +0000 (17:54 -0800)
committerPravin B Shelar <pshelar@nicira.com>
Sat, 14 Jan 2012 01:54:04 +0000 (17:54 -0800)
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. A configuration parameter is added to make TTL
decrement to zero generate packet in.

Feature #8758
Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
16 files changed:
NEWS
include/openflow/nicira-ext.h
include/openflow/openflow.h
lib/ofp-parse.c
lib/ofp-print.c
lib/ofp-util.c
lib/ofp-util.def
lib/ofp-util.h
ofproto/connmgr.c
ofproto/connmgr.h
ofproto/ofproto-dpif.c
ofproto/ofproto.c
tests/ofp-print.at
tests/ofproto-dpif.at
utilities/ovs-ofctl.8.in
utilities/ovs-ofctl.c

diff --git a/NEWS b/NEWS
index b628e29..4f29e19 100644 (file)
--- 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:
index f00f994..e17b313 100644 (file)
@@ -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. */
index f68a140..cd30d32 100644 (file)
@@ -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). */
index 38c3dab..5321364 100644 (file)
@@ -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;
     }
 }
 
index aff12b6..7bc26c9 100644 (file)
@@ -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);
     }
index b20d3fb..6fe1611 100644 (file)
@@ -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;
         }
 
index 2958eb6..d05ec9d 100644 (file)
@@ -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
index d01e17a..422c14a 100644 (file)
@@ -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;
 
index 28d9488..887080a 100644 (file)
@@ -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);
         }
     }
index bbee7f4..8ff89f3 100644 (file)
@@ -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);
 
index 6ecf71b..567240d 100644 (file)
@@ -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();
         }
     }
 
index 29259e4..0504026 100644 (file)
@@ -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));
 
index 0619e98..85562b6 100644 (file)
@@ -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
index 2c4e7c4..bb00714 100644 (file)
@@ -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
 ])
 
index 4bfd543..2a20a2f 100644 (file)
@@ -243,7 +243,7 @@ If a switch has no controller configured, or if
 the configured controller is disconnected, no traffic is sent, so
 monitoring will not show any traffic.
 .
-.IP "\fBmonitor \fIswitch\fR [\fImiss-len\fR]"
+.IP "\fBmonitor \fIswitch\fR [\fImiss-len\fR] [\fIinvalid_ttl\fR]"
 Connects to \fIswitch\fR and prints to the console all OpenFlow
 messages received.  Usually, \fIswitch\fR should specify the name of a
 bridge in the \fBovs\-vswitchd\fR database.
@@ -256,6 +256,13 @@ does not send these and other asynchronous messages to an
 specified on this argument.  (Thus, if \fImiss\-len\fR is not
 specified, very little traffic will ordinarily be printed.)
 .IP
+.IP
+If \fBinvalid_ttl\fR is passed, \fBovs\-ofctl\fR sends an OpenFlow ``set
+configuration'' message at connection setup time that requests
+\fIINVALID_TTL_TO_CONTROLLER\fR, so that \fBovs\-ofctl monitor\fR can
+receive ``packets-in'' messages when TTL reaches zero on \fBdec_ttl\fR action.
+.IP
+
 This command may be useful for debugging switch or controller
 implementations.
 .
@@ -778,6 +785,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
index 6219f94..8a8b8b2 100644 (file)
@@ -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 },