XPS LL TEMAC core calculates raw checksum over the entire Ethernet payload. According to the product specification, to verify the checksum, the checksum of the fields which should not have been included must be subtracted from the raw checksum and the adjusted raw checksum has to be compared with the checksum field of the TCP or UDP header.
But the above mentioned method is a time-consuming solution and I found a more optimized solution by which, the raw checksum is compared with the checksum of the fields which should not have been included. If both are equal, the checksum verification is passed. Otherwise, it is failed.
How is it possible?
Raw checksum = Checksum of TCP/UDP payload + Checksum field of TCP/UDP packet + Checksum of fields that should not have been included
For valid packet, the whole checksum(including the checksum field) of TCP/UDP packet is zero. So,
Raw checksum = 0 + Checksum of fields that should not have been included
Raw checksum = Checksum of fields that should not have been included
Pseudo code for the solution
bool temac_verify_csum(unsigned char *pkt_buf, unsigned short rx_csraw)
{
unsigned short *temp;
unsigned int csum;
if(ntohs(*(unsigned short *)(pkt_buf + 12)) == 0x0800)/* IPv4 */
{
struct ip_header *ip_hdr = (struct ip_header *)pkt_buf + 14;
temp = (unsigned short *)ip_hdr;
/* Other than TCP/UDP, pass to the upper layer */
if(ip_hdr->protocol != UDP && ip_hdr->protocol != TCP)
return PASSED;
/* If fragment, pass it to upper layer */
if(ip_hdr->fragment)
return PASSED;
/* UDP with checksum 0. No need to verify */
if(ip_hdr->protocol == UDP && *(temp + 13) == 0)
return PASSED;
/* Add the fields that should not be included */
csum = *temp++; /* Version, IHL, Differentiated Services */
csum += ip_header_length; /* 20 */
temp++;
csum += *temp++; /* Identification */
csum += *temp++; /* Flags and Fragment offset */
csum += *temp++ & htons(0xFF00); /* TTL */
csum += *temp; /* IP header checksum */
}
else if(ntohs(*(unsigned short *)(pkt_buf + 12)) == 0x86dd)/* IPv6 */
{
struct ip6_header *ip6_hdr = (struct ip6_header *)pkt_buf + 14;
temp = (unsigned short *)ip6_hdr;
if(ip6_hdr->next_header != TCP && ip6_next_header != UDP)
return PASSED;
if(ip6_hdr->next_header == UDP && *(temp + 23) == 0)
return PASSED;/* UDP with checksum 0. No need to verify */
csum = *temp++; /* Version, Traffic class */
csum += *temp++; /* and Flow Label */
temp++;
csum += *temp++; /* Next header and Hop Limit */
/* Next header must be included. So, minus it */
csum -= htons(ip6_hdr->next_header);
}
else
return PASSED;
csum = (csum & 0xFFFF) + (csum >> 16);
csum += (csum >> 16);
if((unsigned short)csum == rx_csraw)
return PASSED;
return FAILED;
}
But the above mentioned method is a time-consuming solution and I found a more optimized solution by which, the raw checksum is compared with the checksum of the fields which should not have been included. If both are equal, the checksum verification is passed. Otherwise, it is failed.
How is it possible?
Raw checksum = Checksum of TCP/UDP payload + Checksum field of TCP/UDP packet + Checksum of fields that should not have been included
For valid packet, the whole checksum(including the checksum field) of TCP/UDP packet is zero. So,
Raw checksum = 0 + Checksum of fields that should not have been included
Raw checksum = Checksum of fields that should not have been included
Pseudo code for the solution
bool temac_verify_csum(unsigned char *pkt_buf, unsigned short rx_csraw)
{
unsigned short *temp;
unsigned int csum;
if(ntohs(*(unsigned short *)(pkt_buf + 12)) == 0x0800)/* IPv4 */
{
struct ip_header *ip_hdr = (struct ip_header *)pkt_buf + 14;
temp = (unsigned short *)ip_hdr;
/* Other than TCP/UDP, pass to the upper layer */
if(ip_hdr->protocol != UDP && ip_hdr->protocol != TCP)
return PASSED;
/* If fragment, pass it to upper layer */
if(ip_hdr->fragment)
return PASSED;
/* UDP with checksum 0. No need to verify */
if(ip_hdr->protocol == UDP && *(temp + 13) == 0)
return PASSED;
/* Add the fields that should not be included */
csum = *temp++; /* Version, IHL, Differentiated Services */
csum += ip_header_length; /* 20 */
temp++;
csum += *temp++; /* Identification */
csum += *temp++; /* Flags and Fragment offset */
csum += *temp++ & htons(0xFF00); /* TTL */
csum += *temp; /* IP header checksum */
}
else if(ntohs(*(unsigned short *)(pkt_buf + 12)) == 0x86dd)/* IPv6 */
{
struct ip6_header *ip6_hdr = (struct ip6_header *)pkt_buf + 14;
temp = (unsigned short *)ip6_hdr;
if(ip6_hdr->next_header != TCP && ip6_next_header != UDP)
return PASSED;
if(ip6_hdr->next_header == UDP && *(temp + 23) == 0)
return PASSED;/* UDP with checksum 0. No need to verify */
csum = *temp++; /* Version, Traffic class */
csum += *temp++; /* and Flow Label */
temp++;
csum += *temp++; /* Next header and Hop Limit */
/* Next header must be included. So, minus it */
csum -= htons(ip6_hdr->next_header);
}
else
return PASSED;
csum = (csum & 0xFFFF) + (csum >> 16);
csum += (csum >> 16);
if((unsigned short)csum == rx_csraw)
return PASSED;
return FAILED;
}
Where rx_csraw is raw checksum in the descriptor and pkt_buf is pointer to the buffer of the descriptor. Surely, it is an optimized solution, right?
For details on checksum offload of transmission side, look at the following post:
http://embeddedknowledge.blogspot.com/2009/08/xilinx-temac-checksum-offload.html
If you have any queries, write them as comments.
For details on checksum offload of transmission side, look at the following post:
http://embeddedknowledge.blogspot.com/2009/08/xilinx-temac-checksum-offload.html
If you have any queries, write them as comments.
No comments:
Post a Comment