RFC1071 - Computing the Internet Checksum
(https://tools.ietf.org/html/rfc1071)" 에서 제시하는 checksum 알고리즘은 IP, UDP, TCP protocol 에서 packet의 오류 정정 확인을 위한 용도로 사용됩니다. (IP 뿐만 아니라 Transport layer 에서 많은 protocol 이 같은 알고리즘을 사용합니다.)
=> do checksum
[과정]
[L00] 1011 0110 1100 1011 B6CBH /* INPUT DATA A */
[L01] + 0100 1001 0011 0110 + 4936H /* INPUT DATA B */
[L02] --------------------- -------
[L03] Carry 1 0000 0000 0000 0001 1 0001H /* sum = A + B , carry = (sum < (A + B)) ? 1 : 0 */
[L04] + Carry 1 + 1H /* sum += carry */
[L05] --------------------- -------
[L06] 0000 0000 0000 0010 0002H
[L07] ~ ~ /* 1's complement */
[L08] --------------------- -------
[L09] 1111 1111 1111 1101 FFFDH /* checksum = ~sum */
=> checksum verify function
[L06] 0000 0000 0000 0010 0002H /* sum = A + B , sum += carry */
[L09] + 1111 1111 1111 1101 + FFFDH /* sum += checksum */
--------------------- -------
Carry 0 1111 1111 1111 1111 FFFFH /* checksum 이 올바르게 계산된 값이라면,
이를 검증하는 방법으로
항상 어떤 INPUT DATA 에 대하여도
INPUT DATA의 합 sum과 sum을 1의 보수로 하는 결과 값인 checksum 을
더하면 그 결과 값은 항상 모든 bit가 1이 되는 특성을 갖습니다.
(즉, L06과 L09 값을 더하는 것에서는 FFFFH 로 동일한 값이 나옵니다.)
여기서 checksum 값이 0000H인 경우는 +0 그리고 FFFFH인 경우는 -0 을 의미하기 때문에
정확히 따지면 다른 값으로 취급되어야 하겠지만 checksum verify 과정에서는 차이가 없습니다.
Carry bit는 부호 없는 정수의 덧셈에서 그 정수 범위를 넘어서면 Carry bit가 set(1)됩니다.
*/
{
/* Compute Internet Checksum for "count" bytes
* beginning at location "addr".
*/
register long sum = 0;
while( count > 1 ) {
/* This is the inner loop */
sum += * (unsigned short) addr++;
count -= 2;
}
/* Add left-over byte, if any */
if( count > 0 )
sum += * (unsigned char *) addr;
/* Fold 32-bit sum to 16 bits */
while (sum>>16)
sum = (sum & 0xffff) + (sum >> 16);
checksum = ~sum;
}
unsigned int hwport_rfc1071_checksum(const void *s_data, size_t s_size)
{ /* hwport_internet_checksum (RFC1071) */
register uint32_t s_result = (uint32_t)0u;
while(s_size > ((size_t)1u)) {
s_result += (uint32_t)(*((const uint16_t *)s_data));
s_data = ((const uint16_t *)s_data) + ((size_t)1u);
s_size -= (size_t)2u;
}
if(s_size > ((size_t)0u)) {
s_result += (uint32_t)(*((const uint8_t *)s_data));
}
/* Fold 32-bit sum to 16 bits */
s_result = (s_result >> 16) + (s_result & ((uint32_t)0xffffu));
s_result += s_result >> 16;
return((unsigned int)((~s_result) & ((uint32_t)0xffffu)));
}
static inline uint16_t csum16_add(uint16_t csum, /* BE16 */ uint16_t addend)
{
uint16_t res = (uint16_t)csum;
res += (uint16_t)addend;
return (uint16_t)(res + (res < (uint16_t)addend));
}
static inline uint16_t csum16_sub(uint16_t csum, /* BE16 */ uint16_t addend)
{
return csum16_add(csum, ~addend);
}
static inline void csum_replace2(uint16_t *sum, /* BE16 */ uint16_t old, /* BE16 */ uint16_t new)
{
*sum = ~csum16_add(csum16_sub(~(*sum), old), new);
}
csum_replace2(&checksum, A, B);
typedef uint16_t sum16_t;
typedef uint32_t sum32_t;
typedef uint64_t sum64_t;
#if 0L
# define wsum_width 32
typedef sum32_t wsum_t;
#else
# define wsum_width 64
typedef sum64_t wsum_t;
#endif
static __inline sum16_t csum_fold(wsum_t sum)
{
#if wsum_width == 64
/* 64bits 값을 32bits 로 줄일 때 상위 32bits를 하위에 합산 */
sum = (sum & 0xfffffffful) /* 하위 32bits */ + (sum >> 32) /* 상위 32bits */;
sum = (sum & 0xfffffffful) /* 하위 32bits */ + (sum >> 32) /* 상위 32bits */;
#endif
/* 32bits 값을 16bits 로 줄일 때 상위 16bits를 하위에 합산 */
sum = (sum & 0xffffu) /* 하위 16bits */ + (sum >> 16) /* 상위 16bits */;
sum = (sum & 0xffffu) /* 하위 16bits */ + (sum >> 16) /* 상위 16bits */;
returm (sum16_t)(~sum); /* 1의 보수로 반환 */
}
static __inline wsum_t csum_unfold(sum16_t sum)
{
return (wsum_t)sum;
}
static __inline wsum_t csum_add(wsum_t sum, wsum_t addend)
{
sum += addend; /* 주어진 sum 과 addend 를 합산 */
return sum + (sum < addend) /* carry */;
}
static __inline sum16_t csum16_add(sum16_t sum, sum16_t addend)
{
sum += addend; /* 주어진 sum 과 addend 를 합산 */
return sum + (sum < addend) /* carry */;
}
static __inline wsum_t csum_sub(wsum_t sum, wsum_t addend)
{
return csum_add(sum, ~addend);
}
static __inline sum16_t csum16_sub(sum16_t sum, sum16_t addend)
{
return csum16_add(sum, ~addend);
}
static __inline void csum_replace(wsum_t *sum_ptr, wsum_t from, wsum_t to)
{
*sum_ptr = ~csum_add(csum_sub(*sum_ptr, from), to);
}
static __inline void csum_replace2(sum16_t *sum_ptr, uint16_t from, uint16_t to)
{
*sum_ptr = ~csum16_add(csum16_sub(~(*sum_ptr), (sum16_t)from), (sum16_t)to);
}
static __inline void csum_replace4(sum16_t *sum_ptr, uint32_t from, uint32_t to)
{
*sum_ptr = csum_fold(csum_add(csum_sub(~csum_unfold(*sum_ptr), (wsum_t)from), (wsum_t)to));
}
static __inline wsum_t csum_partial(const void *data, size_t size, wsum_t sum)
{
while (size >= sizeof(sum16_t)) {
sum = csum_add(sum, (wsum_t)(*((const uint16_t *)data)));
size -= sizeof(uint16_t);
data = (const void *)(((const uint8_t *)data) + sizeof(uint16_t));
}
if (size > ((size_t)0u)) { /* 남은 홀수 합산 */
sum = csum_add(sum, (wsum_t)htons(((uint16_t)(*((const uint8_t *)data))) << 8));
}
return sum;
}
static __inline sum16_t ip_compute_csum(const void *data, size_t size)
{
return csum_fold(csum_partial(data, size, (wsum_t)0u));
}
static __inline sum16_t ip_fast_csum(const void *iph, unsigned int ihl)
{ /* ip_compute_csum optimized, 4 octet boundaries */
#if 0L /* fast, no carry */
wsum_t sum = (wsum_t)0u;
while (ihl > 0u) {
sum = csum_add(sum, (wsum_t)(*((const uint32_t *)iph)));
--ihl;
iph = (const void *)(((const uint8_t *)iph) + sizeof(uint32_t));
}
return (sum16_t)(~wcsum_to_sum16((uint32_t)sum));
#else /* full */
return ip_compute_csum(iph, (size_t)(ihl << 2));
#endif
}
static __inline void ip_send_check(struct iphdr *iph)
{
iph->check = 0;
iph->check = ip_fast_csum((const void *)iph, (unsigned int)iph->ihl);
}
static __inline int ip_decrease_ttl(struct iphdr *iph)
{
wsum_t sum = (wsum_t)iph->check;
sum += (wsum_t)htons(0x0100);
iph->check = (sum16_t)(sum + (sum >= ((wsum_t)0xffffu)));
return --iph->ttl;
}
static __inline wsum_t csum_tcpudp_nofold(uint32_t saddr, uint32_t daddr, uint32_t len, uint8_t proto, wsum_t sum)
{
sum = csum_add(sum, (wsum_t)saddr);
sum = csum_add(sum, (wsum_t)daddr);
sum = csum_add(sum, (wsum_t)htonl(len));
sum = csum_add(sum, (wsum_t)htonl(proto));
return sum;
}
static __inline sum16_t csum_tcpudp_magic(uint32_t saddr, uint32_t daddr, uint32_t len, uint8_t proto, wsum_t sum)
{
return csum_fold(csum_tcpudp_nofold(saddr, daddr, len, proto, sum));
}
static __inline sum16_t tcp_v4_check(uint32_t len, uint32_t saddr, uint32_t daddr, wsum_t sum)
{
return csum_tcpudp_magic(saddr, daddr, len, IPPROTO_TCP, sum);
}
static __inline sum16_t udp_v4_check(uint32_t len, uint32_t saddr, uint32_t daddr, wsum_t sum)
{
return csum_tcpudp_magic(saddr, daddr, len, IPPROTO_UDP, sum);
}
RFC793 Section-3.1
(https://tools.ietf.org/html/rfc793#section-3.1))
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | Description | |
| 32-bit Source Address | ↑↓8 bytes | ↑↓12 bytes | |||||||||||||||||||||||||||||||
| 32-bit Destination Address | |||||||||||||||||||||||||||||||||
| 8-bit Zero | 8-bit PTCL(Protocol) | 16-bit TCP Length (TCP Header + Data) | ↑↓4 bytes | ||||||||||||||||||||||||||||||
static __inline sum16_t csum_ipv6_magic(const struct in6_addr *saddr, const struct in6_addr *daddr, uint32_t len, uint8_t proto, wsum_t sum)
{
sum = csum_partial((const void *)saddr, sizeof(*saddr), sum);
sum = csum_partial((const void *)daddr, sizeof(*saddr), sum);
sum = csum_add(sum, (wsum_t)htonl(len));
sum = csum_add(sum, (wsum_t)htonl(proto));
return csum_fold(sum);
}
static __inline sum16_t tcp_v6_check(uint32_t len, const struct in6_addr *saddr, const struct in6_addr *daddr, wsum_t sum)
{
return csum_ipv6_magic(saddr, daddr, len, IPPROTO_TCP, sum);
}
static __inline sum16_t udp_v6_check(uint32_t len, const struct in6_addr *saddr, const struct in6_addr *daddr, wsum_t sum)
{
return csum_ipv6_magic(saddr, daddr, len, IPPROTO_UDP, sum);
}
RFC2460 Section-8.1
(https://tools.ietf.org/html/rfc2460#section-8.1))
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | Description | |
| 128-bit Source Address | ↑↓32 bytes | ↑↓40 bytes | |||||||||||||||||||||||||||||||
| 128-bit Destination Address (Final destination address) | |||||||||||||||||||||||||||||||||
| 32-bit Upper-Layer Packet Length | ↑↓4 bytes | ||||||||||||||||||||||||||||||||
| 24-bit Zero | 8-bit Next Header | ↑↓4 bytes | |||||||||||||||||||||||||||||||
struct iphdr *iph = ip_hdr(skb); iph->check = 0; // 0으로 초기화 필요 iph->check = csum_fold(csum_partial(iph, iph->ihl << 2, 0));
struct iphdr *iph = ip_hdr(skb); iph->check = 0; // 0으로 초기화 필요 iph->check = ip_compute_csum(iph, iph->ihl << 2);
struct iphdr *iph = ip_hdr(skb); iph->check = 0; // 0으로 초기화 필요 iph->check = ip_fast_csum(iph, iph->ihl /* 4 octet boundaries */); // ip_fast_csum 함수는 IPv4 header checksum 만을 위해서 성능적으로 특화된 함수입니다.
struct iphdr *iph = ip_hdr(skb); ip_send_check(iph); // ip_send_check 함수는 내부적으로 iph->check을 0으로 초기화 하고 계산을 하여 iph->check 에 저장까지 합니다.
struct iphdr *iph = ip_hdr(skb);
if (unlikely(csum_fold(csum_partial(iph, iph->ihl << 2, 0)) != 0)) {
/* checksum error */
}
struct iphdr *iph = ip_hdr(skb);
if (unlikely(ip_compute_csum(iph, iph->ihl << 2) != 0)) {
/* checksum error */
}
struct iphdr *iph = ip_hdr(skb);
if (unlikely(ip_fast_csum(iph, iph->ihl) != 0)) {
/* checksum error */
}
struct iphdr *iph = ip_hdr(skb);
__sum16 verify_csum = ip_fast_csum(iph, iph->ihl);
if (unlikely(verify_csum != 0)) {
/* checksum error */
/* checksum 을 다시 계산하는 방법 (그냥 다시 계산하는 방법이지만 ip_fast_csum을 두 번이나 반복하는 계산이 있다는 점) */
iph->check = 0;
iph->check = ip_fast_csum(iph, iph->ihl);
/* 이제 IPv4 checksum이 올바르게 재계산되었습니다. */
}
struct iphdr *iph = ip_hdr(skb);
__sum16 verify_csum = ip_fast_csum(iph, iph->ihl);
if (unlikely(verify_csum != 0)) {
/* checksum error */
/* 이미 ip_fast_csum 함수로 검증계산을 하였기 때문에 잘못된 값만 보정(뺄셈)하면 ip checksum 이 올바르게 된다는 점. */
iph->check = ~csum16_sub(~verify_csum /* 검증값이 1의 보수로 0이 아닌 상태 */, iph->check /* 잘못계산된 저장값을 빼주기 위해 */);
/* 이제 IPv4 checksum이 올바르게 재계산되었습니다. */
}
RFC768 User Datagram Protocol
(https://tools.ietf.org/html/rfc768)
RFC1071 - Computing the Internet Checksum
(https://tools.ietf.org/html/rfc1071)
https://en.wikipedia.org/wiki/IPv4_header_checksum
https://coding-factory.tistory.com/653
| 참고 영상 |