diff mbox series

[net-next,1/5] net: dsa: tag_rtl4_a: Implement Realtek 4 byte A tag

Message ID 20200602205456.2392024-1-linus.walleij@linaro.org
State Deferred
Delegated to: David Miller
Headers show
Series [net-next,1/5] net: dsa: tag_rtl4_a: Implement Realtek 4 byte A tag | expand

Commit Message

Linus Walleij June 2, 2020, 8:54 p.m. UTC
This implements the known parts of the Realtek 4 byte
tag protocol version 0xA, as found in the RTL8366RB
DSA switch.

It is designated as protocol version 0xA as a
different Realtek 4 byte tag format with protocol
version 0x9 is known to exist in the Realtek RTL8306
chips.

The tag and switch chip lacks public documentation, so
the tag format has been reverse-engineered from
packet dumps. As only ingress traffic has been available
for analysis an egress tag has not been possible to
develop (even using educated guesses about bit fields)
so this is as far as it gets. It is not know if the
switch even supports egress tagging.

Using these ingress tags however, the switch
functionality is vastly improved and the packets find
their way into the destination port without any
tricky configuration. On the D-Link DIR-685 the
LAN ports now come up and respond to ping without
any command line configuration so this is a real
improvement for users.

Egress packets need to be restricted to the proper
target ports using VLAN, which the DSA switch driver
already sets up.

Cc: DENG Qingfang <dqfext@gmail.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
 include/net/dsa.h    |   2 +
 net/dsa/Kconfig      |   7 +++
 net/dsa/Makefile     |   1 +
 net/dsa/tag_rtl4_a.c | 134 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 144 insertions(+)
 create mode 100644 net/dsa/tag_rtl4_a.c

Comments

David Miller June 2, 2020, 10:48 p.m. UTC | #1
net-next is closed, sorry
Linus Walleij June 3, 2020, 8:32 a.m. UTC | #2
On Wed, Jun 3, 2020 at 12:48 AM David Miller <davem@davemloft.net> wrote:

> net-next is closed, sorry

No problem I just resend it later (I should have known since
it is merge window).

I'll watch this URL for opening hours:
http://vger.kernel.org/~davem/net-next.html

Yours,
Linus Walleij
Andrew Lunn June 3, 2020, 1:52 p.m. UTC | #3
> +static struct sk_buff *rtl4a_tag_rcv(struct sk_buff *skb,
> +				     struct net_device *dev,
> +				     struct packet_type *pt)
> +{
> +	u16 protport;
> +	__be16 *p;
> +	u16 etype;
> +	u8 flags;
> +	u8 *tag;
> +	u8 prot;
> +	u8 port;
> +
> +	if (unlikely(!pskb_may_pull(skb, RTL4_A_HDR_LEN)))
> +		return NULL;
> +
> +	/* The RTL4 header has its own custom Ethertype 0x8899 and that
> +	 * starts right at the beginning of the packet, after the src
> +	 * ethernet addr. Apparantly skb->data always points 2 bytes in,
> +	 * behind the Ethertype.
> +	 */
> +	tag = skb->data - 2;
> +	p = (__be16 *)tag;
> +	etype = ntohs(*p);
> +	if (etype != RTL4_A_ETHERTYPE) {
> +		/* Not custom, just pass through */
> +		netdev_dbg(dev, "non-realtek ethertype 0x%04x\n", etype);
> +		return skb;
> +	}
> +	p = (__be16 *)(tag + 2);
> +	protport = ntohs(*p);
> +	/* The 4 upper bits are the protocol */
> +	prot = (protport >> RTL4_A_PROTOCOL_SHIFT) & 0x0f;
> +	if (prot != RTL4_A_PROTOCOL_RTL8366RB) {
> +		netdev_err(dev, "unknown realtek protocol 0x%01x\n", prot);
> +		return NULL;
> +	}
> +	netdev_dbg(dev, "realtek protocol 0x%02x\n", prot);
> +	port = protport & 0xff;
> +	netdev_dbg(dev, "realtek port origin 0x%02x\n", port);
> +
> +	/* Remove RTL4 tag and recalculate checksum */
> +	skb_pull_rcsum(skb, RTL4_A_HDR_LEN);
> +
> +	/* Move ethernet DA and SA in front of the data */
> +	memmove(skb->data - ETH_HLEN,
> +		skb->data - ETH_HLEN - RTL4_A_HDR_LEN,
> +		2 * ETH_ALEN);
> +
> +	skb->dev = dsa_master_find_slave(dev, 0, port);
> +	if (!skb->dev) {
> +		netdev_dbg(dev, "could not find slave for port %d\n", port);
> +		return NULL;
> +	}
> +	netdev_dbg(skb->dev, "forwarded packet to slave port %d\n", port);
> +
> +	skb->offload_fwd_mark = 1;
> +
> +	return skb;
> +}

Hi Linus

Do you think you are passed basic debug/reverse engineering? There are
a lot of netdev_dbg() statements here. It would be nice to remove most
of them, if you think the code is stable.

Is there any hint in OpenRRPC that tags can be used in the other
direction? Where is spanning tree performed? In the switch, or by the
host? That is one example where the host needs to be able to
send/receive frames on specific ports.

       Andrew
Linus Walleij June 3, 2020, 10:01 p.m. UTC | #4
Hi Andrew!

On Wed, Jun 3, 2020 at 3:52 PM Andrew Lunn <andrew@lunn.ch> wrote:

> Do you think you are passed basic debug/reverse engineering? There are
> a lot of netdev_dbg() statements here. It would be nice to remove most
> of them, if you think the code is stable.

OK

> Is there any hint in OpenRRPC that tags can be used in the other
> direction?

It appears OpenRRPC is something totally different, but yeah
I looked in that code. I have documentation on three other tag
types: a 8 byte tag (not similar) another 4 byte tag (protocol 9)
and I looked at the RTL838x kernel code which has a third
tag format (trailer). None of it is very helpful.

> Where is spanning tree performed? In the switch, or by the
> host?

In the switch I think.
There is a register in the ASIC to set the spanning tree status
to disabled, blocking, learning or forwarding.

> That is one example where the host needs to be able to
> send/receive frames on specific ports.

No luck there I'm afraid :/

Yours,
Linus Walleij
Andrew Lunn June 4, 2020, 12:54 a.m. UTC | #5
> > Where is spanning tree performed? In the switch, or by the
> > host?
> 
> In the switch I think.
> There is a register in the ASIC to set the spanning tree status
> to disabled, blocking, learning or forwarding.

Hi Linus

Spanning tree needs two parts. You need to be able to change the ports
between disabled, blocking, learning, forwarding. And you need to be
able to send/receive frames.

If spanning tree is performed in the ASIC, i don't see why there would
be registers to control the port status. It would do it all itself,
and not export these controls.

So i would not give up on spanning tree as a way to reverse engineer
this.

    Andrew
Linus Walleij June 4, 2020, 7:52 a.m. UTC | #6
On Thu, Jun 4, 2020 at 2:54 AM Andrew Lunn <andrew@lunn.ch> wrote:

> If spanning tree is performed in the ASIC, i don't see why there would
> be registers to control the port status. It would do it all itself,
> and not export these controls.
>
> So i would not give up on spanning tree as a way to reverse engineer
> this.

Hm I guess I have to take out the textbooks and refresh my lacking
knowledge about spanning tree :)

What I have for "documentation" is the code drop inside DD Wrt:
https://svn.dd-wrt.com//browser/src/linux/universal/linux-3.2/drivers/net/ethernet/raeth/rb

The code is a bit messy and seems hacked up by Realtek, also
at one point apparently the ASIC was closely related to  RTL8368s
and then renamed to RTL8366RB...

The code accessing the ASIC is here (under the name RTL8368s):
https://svn.dd-wrt.com/browser/src/linux/universal/linux-3.2/drivers/net/ethernet/raeth/rb/rtl8368s_asicdrv.c

I'm hacking on it but a bit stuck :/

Yours,
Linus Walleij
Vladimir Oltean June 4, 2020, 11:22 a.m. UTC | #7
Hi Linus,

On Thu, 4 Jun 2020 at 12:17, Linus Walleij <linus.walleij@linaro.org> wrote:
>
> On Thu, Jun 4, 2020 at 2:54 AM Andrew Lunn <andrew@lunn.ch> wrote:
>
> > If spanning tree is performed in the ASIC, i don't see why there would
> > be registers to control the port status. It would do it all itself,
> > and not export these controls.
> >
> > So i would not give up on spanning tree as a way to reverse engineer
> > this.
>
> Hm I guess I have to take out the textbooks and refresh my lacking
> knowledge about spanning tree :)
>
> What I have for "documentation" is the code drop inside DD Wrt:
> https://svn.dd-wrt.com//browser/src/linux/universal/linux-3.2/drivers/net/ethernet/raeth/rb
>
> The code is a bit messy and seems hacked up by Realtek, also
> at one point apparently the ASIC was closely related to  RTL8368s
> and then renamed to RTL8366RB...
>
> The code accessing the ASIC is here (under the name RTL8368s):
> https://svn.dd-wrt.com/browser/src/linux/universal/linux-3.2/drivers/net/ethernet/raeth/rb/rtl8368s_asicdrv.c
>
> I'm hacking on it but a bit stuck :/
>
> Yours,
> Linus Walleij

In the code you pointed to, there is a potentially relevant comment:

1532//CPU tag: Realtek Ethertype==0x8899(2 bytes)+protocol==0x9(4
MSB)+priority(2 bits)+reserved(4 bits)+portmask(6 LSB)

https://svn.dd-wrt.com/browser/src/linux/universal/linux-3.2/drivers/net/ethernet/raeth/rb/rtl_multicast_snooping.c#L1527
https://svn.dd-wrt.com/browser/src/linux/universal/linux-3.2/drivers/net/ethernet/raeth/rb/rtl_multicast_snooping.c#L5224

This strongly indicates to me that the insertion tag is the same as
the extraction tag.
It is completely opaque to me why in patch "[net-next PATCH 2/5] net:
dsa: rtl8366rb: Support the CPU DSA tag" you are _disabling_ the
injection of these tags via RTL8368RB_CPU_INSTAG. I think it's natural
that the switch drops these packets when CPU tag insertion is
disabled.

Thanks,
-Vladimir
Linus Walleij June 17, 2020, 8:06 a.m. UTC | #8
On Thu, Jun 4, 2020 at 1:23 PM Vladimir Oltean <olteanv@gmail.com> wrote:

> In the code you pointed to, there is a potentially relevant comment:
>
> 1532//CPU tag: Realtek Ethertype==0x8899(2 bytes)+protocol==0x9(4
> MSB)+priority(2 bits)+reserved(4 bits)+portmask(6 LSB)
>
> https://svn.dd-wrt.com/browser/src/linux/universal/linux-3.2/drivers/net/ethernet/raeth/rb/rtl_multicast_snooping.c#L1527
> https://svn.dd-wrt.com/browser/src/linux/universal/linux-3.2/drivers/net/ethernet/raeth/rb/rtl_multicast_snooping.c#L5224
>
> This strongly indicates to me that the insertion tag is the same as
> the extraction tag.

This code is a problem because it is Realtek-development style.
This style seems to be that the hardware people write the drivers
using copy/paste from the previous ASIC and ship is as soon as
possible. Keep this in mind.

The above tag is using protocol 9 and is actually even documented
in a PDF I have for RTL8306. The problem is that the RTL8366RB
(I suspect also RTL8366S) uses protocol "a" (as in hex 10).
Which is of course necessarily different.

I have *really* tried to figure out how the bits in protocol a works
when transmissing from the CPU port to any switch port.

When nothing else worked, I just tried all bit combinations with
0xannp where a is protocol and p is port. I looped through all
values several times trying to get a response from ping.

So this is really how far I can get right now, even with brute
force.

> It is completely opaque to me why in patch "[net-next PATCH 2/5] net:
> dsa: rtl8366rb: Support the CPU DSA tag" you are _disabling_ the
> injection of these tags via RTL8368RB_CPU_INSTAG. I think it's natural
> that the switch drops these packets when CPU tag insertion is
> disabled.

This is another Realtek-ism where they managed to invert the
meaning of a bit.

Bit 15 in register 0x0061 (RTL8368RB_CPU_CTRL_REG) can
be set to 1 and then the special (custom) CPU tag 0x8899
protocol a will be DISABLED. This value Realtek calls
"RTL8368RB_CPU_INSTAG" which makes you think that
the tag will be inserted, it is named "instag" right? But that
is not how it works.

That bit needs to be set to 0 to insert the tag and 1 to disable
insertion of the tag.

For this reason the patch also renames this bit to
RTL8368RB_CPU_NO_TAG which is more to the point.

Yours,
Linus Walleij
Qingfang Deng June 18, 2020, 3:17 a.m. UTC | #9
Hi

On Wed, Jun 17, 2020 at 4:06 PM Linus Walleij <linus.walleij@linaro.org> wrote:
>
> On Thu, Jun 4, 2020 at 1:23 PM Vladimir Oltean <olteanv@gmail.com> wrote:
>
> > In the code you pointed to, there is a potentially relevant comment:
> >
> > 1532//CPU tag: Realtek Ethertype==0x8899(2 bytes)+protocol==0x9(4
> > MSB)+priority(2 bits)+reserved(4 bits)+portmask(6 LSB)
> >
> > https://svn.dd-wrt.com/browser/src/linux/universal/linux-3.2/drivers/net/ethernet/raeth/rb/rtl_multicast_snooping.c#L1527
> > https://svn.dd-wrt.com/browser/src/linux/universal/linux-3.2/drivers/net/ethernet/raeth/rb/rtl_multicast_snooping.c#L5224
> >
> > This strongly indicates to me that the insertion tag is the same as
> > the extraction tag.
>
> This code is a problem because it is Realtek-development style.
> This style seems to be that the hardware people write the drivers
> using copy/paste from the previous ASIC and ship is as soon as
> possible. Keep this in mind.
>
> The above tag is using protocol 9 and is actually even documented
> in a PDF I have for RTL8306. The problem is that the RTL8366RB
> (I suspect also RTL8366S) uses protocol "a" (as in hex 10).
> Which is of course necessarily different.
>
> I have *really* tried to figure out how the bits in protocol a works
> when transmissing from the CPU port to any switch port.
>
> When nothing else worked, I just tried all bit combinations with
> 0xannp where a is protocol and p is port. I looped through all
> values several times trying to get a response from ping.

Have you looped through the whole 32-bit field?

>
> So this is really how far I can get right now, even with brute
> force.
>
> > It is completely opaque to me why in patch "[net-next PATCH 2/5] net:
> > dsa: rtl8366rb: Support the CPU DSA tag" you are _disabling_ the
> > injection of these tags via RTL8368RB_CPU_INSTAG. I think it's natural
> > that the switch drops these packets when CPU tag insertion is
> > disabled.
>
> This is another Realtek-ism where they managed to invert the
> meaning of a bit.
>
> Bit 15 in register 0x0061 (RTL8368RB_CPU_CTRL_REG) can
> be set to 1 and then the special (custom) CPU tag 0x8899
> protocol a will be DISABLED. This value Realtek calls
> "RTL8368RB_CPU_INSTAG" which makes you think that
> the tag will be inserted, it is named "instag" right? But that
> is not how it works.
>
> That bit needs to be set to 0 to insert the tag and 1 to disable
> insertion of the tag.
>
> For this reason the patch also renames this bit to
> RTL8368RB_CPU_NO_TAG which is more to the point.
>
> Yours,
> Linus Walleij
diff mbox series

Patch

diff --git a/include/net/dsa.h b/include/net/dsa.h
index fb3f9222f2a1..7a6a922a509e 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -44,6 +44,7 @@  struct phylink_link_state;
 #define DSA_TAG_PROTO_KSZ8795_VALUE		14
 #define DSA_TAG_PROTO_OCELOT_VALUE		15
 #define DSA_TAG_PROTO_AR9331_VALUE		16
+#define DSA_TAG_PROTO_RTL4_A_VALUE		17
 
 enum dsa_tag_protocol {
 	DSA_TAG_PROTO_NONE		= DSA_TAG_PROTO_NONE_VALUE,
@@ -63,6 +64,7 @@  enum dsa_tag_protocol {
 	DSA_TAG_PROTO_KSZ8795		= DSA_TAG_PROTO_KSZ8795_VALUE,
 	DSA_TAG_PROTO_OCELOT		= DSA_TAG_PROTO_OCELOT_VALUE,
 	DSA_TAG_PROTO_AR9331		= DSA_TAG_PROTO_AR9331_VALUE,
+	DSA_TAG_PROTO_RTL4_A		= DSA_TAG_PROTO_RTL4_A_VALUE,
 };
 
 struct packet_type;
diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
index 92663dcb3aa2..0ec29e49683f 100644
--- a/net/dsa/Kconfig
+++ b/net/dsa/Kconfig
@@ -85,6 +85,13 @@  config NET_DSA_TAG_KSZ
 	  Say Y if you want to enable support for tagging frames for the
 	  Microchip 8795/9477/9893 families of switches.
 
+config NET_DSA_TAG_RTL4_A
+	tristate "Tag driver for Realtek 4 byte protocol A tags"
+	help
+	  Say Y or M if you want to enable support for tagging frames for the
+	  Realtek switches with 4 byte protocol A tags, sich as found in
+	  the Realtek RTL8366RB.
+
 config NET_DSA_TAG_OCELOT
 	tristate "Tag driver for Ocelot family of switches"
 	select PACKING
diff --git a/net/dsa/Makefile b/net/dsa/Makefile
index 108486cfdeef..4f47b2025ff5 100644
--- a/net/dsa/Makefile
+++ b/net/dsa/Makefile
@@ -11,6 +11,7 @@  obj-$(CONFIG_NET_DSA_TAG_DSA) += tag_dsa.o
 obj-$(CONFIG_NET_DSA_TAG_EDSA) += tag_edsa.o
 obj-$(CONFIG_NET_DSA_TAG_GSWIP) += tag_gswip.o
 obj-$(CONFIG_NET_DSA_TAG_KSZ) += tag_ksz.o
+obj-$(CONFIG_NET_DSA_TAG_RTL4_A) += tag_rtl4_a.o
 obj-$(CONFIG_NET_DSA_TAG_LAN9303) += tag_lan9303.o
 obj-$(CONFIG_NET_DSA_TAG_MTK) += tag_mtk.o
 obj-$(CONFIG_NET_DSA_TAG_OCELOT) += tag_ocelot.o
diff --git a/net/dsa/tag_rtl4_a.c b/net/dsa/tag_rtl4_a.c
new file mode 100644
index 000000000000..45f24e5cdde2
--- /dev/null
+++ b/net/dsa/tag_rtl4_a.c
@@ -0,0 +1,134 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Handler for Realtek 4 byte DSA switch tags
+ * Currently only supports protocol "A" found in RTL8366RB
+ * Copyright (c) 2020 Linus Walleij <linus.walleij@linaro.org>
+ *
+ * This "proprietary tag" header looks like so:
+ *
+ * -------------------------------------------------
+ * | MAC DA | MAC SA | 0x8899 | 2 bytes tag | Type |
+ * -------------------------------------------------
+ *
+ * The 2 bytes tag form a 16 bit big endian word. The exact
+ * meaning has been guess from packet dumps from ingress
+ * frames, as no working egress traffic has been available
+ * we do not know the format of the egress tags or if they
+ * are even supported.
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/bits.h>
+
+#include "dsa_priv.h"
+
+#define RTL4_A_HDR_LEN		4
+#define RTL4_A_ETHERTYPE	0x8899
+#define RTL4_A_PROTOCOL_SHIFT	12
+/*
+ * 0x1 = Realtek Remote Control protocol (RRCP)
+ * 0x2/0x3 seems to be used for loopback testing
+ * 0x9 = RTL8306 DSA protocol
+ * 0xa = RTL8366RB DSA protocol
+ */
+#define RTL4_A_PROTOCOL_RTL8366RB	0xa
+
+static struct sk_buff *rtl4a_tag_xmit(struct sk_buff *skb,
+				      struct net_device *dev)
+{
+	/*
+	 * Just let it pass thru, we don't know if it is possible
+	 * to tag a frame with the 0x8899 ethertype and direct it
+	 * to a specific port, all attempts at reverse-engineering have
+	 * ended up with the frames getting dropped.
+	 *
+	 * The VLAN set-up needs to restrict the frames to the right port.
+	 *
+	 * If you have documentation on the tagging format for RTL8366RB
+	 * (tag type A) then please contribute.
+	 */
+	return skb;
+}
+
+static struct sk_buff *rtl4a_tag_rcv(struct sk_buff *skb,
+				     struct net_device *dev,
+				     struct packet_type *pt)
+{
+	u16 protport;
+	__be16 *p;
+	u16 etype;
+	u8 flags;
+	u8 *tag;
+	u8 prot;
+	u8 port;
+
+	if (unlikely(!pskb_may_pull(skb, RTL4_A_HDR_LEN)))
+		return NULL;
+
+	/* The RTL4 header has its own custom Ethertype 0x8899 and that
+	 * starts right at the beginning of the packet, after the src
+	 * ethernet addr. Apparantly skb->data always points 2 bytes in,
+	 * behind the Ethertype.
+	 */
+	tag = skb->data - 2;
+	p = (__be16 *)tag;
+	etype = ntohs(*p);
+	if (etype != RTL4_A_ETHERTYPE) {
+		/* Not custom, just pass through */
+		netdev_dbg(dev, "non-realtek ethertype 0x%04x\n", etype);
+		return skb;
+	}
+	p = (__be16 *)(tag + 2);
+	protport = ntohs(*p);
+	/* The 4 upper bits are the protocol */
+	prot = (protport >> RTL4_A_PROTOCOL_SHIFT) & 0x0f;
+	if (prot != RTL4_A_PROTOCOL_RTL8366RB) {
+		netdev_err(dev, "unknown realtek protocol 0x%01x\n", prot);
+		return NULL;
+	}
+	netdev_dbg(dev, "realtek protocol 0x%02x\n", prot);
+	port = protport & 0xff;
+	netdev_dbg(dev, "realtek port origin 0x%02x\n", port);
+
+	/* Remove RTL4 tag and recalculate checksum */
+	skb_pull_rcsum(skb, RTL4_A_HDR_LEN);
+
+	/* Move ethernet DA and SA in front of the data */
+	memmove(skb->data - ETH_HLEN,
+		skb->data - ETH_HLEN - RTL4_A_HDR_LEN,
+		2 * ETH_ALEN);
+
+	skb->dev = dsa_master_find_slave(dev, 0, port);
+	if (!skb->dev) {
+		netdev_dbg(dev, "could not find slave for port %d\n", port);
+		return NULL;
+	}
+	netdev_dbg(skb->dev, "forwarded packet to slave port %d\n", port);
+
+	skb->offload_fwd_mark = 1;
+
+	return skb;
+}
+
+static int rtl4a_tag_flow_dissect(const struct sk_buff *skb, __be16 *proto,
+				  int *offset)
+{
+	*offset = RTL4_A_HDR_LEN;
+	/* Skip past the tag and fetch the encapsulated Ethertype */
+	*proto = ((__be16 *)skb->data)[1];
+
+	return 0;
+}
+
+static const struct dsa_device_ops rtl4a_netdev_ops = {
+	.name	= "rtl4a",
+	.proto	= DSA_TAG_PROTO_RTL4_A,
+	.xmit	= rtl4a_tag_xmit,
+	.rcv	= rtl4a_tag_rcv,
+	.flow_dissect = rtl4a_tag_flow_dissect,
+	.overhead = RTL4_A_HDR_LEN,
+};
+module_dsa_tag_driver(rtl4a_netdev_ops);
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_RTL4_A);