This new attribute enables applications to create flow rules that do not simply match traffic whose origin is specified in the pattern (e.g. some non-default physical port or VF), but actively affect it by applying the flow rule at the lowest possible level in the underlying device. It breaks ABI compatibility for the following public functions: - rte_flow_copy() - rte_flow_create() - rte_flow_validate() Signed-off-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>
1912 lines
52 KiB
C
1912 lines
52 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
* Copyright(c) 2010-2016 Intel Corporation
|
|
*/
|
|
|
|
#include <sys/queue.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <stdint.h>
|
|
#include <stdarg.h>
|
|
|
|
#include <rte_common.h>
|
|
#include <rte_interrupts.h>
|
|
#include <rte_byteorder.h>
|
|
#include <rte_log.h>
|
|
#include <rte_debug.h>
|
|
#include <rte_pci.h>
|
|
#include <rte_ether.h>
|
|
#include <rte_ethdev_driver.h>
|
|
#include <rte_ethdev_pci.h>
|
|
#include <rte_memory.h>
|
|
#include <rte_eal.h>
|
|
#include <rte_atomic.h>
|
|
#include <rte_malloc.h>
|
|
#include <rte_dev.h>
|
|
#include <rte_flow.h>
|
|
#include <rte_flow_driver.h>
|
|
|
|
#include "e1000_logs.h"
|
|
#include "base/e1000_api.h"
|
|
#include "e1000_ethdev.h"
|
|
|
|
#define NEXT_ITEM_OF_PATTERN(item, pattern, index) \
|
|
do { \
|
|
item = (pattern) + (index); \
|
|
while (item->type == RTE_FLOW_ITEM_TYPE_VOID) { \
|
|
(index)++; \
|
|
item = (pattern) + (index); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define NEXT_ITEM_OF_ACTION(act, actions, index) \
|
|
do { \
|
|
act = (actions) + (index); \
|
|
while (act->type == RTE_FLOW_ACTION_TYPE_VOID) {\
|
|
(index)++; \
|
|
act = (actions) + (index); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define IGB_FLEX_RAW_NUM 12
|
|
|
|
/**
|
|
* Please aware there's an asumption for all the parsers.
|
|
* rte_flow_item is using big endian, rte_flow_attr and
|
|
* rte_flow_action are using CPU order.
|
|
* Because the pattern is used to describe the packets,
|
|
* normally the packets should use network order.
|
|
*/
|
|
|
|
/**
|
|
* Parse the rule to see if it is a n-tuple rule.
|
|
* And get the n-tuple filter info BTW.
|
|
* pattern:
|
|
* The first not void item can be ETH or IPV4.
|
|
* The second not void item must be IPV4 if the first one is ETH.
|
|
* The third not void item must be UDP or TCP or SCTP
|
|
* The next not void item must be END.
|
|
* action:
|
|
* The first not void action should be QUEUE.
|
|
* The next not void action should be END.
|
|
* pattern example:
|
|
* ITEM Spec Mask
|
|
* ETH NULL NULL
|
|
* IPV4 src_addr 192.168.1.20 0xFFFFFFFF
|
|
* dst_addr 192.167.3.50 0xFFFFFFFF
|
|
* next_proto_id 17 0xFF
|
|
* UDP/TCP/ src_port 80 0xFFFF
|
|
* SCTP dst_port 80 0xFFFF
|
|
* END
|
|
* other members in mask and spec should set to 0x00.
|
|
* item->last should be NULL.
|
|
*/
|
|
static int
|
|
cons_parse_ntuple_filter(const struct rte_flow_attr *attr,
|
|
const struct rte_flow_item pattern[],
|
|
const struct rte_flow_action actions[],
|
|
struct rte_eth_ntuple_filter *filter,
|
|
struct rte_flow_error *error)
|
|
{
|
|
const struct rte_flow_item *item;
|
|
const struct rte_flow_action *act;
|
|
const struct rte_flow_item_ipv4 *ipv4_spec;
|
|
const struct rte_flow_item_ipv4 *ipv4_mask;
|
|
const struct rte_flow_item_tcp *tcp_spec;
|
|
const struct rte_flow_item_tcp *tcp_mask;
|
|
const struct rte_flow_item_udp *udp_spec;
|
|
const struct rte_flow_item_udp *udp_mask;
|
|
const struct rte_flow_item_sctp *sctp_spec;
|
|
const struct rte_flow_item_sctp *sctp_mask;
|
|
uint32_t index;
|
|
|
|
if (!pattern) {
|
|
rte_flow_error_set(error,
|
|
EINVAL, RTE_FLOW_ERROR_TYPE_ITEM_NUM,
|
|
NULL, "NULL pattern.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
if (!actions) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ACTION_NUM,
|
|
NULL, "NULL action.");
|
|
return -rte_errno;
|
|
}
|
|
if (!attr) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ATTR,
|
|
NULL, "NULL attribute.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
/* parse pattern */
|
|
index = 0;
|
|
|
|
/* the first not void item can be MAC or IPv4 */
|
|
NEXT_ITEM_OF_PATTERN(item, pattern, index);
|
|
|
|
if (item->type != RTE_FLOW_ITEM_TYPE_ETH &&
|
|
item->type != RTE_FLOW_ITEM_TYPE_IPV4) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
item, "Not supported by ntuple filter");
|
|
return -rte_errno;
|
|
}
|
|
/* Skip Ethernet */
|
|
if (item->type == RTE_FLOW_ITEM_TYPE_ETH) {
|
|
/*Not supported last point for range*/
|
|
if (item->last) {
|
|
rte_flow_error_set(error,
|
|
EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
|
|
item, "Not supported last point for range");
|
|
return -rte_errno;
|
|
}
|
|
/* if the first item is MAC, the content should be NULL */
|
|
if (item->spec || item->mask) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
item, "Not supported by ntuple filter");
|
|
return -rte_errno;
|
|
}
|
|
/* check if the next not void item is IPv4 */
|
|
index++;
|
|
NEXT_ITEM_OF_PATTERN(item, pattern, index);
|
|
if (item->type != RTE_FLOW_ITEM_TYPE_IPV4) {
|
|
rte_flow_error_set(error,
|
|
EINVAL, RTE_FLOW_ERROR_TYPE_ITEM,
|
|
item, "Not supported by ntuple filter");
|
|
return -rte_errno;
|
|
}
|
|
}
|
|
|
|
/* get the IPv4 info */
|
|
if (!item->spec || !item->mask) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
item, "Invalid ntuple mask");
|
|
return -rte_errno;
|
|
}
|
|
/* Not supported last point for range */
|
|
if (item->last) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
|
|
item, "Not supported last point for range");
|
|
return -rte_errno;
|
|
}
|
|
|
|
ipv4_mask = item->mask;
|
|
/**
|
|
* Only support src & dst addresses, protocol,
|
|
* others should be masked.
|
|
*/
|
|
|
|
if (ipv4_mask->hdr.version_ihl ||
|
|
ipv4_mask->hdr.type_of_service ||
|
|
ipv4_mask->hdr.total_length ||
|
|
ipv4_mask->hdr.packet_id ||
|
|
ipv4_mask->hdr.fragment_offset ||
|
|
ipv4_mask->hdr.time_to_live ||
|
|
ipv4_mask->hdr.hdr_checksum) {
|
|
rte_flow_error_set(error,
|
|
EINVAL, RTE_FLOW_ERROR_TYPE_ITEM,
|
|
item, "Not supported by ntuple filter");
|
|
return -rte_errno;
|
|
}
|
|
|
|
filter->dst_ip_mask = ipv4_mask->hdr.dst_addr;
|
|
filter->src_ip_mask = ipv4_mask->hdr.src_addr;
|
|
filter->proto_mask = ipv4_mask->hdr.next_proto_id;
|
|
|
|
ipv4_spec = item->spec;
|
|
filter->dst_ip = ipv4_spec->hdr.dst_addr;
|
|
filter->src_ip = ipv4_spec->hdr.src_addr;
|
|
filter->proto = ipv4_spec->hdr.next_proto_id;
|
|
|
|
/* check if the next not void item is TCP or UDP or SCTP */
|
|
index++;
|
|
NEXT_ITEM_OF_PATTERN(item, pattern, index);
|
|
if (item->type != RTE_FLOW_ITEM_TYPE_TCP &&
|
|
item->type != RTE_FLOW_ITEM_TYPE_UDP &&
|
|
item->type != RTE_FLOW_ITEM_TYPE_SCTP) {
|
|
memset(filter, 0, sizeof(struct rte_eth_ntuple_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
item, "Not supported by ntuple filter");
|
|
return -rte_errno;
|
|
}
|
|
|
|
/* Not supported last point for range */
|
|
if (item->last) {
|
|
memset(filter, 0, sizeof(struct rte_eth_ntuple_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
|
|
item, "Not supported last point for range");
|
|
return -rte_errno;
|
|
}
|
|
|
|
/* get the TCP/UDP/SCTP info */
|
|
if (item->type == RTE_FLOW_ITEM_TYPE_TCP) {
|
|
if (item->spec && item->mask) {
|
|
tcp_mask = item->mask;
|
|
|
|
/**
|
|
* Only support src & dst ports, tcp flags,
|
|
* others should be masked.
|
|
*/
|
|
if (tcp_mask->hdr.sent_seq ||
|
|
tcp_mask->hdr.recv_ack ||
|
|
tcp_mask->hdr.data_off ||
|
|
tcp_mask->hdr.rx_win ||
|
|
tcp_mask->hdr.cksum ||
|
|
tcp_mask->hdr.tcp_urp) {
|
|
memset(filter, 0,
|
|
sizeof(struct rte_eth_ntuple_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
item, "Not supported by ntuple filter");
|
|
return -rte_errno;
|
|
}
|
|
|
|
filter->dst_port_mask = tcp_mask->hdr.dst_port;
|
|
filter->src_port_mask = tcp_mask->hdr.src_port;
|
|
if (tcp_mask->hdr.tcp_flags == 0xFF) {
|
|
filter->flags |= RTE_NTUPLE_FLAGS_TCP_FLAG;
|
|
} else if (!tcp_mask->hdr.tcp_flags) {
|
|
filter->flags &= ~RTE_NTUPLE_FLAGS_TCP_FLAG;
|
|
} else {
|
|
memset(filter, 0,
|
|
sizeof(struct rte_eth_ntuple_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
item, "Not supported by ntuple filter");
|
|
return -rte_errno;
|
|
}
|
|
|
|
tcp_spec = item->spec;
|
|
filter->dst_port = tcp_spec->hdr.dst_port;
|
|
filter->src_port = tcp_spec->hdr.src_port;
|
|
filter->tcp_flags = tcp_spec->hdr.tcp_flags;
|
|
}
|
|
} else if (item->type == RTE_FLOW_ITEM_TYPE_UDP) {
|
|
if (item->spec && item->mask) {
|
|
udp_mask = item->mask;
|
|
|
|
/**
|
|
* Only support src & dst ports,
|
|
* others should be masked.
|
|
*/
|
|
if (udp_mask->hdr.dgram_len ||
|
|
udp_mask->hdr.dgram_cksum) {
|
|
memset(filter, 0,
|
|
sizeof(struct rte_eth_ntuple_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
item, "Not supported by ntuple filter");
|
|
return -rte_errno;
|
|
}
|
|
|
|
filter->dst_port_mask = udp_mask->hdr.dst_port;
|
|
filter->src_port_mask = udp_mask->hdr.src_port;
|
|
|
|
udp_spec = item->spec;
|
|
filter->dst_port = udp_spec->hdr.dst_port;
|
|
filter->src_port = udp_spec->hdr.src_port;
|
|
}
|
|
} else {
|
|
if (item->spec && item->mask) {
|
|
sctp_mask = item->mask;
|
|
|
|
/**
|
|
* Only support src & dst ports,
|
|
* others should be masked.
|
|
*/
|
|
if (sctp_mask->hdr.tag ||
|
|
sctp_mask->hdr.cksum) {
|
|
memset(filter, 0,
|
|
sizeof(struct rte_eth_ntuple_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
item, "Not supported by ntuple filter");
|
|
return -rte_errno;
|
|
}
|
|
|
|
filter->dst_port_mask = sctp_mask->hdr.dst_port;
|
|
filter->src_port_mask = sctp_mask->hdr.src_port;
|
|
|
|
sctp_spec = (const struct rte_flow_item_sctp *)
|
|
item->spec;
|
|
filter->dst_port = sctp_spec->hdr.dst_port;
|
|
filter->src_port = sctp_spec->hdr.src_port;
|
|
}
|
|
}
|
|
/* check if the next not void item is END */
|
|
index++;
|
|
NEXT_ITEM_OF_PATTERN(item, pattern, index);
|
|
if (item->type != RTE_FLOW_ITEM_TYPE_END) {
|
|
memset(filter, 0, sizeof(struct rte_eth_ntuple_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
item, "Not supported by ntuple filter");
|
|
return -rte_errno;
|
|
}
|
|
|
|
/* parse action */
|
|
index = 0;
|
|
|
|
/**
|
|
* n-tuple only supports forwarding,
|
|
* check if the first not void action is QUEUE.
|
|
*/
|
|
NEXT_ITEM_OF_ACTION(act, actions, index);
|
|
if (act->type != RTE_FLOW_ACTION_TYPE_QUEUE) {
|
|
memset(filter, 0, sizeof(struct rte_eth_ntuple_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ACTION,
|
|
item, "Not supported action.");
|
|
return -rte_errno;
|
|
}
|
|
filter->queue =
|
|
((const struct rte_flow_action_queue *)act->conf)->index;
|
|
|
|
/* check if the next not void item is END */
|
|
index++;
|
|
NEXT_ITEM_OF_ACTION(act, actions, index);
|
|
if (act->type != RTE_FLOW_ACTION_TYPE_END) {
|
|
memset(filter, 0, sizeof(struct rte_eth_ntuple_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ACTION,
|
|
act, "Not supported action.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
/* parse attr */
|
|
/* must be input direction */
|
|
if (!attr->ingress) {
|
|
memset(filter, 0, sizeof(struct rte_eth_ntuple_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ATTR_INGRESS,
|
|
attr, "Only support ingress.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
/* not supported */
|
|
if (attr->egress) {
|
|
memset(filter, 0, sizeof(struct rte_eth_ntuple_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ATTR_EGRESS,
|
|
attr, "Not support egress.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
/* not supported */
|
|
if (attr->transfer) {
|
|
memset(filter, 0, sizeof(struct rte_eth_ntuple_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ATTR_TRANSFER,
|
|
attr, "No support for transfer.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
if (attr->priority > 0xFFFF) {
|
|
memset(filter, 0, sizeof(struct rte_eth_ntuple_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY,
|
|
attr, "Error priority.");
|
|
return -rte_errno;
|
|
}
|
|
filter->priority = (uint16_t)attr->priority;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* a specific function for igb because the flags is specific */
|
|
static int
|
|
igb_parse_ntuple_filter(struct rte_eth_dev *dev,
|
|
const struct rte_flow_attr *attr,
|
|
const struct rte_flow_item pattern[],
|
|
const struct rte_flow_action actions[],
|
|
struct rte_eth_ntuple_filter *filter,
|
|
struct rte_flow_error *error)
|
|
{
|
|
struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
|
|
int ret;
|
|
|
|
MAC_TYPE_FILTER_SUP(hw->mac.type);
|
|
|
|
ret = cons_parse_ntuple_filter(attr, pattern, actions, filter, error);
|
|
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Igb doesn't support many priorities. */
|
|
if (filter->priority > E1000_2TUPLE_MAX_PRI) {
|
|
memset(filter, 0, sizeof(struct rte_eth_ntuple_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
NULL, "Priority not supported by ntuple filter");
|
|
return -rte_errno;
|
|
}
|
|
|
|
if (hw->mac.type == e1000_82576) {
|
|
if (filter->queue >= IGB_MAX_RX_QUEUE_NUM_82576) {
|
|
memset(filter, 0, sizeof(struct rte_eth_ntuple_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
NULL, "queue number not "
|
|
"supported by ntuple filter");
|
|
return -rte_errno;
|
|
}
|
|
filter->flags |= RTE_5TUPLE_FLAGS;
|
|
} else {
|
|
if (filter->src_ip_mask || filter->dst_ip_mask ||
|
|
filter->src_port_mask) {
|
|
memset(filter, 0, sizeof(struct rte_eth_ntuple_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
NULL, "only two tuple are "
|
|
"supported by this filter");
|
|
return -rte_errno;
|
|
}
|
|
if (filter->queue >= IGB_MAX_RX_QUEUE_NUM) {
|
|
memset(filter, 0, sizeof(struct rte_eth_ntuple_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
NULL, "queue number not "
|
|
"supported by ntuple filter");
|
|
return -rte_errno;
|
|
}
|
|
filter->flags |= RTE_2TUPLE_FLAGS;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Parse the rule to see if it is a ethertype rule.
|
|
* And get the ethertype filter info BTW.
|
|
* pattern:
|
|
* The first not void item can be ETH.
|
|
* The next not void item must be END.
|
|
* action:
|
|
* The first not void action should be QUEUE.
|
|
* The next not void action should be END.
|
|
* pattern example:
|
|
* ITEM Spec Mask
|
|
* ETH type 0x0807 0xFFFF
|
|
* END
|
|
* other members in mask and spec should set to 0x00.
|
|
* item->last should be NULL.
|
|
*/
|
|
static int
|
|
cons_parse_ethertype_filter(const struct rte_flow_attr *attr,
|
|
const struct rte_flow_item *pattern,
|
|
const struct rte_flow_action *actions,
|
|
struct rte_eth_ethertype_filter *filter,
|
|
struct rte_flow_error *error)
|
|
{
|
|
const struct rte_flow_item *item;
|
|
const struct rte_flow_action *act;
|
|
const struct rte_flow_item_eth *eth_spec;
|
|
const struct rte_flow_item_eth *eth_mask;
|
|
const struct rte_flow_action_queue *act_q;
|
|
uint32_t index;
|
|
|
|
if (!pattern) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM_NUM,
|
|
NULL, "NULL pattern.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
if (!actions) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ACTION_NUM,
|
|
NULL, "NULL action.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
if (!attr) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ATTR,
|
|
NULL, "NULL attribute.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
/* Parse pattern */
|
|
index = 0;
|
|
|
|
/* The first non-void item should be MAC. */
|
|
NEXT_ITEM_OF_PATTERN(item, pattern, index);
|
|
if (item->type != RTE_FLOW_ITEM_TYPE_ETH) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
item, "Not supported by ethertype filter");
|
|
return -rte_errno;
|
|
}
|
|
|
|
/*Not supported last point for range*/
|
|
if (item->last) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
|
|
item, "Not supported last point for range");
|
|
return -rte_errno;
|
|
}
|
|
|
|
/* Get the MAC info. */
|
|
if (!item->spec || !item->mask) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
item, "Not supported by ethertype filter");
|
|
return -rte_errno;
|
|
}
|
|
|
|
eth_spec = item->spec;
|
|
eth_mask = item->mask;
|
|
|
|
/* Mask bits of source MAC address must be full of 0.
|
|
* Mask bits of destination MAC address must be full
|
|
* of 1 or full of 0.
|
|
*/
|
|
if (!is_zero_ether_addr(ð_mask->src) ||
|
|
(!is_zero_ether_addr(ð_mask->dst) &&
|
|
!is_broadcast_ether_addr(ð_mask->dst))) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
item, "Invalid ether address mask");
|
|
return -rte_errno;
|
|
}
|
|
|
|
if ((eth_mask->type & UINT16_MAX) != UINT16_MAX) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
item, "Invalid ethertype mask");
|
|
return -rte_errno;
|
|
}
|
|
|
|
/* If mask bits of destination MAC address
|
|
* are full of 1, set RTE_ETHTYPE_FLAGS_MAC.
|
|
*/
|
|
if (is_broadcast_ether_addr(ð_mask->dst)) {
|
|
filter->mac_addr = eth_spec->dst;
|
|
filter->flags |= RTE_ETHTYPE_FLAGS_MAC;
|
|
} else {
|
|
filter->flags &= ~RTE_ETHTYPE_FLAGS_MAC;
|
|
}
|
|
filter->ether_type = rte_be_to_cpu_16(eth_spec->type);
|
|
|
|
/* Check if the next non-void item is END. */
|
|
index++;
|
|
NEXT_ITEM_OF_PATTERN(item, pattern, index);
|
|
if (item->type != RTE_FLOW_ITEM_TYPE_END) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
item, "Not supported by ethertype filter.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
/* Parse action */
|
|
|
|
index = 0;
|
|
/* Check if the first non-void action is QUEUE or DROP. */
|
|
NEXT_ITEM_OF_ACTION(act, actions, index);
|
|
if (act->type != RTE_FLOW_ACTION_TYPE_QUEUE &&
|
|
act->type != RTE_FLOW_ACTION_TYPE_DROP) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ACTION,
|
|
act, "Not supported action.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
if (act->type == RTE_FLOW_ACTION_TYPE_QUEUE) {
|
|
act_q = (const struct rte_flow_action_queue *)act->conf;
|
|
filter->queue = act_q->index;
|
|
} else {
|
|
filter->flags |= RTE_ETHTYPE_FLAGS_DROP;
|
|
}
|
|
|
|
/* Check if the next non-void item is END */
|
|
index++;
|
|
NEXT_ITEM_OF_ACTION(act, actions, index);
|
|
if (act->type != RTE_FLOW_ACTION_TYPE_END) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ACTION,
|
|
act, "Not supported action.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
/* Parse attr */
|
|
/* Must be input direction */
|
|
if (!attr->ingress) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ATTR_INGRESS,
|
|
attr, "Only support ingress.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
/* Not supported */
|
|
if (attr->egress) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ATTR_EGRESS,
|
|
attr, "Not support egress.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
/* Not supported */
|
|
if (attr->transfer) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ATTR_TRANSFER,
|
|
attr, "No support for transfer.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
/* Not supported */
|
|
if (attr->priority) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY,
|
|
attr, "Not support priority.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
/* Not supported */
|
|
if (attr->group) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ATTR_GROUP,
|
|
attr, "Not support group.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
igb_parse_ethertype_filter(struct rte_eth_dev *dev,
|
|
const struct rte_flow_attr *attr,
|
|
const struct rte_flow_item pattern[],
|
|
const struct rte_flow_action actions[],
|
|
struct rte_eth_ethertype_filter *filter,
|
|
struct rte_flow_error *error)
|
|
{
|
|
struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
|
|
int ret;
|
|
|
|
MAC_TYPE_FILTER_SUP(hw->mac.type);
|
|
|
|
ret = cons_parse_ethertype_filter(attr, pattern,
|
|
actions, filter, error);
|
|
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (hw->mac.type == e1000_82576) {
|
|
if (filter->queue >= IGB_MAX_RX_QUEUE_NUM_82576) {
|
|
memset(filter, 0, sizeof(
|
|
struct rte_eth_ethertype_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
NULL, "queue number not supported "
|
|
"by ethertype filter");
|
|
return -rte_errno;
|
|
}
|
|
} else {
|
|
if (filter->queue >= IGB_MAX_RX_QUEUE_NUM) {
|
|
memset(filter, 0, sizeof(
|
|
struct rte_eth_ethertype_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
NULL, "queue number not supported "
|
|
"by ethertype filter");
|
|
return -rte_errno;
|
|
}
|
|
}
|
|
|
|
if (filter->ether_type == ETHER_TYPE_IPv4 ||
|
|
filter->ether_type == ETHER_TYPE_IPv6) {
|
|
memset(filter, 0, sizeof(struct rte_eth_ethertype_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
NULL, "IPv4/IPv6 not supported by ethertype filter");
|
|
return -rte_errno;
|
|
}
|
|
|
|
if (filter->flags & RTE_ETHTYPE_FLAGS_MAC) {
|
|
memset(filter, 0, sizeof(struct rte_eth_ethertype_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
NULL, "mac compare is unsupported");
|
|
return -rte_errno;
|
|
}
|
|
|
|
if (filter->flags & RTE_ETHTYPE_FLAGS_DROP) {
|
|
memset(filter, 0, sizeof(struct rte_eth_ethertype_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
NULL, "drop option is unsupported");
|
|
return -rte_errno;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Parse the rule to see if it is a TCP SYN rule.
|
|
* And get the TCP SYN filter info BTW.
|
|
* pattern:
|
|
* The first not void item must be ETH.
|
|
* The second not void item must be IPV4 or IPV6.
|
|
* The third not void item must be TCP.
|
|
* The next not void item must be END.
|
|
* action:
|
|
* The first not void action should be QUEUE.
|
|
* The next not void action should be END.
|
|
* pattern example:
|
|
* ITEM Spec Mask
|
|
* ETH NULL NULL
|
|
* IPV4/IPV6 NULL NULL
|
|
* TCP tcp_flags 0x02 0xFF
|
|
* END
|
|
* other members in mask and spec should set to 0x00.
|
|
* item->last should be NULL.
|
|
*/
|
|
static int
|
|
cons_parse_syn_filter(const struct rte_flow_attr *attr,
|
|
const struct rte_flow_item pattern[],
|
|
const struct rte_flow_action actions[],
|
|
struct rte_eth_syn_filter *filter,
|
|
struct rte_flow_error *error)
|
|
{
|
|
const struct rte_flow_item *item;
|
|
const struct rte_flow_action *act;
|
|
const struct rte_flow_item_tcp *tcp_spec;
|
|
const struct rte_flow_item_tcp *tcp_mask;
|
|
const struct rte_flow_action_queue *act_q;
|
|
uint32_t index;
|
|
|
|
if (!pattern) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM_NUM,
|
|
NULL, "NULL pattern.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
if (!actions) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ACTION_NUM,
|
|
NULL, "NULL action.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
if (!attr) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ATTR,
|
|
NULL, "NULL attribute.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
/* parse pattern */
|
|
index = 0;
|
|
|
|
/* the first not void item should be MAC or IPv4 or IPv6 or TCP */
|
|
NEXT_ITEM_OF_PATTERN(item, pattern, index);
|
|
if (item->type != RTE_FLOW_ITEM_TYPE_ETH &&
|
|
item->type != RTE_FLOW_ITEM_TYPE_IPV4 &&
|
|
item->type != RTE_FLOW_ITEM_TYPE_IPV6 &&
|
|
item->type != RTE_FLOW_ITEM_TYPE_TCP) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
item, "Not supported by syn filter");
|
|
return -rte_errno;
|
|
}
|
|
/*Not supported last point for range*/
|
|
if (item->last) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
|
|
item, "Not supported last point for range");
|
|
return -rte_errno;
|
|
}
|
|
|
|
/* Skip Ethernet */
|
|
if (item->type == RTE_FLOW_ITEM_TYPE_ETH) {
|
|
/* if the item is MAC, the content should be NULL */
|
|
if (item->spec || item->mask) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
item, "Invalid SYN address mask");
|
|
return -rte_errno;
|
|
}
|
|
|
|
/* check if the next not void item is IPv4 or IPv6 */
|
|
index++;
|
|
NEXT_ITEM_OF_PATTERN(item, pattern, index);
|
|
if (item->type != RTE_FLOW_ITEM_TYPE_IPV4 &&
|
|
item->type != RTE_FLOW_ITEM_TYPE_IPV6) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
item, "Not supported by syn filter");
|
|
return -rte_errno;
|
|
}
|
|
}
|
|
|
|
/* Skip IP */
|
|
if (item->type == RTE_FLOW_ITEM_TYPE_IPV4 ||
|
|
item->type == RTE_FLOW_ITEM_TYPE_IPV6) {
|
|
/* if the item is IP, the content should be NULL */
|
|
if (item->spec || item->mask) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
item, "Invalid SYN mask");
|
|
return -rte_errno;
|
|
}
|
|
|
|
/* check if the next not void item is TCP */
|
|
index++;
|
|
NEXT_ITEM_OF_PATTERN(item, pattern, index);
|
|
if (item->type != RTE_FLOW_ITEM_TYPE_TCP) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
item, "Not supported by syn filter");
|
|
return -rte_errno;
|
|
}
|
|
}
|
|
|
|
/* Get the TCP info. Only support SYN. */
|
|
if (!item->spec || !item->mask) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
item, "Invalid SYN mask");
|
|
return -rte_errno;
|
|
}
|
|
/*Not supported last point for range*/
|
|
if (item->last) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
|
|
item, "Not supported last point for range");
|
|
return -rte_errno;
|
|
}
|
|
|
|
tcp_spec = item->spec;
|
|
tcp_mask = item->mask;
|
|
if (!(tcp_spec->hdr.tcp_flags & TCP_SYN_FLAG) ||
|
|
tcp_mask->hdr.src_port ||
|
|
tcp_mask->hdr.dst_port ||
|
|
tcp_mask->hdr.sent_seq ||
|
|
tcp_mask->hdr.recv_ack ||
|
|
tcp_mask->hdr.data_off ||
|
|
tcp_mask->hdr.tcp_flags != TCP_SYN_FLAG ||
|
|
tcp_mask->hdr.rx_win ||
|
|
tcp_mask->hdr.cksum ||
|
|
tcp_mask->hdr.tcp_urp) {
|
|
memset(filter, 0, sizeof(struct rte_eth_syn_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
item, "Not supported by syn filter");
|
|
return -rte_errno;
|
|
}
|
|
|
|
/* check if the next not void item is END */
|
|
index++;
|
|
NEXT_ITEM_OF_PATTERN(item, pattern, index);
|
|
if (item->type != RTE_FLOW_ITEM_TYPE_END) {
|
|
memset(filter, 0, sizeof(struct rte_eth_syn_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
item, "Not supported by syn filter");
|
|
return -rte_errno;
|
|
}
|
|
|
|
/* parse action */
|
|
index = 0;
|
|
|
|
/* check if the first not void action is QUEUE. */
|
|
NEXT_ITEM_OF_ACTION(act, actions, index);
|
|
if (act->type != RTE_FLOW_ACTION_TYPE_QUEUE) {
|
|
memset(filter, 0, sizeof(struct rte_eth_syn_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ACTION,
|
|
act, "Not supported action.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
act_q = (const struct rte_flow_action_queue *)act->conf;
|
|
filter->queue = act_q->index;
|
|
|
|
/* check if the next not void item is END */
|
|
index++;
|
|
NEXT_ITEM_OF_ACTION(act, actions, index);
|
|
if (act->type != RTE_FLOW_ACTION_TYPE_END) {
|
|
memset(filter, 0, sizeof(struct rte_eth_syn_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ACTION,
|
|
act, "Not supported action.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
/* parse attr */
|
|
/* must be input direction */
|
|
if (!attr->ingress) {
|
|
memset(filter, 0, sizeof(struct rte_eth_syn_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ATTR_INGRESS,
|
|
attr, "Only support ingress.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
/* not supported */
|
|
if (attr->egress) {
|
|
memset(filter, 0, sizeof(struct rte_eth_syn_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ATTR_EGRESS,
|
|
attr, "Not support egress.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
/* not supported */
|
|
if (attr->transfer) {
|
|
memset(filter, 0, sizeof(struct rte_eth_syn_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ATTR_TRANSFER,
|
|
attr, "No support for transfer.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
/* Support 2 priorities, the lowest or highest. */
|
|
if (!attr->priority) {
|
|
filter->hig_pri = 0;
|
|
} else if (attr->priority == (uint32_t)~0U) {
|
|
filter->hig_pri = 1;
|
|
} else {
|
|
memset(filter, 0, sizeof(struct rte_eth_syn_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY,
|
|
attr, "Not support priority.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
igb_parse_syn_filter(struct rte_eth_dev *dev,
|
|
const struct rte_flow_attr *attr,
|
|
const struct rte_flow_item pattern[],
|
|
const struct rte_flow_action actions[],
|
|
struct rte_eth_syn_filter *filter,
|
|
struct rte_flow_error *error)
|
|
{
|
|
struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
|
|
int ret;
|
|
|
|
MAC_TYPE_FILTER_SUP(hw->mac.type);
|
|
|
|
ret = cons_parse_syn_filter(attr, pattern,
|
|
actions, filter, error);
|
|
|
|
if (hw->mac.type == e1000_82576) {
|
|
if (filter->queue >= IGB_MAX_RX_QUEUE_NUM_82576) {
|
|
memset(filter, 0, sizeof(struct rte_eth_syn_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
NULL, "queue number not "
|
|
"supported by syn filter");
|
|
return -rte_errno;
|
|
}
|
|
} else {
|
|
if (filter->queue >= IGB_MAX_RX_QUEUE_NUM) {
|
|
memset(filter, 0, sizeof(struct rte_eth_syn_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
NULL, "queue number not "
|
|
"supported by syn filter");
|
|
return -rte_errno;
|
|
}
|
|
}
|
|
|
|
if (ret)
|
|
return ret;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Parse the rule to see if it is a flex byte rule.
|
|
* And get the flex byte filter info BTW.
|
|
* pattern:
|
|
* The first not void item must be RAW.
|
|
* The second not void item can be RAW or END.
|
|
* The third not void item can be RAW or END.
|
|
* The last not void item must be END.
|
|
* action:
|
|
* The first not void action should be QUEUE.
|
|
* The next not void action should be END.
|
|
* pattern example:
|
|
* ITEM Spec Mask
|
|
* RAW relative 0 0x1
|
|
* offset 0 0xFFFFFFFF
|
|
* pattern {0x08, 0x06} {0xFF, 0xFF}
|
|
* RAW relative 1 0x1
|
|
* offset 100 0xFFFFFFFF
|
|
* pattern {0x11, 0x22, 0x33} {0xFF, 0xFF, 0xFF}
|
|
* END
|
|
* other members in mask and spec should set to 0x00.
|
|
* item->last should be NULL.
|
|
*/
|
|
static int
|
|
cons_parse_flex_filter(const struct rte_flow_attr *attr,
|
|
const struct rte_flow_item pattern[],
|
|
const struct rte_flow_action actions[],
|
|
struct rte_eth_flex_filter *filter,
|
|
struct rte_flow_error *error)
|
|
{
|
|
const struct rte_flow_item *item;
|
|
const struct rte_flow_action *act;
|
|
const struct rte_flow_item_raw *raw_spec;
|
|
const struct rte_flow_item_raw *raw_mask;
|
|
const struct rte_flow_action_queue *act_q;
|
|
uint32_t index, i, offset, total_offset;
|
|
uint32_t max_offset = 0;
|
|
int32_t shift, j, raw_index = 0;
|
|
int32_t relative[IGB_FLEX_RAW_NUM] = {0};
|
|
int32_t raw_offset[IGB_FLEX_RAW_NUM] = {0};
|
|
|
|
if (!pattern) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM_NUM,
|
|
NULL, "NULL pattern.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
if (!actions) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ACTION_NUM,
|
|
NULL, "NULL action.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
if (!attr) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ATTR,
|
|
NULL, "NULL attribute.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
/* parse pattern */
|
|
index = 0;
|
|
|
|
item_loop:
|
|
|
|
/* the first not void item should be RAW */
|
|
NEXT_ITEM_OF_PATTERN(item, pattern, index);
|
|
if (item->type != RTE_FLOW_ITEM_TYPE_RAW) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
item, "Not supported by flex filter");
|
|
return -rte_errno;
|
|
}
|
|
/*Not supported last point for range*/
|
|
if (item->last) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
|
|
item, "Not supported last point for range");
|
|
return -rte_errno;
|
|
}
|
|
|
|
raw_spec = item->spec;
|
|
raw_mask = item->mask;
|
|
|
|
if (!raw_mask->length ||
|
|
!raw_mask->relative) {
|
|
memset(filter, 0, sizeof(struct rte_eth_flex_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
item, "Not supported by flex filter");
|
|
return -rte_errno;
|
|
}
|
|
|
|
if (raw_mask->offset)
|
|
offset = raw_spec->offset;
|
|
else
|
|
offset = 0;
|
|
|
|
for (j = 0; j < raw_spec->length; j++) {
|
|
if (raw_mask->pattern[j] != 0xFF) {
|
|
memset(filter, 0, sizeof(struct rte_eth_flex_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
item, "Not supported by flex filter");
|
|
return -rte_errno;
|
|
}
|
|
}
|
|
|
|
total_offset = 0;
|
|
|
|
if (raw_spec->relative) {
|
|
for (j = raw_index; j > 0; j--) {
|
|
total_offset += raw_offset[j - 1];
|
|
if (!relative[j - 1])
|
|
break;
|
|
}
|
|
if (total_offset + raw_spec->length + offset > max_offset)
|
|
max_offset = total_offset + raw_spec->length + offset;
|
|
} else {
|
|
if (raw_spec->length + offset > max_offset)
|
|
max_offset = raw_spec->length + offset;
|
|
}
|
|
|
|
if ((raw_spec->length + offset + total_offset) >
|
|
RTE_FLEX_FILTER_MAXLEN) {
|
|
memset(filter, 0, sizeof(struct rte_eth_flex_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
item, "Not supported by flex filter");
|
|
return -rte_errno;
|
|
}
|
|
|
|
if (raw_spec->relative == 0) {
|
|
for (j = 0; j < raw_spec->length; j++)
|
|
filter->bytes[offset + j] =
|
|
raw_spec->pattern[j];
|
|
j = offset / CHAR_BIT;
|
|
shift = offset % CHAR_BIT;
|
|
} else {
|
|
for (j = 0; j < raw_spec->length; j++)
|
|
filter->bytes[total_offset + offset + j] =
|
|
raw_spec->pattern[j];
|
|
j = (total_offset + offset) / CHAR_BIT;
|
|
shift = (total_offset + offset) % CHAR_BIT;
|
|
}
|
|
|
|
i = 0;
|
|
|
|
for ( ; shift < CHAR_BIT; shift++) {
|
|
filter->mask[j] |= (0x80 >> shift);
|
|
i++;
|
|
if (i == raw_spec->length)
|
|
break;
|
|
if (shift == (CHAR_BIT - 1)) {
|
|
j++;
|
|
shift = -1;
|
|
}
|
|
}
|
|
|
|
relative[raw_index] = raw_spec->relative;
|
|
raw_offset[raw_index] = offset + raw_spec->length;
|
|
raw_index++;
|
|
|
|
/* check if the next not void item is RAW */
|
|
index++;
|
|
NEXT_ITEM_OF_PATTERN(item, pattern, index);
|
|
if (item->type != RTE_FLOW_ITEM_TYPE_RAW &&
|
|
item->type != RTE_FLOW_ITEM_TYPE_END) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
item, "Not supported by flex filter");
|
|
return -rte_errno;
|
|
}
|
|
|
|
/* go back to parser */
|
|
if (item->type == RTE_FLOW_ITEM_TYPE_RAW) {
|
|
/* if the item is RAW, the content should be parse */
|
|
goto item_loop;
|
|
}
|
|
|
|
filter->len = RTE_ALIGN(max_offset, 8);
|
|
|
|
/* parse action */
|
|
index = 0;
|
|
|
|
/* check if the first not void action is QUEUE. */
|
|
NEXT_ITEM_OF_ACTION(act, actions, index);
|
|
if (act->type != RTE_FLOW_ACTION_TYPE_QUEUE) {
|
|
memset(filter, 0, sizeof(struct rte_eth_flex_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ACTION,
|
|
act, "Not supported action.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
act_q = (const struct rte_flow_action_queue *)act->conf;
|
|
filter->queue = act_q->index;
|
|
|
|
/* check if the next not void item is END */
|
|
index++;
|
|
NEXT_ITEM_OF_ACTION(act, actions, index);
|
|
if (act->type != RTE_FLOW_ACTION_TYPE_END) {
|
|
memset(filter, 0, sizeof(struct rte_eth_flex_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ACTION,
|
|
act, "Not supported action.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
/* parse attr */
|
|
/* must be input direction */
|
|
if (!attr->ingress) {
|
|
memset(filter, 0, sizeof(struct rte_eth_flex_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ATTR_INGRESS,
|
|
attr, "Only support ingress.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
/* not supported */
|
|
if (attr->egress) {
|
|
memset(filter, 0, sizeof(struct rte_eth_flex_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ATTR_EGRESS,
|
|
attr, "Not support egress.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
/* not supported */
|
|
if (attr->transfer) {
|
|
memset(filter, 0, sizeof(struct rte_eth_flex_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ATTR_TRANSFER,
|
|
attr, "No support for transfer.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
if (attr->priority > 0xFFFF) {
|
|
memset(filter, 0, sizeof(struct rte_eth_flex_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY,
|
|
attr, "Error priority.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
filter->priority = (uint16_t)attr->priority;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
igb_parse_flex_filter(struct rte_eth_dev *dev,
|
|
const struct rte_flow_attr *attr,
|
|
const struct rte_flow_item pattern[],
|
|
const struct rte_flow_action actions[],
|
|
struct rte_eth_flex_filter *filter,
|
|
struct rte_flow_error *error)
|
|
{
|
|
struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
|
|
int ret;
|
|
|
|
MAC_TYPE_FILTER_SUP_EXT(hw->mac.type);
|
|
|
|
ret = cons_parse_flex_filter(attr, pattern,
|
|
actions, filter, error);
|
|
|
|
if (filter->queue >= IGB_MAX_RX_QUEUE_NUM) {
|
|
memset(filter, 0, sizeof(struct rte_eth_flex_filter));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ITEM,
|
|
NULL, "queue number not supported by flex filter");
|
|
return -rte_errno;
|
|
}
|
|
|
|
if (filter->len == 0 || filter->len > E1000_MAX_FLEX_FILTER_LEN ||
|
|
filter->len % sizeof(uint64_t) != 0) {
|
|
PMD_DRV_LOG(ERR, "filter's length is out of range");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (filter->priority > E1000_MAX_FLEX_FILTER_PRI) {
|
|
PMD_DRV_LOG(ERR, "filter's priority is out of range");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (ret)
|
|
return ret;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
igb_parse_rss_filter(struct rte_eth_dev *dev,
|
|
const struct rte_flow_attr *attr,
|
|
const struct rte_flow_action actions[],
|
|
struct igb_rte_flow_rss_conf *rss_conf,
|
|
struct rte_flow_error *error)
|
|
{
|
|
const struct rte_flow_action *act;
|
|
const struct rte_flow_action_rss *rss;
|
|
uint16_t n, index;
|
|
|
|
/**
|
|
* rss only supports forwarding,
|
|
* check if the first not void action is RSS.
|
|
*/
|
|
index = 0;
|
|
NEXT_ITEM_OF_ACTION(act, actions, index);
|
|
if (act->type != RTE_FLOW_ACTION_TYPE_RSS) {
|
|
memset(rss_conf, 0, sizeof(struct igb_rte_flow_rss_conf));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ACTION,
|
|
act, "Not supported action.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
rss = (const struct rte_flow_action_rss *)act->conf;
|
|
|
|
if (!rss || !rss->queue_num) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ACTION,
|
|
act,
|
|
"no valid queues");
|
|
return -rte_errno;
|
|
}
|
|
|
|
for (n = 0; n < rss->queue_num; n++) {
|
|
if (rss->queue[n] >= dev->data->nb_rx_queues) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ACTION,
|
|
act,
|
|
"queue id > max number of queues");
|
|
return -rte_errno;
|
|
}
|
|
}
|
|
|
|
if (rss->func != RTE_ETH_HASH_FUNCTION_DEFAULT)
|
|
return rte_flow_error_set
|
|
(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ACTION, act,
|
|
"non-default RSS hash functions are not supported");
|
|
if (rss->level)
|
|
return rte_flow_error_set
|
|
(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ACTION, act,
|
|
"a nonzero RSS encapsulation level is not supported");
|
|
if (rss->key_len && rss->key_len != RTE_DIM(rss_conf->key))
|
|
return rte_flow_error_set
|
|
(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ACTION, act,
|
|
"RSS hash key must be exactly 40 bytes");
|
|
if (rss->queue_num > RTE_DIM(rss_conf->queue))
|
|
return rte_flow_error_set
|
|
(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ACTION, act,
|
|
"too many queues for RSS context");
|
|
if (igb_rss_conf_init(rss_conf, rss))
|
|
return rte_flow_error_set
|
|
(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION, act,
|
|
"RSS context initialization failure");
|
|
|
|
/* check if the next not void item is END */
|
|
index++;
|
|
NEXT_ITEM_OF_ACTION(act, actions, index);
|
|
if (act->type != RTE_FLOW_ACTION_TYPE_END) {
|
|
memset(rss_conf, 0, sizeof(struct rte_eth_rss_conf));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ACTION,
|
|
act, "Not supported action.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
/* parse attr */
|
|
/* must be input direction */
|
|
if (!attr->ingress) {
|
|
memset(rss_conf, 0, sizeof(struct igb_rte_flow_rss_conf));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ATTR_INGRESS,
|
|
attr, "Only support ingress.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
/* not supported */
|
|
if (attr->egress) {
|
|
memset(rss_conf, 0, sizeof(struct igb_rte_flow_rss_conf));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ATTR_EGRESS,
|
|
attr, "Not support egress.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
/* not supported */
|
|
if (attr->transfer) {
|
|
memset(rss_conf, 0, sizeof(struct igb_rte_flow_rss_conf));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ATTR_TRANSFER,
|
|
attr, "No support for transfer.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
if (attr->priority > 0xFFFF) {
|
|
memset(rss_conf, 0, sizeof(struct igb_rte_flow_rss_conf));
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY,
|
|
attr, "Error priority.");
|
|
return -rte_errno;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Create a flow rule.
|
|
* Theorically one rule can match more than one filters.
|
|
* We will let it use the filter which it hitt first.
|
|
* So, the sequence matters.
|
|
*/
|
|
static struct rte_flow *
|
|
igb_flow_create(struct rte_eth_dev *dev,
|
|
const struct rte_flow_attr *attr,
|
|
const struct rte_flow_item pattern[],
|
|
const struct rte_flow_action actions[],
|
|
struct rte_flow_error *error)
|
|
{
|
|
int ret;
|
|
struct rte_eth_ntuple_filter ntuple_filter;
|
|
struct rte_eth_ethertype_filter ethertype_filter;
|
|
struct rte_eth_syn_filter syn_filter;
|
|
struct rte_eth_flex_filter flex_filter;
|
|
struct igb_rte_flow_rss_conf rss_conf;
|
|
struct rte_flow *flow = NULL;
|
|
struct igb_ntuple_filter_ele *ntuple_filter_ptr;
|
|
struct igb_ethertype_filter_ele *ethertype_filter_ptr;
|
|
struct igb_eth_syn_filter_ele *syn_filter_ptr;
|
|
struct igb_flex_filter_ele *flex_filter_ptr;
|
|
struct igb_rss_conf_ele *rss_filter_ptr;
|
|
struct igb_flow_mem *igb_flow_mem_ptr;
|
|
|
|
flow = rte_zmalloc("igb_rte_flow", sizeof(struct rte_flow), 0);
|
|
if (!flow) {
|
|
PMD_DRV_LOG(ERR, "failed to allocate memory");
|
|
return (struct rte_flow *)flow;
|
|
}
|
|
igb_flow_mem_ptr = rte_zmalloc("igb_flow_mem",
|
|
sizeof(struct igb_flow_mem), 0);
|
|
if (!igb_flow_mem_ptr) {
|
|
PMD_DRV_LOG(ERR, "failed to allocate memory");
|
|
rte_free(flow);
|
|
return NULL;
|
|
}
|
|
igb_flow_mem_ptr->flow = flow;
|
|
igb_flow_mem_ptr->dev = dev;
|
|
TAILQ_INSERT_TAIL(&igb_flow_list,
|
|
igb_flow_mem_ptr, entries);
|
|
|
|
memset(&ntuple_filter, 0, sizeof(struct rte_eth_ntuple_filter));
|
|
ret = igb_parse_ntuple_filter(dev, attr, pattern,
|
|
actions, &ntuple_filter, error);
|
|
if (!ret) {
|
|
ret = igb_add_del_ntuple_filter(dev, &ntuple_filter, TRUE);
|
|
if (!ret) {
|
|
ntuple_filter_ptr = rte_zmalloc("igb_ntuple_filter",
|
|
sizeof(struct igb_ntuple_filter_ele), 0);
|
|
if (!ntuple_filter_ptr) {
|
|
PMD_DRV_LOG(ERR, "failed to allocate memory");
|
|
goto out;
|
|
}
|
|
|
|
rte_memcpy(&ntuple_filter_ptr->filter_info,
|
|
&ntuple_filter,
|
|
sizeof(struct rte_eth_ntuple_filter));
|
|
TAILQ_INSERT_TAIL(&igb_filter_ntuple_list,
|
|
ntuple_filter_ptr, entries);
|
|
flow->rule = ntuple_filter_ptr;
|
|
flow->filter_type = RTE_ETH_FILTER_NTUPLE;
|
|
return flow;
|
|
}
|
|
goto out;
|
|
}
|
|
|
|
memset(ðertype_filter, 0, sizeof(struct rte_eth_ethertype_filter));
|
|
ret = igb_parse_ethertype_filter(dev, attr, pattern,
|
|
actions, ðertype_filter, error);
|
|
if (!ret) {
|
|
ret = igb_add_del_ethertype_filter(dev,
|
|
ðertype_filter, TRUE);
|
|
if (!ret) {
|
|
ethertype_filter_ptr = rte_zmalloc(
|
|
"igb_ethertype_filter",
|
|
sizeof(struct igb_ethertype_filter_ele), 0);
|
|
if (!ethertype_filter_ptr) {
|
|
PMD_DRV_LOG(ERR, "failed to allocate memory");
|
|
goto out;
|
|
}
|
|
|
|
rte_memcpy(ðertype_filter_ptr->filter_info,
|
|
ðertype_filter,
|
|
sizeof(struct rte_eth_ethertype_filter));
|
|
TAILQ_INSERT_TAIL(&igb_filter_ethertype_list,
|
|
ethertype_filter_ptr, entries);
|
|
flow->rule = ethertype_filter_ptr;
|
|
flow->filter_type = RTE_ETH_FILTER_ETHERTYPE;
|
|
return flow;
|
|
}
|
|
goto out;
|
|
}
|
|
|
|
memset(&syn_filter, 0, sizeof(struct rte_eth_syn_filter));
|
|
ret = igb_parse_syn_filter(dev, attr, pattern,
|
|
actions, &syn_filter, error);
|
|
if (!ret) {
|
|
ret = eth_igb_syn_filter_set(dev, &syn_filter, TRUE);
|
|
if (!ret) {
|
|
syn_filter_ptr = rte_zmalloc("igb_syn_filter",
|
|
sizeof(struct igb_eth_syn_filter_ele), 0);
|
|
if (!syn_filter_ptr) {
|
|
PMD_DRV_LOG(ERR, "failed to allocate memory");
|
|
goto out;
|
|
}
|
|
|
|
rte_memcpy(&syn_filter_ptr->filter_info,
|
|
&syn_filter,
|
|
sizeof(struct rte_eth_syn_filter));
|
|
TAILQ_INSERT_TAIL(&igb_filter_syn_list,
|
|
syn_filter_ptr,
|
|
entries);
|
|
flow->rule = syn_filter_ptr;
|
|
flow->filter_type = RTE_ETH_FILTER_SYN;
|
|
return flow;
|
|
}
|
|
goto out;
|
|
}
|
|
|
|
memset(&flex_filter, 0, sizeof(struct rte_eth_flex_filter));
|
|
ret = igb_parse_flex_filter(dev, attr, pattern,
|
|
actions, &flex_filter, error);
|
|
if (!ret) {
|
|
ret = eth_igb_add_del_flex_filter(dev, &flex_filter, TRUE);
|
|
if (!ret) {
|
|
flex_filter_ptr = rte_zmalloc("igb_flex_filter",
|
|
sizeof(struct igb_flex_filter_ele), 0);
|
|
if (!flex_filter_ptr) {
|
|
PMD_DRV_LOG(ERR, "failed to allocate memory");
|
|
goto out;
|
|
}
|
|
|
|
rte_memcpy(&flex_filter_ptr->filter_info,
|
|
&flex_filter,
|
|
sizeof(struct rte_eth_flex_filter));
|
|
TAILQ_INSERT_TAIL(&igb_filter_flex_list,
|
|
flex_filter_ptr, entries);
|
|
flow->rule = flex_filter_ptr;
|
|
flow->filter_type = RTE_ETH_FILTER_FLEXIBLE;
|
|
return flow;
|
|
}
|
|
}
|
|
|
|
memset(&rss_conf, 0, sizeof(struct igb_rte_flow_rss_conf));
|
|
ret = igb_parse_rss_filter(dev, attr,
|
|
actions, &rss_conf, error);
|
|
if (!ret) {
|
|
ret = igb_config_rss_filter(dev, &rss_conf, TRUE);
|
|
if (!ret) {
|
|
rss_filter_ptr = rte_zmalloc("igb_rss_filter",
|
|
sizeof(struct igb_rss_conf_ele), 0);
|
|
if (!rss_filter_ptr) {
|
|
PMD_DRV_LOG(ERR, "failed to allocate memory");
|
|
goto out;
|
|
}
|
|
igb_rss_conf_init(&rss_filter_ptr->filter_info,
|
|
&rss_conf.conf);
|
|
TAILQ_INSERT_TAIL(&igb_filter_rss_list,
|
|
rss_filter_ptr, entries);
|
|
flow->rule = rss_filter_ptr;
|
|
flow->filter_type = RTE_ETH_FILTER_HASH;
|
|
return flow;
|
|
}
|
|
}
|
|
|
|
out:
|
|
TAILQ_REMOVE(&igb_flow_list,
|
|
igb_flow_mem_ptr, entries);
|
|
rte_flow_error_set(error, -ret,
|
|
RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
|
|
"Failed to create flow.");
|
|
rte_free(igb_flow_mem_ptr);
|
|
rte_free(flow);
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Check if the flow rule is supported by igb.
|
|
* It only checkes the format. Don't guarantee the rule can be programmed into
|
|
* the HW. Because there can be no enough room for the rule.
|
|
*/
|
|
static int
|
|
igb_flow_validate(__rte_unused struct rte_eth_dev *dev,
|
|
const struct rte_flow_attr *attr,
|
|
const struct rte_flow_item pattern[],
|
|
const struct rte_flow_action actions[],
|
|
struct rte_flow_error *error)
|
|
{
|
|
struct rte_eth_ntuple_filter ntuple_filter;
|
|
struct rte_eth_ethertype_filter ethertype_filter;
|
|
struct rte_eth_syn_filter syn_filter;
|
|
struct rte_eth_flex_filter flex_filter;
|
|
struct igb_rte_flow_rss_conf rss_conf;
|
|
int ret;
|
|
|
|
memset(&ntuple_filter, 0, sizeof(struct rte_eth_ntuple_filter));
|
|
ret = igb_parse_ntuple_filter(dev, attr, pattern,
|
|
actions, &ntuple_filter, error);
|
|
if (!ret)
|
|
return 0;
|
|
|
|
memset(ðertype_filter, 0, sizeof(struct rte_eth_ethertype_filter));
|
|
ret = igb_parse_ethertype_filter(dev, attr, pattern,
|
|
actions, ðertype_filter, error);
|
|
if (!ret)
|
|
return 0;
|
|
|
|
memset(&syn_filter, 0, sizeof(struct rte_eth_syn_filter));
|
|
ret = igb_parse_syn_filter(dev, attr, pattern,
|
|
actions, &syn_filter, error);
|
|
if (!ret)
|
|
return 0;
|
|
|
|
memset(&flex_filter, 0, sizeof(struct rte_eth_flex_filter));
|
|
ret = igb_parse_flex_filter(dev, attr, pattern,
|
|
actions, &flex_filter, error);
|
|
if (!ret)
|
|
return 0;
|
|
|
|
memset(&rss_conf, 0, sizeof(struct igb_rte_flow_rss_conf));
|
|
ret = igb_parse_rss_filter(dev, attr,
|
|
actions, &rss_conf, error);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Destroy a flow rule on igb. */
|
|
static int
|
|
igb_flow_destroy(struct rte_eth_dev *dev,
|
|
struct rte_flow *flow,
|
|
struct rte_flow_error *error)
|
|
{
|
|
int ret;
|
|
struct rte_flow *pmd_flow = flow;
|
|
enum rte_filter_type filter_type = pmd_flow->filter_type;
|
|
struct igb_ntuple_filter_ele *ntuple_filter_ptr;
|
|
struct igb_ethertype_filter_ele *ethertype_filter_ptr;
|
|
struct igb_eth_syn_filter_ele *syn_filter_ptr;
|
|
struct igb_flex_filter_ele *flex_filter_ptr;
|
|
struct igb_flow_mem *igb_flow_mem_ptr;
|
|
struct igb_rss_conf_ele *rss_filter_ptr;
|
|
|
|
switch (filter_type) {
|
|
case RTE_ETH_FILTER_NTUPLE:
|
|
ntuple_filter_ptr = (struct igb_ntuple_filter_ele *)
|
|
pmd_flow->rule;
|
|
ret = igb_add_del_ntuple_filter(dev,
|
|
&ntuple_filter_ptr->filter_info, FALSE);
|
|
if (!ret) {
|
|
TAILQ_REMOVE(&igb_filter_ntuple_list,
|
|
ntuple_filter_ptr, entries);
|
|
rte_free(ntuple_filter_ptr);
|
|
}
|
|
break;
|
|
case RTE_ETH_FILTER_ETHERTYPE:
|
|
ethertype_filter_ptr = (struct igb_ethertype_filter_ele *)
|
|
pmd_flow->rule;
|
|
ret = igb_add_del_ethertype_filter(dev,
|
|
ðertype_filter_ptr->filter_info, FALSE);
|
|
if (!ret) {
|
|
TAILQ_REMOVE(&igb_filter_ethertype_list,
|
|
ethertype_filter_ptr, entries);
|
|
rte_free(ethertype_filter_ptr);
|
|
}
|
|
break;
|
|
case RTE_ETH_FILTER_SYN:
|
|
syn_filter_ptr = (struct igb_eth_syn_filter_ele *)
|
|
pmd_flow->rule;
|
|
ret = eth_igb_syn_filter_set(dev,
|
|
&syn_filter_ptr->filter_info, FALSE);
|
|
if (!ret) {
|
|
TAILQ_REMOVE(&igb_filter_syn_list,
|
|
syn_filter_ptr, entries);
|
|
rte_free(syn_filter_ptr);
|
|
}
|
|
break;
|
|
case RTE_ETH_FILTER_FLEXIBLE:
|
|
flex_filter_ptr = (struct igb_flex_filter_ele *)
|
|
pmd_flow->rule;
|
|
ret = eth_igb_add_del_flex_filter(dev,
|
|
&flex_filter_ptr->filter_info, FALSE);
|
|
if (!ret) {
|
|
TAILQ_REMOVE(&igb_filter_flex_list,
|
|
flex_filter_ptr, entries);
|
|
rte_free(flex_filter_ptr);
|
|
}
|
|
break;
|
|
case RTE_ETH_FILTER_HASH:
|
|
rss_filter_ptr = (struct igb_rss_conf_ele *)
|
|
pmd_flow->rule;
|
|
ret = igb_config_rss_filter(dev,
|
|
&rss_filter_ptr->filter_info, FALSE);
|
|
if (!ret) {
|
|
TAILQ_REMOVE(&igb_filter_rss_list,
|
|
rss_filter_ptr, entries);
|
|
rte_free(rss_filter_ptr);
|
|
}
|
|
break;
|
|
default:
|
|
PMD_DRV_LOG(WARNING, "Filter type (%d) not supported",
|
|
filter_type);
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
if (ret) {
|
|
rte_flow_error_set(error, EINVAL,
|
|
RTE_FLOW_ERROR_TYPE_HANDLE,
|
|
NULL, "Failed to destroy flow");
|
|
return ret;
|
|
}
|
|
|
|
TAILQ_FOREACH(igb_flow_mem_ptr, &igb_flow_list, entries) {
|
|
if (igb_flow_mem_ptr->flow == pmd_flow) {
|
|
TAILQ_REMOVE(&igb_flow_list,
|
|
igb_flow_mem_ptr, entries);
|
|
rte_free(igb_flow_mem_ptr);
|
|
}
|
|
}
|
|
rte_free(flow);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* remove all the n-tuple filters */
|
|
static void
|
|
igb_clear_all_ntuple_filter(struct rte_eth_dev *dev)
|
|
{
|
|
struct e1000_filter_info *filter_info =
|
|
E1000_DEV_PRIVATE_TO_FILTER_INFO(dev->data->dev_private);
|
|
struct e1000_5tuple_filter *p_5tuple;
|
|
struct e1000_2tuple_filter *p_2tuple;
|
|
|
|
while ((p_5tuple = TAILQ_FIRST(&filter_info->fivetuple_list)))
|
|
igb_delete_5tuple_filter_82576(dev, p_5tuple);
|
|
|
|
while ((p_2tuple = TAILQ_FIRST(&filter_info->twotuple_list)))
|
|
igb_delete_2tuple_filter(dev, p_2tuple);
|
|
}
|
|
|
|
/* remove all the ether type filters */
|
|
static void
|
|
igb_clear_all_ethertype_filter(struct rte_eth_dev *dev)
|
|
{
|
|
struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
|
|
struct e1000_filter_info *filter_info =
|
|
E1000_DEV_PRIVATE_TO_FILTER_INFO(dev->data->dev_private);
|
|
int i;
|
|
|
|
for (i = 0; i < E1000_MAX_ETQF_FILTERS; i++) {
|
|
if (filter_info->ethertype_mask & (1 << i)) {
|
|
(void)igb_ethertype_filter_remove(filter_info,
|
|
(uint8_t)i);
|
|
E1000_WRITE_REG(hw, E1000_ETQF(i), 0);
|
|
E1000_WRITE_FLUSH(hw);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* remove the SYN filter */
|
|
static void
|
|
igb_clear_syn_filter(struct rte_eth_dev *dev)
|
|
{
|
|
struct e1000_hw *hw = E1000_DEV_PRIVATE_TO_HW(dev->data->dev_private);
|
|
struct e1000_filter_info *filter_info =
|
|
E1000_DEV_PRIVATE_TO_FILTER_INFO(dev->data->dev_private);
|
|
|
|
if (filter_info->syn_info & E1000_SYN_FILTER_ENABLE) {
|
|
filter_info->syn_info = 0;
|
|
E1000_WRITE_REG(hw, E1000_SYNQF(0), 0);
|
|
E1000_WRITE_FLUSH(hw);
|
|
}
|
|
}
|
|
|
|
/* remove all the flex filters */
|
|
static void
|
|
igb_clear_all_flex_filter(struct rte_eth_dev *dev)
|
|
{
|
|
struct e1000_filter_info *filter_info =
|
|
E1000_DEV_PRIVATE_TO_FILTER_INFO(dev->data->dev_private);
|
|
struct e1000_flex_filter *flex_filter;
|
|
|
|
while ((flex_filter = TAILQ_FIRST(&filter_info->flex_list)))
|
|
igb_remove_flex_filter(dev, flex_filter);
|
|
}
|
|
|
|
/* remove the rss filter */
|
|
static void
|
|
igb_clear_rss_filter(struct rte_eth_dev *dev)
|
|
{
|
|
struct e1000_filter_info *filter =
|
|
E1000_DEV_PRIVATE_TO_FILTER_INFO(dev->data->dev_private);
|
|
|
|
if (filter->rss_info.conf.queue_num)
|
|
igb_config_rss_filter(dev, &filter->rss_info, FALSE);
|
|
}
|
|
|
|
void
|
|
igb_filterlist_flush(struct rte_eth_dev *dev)
|
|
{
|
|
struct igb_ntuple_filter_ele *ntuple_filter_ptr;
|
|
struct igb_ethertype_filter_ele *ethertype_filter_ptr;
|
|
struct igb_eth_syn_filter_ele *syn_filter_ptr;
|
|
struct igb_flex_filter_ele *flex_filter_ptr;
|
|
struct igb_rss_conf_ele *rss_filter_ptr;
|
|
struct igb_flow_mem *igb_flow_mem_ptr;
|
|
enum rte_filter_type filter_type;
|
|
struct rte_flow *pmd_flow;
|
|
|
|
TAILQ_FOREACH(igb_flow_mem_ptr, &igb_flow_list, entries) {
|
|
if (igb_flow_mem_ptr->dev == dev) {
|
|
pmd_flow = igb_flow_mem_ptr->flow;
|
|
filter_type = pmd_flow->filter_type;
|
|
|
|
switch (filter_type) {
|
|
case RTE_ETH_FILTER_NTUPLE:
|
|
ntuple_filter_ptr =
|
|
(struct igb_ntuple_filter_ele *)
|
|
pmd_flow->rule;
|
|
TAILQ_REMOVE(&igb_filter_ntuple_list,
|
|
ntuple_filter_ptr, entries);
|
|
rte_free(ntuple_filter_ptr);
|
|
break;
|
|
case RTE_ETH_FILTER_ETHERTYPE:
|
|
ethertype_filter_ptr =
|
|
(struct igb_ethertype_filter_ele *)
|
|
pmd_flow->rule;
|
|
TAILQ_REMOVE(&igb_filter_ethertype_list,
|
|
ethertype_filter_ptr, entries);
|
|
rte_free(ethertype_filter_ptr);
|
|
break;
|
|
case RTE_ETH_FILTER_SYN:
|
|
syn_filter_ptr =
|
|
(struct igb_eth_syn_filter_ele *)
|
|
pmd_flow->rule;
|
|
TAILQ_REMOVE(&igb_filter_syn_list,
|
|
syn_filter_ptr, entries);
|
|
rte_free(syn_filter_ptr);
|
|
break;
|
|
case RTE_ETH_FILTER_FLEXIBLE:
|
|
flex_filter_ptr =
|
|
(struct igb_flex_filter_ele *)
|
|
pmd_flow->rule;
|
|
TAILQ_REMOVE(&igb_filter_flex_list,
|
|
flex_filter_ptr, entries);
|
|
rte_free(flex_filter_ptr);
|
|
break;
|
|
case RTE_ETH_FILTER_HASH:
|
|
rss_filter_ptr =
|
|
(struct igb_rss_conf_ele *)
|
|
pmd_flow->rule;
|
|
TAILQ_REMOVE(&igb_filter_rss_list,
|
|
rss_filter_ptr, entries);
|
|
rte_free(rss_filter_ptr);
|
|
break;
|
|
default:
|
|
PMD_DRV_LOG(WARNING, "Filter type"
|
|
"(%d) not supported", filter_type);
|
|
break;
|
|
}
|
|
TAILQ_REMOVE(&igb_flow_list,
|
|
igb_flow_mem_ptr,
|
|
entries);
|
|
rte_free(igb_flow_mem_ptr->flow);
|
|
rte_free(igb_flow_mem_ptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Destroy all flow rules associated with a port on igb. */
|
|
static int
|
|
igb_flow_flush(struct rte_eth_dev *dev,
|
|
__rte_unused struct rte_flow_error *error)
|
|
{
|
|
igb_clear_all_ntuple_filter(dev);
|
|
igb_clear_all_ethertype_filter(dev);
|
|
igb_clear_syn_filter(dev);
|
|
igb_clear_all_flex_filter(dev);
|
|
igb_clear_rss_filter(dev);
|
|
igb_filterlist_flush(dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
const struct rte_flow_ops igb_flow_ops = {
|
|
.validate = igb_flow_validate,
|
|
.create = igb_flow_create,
|
|
.destroy = igb_flow_destroy,
|
|
.flush = igb_flow_flush,
|
|
};
|