[ovs-dev] [PATCH] test suite : add sFlow test
Neil Mckee
neil.mckee at inmon.com
Thu Mar 28 06:02:21 UTC 2013
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>
---
lib/netdev-dummy.c | 15 +-
tests/automake.mk | 4 +
tests/ofproto-dpif.at | 54 +++++
tests/test-sflow.c | 539 ++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 611 insertions(+), 1 deletion(-)
create mode 100644 tests/test-sflow.c
diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c
index f81b68e..8f7e250 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 {
@@ -97,6 +98,7 @@ 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);
@@ -118,6 +120,8 @@ 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;
}
@@ -271,6 +275,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)
@@ -343,7 +356,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, /* get_ifindex */
NULL, /* get_carrier */
NULL, /* get_carrier_resets */
NULL, /* get_miimon */
diff --git a/tests/automake.mk b/tests/automake.mk
index 1ebdf85..84d60c8 100644
--- a/tests/automake.mk
+++ b/tests/automake.mk
@@ -233,6 +233,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 7266054..a764b46 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -952,6 +952,60 @@ 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])
+ADD_OF_PORTS([br0], 1, 2)
+ovs-vsctl \
+ 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
+sleep 3
+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_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.11.7
More information about the dev
mailing list