From: Alexandru Ardelean <ardeleana...@gmail.com>

This has been implemented on a BCM53128 chip.
May very likely work on BCM53125 which is a 4 port variant.
No idea on what other chips it works, but since this is hidden
behind a config flag, others are free to try it out themselves.

We consider this a debugging feature of the B53 hardware, since
you'd normally not need to mirror traffic from ports to a single
capture port, unless debugging or doing some port snooping.

Because the feature is a bit complex in hardware, string parsing
is used to make this a bit more comfortable when setting it
from user space.

To enable/disable port mirroring (or capture):
   swconfig dev switch0 set port_capture on [or off]
   swconfig dev switch0 set apply

Before calling 'set apply', the current port capture configuration
can be read, before applying it to the registers.
   swconfig dev switch0 get port_capture

The output will be:
  Mirror Capture: On
    Capture Port: 8
    Block Not Mirrored Traffic: No
  Incoming Filter:
    Address Filter: None
    Mirrored Ports: None
  Outgoing Filter:
    Address Filter: None
    Mirrored Ports: None

The 'off' parameter takes no arguments.

The 'on' parameter takes these arguments:
   swconfig dev switch0 set port_capture \
   "on cap_port 8 \
    block_not_mirrored \
    in_filter:(da XX:XX:XX:XX:XX:XX ports 1ff div 3ffff) \
    out_filter:(sa YY:YY:YY:YY:YY:YY ports 0ff div 4)" \

Above is an example of how this would be called.
Backslashes mean that the command should be a single line.
The parsing is stupid simple, so quotes, cases & spaces are important.
 - cap_port - the port on which to forward all the captured traffic
 - block_not_mirrored - block the traffic that is not mirrored
 - in_filter/out_filter - filters for incoming/outgoing traffic capture
                          default is for no filters, and no mirrored ports
   - da (or sa) - but not both; filter by MAC address; not specifying it
                  should capture all packets
   - ports - bitmask port list, which ports to capture traffic
   - div - capture every Nth packet; default (0) captures all

Once mirrored ports are set, traffic will be forwarded to the
capture port based on filter parameters.

Signed-off-by: Alexandru Ardelean <ardeleana...@gmail.com>
---
 .../generic/files/drivers/net/phy/b53/Kconfig      |  13 +
 .../generic/files/drivers/net/phy/b53/b53_common.c | 292 +++++++++++++++++++++
 .../generic/files/drivers/net/phy/b53/b53_priv.h   |  25 ++
 .../generic/files/drivers/net/phy/b53/b53_regs.h   |  24 ++
 4 files changed, 354 insertions(+)

diff --git a/target/linux/generic/files/drivers/net/phy/b53/Kconfig 
b/target/linux/generic/files/drivers/net/phy/b53/Kconfig
index 67e053e..545814a 100644
--- a/target/linux/generic/files/drivers/net/phy/b53/Kconfig
+++ b/target/linux/generic/files/drivers/net/phy/b53/Kconfig
@@ -35,3 +35,16 @@ config B53_SRAB_DRIVER
 
 config B53_PHY_FIXUP
        bool
+       depends on B53
+
+config B53_HW_DEBUG_FEATURES
+       depends on B53
+       bool "B53 hw debug features"
+       default n
+       help
+         Various features that are supported by the switch chip and are
+         not needed for normal functioning of the switch chip. 
+         They could come in handy for debugging 
+         So far they've been tested on BCM53128, and should work on BCM53125
+         since that's a 4 port variant.
+
diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_common.c 
b/target/linux/generic/files/drivers/net/phy/b53/b53_common.c
index b82bc93..bdd4006 100644
--- a/target/linux/generic/files/drivers/net/phy/b53/b53_common.c
+++ b/target/linux/generic/files/drivers/net/phy/b53/b53_common.c
@@ -404,6 +404,70 @@ static void b53_enable_ports(struct b53_device *dev)
        }
 }
 
+#ifdef CONFIG_B53_HW_DEBUG_FEATURES
+static void b53_enable_port_capture_filter(struct b53_device *dev,
+                           struct b53_port_capture_filter *flt, u32 reg_off)
+{
+       u16 flt_ctrl;
+
+       b53_read16(dev, B53_MGMT_PAGE, B53_MIRROR_CTRL_FILTER(reg_off), 
&flt_ctrl);
+
+       flt_ctrl &= ~(B53_MIRROR_FILTER_BY_DA | B53_MIRROR_FILTER_BY_SA);
+       if (flt && (flt->mode == B53_CAPTURE_BY_DA))
+               flt_ctrl |= B53_MIRROR_FILTER_BY_DA;
+       else if (flt && (flt->mode == B53_CAPTURE_BY_SA))
+               flt_ctrl |= B53_MIRROR_FILTER_BY_SA;
+
+       if (flt_ctrl & (B53_MIRROR_FILTER_BY_DA | B53_MIRROR_FILTER_BY_SA))
+               b53_write48(dev, B53_MGMT_PAGE, B53_MIRROR_MAC_ADDR(reg_off),
+                           b53_mac_array_to_u64(flt->mac));
+
+       if (flt && flt->nth) {
+               b53_write16(dev, B53_MGMT_PAGE, B53_MIRROR_DIVIDER(reg_off),
+                           B53_MIRROR_DIVIDER_VALUE(flt->divider));
+               flt_ctrl |= B53_MIRROR_DIV_EN;
+       } else
+               flt_ctrl &= ~B53_MIRROR_DIV_EN;
+
+       flt_ctrl &= ~0x1ff;
+       if (flt)
+               flt_ctrl |= B53_MIRROR_PORTS_MASK(flt->ports_mask);
+
+       b53_write16(dev, B53_MGMT_PAGE, B53_MIRROR_CTRL_FILTER(reg_off), 
flt_ctrl);
+}
+
+static void b53_enable_port_capture(struct b53_device *dev)
+{
+       u16 port_mirror_ctrl;
+
+       b53_read16(dev, B53_MGMT_PAGE, B53_MIRROR_CTRL, &port_mirror_ctrl);
+
+       if (!dev->port_capture.enable) {
+               port_mirror_ctrl &= ~B53_MIRROR_CTRL_EN;
+               goto out;
+       } else
+               port_mirror_ctrl |= B53_MIRROR_CTRL_EN;
+
+       if (dev->port_capture.block_not_mirrored)
+               port_mirror_ctrl |= B53_BLOCK_NOT_MIR;
+       else
+               port_mirror_ctrl &= ~B53_BLOCK_NOT_MIR;
+
+       B53_CAP_PORT_SET(port_mirror_ctrl, dev->port_capture.capture_port);
+
+       b53_enable_port_capture_filter(dev, dev->port_capture.in_filter,
+                                      B53_MIR_IN_FILTER_OFFSET);
+       b53_enable_port_capture_filter(dev, dev->port_capture.out_filter,
+                                      B53_MIR_OUT_FILTER_OFFSET);
+
+out:
+       b53_write16(dev, B53_MGMT_PAGE, B53_MIRROR_CTRL, port_mirror_ctrl);
+}
+
+#else
+#define b53_enable_port_capture(x)
+#endif /* CONFIG_B53_HW_DEBUG_FEATURES */
+
 static void b53_enable_mib(struct b53_device *dev)
 {
        u8 gc;
@@ -452,6 +516,7 @@ static int b53_apply(struct b53_device *dev)
        }
 
        b53_enable_ports(dev);
+       b53_enable_port_capture(dev);
 
        if (!is5325(dev) && !is5365(dev))
                b53_set_jumbo(dev, dev->enable_jumbo, 1);
@@ -506,6 +571,8 @@ static int b53_switch_reset(struct b53_device *dev)
 
        /* enable all ports */
        b53_enable_ports(dev);
+       /* disable port capture (if enabled) */
+       b53_enable_port_capture(dev);
 
        /* configure MII port if necessary */
        if (is5325(dev)) {
@@ -610,6 +677,220 @@ static int b53_global_set_4095_enable(struct switch_dev 
*dev,
        return 0;
 }
 
+#ifdef CONFIG_B53_HW_DEBUG_FEATURES
+static int b53_global_get_port_capture_filter(struct b53_device *dev,
+                                      int len, const char *type,
+                                      struct b53_port_capture_filter *flt)
+{
+       int i, ports;
+       const char *flt_mode = "None";
+
+       if (!flt)
+               goto print;
+
+       if (flt->mode == B53_CAPTURE_BY_DA)
+               flt_mode = "By Destination Address";
+       else if (flt->mode == B53_CAPTURE_BY_SA)
+               flt_mode = "By Source Address";
+
+print:
+       len += snprintf(dev->buf + len, (B53_BUF_SIZE - len),
+                       "%s Filter:\n  Address Filter: %s\n", type, flt_mode);
+       if (flt && flt->mode != B53_CAPTURE_ALL)
+               len += snprintf(dev->buf + len, (B53_BUF_SIZE - len),
+                               "  MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", 
+                               flt->mac[0],flt->mac[1],flt->mac[2],
+                               flt->mac[3],flt->mac[4],flt->mac[5]);
+       if (flt && flt->nth)
+               len += snprintf(dev->buf + len, (B53_BUF_SIZE - len),
+                               "  Capture Nth: %u\n", flt->divider);
+
+       len += snprintf(dev->buf + len,(B53_BUF_SIZE - len),"  Mirrored 
Ports:");
+       ports = 0;
+       b53_for_each_port(dev, i) {
+               if (flt && (flt->ports_mask & BIT(i))) {
+                       len += snprintf(dev->buf + len, (B53_BUF_SIZE - len)," 
%u", i);
+                       ports++;
+               }
+       }
+       len += snprintf(dev->buf + len, (B53_BUF_SIZE - len),"%s\n",
+                      (ports ? "" : " None"));
+
+       return len;
+}
+
+static int b53_global_get_port_capture(struct switch_dev *dev,
+                                     const struct switch_attr *attr,
+                                     struct switch_val *val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+       int len;
+
+       len = snprintf(priv->buf, B53_BUF_SIZE, "Mirror Capture: %s\n",
+                      priv->port_capture.enable ? "On" : "Off");
+       if (!priv->port_capture.enable)
+               goto out;
+       len += snprintf(priv->buf + len,B53_BUF_SIZE - len,"  Capture Port: 
%u\n",
+                       priv->port_capture.capture_port);
+       len += snprintf(priv->buf + len,B53_BUF_SIZE - len,
+                       "  Block Not Mirrored Traffic: %s\n",
+                       priv->port_capture.block_not_mirrored ? "Yes" : "No");
+
+       len = b53_global_get_port_capture_filter(priv, len, "Incoming",
+                                                priv->port_capture.in_filter);
+       len = b53_global_get_port_capture_filter(priv, len, "Outgoing",
+                                                priv->port_capture.out_filter);
+out:
+       val->len = len;
+       val->value.s = priv->buf;
+
+       return 0;
+}
+
+static bool b53_mac_from_string(const char *s, u8 *dst)
+{
+       if (6 == sscanf(s, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
+                       &dst[0], &dst[1], &dst[2], &dst[3], &dst[4], &dst[5]))
+               return true;
+       if (6 == sscanf(s, "%02hhX:%02hhX:%02hhX:%02hhX:%02hhx:%02hhX",
+                       &dst[0], &dst[1], &dst[2], &dst[3], &dst[4], &dst[5]))
+               return true;
+
+       return false;
+}
+
+static int b53_ports_from_string(const char *s, const char *endp, u16 *ports)
+{
+       u16 p;
+
+       /* read ports mask (if specified) */
+       if (!(s = strstr(s, "ports")))
+               return 0;
+       if (endp && s >= endp)
+               return 0;
+
+       if (sscanf(s + 6, "%03hX", &p) < 1 && sscanf(s + 6, "%03hx", &p) < 1)
+               return -EINVAL;
+       if (p > 0x1ff)
+               return -EINVAL;
+       *ports = p;
+
+       return 1;
+}
+
+static int b53_global_set_port_capture_filter(struct b53_device *dev,
+                                     struct b53_port_capture_filter **flt, 
const char *s)
+{
+       const char *s1, *endp;
+       u16 ports_mask;
+       u32 div;
+       int rc = -EINVAL;
+
+       /* check end paranthesis; where our params should end */
+       if (!(endp = strstr(s, ")")))
+               goto out;
+
+       if (!*flt)
+               *flt = devm_kzalloc(dev->dev,
+                                   sizeof(struct b53_port_capture_filter),
+                                   GFP_KERNEL);
+       if (!*flt)
+               return -ENOMEM;
+
+       /* first read if we filter by DA or SA */
+       if ((s1 = strstr(s, "da")) && s1 < endp)
+               (*flt)->mode = B53_CAPTURE_BY_DA;
+       else if ((s1 = strstr(s, "sa")) && s1 < endp)
+               (*flt)->mode = B53_CAPTURE_BY_SA;
+       else
+               (*flt)->mode = B53_CAPTURE_ALL;
+
+       if ((*flt)->mode != B53_CAPTURE_ALL &&
+          !b53_mac_from_string(s1 + 3, (*flt)->mac)) {
+               rc = -EINVAL;
+               goto out;
+       }
+
+       /* read divider (if specified) */
+       if ((s1 = strstr(s, "div")) && s1 >= endp)
+               s1 = NULL;
+       if (s1 && (sscanf(s1 + 4, "%u", &div) < 1 || div > 0x3ffff)) {
+               rc = -EINVAL;
+               goto out;
+       }
+       if (s1) {
+               (*flt)->nth = 1;
+               (*flt)->divider = div;
+       }
+
+       /* read ports mask (if specified) */
+       if (b53_ports_from_string(s, endp, &ports_mask) > 0)
+               (*flt)->ports_mask = ports_mask;
+       else
+               (*flt)->ports_mask = 0;
+
+       return 0;
+
+out:
+       return rc;
+}
+
+static inline void b53_global_port_capture_cleanup(struct b53_device *dev)
+{
+       devm_kfree(dev->dev, dev->port_capture.in_filter);
+       devm_kfree(dev->dev, dev->port_capture.out_filter);
+       memset(&dev->port_capture, 0, sizeof(dev->port_capture));
+}
+
+static int b53_global_set_port_capture(struct switch_dev *dev,
+                                     const struct switch_attr *attr,
+                                     struct switch_val *val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+       const char *s;
+       unsigned int cap_port;
+       int rc = 0;
+
+       if (strstr(val->value.s, "off"))
+               goto out_cleanup;
+
+       if (strstr(val->value.s, "on"))
+               priv->port_capture.enable = 1;
+       priv->port_capture.block_not_mirrored = !!strstr(val->value.s, 
"block_not_mirrored");
+
+       /* read capture port (if specified) */
+       if ((s = strstr(val->value.s, "cap_port")) &&
+          sscanf(s + 9, "%u", &cap_port) < 1 &&
+          cap_port > B53_N_PORTS) {
+               rc = -EINVAL;
+               goto out_cleanup;
+       }
+       priv->port_capture.capture_port = (s) ? cap_port : dev->cpu_port;
+
+       if ((s = strstr(val->value.s, "in_filter:")) && (s = strstr(s, "(")))
+               rc = b53_global_set_port_capture_filter(priv,
+                         &priv->port_capture.in_filter, s);
+       if (rc)
+               goto out_cleanup;
+
+       if ((s = strstr(val->value.s, "out_filter:")) && (s = strstr(s, "(")))
+               rc = b53_global_set_port_capture_filter(priv,
+                         &priv->port_capture.out_filter, s);
+       if (!rc)
+               goto out;
+
+out_cleanup:
+       b53_global_port_capture_cleanup(priv);
+out:
+       return rc;
+}
+
+#else
+
+#define b53_global_port_capture_cleanup(x)
+
+#endif /* CONFIG_B53_HW_DEBUG_FEATURES */
+
 static int b53_global_get_ports(struct switch_dev *dev,
                                const struct switch_attr *attr,
                                struct switch_val *val)
@@ -778,6 +1059,8 @@ static int b53_global_reset_switch(struct switch_dev *dev)
        memset(priv->vlans, 0, sizeof(priv->vlans) * dev->vlans);
        memset(priv->ports, 0, sizeof(priv->ports) * dev->ports);
 
+       b53_global_port_capture_cleanup(priv);
+
        return b53_switch_reset(priv);
 }
 
@@ -939,6 +1222,15 @@ static struct switch_attr b53_global_ops[] = {
                .get = b53_global_get_4095_enable,
                .max = 1,
        },
+#ifdef CONFIG_B53_HW_DEBUG_FEATURES
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "port_capture",
+               .description = "Ports Capture Traffic",
+               .set = b53_global_set_port_capture,
+               .get = b53_global_get_port_capture,
+       },
+#endif /* CONFIG_B53_HW_DEBUG_FEATURES */
 };
 
 static struct switch_attr b53_port_ops[] = {
diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h 
b/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h
index bc9b533..1edad71 100644
--- a/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h
+++ b/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h
@@ -22,6 +22,7 @@
 #include <linux/kernel.h>
 #include <linux/mutex.h>
 #include <linux/switch.h>
+#include <linux/if_ether.h>
 
 struct b53_device;
 
@@ -67,6 +68,21 @@ struct b53_port {
        unsigned int    pvid:12;
 };
 
+#ifdef CONFIG_B53_HW_DEBUG_FEATURES
+enum {
+       B53_CAPTURE_ALL = 0,
+       B53_CAPTURE_BY_DA,
+       B53_CAPTURE_BY_SA,
+};
+struct b53_port_capture_filter {
+       unsigned mode:2;
+       unsigned nth:1;
+       unsigned ports_mask:9;
+       u8 mac[ETH_ALEN];
+       unsigned divider:10;
+};
+#endif
+
 struct b53_device {
        struct switch_dev sw_dev;
        struct b53_platform_data *pdata;
@@ -95,6 +111,15 @@ struct b53_device {
        unsigned enable_vlan:1;
        unsigned enable_jumbo:1;
        unsigned allow_vid_4095:1;
+#ifdef CONFIG_B53_HW_DEBUG_FEATURES
+       struct {
+               unsigned enable:1;
+               unsigned block_not_mirrored:1;
+               unsigned capture_port:4;
+               struct b53_port_capture_filter *in_filter;
+               struct b53_port_capture_filter *out_filter;
+       } port_capture;
+#endif
 
        struct b53_port *ports;
        struct b53_vlan *vlans;
diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_regs.h 
b/target/linux/generic/files/drivers/net/phy/b53/b53_regs.h
index 4379c58..28361e6 100644
--- a/target/linux/generic/files/drivers/net/phy/b53/b53_regs.h
+++ b/target/linux/generic/files/drivers/net/phy/b53/b53_regs.h
@@ -166,6 +166,30 @@ static inline u64 b53_mac_array_to_u64(const u8 *u8_arr) {
 #define   GC_FRM_MGMT_PORT_04          0x00
 #define   GC_FRM_MGMT_PORT_MII         0x80
 
+/* Mirror Capture Control Register (16 bit) */
+#define B53_MIRROR_CTRL                        0x10
+#define   B53_CAP_PORT_SET(r,p)                (r = (r & ~0x0f) | (p & 0x0f))
+#define   B53_BLOCK_NOT_MIR            BIT(14)
+#define   B53_MIRROR_CTRL_EN           BIT(15)
+
+/* Offset for groups of filter registers: 0x0 = In, 0xA = Out */
+#define B53_MIR_IN_FILTER_OFFSET       0
+#define B53_MIR_OUT_FILTER_OFFSET      0x0A
+
+/* In/Out Mirror Control Registers (16 bit) */
+#define B53_MIRROR_CTRL_FILTER(o)      (0x12 + o)
+#define   B53_MIRROR_PORTS_MASK(p)     (0x1ff & p)
+#define   B53_MIRROR_DIV_EN            BIT(13)
+#define   B53_MIRROR_FILTER_BY_DA      BIT(14)    /* Do not use both DA/SA */
+#define   B53_MIRROR_FILTER_BY_SA      BIT(15)
+
+/* In/Out Divider Registers (16 bit) */
+#define B53_MIRROR_DIVIDER(o)          (0x14 + o)
+#define   B53_MIRROR_DIVIDER_VALUE(n)  (0x1ff & n)
+
+/* In/Out MAC Address Registers (48 bit) */
+#define B53_MIRROR_MAC_ADDR(o)         (0x16 + o)
+
 /* Device ID register (8 or 32 bit) */
 #define B53_DEVICE_ID                  0x30
 
-- 
2.1.2
_______________________________________________
openwrt-devel mailing list
openwrt-devel@lists.openwrt.org
https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel

Reply via email to