[OpenWrt-Devel] [PATCH 2/3] b53: implement port mirroring feature
Alexandru Ardelean
ardeleanalex at gmail.com
Mon Feb 23 09:41:02 EST 2015
From: Alexandru Ardelean <ardeleanalex at 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 <ardeleanalex at 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 at lists.openwrt.org
https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel
More information about the openwrt-devel
mailing list