diff options
-rw-r--r-- | lib/libnet/Makefile | 2 | ||||
-rw-r--r-- | lib/libnet/pxelinux.c | 212 | ||||
-rw-r--r-- | lib/libnet/pxelinux.h | 33 |
3 files changed, 246 insertions, 1 deletions
diff --git a/lib/libnet/Makefile b/lib/libnet/Makefile index dfefea9..a2a6570 100644 --- a/lib/libnet/Makefile +++ b/lib/libnet/Makefile @@ -19,7 +19,7 @@ include $(TOP)/make.rules CFLAGS += -I. -I.. -I../libc/include -I$(TOP)/include $(FLAG) SRCS = ethernet.c ipv4.c udp.c tcp.c dns.c bootp.c dhcp.c tftp.c \ - ipv6.c dhcpv6.c icmpv6.c ndp.c netload.c ping.c args.c + ipv6.c dhcpv6.c icmpv6.c ndp.c netload.c ping.c args.c pxelinux.c OBJS = $(SRCS:%.c=%.o) diff --git a/lib/libnet/pxelinux.c b/lib/libnet/pxelinux.c new file mode 100644 index 0000000..d702811 --- /dev/null +++ b/lib/libnet/pxelinux.c @@ -0,0 +1,212 @@ +/***************************************************************************** + * pxelinux.cfg-style config file support. + * + * See https://www.syslinux.org/wiki/index.php?title=PXELINUX for information + * about the pxelinux config file layout. + * + * Copyright 2018 Red Hat, Inc. + * + * This program and the accompanying materials are made available under the + * terms of the BSD License which accompanies this distribution, and is + * available at http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * Thomas Huth, Red Hat Inc. - initial implementation + *****************************************************************************/ + +#include <stdio.h> +#include <string.h> +#include "tftp.h" +#include "pxelinux.h" + +/** + * Call tftp() and report errors (excet "file-not-found" errors) + */ +static int pxelinux_tftp_load(filename_ip_t *fnip, void *buffer, int len, + int retries) +{ + tftp_err_t tftp_err; + int rc, ecode; + + rc = tftp(fnip, buffer, len, retries, &tftp_err); + + if (rc > 0) { + printf("\r TFTP: Received %s (%d bytes)\n", + fnip->filename, rc); + } else if (rc == -3) { + /* Ignore file-not-found (since we are probing the files) + * and simply erase the "Receiving data: 0 KBytes" string */ + printf("\r \r"); + } else { + const char *errstr = NULL; + rc = tftp_get_error_info(fnip, &tftp_err, rc, &errstr, &ecode); + if (errstr) + printf("\r TFTP error: %s\n", errstr); + } + + return rc; +} + +/** + * Try to load a pxelinux.cfg file by probing the possible file names. + * Note that this function will overwrite filename_ip_t->filename. + */ +static int pxelinux_load_cfg(filename_ip_t *fn_ip, uint8_t *mac, + int retries, char *cfgbuf, int cfgbufsize) +{ + int rc, idx; + char *baseptr; + + /* Try to get a usable base directory from the DHCP bootfile name */ + baseptr = strrchr(fn_ip->filename, '/'); + if (!baseptr) + baseptr = fn_ip->filename; + else + ++baseptr; + /* Check that we've got enough space to store "pxelinux.cfg/" and + * the UUID (which is the longest file name) there */ + if (baseptr - fn_ip->filename > sizeof(fn_ip->filename) - 50) { + puts("Error: The bootfile string is too long for deriving the " + "pxelinux.cfg file name from it."); + return -1; + } + strcpy(baseptr, "pxelinux.cfg/"); + baseptr += strlen(baseptr); + + puts("Trying pxelinux.cfg files..."); + + /* Look for config file with MAC address in its name */ + sprintf(baseptr, "01-%02x-%02x-%02x-%02x-%02x-%02x", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + rc = pxelinux_tftp_load(fn_ip, cfgbuf, cfgbufsize - 1, retries); + if (rc > 0) { + return rc; + } + + /* Look for config file with IP address in its name */ + if (fn_ip->ip_version == 4) { + sprintf(baseptr, "%02X%02X%02X%02X", + (fn_ip->own_ip >> 24) & 0xff, + (fn_ip->own_ip >> 16) & 0xff, + (fn_ip->own_ip >> 8) & 0xff, + fn_ip->own_ip & 0xff); + for (idx = 0; idx <= 7; idx++) { + baseptr[8 - idx] = 0; + rc = pxelinux_tftp_load(fn_ip, cfgbuf, cfgbufsize - 1, + retries); + if (rc > 0) { + return rc; + } + } + } + + /* Try "default" config file */ + strcpy(baseptr, "default"); + rc = pxelinux_tftp_load(fn_ip, cfgbuf, cfgbufsize - 1, retries); + + return rc; +} + +/** + * Parse a pxelinux-style configuration file. + * The discovered entries are filled into the "struct pl_cfg_entry entries[]" + * array. Note that the callers must keep the cfg buffer valid as long as + * they wish to access the "struct pl_cfg_entry" entries, since the pointers + * in entries point to the original location in the cfg buffer area. The cfg + * buffer is altered for this, too, e.g. terminating NUL-characters are put + * into the right locations. + * @param cfg Pointer to the buffer with contents of the config file + * @param cfgsize Size of the cfg buffer + * @param entries Pointer to array where the results should be put into + * @param max_entries Number of available slots in the entries array + * @param def_ent Used to return the index of the default entry + * @return Number of valid entries + */ +int pxelinux_parse_cfg(char *cfg, int cfgsize, struct pl_cfg_entry *entries, + int max_entries, int *def_ent) +{ + int num_entries = 0; + char *ptr = cfg, *nextptr, *eol, *arg; + char *defaultlabel = NULL; + + *def_ent = 0; + + cfg[cfgsize - 1] = 0; /* Make sure it is NUL-terminated */ + + while (ptr < cfg + cfgsize && num_entries < max_entries) { + eol = strchr(ptr, '\n'); + if (!eol) { + eol = cfg + cfgsize; + } + nextptr = eol + 1; + do { + *eol-- = '\0'; /* Remove spaces, tabs and returns */ + } while (eol >= ptr && + (*eol == '\r' || *eol == ' ' || *eol == '\t')); + while (*ptr == ' ' || *ptr == '\t') { + ptr++; + } + if (*ptr == 0 || *ptr == '#') { + goto nextline; /* Ignore comments and empty lines */ + } + arg = strchr(ptr, ' '); /* Search space between cmnd and arg */ + if (!arg) { + arg = strchr(ptr, '\t'); + } + if (!arg) { + printf("Failed to parse this line:\n %s\n", ptr); + goto nextline; + } + *arg++ = 0; + while (*arg == ' ' || *arg == '\t') { + arg++; + } + if (!strcasecmp("default", ptr)) { + defaultlabel = arg; + } else if (!strcasecmp("label", ptr)) { + entries[num_entries].label = arg; + if (defaultlabel && !strcmp(arg, defaultlabel)) { + *def_ent = num_entries; + } + num_entries++; + } else if (!strcasecmp("kernel", ptr) && num_entries) { + entries[num_entries - 1].kernel = arg; + } else if (!strcasecmp("initrd", ptr) && num_entries) { + entries[num_entries - 1].initrd = arg; + } else if (!strcasecmp("append", ptr) && num_entries) { + entries[num_entries - 1].append = arg; + } else { + printf("Command '%s' is not supported.\n", ptr); + } +nextline: + ptr = nextptr; + } + + return num_entries; +} + +/** + * Try to load and parse a pxelinux-style configuration file. + * @param fn_ip must contain server and client IP information + * @param mac MAC address which should be used for probing + * @param retries Amount of TFTP retries before giving up + * @param cfgbuf Pointer to the buffer where config file should be loaded + * @param cfgsize Size of the cfgbuf buffer + * @param entries Pointer to array where the results should be put into + * @param max_entries Number of available slots in the entries array + * @param def_ent Used to return the index of the default entry + * @return Number of valid entries + */ +int pxelinux_load_parse_cfg(filename_ip_t *fn_ip, uint8_t *mac, + int retries, char *cfgbuf, int cfgsize, + struct pl_cfg_entry *entries, int max_entries, + int *def_ent) +{ + int rc; + + rc = pxelinux_load_cfg(fn_ip, mac, retries, cfgbuf, cfgsize); + if (rc < 0) + return rc; + + return pxelinux_parse_cfg(cfgbuf, rc, entries, max_entries, def_ent); +} diff --git a/lib/libnet/pxelinux.h b/lib/libnet/pxelinux.h new file mode 100644 index 0000000..98b3925 --- /dev/null +++ b/lib/libnet/pxelinux.h @@ -0,0 +1,33 @@ +/***************************************************************************** + * Definitions for pxelinux-style config file support + * + * Copyright 2018 Red Hat, Inc. + * + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * Thomas Huth, Red Hat Inc. - initial implementation + *****************************************************************************/ + +#ifndef LIBNET_PXELINUX_H +#define LIBNET_PXELINUX_H + +/* This structure holds the data from one pxelinux.cfg file entry */ +struct pl_cfg_entry { + const char *label; + const char *kernel; + const char *initrd; + const char *append; +}; + +int pxelinux_parse_cfg(char *cfg, int cfgsize, struct pl_cfg_entry *entries, + int max_entries, int *def_ent); +int pxelinux_load_parse_cfg(filename_ip_t *fn_ip, uint8_t *mac, + int retries, char *cfgbuf, int cfgsize, + struct pl_cfg_entry *entries, + int max_entries, int *def_ent); + +#endif |