chiark / gitweb /
dhcp: Add tests for DHCP options, file and sname fields
authorPatrik Flykt <patrik.flykt@linux.intel.com>
Mon, 9 Dec 2013 21:43:14 +0000 (23:43 +0200)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Thu, 12 Dec 2013 16:43:33 +0000 (11:43 -0500)
Add a structure describing the DHCP file, sname and trailing options
fields. Create a messge holding these fields and call the internal
option parsing function.

In the test callback function verify that only regular options are
passed and figure out which part of the DHCP message is the one that
is being processed. As the test program knows the full contents of
the test options in the test structure, skip all non-regular fields
and verify that the option provided to the callback indeed is the
one expected. Check also if non-regular option fields are to be
ignored in the end of the option field as the callback is not called
again and the final check when the whole message has been processed
needs to be successful.

Add a boolean flag for pretty-printing, anticipate there will be a
nice option to toggle it in the future.

src/libsystemd-dhcp/test-dhcp-option.c

index cde8c29..df717af 100644 (file)
@@ -5,11 +5,74 @@
 #include <string.h>
 #include <assert.h>
 
-#include <util.h>
+#include "util.h"
+#include "macro.h"
 
 #include "dhcp-protocol.h"
 #include "dhcp-internal.h"
 
+struct option_desc {
+        uint8_t sname[64];
+        int snamelen;
+        uint8_t file[128];
+        int filelen;
+        uint8_t options[128];
+        int len;
+        bool success;
+        int filepos;
+        int snamepos;
+        int pos;
+};
+
+static bool verbose = false;
+
+static struct option_desc option_tests[] = {
+        { {}, 0, {}, 0, { 42, 5, 65, 66, 67, 68, 69 }, 7, false, },
+        { {}, 0, {}, 0, { 42, 5, 65, 66, 67, 68, 69, 0, 0,
+                          DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 12, true, },
+        { {}, 0, {}, 0, { 8, 255, 70, 71, 72 }, 5, false, },
+        { {}, 0, {}, 0, { 0x35, 0x01, 0x05, 0x36, 0x04, 0x01, 0x00, 0xa8,
+                          0xc0, 0x33, 0x04, 0x00, 0x01, 0x51, 0x80, 0x01,
+                          0x04, 0xff, 0xff, 0xff, 0x00, 0x03, 0x04, 0xc0,
+                          0xa8, 0x00, 0x01, 0x06, 0x04, 0xc0, 0xa8, 0x00,
+                          0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
+          40, true, },
+        { {}, 0, {}, 0, { DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_OFFER,
+                          42, 3, 0, 0, 0 }, 8, true, },
+        { {}, 0, {}, 0, { 42, 2, 1, 2, 44 }, 5, false, },
+
+        { {}, 0,
+          { 222, 3, 1, 2, 3, DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_NAK }, 8,
+          { DHCP_OPTION_OVERLOAD, 1, DHCP_OVERLOAD_FILE }, 3, true, },
+
+        { { 1, 4, 1, 2, 3, 4, DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 9,
+          { 222, 3, 1, 2, 3 }, 5,
+          { DHCP_OPTION_OVERLOAD, 1,
+            DHCP_OVERLOAD_FILE|DHCP_OVERLOAD_SNAME }, 3, true, },
+};
+
+static const char *dhcp_type(int type)
+{
+        switch(type) {
+        case DHCP_DISCOVER:
+                return "DHCPDISCOVER";
+        case DHCP_OFFER:
+                return "DHCPOFFER";
+        case DHCP_REQUEST:
+                return "DHCPREQUEST";
+        case DHCP_DECLINE:
+                return "DHCPDECLINE";
+        case DHCP_ACK:
+                return "DHCPACK";
+        case DHCP_NAK:
+                return "DHCPNAK";
+        case DHCP_RELEASE:
+                return "DHCPRELEASE";
+        default:
+                return "unknown";
+        }
+}
+
 static void test_invalid_buffer_length(void)
 {
         DHCPMessage message;
@@ -21,7 +84,7 @@ static void test_invalid_buffer_length(void)
 
 static void test_cookie(void)
 {
-        DHCPMessage *message;
+        _cleanup_free_ DHCPMessage *message;
         size_t len = sizeof(DHCPMessage) + 4;
         uint8_t *opt;
 
@@ -38,14 +101,205 @@ static void test_cookie(void)
         opt[3] = 99;
 
         assert(dhcp_option_parse(message, len, NULL, NULL) == -ENOMSG);
+}
+
+static DHCPMessage *create_message(uint8_t *options, uint16_t optlen,
+                uint8_t *file, uint8_t filelen,
+                uint8_t *sname, uint8_t snamelen)
+{
+        DHCPMessage *message;
+        size_t len = sizeof(DHCPMessage) + 4 + optlen;
+        uint8_t *opt;
 
-        free(message);
+        message = malloc0(len);
+        opt = (uint8_t *)(message + 1);
+
+        opt[0] = 99;
+        opt[1] = 130;
+        opt[2] = 83;
+        opt[3] = 99;
+
+        if (options && optlen)
+                memcpy(&opt[4], options, optlen);
+
+        if (file && filelen <= 128)
+                memcpy(&message->file, file, filelen);
+
+        if (sname && snamelen <= 64)
+                memcpy(&message->sname, sname, snamelen);
+
+        return message;
+}
+
+static void test_ignore_opts(uint8_t *descoption, int *descpos, int *desclen)
+{
+        while (*descpos < *desclen) {
+                switch(descoption[*descpos]) {
+                case DHCP_OPTION_PAD:
+                        *descpos += 1;
+                        break;
+
+                case DHCP_OPTION_MESSAGE_TYPE:
+                case DHCP_OPTION_OVERLOAD:
+                        *descpos += 3;
+                        break;
+
+                default:
+                        return;
+                }
+        }
+}
+
+static int test_options_cb(uint8_t code, uint8_t len, const uint8_t *option,
+                           void *user_data)
+{
+        struct option_desc *desc = user_data;
+        uint8_t *descoption = NULL;
+        int *desclen = NULL, *descpos = NULL;
+        uint8_t optcode = 0;
+        uint8_t optlen = 0;
+        uint8_t i;
+
+        assert((!desc && !code && !len) || desc);
+
+        if (!desc)
+                return -EINVAL;
+
+        assert(code != DHCP_OPTION_PAD);
+        assert(code != DHCP_OPTION_END);
+        assert(code != DHCP_OPTION_MESSAGE_TYPE);
+        assert(code != DHCP_OPTION_OVERLOAD);
+
+        while (desc->pos >= 0 || desc->filepos >= 0 || desc->snamepos >= 0) {
+
+                if (desc->pos >= 0) {
+                        descoption = &desc->options[0];
+                        desclen = &desc->len;
+                        descpos = &desc->pos;
+                } else if (desc->filepos >= 0) {
+                        descoption = &desc->file[0];
+                        desclen = &desc->filelen;
+                        descpos = &desc->filepos;
+                } else if (desc->snamepos >= 0) {
+                        descoption = &desc->sname[0];
+                        desclen = &desc->snamelen;
+                        descpos = &desc->snamepos;
+                }
+
+                assert(descoption && desclen && descpos);
+
+                if (*desclen)
+                        test_ignore_opts(descoption, descpos, desclen);
+
+                if (*descpos < *desclen)
+                        break;
+
+                if (*descpos == *desclen)
+                        *descpos = -1;
+        }
+
+        assert(*descpos != -1);
+
+        optcode = descoption[*descpos];
+        optlen = descoption[*descpos + 1];
+
+        if (verbose)
+                printf("DHCP code %2d(%2d) len %2d(%2d) ", code, optcode,
+                                len, optlen);
+
+        assert(code == optcode);
+        assert(len == optlen);
+
+        for (i = 0; i < len; i++) {
+
+                if (verbose)
+                        printf("0x%02x(0x%02x) ", option[i],
+                                        descoption[*descpos + 2 + i]);
+
+                assert(option[i] == descoption[*descpos + 2 + i]);
+        }
+
+        if (verbose)
+                printf("\n");
+
+        *descpos += optlen + 2;
+
+        test_ignore_opts(descoption, descpos, desclen);
+
+        if (desc->pos != -1 && desc->pos == desc->len)
+                desc->pos = -1;
+
+        if (desc->filepos != -1 && desc->filepos == desc->filelen)
+                desc->filepos = -1;
+
+        if (desc->snamepos != -1 && desc->snamepos == desc->snamelen)
+                desc->snamepos = -1;
+
+        return 0;
+}
+
+static void test_options(struct option_desc *desc)
+{
+        uint8_t *options = NULL;
+        uint8_t *file = NULL;
+        uint8_t *sname = NULL;
+        int optlen = 0;
+        int filelen = 0;
+        int snamelen = 0;
+        int buflen = 0;
+        _cleanup_free_ DHCPMessage *message;
+        int res;
+
+        if (desc) {
+                file = &desc->file[0];
+                filelen = desc->filelen;
+                if (!filelen)
+                        desc->filepos = -1;
+
+                sname = &desc->sname[0];
+                snamelen = desc->snamelen;
+                if (!snamelen)
+                        desc->snamepos = -1;
+
+                options = &desc->options[0];
+                optlen = desc->len;
+                desc->pos = 0;
+        }
+        message = create_message(options, optlen, file, filelen,
+                        sname, snamelen);
+
+        buflen = sizeof(DHCPMessage) + 4 + optlen;
+
+        if (!desc) {
+                assert((res = dhcp_option_parse(message, buflen,
+                                                test_options_cb,
+                                                NULL)) == -ENOMSG);
+        } else if (desc->success) {
+                assert((res = dhcp_option_parse(message, buflen,
+                                                test_options_cb,
+                                                desc)) >= 0);
+                assert(desc->pos == -1 && desc->filepos == -1 &&
+                                desc->snamepos == -1);
+        } else
+                assert((res = dhcp_option_parse(message, buflen,
+                                                test_options_cb,
+                                                desc)) < 0);
+
+        if (verbose)
+                printf("DHCP type %s\n", dhcp_type(res));
 }
 
 int main(int argc, char *argv[])
 {
+        unsigned int i;
+
         test_invalid_buffer_length();
         test_cookie();
 
+        test_options(NULL);
+
+        for (i = 0; i < ELEMENTSOF(option_tests); i++)
+                test_options(&option_tests[i]);
+
         return 0;
 }