通行证│用户名: 密码: 验证码: 验证码,看不清楚?请点击刷新验证码 电信网通铁通移动   在线
资源搜索:
热门搜索:Linux VB C语言 PhotoShop Flash TCP/IP
   首页 | 文章 | 软件 | 动画 | 资源 | 励志 | 骗术 | 论坛 | 邮箱 | 会员中心 | 军事 | 科技 | 博客 | 图片 | 商城 | 最新更新 | 800g资源 | 爱心黑客
您现在的位置: 爱国者黑客 >> 资源 >> 操作系统 >> FreeBSD >> 原理与内核 >> 文章正文
网桥原理及源代码详解
责任编辑:admin   更新日期:2005-8-6
usters %d\n", n_clusters);)
   IFNET_RLOCK();/*新加的,老版本中没有,其定义为mtx_lock(&ifnet_lock),好象是互斥锁,我没有研究过.关于ifnet_lock,是
定义在if.c中,mtx结构,应该是互斥体结构,之所以加上他,应该是和SMP有关系.*/
   TAILQ_FOREACH(ifp, &ifnet, if_link) {/*if_link是ifnet链表中的下一个ifnet*/
struct bdg_softc *b;

if (ifp->if_index >= BDG_MAX_PORTS)/*一般不会出现这种情况*/
   continue; /* */
b = &(ifp2sc[ifp->if_index]);

if ( b->flags & IFF_BDG_PROMISC ) {/*如果网卡在混杂模式就做下面的工作*/
   s = splimp();/*关网络中断*/
   ifpromisc(ifp, 0);/*去掉混杂模式,ifp是要去掉该模式的网卡的ifnet结构指针.*/
   splx(s);/*开网络中断*/
   b->flags &= ~(IFF_BDG_PROMISC|IFF_MUTE) ;
   DEB(printf(">> now %s%d promisc OFF if_flags 0x%x bdg_flags 0x%x\n",
   ifp->if_name, ifp->if_unit,
   ifp->if_flags, b->flags);)
}
b->flags &= ~(IFF_USED) ;/*去掉IFF_USED标志,既不再桥转发了.*/
b->cluster = NULL;/*该卡所在的组的指针也置空.*/
bdg_stats.s[ifp->if_index].name[0] = '\0';/*当然统计信息也要改了.*/
   }
   IFNET_RUNLOCK();/*解互斥锁,看到这应该明白了,互斥锁是在修改ifnet结构和bdg_stats结构时进行保护的.*/

   s = splimp();
   for (i=0; i < n_clusters; i++) {/*所有组*/
free(clusters[i].ht, M_IFADDR);/*把HASH表释放掉*/
free(clusters[i].my_macs, M_IFADDR);/*把在组中记录本机网卡硬件地址的空间释放掉*/
   }
   if (clusters != NULL)
free(clusters, M_IFADDR);/*释放组占用的空间*/
   clusters = NULL;/*置组的头的指针为空*/
   n_clusters =0;/*卡分组的数量也重新置0*/
   splx(s);
}

/*
* 把所有卡都置为混杂模式.
*/
static void
bridge_on(void)
{
   struct ifnet *ifp ;
   int s ;

   IFNET_RLOCK();/*看前面bridge_off函数有说明*/
   TAILQ_FOREACH(ifp, &ifnet, if_link) {/*遍历整个ifnet结构*/
struct bdg_softc *b = &ifp2sc[ifp->if_index];

if ( !(b->flags & IFF_USED) )/*如果没有在使用*/
   continue ;
if ( !( ifp->if_flags & IFF_UP) ) {/*如果接口关闭*/
   s = splimp();
   if_up(ifp);/*打开接口,在if.c中,调用if_route函数,比较复杂,到讲route.c和radix.c的时候再讲*/
   splx(s);
}
if ( !(b->flags & IFF_BDG_PROMISC) ) {/*是否在混杂模式?*/
   int ret ;
   s = splimp();
   ret = ifpromisc(ifp, 1);/*设置混杂模式,1是加上混杂模式,0是取消混杂模式*/
   splx(s);
   b->flags |= IFF_BDG_PROMISC ;/*在该卡的bdg_softc结构中也加上混杂模式*/
   DEB(printf(">> now %s%d promisc ON if_flags 0x%x bdg_flags 0x%x\n",
   ifp->if_name, ifp->if_unit,
   ifp->if_flags, b->flags);)
}
if (b->flags & IFF_MUTE) {/*去掉阻塞*/
   DEB(printf(">> unmuting %s%d\n", ifp->if_name, ifp->if_unit);)
   b->flags &= ~IFF_MUTE;
}
   }
   IFNET_RUNLOCK();
}

/**
*该函数在执行系统命令 sysctl net.link.ether.bridge 和sysctl net.link.ether.bdg_cfg后
*/
static void
reconfigure_bridge(void)
{
   bridge_off();/*先关闭所有卡的桥转发,该函数在上面*/
   if (do_bridge) {/*如果桥转发打开了,就执行分析bdg_cfg设置的字符串*/
if (if_index >= BDG_MAX_PORTS) {
   printf("-- sorry too many interfaces (%d, max is %d),"
" disabling bridging\n", if_index, BDG_MAX_PORTS);
   do_bridge=0;
   return;
}
parse_bdg_cfg();/*分析字符串,该函数在下面*/
bridge_on();/*打开所有卡的桥转发,该函数的描述在上面*/
   }
}

static char bridge_cfg[1024]; /* in BSS so initialized to all NULs */

/*
*分析字符串函数,如:...bdg_cfg=vr0:1,vr1:1,fxp0:2,fxp1:2 也就是说对卡进行分组时,要把卡的名称,设备号,及组号
*分解出来,该函数不和内核有太多牵连,纯粹是字符串分解函数,按照目前的这种分解情况,每块卡只能存在于一个组中,
*如果我们希望他能在多个组中应该怎么办?而且一卡多组的情况是非常有用的,如:
*..........................................|
*..........................................| ...Internet 入口
*................................._____________________
*.................................|.......卡1 ........|
*.................................|.卡2...........卡3.| 透明网桥A
*.................................|___________________|
*...................................|..............|
*...................................|..............|
*................................主机B...........主机C
*说明:网桥A是一个有三块卡的FreeBSD主机,其中卡1通向Internet
*   主机B是认证服务器,主机C是数据服务器.
*   要求从Internet进入的数据包只能到主机B进行认证,认证后该机IP地址存入主机A的缓冲,才能和C通讯
*   也就是说卡1和卡2是同组,卡1同卡3在认证后将是同组,关于A记录已认证IP地址的方法,我认为最好使用
*   patricia树,但在树中只存储主机路由及认证信息.
*/
static void
parse_bdg_cfg()
{
   char *p, *beg ;
   int l, cluster;
   static char *sep = ", \t";

   for (p = bridge_cfg; *p ; p++) {
struct ifnet *ifp;
int found = 0;
char c;
/*该函数在libc库中,index.c中.如下:*/
/*
index(p, ch)
register const char *p, ch;
{
for (;; ++p) {
if (*p == ch)
return((char *)p);
if (!*p)
return((char *)NULL);
}
}
*/


if (index(sep, *p)) /* 由上面的解释可知道,跳过',号' 和 'TAB键 ' */
   continue ;
/* 卡名是由小写字母和数字组成,如:vr0,fxp0,等 */
for ( beg = p ; islower(*p) || isdigit(*p) ; p++ )/*循环开始,是小写或数字时继续*/
   ;
l = p - beg ; /* 得到了名字的长度*/
if (l == 0) /* 长度是0当然是不行的 */
   break ;
if ( *p != ':' ) /* 紧接的后面的字符如果不是":",那么就假定默认为组1 */
   cluster = 1 ;
else
   cluster = strtoul( p+1, &p, 10);/*字符转换为无符号整数*/
c = *p; /*暂时把p指针中的东西保存到C中,因为要把0(字符串结尾)放到*p中,以后再换回*/
*p = '\0';
/*
* 开始在接口列表中查找该网卡名
*/
IFNET_RLOCK(); /* 互斥锁 */
TAILQ_FOREACH(ifp, &ifnet, if_link) {/*遍历整个ifnet结构*/
   char buf[IFNAMSIZ];

   snprintf(buf, sizeof(buf), "%s%d", ifp->if_name, ifp->if_unit);/*把卡名字和子设备号合并放到buf中,如:名字=vr,子设备号=0,合并后为vr0*/
   if (!strncmp(beg, buf, max(l, strlen(buf)))) {/*比较我们参数的设备名和buf中的相等吗?*/
struct bdg_softc *b = &ifp2sc[ifp->if_index];
if (ifp->if_type != IFT_ETHER && ifp->if_type != IFT_L2VLAN) {/*不是以太网卡*/
   printf("%s is not an ethernet, continue\n", buf);
   continue;
}
if (b->flags & IFF_USED) {/*如果接口卡中有该标志,那他已经用于bridge了.*/
   printf("%s already used, skipping\n", buf);
   break;
}
b->cluster = add_cluster(htons(cluster), (struct arpcom *)ifp);/*调用前面的函数,把卡加入到组中.*/
b->flags |= IFF_USED ;/*加上bridge开始使用标志*/
sprintf(bdg_stats.s[ifp->if_index].name,             /*打印信息到屏幕*/
"%s%d:%d", ifp->if_name, ifp->if_unit, cluster);

DEB(printf("--++  found %s next c %d\n",
   bdg_stats.s[ifp->if_index].name, c);)
found = 1;/*置发现标志*/
break ;
   }
}
IFNET_RUNLOCK();/*解互斥锁*/
if (!found)/*没找到接口,可能是你参数输入错误*/
   printf("interface %s Not found in bridge\n", beg);
*p = c;/*换回来*/
if (c == '\0')
   break; /* 到了字符串结尾 */
   }
}


/*
* 如果使用的是SYSCTL_PROC来定义一个控制节点,那么第7个参数是一个处理函数指针,以下这两个函数都是处理函数
*/
static int
sysctl_bdg(SYSCTL_HANDLER_ARGS) /*以下是在sysctl.h中关于SYSCTL_HANDLER_ARGS的说明*/
/*#define SYSCTL_HANDLER_ARGS struct sysctl_oid *oidp, void *arg1, int arg2, struct sysctl_req *req   */
{
   int error, oldval = do_bridge ;/*把do_bridge放到oldval中暂时保存*/

   error = sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req);/*该函数把数据放到全局结构变量oidp中*/
/*由于oidp中有指向do_bridge的指针,所以*/
/*sysctl中的=xxx的值将放到do_bridge中*/
   DEB( printf("called sysctl for bridge name %s arg2 %d val %d->%d\n",
oidp->oid_name, oidp->oid_arg2, oldval, do_bridge); )

   if (oldval != do_bridge)/*如果和原来的值不同,就重新设置bridge*/
reconfigure_bridge();
   return error ;
}

/*
* 和上面是一样的,这里就不多解释了.他们不同之处是一个是整数型,一个是字符串型
*/
static int
sysctl_bdg_cfg(SYSCTL_HANDLER_ARGS)
{
   int error = 0 ;
   char old_cfg[1024] ;/*不同的地方,即是字符串*/

   strcpy(old_cfg, bridge_cfg) ;/*字符串拷贝,已经检查过,没有溢出产生.如有兴趣,可查LIBC库*/

   error = sysctl_handle_string(oidp, bridge_cfg, oidp->oid_arg2, req);
   DEB(
printf("called sysctl for bridge name %s arg2 %d err %d val %s->%s\n",
oidp->oid_name, oidp->oid_arg2,
error,
old_cfg, bridge_cfg);
)
   if (strcmp(old_cfg, bridge_cfg))
reconfigure_bridge();
   return error ;
}

static int
sysctl_refresh(SYSCTL_HANDLER_ARGS)
{
   if (req->newptr)
reconfigure_bridge();/*该函数在上面*/

   return 0;
}


SYSCTL_DECL(_net_link_ether);/*申明一节点,表示下面的SYSCTL将继承该节点*/
SYSCTL_PROC(_net_link_ether, OID_AUTO, bridge_cfg, CTLTYPE_STRING|CTLFLAG_RW,
   &bridge_cfg, sizeof(bridge_cfg), &sysctl_bdg_cfg, "A",
   "Bridge configuration");/*网卡的分组,"A"代表参数是字符串,sysctl_bdg_cfg是处理的函数的名称*/

SYSCTL_PROC(_net_link_ether, OID_AUTO, bridge, CTLTYPE_INT|CTLFLAG_RW,
   &do_bridge, 0, &sysctl_bdg, "I", "Bridging");/*对桥转发开关的控制,sysctl_bdf是控制函数,"I"代表参数是整数型*/

SYSCTL_INT(_net_link_ether, OID_AUTO, bridge_ipfw, CTLFLAG_RW,
   &bdg_ipfw,0,"Pass bridged pkts through firewall");/*对桥的防火墙的开关*/

SYSCTL_INT(_net_link_ether, OID_AUTO, bridge_ip

上一页  [1] [2] [3] [4] [5] 下一页

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