aboutgitcodebugslistschat
path: root/dhcp.c
diff options
context:
space:
mode:
Diffstat (limited to 'dhcp.c')
-rw-r--r--dhcp.c112
1 files changed, 81 insertions, 31 deletions
diff --git a/dhcp.c b/dhcp.c
index 2a23ed4..012cec6 100644
--- a/dhcp.c
+++ b/dhcp.c
@@ -63,6 +63,11 @@ static struct opt opts[255];
#define OPT_MIN 60 /* RFC 951 */
+/* Total option size (excluding end option) is 576 (RFC 2131), minus
+ * offset of options (268), minus end option (1).
+ */
+#define OPT_MAX 307
+
/**
* dhcp_init() - Initialise DHCP options
*/
@@ -122,7 +127,7 @@ struct msg {
uint8_t sname[64];
uint8_t file[128];
uint32_t magic;
- uint8_t o[308];
+ uint8_t o[OPT_MAX + 1 /* End option */ ];
} __attribute__((__packed__));
/**
@@ -130,15 +135,28 @@ struct msg {
* @m: Message to fill
* @o: Option number
* @offset: Current offset within options field, updated on insertion
+ *
+ * Return: false if m has space to write the option, true otherwise
*/
-static void fill_one(struct msg *m, int o, int *offset)
+static bool fill_one(struct msg *m, int o, int *offset)
{
+ size_t slen = opts[o].slen;
+
+ /* If we don't have space to write the option, then just skip */
+ if (*offset + 2 /* code and length of option */ + slen > OPT_MAX)
+ return true;
+
m->o[*offset] = o;
- m->o[*offset + 1] = opts[o].slen;
- memcpy(&m->o[*offset + 2], opts[o].s, opts[o].slen);
+ m->o[*offset + 1] = slen;
+
+ /* Move to option */
+ *offset += 2;
+
+ memcpy(&m->o[*offset], opts[o].s, slen);
opts[o].sent = 1;
- *offset += 2 + opts[o].slen;
+ *offset += slen;
+ return false;
}
/**
@@ -159,21 +177,23 @@ static int fill(struct msg *m)
* Put it there explicitly, unless requested via option 55.
*/
if (opts[55].clen > 0 && !memchr(opts[55].c, 53, opts[55].clen))
- fill_one(m, 53, &offset);
+ if (fill_one(m, 53, &offset))
+ debug("DHCP: skipping option 53");
for (i = 0; i < opts[55].clen; i++) {
o = opts[55].c[i];
if (opts[o].slen != -1)
- fill_one(m, o, &offset);
+ if (fill_one(m, o, &offset))
+ debug("DHCP: skipping option %i", o);
}
for (o = 0; o < 255; o++) {
if (opts[o].slen != -1 && !opts[o].sent)
- fill_one(m, o, &offset);
+ if (fill_one(m, o, &offset))
+ debug("DHCP: skipping option %i", o);
}
m->o[offset++] = 255;
- m->o[offset++] = 0;
if (offset < OPT_MIN) {
memset(&m->o[offset], 0, OPT_MIN - offset);
@@ -276,33 +296,35 @@ static void opt_set_dns_search(const struct ctx *c, size_t max_len)
/**
* dhcp() - Check if this is a DHCP message, reply as needed
* @c: Execution context
- * @p: Packet pool, single packet with Ethernet buffer
+ * @data: Single packet with Ethernet buffer
*
* Return: 0 if it's not a DHCP message, 1 if handled, -1 on failure
*/
-int dhcp(const struct ctx *c, const struct pool *p)
+int dhcp(const struct ctx *c, struct iov_tail *data)
{
- size_t mlen, dlen, offset = 0, opt_len, opt_off = 0;
char macstr[ETH_ADDRSTRLEN];
+ size_t mlen, dlen, opt_len;
struct in_addr mask, dst;
+ struct ethhdr eh_storage;
+ struct iphdr iph_storage;
+ struct udphdr uh_storage;
const struct ethhdr *eh;
const struct iphdr *iph;
const struct udphdr *uh;
struct msg const *m;
struct msg reply;
unsigned int i;
+ struct msg m_storage;
- eh = packet_get(p, 0, offset, sizeof(*eh), NULL);
- offset += sizeof(*eh);
-
- iph = packet_get(p, 0, offset, sizeof(*iph), NULL);
+ eh = IOV_REMOVE_HEADER(data, eh_storage);
+ iph = IOV_PEEK_HEADER(data, iph_storage);
if (!eh || !iph)
return -1;
- offset += iph->ihl * 4UL;
- uh = packet_get(p, 0, offset, sizeof(*uh), &mlen);
- offset += sizeof(*uh);
+ if (!iov_drop_header(data, iph->ihl * 4UL))
+ return -1;
+ uh = IOV_REMOVE_HEADER(data, uh_storage);
if (!uh)
return -1;
@@ -312,7 +334,10 @@ int dhcp(const struct ctx *c, const struct pool *p)
if (c->no_dhcp)
return 1;
- m = packet_get(p, 0, offset, offsetof(struct msg, o), &opt_len);
+ mlen = iov_tail_size(data);
+ m = (struct msg const *)iov_remove_header_(data, &m_storage,
+ offsetof(struct msg, o),
+ __alignof__(struct msg));
if (!m ||
mlen != ntohs(uh->len) - sizeof(*uh) ||
mlen < offsetof(struct msg, o) ||
@@ -335,27 +360,28 @@ int dhcp(const struct ctx *c, const struct pool *p)
memset(&reply.file, 0, sizeof(reply.file));
reply.magic = m->magic;
- offset += offsetof(struct msg, o);
-
for (i = 0; i < ARRAY_SIZE(opts); i++)
opts[i].clen = -1;
- while (opt_off + 2 < opt_len) {
- const uint8_t *olen, *val;
+ opt_len = iov_tail_size(data);
+ while (opt_len >= 2) {
+ uint8_t olen_storage, type_storage;
+ const uint8_t *olen;
uint8_t *type;
- type = packet_get(p, 0, offset + opt_off, 1, NULL);
- olen = packet_get(p, 0, offset + opt_off + 1, 1, NULL);
+ type = IOV_REMOVE_HEADER(data, type_storage);
+ olen = IOV_REMOVE_HEADER(data, olen_storage);
if (!type || !olen)
return -1;
- val = packet_get(p, 0, offset + opt_off + 2, *olen, NULL);
- if (!val)
+ opt_len = iov_tail_size(data);
+ if (opt_len < *olen)
return -1;
- memcpy(&opts[*type].c, val, *olen);
+ iov_to_buf(&data->iov[0], data->cnt, data->off, &opts[*type].c, *olen);
opts[*type].clen = *olen;
- opt_off += *olen + 2;
+ iov_drop_header(data, *olen);
+ opt_len -= *olen;
}
opts[80].slen = -1;
@@ -397,7 +423,7 @@ int dhcp(const struct ctx *c, const struct pool *p)
&c->ip4.guest_gw, sizeof(c->ip4.guest_gw));
}
- if (c->mtu != -1) {
+ if (c->mtu) {
opts[26].slen = 2;
opts[26].s[0] = c->mtu / 256;
opts[26].s[1] = c->mtu % 256;
@@ -411,6 +437,30 @@ int dhcp(const struct ctx *c, const struct pool *p)
if (!opts[6].slen)
opts[6].slen = -1;
+ opt_len = strlen(c->hostname);
+ if (opt_len > 0) {
+ opts[12].slen = opt_len;
+ memcpy(opts[12].s, &c->hostname, opt_len);
+ }
+
+ opt_len = strlen(c->fqdn);
+ if (opt_len > 0) {
+ opt_len += 3 /* flags */
+ + 2; /* Length byte for first label, and terminator */
+
+ if (sizeof(opts[81].s) >= opt_len) {
+ opts[81].s[0] = 0x4; /* flags (E) */
+ opts[81].s[1] = 0xff; /* RCODE1 */
+ opts[81].s[2] = 0xff; /* RCODE2 */
+
+ encode_domain_name((char *)opts[81].s + 3, c->fqdn);
+
+ opts[81].slen = opt_len;
+ } else {
+ debug("DHCP: client FQDN option doesn't fit, skipping");
+ }
+ }
+
if (!c->no_dhcp_dns_search)
opt_set_dns_search(c, sizeof(m->o));