Implement new "fin_timeout" action and "learn" feature.
authorBen Pfaff <blp@nicira.com>
Wed, 15 Feb 2012 18:37:03 +0000 (10:37 -0800)
committerBen Pfaff <blp@nicira.com>
Wed, 15 Feb 2012 18:37:03 +0000 (10:37 -0800)
The "learn" action can create matching return flows.  If those have a long
timeout then it's a good idea to have a way to notice when in fact the
flows have terminated.  This new action and matching "learn" feature
provides that way.

Feature #8603.
Signed-off-by: Ben Pfaff <blp@nicira.com>
14 files changed:
NEWS
include/openflow/nicira-ext.h
lib/learn.c
lib/ofp-parse.c
lib/ofp-print.c
lib/ofp-util.c
lib/ofp-util.def
lib/ofp-util.h
ofproto/ofproto-dpif.c
ofproto/ofproto-provider.h
tests/learn.at
tests/ofproto-dpif.at
tests/ovs-ofctl.at
utilities/ovs-ofctl.8.in

diff --git a/NEWS b/NEWS
index a9cb1af9e064cd977dab3b6646824a2d53343bac..b1761309a8a20f119cccfbba8f2ab3aaa1f9d7f2 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -16,6 +16,8 @@ post-v1.5.0
         - Added an OpenFlow extension NXT_SET_ASYNC_CONFIG that allows
           controllers more precise control over which OpenFlow messages they
           receive asynchronously.
         - Added an OpenFlow extension NXT_SET_ASYNC_CONFIG that allows
           controllers more precise control over which OpenFlow messages they
           receive asynchronously.
+        - New "fin_timeout" action.
+        - Added "fin_timeout" support to "learn" action.
     - The default MAC learning timeout has been increased from 60 seconds
       to 300 seconds.  The MAC learning timeout is now configurable.
     - Logging to console and file will have UTC timestamp as a default for all
     - The default MAC learning timeout has been increased from 60 seconds
       to 300 seconds.  The MAC learning timeout is now configurable.
     - Logging to console and file will have UTC timestamp as a default for all
index ff957969ff3a4eb67b4dd0d7dfa8b239d159bd30..042d304a9e3257e91e8756ebd0d401ebc4aa9040 100644 (file)
@@ -335,6 +335,7 @@ enum nx_action_subtype {
     NXAST_LEARN,                /* struct nx_action_learn */
     NXAST_EXIT,                 /* struct nx_action_header */
     NXAST_DEC_TTL,              /* struct nx_action_header */
     NXAST_LEARN,                /* struct nx_action_learn */
     NXAST_EXIT,                 /* struct nx_action_header */
     NXAST_DEC_TTL,              /* struct nx_action_header */
+    NXAST_FIN_TIMEOUT,          /* struct nx_action_fin_timeout */
 };
 
 /* Header for Nicira-defined actions. */
 };
 
 /* Header for Nicira-defined actions. */
@@ -802,6 +803,13 @@ enum nx_mp_algorithm {
  * prepared to handle this by flooding (which can be implemented as a
  * low-priority flow).
  *
  * prepared to handle this by flooding (which can be implemented as a
  * low-priority flow).
  *
+ * If a learned flow matches a single TCP stream with a relatively long
+ * timeout, one may make the best of resource constraints by setting
+ * 'fin_idle_timeout' or 'fin_hard_timeout' (both measured in seconds), or
+ * both, to shorter timeouts.  When either of these is specified as a nonzero
+ * value, OVS adds a NXAST_FIN_TIMEOUT action, with the specified timeouts, to
+ * the learned flow.
+ *
  * Examples
  * --------
  *
  * Examples
  * --------
  *
@@ -905,7 +913,9 @@ struct nx_action_learn {
     ovs_be64 cookie;            /* Cookie for new flow. */
     ovs_be16 flags;             /* Either 0 or OFPFF_SEND_FLOW_REM. */
     uint8_t table_id;           /* Table to insert flow entry. */
     ovs_be64 cookie;            /* Cookie for new flow. */
     ovs_be16 flags;             /* Either 0 or OFPFF_SEND_FLOW_REM. */
     uint8_t table_id;           /* Table to insert flow entry. */
-    uint8_t pad[5];             /* Must be zero. */
+    uint8_t pad;                /* Must be zero. */
+    ovs_be16 fin_idle_timeout;  /* Idle timeout after FIN, if nonzero. */
+    ovs_be16 fin_hard_timeout;  /* Hard timeout after FIN, if nonzero. */
     /* Followed by a sequence of flow_mod_spec elements, as described above,
      * until the end of the action is reached. */
 };
     /* Followed by a sequence of flow_mod_spec elements, as described above,
      * until the end of the action is reached. */
 };
@@ -922,6 +932,40 @@ OFP_ASSERT(sizeof(struct nx_action_learn) == 32);
 #define NX_LEARN_DST_OUTPUT    (2 << 11) /* Add OFPAT_OUTPUT action. */
 #define NX_LEARN_DST_RESERVED  (3 << 11) /* Not yet defined. */
 #define NX_LEARN_DST_MASK      (3 << 11)
 #define NX_LEARN_DST_OUTPUT    (2 << 11) /* Add OFPAT_OUTPUT action. */
 #define NX_LEARN_DST_RESERVED  (3 << 11) /* Not yet defined. */
 #define NX_LEARN_DST_MASK      (3 << 11)
+
+/* Action structure for NXAST_FIN_TIMEOUT.
+ *
+ * This action changes the idle timeout or hard timeout, or both, of this
+ * OpenFlow rule when the rule matches a TCP packet with the FIN or RST flag.
+ * When such a packet is observed, the action reduces the rule's idle timeout
+ * to 'fin_idle_timeout' and its hard timeout to 'fin_hard_timeout'.  This
+ * action has no effect on an existing timeout that is already shorter than the
+ * one that the action specifies.  A 'fin_idle_timeout' or 'fin_hard_timeout'
+ * of zero has no effect on the respective timeout.
+ *
+ * 'fin_idle_timeout' and 'fin_hard_timeout' are measured in seconds.
+ * 'fin_hard_timeout' specifies time since the flow's creation, not since the
+ * receipt of the FIN or RST.
+ *
+ * This is useful for quickly discarding learned TCP flows that otherwise will
+ * take a long time to expire.
+ *
+ * This action is intended for use with an OpenFlow rule that matches only a
+ * single TCP flow.  If the rule matches multiple TCP flows (e.g. it wildcards
+ * all TCP traffic, or all TCP traffic to a particular port), then any FIN or
+ * RST in any of those flows will cause the entire OpenFlow rule to expire
+ * early, which is not normally desirable.
+ */
+struct nx_action_fin_timeout {
+    ovs_be16 type;              /* OFPAT_VENDOR. */
+    ovs_be16 len;               /* 16. */
+    ovs_be32 vendor;            /* NX_VENDOR_ID. */
+    ovs_be16 subtype;           /* NXAST_FIN_TIMEOUT. */
+    ovs_be16 fin_idle_timeout;  /* New idle timeout, if nonzero. */
+    ovs_be16 fin_hard_timeout;  /* New hard timeout, if nonzero. */
+    ovs_be16 pad;               /* Must be zero. */
+};
+OFP_ASSERT(sizeof(struct nx_action_fin_timeout) == 16);
 \f
 /* Action structure for NXAST_AUTOPATH.
  *
 \f
 /* Action structure for NXAST_AUTOPATH.
  *
index 8d947a312a3eabd63cb0098718070aa3b0604dcb..d749ce2ceba505b0b9a27dfa3b56b77260c394d7 100644 (file)
@@ -125,7 +125,7 @@ learn_check(const struct nx_action_learn *learn, const struct flow *flow)
     cls_rule_init_catchall(&rule, 0);
 
     if (learn->flags & ~htons(OFPFF_SEND_FLOW_REM)
     cls_rule_init_catchall(&rule, 0);
 
     if (learn->flags & ~htons(OFPFF_SEND_FLOW_REM)
-        || !is_all_zeros(learn->pad, sizeof learn->pad)
+        || learn->pad
         || learn->table_id == 0xff) {
         return OFPERR_OFPBAC_BAD_ARGUMENT;
     }
         || learn->table_id == 0xff) {
         return OFPERR_OFPBAC_BAD_ARGUMENT;
     }
@@ -209,6 +209,14 @@ learn_execute(const struct nx_action_learn *learn, const struct flow *flow,
 
     ofpbuf_init(&actions, 64);
 
 
     ofpbuf_init(&actions, 64);
 
+    if (learn->fin_idle_timeout || learn->fin_hard_timeout) {
+        struct nx_action_fin_timeout *naft;
+
+        naft = ofputil_put_NXAST_FIN_TIMEOUT(&actions);
+        naft->fin_idle_timeout = learn->fin_idle_timeout;
+        naft->fin_hard_timeout = learn->fin_hard_timeout;
+    }
+
     for (p = learn + 1, end = (char *) learn + ntohs(learn->len); p != end; ) {
         uint16_t header = ntohs(get_be16(&p));
         int n_bits = header & NX_LEARN_N_BITS_MASK;
     for (p = learn + 1, end = (char *) learn + ntohs(learn->len); p != end; ) {
         uint16_t header = ntohs(get_be16(&p));
         int n_bits = header & NX_LEARN_N_BITS_MASK;
@@ -431,6 +439,10 @@ learn_parse(struct ofpbuf *b, char *arg, const struct flow *flow)
             learn->idle_timeout = htons(atoi(value));
         } else if (!strcmp(name, "hard_timeout")) {
             learn->hard_timeout = htons(atoi(value));
             learn->idle_timeout = htons(atoi(value));
         } else if (!strcmp(name, "hard_timeout")) {
             learn->hard_timeout = htons(atoi(value));
+        } else if (!strcmp(name, "fin_idle_timeout")) {
+            learn->fin_idle_timeout = htons(atoi(value));
+        } else if (!strcmp(name, "fin_hard_timeout")) {
+            learn->fin_hard_timeout = htons(atoi(value));
         } else if (!strcmp(name, "cookie")) {
             learn->cookie = htonll(strtoull(value, NULL, 0));
         } else {
         } else if (!strcmp(name, "cookie")) {
             learn->cookie = htonll(strtoull(value, NULL, 0));
         } else {
@@ -522,6 +534,14 @@ learn_format(const struct nx_action_learn *learn, struct ds *s)
     if (learn->hard_timeout != htons(OFP_FLOW_PERMANENT)) {
         ds_put_format(s, ",hard_timeout=%"PRIu16, ntohs(learn->hard_timeout));
     }
     if (learn->hard_timeout != htons(OFP_FLOW_PERMANENT)) {
         ds_put_format(s, ",hard_timeout=%"PRIu16, ntohs(learn->hard_timeout));
     }
+    if (learn->fin_idle_timeout) {
+        ds_put_format(s, ",fin_idle_timeout=%"PRIu16,
+                      ntohs(learn->fin_idle_timeout));
+    }
+    if (learn->fin_hard_timeout) {
+        ds_put_format(s, ",fin_hard_timeout=%"PRIu16,
+                      ntohs(learn->fin_hard_timeout));
+    }
     if (learn->priority != htons(OFP_DEFAULT_PRIORITY)) {
         ds_put_format(s, ",priority=%"PRIu16, ntohs(learn->priority));
     }
     if (learn->priority != htons(OFP_DEFAULT_PRIORITY)) {
         ds_put_format(s, ",priority=%"PRIu16, ntohs(learn->priority));
     }
@@ -535,7 +555,7 @@ learn_format(const struct nx_action_learn *learn, struct ds *s)
     if (learn->cookie != htonll(0)) {
         ds_put_format(s, ",cookie=0x%"PRIx64, ntohll(learn->cookie));
     }
     if (learn->cookie != htonll(0)) {
         ds_put_format(s, ",cookie=0x%"PRIx64, ntohll(learn->cookie));
     }
-    if (!is_all_zeros(learn->pad, sizeof learn->pad)) {
+    if (learn->pad != 0) {
         ds_put_cstr(s, ",***nonzero pad***");
     }
 
         ds_put_cstr(s, ",***nonzero pad***");
     }
 
index f96f8170a0c377813b09809a493503f893b2d9b2..24f6876bc8ab5203f8f54a1c83ee6cf82c44fc29 100644 (file)
@@ -249,6 +249,24 @@ parse_note(struct ofpbuf *b, const char *arg)
     nan->len = htons(b->size - start_ofs);
 }
 
     nan->len = htons(b->size - start_ofs);
 }
 
+static void
+parse_fin_timeout(struct ofpbuf *b, char *arg)
+{
+    struct nx_action_fin_timeout *naft;
+    char *key, *value;
+
+    naft = ofputil_put_NXAST_FIN_TIMEOUT(b);
+    while (ofputil_parse_key_value(&arg, &key, &value)) {
+        if (!strcmp(key, "idle_timeout")) {
+            naft->fin_idle_timeout = htons(str_to_u16(value, key));
+        } else if (!strcmp(key, "hard_timeout")) {
+            naft->fin_hard_timeout = htons(str_to_u16(value, key));
+        } else {
+            ovs_fatal(0, "invalid key '%s' in 'fin_timeout' argument", key);
+        }
+    }
+}
+
 static void
 parse_named_action(enum ofputil_action_code code, const struct flow *flow,
                    struct ofpbuf *b, char *arg)
 static void
 parse_named_action(enum ofputil_action_code code, const struct flow *flow,
                    struct ofpbuf *b, char *arg)
@@ -367,6 +385,10 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow,
     case OFPUTIL_NXAST_DEC_TTL:
         ofputil_put_NXAST_DEC_TTL(b);
         break;
     case OFPUTIL_NXAST_DEC_TTL:
         ofputil_put_NXAST_DEC_TTL(b);
         break;
+
+    case OFPUTIL_NXAST_FIN_TIMEOUT:
+        parse_fin_timeout(b, arg);
+        break;
     }
 }
 
     }
 }
 
index 8df439de1af4124e4664e6dd48b74bb386c4be61..f1eeebb44aa98b735128b3ffc4c13270839b9999 100644 (file)
@@ -194,6 +194,7 @@ ofp_print_action(struct ds *s, const union ofp_action *a,
     const struct nx_action_multipath *nam;
     const struct nx_action_autopath *naa;
     const struct nx_action_output_reg *naor;
     const struct nx_action_multipath *nam;
     const struct nx_action_autopath *naa;
     const struct nx_action_output_reg *naor;
+    const struct nx_action_fin_timeout *naft;
     struct mf_subfield subfield;
     uint16_t port;
 
     struct mf_subfield subfield;
     uint16_t port;
 
@@ -356,6 +357,21 @@ ofp_print_action(struct ds *s, const union ofp_action *a,
         ds_put_cstr(s, "exit");
         break;
 
         ds_put_cstr(s, "exit");
         break;
 
+    case OFPUTIL_NXAST_FIN_TIMEOUT:
+        naft = (const struct nx_action_fin_timeout *) a;
+        ds_put_cstr(s, "fin_timeout(");
+        if (naft->fin_idle_timeout) {
+            ds_put_format(s, "idle_timeout=%"PRIu16",",
+                          ntohs(naft->fin_idle_timeout));
+        }
+        if (naft->fin_hard_timeout) {
+            ds_put_format(s, "hard_timeout=%"PRIu16",",
+                          ntohs(naft->fin_hard_timeout));
+        }
+        ds_chomp(s, ',');
+        ds_put_char(s, ')');
+        break;
+
     default:
         break;
     }
     default:
         break;
     }
index e9366937b55d193af4c6bd654da4e2eb7baa976a..ffdccda43ef1f0d5f7de19dbbe0fc6641137ae34 100644 (file)
@@ -2508,6 +2508,7 @@ validate_actions(const union ofp_action *actions, size_t n_actions,
         case OFPUTIL_NXAST_SET_TUNNEL64:
         case OFPUTIL_NXAST_EXIT:
         case OFPUTIL_NXAST_DEC_TTL:
         case OFPUTIL_NXAST_SET_TUNNEL64:
         case OFPUTIL_NXAST_EXIT:
         case OFPUTIL_NXAST_DEC_TTL:
+        case OFPUTIL_NXAST_FIN_TIMEOUT:
             break;
         }
 
             break;
         }
 
index d05ec9dc2bbec6c3a9249975a46bc3a763224880..27e0f26482b69f465618a47a22bd26afc708cba3 100644 (file)
@@ -37,4 +37,5 @@ 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_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")
 #undef NXAST_ACTION
 #undef NXAST_ACTION
index 816532e076213069eb6cea3370954ccc3a2496ed..e64be514f58965b93fd5c1760c49914f1aa30f8a 100644 (file)
@@ -352,6 +352,9 @@ bool ofputil_frag_handling_from_string(const char *, enum ofp_config_flags *);
  * OFPUTIL_NXAST_BUNDLE_LOAD
  * OFPUTIL_NXAST_RESUBMIT_TABLE
  * OFPUTIL_NXAST_OUTPUT_REG
  * OFPUTIL_NXAST_BUNDLE_LOAD
  * OFPUTIL_NXAST_RESUBMIT_TABLE
  * OFPUTIL_NXAST_OUTPUT_REG
+ * OFPUTIL_NXAST_LEARN
+ * OFPUTIL_NXAST_DEC_TTL
+ * OFPUTIL_NXAST_FIN_TIMEOUT
  *
  * (The above list helps developers who want to "grep" for these definitions.)
  */
  *
  * (The above list helps developers who want to "grep" for these definitions.)
  */
index 8903a7f4e6b6b114373e591f7441b2bbd66510fe..1fe5da68db271f030a2dd3678f2f171914a8417b 100644 (file)
@@ -216,6 +216,11 @@ struct action_xlate_ctx {
     /* The rule that we are currently translating, or NULL. */
     struct rule_dpif *rule;
 
     /* The rule that we are currently translating, or NULL. */
     struct rule_dpif *rule;
 
+    /* Union of the set of TCP flags seen so far in this flow.  (Used only by
+     * NXAST_FIN_TIMEOUT.  Set to zero to avoid updating updating rules'
+     * timeouts.) */
+    uint8_t tcp_flags;
+
     /* If nonnull, called just before executing a resubmit action.
      *
      * This is normally null so the client has to set it manually after
     /* If nonnull, called just before executing a resubmit action.
      *
      * This is normally null so the client has to set it manually after
@@ -231,6 +236,7 @@ struct action_xlate_ctx {
                                  * be reassessed for every packet. */
     bool has_learn;             /* Actions include NXAST_LEARN? */
     bool has_normal;            /* Actions output to OFPP_NORMAL? */
                                  * be reassessed for every packet. */
     bool has_learn;             /* Actions include NXAST_LEARN? */
     bool has_normal;            /* Actions output to OFPP_NORMAL? */
+    bool has_fin_timeout;       /* Actions include NXAST_FIN_TIMEOUT? */
     uint16_t nf_output_iface;   /* Output interface index for NetFlow. */
     mirror_mask_t mirrors;      /* Bitmap of associated mirrors. */
 
     uint16_t nf_output_iface;   /* Output interface index for NetFlow. */
     mirror_mask_t mirrors;      /* Bitmap of associated mirrors. */
 
@@ -250,7 +256,7 @@ struct action_xlate_ctx {
 static void action_xlate_ctx_init(struct action_xlate_ctx *,
                                   struct ofproto_dpif *, const struct flow *,
                                   ovs_be16 initial_tci, struct rule_dpif *,
 static void action_xlate_ctx_init(struct action_xlate_ctx *,
                                   struct ofproto_dpif *, const struct flow *,
                                   ovs_be16 initial_tci, struct rule_dpif *,
-                                  const struct ofpbuf *);
+                                  uint8_t tcp_flags, const struct ofpbuf *);
 static struct ofpbuf *xlate_actions(struct action_xlate_ctx *,
                                     const union ofp_action *in, size_t n_in);
 
 static struct ofpbuf *xlate_actions(struct action_xlate_ctx *,
                                     const union ofp_action *in, size_t n_in);
 
@@ -304,6 +310,7 @@ struct facet {
     /* Accounting. */
     uint64_t accounted_bytes;    /* Bytes processed by facet_account(). */
     struct netflow_flow nf_flow; /* Per-flow NetFlow tracking data. */
     /* Accounting. */
     uint64_t accounted_bytes;    /* Bytes processed by facet_account(). */
     struct netflow_flow nf_flow; /* Per-flow NetFlow tracking data. */
+    uint8_t tcp_flags;           /* TCP flags seen for this 'rule'. */
 
     /* Properties of datapath actions.
      *
 
     /* Properties of datapath actions.
      *
@@ -314,6 +321,7 @@ struct facet {
     bool may_install;            /* Reassess actions for every packet? */
     bool has_learn;              /* Actions include NXAST_LEARN? */
     bool has_normal;             /* Actions output to OFPP_NORMAL? */
     bool may_install;            /* Reassess actions for every packet? */
     bool has_learn;              /* Actions include NXAST_LEARN? */
     bool has_normal;             /* Actions output to OFPP_NORMAL? */
+    bool has_fin_timeout;        /* Actions include NXAST_FIN_TIMEOUT? */
     tag_type tags;               /* Tags that would require revalidation. */
     mirror_mask_t mirrors;       /* Bitmap of dependent mirrors. */
 };
     tag_type tags;               /* Tags that would require revalidation. */
     mirror_mask_t mirrors;       /* Bitmap of dependent mirrors. */
 };
@@ -2954,6 +2962,8 @@ update_stats(struct ofproto_dpif *p)
             subfacet->dp_packet_count = stats->n_packets;
             subfacet->dp_byte_count = stats->n_bytes;
 
             subfacet->dp_packet_count = stats->n_packets;
             subfacet->dp_byte_count = stats->n_bytes;
 
+            facet->tcp_flags |= stats->tcp_flags;
+
             subfacet_update_time(subfacet, stats->used);
             facet_account(facet);
             facet_push_stats(facet);
             subfacet_update_time(subfacet, stats->used);
             facet_account(facet);
             facet_push_stats(facet);
@@ -3225,12 +3235,14 @@ facet_account(struct facet *facet)
     /* Feed information from the active flows back into the learning table to
      * ensure that table is always in sync with what is actually flowing
      * through the datapath. */
     /* Feed information from the active flows back into the learning table to
      * ensure that table is always in sync with what is actually flowing
      * through the datapath. */
-    if (facet->has_learn || facet->has_normal) {
+    if (facet->has_learn || facet->has_normal
+        || (facet->has_fin_timeout
+            && facet->tcp_flags & (TCP_FIN | TCP_RST))) {
         struct action_xlate_ctx ctx;
 
         action_xlate_ctx_init(&ctx, ofproto, &facet->flow,
                               facet->flow.vlan_tci,
         struct action_xlate_ctx ctx;
 
         action_xlate_ctx_init(&ctx, ofproto, &facet->flow,
                               facet->flow.vlan_tci,
-                              facet->rule, NULL);
+                              facet->rule, facet->tcp_flags, NULL);
         ctx.may_learn = true;
         ofpbuf_delete(xlate_actions(&ctx, facet->rule->up.actions,
                                     facet->rule->up.n_actions));
         ctx.may_learn = true;
         ofpbuf_delete(xlate_actions(&ctx, facet->rule->up.actions,
                                     facet->rule->up.n_actions));
@@ -3324,6 +3336,7 @@ facet_flush_stats(struct facet *facet)
     facet_reset_counters(facet);
 
     netflow_flow_clear(&facet->nf_flow);
     facet_reset_counters(facet);
 
     netflow_flow_clear(&facet->nf_flow);
+    facet->tcp_flags = 0;
 }
 
 /* Searches 'ofproto''s table of facets for one exactly equal to 'flow'.
 }
 
 /* Searches 'ofproto''s table of facets for one exactly equal to 'flow'.
@@ -3420,7 +3433,7 @@ facet_check_consistency(struct facet *facet)
         bool should_install;
 
         action_xlate_ctx_init(&ctx, ofproto, &facet->flow,
         bool should_install;
 
         action_xlate_ctx_init(&ctx, ofproto, &facet->flow,
-                              subfacet->initial_tci, rule, NULL);
+                              subfacet->initial_tci, rule, 0, NULL);
         odp_actions = xlate_actions(&ctx, rule->up.actions,
                                     rule->up.n_actions);
 
         odp_actions = xlate_actions(&ctx, rule->up.actions,
                                     rule->up.n_actions);
 
@@ -3539,7 +3552,7 @@ facet_revalidate(struct facet *facet)
         bool should_install;
 
         action_xlate_ctx_init(&ctx, ofproto, &facet->flow,
         bool should_install;
 
         action_xlate_ctx_init(&ctx, ofproto, &facet->flow,
-                              subfacet->initial_tci, new_rule, NULL);
+                              subfacet->initial_tci, new_rule, 0, NULL);
         odp_actions = xlate_actions(&ctx, new_rule->up.actions,
                                     new_rule->up.n_actions);
         actions_changed = (subfacet->actions_len != odp_actions->size
         odp_actions = xlate_actions(&ctx, new_rule->up.actions,
                                     new_rule->up.n_actions);
         actions_changed = (subfacet->actions_len != odp_actions->size
@@ -3581,6 +3594,7 @@ facet_revalidate(struct facet *facet)
     facet->may_install = ctx.may_set_up_flow;
     facet->has_learn = ctx.has_learn;
     facet->has_normal = ctx.has_normal;
     facet->may_install = ctx.may_set_up_flow;
     facet->has_learn = ctx.has_learn;
     facet->has_normal = ctx.has_normal;
+    facet->has_fin_timeout = ctx.has_fin_timeout;
     facet->mirrors = ctx.mirrors;
     if (new_actions) {
         i = 0;
     facet->mirrors = ctx.mirrors;
     if (new_actions) {
         i = 0;
@@ -3690,7 +3704,7 @@ flow_push_stats(struct rule_dpif *rule,
     ofproto_rule_update_used(&rule->up, used);
 
     action_xlate_ctx_init(&push.ctx, ofproto, flow, flow->vlan_tci, rule,
     ofproto_rule_update_used(&rule->up, used);
 
     action_xlate_ctx_init(&push.ctx, ofproto, flow, flow->vlan_tci, rule,
-                          NULL);
+                          0, NULL);
     push.ctx.resubmit_hook = push_resubmit;
     ofpbuf_delete(xlate_actions(&push.ctx,
                                 rule->up.actions, rule->up.n_actions));
     push.ctx.resubmit_hook = push_resubmit;
     ofpbuf_delete(xlate_actions(&push.ctx,
                                 rule->up.actions, rule->up.n_actions));
@@ -3835,12 +3849,13 @@ subfacet_make_actions(struct subfacet *subfacet, const struct ofpbuf *packet)
     struct action_xlate_ctx ctx;
 
     action_xlate_ctx_init(&ctx, ofproto, &facet->flow, subfacet->initial_tci,
     struct action_xlate_ctx ctx;
 
     action_xlate_ctx_init(&ctx, ofproto, &facet->flow, subfacet->initial_tci,
-                          rule, packet);
+                          rule, 0, packet);
     odp_actions = xlate_actions(&ctx, rule->up.actions, rule->up.n_actions);
     facet->tags = ctx.tags;
     facet->may_install = ctx.may_set_up_flow;
     facet->has_learn = ctx.has_learn;
     facet->has_normal = ctx.has_normal;
     odp_actions = xlate_actions(&ctx, rule->up.actions, rule->up.n_actions);
     facet->tags = ctx.tags;
     facet->may_install = ctx.may_set_up_flow;
     facet->has_learn = ctx.has_learn;
     facet->has_normal = ctx.has_normal;
+    facet->has_fin_timeout = ctx.has_fin_timeout;
     facet->nf_flow.output_iface = ctx.nf_output_iface;
     facet->mirrors = ctx.mirrors;
 
     facet->nf_flow.output_iface = ctx.nf_output_iface;
     facet->mirrors = ctx.mirrors;
 
@@ -3960,6 +3975,7 @@ subfacet_update_stats(struct subfacet *subfacet,
         subfacet_update_time(subfacet, stats->used);
         facet->packet_count += stats->n_packets;
         facet->byte_count += stats->n_bytes;
         subfacet_update_time(subfacet, stats->used);
         facet->packet_count += stats->n_packets;
         facet->byte_count += stats->n_bytes;
+        facet->tcp_flags |= stats->tcp_flags;
         facet_push_stats(facet);
         netflow_flow_update_flags(&facet->nf_flow, stats->tcp_flags);
     }
         facet_push_stats(facet);
         netflow_flow_update_flags(&facet->nf_flow, stats->tcp_flags);
     }
@@ -4115,7 +4131,7 @@ rule_execute(struct rule *rule_, const struct flow *flow,
     size_t size;
 
     action_xlate_ctx_init(&ctx, ofproto, flow, flow->vlan_tci,
     size_t size;
 
     action_xlate_ctx_init(&ctx, ofproto, flow, flow->vlan_tci,
-                          rule, packet);
+                          rule, packet_get_tcp_flags(packet, flow), packet);
     odp_actions = xlate_actions(&ctx, rule->up.actions, rule->up.n_actions);
     size = packet->size;
     if (execute_odp_actions(ofproto, flow, odp_actions->data,
     odp_actions = xlate_actions(&ctx, rule->up.actions, rule->up.n_actions);
     size = packet->size;
     if (execute_odp_actions(ofproto, flow, odp_actions->data,
@@ -4709,6 +4725,28 @@ xlate_learn_action(struct action_xlate_ctx *ctx,
     free(fm.actions);
 }
 
     free(fm.actions);
 }
 
+/* Reduces '*timeout' to no more than 'max'.  A value of zero in either case
+ * means "infinite". */
+static void
+reduce_timeout(uint16_t max, uint16_t *timeout)
+{
+    if (max && (!*timeout || *timeout > max)) {
+        *timeout = max;
+    }
+}
+
+static void
+xlate_fin_timeout(struct action_xlate_ctx *ctx,
+                  const struct nx_action_fin_timeout *naft)
+{
+    if (ctx->tcp_flags & (TCP_FIN | TCP_RST) && ctx->rule) {
+        struct rule_dpif *rule = ctx->rule;
+
+        reduce_timeout(ntohs(naft->fin_idle_timeout), &rule->up.idle_timeout);
+        reduce_timeout(ntohs(naft->fin_hard_timeout), &rule->up.hard_timeout);
+    }
+}
+
 static bool
 may_receive(const struct ofport_dpif *port, struct action_xlate_ctx *ctx)
 {
 static bool
 may_receive(const struct ofport_dpif *port, struct action_xlate_ctx *ctx)
 {
@@ -4914,6 +4952,11 @@ do_xlate_actions(const union ofp_action *in, size_t n_in,
         case OFPUTIL_NXAST_EXIT:
             ctx->exit = true;
             break;
         case OFPUTIL_NXAST_EXIT:
             ctx->exit = true;
             break;
+
+        case OFPUTIL_NXAST_FIN_TIMEOUT:
+            ctx->has_fin_timeout = true;
+            xlate_fin_timeout(ctx, (const struct nx_action_fin_timeout *) ia);
+            break;
         }
     }
 
         }
     }
 
@@ -4933,7 +4976,7 @@ static void
 action_xlate_ctx_init(struct action_xlate_ctx *ctx,
                       struct ofproto_dpif *ofproto, const struct flow *flow,
                       ovs_be16 initial_tci, struct rule_dpif *rule,
 action_xlate_ctx_init(struct action_xlate_ctx *ctx,
                       struct ofproto_dpif *ofproto, const struct flow *flow,
                       ovs_be16 initial_tci, struct rule_dpif *rule,
-                      const struct ofpbuf *packet)
+                      uint8_t tcp_flags, const struct ofpbuf *packet)
 {
     ctx->ofproto = ofproto;
     ctx->flow = *flow;
 {
     ctx->ofproto = ofproto;
     ctx->flow = *flow;
@@ -4943,6 +4986,7 @@ action_xlate_ctx_init(struct action_xlate_ctx *ctx,
     ctx->rule = rule;
     ctx->packet = packet;
     ctx->may_learn = packet != NULL;
     ctx->rule = rule;
     ctx->packet = packet;
     ctx->may_learn = packet != NULL;
+    ctx->tcp_flags = tcp_flags;
     ctx->resubmit_hook = NULL;
 }
 
     ctx->resubmit_hook = NULL;
 }
 
@@ -4960,6 +5004,7 @@ xlate_actions(struct action_xlate_ctx *ctx,
     ctx->may_set_up_flow = true;
     ctx->has_learn = false;
     ctx->has_normal = false;
     ctx->may_set_up_flow = true;
     ctx->has_learn = false;
     ctx->has_normal = false;
+    ctx->has_fin_timeout = false;
     ctx->nf_output_iface = NF_OUT_DROP;
     ctx->mirrors = 0;
     ctx->recurse = 0;
     ctx->nf_output_iface = NF_OUT_DROP;
     ctx->mirrors = 0;
     ctx->recurse = 0;
@@ -5730,7 +5775,7 @@ packet_out(struct ofproto *ofproto_, struct ofpbuf *packet,
         odp_flow_key_from_flow(&key, flow);
 
         action_xlate_ctx_init(&push.ctx, ofproto, flow, flow->vlan_tci, NULL,
         odp_flow_key_from_flow(&key, flow);
 
         action_xlate_ctx_init(&push.ctx, ofproto, flow, flow->vlan_tci, NULL,
-                              packet);
+                              packet_get_tcp_flags(packet, flow), packet);
 
         /* Ensure that resubmits in 'ofp_actions' get accounted to their
          * matching rules. */
 
         /* Ensure that resubmits in 'ofp_actions' get accounted to their
          * matching rules. */
@@ -6032,11 +6077,13 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[],
     if (rule) {
         struct ofproto_trace trace;
         struct ofpbuf *odp_actions;
     if (rule) {
         struct ofproto_trace trace;
         struct ofpbuf *odp_actions;
+        uint8_t tcp_flags;
 
 
+        tcp_flags = packet ? packet_get_tcp_flags(packet, &flow) : 0;
         trace.result = &result;
         trace.flow = flow;
         action_xlate_ctx_init(&trace.ctx, ofproto, &flow, initial_tci,
         trace.result = &result;
         trace.flow = flow;
         action_xlate_ctx_init(&trace.ctx, ofproto, &flow, initial_tci,
-                              rule, packet);
+                              rule, tcp_flags, packet);
         trace.ctx.resubmit_hook = trace_resubmit;
         odp_actions = xlate_actions(&trace.ctx,
                                     rule->up.actions, rule->up.n_actions);
         trace.ctx.resubmit_hook = trace_resubmit;
         odp_actions = xlate_actions(&trace.ctx,
                                     rule->up.actions, rule->up.n_actions);
index 3927a2e9bb018f67d79dab954e45bc23f4d0ab06..9ba62de1a94144cc9402301ff86505f654f36abd 100644 (file)
@@ -841,9 +841,10 @@ struct ofproto_class {
      * 'flow' reflects the flow information for 'packet'.  All of the
      * information in 'flow' is extracted from 'packet', except for
      * flow->tun_id and flow->in_port, which are assigned the correct values
      * 'flow' reflects the flow information for 'packet'.  All of the
      * information in 'flow' is extracted from 'packet', except for
      * flow->tun_id and flow->in_port, which are assigned the correct values
-     * for the incoming packet.  The register values are zeroed.
+     * for the incoming packet.  The register values are zeroed.  'packet''s
+     * header pointers (e.g. packet->l3) are appropriately initialized.
      *
      *
-     * The statistics for 'packet' should be included in 'rule'.
+     * The implementation should add the statistics for 'packet' into 'rule'.
      *
      * Returns 0 if successful, otherwise an OpenFlow error code. */
     enum ofperr (*rule_execute)(struct rule *rule, const struct flow *flow,
      *
      * Returns 0 if successful, otherwise an OpenFlow error code. */
     enum ofperr (*rule_execute)(struct rule *rule, const struct flow *flow,
index 943a7c88b43514ac779e3470a61c4d4ee31274d5..a97017181a7b2ac123cc27d33bc305d155d73b77 100644 (file)
@@ -4,12 +4,12 @@ AT_SETUP([learning action - parsing and formatting])
 AT_DATA([flows.txt], [[
 actions=learn()
 actions=learn(NXM_OF_VLAN_TCI[0..11], NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[], output:NXM_OF_IN_PORT[], load:10->NXM_NX_REG0[5..10])
 AT_DATA([flows.txt], [[
 actions=learn()
 actions=learn(NXM_OF_VLAN_TCI[0..11], NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[], output:NXM_OF_IN_PORT[], load:10->NXM_NX_REG0[5..10])
-actions=learn(table=1,idle_timeout=1, hard_timeout=2, priority=10, cookie=0xfedcba9876543210, in_port=99,NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:NXM_OF_IN_PORT[]->NXM_NX_REG1[16..31])
+actions=learn(table=1,idle_timeout=10, hard_timeout=20, fin_idle_timeout=5, fin_hard_timeout=10, priority=10, cookie=0xfedcba9876543210, in_port=99,NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:NXM_OF_IN_PORT[]->NXM_NX_REG1[16..31])
 ]])
 AT_CHECK([ovs-ofctl parse-flows flows.txt], [0],
 [[OFPT_FLOW_MOD (xid=0x1): ADD actions=learn(table=1)
 OFPT_FLOW_MOD (xid=0x2): ADD actions=learn(table=1,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[],load:0x000a->NXM_NX_REG0[5..10])
 ]])
 AT_CHECK([ovs-ofctl parse-flows flows.txt], [0],
 [[OFPT_FLOW_MOD (xid=0x1): ADD actions=learn(table=1)
 OFPT_FLOW_MOD (xid=0x2): ADD actions=learn(table=1,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[],load:0x000a->NXM_NX_REG0[5..10])
-OFPT_FLOW_MOD (xid=0x3): ADD actions=learn(table=1,idle_timeout=1,hard_timeout=2,priority=10,cookie=0xfedcba9876543210,in_port=99,NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:NXM_OF_IN_PORT[]->NXM_NX_REG1[16..31])
+OFPT_FLOW_MOD (xid=0x3): ADD actions=learn(table=1,idle_timeout=10,hard_timeout=20,fin_idle_timeout=5,fin_hard_timeout=10,priority=10,cookie=0xfedcba9876543210,in_port=99,NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:NXM_OF_IN_PORT[]->NXM_NX_REG1[16..31])
 ]])
 AT_CLEANUP
 
 ]])
 AT_CLEANUP
 
@@ -106,3 +106,17 @@ NXST_FLOW reply:
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
+
+AT_SETUP([learning action - fin_timeout feature])
+# This is a totally artificial use of the "learn" action.  The only purpose
+# is to check that specifying fin_idle_timeout or fin_hard_timeout causes
+# a corresponding fin_timeout action to end up in the learned flows.
+OVS_VSWITCHD_START
+AT_CHECK([[ovs-ofctl add-flow br0 'actions=learn(fin_hard_timeout=10, fin_idle_timeout=5, NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[], output:NXM_OF_IN_PORT[])']])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(1),eth(src=50:54:00:00:00:05,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=192.168.0.1,tip=192.168.0.2,op=1,sha=50:54:00:00:00:05,tha=00:00:00:00:00:00)' -generate], [0], [ignore])
+AT_CHECK([ovs-ofctl dump-flows br0 table=1 | ofctl_strip], [0],
+[NXST_FLOW reply:
+ table=1, dl_dst=50:54:00:00:00:05 actions=fin_timeout(idle_timeout=5,hard_timeout=10),output:1
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
index a21d179c4999def7a490024514f55407bd5821cc..a3f32c06a9ed7c0a799853313df21dee151b4146 100644 (file)
@@ -1136,3 +1136,40 @@ AT_CHECK([test $hard4 -lt $duration4])
 
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
 OVS_VSWITCHD_STOP
 AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - fin_timeout])
+OVS_VSWITCHD_START
+AT_DATA([flows.txt], [dnl
+in_port=1 actions=output:2
+in_port=2 actions=mod_vlan_vid:17,output:1
+])
+AT_CHECK([ovs-ofctl add-flow br0 'idle_timeout=60,actions=fin_timeout(idle_timeout=5)'])
+AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip], [0],
+[NXST_FLOW reply:
+ idle_timeout=60, actions=fin_timeout(idle_timeout=5)
+])
+# Check that a TCP SYN packet does not change the timeout.  (Because
+# flow stats updates are mainly what implements the fin_timeout
+# feature, we warp forward a couple of times to ensure that flow stats
+# run before re-checking the flow table.)
+AT_CHECK([ovs-appctl netdev-dummy/receive br0 0021853763af0026b98cb0f908004500003c2e2440004006465dac11370dac11370b828b0016751e267b00000000a00216d017360000020405b40402080a2d25085f0000000001030307], [0], [success
+])
+AT_CHECK([ovs-appctl time/warp 1000 && ovs-appctl time/warp 1000], [0], [warped
+warped
+])
+AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip], [0],
+[NXST_FLOW reply:
+ n_packets=1, n_bytes=74, idle_timeout=60, actions=fin_timeout(idle_timeout=5)
+])
+# Check that a TCP FIN packet does change the timeout.
+AT_CHECK([ovs-appctl netdev-dummy/receive br0 0021853763af0026b98cb0f90800451000342e3e40004006463bac11370dac11370b828b0016751e319dfc96399b801100717ae800000101080a2d250a9408579588], [0], [success
+])
+AT_CHECK([ovs-appctl time/warp 1000 && ovs-appctl time/warp 1000], [0], [warped
+warped
+])
+AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip], [0],
+[NXST_FLOW reply:
+ n_packets=2, n_bytes=140, idle_timeout=5, actions=fin_timeout(idle_timeout=5)
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
index 066a627261d00ecfd36c0645d71e2453f3f829b3..8eba261f7a0d6710c5d45213fa75067894a412c9 100644 (file)
@@ -27,6 +27,7 @@ actions=output:1,bundle_load(eth_src,0,hrw,ofport,NXM_NX_REG0[16..31],slaves:1),
 actions=resubmit:1,resubmit(2),resubmit(,3),resubmit(2,3)
 send_flow_rem,actions=output:1,output:NXM_NX_REG0[],output:2,output:NXM_NX_REG1[16..31],output:3
 check_overlap,actions=output:1,exit,output:2
 actions=resubmit:1,resubmit(2),resubmit(,3),resubmit(2,3)
 send_flow_rem,actions=output:1,output:NXM_NX_REG0[],output:2,output:NXM_NX_REG1[16..31],output:3
 check_overlap,actions=output:1,exit,output:2
+actions=fin_timeout(idle_timeout=5,hard_timeout=15)
 ]])
 
 AT_CHECK([ovs-ofctl parse-flows flows.txt
 ]])
 
 AT_CHECK([ovs-ofctl parse-flows flows.txt
@@ -58,6 +59,7 @@ NXT_FLOW_MOD: ADD table:255 actions=output:1,bundle_load(eth_src,0,hrw,ofport,NX
 NXT_FLOW_MOD: ADD table:255 actions=resubmit:1,resubmit:2,resubmit(,3),resubmit(2,3)
 NXT_FLOW_MOD: ADD table:255 send_flow_rem actions=output:1,output:NXM_NX_REG0[],output:2,output:NXM_NX_REG1[16..31],output:3
 NXT_FLOW_MOD: ADD table:255 check_overlap actions=output:1,exit,output:2
 NXT_FLOW_MOD: ADD table:255 actions=resubmit:1,resubmit:2,resubmit(,3),resubmit(2,3)
 NXT_FLOW_MOD: ADD table:255 send_flow_rem actions=output:1,output:NXM_NX_REG0[],output:2,output:NXM_NX_REG1[16..31],output:3
 NXT_FLOW_MOD: ADD table:255 check_overlap actions=output:1,exit,output:2
+NXT_FLOW_MOD: ADD table:255 actions=fin_timeout(idle_timeout=5,hard_timeout=15)
 ]])
 AT_CLEANUP
 
 ]])
 AT_CLEANUP
 
index 455298e3eca14bd8b552a13c64dcca3607ba0631..8de6af947383bfc24e36bd1c1da312f91e7583ba 100644 (file)
@@ -956,6 +956,11 @@ specified.
 These key-value pairs have the same meaning as in the usual
 \fBovs\-ofctl\fR flow syntax.
 .
 These key-value pairs have the same meaning as in the usual
 \fBovs\-ofctl\fR flow syntax.
 .
+.IP \fBfin_idle_timeout=\fIseconds\fR
+.IQ \fBfin_hard_timeout=\fIseconds\fR
+Adds a \fBfin_timeout\fR action with the specified arguments to the
+new flow.  This feature was added in Open vSwitch 1.5.90.
+.
 .IP \fBtable=\fInumber\fR
 The table in which the new flow should be inserted.  Specify a decimal
 number between 0 and 254.  The default, if \fBtable\fR is unspecified,
 .IP \fBtable=\fInumber\fR
 The table in which the new flow should be inserted.  Specify a decimal
 number between 0 and 254.  The default, if \fBtable\fR is unspecified,
@@ -1006,6 +1011,27 @@ with no match criteria.  (This is why the default \fBtable\fR is 1, to
 keep the learned flows separate from the primary flow table 0.)
 .RE
 .
 keep the learned flows separate from the primary flow table 0.)
 .RE
 .
+.IP "\fBfin_timeout(\fIargument\fR[\fB,\fIargument\fR]\fB)"
+This action changes the idle timeout or hard timeout, or both, of this
+OpenFlow rule when the rule matches a TCP packet with the FIN or RST
+flag.  When such a packet is observed, the action reduces the rule's
+timeouts to those specified on the action.  If the rule's existing
+timeout is already shorter than the one that the action specifies,
+then that timeout is unaffected.
+.IP
+\fIargument\fR takes the following forms:
+.RS
+.IP "\fBidle_timeout=\fIseconds\fR"
+Causes the flow to expire after the given number of seconds of
+inactivity.
+.
+.IP "\fBhard_timeout=\fIseconds\fR"
+Causes the flow to expire after the given number of seconds,
+regardless of activity.  (\fIseconds\fR specifies time since the
+flow's creation, not since the receipt of the FIN or RST.)
+.RE
+.IP
+This action was added in Open vSwitch 1.5.90.
 .IP "\fBexit\fR"
 This action causes Open vSwitch to immediately halt execution of further
 actions.  Those actions which have already been executed are unaffected.  Any
 .IP "\fBexit\fR"
 This action causes Open vSwitch to immediately halt execution of further
 actions.  Those actions which have already been executed are unaffected.  Any