aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/libnet/Makefile2
-rw-r--r--lib/libnet/pxelinux.c212
-rw-r--r--lib/libnet/pxelinux.h33
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