linux/net/bridge/br_ioctl.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 *      Ioctl handler
   4 *      Linux ethernet bridge
   5 *
   6 *      Authors:
   7 *      Lennert Buytenhek               <buytenh@gnu.org>
   8 */
   9
  10#include <linux/capability.h>
  11#include <linux/compat.h>
  12#include <linux/kernel.h>
  13#include <linux/if_bridge.h>
  14#include <linux/netdevice.h>
  15#include <linux/slab.h>
  16#include <linux/times.h>
  17#include <net/net_namespace.h>
  18#include <linux/uaccess.h>
  19#include "br_private.h"
  20
  21static int get_bridge_ifindices(struct net *net, int *indices, int num)
  22{
  23        struct net_device *dev;
  24        int i = 0;
  25
  26        rcu_read_lock();
  27        for_each_netdev_rcu(net, dev) {
  28                if (i >= num)
  29                        break;
  30                if (netif_is_bridge_master(dev))
  31                        indices[i++] = dev->ifindex;
  32        }
  33        rcu_read_unlock();
  34
  35        return i;
  36}
  37
  38/* called with RTNL */
  39static void get_port_ifindices(struct net_bridge *br, int *ifindices, int num)
  40{
  41        struct net_bridge_port *p;
  42
  43        list_for_each_entry(p, &br->port_list, list) {
  44                if (p->port_no < num)
  45                        ifindices[p->port_no] = p->dev->ifindex;
  46        }
  47}
  48
  49/*
  50 * Format up to a page worth of forwarding table entries
  51 * userbuf -- where to copy result
  52 * maxnum  -- maximum number of entries desired
  53 *            (limited to a page for sanity)
  54 * offset  -- number of records to skip
  55 */
  56static int get_fdb_entries(struct net_bridge *br, void __user *userbuf,
  57                           unsigned long maxnum, unsigned long offset)
  58{
  59        int num;
  60        void *buf;
  61        size_t size;
  62
  63        /* Clamp size to PAGE_SIZE, test maxnum to avoid overflow */
  64        if (maxnum > PAGE_SIZE/sizeof(struct __fdb_entry))
  65                maxnum = PAGE_SIZE/sizeof(struct __fdb_entry);
  66
  67        size = maxnum * sizeof(struct __fdb_entry);
  68
  69        buf = kmalloc(size, GFP_USER);
  70        if (!buf)
  71                return -ENOMEM;
  72
  73        num = br_fdb_fillbuf(br, buf, maxnum, offset);
  74        if (num > 0) {
  75                if (copy_to_user(userbuf, buf,
  76                                 array_size(num, sizeof(struct __fdb_entry))))
  77                        num = -EFAULT;
  78        }
  79        kfree(buf);
  80
  81        return num;
  82}
  83
  84/* called with RTNL */
  85static int add_del_if(struct net_bridge *br, int ifindex, int isadd)
  86{
  87        struct net *net = dev_net(br->dev);
  88        struct net_device *dev;
  89        int ret;
  90
  91        if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
  92                return -EPERM;
  93
  94        dev = __dev_get_by_index(net, ifindex);
  95        if (dev == NULL)
  96                return -EINVAL;
  97
  98        if (isadd)
  99                ret = br_add_if(br, dev, NULL);
 100        else
 101                ret = br_del_if(br, dev);
 102
 103        return ret;
 104}
 105
 106#define BR_UARGS_MAX 4
 107static int br_dev_read_uargs(unsigned long *args, size_t nr_args,
 108                             void __user **argp, void __user *data)
 109{
 110        int ret;
 111
 112        if (nr_args < 2 || nr_args > BR_UARGS_MAX)
 113                return -EINVAL;
 114
 115        if (in_compat_syscall()) {
 116                unsigned int cargs[BR_UARGS_MAX];
 117                int i;
 118
 119                ret = copy_from_user(cargs, data, nr_args * sizeof(*cargs));
 120                if (ret)
 121                        goto fault;
 122
 123                for (i = 0; i < nr_args; ++i)
 124                        args[i] = cargs[i];
 125
 126                *argp = compat_ptr(args[1]);
 127        } else {
 128                ret = copy_from_user(args, data, nr_args * sizeof(*args));
 129                if (ret)
 130                        goto fault;
 131                *argp = (void __user *)args[1];
 132        }
 133
 134        return 0;
 135fault:
 136        return -EFAULT;
 137}
 138
 139/*
 140 * Legacy ioctl's through SIOCDEVPRIVATE
 141 * This interface is deprecated because it was too difficult
 142 * to do the translation for 32/64bit ioctl compatibility.
 143 */
 144int br_dev_siocdevprivate(struct net_device *dev, struct ifreq *rq,
 145                          void __user *data, int cmd)
 146{
 147        struct net_bridge *br = netdev_priv(dev);
 148        struct net_bridge_port *p = NULL;
 149        unsigned long args[4];
 150        void __user *argp;
 151        int ret;
 152
 153        ret = br_dev_read_uargs(args, ARRAY_SIZE(args), &argp, data);
 154        if (ret)
 155                return ret;
 156
 157        switch (args[0]) {
 158        case BRCTL_ADD_IF:
 159        case BRCTL_DEL_IF:
 160                return add_del_if(br, args[1], args[0] == BRCTL_ADD_IF);
 161
 162        case BRCTL_GET_BRIDGE_INFO:
 163        {
 164                struct __bridge_info b;
 165
 166                memset(&b, 0, sizeof(struct __bridge_info));
 167                rcu_read_lock();
 168                memcpy(&b.designated_root, &br->designated_root, 8);
 169                memcpy(&b.bridge_id, &br->bridge_id, 8);
 170                b.root_path_cost = br->root_path_cost;
 171                b.max_age = jiffies_to_clock_t(br->max_age);
 172                b.hello_time = jiffies_to_clock_t(br->hello_time);
 173                b.forward_delay = br->forward_delay;
 174                b.bridge_max_age = br->bridge_max_age;
 175                b.bridge_hello_time = br->bridge_hello_time;
 176                b.bridge_forward_delay = jiffies_to_clock_t(br->bridge_forward_delay);
 177                b.topology_change = br->topology_change;
 178                b.topology_change_detected = br->topology_change_detected;
 179                b.root_port = br->root_port;
 180
 181                b.stp_enabled = (br->stp_enabled != BR_NO_STP);
 182                b.ageing_time = jiffies_to_clock_t(br->ageing_time);
 183                b.hello_timer_value = br_timer_value(&br->hello_timer);
 184                b.tcn_timer_value = br_timer_value(&br->tcn_timer);
 185                b.topology_change_timer_value = br_timer_value(&br->topology_change_timer);
 186                b.gc_timer_value = br_timer_value(&br->gc_work.timer);
 187                rcu_read_unlock();
 188
 189                if (copy_to_user((void __user *)args[1], &b, sizeof(b)))
 190                        return -EFAULT;
 191
 192                return 0;
 193        }
 194
 195        case BRCTL_GET_PORT_LIST:
 196        {
 197                int num, *indices;
 198
 199                num = args[2];
 200                if (num < 0)
 201                        return -EINVAL;
 202                if (num == 0)
 203                        num = 256;
 204                if (num > BR_MAX_PORTS)
 205                        num = BR_MAX_PORTS;
 206
 207                indices = kcalloc(num, sizeof(int), GFP_KERNEL);
 208                if (indices == NULL)
 209                        return -ENOMEM;
 210
 211                get_port_ifindices(br, indices, num);
 212                if (copy_to_user(argp, indices, array_size(num, sizeof(int))))
 213                        num =  -EFAULT;
 214                kfree(indices);
 215                return num;
 216        }
 217
 218        case BRCTL_SET_BRIDGE_FORWARD_DELAY:
 219                if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
 220                        return -EPERM;
 221
 222                ret = br_set_forward_delay(br, args[1]);
 223                break;
 224
 225        case BRCTL_SET_BRIDGE_HELLO_TIME:
 226                if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
 227                        return -EPERM;
 228
 229                ret = br_set_hello_time(br, args[1]);
 230                break;
 231
 232        case BRCTL_SET_BRIDGE_MAX_AGE:
 233                if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
 234                        return -EPERM;
 235
 236                ret = br_set_max_age(br, args[1]);
 237                break;
 238
 239        case BRCTL_SET_AGEING_TIME:
 240                if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
 241                        return -EPERM;
 242
 243                ret = br_set_ageing_time(br, args[1]);
 244                break;
 245
 246        case BRCTL_GET_PORT_INFO:
 247        {
 248                struct __port_info p;
 249                struct net_bridge_port *pt;
 250
 251                rcu_read_lock();
 252                if ((pt = br_get_port(br, args[2])) == NULL) {
 253                        rcu_read_unlock();
 254                        return -EINVAL;
 255                }
 256
 257                memset(&p, 0, sizeof(struct __port_info));
 258                memcpy(&p.designated_root, &pt->designated_root, 8);
 259                memcpy(&p.designated_bridge, &pt->designated_bridge, 8);
 260                p.port_id = pt->port_id;
 261                p.designated_port = pt->designated_port;
 262                p.path_cost = pt->path_cost;
 263                p.designated_cost = pt->designated_cost;
 264                p.state = pt->state;
 265                p.top_change_ack = pt->topology_change_ack;
 266                p.config_pending = pt->config_pending;
 267                p.message_age_timer_value = br_timer_value(&pt->message_age_timer);
 268                p.forward_delay_timer_value = br_timer_value(&pt->forward_delay_timer);
 269                p.hold_timer_value = br_timer_value(&pt->hold_timer);
 270
 271                rcu_read_unlock();
 272
 273                if (copy_to_user(argp, &p, sizeof(p)))
 274                        return -EFAULT;
 275
 276                return 0;
 277        }
 278
 279        case BRCTL_SET_BRIDGE_STP_STATE:
 280                if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
 281                        return -EPERM;
 282
 283                ret = br_stp_set_enabled(br, args[1], NULL);
 284                break;
 285
 286        case BRCTL_SET_BRIDGE_PRIORITY:
 287                if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
 288                        return -EPERM;
 289
 290                br_stp_set_bridge_priority(br, args[1]);
 291                ret = 0;
 292                break;
 293
 294        case BRCTL_SET_PORT_PRIORITY:
 295        {
 296                if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
 297                        return -EPERM;
 298
 299                spin_lock_bh(&br->lock);
 300                if ((p = br_get_port(br, args[1])) == NULL)
 301                        ret = -EINVAL;
 302                else
 303                        ret = br_stp_set_port_priority(p, args[2]);
 304                spin_unlock_bh(&br->lock);
 305                break;
 306        }
 307
 308        case BRCTL_SET_PATH_COST:
 309        {
 310                if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
 311                        return -EPERM;
 312
 313                spin_lock_bh(&br->lock);
 314                if ((p = br_get_port(br, args[1])) == NULL)
 315                        ret = -EINVAL;
 316                else
 317                        ret = br_stp_set_path_cost(p, args[2]);
 318                spin_unlock_bh(&br->lock);
 319                break;
 320        }
 321
 322        case BRCTL_GET_FDB_ENTRIES:
 323                return get_fdb_entries(br, argp, args[2], args[3]);
 324
 325        default:
 326                ret = -EOPNOTSUPP;
 327        }
 328
 329        if (!ret) {
 330                if (p)
 331                        br_ifinfo_notify(RTM_NEWLINK, NULL, p);
 332                else
 333                        netdev_state_change(br->dev);
 334        }
 335
 336        return ret;
 337}
 338
 339static int old_deviceless(struct net *net, void __user *data)
 340{
 341        unsigned long args[3];
 342        void __user *argp;
 343        int ret;
 344
 345        ret = br_dev_read_uargs(args, ARRAY_SIZE(args), &argp, data);
 346        if (ret)
 347                return ret;
 348
 349        switch (args[0]) {
 350        case BRCTL_GET_VERSION:
 351                return BRCTL_VERSION;
 352
 353        case BRCTL_GET_BRIDGES:
 354        {
 355                int *indices;
 356                int ret = 0;
 357
 358                if (args[2] >= 2048)
 359                        return -ENOMEM;
 360                indices = kcalloc(args[2], sizeof(int), GFP_KERNEL);
 361                if (indices == NULL)
 362                        return -ENOMEM;
 363
 364                args[2] = get_bridge_ifindices(net, indices, args[2]);
 365
 366                ret = copy_to_user(argp, indices,
 367                                   array_size(args[2], sizeof(int)))
 368                        ? -EFAULT : args[2];
 369
 370                kfree(indices);
 371                return ret;
 372        }
 373
 374        case BRCTL_ADD_BRIDGE:
 375        case BRCTL_DEL_BRIDGE:
 376        {
 377                char buf[IFNAMSIZ];
 378
 379                if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
 380                        return -EPERM;
 381
 382                if (copy_from_user(buf, argp, IFNAMSIZ))
 383                        return -EFAULT;
 384
 385                buf[IFNAMSIZ-1] = 0;
 386
 387                if (args[0] == BRCTL_ADD_BRIDGE)
 388                        return br_add_bridge(net, buf);
 389
 390                return br_del_bridge(net, buf);
 391        }
 392        }
 393
 394        return -EOPNOTSUPP;
 395}
 396
 397int br_ioctl_stub(struct net *net, struct net_bridge *br, unsigned int cmd,
 398                  struct ifreq *ifr, void __user *uarg)
 399{
 400        int ret = -EOPNOTSUPP;
 401
 402        rtnl_lock();
 403
 404        switch (cmd) {
 405        case SIOCGIFBR:
 406        case SIOCSIFBR:
 407                ret = old_deviceless(net, uarg);
 408                break;
 409        case SIOCBRADDBR:
 410        case SIOCBRDELBR:
 411        {
 412                char buf[IFNAMSIZ];
 413
 414                if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) {
 415                        ret = -EPERM;
 416                        break;
 417                }
 418
 419                if (copy_from_user(buf, uarg, IFNAMSIZ)) {
 420                        ret = -EFAULT;
 421                        break;
 422                }
 423
 424                buf[IFNAMSIZ-1] = 0;
 425                if (cmd == SIOCBRADDBR)
 426                        ret = br_add_bridge(net, buf);
 427                else
 428                        ret = br_del_bridge(net, buf);
 429        }
 430                break;
 431        case SIOCBRADDIF:
 432        case SIOCBRDELIF:
 433                ret = add_del_if(br, ifr->ifr_ifindex, cmd == SIOCBRADDIF);
 434                break;
 435        }
 436
 437        rtnl_unlock();
 438
 439        return ret;
 440}
 441