网桥的概念

网桥就是将桥一端的网络数据转发到桥的另一端的设备,这个转发是双向的。网桥和交换机是同一种设备。

网桥工作在数据链路层上。

网桥所连接的网络需要在同一网段内。

网桥在Linux中的实现

Linux中的网桥是一种虚拟设备。通常我们将一个或几个真实的设备(网卡)绑定到网桥设备上,网桥设备将绑定在它上面的设备视为端口port,

从而统一管理所有从设备。可以为网桥配置一个IP地址,而它的MAC地址则是它管理的所有从设备中最小的那个MAC地址。

绑定在网桥上的从设备不再需要IP地址及MAC,且他们被设置为可以接受任何数据包(设置网卡为混杂模式),然后统一交给网桥设备来决定数据包的去向:接受、转发、丢弃。

网桥对于Linux内核而言就是一个普通的网卡设备,当内核要发送数据时会调用网桥向内核注册的网卡驱动程序。然后在网桥内部决策由哪个从设备完成数据包的实际发送。

图2 Linux中网桥设备上的数据传输

网桥设备的初始化

网桥设备的创建

Linux中可以使用brctl命令来管理网桥设备,当创建一个新的网桥设备时的主要函数调用关系如下(net/bridge/br_if.c):

br_add_bridge ==> new_bridge_dev ==> br_dev_setup

在br_dev_setup函数中注册一个net_device_ops,这就是网桥设备对上层的接口。

static const struct net_device_ops br_netdev_ops = {

.ndo_open = br_dev_open,

.ndo_stop = br_dev_stop,

.ndo_start_xmit = br_dev_xmit,

.ndo_get_stats64 = br_get_stats64,

.ndo_set_mac_address = br_set_mac_address,

.ndo_set_multicast_list = 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}

网桥设备下添加从设备

网桥设备创建完成后需要在其下面添加从设备才能使网桥设备正常工作起来,添加从设备也可以使用brctl命令完成。

添加从设备的主要任务在br_add_if函数中完成。其中最重要的两个任务是调用dev_set_promiscuity函数设置从设备为混杂模式。

然后调用netdev_rx_handler_register函数将从设备的dev->rx_handler_data实例赋值为br_handle_frame函数。

这就实现了将从设备接受到的所有数据包转发给网桥设备的功能。

int netdev_rx_handler_register(struct net_device *dev,

rx_handler_func_t *rx_handler,

void *rx_handler_data)

{

ASSERT_RTNL();

if (dev->rx_handler)

return -EBUSY;

rcu_assign_pointer(dev->rx_handler_data, rx_handler_data);

rcu_assign_pointer(dev->rx_handler, rx_handler);

return 0;

}

网桥的数据发送

前面说过网桥设备对于内核来说就是一个普通的网卡,当有数据需要发送时内核调用函数指针ndo_start_xmit,

这样就进入到了网桥设备向内核注册的br_dev_xmit函数中。在此函数中首先检查数据包是否是广播数据包,

如果是则将数据包下发给所有端口(这里的端口指网桥下面的从设备)。如果不是则调用__br_fdb_get函数查找需要下发的端口,

如果找到则下发给指定端口,如果没有找到则下发给所有端口。

netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)

{

···

rcu_read_lock();

/* 检查数据包是否是广播数据包 */

if (is_multicast_ether_addr(dest)) {

if (unlikely(netpoll_tx_running(dev))) {

br_flood_deliver(br, skb);

goto out;

}

if (br_multicast_rcv(br, NULL, skb)) {

kfree_skb(skb);

goto out;

}

mdst = br_mdb_get(br, skb);

if (mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb))

br_multicast_deliver(mdst, skb); /* 多播数据包 */

else

br_flood_deliver(br, skb); /* 广播数据包 */

} /* 查找端口 */

else if ((dst = __br_fdb_get(br, dest)) != NULL)

br_deliver(dst->dst, skb); /* 下发给指定端口 */

else

br_flood_deliver(br, skb); /* 广播数据 */

out:

rcu_read_unlock();

return NETDEV_TX_OK;

}

网桥的数据接收

数据链路层的所有数据包都会经过__netif_receive_skb函数,

在此函数中会调用rcu_dereference函数解引用skb->dev->rx_handler函数指针,

这个函数指针正好是前面说过的在网桥设备下添加从设备时将它赋值为br_handle_frame函数。这样就将数据包交给了网桥设备。

static int __netif_receive_skb(struct sk_buff *skb)

{

···

/* Handle special case of bridge or macvlan */

rx_handler = rcu_dereference(skb->dev->rx_handler);

if (rx_handler) {

if (pt_prev) {

ret = deliver_skb(skb, pt_prev, orig_dev);

pt_prev = NULL;

}

skb = rx_handler(skb);

if (!skb)

goto out;

}

···

}

在br_handle_frame函数中首先检查数据包的合法性,如果有问题就直接丢掉。然后调用br_handle_frame_finish函数

在此函数中决定数据包的前送或者上传给内核。如果是本地数据包则调用br_pass_frame_up函数回到__netif_receive_skb函数中继续处理。

如果是前送数据包则调用br_forward函数,最终在br_dev_queue_push_xmit函数中调用dev_queue_xmit函数下发给指定端口将数据包发送出去。

int br_handle_frame_finish(struct sk_buff *skb)

{

const unsigned char *dest = eth_hdr(skb)->h_dest;

struct net_bridge_port *p = br_port_get_rcu(skb->dev);

struct net_bridge *br;

struct net_bridge_fdb_entry *dst;

struct net_bridge_mdb_entry *mdst;

struct sk_buff *skb2;

if (!p || p->state == BR_STATE_DISABLED)

goto drop;

/* insert into forwarding database after filtering to avoid spoofing */

br = p->br;

br_fdb_update(br, p, eth_hdr(skb)->h_source);

if (is_multicast_ether_addr(dest) &&

br_multicast_rcv(br, p, skb))

goto drop;

if (p->state == BR_STATE_LEARNING)

goto drop;

BR_INPUT_SKB_CB(skb)->brdev = br->dev;

/* The packet skb2 goes to the local host (NULL to skip). */

skb2 = NULL;

if (br->dev->flags & IFF_PROMISC)

skb2 = skb;

dst = NULL;

if (is_multicast_ether_addr(dest)) {

mdst = br_mdb_get(br, skb);

if (mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) {

if ((mdst && !hlist_unhashed(&mdst->mglist)) ||

br_multicast_is_router(br))

skb2 = skb;

br_multicast_forward(mdst, skb, skb2);

skb = NULL;

if (!skb2)

goto out;

} else

skb2 = skb;

br->dev->stats.multicast++;

} else if ((dst = __br_fdb_get(br, dest)) && dst->is_local) {

skb2 = skb;

/* Do not forward the packet since it's local. */

skb = NULL;

}

/* 前送数据包 */

if (skb) {

if (dst)

br_forward(dst->dst, skb, skb2);

else

br_flood_forward(br, skb, skb2);

}

/* 本地数据包上传 */

if (skb2)

return br_pass_frame_up(skb2);

out:

return 0;

drop:

kfree_skb(skb);

goto out;

}

参考

《深入理解LINUX网络技术内幕》

基于Linux2.6.36内核分析

linux bridge 添加fdb,Linux协议栈--网桥设备的实现相关推荐

  1. linux bridge 添加fdb,bridge fdb 与vxlan

    10.10.18.214节点上 2: eth0@if6: mtu 1500 qdisc noqueue state UP group default qlen 1000link/ether 96:43 ...

  2. LInux Bridge(FDB表)

    Linux Bridge 基于 MAC 地址来转发包, MAC 地址表是 Linux Bridge 的核心数据结构,在 br_fdb.c 中定义了一组操作 MAC 地址表的接口函数. MAC 地址表的 ...

  3. linux系添加路由,Linux添加路由的两种方法

    Linux中增加软路由的两种方法 第一种: route add -net 172.16.6.0 netmask 255.255.255.0 gw 172.16.2.254 dev eth0 /* 增加 ...

  4. linux lvm添加磁盘,Linux下添加磁盘创建lvm分区

    shell> fdisk /dev/xvdb #### 选择磁盘 Command (m for help): m #### 帮助 Command action a toggle a bootab ...

  5. linux命令 添加用户,Linux添加用户命令

    Linux是一个多用户.多任务操作系统.多用户是指允许多人在Linux中创建独立的账户来确保用户个人数据的安全性.独立性.因此在Linux中添加用户就是系统管理员的日常基本工作了.其实添加用户命令也是 ...

  6. linux twiki 添加权限,Linux安装TWiki

    听说twiki是一个很好的管理知识的工具,今天我们就来安装体验一下. 1.安装apache2 apt-get install apache2或者yum install httpd 2.下载twiki ...

  7. linux如何添加route,Linux主机添加路由 route(示例代码)

    近期小乱我使用ansible批量部署设备接入到日志审计系统时,发现执行脚本后,日志服务器端仍未收到Linux主机上传的syslog:排查后发现故障原因是主机的路由未放通. 在主机上添加路由,指令比较简 ...

  8. linux界面添加地址,Linux系统下图形界面更改IP地址

    1.打开终端的命令模式: 点击左上角的application 然后点击System Tools下拉菜单,再点击其展开的下拉菜单Terminal,这样就打开一个命令模式了. 2.查找所要使用的命令模块的 ...

  9. linux怎么添加工作组,linux 用户与工作组

    在linux 里面,用户的编号UID ,也就是用户的ID号.工作组的编号为GID 也就是工作组的ID 号 . 1.用户的分类 超级用户:root用户,系统安装过程中自动创建,UID 为0. 普通用户: ...

  10. linux convert 添加文字,Linux convert命令有什么用

    Linux convert命令有什么用? Linux强大的图片处理功能 强大的convert命令---介绍他的主要原因也是应为编程语言在linux下都可以调用使用 convent命令可以对图片进行各种 ...

最新文章

  1. 程序员基本功09 线性表
  2. 路径还原(求两个点之间最短距离的路径)
  3. Centos7开放及查看端口
  4. 雨松MOMO《Unity 3D游戏开发》源码公布
  5. C#基础知识回顾整理
  6. r4烧录卡内核安装_R4烧录卡NDS内核,绝对可用
  7. power supply surges detected
  8. python实现逻辑回归算法
  9. Logistic Regression - IBM 员工离职预测
  10. 手机IMEI串码获取
  11. 关于公司要不要设立测试的讨论
  12. 啦百茁仲尾芬八敌直就乖虏举删捅
  13. OSGi 框架的组件运行机制
  14. 【数据结构】算法的时间复杂度和空间复杂度解析
  15. EasyBPM进销存之物料管理
  16. item_search_img-按图搜索1688商品(拍立淘)接口的接入参数说明
  17. 小学计算机打字基础知识教案绿色圃,小学信息技术公开课教案智能ABC输入法教学设计与反思...
  18. 网管笔记之良好习惯的养成
  19. SpringMVC IP权限设计
  20. 用STK导入段时间TLE数据

热门文章

  1. open modelica RLC仿真
  2. 中职学校计算机课程标准,中等职业学校课程标准发布
  3. RCC_APB2Periph_AFIO--复用IO时钟的使用
  4. Renascence架构介绍——文件夹
  5. HbuilderX 运行到小程序模拟器
  6. MySQL中round函数
  7. android webview最新版下载,Android WebView 支持文件下载的几种方式
  8. 女暴徒BeHype:MINT和Raffle抽奖规则及答疑
  9. PS人像修图,通道磨皮、高低频磨皮、滤镜磨皮、人像精修
  10. excel--text(双坐标图表)