diff options
| -rw-r--r-- | net/net.c | 188 | 
1 files changed, 183 insertions, 5 deletions
| @@ -1107,6 +1107,176 @@ static void CDPStart(void)  }  #endif +#ifdef CONFIG_IP_DEFRAG +/* + * This function collects fragments in a single packet, according + * to the algorithm in RFC815. It returns NULL or the pointer to + * a complete packet, in static storage + */ +#ifndef CONFIG_NET_MAXDEFRAG +#define CONFIG_NET_MAXDEFRAG 16384 +#endif +/* + * MAXDEFRAG, above, is chosen in the config file and  is real data + * so we need to add the NFS overhead, which is more than TFTP. + * To use sizeof in the internal unnamed structures, we need a real + * instance (can't do "sizeof(struct rpc_t.u.reply))", unfortunately). + * The compiler doesn't complain nor allocates the actual structure + */ +static struct rpc_t rpc_specimen; +#define IP_PKTSIZE (CONFIG_NET_MAXDEFRAG + sizeof(rpc_specimen.u.reply)) + +#define IP_MAXUDP (IP_PKTSIZE - IP_HDR_SIZE_NO_UDP) + +/* + * this is the packet being assembled, either data or frag control. + * Fragments go by 8 bytes, so this union must be 8 bytes long + */ +struct hole { +	/* first_byte is address of this structure */ +	u16 last_byte;	/* last byte in this hole + 1 (begin of next hole) */ +	u16 next_hole;	/* index of next (in 8-b blocks), 0 == none */ +	u16 prev_hole;	/* index of prev, 0 == none */ +	u16 unused; +}; + +static IP_t *__NetDefragment(IP_t *ip, int *lenp) +{ +	static uchar pkt_buff[IP_PKTSIZE] __attribute__((aligned(PKTALIGN))); +	static u16 first_hole, total_len; +	struct hole *payload, *thisfrag, *h, *newh; +	IP_t *localip = (IP_t *)pkt_buff; +	uchar *indata = (uchar *)ip; +	int offset8, start, len, done = 0; +	u16 ip_off = ntohs(ip->ip_off); + +	/* payload starts after IP header, this fragment is in there */ +	payload = (struct hole *)(pkt_buff + IP_HDR_SIZE_NO_UDP); +	offset8 =  (ip_off & IP_OFFS); +	thisfrag = payload + offset8; +	start = offset8 * 8; +	len = ntohs(ip->ip_len) - IP_HDR_SIZE_NO_UDP; + +	if (start + len > IP_MAXUDP) /* fragment extends too far */ +		return NULL; + +	if (!total_len || localip->ip_id != ip->ip_id) { +		/* new (or different) packet, reset structs */ +		total_len = 0xffff; +		payload[0].last_byte = ~0; +		payload[0].next_hole = 0; +		payload[0].prev_hole = 0; +		first_hole = 0; +		/* any IP header will work, copy the first we received */ +		memcpy(localip, ip, IP_HDR_SIZE_NO_UDP); +	} + +	/* +	 * What follows is the reassembly algorithm. We use the payload +	 * array as a linked list of hole descriptors, as each hole starts +	 * at a multiple of 8 bytes. However, last byte can be whatever value, +	 * so it is represented as byte count, not as 8-byte blocks. +	 */ + +	h = payload + first_hole; +	while (h->last_byte < start) { +		if (!h->next_hole) { +			/* no hole that far away */ +			return NULL; +		} +		h = payload + h->next_hole; +	} + +	if (offset8 + (len / 8) <= h - payload) { +		/* no overlap with holes (dup fragment?) */ +		return NULL; +	} + +	if (!(ip_off & IP_FLAGS_MFRAG)) { +		/* no more fragmentss: truncate this (last) hole */ +		total_len = start + len; +		h->last_byte = start + len; +	} + +	/* +	 * There is some overlap: fix the hole list. This code doesn't +	 * deal with a fragment that overlaps with two different holes +	 * (thus being a superset of a previously-received fragment). +	 */ + +	if ( (h >= thisfrag) && (h->last_byte <= start + len) ) { +		/* complete overlap with hole: remove hole */ +		if (!h->prev_hole && !h->next_hole) { +			/* last remaining hole */ +			done = 1; +		} else if (!h->prev_hole) { +			/* first hole */ +			first_hole = h->next_hole; +			payload[h->next_hole].prev_hole = 0; +		} else if (!h->next_hole) { +			/* last hole */ +			payload[h->prev_hole].next_hole = 0; +		} else { +			/* in the middle of the list */ +			payload[h->next_hole].prev_hole = h->prev_hole; +			payload[h->prev_hole].next_hole = h->next_hole; +		} + +	} else if (h->last_byte <= start + len) { +		/* overlaps with final part of the hole: shorten this hole */ +		h->last_byte = start; + +	} else if (h >= thisfrag) { +		/* overlaps with initial part of the hole: move this hole */ +		newh = thisfrag + (len / 8); +		*newh = *h; +		h = newh; +		if (h->next_hole) +			payload[h->next_hole].prev_hole = (h - payload); +		if (h->prev_hole) +			payload[h->prev_hole].next_hole = (h - payload); +		else +			first_hole = (h - payload); + +	} else { +		/* fragment sits in the middle: split the hole */ +		newh = thisfrag + (len / 8); +		*newh = *h; +		h->last_byte = start; +		h->next_hole = (newh - payload); +		newh->prev_hole = (h - payload); +		if (newh->next_hole) +			payload[newh->next_hole].prev_hole = (newh - payload); +	} + +	/* finally copy this fragment and possibly return whole packet */ +	memcpy((uchar *)thisfrag, indata + IP_HDR_SIZE_NO_UDP, len); +	if (!done) +		return NULL; + +	localip->ip_len = htons(total_len); +	*lenp = total_len + IP_HDR_SIZE_NO_UDP; +	return localip; +} + +static inline IP_t *NetDefragment(IP_t *ip, int *lenp) +{ +	u16 ip_off = ntohs(ip->ip_off); +	if (!(ip_off & (IP_OFFS | IP_FLAGS_MFRAG))) +		return ip; /* not a fragment */ +	return __NetDefragment(ip, lenp); +} + +#else /* !CONFIG_IP_DEFRAG */ + +static inline IP_t *NetDefragment(IP_t *ip, int *lenp) +{ +	u16 ip_off = ntohs(ip->ip_off); +	if (!(ip_off & (IP_OFFS | IP_FLAGS_MFRAG))) +		return ip; /* not a fragment */ +	return NULL; +} +#endif  void  NetReceive(volatile uchar * inpkt, int len) @@ -1333,10 +1503,12 @@ NetReceive(volatile uchar * inpkt, int len)  	case PROT_IP:  		debug("Got IP\n"); +		/* Before we start poking the header, make sure it is there */  		if (len < IP_HDR_SIZE) {  			debug("len bad %d < %lu\n", len, (ulong)IP_HDR_SIZE);  			return;  		} +		/* Check the packet length */  		if (len < ntohs(ip->ip_len)) {  			printf("len bad %d < %d\n", len, ntohs(ip->ip_len));  			return; @@ -1344,21 +1516,20 @@ NetReceive(volatile uchar * inpkt, int len)  		len = ntohs(ip->ip_len);  		debug("len=%d, v=%02x\n", len, ip->ip_hl_v & 0xff); +		/* Can't deal with anything except IPv4 */  		if ((ip->ip_hl_v & 0xf0) != 0x40) {  			return;  		} -		/* Can't deal with fragments */ -		if (ip->ip_off & htons(IP_OFFS | IP_FLAGS_MFRAG)) { -			return; -		} -		/* can't deal with headers > 20 bytes */ +		/* Can't deal with IP options (headers != 20 bytes) */  		if ((ip->ip_hl_v & 0x0f) > 0x05) {  			return;  		} +		/* Check the Checksum of the header */  		if (!NetCksumOk((uchar *)ip, IP_HDR_SIZE_NO_UDP / 2)) {  			puts ("checksum bad\n");  			return;  		} +		/* If it is not for us, ignore it */  		tmp = NetReadIP(&ip->ip_dst);  		if (NetOurIP && tmp != NetOurIP && tmp != 0xFFFFFFFF) {  #ifdef CONFIG_MCAST_TFTP @@ -1367,6 +1538,13 @@ NetReceive(volatile uchar * inpkt, int len)  			return;  		}  		/* +		 * The function returns the unchanged packet if it's not +		 * a fragment, and either the complete packet or NULL if +		 * it is a fragment (if !CONFIG_IP_DEFRAG, it returns NULL) +		 */ +		if (!(ip = NetDefragment(ip, &len))) +			return; +		/*  		 * watch for ICMP host redirects  		 *  		 * There is no real handler code (yet). We just watch |