]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-dhcp: parse error message in DECLINE or NAK 2029/head
authorTom Gundersen <teg@jklm.no>
Wed, 25 Nov 2015 16:29:30 +0000 (17:29 +0100)
committerTom Gundersen <teg@jklm.no>
Wed, 25 Nov 2015 17:30:31 +0000 (18:30 +0100)
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.

src/libsystemd-network/dhcp-internal.h
src/libsystemd-network/dhcp-option.c
src/libsystemd-network/dhcp-protocol.h
src/libsystemd-network/sd-dhcp-client.c
src/libsystemd-network/sd-dhcp-lease.c
src/libsystemd-network/sd-dhcp-server.c
src/libsystemd-network/test-dhcp-client.c
src/libsystemd-network/test-dhcp-option.c

index a5daaa543a8ee8040cd3ea8308ebbc1e3cfcaa4f..7038212bcf753c322eafde00ce9996853c2c0fa4 100644 (file)
@@ -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,
index a6c410ba9118bac4d65428fa2acc713c8e42220a..1de7f3639cbca8e73ed849b43a157f8e54def2d6 100644 (file)
@@ -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;
+                }
 
-                case DHCP_OPTION_MESSAGE_TYPE:
-                        if (buflen < offset + 3)
-                                return -ENOBUFS;
+                if (buflen < offset + 1)
+                        return -ENOBUFS;
+
+                len = options[offset ++];
 
-                        len = options[++offset];
+                if (buflen < offset + len)
+                        return -EINVAL;
+
+                option = &options[offset];
+
+                switch (code) {
+                case DHCP_OPTION_MESSAGE_TYPE:
                         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;
-
-                        len = options[++offset];
-                        if (len != 1)
+                case DHCP_OPTION_ERROR_MESSAGE:
+                        if (len == 0)
                                 return -EINVAL;
 
-                        if (overload)
-                                *overload = options[++offset];
-                        else
-                                offset++;
+                        if (error_message) {
+                                _cleanup_free_ char *string = NULL;
 
-                        offset++;
+                                /* Accept a trailing NUL byte */
+                                if (memchr(option, 0, len - 1))
+                                        return -EINVAL;
 
-                        break;
+                                string = strndup((const char *) option, len);
+                                if (!string)
+                                        return -ENOMEM;
 
-                default:
-                        if (buflen < offset + 3)
-                                return -ENOBUFS;
+                                if (!ascii_is_valid(string))
+                                        return -EINVAL;
 
-                        code = options[offset];
-                        len = options[++offset];
+                                free(*error_message);
+                                *error_message = string;
+                                string = NULL;
+                        }
 
-                        if (buflen < ++offset + len)
+                        break;
+                case DHCP_OPTION_OVERLOAD:
+                        if (len != 1)
                                 return -EINVAL;
 
-                        if (cb)
-                                cb(code, len, &options[offset], userdata);
+                        if (overload)
+                                *overload = *option;
 
-                        offset += len;
+                        break;
+
+                default:
+                        if (cb)
+                                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;
+
+        if (_error_message && IN_SET(message_type, DHCP_NAK, DHCP_DECLINE)) {
+                *_error_message = error_message;
+                error_message = NULL;
+        }
 
-        return -ENOMSG;
+        return message_type;
 }
index 05bb5ae49336406a484709594513ee5e3e299bbf..f65529a00eb277d724e828907edecb0187b8a3a1 100644 (file)
@@ -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,
index 66b8f101cf8bc3ba516ae72ff6150405290c4c5f..d116180c166199d8c00555023a09d496b7ecaad0 100644 (file)
@@ -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;
         }
 
index 8befedc5002cacc57e9ef792912211da9201f08f..fccdc01bc30e602c9c64b5d593ed46204c8e23eb 100644 (file)
@@ -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;
         }
 
index 3e2ecdadddeea22ce365924a30dcc8bf77e6da67..587ff936ba0dc513fd40894707709f354ad87dff 100644 (file)
@@ -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 */
 
index 1200a7c251a526aef0189051dfd94a238847e5e5..4478147a830b41116f3e3d582908e5dc1e07f986 100644 (file)
@@ -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);
index 3607df63af99814cc793deebe67bdb6f8ded179c..75d22c4df358a99572b8dfc2d8f3eebd4e119f2d 100644 (file)
@@ -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));