1、linux协议栈之网桥实现之一linux协议栈之网桥实现之一 网卡驱动最个函数netif_receive_skb.就从说起。 简单起见,去掉里面预编译代码int netif_receive_skb(struct sk_buff *skb)(net/core/dev.c) struct packet_type *ptype, *pt_prev; int ret = NET_RX_DROP; unsigned short type; /打接收时间戳 if (!skb->stamp.tv_sec) net_timestamp(&skb->stamp); /如果存dev->m
2、aster。则更新相应指针 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
3、->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_basentohs(type)&15, list) if (ptype
4、->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 t
5、o 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)
6、;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_pr
7、ev); *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表。根据这张表以数据
8、送往相应端口进行发送.网桥转发过程:1:接收个包。判断自己CAM表否含包此包源地址.如果没有,则源地址端口更新至于CAM表.2:判断包否送给本机,如果,则送往本机层协议栈处理。如果不,则查寻CAM表。找相应出口。3:如果找出口,则此包送至出口。如果不存,各端口发送。4:如果CAM表应表项规定时间没有得更新,则除此项。网桥配置:Brctl个比较好配置网桥工具。源代码配置极其简单。们从网桥配置流程说起,看linux核怎样步步管理。首先,创建个网桥: brctl addbr br0 (建立个br0网桥)然,接口添加进网桥:brctl addifbr0 eth0 (eth0eth1添加进网桥br0)
9、brctl addif bro eth1OK,网桥现就配置好。这台linux主机以当作交换机使,从eth0包都以转发eth1。现,们看代码进行处理首先 brctl addbr 。查看brctl代码调:ioctl(br_socket_fd, SIOCBRADDBR, brname);然 brctl addifbrctl代码调:ioctl(br_socket_fd, SIOCBRADDIF, &ifr);呵呵。Brctl代码简单吧,只调户空间配置工具ioctl.Linux网桥分析:好,现就以进入核分析网桥模式:static int _init br_init(void)(net/brige
10、/br.c) /分配slab缓冲区 br_fdb_init();/网桥netfiter处理,以章节分析#ifdef CONFIG_BRIDGE_NETFILTER if (br_netfilter_init() return 1;#endif /户空间ioctl调函数 brioctl_set(br_ioctl_deviceless_stub); /接收数据包处理,就们面netif_receive_skb函数看br_handle_frame_hook br_handle_frame_hook = br_handle_frame;#if defined(CONFIG_ATM_LANE) | def
11、ined(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_io
12、ctl_deviceless_stub(unsigned int cmd, void _user *uarg) switch (cmd) case SIOCGIFBR: case SIOCSIFBR: return old_deviceless(uarg); /新建网桥 case SIOCBRADDBR: /除网桥 case SIOCBRDELBR: char bufIFNAMSIZ; if (!capable(CAP_NET_ADMIN)return -EPERM; /copy_from_user:户空间数据拷入核空间 if (copy_from_user(buf, uarg, IFNAMS
13、IZ)return -EFAULT; bufIFNAMSIZ-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_d
14、ev(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 cha
15、r *name) struct net_bridge *br; struct net_device *dev; /分配net_device dev = 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; /队列始化。port_list保存这个桥端口列表 INIT_LIS
16、T_HEAD(&br->port_list); br->hash_lock = SPIN_LOCK_UNLOCKED; /面这部份代码跟stp协议相关,们暂不关心 br->bridge_id.prio0 = 0x80; br->bridge_id.prio1 = 0x00; memset(br->bridge_id.addr, 0, ETH_ALEN); dev->open = br_dev_open; dev->set_multicast_list = br_dev_set_multicast_list; dev->change_mtu
17、 = br_change_mtu; dev->destructor = free_netdev; SET_MODULE_OWNER(dev); dev->stop = br_dev_stop; dev->accept_fastpath = br_dev_accept_fastpath; dev->tx_queue_len = 0; dev->set_mac_address = NULL; dev->priv_flags = IFF_EBRIDGE;这部份,桥设备私区空间进行始化。这里,有必给桥net_device应私区结构:struct net_bridge
18、 /读锁 spinlock_tlock; /端口列表 struct list_head port_list; /网桥应虚拟设备 struct net_device*dev; /网桥应虚拟网卡统计数据 struct net_device_stats statistics; /hash表锁 spinlock_thash_lock; /MAC PORT应表,即CAM struct hlist_head hashBR_HASH_SIZE; struct list_head age_list;/* STP */u16 root_port;rtnl_lock();br->stp_enabled =
19、0;br_stp_timer_init(br);return dev;/stp 协议应数据unsigned char stp_enabled;/由核确定接口名字,例如eth0 eth1等br->designated_root = br->bridge_id;bridge_id designated_root;unsigned char topology_change;if (strchr(dev->name, %) br->root_path_cost = 0;br_dev_setup还做些另函数指针始化:bridge_idbridge_id;ret = dev_all
20、oc_name(dev, dev->name);br->root_port = 0;void br_dev_setup(struct net_device *dev)u32 root_path_cost;if (ret < 0)br->bridge_max_age = br->max_age = 20 * HZ;unsigned long max_age;goto err1;br->bridge_hello_time = br->hello_time = 2 * HZ;/桥MAC地址设零unsigned long hello_time;br->b
21、ridge_forward_delay = br->forward_delay = 15 * HZ;memset(dev->dev_addr, 0, ETH_ALEN);unsigned long forward_delay;/向核注册此网络设备br->topology_change = 0;/以太网结构始化unsigned long bridge_max_age;ret = register_netdevice(dev);br->topology_change_detected = 0;ether_setup(dev);unsigned long ageing_tim
22、e;if (ret)br->ageing_time = 300 * HZ;/系列函数指针始化unsigned long bridge_hello_time;goto err2;INIT_LIST_HEAD(&br->age_list);dev->do_ioctl = br_dev_ioctl;unsigned long bridge_forward_delay;dev_hold(dev);dev->get_stats = br_dev_get_stats; rtnl_unlock(); dev->hard_start_xmit = br_dev_xmit; unsigned char topology_change_detected;
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1