Add Nicira vendor extension action NXAST_DEC_TTL_CNT_IDS.
authorMehak Mahajan <mmahajan@nicira.com>
Thu, 16 Aug 2012 21:25:07 +0000 (14:25 -0700)
committerMehak Mahajan <mmahajan@nicira.com>
Thu, 16 Aug 2012 21:52:04 +0000 (14:52 -0700)
Currently, if a controller having a nonzero id registers to get a
OFPR_INVALID_TTL async message, it will not receive it.  This is because
compose_dec_ttl() only sent the invalid ttl packets to the default controller
id.  NXAST_DEC_TTL_CNT_IDS is a new action that accepts a list of controller
ids, each separated by `,', to which the OFPR_INVALID_TTL packets must be sent.
The earlier requirement of the controller having to explicitly register to
receive these asynchronous messages is retained.
The syntax of this action is:
    dec_ttl(id1,id2)
where id1, id2 are valid controller ids.

Signed-off-by: Mehak Mahajan <mmahajan@nicira.com>
include/openflow/nicira-ext.h
lib/ofp-actions.c
lib/ofp-actions.h
lib/ofp-parse.c
lib/ofp-util.def
ofproto/ofproto-dpif.c
tests/ofp-actions.at
utilities/ovs-ofctl.8.in

index 4fc2049..d1d3654 100644 (file)
@@ -294,6 +294,7 @@ enum nx_action_subtype {
     NXAST_DEC_TTL,              /* struct nx_action_header */
     NXAST_FIN_TIMEOUT,          /* struct nx_action_fin_timeout */
     NXAST_CONTROLLER,           /* struct nx_action_controller */
     NXAST_DEC_TTL,              /* struct nx_action_header */
     NXAST_FIN_TIMEOUT,          /* struct nx_action_fin_timeout */
     NXAST_CONTROLLER,           /* struct nx_action_controller */
+    NXAST_DEC_TTL_CNT_IDS,      /* struct nx_action_cnt_ids */
 };
 
 /* Header for Nicira-defined actions. */
 };
 
 /* Header for Nicira-defined actions. */
@@ -1060,6 +1061,35 @@ enum nx_bd_algorithm {
      * Uses the 'fields' and 'basis' parameters. */
     NX_BD_ALG_HRW /* Highest Random Weight. */
 };
      * Uses the 'fields' and 'basis' parameters. */
     NX_BD_ALG_HRW /* Highest Random Weight. */
 };
+
+\f
+/* Action structure for NXAST_DEC_TTL_CNT_IDS.
+ *
+ * If the packet is not IPv4 or IPv6, does nothing.  For IPv4 or IPv6, if the
+ * TTL or hop limit is at least 2, decrements it by 1.  Otherwise, if TTL or
+ * hop limit is 0 or 1, sends a packet-in to the controllers with each of the
+ * 'n_controllers' controller IDs specified in 'cnt_ids'.
+ *
+ * (This differs from NXAST_DEC_TTL in that for NXAST_DEC_TTL the packet-in is
+ * sent only to controllers with id 0.)
+ */
+struct nx_action_cnt_ids {
+    ovs_be16 type;              /* OFPAT_VENDOR. */
+    ovs_be16 len;               /* Length including slaves. */
+    ovs_be32 vendor;            /* NX_VENDOR_ID. */
+    ovs_be16 subtype;           /* NXAST_DEC_TTL_CNT_IDS. */
+
+    ovs_be16 n_controllers;     /* Number of controllers. */
+    uint8_t zeros[4];           /* Must be zero. */
+
+    /* Followed by 1 or more controller ids.
+     *
+     * uint16_t cnt_ids[];        // Controller ids.
+     * uint8_t pad[];           // Must be 0 to 8-byte align cnt_ids[].
+     */
+};
+OFP_ASSERT(sizeof(struct nx_action_cnt_ids) == 16);
+
 \f
 /* Action structure for NXAST_OUTPUT_REG.
  *
 \f
 /* Action structure for NXAST_OUTPUT_REG.
  *
index 6503f61..db603b6 100644 (file)
@@ -148,6 +148,58 @@ note_from_openflow(const struct nx_action_note *nan, struct ofpbuf *out)
     memcpy(note->data, nan->note, length);
 }
 
     memcpy(note->data, nan->note, length);
 }
 
+static enum ofperr
+dec_ttl_from_openflow(struct ofpbuf *out)
+{
+    uint16_t id = 0;
+    struct ofpact_cnt_ids *ids;
+    enum ofperr error = 0;
+
+    ids = ofpact_put_DEC_TTL(out);
+    ids->ofpact.compat = OFPUTIL_NXAST_DEC_TTL;
+    ids->n_controllers = 1;
+    ofpbuf_put(out, &id, sizeof id);
+    ids = out->l2;
+    ofpact_update_len(out, &ids->ofpact);
+    return error;
+}
+
+static enum ofperr
+dec_ttl_cnt_ids_from_openflow(const struct nx_action_cnt_ids *nac_ids,
+                      struct ofpbuf *out)
+{
+    struct ofpact_cnt_ids *ids;
+    size_t ids_size;
+    int i;
+
+    ids = ofpact_put_DEC_TTL(out);
+    ids->ofpact.compat = OFPUTIL_NXAST_DEC_TTL_CNT_IDS;
+    ids->n_controllers = ntohs(nac_ids->n_controllers);
+    ids_size = ntohs(nac_ids->len) - sizeof *nac_ids;
+
+    if (!is_all_zeros(nac_ids->zeros, sizeof nac_ids->zeros)) {
+        return OFPERR_NXBRC_MUST_BE_ZERO;
+    }
+
+    if (ids_size < ids->n_controllers * sizeof(ovs_be16)) {
+        VLOG_WARN_RL(&rl, "Nicira action dec_ttl_cnt_ids only has %zu bytes "
+                     "allocated for controller ids.  %zu bytes are required for "
+                     "%"PRIu16" controllers.", ids_size,
+                     ids->n_controllers * sizeof(ovs_be16), ids->n_controllers);
+        return OFPERR_OFPBAC_BAD_LEN;
+    }
+
+    for (i = 0; i < ids->n_controllers; i++) {
+        uint16_t id = ntohs(((ovs_be16 *)(nac_ids + 1))[i]);
+        ofpbuf_put(out, &id, sizeof id);
+    }
+
+    ids = out->l2;
+    ofpact_update_len(out, &ids->ofpact);
+
+    return 0;
+}
+
 static enum ofperr
 decode_nxast_action(const union ofp_action *a, enum ofputil_action_code *code)
 {
 static enum ofperr
 decode_nxast_action(const union ofp_action *a, enum ofputil_action_code *code)
 {
@@ -310,7 +362,12 @@ ofpact_from_nxast(const union ofp_action *a, enum ofputil_action_code code,
         break;
 
     case OFPUTIL_NXAST_DEC_TTL:
         break;
 
     case OFPUTIL_NXAST_DEC_TTL:
-        ofpact_put_DEC_TTL(out);
+        error = dec_ttl_from_openflow(out);
+        break;
+
+    case OFPUTIL_NXAST_DEC_TTL_CNT_IDS:
+        error = dec_ttl_cnt_ids_from_openflow(
+                    (const struct nx_action_cnt_ids *) a, out);
         break;
 
     case OFPUTIL_NXAST_FIN_TIMEOUT:
         break;
 
     case OFPUTIL_NXAST_FIN_TIMEOUT:
@@ -1082,6 +1139,29 @@ ofpact_controller_to_nxast(const struct ofpact_controller *oc,
     nac->reason = oc->reason;
 }
 
     nac->reason = oc->reason;
 }
 
+static void
+ofpact_dec_ttl_to_nxast(const struct ofpact_cnt_ids *oc_ids,
+                        struct ofpbuf *out)
+{
+    if (oc_ids->ofpact.compat == OFPUTIL_NXAST_DEC_TTL) {
+        ofputil_put_NXAST_DEC_TTL(out);
+    } else {
+        struct nx_action_cnt_ids *nac_ids =
+            ofputil_put_NXAST_DEC_TTL_CNT_IDS(out);
+        int ids_len = ROUND_UP(2 * oc_ids->n_controllers, OFP_ACTION_ALIGN);
+        ovs_be16 *ids;
+        size_t i;
+
+        nac_ids->len = htons(ntohs(nac_ids->len) + ids_len);
+        nac_ids->n_controllers = htons(oc_ids->n_controllers);
+
+        ids = ofpbuf_put_zeros(out, ids_len);
+        for (i = 0; i < oc_ids->n_controllers; i++) {
+            ids[i] = htons(oc_ids->cnt_ids[i]);
+        }
+    }
+}
+
 static void
 ofpact_fin_timeout_to_nxast(const struct ofpact_fin_timeout *fin_timeout,
                             struct ofpbuf *out)
 static void
 ofpact_fin_timeout_to_nxast(const struct ofpact_fin_timeout *fin_timeout,
                             struct ofpbuf *out)
@@ -1116,7 +1196,7 @@ ofpact_to_nxast(const struct ofpact *a, struct ofpbuf *out)
         break;
 
     case OFPACT_DEC_TTL:
         break;
 
     case OFPACT_DEC_TTL:
-        ofputil_put_NXAST_DEC_TTL(out);
+        ofpact_dec_ttl_to_nxast(ofpact_get_DEC_TTL(a), out);
         break;
 
     case OFPACT_SET_TUNNEL:
         break;
 
     case OFPACT_SET_TUNNEL:
@@ -1511,6 +1591,25 @@ print_note(const struct ofpact_note *note, struct ds *string)
     }
 }
 
     }
 }
 
+static void
+print_dec_ttl(const struct ofpact_cnt_ids *ids,
+              struct ds *s)
+{
+    size_t i;
+
+    ds_put_cstr(s, "dec_ttl");
+    if (ids->ofpact.compat == OFPUTIL_NXAST_DEC_TTL_CNT_IDS) {
+        ds_put_cstr(s, "(");
+        for (i = 0; i < ids->n_controllers; i++) {
+            if (i) {
+                ds_put_cstr(s, ",");
+            }
+            ds_put_format(s, "%"PRIu16, ids->cnt_ids[i]);
+        }
+        ds_put_cstr(s, ")");
+    }
+}
+
 static void
 print_fin_timeout(const struct ofpact_fin_timeout *fin_timeout,
                   struct ds *s)
 static void
 print_fin_timeout(const struct ofpact_fin_timeout *fin_timeout,
                   struct ds *s)
@@ -1647,7 +1746,7 @@ ofpact_format(const struct ofpact *a, struct ds *s)
         break;
 
     case OFPACT_DEC_TTL:
         break;
 
     case OFPACT_DEC_TTL:
-        ds_put_cstr(s, "dec_ttl");
+        print_dec_ttl(ofpact_get_DEC_TTL(a), s);
         break;
 
     case OFPACT_SET_TUNNEL:
         break;
 
     case OFPACT_SET_TUNNEL:
index 2e02181..2003668 100644 (file)
@@ -69,7 +69,7 @@
     DEFINE_OFPACT(SET_L4_DST_PORT, ofpact_l4_port,       ofpact)    \
     DEFINE_OFPACT(REG_MOVE,        ofpact_reg_move,      ofpact)    \
     DEFINE_OFPACT(REG_LOAD,        ofpact_reg_load,      ofpact)    \
     DEFINE_OFPACT(SET_L4_DST_PORT, ofpact_l4_port,       ofpact)    \
     DEFINE_OFPACT(REG_MOVE,        ofpact_reg_move,      ofpact)    \
     DEFINE_OFPACT(REG_LOAD,        ofpact_reg_load,      ofpact)    \
-    DEFINE_OFPACT(DEC_TTL,         ofpact_null,          ofpact)    \
+    DEFINE_OFPACT(DEC_TTL,         ofpact_cnt_ids,       cnt_ids)   \
                                                                     \
     /* Metadata. */                                                 \
     DEFINE_OFPACT(SET_TUNNEL,      ofpact_tunnel,        ofpact)    \
                                                                     \
     /* Metadata. */                                                 \
     DEFINE_OFPACT(SET_TUNNEL,      ofpact_tunnel,        ofpact)    \
@@ -145,7 +145,7 @@ ofpact_end(const struct ofpact *ofpacts, size_t ofpacts_len)
 \f
 /* Action structure for each OFPACT_*. */
 
 \f
 /* Action structure for each OFPACT_*. */
 
-/* OFPACT_STRIP_VLAN, OFPACT_DEC_TTL, OFPACT_POP_QUEUE, OFPACT_EXIT.
+/* OFPACT_STRIP_VLAN, OFPACT_POP_QUEUE, OFPACT_EXIT.
  *
  * Used for OFPAT10_STRIP_VLAN, NXAST_DEC_TTL, NXAST_POP_QUEUE, NXAST_EXIT.
  *
  *
  * Used for OFPAT10_STRIP_VLAN, NXAST_DEC_TTL, NXAST_POP_QUEUE, NXAST_EXIT.
  *
@@ -380,6 +380,18 @@ struct ofpact_note {
     uint8_t data[];
 };
 
     uint8_t data[];
 };
 
+/* OFPACT_DEC_TTL.
+ *
+ * Used for NXAST_DEC_TTL and NXAST_DEC_TTL_CNT_IDS. */
+struct ofpact_cnt_ids {
+    struct ofpact ofpact;
+
+    /* Controller ids. */
+    unsigned int n_controllers;
+    uint16_t cnt_ids[];
+
+};
+
 /* Converting OpenFlow to ofpacts. */
 enum ofperr ofpacts_pull_openflow10(struct ofpbuf *openflow,
                                     unsigned int actions_len,
 /* Converting OpenFlow to ofpacts. */
 enum ofperr ofpacts_pull_openflow10(struct ofpbuf *openflow,
                                     unsigned int actions_len,
index 32d3836..e5f5ea0 100644 (file)
@@ -278,6 +278,41 @@ parse_controller(struct ofpbuf *b, char *arg)
     }
 }
 
     }
 }
 
+static void
+parse_dec_ttl(struct ofpbuf *b, char *arg)
+{
+    struct ofpact_cnt_ids *ids;
+
+    ids = ofpact_put_DEC_TTL(b);
+
+    if (*arg == '\0') {
+        uint16_t id = 0;
+
+        ids->ofpact.compat = OFPUTIL_NXAST_DEC_TTL;
+        ofpbuf_put(b, &id, sizeof id);
+        ids = b->l2;
+        ids->n_controllers++;
+    } else {
+        char *cntr;
+
+        ids->ofpact.compat = OFPUTIL_NXAST_DEC_TTL_CNT_IDS;
+        for (cntr = strtok_r(arg, ", ", &arg); cntr != NULL;
+             cntr = strtok_r(NULL, ", ", &arg)) {
+            uint16_t id = atoi(cntr);
+
+            ofpbuf_put(b, &id, sizeof id);
+            ids = b->l2;
+            ids->n_controllers++;
+        }
+        if (!ids->n_controllers) {
+            ovs_fatal(0, "dec_ttl_cnt_ids: expected at least one controller "
+                      "id.");
+        }
+
+    }
+    ofpact_update_len(b, &ids->ofpact);
+}
+
 static void
 parse_named_action(enum ofputil_action_code code, const struct flow *flow,
                    char *arg, struct ofpbuf *ofpacts)
 static void
 parse_named_action(enum ofputil_action_code code, const struct flow *flow,
                    char *arg, struct ofpbuf *ofpacts)
@@ -413,6 +448,7 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow,
 
     case OFPUTIL_NXAST_RESUBMIT_TABLE:
     case OFPUTIL_NXAST_OUTPUT_REG:
 
     case OFPUTIL_NXAST_RESUBMIT_TABLE:
     case OFPUTIL_NXAST_OUTPUT_REG:
+    case OFPUTIL_NXAST_DEC_TTL_CNT_IDS:
         NOT_REACHED();
 
     case OFPUTIL_NXAST_LEARN:
         NOT_REACHED();
 
     case OFPUTIL_NXAST_LEARN:
@@ -424,7 +460,7 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow,
         break;
 
     case OFPUTIL_NXAST_DEC_TTL:
         break;
 
     case OFPUTIL_NXAST_DEC_TTL:
-        ofpact_put_DEC_TTL(ofpacts);
+        parse_dec_ttl(ofpacts, arg);
         break;
 
     case OFPUTIL_NXAST_FIN_TIMEOUT:
         break;
 
     case OFPUTIL_NXAST_FIN_TIMEOUT:
index 974cd8f..619bb88 100644 (file)
@@ -39,25 +39,26 @@ OFPAT11_ACTION(OFPAT11_SET_TP_DST,   ofp_action_tp_port,  "mod_tp_dst")
 #ifndef NXAST_ACTION
 #define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME)
 #endif
 #ifndef NXAST_ACTION
 #define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME)
 #endif
-NXAST_ACTION(NXAST_RESUBMIT,       nx_action_resubmit,     0, "resubmit")
-NXAST_ACTION(NXAST_SET_TUNNEL,     nx_action_set_tunnel,   0, "set_tunnel")
-NXAST_ACTION(NXAST_SET_QUEUE,      nx_action_set_queue,    0, "set_queue")
-NXAST_ACTION(NXAST_POP_QUEUE,      nx_action_pop_queue,    0, "pop_queue")
-NXAST_ACTION(NXAST_REG_MOVE,       nx_action_reg_move,     0, "move")
-NXAST_ACTION(NXAST_REG_LOAD,       nx_action_reg_load,     0, "load")
-NXAST_ACTION(NXAST_NOTE,           nx_action_note,         1, "note")
-NXAST_ACTION(NXAST_SET_TUNNEL64,   nx_action_set_tunnel64, 0, "set_tunnel64")
-NXAST_ACTION(NXAST_MULTIPATH,      nx_action_multipath,    0, "multipath")
-NXAST_ACTION(NXAST_AUTOPATH,       nx_action_autopath,     0, "autopath")
-NXAST_ACTION(NXAST_BUNDLE,         nx_action_bundle,       1, "bundle")
-NXAST_ACTION(NXAST_BUNDLE_LOAD,    nx_action_bundle,       1, "bundle_load")
-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")
-NXAST_ACTION(NXAST_FIN_TIMEOUT,    nx_action_fin_timeout,  0, "fin_timeout")
-NXAST_ACTION(NXAST_CONTROLLER,     nx_action_controller,   0, "controller")
+NXAST_ACTION(NXAST_RESUBMIT,        nx_action_resubmit,     0, "resubmit")
+NXAST_ACTION(NXAST_SET_TUNNEL,      nx_action_set_tunnel,   0, "set_tunnel")
+NXAST_ACTION(NXAST_SET_QUEUE,       nx_action_set_queue,    0, "set_queue")
+NXAST_ACTION(NXAST_POP_QUEUE,       nx_action_pop_queue,    0, "pop_queue")
+NXAST_ACTION(NXAST_REG_MOVE,        nx_action_reg_move,     0, "move")
+NXAST_ACTION(NXAST_REG_LOAD,        nx_action_reg_load,     0, "load")
+NXAST_ACTION(NXAST_NOTE,            nx_action_note,         1, "note")
+NXAST_ACTION(NXAST_SET_TUNNEL64,    nx_action_set_tunnel64, 0, "set_tunnel64")
+NXAST_ACTION(NXAST_MULTIPATH,       nx_action_multipath,    0, "multipath")
+NXAST_ACTION(NXAST_AUTOPATH,        nx_action_autopath,     0, "autopath")
+NXAST_ACTION(NXAST_BUNDLE,          nx_action_bundle,       1, "bundle")
+NXAST_ACTION(NXAST_BUNDLE_LOAD,     nx_action_bundle,       1, "bundle_load")
+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")
+NXAST_ACTION(NXAST_FIN_TIMEOUT,     nx_action_fin_timeout,  0, "fin_timeout")
+NXAST_ACTION(NXAST_CONTROLLER,      nx_action_controller,   0, "controller")
+NXAST_ACTION(NXAST_DEC_TTL_CNT_IDS, nx_action_cnt_ids,      1, NULL)
 
 #undef OFPAT10_ACTION
 #undef OFPAT11_ACTION
 
 #undef OFPAT10_ACTION
 #undef OFPAT11_ACTION
index 3d68855..d66c500 100644 (file)
@@ -5159,7 +5159,7 @@ execute_controller_action(struct action_xlate_ctx *ctx, int len,
 }
 
 static bool
 }
 
 static bool
-compose_dec_ttl(struct action_xlate_ctx *ctx)
+compose_dec_ttl(struct action_xlate_ctx *ctx, struct ofpact_cnt_ids *ids)
 {
     if (ctx->flow.dl_type != htons(ETH_TYPE_IP) &&
         ctx->flow.dl_type != htons(ETH_TYPE_IPV6)) {
 {
     if (ctx->flow.dl_type != htons(ETH_TYPE_IP) &&
         ctx->flow.dl_type != htons(ETH_TYPE_IPV6)) {
@@ -5170,7 +5170,12 @@ compose_dec_ttl(struct action_xlate_ctx *ctx)
         ctx->flow.nw_ttl--;
         return false;
     } else {
         ctx->flow.nw_ttl--;
         return false;
     } else {
-        execute_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL, 0);
+        size_t i;
+
+        for (i = 0; i < ids->n_controllers; i++) {
+            execute_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL,
+                                      ids->cnt_ids[i]);
+        }
 
         /* Stop processing for current table. */
         return true;
 
         /* Stop processing for current table. */
         return true;
@@ -5530,7 +5535,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             break;
 
         case OFPACT_DEC_TTL:
             break;
 
         case OFPACT_DEC_TTL:
-            if (compose_dec_ttl(ctx)) {
+            if (compose_dec_ttl(ctx, ofpact_get_DEC_TTL(a))) {
                 goto out;
             }
             break;
                 goto out;
             }
             break;
index ba8d309..36c67f1 100644 (file)
@@ -108,6 +108,9 @@ ffff 0010 00002320 0013 000a 0014 0000
 # actions=controller(reason=invalid_ttl,max_len=1234,id=5678)
 ffff 0010 00002320 0014 04d2 162e 02 00
 
 # actions=controller(reason=invalid_ttl,max_len=1234,id=5678)
 ffff 0010 00002320 0014 04d2 162e 02 00
 
+# actions=dec_ttl(32768,12345,90,765,1024)
+ffff 0020 00002320 0015 000500000000 80003039005A02fd 0400000000000000
+
 ])
 sed '/^[[#&]]/d' < test-data > input.txt
 sed -n 's/^# //p; /^$/p' < test-data > expout
 ])
 sed '/^[[#&]]/d' < test-data > input.txt
 sed -n 's/^# //p; /^$/p' < test-data > expout
@@ -222,6 +225,9 @@ ffff 0010 00002320 0013 000a 0014 0000
 # actions=controller(reason=invalid_ttl,max_len=1234,id=5678)
 ffff 0010 00002320 0014 04d2 162e 02 00
 
 # actions=controller(reason=invalid_ttl,max_len=1234,id=5678)
 ffff 0010 00002320 0014 04d2 162e 02 00
 
+# actions=dec_ttl(32768,12345,90,765,1024)
+ffff 0020 00002320 0015 000500000000 80003039005A02fd 0400000000000000
+
 ])
 sed '/^[[#&]]/d' < test-data > input.txt
 sed -n 's/^# //p; /^$/p' < test-data > expout
 ])
 sed '/^[[#&]]/d' < test-data > input.txt
 sed -n 's/^# //p; /^$/p' < test-data > expout
index 65fc6e8..705036f 100644 (file)
@@ -942,14 +942,20 @@ Restores the queue to the value it was before any \fBset_queue\fR
 actions were applied.
 .
 .IP \fBdec_ttl\fR
 actions were applied.
 .
 .IP \fBdec_ttl\fR
+.IQ \fBdec_ttl\fB[\fR(\fIid1,id2\fI)\fR]\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,
 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.
+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.  This action
+also optionally supports the ability to specify a list of valid
+controller ids.  Each of controllers in the list will receive the
+``packet_in'' message only if they have registered to receive the
+invalid ttl packets.  If controller ids are not specified, the
+``packet_in'' message will be sent only to the controllers having
+controller id zero which have registered for the invalid ttl packets.
 .
 .IP \fBnote:\fR[\fIhh\fR]...
 Does nothing at all.  Any number of bytes represented as hex digits
 .
 .IP \fBnote:\fR[\fIhh\fR]...
 Does nothing at all.  Any number of bytes represented as hex digits