10e7623bdSMurali Karicheri // SPDX-License-Identifier: GPL-2.0
281ba6afdSArvid Brodin /* Copyright 2011-2014 Autronica Fire and Security AS
381ba6afdSArvid Brodin *
481ba6afdSArvid Brodin * Author(s):
581ba6afdSArvid Brodin * 2011-2014 Arvid Brodin, arvid.brodin@alten.se
68f4c0e01SMurali Karicheri *
78f4c0e01SMurali Karicheri * Frame handler other utility functions for HSR and PRP.
881ba6afdSArvid Brodin */
981ba6afdSArvid Brodin
1081ba6afdSArvid Brodin #include "hsr_slave.h"
1181ba6afdSArvid Brodin #include <linux/etherdevice.h>
1251f3c605SArvid Brodin #include <linux/if_arp.h>
13d0d7b10bSParav Pandit #include <linux/if_vlan.h>
1481ba6afdSArvid Brodin #include "hsr_main.h"
1551f3c605SArvid Brodin #include "hsr_device.h"
16f266a683SArvid Brodin #include "hsr_forward.h"
1781ba6afdSArvid Brodin #include "hsr_framereg.h"
1881ba6afdSArvid Brodin
hsr_invalid_dan_ingress_frame(__be16 protocol)19451d8123SMurali Karicheri bool hsr_invalid_dan_ingress_frame(__be16 protocol)
20451d8123SMurali Karicheri {
21451d8123SMurali Karicheri return (protocol != htons(ETH_P_PRP) && protocol != htons(ETH_P_HSR));
22451d8123SMurali Karicheri }
23451d8123SMurali Karicheri
hsr_handle_frame(struct sk_buff ** pskb)24f266a683SArvid Brodin static rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb)
25f266a683SArvid Brodin {
26f266a683SArvid Brodin struct sk_buff *skb = *pskb;
27f266a683SArvid Brodin struct hsr_port *port;
28451d8123SMurali Karicheri struct hsr_priv *hsr;
29f5dda315SMurali Karicheri __be16 protocol;
30f266a683SArvid Brodin
31451d8123SMurali Karicheri /* Packets from dev_loopback_xmit() do not have L2 header, bail out */
32451d8123SMurali Karicheri if (unlikely(skb->pkt_type == PACKET_LOOPBACK))
33451d8123SMurali Karicheri return RX_HANDLER_PASS;
34451d8123SMurali Karicheri
35f266a683SArvid Brodin if (!skb_mac_header_was_set(skb)) {
36f266a683SArvid Brodin WARN_ONCE(1, "%s: skb invalid", __func__);
37f266a683SArvid Brodin return RX_HANDLER_PASS;
38f266a683SArvid Brodin }
39f266a683SArvid Brodin
40f266a683SArvid Brodin port = hsr_port_get_rcu(skb->dev);
412b5b8251SEric Dumazet if (!port)
422b5b8251SEric Dumazet goto finish_pass;
43451d8123SMurali Karicheri hsr = port->hsr;
44f266a683SArvid Brodin
45f266a683SArvid Brodin if (hsr_addr_is_self(port->hsr, eth_hdr(skb)->h_source)) {
46f266a683SArvid Brodin /* Directly kill frames sent by ourselves */
47f266a683SArvid Brodin kfree_skb(skb);
48f266a683SArvid Brodin goto finish_consume;
49f266a683SArvid Brodin }
50f266a683SArvid Brodin
51dcf0cd1cSGeorge McCollister /* For HSR, only tagged frames are expected (unless the device offloads
52dcf0cd1cSGeorge McCollister * HSR tag removal), but for PRP there could be non tagged frames as
53dcf0cd1cSGeorge McCollister * well from Single attached nodes (SANs).
54451d8123SMurali Karicheri */
55ee1c2797SPeter Heise protocol = eth_hdr(skb)->h_proto;
56dcf0cd1cSGeorge McCollister
57dcf0cd1cSGeorge McCollister if (!(port->dev->features & NETIF_F_HW_HSR_TAG_RM) &&
58dcf0cd1cSGeorge McCollister hsr->proto_ops->invalid_dan_ingress_frame &&
59451d8123SMurali Karicheri hsr->proto_ops->invalid_dan_ingress_frame(protocol))
60f266a683SArvid Brodin goto finish_pass;
61f266a683SArvid Brodin
62f266a683SArvid Brodin skb_push(skb, ETH_HLEN);
6348b491a5SGeorge McCollister skb_reset_mac_header(skb);
6448b491a5SGeorge McCollister if ((!hsr->prot_version && protocol == htons(ETH_P_PRP)) ||
6548b491a5SGeorge McCollister protocol == htons(ETH_P_HSR))
6648b491a5SGeorge McCollister skb_set_network_header(skb, ETH_HLEN + HSR_HLEN);
6748b491a5SGeorge McCollister skb_reset_mac_len(skb);
68451d8123SMurali Karicheri
69f266a683SArvid Brodin hsr_forward_skb(skb, port);
70f266a683SArvid Brodin
71f266a683SArvid Brodin finish_consume:
72f266a683SArvid Brodin return RX_HANDLER_CONSUMED;
73f266a683SArvid Brodin
74f266a683SArvid Brodin finish_pass:
75f266a683SArvid Brodin return RX_HANDLER_PASS;
76f266a683SArvid Brodin }
77f266a683SArvid Brodin
hsr_port_exists(const struct net_device * dev)78f266a683SArvid Brodin bool hsr_port_exists(const struct net_device *dev)
79f266a683SArvid Brodin {
80f266a683SArvid Brodin return rcu_access_pointer(dev->rx_handler) == hsr_handle_frame;
81f266a683SArvid Brodin }
82f266a683SArvid Brodin
hsr_check_dev_ok(struct net_device * dev,struct netlink_ext_ack * extack)8313eeb5feSTaehee Yoo static int hsr_check_dev_ok(struct net_device *dev,
8413eeb5feSTaehee Yoo struct netlink_ext_ack *extack)
8551f3c605SArvid Brodin {
8651f3c605SArvid Brodin /* Don't allow HSR on non-ethernet like devices */
875670342cSMurali Karicheri if ((dev->flags & IFF_LOOPBACK) || dev->type != ARPHRD_ETHER ||
885670342cSMurali Karicheri dev->addr_len != ETH_ALEN) {
8913eeb5feSTaehee Yoo NL_SET_ERR_MSG_MOD(extack, "Cannot use loopback or non-ethernet device as HSR slave.");
9051f3c605SArvid Brodin return -EINVAL;
9151f3c605SArvid Brodin }
9251f3c605SArvid Brodin
9351f3c605SArvid Brodin /* Don't allow enslaving hsr devices */
9451f3c605SArvid Brodin if (is_hsr_master(dev)) {
9513eeb5feSTaehee Yoo NL_SET_ERR_MSG_MOD(extack,
9613eeb5feSTaehee Yoo "Cannot create trees of HSR devices.");
9751f3c605SArvid Brodin return -EINVAL;
9851f3c605SArvid Brodin }
9951f3c605SArvid Brodin
100c5a75911SArvid Brodin if (hsr_port_exists(dev)) {
10113eeb5feSTaehee Yoo NL_SET_ERR_MSG_MOD(extack,
10213eeb5feSTaehee Yoo "This device is already a HSR slave.");
10351f3c605SArvid Brodin return -EINVAL;
10451f3c605SArvid Brodin }
10551f3c605SArvid Brodin
106d0d7b10bSParav Pandit if (is_vlan_dev(dev)) {
10713eeb5feSTaehee Yoo NL_SET_ERR_MSG_MOD(extack, "HSR on top of VLAN is not yet supported in this driver.");
10851f3c605SArvid Brodin return -EINVAL;
10951f3c605SArvid Brodin }
11051f3c605SArvid Brodin
111f266a683SArvid Brodin if (dev->priv_flags & IFF_DONT_BRIDGE) {
11213eeb5feSTaehee Yoo NL_SET_ERR_MSG_MOD(extack,
11313eeb5feSTaehee Yoo "This device does not support bridging.");
114f266a683SArvid Brodin return -EOPNOTSUPP;
115f266a683SArvid Brodin }
116f266a683SArvid Brodin
11751f3c605SArvid Brodin /* HSR over bonded devices has not been tested, but I'm not sure it
11851f3c605SArvid Brodin * won't work...
11951f3c605SArvid Brodin */
12051f3c605SArvid Brodin
12151f3c605SArvid Brodin return 0;
12251f3c605SArvid Brodin }
12351f3c605SArvid Brodin
124c5a75911SArvid Brodin /* Setup device to be added to the HSR bridge. */
hsr_portdev_setup(struct hsr_priv * hsr,struct net_device * dev,struct hsr_port * port,struct netlink_ext_ack * extack)125e0a4b997STaehee Yoo static int hsr_portdev_setup(struct hsr_priv *hsr, struct net_device *dev,
126e0a4b997STaehee Yoo struct hsr_port *port,
127e0a4b997STaehee Yoo struct netlink_ext_ack *extack)
128e0a4b997STaehee Yoo
12951f3c605SArvid Brodin {
130e0a4b997STaehee Yoo struct net_device *hsr_dev;
131e0a4b997STaehee Yoo struct hsr_port *master;
13251f3c605SArvid Brodin int res;
13351f3c605SArvid Brodin
134e748d0fdSRavi Gunasekaran /* Don't use promiscuous mode for offload since L2 frame forward
135e748d0fdSRavi Gunasekaran * happens at the offloaded hardware.
136e748d0fdSRavi Gunasekaran */
137e748d0fdSRavi Gunasekaran if (!port->hsr->fwd_offloaded) {
13851f3c605SArvid Brodin res = dev_set_promiscuity(dev, 1);
13951f3c605SArvid Brodin if (res)
14056dc0a0eSTaehee Yoo return res;
141e748d0fdSRavi Gunasekaran }
14251f3c605SArvid Brodin
143e0a4b997STaehee Yoo master = hsr_port_get_hsr(hsr, HSR_PT_MASTER);
144e0a4b997STaehee Yoo hsr_dev = master->dev;
145e0a4b997STaehee Yoo
146e0a4b997STaehee Yoo res = netdev_upper_dev_link(dev, hsr_dev, extack);
147e0a4b997STaehee Yoo if (res)
148e0a4b997STaehee Yoo goto fail_upper_dev_link;
14951f3c605SArvid Brodin
150f266a683SArvid Brodin res = netdev_rx_handler_register(dev, hsr_handle_frame, port);
151f266a683SArvid Brodin if (res)
152f266a683SArvid Brodin goto fail_rx_handler;
153f266a683SArvid Brodin dev_disable_lro(dev);
154f266a683SArvid Brodin
15551f3c605SArvid Brodin return 0;
15651f3c605SArvid Brodin
15751f3c605SArvid Brodin fail_rx_handler:
158e0a4b997STaehee Yoo netdev_upper_dev_unlink(dev, hsr_dev);
159e0a4b997STaehee Yoo fail_upper_dev_link:
160e748d0fdSRavi Gunasekaran if (!port->hsr->fwd_offloaded)
16151f3c605SArvid Brodin dev_set_promiscuity(dev, -1);
162e748d0fdSRavi Gunasekaran
16351f3c605SArvid Brodin return res;
16451f3c605SArvid Brodin }
16551f3c605SArvid Brodin
hsr_add_port(struct hsr_priv * hsr,struct net_device * dev,enum hsr_port_type type,struct netlink_ext_ack * extack)166c5a75911SArvid Brodin int hsr_add_port(struct hsr_priv *hsr, struct net_device *dev,
16713eeb5feSTaehee Yoo enum hsr_port_type type, struct netlink_ext_ack *extack)
16851f3c605SArvid Brodin {
169c5a75911SArvid Brodin struct hsr_port *port, *master;
170c5a75911SArvid Brodin int res;
17151f3c605SArvid Brodin
172c5a75911SArvid Brodin if (type != HSR_PT_MASTER) {
17313eeb5feSTaehee Yoo res = hsr_check_dev_ok(dev, extack);
174c5a75911SArvid Brodin if (res)
175c5a75911SArvid Brodin return res;
17651f3c605SArvid Brodin }
17751f3c605SArvid Brodin
178c5a75911SArvid Brodin port = hsr_port_get_hsr(hsr, type);
17905ca6e64SMurali Karicheri if (port)
180c5a75911SArvid Brodin return -EBUSY; /* This port already exists */
181c5a75911SArvid Brodin
182c5a75911SArvid Brodin port = kzalloc(sizeof(*port), GFP_KERNEL);
18305ca6e64SMurali Karicheri if (!port)
184c5a75911SArvid Brodin return -ENOMEM;
185c5a75911SArvid Brodin
1863a303cfdSTaehee Yoo port->hsr = hsr;
1873a303cfdSTaehee Yoo port->dev = dev;
1883a303cfdSTaehee Yoo port->type = type;
1893a303cfdSTaehee Yoo
190c5a75911SArvid Brodin if (type != HSR_PT_MASTER) {
191e0a4b997STaehee Yoo res = hsr_portdev_setup(hsr, dev, port, extack);
192c5a75911SArvid Brodin if (res)
193c5a75911SArvid Brodin goto fail_dev_setup;
194c5a75911SArvid Brodin }
195c5a75911SArvid Brodin
196c5a75911SArvid Brodin list_add_tail_rcu(&port->port_list, &hsr->ports);
19751f3c605SArvid Brodin synchronize_rcu();
198c5a75911SArvid Brodin
199c5a75911SArvid Brodin master = hsr_port_get_hsr(hsr, HSR_PT_MASTER);
2001cc1eb52SArvid Brodin netdev_update_features(master->dev);
201c5a75911SArvid Brodin dev_set_mtu(master->dev, hsr_get_max_mtu(hsr));
202c5a75911SArvid Brodin
203c5a75911SArvid Brodin return 0;
204c5a75911SArvid Brodin
205c5a75911SArvid Brodin fail_dev_setup:
206c5a75911SArvid Brodin kfree(port);
207c5a75911SArvid Brodin return res;
208c5a75911SArvid Brodin }
209c5a75911SArvid Brodin
hsr_del_port(struct hsr_port * port)210c5a75911SArvid Brodin void hsr_del_port(struct hsr_port *port)
211c5a75911SArvid Brodin {
212c5a75911SArvid Brodin struct hsr_priv *hsr;
213c5a75911SArvid Brodin struct hsr_port *master;
214c5a75911SArvid Brodin
215c5a75911SArvid Brodin hsr = port->hsr;
216c5a75911SArvid Brodin master = hsr_port_get_hsr(hsr, HSR_PT_MASTER);
217c5a75911SArvid Brodin list_del_rcu(&port->port_list);
218c5a75911SArvid Brodin
219c5a75911SArvid Brodin if (port != master) {
2201cc1eb52SArvid Brodin netdev_update_features(master->dev);
221c5a75911SArvid Brodin dev_set_mtu(master->dev, hsr_get_max_mtu(hsr));
222c5a75911SArvid Brodin netdev_rx_handler_unregister(port->dev);
223*984c3d96SRavi Gunasekaran if (!port->hsr->fwd_offloaded)
224c5a75911SArvid Brodin dev_set_promiscuity(port->dev, -1);
225e0a4b997STaehee Yoo netdev_upper_dev_unlink(port->dev, master->dev);
226c5a75911SArvid Brodin }
227c5a75911SArvid Brodin
228c5a75911SArvid Brodin synchronize_rcu();
22956b08fdcSArvid Brodin
230619afef0SCong Wang kfree(port);
23151f3c605SArvid Brodin }
232