|
@@ -230,44 +230,65 @@ static struct pk_buff * ipv4_reassemble ( struct pk_buff * pkb ) {
|
230
|
230
|
return NULL;
|
231
|
231
|
}
|
232
|
232
|
|
233
|
|
-
|
234
|
233
|
/**
|
235
|
|
- * Complete the transport-layer checksum
|
236
|
|
- *
|
237
|
|
- * @v pkb Packet buffer
|
238
|
|
- * @v tcpip Transport-layer protocol
|
|
234
|
+ * Add IPv4 pseudo-header checksum to existing checksum
|
239
|
235
|
*
|
240
|
|
- * This function calculates the tcpip
|
|
236
|
+ * @v pkb Packet buffer
|
|
237
|
+ * @v csum Existing checksum
|
|
238
|
+ * @ret csum Updated checksum
|
241
|
239
|
*/
|
242
|
|
-static void ipv4_tx_csum ( struct pk_buff *pkb,
|
243
|
|
- struct tcpip_protocol *tcpip ) {
|
244
|
|
- struct iphdr *iphdr = pkb->data;
|
|
240
|
+static uint16_t ipv4_pshdr_chksum ( struct pk_buff *pkb, uint16_t csum ) {
|
245
|
241
|
struct ipv4_pseudo_header pshdr;
|
246
|
|
- uint16_t *csum = ( ( ( void * ) iphdr ) + sizeof ( *iphdr )
|
247
|
|
- + tcpip->csum_offset );
|
|
242
|
+ struct iphdr *iphdr = pkb->data;
|
|
243
|
+ size_t hdrlen = ( ( iphdr->verhdrlen & IP_MASK_HLEN ) * 4 );
|
248
|
244
|
|
249
|
|
- /* Calculate pseudo header */
|
|
245
|
+ /* Build pseudo-header */
|
250
|
246
|
pshdr.src = iphdr->src;
|
251
|
247
|
pshdr.dest = iphdr->dest;
|
252
|
248
|
pshdr.zero_padding = 0x00;
|
253
|
249
|
pshdr.protocol = iphdr->protocol;
|
254
|
|
- /* This is only valid when IPv4 does not have options */
|
255
|
|
- pshdr.len = htons ( pkb_len ( pkb ) - sizeof ( *iphdr ) );
|
|
250
|
+ pshdr.len = htons ( pkb_len ( pkb ) - hdrlen );
|
256
|
251
|
|
257
|
252
|
/* Update the checksum value */
|
258
|
|
- *csum = tcpip_continue_chksum ( *csum, &pshdr, sizeof ( pshdr ) );
|
|
253
|
+ return tcpip_continue_chksum ( csum, &pshdr, sizeof ( pshdr ) );
|
259
|
254
|
}
|
260
|
255
|
|
261
|
256
|
/**
|
262
|
|
- * Calculate the transport-layer checksum while processing packets
|
|
257
|
+ * Determine link-layer address
|
|
258
|
+ *
|
|
259
|
+ * @v dest IPv4 destination address
|
|
260
|
+ * @v src IPv4 source address
|
|
261
|
+ * @v netdev Network device
|
|
262
|
+ * @v ll_dest Link-layer destination address buffer
|
|
263
|
+ * @ret rc Return status code
|
263
|
264
|
*/
|
264
|
|
-static uint16_t ipv4_rx_csum ( struct pk_buff *pkb __unused,
|
265
|
|
- uint8_t trans_proto __unused ) {
|
266
|
|
- /**
|
267
|
|
- * This function needs to be implemented. Until then, it will return
|
268
|
|
- * 0xffffffff every time
|
269
|
|
- */
|
270
|
|
- return 0xffff;
|
|
265
|
+static int ipv4_ll_addr ( struct in_addr dest, struct in_addr src,
|
|
266
|
+ struct net_device *netdev, uint8_t *ll_dest ) {
|
|
267
|
+ struct ll_protocol *ll_protocol = netdev->ll_protocol;
|
|
268
|
+ uint8_t *dest_bytes = ( ( uint8_t * ) &dest );
|
|
269
|
+
|
|
270
|
+ if ( dest.s_addr == INADDR_BROADCAST ) {
|
|
271
|
+ /* Broadcast address */
|
|
272
|
+ memcpy ( ll_dest, ll_protocol->ll_broadcast,
|
|
273
|
+ ll_protocol->ll_addr_len );
|
|
274
|
+ return 0;
|
|
275
|
+ } else if ( IN_MULTICAST ( dest.s_addr ) ) {
|
|
276
|
+ /* Special case: IPv4 multicast over Ethernet. This
|
|
277
|
+ * code may need to be generalised once we find out
|
|
278
|
+ * what happens for other link layers.
|
|
279
|
+ */
|
|
280
|
+ ll_dest[0] = 0x01;
|
|
281
|
+ ll_dest[1] = 0x00;
|
|
282
|
+ ll_dest[2] = 0x5e;
|
|
283
|
+ ll_dest[3] = dest_bytes[1] & 0x7f;
|
|
284
|
+ ll_dest[4] = dest_bytes[2];
|
|
285
|
+ ll_dest[5] = dest_bytes[3];
|
|
286
|
+ return 0;
|
|
287
|
+ } else {
|
|
288
|
+ /* Unicast address: resolve via ARP */
|
|
289
|
+ return arp_resolve ( netdev, &ipv4_protocol, &dest,
|
|
290
|
+ &src, ll_dest );
|
|
291
|
+ }
|
271
|
292
|
}
|
272
|
293
|
|
273
|
294
|
/**
|
|
@@ -276,24 +297,23 @@ static uint16_t ipv4_rx_csum ( struct pk_buff *pkb __unused,
|
276
|
297
|
* @v pkb Packet buffer
|
277
|
298
|
* @v tcpip Transport-layer protocol
|
278
|
299
|
* @v st_dest Destination network-layer address
|
|
300
|
+ * @v trans_csum Transport-layer checksum to complete, or NULL
|
279
|
301
|
* @ret rc Status
|
280
|
302
|
*
|
281
|
303
|
* This function expects a transport-layer segment and prepends the IP header
|
282
|
304
|
*/
|
283
|
305
|
static int ipv4_tx ( struct pk_buff *pkb,
|
284
|
306
|
struct tcpip_protocol *tcpip_protocol,
|
285
|
|
- struct sockaddr_tcpip *st_dest ) {
|
|
307
|
+ struct sockaddr_tcpip *st_dest, uint16_t *trans_csum ) {
|
286
|
308
|
struct iphdr *iphdr = pkb_push ( pkb, sizeof ( *iphdr ) );
|
287
|
309
|
struct sockaddr_in *sin_dest = ( ( struct sockaddr_in * ) st_dest );
|
288
|
310
|
struct ipv4_miniroute *miniroute;
|
289
|
|
- struct net_device *netdev = NULL;
|
290
|
311
|
struct in_addr next_hop;
|
291
|
|
- uint8_t ll_dest_buf[MAX_LL_ADDR_LEN];
|
292
|
|
- const uint8_t *ll_dest = ll_dest_buf;
|
|
312
|
+ uint8_t ll_dest[MAX_LL_ADDR_LEN];
|
293
|
313
|
int rc;
|
294
|
314
|
|
295
|
315
|
/* Fill up the IP header, except source address */
|
296
|
|
- iphdr->verhdrlen = ( ( IP_VER << 4 ) | ( sizeof ( *iphdr ) / 4 ) );
|
|
316
|
+ iphdr->verhdrlen = ( IP_VER | ( sizeof ( *iphdr ) / 4 ) );
|
297
|
317
|
iphdr->service = IP_TOS;
|
298
|
318
|
iphdr->len = htons ( pkb_len ( pkb ) );
|
299
|
319
|
iphdr->ident = htons ( ++next_ident );
|
|
@@ -307,18 +327,23 @@ static int ipv4_tx ( struct pk_buff *pkb,
|
307
|
327
|
next_hop = iphdr->dest;
|
308
|
328
|
miniroute = ipv4_route ( &next_hop );
|
309
|
329
|
if ( ! miniroute ) {
|
310
|
|
- DBG ( "No route to %s\n", inet_ntoa ( iphdr->dest ) );
|
|
330
|
+ DBG ( "IPv4 has no route to %s\n", inet_ntoa ( iphdr->dest ) );
|
311
|
331
|
rc = -EHOSTUNREACH;
|
312
|
332
|
goto err;
|
313
|
333
|
}
|
314
|
334
|
iphdr->src = miniroute->address;
|
315
|
|
- netdev = miniroute->netdev;
|
316
|
335
|
|
317
|
|
- /* Calculate the transport layer checksum */
|
318
|
|
- if ( tcpip_protocol->csum_offset > 0 )
|
319
|
|
- ipv4_tx_csum ( pkb, tcpip_protocol );
|
|
336
|
+ /* Determine link-layer destination address */
|
|
337
|
+ if ( ( rc = ipv4_ll_addr ( next_hop, iphdr->src, miniroute->netdev,
|
|
338
|
+ ll_dest ) ) != 0 ) {
|
|
339
|
+ DBG ( "IPv4 has no link-layer address for %s\n",
|
|
340
|
+ inet_ntoa ( iphdr->dest ) );
|
|
341
|
+ goto err;
|
|
342
|
+ }
|
320
|
343
|
|
321
|
|
- /* Calculate header checksum, in network byte order */
|
|
344
|
+ /* Fix up checksums */
|
|
345
|
+ if ( trans_csum )
|
|
346
|
+ *trans_csum = ipv4_pshdr_chksum ( pkb, *trans_csum );
|
322
|
347
|
iphdr->chksum = tcpip_chksum ( iphdr, sizeof ( *iphdr ) );
|
323
|
348
|
|
324
|
349
|
/* Print IP4 header for debugging */
|
|
@@ -327,34 +352,8 @@ static int ipv4_tx ( struct pk_buff *pkb,
|
327
|
352
|
inet_ntoa ( iphdr->dest ), ntohs ( iphdr->len ), iphdr->protocol,
|
328
|
353
|
ntohs ( iphdr->ident ), ntohs ( iphdr->chksum ) );
|
329
|
354
|
|
330
|
|
- /* Determine link-layer destination address */
|
331
|
|
- if ( next_hop.s_addr == INADDR_BROADCAST ) {
|
332
|
|
- /* Broadcast address */
|
333
|
|
- ll_dest = netdev->ll_protocol->ll_broadcast;
|
334
|
|
- } else if ( IN_MULTICAST ( next_hop.s_addr ) ) {
|
335
|
|
- /* Special case: IPv4 multicast over Ethernet. This
|
336
|
|
- * code may need to be generalised once we find out
|
337
|
|
- * what happens for other link layers.
|
338
|
|
- */
|
339
|
|
- uint8_t *next_hop_bytes = ( uint8_t * ) &next_hop;
|
340
|
|
- ll_dest_buf[0] = 0x01;
|
341
|
|
- ll_dest_buf[0] = 0x00;
|
342
|
|
- ll_dest_buf[0] = 0x5e;
|
343
|
|
- ll_dest_buf[3] = next_hop_bytes[1] & 0x7f;
|
344
|
|
- ll_dest_buf[4] = next_hop_bytes[2];
|
345
|
|
- ll_dest_buf[5] = next_hop_bytes[3];
|
346
|
|
- } else {
|
347
|
|
- /* Unicast address: resolve via ARP */
|
348
|
|
- if ( ( rc = arp_resolve ( netdev, &ipv4_protocol, &next_hop,
|
349
|
|
- &iphdr->src, ll_dest_buf ) ) != 0 ) {
|
350
|
|
- DBG ( "No ARP entry for %s\n",
|
351
|
|
- inet_ntoa ( iphdr->dest ) );
|
352
|
|
- goto err;
|
353
|
|
- }
|
354
|
|
- }
|
355
|
|
-
|
356
|
355
|
/* Hand off to link layer */
|
357
|
|
- return net_tx ( pkb, netdev, &ipv4_protocol, ll_dest );
|
|
356
|
+ return net_tx ( pkb, miniroute->netdev, &ipv4_protocol, ll_dest );
|
358
|
357
|
|
359
|
358
|
err:
|
360
|
359
|
free_pkb ( pkb );
|
|
@@ -374,73 +373,85 @@ static int ipv4_tx ( struct pk_buff *pkb,
|
374
|
373
|
static int ipv4_rx ( struct pk_buff *pkb, struct net_device *netdev __unused,
|
375
|
374
|
const void *ll_source __unused ) {
|
376
|
375
|
struct iphdr *iphdr = pkb->data;
|
|
376
|
+ size_t hdrlen;
|
|
377
|
+ size_t len;
|
377
|
378
|
union {
|
378
|
379
|
struct sockaddr_in sin;
|
379
|
380
|
struct sockaddr_tcpip st;
|
380
|
381
|
} src, dest;
|
381
|
|
- uint16_t chksum;
|
|
382
|
+ uint16_t csum;
|
|
383
|
+ uint16_t pshdr_csum;
|
382
|
384
|
|
383
|
|
- /* Sanity check */
|
|
385
|
+ /* Sanity check the IPv4 header */
|
384
|
386
|
if ( pkb_len ( pkb ) < sizeof ( *iphdr ) ) {
|
385
|
|
- DBG ( "IP datagram too short (%d bytes)\n", pkb_len ( pkb ) );
|
|
387
|
+ DBG ( "IPv4 packet too short at %d bytes (min %d bytes)\n",
|
|
388
|
+ pkb_len ( pkb ), sizeof ( *iphdr ) );
|
|
389
|
+ goto err;
|
|
390
|
+ }
|
|
391
|
+ if ( ( iphdr->verhdrlen & IP_MASK_VER ) != IP_VER ) {
|
|
392
|
+ DBG ( "IPv4 version %#02x not supported\n", iphdr->verhdrlen );
|
|
393
|
+ goto err;
|
|
394
|
+ }
|
|
395
|
+ hdrlen = ( ( iphdr->verhdrlen & IP_MASK_HLEN ) * 4 );
|
|
396
|
+ if ( hdrlen < sizeof ( *iphdr ) ) {
|
|
397
|
+ DBG ( "IPv4 header too short at %d bytes (min %d bytes)\n",
|
|
398
|
+ hdrlen, sizeof ( *iphdr ) );
|
|
399
|
+ goto err;
|
|
400
|
+ }
|
|
401
|
+ if ( hdrlen > pkb_len ( pkb ) ) {
|
|
402
|
+ DBG ( "IPv4 header too long at %d bytes "
|
|
403
|
+ "(packet is %d bytes)\n", hdrlen, pkb_len ( pkb ) );
|
|
404
|
+ goto err;
|
|
405
|
+ }
|
|
406
|
+ if ( ( csum = tcpip_chksum ( iphdr, hdrlen ) ) != 0 ) {
|
|
407
|
+ DBG ( "IPv4 checksum incorrect (is %04x including checksum "
|
|
408
|
+ "field, should be 0000)\n", csum );
|
|
409
|
+ goto err;
|
|
410
|
+ }
|
|
411
|
+ len = ntohs ( iphdr->len );
|
|
412
|
+ if ( len < hdrlen ) {
|
|
413
|
+ DBG ( "IPv4 length too short at %d bytes "
|
|
414
|
+ "(header is %d bytes)\n", len, hdrlen );
|
|
415
|
+ goto err;
|
|
416
|
+ }
|
|
417
|
+ if ( len > pkb_len ( pkb ) ) {
|
|
418
|
+ DBG ( "IPv4 length too long at %d bytes "
|
|
419
|
+ "(packet is %d bytes)\n", len, pkb_len ( pkb ) );
|
386
|
420
|
goto err;
|
387
|
421
|
}
|
388
|
422
|
|
389
|
|
- /* Print IP4 header for debugging */
|
|
423
|
+ /* Print IPv4 header for debugging */
|
390
|
424
|
DBG ( "IPv4 RX %s<-", inet_ntoa ( iphdr->dest ) );
|
391
|
425
|
DBG ( "%s len %d proto %d id %04x csum %04x\n",
|
392
|
426
|
inet_ntoa ( iphdr->src ), ntohs ( iphdr->len ), iphdr->protocol,
|
393
|
427
|
ntohs ( iphdr->ident ), ntohs ( iphdr->chksum ) );
|
394
|
428
|
|
395
|
|
- /* Validate version and header length */
|
396
|
|
- if ( iphdr->verhdrlen != 0x45 ) {
|
397
|
|
- DBG ( "Bad version and header length %x\n", iphdr->verhdrlen );
|
398
|
|
- goto err;
|
399
|
|
- }
|
400
|
|
-
|
401
|
|
- /* Validate length of IP packet */
|
402
|
|
- if ( ntohs ( iphdr->len ) > pkb_len ( pkb ) ) {
|
403
|
|
- DBG ( "Inconsistent packet length %d\n",
|
404
|
|
- ntohs ( iphdr->len ) );
|
405
|
|
- goto err;
|
406
|
|
- }
|
|
429
|
+ /* Truncate packet to correct length, calculate pseudo-header
|
|
430
|
+ * checksum and then strip off the IPv4 header.
|
|
431
|
+ */
|
|
432
|
+ pkb_unput ( pkb, ( pkb_len ( pkb ) - len ) );
|
|
433
|
+ pshdr_csum = ipv4_pshdr_chksum ( pkb, TCPIP_EMPTY_CSUM );
|
|
434
|
+ pkb_pull ( pkb, hdrlen );
|
407
|
435
|
|
408
|
|
- /* Verify the checksum */
|
409
|
|
- if ( ( chksum = ipv4_rx_csum ( pkb, iphdr->protocol ) ) != 0xffff ) {
|
410
|
|
- DBG ( "Bad checksum %x\n", chksum );
|
411
|
|
- }
|
412
|
436
|
/* Fragment reassembly */
|
413
|
437
|
if ( ( iphdr->frags & htons ( IP_MASK_MOREFRAGS ) ) ||
|
414
|
438
|
( ( iphdr->frags & htons ( IP_MASK_OFFSET ) ) != 0 ) ) {
|
415
|
|
- /* Pass the fragment to the reassembler ipv4_ressable() which
|
416
|
|
- * either returns a fully reassembled packet buffer or NULL.
|
|
439
|
+ /* Pass the fragment to ipv4_reassemble() which either
|
|
440
|
+ * returns a fully reassembled packet buffer or NULL.
|
417
|
441
|
*/
|
418
|
442
|
pkb = ipv4_reassemble ( pkb );
|
419
|
|
- if ( !pkb ) {
|
|
443
|
+ if ( ! pkb )
|
420
|
444
|
return 0;
|
421
|
|
- }
|
422
|
445
|
}
|
423
|
446
|
|
424
|
|
- /* To reduce code size, the following functions are not implemented:
|
425
|
|
- * 1. Check the destination address
|
426
|
|
- * 2. Check the TTL field
|
427
|
|
- * 3. Check the service field
|
428
|
|
- */
|
429
|
|
-
|
430
|
|
- /* Construct socket addresses */
|
|
447
|
+ /* Construct socket addresses and hand off to transport layer */
|
431
|
448
|
memset ( &src, 0, sizeof ( src ) );
|
432
|
449
|
src.sin.sin_family = AF_INET;
|
433
|
450
|
src.sin.sin_addr = iphdr->src;
|
434
|
451
|
memset ( &dest, 0, sizeof ( dest ) );
|
435
|
452
|
dest.sin.sin_family = AF_INET;
|
436
|
453
|
dest.sin.sin_addr = iphdr->dest;
|
437
|
|
-
|
438
|
|
- /* Strip header */
|
439
|
|
- pkb_unput ( pkb, pkb_len ( pkb ) - ntohs ( iphdr->len ) );
|
440
|
|
- pkb_pull ( pkb, sizeof ( *iphdr ) );
|
441
|
|
-
|
442
|
|
- /* Send it to the transport layer */
|
443
|
|
- return tcpip_rx ( pkb, iphdr->protocol, &src.st, &dest.st );
|
|
454
|
+ return tcpip_rx ( pkb, iphdr->protocol, &src.st, &dest.st, pshdr_csum);
|
444
|
455
|
|
445
|
456
|
err:
|
446
|
457
|
free_pkb ( pkb );
|