Rate This Document
Findability
Accuracy
Completeness
Readability

Data Plane Development Reference

  1. Learn about the data plane development components. For details, see Hydra Programming in the OVS Data Plane.
  2. Create an OVS data plane development directory in a custom code project, for example, ovs_project.
    ovs_project/
    ├── CMakeLists.txt                       # Top-level CMake configuration file
    └── src_dsl                              # Subdirectory storing user data plane source code
        ├── CMakeLists.txt                   # CMake configuration file in the data plane subdirectory
        └── ovs_project.hdr                  # Data plane Hydra source file
  3. Analyze the protocol types of the network packets to be processed by the service and implement a packet parser.

    For example, if the network packets are standard VXLAN, TCP, UDP, and ICMP packets, they are within the range supported by the hardware parser as shown in Table 1. You do not need to implement a custom parser or hybrid parser, and can use hdr_fix_parser directly.

  4. Implement the MainPipe based on the network packet processing flow.
    control MainPipe(in hdr_fix_headers hdr) {
        @main
        table ovs_exact {
                key = { ... }
                actions = { ... }
                size = 0x200000;
                default_action = flexda_upcall;
                aging_time = 200;
        }
        apply {
        }
    }
    1. Determine the keys and the corresponding actions to be offloaded based on the packet type.

      Determine whether to customize keys based on Table 1 and whether to customize actions based on Table 1. Then, complete the table definition.

      Code example:

      rte_port_id_hdr_t input_port;
      rte_eth_hdr_t eth;
      rte_vxlan_hdr_t vxlan_vni;
      rte_vlan_hdr_t vlan;
      ip_union l3_union;
      port_icmp_union l4_union;
      @main @rss_fastpath
      table ovs_exact {
              key = {
                  l3_union : exact;
                  l4_union : exact;
                  input_port : exact;
                  eth : exact;
                  vxlan_vni : exact;
                  vlan : exact;
              }
              actions = {
                  rte_port_id;
                  rte_of_pop_vlan;
                  rte_of_push_vlan;
                  rte_of_set_vlan_vid;
                  rte_of_set_vlan_pcp;
                  rte_vxlan_decap;
                  rte_vxlan_encap;
                  rte_set_ipv4_src;
                  rte_set_ipv4_dst;
                  rte_set_ipv6_src;
                  rte_set_ipv6_dst;
                  rte_set_tp_src;
                  rte_set_tp_dst;
                  rte_set_mac_dst;
                  rte_set_mac_src;
              }
              size = 0x200000;
              default_action = flexda_upcall;
              aging_time = 200;
      }

      The keys in the pre- and post-auxiliary tables support fuzzy match. To use fuzzy match, you need to change the key matching mode to ternary.

      @pre
      table ovs_pre_ternary {
              key = {
                  l3_union : ternary;
                  l4_union : exact;
              }
              actions = {}
              size = 0x200;
              default_action = flexda_no_action;
              aging_time = 200;
      }

      Similarly, a fuzzy post-auxiliary table can be defined in the postpipe.

      @post
      table ovs_post_ternary {
              key = {
                  l3_union : exact;
                  l4_union : ternary;
              }
              actions = {}
              size = 0x200;
              default_action = flexda_no_action;
              aging_time = 200;
      }
    2. Implement the apply logic based on the key definition.

      Code example:

      apply {
              input_port.port_id = hdr_input_port_id_get();
              if (hdr.tunnel.vxlan.isValid() && (input_port.port_id >> 12) == VPORT_TYPE_BOND) {
                  if (hdr.inner_ethernet.isValid()) {
                      eth.dst = hdr.inner_ethernet.dmac;
                      eth.src = hdr.inner_ethernet.smac;
                      eth.eth_type = hdr.inner_ethernet.eth_type;
                  }
                  if (hdr.inner_vlan.isValid()) {
                      eth.eth_type = hdr.inner_vlan.eth_type;
                      vlan.vlan_tci = (bit<16>)hdr.inner_vlan.vid;
                  }
                  if (hdr.inner_l3.ipv4.isValid() || hdr.inner_l3.ipv6.isValid()) {
                      if (hdr.inner_l3.ipv4.isValid()) {
                          l3_union.dpdk_ipv4.setValid();
                          l3_union.dpdk_ipv4.protocol = hdr.inner_l3.ipv4.protocol;
                          l3_union.dpdk_ipv4.sip = hdr.inner_l3.ipv4.sip;
                          l3_union.dpdk_ipv4.dip = hdr.inner_l3.ipv4.dip;
                      } else if (hdr.inner_l3.ipv6.isValid()) {
                          l3_union.dpdk_ipv6.setValid();
                          l3_union.dpdk_ipv6.next_header = hdr.inner_l3.ipv6.next_header;
                          l3_union.dpdk_ipv6.sip = hdr.inner_l3.ipv6.sip;
                          l3_union.dpdk_ipv6.dip = hdr.inner_l3.ipv6.dip;
                      }
                      if (hdr.inner_l4.udp.isValid()) {
                          l4_union.dpdk_udp.setValid();
                          l4_union.dpdk_udp.src_port = hdr.inner_l4.udp.src_port;
                          l4_union.dpdk_udp.dst_port = hdr.inner_l4.udp.dst_port;
                      } else if (hdr.inner_l4.tcp.isValid()) {
                          l4_union.dpdk_tcp.setValid();
                          l4_union.dpdk_tcp.src_port = hdr.inner_l4.tcp.src_port;
                          l4_union.dpdk_tcp.dst_port = hdr.inner_l4.tcp.dst_port;
                      } else if (hdr.inner_l4.icmp.isValid()) {
                          if (hdr.inner_l3.ipv4.isValid()) {
                              l4_union.dpdk_icmp.setValid();
                              l4_union.dpdk_icmp.type = hdr.inner_l4.icmp.type;
                              l4_union.dpdk_icmp.code = hdr.inner_l4.icmp.code;
                              l4_union.dpdk_icmp.ident = hdr.inner_l4.icmp.identifier;
                          } else if (hdr.inner_l3.ipv6.isValid()) {
                              l4_union.dpdk_icmp6.setValid();
                              l4_union.dpdk_icmp6.type = hdr.inner_l4.icmp.type;
                              l4_union.dpdk_icmp6.code = hdr.inner_l4.icmp.code;
                          }
                      }
                      vxlan_vni.vni = hdr.tunnel.vxlan.vni;
                  }
                  ovs_exact.apply();
              }
              else {
                  if (hdr.ethernet.isValid()) {
                      eth.dst = hdr.ethernet.dmac;
                      eth.src = hdr.ethernet.smac;
                      eth.eth_type = hdr.ethernet.eth_type;
                  }
                  if (hdr.vlan.isValid()) {
                      eth.eth_type = hdr.vlan.eth_type;
                      vlan.vlan_tci = (bit<16>)hdr.vlan.vid;
                  }
                  if (hdr.l3.ipv4.isValid() || hdr.l3.ipv6.isValid()) {
                      if (hdr.l3.ipv4.isValid()) {
                          l3_union.dpdk_ipv4.setValid();
                          l3_union.dpdk_ipv4.protocol = hdr.l3.ipv4.protocol;
                          l3_union.dpdk_ipv4.sip = hdr.l3.ipv4.sip;
                          l3_union.dpdk_ipv4.dip = hdr.l3.ipv4.dip;
                      } else if (hdr.l3.ipv6.isValid()) {
                          l3_union.dpdk_ipv6.setValid();
                          l3_union.dpdk_ipv6.next_header = hdr.l3.ipv6.next_header;
                          l3_union.dpdk_ipv6.sip = hdr.l3.ipv6.sip;
                          l3_union.dpdk_ipv6.dip = hdr.l3.ipv6.dip;
                      }
                      if (hdr.l4.udp.isValid()) {
                          l4_union.dpdk_udp.setValid();
                          l4_union.dpdk_udp.src_port = hdr.l4.udp.src_port;
                          l4_union.dpdk_udp.dst_port = hdr.l4.udp.dst_port;
                      } else if (hdr.l4.tcp.isValid()) {
                          l4_union.dpdk_tcp.setValid();
                          l4_union.dpdk_tcp.src_port = hdr.l4.tcp.src_port;
                          l4_union.dpdk_tcp.dst_port = hdr.l4.tcp.dst_port;
                      } else if (hdr.l4.icmp.isValid()) {
                          if (hdr.l3.ipv4.isValid()) {
                              l4_union.dpdk_icmp.setValid();
                              l4_union.dpdk_icmp.type = hdr.l4.icmp.type;
                              l4_union.dpdk_icmp.code = hdr.l4.icmp.code;
                              l4_union.dpdk_icmp.ident = hdr.l4.icmp.identifier;
                          } else if (hdr.l3.ipv6.isValid()) {
                              l4_union.dpdk_icmp6.setValid();
                              l4_union.dpdk_icmp6.type = hdr.l4.icmp.type;
                              l4_union.dpdk_icmp6.code = hdr.l4.icmp.code;
                          }
                      }
                  }
                  vxlan_vni.vni = 0;
                  ovs_exact.apply();
              }
      }
  5. Implement the PostPipe based on the network packet processing flow. The PostPipe can have no processing logic.

    Code example:

    control PostPipe(in hdr_fix_headers hdr) {
        apply {}
    }
  6. Assemble components such as the parser and controls.

    Code example:

    Switch(
            TX_OVS(hdr_fix_parser(),MainPipe(),PostPipe()),
            RX_OVS(hdr_fix_parser(),MainPipe(),PostPipe())
    ) main;