`
simohayha
  • 浏览: 1387796 次
  • 性别: Icon_minigender_1
  • 来自: 火星
社区版块
存档分类
最新评论

网桥在内核的实现

阅读更多
我们知道netdevice有一个priv域,这个域用来保存设备的私有数据,当这个设备是一个网桥的时候,priv域也就指向一个struct net_bridge.

接下来看net_bridge以及相关的数据结构:
struct net_bridge
{
///自旋锁
	spinlock_t			lock;
///网桥所有端口的链表,其中每个元素都是一个net_bridge_port结构。
	struct list_head		port_list;
///加到这个网桥的物理设备
	struct net_device		*dev;
///这个锁是用来保护下面的那个hash链表。
	spinlock_t			hash_lock;
///保存forwarding database的一个hash链表(这个也就是地址学习的东东,所以通过hash能 快速定位),这里每个元素都是一个net_bridge_fsb_entry结构
	struct hlist_head		hash[BR_HASH_SIZE];
///这个结构没有被使用
	struct list_head		age_list;
	unsigned long			feature_mask;
#ifdef CONFIG_BRIDGE_NETFILTER
	struct rtable 			fake_rtable;
#endif
	unsigned long			flags;
#define BR_SET_MAC_ADDR		0x00000001

///stp相关的一些东西
	bridge_id			designated_root;
	bridge_id			bridge_id;
	u32				root_path_cost;
	unsigned long			max_age;
	unsigned long			hello_time;
	unsigned long			forward_delay;
	unsigned long			bridge_max_age;
	unsigned long			ageing_time;
	unsigned long			bridge_hello_time;
	unsigned long			bridge_forward_delay;

	u8				group_addr[ETH_ALEN];
	u16				root_port;
///当前使用的协议。
	enum {
		BR_NO_STP, 		/* no spanning tree */
		BR_KERNEL_STP,		/* old STP in kernel */
		BR_USER_STP,		/* new RSTP in userspace */
	} stp_enabled;

	unsigned char			topology_change;
	unsigned char			topology_change_detected;

///stp要用的一些定时器列表。
	struct timer_list		hello_timer;
	struct timer_list		tcn_timer;
	struct timer_list		topology_change_timer;
	struct timer_list		gc_timer;
	struct kobject			*ifobj;
};


struct net_bridge_port
{
///从属于的网桥设备
	struct net_bridge		*br;
///表示链接到这个端口的物理设备
	struct net_device		*dev;
	struct list_head		list;
///stp相关的一些参数。
	u8				priority;
	u8				state;
	u16				port_no;
	unsigned char			topology_change_ack;
	unsigned char			config_pending;
	port_id				port_id;
	port_id				designated_port;
	bridge_id			designated_root;
	bridge_id			designated_bridge;
	u32				path_cost;
	u32				designated_cost;
///端口定时器,也就是stp控制超时的一些定时器列表.(详细的需要去看stp的协议).
	struct timer_list		forward_delay_timer;
	struct timer_list		hold_timer;
	struct timer_list		message_age_timer;
	struct kobject			kobj;
	struct rcu_head			rcu;
};


struct net_bridge_fdb_entry
{
	struct hlist_node		hlist;
///桥的端口(最主要的两个域就是这个域和下面的mac地址域)
	struct net_bridge_port		*dst;
///当使用RCU策略,才用到
	struct rcu_head			rcu;
///引用计数
	atomic_t			use_count;
	unsigned long			ageing_timer;
///mac地址。
	mac_addr			addr;
	unsigned char			is_local;
	unsigned char			is_static;
};

通过下面的图能更好的理解这个结构:



接下来简要的介绍一下网桥的初始化。

网桥的初始化和一般网络设备的初始化很相似,只不过由于它是虚拟设备,因此这里还有一点不同。

首先来看内核的网络模块的初始化br_init,也就是初始化上面介绍的数据结构:

static int __init br_init(void)
{
	int err;
///stp的注册。
	err = stp_proto_register(&br_stp_proto);
	if (err < 0) {
		printk(KERN_ERR "bridge: can't register sap for STP\n");
		return err;
	}

///forwarding database的初始化
	err = br_fdb_init();
	if (err)
		goto err_out;
///网桥的netfilter钩子函数的初始化。
	err = br_netfilter_init();
	if (err)
		goto err_out1;
///注册到netdevice的通知链上
	err = register_netdevice_notifier(&br_device_notifier);
	if (err)
		goto err_out2;

	err = br_netlink_init();
	if (err)
		goto err_out3;
///安装网络设备的do_ioctl函数,也就是提供给用户空间ioctl接口。
	brioctl_set(br_ioctl_deviceless_stub);
	br_handle_frame_hook = br_handle_frame;

	br_fdb_get_hook = br_fdb_get;
	br_fdb_put_hook = br_fdb_put;

	return 0;
.........................................
	return err;
}


我们新建一个网桥,使用br_add_bridge,在这个函数中,主要是调用new_bridge_dev函数,下面我们主要就来看这个函数:

static struct net_device *new_bridge_dev(const char *name)
{
	struct net_bridge *br;
	struct net_device *dev;

///这里看到setup回调函数,是br_dev_setup(也就是网桥设备专用的)。setup函数的用途,可以看我以前写的网络设备初始化的blog。
	dev = alloc_netdev(sizeof(struct net_bridge), name,
			   br_dev_setup);

	if (!dev)
		return NULL;
///得到priv数据。
	br = netdev_priv(dev);

///接下来初始化br数据结构。
	br->dev = dev;

	spin_lock_init(&br->lock);
	INIT_LIST_HEAD(&br->port_list);
	spin_lock_init(&br->hash_lock);

///网桥优先级 32768(也就是默认是0x8000)
	br->bridge_id.prio[0] = 0x80;
	br->bridge_id.prio[1] = 0x00;

	memcpy(br->group_addr, br_group_address, ETH_ALEN);

	br->feature_mask = dev->features;
	br->stp_enabled = BR_NO_STP;
	br->designated_root = br->bridge_id;
	br->root_path_cost = 0;
	br->root_port = 0;
	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->topology_change = 0;
	br->topology_change_detected = 0;
	br->ageing_time = 300 * HZ;
///初始化网桥设备的netfilter相关域。
	br_netfilter_rtable_init(br);

	INIT_LIST_HEAD(&br->age_list);

	br_stp_timer_init(br);

	return dev;
}


加一个新端口到一个网桥使用br_add_if方法。这里就不详细介绍这个方法了,不过这里要注意,他会在sys文件系统下,生成一些相关的东西。要看sysfs的介绍,去看kernel的文档。

最后来看一下网桥的子系统在这个网络子系统的位置:




可以看到这里有很多的hook在ip层,基本都是netfilter子系统的东西。

这里网桥的输入帧的处理是通过br_handle_frame来处理的。而网桥的输出帧是通过br_dev_xmit来处理的。

当网络帧通过NIC的设备驱动被接收了之后,skb->dev被实例化为真实的设备,然后这个帧被放入网络栈,然后当be_handle_frame_finish之后调用br_pass_frame_up。我们来看这个函数的实现:

引用
static void br_pass_frame_up(struct net_bridge *br, struct sk_buff *skb)
{
struct net_device *indev, *brdev = br->dev;

brdev->stats.rx_packets++;
brdev->stats.rx_bytes += skb->len;

indev = skb->dev;
///这步将真实的物理设备替换为虚拟的网桥设备。因此对3层来说就完全不知道物理设备的存在了。
skb->dev = brdev;
///调用netfiltel的相关hook。
NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, indev, NULL,
netif_receive_skb);
}


当这个函数执行完毕后,会再次调用netif_receive_skb,他则会再次调用handle_bridge,而此时由于设备已经替换为虚拟的网桥设备,因此就会直接将包发往下层正确的协议处理。



  • 大小: 65 KB
  • 大小: 52.7 KB
  • 大小: 24.7 KB
1
0
分享到:
评论
1 楼 lenky0401 2009-09-22  
很强大 查这个内容找到你的博客
提供建议:麻烦你下次记得把你分析的linux代码的内核版本号写上,谢谢。

相关推荐

    linux内核网桥分析

    linux内核网桥分析,介绍linux内核网桥的架构实现,和网桥的相关操作

    Linux-网桥原理分析

    分析了2.6.15内核源码,主要解析网桥的实现原理,处理数据的流程

    网桥的原理及在linux内核中的实现.pdf

    网桥的原理及在linux内核中的实现.pdf

    Docker如何实现修改Docker0网桥默认网段

    Docker 服务启动后默认会创建一个 docker0 网桥(其上有一个 docker0 内部接口),它在内核层连通了其他的物理或虚拟网卡,这就将所有容器和本地主机都放到同一个物理网络。 Docker 默认指定了 docker0 接口 的 IP ...

    linux 系统源码全面剖析

    Linux网桥工作原理与实现 其他 定时器实现 多路复用I/O GDB原理之ptrace 容器相关 docker实现原理之 - namespace docker实现原理之 - CGroup介绍 docker实现原理之 - CGroup实现原理 docker实现原理之 - OverlayFS...

    Linux 运维 入门到高级

    备份硬盘数据 管理存储三要素 逻辑卷实现 转移硬盘步骤 逻辑卷快照 开启路由转发 网桥的实现 正在访问文件被删复原 作业管理 Linux启动流程 Linux根据端口号查看被占用的服务 升级gcc编译器 自动化运维 安装...

    WinPcap 用户文档手册、中文技术文档

    以上这些功能需要借助安装在Win32内核中的网络设备驱动程序才能实现,再加上几个动态链接库DLL。 基于WinPcap的典型应用有: • 网络与协议分析器 (network and protocol analyzers) • 网络监视器 (network ...

    winpcap驱动及开发包

    以上这些功能需要借助安装在Win32内核中的网络设备驱动程序才能实现,再加上几个动态链接库DLL。 所有这些功能都能通过一个强大的编程接口来表现出来,易于开发,并能在不同的操作系统上使用。这本手册的主要目标是...

    Linux-Kernel-network-protocol-stack-Reading:linux协议栈的阅读笔记

    我读这份代码也不是这个目的,我只是很好奇这些东西真正是实现的而已,大致会花半年的闲暇时间在这份代码之上。虽然我知道读完之后,不会有什么立竿见影的效果,但希望自己读完之后,有足够的能力走出当前的恶性循环...

    winpcap中文帮助

    以上这些功能需要借助安装在Win32内核中的网络设备驱动程序才能实现,再加上几个动态链接库DLL。 所有这些功能都能通过一个强大的编程接口来表现出来,易于开发,并能在不同的操作系统上使用。这本手册的主要目标是...

    wansim:WANsim 允许您模拟 WAN 连接

    WANsim 利用 Linux 内核 NETEM 功能来实现其目标。 尽管文档相对完备,但此功能并不是最容易使用的。 WANsim 将其包装成一个简单易用的脚本,只需使用 wansim 用户帐户登录 Linux 主机即可访问该脚本(请参阅 ...

    Linux高级路由和流量控制

    16.3. 用ARP代理实现伪网桥 109 16.3.1. ARP和ARP代理 110 16.3.2. 实现 110 第17章 动态路由——OSPF和BGP 112 17.1. 用ZEBRA设置OSPF 112 17.1.1. 必要条件 113 17.1.2. 配置Zebra 113 17.1.3. 运行Zebra ...

    understanding linux network internals

    Kernel Infrastructure for Component Initialization 组件初始化的底层内核(实现) Section 7.1. Boot-Time Kernel Options 内核启动选项 Section 7.2. Module Initialization Code 模块初始化 Section 7.3. ...

    bgp-ipvlan-docker

    bgp-ipvlan-docker ... IPVlan是一种轻量级的L2和L3网络实现,不需要传统的网桥。 BGP是Internetz起作用的原因。 先决条件 从安装在指令多克尔实验二:。 (停止其他Docker实例) 快速实验安装: wget -qO-

    windowsnt 技术内幕

    用Winn32.exe执行Windows NT升级 使用联机丛书 系统策略编辑器简介 理解系统策略编辑器模式 理解系统策略处理 在域控制器上实现系统策略 在非域控制器上实现系统策略 使用系统策略编辑器复选框 从登录对话框中删除...

    网吧维护技术资料 合集

    5239 网吧维护\资料\FW\ASP实现对SQL SERVER 数据库的操作.TXT 2945 网吧维护\资料\FW\MYSQL.TXT 11239 网吧维护\资料\FW\WIN2000SERVER安全设置的一些小技巧.TXT 0 网吧维护\资料\FW\WWW.TXT 6103 网吧维护\资料\FW...

Global site tag (gtag.js) - Google Analytics