linux/net/hsr/hsr_main.c
Fernando Fernandez Mancera b65999e723 net: hsr: sync hw addr of slave2 according to slave1 hw addr on PRP
In order to work properly PRP requires slave1 and slave2 to share the
same MAC address. To ease the configuration process on userspace tools,
sync the slave2 MAC address with slave1. In addition, when deleting the
port from the list, restore the original MAC address.

Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
2025-04-14 12:40:06 +01:00

188 lines
4.3 KiB
C

// SPDX-License-Identifier: GPL-2.0
/* Copyright 2011-2014 Autronica Fire and Security AS
*
* Author(s):
* 2011-2014 Arvid Brodin, arvid.brodin@alten.se
*
* Event handling for HSR and PRP devices.
*/
#include <linux/netdevice.h>
#include <net/rtnetlink.h>
#include <linux/rculist.h>
#include <linux/timer.h>
#include <linux/etherdevice.h>
#include "hsr_main.h"
#include "hsr_device.h"
#include "hsr_netlink.h"
#include "hsr_framereg.h"
#include "hsr_slave.h"
static bool hsr_slave_empty(struct hsr_priv *hsr)
{
struct hsr_port *port;
hsr_for_each_port(hsr, port)
if (port->type != HSR_PT_MASTER)
return false;
return true;
}
static int hsr_netdev_notify(struct notifier_block *nb, unsigned long event,
void *ptr)
{
struct hsr_port *port, *master;
struct net_device *dev;
struct hsr_priv *hsr;
LIST_HEAD(list_kill);
int mtu_max;
int res;
dev = netdev_notifier_info_to_dev(ptr);
port = hsr_port_get_rtnl(dev);
if (!port) {
if (!is_hsr_master(dev))
return NOTIFY_DONE; /* Not an HSR device */
hsr = netdev_priv(dev);
port = hsr_port_get_hsr(hsr, HSR_PT_MASTER);
if (!port) {
/* Resend of notification concerning removed device? */
return NOTIFY_DONE;
}
} else {
hsr = port->hsr;
}
switch (event) {
case NETDEV_UP: /* Administrative state DOWN */
case NETDEV_DOWN: /* Administrative state UP */
case NETDEV_CHANGE: /* Link (carrier) state changes */
hsr_check_carrier_and_operstate(hsr);
break;
case NETDEV_CHANGENAME:
if (is_hsr_master(dev))
hsr_debugfs_rename(dev);
break;
case NETDEV_CHANGEADDR:
if (port->type == HSR_PT_MASTER) {
/* This should not happen since there's no
* ndo_set_mac_address() for HSR devices - i.e. not
* supported.
*/
break;
}
master = hsr_port_get_hsr(hsr, HSR_PT_MASTER);
if (port->type == HSR_PT_SLAVE_A) {
eth_hw_addr_set(master->dev, dev->dev_addr);
call_netdevice_notifiers(NETDEV_CHANGEADDR,
master->dev);
if (hsr->prot_version == PRP_V1) {
port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B);
if (port) {
eth_hw_addr_set(port->dev, dev->dev_addr);
call_netdevice_notifiers(NETDEV_CHANGEADDR,
port->dev);
}
}
}
/* Make sure we recognize frames from ourselves in hsr_rcv() */
port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B);
res = hsr_create_self_node(hsr,
master->dev->dev_addr,
port ?
port->dev->dev_addr :
master->dev->dev_addr);
if (res)
netdev_warn(master->dev,
"Could not update HSR node address.\n");
break;
case NETDEV_CHANGEMTU:
if (port->type == HSR_PT_MASTER)
break; /* Handled in ndo_change_mtu() */
mtu_max = hsr_get_max_mtu(port->hsr);
master = hsr_port_get_hsr(port->hsr, HSR_PT_MASTER);
WRITE_ONCE(master->dev->mtu, mtu_max);
break;
case NETDEV_UNREGISTER:
if (!is_hsr_master(dev)) {
master = hsr_port_get_hsr(port->hsr, HSR_PT_MASTER);
hsr_del_port(port);
if (hsr_slave_empty(master->hsr)) {
const struct rtnl_link_ops *ops;
ops = master->dev->rtnl_link_ops;
ops->dellink(master->dev, &list_kill);
unregister_netdevice_many(&list_kill);
}
}
break;
case NETDEV_PRE_TYPE_CHANGE:
/* HSR works only on Ethernet devices. Refuse slave to change
* its type.
*/
return NOTIFY_BAD;
}
return NOTIFY_DONE;
}
struct hsr_port *hsr_port_get_hsr(struct hsr_priv *hsr, enum hsr_port_type pt)
{
struct hsr_port *port;
hsr_for_each_port(hsr, port)
if (port->type == pt)
return port;
return NULL;
}
int hsr_get_version(struct net_device *dev, enum hsr_version *ver)
{
struct hsr_priv *hsr;
hsr = netdev_priv(dev);
*ver = hsr->prot_version;
return 0;
}
EXPORT_SYMBOL(hsr_get_version);
static struct notifier_block hsr_nb = {
.notifier_call = hsr_netdev_notify, /* Slave event notifications */
};
static int __init hsr_init(void)
{
int err;
BUILD_BUG_ON(sizeof(struct hsr_tag) != HSR_HLEN);
err = register_netdevice_notifier(&hsr_nb);
if (err)
return err;
err = hsr_netlink_init();
if (err) {
unregister_netdevice_notifier(&hsr_nb);
return err;
}
return 0;
}
static void __exit hsr_exit(void)
{
hsr_netlink_exit();
hsr_debugfs_remove_root();
unregister_netdevice_notifier(&hsr_nb);
}
module_init(hsr_init);
module_exit(hsr_exit);
MODULE_DESCRIPTION("High-availability Seamless Redundancy (HSR) driver");
MODULE_LICENSE("GPL");