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. */
* 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.
*
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)
{
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:
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)
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:
}
}
+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)
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:
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) \
\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.
*
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,
}
}
+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)
case OFPUTIL_NXAST_RESUBMIT_TABLE:
case OFPUTIL_NXAST_OUTPUT_REG:
+ case OFPUTIL_NXAST_DEC_TTL_CNT_IDS:
NOT_REACHED();
case OFPUTIL_NXAST_LEARN:
break;
case OFPUTIL_NXAST_DEC_TTL:
- ofpact_put_DEC_TTL(ofpacts);
+ parse_dec_ttl(ofpacts, arg);
break;
case OFPUTIL_NXAST_FIN_TIMEOUT:
#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
}
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)) {
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;
break;
case OFPACT_DEC_TTL:
- if (compose_dec_ttl(ctx)) {
+ if (compose_dec_ttl(ctx, ofpact_get_DEC_TTL(a))) {
goto out;
}
break;
# 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
# 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
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,
-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