-
Notifications
You must be signed in to change notification settings - Fork 0
5.1 DPDK Flows
Intel's Data Plane Development Kit (DPDK) is a set of libraries and drivers for fast packet processing, widely used in high-performance networking applications. One of its key features is the ability to filter packets efficiently, ensuring that only relevant traffic is captured and processed. This is typically achieved using the rte_flow
API, which provides a flexible and powerful way to define filtering rules.
rte_flow
allows developers to define flow rules that match specific packet patterns and specify actions to be taken when those patterns are detected. These rules can offload filtering tasks to the network hardware (if supported), reducing the processing burden on the CPU.
- Pattern: Defines the criteria to match packets (e.g., Ethernet type, IP addresses, ports).
- Action: Specifies what to do with matching packets (e.g., accept, drop, forward to a specific queue).
- Attributes: Additional parameters like queue selection or RSS hashing.
While rte_flow
doesn't use a traditional textual syntax like Berkeley Packet Filter (BPF), it provides a structured API to define patterns and actions. Below is an outline of how to construct these rules programmatically.
A typical rte_flow
rule consists of:
- Flow Item Array: Specifies the matching pattern.
- Action Array: Specifies the actions to perform on matched packets.
- Flow Attributes: Optional parameters for the flow.
Flow items represent the layers and fields to match in the packet. Common flow items include:
-
Ethernet (
RTE_FLOW_ITEM_TYPE_ETH
)- Matches Ethernet headers, allowing specification of MAC addresses and EtherTypes.
-
IPv4 (
RTE_FLOW_ITEM_TYPE_IPV4
)- Matches IPv4 headers, allowing specification of source/destination IPs, protocols, etc.
-
TCP (
RTE_FLOW_ITEM_TYPE_TCP
)- Matches TCP headers, allowing specification of source/destination ports.
-
UDP (
RTE_FLOW_ITEM_TYPE_UDP
)- Matches UDP headers, similar to TCP.
Actions define what to do with packets that match the pattern. Common actions include:
-
Queue (
RTE_FLOW_ACTION_TYPE_QUEUE
)- Directs packets to a specific receive queue.
-
Drop (
RTE_FLOW_ACTION_TYPE_DROP
)- Drops the packet.
-
Mark (
RTE_FLOW_ACTION_TYPE_MARK
)- Marks the packet with a user-defined value.
-
Count (
RTE_FLOW_ACTION_TYPE_COUNT
)- Counts the number of matching packets.
Objective: Capture only IPv4 TCP packets destined for port 80 (HTTP).
struct rte_flow_item pattern_eth[] = {
{
.type = RTE_FLOW_ITEM_TYPE_ETH,
.spec = NULL, // Match any Ethernet
.mask = NULL,
},
{
.type = RTE_FLOW_ITEM_TYPE_IPV4,
.spec = &(struct rte_flow_item_ipv4){
.hdr.src_addr = 0, // Match any source IP
.hdr.dst_addr = 0, // Match any destination IP
.hdr.next_proto_id = IPPROTO_TCP,
},
.mask = &(struct rte_flow_item_ipv4){
.hdr.src_addr = 0,
.hdr.dst_addr = 0,
.hdr.next_proto_id = 0xFF,
},
},
{
.type = RTE_FLOW_ITEM_TYPE_TCP,
.spec = &(struct rte_flow_item_tcp){
.hdr.dst_port = rte_cpu_to_be_16(80),
},
.mask = &(struct rte_flow_item_tcp){
.hdr.dst_port = 0xFFFF,
},
},
RTE_FLOW_ITEM_TYPE_END
};
struct rte_flow_action actions[] = {
{
.type = RTE_FLOW_ACTION_TYPE_QUEUE,
.conf = &(struct rte_flow_action_queue){
.index = 0, // Send to queue 0
},
},
RTE_FLOW_ACTION_TYPE_END
};
struct rte_flow_attr attr = {
.ingress = 1, // Apply to incoming packets
};
struct rte_flow *flow = rte_flow_create(port_id, &attr, pattern_eth, actions, NULL);
if (!flow) {
rte_exit(EXIT_FAILURE, "Flow creation failed\n");
}
Objective: Discard all incoming UDP packets.
struct rte_flow_item pattern_udp[] = {
{
.type = RTE_FLOW_ITEM_TYPE_ETH,
.spec = NULL,
.mask = NULL,
},
{
.type = RTE_FLOW_ITEM_TYPE_IPV4,
.spec = NULL,
.mask = NULL,
},
{
.type = RTE_FLOW_ITEM_TYPE_UDP,
.spec = NULL, // Match any UDP packet
.mask = NULL,
},
RTE_FLOW_ITEM_TYPE_END
};
struct rte_flow_action actions[] = {
{
.type = RTE_FLOW_ACTION_TYPE_DROP, // Drop the packet
},
RTE_FLOW_ACTION_TYPE_END
};
struct rte_flow_attr attr = {
.ingress = 1,
};
struct rte_flow *flow = rte_flow_create(port_id, &attr, pattern_udp, actions, NULL);
if (!flow) {
rte_exit(EXIT_FAILURE, "Flow creation failed\n");
}
Objective: Count the number of IPv6 ICMPv6 packets.
struct rte_flow_item pattern_icmpv6[] = {
{
.type = RTE_FLOW_ITEM_TYPE_ETH,
.spec = NULL,
.mask = NULL,
},
{
.type = RTE_FLOW_ITEM_TYPE_IPV6,
.spec = &(struct rte_flow_item_ipv6){
.hdr.proto = IPPROTO_ICMPV6,
},
.mask = &(struct rte_flow_item_ipv6){
.hdr.proto = 0xFF,
},
},
{
.type = RTE_FLOW_ITEM_TYPE_ICMPV6,
.spec = NULL,
.mask = NULL,
},
RTE_FLOW_ITEM_TYPE_END
};
struct rte_flow_action actions[] = {
{
.type = RTE_FLOW_ACTION_TYPE_COUNT,
.conf = &(struct rte_flow_action_count){
.shared = 0,
.id = 0, // Identifier for the counter
},
},
RTE_FLOW_ACTION_TYPE_END
};
struct rte_flow_attr attr = {
.ingress = 1,
};
struct rte_flow *flow = rte_flow_create(port_id, &attr, pattern_icmpv6, actions, NULL);
if (!flow) {
rte_exit(EXIT_FAILURE, "Flow creation failed\n");
}
Before creating any flows, ensure that DPDK is initialized correctly, including EAL (Environment Abstraction Layer) initialization and port configuration.
int main(int argc, char **argv) {
// Initialize the EAL
int ret = rte_eal_init(argc, argv);
if (ret < 0)
rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
// Configure and start the desired port
// ...
}
As shown in the examples above, define the pattern
, actions
, and attr
structures to specify the filtering criteria and desired actions.
Use rte_flow_create
to compile and apply the filter to a specific port.
struct rte_flow *flow = rte_flow_create(port_id, &attr, pattern, actions, NULL);
if (!flow) {
rte_exit(EXIT_FAILURE, "Flow creation failed\n");
}
-
port_id
: The identifier of the port to which the flow is applied. -
attr
: Flow attributes, such as ingress or egress direction. -
pattern
: Array ofrte_flow_item
defining the matching criteria. -
actions
: Array ofrte_flow_action
defining the actions for matched packets. -
error
: Optional parameter to receive detailed error information.
Manage the lifecycle of the flow, including deletion when no longer needed.
// To delete the flow
int delete_ret = rte_flow_destroy(port_id, flow, NULL);
if (delete_ret < 0) {
rte_exit(EXIT_FAILURE, "Flow deletion failed\n");
}
Once flows are set up, begin processing packets. Only packets matching the defined flows will trigger the specified actions, effectively limiting the capture and processing to relevant traffic.
- Leverage Hardware Offloading: Utilize the NIC's flow capabilities to offload filtering tasks, reducing CPU load.
- Minimize Flow Rules: Keep the number of flow rules minimal to optimize performance.
- Use Shared Counters: When counting packets, use shared counters if possible to reduce resource usage.
-
Error Handling: Always check the return values of
rte_flow_create
and handle errors appropriately. - Performance Testing: Test the performance impact of flow rules to ensure they meet application requirements.
Intel's DPDK rte_flow
API provides a robust and flexible mechanism for packet filtering, enabling high-performance applications to capture and process only the necessary traffic. By defining precise flow patterns and actions, developers can optimize packet processing, leverage hardware acceleration, and maintain efficient resource utilization.
For more detailed information, refer to the DPDK rte_flow
Documentation and explore the various flow item types and actions supported by your network hardware.