[ovs-dev,2/2] ofproto-dpif-ipfix: add interface Information Elements to flow key

Message ID 1501062231-123197-2-git-send-email-przemyslawx.szczerbik@intel.com
State New
Headers show

Commit Message

Przemyslaw Szczerbik July 26, 2017, 9:43 a.m.
Extend flow key part of data record to include following Information Elements:
- ingressInterface
- ingressInterfaceType
- egressInterface
- egressInterfaceType
- interfaceName
- interfaceDescription

In case of input sampling we don't have information about egress port.
Define templates depending not only on protocol types, but also on flow
direction. Only egress flow will include egress information elements.

With this change, dpif_ipfix_exporter stores every port in hmap rather
than only tunnel ports. It allows to easily retrieve required
information about interfaces during sampling upcalls.

Signed-off-by: Przemyslaw Szczerbik <przemyslawx.szczerbik@intel.com>
---
v1->v2
* Add interfaceType and interfaceDescription
* Rework ipfix_get_iface_data_record function

 ofproto/ofproto-dpif-ipfix.c | 356 +++++++++++++++++++++++++++++++------------
 ofproto/ofproto-dpif-ipfix.h |   6 +-
 ofproto/ofproto-dpif-xlate.c |   4 +-
 ofproto/ofproto-dpif.c       |  19 +--
 4 files changed, 275 insertions(+), 110 deletions(-)

Comments

Przemyslaw Szczerbik July 26, 2017, 11 a.m. | #1
Hi,

This patch was supposed to be v2, but I forgot to mention that in the subject.
Previous version: https://patchwork.ozlabs.org/patch/792730/

Let me know if you want me to re-sent it with a proper version.

Regards,
Przemek

> -----Original Message-----
> From: Szczerbik, PrzemyslawX
> Sent: Wednesday, July 26, 2017 10:44 AM
> To: dev@openvswitch.org
> Cc: Szczerbik, PrzemyslawX <przemyslawx.szczerbik@intel.com>
> Subject: [PATCH 2/2] ofproto-dpif-ipfix: add interface Information Elements to
> flow key
> 
> Extend flow key part of data record to include following Information Elements:
> - ingressInterface
> - ingressInterfaceType
> - egressInterface
> - egressInterfaceType
> - interfaceName
> - interfaceDescription
> 
> In case of input sampling we don't have information about egress port.
> Define templates depending not only on protocol types, but also on flow
> direction. Only egress flow will include egress information elements.
> 
> With this change, dpif_ipfix_exporter stores every port in hmap rather
> than only tunnel ports. It allows to easily retrieve required
> information about interfaces during sampling upcalls.
> 
> Signed-off-by: Przemyslaw Szczerbik <przemyslawx.szczerbik@intel.com>
> ---
> v1->v2
> * Add interfaceType and interfaceDescription
> * Rework ipfix_get_iface_data_record function
> 
>  ofproto/ofproto-dpif-ipfix.c | 356 +++++++++++++++++++++++++++++++-------
> -----
>  ofproto/ofproto-dpif-ipfix.h |   6 +-
>  ofproto/ofproto-dpif-xlate.c |   4 +-
>  ofproto/ofproto-dpif.c       |  19 +--
>  4 files changed, 275 insertions(+), 110 deletions(-)
> 
> 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);
>              }
>          }
> 
> --
> 1.8.3.1

--------------------------------------------------------------
Intel Research and Development Ireland Limited
Registered in Ireland
Registered Office: Collinstown Industrial Park, Leixlip, County Kildare
Registered Number: 308263


This e-mail and any attachments may contain confidential material for the sole
use of the intended recipient(s). Any review or distribution by others is
strictly prohibited. If you are not the intended recipient, please contact the
sender and delete all copies.
Przemyslaw Szczerbik Aug. 16, 2017, 8:54 a.m. | #2
Hi,

I haven't received any feedback on this patch for quite some time.
Is there anything that I can do to expedite review process?

Regards,
Przemek

> -----Original Message-----
> From: ovs-dev-bounces@openvswitch.org [mailto:ovs-dev-
> bounces@openvswitch.org] On Behalf Of Szczerbik, PrzemyslawX
> Sent: Wednesday, July 26, 2017 12:01 PM
> To: dev@openvswitch.org
> Subject: Re: [ovs-dev] [PATCH 2/2] ofproto-dpif-ipfix: add interface Information
> Elements to flow key
> 
> Hi,
> 
> This patch was supposed to be v2, but I forgot to mention that in the subject.
> Previous version: https://patchwork.ozlabs.org/patch/792730/
> 
> Let me know if you want me to re-sent it with a proper version.
> 
> Regards,
> Przemek
> 
> > -----Original Message-----
> > From: Szczerbik, PrzemyslawX
> > Sent: Wednesday, July 26, 2017 10:44 AM
> > To: dev@openvswitch.org
> > Cc: Szczerbik, PrzemyslawX <przemyslawx.szczerbik@intel.com>
> > Subject: [PATCH 2/2] ofproto-dpif-ipfix: add interface Information Elements to
> > flow key
> >
> > Extend flow key part of data record to include following Information Elements:
> > - ingressInterface
> > - ingressInterfaceType
> > - egressInterface
> > - egressInterfaceType
> > - interfaceName
> > - interfaceDescription
> >
> > In case of input sampling we don't have information about egress port.
> > Define templates depending not only on protocol types, but also on flow
> > direction. Only egress flow will include egress information elements.
> >
> > With this change, dpif_ipfix_exporter stores every port in hmap rather
> > than only tunnel ports. It allows to easily retrieve required
> > information about interfaces during sampling upcalls.
> >
> > Signed-off-by: Przemyslaw Szczerbik <przemyslawx.szczerbik@intel.com>
> > ---
> > v1->v2
> > * Add interfaceType and interfaceDescription
> > * Rework ipfix_get_iface_data_record function
> >
> >  ofproto/ofproto-dpif-ipfix.c | 356 +++++++++++++++++++++++++++++++-----
> --
> > -----
> >  ofproto/ofproto-dpif-ipfix.h |   6 +-
> >  ofproto/ofproto-dpif-xlate.c |   4 +-
> >  ofproto/ofproto-dpif.c       |  19 +--
> >  4 files changed, 275 insertions(+), 110 deletions(-)
> >
> > 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);
> >              }
> >          }
> >
> > --
> > 1.8.3.1
> 
> --------------------------------------------------------------
> Intel Research and Development Ireland Limited
> Registered in Ireland
> Registered Office: Collinstown Industrial Park, Leixlip, County Kildare
> Registered Number: 308263
> 
> 
> This e-mail and any attachments may contain confidential material for the sole
> use of the intended recipient(s). Any review or distribution by others is
> strictly prohibited. If you are not the intended recipient, please contact the
> sender and delete all copies.
> 
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
--------------------------------------------------------------
Intel Research and Development Ireland Limited
Registered in Ireland
Registered Office: Collinstown Industrial Park, Leixlip, County Kildare
Registered Number: 308263


This e-mail and any attachments may contain confidential material for the sole
use of the intended recipient(s). Any review or distribution by others is
strictly prohibited. If you are not the intended recipient, please contact the
sender and delete all copies.
Greg Rose Aug. 18, 2017, 12:48 a.m. | #3
On 08/16/2017 01:54 AM, Szczerbik, PrzemyslawX wrote:
> Hi,
>
> I haven't received any feedback on this patch for quite some time.
> Is there anything that I can do to expedite review process?
>
> Regards,
> Przemek
>
>
[snip]

I'll have a look at it over the next few days and see if I can provide some feedback.  I was investigating other issues with ipfix some time back but got pulled into some other duties.  Hopefully I can get it working and provide some feedback.

Thanks,

- Greg
Greg Rose Aug. 18, 2017, 10:51 p.m. | #4
On 08/16/2017 01:54 AM, Szczerbik, PrzemyslawX wrote:
> Hi,
>
> I haven't received any feedback on this patch for quite some time.
> Is there anything that I can do to expedite review process?
>
> Regards,
> Przemek
>

Przemek,

I'm in the process of looking into this patch but I'm running into an issue with the vswitch not sending flows to the ntopng/nprobe collector I have set up.  I see this in the vswitchd log:

2017-08-18T21:48:57.917Z|00058|collectors|WARN|couldn't open connection to collector 192.168.0.21:2055 (Network is unreachable)
2017-08-18T21:48:57.917Z|00059|ipfix|WARN|no collectors could be initialized, IPFIX exporter disabled

However, 192.168.0.21 is reachable, at least from the br0 bridge that has the IPFIX flows enabled.

ntopng/nprobe on the collector machine has the right ports open:

netstat -tulpen | grep 2055
udp        0      0 0.0.0.0:2055            0.0.0.0:*                           99         27671      3038/nprobe
udp6       0      0 :::2055                 :::*                                99         27672      3038/nprobe

netstat -tulpen | grep 5556
tcp        0      0 0.0.0.0:5556            0.0.0.0:*               LISTEN      0          27666      3038/nprobe

I'm not sure exactly what the problem is but I'm debugging that.  Until I can get past this issue I can't really test and review your patch.  I am actively working on getting this issue fixed though.

Regards,

- Greg
> > -----Original Message-----
> > From: ovs-dev-bounces@openvswitch.org [mailto:ovs-dev-
> > bounces@openvswitch.org] On Behalf Of Szczerbik, PrzemyslawX
> > Sent: Wednesday, July 26, 2017 12:01 PM
> > To: dev@openvswitch.org
> > Subject: Re: [ovs-dev] [PATCH 2/2] ofproto-dpif-ipfix: add interface Information
> > Elements to flow key
> >
> > Hi,
> >
> > This patch was supposed to be v2, but I forgot to mention that in the subject.
> > Previous version: https://patchwork.ozlabs.org/patch/792730/
> >
> > Let me know if you want me to re-sent it with a proper version.
> >
> > Regards,
> > Przemek
> >
> >> -----Original Message-----
> >> From: Szczerbik, PrzemyslawX
> >> Sent: Wednesday, July 26, 2017 10:44 AM
> >> To: dev@openvswitch.org
> >> Cc: Szczerbik, PrzemyslawX <przemyslawx.szczerbik@intel.com>
> >> Subject: [PATCH 2/2] ofproto-dpif-ipfix: add interface Information Elements to
> >> flow key
> >>
> >> Extend flow key part of data record to include following Information Elements:
> >> - ingressInterface
> >> - ingressInterfaceType
> >> - egressInterface
> >> - egressInterfaceType
> >> - interfaceName
> >> - interfaceDescription
> >>
> >> In case of input sampling we don't have information about egress port.
> >> Define templates depending not only on protocol types, but also on flow
> >> direction. Only egress flow will include egress information elements.
> >>
> >> With this change, dpif_ipfix_exporter stores every port in hmap rather
> >> than only tunnel ports. It allows to easily retrieve required
> >> information about interfaces during sampling upcalls.
> >>
> >> Signed-off-by: Przemyslaw Szczerbik <przemyslawx.szczerbik@intel.com>
> >> ---
> >> v1->v2
> >> * Add interfaceType and interfaceDescription
> >> * Rework ipfix_get_iface_data_record function
> >>
> >>   ofproto/ofproto-dpif-ipfix.c | 356 +++++++++++++++++++++++++++++++-----
> > --
> >> -----
> >>   ofproto/ofproto-dpif-ipfix.h |   6 +-
> >>   ofproto/ofproto-dpif-xlate.c |   4 +-
> >>   ofproto/ofproto-dpif.c       |  19 +--
> >>   4 files changed, 275 insertions(+), 110 deletions(-)
> >>
> >> 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);
> >>               }
> >>           }
> >>
> >> --
> >> 1.8.3.1
> >
> > --------------------------------------------------------------
> > Intel Research and Development Ireland Limited
> > Registered in Ireland
> > Registered Office: Collinstown Industrial Park, Leixlip, County Kildare
> > Registered Number: 308263
> >
> >
> > This e-mail and any attachments may contain confidential material for the sole
> > use of the intended recipient(s). Any review or distribution by others is
> > strictly prohibited. If you are not the intended recipient, please contact the
> > sender and delete all copies.
> >
> > _______________________________________________
> > dev mailing list
> > dev@openvswitch.org
> > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> --------------------------------------------------------------
> Intel Research and Development Ireland Limited
> Registered in Ireland
> Registered Office: Collinstown Industrial Park, Leixlip, County Kildare
> Registered Number: 308263
>
>
> This e-mail and any attachments may contain confidential material for the sole
> use of the intended recipient(s). Any review or distribution by others is
> strictly prohibited. If you are not the intended recipient, please contact the
> sender and delete all copies.
>
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
Weglicki, MichalX Aug. 29, 2017, 11:48 a.m. | #5
Hello Greg, 

Unfortunately Przemek is not available, but I'm more or less familiar with this patch. 

Did you manage to fix this issue? I think that your problems lies on connection level, which wasn't modified by this patch. Patch added additional counters on reporting level, and based on your description, ovs just can't connect to collector. 

Br, 
Michal. 

> -----Original Message-----
> From: ovs-dev-bounces@openvswitch.org [mailto:ovs-dev-bounces@openvswitch.org] On Behalf Of Greg Rose
> Sent: Saturday, August 19, 2017 12:51 AM
> To: Szczerbik, PrzemyslawX <przemyslawx.szczerbik@intel.com>
> Cc: dev@openvswitch.org
> Subject: Re: [ovs-dev] [PATCH 2/2] ofproto-dpif-ipfix: add interface Information Elements to flow key
> 
> On 08/16/2017 01:54 AM, Szczerbik, PrzemyslawX wrote:
> > Hi,
> >
> > I haven't received any feedback on this patch for quite some time.
> > Is there anything that I can do to expedite review process?
> >
> > Regards,
> > Przemek
> >
> 
> Przemek,
> 
> I'm in the process of looking into this patch but I'm running into an issue with the vswitch not sending flows to the ntopng/nprobe
> collector I have set up.  I see this in the vswitchd log:
> 
> 2017-08-18T21:48:57.917Z|00058|collectors|WARN|couldn't open connection to collector 192.168.0.21:2055 (Network is
> unreachable)
> 2017-08-18T21:48:57.917Z|00059|ipfix|WARN|no collectors could be initialized, IPFIX exporter disabled
> 
> However, 192.168.0.21 is reachable, at least from the br0 bridge that has the IPFIX flows enabled.
> 
> ntopng/nprobe on the collector machine has the right ports open:
> 
> netstat -tulpen | grep 2055
> udp        0      0 0.0.0.0:2055            0.0.0.0:*                           99         27671      3038/nprobe
> udp6       0      0 :::2055                 :::*                                99         27672      3038/nprobe
> 
> netstat -tulpen | grep 5556
> tcp        0      0 0.0.0.0:5556            0.0.0.0:*               LISTEN      0          27666      3038/nprobe
> 
> I'm not sure exactly what the problem is but I'm debugging that.  Until I can get past this issue I can't really test and review your
> patch.  I am actively working on getting this issue fixed though.
> 
> Regards,
> 
> - Greg
> > > -----Original Message-----
> > > From: ovs-dev-bounces@openvswitch.org [mailto:ovs-dev-
> > > bounces@openvswitch.org] On Behalf Of Szczerbik, PrzemyslawX
> > > Sent: Wednesday, July 26, 2017 12:01 PM
> > > To: dev@openvswitch.org
> > > Subject: Re: [ovs-dev] [PATCH 2/2] ofproto-dpif-ipfix: add interface Information
> > > Elements to flow key
> > >
> > > Hi,
> > >
> > > This patch was supposed to be v2, but I forgot to mention that in the subject.
> > > Previous version: https://patchwork.ozlabs.org/patch/792730/
> > >
> > > Let me know if you want me to re-sent it with a proper version.
> > >
> > > Regards,
> > > Przemek
> > >
> > >> -----Original Message-----
> > >> From: Szczerbik, PrzemyslawX
> > >> Sent: Wednesday, July 26, 2017 10:44 AM
> > >> To: dev@openvswitch.org
> > >> Cc: Szczerbik, PrzemyslawX <przemyslawx.szczerbik@intel.com>
> > >> Subject: [PATCH 2/2] ofproto-dpif-ipfix: add interface Information Elements to
> > >> flow key
> > >>
> > >> Extend flow key part of data record to include following Information Elements:
> > >> - ingressInterface
> > >> - ingressInterfaceType
> > >> - egressInterface
> > >> - egressInterfaceType
> > >> - interfaceName
> > >> - interfaceDescription
> > >>
> > >> In case of input sampling we don't have information about egress port.
> > >> Define templates depending not only on protocol types, but also on flow
> > >> direction. Only egress flow will include egress information elements.
> > >>
> > >> With this change, dpif_ipfix_exporter stores every port in hmap rather
> > >> than only tunnel ports. It allows to easily retrieve required
> > >> information about interfaces during sampling upcalls.
> > >>
> > >> Signed-off-by: Przemyslaw Szczerbik <przemyslawx.szczerbik@intel.com>
> > >> ---
> > >> v1->v2
> > >> * Add interfaceType and interfaceDescription
> > >> * Rework ipfix_get_iface_data_record function
> > >>
> > >>   ofproto/ofproto-dpif-ipfix.c | 356 +++++++++++++++++++++++++++++++-----
> > > --
> > >> -----
> > >>   ofproto/ofproto-dpif-ipfix.h |   6 +-
> > >>   ofproto/ofproto-dpif-xlate.c |   4 +-
> > >>   ofproto/ofproto-dpif.c       |  19 +--
> > >>   4 files changed, 275 insertions(+), 110 deletions(-)
> > >>
> > >> 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);
> > >>               }
> > >>           }
> > >>
> > >> --
> > >> 1.8.3.1
> > >
> > > --------------------------------------------------------------
> > > Intel Research and Development Ireland Limited
> > > Registered in Ireland
> > > Registered Office: Collinstown Industrial Park, Leixlip, County Kildare
> > > Registered Number: 308263
> > >
> > >
> > > This e-mail and any attachments may contain confidential material for the sole
> > > use of the intended recipient(s). Any review or distribution by others is
> > > strictly prohibited. If you are not the intended recipient, please contact the
> > > sender and delete all copies.
> > >
> > > _______________________________________________
> > > dev mailing list
> > > dev@openvswitch.org
> > > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> > --------------------------------------------------------------
> > Intel Research and Development Ireland Limited
> > Registered in Ireland
> > Registered Office: Collinstown Industrial Park, Leixlip, County Kildare
> > Registered Number: 308263
> >
> >
> > This e-mail and any attachments may contain confidential material for the sole
> > use of the intended recipient(s). Any review or distribution by others is
> > strictly prohibited. If you are not the intended recipient, please contact the
> > sender and delete all copies.
> >
> > _______________________________________________
> > dev mailing list
> > dev@openvswitch.org
> > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> >
> 
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
Greg Rose Aug. 29, 2017, 3:15 p.m. | #6
On 08/29/2017 04:48 AM, Weglicki, MichalX wrote:
> Hello Greg,
>
> Unfortunately Przemek is not available, but I'm more or less familiar with this patch.
>
> Did you manage to fix this issue? I think that your problems lies on connection level, which wasn't modified by this patch. Patch added additional counters on reporting level, and based on your description, ovs just can't connect to collector.
>
> Br,
> Michal.

Correct - it is not an issue with the patch.  However, I need to test the patch and ran into problems while trying to do so.  Since then I've been pulled into some other work and will get back to this afterwards.  I'll just need to debug the connection issue to the collector.

Thanks,

- Greg

>
> > -----Original Message-----
> > From: ovs-dev-bounces@openvswitch.org [mailto:ovs-dev-bounces@openvswitch.org] On Behalf Of Greg Rose
> > Sent: Saturday, August 19, 2017 12:51 AM
> > To: Szczerbik, PrzemyslawX <przemyslawx.szczerbik@intel.com>
> > Cc: dev@openvswitch.org
> > Subject: Re: [ovs-dev] [PATCH 2/2] ofproto-dpif-ipfix: add interface Information Elements to flow key
> >
> > On 08/16/2017 01:54 AM, Szczerbik, PrzemyslawX wrote:
> >> Hi,
> >>
> >> I haven't received any feedback on this patch for quite some time.
> >> Is there anything that I can do to expedite review process?
> >>
> >> Regards,
> >> Przemek
> >>
> >
> > Przemek,
> >
> > I'm in the process of looking into this patch but I'm running into an issue with the vswitch not sending flows to the ntopng/nprobe
> > collector I have set up.  I see this in the vswitchd log:
> >
> > 2017-08-18T21:48:57.917Z|00058|collectors|WARN|couldn't open connection to collector 192.168.0.21:2055 (Network is
> > unreachable)
> > 2017-08-18T21:48:57.917Z|00059|ipfix|WARN|no collectors could be initialized, IPFIX exporter disabled
> >
> > However, 192.168.0.21 is reachable, at least from the br0 bridge that has the IPFIX flows enabled.
> >
> > ntopng/nprobe on the collector machine has the right ports open:
> >
> > netstat -tulpen | grep 2055
> > udp        0      0 0.0.0.0:2055            0.0.0.0:*                           99         27671      3038/nprobe
> > udp6       0      0 :::2055                 :::*                                99         27672      3038/nprobe
> >
> > netstat -tulpen | grep 5556
> > tcp        0      0 0.0.0.0:5556            0.0.0.0:*               LISTEN      0          27666      3038/nprobe
> >
> > I'm not sure exactly what the problem is but I'm debugging that.  Until I can get past this issue I can't really test and review your
> > patch.  I am actively working on getting this issue fixed though.
> >
> > Regards,
> >
> > - Greg
> >>> -----Original Message-----
> >>> From: ovs-dev-bounces@openvswitch.org [mailto:ovs-dev-
> >>> bounces@openvswitch.org] On Behalf Of Szczerbik, PrzemyslawX
> >>> Sent: Wednesday, July 26, 2017 12:01 PM
> >>> To: dev@openvswitch.org
> >>> Subject: Re: [ovs-dev] [PATCH 2/2] ofproto-dpif-ipfix: add interface Information
> >>> Elements to flow key
> >>>
> >>> Hi,
> >>>
> >>> This patch was supposed to be v2, but I forgot to mention that in the subject.
> >>> Previous version: https://patchwork.ozlabs.org/patch/792730/
> >>>
> >>> Let me know if you want me to re-sent it with a proper version.
> >>>
> >>> Regards,
> >>> Przemek
> >>>
> >>>> -----Original Message-----
> >>>> From: Szczerbik, PrzemyslawX
> >>>> Sent: Wednesday, July 26, 2017 10:44 AM
> >>>> To: dev@openvswitch.org
> >>>> Cc: Szczerbik, PrzemyslawX <przemyslawx.szczerbik@intel.com>
> >>>> Subject: [PATCH 2/2] ofproto-dpif-ipfix: add interface Information Elements to
> >>>> flow key
> >>>>
> >>>> Extend flow key part of data record to include following Information Elements:
> >>>> - ingressInterface
> >>>> - ingressInterfaceType
> >>>> - egressInterface
> >>>> - egressInterfaceType
> >>>> - interfaceName
> >>>> - interfaceDescription
> >>>>
> >>>> In case of input sampling we don't have information about egress port.
> >>>> Define templates depending not only on protocol types, but also on flow
> >>>> direction. Only egress flow will include egress information elements.
> >>>>
> >>>> With this change, dpif_ipfix_exporter stores every port in hmap rather
> >>>> than only tunnel ports. It allows to easily retrieve required
> >>>> information about interfaces during sampling upcalls.
> >>>>
> >>>> Signed-off-by: Przemyslaw Szczerbik <przemyslawx.szczerbik@intel.com>
> >>>> ---
> >>>> v1->v2
> >>>> * Add interfaceType and interfaceDescription
> >>>> * Rework ipfix_get_iface_data_record function
> >>>>
> >>>>    ofproto/ofproto-dpif-ipfix.c | 356 +++++++++++++++++++++++++++++++-----
> >>> --
> >>>> -----
> >>>>    ofproto/ofproto-dpif-ipfix.h |   6 +-
> >>>>    ofproto/ofproto-dpif-xlate.c |   4 +-
> >>>>    ofproto/ofproto-dpif.c       |  19 +--
> >>>>    4 files changed, 275 insertions(+), 110 deletions(-)
> >>>>
> >>>> 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);
> >>>>                }
> >>>>            }
> >>>>
> >>>> --
> >>>> 1.8.3.1
> >>>
> >>> --------------------------------------------------------------
> >>> Intel Research and Development Ireland Limited
> >>> Registered in Ireland
> >>> Registered Office: Collinstown Industrial Park, Leixlip, County Kildare
> >>> Registered Number: 308263
> >>>
> >>>
> >>> This e-mail and any attachments may contain confidential material for the sole
> >>> use of the intended recipient(s). Any review or distribution by others is
> >>> strictly prohibited. If you are not the intended recipient, please contact the
> >>> sender and delete all copies.
> >>>
> >>> _______________________________________________
> >>> dev mailing list
> >>> dev@openvswitch.org
> >>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> >> --------------------------------------------------------------
> >> Intel Research and Development Ireland Limited
> >> Registered in Ireland
> >> Registered Office: Collinstown Industrial Park, Leixlip, County Kildare
> >> Registered Number: 308263
> >>
> >>
> >> This e-mail and any attachments may contain confidential material for the sole
> >> use of the intended recipient(s). Any review or distribution by others is
> >> strictly prohibited. If you are not the intended recipient, please contact the
> >> sender and delete all copies.
> >>
> >> _______________________________________________
> >> dev mailing list
> >> dev@openvswitch.org
> >> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> >>
> >
> > _______________________________________________
> > dev mailing list
> > dev@openvswitch.org
> > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
Weglicki, MichalX Sept. 6, 2017, 9:53 a.m. | #7
Hey Greg, 

Do you have any schedule for checking this patch? 

Thank you in advance!

Br, 
Michal. 

> -----Original Message-----
> From: Greg Rose [mailto:gvrose8192@gmail.com]
> Sent: Tuesday, August 29, 2017 5:15 PM
> To: Weglicki, MichalX <michalx.weglicki@intel.com>
> Cc: dev@openvswitch.org; Szczerbik, PrzemyslawX <przemyslawx.szczerbik@intel.com>
> Subject: Re: [ovs-dev] [PATCH 2/2] ofproto-dpif-ipfix: add interface Information Elements to flow key
> 
> On 08/29/2017 04:48 AM, Weglicki, MichalX wrote:
> > Hello Greg,
> >
> > Unfortunately Przemek is not available, but I'm more or less familiar with this patch.
> >
> > Did you manage to fix this issue? I think that your problems lies on connection level, which wasn't modified by this patch. Patch
> added additional counters on reporting level, and based on your description, ovs just can't connect to collector.
> >
> > Br,
> > Michal.
> 
> Correct - it is not an issue with the patch.  However, I need to test the patch and ran into problems while trying to do so.  Since then
> I've been pulled into some other work and will get back to this afterwards.  I'll just need to debug the connection issue to the
> collector.
> 
> Thanks,
> 
> - Greg
> 
> >
> > > -----Original Message-----
> > > From: ovs-dev-bounces@openvswitch.org [mailto:ovs-dev-bounces@openvswitch.org] On Behalf Of Greg Rose
> > > Sent: Saturday, August 19, 2017 12:51 AM
> > > To: Szczerbik, PrzemyslawX <przemyslawx.szczerbik@intel.com>
> > > Cc: dev@openvswitch.org
> > > Subject: Re: [ovs-dev] [PATCH 2/2] ofproto-dpif-ipfix: add interface Information Elements to flow key
> > >
> > > On 08/16/2017 01:54 AM, Szczerbik, PrzemyslawX wrote:
> > >> Hi,
> > >>
> > >> I haven't received any feedback on this patch for quite some time.
> > >> Is there anything that I can do to expedite review process?
> > >>
> > >> Regards,
> > >> Przemek
> > >>
> > >
> > > Przemek,
> > >
> > > I'm in the process of looking into this patch but I'm running into an issue with the vswitch not sending flows to the
> ntopng/nprobe
> > > collector I have set up.  I see this in the vswitchd log:
> > >
> > > 2017-08-18T21:48:57.917Z|00058|collectors|WARN|couldn't open connection to collector 192.168.0.21:2055 (Network is
> > > unreachable)
> > > 2017-08-18T21:48:57.917Z|00059|ipfix|WARN|no collectors could be initialized, IPFIX exporter disabled
> > >
> > > However, 192.168.0.21 is reachable, at least from the br0 bridge that has the IPFIX flows enabled.
> > >
> > > ntopng/nprobe on the collector machine has the right ports open:
> > >
> > > netstat -tulpen | grep 2055
> > > udp        0      0 0.0.0.0:2055            0.0.0.0:*                           99         27671      3038/nprobe
> > > udp6       0      0 :::2055                 :::*                                99         27672      3038/nprobe
> > >
> > > netstat -tulpen | grep 5556
> > > tcp        0      0 0.0.0.0:5556            0.0.0.0:*               LISTEN      0          27666      3038/nprobe
> > >
> > > I'm not sure exactly what the problem is but I'm debugging that.  Until I can get past this issue I can't really test and review your
> > > patch.  I am actively working on getting this issue fixed though.
> > >
> > > Regards,
> > >
> > > - Greg
> > >>> -----Original Message-----
> > >>> From: ovs-dev-bounces@openvswitch.org [mailto:ovs-dev-
> > >>> bounces@openvswitch.org] On Behalf Of Szczerbik, PrzemyslawX
> > >>> Sent: Wednesday, July 26, 2017 12:01 PM
> > >>> To: dev@openvswitch.org
> > >>> Subject: Re: [ovs-dev] [PATCH 2/2] ofproto-dpif-ipfix: add interface Information
> > >>> Elements to flow key
> > >>>
> > >>> Hi,
> > >>>
> > >>> This patch was supposed to be v2, but I forgot to mention that in the subject.
> > >>> Previous version: https://patchwork.ozlabs.org/patch/792730/
> > >>>
> > >>> Let me know if you want me to re-sent it with a proper version.
> > >>>
> > >>> Regards,
> > >>> Przemek
> > >>>
> > >>>> -----Original Message-----
> > >>>> From: Szczerbik, PrzemyslawX
> > >>>> Sent: Wednesday, July 26, 2017 10:44 AM
> > >>>> To: dev@openvswitch.org
> > >>>> Cc: Szczerbik, PrzemyslawX <przemyslawx.szczerbik@intel.com>
> > >>>> Subject: [PATCH 2/2] ofproto-dpif-ipfix: add interface Information Elements to
> > >>>> flow key
> > >>>>
> > >>>> Extend flow key part of data record to include following Information Elements:
> > >>>> - ingressInterface
> > >>>> - ingressInterfaceType
> > >>>> - egressInterface
> > >>>> - egressInterfaceType
> > >>>> - interfaceName
> > >>>> - interfaceDescription
> > >>>>
> > >>>> In case of input sampling we don't have information about egress port.
> > >>>> Define templates depending not only on protocol types, but also on flow
> > >>>> direction. Only egress flow will include egress information elements.
> > >>>>
> > >>>> With this change, dpif_ipfix_exporter stores every port in hmap rather
> > >>>> than only tunnel ports. It allows to easily retrieve required
> > >>>> information about interfaces during sampling upcalls.
> > >>>>
> > >>>> Signed-off-by: Przemyslaw Szczerbik <przemyslawx.szczerbik@intel.com>
> > >>>> ---
> > >>>> v1->v2
> > >>>> * Add interfaceType and interfaceDescription
> > >>>> * Rework ipfix_get_iface_data_record function
> > >>>>
> > >>>>    ofproto/ofproto-dpif-ipfix.c | 356 +++++++++++++++++++++++++++++++-----
> > >>> --
> > >>>> -----
> > >>>>    ofproto/ofproto-dpif-ipfix.h |   6 +-
> > >>>>    ofproto/ofproto-dpif-xlate.c |   4 +-
> > >>>>    ofproto/ofproto-dpif.c       |  19 +--
> > >>>>    4 files changed, 275 insertions(+), 110 deletions(-)
> > >>>>
> > >>>> 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);
> > >>>>                }
> > >>>>            }
> > >>>>
> > >>>> --
> > >>>> 1.8.3.1
> > >>>
> > >>> --------------------------------------------------------------
> > >>> Intel Research and Development Ireland Limited
> > >>> Registered in Ireland
> > >>> Registered Office: Collinstown Industrial Park, Leixlip, County Kildare
> > >>> Registered Number: 308263
> > >>>
> > >>>
> > >>> This e-mail and any attachments may contain confidential material for the sole
> > >>> use of the intended recipient(s). Any review or distribution by others is
> > >>> strictly prohibited. If you are not the intended recipient, please contact the
> > >>> sender and delete all copies.
> > >>>
> > >>> _______________________________________________
> > >>> dev mailing list
> > >>> dev@openvswitch.org
> > >>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> > >> --------------------------------------------------------------
> > >> Intel Research and Development Ireland Limited
> > >> Registered in Ireland
> > >> Registered Office: Collinstown Industrial Park, Leixlip, County Kildare
> > >> Registered Number: 308263
> > >>
> > >>
> > >> This e-mail and any attachments may contain confidential material for the sole
> > >> use of the intended recipient(s). Any review or distribution by others is
> > >> strictly prohibited. If you are not the intended recipient, please contact the
> > >> sender and delete all copies.
> > >>
> > >> _______________________________________________
> > >> dev mailing list
> > >> dev@openvswitch.org
> > >> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> > >>
> > >
> > > _______________________________________________
> > > dev mailing list
> > > dev@openvswitch.org
> > > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
Greg Rose Sept. 6, 2017, 4:59 p.m. | #8
On 09/06/2017 02:53 AM, Weglicki, MichalX wrote:
> Hey Greg,
> 
> Do you have any schedule for checking this patch?
> 
> Thank you in advance!
> 
> Br,
> Michal.

Hi Michal,

I'll work on it this week and see if I can resolve the connection problem to the collector.

Thanks,

- Greg

> 
>> -----Original Message-----
>> From: Greg Rose [mailto:gvrose8192@gmail.com]
>> Sent: Tuesday, August 29, 2017 5:15 PM
>> To: Weglicki, MichalX <michalx.weglicki@intel.com>
>> Cc: dev@openvswitch.org; Szczerbik, PrzemyslawX <przemyslawx.szczerbik@intel.com>
>> Subject: Re: [ovs-dev] [PATCH 2/2] ofproto-dpif-ipfix: add interface Information Elements to flow key
>>
>> On 08/29/2017 04:48 AM, Weglicki, MichalX wrote:
>>> Hello Greg,
>>>
>>> Unfortunately Przemek is not available, but I'm more or less familiar with this patch.
>>>
>>> Did you manage to fix this issue? I think that your problems lies on connection level, which wasn't modified by this patch. Patch
>> added additional counters on reporting level, and based on your description, ovs just can't connect to collector.
>>>
>>> Br,
>>> Michal.
>>
>> Correct - it is not an issue with the patch.  However, I need to test the patch and ran into problems while trying to do so.  Since then
>> I've been pulled into some other work and will get back to this afterwards.  I'll just need to debug the connection issue to the
>> collector.
>>
>> Thanks,
>>
>> - Greg
>>
>>>
>>>> -----Original Message-----
>>>> From: ovs-dev-bounces@openvswitch.org [mailto:ovs-dev-bounces@openvswitch.org] On Behalf Of Greg Rose
>>>> Sent: Saturday, August 19, 2017 12:51 AM
>>>> To: Szczerbik, PrzemyslawX <przemyslawx.szczerbik@intel.com>
>>>> Cc: dev@openvswitch.org
>>>> Subject: Re: [ovs-dev] [PATCH 2/2] ofproto-dpif-ipfix: add interface Information Elements to flow key
>>>>
>>>> On 08/16/2017 01:54 AM, Szczerbik, PrzemyslawX wrote:
>>>>> Hi,
>>>>>
>>>>> I haven't received any feedback on this patch for quite some time.
>>>>> Is there anything that I can do to expedite review process?
>>>>>
>>>>> Regards,
>>>>> Przemek
>>>>>
>>>>
>>>> Przemek,
>>>>
>>>> I'm in the process of looking into this patch but I'm running into an issue with the vswitch not sending flows to the
>> ntopng/nprobe
>>>> collector I have set up.  I see this in the vswitchd log:
>>>>
>>>> 2017-08-18T21:48:57.917Z|00058|collectors|WARN|couldn't open connection to collector 192.168.0.21:2055 (Network is
>>>> unreachable)
>>>> 2017-08-18T21:48:57.917Z|00059|ipfix|WARN|no collectors could be initialized, IPFIX exporter disabled
>>>>
>>>> However, 192.168.0.21 is reachable, at least from the br0 bridge that has the IPFIX flows enabled.
>>>>
>>>> ntopng/nprobe on the collector machine has the right ports open:
>>>>
>>>> netstat -tulpen | grep 2055
>>>> udp        0      0 0.0.0.0:2055            0.0.0.0:*                           99         27671      3038/nprobe
>>>> udp6       0      0 :::2055                 :::*                                99         27672      3038/nprobe
>>>>
>>>> netstat -tulpen | grep 5556
>>>> tcp        0      0 0.0.0.0:5556            0.0.0.0:*               LISTEN      0          27666      3038/nprobe
>>>>
>>>> I'm not sure exactly what the problem is but I'm debugging that.  Until I can get past this issue I can't really test and review your
>>>> patch.  I am actively working on getting this issue fixed though.
>>>>
>>>> Regards,
>>>>
>>>> - Greg
>>>>>> -----Original Message-----
>>>>>> From: ovs-dev-bounces@openvswitch.org [mailto:ovs-dev-
>>>>>> bounces@openvswitch.org] On Behalf Of Szczerbik, PrzemyslawX
>>>>>> Sent: Wednesday, July 26, 2017 12:01 PM
>>>>>> To: dev@openvswitch.org
>>>>>> Subject: Re: [ovs-dev] [PATCH 2/2] ofproto-dpif-ipfix: add interface Information
>>>>>> Elements to flow key
>>>>>>
>>>>>> Hi,
>>>>>>
>>>>>> This patch was supposed to be v2, but I forgot to mention that in the subject.
>>>>>> Previous version: https://patchwork.ozlabs.org/patch/792730/
>>>>>>
>>>>>> Let me know if you want me to re-sent it with a proper version.
>>>>>>
>>>>>> Regards,
>>>>>> Przemek
>>>>>>
>>>>>>> -----Original Message-----
>>>>>>> From: Szczerbik, PrzemyslawX
>>>>>>> Sent: Wednesday, July 26, 2017 10:44 AM
>>>>>>> To: dev@openvswitch.org
>>>>>>> Cc: Szczerbik, PrzemyslawX <przemyslawx.szczerbik@intel.com>
>>>>>>> Subject: [PATCH 2/2] ofproto-dpif-ipfix: add interface Information Elements to
>>>>>>> flow key
>>>>>>>
>>>>>>> Extend flow key part of data record to include following Information Elements:
>>>>>>> - ingressInterface
>>>>>>> - ingressInterfaceType
>>>>>>> - egressInterface
>>>>>>> - egressInterfaceType
>>>>>>> - interfaceName
>>>>>>> - interfaceDescription
>>>>>>>
>>>>>>> In case of input sampling we don't have information about egress port.
>>>>>>> Define templates depending not only on protocol types, but also on flow
>>>>>>> direction. Only egress flow will include egress information elements.
>>>>>>>
>>>>>>> With this change, dpif_ipfix_exporter stores every port in hmap rather
>>>>>>> than only tunnel ports. It allows to easily retrieve required
>>>>>>> information about interfaces during sampling upcalls.
>>>>>>>
>>>>>>> Signed-off-by: Przemyslaw Szczerbik <przemyslawx.szczerbik@intel.com>
>>>>>>> ---
>>>>>>> v1->v2
>>>>>>> * Add interfaceType and interfaceDescription
>>>>>>> * Rework ipfix_get_iface_data_record function
>>>>>>>
>>>>>>>     ofproto/ofproto-dpif-ipfix.c | 356 +++++++++++++++++++++++++++++++-----
>>>>>> --
>>>>>>> -----
>>>>>>>     ofproto/ofproto-dpif-ipfix.h |   6 +-
>>>>>>>     ofproto/ofproto-dpif-xlate.c |   4 +-
>>>>>>>     ofproto/ofproto-dpif.c       |  19 +--
>>>>>>>     4 files changed, 275 insertions(+), 110 deletions(-)
>>>>>>>
>>>>>>> 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);
>>>>>>>                 }
>>>>>>>             }
>>>>>>>
>>>>>>> --
>>>>>>> 1.8.3.1
>>>>>>
>>>>>> --------------------------------------------------------------
>>>>>> Intel Research and Development Ireland Limited
>>>>>> Registered in Ireland
>>>>>> Registered Office: Collinstown Industrial Park, Leixlip, County Kildare
>>>>>> Registered Number: 308263
>>>>>>
>>>>>>
>>>>>> This e-mail and any attachments may contain confidential material for the sole
>>>>>> use of the intended recipient(s). Any review or distribution by others is
>>>>>> strictly prohibited. If you are not the intended recipient, please contact the
>>>>>> sender and delete all copies.
>>>>>>
>>>>>> _______________________________________________
>>>>>> dev mailing list
>>>>>> dev@openvswitch.org
>>>>>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>>>>> --------------------------------------------------------------
>>>>> Intel Research and Development Ireland Limited
>>>>> Registered in Ireland
>>>>> Registered Office: Collinstown Industrial Park, Leixlip, County Kildare
>>>>> Registered Number: 308263
>>>>>
>>>>>
>>>>> This e-mail and any attachments may contain confidential material for the sole
>>>>> use of the intended recipient(s). Any review or distribution by others is
>>>>> strictly prohibited. If you are not the intended recipient, please contact the
>>>>> sender and delete all copies.
>>>>>
>>>>> _______________________________________________
>>>>> dev mailing list
>>>>> dev@openvswitch.org
>>>>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>>>>>
>>>>
>>>> _______________________________________________
>>>> dev mailing list
>>>> dev@openvswitch.org
>>>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
Greg Rose Sept. 6, 2017, 10:01 p.m. | #9
On 09/06/2017 02:53 AM, Weglicki, MichalX wrote:
> Hey Greg,
> 
> Do you have any schedule for checking this patch?
> 
> Thank you in advance!
> 
> Br,
> Michal.

Michal,

The good news is that I have my ntopng/nprobe ipfix collector properly configured now and can see
flows, hosts, etc. when I enable IPFIX.  So I'm ready to test these two patches from Przemyslaw:

https://patchwork.ozlabs.org/patch/793773/
https://patchwork.ozlabs.org/patch/793772/

Unfortunately the patches no longer apply to master.

Can you rebase and resubmit a V3 series?

Thanks,

- Greg

> 
>> -----Original Message-----
>> From: Greg Rose [mailto:gvrose8192@gmail.com]
>> Sent: Tuesday, August 29, 2017 5:15 PM
>> To: Weglicki, MichalX <michalx.weglicki@intel.com>
>> Cc: dev@openvswitch.org; Szczerbik, PrzemyslawX <przemyslawx.szczerbik@intel.com>
>> Subject: Re: [ovs-dev] [PATCH 2/2] ofproto-dpif-ipfix: add interface Information Elements to flow key
>>
>> On 08/29/2017 04:48 AM, Weglicki, MichalX wrote:
>>> Hello Greg,
>>>
>>> Unfortunately Przemek is not available, but I'm more or less familiar with this patch.
>>>
>>> Did you manage to fix this issue? I think that your problems lies on connection level, which wasn't modified by this patch. Patch
>> added additional counters on reporting level, and based on your description, ovs just can't connect to collector.
>>>
>>> Br,
>>> Michal.
>>
>> Correct - it is not an issue with the patch.  However, I need to test the patch and ran into problems while trying to do so.  Since then
>> I've been pulled into some other work and will get back to this afterwards.  I'll just need to debug the connection issue to the
>> collector.
>>
>> Thanks,
>>
>> - Greg
>>
>>>
>>>> -----Original Message-----
>>>> From: ovs-dev-bounces@openvswitch.org [mailto:ovs-dev-bounces@openvswitch.org] On Behalf Of Greg Rose
>>>> Sent: Saturday, August 19, 2017 12:51 AM
>>>> To: Szczerbik, PrzemyslawX <przemyslawx.szczerbik@intel.com>
>>>> Cc: dev@openvswitch.org
>>>> Subject: Re: [ovs-dev] [PATCH 2/2] ofproto-dpif-ipfix: add interface Information Elements to flow key
>>>>
>>>> On 08/16/2017 01:54 AM, Szczerbik, PrzemyslawX wrote:
>>>>> Hi,
>>>>>
>>>>> I haven't received any feedback on this patch for quite some time.
>>>>> Is there anything that I can do to expedite review process?
>>>>>
>>>>> Regards,
>>>>> Przemek
>>>>>
>>>>
>>>> Przemek,
>>>>
>>>> I'm in the process of looking into this patch but I'm running into an issue with the vswitch not sending flows to the
>> ntopng/nprobe
>>>> collector I have set up.  I see this in the vswitchd log:
>>>>
>>>> 2017-08-18T21:48:57.917Z|00058|collectors|WARN|couldn't open connection to collector 192.168.0.21:2055 (Network is
>>>> unreachable)
>>>> 2017-08-18T21:48:57.917Z|00059|ipfix|WARN|no collectors could be initialized, IPFIX exporter disabled
>>>>
>>>> However, 192.168.0.21 is reachable, at least from the br0 bridge that has the IPFIX flows enabled.
>>>>
>>>> ntopng/nprobe on the collector machine has the right ports open:
>>>>
>>>> netstat -tulpen | grep 2055
>>>> udp        0      0 0.0.0.0:2055            0.0.0.0:*                           99         27671      3038/nprobe
>>>> udp6       0      0 :::2055                 :::*                                99         27672      3038/nprobe
>>>>
>>>> netstat -tulpen | grep 5556
>>>> tcp        0      0 0.0.0.0:5556            0.0.0.0:*               LISTEN      0          27666      3038/nprobe
>>>>
>>>> I'm not sure exactly what the problem is but I'm debugging that.  Until I can get past this issue I can't really test and review your
>>>> patch.  I am actively working on getting this issue fixed though.
>>>>
>>>> Regards,
>>>>
>>>> - Greg
>>>>>> -----Original Message-----
>>>>>> From: ovs-dev-bounces@openvswitch.org [mailto:ovs-dev-
>>>>>> bounces@openvswitch.org] On Behalf Of Szczerbik, PrzemyslawX
>>>>>> Sent: Wednesday, July 26, 2017 12:01 PM
>>>>>> To: dev@openvswitch.org
>>>>>> Subject: Re: [ovs-dev] [PATCH 2/2] ofproto-dpif-ipfix: add interface Information
>>>>>> Elements to flow key
>>>>>>
>>>>>> Hi,
>>>>>>
>>>>>> This patch was supposed to be v2, but I forgot to mention that in the subject.
>>>>>> Previous version: https://patchwork.ozlabs.org/patch/792730/
>>>>>>
>>>>>> Let me know if you want me to re-sent it with a proper version.
>>>>>>
>>>>>> Regards,
>>>>>> Przemek
>>>>>>
>>>>>>> -----Original Message-----
>>>>>>> From: Szczerbik, PrzemyslawX
>>>>>>> Sent: Wednesday, July 26, 2017 10:44 AM
>>>>>>> To: dev@openvswitch.org
>>>>>>> Cc: Szczerbik, PrzemyslawX <przemyslawx.szczerbik@intel.com>
>>>>>>> Subject: [PATCH 2/2] ofproto-dpif-ipfix: add interface Information Elements to
>>>>>>> flow key
>>>>>>>
>>>>>>> Extend flow key part of data record to include following Information Elements:
>>>>>>> - ingressInterface
>>>>>>> - ingressInterfaceType
>>>>>>> - egressInterface
>>>>>>> - egressInterfaceType
>>>>>>> - interfaceName
>>>>>>> - interfaceDescription
>>>>>>>
>>>>>>> In case of input sampling we don't have information about egress port.
>>>>>>> Define templates depending not only on protocol types, but also on flow
>>>>>>> direction. Only egress flow will include egress information elements.
>>>>>>>
>>>>>>> With this change, dpif_ipfix_exporter stores every port in hmap rather
>>>>>>> than only tunnel ports. It allows to easily retrieve required
>>>>>>> information about interfaces during sampling upcalls.
>>>>>>>
>>>>>>> Signed-off-by: Przemyslaw Szczerbik <przemyslawx.szczerbik@intel.com>
>>>>>>> ---
>>>>>>> v1->v2
>>>>>>> * Add interfaceType and interfaceDescription
>>>>>>> * Rework ipfix_get_iface_data_record function
>>>>>>>
>>>>>>>     ofproto/ofproto-dpif-ipfix.c | 356 +++++++++++++++++++++++++++++++-----
>>>>>> --
>>>>>>> -----
>>>>>>>     ofproto/ofproto-dpif-ipfix.h |   6 +-
>>>>>>>     ofproto/ofproto-dpif-xlate.c |   4 +-
>>>>>>>     ofproto/ofproto-dpif.c       |  19 +--
>>>>>>>     4 files changed, 275 insertions(+), 110 deletions(-)
>>>>>>>
>>>>>>> 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);
>>>>>>>                 }
>>>>>>>             }
>>>>>>>
>>>>>>> --
>>>>>>> 1.8.3.1
>>>>>>
>>>>>> --------------------------------------------------------------
>>>>>> Intel Research and Development Ireland Limited
>>>>>> Registered in Ireland
>>>>>> Registered Office: Collinstown Industrial Park, Leixlip, County Kildare
>>>>>> Registered Number: 308263
>>>>>>
>>>>>>
>>>>>> This e-mail and any attachments may contain confidential material for the sole
>>>>>> use of the intended recipient(s). Any review or distribution by others is
>>>>>> strictly prohibited. If you are not the intended recipient, please contact the
>>>>>> sender and delete all copies.
>>>>>>
>>>>>> _______________________________________________
>>>>>> dev mailing list
>>>>>> dev@openvswitch.org
>>>>>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>>>>> --------------------------------------------------------------
>>>>> Intel Research and Development Ireland Limited
>>>>> Registered in Ireland
>>>>> Registered Office: Collinstown Industrial Park, Leixlip, County Kildare
>>>>> Registered Number: 308263
>>>>>
>>>>>
>>>>> This e-mail and any attachments may contain confidential material for the sole
>>>>> use of the intended recipient(s). Any review or distribution by others is
>>>>> strictly prohibited. If you are not the intended recipient, please contact the
>>>>> sender and delete all copies.
>>>>>
>>>>> _______________________________________________
>>>>> dev mailing list
>>>>> dev@openvswitch.org
>>>>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>>>>>
>>>>
>>>> _______________________________________________
>>>> dev mailing list
>>>> dev@openvswitch.org
>>>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
Weglicki, MichalX Sept. 8, 2017, 1:35 p.m. | #10
Greg, 

Patch is rebased and sent to mailing list as V3 (Last patch was supposed to be V2 - Przemek by accident sent it again as V1). 

Br, 
Michal. 

> -----Original Message-----
> From: Greg Rose [mailto:gvrose8192@gmail.com]
> Sent: Thursday, September 7, 2017 12:02 AM
> To: Weglicki, MichalX <michalx.weglicki@intel.com>
> Cc: dev@openvswitch.org; Szczerbik, PrzemyslawX <przemyslawx.szczerbik@intel.com>
> Subject: Re: [ovs-dev] [PATCH 2/2] ofproto-dpif-ipfix: add interface Information Elements to flow key
> 
> On 09/06/2017 02:53 AM, Weglicki, MichalX wrote:
> > Hey Greg,
> >
> > Do you have any schedule for checking this patch?
> >
> > Thank you in advance!
> >
> > Br,
> > Michal.
> 
> Michal,
> 
> The good news is that I have my ntopng/nprobe ipfix collector properly configured now and can see
> flows, hosts, etc. when I enable IPFIX.  So I'm ready to test these two patches from Przemyslaw:
> 
> https://patchwork.ozlabs.org/patch/793773/
> https://patchwork.ozlabs.org/patch/793772/
> 
> Unfortunately the patches no longer apply to master.
> 
> Can you rebase and resubmit a V3 series?
> 
> Thanks,
> 
> - Greg
> 
> >
> >> -----Original Message-----
> >> From: Greg Rose [mailto:gvrose8192@gmail.com]
> >> Sent: Tuesday, August 29, 2017 5:15 PM
> >> To: Weglicki, MichalX <michalx.weglicki@intel.com>
> >> Cc: dev@openvswitch.org; Szczerbik, PrzemyslawX <przemyslawx.szczerbik@intel.com>
> >> Subject: Re: [ovs-dev] [PATCH 2/2] ofproto-dpif-ipfix: add interface Information Elements to flow key
> >>
> >> On 08/29/2017 04:48 AM, Weglicki, MichalX wrote:
> >>> Hello Greg,
> >>>
> >>> Unfortunately Przemek is not available, but I'm more or less familiar with this patch.
> >>>
> >>> Did you manage to fix this issue? I think that your problems lies on connection level, which wasn't modified by this patch. Patch
> >> added additional counters on reporting level, and based on your description, ovs just can't connect to collector.
> >>>
> >>> Br,
> >>> Michal.
> >>
> >> Correct - it is not an issue with the patch.  However, I need to test the patch and ran into problems while trying to do so.  Since
> then
> >> I've been pulled into some other work and will get back to this afterwards.  I'll just need to debug the connection issue to the
> >> collector.
> >>
> >> Thanks,
> >>
> >> - Greg
> >>
> >>>
> >>>> -----Original Message-----
> >>>> From: ovs-dev-bounces@openvswitch.org [mailto:ovs-dev-bounces@openvswitch.org] On Behalf Of Greg Rose
> >>>> Sent: Saturday, August 19, 2017 12:51 AM
> >>>> To: Szczerbik, PrzemyslawX <przemyslawx.szczerbik@intel.com>
> >>>> Cc: dev@openvswitch.org
> >>>> Subject: Re: [ovs-dev] [PATCH 2/2] ofproto-dpif-ipfix: add interface Information Elements to flow key
> >>>>
> >>>> On 08/16/2017 01:54 AM, Szczerbik, PrzemyslawX wrote:
> >>>>> Hi,
> >>>>>
> >>>>> I haven't received any feedback on this patch for quite some time.
> >>>>> Is there anything that I can do to expedite review process?
> >>>>>
> >>>>> Regards,
> >>>>> Przemek
> >>>>>
> >>>>
> >>>> Przemek,
> >>>>
> >>>> I'm in the process of looking into this patch but I'm running into an issue with the vswitch not sending flows to the
> >> ntopng/nprobe
> >>>> collector I have set up.  I see this in the vswitchd log:
> >>>>
> >>>> 2017-08-18T21:48:57.917Z|00058|collectors|WARN|couldn't open connection to collector 192.168.0.21:2055 (Network is
> >>>> unreachable)
> >>>> 2017-08-18T21:48:57.917Z|00059|ipfix|WARN|no collectors could be initialized, IPFIX exporter disabled
> >>>>
> >>>> However, 192.168.0.21 is reachable, at least from the br0 bridge that has the IPFIX flows enabled.
> >>>>
> >>>> ntopng/nprobe on the collector machine has the right ports open:
> >>>>
> >>>> netstat -tulpen | grep 2055
> >>>> udp        0      0 0.0.0.0:2055            0.0.0.0:*                           99         27671      3038/nprobe
> >>>> udp6       0      0 :::2055                 :::*                                99         27672      3038/nprobe
> >>>>
> >>>> netstat -tulpen | grep 5556
> >>>> tcp        0      0 0.0.0.0:5556            0.0.0.0:*               LISTEN      0          27666      3038/nprobe
> >>>>
> >>>> I'm not sure exactly what the problem is but I'm debugging that.  Until I can get past this issue I can't really test and review
> your
> >>>> patch.  I am actively working on getting this issue fixed though.
> >>>>
> >>>> Regards,
> >>>>
> >>>> - Greg
> >>>>>> -----Original Message-----
> >>>>>> From: ovs-dev-bounces@openvswitch.org [mailto:ovs-dev-
> >>>>>> bounces@openvswitch.org] On Behalf Of Szczerbik, PrzemyslawX
> >>>>>> Sent: Wednesday, July 26, 2017 12:01 PM
> >>>>>> To: dev@openvswitch.org
> >>>>>> Subject: Re: [ovs-dev] [PATCH 2/2] ofproto-dpif-ipfix: add interface Information
> >>>>>> Elements to flow key
> >>>>>>
> >>>>>> Hi,
> >>>>>>
> >>>>>> This patch was supposed to be v2, but I forgot to mention that in the subject.
> >>>>>> Previous version: https://patchwork.ozlabs.org/patch/792730/
> >>>>>>
> >>>>>> Let me know if you want me to re-sent it with a proper version.
> >>>>>>
> >>>>>> Regards,
> >>>>>> Przemek
> >>>>>>
> >>>>>>> -----Original Message-----
> >>>>>>> From: Szczerbik, PrzemyslawX
> >>>>>>> Sent: Wednesday, July 26, 2017 10:44 AM
> >>>>>>> To: dev@openvswitch.org
> >>>>>>> Cc: Szczerbik, PrzemyslawX <przemyslawx.szczerbik@intel.com>
> >>>>>>> Subject: [PATCH 2/2] ofproto-dpif-ipfix: add interface Information Elements to
> >>>>>>> flow key
> >>>>>>>
> >>>>>>> Extend flow key part of data record to include following Information Elements:
> >>>>>>> - ingressInterface
> >>>>>>> - ingressInterfaceType
> >>>>>>> - egressInterface
> >>>>>>> - egressInterfaceType
> >>>>>>> - interfaceName
> >>>>>>> - interfaceDescription
> >>>>>>>
> >>>>>>> In case of input sampling we don't have information about egress port.
> >>>>>>> Define templates depending not only on protocol types, but also on flow
> >>>>>>> direction. Only egress flow will include egress information elements.
> >>>>>>>
> >>>>>>> With this change, dpif_ipfix_exporter stores every port in hmap rather
> >>>>>>> than only tunnel ports. It allows to easily retrieve required
> >>>>>>> information about interfaces during sampling upcalls.
> >>>>>>>
> >>>>>>> Signed-off-by: Przemyslaw Szczerbik <przemyslawx.szczerbik@intel.com>
> >>>>>>> ---
> >>>>>>> v1->v2
> >>>>>>> * Add interfaceType and interfaceDescription
> >>>>>>> * Rework ipfix_get_iface_data_record function
> >>>>>>>
> >>>>>>>     ofproto/ofproto-dpif-ipfix.c | 356 +++++++++++++++++++++++++++++++-----
> >>>>>> --
> >>>>>>> -----
> >>>>>>>     ofproto/ofproto-dpif-ipfix.h |   6 +-
> >>>>>>>     ofproto/ofproto-dpif-xlate.c |   4 +-
> >>>>>>>     ofproto/ofproto-dpif.c       |  19 +--
> >>>>>>>     4 files changed, 275 insertions(+), 110 deletions(-)
> >>>>>>>
> >>>>>>> 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);
> >>>>>>>                 }
> >>>>>>>             }
> >>>>>>>
> >>>>>>> --
> >>>>>>> 1.8.3.1
> >>>>>>
> >>>>>> --------------------------------------------------------------
> >>>>>> Intel Research and Development Ireland Limited
> >>>>>> Registered in Ireland
> >>>>>> Registered Office: Collinstown Industrial Park, Leixlip, County Kildare
> >>>>>> Registered Number: 308263
> >>>>>>
> >>>>>>
> >>>>>> This e-mail and any attachments may contain confidential material for the sole
> >>>>>> use of the intended recipient(s). Any review or distribution by others is
> >>>>>> strictly prohibited. If you are not the intended recipient, please contact the
> >>>>>> sender and delete all copies.
> >>>>>>
> >>>>>> _______________________________________________
> >>>>>> dev mailing list
> >>>>>> dev@openvswitch.org
> >>>>>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> >>>>> --------------------------------------------------------------
> >>>>> Intel Research and Development Ireland Limited
> >>>>> Registered in Ireland
> >>>>> Registered Office: Collinstown Industrial Park, Leixlip, County Kildare
> >>>>> Registered Number: 308263
> >>>>>
> >>>>>
> >>>>> This e-mail and any attachments may contain confidential material for the sole
> >>>>> use of the intended recipient(s). Any review or distribution by others is
> >>>>> strictly prohibited. If you are not the intended recipient, please contact the
> >>>>> sender and delete all copies.
> >>>>>
> >>>>> _______________________________________________
> >>>>> dev mailing list
> >>>>> dev@openvswitch.org
> >>>>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> >>>>>
> >>>>
> >>>> _______________________________________________
> >>>> dev mailing list
> >>>> dev@openvswitch.org
> >>>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> >
Greg Rose Sept. 8, 2017, 2:55 p.m. | #11
On 09/08/2017 06:35 AM, Weglicki, MichalX wrote:
> Greg,
>
> Patch is rebased and sent to mailing list as V3 (Last patch was supposed to be V2 - Przemek by accident sent it again as V1).
>
> Br,
> Michal.

Thanks, I'll have a look at it!

- Greg

>
> > -----Original Message-----
> > From: Greg Rose [mailto:gvrose8192@gmail.com]
> > Sent: Thursday, September 7, 2017 12:02 AM
> > To: Weglicki, MichalX <michalx.weglicki@intel.com>
> > Cc: dev@openvswitch.org; Szczerbik, PrzemyslawX <przemyslawx.szczerbik@intel.com>
> > Subject: Re: [ovs-dev] [PATCH 2/2] ofproto-dpif-ipfix: add interface Information Elements to flow key
> >
> > On 09/06/2017 02:53 AM, Weglicki, MichalX wrote:
> >> Hey Greg,
> >>
> >> Do you have any schedule for checking this patch?
> >>
> >> Thank you in advance!
> >>
> >> Br,
> >> Michal.
> >
> > Michal,
> >
> > The good news is that I have my ntopng/nprobe ipfix collector properly configured now and can see
> > flows, hosts, etc. when I enable IPFIX.  So I'm ready to test these two patches from Przemyslaw:
> >
> > https://patchwork.ozlabs.org/patch/793773/
> > https://patchwork.ozlabs.org/patch/793772/
> >
> > Unfortunately the patches no longer apply to master.
> >
> > Can you rebase and resubmit a V3 series?
> >
> > Thanks,
> >
> > - Greg
> >
> >>
> >>> -----Original Message-----
> >>> From: Greg Rose [mailto:gvrose8192@gmail.com]
> >>> Sent: Tuesday, August 29, 2017 5:15 PM
> >>> To: Weglicki, MichalX <michalx.weglicki@intel.com>
> >>> Cc: dev@openvswitch.org; Szczerbik, PrzemyslawX <przemyslawx.szczerbik@intel.com>
> >>> Subject: Re: [ovs-dev] [PATCH 2/2] ofproto-dpif-ipfix: add interface Information Elements to flow key
> >>>
> >>> On 08/29/2017 04:48 AM, Weglicki, MichalX wrote:
> >>>> Hello Greg,
> >>>>
> >>>> Unfortunately Przemek is not available, but I'm more or less familiar with this patch.
> >>>>
> >>>> Did you manage to fix this issue? I think that your problems lies on connection level, which wasn't modified by this patch. Patch
> >>> added additional counters on reporting level, and based on your description, ovs just can't connect to collector.
> >>>>
> >>>> Br,
> >>>> Michal.
> >>>
> >>> Correct - it is not an issue with the patch.  However, I need to test the patch and ran into problems while trying to do so.  Since
> > then
> >>> I've been pulled into some other work and will get back to this afterwards.  I'll just need to debug the connection issue to the
> >>> collector.
> >>>
> >>> Thanks,
> >>>
> >>> - Greg
> >>>
> >>>>
> >>>>> -----Original Message-----
> >>>>> From: ovs-dev-bounces@openvswitch.org [mailto:ovs-dev-bounces@openvswitch.org] On Behalf Of Greg Rose
> >>>>> Sent: Saturday, August 19, 2017 12:51 AM
> >>>>> To: Szczerbik, PrzemyslawX <przemyslawx.szczerbik@intel.com>
> >>>>> Cc: dev@openvswitch.org
> >>>>> Subject: Re: [ovs-dev] [PATCH 2/2] ofproto-dpif-ipfix: add interface Information Elements to flow key
> >>>>>
> >>>>> On 08/16/2017 01:54 AM, Szczerbik, PrzemyslawX wrote:
> >>>>>> Hi,
> >>>>>>
> >>>>>> I haven't received any feedback on this patch for quite some time.
> >>>>>> Is there anything that I can do to expedite review process?
> >>>>>>
> >>>>>> Regards,
> >>>>>> Przemek
> >>>>>>
> >>>>>
> >>>>> Przemek,
> >>>>>
> >>>>> I'm in the process of looking into this patch but I'm running into an issue with the vswitch not sending flows to the
> >>> ntopng/nprobe
> >>>>> collector I have set up.  I see this in the vswitchd log:
> >>>>>
> >>>>> 2017-08-18T21:48:57.917Z|00058|collectors|WARN|couldn't open connection to collector 192.168.0.21:2055 (Network is
> >>>>> unreachable)
> >>>>> 2017-08-18T21:48:57.917Z|00059|ipfix|WARN|no collectors could be initialized, IPFIX exporter disabled
> >>>>>
> >>>>> However, 192.168.0.21 is reachable, at least from the br0 bridge that has the IPFIX flows enabled.
> >>>>>
> >>>>> ntopng/nprobe on the collector machine has the right ports open:
> >>>>>
> >>>>> netstat -tulpen | grep 2055
> >>>>> udp        0      0 0.0.0.0:2055            0.0.0.0:*                           99         27671      3038/nprobe
> >>>>> udp6       0      0 :::2055                 :::*                                99         27672      3038/nprobe
> >>>>>
> >>>>> netstat -tulpen | grep 5556
> >>>>> tcp        0      0 0.0.0.0:5556            0.0.0.0:*               LISTEN      0          27666      3038/nprobe
> >>>>>
> >>>>> I'm not sure exactly what the problem is but I'm debugging that.  Until I can get past this issue I can't really test and review
> > your
> >>>>> patch.  I am actively working on getting this issue fixed though.
> >>>>>
> >>>>> Regards,
> >>>>>
> >>>>> - Greg
> >>>>>>> -----Original Message-----
> >>>>>>> From: ovs-dev-bounces@openvswitch.org [mailto:ovs-dev-
> >>>>>>> bounces@openvswitch.org] On Behalf Of Szczerbik, PrzemyslawX
> >>>>>>> Sent: Wednesday, July 26, 2017 12:01 PM
> >>>>>>> To: dev@openvswitch.org
> >>>>>>> Subject: Re: [ovs-dev] [PATCH 2/2] ofproto-dpif-ipfix: add interface Information
> >>>>>>> Elements to flow key
> >>>>>>>
> >>>>>>> Hi,
> >>>>>>>
> >>>>>>> This patch was supposed to be v2, but I forgot to mention that in the subject.
> >>>>>>> Previous version: https://patchwork.ozlabs.org/patch/792730/
> >>>>>>>
> >>>>>>> Let me know if you want me to re-sent it with a proper version.
> >>>>>>>
> >>>>>>> Regards,
> >>>>>>> Przemek
> >>>>>>>
> >>>>>>>> -----Original Message-----
> >>>>>>>> From: Szczerbik, PrzemyslawX
> >>>>>>>> Sent: Wednesday, July 26, 2017 10:44 AM
> >>>>>>>> To: dev@openvswitch.org
> >>>>>>>> Cc: Szczerbik, PrzemyslawX <przemyslawx.szczerbik@intel.com>
> >>>>>>>> Subject: [PATCH 2/2] ofproto-dpif-ipfix: add interface Information Elements to
> >>>>>>>> flow key
> >>>>>>>>
> >>>>>>>> Extend flow key part of data record to include following Information Elements:
> >>>>>>>> - ingressInterface
> >>>>>>>> - ingressInterfaceType
> >>>>>>>> - egressInterface
> >>>>>>>> - egressInterfaceType
> >>>>>>>> - interfaceName
> >>>>>>>> - interfaceDescription
> >>>>>>>>
> >>>>>>>> In case of input sampling we don't have information about egress port.
> >>>>>>>> Define templates depending not only on protocol types, but also on flow
> >>>>>>>> direction. Only egress flow will include egress information elements.
> >>>>>>>>
> >>>>>>>> With this change, dpif_ipfix_exporter stores every port in hmap rather
> >>>>>>>> than only tunnel ports. It allows to easily retrieve required
> >>>>>>>> information about interfaces during sampling upcalls.
> >>>>>>>>
> >>>>>>>> Signed-off-by: Przemyslaw Szczerbik <przemyslawx.szczerbik@intel.com>
> >>>>>>>> ---
> >>>>>>>> v1->v2
> >>>>>>>> * Add interfaceType and interfaceDescription
> >>>>>>>> * Rework ipfix_get_iface_data_record function
> >>>>>>>>
> >>>>>>>>      ofproto/ofproto-dpif-ipfix.c | 356 +++++++++++++++++++++++++++++++-----
> >>>>>>> --
> >>>>>>>> -----
> >>>>>>>>      ofproto/ofproto-dpif-ipfix.h |   6 +-
> >>>>>>>>      ofproto/ofproto-dpif-xlate.c |   4 +-
> >>>>>>>>      ofproto/ofproto-dpif.c       |  19 +--
> >>>>>>>>      4 files changed, 275 insertions(+), 110 deletions(-)
> >>>>>>>>
> >>>>>>>> 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);
> >>>>>>>>                  }
> >>>>>>>>              }
> >>>>>>>>
> >>>>>>>> --
> >>>>>>>> 1.8.3.1
> >>>>>>>
> >>>>>>> --------------------------------------------------------------
> >>>>>>> Intel Research and Development Ireland Limited
> >>>>>>> Registered in Ireland
> >>>>>>> Registered Office: Collinstown Industrial Park, Leixlip, County Kildare
> >>>>>>> Registered Number: 308263
> >>>>>>>
> >>>>>>>
> >>>>>>> This e-mail and any attachments may contain confidential material for the sole
> >>>>>>> use of the intended recipient(s). Any review or distribution by others is
> >>>>>>> strictly prohibited. If you are not the intended recipient, please contact the
> >>>>>>> sender and delete all copies.
> >>>>>>>
> >>>>>>> _______________________________________________
> >>>>>>> dev mailing list
> >>>>>>> dev@openvswitch.org
> >>>>>>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> >>>>>> --------------------------------------------------------------
> >>>>>> Intel Research and Development Ireland Limited
> >>>>>> Registered in Ireland
> >>>>>> Registered Office: Collinstown Industrial Park, Leixlip, County Kildare
> >>>>>> Registered Number: 308263
> >>>>>>
> >>>>>>
> >>>>>> This e-mail and any attachments may contain confidential material for the sole
> >>>>>> use of the intended recipient(s). Any review or distribution by others is
> >>>>>> strictly prohibited. If you are not the intended recipient, please contact the
> >>>>>> sender and delete all copies.
> >>>>>>
> >>>>>> _______________________________________________
> >>>>>> dev mailing list
> >>>>>> dev@openvswitch.org
> >>>>>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> >>>>>>
> >>>>>
> >>>>> _______________________________________________
> >>>>> dev mailing list
> >>>>> dev@openvswitch.org
> >>>>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> >>
>
Greg Rose Sept. 15, 2017, 10:45 p.m. | #12
On 09/08/2017 06:35 AM, Weglicki, MichalX wrote:
> Greg,
> 
> Patch is rebased and sent to mailing list as V3 (Last patch was supposed to be V2 - Przemek by accident sent it again as V1).
> 
> Br,
> Michal.

Michal,

I'm not sure what has happened but I can't find your V3 patches in my mail but they are in patchwork.

I tested the patches and they seem to work fine, or at least the code executes and I didn't see any serious
regression.  My ntopng/nprobe setup accepted the new template.

However, I am seeing this now:

5/Sep/2017 15:11:16 [Lua.cpp:74] ERROR: ntop_get_interface_host_info : expected string, got nil
15/Sep/2017 15:11:16 [Lua.cpp:6658] WARNING: Script failure 
[/usr/share/ntopng/scripts/lua/iface_ndpi_stats.lua][/usr/share/ntopng/scripts/lua/iface_ndpi_stats.lua:31: attempt to index global 
'stats' (a nil value)]
15/Sep/2017 15:11:16 [Lua.cpp:74] ERROR: ntop_get_interface_host_info : expected string, got nil
15/Sep/2017 15:11:15 [Lua.cpp:6658] WARNING: Script failure 
[/usr/share/ntopng/scripts/lua/iface_ndpi_stats.lua][/usr/share/ntopng/scripts/lua/iface_ndpi_stats.lua:31: attempt to index global 
'stats' (a nil value)]
15/Sep/2017 15:11:15 [Lua.cpp:74] ERROR: ntop_get_interface_host_info : expected string, got nil
15/Sep/2017 15:11:12 [Lua.cpp:6658] WARNING: Script failure 
[/usr/share/ntopng/scripts/lua/iface_ndpi_stats.lua][/usr/share/ntopng/scripts/lua/iface_ndpi_stats.lua:31: attempt to index global 
'stats' (a nil value)]
15/Sep/2017 15:11:12 [Lua.cpp:74] ERROR: ntop_get_interface_host_info : expected string, got nil
15/Sep/2017 15:11:09 [Lua.cpp:6658] WARNING: Script failure 
[/usr/share/ntopng/scripts/lua/iface_ndpi_stats.lua][/usr/share/ntopng/scripts/lua/iface_ndpi_stats.lua:31: attempt to index global 
'stats' (a nil value)]
15/Sep/2017 15:11:09 [Lua.cpp:74] ERROR: ntop_get_interface_host_info : expected string, got nil
15/Sep/2017 15:11:09 [Lua.cpp:6658] WARNING: Script failure 
[/usr/share/ntopng/scripts/lua/iface_ndpi_stats.lua][/usr/share/ntopng/scripts/lua/iface_ndpi_stats.lua:31: attempt to index global 
'stats' (a nil value)]
15/Sep/2017 15:11:09 [Lua.cpp:74] ERROR: ntop_get_interface_host_info : expected string, got nil

Also, in patch 1/2 though the interface is hard coded to Ethernet in this bit of the patch:

+    /* Once DPDK library supports retrieving ifType we should get this value
+     * directly from DPDK rather than hardcoding it. */
+    smap_add_format(args, "if_type", "%"PRIu32, IF_TYPE_ETHERNETCSMACD);
+    smap_add_format(args, "if_descr", "%s %s", rte_version(),
+                                               dev_info.driver_name);

I'd like to get Darrel Ball's take on this so I've CC'd him.

In patch 2/2 I have some other comments.

Here you change the ports to point to all ports rather than tunnel ports

-    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;

I didn't see any reason this can't be done but I'm worried about side effects. Are we sure that
there are no other assumptions in the code that depend on that hmap pointing to only tunnel ports?
I realize that patch 2/2 seems to fix that up and I didn't see any particular reason to
doubt but I'm rather new to the code base so I thought I'd ask.

Further on I see this:

+static enum dpif_ipfix_tunnel_type
+dpif_ipfix_tunnel_type(const struct ofport *ofport) {

I think the opening curly brace should be on the next line down?  I saw at least one other instance
if this.  It passes checkpatch to put the curly brace on the same line but all other functions in
the source module put the opening curly brace of a function on the next line.

There are a couple of other checkpatch warnings and an error as well:

ERROR: Too many signoffs; are you missing Co-authored-by lines?
WARNING: Line lacks whitespace around operator
#80 FILE: ofproto/ofproto-dpif-ipfix.c:317:
     ovs_be32 if_index;     /* (INGRESS|EGRESS)_INTERFACE */

WARNING: Line lacks whitespace around operator
#81 FILE: ofproto/ofproto-dpif-ipfix.c:318:
     ovs_be32 if_type;     /* (INGRESS|EGRESS)_INTERFACE_TYPE */

Lines checked: 762, Warnings: 2, Errors: 1

The rest of patch 2/2 looks OK but I am worried about the ntop errors listed above.
I haven't seen those before so I think they're related to this patch.

Thanks for work on this!  Hopefully we can sort this all out and get your ipfix
feature enhancement applied.

Regards,

- Greg
Weglicki, MichalX Sept. 18, 2017, 10:01 a.m. | #13
Hi Greg - comments inline marked [MW]. 

> -----Original Message-----
> From: Greg Rose [mailto:gvrose8192@gmail.com]
> Sent: Saturday, September 16, 2017 12:45 AM
> To: Weglicki, MichalX <michalx.weglicki@intel.com>
> Cc: dev@openvswitch.org; Darrell Ball <dball@vmware.com>
> Subject: Re: [ovs-dev] [PATCH 2/2] ofproto-dpif-ipfix: add interface Information Elements to flow key
> 
> On 09/08/2017 06:35 AM, Weglicki, MichalX wrote:
> > Greg,
> >
> > Patch is rebased and sent to mailing list as V3 (Last patch was supposed to be V2 - Przemek by accident sent it again as V1).
> >
> > Br,
> > Michal.
> 
> Michal,
> 
> I'm not sure what has happened but I can't find your V3 patches in my mail but they are in patchwork.
> 
> I tested the patches and they seem to work fine, or at least the code executes and I didn't see any serious
> regression.  My ntopng/nprobe setup accepted the new template.
> 
> However, I am seeing this now:
> 
> 5/Sep/2017 15:11:16 [Lua.cpp:74] ERROR: ntop_get_interface_host_info : expected string, got nil
> 15/Sep/2017 15:11:16 [Lua.cpp:6658] WARNING: Script failure
> [/usr/share/ntopng/scripts/lua/iface_ndpi_stats.lua][/usr/share/ntopng/scripts/lua/iface_ndpi_stats.lua:31: attempt to index
> global
> 'stats' (a nil value)]
> 15/Sep/2017 15:11:16 [Lua.cpp:74] ERROR: ntop_get_interface_host_info : expected string, got nil
> 15/Sep/2017 15:11:15 [Lua.cpp:6658] WARNING: Script failure
> [/usr/share/ntopng/scripts/lua/iface_ndpi_stats.lua][/usr/share/ntopng/scripts/lua/iface_ndpi_stats.lua:31: attempt to index
> global
> 'stats' (a nil value)]
> 15/Sep/2017 15:11:15 [Lua.cpp:74] ERROR: ntop_get_interface_host_info : expected string, got nil
> 15/Sep/2017 15:11:12 [Lua.cpp:6658] WARNING: Script failure
> [/usr/share/ntopng/scripts/lua/iface_ndpi_stats.lua][/usr/share/ntopng/scripts/lua/iface_ndpi_stats.lua:31: attempt to index
> global
> 'stats' (a nil value)]
> 15/Sep/2017 15:11:12 [Lua.cpp:74] ERROR: ntop_get_interface_host_info : expected string, got nil
> 15/Sep/2017 15:11:09 [Lua.cpp:6658] WARNING: Script failure
> [/usr/share/ntopng/scripts/lua/iface_ndpi_stats.lua][/usr/share/ntopng/scripts/lua/iface_ndpi_stats.lua:31: attempt to index
> global
> 'stats' (a nil value)]
> 15/Sep/2017 15:11:09 [Lua.cpp:74] ERROR: ntop_get_interface_host_info : expected string, got nil
> 15/Sep/2017 15:11:09 [Lua.cpp:6658] WARNING: Script failure
> [/usr/share/ntopng/scripts/lua/iface_ndpi_stats.lua][/usr/share/ntopng/scripts/lua/iface_ndpi_stats.lua:31: attempt to index
> global
> 'stats' (a nil value)]
> 15/Sep/2017 15:11:09 [Lua.cpp:74] ERROR: ntop_get_interface_host_info : expected string, got nil
[MW] Well, are you sure that this patch is causing this error message? I mean, you don't see 
It without this patch, do you? If yes, could you explain your setup a little bit so I can reproduce it here? 

> 
> Also, in patch 1/2 though the interface is hard coded to Ethernet in this bit of the patch:
> 
> +    /* Once DPDK library supports retrieving ifType we should get this value
> +     * directly from DPDK rather than hardcoding it. */
> +    smap_add_format(args, "if_type", "%"PRIu32, IF_TYPE_ETHERNETCSMACD);
> +    smap_add_format(args, "if_descr", "%s %s", rte_version(),
> +                                               dev_info.driver_name);
> 
> I'd like to get Darrel Ball's take on this so I've CC'd him.
> 
> In patch 2/2 I have some other comments.
[MW] There are some DPDK patches which expose this information for the driver, nevertheless I think that anyway value "6" 
Will be returned anyway - as this is very general category for Ethernet devices. I could be improved in the future of course 
When patch will be available. 

> 
> Here you change the ports to point to all ports rather than tunnel ports
> 
> -    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;
> 
> I didn't see any reason this can't be done but I'm worried about side effects. Are we sure that
> there are no other assumptions in the code that depend on that hmap pointing to only tunnel ports?
> I realize that patch 2/2 seems to fix that up and I didn't see any particular reason to
> doubt but I'm rather new to the code base so I thought I'd ask.
[MW] I'm not quite sure wat do you mean here, in previous implementation only tunnel ports were
Mandatory to cache, however currently we need all ports, as we need to query particular 
Netdev to get information that we need. I don't remember exactly the details right now, but we found 
That there is no need to cache tunnel ports separately, but it could be handled through generic list
Of all ports. 

> 
> Further on I see this:
> 
> +static enum dpif_ipfix_tunnel_type
> +dpif_ipfix_tunnel_type(const struct ofport *ofport) {
> 
> I think the opening curly brace should be on the next line down?  I saw at least one other instance
> if this.  It passes checkpatch to put the curly brace on the same line but all other functions in
> the source module put the opening curly brace of a function on the next line.
[MW] True!

> 
> There are a couple of other checkpatch warnings and an error as well:
> 
> ERROR: Too many signoffs; are you missing Co-authored-by lines?
> WARNING: Line lacks whitespace around operator
> #80 FILE: ofproto/ofproto-dpif-ipfix.c:317:
>      ovs_be32 if_index;     /* (INGRESS|EGRESS)_INTERFACE */
> 
> WARNING: Line lacks whitespace around operator
> #81 FILE: ofproto/ofproto-dpif-ipfix.c:318:
>      ovs_be32 if_type;     /* (INGRESS|EGRESS)_INTERFACE_TYPE */
> 
> Lines checked: 762, Warnings: 2, Errors: 1
[MW] Sorry about that, it is my rebase patch which caused this, I will fix it in next revision. 

> 
> The rest of patch 2/2 looks OK but I am worried about the ntop errors listed above.
> I haven't seen those before so I think they're related to this patch.
[MW] I think that everything except those ntop errors is clear, so I'm waiting for your reply, 
After we will figure it out, I will create new patch version!

> 
> Thanks for work on this!  Hopefully we can sort this all out and get your ipfix
> feature enhancement applied.
> 
> Regards,
> 
> - Greg
>
Greg Rose Sept. 18, 2017, 9:43 p.m. | #14
On 09/18/2017 03:01 AM, Weglicki, MichalX wrote:
> Hi Greg - comments inline marked [MW].
> 
>> -----Original Message-----
>> From: Greg Rose [mailto:gvrose8192@gmail.com]
>> Sent: Saturday, September 16, 2017 12:45 AM
>> To: Weglicki, MichalX <michalx.weglicki@intel.com>
>> Cc: dev@openvswitch.org; Darrell Ball <dball@vmware.com>
>> Subject: Re: [ovs-dev] [PATCH 2/2] ofproto-dpif-ipfix: add interface Information Elements to flow key
>>
>> On 09/08/2017 06:35 AM, Weglicki, MichalX wrote:
>>> Greg,
>>>
>>> Patch is rebased and sent to mailing list as V3 (Last patch was supposed to be V2 - Przemek by accident sent it again as V1).
>>>
>>> Br,
>>> Michal.
>>
>> Michal,
>>
>> I'm not sure what has happened but I can't find your V3 patches in my mail but they are in patchwork.
>>
>> I tested the patches and they seem to work fine, or at least the code executes and I didn't see any serious
>> regression.  My ntopng/nprobe setup accepted the new template.
>>
>> However, I am seeing this now:
>>
>> 5/Sep/2017 15:11:16 [Lua.cpp:74] ERROR: ntop_get_interface_host_info : expected string, got nil
>> 15/Sep/2017 15:11:16 [Lua.cpp:6658] WARNING: Script failure
>> [/usr/share/ntopng/scripts/lua/iface_ndpi_stats.lua][/usr/share/ntopng/scripts/lua/iface_ndpi_stats.lua:31: attempt to index
>> global
>> 'stats' (a nil value)]
>> 15/Sep/2017 15:11:16 [Lua.cpp:74] ERROR: ntop_get_interface_host_info : expected string, got nil
>> 15/Sep/2017 15:11:15 [Lua.cpp:6658] WARNING: Script failure
>> [/usr/share/ntopng/scripts/lua/iface_ndpi_stats.lua][/usr/share/ntopng/scripts/lua/iface_ndpi_stats.lua:31: attempt to index
>> global
>> 'stats' (a nil value)]
>> 15/Sep/2017 15:11:15 [Lua.cpp:74] ERROR: ntop_get_interface_host_info : expected string, got nil
>> 15/Sep/2017 15:11:12 [Lua.cpp:6658] WARNING: Script failure
>> [/usr/share/ntopng/scripts/lua/iface_ndpi_stats.lua][/usr/share/ntopng/scripts/lua/iface_ndpi_stats.lua:31: attempt to index
>> global
>> 'stats' (a nil value)]
>> 15/Sep/2017 15:11:12 [Lua.cpp:74] ERROR: ntop_get_interface_host_info : expected string, got nil
>> 15/Sep/2017 15:11:09 [Lua.cpp:6658] WARNING: Script failure
>> [/usr/share/ntopng/scripts/lua/iface_ndpi_stats.lua][/usr/share/ntopng/scripts/lua/iface_ndpi_stats.lua:31: attempt to index
>> global
>> 'stats' (a nil value)]
>> 15/Sep/2017 15:11:09 [Lua.cpp:74] ERROR: ntop_get_interface_host_info : expected string, got nil
>> 15/Sep/2017 15:11:09 [Lua.cpp:6658] WARNING: Script failure
>> [/usr/share/ntopng/scripts/lua/iface_ndpi_stats.lua][/usr/share/ntopng/scripts/lua/iface_ndpi_stats.lua:31: attempt to index
>> global
>> 'stats' (a nil value)]
>> 15/Sep/2017 15:11:09 [Lua.cpp:74] ERROR: ntop_get_interface_host_info : expected string, got nil
> [MW] Well, are you sure that this patch is causing this error message? I mean, you don't see
> It without this patch, do you? If yes, could you explain your setup a little bit so I can reproduce it here?

It took a while but I was able to reproduce without your patches applied.  Seems to be something that
occasionally occurs soon after the ntopng/nprobe services are restarted and then it doesn't occur
again SFAICT.


> 
>>
>> Also, in patch 1/2 though the interface is hard coded to Ethernet in this bit of the patch:
>>
>> +    /* Once DPDK library supports retrieving ifType we should get this value
>> +     * directly from DPDK rather than hardcoding it. */
>> +    smap_add_format(args, "if_type", "%"PRIu32, IF_TYPE_ETHERNETCSMACD);
>> +    smap_add_format(args, "if_descr", "%s %s", rte_version(),
>> +                                               dev_info.driver_name);
>>
>> I'd like to get Darrel Ball's take on this so I've CC'd him.
>>
>> In patch 2/2 I have some other comments.
> [MW] There are some DPDK patches which expose this information for the driver, nevertheless I think that anyway value "6"
> Will be returned anyway - as this is very general category for Ethernet devices. I could be improved in the future of course
> When patch will be available.
> 
>>
>> Here you change the ports to point to all ports rather than tunnel ports
>>
>> -    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;
>>
>> I didn't see any reason this can't be done but I'm worried about side effects. Are we sure that
>> there are no other assumptions in the code that depend on that hmap pointing to only tunnel ports?
>> I realize that patch 2/2 seems to fix that up and I didn't see any particular reason to
>> doubt but I'm rather new to the code base so I thought I'd ask.
> [MW] I'm not quite sure wat do you mean here, in previous implementation only tunnel ports were
> Mandatory to cache, however currently we need all ports, as we need to query particular
> Netdev to get information that we need. I don't remember exactly the details right now, but we found
> That there is no need to cache tunnel ports separately, but it could be handled through generic list
> Of all ports.

OK, that sounds fine to me.  As I had stated, I'm new to this code base so I just wanted
to make sure that due diligence with the change had been done.  Thank you!

> 
>>
>> Further on I see this:
>>
>> +static enum dpif_ipfix_tunnel_type
>> +dpif_ipfix_tunnel_type(const struct ofport *ofport) {
>>
>> I think the opening curly brace should be on the next line down?  I saw at least one other instance
>> if this.  It passes checkpatch to put the curly brace on the same line but all other functions in
>> the source module put the opening curly brace of a function on the next line.
> [MW] True!
> 
>>
>> There are a couple of other checkpatch warnings and an error as well:
>>
>> ERROR: Too many signoffs; are you missing Co-authored-by lines?
>> WARNING: Line lacks whitespace around operator
>> #80 FILE: ofproto/ofproto-dpif-ipfix.c:317:
>>       ovs_be32 if_index;     /* (INGRESS|EGRESS)_INTERFACE */
>>
>> WARNING: Line lacks whitespace around operator
>> #81 FILE: ofproto/ofproto-dpif-ipfix.c:318:
>>       ovs_be32 if_type;     /* (INGRESS|EGRESS)_INTERFACE_TYPE */
>>
>> Lines checked: 762, Warnings: 2, Errors: 1
> [MW] Sorry about that, it is my rebase patch which caused this, I will fix it in next revision.
> 
>>
>> The rest of patch 2/2 looks OK but I am worried about the ntop errors listed above.
>> I haven't seen those before so I think they're related to this patch.
> [MW] I think that everything except those ntop errors is clear, so I'm waiting for your reply,
> After we will figure it out, I will create new patch version!

Sure, go ahead.

Thanks for your work!

- Greg

Patch

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);
             }
         }