| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
|---|
| 1 | 2 | /* Copyright 2011-2014 Autronica Fire and Security AS |
|---|
| 2 | | - * |
|---|
| 3 | | - * This program is free software; you can redistribute it and/or modify it |
|---|
| 4 | | - * under the terms of the GNU General Public License as published by the Free |
|---|
| 5 | | - * Software Foundation; either version 2 of the License, or (at your option) |
|---|
| 6 | | - * any later version. |
|---|
| 7 | 3 | * |
|---|
| 8 | 4 | * Author(s): |
|---|
| 9 | 5 | * 2011-2014 Arvid Brodin, arvid.brodin@alten.se |
|---|
| 6 | + * |
|---|
| 7 | + * Frame handler other utility functions for HSR and PRP. |
|---|
| 10 | 8 | */ |
|---|
| 11 | 9 | |
|---|
| 12 | 10 | #include "hsr_slave.h" |
|---|
| .. | .. |
|---|
| 18 | 16 | #include "hsr_forward.h" |
|---|
| 19 | 17 | #include "hsr_framereg.h" |
|---|
| 20 | 18 | |
|---|
| 19 | +bool hsr_invalid_dan_ingress_frame(__be16 protocol) |
|---|
| 20 | +{ |
|---|
| 21 | + return (protocol != htons(ETH_P_PRP) && protocol != htons(ETH_P_HSR)); |
|---|
| 22 | +} |
|---|
| 21 | 23 | |
|---|
| 22 | 24 | static rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb) |
|---|
| 23 | 25 | { |
|---|
| 24 | 26 | struct sk_buff *skb = *pskb; |
|---|
| 25 | 27 | struct hsr_port *port; |
|---|
| 26 | | - u16 protocol; |
|---|
| 28 | + struct hsr_priv *hsr; |
|---|
| 29 | + __be16 protocol; |
|---|
| 30 | + |
|---|
| 31 | + /* Packets from dev_loopback_xmit() do not have L2 header, bail out */ |
|---|
| 32 | + if (unlikely(skb->pkt_type == PACKET_LOOPBACK)) |
|---|
| 33 | + return RX_HANDLER_PASS; |
|---|
| 27 | 34 | |
|---|
| 28 | 35 | if (!skb_mac_header_was_set(skb)) { |
|---|
| 29 | 36 | WARN_ONCE(1, "%s: skb invalid", __func__); |
|---|
| 30 | 37 | return RX_HANDLER_PASS; |
|---|
| 31 | 38 | } |
|---|
| 32 | 39 | |
|---|
| 33 | | - rcu_read_lock(); /* hsr->node_db, hsr->ports */ |
|---|
| 34 | 40 | port = hsr_port_get_rcu(skb->dev); |
|---|
| 35 | 41 | if (!port) |
|---|
| 36 | 42 | goto finish_pass; |
|---|
| 43 | + hsr = port->hsr; |
|---|
| 37 | 44 | |
|---|
| 38 | 45 | if (hsr_addr_is_self(port->hsr, eth_hdr(skb)->h_source)) { |
|---|
| 39 | 46 | /* Directly kill frames sent by ourselves */ |
|---|
| .. | .. |
|---|
| 41 | 48 | goto finish_consume; |
|---|
| 42 | 49 | } |
|---|
| 43 | 50 | |
|---|
| 51 | + /* For HSR, only tagged frames are expected, but for PRP |
|---|
| 52 | + * there could be non tagged frames as well from Single |
|---|
| 53 | + * attached nodes (SANs). |
|---|
| 54 | + */ |
|---|
| 44 | 55 | protocol = eth_hdr(skb)->h_proto; |
|---|
| 45 | | - if (protocol != htons(ETH_P_PRP) && protocol != htons(ETH_P_HSR)) |
|---|
| 56 | + if (hsr->proto_ops->invalid_dan_ingress_frame && |
|---|
| 57 | + hsr->proto_ops->invalid_dan_ingress_frame(protocol)) |
|---|
| 46 | 58 | goto finish_pass; |
|---|
| 47 | 59 | |
|---|
| 48 | 60 | skb_push(skb, ETH_HLEN); |
|---|
| 61 | + skb_reset_mac_header(skb); |
|---|
| 62 | + if ((!hsr->prot_version && protocol == htons(ETH_P_PRP)) || |
|---|
| 63 | + protocol == htons(ETH_P_HSR)) |
|---|
| 64 | + skb_set_network_header(skb, ETH_HLEN + HSR_HLEN); |
|---|
| 65 | + skb_reset_mac_len(skb); |
|---|
| 49 | 66 | |
|---|
| 50 | 67 | hsr_forward_skb(skb, port); |
|---|
| 51 | 68 | |
|---|
| 52 | 69 | finish_consume: |
|---|
| 53 | | - rcu_read_unlock(); /* hsr->node_db, hsr->ports */ |
|---|
| 54 | 70 | return RX_HANDLER_CONSUMED; |
|---|
| 55 | 71 | |
|---|
| 56 | 72 | finish_pass: |
|---|
| 57 | | - rcu_read_unlock(); /* hsr->node_db, hsr->ports */ |
|---|
| 58 | 73 | return RX_HANDLER_PASS; |
|---|
| 59 | 74 | } |
|---|
| 60 | 75 | |
|---|
| .. | .. |
|---|
| 63 | 78 | return rcu_access_pointer(dev->rx_handler) == hsr_handle_frame; |
|---|
| 64 | 79 | } |
|---|
| 65 | 80 | |
|---|
| 66 | | - |
|---|
| 67 | | -static int hsr_check_dev_ok(struct net_device *dev) |
|---|
| 81 | +static int hsr_check_dev_ok(struct net_device *dev, |
|---|
| 82 | + struct netlink_ext_ack *extack) |
|---|
| 68 | 83 | { |
|---|
| 69 | 84 | /* Don't allow HSR on non-ethernet like devices */ |
|---|
| 70 | | - if ((dev->flags & IFF_LOOPBACK) || (dev->type != ARPHRD_ETHER) || |
|---|
| 71 | | - (dev->addr_len != ETH_ALEN)) { |
|---|
| 72 | | - netdev_info(dev, "Cannot use loopback or non-ethernet device as HSR slave.\n"); |
|---|
| 85 | + if ((dev->flags & IFF_LOOPBACK) || dev->type != ARPHRD_ETHER || |
|---|
| 86 | + dev->addr_len != ETH_ALEN) { |
|---|
| 87 | + NL_SET_ERR_MSG_MOD(extack, "Cannot use loopback or non-ethernet device as HSR slave."); |
|---|
| 73 | 88 | return -EINVAL; |
|---|
| 74 | 89 | } |
|---|
| 75 | 90 | |
|---|
| 76 | 91 | /* Don't allow enslaving hsr devices */ |
|---|
| 77 | 92 | if (is_hsr_master(dev)) { |
|---|
| 78 | | - netdev_info(dev, "Cannot create trees of HSR devices.\n"); |
|---|
| 93 | + NL_SET_ERR_MSG_MOD(extack, |
|---|
| 94 | + "Cannot create trees of HSR devices."); |
|---|
| 79 | 95 | return -EINVAL; |
|---|
| 80 | 96 | } |
|---|
| 81 | 97 | |
|---|
| 82 | 98 | if (hsr_port_exists(dev)) { |
|---|
| 83 | | - netdev_info(dev, "This device is already a HSR slave.\n"); |
|---|
| 99 | + NL_SET_ERR_MSG_MOD(extack, |
|---|
| 100 | + "This device is already a HSR slave."); |
|---|
| 84 | 101 | return -EINVAL; |
|---|
| 85 | 102 | } |
|---|
| 86 | 103 | |
|---|
| 87 | 104 | if (is_vlan_dev(dev)) { |
|---|
| 88 | | - netdev_info(dev, "HSR on top of VLAN is not yet supported in this driver.\n"); |
|---|
| 105 | + NL_SET_ERR_MSG_MOD(extack, "HSR on top of VLAN is not yet supported in this driver."); |
|---|
| 89 | 106 | return -EINVAL; |
|---|
| 90 | 107 | } |
|---|
| 91 | 108 | |
|---|
| 92 | 109 | if (dev->priv_flags & IFF_DONT_BRIDGE) { |
|---|
| 93 | | - netdev_info(dev, "This device does not support bridging.\n"); |
|---|
| 110 | + NL_SET_ERR_MSG_MOD(extack, |
|---|
| 111 | + "This device does not support bridging."); |
|---|
| 94 | 112 | return -EOPNOTSUPP; |
|---|
| 95 | 113 | } |
|---|
| 96 | 114 | |
|---|
| .. | .. |
|---|
| 101 | 119 | return 0; |
|---|
| 102 | 120 | } |
|---|
| 103 | 121 | |
|---|
| 104 | | - |
|---|
| 105 | 122 | /* Setup device to be added to the HSR bridge. */ |
|---|
| 106 | | -static int hsr_portdev_setup(struct net_device *dev, struct hsr_port *port) |
|---|
| 123 | +static int hsr_portdev_setup(struct hsr_priv *hsr, struct net_device *dev, |
|---|
| 124 | + struct hsr_port *port, |
|---|
| 125 | + struct netlink_ext_ack *extack) |
|---|
| 126 | + |
|---|
| 107 | 127 | { |
|---|
| 128 | + struct net_device *hsr_dev; |
|---|
| 129 | + struct hsr_port *master; |
|---|
| 108 | 130 | int res; |
|---|
| 109 | 131 | |
|---|
| 110 | | - dev_hold(dev); |
|---|
| 111 | 132 | res = dev_set_promiscuity(dev, 1); |
|---|
| 112 | 133 | if (res) |
|---|
| 113 | | - goto fail_promiscuity; |
|---|
| 134 | + return res; |
|---|
| 114 | 135 | |
|---|
| 115 | | - /* FIXME: |
|---|
| 116 | | - * What does net device "adjacency" mean? Should we do |
|---|
| 117 | | - * res = netdev_master_upper_dev_link(port->dev, port->hsr->dev); ? |
|---|
| 118 | | - */ |
|---|
| 136 | + master = hsr_port_get_hsr(hsr, HSR_PT_MASTER); |
|---|
| 137 | + hsr_dev = master->dev; |
|---|
| 138 | + |
|---|
| 139 | + res = netdev_upper_dev_link(dev, hsr_dev, extack); |
|---|
| 140 | + if (res) |
|---|
| 141 | + goto fail_upper_dev_link; |
|---|
| 119 | 142 | |
|---|
| 120 | 143 | res = netdev_rx_handler_register(dev, hsr_handle_frame, port); |
|---|
| 121 | 144 | if (res) |
|---|
| .. | .. |
|---|
| 125 | 148 | return 0; |
|---|
| 126 | 149 | |
|---|
| 127 | 150 | fail_rx_handler: |
|---|
| 151 | + netdev_upper_dev_unlink(dev, hsr_dev); |
|---|
| 152 | +fail_upper_dev_link: |
|---|
| 128 | 153 | dev_set_promiscuity(dev, -1); |
|---|
| 129 | | -fail_promiscuity: |
|---|
| 130 | | - dev_put(dev); |
|---|
| 131 | | - |
|---|
| 132 | 154 | return res; |
|---|
| 133 | 155 | } |
|---|
| 134 | 156 | |
|---|
| 135 | 157 | int hsr_add_port(struct hsr_priv *hsr, struct net_device *dev, |
|---|
| 136 | | - enum hsr_port_type type) |
|---|
| 158 | + enum hsr_port_type type, struct netlink_ext_ack *extack) |
|---|
| 137 | 159 | { |
|---|
| 138 | 160 | struct hsr_port *port, *master; |
|---|
| 139 | 161 | int res; |
|---|
| 140 | 162 | |
|---|
| 141 | 163 | if (type != HSR_PT_MASTER) { |
|---|
| 142 | | - res = hsr_check_dev_ok(dev); |
|---|
| 164 | + res = hsr_check_dev_ok(dev, extack); |
|---|
| 143 | 165 | if (res) |
|---|
| 144 | 166 | return res; |
|---|
| 145 | 167 | } |
|---|
| 146 | 168 | |
|---|
| 147 | 169 | port = hsr_port_get_hsr(hsr, type); |
|---|
| 148 | | - if (port != NULL) |
|---|
| 170 | + if (port) |
|---|
| 149 | 171 | return -EBUSY; /* This port already exists */ |
|---|
| 150 | 172 | |
|---|
| 151 | 173 | port = kzalloc(sizeof(*port), GFP_KERNEL); |
|---|
| 152 | | - if (port == NULL) |
|---|
| 174 | + if (!port) |
|---|
| 153 | 175 | return -ENOMEM; |
|---|
| 154 | 176 | |
|---|
| 155 | 177 | port->hsr = hsr; |
|---|
| .. | .. |
|---|
| 157 | 179 | port->type = type; |
|---|
| 158 | 180 | |
|---|
| 159 | 181 | if (type != HSR_PT_MASTER) { |
|---|
| 160 | | - res = hsr_portdev_setup(dev, port); |
|---|
| 182 | + res = hsr_portdev_setup(hsr, dev, port, extack); |
|---|
| 161 | 183 | if (res) |
|---|
| 162 | 184 | goto fail_dev_setup; |
|---|
| 163 | 185 | } |
|---|
| .. | .. |
|---|
| 186 | 208 | list_del_rcu(&port->port_list); |
|---|
| 187 | 209 | |
|---|
| 188 | 210 | if (port != master) { |
|---|
| 189 | | - if (master != NULL) { |
|---|
| 190 | | - netdev_update_features(master->dev); |
|---|
| 191 | | - dev_set_mtu(master->dev, hsr_get_max_mtu(hsr)); |
|---|
| 192 | | - } |
|---|
| 211 | + netdev_update_features(master->dev); |
|---|
| 212 | + dev_set_mtu(master->dev, hsr_get_max_mtu(hsr)); |
|---|
| 193 | 213 | netdev_rx_handler_unregister(port->dev); |
|---|
| 194 | 214 | dev_set_promiscuity(port->dev, -1); |
|---|
| 215 | + netdev_upper_dev_unlink(port->dev, master->dev); |
|---|
| 195 | 216 | } |
|---|
| 196 | | - |
|---|
| 197 | | - /* FIXME? |
|---|
| 198 | | - * netdev_upper_dev_unlink(port->dev, port->hsr->dev); |
|---|
| 199 | | - */ |
|---|
| 200 | 217 | |
|---|
| 201 | 218 | synchronize_rcu(); |
|---|
| 202 | 219 | |
|---|
| 203 | | - if (port != master) |
|---|
| 204 | | - dev_put(port->dev); |
|---|
| 220 | + kfree(port); |
|---|
| 205 | 221 | } |
|---|