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