ftrmon.c (59212B)
1 /* 2 * 3 * This license is set out in https://raw.githubusercontent.com/Broadcom-Network-Switching-Software/OpenBCM/master/Legal/LICENSE file. 4 * 5 * Copyright 2007-2019 Broadcom Inc. All rights reserved. 6 * 7 * FlowTracker Remote record monitor 8 */ 9 10 #include <sal/types.h> 11 #include <shared/bsl.h> 12 #ifndef __KERNEL__ 13 #include <sys/types.h> 14 #include <netinet/in.h> 15 #endif 16 17 #include <soc/debug.h> 18 #include <soc/cm.h> 19 20 #include <sal/appl/io.h> 21 #include <sal/appl/sal.h> 22 #include <sal/core/thread.h> 23 #include <sal/core/sync.h> 24 #include <sal/core/spl.h> 25 #include <sal/core/libc.h> 26 27 #include <appl/diag/shell.h> 28 #include <appl/diag/parse.h> 29 #include <appl/diag/system.h> 30 #include <appl/diag/decode.h> 31 32 #include <bcm/rx.h> 33 #include <bcm/error.h> 34 #include <bcm/flowtracker.h> 35 36 #include <soc/shared/shr_pkt.h> 37 38 #if defined(BCM_FLOWTRACKER_SUPPORT) 39 40 #ifdef GHS 41 #define d_packet_format(pfx, decode, pkt, count, p) 42 #endif 43 44 /* Maximum name length of export elements. */ 45 #define BCMI_FT_EXPORT_ELEMENT_NAME_LEN_MAX 30 46 47 /* 48 * Maximum packet size we will read in. 49 */ 50 #define PW_PACKET_SIZE 10240 51 52 #define PW_COUNT_DEFAULT 100 53 54 55 static char ft_rmon_name[SOC_MAX_NUM_DEVICES][16]; 56 57 /* 58 * Define 2 of the public fields in the dv structure for our use. 59 */ 60 #define dv_ft_rmon_pup dv_public1.ptr 61 #define dv_ft_rmon_chan dv_public2.u32 62 63 64 /* 65 * Typedef: pup_t 66 * Purpose: Maps out each packet array entry - pkt_cnt is sort of 67 * useless, but it makes life easier so we use it to track 68 * the pack # received. 69 * 70 * Notes: Each entry is either FREE or on a circular/double linked 71 * list used for the LOG. When the LOG is full, there are 3 extra 72 * pups used to suppor the up to 3 outstanding DMA operations. 73 */ 74 typedef struct pup_s { 75 struct pup_s *pup_next, *pup_prev; /* Next and previous pointers */ 76 dv_t *pup_esw_dv; /* DMA descriptor, ESW SOC mode only */ 77 int pup_seqno; /* Packet # */ 78 void *pup_pkt; /* Actual packet pointer, for soc */ 79 bcm_pkt_t rx_pkt; /* Packet data for RX */ 80 } pup_t; 81 82 #define REPORT_COUNT 0x01 /* Report packet count */ 83 #define REPORT_RAW 0x02 /* Report Raw packet data */ 84 #define REPORT_DECODE 0x04 /* Report Decode */ 85 #define REPORT_DMA 0x08 /* Report RX Dma */ 86 #define REPORT_CHANNEL 0x10 /* Report RX channel */ 87 88 typedef struct pwu_s { 89 volatile uint32 pu_flags; /* Flags this unit PU_F_XXX */ 90 # define PU_F_RUN 0x01 91 # define PU_F_STOP 0x02 92 # define PU_F_SYNC 0x04 /* Wake up request thread */ 93 sal_mutex_t pu_lock; /* Syncronize updates */ 94 sal_spinlock_t pu_spinlock; /* Protect updates */ 95 int32 pu_channel; /* RX channel list 1 << ch # */ 96 # define PU_CHANNEL(_c) (1 << (_c)) 97 uint32 pu_report; /* Reporting level */ 98 uint32 pu_dump_options; /* Reporting level when dumping logs */ 99 COMPILER_DOUBLE pu_count_last; /* Last count displayed */ 100 COMPILER_DOUBLE pu_count_time; /* Last time count was displayed */ 101 sal_thread_t pu_pid; /* pid this unit */ 102 sal_sem_t pu_sema; /* Sleep semaphore this unit */ 103 sal_sem_t pu_sync; /* Sync command semaphore */ 104 105 /* 106 * pu_pending - a single linked list, where the head's prev pointer 107 * points to the tail. 108 * pu_ch_active - pointers to DMA active on the given channel. 109 */ 110 111 VOL pup_t *pu_pending; /* Pending interrupt servicing */ 112 VOL pup_t *pu_ch_active[MAX_N_DMA_CHAN+1]; /* Active DMA operations */ 113 int pu_ch_count[MAX_N_DMA_CHAN+1]; /* # received on channel */ 114 pup_t *pu_log; /* Last N packets - head is LRU */ 115 pup_t *pu_log_free; /* Last N packets - head is LRU */ 116 pup_t *pup_list; /* List of all PUPs for free */ 117 int pu_log_cnt; /* # packets on LOG list */ 118 int pu_log_max; /* Max # packets logged */ 119 # define PU_LOG_CNT 64 /* Default # log entries */ 120 uint32 pu_packet_received; /* total # received packets */ 121 int pu_interval; /* usec between pkt rcv & next dma */ 122 123 int mode; /* What mode are we in */ 124 #define PW_MODE_RX 0 125 #define PW_MODE_PMUX 2 126 #define PW_INIT_STRINGS \ 127 "RX", \ 128 "PMUX" 129 int chan; 130 int rx_pri; 131 uint32 rx_flags; 132 int pkt_size; 133 bcm_rx_cfg_t rx_cfg; 134 int rate; /* Current global rate, pps */ 135 int last_pkt_count; /* How many pkts processed last wake */ 136 137 int init_done; 138 uint32 hdr_dump; /* Header dump for cmicx */ 139 } pwu_t; 140 141 char *ft_rmon_modes[2] = {PW_INIT_STRINGS}; 142 143 STATIC bcm_rx_t ft_rmon_rx_callback(int unit, bcm_pkt_t *pkt, 144 void *cookie); 145 146 STATIC pup_t * ft_rmon_pup_alloc(int unit); 147 STATIC void ft_rmon_pup_free(int unit, pup_t *pup); 148 STATIC void ft_rmon_dump_packet(int unit, pup_t *pup, uint32 report); 149 STATIC int ft_rmon_channel_config(int unit); 150 STATIC void ft_rmon_thread(void *up); 151 STATIC void ft_rmon_process_pending(int unit); 152 STATIC cmd_result_t ft_rmon_dump_log(int unit, int n); 153 154 pwu_t ft_rmon_units[SOC_MAX_NUM_DEVICES]; 155 156 STATIC void 157 ft_rmon_init(int unit) 158 { 159 pwu_t *pu = &ft_rmon_units[unit]; /* Control this unit */ 160 161 if (pu->init_done) { 162 return; 163 } 164 165 sal_snprintf(ft_rmon_name[unit], sizeof(ft_rmon_name[unit]), "bcmFtRMon.%d", unit); 166 167 /* 168 * sync and lock semaphores are only created once, the others 169 * are created and destroyed every time the packet watcher is 170 * started and stopped. 171 * 172 * We need to check if they already exist to support the 173 * pw reset option. 174 */ 175 if (!pu->pu_sync) { 176 pu->pu_sync = sal_sem_create("ft_rmon_sync", sal_sem_BINARY, 0); 177 } 178 if (!pu->pu_lock) { 179 pu->pu_lock = sal_mutex_create("ft_rmon_lock"); 180 } 181 if (!pu->pu_spinlock) { 182 pu->pu_spinlock = sal_spinlock_create("ft_rmon_spinlock"); 183 } 184 185 if (!pu->pu_sync || !pu->pu_lock || !pu->pu_spinlock) { 186 cli_out("%s ERROR: Could not allocate sync/lock\n", ft_rmon_name[unit]); 187 } 188 pu->chan = 0; 189 pu->rx_pri = 100; 190 pu->rx_flags = BCM_RCO_F_ALL_COS; 191 pu->pkt_size = 10 * 1024; 192 193 bcm_rx_cfg_get(unit, &pu->rx_cfg); 194 pu->rate = pu->rx_cfg.global_pps; 195 196 pu->init_done = TRUE; 197 } 198 199 STATIC int 200 _ft_rmon_set_rate(int unit) 201 { 202 pwu_t *pu = &ft_rmon_units[unit]; /* Control this unit */ 203 int rv = BCM_E_NONE; 204 205 /* Always calculate SOC interval in case modes are switched. */ 206 if (pu->rate <= 0) { 207 pu->pu_interval = 1000000; 208 pu->rate = 0; 209 } else { 210 pu->pu_interval = 1000000 / pu->rate; 211 } 212 213 switch (pu->mode) { 214 case PW_MODE_RX: 215 if ((rv = bcm_rx_rate_set(unit, pu->rate)) < 0) { 216 cli_out("PW RX rate set error: %s.\n", bcm_errmsg(rv)); 217 } 218 /* Then set the burst size to equal the rate */ 219 if ((rv = bcm_rx_burst_set(unit, pu->rate)) < 0) { 220 cli_out("PW RX burst set error: %s.\n", bcm_errmsg(rv)); 221 } 222 break; 223 default: 224 break; 225 } 226 227 return rv; 228 } 229 230 #define PW_LOCK(_u) sal_mutex_take(ft_rmon_units[_u].pu_lock, \ 231 sal_mutex_FOREVER) 232 #define PW_UNLOCK(_u) sal_mutex_give(ft_rmon_units[_u].pu_lock) 233 #define PW_SPIN_LOCK(_u) sal_spinlock_lock(ft_rmon_units[_u].pu_spinlock) 234 #define PW_SPIN_UNLOCK(_u) sal_spinlock_unlock(ft_rmon_units[_u].pu_spinlock) 235 236 int 237 ft_rmon_start(int unit, int sync) 238 /* 239 * Function: ft_rmon_start 240 * Purpose: Start the ipfix packet monitor 241 * Parameters: unit - unit to start. 242 * sync - if true, wait for mon to start. 243 * Returns: 0 - success, -1 failed. 244 */ 245 { 246 pwu_t *pu = &ft_rmon_units[unit]; /* Control this unit */ 247 248 ft_rmon_init(unit); 249 if (pu->pu_sync == NULL || pu->pu_lock == NULL || pu->pu_spinlock == NULL) { 250 return -1; 251 } 252 253 sal_memset(pu->pu_ch_active, 0, sizeof(pu->pu_ch_active)); 254 sal_memset(pu->pu_ch_count, 0, sizeof(pu->pu_ch_count)); 255 256 pu->pu_pending = NULL; 257 pu->pu_log = NULL; 258 pu->pu_log_free = NULL; 259 pu->pu_log_cnt = 0; 260 261 if (ft_rmon_channel_config(unit) < 0) { 262 return -1; 263 } 264 265 pu->pu_sema = sal_sem_create("ft_rmon_thread", sal_sem_BINARY, 0); 266 if (pu->pu_sema == 0) { 267 return -1; 268 } 269 270 pu->pu_flags |= PU_F_RUN; 271 if (sync) { 272 pu->pu_flags |= PU_F_SYNC; 273 } 274 275 pu->pu_pid = 276 sal_thread_create(ft_rmon_name[unit], SAL_THREAD_STKSZ, 277 soc_property_get(unit, spn_DIAG_PW_THREAD_PRI, 100), 278 ft_rmon_thread, INT_TO_PTR(unit)); 279 280 if (pu->pu_pid == SAL_THREAD_ERROR) { 281 cli_out("%s: Unable to start task\n", ft_rmon_name[unit]); 282 sal_sem_destroy(pu->pu_sema); 283 pu->pu_sema = 0; 284 pu->pu_flags &= PU_F_RUN|PU_F_SYNC; 285 return -1; 286 } 287 288 pu->pu_count_last = 0; 289 pu->pu_count_time = SAL_TIME_DOUBLE(); 290 291 pu->pu_packet_received = 0; 292 293 if (sync) { 294 if (sal_sem_take(pu->pu_sync, sal_sem_FOREVER)) { 295 cli_out("%s: Failed to wait for start\n", ft_rmon_name[unit]); 296 return -1; 297 } 298 } 299 300 return 0; 301 } 302 303 cmd_result_t 304 ft_rmon_stop(int unit, int sync) 305 /* 306 * Function: ft_rmon_stop 307 * Purpose: Stop the ipfix packet monitor 308 * Parameters: unit - unit to stop. 309 * sync - if true, wait for rmon to finish. 310 * Returns: 0 - success, -1 failed. 311 */ 312 { 313 pwu_t *pu = &ft_rmon_units[unit]; /* Control this unit */ 314 soc_timeout_t to; 315 sal_usecs_t ft_rmon_timeout; 316 317 ft_rmon_timeout = soc_property_get(unit, spn_CDMA_TIMEOUT_USEC, 10000000); 318 319 pu->pu_flags |= PU_F_STOP; 320 if (sync) { 321 pu->pu_flags |= PU_F_SYNC; 322 } 323 sal_sem_give(pu->pu_sema); 324 soc_timeout_init(&to, ft_rmon_timeout, 0); 325 326 /* Check for ft_rmon_thread exit */ 327 while (pu->pu_pid != SAL_THREAD_ERROR) { 328 /* Handle the case of ft_rmon_thread not exiting */ 329 if (soc_timeout_check(&to)) { 330 cli_out("ft_rmon_stop: ft_rmon_thread did not exit\n"); 331 pu->pu_pid = SAL_THREAD_ERROR; 332 break; 333 } 334 335 sal_usleep(80000); 336 } 337 338 if (pu->pu_pid == SAL_THREAD_ERROR) { 339 /* Abnormal thread exit: Free semaphore resource */ 340 /* coverity[check_after_deref] */ 341 if (pu->pu_sema) { 342 /* Must check, because may have been destroyed by ft_rmon_exit */ 343 sal_sem_destroy(pu->pu_sema); 344 pu->pu_sema = 0; 345 } 346 } 347 348 if (sync) { 349 (void)sal_sem_take(pu->pu_sync, sal_sem_FOREVER); 350 } else { 351 cli_out("%s: Termination requested...\n", ft_rmon_name[unit]); 352 } 353 return CMD_OK; 354 } 355 356 int 357 ft_rmon_running(int unit) 358 /* 359 * Function: ft_rmon_running 360 * Purpose: Determine if ipfix rmon is running for the specified 361 * unit. 362 * Parameters: unit - unit # 363 * Returns: 0 - not running, !0 - running 364 */ 365 { 366 return ft_rmon_units[unit].pu_flags & PU_F_RUN; 367 } 368 369 STATIC void 370 ft_rmon_exit(int unit, int stat) 371 /* 372 * Function: ft_rmon_exit 373 * Purpose: Exit a ftrmon thread. 374 * Parameters: unit - unit #. 375 * stat - exit status (passed to exit). 376 * Returns: Does not return. 377 * 378 * Notes: ASSUMES LOCK held on entry, released in this routine 379 */ 380 { 381 pwu_t *pu = &ft_rmon_units[unit]; 382 int rv; 383 char thread_name[SAL_THREAD_NAME_MAX_LEN]; 384 sal_thread_t thread; 385 386 thread = sal_thread_self(); 387 thread_name[0] = 0; 388 sal_thread_name(thread, thread_name, sizeof (thread_name)); 389 390 switch (pu->mode) { 391 case PW_MODE_RX: 392 /* ft_rmon_exit() is called holding the lock. Give it up 393 * momentarily while we stop the RX task. 394 */ 395 PW_UNLOCK(unit); 396 if ((rv = bcm_rx_stop(unit, NULL)) < 0) { 397 cli_out("FtRemoteMon stop error: Cannot stop RX: %s.\n", 398 bcm_errmsg(rv)); 399 } 400 if ((rv = bcm_rx_queue_unregister(unit, BCM_RX_COS_ALL, 401 ft_rmon_rx_callback, pu->rx_pri)) 402 < 0) { 403 cli_out("FtRemoteMon stop error: Cannot unregister handler: %s.\n", 404 bcm_errmsg(rv)); 405 } 406 PW_LOCK(unit); 407 408 break; 409 default: 410 break; 411 } 412 413 if (pu->pu_sema) { 414 sal_sem_destroy(pu->pu_sema); 415 pu->pu_sema = 0; 416 } 417 418 pu->pu_pid = SAL_THREAD_ERROR; 419 420 /* Put logs on the free list - not fast but .... oh well */ 421 422 if (pu->pu_log != NULL) { 423 pu->pu_log->pup_prev->pup_next = NULL; /* NUll list */ 424 while (pu->pu_log != NULL) { 425 pup_t *pup = pu->pu_log->pup_next; 426 ft_rmon_pup_free(unit, pu->pu_log); 427 pu->pu_log = pup; 428 } 429 } 430 431 /* Now free the free list */ 432 433 while (pu->pu_log_free) { 434 pup_t *pup = pu->pu_log_free; 435 pu->pu_log_free = pup->pup_next; 436 } 437 if (pu->pup_list) { 438 sal_free(pu->pup_list); 439 } 440 441 if (pu->pu_flags & PU_F_SYNC) { 442 sal_sem_give(pu->pu_sync); 443 } else { 444 LOG_VERBOSE(BSL_LS_SOC_COMMON, 445 (BSL_META_U(unit, 446 "FtRemoteMon-Daemon[%d]: Exiting\n"), unit)); 447 } 448 449 pu->pu_flags = 0; 450 451 PW_UNLOCK(unit); 452 if (stat < 0) { 453 LOG_ERROR(BSL_LS_SOC_PKT, 454 (BSL_META_U(unit, 455 "AbnormalThreadExit:%s\n"), thread_name)); 456 } 457 458 sal_thread_exit(stat); 459 } 460 461 /* Start the ipfix packet rmon operation depending on the mode */ 462 STATIC int 463 _ft_rmon_start_op(int unit) 464 { 465 pwu_t *pu = &ft_rmon_units[unit]; 466 int pkt_size; 467 int rv = BCM_E_NONE; 468 469 switch (pu->mode) { 470 case PW_MODE_RX: 471 pu->rx_cfg.global_pps = pu->rate; 472 473 /* 474 * Allow the ipfix rmon to run in truncating mode 475 * by allocating smaller Rx buffers and accepting 476 * oversized packets on all Rx DMA channels. 477 */ 478 pkt_size = soc_property_get(unit, spn_DIAG_PW_BUFFER_SIZE, -1); 479 480 if (pkt_size >= 68) { 481 int chan; 482 dma_chan_t dma_chan; 483 pu->rx_cfg.pkt_size = pkt_size; 484 485 if (soc_feature(unit, soc_feature_cmicx)) { 486 dma_chan = BCM_CMICX_RX_CHANNELS; 487 } else { 488 dma_chan = BCM_RX_CHANNELS; 489 } 490 491 for (chan = 0; chan < dma_chan; chan++) { 492 pu->rx_cfg.chan_cfg[chan].flags |= BCM_RX_F_OVERSIZED_OK; 493 } 494 } 495 496 if ((rv = bcm_rx_start(unit, &pu->rx_cfg)) < 0) { 497 return rv; 498 } 499 500 if ((rv = bcm_rx_queue_register(unit, "RX CMD", BCM_RX_COS_ALL, 501 ft_rmon_rx_callback, pu->rx_pri, NULL, pu->rx_flags)) < 0) { 502 return rv; 503 } 504 break; 505 default: 506 assert("Unknown pkt watcher mode"); 507 break; 508 } 509 510 return rv; 511 } 512 513 STATIC void 514 ft_rmon_thread(void *up) 515 /* 516 * Function: ft_rmon_thread 517 * Purpose: Waits for packets to arrive destined for the CPU and 518 * saves them in a queue. 519 * Parameters: unit - SOC unit # 520 * Returns: Does not return. 521 */ 522 { 523 int unit = PTR_TO_INT(up); 524 int i; 525 pwu_t *pu = &ft_rmon_units[unit]; 526 int rv; 527 int pup_count; 528 pup_t *pup; 529 530 PW_LOCK(unit); 531 532 /* Allocate packet pointer array */ 533 534 if (0 == pu->pu_log_max) { 535 pu->pu_log_max = PW_COUNT_DEFAULT; 536 } 537 538 pup_count = pu->pu_log_max; 539 540 /* Bump up pup_count to be twice the log size so pups will 541 * be available even when log queue is full. 542 */ 543 pup_count += pu->pu_log_max; 544 545 pu->pup_list = sal_alloc(sizeof(pup_t) * pup_count, "FtRemoteMon-pup"); 546 if (pu->pup_list == NULL) { 547 ft_rmon_exit(unit, -1); 548 } 549 /* No forwaqrding null. If the allocation failed, the call to ft_rmon_exit will end the process and we sould never reach here. */ 550 /* coverity[var_deref_model:FALSE] */ 551 sal_memset(pu->pup_list, 0, sizeof(pup_t) * pup_count); 552 553 for (i = 0; i < pup_count; i++) { 554 555 pup = &pu->pup_list[i]; 556 557 ft_rmon_pup_free(unit, pup); /* Put on free list */ 558 } 559 560 if (pu->pu_flags & PU_F_SYNC) { 561 pu->pu_flags &= ~PU_F_SYNC; 562 sal_sem_give(pu->pu_sync); 563 } else { 564 cli_out("FtRemoteMon-daemon[%d] -- Started\n", unit); 565 } 566 567 /* Kick Start channels */ 568 if ((rv = _ft_rmon_start_op(unit)) < 0) { 569 cli_out("FtRemoteMon rx mode: Cannot start %s: %s.\n", ft_rmon_modes[pu->mode], 570 bcm_errmsg(rv)); 571 ft_rmon_exit(unit, -1); 572 } 573 574 PW_UNLOCK(unit); 575 576 /* 577 * Check if there are any actions for us. 578 */ 579 while (1) { 580 int sem_rv; 581 582 sem_rv = sal_sem_take(ft_rmon_units[unit].pu_sema, sal_sem_FOREVER); 583 584 if (sem_rv < 0) { 585 cli_out("Failed sem_take, exiting\n"); 586 ft_rmon_exit(unit, -1); 587 } 588 589 PW_LOCK(unit); /* Block log dumps */ 590 591 if (pu->pu_flags & PU_F_STOP) { /* Time to exit */ 592 ft_rmon_exit(unit, 0); 593 } 594 595 ft_rmon_process_pending(unit); 596 597 PW_UNLOCK(unit); 598 } 599 600 ft_rmon_exit(unit, 0); 601 } 602 603 STATIC int 604 ft_rmon_channel_config(int unit) 605 /* 606 * Function: ft_rmon_channel_config 607 * Purpose: Configure DMA RX channels for PW. 608 * Parameters: unit - unit to configure. 609 * Returns: 0 - success 610 * -1 - failed. 611 */ 612 { 613 pwu_t *pu = &ft_rmon_units[unit]; /* Control this unit */ 614 dma_chan_t tx, i; 615 int rv = SOC_E_NONE; 616 dma_chan_t dma_chan; 617 618 if (pu->pu_channel == 0) { /* Use default */ 619 return 0; 620 } 621 622 if (soc_feature(unit, soc_feature_cmicx)) { 623 dma_chan = CMICX_N_DMA_CHAN; 624 } else { 625 dma_chan = N_DMA_CHAN; 626 } 627 628 /* Pick lowest # free channel for TX default */ 629 tx = -1; 630 for (i = 0; i < dma_chan; i++) { 631 if (pu->pu_channel & (1 << i)) { 632 rv = soc_dma_chan_config(unit, i, DV_RX, SOC_DMA_F_INTR); 633 if (rv < 0) { 634 cli_out("%s: DMA channel configuration failed: %s\n", 635 ft_rmon_name[unit], soc_errmsg(rv)); 636 } 637 } else if (tx == -1) { 638 tx = i; 639 } 640 } 641 642 if ((tx == -1) || 643 ((rv = soc_dma_chan_config(unit, tx, DV_TX, 644 SOC_DMA_F_INTR |SOC_DMA_F_DEFAULT)) < 0)) { 645 cli_out("%s: Unable to configure TX DMA channel: %s\n", 646 ft_rmon_name[unit], 647 tx == -1 ? "No remaining channels" : soc_errmsg(rv)); 648 649 rv = soc_dma_init(unit); 650 cli_out("%s: Unable to re-initialize DMA: %s\n", 651 ft_rmon_name[unit], 652 soc_errmsg(rv)); 653 654 return -1; 655 } 656 return 0; 657 } 658 659 static const parse_pm_t ft_rmon_report_table[] = { 660 {"Count", REPORT_COUNT}, 661 {"DECode", REPORT_DECODE}, 662 {"Raw", REPORT_RAW}, 663 {"DMA", REPORT_DMA}, 664 {"CHannel", REPORT_CHANNEL}, 665 {"@ALL", ~0}, 666 {"@*", ~0}, 667 {NULL, 0} 668 }; 669 670 static const parse_pm_t ft_rmon_channel_table[] = { 671 {"0", 1 << 0}, 672 {"1", 1 << 1}, 673 {"2", 1 << 2}, 674 {"3", 1 << 3}, 675 {"4", 1 << 4}, 676 {"5", 1 << 5}, 677 {"6", 1 << 6}, 678 {"7", 1 << 7}, 679 {NULL, 0} 680 }; 681 682 /**************************************************************** 683 * 684 * Common ipfix rmon routines 685 */ 686 687 688 STATIC pup_t * 689 ft_rmon_pup_alloc(int unit) 690 /* 691 * Function: ft_rmon_alloc 692 * Purpose: Alloc a pup off the free list. 693 * Parameters: unit - unit # 694 * Returns: Pointer to pup. 695 */ 696 { 697 pwu_t *pu = &ft_rmon_units[unit]; 698 pup_t *pup; 699 700 PW_LOCK(unit); 701 pup = pu->pu_log_free; 702 if (pup) { 703 pu->pu_log_free = pup->pup_next; 704 } 705 PW_UNLOCK(unit); 706 707 return(pup); 708 } 709 710 STATIC void 711 ft_rmon_pup_free(int unit, pup_t *pup) 712 /* 713 * Function: ft_rmon_pup_free 714 * Purpose: Put a pup on the free list. 715 * Parameters: unit - unit # 716 * pup - pointer to pup to free. 717 * Returns: Nothing. 718 */ 719 { 720 pwu_t *pu = &ft_rmon_units[unit]; 721 722 pup->pup_next = pu->pu_log_free; 723 pu->pu_log_free = pup; 724 725 if (pu->mode == PW_MODE_RX) { 726 bcm_rx_free(unit, pup->rx_pkt.alloc_ptr); 727 } 728 } 729 730 STATIC void 731 ft_rmon_dump_start(int unit, char *pfx, pup_t *pup, uint32 report, 732 int chan, int count) 733 { 734 char *channel; 735 736 if (!report) { 737 return; 738 } 739 740 cli_out("\n"); 741 742 if (report & REPORT_CHANNEL) { 743 switch (chan) { 744 case -1: channel = ""; break; 745 case 0: channel = "-chn0"; break; 746 case 1: channel = "-chn1"; break; 747 case 2: channel = "-chn2"; break; 748 case 3: channel = "-chn3"; break; 749 case 4: channel = "-chn4"; break; 750 case 5: channel = "-chn5"; break; 751 case 6: channel = "-chn6"; break; 752 case 7: channel = "-chn7"; break; 753 default: channel = "-----"; break; 754 } 755 } else { 756 channel = ""; 757 } 758 759 sal_sprintf(pfx, "Packet[%d]%s: ", pup->pup_seqno, channel); 760 761 if ((report & REPORT_COUNT) && (count >= 0)) { 762 cli_out("%sTotal %d\n", pfx, count); 763 } 764 765 } 766 767 768 extern int 769 bcma_ft_setid_template_id_get(int unit, uint16 setid, 770 bcm_flowtracker_export_template_t *template_id); 771 772 #define BCMI_FT_XLATE_ELEM(_name, _size) \ 773 bcmFlowtrackerExportElementType##_name, \ 774 _size, \ 775 #_name 776 777 778 typedef struct bcma_ft_export_elements_map_s { 779 bcm_flowtracker_export_element_type_t export_element; 780 int default_data_size; 781 char element_name[BCMI_FT_EXPORT_ELEMENT_NAME_LEN_MAX]; 782 } bcma_ft_export_elements_map_t; 783 784 785 static bcma_ft_export_elements_map_t bcma_ft_export_elements_map[] = { 786 { BCMI_FT_XLATE_ELEM(SrcIPv4, 4 ) }, 787 { BCMI_FT_XLATE_ELEM(DstIPv4, 4 ) }, 788 { BCMI_FT_XLATE_ELEM(L4SrcPort, 2 ) }, 789 { BCMI_FT_XLATE_ELEM(L4DstPort, 2 ) }, 790 { BCMI_FT_XLATE_ELEM(SrcIPv6, 16 ) }, 791 { BCMI_FT_XLATE_ELEM(DstIPv6, 16 ) }, 792 { BCMI_FT_XLATE_ELEM(IPProtocol, 1 ) }, 793 { BCMI_FT_XLATE_ELEM(PktCount, 4 ) }, 794 { BCMI_FT_XLATE_ELEM(ByteCount, 4 ) }, 795 { BCMI_FT_XLATE_ELEM(VRF, 2 ) }, 796 { BCMI_FT_XLATE_ELEM(TTL, 1 ) }, 797 { BCMI_FT_XLATE_ELEM(IPLength, 2 ) }, 798 { BCMI_FT_XLATE_ELEM(IP6Length, 2 ) }, 799 { BCMI_FT_XLATE_ELEM(TcpWindowSize, 2 ) }, 800 { BCMI_FT_XLATE_ELEM(DosAttack, 4 ) }, 801 { BCMI_FT_XLATE_ELEM(VxlanNetworkId, 3 ) }, 802 { BCMI_FT_XLATE_ELEM(NextHeader, 1 ) }, 803 { BCMI_FT_XLATE_ELEM(HopLimit, 1 ) }, 804 { BCMI_FT_XLATE_ELEM(InnerSrcIPv4, 4 ) }, 805 { BCMI_FT_XLATE_ELEM(InnerDstIPv4, 4 ) }, 806 { BCMI_FT_XLATE_ELEM(InnerL4SrcPort, 2 ) }, 807 { BCMI_FT_XLATE_ELEM(InnerL4DstPort, 2 ) }, 808 { BCMI_FT_XLATE_ELEM(InnerSrcIPv6, 16 ) }, 809 { BCMI_FT_XLATE_ELEM(InnerDstIPv6, 16 ) }, 810 { BCMI_FT_XLATE_ELEM(InnerIPProtocol, 1 ) }, 811 { BCMI_FT_XLATE_ELEM(InnerTTL, 1 ) }, 812 { BCMI_FT_XLATE_ELEM(InnerIPLength, 2 ) }, 813 { BCMI_FT_XLATE_ELEM(InnerNextHeader, 1 ) }, 814 { BCMI_FT_XLATE_ELEM(InnerHopLimit, 1 ) }, 815 { BCMI_FT_XLATE_ELEM(InnerIP6Length, 2 ) }, 816 { BCMI_FT_XLATE_ELEM(TcpFlags, 1 ) }, 817 { BCMI_FT_XLATE_ELEM(OuterVlanTag, 2 ) }, 818 { BCMI_FT_XLATE_ELEM(FlowtrackerGroup, 2 ) }, 819 { BCMI_FT_XLATE_ELEM(ExportReasons, 2 ) }, 820 { BCMI_FT_XLATE_ELEM(ExportFlags, 1 ) }, 821 { BCMI_FT_XLATE_ELEM(Reserved, 1 ) }, 822 { BCMI_FT_XLATE_ELEM(TimestampNewLearn, 6 ) }, 823 { BCMI_FT_XLATE_ELEM(TimestampFlowStart, 6 ) }, 824 { BCMI_FT_XLATE_ELEM(TimestampFlowEnd, 6 ) }, 825 { BCMI_FT_XLATE_ELEM(TimestampCheckEvent1, 6 ) }, 826 { BCMI_FT_XLATE_ELEM(TimestampCheckEvent2, 6 ) }, 827 { BCMI_FT_XLATE_ELEM(InnerDosAttack, 4 ) }, 828 { BCMI_FT_XLATE_ELEM(TunnelClass, 3 ) }, 829 { BCMI_FT_XLATE_ELEM(FlowtrackerCheck, 4 ) }, 830 }; 831 832 STATIC int 833 bcma_ft_export_elem_name_get(int unit, 834 bcm_flowtracker_export_element_type_t info_elem, 835 int *data_size, 836 char *elem_name) 837 { 838 int ix; 839 840 for (ix = 0; ix < COUNTOF(bcma_ft_export_elements_map); ix++) { 841 if (bcma_ft_export_elements_map[ix].export_element == info_elem) { 842 if (elem_name) { 843 sal_strncpy(elem_name, bcma_ft_export_elements_map[ix].element_name, 844 BCMI_FT_EXPORT_ELEMENT_NAME_LEN_MAX); 845 } 846 if (data_size) { 847 *data_size = bcma_ft_export_elements_map[ix].default_data_size; 848 } 849 break; 850 } 851 } 852 853 if (ix == COUNTOF(bcma_ft_export_elements_map)) { 854 return BCM_E_PARAM; 855 } 856 857 return BCM_E_NONE; 858 } 859 860 void 861 format_ts(char buf[20], uint8 *ts) 862 { 863 sal_sprintf(buf, "%x%x%x%x%x%x", 864 (uint8)ts[0], (uint8)ts[1], (uint8)ts[2], 865 (uint8)ts[3], (uint8)ts[4], (uint8)ts[5]); 866 } 867 868 869 STATIC int 870 bcma_ipfix_data_record_print(int unit, 871 int set_length, 872 uint8 *data_set, 873 int num_elems, 874 bcm_flowtracker_export_element_info_t *list_elems) 875 { 876 int ix, ex; 877 char elem_name[BCMI_FT_EXPORT_ELEMENT_NAME_LEN_MAX]; 878 uint8 u8; 879 uint16 u16; 880 uint32 u32; 881 uint8 *data, *data_tmp; 882 int data_size; 883 bcm_ip6_t ip6; 884 char ip_str[IP6ADDR_STR_LEN]; 885 bcm_flowtracker_export_element_info_t *elem; 886 uint8 ts[6]; 887 char ts_str[20]; 888 889 890 data = data_set; 891 892 if (0) { 893 int ix; 894 for (ix = 0; ix < (set_length - 4); ix+=8) { 895 cli_out("%04d: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", 896 (ix/8), data[ix], data[ix+1], data[ix+2], data[ix+3], 897 data[ix+4], data[ix+5], data[ix+6], data[ix+7]); 898 } 899 } 900 901 902 for (ex = 0; ex < num_elems; ex++) { 903 elem = &(list_elems[ex]); 904 data_tmp = data; 905 906 if (BCM_FAILURE(bcma_ft_export_elem_name_get(unit, elem->element, &data_size, elem_name))) { 907 cli_out("Element name not found: elem = %d\n", elem->element); 908 return -1; 909 } 910 data_size = elem->data_size; 911 912 switch (elem->element) { 913 case bcmFlowtrackerExportElementTypeSrcIPv4: 914 case bcmFlowtrackerExportElementTypeDstIPv4: 915 case bcmFlowtrackerExportElementTypeInnerSrcIPv4: 916 case bcmFlowtrackerExportElementTypeInnerDstIPv4: 917 SHR_PKT_UNPACK_U32(data_tmp, u32); 918 format_ipaddr(ip_str, u32); 919 cli_out("Info Element:: %02d (%20s) | Size: %02d | Value: (%s)\n", elem->element, elem_name, elem->data_size, ip_str); 920 break; 921 case bcmFlowtrackerExportElementTypeSrcIPv6: 922 case bcmFlowtrackerExportElementTypeDstIPv6: 923 case bcmFlowtrackerExportElementTypeInnerSrcIPv6: 924 case bcmFlowtrackerExportElementTypeInnerDstIPv6: 925 for (ix = 0; ix < 16; ix++) { 926 SHR_PKT_UNPACK_U8(data_tmp, u8); 927 ip6[ix] = u8; 928 } 929 format_ip6addr(ip_str, ip6); 930 cli_out("Info Element:: %02d (%20s) | Size: %02d | Value: (%s)\n", elem->element, elem_name, elem->data_size, ip_str); 931 break; 932 case bcmFlowtrackerExportElementTypeTimestampNewLearn: 933 case bcmFlowtrackerExportElementTypeTimestampFlowStart: 934 case bcmFlowtrackerExportElementTypeTimestampFlowEnd: 935 case bcmFlowtrackerExportElementTypeTimestampCheckEvent1: 936 case bcmFlowtrackerExportElementTypeTimestampCheckEvent2: 937 for (ix = 0; ix < 6; ix++) { 938 SHR_PKT_UNPACK_U8(data_tmp, u8); 939 ts[ix] = u8; 940 } 941 sal_memset(ts_str, 0, 20); 942 format_ts(ts_str, ts); 943 cli_out("Info Element:: %02d (%20s) | Size: %02d | Value: (%s)\n", elem->element, elem_name, data_size, ts_str); 944 break; 945 946 case bcmFlowtrackerExportElementTypeReserved: 947 cli_out("Info Element:: %02d (%20s) | Size: %02d | Value: (%s)\n", elem->element, elem_name, elem->data_size, "????"); 948 break; 949 default: 950 switch (data_size) { 951 case 1: 952 SHR_PKT_UNPACK_U8(data_tmp, u8); 953 cli_out("Info Element:: %02d (%20s) | Size: %02d | Value: 0x%02x(%02d)\n", elem->element, elem_name, elem->data_size, u8, u8); 954 break; 955 case 2: 956 SHR_PKT_UNPACK_U16(data_tmp, u16); 957 cli_out("Info Element:: %02d (%20s) | Size: %02d | Value: 0x%04x(%04d)\n", elem->element, elem_name, elem->data_size, u16, u16); 958 break; 959 case 3: 960 SHR_PKT_UNPACK_U8(data_tmp, u8); 961 SHR_PKT_UNPACK_U16(data_tmp, u16); 962 u32 = ((u8 << 16) | u16); 963 cli_out("Info Element:: %02d (%20s) | Size: %02d | Value: 0x%06x(%06d)\n", elem->element, elem_name, elem->data_size, u32, u32); 964 break; 965 case 4: 966 SHR_PKT_UNPACK_U32(data_tmp, u32); 967 cli_out("Info Element:: %02d (%20s) | Size: %02d | Value: 0x%08x(%08d)\n", elem->element, elem_name, elem->data_size, u32, u32); 968 break; 969 default: 970 break; 971 972 } 973 } 974 975 data += (elem->data_size); 976 } 977 978 return BCM_E_NONE; 979 } 980 981 982 int bcma_ft_ipfix_print_record(int unit, int set_c, int payload_length, uint8 *data) 983 { 984 uint8 *data_set; 985 uint16 set_id, set_length; 986 int ix, num_els; 987 bcm_flowtracker_export_template_t template_id; 988 bcm_flowtracker_export_element_info_t *elem_list; 989 char elem_name[BCMI_FT_EXPORT_ELEMENT_NAME_LEN_MAX]; 990 int rv = BCM_E_NONE; 991 992 data_set = data; 993 SHR_PKT_UNPACK_U16(data_set, set_id); 994 SHR_PKT_UNPACK_U16(data_set, set_length); 995 if (set_length == 0) { 996 cli_out("Data Set with invalid length: id= %02d len = %02d\n", set_id, set_length); 997 return -1; 998 } 999 1000 cli_out("*************************************************\n"); 1001 cli_out("IPFIX data set # %02d, set_id = %02d (0x%02x) record length = %02d\n", 1002 set_c, set_id, set_id, set_length); 1003 1004 rv = bcma_ft_setid_template_id_get(unit, set_id, &template_id); 1005 if (BCM_FAILURE(rv)) { 1006 cli_out("No template created with set_id = %d\n", set_id); 1007 return -1; 1008 } 1009 1010 rv = bcm_flowtracker_export_template_get(unit, template_id, 1011 &set_id, 0, NULL, &num_els); 1012 if (BCM_FAILURE(rv)) { 1013 cli_out("template_get count failed with rv = %d\n", rv); 1014 return -1; 1015 } 1016 1017 1018 elem_list = sal_alloc((sizeof(bcm_flowtracker_export_element_info_t) * num_els), "template_get element list"); 1019 1020 rv = bcm_flowtracker_export_template_get(unit, template_id, 1021 &set_id, num_els, 1022 elem_list, &num_els); 1023 if (BCM_FAILURE(rv)) { 1024 cli_out("template_get list failed with rv = %d\n", rv); 1025 return -1; 1026 } 1027 1028 cli_out("Template Id matching with set id %d is %d and num of elements = %d\n", set_id, template_id, num_els); 1029 for (ix = 0; ix < num_els; ix++) { 1030 1031 rv = bcma_ft_export_elem_name_get(unit, elem_list[ix].element, NULL, elem_name); 1032 BCM_IF_ERROR_RETURN(rv); 1033 if (elem_list[ix].element == bcmFlowtrackerExportElementTypeFlowtrackerCheck) { 1034 cli_out("Template Id: %2d Info Element(%2d) = %2d (%20s) Size = %02d" 1035 " (0x%x)\n", template_id, ix, elem_list[ix].element, 1036 elem_name, elem_list[ix].data_size, elem_list[ix].check_id); 1037 } else { 1038 cli_out("Template Id: %2d Info Element(%2d) = %2d (%20s) Size = %02d\n", 1039 template_id, ix, elem_list[ix].element, elem_name, elem_list[ix].data_size); 1040 } 1041 } 1042 1043 rv = bcma_ipfix_data_record_print(unit, set_length, data_set, 1044 num_els, elem_list); 1045 if (BCM_FAILURE(rv)) { 1046 cli_out("ipfix data_set parsing failed for set = %d\n", set_id); 1047 return -1; 1048 } 1049 1050 cli_out("*************************************************\n"); 1051 1052 1053 return BCM_E_NONE; 1054 } 1055 1056 1057 int bcma_ft_ipfix_template_print(int unit, int payload_length, uint8 *payload_data) 1058 { 1059 uint16 d16; 1060 uint32 d32; 1061 uint16 set_id; 1062 int ix, set_c, set_length; 1063 uint8 *data, *data_set; 1064 1065 data = payload_data; 1066 1067 if (0) { 1068 for (ix = 0; ix < (payload_length - 4); ix+=8) { 1069 cli_out("%04d: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", 1070 (ix/8), data[ix], data[ix+1], data[ix+2], data[ix+3], 1071 data[ix+4], data[ix+5], data[ix+6], data[ix+7]); 1072 } 1073 } 1074 1075 /* Print ipfix header. */ 1076 cli_out("*************************************************\n"); 1077 SHR_PKT_UNPACK_U16(data, d16); 1078 cli_out("IPFIX version = %02d (0x%02x)\n", d16, d16); 1079 1080 SHR_PKT_UNPACK_U16(data, d16); 1081 cli_out("IPFIX message length = %02d (0x%02x)\n", d16, d16); 1082 1083 SHR_PKT_UNPACK_U32(data, d32); 1084 cli_out("IPFIX export timestamp = %02d (0x%02x)\n", d32, d32); 1085 1086 SHR_PKT_UNPACK_U32(data, d32); 1087 cli_out("IPFIX sequence number = %02d (0x%02x)\n", d32, d32); 1088 1089 SHR_PKT_UNPACK_U32(data, d32); 1090 cli_out("IPFIX observation domain id = %02d (0x%02x)\n", d32, d32); 1091 cli_out("*************************************************\n"); 1092 1093 payload_length -= 16; 1094 1095 set_c = 1; 1096 while (payload_length > 0) { 1097 data_set = data; 1098 SHR_PKT_UNPACK_U16(data_set, set_id); 1099 SHR_PKT_UNPACK_U16(data_set, set_length); 1100 1101 data_set = data; 1102 if (bcma_ft_ipfix_print_record(unit, set_c, set_length, data_set) != BCM_E_NONE) { 1103 cli_out("set_id:%d, set:%d:ipfix parsing failed\n", set_id, set_c); 1104 return BCM_E_FAIL; 1105 } 1106 1107 set_c++; 1108 data += set_length; 1109 payload_length -= set_length; 1110 } 1111 1112 return BCM_E_NONE; 1113 1114 } 1115 1116 STATIC char *_ft_rmon_reason_names[bcmRxReasonCount] = BCM_RX_REASON_NAMES_INITIALIZER; 1117 1118 STATIC void 1119 ft_rmon_dump_packet_rx(int unit, pup_t *pup, uint32 report) 1120 { 1121 bcm_pkt_t *pkt; 1122 char pfx[64]; 1123 char info_str[256]; 1124 pwu_t *pu = &ft_rmon_units[unit]; 1125 int i; 1126 1127 pkt = &pup->rx_pkt; 1128 1129 1130 ft_rmon_dump_start(unit, pfx, pup, report, pkt->dma_channel, 1131 pu->pu_packet_received); 1132 1133 if (report & REPORT_DMA) { 1134 if (pkt->_dv) { 1135 soc_dma_dump_dv(unit, pfx, (dv_t *)pkt->_dv); 1136 } 1137 } 1138 1139 if (report & REPORT_RAW) { 1140 #if defined(BCM_CMICX_SUPPORT) 1141 if (soc_feature(unit, soc_feature_cmicx)) { 1142 if (pu->hdr_dump == 1) { 1143 soc_dma_ep_to_cpu_hdr_dump(unit, pfx, pkt->_pkt_data.data - pkt->egress_to_cpu_hdr_size, 1144 pkt->egress_to_cpu_hdr_size, 0); 1145 } 1146 1147 if (report & REPORT_DMA) { 1148 uint8 *c = (uint8 *)pkt->_pkt_data.data; 1149 c -= pkt->egress_to_cpu_hdr_size; 1150 #ifdef LE_HOST 1151 { 1152 /* Higig field accessors expect network byte ordering, 1153 * so we must reverse the bytes on LE hosts */ 1154 int word; 1155 uint32 *hg_data = (uint32 *)c; 1156 for (word = 0; word < BYTES2WORDS(pkt->egress_to_cpu_hdr_size); word++) { 1157 hg_data[word] = _shr_swap32(hg_data[word]); 1158 } 1159 c = (uint8 *)hg_data; 1160 } 1161 #endif 1162 soc_dma_higig_dump(unit, pfx, c, 0, 0, NULL); 1163 soc_dma_ep_to_cpu_hdr_decoded_dump(unit, pfx, (void *)c, 1, pkt->egress_to_cpu_hdr_size); 1164 } 1165 } 1166 #endif 1167 soc_dma_ether_dump(unit, pfx, pkt->_pkt_data.data, pkt->pkt_len, 0); 1168 if (SOC_IS_TRIDENT3X(unit)) { 1169 cli_out("%slength %d (%d). rx-port %d. cos %d. prio_int %d. vlan %d. " 1170 "reason 0x%x. %s.\n", pfx, 1171 pkt->pkt_len, pkt->tot_len, pkt->rx_port, 1172 pkt->cos, pkt->prio_int, pkt->vlan, 1173 pkt->rx_reason, 1174 (pkt->rx_untagged) ? 1175 "Untagged" : "Ingress tagged"); 1176 } else { 1177 cli_out("%slength %d (%d). rx-port %d. cos %d. prio_int %d. vlan %d. " 1178 "reason 0x%x. %s.\n", pfx, 1179 pkt->pkt_len, pkt->tot_len, pkt->rx_port, 1180 pkt->cos, pkt->prio_int, pkt->vlan, 1181 pkt->rx_reason, 1182 (pkt->rx_untagged & BCM_PKT_OUTER_UNTAGGED) ? 1183 ((pkt->rx_untagged & BCM_PKT_INNER_UNTAGGED) ? 1184 "Untagged" : "Inner tagged") : 1185 ((pkt->rx_untagged & BCM_PKT_INNER_UNTAGGED) ? 1186 "Outer tagged" : "Double tagged")); 1187 } 1188 if ((pkt->stk_flags & BCM_PKT_STK_F_SRC_PORT) && 1189 (pkt->stk_flags & BCM_PKT_STK_F_DST_PORT)) { 1190 1191 sal_sprintf(info_str, 1192 "dest-gport %d. src-gport %d. ", 1193 pkt->dst_gport, pkt->src_gport); 1194 } else if ((pkt->stk_flags & BCM_PKT_STK_F_SRC_PORT)) { 1195 1196 sal_sprintf(info_str, 1197 "dest-port %d. dest-mod %d. src-gport %d. ", 1198 pkt->dest_port, pkt->dest_mod, pkt->src_gport); 1199 } else if ((pkt->stk_flags & BCM_PKT_STK_F_DST_PORT)) { 1200 1201 sal_sprintf(info_str, 1202 "dest-gport %d. %s %d. src-mod %d. ", 1203 pkt->dst_gport, 1204 (pkt->flags & BCM_PKT_F_TRUNK) ? "src-trunk" : "src-port", 1205 (pkt->flags & BCM_PKT_F_TRUNK) ? pkt->src_trunk : pkt->src_port, 1206 pkt->src_mod); 1207 } else { 1208 1209 sal_sprintf(info_str, 1210 "dest-port %d. dest-mod %d. %s %d. src-mod %d. ", 1211 pkt->dest_port, pkt->dest_mod, 1212 (pkt->flags & BCM_PKT_F_TRUNK) ? "src-trunk" : "src-port", 1213 (pkt->flags & BCM_PKT_F_TRUNK) ? pkt->src_trunk : pkt->src_port, 1214 pkt->src_mod); 1215 } 1216 1217 cli_out("%s%sopcode %d. %s matched %d. classification-tag %d.\n", 1218 pfx, info_str, 1219 pkt->opcode, 1220 (pkt->flags & BCM_RX_TRUNCATED) ? "Truncated." : "", 1221 pkt->rx_matched, pkt->rx_classification_tag); 1222 1223 for (i = 0; i < bcmRxReasonCount; i++) { 1224 if (BCM_RX_REASON_GET(pkt->rx_reasons, i)) { 1225 cli_out("%sreasons: %s\n", pfx, _ft_rmon_reason_names[i]); 1226 } 1227 } 1228 1229 /* 1230 * Seamless Redundancy(SR) copy to CPU 1231 * SR custom RX reason code (1~63) for IFP action bcmFieldActionSrCopyToCpu . 1232 * construct reason code (1~63) from 6-bit binary encoding 1233 */ 1234 #define SR_CUSTOM_RX_REASON_CODE \ 1235 ((BCM_RX_REASON_GET(pkt->rx_reasons, bcmRxReasonSrCopyToCpuBit0) << 0)| \ 1236 (BCM_RX_REASON_GET(pkt->rx_reasons, bcmRxReasonSrCopyToCpuBit1) << 1)| \ 1237 (BCM_RX_REASON_GET(pkt->rx_reasons, bcmRxReasonSrCopyToCpuBit2) << 2)| \ 1238 (BCM_RX_REASON_GET(pkt->rx_reasons, bcmRxReasonSrCopyToCpuBit3) << 3)| \ 1239 (BCM_RX_REASON_GET(pkt->rx_reasons, bcmRxReasonSrCopyToCpuBit4) << 4)| \ 1240 (BCM_RX_REASON_GET(pkt->rx_reasons, bcmRxReasonSrCopyToCpuBit5) << 5)) 1241 1242 /* no copy to CPU when SR_CUSTOM_RX_REASON_CODE == 0 */ 1243 if(SR_CUSTOM_RX_REASON_CODE != 0) { 1244 cli_out("%sSR custom RX reason code: %d\n", 1245 pfx, 1246 SR_CUSTOM_RX_REASON_CODE); 1247 } 1248 } 1249 1250 #ifndef NO_SAL_APPL 1251 #ifndef __STRICT_ANSI__ 1252 #ifndef __KERNEL__ 1253 if (report & REPORT_DECODE) { 1254 if (pkt->_dv) { 1255 int ix; 1256 uint8 *data; 1257 int encap_length; 1258 1259 cli_out("*************************************************\n"); 1260 data = &(pkt->_pkt_data.data[0]); 1261 for (ix = 0; ix < (pkt->pkt_len); ix+=8) { 1262 cli_out("%04d: %02x %02x %02x %02x %02x %02x %02x %02x\n", 1263 (ix/8), data[ix], data[ix+1], data[ix+2], data[ix+3], data[ix+4], data[ix+5], data[ix+6], data[ix+7]); 1264 } 1265 cli_out("*************************************************\n"); 1266 1267 d_packet_format(pfx, DECODE_ETHER, pkt->_pkt_data.data, 1268 pkt->pkt_len, NULL); 1269 1270 /* DA+SA+TPID+VTAG+TPID+IP+UDP */ 1271 encap_length = 46; 1272 1273 if (bcma_ft_ipfix_template_print(unit, 1274 (pkt->pkt_len - encap_length - 4), &(pkt->_pkt_data.data[encap_length]))) { 1275 cli_out("ipfix parsing failed\n"); 1276 } 1277 1278 1279 } 1280 } 1281 #endif /* __KERNEL__ */ 1282 #endif /* __STRICT_ANSI__ */ 1283 #endif /* NO_SAL_APPL */ 1284 1285 } 1286 1287 STATIC void 1288 ft_rmon_dump_packet(int unit, pup_t *pup, uint32 report) 1289 { 1290 pwu_t *pu = &ft_rmon_units[unit]; 1291 1292 switch (pu->mode) { 1293 case PW_MODE_RX: 1294 ft_rmon_dump_packet_rx(unit, pup, report); 1295 break; 1296 default: 1297 break; 1298 } 1299 } 1300 1301 STATIC cmd_result_t 1302 ft_rmon_dump_log(int unit, int n) 1303 /* 1304 * Function: ft_rmon_dump_log 1305 * Purpose: Dump out currently logged packets 1306 * Parameters: unit - unit # 1307 * n - number of packets to dump, -n means last "n" 1308 * packets. 1309 * Returns: CMD_OK - success 1310 */ 1311 { 1312 pwu_t *pu = &ft_rmon_units[unit]; 1313 pup_t *pup; 1314 int abs_n; 1315 1316 PW_LOCK(unit); /* Stop updates */ 1317 1318 if ((pu->pu_log == NULL) || (n == 0)) { /* Nothing to show */ 1319 PW_UNLOCK(unit); 1320 cli_out("ft_rmon_dump_log[%d]: Warning: no packets logged " 1321 "or selected to dump\n", unit); 1322 return CMD_OK; /* Return nothing */ 1323 } 1324 1325 /* 1326 * Loop through list of packets, stop when we have dumped the correct 1327 * number - we know there is at least one logged since we checked above. 1328 * If dumping the last n packets, move backwards, otherwise move forwards. 1329 * Anything on this list is complete, so the PW_LOCK protects this 1330 * operation. 1331 */ 1332 abs_n = n < 0 ? -n : n; 1333 abs_n = abs_n > pu->pu_log_cnt ? pu->pu_log_cnt : abs_n; 1334 1335 if (n < 0) { /* Dump last N packets */ 1336 for (pup = pu->pu_log; abs_n-- != 0; pup = pup->pup_next) { 1337 ft_rmon_dump_packet(unit, pup, pu->pu_dump_options); 1338 } 1339 } else { /* Dump first N packets */ 1340 for (pup = pu->pu_log->pup_prev; abs_n-- != 0; pup = pup->pup_prev) { 1341 ft_rmon_dump_packet(unit, pup, pu->pu_dump_options); 1342 } 1343 } 1344 PW_UNLOCK(unit); 1345 1346 return CMD_OK; 1347 } 1348 1349 STATIC void 1350 ft_rmon_log_packet(int unit, pup_t *pup) 1351 /* 1352 * Function: ft_rmon_log_packet 1353 * Purpose: Commit a recieved packet to the logs. 1354 * Parameters: unit - unit # 1355 * pup - pointer to pup to add to log. 1356 * Returns: Nothing. 1357 * 1358 * Notes: Assumes PW_LOCK held. 1359 */ 1360 { 1361 pwu_t *pu = &ft_rmon_units[unit]; 1362 1363 /* 1364 * There are 2 cases: 1365 * 1) First entry 1366 * 2) Log is full so the LRU packet is dropped (common case) 1367 * 3) Log is not full, no packet is dropped. 1368 */ 1369 1370 if (pu->pu_log_cnt == 0) { /* 1) First entry */ 1371 pu->pu_log = pup->pup_next = pup->pup_prev = pup; 1372 pu->pu_log_cnt = 1; 1373 } else if (pu->pu_log_cnt == pu->pu_log_max) { /* 2) Full log, drop one */ 1374 pup_t *tp; 1375 1376 tp = pu->pu_log->pup_prev; /* Entry to delete */ 1377 pup->pup_next = pu->pu_log; /* Link new one in at head */ 1378 pup->pup_prev = tp->pup_prev; 1379 pup->pup_next->pup_prev = pup; 1380 pup->pup_prev->pup_next = pup; 1381 1382 ft_rmon_pup_free(unit, tp); 1383 } else { /* 3) Just add to chain */ 1384 /* 1385 * Case 2 - add new entry and increment log count. 1386 */ 1387 pu->pu_log_cnt++; 1388 pup->pup_next = pu->pu_log; 1389 pup->pup_prev = pu->pu_log->pup_prev; 1390 pup->pup_next->pup_prev = pup; 1391 pup->pup_prev->pup_next = pup; 1392 } 1393 pu->pu_log = pup; /* Set new list */ 1394 } 1395 1396 1397 STATIC void 1398 ft_rmon_process_pending(int unit) 1399 /* 1400 * Function: ft_rmon_process_pending 1401 * Purpose: Process packets queued from interrupt 1402 * Parameters: unit - unit # 1403 * Returns: Nothing. 1404 * 1405 * Notes: Can be called only from a non-interrupt context. 1406 */ 1407 { 1408 pwu_t *pu = &ft_rmon_units[unit]; 1409 pup_t *pup; 1410 int chan; 1411 chan = 0; 1412 /* 1413 * Pick up current done list and process in order. 1414 */ 1415 PW_SPIN_LOCK(unit); 1416 pup = (pup_t *)pu->pu_pending; 1417 pu->pu_pending = NULL; 1418 PW_SPIN_UNLOCK(unit); 1419 1420 pu->last_pkt_count = 0; 1421 while (pup) { 1422 pup_t *tp = pup->pup_next; 1423 1424 pup->pup_seqno = ++pu->pu_packet_received; 1425 ft_rmon_log_packet(unit, (pup_t *)pup); 1426 chan = 0; /* Per-channel counted in Rx call-back */ 1427 pu->pu_ch_count[chan]++; 1428 ft_rmon_dump_packet(unit, (pup_t *)pup, pu->pu_report); /* Dump packet */ 1429 pup = tp; 1430 pu->last_pkt_count++; 1431 } 1432 } 1433 1434 /**************************************************************** 1435 * 1436 * RX API packet watcher code, non-interrupt 1437 */ 1438 STATIC bcm_rx_t 1439 ft_rmon_rx_callback(int unit, bcm_pkt_t *pkt, void *cookie) 1440 { 1441 pup_t *pup; 1442 pwu_t *pu = &ft_rmon_units[unit]; 1443 int8 chan; 1444 dma_chan_t dma_chan; 1445 1446 chan = pkt->dma_channel; 1447 1448 if (soc_feature(unit, soc_feature_cmicx)) { 1449 dma_chan = CMICX_N_DMA_CHAN; 1450 } else { 1451 dma_chan = N_DMA_CHAN; 1452 } 1453 1454 if (chan >= 0 && chan < dma_chan) { 1455 pu->pu_ch_count[chan + 1]++; 1456 } 1457 if (pu->pu_report == 0 && pu->pu_log_max == 1) { 1458 /* No post-processing - just count packet */ 1459 return BCM_RX_HANDLED; 1460 } 1461 1462 pup = ft_rmon_pup_alloc(unit); 1463 if (!pup) { 1464 LOG_VERBOSE(BSL_LS_SOC_COMMON, 1465 (BSL_META_U(unit, 1466 "FtRemoteMon: Failed to allocate pup struct. Discarding\n"))); 1467 return BCM_RX_NOT_HANDLED; 1468 } 1469 1470 sal_memcpy(&pup->rx_pkt, pkt, sizeof(bcm_pkt_t)); 1471 1472 PW_SPIN_LOCK(unit); 1473 pup->pup_next = NULL; 1474 if (pu->pu_pending == NULL) { 1475 pup->pup_prev = pup; 1476 pu->pu_pending = pup; 1477 } else { 1478 pu->pu_pending->pup_prev->pup_next = pup; 1479 pu->pu_pending->pup_prev = pup; 1480 } 1481 PW_SPIN_UNLOCK(unit); 1482 1483 sal_sem_give(ft_rmon_units[unit].pu_sema); 1484 return BCM_RX_HANDLED_OWNED; 1485 } 1486 1487 #endif /* BCM_FLOWTRACKER_SUPPORT */ 1488 1489 char cmd_ft_rmon_usage[] = 1490 "Parameters: [report +/-<values>] [stop|start] [count]\n" 1491 #ifndef COMPILER_STRING_CONST_LIMIT 1492 "\tlog # - specifies # packets to remember\n" 1493 "\tdump [# | options] - # > 0 dumps # packets from start of log\n" 1494 "\t # < 0 dumps last # packets\n" 1495 "\t nothing - dumps all logs\n" 1496 "\t options - count - print packet # only\n" 1497 "\t decode - attempt to decode packet\n" 1498 "\t raw - dump raw byte stream (host order)\n" 1499 "\t Prepend + to set, - to clear, nothing to toggle\n" 1500 "\treport- count - print packet # only\n" 1501 "\t decode - attempt to decode packet\n" 1502 "\t raw - dump raw byte stream (host order)\n" 1503 "\t Prepend + to set, - to clear, nothing to toggle\n" 1504 "\tquiet - disable all reporting\n" 1505 "\tstart - start the FtRemoteMon-daemon\n" 1506 "\tstop - stop the FtRemoteMon-daemon and deallocate log space\n" 1507 "\tcount - print # packets received.\n" 1508 "\treset - reinitialize configuration from bcm_rx_* API state\n" 1509 #endif 1510 ; 1511 1512 cmd_result_t 1513 cmd_ft_rmon_command(int unit, args_t *a) 1514 /* 1515 * Function: ft_rmon_command 1516 * Purpose: Command interface to the "ipfix rmon" thread. 1517 * Parameters: 1518 * "verbose" - print lots of stuff. 1519 * "start" - start ipfix rmon thread. 1520 * "stop" - stop ipfix rmon thread. 1521 * "quiet" - print no stuff. 1522 * Returns: CMD_OK - success 1523 * CMD_FAIL - failed 1524 * CMD_USAGE - print usage instructions 1525 */ 1526 { 1527 #if defined(BCM_FLOWTRACKER_SUPPORT) 1528 char *c; 1529 int n; 1530 uint32 on = 0; /* flags to turn on */ 1531 pwu_t *pu = &ft_rmon_units[unit]; /* Control this unit */ 1532 1533 if (!sh_check_attached(ARG_CMD(a), unit)) { 1534 return(CMD_FAIL); 1535 } 1536 1537 if (!pu->init_done) { 1538 ft_rmon_init(unit); 1539 } 1540 1541 if (!ARG_CNT(a)) { /* Nothing passed */ 1542 cli_out("%s: Status: %s. Mode %s. Buffering up to %d packets.%s\n", 1543 ft_rmon_name[unit], 1544 (pu->pu_flags & PU_F_RUN) ? "Running" : "Not Running", 1545 ft_rmon_modes[pu->mode], pu->pu_log_max, 1546 (pu->pu_flags & PU_F_STOP) ? " STOP Requested." : ""); 1547 if (pu->rate <= 0) { 1548 cli_out("Rate limiting is off.\n"); 1549 } else { 1550 cli_out("Rate limit is %d (soc intvl %d).\n", pu->rate, 1551 pu->pu_interval); 1552 } 1553 1554 cli_out("Reporting is enabled for: "); 1555 parse_mask_format(80, ft_rmon_report_table, pu->pu_report); 1556 cli_out("Reporting is disabled for: "); 1557 parse_mask_format(80, ft_rmon_report_table, pu->pu_report ^ ~0); 1558 1559 cli_out("Dump options are enabled for: "); 1560 parse_mask_format(80, ft_rmon_report_table, pu->pu_dump_options); 1561 cli_out("Dump options are disabled for: "); 1562 parse_mask_format(80, ft_rmon_report_table, pu->pu_dump_options ^ ~0); 1563 1564 cli_out("RX on for channel(s): "); 1565 if (pu->pu_channel) { 1566 parse_mask_format(50, ft_rmon_channel_table, pu->pu_channel); 1567 cli_out("RX off for channel(s): "); 1568 parse_mask_format(50, ft_rmon_channel_table, ~pu->pu_channel); 1569 } else { 1570 cli_out(" -- using default --\n"); 1571 } 1572 #ifdef BROADCOM_DEBUG 1573 if (pu->mode == PW_MODE_RX) { 1574 bcm_rx_show(unit); 1575 } 1576 #endif /* BROADCOM_DEBUG */ 1577 return(CMD_OK); 1578 } 1579 1580 /* Scan args and see what's up */ 1581 1582 while ((c = ARG_GET(a)) != NULL) { 1583 if (!sal_strcasecmp(c, "start")) { 1584 if (pu->pu_flags & PU_F_RUN) { 1585 cli_out("%s: WARNING: Already running\n", ft_rmon_name[unit]); 1586 } else { 1587 on |= PU_F_RUN; 1588 } 1589 } else if (!sal_strcasecmp(c, "stop")) { 1590 if (!(pu->pu_flags & PU_F_RUN)) { 1591 cli_out("%s: WARNING: Not running\n", ft_rmon_name[unit]); 1592 return(CMD_OK); /* Return OK to avoid rc aborts */ 1593 } 1594 on |= PU_F_STOP; 1595 } else if (!sal_strcasecmp(c, "reset")) { 1596 if (pu->pu_flags & PU_F_RUN) { 1597 cli_out("%s: Unable to reset configuration " 1598 "while running\n", ft_rmon_name[unit]); 1599 return(CMD_FAIL); 1600 } else { 1601 pu->init_done = FALSE; 1602 ft_rmon_init(unit); 1603 return CMD_OK; 1604 } 1605 } else if (!sal_strcasecmp(c, "channel")) { 1606 if (ARG_CNT(a) == 0) { 1607 cli_out("%s: RX on for channel(s): ", ft_rmon_name[unit]); 1608 if (pu->pu_channel) { 1609 parse_mask_format(50, ft_rmon_channel_table, pu->pu_channel); 1610 cli_out("%s: RX off for channel(s): ", ft_rmon_name[unit]); 1611 parse_mask_format(50, ft_rmon_channel_table, ~pu->pu_channel); 1612 } else { 1613 cli_out(" -- using default --\n"); 1614 } 1615 return(CMD_OK); 1616 } else if (pu->pu_flags & PU_F_RUN) { 1617 cli_out("%s: Unable to configure DMA channels " 1618 "while running\n", ft_rmon_name[unit]); 1619 return(CMD_FAIL); 1620 } 1621 1622 /* Set channels */ 1623 while ((c = ARG_CUR(a)) != NULL && 1624 !parse_mask(c, ft_rmon_channel_table, 1625 (uint32 *)&pu->pu_channel)) { 1626 ARG_NEXT(a); /* Bump arg if matched report */ 1627 } 1628 1629 if (pu->pu_flags & PU_F_RUN) { 1630 cli_out("%s: Warning channel re-assignment will not take\n" 1631 "%s: until restarted.\n", 1632 ft_rmon_name[unit], ft_rmon_name[unit]); 1633 } 1634 } else if (!sal_strcasecmp(c, "quiet")) { 1635 pu->pu_report = 0; 1636 } else if (!sal_strcasecmp(c, "rate")) { 1637 c = ARG_GET(a); 1638 if (c == NULL) { 1639 if (pu->rate <= 0) { 1640 cli_out("%s: No rate limiting.\n", ft_rmon_name[unit]); 1641 } else { 1642 cli_out("%s: Rate limit is currently %d pkts/sec.\n", 1643 ft_rmon_name[unit], pu->rate); 1644 } 1645 return CMD_OK; 1646 } 1647 pu->rate = strtoul(c, NULL, 10); 1648 if (_ft_rmon_set_rate(unit) < 0) { 1649 return CMD_FAIL; 1650 } 1651 return CMD_OK; 1652 } else if (!sal_strcasecmp(c, "report")) { 1653 if (ARG_CNT(a)) { 1654 while ((c = ARG_CUR(a)) != NULL && 1655 !parse_mask(c, ft_rmon_report_table, &pu->pu_report)) { 1656 ARG_NEXT(a); /* Bump arg if matched report */ 1657 } 1658 } else { /* Print values */ 1659 cli_out("%s: Reporting on for: ", ft_rmon_name[unit]); 1660 parse_mask_format(50, ft_rmon_report_table, pu->pu_report); 1661 cli_out("%s: Reporting off for: ", ft_rmon_name[unit]); 1662 parse_mask_format(50, ft_rmon_report_table, ~pu->pu_report); 1663 } 1664 } else if (!sal_strcasecmp(c, "log")) { 1665 c = ARG_GET(a); 1666 if (c) { /* We have an option */ 1667 if (pu->pu_flags & PU_F_RUN) { 1668 cli_out("%s: Can not change \"log\" " 1669 "while FtRemoteMon-daemon running\n", ft_rmon_name[unit]); 1670 return(CMD_FAIL); 1671 } 1672 if (!sal_strcasecmp(c, "on")) { 1673 n = PU_LOG_CNT; 1674 } else if (!sal_strcasecmp(c, "off")) { 1675 n = 1; 1676 } else if (isint(c)) { /* Set log size */ 1677 n = parse_integer(c); 1678 if (n < 1) { 1679 cli_out("%s: What is %s?\n", ft_rmon_name[unit], c); 1680 n = 1; 1681 } 1682 } else { 1683 cli_out("%s: Invalid count \"%s\"\n", ft_rmon_name[unit], c); 1684 return(CMD_FAIL); 1685 } 1686 pu->pu_log_max = n; 1687 } 1688 cli_out("%s: Logging(%d-packets)\n", 1689 ft_rmon_name[unit], pu->pu_log_max); 1690 if (!c || !*c) { 1691 return(CMD_OK); 1692 } 1693 } else if (!sal_strcasecmp(c, "dump")) { 1694 c = ARG_GET(a); 1695 if (!c) { /* no args */ 1696 n = pu->pu_log_cnt; 1697 } else if (!isint(c)) { 1698 if (!sal_strcasecmp(c, "options")) { 1699 if (ARG_CNT(a)) { 1700 while ((c = ARG_CUR(a)) != NULL && 1701 !parse_mask(c, ft_rmon_report_table, 1702 &pu->pu_dump_options)) { 1703 ARG_NEXT(a); /* Bump arg if matched report */ 1704 } 1705 } else { /* Print values */ 1706 cli_out("%s: Dump options on for: ", ft_rmon_name[unit]); 1707 parse_mask_format(50, ft_rmon_report_table, pu->pu_dump_options); 1708 cli_out("%s: Dump options off for: ", ft_rmon_name[unit]); 1709 parse_mask_format(50, ft_rmon_report_table, ~pu->pu_dump_options); 1710 } 1711 return(CMD_OK); 1712 } else { 1713 cli_out("%s: Invalid log count \"%s\"\n", ft_rmon_name[unit], c); 1714 return(CMD_FAIL); 1715 } 1716 } else { 1717 n = parse_integer(c); 1718 } 1719 return(ft_rmon_dump_log(unit, n)); 1720 } else if (!sal_strcasecmp(c, "count")) { 1721 COMPILER_DOUBLE cur_time, last_time; 1722 int cur_count, last_count; 1723 int rate; 1724 1725 cur_time = SAL_TIME_DOUBLE(); 1726 1727 last_time = pu->pu_count_time; 1728 last_count = pu->pu_count_last; 1729 cur_count = pu->pu_packet_received; 1730 1731 if (cur_time == last_time) { 1732 rate = 0; 1733 } else { 1734 rate = (int) ((cur_count - last_count) / 1735 (cur_time - last_time)); 1736 } 1737 1738 cli_out("%s: Received %d packets [(%d),%d,%d,%d,%d] (%d/sec), " 1739 "last %d packet(s) logged\n", 1740 ft_rmon_name[unit], 1741 pu->pu_packet_received, 1742 pu->pu_ch_count[0], 1743 pu->pu_ch_count[1], 1744 pu->pu_ch_count[2], 1745 pu->pu_ch_count[3], 1746 pu->pu_ch_count[4], 1747 rate, 1748 pu->pu_log_cnt); 1749 1750 pu->pu_count_last = cur_count; 1751 pu->pu_count_time = cur_time; 1752 } else if (!sal_strcasecmp(c, "interval")) { 1753 c = ARG_GET(a); 1754 if (!c || !isint(c)) { 1755 return(CMD_USAGE); 1756 } 1757 n = parse_integer(c); 1758 if (n < 0) { 1759 cli_out("%s: Invalid interval: %s\n", ft_rmon_name[unit], c); 1760 return(CMD_FAIL); 1761 } 1762 pu->pu_interval = n; 1763 } else if (!sal_strcasecmp(c, "mode")) { 1764 if ((c = ARG_GET(a)) == NULL) { 1765 cli_out("Current mode is %s\n", ft_rmon_modes[pu->mode]); 1766 return CMD_OK; 1767 } 1768 1769 if (ft_rmon_running(unit)) { 1770 cli_out("Can't set modes while running\n"); 1771 return CMD_FAIL; 1772 } 1773 1774 if (!sal_strcasecmp(c, "rx")) { 1775 pu->mode = PW_MODE_RX; 1776 } else { 1777 cli_out("Unknown mode: %s\n", c); 1778 return CMD_FAIL; 1779 } 1780 1781 return CMD_OK; 1782 } else if (!sal_strcasecmp(c, "hdr")) { 1783 c = ARG_GET(a); 1784 if (c) { /* We have an option */ 1785 if (!sal_strcasecmp(c, "on")) { 1786 pu->hdr_dump = 1; 1787 } else if (!sal_strcasecmp(c, "off")) { 1788 pu->hdr_dump = 0; 1789 } 1790 } 1791 } else { 1792 return(CMD_USAGE); 1793 } 1794 } 1795 1796 /* Everything OK, start or stop ipfix rmon now */ 1797 1798 if (on & PU_F_RUN) { /* start off thread */ 1799 return(ft_rmon_start(unit, TRUE)); 1800 } else if (on & PU_F_STOP) { /* stop task */ 1801 return(ft_rmon_stop(unit, TRUE)); 1802 } 1803 #endif 1804 1805 return(CMD_OK); 1806 }