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.mc...@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 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev