15630257fSFerruh Yigit.. SPDX-License-Identifier: BSD-3-Clause 25630257fSFerruh Yigit Copyright(c) 2010-2014 Intel Corporation. 3d0dff9baSBernard Iremonger 4d0dff9baSBernard IremongerL3 Forwarding Sample Application 5d0dff9baSBernard Iremonger================================ 6d0dff9baSBernard Iremonger 7e0c7c473SSiobhan ButlerThe L3 Forwarding application is a simple example of packet processing using the DPDK. 8d0dff9baSBernard IremongerThe application performs L3 forwarding. 9d0dff9baSBernard Iremonger 10d0dff9baSBernard IremongerOverview 11d0dff9baSBernard Iremonger-------- 12d0dff9baSBernard Iremonger 13e0c7c473SSiobhan ButlerThe application demonstrates the use of the hash and LPM libraries in the DPDK to implement packet forwarding. 14513b0723SMauricio Vasquez BThe initialization and run-time paths are very similar to those of the :doc:`l2_forward_real_virtual`. 15d0dff9baSBernard IremongerThe main difference from the L2 Forwarding sample application is that the forwarding decision 16d0dff9baSBernard Iremongeris made based on information read from the input packet. 17d0dff9baSBernard Iremonger 189a998f2dSReshma PattanThe lookup method is either hash-based or LPM-based and is selected at run time. When the selected lookup method is hash-based, 19d0dff9baSBernard Iremongera hash object is used to emulate the flow classification stage. 20d0dff9baSBernard IremongerThe hash object is used in correlation with a flow table to map each input packet to its flow at runtime. 21d0dff9baSBernard Iremonger 22d0dff9baSBernard IremongerThe hash lookup key is represented by a DiffServ 5-tuple composed of the following fields read from the input packet: 23d0dff9baSBernard IremongerSource IP Address, Destination IP Address, Protocol, Source Port and Destination Port. 24d0dff9baSBernard IremongerThe ID of the output interface for the input packet is read from the identified flow table entry. 25d0dff9baSBernard IremongerThe set of flows used by the application is statically configured and loaded into the hash at initialization time. 26d0dff9baSBernard IremongerWhen the selected lookup method is LPM based, an LPM object is used to emulate the forwarding stage for IPv4 packets. 27d0dff9baSBernard IremongerThe LPM object is used as the routing table to identify the next hop for each input packet at runtime. 28d0dff9baSBernard Iremonger 29d0dff9baSBernard IremongerThe LPM lookup key is represented by the Destination IP Address field read from the input packet. 30d0dff9baSBernard IremongerThe ID of the output interface for the input packet is the next hop returned by the LPM lookup. 31d0dff9baSBernard IremongerThe set of LPM rules used by the application is statically configured and loaded into the LPM object at initialization time. 32d0dff9baSBernard Iremonger 33d0dff9baSBernard IremongerIn the sample application, hash-based forwarding supports IPv4 and IPv6. LPM-based forwarding supports IPv4 only. 34d0dff9baSBernard Iremonger 35d0dff9baSBernard IremongerCompiling the Application 36d0dff9baSBernard Iremonger------------------------- 37d0dff9baSBernard Iremonger 387cacb056SHerakliusz LipiecTo compile the sample application see :doc:`compiling`. 39d0dff9baSBernard Iremonger 407cacb056SHerakliusz LipiecThe application is located in the ``l3fwd`` sub-directory. 41d0dff9baSBernard Iremonger 42d0dff9baSBernard IremongerRunning the Application 43d0dff9baSBernard Iremonger----------------------- 44d0dff9baSBernard Iremonger 4554659744SBeilei XingThe application has a number of command line options:: 46d0dff9baSBernard Iremonger 4754659744SBeilei Xing ./l3fwd [EAL options] -- -p PORTMASK 4854659744SBeilei Xing [-P] 4954659744SBeilei Xing [-E] 5054659744SBeilei Xing [-L] 5154659744SBeilei Xing --config(port,queue,lcore)[,(port,queue,lcore)] 5254659744SBeilei Xing [--eth-dest=X,MM:MM:MM:MM:MM:MM] 5354659744SBeilei Xing [--enable-jumbo [--max-pkt-len PKTLEN]] 5454659744SBeilei Xing [--no-numa] 5554659744SBeilei Xing [--hash-entry-num] 5654659744SBeilei Xing [--ipv6] 5754659744SBeilei Xing [--parse-ptype] 58f0a26885SShreyansh Jain [--per-port-pool] 59d0dff9baSBernard Iremonger 6054659744SBeilei XingWhere, 61d0dff9baSBernard Iremonger 6254659744SBeilei Xing* ``-p PORTMASK:`` Hexadecimal bitmask of ports to configure 63d0dff9baSBernard Iremonger 6454659744SBeilei Xing* ``-P:`` Optional, sets all ports to promiscuous mode so that packets are accepted regardless of the packet's Ethernet MAC destination address. 65d0dff9baSBernard Iremonger Without this option, only packets with the Ethernet MAC destination address set to the Ethernet address of the port are accepted. 66d0dff9baSBernard Iremonger 6754659744SBeilei Xing* ``-E:`` Optional, enable exact match. 68d0dff9baSBernard Iremonger 6954659744SBeilei Xing* ``-L:`` Optional, enable longest prefix match. 70d0dff9baSBernard Iremonger 7154659744SBeilei Xing* ``--config (port,queue,lcore)[,(port,queue,lcore)]:`` Determines which queues from which ports are mapped to which cores. 72d0dff9baSBernard Iremonger 7354659744SBeilei Xing* ``--eth-dest=X,MM:MM:MM:MM:MM:MM:`` Optional, ethernet destination for port X. 74d0dff9baSBernard Iremonger 7554659744SBeilei Xing* ``--enable-jumbo:`` Optional, enables jumbo frames. 76d0dff9baSBernard Iremonger 7754659744SBeilei Xing* ``--max-pkt-len:`` Optional, under the premise of enabling jumbo, maximum packet length in decimal (64-9600). 78d0dff9baSBernard Iremonger 7954659744SBeilei Xing* ``--no-numa:`` Optional, disables numa awareness. 8054659744SBeilei Xing 8154659744SBeilei Xing* ``--hash-entry-num:`` Optional, specifies the hash entry number in hexadecimal to be setup. 8254659744SBeilei Xing 8354659744SBeilei Xing* ``--ipv6:`` Optional, set if running ipv6 packets. 8454659744SBeilei Xing 8554659744SBeilei Xing* ``--parse-ptype:`` Optional, set to use software to analyze packet type. Without this option, hardware will check the packet type. 8671a7e242SJianfeng Tan 87f0a26885SShreyansh Jain* ``--per-port-pool:`` Optional, set to use independent buffer pools per port. Without this option, single buffer pool is used for all ports. 88f0a26885SShreyansh Jain 89e0ef2aecSPablo de LaraFor example, consider a dual processor socket platform with 8 physical cores, where cores 0-7 and 16-23 appear on socket 0, 90e0ef2aecSPablo de Larawhile cores 8-15 and 24-31 appear on socket 1. 91d0dff9baSBernard Iremonger 92e0ef2aecSPablo de LaraTo enable L3 forwarding between two ports, assuming that both ports are in the same socket, using two cores, cores 1 and 2, 93e0ef2aecSPablo de Lara(which are in the same socket too), use the following command: 94d0dff9baSBernard Iremonger 95d0dff9baSBernard Iremonger.. code-block:: console 96d0dff9baSBernard Iremonger 97e0ef2aecSPablo de Lara ./build/l3fwd -l 1,2 -n 4 -- -p 0x3 --config="(0,0,1),(1,0,2)" 98d0dff9baSBernard Iremonger 99d0dff9baSBernard IremongerIn this command: 100d0dff9baSBernard Iremonger 101e0ef2aecSPablo de Lara* The -l option enables cores 1, 2 102d0dff9baSBernard Iremonger 103d0dff9baSBernard Iremonger* The -p option enables ports 0 and 1 104d0dff9baSBernard Iremonger 105e0ef2aecSPablo de Lara* The --config option enables one queue on each port and maps each (port,queue) pair to a specific core. 106d0dff9baSBernard Iremonger The following table shows the mapping in this example: 107d0dff9baSBernard Iremonger 108d0dff9baSBernard Iremonger+----------+-----------+-----------+-------------------------------------+ 109d0dff9baSBernard Iremonger| **Port** | **Queue** | **lcore** | **Description** | 110d0dff9baSBernard Iremonger| | | | | 111d0dff9baSBernard Iremonger+----------+-----------+-----------+-------------------------------------+ 112e0ef2aecSPablo de Lara| 0 | 0 | 1 | Map queue 0 from port 0 to lcore 1. | 113d0dff9baSBernard Iremonger| | | | | 114d0dff9baSBernard Iremonger+----------+-----------+-----------+-------------------------------------+ 115e0ef2aecSPablo de Lara| 1 | 0 | 2 | Map queue 0 from port 1 to lcore 2. | 116d0dff9baSBernard Iremonger| | | | | 117d0dff9baSBernard Iremonger+----------+-----------+-----------+-------------------------------------+ 118d0dff9baSBernard Iremonger 119e0c7c473SSiobhan ButlerRefer to the *DPDK Getting Started Guide* for general information on running applications and 120d0dff9baSBernard Iremongerthe Environment Abstraction Layer (EAL) options. 121d0dff9baSBernard Iremonger 122513b0723SMauricio Vasquez B.. _l3_fwd_explanation: 123513b0723SMauricio Vasquez B 124d0dff9baSBernard IremongerExplanation 125d0dff9baSBernard Iremonger----------- 126d0dff9baSBernard Iremonger 127d0dff9baSBernard IremongerThe following sections provide some explanation of the sample application code. As mentioned in the overview section, 128513b0723SMauricio Vasquez Bthe initialization and run-time paths are very similar to those of the :doc:`l2_forward_real_virtual`. 129d0dff9baSBernard IremongerThe following sections describe aspects that are specific to the L3 Forwarding sample application. 130d0dff9baSBernard Iremonger 131d0dff9baSBernard IremongerHash Initialization 132d0dff9baSBernard Iremonger~~~~~~~~~~~~~~~~~~~ 133d0dff9baSBernard Iremonger 134d0dff9baSBernard IremongerThe hash object is created and loaded with the pre-configured entries read from a global array, 135d0dff9baSBernard Iremongerand then generate the expected 5-tuple as key to keep consistence with those of real flow 136d0dff9baSBernard Iremongerfor the convenience to execute hash performance test on 4M/8M/16M flows. 137d0dff9baSBernard Iremonger 138d0dff9baSBernard Iremonger.. note:: 139d0dff9baSBernard Iremonger 140d0dff9baSBernard Iremonger The Hash initialization will setup both ipv4 and ipv6 hash table, 141d0dff9baSBernard Iremonger and populate the either table depending on the value of variable ipv6. 142d0dff9baSBernard Iremonger To support the hash performance test with up to 8M single direction flows/16M bi-direction flows, 143d0dff9baSBernard Iremonger populate_ipv4_many_flow_into_table() function will populate the hash table with specified hash table entry number(default 4M). 144d0dff9baSBernard Iremonger 145d0dff9baSBernard Iremonger.. note:: 146d0dff9baSBernard Iremonger 147d0dff9baSBernard Iremonger Value of global variable ipv6 can be specified with --ipv6 in the command line. 148d0dff9baSBernard Iremonger Value of global variable hash_entry_number, 149d0dff9baSBernard Iremonger which is used to specify the total hash entry number for all used ports in hash performance test, 150d0dff9baSBernard Iremonger can be specified with --hash-entry-num VALUE in command line, being its default value 4. 151d0dff9baSBernard Iremonger 152d0dff9baSBernard Iremonger.. code-block:: c 153d0dff9baSBernard Iremonger 154d0dff9baSBernard Iremonger #if (APP_LOOKUP_METHOD == APP_LOOKUP_EXACT_MATCH) 155d0dff9baSBernard Iremonger 156d0dff9baSBernard Iremonger static void 157d0dff9baSBernard Iremonger setup_hash(int socketid) 158d0dff9baSBernard Iremonger { 159d0dff9baSBernard Iremonger // ... 160d0dff9baSBernard Iremonger 161d0dff9baSBernard Iremonger if (hash_entry_number != HASH_ENTRY_NUMBER_DEFAULT) { 162d0dff9baSBernard Iremonger if (ipv6 == 0) { 163d0dff9baSBernard Iremonger /* populate the ipv4 hash */ 164d0dff9baSBernard Iremonger populate_ipv4_many_flow_into_table(ipv4_l3fwd_lookup_struct[socketid], hash_entry_number); 165d0dff9baSBernard Iremonger } else { 166d0dff9baSBernard Iremonger /* populate the ipv6 hash */ 167d0dff9baSBernard Iremonger populate_ipv6_many_flow_into_table( ipv6_l3fwd_lookup_struct[socketid], hash_entry_number); 168d0dff9baSBernard Iremonger } 169d0dff9baSBernard Iremonger } else 170d0dff9baSBernard Iremonger if (ipv6 == 0) { 171d0dff9baSBernard Iremonger /* populate the ipv4 hash */ 172d0dff9baSBernard Iremonger populate_ipv4_few_flow_into_table(ipv4_l3fwd_lookup_struct[socketid]); 173d0dff9baSBernard Iremonger } else { 174d0dff9baSBernard Iremonger /* populate the ipv6 hash */ 175d0dff9baSBernard Iremonger populate_ipv6_few_flow_into_table(ipv6_l3fwd_lookup_struct[socketid]); 176d0dff9baSBernard Iremonger } 177d0dff9baSBernard Iremonger } 178d0dff9baSBernard Iremonger } 179d0dff9baSBernard Iremonger #endif 180d0dff9baSBernard Iremonger 181d0dff9baSBernard IremongerLPM Initialization 182d0dff9baSBernard Iremonger~~~~~~~~~~~~~~~~~~ 183d0dff9baSBernard Iremonger 184d0dff9baSBernard IremongerThe LPM object is created and loaded with the pre-configured entries read from a global array. 185d0dff9baSBernard Iremonger 186d0dff9baSBernard Iremonger.. code-block:: c 187d0dff9baSBernard Iremonger 188d0dff9baSBernard Iremonger #if (APP_LOOKUP_METHOD == APP_LOOKUP_LPM) 189d0dff9baSBernard Iremonger 190d0dff9baSBernard Iremonger static void 191d0dff9baSBernard Iremonger setup_lpm(int socketid) 192d0dff9baSBernard Iremonger { 193d0dff9baSBernard Iremonger unsigned i; 194d0dff9baSBernard Iremonger int ret; 195d0dff9baSBernard Iremonger char s[64]; 196d0dff9baSBernard Iremonger 197d0dff9baSBernard Iremonger /* create the LPM table */ 198d0dff9baSBernard Iremonger 199a5cf3924SThomas Monjalon snprintf(s, sizeof(s), "IPV4_L3FWD_LPM_%d", socketid); 200d0dff9baSBernard Iremonger 201d0dff9baSBernard Iremonger ipv4_l3fwd_lookup_struct[socketid] = rte_lpm_create(s, socketid, IPV4_L3FWD_LPM_MAX_RULES, 0); 202d0dff9baSBernard Iremonger 203d0dff9baSBernard Iremonger if (ipv4_l3fwd_lookup_struct[socketid] == NULL) 204d0dff9baSBernard Iremonger rte_exit(EXIT_FAILURE, "Unable to create the l3fwd LPM table" 205d0dff9baSBernard Iremonger " on socket %d\n", socketid); 206d0dff9baSBernard Iremonger 207d0dff9baSBernard Iremonger /* populate the LPM table */ 208d0dff9baSBernard Iremonger 209d0dff9baSBernard Iremonger for (i = 0; i < IPV4_L3FWD_NUM_ROUTES; i++) { 210d0dff9baSBernard Iremonger /* skip unused ports */ 211d0dff9baSBernard Iremonger 212d0dff9baSBernard Iremonger if ((1 << ipv4_l3fwd_route_array[i].if_out & enabled_port_mask) == 0) 213d0dff9baSBernard Iremonger continue; 214d0dff9baSBernard Iremonger 215d0dff9baSBernard Iremonger ret = rte_lpm_add(ipv4_l3fwd_lookup_struct[socketid], ipv4_l3fwd_route_array[i].ip, 216d0dff9baSBernard Iremonger ipv4_l3fwd_route_array[i].depth, ipv4_l3fwd_route_array[i].if_out); 217d0dff9baSBernard Iremonger 218d0dff9baSBernard Iremonger if (ret < 0) { 219d0dff9baSBernard Iremonger rte_exit(EXIT_FAILURE, "Unable to add entry %u to the " 220d0dff9baSBernard Iremonger "l3fwd LPM table on socket %d\n", i, socketid); 221d0dff9baSBernard Iremonger } 222d0dff9baSBernard Iremonger 223d0dff9baSBernard Iremonger printf("LPM: Adding route 0x%08x / %d (%d)\n", 224d0dff9baSBernard Iremonger (unsigned)ipv4_l3fwd_route_array[i].ip, ipv4_l3fwd_route_array[i].depth, ipv4_l3fwd_route_array[i].if_out); 225d0dff9baSBernard Iremonger } 226d0dff9baSBernard Iremonger } 227d0dff9baSBernard Iremonger #endif 228d0dff9baSBernard Iremonger 229d0dff9baSBernard IremongerPacket Forwarding for Hash-based Lookups 230d0dff9baSBernard Iremonger~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 231d0dff9baSBernard Iremonger 232d0dff9baSBernard IremongerFor each input packet, the packet forwarding operation is done by the l3fwd_simple_forward() 233d0dff9baSBernard Iremongeror simple_ipv4_fwd_4pkts() function for IPv4 packets or the simple_ipv6_fwd_4pkts() function for IPv6 packets. 234d0dff9baSBernard IremongerThe l3fwd_simple_forward() function provides the basic functionality for both IPv4 and IPv6 packet forwarding 235d0dff9baSBernard Iremongerfor any number of burst packets received, 236d0dff9baSBernard Iremongerand the packet forwarding decision (that is, the identification of the output interface for the packet) 237d0dff9baSBernard Iremongerfor hash-based lookups is done by the get_ipv4_dst_port() or get_ipv6_dst_port() function. 238d0dff9baSBernard IremongerThe get_ipv4_dst_port() function is shown below: 239d0dff9baSBernard Iremonger 240d0dff9baSBernard Iremonger.. code-block:: c 241d0dff9baSBernard Iremonger 242d0dff9baSBernard Iremonger static inline uint8_t 243c6d6982dSZhiyong Yang get_ipv4_dst_port(void *ipv4_hdr, uint16_t portid, lookup_struct_t *ipv4_l3fwd_lookup_struct) 244d0dff9baSBernard Iremonger { 245d0dff9baSBernard Iremonger int ret = 0; 246d0dff9baSBernard Iremonger union ipv4_5tuple_host key; 247d0dff9baSBernard Iremonger 248*a7c528e5SOlivier Matz ipv4_hdr = (uint8_t *)ipv4_hdr + offsetof(struct rte_ipv4_hdr, time_to_live); 249d0dff9baSBernard Iremonger 250d0dff9baSBernard Iremonger m128i data = _mm_loadu_si128(( m128i*)(ipv4_hdr)); 251d0dff9baSBernard Iremonger 252d0dff9baSBernard Iremonger /* Get 5 tuple: dst port, src port, dst IP address, src IP address and protocol */ 253d0dff9baSBernard Iremonger 254d0dff9baSBernard Iremonger key.xmm = _mm_and_si128(data, mask0); 255d0dff9baSBernard Iremonger 256d0dff9baSBernard Iremonger /* Find destination port */ 257d0dff9baSBernard Iremonger 258d0dff9baSBernard Iremonger ret = rte_hash_lookup(ipv4_l3fwd_lookup_struct, (const void *)&key); 259d0dff9baSBernard Iremonger 260d0dff9baSBernard Iremonger return (uint8_t)((ret < 0)? portid : ipv4_l3fwd_out_if[ret]); 261d0dff9baSBernard Iremonger } 262d0dff9baSBernard Iremonger 263d0dff9baSBernard IremongerThe get_ipv6_dst_port() function is similar to the get_ipv4_dst_port() function. 264d0dff9baSBernard Iremonger 265d0dff9baSBernard IremongerThe simple_ipv4_fwd_4pkts() and simple_ipv6_fwd_4pkts() function are optimized for continuous 4 valid ipv4 and ipv6 packets, 266d0dff9baSBernard Iremongerthey leverage the multiple buffer optimization to boost the performance of forwarding packets with the exact match on hash table. 267d0dff9baSBernard IremongerThe key code snippet of simple_ipv4_fwd_4pkts() is shown below: 268d0dff9baSBernard Iremonger 269d0dff9baSBernard Iremonger.. code-block:: c 270d0dff9baSBernard Iremonger 271d0dff9baSBernard Iremonger static inline void 272c6d6982dSZhiyong Yang simple_ipv4_fwd_4pkts(struct rte_mbuf* m[4], uint16_t portid, struct lcore_conf *qconf) 273d0dff9baSBernard Iremonger { 274d0dff9baSBernard Iremonger // ... 275d0dff9baSBernard Iremonger 276*a7c528e5SOlivier Matz data[0] = _mm_loadu_si128(( m128i*)(rte_pktmbuf_mtod(m[0], unsigned char *) + sizeof(struct rte_ether_hdr) + offsetof(struct rte_ipv4_hdr, time_to_live))); 277*a7c528e5SOlivier Matz data[1] = _mm_loadu_si128(( m128i*)(rte_pktmbuf_mtod(m[1], unsigned char *) + sizeof(struct rte_ether_hdr) + offsetof(struct rte_ipv4_hdr, time_to_live))); 278*a7c528e5SOlivier Matz data[2] = _mm_loadu_si128(( m128i*)(rte_pktmbuf_mtod(m[2], unsigned char *) + sizeof(struct rte_ether_hdr) + offsetof(struct rte_ipv4_hdr, time_to_live))); 279*a7c528e5SOlivier Matz data[3] = _mm_loadu_si128(( m128i*)(rte_pktmbuf_mtod(m[3], unsigned char *) + sizeof(struct rte_ether_hdr) + offsetof(struct rte_ipv4_hdr, time_to_live))); 280d0dff9baSBernard Iremonger 281d0dff9baSBernard Iremonger key[0].xmm = _mm_and_si128(data[0], mask0); 282d0dff9baSBernard Iremonger key[1].xmm = _mm_and_si128(data[1], mask0); 283d0dff9baSBernard Iremonger key[2].xmm = _mm_and_si128(data[2], mask0); 284d0dff9baSBernard Iremonger key[3].xmm = _mm_and_si128(data[3], mask0); 285d0dff9baSBernard Iremonger 286d0dff9baSBernard Iremonger const void *key_array[4] = {&key[0], &key[1], &key[2],&key[3]}; 287d0dff9baSBernard Iremonger 288656ecbe9SThomas Monjalon rte_hash_lookup_bulk(qconf->ipv4_lookup_struct, &key_array[0], 4, ret); 289d0dff9baSBernard Iremonger 290d0dff9baSBernard Iremonger dst_port[0] = (ret[0] < 0)? portid:ipv4_l3fwd_out_if[ret[0]]; 291d0dff9baSBernard Iremonger dst_port[1] = (ret[1] < 0)? portid:ipv4_l3fwd_out_if[ret[1]]; 292d0dff9baSBernard Iremonger dst_port[2] = (ret[2] < 0)? portid:ipv4_l3fwd_out_if[ret[2]]; 293d0dff9baSBernard Iremonger dst_port[3] = (ret[3] < 0)? portid:ipv4_l3fwd_out_if[ret[3]]; 294d0dff9baSBernard Iremonger 295d0dff9baSBernard Iremonger // ... 296d0dff9baSBernard Iremonger } 297d0dff9baSBernard Iremonger 298d0dff9baSBernard IremongerThe simple_ipv6_fwd_4pkts() function is similar to the simple_ipv4_fwd_4pkts() function. 299d0dff9baSBernard Iremonger 30071a7e242SJianfeng TanKnown issue: IP packets with extensions or IP packets which are not TCP/UDP cannot work well at this mode. 30171a7e242SJianfeng Tan 302d0dff9baSBernard IremongerPacket Forwarding for LPM-based Lookups 303d0dff9baSBernard Iremonger~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 304d0dff9baSBernard Iremonger 305d0dff9baSBernard IremongerFor each input packet, the packet forwarding operation is done by the l3fwd_simple_forward() function, 306d0dff9baSBernard Iremongerbut the packet forwarding decision (that is, the identification of the output interface for the packet) 307d0dff9baSBernard Iremongerfor LPM-based lookups is done by the get_ipv4_dst_port() function below: 308d0dff9baSBernard Iremonger 309d0dff9baSBernard Iremonger.. code-block:: c 310d0dff9baSBernard Iremonger 311c6d6982dSZhiyong Yang static inline uint16_t 312*a7c528e5SOlivier Matz get_ipv4_dst_port(struct rte_ipv4_hdr *ipv4_hdr, uint16_t portid, lookup_struct_t *ipv4_l3fwd_lookup_struct) 313d0dff9baSBernard Iremonger { 314d0dff9baSBernard Iremonger uint8_t next_hop; 315d0dff9baSBernard Iremonger 316c6d6982dSZhiyong Yang return ((rte_lpm_lookup(ipv4_l3fwd_lookup_struct, rte_be_to_cpu_32(ipv4_hdr->dst_addr), &next_hop) == 0)? next_hop : portid); 317d0dff9baSBernard Iremonger } 318