精华内容
下载资源
问答
  • 添加网桥添加一对虚拟网卡

    千次阅读 2018-11-15 23:17:17
    添加网桥 添加网桥,名称为br0 brctl addbr br0 将网桥关联到网卡ens33 brctl addif br0 ens33 这里如果你是ssh连接,执行完了你可能就会断开连接了,所以最好吧这一步和下面几步连在一起执行这样就不会断开了 brctl ...

    添加网桥

    添加网桥,名称为br0

    brctl addbr br0

    将网桥关联到网卡ens33

    brctl addif br0 ens33
    这里如果你是ssh连接,执行完了你可能就会断开连接了,所以最好吧这一步和下面几步连在一起执行这样就不会断开了

    brctl addif br0 ens33 ;\
    ip addr del dev ens33 192.168.253.134/24 ;\
    ifconfig  br0 192.168.253.134/24 up
    

    查看

    brctl show
    在这里插入图片描述

    删除网卡上的ip

    ip addr del dev ens33 192.168.253.134/24

    将ip配置在br0上

    ifconfig br0 192.168.253.134/24 up

    设置默认网关

    route add default gw 192.168.253.2

    添加虚拟网卡

    虚拟网卡是使用网络底层编程技术实现的一个驱动软件。安装此类程序后主机上会增加一个非真实的网卡,并可以像其它网卡一样进行配置。服务程序可以在应用层打开虚拟网卡,如果应用软件(如网络浏览器)向虚拟网卡发送数据,则服务程序可以读取到该数据。如果服务程序写合适的数据到虚拟网卡,应用软件也可以接收得到。
    简单的说相当于一条数据线,如果你有一个虚拟机/网络命名空间,这时候你可以一头吧数据线查到虚拟机中,另一头放在物理机中,从而实现网络通信,

    具体实现

    第一步:创建一对虚拟网卡
    ip link add vethx.1 type veth peer name vethx.2
    

    查看一下
    在这里插入图片描述

    第二步:网络命名空间

    创建一个网络命名空间,起到隔离网络的作用

    ip netns add netns1
    

    将虚拟网卡vethx2添加到netns1这个网络命名空间中

    ip link set dev vethx.2 netns netns1
    

    在这里插入图片描述
    可以看到vethx.2已经不再物理机上显示了,因为被我们放到网络命名空间中了

    第三步:进入网络命名空间配置vthx.2的ip

    设置ip为10.1.0.3/24

    ip netns exec netns1  ifconfig vethx.2 10.1.0.3/24 up
    

    在这里插入图片描述
    ping一下发现不可达

    第四步:配置vthx.1的ip

    设置ip为10.1.0.4/24

    ifconfig vethx.1 10.1.0.4/24 up
    

    在这里插入图片描述
    可以看到成功ping通了。

    删除虚拟网卡

    ip link delete vethx.1

    删除网络名称空间

    ip netns delete netns1

    展开全文
  • 1 网桥添加br_add_bridge() 1.1 申请并初始化 net_device、net_bridge 1.1.1 初始化网桥br_dev_setup() 1.1.2 网桥操作函数集合 2网桥删除br_del_bridge() 3 网桥端口添加br_add_if() 3.1 网桥端口创建new_nbp...

    目录

    1 网桥添加 br_add_bridge()

    1.1 申请并初始化 net_device、net_bridge

    1.1.1 初始化网桥 br_dev_setup()

    1.1.2 网桥操作函数集合

    2 网桥删除 br_del_bridge()

    3 网桥端口添加 br_add_if()

    3.1 网桥端口创建 new_nbp()

    4 网桥端口删除 br_del_if()

    4.1 删除网桥端口 del_nbp()

    4.1.1 网桥端口销毁 destroy_nbp_rcu()、destroy_nbp()

    4.1.2 网桥端口计数释放判断 kobject_put()

    4.1.3 网桥端口释放动作 kobject_release()

    4.1.4 网桥端口对象释放接口 release_nbp()


    1 网桥添加 br_add_bridge()

    终端命令如下:

    [root@Node_A ~]# brctl addbr  bridge_200 //新建一个网桥,名为“bridge_200”
    

    当用户态执行如上命令,内核将调用接口创建并初始化一个网桥。

    • 调用 alloc_netdev 创建并初始化一个网桥设备与网桥
    • 调用 register_netdev 注册网桥设备
    int br_add_bridge(struct net *net, const char *name)
    {
    	struct net_device *dev;
    	int res;
    
    	dev = alloc_netdev(sizeof(struct net_bridge), name,
    			   br_dev_setup);
    
    	if (!dev)
    		return -ENOMEM;
    
    	dev_net_set(dev, net);
    	dev->rtnl_link_ops = &br_link_ops;
    
    	res = register_netdev(dev);
    	if (res)
    		free_netdev(dev);
    	return res;
    }
    
    #define alloc_netdev(sizeof_priv, name, setup) \
    	alloc_netdev_mqs(sizeof_priv, name, setup, 1, 1)
    
    
    int register_netdev(struct net_device *dev)
    {
    	int err;
    
    	rtnl_lock();
    	err = register_netdevice(dev);
    	rtnl_unlock();
    	return err;
    }

    1.1 申请并初始化 net_device、net_bridge

    /**
     *	alloc_netdev_mqs - allocate network device
     *	@sizeof_priv:	size of private data to allocate space for
     *	@name:		device name format string
     *	@setup:		callback to initialize device
     *	@txqs:		the number of TX subqueues to allocate
     *	@rxqs:		the number of RX subqueues to allocate
     *
     *	Allocates a struct net_device with private data area for driver use
     *	and performs basic initialization.  Also allocates subquue structs
     *	for each queue on the device.
     */
    struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
    		void (*setup)(struct net_device *),
    		unsigned int txqs, unsigned int rxqs)
    {
    	struct net_device *dev;
    	size_t alloc_size;
    	struct net_device *p;
    
    	BUG_ON(strlen(name) >= sizeof(dev->name));
    
    	if (txqs < 1) {
    		pr_err("alloc_netdev: Unable to allocate device with zero queues\n");
    		return NULL;
    	}
    
    #ifdef CONFIG_RPS
    	if (rxqs < 1) {
    		pr_err("alloc_netdev: Unable to allocate device with zero RX queues\n");
    		return NULL;
    	}
    #endif
    
    	alloc_size = sizeof(struct net_device);
    	if (sizeof_priv) {
    		/* ensure 32-byte alignment of private area */
    		alloc_size = ALIGN(alloc_size, NETDEV_ALIGN);
    		alloc_size += sizeof_priv;
    	}
    	/* ensure 32-byte alignment of whole construct */
    	alloc_size += NETDEV_ALIGN - 1;
    
    	p = kzalloc(alloc_size, GFP_KERNEL);
    	if (!p)
    		return NULL;
    
    	dev = PTR_ALIGN(p, NETDEV_ALIGN);
    	dev->padded = (char *)dev - (char *)p;
    
    	dev->pcpu_refcnt = alloc_percpu(int);
    	if (!dev->pcpu_refcnt)
    		goto free_p;
    
    	if (dev_addr_init(dev))
    		goto free_pcpu;
    
    	dev_mc_init(dev);
    	dev_uc_init(dev);
    
    	dev_net_set(dev, &init_net);
    
    	dev->gso_max_size = GSO_MAX_SIZE;
    	dev->gso_max_segs = GSO_MAX_SEGS;
    
    	INIT_LIST_HEAD(&dev->napi_list);
    	INIT_LIST_HEAD(&dev->unreg_list);
    	INIT_LIST_HEAD(&dev->link_watch_list);
    	INIT_LIST_HEAD(&dev->upper_dev_list);
    	dev->priv_flags = IFF_XMIT_DST_RELEASE;
    	setup(dev);//br_dev_setup
    
    	dev->num_tx_queues = txqs;
    	dev->real_num_tx_queues = txqs;
    	if (netif_alloc_netdev_queues(dev))
    		goto free_all;
    
    #ifdef CONFIG_RPS
    	dev->num_rx_queues = rxqs;
    	dev->real_num_rx_queues = rxqs;
    	if (netif_alloc_rx_queues(dev))
    		goto free_all;
    #endif
    
    	strcpy(dev->name, name);
    	dev->group = INIT_NETDEV_GROUP;
    	if (!dev->ethtool_ops)
    		dev->ethtool_ops = &default_ethtool_ops;
    	return dev;
    
    free_all:
    	free_netdev(dev);
    	return NULL;
    
    free_pcpu:
    	free_percpu(dev->pcpu_refcnt);
    	kfree(dev->_tx);
    #ifdef CONFIG_RPS
    	kfree(dev->_rx);
    #endif
    
    free_p:
    	kfree(p);
    	return NULL;
    }
    EXPORT_SYMBOL(alloc_netdev_mqs);

    1.1.1 初始化网桥 br_dev_setup()

    1. 设置网桥操作回调函数集
    2. 初始化br相关的自旋锁与链表
    3. 设置br的优先级为0x8000
    4. 初始化netfilter相关的参数
    void br_dev_setup(struct net_device *dev)
    {
    	struct net_bridge *br = netdev_priv(dev);
    
    	eth_hw_addr_random(dev);
    	ether_setup(dev);
    	
    	/*设置网桥设备的回调函数*/
    	dev->netdev_ops = &br_netdev_ops;
    	dev->destructor = br_dev_free;
    	SET_ETHTOOL_OPS(dev, &br_ethtool_ops);
    	SET_NETDEV_DEVTYPE(dev, &br_type);
    	dev->tx_queue_len = 0;
    	/*设置设备的标签为IFF_EBRIDGE*/
    	dev->priv_flags = IFF_EBRIDGE;
    
    	dev->features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA |
    			NETIF_F_GSO_MASK | NETIF_F_HW_CSUM | NETIF_F_LLTX |
    			NETIF_F_NETNS_LOCAL | NETIF_F_HW_VLAN_CTAG_TX;
    	dev->hw_features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA |
    			   NETIF_F_GSO_MASK | NETIF_F_HW_CSUM |
    			   NETIF_F_HW_VLAN_CTAG_TX;
    
    	br->dev = dev;
    	spin_lock_init(&br->lock);
    	INIT_LIST_HEAD(&br->port_list);
    	spin_lock_init(&br->hash_lock);
    
    	br->bridge_id.prio[0] = 0x80;
    	br->bridge_id.prio[1] = 0x00;
    
    	memcpy(br->group_addr, eth_reserved_addr_base, ETH_ALEN);
    
    	br->stp_enabled = BR_NO_STP;
    	br->group_fwd_mask = BR_GROUPFWD_DEFAULT;
    
    	br->designated_root = br->bridge_id;
    	br->bridge_max_age = br->max_age = 20 * HZ;
    	br->bridge_hello_time = br->hello_time = 2 * HZ;
    	br->bridge_forward_delay = br->forward_delay = 15 * HZ;
    	br->ageing_time = 300 * HZ;
    
    	br_netfilter_rtable_init(br);
    	br_stp_timer_init(br);
    	br_multicast_init(br);
    }

    1.1.2 网桥操作函数集合

    static const struct ethtool_ops br_ethtool_ops = {
    	.get_drvinfo    = br_getinfo,
    	.get_link	= ethtool_op_get_link,
    };
    
    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,//网桥端口的添加、删除
    #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,
    };

    当socket接收到添加或者删除网桥端口的ioctl后,就会调用到函数dev_ioctl,dev_ioctl会调用 dev_ifsioc,而 dev_ifsioc就通过 ops->ndo_do_ioctl,从而调用到br_dev_ioctl

    在该数据结构中,将 ndo_do_ioctl 成员变量赋值为br_dev_ioctl,而br_dev_ioctl主要是处理add br if、del br if 的 ioctl,我们还记得在网桥代码初始化时,有对socket的ioctl进行网桥相关的扩展,实现add br 、del br的ioctl。而此处则实现了add br if 、del br if 的socket ioctl扩展。

    2 网桥删除 br_del_bridge()

      终端命令:

    [root@localhost xxx]# brctl addbr bridge_200
    [root@localhost xxx]# ifconfig bridge_200
    bridge_200 Link encap:Ethernet  HWaddr EE:88:16:9D:4E:8F
              BROADCAST MULTICAST  MTU:1500  Metric:1
              RX packets:0 errors:0 dropped:0 overruns:0 frame:0
              TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:0
              RX bytes:0 (0.0 b)  TX bytes:0 (0.0 b)
    [root@localhost xxx]# ifconfig bridge_200 up
    [root@localhost xxx]# ifconfig bridge_200
    bridge_200 Link encap:Ethernet  HWaddr EE:88:16:9D:4E:8F
              inet6 addr: fe80::ec88:16ff:fe9d:4e8f/64 Scope:Link
              UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
              RX packets:0 errors:0 dropped:0 overruns:0 frame:0
              TX packets:6 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:0
              RX bytes:0 (0.0 b)  TX bytes:468 (468.0 b)
    
    [root@localhost xxx]# brctl delbr bridge_200
    bridge bridge_200 is still up; can't delete it
    [root@localhost xxx]# brctl delbr eth19
    can't delete bridge eth19: Operation not permitted
    [root@localhost xxx]#
    

    该函数实现删除网桥的功能,该函数的逻辑比较简单,主要实现以下两点

    1. 首先判断能否删除网桥(如果设备不是网桥或者网桥设备是up状态时不能删除网桥)
    2. 若符合删除的条件,则调用 br_dev_delete() 进行删除
    int br_del_bridge(struct net *net, const char *name)
    {
    	struct net_device *dev;
    	int ret = 0;
    
    	rtnl_lock();
    	dev = __dev_get_by_name(net, name);
    	if (dev == NULL)
    		ret =  -ENXIO; 	/* Could not find device */
    
    	else if (!(dev->priv_flags & IFF_EBRIDGE)) {
    		/* Attempt to delete non bridge device! */
    		ret = -EPERM;
    	}
    
    	else if (dev->flags & IFF_UP) {
    		/* Not shutdown yet. */
    		ret = -EBUSY;
    	}
    
    	else
    		br_dev_delete(dev, NULL);
    
    	rtnl_unlock();
    	return ret;
    }

    3 网桥端口添加 br_add_if()

    首先我们需要知道,哪些设备不能作为网桥端口不能添加到桥组的设备符合以下几个条件

    • 回环设备不能作为网桥端口
    • 非以太网设备不能作为网桥端口
    • 网桥设备不能作为网桥端口
    • 已经加入到桥组中的设备不能再次加入桥组

      终端命令:

    [root@localhost xxx]# brctl addif bridge_200 eth18
    [root@localhost xxx]# ifconfig bridge_200
    bridge_200 Link encap:Ethernet  HWaddr 00:22:93:75:9C:88
              inet6 addr: fe80::ec88:16ff:fe9d:4e8f/64 Scope:Link
              UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
              RX packets:0 errors:0 dropped:0 overruns:0 frame:0
              TX packets:6 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:0
              RX bytes:0 (0.0 b)  TX bytes:468 (468.0 b)
    
    [root@localhost xxx]# ifconfig eth18
    eth18     Link encap:Ethernet  HWaddr 00:22:93:75:9C:88
              inet6 addr: fe80::222:93ff:fe75:9c88/64 Scope:Link
              UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
              RX packets:67 errors:0 dropped:0 overruns:0 frame:0
              TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:1000
              RX bytes:4020 (3.9 KiB)  TX bytes:600 (600.0 b)
              Interrupt:17 Memory:fbca0000-fbcc0000
    
    [root@localhost xxx]# brctl delif bridge_200 eth18
    [root@localhost xxx]# ifconfig bridge_200
    bridge_200 Link encap:Ethernet  HWaddr 00:00:00:00:00:00
              inet6 addr: fe80::ec88:16ff:fe9d:4e8f/64 Scope:Link
              UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
              RX packets:0 errors:0 dropped:0 overruns:0 frame:0
              TX packets:6 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:0
              RX bytes:0 (0.0 b)  TX bytes:468 (468.0 b)
    
    
    

    br_add_if() 网桥添加端口主要逻辑如下:

    1. 首先判断该设备是否符合加入桥组的条件
    2. 若设备符合条件,则调用new_nbp,生成一个新的网桥端口并进行初始化
    3. 调用dev_set_promiscuity,将该设备设置为混杂模式
    4. 调用kobject_init_and_add,为该网桥端口创建kobject并与网桥的kobject关联
    5. 调用netdev_rx_handler_register(dev, br_handle_frame, p),为该网桥端口注册处理函数
    6. 将该网桥端口加入到网桥的port_list中
    7. 判断设备是否是up状态,若是up状态,则调用br_stp_enable_port,使能网桥端口
    8. 设置网桥设备的mtu
    9. 调用 br_fdb_insert,为该网桥端口建立转发数据库项
    10. 调用kobject与sysfs的接口函数,实现kobject与sysfs之间的关联
    /* called with RTNL */
    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)
    		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;
    
        ....
    	return err;
    }

    3.1 网桥端口创建 new_nbp()

    该函数的功能如下:

    1. 首先获取一个未被使用的端口号,主要使用函数 find_portno
    2. 申请内存
    3. 对端口的br、dev、port_no进行初始化
    /* called with RTNL but without bridge lock */
    static struct net_bridge_port *new_nbp(struct net_bridge *br,
    				       struct net_device *dev)
    {
    	int index;
    	struct net_bridge_port *p;
    
    	index = find_portno(br);
    	if (index < 0)
    		return ERR_PTR(index);
    
    	p = kzalloc(sizeof(*p), GFP_KERNEL);
    	if (p == NULL)
    		return ERR_PTR(-ENOMEM);
    
    	p->br = br;
    	dev_hold(dev);
    	p->dev = dev;
    	p->path_cost = port_cost(dev);
    	p->priority = 0x8000 >> BR_PORT_BITS;
    	p->port_no = index;
    	p->flags = 0;
    	br_init_port(p);
    	p->state = BR_STATE_DISABLED;
    	br_stp_port_timer_init(p);
    	br_multicast_add_port(p);
    
    	return p;
    }

    4 网桥端口删除 br_del_if()

         终端命令见第三章,主要逻辑如下:

    1. 判断端口是否在一个网桥中,若没在桥组中,则直接返回
    2. 调用 del_nbp(),删除一个网桥端口
    /* called with RTNL */
    int br_del_if(struct net_bridge *br, struct net_device *dev)
    {
    	struct net_bridge_port *p;
    	bool changed_addr;
    
    	p = br_port_get_rtnl(dev);
    	if (!p || p->br != br)
    		return -EINVAL;
    
    	/* Since more than one interface can be attached to a bridge,
    	 * there still maybe an alternate path for netconsole to use;
    	 * therefore there is no reason for a NETDEV_RELEASE event.
    	 */
    	del_nbp(p);
    
    	spin_lock_bh(&br->lock);
    	changed_addr = br_stp_recalculate_bridge_id(br);
    	spin_unlock_bh(&br->lock);
    
    	if (changed_addr)
    		call_netdevice_notifiers(NETDEV_CHANGEADDR, br->dev);
    
    	netdev_update_features(br->dev);
    
    	return 0;
    }

    4.1 删除网桥端口 del_nbp()

    该函数主要逻辑如下:

    1. 调用sysfs_remove_link,删除br->ifobj目录下名为name的软链接文件
    2. 调用dev_set_promiscuity,设置网卡的工作模式为普通模式
    3. 调用br_stp_disable_port,设置端口的状态为disable
    4. 调用br_fdb_delete_by_port,删除CAM表中与该port有关的表项
    5. 调用list_del_rcu,将该port端口从网桥的port_list链表中删除
    6. 设置dev->priv_flags为非网桥模式
    7. 从linux系统中删除该port对应的kobject项
    8. 通过call_rcu机制,调用destroy_nbp_rcu,将该网桥端口的内存释放掉
    /* Delete port(interface) from bridge is done in two steps.
     * via RCU. First step, marks device as down. That deletes
     * all the timers and stops new packets from flowing through.
     *
     * Final cleanup doesn't occur until after all CPU's finished
     * processing packets.
     *
     * Protected from multiple admin operations by RTNL mutex
     */
    static void del_nbp(struct net_bridge_port *p)
    {
    	struct net_bridge *br = p->br;
    	struct net_device *dev = p->dev;
    
    	sysfs_remove_link(br->ifobj, p->dev->name);
    
    	dev_set_promiscuity(dev, -1);
    
    	spin_lock_bh(&br->lock);
    	br_stp_disable_port(p);
    	spin_unlock_bh(&br->lock);
    
    	br_ifinfo_notify(RTM_DELLINK, p);
    
    	nbp_vlan_flush(p);
    	br_fdb_delete_by_port(br, p, 1);
    
    	list_del_rcu(&p->list);
    
    	dev->priv_flags &= ~IFF_BRIDGE_PORT;
    
    	netdev_rx_handler_unregister(dev);
    
    	netdev_upper_dev_unlink(dev, br->dev);
    
    	br_multicast_del_port(p);
    
    	kobject_uevent(&p->kobj, KOBJ_REMOVE);
    	kobject_del(&p->kobj);
    
    	br_netpoll_disable(p);
    
    	call_rcu(&p->rcu, destroy_nbp_rcu);
    }

    4.1.1 网桥端口销毁 destroy_nbp_rcu()、destroy_nbp()

    该函数通过call_rcu,会调用destroy_nbp_rcu删除网桥端口,而destroy_nbp_rcu实现以下功能

    1. 通过调用 container_of,获取到桥端口的地址
    2. 调用 destory_nbp 销毁端口

    destroy_nbp,其执行以下操作:

    1. 将该桥端口中的br、dev指针设置为NULL
    2. 递减 dev 的使用计数
    3. 递减 p->kobj 的引用计数
    static void destroy_nbp_rcu(struct rcu_head *head)
    {
    	struct net_bridge_port *p =
    			container_of(head, struct net_bridge_port, rcu);
    	destroy_nbp(p);
    }
    
    static void destroy_nbp(struct net_bridge_port *p)
    {
    	struct net_device *dev = p->dev;
    
    	p->br = NULL;
    	p->dev = NULL;
    	dev_put(dev);
    
    	kobject_put(&p->kobj);
    }
    
    static inline void dev_put(struct net_device *dev)
    {
    	this_cpu_dec(*dev->pcpu_refcnt);
    }

    4.1.2 网桥端口计数释放判断 kobject_put()

    /**
     * kobject_put - decrement refcount for object.
     * @kobj: object.
     *
     * Decrement the refcount, and if 0, call kobject_cleanup().
     */
    void kobject_put(struct kobject *kobj)
    {
    	if (kobj) {
    		if (!kobj->state_initialized)
    			WARN(1, KERN_WARNING "kobject: '%s' (%p): is not "
    			       "initialized, yet kobject_put() is being "
    			       "called.\n", kobject_name(kobj), kobj);
    		kref_put(&kobj->kref, kobject_release);
    	}
    }
    
    static inline int kref_put(struct kref *kref, void (*release)(struct kref *kref))
    {
    	return kref_sub(kref, 1, release);
    }
    
    static inline int kref_sub(struct kref *kref, unsigned int count,
    	     void (*release)(struct kref *kref))
    {
    	WARN_ON(release == NULL);
    
    	if (atomic_sub_and_test((int) count, &kref->refcount)) {
    		release(kref);//kobject_release
    		return 1;
    	}
    	return 0;
    }

    4.1.3 网桥端口释放动作 kobject_release()

    static void kobject_release(struct kref *kref)
    {
    	kobject_cleanup(container_of(kref, struct kobject, kref));
    }
    
    static void kobject_cleanup(struct kobject *kobj)
    {
    	struct kobj_type *t = get_ktype(kobj);
    	const char *name = kobj->name;
    
    	pr_debug("kobject: '%s' (%p): %s\n",
    		 kobject_name(kobj), kobj, __func__);
    
    	if (t && !t->release) //release_nbp
    		pr_debug("kobject: '%s' (%p): does not have a release() "
    			 "function, it is broken and must be fixed.\n",
    			 kobject_name(kobj), kobj);
    
    	/* send "remove" if the caller did not do it but sent "add" */
    	if (kobj->state_add_uevent_sent && !kobj->state_remove_uevent_sent) {
    		pr_debug("kobject: '%s' (%p): auto cleanup 'remove' event\n",
    			 kobject_name(kobj), kobj);
    		kobject_uevent(kobj, KOBJ_REMOVE);
    	}
    
    	/* remove from sysfs if the caller did not do it */
    	if (kobj->state_in_sysfs) {
    		pr_debug("kobject: '%s' (%p): auto cleanup kobject_del\n",
    			 kobject_name(kobj), kobj);
    		kobject_del(kobj);
    	}
    
    	if (t && t->release) {
    		pr_debug("kobject: '%s' (%p): calling ktype release\n",
    			 kobject_name(kobj), kobj);
    		t->release(kobj);//release_nbp()
    	}
    
    	/* free name if we allocated it */
    	if (name) {
    		pr_debug("kobject: '%s': free name\n", name);
    		kfree(name);
    	}
    }
    

    4.1.4 网桥端口对象释放接口 release_nbp()

    struct kobj_type brport_ktype 结构体里包含了 kobject 相关的属性,当 kobject 的引用计数归零后,就会调用 release 函数释放 kobject 占用的内存。
    release_nbp() 的作用就是根据kobj,释放掉一个网桥端口,具体操作如下:

    1. 通过调用container_of,获取kobj所属于的网桥端口
    2. 释放内存
    static struct kobj_type brport_ktype = {
    	#ifdef CONFIG_SYSFS
    		.sysfs_ops =&brport_sysfs_ops,
    	#endif
    		.release = release_nbp,
    };
    
    static void release_nbp(struct kobject *kobj)
    {
    	struct net_bridge_port *p
    		= container_of(kobj, struct net_bridge_port, kobj);
    	kfree(p);
    }

     

    展开全文
  • 网桥接口的混杂模式

    千次阅读 2018-06-29 09:43:59
    网桥接口的混杂模式取决于三个因素,一个是自身的混杂模式设置;第二是vlan过滤是否开启;最后是处于自动状态(auto_port)的子接口的数量。网桥的混杂模式管理函数br_manage_promisc如下:void br_manage_promisc...

    网桥接口的混杂模式取决于三个因素,一个是自身的混杂模式设置;第二是vlan过滤是否开启;最后是处于自动状态(auto_port)的子接口的数量。网桥的混杂模式管理函数br_manage_promisc如下:


    void br_manage_promisc(struct net_bridge *br)
    {
        if ((br->dev->flags & IFF_PROMISC) || !br_vlan_enabled(br))
            set_all = true;
    
        list_for_each_entry(p, &br->port_list, list) {
            if (set_all) {
                br_port_set_promisc(p);
            } else {
                if (br->auto_cnt == 0 ||
                    (br->auto_cnt == 1 && br_auto_port(p)))
                    br_port_clear_promisc(p);
                else
                    br_port_set_promisc(p);
            }
        }
    }


    混杂模式设置



    如下通过命令设置网桥br_2为混杂模式,此时,所有网桥子接口都将处于混杂模式(eth1与eth2 promiscuity mode):

    ip link add br_2 type bridge
    ip link set eth1 master br_2
    ip link set eth2 master br_2


    通过ip命令设置接口的混杂模式:
    ip link set br_2 promisc on

    或者ioctl系统调用设置混杂模式:

    int set_bridge_promiscuity(void)
    {
      sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    
      strncpy(ifr.ifr_name, "br_2", IFNAMSIZ);
      ioctl(sockfd, SIOCGIFFLAGS, &ifr);
    
      ifr.ifr_flags |= IFF_PROMISC;
      ioctl(sockfd, SIOCSIFFLAGS, &ifr);
    }


    VLAN过滤


    如果网桥的VLAN filtering过滤功能没有开启,即使网桥没有设置混杂模式,也需开启所有子接口的混杂模式。

    /sys/devices/virtual/net/br_2/bridge/vlan_filtering


    自动状态接口AUTO_PORT


    内核中对自动状态接口的定义如下,即设置了学习(learning)或者单播泛洪(flood)标志的接口。

    #define BR_AUTO_MASK        (BR_FLOOD | BR_LEARNING)
    #define br_auto_port(p) ((p)->flags & BR_AUTO_MASK)

    可通过bridge或者ip命令设置网桥接口的flood/learning等标志:

    bridge link set dev eth1 flood on/off
    bridge link set dev eth1 learning on/off
    ip link set dev eth1 type bridge_slave flood on/off
    ip link set dev eth1 type bridge_slave learning on/off


    网桥添加网络设备时,新接口的flags赋值为BR_LEARNING与BR_FLOOD标志,即新接口为自动状态接口。


    static struct net_bridge_port *new_nbp(struct net_bridge *br, struct net_device *dev)
    {
    	struct net_bridge_port *p;
        p->flags = BR_LEARNING | BR_FLOOD;
    }


    BR_LEARNING标志,在函数br_handle_frame_finish中判断,用来决定是否使用数据包的源MAC地址更新网桥的FDB转发表。


    int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
    {
        struct net_bridge_port *p = br_port_get_rcu(skb->dev);
        struct net_bridge *br;
    
        br = p->br;
        if (p->flags & BR_LEARNING)
            br_fdb_update(br, p, eth_hdr(skb)->h_source, vid, false);
    }
    

    BR_FLOOD标志用来控制是否在接口上泛洪单播数据包,如果没有设置此标志,不能在此接口泛洪单播包。


    void br_flood(struct net_bridge *br, struct sk_buff *skb, ...)
    {
        struct net_bridge_port *p;
    
        list_for_each_entry_rcu(p, &br->port_list, list) {
            switch (pkt_type) {
            case BR_PKT_UNICAST:
                if (!(p->flags & BR_FLOOD))
                    continue;
                break;
    }

    网桥结构体中保存了自动状态接口的数量auto_cnt,在向网桥添加接口或者删除接口的时候,调用nbp_update_port_count更新自动状态接口的数量。通过遍历网桥的接口列表,获取自动接口数量。


    static void nbp_update_port_count(struct net_bridge *br)
    {
        struct net_bridge_port *p;
        u32 cnt = 0;
    
        list_for_each_entry(p, &br->port_list, list) {
            if (br_auto_port(p))
                cnt++;
        }
        if (br->auto_cnt != cnt) {
            br->auto_cnt = cnt;
            br_manage_promisc(br);
        }
    }


    所谓非自动接口,即此接口不自动学习数据包的源MAC地址,或者不泛洪单播包。对于此类接口,要正常转发报文,必须静态指定其在FDB转发表中的转发表项。由上文br_manage_promisc函数中的条件判断可知,当网桥下都是非自动接口时(auto_cnt=0),关闭混杂模式,因为此时所有接口都已经配置好了转发表项,不需要混杂模式。当只有一个接口时自动接口的时候,从这个接口接收到的数据包无非是要转发到系统剩余的其它接口,而其它接口都是非自动的,这就反向决定了此自动接口的转发路径也被确定下了,此时也不需要混杂模式。


    所以当自动接口大于1时,开启混杂模式。


    内核版本

    linux-4.15


    展开全文
  • 内核获取网络设备的网桥接口

    千次阅读 2018-05-21 12:32:38
    控制路径用户平面的程序brctl下发控制命令,比如setpathcost和setportprio命令以port number为参数下发到内核,内核使用br_get_port函数遍历网桥上的接口列表,最终找到对应的网桥接口。br_get_port函数需要在获得了...

    Linux内核中有两种获取网络设备的桥接口方法,其中一种是在控制路径上;另一种是在数据路径上。


    控制路径


    用户平面的程序brctl下发控制命令,比如setpathcost和setportprio命令以port number为参数下发到内核,内核使用br_get_port函数遍历网桥上的接口列表,最终找到对应的网桥接口。br_get_port函数需要在获得了网桥spinlock锁后调用,防止对port_list操作的不一致发送。


    Usage: brctl [commands]
    commands:
            setpathcost     <bridge> <port> <cost>  set path cost
            setportprio     <bridge> <port> <prio>  set port priority
    
    
    struct net_bridge_port *br_get_port(struct net_bridge *br, u16 port_no)
    {
        struct net_bridge_port *p;
    
        list_for_each_entry_rcu(p, &br->port_list, list) {
            if (p->port_no == port_no)
                return p;
        }
    }


    数据路径


    在数据路径上,调用函数br_port_get_rcu获取对应网桥接口,效率高于控制路径上的br_get_port函数。首先在添加桥接口时(br_add_if),调用函数(netdev_rx_handler_register)注册rx_handler,将网桥接口赋值给rx_handler_data变量,在数据路径上,比如br_handle_frame_finish中,使用br_port_get_rcu可直接获得网桥接口结构体。


    static inline struct net_bridge_port *br_port_get_rcu(const struct net_device *dev)
    {
        return rcu_dereference(dev->rx_handler_data);
    }
    
    int netdev_rx_handler_register(struct net_device *dev,
                       rx_handler_func_t *rx_handler,
                       void *rx_handler_data)
    {
        rcu_assign_pointer(dev->rx_handler_data, rx_handler_data);
        rcu_assign_pointer(dev->rx_handler, rx_handler);
    }
    int br_add_if(struct net_bridge *br, struct net_device *dev)
    {
    	struct net_bridge_port *p;
    	
        p = new_nbp(br, dev);
        err = netdev_rx_handler_register(dev, br_handle_frame, p);
    }


    内核版本

    Linux-3.10.0



    展开全文
  • 网桥添加端口——br_add_if(三)

    千次阅读 2017-07-21 15:24:42
    brctrl addif br0 eth1 这个命令的作用是在br0上添加一个eth1的接口,内核实现的相关函数是br_add_if. 那么如何调用到这个接口呢? 上节我们在将添加一个桥设备的时候,有一个参数br_netdev_ops,我们当时说这里面...
  • 网桥接口非混杂模式下数据包转发

    千次阅读 2018-07-02 17:37:43
    网桥接口处在非混杂模式下,只能接收目的MAC地址为自身的数据包,也就是说如果数据包的目的MAC为其它地址,将会被丢弃掉。对于单网口的设备这样没有问题,但是对于存在多个网卡的交换设备,如果从一个网口接收到的...
  • 1、网桥添加及相关的函数     br_add_bridge,其函数实现以下功能: 1、调用new_bridge_dev创建并初始化一个网桥设备与网桥 2、调用register_netdevice注册网桥设备 3、调用br_sysfs_addbr增加sysfs相关的功能,...
  • tunctl添加虚拟网卡TUN/TAP与brctl添加网桥
  • 网桥

    2019-04-02 10:01:20
    把一套机器上的若干个网络接口 “连接” 起来,其结果是,其中一个网口收到的报文会被复制给其他网口并发送出去。以使得网口之间的报文能够互相转发。网桥就是这样一个设备,它有若干个网口,并且这些网口是桥接起来...
  • 高级网络配置 本次博文主要介绍网络桥接、bond网络接口、Team网络接口的功能演示。...addbr 添加网桥 delbr 删除网桥 addif 添加网桥连接 delif 删除网桥连接 初始化的时候是没有br0的 [root@serv
  • 网桥、bond和team网络接口

    千次阅读 2017-08-12 10:06:21
    一、网桥 网桥就是网络桥接,用来连接不同的网段 可以使虚拟机的网卡直接连接到真机的真实网卡,使上网速度更快 1.网桥的配置 需要配置两个主配置文件,如图 /etc/sysconfig/network-scripts/ifcfg-enp0s25 ...
  • 一、Bond网络接口 (一)概述 Bonding就是把多个物理网络接口绑定到一起,使它们就像一个网络接口那样运行。通过Bonding技术,可以实现网口冗余,负载均衡,从而达到高可用高可靠的目的。 那在传输数据包的时候,...
  • Linux网桥

    2017-08-14 06:39:46
    网桥基础命令brctl addbr br0 ##添加br...ifconfig br0 ip netmask netmask ##网桥添加ip brctl addif br0 eth0 ##添加网卡 ifconfig br0 down ##关闭网桥 brctl delif br0 eth0 ##删除br0的eth0网卡 brctl delbr b
  • docker网桥

    2017-02-27 15:41:09
    命令brctl show可以查看存在的网桥的信息,可以看到该网桥上还没有接口,这是因为还没有创建容器。docker0网桥接口默认是以veth开头的字符串。创建完新的容器,会被分配一个该网段可用的IP地址,网关默认就...
  • 虚拟机可以绑定若干个以太网接口设备,从而将他们桥接起来。网桥设备br0绑定eth0,br0作为一个网桥,同时也是虚拟机的网络设备,它既可以用作网桥的管理端口,也可以作为网桥所连接局域网的网关,视具体需求而定。 ...
  • 网桥原理

    千次阅读 2017-08-07 15:14:57
    网桥原原理
  • linux网桥

    千次阅读 2014-09-12 18:41:43
    linux网桥linux网桥  linux网桥是一种以同种协议连接两个局域网的产品。可以把它看成是一个决定发送的包是到自己的局域网还是到另一个局域网的设备。linux网桥检查局域网上所有的包,在同一个局域网中传送它...
  • 虚拟网桥

    千次阅读 2018-08-11 16:18:37
    1.虚拟网桥与NAT网桥区别 1)bridged networking(桥接模式) 在这种模式下,VMWare虚拟出来的操作系统就像是局域网中的一台独立的主机,它可以访问网内任何一台机器。 在桥接模式下,需要手工为虚拟系统配置IP...
  • 为了使用网桥而关闭桌面 Linux 上的 NetworkManager 显然是不明智的。nmcli 可以创建一个永久的网桥而不需要编辑任何文件。-- Vivek Gite有用的原文链接请访问文末的“原文链接”获得可点击的文内链接、全尺寸原图和...
  • Linux网桥配置

    2018-04-08 14:35:00
    CentOS:1、配置临时网桥,重启后风格配置丢失[root@CentOS ~]# yum -y install bridge-utils[root@CentOS ~]# brctl addbr br0 #创建网桥逻辑接口[root@CentOS ~]# brctl addif br0 eth0 #将eth0网卡添加网桥中...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 14,128
精华内容 5,651
关键字:

网桥添加接口