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 4fc2049d15d19a9e12d5e6e6f80768b1bb864245..d1d3654e0224d70fc560c56bd69370df902bc2ff 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 6503f61ca56561c7ce61583635417d707a31d7ab..db603b6e5e06fe5ef59d1d43a68cddaedb044e28 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 2e021819d4464d71bfe9c7085d9960d21d50e0d3..2003668262e82ae312ee143accb1785e35d7afb7 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 32d38368908bed37a6f271e639ba7d317e4788f1..e5f5ea03adea611f1ee570ae5bfe60f1b088e95b 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 974cd8f874f5b7caed575b6e88b281ed8b24c271..619bb88cdf519de86902d1d9493d62956a80f861 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 3d68855477422b76cd42e1ca41b3717a417a87dc..d66c500e6a62f4d40e9626465f9ee35d37895a0f 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 ba8d309d06475659dd8fedd02eee62cd008ca84b..36c67f106ead4c21d6e3a3e78fe7534ab604df54 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 65fc6e8e6dedbb05d9dfe5e3a4e1066a30a850d8..705036f9c4bcd52ddab7c9b7f9137b6ff8e6cf3c 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