Commit b7cf9661 authored by Naveen Mamindlapalli's avatar Naveen Mamindlapalli Committed by Jakub Kicinski

octeontx2-pf: Add flow classification using IP next level protocol

This patch adds support to install flow rules using ipv4 proto or
ipv6 next header field to distinguish between tcp/udp/sctp/esp/ah
flows when user doesn't specify the other match creteria related to
the flow such as tcp/udp/sctp source port and destination port, ah/esp
spi value. This is achieved by matching the layer type extracted by
NPC HW into the search key. Modified the driver to use Ethertype as
match criteria when user doesn't specify source IP address and dest
IP address. The esp/ah flow rule matching using security parameter
index (spi) is not supported as of now since the field is not extracted
into MCAM search key. Modified npc_check_field function to return bool.
Signed-off-by: default avatarNaveen Mamindlapalli <naveenm@marvell.com>
Signed-off-by: default avatarSunil Goutham <sgoutham@marvell.com>
Link: https://lore.kernel.org/r/20210111112537.3277-1-naveenm@marvell.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 324cefaf
......@@ -162,6 +162,11 @@ enum key_fields {
NPC_DIP_IPV4,
NPC_SIP_IPV6,
NPC_DIP_IPV6,
NPC_IPPROTO_TCP,
NPC_IPPROTO_UDP,
NPC_IPPROTO_SCTP,
NPC_IPPROTO_AH,
NPC_IPPROTO_ESP,
NPC_SPORT_TCP,
NPC_DPORT_TCP,
NPC_SPORT_UDP,
......
......@@ -1757,6 +1757,7 @@ static void rvu_dbg_npc_mcam_show_flows(struct seq_file *s,
seq_printf(s, "mask 0x%x\n", ntohs(rule->mask.dport));
break;
default:
seq_puts(s, "\n");
break;
}
}
......
......@@ -26,6 +26,11 @@ static const char * const npc_flow_names[] = {
[NPC_DIP_IPV4] = "ipv4 destination ip",
[NPC_SIP_IPV6] = "ipv6 source ip",
[NPC_DIP_IPV6] = "ipv6 destination ip",
[NPC_IPPROTO_TCP] = "ip proto tcp",
[NPC_IPPROTO_UDP] = "ip proto udp",
[NPC_IPPROTO_SCTP] = "ip proto sctp",
[NPC_IPPROTO_AH] = "ip proto AH",
[NPC_IPPROTO_ESP] = "ip proto ESP",
[NPC_SPORT_TCP] = "tcp source port",
[NPC_DPORT_TCP] = "tcp destination port",
[NPC_SPORT_UDP] = "udp source port",
......@@ -212,13 +217,13 @@ static bool npc_check_overlap(struct rvu *rvu, int blkaddr,
return false;
}
static int npc_check_field(struct rvu *rvu, int blkaddr, enum key_fields type,
static bool npc_check_field(struct rvu *rvu, int blkaddr, enum key_fields type,
u8 intf)
{
if (!npc_is_field_present(rvu, type, intf) ||
npc_check_overlap(rvu, blkaddr, type, 0, intf))
return -EOPNOTSUPP;
return 0;
return false;
return true;
}
static void npc_scan_parse_result(struct npc_mcam *mcam, u8 bit_number,
......@@ -448,14 +453,13 @@ static void npc_set_features(struct rvu *rvu, int blkaddr, u8 intf)
struct npc_mcam *mcam = &rvu->hw->mcam;
u64 *features = &mcam->rx_features;
u64 tcp_udp_sctp;
int err, hdr;
int hdr;
if (is_npc_intf_tx(intf))
features = &mcam->tx_features;
for (hdr = NPC_DMAC; hdr < NPC_HEADER_FIELDS_MAX; hdr++) {
err = npc_check_field(rvu, blkaddr, hdr, intf);
if (!err)
if (npc_check_field(rvu, blkaddr, hdr, intf))
*features |= BIT_ULL(hdr);
}
......@@ -464,13 +468,26 @@ static void npc_set_features(struct rvu *rvu, int blkaddr, u8 intf)
BIT_ULL(NPC_SPORT_SCTP) | BIT_ULL(NPC_DPORT_SCTP);
/* for tcp/udp/sctp corresponding layer type should be in the key */
if (*features & tcp_udp_sctp)
if (npc_check_field(rvu, blkaddr, NPC_LD, intf))
if (*features & tcp_udp_sctp) {
if (!npc_check_field(rvu, blkaddr, NPC_LD, intf))
*features &= ~tcp_udp_sctp;
else
*features |= BIT_ULL(NPC_IPPROTO_TCP) |
BIT_ULL(NPC_IPPROTO_UDP) |
BIT_ULL(NPC_IPPROTO_SCTP);
}
/* for AH, check if corresponding layer type is present in the key */
if (npc_check_field(rvu, blkaddr, NPC_LD, intf))
*features |= BIT_ULL(NPC_IPPROTO_AH);
/* for ESP, check if corresponding layer type is present in the key */
if (npc_check_field(rvu, blkaddr, NPC_LE, intf))
*features |= BIT_ULL(NPC_IPPROTO_ESP);
/* for vlan corresponding layer type should be in the key */
if (*features & BIT_ULL(NPC_OUTER_VID))
if (npc_check_field(rvu, blkaddr, NPC_LB, intf))
if (!npc_check_field(rvu, blkaddr, NPC_LB, intf))
*features &= ~BIT_ULL(NPC_OUTER_VID);
}
......@@ -743,13 +760,13 @@ static void npc_update_flow(struct rvu *rvu, struct mcam_entry *entry,
return;
/* For tcp/udp/sctp LTYPE should be present in entry */
if (features & (BIT_ULL(NPC_SPORT_TCP) | BIT_ULL(NPC_DPORT_TCP)))
if (features & BIT_ULL(NPC_IPPROTO_TCP))
npc_update_entry(rvu, NPC_LD, entry, NPC_LT_LD_TCP,
0, ~0ULL, 0, intf);
if (features & (BIT_ULL(NPC_SPORT_UDP) | BIT_ULL(NPC_DPORT_UDP)))
if (features & BIT_ULL(NPC_IPPROTO_UDP))
npc_update_entry(rvu, NPC_LD, entry, NPC_LT_LD_UDP,
0, ~0ULL, 0, intf);
if (features & (BIT_ULL(NPC_SPORT_SCTP) | BIT_ULL(NPC_DPORT_SCTP)))
if (features & BIT_ULL(NPC_IPPROTO_SCTP))
npc_update_entry(rvu, NPC_LD, entry, NPC_LT_LD_SCTP,
0, ~0ULL, 0, intf);
......@@ -758,6 +775,15 @@ static void npc_update_flow(struct rvu *rvu, struct mcam_entry *entry,
NPC_LT_LB_STAG_QINQ | NPC_LT_LB_CTAG, 0,
NPC_LT_LB_STAG_QINQ & NPC_LT_LB_CTAG, 0, intf);
/* For AH, LTYPE should be present in entry */
if (features & BIT_ULL(NPC_IPPROTO_AH))
npc_update_entry(rvu, NPC_LD, entry, NPC_LT_LD_AH,
0, ~0ULL, 0, intf);
/* For ESP, LTYPE should be present in entry */
if (features & BIT_ULL(NPC_IPPROTO_ESP))
npc_update_entry(rvu, NPC_LE, entry, NPC_LT_LE_ESP,
0, ~0ULL, 0, intf);
#define NPC_WRITE_FLOW(field, member, val_lo, val_hi, mask_lo, mask_hi) \
do { \
if (features & BIT_ULL((field))) { \
......
......@@ -272,7 +272,7 @@ int otx2_get_all_flows(struct otx2_nic *pfvf, struct ethtool_rxnfc *nfc,
return err;
}
static void otx2_prepare_ipv4_flow(struct ethtool_rx_flow_spec *fsp,
static int otx2_prepare_ipv4_flow(struct ethtool_rx_flow_spec *fsp,
struct npc_install_flow_req *req,
u32 flow_type)
{
......@@ -280,6 +280,8 @@ static void otx2_prepare_ipv4_flow(struct ethtool_rx_flow_spec *fsp,
struct ethtool_usrip4_spec *ipv4_usr_hdr = &fsp->h_u.usr_ip4_spec;
struct ethtool_tcpip4_spec *ipv4_l4_mask = &fsp->m_u.tcp_ip4_spec;
struct ethtool_tcpip4_spec *ipv4_l4_hdr = &fsp->h_u.tcp_ip4_spec;
struct ethtool_ah_espip4_spec *ah_esp_hdr = &fsp->h_u.ah_ip4_spec;
struct ethtool_ah_espip4_spec *ah_esp_mask = &fsp->m_u.ah_ip4_spec;
struct flow_msg *pmask = &req->mask;
struct flow_msg *pkt = &req->packet;
......@@ -299,10 +301,16 @@ static void otx2_prepare_ipv4_flow(struct ethtool_rx_flow_spec *fsp,
sizeof(pmask->ip4dst));
req->features |= BIT_ULL(NPC_DIP_IPV4);
}
pkt->etype = cpu_to_be16(ETH_P_IP);
pmask->etype = cpu_to_be16(0xFFFF);
req->features |= BIT_ULL(NPC_ETYPE);
break;
case TCP_V4_FLOW:
case UDP_V4_FLOW:
case SCTP_V4_FLOW:
pkt->etype = cpu_to_be16(ETH_P_IP);
pmask->etype = cpu_to_be16(0xFFFF);
req->features |= BIT_ULL(NPC_ETYPE);
if (ipv4_l4_mask->ip4src) {
memcpy(&pkt->ip4src, &ipv4_l4_hdr->ip4src,
sizeof(pkt->ip4src));
......@@ -341,13 +349,51 @@ static void otx2_prepare_ipv4_flow(struct ethtool_rx_flow_spec *fsp,
else
req->features |= BIT_ULL(NPC_DPORT_SCTP);
}
if (flow_type == UDP_V4_FLOW)
req->features |= BIT_ULL(NPC_IPPROTO_UDP);
else if (flow_type == TCP_V4_FLOW)
req->features |= BIT_ULL(NPC_IPPROTO_TCP);
else
req->features |= BIT_ULL(NPC_IPPROTO_SCTP);
break;
case AH_V4_FLOW:
case ESP_V4_FLOW:
pkt->etype = cpu_to_be16(ETH_P_IP);
pmask->etype = cpu_to_be16(0xFFFF);
req->features |= BIT_ULL(NPC_ETYPE);
if (ah_esp_mask->ip4src) {
memcpy(&pkt->ip4src, &ah_esp_hdr->ip4src,
sizeof(pkt->ip4src));
memcpy(&pmask->ip4src, &ah_esp_mask->ip4src,
sizeof(pmask->ip4src));
req->features |= BIT_ULL(NPC_SIP_IPV4);
}
if (ah_esp_mask->ip4dst) {
memcpy(&pkt->ip4dst, &ah_esp_hdr->ip4dst,
sizeof(pkt->ip4dst));
memcpy(&pmask->ip4dst, &ah_esp_mask->ip4dst,
sizeof(pmask->ip4dst));
req->features |= BIT_ULL(NPC_DIP_IPV4);
}
/* NPC profile doesn't extract AH/ESP header fields */
if ((ah_esp_mask->spi & ah_esp_hdr->spi) ||
(ah_esp_mask->tos & ah_esp_mask->tos))
return -EOPNOTSUPP;
if (flow_type == AH_V4_FLOW)
req->features |= BIT_ULL(NPC_IPPROTO_AH);
else
req->features |= BIT_ULL(NPC_IPPROTO_ESP);
break;
default:
break;
}
return 0;
}
static void otx2_prepare_ipv6_flow(struct ethtool_rx_flow_spec *fsp,
static int otx2_prepare_ipv6_flow(struct ethtool_rx_flow_spec *fsp,
struct npc_install_flow_req *req,
u32 flow_type)
{
......@@ -355,6 +401,8 @@ static void otx2_prepare_ipv6_flow(struct ethtool_rx_flow_spec *fsp,
struct ethtool_usrip6_spec *ipv6_usr_hdr = &fsp->h_u.usr_ip6_spec;
struct ethtool_tcpip6_spec *ipv6_l4_mask = &fsp->m_u.tcp_ip6_spec;
struct ethtool_tcpip6_spec *ipv6_l4_hdr = &fsp->h_u.tcp_ip6_spec;
struct ethtool_ah_espip6_spec *ah_esp_hdr = &fsp->h_u.ah_ip6_spec;
struct ethtool_ah_espip6_spec *ah_esp_mask = &fsp->m_u.ah_ip6_spec;
struct flow_msg *pmask = &req->mask;
struct flow_msg *pkt = &req->packet;
......@@ -374,10 +422,16 @@ static void otx2_prepare_ipv6_flow(struct ethtool_rx_flow_spec *fsp,
sizeof(pmask->ip6dst));
req->features |= BIT_ULL(NPC_DIP_IPV6);
}
pkt->etype = cpu_to_be16(ETH_P_IPV6);
pmask->etype = cpu_to_be16(0xFFFF);
req->features |= BIT_ULL(NPC_ETYPE);
break;
case TCP_V6_FLOW:
case UDP_V6_FLOW:
case SCTP_V6_FLOW:
pkt->etype = cpu_to_be16(ETH_P_IPV6);
pmask->etype = cpu_to_be16(0xFFFF);
req->features |= BIT_ULL(NPC_ETYPE);
if (!ipv6_addr_any((struct in6_addr *)ipv6_l4_mask->ip6src)) {
memcpy(&pkt->ip6src, &ipv6_l4_hdr->ip6src,
sizeof(pkt->ip6src));
......@@ -416,10 +470,47 @@ static void otx2_prepare_ipv6_flow(struct ethtool_rx_flow_spec *fsp,
else
req->features |= BIT_ULL(NPC_DPORT_SCTP);
}
if (flow_type == UDP_V6_FLOW)
req->features |= BIT_ULL(NPC_IPPROTO_UDP);
else if (flow_type == TCP_V6_FLOW)
req->features |= BIT_ULL(NPC_IPPROTO_TCP);
else
req->features |= BIT_ULL(NPC_IPPROTO_SCTP);
break;
case AH_V6_FLOW:
case ESP_V6_FLOW:
pkt->etype = cpu_to_be16(ETH_P_IPV6);
pmask->etype = cpu_to_be16(0xFFFF);
req->features |= BIT_ULL(NPC_ETYPE);
if (!ipv6_addr_any((struct in6_addr *)ah_esp_hdr->ip6src)) {
memcpy(&pkt->ip6src, &ah_esp_hdr->ip6src,
sizeof(pkt->ip6src));
memcpy(&pmask->ip6src, &ah_esp_mask->ip6src,
sizeof(pmask->ip6src));
req->features |= BIT_ULL(NPC_SIP_IPV6);
}
if (!ipv6_addr_any((struct in6_addr *)ah_esp_hdr->ip6dst)) {
memcpy(&pkt->ip6dst, &ah_esp_hdr->ip6dst,
sizeof(pkt->ip6dst));
memcpy(&pmask->ip6dst, &ah_esp_mask->ip6dst,
sizeof(pmask->ip6dst));
req->features |= BIT_ULL(NPC_DIP_IPV6);
}
/* NPC profile doesn't extract AH/ESP header fields */
if ((ah_esp_mask->spi & ah_esp_hdr->spi) ||
(ah_esp_mask->tclass & ah_esp_mask->tclass))
return -EOPNOTSUPP;
if (flow_type == AH_V6_FLOW)
req->features |= BIT_ULL(NPC_IPPROTO_AH);
else
req->features |= BIT_ULL(NPC_IPPROTO_ESP);
default:
break;
}
return 0;
}
int otx2_prepare_flow_request(struct ethtool_rx_flow_spec *fsp,
......@@ -430,6 +521,7 @@ int otx2_prepare_flow_request(struct ethtool_rx_flow_spec *fsp,
struct flow_msg *pmask = &req->mask;
struct flow_msg *pkt = &req->packet;
u32 flow_type;
int ret;
flow_type = fsp->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT | FLOW_RSS);
switch (flow_type) {
......@@ -457,13 +549,21 @@ int otx2_prepare_flow_request(struct ethtool_rx_flow_spec *fsp,
case TCP_V4_FLOW:
case UDP_V4_FLOW:
case SCTP_V4_FLOW:
otx2_prepare_ipv4_flow(fsp, req, flow_type);
case AH_V4_FLOW:
case ESP_V4_FLOW:
ret = otx2_prepare_ipv4_flow(fsp, req, flow_type);
if (ret)
return ret;
break;
case IPV6_USER_FLOW:
case TCP_V6_FLOW:
case UDP_V6_FLOW:
case SCTP_V6_FLOW:
otx2_prepare_ipv6_flow(fsp, req, flow_type);
case AH_V6_FLOW:
case ESP_V6_FLOW:
ret = otx2_prepare_ipv6_flow(fsp, req, flow_type);
if (ret)
return ret;
break;
default:
return -EOPNOTSUPP;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment