pxe/tftpboot work - Allow multiple responses to broadcast query
authorMatthew Dillon <dillon@apollo.backplane.com>
Sun, 15 Feb 2009 12:04:44 +0000 (04:04 -0800)
committerMatthew Dillon <dillon@apollo.backplane.com>
Sun, 15 Feb 2009 12:04:44 +0000 (04:04 -0800)
The PXE boot loader may use a broadcast address to query for files via
TFTP, resulting in multiple responses.  Accept the first DATA response
and keep track of the last ERROR response if no DATA response is received.

The first DATA packet locks the server IP for the tftp transfer.

Bring in some structure options (__packed, etc) from FreeBSD plus a fix
to an address increment in the code.

lib/libstand/tftp.c
sys/boot/pc32/libi386/pxe.c

index 98f66a2..550ee33 100644 (file)
@@ -49,6 +49,7 @@
 #include <sys/stat.h>
 #include <netinet/in.h>
 #include <netinet/udp.h>
+#include <netinet/ip.h>
 #include <netinet/in_systm.h>
 #include <arpa/tftp.h>
 
@@ -95,7 +96,7 @@ struct tftp_handle {
                u_char header[HEADER_SIZE];
                struct tftphdr t;
                u_char space[RSPACE];
-       } lastdata;
+       } __packed __aligned(4) lastdata;
 };
 
 static int tftperrors[8] = {
@@ -110,18 +111,38 @@ static int tftperrors[8] = {
 };
 
 static ssize_t 
-recvtftp(struct iodesc *d, void *pkt, size_t len, time_t tleft)
+recvtftp(struct iodesc *d, void *pkt, size_t max_len, time_t tleft)
 {
        struct tftphdr *t;
-
+       ssize_t len;
+       ssize_t tmp_len;
+
+       /*
+        * Note: errno of 0 with -1 return means udp poll failed or
+        * packet was not for us.
+        *
+        * We may end up broadcasting the initial TFTP request.  Take the
+        * first DATA result and save any ERROR result in case we do not
+        * get a DATA.
+        */
        errno = 0;
-
-       len = readudp(d, pkt, len, tleft);
-
-       if (len < 4)
+       bzero(pkt, len);
+       if (d->xid == 1) {
+               len = -1;
+               while ((tmp_len = readudp(d, pkt, max_len, tleft)) > 0) {
+                       len = tmp_len;
+                       t = (struct tftphdr *)pkt;
+                       if (ntohs(t->th_opcode) == DATA)
+                               break;
+               }
+       } else {
+               len = readudp(d, pkt, max_len, tleft);
+       }
+       if ((int)len < (int)sizeof(*t))
                return (-1);
+       t = (struct tftphdr *)pkt;
+       errno = 0;
 
-       t = (struct tftphdr *) pkt;
        switch (ntohs(t->th_opcode)) {
        case DATA: {
                int got;
@@ -134,13 +155,19 @@ recvtftp(struct iodesc *d, void *pkt, size_t len, time_t tleft)
                }
                if (d->xid == 1) {
                        /*
-                        * First data packet from new port.
+                        * First data packet from new port.  Set destip in
+                        * case we got replies from multiple hosts, so only
+                        * one host is selected.
                         */
                        struct udphdr *uh;
+                       struct ip *ip;
+
                        uh = (struct udphdr *) pkt - 1;
+                       ip = (struct ip *)uh - 1;
                        d->destport = uh->uh_sport;
+                       d->destip = ip->ip_src;
                } /* else check uh_sport has not changed??? */
-               got = len - (t->th_data - (char *) t);
+               got = len - (t->th_data - (char *)t);
                return got;
        }
        case ERROR:
@@ -170,7 +197,7 @@ tftp_makereq(struct tftp_handle *h)
                u_char header[HEADER_SIZE];
                struct tftphdr  t;
                u_char space[FNAME_SIZE + 6];
-       } wbuf;
+       } __packed __aligned(4) wbuf;
        char           *wtail;
        int             l;
        ssize_t         res;
@@ -212,11 +239,14 @@ tftp_getnextblock(struct tftp_handle *h)
        struct {
                u_char header[HEADER_SIZE];
                struct tftphdr t;
-       } wbuf;
+       } __packed __aligned(4) wbuf;
        char           *wtail;
        int             res;
        struct tftphdr *t;
 
+       /*
+        * Ack previous block
+        */
        wbuf.t.th_opcode = htons((u_short) ACK);
        wtail = (char *) &wbuf.t.th_block;
        wbuf.t.th_block = htons((u_short) h->currblock);
@@ -325,9 +355,9 @@ tftp_read(struct open_file *f, void *addr, size_t size, size_t *resid)
                        }
                        count = (size < inbuffer ? size : inbuffer);
                        bcopy(tftpfile->lastdata.t.th_data + offinblock,
-                           addr, count);
+                             addr, count);
 
-                       addr += count;
+                       addr = (char *)addr + count;
                        tftpfile->off += count;
                        size -= count;
 
index 89254e6..07cf1b5 100644 (file)
@@ -37,6 +37,7 @@
 #include <netinet/in_systm.h>
 #include <netinet/in.h>
 #include <netinet/udp.h>
+#include <netinet/ip.h>
 
 #include <net.h>
 #include <netif.h>
@@ -597,9 +598,11 @@ ssize_t
 readudp(struct iodesc *h, void *pkt, size_t len, time_t timeout)
 {
        t_PXENV_UDP_READ *udpread_p = (t_PXENV_UDP_READ *)scratch_buffer;
-       struct udphdr *uh = NULL;
+       struct udphdr *uh;
+       struct ip *ip;
        
        uh = (struct udphdr *) pkt - 1;
+       ip = (struct ip *)uh - 1;
        bzero(udpread_p, sizeof(*udpread_p));
        
        udpread_p->dest_ip        = h->myip.s_addr;
@@ -622,5 +625,6 @@ readudp(struct iodesc *h, void *pkt, size_t len, time_t timeout)
        }
        bcopy(data_buffer, pkt, udpread_p->buffer_size);
        uh->uh_sport = udpread_p->s_port;
+       ip->ip_src.s_addr = udpread_p->src_ip;
        return udpread_p->buffer_size;
 }