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