cxgb4: add tc flower support for L3/L4 rewrite
authorKumar Sanghvi <kumaras@chelsio.com>
Wed, 18 Oct 2017 15:19:14 +0000 (20:49 +0530)
committerDavid S. Miller <davem@davemloft.net>
Fri, 20 Oct 2017 12:06:53 +0000 (13:06 +0100)
Adds support to rewrite L3/L4 fields via TC-PEDIT action.
Supported fields for rewrite are:
IPv4 src/dst address, IPv6 src/dst address, TCP/UDP sport/dport.

Also, process match fields first and then process the action items.

Refactor pedit action validation to separate function to avoid
excessive code indentation.

Signed-off-by: Kumar Sanghvi <kumaras@chelsio.com>
Signed-off-by: Rahul Lakkireddy <rahul.lakkireddy@chelsio.com>
Signed-off-by: Ganesh Goudar <ganeshgr@chelsio.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c
drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.h

index 6a1c0b1fe8d0077f05717a850fdb394c0d0298df..92a0b022687ee7b139da3e8d69a48ab524cdcdd9 100644 (file)
@@ -1097,6 +1097,10 @@ enum {
        VLAN_REWRITE
 };
 
+enum {
+       NAT_MODE_ALL = 7,       /* NAT on entire 4-tuple */
+};
+
 /* Host shadow copy of ingress filter entry.  This is in host native format
  * and doesn't match the ordering or bit order, etc. of the hardware of the
  * firmware command.  The use of bit-field structure elements is purely to
index 34d67a2a86f4e5fc0ed6b9ad68e52469594bd2a6..892dfce1fa6391f7a7d3ea70cfd414f520c5707b 100644 (file)
@@ -47,6 +47,20 @@ struct ch_tc_pedit_fields pedits[] = {
        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)
@@ -121,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->mask.lip[0], &mask->dst, sizeof(mask->dst));
                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) {
@@ -138,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->mask.lip[0], mask->dst.s6_addr, sizeof(mask->dst));
                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)) {
@@ -153,6 +176,10 @@ static void cxgb4_process_flow_match(struct net_device *dev,
                fs->mask.lport = cpu_to_be16(mask->dst);
                fs->val.fport = cpu_to_be16(key->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)) {
@@ -301,6 +328,70 @@ static void process_pedit_field(struct ch_filter_specification *fs, u32 val,
                        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;
        }
 }
 
@@ -365,6 +456,119 @@ static void cxgb4_process_flow_actions(struct net_device *in,
        }
 }
 
+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,
                                       struct tc_cls_flower_offload *cls)
 {
@@ -426,43 +630,10 @@ static int cxgb4_validate_flow_actions(struct net_device *dev,
                        }
                        act_vlan = true;
                } else if (is_tcf_pedit(a)) {
-                       u32 mask, val, offset;
-                       u8 cmd, htype;
-                       int nkeys, i;
+                       bool pedit_valid = valid_pedit_action(dev, a);
 
-                       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);
-                               val = tcf_pedit_val(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 -EOPNOTSUPP;
-                               }
-
-                               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 -EOPNOTSUPP;
-                                       }
-                                       break;
-                               default:
-                                       netdev_err(dev, "%s: Unsupported pedit type\n",
-                                                  __func__);
-                                       return -EOPNOTSUPP;
-                               }
-                       }
+                       if (!pedit_valid)
+                               return -EOPNOTSUPP;
                        act_pedit = true;
                } else {
                        netdev_err(dev, "%s: Unsupported action\n", __func__);
@@ -503,8 +674,8 @@ int cxgb4_tc_flower_replace(struct net_device *dev,
 
        fs = &ch_flower->fs;
        fs->hitcnts = 1;
-       cxgb4_process_flow_actions(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);
        if (fidx < 0) {
index a2acb782918f1584ced33942c3dced4d0e8209a9..202d5c9ec3037437198ec78eec2cece42049a1fe 100644 (file)
@@ -59,6 +59,25 @@ enum {
        ETH_DMAC_47_32, /* dmac bits 32..47 */
        ETH_SMAC_15_0,  /* smac bits 0.. 15 */
        ETH_SMAC_47_16, /* smac bits 16..47 */
+
+       IP4_SRC,        /* 32-bit IPv4 src  */
+       IP4_DST,        /* 32-bit IPv4 dst  */
+
+       IP6_SRC_31_0,   /* src bits 0..  31 */
+       IP6_SRC_63_32,  /* src bits 63.. 32 */
+       IP6_SRC_95_64,  /* src bits 95.. 64 */
+       IP6_SRC_127_96, /* src bits 127..96 */
+
+       IP6_DST_31_0,   /* dst bits 0..  31 */
+       IP6_DST_63_32,  /* dst bits 63.. 32 */
+       IP6_DST_95_64,  /* dst bits 95.. 64 */
+       IP6_DST_127_96, /* dst bits 127..96 */
+
+       TCP_SPORT,      /* 16-bit TCP sport */
+       TCP_DPORT,      /* 16-bit TCP dport */
+
+       UDP_SPORT,      /* 16-bit UDP sport */
+       UDP_DPORT,      /* 16-bit UDP dport */
 };
 
 struct ch_tc_pedit_fields {
@@ -72,9 +91,22 @@ struct ch_tc_pedit_fields {
                offsetof(struct ch_filter_specification, fs_field) + (offset) }
 
 #define PEDIT_ETH_DMAC_MASK            0xffff
+#define PEDIT_TCP_UDP_SPORT_MASK       0xffff
 #define PEDIT_ETH_DMAC_31_0            0x0
 #define PEDIT_ETH_DMAC_47_32_SMAC_15_0 0x4
 #define PEDIT_ETH_SMAC_47_16           0x8
+#define PEDIT_IP4_SRC                  0xC
+#define PEDIT_IP4_DST                  0x10
+#define PEDIT_IP6_SRC_31_0             0x8
+#define PEDIT_IP6_SRC_63_32            0xC
+#define PEDIT_IP6_SRC_95_64            0x10
+#define PEDIT_IP6_SRC_127_96           0x14
+#define PEDIT_IP6_DST_31_0             0x18
+#define PEDIT_IP6_DST_63_32            0x1C
+#define PEDIT_IP6_DST_95_64            0x20
+#define PEDIT_IP6_DST_127_96           0x24
+#define PEDIT_TCP_SPORT_DPORT          0x0
+#define PEDIT_UDP_SPORT_DPORT          0x0
 
 int cxgb4_tc_flower_replace(struct net_device *dev,
                            struct tc_cls_flower_offload *cls);