API: Creating Flow Rules

Link-Inline™ Software User Guide

Platform
Napatech SmartNIC
Content Type
User Guide
Capture Software Version
Link-Inline™ Software 3.2

flow rules must be created in correct sequence.

Correct sequence to define flow rules

A list of flow rules can be grouped using the group attribute. Group 0 is used to set up filters based on various header fields or to process all frames. For group 1 to 32, flow rules that match the same location of header fields can be grouped together to process frames. For example, group 1 can be used for a set of flow rules to match frames with a list of IPv4 addresses, and group 2 can be used for another set of flow rules to match IPv6 addresses. The jump action (jump to another group) is allowed only in flow rules with group 0.

Firstly, one or more filters must be applied using group 0 with a jump action. The filter in the first flow rule with group 0 can match all traffic or filter on various header fields. After that, flows can be programmed for offloading tasks in the SmartNIC. For example, flows could be handled as follows:
  • Frames with specific IPv4 addresses and UDP port numbers (known flows) are processed in the SmartNIC and transmitted on another port.
  • The rest of IPv4 traffic (unknown flows) is forwarded to the host for further processing.
Flow rules must be created in the following sequence:
  1. Create a flow rule with group 0 to filter IPv4/UDP traffic and jump to group 1.
  2. Create a flow rule with group 1 to forward all traffic to queue 0.
  3. Creates flow rules with group 1 to match specific IP addresses and UDP port numbers and forward them to another port.

Matching IPv4 traffic

This DPDK API example creates a basic flow rule that matches IPv4 traffic and forwards it to another flow group, which can then have its own set of rules to handle the IPv4 traffic. The group ID of 0 is used to filter IPv4 traffic.
/* Create group 0 flow to forward IPv4 packets to group 1. */
struct rte_flow_attr attr = { .group = 0, .ingress = 1 };

struct rte_flow_item pattern[] = {
    [0] = { .type = RTE_FLOW_ITEM_TYPE_IPV4 },
    [1] = { .type = RTE_FLOW_ITEM_TYPE_UDP },
    [2] = { .type = RTE_FLOW_ITEM_TYPE_END }};

struct rte_flow_action_jump jump = { .group = 1 };
struct rte_flow_action action[] = {
  [0] = { .type = RTE_FLOW_ACTION_TYPE_JUMP, .conf = &jump },
  [1] = { .type = RTE_FLOW_ACTION_TYPE_END }};

struct rte_flow_error error;
struct rte_flow *flow = rte_flow_create(PORT_ID, &attr, pattern, action, &error);
if (!flow) {
  /* Error handling */
}
A flow rule is created with a specified flow attribute attr that includes a group ID of 0. The .ingress = 1 indicates that the flow is for received traffic. The pattern array specifies to match an IPv4 header on received frames. The action array specifies the action to be taken when a matching frame is found which is jump to group 1. The rte_flow_create function is then called to create the flow rule. If the flow rule is successfully created, the function returns 0.

Forwarding all traffic to a queue

This DPDK API example shows how to create a flow rule that matches all frames and forwards them to a specific queue for further processing in the host. The flow rule is created using group 1.
/* Create default flow for group 1 to receive unmatched frames in queue 0. */
struct rte_flow_attr attr = { .group = 1, .ingress = 1 };

struct rte_flow_item pattern[] = {
  [0] = { .type = RTE_FLOW_ITEM_TYPE_END }};

struct rte_flow_action_queue queue = { .index = 0 };
struct rte_flow_action action[] = {
  [0] = { .type = RTE_FLOW_ACTION_TYPE_QUEUE, .conf = &queue },
  [1] = { .type = RTE_FLOW_ACTION_TYPE_END }};

struct rte_flow_error error;
struct rte_flow *flow = rte_flow_create(PORT_ID, &attr, pattern, action, &error);
if (!flow) {
  /* Error handling */
}
This creates a flow rule that matches all received frames and forwards them to queue 0. The flow attribute attr includes a group ID of 1. The pattern array only contains an end item, which means that it matches all frames. The action array specifies the action to send frames to queue 0.

Programming a flow

This DPDK API example illustrates how to create a flow rule that matches frames with specific source and destination IPv4 addresses, source and destination port numbers, and then forwards the matching frames to a specific port. Group ID 1 is used for this flow rule. This flow rule triggers adding a flow to the flow table in the SmartNIC.
/* Create group 1 exact match 5-tuple to retransmit offloaded IPv4 UDP packets. */
struct rte_flow_attr attr = { .group = 1, .ingress = 1 };

struct rte_flow_item_ipv4 ipv4 = { .hdr = {
  .src_addr = RTE_BE32(0x12345678),
  .dst_addr = RTE_BE32(0xbaddecaf) }};
struct rte_flow_item_udp udp = { .hdr = {
  .src_port = RTE_BE16(0x1000),
  .dst_port = RTE_BE16(0x1001) }};
struct rte_flow_item pattern[] = {
  [0] = { .type = RTE_FLOW_ITEM_TYPE_IPV4, .spec = &ipv4, .mask = &rte_flow_item_ipv4_mask },
  [1] = { .type = RTE_FLOW_ITEM_TYPE_UDP, .spec = &udp, .mask = &rte_flow_item_udp_mask }};

struct rte_flow_action_port_id port_id = { .id = 1 };
struct rte_flow_action action[] = {
  [0] = { .type = RTE_FLOW_ACTION_TYPE_PORT_ID, .conf = &port_id },
  [1] = { .type = RTE_FLOW_ACTION_TYPE_END }};

struct rte_flow_error error;
struct rte_flow *flow = rte_flow_create(PORT_ID, &attr, pattern, action, &error);
if (!flow) {
  /* Error handling */
}
The flow rule contains a specified flow attribute attr that includes a group ID of 1. The pattern array specifies the pattern to match on received frames, which includes an IPv4 header and a UDP header. The spec field in each item defines the specific values that must match in the frame, while the mask field specifies which bits in the frame should be matched against. The action array specifies the action to forward the frame to port 1. Further hardware-offload actions can be added to the action array.
The following example shows how to create a flow rule matching the inner layer IP addresses and TCP port numbers.
/* Create group 1 exact match inner layers 5-tuple to retransmit offloaded IPv4 TCP packets. */
struct rte_flow_attr attr = { .group = 1, .ingress = 1 };

struct rte_flow_item_any any = { .num = 3 };
struct rte_flow_item_any any_mask = { .num = 0xffffffff };
struct rte_flow_item_ipv4 ipv4 = { .hdr = {
  .src_addr = RTE_BE32(RTE_IPV4(20, 10, 10, 2)),
  .dst_addr = RTE_BE32(RTE_IPV4(20, 10, 11, 23)) }};
struct rte_flow_item_tcp tcp = { .hdr = {
  .src_port = RTE_BE16(0x1000),
  .dst_port = RTE_BE16(0x1001) }};
struct rte_flow_item pattern[] = {
  [0] = { .type = RTE_FLOW_ITEM_TYPE_ANY, .spec = &any, .mask = &any_mask },
  [1] = { .type = RTE_FLOW_ITEM_TYPE_IPV4, .spec = &ipv4, .mask = &rte_flow_item_ipv4_mask },
  [2] = { .type = RTE_FLOW_ITEM_TYPE_TCP, .spec = &tcp, .mask = &rte_flow_item_tcp_mask }};

struct rte_flow_action_port_id port_id = { .id = 1 };
struct rte_flow_action action[] = {
  [0] = { .type = RTE_FLOW_ACTION_TYPE_PORT_ID, .conf = &port_id },
  [1] = { .type = RTE_FLOW_ACTION_TYPE_END }};

struct rte_flow_error error;
struct rte_flow *flow = rte_flow_create(PORT_ID, &attr, pattern, action, &error);
if (!flow) {
  /* ERROR */
}
The first item in the pattern array uses the RTE_FLOW_ITEM_TYPE_ANY type, which means that this item is matching any part of the outer layer in a frame.