aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNikhil Chandru Rao <nikhilcrao@users.sourceforge.net>2006-06-30 08:52:03 +0000
committerNikhil Chandru Rao <nikhilcrao@users.sourceforge.net>2006-06-30 08:52:03 +0000
commit5f651f862232a63ca44833041deb381f305febc6 (patch)
tree5b675a1a6d26ae34e4bb95b1d9808e3980332de9
parenteb091f03e3b05606e028df96931c8de5daeffe23 (diff)
downloadipxe-5f651f862232a63ca44833041deb381f305febc6.tar.gz
Added fragment reassembly code
-rw-r--r--src/include/gpxe/ip.h23
-rw-r--r--src/net/ipv4.c123
-rw-r--r--src/net/udp.c24
3 files changed, 155 insertions, 15 deletions
diff --git a/src/include/gpxe/ip.h b/src/include/gpxe/ip.h
index a6c590643..4f0f42f85 100644
--- a/src/include/gpxe/ip.h
+++ b/src/include/gpxe/ip.h
@@ -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;
diff --git a/src/net/ipv4.c b/src/net/ipv4.c
index 9669b07db..19e564401 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
*
@@ -120,6 +123,110 @@ static void ipv4_dump ( struct iphdr *iphdr __unused ) {
}
/**
+ * 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
*
* @v pkb Packet buffer
@@ -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 4a82e9761..b84d51610 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 ) ) );