854 lines
24 KiB
C
854 lines
24 KiB
C
|
// SPDX-License-Identifier: GPL-2.0
|
||
|
/* Copyright (c) 2020, Intel Corporation. */
|
||
|
|
||
|
/* flow director ethtool support for iavf */
|
||
|
|
||
|
#include "iavf.h"
|
||
|
|
||
|
#define GTPU_PORT 2152
|
||
|
#define NAT_T_ESP_PORT 4500
|
||
|
#define PFCP_PORT 8805
|
||
|
|
||
|
static const struct in6_addr ipv6_addr_full_mask = {
|
||
|
.in6_u = {
|
||
|
.u6_addr8 = {
|
||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
static const struct in6_addr ipv6_addr_zero_mask = {
|
||
|
.in6_u = {
|
||
|
.u6_addr8 = {
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* iavf_validate_fdir_fltr_masks - validate Flow Director filter fields masks
|
||
|
* @adapter: pointer to the VF adapter structure
|
||
|
* @fltr: Flow Director filter data structure
|
||
|
*
|
||
|
* Returns 0 if all masks of packet fields are either full or empty. Returns
|
||
|
* error on at least one partial mask.
|
||
|
*/
|
||
|
int iavf_validate_fdir_fltr_masks(struct iavf_adapter *adapter,
|
||
|
struct iavf_fdir_fltr *fltr)
|
||
|
{
|
||
|
if (fltr->eth_mask.etype && fltr->eth_mask.etype != htons(U16_MAX))
|
||
|
goto partial_mask;
|
||
|
|
||
|
if (fltr->ip_ver == 4) {
|
||
|
if (fltr->ip_mask.v4_addrs.src_ip &&
|
||
|
fltr->ip_mask.v4_addrs.src_ip != htonl(U32_MAX))
|
||
|
goto partial_mask;
|
||
|
|
||
|
if (fltr->ip_mask.v4_addrs.dst_ip &&
|
||
|
fltr->ip_mask.v4_addrs.dst_ip != htonl(U32_MAX))
|
||
|
goto partial_mask;
|
||
|
|
||
|
if (fltr->ip_mask.tos && fltr->ip_mask.tos != U8_MAX)
|
||
|
goto partial_mask;
|
||
|
} else if (fltr->ip_ver == 6) {
|
||
|
if (memcmp(&fltr->ip_mask.v6_addrs.src_ip, &ipv6_addr_zero_mask,
|
||
|
sizeof(struct in6_addr)) &&
|
||
|
memcmp(&fltr->ip_mask.v6_addrs.src_ip, &ipv6_addr_full_mask,
|
||
|
sizeof(struct in6_addr)))
|
||
|
goto partial_mask;
|
||
|
|
||
|
if (memcmp(&fltr->ip_mask.v6_addrs.dst_ip, &ipv6_addr_zero_mask,
|
||
|
sizeof(struct in6_addr)) &&
|
||
|
memcmp(&fltr->ip_mask.v6_addrs.dst_ip, &ipv6_addr_full_mask,
|
||
|
sizeof(struct in6_addr)))
|
||
|
goto partial_mask;
|
||
|
|
||
|
if (fltr->ip_mask.tclass && fltr->ip_mask.tclass != U8_MAX)
|
||
|
goto partial_mask;
|
||
|
}
|
||
|
|
||
|
if (fltr->ip_mask.proto && fltr->ip_mask.proto != U8_MAX)
|
||
|
goto partial_mask;
|
||
|
|
||
|
if (fltr->ip_mask.src_port && fltr->ip_mask.src_port != htons(U16_MAX))
|
||
|
goto partial_mask;
|
||
|
|
||
|
if (fltr->ip_mask.dst_port && fltr->ip_mask.dst_port != htons(U16_MAX))
|
||
|
goto partial_mask;
|
||
|
|
||
|
if (fltr->ip_mask.spi && fltr->ip_mask.spi != htonl(U32_MAX))
|
||
|
goto partial_mask;
|
||
|
|
||
|
if (fltr->ip_mask.l4_header &&
|
||
|
fltr->ip_mask.l4_header != htonl(U32_MAX))
|
||
|
goto partial_mask;
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
partial_mask:
|
||
|
dev_err(&adapter->pdev->dev, "Failed to add Flow Director filter, partial masks are not supported\n");
|
||
|
return -EOPNOTSUPP;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* iavf_pkt_udp_no_pay_len - the length of UDP packet without payload
|
||
|
* @fltr: Flow Director filter data structure
|
||
|
*/
|
||
|
static u16 iavf_pkt_udp_no_pay_len(struct iavf_fdir_fltr *fltr)
|
||
|
{
|
||
|
return sizeof(struct ethhdr) +
|
||
|
(fltr->ip_ver == 4 ? sizeof(struct iphdr) : sizeof(struct ipv6hdr)) +
|
||
|
sizeof(struct udphdr);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* iavf_fill_fdir_gtpu_hdr - fill the GTP-U protocol header
|
||
|
* @fltr: Flow Director filter data structure
|
||
|
* @proto_hdrs: Flow Director protocol headers data structure
|
||
|
*
|
||
|
* Returns 0 if the GTP-U protocol header is set successfully
|
||
|
*/
|
||
|
static int
|
||
|
iavf_fill_fdir_gtpu_hdr(struct iavf_fdir_fltr *fltr,
|
||
|
struct virtchnl_proto_hdrs *proto_hdrs)
|
||
|
{
|
||
|
struct virtchnl_proto_hdr *uhdr = &proto_hdrs->proto_hdr[proto_hdrs->count - 1];
|
||
|
struct virtchnl_proto_hdr *ghdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];
|
||
|
struct virtchnl_proto_hdr *ehdr = NULL; /* Extension Header if it exists */
|
||
|
u16 adj_offs, hdr_offs;
|
||
|
int i;
|
||
|
|
||
|
VIRTCHNL_SET_PROTO_HDR_TYPE(ghdr, GTPU_IP);
|
||
|
|
||
|
adj_offs = iavf_pkt_udp_no_pay_len(fltr);
|
||
|
|
||
|
for (i = 0; i < fltr->flex_cnt; i++) {
|
||
|
#define IAVF_GTPU_HDR_TEID_OFFS0 4
|
||
|
#define IAVF_GTPU_HDR_TEID_OFFS1 6
|
||
|
#define IAVF_GTPU_HDR_N_PDU_AND_NEXT_EXTHDR_OFFS 10
|
||
|
#define IAVF_GTPU_HDR_NEXT_EXTHDR_TYPE_MASK 0x00FF /* skip N_PDU */
|
||
|
/* PDU Session Container Extension Header (PSC) */
|
||
|
#define IAVF_GTPU_PSC_EXTHDR_TYPE 0x85
|
||
|
#define IAVF_GTPU_HDR_PSC_PDU_TYPE_AND_QFI_OFFS 13
|
||
|
#define IAVF_GTPU_HDR_PSC_PDU_QFI_MASK 0x3F /* skip Type */
|
||
|
#define IAVF_GTPU_EH_QFI_IDX 1
|
||
|
|
||
|
if (fltr->flex_words[i].offset < adj_offs)
|
||
|
return -EINVAL;
|
||
|
|
||
|
hdr_offs = fltr->flex_words[i].offset - adj_offs;
|
||
|
|
||
|
switch (hdr_offs) {
|
||
|
case IAVF_GTPU_HDR_TEID_OFFS0:
|
||
|
case IAVF_GTPU_HDR_TEID_OFFS1: {
|
||
|
__be16 *pay_word = (__be16 *)ghdr->buffer;
|
||
|
|
||
|
pay_word[hdr_offs >> 1] = htons(fltr->flex_words[i].word);
|
||
|
VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(ghdr, GTPU_IP, TEID);
|
||
|
}
|
||
|
break;
|
||
|
case IAVF_GTPU_HDR_N_PDU_AND_NEXT_EXTHDR_OFFS:
|
||
|
if ((fltr->flex_words[i].word &
|
||
|
IAVF_GTPU_HDR_NEXT_EXTHDR_TYPE_MASK) !=
|
||
|
IAVF_GTPU_PSC_EXTHDR_TYPE)
|
||
|
return -EOPNOTSUPP;
|
||
|
if (!ehdr)
|
||
|
ehdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];
|
||
|
VIRTCHNL_SET_PROTO_HDR_TYPE(ehdr, GTPU_EH);
|
||
|
break;
|
||
|
case IAVF_GTPU_HDR_PSC_PDU_TYPE_AND_QFI_OFFS:
|
||
|
if (!ehdr)
|
||
|
return -EINVAL;
|
||
|
ehdr->buffer[IAVF_GTPU_EH_QFI_IDX] =
|
||
|
fltr->flex_words[i].word &
|
||
|
IAVF_GTPU_HDR_PSC_PDU_QFI_MASK;
|
||
|
VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(ehdr, GTPU_EH, QFI);
|
||
|
break;
|
||
|
default:
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
uhdr->field_selector = 0; /* The PF ignores the UDP header fields */
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* iavf_fill_fdir_pfcp_hdr - fill the PFCP protocol header
|
||
|
* @fltr: Flow Director filter data structure
|
||
|
* @proto_hdrs: Flow Director protocol headers data structure
|
||
|
*
|
||
|
* Returns 0 if the PFCP protocol header is set successfully
|
||
|
*/
|
||
|
static int
|
||
|
iavf_fill_fdir_pfcp_hdr(struct iavf_fdir_fltr *fltr,
|
||
|
struct virtchnl_proto_hdrs *proto_hdrs)
|
||
|
{
|
||
|
struct virtchnl_proto_hdr *uhdr = &proto_hdrs->proto_hdr[proto_hdrs->count - 1];
|
||
|
struct virtchnl_proto_hdr *hdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];
|
||
|
u16 adj_offs, hdr_offs;
|
||
|
int i;
|
||
|
|
||
|
VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, PFCP);
|
||
|
|
||
|
adj_offs = iavf_pkt_udp_no_pay_len(fltr);
|
||
|
|
||
|
for (i = 0; i < fltr->flex_cnt; i++) {
|
||
|
#define IAVF_PFCP_HDR_SFIELD_AND_MSG_TYPE_OFFS 0
|
||
|
if (fltr->flex_words[i].offset < adj_offs)
|
||
|
return -EINVAL;
|
||
|
|
||
|
hdr_offs = fltr->flex_words[i].offset - adj_offs;
|
||
|
|
||
|
switch (hdr_offs) {
|
||
|
case IAVF_PFCP_HDR_SFIELD_AND_MSG_TYPE_OFFS:
|
||
|
hdr->buffer[0] = (fltr->flex_words[i].word >> 8) & 0xff;
|
||
|
VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, PFCP, S_FIELD);
|
||
|
break;
|
||
|
default:
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
uhdr->field_selector = 0; /* The PF ignores the UDP header fields */
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* iavf_fill_fdir_nat_t_esp_hdr - fill the NAT-T-ESP protocol header
|
||
|
* @fltr: Flow Director filter data structure
|
||
|
* @proto_hdrs: Flow Director protocol headers data structure
|
||
|
*
|
||
|
* Returns 0 if the NAT-T-ESP protocol header is set successfully
|
||
|
*/
|
||
|
static int
|
||
|
iavf_fill_fdir_nat_t_esp_hdr(struct iavf_fdir_fltr *fltr,
|
||
|
struct virtchnl_proto_hdrs *proto_hdrs)
|
||
|
{
|
||
|
struct virtchnl_proto_hdr *uhdr = &proto_hdrs->proto_hdr[proto_hdrs->count - 1];
|
||
|
struct virtchnl_proto_hdr *hdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];
|
||
|
u16 adj_offs, hdr_offs;
|
||
|
u32 spi = 0;
|
||
|
int i;
|
||
|
|
||
|
VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, ESP);
|
||
|
|
||
|
adj_offs = iavf_pkt_udp_no_pay_len(fltr);
|
||
|
|
||
|
for (i = 0; i < fltr->flex_cnt; i++) {
|
||
|
#define IAVF_NAT_T_ESP_SPI_OFFS0 0
|
||
|
#define IAVF_NAT_T_ESP_SPI_OFFS1 2
|
||
|
if (fltr->flex_words[i].offset < adj_offs)
|
||
|
return -EINVAL;
|
||
|
|
||
|
hdr_offs = fltr->flex_words[i].offset - adj_offs;
|
||
|
|
||
|
switch (hdr_offs) {
|
||
|
case IAVF_NAT_T_ESP_SPI_OFFS0:
|
||
|
spi |= fltr->flex_words[i].word << 16;
|
||
|
break;
|
||
|
case IAVF_NAT_T_ESP_SPI_OFFS1:
|
||
|
spi |= fltr->flex_words[i].word;
|
||
|
break;
|
||
|
default:
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!spi)
|
||
|
return -EOPNOTSUPP; /* Not support IKE Header Format with SPI 0 */
|
||
|
|
||
|
*(__be32 *)hdr->buffer = htonl(spi);
|
||
|
VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, ESP, SPI);
|
||
|
|
||
|
uhdr->field_selector = 0; /* The PF ignores the UDP header fields */
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* iavf_fill_fdir_udp_flex_pay_hdr - fill the UDP payload header
|
||
|
* @fltr: Flow Director filter data structure
|
||
|
* @proto_hdrs: Flow Director protocol headers data structure
|
||
|
*
|
||
|
* Returns 0 if the UDP payload defined protocol header is set successfully
|
||
|
*/
|
||
|
static int
|
||
|
iavf_fill_fdir_udp_flex_pay_hdr(struct iavf_fdir_fltr *fltr,
|
||
|
struct virtchnl_proto_hdrs *proto_hdrs)
|
||
|
{
|
||
|
int err;
|
||
|
|
||
|
switch (ntohs(fltr->ip_data.dst_port)) {
|
||
|
case GTPU_PORT:
|
||
|
err = iavf_fill_fdir_gtpu_hdr(fltr, proto_hdrs);
|
||
|
break;
|
||
|
case NAT_T_ESP_PORT:
|
||
|
err = iavf_fill_fdir_nat_t_esp_hdr(fltr, proto_hdrs);
|
||
|
break;
|
||
|
case PFCP_PORT:
|
||
|
err = iavf_fill_fdir_pfcp_hdr(fltr, proto_hdrs);
|
||
|
break;
|
||
|
default:
|
||
|
err = -EOPNOTSUPP;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* iavf_fill_fdir_ip4_hdr - fill the IPv4 protocol header
|
||
|
* @fltr: Flow Director filter data structure
|
||
|
* @proto_hdrs: Flow Director protocol headers data structure
|
||
|
*
|
||
|
* Returns 0 if the IPv4 protocol header is set successfully
|
||
|
*/
|
||
|
static int
|
||
|
iavf_fill_fdir_ip4_hdr(struct iavf_fdir_fltr *fltr,
|
||
|
struct virtchnl_proto_hdrs *proto_hdrs)
|
||
|
{
|
||
|
struct virtchnl_proto_hdr *hdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];
|
||
|
struct iphdr *iph = (struct iphdr *)hdr->buffer;
|
||
|
|
||
|
VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, IPV4);
|
||
|
|
||
|
if (fltr->ip_mask.tos == U8_MAX) {
|
||
|
iph->tos = fltr->ip_data.tos;
|
||
|
VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV4, DSCP);
|
||
|
}
|
||
|
|
||
|
if (fltr->ip_mask.proto == U8_MAX) {
|
||
|
iph->protocol = fltr->ip_data.proto;
|
||
|
VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV4, PROT);
|
||
|
}
|
||
|
|
||
|
if (fltr->ip_mask.v4_addrs.src_ip == htonl(U32_MAX)) {
|
||
|
iph->saddr = fltr->ip_data.v4_addrs.src_ip;
|
||
|
VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV4, SRC);
|
||
|
}
|
||
|
|
||
|
if (fltr->ip_mask.v4_addrs.dst_ip == htonl(U32_MAX)) {
|
||
|
iph->daddr = fltr->ip_data.v4_addrs.dst_ip;
|
||
|
VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV4, DST);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* iavf_fill_fdir_ip6_hdr - fill the IPv6 protocol header
|
||
|
* @fltr: Flow Director filter data structure
|
||
|
* @proto_hdrs: Flow Director protocol headers data structure
|
||
|
*
|
||
|
* Returns 0 if the IPv6 protocol header is set successfully
|
||
|
*/
|
||
|
static int
|
||
|
iavf_fill_fdir_ip6_hdr(struct iavf_fdir_fltr *fltr,
|
||
|
struct virtchnl_proto_hdrs *proto_hdrs)
|
||
|
{
|
||
|
struct virtchnl_proto_hdr *hdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];
|
||
|
struct ipv6hdr *iph = (struct ipv6hdr *)hdr->buffer;
|
||
|
|
||
|
VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, IPV6);
|
||
|
|
||
|
if (fltr->ip_mask.tclass == U8_MAX) {
|
||
|
iph->priority = (fltr->ip_data.tclass >> 4) & 0xF;
|
||
|
iph->flow_lbl[0] = (fltr->ip_data.tclass << 4) & 0xF0;
|
||
|
VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV6, TC);
|
||
|
}
|
||
|
|
||
|
if (fltr->ip_mask.proto == U8_MAX) {
|
||
|
iph->nexthdr = fltr->ip_data.proto;
|
||
|
VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV6, PROT);
|
||
|
}
|
||
|
|
||
|
if (!memcmp(&fltr->ip_mask.v6_addrs.src_ip, &ipv6_addr_full_mask,
|
||
|
sizeof(struct in6_addr))) {
|
||
|
memcpy(&iph->saddr, &fltr->ip_data.v6_addrs.src_ip,
|
||
|
sizeof(struct in6_addr));
|
||
|
VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV6, SRC);
|
||
|
}
|
||
|
|
||
|
if (!memcmp(&fltr->ip_mask.v6_addrs.dst_ip, &ipv6_addr_full_mask,
|
||
|
sizeof(struct in6_addr))) {
|
||
|
memcpy(&iph->daddr, &fltr->ip_data.v6_addrs.dst_ip,
|
||
|
sizeof(struct in6_addr));
|
||
|
VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV6, DST);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* iavf_fill_fdir_tcp_hdr - fill the TCP protocol header
|
||
|
* @fltr: Flow Director filter data structure
|
||
|
* @proto_hdrs: Flow Director protocol headers data structure
|
||
|
*
|
||
|
* Returns 0 if the TCP protocol header is set successfully
|
||
|
*/
|
||
|
static int
|
||
|
iavf_fill_fdir_tcp_hdr(struct iavf_fdir_fltr *fltr,
|
||
|
struct virtchnl_proto_hdrs *proto_hdrs)
|
||
|
{
|
||
|
struct virtchnl_proto_hdr *hdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];
|
||
|
struct tcphdr *tcph = (struct tcphdr *)hdr->buffer;
|
||
|
|
||
|
VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, TCP);
|
||
|
|
||
|
if (fltr->ip_mask.src_port == htons(U16_MAX)) {
|
||
|
tcph->source = fltr->ip_data.src_port;
|
||
|
VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, TCP, SRC_PORT);
|
||
|
}
|
||
|
|
||
|
if (fltr->ip_mask.dst_port == htons(U16_MAX)) {
|
||
|
tcph->dest = fltr->ip_data.dst_port;
|
||
|
VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, TCP, DST_PORT);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* iavf_fill_fdir_udp_hdr - fill the UDP protocol header
|
||
|
* @fltr: Flow Director filter data structure
|
||
|
* @proto_hdrs: Flow Director protocol headers data structure
|
||
|
*
|
||
|
* Returns 0 if the UDP protocol header is set successfully
|
||
|
*/
|
||
|
static int
|
||
|
iavf_fill_fdir_udp_hdr(struct iavf_fdir_fltr *fltr,
|
||
|
struct virtchnl_proto_hdrs *proto_hdrs)
|
||
|
{
|
||
|
struct virtchnl_proto_hdr *hdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];
|
||
|
struct udphdr *udph = (struct udphdr *)hdr->buffer;
|
||
|
|
||
|
VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, UDP);
|
||
|
|
||
|
if (fltr->ip_mask.src_port == htons(U16_MAX)) {
|
||
|
udph->source = fltr->ip_data.src_port;
|
||
|
VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, UDP, SRC_PORT);
|
||
|
}
|
||
|
|
||
|
if (fltr->ip_mask.dst_port == htons(U16_MAX)) {
|
||
|
udph->dest = fltr->ip_data.dst_port;
|
||
|
VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, UDP, DST_PORT);
|
||
|
}
|
||
|
|
||
|
if (!fltr->flex_cnt)
|
||
|
return 0;
|
||
|
|
||
|
return iavf_fill_fdir_udp_flex_pay_hdr(fltr, proto_hdrs);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* iavf_fill_fdir_sctp_hdr - fill the SCTP protocol header
|
||
|
* @fltr: Flow Director filter data structure
|
||
|
* @proto_hdrs: Flow Director protocol headers data structure
|
||
|
*
|
||
|
* Returns 0 if the SCTP protocol header is set successfully
|
||
|
*/
|
||
|
static int
|
||
|
iavf_fill_fdir_sctp_hdr(struct iavf_fdir_fltr *fltr,
|
||
|
struct virtchnl_proto_hdrs *proto_hdrs)
|
||
|
{
|
||
|
struct virtchnl_proto_hdr *hdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];
|
||
|
struct sctphdr *sctph = (struct sctphdr *)hdr->buffer;
|
||
|
|
||
|
VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, SCTP);
|
||
|
|
||
|
if (fltr->ip_mask.src_port == htons(U16_MAX)) {
|
||
|
sctph->source = fltr->ip_data.src_port;
|
||
|
VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, SCTP, SRC_PORT);
|
||
|
}
|
||
|
|
||
|
if (fltr->ip_mask.dst_port == htons(U16_MAX)) {
|
||
|
sctph->dest = fltr->ip_data.dst_port;
|
||
|
VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, SCTP, DST_PORT);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* iavf_fill_fdir_ah_hdr - fill the AH protocol header
|
||
|
* @fltr: Flow Director filter data structure
|
||
|
* @proto_hdrs: Flow Director protocol headers data structure
|
||
|
*
|
||
|
* Returns 0 if the AH protocol header is set successfully
|
||
|
*/
|
||
|
static int
|
||
|
iavf_fill_fdir_ah_hdr(struct iavf_fdir_fltr *fltr,
|
||
|
struct virtchnl_proto_hdrs *proto_hdrs)
|
||
|
{
|
||
|
struct virtchnl_proto_hdr *hdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];
|
||
|
struct ip_auth_hdr *ah = (struct ip_auth_hdr *)hdr->buffer;
|
||
|
|
||
|
VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, AH);
|
||
|
|
||
|
if (fltr->ip_mask.spi == htonl(U32_MAX)) {
|
||
|
ah->spi = fltr->ip_data.spi;
|
||
|
VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, AH, SPI);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* iavf_fill_fdir_esp_hdr - fill the ESP protocol header
|
||
|
* @fltr: Flow Director filter data structure
|
||
|
* @proto_hdrs: Flow Director protocol headers data structure
|
||
|
*
|
||
|
* Returns 0 if the ESP protocol header is set successfully
|
||
|
*/
|
||
|
static int
|
||
|
iavf_fill_fdir_esp_hdr(struct iavf_fdir_fltr *fltr,
|
||
|
struct virtchnl_proto_hdrs *proto_hdrs)
|
||
|
{
|
||
|
struct virtchnl_proto_hdr *hdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];
|
||
|
struct ip_esp_hdr *esph = (struct ip_esp_hdr *)hdr->buffer;
|
||
|
|
||
|
VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, ESP);
|
||
|
|
||
|
if (fltr->ip_mask.spi == htonl(U32_MAX)) {
|
||
|
esph->spi = fltr->ip_data.spi;
|
||
|
VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, ESP, SPI);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* iavf_fill_fdir_l4_hdr - fill the L4 protocol header
|
||
|
* @fltr: Flow Director filter data structure
|
||
|
* @proto_hdrs: Flow Director protocol headers data structure
|
||
|
*
|
||
|
* Returns 0 if the L4 protocol header is set successfully
|
||
|
*/
|
||
|
static int
|
||
|
iavf_fill_fdir_l4_hdr(struct iavf_fdir_fltr *fltr,
|
||
|
struct virtchnl_proto_hdrs *proto_hdrs)
|
||
|
{
|
||
|
struct virtchnl_proto_hdr *hdr;
|
||
|
__be32 *l4_4_data;
|
||
|
|
||
|
if (!fltr->ip_mask.proto) /* IPv4/IPv6 header only */
|
||
|
return 0;
|
||
|
|
||
|
hdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];
|
||
|
l4_4_data = (__be32 *)hdr->buffer;
|
||
|
|
||
|
/* L2TPv3 over IP with 'Session ID' */
|
||
|
if (fltr->ip_data.proto == 115 && fltr->ip_mask.l4_header == htonl(U32_MAX)) {
|
||
|
VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, L2TPV3);
|
||
|
VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, L2TPV3, SESS_ID);
|
||
|
|
||
|
*l4_4_data = fltr->ip_data.l4_header;
|
||
|
} else {
|
||
|
return -EOPNOTSUPP;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* iavf_fill_fdir_eth_hdr - fill the Ethernet protocol header
|
||
|
* @fltr: Flow Director filter data structure
|
||
|
* @proto_hdrs: Flow Director protocol headers data structure
|
||
|
*
|
||
|
* Returns 0 if the Ethernet protocol header is set successfully
|
||
|
*/
|
||
|
static int
|
||
|
iavf_fill_fdir_eth_hdr(struct iavf_fdir_fltr *fltr,
|
||
|
struct virtchnl_proto_hdrs *proto_hdrs)
|
||
|
{
|
||
|
struct virtchnl_proto_hdr *hdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];
|
||
|
struct ethhdr *ehdr = (struct ethhdr *)hdr->buffer;
|
||
|
|
||
|
VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, ETH);
|
||
|
|
||
|
if (fltr->eth_mask.etype == htons(U16_MAX)) {
|
||
|
if (fltr->eth_data.etype == htons(ETH_P_IP) ||
|
||
|
fltr->eth_data.etype == htons(ETH_P_IPV6))
|
||
|
return -EOPNOTSUPP;
|
||
|
|
||
|
ehdr->h_proto = fltr->eth_data.etype;
|
||
|
VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, ETH, ETHERTYPE);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* iavf_fill_fdir_add_msg - fill the Flow Director filter into virtchnl message
|
||
|
* @adapter: pointer to the VF adapter structure
|
||
|
* @fltr: Flow Director filter data structure
|
||
|
*
|
||
|
* Returns 0 if the add Flow Director virtchnl message is filled successfully
|
||
|
*/
|
||
|
int iavf_fill_fdir_add_msg(struct iavf_adapter *adapter, struct iavf_fdir_fltr *fltr)
|
||
|
{
|
||
|
struct virtchnl_fdir_add *vc_msg = &fltr->vc_add_msg;
|
||
|
struct virtchnl_proto_hdrs *proto_hdrs;
|
||
|
int err;
|
||
|
|
||
|
proto_hdrs = &vc_msg->rule_cfg.proto_hdrs;
|
||
|
|
||
|
err = iavf_fill_fdir_eth_hdr(fltr, proto_hdrs); /* L2 always exists */
|
||
|
if (err)
|
||
|
return err;
|
||
|
|
||
|
switch (fltr->flow_type) {
|
||
|
case IAVF_FDIR_FLOW_IPV4_TCP:
|
||
|
err = iavf_fill_fdir_ip4_hdr(fltr, proto_hdrs) |
|
||
|
iavf_fill_fdir_tcp_hdr(fltr, proto_hdrs);
|
||
|
break;
|
||
|
case IAVF_FDIR_FLOW_IPV4_UDP:
|
||
|
err = iavf_fill_fdir_ip4_hdr(fltr, proto_hdrs) |
|
||
|
iavf_fill_fdir_udp_hdr(fltr, proto_hdrs);
|
||
|
break;
|
||
|
case IAVF_FDIR_FLOW_IPV4_SCTP:
|
||
|
err = iavf_fill_fdir_ip4_hdr(fltr, proto_hdrs) |
|
||
|
iavf_fill_fdir_sctp_hdr(fltr, proto_hdrs);
|
||
|
break;
|
||
|
case IAVF_FDIR_FLOW_IPV4_AH:
|
||
|
err = iavf_fill_fdir_ip4_hdr(fltr, proto_hdrs) |
|
||
|
iavf_fill_fdir_ah_hdr(fltr, proto_hdrs);
|
||
|
break;
|
||
|
case IAVF_FDIR_FLOW_IPV4_ESP:
|
||
|
err = iavf_fill_fdir_ip4_hdr(fltr, proto_hdrs) |
|
||
|
iavf_fill_fdir_esp_hdr(fltr, proto_hdrs);
|
||
|
break;
|
||
|
case IAVF_FDIR_FLOW_IPV4_OTHER:
|
||
|
err = iavf_fill_fdir_ip4_hdr(fltr, proto_hdrs) |
|
||
|
iavf_fill_fdir_l4_hdr(fltr, proto_hdrs);
|
||
|
break;
|
||
|
case IAVF_FDIR_FLOW_IPV6_TCP:
|
||
|
err = iavf_fill_fdir_ip6_hdr(fltr, proto_hdrs) |
|
||
|
iavf_fill_fdir_tcp_hdr(fltr, proto_hdrs);
|
||
|
break;
|
||
|
case IAVF_FDIR_FLOW_IPV6_UDP:
|
||
|
err = iavf_fill_fdir_ip6_hdr(fltr, proto_hdrs) |
|
||
|
iavf_fill_fdir_udp_hdr(fltr, proto_hdrs);
|
||
|
break;
|
||
|
case IAVF_FDIR_FLOW_IPV6_SCTP:
|
||
|
err = iavf_fill_fdir_ip6_hdr(fltr, proto_hdrs) |
|
||
|
iavf_fill_fdir_sctp_hdr(fltr, proto_hdrs);
|
||
|
break;
|
||
|
case IAVF_FDIR_FLOW_IPV6_AH:
|
||
|
err = iavf_fill_fdir_ip6_hdr(fltr, proto_hdrs) |
|
||
|
iavf_fill_fdir_ah_hdr(fltr, proto_hdrs);
|
||
|
break;
|
||
|
case IAVF_FDIR_FLOW_IPV6_ESP:
|
||
|
err = iavf_fill_fdir_ip6_hdr(fltr, proto_hdrs) |
|
||
|
iavf_fill_fdir_esp_hdr(fltr, proto_hdrs);
|
||
|
break;
|
||
|
case IAVF_FDIR_FLOW_IPV6_OTHER:
|
||
|
err = iavf_fill_fdir_ip6_hdr(fltr, proto_hdrs) |
|
||
|
iavf_fill_fdir_l4_hdr(fltr, proto_hdrs);
|
||
|
break;
|
||
|
case IAVF_FDIR_FLOW_NON_IP_L2:
|
||
|
break;
|
||
|
default:
|
||
|
err = -EINVAL;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (err)
|
||
|
return err;
|
||
|
|
||
|
vc_msg->vsi_id = adapter->vsi.id;
|
||
|
vc_msg->rule_cfg.action_set.count = 1;
|
||
|
vc_msg->rule_cfg.action_set.actions[0].type = fltr->action;
|
||
|
vc_msg->rule_cfg.action_set.actions[0].act_conf.queue.index = fltr->q_index;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* iavf_fdir_flow_proto_name - get the flow protocol name
|
||
|
* @flow_type: Flow Director filter flow type
|
||
|
**/
|
||
|
static const char *iavf_fdir_flow_proto_name(enum iavf_fdir_flow_type flow_type)
|
||
|
{
|
||
|
switch (flow_type) {
|
||
|
case IAVF_FDIR_FLOW_IPV4_TCP:
|
||
|
case IAVF_FDIR_FLOW_IPV6_TCP:
|
||
|
return "TCP";
|
||
|
case IAVF_FDIR_FLOW_IPV4_UDP:
|
||
|
case IAVF_FDIR_FLOW_IPV6_UDP:
|
||
|
return "UDP";
|
||
|
case IAVF_FDIR_FLOW_IPV4_SCTP:
|
||
|
case IAVF_FDIR_FLOW_IPV6_SCTP:
|
||
|
return "SCTP";
|
||
|
case IAVF_FDIR_FLOW_IPV4_AH:
|
||
|
case IAVF_FDIR_FLOW_IPV6_AH:
|
||
|
return "AH";
|
||
|
case IAVF_FDIR_FLOW_IPV4_ESP:
|
||
|
case IAVF_FDIR_FLOW_IPV6_ESP:
|
||
|
return "ESP";
|
||
|
case IAVF_FDIR_FLOW_IPV4_OTHER:
|
||
|
case IAVF_FDIR_FLOW_IPV6_OTHER:
|
||
|
return "Other";
|
||
|
case IAVF_FDIR_FLOW_NON_IP_L2:
|
||
|
return "Ethernet";
|
||
|
default:
|
||
|
return NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* iavf_print_fdir_fltr
|
||
|
* @adapter: adapter structure
|
||
|
* @fltr: Flow Director filter to print
|
||
|
*
|
||
|
* Print the Flow Director filter
|
||
|
**/
|
||
|
void iavf_print_fdir_fltr(struct iavf_adapter *adapter, struct iavf_fdir_fltr *fltr)
|
||
|
{
|
||
|
const char *proto = iavf_fdir_flow_proto_name(fltr->flow_type);
|
||
|
|
||
|
if (!proto)
|
||
|
return;
|
||
|
|
||
|
switch (fltr->flow_type) {
|
||
|
case IAVF_FDIR_FLOW_IPV4_TCP:
|
||
|
case IAVF_FDIR_FLOW_IPV4_UDP:
|
||
|
case IAVF_FDIR_FLOW_IPV4_SCTP:
|
||
|
dev_info(&adapter->pdev->dev, "Rule ID: %u dst_ip: %pI4 src_ip %pI4 %s: dst_port %hu src_port %hu\n",
|
||
|
fltr->loc,
|
||
|
&fltr->ip_data.v4_addrs.dst_ip,
|
||
|
&fltr->ip_data.v4_addrs.src_ip,
|
||
|
proto,
|
||
|
ntohs(fltr->ip_data.dst_port),
|
||
|
ntohs(fltr->ip_data.src_port));
|
||
|
break;
|
||
|
case IAVF_FDIR_FLOW_IPV4_AH:
|
||
|
case IAVF_FDIR_FLOW_IPV4_ESP:
|
||
|
dev_info(&adapter->pdev->dev, "Rule ID: %u dst_ip: %pI4 src_ip %pI4 %s: SPI %u\n",
|
||
|
fltr->loc,
|
||
|
&fltr->ip_data.v4_addrs.dst_ip,
|
||
|
&fltr->ip_data.v4_addrs.src_ip,
|
||
|
proto,
|
||
|
ntohl(fltr->ip_data.spi));
|
||
|
break;
|
||
|
case IAVF_FDIR_FLOW_IPV4_OTHER:
|
||
|
dev_info(&adapter->pdev->dev, "Rule ID: %u dst_ip: %pI4 src_ip %pI4 proto: %u L4_bytes: 0x%x\n",
|
||
|
fltr->loc,
|
||
|
&fltr->ip_data.v4_addrs.dst_ip,
|
||
|
&fltr->ip_data.v4_addrs.src_ip,
|
||
|
fltr->ip_data.proto,
|
||
|
ntohl(fltr->ip_data.l4_header));
|
||
|
break;
|
||
|
case IAVF_FDIR_FLOW_IPV6_TCP:
|
||
|
case IAVF_FDIR_FLOW_IPV6_UDP:
|
||
|
case IAVF_FDIR_FLOW_IPV6_SCTP:
|
||
|
dev_info(&adapter->pdev->dev, "Rule ID: %u dst_ip: %pI6 src_ip %pI6 %s: dst_port %hu src_port %hu\n",
|
||
|
fltr->loc,
|
||
|
&fltr->ip_data.v6_addrs.dst_ip,
|
||
|
&fltr->ip_data.v6_addrs.src_ip,
|
||
|
proto,
|
||
|
ntohs(fltr->ip_data.dst_port),
|
||
|
ntohs(fltr->ip_data.src_port));
|
||
|
break;
|
||
|
case IAVF_FDIR_FLOW_IPV6_AH:
|
||
|
case IAVF_FDIR_FLOW_IPV6_ESP:
|
||
|
dev_info(&adapter->pdev->dev, "Rule ID: %u dst_ip: %pI6 src_ip %pI6 %s: SPI %u\n",
|
||
|
fltr->loc,
|
||
|
&fltr->ip_data.v6_addrs.dst_ip,
|
||
|
&fltr->ip_data.v6_addrs.src_ip,
|
||
|
proto,
|
||
|
ntohl(fltr->ip_data.spi));
|
||
|
break;
|
||
|
case IAVF_FDIR_FLOW_IPV6_OTHER:
|
||
|
dev_info(&adapter->pdev->dev, "Rule ID: %u dst_ip: %pI6 src_ip %pI6 proto: %u L4_bytes: 0x%x\n",
|
||
|
fltr->loc,
|
||
|
&fltr->ip_data.v6_addrs.dst_ip,
|
||
|
&fltr->ip_data.v6_addrs.src_ip,
|
||
|
fltr->ip_data.proto,
|
||
|
ntohl(fltr->ip_data.l4_header));
|
||
|
break;
|
||
|
case IAVF_FDIR_FLOW_NON_IP_L2:
|
||
|
dev_info(&adapter->pdev->dev, "Rule ID: %u eth_type: 0x%x\n",
|
||
|
fltr->loc,
|
||
|
ntohs(fltr->eth_data.etype));
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* iavf_fdir_is_dup_fltr - test if filter is already in list
|
||
|
* @adapter: pointer to the VF adapter structure
|
||
|
* @fltr: Flow Director filter data structure
|
||
|
*
|
||
|
* Returns true if the filter is found in the list
|
||
|
*/
|
||
|
bool iavf_fdir_is_dup_fltr(struct iavf_adapter *adapter, struct iavf_fdir_fltr *fltr)
|
||
|
{
|
||
|
struct iavf_fdir_fltr *tmp;
|
||
|
bool ret = false;
|
||
|
|
||
|
spin_lock_bh(&adapter->fdir_fltr_lock);
|
||
|
list_for_each_entry(tmp, &adapter->fdir_list_head, list) {
|
||
|
if (tmp->flow_type != fltr->flow_type)
|
||
|
continue;
|
||
|
|
||
|
if (!memcmp(&tmp->eth_data, &fltr->eth_data,
|
||
|
sizeof(fltr->eth_data)) &&
|
||
|
!memcmp(&tmp->ip_data, &fltr->ip_data,
|
||
|
sizeof(fltr->ip_data)) &&
|
||
|
!memcmp(&tmp->ext_data, &fltr->ext_data,
|
||
|
sizeof(fltr->ext_data))) {
|
||
|
ret = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
spin_unlock_bh(&adapter->fdir_fltr_lock);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* iavf_find_fdir_fltr_by_loc - find filter with location
|
||
|
* @adapter: pointer to the VF adapter structure
|
||
|
* @loc: location to find.
|
||
|
*
|
||
|
* Returns pointer to Flow Director filter if found or null
|
||
|
*/
|
||
|
struct iavf_fdir_fltr *iavf_find_fdir_fltr_by_loc(struct iavf_adapter *adapter, u32 loc)
|
||
|
{
|
||
|
struct iavf_fdir_fltr *rule;
|
||
|
|
||
|
list_for_each_entry(rule, &adapter->fdir_list_head, list)
|
||
|
if (rule->loc == loc)
|
||
|
return rule;
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* iavf_fdir_list_add_fltr - add a new node to the flow director filter list
|
||
|
* @adapter: pointer to the VF adapter structure
|
||
|
* @fltr: filter node to add to structure
|
||
|
*/
|
||
|
void iavf_fdir_list_add_fltr(struct iavf_adapter *adapter, struct iavf_fdir_fltr *fltr)
|
||
|
{
|
||
|
struct iavf_fdir_fltr *rule, *parent = NULL;
|
||
|
|
||
|
list_for_each_entry(rule, &adapter->fdir_list_head, list) {
|
||
|
if (rule->loc >= fltr->loc)
|
||
|
break;
|
||
|
parent = rule;
|
||
|
}
|
||
|
|
||
|
if (parent)
|
||
|
list_add(&fltr->list, &parent->list);
|
||
|
else
|
||
|
list_add(&fltr->list, &adapter->fdir_list_head);
|
||
|
}
|