From 5f651f862232a63ca44833041deb381f305febc6 Mon Sep 17 00:00:00 2001 From: Nikhil Chandru Rao Date: Fri, 30 Jun 2006 08:52:03 +0000 Subject: [PATCH] Added fragment reassembly code --- src/include/gpxe/ip.h | 23 ++++++++ src/net/ipv4.c | 123 +++++++++++++++++++++++++++++++++++++++++- src/net/udp.c | 24 ++++----- 3 files changed, 155 insertions(+), 15 deletions(-) diff --git a/src/include/gpxe/ip.h b/src/include/gpxe/ip.h index a6c59064..4f0f42f8 100644 --- a/src/include/gpxe/ip.h +++ b/src/include/gpxe/ip.h @@ -8,18 +8,25 @@ */ #include +#include /* 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; diff --git a/src/net/ipv4.c b/src/net/ipv4.c index 9669b07d..19e56440 100644 --- a/src/net/ipv4.c +++ b/src/net/ipv4.c @@ -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 diff --git a/src/net/udp.c b/src/net/udp.c index 4a82e976..b84d5161 100644 --- a/src/net/udp.c +++ b/src/net/udp.c @@ -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 ) ) );