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 26 #include "checksum.h" 27 28 /* function: ip_checksum_add 29 * adds data to a checksum 30 * current - the current checksum (or 0 to start a new checksum) 31 * data - the data to add to the checksum 32 * len - length of data 33 */ 34 uint32_t ip_checksum_add(uint32_t current, const void *data, int len) { 35 uint32_t checksum = current; 36 int left = len; 37 const uint16_t *data_16 = data; 38 39 while(left > 1) { 40 checksum += *data_16; 41 data_16++; 42 left -= 2; 43 } 44 if(left) { 45 checksum += *(uint8_t *)data_16; 46 } 47 48 return checksum; 49 } 50 51 /* function: ip_checksum_fold 52 * folds a 32-bit partial checksum into 16 bits 53 * temp_sum - sum from ip_checksum_add 54 * returns: the folded checksum in network byte order 55 */ 56 uint16_t ip_checksum_fold(uint32_t temp_sum) { 57 while(temp_sum > 0xffff) 58 temp_sum = (temp_sum >> 16) + (temp_sum & 0xFFFF); 59 60 return temp_sum; 61 } 62 63 /* function: ip_checksum_finish 64 * folds and closes the checksum 65 * temp_sum - sum from ip_checksum_add 66 * returns: a header checksum value in network byte order 67 */ 68 uint16_t ip_checksum_finish(uint32_t temp_sum) { 69 return ~ip_checksum_fold(temp_sum); 70 } 71 72 /* function: ip_checksum 73 * combined ip_checksum_add and ip_checksum_finish 74 * data - data to checksum 75 * len - length of data 76 */ 77 uint16_t ip_checksum(const void *data, int len) { 78 uint32_t temp_sum; 79 80 temp_sum = ip_checksum_add(0,data,len); 81 return ip_checksum_finish(temp_sum); 82 } 83 84 /* function: ipv6_pseudo_header_checksum 85 * calculate the pseudo header checksum for use in tcp/udp/icmp headers 86 * ip6 - the ipv6 header 87 * len - the transport length (transport header + payload) 88 * protocol - the transport layer protocol, can be different from ip6->ip6_nxt for fragments 89 */ 90 uint32_t ipv6_pseudo_header_checksum(const struct ip6_hdr *ip6, uint16_t len, uint8_t protocol) { 91 uint32_t checksum_len, checksum_next; 92 checksum_len = htonl((uint32_t) len); 93 checksum_next = htonl(protocol); 94 95 uint32_t current = 0; 96 current = ip_checksum_add(current, &(ip6->ip6_src), sizeof(struct in6_addr)); 97 current = ip_checksum_add(current, &(ip6->ip6_dst), sizeof(struct in6_addr)); 98 current = ip_checksum_add(current, &checksum_len, sizeof(checksum_len)); 99 current = ip_checksum_add(current, &checksum_next, sizeof(checksum_next)); 100 101 return current; 102 } 103 104 /* function: ipv4_pseudo_header_checksum 105 * calculate the pseudo header checksum for use in tcp/udp headers 106 * ip - the ipv4 header 107 * len - the transport length (transport header + payload) 108 */ 109 uint32_t ipv4_pseudo_header_checksum(const struct iphdr *ip, uint16_t len) { 110 uint16_t temp_protocol, temp_length; 111 112 temp_protocol = htons(ip->protocol); 113 temp_length = htons(len); 114 115 uint32_t current = 0; 116 current = ip_checksum_add(current, &(ip->saddr), sizeof(uint32_t)); 117 current = ip_checksum_add(current, &(ip->daddr), sizeof(uint32_t)); 118 current = ip_checksum_add(current, &temp_protocol, sizeof(uint16_t)); 119 current = ip_checksum_add(current, &temp_length, sizeof(uint16_t)); 120 121 return current; 122 } 123 124 /* function: ip_checksum_adjust 125 * calculates a new checksum given a previous checksum and the old and new pseudo-header checksums 126 * checksum - the header checksum in the original packet in network byte order 127 * old_hdr_sum - the pseudo-header checksum of the original packet 128 * new_hdr_sum - the pseudo-header checksum of the translated packet 129 * returns: the new header checksum in network byte order 130 */ 131 uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum) { 132 // Algorithm suggested in RFC 1624. 133 // http://tools.ietf.org/html/rfc1624#section-3 134 checksum = ~checksum; 135 uint16_t folded_sum = ip_checksum_fold(checksum + new_hdr_sum); 136 uint16_t folded_old = ip_checksum_fold(old_hdr_sum); 137 if (folded_sum > folded_old) { 138 return ~(folded_sum - folded_old); 139 } else { 140 return ~(folded_sum - folded_old - 1); // end-around borrow 141 } 142 } 143