📄 bridge_current.c
字号:
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 + -