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\n", 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\n");

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 */

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

client模式下对应接口加入桥接出错的更多相关文章

  1. Apache Spark技术实战之8:Standalone部署模式下的临时文件清理

    未经本人同意严禁转载,徽沪一郎. 概要 在Standalone部署模式下,Spark运行过程中会创建哪些临时性目录及文件,这些临时目录和文件又是在什么时候被清理,本文将就这些问题做深入细致的解答. 从 ...

  2. Java虚拟机6:内存溢出和内存泄露、并行和并发、Minor GC和Full GC、Client模式和Server模式的区别

    前言 之前的文章尤其是讲解GC的时候提到了很多的概念,比如内存溢出和内存泄露.并行与并发.Client模式和Server模式.Minor GC和Full GC,本文详细讲解下这些概念的区别. 内存溢出 ...

  3. Apache Spark技术实战之6 --Standalone部署模式下的临时文件清理

    问题导读 1.在Standalone部署模式下,Spark运行过程中会创建哪些临时性目录及文件? 2.在Standalone部署模式下分为几种模式? 3.在client模式和cluster模式下有什么 ...

  4. Java虚拟机10:Client模式和Server模式的区别

    部分商用虚拟机中,Java程序最初是通过解释器对.class文件进行解释执行的,当虚拟机发现某个方法或代码块运行地特别频繁的时候,就会把这些代码认定为热点代码Hot Spot Code(这也是我们使用 ...

  5. spark on yarn模式下内存资源管理(笔记2)

    1.spark 2.2内存占用计算公式 https://blog.csdn.net/lingbo229/article/details/80914283 2.spark on yarn内存分配** 本 ...

  6. 嵌入式以太网模块的TCP Client模式说明

    嵌入式以太网模块采用TTL电平串口,支持TCP Server,TCP Client,UDP Slave,UDP Master,TCP-ZSD,UDP-ZSD多种通信协议,TCP服务器模式支持多连接,可 ...

  7. 虚拟机Linux桥接模式下设置静态IP

    之前一直使用NAT模式,测试时android端远程访问虚拟机的mysql时发现无法连接,但是访问同学拷过来的虚拟机Linux的mysql却成功了,想了下原因是他设置的桥接模式.关于两种模式的区别,网上 ...

  8. 业务类接口在TCP,HTTP,BLL模式下的实例 设计模式混搭 附源码一份

    业务类接口在TCP,HTTP,BLL模式下的实例 设计模式混搭 附源码一份 WinForm酒店管理软件--框架这篇随笔可以说是我写的最被大家争议的随笔,一度是支持和反对是一样的多.大家对我做的这个行业 ...

  9. c# 调试模式下Swaggerf附加接口参数

    c# 调试模式下Swaggerf附加接口参数,如:每个接口Header中附加参数appId 1.新增过滤器: public class GlobalHttpHeaderFilter : IOperat ...

随机推荐

  1. Centos7下安装python3

    1. 安装依赖环境 # yum -y install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline- ...

  2. [HAOI2015]按位或(min-max容斥,FWT,FMT)

    题目链接:洛谷 题目大意:给定正整数 $n$.一开始有一个数字 $0$,然后每一秒,都有 $p_i$ 的概率获得 $i$ 这个数 $(0\le i< 2^n)$.一秒恰好会获得一个数.每获得一个 ...

  3. Python数据类型(列表和元组)

    1.3 List(列表) 列表由一系列按特定顺序排列的元素组成. 在Python中,用方括号[ ]来表示列表,并用逗号来分隔其中的元素. 1.3.1 访问列表元素 在Python中,第一个列表元素的索 ...

  4. 【洛谷P1379】八数码难题 状压bfs

    对于这道题来说,每个时刻的状态是整个棋盘所有棋子的位置,即:任何一个棋子位置发生了移动,都会使得状态转移. 因此,需要采取将整个状态作为广搜的搜索对象,进行状态压缩.采用哈希得到每个状态的对应的数值, ...

  5. Java: |(或运算) 与 多选判断

    今天需要在程序中做一个多选判断,突然想起以前经常遇到的 x |= y | z; 这样的,我也明白这个是多选的用意,但为什么能达到我们希望的多选操作,我还真的没去研究过. 今天早上,百度了一下,搜索到了 ...

  6. chage命令

    chage命令是用来修改帐号和密码的有效期限. 语法 chage [选项] 用户名 选项 -m:密码可更改的最小天数.为零时代表任何时候都可以更改密码. -M:密码保持有效的最大天数. -w:用户密码 ...

  7. 搭建Spring4+Spring MVC web工程的最佳实践

    Spring是个非常非常非常优秀的java框架,主要是用它的IOC容器帮我们依赖注入和管理一些程序中的Bean组件,实现低耦合关联,最终提高系统可扩展性和可维护性,用它来辅助我们构建web工程将会感觉 ...

  8. 5个强大的Java分布式缓存框架

    在开发中大型Java软件项目时,很多Java架构师都会遇到数据库读写瓶颈,如果你在系统架构时并没有将缓存策略考虑进去,或者并没有选择更优的缓存策略,那么到时候重构起来将会是一个噩梦.本文主要是分享了5 ...

  9. DataSet in Machine Learning

    一.UCI Wine dataset:https://archive.ics.uci.edu/ml/datasets/Wine,包含178个样本,每个样本包含13个与酒的化学特性的特征,标签有1,2, ...

  10. Scala进阶之路-Scala高级语法之隐式(implicit)详解

    Scala进阶之路-Scala高级语法之隐式(implicit)详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 我们调用别人的框架,发现少了一些方法,需要添加,但是让别人为你一 ...