/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * 9boot - load next kernel via pxe (bootp, tftp) and start it * * intel says that pxe can only load into the bottom 640K, * and intel's boot agent takes 128K, leaving only 512K for 9boot. * * some of this code is from the old 9load's bootp.c. */#include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "io.h"#include "ureg.h"#include "pool.h"#include "../port/netif.h"#include "etherif.h"#include "../ip/ip.h"#include "pxe.h" #define TFTPDEF "135.104.9.6" /* IP of default tftp server */ enum { Tftpusehdrs = 0, /* flag: use announce+headers for tftp? */ Debug = 0, Tftphdrsz = 4, /* * this can be bigger than the ether mtu and * will work due to ip fragmentation, at least on v4. */ Prefsegsize = 1400, Maxsegsize = 2048, Bufsz = Maxsegsize + 2, Ok = 0, Err = -1, Nonexist = -2,}; typedef struct Ethaddr Ethaddr;typedef struct Kernname Kernname;typedef struct Openeth Openeth;typedef struct Tftp Tftp; struct Tftp { uchar header[Tftphdrsz]; uchar data[Maxsegsize];}; struct Kernname { char *edev; char *bootfile;}; struct Openeth { /* names */ int ctlrno; char ethname[16]; /* ether%d */ char netethname[32]; /* /net/ether%d */ char filename[128]; /* from bootp, for tftp */ Chan *ifcctl; /* /net/ipifc/clone */ Chan *ethctl; /* /net/etherN/0/ctl, for promiscuous mode */ /* udp connection */ Chan *udpctl; Chan *udpdata; Pxenetaddr *netaddr; int rxactive;}; struct Ethaddr { /* communication with sleep procs */ Openeth *oe; Pxenetaddr *a;}; static char ethernm[] = "ether";static uchar myea[Eaddrlen];static Pxenetaddr myaddr; /* actually, local ip addr & port */ /* * there can be at most one concurrent tftp session until we move these * variables into Openeth or some other struct (Tftpstate). */static ushort tftpport;static int tftpblockno;static int tftpphase;static int progress;static int segsize;static Tftp *tftpb;static Pxenetaddr tftpserv; /* actually, remote ip addr & port */static Pxenetaddr bootpserv; static int tftpconnect(Openeth *, Bootp *); uchar *etheraddr(Openeth *oe){ int n; char name[32], buf[32]; static uchar ea[Eaddrlen]; memset(ea, 0, sizeof ea); snprint(name, sizeof name, "#l%d/ether%d/addr", oe->ctlrno, oe->ctlrno); n = readfile(name, buf, sizeof buf - 1); if (n < 0) return ea; buf[n] = '\0'; parseether(ea, buf); return ea;} static voidudpsend(Openeth *oe, Pxenetaddr *a, void *data, int dlen){ int n; uchar *buf; Chan *c; Etherpkt pkt; Udphdr *uh; buf = data; if (dlen > sizeof pkt) panic("udpsend: packet too big"); oe->netaddr = a; /* * add Plan 9 UDP pseudo-headers */ if (!tftpphase || Tftpusehdrs) { memset(&pkt, 0, sizeof pkt); uh = (Udphdr*)&pkt; memmove(uh + 1, data, dlen); USED(buf); buf = (uchar *)uh; dlen += sizeof *uh; if (dlen > sizeof pkt) panic("udpsend: packet too big"); ipmove(uh->laddr, myaddr.ip); hnputs(uh->lport, myaddr.port); ipmove(uh->raddr, a->ip); hnputs(uh->rport, a->port); if(Debug) print("udpsend %I!%d -> %I!%d ", uh->laddr, nhgets(uh->lport), uh->raddr, nhgets(uh->rport)); } if (waserror()) { iprint("udp write error\n"); return; /* send another req later */ } c = oe->udpdata; assert(oe->udpdata != nil); n = devtab[c->type]->write(c, buf, dlen, c->offset); poperror(); c->offset += n; if (n != dlen) print("udpsend: wrote %d/%d\n", n, dlen); else if (progress) print(".");} static voidnak(Openeth *oe, Pxenetaddr *a, int code, char *msg, int report){ char buf[4 + 32]; buf[0] = 0; buf[1] = Tftp_ERROR; buf[2] = 0; buf[3] = code; strncpy(buf+4, msg, sizeof buf - 4 - 1); udpsend(oe, a, buf, 4 + strlen(buf+4) + 1); if(report) print("\ntftp: error(%d): %s\n", code, msg);} /* a is the source address we're looking for */static inttuplematch(Pxenetaddr *a, Udphdr *h){ int port; uchar *ip; if (tftpphase && !Tftpusehdrs) return 1; /* * we're using udp headers mode, because we're still doing bootp, * or we are doing tftp and we chose to use headers mode. */ port = a->port; ip = a->ip; /* * we're accepting any src port or it's from the port we want, and * it's from the ip we want or we sent to a broadcast address, and * it's for us or it's a broadcast. */ return (port == 0 || nhgets(h->rport) == port) && (equivip6(h->raddr, ip) || equivip6(ip, IPv4bcast)) && (equivip6(h->laddr, myaddr.ip) || equivip6(h->laddr, IPv4bcast));} /* extract UDP payload into data and set a */static intudppayload(Udphdr *h, int len, Pxenetaddr *a, uchar *data, int dlen){ if(Debug) print("udprecv %I!%d to %I!%d...\n", h->raddr, nhgets(h->rport), h->laddr, nhgets(h->lport)); if(a->port != 0 && nhgets(h->rport) != a->port) { if(Debug) print("udpport %ux not %ux\n", nhgets(h->rport), a->port); return -1; } if(!equivip6(a->ip, IPv4bcast) && !equivip6(a->ip, h->raddr)) { if(Debug) print("bad ip %I not %I\n", h->raddr, a->ip); return -1; } len -= sizeof *h; /* don't count pseudo-headers */ if(len > dlen) { print("udp packet too big: %d > %d; from addr %I\n", len, dlen, h->raddr); return -1; } memmove(data, h + 1, len); /* skip pseudo-headers */ /* set a from remote address */ ipmove(a->ip, h->raddr); a->port = nhgets(h->rport); return len;} static intchanlen(Chan *ch){ int len; Dir *dp; dp = dirchstat(ch); if (dp == nil) return -1; len = dp->length; /* qlen(cv->rq) in devip */ free(dp); return len;} static intudprecv(Openeth *oe, Pxenetaddr *a, void *data, int dlen){ int len, buflen, chlen; ulong timo, now; char *buf; Chan *c; Etherpkt pkt; oe->netaddr = a; /* timo is frequency of tftp ack and broadcast bootp retransmission */ if(oe->rxactive == 0) timo = 1000; else timo = Timeout; now = TK2MS(m->ticks); timo += now; /* deadline */ c = oe->udpdata; spllo(); /* paranoia */ do { /* * wait for data to arrive or time-out. * alarms only work for user procs, so we poll to avoid getting * stuck in ipread. */ for (chlen = chanlen(c); chlen == 0 && now < timo; chlen = chanlen(c)) { /* briefly give somebody else a chance to run */ tsleep(&up->sleep, return0, 0, 0); now = TK2MS(m->ticks); } if (chlen <= 0) { print("T"); return -1; /* timed out */ } while (waserror()) { print("read err: %s\n", up->errstr); tsleep(&up->sleep, return0, 0, 1000); } /* * using Plan 9 UDP pseudo-headers? */ if (tftpphase && !Tftpusehdrs) { buf = data; /* read directly in caller's buffer */ buflen = dlen; } else { buf = (char *)&pkt; /* read pkt with hdrs */ buflen = sizeof pkt; } /* devtab[c->type]->read calls ipread */ len = devtab[c->type]->read(c, buf, buflen, c->offset); poperror(); if (len <= 0) return len; c->offset += len; } while (!tuplematch(oe->netaddr, (Udphdr *)buf)); /* * using Plan 9 UDP pseudo-headers? extract payload into caller's buf. */ if (!tftpphase || Tftpusehdrs) len = udppayload((Udphdr *)&pkt, len, a, data, dlen); if (len >= 0) oe->rxactive = 1; return len;} static voidack(Openeth *oe, Pxenetaddr *a, int blkno){ char buf[4]; buf[0] = 0; buf[1] = Tftp_ACK; buf[2] = blkno>>8; buf[3] = blkno; udpsend(oe, a, buf, sizeof buf);} static char *skipwd(char *wd){ while (*wd != '\0') wd++; return wd + 1; /* skip terminating NUL */} static intoptval(char *opt, char *pkt, int len){ char *wd, *ep, *p; ep = pkt + len; for (p = pkt; p < ep && *p != '\0'; p = skipwd(wd)) { wd = skipwd(p); if (cistrcmp(p, opt) == 0) return strtol(wd, 0, 10); } return -1;} /* * send a tftp read request to `a' for name. if we get a data packet back, * ack it and stash it in tftp for later. * * format of a request packet, from the RFC: * * 2 bytes string 1 byte string 1 byte * ------------------------------------------------ * | Opcode | Filename | 0 | Mode | 0 | * ------------------------------------------------ */static inttftpread1st(Openeth *oe, Pxenetaddr *a, char *name, Tftp *tftp){ int i, n, len, rlen, oport, sendack; static char *buf; if (buf == nil) buf = malloc(Bufsz); buf[0] = 0; buf[1] = Tftp_READ; len = 2 + snprint(buf+2, Bufsz - 2, "%s", name) + 1; len += snprint(buf+len, Bufsz - len, "octet") + 1; len += snprint(buf+len, Bufsz - len, "blksize") + 1; /* option */ len += snprint(buf+len, Bufsz - len, "%d", Prefsegsize) + 1; /* * keep sending the same packet until we get an answer. */ if (Debug) print("tftpread1st %s\n", name); oe->netaddr = a; /* * the first packet or two sent seem to get dropped, * so use a shorter time-out on the first packet. */ oe->rxactive = 0; oport = a->port; tftpblockno = 0; segsize = Defsegsize; sendack = 0; for(i = 0; i < 10; i++){ a->port = oport; if (sendack) ack(oe, a, tftpblockno); else udpsend(oe, a, buf, len); /* tftp read name */ if((rlen = udprecv(oe, a, tftp, sizeof(Tftp))) < Tftphdrsz) continue; /* runt or time-out */ switch((tftp->header[0]<<8)|tftp->header[1]){ case Tftp_ERROR: if(strstr((char *)tftp->data, "does not exist") != nil){ print("%s\n", (char*)tftp->data); return Nonexist; } print("tftpread1st: error (%d): %s\n", (tftp->header[2]<<8)|tftp->header[3], (char*)tftp->data); return Err; case Tftp_OACK: n = optval("blksize", (char *)tftp->header+2, rlen-2); if (n <= 0) { nak(oe, a, 0, "bad blksize option value", 0); return Err; } segsize = n; /* no bytes stashed in tftp.data */ i = 0; sendack = 1; break; case Tftp_DATA: tftpblockno = 1; len = (tftp->header[2]<<8)|tftp->header[3]; if(len != tftpblockno){ print("tftpread1st: block error: %d\n", len); nak(oe, a, 1, "block error", 0); return Err; } rlen -= Tftphdrsz; if(rlen < segsize) /* ACK now, in case we don't later */ ack(oe, a, tftpblockno); return rlen; default: print("tftpread1st: unexpected pkt type recv'd\n"); nak(oe, a, 0, "unexpected pkt type recv'd", 0); return Err; } } print("tftpread1st: failed to connect to server (%I!%d)\n", a->ip, oport); return Err;} static inttftpread(Openeth *oe, Pxenetaddr *a, Tftp *tftp, int dlen){ int try, blockno, len; dlen += Tftphdrsz; /* * keep sending ACKs until we get an answer. */ for(try = 0; try < 10; try++) { ack(oe, a, tftpblockno); len = udprecv(oe, a, tftp, dlen); /* * NB: not `<='; just a header is legal and happens when * file being read is a multiple of segsize bytes long. */ if(len < Tftphdrsz){ if(Debug) print("tftpread: too short %d <= %d\n", len, Tftphdrsz); continue; } switch((tftp->header[0]<<8)|tftp->header[1]){ case Tftp_ERROR: print("tftpread: error (blk %d): %s\n", (tftp->header[2]<<8)|tftp->header[3], (char*)tftp->data); nak(oe, a, 0, "error pkt recv'd", 0); return -1; case Tftp_OACK: print("tftpread: oack pkt recv'd too late\n"); nak(oe, a, 0, "oack pkt recv'd too late", 0); return -1;Lines 1–500 of 1308. Load more
/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * 9boot - load next kernel via pxe (bootp, tftp) and start it * * intel says that pxe can only load into the bottom 640K, * and intel's boot agent takes 128K, leaving only 512K for 9boot. * * some of this code is from the old 9load's bootp.c. */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "ureg.h" #include "pool.h" #include "../port/netif.h" #include "etherif.h" #include "../ip/ip.h" #include "pxe.h" #define TFTPDEF "135.104.9.6" /* IP of default tftp server */ enum { Tftpusehdrs = 0, /* flag: use announce+headers for tftp? */ Debug = 0, Tftphdrsz = 4, /* * this can be bigger than the ether mtu and * will work due to ip fragmentation, at least on v4. */ Prefsegsize = 1400, Maxsegsize = 2048, Bufsz = Maxsegsize + 2, Ok = 0, Err = -1, Nonexist = -2, }; typedef struct Ethaddr Ethaddr; typedef struct Kernname Kernname; typedef struct Openeth Openeth; typedef struct Tftp Tftp; struct Tftp { uchar header[Tftphdrsz]; uchar data[Maxsegsize]; }; struct Kernname { char *edev; char *bootfile; }; struct Openeth { /* names */ int ctlrno; char ethname[16]; /* ether%d */ char netethname[32]; /* /net/ether%d */ char filename[128]; /* from bootp, for tftp */ Chan *ifcctl; /* /net/ipifc/clone */ Chan *ethctl; /* /net/etherN/0/ctl, for promiscuous mode */ /* udp connection */ Chan *udpctl; Chan *udpdata; Pxenetaddr *netaddr; int rxactive; }; struct Ethaddr { /* communication with sleep procs */ Openeth *oe; Pxenetaddr *a; }; static char ethernm[] = "ether"; static uchar myea[Eaddrlen]; static Pxenetaddr myaddr; /* actually, local ip addr & port */ /* * there can be at most one concurrent tftp session until we move these * variables into Openeth or some other struct (Tftpstate). */ static ushort tftpport; static int tftpblockno; static int tftpphase; static int progress; static int segsize; static Tftp *tftpb; static Pxenetaddr tftpserv; /* actually, remote ip addr & port */ static Pxenetaddr bootpserv; static int tftpconnect(Openeth *, Bootp *); uchar * etheraddr(Openeth *oe) { int n; char name[32], buf[32]; static uchar ea[Eaddrlen]; memset(ea, 0, sizeof ea); snprint(name, sizeof name, "#l%d/ether%d/addr", oe->ctlrno, oe->ctlrno); n = readfile(name, buf, sizeof buf - 1); if (n < 0) return ea; buf[n] = '\0'; parseether(ea, buf); return ea; } static void udpsend(Openeth *oe, Pxenetaddr *a, void *data, int dlen) { int n; uchar *buf; Chan *c; Etherpkt pkt; Udphdr *uh; buf = data; if (dlen > sizeof pkt) panic("udpsend: packet too big"); oe->netaddr = a; /* * add Plan 9 UDP pseudo-headers */ if (!tftpphase || Tftpusehdrs) { memset(&pkt, 0, sizeof pkt); uh = (Udphdr*)&pkt; memmove(uh + 1, data, dlen); USED(buf); buf = (uchar *)uh; dlen += sizeof *uh; if (dlen > sizeof pkt) panic("udpsend: packet too big"); ipmove(uh->laddr, myaddr.ip); hnputs(uh->lport, myaddr.port); ipmove(uh->raddr, a->ip); hnputs(uh->rport, a->port); if(Debug) print("udpsend %I!%d -> %I!%d ", uh->laddr, nhgets(uh->lport), uh->raddr, nhgets(uh->rport)); } if (waserror()) { iprint("udp write error\n"); return; /* send another req later */ } c = oe->udpdata; assert(oe->udpdata != nil); n = devtab[c->type]->write(c, buf, dlen, c->offset); poperror(); c->offset += n; if (n != dlen) print("udpsend: wrote %d/%d\n", n, dlen); else if (progress) print("."); } static void nak(Openeth *oe, Pxenetaddr *a, int code, char *msg, int report) { char buf[4 + 32]; buf[0] = 0; buf[1] = Tftp_ERROR; buf[2] = 0; buf[3] = code; strncpy(buf+4, msg, sizeof buf - 4 - 1); udpsend(oe, a, buf, 4 + strlen(buf+4) + 1); if(report) print("\ntftp: error(%d): %s\n", code, msg); } /* a is the source address we're looking for */ static int tuplematch(Pxenetaddr *a, Udphdr *h) { int port; uchar *ip; if (tftpphase && !Tftpusehdrs) return 1; /* * we're using udp headers mode, because we're still doing bootp, * or we are doing tftp and we chose to use headers mode. */ port = a->port; ip = a->ip; /* * we're accepting any src port or it's from the port we want, and * it's from the ip we want or we sent to a broadcast address, and * it's for us or it's a broadcast. */ return (port == 0 || nhgets(h->rport) == port) && (equivip6(h->raddr, ip) || equivip6(ip, IPv4bcast)) && (equivip6(h->laddr, myaddr.ip) || equivip6(h->laddr, IPv4bcast)); } /* extract UDP payload into data and set a */ static int udppayload(Udphdr *h, int len, Pxenetaddr *a, uchar *data, int dlen) { if(Debug) print("udprecv %I!%d to %I!%d...\n", h->raddr, nhgets(h->rport), h->laddr, nhgets(h->lport)); if(a->port != 0 && nhgets(h->rport) != a->port) { if(Debug) print("udpport %ux not %ux\n", nhgets(h->rport), a->port); return -1; } if(!equivip6(a->ip, IPv4bcast) && !equivip6(a->ip, h->raddr)) { if(Debug) print("bad ip %I not %I\n", h->raddr, a->ip); return -1; } len -= sizeof *h; /* don't count pseudo-headers */ if(len > dlen) { print("udp packet too big: %d > %d; from addr %I\n", len, dlen, h->raddr); return -1; } memmove(data, h + 1, len); /* skip pseudo-headers */ /* set a from remote address */ ipmove(a->ip, h->raddr); a->port = nhgets(h->rport); return len; } static int chanlen(Chan *ch) { int len; Dir *dp; dp = dirchstat(ch); if (dp == nil) return -1; len = dp->length; /* qlen(cv->rq) in devip */ free(dp); return len; } static int udprecv(Openeth *oe, Pxenetaddr *a, void *data, int dlen) { int len, buflen, chlen; ulong timo, now; char *buf; Chan *c; Etherpkt pkt; oe->netaddr = a; /* timo is frequency of tftp ack and broadcast bootp retransmission */ if(oe->rxactive == 0) timo = 1000; else timo = Timeout; now = TK2MS(m->ticks); timo += now; /* deadline */ c = oe->udpdata; spllo(); /* paranoia */ do { /* * wait for data to arrive or time-out. * alarms only work for user procs, so we poll to avoid getting * stuck in ipread. */ for (chlen = chanlen(c); chlen == 0 && now < timo; chlen = chanlen(c)) { /* briefly give somebody else a chance to run */ tsleep(&up->sleep, return0, 0, 0); now = TK2MS(m->ticks); } if (chlen <= 0) { print("T"); return -1; /* timed out */ } while (waserror()) { print("read err: %s\n", up->errstr); tsleep(&up->sleep, return0, 0, 1000); } /* * using Plan 9 UDP pseudo-headers? */ if (tftpphase && !Tftpusehdrs) { buf = data; /* read directly in caller's buffer */ buflen = dlen; } else { buf = (char *)&pkt; /* read pkt with hdrs */ buflen = sizeof pkt; } /* devtab[c->type]->read calls ipread */ len = devtab[c->type]->read(c, buf, buflen, c->offset); poperror(); if (len <= 0) return len; c->offset += len; } while (!tuplematch(oe->netaddr, (Udphdr *)buf)); /* * using Plan 9 UDP pseudo-headers? extract payload into caller's buf. */ if (!tftpphase || Tftpusehdrs) len = udppayload((Udphdr *)&pkt, len, a, data, dlen); if (len >= 0) oe->rxactive = 1; return len; } static void ack(Openeth *oe, Pxenetaddr *a, int blkno) { char buf[4]; buf[0] = 0; buf[1] = Tftp_ACK; buf[2] = blkno>>8; buf[3] = blkno; udpsend(oe, a, buf, sizeof buf); } static char * skipwd(char *wd) { while (*wd != '\0') wd++; return wd + 1; /* skip terminating NUL */ } static int optval(char *opt, char *pkt, int len) { char *wd, *ep, *p; ep = pkt + len; for (p = pkt; p < ep && *p != '\0'; p = skipwd(wd)) { wd = skipwd(p); if (cistrcmp(p, opt) == 0) return strtol(wd, 0, 10); } return -1; } /* * send a tftp read request to `a' for name. if we get a data packet back, * ack it and stash it in tftp for later. * * format of a request packet, from the RFC: * * 2 bytes string 1 byte string 1 byte * ------------------------------------------------ * | Opcode | Filename | 0 | Mode | 0 | * ------------------------------------------------ */ static int tftpread1st(Openeth *oe, Pxenetaddr *a, char *name, Tftp *tftp) { int i, n, len, rlen, oport, sendack; static char *buf; if (buf == nil) buf = malloc(Bufsz); buf[0] = 0; buf[1] = Tftp_READ; len = 2 + snprint(buf+2, Bufsz - 2, "%s", name) + 1; len += snprint(buf+len, Bufsz - len, "octet") + 1; len += snprint(buf+len, Bufsz - len, "blksize") + 1; /* option */ len += snprint(buf+len, Bufsz - len, "%d", Prefsegsize) + 1; /* * keep sending the same packet until we get an answer. */ if (Debug) print("tftpread1st %s\n", name); oe->netaddr = a; /* * the first packet or two sent seem to get dropped, * so use a shorter time-out on the first packet. */ oe->rxactive = 0; oport = a->port; tftpblockno = 0; segsize = Defsegsize; sendack = 0; for(i = 0; i < 10; i++){ a->port = oport; if (sendack) ack(oe, a, tftpblockno); else udpsend(oe, a, buf, len); /* tftp read name */ if((rlen = udprecv(oe, a, tftp, sizeof(Tftp))) < Tftphdrsz) continue; /* runt or time-out */ switch((tftp->header[0]<<8)|tftp->header[1]){ case Tftp_ERROR: if(strstr((char *)tftp->data, "does not exist") != nil){ print("%s\n", (char*)tftp->data); return Nonexist; } print("tftpread1st: error (%d): %s\n", (tftp->header[2]<<8)|tftp->header[3], (char*)tftp->data); return Err; case Tftp_OACK: n = optval("blksize", (char *)tftp->header+2, rlen-2); if (n <= 0) { nak(oe, a, 0, "bad blksize option value", 0); return Err; } segsize = n; /* no bytes stashed in tftp.data */ i = 0; sendack = 1; break; case Tftp_DATA: tftpblockno = 1; len = (tftp->header[2]<<8)|tftp->header[3]; if(len != tftpblockno){ print("tftpread1st: block error: %d\n", len); nak(oe, a, 1, "block error", 0); return Err; } rlen -= Tftphdrsz; if(rlen < segsize) /* ACK now, in case we don't later */ ack(oe, a, tftpblockno); return rlen; default: print("tftpread1st: unexpected pkt type recv'd\n"); nak(oe, a, 0, "unexpected pkt type recv'd", 0); return Err; } } print("tftpread1st: failed to connect to server (%I!%d)\n", a->ip, oport); return Err; } static int tftpread(Openeth *oe, Pxenetaddr *a, Tftp *tftp, int dlen) { int try, blockno, len; dlen += Tftphdrsz; /* * keep sending ACKs until we get an answer. */ for(try = 0; try < 10; try++) { ack(oe, a, tftpblockno); len = udprecv(oe, a, tftp, dlen); /* * NB: not `<='; just a header is legal and happens when * file being read is a multiple of segsize bytes long. */ if(len < Tftphdrsz){ if(Debug) print("tftpread: too short %d <= %d\n", len, Tftphdrsz); continue; } switch((tftp->header[0]<<8)|tftp->header[1]){ case Tftp_ERROR: print("tftpread: error (blk %d): %s\n", (tftp->header[2]<<8)|tftp->header[3], (char*)tftp->data); nak(oe, a, 0, "error pkt recv'd", 0); return -1; case Tftp_OACK: print("tftpread: oack pkt recv'd too late\n"); nak(oe, a, 0, "oack pkt recv'd too late", 0); return -1;