The includes in test-netflow.c are not in alphabetical order (particularly util.h).
I think you need a comma before "interval" when printing hdr->sampling_interval. Otherwise looks good. Ethan On Thu, Dec 8, 2011 at 14:01, Ben Pfaff <b...@nicira.com> wrote: > These tests would have caught the flow statistics bug introduced by commit > 501f8d1fd75 (ofproto-dpif: Batch interacting with the dpif on flow miss > operations.) > --- > tests/automake.mk | 4 + > tests/ofproto-dpif.at | 128 +++++++++++++++++++ > tests/test-netflow.c | 323 > +++++++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 455 insertions(+), 0 deletions(-) > create mode 100644 tests/test-netflow.c > > diff --git a/tests/automake.mk b/tests/automake.mk > index 8649b80..9ae4c5c 100644 > --- a/tests/automake.mk > +++ b/tests/automake.mk > @@ -261,6 +261,10 @@ noinst_PROGRAMS += tests/test-stp > tests_test_stp_SOURCES = tests/test-stp.c > tests_test_stp_LDADD = lib/libopenvswitch.a > > +noinst_PROGRAMS += tests/test-netflow > +tests_test_netflow_SOURCES = tests/test-netflow.c > +tests_test_netflow_LDADD = lib/libopenvswitch.a > + > noinst_PROGRAMS += tests/test-unix-socket > tests_test_unix_socket_SOURCES = tests/test-unix-socket.c > tests_test_unix_socket_LDADD = lib/libopenvswitch.a > diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at > index 36e5493..33de783 100644 > --- a/tests/ofproto-dpif.at > +++ b/tests/ofproto-dpif.at > @@ -634,3 +634,131 @@ AT_CHECK([ovs-dpctl normalize-actions "$flow" "$actual" > br0=$br0 p1=$p1 p2=$p2], > > OVS_VSWITCHD_STOP > 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. > +dnl - Flow actions changing (in this case, due to MAC learning) > +dnl cause a record to be sent. > +AT_SETUP([ofproto-dpif - NetFlow flow expiration]) > + > +AT_SKIP_IF([test "x$RANDOM" = x]) > +NETFLOW_PORT=`expr 32767 + \( $RANDOM % 32767 \)` > + > +OVS_VSWITCHD_START( > + [set Bridge br0 fail-mode=standalone -- \ > + add-port br0 p1 -- set Interface p1 type=dummy -- \ > + add-port br0 p2 -- set Interface p2 type=dummy -- \ > + set Bridge br0 netflow=@nf -- \ > + --id=@nf create NetFlow targets=\"127.0.0.1:$NETFLOW_PORT\" \ > + engine_id=1 engine_type=2 active_timeout=30 \ > + add-id-to-interface=false], [<0> > +]) > + > +AT_CHECK([test-netflow --detach --pidfile $NETFLOW_PORT:127.0.0.1 > > netflow.log])AT_CAPTURE_FILE([netflow.log]) > + > +for delay in 1000 30000; do > + 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 time/warp $delay > +done > + > +OVS_VSWITCHD_STOP > +ovs-appctl -t test-netflow exit > + > +AT_CHECK([[sed -e 's/, uptime [0-9]*// > +s/, now [0-9.]*// > +s/time \([0-9]*\)\.\.\.\1/time <moment>/ > +s/time [0-9]*\.\.\.[0-9]*/time <range>/ > +' netflow.log]], [0], > + [header: v5, seq 0, engine 2,1 > +rec: 192.168.0.1 > 192.168.0.2, if 1 > 65535, 1 pkts, 60 bytes, ICMP 8:0, > time <moment> > + > +header: v5, seq 1, engine 2,1 > +rec: 192.168.0.2 > 192.168.0.1, if 2 > 1, 2 pkts, 120 bytes, ICMP 0:0, time > <range> > +rec: 192.168.0.1 > 192.168.0.2, if 1 > 2, 1 pkts, 60 bytes, ICMP 8:0, time > <moment> > +]) > +AT_CLEANUP > + > +dnl Test that basic NetFlow reports active expirations correctly. > +AT_SETUP([ofproto-dpif - NetFlow active expiration]) > + > +AT_SKIP_IF([test "x$RANDOM" = x]) > +NETFLOW_PORT=`expr 32767 + \( $RANDOM % 32767 \)` > + > +OVS_VSWITCHD_START( > + [set Bridge br0 fail-mode=standalone -- \ > + add-port br0 p1 -- set Interface p1 type=dummy -- \ > + add-port br0 p2 -- set Interface p2 type=dummy -- \ > + set Bridge br0 netflow=@nf -- \ > + --id=@nf create NetFlow targets=\"127.0.0.1:$NETFLOW_PORT\" \ > + engine_id=1 engine_type=2 active_timeout=10 \ > + add-id-to-interface=false], [<0> > +]) > + > +AT_CHECK([test-netflow --detach --pidfile $NETFLOW_PORT:127.0.0.1 > > netflow.log])AT_CAPTURE_FILE([netflow.log]) > + > +n=1 > +while test $n -le 60; do > + n=`expr $n + 1` > + > + 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=6,tos=0,ttl=64,frag=no),tcp(src=1234,dst=80)' > + 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=6,tos=0,ttl=64,frag=no),tcp(src=80,dst=1234)' > + > + ovs-appctl time/warp 1000 > +done > + > +ovs-appctl time/warp 10000 > + > +OVS_VSWITCHD_STOP > +ovs-appctl -t test-netflow exit > + > +# Count the number of reported packets: > +# - From source to destination before MAC learning kicks in (just one). > +# - From source to destination after that. > +# - From destination to source. > +n_learn=0 > +n_in=0 > +n_out=0 > +n_other=0 > +n_recs=0 > +none=0 > +while read line; do > + pkts=`echo "$line" | sed 's/.*, \([[0-9]]*\) pkts,.*/\1/'` > + case $pkts in > + [[0-9]]*) ;; > + *) continue ;; > + esac > + > + case $line in > + "rec: 192.168.0.1 > 192.168.0.2, if 1 > 65535, "*" pkts, "*" bytes, > TCP 1234 > 80, time "*) > + counter=n_learn > + ;; > + "rec: 192.168.0.1 > 192.168.0.2, if 1 > 2, "*" pkts, "*" bytes, TCP > 1234 > 80, time "*) > + counter=n_in > + ;; > + "rec: 192.168.0.2 > 192.168.0.1, if 2 > 1, "*" pkts, "*" bytes, TCP > 80 > 1234, time "*) > + counter=n_out > + ;; > + *) > + counter=n_other > + ;; > + esac > + eval $counter=\`expr \$$counter + \$pkts\` > + n_recs=`expr $n_recs + 1` > +done < netflow.log > + > +# There should be exactly 1 MAC learning packet, > +# exactly 59 other packets in that direction, > +# and exactly 60 packets in the other direction. > +AT_CHECK([echo $n_learn $n_in $n_out $n_other], [0], [1 59 60 0 > +]) > + > +# There should be 1 expiration for MAC learning, > +# at least 5 active and a final expiration in one direction, > +# and at least 5 active and a final expiration in the other direction. > +echo $n_recs > +AT_CHECK([test $n_recs -ge 13]) > + > +AT_CLEANUP > diff --git a/tests/test-netflow.c b/tests/test-netflow.c > new file mode 100644 > index 0000000..fa48a45 > --- /dev/null > +++ b/tests/test-netflow.c > @@ -0,0 +1,323 @@ > +/* > + * Copyright (c) 2011 Nicira Networks. > + * > + * 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 "command-line.h" > +#include "daemon.h" > +#include "netflow.h" > +#include "ofpbuf.h" > +#include "packets.h" > +#include "poll-loop.h" > +#include "util.h" > +#include "socket-util.h" > +#include "unixctl.h" > +#include "vlog.h" > + > +static void usage(void) NO_RETURN; > +static void parse_options(int argc, char *argv[]); > + > +static unixctl_cb_func test_netflow_exit; > + > +static void > +print_netflow(struct ofpbuf *buf) > +{ > + const struct netflow_v5_header *hdr; > + int i; > + > + hdr = ofpbuf_try_pull(buf, sizeof *hdr); > + if (!hdr) { > + printf("truncated NetFlow packet header\n"); > + return; > + } > + printf("header: v%"PRIu16", " > + "uptime %"PRIu32", " > + "now %"PRIu32".%09"PRIu32", " > + "seq %"PRIu32", " > + "engine %"PRIu8",%"PRIu8, > + ntohs(hdr->version), > + ntohl(hdr->sysuptime), > + ntohl(hdr->unix_secs), ntohl(hdr->unix_nsecs), > + ntohl(hdr->flow_seq), > + hdr->engine_type, hdr->engine_id); > + if (hdr->sampling_interval != htons(0)) { > + printf("interval %"PRIu16, ntohs(hdr->sampling_interval)); > + } > + putchar('\n'); > + > + for (i = 0; i < ntohs(hdr->count); i++) { > + struct netflow_v5_record *rec; > + > + rec = ofpbuf_try_pull(buf, sizeof *rec); > + if (!rec) { > + printf("truncated NetFlow records\n"); > + return; > + } > + > + printf("rec: "IP_FMT" > "IP_FMT, > + IP_ARGS(&rec->src_addr), IP_ARGS(&rec->dst_addr)); > + > + printf(", if %"PRIu16" > %"PRIu16, > + ntohs(rec->input), ntohs(rec->output)); > + > + printf(", %"PRIu32" pkts, %"PRIu32" bytes", > + ntohl(rec->packet_count), ntohl(rec->byte_count)); > + > + switch (rec->ip_proto) { > + case IPPROTO_TCP: > + printf(", TCP %"PRIu16" > %"PRIu16, > + ntohs(rec->src_port), ntohs(rec->dst_port)); > + if (rec->tcp_flags) { > + putchar(' '); > + if (rec->tcp_flags & TCP_SYN) { > + putchar('S'); > + } > + if (rec->tcp_flags & TCP_FIN) { > + putchar('F'); > + } > + if (rec->tcp_flags & TCP_PSH) { > + putchar('P'); > + } > + if (rec->tcp_flags & TCP_RST) { > + putchar('R'); > + } > + if (rec->tcp_flags & TCP_URG) { > + putchar('U'); > + } > + if (rec->tcp_flags & TCP_ACK) { > + putchar('.'); > + } > + if (rec->tcp_flags & 0x40) { > + printf("[40]"); > + } > + if (rec->tcp_flags & 0x80) { > + printf("[80]"); > + } > + } > + break; > + > + case IPPROTO_UDP: > + printf(", UDP %"PRIu16" > %"PRIu16, > + ntohs(rec->src_port), ntohs(rec->dst_port)); > + break; > + > + case IPPROTO_ICMP: > + printf(", ICMP %"PRIu16":%"PRIu16, > + ntohs(rec->dst_port) >> 8, > + ntohs(rec->dst_port) & 0xff); > + if (rec->src_port != htons(0)) { > + printf(", src_port=%"PRIu16, ntohs(rec->src_port)); > + } > + break; > + > + default: > + printf(", proto %"PRIu8, rec->ip_proto); > + break; > + } > + > + if (rec->ip_proto != IPPROTO_TCP && rec->tcp_flags != 0) { > + printf(", flags %"PRIx8, rec->tcp_flags); > + } > + > + if (rec->ip_proto != IPPROTO_TCP && > + rec->ip_proto != IPPROTO_UDP && > + rec->ip_proto != IPPROTO_ICMP) { > + if (rec->src_port != htons(0)) { > + printf(", src_port %"PRIu16, ntohs(rec->src_port)); > + } > + if (rec->dst_port != htons(0)) { > + printf(", dst_port %"PRIu16, ntohs(rec->dst_port)); > + } > + } > + > + if (rec->ip_tos) { > + printf(", TOS %"PRIx8, rec->ip_tos); > + } > + > + printf(", time %"PRIu32"...%"PRIu32, > + ntohl(rec->init_time), ntohl(rec->used_time)); > + > + if (rec->nexthop != htonl(0)) { > + printf(", nexthop "IP_FMT, IP_ARGS(&rec->nexthop)); > + } > + if (rec->src_as != htons(0) || rec->dst_as != htons(0)) { > + printf(", AS %"PRIu16" > %"PRIu16, > + ntohs(rec->src_as), ntohs(rec->dst_as)); > + } > + if (rec->src_mask != 0 || rec->dst_mask != 0) { > + printf(", mask %"PRIu8" > %"PRIu8, rec->src_mask, rec->dst_mask); > + } > + if (rec->pad1) { > + printf(", pad1 %"PRIu8, rec->pad1); > + } > + if (rec->pad[0] || rec->pad[1]) { > + printf(", pad %"PRIu8", %"PRIu8, rec->pad[0], rec->pad[1]); > + } > + putchar('\n'); > + } > + > + if (buf->size) { > + printf("%zu extra bytes after last record\n", buf->size); > + } > +} > + > +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; > + int fd; > + int n; > + > + 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); > + if (sock < 0) { > + ovs_fatal(0, "%s: failed to open (%s)", argv[1], strerror(-sock)); > + } > + > + /* Daemonization will close stdout but we really want to keep it, so > make a > + * copy. */ > + fd = dup(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_netflow_exit, &exiting); > + > + daemonize_complete(); > + > + /* Now get stdout back. */ > + dup2(fd, STDOUT_FILENO); > + > + ofpbuf_init(&buf, MAX_RECV); > + n = 0; > + 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); > + if (n++ > 0) { > + putchar('\n'); > + } > + print_netflow(&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: netflow 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_netflow_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, 200, ""); > +} > -- > 1.7.4.4 > > _______________________________________________ > dev mailing list > dev@openvswitch.org > http://openvswitch.org/mailman/listinfo/dev _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev