zoukankan      html  css  js  c++  java
  • client模式下对应接口加入桥接出错

    client模式下,响应的接口wlan0 加入桥接时出现如下错误:

    root@root:~# brctl addif br-lan wlan0
    brctl: bridge br-lan: Operation not supported。

    查看相应busybox代码(brctl.c),函数为 brctl_main,

      strncpy_IFNAMSIZ(ifr.ifr_name, br);
      if (key == ARG_addif || key == ARG_delif) { /* addif or delif */
         brif = *argv;
         ifr.ifr_ifindex = if_nametoindex(brif);
        if (!ifr.ifr_ifindex) {
          bb_perror_msg_and_die("iface %s", brif);
        }
        ioctl_or_perror_and_die(fd,
             key == ARG_addif ? SIOCBRADDIF : SIOCBRDELIF,  &ifr, "bridge %s", br);
         goto done_next_argv;
      }

    得到addif 和 delif 相应的ioctl命令码为   SIOCBRADDIF  和  SIOCBRDELIF。

    内核中对应命令字的定义:
    include/linux/sockios.h:#define SIOCBRADDIF     0x89a2         /* add interface to bridge,十进制为35234

    查找内核中响应的代码, 此处内核版本为3.10.14。

     * Perform the SIOCxIFxxx calls, inside rtnl_lock()
     */
    static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd)
    {
     switch (cmd) {
      /*
      * Unknown or private ioctl
      */
       default:
      if ((cmd >= SIOCDEVPRIVATE &&
          cmd <= SIOCDEVPRIVATE + 15) ||
          cmd == SIOCBONDENSLAVE ||
          cmd == SIOCBONDRELEASE ||
          cmd == SIOCBONDSETHWADDR ||
          cmd == SIOCBONDSLAVEINFOQUERY ||
          cmd == SIOCBONDINFOQUERY ||
          cmd == SIOCBONDCHANGEACTIVE ||
          cmd == SIOCGMIIPHY ||
          cmd == SIOCGMIIREG ||
          cmd == SIOCSMIIREG ||
          cmd == SIOCBRADDIF ||      // 添加桥接接口
          cmd == SIOCBRDELIF ||      // 删除桥接接口
          cmd == SIOCSHWTSTAMP ||
          cmd == SIOCWANDEV) {
             err = -EOPNOTSUPP;
             if (ops->ndo_do_ioctl) {
                if (netif_device_present(dev))
                   err = ops->ndo_do_ioctl(dev, ifr, cmd);  // 调用回调函数
                else
                   err = -ENODEV;
             }
       } else
         err = -EINVAL;
      }
    }

     对应的回调函数位于 /net/bridge/br_device.c中

    static const struct net_device_ops br_netdev_ops = {
     .ndo_open     = br_dev_open,
     .ndo_stop     = br_dev_stop,
     .ndo_init    = br_dev_init,
     .ndo_start_xmit    = br_dev_xmit,
     .ndo_get_stats64  = br_get_stats64,
     .ndo_set_mac_address = br_set_mac_address,
     .ndo_set_rx_mode   = br_dev_set_multicast_list,
     .ndo_change_mtu    = br_change_mtu,
     .ndo_do_ioctl    = br_dev_ioctl,    // 此处对应增加(addif)和删除(delif)的回调
    #ifdef CONFIG_NET_POLL_CONTROLLER
     .ndo_netpoll_setup   = br_netpoll_setup,
     .ndo_netpoll_cleanup = br_netpoll_cleanup,
     .ndo_poll_controller = br_poll_controller,
    #endif
     .ndo_add_slave    = br_add_slave,
     .ndo_del_slave    = br_del_slave,
     .ndo_fix_features       = br_fix_features,
     .ndo_fdb_add    = br_fdb_add,
     .ndo_fdb_del    = br_fdb_delete,
     .ndo_fdb_dump    = br_fdb_dump,
     .ndo_bridge_getlink   = br_getlink,
     .ndo_bridge_setlink   = br_setlink,
     .ndo_bridge_dellink   = br_dellink,
    };

    查看回调函数如下:

    int br_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
    {
     struct net_bridge *br = netdev_priv(dev);
     
     switch(cmd) {

       // 旧式的处理方式
       case SIOCDEVPRIVATE:
          return old_dev_ioctl(dev, rq, cmd);

       // 实现如下:
       case SIOCBRADDIF:
       case SIOCBRDELIF:
          return add_del_if(br, rq->ifr_ifindex, cmd == SIOCBRADDIF);

       }

      br_debug(br, "Bridge does not support ioctl 0x%x ", cmd);
      return -EOPNOTSUPP;
    }

    位于 /net/bridge/br_ioctl.c中
    static int add_del_if(struct net_bridge *br, int ifindex, int isadd)
    {
     struct net *net = dev_net(br->dev);
     struct net_device *dev;
     int ret;

     if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
      return -EPERM;

     dev = __dev_get_by_index(net, ifindex);
     if (dev == NULL)
      return -EINVAL;

     if (isadd)
      ret = br_add_if(br, dev);   // 添加桥接接口
     else
      ret = br_del_if(br, dev);   // 删除桥接接口

     return ret;
    }

    位于 /net/bridge/br_if.c中
    int br_add_if(struct net_bridge *br, struct net_device *dev)
    {
     struct net_bridge_port *p;
     int err = 0;
     bool changed_addr;

     /* Don't allow bridging non-ethernet like devices */
     if ((dev->flags & IFF_LOOPBACK) ||
         dev->type != ARPHRD_ETHER || dev->addr_len != ETH_ALEN ||
         !is_valid_ether_addr(dev->dev_addr))
      return -EINVAL;

     /* No bridging of bridges */
     if (dev->netdev_ops->ndo_start_xmit == br_dev_xmit)
      return -ELOOP;

     /* Device is already being bridged */
     if (br_port_exists(dev))
      return -EBUSY;

     /* No bridging devices that dislike that (e.g. wireless) */
     if (dev->priv_flags & IFF_DONT_BRIDGE)     //  wlan0加入桥接时在此处遇到问题,退出
        return -EOPNOTSUPP;

     p = new_nbp(br, dev);
     if (IS_ERR(p))
      return PTR_ERR(p);

     call_netdevice_notifiers(NETDEV_JOIN, dev);

     err = dev_set_promiscuity(dev, 1);
     if (err)
      goto put_back;

     err = kobject_init_and_add(&p->kobj, &brport_ktype, &(dev->dev.kobj),
           SYSFS_BRIDGE_PORT_ATTR);
     if (err)
      goto err1;

     err = br_sysfs_addif(p);
     if (err)
      goto err2;

     if (br_netpoll_info(br) && ((err = br_netpoll_enable(p, GFP_KERNEL))))
      goto err3;

     err = netdev_master_upper_dev_link(dev, br->dev);
     if (err)
      goto err4;

     err = netdev_rx_handler_register(dev, br_handle_frame, p);
     if (err)
      goto err5;

     dev->priv_flags |= IFF_BRIDGE_PORT;

     dev_disable_lro(dev);

     list_add_rcu(&p->list, &br->port_list);

     netdev_update_features(br->dev);

     spin_lock_bh(&br->lock);
     changed_addr = br_stp_recalculate_bridge_id(br);

     if (netif_running(dev) && netif_oper_up(dev) &&
         (br->dev->flags & IFF_UP))
      br_stp_enable_port(p);
     spin_unlock_bh(&br->lock);

     br_ifinfo_notify(RTM_NEWLINK, p);

     if (changed_addr)
      call_netdevice_notifiers(NETDEV_CHANGEADDR, br->dev);

     dev_set_mtu(br->dev, br_min_mtu(br));

     if (br_fdb_insert(br, p, dev->dev_addr, 0))
      netdev_err(dev, "failed insert local address bridge forwarding table ");

     kobject_uevent(&p->kobj, KOBJ_ADD);

     return 0;

    err5:
     netdev_upper_dev_unlink(dev, br->dev);
    err4:
     br_netpoll_disable(p);
    err3:
     sysfs_remove_link(br->ifobj, p->dev->name);
    err2:
     kobject_put(&p->kobj);
     p = NULL; /* kobject_put frees */
    err1:
     dev_set_promiscuity(dev, -1);
    put_back:
     dev_put(dev);
     kfree(p);
     return err;
    }

    追溯出问题的地方,查找 IFF_DONT_BRIDGE 标志置位的地方,找到如下:

    static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
          unsigned long state, void *ndev)
    {

       .......
     switch (state) {
      case NETDEV_REGISTER:   // 注册设备
        if ((wdev->iftype == NL80211_IFTYPE_STATION ||
          wdev->iftype == NL80211_IFTYPE_P2P_CLIENT ||
          wdev->iftype == NL80211_IFTYPE_ADHOC) && !wdev->use_4addr)
         dev->priv_flags |= IFF_DONT_BRIDGE;   
        break;
     }

     .........

    从以上红色部分可以看出,当设备为client或者adhoc以及wds时,对应的无线接口是无法加入到桥接中去的。

    在br_add_if 中判断标志位出错后,返回值为EOPNOTSUPP,定义如下:

    #define EOPNOTSUPP  45 /* Op not supported on transport endpoint */ 

    跟串口中配置出错时打印相符。

  • 相关阅读:
    剑指Offer——构建乘积数组
    剑指Offer——把二叉树打印成多行
    剑指Offer——二叉树的下一个结点
    剑指Offer——二叉搜索树与双向链表
    剑指Offer——二叉搜索树的后序遍历序列
    LeetCode——Construct Binary Tree from Inorder and Postorder Traversal
    LeetCode——Construct Binary Tree from Preorder and Inorder Traversal
    剑指Offer——重建二叉树2
    C++ STL基本容器的使用
    mysql中模糊查询的四种用法介绍
  • 原文地址:https://www.cnblogs.com/rohens-hbg/p/6515193.html
Copyright © 2011-2022 走看看