1 /* 2 * Copyright 2011 Daniel Drown 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 * checksum.c - ipv4/ipv6 checksum calculation 17 */ 18 #include <netinet/in.h> 19 #include <netinet/ip.h> 20 #include <netinet/ip_icmp.h> 21 #include <netinet/udp.h> 22 #include <netinet/tcp.h> 23 #include <netinet/ip6.h> 24 #include <netinet/icmp6.h> 25 #include <linux/icmp.h> 26 27 #include "checksum.h" 28 29 /* function: ip_checksum_add 30 * adds data to a checksum 31 * current - the current checksum (or 0 to start a new checksum) 32 * data - the data to add to the checksum 33 * len - length of data 34 */ 35 uint32_t ip_checksum_add(uint32_t current, const void *data, int len) { 36 uint32_t checksum = current; 37 int left = len; 38 const uint16_t *data_16 = data; 39 40 while(left > 1) { 41 checksum += *data_16; 42 data_16++; 43 left -= 2; 44 } 45 if(left) { 46 checksum += *(uint8_t *)data_16; 47 } 48 49 return checksum; 50 } 51 52 /* function: ip_checksum_fold 53 * folds a 32-bit partial checksum into 16 bits 54 * temp_sum - sum from ip_checksum_add 55 * returns: the folded checksum in network byte order 56 */ 57 uint16_t ip_checksum_fold(uint32_t temp_sum) { 58 while(temp_sum > 0xffff) 59 temp_sum = (temp_sum >> 16) + (temp_sum & 0xFFFF); 60 61 return temp_sum; 62 } 63 64 /* function: ip_checksum_finish 65 * folds and closes the checksum 66 * temp_sum - sum from ip_checksum_add 67 * returns: a header checksum value in network byte order 68 */ 69 uint16_t ip_checksum_finish(uint32_t temp_sum) { 70 return ~ip_checksum_fold(temp_sum); 71 } 72 73 /* function: ip_checksum 74 * combined ip_checksum_add and ip_checksum_finish 75 * data - data to checksum 76 * len - length of data 77 */ 78 uint16_t ip_checksum(const void *data, int len) { 79 uint32_t temp_sum; 80 81 temp_sum = ip_checksum_add(0,data,len); 82 return ip_checksum_finish(temp_sum); 83 } 84 85 /* function: ipv6_pseudo_header_checksum 86 * calculate the pseudo header checksum for use in tcp/udp/icmp headers 87 * ip6 - the ipv6 header 88 * len - the transport length (transport header + payload) 89 * protocol - the transport layer protocol, can be different from ip6->ip6_nxt for fragments 90 */ 91 uint32_t ipv6_pseudo_header_checksum(const struct ip6_hdr *ip6, uint16_t len, uint8_t protocol) { 92 uint32_t checksum_len, checksum_next; 93 checksum_len = htonl((uint32_t) len); 94 checksum_next = htonl(protocol); 95 96 uint32_t current = 0; 97 current = ip_checksum_add(current, &(ip6->ip6_src), sizeof(struct in6_addr)); 98 current = ip_checksum_add(current, &(ip6->ip6_dst), sizeof(struct in6_addr)); 99 current = ip_checksum_add(current, &checksum_len, sizeof(checksum_len)); 100 current = ip_checksum_add(current, &checksum_next, sizeof(checksum_next)); 101 102 return current; 103 } 104 105 /* function: ipv4_pseudo_header_checksum 106 * calculate the pseudo header checksum for use in tcp/udp headers 107 * ip - the ipv4 header 108 * len - the transport length (transport header + payload) 109 */ 110 uint32_t ipv4_pseudo_header_checksum(const struct iphdr *ip, uint16_t len) { 111 uint16_t temp_protocol, temp_length; 112 113 temp_protocol = htons(ip->protocol); 114 temp_length = htons(len); 115 116 uint32_t current = 0; 117 current = ip_checksum_add(current, &(ip->saddr), sizeof(uint32_t)); 118 current = ip_checksum_add(current, &(ip->daddr), sizeof(uint32_t)); 119 current = ip_checksum_add(current, &temp_protocol, sizeof(uint16_t)); 120 current = ip_checksum_add(current, &temp_length, sizeof(uint16_t)); 121 122 return current; 123 } 124 125 /* function: ip_checksum_adjust 126 * calculates a new checksum given a previous checksum and the old and new pseudo-header checksums 127 * checksum - the header checksum in the original packet in network byte order 128 * old_hdr_sum - the pseudo-header checksum of the original packet 129 * new_hdr_sum - the pseudo-header checksum of the translated packet 130 * returns: the new header checksum in network byte order 131 */ 132 uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum) { 133 // Algorithm suggested in RFC 1624. 134 // http://tools.ietf.org/html/rfc1624#section-3 135 checksum = ~checksum; 136 uint16_t folded_sum = ip_checksum_fold(checksum + new_hdr_sum); 137 uint16_t folded_old = ip_checksum_fold(old_hdr_sum); 138 if (folded_sum > folded_old) { 139 return ~(folded_sum - folded_old); 140 } else { 141 return ~(folded_sum - folded_old - 1); // end-around borrow 142 } 143 } 144