Post

Dpdk 初学(5) Flow Filtering

flow_filtering example

重复函数不再赘述,只会涉及到新增的部分

关键函数

init_port

定义了需要的卸载功能,后续通过与网卡能提供的能力做&即可。

这里有个坑,默认的nr_queues为5,但是我本机的vm虚拟机网卡(e1000)提供的max_rx_queues为2,注意修改否则会初始化失败。真实的物理网卡为RTL8168H,搜了下没找到硬件手册。

vmvare workstation 默认启动的时候,网卡采用的是e1000,而这个配置是可以修改的,具体的解释可以参考这里

我们可以手动的修改vmx文件来启动另一个网络适配器VMXNET3

在vmx文件内,找到你想要修改的网络适配器编号,随后修改即可,例如:ethernet0.virtualDev = “vmxnet3”

我自己的网卡(e1000)只提供了0x800f的功能,即MULTI_SEGS、VLAN_INSET、IPV4&UDP&TCP的CKSUM。

修改成vmxnet3之后,提供0x802d,即MULTI_SEGS、TCP_TSO、VLAN_INSET、UDP&TCP的CKSUM。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
  struct rte_eth_conf port_conf = {
    .txmode = {
     .offloads =
      RTE_ETH_TX_OFFLOAD_VLAN_INSERT |
      RTE_ETH_TX_OFFLOAD_IPV4_CKSUM  |
      RTE_ETH_TX_OFFLOAD_UDP_CKSUM   |
      RTE_ETH_TX_OFFLOAD_TCP_CKSUM   |
      RTE_ETH_TX_OFFLOAD_SCTP_CKSUM  |
      RTE_ETH_TX_OFFLOAD_TCP_TSO,
    },
   };
  ...
  port_conf.txmode.offloads &= dev_info.tx_offload_capa;
  rte_eth_dev_configure(port_id, nr_queues, nr_queues, &port_conf);

随即开始初始化队列,这里直接使用了网卡的默认设置。对每一个port的每一个rx_queue,设置他们分配的描述符数量和配置。下面的tx_queue设置同理,只有一个区别。rx队列需要一个mbuf_pool来存储接收的frame data。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  rxq_conf = dev_info.default_rxconf;
  rxq_conf.offloads = port_conf.rxmode.offloads;
  for (i = 0; i < nr_queues; i++) {
    ret = rte_eth_rx_queue_setup(port_id, i, 512,
           rte_eth_dev_socket_id(port_id),
           &rxq_conf,
           mbuf_pool);
  }
  
  txq_conf = dev_info.default_txconf;
  txq_conf.offloads = port_conf.txmode.offloads;
  
  for (i = 0; i < nr_queues; i++) {
    ret = rte_eth_tx_queue_setup(port_id, i, 512,
      rte_eth_dev_socket_id(port_id),
      &txq_conf);

设置完成后,开启网卡混杂模式并且启动

1
2
  rte_eth_promiscuous_enable(port_id);
  rte_eth_dev_start(port_id);

generate_ipv4_flow

函数原型如下,从参数能大概推测是为了在port_id指定的nic上对指定rx_queue开启某种规则

1
2
3
4
5
6
  struct rte_flow *
  generate_ipv4_flow(uint16_t port_id, uint16_t rx_q,
    uint32_t src_ip, uint32_t src_mask,
    uint32_t dest_ip, uint32_t dest_mask,
    struct rte_flow_error *error)
  {

如何设置filter主要是利用下面的几个结构

1
2
3
  struct rte_flow_attr attr;
  struct rte_flow_item pattern[MAX_PATTERN_NUM];
  struct rte_flow_action action[MAX_ACTION_NUM];

rte_flow_attr: 设定包的流向属性(group、优先级、出入或转发)

rte_flow_item: 设定过滤的包具体需要满足什么条件

rte_flow_action: 设定过滤的操作是什么

其中pattern和action都是一个结构体数组,他们的最后一个成员都是一个结尾标志,如下所示

1
2
  action[1].type = RTE_FLOW_ACTION_TYPE_END;
  pattern[2].type = RTE_FLOW_ITEM_TYPE_END;

其中pattern有一点特殊,需要按照分层的思路来进行每一层的设定,如以下代码所示,从以太帧层到IP层,分别设置。

1
2
3
4
  pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
  pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV4;
  pattern[1].spec = &ip_spec;
  pattern[1].mask = &ip_mask;

IP层的设置如下,通过pattern[1].type可知是需要进行IPV4的配置,所以pattern[1].specrte_flow_item_ipv4结构

1
2
3
4
5
6
7
8
9
  memset(&ip_spec, 0, sizeof(struct rte_flow_item_ipv4));
  memset(&ip_mask, 0, sizeof(struct rte_flow_item_ipv4));
  ip_spec.hdr.dst_addr = htonl(dest_ip);
  ip_mask.hdr.dst_addr = dest_mask;
  ip_spec.hdr.src_addr = htonl(src_ip);
  ip_mask.hdr.src_addr = src_mask;
  pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV4;
  pattern[1].spec = &ip_spec;
  pattern[1].mask = &ip_mask;

最终检查filter并创建

1
2
  rte_flow_validate(port_id, &attr, pattern, action, error);
  rte_flow_create(port_id, &attr, pattern, action, error);

这里还有个小问题,在e1000模式下,由于不支持MSI-X会报错,需要更换到vmxnet3下

MSI-X是一种PCIe的中断模式,更详细的说明可以参考此链接

可惜的是目前切换到vmxnet3后,出现新的报错,如下所示

Flow can’t be created 1 message: Function not implemented, cause: (nil)

跟踪了一下源代码然后gdb看了看,问题出在dev->dev_ops->flow_ops_get,这玩意是空的,vmxnet3没实现这个地方,导致没法应用。有兴趣的可以跟一下,调用链如下

1
2
3
4
  0x00007ffff7eb4a10 in rte_flow_ops_get () 
  0x00007ffff7eb5fbf in rte_flow_validate () 
  0x00005555555564b5 in generate_ipv4_flow ()
  0x00005555555554fa in main ()

关键流程

  • 初始化EAL
  • 对端口进行设置
  • 构造flow rule并且应用
This post is licensed under CC BY 4.0 by the author.