mirror of https://github.com/ipxe/ipxe.git
Added fragment reassembly code
parent
eb091f03e3
commit
5f651f8622
|
@ -8,18 +8,25 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <ip.h>
|
#include <ip.h>
|
||||||
|
#include <gpxe/retry.h>
|
||||||
|
|
||||||
/* IP constants */
|
/* IP constants */
|
||||||
|
|
||||||
#define IP_VER 4
|
#define IP_VER 4
|
||||||
#define IP_MASK_VER 0xf0
|
#define IP_MASK_VER 0xf0
|
||||||
#define IP_MASK_HLEN 0x0f
|
#define IP_MASK_HLEN 0x0f
|
||||||
|
#define IP_MASK_OFFSET 0x1fff
|
||||||
|
#define IP_MASK_DONOTFRAG 0x4000
|
||||||
|
#define IP_MASK_MOREFRAGS 0x2000
|
||||||
#define IP_PSHLEN 12
|
#define IP_PSHLEN 12
|
||||||
|
|
||||||
/* IP header defaults */
|
/* IP header defaults */
|
||||||
#define IP_TOS 0
|
#define IP_TOS 0
|
||||||
#define IP_TTL 64
|
#define IP_TTL 64
|
||||||
|
|
||||||
|
#define IP_FRAG_PKB_SIZE 1500
|
||||||
|
#define IP_FRAG_TIMEOUT 50
|
||||||
|
|
||||||
/* IP4 pseudo header */
|
/* IP4 pseudo header */
|
||||||
struct ipv4_pseudo_header {
|
struct ipv4_pseudo_header {
|
||||||
struct in_addr src;
|
struct in_addr src;
|
||||||
|
@ -29,6 +36,22 @@ struct ipv4_pseudo_header {
|
||||||
uint16_t len;
|
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 pk_buff;
|
||||||
struct net_device;
|
struct net_device;
|
||||||
struct net_protocol;
|
struct net_protocol;
|
||||||
|
|
121
src/net/ipv4.c
121
src/net/ipv4.c
|
@ -47,6 +47,9 @@ struct ipv4_miniroute {
|
||||||
/** List of IPv4 miniroutes */
|
/** List of IPv4 miniroutes */
|
||||||
static LIST_HEAD ( miniroutes );
|
static LIST_HEAD ( miniroutes );
|
||||||
|
|
||||||
|
/** List of fragment reassembly buffers */
|
||||||
|
static LIST_HEAD ( frag_buffers );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add IPv4 interface
|
* Add IPv4 interface
|
||||||
*
|
*
|
||||||
|
@ -119,6 +122,110 @@ static void ipv4_dump ( struct iphdr *iphdr __unused ) {
|
||||||
DBG ( "\tDestination = %s\n", inet_ntoa ( iphdr->dest ) );
|
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
|
* 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 */
|
/* Calculate the transport layer checksum */
|
||||||
|
if ( tcpip->csum_offset > 0 ) {
|
||||||
ipv4_tx_csum ( pkb, tcpip );
|
ipv4_tx_csum ( pkb, tcpip );
|
||||||
|
}
|
||||||
|
|
||||||
/* Calculate header checksum, in network byte order */
|
/* Calculate header checksum, in network byte order */
|
||||||
iphdr->chksum = 0;
|
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 ) {
|
if ( ( chksum = ipv4_rx_csum ( pkb, iphdr->protocol ) ) != 0xffff ) {
|
||||||
DBG ( "Bad checksum %x\n", chksum );
|
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:
|
/* To reduce code size, the following functions are not implemented:
|
||||||
* 1. Check the destination address
|
* 1. Check the destination address
|
||||||
|
|
|
@ -22,19 +22,14 @@ static inline void copy_sockaddr ( struct sockaddr *source, struct sockaddr *des
|
||||||
memcpy ( dest, source, sizeof ( *dest ) );
|
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 ) {
|
switch ( sock->sa_family ) {
|
||||||
case AF_INET:
|
case AF_INET:
|
||||||
dest = &sock->sin.sin_port;
|
return &sock->sin.sin_port;
|
||||||
break;
|
|
||||||
case AF_INET6:
|
case AF_INET6:
|
||||||
dest = &sock->sin6.sin6_port;
|
return &sock->sin6.sin6_port;
|
||||||
break;
|
|
||||||
default:
|
|
||||||
DBG ( "Network family %d not supported\n", sock->sa_family );
|
|
||||||
return -EAFNOSUPPORT;
|
|
||||||
}
|
}
|
||||||
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 ( "\tSource Port = %d\n", ntohs ( udphdr->source_port ) );
|
||||||
DBG ( "\tDestination Port = %d\n", ntohs ( udphdr->dest_port ) );
|
DBG ( "\tDestination Port = %d\n", ntohs ( udphdr->dest_port ) );
|
||||||
DBG ( "\tLength = %d\n", ntohs ( udphdr->len ) );
|
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 );
|
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
|
* sending it over the network
|
||||||
*/
|
*/
|
||||||
udphdr = pkb_push ( conn->tx_pkb, sizeof ( *udphdr ) );
|
udphdr = pkb_push ( conn->tx_pkb, sizeof ( *udphdr ) );
|
||||||
if ( (rc = dest_port ( sock, dest ) ) != 0 ) {
|
if ( (dest = dest_port ( sock ) ) == NULL ) {
|
||||||
return rc;
|
DBG ( "Network family %d not supported\n", sock->sa_family );
|
||||||
|
return -EAFNOSUPPORT;
|
||||||
}
|
}
|
||||||
udphdr->dest_port = htons ( *dest );
|
udphdr->dest_port = *dest;
|
||||||
udphdr->source_port = htons ( conn->local_port );
|
udphdr->source_port = conn->local_port;
|
||||||
udphdr->len = htons ( pkb_len ( conn->tx_pkb ) );
|
udphdr->len = htons ( pkb_len ( conn->tx_pkb ) );
|
||||||
udphdr->chksum = htons ( calc_chksum ( udphdr, sizeof ( *udphdr ) ) );
|
udphdr->chksum = htons ( calc_chksum ( udphdr, sizeof ( *udphdr ) ) );
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue