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