Skip to content

Commit 4308384

Browse files
rikka0w0dedeckeh
authored andcommitted
dhcpv6: add ipv6 pxe support
1. Implement PxE in separate files where possible 2. Update README 3. User can add IPv6 PxE entries in `/etc/config/dhcp`. 4. The new section type is "boot6". 5. The compulsory "url" string specifies the URL to the bootable image. 6. The optional "arch" integer specifies the expected client machine type. 7. The last "boot6" section without "arch" defines the default bootable image. Signed-off-by: Hang Zhou <929513338@qq.com>
1 parent 5585b96 commit 4308384

File tree

7 files changed

+183
-2
lines changed

7 files changed

+183
-2
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ if(${DHCPV4_SUPPORT})
3636
set(EXT_SRC ${EXT_SRC} src/dhcpv4.c)
3737
endif(${DHCPV4_SUPPORT})
3838

39-
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})
39+
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})
4040
target_link_libraries(odhcpd resolv ubox uci ${libnl} ${EXT_LINK})
4141

4242
# Installation

README

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ prefix delegation and can be used to relay RA, DHCPv6 and NDP between routed
4040
and only serving NDP for DAD and for traffic to the router itself
4141
[Warning: you should provide additional firewall rules for security]
4242

43+
5. IPv6 PxE Support.
4344

4445
** Compiling **
4546

@@ -168,3 +169,8 @@ hostid string IPv6 host identifier
168169
name string Hostname
169170
leasetime string DHCPv4/v6 leasetime
170171

172+
Sections of type boot6
173+
Option Type Required Description
174+
url string yes e.g. tftp://[fd11::1]/pxe.efi
175+
arch integer no the arch code. 07 is EFI.
176+
If not present, this boot6 will be the default.

src/config.c

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <libubox/vlist.h>
1919

2020
#include "odhcpd.h"
21+
#include "dhcpv6-pxe.h"
2122

2223
static struct blob_buf b;
2324
static int reload_pipe[2] = { -1, -1 };
@@ -43,6 +44,22 @@ struct config config = {.legacy = false, .main_dhcpv4 = false,
4344

4445
#define OAF_DHCPV6 (OAF_DHCPV6_NA | OAF_DHCPV6_PD)
4546

47+
enum {
48+
IPV6_PXE_URL,
49+
IPV6_PXE_ARCH,
50+
IPV6_PXE_MAX
51+
};
52+
53+
static const struct blobmsg_policy ipv6_pxe_attrs[IPV6_PXE_MAX] = {
54+
[IPV6_PXE_URL] = {.name = "url", .type = BLOBMSG_TYPE_STRING },
55+
[IPV6_PXE_ARCH] = {.name = "arch", .type = BLOBMSG_TYPE_INT32 },
56+
};
57+
58+
const struct uci_blob_param_list ipv6_pxe_attr_list = {
59+
.n_params = IPV6_PXE_MAX,
60+
.params = ipv6_pxe_attrs,
61+
};
62+
4663
enum {
4764
IFACE_ATTR_INTERFACE,
4865
IFACE_ATTR_IFNAME,
@@ -1623,6 +1640,29 @@ void reload_services(struct interface *iface)
16231640
}
16241641
}
16251642

1643+
static int ipv6_pxe_from_uci(struct uci_section* s)
1644+
{
1645+
blob_buf_init(&b, 0);
1646+
uci_to_blob(&b, s, &ipv6_pxe_attr_list);
1647+
1648+
void* data = blob_data(b.head);
1649+
size_t len = blob_len(b.head);
1650+
1651+
struct blob_attr* tb[IFACE_ATTR_MAX];
1652+
blobmsg_parse(ipv6_pxe_attrs, IPV6_PXE_MAX, tb, data, len);
1653+
1654+
if (!tb[IPV6_PXE_URL])
1655+
return -1;
1656+
1657+
const char* url = blobmsg_get_string(tb[IPV6_PXE_URL]);
1658+
1659+
uint32_t arch = 0xFFFFFFFF;
1660+
if (tb[IPV6_PXE_ARCH])
1661+
arch = blobmsg_get_u32(tb[IPV6_PXE_ARCH]);
1662+
1663+
return ipv6_pxe_entry_new(arch, url) ? -1 : 0;
1664+
}
1665+
16261666
void odhcpd_reload(void)
16271667
{
16281668
struct uci_context *uci = uci_alloc_context();
@@ -1659,6 +1699,15 @@ void odhcpd_reload(void)
16591699
if (!strcmp(s->type, "host"))
16601700
set_lease_from_uci(s);
16611701
}
1702+
1703+
/* 4. IPv6 PxE */
1704+
ipv6_pxe_clear();
1705+
uci_foreach_element(&dhcp->sections, e) {
1706+
struct uci_section* s = uci_to_section(e);
1707+
if (!strcmp(s->type, "boot6"))
1708+
ipv6_pxe_from_uci(s);
1709+
}
1710+
ipv6_pxe_dump();
16621711
}
16631712

16641713
if (config.dhcp_statefile) {

src/dhcpv6-pxe.c

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
#include <unistd.h>
2+
#include <stddef.h>
3+
4+
#include <libubox/list.h>
5+
6+
#include "dhcpv6.h"
7+
#include "dhcpv6-pxe.h"
8+
9+
struct ipv6_pxe_entry {
10+
struct list_head list; // List head for linking
11+
uint32_t arch;
12+
13+
// Ready to send
14+
struct __attribute__((packed)) {
15+
uint16_t type; // In network endianess
16+
uint16_t len; // In network endianess, without /0
17+
char payload[]; // Null-terminated here
18+
} bootfile_url;
19+
};
20+
21+
static struct ipv6_pxe_entry* ipv6_pxe_default = NULL;
22+
LIST_HEAD(ipv6_pxe_list);
23+
24+
const struct ipv6_pxe_entry* ipv6_pxe_entry_new(uint32_t arch, const char* url) {
25+
size_t url_len = strlen(url);
26+
struct ipv6_pxe_entry* ipe = malloc(sizeof(struct ipv6_pxe_entry) + url_len + 1);
27+
if (!ipe)
28+
return NULL;
29+
30+
memcpy(ipe->bootfile_url.payload, url, url_len + 1);
31+
ipe->bootfile_url.len = htons(url_len);
32+
ipe->bootfile_url.type = htons(DHCPV6_OPT_BOOTFILE_URL);
33+
34+
if (arch == 0xFFFFFFFF) {
35+
ipv6_pxe_default = ipe;
36+
}
37+
else {
38+
ipe->arch = arch;
39+
list_add(&ipe->list, &ipv6_pxe_list);
40+
}
41+
42+
return ipe;
43+
}
44+
45+
const struct ipv6_pxe_entry* ipv6_pxe_of_arch(uint16_t arch) {
46+
struct ipv6_pxe_entry* entry;
47+
list_for_each_entry(entry, &ipv6_pxe_list, list) {
48+
if (arch == entry->arch)
49+
return entry;
50+
}
51+
52+
return ipv6_pxe_default;
53+
}
54+
55+
void ipv6_pxe_serve_boot_url(uint16_t arch, struct iovec* iov) {
56+
const struct ipv6_pxe_entry* entry = ipv6_pxe_of_arch(arch);
57+
58+
if (entry == NULL) {
59+
// No IPv6 PxE bootfile defined
60+
iov->iov_base = NULL;
61+
iov->iov_len = 0;
62+
}
63+
else {
64+
iov->iov_base = (void*)&(entry->bootfile_url);
65+
iov->iov_len = 4 + ntohs(entry->bootfile_url.len);
66+
syslog(LOG_INFO, "Serve IPv6 PxE, arch = %d, url = %s", arch, entry->bootfile_url.payload);
67+
}
68+
}
69+
70+
void ipv6_pxe_dump(void) {
71+
struct ipv6_pxe_entry* entry;
72+
int count = 0;
73+
74+
if (ipv6_pxe_default)
75+
count++;
76+
77+
list_for_each_entry(entry, &ipv6_pxe_list, list) {
78+
count++;
79+
}
80+
81+
if (count) {
82+
syslog(LOG_INFO, "IPv6 PxE URLs:\n");
83+
84+
list_for_each_entry(entry, &ipv6_pxe_list, list) {
85+
syslog(LOG_INFO, " arch %04d = %s\n", entry->arch, entry->bootfile_url.payload);
86+
}
87+
88+
if (ipv6_pxe_default)
89+
syslog(LOG_INFO, " Default = %s\n", ipv6_pxe_default->bootfile_url.payload);
90+
}
91+
}
92+
93+
void ipv6_pxe_clear(void) {
94+
struct ipv6_pxe_entry* entry, * temp;
95+
list_for_each_entry_safe(entry, temp, &ipv6_pxe_list, list) {
96+
list_del(&entry->list);
97+
free(entry);
98+
}
99+
100+
if (ipv6_pxe_default) {
101+
free(ipv6_pxe_default);
102+
ipv6_pxe_default = NULL;
103+
}
104+
}

src/dhcpv6-pxe.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#pragma once
2+
3+
#include <unistd.h>
4+
#include <stddef.h>
5+
6+
// The detail is hidden except for dhcpv6-pxe.c
7+
struct ipv6_pxe_entry;
8+
9+
const struct ipv6_pxe_entry* ipv6_pxe_entry_new(uint32_t arch, const char* url);
10+
const struct ipv6_pxe_entry* ipv6_pxe_of_arch(uint16_t arch);
11+
void ipv6_pxe_serve_boot_url(uint16_t arch, struct iovec* iov);
12+
void ipv6_pxe_dump(void);
13+
void ipv6_pxe_clear(void);

src/dhcpv6.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
#include "odhcpd.h"
2727
#include "dhcpv6.h"
28+
#include "dhcpv6-pxe.h"
2829
#ifdef DHCPV4_SUPPORT
2930
#include "dhcpv4.h"
3031
#endif
@@ -183,6 +184,7 @@ enum {
183184
IOV_RELAY_MSG,
184185
IOV_DHCPV4O6_SERVER,
185186
IOV_DNR,
187+
IOV_BOOTFILE_URL,
186188
IOV_TOTAL
187189
};
188190

@@ -558,6 +560,7 @@ static void handle_client_request(void *addr, void *data, size_t len,
558560
[IOV_DNR] = {dnrs, dnrs_len},
559561
[IOV_RELAY_MSG] = {NULL, 0},
560562
[IOV_DHCPV4O6_SERVER] = {&dhcpv4o6_server, 0},
563+
[IOV_BOOTFILE_URL] = {NULL, 0}
561564
};
562565

563566
if (hdr->msg_type == DHCPV6_MSG_RELAY_FORW)
@@ -663,6 +666,9 @@ static void handle_client_request(void *addr, void *data, size_t len,
663666
break;
664667
}
665668
}
669+
} else if (otype == DHCPV6_OPT_CLIENT_ARCH) {
670+
uint16_t arch_code = ntohs(((uint16_t*)odata)[0]);
671+
ipv6_pxe_serve_boot_url(arch_code, &iov[IOV_BOOTFILE_URL]);
666672
}
667673
}
668674

@@ -732,7 +738,7 @@ static void handle_client_request(void *addr, void *data, size_t len,
732738
iov[IOV_CERID].iov_len + iov[IOV_DHCPV6_RAW].iov_len +
733739
iov[IOV_NTP].iov_len + iov[IOV_NTP_ADDR].iov_len +
734740
iov[IOV_SNTP].iov_len + iov[IOV_SNTP_ADDR].iov_len +
735-
iov[IOV_DNR].iov_len -
741+
iov[IOV_DNR].iov_len + iov[IOV_BOOTFILE_URL].iov_len -
736742
(4 + opts_end - opts));
737743

738744
syslog(LOG_DEBUG, "Sending a DHCPv6-%s on %s", iov[IOV_NESTED].iov_len ? "relay-reply" : "reply", iface->name);

src/dhcpv6.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@
5959
#define DHCPV6_OPT_INFO_REFRESH 32
6060
#define DHCPV6_OPT_FQDN 39
6161
#define DHCPV6_OPT_NTP_SERVERS 56
62+
#define DHCPV6_OPT_BOOTFILE_URL 59
63+
#define DHCPV6_OPT_BOOTFILE_PARAM 60
64+
#define DHCPV6_OPT_CLIENT_ARCH 61
6265
#define DHCPV6_OPT_SOL_MAX_RT 82
6366
#define DHCPV6_OPT_INF_MAX_RT 83
6467
#define DHCPV6_OPT_DHCPV4_MSG 87

0 commit comments

Comments
 (0)