diff --git a/ofproto/ofproto-dpif-ipfix.c b/ofproto/ofproto-dpif-ipfix.c
index 13ff426..e7ce279 100644
--- a/ofproto/ofproto-dpif-ipfix.c
+++ b/ofproto/ofproto-dpif-ipfix.c
@@ -113,11 +113,12 @@ struct dpif_ipfix_global_stats {
 };
 
 struct dpif_ipfix_port {
-    struct hmap_node hmap_node; /* In struct dpif_ipfix's "tunnel_ports" hmap. */
+    struct hmap_node hmap_node; /* In struct dpif_ipfix's "ports" hmap. */
     struct ofport *ofport;      /* To retrieve port stats. */
     odp_port_t odp_port;
     enum dpif_ipfix_tunnel_type tunnel_type;
     uint8_t tunnel_key_length;
+    uint32_t ifindex;
 };
 
 struct dpif_ipfix_exporter {
@@ -155,9 +156,9 @@ struct dpif_ipfix_flow_exporter_map_node {
 struct dpif_ipfix {
     struct dpif_ipfix_bridge_exporter bridge_exporter;
     struct hmap flow_exporter_map;  /* dpif_ipfix_flow_exporter_map_node. */
-    struct hmap tunnel_ports;       /* Contains "struct dpif_ipfix_port"s.
-                                     * It makes tunnel port lookups faster in
-                                     * sampling upcalls. */
+    struct hmap ports;              /* Contains "struct dpif_ipfix_port"s.
+                                     * It makes port lookups faster in sampling
+                                     * upcalls. */
     struct ovs_refcount ref_cnt;
 };
 
@@ -291,7 +292,8 @@ BUILD_ASSERT_DECL(sizeof(struct ipfix_template_field_specifier) == 8);
 /* Cf. IETF RFC 5102 Section 5.11.6. */
 enum ipfix_flow_direction {
     INGRESS_FLOW = 0x00,
-    EGRESS_FLOW = 0x01
+    EGRESS_FLOW = 0x01,
+    NUM_IPFIX_FLOW_DIRECTION
 };
 
 /* Part of data record flow key for common metadata and Ethernet entities. */
@@ -306,6 +308,18 @@ struct ipfix_data_record_flow_key_common {
 });
 BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_flow_key_common) == 20);
 
+/* Part of data record flow key for interface information. Since some of the
+ * elements have variable length, members of this structure should be appended
+ * to the 'struct dp_packet' one by one. */
+struct ipfix_data_record_flow_key_iface {
+    ovs_be32 if_index;     /* (INGRESS|EGRESS)_INTERFACE */
+    ovs_be32 if_type;     /* (INGRESS|EGRESS)_INTERFACE_TYPE */
+    uint8_t if_name_len;   /* Variable length element: INTERFACE_NAME */
+    char *if_name;
+    uint8_t if_descr_len; /* Variable length element: INTERFACE_DESCRIPTION */
+    char *if_descr;
+};
+
 /* Part of data record flow key for VLAN entities. */
 OVS_PACKED(
 struct ipfix_data_record_flow_key_vlan {
@@ -735,7 +749,7 @@ dpif_ipfix_find_port(const struct dpif_ipfix *di,
     struct dpif_ipfix_port *dip;
 
     HMAP_FOR_EACH_IN_BUCKET (dip, hmap_node, hash_odp_port(odp_port),
-                             &di->tunnel_ports) {
+                             &di->ports) {
         if (dip->odp_port == odp_port) {
             return dip;
         }
@@ -744,82 +758,114 @@ dpif_ipfix_find_port(const struct dpif_ipfix *di,
 }
 
 static void
-dpif_ipfix_del_port(struct dpif_ipfix *di,
+dpif_ipfix_del_port__(struct dpif_ipfix *di,
                       struct dpif_ipfix_port *dip)
     OVS_REQUIRES(mutex)
 {
-    hmap_remove(&di->tunnel_ports, &dip->hmap_node);
+    hmap_remove(&di->ports, &dip->hmap_node);
     free(dip);
 }
 
+static enum dpif_ipfix_tunnel_type
+dpif_ipfix_tunnel_type(const struct ofport *ofport) {
+    const char *type = netdev_get_type(ofport->netdev);
+
+    if (type == NULL) {
+        return DPIF_IPFIX_TUNNEL_UNKNOWN;
+    }
+    if (strcmp(type, "gre") == 0) {
+        return DPIF_IPFIX_TUNNEL_GRE;
+    } else if (strcmp(type, "vxlan") == 0) {
+        return DPIF_IPFIX_TUNNEL_VXLAN;
+    } else if (strcmp(type, "lisp") == 0) {
+        return DPIF_IPFIX_TUNNEL_LISP;
+    } else if (strcmp(type, "geneve") == 0) {
+        return DPIF_IPFIX_TUNNEL_GENEVE;
+    } else if (strcmp(type, "stt") == 0) {
+        return DPIF_IPFIX_TUNNEL_STT;
+    }
+
+    return DPIF_IPFIX_TUNNEL_UNKNOWN;
+}
+
+static uint8_t
+dpif_ipfix_tunnel_key_length(enum dpif_ipfix_tunnel_type tunnel_type) {
+
+    switch (tunnel_type) {
+        case DPIF_IPFIX_TUNNEL_GRE:
+            /* 32-bit key gre */
+            return 4;
+        case DPIF_IPFIX_TUNNEL_VXLAN:
+        case DPIF_IPFIX_TUNNEL_LISP:
+        case DPIF_IPFIX_TUNNEL_GENEVE:
+            return 3;
+        case DPIF_IPFIX_TUNNEL_STT:
+            return 8;
+        case DPIF_IPFIX_TUNNEL_UNKNOWN:
+        case NUM_DPIF_IPFIX_TUNNEL:
+        default:
+            return 0;
+    }
+}
+
 void
-dpif_ipfix_add_tunnel_port(struct dpif_ipfix *di, struct ofport *ofport,
-                           odp_port_t odp_port) OVS_EXCLUDED(mutex)
+dpif_ipfix_add_port(struct dpif_ipfix *di, struct ofport *ofport,
+                    odp_port_t odp_port) OVS_EXCLUDED(mutex)
 {
     struct dpif_ipfix_port *dip;
-    const char *type;
+    int64_t ifindex;
 
     ovs_mutex_lock(&mutex);
     dip = dpif_ipfix_find_port(di, odp_port);
     if (dip) {
-        dpif_ipfix_del_port(di, dip);
+        dpif_ipfix_del_port__(di, dip);
     }
 
-    type = netdev_get_type(ofport->netdev);
-    if (type == NULL) {
-        goto out;
+    ifindex = netdev_get_ifindex(ofport->netdev);
+    if (ifindex < 0) {
+        ifindex = 0;
     }
 
-    /* Add to table of tunnel ports. */
+    /* Add to table of ports. */
     dip = xmalloc(sizeof *dip);
     dip->ofport = ofport;
     dip->odp_port = odp_port;
-    if (strcmp(type, "gre") == 0) {
-        /* 32-bit key gre */
-        dip->tunnel_type = DPIF_IPFIX_TUNNEL_GRE;
-        dip->tunnel_key_length = 4;
-    } else if (strcmp(type, "vxlan") == 0) {
-        dip->tunnel_type = DPIF_IPFIX_TUNNEL_VXLAN;
-        dip->tunnel_key_length = 3;
-    } else if (strcmp(type, "lisp") == 0) {
-        dip->tunnel_type = DPIF_IPFIX_TUNNEL_LISP;
-        dip->tunnel_key_length = 3;
-    } else if (strcmp(type, "geneve") == 0) {
-        dip->tunnel_type = DPIF_IPFIX_TUNNEL_GENEVE;
-        dip->tunnel_key_length = 3;
-    } else if (strcmp(type, "stt") == 0) {
-        dip->tunnel_type = DPIF_IPFIX_TUNNEL_STT;
-        dip->tunnel_key_length = 8;
-    } else {
-        free(dip);
-        goto out;
-    }
-    hmap_insert(&di->tunnel_ports, &dip->hmap_node, hash_odp_port(odp_port));
+    dip->tunnel_type = dpif_ipfix_tunnel_type(ofport);
+    dip->tunnel_key_length = dpif_ipfix_tunnel_key_length(dip->tunnel_type);
+    dip->ifindex = ifindex;
+    hmap_insert(&di->ports, &dip->hmap_node, hash_odp_port(odp_port));
 
-out:
     ovs_mutex_unlock(&mutex);
 }
 
 void
-dpif_ipfix_del_tunnel_port(struct dpif_ipfix *di, odp_port_t odp_port)
+dpif_ipfix_del_port(struct dpif_ipfix *di, odp_port_t odp_port)
     OVS_EXCLUDED(mutex)
 {
     struct dpif_ipfix_port *dip;
     ovs_mutex_lock(&mutex);
     dip = dpif_ipfix_find_port(di, odp_port);
     if (dip) {
-        dpif_ipfix_del_port(di, dip);
+        dpif_ipfix_del_port__(di, dip);
     }
     ovs_mutex_unlock(&mutex);
 }
 
+static struct dpif_ipfix_port *
+dpif_ipfix_find_tunnel_port(const struct dpif_ipfix *di, odp_port_t odp_port)
+    OVS_REQUIRES(mutex)
+{
+    struct dpif_ipfix_port *dip = dpif_ipfix_find_port(di, odp_port);
+    return (dip && dip->tunnel_type != DPIF_IPFIX_TUNNEL_UNKNOWN) ? dip : NULL;
+}
+
 bool
-dpif_ipfix_get_tunnel_port(const struct dpif_ipfix *di, odp_port_t odp_port)
+dpif_ipfix_is_tunnel_port(const struct dpif_ipfix *di, odp_port_t odp_port)
     OVS_EXCLUDED(mutex)
 {
     struct dpif_ipfix_port *dip;
     ovs_mutex_lock(&mutex);
-    dip = dpif_ipfix_find_port(di, odp_port);
+    dip = dpif_ipfix_find_tunnel_port(di, odp_port);
     ovs_mutex_unlock(&mutex);
     return dip != NULL;
 }
@@ -1055,7 +1101,7 @@ dpif_ipfix_create(void)
     di = xzalloc(sizeof *di);
     dpif_ipfix_bridge_exporter_init(&di->bridge_exporter);
     hmap_init(&di->flow_exporter_map);
-    hmap_init(&di->tunnel_ports);
+    hmap_init(&di->ports);
     ovs_refcount_init(&di->ref_cnt);
     return di;
 }
@@ -1149,8 +1195,8 @@ dpif_ipfix_clear(struct dpif_ipfix *di) OVS_REQUIRES(mutex)
         free(exp_node);
     }
 
-    HMAP_FOR_EACH_SAFE (dip, next, hmap_node, &di->tunnel_ports) {
-        dpif_ipfix_del_port(di, dip);
+    HMAP_FOR_EACH_SAFE (dip, next, hmap_node, &di->ports) {
+        dpif_ipfix_del_port__(di, dip);
     }
 }
 
@@ -1162,7 +1208,7 @@ dpif_ipfix_unref(struct dpif_ipfix *di) OVS_EXCLUDED(mutex)
         dpif_ipfix_clear(di);
         dpif_ipfix_bridge_exporter_destroy(&di->bridge_exporter);
         hmap_destroy(&di->flow_exporter_map);
-        hmap_destroy(&di->tunnel_ports);
+        hmap_destroy(&di->ports);
         free(di);
         ovs_mutex_unlock(&mutex);
     }
@@ -1201,13 +1247,15 @@ ipfix_send_msg(const struct collectors *collectors, struct dp_packet *msg)
 
 static uint16_t
 ipfix_get_template_id(enum ipfix_proto_l2 l2, enum ipfix_proto_l3 l3,
-                      enum ipfix_proto_l4 l4, enum ipfix_proto_tunnel tunnel)
+                      enum ipfix_proto_l4 l4, enum ipfix_proto_tunnel tunnel,
+                      enum ipfix_flow_direction flow_direction)
 {
     uint16_t template_id;
     template_id = l2;
     template_id = template_id * NUM_IPFIX_PROTO_L3 + l3;
     template_id = template_id * NUM_IPFIX_PROTO_L4 + l4;
     template_id = template_id * NUM_IPFIX_PROTO_TUNNEL + tunnel;
+    template_id = template_id * NUM_IPFIX_FLOW_DIRECTION + flow_direction;
     return IPFIX_TEMPLATE_ID_MIN + template_id;
 }
 
@@ -1219,7 +1267,8 @@ ipfix_get_options_template_id(enum ipfix_options_template opt_tmpl_type)
     uint16_t max_tmpl_id = ipfix_get_template_id(NUM_IPFIX_PROTO_L2,
                                                  NUM_IPFIX_PROTO_L3,
                                                  NUM_IPFIX_PROTO_L4,
-                                                 NUM_IPFIX_PROTO_TUNNEL);
+                                                 NUM_IPFIX_PROTO_TUNNEL,
+                                                 NUM_IPFIX_FLOW_DIRECTION);
 
     return max_tmpl_id + opt_tmpl_type;
 }
@@ -1315,7 +1364,9 @@ ipfix_def_options_template_fields(enum ipfix_options_template opt_tmpl_type,
 static uint16_t
 ipfix_define_template_fields(enum ipfix_proto_l2 l2, enum ipfix_proto_l3 l3,
                              enum ipfix_proto_l4 l4, enum ipfix_proto_tunnel tunnel,
-                             bool virtual_obs_id_set, size_t tmpl_hdr_offset,
+                             enum ipfix_flow_direction flow_direction,
+                             bool virtual_obs_id_set,
+                             size_t tmpl_hdr_offset,
                              struct dp_packet *msg)
 {
 
@@ -1333,6 +1384,19 @@ ipfix_define_template_fields(enum ipfix_proto_l2 l2, enum ipfix_proto_l3 l3,
     DEF(ETHERNET_TYPE);
     DEF(ETHERNET_HEADER_LENGTH);
 
+    /* Interface Information Elements */
+    DEF(INGRESS_INTERFACE);
+    DEF(INGRESS_INTERFACE_TYPE);
+    DEF(INTERFACE_NAME);
+    DEF(INTERFACE_DESCRIPTION);
+
+    if (flow_direction == EGRESS_FLOW) {
+        DEF(EGRESS_INTERFACE);
+        DEF(EGRESS_INTERFACE_TYPE);
+        DEF(INTERFACE_NAME);
+        DEF(INTERFACE_DESCRIPTION);
+    }
+
     if (l2 == IPFIX_PROTO_L2_VLAN) {
         DEF(VLAN_ID);
         DEF(DOT1Q_VLAN_ID);
@@ -1530,6 +1594,24 @@ ipfix_send_options_template_msgs(struct dpif_ipfix_exporter *exporter,
 }
 
 static void
+ipfix_add_template_record(enum ipfix_proto_l2 l2, enum ipfix_proto_l3 l3,
+                          enum ipfix_proto_l4 l4,
+                          enum ipfix_proto_tunnel tunnel,
+                          enum ipfix_flow_direction flow_direction,
+                          bool virtual_obs_id_set,
+                          struct dp_packet *msg)
+{
+    struct ipfix_template_record_header *tmpl_hdr;
+    size_t tmpl_hdr_offset = dp_packet_size(msg);
+
+    tmpl_hdr = dp_packet_put_zeros(msg, sizeof *tmpl_hdr);
+    tmpl_hdr->template_id =
+        htons(ipfix_get_template_id(l2, l3, l4, tunnel, flow_direction));
+    ipfix_define_template_fields(l2, l3, l4, tunnel, flow_direction,
+                                 virtual_obs_id_set, tmpl_hdr_offset, msg);
+}
+
+static void
 ipfix_send_template_msgs(struct dpif_ipfix_exporter *exporter,
                          uint32_t export_time_sec, uint32_t obs_domain_id)
 {
@@ -1537,14 +1619,14 @@ ipfix_send_template_msgs(struct dpif_ipfix_exporter *exporter,
     struct dp_packet msg;
     dp_packet_use_stub(&msg, msg_stub, sizeof msg_stub);
 
-    size_t set_hdr_offset, tmpl_hdr_offset, error_pkts;
-    struct ipfix_template_record_header *tmpl_hdr;
+    size_t set_hdr_offset, error_pkts;
     size_t tx_packets = 0;
     size_t tx_errors = 0;
     enum ipfix_proto_l2 l2;
     enum ipfix_proto_l3 l3;
     enum ipfix_proto_l4 l4;
     enum ipfix_proto_tunnel tunnel;
+    enum ipfix_flow_direction flow_direction;
 
     ipfix_init_template_msg(export_time_sec, exporter->seq_number,
                             obs_domain_id, IPFIX_SET_ID_TEMPLATE, &msg,
@@ -1559,41 +1641,44 @@ ipfix_send_template_msgs(struct dpif_ipfix_exporter *exporter,
                     continue;
                 }
                 for (tunnel = 0; tunnel < NUM_IPFIX_PROTO_TUNNEL; tunnel++) {
-                    /* When the size of the template packet reaches
-                     * MAX_MESSAGE_LEN(1024), send it out.
-                     * And then reinitialize the msg to construct a new
-                     * packet for the following templates.
-                     */
-                    if (dp_packet_size(&msg) >= MAX_MESSAGE_LEN) {
-                        /* Send template message. */
-                        error_pkts = ipfix_send_template_msg(exporter->collectors,
-                                                             &msg, set_hdr_offset);
-                        tx_errors += error_pkts;
-                        tx_packets += collectors_count(exporter->collectors) - error_pkts;
-
-                        /* Reinitialize the template msg. */
-                        ipfix_init_template_msg(export_time_sec,
-                                                exporter->seq_number,
-                                                obs_domain_id,
-                                                IPFIX_SET_ID_TEMPLATE,
-                                                &msg,
-                                                &set_hdr_offset);
+                    for (flow_direction = 0;
+                         flow_direction < NUM_IPFIX_FLOW_DIRECTION;
+                         flow_direction++) {
+                        /* When the size of the template packet reaches
+                         * MAX_MESSAGE_LEN(1024), send it out.
+                         * And then reinitialize the msg to construct a new
+                         * packet for the following templates.
+                         */
+                        if (dp_packet_size(&msg) >= MAX_MESSAGE_LEN) {
+                            /* Send template message. */
+                            error_pkts =
+                                ipfix_send_template_msg(exporter->collectors,
+                                                        &msg, set_hdr_offset);
+                            tx_errors += error_pkts;
+                            tx_packets +=
+                                collectors_count(exporter->collectors)
+                                - error_pkts;
+
+                            /* Reinitialize the template msg. */
+                            ipfix_init_template_msg(export_time_sec,
+                                                    exporter->seq_number,
+                                                    obs_domain_id,
+                                                    IPFIX_SET_ID_TEMPLATE,
+                                                    &msg, &set_hdr_offset);
+                        }
+
+                        ipfix_add_template_record(l2, l3, l4, tunnel,
+                                flow_direction,
+                                exporter->virtual_obs_id != NULL, &msg);
                     }
-
-                    tmpl_hdr_offset = dp_packet_size(&msg);
-                    tmpl_hdr = dp_packet_put_zeros(&msg, sizeof *tmpl_hdr);
-                    tmpl_hdr->template_id = htons(
-                        ipfix_get_template_id(l2, l3, l4, tunnel));
-                    ipfix_define_template_fields(
-                        l2, l3, l4, tunnel, exporter->virtual_obs_id != NULL,
-                        tmpl_hdr_offset, &msg);
                 }
             }
         }
     }
 
     /* Send template message. */
-    error_pkts = ipfix_send_template_msg(exporter->collectors, &msg, set_hdr_offset);
+    error_pkts = ipfix_send_template_msg(exporter->collectors, &msg,
+                                         set_hdr_offset);
     tx_errors += error_pkts;
     tx_packets += collectors_count(exporter->collectors) - error_pkts;
 
@@ -1878,8 +1963,80 @@ ipfix_cache_update(struct dpif_ipfix_exporter *exporter,
     }
 }
 
+static void
+ipfix_destroy_iface_data_record(struct ipfix_data_record_flow_key_iface *data)
+{
+    free(data->if_descr);
+    free(data->if_name);
+}
+
+/* Fills '*data' structure based on port number 'port_no'.  Caller must destroy
+ * 'data' with ipfix_destroy_iface_data_record(). */
+static int
+ipfix_get_iface_data_record(const struct dpif_ipfix *di, odp_port_t port_no,
+                            struct ipfix_data_record_flow_key_iface *data)
+    OVS_REQUIRES(mutex)
+{
+    struct dpif_ipfix_port *port;
+    struct smap netdev_status;
+
+    port = dpif_ipfix_find_port(di, port_no);
+    if (!port) {
+        *data = (struct ipfix_data_record_flow_key_iface) {0};
+        return -1;
+    }
+
+    smap_init(&netdev_status);
+    if (!netdev_get_status(port->ofport->netdev, &netdev_status)) {
+        data->if_type = htonl(smap_get_ullong(&netdev_status, "if_type", 0));
+        data->if_descr = nullable_xstrdup(smap_get(&netdev_status,
+                                                   "if_descr"));
+    } else {
+        data->if_type = 0;
+        data->if_descr = NULL;
+    }
+
+    smap_destroy(&netdev_status);
+    data->if_index = htonl(port->ifindex);
+    data->if_descr_len = data->if_descr ? strlen(data->if_descr) : 0;
+    data->if_name = nullable_xstrdup(netdev_get_name(port->ofport->netdev));
+    data->if_name_len = data->if_name ? strlen(data->if_name) : 0;
+
+    return 0;
+}
+
+static void
+ipfix_put_iface_data_record(const struct dpif_ipfix *di, odp_port_t port_no,
+                            struct dp_packet *msg)
+    OVS_REQUIRES(mutex)
+{
+    struct ipfix_data_record_flow_key_iface data;
+    int err;
+
+    err = ipfix_get_iface_data_record(di, port_no, &data);
+    if (err == 0) {
+        dp_packet_put(msg, &data.if_index, sizeof data.if_index);
+        dp_packet_put(msg, &data.if_type, sizeof data.if_type);
+        dp_packet_put(msg, &data.if_name_len, sizeof data.if_name_len);
+        if (data.if_name_len) {
+            dp_packet_put(msg, data.if_name, data.if_name_len);
+        }
+        dp_packet_put(msg, &data.if_descr_len, sizeof data.if_descr_len);
+        if (data.if_descr_len) {
+            dp_packet_put(msg, data.if_descr, data.if_descr_len);
+        }
+        ipfix_destroy_iface_data_record(&data);
+    } else {
+        dp_packet_put_zeros(msg, sizeof data.if_index);
+        dp_packet_put_zeros(msg, sizeof data.if_type);
+        dp_packet_put_zeros(msg, sizeof data.if_name_len);
+        dp_packet_put_zeros(msg, sizeof data.if_descr_len);
+    }
+}
+
 static enum ipfix_sampled_packet_type
-ipfix_cache_entry_init(struct ipfix_flow_cache_entry *entry,
+ipfix_cache_entry_init(const struct dpif_ipfix *di,
+                       struct ipfix_flow_cache_entry *entry,
                        const struct dp_packet *packet, const struct flow *flow,
                        uint64_t packet_delta_count, uint32_t obs_domain_id,
                        uint32_t obs_point_id, odp_port_t output_odp_port,
@@ -1887,6 +2044,7 @@ ipfix_cache_entry_init(struct ipfix_flow_cache_entry *entry,
                        const struct dpif_ipfix_port *tunnel_port,
                        const struct flow_tnl *tunnel_key,
                        struct dpif_ipfix_global_stats *stats)
+    OVS_REQUIRES(mutex)
 {
     struct ipfix_flow_key *flow_key;
     struct dp_packet msg;
@@ -1961,8 +2119,14 @@ ipfix_cache_entry_init(struct ipfix_flow_cache_entry *entry,
        tunnel = IPFIX_PROTO_TUNNELED;
     }
 
+    uint8_t flow_direction =
+        (direction == NX_ACTION_SAMPLE_INGRESS ? INGRESS_FLOW
+         : direction == NX_ACTION_SAMPLE_EGRESS ? EGRESS_FLOW
+         : output_odp_port == ODPP_NONE ? INGRESS_FLOW : EGRESS_FLOW);
+
     flow_key->obs_domain_id = obs_domain_id;
-    flow_key->template_id = ipfix_get_template_id(l2, l3, l4, tunnel);
+    flow_key->template_id = ipfix_get_template_id(l2, l3, l4, tunnel,
+                                                  flow_direction);
 
     /* The fields defined in the ipfix_data_record_* structs and sent
      * below must match exactly the templates defined in
@@ -1972,11 +2136,6 @@ ipfix_cache_entry_init(struct ipfix_flow_cache_entry *entry,
         ? VLAN_ETH_HEADER_LEN : ETH_HEADER_LEN;
     ethernet_total_length = dp_packet_size(packet);
 
-    uint8_t flow_direction =
-        (direction == NX_ACTION_SAMPLE_INGRESS ? INGRESS_FLOW
-         : direction == NX_ACTION_SAMPLE_EGRESS ? EGRESS_FLOW
-         : output_odp_port == ODPP_NONE ? INGRESS_FLOW : EGRESS_FLOW);
-
     /* Common Ethernet entities. */
     {
         struct ipfix_data_record_flow_key_common *data_common;
@@ -1990,6 +2149,13 @@ ipfix_cache_entry_init(struct ipfix_flow_cache_entry *entry,
         data_common->ethernet_header_length = ethernet_header_length;
     }
 
+    /* Interface Information Elements */
+    ipfix_put_iface_data_record(di, flow->in_port.odp_port, &msg);
+
+    if (flow_direction == EGRESS_FLOW) {
+        ipfix_put_iface_data_record(di, output_odp_port, &msg);
+    }
+
     if (l2 == IPFIX_PROTO_L2_VLAN) {
         struct ipfix_data_record_flow_key_vlan *data_vlan;
         uint16_t vlan_id = vlan_tci_to_vid(flow->vlans[0].tci);
@@ -2414,13 +2580,15 @@ ipfix_send_data_msg(struct dpif_ipfix_exporter *exporter,
 }
 
 static void
-dpif_ipfix_sample(struct dpif_ipfix_exporter *exporter,
+dpif_ipfix_sample(const struct dpif_ipfix *di,
+                  struct dpif_ipfix_exporter *exporter,
                   const struct dp_packet *packet, const struct flow *flow,
                   uint64_t packet_delta_count, uint32_t obs_domain_id,
                   uint32_t obs_point_id, odp_port_t output_odp_port,
                   enum nx_action_sample_direction direction,
                   const struct dpif_ipfix_port *tunnel_port,
                   const struct flow_tnl *tunnel_key)
+    OVS_REQUIRES(mutex)
 {
     struct ipfix_flow_cache_entry *entry;
     enum ipfix_sampled_packet_type sampled_packet_type;
@@ -2428,7 +2596,7 @@ dpif_ipfix_sample(struct dpif_ipfix_exporter *exporter,
     /* Create a flow cache entry from the sample. */
     entry = xmalloc(sizeof *entry);
     sampled_packet_type =
-            ipfix_cache_entry_init(entry, packet,
+            ipfix_cache_entry_init(di, entry, packet,
                                    flow, packet_delta_count,
                                    obs_domain_id, obs_point_id,
                                    output_odp_port, direction,
@@ -2484,16 +2652,16 @@ dpif_ipfix_bridge_sample(struct dpif_ipfix *di, const struct dp_packet *packet,
         if (output_odp_port == ODPP_NONE && flow->tunnel.ip_dst) {
             /* Input tunnel. */
             tunnel_key = &flow->tunnel;
-            tunnel_port = dpif_ipfix_find_port(di, input_odp_port);
+            tunnel_port = dpif_ipfix_find_tunnel_port(di, input_odp_port);
         }
         if (output_odp_port != ODPP_NONE && output_tunnel_key) {
             /* Output tunnel, output_tunnel_key must be valid. */
             tunnel_key = output_tunnel_key;
-            tunnel_port = dpif_ipfix_find_port(di, output_odp_port);
+            tunnel_port = dpif_ipfix_find_tunnel_port(di, output_odp_port);
         }
     }
 
-    dpif_ipfix_sample(&di->bridge_exporter.exporter, packet, flow,
+    dpif_ipfix_sample(di, &di->bridge_exporter.exporter, packet, flow,
                       packet_delta_count,
                       di->bridge_exporter.options->obs_domain_id,
                       di->bridge_exporter.options->obs_point_id,
@@ -2528,16 +2696,16 @@ dpif_ipfix_flow_sample(struct dpif_ipfix *di, const struct dp_packet *packet,
             if (output_odp_port == ODPP_NONE && flow->tunnel.ip_dst) {
                 /* Input tunnel. */
                 tunnel_key = &flow->tunnel;
-                tunnel_port = dpif_ipfix_find_port(di, input_odp_port);
+                tunnel_port = dpif_ipfix_find_tunnel_port(di, input_odp_port);
             }
             if (output_odp_port != ODPP_NONE && output_tunnel_key) {
                 /* Output tunnel, output_tunnel_key must be valid. */
                 tunnel_key = output_tunnel_key;
-                tunnel_port = dpif_ipfix_find_port(di, output_odp_port);
+                tunnel_port = dpif_ipfix_find_tunnel_port(di, output_odp_port);
             }
         }
 
-        dpif_ipfix_sample(&node->exporter.exporter, packet, flow,
+        dpif_ipfix_sample(di, &node->exporter.exporter, packet, flow,
                           packet_delta_count,
                           cookie->flow_sample.obs_domain_id,
                           cookie->flow_sample.obs_point_id,
diff --git a/ofproto/ofproto-dpif-ipfix.h b/ofproto/ofproto-dpif-ipfix.h
index 0808ede..0b21441 100644
--- a/ofproto/ofproto-dpif-ipfix.h
+++ b/ofproto/ofproto-dpif-ipfix.h
@@ -33,8 +33,8 @@ struct dpif_ipfix *dpif_ipfix_create(void);
 struct dpif_ipfix *dpif_ipfix_ref(const struct dpif_ipfix *);
 void dpif_ipfix_unref(struct dpif_ipfix *);
 
-void dpif_ipfix_add_tunnel_port(struct dpif_ipfix *, struct ofport *, odp_port_t);
-void dpif_ipfix_del_tunnel_port(struct dpif_ipfix *, odp_port_t);
+void dpif_ipfix_add_port(struct dpif_ipfix *, struct ofport *, odp_port_t);
+void dpif_ipfix_del_port(struct dpif_ipfix *, odp_port_t);
 
 uint32_t dpif_ipfix_get_bridge_exporter_probability(const struct dpif_ipfix *);
 bool dpif_ipfix_get_bridge_exporter_tunnel_sampling(const struct dpif_ipfix *);
@@ -42,7 +42,7 @@ bool dpif_ipfix_get_bridge_exporter_input_sampling(const struct dpif_ipfix *);
 bool dpif_ipfix_get_bridge_exporter_output_sampling(const struct dpif_ipfix *);
 bool dpif_ipfix_get_flow_exporter_tunnel_sampling(const struct dpif_ipfix *,
                                                   const uint32_t);
-bool dpif_ipfix_get_tunnel_port(const struct dpif_ipfix *, odp_port_t);
+bool dpif_ipfix_is_tunnel_port(const struct dpif_ipfix *, odp_port_t);
 void dpif_ipfix_set_options(
     struct dpif_ipfix *,
     const struct ofproto_ipfix_bridge_exporter_options *,
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 7f7adb2..3a37559 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -2946,7 +2946,7 @@ compose_ipfix_action(struct xlate_ctx *ctx, odp_port_t output_odp_port)
          * OVS_USERSPACE_ATTR_TUNNEL_OUT_PORT
          */
         if (dpif_ipfix_get_bridge_exporter_tunnel_sampling(ipfix) &&
-            dpif_ipfix_get_tunnel_port(ipfix, output_odp_port) ) {
+            dpif_ipfix_is_tunnel_port(ipfix, output_odp_port) ) {
            tunnel_out_port = output_odp_port;
         }
     }
@@ -5199,7 +5199,7 @@ xlate_sample_action(struct xlate_ctx *ctx,
 
         if (dpif_ipfix_get_flow_exporter_tunnel_sampling(ipfix,
                                                          os->collector_set_id)
-            && dpif_ipfix_get_tunnel_port(ipfix, output_odp_port)) {
+            && dpif_ipfix_is_tunnel_port(ipfix, output_odp_port)) {
             tunnel_out_port = output_odp_port;
             emit_set_tunnel = true;
         }
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index 50f440f..72a4334 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -1865,9 +1865,6 @@ port_construct(struct ofport *port_)
         }
 
         port->is_tunnel = true;
-        if (ofproto->ipfix) {
-           dpif_ipfix_add_tunnel_port(ofproto->ipfix, port_, port->odp_port);
-        }
     } else {
         /* Sanity-check that a mapping doesn't already exist.  This
          * shouldn't happen for non-tunnel ports. */
@@ -1888,6 +1885,9 @@ port_construct(struct ofport *port_)
     if (ofproto->sflow) {
         dpif_sflow_add_port(ofproto->sflow, port_, port->odp_port);
     }
+    if (ofproto->ipfix) {
+       dpif_ipfix_add_port(ofproto->ipfix, port_, port->odp_port);
+    }
 
     return 0;
 }
@@ -1933,10 +1933,6 @@ port_destruct(struct ofport *port_, bool del)
         atomic_count_dec(&ofproto->backer->tnl_count);
     }
 
-    if (port->is_tunnel && ofproto->ipfix) {
-       dpif_ipfix_del_tunnel_port(ofproto->ipfix, port->odp_port);
-    }
-
     tnl_port_del(port);
     sset_find_and_delete(&ofproto->ports, devname);
     sset_find_and_delete(&ofproto->ghost_ports, devname);
@@ -1951,6 +1947,9 @@ port_destruct(struct ofport *port_, bool del)
     if (ofproto->sflow) {
         dpif_sflow_del_port(ofproto->sflow, port->odp_port);
     }
+    if (ofproto->ipfix) {
+       dpif_ipfix_del_port(ofproto->ipfix, port->odp_port);
+    }
 
     free(port->qdscp);
 }
@@ -2076,13 +2075,11 @@ set_ipfix(
             di, bridge_exporter_options, flow_exporters_options,
             n_flow_exporters_options);
 
-        /* Add tunnel ports only when a new ipfix created */
+        /* Add ports only when a new ipfix created */
         if (new_di == true) {
             struct ofport_dpif *ofport;
             HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
-                if (ofport->is_tunnel == true) {
-                    dpif_ipfix_add_tunnel_port(di, &ofport->up, ofport->odp_port);
-                }
+                dpif_ipfix_add_port(di, &ofport->up, ofport->odp_port);
             }
         }
 
