Address Resolution Protocol (ARP)

Whenever there is a transportation of packet through network, both MAC and IP address are required. Nevertheless, for the first time you are going to send a packet to a desired host, you most probably do not have its MAC address. ARP is then a way for you to get the MAC address you want. 

In ARP, you first send an ARP packet, which is a broadcast including the desired IP address to throughout the network. When the desired host receives the packet and finds that the IP address matches its own IP address, it sends an ARP reply, with its MAC address included, back to you. Then it is done and you can send any packet to the desired host without hesitation since you already have its MAC and IP address.

The Next time when you want to send a packet to the same host, you do not have to send a ARP packet again since its MAC address was already in your device’s memory, which is called ARP Table, if you are doing the communication using Laptop or PC.

While in this example, since we are using our W5500EVB, there would be no ARP Table unless you build one with your code. Therefore, we are demonstrating the situation that you send an ARP packet to acquire MAC address from your desired host.


Before burning the bin file into your EVB:

      1. Connect W5500 EVB to the router/NAT that you are using, or any other device that allows you to connect to the internet
      2. Manually change the Network configuration:
          • Go to the definition of function “set_default()” (Should be inside device.c), make sure the subnet mask, local IP address, gateway and DNS are set according to the internet setting you are using with your router/NAT
      3. Rebuild the sample code and burn it into W5500EVB
      4. Change your PC’s IP address to a static IP if you are not using router


Code Explanation

int main(void)
{


After all the necessary initialization, it goes into the main loop, in which only one function,do_arp(), would be looped.

  RCC_Configuration();
  Systick_Init(72);
  GPIO_Configuration();
  NVIC_Configuration();
  Timer_Configuration();
  USART1_Init();
  Reset_W5500();
  WIZ_SPI_Init();
  set_default();
  set_network();

  while(1)
  {
    do_arp();
  } 
}
/******************


These are all the necessary
initialization







Start going into main loop 
function

********************/

do_arp()

There are two stages in this function, which are divided by using switch statement.

void do_arp(void)
{
  uint16 rlen =0;
  SOCKET s = SOCK_ARP;
  uint16 aPort=5000;

  switch(getSn_SR(s))
  {

Socket_Closed

Basically W5500EVB goes into this stage once it is on, as no socket would be initially opened. It closes the socket to make sure the socket is closed, and for later use. Then it opens a socket in Socket_MacRaw mode, which would be discussed later.

    case SOCK_CLOSED:
      close(s);
      socket(s,Sn_MR_MACRAW,aPort,0);
    break;


Socket_MacRaw

It first checks if no ARP request has sent before by checking the value of flag tx_arp_flag (which is initialized with value 1 in main.c)

  • If it is, then W5500EVB would send an ARP request to acquire MAC address of the desired host.
  • It remains in this stage and waits for ARP reply packet from the desired host. Whether or not there is a packet received, the program ends by staying in this stage anyway.
    case SOCK_MACRAW:
      if(tx_arp_flag==1)
      {
	tx_arp_flag=0;
        arp_request(s, aPort, ConfigMsg.lip, ConfigMsg.mac, target_ip_addr);
      }
      if((rlen = getSn_RX_RSR(s)) > 0)
      {
        arp_reply(s, rlen);
      }
      break;
  }
}


arp_reply()

Once a packet is received, it would be divided into segments accordingly by putting it into a struct after checking what type the ARP packet is.

void arp_reply(SOCKET s, uint16 rlen)
{
  uint16 mac_destport;  
  uint8 mac_destip[4];

  recvfrom(s,(uint8 *)pub_buf,rlen,mac_destip,&mac_destport);

  if( pub_buf[12]==ARP_TYPE_HI && pub_buf[13]==ARP_TYPE_LO ){
    aARPMSG =(ARPMSG*) pub_buf;	


If the packet is a ARP reply, the MAC and IP address of the host would be printed out for debugging

    if((aARPMSG->opcode) == htons(ARP_REPLY)) {

      memset(ret_arp_reply,0x00,128);
      sprintf((int8*)ret_arp_reply,"%.3u.%.3u.%.3u.%.3u is at %.2x.%.2x.%.2x.%.2x.%.2x.%.2x",
                          (aARPMSG->sender_ip[0]),  (aARPMSG->sender_ip[1]),(aARPMSG->sender_ip[2]),  (aARPMSG->sender_ip[3]),
                          (aARPMSG->sender_mac[0]),  (aARPMSG->sender_mac[1]),  (aARPMSG->sender_mac[2]),
                          (aARPMSG->sender_mac[3]), (aARPMSG->sender_mac[4]),  (aARPMSG->sender_mac[5]));
    
      printf( "%.u.%.u.%.u.%.u ", 
             (aARPMSG->sender_ip[0]),  (aARPMSG->sender_ip[1]), 
             (aARPMSG->sender_ip[2]),  (aARPMSG->sender_ip[3])) ;
      printf(" is at  ");
      printf( "%.2x.%.2x.%.2x.%.2x.%.2x.%.2x \r\b", 
             (aARPMSG->sender_mac[0]),  (aARPMSG->sender_mac[1]),  (aARPMSG->sender_mac[2]), 
             (aARPMSG->sender_mac[3]), (aARPMSG->sender_mac[4]),  (aARPMSG->sender_mac[5])) ;
    
  
    
    }


While if it is a ARP request, W5500EVB prints different string, showing that it receives an ARP request packet

else if((aARPMSG->opcode) == htons(ARP_REQUEST)) {

      printf( "Who has %.3u.%.3u.%.3u.%.3u ? ", 
             (aARPMSG->tgt_ip[0]),  (aARPMSG->tgt_ip[1]), 
             (aARPMSG->tgt_ip[2]),  (aARPMSG->tgt_ip[3])) ;
      printf(" Tell  ");
      printf( "%.2x.%.2x.%.2x.%.2x.%.2x.%.2x \r\b",  
             (aARPMSG->sender_mac[0]),  (aARPMSG->sender_mac[1]),  (aARPMSG->sender_mac[2]), 
             (aARPMSG->sender_mac[3]), (aARPMSG->sender_mac[4]),  (aARPMSG->sender_mac[5])) ;

    }else{
      printf(" This msg is not ARP reply : opcode is not 0x02 \n");	
    }
  }
}



arp_request() (ARP Packet Format)

For function arp_request(), it simply constructs a packet and sends the packet out. Therefore, instead of analyzing arp_request() here, a brief analysis of an ARP packet are done below:

typedef struct _ARPMSG
{
  /*************************Ethernet Header***************************/
	uint8  dst_mac[6];	// ff.ff.ff.ff.ff.ff
	uint8  src_mac[6];	
	uint16 msg_type;	// ARP (0x0806)

    /*************************ARP Format***************************/
	uint16 hw_type;		// Ethernet (0x0001)
	uint16 pro_type;	// IP	(0x0800)
	uint8  hw_size;		// 6
	uint8  pro_size;	// 4
	uint16 opcode;		// request (0x0001), reply(0x0002)
	uint8  sender_mac[6];	
	uint8  sender_ip[4];		
	uint8  tgt_mac[6];	// 00.00.00.00.00.00
	uint8  tgt_ip[4];

	uint8  trailer[22];	// All zeros, element of Ethernet frame(not necessary)
}ARPMSG;


To better understand the packet, it is suggested to take a look at the real packet data below. FYI, Padding shown below is the array trailer[], which is not necessary to be included in the packet.

Photo: Detail of an ARP Request Packet Sent by W5500EVB Using the Sample Code



Remarks

  • Static entries remain in ARP table forever and are not timed out. The default timeout timer for is 4 hours for Cisco devices, this means that a dynamic ARP entry will remain for 4 hours in the cache table before the router attempt to refresh the entry.
  • To understand about the difference of MAC and IP address, you are suggested to study about “routing” and try to compare the difference of routing and ARP
  • There are generally four types of ARP packets. This tutorial only demonstrated one of them.