summaryrefslogtreecommitdiffstats
path: root/checksum.c
diff options
context:
space:
mode:
authorLorenzo Colitti <lorenzo@google.com>2014-02-10 09:20:05 +0900
committerLorenzo Colitti <lorenzo@google.com>2014-02-14 13:33:04 +0900
commit5a50c0283346a197cda7af19e68f611f14b8fe57 (patch)
tree1cccafeba5bbd41374daa0aed35379faa4c26a88 /checksum.c
parenta33592bd08a20c6a521b8508975b7a74ecdf4f03 (diff)
downloadandroid_external_android-clat-5a50c0283346a197cda7af19e68f611f14b8fe57.tar.gz
android_external_android-clat-5a50c0283346a197cda7af19e68f611f14b8fe57.tar.bz2
android_external_android-clat-5a50c0283346a197cda7af19e68f611f14b8fe57.zip
Fix up checksums instead of recalculating them.
Currently the checksums of translated packets are calculated from scratch by checksumming the translated packet. This is slow and does not work in the case of fragments, because the whole packet is not available. Instead, calculate the checksum by adjusting the checksum of the original packet. Bug: 11542311 Bug: 12116252 Change-Id: I6b78a94ca5bd96b13ee2653b6200551193b3dcc1
Diffstat (limited to 'checksum.c')
-rw-r--r--checksum.c38
1 files changed, 33 insertions, 5 deletions
diff --git a/checksum.c b/checksum.c
index a4dc9b8..099be6a 100644
--- a/checksum.c
+++ b/checksum.c
@@ -49,19 +49,27 @@ uint32_t ip_checksum_add(uint32_t current, const void *data, int len) {
return checksum;
}
-/* function: ip_checksum_finish
- * close the checksum
+/* function: ip_checksum_fold
+ * folds a 32-bit partial checksum into 16 bits
* temp_sum - sum from ip_checksum_add
+ * returns: the folded checksum in network byte order
*/
-uint16_t ip_checksum_finish(uint32_t temp_sum) {
+uint16_t ip_checksum_fold(uint32_t temp_sum) {
while(temp_sum > 0xffff)
temp_sum = (temp_sum >> 16) + (temp_sum & 0xFFFF);
- temp_sum = (~temp_sum) & 0xffff;
-
return temp_sum;
}
+/* function: ip_checksum_finish
+ * folds and closes the checksum
+ * temp_sum - sum from ip_checksum_add
+ * returns: a header checksum value in network byte order
+ */
+uint16_t ip_checksum_finish(uint32_t temp_sum) {
+ return ~ip_checksum_fold(temp_sum);
+}
+
/* function: ip_checksum
* combined ip_checksum_add and ip_checksum_finish
* data - data to checksum
@@ -113,3 +121,23 @@ uint32_t ipv4_pseudo_header_checksum(uint32_t current, const struct iphdr *ip, u
return current;
}
+
+/* function: ip_checksum_adjust
+ * calculates a new checksum given a previous checksum and the old and new pseudo-header checksums
+ * checksum - the header checksum in the original packet in network byte order
+ * old_hdr_sum - the pseudo-header checksum of the original packet
+ * new_hdr_sum - the pseudo-header checksum of the translated packet
+ * returns: the new header checksum in network byte order
+ */
+uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum) {
+ // Algorithm suggested in RFC 1624.
+ // http://tools.ietf.org/html/rfc1624#section-3
+ checksum = ~checksum;
+ uint16_t folded_sum = ip_checksum_fold(checksum + new_hdr_sum);
+ uint16_t folded_old = ip_checksum_fold(old_hdr_sum);
+ if (folded_sum > folded_old) {
+ return ~(folded_sum - folded_old);
+ } else {
+ return ~(folded_sum - folded_old - 1); // end-around borrow
+ }
+}