net: tftpput: Add support for receiving ICMP packets
authorSimon Glass <sjg@chromium.org>
Mon, 24 Oct 2011 18:00:01 +0000 (18:00 +0000)
committerWolfgang Denk <wd@denx.de>
Wed, 26 Oct 2011 19:33:25 +0000 (21:33 +0200)
ICMP packets can tell you when there is no server at the other end. It
is useful for tftp to figure this out, so that a quick error can be
displayed, rather than pointlessly retrying.

This adds an ICMP packet handler to the net interface.

Signed-off-by: Simon Glass <sjg@chromium.org>
include/net.h
net/net.c

index d5d37b6506b05989f37d31cb08676edbab47fda1..d7ff0687ccacc49812fc58b7299e219c1cf26785 100644 (file)
@@ -48,6 +48,19 @@ typedef void rxhand_f(uchar *pkt, unsigned dport,
                      IPaddr_t sip, unsigned sport,
                      unsigned len);
 
+/**
+ * An incoming ICMP packet handler.
+ * @param type ICMP type
+ * @param code ICMP code
+ * @param dport        destination UDP port
+ * @param sip  source IP address
+ * @param sport        source UDP port
+ * @param pkt  pointer to the ICMP packet data
+ * @param len  packet length
+ */
+typedef void rxhand_icmp_f(unsigned type, unsigned code, unsigned dport,
+               IPaddr_t sip, unsigned sport, uchar *pkt, unsigned len);
+
 /*
  *     A timeout handler.  Called after time interval has expired.
  */
@@ -244,6 +257,7 @@ typedef struct
  * ICMP stuff (just enough to handle (host) redirect messages)
  */
 #define ICMP_ECHO_REPLY                0       /* Echo reply                   */
+#define ICMP_NOT_REACH         3       /* Detination unreachable       */
 #define ICMP_REDIRECT          5       /* Redirect (change route)      */
 #define ICMP_ECHO_REQUEST      8       /* Echo request                 */
 
@@ -251,6 +265,9 @@ typedef struct
 #define ICMP_REDIR_NET         0       /* Redirect Net                 */
 #define ICMP_REDIR_HOST                1       /* Redirect Host                */
 
+/* Codes for NOT_REACH */
+#define ICMP_NOT_REACH_PORT    3       /* Port unreachable             */
+
 typedef struct icmphdr {
        uchar           type;
        uchar           code;
@@ -265,6 +282,7 @@ typedef struct icmphdr {
                        ushort  __unused;
                        ushort  mtu;
                } frag;
+               uchar data[0];
        } un;
 } ICMP_t;
 
@@ -397,6 +415,7 @@ extern uint NetCksum(uchar *, int);         /* Calculate the checksum       */
 
 /* Set callbacks */
 extern void    NetSetHandler(rxhand_f *);      /* Set RX packet handler        */
+extern void net_set_icmp_handler(rxhand_icmp_f *f); /* Set ICMP RX handler */
 extern void    NetSetTimeout(ulong, thand_f *);/* Set timeout handler          */
 
 /* Transmit "NetTxPacket" */
index 266b9538d8c916b67f660becf356fdd8742de5b9..f6afb6439624133e8a1d5125849d22ce0c0a5e16 100644 (file)
--- a/net/net.c
+++ b/net/net.c
@@ -215,6 +215,7 @@ volatile uchar *NetRxPackets[PKTBUFSRX];
 
 /* Current RX packet handler */
 static rxhand_f *packetHandler;
+static rxhand_icmp_f *packet_icmp_handler;     /* Current ICMP rx handler */
 /* Current timeout handler */
 static thand_f *timeHandler;
 /* Time base value */
@@ -344,6 +345,7 @@ int
 NetLoop(proto_t protocol)
 {
        bd_t *bd = gd->bd;
+       int ret = -1;
 
        NetRestarted = 0;
        NetDevExists = 0;
@@ -512,7 +514,7 @@ restart:
                if (ctrlc()) {
                        eth_halt();
                        puts("\nAbort\n");
-                       return -1;
+                       goto done;
                }
 
                ArpTimeoutCheck();
@@ -564,12 +566,19 @@ restart:
                                setenv("fileaddr", buf);
                        }
                        eth_halt();
-                       return NetBootFileXferSize;
+                       ret = NetBootFileXferSize;
+                       goto done;
 
                case NETLOOP_FAIL:
-                       return -1;
+                       goto done;
                }
        }
+
+done:
+       /* Clear out the handlers */
+       NetSetHandler(NULL);
+       net_set_icmp_handler(NULL);
+       return ret;
 }
 
 /**********************************************************************/
@@ -643,6 +652,10 @@ NetSetHandler(rxhand_f *f)
        packetHandler = f;
 }
 
+void net_set_icmp_handler(rxhand_icmp_f *f)
+{
+       packet_icmp_handler = f;
+}
 
 void
 NetSetTimeout(ulong iv, thand_f *f)
@@ -1383,6 +1396,10 @@ static void receive_icmp(IP_t *ip, int len, IPaddr_t src_ip, Ethernet_t *et)
                break;
 #endif
        default:
+               if (packet_icmp_handler)
+                       packet_icmp_handler(icmph->type, icmph->code,
+                               ntohs(ip->udp_dst), src_ip, ntohs(ip->udp_src),
+                               icmph->un.data, ntohs(ip->udp_len));
                break;
        }
 }
@@ -1671,6 +1688,10 @@ NetReceive(volatile uchar *inpkt, int len)
                 * subnet. So this is probably a warning that your
                 * configuration might be wrong. But I'm not really
                 * sure if there aren't any other situations.
+                *
+                * Simon Glass <sjg@chromium.org>: We get an ICMP when
+                * we send a tftp packet to a dead connection, or when
+                * there is no server at the other end.
                 */
                if (ip->ip_p == IPPROTO_ICMP) {
                        receive_icmp(ip, len, src_ip, et);