set(EXT_SRC ${EXT_SRC} src/dhcpv4.c)
endif(${DHCPV4_SUPPORT})
-add_executable(odhcpd src/odhcpd.c src/config.c src/router.c src/dhcpv6.c src/ndp.c src/dhcpv6-ia.c src/netlink.c ${EXT_SRC})
+add_executable(odhcpd src/odhcpd.c src/config.c src/router.c src/dhcpv6.c src/ndp.c src/dhcpv6-ia.c src/dhcpv6-pxe.c src/netlink.c ${EXT_SRC})
target_link_libraries(odhcpd resolv ubox uci ${libnl} ${EXT_LINK})
# Installation
and only serving NDP for DAD and for traffic to the router itself
[Warning: you should provide additional firewall rules for security]
+5. IPv6 PxE Support.
** Compiling **
name string Hostname
leasetime string DHCPv4/v6 leasetime
+Sections of type boot6
+Option Type Required Description
+url string yes e.g. tftp://[fd11::1]/pxe.efi
+arch integer no the arch code. 07 is EFI.
+ If not present, this boot6 will be the default.
#include <libubox/vlist.h>
#include "odhcpd.h"
+#include "dhcpv6-pxe.h"
static struct blob_buf b;
static int reload_pipe[2] = { -1, -1 };
#define OAF_DHCPV6 (OAF_DHCPV6_NA | OAF_DHCPV6_PD)
+enum {
+ IPV6_PXE_URL,
+ IPV6_PXE_ARCH,
+ IPV6_PXE_MAX
+};
+
+static const struct blobmsg_policy ipv6_pxe_attrs[IPV6_PXE_MAX] = {
+ [IPV6_PXE_URL] = {.name = "url", .type = BLOBMSG_TYPE_STRING },
+ [IPV6_PXE_ARCH] = {.name = "arch", .type = BLOBMSG_TYPE_INT32 },
+};
+
+const struct uci_blob_param_list ipv6_pxe_attr_list = {
+ .n_params = IPV6_PXE_MAX,
+ .params = ipv6_pxe_attrs,
+};
+
enum {
IFACE_ATTR_INTERFACE,
IFACE_ATTR_IFNAME,
}
}
+static int ipv6_pxe_from_uci(struct uci_section* s)
+{
+ blob_buf_init(&b, 0);
+ uci_to_blob(&b, s, &ipv6_pxe_attr_list);
+
+ void* data = blob_data(b.head);
+ size_t len = blob_len(b.head);
+
+ struct blob_attr* tb[IFACE_ATTR_MAX];
+ blobmsg_parse(ipv6_pxe_attrs, IPV6_PXE_MAX, tb, data, len);
+
+ if (!tb[IPV6_PXE_URL])
+ return -1;
+
+ const char* url = blobmsg_get_string(tb[IPV6_PXE_URL]);
+
+ uint32_t arch = 0xFFFFFFFF;
+ if (tb[IPV6_PXE_ARCH])
+ arch = blobmsg_get_u32(tb[IPV6_PXE_ARCH]);
+
+ return ipv6_pxe_entry_new(arch, url) ? -1 : 0;
+}
+
void odhcpd_reload(void)
{
struct uci_context *uci = uci_alloc_context();
if (!strcmp(s->type, "host"))
set_lease_from_uci(s);
}
+
+ /* 4. IPv6 PxE */
+ ipv6_pxe_clear();
+ uci_foreach_element(&dhcp->sections, e) {
+ struct uci_section* s = uci_to_section(e);
+ if (!strcmp(s->type, "boot6"))
+ ipv6_pxe_from_uci(s);
+ }
+ ipv6_pxe_dump();
}
if (config.dhcp_statefile) {
--- /dev/null
+#include <unistd.h>
+#include <stddef.h>
+
+#include <libubox/list.h>
+
+#include "dhcpv6.h"
+#include "dhcpv6-pxe.h"
+
+struct ipv6_pxe_entry {
+ struct list_head list; // List head for linking
+ uint32_t arch;
+
+ // Ready to send
+ struct __attribute__((packed)) {
+ uint16_t type; // In network endianess
+ uint16_t len; // In network endianess, without /0
+ char payload[]; // Null-terminated here
+ } bootfile_url;
+};
+
+static struct ipv6_pxe_entry* ipv6_pxe_default = NULL;
+LIST_HEAD(ipv6_pxe_list);
+
+const struct ipv6_pxe_entry* ipv6_pxe_entry_new(uint32_t arch, const char* url) {
+ size_t url_len = strlen(url);
+ struct ipv6_pxe_entry* ipe = malloc(sizeof(struct ipv6_pxe_entry) + url_len + 1);
+ if (!ipe)
+ return NULL;
+
+ memcpy(ipe->bootfile_url.payload, url, url_len + 1);
+ ipe->bootfile_url.len = htons(url_len);
+ ipe->bootfile_url.type = htons(DHCPV6_OPT_BOOTFILE_URL);
+
+ if (arch == 0xFFFFFFFF) {
+ ipv6_pxe_default = ipe;
+ }
+ else {
+ ipe->arch = arch;
+ list_add(&ipe->list, &ipv6_pxe_list);
+ }
+
+ return ipe;
+}
+
+const struct ipv6_pxe_entry* ipv6_pxe_of_arch(uint16_t arch) {
+ struct ipv6_pxe_entry* entry;
+ list_for_each_entry(entry, &ipv6_pxe_list, list) {
+ if (arch == entry->arch)
+ return entry;
+ }
+
+ return ipv6_pxe_default;
+}
+
+void ipv6_pxe_serve_boot_url(uint16_t arch, struct iovec* iov) {
+ const struct ipv6_pxe_entry* entry = ipv6_pxe_of_arch(arch);
+
+ if (entry == NULL) {
+ // No IPv6 PxE bootfile defined
+ iov->iov_base = NULL;
+ iov->iov_len = 0;
+ }
+ else {
+ iov->iov_base = (void*)&(entry->bootfile_url);
+ iov->iov_len = 4 + ntohs(entry->bootfile_url.len);
+ syslog(LOG_INFO, "Serve IPv6 PxE, arch = %d, url = %s", arch, entry->bootfile_url.payload);
+ }
+}
+
+void ipv6_pxe_dump(void) {
+ struct ipv6_pxe_entry* entry;
+ int count = 0;
+
+ if (ipv6_pxe_default)
+ count++;
+
+ list_for_each_entry(entry, &ipv6_pxe_list, list) {
+ count++;
+ }
+
+ if (count) {
+ syslog(LOG_INFO, "IPv6 PxE URLs:\n");
+
+ list_for_each_entry(entry, &ipv6_pxe_list, list) {
+ syslog(LOG_INFO, " arch %04d = %s\n", entry->arch, entry->bootfile_url.payload);
+ }
+
+ if (ipv6_pxe_default)
+ syslog(LOG_INFO, " Default = %s\n", ipv6_pxe_default->bootfile_url.payload);
+ }
+}
+
+void ipv6_pxe_clear(void) {
+ struct ipv6_pxe_entry* entry, * temp;
+ list_for_each_entry_safe(entry, temp, &ipv6_pxe_list, list) {
+ list_del(&entry->list);
+ free(entry);
+ }
+
+ if (ipv6_pxe_default) {
+ free(ipv6_pxe_default);
+ ipv6_pxe_default = NULL;
+ }
+}
--- /dev/null
+#pragma once
+
+#include <unistd.h>
+#include <stddef.h>
+
+// The detail is hidden except for dhcpv6-pxe.c
+struct ipv6_pxe_entry;
+
+const struct ipv6_pxe_entry* ipv6_pxe_entry_new(uint32_t arch, const char* url);
+const struct ipv6_pxe_entry* ipv6_pxe_of_arch(uint16_t arch);
+void ipv6_pxe_serve_boot_url(uint16_t arch, struct iovec* iov);
+void ipv6_pxe_dump(void);
+void ipv6_pxe_clear(void);
#include "odhcpd.h"
#include "dhcpv6.h"
+#include "dhcpv6-pxe.h"
#ifdef DHCPV4_SUPPORT
#include "dhcpv4.h"
#endif
IOV_RELAY_MSG,
IOV_DHCPV4O6_SERVER,
IOV_DNR,
+ IOV_BOOTFILE_URL,
IOV_TOTAL
};
[IOV_DNR] = {dnrs, dnrs_len},
[IOV_RELAY_MSG] = {NULL, 0},
[IOV_DHCPV4O6_SERVER] = {&dhcpv4o6_server, 0},
+ [IOV_BOOTFILE_URL] = {NULL, 0}
};
if (hdr->msg_type == DHCPV6_MSG_RELAY_FORW)
break;
}
}
+ } else if (otype == DHCPV6_OPT_CLIENT_ARCH) {
+ uint16_t arch_code = ntohs(((uint16_t*)odata)[0]);
+ ipv6_pxe_serve_boot_url(arch_code, &iov[IOV_BOOTFILE_URL]);
}
}
iov[IOV_CERID].iov_len + iov[IOV_DHCPV6_RAW].iov_len +
iov[IOV_NTP].iov_len + iov[IOV_NTP_ADDR].iov_len +
iov[IOV_SNTP].iov_len + iov[IOV_SNTP_ADDR].iov_len +
- iov[IOV_DNR].iov_len -
+ iov[IOV_DNR].iov_len + iov[IOV_BOOTFILE_URL].iov_len -
(4 + opts_end - opts));
syslog(LOG_DEBUG, "Sending a DHCPv6-%s on %s", iov[IOV_NESTED].iov_len ? "relay-reply" : "reply", iface->name);
#define DHCPV6_OPT_INFO_REFRESH 32
#define DHCPV6_OPT_FQDN 39
#define DHCPV6_OPT_NTP_SERVERS 56
+#define DHCPV6_OPT_BOOTFILE_URL 59
+#define DHCPV6_OPT_BOOTFILE_PARAM 60
+#define DHCPV6_OPT_CLIENT_ARCH 61
#define DHCPV6_OPT_SOL_MAX_RT 82
#define DHCPV6_OPT_INF_MAX_RT 83
#define DHCPV6_OPT_DHCPV4_MSG 87