⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 bridge_current.c

📁 Details description of Free BSD network source code.Those documents have explained each line of code
💻 C
📖 第 1 页 / 共 3 页
字号:
    switch ((uintptr_t)dst) {    case (uintptr_t)BDG_BCAST:    case (uintptr_t)BDG_MCAST:    case (uintptr_t)BDG_LOCAL:    case (uintptr_t)BDG_UNKNOWN:    case (uintptr_t)BDG_DROP:	BDG_STAT(ifp, dst);	break ;    default :	if (dst == ifp || dropit)	    BDG_STAT(ifp, BDG_DROP);	else	    BDG_STAT(ifp, BDG_FORWARD);	break ;    }    if ( dropit ) {/*不转发为真吗?*/		if (dst == BDG_BCAST || dst == BDG_MCAST || dst == BDG_LOCAL)			dst = BDG_LOCAL ;/*如果是发送给本机的包,即上面那些条件成立,返回该标志由ether_input函数处理*/		else		   dst = BDG_DROP ;/*该标志返回给ether_input后,该函数会把包抛弃*/    } else {	if (dst == ifp)/*如果包来自该接口,又要发送到该接口,当然应该丢弃该包*/	    dst = BDG_DROP;    }    DEB(printf("bridge_in %6D ->%6D ty 0x%04x dst %s%d\n",eh->ether_shost, ".",eh->ether_dhost, ".",ntohs(eh->ether_type),	(dst <= BDG_FORWARD) ? bdg_dst_names[(int)dst] :dst->if_name,(dst <= BDG_FORWARD) ? 0 : dst->if_unit); )    return dst ;/*返回的值是给ether_input函数的*/}/* 该函数由ether_input函数(if_ethersubr.c中)调用,作用是把包转发到相应的网络接口 * 参数dst是将要被转发的接口,当然,他可以是一个接口,也有可能是一组或所有接口. * 该函数内是作为放火墙代码的放置地的理想地方.非同组接口过滤,以太层包过滤,IP层包过滤 * 或自己编写的钩子都可以在此实现. */static struct mbuf *bdg_forward(struct mbuf *m0, struct ifnet *dst){/*该宏的作用是把先前保存的以太网包头部恢复到mbuf中.*/#define	EH_RESTORE(_m) do {						   \	/*关于M_PREPEND宏我在以前的文章中讲过,该宏是对mbuf进行操作,在此处是在mbuf前申请以太网头部长度的空间*/    M_PREPEND((_m), ETHER_HDR_LEN, M_DONTWAIT);			   	   \    if ((_m) == NULL) {							   \	bdg_dropped++;							   \	return NULL;							   \    }									   \    if (eh != mtod((_m), struct ether_header *))			   \	bcopy(&save_eh, mtod((_m), struct ether_header *), ETHER_HDR_LEN); \    else								   \	bdg_predict++;							   \} while (0);    struct ether_header *eh;  /*暂时存放以太网头部*/    struct ifnet *src;        /*该包是本机的哪块网卡接收的*/    struct ifnet *ifp, *last;  /*转发包时要用到的一些临时存放ifnet结构的指针*/    int shared = bdg_copy ; /* 看前面的sysctl宏 */    int once = 0;      /* 代表只发送一次 */    struct ifnet *real_dst = dst ;     struct ip_fw_args args;#ifdef PFIL_HOOKS /* PFIL_HOOKS 即包过滤钩子*/    struct packet_filter_hook *pfh;/* 包过滤钩子结构*/    int rv;#endif /* PFIL_HOOKS 即包过滤钩子*/    struct ether_header save_eh;    DEB(quad_t ticks; ticks = rdtsc();)    args.rule = NULL;	/*放火墙规则*/    /* 关于这一些放火墙及DUMMYNET,我没有研究过,有兴趣的可以自己扩展研究 */    for (;m0->m_type == MT_TAG; m0 = m0->m_next)	if (m0->_m_tag_id == PACKET_TAG_DUMMYNET) {	    args.rule = ((struct dn_pkt *)m0)->rule;	    shared = 0;			}    if (args.rule == NULL)	bdg_thru++;     eh = mtod(m0, struct ether_header *);/*eh指向了m0中的以太网头部*/    src = m0->m_pkthdr.rcvif;   /*接收该包的本机的网卡接口的ifnet结构指针*/    if (src == NULL)			/* 代表包是从ether_output函数输出,即从本机的上层协议输出 */		dst = bridge_dst_lookup(eh, ifp2sc[real_dst->if_index].cluster);    if (dst == BDG_DROP) { /* 这种情况不会发生,因为在ether_input函数中已经对BDG_DROP进行了过滤 */		printf("xx bdg_forward for BDG_DROP\n");		m_freem(m0);		bdg_dropped++;/*统计丢弃的包数量*/		return NULL;    }    if (dst == BDG_LOCAL) { /* 这种情况不会发生,因为在ether_input函数中已经对BDG_DROP进行了过滤 */		printf("xx ouch, bdg_forward for local pkt\n");		return m0;    }    if (dst == BDG_BCAST || dst == BDG_MCAST) {		/* need a copy for the local stack */		shared = 1 ;    }    /* 在这是做了一个和ip_output中类似的过滤器,当放火墙已经打开,并且包不是从ether_output输出的时候(     * 会过滤两次).当然在此处还可以限制一些非IP包,其他链路层的包.     */    if (src != NULL && (#ifdef PFIL_HOOKS	((pfh = pfil_hook_get(PFIL_IN, &inetsw[ip_protox[IPPROTO_IP]].pr_pfh)) != NULL && bdg_ipf !=0) ||#endif	(IPFW_LOADED && bdg_ipfw != 0))) 	{		int i;		if (args.rule != NULL && fw_one_pass)			goto forward; /* 包已经处理过了,直接到forward转发 */		i = min(m0->m_pkthdr.len, max_protohdr) ;		if ( shared || m0->m_len < i) {			m0 = m_pullup(m0, i) ;			if (m0 == NULL) {				printf("-- bdg: pullup failed.\n") ;				bdg_dropped++;				return NULL ;			}			eh = mtod(m0, struct ether_header *);		}		bcopy(eh, &save_eh, ETHER_HDR_LEN);	/*保存以太网头部,以后用EH_RESTORE恢复 */		m_adj(m0, ETHER_HDR_LEN);		/* 剥掉头部 */#ifdef PFIL_HOOKS	/*	 * NetBSD风格的过滤器	 */		if (pfh != NULL && m0->m_pkthdr.len >= sizeof(struct ip) &&	ntohs(save_eh.ether_type) == ETHERTYPE_IP) {	    /*	     * 调用放火墙前,要确定是IP包.	     */			struct ip *ip = mtod(m0, struct ip *);/*指向IP头部*/			ip->ip_len = ntohs(ip->ip_len);			ip->ip_off = ntohs(ip->ip_off);			do {				if (pfh->pfil_func) {					rv = pfh->pfil_func(ip, ip->ip_hl << 2, src, 0, &m0);/*过滤*/					if (m0 == NULL) {						bdg_dropped++;						return NULL;					}					if (rv != 0) {						EH_RESTORE(m0);		/* 恢复以太网头部 */						return m0;					}					ip = mtod(m0, struct ip *);				}			} while ((pfh = TAILQ_NEXT(pfh, pfil_link)) != NULL);	    /*	     * 到这时,放火墙已经通过了该包, 恢复IP指针和把IP内的一些成员转回到网络字节顺序	     */			ip = mtod(m0, struct ip *);			ip->ip_len = htons(ip->ip_len);			ip->ip_off = htons(ip->ip_off);		}#endif /* PFIL_HOOKS结束 */		if (!IPFW_LOADED || bdg_ipfw == 0) {			EH_RESTORE(m0);	/* 恢复以太网头部 */			goto forward;	/* 不使用ipfw, 直接转发 */		}	/*	 * 下面的代码和if_ethersubr.c:ether_ipfw_chk()非常类似	 */		args.m = m0;		/* 将查看的包	*/		args.oif = NULL;	/* 输入的ifnet		*/		args.divert_rule = 0;	/* 目前不支持定向的规则		*/		args.next_hop = NULL;	/* 目前也不支持转发的规则	*/		args.eh = &save_eh;	/* 头部	*/		i = ip_fw_chk_ptr(&args);		m0 = args.m;			if (m0 != NULL)/*通过了*/			EH_RESTORE(m0);	/* 恢复以太网头部 */		if ( (i & IP_FW_PORT_DENY_FLAG) || m0 == NULL) /* 没通过,抛弃 */			return m0 ;		if (i == 0) 			goto forward ;		if (DUMMYNET_LOADED && (i & IP_FW_PORT_DYNT_FLAG)) {			struct mbuf *m ;			if (shared) {				m = m_copypacket(m0, M_DONTWAIT);/*共享为真,则做一个备份*/				if (m == NULL) {						bdg_dropped++;					return NULL;				}			} else {				m = m0 ; /* 把原包放到 dummynet 中处理*/				m0 = NULL ; 			}			args.oif = real_dst;			ip_dn_io_ptr(m, (i & 0xffff),DN_TO_BDG_FWD, &args);			return m0 ;		}		bdg_ipfw_drops++ ;		return m0 ;    }forward:  /*转发*/    if ( shared ) {		int i = min(m0->m_pkthdr.len, max_protohdr) ;/*取mbuf链表的第一个mbuf的包头部长度与最大协议长度的最小值*/		m0 = m_pullup(m0, i) ;/*调整m0->data的指向位置到i*/		if (m0 == NULL) {/*不成功*/			bdg_dropped++ ;			return NULL ;		}    }    if (src != NULL)		real_dst = src ;    last = NULL;    IFNET_RLOCK();    if (dst == BDG_BCAST || dst == BDG_MCAST || dst == BDG_UNKNOWN) {/*如果目的地是广播,多播和不知道的包类型,则全部都要转发*/		ifp = TAILQ_FIRST(&ifnet) ; /* 从第一个开始吧 */		once = 0 ;/*该变量是用来控制是否都转发,0为都转发,1为只从1个网卡转发*/    } else {		ifp = dst ;/*转发的网卡只要一个*/		once = 1 ;    }    if ((uintptr_t)(ifp) <= (u_int)BDG_FORWARD)/*BDG_FORWARD定义为9*/		panic("bdg_forward: bad dst");    for (;;) {/*开始转发,转发过程有可能是对一个接口,也有可能是转发到多个接口,如广播,多播*/		if (last) { /* 第一次进来的时候,因为last为空,所以跳过他. */			struct mbuf *m ;			if (shared == 0 && once ) { /* shared代表是否共享,once代表发送一次,此句意思为:当不是广播或多播,并只发送一次时 */				m = m0 ;				m0 = NULL ; /*  */			} else {				m = m_copypacket(m0, M_DONTWAIT);				if (m == NULL) {					IFNET_RUNLOCK();					printf("bdg_forward: sorry, m_copypacket failed!\n");					bdg_dropped++ ;					return m0 ; 				}			}			if (!IF_HANDOFF(&last->if_snd, m, last)) {/*发送包,早版本的只是用(*ifp->if_start)(ifp);当前版考虑到SMP,使用了*/#if 0													/*一些锁技术,该过程跳过了普通的ether_output函数而直接调用驱动程序*/			BDG_MUTE(last); #endif			}			BDG_STAT(last, BDG_OUT);/*统计*/			last = NULL ;			if (once)/*只发送一次为真吗?*/				break ;		}		if (ifp == NULL)			break ;		if ( BDG_USED(ifp) && !BDG_MUTED(ifp) && !_IF_QFULL(&ifp->if_snd)  &&	     (ifp->if_flags & (IFF_UP|IFF_RUNNING)) == (IFF_UP|IFF_RUNNING) &&	     ifp != src && BDG_SAMECLUSTER(ifp, real_dst) )/*除了前面的判断接口的正常状态和接口发送对列是否满*/			last = ifp ;									/*主要的判断是对输入和输出接口是否同组接口的判断*/		ifp = TAILQ_NEXT(ifp, if_link) ;/*下一网卡的ifnet结构指针*/		if (ifp == NULL)/*代表发完了last所指向的网卡就不发送了.*/			once = 1 ;    }    IFNET_RUNLOCK();    DEB(bdg_fw_ticks += (u_long)(rdtsc() - ticks) ; bdg_fw_count++ ;		if (bdg_fw_count != 0) bdg_fw_avg = bdg_fw_ticks/bdg_fw_count; )/*后面的括号是前一行的DEB的结束哦,不要搞错了.*/	return m0 ;#undef EH_RESTORE}/* * 初始化工作. */static intbdginit(void){    printf("BRIDGE 020214 loaded\n");/*020214是开发的时间2002-02-14吗?*/    ifp2sc = malloc(BDG_MAX_PORTS * sizeof(struct bdg_softc),/* BDG_MAX_PORTS=128 */		M_IFADDR, M_WAITOK | M_ZERO );  /*即在此申请bdg_softc结构所用的内存,ifp2sc是该结构的首指针*/    if (ifp2sc == NULL)	return ENOMEM ;    bridge_in_ptr = bridge_in; /*存放桥路经分析函数指针*/    bdg_forward_ptr = bdg_forward;/*存放桥转发函数指针*/    bdgtakeifaces_ptr = reconfigure_bridge;/*存放接口设置函数指针*/    n_clusters = 0; /*初始化桥组的数量为0*/    clusters = NULL;/*组结构头部为空*/    do_bridge=0;/*暂时不开bridge*/    bzero(&bdg_stats, sizeof(bdg_stats) );/*桥状态数据统计结构清0*/    bdgtakeifaces_ptr();/*在前4行可看到把reconfigure_bridge的指针放入了,该函数在上面*/    bdg_timeout(0);/*打开定时器*/    return 0 ;}/* *在bridge模块被静态或动态导入时要执行的初始工作,从这开使,基本上是新加的(和4.4版比) */static intbridge_modevent(module_t mod, int type, void *unused)/*只用到了type*/{	int s;	int err = 0 ;	switch (type) {	case MOD_LOAD:     /*模块加载*/		if (BDG_LOADED) {			err = EEXIST;			break ;		}		s = splimp();/*关网络中断*/		err = bdginit();/*执行初始化程序*/		splx(s);/*开网络中断*/		break;	case MOD_UNLOAD:/*在模块卸载时被调用*/#if !defined(KLD_MODULE)		printf("bridge statically compiled, cannot unload\n");		err = EINVAL ;#else		s = splimp();		do_bridge = 0;		bridge_in_ptr = NULL;/*存放桥路经分析函数指针置为空*/		bdg_forward_ptr = NULL;/*存放桥转发函数指针置为空*/		bdgtakeifaces_ptr = NULL;		untimeout(bdg_timeout, NULL, bdg_timeout_h);/*卸载监视器*/		bridge_off();		if (clusters) /*如果你的网卡编了组*/		    free(clusters, M_IFADDR);/*释放掉组结构占用的空间*/		free(ifp2sc, M_IFADDR);/*释放掉bdg_softc结构占用的空间*/		ifp2sc = NULL ;/*并且把指向首bdg_softc结构的指针置为空*/		splx(s);#endif		break;	default:		err = EINVAL ;		break;	}	return err;}static moduledata_t bridge_mod = {	"bridge",	bridge_modevent,	0};DECLARE_MODULE(bridge, bridge_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);/*动态可加载模块*/MODULE_VERSION(bridge, 1);/*说实话,写此解析花了我不少时间,自己理解是不难的,要把理解后自己的思路写成文章说明*//*是比较难,尤其是该程序是很早以前分析过的,你要写成文章,又不得不重新分析一遍.还要照顾*//*到一些基础稍微差一点儿的网友,一些函数需要适当展开.文章中可能有一些错误的地方,希望*//*网友们能指正,谢谢*/

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -