2 * Copyright (c) 2011, 2012, 2013, 2014 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.
21 #include <arpa/inet.h>
28 #include "command-line.h"
30 #include "dynamic-string.h"
34 #include "poll-loop.h"
35 #include "socket-util.h"
40 NO_RETURN
static void usage(void);
41 static void parse_options(int argc
, char *argv
[]);
43 static unixctl_cb_func test_sflow_exit
;
46 #define SFLOW_VERSION_5 5
47 #define SFLOW_MIN_LEN 36
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
[INET6_ADDRSTRLEN
+ 2];
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 char ipstr
[INET6_ADDRSTRLEN
];
329 inet_ntop(AF_INET6
, (const void *) &x
->agentAddr
.a
.ip6
,
330 ipstr
, INET6_ADDRSTRLEN
);
331 snprintf(x
->agentIPStr
, sizeof x
->agentIPStr
, "[%s]", ipstr
);
333 snprintf(x
->agentIPStr
, sizeof x
->agentIPStr
,
334 IP_FMT
, IP_ARGS(x
->agentAddr
.a
.ip4
));
337 /* Array of flow/counter samples. */
338 samples
= sflowxdr_next(x
);
339 for (s
= 0; s
< samples
; s
++) {
340 uint32_t sType
= sflowxdr_next(x
);
341 uint32_t sQuads
= sflowxdr_next(x
) >> 2;
342 uint32_t sMark
= sflowxdr_mark(x
, sQuads
);
343 SFLOWXDR_assert(x
, sflowxdr_more(x
, sQuads
));
346 case SFLOW_COUNTERS_SAMPLE_EXPANDED
:
347 case SFLOW_COUNTERS_SAMPLE
:
349 uint32_t csElements
, e
;
350 uint32_t ceTag
, ceQuads
, ceMark
, csEnd
;
352 x
->csSeqNo
= sflowxdr_next(x
);
353 if (sType
== SFLOW_COUNTERS_SAMPLE_EXPANDED
) {
354 x
->dsClass
= sflowxdr_next(x
);
355 x
->dsIndex
= sflowxdr_next(x
);
357 uint32_t dsCombined
= sflowxdr_next(x
);
358 x
->dsClass
= dsCombined
>> 24;
359 x
->dsIndex
= dsCombined
& 0x00FFFFFF;
362 csElements
= sflowxdr_next(x
);
363 for (e
= 0; e
< csElements
; e
++) {
364 SFLOWXDR_assert(x
, sflowxdr_more(x
,2));
365 ceTag
= sflowxdr_next(x
);
366 ceQuads
= sflowxdr_next(x
) >> 2;
367 ceMark
= sflowxdr_mark(x
, ceQuads
);
368 SFLOWXDR_assert(x
, sflowxdr_more(x
,ceQuads
));
369 /* Only care about selected structures. Just record their
370 * offsets here. We'll read the fields out later. */
372 case SFLOW_TAG_CTR_IFCOUNTERS
:
373 sflowxdr_mark_unique(x
, &x
->offset
.IFCOUNTERS
);
376 /* Add others here... */
379 sflowxdr_skip(x
, ceQuads
);
380 SFLOWXDR_assert(x
, sflowxdr_mark_ok(x
, ceMark
));
383 csEnd
= sflowxdr_mark(x
, 0);
384 process_counter_sample(x
);
385 /* Make sure we pick up the decoding where we left off. */
386 sflowxdr_setc(x
, csEnd
);
388 /* Clear the offsets for the next sample. */
389 memset(&x
->offset
, 0, sizeof x
->offset
);
393 case SFLOW_FLOW_SAMPLE
:
394 case SFLOW_FLOW_SAMPLE_EXPANDED
:
396 uint32_t fsElements
, e
;
397 uint32_t feTag
, feQuads
, feMark
, fsEnd
;
398 x
->fsSeqNo
= sflowxdr_next(x
);
399 if (sType
== SFLOW_FLOW_SAMPLE_EXPANDED
) {
400 x
->dsClass
= sflowxdr_next(x
);
401 x
->dsIndex
= sflowxdr_next(x
);
403 uint32_t dsCombined
= sflowxdr_next(x
);
404 x
->dsClass
= dsCombined
>> 24;
405 x
->dsIndex
= dsCombined
& 0x00FFFFFF;
407 x
->meanSkipCount
= sflowxdr_next(x
);
408 x
->samplePool
= sflowxdr_next(x
);
409 x
->dropEvents
= sflowxdr_next(x
);
410 if (sType
== SFLOW_FLOW_SAMPLE_EXPANDED
) {
411 x
->inputPortFormat
= sflowxdr_next(x
);
412 x
->inputPort
= sflowxdr_next(x
);
413 x
->outputPortFormat
= sflowxdr_next(x
);
414 x
->outputPort
= sflowxdr_next(x
);
418 inp
= sflowxdr_next(x
);
419 outp
= sflowxdr_next(x
);
420 x
->inputPortFormat
= inp
>> 30;
421 x
->inputPort
= inp
& 0x3fffffff;
422 x
->outputPortFormat
= outp
>> 30;
423 x
->outputPort
= outp
& 0x3fffffff;
425 fsElements
= sflowxdr_next(x
);
426 for (e
= 0; e
< fsElements
; e
++) {
427 SFLOWXDR_assert(x
, sflowxdr_more(x
,2));
428 feTag
= sflowxdr_next(x
);
429 feQuads
= sflowxdr_next(x
) >> 2;
430 feMark
= sflowxdr_mark(x
, feQuads
);
431 SFLOWXDR_assert(x
, sflowxdr_more(x
,feQuads
));
432 /* Only care about selected structures. Just record their
433 * offsets here. We'll read the fields out below. */
435 case SFLOW_TAG_PKT_HEADER
:
436 sflowxdr_mark_unique(x
, &x
->offset
.HEADER
);
439 case SFLOW_TAG_PKT_SWITCH
:
440 sflowxdr_mark_unique(x
, &x
->offset
.SWITCH
);
443 /* Add others here... */
446 sflowxdr_skip(x
, feQuads
);
447 SFLOWXDR_assert(x
, sflowxdr_mark_ok(x
, feMark
));
450 fsEnd
= sflowxdr_mark(x
, 0);
451 process_flow_sample(x
);
452 /* Make sure we pick up the decoding where we left off. */
453 sflowxdr_setc(x
, fsEnd
);
455 /* Clear the offsets for the next counter/flow sample. */
456 memset(&x
->offset
, 0, sizeof x
->offset
);
461 /* Skip other sample types. */
462 sflowxdr_skip(x
, sQuads
);
464 SFLOWXDR_assert(x
, sflowxdr_mark_ok(x
, sMark
));
469 print_sflow(struct ofpbuf
*buf
)
472 int dgram_len
= ofpbuf_size(buf
);
473 struct sflow_xdr xdrDatagram
;
474 struct sflow_xdr
*x
= &xdrDatagram
;
476 memset(x
, 0, sizeof *x
);
477 if (SFLOWXDR_try(x
)) {
478 SFLOWXDR_assert(x
, (dgram_buf
= ofpbuf_try_pull(buf
, ofpbuf_size(buf
))));
479 sflowxdr_init(x
, dgram_buf
, dgram_len
);
480 SFLOWXDR_assert(x
, dgram_len
>= SFLOW_MIN_LEN
);
484 printf("\n>>>>> ERROR in " __FILE__
" at line %u\n", x
->errline
);
489 test_sflow_main(int argc
, char *argv
[])
491 struct unixctl_server
*server
;
492 enum { MAX_RECV
= 1500 };
495 bool exiting
= false;
499 proctitle_init(argc
, argv
);
500 set_program_name(argv
[0]);
501 service_start(&argc
, &argv
);
502 parse_options(argc
, argv
);
504 if (argc
- optind
!= 1) {
505 ovs_fatal(0, "exactly one non-option argument required "
506 "(use --help for help)");
508 target
= argv
[optind
];
510 sock
= inet_open_passive(SOCK_DGRAM
, target
, 0, NULL
, 0, true);
512 ovs_fatal(0, "%s: failed to open (%s)", argv
[1], ovs_strerror(-sock
));
515 daemon_save_fd(STDOUT_FILENO
);
518 error
= unixctl_server_create(NULL
, &server
);
520 ovs_fatal(error
, "failed to create unixctl server");
522 unixctl_command_register("exit", "", 0, 0, test_sflow_exit
, &exiting
);
524 daemonize_complete();
526 ofpbuf_init(&buf
, MAX_RECV
);
530 unixctl_server_run(server
);
534 retval
= recv(sock
, ofpbuf_data(&buf
), buf
.allocated
, 0);
535 } while (retval
< 0 && errno
== EINTR
);
537 ofpbuf_put_uninit(&buf
, retval
);
546 poll_fd_wait(sock
, POLLIN
);
547 unixctl_server_wait(server
);
553 parse_options(int argc
, char *argv
[])
559 static const struct option long_options
[] = {
560 {"verbose", optional_argument
, NULL
, 'v'},
561 {"help", no_argument
, NULL
, 'h'},
566 char *short_options
= long_options_to_short_options(long_options
);
569 int c
= getopt_long(argc
, argv
, short_options
, long_options
, NULL
);
578 DAEMON_OPTION_HANDLERS
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
);
601 printf("\nOther options:\n"
602 " -h, --help display this help message\n");
607 test_sflow_exit(struct unixctl_conn
*conn
,
608 int argc OVS_UNUSED
, const char *argv
[] OVS_UNUSED
,
611 bool *exiting
= exiting_
;
613 unixctl_command_reply(conn
, NULL
);
616 OVSTEST_REGISTER("test-sflow", test_sflow_main
);