sd-dhcp: parse error message in DECLINE or NAK
If a client sends a DECLINE or a server sends a NAK, they can include a string with a message to explain the error. Parse this and print it at debug level.
This commit is contained in:
@@ -47,8 +47,7 @@ int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset, uint8_
|
||||
typedef int (*dhcp_option_cb_t)(uint8_t code, uint8_t len,
|
||||
const void *option, void *userdata);
|
||||
|
||||
int dhcp_option_parse(DHCPMessage *message, size_t len,
|
||||
dhcp_option_cb_t cb, void *userdata);
|
||||
int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_cb_t cb, void *userdata, char **error_message);
|
||||
|
||||
int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid,
|
||||
uint8_t type, uint16_t arp_type, size_t optlen,
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "utf8.h"
|
||||
|
||||
#include "dhcp-internal.h"
|
||||
|
||||
static int option_append(uint8_t options[], size_t size, size_t *offset,
|
||||
@@ -139,72 +142,84 @@ int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset,
|
||||
}
|
||||
|
||||
static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overload,
|
||||
uint8_t *message_type, dhcp_option_cb_t cb,
|
||||
uint8_t *message_type, char **error_message, dhcp_option_cb_t cb,
|
||||
void *userdata) {
|
||||
uint8_t code, len;
|
||||
const uint8_t *option;
|
||||
size_t offset = 0;
|
||||
|
||||
while (offset < buflen) {
|
||||
switch (options[offset]) {
|
||||
case DHCP_OPTION_PAD:
|
||||
offset++;
|
||||
code = options[offset ++];
|
||||
|
||||
break;
|
||||
switch (code) {
|
||||
case DHCP_OPTION_PAD:
|
||||
continue;
|
||||
|
||||
case DHCP_OPTION_END:
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (buflen < offset + 1)
|
||||
return -ENOBUFS;
|
||||
|
||||
len = options[offset ++];
|
||||
|
||||
if (buflen < offset + len)
|
||||
return -EINVAL;
|
||||
|
||||
option = &options[offset];
|
||||
|
||||
switch (code) {
|
||||
case DHCP_OPTION_MESSAGE_TYPE:
|
||||
if (buflen < offset + 3)
|
||||
return -ENOBUFS;
|
||||
|
||||
len = options[++offset];
|
||||
if (len != 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (message_type)
|
||||
*message_type = options[++offset];
|
||||
else
|
||||
offset++;
|
||||
|
||||
offset++;
|
||||
*message_type = *option;
|
||||
|
||||
break;
|
||||
|
||||
case DHCP_OPTION_OVERLOAD:
|
||||
if (buflen < offset + 3)
|
||||
return -ENOBUFS;
|
||||
case DHCP_OPTION_ERROR_MESSAGE:
|
||||
if (len == 0)
|
||||
return -EINVAL;
|
||||
|
||||
len = options[++offset];
|
||||
if (error_message) {
|
||||
_cleanup_free_ char *string = NULL;
|
||||
|
||||
/* Accept a trailing NUL byte */
|
||||
if (memchr(option, 0, len - 1))
|
||||
return -EINVAL;
|
||||
|
||||
string = strndup((const char *) option, len);
|
||||
if (!string)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!ascii_is_valid(string))
|
||||
return -EINVAL;
|
||||
|
||||
free(*error_message);
|
||||
*error_message = string;
|
||||
string = NULL;
|
||||
}
|
||||
|
||||
break;
|
||||
case DHCP_OPTION_OVERLOAD:
|
||||
if (len != 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (overload)
|
||||
*overload = options[++offset];
|
||||
else
|
||||
offset++;
|
||||
|
||||
offset++;
|
||||
*overload = *option;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
if (buflen < offset + 3)
|
||||
return -ENOBUFS;
|
||||
|
||||
code = options[offset];
|
||||
len = options[++offset];
|
||||
|
||||
if (buflen < ++offset + len)
|
||||
return -EINVAL;
|
||||
|
||||
if (cb)
|
||||
cb(code, len, &options[offset], userdata);
|
||||
|
||||
offset += len;
|
||||
cb(code, len, option, userdata);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
offset += len;
|
||||
}
|
||||
|
||||
if (offset < buflen)
|
||||
@@ -213,8 +228,8 @@ static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overlo
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhcp_option_parse(DHCPMessage *message, size_t len,
|
||||
dhcp_option_cb_t cb, void *userdata) {
|
||||
int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_cb_t cb, void *userdata, char **_error_message) {
|
||||
_cleanup_free_ char *error_message = NULL;
|
||||
uint8_t overload = 0;
|
||||
uint8_t message_type = 0;
|
||||
int r;
|
||||
@@ -227,27 +242,29 @@ int dhcp_option_parse(DHCPMessage *message, size_t len,
|
||||
|
||||
len -= sizeof(DHCPMessage);
|
||||
|
||||
r = parse_options(message->options, len, &overload, &message_type,
|
||||
cb, userdata);
|
||||
r = parse_options(message->options, len, &overload, &message_type, &error_message, cb, userdata);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (overload & DHCP_OVERLOAD_FILE) {
|
||||
r = parse_options(message->file, sizeof(message->file),
|
||||
NULL, &message_type, cb, userdata);
|
||||
r = parse_options(message->file, sizeof(message->file), NULL, &message_type, &error_message, cb, userdata);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (overload & DHCP_OVERLOAD_SNAME) {
|
||||
r = parse_options(message->sname, sizeof(message->sname),
|
||||
NULL, &message_type, cb, userdata);
|
||||
r = parse_options(message->sname, sizeof(message->sname), NULL, &message_type, &error_message, cb, userdata);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (message_type)
|
||||
return message_type;
|
||||
if (message_type == 0)
|
||||
return -ENOMSG;
|
||||
|
||||
return -ENOMSG;
|
||||
if (_error_message && IN_SET(message_type, DHCP_NAK, DHCP_DECLINE)) {
|
||||
*_error_message = error_message;
|
||||
error_message = NULL;
|
||||
}
|
||||
|
||||
return message_type;
|
||||
}
|
||||
|
||||
@@ -132,6 +132,7 @@ enum {
|
||||
DHCP_OPTION_MESSAGE_TYPE = 53,
|
||||
DHCP_OPTION_SERVER_IDENTIFIER = 54,
|
||||
DHCP_OPTION_PARAMETER_REQUEST_LIST = 55,
|
||||
DHCP_OPTION_ERROR_MESSAGE = 56,
|
||||
DHCP_OPTION_MAXIMUM_MESSAGE_SIZE = 57,
|
||||
DHCP_OPTION_RENEWAL_T1_TIME = 58,
|
||||
DHCP_OPTION_REBINDING_T2_TIME = 59,
|
||||
|
||||
@@ -1082,7 +1082,7 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer,
|
||||
return r;
|
||||
}
|
||||
|
||||
r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease);
|
||||
r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease, NULL);
|
||||
if (r != DHCP_OFFER) {
|
||||
log_dhcp_client(client, "received message was not an OFFER, ignoring");
|
||||
return -ENOMSG;
|
||||
@@ -1121,7 +1121,7 @@ static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force,
|
||||
size_t len) {
|
||||
int r;
|
||||
|
||||
r = dhcp_option_parse(force, len, NULL, NULL);
|
||||
r = dhcp_option_parse(force, len, NULL, NULL, NULL);
|
||||
if (r != DHCP_FORCERENEW)
|
||||
return -ENOMSG;
|
||||
|
||||
@@ -1133,6 +1133,7 @@ static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force,
|
||||
static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
|
||||
size_t len) {
|
||||
_cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
|
||||
_cleanup_free_ char *error_message = NULL;
|
||||
int r;
|
||||
|
||||
r = dhcp_lease_new(&lease);
|
||||
@@ -1147,9 +1148,9 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
|
||||
return r;
|
||||
}
|
||||
|
||||
r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease);
|
||||
r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease, &error_message);
|
||||
if (r == DHCP_NAK) {
|
||||
log_dhcp_client(client, "NAK");
|
||||
log_dhcp_client(client, "NAK: %s", strna(error_message));
|
||||
return -EADDRNOTAVAIL;
|
||||
}
|
||||
|
||||
|
||||
@@ -661,7 +661,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
|
||||
break;
|
||||
|
||||
default:
|
||||
log_debug("Ignoring option DHCP option %i while parsing.", code);
|
||||
log_debug("Ignoring option DHCP option %"PRIu8" while parsing.", code);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -699,6 +699,7 @@ static int get_pool_offset(sd_dhcp_server *server, be32_t requested_ip) {
|
||||
int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
|
||||
size_t length) {
|
||||
_cleanup_dhcp_request_free_ DHCPRequest *req = NULL;
|
||||
_cleanup_free_ char *error_message = NULL;
|
||||
DHCPLease *existing_lease;
|
||||
int type, r;
|
||||
|
||||
@@ -714,7 +715,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
|
||||
if (!req)
|
||||
return -ENOMEM;
|
||||
|
||||
type = dhcp_option_parse(message, length, parse_request, req);
|
||||
type = dhcp_option_parse(message, length, parse_request, req, &error_message);
|
||||
if (type < 0)
|
||||
return 0;
|
||||
|
||||
@@ -784,8 +785,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
|
||||
break;
|
||||
}
|
||||
case DHCP_DECLINE:
|
||||
log_dhcp_server(server, "DECLINE (0x%x)",
|
||||
be32toh(req->message->xid));
|
||||
log_dhcp_server(server, "DECLINE (0x%x): %s", be32toh(req->message->xid), strna(error_message));
|
||||
|
||||
/* TODO: make sure we don't offer this address again */
|
||||
|
||||
|
||||
@@ -223,7 +223,7 @@ int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port, const voi
|
||||
static int test_discover_message_verify(size_t size, struct DHCPMessage *dhcp) {
|
||||
int res;
|
||||
|
||||
res = dhcp_option_parse(dhcp, size, check_options, NULL);
|
||||
res = dhcp_option_parse(dhcp, size, check_options, NULL, NULL);
|
||||
assert_se(res == DHCP_DISCOVER);
|
||||
|
||||
if (verbose)
|
||||
@@ -390,7 +390,7 @@ static int test_addr_acq_recv_request(size_t size, DHCPMessage *request) {
|
||||
uint8_t *msg_bytes = (uint8_t *)request;
|
||||
int res;
|
||||
|
||||
res = dhcp_option_parse(request, size, check_options, NULL);
|
||||
res = dhcp_option_parse(request, size, check_options, NULL, NULL);
|
||||
assert_se(res == DHCP_REQUEST);
|
||||
assert_se(xid == request->xid);
|
||||
|
||||
@@ -420,7 +420,7 @@ static int test_addr_acq_recv_discover(size_t size, DHCPMessage *discover) {
|
||||
uint8_t *msg_bytes = (uint8_t *)discover;
|
||||
int res;
|
||||
|
||||
res = dhcp_option_parse(discover, size, check_options, NULL);
|
||||
res = dhcp_option_parse(discover, size, check_options, NULL, NULL);
|
||||
assert_se(res == DHCP_DISCOVER);
|
||||
|
||||
assert_se(msg_bytes[size - 1] == DHCP_OPTION_END);
|
||||
|
||||
@@ -75,9 +75,8 @@ static const char *dhcp_type(int type) {
|
||||
static void test_invalid_buffer_length(void) {
|
||||
DHCPMessage message;
|
||||
|
||||
assert_se(dhcp_option_parse(&message, 0, NULL, NULL) == -EINVAL);
|
||||
assert_se(dhcp_option_parse(&message, sizeof(DHCPMessage) - 1, NULL, NULL)
|
||||
== -EINVAL);
|
||||
assert_se(dhcp_option_parse(&message, 0, NULL, NULL, NULL) == -EINVAL);
|
||||
assert_se(dhcp_option_parse(&message, sizeof(DHCPMessage) - 1, NULL, NULL, NULL) == -EINVAL);
|
||||
}
|
||||
|
||||
static void test_message_init(void) {
|
||||
@@ -101,7 +100,7 @@ static void test_message_init(void) {
|
||||
assert_se(magic[2] == 83);
|
||||
assert_se(magic[3] == 99);
|
||||
|
||||
assert_se(dhcp_option_parse(message, len, NULL, NULL) >= 0);
|
||||
assert_se(dhcp_option_parse(message, len, NULL, NULL, NULL) >= 0);
|
||||
}
|
||||
|
||||
static DHCPMessage *create_message(uint8_t *options, uint16_t optlen,
|
||||
@@ -264,19 +263,12 @@ static void test_options(struct option_desc *desc) {
|
||||
buflen = sizeof(DHCPMessage) + optlen;
|
||||
|
||||
if (!desc) {
|
||||
assert_se((res = dhcp_option_parse(message, buflen,
|
||||
test_options_cb,
|
||||
NULL)) == -ENOMSG);
|
||||
assert_se((res = dhcp_option_parse(message, buflen, test_options_cb, NULL, NULL)) == -ENOMSG);
|
||||
} else if (desc->success) {
|
||||
assert_se((res = dhcp_option_parse(message, buflen,
|
||||
test_options_cb,
|
||||
desc)) >= 0);
|
||||
assert_se(desc->pos == -1 && desc->filepos == -1 &&
|
||||
desc->snamepos == -1);
|
||||
assert_se((res = dhcp_option_parse(message, buflen, test_options_cb, desc, NULL)) >= 0);
|
||||
assert_se(desc->pos == -1 && desc->filepos == -1 && desc->snamepos == -1);
|
||||
} else
|
||||
assert_se((res = dhcp_option_parse(message, buflen,
|
||||
test_options_cb,
|
||||
desc)) < 0);
|
||||
assert_se((res = dhcp_option_parse(message, buflen, test_options_cb, desc, NULL)) < 0);
|
||||
|
||||
if (verbose)
|
||||
printf("DHCP type %s\n", dhcp_type(res));
|
||||
|
||||
Reference in New Issue
Block a user