xref: /openbmc/linux/net/hsr/hsr_slave.c (revision 86aa961bb4619a68077ebeba21c52e9ba0eab43d)
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