/* Hanoh Haim Cisco Systems, Inc. */ /* Copyright (c) 2015-2015 Cisco Systems, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ // DPDK c++ issue #define UINT8_MAX 255 #define UINT16_MAX 0xFFFF // DPDK c++ issue #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bp_sim.h" #include "latency.h" #include "os_time.h" #include #include #include #include #include #include #include #include <../linux_dpdk/version.h> extern "C" { #include } #include #include #include #include #include #include #include #include "global_io_mode.h" #include "utl_term_io.h" #include "msg_manager.h" #include "platform_cfg.h" #include "latency.h" #include #define RX_CHECK_MIX_SAMPLE_RATE 8 #define RX_CHECK_MIX_SAMPLE_RATE_1G 2 #define SOCKET0 0 #define BP_MAX_PKT 32 #define MAX_PKT_BURST 32 #define BP_MAX_PORTS (MAX_LATENCY_PORTS) #define BP_MAX_CORES 32 #define BP_MAX_TX_QUEUE 16 #define BP_MASTER_AND_LATENCY 2 #define RTE_TEST_RX_DESC_DEFAULT 64 #define RTE_TEST_RX_LATENCY_DESC_DEFAULT (1*1024) #define RTE_TEST_RX_DESC_VM_DEFAULT 512 #define RTE_TEST_TX_DESC_VM_DEFAULT 512 typedef struct rte_mbuf * (*rte_mbuf_convert_to_one_seg_t)(struct rte_mbuf *m); struct rte_mbuf * rte_mbuf_convert_to_one_seg(struct rte_mbuf *m); extern "C" int vmxnet3_xmit_set_callback(rte_mbuf_convert_to_one_seg_t cb); #define RTE_TEST_TX_DESC_DEFAULT 512 #define RTE_TEST_RX_DESC_DROP 0 static inline int get_vm_one_queue_enable(){ return (CGlobalInfo::m_options.preview.get_vm_one_queue_enable() ?1:0); } static inline int get_is_latency_thread_enable(){ return (CGlobalInfo::m_options.is_latency_enabled() ?1:0); } struct port_cfg_t; class CPhyEthIF; class CPhyEthIFStats ; class CTRexExtendedDriverBase { public: virtual TrexPlatformApi::driver_speed_e get_driver_speed() = 0; virtual int get_min_sample_rate(void)=0; virtual void update_configuration(port_cfg_t * cfg)=0; virtual void update_global_config_fdir(port_cfg_t * cfg)=0; virtual bool is_hardware_filter_is_supported(){ return(false); } virtual int configure_rx_filter_rules(CPhyEthIF * _if)=0; virtual bool is_hardware_support_drop_queue(){ return(false); } virtual int configure_drop_queue(CPhyEthIF * _if); virtual void get_extended_stats(CPhyEthIF * _if,CPhyEthIFStats *stats)=0; virtual void clear_extended_stats(CPhyEthIF * _if)=0; virtual int wait_for_stable_link()=0; virtual void wait_after_link_up(){}; virtual bool flow_control_disable_supported(){return true;} }; class CTRexExtendedDriverBase1G : public CTRexExtendedDriverBase { public: CTRexExtendedDriverBase1G(){ } TrexPlatformApi::driver_speed_e get_driver_speed() { return TrexPlatformApi::SPEED_1G; } static CTRexExtendedDriverBase * create(){ return ( new CTRexExtendedDriverBase1G() ); } virtual void update_global_config_fdir(port_cfg_t * cfg); virtual int get_min_sample_rate(void){ return ( RX_CHECK_MIX_SAMPLE_RATE_1G); } virtual void update_configuration(port_cfg_t * cfg); virtual bool is_hardware_filter_is_supported(){ return (true); } virtual int configure_drop_queue(CPhyEthIF * _if); virtual int configure_rx_filter_rules(CPhyEthIF * _if); virtual bool is_hardware_support_drop_queue(){ return(true); } virtual void get_extended_stats(CPhyEthIF * _if,CPhyEthIFStats *stats); virtual void clear_extended_stats(CPhyEthIF * _if); virtual int wait_for_stable_link(); void wait_after_link_up(); }; class CTRexExtendedDriverBase1GVm : public CTRexExtendedDriverBase { public: CTRexExtendedDriverBase1GVm(){ /* we are working in mode that we have 1 queue for rx and one queue for tx*/ CGlobalInfo::m_options.preview.set_vm_one_queue_enable(true); } TrexPlatformApi::driver_speed_e get_driver_speed() { return TrexPlatformApi::SPEED_1G; } static CTRexExtendedDriverBase * create(){ return ( new CTRexExtendedDriverBase1GVm() ); } virtual void update_global_config_fdir(port_cfg_t * cfg){ } virtual int get_min_sample_rate(void){ return ( RX_CHECK_MIX_SAMPLE_RATE_1G); } virtual void update_configuration(port_cfg_t * cfg); virtual bool is_hardware_filter_is_supported(){ return (true); } virtual int configure_rx_filter_rules(CPhyEthIF * _if); virtual bool is_hardware_support_drop_queue(){ return(false); } virtual int configure_drop_queue(CPhyEthIF * _if); virtual void get_extended_stats(CPhyEthIF * _if,CPhyEthIFStats *stats); virtual void clear_extended_stats(CPhyEthIF * _if); virtual int wait_for_stable_link(); }; class CTRexExtendedDriverBase10G : public CTRexExtendedDriverBase { public: CTRexExtendedDriverBase10G(){ } TrexPlatformApi::driver_speed_e get_driver_speed() { return TrexPlatformApi::SPEED_10G; } static CTRexExtendedDriverBase * create(){ return ( new CTRexExtendedDriverBase10G() ); } virtual void update_global_config_fdir(port_cfg_t * cfg); virtual int get_min_sample_rate(void){ return (RX_CHECK_MIX_SAMPLE_RATE); } virtual void update_configuration(port_cfg_t * cfg); virtual bool is_hardware_filter_is_supported(){ return (true); } virtual int configure_rx_filter_rules(CPhyEthIF * _if); virtual bool is_hardware_support_drop_queue(){ return(true); } virtual void get_extended_stats(CPhyEthIF * _if,CPhyEthIFStats *stats); virtual void clear_extended_stats(CPhyEthIF * _if); virtual int wait_for_stable_link(); }; class CTRexExtendedDriverBase40G : public CTRexExtendedDriverBase10G { public: CTRexExtendedDriverBase40G(){ } TrexPlatformApi::driver_speed_e get_driver_speed() { return TrexPlatformApi::SPEED_40G; } static CTRexExtendedDriverBase * create(){ return ( new CTRexExtendedDriverBase40G() ); } virtual void update_global_config_fdir(port_cfg_t * cfg){ } virtual void update_configuration(port_cfg_t * cfg); virtual int configure_rx_filter_rules(CPhyEthIF * _if); virtual bool is_hardware_filter_is_supported(){ return (true); } virtual bool is_hardware_support_drop_queue(){ return(true); } virtual void get_extended_stats(CPhyEthIF * _if,CPhyEthIFStats *stats); virtual void clear_extended_stats(CPhyEthIF * _if); virtual int wait_for_stable_link(); // disabling flow control on 40G using DPDK API causes the interface to malfunction bool flow_control_disable_supported(){return false;} private: void add_rules(CPhyEthIF * _if, uint16_t type, uint8_t ttl); }; typedef CTRexExtendedDriverBase * (*create_object_t) (void); class CTRexExtendedDriverRec { public: std::string m_driver_name; create_object_t m_constructor; }; class CTRexExtendedDriverDb { public: const std::string & get_driver_name() { return m_driver_name; } bool is_driver_exists(std::string name); void set_driver_name(std::string name){ m_driver_was_set=true; m_driver_name=name; printf(" set driver name %s \n",name.c_str()); m_drv=create_driver(m_driver_name); assert(m_drv); } CTRexExtendedDriverBase * get_drv(){ if (!m_driver_was_set) { printf(" ERROR too early to use this object !\n"); printf(" need to set the right driver \n"); assert(0); } assert(m_drv); return (m_drv); } public: static CTRexExtendedDriverDb * Ins(); private: CTRexExtendedDriverBase * create_driver(std::string name); CTRexExtendedDriverDb(){ register_driver(std::string("rte_ixgbe_pmd"),CTRexExtendedDriverBase10G::create); register_driver(std::string("rte_igb_pmd"),CTRexExtendedDriverBase1G::create); register_driver(std::string("rte_i40e_pmd"),CTRexExtendedDriverBase40G::create); /* virtual devices */ register_driver(std::string("rte_em_pmd"),CTRexExtendedDriverBase1GVm::create); register_driver(std::string("rte_vmxnet3_pmd"),CTRexExtendedDriverBase1GVm::create); register_driver(std::string("rte_virtio_pmd"),CTRexExtendedDriverBase1GVm::create); register_driver(std::string("rte_enic_pmd"),CTRexExtendedDriverBase1GVm::create); m_driver_was_set=false; m_drv=0; m_driver_name=""; } void register_driver(std::string name,create_object_t func); static CTRexExtendedDriverDb * m_ins; bool m_driver_was_set; std::string m_driver_name; CTRexExtendedDriverBase * m_drv; std::vector m_list; }; CTRexExtendedDriverDb * CTRexExtendedDriverDb::m_ins; void CTRexExtendedDriverDb::register_driver(std::string name, create_object_t func){ CTRexExtendedDriverRec * rec; rec = new CTRexExtendedDriverRec(); rec->m_driver_name=name; rec->m_constructor=func; m_list.push_back(rec); } bool CTRexExtendedDriverDb::is_driver_exists(std::string name){ int i; for (i=0; i<(int)m_list.size(); i++) { if (m_list[i]->m_driver_name == name) { return (true); } } return (false); } CTRexExtendedDriverBase * CTRexExtendedDriverDb::create_driver(std::string name){ int i; for (i=0; i<(int)m_list.size(); i++) { if (m_list[i]->m_driver_name == name) { return ( m_list[i]->m_constructor() ); } } return( (CTRexExtendedDriverBase *)0); } CTRexExtendedDriverDb * CTRexExtendedDriverDb::Ins(){ if (!m_ins) { m_ins = new CTRexExtendedDriverDb(); } return (m_ins); } static CTRexExtendedDriverBase * get_ex_drv(){ return ( CTRexExtendedDriverDb::Ins()->get_drv()); } static inline int get_min_sample_rate(void){ return ( get_ex_drv()->get_min_sample_rate()); } #define MAX_DPDK_ARGS 40 static CPlatformYamlInfo global_platform_cfg_info; static int global_dpdk_args_num ; static char * global_dpdk_args[MAX_DPDK_ARGS]; static char global_cores_str[100]; static char global_prefix_str[100]; static char global_loglevel_str[20]; // cores =0==1,1*2,2,3,4,5,6 // An enum for all the option types enum { OPT_HELP, OPT_MODE_BATCH, OPT_MODE_INTERACTIVE, OPT_NODE_DUMP, OPT_UT, OPT_FILE_OUT, OPT_REAL_TIME, OPT_CORES, OPT_SINGLE_CORE, OPT_FLIP_CLIENT_SERVER, OPT_FLOW_FLIP_CLIENT_SERVER, OPT_FLOW_FLIP_CLIENT_SERVER_SIDE, OPT_BW_FACTOR, OPT_DURATION, OPT_PLATFORM_FACTOR, OPT_PUB_DISABLE, OPT_LIMT_NUM_OF_PORTS, OPT_PLAT_CFG_FILE, OPT_LATENCY, OPT_NO_CLEAN_FLOW_CLOSE, OPT_LATENCY_MASK, OPT_ONLY_LATENCY, OPT_1G_MODE, OPT_LATENCY_PREVIEW , OPT_PCAP, OPT_RX_CHECK, OPT_IO_MODE, OPT_IPV6, OPT_LEARN, OPT_LEARN_VERIFY, OPT_L_PKT_MODE, OPT_NO_FLOW_CONTROL, OPT_RX_CHECK_HOPS, OPT_MAC_FILE, OPT_NO_KEYBOARD_INPUT, OPT_VLAN, OPT_VIRT_ONE_TX_RX_QUEUE, OPT_PREFIX, OPT_MAC_SPLIT }; /* these are the argument types: SO_NONE -- no argument needed SO_REQ_SEP -- single required argument SO_MULTI -- multiple arguments needed */ static CSimpleOpt::SOption parser_options[] = { { OPT_HELP, "-?", SO_NONE }, { OPT_HELP, "-h", SO_NONE }, { OPT_HELP, "--help", SO_NONE }, { OPT_UT, "--ut", SO_NONE }, { OPT_MODE_BATCH, "-f", SO_REQ_SEP}, { OPT_MODE_INTERACTIVE, "-i", SO_NONE }, { OPT_PLAT_CFG_FILE, "--cfg", SO_REQ_SEP}, { OPT_REAL_TIME , "-r", SO_NONE }, { OPT_SINGLE_CORE, "-s", SO_NONE }, { OPT_FILE_OUT, "-o" , SO_REQ_SEP}, { OPT_FLIP_CLIENT_SERVER,"--flip",SO_NONE }, { OPT_FLOW_FLIP_CLIENT_SERVER,"-p",SO_NONE }, { OPT_FLOW_FLIP_CLIENT_SERVER_SIDE,"-e",SO_NONE }, { OPT_NO_CLEAN_FLOW_CLOSE,"--nc",SO_NONE }, { OPT_LIMT_NUM_OF_PORTS,"--limit-ports", SO_REQ_SEP }, { OPT_CORES , "-c", SO_REQ_SEP }, { OPT_NODE_DUMP , "-v", SO_REQ_SEP }, { OPT_LATENCY , "-l", SO_REQ_SEP }, { OPT_DURATION , "-d", SO_REQ_SEP }, { OPT_PLATFORM_FACTOR , "-pm", SO_REQ_SEP }, { OPT_PUB_DISABLE , "-pubd", SO_NONE }, { OPT_BW_FACTOR , "-m", SO_REQ_SEP }, { OPT_LATENCY_MASK , "--lm", SO_REQ_SEP }, { OPT_ONLY_LATENCY, "--lo", SO_NONE }, { OPT_1G_MODE, "-1g", SO_NONE }, { OPT_LATENCY_PREVIEW , "-k", SO_REQ_SEP }, { OPT_PCAP, "--pcap", SO_NONE }, { OPT_RX_CHECK, "--rx-check", SO_REQ_SEP }, { OPT_IO_MODE, "--iom", SO_REQ_SEP }, { OPT_RX_CHECK_HOPS, "--hops", SO_REQ_SEP }, { OPT_IPV6, "--ipv6", SO_NONE }, { OPT_LEARN, "--learn", SO_NONE }, { OPT_LEARN_VERIFY, "--learn-verify", SO_NONE }, { OPT_L_PKT_MODE, "--l-pkt-mode", SO_REQ_SEP }, { OPT_NO_FLOW_CONTROL, "--no-flow-control-change", SO_NONE }, { OPT_VLAN, "--vlan", SO_NONE }, { OPT_MAC_FILE, "--mac", SO_REQ_SEP }, { OPT_NO_KEYBOARD_INPUT ,"--no-key", SO_NONE }, { OPT_VIRT_ONE_TX_RX_QUEUE, "--vm-sim", SO_NONE }, { OPT_PREFIX, "--prefix", SO_REQ_SEP }, { OPT_MAC_SPLIT, "--mac-spread", SO_REQ_SEP }, SO_END_OF_OPTIONS }; static int usage(){ printf(" Usage: t-rex-64 [MODE] [OPTION] -f cfg.yaml -c cores \n"); printf(" \n"); printf(" \n"); printf(" mode \n\n"); printf(" -f [file] : YAML file with template configuration \n"); printf(" -i : launch TRex in interactive mode (RPC server)\n"); printf(" \n\n"); printf(" options \n\n"); printf(" --mac [file] : YAML file with configuration \n"); printf(" \n\n"); printf(" -c [number of threads] : default is 1. number of threads to allocate for each dual ports. \n"); printf(" \n"); printf(" -s : run only one data path core. for debug\n"); printf(" \n"); printf(" --flip : flow will be sent from client->server and server->client for maximum throughput \n"); printf(" \n"); printf(" -p : flow-flip , send all flow packets from the same interface base of client ip \n"); printf(" -e : like -p but comply to the generator rules \n"); printf(" \n"); printf(" -l [pkt/sec] : run latency daemon in this rate \n"); printf(" e.g -l 1000 run 1000 pkt/sec from each interface , zero mean to disable latency check \n"); printf(" --lm : latency mask \n"); printf(" 0x1 only port 0 will send traffic \n"); printf(" --lo :only latency test \n"); printf(" \n"); printf(" --limit-ports : limit number of ports, must be even e.g. 2,4 \n"); printf(" \n"); printf(" --nc : If set, will not wait for all the flows to be closed, terminate faster- see manual for more information \n"); printf(" \n"); printf(" -d : duration of the test in sec. look for --nc \n"); printf(" \n"); printf(" -pm : platform factor ,in case you have splitter in the setup you can multiply the total results in this factor \n"); printf(" e.g --pm 2.0 will multiply all the results bps in this factor \n"); printf(" \n"); printf(" -pubd : disable monitors publishers \n"); printf(" -m : factor of bandwidth \n"); printf(" \n"); printf(" -k [sec] : run latency test before starting the test. it will wait for x sec sending packet and x sec after that \n"); printf(" \n"); printf(" --cfg [platform_yaml] : load and configure platform using this file see example in cfg/cfg_examplexx.yaml file \n"); printf(" this file is used to configure/mask interfaces cores affinity and mac addr \n"); printf(" you can copy this file to /etc/trex_cfg.yaml \n"); printf(" \n"); printf(" --ipv6 : work in ipv6 mode\n"); printf(" --learn : Work in NAT environments, learn the dynamic NAT translation and ALG \n"); printf(" --learn-verify : Learn the translation, but intended for verification of the mechanism in cases that NAT does not exist \n"); printf(" \n"); printf(" --l-pkt-mode [0-3] : Set mode for sending latency packets.\n"); printf(" 0 (default) send SCTP packets \n"); printf(" 1 Send ICMP request packets \n"); printf(" 2 Send ICMP requests from client side, and response from server side (for working with firewall) \n"); printf(" 3 Send ICMP requests with sequence ID 0 from both sides \n"); printf(" -v [1-3] : verbose mode ( works only on the debug image ! ) \n"); printf(" 1 show only stats \n"); printf(" 2 run preview do not write to file \n"); printf(" 3 run preview write stats file \n"); printf(" Note in case of verbose mode you don't need to add the output file \n"); printf(" \n"); printf(" Warning : This program can generate huge-files (TB ) watch out! try this only on local drive \n"); printf(" \n"); printf(" \n"); printf(" --rx-check [sample] : enable rx check thread, using this thread we sample flows 1/sample and check order,latency and more \n"); printf(" this feature consume another thread \n"); printf(" \n"); printf(" --hops [hops] : If rx check is enabled, the hop number can be assigned. The default number of hops is 1\n"); printf(" --iom [mode] : io mode for interactive mode [0- silent, 1- normal , 2- short] \n"); printf(" this feature consume another thread \n"); printf(" \n"); printf(" --no-key : daemon mode, don't get input from keyboard \n"); printf(" --no-flow-control-change : By default TRex disables flow-control. If this option is given, it does not touch it\n"); printf(" --prefix : for multi trex, each instance should have a different name \n"); printf(" --mac-spread : Spread the destination mac-order by this factor. e.g 2 will generate the traffic to 2 devices DEST-MAC ,DEST-MAC+1 \n"); printf(" maximum is up to 128 devices \n"); printf("\n simulation mode : \n"); printf(" Using this mode you can generate the traffic into a pcap file and learn how trex works \n"); printf(" With this version you must be SUDO to use this mode ( I know this is not normal ) \n"); printf(" you can use the Linux CEL version of t-rex to do it without super user \n"); printf(" \n"); printf(" -o [capfile_name] simulate trex into pcap file \n"); printf(" --pcap export the file in pcap mode \n"); printf(" bp-sim-64 -d 10 -f cfg.yaml -o my.pcap --pcap # export 10 sec of what Trex will do on real-time to a file my.pcap \n"); printf(" --vm-sim : simulate vm with driver of one input queue and one output queue \n"); printf(" \n"); printf(" Examples: "); printf(" basic trex run for 10 sec and multiplier of x10 \n"); printf(" #>t-rex-64 -f cfg.yaml -m 10 -d 10 \n"); printf(" \n "); printf(" preview show csv stats \n"); printf(" #>t-rex-64 -c 1 -f cfg.yaml -v 1 -p -m 10 -d 10 --nc -l 1000\n"); printf(" \n "); printf(" 5) ! \n"); printf(" #>t-rex-64 -f cfg.yaml -c 1 --flip \n"); printf("\n"); printf("\n"); printf(" Copyright (c) 2015-2015 Cisco Systems, Inc. \n"); printf(" \n"); printf(" Licensed under the Apache License, Version 2.0 (the 'License') \n"); printf(" you may not use this file except in compliance with the License. \n"); printf(" You may obtain a copy of the License at \n"); printf(" \n"); printf(" http://www.apache.org/licenses/LICENSE-2.0 \n"); printf(" \n"); printf(" Unless required by applicable law or agreed to in writing, software \n"); printf(" distributed under the License is distributed on an \"AS IS\" BASIS, \n"); printf(" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. \n"); printf(" See the License for the specific language governing permissions and \n"); printf(" limitations under the License. \n"); printf(" \n"); printf(" Open Source Components / Libraries \n"); printf(" DPDK (BSD) \n"); printf(" YAML-CPP (BSD) \n"); printf(" JSONCPP (MIT) \n"); printf(" \n"); printf(" Open Source Binaries \n"); printf(" ZMQ (LGPL v3plus) \n"); printf(" \n"); printf(" Version : %s \n",VERSION_BUILD_NUM); printf(" DPDK version : %s \n",rte_version()); printf(" User : %s \n",VERSION_USER); printf(" Date : %s , %s \n",get_build_date(),get_build_time()); printf(" Uuid : %s \n",VERSION_UIID); printf(" Git SHA : %s \n",VERSION_GIT_SHA); return (0); } int gtest_main(int argc, char **argv) ; static void parse_err(const std::string &msg) { std::cout << "\nArgument Parsing Error: \n\n" << "*** "<< msg << "\n\n"; exit(-1); } static int parse_options(int argc, char *argv[], CParserOption* po, bool first_time ) { CSimpleOpt args(argc, argv, parser_options); bool latency_was_set=false; (void)latency_was_set; int a=0; int node_dump=0; po->preview.setFileWrite(true); po->preview.setRealTime(true); uint32_t tmp_data; po->m_run_mode = CParserOption::RUN_MODE_INVALID; while ( args.Next() ){ if (args.LastError() == SO_SUCCESS) { switch (args.OptionId()) { case OPT_UT : parse_err("Supported only in simulation"); break; case OPT_HELP: usage(); return -1; case OPT_MODE_BATCH: if (po->m_run_mode != CParserOption::RUN_MODE_INVALID) { parse_err("Please specify single run mode"); } po->m_run_mode = CParserOption::RUN_MODE_BATCH; po->cfg_file = args.OptionArg(); break; case OPT_MODE_INTERACTIVE: if (po->m_run_mode != CParserOption::RUN_MODE_INVALID) { parse_err("Please specify single run mode"); } po->m_run_mode = CParserOption::RUN_MODE_INTERACTIVE; break; case OPT_NO_KEYBOARD_INPUT : po->preview.set_no_keyboard(true); break; case OPT_MAC_FILE : po->mac_file = args.OptionArg(); break; case OPT_PLAT_CFG_FILE : po->platform_cfg_file = args.OptionArg(); break; case OPT_SINGLE_CORE : po->preview.setSingleCore(true); break; case OPT_IPV6: po->preview.set_ipv6_mode_enable(true); break; case OPT_VLAN: po->preview.set_vlan_mode_enable(true); break; case OPT_LEARN : po->preview.set_lean_mode_enable(true); break; case OPT_LEARN_VERIFY : po->preview.set_lean_mode_enable(true); po->preview.set_lean_and_verify_mode_enable(true); break; case OPT_L_PKT_MODE : sscanf(args.OptionArg(),"%d", &tmp_data); po->m_l_pkt_mode=(uint8_t)tmp_data; break; case OPT_REAL_TIME : printf(" warning -r is deprecated, real time is not needed any more , it is the default \n"); po->preview.setRealTime(true); break; case OPT_NO_FLOW_CONTROL: po->preview.set_disable_flow_control_setting(true); break; case OPT_LIMT_NUM_OF_PORTS : po->m_expected_portd =atoi(args.OptionArg()); break; case OPT_CORES : po->preview.setCores(atoi(args.OptionArg())); break; case OPT_FLIP_CLIENT_SERVER : po->preview.setClientServerFlip(true); break; case OPT_NO_CLEAN_FLOW_CLOSE : po->preview.setNoCleanFlowClose(true); break; case OPT_FLOW_FLIP_CLIENT_SERVER : po->preview.setClientServerFlowFlip(true); break; case OPT_FLOW_FLIP_CLIENT_SERVER_SIDE: po->preview.setClientServerFlowFlipAddr(true); break; case OPT_FILE_OUT: po->out_file = args.OptionArg(); break; case OPT_NODE_DUMP: a=atoi(args.OptionArg()); node_dump=1; po->preview.setFileWrite(false); break; case OPT_BW_FACTOR : sscanf(args.OptionArg(),"%f", &po->m_factor); break; case OPT_DURATION : sscanf(args.OptionArg(),"%f", &po->m_duration); break; case OPT_PUB_DISABLE: po->preview.set_zmq_publish_enable(false); break; case OPT_PLATFORM_FACTOR: sscanf(args.OptionArg(),"%f", &po->m_platform_factor); break; case OPT_LATENCY : latency_was_set=true; sscanf(args.OptionArg(),"%d", &po->m_latency_rate); break; case OPT_LATENCY_MASK : sscanf(args.OptionArg(),"%x", &po->m_latency_mask); break; case OPT_ONLY_LATENCY : po->preview.setOnlyLatency(true); break; case OPT_1G_MODE : po->preview.set_1g_mode(true); break; case OPT_LATENCY_PREVIEW : sscanf(args.OptionArg(),"%d", &po->m_latency_prev); break; case OPT_PCAP: po->preview.set_pcap_mode_enable(true); break; case OPT_RX_CHECK : sscanf(args.OptionArg(),"%d", &tmp_data); po->m_rx_check_sampe=(uint16_t)tmp_data; po->preview.set_rx_check_enable(true); break; case OPT_RX_CHECK_HOPS : sscanf(args.OptionArg(),"%d", &tmp_data); po->m_rx_check_hops = (uint16_t)tmp_data; break; case OPT_IO_MODE : sscanf(args.OptionArg(),"%d", &tmp_data); po->m_io_mode=(uint16_t)tmp_data; break; case OPT_VIRT_ONE_TX_RX_QUEUE:
/*
 * mactime.c - time-based src mac address filtration
 *
 * Copyright (c) 2018 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <vnet/vnet.h>
#include <vnet/plugin/plugin.h>
#include <mactime/mactime.h>

#include <vlibapi/api.h>
#include <vlibmemory/api.h>
#include <vpp/app/version.h>

/* define message IDs */
#include <vnet/format_fns.h>
#include <mactime/mactime.api_enum.h>
#include <mactime/mactime.api_types.h>

#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)

#define REPLY_MSG_ID_BASE mm->msg_id_base
#include <vlibapi/api_helper_macros.h>

#include <vnet/ip-neighbor/ip_neighbor.h>

mactime_main_t mactime_main;

/** \file mactime.c
 * time-base src-mac filter device-input feature arc implementation
 */

static void
feature_init (mactime_main_t * mm)
{
  if (mm->feature_initialized == 0)
    {
      /* Create the lookup table */
      clib_bihash_init_8_8 (&mm->lookup_table, "mactime lookup table",
			    mm->lookup_table_num_buckets,
			    mm->lookup_table_memory_size);
      clib_timebase_init (&mm->timebase, mm->timezone_offset,
			  CLIB_TIMEBASE_DAYLIGHT_USA,
			  &(mm->vlib_main->clib_time));
      mm->allow_counters.name = "allow";
      mm->allow_counters.stat_segment_name = "/mactime/allow";
      mm->drop_counters.name = "drop";
      mm->drop_counters.stat_segment_name = "/mactime/drop";
      mm->feature_initialized = 1;
    }
}

/** Action function shared between message handler and debug CLI
*/
int
mactime_enable_disable (mactime_main_t * mm, u32 sw_if_index,
			int enable_disable)
{
  vnet_sw_interface_t *sw;
  int rv = 0;
  static u8 url_init_done;

  feature_init (mm);

  /* Utterly wrong? */
  if (pool_is_free_index (mm->vnet_main->interface_main.sw_interfaces,
			  sw_if_index))
    return VNET_API_ERROR_INVALID_SW_IF_INDEX;

  /* Not a physical port? */
  sw = vnet_get_sw_interface (mm->vnet_main, sw_if_index);
  if (sw->type != VNET_SW_INTERFACE_TYPE_HARDWARE)
    return VNET_API_ERROR_INVALID_SW_IF_INDEX;

  vnet_feature_enable_disable ("device-input", "mactime",
			       sw_if_index, enable_disable, 0, 0);
  vnet_feature_enable_disable ("interface-output", "mactime-tx",
			       sw_if_index, enable_disable, 0, 0);
  if (url_init_done == 0)
    {
      mactime_url_init (mm->vlib_main);
      url_init_done = 1;
    }

  return rv;
}

static clib_error_t *
mactime_enable_disable_command_fn (vlib_main_t * vm,
				   unformat_input_t * input,
				   vlib_cli_command_t * cmd)
{
  mactime_main_t *mm = &mactime_main;
  u32 sw_if_index = ~0;
  int enable_disable = 1;

  int rv;

  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (input, "disable"))
	enable_disable = 0;
      else if (unformat (input, "%U", unformat_vnet_sw_interface,
			 mm->vnet_main, &sw_if_index))
	;
      else if (unformat (input, "sw_if_index %d", &sw_if_index))
	;
      else
	break;
    }

  if (sw_if_index == ~0)
    return clib_error_return (0, "Please specify an interface...");

  rv = mactime_enable_disable (mm, sw_if_index, enable_disable);

  switch (rv)
    {
    case 0:
      break;

    case VNET_API_ERROR_INVALID_SW_IF_INDEX:
      return clib_error_return
	(0, "Invalid interface, only works on physical ports");
      break;

    default:
      return clib_error_return (0, "mactime_enable_disable returned %d", rv);
    }
  return 0;
}

/* *INDENT-OFF* */
VLIB_CLI_COMMAND (mactime_enable_disable_command, static) =
{
  .path = "mactime enable-disable",
  .short_help =
  "mactime enable-disable <interface-name> [disable]",
  .function = mactime_enable_disable_command_fn,
};
/* *INDENT-ON* */


/** Enable / disable time-base src mac filtration on an interface
 */

static void vl_api_mactime_enable_disable_t_handler
  (vl_api_mactime_enable_disable_t * mp)
{
  vl_api_mactime_enable_disable_reply_t *rmp;
  mactime_main_t *mm = &mactime_main;
  int rv;

  VALIDATE_SW_IF_INDEX (mp);

  rv = mactime_enable_disable (mm, ntohl (mp->sw_if_index),
			       (int) (mp->enable_disable));
  BAD_SW_IF_INDEX_LABEL;
  REPLY_MACRO (VL_API_MACTIME_ENABLE_DISABLE_REPLY);
}

static void
vl_api_mactime_dump_t_handler (vl_api_mactime_dump_t * mp)
{
  vl_api_mactime_details_t *ep;
  vl_api_mactime_dump_reply_t *rmp;
  mactime_device_t *dev;
  mactime_main_t *mm = &mactime_main;
  vl_api_registration_t *rp;
  int rv = 0, i;
  u32 his_table_epoch = clib_net_to_host_u32 (mp->my_table_epoch);
  u32 message_size;
  u32 name_len;
  u32 nranges;

  rp = vl_api_client_index_to_registration (mp->client_index);
  if (rp == 0)
    return;

  if (his_table_epoch == mm->device_table_epoch)
    {
      rv = VNET_API_ERROR_NO_CHANGE;
      goto send_reply;
    }

  /* *INDENT-OFF* */
  pool_foreach (dev, mm->devices,
  ({
    message_size = sizeof(*ep) + vec_len(dev->device_name) +
      vec_len(dev->ranges) * sizeof(ep->ranges[0]);

    ep = vl_msg_api_alloc (message_size);
    memset (ep, 0, message_size);
    ep->_vl_msg_id = clib_host_to_net_u16 (VL_API_MACTIME_DETAILS
                                           + mm->msg_id_base);
    ep->context = mp->context;
    /* Index is the key for the stats segment combined counters */
    ep->pool_index = clib_host_to_net_u32 (dev - mm->devices);

    clib_memcpy_fast (ep->mac_address, dev->mac_address,
                      sizeof (ep->mac_address));
    ep->data_quota = clib_host_to_net_u64 (dev->data_quota);
    ep->data_used_in_range = clib_host_to_net_u64 (dev->data_used_in_range);
    ep->flags = clib_host_to_net_u32 (dev->flags);
    nranges = vec_len (dev->ranges);
    ep->nranges = clib_host_to_net_u32 (nranges);

    for (i = 0; i < vec_len (dev->ranges); i++)
      {
        ep->ranges[i].start = dev->ranges[i].start;
        ep->ranges[i].end = dev->ranges[i].end;
      }

    name_len = vec_len (dev->device_name);
    name_len = (name_len < ARRAY_LEN(ep->device_name)) ?
      name_len : ARRAY_LEN(ep->device_name) - 1;

    clib_memcpy_fast (ep->device_name, dev->device_name,
                      name_len);
    ep->device_name [ARRAY_LEN(ep->device_name) -1] = 0;
    vl_api_send_msg (rp, (u8 *)ep);
  }));
  /* *INDENT-OFF* */

 send_reply:
  /* *INDENT-OFF* */
  REPLY_MACRO2 (VL_API_MACTIME_DUMP_REPLY,
  ({
    rmp->table_epoch = clib_host_to_net_u32 (mm->device_table_epoch);
  }));
  /* *INDENT-ON* */
}

/** Create a lookup table entry for the indicated mac address
 */
void
mactime_send_create_entry_message (u8 * mac_address)
{
  mactime_main_t *mm = &mactime_main;
  api_main_t *am;
  vl_shmem_hdr_t *shmem_hdr;
  u8 *name;
  vl_api_mactime_add_del_range_t *mp;

  am = vlibapi_get_main ();
  shmem_hdr = am->shmem_hdr;
  mp = vl_msg_api_alloc_as_if_client (sizeof (*mp));
  clib_memset (mp, 0, sizeof (*mp));
  mp->_vl_msg_id = ntohs (VL_API_MACTIME_ADD_DEL_RANGE + mm->msg_id_base);
  name = format (0, "mac-%U", format_mac_address, mac_address);

  memcpy (mp->device_name, name, vec_len (name));
  memcpy (mp->mac_address, mac_address, sizeof (mp->mac_address));
  /* $$$ config: create allow / drop / range */
  mp->allow = 1;
  mp->is_add = 1;
  vl_msg_api_send_shmem (shmem_hdr->vl_input_queue, (u8 *) & mp);
}

/** Add or delete static / dynamic accept/drop configuration for a src mac
 */

static void vl_api_mactime_add_del_range_t_handler
  (vl_api_mactime_add_del_range_t * mp)
{
  mactime_main_t *mm = &mactime_main;
  vl_api_mactime_add_del_range_reply_t *rmp;
  mactime_device_t *dp;
  clib_bihash_kv_8_8_t kv;
  int found = 1;
  clib_bihash_8_8_t *lut = &mm->lookup_table;
  u64 data_quota;
  int i, rv = 0;

  feature_init (mm);

  /*
   * Change the table epoch. Skip 0 so clients can code my_table_epoch = 0
   * to receive a full dump.
   */
  mm->device_table_epoch++;
  if (PREDICT_FALSE (mm->device_table_epoch == 0))
    mm->device_table_epoch++;

  data_quota = clib_net_to_host_u64 (mp->data_quota);

  clib_memset (&kv, 0, sizeof (kv));
  memcpy (&kv.key, mp->mac_address, sizeof (mp->mac_address));

  /* See if we have a lookup table entry for this src mac address */
  if (clib_bihash_search_8_8 (lut, &kv, &kv) < 0)
    found = 0;

  /* Add an entry? */
  if (mp->is_add)
    {
      /* Create the device entry? */
      if (found == 0)
	{
	  pool_get (mm->devices, dp);
	  clib_memset (dp, 0, sizeof (*dp));
	  vlib_validate_combined_counter (&mm->allow_counters,
					  dp - mm->devices);
	  vlib_zero_combined_counter (&mm->allow_counters, dp - mm->devices);
	  vlib_validate_combined_counter (&mm->drop_counters,
					  dp - mm->devices);
	  vlib_zero_combined_counter (&mm->drop_counters, dp - mm->devices);
	  mp->device_name[ARRAY_LEN (mp->device_name) - 1] = 0;
	  dp->device_name = format (0, "%s%c", mp->device_name, 0);
	  memcpy (dp->mac_address, mp->mac_address, sizeof (mp->mac_address));
	  for (i = 0; i < clib_net_to_host_u32 (mp->count); i++)
	    {
	      clib_timebase_range_t _r, *r = &_r;
	      r->start = mp->ranges[i].start;
	      r->end = mp->ranges[i].end;
	      vec_add1 (dp->ranges, r[0]);
	    }
	  /* If we found some time ranges */
	  if (i)
	    {
	      /* Set allow/drop based on msg flags */
	      if (mp->drop)
		dp->flags = MACTIME_DEVICE_FLAG_DYNAMIC_DROP;
	      if (mp->allow)
		dp->flags = MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW;
	      if (mp->allow_quota)
		dp->flags = MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW_QUOTA;
	    }
	  else
	    {
	      /* no ranges, it's a static allow/drop */
	      if (mp->drop)
		dp->flags = MACTIME_DEVICE_FLAG_STATIC_DROP;
	      if (mp->allow)
		dp->flags = MACTIME_DEVICE_FLAG_STATIC_ALLOW;
	    }
	  if (mp->no_udp_10001)
	    dp->flags |= MACTIME_DEVICE_FLAG_DROP_UDP_10001;

	  dp->data_quota = data_quota;

	  /* Add the hash table entry */
	  kv.value = dp - mm->devices;
	  clib_bihash_add_del_8_8 (lut, &kv, 1 /* is_add */ );
	}
      else			/* add more ranges, flags, etc. */
	{
	  dp = pool_elt_at_index (mm->devices, kv.value);

	  for (i = 0; i < clib_net_to_host_u32 (mp->count); i++)
	    {
	      clib_timebase_range_t _r, *r = &_r;
	      r->start = mp->ranges[i].start;
	      r->end = mp->ranges[i].end;
	      vec_add1 (dp->ranges, r[0]);
	    }

	  if (vec_len (dp->ranges))
	    {
	      /* Set allow/drop based on msg flags */
	      if (mp->drop)
		dp->flags = MACTIME_DEVICE_FLAG_DYNAMIC_DROP;
	      if (mp->allow)
		dp->flags = MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW;
	      if (mp->allow_quota)
		dp->flags = MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW_QUOTA;
	    }
	  else
	    {
	      /* no ranges, it's a static allow/drop */
	      if (mp->drop)
		dp->flags = MACTIME_DEVICE_FLAG_STATIC_DROP;
	      if (mp->allow)
		dp->flags = MACTIME_DEVICE_FLAG_STATIC_ALLOW;
	    }
	  if (mp->no_udp_10001)
	    dp->flags |= MACTIME_DEVICE_FLAG_DROP_UDP_10001;

	  dp->data_quota = data_quota;
	}
    }
  else				/* delete case */
    {
      if (found == 0)
	{
	  rv = VNET_API_ERROR_NO_SUCH_ENTRY;
	  goto reply;
	}

      /* find the device entry */
      dp = pool_elt_at_index (mm->devices, kv.value);

      /* Remove it from the lookup table */
      clib_bihash_add_del_8_8 (lut, &kv, 0 /* is_add */ );
      vec_free (dp->ranges);
      pool_put (mm->devices, dp);
    }

reply:
  REPLY_MACRO (VL_API_MACTIME_ADD_DEL_RANGE_REPLY);
}

#include <mactime/mactime.api.c>
static clib_error_t *
mactime_init (vlib_main_t * vm)
{
  mactime_main_t *mm = &mactime_main;

  mm->vlib_main = vm;
  mm->vnet_main = vnet_get_main ();

  /* Ask for a correctly-sized block of API message decode slots */
  mm->msg_id_base = setup_message_id_table ();

  mm->lookup_table_num_buckets = MACTIME_NUM_BUCKETS;
  mm->lookup_table_memory_size = MACTIME_MEMORY_SIZE;
  mm->timezone_offset = -5;	/* US EST / EDT */
  return 0;
}

/* *INDENT-OFF* */
VLIB_INIT_FUNCTION (mactime_init) =
{
  .runs_after = VLIB_INITS("ip_neighbor_init"),
};
/* *INDENT-ON* */

static clib_error_t *
mactime_config (vlib_main_t * vm, unformat_input_t * input)
{
  mactime_main_t *mm = &mactime_main;

  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (input, "lookup-table-buckets %u",
		    &mm->lookup_table_num_buckets))
	;
      else if (unformat (input, "lookup-table-memory %U",
			 unformat_memory_size, &mm->lookup_table_memory_size))
	;
      else if (unformat (input, "timezone_offset %d", &mm->timezone_offset))
	;
      else
	{
	  return clib_error_return (0, "unknown input '%U'",
				    format_unformat_error, input);
	}
    }
  return 0;
}

VLIB_CONFIG_FUNCTION (mactime_config, "mactime");

/* *INDENT-OFF* */
VNET_FEATURE_INIT (mactime, static) =
{
  .arc_name = "device-input",
  .node_name = "mactime",
  .runs_before = VNET_FEATURES ("ethernet-input"),
};
/* *INDENT-ON */

/* *INDENT-OFF* */
VNET_FEATURE_INIT (mactime_tx, static) =
{
  .arc_name = "interface-output",
  .node_name = "mactime-tx",
  .runs_before = VNET_FEATURES ("interface-tx"),
};
/* *INDENT-ON */

/* *INDENT-OFF* */
VLIB_PLUGIN_REGISTER () =
{
  .version = VPP_BUILD_VER,
  .description = "Time-based MAC Source Address Filter",
};
/* *INDENT-ON* */

u8 *
format_bytes_with_width (u8 * s, va_list * va)
{
  uword nbytes = va_arg (*va, u64);
  int width = va_arg (*va, int);
  f64 nbytes_f64;
  u8 *fmt;
  char *suffix = "";

  if (width > 0)
    fmt = format (0, "%%%d.3f%%s%c", width, 0);
  else
    fmt = format (0, "%%.3f%%s%c", 0);

  if (nbytes > (1024ULL * 1024ULL * 1024ULL))
    {
      nbytes_f64 = ((f64) nbytes) / (1024.0 * 1024.0 * 1024.0);
      suffix = "G";
    }
  else if (nbytes > (1024ULL * 1024ULL))
    {
      nbytes_f64 = ((f64) nbytes) / (1024.0 * 1024.0);
      suffix = "M";
    }
  else if (nbytes > 1024ULL)
    {
      nbytes_f64 = ((f64) nbytes) / (1024.0);
      suffix = "K";
    }
  else
    {
      nbytes_f64 = (f64) nbytes;
      suffix = "B";
    }

  s = format (s, (char *) fmt, nbytes_f64, suffix);
  vec_free (fmt);
  return s;
}

static walk_rc_t
mactime_ip_neighbor_copy (index_t ipni, void *ctx)
{
  mactime_main_t *mm = ctx;

  vec_add1 (mm->arp_cache_copy, ipni);

  return (WALK_CONTINUE);
}

static clib_error_t *
show_mactime_command_fn (vlib_main_t * vm,
			 unformat_input_t * input, vlib_cli_command_t * cmd)
{
  mactime_main_t *mm = &mactime_main;
  mactime_device_t *dp;
  u8 *macstring = 0;
  char *status_string;
  u32 *pool_indices = 0;
  int verbose = 0;
  int current_status = 99;
  int i, j;
  f64 now;
  vlib_counter_t allow, drop;
  ip_neighbor_t *ipn;

  if (mm->feature_initialized == 0)
    return clib_error_return
      (0,
       "Feature not initialized, suggest 'help mactime enable-disable'...");

  vec_reset_length (mm->arp_cache_copy);
  /* Walk all ip4 neighbours on all interfaces */
  ip_neighbor_walk (IP46_TYPE_IP4, ~0, mactime_ip_neighbor_copy, mm);

  now = clib_timebase_now (&mm->timebase);

  if (PREDICT_FALSE ((now - mm->sunday_midnight) > 86400.0 * 7.0))
    mm->sunday_midnight = clib_timebase_find_sunday_midnight (now);

  if (unformat (input, "verbose %d", &verbose))
    ;

  if (unformat (input, "verbose"))
    verbose = 1;

  if (verbose)
    vlib_cli_output (vm, "Time now: %U", format_clib_timebase_time, now);

  /* *INDENT-OFF* */
  pool_foreach (dp, mm->devices,
  ({
    vec_add1 (pool_indices, dp - mm->devices);
  }));
  /* *INDENT-ON* */

  vlib_cli_output (vm, "%-15s %18s %14s %10s %11s %13s",
		   "Device Name", "Addresses", "Status",
		   "AllowPkt", "AllowByte", "DropPkt");

  for (i = 0; i < vec_len (pool_indices); i++)
    {
      dp = pool_elt_at_index (mm->devices, pool_indices[i]);

      /* Check dynamic ranges */
      for (j = 0; j < vec_len (dp->ranges); j++)
	{
	  clib_timebase_range_t *r = dp->ranges + j;
	  f64 start0, end0;

	  start0 = r->start + mm->sunday_midnight;
	  end0 = r->end + mm->sunday_midnight;
	  if (verbose > 1)
	    vlib_cli_output (vm, "  Range %d: %U - %U", j,
			     format_clib_timebase_time, start0,
			     format_clib_timebase_time, end0);

	  if (now >= start0 && now <= end0)
	    {
	      if (dp->flags & MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW)
		current_status = 3;
	      else if (dp->flags & MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW_QUOTA)
		current_status = 5;
	      else
		current_status = 2;
	      if (verbose)
		{
		  vlib_cli_output (vm, "  Time in range %d:", j);
		  vlib_cli_output (vm, "     %U - %U",
				   format_clib_timebase_time, start0,
				   format_clib_timebase_time, end0);
		}
	      goto print;
	    }
	}
      if (verbose && j)
	vlib_cli_output (vm, "  No range match.");
      if (dp->flags & MACTIME_DEVICE_FLAG_STATIC_DROP)
	current_status = 0;
      if (dp->flags & MACTIME_DEVICE_FLAG_STATIC_ALLOW)
	current_status = 1;
      if (dp->flags & MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW)
	current_status = 2;
      if (dp->flags & MACTIME_DEVICE_FLAG_DYNAMIC_DROP)
	current_status = 3;
      if (dp->flags & MACTIME_DEVICE_FLAG_DYNAMIC_ALLOW_QUOTA)
	current_status = 4;

    print:
      vec_reset_length (macstring);
      macstring = format (0, "%U", format_mac_address, dp->mac_address);
      switch (current_status)
	{
	case 0:
	  status_string = "static drop";
	  break;
	case 1:
	  status_string = "static allow";
	  break;
	case 2:
	  status_string = "dynamic drop";
	  break;
	case 3:
	  status_string = "dynamic allow";
	  break;
	case 4:
	  status_string = "d-quota inact";
	  break;
	case 5:
	  status_string = "d-quota activ";
	  break;
	default:
	  status_string = "code bug!";
	  break;
	}
      vlib_get_combined_counter (&mm->allow_counters, dp - mm->devices,
				 &allow);
      vlib_get_combined_counter (&mm->drop_counters, dp - mm->devices, &drop);
      vlib_cli_output (vm, "%-15s %18s %14s %10lld %U %13lld",
		       dp->device_name, macstring, status_string,
		       allow.packets, format_bytes_with_width, allow.bytes,
		       10, drop.packets);
      if (dp->data_quota > 0)
	vlib_cli_output (vm, "%-54s %s%U %s%U", " ", "Quota ",
			 format_bytes_with_width, dp->data_quota, 10,
			 "Use ", format_bytes_with_width,
			 dp->data_used_in_range, 8);
      /* This is really only good for small N... */
      for (j = 0; j < vec_len (mm->arp_cache_copy); j++)
	{
	  ipn = ip_neighbor_get (mm->arp_cache_copy[j]);
	  if (!memcmp
	      (dp->mac_address, ipn->ipn_mac.bytes, sizeof (ipn->ipn_mac)))
	    {
	      vlib_cli_output (vm, "%17s%U", " ", format_ip46_address,
			       ip_neighbor_get_ip (ipn), IP46_TYPE_IP4);
	    }
	}
    }
  vec_free (macstring);
  vec_free (pool_indices);

  return 0;
}

/* *INDENT-OFF* */
VLIB_CLI_COMMAND (show_mactime_command, static) =
{
  .path = "show mactime",
  .short_help = "show mactime [verbose]",
  .function = show_mactime_command_fn,
};
/* *INDENT-ON* */

static clib_error_t *
clear_mactime_command_fn (vlib_main_t * vm,
			  unformat_input_t * input, vlib_cli_command_t * cmd)
{
  mactime_main_t *mm = &mactime_main;

  if (mm->feature_initialized == 0)
    return clib_error_return
      (0,
       "Feature not initialized, suggest 'help mactime enable-disable'...");

  vlib_clear_combined_counters (&mm->allow_counters);
  vlib_clear_combined_counters (&mm->drop_counters);
  vlib_cli_output (vm, "Mactime counters cleared...");
  return 0;
}

/* *INDENT-OFF* */
VLIB_CLI_COMMAND (clear_mactime_command, static) =
{
  .path = "clear mactime",
  .short_help = "clear mactime counters",
  .function = clear_mactime_command_fn,
};
/* *INDENT-ON* */



/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */
set_vif(erf_vif); /* socket id */ lpt->m_node_gen.m_socket_id =m_cores_vif[i+1]->get_socket_id(); } m_fl_was_init=true; return (0); } //////////////////////////////////////////// static CGlobalTRex g_trex; bool CCoreEthIF::process_rx_pkt(pkt_dir_t dir, rte_mbuf_t * m){ CSimplePacketParser parser(m); if ( !parser.Parse() ){ return false; } bool send=false; CLatencyPktMode *c_l_pkt_mode = g_trex.m_mg.c_l_pkt_mode; bool is_lateancy_pkt = c_l_pkt_mode->IsLatencyPkt(parser.m_ipv4) & parser.IsLatencyPkt(parser.m_l4 + c_l_pkt_mode->l4_header_len()); if (is_lateancy_pkt){ send=true; }else{ if ( get_is_rx_filter_enable() ){ uint8_t max_ttl = 0xff - get_rx_check_hops(); uint8_t pkt_ttl = parser.getTTl(); if ( (pkt_ttl==max_ttl) || (pkt_ttl==(max_ttl-1) ) ) { send=true; } } } if (send) { CGenNodeLatencyPktInfo * node=(CGenNodeLatencyPktInfo * )CGlobalInfo::create_node(); if ( node ) { node->m_msg_type = CGenNodeMsgBase::LATENCY_PKT; node->m_dir = dir; node->m_latency_offset = 0xdead; node->m_pkt = m; if ( m_ring_to_rx->Enqueue((CGenNode*)node)==0 ){ }else{ CGlobalInfo::free_node((CGenNode *)node); send=false; } #ifdef LATENCY_QUEUE_TRACE_ printf("rx to cp --\n"); rte_pktmbuf_dump(stdout,m, rte_pktmbuf_pkt_len(m)); #endif }else{ send=false; } } return (send); } TrexStateless * get_stateless_obj() { return g_trex.m_trex_stateless; } static int latency_one_lcore(__attribute__((unused)) void *dummy) { CPlatformSocketInfo * lpsock=&CGlobalInfo::m_socket; physical_thread_id_t phy_id =rte_lcore_id(); if ( lpsock->thread_phy_is_latency( phy_id ) ){ g_trex.run_in_laterncy_core(); }else{ if ( lpsock->thread_phy_is_master( phy_id ) ) { g_trex.run_in_master(); delay(1); }else{ delay((uint32_t)(1000.0*CGlobalInfo::m_options.m_duration)); /* this core has stopped */ g_trex.m_signal[ lpsock->thread_phy_to_virt( phy_id ) ]=1; } } return 0; } static int slave_one_lcore(__attribute__((unused)) void *dummy) { CPlatformSocketInfo * lpsock=&CGlobalInfo::m_socket; physical_thread_id_t phy_id =rte_lcore_id(); if ( lpsock->thread_phy_is_latency( phy_id ) ){ g_trex.run_in_laterncy_core(); }else{ if ( lpsock->thread_phy_is_master( phy_id ) ) { g_trex.run_in_master(); delay(1); }else{ g_trex.run_in_core( lpsock->thread_phy_to_virt( phy_id ) ); } } return 0; } uint32_t get_cores_mask(uint32_t cores,int offset){ int i; uint32_t res=1; uint32_t mask=(1<<(offset+1)); for (i=0; i<(cores-1); i++) { res |= mask ; mask = mask <<1; } return (res); } int main(int argc , char * argv[]){ return ( main_test(argc , argv)); } int update_global_info_from_platform_file(){ CPlatformYamlInfo *cg=&global_platform_cfg_info; CGlobalInfo::m_socket.Create(&cg->m_platform); if (!cg->m_info_exist) { /* nothing to do ! */ return 0; } CGlobalInfo::m_options.prefix =cg->m_prefix; CGlobalInfo::m_options.preview.setCores(cg->m_thread_per_dual_if); if ( cg->m_port_limit_exist ){ CGlobalInfo::m_options.m_expected_portd =cg->m_port_limit; } if ( cg->m_enable_zmq_pub_exist ){ CGlobalInfo::m_options.preview.set_zmq_publish_enable(cg->m_enable_zmq_pub); CGlobalInfo::m_options.m_zmq_port = cg->m_zmq_pub_port; } if ( cg->m_telnet_exist ){ CGlobalInfo::m_options.m_telnet_port = cg->m_telnet_port; } if ( cg->m_mac_info_exist ){ int i; /* cop the file info */ int port_size=cg->m_mac_info.size(); if ( port_size > BP_MAX_PORTS ){ port_size = BP_MAX_PORTS; } for (i=0; im_mac_info[i].copy_src(( char *)CGlobalInfo::m_options.m_mac_addr[i].u.m_mac.src) ; cg->m_mac_info[i].copy_dest(( char *)CGlobalInfo::m_options.m_mac_addr[i].u.m_mac.dest) ; } } /* mul by interface type */ float mul=1.0; if (cg->m_port_bandwidth_gb<10) { cg->m_port_bandwidth_gb=10.0; } mul = mul*(float)cg->m_port_bandwidth_gb/10.0; mul= mul * (float)cg->m_port_limit/2.0; CGlobalInfo::m_memory_cfg.set(cg->m_memory,mul); CGlobalInfo::m_memory_cfg.set_number_of_dp_cors( CGlobalInfo::m_options.get_number_of_dp_cores_needed() ); return (0); } extern "C" int eal_cpu_detected(unsigned lcore_id); // return mask representing available cores int core_mask_calc() { uint32_t mask = 0; int lcore_id; for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) { if (eal_cpu_detected(lcore_id)) { mask |= (1 << lcore_id); } } return mask; } // Return number of set bits in i uint32_t num_set_bits(uint32_t i) { i = i - ((i >> 1) & 0x55555555); i = (i & 0x33333333) + ((i >> 2) & 0x33333333); return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24; } // sanity check if the cores we want to use really exist int core_mask_sanity(uint32_t wanted_core_mask) { uint32_t calc_core_mask = core_mask_calc(); uint32_t wanted_core_num, calc_core_num; wanted_core_num = num_set_bits(wanted_core_mask); calc_core_num = num_set_bits(calc_core_mask); if (wanted_core_num > calc_core_num) { printf("Error: You have %d threads available, but you asked for %d threads.\n", calc_core_num, wanted_core_num); printf(" Calculation is: -c (%d) * dual ports (%d) + 1 master thread %s" , CGlobalInfo::m_options.preview.getCores(), CGlobalInfo::m_options.get_expected_dual_ports() , get_is_latency_thread_enable() ? "+1 latency thread (because of -l flag)\n" : "\n"); printf(" Maybe try smaller -c .\n"); return -1; } if (wanted_core_mask != (wanted_core_mask & calc_core_mask)) { printf ("Serious error: Something is wrong with the hardware. Wanted core mask is %x. Existing core mask is %x\n", wanted_core_mask, calc_core_mask); return -1; } return 0; } int update_dpdk_args(void){ CPlatformSocketInfo * lpsock=&CGlobalInfo::m_socket; CParserOption * lpop= &CGlobalInfo::m_options; lpsock->set_latency_thread_is_enabled(get_is_latency_thread_enable()); lpsock->set_number_of_threads_per_ports(lpop->preview.getCores() ); lpsock->set_number_of_dual_ports(lpop->get_expected_dual_ports()); if ( !lpsock->sanity_check() ){ printf(" ERROR in configuration file \n"); return (-1); } if ( CGlobalInfo::m_options.preview.getVMode() > 0 ) { lpsock->dump(stdout); } sprintf(global_cores_str,"0x%llx",(unsigned long long)lpsock->get_cores_mask()); if (core_mask_sanity(strtol(global_cores_str, NULL, 16)) < 0) { return -1; } /* set the DPDK options */ global_dpdk_args_num =7; global_dpdk_args[0]=(char *)"xx"; global_dpdk_args[1]=(char *)"-c"; global_dpdk_args[2]=(char *)global_cores_str; global_dpdk_args[3]=(char *)"-n"; global_dpdk_args[4]=(char *)"4"; if ( CGlobalInfo::m_options.preview.getVMode() == 0 ) { global_dpdk_args[5]=(char *)"--log-level"; sprintf(global_loglevel_str,"%d",1); global_dpdk_args[6]=(char *)global_loglevel_str; }else{ global_dpdk_args[5]=(char *)"--log-level"; sprintf(global_loglevel_str,"%d",CGlobalInfo::m_options.preview.getVMode()+1); global_dpdk_args[6]=(char *)global_loglevel_str; } global_dpdk_args_num = 7; /* add white list */ for (int i=0; i<(int)global_platform_cfg_info.m_if_list.size(); i++) { global_dpdk_args[global_dpdk_args_num++]=(char *)"-w"; global_dpdk_args[global_dpdk_args_num++]=(char *)global_platform_cfg_info.m_if_list[i].c_str(); } if ( lpop->prefix.length() ){ global_dpdk_args[global_dpdk_args_num++]=(char *)"--file-prefix"; sprintf(global_prefix_str,"%s",lpop->prefix.c_str()); global_dpdk_args[global_dpdk_args_num++]=(char *)global_prefix_str; global_dpdk_args[global_dpdk_args_num++]=(char *)"-m"; if (global_platform_cfg_info.m_limit_memory.length()) { global_dpdk_args[global_dpdk_args_num++]=(char *)global_platform_cfg_info.m_limit_memory.c_str(); }else{ global_dpdk_args[global_dpdk_args_num++]=(char *)"1024"; } } if ( CGlobalInfo::m_options.preview.getVMode() > 0 ) { printf("args \n"); int i; for (i=0; icfg_file,1); if ( op->preview.getVMode() >0 ) { fl.DumpCsv(stdout); } uint32_t start= os_get_time_msec(); CErfIF erf_vif; fl.generate_p_thread_info(1); CFlowGenListPerThread * lpt; lpt=fl.m_threads_info[0]; lpt->set_vif(&erf_vif); if ( (op->preview.getVMode() >1) || op->preview.getFileWrite() ) { lpt->start_generate_stateful(op->out_file,op->preview); } lpt->m_node_gen.DumpHist(stdout); uint32_t stop= os_get_time_msec(); printf(" d time = %ul %ul \n",stop-start,os_get_time_freq()); fl.Delete(); return (0); } int main_test(int argc , char * argv[]){ utl_termio_init(); int ret; unsigned lcore_id; printf("Starting TRex %s please wait ... \n",VERSION_BUILD_NUM); CGlobalInfo::m_options.preview.clean(); if ( parse_options(argc, argv, &CGlobalInfo::m_options,true ) != 0){ exit(-1); } update_global_info_from_platform_file(); /* it is not a mistake , give the user higher priorty over the configuration file */ parse_options(argc, argv, &CGlobalInfo::m_options ,false); if ( CGlobalInfo::m_options.preview.getVMode() > 0){ CGlobalInfo::m_options.dump(stdout); CGlobalInfo::m_memory_cfg.Dump(stdout); } if (update_dpdk_args() < 0) { return -1; } CParserOption * po=&CGlobalInfo::m_options; if ( CGlobalInfo::m_options.preview.getVMode() == 0 ) { rte_set_log_level(1); } uid_t uid; uid = geteuid (); if ( uid != 0 ) { printf("ERROR you must run with superuser priviliges \n"); printf("User id : %d \n",uid); printf("try 'sudo' %s \n",argv[0]); return (-1); } ret = rte_eal_init(global_dpdk_args_num, (char **)global_dpdk_args); if (ret < 0){ printf(" You might need to run ./trex-cfg once \n"); rte_exit(EXIT_FAILURE, "Invalid EAL arguments\n"); } time_init(); /* check if we are in simulation mode */ if ( CGlobalInfo::m_options.out_file != "" ){ printf(" t-rex simulation mode into %s \n",CGlobalInfo::m_options.out_file.c_str()); return ( sim_load_list_of_cap_files(&CGlobalInfo::m_options) ); } if ( !g_trex.Create() ){ exit(1); } if (po->preview.get_is_rx_check_enable() && (po->m_rx_check_sampe< get_min_sample_rate()) ) { po->m_rx_check_sampe = get_min_sample_rate(); printf("Warning rx check sample rate should be lower than %d setting it to %d\n",get_min_sample_rate(),get_min_sample_rate()); } /* set dump mode */ g_trex.m_io_modes.set_mode((CTrexGlobalIoMode::CliDumpMode)CGlobalInfo::m_options.m_io_mode); if ( !CGlobalInfo::m_options.is_latency_disabled() && (CGlobalInfo::m_options.m_latency_prev>0) ){ uint32_t pkts = CGlobalInfo::m_options.m_latency_prev* CGlobalInfo::m_options.m_latency_rate; printf("Start prev latency check- for %d sec \n",CGlobalInfo::m_options.m_latency_prev); g_trex.m_mg.start(pkts); delay(CGlobalInfo::m_options.m_latency_prev* 1000); printf("Finished \n"); g_trex.m_mg.reset(); g_trex.reset_counters(); } if ( get_is_stateless() ) { g_trex.start_master_stateless(); }else{ g_trex.start_send_master(); } /* TBD_FDIR */ #if 0 printf(" test_send \n"); g_trex.test_send(); // while (1) { delay(10000); exit(0); // } #endif if ( CGlobalInfo::m_options.preview.getOnlyLatency() ){ rte_eal_mp_remote_launch(latency_one_lcore, NULL, CALL_MASTER); RTE_LCORE_FOREACH_SLAVE(lcore_id) { if (rte_eal_wait_lcore(lcore_id) < 0) return -1; } g_trex.stop_master(); return (0); } if ( CGlobalInfo::m_options.preview.getSingleCore() ) { g_trex.run_in_core(1); g_trex.stop_master(); return (0); } rte_eal_mp_remote_launch(slave_one_lcore, NULL, CALL_MASTER); RTE_LCORE_FOREACH_SLAVE(lcore_id) { if (rte_eal_wait_lcore(lcore_id) < 0) return -1; } g_trex.stop_master(); g_trex.Delete(); utl_termio_reset(); return (0); } ////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////// // driver section ////////////////////////////////////////////////////////////////////////////////////////////// int CTRexExtendedDriverBase::configure_drop_queue(CPhyEthIF * _if) { uint8_t port_id=_if->get_rte_port_id(); return (rte_eth_dev_rx_queue_stop(port_id, 0)); } void wait_x_sec(int sec) { int i; printf(" wait %d sec ", sec); fflush(stdout); for (i=0; im_tx_conf.tx_thresh.pthresh = TX_PTHRESH_1G; cfg->m_tx_conf.tx_thresh.hthresh = TX_HTHRESH; cfg->m_tx_conf.tx_thresh.wthresh = 0; } void CTRexExtendedDriverBase1G::update_global_config_fdir(port_cfg_t * cfg){ // Configuration is done in configure_rx_filter_rules by writing to registers } // e1000 driver does not support the generic stop queue API, so we need to implement ourselves int CTRexExtendedDriverBase1G::configure_drop_queue(CPhyEthIF * _if) { // Drop packets coming to RX queue 0 _if->pci_reg_write( E1000_RXDCTL(0) , 0); return 0; } int CTRexExtendedDriverBase1G::configure_rx_filter_rules(CPhyEthIF * _if){ uint16_t hops = get_rx_check_hops(); uint16_t v4_hops = (hops << 8)&0xff00; uint8_t protocol; if (CGlobalInfo::m_options.m_l_pkt_mode == 0) { protocol = IPPROTO_SCTP; } else { protocol = IPPROTO_ICMP; } /* enable filter to pass packet to rx queue 1 */ _if->pci_reg_write( E1000_IMIR(0), 0x00020000); _if->pci_reg_write( E1000_IMIREXT(0), 0x00081000); _if->pci_reg_write( E1000_TTQF(0), protocol | 0x00008100 /* enable */ | 0xE0010000 /* RX queue is 1 */ ); /* 16 : 12 MAC , (2)0x0800,2 | DW0 , DW1 6 bytes , TTL , PROTO | DW2=0 , DW3=0x0000FF06 */ int i; // IPv4: bytes being compared are {TTL, Protocol} uint16_t ff_rules_v4[6]={ (uint16_t)(0xFF06 - v4_hops), (uint16_t)(0xFE11 - v4_hops), (uint16_t)(0xFF11 - v4_hops), (uint16_t)(0xFE06 - v4_hops), (uint16_t)(0xFF01 - v4_hops), (uint16_t)(0xFE01 - v4_hops), } ; // IPv6: bytes being compared are {NextHdr, HopLimit} uint16_t ff_rules_v6[2]={ (uint16_t)(0x3CFF - hops), (uint16_t)(0x3CFE - hops), } ; uint16_t *ff_rules; uint16_t num_rules; uint32_t mask=0; int rule_id; if ( CGlobalInfo::m_options.preview.get_ipv6_mode_enable() ){ ff_rules = &ff_rules_v6[0]; num_rules = sizeof(ff_rules_v6)/sizeof(ff_rules_v6[0]); }else{ ff_rules = &ff_rules_v4[0]; num_rules = sizeof(ff_rules_v4)/sizeof(ff_rules_v4[0]); } uint8_t len = 24; for (rule_id=0; rule_idpci_reg_write( (E1000_FHFT(rule_id)+i) , 0); } if ( CGlobalInfo::m_options.preview.get_vlan_mode_enable() ){ len += 8; if ( CGlobalInfo::m_options.preview.get_ipv6_mode_enable() ){ // IPv6 VLAN: NextHdr/HopLimit offset = 0x18 _if->pci_reg_write( (E1000_FHFT(rule_id)+(3*16)+0) , PKT_NTOHS(ff_rules[rule_id]) ); _if->pci_reg_write( (E1000_FHFT(rule_id)+(3*16)+8) , 0x03); /* MASK */ }else{ // IPv4 VLAN: TTL/Protocol offset = 0x1A _if->pci_reg_write( (E1000_FHFT(rule_id)+(3*16)+0) , (PKT_NTOHS(ff_rules[rule_id])<<16) ); _if->pci_reg_write( (E1000_FHFT(rule_id)+(3*16)+8) , 0x0C); /* MASK */ } }else{ if ( CGlobalInfo::m_options.preview.get_ipv6_mode_enable() ){ // IPv6: NextHdr/HopLimit offset = 0x14 _if->pci_reg_write( (E1000_FHFT(rule_id)+(2*16)+4) , PKT_NTOHS(ff_rules[rule_id]) ); _if->pci_reg_write( (E1000_FHFT(rule_id)+(2*16)+8) , 0x30); /* MASK */ }else{ // IPv4: TTL/Protocol offset = 0x16 _if->pci_reg_write( (E1000_FHFT(rule_id)+(2*16)+4) , (PKT_NTOHS(ff_rules[rule_id])<<16) ); _if->pci_reg_write( (E1000_FHFT(rule_id)+(2*16)+8) , 0xC0); /* MASK */ } } // FLEX_PRIO[[18:16] = 1, RQUEUE[10:8] = 1 _if->pci_reg_write( (E1000_FHFT(rule_id)+0xFC) , (1<<16) | (1<<8) | len); mask |=(1<pci_reg_write(E1000_WUFC, (mask<<16) | (1<<14) ); return (0); } void CTRexExtendedDriverBase1G::get_extended_stats(CPhyEthIF * _if,CPhyEthIFStats *stats){ stats->ipackets += _if->pci_reg_read(E1000_GPRC) ; stats->ibytes += (_if->pci_reg_read(E1000_GORCL) ); stats->ibytes += (((uint64_t)_if->pci_reg_read(E1000_GORCH))<<32); stats->opackets += _if->pci_reg_read(E1000_GPTC); stats->obytes += _if->pci_reg_read(E1000_GOTCL) ; stats->obytes += ( (((uint64_t)_if->pci_reg_read(IXGBE_GOTCH))<<32) ); stats->f_ipackets += 0; stats->f_ibytes += 0; stats->ierrors += ( _if->pci_reg_read(E1000_RNBC) + _if->pci_reg_read(E1000_CRCERRS) + _if->pci_reg_read(E1000_ALGNERRC ) + _if->pci_reg_read(E1000_SYMERRS ) + _if->pci_reg_read(E1000_RXERRC ) + _if->pci_reg_read(E1000_ROC)+ _if->pci_reg_read(E1000_RUC)+ _if->pci_reg_read(E1000_RJC) + _if->pci_reg_read(E1000_XONRXC)+ _if->pci_reg_read(E1000_XONTXC)+ _if->pci_reg_read(E1000_XOFFRXC)+ _if->pci_reg_read(E1000_XOFFTXC)+ _if->pci_reg_read(E1000_FCRUC) ); stats->oerrors += 0; stats->imcasts = 0; stats->rx_nombuf = 0; } void CTRexExtendedDriverBase1G::clear_extended_stats(CPhyEthIF * _if){ } void CTRexExtendedDriverBase10G::clear_extended_stats(CPhyEthIF * _if){ _if->pci_reg_read(IXGBE_RXNFGPC); } void CTRexExtendedDriverBase10G::update_global_config_fdir(port_cfg_t * cfg){ cfg->update_global_config_fdir_10g(); } void CTRexExtendedDriverBase10G::update_configuration(port_cfg_t * cfg){ cfg->m_tx_conf.tx_thresh.pthresh = TX_PTHRESH; cfg->m_tx_conf.tx_thresh.hthresh = TX_HTHRESH; cfg->m_tx_conf.tx_thresh.wthresh = TX_WTHRESH; } int CTRexExtendedDriverBase10G::configure_rx_filter_rules(CPhyEthIF * _if){ uint8_t port_id=_if->get_rte_port_id(); uint16_t hops = get_rx_check_hops(); uint16_t v4_hops = (hops << 8)&0xff00; /* enable rule 0 SCTP -> queue 1 for latency */ /* 1<<21 means that queue 1 is for SCTP */ _if->pci_reg_write(IXGBE_L34T_IMIR(0),(1<<21)); _if->pci_reg_write(IXGBE_FTQF(0), IXGBE_FTQF_PROTOCOL_SCTP| (IXGBE_FTQF_PRIORITY_MASK<> 8) & 0xff; fdir_filter.input.flow_ext.flexbytes[1] = ff_rule & 0xff; fdir_filter.action.rx_queue = 1; fdir_filter.action.behavior = RTE_ETH_FDIR_ACCEPT; fdir_filter.action.report_status = RTE_ETH_FDIR_NO_REPORT_STATUS; res = rte_eth_dev_filter_ctrl(port_id, RTE_ETH_FILTER_FDIR, RTE_ETH_FILTER_ADD, &fdir_filter); if (res != 0) { rte_exit(EXIT_FAILURE, " ERROR rte_eth_dev_filter_ctrl : %d\n",res); } } return (0); } void CTRexExtendedDriverBase10G::get_extended_stats(CPhyEthIF * _if,CPhyEthIFStats *stats){ int i; uint64_t t=0; for (i=0; i<8;i++) { t+=_if->pci_reg_read(IXGBE_MPC(i)); } stats->ipackets += _if->pci_reg_read(IXGBE_GPRC) ; stats->ibytes += (_if->pci_reg_read(IXGBE_GORCL) +(((uint64_t)_if->pci_reg_read(IXGBE_GORCH))<<32)); stats->opackets += _if->pci_reg_read(IXGBE_GPTC); stats->obytes += (_if->pci_reg_read(IXGBE_GOTCL) +(((uint64_t)_if->pci_reg_read(IXGBE_GOTCH))<<32)); stats->f_ipackets += _if->pci_reg_read(IXGBE_RXDGPC); stats->f_ibytes += (_if->pci_reg_read(IXGBE_RXDGBCL) +(((uint64_t)_if->pci_reg_read(IXGBE_RXDGBCH))<<32)); stats->ierrors += ( _if->pci_reg_read(IXGBE_RLEC) + _if->pci_reg_read(IXGBE_ERRBC) + _if->pci_reg_read(IXGBE_CRCERRS) + _if->pci_reg_read(IXGBE_ILLERRC ) + _if->pci_reg_read(IXGBE_ROC)+ _if->pci_reg_read(IXGBE_RUC)+t); stats->oerrors += 0; stats->imcasts = 0; stats->rx_nombuf = 0; } int CTRexExtendedDriverBase10G::wait_for_stable_link(){ delay(2000); return (0); } //////////////////////////////////////////////////////////////////////////////// void CTRexExtendedDriverBase40G::clear_extended_stats(CPhyEthIF * _if){ rte_eth_stats_reset(_if->get_port_id()); } void CTRexExtendedDriverBase40G::update_configuration(port_cfg_t * cfg){ cfg->m_tx_conf.tx_thresh.pthresh = TX_PTHRESH; cfg->m_tx_conf.tx_thresh.hthresh = TX_HTHRESH; cfg->m_tx_conf.tx_thresh.wthresh = TX_WTHRESH; cfg->update_global_config_fdir_40g(); } /* Add rule to send packets with protocol 'type', and ttl 'ttl' to rx queue 1 */ void CTRexExtendedDriverBase40G::add_rules(CPhyEthIF * _if, uint16_t type, uint8_t ttl){ uint8_t port_id = _if->get_port_id(); int ret=rte_eth_dev_filter_supported(port_id, RTE_ETH_FILTER_FDIR); if ( ret !=0 ){ rte_exit(EXIT_FAILURE, "rte_eth_dev_filter_supported " "err=%d, port=%u \n", ret, port_id); } struct rte_eth_fdir_filter filter; memset(&filter,0,sizeof(struct rte_eth_fdir_filter)); filter.action.rx_queue =1; filter.action.behavior =RTE_ETH_FDIR_ACCEPT; filter.action.report_status =RTE_ETH_FDIR_NO_REPORT_STATUS; filter.soft_id=0; filter.input.flow_type = type; switch (type) { case RTE_ETH_FLOW_NONFRAG_IPV4_OTHER: filter.input.flow.ip4_flow.ttl=ttl; filter.input.flow.ip4_flow.l4_protocol = IPPROTO_ICMP; // In this case we want filter for icmp packets break; case RTE_ETH_FLOW_NONFRAG_IPV4_UDP: case RTE_ETH_FLOW_NONFRAG_IPV4_TCP: case RTE_ETH_FLOW_NONFRAG_IPV4_SCTP: filter.input.flow.ip4_flow.ttl=ttl; break; case RTE_ETH_FLOW_NONFRAG_IPV6_UDP: case RTE_ETH_FLOW_NONFRAG_IPV6_TCP: filter.input.flow.ipv6_flow.hop_limit=ttl; break; case RTE_ETH_FLOW_NONFRAG_IPV6_OTHER: filter.input.flow.ipv6_flow.hop_limit=ttl; filter.input.flow.ipv6_flow.l4_protocol = IPPROTO_ICMP; // In the future we want to support this break; } /* We want to place latency packets in queue 1 */ ret=rte_eth_dev_filter_ctrl(port_id, RTE_ETH_FILTER_FDIR, RTE_ETH_FILTER_ADD, (void*)&filter); if ( ret !=0 ){ rte_exit(EXIT_FAILURE, "rte_eth_dev_filter_ctrl" "err=%d, port=%u \n", ret, port_id); } } int CTRexExtendedDriverBase40G::configure_rx_filter_rules(CPhyEthIF * _if){ uint16_t hops = get_rx_check_hops(); int i; for (i=0; i<2; i++) { uint8_t ttl=0xff-i-hops; add_rules(_if,RTE_ETH_FLOW_NONFRAG_IPV4_UDP, ttl); add_rules(_if,RTE_ETH_FLOW_NONFRAG_IPV4_TCP, ttl); add_rules(_if,RTE_ETH_FLOW_NONFRAG_IPV6_UDP, ttl); add_rules(_if,RTE_ETH_FLOW_NONFRAG_IPV6_TCP, ttl); } /* Configure queue for latency packets */ add_rules(_if,RTE_ETH_FLOW_NONFRAG_IPV4_OTHER, 255); add_rules(_if,RTE_ETH_FLOW_NONFRAG_IPV4_SCTP, 255); return (0); } void CTRexExtendedDriverBase40G::get_extended_stats(CPhyEthIF * _if,CPhyEthIFStats *stats){ struct rte_eth_stats stats1; rte_eth_stats_get(_if->get_port_id(), &stats1); stats->ipackets = stats1.ipackets; stats->ibytes = stats1.ibytes; stats->opackets = stats1.opackets; stats->obytes = stats1.obytes; stats->f_ipackets = 0; stats->f_ibytes = 0; stats->ierrors = stats1.ierrors + stats1.imissed + stats1.ibadcrc + stats1.ibadlen + stats1.ierrors + stats1.oerrors + stats1.imcasts + stats1.rx_nombuf + stats1.tx_pause_xon + stats1.rx_pause_xon + stats1.tx_pause_xoff+ stats1.rx_pause_xoff ; stats->oerrors = stats1.oerrors;; stats->imcasts = 0; stats->rx_nombuf = stats1.rx_nombuf; } int CTRexExtendedDriverBase40G::wait_for_stable_link(){ delay(2000); return (0); } ///////////////////////////////////////////////////////////////////// void CTRexExtendedDriverBase1GVm::update_configuration(port_cfg_t * cfg){ struct rte_eth_dev_info dev_info; rte_eth_dev_info_get((uint8_t) 0,&dev_info); cfg->m_tx_conf.tx_thresh.pthresh = TX_PTHRESH_1G; cfg->m_tx_conf.tx_thresh.hthresh = TX_HTHRESH; cfg->m_tx_conf.tx_thresh.wthresh = 0; cfg->m_tx_conf.txq_flags=dev_info.default_txconf.txq_flags; } int CTRexExtendedDriverBase1GVm::configure_rx_filter_rules(CPhyEthIF * _if){ return (0); } void CTRexExtendedDriverBase1GVm::clear_extended_stats(CPhyEthIF * _if){ rte_eth_stats_reset(_if->get_port_id()); } int CTRexExtendedDriverBase1GVm::configure_drop_queue(CPhyEthIF * _if){ return (0); } void CTRexExtendedDriverBase1GVm::get_extended_stats(CPhyEthIF * _if,CPhyEthIFStats *stats){ struct rte_eth_stats stats1; rte_eth_stats_get(_if->get_port_id(), &stats1); stats->ipackets = stats1.ipackets; stats->ibytes = stats1.ibytes; stats->opackets = stats1.opackets; stats->obytes = stats1.obytes; stats->f_ipackets = 0; stats->f_ibytes = 0; stats->ierrors = stats1.ierrors + stats1.imissed + stats1.ibadcrc + stats1.ibadlen + stats1.ierrors + stats1.oerrors + stats1.imcasts + stats1.rx_nombuf + stats1.tx_pause_xon + stats1.rx_pause_xon + stats1.tx_pause_xoff+ stats1.rx_pause_xoff ; stats->oerrors = stats1.oerrors;; stats->imcasts = 0; stats->rx_nombuf = stats1.rx_nombuf; } int CTRexExtendedDriverBase1GVm::wait_for_stable_link(){ delay(10); return (0); } /** * convert chain of mbuf to one big mbuf * * @param m * * @return */ struct rte_mbuf * rte_mbuf_convert_to_one_seg(struct rte_mbuf *m){ unsigned int len; struct rte_mbuf * r; struct rte_mbuf * old_m; old_m=m; len=rte_pktmbuf_pkt_len(m); /* allocate one big mbuf*/ r = CGlobalInfo::pktmbuf_alloc(0,len); assert(r); if (r==0) { rte_pktmbuf_free(m); return(r); } char *p=rte_pktmbuf_append(r,len); while ( m ) { len = m->data_len; assert(len); memcpy(p,(char *)m->buf_addr, len); p+=len; m = m->next; } rte_pktmbuf_free(old_m); return(r); } /*********************************************************** * platfrom API object * TODO: REMOVE THIS TO A SEPERATE FILE * **********************************************************/ void TrexDpdkPlatformApi::get_global_stats(TrexPlatformGlobalStats &stats) const { CGlobalStats trex_stats; g_trex.get_stats(trex_stats); stats.m_stats.m_cpu_util = trex_stats.m_cpu_util; stats.m_stats.m_tx_bps = trex_stats.m_tx_bps; stats.m_stats.m_tx_pps = trex_stats.m_tx_pps; stats.m_stats.m_total_tx_pkts = trex_stats.m_total_tx_pkts; stats.m_stats.m_total_tx_bytes = trex_stats.m_total_tx_bytes; stats.m_stats.m_rx_bps = trex_stats.m_rx_bps; stats.m_stats.m_rx_pps = /*trex_stats.m_rx_pps*/ 0; /* missing */ stats.m_stats.m_total_rx_pkts = trex_stats.m_total_rx_pkts; stats.m_stats.m_total_rx_bytes = trex_stats.m_total_rx_bytes; } void TrexDpdkPlatformApi::get_interface_stats(uint8_t interface_id, TrexPlatformInterfaceStats &stats) const { } uint8_t TrexDpdkPlatformApi::get_dp_core_count() const { return CGlobalInfo::m_options.preview.getCores(); } void TrexDpdkPlatformApi::port_id_to_cores(uint8_t port_id, std::vector> &cores_id_list) const { cores_id_list.clear(); /* iterate over all DP cores */ for (uint8_t core_id = 0; core_id < g_trex.get_cores_tx(); core_id++) { /* iterate over all the directions*/ for (uint8_t dir = 0 ; dir < CS_NUM; dir++) { if (g_trex.m_cores_vif[core_id + 1]->get_ports()[dir].m_port->get_port_id() == port_id) { cores_id_list.push_back(std::make_pair(core_id, dir)); } } } } void TrexDpdkPlatformApi::get_interface_info(uint8_t interface_id, std::string &driver_name, driver_speed_e &speed) const { driver_name = CTRexExtendedDriverDb::Ins()->get_driver_name(); speed = CTRexExtendedDriverDb::Ins()->get_drv()->get_driver_speed(); }