|
@@ -32,8 +32,9 @@
|
|
* SOFTWARE.
|
|
* SOFTWARE.
|
|
*/
|
|
*/
|
|
|
|
|
|
-#include <net/tc_act/tc_gact.h>
|
|
|
|
#include <net/tc_act/tc_mirred.h>
|
|
#include <net/tc_act/tc_mirred.h>
|
|
|
|
+#include <net/tc_act/tc_pedit.h>
|
|
|
|
+#include <net/tc_act/tc_gact.h>
|
|
#include <net/tc_act/tc_vlan.h>
|
|
#include <net/tc_act/tc_vlan.h>
|
|
|
|
|
|
#include "cxgb4.h"
|
|
#include "cxgb4.h"
|
|
@@ -41,6 +42,27 @@
|
|
|
|
|
|
#define STATS_CHECK_PERIOD (HZ / 2)
|
|
#define STATS_CHECK_PERIOD (HZ / 2)
|
|
|
|
|
|
|
|
+struct ch_tc_pedit_fields pedits[] = {
|
|
|
|
+ PEDIT_FIELDS(ETH_, DMAC_31_0, 4, dmac, 0),
|
|
|
|
+ PEDIT_FIELDS(ETH_, DMAC_47_32, 2, dmac, 4),
|
|
|
|
+ PEDIT_FIELDS(ETH_, SMAC_15_0, 2, smac, 0),
|
|
|
|
+ PEDIT_FIELDS(ETH_, SMAC_47_16, 4, smac, 2),
|
|
|
|
+ PEDIT_FIELDS(IP4_, SRC, 4, nat_fip, 0),
|
|
|
|
+ PEDIT_FIELDS(IP4_, DST, 4, nat_lip, 0),
|
|
|
|
+ PEDIT_FIELDS(IP6_, SRC_31_0, 4, nat_fip, 0),
|
|
|
|
+ PEDIT_FIELDS(IP6_, SRC_63_32, 4, nat_fip, 4),
|
|
|
|
+ PEDIT_FIELDS(IP6_, SRC_95_64, 4, nat_fip, 8),
|
|
|
|
+ PEDIT_FIELDS(IP6_, SRC_127_96, 4, nat_fip, 12),
|
|
|
|
+ PEDIT_FIELDS(IP6_, DST_31_0, 4, nat_lip, 0),
|
|
|
|
+ PEDIT_FIELDS(IP6_, DST_63_32, 4, nat_lip, 4),
|
|
|
|
+ PEDIT_FIELDS(IP6_, DST_95_64, 4, nat_lip, 8),
|
|
|
|
+ PEDIT_FIELDS(IP6_, DST_127_96, 4, nat_lip, 12),
|
|
|
|
+ PEDIT_FIELDS(TCP_, SPORT, 2, nat_fport, 0),
|
|
|
|
+ PEDIT_FIELDS(TCP_, DPORT, 2, nat_lport, 0),
|
|
|
|
+ PEDIT_FIELDS(UDP_, SPORT, 2, nat_fport, 0),
|
|
|
|
+ PEDIT_FIELDS(UDP_, DPORT, 2, nat_lport, 0),
|
|
|
|
+};
|
|
|
|
+
|
|
static struct ch_tc_flower_entry *allocate_flower_entry(void)
|
|
static struct ch_tc_flower_entry *allocate_flower_entry(void)
|
|
{
|
|
{
|
|
struct ch_tc_flower_entry *new = kzalloc(sizeof(*new), GFP_KERNEL);
|
|
struct ch_tc_flower_entry *new = kzalloc(sizeof(*new), GFP_KERNEL);
|
|
@@ -113,6 +135,11 @@ static void cxgb4_process_flow_match(struct net_device *dev,
|
|
memcpy(&fs->val.fip[0], &key->src, sizeof(key->src));
|
|
memcpy(&fs->val.fip[0], &key->src, sizeof(key->src));
|
|
memcpy(&fs->mask.lip[0], &mask->dst, sizeof(mask->dst));
|
|
memcpy(&fs->mask.lip[0], &mask->dst, sizeof(mask->dst));
|
|
memcpy(&fs->mask.fip[0], &mask->src, sizeof(mask->src));
|
|
memcpy(&fs->mask.fip[0], &mask->src, sizeof(mask->src));
|
|
|
|
+
|
|
|
|
+ /* also initialize nat_lip/fip to same values */
|
|
|
|
+ memcpy(&fs->nat_lip[0], &key->dst, sizeof(key->dst));
|
|
|
|
+ memcpy(&fs->nat_fip[0], &key->src, sizeof(key->src));
|
|
|
|
+
|
|
}
|
|
}
|
|
|
|
|
|
if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
|
|
if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
|
|
@@ -130,6 +157,10 @@ static void cxgb4_process_flow_match(struct net_device *dev,
|
|
memcpy(&fs->val.fip[0], key->src.s6_addr, sizeof(key->src));
|
|
memcpy(&fs->val.fip[0], key->src.s6_addr, sizeof(key->src));
|
|
memcpy(&fs->mask.lip[0], mask->dst.s6_addr, sizeof(mask->dst));
|
|
memcpy(&fs->mask.lip[0], mask->dst.s6_addr, sizeof(mask->dst));
|
|
memcpy(&fs->mask.fip[0], mask->src.s6_addr, sizeof(mask->src));
|
|
memcpy(&fs->mask.fip[0], mask->src.s6_addr, sizeof(mask->src));
|
|
|
|
+
|
|
|
|
+ /* also initialize nat_lip/fip to same values */
|
|
|
|
+ memcpy(&fs->nat_lip[0], key->dst.s6_addr, sizeof(key->dst));
|
|
|
|
+ memcpy(&fs->nat_fip[0], key->src.s6_addr, sizeof(key->src));
|
|
}
|
|
}
|
|
|
|
|
|
if (dissector_uses_key(cls->dissector, FLOW_DISSECTOR_KEY_PORTS)) {
|
|
if (dissector_uses_key(cls->dissector, FLOW_DISSECTOR_KEY_PORTS)) {
|
|
@@ -145,6 +176,57 @@ static void cxgb4_process_flow_match(struct net_device *dev,
|
|
fs->mask.lport = cpu_to_be16(mask->dst);
|
|
fs->mask.lport = cpu_to_be16(mask->dst);
|
|
fs->val.fport = cpu_to_be16(key->src);
|
|
fs->val.fport = cpu_to_be16(key->src);
|
|
fs->mask.fport = cpu_to_be16(mask->src);
|
|
fs->mask.fport = cpu_to_be16(mask->src);
|
|
|
|
+
|
|
|
|
+ /* also initialize nat_lport/fport to same values */
|
|
|
|
+ fs->nat_lport = cpu_to_be16(key->dst);
|
|
|
|
+ fs->nat_fport = cpu_to_be16(key->src);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (dissector_uses_key(cls->dissector, FLOW_DISSECTOR_KEY_IP)) {
|
|
|
|
+ struct flow_dissector_key_ip *key, *mask;
|
|
|
|
+
|
|
|
|
+ key = skb_flow_dissector_target(cls->dissector,
|
|
|
|
+ FLOW_DISSECTOR_KEY_IP,
|
|
|
|
+ cls->key);
|
|
|
|
+ mask = skb_flow_dissector_target(cls->dissector,
|
|
|
|
+ FLOW_DISSECTOR_KEY_IP,
|
|
|
|
+ cls->mask);
|
|
|
|
+ fs->val.tos = key->tos;
|
|
|
|
+ fs->mask.tos = mask->tos;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (dissector_uses_key(cls->dissector, FLOW_DISSECTOR_KEY_VLAN)) {
|
|
|
|
+ struct flow_dissector_key_vlan *key, *mask;
|
|
|
|
+ u16 vlan_tci, vlan_tci_mask;
|
|
|
|
+
|
|
|
|
+ key = skb_flow_dissector_target(cls->dissector,
|
|
|
|
+ FLOW_DISSECTOR_KEY_VLAN,
|
|
|
|
+ cls->key);
|
|
|
|
+ mask = skb_flow_dissector_target(cls->dissector,
|
|
|
|
+ FLOW_DISSECTOR_KEY_VLAN,
|
|
|
|
+ cls->mask);
|
|
|
|
+ vlan_tci = key->vlan_id | (key->vlan_priority <<
|
|
|
|
+ VLAN_PRIO_SHIFT);
|
|
|
|
+ vlan_tci_mask = mask->vlan_id | (mask->vlan_priority <<
|
|
|
|
+ VLAN_PRIO_SHIFT);
|
|
|
|
+ fs->val.ivlan = cpu_to_be16(vlan_tci);
|
|
|
|
+ fs->mask.ivlan = cpu_to_be16(vlan_tci_mask);
|
|
|
|
+
|
|
|
|
+ /* Chelsio adapters use ivlan_vld bit to match vlan packets
|
|
|
|
+ * as 802.1Q. Also, when vlan tag is present in packets,
|
|
|
|
+ * ethtype match is used then to match on ethtype of inner
|
|
|
|
+ * header ie. the header following the vlan header.
|
|
|
|
+ * So, set the ivlan_vld based on ethtype info supplied by
|
|
|
|
+ * TC for vlan packets if its 802.1Q. And then reset the
|
|
|
|
+ * ethtype value else, hw will try to match the supplied
|
|
|
|
+ * ethtype value with ethtype of inner header.
|
|
|
|
+ */
|
|
|
|
+ if (fs->val.ethtype == ETH_P_8021Q) {
|
|
|
|
+ fs->val.ivlan_vld = 1;
|
|
|
|
+ fs->mask.ivlan_vld = 1;
|
|
|
|
+ fs->val.ethtype = 0;
|
|
|
|
+ fs->mask.ethtype = 0;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
/* Match only packets coming from the ingress port where this
|
|
/* Match only packets coming from the ingress port where this
|
|
@@ -157,19 +239,162 @@ static void cxgb4_process_flow_match(struct net_device *dev,
|
|
static int cxgb4_validate_flow_match(struct net_device *dev,
|
|
static int cxgb4_validate_flow_match(struct net_device *dev,
|
|
struct tc_cls_flower_offload *cls)
|
|
struct tc_cls_flower_offload *cls)
|
|
{
|
|
{
|
|
|
|
+ u16 ethtype_mask = 0;
|
|
|
|
+ u16 ethtype_key = 0;
|
|
|
|
+
|
|
if (cls->dissector->used_keys &
|
|
if (cls->dissector->used_keys &
|
|
~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
|
|
~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
|
|
BIT(FLOW_DISSECTOR_KEY_BASIC) |
|
|
BIT(FLOW_DISSECTOR_KEY_BASIC) |
|
|
BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
|
|
BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
|
|
BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
|
|
BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
|
|
- BIT(FLOW_DISSECTOR_KEY_PORTS))) {
|
|
|
|
|
|
+ BIT(FLOW_DISSECTOR_KEY_PORTS) |
|
|
|
|
+ BIT(FLOW_DISSECTOR_KEY_VLAN) |
|
|
|
|
+ BIT(FLOW_DISSECTOR_KEY_IP))) {
|
|
netdev_warn(dev, "Unsupported key used: 0x%x\n",
|
|
netdev_warn(dev, "Unsupported key used: 0x%x\n",
|
|
cls->dissector->used_keys);
|
|
cls->dissector->used_keys);
|
|
return -EOPNOTSUPP;
|
|
return -EOPNOTSUPP;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ if (dissector_uses_key(cls->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
|
|
|
|
+ struct flow_dissector_key_basic *key =
|
|
|
|
+ skb_flow_dissector_target(cls->dissector,
|
|
|
|
+ FLOW_DISSECTOR_KEY_BASIC,
|
|
|
|
+ cls->key);
|
|
|
|
+ struct flow_dissector_key_basic *mask =
|
|
|
|
+ skb_flow_dissector_target(cls->dissector,
|
|
|
|
+ FLOW_DISSECTOR_KEY_BASIC,
|
|
|
|
+ cls->mask);
|
|
|
|
+ ethtype_key = ntohs(key->n_proto);
|
|
|
|
+ ethtype_mask = ntohs(mask->n_proto);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (dissector_uses_key(cls->dissector, FLOW_DISSECTOR_KEY_IP)) {
|
|
|
|
+ u16 eth_ip_type = ethtype_key & ethtype_mask;
|
|
|
|
+ struct flow_dissector_key_ip *mask;
|
|
|
|
+
|
|
|
|
+ if (eth_ip_type != ETH_P_IP && eth_ip_type != ETH_P_IPV6) {
|
|
|
|
+ netdev_err(dev, "IP Key supported only with IPv4/v6");
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ mask = skb_flow_dissector_target(cls->dissector,
|
|
|
|
+ FLOW_DISSECTOR_KEY_IP,
|
|
|
|
+ cls->mask);
|
|
|
|
+ if (mask->ttl) {
|
|
|
|
+ netdev_warn(dev, "ttl match unsupported for offload");
|
|
|
|
+ return -EOPNOTSUPP;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void offload_pedit(struct ch_filter_specification *fs, u32 val, u32 mask,
|
|
|
|
+ u8 field)
|
|
|
|
+{
|
|
|
|
+ u32 set_val = val & ~mask;
|
|
|
|
+ u32 offset = 0;
|
|
|
|
+ u8 size = 1;
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < ARRAY_SIZE(pedits); i++) {
|
|
|
|
+ if (pedits[i].field == field) {
|
|
|
|
+ offset = pedits[i].offset;
|
|
|
|
+ size = pedits[i].size;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ memcpy((u8 *)fs + offset, &set_val, size);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void process_pedit_field(struct ch_filter_specification *fs, u32 val,
|
|
|
|
+ u32 mask, u32 offset, u8 htype)
|
|
|
|
+{
|
|
|
|
+ switch (htype) {
|
|
|
|
+ case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH:
|
|
|
|
+ switch (offset) {
|
|
|
|
+ case PEDIT_ETH_DMAC_31_0:
|
|
|
|
+ fs->newdmac = 1;
|
|
|
|
+ offload_pedit(fs, val, mask, ETH_DMAC_31_0);
|
|
|
|
+ break;
|
|
|
|
+ case PEDIT_ETH_DMAC_47_32_SMAC_15_0:
|
|
|
|
+ if (~mask & PEDIT_ETH_DMAC_MASK)
|
|
|
|
+ offload_pedit(fs, val, mask, ETH_DMAC_47_32);
|
|
|
|
+ else
|
|
|
|
+ offload_pedit(fs, val >> 16, mask >> 16,
|
|
|
|
+ ETH_SMAC_15_0);
|
|
|
|
+ break;
|
|
|
|
+ case PEDIT_ETH_SMAC_47_16:
|
|
|
|
+ fs->newsmac = 1;
|
|
|
|
+ offload_pedit(fs, val, mask, ETH_SMAC_47_16);
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4:
|
|
|
|
+ switch (offset) {
|
|
|
|
+ case PEDIT_IP4_SRC:
|
|
|
|
+ offload_pedit(fs, val, mask, IP4_SRC);
|
|
|
|
+ break;
|
|
|
|
+ case PEDIT_IP4_DST:
|
|
|
|
+ offload_pedit(fs, val, mask, IP4_DST);
|
|
|
|
+ }
|
|
|
|
+ fs->nat_mode = NAT_MODE_ALL;
|
|
|
|
+ break;
|
|
|
|
+ case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6:
|
|
|
|
+ switch (offset) {
|
|
|
|
+ case PEDIT_IP6_SRC_31_0:
|
|
|
|
+ offload_pedit(fs, val, mask, IP6_SRC_31_0);
|
|
|
|
+ break;
|
|
|
|
+ case PEDIT_IP6_SRC_63_32:
|
|
|
|
+ offload_pedit(fs, val, mask, IP6_SRC_63_32);
|
|
|
|
+ break;
|
|
|
|
+ case PEDIT_IP6_SRC_95_64:
|
|
|
|
+ offload_pedit(fs, val, mask, IP6_SRC_95_64);
|
|
|
|
+ break;
|
|
|
|
+ case PEDIT_IP6_SRC_127_96:
|
|
|
|
+ offload_pedit(fs, val, mask, IP6_SRC_127_96);
|
|
|
|
+ break;
|
|
|
|
+ case PEDIT_IP6_DST_31_0:
|
|
|
|
+ offload_pedit(fs, val, mask, IP6_DST_31_0);
|
|
|
|
+ break;
|
|
|
|
+ case PEDIT_IP6_DST_63_32:
|
|
|
|
+ offload_pedit(fs, val, mask, IP6_DST_63_32);
|
|
|
|
+ break;
|
|
|
|
+ case PEDIT_IP6_DST_95_64:
|
|
|
|
+ offload_pedit(fs, val, mask, IP6_DST_95_64);
|
|
|
|
+ break;
|
|
|
|
+ case PEDIT_IP6_DST_127_96:
|
|
|
|
+ offload_pedit(fs, val, mask, IP6_DST_127_96);
|
|
|
|
+ }
|
|
|
|
+ fs->nat_mode = NAT_MODE_ALL;
|
|
|
|
+ break;
|
|
|
|
+ case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP:
|
|
|
|
+ switch (offset) {
|
|
|
|
+ case PEDIT_TCP_SPORT_DPORT:
|
|
|
|
+ if (~mask & PEDIT_TCP_UDP_SPORT_MASK)
|
|
|
|
+ offload_pedit(fs, cpu_to_be32(val) >> 16,
|
|
|
|
+ cpu_to_be32(mask) >> 16,
|
|
|
|
+ TCP_SPORT);
|
|
|
|
+ else
|
|
|
|
+ offload_pedit(fs, cpu_to_be32(val),
|
|
|
|
+ cpu_to_be32(mask), TCP_DPORT);
|
|
|
|
+ }
|
|
|
|
+ fs->nat_mode = NAT_MODE_ALL;
|
|
|
|
+ break;
|
|
|
|
+ case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP:
|
|
|
|
+ switch (offset) {
|
|
|
|
+ case PEDIT_UDP_SPORT_DPORT:
|
|
|
|
+ if (~mask & PEDIT_TCP_UDP_SPORT_MASK)
|
|
|
|
+ offload_pedit(fs, cpu_to_be32(val) >> 16,
|
|
|
|
+ cpu_to_be32(mask) >> 16,
|
|
|
|
+ UDP_SPORT);
|
|
|
|
+ else
|
|
|
|
+ offload_pedit(fs, cpu_to_be32(val),
|
|
|
|
+ cpu_to_be32(mask), UDP_DPORT);
|
|
|
|
+ }
|
|
|
|
+ fs->nat_mode = NAT_MODE_ALL;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
static void cxgb4_process_flow_actions(struct net_device *in,
|
|
static void cxgb4_process_flow_actions(struct net_device *in,
|
|
struct tc_cls_flower_offload *cls,
|
|
struct tc_cls_flower_offload *cls,
|
|
struct ch_filter_specification *fs)
|
|
struct ch_filter_specification *fs)
|
|
@@ -179,7 +404,9 @@ static void cxgb4_process_flow_actions(struct net_device *in,
|
|
|
|
|
|
tcf_exts_to_list(cls->exts, &actions);
|
|
tcf_exts_to_list(cls->exts, &actions);
|
|
list_for_each_entry(a, &actions, list) {
|
|
list_for_each_entry(a, &actions, list) {
|
|
- if (is_tcf_gact_shot(a)) {
|
|
|
|
|
|
+ if (is_tcf_gact_ok(a)) {
|
|
|
|
+ fs->action = FILTER_PASS;
|
|
|
|
+ } else if (is_tcf_gact_shot(a)) {
|
|
fs->action = FILTER_DROP;
|
|
fs->action = FILTER_DROP;
|
|
} else if (is_tcf_mirred_egress_redirect(a)) {
|
|
} else if (is_tcf_mirred_egress_redirect(a)) {
|
|
int ifindex = tcf_mirred_ifindex(a);
|
|
int ifindex = tcf_mirred_ifindex(a);
|
|
@@ -210,19 +437,152 @@ static void cxgb4_process_flow_actions(struct net_device *in,
|
|
default:
|
|
default:
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
+ } else if (is_tcf_pedit(a)) {
|
|
|
|
+ u32 mask, val, offset;
|
|
|
|
+ int nkeys, i;
|
|
|
|
+ u8 htype;
|
|
|
|
+
|
|
|
|
+ nkeys = tcf_pedit_nkeys(a);
|
|
|
|
+ for (i = 0; i < nkeys; i++) {
|
|
|
|
+ htype = tcf_pedit_htype(a, i);
|
|
|
|
+ mask = tcf_pedit_mask(a, i);
|
|
|
|
+ val = tcf_pedit_val(a, i);
|
|
|
|
+ offset = tcf_pedit_offset(a, i);
|
|
|
|
+
|
|
|
|
+ process_pedit_field(fs, val, mask, offset,
|
|
|
|
+ htype);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static bool valid_l4_mask(u32 mask)
|
|
|
|
+{
|
|
|
|
+ u16 hi, lo;
|
|
|
|
+
|
|
|
|
+ /* Either the upper 16-bits (SPORT) OR the lower
|
|
|
|
+ * 16-bits (DPORT) can be set, but NOT BOTH.
|
|
|
|
+ */
|
|
|
|
+ hi = (mask >> 16) & 0xFFFF;
|
|
|
|
+ lo = mask & 0xFFFF;
|
|
|
|
+
|
|
|
|
+ return hi && lo ? false : true;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static bool valid_pedit_action(struct net_device *dev,
|
|
|
|
+ const struct tc_action *a)
|
|
|
|
+{
|
|
|
|
+ u32 mask, offset;
|
|
|
|
+ u8 cmd, htype;
|
|
|
|
+ int nkeys, i;
|
|
|
|
+
|
|
|
|
+ nkeys = tcf_pedit_nkeys(a);
|
|
|
|
+ for (i = 0; i < nkeys; i++) {
|
|
|
|
+ htype = tcf_pedit_htype(a, i);
|
|
|
|
+ cmd = tcf_pedit_cmd(a, i);
|
|
|
|
+ mask = tcf_pedit_mask(a, i);
|
|
|
|
+ offset = tcf_pedit_offset(a, i);
|
|
|
|
+
|
|
|
|
+ if (cmd != TCA_PEDIT_KEY_EX_CMD_SET) {
|
|
|
|
+ netdev_err(dev, "%s: Unsupported pedit cmd\n",
|
|
|
|
+ __func__);
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ switch (htype) {
|
|
|
|
+ case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH:
|
|
|
|
+ switch (offset) {
|
|
|
|
+ case PEDIT_ETH_DMAC_31_0:
|
|
|
|
+ case PEDIT_ETH_DMAC_47_32_SMAC_15_0:
|
|
|
|
+ case PEDIT_ETH_SMAC_47_16:
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ netdev_err(dev, "%s: Unsupported pedit field\n",
|
|
|
|
+ __func__);
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4:
|
|
|
|
+ switch (offset) {
|
|
|
|
+ case PEDIT_IP4_SRC:
|
|
|
|
+ case PEDIT_IP4_DST:
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ netdev_err(dev, "%s: Unsupported pedit field\n",
|
|
|
|
+ __func__);
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6:
|
|
|
|
+ switch (offset) {
|
|
|
|
+ case PEDIT_IP6_SRC_31_0:
|
|
|
|
+ case PEDIT_IP6_SRC_63_32:
|
|
|
|
+ case PEDIT_IP6_SRC_95_64:
|
|
|
|
+ case PEDIT_IP6_SRC_127_96:
|
|
|
|
+ case PEDIT_IP6_DST_31_0:
|
|
|
|
+ case PEDIT_IP6_DST_63_32:
|
|
|
|
+ case PEDIT_IP6_DST_95_64:
|
|
|
|
+ case PEDIT_IP6_DST_127_96:
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ netdev_err(dev, "%s: Unsupported pedit field\n",
|
|
|
|
+ __func__);
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP:
|
|
|
|
+ switch (offset) {
|
|
|
|
+ case PEDIT_TCP_SPORT_DPORT:
|
|
|
|
+ if (!valid_l4_mask(~mask)) {
|
|
|
|
+ netdev_err(dev, "%s: Unsupported mask for TCP L4 ports\n",
|
|
|
|
+ __func__);
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ netdev_err(dev, "%s: Unsupported pedit field\n",
|
|
|
|
+ __func__);
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP:
|
|
|
|
+ switch (offset) {
|
|
|
|
+ case PEDIT_UDP_SPORT_DPORT:
|
|
|
|
+ if (!valid_l4_mask(~mask)) {
|
|
|
|
+ netdev_err(dev, "%s: Unsupported mask for UDP L4 ports\n",
|
|
|
|
+ __func__);
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ netdev_err(dev, "%s: Unsupported pedit field\n",
|
|
|
|
+ __func__);
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ netdev_err(dev, "%s: Unsupported pedit type\n",
|
|
|
|
+ __func__);
|
|
|
|
+ return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+ return true;
|
|
}
|
|
}
|
|
|
|
|
|
static int cxgb4_validate_flow_actions(struct net_device *dev,
|
|
static int cxgb4_validate_flow_actions(struct net_device *dev,
|
|
struct tc_cls_flower_offload *cls)
|
|
struct tc_cls_flower_offload *cls)
|
|
{
|
|
{
|
|
const struct tc_action *a;
|
|
const struct tc_action *a;
|
|
|
|
+ bool act_redir = false;
|
|
|
|
+ bool act_pedit = false;
|
|
|
|
+ bool act_vlan = false;
|
|
LIST_HEAD(actions);
|
|
LIST_HEAD(actions);
|
|
|
|
|
|
tcf_exts_to_list(cls->exts, &actions);
|
|
tcf_exts_to_list(cls->exts, &actions);
|
|
list_for_each_entry(a, &actions, list) {
|
|
list_for_each_entry(a, &actions, list) {
|
|
- if (is_tcf_gact_shot(a)) {
|
|
|
|
|
|
+ if (is_tcf_gact_ok(a)) {
|
|
|
|
+ /* Do nothing */
|
|
|
|
+ } else if (is_tcf_gact_shot(a)) {
|
|
/* Do nothing */
|
|
/* Do nothing */
|
|
} else if (is_tcf_mirred_egress_redirect(a)) {
|
|
} else if (is_tcf_mirred_egress_redirect(a)) {
|
|
struct adapter *adap = netdev2adap(dev);
|
|
struct adapter *adap = netdev2adap(dev);
|
|
@@ -247,6 +607,7 @@ static int cxgb4_validate_flow_actions(struct net_device *dev,
|
|
__func__);
|
|
__func__);
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
+ act_redir = true;
|
|
} else if (is_tcf_vlan(a)) {
|
|
} else if (is_tcf_vlan(a)) {
|
|
u16 proto = be16_to_cpu(tcf_vlan_push_proto(a));
|
|
u16 proto = be16_to_cpu(tcf_vlan_push_proto(a));
|
|
u32 vlan_action = tcf_vlan_action(a);
|
|
u32 vlan_action = tcf_vlan_action(a);
|
|
@@ -267,11 +628,25 @@ static int cxgb4_validate_flow_actions(struct net_device *dev,
|
|
__func__);
|
|
__func__);
|
|
return -EOPNOTSUPP;
|
|
return -EOPNOTSUPP;
|
|
}
|
|
}
|
|
|
|
+ act_vlan = true;
|
|
|
|
+ } else if (is_tcf_pedit(a)) {
|
|
|
|
+ bool pedit_valid = valid_pedit_action(dev, a);
|
|
|
|
+
|
|
|
|
+ if (!pedit_valid)
|
|
|
|
+ return -EOPNOTSUPP;
|
|
|
|
+ act_pedit = true;
|
|
} else {
|
|
} else {
|
|
netdev_err(dev, "%s: Unsupported action\n", __func__);
|
|
netdev_err(dev, "%s: Unsupported action\n", __func__);
|
|
return -EOPNOTSUPP;
|
|
return -EOPNOTSUPP;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ if ((act_pedit || act_vlan) && !act_redir) {
|
|
|
|
+ netdev_err(dev, "%s: pedit/vlan rewrite invalid without egress redirect\n",
|
|
|
|
+ __func__);
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -299,8 +674,8 @@ int cxgb4_tc_flower_replace(struct net_device *dev,
|
|
|
|
|
|
fs = &ch_flower->fs;
|
|
fs = &ch_flower->fs;
|
|
fs->hitcnts = 1;
|
|
fs->hitcnts = 1;
|
|
- cxgb4_process_flow_actions(dev, cls, fs);
|
|
|
|
cxgb4_process_flow_match(dev, cls, fs);
|
|
cxgb4_process_flow_match(dev, cls, fs);
|
|
|
|
+ cxgb4_process_flow_actions(dev, cls, fs);
|
|
|
|
|
|
fidx = cxgb4_get_free_ftid(dev, fs->type ? PF_INET6 : PF_INET);
|
|
fidx = cxgb4_get_free_ftid(dev, fs->type ? PF_INET6 : PF_INET);
|
|
if (fidx < 0) {
|
|
if (fidx < 0) {
|