+static struct i40e_customized_pctype *
+i40e_flow_fdir_find_customized_pctype(struct i40e_pf *pf, uint8_t pctype)
+{
+ struct i40e_customized_pctype *cus_pctype;
+ enum i40e_new_pctype i = I40E_CUSTOMIZED_GTPC;
+
+ for (; i < I40E_CUSTOMIZED_MAX; i++) {
+ cus_pctype = &pf->customized_pctype[i];
+ if (pctype == cus_pctype->pctype)
+ return cus_pctype;
+ }
+ return NULL;
+}
+
+static inline int
+i40e_flow_fdir_fill_eth_ip_head(struct i40e_pf *pf,
+ const struct i40e_fdir_input *fdir_input,
+ unsigned char *raw_pkt,
+ bool vlan)
+{
+ struct i40e_customized_pctype *cus_pctype = NULL;
+ static uint8_t vlan_frame[] = {0x81, 0, 0, 0};
+ uint16_t *ether_type;
+ uint8_t len = 2 * sizeof(struct rte_ether_addr);
+ struct rte_ipv4_hdr *ip;
+ struct rte_ipv6_hdr *ip6;
+ uint8_t pctype = fdir_input->pctype;
+ bool is_customized_pctype = fdir_input->flow_ext.customized_pctype;
+ static const uint8_t next_proto[] = {
+ [I40E_FILTER_PCTYPE_FRAG_IPV4] = IPPROTO_IP,
+ [I40E_FILTER_PCTYPE_NONF_IPV4_TCP] = IPPROTO_TCP,
+ [I40E_FILTER_PCTYPE_NONF_IPV4_UDP] = IPPROTO_UDP,
+ [I40E_FILTER_PCTYPE_NONF_IPV4_SCTP] = IPPROTO_SCTP,
+ [I40E_FILTER_PCTYPE_NONF_IPV4_OTHER] = IPPROTO_IP,
+ [I40E_FILTER_PCTYPE_FRAG_IPV6] = IPPROTO_NONE,
+ [I40E_FILTER_PCTYPE_NONF_IPV6_TCP] = IPPROTO_TCP,
+ [I40E_FILTER_PCTYPE_NONF_IPV6_UDP] = IPPROTO_UDP,
+ [I40E_FILTER_PCTYPE_NONF_IPV6_SCTP] = IPPROTO_SCTP,
+ [I40E_FILTER_PCTYPE_NONF_IPV6_OTHER] = IPPROTO_NONE,
+ };
+
+ raw_pkt += 2 * sizeof(struct rte_ether_addr);
+ if (vlan && fdir_input->flow_ext.vlan_tci) {
+ rte_memcpy(raw_pkt, vlan_frame, sizeof(vlan_frame));
+ rte_memcpy(raw_pkt + sizeof(uint16_t),
+ &fdir_input->flow_ext.vlan_tci,
+ sizeof(uint16_t));
+ raw_pkt += sizeof(vlan_frame);
+ len += sizeof(vlan_frame);
+ }
+ ether_type = (uint16_t *)raw_pkt;
+ raw_pkt += sizeof(uint16_t);
+ len += sizeof(uint16_t);
+
+ if (is_customized_pctype) {
+ cus_pctype = i40e_flow_fdir_find_customized_pctype(pf, pctype);
+ if (!cus_pctype) {
+ PMD_DRV_LOG(ERR, "unknown pctype %u.",
+ fdir_input->pctype);
+ return -1;
+ }
+ }
+
+ if (pctype == I40E_FILTER_PCTYPE_L2_PAYLOAD)
+ *ether_type = fdir_input->flow.l2_flow.ether_type;
+ else if (pctype == I40E_FILTER_PCTYPE_NONF_IPV4_TCP ||
+ pctype == I40E_FILTER_PCTYPE_NONF_IPV4_UDP ||
+ pctype == I40E_FILTER_PCTYPE_NONF_IPV4_SCTP ||
+ pctype == I40E_FILTER_PCTYPE_NONF_IPV4_OTHER ||
+ pctype == I40E_FILTER_PCTYPE_FRAG_IPV4 ||
+ is_customized_pctype) {
+ ip = (struct rte_ipv4_hdr *)raw_pkt;
+
+ *ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPv4);
+ ip->version_ihl = I40E_FDIR_IP_DEFAULT_VERSION_IHL;
+ /* set len to by default */
+ ip->total_length = rte_cpu_to_be_16(I40E_FDIR_IP_DEFAULT_LEN);
+ ip->time_to_live = fdir_input->flow.ip4_flow.ttl ?
+ fdir_input->flow.ip4_flow.ttl :
+ I40E_FDIR_IP_DEFAULT_TTL;
+ ip->type_of_service = fdir_input->flow.ip4_flow.tos;
+ /**
+ * The source and destination fields in the transmitted packet
+ * need to be presented in a reversed order with respect
+ * to the expected received packets.
+ */
+ ip->src_addr = fdir_input->flow.ip4_flow.dst_ip;
+ ip->dst_addr = fdir_input->flow.ip4_flow.src_ip;
+
+ if (!is_customized_pctype)
+ ip->next_proto_id = fdir_input->flow.ip4_flow.proto ?
+ fdir_input->flow.ip4_flow.proto :
+ next_proto[fdir_input->pctype];
+ else if (cus_pctype->index == I40E_CUSTOMIZED_GTPC ||
+ cus_pctype->index == I40E_CUSTOMIZED_GTPU_IPV4 ||
+ cus_pctype->index == I40E_CUSTOMIZED_GTPU_IPV6 ||
+ cus_pctype->index == I40E_CUSTOMIZED_GTPU)
+ ip->next_proto_id = IPPROTO_UDP;
+ len += sizeof(struct rte_ipv4_hdr);
+ } else if (pctype == I40E_FILTER_PCTYPE_NONF_IPV6_TCP ||
+ pctype == I40E_FILTER_PCTYPE_NONF_IPV6_UDP ||
+ pctype == I40E_FILTER_PCTYPE_NONF_IPV6_SCTP ||
+ pctype == I40E_FILTER_PCTYPE_NONF_IPV6_OTHER ||
+ pctype == I40E_FILTER_PCTYPE_FRAG_IPV6) {
+ ip6 = (struct rte_ipv6_hdr *)raw_pkt;
+
+ *ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPv6);
+ ip6->vtc_flow =
+ rte_cpu_to_be_32(I40E_FDIR_IPv6_DEFAULT_VTC_FLOW |
+ (fdir_input->flow.ipv6_flow.tc <<
+ I40E_FDIR_IPv6_TC_OFFSET));
+ ip6->payload_len =
+ rte_cpu_to_be_16(I40E_FDIR_IPv6_PAYLOAD_LEN);
+ ip6->proto = fdir_input->flow.ipv6_flow.proto ?
+ fdir_input->flow.ipv6_flow.proto :
+ next_proto[fdir_input->pctype];
+ ip6->hop_limits = fdir_input->flow.ipv6_flow.hop_limits ?
+ fdir_input->flow.ipv6_flow.hop_limits :
+ I40E_FDIR_IPv6_DEFAULT_HOP_LIMITS;
+ /**
+ * The source and destination fields in the transmitted packet
+ * need to be presented in a reversed order with respect
+ * to the expected received packets.
+ */
+ rte_memcpy(&ip6->src_addr,
+ &fdir_input->flow.ipv6_flow.dst_ip,
+ IPV6_ADDR_LEN);
+ rte_memcpy(&ip6->dst_addr,
+ &fdir_input->flow.ipv6_flow.src_ip,
+ IPV6_ADDR_LEN);
+ len += sizeof(struct rte_ipv6_hdr);
+ } else {
+ PMD_DRV_LOG(ERR, "unknown pctype %u.",
+ fdir_input->pctype);
+ return -1;
+ }
+
+ return len;
+}
+
+/**
+ * i40e_flow_fdir_construct_pkt - construct packet based on fields in input
+ * @pf: board private structure
+ * @fdir_input: input set of the flow director entry
+ * @raw_pkt: a packet to be constructed
+ */
+static int
+i40e_flow_fdir_construct_pkt(struct i40e_pf *pf,
+ const struct i40e_fdir_input *fdir_input,
+ unsigned char *raw_pkt)
+{
+ unsigned char *payload = NULL;
+ unsigned char *ptr;
+ struct udp_hdr *udp;
+ struct tcp_hdr *tcp;
+ struct sctp_hdr *sctp;
+ struct rte_flow_item_gtp *gtp;
+ struct rte_ipv4_hdr *gtp_ipv4;
+ struct rte_ipv6_hdr *gtp_ipv6;
+ uint8_t size, dst = 0;
+ uint8_t i, pit_idx, set_idx = I40E_FLXPLD_L4_IDX; /* use l4 by default*/
+ int len;
+ uint8_t pctype = fdir_input->pctype;
+ struct i40e_customized_pctype *cus_pctype;
+
+ /* raw pcket template - just copy contents of the raw packet */
+ if (fdir_input->flow_ext.pkt_template) {
+ memcpy(raw_pkt, fdir_input->flow.raw_flow.packet,
+ fdir_input->flow.raw_flow.length);
+ return 0;
+ }
+
+ /* fill the ethernet and IP head */
+ len = i40e_flow_fdir_fill_eth_ip_head(pf, fdir_input, raw_pkt,
+ !!fdir_input->flow_ext.vlan_tci);
+ if (len < 0)
+ return -EINVAL;
+
+ /* fill the L4 head */
+ if (pctype == I40E_FILTER_PCTYPE_NONF_IPV4_UDP) {
+ udp = (struct udp_hdr *)(raw_pkt + len);
+ payload = (unsigned char *)udp + sizeof(struct udp_hdr);
+ /**
+ * The source and destination fields in the transmitted packet
+ * need to be presented in a reversed order with respect
+ * to the expected received packets.
+ */
+ udp->src_port = fdir_input->flow.udp4_flow.dst_port;
+ udp->dst_port = fdir_input->flow.udp4_flow.src_port;
+ udp->dgram_len = rte_cpu_to_be_16(I40E_FDIR_UDP_DEFAULT_LEN);
+ } else if (pctype == I40E_FILTER_PCTYPE_NONF_IPV4_TCP) {
+ tcp = (struct tcp_hdr *)(raw_pkt + len);
+ payload = (unsigned char *)tcp + sizeof(struct tcp_hdr);
+ /**
+ * The source and destination fields in the transmitted packet
+ * need to be presented in a reversed order with respect
+ * to the expected received packets.
+ */
+ tcp->src_port = fdir_input->flow.tcp4_flow.dst_port;
+ tcp->dst_port = fdir_input->flow.tcp4_flow.src_port;
+ tcp->data_off = I40E_FDIR_TCP_DEFAULT_DATAOFF;
+ } else if (pctype == I40E_FILTER_PCTYPE_NONF_IPV4_SCTP) {
+ sctp = (struct sctp_hdr *)(raw_pkt + len);
+ payload = (unsigned char *)sctp + sizeof(struct sctp_hdr);
+ /**
+ * The source and destination fields in the transmitted packet
+ * need to be presented in a reversed order with respect
+ * to the expected received packets.
+ */
+ sctp->src_port = fdir_input->flow.sctp4_flow.dst_port;
+ sctp->dst_port = fdir_input->flow.sctp4_flow.src_port;
+ sctp->tag = fdir_input->flow.sctp4_flow.verify_tag;
+ } else if (pctype == I40E_FILTER_PCTYPE_NONF_IPV4_OTHER ||
+ pctype == I40E_FILTER_PCTYPE_FRAG_IPV4) {
+ payload = raw_pkt + len;
+ set_idx = I40E_FLXPLD_L3_IDX;
+ } else if (pctype == I40E_FILTER_PCTYPE_NONF_IPV6_UDP) {
+ udp = (struct udp_hdr *)(raw_pkt + len);
+ payload = (unsigned char *)udp + sizeof(struct udp_hdr);
+ /**
+ * The source and destination fields in the transmitted packet
+ * need to be presented in a reversed order with respect
+ * to the expected received packets.
+ */
+ udp->src_port = fdir_input->flow.udp6_flow.dst_port;
+ udp->dst_port = fdir_input->flow.udp6_flow.src_port;
+ udp->dgram_len = rte_cpu_to_be_16(I40E_FDIR_IPv6_PAYLOAD_LEN);
+ } else if (pctype == I40E_FILTER_PCTYPE_NONF_IPV6_TCP) {
+ tcp = (struct tcp_hdr *)(raw_pkt + len);
+ payload = (unsigned char *)tcp + sizeof(struct tcp_hdr);
+ /**
+ * The source and destination fields in the transmitted packet
+ * need to be presented in a reversed order with respect
+ * to the expected received packets.
+ */
+ tcp->data_off = I40E_FDIR_TCP_DEFAULT_DATAOFF;
+ tcp->src_port = fdir_input->flow.udp6_flow.dst_port;
+ tcp->dst_port = fdir_input->flow.udp6_flow.src_port;
+ } else if (pctype == I40E_FILTER_PCTYPE_NONF_IPV6_SCTP) {
+ sctp = (struct sctp_hdr *)(raw_pkt + len);
+ payload = (unsigned char *)sctp + sizeof(struct sctp_hdr);
+ /**
+ * The source and destination fields in the transmitted packet
+ * need to be presented in a reversed order with respect
+ * to the expected received packets.
+ */
+ sctp->src_port = fdir_input->flow.sctp6_flow.dst_port;
+ sctp->dst_port = fdir_input->flow.sctp6_flow.src_port;
+ sctp->tag = fdir_input->flow.sctp6_flow.verify_tag;
+ } else if (pctype == I40E_FILTER_PCTYPE_NONF_IPV6_OTHER ||
+ pctype == I40E_FILTER_PCTYPE_FRAG_IPV6) {
+ payload = raw_pkt + len;
+ set_idx = I40E_FLXPLD_L3_IDX;
+ } else if (pctype == I40E_FILTER_PCTYPE_L2_PAYLOAD) {
+ payload = raw_pkt + len;
+ /**
+ * ARP packet is a special case on which the payload
+ * starts after the whole ARP header
+ */
+ if (fdir_input->flow.l2_flow.ether_type ==
+ rte_cpu_to_be_16(RTE_ETHER_TYPE_ARP))
+ payload += sizeof(struct rte_arp_hdr);
+ set_idx = I40E_FLXPLD_L2_IDX;
+ } else if (fdir_input->flow_ext.customized_pctype) {
+ /* If customized pctype is used */
+ cus_pctype = i40e_flow_fdir_find_customized_pctype(pf, pctype);
+ if (cus_pctype->index == I40E_CUSTOMIZED_GTPC ||
+ cus_pctype->index == I40E_CUSTOMIZED_GTPU_IPV4 ||
+ cus_pctype->index == I40E_CUSTOMIZED_GTPU_IPV6 ||
+ cus_pctype->index == I40E_CUSTOMIZED_GTPU) {
+ udp = (struct udp_hdr *)(raw_pkt + len);
+ udp->dgram_len =
+ rte_cpu_to_be_16(I40E_FDIR_UDP_DEFAULT_LEN);
+
+ gtp = (struct rte_flow_item_gtp *)
+ ((unsigned char *)udp + sizeof(struct udp_hdr));
+ gtp->msg_len =
+ rte_cpu_to_be_16(I40E_FDIR_GTP_DEFAULT_LEN);
+ gtp->teid = fdir_input->flow.gtp_flow.teid;
+ gtp->msg_type = I40E_FDIR_GTP_MSG_TYPE_0X01;
+
+ /* GTP-C message type is not supported. */
+ if (cus_pctype->index == I40E_CUSTOMIZED_GTPC) {
+ udp->dst_port =
+ rte_cpu_to_be_16(I40E_FDIR_GTPC_DST_PORT);
+ gtp->v_pt_rsv_flags =
+ I40E_FDIR_GTP_VER_FLAG_0X32;
+ } else {
+ udp->dst_port =
+ rte_cpu_to_be_16(I40E_FDIR_GTPU_DST_PORT);
+ gtp->v_pt_rsv_flags =
+ I40E_FDIR_GTP_VER_FLAG_0X30;
+ }
+
+ if (cus_pctype->index == I40E_CUSTOMIZED_GTPU_IPV4) {
+ gtp->msg_type = I40E_FDIR_GTP_MSG_TYPE_0XFF;
+ gtp_ipv4 = (struct rte_ipv4_hdr *)
+ ((unsigned char *)gtp +
+ sizeof(struct rte_flow_item_gtp));
+ gtp_ipv4->version_ihl =
+ I40E_FDIR_IP_DEFAULT_VERSION_IHL;
+ gtp_ipv4->next_proto_id = IPPROTO_IP;
+ gtp_ipv4->total_length =
+ rte_cpu_to_be_16(
+ I40E_FDIR_INNER_IP_DEFAULT_LEN);
+ payload = (unsigned char *)gtp_ipv4 +
+ sizeof(struct rte_ipv4_hdr);
+ } else if (cus_pctype->index ==
+ I40E_CUSTOMIZED_GTPU_IPV6) {
+ gtp->msg_type = I40E_FDIR_GTP_MSG_TYPE_0XFF;
+ gtp_ipv6 = (struct rte_ipv6_hdr *)
+ ((unsigned char *)gtp +
+ sizeof(struct rte_flow_item_gtp));
+ gtp_ipv6->vtc_flow =
+ rte_cpu_to_be_32(
+ I40E_FDIR_IPv6_DEFAULT_VTC_FLOW |
+ (0 << I40E_FDIR_IPv6_TC_OFFSET));
+ gtp_ipv6->proto = IPPROTO_NONE;
+ gtp_ipv6->payload_len =
+ rte_cpu_to_be_16(
+ I40E_FDIR_INNER_IPV6_DEFAULT_LEN);
+ gtp_ipv6->hop_limits =
+ I40E_FDIR_IPv6_DEFAULT_HOP_LIMITS;
+ payload = (unsigned char *)gtp_ipv6 +
+ sizeof(struct rte_ipv6_hdr);
+ } else
+ payload = (unsigned char *)gtp +
+ sizeof(struct rte_flow_item_gtp);
+ }
+ } else {
+ PMD_DRV_LOG(ERR, "unknown pctype %u.",
+ fdir_input->pctype);
+ return -1;
+ }
+
+ /* fill the flexbytes to payload */
+ for (i = 0; i < I40E_MAX_FLXPLD_FIED; i++) {
+ pit_idx = set_idx * I40E_MAX_FLXPLD_FIED + i;
+ size = pf->fdir.flex_set[pit_idx].size;
+ if (size == 0)
+ continue;
+ dst = pf->fdir.flex_set[pit_idx].dst_offset * sizeof(uint16_t);
+ ptr = payload +
+ pf->fdir.flex_set[pit_idx].src_offset * sizeof(uint16_t);
+ (void)rte_memcpy(ptr,
+ &fdir_input->flow_ext.flexbytes[dst],
+ size * sizeof(uint16_t));
+ }
+
+ return 0;
+}
+