1 /* ----------------------------------------------------------------------- * 2 * 3 * Copyright 2010-2012 Gene Cumm - All Rights Reserved 4 * 5 * Portions from chain.c: 6 * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved 7 * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin 8 * Significant portions copyright (C) 2010 Shao Miller 9 * [partition iteration, GPT, "fs"] 10 * 11 * This program is free software; you can redistribute it and/or modify 12 * it under the terms of the GNU General Public License as published by 13 * the Free Software Foundation, Inc., 53 Temple Place Ste 330, 14 * Boston MA 02111-1307, USA; either version 2 of the License, or 15 * (at your option) any later version; incorporated herein by reference. 16 * 17 * ----------------------------------------------------------------------- */ 18 19 /* 20 * pxechn.c 21 * 22 * PXE Chain Loader; Chain load to another PXE network boot program 23 * that may be on another host. 24 */ 25 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <consoles.h> 29 #include <console.h> 30 #include <errno.h> 31 #include <string.h> 32 #include <syslinux/config.h> 33 #include <syslinux/loadfile.h> 34 #include <syslinux/bootrm.h> 35 #include <syslinux/video.h> 36 #include <com32.h> 37 #include <stdint.h> 38 #include <syslinux/pxe.h> 39 #include <sys/gpxe.h> 40 #include <unistd.h> 41 #include <getkey.h> 42 #include <dhcp.h> 43 #include <limits.h> 44 45 46 #ifdef DEBUG 47 # define PXECHN_DEBUG 1 48 #else 49 # define PXECHN_DEBUG 0 50 #endif 51 52 typedef union { 53 uint64_t q; 54 uint32_t l[2]; 55 uint16_t w[4]; 56 uint8_t b[8]; 57 } reg64_t; 58 59 #define dprintf0(f, ...) ((void)0) 60 61 #ifndef dprintf 62 # if (PXECHN_DEBUG > 0) 63 # define dprintf printf 64 # else 65 # define dprintf(f, ...) ((void)0) 66 # endif 67 #endif 68 69 #if (PXECHN_DEBUG > 0) 70 # define dpressanykey pressanykey 71 # define dprint_pxe_bootp_t print_pxe_bootp_t 72 # define dprint_pxe_vendor_blk print_pxe_vendor_blk 73 # define dprint_pxe_vendor_raw print_pxe_vendor_raw 74 #else 75 # define dpressanykey(tm) ((void)0) 76 # define dprint_pxe_bootp_t(p, l) ((void)0) 77 # define dprint_pxe_vendor_blk(p, l) ((void)0) 78 # define dprint_pxe_vendor_raw(p, l) ((void)0) 79 #endif 80 81 #define dprintf_opt_cp dprintf0 82 #define dprintf_opt_inj dprintf0 83 #define dprintf_pc_pa dprintf 84 #define dprintf_pc_so_s dprintf0 85 86 #define t_PXENV_RESTART_TFTP t_PXENV_TFTP_READ_FILE 87 88 #define STACK_SPLIT 11 89 90 /* same as pxelinux.asm REBOOT_TIME */ 91 #define REBOOT_TIME 300 92 93 #define NUM_DHCP_OPTS 256 94 #define DHCP_OPT_LEN_MAX 256 95 #define PXE_VENDOR_RAW_PRN_MAX 0x7F 96 #define PXECHN_HOST_LEN 256 /* 63 bytes per label; 255 max total */ 97 98 #define PXECHN_NUM_PKT_TYPE 3 99 #define PXECHN_NUM_PKT_AVAIL 2*PXECHN_NUM_PKT_TYPE 100 #define PXECHN_PKT_TYPE_START PXENV_PACKET_TYPE_DHCP_DISCOVER 101 102 #define PXECHN_FORCE_PKT1 0x80000000 103 #define PXECHN_FORCE_PKT2 0x40000000 104 #define PXECHN_FORCE_ALL (PXECHN_FORCE_PKT1 | PXECHN_FORCE_PKT2) 105 #define PXECHN_FORCE_ALL_1 0 106 #define STRASINT_str ('s' + (('t' + ('r' << 8)) << 8)) 107 108 #define min(a,b) (((a) < (b)) ? (a) : (b)) 109 110 const char app_name_str[] = "pxechn.c32"; 111 112 struct pxelinux_opt { 113 char *fn; /* Filename as passed to us */ 114 in_addr_t fip; /* fn's IP component */ 115 char *fp; /* fn's path component */ 116 in_addr_t gip; /* giaddr; Gateway/DHCP relay */ 117 uint32_t force; 118 uint32_t wait; /* Additional decision to wait before boot */ 119 int32_t wds; /* WDS option/level */ 120 in_addr_t sip; /* siaddr: Next Server IP Address */ 121 struct dhcp_option p[PXECHN_NUM_PKT_AVAIL]; 122 /* original _DHCP_DISCOVER, _DHCP_ACK, _CACHED_REPLY then modified packets */ 123 char host[PXECHN_HOST_LEN]; 124 struct dhcp_option opts[PXECHN_NUM_PKT_TYPE][NUM_DHCP_OPTS]; 125 char p_unpacked[PXECHN_NUM_PKT_TYPE]; 126 }; 127 128 129 /* from chain.c */ 130 struct data_area { 131 void *data; 132 addr_t base; 133 addr_t size; 134 }; 135 136 /* From chain.c */ error(const char * msg)137 static inline void error(const char *msg) 138 { 139 fputs(msg, stderr); 140 } 141 142 /* From chain.c */ do_boot(struct data_area * data,int ndata,struct syslinux_rm_regs * regs)143 static void do_boot(struct data_area *data, int ndata, 144 struct syslinux_rm_regs *regs) 145 { 146 uint16_t *const bios_fbm = (uint16_t *) 0x413; 147 addr_t dosmem = *bios_fbm << 10; /* Technically a low bound */ 148 struct syslinux_memmap *mmap; 149 struct syslinux_movelist *mlist = NULL; 150 addr_t endimage; 151 int i; 152 153 mmap = syslinux_memory_map(); 154 155 if (!mmap) { 156 error("Cannot read system memory map\n"); 157 return; 158 } 159 160 endimage = 0; 161 for (i = 0; i < ndata; i++) { 162 if (data[i].base + data[i].size > endimage) 163 endimage = data[i].base + data[i].size; 164 } 165 if (endimage > dosmem) 166 goto too_big; 167 168 for (i = 0; i < ndata; i++) { 169 if (syslinux_add_movelist(&mlist, data[i].base, 170 (addr_t) data[i].data, data[i].size)) 171 goto enomem; 172 } 173 174 175 /* Tell the shuffler not to muck with this area... */ 176 syslinux_add_memmap(&mmap, endimage, 0xa0000 - endimage, SMT_RESERVED); 177 178 /* Force text mode */ 179 syslinux_force_text_mode(); 180 181 fputs("Booting...\n", stdout); 182 syslinux_shuffle_boot_rm(mlist, mmap, 3, regs); 183 error("Chainboot failed!\n"); 184 return; 185 186 too_big: 187 error("Loader file too large\n"); 188 return; 189 190 enomem: 191 error("Out of memory\n"); 192 return; 193 } 194 usage(void)195 void usage(void) 196 { 197 printf("USAGE:\n" 198 " %s [OPTIONS]... _new-nbp_\n" 199 " %s -r _new-nbp_ (calls PXE stack PXENV_RESTART_TFTP)\n" 200 "OPTIONS:\n" 201 " [-c config] [-g gateway] [-p prefix] [-t reboot] [-u] [-w] [-W]" 202 " [-o opt.ty=val]\n\n", 203 app_name_str, app_name_str); 204 } 205 pxe_error(int ierr,const char * evt,const char * msg)206 void pxe_error(int ierr, const char *evt, const char *msg) 207 { 208 if (msg) 209 printf("%s", msg); 210 else if (evt) 211 printf("Error while %s: ", evt); 212 printf("%d:%s\n", ierr, strerror(ierr)); 213 } 214 pressanykey(clock_t tm)215 int pressanykey(clock_t tm) { 216 int inc; 217 218 printf("Press any key to continue. "); 219 inc = get_key(stdin, tm); 220 puts(""); 221 return inc; 222 } 223 dhcp_find_opt(pxe_bootp_t * p,size_t len,uint8_t opt)224 int dhcp_find_opt(pxe_bootp_t *p, size_t len, uint8_t opt) 225 { 226 int rv = -1; 227 int i, vlen, oplen; 228 uint8_t *d; 229 uint32_t magic; 230 231 if (!p) { 232 dprintf(" packet pointer is null\n"); 233 return rv; 234 } 235 vlen = len - ((void *)&(p->vendor) - (void *)p); 236 d = p->vendor.d; 237 magic = ntohl(*((uint32_t *)d)); 238 if (magic != VM_RFC1048) /* Invalid DHCP packet */ 239 vlen = 0; 240 for (i = 4; i < vlen; i++) { 241 if (d[i] == opt) { 242 dprintf("\n @%03X-%2d\n", i, d[i]); 243 rv = i; 244 break; 245 } 246 if (d[i] == ((NUM_DHCP_OPTS) - 1)) /* End of list */ 247 break; 248 if (d[i]) { /* Skip padding */ 249 oplen = d[++i]; 250 i += oplen; 251 } 252 } 253 return rv; 254 } 255 print_pxe_vendor_raw(pxe_bootp_t * p,size_t len)256 void print_pxe_vendor_raw(pxe_bootp_t *p, size_t len) 257 { 258 int i, vlen; 259 260 if (!p) { 261 printf(" packet pointer is null\n"); 262 return; 263 } 264 vlen = len - ((void *)&(p->vendor) - (void *)p); 265 if (vlen > PXE_VENDOR_RAW_PRN_MAX) 266 vlen = PXE_VENDOR_RAW_PRN_MAX; 267 dprintf(" rawLen = %d", vlen); 268 for (i = 0; i < vlen; i++) { 269 if ((i & 0xf) == 0) 270 printf("\n %04X:", i); 271 printf(" %02X", p->vendor.d[i]); 272 } 273 printf("\n"); 274 } 275 print_pxe_vendor_blk(pxe_bootp_t * p,size_t len)276 void print_pxe_vendor_blk(pxe_bootp_t *p, size_t len) 277 { 278 int i, vlen, oplen, j; 279 uint8_t *d; 280 uint32_t magic; 281 if (!p) { 282 printf(" packet pointer is null\n"); 283 return; 284 } 285 vlen = len - ((void *)&(p->vendor) - (void *)p); 286 printf(" Vendor Data: Len=%d", vlen); 287 d = p->vendor.d; 288 magic = ntohl(*((uint32_t *)d)); 289 printf(" Magic: %08X", ntohl(*((uint32_t *)d))); 290 if (magic != VM_RFC1048) /* Invalid DHCP packet */ 291 vlen = 0; 292 for (i = 4; i < vlen; i++) { 293 if (d[i]) /* Skip the padding */ 294 printf("\n @%03X-%3d", i, d[i]); 295 if (d[i] == ((NUM_DHCP_OPTS) - 1)) /* End of list */ 296 break; 297 if (d[i]) { 298 oplen = d[++i]; 299 printf(" l=%3d:", oplen); 300 for (j = (++i + oplen); i < vlen && i < j; i++) { 301 printf(" %02X", d[i]); 302 } 303 i--; 304 } 305 } 306 printf("\n"); 307 } 308 print_pxe_bootp_t(pxe_bootp_t * p,size_t len)309 void print_pxe_bootp_t(pxe_bootp_t *p, size_t len) 310 { 311 if (!p || len <= 0) { 312 printf(" packet pointer is null\n"); 313 return; 314 } 315 printf(" op:%02X hw:%02X hl:%02X gh:%02X id:%08X se:%04X f:%04X" 316 " cip:%08X\n", p->opcode, p->Hardware, p->Hardlen, p->Gatehops, 317 ntohl(p->ident), ntohs(p->seconds), ntohs(p->Flags), ntohl(p->cip)); 318 printf(" yip:%08X sip:%08X gip:%08X", 319 ntohl(p->yip), ntohl(p->sip), ntohl(p->gip)); 320 printf(" caddr-%02X:%02X:%02X:%02X:%02X:%02X\n", p->CAddr[0], 321 p->CAddr[1], p->CAddr[2], p->CAddr[3], p->CAddr[4], p->CAddr[5]); 322 printf(" sName: '%s'\n", p->Sname); 323 printf(" bootfile: '%s'\n", p->bootfile); 324 dprint_pxe_vendor_blk(p, len); 325 } 326 pxe_set_regs(struct syslinux_rm_regs * regs)327 void pxe_set_regs(struct syslinux_rm_regs *regs) 328 { 329 const union syslinux_derivative_info *sdi; 330 const com32sys_t *pxe_regs; 331 332 sdi = syslinux_derivative_info(); 333 pxe_regs = sdi->pxe.stack; /* Original register values */ 334 335 /* Just to be sure... */ 336 memset(regs, 0, sizeof *regs); 337 338 regs->ip = 0x7C00; 339 340 /* Point to the original stack */ 341 regs->ss = sdi->pxe.stack_seg; 342 regs->esp.l = sdi->pxe.stack_offs + sizeof(com32sys_t); 343 344 /* Point to the PXENV+ address */ 345 regs->es = pxe_regs->es; 346 regs->ebx.l = pxe_regs->ebx.l; 347 348 dprintf("\nsp:%04x ss:%04x es:%04x bx:%04x\n", regs->esp.w[0], 349 regs->ss, regs->es, regs->ebx.w[0]); 350 } 351 hostlen_limit(int len)352 int hostlen_limit(int len) 353 { 354 return min(len, ((PXECHN_HOST_LEN) - 1)); 355 } 356 357 //FIXME: To a library 358 /* Parse a filename into an IPv4 address and filename pointer 359 * returns Based on the interpretation of fn 360 * 0 regular file name 361 * 1 in format IP::FN 362 * 2 TFTP URL 363 * 3 HTTP URL 364 * 4 FTP URL 365 * 3 + 2^30 HTTPS URL 366 * -1 if fn is another URL type 367 */ pxechn_parse_fn(char fn[],in_addr_t * fip,char * host,char * fp[])368 int pxechn_parse_fn(char fn[], in_addr_t *fip, char *host, char *fp[]) 369 { 370 in_addr_t tip = 0; 371 char *csep, *ssep, *hsep; /* Colon, Slash separator positions */ 372 int hlen, plen; /* Hostname, protocol length */ 373 int rv = 0; 374 375 csep = strchr(fn, ':'); 376 if (csep) { 377 if (csep[1] == ':') { /* assume IP::FN */ 378 *fp = &csep[2]; 379 rv = 1; 380 if (fn[0] != ':') { 381 hlen = hostlen_limit(csep - fn); 382 memcpy(host, fn, hlen); 383 host[hlen] = 0; 384 } 385 } else if ((csep[1] == '/') && (csep[2] == '/')) { 386 /* URL: proto://host:port/path/file */ 387 /* proto://[user[:passwd]@]host[:port]/path/file */ 388 ssep = strchr(csep + 3, '/'); 389 if (ssep) { 390 hlen = hostlen_limit(ssep - (csep + 3)); 391 *fp = ssep + 1; 392 } else { 393 hlen = hostlen_limit(strlen(csep + 3)); 394 } 395 memcpy(host, (csep + 3), hlen); 396 host[hlen] = 0; 397 plen = csep - fn; 398 if (strncmp(fn, "tftp", plen) == 0) 399 rv = 2; 400 else if (strncmp(fn, "http", plen) == 0) 401 rv = 3; 402 else if (strncmp(fn, "ftp", plen) == 0) 403 rv = 4; 404 else if (strncmp(fn, "https", plen) == 0) 405 rv = 3 + ( 1 << 30 ); 406 else 407 rv = -1; 408 } else { 409 csep = NULL; 410 } 411 } 412 if (!csep) { 413 *fp = fn; 414 } 415 if (host[0]) { 416 hsep = strchr(host, '@'); 417 if (!hsep) 418 hsep = host; 419 tip = pxe_dns(hsep); 420 } 421 if (tip != 0) 422 *fip = tip; 423 dprintf0(" host '%s'\n fp '%s'\n fip %08x\n", host, *fp, ntohl(*fip)); 424 return rv; 425 } 426 pxechn_opt_free(struct dhcp_option * opt)427 void pxechn_opt_free(struct dhcp_option *opt) 428 { 429 free(opt->data); 430 opt->len = -1; 431 } 432 pxechn_fill_pkt(struct pxelinux_opt * pxe,int ptype)433 void pxechn_fill_pkt(struct pxelinux_opt *pxe, int ptype) 434 { 435 int rv = -1; 436 int p1, p2; 437 if ((ptype < 0) || (ptype > PXECHN_NUM_PKT_TYPE)) 438 rv = -2; 439 p1 = ptype - PXECHN_PKT_TYPE_START; 440 p2 = p1 + PXECHN_NUM_PKT_TYPE; 441 if ((rv >= -1) && (!pxe_get_cached_info(ptype, 442 (void **)&(pxe->p[p1].data), (size_t *)&(pxe->p[p1].len)))) { 443 pxe->p[p2].data = malloc(2048); 444 if (pxe->p[p2].data) { 445 memcpy(pxe->p[p2].data, pxe->p[p1].data, pxe->p[p1].len); 446 pxe->p[p2].len = pxe->p[p1].len; 447 rv = 0; 448 dprint_pxe_bootp_t((pxe_bootp_t *)(pxe->p[p1].data), pxe->p[p1].len); 449 dpressanykey(INT_MAX); 450 } else { 451 printf("%s: ERROR: Unable to malloc() for second packet\n", app_name_str); 452 } 453 } else { 454 printf("%s: ERROR: Unable to retrieve first packet\n", app_name_str); 455 } 456 if (rv <= -1) { 457 pxechn_opt_free(&pxe->p[p1]); 458 } 459 } 460 pxechn_init(struct pxelinux_opt * pxe)461 void pxechn_init(struct pxelinux_opt *pxe) 462 { 463 /* Init for paranoia */ 464 pxe->fn = NULL; 465 pxe->fp = NULL; 466 pxe->force = 0; 467 pxe->wait = 0; 468 pxe->gip = 0; 469 pxe->wds = 0; 470 pxe->sip = 0; 471 pxe->host[0] = 0; 472 pxe->host[((NUM_DHCP_OPTS) - 1)] = 0; 473 for (int j = 0; j < PXECHN_NUM_PKT_TYPE; j++){ 474 for (int i = 0; i < NUM_DHCP_OPTS; i++) { 475 pxe->opts[j][i].data = NULL; 476 pxe->opts[j][i].len = -1; 477 } 478 pxe->p_unpacked[j] = 0; 479 pxe->p[j].data = NULL; 480 pxe->p[j+PXECHN_NUM_PKT_TYPE].data = NULL; 481 pxe->p[j].len = 0; 482 pxe->p[j+PXECHN_NUM_PKT_TYPE].len = 0; 483 } 484 pxechn_fill_pkt(pxe, PXENV_PACKET_TYPE_CACHED_REPLY); 485 } 486 pxechn_to_hex(char i)487 int pxechn_to_hex(char i) 488 { 489 if (i >= '0' && i <= '9') 490 return (i - '0'); 491 if (i >= 'A' && i <= 'F') 492 return (i - 'A' + 10); 493 if (i >= 'a' && i <= 'f') 494 return (i - 'a' + 10); 495 if (i == 0) 496 return -1; 497 return -2; 498 } 499 pxechn_parse_2bhex(char ins[])500 int pxechn_parse_2bhex(char ins[]) 501 { 502 int ret = -2; 503 int n0 = -3, n1 = -3; 504 /* NULL pointer */ 505 if (!ins) { 506 ret = -1; 507 /* pxechn_to_hex can handle the NULL character by returning -1 and 508 breaking the execution of the statement chain */ 509 } else if (((n0 = pxechn_to_hex(ins[0])) >= 0) 510 && ((n1 = pxechn_to_hex(ins[1])) >= 0)) { 511 ret = (n0 * 16) + n1; 512 } else if (n0 == -1) { /* Leading NULL char */ 513 ret = -1; 514 } 515 return ret; 516 } 517 pxechn_optnum_ok(int optnum)518 int pxechn_optnum_ok(int optnum) 519 { 520 if ((optnum > 0) && (optnum < ((NUM_DHCP_OPTS) - 1))) 521 return 1; 522 return 0; 523 } 524 pxechn_optnum_ok_notres(int optnum)525 int pxechn_optnum_ok_notres(int optnum) 526 { 527 if ((optnum <= 0) && (optnum >= ((NUM_DHCP_OPTS) - 1))) 528 return 0; 529 switch(optnum){ 530 case 66: case 67: 531 return 0; 532 break; 533 default: return 1; 534 } 535 } 536 pxechn_optlen_ok(int optlen)537 int pxechn_optlen_ok(int optlen) 538 { 539 if ((optlen >= 0) && (optlen < ((DHCP_OPT_LEN_MAX) - 1))) 540 return 1; 541 return 0; 542 } 543 pxechn_setopt(struct dhcp_option * opt,void * data,int len)544 int pxechn_setopt(struct dhcp_option *opt, void *data, int len) 545 { 546 void *p; 547 if (!opt || !data) 548 return -1; 549 if (len < 0) { 550 return -3; 551 } 552 p = realloc(opt->data, len); 553 if (!p && len) { /* Allow for len=0 */ 554 pxechn_opt_free(opt); 555 return -2; 556 } 557 opt->data = p; 558 memcpy(opt->data, data, len); 559 opt->len = len; 560 return len; 561 } 562 pxechn_setopt_str(struct dhcp_option * opt,void * data)563 int pxechn_setopt_str(struct dhcp_option *opt, void *data) 564 { 565 return pxechn_setopt(opt, data, strnlen(data, DHCP_OPT_LEN_MAX)); 566 } 567 pxechn_parse_int(char * data,char istr[],int tlen)568 int pxechn_parse_int(char *data, char istr[], int tlen) 569 { 570 int terr = errno; 571 572 if ((tlen == 1) || (tlen == 2) || (tlen == 4)) { 573 errno = 0; 574 uint32_t optval = strtoul(istr, NULL, 0); 575 if (errno) 576 return -3; 577 errno = terr; 578 switch(tlen){ 579 case 1: 580 if (optval & 0xFFFFFF00) 581 return -4; 582 break; 583 case 2: 584 if (optval & 0xFFFF0000) 585 return -4; 586 optval = htons(optval); 587 break; 588 case 4: 589 optval = htonl(optval); 590 break; 591 } 592 memcpy(data, &optval, tlen); 593 } else if (tlen == 8) { 594 errno = 0; 595 uint64_t optval = strtoull(istr, NULL, 0); 596 if (errno) 597 return -3; 598 errno = terr; 599 optval = htonq(optval); 600 memcpy(data, &optval, tlen); 601 } else { 602 return -2; 603 } 604 return tlen; 605 } 606 pxechn_parse_hex_sep(char * data,char istr[],char sep)607 int pxechn_parse_hex_sep(char *data, char istr[], char sep) 608 { 609 int len = 0; 610 int ipos = 0, ichar; 611 612 if (!data || !istr) 613 return -1; 614 while ((istr[ipos]) && (len < DHCP_OPT_LEN_MAX)) { 615 dprintf(" %02X%02X", *((int *)(istr + ipos)) & 0xFF, *((int *)(istr + ipos +1)) & 0xFF); 616 ichar = pxechn_parse_2bhex(istr + ipos); 617 if (ichar >=0) { 618 data[len++] = ichar; 619 } else { 620 return -EINVAL; 621 } 622 if (!istr[ipos+2]){ 623 ipos += 2; 624 } else if (istr[ipos+2] != sep) { 625 return -(EINVAL + 1); 626 } else { 627 ipos += 3; 628 } 629 } 630 return len; 631 } 632 pxechn_parse_opttype(char istr[],int optnum)633 int pxechn_parse_opttype(char istr[], int optnum) 634 { 635 char *pos; 636 int tlen, type, tmask; 637 638 if (!istr) 639 return -1; 640 pos = strchr(istr, '='); 641 if (!pos) 642 return -2; 643 if (istr[0] != '.') { 644 if (!pxechn_optnum_ok(optnum)) 645 return -3; 646 return -3; /* do lookup here */ 647 } else { 648 tlen = pos - istr - 1; 649 if ((tlen < 1) || (tlen > 4)) 650 return -4; 651 tmask = 0xFFFFFFFF >> (8 * (4 - tlen)); 652 type = (*(int*)(istr + 1)) & tmask; 653 } 654 return type; 655 } 656 pxechn_parse_setopt(struct dhcp_option opts[],struct dhcp_option * iopt,char istr[])657 int pxechn_parse_setopt(struct dhcp_option opts[], struct dhcp_option *iopt, 658 char istr[]) 659 { 660 int rv = 0, optnum, opttype; 661 char *cpos = NULL, *pos; 662 663 if (!opts || !iopt || !(iopt->data)) 664 return -1; 665 if (!istr || !istr[0]) 666 return -2; 667 // -EINVAL; 668 optnum = strtoul(istr, &cpos, 0); 669 if (!pxechn_optnum_ok(optnum)) 670 return -3; 671 pos = strchr(cpos, '='); 672 if (!pos) 673 return -4; 674 opttype = pxechn_parse_opttype(cpos, optnum); 675 pos++; 676 switch(opttype) { 677 case 'b': 678 iopt->len = pxechn_parse_int(iopt->data, pos, 1); 679 break; 680 case 'l': 681 iopt->len = pxechn_parse_int(iopt->data, pos, 4); 682 break; 683 case 'q': 684 iopt->len = pxechn_parse_int(iopt->data, pos, 8); 685 break; 686 case 's': 687 case STRASINT_str: 688 iopt->len = strlen(pos); 689 if (iopt->len > DHCP_OPT_LEN_MAX) 690 iopt->len = DHCP_OPT_LEN_MAX; 691 memcpy(iopt->data, pos, iopt->len); 692 dprintf_pc_so_s("s.len=%d\trv=%d\n", iopt->len, rv); 693 break; 694 case 'w': 695 iopt->len = pxechn_parse_int(iopt->data, pos, 2); 696 break; 697 case 'x': 698 iopt->len = pxechn_parse_hex_sep(iopt->data, pos, ':'); 699 break; 700 default: 701 return -6; 702 break; 703 } 704 if (pxechn_optlen_ok(iopt->len)) { 705 rv = pxechn_setopt(&(opts[optnum]), (void *)(iopt->data), iopt->len); 706 } 707 if((opttype == 's') || (opttype == STRASINT_str)) 708 dprintf_pc_so_s("rv=%d\n", rv); 709 return rv; 710 } 711 pxechn_parse_force(const char istr[])712 int pxechn_parse_force(const char istr[]) 713 { 714 uint32_t rv = 0; 715 char *pos; 716 int terr = errno; 717 718 errno = 0; 719 rv = strtoul(istr, &pos, 0); 720 if ((istr == pos ) || ((rv == ULONG_MAX) && (errno))) 721 rv = 0; 722 errno = terr; 723 return rv; 724 } 725 pxechn_uuid_set(struct pxelinux_opt * pxe)726 int pxechn_uuid_set(struct pxelinux_opt *pxe) 727 { 728 int ret = 0; 729 730 if (!pxe->p_unpacked[0]) 731 ret = dhcp_unpack_packet((pxe_bootp_t *)(pxe->p[0].data), 732 pxe->p[0].len, pxe->opts[0]); 733 if (ret) { 734 error("Could not unpack packet\n"); 735 return -ret; /* dhcp_unpack_packet always returns positive errors */ 736 } 737 738 if (pxe->opts[0][97].len >= 0 ) 739 pxechn_setopt(&(pxe->opts[2][97]), pxe->opts[0][97].data, pxe->opts[0][97].len); 740 return 1; 741 return 0; 742 } 743 pxechn_parse_args(int argc,char * argv[],struct pxelinux_opt * pxe,struct dhcp_option opts[])744 int pxechn_parse_args(int argc, char *argv[], struct pxelinux_opt *pxe, 745 struct dhcp_option opts[]) 746 { 747 int arg, optnum, rv = 0; 748 char *p = NULL; 749 const char optstr[] = "c:f:g:o:p:St:uwW"; 750 struct dhcp_option iopt; 751 752 if (pxe->p[5].data) 753 pxe->fip = ( (pxe_bootp_t *)(pxe->p[5].data) )->sip; 754 else 755 pxe->fip = 0; 756 /* Fill */ 757 pxe->fn = argv[0]; 758 pxechn_parse_fn(pxe->fn, &(pxe->fip), pxe->host, &(pxe->fp)); 759 pxechn_setopt_str(&(opts[67]), pxe->fp); 760 pxechn_setopt_str(&(opts[66]), pxe->host); 761 iopt.data = malloc(DHCP_OPT_LEN_MAX); 762 iopt.len = 0; 763 while ((rv >= 0) && (arg = getopt(argc, argv, optstr)) >= 0) { 764 dprintf_pc_pa(" Got arg '%c'/'%c' addr %08X val %s\n", arg == '?' ? optopt : arg, arg, (unsigned int)optarg, optarg ? optarg : ""); 765 switch(arg) { 766 case 'c': /* config */ 767 pxechn_setopt_str(&(opts[209]), optarg); 768 break; 769 case 'f': /* force */ 770 pxe->force = pxechn_parse_force(optarg); 771 break; 772 case 'g': /* gateway/DHCP relay */ 773 pxe->gip = pxe_dns(optarg); 774 break; 775 case 'n': /* native */ 776 break; 777 case 'o': /* option */ 778 rv = pxechn_parse_setopt(opts, &iopt, optarg); 779 break; 780 case 'p': /* prefix */ 781 pxechn_setopt_str(&(opts[210]), optarg); 782 break; 783 case 'S': /* sip from sName */ 784 pxe->sip = 1; 785 break; 786 case 't': /* timeout */ 787 optnum = strtoul(optarg, &p, 0); 788 if (p != optarg) { 789 optnum = htonl(optnum); 790 pxechn_setopt(&(opts[211]), (void *)(&optnum), 4); 791 } else { 792 rv = -3; 793 } 794 break; 795 case 'u': /* UUID: copy option 97 from packet 1 if present */ 796 pxechn_uuid_set(pxe); 797 break; 798 case 'w': /* wait */ 799 pxe->wait = 1; 800 break; 801 case 'W': /* WDS */ 802 pxe->wds = 1; 803 break; 804 case '?': 805 rv = -'?'; 806 default: 807 break; 808 } 809 if (rv >= 0) /* Clear it since getopt() doesn't guarentee it */ 810 optarg = NULL; 811 } 812 if (iopt.data) 813 pxechn_opt_free(&iopt); 814 /* FIXME: consider reordering the application of parsed command line options 815 such that the new nbp may be at the end */ 816 if (rv >= 0) { 817 rv = 0; 818 } else if (arg != '?') { 819 printf("Invalid argument for -%c: %s\n", arg, optarg); 820 } 821 dprintf("pxechn_parse_args rv=%d\n", rv); 822 return rv; 823 } 824 pxechn_args(int argc,char * argv[],struct pxelinux_opt * pxe)825 int pxechn_args(int argc, char *argv[], struct pxelinux_opt *pxe) 826 { 827 pxe_bootp_t *bootp0, *bootp1; 828 int ret = 0; 829 struct dhcp_option *opts; 830 char *str; 831 832 opts = pxe->opts[2]; 833 /* Start filling packet #1 */ 834 bootp0 = (pxe_bootp_t *)(pxe->p[2].data); 835 bootp1 = (pxe_bootp_t *)(pxe->p[5].data); 836 837 ret = dhcp_unpack_packet(bootp0, pxe->p[2].len, opts); 838 if (ret) { 839 error("Could not unpack packet\n"); 840 return -ret; 841 } 842 pxe->p_unpacked[2] = 1; 843 pxe->gip = bootp1->gip; 844 845 ret = pxechn_parse_args(argc, argv, pxe, opts); 846 if (ret) 847 return ret; 848 if (pxe->sip > 0xFFFFFF) { /* a real IPv4 address */ 849 bootp1->sip = pxe->sip; 850 } else if ((pxe->sip == 1) 851 && (opts[66].len > 0)){ 852 /* unterminated? */ 853 if (strnlen(opts[66].data, opts[66].len) == (size_t)opts[66].len) { 854 str = malloc(opts[66].len + 1); 855 if (str) { 856 memcpy(str, opts[66].data, opts[66].len); 857 str[opts[66].len] = 0; 858 } 859 } else { 860 str = opts[66].data; 861 } 862 if (str) { 863 bootp1->sip = pxe_dns(str); 864 if (str != opts[66].data) 865 free(str); 866 } else { 867 bootp1->sip = pxe->fip; 868 } 869 } else { 870 bootp1->sip = pxe->fip; 871 } 872 bootp1->gip = pxe->gip; 873 874 ret = dhcp_pack_packet(bootp1, (size_t *)&(pxe->p[5].len), opts); 875 if (ret) { 876 error("Could not pack packet\n"); 877 return -ret; /* dhcp_pack_packet always returns positive errors */ 878 } 879 return ret; 880 } 881 882 /* dhcp_pkt2pxe: Copy packet to PXE's BC data for a ptype packet 883 * Input: 884 * p Packet data to copy 885 * len length of data to copy 886 * ptype Packet type to overwrite 887 */ dhcp_pkt2pxe(pxe_bootp_t * p,size_t len,int ptype)888 int dhcp_pkt2pxe(pxe_bootp_t *p, size_t len, int ptype) 889 { 890 t_PXENV_GET_CACHED_INFO *ci; 891 void *cp; 892 int rv = -1; 893 894 if (!(ci = lzalloc(sizeof(t_PXENV_GET_CACHED_INFO)))){ 895 dprintf("Unable to lzalloc() for PXE call structure\n"); 896 rv = 1; 897 goto ret; 898 } 899 ci->Status = PXENV_STATUS_FAILURE; 900 ci->PacketType = ptype; 901 pxe_call(PXENV_GET_CACHED_INFO, ci); 902 903 if (ci->Status != PXENV_STATUS_SUCCESS) { 904 dprintf("PXE Get Cached Info failed: %d\n", ci->Status); 905 rv = 2; 906 goto ret; 907 } 908 909 cp = MK_PTR(ci->Buffer.seg, ci->Buffer.offs); 910 if (!(memcpy(cp, p, len))) { 911 dprintf("Failed to copy packet\n"); 912 rv = 3; 913 goto ret; 914 } 915 ret: 916 lfree(ci); 917 return rv; 918 } 919 pxechn_mergeopt(struct pxelinux_opt * pxe,int d,int s)920 int pxechn_mergeopt(struct pxelinux_opt *pxe, int d, int s) 921 { 922 int ret = 0, i; 923 924 if ((d >= PXECHN_NUM_PKT_TYPE) || (s >= PXECHN_NUM_PKT_TYPE) 925 || (d < 0) || (s < 0)) { 926 return -2; 927 } 928 if (!pxe->p_unpacked[s]) 929 ret = dhcp_unpack_packet(pxe->p[s].data, pxe->p[s].len, pxe->opts[s]); 930 if (ret) { 931 error("Could not unpack packet for merge\n"); 932 printf("Error %d (%d)\n", ret, EINVAL); 933 if (ret == EINVAL) { 934 if (pxe->p[s].len < 240) 935 printf("Packet %d is too short: %d (240)\n", s, pxe->p[s].len); 936 else if (((const struct dhcp_packet *)(pxe->p[s].data))->magic != htonl(DHCP_VENDOR_MAGIC)) 937 printf("Packet %d has no magic\n", s); 938 else 939 error("Unknown EINVAL error\n"); 940 } else { 941 error("Unknown error\n"); 942 } 943 return -ret; 944 } 945 for (i = 0; i < NUM_DHCP_OPTS; i++) { 946 if (pxe->opts[d][i].len <= -1) { 947 if (pxe->opts[s][i].len >= 0) 948 pxechn_setopt(&(pxe->opts[d][i]), pxe->opts[s][i].data, pxe->opts[s][i].len); 949 } 950 } 951 return 0; 952 } 953 954 /* pxechn: Chainload to new PXE file ourselves 955 * Input: 956 * argc Count of arguments passed 957 * argv Values of arguments passed 958 * Returns 0 on success (which should never happen) 959 * 1 on loadfile() error 960 * 2 if DHCP Option 52 (Option Overload) used file field 961 * -1 on usage error 962 */ pxechn(int argc,char * argv[])963 int pxechn(int argc, char *argv[]) 964 { 965 struct pxelinux_opt pxe; 966 pxe_bootp_t* p[(2 * PXECHN_NUM_PKT_TYPE)]; 967 int rv = 0; 968 int i; 969 struct data_area file; 970 struct syslinux_rm_regs regs; 971 972 pxechn_init(&pxe); 973 for (i = 0; i < (2 * PXECHN_NUM_PKT_TYPE); i++) { 974 p[i] = (pxe_bootp_t *)(pxe.p[i].data); 975 } 976 977 /* Parse arguments and patch packet 1 */ 978 rv = pxechn_args(argc, argv, &pxe); 979 dpressanykey(INT_MAX); 980 if (rv) 981 goto ret; 982 pxe_set_regs(®s); 983 /* Load the file late; it's the most time-expensive operation */ 984 printf("%s: Attempting to load '%s': ", app_name_str, pxe.fn); 985 if (loadfile(pxe.fn, &file.data, &file.size)) { 986 pxe_error(errno, NULL, NULL); 987 rv = -2; 988 goto ret; 989 } 990 puts("loaded."); 991 /* we'll be shuffling to the standard location of 7C00h */ 992 file.base = 0x7C00; 993 if ((pxe.wds) || 994 ((pxe.force) && ((pxe.force & (~PXECHN_FORCE_ALL)) == 0))) { 995 printf("Forcing behavior %08X\n", pxe.force); 996 // P2 is the same as P3 if no PXE server present. 997 if ((pxe.wds) || 998 (pxe.force & PXECHN_FORCE_PKT2)) { 999 pxechn_fill_pkt(&pxe, PXENV_PACKET_TYPE_DHCP_ACK); 1000 rv = pxechn_mergeopt(&pxe, 2, 1); 1001 if (rv) { 1002 dprintf("Merge Option returned %d\n", rv); 1003 } 1004 rv = dhcp_pack_packet(p[5], (size_t *)&(pxe.p[5].len), pxe.opts[2]); 1005 rv = dhcp_pkt2pxe(p[5], pxe.p[5].len, PXENV_PACKET_TYPE_DHCP_ACK); 1006 } 1007 if (pxe.force & PXECHN_FORCE_PKT1) { 1008 puts("Unimplemented force option utilized"); 1009 } 1010 } 1011 rv = dhcp_pkt2pxe(p[5], pxe.p[5].len, PXENV_PACKET_TYPE_CACHED_REPLY); 1012 dprint_pxe_bootp_t(p[5], pxe.p[5].len); 1013 if ((pxe.wds) || 1014 ((pxe.force) && ((pxe.force & (~PXECHN_FORCE_ALL)) == 0))) { 1015 // printf("Forcing behavior %08X\n", pxe.force); 1016 // P2 is the same as P3 if no PXE server present. 1017 if ((pxe.wds) || 1018 (pxe.force & PXECHN_FORCE_PKT2)) { 1019 rv = dhcp_pkt2pxe(p[5], pxe.p[5].len, PXENV_PACKET_TYPE_DHCP_ACK); 1020 } 1021 } else if (pxe.force) { 1022 printf("FORCE: bad argument %08X\n", pxe.force); 1023 } 1024 printf("\n...Ready to boot:\n"); 1025 if (pxe.wait) { 1026 pressanykey(INT_MAX); 1027 } else { 1028 dpressanykey(INT_MAX); 1029 } 1030 if (true) { 1031 puts(" Attempting to boot..."); 1032 do_boot(&file, 1, ®s); 1033 } 1034 /* If failed, copy backup back in and abort */ 1035 dhcp_pkt2pxe(p[2], pxe.p[2].len, PXENV_PACKET_TYPE_CACHED_REPLY); 1036 if (pxe.force && ((pxe.force & (~PXECHN_FORCE_ALL)) == 0)) { 1037 if (pxe.force & PXECHN_FORCE_PKT2) { 1038 rv = dhcp_pkt2pxe(p[1], pxe.p[1].len, PXENV_PACKET_TYPE_DHCP_ACK); 1039 } 1040 } 1041 ret: 1042 return rv; 1043 } 1044 1045 /* pxe_restart: Restart the PXE environment with a new PXE file 1046 * Input: 1047 * ifn Name of file to chainload to in a format PXELINUX understands 1048 * This must strictly be TFTP or relative file 1049 */ pxe_restart(char * ifn)1050 int pxe_restart(char *ifn) 1051 { 1052 int rv = 0; 1053 struct pxelinux_opt pxe; 1054 t_PXENV_RESTART_TFTP *pxep; /* PXENV callback Parameter */ 1055 1056 pxe.fn = ifn; 1057 pxechn_fill_pkt(&pxe, PXENV_PACKET_TYPE_CACHED_REPLY); 1058 if (pxe.p[5].data) 1059 pxe.fip = ( (pxe_bootp_t *)(pxe.p[5].data) )->sip; 1060 else 1061 pxe.fip = 0; 1062 rv = pxechn_parse_fn(pxe.fn, &(pxe.fip), pxe.host, &(pxe.fp)); 1063 if ((rv > 2) || (rv < 0)) { 1064 printf("%s: ERROR: Unparsable filename argument: '%s'\n\n", app_name_str, pxe.fn); 1065 goto ret; 1066 } 1067 printf(" Attempting to boot '%s'...\n\n", pxe.fn); 1068 if (!(pxep = lzalloc(sizeof(t_PXENV_RESTART_TFTP)))){ 1069 dprintf("Unable to lzalloc() for PXE call structure\n"); 1070 goto ret; 1071 } 1072 pxep->Status = PXENV_STATUS_SUCCESS; /* PXENV_STATUS_FAILURE */ 1073 strcpy((char *)pxep->FileName, ifn); 1074 pxep->BufferSize = 0x8000; 1075 pxep->Buffer = (void *)0x7c00; 1076 pxep->ServerIPAddress = pxe.fip; 1077 dprintf("FN='%s' %08X %08X %08X %08X\n\n", (char *)pxep->FileName, 1078 pxep->ServerIPAddress, (unsigned int)pxep, 1079 pxep->BufferSize, (unsigned int)pxep->Buffer); 1080 dprintf("PXENV_RESTART_TFTP status %d\n", pxep->Status); 1081 1082 pxe_call(PXENV_RESTART_TFTP, pxep); 1083 1084 printf("PXENV_RESTART_TFTP returned %d\n", pxep->Status); 1085 lfree(pxep); 1086 1087 ret: 1088 return rv; 1089 } 1090 1091 /* pxechn_gpxe: Use gPXE to chainload a new NBP 1092 * Input: 1093 * argc Count of arguments passed 1094 * argv Values of arguments passed 1095 * Returns 0 on success (which should never happen) 1096 * 1 on loadfile() error 1097 * -1 on usage error 1098 */ 1099 //FIXME:Implement pxechn_gpxe(int argc,char * argv[])1100 int pxechn_gpxe(int argc, char *argv[]) 1101 { 1102 int rv = 0; 1103 struct pxelinux_opt pxe; 1104 1105 if (argc) { 1106 printf("%s\n", argv[0]); 1107 pxechn_args(argc, argv, &pxe); 1108 } 1109 return rv; 1110 } 1111 main(int argc,char * argv[])1112 int main(int argc, char *argv[]) 1113 { 1114 int rv= -1; 1115 int err; 1116 const struct syslinux_version *sv; 1117 1118 /* Initialization */ 1119 err = errno; 1120 console_ansi_raw(); /* sets errno = 9 (EBADF) */ 1121 /* printf("%d %d\n", err, errno); */ 1122 errno = err; 1123 sv = syslinux_version(); 1124 if (sv->filesystem != SYSLINUX_FS_PXELINUX) { 1125 printf("%s: May only run in PXELINUX\n", app_name_str); 1126 argc = 1; /* prevents further processing to boot */ 1127 } 1128 if (argc == 2) { 1129 if ((strcasecmp(argv[1], "-h") == 0) || ((strcmp(argv[1], "-?") == 0)) 1130 || (strcasecmp(argv[1], "--help") == 0)) { 1131 argc = 1; 1132 } else { 1133 rv = pxechn(argc - 1, &argv[1]); 1134 } 1135 } else if (argc >= 3) { 1136 if ((strcmp(argv[1], "-r") == 0)) { 1137 if (argc == 3) 1138 rv = pxe_restart(argv[2]); 1139 } else { 1140 rv = pxechn(argc - 1, &argv[1]); 1141 } 1142 } 1143 if (rv <= -1 ) { 1144 usage(); 1145 rv = 1; 1146 } 1147 return rv; 1148 } 1149