[ovs-dev] [PATCH] test suite : add sFlow test

Neil Mckee neil.mckee at inmon.com
Sat Mar 30 03:39:42 UTC 2013


On Mar 29, 2013, at 4:16 PM, Ben Pfaff wrote:

> On Wed, Mar 27, 2013 at 11:02:21PM -0700, Neil Mckee wrote:
>> This patch adds an sFlow test to the test suite (in branch 1.10).  To make that work properly I added netdev_dummy_get_ifindex() so that a dummy netdev can return a dummy ifindex when asked.   Is there anywhere in OVS that assumes that a netdev_dummy cannot make up a dummy ifindex?  If so, I guess this behavior could be off by default and turned on just for this test.
>> 
>> I have only tested this on a Fedora 17 OS.
>> 
>> Signed-off-by: Neil McKee <neil.mckee at inmon.com>
>> 
>> ---
> 
> Thanks for the test!
> 
> I noticed that the new test-sflow.c has the Apache 2 boilerplate
> license text in it, which is fine, but the copyright notice should
> probably mention InMon in place of or in addition to Nicira, since a
> lot of the code in test-sflow.c is not written by Nicira.  Neil, can
> you give me a correct copyright notice for that file?
> 

How about we add the same copyright notice that appears at the top of all the lib/sflow* source files (just updating the year to 2013)?

> Running this, I had some trouble with ifindexes.  As written, the
> ifindex that each dummy device receives depends on the order in which
> they are created.  That, in turn, depends on hash order in the
> database and other factors.  I decided to fix it by making the ifindex
> a configurable property of the dummy devices, applying the following
> incremental patch.  Does that make sense?
> 

Yes.  Much better.  I had started to do the same thing but baulked at the number of lines I was adding.

> ----------------------------------------------------------------------
> diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c
> index 04fd8b2..bdb3ea1 100644
> --- a/lib/netdev-dummy.c
> +++ b/lib/netdev-dummy.c
> @@ -98,7 +98,6 @@ netdev_dummy_create(const struct netdev_class *class, const char *name,
>                     struct netdev_dev **netdev_devp)
> {
>     static unsigned int n = 0xaa550000;
> -    static unsigned int next_dummy_ifindex = 1001;
>     struct netdev_dev_dummy *netdev_dev;
> 
>     netdev_dev = xzalloc(sizeof *netdev_dev);
> @@ -112,6 +111,7 @@ netdev_dummy_create(const struct netdev_class *class, const char *name,
>     netdev_dev->mtu = 1500;
>     netdev_dev->flags = 0;
>     netdev_dev->change_seq = 1;
> +    netdev_dev->ifindex = -EOPNOTSUPP;
>     list_init(&netdev_dev->devs);
> 
>     shash_add(&dummy_netdev_devs, name, netdev_dev);
> @@ -120,8 +120,6 @@ netdev_dummy_create(const struct netdev_class *class, const char *name,
> 
>     *netdev_devp = &netdev_dev->netdev_dev;
> 
> -    netdev_dev->ifindex = next_dummy_ifindex++;
> -
>     return 0;
> }
> 
> @@ -136,6 +134,27 @@ netdev_dummy_destroy(struct netdev_dev *netdev_dev_)
> }
> 
> static int
> +netdev_dummy_get_config(struct netdev_dev *netdev_dev_, struct smap *args)
> +{
> +    struct netdev_dev_dummy *netdev_dev = netdev_dev_dummy_cast(netdev_dev_);
> +
> +    if (netdev_dev->ifindex >= 0) {
> +        smap_add_format(args, "ifindex", "%d", netdev_dev->ifindex);
> +    }
> +    return 0;
> +}
> +
> +static int
> +netdev_dummy_set_config(struct netdev_dev *netdev_dev_,
> +                        const struct smap *args)
> +{
> +    struct netdev_dev_dummy *netdev_dev = netdev_dev_dummy_cast(netdev_dev_);
> +
> +    netdev_dev->ifindex = smap_get_int(args, "ifindex", -EOPNOTSUPP);
> +    return 0;
> +}
> +
> +static int
> netdev_dummy_open(struct netdev_dev *netdev_dev_, struct netdev **netdevp)
> {
>     struct netdev_dev_dummy *netdev_dev = netdev_dev_dummy_cast(netdev_dev_);
> @@ -350,8 +369,8 @@ static const struct netdev_class dummy_class = {
> 
>     netdev_dummy_create,
>     netdev_dummy_destroy,
> -    NULL,                       /* get_config */
> -    NULL,                       /* set_config */
> +    netdev_dummy_get_config,
> +    netdev_dummy_set_config,
>     NULL,                       /* get_tunnel_config */
> 
>     netdev_dummy_open,
> @@ -369,7 +388,7 @@ static const struct netdev_class dummy_class = {
>     netdev_dummy_get_etheraddr,
>     netdev_dummy_get_mtu,
>     netdev_dummy_set_mtu,
> -    netdev_dummy_get_ifindex,   /* get_ifindex */
> +    netdev_dummy_get_ifindex,
>     NULL,                       /* get_carrier */
>     NULL,                       /* get_carrier_resets */
>     NULL,                       /* get_miimon */
> diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
> index e9d52bb..7e12f2d 100644
> --- a/tests/ofproto-dpif.at
> +++ b/tests/ofproto-dpif.at
> @@ -1213,6 +1213,9 @@ SFLOW_PORT=`cat stdout`
> OVS_VSWITCHD_START([set Bridge br0 fail-mode=standalone])
> ADD_OF_PORTS([br0], 1, 2)
> ovs-vsctl \
> +   set Interface br0 options:ifindex=1002 -- \
> +   set Interface p1 options:ifindex=1004 -- \
> +   set Interface p2 options:ifindex=1003 -- \
>    set Bridge br0 sflow=@sf -- \
>    --id=@sf create sflow targets=\"127.0.0.1:$SFLOW_PORT\" \
>      header=128 sampling=1 polling=1
> ----------------------------------------------------------------------
> 
> At that point, I noticed that there was timing sensitivity.  Applying
> the following incremental fixed that:
> 
> ----------------------------------------------------------------------
> diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
> index 7e12f2d..3804b1a 100644
> --- a/tests/ofproto-dpif.at
> +++ b/tests/ofproto-dpif.at
> @@ -1211,6 +1211,9 @@ AT_SETUP([ofproto-dpif - sFlow packet sampling])
> AT_CHECK([perl $srcdir/choose-port.pl], [0], [stdout])
> SFLOW_PORT=`cat stdout`
> OVS_VSWITCHD_START([set Bridge br0 fail-mode=standalone])
> +
> +ovs-appctl time/stop
> +
> ADD_OF_PORTS([br0], 1, 2)
> ovs-vsctl \
>    set Interface br0 options:ifindex=1002 -- \
> @@ -1239,7 +1242,9 @@ ovs-appctl netdev-dummy/receive p2 'in_port(1),eth(src=50:54:00:00:00:07,dst=50:
> 
> dnl sleep long enough to get more than one counter sample
> dnl from each datasource so we can check sequence numbers
> -sleep 3
> +for i in `seq 1 30`; do
> +    ovs-appctl time/warp 100
> +done
> OVS_VSWITCHD_STOP
> ovs-appctl -t test-sflow exit
> ----------------------------------------------------------------------
> 
> Even after I applied those changes, the test still didn't pass for me.
> When I looked more closely, some of the expected output didn't
> entirely make sense.  For example, my reading of the sflow spec says
> that samplePool should more or less count upward, which is what I
> actually saw in the test output, but the expected output provided in
> the patch shows all the samplePool values as 0.  Some of the other
> expected output needed to be adjusted too.
> 

I wonder why your samplePool numbers are incrementing and mine are not?
The samplePool is supplied in ofproto-dpif-sflow.c:dpif-sflow-received():

   fs.sample_pool = stats.rx_packets;

So I was assuming that since netdev-dummy doesn't seem to increment any of if's interface-counter stats then that would account for rx_packets being always 0.  Does this vary depending on the presence or absence of the kernel module,  or something like that?

> While I was comparing the expected output, I realized that it was much
> easier to read the diffs if each packet was described on multiple
> lines, since diffs of very long lines with few differences are hard to
> read.  So I ended up applying the following incremental that both
> makes the test pass for me and makes diff output easier to read:
> 

OK.

I was thinking that we should probably also change "grep HEADER" to something like "egrep 'HEADER|ERROR' to make certain than any exception flagged in the sflow-test.c code is certain to get through and break the test.   Same thing for "grep IFCOUNTERS".    I hesitated because I don't know how portable that egrep construct is.

Just two minor questions left:

1) I wanted to craft a test packet with an odd-numbered length between 64 and 128 bytes,  and another with > 128 bytes.  Where should I look for documentation on that packet-construction language?  Can I specify something like ipv6-over-vxlan-over-ipv4-over-mpls?   I'm not concerned about this for now,  just thinking about future enhancements to the test.

2) Are you really going to let me away with 2-character indentation in C?

Regards,
Neil


> ----------------------------------------------------------------------
> diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
> index 3804b1a..497b463 100644
> --- a/tests/ofproto-dpif.at
> +++ b/tests/ofproto-dpif.at
> @@ -1248,21 +1248,250 @@ done
> OVS_VSWITCHD_STOP
> ovs-appctl -t test-sflow exit
> 
> -AT_CHECK([[sort sflow.log | grep HEADER]], [0],
> -  [HEADER dgramSeqNo=1 ds=127.0.0.1>0:1003 fsSeqNo=1 in_vlan=0 in_priority=0 out_vlan=0 out_priority=0 meanSkip=1 samplePool=0 dropEvents=0 in_ifindex=1003 in_format=0 out_ifindex=2 out_format=2 hdr_prot=1 pkt_len=64 stripped=4 hdr_len=60 hdr=FF-FF-FF-FF-FF-FF-50-54-00-00-00-07-08-06-00-01-08-00-06-04-00-01-50-54-00-00-00-07-C0-A8-00-01-00-00-00-00-00-00-C0-A8-00-02-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
> -HEADER dgramSeqNo=1 ds=127.0.0.1>0:1003 fsSeqNo=2 in_vlan=0 in_priority=0 out_vlan=0 out_priority=0 meanSkip=1 samplePool=0 dropEvents=0 in_ifindex=1003 in_format=0 out_ifindex=1004 out_format=0 hdr_prot=1 pkt_len=64 stripped=4 hdr_len=60 hdr=50-54-00-00-00-05-50-54-00-00-00-07-08-00-45-00-00-1C-00-00-00-00-40-01-F9-8D-C0-A8-00-02-C0-A8-00-01-00-00-FF-FF-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
> -HEADER dgramSeqNo=1 ds=127.0.0.1>0:1003 fsSeqNo=3 in_vlan=0 in_priority=0 out_vlan=0 out_priority=0 meanSkip=1 samplePool=0 dropEvents=0 in_ifindex=1003 in_format=0 out_ifindex=1004 out_format=0 hdr_prot=1 pkt_len=64 stripped=4 hdr_len=60 hdr=50-54-00-00-00-05-50-54-00-00-00-07-86-DD-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
> -HEADER dgramSeqNo=1 ds=127.0.0.1>0:1004 fsSeqNo=1 in_vlan=0 in_priority=0 out_vlan=0 out_priority=0 meanSkip=1 samplePool=0 dropEvents=0 in_ifindex=1004 in_format=0 out_ifindex=2 out_format=2 hdr_prot=1 pkt_len=64 stripped=4 hdr_len=60 hdr=FF-FF-FF-FF-FF-FF-50-54-00-00-00-05-08-06-00-01-08-00-06-04-00-01-50-54-00-00-00-05-C0-A8-00-02-00-00-00-00-00-00-C0-A8-00-01-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
> -HEADER dgramSeqNo=1 ds=127.0.0.1>0:1004 fsSeqNo=2 in_vlan=0 in_priority=0 out_vlan=0 out_priority=0 meanSkip=1 samplePool=0 dropEvents=0 in_ifindex=1004 in_format=0 out_ifindex=1003 out_format=0 hdr_prot=1 pkt_len=64 stripped=4 hdr_len=60 hdr=50-54-00-00-00-07-50-54-00-00-00-05-08-00-45-00-00-1C-00-00-00-00-40-01-F9-8D-C0-A8-00-01-C0-A8-00-02-08-00-F7-FF-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
> -])
> -
> -AT_CHECK([[sort sflow.log | grep IFCOUNTERS | head -6]], [0],
> -  [IFCOUNTERS dgramSeqNo=2 ds=127.0.0.1>0:1002 csSeqNo=1 ifindex=1002 type=6 ifspeed=100000000 direction=0 status=3 in_octets=0 in_unicasts=0 in_multicasts=0 in_broadcasts=4294967295 in_discards=0 in_errors=0 in_unknownprotos=4294967295 out_octets=0 out_unicasts=0 out_multicasts=4294967295 out_broadcasts=4294967295 out_discards=0 out_errors=0 promiscuous=0
> -IFCOUNTERS dgramSeqNo=2 ds=127.0.0.1>0:1003 csSeqNo=1 ifindex=1003 type=6 ifspeed=100000000 direction=0 status=0 in_octets=0 in_unicasts=0 in_multicasts=0 in_broadcasts=4294967295 in_discards=0 in_errors=0 in_unknownprotos=4294967295 out_octets=0 out_unicasts=0 out_multicasts=4294967295 out_broadcasts=4294967295 out_discards=0 out_errors=0 promiscuous=0
> -IFCOUNTERS dgramSeqNo=2 ds=127.0.0.1>0:1004 csSeqNo=1 ifindex=1004 type=6 ifspeed=100000000 direction=0 status=0 in_octets=0 in_unicasts=0 in_multicasts=0 in_broadcasts=4294967295 in_discards=0 in_errors=0 in_unknownprotos=4294967295 out_octets=0 out_unicasts=0 out_multicasts=4294967295 out_broadcasts=4294967295 out_discards=0 out_errors=0 promiscuous=0
> -IFCOUNTERS dgramSeqNo=3 ds=127.0.0.1>0:1002 csSeqNo=2 ifindex=1002 type=6 ifspeed=100000000 direction=0 status=3 in_octets=0 in_unicasts=0 in_multicasts=0 in_broadcasts=4294967295 in_discards=0 in_errors=0 in_unknownprotos=4294967295 out_octets=0 out_unicasts=0 out_multicasts=4294967295 out_broadcasts=4294967295 out_discards=0 out_errors=0 promiscuous=0
> -IFCOUNTERS dgramSeqNo=3 ds=127.0.0.1>0:1003 csSeqNo=2 ifindex=1003 type=6 ifspeed=100000000 direction=0 status=0 in_octets=0 in_unicasts=0 in_multicasts=0 in_broadcasts=4294967295 in_discards=0 in_errors=0 in_unknownprotos=4294967295 out_octets=0 out_unicasts=0 out_multicasts=4294967295 out_broadcasts=4294967295 out_discards=0 out_errors=0 promiscuous=0
> -IFCOUNTERS dgramSeqNo=3 ds=127.0.0.1>0:1004 csSeqNo=2 ifindex=1004 type=6 ifspeed=100000000 direction=0 status=0 in_octets=0 in_unicasts=0 in_multicasts=0 in_broadcasts=4294967295 in_discards=0 in_errors=0 in_unknownprotos=4294967295 out_octets=0 out_unicasts=0 out_multicasts=4294967295 out_broadcasts=4294967295 out_discards=0 out_errors=0 promiscuous=0
> +AT_CHECK([[sort sflow.log | grep HEADER | sed 's/ /\
> +	/g']], [0], [dnl
> +HEADER
> +	dgramSeqNo=1
> +	ds=127.0.0.1>0:1003
> +	fsSeqNo=1
> +	in_vlan=0
> +	in_priority=0
> +	out_vlan=0
> +	out_priority=0
> +	meanSkip=1
> +	samplePool=1
> +	dropEvents=0
> +	in_ifindex=1003
> +	in_format=0
> +	out_ifindex=2
> +	out_format=2
> +	hdr_prot=1
> +	pkt_len=64
> +	stripped=4
> +	hdr_len=60
> +	hdr=FF-FF-FF-FF-FF-FF-50-54-00-00-00-07-08-06-00-01-08-00-06-04-00-01-50-54-00-00-00-07-C0-A8-00-01-00-00-00-00-00-00-C0-A8-00-02-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
> +HEADER
> +	dgramSeqNo=1
> +	ds=127.0.0.1>0:1003
> +	fsSeqNo=2
> +	in_vlan=0
> +	in_priority=0
> +	out_vlan=0
> +	out_priority=0
> +	meanSkip=1
> +	samplePool=2
> +	dropEvents=0
> +	in_ifindex=1003
> +	in_format=0
> +	out_ifindex=1004
> +	out_format=0
> +	hdr_prot=1
> +	pkt_len=64
> +	stripped=4
> +	hdr_len=60
> +	hdr=50-54-00-00-00-05-50-54-00-00-00-07-08-00-45-00-00-1C-00-00-00-00-40-01-F9-8D-C0-A8-00-02-C0-A8-00-01-00-00-FF-FF-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
> +HEADER
> +	dgramSeqNo=1
> +	ds=127.0.0.1>0:1003
> +	fsSeqNo=3
> +	in_vlan=0
> +	in_priority=0
> +	out_vlan=0
> +	out_priority=0
> +	meanSkip=1
> +	samplePool=3
> +	dropEvents=0
> +	in_ifindex=1003
> +	in_format=0
> +	out_ifindex=1004
> +	out_format=0
> +	hdr_prot=1
> +	pkt_len=64
> +	stripped=4
> +	hdr_len=60
> +	hdr=50-54-00-00-00-05-50-54-00-00-00-07-86-DD-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
> +HEADER
> +	dgramSeqNo=1
> +	ds=127.0.0.1>0:1004
> +	fsSeqNo=1
> +	in_vlan=0
> +	in_priority=0
> +	out_vlan=0
> +	out_priority=0
> +	meanSkip=1
> +	samplePool=1
> +	dropEvents=0
> +	in_ifindex=1004
> +	in_format=0
> +	out_ifindex=2
> +	out_format=2
> +	hdr_prot=1
> +	pkt_len=64
> +	stripped=4
> +	hdr_len=60
> +	hdr=FF-FF-FF-FF-FF-FF-50-54-00-00-00-05-08-06-00-01-08-00-06-04-00-01-50-54-00-00-00-05-C0-A8-00-02-00-00-00-00-00-00-C0-A8-00-01-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
> +HEADER
> +	dgramSeqNo=1
> +	ds=127.0.0.1>0:1004
> +	fsSeqNo=2
> +	in_vlan=0
> +	in_priority=0
> +	out_vlan=0
> +	out_priority=0
> +	meanSkip=1
> +	samplePool=2
> +	dropEvents=0
> +	in_ifindex=1004
> +	in_format=0
> +	out_ifindex=1003
> +	out_format=0
> +	hdr_prot=1
> +	pkt_len=64
> +	stripped=4
> +	hdr_len=60
> +	hdr=50-54-00-00-00-07-50-54-00-00-00-05-08-00-45-00-00-1C-00-00-00-00-40-01-F9-8D-C0-A8-00-01-C0-A8-00-02-08-00-F7-FF-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
> +])
> +
> +AT_CHECK([[sort sflow.log | grep IFCOUNTERS | head -6 | sed 's/ /\
> +	/g']], [0], [dnl
> +IFCOUNTERS
> +	dgramSeqNo=2
> +	ds=127.0.0.1>0:1002
> +	csSeqNo=1
> +	ifindex=1002
> +	type=6
> +	ifspeed=100000000
> +	direction=0
> +	status=3
> +	in_octets=0
> +	in_unicasts=0
> +	in_multicasts=0
> +	in_broadcasts=4294967295
> +	in_discards=0
> +	in_errors=0
> +	in_unknownprotos=4294967295
> +	out_octets=120
> +	out_unicasts=2
> +	out_multicasts=4294967295
> +	out_broadcasts=4294967295
> +	out_discards=0
> +	out_errors=0
> +	promiscuous=0
> +IFCOUNTERS
> +	dgramSeqNo=2
> +	ds=127.0.0.1>0:1003
> +	csSeqNo=1
> +	ifindex=1003
> +	type=6
> +	ifspeed=100000000
> +	direction=0
> +	status=0
> +	in_octets=98
> +	in_unicasts=3
> +	in_multicasts=0
> +	in_broadcasts=4294967295
> +	in_discards=0
> +	in_errors=0
> +	in_unknownprotos=4294967295
> +	out_octets=120
> +	out_unicasts=2
> +	out_multicasts=4294967295
> +	out_broadcasts=4294967295
> +	out_discards=0
> +	out_errors=0
> +	promiscuous=0
> +IFCOUNTERS
> +	dgramSeqNo=2
> +	ds=127.0.0.1>0:1004
> +	csSeqNo=1
> +	ifindex=1004
> +	type=6
> +	ifspeed=100000000
> +	direction=0
> +	status=0
> +	in_octets=84
> +	in_unicasts=2
> +	in_multicasts=0
> +	in_broadcasts=4294967295
> +	in_discards=0
> +	in_errors=0
> +	in_unknownprotos=4294967295
> +	out_octets=180
> +	out_unicasts=3
> +	out_multicasts=4294967295
> +	out_broadcasts=4294967295
> +	out_discards=0
> +	out_errors=0
> +	promiscuous=0
> +IFCOUNTERS
> +	dgramSeqNo=3
> +	ds=127.0.0.1>0:1002
> +	csSeqNo=2
> +	ifindex=1002
> +	type=6
> +	ifspeed=100000000
> +	direction=0
> +	status=3
> +	in_octets=0
> +	in_unicasts=0
> +	in_multicasts=0
> +	in_broadcasts=4294967295
> +	in_discards=0
> +	in_errors=0
> +	in_unknownprotos=4294967295
> +	out_octets=120
> +	out_unicasts=2
> +	out_multicasts=4294967295
> +	out_broadcasts=4294967295
> +	out_discards=0
> +	out_errors=0
> +	promiscuous=0
> +IFCOUNTERS
> +	dgramSeqNo=3
> +	ds=127.0.0.1>0:1003
> +	csSeqNo=2
> +	ifindex=1003
> +	type=6
> +	ifspeed=100000000
> +	direction=0
> +	status=0
> +	in_octets=98
> +	in_unicasts=3
> +	in_multicasts=0
> +	in_broadcasts=4294967295
> +	in_discards=0
> +	in_errors=0
> +	in_unknownprotos=4294967295
> +	out_octets=120
> +	out_unicasts=2
> +	out_multicasts=4294967295
> +	out_broadcasts=4294967295
> +	out_discards=0
> +	out_errors=0
> +	promiscuous=0
> +IFCOUNTERS
> +	dgramSeqNo=3
> +	ds=127.0.0.1>0:1004
> +	csSeqNo=2
> +	ifindex=1004
> +	type=6
> +	ifspeed=100000000
> +	direction=0
> +	status=0
> +	in_octets=84
> +	in_unicasts=2
> +	in_multicasts=0
> +	in_broadcasts=4294967295
> +	in_discards=0
> +	in_errors=0
> +	in_unknownprotos=4294967295
> +	out_octets=180
> +	out_unicasts=3
> +	out_multicasts=4294967295
> +	out_broadcasts=4294967295
> +	out_discards=0
> +	out_errors=0
> +	promiscuous=0
> ])
> AT_CLEANUP
> ----------------------------------------------------------------------
> 
> With that change, this test passes for me.
> 
> Here's the full revised patch.
> 
> --8<--------------------------cut here-------------------------->8--
> 
> From: Neil Mckee <neil.mckee at inmon.com>
> Date: Wed, 27 Mar 2013 23:02:21 -0700
> Subject: [PATCH] test suite : add sFlow test
> 
> This patch adds an sFlow test to the test suite (in branch 1.10).  To make that work properly I added netdev_dummy_get_ifindex() so that a dummy netdev can return a dummy ifindex when asked.   Is there anywhere in OVS that assumes that a netdev_dummy cannot make up a dummy ifindex?  If so, I guess this behavior could be off by default and turned on just for this test.
> 
> I have only tested this on a Fedora 17 OS.
> 
> Signed-off-by: Neil McKee <neil.mckee at inmon.com>
> Signed-off-by: Ben Pfaff <blp at nicira.com>
> ---
> lib/netdev-dummy.c    |   38 ++++-
> tests/automake.mk     |    4 +
> tests/ofproto-dpif.at |  291 ++++++++++++++++++++++++++
> tests/test-sflow.c    |  539 +++++++++++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 869 insertions(+), 3 deletions(-)
> create mode 100644 tests/test-sflow.c
> 
> diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c
> index 234d7bc..bdb3ea1 100644
> --- a/lib/netdev-dummy.c
> +++ b/lib/netdev-dummy.c
> @@ -51,6 +51,7 @@ struct netdev_dev_dummy {
>     unsigned int change_seq;
> 
>     struct list devs;           /* List of child "netdev_dummy"s. */
> +    int ifindex;
> };
> 
> struct netdev_dummy {
> @@ -110,6 +111,7 @@ netdev_dummy_create(const struct netdev_class *class, const char *name,
>     netdev_dev->mtu = 1500;
>     netdev_dev->flags = 0;
>     netdev_dev->change_seq = 1;
> +    netdev_dev->ifindex = -EOPNOTSUPP;
>     list_init(&netdev_dev->devs);
> 
>     shash_add(&dummy_netdev_devs, name, netdev_dev);
> @@ -132,6 +134,27 @@ netdev_dummy_destroy(struct netdev_dev *netdev_dev_)
> }
> 
> static int
> +netdev_dummy_get_config(struct netdev_dev *netdev_dev_, struct smap *args)
> +{
> +    struct netdev_dev_dummy *netdev_dev = netdev_dev_dummy_cast(netdev_dev_);
> +
> +    if (netdev_dev->ifindex >= 0) {
> +        smap_add_format(args, "ifindex", "%d", netdev_dev->ifindex);
> +    }
> +    return 0;
> +}
> +
> +static int
> +netdev_dummy_set_config(struct netdev_dev *netdev_dev_,
> +                        const struct smap *args)
> +{
> +    struct netdev_dev_dummy *netdev_dev = netdev_dev_dummy_cast(netdev_dev_);
> +
> +    netdev_dev->ifindex = smap_get_int(args, "ifindex", -EOPNOTSUPP);
> +    return 0;
> +}
> +
> +static int
> netdev_dummy_open(struct netdev_dev *netdev_dev_, struct netdev **netdevp)
> {
>     struct netdev_dev_dummy *netdev_dev = netdev_dev_dummy_cast(netdev_dev_);
> @@ -284,6 +307,15 @@ netdev_dummy_set_stats(struct netdev *netdev, const struct netdev_stats *stats)
> }
> 
> static int
> +netdev_dummy_get_ifindex(const struct netdev *netdev)
> +{
> +    struct netdev_dev_dummy *dev =
> +        netdev_dev_dummy_cast(netdev_get_dev(netdev));
> +
> +    return dev->ifindex;
> +}
> +
> +static int
> netdev_dummy_update_flags(struct netdev *netdev,
>                           enum netdev_flags off, enum netdev_flags on,
>                           enum netdev_flags *old_flagsp)
> @@ -337,8 +369,8 @@ static const struct netdev_class dummy_class = {
> 
>     netdev_dummy_create,
>     netdev_dummy_destroy,
> -    NULL,                       /* get_config */
> -    NULL,                       /* set_config */
> +    netdev_dummy_get_config,
> +    netdev_dummy_set_config,
>     NULL,                       /* get_tunnel_config */
> 
>     netdev_dummy_open,
> @@ -356,7 +388,7 @@ static const struct netdev_class dummy_class = {
>     netdev_dummy_get_etheraddr,
>     netdev_dummy_get_mtu,
>     netdev_dummy_set_mtu,
> -    NULL,                       /* get_ifindex */
> +    netdev_dummy_get_ifindex,
>     NULL,                       /* get_carrier */
>     NULL,                       /* get_carrier_resets */
>     NULL,                       /* get_miimon */
> diff --git a/tests/automake.mk b/tests/automake.mk
> index b11e0a2..275ff53 100644
> --- a/tests/automake.mk
> +++ b/tests/automake.mk
> @@ -232,6 +232,10 @@ noinst_PROGRAMS += tests/test-stp
> tests_test_stp_SOURCES = tests/test-stp.c
> tests_test_stp_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
> 
> +noinst_PROGRAMS += tests/test-sflow
> +tests_test_sflow_SOURCES = tests/test-sflow.c
> +tests_test_sflow_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
> +
> noinst_PROGRAMS += tests/test-netflow
> tests_test_netflow_SOURCES = tests/test-netflow.c
> tests_test_netflow_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
> diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
> index 96b166e..497b463 100644
> --- a/tests/ofproto-dpif.at
> +++ b/tests/ofproto-dpif.at
> @@ -1206,6 +1206,297 @@ AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/[[0-9]]\{1,\}$/?/' | sort],
> OVS_VSWITCHD_STOP
> AT_CLEANUP
> 
> +dnl Test that sFlow samples packets correctly.
> +AT_SETUP([ofproto-dpif - sFlow packet sampling])
> +AT_CHECK([perl $srcdir/choose-port.pl], [0], [stdout])
> +SFLOW_PORT=`cat stdout`
> +OVS_VSWITCHD_START([set Bridge br0 fail-mode=standalone])
> +
> +ovs-appctl time/stop
> +
> +ADD_OF_PORTS([br0], 1, 2)
> +ovs-vsctl \
> +   set Interface br0 options:ifindex=1002 -- \
> +   set Interface p1 options:ifindex=1004 -- \
> +   set Interface p2 options:ifindex=1003 -- \
> +   set Bridge br0 sflow=@sf -- \
> +   --id=@sf create sflow targets=\"127.0.0.1:$SFLOW_PORT\" \
> +     header=128 sampling=1 polling=1
> +ON_EXIT([kill `cat test-sflow.pid`])
> +AT_CHECK([test-sflow --detach --no-chdir --pidfile $SFLOW_PORT:127.0.0.1 > sflow.log])
> +AT_CAPTURE_FILE([sflow.log])
> +
> +dnl open with ARP packets to seed the bridge-learning.  The output
> +dnl ifIndex numbers should be reported predictably after that.
> +dnl Since we set sampling=1 we should see all of these packets
> +dnl reported. Sorting the output by data-source and seqNo makes
> +dnl it deterministic. Ensuring that we send at least two packets
> +dnl into each port means we get to check the seq nos are
> +dnl incrementing correctly.
> +
> +ovs-appctl netdev-dummy/receive p1 'in_port(2),eth(src=50:54:00:00:00:05,dst=FF:FF:FF:FF:FF:FF),eth_type(0x0806),arp(sip=192.168.0.2,tip=192.168.0.1,op=1,sha=50:54:00:00:00:05,tha=00:00:00:00:00:00)'
> +ovs-appctl netdev-dummy/receive p2 'in_port(1),eth(src=50:54:00:00:00:07,dst=FF:FF:FF:FF:FF:FF),eth_type(0x0806),arp(sip=192.168.0.1,tip=192.168.0.2,op=1,sha=50:54:00:00:00:07,tha=00:00:00:00:00:00)'
> +ovs-appctl netdev-dummy/receive p1 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'
> +ovs-appctl netdev-dummy/receive p2 'in_port(1),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0)'
> +ovs-appctl netdev-dummy/receive p2 'in_port(1),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x86dd),ipv6(src=fe80::1,dst=fe80::2,label=0,proto=10,tclass=0x70,hlimit=128,frag=no)'
> +
> +dnl sleep long enough to get more than one counter sample
> +dnl from each datasource so we can check sequence numbers
> +for i in `seq 1 30`; do
> +    ovs-appctl time/warp 100
> +done
> +OVS_VSWITCHD_STOP
> +ovs-appctl -t test-sflow exit
> +
> +AT_CHECK([[sort sflow.log | grep HEADER | sed 's/ /\
> +	/g']], [0], [dnl
> +HEADER
> +	dgramSeqNo=1
> +	ds=127.0.0.1>0:1003
> +	fsSeqNo=1
> +	in_vlan=0
> +	in_priority=0
> +	out_vlan=0
> +	out_priority=0
> +	meanSkip=1
> +	samplePool=1
> +	dropEvents=0
> +	in_ifindex=1003
> +	in_format=0
> +	out_ifindex=2
> +	out_format=2
> +	hdr_prot=1
> +	pkt_len=64
> +	stripped=4
> +	hdr_len=60
> +	hdr=FF-FF-FF-FF-FF-FF-50-54-00-00-00-07-08-06-00-01-08-00-06-04-00-01-50-54-00-00-00-07-C0-A8-00-01-00-00-00-00-00-00-C0-A8-00-02-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
> +HEADER
> +	dgramSeqNo=1
> +	ds=127.0.0.1>0:1003
> +	fsSeqNo=2
> +	in_vlan=0
> +	in_priority=0
> +	out_vlan=0
> +	out_priority=0
> +	meanSkip=1
> +	samplePool=2
> +	dropEvents=0
> +	in_ifindex=1003
> +	in_format=0
> +	out_ifindex=1004
> +	out_format=0
> +	hdr_prot=1
> +	pkt_len=64
> +	stripped=4
> +	hdr_len=60
> +	hdr=50-54-00-00-00-05-50-54-00-00-00-07-08-00-45-00-00-1C-00-00-00-00-40-01-F9-8D-C0-A8-00-02-C0-A8-00-01-00-00-FF-FF-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
> +HEADER
> +	dgramSeqNo=1
> +	ds=127.0.0.1>0:1003
> +	fsSeqNo=3
> +	in_vlan=0
> +	in_priority=0
> +	out_vlan=0
> +	out_priority=0
> +	meanSkip=1
> +	samplePool=3
> +	dropEvents=0
> +	in_ifindex=1003
> +	in_format=0
> +	out_ifindex=1004
> +	out_format=0
> +	hdr_prot=1
> +	pkt_len=64
> +	stripped=4
> +	hdr_len=60
> +	hdr=50-54-00-00-00-05-50-54-00-00-00-07-86-DD-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
> +HEADER
> +	dgramSeqNo=1
> +	ds=127.0.0.1>0:1004
> +	fsSeqNo=1
> +	in_vlan=0
> +	in_priority=0
> +	out_vlan=0
> +	out_priority=0
> +	meanSkip=1
> +	samplePool=1
> +	dropEvents=0
> +	in_ifindex=1004
> +	in_format=0
> +	out_ifindex=2
> +	out_format=2
> +	hdr_prot=1
> +	pkt_len=64
> +	stripped=4
> +	hdr_len=60
> +	hdr=FF-FF-FF-FF-FF-FF-50-54-00-00-00-05-08-06-00-01-08-00-06-04-00-01-50-54-00-00-00-05-C0-A8-00-02-00-00-00-00-00-00-C0-A8-00-01-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
> +HEADER
> +	dgramSeqNo=1
> +	ds=127.0.0.1>0:1004
> +	fsSeqNo=2
> +	in_vlan=0
> +	in_priority=0
> +	out_vlan=0
> +	out_priority=0
> +	meanSkip=1
> +	samplePool=2
> +	dropEvents=0
> +	in_ifindex=1004
> +	in_format=0
> +	out_ifindex=1003
> +	out_format=0
> +	hdr_prot=1
> +	pkt_len=64
> +	stripped=4
> +	hdr_len=60
> +	hdr=50-54-00-00-00-07-50-54-00-00-00-05-08-00-45-00-00-1C-00-00-00-00-40-01-F9-8D-C0-A8-00-01-C0-A8-00-02-08-00-F7-FF-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
> +])
> +
> +AT_CHECK([[sort sflow.log | grep IFCOUNTERS | head -6 | sed 's/ /\
> +	/g']], [0], [dnl
> +IFCOUNTERS
> +	dgramSeqNo=2
> +	ds=127.0.0.1>0:1002
> +	csSeqNo=1
> +	ifindex=1002
> +	type=6
> +	ifspeed=100000000
> +	direction=0
> +	status=3
> +	in_octets=0
> +	in_unicasts=0
> +	in_multicasts=0
> +	in_broadcasts=4294967295
> +	in_discards=0
> +	in_errors=0
> +	in_unknownprotos=4294967295
> +	out_octets=120
> +	out_unicasts=2
> +	out_multicasts=4294967295
> +	out_broadcasts=4294967295
> +	out_discards=0
> +	out_errors=0
> +	promiscuous=0
> +IFCOUNTERS
> +	dgramSeqNo=2
> +	ds=127.0.0.1>0:1003
> +	csSeqNo=1
> +	ifindex=1003
> +	type=6
> +	ifspeed=100000000
> +	direction=0
> +	status=0
> +	in_octets=98
> +	in_unicasts=3
> +	in_multicasts=0
> +	in_broadcasts=4294967295
> +	in_discards=0
> +	in_errors=0
> +	in_unknownprotos=4294967295
> +	out_octets=120
> +	out_unicasts=2
> +	out_multicasts=4294967295
> +	out_broadcasts=4294967295
> +	out_discards=0
> +	out_errors=0
> +	promiscuous=0
> +IFCOUNTERS
> +	dgramSeqNo=2
> +	ds=127.0.0.1>0:1004
> +	csSeqNo=1
> +	ifindex=1004
> +	type=6
> +	ifspeed=100000000
> +	direction=0
> +	status=0
> +	in_octets=84
> +	in_unicasts=2
> +	in_multicasts=0
> +	in_broadcasts=4294967295
> +	in_discards=0
> +	in_errors=0
> +	in_unknownprotos=4294967295
> +	out_octets=180
> +	out_unicasts=3
> +	out_multicasts=4294967295
> +	out_broadcasts=4294967295
> +	out_discards=0
> +	out_errors=0
> +	promiscuous=0
> +IFCOUNTERS
> +	dgramSeqNo=3
> +	ds=127.0.0.1>0:1002
> +	csSeqNo=2
> +	ifindex=1002
> +	type=6
> +	ifspeed=100000000
> +	direction=0
> +	status=3
> +	in_octets=0
> +	in_unicasts=0
> +	in_multicasts=0
> +	in_broadcasts=4294967295
> +	in_discards=0
> +	in_errors=0
> +	in_unknownprotos=4294967295
> +	out_octets=120
> +	out_unicasts=2
> +	out_multicasts=4294967295
> +	out_broadcasts=4294967295
> +	out_discards=0
> +	out_errors=0
> +	promiscuous=0
> +IFCOUNTERS
> +	dgramSeqNo=3
> +	ds=127.0.0.1>0:1003
> +	csSeqNo=2
> +	ifindex=1003
> +	type=6
> +	ifspeed=100000000
> +	direction=0
> +	status=0
> +	in_octets=98
> +	in_unicasts=3
> +	in_multicasts=0
> +	in_broadcasts=4294967295
> +	in_discards=0
> +	in_errors=0
> +	in_unknownprotos=4294967295
> +	out_octets=120
> +	out_unicasts=2
> +	out_multicasts=4294967295
> +	out_broadcasts=4294967295
> +	out_discards=0
> +	out_errors=0
> +	promiscuous=0
> +IFCOUNTERS
> +	dgramSeqNo=3
> +	ds=127.0.0.1>0:1004
> +	csSeqNo=2
> +	ifindex=1004
> +	type=6
> +	ifspeed=100000000
> +	direction=0
> +	status=0
> +	in_octets=84
> +	in_unicasts=2
> +	in_multicasts=0
> +	in_broadcasts=4294967295
> +	in_discards=0
> +	in_errors=0
> +	in_unknownprotos=4294967295
> +	out_octets=180
> +	out_unicasts=3
> +	out_multicasts=4294967295
> +	out_broadcasts=4294967295
> +	out_discards=0
> +	out_errors=0
> +	promiscuous=0
> +])
> +AT_CLEANUP
> +
> +
> +
> dnl Test that basic NetFlow reports flow statistics correctly:
> dnl - The initial packet of a flow are correctly accounted.
> dnl - Later packets within a flow are correctly accounted.
> diff --git a/tests/test-sflow.c b/tests/test-sflow.c
> new file mode 100644
> index 0000000..ee3e6b3
> --- /dev/null
> +++ b/tests/test-sflow.c
> @@ -0,0 +1,539 @@
> +/*
> + * Copyright (c) 2011, 2012 Nicira, Inc.
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at:
> + *
> + *     http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +#include <config.h>
> +
> +#include <errno.h>
> +#include <getopt.h>
> +#include <signal.h>
> +#include <stdlib.h>
> +#include <unistd.h>
> +#include <setjmp.h>
> +
> +#include "command-line.h"
> +#include "daemon.h"
> +#include "dynamic-string.h"
> +#include "netflow.h"
> +#include "ofpbuf.h"
> +#include "packets.h"
> +#include "poll-loop.h"
> +#include "socket-util.h"
> +#include "unixctl.h"
> +#include "util.h"
> +#include "vlog.h"
> +
> +static void usage(void) NO_RETURN;
> +static void parse_options(int argc, char *argv[]);
> +
> +static unixctl_cb_func test_sflow_exit;
> +
> +/* datagram */
> +#define SFLOW_VERSION_5 5
> +#define SFLOW_MIN_LEN 36
> +#define SFLOW_MAX_AGENTIP_STRLEN 64
> +
> +/* sample tag numbers */
> +#define SFLOW_FLOW_SAMPLE 1
> +#define SFLOW_COUNTERS_SAMPLE 2
> +#define SFLOW_FLOW_SAMPLE_EXPANDED 3
> +#define SFLOW_COUNTERS_SAMPLE_EXPANDED 4
> +/* structure element tag numbers */
> +#define SFLOW_TAG_CTR_IFCOUNTERS 1
> +#define SFLOW_TAG_PKT_HEADER 1
> +#define SFLOW_TAG_PKT_SWITCH 1001
> +
> +typedef struct _SFlowAddr {
> +  enum { SFLOW_ADDRTYPE_undefined=0, SFLOW_ADDRTYPE_IP4, SFLOW_ADDRTYPE_IP6 } type;
> +  union {
> +    uint32_t ip4;
> +    uint32_t ip6[4];
> +  } a;
> +} SFlowAddr;
> +
> +typedef struct _SFlowXDR {
> +  /* exceptions */
> +  jmp_buf env;
> +  int errline;
> +  /* cursor */
> +  uint32_t *datap;
> +  uint32_t i;
> +  uint32_t quads;
> +  /* agent */
> +  SFlowAddr agentAddr;
> +  char agentIPStr[SFLOW_MAX_AGENTIP_STRLEN];
> +  uint32_t subAgentId;
> +  uint32_t uptime_mS;
> +  /* datasource */
> +  uint32_t dsClass;
> +  uint32_t dsIndex;
> +  /* sequence numbers */
> +  uint32_t dgramSeqNo;
> +  uint32_t fsSeqNo;
> +  uint32_t csSeqNo;
> +  /* structure offsets */
> +  struct {
> +    uint32_t HEADER;
> +    uint32_t SWITCH;
> +    uint32_t IFCOUNTERS;
> +  } offset;
> +  /* flow sample fields */
> +  uint32_t meanSkipCount;
> +  uint32_t samplePool;
> +  uint32_t dropEvents;
> +  uint32_t inputPortFormat;
> +  uint32_t inputPort;
> +  uint32_t outputPortFormat;
> +  uint32_t outputPort;
> +} SFlowXDR;
> +
> +#define SFLOWXDR_try(x) ((x->errline = setjmp(x->env)) == 0)
> +#define SFLOWXDR_throw(x) longjmp(x->env, __LINE__)
> +#define SFLOWXDR_assert(x, t) if(!(t)) SFLOWXDR_throw(x)
> +
> +#define SFLOWXDR_init(x,buf,len) do {  x->datap = (uint32_t *)buf; x->quads = (len >> 2); } while(0)
> +#define SFLOWXDR_next(x) ntohl(x->datap[x->i++])
> +#define SFLOWXDR_next_n(x) x->datap[x->i++]
> +#define SFLOWXDR_more(x,q) ((q + x->i) <= x->quads)
> +#define SFLOWXDR_skip(x,q) x->i += q
> +#define SFLOWXDR_skip_b(x,b) x->i += ((b+3)>>2)
> +#define SFLOWXDR_mark(x,q) x->i + q
> +#define SFLOWXDR_markOK(x,m) (m == x->i)
> +#define SFLOWXDR_mark_unique(x, pi) do { if(*pi) SFLOWXDR_throw(x); (*pi) = x->i; } while(0)
> +#define SFLOWXDR_off_b() (x->i << 2)
> +#define SFLOWXDR_setc(x,j) x->i = j
> +#define SFLOWXDR_str(x) (char *)(x->datap + x->i)
> +
> +static uint64_t
> +SFLOWXDR_next_int64(SFlowXDR *x)
> +{
> +  uint64_t scratch;
> +  scratch = SFLOWXDR_next(x);
> +  scratch <<= 32;
> +  scratch += SFLOWXDR_next(x);
> +  return scratch;
> +}
> +
> +#if 0 // not used
> +static float
> +SFLOWXDR_next_float(SFlowXDR *x)
> +{
> +  float scratch_fl;
> +  uint32_t scratch_32;
> +  scratch_32 = SFLOWXDR_next(x);
> +  memcpy(&scratch_fl, &scratch_32, 4);
> +  return scratch_fl;
> +}
> +#endif
> +
> +static void
> +processCounterSample(SFlowXDR *x) {
> +  if(x->offset.IFCOUNTERS) {
> +    SFLOWXDR_setc(x, x->offset.IFCOUNTERS);
> +    printf("IFCOUNTERS");
> +    printf(" dgramSeqNo=%"PRIu32, x->dgramSeqNo);
> +    printf(" ds=%s>%"PRIu32":%"PRIu32, x->agentIPStr, x->dsClass, x->dsIndex);
> +    printf(" csSeqNo=%"PRIu32, x->csSeqNo);
> +    printf(" ifindex=%"PRIu32, SFLOWXDR_next(x));
> +    printf(" type=%"PRIu32, SFLOWXDR_next(x));
> +    printf(" ifspeed=%"PRIu64, SFLOWXDR_next_int64(x));
> +    printf(" direction=%"PRIu32, SFLOWXDR_next(x));
> +    printf(" status=%"PRIu32, SFLOWXDR_next(x));
> +    printf(" in_octets=%"PRIu64, SFLOWXDR_next_int64(x));
> +    printf(" in_unicasts=%"PRIu32, SFLOWXDR_next(x));
> +    printf(" in_multicasts=%"PRIu32, SFLOWXDR_next(x));
> +    printf(" in_broadcasts=%"PRIu32, SFLOWXDR_next(x));
> +    printf(" in_discards=%"PRIu32, SFLOWXDR_next(x));
> +    printf(" in_errors=%"PRIu32, SFLOWXDR_next(x));
> +    printf(" in_unknownprotos=%"PRIu32, SFLOWXDR_next(x));
> +    printf(" out_octets=%"PRIu64, SFLOWXDR_next_int64(x));
> +    printf(" out_unicasts=%"PRIu32, SFLOWXDR_next(x));
> +    printf(" out_multicasts=%"PRIu32, SFLOWXDR_next(x));
> +    printf(" out_broadcasts=%"PRIu32, SFLOWXDR_next(x));
> +    printf(" out_discards=%"PRIu32, SFLOWXDR_next(x));
> +    printf(" out_errors=%"PRIu32, SFLOWXDR_next(x));
> +    printf(" promiscuous=%"PRIu32, SFLOWXDR_next(x));
> +    printf("\n");
> +  }
> +}
> +
> +#define bin2hex(nib) (((nib) < 10) ? ('0' + (nib)) : ('A' - 10 + (nib)))
> +
> +static int printHex(const char *a, int len, char *buf, int bufLen)
> +{
> +  int b = 0, i = 0;
> +  unsigned char nextByte;
> +  for (; i < len; i++) {
> +    if(b > (bufLen - 10)) break;
> +    nextByte = a[i];
> +    buf[b++] = bin2hex(nextByte >> 4);
> +    buf[b++] = bin2hex(nextByte & 0x0f);
> +    if(i < (len - 1)) buf[b++] = '-';
> +  }
> +  buf[b] = '\0';
> +  return b;
> +}
> +
> +#define SFLOW_HEX_SCRATCH 1024
> +
> +static void
> +processFlowSample(SFlowXDR *x) {
> +  if(x->offset.HEADER) {
> +    uint32_t headerLen;
> +    char scratch[SFLOW_HEX_SCRATCH];
> +
> +    printf("HEADER");
> +    printf(" dgramSeqNo=%"PRIu32, x->dgramSeqNo);
> +    printf(" ds=%s>%"PRIu32":%"PRIu32, x->agentIPStr, x->dsClass, x->dsIndex);
> +    printf(" fsSeqNo=%"PRIu32, x->fsSeqNo);
> +
> +    if(x->offset.SWITCH) {
> +      SFLOWXDR_setc(x, x->offset.SWITCH);
> +      printf(" in_vlan=%"PRIu32, SFLOWXDR_next(x));
> +      printf(" in_priority=%"PRIu32, SFLOWXDR_next(x));
> +      printf(" out_vlan=%"PRIu32, SFLOWXDR_next(x));
> +      printf(" out_priority=%"PRIu32, SFLOWXDR_next(x));
> +    }
> +    
> +    SFLOWXDR_setc(x, x->offset.HEADER);
> +    printf(" meanSkip=%"PRIu32, x->meanSkipCount);
> +    printf(" samplePool=%"PRIu32, x->samplePool);
> +    printf(" dropEvents=%"PRIu32, x->dropEvents);
> +    printf(" in_ifindex=%"PRIu32, x->inputPort);
> +    printf(" in_format=%"PRIu32, x->inputPortFormat);
> +    printf(" out_ifindex=%"PRIu32, x->outputPort);
> +    printf(" out_format=%"PRIu32, x->outputPortFormat);
> +    printf(" hdr_prot=%"PRIu32, SFLOWXDR_next(x));
> +    printf(" pkt_len=%"PRIu32, SFLOWXDR_next(x));
> +    printf(" stripped=%"PRIu32, SFLOWXDR_next(x));
> +    headerLen = SFLOWXDR_next(x);
> +    printf(" hdr_len=%"PRIu32, headerLen);
> +    printHex(SFLOWXDR_str(x), headerLen, scratch, SFLOW_HEX_SCRATCH);
> +    printf(" hdr=%s", scratch);
> +    printf("\n");
> +  }
> +}
> +
> +static void
> +processDatagram(SFlowXDR *x)
> +{
> +  uint32_t samples,s;
> +
> +  SFLOWXDR_assert(x, (SFLOWXDR_next(x) == SFLOW_VERSION_5));
> +  /* read the sFlow header */
> +  x->agentAddr.type = SFLOWXDR_next(x);
> +  switch(x->agentAddr.type) {
> +  case SFLOW_ADDRTYPE_IP4:
> +    x->agentAddr.a.ip4 = SFLOWXDR_next_n(x);
> +    break;
> +  case SFLOW_ADDRTYPE_IP6:
> +    x->agentAddr.a.ip6[0] = SFLOWXDR_next_n(x);
> +    x->agentAddr.a.ip6[1] = SFLOWXDR_next_n(x);
> +    x->agentAddr.a.ip6[2] = SFLOWXDR_next_n(x);
> +    x->agentAddr.a.ip6[3] = SFLOWXDR_next_n(x);
> +    break;
> +  case SFLOW_ADDRTYPE_undefined:
> +  default:
> +    SFLOWXDR_throw(x);
> +    break;
> +  }
> +  x->subAgentId = SFLOWXDR_next(x);
> +  x->dgramSeqNo = SFLOWXDR_next(x);
> +  x->uptime_mS = SFLOWXDR_next(x);
> +
> +  /* store the agent address as a string */
> +  if(x->agentAddr.type == SFLOW_ADDRTYPE_IP6) {
> +    snprintf(x->agentIPStr, SFLOW_MAX_AGENTIP_STRLEN, "%04x:%04x:%04x:%04x",
> +	    x->agentAddr.a.ip6[0],
> +	    x->agentAddr.a.ip6[1],
> +	    x->agentAddr.a.ip6[2],
> +	    x->agentAddr.a.ip6[3]);
> +  }
> +  else {
> +    snprintf(x->agentIPStr, SFLOW_MAX_AGENTIP_STRLEN, IP_FMT, IP_ARGS(x->agentAddr.a.ip4));
> +  }
> +
> +  /* array of flow/counter samples */
> +  samples = SFLOWXDR_next(x);
> +  for(s = 0; s < samples; s++) {
> +    uint32_t sType = SFLOWXDR_next(x);
> +    uint32_t sQuads = SFLOWXDR_next(x) >> 2;
> +    uint32_t sMark = SFLOWXDR_mark(x,sQuads);
> +    SFLOWXDR_assert(x, SFLOWXDR_more(x,sQuads));
> +    
> +    switch(sType) {
> +    case SFLOW_COUNTERS_SAMPLE_EXPANDED:
> +    case SFLOW_COUNTERS_SAMPLE:
> +      {
> +        uint32_t csElements, e;
> +        uint32_t ceTag, ceQuads, ceMark, csEnd;
> +        x->csSeqNo = SFLOWXDR_next(x);
> +        if(sType == SFLOW_COUNTERS_SAMPLE_EXPANDED) {
> +          x->dsClass = SFLOWXDR_next(x);
> +          x->dsIndex = SFLOWXDR_next(x);
> +        }
> +        else {
> +          uint32_t dsCombined = SFLOWXDR_next(x);
> +          x->dsClass = dsCombined >> 24;
> +          x->dsIndex = dsCombined & 0x00FFFFFF;
> +        }
> +
> +        csElements = SFLOWXDR_next(x);
> +        for(e = 0; e < csElements; e++) {
> +          SFLOWXDR_assert(x, SFLOWXDR_more(x,2));
> +          ceTag = SFLOWXDR_next(x);
> +          ceQuads = SFLOWXDR_next(x) >> 2;
> +          ceMark = SFLOWXDR_mark(x,ceQuads);
> +          SFLOWXDR_assert(x, SFLOWXDR_more(x,ceQuads));
> +          /* only care about selected structures.
> +	   * Just record their offsets here. We'll read the fields out later. */
> +          switch(ceTag) {
> +	  case SFLOW_TAG_CTR_IFCOUNTERS:  SFLOWXDR_mark_unique(x, &(x->offset.IFCOUNTERS)); break;
> +	    /* add others here... */
> +          }
> +	  
> +          SFLOWXDR_skip(x,ceQuads);
> +          SFLOWXDR_assert(x, SFLOWXDR_markOK(x,ceMark));
> +        }
> +	
> +	csEnd = SFLOWXDR_mark(x,0);
> +	processCounterSample(x);
> +	/* make sure we pick up the decoding where we left off */
> +	SFLOWXDR_setc(x, csEnd);
> +	
> +        /* clear the offsets for the next sample */
> +        memset(&x->offset, 0, sizeof(x->offset));
> +      }
> +      break;
> +      
> +    case SFLOW_FLOW_SAMPLE:
> +    case SFLOW_FLOW_SAMPLE_EXPANDED:
> +      {
> +        uint32_t fsElements, e;
> +        uint32_t feTag, feQuads, feMark, fsEnd;
> +        x->fsSeqNo = SFLOWXDR_next(x);
> +        if(sType == SFLOW_FLOW_SAMPLE_EXPANDED) {
> +          x->dsClass = SFLOWXDR_next(x);
> +          x->dsIndex = SFLOWXDR_next(x);
> +        }
> +        else {
> +          uint32_t dsCombined = SFLOWXDR_next(x);
> +          x->dsClass = dsCombined >> 24;
> +          x->dsIndex = dsCombined & 0x00FFFFFF;
> +        }
> +	x->meanSkipCount = SFLOWXDR_next(x);
> +	x->samplePool = SFLOWXDR_next(x);
> +	x->dropEvents = SFLOWXDR_next(x);
> +        if(sType == SFLOW_FLOW_SAMPLE_EXPANDED) {
> +	  x->inputPortFormat = SFLOWXDR_next(x);
> +	  x->inputPort = SFLOWXDR_next(x);
> +	  x->outputPortFormat = SFLOWXDR_next(x);
> +	  x->outputPort = SFLOWXDR_next(x);
> +	}
> +	else {
> +	  uint32_t inp, outp;
> +	  inp = SFLOWXDR_next(x);
> +	  outp = SFLOWXDR_next(x);
> +	  x->inputPortFormat = inp >> 30;
> +	  x->inputPort = inp & 0x3fffffff;
> +	  x->outputPortFormat = outp >> 30;
> +	  x->outputPort = outp & 0x3fffffff;
> +	}
> +        fsElements = SFLOWXDR_next(x);
> +        for(e = 0; e < fsElements; e++) {
> +          SFLOWXDR_assert(x, SFLOWXDR_more(x,2));
> +          feTag = SFLOWXDR_next(x);
> +          feQuads = SFLOWXDR_next(x) >> 2;
> +          feMark = SFLOWXDR_mark(x,feQuads);
> +          SFLOWXDR_assert(x, SFLOWXDR_more(x,feQuads));
> +          /* only care about selected structures.
> +	   * Just record their offsets here. We'll read the fields out below. */
> +          switch(feTag) {
> +	  case SFLOW_TAG_PKT_HEADER: SFLOWXDR_mark_unique(x, &x->offset.HEADER); break;
> +	  case SFLOW_TAG_PKT_SWITCH: SFLOWXDR_mark_unique(x, &x->offset.SWITCH); break;
> +	    /* add others here... */
> +	  }
> +	  
> +	  SFLOWXDR_skip(x,feQuads);
> +	  SFLOWXDR_assert(x, SFLOWXDR_markOK(x,feMark));
> +	}
> +	
> +	
> +	fsEnd = SFLOWXDR_mark(x,0);
> +	processFlowSample(x);
> +	/* make sure we pick up the decoding where we left off */
> +	SFLOWXDR_setc(x, fsEnd);
> +	
> +	/* clear the offsets for the next counter/flow sample */
> +	memset(&x->offset, 0, sizeof(x->offset));
> +      }
> +      break;
> +    default:
> +      /* skip other sample types */
> +      SFLOWXDR_skip(x,sQuads);
> +    }
> +    SFLOWXDR_assert(x, SFLOWXDR_markOK(x,sMark));
> +  } 
> +}
> +
> +static void
> +print_sflow(struct ofpbuf *buf)
> +{
> +  char *dgram_buf;
> +  int dgram_len = buf->size;
> +  SFlowXDR xdrDatagram;
> +  SFlowXDR *x = &xdrDatagram;
> +  memset(x, 0, sizeof(SFlowXDR));
> +  if(SFLOWXDR_try(x)) {
> +    SFLOWXDR_assert(x, (dgram_buf = ofpbuf_try_pull(buf, buf->size)));
> +    SFLOWXDR_init(x, dgram_buf, dgram_len);
> +    SFLOWXDR_assert(x, dgram_len >= SFLOW_MIN_LEN);
> +    processDatagram(x);
> +  }
> +  else {
> +    // CATCH
> +    printf("\n>>>>> ERROR in " __FILE__ " at line %u\n", x->errline);
> +  }
> +}
> +
> +int
> +main(int argc, char *argv[])
> +{
> +    struct unixctl_server *server;
> +    enum { MAX_RECV = 1500 };
> +    const char *target;
> +    struct ofpbuf buf;
> +    bool exiting = false;
> +    int error;
> +    int sock;
> +
> +    proctitle_init(argc, argv);
> +    set_program_name(argv[0]);
> +    parse_options(argc, argv);
> +
> +    if (argc - optind != 1) {
> +        ovs_fatal(0, "exactly one non-option argument required "
> +                  "(use --help for help)");
> +    }
> +    target = argv[optind];
> +
> +    sock = inet_open_passive(SOCK_DGRAM, target, 0, NULL, 0);
> +    if (sock < 0) {
> +        ovs_fatal(0, "%s: failed to open (%s)", argv[1], strerror(-sock));
> +    }
> +
> +    daemon_save_fd(STDOUT_FILENO);
> +    daemonize_start();
> +
> +    error = unixctl_server_create(NULL, &server);
> +    if (error) {
> +        ovs_fatal(error, "failed to create unixctl server");
> +    }
> +    unixctl_command_register("exit", "", 0, 0, test_sflow_exit, &exiting);
> +
> +    daemonize_complete();
> +
> +    ofpbuf_init(&buf, MAX_RECV);
> +    for (;;) {
> +        int retval;
> +
> +        unixctl_server_run(server);
> +
> +        ofpbuf_clear(&buf);
> +        do {
> +            retval = read(sock, buf.data, buf.allocated);
> +        } while (retval < 0 && errno == EINTR);
> +        if (retval > 0) {
> +            ofpbuf_put_uninit(&buf, retval);
> +            print_sflow(&buf);
> +            fflush(stdout);
> +        }
> +
> +        if (exiting) {
> +            break;
> +        }
> +
> +        poll_fd_wait(sock, POLLIN);
> +        unixctl_server_wait(server);
> +        poll_block();
> +    }
> +
> +    return 0;
> +}
> +
> +static void
> +parse_options(int argc, char *argv[])
> +{
> +    enum {
> +        DAEMON_OPTION_ENUMS
> +    };
> +    static struct option long_options[] = {
> +        {"verbose", optional_argument, NULL, 'v'},
> +        {"help", no_argument, NULL, 'h'},
> +        DAEMON_LONG_OPTIONS,
> +        {NULL, 0, NULL, 0},
> +    };
> +    char *short_options = long_options_to_short_options(long_options);
> +
> +    for (;;) {
> +        int c = getopt_long(argc, argv, short_options, long_options, NULL);
> +        if (c == -1) {
> +            break;
> +        }
> +
> +        switch (c) {
> +        case 'h':
> +            usage();
> +
> +        case 'v':
> +            vlog_set_verbosity(optarg);
> +            break;
> +
> +        DAEMON_OPTION_HANDLERS
> +
> +        case '?':
> +            exit(EXIT_FAILURE);
> +
> +        default:
> +            abort();
> +        }
> +    }
> +    free(short_options);
> +}
> +
> +static void
> +usage(void)
> +{
> +    printf("%s: sflow collector test utility\n"
> +           "usage: %s [OPTIONS] PORT[:IP]\n"
> +           "where PORT is the UDP port to listen on and IP is optionally\n"
> +           "the IP address to listen on.\n",
> +           program_name, program_name);
> +    daemon_usage();
> +    vlog_usage();
> +    printf("\nOther options:\n"
> +           "  -h, --help                  display this help message\n");
> +    exit(EXIT_SUCCESS);
> +}
> +
> +static void
> +test_sflow_exit(struct unixctl_conn *conn,
> +                  int argc OVS_UNUSED, const char *argv[] OVS_UNUSED,
> +                  void *exiting_)
> +{
> +    bool *exiting = exiting_;
> +    *exiting = true;
> +    unixctl_command_reply(conn, NULL);
> +}
> -- 
> 1.7.2.5
> 




More information about the dev mailing list