API: GTP-U Encapsulation and Decapsulation

Link-Inline™ Software User Guide

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

The SmartNIC can fully offload GTPv1-U encapsulation and decapsulation for UPF frame processing in the 5G core network architecture. Use this DPDK API example to configure the SmartNIC for GTPv1-U encapsulation and decapsulation.

GTPv1-U encapsulation on downlink

For GTP-U encapsulation, the MAC headers must be decapsulated using RTE_FLOW_ACTION_TYPE_RAW_DECAP first. After the MAC header is decapsulated, the GTP-U header can be encapsulated using RTE_FLOW_ACTION_TYPE_RAW_ENCAP. This example shows how to configure the SmartNIC to decapsulate the MAC header using the rte_flow_action_raw_decap structure and encapsulate the MAC/IP/UDP/GTPv1-U headers using the rte_flow_action_raw_encap structure.

/* 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(RTE_IPV4(192, 168, 0, 2)),
  .dst_addr = RTE_BE32(RTE_IPV4(192, 168, 1, 1)) }};
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 }};

/* Decap data */
struct rte_ether_hdr d_eth = {
  .ether_type = RTE_BE16(RTE_ETHER_TYPE_IPV4) };

uint8_t decap_data[sizeof(d_eth)];
memcpy(decap_data, &d_eth, sizeof(d_eth));

/* Encap data */
struct rte_ether_hdr e_eth = {
  .dst_addr.addr_bytes = "\xAA\xBB\xCC\xDD\xEE\xFF",
  .src_addr.addr_bytes = "\x11\xEE\xDD\xCC\xBB\xAA",
  .ether_type = RTE_BE16(RTE_ETHER_TYPE_IPV4) };
struct rte_ipv4_hdr e_ipv4 = {
  .version_ihl = 0x45,
  .src_addr = RTE_BE32(RTE_IPV4(10, 10, 0, 0)),
  .dst_addr = RTE_BE32(RTE_IPV4(10, 10, 0, 0)),
  .type_of_service = 10,
  .time_to_live = 0x40,
  .next_proto_id = IPPROTO_UDP };
struct rte_udp_hdr e_udp = {
  .dst_port = RTE_BE16(RTE_GTPU_UDP_PORT) };
struct rte_gtp_hdr e_gtp = {
  .ver = 1,
  .pt = 1,
  .e = 1,
  .msg_type = 255,
  .teid = RTE_BE32(0x00000000) };
struct rte_gtp_hdr_ext_word e_gtp_ext = {
  .next_ext = 0x85 };
struct gtp_pdu_session_container {
  uint8_t ext_hdr_len;
  uint8_t type_spare;
  uint8_t ppp_rqi_qfi;
  uint8_t next_ext;
  } e_gtp_psc = {
  .ext_hdr_len = 1,
  .type_spare = 0x10,
  .ppp_rqi_qfi = 6,
  .next_ext = 0 };

uint64_t encap_it = 0;
uint8_t encap_data[sizeof(e_eth) + sizeof(e_ipv4) + sizeof(e_udp) +
  sizeof(e_gtp) + sizeof(e_gtp_ext) + sizeof(e_gtp_psc)];
memcpy(encap_data + encap_it, &e_eth, sizeof(e_eth)); encap_it += sizeof(e_eth);
memcpy(encap_data + encap_it, &e_ipv4, sizeof(e_ipv4)); encap_it += sizeof(e_ipv4);
memcpy(encap_data + encap_it, &e_udp, sizeof(e_udp)); encap_it += sizeof(e_udp);
memcpy(encap_data + encap_it, &e_gtp, sizeof(e_gtp)); encap_it += sizeof(e_gtp);
memcpy(encap_data + encap_it, &e_gtp_ext, sizeof(e_gtp_ext)); encap_it += sizeof(e_gtp_ext);
memcpy(encap_data + encap_it, &e_gtp_psc, sizeof(e_gtp_psc));

/* Actions */
struct rte_flow_action_raw_decap raw_decap = { .data = decap_data, .size = sizeof(decap_data) };
struct rte_flow_action_raw_encap raw_encap = { .data = encap_data, .size = sizeof(encap_data) };
struct rte_flow_action_modify_field modify_field = {
  .operation = RTE_FLOW_MODIFY_SET,
  .dst = { .field = RTE_FLOW_FIELD_GTP_TEID },
  .src = { .field = RTE_FLOW_FIELD_VALUE, .value = "\x40\x41\x42\x43" },
  .width = 4};
struct rte_flow_action_port_id port_id = { .id = 1 };
struct rte_flow_action action[] = {
  [0] = { .type = RTE_FLOW_ACTION_TYPE_RAW_DECAP, .conf = &raw_decap },
  [1] = { .type = RTE_FLOW_ACTION_TYPE_RAW_ENCAP, .conf = &raw_encap },
  [2] = { .type = RTE_FLOW_ACTION_TYPE_MODIFY_FIELD, .conf = &modify_field },
  [3] = { .type = RTE_FLOW_ACTION_TYPE_PORT_ID, .conf = &port_id },
  [4] = { .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 */
}
Note: A flow rule with group 0 must be created before the flow rule in this API example is created. This step has been omitted in this example for brevity.

GTPv1-U decapsulation on uplink

For GTPv1-U decapsulation, the MAC/IP/UDP/GTPv-U headers must be decapsulated using RTE_FLOW_ACTION_TYPE_RAW_DECAP first. After the GTPv1-U header is decapsulated, the MAC header can be encapsulated using RTE_FLOW_ACTION_TYPE_RAW_ENCAP. This example shows how to decapsulate the MAC/IP/UDP/GTPv1-U headers using the rte_flow_action_raw_decap structure and encapsulate the MAC header using the rte_flow_action_raw_encap structure.

/* 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(RTE_IPV4(192, 168, 0, 2)), .dst_addr = RTE_BE32(RTE_IPV4(192, 168, 1, 1)) }};
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 }};

/* Decap data */
struct rte_ether_hdr d_eth = { .ether_type = RTE_BE16(RTE_ETHER_TYPE_IPV4) };
struct rte_ipv4_hdr d_ipv4 = { .version_ihl = 0x45, .next_proto_id = IPPROTO_UDP };
struct rte_udp_hdr d_udp = { .dst_port = RTE_BE16(RTE_GTPU_UDP_PORT) };
struct rte_gtp_hdr d_gtp = { .ver = 1, .pt = 1, .e = 1, .msg_type = 255};
struct rte_gtp_hdr_ext_word d_gtp_ext = { .next_ext = 0x85 };
struct gtp_pdu_session_container {
  uint8_t ext_hdr_len; 
  uint8_t type_spare;
  uint8_t ppp_rqi_qfi;
  uint8_t next_ext;
  } d_gtp_psc = {
  .ext_hdr_len = 1,
  .type_spare = 0x10};

uint64_t decap_it = 0;
uint8_t decap_data[sizeof(d_eth) + sizeof(d_ipv4) + sizeof(d_udp) + sizeof(d_gtp) + 
  sizeof(d_gtp_ext) + sizeof(d_gtp_psc)];
memcpy(decap_data + decap_it, &d_eth, sizeof(d_eth)); decap_it += sizeof(d_eth);
memcpy(decap_data + decap_it, &d_ipv4, sizeof(d_ipv4)); decap_it += sizeof(d_ipv4);
memcpy(decap_data + decap_it, &d_udp, sizeof(d_udp)); decap_it += sizeof(d_udp);
memcpy(decap_data + decap_it, &d_gtp, sizeof(d_gtp)); decap_it += sizeof(d_gtp);
memcpy(decap_data + decap_it, &d_gtp_ext, sizeof(d_gtp_ext)); decap_it += sizeof(d_gtp_ext);
memcpy(decap_data + decap_it, &d_gtp_psc, sizeof(d_gtp_psc));

/* Encap data */
struct rte_ether_hdr e_eth = {
  .dst_addr.addr_bytes = "\xAA\xBB\xCC\xDD\xEE\xFF",
  .src_addr.addr_bytes = "\x11\xEE\xDD\xCC\xBB\xAA",
  .ether_type = RTE_BE16(RTE_ETHER_TYPE_IPV4) };

uint8_t encap_data[sizeof(e_eth)];
memcpy(encap_data, &e_eth, sizeof(e_eth));

/* Actions */
struct rte_flow_action_raw_decap raw_decap = { .data = decap_data, .size = sizeof(decap_data) };
struct rte_flow_action_raw_encap raw_encap = { .data = encap_data, .size = sizeof(encap_data) };
struct rte_flow_action_port_id port_id = { .id = 1 };
struct rte_flow_action action[] = {
  [0] = { .type = RTE_FLOW_ACTION_TYPE_RAW_DECAP, .conf = &raw_decap },
  [1] = { .type = RTE_FLOW_ACTION_TYPE_RAW_ENCAP, .conf = &raw_encap },
  [2] = { .type = RTE_FLOW_ACTION_TYPE_PORT_ID, .conf = &port_id },
  [3] = { .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 */
}
Note: A flow rule with group 0 must be created before the flow rule in this API example is created. This step has been omitted in this example for brevity.