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