=> 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); }
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); }
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이 올바르게 재계산되었습니다. */ }
참고 영상 |