📄 bridge_current.c
字号:
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 intsysctl_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 intsysctl_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 intsysctl_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_ipf, CTLFLAG_RW, &bdg_ipf, 0,"Pass bridged pkts through IPFilter");/*包过滤的开关*//*因为下面都是控制整数型变量,所以做一个宏*/#define SY(parent, var, comment) \ static int var ; \ SYSCTL_INT(parent, OID_AUTO, var, CTLFLAG_RW, &(var), 0, comment);/*以下的SYSCTL大都用于防火墙控制*/int bdg_ipfw_drops;SYSCTL_INT(_net_link_ether, OID_AUTO, bridge_ipfw_drop, CTLFLAG_RW, &bdg_ipfw_drops,0,"");int bdg_ipfw_colls;SYSCTL_INT(_net_link_ether, OID_AUTO, bridge_ipfw_collisions, CTLFLAG_RW, &bdg_ipfw_colls,0,"");SYSCTL_PROC(_net_link_ether, OID_AUTO, bridge_refresh, CTLTYPE_INT|CTLFLAG_WR, NULL, 0, &sysctl_refresh, "I", "iface refresh");#if 1 SY(_net_link_ether, verbose, "Be verbose");SY(_net_link_ether, bdg_split_pkts, "Packets split in bdg_forward");SY(_net_link_ether, bdg_thru, "Packets through bridge");SY(_net_link_ether, bdg_copied, "Packets copied in bdg_forward");SY(_net_link_ether, bdg_dropped, "Packets dropped in bdg_forward");SY(_net_link_ether, bdg_copy, "Force copy in bdg_forward");SY(_net_link_ether, bdg_predict, "Correctly predicted header location");SY(_net_link_ether, bdg_fw_avg, "Cycle counter avg");SY(_net_link_ether, bdg_fw_ticks, "Cycle counter item");SY(_net_link_ether, bdg_fw_count, "Cycle counter count");#endifSYSCTL_STRUCT(_net_link_ether, PF_BDG, bdgstats, CTLFLAG_RD, &bdg_stats , bdg_stats, "bridge statistics");static int bdg_loops ;static voidbdg_timeout(void *dummy){ static int slowtimer; /*会初始化为0*/ if (do_bridge) {/*桥转发打开了就执行下面的*/ static int age_index = 0 ; int l = age_index + HASH_SIZE/4 ;/*l=2048,因为HASH表内放的是指针,每个指针占用4字节,所以/4表示有多少个指针*/ int i; /* */ if (l > HASH_SIZE)/*这时候l=2048,怎么可能>HASH_SIZE(9182)*/ l = HASH_SIZE ; for (i=0; i<n_clusters; i++) {/*遍历每个组*/ bdg_hash_table *bdg_table = clusters[i].ht;/*该网卡的HASH表*/ for (; age_index < l ; age_index++)/*遍历整个HASH表*/ if (bdg_table[age_index].used) /*如果该成员被使用了*/ bdg_table[age_index].used = 0 ;/*清除掉,但我不清楚为什么不同时清除成员name,如果在此时bridge_in正接收*/ /*到包,会把name保存到old变量中,会不会出问题呢?(可看看下面的bridge_in)*/ else if (bdg_table[age_index].name) { bdg_table[age_index].name = NULL ; } } if (age_index >= HASH_SIZE) age_index = 0 ; if (--slowtimer <= 0 ) {/*经过5次的bdg_timeout后,才为0*/ slowtimer = 5 ;/*由于在初始化时,slowtimer被置为0,所以在函数第一次被调用时,次处总会被执行*/ bridge_on() ; /* 打开桥的一些设置,看上面的该函数说明.*/ bdg_loops = 0 ; } } bdg_timeout_h = timeout(bdg_timeout, NULL, 2*hz );/*启动监视器*/}/* * 查找包的目的地,返回值如下: * BDG_BCAST 广播包,这种包是要发送到每一个接口的 * BDG_MCAST 多播包 * BDG_LOCAL 该包是发送给本机的一个包,如果该机做为透明网桥放火墙,应该拦截该包,并做特殊处理 * BDG_DROP 该包必须抛弃 * other 其他类型的包 * */static __inlinestruct ifnet *bridge_dst_lookup(struct ether_header *eh, struct cluster_softc *c){/*eh是以太网包的头部*/ struct ifnet *dst ; int index ; struct bdg_addr *p ; bdg_hash_table *bt; /*HASH表入口指针 */ if (IS_ETHER_BROADCAST(eh->ether_dhost))/*是广播地址吗?*/ return BDG_BCAST ;/*是的就返回广播地址标志*/ if (eh->ether_dhost[0] & 1)/*硬件地址的最后一位是1吗?即是多播地址吗*/ return BDG_MCAST ;/*是的就返回多播地址标志*/ /* * 以下循环是查看本机的所有网卡的硬件地址是否和eh中的目的地址相同,相同就是发送到本机的. */ for (index = c->ports, p = c->my_macs; index ; index--, p++ )/*在cluster_softc结构中遍历本机所有网卡*/ if (BDG_MATCH(p->etheraddr, eh->ether_dhost) )/*和这块卡的硬件地址相同吗?*/ return BDG_LOCAL ;/*相同就返回本地的标志*/ /* * 如果以上都不是,那么在HASH表中查找一下,目的地和本机的那块卡相连. */ index= HASH_FN( eh->ether_dhost );/*HASH查找,精华部分,查到该地址在HASH表的第index个偏移*/ bt = &(c->ht[index]);/*定位该HASH条目的入口*/ dst = bt->name;/*得到与目的地机器相连的本机某网卡的ifnet结构指针*/ if ( dst && BDG_MATCH( bt->etheraddr, eh->ether_dhost) ) return dst ;/*返回该指针*/ else return BDG_UNKNOWN ;/*否则没查到,我不知道什么时候将出现该情况.*/}/** * bridge_in() 函数由if_ethersubr.c中的ether_input函数调用,在该函数中会判断bridge功能是否打开,如果打开 * 既调用该函数.ether_input函数会根据返回值决定是否调用我们即将讲的下一个函数bridge_forward. * 函数入口: * eh 进入以太网包的以太网包头. * ifp ifnet结构,即该包是从哪块卡进来的.(ifnet包含了卡的所有信息) * * 函数返回: 目的地要进过本机哪块网卡发送,即那块卡的ifnet结构指针.说明如下 * BDG_BCAST 广播地址 * BDG_MCAST 多播地址 * BDG_LOCAL 不需要转发,该包是发给本机的. * BDG_DROP 该包要丢弃 * ifp 即将发送的网卡的ifnet指针. * */static struct ifnet *bridge_in(struct ifnet *ifp, struct ether_header *eh){ int index; struct ifnet *dst , *old ; bdg_hash_table *bt; /* 将用来放置当前HASH表中该地址的HASH指针的位置 */ int dropit = BDG_MUTED(ifp) ; /* * HASH_FN宏在上面的函数中已经有描述.不过在这里是查看对方的MAC地址是否以前有记录(即在HASH表中查找) */ index= HASH_FN(eh->ether_shost);/*这中HASH的查找方法是否有问题,是否会产生同义词?他的算法是MAC地址的*/ /*[1]和[2]两字节互补后在和HASH长度-1相与,那他认为是唯一值,这是不可靠的.*/ /*我们可以利用该情况生成同义词,进行HASH覆盖,带着次问题我又查看了OpenBSD*/ /*的源代码,他的算法又是另外一种,请看OpenBSD的Alley算法(Bob Jenkins):*/ /* #define mix(a,b,c) \ 本人因能力有限,看不懂OpenBSD的算法 do { \ a -= b; a -= c; a ^= (c >> 13); \ b -= c; b -= a; b ^= (a << 8); \ c -= a; c -= b; c ^= (b >> 13); \ a -= b; a -= c; a ^= (c >> 12); \ b -= c; b -= a; b ^= (a << 16); \ c -= a; c -= b; c ^= (b >> 5); \ a -= b; a -= c; a ^= (c >> 3); \ b -= c; b -= a; b ^= (a << 10); \ c -= a; c -= b; c ^= (b >> 15); \ } while (0) u_int32_t bridge_hash(struct bridge_softc *sc, struct ether_addr *addr) 下面的更看不懂了,OpenBSD的哈稀函数 { u_int32_t a = 0x9e3779b9, b = 0x9e3779b9, c = sc->sc_hashkey; b += addr->ether_addr_octet[5] << 8; b += addr->ether_addr_octet[4]; a += addr->ether_addr_octet[3] << 24; a += addr->ether_addr_octet[2] << 16; a += addr->ether_addr_octet[1] << 8; a += addr->ether_addr_octet[0]; mix(a, b, c); return (c & BRIDGE_RTABLE_MASK); } 如果你不懂得以上的算法,那么桥的技术应该说还没精通.本人就是这样,不是谦虚.计算机搞 到后面基本上就是拼算法的先进与合理性. */ bt = &(ifp2sc[ifp->if_index].cluster->ht[index]);/*当然假定index没有同义词,那么就 /*可以找到该MAC地址在HASH表的入口了*/ bt->used = 1 ;/*该MAC的HASH指针开始启用.*/ old = bt->name ;/*暂时存放到old中,记住,大家看看timeout中对bt->name的清除是多么的重要啊*/ if ( old ) { /* 为真就是以前就填充过,说明该机器以前发过包通过本机. */ if (!BDG_MATCH( eh->ether_shost, bt->etheraddr) ) {/*看看上次对方机器的包的源硬件地址和本次的地址相同吗?*/ bdg_ipfw_colls++ ;/*不同,有问题,其实这里的操作有点类似ARP中的.*/ bt->name = NULL ; } else if (old != ifp) {/*源地址是对的,但本机接收网卡发生了更换(重新设置了网卡)或源机器移动了.环回也有可能*/ bt->name = ifp ; /* 指向新的正确的接收网卡的ifnet结构 */ printf("-- loop (%d) %6D to %s%d from %s%d (%s)\n", bdg_loops, eh->ether_shost, ".", ifp->if_name, ifp->if_unit, old->if_name, old->if_unit, BDG_MUTED(old) ? "muted":"active");/*打印信息到屏幕*/ dropit = 1 ;/*在本次转发中是否转发,1是不转发,就是说在发现上面的那种情况后,不转发该包*/ if ( !BDG_MUTED(old) ) { if (++bdg_loops > 10) BDG_MUTE(old) ; } } } /* * 把发送方的地址写到HASH表中. */ if (bt->name == NULL) {/*因为发送方是第一次发送包.*/ DEB(printf("new addr %6D at %d for %s%d\n", eh->ether_shost, ".", index, ifp->if_name, ifp->if_unit);) bcopy(eh->ether_shost, bt->etheraddr, 6);/*把发送方的以太网硬件地址放到HASH表中该发送方HASH索引的地方.*/ bt->name = ifp ; } dst = bridge_dst_lookup(eh, ifp2sc[ifp->if_index].cluster);/*调用上面说明的函数来查找目的地要经过的本机网卡.*/ /* * BDG_STAT是对bdg_port_stat结构进行操作,统计各种包的in的数量(做++操作) */ BDG_STAT(ifp, BDG_IN);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -