From a0525a4ed36a28260d6dee4723bd6a1eb29020be Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 3 Jan 2007 20:48:52 +0000 Subject: Verify checksums on the RX datapath. Simplify checksum generation on the TX datapath. --- src/include/gpxe/ip.h | 2 +- src/include/gpxe/tcpip.h | 40 ++++----- src/net/icmpv6.c | 3 +- src/net/ipv4.c | 209 +++++++++++++++++++++++++---------------------- src/net/ipv6.c | 16 ++-- src/net/tcp.c | 25 ++++-- src/net/tcpip.c | 22 +++-- src/net/udp.c | 18 ++-- 8 files changed, 176 insertions(+), 159 deletions(-) diff --git a/src/include/gpxe/ip.h b/src/include/gpxe/ip.h index bec7c90..352cf64 100644 --- a/src/include/gpxe/ip.h +++ b/src/include/gpxe/ip.h @@ -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 diff --git a/src/include/gpxe/tcpip.h b/src/include/gpxe/tcpip.h index 6ab2f19..23c6ce7 100644 --- a/src/include/gpxe/tcpip.h +++ b/src/include/gpxe/tcpip.h @@ -14,6 +14,13 @@ 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 */ diff --git a/src/net/icmpv6.c b/src/net/icmpv6.c index d1f58bd..3f2b06f 100644 --- a/src/net/icmpv6.c +++ b/src/net/icmpv6.c @@ -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, }; diff --git a/src/net/ipv4.c b/src/net/ipv4.c index bf49587..a37c27a 100644 --- a/src/net/ipv4.c +++ b/src/net/ipv4.c @@ -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 ); diff --git a/src/net/ipv6.c b/src/net/ipv6.c index d00562a..8422da6 100644 --- a/src/net/ipv6.c +++ b/src/net/ipv6.c @@ -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 */ ); } /** diff --git a/src/net/tcp.c b/src/net/tcp.c index cd625c3..d9d427c 100644 --- a/src/net/tcp.c +++ b/src/net/tcp.c @@ -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, }; diff --git a/src/net/tcpip.c b/src/net/tcpip.c index 33c9872..ae22104 100644 --- a/src/net/tcpip.c +++ b/src/net/tcpip.c @@ -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 ); } diff --git a/src/net/udp.c b/src/net/udp.c index 21bfebc..e55cfa2 100644 --- a/src/net/udp.c +++ b/src/net/udp.c @@ -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, }; -- cgit v1.1