[pisa-src] r1011 - in trunk: include libpisa pisacd pisasd

  • From: Thomas Jansen <mithi@xxxxxxxxx>
  • To: pisa-src@xxxxxxxxxxxxx
  • Date: Fri, 02 Oct 2009 14:30:01 +0200

Author: tjansen
Date: Fri Oct  2 14:30:01 2009
New Revision: 1011

Log:
Rewrote checksum updates for NAT mappings.

Previously, we recalculated the checksum of the whole packet from scratch.
This does not work for fragmented TCP and UDP packets, as the packet is not
completely known when we receive the first fragment. Instead, we take the old
checksum and update it by adding the complement of the old IP address and then
adding the new IP address. As we no longer need the whole packet, NAT works
with fragmented packages now.

Added:
   trunk/include/checksum.h
Modified:
   trunk/include/Makefile.am
   trunk/include/nat.h
   trunk/libpisa/nat.c
   trunk/pisacd/cdtun.c
   trunk/pisasd/sdtun.c

Modified: trunk/include/Makefile.am
==============================================================================
--- trunk/include/Makefile.am   Wed Sep 30 18:17:31 2009        (r1010)
+++ trunk/include/Makefile.am   Fri Oct  2 14:30:01 2009        (r1011)
@@ -5,6 +5,7 @@
 pisainclude_HEADERS= \
        ac_config.h \
        buffer.h \
+       checksum.h \
        config.h \
        crypto.h \
        debug.h \

Added: trunk/include/checksum.h
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ trunk/include/checksum.h    Fri Oct  2 14:30:01 2009        (r1011)
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2009, Distributed Systems Group, RWTH Aachen
+ * All rights reserved.
+ */
+
+/**
+ * @file checksum.h
+ * @brief Checksum manipulation functions.
+ * @author Thomas Jansen <mithi@xxxxxxxxx>
+ * @date Oct. 2009
+ */
+
+#ifndef PISA_CHECKSUM_H
+#define PISA_CHECKSUM_H
+
+typedef uint16_t ps_csum16;
+typedef uint32_t ps_csum32;
+
+/**
+ * Fold a 32-bit checksum into a 16-bit checksum.
+ * @param c checksum that will be folded
+ * @return folded checksum (16 bit)
+ */
+static inline uint16_t ps_csum_fold(ps_csum32 c)
+{
+       while (c >> 16)
+               c = (c & 0xffff) + (c >> 16);
+       return c;
+}
+
+/**
+ * Unfold a 16-bit checksum into a 32-bit checksum.
+ * @param c checksum that will be unfolded
+ * @return unfolded checksum (32 bit)
+ */
+static inline ps_csum32 ps_csum_unfold(ps_csum16 c)
+{
+       return (ps_csum32) c;
+}
+
+/**
+ * Replace 4 bytes in a checksum.
+ * @param addr pointer to the current checksum. Will be updated to the new
+ *     checksum when the functions returns.
+ * @param before 4 bytes that were used to calculate the old checksum and
+ *     should be replaced.
+ * @param after 4 bytes that will become part of the new checksum.
+ */
+static inline void ps_csum_replace4(ps_csum16 *addr, uint32_t before, uint32_t 
after)
+{
+       ps_csum32 csum = ps_csum_unfold(~*addr);
+       csum += ((~before) & 0xffff) + ((~before) >> 16);
+       csum += (after & 0xffff) + (after >> 16);
+       *addr = ~ps_csum_fold(csum);
+}
+
+#endif /* PISA_CHECKSUM_H */

Modified: trunk/include/nat.h
==============================================================================
--- trunk/include/nat.h Wed Sep 30 18:17:31 2009        (r1010)
+++ trunk/include/nat.h Fri Oct  2 14:30:01 2009        (r1011)
@@ -50,6 +50,6 @@
 void pisa_nat_update_connection(pisa_nat_list *natlist, pisa_nat_mapping *map, 
pisa_conmgr_entry *entry);
 
 struct iphdr *pisa_nat_get_iphdr(char *packet);
-void pisa_nat_fix_headers(struct iphdr *ip);
+void pisa_nat_apply(struct iphdr *ip, struct in_addr *old_addr, struct in_addr 
*new_addr);
 
 #endif /* PISA_NAT_H */

Modified: trunk/libpisa/nat.c
==============================================================================
--- trunk/libpisa/nat.c Wed Sep 30 18:17:31 2009        (r1010)
+++ trunk/libpisa/nat.c Fri Oct  2 14:30:01 2009        (r1011)
@@ -13,113 +13,44 @@
 #include <netinet/ip.h>
 #include <netinet/udp.h>
 #include <netinet/tcp.h>
-#include "nat.h"
-
-/**
- * Fix the IPv4 header checksum for a modified packet.
- * @param hdr pointer to the IPv4 header of the modified packet
- */
-static void pisa_nat_fix_ipv4_checksum(struct iphdr *ip)
-{
-       unsigned short *w = (unsigned short *) ip;
-       unsigned long checksum = 0;
-       int hdrlen;
-
-       ip->check = 0;
-       for (hdrlen = ip->ihl * 4; hdrlen > 1; hdrlen -= 2)
-               checksum += *w++;
-       if (hdrlen == 1) {
-               unsigned short padding = 0;
-               *(unsigned char *)(&padding)=*(unsigned char *)w;
-               checksum += padding;
-       }
-
-       checksum = (checksum >> 16) + (checksum & 0xffff);
-       checksum += (checksum >> 16);
-
-       ip->check = ~checksum;
-}
-
-#define CHECKSUM_CARRY(x) \
-(x = (x >> 16) + (x & 0xffff), (~(x + (x >> 16)) & 0xffff))
-
-/**
- * Fix the checksum of the UDP header.
- * @param ip IPv4 header of the packet
- */
-static void pisa_nat_fix_udp_checksum(struct iphdr *ip)
-{
-       unsigned long sum = 0;
-       u_int16_t *w = (u_int16_t *)((unsigned char*)ip + (ip->ihl * 4));
-       int len;
-       struct udphdr *udp = (struct udphdr *) w;
-
-       udp->check = 0;
-
-       /* UDP header and data */
-       for (len = ntohs(udp->len); len > 0; len -= 2)
-               sum += *w++;
-       if (len == 1) {
-               unsigned short padding = 0;
-               *(unsigned char *)(&padding)=*(unsigned char *)w;
-               sum += padding;
-       }
 
-       /* add UDP pseudoheader, consisting of src and dst IPv4, protocol
-        * number and total length (UDP header and payload) */
-       w = (u_int16_t *) &ip->saddr;
-       for (len = 0; len < 4; len++)
-               sum += *w++;
-       sum += htons(IPPROTO_UDP) + udp->len;
-
-       /* set the checksum */
-       udp->check = (CHECKSUM_CARRY(sum));
-}
+#include "checksum.h"
+#include "nat.h"
 
 /**
- * Fix the checksum of the TCP header.
- * @param ip IPv4 header of the packet
- */
-static void pisa_nat_fix_tcp_checksum(struct iphdr *ip)
-{
-       unsigned long sum = 0;
-       u_int16_t *w = (u_int16_t *)((unsigned char*)ip + (ip->ihl * 4));
-       int len;
-       struct tcphdr *tcp = (struct tcphdr *) w;
-
-       tcp->check = 0;
-
-       /* TCP header and data */
-       for (len = ntohs(ip->tot_len) - (ip->ihl * 4); len > 0; len -= 2)
-               sum += *w++;
-       if (len == 1) {
-               unsigned short padding = 0;
-               *(unsigned char *)(&padding)=*(unsigned char *)w;
-               sum += padding;
+ * Apply NAT mapping to a given IPv4 packet.
+ * @param ip pointer to the IPv4 header of the packet
+ * @param before pointer to the old IPv4 address in the packet. Can be
+ *     either src or dst and points into the packet buffer starting at ip.
+ * @param after pointer to the new IPv4 address. Points to memory outside
+ *     the packet buffer.
+ */
+void pisa_nat_apply(struct iphdr *ip, struct in_addr *before, struct in_addr 
*after)
+{
+       /* Only the first fragment can contain the TCP or UDP header */
+       if ((ntohs(ip->frag_off) & IP_OFFMASK) == 0) {
+               ps_csum16 *csum = NULL;
+
+               if (ip->protocol == IPPROTO_TCP)
+                       csum = &(((struct tcphdr *)(ip+1))->check);
+               else if (ip->protocol == IPPROTO_UDP)
+                       csum = &(((struct udphdr *)(ip+1))->check);
+
+               /* If we have a protocol that requires a checksum update,
+                * csum points to the checksum in the packet. Instead of
+                * recalculating the whole checksum from scratch (impossible
+                * with fragmented packets), we just subtract the old value
+                * and add the new value.*/
+               if (csum != NULL)
+                       ps_csum_replace4(csum, before->s_addr, after->s_addr);
        }
 
-       /* add TCP pseudoheader, consisting of src and dst IPv4, protocol
-        * number and total length (TCP header and payload) */
-       w = (u_int16_t *) &ip->saddr;
-       for (len = 0; len < 4; len++)
-               sum += *w++;
-       sum += htons(IPPROTO_TCP) + htons(ntohs(ip->tot_len) - (ip->ihl * 4));
-
-       tcp->check = (CHECKSUM_CARRY(sum));
-}
-
-/**
- * Fix all headers in a modified packet.
- * @param hdr pointer to the IPv4 header of the modified packet
- */
-void pisa_nat_fix_headers(struct iphdr *ip)
-{
-       pisa_nat_fix_ipv4_checksum(ip);
+       /* The IP header has to be updated in every case. */
+       ps_csum_replace4(&ip->check, before->s_addr, after->s_addr);
 
-       if (ip->protocol == IPPROTO_TCP)
-               pisa_nat_fix_tcp_checksum(ip);
-       else if (ip->protocol == IPPROTO_UDP)
-               pisa_nat_fix_udp_checksum(ip);
+       /* Overwrite the IP address in the packet only after we don't need the
+        * old value anymore. */
+       pisa_ipv4_copy(before, after);
 }
 
 /**

Modified: trunk/pisacd/cdtun.c
==============================================================================
--- trunk/pisacd/cdtun.c        Wed Sep 30 18:17:31 2009        (r1010)
+++ trunk/pisacd/cdtun.c        Fri Oct  2 14:30:01 2009        (r1011)
@@ -423,11 +423,8 @@
                /* Apply NAT if needed */
                srcaddr = (struct in_addr *)&hdr->saddr;
                map = pisa_nat_mapping_find_by_remote(cd_ctx.natlist, 
&from.sin6_addr, srcaddr);
-               if (map != NULL) {
-                       /* found a matching NAT entry, change the source IPv4 */
-                       pisa_ipv4_copy(srcaddr, &map->local_private);
-                       pisa_nat_fix_headers(hdr);
-               }
+               if (map != NULL)
+                       pisa_nat_apply(hdr, srcaddr, &map->local_private);
 
                /* Send the packet out to the tunnel device */
                if (write(cd_ctx.tunnel, buffer + 6, len - 6) == -1)
@@ -474,8 +471,7 @@
        map = pisa_nat_mapping_find_by_local_private(cd_ctx.natlist, dst);
        if (map != NULL) {
                /* found a matching NAT entry, change the destination IPv4 */
-               pisa_ipv4_copy(dst, &map->remote.ipv4);
-               pisa_nat_fix_headers(hdr);
+               pisa_nat_apply(hdr, dst, &map->remote.ipv4);
                entry = map->connection;
        } else {
                /* No NAT mapping found, just find out which server is the

Modified: trunk/pisasd/sdtun.c
==============================================================================
--- trunk/pisasd/sdtun.c        Wed Sep 30 18:17:31 2009        (r1010)
+++ trunk/pisasd/sdtun.c        Fri Oct  2 14:30:01 2009        (r1011)
@@ -335,12 +335,9 @@
                        map = pisa_nat_mapping_find_by_remote(sd_ctx.natlist, 
&from.sin6_addr, srcaddr);
                }
 
-               /* Apply NAT if needed */
-               if (map != NULL) {
-                       /* found a matching NAT entry, change the source IPv4 */
-                       pisa_ipv4_copy(srcaddr, &map->local_private);
-                       pisa_nat_fix_headers(hdr);
-               }
+               /* Change the source IPv4 if a NAT mapping should be applied */
+               if (map != NULL)
+                       pisa_nat_apply(hdr, srcaddr, &map->local_private);
 
                /* Send the packet out to the tunnel device */
                if (write(sd_ctx.tunnel, buffer + 6, len - 6) == -1)
@@ -389,8 +386,7 @@
        map = pisa_nat_mapping_find_by_local_private(sd_ctx.natlist, dst);
        if (map != NULL) {
                /* found a matching NAT entry, change the destination IPv4 */
-               pisa_ipv4_copy(dst, &map->remote.ipv4);
-               pisa_nat_fix_headers(hdr);
+               pisa_nat_apply(hdr, dst, &map->remote.ipv4);
                entry = map->connection;
                /* @todo: first 6 bytes should be MAC of the receiver */
        } else {

Other related posts:

  • » [pisa-src] r1011 - in trunk: include libpisa pisacd pisasd - Thomas Jansen