]> git.kernelconcepts.de Git - karo-tx-uboot.git/blobdiff - net/net.c
Merge branch 'master' of git://git.denx.de/u-boot-microblaze
[karo-tx-uboot.git] / net / net.c
index d1cc9b2e9b0592e0100084d2ec2b6ac02a853d5f..7d2220d48dec224529f2917734965d1d0e73d526 100644 (file)
--- a/net/net.c
+++ b/net/net.c
@@ -96,8 +96,6 @@
 #include "dns.h"
 #endif
 
-#if defined(CONFIG_CMD_NET)
-
 DECLARE_GLOBAL_DATA_PTR;
 
 #ifndef        CONFIG_ARP_TIMEOUT
@@ -197,6 +195,8 @@ volatile uchar *NetTxPacket = 0;    /* THE transmit packet                  */
 
 static int net_check_prereq (proto_t protocol);
 
+static int NetTryCount;
+
 /**********************************************************************/
 
 IPaddr_t       NetArpWaitPacketIP;
@@ -320,6 +320,7 @@ NetLoop(proto_t protocol)
        NetArpWaitReplyIP = 0;
        NetArpWaitTxPacket = NULL;
        NetTxPacket = NULL;
+       NetTryCount = 1;
 
        if (!NetTxPacket) {
                int     i;
@@ -558,17 +559,30 @@ startAgainHandler(uchar * pkt, unsigned dest, unsigned src, unsigned len)
 void NetStartAgain (void)
 {
        char *nretry;
-       int noretry = 0, once = 0;
-
-       if ((nretry = getenv ("netretry")) != NULL) {
-               noretry = (strcmp (nretry, "no") == 0);
-               once = (strcmp (nretry, "once") == 0);
-       }
-       if (noretry) {
-               eth_halt ();
+       int retry_forever = 0;
+       unsigned long retrycnt = 0;
+
+       nretry = getenv("netretry");
+       if (nretry) {
+               if (!strcmp(nretry, "yes"))
+                       retry_forever = 1;
+               else if (!strcmp(nretry, "no"))
+                       retrycnt = 0;
+               else if (!strcmp(nretry, "once"))
+                       retrycnt = 1;
+               else
+                       retrycnt = simple_strtoul(nretry, NULL, 0);
+       } else
+               retry_forever = 1;
+
+       if ((!retry_forever) && (NetTryCount >= retrycnt)) {
+               eth_halt();
                NetState = NETLOOP_FAIL;
                return;
        }
+
+       NetTryCount++;
+
 #ifndef CONFIG_NET_MULTI
        NetSetTimeout (10000UL, startAgainTimeout);
        NetSetHandler (startAgainHandler);
@@ -580,7 +594,7 @@ void NetStartAgain (void)
        eth_init (gd->bd);
        if (NetRestartWrap) {
                NetRestartWrap = 0;
-               if (NetDevExists && !once) {
+               if (NetDevExists) {
                        NetSetTimeout (10000UL, startAgainTimeout);
                        NetSetHandler (startAgainHandler);
                } else {
@@ -1107,6 +1121,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 +1517,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 +1530,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
@@ -1366,6 +1551,13 @@ NetReceive(volatile uchar * inpkt, int len)
 #endif
                        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
                 *
@@ -1678,8 +1870,6 @@ void copy_filename (char *dst, char *src, int size)
        *dst = '\0';
 }
 
-#endif
-
 #if defined(CONFIG_CMD_NFS) || defined(CONFIG_CMD_SNTP) || defined(CONFIG_CMD_DNS)
 /*
  * make port a little random, but use something trivial to compute
@@ -1700,27 +1890,6 @@ void ip_to_string (IPaddr_t x, char *s)
        );
 }
 
-IPaddr_t string_to_ip(char *s)
-{
-       IPaddr_t addr;
-       char *e;
-       int i;
-
-       if (s == NULL)
-               return(0);
-
-       for (addr=0, i=0; i<4; ++i) {
-               ulong val = s ? simple_strtoul(s, &e, 10) : 0;
-               addr <<= 8;
-               addr |= (val & 0xFF);
-               if (s) {
-                       s = (*e) ? e+1 : e;
-               }
-       }
-
-       return (htonl(addr));
-}
-
 void VLAN_to_string(ushort x, char *s)
 {
        x = ntohs(x);
@@ -1749,11 +1918,6 @@ ushort string_to_VLAN(char *s)
        return htons(id);
 }
 
-IPaddr_t getenv_IPaddr (char *var)
-{
-       return (string_to_ip(getenv(var)));
-}
-
 ushort getenv_VLAN(char *var)
 {
        return (string_to_VLAN(getenv(var)));