Verify checksums on the RX datapath.
authorMichael Brown <mcb30@etherboot.org>
Wed, 3 Jan 2007 20:48:52 +0000 (20:48 +0000)
committerMichael Brown <mcb30@etherboot.org>
Wed, 3 Jan 2007 20:48:52 +0000 (20:48 +0000)
Simplify checksum generation on the TX datapath.

src/include/gpxe/ip.h
src/include/gpxe/tcpip.h
src/net/icmpv6.c
src/net/ipv4.c
src/net/ipv6.c
src/net/tcp.c
src/net/tcpip.c
src/net/udp.c

index bec7c90..352cf64 100644 (file)
@@ -12,7 +12,7 @@
 
 /* IP constants */
 
-#define IP_VER         4
+#define IP_VER         0x40
 #define IP_MASK_VER    0xf0
 #define IP_MASK_HLEN   0x0f
 #define IP_MASK_OFFSET 0x1fff
index 6ab2f19..23c6ce7 100644 (file)
 
 struct pk_buff;
 
+/** Empty checksum value
+ *
+ * This is the TCP/IP checksum over a zero-length block of data.
+ */
+#define TCPIP_EMPTY_CSUM 0xffff
+
+/** Length of a @c struct @c sockaddr_tcpip */
 #define SA_TCPIP_LEN 32
 
 /**
@@ -45,30 +52,22 @@ struct tcpip_protocol {
                /**
          * Process received packet
          *
-         * @v pkb      Packet buffer
-        * @v st_src    Partially-filled source address
-        * @v st_dest   Partially-filled destination address
-        * @ret rc      Return status code
+         * @v pkb              Packet buffer
+        * @v st_src            Partially-filled source address
+        * @v st_dest           Partially-filled destination address
+        * @v pshdr_csum        Pseudo-header checksum
+        * @ret rc              Return status code
          *
          * This method takes ownership of the packet buffer.
          */
         int ( * rx ) ( struct pk_buff *pkb, struct sockaddr_tcpip *st_src,
-                      struct sockaddr_tcpip *st_dest );
+                      struct sockaddr_tcpip *st_dest, uint16_t pshdr_csum );
         /** 
         * Transport-layer protocol number
         *
         * This is a constant of the type IP_XXX
          */
         uint8_t tcpip_proto;
-       /**
-        * Checksum offset
-        *
-        * A negative number indicates that the protocol does not
-        * require checksumming to be performed by the network layer.
-        * A positive number is the offset of the checksum field in
-        * the transport-layer header.
-        */
-       int csum_offset;
 };
 
 /**
@@ -85,13 +84,14 @@ struct tcpip_net_protocol {
         * @v pkb               Packet buffer
         * @v tcpip_protocol    Transport-layer protocol
         * @v st_dest           Destination address
+        * @v trans_csum        Transport-layer checksum to complete, or NULL
         * @ret rc              Return status code
         *
         * This function takes ownership of the packet buffer.
         */
        int ( * tx ) ( struct pk_buff *pkb,
                       struct tcpip_protocol *tcpip_protocol,
-                      struct sockaddr_tcpip *st_dest );
+                      struct sockaddr_tcpip *st_dest, uint16_t *trans_csum );
 };
 
 /** Declare a TCP/IP transport-layer protocol */
@@ -102,11 +102,11 @@ struct tcpip_net_protocol {
 
 extern int tcpip_rx ( struct pk_buff *pkb, uint8_t tcpip_proto,
                      struct sockaddr_tcpip *st_src,
-                     struct sockaddr_tcpip *st_dest );
+                     struct sockaddr_tcpip *st_dest, uint16_t pshdr_csum );
 extern int tcpip_tx ( struct pk_buff *pkb, struct tcpip_protocol *tcpip, 
-                     struct sockaddr_tcpip *st_dest );
-extern unsigned int tcpip_continue_chksum ( unsigned int partial,
-                                           const void *data, size_t len );
-extern unsigned int tcpip_chksum ( const void *data, size_t len );
+                     struct sockaddr_tcpip *st_dest, uint16_t *trans_csum );
+extern uint16_t tcpip_continue_chksum ( uint16_t partial,
+                                       const void *data, size_t len );
+extern uint16_t tcpip_chksum ( const void *data, size_t len );
 
 #endif /* _GPXE_TCPIP_H */
index d1f58bd..3f2b06f 100644 (file)
@@ -60,7 +60,7 @@ int icmp6_send_solicit ( struct net_device *netdev, struct in6_addr *src __unuse
        st_dest.sin6.sin6_addr.in6_u.u6_addr8[13] = 0xff;
        
        /* Send packet over IP6 */
-       return ( tcpip_tx ( pkb, &icmp6_protocol, &st_dest.st ) );
+       return tcpip_tx ( pkb, &icmp6_protocol, &st_dest.st, &nsolicit->csum );
 }
 
 /**
@@ -124,5 +124,4 @@ struct tcpip_protocol icmp6_protocol __tcpip_protocol = {
        .name = "ICMP6",
        .rx = icmp6_rx,
        .tcpip_proto = IP_ICMP6, // 58
-       .csum_offset = 2,
 };
index bf49587..a37c27a 100644 (file)
@@ -230,44 +230,65 @@ static struct pk_buff * ipv4_reassemble ( struct pk_buff * pkb ) {
        return NULL;
 }
 
-
 /**
- * Complete the transport-layer checksum
- *
- * @v pkb      Packet buffer
- * @v tcpip    Transport-layer protocol
+ * Add IPv4 pseudo-header checksum to existing checksum
  *
- * This function calculates the tcpip 
+ * @v pkb              Packet buffer
+ * @v csum             Existing checksum
+ * @ret csum           Updated checksum
  */
-static void ipv4_tx_csum ( struct pk_buff *pkb,
-                          struct tcpip_protocol *tcpip ) {
-       struct iphdr *iphdr = pkb->data;
+static uint16_t ipv4_pshdr_chksum ( struct pk_buff *pkb, uint16_t csum ) {
        struct ipv4_pseudo_header pshdr;
-       uint16_t *csum = ( ( ( void * ) iphdr ) + sizeof ( *iphdr )
-                          + tcpip->csum_offset );
+       struct iphdr *iphdr = pkb->data;
+       size_t hdrlen = ( ( iphdr->verhdrlen & IP_MASK_HLEN ) * 4 );
 
-       /* Calculate pseudo header */
+       /* Build pseudo-header */
        pshdr.src = iphdr->src;
        pshdr.dest = iphdr->dest;
        pshdr.zero_padding = 0x00;
        pshdr.protocol = iphdr->protocol;
-       /* This is only valid when IPv4 does not have options */
-       pshdr.len = htons ( pkb_len ( pkb ) - sizeof ( *iphdr ) );
+       pshdr.len = htons ( pkb_len ( pkb ) - hdrlen );
 
        /* Update the checksum value */
-       *csum = tcpip_continue_chksum ( *csum, &pshdr, sizeof ( pshdr ) );
+       return tcpip_continue_chksum ( csum, &pshdr, sizeof ( pshdr ) );
 }
 
 /**
- * Calculate the transport-layer checksum while processing packets
+ * Determine link-layer address
+ *
+ * @v dest             IPv4 destination address
+ * @v src              IPv4 source address
+ * @v netdev           Network device
+ * @v ll_dest          Link-layer destination address buffer
+ * @ret rc             Return status code
  */
-static uint16_t ipv4_rx_csum ( struct pk_buff *pkb __unused,
-                              uint8_t trans_proto __unused ) {
-       /** 
-        * This function needs to be implemented. Until then, it will return
-        * 0xffffffff every time
-        */
-       return 0xffff;
+static int ipv4_ll_addr ( struct in_addr dest, struct in_addr src,
+                         struct net_device *netdev, uint8_t *ll_dest ) {
+       struct ll_protocol *ll_protocol = netdev->ll_protocol;
+       uint8_t *dest_bytes = ( ( uint8_t * ) &dest );
+
+       if ( dest.s_addr == INADDR_BROADCAST ) {
+               /* Broadcast address */
+               memcpy ( ll_dest, ll_protocol->ll_broadcast,
+                        ll_protocol->ll_addr_len );
+               return 0;
+       } else if ( IN_MULTICAST ( dest.s_addr ) ) {
+               /* Special case: IPv4 multicast over Ethernet.  This
+                * code may need to be generalised once we find out
+                * what happens for other link layers.
+                */
+               ll_dest[0] = 0x01;
+               ll_dest[1] = 0x00;
+               ll_dest[2] = 0x5e;
+               ll_dest[3] = dest_bytes[1] & 0x7f;
+               ll_dest[4] = dest_bytes[2];
+               ll_dest[5] = dest_bytes[3];
+               return 0;
+       } else {
+               /* Unicast address: resolve via ARP */
+               return arp_resolve ( netdev, &ipv4_protocol, &dest,
+                                    &src, ll_dest );
+       }
 }
 
 /**
@@ -276,24 +297,23 @@ static uint16_t ipv4_rx_csum ( struct pk_buff *pkb __unused,
  * @v pkb              Packet buffer
  * @v tcpip            Transport-layer protocol
  * @v st_dest          Destination network-layer address
+ * @v trans_csum       Transport-layer checksum to complete, or NULL
  * @ret rc             Status
  *
  * This function expects a transport-layer segment and prepends the IP header
  */
 static int ipv4_tx ( struct pk_buff *pkb,
                     struct tcpip_protocol *tcpip_protocol,
-                    struct sockaddr_tcpip *st_dest ) {
+                    struct sockaddr_tcpip *st_dest, uint16_t *trans_csum ) {
        struct iphdr *iphdr = pkb_push ( pkb, sizeof ( *iphdr ) );
        struct sockaddr_in *sin_dest = ( ( struct sockaddr_in * ) st_dest );
        struct ipv4_miniroute *miniroute;
-       struct net_device *netdev = NULL;
        struct in_addr next_hop;
-       uint8_t ll_dest_buf[MAX_LL_ADDR_LEN];
-       const uint8_t *ll_dest = ll_dest_buf;
+       uint8_t ll_dest[MAX_LL_ADDR_LEN];
        int rc;
 
        /* Fill up the IP header, except source address */
-       iphdr->verhdrlen = ( ( IP_VER << 4 ) | ( sizeof ( *iphdr ) / 4 ) );
+       iphdr->verhdrlen = ( IP_VER | ( sizeof ( *iphdr ) / 4 ) );
        iphdr->service = IP_TOS;
        iphdr->len = htons ( pkb_len ( pkb ) ); 
        iphdr->ident = htons ( ++next_ident );
@@ -307,18 +327,23 @@ static int ipv4_tx ( struct pk_buff *pkb,
        next_hop = iphdr->dest;
        miniroute = ipv4_route ( &next_hop );
        if ( ! miniroute ) {
-               DBG ( "No route to %s\n", inet_ntoa ( iphdr->dest ) );
+               DBG ( "IPv4 has no route to %s\n", inet_ntoa ( iphdr->dest ) );
                rc = -EHOSTUNREACH;
                goto err;
        }
        iphdr->src = miniroute->address;
-       netdev = miniroute->netdev;
 
-       /* Calculate the transport layer checksum */
-       if ( tcpip_protocol->csum_offset > 0 )
-               ipv4_tx_csum ( pkb, tcpip_protocol );
+       /* Determine link-layer destination address */
+       if ( ( rc = ipv4_ll_addr ( next_hop, iphdr->src, miniroute->netdev,
+                                  ll_dest ) ) != 0 ) {
+               DBG ( "IPv4 has no link-layer address for %s\n",
+                     inet_ntoa ( iphdr->dest ) );
+               goto err;
+       }
 
-       /* Calculate header checksum, in network byte order */
+       /* Fix up checksums */
+       if ( trans_csum )
+               *trans_csum = ipv4_pshdr_chksum ( pkb, *trans_csum );
        iphdr->chksum = tcpip_chksum ( iphdr, sizeof ( *iphdr ) );
 
        /* Print IP4 header for debugging */
@@ -327,34 +352,8 @@ static int ipv4_tx ( struct pk_buff *pkb,
              inet_ntoa ( iphdr->dest ), ntohs ( iphdr->len ), iphdr->protocol,
              ntohs ( iphdr->ident ), ntohs ( iphdr->chksum ) );
 
-       /* Determine link-layer destination address */
-       if ( next_hop.s_addr == INADDR_BROADCAST ) {
-               /* Broadcast address */
-               ll_dest = netdev->ll_protocol->ll_broadcast;
-       } else if ( IN_MULTICAST ( next_hop.s_addr ) ) {
-               /* Special case: IPv4 multicast over Ethernet.  This
-                * code may need to be generalised once we find out
-                * what happens for other link layers.
-                */
-               uint8_t *next_hop_bytes = ( uint8_t * ) &next_hop;
-               ll_dest_buf[0] = 0x01;
-               ll_dest_buf[0] = 0x00;
-               ll_dest_buf[0] = 0x5e;
-               ll_dest_buf[3] = next_hop_bytes[1] & 0x7f;
-               ll_dest_buf[4] = next_hop_bytes[2];
-               ll_dest_buf[5] = next_hop_bytes[3];
-       } else {
-               /* Unicast address: resolve via ARP */
-               if ( ( rc = arp_resolve ( netdev, &ipv4_protocol, &next_hop,
-                                         &iphdr->src, ll_dest_buf ) ) != 0 ) {
-                       DBG ( "No ARP entry for %s\n",
-                             inet_ntoa ( iphdr->dest ) );
-                       goto err;
-               }
-       }
-
        /* Hand off to link layer */
-       return net_tx ( pkb, netdev, &ipv4_protocol, ll_dest );
+       return net_tx ( pkb, miniroute->netdev, &ipv4_protocol, ll_dest );
 
  err:
        free_pkb ( pkb );
@@ -374,73 +373,85 @@ static int ipv4_tx ( struct pk_buff *pkb,
 static int ipv4_rx ( struct pk_buff *pkb, struct net_device *netdev __unused,
                     const void *ll_source __unused ) {
        struct iphdr *iphdr = pkb->data;
+       size_t hdrlen;
+       size_t len;
        union {
                struct sockaddr_in sin;
                struct sockaddr_tcpip st;
        } src, dest;
-       uint16_t chksum;
+       uint16_t csum;
+       uint16_t pshdr_csum;
 
-       /* Sanity check */
+       /* Sanity check the IPv4 header */
        if ( pkb_len ( pkb ) < sizeof ( *iphdr ) ) {
-               DBG ( "IP datagram too short (%d bytes)\n", pkb_len ( pkb ) );
+               DBG ( "IPv4 packet too short at %d bytes (min %d bytes)\n",
+                     pkb_len ( pkb ), sizeof ( *iphdr ) );
+               goto err;
+       }
+       if ( ( iphdr->verhdrlen & IP_MASK_VER ) != IP_VER ) {
+               DBG ( "IPv4 version %#02x not supported\n", iphdr->verhdrlen );
+               goto err;
+       }
+       hdrlen = ( ( iphdr->verhdrlen & IP_MASK_HLEN ) * 4 );
+       if ( hdrlen < sizeof ( *iphdr ) ) {
+               DBG ( "IPv4 header too short at %d bytes (min %d bytes)\n",
+                     hdrlen, sizeof ( *iphdr ) );
+               goto err;
+       }
+       if ( hdrlen > pkb_len ( pkb ) ) {
+               DBG ( "IPv4 header too long at %d bytes "
+                     "(packet is %d bytes)\n", hdrlen, pkb_len ( pkb ) );
+               goto err;
+       }
+       if ( ( csum = tcpip_chksum ( iphdr, hdrlen ) ) != 0 ) {
+               DBG ( "IPv4 checksum incorrect (is %04x including checksum "
+                     "field, should be 0000)\n", csum );
+               goto err;
+       }
+       len = ntohs ( iphdr->len );
+       if ( len < hdrlen ) {
+               DBG ( "IPv4 length too short at %d bytes "
+                     "(header is %d bytes)\n", len, hdrlen );
+               goto err;
+       }
+       if ( len > pkb_len ( pkb ) ) {
+               DBG ( "IPv4 length too long at %d bytes "
+                     "(packet is %d bytes)\n", len, pkb_len ( pkb ) );
                goto err;
        }
 
-       /* Print IP4 header for debugging */
+       /* Print IPv4 header for debugging */
        DBG ( "IPv4 RX %s<-", inet_ntoa ( iphdr->dest ) );
        DBG ( "%s len %d proto %d id %04x csum %04x\n",
              inet_ntoa ( iphdr->src ), ntohs ( iphdr->len ), iphdr->protocol,
              ntohs ( iphdr->ident ), ntohs ( iphdr->chksum ) );
 
-       /* Validate version and header length */
-       if ( iphdr->verhdrlen != 0x45 ) {
-               DBG ( "Bad version and header length %x\n", iphdr->verhdrlen );
-               goto err;
-       }
-
-       /* Validate length of IP packet */
-       if ( ntohs ( iphdr->len ) > pkb_len ( pkb ) ) {
-               DBG ( "Inconsistent packet length %d\n",
-                     ntohs ( iphdr->len ) );
-               goto err;
-       }
+       /* Truncate packet to correct length, calculate pseudo-header
+        * checksum and then strip off the IPv4 header.
+        */
+       pkb_unput ( pkb, ( pkb_len ( pkb ) - len ) );
+       pshdr_csum = ipv4_pshdr_chksum ( pkb, TCPIP_EMPTY_CSUM );
+       pkb_pull ( pkb, hdrlen );
 
-       /* Verify the checksum */
-       if ( ( chksum = ipv4_rx_csum ( pkb, iphdr->protocol ) ) != 0xffff ) {
-               DBG ( "Bad checksum %x\n", chksum );
-       }
        /* Fragment reassembly */
        if ( ( iphdr->frags & htons ( IP_MASK_MOREFRAGS ) ) || 
             ( ( iphdr->frags & htons ( IP_MASK_OFFSET ) ) != 0 ) ) {
-               /* Pass the fragment to the reassembler ipv4_ressable() which
-                * either returns a fully reassembled packet buffer or NULL.
+               /* Pass the fragment to ipv4_reassemble() which either
+                * returns a fully reassembled packet buffer or NULL.
                 */
                pkb = ipv4_reassemble ( pkb );
-               if ( !pkb ) {
+               if ( ! pkb )
                        return 0;
-               }
        }
 
-       /* To reduce code size, the following functions are not implemented:
-        * 1. Check the destination address
-        * 2. Check the TTL field
-        * 3. Check the service field
-        */
-
-       /* Construct socket addresses */
+       /* Construct socket addresses and hand off to transport layer */
        memset ( &src, 0, sizeof ( src ) );
        src.sin.sin_family = AF_INET;
        src.sin.sin_addr = iphdr->src;
        memset ( &dest, 0, sizeof ( dest ) );
        dest.sin.sin_family = AF_INET;
        dest.sin.sin_addr = iphdr->dest;
-
-       /* Strip header */
-       pkb_unput ( pkb, pkb_len ( pkb ) - ntohs ( iphdr->len ) );
-       pkb_pull ( pkb, sizeof ( *iphdr ) );
-
-       /* Send it to the transport layer */
-       return tcpip_rx ( pkb, iphdr->protocol, &src.st, &dest.st );
+       return tcpip_rx ( pkb, iphdr->protocol, &src.st, &dest.st, pshdr_csum);
 
  err:
        free_pkb ( pkb );
index d00562a..8422da6 100644 (file)
@@ -103,11 +103,9 @@ void del_ipv6_address ( struct net_device *netdev ) {
  * This function constructs the pseudo header and completes the checksum in the
  * upper layer header.
  */
-static void ipv6_tx_csum ( struct pk_buff *pkb, struct tcpip_protocol *tcpip ) {
+static uint16_t ipv6_tx_csum ( struct pk_buff *pkb, uint16_t csum ) {
        struct ip6_header *ip6hdr = pkb->data;
        struct ipv6_pseudo_header pshdr;
-       uint16_t *csum = ( ( ( void * ) ip6hdr ) + sizeof ( *ip6hdr ) +
-                       tcpip->csum_offset );
 
        /* Calculate pseudo header */
        memset ( &pshdr, 0, sizeof ( pshdr ) );
@@ -117,7 +115,7 @@ static void ipv6_tx_csum ( struct pk_buff *pkb, struct tcpip_protocol *tcpip ) {
        pshdr.nxt_hdr = ip6hdr->nxt_hdr;
 
        /* Update checksum value */
-       *csum = tcpip_continue_chksum ( *csum, &pshdr, sizeof ( pshdr ) );
+       return tcpip_continue_chksum ( csum, &pshdr, sizeof ( pshdr ) );
 }
 
 /**
@@ -142,7 +140,8 @@ void ipv6_dump ( struct ip6_header *ip6hdr ) {
  */
 static int ipv6_tx ( struct pk_buff *pkb,
                     struct tcpip_protocol *tcpip,
-                    struct sockaddr_tcpip *st_dest ) {
+                    struct sockaddr_tcpip *st_dest,
+                    uint16_t *trans_csum ) {
        struct sockaddr_in6 *dest = ( struct sockaddr_in6* ) st_dest;
        struct in6_addr next_hop;
        struct ipv6_miniroute *miniroute;
@@ -184,9 +183,8 @@ static int ipv6_tx ( struct pk_buff *pkb,
        }
 
        /* Complete the transport layer checksum */
-       if ( tcpip->csum_offset > 0 ) {
-               ipv6_tx_csum ( pkb, tcpip );
-       }
+       if ( trans_csum )
+               *trans_csum = ipv6_tx_csum ( pkb, *trans_csum );
 
        /* Print IPv6 header */
        ipv6_dump ( ip6hdr );
@@ -244,7 +242,7 @@ static int ipv6_process_nxt_hdr ( struct pk_buff *pkb, uint8_t nxt_hdr,
                return 0;
        }
        /* Next header is not a IPv6 extension header */
-       return tcpip_rx ( pkb, nxt_hdr, src, dest );
+       return tcpip_rx ( pkb, nxt_hdr, src, dest, 0 /* fixme */ );
 }
 
 /**
index cd625c3..d9d427c 100644 (file)
@@ -309,7 +309,7 @@ static int tcp_senddata_conn ( struct tcp_connection *conn, int force_send ) {
        DBGC ( conn, "\n" );
 
        /* Transmit packet */
-       return tcpip_tx ( pkb, &tcp_protocol, &conn->peer );
+       return tcpip_tx ( pkb, &tcp_protocol, &conn->peer, &tcphdr->csum );
 }
 
 /**
@@ -591,14 +591,19 @@ static int tcp_rx_fin ( struct tcp_connection *conn, uint32_t seq ) {
  * Process received packet
  *
  * @v pkb              Packet buffer
- * @v partial          Partial checksum
- */
+ * @v st_src           Partially-filled source address
+ * @v st_dest          Partially-filled destination address
+ * @v pshdr_csum       Pseudo-header checksum
+ * @ret rc             Return status code
+  */
 static int tcp_rx ( struct pk_buff *pkb,
                    struct sockaddr_tcpip *st_src __unused,
-                   struct sockaddr_tcpip *st_dest __unused ) {
+                   struct sockaddr_tcpip *st_dest __unused,
+                   uint16_t pshdr_csum ) {
        struct tcp_header *tcphdr;
        struct tcp_connection *conn;
        unsigned int hlen;
+       uint16_t csum;
        uint32_t start_seq;
        uint32_t seq;
        uint32_t ack;
@@ -608,7 +613,7 @@ static int tcp_rx ( struct pk_buff *pkb,
        size_t len;
        int rc = 0;
 
-       /* Sanity check packet and strip TCP header */
+       /* Sanity check packet */
        if ( pkb_len ( pkb ) < sizeof ( *tcphdr ) ) {
                DBG ( "TCP packet too short at %d bytes (min %d bytes)\n",
                      pkb_len ( pkb ), sizeof ( *tcphdr ) );
@@ -629,9 +634,12 @@ static int tcp_rx ( struct pk_buff *pkb,
                rc = -EINVAL;
                goto err;
        }
-
-       /* TODO: Verify checksum */
-#warning "Verify checksum"
+       csum = tcpip_continue_chksum ( pshdr_csum, pkb->data, pkb_len ( pkb ));
+       if ( csum != 0 ) {
+               DBG ( "TCP checksum incorrect (is %04x including checksum "
+                     "field, should be 0000)\n", csum );
+               goto err;
+       }
        
        /* Parse parameters from header and strip header */
        conn = tcp_demux ( tcphdr->dest );
@@ -845,5 +853,4 @@ struct tcpip_protocol tcp_protocol __tcpip_protocol = {
        .name = "TCP",
        .rx = tcp_rx,
        .tcpip_proto = IP_TCP,
-       .csum_offset = 16,
 };
index 33c9872..ae22104 100644 (file)
@@ -32,6 +32,7 @@ tcpip_protocols_end[0] __table_end ( tcpip_protocols );
  * @v tcpip_proto      Transport-layer protocol number
  * @v st_src           Partially-filled source address
  * @v st_dest          Partially-filled destination address
+ * @v pshdr_csum       Pseudo-header checksum
  * @ret rc             Return status code
  *
  * This function expects a transport-layer segment from the network
@@ -42,14 +43,15 @@ tcpip_protocols_end[0] __table_end ( tcpip_protocols );
  */
 int tcpip_rx ( struct pk_buff *pkb, uint8_t tcpip_proto, 
               struct sockaddr_tcpip *st_src,
-              struct sockaddr_tcpip *st_dest ) {
+              struct sockaddr_tcpip *st_dest,
+              uint16_t pshdr_csum ) {
        struct tcpip_protocol *tcpip;
 
        /* Hand off packet to the appropriate transport-layer protocol */
        for ( tcpip = tcpip_protocols; tcpip < tcpip_protocols_end; tcpip++ ) {
                if ( tcpip->tcpip_proto == tcpip_proto ) {
                        DBG ( "TCP/IP received %s packet\n", tcpip->name );
-                       return tcpip->rx ( pkb, st_src, st_dest );
+                       return tcpip->rx ( pkb, st_src, st_dest, pshdr_csum );
                }
        }
 
@@ -63,10 +65,11 @@ int tcpip_rx ( struct pk_buff *pkb, uint8_t tcpip_proto,
  * @v pkb              Packet buffer
  * @v tcpip_protocol   Transport-layer protocol
  * @v st_dest          Destination address
+ * @v trans_csum       Transport-layer checksum to complete, or NULL
  * @ret rc             Return status code
  */
 int tcpip_tx ( struct pk_buff *pkb, struct tcpip_protocol *tcpip_protocol,
-              struct sockaddr_tcpip *st_dest ) {
+              struct sockaddr_tcpip *st_dest, uint16_t *trans_csum ) {
        struct tcpip_net_protocol *tcpip_net;
 
        /* Hand off packet to the appropriate network-layer protocol */
@@ -74,7 +77,8 @@ int tcpip_tx ( struct pk_buff *pkb, struct tcpip_protocol *tcpip_protocol,
              tcpip_net < tcpip_net_protocols_end ; tcpip_net++ ) {
                if ( tcpip_net->sa_family == st_dest->st_family ) {
                        DBG ( "TCP/IP sending %s packet\n", tcpip_net->name );
-                       return tcpip_net->tx ( pkb, tcpip_protocol, st_dest );
+                       return tcpip_net->tx ( pkb, tcpip_protocol, st_dest,
+                                              trans_csum );
                }
        }
        
@@ -101,8 +105,8 @@ int tcpip_tx ( struct pk_buff *pkb, struct tcpip_protocol *tcpip_protocol,
  * or both.  Deciding which to swap is left as an exercise for the
  * interested reader.
  */
-unsigned int tcpip_continue_chksum ( unsigned int partial, const void *data,
-                                    size_t len ) {
+uint16_t tcpip_continue_chksum ( uint16_t partial, const void *data,
+                                size_t len ) {
        unsigned int cksum = ( ( ~partial ) & 0xffff );
        unsigned int value;
        unsigned int i;
@@ -121,7 +125,7 @@ unsigned int tcpip_continue_chksum ( unsigned int partial, const void *data,
                        cksum -= 0xffff;
        }
        
-       return ( ( ~cksum ) & 0xffff );
+       return ( ~cksum );
 }
 
 /**
@@ -134,6 +138,6 @@ unsigned int tcpip_continue_chksum ( unsigned int partial, const void *data,
  * Calculates a TCP/IP-style 16-bit checksum over the data block.  The
  * checksum is returned in network byte order.
  */
-unsigned int tcpip_chksum ( const void *data, size_t len ) {
-       return tcpip_continue_chksum ( 0xffff, data, len );
+uint16_t tcpip_chksum ( const void *data, size_t len ) {
+       return tcpip_continue_chksum ( TCPIP_EMPTY_CSUM, data, len );
 }
index 21bfebc..e55cfa2 100644 (file)
@@ -163,7 +163,7 @@ int udp_sendto ( struct udp_connection *conn, struct sockaddr_tcpip *peer,
              ntohs ( udphdr->chksum ) );
 
        /* Send it to the next layer for processing */
-       return tcpip_tx ( pkb, &udp_protocol, peer );
+       return tcpip_tx ( pkb, &udp_protocol, peer, &udphdr->chksum );
 }
 
 /**
@@ -190,14 +190,15 @@ int udp_send ( struct udp_connection *conn, const void *data, size_t len ) {
  * @v pkb              Packet buffer
  * @v st_src           Partially-filled source address
  * @v st_dest          Partially-filled destination address
+ * @v pshdr_csum       Pseudo-header checksum
  * @ret rc             Return status code
  */
 static int udp_rx ( struct pk_buff *pkb, struct sockaddr_tcpip *st_src,
-                   struct sockaddr_tcpip *st_dest ) {
+                   struct sockaddr_tcpip *st_dest, uint16_t pshdr_csum ) {
        struct udp_header *udphdr = pkb->data;
        struct udp_connection *conn;
        unsigned int ulen;
-       uint16_t chksum;
+       uint16_t csum;
        int rc;
 
        /* Sanity check */
@@ -225,15 +226,13 @@ static int udp_rx ( struct pk_buff *pkb, struct sockaddr_tcpip *st_src,
        pkb_unput ( pkb, ( pkb_len ( pkb ) - ulen ) );
 
        /* Verify the checksum */
-#warning "Don't we need to take the pseudo-header into account here?"
-#if 0
-       chksum = tcpip_chksum ( pkb->data, pkb_len ( pkb ) );
-       if ( chksum != 0xffff ) {
-               DBG ( "Bad checksum %#x\n", chksum );
+       csum = tcpip_continue_chksum ( pshdr_csum, pkb->data, pkb_len ( pkb ));
+       if ( csum != 0 ) {
+               DBG ( "UDP checksum incorrect (is %04x including checksum "
+                     "field, should be 0000)\n", csum );
                rc = -EINVAL;
                goto done;
        }
-#endif
 
        /* Complete the socket addresses */
        st_src->st_port = udphdr->source_port;
@@ -271,5 +270,4 @@ struct tcpip_protocol udp_protocol __tcpip_protocol = {
        .name = "UDP",
        .rx = udp_rx,
        .tcpip_proto = IP_UDP,
-       .csum_offset = 6,
 };