/* * Copyright (c) 2007-2012 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA */ /* * Includes Inango Systems Ltd’s changes/modifications dated: 2021. * Changed/modified portions - Copyright (c) 2021 , Inango Systems Ltd. */ /* Includes MaxLinear's changes dated: 2021, 2022, 2023. Changed portions - Copyright 2021-2023 MaxLinear, Inc. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include /* * Sysfs attributes of bridge for Open vSwitch * * This has been shamelessly copied from the kernel sources. */ #include #include #include #include #include #include #include #include #if IS_ENABLED(CONFIG_BRIDGE) #include #endif #include "dp_sysfs.h" #include "datapath.h" #include "vport-internal_dev.h" #ifdef CONFIG_SYSFS #define INTERNAL_DEVICE_ATTR DEVICE_ATTR #define DEVICE_PARAMS struct device *d, struct device_attribute *attr #define DEVICE_ARGS d, attr #define DEV_ATTR(NAME) dev_attr_##NAME static bool nf_disable_arptables_value = false; static bool nf_disable_ip6tables_value = false; static bool nf_disable_iptables_value = false; /* * Common code for storing bridge parameters. */ static int set_unsupported_param(struct net_device *dev, unsigned long val) { return -EOPNOTSUPP; } static ssize_t store_bridge_parm(DEVICE_PARAMS, const char *buf, size_t len, int (*set)(struct net_device *, unsigned long)) { char *endp; unsigned long val; int err; if (!capable(CAP_NET_ADMIN)) return -EPERM; val = simple_strtoul(buf, &endp, 0); if (endp == buf) return -EINVAL; if (!rtnl_trylock()) return restart_syscall(); if (to_net_dev(d)) err = (*set)(to_net_dev(d), val); else err = -ENODEV; rtnl_unlock(); return err ? err : len; } static ssize_t show_ageing_time(DEVICE_PARAMS, char *buf) { unsigned long value; if (ovs_dp_sysfs_hook) ovs_dp_sysfs_hook((to_net_dev(d)), &value, IFLA_BR_AGEING_TIME, GET_PARAMETER); return sprintf(buf, "%lu\n", value); } static int set_ageing_time(struct net_device *dev, unsigned long val) { if (ovs_dp_sysfs_hook) ovs_dp_sysfs_hook(dev, &val, IFLA_BR_AGEING_TIME, SET_PARAMETER); return 0; } static ssize_t store_ageing_time(DEVICE_PARAMS, const char *buf, size_t len) { return store_bridge_parm(DEVICE_ARGS, buf, len, set_ageing_time); } static INTERNAL_DEVICE_ATTR(ageing_time, S_IRUGO | S_IWUSR, show_ageing_time, store_ageing_time); static ssize_t show_forward_delay(DEVICE_PARAMS, char *buf) { unsigned long value; if (ovs_dp_sysfs_hook) ovs_dp_sysfs_hook((to_net_dev(d)), &value, IFLA_BR_FORWARD_DELAY, GET_PARAMETER); return sprintf(buf, "%lu\n", value); } static int set_forward_delay(struct net_device *dev, unsigned long val) { if (ovs_dp_sysfs_hook) ovs_dp_sysfs_hook(dev, &val, IFLA_BR_FORWARD_DELAY, SET_PARAMETER); return 0; } static ssize_t store_forward_delay(DEVICE_PARAMS, const char *buf, size_t len) { return store_bridge_parm(DEVICE_ARGS, buf, len, set_forward_delay); } static INTERNAL_DEVICE_ATTR(forward_delay, S_IRUGO | S_IWUSR, show_forward_delay, store_forward_delay); static ssize_t show_hello_time(DEVICE_PARAMS, char *buf) { unsigned long value; if (ovs_dp_sysfs_hook) ovs_dp_sysfs_hook((to_net_dev(d)), &value, IFLA_BR_HELLO_TIME, GET_PARAMETER); return sprintf(buf, "%lu\n", value); } static int set_hello_time(struct net_device *dev, unsigned long val) { if (ovs_dp_sysfs_hook) ovs_dp_sysfs_hook(dev, &val, IFLA_BR_HELLO_TIME, SET_PARAMETER); return 0; } static ssize_t store_hello_time(DEVICE_PARAMS, const char *buf, size_t len) { return store_bridge_parm(DEVICE_ARGS, buf, len, set_hello_time); } static INTERNAL_DEVICE_ATTR(hello_time, S_IRUGO | S_IWUSR, show_hello_time, store_hello_time); static ssize_t show_max_age(DEVICE_PARAMS, char *buf) { unsigned long value; if (ovs_dp_sysfs_hook) ovs_dp_sysfs_hook((to_net_dev(d)), &value, IFLA_BR_MAX_AGE, GET_PARAMETER); return sprintf(buf, "%lu\n", value); } static int set_max_age(struct net_device *dev, unsigned long val) { if (ovs_dp_sysfs_hook) ovs_dp_sysfs_hook(dev, &val, IFLA_BR_MAX_AGE, SET_PARAMETER); return 0; } static ssize_t store_max_age(DEVICE_PARAMS, const char *buf, size_t len) { return store_bridge_parm(DEVICE_ARGS, buf, len, set_max_age); } static INTERNAL_DEVICE_ATTR(max_age, S_IRUGO | S_IWUSR, show_max_age, store_max_age); static ssize_t show_multicast_snooping(DEVICE_PARAMS, char *buf) { unsigned long value; if (ovs_dp_sysfs_hook) ovs_dp_sysfs_hook((to_net_dev(d)), &value, IFLA_BR_MCAST_SNOOPING, GET_PARAMETER); return sprintf(buf, "%lu\n", value); } static int set_multicast_snooping(struct net_device *dev, unsigned long val) { if (ovs_dp_sysfs_hook) ovs_dp_sysfs_hook(dev, &val, IFLA_BR_MCAST_SNOOPING, SET_PARAMETER); return 0; } static ssize_t store_multicast_snooping(DEVICE_PARAMS, const char *buf, size_t len) { return store_bridge_parm(DEVICE_ARGS, buf, len, set_multicast_snooping); } static INTERNAL_DEVICE_ATTR(multicast_snooping, S_IRUGO | S_IWUSR, show_multicast_snooping, store_multicast_snooping); static ssize_t show_priority(DEVICE_PARAMS, char *buf) { unsigned long value; if (ovs_dp_sysfs_hook) ovs_dp_sysfs_hook((to_net_dev(d)), &value, IFLA_BR_PRIORITY, GET_PARAMETER); return sprintf(buf, "%lu\n", value); } static int set_priority(struct net_device *dev, unsigned long val) { if (ovs_dp_sysfs_hook) ovs_dp_sysfs_hook(dev, &val, IFLA_BR_PRIORITY, SET_PARAMETER); return 0; } static ssize_t store_priority(DEVICE_PARAMS, const char *buf, size_t len) { return store_bridge_parm(DEVICE_ARGS, buf, len, set_priority); } static INTERNAL_DEVICE_ATTR(priority, S_IRUGO | S_IWUSR, show_priority, store_priority); static ssize_t show_stp_state(DEVICE_PARAMS, char *buf) { unsigned long value; if (ovs_dp_sysfs_hook) ovs_dp_sysfs_hook((to_net_dev(d)), &value, IFLA_BR_STP_STATE, GET_PARAMETER); return sprintf(buf, "%lu\n", value); } static int set_stp_state(struct net_device *dev, unsigned long val) { if (ovs_dp_sysfs_hook) ovs_dp_sysfs_hook(dev, &val, IFLA_BR_STP_STATE, SET_PARAMETER); return 0; } static ssize_t store_stp_state(DEVICE_PARAMS, const char *buf, size_t len) { return store_bridge_parm(DEVICE_ARGS, buf, len, set_stp_state); } static INTERNAL_DEVICE_ATTR(stp_state, S_IRUGO | S_IWUSR, show_stp_state, store_stp_state); static ssize_t show_root_id(DEVICE_PARAMS, char *buf) { unsigned char *dev_addr; dev_addr = to_net_dev(d)->dev_addr; return sprintf(buf, "8000.%.2x%.2x%.2x%.2x%.2x%.2x\n", dev_addr[0], dev_addr[1], dev_addr[2], dev_addr[3], dev_addr[4], dev_addr[5]); } static INTERNAL_DEVICE_ATTR(root_id, S_IRUGO, show_root_id, NULL); static ssize_t show_bridge_id(DEVICE_PARAMS, char *buf) { struct vport *vport; ssize_t result; unsigned long u_value; u8 *prio = (u8 *)&u_value; if (ovs_dp_sysfs_hook) ovs_dp_sysfs_hook((to_net_dev(d)), &u_value, IFLA_BR_PRIORITY, GET_PARAMETER); rcu_read_lock(); vport = ovs_internal_dev_get_vport(to_net_dev(d)); if (vport) { const unsigned char *addr; addr = vport->ops->get_addr(vport); result = sprintf(buf, "%.2x%.2x.%.2x%.2x%.2x%.2x%.2x%.2x\n", prio[1], prio[0], addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); } else result = -ENODEV; rcu_read_unlock(); return result; } static INTERNAL_DEVICE_ATTR(bridge_id, S_IRUGO, show_bridge_id, NULL); /* At the moment next sysfs are not implemented in the openvswitch, we make a stub from the value as in Linux bridge. */ static ssize_t show_root_port(DEVICE_PARAMS, char *buf) { return sprintf(buf, "%d\n", 0); } static INTERNAL_DEVICE_ATTR(root_port, S_IRUGO, show_root_port, NULL); static ssize_t show_root_path_cost(DEVICE_PARAMS, char *buf) { return sprintf(buf, "%d\n", 0); } static INTERNAL_DEVICE_ATTR(root_path_cost, S_IRUGO, show_root_path_cost, NULL); static int show_hash_max(DEVICE_PARAMS, char *buf) { unsigned long value; value = 512; return sprintf(buf, "%lu\n", value); } static ssize_t store_hash_max(DEVICE_PARAMS, const char *buf, size_t len) { return store_bridge_parm(DEVICE_ARGS, buf, len, set_unsupported_param); } static INTERNAL_DEVICE_ATTR(hash_max, S_IRUGO | S_IWUSR, show_hash_max, store_hash_max); static int show_multicast_last_member_count(DEVICE_PARAMS, char *buf) { unsigned long value = 0; if (ovs_dp_sysfs_hook) ovs_dp_sysfs_hook(to_net_dev(d), &value, IFLA_BR_MCAST_LAST_MEMBER_CNT, GET_PARAMETER); return sprintf(buf, "%lu\n", value); } static int set_multicast_last_member_count(struct net_device *dev, unsigned long val) { if (ovs_dp_sysfs_hook) ovs_dp_sysfs_hook(dev, &val, IFLA_BR_MCAST_LAST_MEMBER_CNT, SET_PARAMETER); return 0; } static ssize_t store_multicast_last_member_count(DEVICE_PARAMS, const char *buf, size_t len) { return store_bridge_parm(DEVICE_ARGS, buf, len, set_multicast_last_member_count); } static INTERNAL_DEVICE_ATTR(multicast_last_member_count, S_IRUGO | S_IWUSR, show_multicast_last_member_count, store_multicast_last_member_count); static int show_multicast_last_member_interval(DEVICE_PARAMS, char *buf) { unsigned long value = 0; if (ovs_dp_sysfs_hook) ovs_dp_sysfs_hook(to_net_dev(d), &value, IFLA_BR_MCAST_LAST_MEMBER_INTVL, GET_PARAMETER); return sprintf(buf, "%lu\n", value); } static int set_multicast_last_member_interval(struct net_device *dev, unsigned long val) { if (ovs_dp_sysfs_hook) ovs_dp_sysfs_hook(dev, &val, IFLA_BR_MCAST_LAST_MEMBER_INTVL, SET_PARAMETER); return 0; } static ssize_t store_multicast_last_member_interval(DEVICE_PARAMS, const char *buf, size_t len) { return store_bridge_parm(DEVICE_ARGS, buf, len, set_multicast_last_member_interval); } static INTERNAL_DEVICE_ATTR(multicast_last_member_interval, S_IRUGO | S_IWUSR, show_multicast_last_member_interval, store_multicast_last_member_interval); static int show_multicast_membership_interval(DEVICE_PARAMS, char *buf) { unsigned long value = 0; if (ovs_dp_sysfs_hook) ovs_dp_sysfs_hook(to_net_dev(d), &value, IFLA_BR_MCAST_MEMBERSHIP_INTVL, GET_PARAMETER); return sprintf(buf, "%lu\n", value); } static int set_multicast_membership_interval(struct net_device *dev, unsigned long val) { if (ovs_dp_sysfs_hook) ovs_dp_sysfs_hook(dev, &val, IFLA_BR_MCAST_MEMBERSHIP_INTVL, SET_PARAMETER); return 0; } static ssize_t store_multicast_membership_interval(DEVICE_PARAMS, const char *buf, size_t len) { return store_bridge_parm(DEVICE_ARGS, buf, len, set_multicast_membership_interval); } static INTERNAL_DEVICE_ATTR(multicast_membership_interval, S_IRUGO | S_IWUSR, show_multicast_membership_interval, store_multicast_membership_interval); static int show_multicast_querier(DEVICE_PARAMS, char *buf) { unsigned long value = 0; if (ovs_dp_sysfs_hook) ovs_dp_sysfs_hook(to_net_dev(d), &value, IFLA_BR_MCAST_QUERIER, GET_PARAMETER); return sprintf(buf, "%lu\n", value); } static int set_multicast_querier(struct net_device *dev, unsigned long val) { if (ovs_dp_sysfs_hook) ovs_dp_sysfs_hook(dev, &val, IFLA_BR_MCAST_QUERIER, SET_PARAMETER); return 0; } static ssize_t store_multicast_querier(DEVICE_PARAMS, const char *buf, size_t len) { return store_bridge_parm(DEVICE_ARGS, buf, len, set_multicast_querier); } static INTERNAL_DEVICE_ATTR(multicast_querier, S_IRUGO | S_IWUSR, show_multicast_querier, store_multicast_querier); static int show_multicast_querier_interval(DEVICE_PARAMS, char *buf) { unsigned long value; value = 25500; return sprintf(buf, "%lu\n", value); } static ssize_t store_multicast_querier_interval(DEVICE_PARAMS, const char *buf, size_t len) { return store_bridge_parm(DEVICE_ARGS, buf, len, set_unsupported_param); } static INTERNAL_DEVICE_ATTR(multicast_querier_interval, S_IRUGO | S_IWUSR, show_multicast_querier_interval, store_multicast_querier_interval); static int show_multicast_query_interval(DEVICE_PARAMS, char *buf) { unsigned long value = 0; if (ovs_dp_sysfs_hook) ovs_dp_sysfs_hook(to_net_dev(d), &value, IFLA_BR_MCAST_QUERY_INTVL, GET_PARAMETER); return sprintf(buf, "%lu\n", value); } static int set_multicast_query_interval(struct net_device *dev, unsigned long val) { if (ovs_dp_sysfs_hook) ovs_dp_sysfs_hook(dev, &val, IFLA_BR_MCAST_QUERY_INTVL, SET_PARAMETER); return 0; } static ssize_t store_multicast_query_interval(DEVICE_PARAMS, const char *buf, size_t len) { return store_bridge_parm(DEVICE_ARGS, buf, len, set_multicast_query_interval); } static INTERNAL_DEVICE_ATTR(multicast_query_interval, S_IRUGO | S_IWUSR, show_multicast_query_interval, store_multicast_query_interval); static int show_multicast_query_response_interval(DEVICE_PARAMS, char *buf) { unsigned long value = 0; if (ovs_dp_sysfs_hook) ovs_dp_sysfs_hook(to_net_dev(d), &value, IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, GET_PARAMETER); return sprintf(buf, "%lu\n", value); } static int set_multicast_query_response_interval(struct net_device *dev, unsigned long val) { if (ovs_dp_sysfs_hook) ovs_dp_sysfs_hook(dev, &val, IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, SET_PARAMETER); return 0; } static ssize_t store_multicast_query_response_interval(DEVICE_PARAMS, const char *buf, size_t len) { return store_bridge_parm(DEVICE_ARGS, buf, len, set_multicast_query_response_interval); } static INTERNAL_DEVICE_ATTR(multicast_query_response_interval, S_IRUGO | S_IWUSR, show_multicast_query_response_interval, store_multicast_query_response_interval); static int show_multicast_query_use_ifaddr(DEVICE_PARAMS, char *buf) { unsigned long value = 0; if (ovs_dp_sysfs_hook) ovs_dp_sysfs_hook(to_net_dev(d), &value, IFLA_BR_MCAST_QUERY_USE_IFADDR, GET_PARAMETER); return sprintf(buf, "%lu\n", value); } static int set_multicast_query_use_ifaddr(struct net_device *dev, unsigned long val) { if (ovs_dp_sysfs_hook) ovs_dp_sysfs_hook(dev, &val, IFLA_BR_MCAST_QUERY_USE_IFADDR, SET_PARAMETER); return 0; } static ssize_t store_multicast_query_use_ifaddr(DEVICE_PARAMS, const char *buf, size_t len) { return store_bridge_parm(DEVICE_ARGS, buf, len, set_multicast_query_use_ifaddr); } static INTERNAL_DEVICE_ATTR(multicast_query_use_ifaddr, S_IRUGO | S_IWUSR, show_multicast_query_use_ifaddr, store_multicast_query_use_ifaddr); static int show_multicast_igmp_version(DEVICE_PARAMS, char *buf) { unsigned long value = 0; if (ovs_dp_sysfs_hook) ovs_dp_sysfs_hook(to_net_dev(d), &value, IFLA_BR_MCAST_IGMP_VERSION, GET_PARAMETER); return sprintf(buf, "%lu\n", value); } static int set_multicast_igmp_version(struct net_device *dev, unsigned long val) { if (ovs_dp_sysfs_hook) ovs_dp_sysfs_hook(dev, &val, IFLA_BR_MCAST_IGMP_VERSION, SET_PARAMETER); return 0; } static ssize_t store_multicast_igmp_version(DEVICE_PARAMS, const char *buf, size_t len) { return store_bridge_parm(DEVICE_ARGS, buf, len, set_multicast_igmp_version); } static INTERNAL_DEVICE_ATTR(multicast_igmp_version, S_IRUGO | S_IWUSR, show_multicast_igmp_version, store_multicast_igmp_version); #if IS_ENABLED(CONFIG_IPV6) static int show_multicast_mld_version(DEVICE_PARAMS, char *buf) { unsigned long value = 0; if (ovs_dp_sysfs_hook) ovs_dp_sysfs_hook(to_net_dev(d), &value, IFLA_BR_MCAST_MLD_VERSION, GET_PARAMETER); return sprintf(buf, "%lu\n", value); } static int set_multicast_mld_version(struct net_device *dev, unsigned long val) { if (ovs_dp_sysfs_hook) ovs_dp_sysfs_hook(dev, &val, IFLA_BR_MCAST_MLD_VERSION, SET_PARAMETER); return 0; } static ssize_t store_multicast_mld_version(DEVICE_PARAMS, const char *buf, size_t len) { return store_bridge_parm(DEVICE_ARGS, buf, len, set_multicast_mld_version); } static INTERNAL_DEVICE_ATTR(multicast_mld_version, S_IRUGO | S_IWUSR, show_multicast_mld_version, store_multicast_mld_version); #endif static int show_multicast_router(DEVICE_PARAMS, char *buf) { unsigned long value; value = 1; return sprintf(buf, "%lu\n", value); } static ssize_t store_multicast_router(DEVICE_PARAMS, const char *buf, size_t len) { return store_bridge_parm(DEVICE_ARGS, buf, len, set_unsupported_param); } static INTERNAL_DEVICE_ATTR(multicast_router, S_IRUGO | S_IWUSR, show_multicast_router, store_multicast_router); static int show_multicast_startup_query_count(DEVICE_PARAMS, char *buf) { unsigned long value; value = 2; return sprintf(buf, "%lu\n", value); } static ssize_t store_multicast_startup_query_count(DEVICE_PARAMS, const char *buf, size_t len) { return store_bridge_parm(DEVICE_ARGS, buf, len, set_unsupported_param); } static INTERNAL_DEVICE_ATTR(multicast_startup_query_count, S_IRUGO | S_IWUSR, show_multicast_startup_query_count, store_multicast_startup_query_count); static int show_multicast_startup_query_interval(DEVICE_PARAMS, char *buf) { unsigned long value; value = 3125; return sprintf(buf, "%lu\n", value); } static ssize_t store_multicast_startup_query_interval(DEVICE_PARAMS, const char *buf, size_t len) { return store_bridge_parm(DEVICE_ARGS, buf, len, set_unsupported_param); } static INTERNAL_DEVICE_ATTR(multicast_startup_query_interval, S_IRUGO | S_IWUSR, show_multicast_startup_query_interval, store_multicast_startup_query_interval); static int show_nf_call_iptables(DEVICE_PARAMS, char *buf) { unsigned long value; value = 0; return sprintf(buf, "%lu\n", value); } static ssize_t store_nf_call_iptables(DEVICE_PARAMS, const char *buf, size_t len) { return store_bridge_parm(DEVICE_ARGS, buf, len, set_unsupported_param); } static INTERNAL_DEVICE_ATTR(nf_call_iptables, S_IRUGO | S_IWUSR, show_nf_call_iptables, store_nf_call_iptables); static int show_nf_call_arptables(DEVICE_PARAMS, char *buf) { unsigned long value; value = 0; return sprintf(buf, "%lu\n", value); } static ssize_t store_nf_call_arptables(DEVICE_PARAMS, const char *buf, size_t len) { return store_bridge_parm(DEVICE_ARGS, buf, len, set_unsupported_param); } static INTERNAL_DEVICE_ATTR(nf_call_arptables, S_IRUGO | S_IWUSR, show_nf_call_arptables, store_nf_call_arptables); static int show_nf_disable_arptables(DEVICE_PARAMS, char *buf) { return sprintf(buf, "%d\n", nf_disable_arptables_value); } /* * Stub for saving nf_disable_arptables */ static ssize_t store_nf_disable_arptables(DEVICE_PARAMS, const char *buf, size_t len) { char *endp; unsigned long val; val = simple_strtoul(buf, &endp, 0); if (endp == buf) { return -EINVAL; } nf_disable_arptables_value = !!val; pr_warn("%s: Changing the value of nf_disable_arptables is handled by the " "stub function. It has no effect.", __func__); return len; } static INTERNAL_DEVICE_ATTR(nf_disable_arptables, S_IRUGO | S_IWUSR, show_nf_disable_arptables, store_nf_disable_arptables); /* * Stub for saving nf_disable_ip6tables */ static int show_nf_disable_ip6tables(DEVICE_PARAMS, char *buf) { return sprintf(buf, "%d\n", nf_disable_ip6tables_value); } static ssize_t store_nf_disable_ip6tables(DEVICE_PARAMS, const char *buf, size_t len) { char *endp; unsigned long val; val = simple_strtoul(buf, &endp, 0); if (endp == buf) { return -EINVAL; } nf_disable_ip6tables_value = !!val; pr_warn("%s: Changing the value of nf_disable_ip6tables is handled by the " "stub function. It has no effect.", __func__); return len; } static INTERNAL_DEVICE_ATTR(nf_disable_ip6tables, S_IRUGO | S_IWUSR, show_nf_disable_ip6tables, store_nf_disable_ip6tables); static int show_nf_disable_iptables(DEVICE_PARAMS, char *buf) { return sprintf(buf, "%d\n", nf_disable_iptables_value); } /* * Stub for saving nf_disable_iptables */ static ssize_t store_nf_disable_iptables(DEVICE_PARAMS, const char *buf, size_t len) { char *endp; unsigned long val; val = simple_strtoul(buf, &endp, 0); if (endp == buf) { return -EINVAL; } nf_disable_iptables_value = !!val; pr_warn("%s: Changing the value of nf_disable_iptables is handled by the " "stub function. It has no effect.", __func__); return len; } static INTERNAL_DEVICE_ATTR(nf_disable_iptables, S_IRUGO | S_IWUSR, show_nf_disable_iptables, store_nf_disable_iptables); static ssize_t show_topology_change(DEVICE_PARAMS, char *buf) { return sprintf(buf, "%d\n", 0); } static INTERNAL_DEVICE_ATTR(topology_change, S_IRUGO, show_topology_change, NULL); static ssize_t show_topology_change_timer(DEVICE_PARAMS, char *buf) { return sprintf(buf, "%d\n", 0); } static INTERNAL_DEVICE_ATTR(topology_change_timer, S_IRUGO, show_topology_change_timer, NULL); static struct attribute *bridge_attrs[] = { &DEV_ATTR(ageing_time).attr, &DEV_ATTR(forward_delay).attr, &DEV_ATTR(hello_time).attr, &DEV_ATTR(max_age).attr, &DEV_ATTR(multicast_snooping).attr, &DEV_ATTR(priority).attr, &DEV_ATTR(stp_state).attr, &DEV_ATTR(root_id).attr, &DEV_ATTR(bridge_id).attr, &DEV_ATTR(root_path_cost).attr, &DEV_ATTR(root_port).attr, &DEV_ATTR(hash_max).attr, &DEV_ATTR(multicast_last_member_count).attr, &DEV_ATTR(multicast_last_member_interval).attr, &DEV_ATTR(multicast_membership_interval).attr, &DEV_ATTR(multicast_querier).attr, &DEV_ATTR(multicast_querier_interval).attr, &DEV_ATTR(multicast_query_interval).attr, &DEV_ATTR(multicast_query_response_interval).attr, &DEV_ATTR(multicast_router).attr, &DEV_ATTR(multicast_startup_query_count).attr, &DEV_ATTR(multicast_startup_query_interval).attr, &DEV_ATTR(nf_call_arptables).attr, &DEV_ATTR(nf_call_iptables).attr, &DEV_ATTR(nf_disable_arptables).attr, &DEV_ATTR(nf_disable_ip6tables).attr, &DEV_ATTR(nf_disable_iptables).attr, &DEV_ATTR(topology_change).attr, &DEV_ATTR(topology_change_timer).attr, &DEV_ATTR(multicast_query_use_ifaddr).attr, &DEV_ATTR(multicast_igmp_version).attr, #if IS_ENABLED(CONFIG_IPV6) &DEV_ATTR(multicast_mld_version).attr, #endif NULL }; static struct attribute_group bridge_group = { .name = SYSFS_BRIDGE_ATTR, /* "bridge" */ .attrs = bridge_attrs, }; static ssize_t brforward_show(struct file *file, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct device *dev = container_of(kobj, struct device, kobj); struct net_device *net_dev = container_of(dev, struct net_device, dev); unsigned long maxnum = count / sizeof(struct __fdb_entry); unsigned long offset = off / sizeof(struct __fdb_entry); int res; if (off % sizeof(struct __fdb_entry) != 0) return -EINVAL; if (ovs_get_fdb_entries == NULL) { pr_err("%s: ovs_get_fdb_entries is not defined\n", __func__); return -EAGAIN; } if (!rtnl_trylock()) return restart_syscall(); res = ovs_get_fdb_entries(net_dev, buf, maxnum, offset, false); rtnl_unlock(); if (res > 0) res *= sizeof(struct __fdb_entry); return res; } static struct bin_attribute brforward_attribute = { .attr = { .name = SYSFS_BRIDGE_FDB, .mode = 0444, }, .read = brforward_show, }; /* * Add entries in sysfs onto the existing network class device * for the bridge. * Adds a attribute group "bridge" containing tuning parameters. * Sub directory to hold links to interfaces. * * Note: the ifobj exists only to be a subdirectory * to hold links. The ifobj exists in the same data structure * as its parent the bridge so reference counting works. */ int ovs_dp_sysfs_add_bridge(struct datapath *dp, struct vport *vport) { struct kobject *kobj = vport->ops->get_kobj(vport); int err; #ifdef CONFIG_NET_NS /* Due to bug in 2.6.32 kernel, sysfs_create_group() could panic * in other namespace than init_net. Following check is to avoid it. */ if (!kobj->sd) return -ENOENT; #endif /* Create /sys/class/net//brforward file */ err = sysfs_create_bin_file(kobj, &brforward_attribute); if (err) { pr_info("%s: can't add attribute file %s/%s\n", __func__, ovs_dp_name(dp), brforward_attribute.attr.name); goto out1; } /* Create /sys/class/net//bridge directory. */ err = sysfs_create_group(kobj, &bridge_group); if (err) { pr_info("%s: can't create group %s/%s\n", __func__, ovs_dp_name(dp), bridge_group.name); goto out1; } /* Create /sys/class/net//brif directory. */ vport->ifobj = kobject_create_and_add(SYSFS_BRIDGE_PORT_SUBDIR, kobj); if (!vport->ifobj) { pr_info("%s: can't add kobject (directory) %s/%s\n", __func__, ovs_dp_name(dp), kobject_name(vport->ifobj)); goto out2; } kobject_uevent(vport->ifobj, KOBJ_ADD); return 0; out2: sysfs_remove_group(kobj, &bridge_group); sysfs_remove_bin_file(kobj, &brforward_attribute); out1: return err; } int ovs_dp_sysfs_del_bridge(struct vport *vport) { struct kobject *kobj = vport->ops->get_kobj(vport); #ifdef CONFIG_NET_NS if (!kobj->sd) return 0; #endif kobject_put(vport->ifobj); sysfs_remove_group(kobj, &bridge_group); sysfs_remove_bin_file(kobj, &brforward_attribute); return 0; } #else /* !CONFIG_SYSFS */ int ovs_dp_sysfs_add_bridge(struct datapath *dp, struct vport *vport) { return 0; } int ovs_dp_sysfs_del_bridge(struct vport *vport) { return 0; } #endif /* !CONFIG_SYSFS */