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