linux协议栈之网桥实现之一网卡驱动最个函数netif_receive_skb.就从说起。
简单起见,去掉里面预编译代码int netif_receive_skb(structsk_buff *skb) (net/core/dev.c){struct packet_type *ptype, *pt_prev;int ret = NET_RX_DROP;unsigned short type;//打接收时间戳if (!skb->_sec)net_timestamp(&skb->stamp);//如果存dev->master。
则更新相应指针skb_bond(skb);//更新CPU接收统计数据__get_cpu_var(netdev_rx_stat).total++;skb->h.raw = skb->nh.raw = skb->data;skb->mac_len = skb->nh.raw -skb->mac.raw;pt_prev = NULL;rcu_read_lock();//处理所有协议模块list_for_each_entry_rcu(ptype, &ptype_all, list) {if (!ptype->dev || ptype->dev == skb->dev) {if (pt_prev)ret =deliver_skb(skb, pt_prev);pt_prev = ptype;}}//分片处理handle_diverter(skb);//网桥处理if (handle_bridge(&skb, &pt_prev,&ret))goto out;type = skb->protocol;//协议调相应模块处理。
list_for_each_entry_rcu(ptype,&ptype_base[ntohs(type)&15], list) {if (ptype->type == type&&(!ptype->dev || ptype->dev == skb->dev)) {if (pt_prev)ret =deliver_skb(skb, pt_prev);pt_prev = ptype;}}if (pt_prev) {ret = pt_prev->func(skb,skb->dev, pt_prev);} else {kfree_skb(skb);/* Jamal, now you will not able to escape explaining* me how you were going to use this. :-)*/ret = NET_RX_DROP;}out:rcu_read_unlock();return ret;}此函数主完成分片重组,网桥处理,根据不同协议调不同传输层处理模块。
本节重点概述linux网桥实现处理。
传输层协议分层续章节陆续给出。
进入网桥处理代码:#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)(net/core/dev.c)int (*br_handle_frame_hook)(struct net_bridge_port *p, struct sk_buff **pskb);static __inline__ int handle_bridge(struct sk_buff **pskb,struct packet_type **pt_prev, int *ret){struct net_bridge_port *port;//回环接口?非以太网接口?if ((*pskb)->pkt_type ==PACKET_LOOPBACK ||(port =rcu_dereference((*pskb)->dev->br_port)) == NULL)return 0;if (*pt_prev) {*ret = deliver_skb(*pskb, *pt_prev);*pt_prev = NULL;}// br_handle_frame_hook个全局函数指针return br_handle_frame_hook(port, pskb);}#else#define handle_bridge(skb, pt_prev, ret) (0)#endif从此以看出。
如果编译时候选择网桥模式,则进入网桥处理模块,否则,只个空函数,直接返回。
br_handle_frame_hook代表函数什么呢?网桥数据处理框架又什么样呢?关于网桥:网桥个二层设备,深入以当成个二层交换机。
二层协议转发数据。
网桥转发数据,维持个端口MAC应表,通常通CAM表。
根据这张表以数据送往相应端口进行发送.网桥转发过程:1:接收个包。
判断自己CAM表否含包此包源地址.如果没有,则源地址端口更新至于CAM表.2:判断包否送给本机,如果,则送往本机层协议栈处理。
如果不,则查寻CAM表。
找相应出口。
3:如果找出口,则此包送至出口。
如果不存,各端口发送。
4:如果CAM表应表项规定时间没有得更新,则除此项。
网桥配置:Brctl个比较好配置网桥工具。
源代码配置极其简单。
们从网桥配置流程说起,看linux 核怎样步步管理。
首先,创建个网桥: brctl addbr br0(建立个br0网桥)然,接口添加进网桥:brctl addif br0 eth0 (eth0eth1添加进网桥br0)brctl addif bro eth1OK,网桥现就配置好。
这台linux主机以当作交换机使,从eth0包都以转发eth1。
现,们看代码进行处理首先brctl addbr 。
查看brctl代码调:ioctl(br_socket_fd, SIOCBRADDBR, brname);然brctl addif brctl代码调:ioctl(br_socket_fd, SIOCBRADDIF, &ifr);呵呵。
Brctl代码简单吧,只调户空间配置工具ioctl.Linux网桥分析:好,现就以进入核分析网桥模式:static int __init br_init(void)(net/brige/br.c){//分配slab缓冲区br_fdb_init();//网桥netfiter处理,以章节分析#ifdef CONFIG_BRIDGE_NETFILTERif (br_netfilter_init())return 1;#endif//户空间ioctl调函数brioctl_set(br_ioctl_deviceless_stub);//接收数据包处理,就们面netif_receive_skb函数看br_handle_frame_hookbr_handle_frame_hook = br_handle_frame;#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//netdev_chain通知链表注册。
关于通知链表,面已经介绍过,这里不再讨论register_netdevice_notifier(&br_device_notifier);return 0;}新建网桥:从面分析以知道,户空间调ioctl(br_socket_fd, SIOCBRADDBR, brname).进入br_ioctl_deviceless_stub,以看相关处理:int br_ioctl_deviceless_stub(unsigned int cmd, void __user *uarg){switch (cmd) {case SIOCGIFBR:case SIOCSIFBR:return old_deviceless(uarg);//新建网桥case SIOCBRADDBR://除网桥case SIOCBRDELBR:{char buf[IFNAMSIZ];if (!capable(CAP_NET_ADMIN))return -EPERM;//copy_from_user:户空间数据拷入核空间if (copy_from_user(buf, uarg, IFNAMSIZ))return -EFAULT;buf[IFNAMSIZ-1] = 0;if (cmd == SIOCBRADDBR)return br_add_bridge(buf);return br_del_bridge(buf);}}return -EOPNOTSUPP;}这里,们传入cmdSIOCBRADDBR.转入br_add_bridge(buf)进行:int br_add_bridge(const char *name){struct net_device *dev;int ret;//虚拟桥新建个net_device//面“网络设备管理”经讲述此结构dev = new_bridge_dev(name);if (!dev)return -ENOMEM;//sysfs建立相关信息ret = br_sysfs_addbr(dev);dev_put(dev);if (ret)unregister_netdev(dev);out:return ret;err2:free_netdev(dev);err1:rtnl_unlock();goto out;}网桥注册跟们以看物理网络设备注册样。
们关心网桥应net_device结构什么样,继续跟踪进new_bridge_dev:static struct net_device *new_bridge_dev(const char *name) {struct net_bridge *br;struct net_device *dev;//分配net_devicedev = alloc_netdev(sizeof(struct net_bridge), name,br_dev_setup);if (!dev)return NULL; 网桥私区结构net_bridge br = netdev_priv(dev);//私区结构dev字段指向本身br->dev = dev;br->lock = SPIN_LOCK_UNLOCKED;//队列始化。