首页 > 代码库 > DHCP分析
DHCP分析
一 DHCP简介
DHCP是DynamicHostConfigurationProtocol之缩写﹐它的前身是BOOTP(用网络抓包软件过滤时,要输入bootp)。DHCP可以说是BOOTP的增强版本﹐它分为两个部份(C/S架构): 一个是服器端,而另一个是客户端。所有的IP网路设定资料都由DHCP伺服器集中管理﹐并负责处理客户端的DHCP要求﹔而客户端则会使用从伺服器分配下来的IP环境资料。比较起BOOTP,DHCP透过"租约"的概念,有效且动态的分配客户端的TCP/IP设定,而且,作为兼容考量,DHCP也完全照顾了BOOTPClient的需求。
DHCP支持三种类型的地址分配:
1. 自动分配方式: DHCP给主机指定一个永久的IP地址,一旦DHCP客户端第一次成功的从DHCP伺服器端租用到IP位址之后﹐就永远使用这个位址 2. 动态分配方式: DHCP给主机指定一个有"时间限制(租约)"的IP地址,到时间(租约到期)或主机明确表示放弃这个地址(主机主动断开连接)时,这个地址可以被其他的主机使用,当然,客户端可以比其它主机更优先的延续(renew)租约,或是租用其它的IP位址 3. 手工分配方式: 主机的IP地址是由网络管理员指定的,它可以按照MAC地址来固定的分配IP位址﹐DHCP只是把指定的IP地址告诉主机,相当于将某个MAC和IP进行了静态绑定。
除了分配IP之外﹐DHCP还可以帮客户端指定IP环境:
1. gateway(默认网关)
2. netmask(默认子网掩码)
3. DNSServer(DNS服务器IP)
4. WINSServer﹑等等项目
DHCP有3个端口,其中:
1. UDP67: DHCP Server服务端口 2. UDP68: DHCP Client服务端口 3. UDP546: 用于DHCPv6 Client,而不用于DHCPv4,是为DHCP failover服务,这是需要特别开启的服务,DHCP failover是用来做"双机热备"的。
二 DHCP协议数据包格式
1. 链路层头: 承载报文的链路层信息头,常见的有Ethernet_II格式、802.1Q格式、 IEEE802.3格式、令牌环链路层头格式等。 2. IP头: 标准的IP协议头,IPV4中长度为20bytes,包括了SrcIp,DstIp等信息。 3. UDP头: 8bytes,包括了SrcPort,DstPort,报文长度及UDP校验和等信息。 4. DHCP报文:具体的DHCP报文内容。
三 DHCP的作用
在局域网中,用户电脑都需要IP地址才使用网络服务,但是客户并不都会配置IP地址,这时,可以在网络中部署一个DHCP服务器,用来给这些客户主机动态的分配IP。
所有DHCP的客户端,在向DHCP服务器租用到地址后,会在DHCP服务器端留下租用信息,网络管理员可以根据这些分配信息统一管理这些客户。
四 DHCP的工作流程
根据这四个数据包来看,DHCP的工作流程如下图:
第一步:
DHCP客户端主动发起DHCP Discover包,用来寻找DHCP服务器,其中:
- 源MAC是自己的MAC地址,目的MAC是FFFF.FFFF.FFFF的广播
- 源IP是0.0.0.0(现在还没有IP,就用全0地址),目的IP是255.255.255.255的三层广播
因为DHCP服务器在哪里还不知道,所以使用广播来寻找,广播会泛洪到整个网段中;
第二步:
DHCP服务器收到客户端发的DHCP Discover之后,会在自己的地址池中拿出一个没有分配的地址以及配套的参数(如:掩码、DNS、网关、域名、租期……),然后以一个DHCP Offer包发送出去。
这个DHCP Offer数据包的地址如下:
- 源MAC是DHCP服务器的MAC,目的MAC是FFFF.FFFF.FFFF的广播
- 源IP是DHCP服务器的IP,目的IP是255.255.255.255的广播
这时客户端还没有获得IP,DHCP服务器端现在还无法定位客户端,所以用广播来回应。
这一步既可以是单播,也可以是广播。因为第一步服务器肯定知道了客户端MAC,知道MAC就可以只发个单波就可以(交换机MAC-端口表,这样就可以单波传送特定端口了)
第三步:
客户端收到这个DHCP Offer后,会再发出一个DHCP Request给服务器来申请这个Offer中包含的地址。
这个时候,客户端还没有正式拿到地址,所以还需要向DHCP服务器申请。
- 这时客户端的源IP还是0.0.0.0,目的IP还是255.255.255.255
- 源MAC是客户端的MAC,目的MAC是FFFF.FFFF.FFFF广播包
第四步:
服务器收到客户端的请求后,会发出一个DHCP ACK用来确认这个IP地址可以分配给这个客户端。
客户端收到第四个DHCP ACK数据包才算正式拿到了这个IP。
五 LWIP中使用DHCP
初始化IP,子网掩码,默认网关为0
ipaddr.addr = 0; netmask.addr = 0; gw.addr = 0;
创建DHCP任务
void lwip_comm_dhcp_creat(void) { OS_CPU_SR cpu_sr; OS_ENTER_CRITICAL();
OSTaskCreate(lwip_dhcp_task,(void*)0,(OS_STK*)&LWIP_DHCP_TASK_STK[LWIP_DHCP_STK_SIZE-1],LWIP_DHCP_TASK_PRIO); OS_EXIT_CRITICAL();
}
编写DHCP处理任务既lwip_dhcp_task
void lwip_dhcp_task(void *pdata) { u32 ip=0,netmask=0,gw=0; dhcp_start(&lwip_netif);//开启DHCP lwipdev.dhcpstatus=0; //正在DHCP printf("正在查找DHCP服务器,请稍等...........\r\n"); while(1) { printf("正在获取地址...\r\n"); ip=lwip_netif.ip_addr.addr; //读取新IP地址 netmask=lwip_netif.netmask.addr;//读取子网掩码 gw=lwip_netif.gw.addr; //读取默认网关 if(ip!=0) //当正确读取到IP地址的时候 { lwipdev.dhcpstatus=2; //DHCP成功 /*打印获得的信息*/
}
else if(lwip_netif.dhcp->tries>LWIP_MAX_DHCP_TRIES) //通过DHCP服务获取IP地址失败,且超过最大尝试次数 { //获得失败,做下一步处理
} delay_ms(250); //延时250ms } lwip_comm_dhcp_delete();//删除DHCP任务 }
把LWIP_DHCP设置为1,即可使用LWIP的DHCP功能.
以上代码主要的就一步:
dhcp_start(&lwip_netif);//开启DHCP
err_t dhcp_start(struct netif *netif) { ...... //先检查是否已经存在DHCP客户端了,如果没有 //则先创建,如果有,则重新利用 /* set up local and remote port for the pcb */ udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT); udp_connect(dhcp->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT); /* set up the recv callback and argument */ udp_recv(dhcp->pcb, dhcp_recv, netif); /* (re)start the DHCP negotiation */ result = dhcp_discover(netif); ...... }
在LWIP的DHCP中有个问题:有两个客户端同时向局域网发送DHCP请求,结果获取到的IP竟然是相同的。
原因在于DHCP事务ID——xid。DHCP客户端生成一个随机数事务ID,并记录下来,然后把它插入到xid字段。客户端发送DHCP DISCOVER广播报文。服务器从DHCP DISCOVER消息解析得到xid值,把xid值插入到DHCP OFFER消息的xid字段,发送DHCP OFFER报文到请求客户端。如果DHCP OFFER消息中的xid值与最近发送DHCP DISCOVER消息中的xid值不同,那么客户端必须忽略这个DHCP OFFER。接收到的任何DHCPACK必须悄悄地丢弃。
而LWIP在默认设置DHCP事务ID为static u32_t xid = 0xABCD0000;而在每创建requset信息时,简单的使其加1。这样两个客户端发送送出去的 discover报文中的Transaction ID是相同的,并且当时的那个路由器是以广播包的方式发送offer报文和回应ack报文的。那么当我的两个客户端都收到这个广播包的时候,并且发现offer和ack报文中的Transaction ID和自己发出去的是一样的,那么就理所当然的认为这个IP是分配给自己的,所以导致两者的IP是一样的。所以,如果片子没有随机数发生器的话,就根据片子的ID弄一个数替代xid = 0xABCD0000吧。
DHCP分析