From patchwork Mon Feb 23 15:55:24 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexandru Ardelean X-Patchwork-Id: 442588 X-Patchwork-Delegate: jogo@openwrt.org Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from arrakis.dune.hu (arrakis.dune.hu [78.24.191.176]) (using TLSv1.1 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4E38C1400EA for ; Tue, 24 Feb 2015 02:55:52 +1100 (AEDT) Received: from arrakis.dune.hu (localhost [127.0.0.1]) by arrakis.dune.hu (Postfix) with ESMTP id D7DE428BDA1; Mon, 23 Feb 2015 16:55:32 +0100 (CET) X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on arrakis.dune.hu X-Spam-Level: X-Spam-Status: No, score=-1.5 required=5.0 tests=BAYES_00,FREEMAIL_FROM, T_DKIM_INVALID autolearn=unavailable version=3.3.2 Received: from arrakis.dune.hu (localhost [127.0.0.1]) by arrakis.dune.hu (Postfix) with ESMTP id 6141528BD57 for ; Mon, 23 Feb 2015 16:55:27 +0100 (CET) X-policyd-weight: using cached result; rate: -8.5 Received: from mail-we0-f173.google.com (mail-we0-f173.google.com [74.125.82.173]) by arrakis.dune.hu (Postfix) with ESMTPS for ; Mon, 23 Feb 2015 16:55:27 +0100 (CET) Received: by wesw55 with SMTP id w55so19383253wes.5 for ; Mon, 23 Feb 2015 07:55:34 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id; bh=E1ljVoIgCqHb1ao09XF/oOr+SCe2ZBs+yqc6fvxKC/k=; b=o1H3ACiNroKotvdaOTlG9sSZWObhfxGjJSx9hGgYCJREUYHiqikRUEIzCqUNYGIIud SMb37Y/201JBiGObbbSRjsfsf8r0llulUp1K0RmQPMhBV3ymqsWVO8if1WGfcY86kItV 8IfdLRBFHc0/BnrY+Ixqlhqhc/LhjcfVE41e6Ikw0t4krfcdkI8YK9Y9gKytHN2llt5e SNVnACdlFqv8yxZyQaaCjkhKlBVzvUygPS3rjyobM44UOcoPwtZ8BUhGub4kDoAkHljH wlzRsVHs4tJ3WMRWVKDup6Baa4qt+Y1j6dm+RkFEvcpFuz6MY9KVak1EIQA0c2tP6RSq DtxA== X-Received: by 10.181.8.75 with SMTP id di11mr17025564wid.26.1424706934514; Mon, 23 Feb 2015 07:55:34 -0800 (PST) Received: from orion.local ([194.105.29.179]) by mx.google.com with ESMTPSA id hg7sm56129223wjb.44.2015.02.23.07.55.33 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 23 Feb 2015 07:55:33 -0800 (PST) From: Alexandru Ardelean X-Google-Original-From: Alexandru Ardelean To: openwrt-devel@lists.openwrt.org Date: Mon, 23 Feb 2015 17:55:24 +0200 Message-Id: <1424706924-32646-1-git-send-email-aa@ocedo.com> X-Mailer: git-send-email 2.1.2 Subject: [OpenWrt-Devel] [PATCH 2/3][RESEND] b53: implement port mirroring feature X-BeenThere: openwrt-devel@lists.openwrt.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: OpenWrt Development List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: openwrt-devel-bounces@lists.openwrt.org Sender: "openwrt-devel" From: Alexandru Ardelean 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 --- .../generic/files/drivers/net/phy/b53/Kconfig | 13 + .../generic/files/drivers/net/phy/b53/b53_common.c | 294 +++++++++++++++++++++ .../generic/files/drivers/net/phy/b53/b53_priv.h | 25 ++ .../generic/files/drivers/net/phy/b53/b53_regs.h | 24 ++ 4 files changed, 356 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..4abc8c3 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,222 @@ 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) +{ + if (dev->port_capture.in_filter) + devm_kfree(dev->dev, dev->port_capture.in_filter); + if (dev->port_capture.out_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 +1061,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 +1224,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 #include #include +#include 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