netdev-dpdk: fix mbuf leaks
[cascardo/ovs.git] / tests / test-netflow.c
1 /*
2  * Copyright (c) 2011, 2012, 2013, 2014, 2015 Nicira, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <config.h>
18 #undef NDEBUG
19 #include "netflow.h"
20 #include <errno.h>
21 #include <getopt.h>
22 #include <signal.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include "command-line.h"
26 #include "daemon.h"
27 #include "dynamic-string.h"
28 #include "ofpbuf.h"
29 #include "ovstest.h"
30 #include "packets.h"
31 #include "poll-loop.h"
32 #include "socket-util.h"
33 #include "unixctl.h"
34 #include "util.h"
35 #include "openvswitch/vlog.h"
36
37 OVS_NO_RETURN static void usage(void);
38 static void parse_options(int argc, char *argv[]);
39
40 static unixctl_cb_func test_netflow_exit;
41
42 static void
43 print_netflow(struct ofpbuf *buf)
44 {
45     const struct netflow_v5_header *hdr;
46     int i;
47
48     hdr = ofpbuf_try_pull(buf, sizeof *hdr);
49     if (!hdr) {
50         printf("truncated NetFlow packet header\n");
51         return;
52     }
53     printf("header: v%"PRIu16", "
54            "uptime %"PRIu32", "
55            "now %"PRIu32".%09"PRIu32", "
56            "seq %"PRIu32", "
57            "engine %"PRIu8",%"PRIu8,
58            ntohs(hdr->version),
59            ntohl(hdr->sysuptime),
60            ntohl(hdr->unix_secs), ntohl(hdr->unix_nsecs),
61            ntohl(hdr->flow_seq),
62            hdr->engine_type, hdr->engine_id);
63     if (hdr->sampling_interval != htons(0)) {
64         printf(", interval %"PRIu16, ntohs(hdr->sampling_interval));
65     }
66     putchar('\n');
67
68     for (i = 0; i < ntohs(hdr->count); i++) {
69         struct netflow_v5_record *rec;
70
71         rec = ofpbuf_try_pull(buf, sizeof *rec);
72         if (!rec) {
73             printf("truncated NetFlow records\n");
74             return;
75         }
76
77         printf("seq %"PRIu32": "IP_FMT" > "IP_FMT, ntohl(hdr->flow_seq),
78                IP_ARGS(rec->src_addr), IP_ARGS(rec->dst_addr));
79
80         printf(", if %"PRIu16" > %"PRIu16,
81                ntohs(rec->input), ntohs(rec->output));
82
83         printf(", %"PRIu32" pkts, %"PRIu32" bytes",
84                ntohl(rec->packet_count), ntohl(rec->byte_count));
85
86         switch (rec->ip_proto) {
87         case IPPROTO_TCP:
88             printf(", TCP %"PRIu16" > %"PRIu16,
89                    ntohs(rec->src_port), ntohs(rec->dst_port));
90             if (rec->tcp_flags) {
91                 struct ds s = DS_EMPTY_INITIALIZER;
92                 packet_format_tcp_flags(&s, rec->tcp_flags);
93                 printf(" %s", ds_cstr(&s));
94                 ds_destroy(&s);
95             }
96             break;
97
98         case IPPROTO_UDP:
99             printf(", UDP %"PRIu16" > %"PRIu16,
100                    ntohs(rec->src_port), ntohs(rec->dst_port));
101             break;
102
103         case IPPROTO_SCTP:
104             printf(", SCTP %"PRIu16" > %"PRIu16,
105                    ntohs(rec->src_port), ntohs(rec->dst_port));
106             break;
107
108         case IPPROTO_ICMP:
109             printf(", ICMP %"PRIu16":%"PRIu16,
110                    ntohs(rec->dst_port) >> 8,
111                    ntohs(rec->dst_port) & 0xff);
112             if (rec->src_port != htons(0)) {
113                 printf(", src_port=%"PRIu16, ntohs(rec->src_port));
114             }
115             break;
116
117         default:
118             printf(", proto %"PRIu8, rec->ip_proto);
119             break;
120         }
121
122         if (rec->ip_proto != IPPROTO_TCP && rec->tcp_flags != 0) {
123             printf(", flags %"PRIx8, rec->tcp_flags);
124         }
125
126         if (rec->ip_proto != IPPROTO_TCP &&
127             rec->ip_proto != IPPROTO_UDP &&
128             rec->ip_proto != IPPROTO_SCTP &&
129             rec->ip_proto != IPPROTO_ICMP) {
130             if (rec->src_port != htons(0)) {
131                 printf(", src_port %"PRIu16, ntohs(rec->src_port));
132             }
133             if (rec->dst_port != htons(0)) {
134                 printf(", dst_port %"PRIu16, ntohs(rec->dst_port));
135             }
136         }
137
138         if (rec->ip_tos) {
139             printf(", TOS %"PRIx8, rec->ip_tos);
140         }
141
142         printf(", time %"PRIu32"...%"PRIu32,
143                ntohl(rec->init_time), ntohl(rec->used_time));
144
145         if (rec->nexthop != htonl(0)) {
146             printf(", nexthop "IP_FMT, IP_ARGS(rec->nexthop));
147         }
148         if (rec->src_as != htons(0) || rec->dst_as != htons(0)) {
149             printf(", AS %"PRIu16" > %"PRIu16,
150                    ntohs(rec->src_as), ntohs(rec->dst_as));
151         }
152         if (rec->src_mask != 0 || rec->dst_mask != 0) {
153             printf(", mask %"PRIu8" > %"PRIu8, rec->src_mask, rec->dst_mask);
154         }
155         if (rec->pad1) {
156             printf(", pad1 %"PRIu8, rec->pad1);
157         }
158         if (rec->pad[0] || rec->pad[1]) {
159             printf(", pad %"PRIu8", %"PRIu8, rec->pad[0], rec->pad[1]);
160         }
161         putchar('\n');
162     }
163
164     if (buf->size) {
165         printf("%"PRIu32" extra bytes after last record\n", buf->size);
166     }
167 }
168
169 static void
170 test_netflow_main(int argc, char *argv[])
171 {
172     struct unixctl_server *server;
173     enum { MAX_RECV = 1500 };
174     const char *target;
175     struct ofpbuf buf;
176     bool exiting = false;
177     int error;
178     int sock;
179     int n;
180
181     ovs_cmdl_proctitle_init(argc, argv);
182     set_program_name(argv[0]);
183     service_start(&argc, &argv);
184     parse_options(argc, argv);
185
186     if (argc - optind != 1) {
187         ovs_fatal(0, "exactly one non-option argument required "
188                   "(use --help for help)");
189     }
190     target = argv[optind];
191
192     sock = inet_open_passive(SOCK_DGRAM, target, 0, NULL, 0, true);
193     if (sock < 0) {
194         ovs_fatal(0, "%s: failed to open (%s)", argv[1], ovs_strerror(-sock));
195     }
196
197     daemon_save_fd(STDOUT_FILENO);
198     daemonize_start(false);
199
200     error = unixctl_server_create(NULL, &server);
201     if (error) {
202         ovs_fatal(error, "failed to create unixctl server");
203     }
204     unixctl_command_register("exit", "", 0, 0, test_netflow_exit, &exiting);
205
206     daemonize_complete();
207
208     ofpbuf_init(&buf, MAX_RECV);
209     n = 0;
210     for (;;) {
211         int retval;
212
213         unixctl_server_run(server);
214
215         ofpbuf_clear(&buf);
216         do {
217             retval = recv(sock, buf.data, buf.allocated, 0);
218         } while (retval < 0 && errno == EINTR);
219         if (retval > 0) {
220             ofpbuf_put_uninit(&buf, retval);
221             if (n++ > 0) {
222                 putchar('\n');
223             }
224             print_netflow(&buf);
225             fflush(stdout);
226         }
227
228         if (exiting) {
229             break;
230         }
231
232         poll_fd_wait(sock, POLLIN);
233         unixctl_server_wait(server);
234         poll_block();
235     }
236
237     ofpbuf_uninit(&buf);
238     unixctl_server_destroy(server);
239 }
240
241 static void
242 parse_options(int argc, char *argv[])
243 {
244     enum {
245         DAEMON_OPTION_ENUMS,
246         VLOG_OPTION_ENUMS
247     };
248     static const struct option long_options[] = {
249         {"help", no_argument, NULL, 'h'},
250         DAEMON_LONG_OPTIONS,
251         VLOG_LONG_OPTIONS,
252         {NULL, 0, NULL, 0},
253     };
254     char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
255
256     for (;;) {
257         int c = getopt_long(argc, argv, short_options, long_options, NULL);
258         if (c == -1) {
259             break;
260         }
261
262         switch (c) {
263         case 'h':
264             usage();
265
266         DAEMON_OPTION_HANDLERS
267         VLOG_OPTION_HANDLERS
268
269         case '?':
270             exit(EXIT_FAILURE);
271
272         default:
273             abort();
274         }
275     }
276     free(short_options);
277 }
278
279 static void
280 usage(void)
281 {
282     printf("%s: netflow collector test utility\n"
283            "usage: %s [OPTIONS] PORT[:IP]\n"
284            "where PORT is the UDP port to listen on and IP is optionally\n"
285            "the IP address to listen on.\n",
286            program_name, program_name);
287     daemon_usage();
288     vlog_usage();
289     printf("\nOther options:\n"
290            "  -h, --help                  display this help message\n");
291     exit(EXIT_SUCCESS);
292 }
293
294 static void
295 test_netflow_exit(struct unixctl_conn *conn,
296                   int argc OVS_UNUSED, const char *argv[] OVS_UNUSED,
297                   void *exiting_)
298 {
299     bool *exiting = exiting_;
300     *exiting = true;
301     unixctl_command_reply(conn, NULL);
302 }
303
304 OVSTEST_REGISTER("test-netflow", test_netflow_main);