From 8073dd318b5a08fa447392f100d62b3bf54388a7 Mon Sep 17 00:00:00 2001 From: Neil Mckee Date: Wed, 27 Mar 2013 23:02:21 -0700 Subject: [PATCH] tests: Add sFlow test. This patch adds an sFlow test to the test suite. I have only tested this on a Fedora 17 OS. Signed-off-by: Neil Mckee Signed-off-by: Ben Pfaff --- lib/netdev-dummy.c | 38 ++- tests/atlocal.in | 1 + tests/automake.mk | 4 + tests/ofproto-dpif.at | 291 ++++++++++++++++++++ tests/test-sflow.c | 618 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 949 insertions(+), 3 deletions(-) create mode 100644 tests/test-sflow.c diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c index 234d7bc56..bdb3ea12e 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); @@ -131,6 +133,27 @@ netdev_dummy_destroy(struct netdev_dev *netdev_dev_) free(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) { @@ -283,6 +306,15 @@ netdev_dummy_set_stats(struct netdev *netdev, const struct netdev_stats *stats) return 0; } +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, @@ -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/atlocal.in b/tests/atlocal.in index c736df40d..3db626c34 100644 --- a/tests/atlocal.in +++ b/tests/atlocal.in @@ -1,6 +1,7 @@ # -*- shell-script -*- HAVE_OPENSSL='@HAVE_OPENSSL@' HAVE_PYTHON='@HAVE_PYTHON@' +EGREP='@EGREP@' PERL='@PERL@' if test x"$PYTHON" = x; then diff --git a/tests/automake.mk b/tests/automake.mk index b11e0a236..275ff53fd 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 96b166e4a..06ebf23ae 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 | $EGREP 'HEADER|ERROR' | 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 | $EGREP 'IFCOUNTERS|ERROR' | 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 000000000..3eb93c5b0 --- /dev/null +++ b/tests/test-sflow.c @@ -0,0 +1,618 @@ +/* + * Copyright (c) 2011, 2012 Nicira, Inc. + * Copyright (c) 2013 InMon Corp. + * + * 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 + +#include +#include +#include +#include +#include +#include + +#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 + +struct sflow_addr { + enum { + SFLOW_ADDRTYPE_undefined = 0, + SFLOW_ADDRTYPE_IP4, + SFLOW_ADDRTYPE_IP6 + } type; + + union { + ovs_be32 ip4; + ovs_be32 ip6[4]; + } a; +}; + +struct sflow_xdr { + /* Exceptions. */ + jmp_buf env; + int errline; + + /* Cursor. */ + ovs_be32 *datap; + uint32_t i; + uint32_t quads; + + /* Agent. */ + struct sflow_addr 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; +}; + +#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) + +static void +sflowxdr_init(struct sflow_xdr *x, void *buf, size_t len) +{ + x->datap = buf; + x->quads = len >> 2; +} + +static uint32_t +sflowxdr_next(struct sflow_xdr *x) +{ + return ntohl(x->datap[x->i++]); +} + +static ovs_be32 +sflowxdr_next_n(struct sflow_xdr *x) +{ + return x->datap[x->i++]; +} + +static bool +sflowxdr_more(const struct sflow_xdr *x, uint32_t q) +{ + return q + x->i <= x->quads; +} + +static void +sflowxdr_skip(struct sflow_xdr *x, uint32_t q) +{ + x->i += q; +} + +static uint32_t +sflowxdr_mark(const struct sflow_xdr *x, uint32_t q) +{ + return x->i + q; +} + +static bool +sflowxdr_mark_ok(const struct sflow_xdr *x, uint32_t m) +{ + return m == x->i; +} + +static void +sflowxdr_mark_unique(struct sflow_xdr *x, uint32_t *pi) +{ + if (*pi) { + SFLOWXDR_throw(x); + } + *pi = x->i; +} + +static void +sflowxdr_setc(struct sflow_xdr *x, uint32_t j) +{ + x->i = j; +} + +static const char * +sflowxdr_str(const struct sflow_xdr *x) +{ + return (const char *) (x->datap + x->i); +} + +static uint64_t +sflowxdr_next_int64(struct sflow_xdr *x) +{ + uint64_t scratch; + scratch = sflowxdr_next(x); + scratch <<= 32; + scratch += sflowxdr_next(x); + return scratch; +} + +static void +process_counter_sample(struct sflow_xdr *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"); + } +} + +static char +bin_to_hex(int hexit) +{ + return "0123456789ABCDEF"[hexit]; +} + +static int +print_hex(const char *a, int len, char *buf, int bufLen) +{ + unsigned char nextByte; + int b = 0; + int i; + + for (i = 0; i < len; i++) { + if (b > bufLen - 10) { + break; + } + nextByte = a[i]; + buf[b++] = bin_to_hex(nextByte >> 4); + buf[b++] = bin_to_hex(nextByte & 0x0f); + if (i < len - 1) { + buf[b++] = '-'; + } + } + buf[b] = '\0'; + return b; +} + +#define SFLOW_HEX_SCRATCH 1024 + +static void +process_flow_sample(struct sflow_xdr *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); + print_hex(sflowxdr_str(x), headerLen, scratch, SFLOW_HEX_SCRATCH); + printf(" hdr=%s", scratch); + printf("\n"); + } +} + +static void +process_datagram(struct sflow_xdr *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_mark_ok(x, ceMark)); + } + + csEnd = sflowxdr_mark(x, 0); + process_counter_sample(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_mark_ok(x, feMark)); + } + + fsEnd = sflowxdr_mark(x, 0); + process_flow_sample(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_mark_ok(x, sMark)); + } +} + +static void +print_sflow(struct ofpbuf *buf) +{ + char *dgram_buf; + int dgram_len = buf->size; + struct sflow_xdr xdrDatagram; + struct sflow_xdr *x = &xdrDatagram; + + memset(x, 0, sizeof *x); + 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); + process_datagram(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); +} -- 2.43.0