1 /*
2 * 9boot - load next kernel via pxe (bootp, tftp) and start it
3 *
4 * intel says that pxe can only load into the bottom 640K,
5 * and intel's boot agent takes 128K, leaving only 512K for 9boot.
6 *
7 * some of this code is from the old 9load's bootp.c.
8 */
9 #include "u.h"
10 #include "../port/lib.h"
11 #include "mem.h"
12 #include "dat.h"
13 #include "fns.h"
14 #include "io.h"
15 #include "ureg.h"
16 #include "pool.h"
17 #include "../port/netif.h"
18 #include "etherif.h"
19 #include "../ip/ip.h"
20 #include "pxe.h"
21
22 #define TFTPDEF "135.104.9.6" /* IP of default tftp server */
23
24 enum {
25 Tftpusehdrs = 0, /* flag: use announce+headers for tftp? */
26 Debug = 0,
27
28 Tftphdrsz = 4,
29 /*
30 * this can be bigger than the ether mtu and
31 * will work due to ip fragmentation, at least on v4.
32 */
33 Prefsegsize = 1400,
34 Maxsegsize = 2048,
35 Bufsz = Maxsegsize + 2,
36
37 Ok = 0,
38 Err = -1,
39 Nonexist = -2,
40 };
41
42 typedef struct Ethaddr Ethaddr;
43 typedef struct Kernname Kernname;
44 typedef struct Openeth Openeth;
45 typedef struct Tftp Tftp;
46
47 struct Tftp {
48 uchar header[Tftphdrsz];
49 uchar data[Maxsegsize];
50 };
51
52 struct Kernname {
53 char *edev;
54 char *bootfile;
55 };
56
57 struct Openeth {
58 /* names */
59 int ctlrno;
60 char ethname[16]; /* ether%d */
61 char netethname[32]; /* /net/ether%d */
62 char filename[128]; /* from bootp, for tftp */
63
64 Chan *ifcctl; /* /net/ipifc/clone */
65 Chan *ethctl; /* /net/etherN/0/ctl, for promiscuous mode */
66
67 /* udp connection */
68 Chan *udpctl;
69 Chan *udpdata;
70 Pxenetaddr *netaddr;
71 int rxactive;
72 };
73
74 struct Ethaddr { /* communication with sleep procs */
75 Openeth *oe;
76 Pxenetaddr *a;
77 };
78
79 static char ethernm[] = "ether";
80 static uchar myea[Eaddrlen];
81 static Pxenetaddr myaddr; /* actually, local ip addr & port */
82
83 /*
84 * there can be at most one concurrent tftp session until we move these
85 * variables into Openeth or some other struct (Tftpstate).
86 */
87 static ushort tftpport;
88 static int tftpblockno;
89 static int tftpphase;
90 static int progress;
91 static int segsize;
92 static Tftp *tftpb;
93 static Pxenetaddr tftpserv; /* actually, remote ip addr & port */
94 static Pxenetaddr bootpserv;
95
96 static int tftpconnect(Openeth *, Bootp *);
97
98 uchar *
etheraddr(Openeth * oe)99 etheraddr(Openeth *oe)
100 {
101 int n;
102 char name[32], buf[32];
103 static uchar ea[Eaddrlen];
104
105 memset(ea, 0, sizeof ea);
106 snprint(name, sizeof name, "#l%d/ether%d/addr", oe->ctlrno, oe->ctlrno);
107 n = readfile(name, buf, sizeof buf - 1);
108 if (n < 0)
109 return ea;
110 buf[n] = '\0';
111 parseether(ea, buf);
112 return ea;
113 }
114
115 static void
udpsend(Openeth * oe,Pxenetaddr * a,void * data,int dlen)116 udpsend(Openeth *oe, Pxenetaddr *a, void *data, int dlen)
117 {
118 int n;
119 uchar *buf;
120 Chan *c;
121 Etherpkt pkt;
122 Udphdr *uh;
123
124 buf = data;
125 if (dlen > sizeof pkt)
126 panic("udpsend: packet too big");
127
128 oe->netaddr = a;
129 /*
130 * add Plan 9 UDP pseudo-headers
131 */
132 if (!tftpphase || Tftpusehdrs) {
133 memset(&pkt, 0, sizeof pkt);
134 uh = (Udphdr*)&pkt;
135 memmove(uh + 1, data, dlen);
136 USED(buf);
137 buf = (uchar *)uh;
138 dlen += sizeof *uh;
139 if (dlen > sizeof pkt)
140 panic("udpsend: packet too big");
141
142 ipmove(uh->laddr, myaddr.ip);
143 hnputs(uh->lport, myaddr.port);
144 ipmove(uh->raddr, a->ip);
145 hnputs(uh->rport, a->port);
146 if(Debug)
147 print("udpsend %I!%d -> %I!%d ", uh->laddr,
148 nhgets(uh->lport), uh->raddr, nhgets(uh->rport));
149 }
150 if (waserror()) {
151 iprint("udp write error\n");
152 return; /* send another req later */
153 }
154 c = oe->udpdata;
155 assert(oe->udpdata != nil);
156 n = devtab[c->type]->write(c, buf, dlen, c->offset);
157 poperror();
158 c->offset += n;
159 if (n != dlen)
160 print("udpsend: wrote %d/%d\n", n, dlen);
161 else if (progress)
162 print(".");
163 }
164
165 static void
nak(Openeth * oe,Pxenetaddr * a,int code,char * msg,int report)166 nak(Openeth *oe, Pxenetaddr *a, int code, char *msg, int report)
167 {
168 char buf[4 + 32];
169
170 buf[0] = 0;
171 buf[1] = Tftp_ERROR;
172 buf[2] = 0;
173 buf[3] = code;
174 strncpy(buf+4, msg, sizeof buf - 4 - 1);
175 udpsend(oe, a, buf, 4 + strlen(buf+4) + 1);
176 if(report)
177 print("\ntftp: error(%d): %s\n", code, msg);
178 }
179
180 /* a is the source address we're looking for */
181 static int
tuplematch(Pxenetaddr * a,Udphdr * h)182 tuplematch(Pxenetaddr *a, Udphdr *h)
183 {
184 int port;
185 uchar *ip;
186
187 if (tftpphase && !Tftpusehdrs)
188 return 1;
189 /*
190 * we're using udp headers mode, because we're still doing bootp,
191 * or we are doing tftp and we chose to use headers mode.
192 */
193 port = a->port;
194 ip = a->ip;
195 /*
196 * we're accepting any src port or it's from the port we want, and
197 * it's from the ip we want or we sent to a broadcast address, and
198 * it's for us or it's a broadcast.
199 */
200 return (port == 0 || nhgets(h->rport) == port) &&
201 (equivip6(h->raddr, ip) || equivip6(ip, IPv4bcast)) &&
202 (equivip6(h->laddr, myaddr.ip) || equivip6(h->laddr, IPv4bcast));
203 }
204
205 /* extract UDP payload into data and set a */
206 static int
udppayload(Udphdr * h,int len,Pxenetaddr * a,uchar * data,int dlen)207 udppayload(Udphdr *h, int len, Pxenetaddr *a, uchar *data, int dlen)
208 {
209 if(Debug)
210 print("udprecv %I!%d to %I!%d...\n",
211 h->raddr, nhgets(h->rport), h->laddr, nhgets(h->lport));
212
213 if(a->port != 0 && nhgets(h->rport) != a->port) {
214 if(Debug)
215 print("udpport %ux not %ux\n", nhgets(h->rport), a->port);
216 return -1;
217 }
218
219 if(!equivip6(a->ip, IPv4bcast) && !equivip6(a->ip, h->raddr)) {
220 if(Debug)
221 print("bad ip %I not %I\n", h->raddr, a->ip);
222 return -1;
223 }
224
225 len -= sizeof *h; /* don't count pseudo-headers */
226 if(len > dlen) {
227 print("udp packet too big: %d > %d; from addr %I\n",
228 len, dlen, h->raddr);
229 return -1;
230 }
231 memmove(data, h + 1, len); /* skip pseudo-headers */
232
233 /* set a from remote address */
234 ipmove(a->ip, h->raddr);
235 a->port = nhgets(h->rport);
236 return len;
237 }
238
239 static int
chanlen(Chan * ch)240 chanlen(Chan *ch)
241 {
242 int len;
243 Dir *dp;
244
245 dp = dirchstat(ch);
246 if (dp == nil)
247 return -1;
248 len = dp->length; /* qlen(cv->rq) in devip */
249 free(dp);
250 return len;
251 }
252
253 static int
udprecv(Openeth * oe,Pxenetaddr * a,void * data,int dlen)254 udprecv(Openeth *oe, Pxenetaddr *a, void *data, int dlen)
255 {
256 int len, buflen, chlen;
257 ulong timo, now;
258 char *buf;
259 Chan *c;
260 Etherpkt pkt;
261
262 oe->netaddr = a;
263 /* timo is frequency of tftp ack and broadcast bootp retransmission */
264 if(oe->rxactive == 0)
265 timo = 1000;
266 else
267 timo = Timeout;
268 now = TK2MS(m->ticks);
269 timo += now; /* deadline */
270
271 c = oe->udpdata;
272 spllo(); /* paranoia */
273 do {
274 /*
275 * wait for data to arrive or time-out.
276 * alarms only work for user procs, so we poll to avoid getting
277 * stuck in ipread.
278 */
279 for (chlen = chanlen(c); chlen == 0 && now < timo;
280 chlen = chanlen(c)) {
281 /* briefly give somebody else a chance to run */
282 tsleep(&up->sleep, return0, 0, 0);
283 now = TK2MS(m->ticks);
284 }
285 if (chlen <= 0) {
286 print("T");
287 return -1; /* timed out */
288 }
289
290 while (waserror()) {
291 print("read err: %s\n", up->errstr);
292 tsleep(&up->sleep, return0, 0, 1000);
293 }
294
295 /*
296 * using Plan 9 UDP pseudo-headers?
297 */
298 if (tftpphase && !Tftpusehdrs) {
299 buf = data; /* read directly in caller's buffer */
300 buflen = dlen;
301 } else {
302 buf = (char *)&pkt; /* read pkt with hdrs */
303 buflen = sizeof pkt;
304 }
305 /* devtab[c->type]->read calls ipread */
306 len = devtab[c->type]->read(c, buf, buflen, c->offset);
307 poperror();
308
309 if (len <= 0)
310 return len;
311 c->offset += len;
312 } while (!tuplematch(oe->netaddr, (Udphdr *)buf));
313
314 /*
315 * using Plan 9 UDP pseudo-headers? extract payload into caller's buf.
316 */
317 if (!tftpphase || Tftpusehdrs)
318 len = udppayload((Udphdr *)&pkt, len, a, data, dlen);
319 if (len >= 0)
320 oe->rxactive = 1;
321 return len;
322 }
323
324 static void
ack(Openeth * oe,Pxenetaddr * a,int blkno)325 ack(Openeth *oe, Pxenetaddr *a, int blkno)
326 {
327 char buf[4];
328
329 buf[0] = 0;
330 buf[1] = Tftp_ACK;
331 buf[2] = blkno>>8;
332 buf[3] = blkno;
333 udpsend(oe, a, buf, sizeof buf);
334 }
335
336 static char *
skipwd(char * wd)337 skipwd(char *wd)
338 {
339 while (*wd != '\0')
340 wd++;
341 return wd + 1; /* skip terminating NUL */
342 }
343
344 static int
optval(char * opt,char * pkt,int len)345 optval(char *opt, char *pkt, int len)
346 {
347 char *wd, *ep, *p;
348
349 ep = pkt + len;
350 for (p = pkt; p < ep && *p != '\0'; p = skipwd(wd)) {
351 wd = skipwd(p);
352 if (cistrcmp(p, opt) == 0)
353 return strtol(wd, 0, 10);
354 }
355 return -1;
356 }
357
358 /*
359 * send a tftp read request to `a' for name. if we get a data packet back,
360 * ack it and stash it in tftp for later.
361 *
362 * format of a request packet, from the RFC:
363 *
364 * 2 bytes string 1 byte string 1 byte
365 * ------------------------------------------------
366 * | Opcode | Filename | 0 | Mode | 0 |
367 * ------------------------------------------------
368 */
369 static int
tftpread1st(Openeth * oe,Pxenetaddr * a,char * name,Tftp * tftp)370 tftpread1st(Openeth *oe, Pxenetaddr *a, char *name, Tftp *tftp)
371 {
372 int i, n, len, rlen, oport, sendack;
373 static char *buf;
374
375 if (buf == nil)
376 buf = malloc(Bufsz);
377 buf[0] = 0;
378 buf[1] = Tftp_READ;
379 len = 2 + snprint(buf+2, Bufsz - 2, "%s", name) + 1;
380 len += snprint(buf+len, Bufsz - len, "octet") + 1;
381 len += snprint(buf+len, Bufsz - len, "blksize") + 1; /* option */
382 len += snprint(buf+len, Bufsz - len, "%d", Prefsegsize) + 1;
383
384 /*
385 * keep sending the same packet until we get an answer.
386 */
387 if (Debug)
388 print("tftpread1st %s\n", name);
389 oe->netaddr = a;
390 /*
391 * the first packet or two sent seem to get dropped,
392 * so use a shorter time-out on the first packet.
393 */
394 oe->rxactive = 0;
395 oport = a->port;
396 tftpblockno = 0;
397 segsize = Defsegsize;
398 sendack = 0;
399 for(i = 0; i < 10; i++){
400 a->port = oport;
401 if (sendack)
402 ack(oe, a, tftpblockno);
403 else
404 udpsend(oe, a, buf, len); /* tftp read name */
405
406 if((rlen = udprecv(oe, a, tftp, sizeof(Tftp))) < Tftphdrsz)
407 continue; /* runt or time-out */
408
409 switch((tftp->header[0]<<8)|tftp->header[1]){
410
411 case Tftp_ERROR:
412 if(strstr((char *)tftp->data, "does not exist") != nil){
413 print("%s\n", (char*)tftp->data);
414 return Nonexist;
415 }
416 print("tftpread1st: error (%d): %s\n",
417 (tftp->header[2]<<8)|tftp->header[3], (char*)tftp->data);
418 return Err;
419
420 case Tftp_OACK:
421 n = optval("blksize", (char *)tftp->header+2, rlen-2);
422 if (n <= 0) {
423 nak(oe, a, 0, "bad blksize option value", 0);
424 return Err;
425 }
426 segsize = n;
427 /* no bytes stashed in tftp.data */
428 i = 0;
429 sendack = 1;
430 break;
431
432 case Tftp_DATA:
433 tftpblockno = 1;
434 len = (tftp->header[2]<<8)|tftp->header[3];
435 if(len != tftpblockno){
436 print("tftpread1st: block error: %d\n", len);
437 nak(oe, a, 1, "block error", 0);
438 return Err;
439 }
440 rlen -= Tftphdrsz;
441 if(rlen < segsize)
442 /* ACK now, in case we don't later */
443 ack(oe, a, tftpblockno);
444 return rlen;
445
446 default:
447 print("tftpread1st: unexpected pkt type recv'd\n");
448 nak(oe, a, 0, "unexpected pkt type recv'd", 0);
449 return Err;
450 }
451 }
452
453 print("tftpread1st: failed to connect to server (%I!%d)\n", a->ip, oport);
454 return Err;
455 }
456
457 static int
tftpread(Openeth * oe,Pxenetaddr * a,Tftp * tftp,int dlen)458 tftpread(Openeth *oe, Pxenetaddr *a, Tftp *tftp, int dlen)
459 {
460 int try, blockno, len;
461
462 dlen += Tftphdrsz;
463
464 /*
465 * keep sending ACKs until we get an answer.
466 */
467 for(try = 0; try < 10; try++) {
468 ack(oe, a, tftpblockno);
469
470 len = udprecv(oe, a, tftp, dlen);
471 /*
472 * NB: not `<='; just a header is legal and happens when
473 * file being read is a multiple of segsize bytes long.
474 */
475 if(len < Tftphdrsz){
476 if(Debug)
477 print("tftpread: too short %d <= %d\n",
478 len, Tftphdrsz);
479 continue;
480 }
481 switch((tftp->header[0]<<8)|tftp->header[1]){
482 case Tftp_ERROR:
483 print("tftpread: error (blk %d): %s\n",
484 (tftp->header[2]<<8)|tftp->header[3],
485 (char*)tftp->data);
486 nak(oe, a, 0, "error pkt recv'd", 0);
487 return -1;
488 case Tftp_OACK:
489 print("tftpread: oack pkt recv'd too late\n");
490 nak(oe, a, 0, "oack pkt recv'd too late", 0);
491 return -1;
492 default:
493 print("tftpread: unexpected pkt type recv'd\n");
494 nak(oe, a, 0, "unexpected pkt type recv'd", 0);
495 return -1;
496 case Tftp_DATA:
497 break;
498 }
499 blockno = (tftp->header[2]<<8)|tftp->header[3];
500 if(blockno <= tftpblockno){
501 if(Debug)
502 print("tftpread: blkno %d <= %d\n",
503 blockno, tftpblockno);
504 continue;
505 }
506
507 if(blockno == tftpblockno+1) {
508 tftpblockno++;
509 if(len < dlen) /* last packet? send final ack */
510 ack(oe, a, tftpblockno);
511 return len-Tftphdrsz;
512 }
513 print("tftpread: block error: %d, expected %d\n",
514 blockno, tftpblockno+1);
515 }
516
517 return -1;
518 }
519
520 /*
521 * broadcast a bootp request for file. stash any answer in rep.
522 */
523 static int
bootpbcast(Openeth * oe,char * file,Bootp * rep)524 bootpbcast(Openeth *oe, char *file, Bootp *rep)
525 {
526 Bootp req;
527 int i;
528 uchar *ea;
529 char name[128], *filename, *sysname;
530 static char zeroes[IPaddrlen];
531
532 oe->filename[0] = '\0';
533 if (Debug)
534 if (file == nil)
535 print("bootpopen: %s...", oe->ethname);
536 else
537 print("bootpopen: %s!%s...", oe->ethname, file);
538 if((ea = etheraddr(oe)) == nil){
539 print("bad ether %s\n", oe->ethname);
540 return -1;
541 }
542
543 filename = nil;
544 sysname = 0;
545 if(file && *file){
546 strncpy(name, file, sizeof name);
547 if(filename = strchr(name, '!')){
548 sysname = name;
549 *filename++ = 0;
550 }
551 else
552 filename = name;
553 }
554
555 /*
556 * form a bootp request packet
557 */
558 memset(&req, 0, sizeof(req));
559 req.op = Bootrequest;
560 req.htype = 1; /* ethernet */
561 req.hlen = Eaddrlen; /* ethernet */
562 memmove(req.chaddr, ea, Eaddrlen);
563 req.flags[0] = 0x80; /* request broadcast reply */
564 if(filename != nil) {
565 strncpy(req.file, filename, sizeof(req.file));
566 strncpy(oe->filename, filename, sizeof oe->filename);
567 }
568 if(sysname != nil) /* if server name given, supply it */
569 strncpy(req.sname, sysname, sizeof(req.sname));
570
571 if (memcmp(myaddr.ip, zeroes, sizeof myaddr.ip) == 0)
572 ipmove(myaddr.ip, IPv4bcast); /* didn't know my ip yet */
573 myaddr.port = BPportsrc;
574 memmove(myea, ea, Eaddrlen);
575
576 /* send to 255.255.255.255!67 */
577 ipmove(bootpserv.ip, IPv4bcast);
578 bootpserv.port = BPportdst;
579
580 /*
581 * send it until we get a matching answer
582 */
583 memset(rep, 0, sizeof *rep);
584 for(i = 10; i > 0; i--) {
585 req.xid[0] = i; /* try different xids */
586 udpsend(oe, &bootpserv, &req, sizeof(req));
587
588 if(udprecv(oe, &bootpserv, rep, sizeof(*rep)) <= 0)
589 continue;
590 if(memcmp(req.chaddr, rep->chaddr, Eaddrlen) != 0)
591 continue;
592 if(rep->htype != 1 || rep->hlen != Eaddrlen)
593 continue;
594 if(sysname == 0 || strcmp(sysname, rep->sname) == 0)
595 break;
596 }
597 if(i <= 0) {
598 if (file == nil)
599 print("bootp on %s timed out\n", oe->ethname);
600 else
601 print("bootp on %s for %s timed out\n", oe->ethname, file);
602 return -1;
603 }
604 return 0;
605 }
606
607 /*
608 * request file via tftp from server named in rep.
609 * initial data packet will be stashed in tftpb.
610 */
611 static int
tftpopen(Openeth * oe,char * file,Bootp * rep)612 tftpopen(Openeth *oe, char *file, Bootp *rep)
613 {
614 char *filename;
615 char buf[128];
616 static uchar ipv4noaddr[IPv4addrlen];
617
618 if (tftpconnect(oe, rep) < 0)
619 return Err;
620
621 /*
622 * read file from tftp server in bootp answer
623 */
624 filename = oe->filename;
625 if (file)
626 filename = file;
627 if(filename == 0 || *filename == 0){
628 if(strcmp(rep->file, "/386/9boot") == 0 ||
629 strcmp(rep->file, "/386/9pxeload") == 0) {
630 print("won't load another boot loader (%s)\n", rep->file);
631 return -1; /* avoid infinite loop */
632 }
633 filename = rep->file;
634 }
635
636 print("\n");
637 if(rep->sname[0] != '\0')
638 print("%s ", rep->sname);
639
640 v4tov6(myaddr.ip, rep->yiaddr);
641 myaddr.port = tftpport;
642 if (equivip4(rep->siaddr, ipv4noaddr)) { /* no server address? */
643 getstr("tftp server IP address", buf, sizeof buf, TFTPDEF, 0);
644 v4parseip(rep->siaddr, buf);
645 }
646 v4tov6(tftpserv.ip, rep->siaddr);
647 tftpserv.port = TFTPport;
648 if (tftpb == nil)
649 tftpb = malloc(sizeof *tftpb);
650
651 print("(%V!%d): %s ", rep->siaddr, tftpserv.port, filename);
652
653 return tftpread1st(oe, &tftpserv, filename, tftpb);
654 }
655
656 /* load the kernel in file via tftp on oe */
657 int
tftpboot(Openeth * oe,char * file,Bootp * rep,Boot * b)658 tftpboot(Openeth *oe, char *file, Bootp *rep, Boot *b)
659 {
660 int n;
661
662 /* file must exist, else it's an error */
663 if((n = tftpopen(oe, file, rep)) < 0)
664 return n;
665
666 progress = 0; /* no more dots; we're on a roll now */
667 print(" "); /* after "sys (ip!port): kernel ..." */
668 while(bootpass(b, tftpb->data, n) == MORE){
669 n = tftpread(oe, &tftpserv, tftpb, segsize);
670 if(n < segsize)
671 break;
672 }
673 if(0 < n && n < segsize) /* got to end of file */
674 bootpass(b, tftpb->data, n);
675 else
676 nak(oe, &tftpserv, 3, "ok", 0); /* tftpclose to abort transfer */
677 bootpass(b, nil, 0); /* boot if possible */
678 return Err;
679 }
680
681 /* leave the channel to /net/ipifc/clone open */
682 static int
binddevip(Openeth * oe)683 binddevip(Openeth *oe)
684 {
685 Chan *icc;
686 char buf[32];
687
688 if (waserror()) {
689 print("binddevip: can't bind ether %s: %s\n",
690 oe->netethname, up->errstr);
691 nexterror();
692 }
693 /* get a new ip interface */
694 oe->ifcctl = icc = namecopen("/net/ipifc/clone", ORDWR);
695 if(icc == nil)
696 error("can't open /net/ipifc/clone");
697
698 /*
699 * specify medium as ethernet, bind the interface to it.
700 * this should trigger chandial of types 0x800, 0x806 and 0x86dd.
701 */
702 snprint(buf, sizeof buf, "bind ether %s", oe->netethname);
703 devtab[icc->type]->write(icc, buf, strlen(buf), 0); /* bind ether %s */
704 poperror();
705 return 0;
706 }
707
708 /* set the default route */
709 static int
adddefroute(char *,uchar * gaddr)710 adddefroute(char *, uchar *gaddr)
711 {
712 char buf[64];
713 Chan *rc;
714
715 rc = nil;
716 if (waserror()) {
717 if (rc)
718 cclose(rc);
719 return -1;
720 }
721 rc = enamecopen("/net/iproute", ORDWR);
722
723 if(isv4(gaddr))
724 snprint(buf, sizeof buf, "add 0 0 %I", gaddr);
725 else
726 snprint(buf, sizeof buf, "add :: /0 %I", gaddr);
727 devtab[rc->type]->write(rc, buf, strlen(buf), 0);
728 poperror();
729 cclose(rc);
730 return 0;
731 }
732
733 static int
validip(uchar * ip)734 validip(uchar *ip)
735 {
736 return ipcmp(ip, IPnoaddr) != 0 && ipcmp(ip, v4prefix) != 0;
737 }
738
739 static int
openetherdev(Openeth * oe)740 openetherdev(Openeth *oe)
741 {
742 int n;
743 char num[16];
744 Chan *c;
745 static char promisc[] = "promiscuous";
746
747 if (chdir(oe->netethname) < 0)
748 return -1; /* out of ethers */
749
750 oe->ethctl = nil;
751 if (waserror()) {
752 print("error opening /net/ether%d/0/ctl: %s\n",
753 oe->ctlrno, up->errstr);
754 if (oe->ethctl) {
755 cclose(oe->ethctl);
756 oe->ethctl = nil;
757 }
758 chdir("/"); /* don't hold conv. open */
759 return -1;
760 }
761 oe->ethctl = c = namecopen("0/ctl", ORDWR); /* should be ipv4 */
762 if (c == nil) {
763 /* read clone file to make conversation 0 since not present */
764 oe->ethctl = c = enamecopen("clone", ORDWR);
765 n = devtab[c->type]->read(c, num, sizeof num - 1, 0);
766 if (n < 0)
767 print("no %s/clone: %s\n", oe->netethname, up->errstr);
768 else {
769 num[n] = 0;
770 print("%s/clone returned %s\n", oe->netethname, num);
771 }
772 }
773 /* shouldn't be needed to read bootp (broadcast) reply */
774 devtab[c->type]->write(c, promisc, sizeof promisc-1, 0);
775 poperror();
776 chdir("/");
777 /* leave oe->ethctl open to keep promiscuous mode on */
778 return 0;
779 }
780
781 /* add a logical interface to the ip stack */
782 int
minip4cfg(Openeth * oe)783 minip4cfg(Openeth *oe)
784 {
785 int n;
786 char buf[64];
787
788 n = snprint(buf, sizeof buf, "add %I", IPnoaddr);
789 devtab[oe->ifcctl->type]->write(oe->ifcctl, buf, n, 0); /* add %I */
790
791 openetherdev(oe);
792 return 0;
793 }
794
795 /* remove the :: address added by minip4cfg */
796 int
unminip4cfg(Openeth * oe)797 unminip4cfg(Openeth *oe)
798 {
799 int n;
800 char buf[64];
801
802 n = snprint(buf, sizeof buf, "remove %I /128", IPnoaddr);
803 if (waserror()) {
804 print("failed write to ifc: %s: %s\n", buf, up->errstr);
805 return -1;
806 }
807 devtab[oe->ifcctl->type]->write(oe->ifcctl, buf, n, 0); /* remove %I */
808 cclose(oe->ethctl); /* turn promiscuous mode off */
809 oe->ethctl = nil;
810 poperror();
811 return 0;
812 }
813
814 /*
815 * parse p, looking for option `op'. if non-nil, np points to minimum length.
816 * return nil if option is too small, else ptr to opt, and
817 * store actual length via np if non-nil.
818 */
819 uchar*
optget(uchar * p,int op,int * np)820 optget(uchar *p, int op, int *np)
821 {
822 int len, code;
823
824 while ((code = *p++) != OBend) {
825 if(code == OBpad)
826 continue;
827 len = *p++;
828 if(code != op) {
829 p += len;
830 continue;
831 }
832 if(np != nil){
833 if(*np > len)
834 return 0;
835 *np = len;
836 }
837 return p;
838 }
839 return 0;
840 }
841
842 int
optgetaddr(uchar * p,int op,uchar * ip)843 optgetaddr(uchar *p, int op, uchar *ip)
844 {
845 int len;
846
847 len = 4;
848 p = optget(p, op, &len);
849 if(p == nil)
850 return 0;
851 v4tov6(ip, p);
852 return 1;
853 }
854
855 int beprimary = 1;
856
857 /* add a logical interface to the ip stack */
858 int
ip4cfg(Openeth * oe,Bootp * rep)859 ip4cfg(Openeth *oe, Bootp *rep)
860 {
861 int n;
862 uchar gaddr[IPaddrlen], v6mask[IPaddrlen];
863 uchar v4mask[IPv4addrlen];
864 char buf[64];
865 static uchar zeroes[4];
866
867 v4tov6(gaddr, rep->yiaddr);
868 if(!validip(gaddr))
869 return -1;
870
871 /* dig subnet mask, if any, out of options. if none, guess. */
872 if(optgetaddr(rep->optdata, OBmask, v6mask)) {
873 v6tov4(v4mask, v6mask);
874 n = snprint(buf, sizeof buf, "add %V %M", rep->yiaddr, v4mask);
875 } else
876 n = snprint(buf, sizeof buf, "add %V 255.255.255.0", rep->yiaddr);
877
878 devtab[oe->ifcctl->type]->write(oe->ifcctl, buf, n, 0);
879
880 v4tov6(gaddr, rep->giaddr);
881 if(beprimary==1 && validip(gaddr) && !equivip4(rep->giaddr, zeroes))
882 adddefroute("/net", gaddr);
883 return 0;
884 }
885
886 static int
openudp(Openeth * oe)887 openudp(Openeth *oe)
888 {
889 int n;
890 char buf[16];
891 Chan *cc;
892
893 /* read clone file for conversation number */
894 if (waserror())
895 panic("openudp: can't open /net/udp/clone");
896 cc = enamecopen("/net/udp/clone", ORDWR);
897 oe->udpctl = cc;
898 n = devtab[cc->type]->read(cc, buf, sizeof buf - 1, 0);
899 poperror();
900 buf[n] = '\0';
901 return atoi(buf);
902 }
903
904 static void
initbind(Openeth * oe)905 initbind(Openeth *oe)
906 {
907 char buf[8];
908
909 if (waserror()) {
910 print("error while binding: %s\n", up->errstr);
911 return;
912 }
913 snprint(buf, sizeof buf, "#I%d", oe->ctlrno);
914 bind(buf, "/net", MAFTER);
915 snprint(buf, sizeof buf, "#l%d", oe->ctlrno);
916 bind(buf, "/net", MAFTER);
917 binddevip(oe);
918 poperror();
919 }
920
921 static void
closeudp(Openeth * oe)922 closeudp(Openeth *oe)
923 {
924 if (oe->udpctl) {
925 cclose(oe->udpctl);
926 oe->udpctl = nil;
927 }
928 if (oe->udpdata) {
929 cclose(oe->udpdata);
930 oe->udpdata = nil;
931 }
932 }
933
934 static int
announce(Openeth * oe,char * port)935 announce(Openeth *oe, char *port)
936 {
937 int udpconv;
938 char buf[32];
939 static char hdrs[] = "headers";
940
941 while (waserror()) {
942 print("can't announce udp!*!%s: %s\n", port, up->errstr);
943 closeudp(oe);
944 nexterror();
945 }
946 udpconv = openudp(oe);
947 if (udpconv < 0)
948 panic("can't open udp conversation: %s", up->errstr);
949
950 /* headers is only effective after a udp announce */
951 snprint(buf, sizeof buf, "announce %s", port);
952 devtab[oe->udpctl->type]->write(oe->udpctl, buf, strlen(buf), 0);
953 devtab[oe->udpctl->type]->write(oe->udpctl, hdrs, sizeof hdrs - 1, 0);
954 poperror();
955
956 /* now okay to open the data file */
957 snprint(buf, sizeof buf, "/net/udp/%d/data", udpconv);
958 /*
959 * we must use create, not open, to get Conv->rq and ->wq
960 * allocated by udpcreate.
961 */
962 oe->udpdata = enameccreate(buf, ORDWR);
963 cclose(oe->udpctl);
964 oe->udpctl = nil;
965 return udpconv;
966 }
967
968 static long
tftprdfile(Openeth * oe,int openread,void * va,long len)969 tftprdfile(Openeth *oe, int openread, void* va, long len)
970 {
971 int n;
972 char *p, *v;
973
974 n = openread; /* have read this many bytes already into tftpb->data */
975 p = v = va;
976 len--; /* leave room for NUL */
977 while(n > 0) {
978 if((p-v)+n > len)
979 n = len - (p-v);
980 memmove(p, tftpb->data, n);
981 p += n;
982 *p = 0;
983 if(n != segsize)
984 break;
985
986 if((n = tftpread(oe, &tftpserv, tftpb, segsize)) < 0)
987 return n;
988 }
989 return p-v;
990 }
991
992 static int
tftpconnect(Openeth * oe,Bootp * rep)993 tftpconnect(Openeth *oe, Bootp *rep)
994 {
995 char num[16], dialstr[64];
996
997 if (waserror()) {
998 print("can't dial: %s\n", up->errstr);
999 return -1;
1000 }
1001 closeudp(oe);
1002
1003 tftpphase = 1;
1004 tftpport = 5000 + nrand(20480);
1005 snprint(num, sizeof num, "%d", tftpport);
1006 if (Tftpusehdrs)
1007 announce(oe, num);
1008 else {
1009 snprint(dialstr, sizeof dialstr, "/net/udp!%V!%d",
1010 rep->siaddr, TFTPport);
1011 oe->udpdata = chandial(dialstr, num, nil, nil);
1012 oe->udpctl = nil;
1013 }
1014 poperror();
1015 return 0;
1016 }
1017
1018 static int
setipcfg(Openeth * oe,Bootp * rep)1019 setipcfg(Openeth *oe, Bootp *rep)
1020 {
1021 int r;
1022
1023 tftpphase = 0;
1024 progress = 1;
1025
1026 /* /net/iproute is unpopulated here; add at least broadcast */
1027 minip4cfg(oe);
1028 announce(oe, "68");
1029 r = bootpbcast(oe, nil, rep);
1030 closeudp(oe);
1031 unminip4cfg(oe);
1032 if(r < 0)
1033 return -1;
1034
1035 ip4cfg(oe, rep);
1036 if (Debug)
1037 print("got & set ip config\n");
1038 return 0;
1039 }
1040
1041 /*
1042 * use bootp answer (rep) to open cfgpxe.
1043 * reads first pkt of cfgpxe into tftpb->data.
1044 */
1045 static int
rdcfgpxe(Openeth * oe,Bootp * rep,char * cfgpxe)1046 rdcfgpxe(Openeth *oe, Bootp *rep, char *cfgpxe)
1047 {
1048 int n;
1049 char *ini;
1050
1051 /* cfgpxe is optional */
1052 n = tftpopen(oe, cfgpxe, rep);
1053 if (n < 0)
1054 return n;
1055 if (Debug)
1056 print("\opened %s\n", cfgpxe);
1057
1058 ini = smalloc(2*BOOTARGSLEN);
1059 /* starts by copying data from tftpb->data into ini */
1060 n = tftprdfile(oe, n, ini, 2*BOOTARGSLEN);
1061 if (n < 0) {
1062 print("error reading %s\n", cfgpxe);
1063 free(ini);
1064 return n;
1065 }
1066 print(" read %d bytes", n);
1067
1068 /*
1069 * take note of plan9.ini contents. consumes ini to make config vars,
1070 * thus we can't free ini.
1071 */
1072 dotini(ini);
1073 return Ok;
1074 }
1075
1076 /*
1077 * break kp->bootfile into kp->edev & kp->bootfile,
1078 * copy any args for new kernel to low memory.
1079 */
1080 static int
parsebootfile(Kernname * kp)1081 parsebootfile(Kernname *kp)
1082 {
1083 char *p;
1084
1085 p = strchr(kp->bootfile, '!');
1086 if (p != nil) {
1087 *p++ = '\0';
1088 kp->edev = kp->bootfile;
1089 kp->bootfile = nil;
1090 kstrdup(&kp->bootfile, p);
1091 if (strncmp(kp->edev, ethernm, sizeof ethernm - 1) != 0) {
1092 print("bad ether device %s\n", kp->edev);
1093 return Err;
1094 }
1095 }
1096
1097 /* pass any arguments to kernels that expect them */
1098 strecpy(BOOTLINE, BOOTLINE+BOOTLINELEN, kp->bootfile);
1099 p = strchr(kp->bootfile, ' ');
1100 if(p != nil)
1101 *p = '\0';
1102 return Ok;
1103 }
1104
1105 static int
getkernname(Openeth * oe,Bootp * rep,Kernname * kp)1106 getkernname(Openeth *oe, Bootp *rep, Kernname *kp)
1107 {
1108 int n;
1109 char *p;
1110 char cfgpxe[32], buf[64];
1111
1112 if (kp->bootfile) {
1113 /* i think returning here is a bad idea */
1114 // print("getkernname: already have bootfile %s\n",
1115 // kp->bootfile);
1116 free(kp->bootfile);
1117 // return Ok;
1118 }
1119 kp->edev = kp->bootfile = nil;
1120 i8250console(); /* configure serial port with defaults */
1121
1122 /* use our mac address instead of relying on a bootp answer. */
1123 snprint(cfgpxe, sizeof cfgpxe, "/cfg/pxe/%E", myea);
1124 n = rdcfgpxe(oe, rep, cfgpxe);
1125 switch (n) {
1126 case Ok:
1127 p = getconf("bootfile");
1128 if (p)
1129 kstrdup(&kp->bootfile, p);
1130 if (kp->bootfile == nil)
1131 askbootfile(buf, sizeof buf, &kp->bootfile, Promptsecs,
1132 "ether0!/386/9pccpu");
1133 if (strcmp(kp->bootfile, "manual") == 0)
1134 askbootfile(buf, sizeof buf, &kp->bootfile, 0, "");
1135 break;
1136 case Err:
1137 print("\nfailed.\n");
1138 return n;
1139 case Nonexist:
1140 askbootfile(buf, sizeof buf, &kp->bootfile, 0, "");
1141 break;
1142 }
1143 return parsebootfile(kp);
1144 }
1145
1146 static void
unbinddevip(Openeth * oe)1147 unbinddevip(Openeth *oe)
1148 {
1149 Chan *icc;
1150 static char unbind[] = "unbind";
1151
1152 icc = oe->ifcctl;
1153 if (icc) {
1154 devtab[icc->type]->write(icc, unbind, sizeof unbind - 1, 0);
1155 cclose(icc);
1156 oe->ifcctl = nil;
1157 }
1158 }
1159
1160 /*
1161 * phase 1: get our ip (v4) configuration via bootp, set new ip configuration.
1162 * phase 2: load /cfg/pxe, parse it, extract kernel filename.
1163 * phase 3: load kernel and jump to it.
1164 */
1165 static int
tftpload(Openeth * oe,Kernname * kp)1166 tftpload(Openeth *oe, Kernname *kp)
1167 {
1168 int r, n;
1169 char buf[64];
1170 Bootp rep;
1171 Boot boot;
1172
1173 r = -1;
1174 if(waserror()) {
1175 print("tftpload: %s\n", up->errstr);
1176 closeudp(oe);
1177 unbinddevip(oe);
1178 return r;
1179 }
1180
1181 memset(&rep, 0, sizeof rep);
1182 if (setipcfg(oe, &rep) < 0)
1183 error("can't set ip config");
1184
1185 n = getkernname(oe, &rep, kp);
1186 if (n < 0) {
1187 r = n; /* pass reason back to caller */
1188 USED(r);
1189 nexterror();
1190 }
1191 do {
1192 if (kp->edev &&
1193 oe->ctlrno != strtol(kp->edev + sizeof ethernm - 1, 0, 10)){
1194 /* user specified an ether & it's not this one; next! */
1195 r = Ok;
1196 USED(r);
1197 nexterror();
1198 }
1199
1200 memset(&boot, 0, sizeof boot);
1201 boot.state = INITKERNEL;
1202 r = tftpboot(oe, kp->bootfile, &rep, &boot);
1203
1204 /* we failed or bootfile asked for another ether */
1205 if (r == Nonexist)
1206 do {
1207 askbootfile(buf, sizeof buf, &kp->bootfile, 0, "");
1208 } while (parsebootfile(kp) != Ok);
1209 } while (r == Nonexist);
1210
1211 poperror();
1212 closeudp(oe);
1213 unbinddevip(oe);
1214 return r;
1215 }
1216
1217 static int
etherload(int eth,Kernname * kp)1218 etherload(int eth, Kernname *kp)
1219 {
1220 int r;
1221 Openeth *oe;
1222
1223 print("pxe on ether%d ", eth);
1224 oe = smalloc(sizeof *oe);
1225 memset(oe, 0, sizeof *oe);
1226 oe->ctlrno = eth;
1227 snprint(oe->ethname, sizeof oe->ethname, "ether%d", oe->ctlrno);
1228 snprint(oe->netethname, sizeof oe->netethname, "/net/ether%d",
1229 oe->ctlrno);
1230 initbind(oe);
1231
1232 r = tftpload(oe, kp);
1233
1234 /* failed to boot; keep going */
1235 unmount(nil, "/net");
1236 return r;
1237 }
1238
1239 static int
attacheth(int neth)1240 attacheth(int neth)
1241 {
1242 char num[4];
1243 Chan *cc;
1244
1245 cc = nil;
1246 if (waserror()) { /* no more interfaces */
1247 if (cc)
1248 cclose(cc);
1249 return -1;
1250 }
1251 snprint(num, sizeof num, "%d", neth);
1252 cc = etherattach(num);
1253 if (cc)
1254 cclose(cc);
1255 poperror();
1256 return cc == nil? -1: 0;
1257 }
1258
1259 void
bootloadproc(void *)1260 bootloadproc(void *)
1261 {
1262 int eth, neth, needattach;
1263 Kernname kernnm;
1264
1265 srand(TK2MS(m->ticks)); /* for local port numbers */
1266 nrand(20480); /* 1st # is always 0; toss it */
1267 kernnm.edev = kernnm.bootfile = nil;
1268
1269 while(waserror()) {
1270 print("%s\n", up->errstr);
1271 tsleep(&up->sleep, return0, 0, 30*1000);
1272 }
1273 neth = MaxEther;
1274 needattach = 1;
1275 for (;;) {
1276 /* try each interface in turn: first get /cfg/pxe file */
1277 for (eth = 0; eth < neth && kernnm.edev == nil; eth++) {
1278 if (needattach && attacheth(eth) < 0)
1279 break;
1280 etherload(eth, &kernnm);
1281 }
1282 if (needattach) {
1283 neth = eth;
1284 needattach = 0;
1285 if (neth == 0)
1286 print("no ethernet interfaces found\n");
1287 }
1288 if (kernnm.edev != nil) {
1289 eth = strtol(kernnm.edev + sizeof ethernm - 1, 0, 10);
1290 etherload(eth, &kernnm);
1291 }
1292 /*
1293 * couldn't boot on any ether. don't give up;
1294 * perhaps the boot servers are down, so try again later.
1295 */
1296 print("failed to boot via pxe; will try again.\n");
1297 tsleep(&up->sleep, return0, 0, 15*1000);
1298 }
1299 }
1300