2
* Copyright (c) 2011, 2012, 2013 Nicira, Inc.
3
* Copyright (c) 2013 InMon Corp.
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:
9
* http://www.apache.org/licenses/LICENSE-2.0
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.
27
#include "command-line.h"
29
#include "dynamic-string.h"
33
#include "poll-loop.h"
34
#include "socket-util.h"
39
static void usage(void) NO_RETURN;
40
static void parse_options(int argc, char *argv[]);
42
static unixctl_cb_func test_sflow_exit;
45
#define SFLOW_VERSION_5 5
46
#define SFLOW_MIN_LEN 36
47
#define SFLOW_MAX_AGENTIP_STRLEN 64
49
/* Sample tag numbers. */
50
#define SFLOW_FLOW_SAMPLE 1
51
#define SFLOW_COUNTERS_SAMPLE 2
52
#define SFLOW_FLOW_SAMPLE_EXPANDED 3
53
#define SFLOW_COUNTERS_SAMPLE_EXPANDED 4
55
/* Structure element tag numbers. */
56
#define SFLOW_TAG_CTR_IFCOUNTERS 1
57
#define SFLOW_TAG_PKT_HEADER 1
58
#define SFLOW_TAG_PKT_SWITCH 1001
62
SFLOW_ADDRTYPE_undefined = 0,
84
struct sflow_addr agentAddr;
85
char agentIPStr[SFLOW_MAX_AGENTIP_STRLEN];
93
/* Sequence numbers. */
98
/* Structure offsets. */
105
/* Flow sample fields. */
106
uint32_t meanSkipCount;
109
uint32_t inputPortFormat;
111
uint32_t outputPortFormat;
115
#define SFLOWXDR_try(x) ((x->errline = setjmp(x->env)) == 0)
116
#define SFLOWXDR_throw(x) longjmp(x->env, __LINE__)
117
#define SFLOWXDR_assert(x, t) if (!(t)) SFLOWXDR_throw(x)
120
sflowxdr_init(struct sflow_xdr *x, void *buf, size_t len)
127
sflowxdr_next(struct sflow_xdr *x)
129
return ntohl(x->datap[x->i++]);
133
sflowxdr_next_n(struct sflow_xdr *x)
135
return x->datap[x->i++];
139
sflowxdr_more(const struct sflow_xdr *x, uint32_t q)
141
return q + x->i <= x->quads;
145
sflowxdr_skip(struct sflow_xdr *x, uint32_t q)
151
sflowxdr_mark(const struct sflow_xdr *x, uint32_t q)
157
sflowxdr_mark_ok(const struct sflow_xdr *x, uint32_t m)
163
sflowxdr_mark_unique(struct sflow_xdr *x, uint32_t *pi)
172
sflowxdr_setc(struct sflow_xdr *x, uint32_t j)
178
sflowxdr_str(const struct sflow_xdr *x)
180
return (const char *) (x->datap + x->i);
184
sflowxdr_next_int64(struct sflow_xdr *x)
187
scratch = sflowxdr_next(x);
189
scratch += sflowxdr_next(x);
194
process_counter_sample(struct sflow_xdr *x)
196
if (x->offset.IFCOUNTERS) {
197
sflowxdr_setc(x, x->offset.IFCOUNTERS);
198
printf("IFCOUNTERS");
199
printf(" dgramSeqNo=%"PRIu32, x->dgramSeqNo);
200
printf(" ds=%s>%"PRIu32":%"PRIu32,
201
x->agentIPStr, x->dsClass, x->dsIndex);
202
printf(" csSeqNo=%"PRIu32, x->csSeqNo);
203
printf(" ifindex=%"PRIu32, sflowxdr_next(x));
204
printf(" type=%"PRIu32, sflowxdr_next(x));
205
printf(" ifspeed=%"PRIu64, sflowxdr_next_int64(x));
206
printf(" direction=%"PRIu32, sflowxdr_next(x));
207
printf(" status=%"PRIu32, sflowxdr_next(x));
208
printf(" in_octets=%"PRIu64, sflowxdr_next_int64(x));
209
printf(" in_unicasts=%"PRIu32, sflowxdr_next(x));
210
printf(" in_multicasts=%"PRIu32, sflowxdr_next(x));
211
printf(" in_broadcasts=%"PRIu32, sflowxdr_next(x));
212
printf(" in_discards=%"PRIu32, sflowxdr_next(x));
213
printf(" in_errors=%"PRIu32, sflowxdr_next(x));
214
printf(" in_unknownprotos=%"PRIu32, sflowxdr_next(x));
215
printf(" out_octets=%"PRIu64, sflowxdr_next_int64(x));
216
printf(" out_unicasts=%"PRIu32, sflowxdr_next(x));
217
printf(" out_multicasts=%"PRIu32, sflowxdr_next(x));
218
printf(" out_broadcasts=%"PRIu32, sflowxdr_next(x));
219
printf(" out_discards=%"PRIu32, sflowxdr_next(x));
220
printf(" out_errors=%"PRIu32, sflowxdr_next(x));
221
printf(" promiscuous=%"PRIu32, sflowxdr_next(x));
227
bin_to_hex(int hexit)
229
return "0123456789ABCDEF"[hexit];
233
print_hex(const char *a, int len, char *buf, int bufLen)
235
unsigned char nextByte;
239
for (i = 0; i < len; i++) {
240
if (b > bufLen - 10) {
244
buf[b++] = bin_to_hex(nextByte >> 4);
245
buf[b++] = bin_to_hex(nextByte & 0x0f);
254
#define SFLOW_HEX_SCRATCH 1024
257
process_flow_sample(struct sflow_xdr *x)
259
if (x->offset.HEADER) {
261
char scratch[SFLOW_HEX_SCRATCH];
264
printf(" dgramSeqNo=%"PRIu32, x->dgramSeqNo);
265
printf(" ds=%s>%"PRIu32":%"PRIu32,
266
x->agentIPStr, x->dsClass, x->dsIndex);
267
printf(" fsSeqNo=%"PRIu32, x->fsSeqNo);
269
if (x->offset.SWITCH) {
270
sflowxdr_setc(x, x->offset.SWITCH);
271
printf(" in_vlan=%"PRIu32, sflowxdr_next(x));
272
printf(" in_priority=%"PRIu32, sflowxdr_next(x));
273
printf(" out_vlan=%"PRIu32, sflowxdr_next(x));
274
printf(" out_priority=%"PRIu32, sflowxdr_next(x));
277
sflowxdr_setc(x, x->offset.HEADER);
278
printf(" meanSkip=%"PRIu32, x->meanSkipCount);
279
printf(" samplePool=%"PRIu32, x->samplePool);
280
printf(" dropEvents=%"PRIu32, x->dropEvents);
281
printf(" in_ifindex=%"PRIu32, x->inputPort);
282
printf(" in_format=%"PRIu32, x->inputPortFormat);
283
printf(" out_ifindex=%"PRIu32, x->outputPort);
284
printf(" out_format=%"PRIu32, x->outputPortFormat);
285
printf(" hdr_prot=%"PRIu32, sflowxdr_next(x));
286
printf(" pkt_len=%"PRIu32, sflowxdr_next(x));
287
printf(" stripped=%"PRIu32, sflowxdr_next(x));
288
headerLen = sflowxdr_next(x);
289
printf(" hdr_len=%"PRIu32, headerLen);
290
print_hex(sflowxdr_str(x), headerLen, scratch, SFLOW_HEX_SCRATCH);
291
printf(" hdr=%s", scratch);
297
process_datagram(struct sflow_xdr *x)
301
SFLOWXDR_assert(x, (sflowxdr_next(x) == SFLOW_VERSION_5));
303
/* Read the sFlow header. */
304
x->agentAddr.type = sflowxdr_next(x);
305
switch (x->agentAddr.type) {
306
case SFLOW_ADDRTYPE_IP4:
307
x->agentAddr.a.ip4 = sflowxdr_next_n(x);
310
case SFLOW_ADDRTYPE_IP6:
311
x->agentAddr.a.ip6[0] = sflowxdr_next_n(x);
312
x->agentAddr.a.ip6[1] = sflowxdr_next_n(x);
313
x->agentAddr.a.ip6[2] = sflowxdr_next_n(x);
314
x->agentAddr.a.ip6[3] = sflowxdr_next_n(x);
317
case SFLOW_ADDRTYPE_undefined:
322
x->subAgentId = sflowxdr_next(x);
323
x->dgramSeqNo = sflowxdr_next(x);
324
x->uptime_mS = sflowxdr_next(x);
326
/* Store the agent address as a string. */
327
if (x->agentAddr.type == SFLOW_ADDRTYPE_IP6) {
328
snprintf(x->agentIPStr, SFLOW_MAX_AGENTIP_STRLEN,
329
"%04x:%04x:%04x:%04x",
330
x->agentAddr.a.ip6[0],
331
x->agentAddr.a.ip6[1],
332
x->agentAddr.a.ip6[2],
333
x->agentAddr.a.ip6[3]);
335
snprintf(x->agentIPStr, SFLOW_MAX_AGENTIP_STRLEN,
336
IP_FMT, IP_ARGS(x->agentAddr.a.ip4));
339
/* Array of flow/counter samples. */
340
samples = sflowxdr_next(x);
341
for (s = 0; s < samples; s++) {
342
uint32_t sType = sflowxdr_next(x);
343
uint32_t sQuads = sflowxdr_next(x) >> 2;
344
uint32_t sMark = sflowxdr_mark(x, sQuads);
345
SFLOWXDR_assert(x, sflowxdr_more(x, sQuads));
348
case SFLOW_COUNTERS_SAMPLE_EXPANDED:
349
case SFLOW_COUNTERS_SAMPLE:
351
uint32_t csElements, e;
352
uint32_t ceTag, ceQuads, ceMark, csEnd;
354
x->csSeqNo = sflowxdr_next(x);
355
if (sType == SFLOW_COUNTERS_SAMPLE_EXPANDED) {
356
x->dsClass = sflowxdr_next(x);
357
x->dsIndex = sflowxdr_next(x);
359
uint32_t dsCombined = sflowxdr_next(x);
360
x->dsClass = dsCombined >> 24;
361
x->dsIndex = dsCombined & 0x00FFFFFF;
364
csElements = sflowxdr_next(x);
365
for (e = 0; e < csElements; e++) {
366
SFLOWXDR_assert(x, sflowxdr_more(x,2));
367
ceTag = sflowxdr_next(x);
368
ceQuads = sflowxdr_next(x) >> 2;
369
ceMark = sflowxdr_mark(x, ceQuads);
370
SFLOWXDR_assert(x, sflowxdr_more(x,ceQuads));
371
/* Only care about selected structures. Just record their
372
* offsets here. We'll read the fields out later. */
374
case SFLOW_TAG_CTR_IFCOUNTERS:
375
sflowxdr_mark_unique(x, &x->offset.IFCOUNTERS);
378
/* Add others here... */
381
sflowxdr_skip(x, ceQuads);
382
SFLOWXDR_assert(x, sflowxdr_mark_ok(x, ceMark));
385
csEnd = sflowxdr_mark(x, 0);
386
process_counter_sample(x);
387
/* Make sure we pick up the decoding where we left off. */
388
sflowxdr_setc(x, csEnd);
390
/* Clear the offsets for the next sample. */
391
memset(&x->offset, 0, sizeof x->offset);
395
case SFLOW_FLOW_SAMPLE:
396
case SFLOW_FLOW_SAMPLE_EXPANDED:
398
uint32_t fsElements, e;
399
uint32_t feTag, feQuads, feMark, fsEnd;
400
x->fsSeqNo = sflowxdr_next(x);
401
if (sType == SFLOW_FLOW_SAMPLE_EXPANDED) {
402
x->dsClass = sflowxdr_next(x);
403
x->dsIndex = sflowxdr_next(x);
405
uint32_t dsCombined = sflowxdr_next(x);
406
x->dsClass = dsCombined >> 24;
407
x->dsIndex = dsCombined & 0x00FFFFFF;
409
x->meanSkipCount = sflowxdr_next(x);
410
x->samplePool = sflowxdr_next(x);
411
x->dropEvents = sflowxdr_next(x);
412
if (sType == SFLOW_FLOW_SAMPLE_EXPANDED) {
413
x->inputPortFormat = sflowxdr_next(x);
414
x->inputPort = sflowxdr_next(x);
415
x->outputPortFormat = sflowxdr_next(x);
416
x->outputPort = sflowxdr_next(x);
420
inp = sflowxdr_next(x);
421
outp = sflowxdr_next(x);
422
x->inputPortFormat = inp >> 30;
423
x->inputPort = inp & 0x3fffffff;
424
x->outputPortFormat = outp >> 30;
425
x->outputPort = outp & 0x3fffffff;
427
fsElements = sflowxdr_next(x);
428
for (e = 0; e < fsElements; e++) {
429
SFLOWXDR_assert(x, sflowxdr_more(x,2));
430
feTag = sflowxdr_next(x);
431
feQuads = sflowxdr_next(x) >> 2;
432
feMark = sflowxdr_mark(x, feQuads);
433
SFLOWXDR_assert(x, sflowxdr_more(x,feQuads));
434
/* Only care about selected structures. Just record their
435
* offsets here. We'll read the fields out below. */
437
case SFLOW_TAG_PKT_HEADER:
438
sflowxdr_mark_unique(x, &x->offset.HEADER);
441
case SFLOW_TAG_PKT_SWITCH:
442
sflowxdr_mark_unique(x, &x->offset.SWITCH);
445
/* Add others here... */
448
sflowxdr_skip(x, feQuads);
449
SFLOWXDR_assert(x, sflowxdr_mark_ok(x, feMark));
452
fsEnd = sflowxdr_mark(x, 0);
453
process_flow_sample(x);
454
/* Make sure we pick up the decoding where we left off. */
455
sflowxdr_setc(x, fsEnd);
457
/* Clear the offsets for the next counter/flow sample. */
458
memset(&x->offset, 0, sizeof x->offset);
463
/* Skip other sample types. */
464
sflowxdr_skip(x, sQuads);
466
SFLOWXDR_assert(x, sflowxdr_mark_ok(x, sMark));
471
print_sflow(struct ofpbuf *buf)
474
int dgram_len = buf->size;
475
struct sflow_xdr xdrDatagram;
476
struct sflow_xdr *x = &xdrDatagram;
478
memset(x, 0, sizeof *x);
479
if (SFLOWXDR_try(x)) {
480
SFLOWXDR_assert(x, (dgram_buf = ofpbuf_try_pull(buf, buf->size)));
481
sflowxdr_init(x, dgram_buf, dgram_len);
482
SFLOWXDR_assert(x, dgram_len >= SFLOW_MIN_LEN);
486
printf("\n>>>>> ERROR in " __FILE__ " at line %u\n", x->errline);
491
main(int argc, char *argv[])
493
struct unixctl_server *server;
494
enum { MAX_RECV = 1500 };
497
bool exiting = false;
501
proctitle_init(argc, argv);
502
set_program_name(argv[0]);
503
parse_options(argc, argv);
505
if (argc - optind != 1) {
506
ovs_fatal(0, "exactly one non-option argument required "
507
"(use --help for help)");
509
target = argv[optind];
511
sock = inet_open_passive(SOCK_DGRAM, target, 0, NULL, 0);
513
ovs_fatal(0, "%s: failed to open (%s)", argv[1], ovs_strerror(-sock));
516
daemon_save_fd(STDOUT_FILENO);
519
error = unixctl_server_create(NULL, &server);
521
ovs_fatal(error, "failed to create unixctl server");
523
unixctl_command_register("exit", "", 0, 0, test_sflow_exit, &exiting);
525
daemonize_complete();
527
ofpbuf_init(&buf, MAX_RECV);
531
unixctl_server_run(server);
535
retval = read(sock, buf.data, buf.allocated);
536
} while (retval < 0 && errno == EINTR);
538
ofpbuf_put_uninit(&buf, retval);
547
poll_fd_wait(sock, POLLIN);
548
unixctl_server_wait(server);
556
parse_options(int argc, char *argv[])
562
static const struct option long_options[] = {
563
{"verbose", optional_argument, NULL, 'v'},
564
{"help", no_argument, NULL, 'h'},
569
char *short_options = long_options_to_short_options(long_options);
572
int c = getopt_long(argc, argv, short_options, long_options, NULL);
581
DAEMON_OPTION_HANDLERS
597
printf("%s: sflow collector test utility\n"
598
"usage: %s [OPTIONS] PORT[:IP]\n"
599
"where PORT is the UDP port to listen on and IP is optionally\n"
600
"the IP address to listen on.\n",
601
program_name, program_name);
604
printf("\nOther options:\n"
605
" -h, --help display this help message\n");
610
test_sflow_exit(struct unixctl_conn *conn,
611
int argc OVS_UNUSED, const char *argv[] OVS_UNUSED,
614
bool *exiting = exiting_;
616
unixctl_command_reply(conn, NULL);