1d30ea906Sjfb8856606..  SPDX-License-Identifier: BSD-3-Clause
2d30ea906Sjfb8856606    Copyright(c) 2017 Intel Corporation.
32bfe3f2eSlogwang
42bfe3f2eSlogwangFlow Classify Sample Application
52bfe3f2eSlogwang================================
62bfe3f2eSlogwang
72bfe3f2eSlogwangThe Flow Classify sample application is based on the simple *skeleton* example
82bfe3f2eSlogwangof a forwarding application.
92bfe3f2eSlogwang
102bfe3f2eSlogwangIt is intended as a demonstration of the basic components of a DPDK forwarding
112bfe3f2eSlogwangapplication which uses the Flow Classify library API's.
122bfe3f2eSlogwang
132bfe3f2eSlogwangPlease refer to the
142bfe3f2eSlogwang:doc:`../prog_guide/flow_classify_lib`
152bfe3f2eSlogwangfor more information.
162bfe3f2eSlogwang
172bfe3f2eSlogwangCompiling the Application
182bfe3f2eSlogwang-------------------------
192bfe3f2eSlogwang
202bfe3f2eSlogwangTo compile the sample application see :doc:`compiling`.
212bfe3f2eSlogwang
222bfe3f2eSlogwangThe application is located in the ``flow_classify`` sub-directory.
232bfe3f2eSlogwang
242bfe3f2eSlogwangRunning the Application
252bfe3f2eSlogwang-----------------------
262bfe3f2eSlogwang
274418919fSjohnjiangTo run the example in a ``linux`` environment:
282bfe3f2eSlogwang
292bfe3f2eSlogwang.. code-block:: console
302bfe3f2eSlogwang
31*2d9fd380Sjfb8856606    ./<build_dir>/examples/dpdk-flow_classify -c 4 -n 4 -- /
32*2d9fd380Sjfb8856606    --rule_ipv4="../ipv4_rules_file.txt"
332bfe3f2eSlogwang
342bfe3f2eSlogwangPlease refer to the *DPDK Getting Started Guide*, section
352bfe3f2eSlogwang:doc:`../linux_gsg/build_sample_apps`
362bfe3f2eSlogwangfor general information on running applications and the Environment Abstraction
372bfe3f2eSlogwangLayer (EAL) options.
382bfe3f2eSlogwang
392bfe3f2eSlogwang
402bfe3f2eSlogwangSample ipv4_rules_file.txt
412bfe3f2eSlogwang--------------------------
422bfe3f2eSlogwang
432bfe3f2eSlogwang.. code-block:: console
442bfe3f2eSlogwang
452bfe3f2eSlogwang    #file format:
462bfe3f2eSlogwang    #src_ip/masklen dst_ip/masklen src_port : mask dst_port : mask proto/mask priority
472bfe3f2eSlogwang    #
482bfe3f2eSlogwang    2.2.2.3/24 2.2.2.7/24 32 : 0xffff 33 : 0xffff 17/0xff 0
492bfe3f2eSlogwang    9.9.9.3/24 9.9.9.7/24 32 : 0xffff 33 : 0xffff 17/0xff 1
502bfe3f2eSlogwang    9.9.9.3/24 9.9.9.7/24 32 : 0xffff 33 : 0xffff 6/0xff 2
512bfe3f2eSlogwang    9.9.8.3/24 9.9.8.7/24 32 : 0xffff 33 : 0xffff 6/0xff 3
522bfe3f2eSlogwang    6.7.8.9/24 2.3.4.5/24 32 : 0x0000 33 : 0x0000 132/0xff 4
532bfe3f2eSlogwang
542bfe3f2eSlogwangExplanation
552bfe3f2eSlogwang-----------
562bfe3f2eSlogwang
572bfe3f2eSlogwangThe following sections provide an explanation of the main components of the
582bfe3f2eSlogwangcode.
592bfe3f2eSlogwang
602bfe3f2eSlogwangAll DPDK library functions used in the sample code are prefixed with ``rte_``
612bfe3f2eSlogwangand are explained in detail in the *DPDK API Documentation*.
622bfe3f2eSlogwang
632bfe3f2eSlogwangACL field definitions for the IPv4 5 tuple rule
642bfe3f2eSlogwang~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
652bfe3f2eSlogwang
662bfe3f2eSlogwangThe following field definitions are used when creating the ACL table during
672bfe3f2eSlogwanginitialisation of the ``Flow Classify`` application..
682bfe3f2eSlogwang
692bfe3f2eSlogwang.. code-block:: c
702bfe3f2eSlogwang
712bfe3f2eSlogwang     enum {
722bfe3f2eSlogwang         PROTO_FIELD_IPV4,
732bfe3f2eSlogwang         SRC_FIELD_IPV4,
742bfe3f2eSlogwang         DST_FIELD_IPV4,
752bfe3f2eSlogwang         SRCP_FIELD_IPV4,
762bfe3f2eSlogwang         DSTP_FIELD_IPV4,
772bfe3f2eSlogwang         NUM_FIELDS_IPV4
782bfe3f2eSlogwang    };
792bfe3f2eSlogwang
802bfe3f2eSlogwang    enum {
812bfe3f2eSlogwang        PROTO_INPUT_IPV4,
822bfe3f2eSlogwang        SRC_INPUT_IPV4,
832bfe3f2eSlogwang        DST_INPUT_IPV4,
842bfe3f2eSlogwang        SRCP_DESTP_INPUT_IPV4
852bfe3f2eSlogwang    };
862bfe3f2eSlogwang
872bfe3f2eSlogwang    static struct rte_acl_field_def ipv4_defs[NUM_FIELDS_IPV4] = {
882bfe3f2eSlogwang        /* first input field - always one byte long. */
892bfe3f2eSlogwang        {
902bfe3f2eSlogwang            .type = RTE_ACL_FIELD_TYPE_BITMASK,
912bfe3f2eSlogwang            .size = sizeof(uint8_t),
922bfe3f2eSlogwang            .field_index = PROTO_FIELD_IPV4,
932bfe3f2eSlogwang            .input_index = PROTO_INPUT_IPV4,
944418919fSjohnjiang            .offset = sizeof(struct rte_ether_hdr) +
954418919fSjohnjiang                offsetof(struct rte_ipv4_hdr, next_proto_id),
962bfe3f2eSlogwang        },
972bfe3f2eSlogwang        /* next input field (IPv4 source address) - 4 consecutive bytes. */
982bfe3f2eSlogwang        {
992bfe3f2eSlogwang            /* rte_flow uses a bit mask for IPv4 addresses */
1002bfe3f2eSlogwang            .type = RTE_ACL_FIELD_TYPE_BITMASK,
1012bfe3f2eSlogwang            .size = sizeof(uint32_t),
1022bfe3f2eSlogwang            .field_index = SRC_FIELD_IPV4,
1032bfe3f2eSlogwang            .input_index = SRC_INPUT_IPV4,
1044418919fSjohnjiang            .offset = sizeof(struct rte_ether_hdr) +
1054418919fSjohnjiang                offsetof(struct rte_ipv4_hdr, src_addr),
1062bfe3f2eSlogwang        },
1072bfe3f2eSlogwang        /* next input field (IPv4 destination address) - 4 consecutive bytes. */
1082bfe3f2eSlogwang        {
1092bfe3f2eSlogwang            /* rte_flow uses a bit mask for IPv4 addresses */
1102bfe3f2eSlogwang            .type = RTE_ACL_FIELD_TYPE_BITMASK,
1112bfe3f2eSlogwang            .size = sizeof(uint32_t),
1122bfe3f2eSlogwang            .field_index = DST_FIELD_IPV4,
1132bfe3f2eSlogwang            .input_index = DST_INPUT_IPV4,
1144418919fSjohnjiang            .offset = sizeof(struct rte_ether_hdr) +
1154418919fSjohnjiang                offsetof(struct rte_ipv4_hdr, dst_addr),
1162bfe3f2eSlogwang        },
1172bfe3f2eSlogwang        /*
1182bfe3f2eSlogwang         * Next 2 fields (src & dst ports) form 4 consecutive bytes.
1192bfe3f2eSlogwang         * They share the same input index.
1202bfe3f2eSlogwang         */
1212bfe3f2eSlogwang	{
1222bfe3f2eSlogwang            /* rte_flow uses a bit mask for protocol ports */
1232bfe3f2eSlogwang            .type = RTE_ACL_FIELD_TYPE_BITMASK,
1242bfe3f2eSlogwang            .size = sizeof(uint16_t),
1252bfe3f2eSlogwang            .field_index = SRCP_FIELD_IPV4,
1262bfe3f2eSlogwang            .input_index = SRCP_DESTP_INPUT_IPV4,
1274418919fSjohnjiang            .offset = sizeof(struct rte_ether_hdr) +
1284418919fSjohnjiang                sizeof(struct rte_ipv4_hdr) +
1294418919fSjohnjiang                offsetof(struct rte_tcp_hdr, src_port),
1302bfe3f2eSlogwang        },
1312bfe3f2eSlogwang        {
1322bfe3f2eSlogwang             /* rte_flow uses a bit mask for protocol ports */
1332bfe3f2eSlogwang             .type = RTE_ACL_FIELD_TYPE_BITMASK,
1342bfe3f2eSlogwang             .size = sizeof(uint16_t),
1352bfe3f2eSlogwang             .field_index = DSTP_FIELD_IPV4,
1362bfe3f2eSlogwang             .input_index = SRCP_DESTP_INPUT_IPV4,
1374418919fSjohnjiang             .offset = sizeof(struct rte_ether_hdr) +
1384418919fSjohnjiang                 sizeof(struct rte_ipv4_hdr) +
1394418919fSjohnjiang                 offsetof(struct rte_tcp_hdr, dst_port),
1402bfe3f2eSlogwang        },
1412bfe3f2eSlogwang    };
1422bfe3f2eSlogwang
1432bfe3f2eSlogwangThe Main Function
1442bfe3f2eSlogwang~~~~~~~~~~~~~~~~~
1452bfe3f2eSlogwang
1462bfe3f2eSlogwangThe ``main()`` function performs the initialization and calls the execution
1472bfe3f2eSlogwangthreads for each lcore.
1482bfe3f2eSlogwang
1492bfe3f2eSlogwangThe first task is to initialize the Environment Abstraction Layer (EAL).
1502bfe3f2eSlogwangThe ``argc`` and ``argv`` arguments are provided to the ``rte_eal_init()``
1512bfe3f2eSlogwangfunction. The value returned is the number of parsed arguments:
1522bfe3f2eSlogwang
1532bfe3f2eSlogwang.. code-block:: c
1542bfe3f2eSlogwang
1552bfe3f2eSlogwang    int ret = rte_eal_init(argc, argv);
1562bfe3f2eSlogwang    if (ret < 0)
1572bfe3f2eSlogwang        rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
1582bfe3f2eSlogwang
1592bfe3f2eSlogwangIt then parses the flow_classify application arguments
1602bfe3f2eSlogwang
1612bfe3f2eSlogwang.. code-block:: c
1622bfe3f2eSlogwang
1632bfe3f2eSlogwang    ret = parse_args(argc, argv);
1642bfe3f2eSlogwang    if (ret < 0)
1652bfe3f2eSlogwang        rte_exit(EXIT_FAILURE, "Invalid flow_classify parameters\n");
1662bfe3f2eSlogwang
1672bfe3f2eSlogwangThe ``main()`` function also allocates a mempool to hold the mbufs
1682bfe3f2eSlogwang(Message Buffers) used by the application:
1692bfe3f2eSlogwang
1702bfe3f2eSlogwang.. code-block:: c
1712bfe3f2eSlogwang
1722bfe3f2eSlogwang    mbuf_pool = rte_mempool_create("MBUF_POOL",
1732bfe3f2eSlogwang                                   NUM_MBUFS * nb_ports,
1742bfe3f2eSlogwang                                   MBUF_SIZE,
1752bfe3f2eSlogwang                                   MBUF_CACHE_SIZE,
1762bfe3f2eSlogwang                                   sizeof(struct rte_pktmbuf_pool_private),
1772bfe3f2eSlogwang                                   rte_pktmbuf_pool_init, NULL,
1782bfe3f2eSlogwang                                   rte_pktmbuf_init, NULL,
1792bfe3f2eSlogwang                                   rte_socket_id(),
1802bfe3f2eSlogwang                                   0);
1812bfe3f2eSlogwang
1822bfe3f2eSlogwangmbufs are the packet buffer structure used by DPDK. They are explained in
1832bfe3f2eSlogwangdetail in the "Mbuf Library" section of the *DPDK Programmer's Guide*.
1842bfe3f2eSlogwang
1852bfe3f2eSlogwangThe ``main()`` function also initializes all the ports using the user defined
1862bfe3f2eSlogwang``port_init()`` function which is explained in the next section:
1872bfe3f2eSlogwang
1882bfe3f2eSlogwang.. code-block:: c
1892bfe3f2eSlogwang
190d30ea906Sjfb8856606    RTE_ETH_FOREACH_DEV(portid) {
1912bfe3f2eSlogwang        if (port_init(portid, mbuf_pool) != 0) {
1922bfe3f2eSlogwang            rte_exit(EXIT_FAILURE,
1932bfe3f2eSlogwang                     "Cannot init port %" PRIu8 "\n", portid);
1942bfe3f2eSlogwang        }
1952bfe3f2eSlogwang    }
1962bfe3f2eSlogwang
1972bfe3f2eSlogwangThe ``main()`` function creates the ``flow classifier object`` and adds an ``ACL
1982bfe3f2eSlogwangtable`` to the flow classifier.
1992bfe3f2eSlogwang
2002bfe3f2eSlogwang.. code-block:: c
2012bfe3f2eSlogwang
2022bfe3f2eSlogwang    struct flow_classifier {
2032bfe3f2eSlogwang        struct rte_flow_classifier *cls;
2042bfe3f2eSlogwang    };
2052bfe3f2eSlogwang
2062bfe3f2eSlogwang    struct flow_classifier_acl {
2072bfe3f2eSlogwang        struct flow_classifier cls;
2082bfe3f2eSlogwang    } __rte_cache_aligned;
2092bfe3f2eSlogwang
2102bfe3f2eSlogwang    /* Memory allocation */
2112bfe3f2eSlogwang    size = RTE_CACHE_LINE_ROUNDUP(sizeof(struct flow_classifier_acl));
2122bfe3f2eSlogwang    cls_app = rte_zmalloc(NULL, size, RTE_CACHE_LINE_SIZE);
2132bfe3f2eSlogwang    if (cls_app == NULL)
2142bfe3f2eSlogwang        rte_exit(EXIT_FAILURE, "Cannot allocate classifier memory\n");
2152bfe3f2eSlogwang
2162bfe3f2eSlogwang    cls_params.name = "flow_classifier";
2172bfe3f2eSlogwang    cls_params.socket_id = socket_id;
2182bfe3f2eSlogwang
2192bfe3f2eSlogwang    cls_app->cls = rte_flow_classifier_create(&cls_params);
2202bfe3f2eSlogwang    if (cls_app->cls == NULL) {
2212bfe3f2eSlogwang        rte_free(cls_app);
2222bfe3f2eSlogwang        rte_exit(EXIT_FAILURE, "Cannot create classifier\n");
2232bfe3f2eSlogwang    }
2242bfe3f2eSlogwang
2252bfe3f2eSlogwang    /* initialise ACL table params */
2262bfe3f2eSlogwang    table_acl_params.name = "table_acl_ipv4_5tuple";
2272bfe3f2eSlogwang    table_acl_params.n_rule_fields = RTE_DIM(ipv4_defs);
2282bfe3f2eSlogwang    table_acl_params.n_rules = FLOW_CLASSIFY_MAX_RULE_NUM;
2292bfe3f2eSlogwang    memcpy(table_acl_params.field_format, ipv4_defs, sizeof(ipv4_defs));
2302bfe3f2eSlogwang
2312bfe3f2eSlogwang    /* initialise table create params */
2322bfe3f2eSlogwang    cls_table_params.ops = &rte_table_acl_ops,
2332bfe3f2eSlogwang    cls_table_params.arg_create = &table_acl_params,
234d30ea906Sjfb8856606    cls_table_params.type = RTE_FLOW_CLASSIFY_TABLE_ACL_IP4_5TUPLE;
2352bfe3f2eSlogwang
236d30ea906Sjfb8856606    ret = rte_flow_classify_table_create(cls_app->cls, &cls_table_params);
2372bfe3f2eSlogwang    if (ret) {
2382bfe3f2eSlogwang        rte_flow_classifier_free(cls_app->cls);
2392bfe3f2eSlogwang        rte_free(cls);
2402bfe3f2eSlogwang        rte_exit(EXIT_FAILURE, "Failed to create classifier table\n");
2412bfe3f2eSlogwang    }
2422bfe3f2eSlogwang
2432bfe3f2eSlogwangIt then reads the ipv4_rules_file.txt file and initialises the parameters for
2442bfe3f2eSlogwangthe ``rte_flow_classify_table_entry_add`` API.
2452bfe3f2eSlogwangThis API adds a rule to the ACL table.
2462bfe3f2eSlogwang
2472bfe3f2eSlogwang.. code-block:: c
2482bfe3f2eSlogwang
2492bfe3f2eSlogwang    if (add_rules(parm_config.rule_ipv4_name)) {
2502bfe3f2eSlogwang        rte_flow_classifier_free(cls_app->cls);
2512bfe3f2eSlogwang        rte_free(cls_app);
2522bfe3f2eSlogwang        rte_exit(EXIT_FAILURE, "Failed to add rules\n");
2532bfe3f2eSlogwang    }
2542bfe3f2eSlogwang
2552bfe3f2eSlogwangOnce the initialization is complete, the application is ready to launch a
2562bfe3f2eSlogwangfunction on an lcore. In this example ``lcore_main()`` is called on a single
2572bfe3f2eSlogwanglcore.
2582bfe3f2eSlogwang
2592bfe3f2eSlogwang.. code-block:: c
2602bfe3f2eSlogwang
2612bfe3f2eSlogwang    lcore_main(cls_app);
2622bfe3f2eSlogwang
2632bfe3f2eSlogwangThe ``lcore_main()`` function is explained below.
2642bfe3f2eSlogwang
2652bfe3f2eSlogwangThe Port Initialization  Function
2662bfe3f2eSlogwang~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2672bfe3f2eSlogwang
2682bfe3f2eSlogwangThe main functional part of the port initialization used in the Basic
2692bfe3f2eSlogwangForwarding application is shown below:
2702bfe3f2eSlogwang
2712bfe3f2eSlogwang.. code-block:: c
2722bfe3f2eSlogwang
2732bfe3f2eSlogwang    static inline int
2740c6bd470Sfengbojiang    port_init(uint16_t port, struct rte_mempool *mbuf_pool)
2752bfe3f2eSlogwang    {
2762bfe3f2eSlogwang        struct rte_eth_conf port_conf = port_conf_default;
2772bfe3f2eSlogwang        const uint16_t rx_rings = 1, tx_rings = 1;
2784418919fSjohnjiang        struct rte_ether_addr addr;
2792bfe3f2eSlogwang        int retval;
2802bfe3f2eSlogwang        uint16_t q;
2812bfe3f2eSlogwang
2822bfe3f2eSlogwang        /* Configure the Ethernet device. */
2832bfe3f2eSlogwang        retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf);
2842bfe3f2eSlogwang        if (retval != 0)
2852bfe3f2eSlogwang            return retval;
2862bfe3f2eSlogwang
2872bfe3f2eSlogwang        /* Allocate and set up 1 RX queue per Ethernet port. */
2882bfe3f2eSlogwang        for (q = 0; q < rx_rings; q++) {
2892bfe3f2eSlogwang            retval = rte_eth_rx_queue_setup(port, q, RX_RING_SIZE,
2902bfe3f2eSlogwang                    rte_eth_dev_socket_id(port), NULL, mbuf_pool);
2912bfe3f2eSlogwang            if (retval < 0)
2922bfe3f2eSlogwang                return retval;
2932bfe3f2eSlogwang        }
2942bfe3f2eSlogwang
2952bfe3f2eSlogwang        /* Allocate and set up 1 TX queue per Ethernet port. */
2962bfe3f2eSlogwang        for (q = 0; q < tx_rings; q++) {
2972bfe3f2eSlogwang            retval = rte_eth_tx_queue_setup(port, q, TX_RING_SIZE,
2982bfe3f2eSlogwang                    rte_eth_dev_socket_id(port), NULL);
2992bfe3f2eSlogwang            if (retval < 0)
3002bfe3f2eSlogwang                return retval;
3012bfe3f2eSlogwang        }
3022bfe3f2eSlogwang
3032bfe3f2eSlogwang        /* Start the Ethernet port. */
3042bfe3f2eSlogwang        retval = rte_eth_dev_start(port);
3052bfe3f2eSlogwang        if (retval < 0)
3062bfe3f2eSlogwang            return retval;
3072bfe3f2eSlogwang
3082bfe3f2eSlogwang        /* Display the port MAC address. */
3094418919fSjohnjiang        retval = rte_eth_macaddr_get(port, &addr);
3104418919fSjohnjiang        if (retval < 0)
3114418919fSjohnjiang            return retval;
3122bfe3f2eSlogwang        printf("Port %u MAC: %02" PRIx8 " %02" PRIx8 " %02" PRIx8
3132bfe3f2eSlogwang               " %02" PRIx8 " %02" PRIx8 " %02" PRIx8 "\n",
3142bfe3f2eSlogwang               port,
3152bfe3f2eSlogwang               addr.addr_bytes[0], addr.addr_bytes[1],
3162bfe3f2eSlogwang               addr.addr_bytes[2], addr.addr_bytes[3],
3172bfe3f2eSlogwang               addr.addr_bytes[4], addr.addr_bytes[5]);
3182bfe3f2eSlogwang
3192bfe3f2eSlogwang        /* Enable RX in promiscuous mode for the Ethernet device. */
3204418919fSjohnjiang        retval = rte_eth_promiscuous_enable(port);
3214418919fSjohnjiang        if (retval != 0)
3224418919fSjohnjiang                return retval;
3232bfe3f2eSlogwang
3242bfe3f2eSlogwang        return 0;
3252bfe3f2eSlogwang    }
3262bfe3f2eSlogwang
3272bfe3f2eSlogwangThe Ethernet ports are configured with default settings using the
3282bfe3f2eSlogwang``rte_eth_dev_configure()`` function and the ``port_conf_default`` struct.
3292bfe3f2eSlogwang
3302bfe3f2eSlogwang.. code-block:: c
3312bfe3f2eSlogwang
3322bfe3f2eSlogwang    static const struct rte_eth_conf port_conf_default = {
3334418919fSjohnjiang        .rxmode = { .max_rx_pkt_len = RTE_ETHER_MAX_LEN }
3342bfe3f2eSlogwang    };
3352bfe3f2eSlogwang
3362bfe3f2eSlogwangFor this example the ports are set up with 1 RX and 1 TX queue using the
3372bfe3f2eSlogwang``rte_eth_rx_queue_setup()`` and ``rte_eth_tx_queue_setup()`` functions.
3382bfe3f2eSlogwang
3392bfe3f2eSlogwangThe Ethernet port is then started:
3402bfe3f2eSlogwang
3412bfe3f2eSlogwang.. code-block:: c
3422bfe3f2eSlogwang
3432bfe3f2eSlogwang    retval  = rte_eth_dev_start(port);
3442bfe3f2eSlogwang
3452bfe3f2eSlogwang
3462bfe3f2eSlogwangFinally the RX port is set in promiscuous mode:
3472bfe3f2eSlogwang
3482bfe3f2eSlogwang.. code-block:: c
3492bfe3f2eSlogwang
3504418919fSjohnjiang    retval = rte_eth_promiscuous_enable(port);
3512bfe3f2eSlogwang
3522bfe3f2eSlogwangThe Add Rules function
3532bfe3f2eSlogwang~~~~~~~~~~~~~~~~~~~~~~
3542bfe3f2eSlogwang
3552bfe3f2eSlogwangThe ``add_rules`` function reads the ``ipv4_rules_file.txt`` file and calls the
3562bfe3f2eSlogwang``add_classify_rule`` function which calls the
3572bfe3f2eSlogwang``rte_flow_classify_table_entry_add`` API.
3582bfe3f2eSlogwang
3592bfe3f2eSlogwang.. code-block:: c
3602bfe3f2eSlogwang
3612bfe3f2eSlogwang    static int
3622bfe3f2eSlogwang    add_rules(const char *rule_path)
3632bfe3f2eSlogwang    {
3642bfe3f2eSlogwang        FILE *fh;
3652bfe3f2eSlogwang        char buff[LINE_MAX];
3662bfe3f2eSlogwang        unsigned int i = 0;
3672bfe3f2eSlogwang        unsigned int total_num = 0;
3682bfe3f2eSlogwang        struct rte_eth_ntuple_filter ntuple_filter;
3692bfe3f2eSlogwang
3702bfe3f2eSlogwang        fh = fopen(rule_path, "rb");
3712bfe3f2eSlogwang        if (fh == NULL)
3722bfe3f2eSlogwang            rte_exit(EXIT_FAILURE, "%s: Open %s failed\n", __func__,
3732bfe3f2eSlogwang                     rule_path);
3742bfe3f2eSlogwang
3752bfe3f2eSlogwang        fseek(fh, 0, SEEK_SET);
3762bfe3f2eSlogwang
3772bfe3f2eSlogwang        i = 0;
3782bfe3f2eSlogwang        while (fgets(buff, LINE_MAX, fh) != NULL) {
3792bfe3f2eSlogwang            i++;
3802bfe3f2eSlogwang
3812bfe3f2eSlogwang            if (is_bypass_line(buff))
3822bfe3f2eSlogwang                continue;
3832bfe3f2eSlogwang
3842bfe3f2eSlogwang            if (total_num >= FLOW_CLASSIFY_MAX_RULE_NUM - 1) {
3852bfe3f2eSlogwang                printf("\nINFO: classify rule capacity %d reached\n",
3862bfe3f2eSlogwang                       total_num);
3872bfe3f2eSlogwang                break;
3882bfe3f2eSlogwang            }
3892bfe3f2eSlogwang
3902bfe3f2eSlogwang            if (parse_ipv4_5tuple_rule(buff, &ntuple_filter) != 0)
3912bfe3f2eSlogwang                rte_exit(EXIT_FAILURE,
3922bfe3f2eSlogwang                         "%s Line %u: parse rules error\n",
3932bfe3f2eSlogwang                         rule_path, i);
3942bfe3f2eSlogwang
3952bfe3f2eSlogwang            if (add_classify_rule(&ntuple_filter) != 0)
3962bfe3f2eSlogwang                rte_exit(EXIT_FAILURE, "add rule error\n");
3972bfe3f2eSlogwang
3982bfe3f2eSlogwang            total_num++;
3992bfe3f2eSlogwang	}
4002bfe3f2eSlogwang
4012bfe3f2eSlogwang	fclose(fh);
4022bfe3f2eSlogwang	return 0;
4032bfe3f2eSlogwang    }
4042bfe3f2eSlogwang
4052bfe3f2eSlogwang
4062bfe3f2eSlogwangThe Lcore Main function
4072bfe3f2eSlogwang~~~~~~~~~~~~~~~~~~~~~~~
4082bfe3f2eSlogwang
4092bfe3f2eSlogwangAs we saw above the ``main()`` function calls an application function on the
4102bfe3f2eSlogwangavailable lcores.
4112bfe3f2eSlogwangThe ``lcore_main`` function calls the ``rte_flow_classifier_query`` API.
4122bfe3f2eSlogwangFor the Basic Forwarding application the ``lcore_main`` function looks like the
4132bfe3f2eSlogwangfollowing:
4142bfe3f2eSlogwang
4152bfe3f2eSlogwang.. code-block:: c
4162bfe3f2eSlogwang
4172bfe3f2eSlogwang    /* flow classify data */
4182bfe3f2eSlogwang    static int num_classify_rules;
4192bfe3f2eSlogwang    static struct rte_flow_classify_rule *rules[MAX_NUM_CLASSIFY];
4202bfe3f2eSlogwang    static struct rte_flow_classify_ipv4_5tuple_stats ntuple_stats;
4212bfe3f2eSlogwang    static struct rte_flow_classify_stats classify_stats = {
4222bfe3f2eSlogwang            .stats = (void *)&ntuple_stats
4232bfe3f2eSlogwang    };
4242bfe3f2eSlogwang
425*2d9fd380Sjfb8856606    static __rte_noreturn void
4262bfe3f2eSlogwang    lcore_main(cls_app)
4272bfe3f2eSlogwang    {
428d30ea906Sjfb8856606        uint16_t port;
4292bfe3f2eSlogwang
4302bfe3f2eSlogwang        /*
4312bfe3f2eSlogwang         * Check that the port is on the same NUMA node as the polling thread
4322bfe3f2eSlogwang         * for best performance.
4332bfe3f2eSlogwang         */
434d30ea906Sjfb8856606        RTE_ETH_FOREACH_DEV(port)
4352bfe3f2eSlogwang            if (rte_eth_dev_socket_id(port) > 0 &&
4362bfe3f2eSlogwang                rte_eth_dev_socket_id(port) != (int)rte_socket_id()) {
4372bfe3f2eSlogwang                printf("\n\n");
4382bfe3f2eSlogwang                printf("WARNING: port %u is on remote NUMA node\n",
4392bfe3f2eSlogwang                       port);
4402bfe3f2eSlogwang                printf("to polling thread.\n");
4412bfe3f2eSlogwang                printf("Performance will not be optimal.\n");
4422bfe3f2eSlogwang
4432bfe3f2eSlogwang                printf("\nCore %u forwarding packets. \n",
4442bfe3f2eSlogwang                       rte_lcore_id());
4452bfe3f2eSlogwang                printf("[Ctrl+C to quit]\n
4462bfe3f2eSlogwang            }
4472bfe3f2eSlogwang
4482bfe3f2eSlogwang        /* Run until the application is quit or killed. */
4492bfe3f2eSlogwang        for (;;) {
4502bfe3f2eSlogwang            /*
4512bfe3f2eSlogwang             * Receive packets on a port and forward them on the paired
4522bfe3f2eSlogwang             * port. The mapping is 0 -> 1, 1 -> 0, 2 -> 3, 3 -> 2, etc.
4532bfe3f2eSlogwang             */
454d30ea906Sjfb8856606            RTE_ETH_FOREACH_DEV(port) {
4552bfe3f2eSlogwang
4562bfe3f2eSlogwang                /* Get burst of RX packets, from first port of pair. */
4572bfe3f2eSlogwang                struct rte_mbuf *bufs[BURST_SIZE];
4582bfe3f2eSlogwang                const uint16_t nb_rx = rte_eth_rx_burst(port, 0,
4592bfe3f2eSlogwang                        bufs, BURST_SIZE);
4602bfe3f2eSlogwang
4612bfe3f2eSlogwang                if (unlikely(nb_rx == 0))
4622bfe3f2eSlogwang                    continue;
4632bfe3f2eSlogwang
4642bfe3f2eSlogwang                for (i = 0; i < MAX_NUM_CLASSIFY; i++) {
4652bfe3f2eSlogwang                    if (rules[i]) {
4662bfe3f2eSlogwang                        ret = rte_flow_classifier_query(
4672bfe3f2eSlogwang                            cls_app->cls,
4682bfe3f2eSlogwang                            bufs, nb_rx, rules[i],
4692bfe3f2eSlogwang                            &classify_stats);
4702bfe3f2eSlogwang                        if (ret)
4712bfe3f2eSlogwang                            printf(
4722bfe3f2eSlogwang                                "rule [%d] query failed ret [%d]\n\n",
4732bfe3f2eSlogwang                                i, ret);
4742bfe3f2eSlogwang                        else {
4752bfe3f2eSlogwang                            printf(
4762bfe3f2eSlogwang                                "rule[%d] count=%"PRIu64"\n",
4772bfe3f2eSlogwang                                i, ntuple_stats.counter1);
4782bfe3f2eSlogwang
4792bfe3f2eSlogwang                            printf("proto = %d\n",
4802bfe3f2eSlogwang                                ntuple_stats.ipv4_5tuple.proto);
4812bfe3f2eSlogwang                        }
4822bfe3f2eSlogwang                     }
4832bfe3f2eSlogwang                 }
4842bfe3f2eSlogwang
4852bfe3f2eSlogwang                /* Send burst of TX packets, to second port of pair. */
4862bfe3f2eSlogwang                const uint16_t nb_tx = rte_eth_tx_burst(port ^ 1, 0,
4872bfe3f2eSlogwang                        bufs, nb_rx);
4882bfe3f2eSlogwang
4892bfe3f2eSlogwang                /* Free any unsent packets. */
4902bfe3f2eSlogwang                if (unlikely(nb_tx < nb_rx)) {
4912bfe3f2eSlogwang                    uint16_t buf;
4922bfe3f2eSlogwang                    for (buf = nb_tx; buf < nb_rx; buf++)
4932bfe3f2eSlogwang                        rte_pktmbuf_free(bufs[buf]);
4942bfe3f2eSlogwang                }
4952bfe3f2eSlogwang            }
4962bfe3f2eSlogwang        }
4972bfe3f2eSlogwang    }
4982bfe3f2eSlogwang
4992bfe3f2eSlogwangThe main work of the application is done within the loop:
5002bfe3f2eSlogwang
5012bfe3f2eSlogwang.. code-block:: c
5022bfe3f2eSlogwang
5032bfe3f2eSlogwang        for (;;) {
504d30ea906Sjfb8856606            RTE_ETH_FOREACH_DEV(port) {
5052bfe3f2eSlogwang
5062bfe3f2eSlogwang                /* Get burst of RX packets, from first port of pair. */
5072bfe3f2eSlogwang                struct rte_mbuf *bufs[BURST_SIZE];
5082bfe3f2eSlogwang                const uint16_t nb_rx = rte_eth_rx_burst(port, 0,
5092bfe3f2eSlogwang                        bufs, BURST_SIZE);
5102bfe3f2eSlogwang
5112bfe3f2eSlogwang                if (unlikely(nb_rx == 0))
5122bfe3f2eSlogwang                    continue;
5132bfe3f2eSlogwang
5142bfe3f2eSlogwang                /* Send burst of TX packets, to second port of pair. */
5152bfe3f2eSlogwang                const uint16_t nb_tx = rte_eth_tx_burst(port ^ 1, 0,
5162bfe3f2eSlogwang                        bufs, nb_rx);
5172bfe3f2eSlogwang
5182bfe3f2eSlogwang                /* Free any unsent packets. */
5192bfe3f2eSlogwang                if (unlikely(nb_tx < nb_rx)) {
5202bfe3f2eSlogwang                    uint16_t buf;
5212bfe3f2eSlogwang                    for (buf = nb_tx; buf < nb_rx; buf++)
5222bfe3f2eSlogwang                        rte_pktmbuf_free(bufs[buf]);
5232bfe3f2eSlogwang                }
5242bfe3f2eSlogwang            }
5252bfe3f2eSlogwang        }
5262bfe3f2eSlogwang
5272bfe3f2eSlogwangPackets are received in bursts on the RX ports and transmitted in bursts on
5282bfe3f2eSlogwangthe TX ports. The ports are grouped in pairs with a simple mapping scheme
5292bfe3f2eSlogwangusing the an XOR on the port number::
5302bfe3f2eSlogwang
5312bfe3f2eSlogwang    0 -> 1
5322bfe3f2eSlogwang    1 -> 0
5332bfe3f2eSlogwang
5342bfe3f2eSlogwang    2 -> 3
5352bfe3f2eSlogwang    3 -> 2
5362bfe3f2eSlogwang
5372bfe3f2eSlogwang    etc.
5382bfe3f2eSlogwang
5392bfe3f2eSlogwangThe ``rte_eth_tx_burst()`` function frees the memory buffers of packets that
5402bfe3f2eSlogwangare transmitted. If packets fail to transmit, ``(nb_tx < nb_rx)``, then they
5412bfe3f2eSlogwangmust be freed explicitly using ``rte_pktmbuf_free()``.
5422bfe3f2eSlogwang
5432bfe3f2eSlogwangThe forwarding loop can be interrupted and the application closed using
5442bfe3f2eSlogwang``Ctrl-C``.
545