Added fragment reassembly code
authorNikhil Chandru Rao <nikhilcrao@users.sourceforge.net>
Fri, 30 Jun 2006 08:52:03 +0000 (08:52 +0000)
committerNikhil Chandru Rao <nikhilcrao@users.sourceforge.net>
Fri, 30 Jun 2006 08:52:03 +0000 (08:52 +0000)
src/include/gpxe/ip.h
src/net/ipv4.c
src/net/udp.c

index a6c5906..4f0f42f 100644 (file)
@@ -8,18 +8,25 @@
  */
 
 #include <ip.h>
+#include <gpxe/retry.h>
 
 /* IP constants */
 
 #define IP_VER         4
 #define IP_MASK_VER    0xf0
 #define IP_MASK_HLEN   0x0f
+#define IP_MASK_OFFSET 0x1fff
+#define IP_MASK_DONOTFRAG      0x4000
+#define IP_MASK_MOREFRAGS      0x2000
 #define IP_PSHLEN      12
 
 /* IP header defaults */
 #define IP_TOS         0
 #define IP_TTL         64
 
+#define IP_FRAG_PKB_SIZE       1500
+#define IP_FRAG_TIMEOUT                50
+
 /* IP4 pseudo header */
 struct ipv4_pseudo_header {
        struct in_addr src;
@@ -29,6 +36,22 @@ struct ipv4_pseudo_header {
        uint16_t len;
 };
 
+/* Fragment reassembly buffer */
+struct frag_buffer {
+       /* Identification number */
+       uint16_t ident;
+       /* Source network address */
+       struct in_addr src;
+       /* Destination network address */
+       struct in_addr dest;
+       /* Reassembled packet buffer */
+       struct pk_buff *frag_pkb;
+       /* Reassembly timer */
+       struct retry_timer frag_timer;
+       /* List of fragment reassembly buffers */
+       struct list_head list;
+};
+
 struct pk_buff;
 struct net_device;
 struct net_protocol;
index 9669b07..19e5644 100644 (file)
@@ -47,6 +47,9 @@ struct ipv4_miniroute {
 /** List of IPv4 miniroutes */
 static LIST_HEAD ( miniroutes );
 
+/** List of fragment reassembly buffers */
+static LIST_HEAD ( frag_buffers );
+
 /**
  * Add IPv4 interface
  *
@@ -119,6 +122,110 @@ static void ipv4_dump ( struct iphdr *iphdr __unused ) {
        DBG ( "\tDestination = %s\n", inet_ntoa ( iphdr->dest ) );
 }
 
+/**
+ * Fragment reassembly counter timeout
+ *
+ * @v timer    Retry timer
+ * @v over     If asserted, the timer is greater than @c MAX_TIMEOUT 
+ */
+void ipv4_frag_expired ( struct retry_timer *timer __unused , int over ) {
+       if ( over ) {
+               DBG ( "Fragment reassembly timeout" );
+               /* Free the fragment buffer */
+       }
+}
+
+/**
+ * Free fragment buffer
+ *
+ * @v fragbug  Fragment buffer
+ */
+void free_fragbuf ( struct frag_buffer *fragbuf ) {
+       if ( fragbuf ) {
+               free_dma ( fragbuf, sizeof ( *fragbuf ) );
+       }
+}
+
+/**
+ * Fragment reassembler
+ *
+ * @v pkb              Packet buffer, fragment of the datagram
+ * @ret frag_pkb       Reassembled packet, or NULL
+ */
+struct pk_buff * ipv4_reassemble ( struct pk_buff * pkb ) {
+       struct iphdr *iphdr = pkb->data;
+       struct frag_buffer *fragbuf;
+       
+       /**
+        * Check if the fragment belongs to any fragment series
+        */
+       list_for_each_entry ( fragbuf, &frag_buffers, list ) {
+               if ( fragbuf->ident == iphdr->ident &&
+                    fragbuf->src.s_addr == iphdr->src.s_addr ) {
+                       /**
+                        * Check if the packet is the expected fragment
+                        * 
+                        * The offset of the new packet must be equal to the
+                        * length of the data accumulated so far (the length of
+                        * the reassembled packet buffer
+                        */
+                       if ( pkb_len ( fragbuf->frag_pkb ) == 
+                             ( iphdr->frags & IP_MASK_OFFSET ) ) {
+                               /**
+                                * Append the contents of the fragment to the
+                                * reassembled packet buffer
+                                */
+                               pkb_pull ( pkb, sizeof ( *iphdr ) );
+                               memcpy ( pkb_put ( fragbuf->frag_pkb,
+                                                       pkb_len ( pkb ) ),
+                                        pkb->data, pkb_len ( pkb ) );
+                               free_pkb ( pkb );
+
+                               /** Check if the fragment series is over */
+                               if ( !iphdr->frags & IP_MASK_MOREFRAGS ) {
+                                       pkb = fragbuf->frag_pkb;
+                                       free_fragbuf ( fragbuf );
+                                       return pkb;
+                               }
+
+                       } else {
+                               /* Discard the fragment series */
+                               free_fragbuf ( fragbuf );
+                               free_pkb ( pkb );
+                       }
+                       return NULL;
+               }
+       }
+       
+       /** Check if the fragment is the first in the fragment series */
+       if ( iphdr->frags & IP_MASK_MOREFRAGS &&
+                       ( ( iphdr->frags & IP_MASK_OFFSET ) == 0 ) ) {
+       
+               /** Create a new fragment buffer */
+               fragbuf = ( struct frag_buffer* ) malloc ( sizeof( *fragbuf ) );
+               fragbuf->ident = iphdr->ident;
+               fragbuf->src = iphdr->src;
+
+               /* Set up the reassembly packet buffer */
+               fragbuf->frag_pkb = alloc_pkb ( IP_FRAG_PKB_SIZE );
+               pkb_pull ( pkb, sizeof ( *iphdr ) );
+               memcpy ( pkb_put ( fragbuf->frag_pkb, pkb_len ( pkb ) ),
+                        pkb->data, pkb_len ( pkb ) );
+               free_pkb ( pkb );
+
+               /* Set the reassembly timer */
+               fragbuf->frag_timer.timeout = IP_FRAG_TIMEOUT;
+               fragbuf->frag_timer.expired = ipv4_frag_expired;
+               start_timer ( &fragbuf->frag_timer );
+
+               /* Add the fragment buffer to the list of fragment buffers */
+               list_add ( &fragbuf->list, &frag_buffers );
+       }
+       
+       return NULL;
+}
+
+
 /**
  * Complete the transport-layer checksum
  *
@@ -294,7 +401,9 @@ int ipv4_tx ( struct pk_buff *pkb, struct tcpip_protocol *tcpip,
        }
 
        /* Calculate the transport layer checksum */
-       ipv4_tx_csum ( pkb, tcpip );
+       if ( tcpip->csum_offset > 0 ) {
+               ipv4_tx_csum ( pkb, tcpip );
+       }
 
        /* Calculate header checksum, in network byte order */
        iphdr->chksum = 0;
@@ -416,6 +525,18 @@ void ipv4_rx ( struct pk_buff *pkb, struct net_device *netdev __unused,
        if ( ( chksum = ipv4_rx_csum ( pkb, iphdr->protocol ) ) != 0xffff ) {
                DBG ( "Bad checksum %x\n", chksum );
        }
+       /* Fragment reassembly */
+       if ( iphdr->frags & IP_MASK_MOREFRAGS || 
+               ( !iphdr->frags & IP_MASK_MOREFRAGS &&
+                       iphdr->frags & IP_MASK_OFFSET != 0 ) ) {
+               /* Pass the fragment to the reassembler ipv4_ressable() which
+                * either returns a fully reassembled packet buffer or NULL.
+                */
+               pkb = ipv4_reassemble ( pkb );
+               if ( !pkb ) {
+                       return;
+               }
+       }
 
        /* To reduce code size, the following functions are not implemented:
         * 1. Check the destination address
index 4a82e97..b84d516 100644 (file)
@@ -22,19 +22,14 @@ static inline void copy_sockaddr ( struct sockaddr *source, struct sockaddr *des
        memcpy ( dest, source, sizeof ( *dest ) );
 }
 
-static inline uint16_t dest_port ( struct sockaddr *sock, uint16_t *dest ) {
+static inline uint16_t * dest_port ( struct sockaddr *sock ) {
        switch ( sock->sa_family ) {
        case AF_INET:
-               dest = &sock->sin.sin_port;
-               break;
+               return &sock->sin.sin_port;
        case AF_INET6:
-               dest = &sock->sin6.sin6_port;
-               break;
-       default:
-               DBG ( "Network family %d not supported\n", sock->sa_family );
-               return -EAFNOSUPPORT;
+               return &sock->sin6.sin6_port;
        }
-       return 0;
+       return NULL;
 }
 
 /**
@@ -49,7 +44,7 @@ void udp_dump ( struct udp_header *udphdr ) {
        DBG ( "\tSource Port = %d\n", ntohs ( udphdr->source_port ) );
        DBG ( "\tDestination Port = %d\n", ntohs ( udphdr->dest_port ) );
        DBG ( "\tLength = %d\n", ntohs ( udphdr->len ) );
-       DBG ( "\tChecksum = %d\n", ntohs ( udphdr->chksum ) );
+       DBG ( "\tChecksum = %x\n", ntohs ( udphdr->chksum ) );
        DBG ( "\tChecksum located at %#x\n", &udphdr->chksum );
 }
 
@@ -139,11 +134,12 @@ int udp_send ( struct udp_connection *conn, const void *data, size_t len ) {
         * sending it over the network
         */
        udphdr = pkb_push ( conn->tx_pkb, sizeof ( *udphdr ) );
-       if ( (rc = dest_port ( sock, dest ) ) != 0 ) {
-               return rc;
+       if ( (dest = dest_port ( sock ) ) == NULL ) {
+               DBG ( "Network family %d not supported\n", sock->sa_family );
+               return -EAFNOSUPPORT;
        }
-       udphdr->dest_port = htons ( *dest );
-       udphdr->source_port = htons ( conn->local_port );
+       udphdr->dest_port = *dest;
+       udphdr->source_port = conn->local_port;
        udphdr->len = htons ( pkb_len ( conn->tx_pkb ) );
        udphdr->chksum = htons ( calc_chksum ( udphdr, sizeof ( *udphdr ) ) );