STM32F103RCT6 W5500 DHCP 实现过程分析

12 篇文章 8 订阅
1 篇文章 0 订阅

对于DHCP的介绍,引用一下百度百科的相关内容:

W5500传输网络数据,通过TCP、UDP来实现,也必须要有IP地址。要么给W5500设置一个固定写死的IP地址,这样的话,在不同的网络环境下,并不通用。有的局域网段的IP地址是 192.168.1.XXX;还有的局域网段IP地址是 10.0.0.XXX等等,写死固定IP地址,显然不太合适。那么DHCP的应用,就可以解决复杂的网络环境的IP地址获取。

DHCP是在UDP的基础上实现的,DHCP有它的协议。关于协议内容,可以参考 《DHCP协议详解》

STM32 W5500要实现DHCP Client的功能,要具备以下几点:

1、STM32驱动W5500入网,可以PING通,可参考 《STM32F103RC驱动W5500入网,并可ping通

2、STM32 W5500 UDP发数据测试, 可参考 《STM32 W5500 UDP Client 发送数据

3、代码实现DHCP报文的封装和解析。

按照DHCP协议实现以下过程,就可以获取到IP地址,网关,子网掩码的信息了:

代码实现 dhcp.c:

#include "w5500.h"
#include "socket.h"

#include "dhcp.h"
#include "utility.h"
#include "w5500_conf.h"

u8    DHCP_SIP[4] = {0,};											/*ÒÑ·¢ÏÖµÄDNS·þÎñÆ÷µØÖ·*/
u8    DHCP_REAL_SIP[4] = {0,};
RIP_MSG*  pRIPMSG;

u8 func_pack_discover_info(u8 *buf_dhcp, u16 len_buf, u8 *mac, u32 xid)
{
	u16 i = 0;
	u8 host_name[24];
	pRIPMSG = (RIP_MSG*)buf_dhcp;	
	memset(buf_dhcp, 0, len_buf);
	
	pRIPMSG->op = DHCP_BOOTREQUEST;
	pRIPMSG->htype = DHCP_HTYPE10MB;
	pRIPMSG->hlen = DHCP_HLENETHERNET;
	pRIPMSG->hops = DHCP_HOPS;
	pRIPMSG->xid = htonl(xid);
	pRIPMSG->secs = htons(DHCP_SECS);
	pRIPMSG->flags =htons(DHCP_FLAGSBROADCAST);
	
	memcpy(pRIPMSG->chaddr, mac, 6);
	
	/* MAGIC_COOKIE */
	pRIPMSG->OPT[i++] = (u8)((MAGIC_COOKIE >> 24)& 0xFF);
	pRIPMSG->OPT[i++] = (u8)((MAGIC_COOKIE >> 16)& 0xFF);
	pRIPMSG->OPT[i++] = (u8)((MAGIC_COOKIE >> 8)& 0xFF);
	pRIPMSG->OPT[i++] = (u8)(MAGIC_COOKIE& 0xFF);
	
	/* Option Request Param. */
	pRIPMSG->OPT[i++] = dhcpMessageType;
	pRIPMSG->OPT[i++] = 0x01;
	pRIPMSG->OPT[i++] = DHCP_DISCOVER;
		
	/*Client identifier*/
	pRIPMSG->OPT[i++] = dhcpClientIdentifier;
	pRIPMSG->OPT[i++] = 0x07;
	pRIPMSG->OPT[i++] = 0x01;
	pRIPMSG->OPT[i++] = mac[0];
	pRIPMSG->OPT[i++] = mac[1];
	pRIPMSG->OPT[i++] = mac[2];
	pRIPMSG->OPT[i++] = mac[3];
	pRIPMSG->OPT[i++] = mac[4];
	pRIPMSG->OPT[i++] = mac[5];
	
	// host name
	pRIPMSG->OPT[i++] = hostName;
	// set the host name
	sprintf((char*)host_name, "%s-%02X%02X%02X", DEVICE_ID, mac[3], mac[4], mac[5]);
	
	pRIPMSG->OPT[i++] = (u8)strlen((char*)host_name);
	
	strcpy((char*)(&(pRIPMSG->OPT[i])),(char*)host_name);
	
	i+=(u16)strlen((char*)host_name);
	
	pRIPMSG->OPT[i++] = dhcpParamRequest;
	pRIPMSG->OPT[i++] = 0x08;
	pRIPMSG->OPT[i++] = subnetMask;
	pRIPMSG->OPT[i++] = routersOnSubnet;
	pRIPMSG->OPT[i++] = dns;
	pRIPMSG->OPT[i++] = domainName;
	pRIPMSG->OPT[i++] = performRouterDiscovery;
	pRIPMSG->OPT[i++] = staticRoute;
	pRIPMSG->OPT[i++] = dhcpT1value;
	pRIPMSG->OPT[i++] = dhcpT2value;
	pRIPMSG->OPT[i++] = endOption;
	
	return 0;
}

u8 func_pack_request_info(u8 *buf_dhcp, u16 len_buf, u8 *mac, u8 *alloc_ip, u8 *dhcps_ip, u32 xid)
{
	u16 i = 0;
	u8 host_name[24];
	
	pRIPMSG = (RIP_MSG*)buf_dhcp;	
	memset(buf_dhcp, 0, len_buf);
	
	pRIPMSG->op = DHCP_BOOTREQUEST;
	pRIPMSG->htype = DHCP_HTYPE10MB;
	pRIPMSG->hlen = DHCP_HLENETHERNET;
	pRIPMSG->hops = DHCP_HOPS;
	pRIPMSG->xid = htonl(xid);
	pRIPMSG->secs = htons(DHCP_SECS);
	pRIPMSG->flags =htons(DHCP_FLAGSBROADCAST);// discover and request broadcast ; else unicast
	
	memcpy(pRIPMSG->chaddr, mac, 6);
	
	/* MAGIC_COOKIE */
	pRIPMSG->OPT[i++] = (u8)((MAGIC_COOKIE >> 24) & 0xFF);
	pRIPMSG->OPT[i++] = (u8)((MAGIC_COOKIE >> 16) & 0xFF);
	pRIPMSG->OPT[i++] = (u8)((MAGIC_COOKIE >> 8) & 0xFF);
	pRIPMSG->OPT[i++] = (u8)(MAGIC_COOKIE & 0xFF);
	
	/* Option Request Param. */
	pRIPMSG->OPT[i++] = dhcpMessageType;
	pRIPMSG->OPT[i++] = 0x01;
	pRIPMSG->OPT[i++] = DHCP_REQUEST;
	
	pRIPMSG->OPT[i++] = dhcpClientIdentifier;
	pRIPMSG->OPT[i++] = 0x07;
	pRIPMSG->OPT[i++] = 0x01;
	pRIPMSG->OPT[i++] = mac[0];
	pRIPMSG->OPT[i++] = mac[1];
	pRIPMSG->OPT[i++] = mac[2];
	pRIPMSG->OPT[i++] = mac[3];
	pRIPMSG->OPT[i++] = mac[4];
	pRIPMSG->OPT[i++] = mac[5];
	
	pRIPMSG->OPT[i++] = dhcpRequestedIPaddr;
	pRIPMSG->OPT[i++] = 0x04;
	pRIPMSG->OPT[i++] = alloc_ip[0];
	pRIPMSG->OPT[i++] = alloc_ip[1];
	pRIPMSG->OPT[i++] = alloc_ip[2];
	pRIPMSG->OPT[i++] = alloc_ip[3];
	
	pRIPMSG->OPT[i++] = dhcpIPaddrLeaseTime;
	pRIPMSG->OPT[i++] = 0x04;
	pRIPMSG->OPT[i++] = 0xff;
	pRIPMSG->OPT[i++] = 0xff;
	pRIPMSG->OPT[i++] = 0xff;
	pRIPMSG->OPT[i++] = 0xff;
	
	pRIPMSG->OPT[i++] = dhcpServerIdentifier;
	pRIPMSG->OPT[i++] = 0x04;
	pRIPMSG->OPT[i++] = dhcps_ip[0];
	pRIPMSG->OPT[i++] = dhcps_ip[1];
	pRIPMSG->OPT[i++] = dhcps_ip[2];
	pRIPMSG->OPT[i++] = dhcps_ip[3];
	
	// host name
	pRIPMSG->OPT[i++] = hostName;
	//set the host name
	sprintf((char*)host_name,(char*)"%s-%02X%02X%02X", DEVICE_ID, mac[3], mac[4], mac[5]);
	pRIPMSG->OPT[i++] = (u8)strlen((char*)host_name);	
	strcpy((char*)&(pRIPMSG->OPT[i]),(char*)host_name);
	i+=strlen((char*)host_name);
		
	pRIPMSG->OPT[i++] = dhcpParamRequest;
	pRIPMSG->OPT[i++] = 0x08;
	pRIPMSG->OPT[i++] = subnetMask;
	pRIPMSG->OPT[i++] = routersOnSubnet;
	pRIPMSG->OPT[i++] = dns;
	pRIPMSG->OPT[i++] = domainName;
	pRIPMSG->OPT[i++] = dhcpT1value;
	pRIPMSG->OPT[i++] = dhcpT2value;
	pRIPMSG->OPT[i++] = performRouterDiscovery;
	pRIPMSG->OPT[i++] = staticRoute;
	pRIPMSG->OPT[i++] = endOption;
	
	return 0;
}

u8 func_analysis_dhcp_server_msg(u8 *buf_dhcp, u16 len_buf, u16 len_recv, u8 *dhcp_server_ip, DHCP_Get *dhcp_get)
{
	u8  * p;
	u8  * e;
	u8  opt_len = 0;
	u8 svr_addr[4];
	
	pRIPMSG = (RIP_MSG*)buf_dhcp;	
	memcpy(svr_addr, dhcp_server_ip, 4);
	if( *((u32*)DHCP_SIP) != 0x00000000 )
	{
		if( *((u32*)DHCP_REAL_SIP) != *((u32*)svr_addr) && 
		*((u32*)DHCP_SIP) != *((u32*)svr_addr) ) 
		{		
			return 1;
		}
	}	
	memcpy(dhcp_get->lip, pRIPMSG->yiaddr, 4);
	
	p = (u8 *)(&pRIPMSG->op);
	p = p + 240;
	e = p + (len_recv - 240);
	while ( p < e ) 
	{
		switch ( *p++ ) 
		{
			case endOption :
				return 0;
		
			case padOption :
				break;
			
			case dhcpMessageType :
				opt_len = *p++;
				break;
			
			case subnetMask :
				opt_len =* p++;
				memcpy(dhcp_get->sub, p, 4);
				break;
			
			case routersOnSubnet :
				opt_len = *p++;
				memcpy(dhcp_get->gw,p,4);
				break;
			
			case dns :
				opt_len = *p++;
				memcpy(dhcp_get->dns,p,4);
				break;
			
			case dhcpIPaddrLeaseTime :
				opt_len = *p++;
				dhcp_get->leased_time = ntohl(*((u32*)p));
				break;
			
			case dhcpServerIdentifier :
				opt_len = *p++;	
				if( *((u32*)DHCP_SIP) == 0 || 
				*((u32*)DHCP_REAL_SIP) == *((u32*)svr_addr) || 
				*((u32*)DHCP_SIP) == *((u32*)svr_addr) )
				{
					memcpy(DHCP_SIP, p, 4);
					memcpy(DHCP_REAL_SIP, svr_addr, 4);	// Copy the real ip address of my DHCP server					
				}
				break;
				
			default :
				opt_len = *p++;		
				break;
		}
		p+=opt_len;
	} // while	
	return 0;
}

u8 func_dhcp_get_ip_sub_gw(u8 *buf_dhcp, u16 len_buf, u8 sock, u8 *mac, DHCP_Get *dhcp_get, u16 timeout)
{
	u8 dhcp_server_ip[] = {255, 255, 255, 255};
	u8 recv_ip[4];
	u16 dhcp_server_port;
	u16 len;
	u16 cnt_timeout = 0;
	u32 xid = DEFAULT_XID;	
	
	if(getSn_SR(sock) == SOCK_CLOSED)
	{
		socket(sock, Sn_MR_UDP, DHCP_CLIENT_PORT, 0x00);
	}
	//package DSICOVER message
	func_pack_discover_info(buf_dhcp, len_buf, mac, xid);
	//send DISCOVER to DHCP server
	sendto(sock, buf_dhcp, sizeof(RIP_MSG), dhcp_server_ip, DHCP_SERVER_PORT);
	//wait to receive OFFER
	for(;;)
	{
		if(getSn_IR(sock) & Sn_IR_RECV)
		{
			setSn_IR(sock, Sn_IR_RECV);
		}
		if ((len = getSn_RX_RSR(sock)) > 0)
		{
			memset(buf_dhcp, 0, len_buf);
			len = recvfrom(sock, buf_dhcp, len_buf, recv_ip, &dhcp_server_port);
			if(len > 0 && dhcp_server_port == DHCP_SERVER_PORT)
			{
				pRIPMSG = (RIP_MSG*)buf_dhcp;
				if(pRIPMSG->op == DHCP_BOOTREPLY && memcmp(pRIPMSG->chaddr, mac, 6) == 0 && pRIPMSG->xid == htonl(xid))
				{
					if(func_analysis_dhcp_server_msg(buf_dhcp, len_buf, len, recv_ip, dhcp_get) == 0)
					{
						break;
					}
				}
			}
		}
		
		cnt_timeout ++;
		if(cnt_timeout > timeout)
		{
			close(sock);
			return 1;
		}
		delay_ms(1);
	}
	
	//REQUEST
	xid += 1;
	func_pack_request_info(buf_dhcp, len_buf, mac, dhcp_get->lip, dhcp_get->gw, xid);
	sendto(sock, buf_dhcp, sizeof(RIP_MSG), dhcp_server_ip, DHCP_SERVER_PORT);
	cnt_timeout = 0;
	//wait to receive ACK
	for(;;)
	{
		if(getSn_IR(sock) & Sn_IR_RECV)
		{
			setSn_IR(sock, Sn_IR_RECV);
		}
		if ((len = getSn_RX_RSR(sock)) > 0)
		{
			memset(buf_dhcp, 0, len_buf);
			len = recvfrom(sock, buf_dhcp, len_buf, recv_ip, &dhcp_server_port);
			if(len > 0 && dhcp_server_port == DHCP_SERVER_PORT)
			{
				pRIPMSG = (RIP_MSG*)buf_dhcp;
				if(pRIPMSG->op == DHCP_BOOTREPLY && memcmp(pRIPMSG->chaddr, mac, 6) == 0 && pRIPMSG->xid == htonl(xid))
				{
					if(func_analysis_dhcp_server_msg(buf_dhcp, len_buf, len, recv_ip, dhcp_get) == 0)
					{
						break;
					}
				}
			}
		}		
		
		cnt_timeout ++;
		if(cnt_timeout > timeout)
		{
			close(sock);
			return 1;
		}
		delay_ms(1);
	}
	close(sock);
	return 0;
}

dhcp. h代码如下:

#ifndef _DHCP_H_
#define _DHCP_H_

#ifndef __STM32F10X_H
#define __STM32F10X_H
#include "stm32f10x.h"
#endif
#include "stdio.h"
#include "types.h"
#include <string.h>

#define DEVICE_ID "W5500"

typedef struct _DHCP_GET
{
  u8 mac[6];
  u8 lip[4];
  u8 sub[4];
  u8 gw[4];
  u8 dns[4];
	u32 leased_time;
}DHCP_Get;


#define DHCP_RET_NONE      0
#define DHCP_RET_ERR       1
#define DHCP_RET_TIMEOUT   2
#define DHCP_RET_UPDATE    3
#define DHCP_RET_CONFLICT  4


/* DHCP state machine. */
#define  STATE_DHCP_READY        0
#define  STATE_DHCP_DISCOVER     1
#define  STATE_DHCP_REQUEST      2
#define  STATE_DHCP_LEASED       3
#define  STATE_DHCP_REREQUEST    4
#define  STATE_DHCP_RELEASE      5
         
#define  MAX_DHCP_RETRY          3
#define  DHCP_WAIT_TIME          5
         
#define  DHCP_FLAGSBROADCAST     0x8000

/* UDP port numbers for DHCP */
#define  DHCP_SERVER_PORT        67	/* from server to client */
#define  DHCP_CLIENT_PORT        68	/* from client to server */

/* DHCP message OP code */
#define  DHCP_BOOTREQUEST        1
#define  DHCP_BOOTREPLY          2

/* DHCP message type */
#define  DHCP_DISCOVER           1
#define  DHCP_OFFER              2
#define  DHCP_REQUEST            3
#define  DHCP_DECLINE            4
#define  DHCP_ACK                5
#define  DHCP_NAK                6
#define  DHCP_RELEASE            7
#define  DHCP_INFORM             8

#define DHCP_HTYPE10MB           1
#define DHCP_HTYPE100MB          2

#define DHCP_HLENETHERNET        6
#define DHCP_HOPS                0
#define DHCP_SECS                0

#define MAGIC_COOKIE		         0x63825363
#define DEFAULT_XID              0x12348765

#define DEFAULT_LEASETIME        0xffffffff	/* infinite lease time */

/* DHCP option and value (cf. RFC1533) */
enum
{
   padOption               = 0,
   subnetMask              = 1,
   timerOffset             = 2,
   routersOnSubnet         = 3,
   timeServer              = 4,
   nameServer              = 5,
   dns                     = 6,
   logServer               = 7,
   cookieServer            = 8,
   lprServer               = 9,
   impressServer           = 10,
   resourceLocationServer  = 11,
   hostName                = 12,
   bootFileSize            = 13,
   meritDumpFile           = 14,
   domainName              = 15,
   swapServer              = 16,
   rootPath                = 17,
   extentionsPath          = 18,
   IPforwarding            = 19,
   nonLocalSourceRouting   = 20,
   policyFilter            = 21,
   maxDgramReasmSize       = 22,
   defaultIPTTL            = 23,
   pathMTUagingTimeout     = 24,
   pathMTUplateauTable     = 25,
   ifMTU                   = 26,
   allSubnetsLocal         = 27,
   broadcastAddr           = 28,
   performMaskDiscovery    = 29,
   maskSupplier            = 30,
   performRouterDiscovery  = 31,
   routerSolicitationAddr  = 32,
   staticRoute             = 33,
   trailerEncapsulation    = 34,
   arpCacheTimeout         = 35,
   ethernetEncapsulation   = 36,
   tcpDefaultTTL           = 37,
   tcpKeepaliveInterval    = 38,
   tcpKeepaliveGarbage     = 39,
   nisDomainName           = 40,
   nisServers              = 41,
   ntpServers              = 42,
   vendorSpecificInfo      = 43,
   netBIOSnameServer       = 44,
   netBIOSdgramDistServer  = 45,
   netBIOSnodeType         = 46,
   netBIOSscope            = 47,
   xFontServer             = 48,
   xDisplayManager         = 49,
   dhcpRequestedIPaddr     = 50,
   dhcpIPaddrLeaseTime     = 51,
   dhcpOptionOverload      = 52,
   dhcpMessageType         = 53,
   dhcpServerIdentifier    = 54,
   dhcpParamRequest        = 55,
   dhcpMsg                 = 56,
   dhcpMaxMsgSize          = 57,
   dhcpT1value             = 58,
   dhcpT2value             = 59,
   dhcpClassIdentifier     = 60,
   dhcpClientIdentifier    = 61,
   endOption               = 255
};

typedef struct _RIP_MSG
{
   u8  op; 
   u8  htype; 
   u8  hlen;
   u8  hops;
   u32 xid;
   u16 secs;
   u16 flags;
   u8  ciaddr[4];
   u8  yiaddr[4];
   u8  siaddr[4];
   u8  giaddr[4];
   u8  chaddr[16];
   u8  sname[64];
   u8  file[128];
   u8  OPT[128];
}RIP_MSG;

u8 func_dhcp_get_ip_sub_gw(u8 *buf_dhcp, u16 len_buf, u8 sock, u8 *mac, DHCP_Get *dhcp_get, u16 timeout);
#endif	/* _DHCP_H_ */

值得一提的是,设备名称的命名,通过 #define DEVICE_ID "W5500" 和MAC地址来组设备的名称,在路由器的设备连接管理页面上可以看到:

主函数的测试代码如下:

#ifndef __STM32F10X_H
#define __STM32F10X_H
#include "stm32f10x.h"
#endif

#ifndef __Z_UTIL_TIME_H
#define __Z_UTIL_TIME_H
#include "z_util_time.h"
#endif

#ifndef __Z_HARDWARE_LED_H
#define __Z_HARDWARE_LED_H
#include "z_hardware_led.h"
#endif

#ifndef __Z_HARDWARE_SPI_H
#define __Z_HARDWARE_SPI_H
#include "z_hardware_spi.h"
#endif

#ifndef __Z_HARDWARE_USART2_H
#define __Z_HARDWARE_USART2_H
#include "z_hardware_usart2.h"
#endif

#include "w5500.h"
#include "socket.h"
#include "w5500_conf.h"
#include "dhcp.h"


int main(void)
{
	DHCP_Get dhcp_get;
	uint8 buffer[1024];
	
	uint8 mac[6]={0, 0, 0, 0, 0, 0};
	
	u16 local_port = 6666;
	u16 remote_port = 7777;
	u8 remote_ip[4] = {192, 168, 1, 105};
	
	init_led();
	init_system_spi();
	func_w5500_reset();
	
	init_hardware_usart2_dma(9600);
	
	getMacByLockCode(mac);
	setSHAR(mac);
	
	sysinit(txsize, rxsize);
	setRTR(2000);
  setRCR(3);
	
	func_usart2_dma_send_bytes(buffer, 2);
	
	for(;func_dhcp_get_ip_sub_gw(buffer, sizeof(buffer), 0, mac, &dhcp_get, 500) != 0;);
	if(func_dhcp_get_ip_sub_gw(buffer, sizeof(buffer), 0, mac, &dhcp_get, 500) == 0)
	{
		func_usart2_dma_send_bytes(dhcp_get.lip, sizeof(dhcp_get.lip));
		
		setSUBR(dhcp_get.sub);
		setGAR(dhcp_get.gw);
		setSIPR(dhcp_get.lip);
	}
	for(;;)
	{
		socket(0, Sn_MR_UDP, local_port, 0);
		sendto(0, dhcp_get.lip, sizeof(dhcp_get.lip), remote_ip, remote_port);
		delay_ms(500);
		
		sendto(0, dhcp_get.sub, sizeof(dhcp_get.sub), remote_ip, remote_port);
		delay_ms(500);
		
		sendto(0, dhcp_get.gw, sizeof(dhcp_get.gw), remote_ip, remote_port);
		delay_ms(500);
		
		sendto(0, mac, sizeof(mac), remote_ip, remote_port);
		delay_ms(500);
		
		func_led1_on();
		delay_ms(500);
		func_led1_off();
		delay_ms(500);
		
	}
}

通过W5500的SOCK 0 以UDP的方式,将IP地址,网关,子网掩码,MAC地址发送出来:

以上。

  • 2
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值