18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/* Copyright 2011-2014 Autronica Fire and Security AS
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Author(s):
58c2ecf20Sopenharmony_ci *	2011-2014 Arvid Brodin, arvid.brodin@alten.se
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Frame handler other utility functions for HSR and PRP.
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include "hsr_slave.h"
118c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
128c2ecf20Sopenharmony_ci#include <linux/if_arp.h>
138c2ecf20Sopenharmony_ci#include <linux/if_vlan.h>
148c2ecf20Sopenharmony_ci#include "hsr_main.h"
158c2ecf20Sopenharmony_ci#include "hsr_device.h"
168c2ecf20Sopenharmony_ci#include "hsr_forward.h"
178c2ecf20Sopenharmony_ci#include "hsr_framereg.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cibool hsr_invalid_dan_ingress_frame(__be16 protocol)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	return (protocol != htons(ETH_P_PRP) && protocol != htons(ETH_P_HSR));
228c2ecf20Sopenharmony_ci}
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb)
258c2ecf20Sopenharmony_ci{
268c2ecf20Sopenharmony_ci	struct sk_buff *skb = *pskb;
278c2ecf20Sopenharmony_ci	struct hsr_port *port;
288c2ecf20Sopenharmony_ci	struct hsr_priv *hsr;
298c2ecf20Sopenharmony_ci	__be16 protocol;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	/* Packets from dev_loopback_xmit() do not have L2 header, bail out */
328c2ecf20Sopenharmony_ci	if (unlikely(skb->pkt_type == PACKET_LOOPBACK))
338c2ecf20Sopenharmony_ci		return RX_HANDLER_PASS;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	if (!skb_mac_header_was_set(skb)) {
368c2ecf20Sopenharmony_ci		WARN_ONCE(1, "%s: skb invalid", __func__);
378c2ecf20Sopenharmony_ci		return RX_HANDLER_PASS;
388c2ecf20Sopenharmony_ci	}
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	port = hsr_port_get_rcu(skb->dev);
418c2ecf20Sopenharmony_ci	if (!port)
428c2ecf20Sopenharmony_ci		goto finish_pass;
438c2ecf20Sopenharmony_ci	hsr = port->hsr;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	if (hsr_addr_is_self(port->hsr, eth_hdr(skb)->h_source)) {
468c2ecf20Sopenharmony_ci		/* Directly kill frames sent by ourselves */
478c2ecf20Sopenharmony_ci		kfree_skb(skb);
488c2ecf20Sopenharmony_ci		goto finish_consume;
498c2ecf20Sopenharmony_ci	}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	/* For HSR, only tagged frames are expected, but for PRP
528c2ecf20Sopenharmony_ci	 * there could be non tagged frames as well from Single
538c2ecf20Sopenharmony_ci	 * attached nodes (SANs).
548c2ecf20Sopenharmony_ci	 */
558c2ecf20Sopenharmony_ci	protocol = eth_hdr(skb)->h_proto;
568c2ecf20Sopenharmony_ci	if (hsr->proto_ops->invalid_dan_ingress_frame &&
578c2ecf20Sopenharmony_ci	    hsr->proto_ops->invalid_dan_ingress_frame(protocol))
588c2ecf20Sopenharmony_ci		goto finish_pass;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	skb_push(skb, ETH_HLEN);
618c2ecf20Sopenharmony_ci	skb_reset_mac_header(skb);
628c2ecf20Sopenharmony_ci	if ((!hsr->prot_version && protocol == htons(ETH_P_PRP)) ||
638c2ecf20Sopenharmony_ci	    protocol == htons(ETH_P_HSR))
648c2ecf20Sopenharmony_ci		skb_set_network_header(skb, ETH_HLEN + HSR_HLEN);
658c2ecf20Sopenharmony_ci	skb_reset_mac_len(skb);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	hsr_forward_skb(skb, port);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cifinish_consume:
708c2ecf20Sopenharmony_ci	return RX_HANDLER_CONSUMED;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cifinish_pass:
738c2ecf20Sopenharmony_ci	return RX_HANDLER_PASS;
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cibool hsr_port_exists(const struct net_device *dev)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	return rcu_access_pointer(dev->rx_handler) == hsr_handle_frame;
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic int hsr_check_dev_ok(struct net_device *dev,
828c2ecf20Sopenharmony_ci			    struct netlink_ext_ack *extack)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	/* Don't allow HSR on non-ethernet like devices */
858c2ecf20Sopenharmony_ci	if ((dev->flags & IFF_LOOPBACK) || dev->type != ARPHRD_ETHER ||
868c2ecf20Sopenharmony_ci	    dev->addr_len != ETH_ALEN) {
878c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Cannot use loopback or non-ethernet device as HSR slave.");
888c2ecf20Sopenharmony_ci		return -EINVAL;
898c2ecf20Sopenharmony_ci	}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	/* Don't allow enslaving hsr devices */
928c2ecf20Sopenharmony_ci	if (is_hsr_master(dev)) {
938c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack,
948c2ecf20Sopenharmony_ci				   "Cannot create trees of HSR devices.");
958c2ecf20Sopenharmony_ci		return -EINVAL;
968c2ecf20Sopenharmony_ci	}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	if (hsr_port_exists(dev)) {
998c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack,
1008c2ecf20Sopenharmony_ci				   "This device is already a HSR slave.");
1018c2ecf20Sopenharmony_ci		return -EINVAL;
1028c2ecf20Sopenharmony_ci	}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	if (is_vlan_dev(dev)) {
1058c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "HSR on top of VLAN is not yet supported in this driver.");
1068c2ecf20Sopenharmony_ci		return -EINVAL;
1078c2ecf20Sopenharmony_ci	}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	if (dev->priv_flags & IFF_DONT_BRIDGE) {
1108c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack,
1118c2ecf20Sopenharmony_ci				   "This device does not support bridging.");
1128c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	/* HSR over bonded devices has not been tested, but I'm not sure it
1168c2ecf20Sopenharmony_ci	 * won't work...
1178c2ecf20Sopenharmony_ci	 */
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	return 0;
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci/* Setup device to be added to the HSR bridge. */
1238c2ecf20Sopenharmony_cistatic int hsr_portdev_setup(struct hsr_priv *hsr, struct net_device *dev,
1248c2ecf20Sopenharmony_ci			     struct hsr_port *port,
1258c2ecf20Sopenharmony_ci			     struct netlink_ext_ack *extack)
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	struct net_device *hsr_dev;
1298c2ecf20Sopenharmony_ci	struct hsr_port *master;
1308c2ecf20Sopenharmony_ci	int res;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	res = dev_set_promiscuity(dev, 1);
1338c2ecf20Sopenharmony_ci	if (res)
1348c2ecf20Sopenharmony_ci		return res;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	master = hsr_port_get_hsr(hsr, HSR_PT_MASTER);
1378c2ecf20Sopenharmony_ci	hsr_dev = master->dev;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	res = netdev_upper_dev_link(dev, hsr_dev, extack);
1408c2ecf20Sopenharmony_ci	if (res)
1418c2ecf20Sopenharmony_ci		goto fail_upper_dev_link;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	res = netdev_rx_handler_register(dev, hsr_handle_frame, port);
1448c2ecf20Sopenharmony_ci	if (res)
1458c2ecf20Sopenharmony_ci		goto fail_rx_handler;
1468c2ecf20Sopenharmony_ci	dev_disable_lro(dev);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	return 0;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cifail_rx_handler:
1518c2ecf20Sopenharmony_ci	netdev_upper_dev_unlink(dev, hsr_dev);
1528c2ecf20Sopenharmony_cifail_upper_dev_link:
1538c2ecf20Sopenharmony_ci	dev_set_promiscuity(dev, -1);
1548c2ecf20Sopenharmony_ci	return res;
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ciint hsr_add_port(struct hsr_priv *hsr, struct net_device *dev,
1588c2ecf20Sopenharmony_ci		 enum hsr_port_type type, struct netlink_ext_ack *extack)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	struct hsr_port *port, *master;
1618c2ecf20Sopenharmony_ci	int res;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	if (type != HSR_PT_MASTER) {
1648c2ecf20Sopenharmony_ci		res = hsr_check_dev_ok(dev, extack);
1658c2ecf20Sopenharmony_ci		if (res)
1668c2ecf20Sopenharmony_ci			return res;
1678c2ecf20Sopenharmony_ci	}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	port = hsr_port_get_hsr(hsr, type);
1708c2ecf20Sopenharmony_ci	if (port)
1718c2ecf20Sopenharmony_ci		return -EBUSY;	/* This port already exists */
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	port = kzalloc(sizeof(*port), GFP_KERNEL);
1748c2ecf20Sopenharmony_ci	if (!port)
1758c2ecf20Sopenharmony_ci		return -ENOMEM;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	port->hsr = hsr;
1788c2ecf20Sopenharmony_ci	port->dev = dev;
1798c2ecf20Sopenharmony_ci	port->type = type;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	if (type != HSR_PT_MASTER) {
1828c2ecf20Sopenharmony_ci		res = hsr_portdev_setup(hsr, dev, port, extack);
1838c2ecf20Sopenharmony_ci		if (res)
1848c2ecf20Sopenharmony_ci			goto fail_dev_setup;
1858c2ecf20Sopenharmony_ci	}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	list_add_tail_rcu(&port->port_list, &hsr->ports);
1888c2ecf20Sopenharmony_ci	synchronize_rcu();
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	master = hsr_port_get_hsr(hsr, HSR_PT_MASTER);
1918c2ecf20Sopenharmony_ci	netdev_update_features(master->dev);
1928c2ecf20Sopenharmony_ci	dev_set_mtu(master->dev, hsr_get_max_mtu(hsr));
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	return 0;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_cifail_dev_setup:
1978c2ecf20Sopenharmony_ci	kfree(port);
1988c2ecf20Sopenharmony_ci	return res;
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_civoid hsr_del_port(struct hsr_port *port)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	struct hsr_priv *hsr;
2048c2ecf20Sopenharmony_ci	struct hsr_port *master;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	hsr = port->hsr;
2078c2ecf20Sopenharmony_ci	master = hsr_port_get_hsr(hsr, HSR_PT_MASTER);
2088c2ecf20Sopenharmony_ci	list_del_rcu(&port->port_list);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	if (port != master) {
2118c2ecf20Sopenharmony_ci		netdev_update_features(master->dev);
2128c2ecf20Sopenharmony_ci		dev_set_mtu(master->dev, hsr_get_max_mtu(hsr));
2138c2ecf20Sopenharmony_ci		netdev_rx_handler_unregister(port->dev);
2148c2ecf20Sopenharmony_ci		dev_set_promiscuity(port->dev, -1);
2158c2ecf20Sopenharmony_ci		netdev_upper_dev_unlink(port->dev, master->dev);
2168c2ecf20Sopenharmony_ci	}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	synchronize_rcu();
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	kfree(port);
2218c2ecf20Sopenharmony_ci}
222