diff mbox

[ovs-dev,V3] ipfix: Export user specified virtual observation ID

Message ID 1466813407-33296-1-git-send-email-wenyuz@vmware.com
State Accepted
Headers show

Commit Message

Wenyu Zhang June 25, 2016, 12:10 a.m. UTC
In virtual network, users want more info about the virtual point to observe the traffic.
It should be a string to provide clear info, not a simple interger ID.

Introduce "other-config: virtual_obs_id" in IPFIX, which is a string configured by user.
Introduce an enterprise IPFIX entity "virtualObsID"(898) to export the value. The entity is a
variable-length string.

Signed-off-by: Wenyu Zhang <wenyuz@vmware.com>
---
 NEWS                                  |  11 ++--
 ofproto/ipfix-enterprise-entities.def |   1 +
 ofproto/ofproto-dpif-ipfix.c          | 102 ++++++++++++++++++++++++++++++----
 ofproto/ofproto.h                     |   2 +
 vswitchd/bridge.c                     |  13 +++++
 vswitchd/vswitch.xml                  |  33 +++++++++++
 6 files changed, 147 insertions(+), 15 deletions(-)

Comments

Wenyu Zhang June 25, 2016, 12:35 a.m. UTC | #1
All tests passed except following two cases?
1055: ofproto-dpif - Bridge IPFIX statistics check    FAILED (ofproto-dpif.at<http://ofproto-dpif.at/>:5975)
1058: ofproto-dpif - Flow IPFIX statistics check      FAILED (ofproto-dpif.at<http://ofproto-dpif.at/>:6140)

The errors exist without this patch. We are looking at the errors and try to fix it later.


? 2016?6?25????8:11?Wenyu Zhang <wenyuz@vmware.com<mailto:wenyuz@vmware.com>> ???

In virtual network, users want more info about the virtual point to observe the traffic.
It should be a string to provide clear info, not a simple interger ID.

Introduce "other-config: virtual_obs_id" in IPFIX, which is a string configured by user.
Introduce an enterprise IPFIX entity "virtualObsID"(898) to export the value. The entity is a
variable-length string.

Signed-off-by: Wenyu Zhang <wenyuz@vmware.com<mailto:wenyuz@vmware.com>>
---
NEWS                                  |  11 ++--
ofproto/ipfix-enterprise-entities.def |   1 +
ofproto/ofproto-dpif-ipfix.c          | 102 ++++++++++++++++++++++++++++++----
ofproto/ofproto.h                     |   2 +
vswitchd/bridge.c                     |  13 +++++
vswitchd/vswitch.xml                  |  33 +++++++++++
6 files changed, 147 insertions(+), 15 deletions(-)
Wenyu Zhang June 25, 2016, 1:11 a.m. UTC | #2
It is caused by an environment issue.
Clear the environment and rerun the tests, all
tests passed now.

???? iPhone

? 2016?6?25????8:35?Wenyu Zhang <wenyuz@vmware.com<mailto:wenyuz@vmware.com>> ???

All tests passed except following two cases?
1055: ofproto-dpif - Bridge IPFIX statistics check    FAILED (ofproto-dpif.at<http://ofproto-dpif.at/>:5975)
1058: ofproto-dpif - Flow IPFIX statistics check      FAILED (ofproto-dpif.at<http://ofproto-dpif.at/>:6140)

The errors exist without this patch. We are looking at the errors and try to fix it later.


? 2016?6?25????8:11?Wenyu Zhang <wenyuz@vmware.com<mailto:wenyuz@vmware.com>> ???

In virtual network, users want more info about the virtual point to observe the traffic.
It should be a string to provide clear info, not a simple interger ID.

Introduce "other-config: virtual_obs_id" in IPFIX, which is a string configured by user.
Introduce an enterprise IPFIX entity "virtualObsID"(898) to export the value. The entity is a
variable-length string.

Signed-off-by: Wenyu Zhang <wenyuz@vmware.com<mailto:wenyuz@vmware.com>>
---
NEWS                                  |  11 ++--
ofproto/ipfix-enterprise-entities.def |   1 +
ofproto/ofproto-dpif-ipfix.c          | 102 ++++++++++++++++++++++++++++++----
ofproto/ofproto.h                     |   2 +
vswitchd/bridge.c                     |  13 +++++
vswitchd/vswitch.xml                  |  33 +++++++++++
6 files changed, 147 insertions(+), 15 deletions(-)
Ben Pfaff June 25, 2016, 3:56 a.m. UTC | #3
On Fri, Jun 24, 2016 at 05:10:07PM -0700, Wenyu Zhang wrote:
> In virtual network, users want more info about the virtual point to observe the traffic.
> It should be a string to provide clear info, not a simple interger ID.
> 
> Introduce "other-config: virtual_obs_id" in IPFIX, which is a string configured by user.
> Introduce an enterprise IPFIX entity "virtualObsID"(898) to export the value. The entity is a
> variable-length string.
> 
> Signed-off-by: Wenyu Zhang <wenyuz@vmware.com>

Thanks, I applied this to master.
Ben Pfaff June 25, 2016, 3:57 a.m. UTC | #4
The tests under "make check" try to be environment independent.  Do you
know what factor of your environment caused the failures?  Perhaps we
can fix it.

On Sat, Jun 25, 2016 at 01:11:04AM +0000, Wenyu Zhang wrote:
> It is caused by an environment issue.
> Clear the environment and rerun the tests, all
> tests passed now.
> 
> ???? iPhone
> 
> ? 2016?6?25????8:35?Wenyu Zhang <wenyuz@vmware.com<mailto:wenyuz@vmware.com>> ???
> 
> All tests passed except following two cases?
> 1055: ofproto-dpif - Bridge IPFIX statistics check    FAILED (ofproto-dpif.at<http://ofproto-dpif.at/>:5975)
> 1058: ofproto-dpif - Flow IPFIX statistics check      FAILED (ofproto-dpif.at<http://ofproto-dpif.at/>:6140)
> 
> The errors exist without this patch. We are looking at the errors and try to fix it later.
> 
> 
> ? 2016?6?25????8:11?Wenyu Zhang <wenyuz@vmware.com<mailto:wenyuz@vmware.com>> ???
> 
> In virtual network, users want more info about the virtual point to observe the traffic.
> It should be a string to provide clear info, not a simple interger ID.
> 
> Introduce "other-config: virtual_obs_id" in IPFIX, which is a string configured by user.
> Introduce an enterprise IPFIX entity "virtualObsID"(898) to export the value. The entity is a
> variable-length string.
> 
> Signed-off-by: Wenyu Zhang <wenyuz@vmware.com<mailto:wenyuz@vmware.com>>
> ---
> NEWS                                  |  11 ++--
> ofproto/ipfix-enterprise-entities.def |   1 +
> ofproto/ofproto-dpif-ipfix.c          | 102 ++++++++++++++++++++++++++++++----
> ofproto/ofproto.h                     |   2 +
> vswitchd/bridge.c                     |  13 +++++
> vswitchd/vswitch.xml                  |  33 +++++++++++
> 6 files changed, 147 insertions(+), 15 deletions(-)
Daniel Benli Ye June 25, 2016, 4:12 a.m. UTC | #5
Hi Ben,

It’s because of the "packet send error" statistic. The test of checking IPFIX statistics set collector as “127.0.0.1:4739”.
Test code:
-----------------------------------------------------
dnl Sample every packet using bridge-based sampling.
AT_CHECK([ovs-vsctl -- set bridge br0 ipfix=@fix -- \
                    --id=@fix create ipfix targets=\"127.0.0.1:4739\" \
                              sampling=1], [0], [ignore])
——————————————————————————

We expect “ tx errs=12” because we don’t listen on port 4739 on local host.
Test code:
-----------------------------------------------------
AT_CHECK([ovs-ofctl dump-ipfix-bridge br0], [0], [dnl
NXST_IPFIX_BRIDGE reply (xid=0x2):
  bridge ipfix: flows=20, current flows=0, sampled pkts=20, ipv4 ok=0, ipv6 ok=0, tx pkts=12
                pkts errs=20, ipv4 errs=20, ipv6 errs=0, tx errs=12
])
——————————————————————————

As I talked with Wenyu, she has listened on port 4739 for testing. If port 4739 on the local host is listened, there will
be no "tx errs”.

Should we remove "tx errs” check?

Bests,
Daniel

> On Jun 25, 2016, at 11:57 AM, Ben Pfaff <blp@ovn.org> wrote:

> 

> The tests under "make check" try to be environment independent.  Do you

> know what factor of your environment caused the failures?  Perhaps we

> can fix it.

> 

> On Sat, Jun 25, 2016 at 01:11:04AM +0000, Wenyu Zhang wrote:

>> It is caused by an environment issue.

>> Clear the environment and rerun the tests, all

>> tests passed now.

>> 

>> ???? iPhone

>> 

>> ? 2016?6?25????8:35?Wenyu Zhang <wenyuz@vmware.com<mailto:wenyuz@vmware.com>> ???

>> 

>> All tests passed except following two cases?

>> 1055: ofproto-dpif - Bridge IPFIX statistics check    FAILED (ofproto-dpif.at<https://urldefense.proofpoint.com/v2/url?u=http-3A__ofproto-2Ddpif.at_&d=CwIBAg&c=Sqcl0Ez6M0X8aeM67LKIiDJAXVeAw-YihVMNtXt-uEs&r=AMeQBzObb3Yn4XemNxgato0M1gEhd6eNH0myARLK2io&m=3tT8zWJ-n2PG-Vl1hJ7bqR6TzF0Y6kvUMsCiVFteruc&s=sYJQ7uB_IEN0lN6g1_BnSAIPN69JN8wurN_OlTAbVCY&e= >:5975)

>> 1058: ofproto-dpif - Flow IPFIX statistics check      FAILED (ofproto-dpif.at<https://urldefense.proofpoint.com/v2/url?u=http-3A__ofproto-2Ddpif.at_&d=CwIBAg&c=Sqcl0Ez6M0X8aeM67LKIiDJAXVeAw-YihVMNtXt-uEs&r=AMeQBzObb3Yn4XemNxgato0M1gEhd6eNH0myARLK2io&m=3tT8zWJ-n2PG-Vl1hJ7bqR6TzF0Y6kvUMsCiVFteruc&s=sYJQ7uB_IEN0lN6g1_BnSAIPN69JN8wurN_OlTAbVCY&e= >:6140)

>> 

>> The errors exist without this patch. We are looking at the errors and try to fix it later.

>> 

>> 

>> ? 2016?6?25????8:11?Wenyu Zhang <wenyuz@vmware.com<mailto:wenyuz@vmware.com>> ???

>> 

>> In virtual network, users want more info about the virtual point to observe the traffic.

>> It should be a string to provide clear info, not a simple interger ID.

>> 

>> Introduce "other-config: virtual_obs_id" in IPFIX, which is a string configured by user.

>> Introduce an enterprise IPFIX entity "virtualObsID"(898) to export the value. The entity is a

>> variable-length string.

>> 

>> Signed-off-by: Wenyu Zhang <wenyuz@vmware.com<mailto:wenyuz@vmware.com>>

>> ---

>> NEWS                                  |  11 ++--

>> ofproto/ipfix-enterprise-entities.def |   1 +

>> ofproto/ofproto-dpif-ipfix.c          | 102 ++++++++++++++++++++++++++++++----

>> ofproto/ofproto.h                     |   2 +

>> vswitchd/bridge.c                     |  13 +++++

>> vswitchd/vswitch.xml                  |  33 +++++++++++

>> 6 files changed, 147 insertions(+), 15 deletions(-)
Ben Pfaff June 25, 2016, 4:22 a.m. UTC | #6
On Sat, Jun 25, 2016 at 04:12:04AM +0000, Daniel Ye wrote:
> It’s because of the "packet send error" statistic. The test of checking IPFIX statistics set collector as “127.0.0.1:4739”.
> Test code:
> -----------------------------------------------------
> dnl Sample every packet using bridge-based sampling.
> AT_CHECK([ovs-vsctl -- set bridge br0 ipfix=@fix -- \
>                     --id=@fix create ipfix targets=\"127.0.0.1:4739\" \
>                               sampling=1], [0], [ignore])
> ——————————————————————————
> 
> We expect “ tx errs=12” because we don’t listen on port 4739 on local host.
> Test code:
> -----------------------------------------------------
> AT_CHECK([ovs-ofctl dump-ipfix-bridge br0], [0], [dnl
> NXST_IPFIX_BRIDGE reply (xid=0x2):
>   bridge ipfix: flows=20, current flows=0, sampled pkts=20, ipv4 ok=0, ipv6 ok=0, tx pkts=12
>                 pkts errs=20, ipv4 errs=20, ipv6 errs=0, tx errs=12
> ])
> ——————————————————————————
> 
> As I talked with Wenyu, she has listened on port 4739 for testing. If port 4739 on the local host is listened, there will
> be no "tx errs”.
> 
> Should we remove "tx errs” check?

OK, that's a bug in the test then.  The tests shouldn't depend on or
require listening on any fixed port numbers.  A lot of tests use the
PARSE_LISTENING_PORT macro to avoid the need to listen on a fixed port.
Here's the definition from ofproto-macros.at:

    # PARSE_LISTENING_PORT LOGFILE VARIABLE
    #
    # Parses the TCP or SSL port on which a server is listening from
    # LOGFILE, given that the server was told to listen on a kernel-chosen
    # port, and assigns the port number to shell VARIABLE.  You should
    # specify the listening remote as ptcp:0:127.0.0.1 or
    # pssl:0:127.0.0.1, or the equivalent with [::1] instead of 127.0.0.1.
    #
    # Here's an example of how to use this with ovsdb-server:
    #
    #    ovsdb-server --log-file --remote=ptcp:0:127.0.0.1 ...
    #    PARSE_LISTENING_PORT([ovsdb-server.log], [TCP_PORT])
    #    # Now $TCP_PORT holds the listening port.
    m4_define([PARSE_LISTENING_PORT],
        [OVS_WAIT_UNTIL([$2=`sed -n 's/.*0:.*: listening on port \([[0-9]]*\)$/\1/p' "$1"` && test X != X"[$]$2"])])

    start_daemon () {
        "$@" -vconsole:off --detach --no-chdir --pidfile --log-file
        pid=`cat "$OVS_RUNDIR"/$1.pid`
        on_exit "kill $pid"
    }

You can find several examples of its use in the tests directory.
Daniel Benli Ye June 25, 2016, 4:34 a.m. UTC | #7
OK, I got it and I will handle this.

Bests,
Daniel

> On Jun 25, 2016, at 12:22 PM, Ben Pfaff <blp@ovn.org> wrote:

> 

> On Sat, Jun 25, 2016 at 04:12:04AM +0000, Daniel Ye wrote:

>> It’s because of the "packet send error" statistic. The test of checking IPFIX statistics set collector as “127.0.0.1:4739”.

>> Test code:

>> -----------------------------------------------------

>> dnl Sample every packet using bridge-based sampling.

>> AT_CHECK([ovs-vsctl -- set bridge br0 ipfix=@fix -- \

>>                    --id=@fix create ipfix targets=\"127.0.0.1:4739\" \

>>                              sampling=1], [0], [ignore])

>> ——————————————————————————

>> 

>> We expect “ tx errs=12” because we don’t listen on port 4739 on local host.

>> Test code:

>> -----------------------------------------------------

>> AT_CHECK([ovs-ofctl dump-ipfix-bridge br0], [0], [dnl

>> NXST_IPFIX_BRIDGE reply (xid=0x2):

>>  bridge ipfix: flows=20, current flows=0, sampled pkts=20, ipv4 ok=0, ipv6 ok=0, tx pkts=12

>>                pkts errs=20, ipv4 errs=20, ipv6 errs=0, tx errs=12

>> ])

>> ——————————————————————————

>> 

>> As I talked with Wenyu, she has listened on port 4739 for testing. If port 4739 on the local host is listened, there will

>> be no "tx errs”.

>> 

>> Should we remove "tx errs” check?

> 

> OK, that's a bug in the test then.  The tests shouldn't depend on or

> require listening on any fixed port numbers.  A lot of tests use the

> PARSE_LISTENING_PORT macro to avoid the need to listen on a fixed port.

> Here's the definition from ofproto-macros.at:

> 

>    # PARSE_LISTENING_PORT LOGFILE VARIABLE

>    #

>    # Parses the TCP or SSL port on which a server is listening from

>    # LOGFILE, given that the server was told to listen on a kernel-chosen

>    # port, and assigns the port number to shell VARIABLE.  You should

>    # specify the listening remote as ptcp:0:127.0.0.1 or

>    # pssl:0:127.0.0.1, or the equivalent with [::1] instead of 127.0.0.1.

>    #

>    # Here's an example of how to use this with ovsdb-server:

>    #

>    #    ovsdb-server --log-file --remote=ptcp:0:127.0.0.1 ...

>    #    PARSE_LISTENING_PORT([ovsdb-server.log], [TCP_PORT])

>    #    # Now $TCP_PORT holds the listening port.

>    m4_define([PARSE_LISTENING_PORT],

>        [OVS_WAIT_UNTIL([$2=`sed -n 's/.*0:.*: listening on port \([[0-9]]*\)$/\1/p' "$1"` && test X != X"[$]$2"])])

> 

>    start_daemon () {

>        "$@" -vconsole:off --detach --no-chdir --pidfile --log-file

>        pid=`cat "$OVS_RUNDIR"/$1.pid`

>        on_exit "kill $pid"

>    }

> 

> You can find several examples of its use in the tests directory.
Ben Pfaff June 25, 2016, 4:47 a.m. UTC | #8
Thanks!

On Sat, Jun 25, 2016 at 04:34:49AM +0000, Daniel Ye wrote:
> OK, I got it and I will handle this.
> 
> Bests,
> Daniel
> 
> > On Jun 25, 2016, at 12:22 PM, Ben Pfaff <blp@ovn.org> wrote:
> > 
> > On Sat, Jun 25, 2016 at 04:12:04AM +0000, Daniel Ye wrote:
> >> It’s because of the "packet send error" statistic. The test of checking IPFIX statistics set collector as “127.0.0.1:4739”.
> >> Test code:
> >> -----------------------------------------------------
> >> dnl Sample every packet using bridge-based sampling.
> >> AT_CHECK([ovs-vsctl -- set bridge br0 ipfix=@fix -- \
> >>                    --id=@fix create ipfix targets=\"127.0.0.1:4739\" \
> >>                              sampling=1], [0], [ignore])
> >> ——————————————————————————
> >> 
> >> We expect “ tx errs=12” because we don’t listen on port 4739 on local host.
> >> Test code:
> >> -----------------------------------------------------
> >> AT_CHECK([ovs-ofctl dump-ipfix-bridge br0], [0], [dnl
> >> NXST_IPFIX_BRIDGE reply (xid=0x2):
> >>  bridge ipfix: flows=20, current flows=0, sampled pkts=20, ipv4 ok=0, ipv6 ok=0, tx pkts=12
> >>                pkts errs=20, ipv4 errs=20, ipv6 errs=0, tx errs=12
> >> ])
> >> ——————————————————————————
> >> 
> >> As I talked with Wenyu, she has listened on port 4739 for testing. If port 4739 on the local host is listened, there will
> >> be no "tx errs”.
> >> 
> >> Should we remove "tx errs” check?
> > 
> > OK, that's a bug in the test then.  The tests shouldn't depend on or
> > require listening on any fixed port numbers.  A lot of tests use the
> > PARSE_LISTENING_PORT macro to avoid the need to listen on a fixed port.
> > Here's the definition from ofproto-macros.at:
> > 
> >    # PARSE_LISTENING_PORT LOGFILE VARIABLE
> >    #
> >    # Parses the TCP or SSL port on which a server is listening from
> >    # LOGFILE, given that the server was told to listen on a kernel-chosen
> >    # port, and assigns the port number to shell VARIABLE.  You should
> >    # specify the listening remote as ptcp:0:127.0.0.1 or
> >    # pssl:0:127.0.0.1, or the equivalent with [::1] instead of 127.0.0.1.
> >    #
> >    # Here's an example of how to use this with ovsdb-server:
> >    #
> >    #    ovsdb-server --log-file --remote=ptcp:0:127.0.0.1 ...
> >    #    PARSE_LISTENING_PORT([ovsdb-server.log], [TCP_PORT])
> >    #    # Now $TCP_PORT holds the listening port.
> >    m4_define([PARSE_LISTENING_PORT],
> >        [OVS_WAIT_UNTIL([$2=`sed -n 's/.*0:.*: listening on port \([[0-9]]*\)$/\1/p' "$1"` && test X != X"[$]$2"])])
> > 
> >    start_daemon () {
> >        "$@" -vconsole:off --detach --no-chdir --pidfile --log-file
> >        pid=`cat "$OVS_RUNDIR"/$1.pid`
> >        on_exit "kill $pid"
> >    }
> > 
> > You can find several examples of its use in the tests directory.
>
diff mbox

Patch

diff --git a/NEWS b/NEWS
index d00b183..7aa050b 100644
--- a/NEWS
+++ b/NEWS
@@ -15,16 +15,19 @@  Post-v2.5.0
        now implemented.  Only flow mod and port mod messages are supported
        in bundles.
      * New OpenFlow extension NXM_NX_MPLS_TTL to provide access to MPLS TTL.
-     * New "sampling_port" option for "sample" action to allow sampling
-       ingress and egress tunnel metadata with IPFIX.
      * New output option, output(port=N,max_len=M), to allow truncating a
        packet to size M bytes when outputting to port N.
    - ovs-ofctl:
      * queue-get-config command now allows a queue ID to be specified.
      * '--bundle' option can now be used with OpenFlow 1.3.
      * New option "--color" to produce colorized output for some commands.
-     * New commands "dump-ipfix-bridge" and "dump-ipfix-flow" to dump bridge
-       IPFIX statistics and flow based IPFIX statistics.
+   - IPFIX:
+     * New "sampling_port" option for "sample" action to allow sampling
+       ingress and egress tunnel metadata with IPFIX.
+     * New ovs-ofctl commands "dump-ipfix-bridge" and "dump-ipfix-flow" to
+       dump bridge IPFIX statistics and flow based IPFIX statistics.
+     * New setting other-config:virtual_obs_id to add an arbitrary string
+       to IPFIX records.
    - Linux:
      * New QoS type "linux-noop" that prevents Open vSwitch from trying to
        manage QoS for a given port (useful when other software manages QoS).
diff --git a/ofproto/ipfix-enterprise-entities.def b/ofproto/ipfix-enterprise-entities.def
index ea94586..73a520c 100644
--- a/ofproto/ipfix-enterprise-entities.def
+++ b/ofproto/ipfix-enterprise-entities.def
@@ -12,5 +12,6 @@  IPFIX_ENTERPRISE_ENTITY(TUNNEL_DESTINATION_IPV4_ADDRESS, 894, 4, tunnelDestinati
 IPFIX_ENTERPRISE_ENTITY(TUNNEL_PROTOCOL_IDENTIFIER, 895, 1, tunnelProtocolIdentifier, IPFIX_ENTERPRISE_VMWARE)
 IPFIX_ENTERPRISE_ENTITY(TUNNEL_SOURCE_TRANSPORT_PORT, 896, 2, tunnelSourceTransportPort, IPFIX_ENTERPRISE_VMWARE)
 IPFIX_ENTERPRISE_ENTITY(TUNNEL_DESTINATION_TRANSPORT_PORT, 897, 2, tunnelDestinationTransportPort, IPFIX_ENTERPRISE_VMWARE)
+IPFIX_ENTERPRISE_ENTITY(VIRTUAL_OBS_ID, 898, 0, virtualObsID, IPFIX_ENTERPRISE_VMWARE)
 
 #undef IPFIX_ENTERPRISE_ENTITY
diff --git a/ofproto/ofproto-dpif-ipfix.c b/ofproto/ofproto-dpif-ipfix.c
index f166014..35f481d 100644
--- a/ofproto/ofproto-dpif-ipfix.c
+++ b/ofproto/ofproto-dpif-ipfix.c
@@ -101,6 +101,8 @@  struct dpif_ipfix_exporter {
     struct ovs_list cache_flow_start_timestamp_list;  /* ipfix_flow_cache_entry. */
     uint32_t cache_active_timeout;  /* In seconds. */
     uint32_t cache_max_flows;
+    char *virtual_obs_id;
+    uint8_t virtual_obs_len;
 
     ofproto_ipfix_stats stats;
 };
@@ -367,6 +369,32 @@  struct ipfix_data_record_aggregated_ip {
 BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_aggregated_ip) == 32);
 
 /*
+ * Refer to RFC 7011, the length of Variable length element is 0~65535:
+ * In most case, it should be less than 255 octets:
+ *  0                   1                   2                   3
+ *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *  | Length (< 255)|          Information Element                  |
+ *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *  |                      ... continuing as needed                 |
+ *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * When it is greater than or equeal to 255 octets:
+ *  0                   1                   2                   3
+ *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *  |      255      |      Length (0 to 65535)      |       IE      |
+ *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *  |                      ... continuing as needed                 |
+ *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ *
+ * Now, only the virtual_obs_id whose length < 255 is implemented.
+ */
+
+#define IPFIX_VIRTUAL_OBS_MAX_LEN 254
+
+/*
  * support tunnel key for:
  * VxLAN: 24-bit VIN,
  * GRE: 32-bit key,
@@ -436,6 +464,18 @@  static void get_export_time_now(uint64_t *, uint32_t *);
 static void dpif_ipfix_cache_expire_now(struct dpif_ipfix_exporter *, bool);
 
 static bool
+nullable_string_is_equal(const char *a, const char *b)
+{
+    return a ? b && !strcmp(a, b) : !b;
+}
+
+static char *
+nullable_xstrdup(const char *s)
+{
+    return s ? xstrdup(s) : NULL;
+}
+
+static bool
 ofproto_ipfix_bridge_exporter_options_equal(
     const struct ofproto_ipfix_bridge_exporter_options *a,
     const struct ofproto_ipfix_bridge_exporter_options *b)
@@ -448,7 +488,8 @@  ofproto_ipfix_bridge_exporter_options_equal(
             && a->enable_tunnel_sampling == b->enable_tunnel_sampling
             && a->enable_input_sampling == b->enable_input_sampling
             && a->enable_output_sampling == b->enable_output_sampling
-            && sset_equals(&a->targets, &b->targets));
+            && sset_equals(&a->targets, &b->targets)
+            && nullable_string_is_equal(a->virtual_obs_id, b->virtual_obs_id));
 }
 
 static struct ofproto_ipfix_bridge_exporter_options *
@@ -458,6 +499,7 @@  ofproto_ipfix_bridge_exporter_options_clone(
     struct ofproto_ipfix_bridge_exporter_options *new =
         xmemdup(old, sizeof *old);
     sset_clone(&new->targets, &old->targets);
+    new->virtual_obs_id = nullable_xstrdup(old->virtual_obs_id);
     return new;
 }
 
@@ -467,6 +509,7 @@  ofproto_ipfix_bridge_exporter_options_destroy(
 {
     if (options) {
         sset_destroy(&options->targets);
+        free(options->virtual_obs_id);
         free(options);
     }
 }
@@ -480,7 +523,8 @@  ofproto_ipfix_flow_exporter_options_equal(
             && a->cache_active_timeout == b->cache_active_timeout
             && a->cache_max_flows == b->cache_max_flows
             && a->enable_tunnel_sampling == b->enable_tunnel_sampling
-            && sset_equals(&a->targets, &b->targets));
+            && sset_equals(&a->targets, &b->targets)
+            && nullable_string_is_equal(a->virtual_obs_id, b->virtual_obs_id));
 }
 
 static struct ofproto_ipfix_flow_exporter_options *
@@ -490,6 +534,7 @@  ofproto_ipfix_flow_exporter_options_clone(
     struct ofproto_ipfix_flow_exporter_options *new =
         xmemdup(old, sizeof *old);
     sset_clone(&new->targets, &old->targets);
+    new->virtual_obs_id = nullable_xstrdup(old->virtual_obs_id);
     return new;
 }
 
@@ -499,6 +544,7 @@  ofproto_ipfix_flow_exporter_options_destroy(
 {
     if (options) {
         sset_destroy(&options->targets);
+        free(options->virtual_obs_id);
         free(options);
     }
 }
@@ -513,6 +559,8 @@  dpif_ipfix_exporter_init(struct dpif_ipfix_exporter *exporter)
     ovs_list_init(&exporter->cache_flow_start_timestamp_list);
     exporter->cache_active_timeout = 0;
     exporter->cache_max_flows = 0;
+    exporter->virtual_obs_id = NULL;
+    exporter->virtual_obs_len = 0;
 }
 
 static void
@@ -527,6 +575,9 @@  dpif_ipfix_exporter_clear(struct dpif_ipfix_exporter *exporter)
     exporter->last_template_set_time = 0;
     exporter->cache_active_timeout = 0;
     exporter->cache_max_flows = 0;
+    free(exporter->virtual_obs_id);
+    exporter->virtual_obs_id = NULL;
+    exporter->virtual_obs_len = 0;
 }
 
 static void
@@ -540,8 +591,10 @@  static bool
 dpif_ipfix_exporter_set_options(struct dpif_ipfix_exporter *exporter,
                                 const struct sset *targets,
                                 const uint32_t cache_active_timeout,
-                                const uint32_t cache_max_flows)
+                                const uint32_t cache_max_flows,
+                                const char *virtual_obs_id)
 {
+    size_t virtual_obs_len;
     collectors_destroy(exporter->collectors);
     collectors_create(targets, IPFIX_DEFAULT_COLLECTOR_PORT,
                       &exporter->collectors);
@@ -553,6 +606,16 @@  dpif_ipfix_exporter_set_options(struct dpif_ipfix_exporter *exporter,
     }
     exporter->cache_active_timeout = cache_active_timeout;
     exporter->cache_max_flows = cache_max_flows;
+    virtual_obs_len = virtual_obs_id ? strlen(virtual_obs_id) : 0;
+    if (virtual_obs_len > IPFIX_VIRTUAL_OBS_MAX_LEN) {
+        VLOG_WARN_RL(&rl, "Virtual obsevation ID too long (%d bytes), "
+                     "should not be longer than %d bytes.",
+                     exporter->virtual_obs_len, IPFIX_VIRTUAL_OBS_MAX_LEN);
+        dpif_ipfix_exporter_clear(exporter);
+        return false;
+    }
+    exporter->virtual_obs_len = virtual_obs_len;
+    exporter->virtual_obs_id = nullable_xstrdup(virtual_obs_id);
     return true;
 }
 
@@ -707,7 +770,8 @@  dpif_ipfix_bridge_exporter_set_options(
             < sset_count(&options->targets)) {
         if (!dpif_ipfix_exporter_set_options(
                 &exporter->exporter, &options->targets,
-                options->cache_active_timeout, options->cache_max_flows)) {
+                options->cache_active_timeout, options->cache_max_flows,
+                options->virtual_obs_id)) {
             return;
         }
     }
@@ -795,7 +859,8 @@  dpif_ipfix_flow_exporter_set_options(
             < sset_count(&options->targets)) {
         if (!dpif_ipfix_exporter_set_options(
                 &exporter->exporter, &options->targets,
-                options->cache_active_timeout, options->cache_max_flows)) {
+                options->cache_active_timeout, options->cache_max_flows,
+                options->virtual_obs_id)) {
             return false;
         }
     }
@@ -1074,6 +1139,7 @@  ipfix_define_template_entity(enum ipfix_entity_id id,
 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,
                              struct dp_packet *msg)
 {
     uint16_t count = 0;
@@ -1145,7 +1211,12 @@  ipfix_define_template_fields(enum ipfix_proto_l2 l2, enum ipfix_proto_l3 l3,
         DEF(TUNNEL_KEY);
     }
 
-    /* 2. Flow aggregated data. */
+    /* 2. Virtual observation ID, which is not a part of flow key. */
+    if (virtual_obs_id_set) {
+        DEF(VIRTUAL_OBS_ID);
+    }
+
+    /* 3. Flow aggregated data. */
 
     DEF(FLOW_START_DELTA_MICROSECONDS);
     DEF(FLOW_END_DELTA_MICROSECONDS);
@@ -1159,8 +1230,6 @@  ipfix_define_template_fields(enum ipfix_proto_l2 l2, enum ipfix_proto_l3 l3,
         DEF(MINIMUM_IP_TOTAL_LENGTH);
         DEF(MAXIMUM_IP_TOTAL_LENGTH);
     }
-
-
 #undef DEF
 
     return count;
@@ -1253,8 +1322,9 @@  ipfix_send_template_msgs(struct dpif_ipfix_exporter *exporter,
                     tmpl_hdr = dp_packet_put_zeros(&msg, sizeof *tmpl_hdr);
                     tmpl_hdr->template_id = htons(
                         ipfix_get_template_id(l2, l3, l4, tunnel));
-                    field_count =
-                        ipfix_define_template_fields(l2, l3, l4, tunnel, &msg);
+                    field_count = ipfix_define_template_fields(
+                        l2, l3, l4, tunnel, exporter->virtual_obs_id != NULL,
+                        &msg);
                     tmpl_hdr = (struct ipfix_template_record_header*)
                         ((uint8_t*)dp_packet_data(&msg) + tmpl_hdr_offset);
                     tmpl_hdr->field_count = htons(field_count);
@@ -1738,6 +1808,8 @@  static void
 ipfix_put_data_set(uint32_t export_time_sec,
                    struct ipfix_flow_cache_entry *entry,
                    enum ipfix_flow_end_reason flow_end_reason,
+                   const char *virtual_obs_id,
+                   uint8_t virtual_obs_len,
                    struct dp_packet *msg)
 {
     size_t set_hdr_offset;
@@ -1754,6 +1826,12 @@  ipfix_put_data_set(uint32_t export_time_sec,
     dp_packet_put(msg, entry->flow_key.flow_key_msg_part,
                entry->flow_key.flow_key_msg_part_size);
 
+    /* Export virtual observation ID. */
+    if (virtual_obs_id) {
+        dp_packet_put(msg, &virtual_obs_len, sizeof(virtual_obs_len));
+        dp_packet_put(msg, virtual_obs_id, virtual_obs_len);
+    }
+
     /* Put the non-key part of the data record. */
 
     {
@@ -1816,7 +1894,9 @@  ipfix_send_data_msg(struct dpif_ipfix_exporter *exporter,
 
     ipfix_init_header(export_time_sec, exporter->seq_number++,
                       entry->flow_key.obs_domain_id, &msg);
-    ipfix_put_data_set(export_time_sec, entry, flow_end_reason, &msg);
+    ipfix_put_data_set(export_time_sec, entry, flow_end_reason,
+                       exporter->virtual_obs_id, exporter->virtual_obs_len,
+                       &msg);
     tx_errors = ipfix_send_msg(exporter->collectors, &msg);
 
     dp_packet_uninit(&msg);
diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h
index 774b5b6..26c687c 100644
--- a/ofproto/ofproto.h
+++ b/ofproto/ofproto.h
@@ -82,6 +82,7 @@  struct ofproto_ipfix_bridge_exporter_options {
     bool enable_tunnel_sampling;
     bool enable_input_sampling;
     bool enable_output_sampling;
+    char *virtual_obs_id;
 };
 
 struct ofproto_ipfix_flow_exporter_options {
@@ -90,6 +91,7 @@  struct ofproto_ipfix_flow_exporter_options {
     uint32_t cache_active_timeout;
     uint32_t cache_max_flows;
     bool enable_tunnel_sampling;
+    char *virtual_obs_id;
 };
 
 struct ofproto_rstp_status {
diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
index 4273552..f8f8964 100644
--- a/vswitchd/bridge.c
+++ b/vswitchd/bridge.c
@@ -1165,6 +1165,7 @@  bridge_configure_ipfix(struct bridge *br)
     struct ofproto_ipfix_bridge_exporter_options be_opts;
     struct ofproto_ipfix_flow_exporter_options *fe_opts = NULL;
     size_t n_fe_opts = 0;
+    const char *virtual_obs_id;
 
     OVSREC_FLOW_SAMPLE_COLLECTOR_SET_FOR_EACH(fe_cfg, idl) {
         if (ovsrec_fscs_is_valid(fe_cfg, br)) {
@@ -1209,6 +1210,11 @@  bridge_configure_ipfix(struct bridge *br)
 
         be_opts.enable_output_sampling = !smap_get_bool(&be_cfg->other_config,
                                               "enable-output-sampling", false);
+
+        virtual_obs_id = smap_get(&be_cfg->other_config, "virtual_obs_id");
+        be_opts.virtual_obs_id = (virtual_obs_id
+                                  ? xstrdup(virtual_obs_id)
+                                  : NULL);
     }
 
     if (n_fe_opts > 0) {
@@ -1228,6 +1234,11 @@  bridge_configure_ipfix(struct bridge *br)
                 opts->enable_tunnel_sampling = smap_get_bool(
                                                    &fe_cfg->ipfix->other_config,
                                                   "enable-tunnel-sampling", true);
+                virtual_obs_id = smap_get(&fe_cfg->ipfix->other_config,
+                                          "virtual_obs_id");
+                opts->virtual_obs_id = (virtual_obs_id
+                                        ? xstrdup(virtual_obs_id)
+                                        : NULL);
                 opts++;
             }
         }
@@ -1238,6 +1249,7 @@  bridge_configure_ipfix(struct bridge *br)
 
     if (valid_be_cfg) {
         sset_destroy(&be_opts.targets);
+        free(be_opts.virtual_obs_id);
     }
 
     if (n_fe_opts > 0) {
@@ -1245,6 +1257,7 @@  bridge_configure_ipfix(struct bridge *br)
         size_t i;
         for (i = 0; i < n_fe_opts; i++) {
             sset_destroy(&opts->targets);
+            free(opts->virtual_obs_id);
             opts++;
         }
         free(fe_opts);
diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml
index 7ae0b51..aad4904 100644
--- a/vswitchd/vswitch.xml
+++ b/vswitchd/vswitch.xml
@@ -4747,6 +4747,39 @@ 
       </p>
     </column>
 
+    <column name="other_config" key="virtual_obs_id"
+            type='{"type": "string"}'>
+      <p>
+        A string that accompanies each IPFIX flow record.  Its intended use is
+        for the ``virtual observation ID,'' an identifier of a virtual
+        observation point that is locally unique in a virtual network.  It
+        describes a location in the virtual network where IP packets can be
+        observed.  The maximum length is 254 bytes.  If not specified, the
+        field is omitted from the IPFIX flow record.
+      </p>
+
+      <p>
+        The following enterprise entity reports the specified virtual
+        observation ID:
+      </p>
+
+      <dl>
+        <dt>virtualObsID:</dt>
+        <dd>
+          <p>ID: 898, and enterprise ID 6876 (VMware).</p>
+          <p>type: variable-length string.</p>
+          <p>data type semantics: identifier.</p>
+          <p>description: A virtual observation domain ID that is locally
+          unique in a virtual network.
+          </p>
+        </dd>
+      </dl>
+
+      <p>
+        This feature was introduced in Open vSwitch 2.5.90.
+      </p>
+    </column>
+
     <group title="Per-Bridge Sampling">
       <p>
         These values affect only per-bridge sampling.  See above for a