]> git.proxmox.com Git - ovs.git/blame - tests/test-sflow.c
unit-test: Improve ovstest user interface
[ovs.git] / tests / test-sflow.c
CommitLineData
8073dd31 1/*
e731d71b 2 * Copyright (c) 2011, 2012, 2013, 2014 Nicira, Inc.
8073dd31
NM
3 * Copyright (c) 2013 InMon Corp.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at:
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#include <config.h>
19
20#include <errno.h>
21#include <getopt.h>
22#include <signal.h>
23#include <stdlib.h>
24#include <unistd.h>
25#include <setjmp.h>
26
27#include "command-line.h"
28#include "daemon.h"
29#include "dynamic-string.h"
30#include "netflow.h"
31#include "ofpbuf.h"
32#include "packets.h"
33#include "poll-loop.h"
34#include "socket-util.h"
35#include "unixctl.h"
36#include "util.h"
37#include "vlog.h"
38
39static void usage(void) NO_RETURN;
40static void parse_options(int argc, char *argv[]);
41
42static unixctl_cb_func test_sflow_exit;
43
44/* Datagram. */
45#define SFLOW_VERSION_5 5
46#define SFLOW_MIN_LEN 36
8073dd31
NM
47
48/* Sample tag numbers. */
49#define SFLOW_FLOW_SAMPLE 1
50#define SFLOW_COUNTERS_SAMPLE 2
51#define SFLOW_FLOW_SAMPLE_EXPANDED 3
52#define SFLOW_COUNTERS_SAMPLE_EXPANDED 4
53
54/* Structure element tag numbers. */
55#define SFLOW_TAG_CTR_IFCOUNTERS 1
56#define SFLOW_TAG_PKT_HEADER 1
57#define SFLOW_TAG_PKT_SWITCH 1001
58
59struct sflow_addr {
60 enum {
61 SFLOW_ADDRTYPE_undefined = 0,
62 SFLOW_ADDRTYPE_IP4,
63 SFLOW_ADDRTYPE_IP6
64 } type;
65
66 union {
67 ovs_be32 ip4;
68 ovs_be32 ip6[4];
69 } a;
70};
71
72struct sflow_xdr {
73 /* Exceptions. */
74 jmp_buf env;
75 int errline;
76
77 /* Cursor. */
78 ovs_be32 *datap;
79 uint32_t i;
80 uint32_t quads;
81
82 /* Agent. */
83 struct sflow_addr agentAddr;
e731d71b 84 char agentIPStr[INET6_ADDRSTRLEN + 2];
8073dd31
NM
85 uint32_t subAgentId;
86 uint32_t uptime_mS;
87
88 /* Datasource. */
89 uint32_t dsClass;
90 uint32_t dsIndex;
91
92 /* Sequence numbers. */
93 uint32_t dgramSeqNo;
94 uint32_t fsSeqNo;
95 uint32_t csSeqNo;
96
97 /* Structure offsets. */
98 struct {
99 uint32_t HEADER;
100 uint32_t SWITCH;
101 uint32_t IFCOUNTERS;
102 } offset;
103
104 /* Flow sample fields. */
105 uint32_t meanSkipCount;
106 uint32_t samplePool;
107 uint32_t dropEvents;
108 uint32_t inputPortFormat;
109 uint32_t inputPort;
110 uint32_t outputPortFormat;
111 uint32_t outputPort;
112};
113
114#define SFLOWXDR_try(x) ((x->errline = setjmp(x->env)) == 0)
115#define SFLOWXDR_throw(x) longjmp(x->env, __LINE__)
116#define SFLOWXDR_assert(x, t) if (!(t)) SFLOWXDR_throw(x)
117
118static void
119sflowxdr_init(struct sflow_xdr *x, void *buf, size_t len)
120{
121 x->datap = buf;
122 x->quads = len >> 2;
123}
124
125static uint32_t
126sflowxdr_next(struct sflow_xdr *x)
127{
128 return ntohl(x->datap[x->i++]);
129}
130
131static ovs_be32
132sflowxdr_next_n(struct sflow_xdr *x)
133{
134 return x->datap[x->i++];
135}
136
137static bool
138sflowxdr_more(const struct sflow_xdr *x, uint32_t q)
139{
140 return q + x->i <= x->quads;
141}
142
143static void
144sflowxdr_skip(struct sflow_xdr *x, uint32_t q)
145{
146 x->i += q;
147}
148
149static uint32_t
150sflowxdr_mark(const struct sflow_xdr *x, uint32_t q)
151{
152 return x->i + q;
153}
154
155static bool
156sflowxdr_mark_ok(const struct sflow_xdr *x, uint32_t m)
157{
158 return m == x->i;
159}
160
161static void
162sflowxdr_mark_unique(struct sflow_xdr *x, uint32_t *pi)
163{
164 if (*pi) {
165 SFLOWXDR_throw(x);
166 }
167 *pi = x->i;
168}
169
170static void
171sflowxdr_setc(struct sflow_xdr *x, uint32_t j)
172{
173 x->i = j;
174}
175
176static const char *
177sflowxdr_str(const struct sflow_xdr *x)
178{
179 return (const char *) (x->datap + x->i);
180}
181
182static uint64_t
183sflowxdr_next_int64(struct sflow_xdr *x)
184{
185 uint64_t scratch;
186 scratch = sflowxdr_next(x);
187 scratch <<= 32;
188 scratch += sflowxdr_next(x);
189 return scratch;
190}
191
192static void
193process_counter_sample(struct sflow_xdr *x)
194{
195 if (x->offset.IFCOUNTERS) {
196 sflowxdr_setc(x, x->offset.IFCOUNTERS);
197 printf("IFCOUNTERS");
198 printf(" dgramSeqNo=%"PRIu32, x->dgramSeqNo);
199 printf(" ds=%s>%"PRIu32":%"PRIu32,
200 x->agentIPStr, x->dsClass, x->dsIndex);
201 printf(" csSeqNo=%"PRIu32, x->csSeqNo);
202 printf(" ifindex=%"PRIu32, sflowxdr_next(x));
203 printf(" type=%"PRIu32, sflowxdr_next(x));
204 printf(" ifspeed=%"PRIu64, sflowxdr_next_int64(x));
205 printf(" direction=%"PRIu32, sflowxdr_next(x));
206 printf(" status=%"PRIu32, sflowxdr_next(x));
207 printf(" in_octets=%"PRIu64, sflowxdr_next_int64(x));
208 printf(" in_unicasts=%"PRIu32, sflowxdr_next(x));
209 printf(" in_multicasts=%"PRIu32, sflowxdr_next(x));
210 printf(" in_broadcasts=%"PRIu32, sflowxdr_next(x));
211 printf(" in_discards=%"PRIu32, sflowxdr_next(x));
212 printf(" in_errors=%"PRIu32, sflowxdr_next(x));
213 printf(" in_unknownprotos=%"PRIu32, sflowxdr_next(x));
214 printf(" out_octets=%"PRIu64, sflowxdr_next_int64(x));
215 printf(" out_unicasts=%"PRIu32, sflowxdr_next(x));
216 printf(" out_multicasts=%"PRIu32, sflowxdr_next(x));
217 printf(" out_broadcasts=%"PRIu32, sflowxdr_next(x));
218 printf(" out_discards=%"PRIu32, sflowxdr_next(x));
219 printf(" out_errors=%"PRIu32, sflowxdr_next(x));
220 printf(" promiscuous=%"PRIu32, sflowxdr_next(x));
221 printf("\n");
222 }
223}
224
225static char
226bin_to_hex(int hexit)
227{
228 return "0123456789ABCDEF"[hexit];
229}
230
231static int
232print_hex(const char *a, int len, char *buf, int bufLen)
233{
234 unsigned char nextByte;
235 int b = 0;
236 int i;
237
238 for (i = 0; i < len; i++) {
239 if (b > bufLen - 10) {
240 break;
241 }
242 nextByte = a[i];
243 buf[b++] = bin_to_hex(nextByte >> 4);
244 buf[b++] = bin_to_hex(nextByte & 0x0f);
245 if (i < len - 1) {
246 buf[b++] = '-';
247 }
248 }
249 buf[b] = '\0';
250 return b;
251}
252
253#define SFLOW_HEX_SCRATCH 1024
254
255static void
256process_flow_sample(struct sflow_xdr *x)
257{
258 if (x->offset.HEADER) {
259 uint32_t headerLen;
260 char scratch[SFLOW_HEX_SCRATCH];
261
262 printf("HEADER");
263 printf(" dgramSeqNo=%"PRIu32, x->dgramSeqNo);
264 printf(" ds=%s>%"PRIu32":%"PRIu32,
265 x->agentIPStr, x->dsClass, x->dsIndex);
266 printf(" fsSeqNo=%"PRIu32, x->fsSeqNo);
267
268 if (x->offset.SWITCH) {
269 sflowxdr_setc(x, x->offset.SWITCH);
270 printf(" in_vlan=%"PRIu32, sflowxdr_next(x));
271 printf(" in_priority=%"PRIu32, sflowxdr_next(x));
272 printf(" out_vlan=%"PRIu32, sflowxdr_next(x));
273 printf(" out_priority=%"PRIu32, sflowxdr_next(x));
274 }
275
276 sflowxdr_setc(x, x->offset.HEADER);
277 printf(" meanSkip=%"PRIu32, x->meanSkipCount);
278 printf(" samplePool=%"PRIu32, x->samplePool);
279 printf(" dropEvents=%"PRIu32, x->dropEvents);
280 printf(" in_ifindex=%"PRIu32, x->inputPort);
281 printf(" in_format=%"PRIu32, x->inputPortFormat);
282 printf(" out_ifindex=%"PRIu32, x->outputPort);
283 printf(" out_format=%"PRIu32, x->outputPortFormat);
284 printf(" hdr_prot=%"PRIu32, sflowxdr_next(x));
285 printf(" pkt_len=%"PRIu32, sflowxdr_next(x));
286 printf(" stripped=%"PRIu32, sflowxdr_next(x));
287 headerLen = sflowxdr_next(x);
288 printf(" hdr_len=%"PRIu32, headerLen);
289 print_hex(sflowxdr_str(x), headerLen, scratch, SFLOW_HEX_SCRATCH);
290 printf(" hdr=%s", scratch);
291 printf("\n");
292 }
293}
294
295static void
296process_datagram(struct sflow_xdr *x)
297{
298 uint32_t samples, s;
299
300 SFLOWXDR_assert(x, (sflowxdr_next(x) == SFLOW_VERSION_5));
301
302 /* Read the sFlow header. */
303 x->agentAddr.type = sflowxdr_next(x);
304 switch (x->agentAddr.type) {
305 case SFLOW_ADDRTYPE_IP4:
306 x->agentAddr.a.ip4 = sflowxdr_next_n(x);
307 break;
308
309 case SFLOW_ADDRTYPE_IP6:
310 x->agentAddr.a.ip6[0] = sflowxdr_next_n(x);
311 x->agentAddr.a.ip6[1] = sflowxdr_next_n(x);
312 x->agentAddr.a.ip6[2] = sflowxdr_next_n(x);
313 x->agentAddr.a.ip6[3] = sflowxdr_next_n(x);
314 break;
315
316 case SFLOW_ADDRTYPE_undefined:
317 default:
318 SFLOWXDR_throw(x);
319 break;
320 }
321 x->subAgentId = sflowxdr_next(x);
322 x->dgramSeqNo = sflowxdr_next(x);
323 x->uptime_mS = sflowxdr_next(x);
324
325 /* Store the agent address as a string. */
326 if (x->agentAddr.type == SFLOW_ADDRTYPE_IP6) {
e731d71b
AS
327 char ipstr[INET6_ADDRSTRLEN];
328 inet_ntop(AF_INET6, (const void *) &x->agentAddr.a.ip6,
329 ipstr, INET6_ADDRSTRLEN);
330 snprintf(x->agentIPStr, sizeof x->agentIPStr, "[%s]", ipstr);
8073dd31 331 } else {
e731d71b 332 snprintf(x->agentIPStr, sizeof x->agentIPStr,
8073dd31
NM
333 IP_FMT, IP_ARGS(x->agentAddr.a.ip4));
334 }
335
336 /* Array of flow/counter samples. */
337 samples = sflowxdr_next(x);
338 for (s = 0; s < samples; s++) {
339 uint32_t sType = sflowxdr_next(x);
340 uint32_t sQuads = sflowxdr_next(x) >> 2;
341 uint32_t sMark = sflowxdr_mark(x, sQuads);
342 SFLOWXDR_assert(x, sflowxdr_more(x, sQuads));
343
344 switch (sType) {
345 case SFLOW_COUNTERS_SAMPLE_EXPANDED:
346 case SFLOW_COUNTERS_SAMPLE:
347 {
348 uint32_t csElements, e;
349 uint32_t ceTag, ceQuads, ceMark, csEnd;
350
351 x->csSeqNo = sflowxdr_next(x);
352 if (sType == SFLOW_COUNTERS_SAMPLE_EXPANDED) {
353 x->dsClass = sflowxdr_next(x);
354 x->dsIndex = sflowxdr_next(x);
355 } else {
356 uint32_t dsCombined = sflowxdr_next(x);
357 x->dsClass = dsCombined >> 24;
358 x->dsIndex = dsCombined & 0x00FFFFFF;
359 }
360
361 csElements = sflowxdr_next(x);
362 for (e = 0; e < csElements; e++) {
363 SFLOWXDR_assert(x, sflowxdr_more(x,2));
364 ceTag = sflowxdr_next(x);
365 ceQuads = sflowxdr_next(x) >> 2;
366 ceMark = sflowxdr_mark(x, ceQuads);
367 SFLOWXDR_assert(x, sflowxdr_more(x,ceQuads));
368 /* Only care about selected structures. Just record their
369 * offsets here. We'll read the fields out later. */
370 switch (ceTag) {
371 case SFLOW_TAG_CTR_IFCOUNTERS:
372 sflowxdr_mark_unique(x, &x->offset.IFCOUNTERS);
373 break;
374
375 /* Add others here... */
376 }
377
378 sflowxdr_skip(x, ceQuads);
379 SFLOWXDR_assert(x, sflowxdr_mark_ok(x, ceMark));
380 }
381
382 csEnd = sflowxdr_mark(x, 0);
383 process_counter_sample(x);
384 /* Make sure we pick up the decoding where we left off. */
385 sflowxdr_setc(x, csEnd);
386
387 /* Clear the offsets for the next sample. */
388 memset(&x->offset, 0, sizeof x->offset);
389 }
390 break;
391
392 case SFLOW_FLOW_SAMPLE:
393 case SFLOW_FLOW_SAMPLE_EXPANDED:
394 {
395 uint32_t fsElements, e;
396 uint32_t feTag, feQuads, feMark, fsEnd;
397 x->fsSeqNo = sflowxdr_next(x);
398 if (sType == SFLOW_FLOW_SAMPLE_EXPANDED) {
399 x->dsClass = sflowxdr_next(x);
400 x->dsIndex = sflowxdr_next(x);
401 } else {
402 uint32_t dsCombined = sflowxdr_next(x);
403 x->dsClass = dsCombined >> 24;
404 x->dsIndex = dsCombined & 0x00FFFFFF;
405 }
406 x->meanSkipCount = sflowxdr_next(x);
407 x->samplePool = sflowxdr_next(x);
408 x->dropEvents = sflowxdr_next(x);
409 if (sType == SFLOW_FLOW_SAMPLE_EXPANDED) {
410 x->inputPortFormat = sflowxdr_next(x);
411 x->inputPort = sflowxdr_next(x);
412 x->outputPortFormat = sflowxdr_next(x);
413 x->outputPort = sflowxdr_next(x);
414 } else {
415 uint32_t inp, outp;
416
417 inp = sflowxdr_next(x);
418 outp = sflowxdr_next(x);
419 x->inputPortFormat = inp >> 30;
420 x->inputPort = inp & 0x3fffffff;
421 x->outputPortFormat = outp >> 30;
422 x->outputPort = outp & 0x3fffffff;
423 }
424 fsElements = sflowxdr_next(x);
425 for (e = 0; e < fsElements; e++) {
426 SFLOWXDR_assert(x, sflowxdr_more(x,2));
427 feTag = sflowxdr_next(x);
428 feQuads = sflowxdr_next(x) >> 2;
429 feMark = sflowxdr_mark(x, feQuads);
430 SFLOWXDR_assert(x, sflowxdr_more(x,feQuads));
431 /* Only care about selected structures. Just record their
432 * offsets here. We'll read the fields out below. */
433 switch (feTag) {
434 case SFLOW_TAG_PKT_HEADER:
435 sflowxdr_mark_unique(x, &x->offset.HEADER);
436 break;
437
438 case SFLOW_TAG_PKT_SWITCH:
439 sflowxdr_mark_unique(x, &x->offset.SWITCH);
440 break;
441
442 /* Add others here... */
443 }
444
445 sflowxdr_skip(x, feQuads);
446 SFLOWXDR_assert(x, sflowxdr_mark_ok(x, feMark));
447 }
448
449 fsEnd = sflowxdr_mark(x, 0);
450 process_flow_sample(x);
451 /* Make sure we pick up the decoding where we left off. */
452 sflowxdr_setc(x, fsEnd);
453
454 /* Clear the offsets for the next counter/flow sample. */
455 memset(&x->offset, 0, sizeof x->offset);
456 }
457 break;
458
459 default:
460 /* Skip other sample types. */
461 sflowxdr_skip(x, sQuads);
462 }
463 SFLOWXDR_assert(x, sflowxdr_mark_ok(x, sMark));
464 }
465}
466
467static void
468print_sflow(struct ofpbuf *buf)
469{
470 char *dgram_buf;
1f317cb5 471 int dgram_len = ofpbuf_size(buf);
8073dd31
NM
472 struct sflow_xdr xdrDatagram;
473 struct sflow_xdr *x = &xdrDatagram;
474
475 memset(x, 0, sizeof *x);
476 if (SFLOWXDR_try(x)) {
1f317cb5 477 SFLOWXDR_assert(x, (dgram_buf = ofpbuf_try_pull(buf, ofpbuf_size(buf))));
8073dd31
NM
478 sflowxdr_init(x, dgram_buf, dgram_len);
479 SFLOWXDR_assert(x, dgram_len >= SFLOW_MIN_LEN);
480 process_datagram(x);
481 } else {
482 // CATCH
483 printf("\n>>>>> ERROR in " __FILE__ " at line %u\n", x->errline);
484 }
485}
486
487int
488main(int argc, char *argv[])
489{
490 struct unixctl_server *server;
491 enum { MAX_RECV = 1500 };
492 const char *target;
493 struct ofpbuf buf;
494 bool exiting = false;
495 int error;
496 int sock;
497
498 proctitle_init(argc, argv);
499 set_program_name(argv[0]);
500 parse_options(argc, argv);
501
502 if (argc - optind != 1) {
503 ovs_fatal(0, "exactly one non-option argument required "
504 "(use --help for help)");
505 }
506 target = argv[optind];
507
508 sock = inet_open_passive(SOCK_DGRAM, target, 0, NULL, 0);
509 if (sock < 0) {
10a89ef0 510 ovs_fatal(0, "%s: failed to open (%s)", argv[1], ovs_strerror(-sock));
8073dd31
NM
511 }
512
513 daemon_save_fd(STDOUT_FILENO);
514 daemonize_start();
515
516 error = unixctl_server_create(NULL, &server);
517 if (error) {
518 ovs_fatal(error, "failed to create unixctl server");
519 }
520 unixctl_command_register("exit", "", 0, 0, test_sflow_exit, &exiting);
521
522 daemonize_complete();
523
524 ofpbuf_init(&buf, MAX_RECV);
525 for (;;) {
526 int retval;
527
528 unixctl_server_run(server);
529
530 ofpbuf_clear(&buf);
531 do {
1f317cb5 532 retval = read(sock, ofpbuf_data(&buf), buf.allocated);
8073dd31
NM
533 } while (retval < 0 && errno == EINTR);
534 if (retval > 0) {
535 ofpbuf_put_uninit(&buf, retval);
536 print_sflow(&buf);
537 fflush(stdout);
538 }
539
540 if (exiting) {
541 break;
542 }
543
544 poll_fd_wait(sock, POLLIN);
545 unixctl_server_wait(server);
546 poll_block();
547 }
548
549 return 0;
550}
551
552static void
553parse_options(int argc, char *argv[])
554{
555 enum {
977529e4
BP
556 DAEMON_OPTION_ENUMS,
557 VLOG_OPTION_ENUMS
8073dd31 558 };
07fc4ed3 559 static const struct option long_options[] = {
8073dd31
NM
560 {"verbose", optional_argument, NULL, 'v'},
561 {"help", no_argument, NULL, 'h'},
562 DAEMON_LONG_OPTIONS,
977529e4 563 VLOG_LONG_OPTIONS,
8073dd31
NM
564 {NULL, 0, NULL, 0},
565 };
566 char *short_options = long_options_to_short_options(long_options);
567
568 for (;;) {
569 int c = getopt_long(argc, argv, short_options, long_options, NULL);
570 if (c == -1) {
571 break;
572 }
573
574 switch (c) {
575 case 'h':
576 usage();
577
977529e4
BP
578 DAEMON_OPTION_HANDLERS
579 VLOG_OPTION_HANDLERS
8073dd31
NM
580
581 case '?':
582 exit(EXIT_FAILURE);
583
584 default:
585 abort();
586 }
587 }
588 free(short_options);
589}
590
591static void
592usage(void)
593{
594 printf("%s: sflow collector test utility\n"
595 "usage: %s [OPTIONS] PORT[:IP]\n"
596 "where PORT is the UDP port to listen on and IP is optionally\n"
597 "the IP address to listen on.\n",
598 program_name, program_name);
599 daemon_usage();
600 vlog_usage();
601 printf("\nOther options:\n"
602 " -h, --help display this help message\n");
603 exit(EXIT_SUCCESS);
604}
605
606static void
607test_sflow_exit(struct unixctl_conn *conn,
608 int argc OVS_UNUSED, const char *argv[] OVS_UNUSED,
609 void *exiting_)
610{
611 bool *exiting = exiting_;
612 *exiting = true;
613 unixctl_command_reply(conn, NULL);
614}