1a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown/* 2a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown * Copyright 2011 Daniel Drown 3a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown * 4a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown * Licensed under the Apache License, Version 2.0 (the "License"); 5a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown * you may not use this file except in compliance with the License. 6a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown * You may obtain a copy of the License at 7a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown * 8a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown * http://www.apache.org/licenses/LICENSE-2.0 9a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown * 10a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown * Unless required by applicable law or agreed to in writing, software 11a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown * distributed under the License is distributed on an "AS IS" BASIS, 12a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown * See the License for the specific language governing permissions and 14a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown * limitations under the License. 15a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown * 16a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown * checksum.c - ipv4/ipv6 checksum calculation 17a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown */ 18a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown#include <netinet/in.h> 19a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown#include <netinet/ip.h> 20a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown#include <netinet/ip_icmp.h> 21a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown#include <netinet/udp.h> 22a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown#include <netinet/tcp.h> 23a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown#include <netinet/ip6.h> 24a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown#include <netinet/icmp6.h> 25a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown 26a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown#include "checksum.h" 27a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown 28a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown/* function: ip_checksum_add 29a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown * adds data to a checksum 300278627f576832860af2d84e04e383ecaa92d74fLorenzo Colitti * current - the current checksum (or 0 to start a new checksum) 31a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown * data - the data to add to the checksum 32a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown * len - length of data 33a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown */ 340278627f576832860af2d84e04e383ecaa92d74fLorenzo Colittiuint32_t ip_checksum_add(uint32_t current, const void *data, int len) { 350278627f576832860af2d84e04e383ecaa92d74fLorenzo Colitti uint32_t checksum = current; 36a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown int left = len; 37a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown const uint16_t *data_16 = data; 38a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown 39a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown while(left > 1) { 40a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown checksum += *data_16; 41a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown data_16++; 42a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown left -= 2; 43a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown } 44a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown if(left) { 45a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown checksum += *(uint8_t *)data_16; 46a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown } 47a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown 48a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown return checksum; 49a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown} 50a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown 515a50c0283346a197cda7af19e68f611f14b8fe57Lorenzo Colitti/* function: ip_checksum_fold 525a50c0283346a197cda7af19e68f611f14b8fe57Lorenzo Colitti * folds a 32-bit partial checksum into 16 bits 53a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown * temp_sum - sum from ip_checksum_add 545a50c0283346a197cda7af19e68f611f14b8fe57Lorenzo Colitti * returns: the folded checksum in network byte order 55a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown */ 565a50c0283346a197cda7af19e68f611f14b8fe57Lorenzo Colittiuint16_t ip_checksum_fold(uint32_t temp_sum) { 57a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown while(temp_sum > 0xffff) 58a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown temp_sum = (temp_sum >> 16) + (temp_sum & 0xFFFF); 59a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown 60a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown return temp_sum; 61a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown} 62a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown 635a50c0283346a197cda7af19e68f611f14b8fe57Lorenzo Colitti/* function: ip_checksum_finish 645a50c0283346a197cda7af19e68f611f14b8fe57Lorenzo Colitti * folds and closes the checksum 655a50c0283346a197cda7af19e68f611f14b8fe57Lorenzo Colitti * temp_sum - sum from ip_checksum_add 665a50c0283346a197cda7af19e68f611f14b8fe57Lorenzo Colitti * returns: a header checksum value in network byte order 675a50c0283346a197cda7af19e68f611f14b8fe57Lorenzo Colitti */ 685a50c0283346a197cda7af19e68f611f14b8fe57Lorenzo Colittiuint16_t ip_checksum_finish(uint32_t temp_sum) { 695a50c0283346a197cda7af19e68f611f14b8fe57Lorenzo Colitti return ~ip_checksum_fold(temp_sum); 705a50c0283346a197cda7af19e68f611f14b8fe57Lorenzo Colitti} 715a50c0283346a197cda7af19e68f611f14b8fe57Lorenzo Colitti 72a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown/* function: ip_checksum 73a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown * combined ip_checksum_add and ip_checksum_finish 74a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown * data - data to checksum 75a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown * len - length of data 76a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown */ 77a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drownuint16_t ip_checksum(const void *data, int len) { 78a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown uint32_t temp_sum; 79a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown 80a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown temp_sum = ip_checksum_add(0,data,len); 81a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown return ip_checksum_finish(temp_sum); 82a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown} 83a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown 84a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown/* function: ipv6_pseudo_header_checksum 85a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown * calculate the pseudo header checksum for use in tcp/udp/icmp headers 8607f0265830fcae2632159e9993b93a161d7ea23bLorenzo Colitti * ip6 - the ipv6 header 8707f0265830fcae2632159e9993b93a161d7ea23bLorenzo Colitti * len - the transport length (transport header + payload) 8807f0265830fcae2632159e9993b93a161d7ea23bLorenzo Colitti * protocol - the transport layer protocol, can be different from ip6->ip6_nxt for fragments 89a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown */ 9007f0265830fcae2632159e9993b93a161d7ea23bLorenzo Colittiuint32_t ipv6_pseudo_header_checksum(const struct ip6_hdr *ip6, uint16_t len, uint8_t protocol) { 91a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown uint32_t checksum_len, checksum_next; 920278627f576832860af2d84e04e383ecaa92d74fLorenzo Colitti checksum_len = htonl((uint32_t) len); 9307f0265830fcae2632159e9993b93a161d7ea23bLorenzo Colitti checksum_next = htonl(protocol); 94a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown 9507f0265830fcae2632159e9993b93a161d7ea23bLorenzo Colitti uint32_t current = 0; 960278627f576832860af2d84e04e383ecaa92d74fLorenzo Colitti current = ip_checksum_add(current, &(ip6->ip6_src), sizeof(struct in6_addr)); 970278627f576832860af2d84e04e383ecaa92d74fLorenzo Colitti current = ip_checksum_add(current, &(ip6->ip6_dst), sizeof(struct in6_addr)); 980278627f576832860af2d84e04e383ecaa92d74fLorenzo Colitti current = ip_checksum_add(current, &checksum_len, sizeof(checksum_len)); 990278627f576832860af2d84e04e383ecaa92d74fLorenzo Colitti current = ip_checksum_add(current, &checksum_next, sizeof(checksum_next)); 100a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown 1010278627f576832860af2d84e04e383ecaa92d74fLorenzo Colitti return current; 102a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown} 103a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown 104a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown/* function: ipv4_pseudo_header_checksum 105a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown * calculate the pseudo header checksum for use in tcp/udp headers 1060278627f576832860af2d84e04e383ecaa92d74fLorenzo Colitti * ip - the ipv4 header 1070278627f576832860af2d84e04e383ecaa92d74fLorenzo Colitti * len - the transport length (transport header + payload) 108a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown */ 10907f0265830fcae2632159e9993b93a161d7ea23bLorenzo Colittiuint32_t ipv4_pseudo_header_checksum(const struct iphdr *ip, uint16_t len) { 110a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown uint16_t temp_protocol, temp_length; 111a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown 112a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown temp_protocol = htons(ip->protocol); 1130278627f576832860af2d84e04e383ecaa92d74fLorenzo Colitti temp_length = htons(len); 114a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown 11507f0265830fcae2632159e9993b93a161d7ea23bLorenzo Colitti uint32_t current = 0; 1160278627f576832860af2d84e04e383ecaa92d74fLorenzo Colitti current = ip_checksum_add(current, &(ip->saddr), sizeof(uint32_t)); 1170278627f576832860af2d84e04e383ecaa92d74fLorenzo Colitti current = ip_checksum_add(current, &(ip->daddr), sizeof(uint32_t)); 1180278627f576832860af2d84e04e383ecaa92d74fLorenzo Colitti current = ip_checksum_add(current, &temp_protocol, sizeof(uint16_t)); 1190278627f576832860af2d84e04e383ecaa92d74fLorenzo Colitti current = ip_checksum_add(current, &temp_length, sizeof(uint16_t)); 120a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown 1210278627f576832860af2d84e04e383ecaa92d74fLorenzo Colitti return current; 122a45056e35c1af2a0f0a6eed258fd5fdf4846a79fDaniel Drown} 1235a50c0283346a197cda7af19e68f611f14b8fe57Lorenzo Colitti 1245a50c0283346a197cda7af19e68f611f14b8fe57Lorenzo Colitti/* function: ip_checksum_adjust 1255a50c0283346a197cda7af19e68f611f14b8fe57Lorenzo Colitti * calculates a new checksum given a previous checksum and the old and new pseudo-header checksums 1265a50c0283346a197cda7af19e68f611f14b8fe57Lorenzo Colitti * checksum - the header checksum in the original packet in network byte order 1275a50c0283346a197cda7af19e68f611f14b8fe57Lorenzo Colitti * old_hdr_sum - the pseudo-header checksum of the original packet 1285a50c0283346a197cda7af19e68f611f14b8fe57Lorenzo Colitti * new_hdr_sum - the pseudo-header checksum of the translated packet 1295a50c0283346a197cda7af19e68f611f14b8fe57Lorenzo Colitti * returns: the new header checksum in network byte order 1305a50c0283346a197cda7af19e68f611f14b8fe57Lorenzo Colitti */ 1315a50c0283346a197cda7af19e68f611f14b8fe57Lorenzo Colittiuint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum) { 1325a50c0283346a197cda7af19e68f611f14b8fe57Lorenzo Colitti // Algorithm suggested in RFC 1624. 1335a50c0283346a197cda7af19e68f611f14b8fe57Lorenzo Colitti // http://tools.ietf.org/html/rfc1624#section-3 1345a50c0283346a197cda7af19e68f611f14b8fe57Lorenzo Colitti checksum = ~checksum; 1355a50c0283346a197cda7af19e68f611f14b8fe57Lorenzo Colitti uint16_t folded_sum = ip_checksum_fold(checksum + new_hdr_sum); 1365a50c0283346a197cda7af19e68f611f14b8fe57Lorenzo Colitti uint16_t folded_old = ip_checksum_fold(old_hdr_sum); 1375a50c0283346a197cda7af19e68f611f14b8fe57Lorenzo Colitti if (folded_sum > folded_old) { 1385a50c0283346a197cda7af19e68f611f14b8fe57Lorenzo Colitti return ~(folded_sum - folded_old); 1395a50c0283346a197cda7af19e68f611f14b8fe57Lorenzo Colitti } else { 1405a50c0283346a197cda7af19e68f611f14b8fe57Lorenzo Colitti return ~(folded_sum - folded_old - 1); // end-around borrow 1415a50c0283346a197cda7af19e68f611f14b8fe57Lorenzo Colitti } 1425a50c0283346a197cda7af19e68f611f14b8fe57Lorenzo Colitti} 143