2018-02-01 17:18:17 +00:00
|
|
|
.. SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
Copyright(c) 2010-2014 Intel Corporation.
|
2014-11-11 12:27:01 +00:00
|
|
|
|
|
|
|
L3 Forwarding Sample Application
|
|
|
|
================================
|
|
|
|
|
2014-12-18 10:51:19 +00:00
|
|
|
The L3 Forwarding application is a simple example of packet processing using the DPDK.
|
2014-11-11 12:27:01 +00:00
|
|
|
The application performs L3 forwarding.
|
|
|
|
|
|
|
|
Overview
|
|
|
|
--------
|
|
|
|
|
2014-12-18 10:51:19 +00:00
|
|
|
The application demonstrates the use of the hash and LPM libraries in the DPDK to implement packet forwarding.
|
2016-02-25 17:02:27 +00:00
|
|
|
The initialization and run-time paths are very similar to those of the :doc:`l2_forward_real_virtual`.
|
2014-11-11 12:27:01 +00:00
|
|
|
The main difference from the L2 Forwarding sample application is that the forwarding decision
|
|
|
|
is made based on information read from the input packet.
|
|
|
|
|
2016-11-10 16:29:50 +00:00
|
|
|
The lookup method is either hash-based or LPM-based and is selected at run time. When the selected lookup method is hash-based,
|
2014-11-11 12:27:01 +00:00
|
|
|
a hash object is used to emulate the flow classification stage.
|
|
|
|
The hash object is used in correlation with a flow table to map each input packet to its flow at runtime.
|
|
|
|
|
|
|
|
The hash lookup key is represented by a DiffServ 5-tuple composed of the following fields read from the input packet:
|
|
|
|
Source IP Address, Destination IP Address, Protocol, Source Port and Destination Port.
|
|
|
|
The ID of the output interface for the input packet is read from the identified flow table entry.
|
|
|
|
The set of flows used by the application is statically configured and loaded into the hash at initialization time.
|
|
|
|
When the selected lookup method is LPM based, an LPM object is used to emulate the forwarding stage for IPv4 packets.
|
|
|
|
The LPM object is used as the routing table to identify the next hop for each input packet at runtime.
|
|
|
|
|
|
|
|
The LPM lookup key is represented by the Destination IP Address field read from the input packet.
|
|
|
|
The ID of the output interface for the input packet is the next hop returned by the LPM lookup.
|
|
|
|
The set of LPM rules used by the application is statically configured and loaded into the LPM object at initialization time.
|
|
|
|
|
|
|
|
In the sample application, hash-based forwarding supports IPv4 and IPv6. LPM-based forwarding supports IPv4 only.
|
|
|
|
|
|
|
|
Compiling the Application
|
|
|
|
-------------------------
|
|
|
|
|
2017-10-25 15:50:59 +00:00
|
|
|
To compile the sample application see :doc:`compiling`.
|
2014-11-11 12:27:01 +00:00
|
|
|
|
2017-10-25 15:50:59 +00:00
|
|
|
The application is located in the ``l3fwd`` sub-directory.
|
2014-11-11 12:27:01 +00:00
|
|
|
|
|
|
|
Running the Application
|
|
|
|
-----------------------
|
|
|
|
|
2016-06-27 14:26:48 +00:00
|
|
|
The application has a number of command line options::
|
2014-11-11 12:27:01 +00:00
|
|
|
|
2016-06-27 14:26:48 +00:00
|
|
|
./l3fwd [EAL options] -- -p PORTMASK
|
|
|
|
[-P]
|
|
|
|
[-E]
|
|
|
|
[-L]
|
|
|
|
--config(port,queue,lcore)[,(port,queue,lcore)]
|
|
|
|
[--eth-dest=X,MM:MM:MM:MM:MM:MM]
|
|
|
|
[--enable-jumbo [--max-pkt-len PKTLEN]]
|
|
|
|
[--no-numa]
|
|
|
|
[--hash-entry-num]
|
|
|
|
[--ipv6]
|
|
|
|
[--parse-ptype]
|
2019-04-25 09:40:19 +00:00
|
|
|
[--per-port-pool]
|
2014-11-11 12:27:01 +00:00
|
|
|
|
2016-06-27 14:26:48 +00:00
|
|
|
Where,
|
2014-11-11 12:27:01 +00:00
|
|
|
|
2016-06-27 14:26:48 +00:00
|
|
|
* ``-p PORTMASK:`` Hexadecimal bitmask of ports to configure
|
2014-11-11 12:27:01 +00:00
|
|
|
|
2016-06-27 14:26:48 +00:00
|
|
|
* ``-P:`` Optional, sets all ports to promiscuous mode so that packets are accepted regardless of the packet's Ethernet MAC destination address.
|
|
|
|
Without this option, only packets with the Ethernet MAC destination address set to the Ethernet address of the port are accepted.
|
2014-11-11 12:27:01 +00:00
|
|
|
|
2016-06-27 14:26:48 +00:00
|
|
|
* ``-E:`` Optional, enable exact match.
|
2014-11-11 12:27:01 +00:00
|
|
|
|
2016-06-27 14:26:48 +00:00
|
|
|
* ``-L:`` Optional, enable longest prefix match.
|
2014-11-11 12:27:01 +00:00
|
|
|
|
2016-06-27 14:26:48 +00:00
|
|
|
* ``--config (port,queue,lcore)[,(port,queue,lcore)]:`` Determines which queues from which ports are mapped to which cores.
|
2014-11-11 12:27:01 +00:00
|
|
|
|
2016-06-27 14:26:48 +00:00
|
|
|
* ``--eth-dest=X,MM:MM:MM:MM:MM:MM:`` Optional, ethernet destination for port X.
|
2014-11-11 12:27:01 +00:00
|
|
|
|
2016-06-27 14:26:48 +00:00
|
|
|
* ``--enable-jumbo:`` Optional, enables jumbo frames.
|
2014-11-11 12:27:01 +00:00
|
|
|
|
2016-06-27 14:26:48 +00:00
|
|
|
* ``--max-pkt-len:`` Optional, under the premise of enabling jumbo, maximum packet length in decimal (64-9600).
|
2014-11-11 12:27:01 +00:00
|
|
|
|
2016-06-27 14:26:48 +00:00
|
|
|
* ``--no-numa:`` Optional, disables numa awareness.
|
2014-11-11 12:27:01 +00:00
|
|
|
|
2016-06-27 14:26:48 +00:00
|
|
|
* ``--hash-entry-num:`` Optional, specifies the hash entry number in hexadecimal to be setup.
|
|
|
|
|
|
|
|
* ``--ipv6:`` Optional, set if running ipv6 packets.
|
|
|
|
|
|
|
|
* ``--parse-ptype:`` Optional, set to use software to analyze packet type. Without this option, hardware will check the packet type.
|
2016-03-25 00:47:46 +00:00
|
|
|
|
2019-04-25 09:40:19 +00:00
|
|
|
* ``--per-port-pool:`` Optional, set to use independent buffer pools per port. Without this option, single buffer pool is used for all ports.
|
|
|
|
|
2016-12-19 16:34:12 +00:00
|
|
|
For example, consider a dual processor socket platform with 8 physical cores, where cores 0-7 and 16-23 appear on socket 0,
|
|
|
|
while cores 8-15 and 24-31 appear on socket 1.
|
2014-11-11 12:27:01 +00:00
|
|
|
|
2016-12-19 16:34:12 +00:00
|
|
|
To enable L3 forwarding between two ports, assuming that both ports are in the same socket, using two cores, cores 1 and 2,
|
|
|
|
(which are in the same socket too), use the following command:
|
2014-11-11 12:27:01 +00:00
|
|
|
|
|
|
|
.. code-block:: console
|
|
|
|
|
2016-12-19 16:34:12 +00:00
|
|
|
./build/l3fwd -l 1,2 -n 4 -- -p 0x3 --config="(0,0,1),(1,0,2)"
|
2014-11-11 12:27:01 +00:00
|
|
|
|
|
|
|
In this command:
|
|
|
|
|
2016-12-19 16:34:12 +00:00
|
|
|
* The -l option enables cores 1, 2
|
2014-11-11 12:27:01 +00:00
|
|
|
|
|
|
|
* The -p option enables ports 0 and 1
|
|
|
|
|
2016-12-19 16:34:12 +00:00
|
|
|
* The --config option enables one queue on each port and maps each (port,queue) pair to a specific core.
|
2014-11-11 12:27:01 +00:00
|
|
|
The following table shows the mapping in this example:
|
|
|
|
|
|
|
|
+----------+-----------+-----------+-------------------------------------+
|
|
|
|
| **Port** | **Queue** | **lcore** | **Description** |
|
|
|
|
| | | | |
|
|
|
|
+----------+-----------+-----------+-------------------------------------+
|
2016-12-19 16:34:12 +00:00
|
|
|
| 0 | 0 | 1 | Map queue 0 from port 0 to lcore 1. |
|
2014-11-11 12:27:01 +00:00
|
|
|
| | | | |
|
|
|
|
+----------+-----------+-----------+-------------------------------------+
|
2016-12-19 16:34:12 +00:00
|
|
|
| 1 | 0 | 2 | Map queue 0 from port 1 to lcore 2. |
|
2014-11-11 12:27:01 +00:00
|
|
|
| | | | |
|
|
|
|
+----------+-----------+-----------+-------------------------------------+
|
|
|
|
|
2014-12-18 10:51:19 +00:00
|
|
|
Refer to the *DPDK Getting Started Guide* for general information on running applications and
|
2014-11-11 12:27:01 +00:00
|
|
|
the Environment Abstraction Layer (EAL) options.
|
|
|
|
|
2016-02-25 17:02:27 +00:00
|
|
|
.. _l3_fwd_explanation:
|
|
|
|
|
2014-11-11 12:27:01 +00:00
|
|
|
Explanation
|
|
|
|
-----------
|
|
|
|
|
|
|
|
The following sections provide some explanation of the sample application code. As mentioned in the overview section,
|
2016-02-25 17:02:27 +00:00
|
|
|
the initialization and run-time paths are very similar to those of the :doc:`l2_forward_real_virtual`.
|
2014-11-11 12:27:01 +00:00
|
|
|
The following sections describe aspects that are specific to the L3 Forwarding sample application.
|
|
|
|
|
|
|
|
Hash Initialization
|
|
|
|
~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
The hash object is created and loaded with the pre-configured entries read from a global array,
|
|
|
|
and then generate the expected 5-tuple as key to keep consistence with those of real flow
|
|
|
|
for the convenience to execute hash performance test on 4M/8M/16M flows.
|
|
|
|
|
|
|
|
.. note::
|
|
|
|
|
|
|
|
The Hash initialization will setup both ipv4 and ipv6 hash table,
|
|
|
|
and populate the either table depending on the value of variable ipv6.
|
|
|
|
To support the hash performance test with up to 8M single direction flows/16M bi-direction flows,
|
|
|
|
populate_ipv4_many_flow_into_table() function will populate the hash table with specified hash table entry number(default 4M).
|
|
|
|
|
|
|
|
.. note::
|
|
|
|
|
|
|
|
Value of global variable ipv6 can be specified with --ipv6 in the command line.
|
|
|
|
Value of global variable hash_entry_number,
|
|
|
|
which is used to specify the total hash entry number for all used ports in hash performance test,
|
|
|
|
can be specified with --hash-entry-num VALUE in command line, being its default value 4.
|
|
|
|
|
|
|
|
.. code-block:: c
|
|
|
|
|
|
|
|
#if (APP_LOOKUP_METHOD == APP_LOOKUP_EXACT_MATCH)
|
|
|
|
|
|
|
|
static void
|
|
|
|
setup_hash(int socketid)
|
|
|
|
{
|
|
|
|
// ...
|
|
|
|
|
|
|
|
if (hash_entry_number != HASH_ENTRY_NUMBER_DEFAULT) {
|
|
|
|
if (ipv6 == 0) {
|
|
|
|
/* populate the ipv4 hash */
|
|
|
|
populate_ipv4_many_flow_into_table(ipv4_l3fwd_lookup_struct[socketid], hash_entry_number);
|
|
|
|
} else {
|
|
|
|
/* populate the ipv6 hash */
|
|
|
|
populate_ipv6_many_flow_into_table( ipv6_l3fwd_lookup_struct[socketid], hash_entry_number);
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
if (ipv6 == 0) {
|
|
|
|
/* populate the ipv4 hash */
|
|
|
|
populate_ipv4_few_flow_into_table(ipv4_l3fwd_lookup_struct[socketid]);
|
|
|
|
} else {
|
|
|
|
/* populate the ipv6 hash */
|
|
|
|
populate_ipv6_few_flow_into_table(ipv6_l3fwd_lookup_struct[socketid]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
LPM Initialization
|
|
|
|
~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
The LPM object is created and loaded with the pre-configured entries read from a global array.
|
|
|
|
|
|
|
|
.. code-block:: c
|
|
|
|
|
|
|
|
#if (APP_LOOKUP_METHOD == APP_LOOKUP_LPM)
|
|
|
|
|
|
|
|
static void
|
|
|
|
setup_lpm(int socketid)
|
|
|
|
{
|
|
|
|
unsigned i;
|
|
|
|
int ret;
|
|
|
|
char s[64];
|
|
|
|
|
|
|
|
/* create the LPM table */
|
|
|
|
|
2015-12-15 09:26:20 +00:00
|
|
|
snprintf(s, sizeof(s), "IPV4_L3FWD_LPM_%d", socketid);
|
2014-11-11 12:27:01 +00:00
|
|
|
|
|
|
|
ipv4_l3fwd_lookup_struct[socketid] = rte_lpm_create(s, socketid, IPV4_L3FWD_LPM_MAX_RULES, 0);
|
|
|
|
|
|
|
|
if (ipv4_l3fwd_lookup_struct[socketid] == NULL)
|
|
|
|
rte_exit(EXIT_FAILURE, "Unable to create the l3fwd LPM table"
|
|
|
|
" on socket %d\n", socketid);
|
|
|
|
|
|
|
|
/* populate the LPM table */
|
|
|
|
|
|
|
|
for (i = 0; i < IPV4_L3FWD_NUM_ROUTES; i++) {
|
|
|
|
/* skip unused ports */
|
|
|
|
|
|
|
|
if ((1 << ipv4_l3fwd_route_array[i].if_out & enabled_port_mask) == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
ret = rte_lpm_add(ipv4_l3fwd_lookup_struct[socketid], ipv4_l3fwd_route_array[i].ip,
|
|
|
|
ipv4_l3fwd_route_array[i].depth, ipv4_l3fwd_route_array[i].if_out);
|
|
|
|
|
|
|
|
if (ret < 0) {
|
|
|
|
rte_exit(EXIT_FAILURE, "Unable to add entry %u to the "
|
|
|
|
"l3fwd LPM table on socket %d\n", i, socketid);
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("LPM: Adding route 0x%08x / %d (%d)\n",
|
|
|
|
(unsigned)ipv4_l3fwd_route_array[i].ip, ipv4_l3fwd_route_array[i].depth, ipv4_l3fwd_route_array[i].if_out);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
Packet Forwarding for Hash-based Lookups
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
For each input packet, the packet forwarding operation is done by the l3fwd_simple_forward()
|
|
|
|
or simple_ipv4_fwd_4pkts() function for IPv4 packets or the simple_ipv6_fwd_4pkts() function for IPv6 packets.
|
|
|
|
The l3fwd_simple_forward() function provides the basic functionality for both IPv4 and IPv6 packet forwarding
|
|
|
|
for any number of burst packets received,
|
|
|
|
and the packet forwarding decision (that is, the identification of the output interface for the packet)
|
|
|
|
for hash-based lookups is done by the get_ipv4_dst_port() or get_ipv6_dst_port() function.
|
|
|
|
The get_ipv4_dst_port() function is shown below:
|
|
|
|
|
|
|
|
.. code-block:: c
|
|
|
|
|
|
|
|
static inline uint8_t
|
2017-10-13 13:17:01 +00:00
|
|
|
get_ipv4_dst_port(void *ipv4_hdr, uint16_t portid, lookup_struct_t *ipv4_l3fwd_lookup_struct)
|
2014-11-11 12:27:01 +00:00
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
union ipv4_5tuple_host key;
|
|
|
|
|
2019-05-21 16:13:10 +00:00
|
|
|
ipv4_hdr = (uint8_t *)ipv4_hdr + offsetof(struct rte_ipv4_hdr, time_to_live);
|
2014-11-11 12:27:01 +00:00
|
|
|
|
|
|
|
m128i data = _mm_loadu_si128(( m128i*)(ipv4_hdr));
|
|
|
|
|
|
|
|
/* Get 5 tuple: dst port, src port, dst IP address, src IP address and protocol */
|
|
|
|
|
|
|
|
key.xmm = _mm_and_si128(data, mask0);
|
|
|
|
|
|
|
|
/* Find destination port */
|
|
|
|
|
|
|
|
ret = rte_hash_lookup(ipv4_l3fwd_lookup_struct, (const void *)&key);
|
|
|
|
|
|
|
|
return (uint8_t)((ret < 0)? portid : ipv4_l3fwd_out_if[ret]);
|
|
|
|
}
|
|
|
|
|
|
|
|
The get_ipv6_dst_port() function is similar to the get_ipv4_dst_port() function.
|
|
|
|
|
|
|
|
The simple_ipv4_fwd_4pkts() and simple_ipv6_fwd_4pkts() function are optimized for continuous 4 valid ipv4 and ipv6 packets,
|
|
|
|
they leverage the multiple buffer optimization to boost the performance of forwarding packets with the exact match on hash table.
|
|
|
|
The key code snippet of simple_ipv4_fwd_4pkts() is shown below:
|
|
|
|
|
|
|
|
.. code-block:: c
|
|
|
|
|
|
|
|
static inline void
|
2017-10-13 13:17:01 +00:00
|
|
|
simple_ipv4_fwd_4pkts(struct rte_mbuf* m[4], uint16_t portid, struct lcore_conf *qconf)
|
2014-11-11 12:27:01 +00:00
|
|
|
{
|
|
|
|
// ...
|
|
|
|
|
2019-05-21 16:13:10 +00:00
|
|
|
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)));
|
|
|
|
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)));
|
|
|
|
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)));
|
|
|
|
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)));
|
2014-11-11 12:27:01 +00:00
|
|
|
|
|
|
|
key[0].xmm = _mm_and_si128(data[0], mask0);
|
|
|
|
key[1].xmm = _mm_and_si128(data[1], mask0);
|
|
|
|
key[2].xmm = _mm_and_si128(data[2], mask0);
|
|
|
|
key[3].xmm = _mm_and_si128(data[3], mask0);
|
|
|
|
|
|
|
|
const void *key_array[4] = {&key[0], &key[1], &key[2],&key[3]};
|
|
|
|
|
2016-04-19 16:04:00 +00:00
|
|
|
rte_hash_lookup_bulk(qconf->ipv4_lookup_struct, &key_array[0], 4, ret);
|
2014-11-11 12:27:01 +00:00
|
|
|
|
|
|
|
dst_port[0] = (ret[0] < 0)? portid:ipv4_l3fwd_out_if[ret[0]];
|
|
|
|
dst_port[1] = (ret[1] < 0)? portid:ipv4_l3fwd_out_if[ret[1]];
|
|
|
|
dst_port[2] = (ret[2] < 0)? portid:ipv4_l3fwd_out_if[ret[2]];
|
|
|
|
dst_port[3] = (ret[3] < 0)? portid:ipv4_l3fwd_out_if[ret[3]];
|
|
|
|
|
|
|
|
// ...
|
|
|
|
}
|
|
|
|
|
|
|
|
The simple_ipv6_fwd_4pkts() function is similar to the simple_ipv4_fwd_4pkts() function.
|
|
|
|
|
2016-03-25 00:47:46 +00:00
|
|
|
Known issue: IP packets with extensions or IP packets which are not TCP/UDP cannot work well at this mode.
|
|
|
|
|
2014-11-11 12:27:01 +00:00
|
|
|
Packet Forwarding for LPM-based Lookups
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
For each input packet, the packet forwarding operation is done by the l3fwd_simple_forward() function,
|
|
|
|
but the packet forwarding decision (that is, the identification of the output interface for the packet)
|
|
|
|
for LPM-based lookups is done by the get_ipv4_dst_port() function below:
|
|
|
|
|
|
|
|
.. code-block:: c
|
|
|
|
|
2017-10-13 13:17:01 +00:00
|
|
|
static inline uint16_t
|
2019-05-21 16:13:10 +00:00
|
|
|
get_ipv4_dst_port(struct rte_ipv4_hdr *ipv4_hdr, uint16_t portid, lookup_struct_t *ipv4_l3fwd_lookup_struct)
|
2014-11-11 12:27:01 +00:00
|
|
|
{
|
|
|
|
uint8_t next_hop;
|
|
|
|
|
2017-10-13 13:17:01 +00:00
|
|
|
return ((rte_lpm_lookup(ipv4_l3fwd_lookup_struct, rte_be_to_cpu_32(ipv4_hdr->dst_addr), &next_hop) == 0)? next_hop : portid);
|
2014-11-11 12:27:01 +00:00
|
|
|
}
|