Send an optional OVS_PACKET_ATTR_TIMESTAMP attribute in upcalls with
the packet's timestamp.  Add a corresponding tstamp attribute into
struct dpif_upcall.

Enable Linux packet timestamping, only if at least one flow requires
it since Linux packet timestamping is set on or off for all packets.
Define a new DPIF_FP_TIMESTAMP flag for dpif_flow_put.flags to enable
timestamping for the lifetime of a flow.  Add a new netlink
OVS_FLOW_ATTR_TIMESTAMP flag attribute to the Linux datapath's flow
set/mod/get/dump commands.  Call net_enable_timestamp() and
net_disable_timestamp() only if that flag is set.

Enable packet timestamping in the Linux datapath for flows with
"sample" or "ipfix" actions.  Send microsecond-precision timestamps
received in upcalls in IPFIX flow records.

Signed-off-by: Romain Lenglet <rleng...@vmware.com>
---
 datapath/datapath.c          | 67 +++++++++++++++++++++++++++++++++++++++--
 datapath/datapath.h          |  3 ++
 datapath/flow.h              |  1 +
 include/linux/openvswitch.h  | 15 +++++++++-
 lib/dpif-linux.c             | 29 +++++++++++++++++-
 lib/dpif-netdev.c            | 22 ++++++++++++--
 lib/dpif-provider.h          | 15 +++++++---
 lib/dpif.c                   | 49 ++++++++++++++++++++++--------
 lib/dpif.h                   |  8 +++--
 ofproto/ofproto-dpif-ipfix.c | 71 +++++++++++++++++++++++++++++++++++---------
 ofproto/ofproto-dpif-ipfix.h |  3 ++
 ofproto/ofproto-dpif.c       | 36 +++++++++++++++++-----
 utilities/ovs-dpctl.8.in     |  8 +++--
 utilities/ovs-dpctl.c        | 30 +++++++++++++++----
 14 files changed, 301 insertions(+), 56 deletions(-)

diff --git a/datapath/datapath.c b/datapath/datapath.c
index 3cb58b0..9ed81da 100644
--- a/datapath/datapath.c
+++ b/datapath/datapath.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2012 Nicira, Inc.
+ * Copyright (c) 2007-2013 Nicira, Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
@@ -41,6 +41,7 @@
 #include <linux/wait.h>
 #include <asm/div64.h>
 #include <linux/highmem.h>
+#include <linux/netdevice.h>
 #include <linux/netfilter_bridge.h>
 #include <linux/netfilter_ipv4.h>
 #include <linux/inetdevice.h>
@@ -397,6 +398,10 @@ static size_t upcall_msg_size(const struct sk_buff *skb,
                + nla_total_size(skb->len) /* OVS_PACKET_ATTR_PACKET */
                + nla_total_size(key_attr_size()); /* OVS_PACKET_ATTR_KEY */
 
+       /* OVS_PACKET_ATTR_TIMESTAMP */
+       if (skb->tstamp.tv64)
+               size += nla_total_size(sizeof(struct ovs_packet_tstamp));
+
        /* OVS_PACKET_ATTR_USERDATA */
        if (userdata)
                size += NLA_ALIGN(userdata->nla_len);
@@ -445,6 +450,15 @@ static int queue_userspace_packet(struct net *net, int 
dp_ifindex,
        ovs_flow_to_nlattrs(upcall_info->key, user_skb);
        nla_nest_end(user_skb, nla);
 
+       if (skb->tstamp.tv64) {
+               struct ovs_packet_tstamp ts;
+               struct timeval tv = ktime_to_timeval(skb->tstamp);
+               ts.sec = tv.tv_sec;
+               ts.usec = tv.tv_usec;
+
+               nla_put(user_skb, OVS_PACKET_ATTR_TIMESTAMP, sizeof(ts), &ts);
+       }
+
        if (upcall_info->userdata)
                __nla_put(user_skb, OVS_PACKET_ATTR_USERDATA,
                          nla_len(upcall_info->userdata),
@@ -996,6 +1010,7 @@ static const struct nla_policy 
flow_policy[OVS_FLOW_ATTR_MAX + 1] = {
        [OVS_FLOW_ATTR_KEY] = { .type = NLA_NESTED },
        [OVS_FLOW_ATTR_ACTIONS] = { .type = NLA_NESTED },
        [OVS_FLOW_ATTR_CLEAR] = { .type = NLA_FLAG },
+       [OVS_FLOW_ATTR_TIMESTAMP] = { .type = NLA_FLAG },
 };
 
 static struct genl_family dp_flow_genl_family = {
@@ -1111,7 +1126,8 @@ static size_t ovs_flow_cmd_msg_size(const struct 
sw_flow_actions *acts)
                + nla_total_size(sizeof(struct ovs_flow_stats)) /* 
OVS_FLOW_ATTR_STATS */
                + nla_total_size(1) /* OVS_FLOW_ATTR_TCP_FLAGS */
                + nla_total_size(8) /* OVS_FLOW_ATTR_USED */
-               + nla_total_size(acts->actions_len); /* OVS_FLOW_ATTR_ACTIONS */
+               + nla_total_size(acts->actions_len) /* OVS_FLOW_ATTR_ACTIONS */
+               + nla_total_size(0); /* OVS_FLOW_ATTR_TIMESTAMP */
 }
 
 /* Called with ovs_mutex. */
@@ -1165,6 +1181,10 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, 
struct datapath *dp,
            nla_put_u8(skb, OVS_FLOW_ATTR_TCP_FLAGS, tcp_flags))
                goto nla_put_failure;
 
+       if (flow->enable_timestamp &&
+           nla_put_flag(skb, OVS_FLOW_ATTR_TIMESTAMP))
+               goto nla_put_failure;
+
        /* If OVS_FLOW_ATTR_ACTIONS doesn't fit, skip dumping the actions if
         * this is the first flow to be dumped into 'skb'.  This is unusual for
         * Netlink but individual action lists can be longer than
@@ -1223,6 +1243,38 @@ static struct sk_buff *ovs_flow_cmd_build_info(struct 
sw_flow *flow,
        return skb;
 }
 
+static void ovs_flow_set_enable_timestamp(struct sw_flow *flow,
+                                         struct datapath *dp,
+                                         bool enable_timestamp)
+{
+       if (flow->enable_timestamp) {
+               if (!enable_timestamp) {
+                       net_disable_timestamp();
+                       dp->n_enable_timestamp--;
+                       if (dp->n_enable_timestamp < 0) {
+                               pr_err("disabled timestamps one too many 
times\n");
+                       }
+               }
+       } else {
+               if (enable_timestamp) {
+                       net_enable_timestamp();
+                       dp->n_enable_timestamp++;
+               }
+       }
+       flow->enable_timestamp = enable_timestamp;
+}
+
+static void ovs_dp_disable_timestamp(struct datapath *dp)
+{
+       int i, n_enable_timestamp;
+
+       /* Disable packet timestamping for all remaining flows that enabled it. 
*/
+       n_enable_timestamp = dp->n_enable_timestamp;
+       for (i = 0; i < n_enable_timestamp; i++) {
+               net_disable_timestamp();
+       }
+}
+
 static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
 {
        struct nlattr **a = info->attrs;
@@ -1235,6 +1287,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, 
struct genl_info *info)
        struct sw_flow_actions *acts = NULL;
        int error;
        int key_len;
+       bool enable_timestamp;
 
        /* Extract key. */
        error = -EINVAL;
@@ -1259,6 +1312,8 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, 
struct genl_info *info)
                goto error;
        }
 
+       enable_timestamp = a[OVS_FLOW_ATTR_TIMESTAMP];
+
        ovs_lock();
        dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
        error = -ENODEV;
@@ -1291,6 +1346,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, 
struct genl_info *info)
                        error = PTR_ERR(flow);
                        goto err_unlock_ovs;
                }
+               ovs_flow_set_enable_timestamp(flow, dp, enable_timestamp);
                clear_stats(flow);
 
                rcu_assign_pointer(flow->sf_acts, acts);
@@ -1321,6 +1377,8 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, 
struct genl_info *info)
                rcu_assign_pointer(flow->sf_acts, acts);
                ovs_flow_deferred_free_acts(old_acts);
 
+               ovs_flow_set_enable_timestamp(flow, dp, enable_timestamp);
+
                reply = ovs_flow_cmd_build_info(flow, dp, info->snd_portid,
                                               info->snd_seq, OVS_FLOW_CMD_NEW);
 
@@ -1333,6 +1391,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, 
struct genl_info *info)
        }
        ovs_unlock();
 
+
        if (!IS_ERR(reply))
                ovs_notify(reply, info, &ovs_dp_flow_multicast_group);
        else
@@ -1440,6 +1499,8 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct 
genl_info *info)
                                     info->snd_seq, 0, OVS_FLOW_CMD_DEL);
        BUG_ON(err < 0);
 
+       ovs_flow_set_enable_timestamp(flow, dp, false);
+
        ovs_flow_deferred_free(flow);
        ovs_unlock();
 
@@ -1735,6 +1796,8 @@ static void __dp_destroy(struct datapath *dp)
         */
        ovs_dp_detach_port(ovs_vport_ovsl(dp, OVSP_LOCAL));
 
+       ovs_dp_disable_timestamp(dp);
+
        call_rcu(&dp->rcu, destroy_dp_rcu);
 }
 
diff --git a/datapath/datapath.h b/datapath/datapath.h
index ad59a3a..4baaa33 100644
--- a/datapath/datapath.h
+++ b/datapath/datapath.h
@@ -66,6 +66,7 @@ struct dp_stats_percpu {
  * ovs_mutex and RCU.
  * @stats_percpu: Per-CPU datapath statistics.
  * @net: Reference to net namespace.
+ * @n_enable_timestamp: Number of flows that enabled packet timestamping.
  *
  * Context: See the comment on locking at the top of datapath.c for additional
  * locking information.
@@ -87,6 +88,8 @@ struct datapath {
        /* Network namespace ref. */
        struct net *net;
 #endif
+
+       int n_enable_timestamp;
 };
 
 /**
diff --git a/datapath/flow.h b/datapath/flow.h
index dba66cf..6eba67e 100644
--- a/datapath/flow.h
+++ b/datapath/flow.h
@@ -127,6 +127,7 @@ struct sw_flow {
        u64 packet_count;       /* Number of packets matched. */
        u64 byte_count;         /* Number of bytes matched. */
        u8 tcp_flags;           /* Union of seen TCP flags. */
+       bool enable_timestamp;  /* Packet timestamping enabled. */
 };
 
 struct arp_eth_header {
diff --git a/include/linux/openvswitch.h b/include/linux/openvswitch.h
index bd2f05f..432ea90 100644
--- a/include/linux/openvswitch.h
+++ b/include/linux/openvswitch.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2011 Nicira, Inc.
+ * Copyright (c) 2007-2011, 2013 Nicira, Inc.
  *
  * This file is offered under your choice of two licenses: Apache 2.0 or GNU
  * GPL 2.0 or later.  The permission statements for each of these licenses is
@@ -151,6 +151,8 @@ enum ovs_packet_cmd {
  * notification if the %OVS_ACTION_ATTR_USERSPACE action specified an
  * %OVS_USERSPACE_ATTR_USERDATA attribute, with the same length and content
  * specified there.
+ * @OVS_PACKET_ATTR_TIMESTAMP: Present for all notifications if the
+ * packet contains a non-zero timestamp.
  *
  * These attributes follow the &struct ovs_header within the Generic Netlink
  * payload for %OVS_PACKET_* commands.
@@ -161,11 +163,17 @@ enum ovs_packet_attr {
        OVS_PACKET_ATTR_KEY,         /* Nested OVS_KEY_ATTR_* attributes. */
        OVS_PACKET_ATTR_ACTIONS,     /* Nested OVS_ACTION_ATTR_* attributes. */
        OVS_PACKET_ATTR_USERDATA,    /* OVS_ACTION_ATTR_USERSPACE arg. */
+       OVS_PACKET_ATTR_TIMESTAMP,   /* struct ovs_packet_tstamp */
        __OVS_PACKET_ATTR_MAX
 };
 
 #define OVS_PACKET_ATTR_MAX (__OVS_PACKET_ATTR_MAX - 1)
 
+struct ovs_packet_tstamp {
+       __u32 sec;
+       __u32 usec;
+};
+
 /* Virtual ports. */
 
 #define OVS_VPORT_FAMILY  "ovs_vport"
@@ -408,6 +416,10 @@ struct ovs_key_nd {
  * @OVS_FLOW_ATTR_CLEAR: If present in a %OVS_FLOW_CMD_SET request, clears the
  * last-used time, accumulated TCP flags, and statistics for this flow.
  * Otherwise ignored in requests.  Never present in notifications.
+ * @OVS_FLOW_ATTR_TIMESTAMP: If present in a %OVS_FLOW_CMD_NEW or
+ * %OVS_FLOW_CMD_SET request, enables packet timestamping, for one or
+ * more userspace actions in this flow require timestamps.  Otherwise
+ * ignored in requests.
  *
  * These attributes follow the &struct ovs_header within the Generic Netlink
  * payload for %OVS_FLOW_* commands.
@@ -420,6 +432,7 @@ enum ovs_flow_attr {
        OVS_FLOW_ATTR_TCP_FLAGS, /* 8-bit OR'd TCP flags. */
        OVS_FLOW_ATTR_USED,      /* u64 msecs last used in monotonic time. */
        OVS_FLOW_ATTR_CLEAR,     /* Flag to clear stats, tcp_flags, used. */
+       OVS_FLOW_ATTR_TIMESTAMP, /* Flag to enable packet timestamping. */
        __OVS_FLOW_ATTR_MAX
 };
 
diff --git a/lib/dpif-linux.c b/lib/dpif-linux.c
index b863a2e..b20f00f 100644
--- a/lib/dpif-linux.c
+++ b/lib/dpif-linux.c
@@ -111,6 +111,7 @@ struct dpif_linux_flow {
     const uint8_t *tcp_flags;           /* OVS_FLOW_ATTR_TCP_FLAGS. */
     const ovs_32aligned_u64 *used;      /* OVS_FLOW_ATTR_USED. */
     bool clear;                         /* OVS_FLOW_ATTR_CLEAR. */
+    bool enable_timestamp;              /* OVS_FLOW_ATTR_TIMESTAMP. */
 };
 
 static void dpif_linux_flow_init(struct dpif_linux_flow *);
@@ -768,7 +769,8 @@ dpif_linux_flow_get__(const struct dpif *dpif_,
 static int
 dpif_linux_flow_get(const struct dpif *dpif_,
                     const struct nlattr *key, size_t key_len,
-                    struct ofpbuf **actionsp, struct dpif_flow_stats *stats)
+                    struct ofpbuf **actionsp, bool *enable_timestamp,
+                    struct dpif_flow_stats *stats)
 {
     struct dpif_linux_flow reply;
     struct ofpbuf *buf;
@@ -786,6 +788,9 @@ dpif_linux_flow_get(const struct dpif *dpif_,
         } else {
             ofpbuf_delete(buf);
         }
+        if (enable_timestamp) {
+            *enable_timestamp = reply.enable_timestamp;
+        }
     }
     return error;
 }
@@ -810,6 +815,9 @@ dpif_linux_init_flow_put(struct dpif *dpif_, const struct 
dpif_flow_put *put,
     if (put->flags & DPIF_FP_ZERO_STATS) {
         request->clear = true;
     }
+    if (put->flags & DPIF_FP_TIMESTAMP) {
+        request->enable_timestamp = true;
+    }
     request->nlmsg_flags = put->flags & DPIF_FP_MODIFY ? 0 : NLM_F_CREATE;
 }
 
@@ -897,6 +905,7 @@ static int
 dpif_linux_flow_dump_next(const struct dpif *dpif_ OVS_UNUSED, void *state_,
                           const struct nlattr **key, size_t *key_len,
                           const struct nlattr **actions, size_t *actions_len,
+                          bool *enable_timestamp,
                           const struct dpif_flow_stats **stats)
 {
     struct dpif_linux_flow_state *state = state_;
@@ -936,6 +945,9 @@ dpif_linux_flow_dump_next(const struct dpif *dpif_ 
OVS_UNUSED, void *state_,
         *key = state->flow.key;
         *key_len = state->flow.key_len;
     }
+    if (enable_timestamp) {
+        *enable_timestamp = state->flow.enable_timestamp;
+    }
     if (stats) {
         dpif_linux_flow_get_stats(&state->flow, &state->stats);
         *stats = &state->stats;
@@ -1236,6 +1248,9 @@ parse_odp_packet(struct ofpbuf *buf, struct dpif_upcall 
*upcall,
                                      .min_len = ETH_HEADER_LEN },
         [OVS_PACKET_ATTR_KEY] = { .type = NL_A_NESTED },
 
+       /* Present if timestamp available. */
+       [OVS_PACKET_ATTR_TIMESTAMP] = { .type = NL_A_UNSPEC, .optional = true },
+
         /* OVS_PACKET_CMD_ACTION only. */
         [OVS_PACKET_ATTR_USERDATA] = { .type = NL_A_UNSPEC, .optional = true },
     };
@@ -1275,6 +1290,12 @@ parse_odp_packet(struct ofpbuf *buf, struct dpif_upcall 
*upcall,
     upcall->key = CONST_CAST(struct nlattr *,
                              nl_attr_get(a[OVS_PACKET_ATTR_KEY]));
     upcall->key_len = nl_attr_get_size(a[OVS_PACKET_ATTR_KEY]);
+    if (a[OVS_PACKET_ATTR_TIMESTAMP]) {
+        upcall->tstamp = CONST_CAST(
+            struct ovs_packet_tstamp *,
+            nl_attr_get_unspec(a[OVS_PACKET_ATTR_TIMESTAMP],
+                               sizeof *upcall->tstamp));
+    }
     upcall->userdata = a[OVS_PACKET_ATTR_USERDATA];
     *dp_ifindex = ovs_header->dp_ifindex;
 
@@ -1833,6 +1854,7 @@ dpif_linux_flow_from_ofpbuf(struct dpif_linux_flow *flow,
         [OVS_FLOW_ATTR_TCP_FLAGS] = { .type = NL_A_U8, .optional = true },
         [OVS_FLOW_ATTR_USED] = { .type = NL_A_U64, .optional = true },
         /* The kernel never uses OVS_FLOW_ATTR_CLEAR. */
+        [OVS_FLOW_ATTR_TIMESTAMP] = { .type = NL_A_FLAG, .optional = true },
     };
 
     struct nlattr *a[ARRAY_SIZE(ovs_flow_policy)];
@@ -1871,6 +1893,7 @@ dpif_linux_flow_from_ofpbuf(struct dpif_linux_flow *flow,
     if (a[OVS_FLOW_ATTR_USED]) {
         flow->used = nl_attr_get(a[OVS_FLOW_ATTR_USED]);
     }
+    flow->enable_timestamp = a[OVS_FLOW_ATTR_TIMESTAMP];
     return 0;
 }
 
@@ -1906,6 +1929,10 @@ dpif_linux_flow_to_ofpbuf(const struct dpif_linux_flow 
*flow,
     if (flow->clear) {
         nl_msg_put_flag(buf, OVS_FLOW_ATTR_CLEAR);
     }
+
+    if (flow->enable_timestamp) {
+        nl_msg_put_flag(buf, OVS_FLOW_ATTR_TIMESTAMP);
+    }
 }
 
 /* Clears 'flow' to "empty" values. */
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index e4a2f75..bc19d05 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -123,6 +123,8 @@ struct dp_netdev_flow {
     /* Actions. */
     struct nlattr *actions;
     size_t actions_len;
+
+    bool enable_timestamp;
 };
 
 /* Interface to netdev-based datapath. */
@@ -708,7 +710,8 @@ dpif_netdev_flow_from_nlattrs(const struct nlattr *key, 
uint32_t key_len,
 static int
 dpif_netdev_flow_get(const struct dpif *dpif,
                      const struct nlattr *nl_key, size_t nl_key_len,
-                     struct ofpbuf **actionsp, struct dpif_flow_stats *stats)
+                     struct ofpbuf **actionsp, bool *enable_timestamp,
+                     struct dpif_flow_stats *stats)
 {
     struct dp_netdev *dp = get_dp_netdev(dpif);
     struct dp_netdev_flow *flow;
@@ -731,6 +734,9 @@ dpif_netdev_flow_get(const struct dpif *dpif,
     if (actionsp) {
         *actionsp = ofpbuf_clone_data(flow->actions, flow->actions_len);
     }
+    if (enable_timestamp) {
+        *enable_timestamp = flow->enable_timestamp;
+    }
     return 0;
 }
 
@@ -746,7 +752,8 @@ set_flow_actions(struct dp_netdev_flow *flow,
 
 static int
 dp_netdev_flow_add(struct dp_netdev *dp, const struct flow *key,
-                   const struct nlattr *actions, size_t actions_len)
+                   const struct nlattr *actions, size_t actions_len,
+                   bool enable_timestamp)
 {
     struct dp_netdev_flow *flow;
     int error;
@@ -760,6 +767,8 @@ dp_netdev_flow_add(struct dp_netdev *dp, const struct flow 
*key,
         return error;
     }
 
+    flow->enable_timestamp = enable_timestamp;
+
     hmap_insert(&dp->flow_table, &flow->node, flow_hash(&flow->key, 0));
     return 0;
 }
@@ -794,7 +803,8 @@ dpif_netdev_flow_put(struct dpif *dpif, const struct 
dpif_flow_put *put)
                     memset(put->stats, 0, sizeof *put->stats);
                 }
                 return dp_netdev_flow_add(dp, &key, put->actions,
-                                          put->actions_len);
+                                          put->actions_len,
+                                          put->flags & DPIF_FP_TIMESTAMP);
             } else {
                 return EFBIG;
             }
@@ -811,6 +821,7 @@ dpif_netdev_flow_put(struct dpif *dpif, const struct 
dpif_flow_put *put)
                 if (put->flags & DPIF_FP_ZERO_STATS) {
                     clear_stats(flow);
                 }
+                flow->enable_timestamp = put->flags & DPIF_FP_TIMESTAMP;
             }
             return error;
         } else {
@@ -868,6 +879,7 @@ static int
 dpif_netdev_flow_dump_next(const struct dpif *dpif, void *state_,
                            const struct nlattr **key, size_t *key_len,
                            const struct nlattr **actions, size_t *actions_len,
+                           bool *enable_timestamp,
                            const struct dpif_flow_stats **stats)
 {
     struct dp_netdev_flow_state *state = state_;
@@ -900,6 +912,10 @@ dpif_netdev_flow_dump_next(const struct dpif *dpif, void 
*state_,
         *actions_len = flow->actions_len;
     }
 
+    if (enable_timestamp) {
+        *enable_timestamp = flow->enable_timestamp;
+    }
+
     if (stats) {
         get_dpif_flow_stats(flow, &state->stats);
         *stats = &state->stats;
diff --git a/lib/dpif-provider.h b/lib/dpif-provider.h
index bea822f..58e9b19 100644
--- a/lib/dpif-provider.h
+++ b/lib/dpif-provider.h
@@ -221,13 +221,16 @@ struct dpif_class {
      * If 'actionsp' is nonnull, then on success '*actionsp' must be set to an
      * ofpbuf owned by the caller that contains the Netlink attributes for the
      * flow's actions.  The caller must free the ofpbuf (with ofpbuf_delete())
-     * when it is no longer needed.
+     * when it is no longer needed.  If 'enable_timestamp' is nonnull
+     * then it should be set to true or false depending on whether
+     * packet timestamping is enable for the flow.
      *
      * If 'stats' is nonnull, then on success it must be updated with the
      * flow's statistics. */
     int (*flow_get)(const struct dpif *dpif,
                     const struct nlattr *key, size_t key_len,
-                    struct ofpbuf **actionsp, struct dpif_flow_stats *stats);
+                    struct ofpbuf **actionsp, bool *enable_timestamp,
+                    struct dpif_flow_stats *stats);
 
     /* Adds or modifies a flow in 'dpif'.  The flow is specified by the Netlink
      * attributes with types OVS_KEY_ATTR_* in the 'put->key_len' bytes
@@ -283,8 +286,11 @@ struct dpif_class {
      * '*key_len' must be set to Netlink attributes with types OVS_KEY_ATTR_*
      * representing the dumped flow's key.  If 'actions' and 'actions_len' are
      * nonnull then they should be set to Netlink attributes with types
-     * OVS_ACTION_ATTR_* representing the dumped flow's actions.  If 'stats'
-     * is nonnull then it should be set to the dumped flow's statistics.
+     * OVS_ACTION_ATTR_* representing the dumped flow's actions.  If
+     * 'enable_timestamp' is nonnull then it should be set to true or
+     * false depending on whether packet timestamping is enable for
+     * the flow.  If 'stats' is nonnull then it should be set to the
+     * dumped flow's statistics.
      *
      * All of the returned data is owned by 'dpif', not by the caller, and the
      * caller must not modify or free it.  'dpif' must guarantee that it
@@ -293,6 +299,7 @@ struct dpif_class {
     int (*flow_dump_next)(const struct dpif *dpif, void *state,
                           const struct nlattr **key, size_t *key_len,
                           const struct nlattr **actions, size_t *actions_len,
+                          bool *enable_timestamp,
                           const struct dpif_flow_stats **stats);
 
     /* Releases resources from 'dpif' for 'state', which was initialized by a
diff --git a/lib/dpif.c b/lib/dpif.c
index 6aa52d5..1483688 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -82,7 +82,8 @@ static void log_flow_message(const struct dpif *dpif, int 
error,
                              const char *operation,
                              const struct nlattr *key, size_t key_len,
                              const struct dpif_flow_stats *stats,
-                             const struct nlattr *actions, size_t actions_len);
+                             const struct nlattr *actions, size_t actions_len,
+                             bool enable_timestamp);
 static void log_operation(const struct dpif *, const char *operation,
                           int error);
 static bool should_log_flow_message(int error);
@@ -775,24 +776,31 @@ dpif_flow_flush(struct dpif *dpif)
  * If 'actionsp' is nonnull, then on success '*actionsp' will be set to an
  * ofpbuf owned by the caller that contains the Netlink attributes for the
  * flow's actions.  The caller must free the ofpbuf (with ofpbuf_delete()) when
- * it is no longer needed.
+ * it is no longer needed.  If 'enable_timestamp' is nonnull, then on
+ * success '*enable_timestamp' will be set to true or false depending
+ * on whether packet timestamping is enabled for the flow.
  *
  * If 'stats' is nonnull, then on success it will be updated with the flow's
  * statistics. */
 int
 dpif_flow_get(const struct dpif *dpif,
               const struct nlattr *key, size_t key_len,
-              struct ofpbuf **actionsp, struct dpif_flow_stats *stats)
+              struct ofpbuf **actionsp, bool *enable_timestamp,
+              struct dpif_flow_stats *stats)
 {
     int error;
 
     COVERAGE_INC(dpif_flow_get);
 
-    error = dpif->dpif_class->flow_get(dpif, key, key_len, actionsp, stats);
+    error = dpif->dpif_class->flow_get(dpif, key, key_len, actionsp,
+                                       enable_timestamp, stats);
     if (error) {
         if (actionsp) {
             *actionsp = NULL;
         }
+        if (enable_timestamp) {
+            *enable_timestamp = false;
+        }
         if (stats) {
             memset(stats, 0, sizeof *stats);
         }
@@ -809,7 +817,8 @@ dpif_flow_get(const struct dpif *dpif,
             actions_len = 0;
         }
         log_flow_message(dpif, error, "flow_get", key, key_len, stats,
-                         actions, actions_len);
+                         actions, actions_len,
+                         enable_timestamp ? *enable_timestamp : false);
     }
     return error;
 }
@@ -821,7 +830,7 @@ dpif_flow_put__(struct dpif *dpif, const struct 
dpif_flow_put *put)
 
     COVERAGE_INC(dpif_flow_put);
     ovs_assert(!(put->flags & ~(DPIF_FP_CREATE | DPIF_FP_MODIFY
-                                | DPIF_FP_ZERO_STATS)));
+                                | DPIF_FP_ZERO_STATS | DPIF_FP_TIMESTAMP)));
 
     error = dpif->dpif_class->flow_put(dpif, put);
     if (error && put->stats) {
@@ -835,6 +844,8 @@ dpif_flow_put__(struct dpif *dpif, const struct 
dpif_flow_put *put)
  * attributes with types OVS_KEY_ATTR_* in the 'key_len' bytes starting at
  * 'key'.  The associated actions are specified by the Netlink attributes with
  * types OVS_ACTION_ATTR_* in the 'actions_len' bytes starting at 'actions'.
+ * Packet timestamping can be enabled for the flow by setting the
+ * DPIF_FP_TIMESTAMP flag in 'flags'.
  *
  * - If the flow's key does not exist in 'dpif', then the flow will be added if
  *   'flags' includes DPIF_FP_CREATE.  Otherwise the operation will fail with
@@ -927,8 +938,10 @@ dpif_flow_dump_start(struct dpif_flow_dump *dump, const 
struct dpif *dpif)
  * will be set to Netlink attributes with types OVS_KEY_ATTR_* representing the
  * dumped flow's key.  If 'actions' and 'actions_len' are nonnull then they are
  * set to Netlink attributes with types OVS_ACTION_ATTR_* representing the
- * dumped flow's actions.  If 'stats' is nonnull then it will be set to the
- * dumped flow's statistics.
+ * dumped flow's actions.  If 'enable_timestamp' is nonnull then it is
+ * set to true or false depending on whether packet timestamping is
+ * enabled for the flow.  If 'stats' is nonnull then it will be set to
+ * the dumped flow's statistics.
  *
  * All of the returned data is owned by 'dpif', not by the caller, and the
  * caller must not modify or free it.  'dpif' guarantees that it remains
@@ -938,6 +951,7 @@ bool
 dpif_flow_dump_next(struct dpif_flow_dump *dump,
                     const struct nlattr **key, size_t *key_len,
                     const struct nlattr **actions, size_t *actions_len,
+                    bool *enable_timestamp,
                     const struct dpif_flow_stats **stats)
 {
     const struct dpif *dpif = dump->dpif;
@@ -947,7 +961,7 @@ dpif_flow_dump_next(struct dpif_flow_dump *dump,
         error = dpif->dpif_class->flow_dump_next(dpif, dump->state,
                                                  key, key_len,
                                                  actions, actions_len,
-                                                 stats);
+                                                 enable_timestamp, stats);
         if (error) {
             dpif->dpif_class->flow_dump_done(dpif, dump->state);
         }
@@ -961,6 +975,9 @@ dpif_flow_dump_next(struct dpif_flow_dump *dump,
             *actions = NULL;
             *actions_len = 0;
         }
+        if (enable_timestamp) {
+            *enable_timestamp = false;
+        }
         if (stats) {
             *stats = NULL;
         }
@@ -972,7 +989,8 @@ dpif_flow_dump_next(struct dpif_flow_dump *dump,
             log_flow_message(dpif, error, "flow_dump",
                              key ? *key : NULL, key ? *key_len : 0,
                              stats ? *stats : NULL, actions ? *actions : NULL,
-                             actions ? *actions_len : 0);
+                             actions ? *actions_len : 0,
+                             enable_timestamp ? *enable_timestamp : false);
         }
     }
     dump->error = error;
@@ -1264,7 +1282,8 @@ static void
 log_flow_message(const struct dpif *dpif, int error, const char *operation,
                  const struct nlattr *key, size_t key_len,
                  const struct dpif_flow_stats *stats,
-                 const struct nlattr *actions, size_t actions_len)
+                 const struct nlattr *actions, size_t actions_len,
+                 bool enable_timestamp)
 {
     struct ds ds = DS_EMPTY_INITIALIZER;
     ds_put_format(&ds, "%s: ", dpif_name(dpif));
@@ -1284,6 +1303,9 @@ log_flow_message(const struct dpif *dpif, int error, 
const char *operation,
         ds_put_cstr(&ds, ", actions:");
         format_odp_actions(&ds, actions, actions_len);
     }
+    if (enable_timestamp) {
+        ds_put_cstr(&ds, ", enable_timestamp");
+    }
     vlog(THIS_MODULE, flow_message_log_level(error), "%s", ds_cstr(&ds));
     ds_destroy(&ds);
 }
@@ -1308,7 +1330,8 @@ log_flow_put_message(struct dpif *dpif, const struct 
dpif_flow_put *put,
         }
         log_flow_message(dpif, error, ds_cstr(&s),
                          put->key, put->key_len, put->stats,
-                         put->actions, put->actions_len);
+                         put->actions, put->actions_len,
+                         put->flags & DPIF_FP_TIMESTAMP);
         ds_destroy(&s);
     }
 }
@@ -1319,7 +1342,7 @@ log_flow_del_message(struct dpif *dpif, const struct 
dpif_flow_del *del,
 {
     if (should_log_flow_message(error)) {
         log_flow_message(dpif, error, "flow_del", del->key, del->key_len,
-                         !error ? del->stats : NULL, NULL, 0);
+                         !error ? del->stats : NULL, NULL, 0, false);
     }
 }
 
diff --git a/lib/dpif.h b/lib/dpif.h
index fd05b2f..c23a8d3 100644
--- a/lib/dpif.h
+++ b/lib/dpif.h
@@ -441,7 +441,8 @@ void dpif_flow_stats_format(const struct dpif_flow_stats *, 
struct ds *);
 enum dpif_flow_put_flags {
     DPIF_FP_CREATE = 1 << 0,    /* Allow creating a new flow. */
     DPIF_FP_MODIFY = 1 << 1,    /* Allow modifying an existing flow. */
-    DPIF_FP_ZERO_STATS = 1 << 2 /* Zero the stats of an existing flow. */
+    DPIF_FP_ZERO_STATS = 1 << 2, /* Zero the stats of an existing flow. */
+    DPIF_FP_TIMESTAMP = 1 << 3  /* Enable packet timestamping. */
 };
 
 int dpif_flow_flush(struct dpif *);
@@ -454,7 +455,8 @@ int dpif_flow_del(struct dpif *,
                   struct dpif_flow_stats *);
 int dpif_flow_get(const struct dpif *,
                   const struct nlattr *key, size_t key_len,
-                  struct ofpbuf **actionsp, struct dpif_flow_stats *);
+                  struct ofpbuf **actionsp, bool *enable_timestamp,
+                  struct dpif_flow_stats *);
 
 struct dpif_flow_dump {
     const struct dpif *dpif;
@@ -465,6 +467,7 @@ void dpif_flow_dump_start(struct dpif_flow_dump *, const 
struct dpif *);
 bool dpif_flow_dump_next(struct dpif_flow_dump *,
                          const struct nlattr **key, size_t *key_len,
                          const struct nlattr **actions, size_t *actions_len,
+                         bool *enable_timestamp,
                          const struct dpif_flow_stats **);
 int dpif_flow_dump_done(struct dpif_flow_dump *);
 
@@ -551,6 +554,7 @@ struct dpif_upcall {
     struct ofpbuf *packet;      /* Packet data. */
     struct nlattr *key;         /* Flow key. */
     size_t key_len;             /* Length of 'key' in bytes. */
+    struct ovs_packet_tstamp *tstamp; /* Optional packet timestamp. */
 
     /* DPIF_UC_ACTION only. */
     struct nlattr *userdata;    /* Argument to OVS_ACTION_ATTR_USERSPACE. */
diff --git a/ofproto/ofproto-dpif-ipfix.c b/ofproto/ofproto-dpif-ipfix.c
index 9de8b1c..545405f 100644
--- a/ofproto/ofproto-dpif-ipfix.c
+++ b/ofproto/ofproto-dpif-ipfix.c
@@ -23,6 +23,7 @@
 #include "hmap.h"
 #include "ofpbuf.h"
 #include "ofproto.h"
+#include "linux/openvswitch.h"
 #include "packets.h"
 #include "sset.h"
 #include "util.h"
@@ -139,6 +140,8 @@ BUILD_ASSERT_DECL(sizeof(struct 
ipfix_template_field_specifier) == 4);
 
 /* Part of data record for common metadata and Ethernet entities. */
 struct ipfix_data_record_common {
+    ovs_be32 flow_start_delta_microseconds; /* FLOW_START_DELTA_MICROSECONDS */
+    ovs_be32 flow_end_delta_microseconds; /* FLOW_END_DELTA_MICROSECONDS */
     ovs_be32 observation_point_id;  /* OBSERVATION_POINT_ID */
     ovs_be64 packet_delta_count;  /* PACKET_DELTA_COUNT */
     ovs_be64 layer2_octet_delta_count;  /* LAYER2_OCTET_DELTA_COUNT */
@@ -148,7 +151,7 @@ struct ipfix_data_record_common {
     ovs_be16 ethernet_total_length;  /* ETHERNET_TOTAL_LENGTH */
     uint8_t ethernet_header_length;  /* ETHERNET_HEADER_LENGTH */
 } __attribute__((packed));
-BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_common) == 37);
+BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_common) == 45);
 
 /* Part of data record for VLAN entities. */
 struct ipfix_data_record_vlan {
@@ -498,15 +501,15 @@ dpif_ipfix_destroy(struct dpif_ipfix *di)
 }
 
 static void
-ipfix_init_header(uint32_t seq_number, uint32_t obs_domain_id,
-                  struct ofpbuf *msg)
+ipfix_init_header(uint32_t export_time_sec, uint32_t seq_number,
+                  uint32_t obs_domain_id, struct ofpbuf *msg)
 {
     struct ipfix_header *hdr;
 
     hdr = ofpbuf_put_zeros(msg, sizeof *hdr);
     hdr->version = htons(IPFIX_VERSION);
     hdr->length = htons(sizeof *hdr);  /* Updated in ipfix_send_msg. */
-    hdr->export_time = htonl(time_wall());
+    hdr->export_time = htonl(export_time_sec);
     hdr->seq_number = htonl(seq_number);
     hdr->obs_domain_id = htonl(obs_domain_id);
 }
@@ -559,6 +562,8 @@ ipfix_define_template_fields(enum ipfix_proto_l2 l2, enum 
ipfix_proto_l3 l3,
         count++; \
     }
 
+    DEF(FLOW_START_DELTA_MICROSECONDS);
+    DEF(FLOW_END_DELTA_MICROSECONDS);
     DEF(OBSERVATION_POINT_ID);
     DEF(PACKET_DELTA_COUNT);
     DEF(LAYER2_OCTET_DELTA_COUNT);
@@ -606,7 +611,7 @@ ipfix_define_template_fields(enum ipfix_proto_l2 l2, enum 
ipfix_proto_l3 l3,
 
 static void
 ipfix_send_template_msg(struct dpif_ipfix_exporter *exporter,
-                        uint32_t obs_domain_id)
+                        uint32_t export_time_sec, uint32_t obs_domain_id)
 {
     uint64_t msg_stub[DIV_ROUND_UP(1500, 8)];
     struct ofpbuf msg;
@@ -620,7 +625,8 @@ ipfix_send_template_msg(struct dpif_ipfix_exporter 
*exporter,
 
     ofpbuf_use_stub(&msg, msg_stub, sizeof msg_stub);
 
-    ipfix_init_header(exporter->seq_number, obs_domain_id, &msg);
+    ipfix_init_header(export_time_sec, exporter->seq_number, obs_domain_id,
+                      &msg);
     set_hdr_offset = msg.size;
 
     /* Add a Template Set. */
@@ -661,6 +667,8 @@ ipfix_send_template_msg(struct dpif_ipfix_exporter 
*exporter,
 
 static void
 ipfix_send_data_msg(struct dpif_ipfix_exporter *exporter, struct ofpbuf 
*packet,
+                    uint32_t export_time_sec,
+                    uint32_t export_time_neg_delta_usec,
                     const struct flow *flow, uint64_t packet_delta_count,
                     uint32_t obs_domain_id, uint32_t obs_point_id)
 {
@@ -674,7 +682,8 @@ ipfix_send_data_msg(struct dpif_ipfix_exporter *exporter, 
struct ofpbuf *packet,
 
     ofpbuf_use_stub(&msg, msg_stub, sizeof msg_stub);
 
-    ipfix_init_header(exporter->seq_number, obs_domain_id, &msg);
+    ipfix_init_header(export_time_sec, exporter->seq_number, obs_domain_id,
+                      &msg);
     exporter->seq_number++;
     set_hdr_offset = msg.size;
 
@@ -728,6 +737,12 @@ ipfix_send_data_msg(struct dpif_ipfix_exporter *exporter, 
struct ofpbuf *packet,
         layer2_octet_delta_count = packet_delta_count * ethernet_total_length;
 
         data_common = ofpbuf_put_zeros(&msg, sizeof *data_common);
+        /* Send a duration of 1usec for every flow, so that it's
+         * non-zero, which could be confusing to collectors. */
+        data_common->flow_start_delta_microseconds =
+            htonl(export_time_neg_delta_usec + 1);
+        data_common->flow_end_delta_microseconds =
+            htonl(export_time_neg_delta_usec);
         data_common->observation_point_id = htonl(obs_point_id);
         data_common->packet_delta_count = htonll(packet_delta_count);
         data_common->layer2_octet_delta_count =
@@ -798,29 +813,56 @@ ipfix_send_data_msg(struct dpif_ipfix_exporter *exporter, 
struct ofpbuf *packet,
 
 static void
 dpif_ipfix_sample(struct dpif_ipfix_exporter *exporter,
-                  struct ofpbuf *packet, const struct flow *flow,
-                  uint64_t packet_delta_count, uint32_t obs_domain_id,
-                  uint32_t obs_point_id)
+                  struct ofpbuf *packet, const struct ovs_packet_tstamp 
*tstamp,
+                  const struct flow *flow, uint64_t packet_delta_count,
+                  uint32_t obs_domain_id, uint32_t obs_point_id)
 {
     time_t now = time_wall();
+    uint32_t sample_time_sec, sample_time_delta_usec;
+    uint32_t export_time_sec, export_time_neg_delta_usec;
+
+    if (tstamp) {
+        sample_time_sec = tstamp->sec;
+        sample_time_delta_usec = tstamp->usec;
+    } else {
+        /* A packet in an upcall may not have a timestamp in a few
+         * cases, e.g. if the upcall was triggered by a packet sent
+         * from userspace in a dpif_execute() and not received on a
+         * Linux interface, or if the datapath doesn't support packet
+         * timestamping.  In those cases, build a less precise
+         * timestamp now using the clock. */
+        struct timeval tv;
+        xgettimeofday(&tv);
+        sample_time_sec = tv.tv_sec;
+        sample_time_delta_usec = tv.tv_usec;
+    }
+
+    /* The IPFIX start and end deltas are negative deltas relative to
+     * the export time, so set the export time 1 second off to
+     * calculate those deltas. */
+    export_time_sec = sample_time_sec + 1;
+    export_time_neg_delta_usec = 1000000 - sample_time_delta_usec;
+
     if ((exporter->last_template_set_time + IPFIX_TEMPLATE_INTERVAL) <= now) {
-        ipfix_send_template_msg(exporter, obs_domain_id);
+        ipfix_send_template_msg(exporter, export_time_sec, obs_domain_id);
         exporter->last_template_set_time = now;
     }
 
-    ipfix_send_data_msg(exporter, packet, flow, packet_delta_count,
+    ipfix_send_data_msg(exporter, packet, export_time_sec,
+                        export_time_neg_delta_usec, flow, packet_delta_count,
                         obs_domain_id, obs_point_id);
 }
 
 void
 dpif_ipfix_bridge_sample(struct dpif_ipfix *di, struct ofpbuf *packet,
+                         const struct ovs_packet_tstamp *tstamp,
                          const struct flow *flow)
 {
     /* Use the sampling probability as an approximation of the number
      * of matched packets. */
     uint64_t packet_delta_count = UINT32_MAX / di->bridge_exporter.probability;
 
-    dpif_ipfix_sample(&di->bridge_exporter.exporter, packet, flow,
+    dpif_ipfix_sample(&di->bridge_exporter.exporter, packet, tstamp, flow,
                       packet_delta_count,
                       di->bridge_exporter.options->obs_domain_id,
                       di->bridge_exporter.options->obs_point_id);
@@ -828,6 +870,7 @@ dpif_ipfix_bridge_sample(struct dpif_ipfix *di, struct 
ofpbuf *packet,
 
 void
 dpif_ipfix_flow_sample(struct dpif_ipfix *di, struct ofpbuf *packet,
+                       const struct ovs_packet_tstamp *tstamp,
                        const struct flow *flow, uint32_t collector_set_id,
                        uint16_t probability, uint32_t obs_domain_id,
                        uint32_t obs_point_id)
@@ -843,6 +886,6 @@ dpif_ipfix_flow_sample(struct dpif_ipfix *di, struct ofpbuf 
*packet,
         return;
     }
 
-    dpif_ipfix_sample(&node->exporter.exporter, packet, flow,
+    dpif_ipfix_sample(&node->exporter.exporter, packet, tstamp, flow,
                       packet_delta_count, obs_domain_id, obs_point_id);
 }
diff --git a/ofproto/ofproto-dpif-ipfix.h b/ofproto/ofproto-dpif-ipfix.h
index 26b02f1..6d8db94 100644
--- a/ofproto/ofproto-dpif-ipfix.h
+++ b/ofproto/ofproto-dpif-ipfix.h
@@ -24,6 +24,7 @@ struct flow;
 struct ofpbuf;
 struct ofproto_ipfix_bridge_exporter_options;
 struct ofproto_ipfix_flow_exporter_options;
+struct ovs_packet_tstamp;
 
 struct dpif_ipfix *dpif_ipfix_create(void);
 uint32_t dpif_ipfix_get_bridge_exporter_probability(const struct dpif_ipfix *);
@@ -34,8 +35,10 @@ void dpif_ipfix_set_options(
     const struct ofproto_ipfix_flow_exporter_options *, size_t);
 
 void dpif_ipfix_bridge_sample(struct dpif_ipfix *, struct ofpbuf *,
+                              const struct ovs_packet_tstamp *,
                               const struct flow *);
 void dpif_ipfix_flow_sample(struct dpif_ipfix *, struct ofpbuf *,
+                            const struct ovs_packet_tstamp *,
                             const struct flow *, uint32_t, uint16_t, uint32_t,
                             uint32_t);
 
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index 40e897f..027fc60 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -274,6 +274,7 @@ struct action_xlate_ctx {
     bool has_learn;             /* Actions include NXAST_LEARN? */
     bool has_normal;            /* Actions output to OFPP_NORMAL? */
     bool has_fin_timeout;       /* Actions include NXAST_FIN_TIMEOUT? */
+    bool enable_timestamp;      /* Actions require packet timestamping? */
     uint16_t nf_output_iface;   /* Output interface index for NetFlow. */
     mirror_mask_t mirrors;      /* Bitmap of associated mirrors. */
 
@@ -485,6 +486,7 @@ struct facet {
     bool has_learn;              /* Actions include NXAST_LEARN? */
     bool has_normal;             /* Actions output to OFPP_NORMAL? */
     bool has_fin_timeout;        /* Actions include NXAST_FIN_TIMEOUT? */
+    bool enable_timestamp;       /* Actions require packet timestamping? */
     tag_type tags;               /* Tags that would require revalidation. */
     mirror_mask_t mirrors;       /* Bitmap of dependent mirrors. */
 
@@ -825,7 +827,7 @@ static size_t compose_sflow_action(const struct 
ofproto_dpif *,
                                    const struct flow *, uint32_t odp_port);
 static void compose_ipfix_action(const struct ofproto_dpif *,
                                  struct ofpbuf *odp_actions,
-                                 const struct flow *);
+                                 const struct flow *, bool *);
 static void add_mirror_actions(struct action_xlate_ctx *ctx,
                                const struct flow *flow);
 /* Global variables. */
@@ -3725,6 +3727,9 @@ handle_flow_miss_with_facet(struct flow_miss *miss, 
struct facet *facet,
         op->garbage = NULL;
         op->dpif_op.type = DPIF_OP_FLOW_PUT;
         put->flags = DPIF_FP_CREATE | DPIF_FP_MODIFY;
+        if (facet->enable_timestamp) {
+            put->flags |= DPIF_FP_TIMESTAMP;
+        }
         put->key = miss->key;
         put->key_len = miss->key_len;
         if (want_path == SF_FAST_PATH) {
@@ -4133,8 +4138,8 @@ handle_flow_sample_upcall(struct dpif_backer *backer,
 
     /* The flow reflects exactly the contents of the packet.  Sample
      * the packet using it. */
-    dpif_ipfix_flow_sample(ofproto->ipfix, upcall->packet, &flow,
-                           cookie.flow_sample.collector_set_id,
+    dpif_ipfix_flow_sample(ofproto->ipfix, upcall->packet, upcall->tstamp,
+                           &flow, cookie.flow_sample.collector_set_id,
                            cookie.flow_sample.probability,
                            cookie.flow_sample.obs_domain_id,
                            cookie.flow_sample.obs_point_id);
@@ -4155,7 +4160,8 @@ handle_ipfix_upcall(struct dpif_backer *backer,
 
     /* The flow reflects exactly the contents of the packet.  Sample
      * the packet using it. */
-    dpif_ipfix_bridge_sample(ofproto->ipfix, upcall->packet, &flow);
+    dpif_ipfix_bridge_sample(ofproto->ipfix, upcall->packet, upcall->tstamp,
+                             &flow);
 }
 
 static int
@@ -4365,7 +4371,8 @@ update_stats(struct dpif_backer *backer)
     size_t key_len;
 
     dpif_flow_dump_start(&dump, backer->dpif);
-    while (dpif_flow_dump_next(&dump, &key, &key_len, NULL, NULL, &stats)) {
+    while (dpif_flow_dump_next(&dump, &key, &key_len, NULL, NULL, NULL,
+                               &stats)) {
         struct flow flow;
         struct subfacet *subfacet;
         struct ofport_dpif *ofport;
@@ -5127,6 +5134,7 @@ facet_revalidate(struct facet *facet)
     facet->has_learn = ctx.has_learn;
     facet->has_normal = ctx.has_normal;
     facet->has_fin_timeout = ctx.has_fin_timeout;
+    facet->enable_timestamp = ctx.enable_timestamp;
     facet->mirrors = ctx.mirrors;
 
     i = 0;
@@ -5421,6 +5429,7 @@ subfacet_make_actions(struct subfacet *subfacet, const 
struct ofpbuf *packet,
     facet->has_learn = ctx.has_learn;
     facet->has_normal = ctx.has_normal;
     facet->has_fin_timeout = ctx.has_fin_timeout;
+    facet->enable_timestamp = ctx.enable_timestamp;
     facet->nf_flow.output_iface = ctx.nf_output_iface;
     facet->mirrors = ctx.mirrors;
 
@@ -5456,6 +5465,9 @@ subfacet_install(struct subfacet *subfacet,
     if (stats) {
         flags |= DPIF_FP_ZERO_STATS;
     }
+    if (facet->enable_timestamp) {
+        flags |= DPIF_FP_TIMESTAMP;
+    }
 
     if (path == SF_SLOW_PATH) {
         compose_slow_path(ofproto, &facet->flow, slow,
@@ -5791,6 +5803,7 @@ send_packet(const struct ofport_dpif *ofport, struct 
ofpbuf *packet)
     uint32_t odp_port;
     struct flow flow;
     int error;
+    bool enable_timestamp = false;
 
     flow_extract(packet, 0, 0, NULL, OFPP_LOCAL, &flow);
     if (netdev_vport_is_patch(ofport->up.netdev)) {
@@ -5844,7 +5857,7 @@ send_packet(const struct ofport_dpif *ofport, struct 
ofpbuf *packet)
                            ofp_port_to_odp_port(ofproto, flow.in_port));
 
     compose_sflow_action(ofproto, &odp_actions, &flow, odp_port);
-    compose_ipfix_action(ofproto, &odp_actions, &flow);
+    compose_ipfix_action(ofproto, &odp_actions, &flow, &enable_timestamp);
 
     nl_msg_put_u32(&odp_actions, OVS_ACTION_ATTR_OUTPUT, odp_port);
     error = dpif_execute(ofproto->backer->dpif,
@@ -6022,7 +6035,7 @@ compose_ipfix_cookie(union user_action_cookie *cookie)
 static void
 compose_ipfix_action(const struct ofproto_dpif *ofproto,
                      struct ofpbuf *odp_actions,
-                     const struct flow *flow)
+                     const struct flow *flow, bool *enable_timestamp)
 {
     uint32_t probability;
     union user_action_cookie cookie;
@@ -6036,6 +6049,10 @@ compose_ipfix_action(const struct ofproto_dpif *ofproto,
 
     compose_sample_action(ofproto, odp_actions, flow,  probability,
                           &cookie, sizeof cookie.ipfix);
+
+    if (enable_timestamp) {
+        *enable_timestamp = true;
+    }
 }
 
 /* SAMPLE action for sFlow must be first action in any given list of
@@ -6056,7 +6073,8 @@ add_sflow_action(struct action_xlate_ctx *ctx)
 static void
 add_ipfix_action(struct action_xlate_ctx *ctx)
 {
-    compose_ipfix_action(ctx->ofproto, ctx->odp_actions, &ctx->flow);
+    compose_ipfix_action(ctx->ofproto, ctx->odp_actions, &ctx->flow,
+                         &ctx->enable_timestamp);
 }
 
 /* Fix SAMPLE action according to data collected while composing ODP actions.
@@ -7003,6 +7021,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t 
ofpacts_len,
         }
 
         case OFPACT_SAMPLE:
+            ctx->enable_timestamp = true;
             xlate_sample_action(ctx, ofpact_get_SAMPLE(a));
             break;
         }
@@ -7087,6 +7106,7 @@ xlate_actions(struct action_xlate_ctx *ctx,
     ctx->has_learn = false;
     ctx->has_normal = false;
     ctx->has_fin_timeout = false;
+    ctx->enable_timestamp = false;
     ctx->nf_output_iface = NF_OUT_DROP;
     ctx->mirrors = 0;
     ctx->recurse = 0;
diff --git a/utilities/ovs-dpctl.8.in b/utilities/ovs-dpctl.8.in
index 2b0036c..e28de66 100644
--- a/utilities/ovs-dpctl.8.in
+++ b/utilities/ovs-dpctl.8.in
@@ -122,11 +122,15 @@ required.
 Prints to the console all flow entries in datapath \fIdp\fR's
 flow table.
 .
-.IP "\fBadd\-flow\fR [\fIdp\fR] \fIflow actions\fR"
-.IQ "[\fB\-\-clear\fR] [\fB\-\-may-create\fR] [\fB\-s\fR | 
\fB\-\-statistics\fR] \fBmod\-flow\fR [\fIdp\fR] \fIflow actions\fR"
+.IP "\fBadd\-flow\fR [\fIdp\fR] \fIflow actions\fR [\fBenable_timestamp\fR]"
+.IQ "[\fB\-\-clear\fR] [\fB\-\-may-create\fR] [\fB\-s\fR | 
\fB\-\-statistics\fR] \fBmod\-flow\fR [\fIdp\fR] \fIflow actions\fR 
[\fBenable_timestamp\fR]"
 Adds or modifies a flow in \fIdp\fR's flow table that, when a packet
 matching \fIflow\fR arrives, causes \fIactions\fR to be executed.
 .IP
+If \fBenable_timestamp\fR is specified, timestamping of packets sent
+in upcalls is enabled until all flows that enable timestamping are
+deleted.
+.IP
 The \fBadd\-flow\fR command succeeds only if \fIflow\fR does not
 already exist in \fIdp\fR.  Contrariwise, \fBmod\-flow\fR without
 \fB\-\-may\-create\fR only modifies the actions for an existing flow.
diff --git a/utilities/ovs-dpctl.c b/utilities/ovs-dpctl.c
index 3b6e6a5..6fcb00c 100644
--- a/utilities/ovs-dpctl.c
+++ b/utilities/ovs-dpctl.c
@@ -748,6 +748,7 @@ dpctl_dump_flows(int argc, char *argv[])
     size_t key_len;
     struct ds ds;
     char *name;
+    bool enable_timestamp;
 
     name = (argc == 2) ? xstrdup(argv[1]) : get_one_dp();
     run(parsed_dpif_open(name, false, &dpif), "opening datapath");
@@ -756,13 +757,17 @@ dpctl_dump_flows(int argc, char *argv[])
     ds_init(&ds);
     dpif_flow_dump_start(&dump, dpif);
     while (dpif_flow_dump_next(&dump, &key, &key_len,
-                               &actions, &actions_len, &stats)) {
+                               &actions, &actions_len, &enable_timestamp,
+                               &stats)) {
         ds_clear(&ds);
         odp_flow_key_format(key, key_len, &ds);
         ds_put_cstr(&ds, ", ");
         dpif_flow_stats_format(stats, &ds);
         ds_put_cstr(&ds, ", actions:");
         format_odp_actions(&ds, actions, actions_len);
+        if (enable_timestamp) {
+            ds_put_cstr(&ds, ", enable_timestamp");
+        }
         printf("%s\n", ds_cstr(&ds));
     }
     dpif_flow_dump_done(&dump);
@@ -773,21 +778,34 @@ dpctl_dump_flows(int argc, char *argv[])
 static void
 dpctl_put_flow(int argc, char *argv[], enum dpif_flow_put_flags flags)
 {
-    const char *key_s = argv[argc - 2];
-    const char *actions_s = argv[argc - 1];
+    const char *key_s;
+    const char *actions_s;
     struct dpif_flow_stats stats;
     struct ofpbuf actions;
     struct ofpbuf key;
     struct dpif *dpif;
+    bool has_dp_name;
     char *dp_name;
 
+    if (argc > 2 && !strcmp(argv[argc - 1], "enable_timestamp")) {
+        flags |= DPIF_FP_TIMESTAMP;
+
+        key_s = argv[argc - 3];
+        actions_s = argv[argc - 2];
+        has_dp_name = argc == 4;
+    } else {
+        key_s = argv[argc - 2];
+        actions_s = argv[argc - 1];
+        has_dp_name = argc == 3;
+    }
+
     ofpbuf_init(&key, 0);
     run(odp_flow_key_from_string(key_s, NULL, &key), "parsing flow key");
 
     ofpbuf_init(&actions, 0);
     run(odp_actions_from_string(actions_s, NULL, &actions), "parsing actions");
 
-    dp_name = argc == 3 ? xstrdup(argv[1]) : get_one_dp();
+    dp_name = has_dp_name ? xstrdup(argv[1]) : get_one_dp();
     run(parsed_dpif_open(dp_name, false, &dpif), "opening datapath");
     free(dp_name);
 
@@ -1121,8 +1139,8 @@ static const struct command all_commands[] = {
     { "dump-dps", 0, 0, dpctl_dump_dps },
     { "show", 0, INT_MAX, dpctl_show },
     { "dump-flows", 0, 1, dpctl_dump_flows },
-    { "add-flow", 2, 3, dpctl_add_flow },
-    { "mod-flow", 2, 3, dpctl_mod_flow },
+    { "add-flow", 2, 4, dpctl_add_flow },
+    { "mod-flow", 2, 4, dpctl_mod_flow },
     { "del-flow", 1, 2, dpctl_del_flow },
     { "del-flows", 0, 1, dpctl_del_flows },
     { "help", 0, INT_MAX, dpctl_help },
-- 
1.8.1.3
_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to