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
//Total length of IP header
#define IP_HEADER_LEN 20
// agreement type
//ICMP protocol
#define IP_PROTO_ICMP_V 0x01
// TCP protocol
#define IP_PROTO_TCP_V 0x06
// UDP protocol
#define IP_PROTO_UDP_V 0x11
//IPV4 version
#define IP_V4_V 0x40
#define IP_HEADER_LENGTH_V 0x05
//IP version number location Ethernet header 2+6+6
#define IP_P 0x0E
//Header length
#define IP_HEADER_VER_LEN_P 0x0E
// Service type
#define IP_TOS_P 0x0F
//Total length of IP
#define IP_TOTLEN_H_P 0x10
#define IP_TOTLEN_L_P 0x11
//IP ID
#define IP_ID_H_P 0x12
#define IP_ID_L_P 0x13
//
#define IP_FLAGS_H_P 0x14
#define IP_FLAGS_L_P 0x15
// TTL lifetime
#define IP_TTL_P 0x16
//IP protocol type, such as ICMP TCP UDP
#define IP_PROTO_P 0x17
//Header checksum
#define IP_CHECKSUM_H_P 0x18
#define IP_CHECKSUM_L_P 0x19
// Source IP address
#define IP_SRC_IP_P 0x1A
// Target IP address
#define IP_DST_IP_P 0x1E
void ip_generate_header ( BYTE *rxtx_buffer, WORD_BYTES total_length, BYTE protocol, BYTE *dest_ip )
{
BYTE i;
// Verify the result
WORD_BYTES ck;
// Version number and capital length
rxtx_buffer[ IP_P ] = IP_V4_V | IP_HEADER_LENGTH_V;
// Service type
rxtx_buffer[IP_TOS_P] = 0x00;
//Total length
rxtx_buffer [ IP_TOTLEN_H_P ] = total_length.byte.high;
rxtx_buffer [ IP_TOTLEN_L_P ] = total_length.byte.low;
//IP ID
rxtx_buffer [IP_ID_H_P] = ip_identfier >> 8;
rxtx_buffer [ IP_ID_H_P ] = ip_identfier & 0x00ff;
// Accumulate
ip_identfier++;
// Flags and fragment offsets
rxtx_buffer [IP_FLAGS_H_P] = 0x00;
rxtx_buffer [IP_FLAGS_L_P] = 0x00;
// Lifetime
rxtx_buffer [IP_TTL_P] = 128;
// set ip packettype to tcp/udp/icmp...
rxtx_buffer [ IP_PROTO_P ] = protocol;
// Set the destination address and source address
for ( i = 0; i < 4; i++ )
{
rxtx_buffer[ IP_DST_IP_P + i ] = dest_ip[i];
rxtx_buffer[ IP_SRC_IP_P + i ] = avr_ip.byte[ i ];
}
// Verify the result
rxtx_buffer[IP_CHECKSUM_H_P] = 0;
rxtx_buffer[IP_CHECKSUM_L_P] = 0;
ck.word = software_checksum ( &rxtx_buffer[ IP_P ], sizeof(IP_HEADER), 0 );
rxtx_buffer[IP_CHECKSUM_H_P] = ck.byte.high;
rxtx_buffer[IP_CHECKSUM_L_P] = ck.byte.low;
}
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.
BYTE ip_packet_is_ip ( BYTE *rxtx_buffer )
{
unsigned char i;
// Check if the message is an IP message
if ( rxtx_buffer[ ETH_TYPE_H_P ] != ETH_TYPE_IP_H_V || rxtx_buffer[ ETH_TYPE_L_P ] != ETH_TYPE_IP_L_V)
return 0;
// Check if the IP address of the message is the local IP address, check one by one
for ( i=0; i
{
if ( rxtx_buffer[ IP_DST_IP_P + i ] != avr_ip.byte[i] )
return 0;
}
// If the message is an IP message and the target IP address is the local address, return 1
return 1;
}
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.
// Echo the response
#define ICMP_TYPE_ECHOREPLY_V 0
// Echo request
#define ICMP_TYPE_ECHOREQUEST_V 8
// ICMP header length
#define ICMP_PACKET_LEN 40
// ICMP type
#define ICMP_TYPE_P 0x22
//ICMP code
#define ICMP_CODE_P 0x23
//ICMP header checksum
#define ICMP_CHECKSUM_H_P 0x24
#define ICMP_CHECKSUM_L_P 0x25
// ICMP identifier
#define ICMP_IDENTIFIER_H_P 0x26
#define ICMP_IDENTIFIER_L_P 0x27
//ICMP sequence number
#define ICMP_SEQUENCE_H_P 0x28
#define ICMP_SEQUENCE_L_P 0x29
#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.
void icmp_generate_packet ( BYTE *rxtx_buffer ,BYTE type)
{
BYTE i;
WORD_BYTES ck;
// ICMP echo request
if( type == ICMP_TYPE_ECHOREQUEST_V )
{
rxtx_buffer[ICMP_TYPE_P] == ICMP_TYPE_ECHOREQUEST_V;
rxtx_buffer[ICMP_CODE_P] = 0;
rxtx_buffer[ICMP_IDENTIFIER_H_P] = icmp_id;
rxtx_buffer[ICMP_IDENTIFIER_L_P] = 0;
rxtx_buffer[ICMP_SEQUENCE_H_P] = icmp_seq;
rxtx_buffer[ICMP_SEQUENCE_L_P] = 0;
icmp_id++;
icmp_seq++;
for ( i=0; i
{
rxtx_buffer[ICMP_DATA_P + i] = 'A' + i;
}
}
//ICMP echo
if(type == ICMP_TYPE_ECHOREPLY_V)
{
rxtx_buffer[ICMP_TYPE_P] = ICMP_TYPE_ECHOREPLY_V;
}
//ICMP header checksum
rxtx_buffer[ICMP_CHECKSUM_H_P] = 0;
rxtx_buffer[ICMP_CHECKSUM_L_P] = 0;
ck.word = software_checksum ( &rxtx_buffer[ ICMP_TYPE_P ], sizeof(ICMP_PACKET), 0 );
rxtx_buffer[ICMP_CHECKSUM_H_P] = ck.byte.high;
rxtx_buffer[ICMP_CHECKSUM_L_P] = ck.byte.low;
}
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
BYTE icmp_send_reply ( BYTE *rxtx_buffer, BYTE *dest_mac, BYTE *dest_ip )
{
// The IP header contains the ICMP protocol type
if ( rxtx_buffer [ IP_PROTO_P ] != IP_PROTO_ICMP_V )
return 0;
// Is it an ICMP echo request?
if ( rxtx_buffer [ ICMP_TYPE_P ] != ICMP_TYPE_ECHOREQUEST_V )
return 0;
#ifdef ICMP_DEBUG
printf("ICMP Request!\n");
printf("Ping from:%d.%d.%d.%d\n",\
rxtx_buffer[IP_SRC_IP_P+0],rxtx_buffer[IP_SRC_IP_P+1],\
rxtx_buffer[IP_SRC_IP_P+2],rxtx_buffer[IP_SRC_IP_P+3]);
#endif
// Generate Ethernet header
eth_generate_header ( rxtx_buffer, (WORD_BYTES){ETH_TYPE_IP_V}, dest_mac );
// Generate IP header
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 );
// Generate ICMP header
icmp_generate_packet ( rxtx_buffer ,(BYTE)ICMP_TYPE_ECHOREPLY_V);
// Send message
enc28j60_packet_send ( rxtx_buffer, sizeof(ETH_HEADER) + sizeof(IP_HEADER) + sizeof(ICMP_PACKET) );
return 1;
}
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
/* Get a new IP packet */
plen = enc28j60_packet_receive( (BYTE*)&rxtx_buffer, MAX_RXTX_BUFFER );
if(plen==0) return;
/* Save the client's MAC address */
memcpy ( (BYTE*)&client_mac, &rxtx_buffer[ ETH_SRC_MAC_P ], sizeof(MAC_ADDR) );
/* Check if the message is an ARP message */
if ( arp_packet_is_arp( rxtx_buffer, (WORD_BYTES){ARP_OPCODE_REQUEST_V} ) )
{
/* Return ARP message to the client */
arp_send_reply ( (BYTE*)&rxtx_buffer, (BYTE*)&client_mac );
return;
}
/* Save the client's IP address */
memcpy ( (BYTE*)&client_ip, &rxtx_buffer[ IP_SRC_IP_P ], sizeof(IP_ADDR) );
/* Check if the message is an IP message */
if ( ip_packet_is_ip ( (BYTE*)&rxtx_buffer ) == 0 )
{
return;
}
/* If it is an ICMP message, return data to the initiator */
if ( icmp_send_reply ( (BYTE*)&rxtx_buffer, (BYTE*)&client_mac, (BYTE*)&client_ip ) )
{
return;
}
【Experimental results】
Figure 1 PING command
Figure 2 Serial port output
Previous article:W5500 adjusts the brightness of LED light strips through host computer control
Next article:STM32NET study notes ARP and Ethernet part
- Popular Resources
- Popular amplifiers
- Learn ARM development(19)
- Learn ARM development(14)
- Learn ARM development(15)
- Learn ARM development(16)
- Learn ARM development(17)
- Learn ARM development(18)
- Embedded system debugging simulation tool
- A small question that has been bothering me recently has finally been solved~~
- Learn ARM development (1)
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- LED chemical incompatibility test to see which chemicals LEDs can be used with
- Application of ARM9 hardware coprocessor on WinCE embedded motherboard
- What are the key points for selecting rotor flowmeter?
- LM317 high power charger circuit
- A brief analysis of Embest's application and development of embedded medical devices
- Single-phase RC protection circuit
- stm32 PVD programmable voltage monitor
- Introduction and measurement of edge trigger and level trigger of 51 single chip microcomputer
- Improved design of Linux system software shell protection technology
- What to do if the ABB robot protection device stops
- Learn ARM development(19)
- Learn ARM development(14)
- Learn ARM development(15)
- Analysis of the application of several common contact parts in high-voltage connectors of new energy vehicles
- Wiring harness durability test and contact voltage drop test method
- From probes to power supplies, Tektronix is leading the way in comprehensive innovation in power electronics testing
- From probes to power supplies, Tektronix is leading the way in comprehensive innovation in power electronics testing
- Sn-doped CuO nanostructure-based ethanol gas sensor for real-time drunk driving detection in vehicles
- Design considerations for automotive battery wiring harness
- Do you know all the various motors commonly used in automotive electronics?
- If you urinate on a 380V high voltage wire (live electricity), will you be electrocuted? Will urine become a conductor and carry electricity, giving you an electric shock?
- Please help me find the schematic diagram, thank you very much
- msp430f5529 capture plus serial port source code
- How to drive a servo motor using the MSP430G2 LaunchPad development board
- Playing with circuits (3) - Alternative circuits for LM5050/LM5060
- sensortile.box expert mode try
- TMR sensor solution enables ultra-high-precision monitoring of electric vehicle batteries
- I'd like to share a book on compilers and a book on interpreters, both of which have high Douban ratings.
- Mobile 5G device antenna tuning revealed
- How to access the external network through the router when STM32 is plugged into the network cable?