抓包发现我的路由器,如果没获取IP,会间隔1.5s发送一次DHCP DISCOVER报文,这个肯定是个BUG
正常的DHCP服务的工作过程如下:
如果在局域网中部署了DHCP服务器,并且客户端设置为自动获得IP地址。这样当DHCP客户端第一次登录网络的时候,也 就是客户端发现本机上没有任何IP数据设定,它会向网络发出一个DHCP discover封包。因为客户端还不知道自己属于哪一个网络,所以封包的来源地址会为 0.0.0.0,而目的地址则为255.255.255.255,然后再附上DHCP discover的信息,向网络进行广播。在windows的预设默认情况下,DHCP discover的等待时间预设为1秒,也就是当客户端将第一个DHCP discover封包送出去之后,在1秒之内没有得到响应的话,就会进行第二次DHCP discover广播。若一直得不到响应的情况下,客户端一共会有四次DHCP discover广播(包括第一次在内)。除了第一次会等待1秒之外,其余三次的等待时间分别是9、13、16秒。如果都没有得到DHCP服务器的响应, 客户端则会显示错误信息,宣告DHCP discover的失败。之后,基于使用者的选择,系统会继续在5分钟之后再重复一次DHCP discover的过程;当DHCP服务器收到DHCP客户机广播的DHCP discover信息后,它会向DHCP客户机发送DHCP offer信息,其中包括一个可租用的IP地址。一旦客户机收到DHCP offer信息,就将使用服务器所提供的IP地址。(http://blog.chinaunix.net/uid-14239467-id-93140.html)那么如何解决呢?
刚开始以为控制DHCP就是在dhcp.c文件里面,但是printf打印却没有相关信息,后来找到了相关文件,是dhcp_port.c文件
刚开始运行的是这个函数,当然可以找到在set_wan.c里面有:
if (wan_type == DHCP_CLIENT) //当为DHCP客户端时候
wandhcpc_startup(wan_interface, dns_mode);
函数注释如下:
__externC int wandhcpc_startup(char *if_name, int dns_mode)
{
diag_printf("enter dhcpc_startup\n");//在串口打印信息里面可以找到这条打印信息
if (wandhcpc_started==1)
{
diag_printf("DHCPC has already been startup\n");//串口里面没有找到这条打印信息
return(-1);
}
unsigned char flag=0;
flag |= 0x01;
if(dns_mode==0) //auto dns
flag |= 0x02;
sprintf(wan_ifname, "dhcpc %s", if_name);
if (wandhcpc_running==0)
{
cyg_thread_create(DHCPC_THREAD_PRIORITY,//创建线程,DHCPC_THREAD_PRIORITY为线程优先级,找到定义为16
dhcpc_main, //线程函数指针,即为相应的函数入口
flag, //线程函数的入口参数
wan_ifname, //线程名
wandhcpc_stack,//堆栈基址
sizeof(wandhcpc_stack),//堆栈大小
&wandhcpc_thread,//线程句柄
&wandhcpc_thread_object);//保存线程内核数据结构的存储空间
diag_printf("Starting WAN DHCPC thread\n");//串口有这条打印信息
cyg_thread_resume(wandhcpc_thread);//线程进入RUNNING状态
wandhcpc_started=1;
return(0);
}
else
{
diag_printf("WAN DHCPC is already running\n");
return(-1);
}
}
跳往:
void dhcpc_main(cyg_addrword_t data)
{
while(1)
{
do_dhcp(data);//可见这个函数才是关键
sleep(2);
}
}
再跳往int do_dhcp(cyg_addrword_t data),这个函数有点长
if(((cyg_uint8)data) & 0x01)//data是入口函数,在wandhcpc_startup函数里对应flag,调试知道((cyg_uint8)data) & 0x01为真
{
intf=wan_ifname+6;
res=&wan_bootp_data;
pstate=&wan_dhcpstate;
lease=&wan_lease;
wandhcpc_running=1;
}
else
{
intf=lan_ifname+6;
res=&lan_bootp_data;
pstate=&lan_dhcpstate;
lease=&lan_lease;
landhcpc_running=1;
}
在这个函数里发现reset_timeout( &tv, &timeout_scratch );留意一下
接着进入while循环
while ( 1 ) {
if(recon_flag==1)
{
diag_printf("%s:%d RECONNECT!\n",__FUNCTION__,__LINE__);//没有发现打印这个信息,所以recon_flag==0应该
no_lease( lease );
do_dhcp_down_net( intf, res, &oldstate, lease );
oldstate=DHCPSTATE_INIT;
syslogAll_printk("DHCPC disconnect!\n");
syslogAll_printk("DHCPC reinit!\n");
goto RE_CONNECT;
}
diag_printf("%s:%d\n",__FUNCTION__,__LINE__);//打印了这条信息,__FUNCTION__是正在运行的函数,__LINE__是在文件的哪一行
if ( DHCPSTATE_DO_RELEASE != *pstate
&& DHCPSTATE_NOTBOUND != *pstate
&& DHCPSTATE_FAILED != *pstate ) {
cyg_uint8 lease_state;
diag_printf("%s:%d\n",__FUNCTION__,__LINE__);//有这个打印信息,证明进入了这个if语句
if ( lease_state & DHCP_LEASE_EX ) { }//调试信息显示没有进入这个if语句,三个都没进入
else if ( lease_state & DHCP_LEASE_T2 ) {
else if ( lease_state & DHCP_LEASE_T1 ) {
然后进入switch ( *pstate ) {
case DHCPSTATE_INIT: //DHCP的init状态,在这个状态里发送DISCOVER报文
bzero(xmit, sizeof(*xmit));
xmit->bp_op = BOOTREQUEST; //操作码
xmit->bp_htype = HTYPE_ETHERNET; //硬件地址类型
xmit->bp_hlen = IFHWADDRLEN; //硬件地址长度
xmit->bp_xid = xid; //传输ID,每次好像都不一样,应该是个随机数吧
xmit->bp_secs = cyg_current_time() / 100;// 客户机所使用的地址,在最近一次地址获取/地址更新后所经过的时间
*pstate = DHCPSTATE_SELECTING;//说明接着进入DHCPSTATE_SELECTING的case项,即进入DHCP的SELECTING状态
在case DHCPSTATE_SELECTING:选项里发现:
if ( ! next_timeout( &tv, &timeout_scratch ) ) {
//// *pstate = DHCPSTATE_FAILED;
*pstate =DHCPSTATE_INIT;
break;
那么现在基本定位了发送DISCOVER报文时间间隔的地方:
static inline void reset_timeout( struct timeval *ptv, struct timeout_state *pstate )
{
#if 0
timeout_random++;
pstate->countdown = 4; // initial fast retries
pstate->secs = 3 + (timeout_random & 3);
ptv->tv_sec = 0;
ptv->tv_usec = 65536 * (2 + (timeout_random & 3)); // 0.1 - 0.3S, about
#endif
ptv->tv_sec=1;
ptv->tv_usec=500000;
}
static inline int next_timeout( struct timeval *ptv, struct timeout_state *pstate )
{
#if 0
if ( 0 < pstate->countdown-- )
return true;
if ( 0 == ptv->tv_sec )
ptv->tv_sec = pstate->secs;
else {
timeout_random++;
pstate->secs = ptv->tv_sec * 2 - 2 + (timeout_random & 3);
pstate->countdown = 2; // later fast retries
ptv->tv_sec = 0;
}
// If longer, too many tries...
return pstate->secs < CYGNUM_NET_DHCP_MIN_RETRY_TIME;
#endif
ptv->tv_sec=1;
ptv->tv_usec=500000;
return 1;
}
问题应该就在这里,但是为什么原厂#if 0 #endif把中间的代码屏蔽掉呢?
是否有特殊原因,这一点不好确认,但是1.5S发送一次报文也不会造成网络风暴吧,而且只要获取到IP了,DISCOVER报文就会停止,那么这是算一个bug呢,还是某种优化处理,也不确定。得去和市场部和测试部商量一下了,关于这个问题。