source: bootcd/isolinux/syslinux-6.03/com32/modules/pxechn.c @ dd1be7c

Last change on this file since dd1be7c was e16e8f2, checked in by Edwin Eefting <edwin@datux.nl>, 3 years ago

bootstuff

  • Property mode set to 100644
File size: 28.3 KB
RevLine 
[e16e8f2]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
52typedef 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
110const char app_name_str[] = "pxechn.c32";
111
112struct 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 */
130struct data_area {
131    void *data;
132    addr_t base;
133    addr_t size;
134};
135
136/* From chain.c */
137static inline void error(const char *msg)
138{
139    fputs(msg, stderr);
140}
141
142/* From chain.c */
143static 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
186too_big:
187    error("Loader file too large\n");
188    return;
189
190enomem:
191    error("Out of memory\n");
192    return;
193}
194
195void 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
206void 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
215int 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
224int 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
256void 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
276void 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
309void 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
327void 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
352int 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 */
368int 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
427void pxechn_opt_free(struct dhcp_option *opt)
428{
429    free(opt->data);
430    opt->len = -1;
431}
432
433void 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
461void 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
487int 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
500int 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
518int pxechn_optnum_ok(int optnum)
519{
520    if ((optnum > 0) && (optnum < ((NUM_DHCP_OPTS) - 1)))
521        return 1;
522    return 0;
523}
524
525int 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
537int pxechn_optlen_ok(int optlen)
538{
539    if ((optlen >= 0) && (optlen < ((DHCP_OPT_LEN_MAX) - 1)))
540        return 1;
541    return 0;
542}
543
544int 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
563int pxechn_setopt_str(struct dhcp_option *opt, void *data)
564{
565    return pxechn_setopt(opt, data, strnlen(data, DHCP_OPT_LEN_MAX));
566}
567
568int 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
607int 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
633int 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
657int 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
712int 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
726int 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
744int 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
825int 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 */
888int 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    }
915ret:
916    lfree(ci);
917   return rv;
918}
919
920int 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 */
963int 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(&regs);
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, &regs);
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    }
1041ret:
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 */
1050int 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
1087ret:
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
1100int 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
1112int 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}
Note: See TracBrowser for help on using the repository browser.