xref: /plan9-contrib/sys/src/boot/vt4/bootp.c (revision da917039c7f233c1a27d212bf012c6afa758af39)
1 #include "include.h"
2 #include "ip.h"
3 #include "fs.h"
4 
5 #define INIPATHLEN	64
6 
7 typedef struct Pxether Pxether;
8 static struct Pxether {
9 	Fs	fs;
10 	char	ini[INIPATHLEN];
11 } *pxether;
12 
13 extern int debug;
14 extern int debugload;
15 extern char *persist;
16 
17 uchar broadcast[Eaddrlen] = {
18 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
19 };
20 
21 static ushort tftpport = 5000;
22 static int Id = 1;
23 static int ctlrinuse;
24 static Netaddr myaddr;
25 static Netaddr server;
26 
27 typedef struct {
28 	uchar	header[4];
29 	uchar	data[Segsize];
30 } Tftp;
31 static Tftp *tftpbp;
32 
33 static void
hnputs(uchar * ptr,ushort val)34 hnputs(uchar *ptr, ushort val)
35 {
36 	ptr[0] = val>>8;
37 	ptr[1] = val;
38 }
39 
40 static void
hnputl(uchar * ptr,ulong val)41 hnputl(uchar *ptr, ulong val)
42 {
43 	ptr[0] = val>>24;
44 	ptr[1] = val>>16;
45 	ptr[2] = val>>8;
46 	ptr[3] = val;
47 }
48 
49 static ulong
nhgetl(uchar * ptr)50 nhgetl(uchar *ptr)
51 {
52 	return ((ptr[0]<<24) | (ptr[1]<<16) | (ptr[2]<<8) | ptr[3]);
53 }
54 
55 static ushort
nhgets(uchar * ptr)56 nhgets(uchar *ptr)
57 {
58 	return ((ptr[0]<<8) | ptr[1]);
59 }
60 
61 static	short	endian	= 1;
62 static	char*	aendian	= (char*)&endian;
63 #define	LITTLE	*aendian
64 
65 static ushort
ptcl_csum(void * a,int len)66 ptcl_csum(void *a, int len)
67 {
68 	uchar *addr;
69 	ulong t1, t2;
70 	ulong losum, hisum, mdsum, x;
71 
72 	addr = a;
73 	losum = 0;
74 	hisum = 0;
75 	mdsum = 0;
76 
77 	x = 0;
78 	if((ulong)addr & 1) {
79 		if(len) {
80 			hisum += addr[0];
81 			len--;
82 			addr++;
83 		}
84 		x = 1;
85 	}
86 	while(len >= 16) {
87 		t1 = *(ushort*)(addr+0);
88 		t2 = *(ushort*)(addr+2);	mdsum += t1;
89 		t1 = *(ushort*)(addr+4);	mdsum += t2;
90 		t2 = *(ushort*)(addr+6);	mdsum += t1;
91 		t1 = *(ushort*)(addr+8);	mdsum += t2;
92 		t2 = *(ushort*)(addr+10);	mdsum += t1;
93 		t1 = *(ushort*)(addr+12);	mdsum += t2;
94 		t2 = *(ushort*)(addr+14);	mdsum += t1;
95 		mdsum += t2;
96 		len -= 16;
97 		addr += 16;
98 	}
99 	while(len >= 2) {
100 		mdsum += *(ushort*)addr;
101 		len -= 2;
102 		addr += 2;
103 	}
104 	if(x) {
105 		if(len)
106 			losum += addr[0];
107 		if(LITTLE)
108 			losum += mdsum;
109 		else
110 			hisum += mdsum;
111 	} else {
112 		if(len)
113 			hisum += addr[0];
114 		if(LITTLE)
115 			hisum += mdsum;
116 		else
117 			losum += mdsum;
118 	}
119 
120 	losum += hisum >> 8;
121 	losum += (hisum & 0xff) << 8;
122 	while(hisum = losum>>16)
123 		losum = hisum + (losum & 0xffff);
124 
125 	return ~losum;
126 }
127 
128 static ushort
ip_csum(uchar * addr)129 ip_csum(uchar *addr)
130 {
131 	int len;
132 	ulong sum = 0;
133 
134 	len = (addr[0]&0xf)<<2;
135 
136 	while(len > 0) {
137 		sum += addr[0]<<8 | addr[1] ;
138 		len -= 2;
139 		addr += 2;
140 	}
141 
142 	sum = (sum & 0xffff) + (sum >> 16);
143 	sum = (sum & 0xffff) + (sum >> 16);
144 	return (sum^0xffff);
145 }
146 
147 static void
udpsend(int ctlrno,Netaddr * a,void * data,int dlen)148 udpsend(int ctlrno, Netaddr *a, void *data, int dlen)
149 {
150 	Udphdr *uh;
151 	Etherhdr *ip;
152 	Etherpkt pkt;
153 	int len, ptcllen;
154 
155 	uh = (Udphdr*)&pkt;
156 
157 	memset(uh, 0, sizeof(Etherpkt));
158 	memmove(uh->udpcksum+sizeof(uh->udpcksum), data, dlen);
159 
160 	/*
161 	 * UDP portion
162 	 */
163 	ptcllen = dlen + (UDP_HDRSIZE-UDP_PHDRSIZE);
164 	uh->ttl = 0;
165 	uh->udpproto = IP_UDPPROTO;
166 	uh->frag[0] = 0;
167 	uh->frag[1] = 0;
168 	hnputs(uh->udpplen, ptcllen);
169 	hnputl(uh->udpsrc, myaddr.ip);
170 	hnputs(uh->udpsport, myaddr.port);
171 	hnputl(uh->udpdst, a->ip);
172 	hnputs(uh->udpdport, a->port);
173 	hnputs(uh->udplen, ptcllen);
174 	uh->udpcksum[0] = 0;
175 	uh->udpcksum[1] = 0;
176 	dlen = (dlen+1)&~1;
177 	hnputs(uh->udpcksum, ptcl_csum(&uh->ttl, dlen+UDP_HDRSIZE));
178 
179 	/*
180 	 * IP portion
181 	 */
182 	ip = (Etherhdr*)&pkt;
183 	len = UDP_EHSIZE+UDP_HDRSIZE+dlen;		/* non-descriptive names */
184 	ip->vihl = IP_VER|IP_HLEN;
185 	ip->tos = 0;
186 	ip->ttl = 255;
187 	hnputs(ip->length, len-ETHER_HDR);
188 	hnputs(ip->id, Id++);
189 	ip->frag[0] = 0;
190 	ip->frag[1] = 0;
191 	ip->cksum[0] = 0;
192 	ip->cksum[1] = 0;
193 	hnputs(ip->cksum, ip_csum(&ip->vihl));
194 
195 	/*
196 	 * Ethernet MAC portion
197 	 */
198 	hnputs(ip->type, ET_IP);
199 	memmove(ip->d, a->ea, sizeof(ip->d));
200 
201 if(debug) {
202 	print("udpsend ");
203 }
204 	/*
205 	 * if packet is too short, make it longer rather than relying
206 	 * on ethernet interface or lower layers to pad it.
207 	 */
208 	if (len < ETHERMINTU)
209 		len = ETHERMINTU;
210 	ethertxpkt(ctlrno, &pkt, len, Timeout);
211 }
212 
213 static void
nak(int ctlrno,Netaddr * a,int code,char * msg,int report)214 nak(int ctlrno, Netaddr *a, int code, char *msg, int report)
215 {
216 	int n;
217 	char buf[128];
218 
219 	buf[0] = 0;
220 	buf[1] = Tftp_ERROR;
221 	buf[2] = 0;
222 	buf[3] = code;
223 	strecpy(buf+4, buf + sizeof buf, msg);
224 	n = strlen(msg) + 4 + 1;
225 	udpsend(ctlrno, a, buf, n);
226 	if(report)
227 		print("\ntftp: error(%d): %s\n", code, msg);
228 }
229 
230 void
dump(void * vaddr,int words)231 dump(void *vaddr, int words)
232 {
233 	ulong *addr;
234 
235 	addr = vaddr;
236 	while (words-- > 0)
237 		print("%.8lux%c", *addr++, words % 8 == 0? '\n': ' ');
238 }
239 
240 static int
udprecv(int ctlrno,Netaddr * a,void * data,int dlen)241 udprecv(int ctlrno, Netaddr *a, void *data, int dlen)
242 {
243 	int n, len;
244 	ushort csm;
245 	Udphdr *h;
246 	ulong addr, timo;
247 	Etherpkt pkt;
248 	static int rxactive;
249 
250 	if(rxactive == 0)
251 		timo = 1000;
252 	else
253 		timo = Timeout;
254 	timo += TK2MS(m->ticks);
255 	while(timo > TK2MS(m->ticks)){
256 		n = etherrxpkt(ctlrno, &pkt, timo - TK2MS(m->ticks));
257 		if(n <= 0)
258 			continue;
259 
260 		h = (Udphdr*)&pkt;
261 		if(debug)
262 			print("udprecv %E to %E...\n", h->s, h->d);
263 
264 		if(nhgets(h->type) != ET_IP) {
265 			if(debug)
266 				print("not ip...");
267 			continue;
268 		}
269 
270 		if(ip_csum(&h->vihl)) {
271 			print("ip chksum error\n");
272 			continue;
273 		}
274 		if(h->vihl != (IP_VER|IP_HLEN)) {
275 			print("ip bad vers/hlen\n");
276 			continue;
277 		}
278 
279 		if(h->udpproto != IP_UDPPROTO) {
280 			if(debug)
281 				print("not udp (%d)...", h->udpproto);
282 			continue;
283 		}
284 
285 		if(debug)
286 			print("okay udp...");
287 
288 		h->ttl = 0;
289 		len = nhgets(h->udplen);
290 		hnputs(h->udpplen, len);
291 
292 		if(nhgets(h->udpcksum)) {
293 			csm = ptcl_csum(&h->ttl, len+UDP_PHDRSIZE);
294 			if(csm != 0) {
295 				print("udp chksum error csum #%4ux len %d\n",
296 					csm, n);
297 				break;
298 			}
299 		}
300 
301 		if(a->port != 0 && nhgets(h->udpsport) != a->port) {
302 			if(debug)
303 				print("udpport %ux not %ux\n",
304 					nhgets(h->udpsport), a->port);
305 			continue;
306 		}
307 
308 		addr = nhgetl(h->udpsrc);
309 		if(a->ip != Bcastip && a->ip != addr) {
310 			if(debug)
311 				print("bad ip %lux not %lux\n", addr, a->ip);
312 			continue;
313 		}
314 
315 		len -= UDP_HDRSIZE-UDP_PHDRSIZE;
316 		if(len > dlen) {
317 			print("udp: packet too big: %d > %d; from addr %E\n",
318 				len, dlen, h->udpsrc);
319 			continue;
320 		}
321 
322 		memmove(data, h->udpcksum+sizeof(h->udpcksum), len);
323 		a->ip = addr;
324 		a->port = nhgets(h->udpsport);
325 		memmove(a->ea, pkt.s, sizeof(a->ea));
326 
327 		rxactive = 1;
328 		return len;
329 	}
330 
331 	return 0;
332 }
333 
334 static int tftpblockno;
335 
336 /*
337  * format of a request packet, from the RFC:
338  *
339             2 bytes     string    1 byte     string   1 byte
340             ------------------------------------------------
341            | Opcode |  Filename  |   0  |    Mode    |   0  |
342             ------------------------------------------------
343  */
344 static int
tftpopen(int ctlrno,Netaddr * a,char * name,Tftp * tftp,int op)345 tftpopen(int ctlrno, Netaddr *a, char *name, Tftp *tftp, int op)
346 {
347 	int i, len, rlen, oport;
348 	char *end;
349 	static char buf[Segsize+2];	/* reduce stack use */
350 
351 	ctlrinuse = ctlrno;
352 	buf[0] = 0;
353 	buf[1] = op;
354 	end = seprint(buf+2, buf + sizeof buf, "%s", name) + 1;
355 	end = seprint(end, buf + sizeof buf, "octet") + 1;
356 	len = end - buf;
357 
358 	oport = a->port;
359 	for(i = 0; i < 5; i++){
360 		a->port = oport;
361 		udpsend(ctlrno, a, buf, len);
362 		a->port = 0;
363 		if((rlen = udprecv(ctlrno, a, tftp, sizeof(Tftp))) < sizeof(tftp->header))
364 			continue;
365 
366 		switch((tftp->header[0]<<8)|tftp->header[1]){
367 		case Tftp_ERROR:
368 			print("tftpopen: error (%d): %s\n",
369 				(tftp->header[2]<<8)|tftp->header[3],
370 				(char*)tftp->data);
371 			return -1;
372 		case Tftp_DATA:
373 			/* this should only happen when opening to read */
374 			tftpblockno = 1;
375 			len = (tftp->header[2]<<8)|tftp->header[3];
376 			if(len != tftpblockno){
377 				print("tftpopen: block error: %d\n", len);
378 				nak(ctlrno, a, 1, "block error", 0);
379 				return -1;
380 			}
381 			rlen -= sizeof(tftp->header);
382 			if(rlen < Segsize){
383 				/* ACK last block now, in case we don't later */
384 				buf[0] = 0;
385 				buf[1] = Tftp_ACK;
386 				buf[2] = tftpblockno>>8;
387 				buf[3] = tftpblockno;
388 				udpsend(ctlrno, a, buf, sizeof(tftp->header));
389 			}
390 			return rlen;
391 		case Tftp_ACK:
392 			/* this should only happen when opening to write */
393 			len = (tftp->header[2]<<8)|tftp->header[3];
394 			if(len != 0){
395 				print("tftpopen: block # error: %d != 0\n",
396 					len);
397 				nak(ctlrno, a, 1, "block # error", 0);
398 				return -1;
399 			}
400 			return 0;
401 		}
402 	}
403 
404 	print("tftpopen: failed to connect to server\n");
405 	return -1;
406 }
407 
408 static int
tftpread(int ctlrno,Netaddr * a,Tftp * tftp,int dlen)409 tftpread(int ctlrno, Netaddr *a, Tftp *tftp, int dlen)
410 {
411 	uchar buf[4];
412 	int try, blockno, len;
413 
414 	dlen += sizeof(tftp->header);
415 
416 	for(try = 0; try < 10; try++) {
417 		buf[0] = 0;
418 		buf[1] = Tftp_ACK;
419 		buf[2] = tftpblockno>>8;
420 		buf[3] = tftpblockno;
421 
422 		udpsend(ctlrno, a, buf, sizeof(buf));
423 		len = udprecv(ctlrno, a, tftp, dlen);
424 		if(len <= sizeof(tftp->header)){
425 			if(debug)
426 				print("tftpread: too short %d <= %d\n",
427 					len, sizeof(tftp->header));
428 			continue;
429 		}
430 		blockno = (tftp->header[2]<<8)|tftp->header[3];
431 		if(blockno <= tftpblockno){
432 			if(debug)
433 				print("tftpread: blkno %d <= %d\n",
434 					blockno, tftpblockno);
435 			continue;
436 		}
437 
438 		if(blockno == tftpblockno+1) {
439 			tftpblockno++;
440 			if(len < dlen) {	/* last packet; send final ack */
441 				tftpblockno++;
442 				buf[0] = 0;
443 				buf[1] = Tftp_ACK;
444 				buf[2] = tftpblockno>>8;
445 				buf[3] = tftpblockno;
446 				udpsend(ctlrno, a, buf, sizeof(buf));
447 			}
448 			return len-sizeof(tftp->header);
449 		}
450 		print("tftpread: block error: %d, expected %d\n",
451 			blockno, tftpblockno+1);
452 	}
453 
454 	return -1;
455 }
456 
457 static int
bootpopen(int ctlrno,char * file,Bootp * rep,int dotftpopen)458 bootpopen(int ctlrno, char *file, Bootp *rep, int dotftpopen)
459 {
460 	Bootp req;
461 	int i, n;
462 	uchar *ea;
463 	char name[128], *filename, *sysname;
464 
465 	if (debugload)
466 		print("bootpopen: ether%d!%s...", ctlrno, file);
467 	if((ea = etheraddr(ctlrno)) == 0){
468 		print("invalid ctlrno %d\n", ctlrno);
469 		return -1;
470 	}
471 
472 	filename = 0;
473 	sysname = 0;
474 	if(file && *file){
475 		strecpy(name, name + sizeof name, file);
476 		if(filename = strchr(name, '!')){
477 			sysname = name;
478 			*filename++ = 0;
479 		}
480 		else
481 			filename = name;
482 	}
483 
484 	memset(&req, 0, sizeof(req));
485 	req.op = Bootrequest;
486 	req.htype = 1;			/* ethernet */
487 	req.hlen = Eaddrlen;		/* ethernet */
488 	memmove(req.chaddr, ea, Eaddrlen);
489 	if(filename != nil)
490 		strncpy(req.file, filename, sizeof(req.file));
491 	if(sysname != nil)
492 		strncpy(req.sname, sysname, sizeof(req.sname));
493 
494 	myaddr.ip = 0;
495 	myaddr.port = BPportsrc;
496 	memmove(myaddr.ea, ea, Eaddrlen);
497 
498 	etherrxflush(ctlrno);
499 	for(i = 0; i < 10; i++) {
500 		server.ip = Bcastip;
501 		server.port = BPportdst;
502 		memmove(server.ea, broadcast, sizeof(server.ea));
503 		udpsend(ctlrno, &server, &req, sizeof(req));
504 		if(udprecv(ctlrno, &server, rep, sizeof(*rep)) <= 0)
505 			continue;
506 		if(memcmp(req.chaddr, rep->chaddr, Eaddrlen))
507 			continue;
508 		if(rep->htype != 1 || rep->hlen != Eaddrlen)
509 			continue;
510 		if(sysname == 0 || strcmp(sysname, rep->sname) == 0)
511 			break;
512 	}
513 	if(i >= 10) {
514 		print("bootp on ether%d for %s timed out\n", ctlrno, file);
515 		return -1;
516 	}
517 
518 	if(!dotftpopen)
519 		return 0;
520 
521 	if(filename == 0 || *filename == 0){
522 		if(strcmp(rep->file, "/386/9pxeload") == 0)
523 			return -1;
524 		filename = rep->file;
525 	}
526 
527 	if(rep->sname[0] != '\0')
528 		 print("%s ", rep->sname);
529 	print("(%d.%d.%d.%d!%d): %s\n",
530 		rep->siaddr[0],
531 		rep->siaddr[1],
532 		rep->siaddr[2],
533 		rep->siaddr[3],
534 		server.port,
535 		filename);
536 
537 	myaddr.ip = nhgetl(rep->yiaddr);
538 	myaddr.port = tftpport++;
539 	server.ip = nhgetl(rep->siaddr);
540 	server.port = TFTPport;
541 
542 	if((n = tftpopen(ctlrno, &server, filename, tftpbp, Tftp_READ)) < 0)
543 		return -1;
544 
545 	return n;
546 }
547 
548 int
bootpboot(int ctlrno,char * file,Boot * b)549 bootpboot(int ctlrno, char *file, Boot *b)
550 {
551 	int n;
552 	Bootp rep;
553 
554 	if (tftpbp == nil)
555 		tftpbp = malloc(sizeof *tftpbp);
556 	if((n = bootpopen(ctlrno, file, &rep, 1)) < 0)
557 		return -1;
558 
559 	while(bootpass(b, tftpbp->data, n) == MORE){
560 		n = tftpread(ctlrno, &server, tftpbp, sizeof(tftpbp->data));
561 		if(n < sizeof(tftpbp->data))
562 			break;
563 	}
564 
565 	if(0 < n && n < sizeof(tftpbp->data))	/* got to end of file */
566 		bootpass(b, tftpbp->data, n);
567 	else
568 		nak(ctlrno, &server, 3, "ok", 0);	/* tftpclose to abort transfer */
569 	bootpass(b, nil, 0);	/* boot if possible */
570 	return -1;
571 }
572 
573 static vlong
pxediskseek(Fs *,vlong)574 pxediskseek(Fs*, vlong)
575 {
576 	return -1;
577 }
578 
579 static long
pxediskread(Fs *,void *,long)580 pxediskread(Fs*, void*, long)
581 {
582 	return -1;
583 }
584 
585 static long
pxeread(File * f,void * va,long len)586 pxeread(File* f, void* va, long len)
587 {
588 	int n;
589 	Bootp rep;
590 	char *p, *v;
591 
592 	if (pxether == nil)
593 		pxether = malloc(MaxEther * sizeof *pxether);
594 	if((n = bootpopen(f->fs->dev, pxether[f->fs->dev].ini, &rep, 1)) < 0)
595 		return -1;
596 
597 	p = v = va;
598 	while(n > 0) {
599 		if((p-v)+n > len)
600 			n = len - (p-v);
601 		memmove(p, tftpbp->data, n);
602 		p += n;
603 		if(n != Segsize)
604 			break;
605 		if((n = tftpread(f->fs->dev, &server, tftpbp, sizeof(tftpbp->data))) < 0)
606 			return -1;
607 	}
608 	return p-v;
609 }
610 
611 static int
pxewalk(File * f,char * name)612 pxewalk(File* f, char* name)
613 {
614 	Bootp rep;
615 	char *ini;
616 
617 	switch(f->walked){
618 	default:
619 		return -1;
620 	case 0:
621 		if(strcmp(name, "cfg") == 0){
622 			f->walked = 1;
623 			return 1;
624 		}
625 		break;
626 	case 1:
627 		if(strcmp(name, "pxe") == 0){
628 			f->walked = 2;
629 			return 1;
630 		}
631 		break;
632 	case 2:
633 		if(strcmp(name, "%E") != 0)
634 			break;
635 		f->walked = 3;
636 
637 		if(bootpopen(f->fs->dev, nil, &rep, 0) < 0)
638 			return 0;
639 
640 		if (pxether == nil)
641 			pxether = malloc(MaxEther * sizeof *pxether);
642 		ini = pxether[f->fs->dev].ini;
643 		/* use our mac address instead of relying on a bootp answer */
644 		seprint(ini, ini+INIPATHLEN, "/cfg/pxe/%E", (uchar *)myaddr.ea);
645 		f->path = ini;
646 
647 		return 1;
648 	}
649 	return 0;
650 }
651 
652 void*
pxegetfspart(int ctlrno,char * part,int)653 pxegetfspart(int ctlrno, char* part, int)
654 {
655 	Fs *fs;
656 
657 	if(!pxe || strcmp(part, "*") != 0 || ctlrno >= MaxEther ||
658 	    iniread && getconf("*pxeini") != nil)
659 		return nil;
660 
661 	if (pxether == nil)
662 		pxether = malloc(MaxEther * sizeof *pxether);
663 	fs = &pxether[ctlrno].fs;
664 	fs->dev = ctlrno;
665 	fs->diskread = pxediskread;
666 	fs->diskseek = pxediskseek;
667 
668 	fs->read = pxeread;
669 	fs->walk = pxewalk;
670 
671 	fs->root.fs = fs;
672 	fs->root.walked = 0;
673 
674 	return fs;
675 }
676 
677 /*
678  * tftp upload, for memory dumps and the like.
679  */
680 
681 void
sendfile(int ctlrno,Netaddr * a,void * p,int len,char * name)682 sendfile(int ctlrno, Netaddr *a, void *p, int len, char *name)
683 {
684 	int ackblock, block, ret, rexmit, rlen, n, txtry, rxl;
685 	short op;
686 	char *mem, *emem;
687 	static uchar ack[1024], buf[Segsize+4];	/* reduce stack use */
688 
689 	block = 0;
690 	rexmit = 0;
691 	n = 0;
692 	mem = p;
693 	emem = mem + len;
694 	for(txtry = 0; txtry < 10;) {
695 		if(rexmit == 0) {
696 			block++;
697 			buf[0] = 0;
698 			buf[1] = Tftp_DATA;
699 			buf[2] = block>>8;
700 			buf[3] = block;
701 			if (mem < emem) {
702 				if (emem - mem < Segsize)
703 					n = emem - mem;
704 				else
705 					n = Segsize;
706 				memmove(buf+4, mem, n);
707 				mem += n;
708 			} else
709 				n = 0;
710 			txtry = 0;
711 		} else {
712 			print("rexmit %d bytes to %s:%d\n", 4+n, name, block);
713 			txtry++;
714 		}
715 
716 		/* write buf to network */
717 		udpsend(ctlrno, a, buf, 4+n);
718 		ret = 4+n;
719 
720 		for(rxl = 0; rxl < 10; rxl++) {
721 			rexmit = 0;
722 			/* read ack from network */
723 			memset(ack, 0, 32);
724 			rlen = udprecv(ctlrno, a, ack, sizeof ack);
725 			if(rlen < sizeof tftpbp->header) {
726 				rexmit = 1;
727 				print("reply too small\n");
728 				break;
729 			}
730 			op = ack[0]<<8 | ack[1];
731 			if(op == Tftp_ERROR) {
732 				print("sendfile: tftp error\n");
733 				break;
734 			}
735 			if (op != Tftp_ACK) {
736 				print("expected ACK!\n");
737 				continue;
738 			}
739 			ackblock = ack[2]<<8 | ack[3];
740 			if(ackblock == block)
741 				break;
742 			else if(ackblock == 0xffff) {
743 				rexmit = 1;
744 				break;
745 			} else
746 				print("ack not for last block sent, "
747 					"awaiting another\n");
748 		}
749 		if (rxl >= 10)
750 			print("never got ack for block %d\n", block);
751 		if(ret != Segsize+4 && rexmit == 0) {
752 			print(" done.\n");
753 			break;
754 		}
755 		if (0 && rexmit == 0)
756 			print(".");
757 	}
758 	if (txtry >= 5)
759 		print("too many rexmits\n");
760 }
761 
762 int
tftpupload(char * name,void * p,int len)763 tftpupload(char *name, void *p, int len)
764 {
765 	int n;
766 
767 	if (tftpbp == nil)
768 		tftpbp = malloc(sizeof *tftpbp);
769 
770 	/* assume myaddr and server are still set from downloading */
771 	myaddr.port = tftpport++;
772 	server.port = TFTPport;
773 
774 	n = tftpopen(ctlrinuse, &server, name, tftpbp, Tftp_WRITE);
775 	if(n < 0)
776 		return -1;
777 	sendfile(ctlrinuse, &server, p, len, name);
778 	return 0;
779 }
780