/*关于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 int bdginit(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 int bridge_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);/*释放掉b上一页 [1] [2] [3] [4] [5] 下一页 |