Bladeren bron

Correct TCP/IP checksum generation.

tags/v0.9.3
Michael Brown 18 jaren geleden
bovenliggende
commit
2c0eb6eb1d
4 gewijzigde bestanden met toevoegingen van 60 en 28 verwijderingen
  1. 3
    1
      src/include/gpxe/tcpip_if.h
  2. 4
    6
      src/net/ipv4.c
  3. 50
    15
      src/net/tcpip_if.c
  4. 3
    6
      src/net/udp.c

+ 3
- 1
src/include/gpxe/tcpip_if.h Bestand weergeven

@@ -83,7 +83,9 @@ extern void trans_rx ( struct pk_buff *pkb, uint8_t trans_proto,
83 83
 extern int trans_tx ( struct pk_buff *pkb, struct tcpip_protocol *tcpip, 
84 84
 		      struct sockaddr *dest );
85 85
 
86
-extern uint16_t calc_chksum ( void *b, int len );
86
+extern unsigned int tcpip_continue_chksum ( unsigned int partial,
87
+					    const void *data, size_t len );
88
+extern unsigned int tcpip_chksum ( const void *data, size_t len );
87 89
 
88 90
 extern struct tcpip_protocol * find_tcpip_protocol ( uint8_t trans_proto );
89 91
 extern struct tcpip_net_protocol * find_tcpip_net_protocol ( sa_family_t sa_family );

+ 4
- 6
src/net/ipv4.c Bestand weergeven

@@ -238,9 +238,8 @@ void ipv4_tx_csum ( struct pk_buff *pkb, struct tcpip_protocol *tcpip ) {
238 238
 
239 239
 	struct iphdr *iphdr = pkb->data;
240 240
 	struct ipv4_pseudo_header pshdr;
241
-	void *csum_offset = iphdr + sizeof ( *iphdr ) + tcpip->csum_offset;
242
-	uint16_t partial_csum = *( ( uint16_t* ) csum_offset );
243
-	uint16_t csum;
241
+	uint16_t *csum = ( ( ( void * ) iphdr ) + sizeof ( *iphdr )
242
+			   + tcpip->csum_offset );
244 243
 
245 244
 	/* Calculate pseudo header */
246 245
 	pshdr.src = iphdr->src;
@@ -250,8 +249,7 @@ void ipv4_tx_csum ( struct pk_buff *pkb, struct tcpip_protocol *tcpip ) {
250 249
 	pshdr.len = htons ( pkb_len ( pkb ) - sizeof ( *iphdr ) );
251 250
 
252 251
 	/* Update the checksum value */
253
-	csum = partial_csum + calc_chksum ( &pshdr, sizeof ( pshdr ) );
254
-	memcpy ( csum_offset, &csum, 2 );
252
+	*csum = tcpip_continue_chksum ( *csum, &pshdr, sizeof ( pshdr ) );
255 253
 }
256 254
 
257 255
 /**
@@ -407,7 +405,7 @@ int ipv4_tx ( struct pk_buff *pkb, struct tcpip_protocol *tcpip,
407 405
 
408 406
 	/* Calculate header checksum, in network byte order */
409 407
 	iphdr->chksum = 0;
410
-	iphdr->chksum = htons ( calc_chksum ( iphdr, sizeof ( *iphdr ) ) );
408
+	iphdr->chksum = tcpip_chksum ( iphdr, sizeof ( *iphdr ) );
411 409
 
412 410
 	/* Print IP4 header for debugging */
413 411
 	ipv4_dump ( iphdr );

+ 50
- 15
src/net/tcpip_if.c Bestand weergeven

@@ -105,21 +105,56 @@ int trans_tx ( struct pk_buff *pkb, struct tcpip_protocol *tcpip,
105 105
 }
106 106
 
107 107
 /**
108
- * Calculate internet checksum
108
+ * Calculate continued TCP/IP checkum
109 109
  *
110
- * @v b		Pointer to the data
111
- * @v len	Length of data to be checksummed
112
- * @ret result	16 bit internet checksum
110
+ * @v partial		Checksum of already-summed data, in network byte order
111
+ * @v data		Data buffer
112
+ * @v len		Length of data buffer
113
+ * @ret cksum		Updated checksum, in network byte order
114
+ *
115
+ * Calculates a TCP/IP-style 16-bit checksum over the data block.  The
116
+ * checksum is returned in network byte order.
117
+ *
118
+ * This function may be used to add new data to an existing checksum.
119
+ * The function assumes that both the old data and the new data start
120
+ * on even byte offsets; if this is not the case then you will need to
121
+ * byte-swap either the input partial checksum, the output checksum,
122
+ * or both.  Deciding which to swap is left as an exercise for the
123
+ * interested reader.
124
+ */
125
+unsigned int tcpip_continue_chksum ( unsigned int partial, const void *data,
126
+				     size_t len ) {
127
+	unsigned int cksum = ( ( ~partial ) & 0xffff );
128
+	unsigned int value;
129
+	unsigned int i;
130
+	
131
+	for ( i = 0 ; i < len ; i++ ) {
132
+		value = * ( ( uint8_t * ) data + i );
133
+		if ( i & 1 ) {
134
+			/* Odd bytes: swap on little-endian systems */
135
+			value = be16_to_cpu ( value );
136
+		} else {
137
+			/* Even bytes: swap on big-endian systems */
138
+			value = le16_to_cpu ( value );
139
+		}
140
+		cksum += value;
141
+		if ( cksum > 0xffff )
142
+			cksum -= 0xffff;
143
+	}
144
+	
145
+	return ( ( ~cksum ) & 0xffff );
146
+}
147
+
148
+/**
149
+ * Calculate TCP/IP checkum
150
+ *
151
+ * @v data		Data buffer
152
+ * @v len		Length of data buffer
153
+ * @ret cksum		Checksum, in network byte order
154
+ *
155
+ * Calculates a TCP/IP-style 16-bit checksum over the data block.  The
156
+ * checksum is returned in network byte order.
113 157
  */
114
-uint16_t calc_chksum(void *b, int len) {
115
-	uint16_t *buf = b, result;
116
-	uint16_t sum=0;
117
-	for ( sum = 0; len > 1; len -= 2 ) /* Sum all 16b words */
118
-		sum += *buf++;
119
-	if ( len == 1 )                  /* If any stray bytes, */
120
-		sum += *(unsigned char*)buf;          /* add to sum */
121
-	sum = (sum >> 16) + (sum & 0xffff);    /* Add the carry */
122
-	sum += (sum >> 16);                          /* (again) */
123
-	result = ~sum;             /* Take the one's complement */
124
-	return result;                      /* Return 16b value */
158
+unsigned int tcpip_chksum ( const void *data, size_t len ) {
159
+	return tcpip_continue_chksum ( 0xffff, data, len );
125 160
 }

+ 3
- 6
src/net/udp.c Bestand weergeven

@@ -142,11 +142,8 @@ int udp_sendto ( struct udp_connection *conn, struct sockaddr *peer,
142 142
 	udphdr->dest_port = *dest;
143 143
 	udphdr->source_port = conn->local_port;
144 144
 	udphdr->len = htons ( pkb_len ( conn->tx_pkb ) );
145
-	/**
146
-	 * Calculate the partial checksum. Note this is stored in host byte
147
-	 * order.
148
-	 */
149
-	udphdr->chksum = calc_chksum ( udphdr, sizeof ( *udphdr ) + len );
145
+	udphdr->chksum = 0;
146
+	udphdr->chksum = tcpip_chksum ( udphdr, sizeof ( *udphdr ) + len );
150 147
 
151 148
 	/**
152 149
 	 * Dump the contents of the UDP header
@@ -238,7 +235,7 @@ void udp_rx ( struct pk_buff *pkb, struct in_addr *src_net_addr __unused,
238 235
 	}
239 236
 
240 237
 	/* Verify the checksum */
241
-	chksum = calc_chksum ( pkb->data, pkb_len ( pkb ) );
238
+	chksum = tcpip_chksum ( pkb->data, pkb_len ( pkb ) );
242 239
 	if ( chksum != 0xffff ) {
243 240
 		DBG ( "Bad checksum %d\n", chksum );
244 241
 		return;

Laden…
Annuleren
Opslaan