2 * Copyright (c) 2011, 2012 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], 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
[])
561 static struct option long_options
[] = {
562 {"verbose", optional_argument
, NULL
, 'v'},
563 {"help", no_argument
, NULL
, 'h'},
567 char *short_options
= long_options_to_short_options(long_options
);
570 int c
= getopt_long(argc
, argv
, short_options
, long_options
, NULL
);
580 vlog_set_verbosity(optarg
);
583 DAEMON_OPTION_HANDLERS
598 printf("%s: sflow collector test utility\n"
599 "usage: %s [OPTIONS] PORT[:IP]\n"
600 "where PORT is the UDP port to listen on and IP is optionally\n"
601 "the IP address to listen on.\n",
602 program_name
, program_name
);
605 printf("\nOther options:\n"
606 " -h, --help display this help message\n");
611 test_sflow_exit(struct unixctl_conn
*conn
,
612 int argc OVS_UNUSED
, const char *argv
[] OVS_UNUSED
,
615 bool *exiting
= exiting_
;
617 unixctl_command_reply(conn
, NULL
);