meta-flow: Correctly set destination MAC in mf_set_flow_value().
[sliver-openvswitch.git] / tests / test-netflow.c
1 /*
2  * Copyright (c) 2011 Nicira Networks.
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 "netflow.h"
28 #include "ofpbuf.h"
29 #include "packets.h"
30 #include "poll-loop.h"
31 #include "socket-util.h"
32 #include "unixctl.h"
33 #include "util.h"
34 #include "vlog.h"
35
36 static void usage(void) NO_RETURN;
37 static void parse_options(int argc, char *argv[]);
38
39 static unixctl_cb_func test_netflow_exit;
40
41 static void
42 print_netflow(struct ofpbuf *buf)
43 {
44     const struct netflow_v5_header *hdr;
45     int i;
46
47     hdr = ofpbuf_try_pull(buf, sizeof *hdr);
48     if (!hdr) {
49         printf("truncated NetFlow packet header\n");
50         return;
51     }
52     printf("header: v%"PRIu16", "
53            "uptime %"PRIu32", "
54            "now %"PRIu32".%09"PRIu32", "
55            "seq %"PRIu32", "
56            "engine %"PRIu8",%"PRIu8,
57            ntohs(hdr->version),
58            ntohl(hdr->sysuptime),
59            ntohl(hdr->unix_secs), ntohl(hdr->unix_nsecs),
60            ntohl(hdr->flow_seq),
61            hdr->engine_type, hdr->engine_id);
62     if (hdr->sampling_interval != htons(0)) {
63         printf(", interval %"PRIu16, ntohs(hdr->sampling_interval));
64     }
65     putchar('\n');
66
67     for (i = 0; i < ntohs(hdr->count); i++) {
68         struct netflow_v5_record *rec;
69
70         rec = ofpbuf_try_pull(buf, sizeof *rec);
71         if (!rec) {
72             printf("truncated NetFlow records\n");
73             return;
74         }
75
76         printf("rec: "IP_FMT" > "IP_FMT,
77                IP_ARGS(&rec->src_addr), IP_ARGS(&rec->dst_addr));
78
79         printf(", if %"PRIu16" > %"PRIu16,
80                ntohs(rec->input), ntohs(rec->output));
81
82         printf(", %"PRIu32" pkts, %"PRIu32" bytes",
83                ntohl(rec->packet_count), ntohl(rec->byte_count));
84
85         switch (rec->ip_proto) {
86         case IPPROTO_TCP:
87             printf(", TCP %"PRIu16" > %"PRIu16,
88                    ntohs(rec->src_port), ntohs(rec->dst_port));
89             if (rec->tcp_flags) {
90                 putchar(' ');
91                 if (rec->tcp_flags & TCP_SYN) {
92                     putchar('S');
93                 }
94                 if (rec->tcp_flags & TCP_FIN) {
95                     putchar('F');
96                 }
97                 if (rec->tcp_flags & TCP_PSH) {
98                     putchar('P');
99                 }
100                 if (rec->tcp_flags & TCP_RST) {
101                     putchar('R');
102                 }
103                 if (rec->tcp_flags & TCP_URG) {
104                     putchar('U');
105                 }
106                 if (rec->tcp_flags & TCP_ACK) {
107                     putchar('.');
108                 }
109                 if (rec->tcp_flags & 0x40) {
110                     printf("[40]");
111                 }
112                 if (rec->tcp_flags & 0x80) {
113                     printf("[80]");
114                 }
115             }
116             break;
117
118         case IPPROTO_UDP:
119             printf(", UDP %"PRIu16" > %"PRIu16,
120                    ntohs(rec->src_port), ntohs(rec->dst_port));
121             break;
122
123         case IPPROTO_ICMP:
124             printf(", ICMP %"PRIu16":%"PRIu16,
125                    ntohs(rec->dst_port) >> 8,
126                    ntohs(rec->dst_port) & 0xff);
127             if (rec->src_port != htons(0)) {
128                 printf(", src_port=%"PRIu16, ntohs(rec->src_port));
129             }
130             break;
131
132         default:
133             printf(", proto %"PRIu8, rec->ip_proto);
134             break;
135         }
136
137         if (rec->ip_proto != IPPROTO_TCP && rec->tcp_flags != 0) {
138             printf(", flags %"PRIx8, rec->tcp_flags);
139         }
140
141         if (rec->ip_proto != IPPROTO_TCP &&
142             rec->ip_proto != IPPROTO_UDP &&
143             rec->ip_proto != IPPROTO_ICMP) {
144             if (rec->src_port != htons(0)) {
145                 printf(", src_port %"PRIu16, ntohs(rec->src_port));
146             }
147             if (rec->dst_port != htons(0)) {
148                 printf(", dst_port %"PRIu16, ntohs(rec->dst_port));
149             }
150         }
151
152         if (rec->ip_tos) {
153             printf(", TOS %"PRIx8, rec->ip_tos);
154         }
155
156         printf(", time %"PRIu32"...%"PRIu32,
157                ntohl(rec->init_time), ntohl(rec->used_time));
158
159         if (rec->nexthop != htonl(0)) {
160             printf(", nexthop "IP_FMT, IP_ARGS(&rec->nexthop));
161         }
162         if (rec->src_as != htons(0) || rec->dst_as != htons(0)) {
163             printf(", AS %"PRIu16" > %"PRIu16,
164                    ntohs(rec->src_as), ntohs(rec->dst_as));
165         }
166         if (rec->src_mask != 0 || rec->dst_mask != 0) {
167             printf(", mask %"PRIu8" > %"PRIu8, rec->src_mask, rec->dst_mask);
168         }
169         if (rec->pad1) {
170             printf(", pad1 %"PRIu8, rec->pad1);
171         }
172         if (rec->pad[0] || rec->pad[1]) {
173             printf(", pad %"PRIu8", %"PRIu8, rec->pad[0], rec->pad[1]);
174         }
175         putchar('\n');
176     }
177
178     if (buf->size) {
179         printf("%zu extra bytes after last record\n", buf->size);
180     }
181 }
182
183 int
184 main(int argc, char *argv[])
185 {
186     struct unixctl_server *server;
187     enum { MAX_RECV = 1500 };
188     const char *target;
189     struct ofpbuf buf;
190     bool exiting = false;
191     int error;
192     int sock;
193     int fd;
194     int n;
195
196     proctitle_init(argc, argv);
197     set_program_name(argv[0]);
198     parse_options(argc, argv);
199
200     if (argc - optind != 1) {
201         ovs_fatal(0, "exactly one non-option argument required "
202                   "(use --help for help)");
203     }
204     target = argv[optind];
205
206     sock = inet_open_passive(SOCK_DGRAM, target, 0, NULL);
207     if (sock < 0) {
208         ovs_fatal(0, "%s: failed to open (%s)", argv[1], strerror(-sock));
209     }
210
211     /* Daemonization will close stdout but we really want to keep it, so make a
212      * copy. */
213     fd = dup(STDOUT_FILENO);
214
215     daemonize_start();
216
217     error = unixctl_server_create(NULL, &server);
218     if (error) {
219         ovs_fatal(error, "failed to create unixctl server");
220     }
221     unixctl_command_register("exit", "", 0, 0, test_netflow_exit, &exiting);
222
223     daemonize_complete();
224
225     /* Now get stdout back. */
226     dup2(fd, STDOUT_FILENO);
227
228     ofpbuf_init(&buf, MAX_RECV);
229     n = 0;
230     for (;;) {
231         int retval;
232
233         unixctl_server_run(server);
234
235         ofpbuf_clear(&buf);
236         do {
237             retval = read(sock, buf.data, buf.allocated);
238         } while (retval < 0 && errno == EINTR);
239         if (retval > 0) {
240             ofpbuf_put_uninit(&buf, retval);
241             if (n++ > 0) {
242                 putchar('\n');
243             }
244             print_netflow(&buf);
245             fflush(stdout);
246         }
247
248         if (exiting) {
249             break;
250         }
251
252         poll_fd_wait(sock, POLLIN);
253         unixctl_server_wait(server);
254         poll_block();
255     }
256
257     return 0;
258 }
259
260 static void
261 parse_options(int argc, char *argv[])
262 {
263     enum {
264         DAEMON_OPTION_ENUMS
265     };
266     static struct option long_options[] = {
267         {"verbose", optional_argument, NULL, 'v'},
268         {"help", no_argument, NULL, 'h'},
269         DAEMON_LONG_OPTIONS,
270         {NULL, 0, NULL, 0},
271     };
272     char *short_options = long_options_to_short_options(long_options);
273
274     for (;;) {
275         int c = getopt_long(argc, argv, short_options, long_options, NULL);
276         if (c == -1) {
277             break;
278         }
279
280         switch (c) {
281         case 'h':
282             usage();
283
284         case 'v':
285             vlog_set_verbosity(optarg);
286             break;
287
288         DAEMON_OPTION_HANDLERS
289
290         case '?':
291             exit(EXIT_FAILURE);
292
293         default:
294             abort();
295         }
296     }
297     free(short_options);
298 }
299
300 static void
301 usage(void)
302 {
303     printf("%s: netflow collector test utility\n"
304            "usage: %s [OPTIONS] PORT[:IP]\n"
305            "where PORT is the UDP port to listen on and IP is optionally\n"
306            "the IP address to listen on.\n",
307            program_name, program_name);
308     daemon_usage();
309     vlog_usage();
310     printf("\nOther options:\n"
311            "  -h, --help                  display this help message\n");
312     exit(EXIT_SUCCESS);
313 }
314
315 static void
316 test_netflow_exit(struct unixctl_conn *conn,
317                   int argc OVS_UNUSED, const char *argv[] OVS_UNUSED,
318                   void *exiting_)
319 {
320     bool *exiting = exiting_;
321     *exiting = true;
322     unixctl_command_reply(conn, 200, "");
323 }