Skip to contentSkip to conversation

Add simple IP/UDP fragmentation support

PATCH New Josh Boyer 4 participants 8 messages Aug 11, 2008 – Oct 28, 2008 Networking

lore
Share & tools
lore

Josh Boyer

author
#0 lore raw

PATCH 1/1 Add simple IP/UDP fragmentation support

From: Frank Haverkamp <[email protected]>

http://tools.ietf.org/html/rfc2348 describes the TFTP block size option which allows larger packtes than the 512 byte default. This reduces the number of TFTP ACKs significantly and improves performance.

To get the most benefit out of the tftp block size option the support of defragementation of IP/UDP packet is helpful. The current implemenation should work even with packets received out of order. To enable the large packet size the user should set "tftp_block_size" so a value like 16352.

We experimented with different packet sizes and found that more than those 16KiB do not contribute much to the performance anymore. Therefor I limited the defragmentation buffer to 16KiB no too waste memory.

---
 include/net.h |   17 ++++++
 net/net.c     |  156 ++++++++++++++++++++++++++++++++++++++++++++++++++--------
 net/tftp.c    |   22 ++++++++
 net/tftp.h    |   10 +++
 4 files changed, 185 insertions(+), 20 deletions(-)
include/net.h +17 -0
@@ -200,6 +200,13 @@ typedef struct {
 	ushort		udp_xsum;	/* Checksum			*/
 } IP_t;
 
+#define IP_OFFS			 0x1FFF	/* ip offset *= 8 */
+#define	  IP_OFFS_SHIFT	  3	/* in 8 byte steps */
+#define IP_FLAGS	 0xE000	/* first 3 bits */
+#define		 IP_FLAGS_RES	 0x8000	/* reserved */
+#define		 IP_FLAGS_DFRAG	 0x4000	/* don't fragments */
+#define		 IP_FLAGS_MFRAG	 0x2000	/* more fragments */
+
 #define IP_HDR_SIZE_NO_UDP	(sizeof (IP_t) - 8)
 #define IP_HDR_SIZE		(sizeof (IP_t))
 
@@ -282,6 +289,16 @@ typedef struct icmphdr {
 #define PKTSIZE_ALIGN		1536
 /*#define PKTSIZE		608*/
 
+ /*
+  * IP/UDP Fragmentation support
+  * See: http://en.wikipedia.org/wiki/IPv4#Fragmentation_and_reassembly
+  * MAX possible UDP packet size is 64 KiB, if there is memory available.
+  */
+#define NET_ETH_MTU		1500
+#define NET_FRAG_BUF_SIZE	(16 * 1024) /* MAX is 64 KiB */
+#define NET_UDP_FRAG_SIZE	(NET_ETH_MTU - IP_HDR_SIZE_NO_UDP) /* 1480 */
+#define NET_FRAG_BUF_USED	(NET_FRAG_BUF_SIZE / NET_UDP_FRAG_SIZE + 1)
+
 /*
  * Maximum receive ring size; that is, the number of packets
  * we can buffer before overflow happens. Basically, this just
net/net.c +136 -20

201 lines not on this page · raw message

net/tftp.c +22 -0
@@ -456,6 +456,7 @@ TftpTimeout (void)
 void
 TftpStart (void)
 {
+	char *s, *err;
 #ifdef CONFIG_TFTP_PORT
 	char *ep;             /* Environment pointer */
 #endif
@@ -518,6 +519,27 @@ TftpStart (void)
 
 	puts ("Loading: *\b");
 
+	/* Get alternate tftp_block_size */
+	if ((s = getenv("tftp_block_size")) != NULL) {
+		err = NULL;
+
+		TftpBlkSizeOption = simple_strtoul(s, &err, 10);
+		if (*err) {
+			printf("ERR: \"tftp_block_size\" is not a number\n");
+			TftpBlkSizeOption = TFTP_BLOCK_SIZE;
+		}
+		/*
+		 * Reject values which require extensive handling.
+		 * block size of 1428 octets (Ethernet MTU, less
+		 * the TFTP, UDP and IP header lengths).
+		 */
+		if (TftpBlkSizeOption > TFTP_BLOCK_SIZE_MAX) {
+			printf("ERR: tftp_block_sizes larger than %d not "
+			       "supported\n", TFTP_BLOCK_SIZE_MAX);
+			TftpBlkSizeOption = TFTP_BLOCK_SIZE;
+		}
+	}
+
 	NetSetTimeout (TIMEOUT * CFG_HZ, TftpTimeout);
 	NetSetHandler (TftpHandler);
 
net/tftp.h +10 -0
@@ -8,11 +8,21 @@
 #ifndef __TFTP_H__
 #define __TFTP_H__
 
+#include <net.h>
+
 /**********************************************************************/
 /*
  *	Global functions and variables.
  */
 
+/*
+ * Maximum TFTP block size bound to max size of fragmented IP/UDP
+ * packets minus TFTP and UDP/IP overhead. TFTP overhead is 2 byte
+ * opcode and 2 byte block-number.
+ */
+#define TFTP_BLOCK_SIZE_MAX     (NET_FRAG_BUF_SIZE - sizeof(IP_t) - 4)
+
+
 /* tftp.c */
 extern void	TftpStart (void);	/* Begin TFTP get */
 
#1 lore raw

PATCH 1/1 Add simple IP/UDP fragmentation support

Quoted 11 lines · Josh Boyer

From: Frank Haverkamp <[email protected]>

http://tools.ietf.org/html/rfc2348 describes the TFTP block size option which allows larger packtes than the 512 byte default. This reduces the number of TFTP ACKs significantly and improves performance.

To get the most benefit out of the tftp block size option the support of defragementation of IP/UDP packet is helpful. The current implemenation should work even with packets received out of order. To enable the large packet size the user should set "tftp_block_size" so a value like 16352.

Quoted 8 lines

We experimented with different packet sizes and found that more than those 16KiB do not contribute much to the performance anymore. Therefor I limited the defragmentation buffer to 16KiB no too waste memory.

---

Thanks. This looks like a good feature. I'll try to digest it tonight.

regards,
Ben

#2 lore raw

PATCH 1/1 Add simple IP/UDP fragmentation support

In reply to Josh Boyer

Hi Josh & Frank,

A few nits. Overall a nice patch.

One question: Will multicast TFTP still work when this is applied?

Quoted 5 lines · Josh Boyer

From: Frank Haverkamp <[email protected]>

http://tools.ietf.org/html/rfc2348 describes the TFTP block size option which allows larger packtes than the 512 byte default. This reduces the

s/packtes/packets/

Quoted 5 lines

number of TFTP ACKs significantly and improves performance.

To get the most benefit out of the tftp block size option the support of defragementation of IP/UDP packet is helpful. The current implemenation

s/defragementation/defragmentation/
s/implemenation/implementation/

Quoted 4 lines

should work even with packets received out of order. To enable the large packet size the user should set "tftp_block_size" so a value like 16352.

s/so/to/

Quoted 3 lines

We experimented with different packet sizes and found that more than those 16KiB do not contribute much to the performance anymore. Therefor I limited

s/Therefor/Therefore/

Quoted 3 lines

the defragmentation buffer to 16KiB no too waste memory.

so as to not waste memory

Quoted 14 lines
---
 include/net.h |   17 ++++++
 net/net.c     |  156 ++++++++++++++++++++++++++++++++++++++++++++++++++--------
 net/tftp.c    |   22 ++++++++
 net/tftp.h    |   10 +++
 4 files changed, 185 insertions(+), 20 deletions(-)
include/net.h +0 -0
@@ -200,6 +200,13 @@ typedef struct {
 	ushort		udp_xsum;	/* Checksum			*/

include/net.h typedef struct { · line 210

Quoted 10 lines
include/net.h +7 -0
 } IP_t;
 
+#define IP_OFFS			 0x1FFF	/* ip offset *= 8 */
+#define	  IP_OFFS_SHIFT	  3	/* in 8 byte steps */
+#define IP_FLAGS	 0xE000	/* first 3 bits */
+#define		 IP_FLAGS_RES	 0x8000	/* reserved */
+#define		 IP_FLAGS_DFRAG	 0x4000	/* don't fragments */
+#define		 IP_FLAGS_MFRAG	 0x2000	/* more fragments */
+
 

Please align these better. After applying, it's:

#define IP_OFFS 0x1FFF /* ip offset *= 8 */
#define IP_OFFS_SHIFT 3 /* in 8 byte steps */
#define IP_FLAGS 0xE000 /* first 3 bits */
#define IP_FLAGS_RES 0x8000 /* reserved */
#define IP_FLAGS_DFRAG 0x4000 /* don't fragments */
#define IP_FLAGS_MFRAG 0x2000 /* more fragments */

Quoted 54 lines
include/net.h +10 -0
 #define IP_HDR_SIZE_NO_UDP	(sizeof (IP_t) - 8)
 #define IP_HDR_SIZE		(sizeof (IP_t))
@@ -282,6 +289,16 @@ typedef struct icmphdr {
 #define PKTSIZE_ALIGN		1536
 /*#define PKTSIZE		608*/
 
+ /*
+  * IP/UDP Fragmentation support
+  * See: http://en.wikipedia.org/wiki/IPv4#Fragmentation_and_reassembly
+  * MAX possible UDP packet size is 64 KiB, if there is memory available.
+  */
+#define NET_ETH_MTU		1500
+#define NET_FRAG_BUF_SIZE	(16 * 1024) /* MAX is 64 KiB */
+#define NET_UDP_FRAG_SIZE	(NET_ETH_MTU - IP_HDR_SIZE_NO_UDP) /* 1480 */
+#define NET_FRAG_BUF_USED	(NET_FRAG_BUF_SIZE / NET_UDP_FRAG_SIZE + 1)
+
 /*
  * Maximum receive ring size; that is, the number of packets
  * we can buffer before overflow happens. Basically, this just
net/net.c +16 -0
@@ -192,6 +192,15 @@ volatile uchar	PktBuf[(PKTBUFSRX+1) * PK
 
 volatile uchar *NetRxPackets[PKTBUFSRX]; /* Receive packets			*/
 
+/* Packet fragmentation support */
+static uint16_t ip_id = 0; /* sequence number */
+static uint16_t udp_len = 0;
+static uint16_t udp_src = 0;
+static uint16_t udp_dst = 0;
+static int max_idx = 0;
+static uchar NetFragBuf[NET_FRAG_BUF_SIZE];
+static char NetFragBufUsed[NET_FRAG_BUF_USED] = { 0, };
+
 static rxhand_f *packetHandler;		/* Current RX packet handler		*/
 static thand_f *timeHandler;		/* Current timeout handler		*/
 static ulong	timeStart;		/* Time base value			*/
@@ -288,6 +297,13 @@ NetLoop(proto_t protocol)
 {
 	bd_t *bd = gd->bd;
 
+	/* Packet fragmentation support */
+	ip_id = udp_len = udp_src = udp_dst = max_idx = 0;
+	memset(NetFragBuf, 0xFF, sizeof(NetFragBuf));
+	memset(NetFragBufUsed, 0, sizeof(NetFragBufUsed));
+	printf("NetFragBuf @ %08x max tftp_block_size=%d udp_frag_size=%d\n",
+	       NetFragBuf, TFTP_BLOCK_SIZE_MAX, NET_UDP_FRAG_SIZE);
+
 #ifdef CONFIG_NET_MULTI
 	NetRestarted = 0;
 	NetDevExists = 0;
@@ -1150,6 +1166,39 @@ static void CDPStart(void)
 }

net/net.c static void CDPStart(void) · line 1176

Quoted 10 lines
net/net.c +7 -0
 #endif
 
+#ifdef CONFIG_UDP_CHECKSUM
+/*
+ * @sumptr: Points to UDP data
+ * @sumlen: Size of UDP data
+ * @xsum: UDP checksum across IP source, destination address, protocol and size
+ *
+ * Returns 0 when checksum is correct and 1 if it is not
 

Can you return -1 on failure? Not a big deal, just more conventional.

Quoted 30 lines
net/net.c +26 -0
+ */
+static int udp_checksum(ushort *sumptr, ushort sumlen, ulong xsum)
+{
+       while (sumlen > 1) {
+	       ushort sumdata;
+
+	       sumdata = *sumptr++;
+	       xsum += ntohs(sumdata);
+	       sumlen -= 2;
+       }
+       if (sumlen > 0) {
+	       ushort sumdata;
+
+	       sumdata = *(unsigned char *) sumptr;
+	       sumdata = (sumdata << 8) & 0xff00;
+	       xsum += sumdata;
+       }
+       while ((xsum >> 16) != 0) {
+	       xsum = (xsum & 0x0000ffff) + ((xsum >> 16) & 0x0000ffff);
+       }
+       if ((xsum != 0x00000000) && (xsum != 0x0000ffff))
+	       return 1;
+
+       return 0;
+}
+#endif /* CONFIG_UDP_CHECKSUM */
 
 void

NetReceive(volatile uchar * inpkt, int len)

net/net.c NetReceive(volatile uchar * inpkt, int l · line 1217

Quoted 5 lines
net/net.c +1 -0
@@ -1164,6 +1213,7 @@ NetReceive(volatile uchar * inpkt, int l
 	int iscdp;
 #endif
 	ushort cti = 0, vlanid = VLAN_NONE, myvlanid, mynvlanid;
+	uint32_t off;		/* ip_off for fragmentation */
 

Can you pick a better variable name than 'off'? 'offset' maybe?

net/net.c NetReceive(volatile uchar * inpkt, int l · line 1462

Quoted 13 lines
net/net.c +2 -0
 #ifdef ET_DEBUG
 	printf("packet received\n");
@@ -1404,9 +1454,11 @@ NetReceive(volatile uchar * inpkt, int l
 		if ((ip->ip_hl_v & 0xf0) != 0x40) {
 			return;
 		}
+#if 0		/* Obsolete after adding the fragmentation support */
 		if (ip->ip_off & htons(0x1fff)) { /* Can't deal w/ fragments */
 			return;
 		}
+#endif
 

Please delete this block if it's now dead code.

Quoted 37 lines
net/net.c +0 -0
 		/* can't deal with headers > 20 bytes */
 		if ((ip->ip_hl_v & 0x0f) > 0x05) {
net/net.c +30 -0
 			return;
@@ -1422,6 +1474,88 @@ NetReceive(volatile uchar * inpkt, int l
 #endif
 			return;
 		}
+
+		/*
+		 * Fragmentation support. We need to check the ip_id
+		 * and if all fragments were received correctly.
+		 */
+		off = (ntohs(ip->ip_off) & IP_OFFS) << IP_OFFS_SHIFT;
+		if ((off != 0) || (ip->ip_off & htons(IP_FLAGS_MFRAG))) {
+			int size, idx, complete;
+			char *start;
+
+			/* New fragmented packet arrived, clear data. */
+			if (ntohs(ip->ip_id) != ip_id) {
+				ip_id = ntohs(ip->ip_id);
+				memset(NetFragBufUsed, 0, sizeof(NetFragBufUsed));
+				udp_len = udp_src = udp_dst = max_idx = 0;
+			}
+
+			idx = off / NET_UDP_FRAG_SIZE;
+
+			/* Packet does not fit into IP/UDP fragmentation buf */
+			if (idx >= NET_FRAG_BUF_USED) {
+				return;
+			}
+
+			NetFragBufUsed[idx] = 1;
+
+			/* Copy the UDP hdr with the data for 1st
+			   fragment, else copy just payload */
+			if (off == 0) {
+				udp_len = ntohs(ip->udp_len);

net/net.c NetReceive(volatile uchar * inpkt, int l · line 1516

Quoted 10 lines
net/net.c +9 -0
+				udp_src = ntohs(ip->udp_src);
+				udp_dst = ntohs(ip->udp_dst);
+			}
+			size = ntohs(ip->ip_len) - IP_HDR_SIZE_NO_UDP;
+			start = (char *)ip + IP_HDR_SIZE_NO_UDP;
+			memcpy(NetFragBuf + off, start, size);
+
+			/*
+			 * When last fragement has been received we
 

s/fragement/fragment/

Quoted 31 lines
net/net.c +31 -0
+			 * know the number of fragments we expect. If
+			 * all have arrived we process the packet.
+			 */
+			if (((off != 0) && !(ip->ip_off & htons(IP_FLAGS_MFRAG))))
+				max_idx = idx;
+
+			if (max_idx == 0)
+				return;
+
+			complete = 1;
+			for (idx = 0; idx < max_idx; idx++) {
+				if (NetFragBufUsed[idx] == 0) {
+					complete = 0;
+					break;
+				}
+			}
+			if (!complete)
+				return;
+#ifdef CONFIG_UDP_CHECKSUM
+			if (ip->udp_xsum != 0) {
+				ulong xsum = ip->ip_p;
+				uint16_t *sumptr;
+
+				xsum += udp_len;
+				xsum += (ntohl(ip->ip_src) >> 16) & 0xffff;
+				xsum += (ntohl(ip->ip_src) >>  0) & 0xffff;
+				xsum += (ntohl(ip->ip_dst) >> 16) & 0xffff;
+				xsum += (ntohl(ip->ip_dst) >>  0) & 0xffff;
+				sumptr = (ushort *)NetFragBuf;
+
+				if (udp_checksum(sumptr, udp_len, xsum)) {

net/net.c NetReceive(volatile uchar * inpkt, int l

Quoted 10 lines
net/net.c +9 -0
+					putc('U');
+					return;
+				}
+			}
+#endif /* CONFIG_UDP_CHECKSUM */
+			(*packetHandler)(NetFragBuf + 8,
+					 udp_dst,
+					 udp_src,
+					 udp_len - 8);
 

Can some of these arguments share lines?

Quoted 46 lines
net/net.c +3 -0
+			return;
+		}
+
 		/*
net/net.c +2 -20
 		 * watch for ICMP host redirects
 		 *
@@ -1502,26 +1636,8 @@ NetReceive(volatile uchar * inpkt, int l
 			sumlen = ntohs(ip->udp_len);
 			sumptr = (ushort *) &(ip->udp_src);
 
-			while (sumlen > 1) {
-				ushort sumdata;
-
-				sumdata = *sumptr++;
-				xsum += ntohs(sumdata);
-				sumlen -= 2;
-			}
-			if (sumlen > 0) {
-				ushort sumdata;
-
-				sumdata = *(unsigned char *) sumptr;
-				sumdata = (sumdata << 8) & 0xff00;
-				xsum += sumdata;
-			}
-			while ((xsum >> 16) != 0) {
-				xsum = (xsum & 0x0000ffff) + ((xsum >> 16) & 0x0000ffff);
-			}
-			if ((xsum != 0x00000000) && (xsum != 0x0000ffff)) {
-				printf(" UDP wrong checksum %08lx %08x\n",
-					xsum, ntohs(ip->udp_xsum));
+			if (udp_checksum(sumptr, sumlen, xsum)) {
+				putc('U');
 				return;
 			}
 		}
net/tftp.c +1 -0
@@ -456,6 +456,7 @@ TftpTimeout (void)
 void
 TftpStart (void)
 {
+	char *s, *err;
 #ifdef CONFIG_TFTP_PORT
 	char *ep;             /* Environment pointer */
 #endif

net/tftp.c TftpStart (void) · line 524

Quoted 6 lines
net/tftp.c +2 -0
@@ -518,6 +519,27 @@ TftpStart (void)
 
 	puts ("Loading: *\b");
 
+	/* Get alternate tftp_block_size */
+	if ((s = getenv("tftp_block_size")) != NULL) {
 

Kind of a long environment variable name. Maybe "tftp_bs"?

Quoted 38 lines
net/tftp.c +19 -0
+		err = NULL;
+
+		TftpBlkSizeOption = simple_strtoul(s, &err, 10);
+		if (*err) {
+			printf("ERR: \"tftp_block_size\" is not a number\n");
+			TftpBlkSizeOption = TFTP_BLOCK_SIZE;
+		}
+		/*
+		 * Reject values which require extensive handling.
+		 * block size of 1428 octets (Ethernet MTU, less
+		 * the TFTP, UDP and IP header lengths).
+		 */
+		if (TftpBlkSizeOption > TFTP_BLOCK_SIZE_MAX) {
+			printf("ERR: tftp_block_sizes larger than %d not "
+			       "supported\n", TFTP_BLOCK_SIZE_MAX);
+			TftpBlkSizeOption = TFTP_BLOCK_SIZE;
+		}
+	}
+
 	NetSetTimeout (TIMEOUT * CFG_HZ, TftpTimeout);
 	NetSetHandler (TftpHandler);
net/tftp.h +3 -0
@@ -8,11 +8,21 @@
 #ifndef __TFTP_H__
 #define __TFTP_H__
 
+#include <net.h>
+
 /**********************************************************************/
 /*
  *	Global functions and variables.
  */
 
+/*

net/tftp.h line 28

Quoted 10 lines
net/tftp.h +7 -0
+ * Maximum TFTP block size bound to max size of fragmented IP/UDP
+ * packets minus TFTP and UDP/IP overhead. TFTP overhead is 2 byte
+ * opcode and 2 byte block-number.
+ */
+#define TFTP_BLOCK_SIZE_MAX     (NET_FRAG_BUF_SIZE - sizeof(IP_t) - 4)
+
+
 /* tftp.c */
 extern void	TftpStart (void);	/* Begin TFTP get */
 

regards,
Ben

#3 lore raw

PATCH 1/1 Add simple IP/UDP fragmentation support

Subject: U-Boot: Add simple IP/UDP fragmentation support
From: Frank Haverkamp <[email protected]>

http://tools.ietf.org/html/rfc2348 describes the TFTP block size option which allows larger packets than the 512 byte default. This reduces the number of TFTP ACKs significantly and improves performance.

To get the most benefit out of the tftp block size option the support of defragmentation of IP/UDP packet is helpful. The current implementation
should work even with packets received out of order. To enable the large packet size the user should set "tftp_bsize" to a value like 16352.

We experimented with different packet sizes and found that more than those
16KiB do not contribute much to the performance anymore. Therefore I limited
the defragmentation buffer to 16KiB as not to not waste memory.

Multicast TFTP is not yet tested with this patch. Would be nice if someone who has set this up could give it a try.

---
 include/net.h |   17 ++++++
 net/net.c     |  155
+++++++++++++++++++++++++++++++++++++++++++++++++---------
 net/tftp.c    |   22 ++++++++
 net/tftp.h    |   10 +++
 4 files changed, 182 insertions(+), 22 deletions(-)
include/net.h +11 -0
@@ -200,6 +200,13 @@ typedef struct {
 	ushort		udp_xsum;	/* Checksum			*/
 } IP_t;
 
+#define IP_OFFS			0x1FFF	/* ip offset *= 8 */
+#define	  IP_OFFS_SHIFT		3	/* in 8 byte steps */
+#define IP_FLAGS		0xE000	/* first 3 bits */
+#define	  IP_FLAGS_RES		0x8000	/* reserved */
+#define	  IP_FLAGS_DFRAG	0x4000	/* don't fragments */
+#define	  IP_FLAGS_MFRAG	0x2000	/* more fragments */
+
 #define IP_HDR_SIZE_NO_UDP	(sizeof (IP_t) - 8)
 #define IP_HDR_SIZE		(sizeof (IP_t))
 
@@ -282,6 +289,16 @@ typedef struct icmphdr {
 #define PKTSIZE_ALIGN		1536
 /*#define PKTSIZE		608*/
 
+ /*
+  * IP/UDP Fragmentation support
+  * See: http://en.wikipedia.org/wiki/IPv4#Fragmentation_and_reassembly
+  * MAX possible UDP packet size is 64 KiB, if there is memory

available.
+ */
+#define NET_ETH_MTU 1500
+#define NET_FRAG_BUF_SIZE (16 * 1024) /* MAX is 64 KiB */
+#define NET_UDP_FRAG_SIZE (NET_ETH_MTU - IP_HDR_SIZE_NO_UDP) /* 1480 */
+#define NET_FRAG_BUF_USED (NET_FRAG_BUF_SIZE / NET_UDP_FRAG_SIZE + 1)
+
/*
* Maximum receive ring size; that is, the number of packets
* we can buffer before overflow happens. Basically, this just

net/net.c +21 -0
@@ -192,6 +192,15 @@ volatile uchar	PktBuf[(PKTBUFSRX+1) * PK
 
 volatile uchar *NetRxPackets[PKTBUFSRX]; /* Receive packets			*/
 
+/* Packet fragmentation support */
+static uint16_t ip_id = 0; /* sequence number */
+static uint16_t udp_len = 0;
+static uint16_t udp_src = 0;
+static uint16_t udp_dst = 0;
+static int max_idx = 0;
+static uchar NetFragBuf[NET_FRAG_BUF_SIZE];
+static char NetFragBufUsed[NET_FRAG_BUF_USED] = { 0, };
+
 static rxhand_f *packetHandler;		/* Current RX packet handler		*/
 static thand_f *timeHandler;		/* Current timeout handler		*/
 static ulong	timeStart;		/* Time base value			*/
@@ -288,6 +297,13 @@ NetLoop(proto_t protocol)
 {
 	bd_t *bd = gd->bd;
 
+	/* Packet fragmentation support */
+	ip_id = udp_len = udp_src = udp_dst = max_idx = 0;
+	memset(NetFragBuf, 0xFF, sizeof(NetFragBuf));
+	memset(NetFragBufUsed, 0, sizeof(NetFragBufUsed));
+	printf("NetFragBuf @ %08x max tftp_bsize=%d udp_frag_size=%d\n",
+	       NetFragBuf, TFTP_BLOCK_SIZE_MAX, NET_UDP_FRAG_SIZE);
+
 #ifdef CONFIG_NET_MULTI
 	NetRestarted = 0;
 	NetDevExists = 0;
@@ -1150,6 +1166,39 @@ static void CDPStart(void)
 }
 #endif
 
+#ifdef CONFIG_UDP_CHECKSUM
+/*
+ * @sumptr: Points to UDP data
+ * @sumlen: Size of UDP data
+ * @xsum: UDP checksum across IP source, destination address, protocol

and size
+ *
+ * Returns 0 when checksum is correct and -1 if it is not.
+ */
+static int udp_checksum(ushort *sumptr, ushort sumlen, ulong xsum) +{
+ while (sumlen > 1) {
+ ushort sumdata;
+

+	       sumdata = *sumptr++;
+	       xsum += ntohs(sumdata);
+	       sumlen -= 2;

+ }
+ if (sumlen > 0) {
+ ushort sumdata;
+

+	       sumdata = *(unsigned char *) sumptr;
+	       sumdata = (sumdata << 8) & 0xff00;
+	       xsum += sumdata;

+ }
+ while ((xsum >> 16) != 0) {
+ xsum = (xsum & 0x0000ffff) + ((xsum >> 16) & 0x0000ffff);
+ }
+ if ((xsum != 0x00000000) && (xsum != 0x0000ffff))
+ return -1;
+
+ return 0;
+}
+#endif /* CONFIG_UDP_CHECKSUM */

void
NetReceive(volatile uchar * inpkt, int len)
@@ -1164,6 +1213,7 @@ NetReceive(volatile uchar * inpkt, int l
int iscdp;
#endif
ushort cti = 0, vlanid = VLAN_NONE, myvlanid, mynvlanid;
+ uint32_t ip_off; /* ip_off for fragmentation */

#ifdef ET_DEBUG
printf("packet received\n");
@@ -1404,9 +1454,7 @@ NetReceive(volatile uchar * inpkt, int l
if ((ip->ip_hl_v & 0xf0) != 0x40) {
return;
}
- if (ip->ip_off & htons(0x1fff)) { /* Can't deal w/ fragments */
- return;
- }
+
/* can't deal with headers > 20 bytes */ if ((ip->ip_hl_v & 0x0f) > 0x05) {
return;
@@ -1422,6 +1470,86 @@ NetReceive(volatile uchar * inpkt, int l
#endif
return;
}
+
+ /*
+ * Fragmentation support. We need to check the ip_id
+ * and if all fragments were received correctly.
+ */
+ ip_off = (ntohs(ip->ip_off) & IP_OFFS) << IP_OFFS_SHIFT;
+ if ((ip_off != 0) || (ip->ip_off & htons(IP_FLAGS_MFRAG))) {
+ int size, idx, complete;
+ char *start;
+
+ /* New fragmented packet arrived, clear data. */
+ if (ntohs(ip->ip_id) != ip_id) {
+ ip_id = ntohs(ip->ip_id);
+ memset(NetFragBufUsed, 0, sizeof(NetFragBufUsed));
+ udp_len = udp_src = udp_dst = max_idx = 0;
+ }
+
+ idx = ip_off / NET_UDP_FRAG_SIZE;
+
+ /* Packet does not fit into IP/UDP fragmentation buf */
+ if (idx >= NET_FRAG_BUF_USED) {
+ return;
+ }
+
+ NetFragBufUsed[idx] = 1;
+
+ /* Copy the UDP hdr with the data for 1st
+ fragment, else copy just payload */
+ if (ip_off == 0) {
+ udp_len = ntohs(ip->udp_len);
+ udp_src = ntohs(ip->udp_src);
+ udp_dst = ntohs(ip->udp_dst);
+ }
+ size = ntohs(ip->ip_len) - IP_HDR_SIZE_NO_UDP;
+ start = (char *)ip + IP_HDR_SIZE_NO_UDP;
+ memcpy(NetFragBuf + ip_off, start, size);
+
+ /*
+ * When last fragment has been received we
+ * know the number of fragments we expect. If
+ * all have arrived we process the packet.
+ */
+ if (((ip_off != 0) && !(ip->ip_off & htons(IP_FLAGS_MFRAG))))
+ max_idx = idx;
+
+ if (max_idx == 0)
+ return;
+
+ complete = 1;
+ for (idx = 0; idx < max_idx; idx++) {
+ if (NetFragBufUsed[idx] == 0) {
+ complete = 0;
+ break;
+ }
+ }
+ if (!complete)
+ return;
+#ifdef CONFIG_UDP_CHECKSUM
+ if (ip->udp_xsum != 0) {
+ ulong xsum = ip->ip_p;
+ uint16_t *sumptr;
+
+ xsum += udp_len;
+ xsum += (ntohl(ip->ip_src) >> 16) & 0xffff;
+ xsum += (ntohl(ip->ip_src) >> 0) & 0xffff;
+ xsum += (ntohl(ip->ip_dst) >> 16) & 0xffff;
+ xsum += (ntohl(ip->ip_dst) >> 0) & 0xffff;
+ sumptr = (ushort *)NetFragBuf;
+
+ if (udp_checksum(sumptr, udp_len, xsum)) {
+ putc('U');
+ return;
+ }
+ }
+#endif /* CONFIG_UDP_CHECKSUM */
+ (*packetHandler)(NetFragBuf + 8, udp_dst, udp_src,
+ udp_len - 8);
+ return;
+ }
+
/*
* watch for ICMP host redirects
*
@@ -1502,26 +1630,9 @@ NetReceive(volatile uchar * inpkt, int l
sumlen = ntohs(ip->udp_len); sumptr = (ushort *) &(ip->udp_src);

- while (sumlen > 1) {
- ushort sumdata;
-
- sumdata = *sumptr++;
- xsum += ntohs(sumdata);
- sumlen -= 2;
- }
- if (sumlen > 0) {
- ushort sumdata;

- sumdata = *(unsigned char *) sumptr;
- sumdata = (sumdata << 8) & 0xff00;
- xsum += sumdata;
- }
- while ((xsum >> 16) != 0) {
- xsum = (xsum & 0x0000ffff) + ((xsum >> 16) & 0x0000ffff);
- }
- if ((xsum != 0x00000000) && (xsum != 0x0000ffff)) {
- printf(" UDP wrong checksum %08lx %08x\n",
- xsum, ntohs(ip->udp_xsum));
+ if (udp_checksum(sumptr, sumlen, xsum)) {
+ putc('U');
return;
}
}

net/tftp.c +22 -0
@@ -456,6 +456,7 @@ TftpTimeout (void)
 void
 TftpStart (void)
 {
+	char *s, *err;
 #ifdef CONFIG_TFTP_PORT
 	char *ep;             /* Environment pointer */
 #endif
@@ -518,6 +519,27 @@ TftpStart (void)
 
 	puts ("Loading: *\b");
 
+	/* Get alternate tftp_bsize */
+	if ((s = getenv("tftp_bsize")) != NULL) {
+		err = NULL;
+
+		TftpBlkSizeOption = simple_strtoul(s, &err, 10);
+		if (*err) {
+			printf("ERR: \"tftp_bsize\" is not a number\n");
+			TftpBlkSizeOption = TFTP_BLOCK_SIZE;
+		}
+		/*
+		 * Reject values which require extensive handling.
+		 * block size of 1428 octets (Ethernet MTU, less
+		 * the TFTP, UDP and IP header lengths).
+		 */
+		if (TftpBlkSizeOption > TFTP_BLOCK_SIZE_MAX) {
+			printf("ERR: tftp_bsize larger than %d not supported\n",
+			       TFTP_BLOCK_SIZE_MAX);
+			TftpBlkSizeOption = TFTP_BLOCK_SIZE;
+		}
+	}
+
 	NetSetTimeout (TIMEOUT * CFG_HZ, TftpTimeout);
 	NetSetHandler (TftpHandler);
 
net/tftp.h +10 -0
@@ -8,11 +8,21 @@
 #ifndef __TFTP_H__
 #define __TFTP_H__
 
+#include <net.h>
+
 /**********************************************************************/
 /*
  *	Global functions and variables.
  */
 
+/*
+ * Maximum TFTP block size bound to max size of fragmented IP/UDP
+ * packets minus TFTP and UDP/IP overhead. TFTP overhead is 2 byte
+ * opcode and 2 byte block-number.
+ */
+#define TFTP_BLOCK_SIZE_MAX     (NET_FRAG_BUF_SIZE - sizeof(IP_t) - 4)
+
+
 /* tftp.c */
 extern void	TftpStart (void);	/* Begin TFTP get */
 
Signature · 8 lines
IBM Systems & Technology Group, Integrated Systems Development / Open
Systems Firmware Development, IBM Deutschland Schoenaicher Str. 220
71032 Boeblingen Phone: +49-7031-16-5133 E-Mail: haverkam at de.ibm.com

IBM Deutschland Research & Development GmbH / Vorsitzender des
Aufsichtsrats: Martin Jetter, Gesch?ftsf?hrung: Erich Baier, Sitz der
Gesellschaft: B?blingen / Registergericht: Amtsgericht Stuttgart, HRB
243294
#4 lore raw

PATCH 1/1 Add simple IP/UDP fragmentation support

Dear Ben,

Quoted 2 lines · In message <1220460631.17453.3.camel@august> Frank Haverkamp wrote:

Subject: U-Boot: Add simple IP/UDP fragmentation support
From: Frank Haverkamp <[email protected]>

You replied:

| I know you guys posted this one a couple of months ago and I apologize 
| for not being more active.  I've put this in the net/testing repo with 
| the hope that people will smack it around a bit.

What to do with this now?

Best regards,

Wolfgang Denk

Signature · 5 lines
DENX Software Engineering GmbH,     MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd at denx.de
Philosophy is a game with objectives and no rules.
Mathematics is a game with rules and no objectives.
#5 lore raw

PATCH 1/1 Add simple IP/UDP fragmentation support

Quoted 14 lines · Wolfgang Denk

Dear Ben,

Quoted 3 lines · In message <1220460631.17453.3.camel@august> Frank Haverkamp wrote:

Subject: U-Boot: Add simple IP/UDP fragmentation support
From: Frank Haverkamp <[email protected]>

You replied:

| I know you guys posted this one a couple of months ago and I apologize 
| for not being more active.  I've put this in the net/testing repo with 
| the hope that people will smack it around a bit.
Quoted 7 lines

What to do with this now?

Best regards,

Wolfgang Denk

We may as well pull it in. I have quite a bit of net-related stuff in the next few days and will include this patch.

regards,
Ben

#6 lore raw

PATCH 1/1 Add simple IP/UDP fragmentation support

In reply to Josh Boyer

Hi Josh,

Quoted 11 lines · Josh Boyer

From: Frank Haverkamp <[email protected]>

http://tools.ietf.org/html/rfc2348 describes the TFTP block size option which allows larger packtes than the 512 byte default. This reduces the number of TFTP ACKs significantly and improves performance.

To get the most benefit out of the tftp block size option the support of defragementation of IP/UDP packet is helpful. The current implemenation should work even with packets received out of order. To enable the large packet size the user should set "tftp_block_size" so a value like 16352.

Quoted 7 lines

We experimented with different packet sizes and found that more than those 16KiB do not contribute much to the performance anymore. Therefor I limited the defragmentation buffer to 16KiB no too waste memory.

I know you guys posted this one a couple of months ago and I apologize for not being more active. I've put this in the net/testing repo with the hope that people will smack it around a bit.

regards,
Ben

#7 lore raw

PATCH 1/1 Add simple IP/UDP fragmentation support

In reply to Josh Boyer

Josh & Frank,

Sorry again for dropping the ball on this one. This is a useful feature that we should add to U-boot, but I'm concerned about the memory footprint.

Quoted 63 lines · Josh Boyer

From: Frank Haverkamp <[email protected]>

http://tools.ietf.org/html/rfc2348 describes the TFTP block size option which allows larger packtes than the 512 byte default. This reduces the number of TFTP ACKs significantly and improves performance.

To get the most benefit out of the tftp block size option the support of defragementation of IP/UDP packet is helpful. The current implemenation should work even with packets received out of order. To enable the large packet size the user should set "tftp_block_size" so a value like 16352.

We experimented with different packet sizes and found that more than those 16KiB do not contribute much to the performance anymore. Therefor I limited the defragmentation buffer to 16KiB no too waste memory.

---
 include/net.h |   17 ++++++
 net/net.c     |  156 ++++++++++++++++++++++++++++++++++++++++++++++++++--------
 net/tftp.c    |   22 ++++++++
 net/tftp.h    |   10 +++
 4 files changed, 185 insertions(+), 20 deletions(-)
include/net.h +17 -0
@@ -200,6 +200,13 @@ typedef struct {
 	ushort		udp_xsum;	/* Checksum			*/
 } IP_t;
 
+#define IP_OFFS			 0x1FFF	/* ip offset *= 8 */
+#define	  IP_OFFS_SHIFT	  3	/* in 8 byte steps */
+#define IP_FLAGS	 0xE000	/* first 3 bits */
+#define		 IP_FLAGS_RES	 0x8000	/* reserved */
+#define		 IP_FLAGS_DFRAG	 0x4000	/* don't fragments */
+#define		 IP_FLAGS_MFRAG	 0x2000	/* more fragments */
+
 #define IP_HDR_SIZE_NO_UDP	(sizeof (IP_t) - 8)
 #define IP_HDR_SIZE		(sizeof (IP_t))
 
@@ -282,6 +289,16 @@ typedef struct icmphdr {
 #define PKTSIZE_ALIGN		1536
 /*#define PKTSIZE		608*/
 
+ /*
+  * IP/UDP Fragmentation support
+  * See: http://en.wikipedia.org/wiki/IPv4#Fragmentation_and_reassembly
+  * MAX possible UDP packet size is 64 KiB, if there is memory available.
+  */
+#define NET_ETH_MTU		1500
+#define NET_FRAG_BUF_SIZE	(16 * 1024) /* MAX is 64 KiB */
+#define NET_UDP_FRAG_SIZE	(NET_ETH_MTU - IP_HDR_SIZE_NO_UDP) /* 1480 */
+#define NET_FRAG_BUF_USED	(NET_FRAG_BUF_SIZE / NET_UDP_FRAG_SIZE + 1)
+
 /*
  * Maximum receive ring size; that is, the number of packets
  * we can buffer before overflow happens. Basically, this just
net/net.c +0 -0
@@ -192,6 +192,15 @@ volatile uchar	PktBuf[(PKTBUFSRX+1) * PK
 

net/net.c volatile uchar PktBuf[(PKTBUFSRX+1) * PK · line 202

Quoted 10 lines
net/net.c +7 -0
 volatile uchar *NetRxPackets[PKTBUFSRX]; /* Receive packets			*/
 
+/* Packet fragmentation support */
+static uint16_t ip_id = 0; /* sequence number */
+static uint16_t udp_len = 0;
+static uint16_t udp_src = 0;
+static uint16_t udp_dst = 0;
+static int max_idx = 0;
+static uchar NetFragBuf[NET_FRAG_BUF_SIZE];
 

Allocating a 16k buffer should be optional. Please consider wrapping this feature in a CONFIG.

Quoted 6 lines
net/net.c +2 -0
+static char NetFragBufUsed[NET_FRAG_BUF_USED] = { 0, };
+
 static rxhand_f *packetHandler;		/* Current RX packet handler		*/
 static thand_f *timeHandler;		/* Current timeout handler		*/

static ulong timeStart; /* Time base value */

net/net.c NetLoop(proto_t protocol) · line 306

Quoted 10 lines
net/net.c +6 -0
@@ -288,6 +297,13 @@ NetLoop(proto_t protocol)
 {
 	bd_t *bd = gd->bd;
 
+	/* Packet fragmentation support */
+	ip_id = udp_len = udp_src = udp_dst = max_idx = 0;
+	memset(NetFragBuf, 0xFF, sizeof(NetFragBuf));
+	memset(NetFragBufUsed, 0, sizeof(NetFragBufUsed));
+	printf("NetFragBuf @ %08x max tftp_block_size=%d udp_frag_size=%d\n",
+	       NetFragBuf, TFTP_BLOCK_SIZE_MAX, NET_UDP_FRAG_SIZE);
 

I get a compiler warning here that a simple cast should fix:
net.c: In function 'NetLoop':
net.c:300: warning: format '%08x' expects type 'unsigned int', but argument 2 has type 'uchar *'

regards,
Ben