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

Reply via email to