STM32NET study notes IP ICMP part

Publisher:火箭飞人Latest update time:2018-04-28 Source: eefocusKeywords:STM32NET Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

1 Introduction 

    Embedded Ethernet development is a very challenging job. After several months of study, I personally think there are roughly two ways. The first way is to first get familiar with socket programming through high-level languages, such as C# or C++, and apply lwIP after becoming familiar with functions such as bind, listen, connect, and accept. The second way is to analyze the embedded Ethernet code and gradually practice the code in combination with the TCPIP protocol stack specification. The first way is efficient, has a short development cycle, and the performance of the written code is stable. The second way takes a long time, and the developed code is not perfect in function, but because it is closely combined with the TCPIP specification, more content can be understood, which is suitable for learning. This article gradually implements the various sub-parts of the TCPIP protocol stack, including the ETHERNET part, ARP part, IP part, ICMP part, UDP part, TCP part, and HTTP part, by analyzing and modifying the AVRNET source code.

 This article will implement the IP part and the ICMP part.

1.2 Related Materials

【ENC28J60 study notes】

[ STM32NET study notes ARP and Ethernet part ]

【AVRNET Project (Overseas)  】

【AVR webserver project (foreign) 】


1.3 Code Repository

    【Code Repository】——CSDN Code code repository.


2. IP partial implementation

    The IP layer is the basis for TCP and UDP implementation. The IP header follows the Ethernet header and is 20 bytes long. The IP header has two basic tasks:

    [First] Define the specific protocol type of the IP packet, such as ICMP, TCP or UDP;

    [Second] Define which IP address the IP packet comes from and goes to.

    It should be emphasized that in the same subnet, that is, in the same physical network, the target IP address in the IP message corresponds to the target MAC address in the Ethernet header. If they are not in the same physical network, the target IP address and the target MAC address are different, and the target MAC address is replaced by the MAC address of the router, which means that the message is forwarded through the router. The IP header also includes many other contents. It should be noted that the IP identifier is mainly used to distinguish IP messages. The simplest algorithm is to accumulate the IP identifier after each IP message is sent. The following code is used to implement the filling of the IP header.

2.1 IP header padding


  1. //Total length of IP header  

  2. #define IP_HEADER_LEN 20  

  3. // agreement type  

  4. //ICMP protocol  

  5. #define IP_PROTO_ICMP_V 0x01  

  6. // TCP protocol  

  7. #define IP_PROTO_TCP_V 0x06  

  8. // UDP protocol  

  9. #define IP_PROTO_UDP_V 0x11  

  10. //IPV4 version  

  11. #define IP_V4_V 0x40  

  12. #define IP_HEADER_LENGTH_V 0x05  

  13. //IP version number location Ethernet header 2+6+6  

  14. #define IP_P 0x0E  

  15. //Header length  

  16. #define IP_HEADER_VER_LEN_P 0x0E  

  17. // Service type  

  18. #define IP_TOS_P 0x0F  

  19. //Total length of IP  

  20. #define IP_TOTLEN_H_P 0x10  

  21. #define IP_TOTLEN_L_P 0x11  

  22. //IP ID  

  23. #define IP_ID_H_P 0x12  

  24. #define IP_ID_L_P 0x13  

  25. //  

  26. #define IP_FLAGS_H_P 0x14  

  27. #define IP_FLAGS_L_P 0x15  

  28. // TTL lifetime  

  29. #define IP_TTL_P 0x16  

  30. //IP protocol type, such as ICMP TCP UDP  

  31. #define IP_PROTO_P 0x17  

  32. //Header checksum  

  33. #define IP_CHECKSUM_H_P 0x18  

  34. #define IP_CHECKSUM_L_P 0x19  

  35. // Source IP address  

  36. #define IP_SRC_IP_P 0x1A  

  37. // Target IP address  

  38. #define IP_DST_IP_P 0x1E  

  39. void ip_generate_header ( BYTE *rxtx_buffer, WORD_BYTES total_length, BYTE protocol, BYTE *dest_ip )  

  40. {  

  41.   BYTE i;  

  42.   // Verify the result  

  43.   WORD_BYTES ck;  

  44.    

  45.   // Version number and capital length  

  46.   rxtx_buffer[ IP_P ] = IP_V4_V | IP_HEADER_LENGTH_V;  

  47.   // Service type  

  48.   rxtx_buffer[IP_TOS_P] = 0x00;  

  49.   //Total length  

  50.   rxtx_buffer [ IP_TOTLEN_H_P ] = total_length.byte.high;  

  51.   rxtx_buffer [ IP_TOTLEN_L_P ] = total_length.byte.low;  

  52.    

  53.   //IP ID  

  54.   rxtx_buffer [IP_ID_H_P] = ip_identfier >> 8;  

  55.   rxtx_buffer [ IP_ID_H_P ] = ip_identfier & 0x00ff;  

  56.   // Accumulate  

  57.   ip_identfier++;  

  58.    

  59.   // Flags and fragment offsets  

  60.   rxtx_buffer [IP_FLAGS_H_P] = 0x00;  

  61.   rxtx_buffer [IP_FLAGS_L_P] = 0x00;  

  62.    

  63.   // Lifetime  

  64.   rxtx_buffer [IP_TTL_P] = 128;  

  65.    

  66.   // set ip packettype to tcp/udp/icmp...  

  67.   rxtx_buffer [ IP_PROTO_P ] = protocol;  

  68.    

  69.   // Set the destination address and source address  

  70.   for ( i = 0; i < 4; i++ )  

  71.   {  

  72.     rxtx_buffer[ IP_DST_IP_P + i ] = dest_ip[i];  

  73.     rxtx_buffer[ IP_SRC_IP_P + i ] = avr_ip.byte[ i ];  

  74.   }  

  75.    

  76.   // Verify the result  

  77.   rxtx_buffer[IP_CHECKSUM_H_P] = 0;  

  78.   rxtx_buffer[IP_CHECKSUM_L_P] = 0;  

  79.   ck.word = software_checksum ( &rxtx_buffer[ IP_P ], sizeof(IP_HEADER), 0 );  

  80.   rxtx_buffer[IP_CHECKSUM_H_P] = ck.byte.high;  

  81.   rxtx_buffer[IP_CHECKSUM_L_P] = ck.byte.low;  

  82. }  



2.2 IP packet query

    The IP message query function corresponds to the ARP message query function. It determines whether the message is an IP message by the last two bytes in the Ethernet header. If it is an IP message, it will continue to compare with the local IP address. If both checks pass, it is considered to be a legal IP message. Of course, the IP version number and header checksum checks are abandoned. Although there are some hidden dangers, it does not hinder the realization of basic functions.


  1. BYTE ip_packet_is_ip ( BYTE *rxtx_buffer )  

  2. {  

  3.   unsigned char i;  

  4.    

  5.   // Check if the message is an IP message  

  6.   if ( rxtx_buffer[ ETH_TYPE_H_P ] != ETH_TYPE_IP_H_V || rxtx_buffer[ ETH_TYPE_L_P ] != ETH_TYPE_IP_L_V)  

  7.     return 0;  

  8.    

  9.   // Check if the IP address of the message is the local IP address, check one by one  

  10.   for ( i=0; i

  11.   {  

  12.     if ( rxtx_buffer[ IP_DST_IP_P + i ] != avr_ip.byte[i] )  

  13.       return 0;  

  14.   }  

  15.    

  16.   // If the message is an IP message and the target IP address is the local address, return 1  

  17.   return 1;  

  18. }  



3. ICMP Partial Implementation

    Although ICMP has many sub-protocols, the most famous one is the ping program, which is the ICMP echo request and reply message. The ping command is used to determine whether the message can reach the destination address. The implementation of ICMP is a step-by-step process of following the rules, that is, filling data into fixed bytes.


  1. // Echo the response  

  2. #define ICMP_TYPE_ECHOREPLY_V 0  

  3. // Echo request  

  4. #define ICMP_TYPE_ECHOREQUEST_V 8  

  5. // ICMP header length  

  6. #define ICMP_PACKET_LEN 40  

  7. // ICMP type  

  8. #define ICMP_TYPE_P 0x22  

  9. //ICMP code  

  10. #define ICMP_CODE_P 0x23  

  11. //ICMP header checksum  

  12. #define ICMP_CHECKSUM_H_P 0x24  

  13. #define ICMP_CHECKSUM_L_P 0x25  

  14. // ICMP identifier  

  15. #define ICMP_IDENTIFIER_H_P 0x26  

  16. #define ICMP_IDENTIFIER_L_P 0x27  

  17. //ICMP sequence number  

  18. #define ICMP_SEQUENCE_H_P 0x28  

  19. #define ICMP_SEQUENCE_L_P 0x29  

  20. #define ICMP_DATA_P 0x2A  



3.1 ICMP header padding

The ICMP header needs to be filled with different content according to the protocol type. For echo requests, you only need to fill 0 in the ICMP protocol type part. Of course, the ICMP part also includes the ICMP header checksum.


  1. void icmp_generate_packet ( BYTE *rxtx_buffer ,BYTE type)  

  2. {  

  3.   BYTE i;  

  4.   WORD_BYTES ck;  

  5.    

  6.   // ICMP echo request  

  7.   if( type == ICMP_TYPE_ECHOREQUEST_V )  

  8.   {  

  9.     rxtx_buffer[ICMP_TYPE_P] == ICMP_TYPE_ECHOREQUEST_V;  

  10.     rxtx_buffer[ICMP_CODE_P] = 0;  

  11.     rxtx_buffer[ICMP_IDENTIFIER_H_P] = icmp_id;  

  12.     rxtx_buffer[ICMP_IDENTIFIER_L_P] = 0;  

  13.     rxtx_buffer[ICMP_SEQUENCE_H_P] = icmp_seq;  

  14.     rxtx_buffer[ICMP_SEQUENCE_L_P] = 0;  

  15.     icmp_id++;  

  16.     icmp_seq++;  

  17.     for ( i=0; i

  18.     {  

  19.       rxtx_buffer[ICMP_DATA_P + i] = 'A' + i;  

  20.     }  

  21.   }  

  22.   //ICMP echo  

  23.   if(type == ICMP_TYPE_ECHOREPLY_V)  

  24.   {  

  25.     rxtx_buffer[ICMP_TYPE_P] = ICMP_TYPE_ECHOREPLY_V;  

  26.   }  

  27.    

  28.   //ICMP header checksum  

  29.   rxtx_buffer[ICMP_CHECKSUM_H_P] = 0;  

  30.   rxtx_buffer[ICMP_CHECKSUM_L_P] = 0;  

  31.   ck.word = software_checksum ( &rxtx_buffer[ ICMP_TYPE_P ], sizeof(ICMP_PACKET), 0 );  

  32.   rxtx_buffer[ICMP_CHECKSUM_H_P] = ck.byte.high;  

  33.   rxtx_buffer[ICMP_CHECKSUM_L_P] = ck.byte.low;  

  34. }  



3.2 ICMP Echo Reply

 ICMP echo reply needs to be done in two steps. The first step is to check whether the protocol type in the IP header is an ICMP message; the second step is to check whether the ICMP type in the ICMP header is an ICMP request. If it is, an ICMP echo reply is generated and sent through the Ethernet driver chip. For the convenience of debugging, the IP address of the initiator is output through the serial port when the ICMP echo request is received. The IP address of the initiator of the ping command exists in the source IP address part of the IP header.


[cpp]  view plain  copy

  1. BYTE icmp_send_reply ( BYTE *rxtx_buffer, BYTE *dest_mac, BYTE *dest_ip )  

  2. {  

  3.    

  4.   // The IP header contains the ICMP protocol type  

  5.   if ( rxtx_buffer [ IP_PROTO_P ] != IP_PROTO_ICMP_V )  

  6.     return 0;  

  7.    

  8.   // Is it an ICMP echo request?  

  9.   if ( rxtx_buffer [ ICMP_TYPE_P ] != ICMP_TYPE_ECHOREQUEST_V )  

  10. return 0;  

  11. #ifdef ICMP_DEBUG  

  12.   printf("ICMP Request!\n");  

  13.   printf("Ping from:%d.%d.%d.%d\n",\  

  14.             rxtx_buffer[IP_SRC_IP_P+0],rxtx_buffer[IP_SRC_IP_P+1],\  

  15.             rxtx_buffer[IP_SRC_IP_P+2],rxtx_buffer[IP_SRC_IP_P+3]);  

  16. #endif  

  17.   // Generate Ethernet header  

  18.   eth_generate_header ( rxtx_buffer, (WORD_BYTES){ETH_TYPE_IP_V}, dest_mac );  

  19.    

  20.   // Generate IP header  

  21.   ip_generate_header ( rxtx_buffer, (WORD_BYTES){(rxtx_buffer[IP_TOTLEN_H_P]<<8)|rxtx_buffer[IP_TOTLEN_L_P]}, IP_PROTO_ICMP_V, dest_ip );  

  22.   // Generate ICMP header  

  23.   icmp_generate_packet ( rxtx_buffer ,(BYTE)ICMP_TYPE_ECHOREPLY_V);  

  24.   // Send message  

  25.   enc28j60_packet_send ( rxtx_buffer, sizeof(ETH_HEADER) + sizeof(IP_HEADER) + sizeof(ICMP_PACKET) );  

  26.   return 1;  

  27. }  



4. Examples

    You can use the ping command to test whether the message can reach the target host. For example, send ping 192.168.1.115.

    In the infinite loop of the program, queries need to be performed layer by layer.

【1】Query whether there is data in Ethernet, and return if there is no data.

【2】Save the source MAC address for later use when returning.

【3】Query whether it is an ARP message and return the ARP message.

【4】Save the source IP address for later use when returning.

【5】Check whether it is an IP message. If it is not an IP message, return it.

【6】Query whether it is an ICMP message and return an ICMP echo reply.

[html]  view plain  copy

  1. /* Get a new IP packet */  

  2.  plen = enc28j60_packet_receive( (BYTE*)&rxtx_buffer, MAX_RXTX_BUFFER );  

  3.  if(plen==0) return;  

  4.  /* Save the client's MAC address */  

  5.  memcpy ( (BYTE*)&client_mac, &rxtx_buffer[ ETH_SRC_MAC_P ], sizeof(MAC_ADDR) );  

  6.  /* Check if the message is an ARP message */  

  7.  if ( arp_packet_is_arp( rxtx_buffer, (WORD_BYTES){ARP_OPCODE_REQUEST_V} ) )  

  8.  {  

  9.    /* Return ARP message to the client */  

  10.    arp_send_reply ( (BYTE*)&rxtx_buffer, (BYTE*)&client_mac );  

  11.    return;  

  12.  }  

  13.  /* Save the client's IP address */  

  14.  memcpy ( (BYTE*)&client_ip, &rxtx_buffer[ IP_SRC_IP_P ], sizeof(IP_ADDR) );  

  15.  /* Check if the message is an IP message */  

  16.  if ( ip_packet_is_ip ( (BYTE*)&rxtx_buffer ) == 0 )  

  17.  {  

  18.    return;  

  19.  }  

  20.   

  21.  /* If it is an ICMP message, return data to the initiator */  

  22.  if ( icmp_send_reply ( (BYTE*)&rxtx_buffer, (BYTE*)&client_mac, (BYTE*)&client_ip ) )  

  23.  {  

  24.    return;  

  25.  }  


【Experimental results】

 

Figure 1 PING command

 

Figure 2 Serial port output


Keywords:STM32NET Reference address:STM32NET study notes IP ICMP part

Previous article:W5500 adjusts the brightness of LED light strips through host computer control
Next article:STM32NET study notes ARP and Ethernet part

Latest Microcontroller Articles
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号