// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Sysfs attributes of bridge
* Linux ethernet bridge
*
* Authors:
* Stephen Hemminger <shemminger@osdl.org>
*/
#include <linux/capability.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/if_bridge.h>
#include <linux/rtnetlink.h>
#include <linux/spinlock.h>
#include <linux/times.h>
#include <linux/sched/signal.h>
#include "br_private.h"
/* IMPORTANT: new bridge options must be added with netlink support only
* please do not add new sysfs entries
*/
#define to_bridge(cd) ((struct net_bridge *)netdev_priv(to_net_dev(cd)))
/*
* Common code for storing bridge parameters.
*/
static ssize_t store_bridge_parm(struct device *d,
const char *buf, size_t len,
int (*set)(struct net_bridge *br, unsigned long val,
struct netlink_ext_ack *extack))
{
struct net_bridge *br = to_bridge(d);
struct netlink_ext_ack extack = {0};
unsigned long val;
int err;
if (!ns_capable(dev_net(br->dev)->user_ns, CAP_NET_ADMIN))
return -EPERM;
err = kstrtoul(buf, 0, &val);
if (err != 0)
return err;
if (!rtnl_trylock())
return restart_syscall();
err = (*set)(br, val, &extack);
if (!err)
netdev_state_change(br->dev);
if (extack._msg) {
if (err)
br_err(br, "%s\n", extack._msg);
else
br_warn(br, "%s\n", extack._msg);
}
rtnl_unlock();
return err ? err : len;
}
static ssize_t forward_delay_show(struct device *d,
struct device_attribute *attr, char *buf)
{
struct net_bridge *br = to_bridge(d);
return sprintf(buf, "%lu\n", jiffies_to_clock_t(br->forward_delay));
}
static int set_forward_delay(struct net_bridge *br, unsigned long val,
struct netlink_ext_ack *extack)
{
return br_set_forward_delay(br, val);
}
static ssize_t forward_delay_store(struct device *d,
struct device_attribute *attr,
const char *buf, size_t len)
{
return store_bridge_parm(d, buf, len, set_forward_delay);
}
static DEVICE_ATTR_RW(forward_delay);
static ssize_t hello_time_show(struct device *d, struct device_attribute *attr,
char *buf)
{
return sprintf(buf, "%lu\n",
jiffies_to_clock_t(to_bridge(d)->hello_time));
}
static int set_hello_time(struct net_bridge *br, unsigned long val,
struct netlink_ext_ack *extack)
{
return br_set_hello_time(br, val);
}
static ssize_t hello_time_store(struct device *d,
struct device_attribute *attr, const char *buf,
size_t len)
{
return store_bridge_parm(d, buf, len, set_hello_time);
}
static DEVICE_ATTR_RW(hello_time);
static ssize_t max_age_show(struct device *d, struct device_attribute *attr,
char *buf)
{
return sprintf(buf, "%lu\n",
jiffies_to_clock_t(to_bridge(d)->max_age));
}
static int set_max_age(struct net_bridge *br, unsigned long val,
struct netlink_ext_ack *extack)
{
return br_set_max_age(br, val);
}
static ssize_t max_age_store(struct device *d, struct device_attribute *attr,
const char *buf, size_t len)
{
return store_bridge_parm(d, buf, len, set_max_age);
}
static DEVICE_ATTR_RW(max_age);
static ssize_t ageing_time_show(struct device *d,
struct device_attribute *attr, char *buf)
{
struct net_bridge *br = to_bridge(d);
return sprintf(buf, "%lu\n", jiffies_to_clock_t(br->ageing_time));
}
static int set_ageing_time(struct net_bridge *br, unsigned long val,
struct netlink_ext_ack *extack)
{
return br_set_ageing_time(br, val);
}
static ssize_t ageing_time_store(struct device *d,
struct device_attribute *attr,
const char *buf, size_t len)
{
return store_bridge_parm(d, buf, len, set_ageing_time);
}
static DEVICE_ATTR_RW(ageing_time);
static ssize_t stp_state_show(struct device *d,
struct device_attribute *attr, char *buf)