今天处理网桥的STP的问题遇到了麻烦,对这个东东理论的倒是看了不少,没有真真学习到它的源理,来看Linux的实现,手头没有资料,看了两个钟头,只把网桥的框架结构看完,所以想先贴出来,希望有研究这块的大哥们讨论,继续把它写完,好好学习一下:

版本:Linux 2.4.18

一、调用

在src/net/core/dev.c的软中断函数static void net_rx_action(struct softirq_action *h)中:

line 1479

#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)

if (skb->dev->br_port != NULL &&

br_handle_frame_hook != NULL) {

handle_bridge(skb, pt_prev);

dev_put(rx_dev);

continue;

}

#endif

如果定义了网桥或网桥模块,则由handle_bridge函数处理skb->dev->br_port :接收该数据包的端口是网桥端口组的一员br_handle_frame_hook :定义了网桥处理函数。

二、初始化

src/net/bridge/br.c:

static int __init br_init(void)

{

printk(KERN_INFO "NET4: Ethernet Bridge 008 for NET4.0\n";

br_handle_frame_hook = br_handle_frame;

br_ioctl_hook = br_ioctl_deviceless_stub;

#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)

br_fdb_get_hook = br_fdb_get;

br_fdb_put_hook = br_fdb_put;

#endif

register_netdevice_notifier(&br_device_notifier);

return 0;

}

初始化函数指明了网桥的处理函数是br_handle_frameioctl处理函数是:br_ioctl_deviceless_stub。

三、br_handle_frame(br_input.c)

网桥处理函数

void br_handle_frame(struct sk_buff *skb)

{

struct net_bridge *br;

unsigned char *dest;

struct net_bridge_port *p;

/*获取目的MAC地址*/

dest = skb->mac.ethernet->h_dest;

/*skb->dev->br_port用于指定接收该数据包的端口,

若不是属于网桥的端口,则为NULL*/

p = skb->dev->br_port;

if (p == NULL) /*端口不是网桥组端口中*/

goto err_nolock;

/*本端口所属的网桥组*/

br = p->br;

/*加锁,因为在转发中需要读CAM表,所以必须加读锁,

避免在这个过程中另外的内核控制路径(如多处理机上另外一个CPU上的系统调用)修改CAM表*/

read_lock(&br->lock);

if (skb->dev->br_port == NULL) /*前面判断过的*/

goto err;

/*br->dev是网桥的虚拟网卡,如果它未UP,

或网桥DISABLED,p->state实际上是桥的当前端口的STP计算判断后的状态*/

if (!(br->dev.flags & IFF_UP) ||

p->state == BR_STATE_DISABLED)

goto err;

/*源MAC地址为255.X.X.X,即源MAC是多播或广播,丢弃之*/

if (skb->mac.ethernet->h_source[0] & 1)

goto err;

众所周之,网桥之所以是网桥,比HUB更智能,是因为它有一个MAC-PORT的表,这样转发数据就不用广播,而查表定端口就可以了

每 次收到一个包,网桥都会学习其来源MAC,添加进这个表。Linux中这个表叫CAM表(这个名字是其它资料上看的)。如果桥的状态是LEARNING或 FORWARDING(学习或转发),则学习该包的源地址skb->mac.ethernet->h_source,将其添加到CAM表中, 如果已经存在于表中了,则更新定时器,br_fdb_insert完成了这一过程

if (p->state == BR_STATE_LEARNING ||

p->state == BR_STATE_FORWARDING)

br_fdb_insert(br, p, skb->mac.ethernet->h_source, 0);

STP 协议的BPDU包的目的MAC采用的是多播目标MAC地址:从01-80-c2-00-00-00(Bridge_group_addr:网桥组多播地 址)开始.所以这里是如果开启了STP,而当前数据包又是一个BPDU(!memcmp(dest, bridge_ula, 5), unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 },则交由相应函数处理。if (br->stp_enabled &&,这里只比较前5个字节,没有仔细研究过STP是使用了全部多播地址(从0 1 : 0 0 : 5 e : 0 0 : 0 0 : 0 0到0 1 : 0 0 : 5 e : 7 f : ff : ff。),还是只使用了一部份,这里看来似乎只是一部份,没去深究了

!memcmp(dest, bridge_ula, 5) &&

!(dest[5] & 0xF0)) /*01-80-c2-00-00-F0 是一个什么地址?为什么要判断呢?*/

goto handle_special_frame;

/*处理钩子函数,然后转交br_handle_frame_finish函数继续处理*/

if (p->state == BR_STATE_FORWARDING) {

NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,

br_handle_frame_finish);

read_unlock(&br->lock);

return;

}

err:

read_unlock(&br->lock);

err_nolock:

kfree_skb(skb);

return;

handle_special_frame:

if (!dest[5]) {

br_stp_handle_bpdu(skb);

return;

}

kfree_skb(skb);

}

四、br_handle_frame_finish

static int br_handle_frame_finish(struct sk_buff *skb)

{

struct net_bridge *br;

unsigned char *dest;

struct net_bridge_fdb_entry *dst;

struct net_bridge_port *p;

int passedup;

/*前面基本相同*/

dest = skb->mac.ethernet->h_dest;

p = skb->dev->br_port;

if (p == NULL)

goto err_nolock;

br = p->br;

read_lock(&br->lock);

if (skb->dev->br_port == NULL)

goto err;

passedup = 0;

/*如果网桥的虚拟网卡处于混杂模式,那么每个接收到的数据包都需要克隆一份

送到AF_PACKET协议处理体(网络软中断函数net_rx_action中ptype_all链的处理)。*/

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

struct sk_buff *skb2;

skb2 = skb_clone(skb, GFP_ATOMIC);

if (skb2 != NULL) {

passedup = 1;

br_pass_frame_up(br, skb2);

}

}

/*目的MAC为广播或多播,则需要向本机的上层协议栈传送这个数据包,

这里有一个标志变量passedup

用于表示是否传送过了,如果已传送过,那就算了*/

if (dest[0] & 1) {

br_flood_forward(br, skb, !passedup);

if (!passedup)

br_pass_frame_up(br, skb);

goto out;

}

/*Linux中的MAC-PORT表是CAM表,这里根据目的地址来查表,

以确定由哪个接口把包转发出去

每一个表项是通过结构struct net_bridge_fdb_entry来描述的:

struct net_bridge_fdb_entry

{

struct net_bridge_fdb_entry *next_hash; //用于CAM表连接的链表指针

struct net_bridge_fdb_entry **pprev_hash;

//为什么是pprev不是prev呢?还没有仔细去研究

atomic_t use_count; //此项当前的引用计数器

mac_addr addr; //MAC地址

struct net_bridge_port *dst; //此项所对应的物理端口

unsigned long ageing_timer; //处理MAC超时

unsigned is_local:1; //是否是本机的MAC地址

unsigned is_static:1; //是否是静态MAC地址

};*/

dst = br_fdb_get(br, dest);

/*查询CAM表后,如果能够找到表项,并且目的MAC是到本机的虚拟网卡的,

那么就需要把这个包提交给上层协议,

这样,我们就可以通过这个虚拟网卡的地址来远程管理网桥了*/

if (dst != NULL && dst->is_local) {

if (!passedup)

br_pass_frame_up(br, skb);

else

kfree_skb(skb);

br_fdb_put(dst);

goto out;

}

/*查到表了,且不是本地虚拟网卡的,转发之*/

if (dst != NULL) {

br_forward(dst->dst, skb);

br_fdb_put(dst);

goto out;

}

/*如果表里边查不到,那么只好学习学习HUB了……*/

br_flood_forward(br, skb, 0);

out:

read_unlock(&br->lock);

return 0;

err:

read_unlock(&br->lock);

err_nolock:

kfree_skb(skb);

return 0;

}

基本框架就是这样了,与那些讲网桥原理的书上讲的基本差不多……

网桥之所以是网桥,主要靠这两个函数:

br_fdb_insert

br_fdb_get

一个学习,一个查表;另外,支持STP,处理BPDU,需要用到函数br_stp_handle_bpdu.哪位有这三个函数的细节分析,可否送九贱一份,免得下午那么辛苦再去啃代码……

扫了一下 br_fdb_insert,结构还是很清析,如果当前项已存在于hash表项中,则更新它(__fdb_possibly_replace),如果是新项,则插入,实际是一个双向链表的维护过程(__hash_link):

void br_fdb_insert(struct net_bridge *br,

struct net_bridge_port *source,

unsigned char *addr,

int is_local)

{

struct net_bridge_fdb_entry *fdb;

int hash;

hash = br_mac_hash(addr);

write_lock_bh(&br->hash_lock);

fdb = br->hash[hash];

while (fdb != NULL) {

if (!fdb->is_local &&

!memcmp(fdb->addr.addr, addr, ETH_ALEN)) {

__fdb_possibly_replace(fdb, source, is_local);

write_unlock_bh(&br->hash_lock);

return;

}

fdb = fdb->next_hash;

}

fdb = kmalloc(sizeof(*fdb), GFP_ATOMIC);

if (fdb == NULL) {

write_unlock_bh(&br->hash_lock);

return;

}

memcpy(fdb->addr.addr, addr, ETH_ALEN);

atomic_set(&fdb->use_count, 1);

fdb->dst = source;

fdb->is_local = is_local;

fdb->is_static = is_local;

fdb->ageing_timer = jiffies;

__hash_link(br, fdb, hash);

write_unlock_bh(&br->hash_lock);

}

同样,查表也是一个遍历链表,进行地址匹配的过程:

struct net_bridge_fdb_entry *br_fdb_get

(struct net_bridge *br, unsigned char *addr)

{

struct net_bridge_fdb_entry *fdb;

read_lock_bh(&br->hash_lock);

fdb = br->hash[br_mac_hash(addr)];

while (fdb != NULL) {

if (!memcmp(fdb->addr.addr, addr, ETH_ALEN)) {

if (!has_expired(br, fdb)) {

atomic_inc(&fdb->use_count);

read_unlock_bh(&br->hash_lock);

return fdb;

}

read_unlock_bh(&br->hash_lock);

return NULL;

}

fdb = fdb->next_hash;

}

read_unlock_bh(&br->hash_lock);

return NULL;

}

linux 桥接stp原理,Linux操作系统网桥源码框架初步分析相关推荐

  1. Linux有问必答:怎么用CheckInstall从源码创建一个RPM或DEB包

    Linux有问必答:怎么用CheckInstall从源码创建一个RPM或DEB包 问题:我想要从源码创建安装的软件包.有没有一种方式从源码来创建和安装软件包,而不是运行"make insta ...

  2. linux设备驱动开发详解源码,linux设备驱动开发详解光盘源码.rar

    压缩包 : linux设备驱动开发详解光盘源码.rar 列表 19/busybox源代码/busybox-1.2.1.tar.bz2 19/MTD工具/mtd-utils-1.0.0.tar.gz 1 ...

  3. 09 编译2022年最新的Linux kernel、U-Boot和BusyBox rootfs源码,并用QEMU模拟器运行

    编译2022年最新的Linux kernel.U-Boot和BusyBox rootfs源码,并用QEMU模拟器运行 作者 将狼才鲸 创建日期 2022-11-26 Gitee源码和工程地址:才鲸嵌入 ...

  4. CSDN开发者周刊第 20 期:Google 下一代操作系统 Fuchsia 源码正式公开;AI 消除马赛克 效果惊人

    CSDN开发者周刊:只为传递"有趣/有用"的开发者内容,点击 Star(CodeChina)! 本周热门项目 1.红帽停止维护 CentOS 8,创始人另起炉灶 12 月 8 日, ...

  5. mvcc原理_MVCC原理探究及MySQL源码实现分析

    沃趣科技数据库专家  董红禹 MVCC原理探究及MySQL源码实现分析 数据库多版本读场景 session 1 session 2 select a from test; return a = 10 ...

  6. JDK动态代理实现原理详解(源码分析)

    无论是静态代理,还是Cglib动态代理,都比较容易理解,本文就通过进入源码的方式来看看JDK动态代理的实现原理进行分析 要了解动态代理的可以参考另一篇文章,有详细介绍,这里仅仅对JDK动态代理做源码分 ...

  7. ❤️缓存集合(一级缓存、二级缓存、缓存原理以及自定义缓存—源码+图文分析,建议收藏) ❤️

    ❤️缓存集合(一级缓存.二级缓存.缓存原理以及自定义缓存-源码+图文分析,建议收藏) ❤️ 查询 : 连接数据库 ,耗资源!一次查询的结果,给他暂存在一个可以直接取到的地方!--> 内存 : 缓 ...

  8. 详解非局部均值滤波原理以及用MATLAB源码实现

    详解非局部均值滤波原理以及用MATLAB源码实现 序言 均值滤波.中值滤波.高斯滤波在滤除噪声的过程中,无可避免的使图像的边缘细节和纹理信息所被滤除.针对此问题,Buades[1]等人提出了非局部均值 ...

  9. SHA224和SHA256哈希算法原理及实现(附源码)

    相关文章: SHA224和SHA256哈希算法原理及实现(附源码) 国密SM3哈希算法原理及实现(附源码) SHA1哈希算法原理及实现(附源码) MD5哈希算法原理及实现(附源码) MD4哈希算法原理 ...

最新文章

  1. mysql分页与分页性能查询优化
  2. python 跳出多重循环
  3. Chrome插件-新浪微博阅读器
  4. Intel Realsense C/C++ 转 python (1)rs-hello-realsense 获取摄像头正中心对应的深度数据 get_distance()
  5. 如何挂载阿里云Linux服务器的“数据盘”(新购买)
  6. 在 k8s 上最小化安装 KubeSphere
  7. AspNetCore结合Redis实践消息队列
  8. 转:PHP应用性能优化指南
  9. php向下滑动,js如何判断鼠标滚轮是向下还是向上滚动
  10. 梦想还是要有的,万一实现了呢
  11. 如何改变php的语言变中文,yii2怎么设置切换语言
  12. 编程修养 阅读笔记四
  13. 老版资源嗅探浏览器 - 遨游浏览器稀有绿色版
  14. Linux程序设计(Linux shell编程五)
  15. macd底背离的python_Python量化交易之MACD#39;顶底背离#39;形态的实现,自动化交易!...
  16. 阿里云oss添加cdn
  17. vc 调用matlab figure 画图
  18. 成员函数的重载、覆盖与隐藏(详细)【转】
  19. 图片提取利器,从PDF中快速提取图片并存储到本地
  20. 解决aab上传GooglePlay超过150M的问题及aab包测试方法

热门文章

  1. golang中字符串的查找方法小结
  2. javaweb——总结
  3. 25个超有用的 AngularJS Web 开发工具
  4. Spring Boot——开发新一代Spring应用
  5. 让你提前认识软件开发(28):数据库存储过程中的重要表信息的保存及相关建议...
  6. [Silverlight]如何创建超链接
  7. CCF202104-3 DHCP服务器(100分)【模拟】
  8. UVA558 LA5579 Wormholes【Floyd算法】
  9. hdu1861 游船出租【模拟】
  10. 奇妙的等式 精妙的证明