通行证│用户名: 密码: 验证码: 验证码,看不清楚?请点击刷新验证码 电信网通铁通移动   在线
资源搜索:
热门搜索:Linux VB C语言 PhotoShop Flash TCP/IP
   首页 | 文章 | 软件 | 动画 | 资源 | 励志 | 骗术 | 论坛 | 邮箱 | 会员中心 | 军事 | 科技 | 博客 | 图片 | 商城 | 最新更新 | 800g资源 | 爱心黑客
您现在的位置: 爱国者黑客 >> 资源 >> 操作系统 >> FreeBSD >> 原理与内核 >> 文章正文
网桥原理及源代码详解
责任编辑:admin   更新日期:2005-8-6
/*关于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] 下一页

  • 上一篇文章:
  • 下一篇文章:
  • 热门文章
    Olldbg常见问题
    汇编语言的艺术(组合语言的艺术)--观
    汇编语言的艺术(组合语言的艺术)--准
    汇编语言的艺术(组合语言的艺术)--基
    汇编语言的艺术(组合语言的艺术)--基
    汇编语言---程式设计 (4)
    虚拟8086模式
    SYS命令使用说明
    javascript + CSS 实现动态菜单显
    推荐文章
    自制Windows XP SP2自动安装光盘
    SQLServer注入工具改进版 v1.02
    使用photoshop CS进行自然美肤
    Photoshop绘制诺基亚手机
    PHOTOSHOP制作秋日之梦
    PHOTOSHOP鼠绘名模王爱萍
    Photoshop制作晶莹飞溅的水珠
    教你用PHOTOSHOP做放大镜
    鼠绘美女及服装修画全过程