[systemd-devel] [PATCH 08/28] dhcp: Add tests for DHCP options, file and sname fields

Patrik Flykt patrik.flykt at linux.intel.com
Wed Nov 13 13:22:36 PST 2013


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/dhcp/test-dhcp-option.c |  258 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 258 insertions(+)

diff --git a/src/dhcp/test-dhcp-option.c b/src/dhcp/test-dhcp-option.c
index 9302fd6..a2b79ce 100644
--- a/src/dhcp/test-dhcp-option.c
+++ b/src/dhcp/test-dhcp-option.c
@@ -10,6 +10,68 @@
 #include "protocol.h"
 #include "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;
@@ -43,10 +105,206 @@ static void test_cookie(void)
         free(message);
 }
 
+static DHCPMessage *create_message(uint8_t *options, uint16_t optlen,
+                uint8_t *file, uint8_t filelen,
+                uint8_t *sname, uint8_t snamelen)
+{
+        DHCPMessage *message;
+        int len = sizeof(DHCPMessage) + 4 + optlen;
+        uint8_t *opt;
+
+        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, 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;
+        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));
+
+        free(message);
+}
+
 int main(int argc, char *argv[])
 {
+        unsigned int i;
+
         test_invalid_buffer_length();
         test_cookie();
 
+        test_options(NULL);
+
+        for (i = 0; i < sizeof(option_tests)/sizeof(struct option_desc); i++)
+                test_options(&option_tests[i]);
+
         return 0;
 }
-- 
1.7.10.4



More information about the systemd-devel mailing list