diff options
author | Patrik Flykt <patrik.flykt@linux.intel.com> | 2013-12-09 23:43:11 +0200 |
---|---|---|
committer | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2013-12-12 11:43:33 -0500 |
commit | 524cf45894e8219bb66caca58a451bffdc5167ad (patch) | |
tree | aac5a0893ea5761574fc859689a05c47a797e90e | |
parent | b5d01d174c5cb3d2ae49875d3bc1f233c5412120 (diff) |
dhcp: Add option appending and parsing
Add functions to append and parse DHCP options. Not all options
are passed to the callback function, the ones not exposed are
pad, end, message type and overload. If indicated by the overload
option, file and sname fields will be examined for more options.
The option functions are internal to DHCP, add a new header files
for interal function prototypes.
-rw-r--r-- | src/libsystemd-dhcp/dhcp-internal.h | 34 | ||||
-rw-r--r-- | src/libsystemd-dhcp/dhcp-option.c | 184 |
2 files changed, 218 insertions, 0 deletions
diff --git a/src/libsystemd-dhcp/dhcp-internal.h b/src/libsystemd-dhcp/dhcp-internal.h new file mode 100644 index 0000000000..1f9c9d249b --- /dev/null +++ b/src/libsystemd-dhcp/dhcp-internal.h @@ -0,0 +1,34 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright (C) 2013 Intel Corporation. All rights reserved. + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <stdint.h> + +#include "dhcp-protocol.h" + +int dhcp_option_append(uint8_t **buf, size_t *buflen, uint8_t code, + size_t optlen, const void *optval); + +typedef int (*dhcp_option_cb_t)(uint8_t code, uint8_t len, + const uint8_t *option, void *user_data); +int dhcp_option_parse(DHCPMessage *message, size_t len, + dhcp_option_cb_t cb, void *user_data); diff --git a/src/libsystemd-dhcp/dhcp-option.c b/src/libsystemd-dhcp/dhcp-option.c new file mode 100644 index 0000000000..4d45b3b3a4 --- /dev/null +++ b/src/libsystemd-dhcp/dhcp-option.c @@ -0,0 +1,184 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright (C) 2013 Intel Corporation. All rights reserved. + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <stdint.h> +#include <string.h> +#include <errno.h> +#include <stdio.h> + +#include "dhcp-internal.h" + +int dhcp_option_append(uint8_t **buf, size_t *buflen, uint8_t code, + size_t optlen, const void *optval) +{ + if (!buf || !buflen) + return -EINVAL; + + switch (code) { + + case DHCP_OPTION_PAD: + case DHCP_OPTION_END: + if (*buflen < 1) + return -ENOBUFS; + + (*buf)[0] = code; + *buf += 1; + *buflen -= 1; + break; + + default: + if (*buflen < optlen + 2) + return -ENOBUFS; + + if (!optval) + return -EINVAL; + + (*buf)[0] = code; + (*buf)[1] = optlen; + memcpy(&(*buf)[2], optval, optlen); + + *buf += optlen + 2; + *buflen -= (optlen + 2); + + break; + } + + return 0; +} + +static int parse_options(const uint8_t *buf, size_t buflen, uint8_t *overload, + uint8_t *message_type, dhcp_option_cb_t cb, + void *user_data) +{ + const uint8_t *code = buf; + const uint8_t *len; + + while (buflen > 0) { + switch (*code) { + case DHCP_OPTION_PAD: + buflen -= 1; + code++; + break; + + case DHCP_OPTION_END: + return 0; + + case DHCP_OPTION_MESSAGE_TYPE: + if (buflen < 3) + return -ENOBUFS; + buflen -= 3; + + len = code + 1; + if (*len != 1) + return -EINVAL; + + if (message_type) + *message_type = *(len + 1); + + code += 3; + + break; + + case DHCP_OPTION_OVERLOAD: + if (buflen < 3) + return -ENOBUFS; + buflen -= 3; + + len = code + 1; + if (*len != 1) + return -EINVAL; + + if (overload) + *overload = *(len + 1); + + code += 3; + + break; + + default: + if (buflen < 3) + return -ENOBUFS; + + len = code + 1; + + if (buflen < (size_t)*len + 2) + return -EINVAL; + buflen -= *len + 2; + + if (cb) + cb(*code, *len, len + 1, user_data); + + code += *len + 2; + + break; + } + } + + if (buflen) + return -EINVAL; + + return 0; +} + +int dhcp_option_parse(DHCPMessage *message, size_t len, + dhcp_option_cb_t cb, void *user_data) +{ + uint8_t overload = 0; + uint8_t message_type = 0; + uint8_t *opt = (uint8_t *)(message + 1); + int res; + + if (!message) + return -EINVAL; + + if (len < sizeof(DHCPMessage) + 4) + return -EINVAL; + + len -= sizeof(DHCPMessage) + 4; + + if (opt[0] != 0x63 && opt[1] != 0x82 && opt[2] != 0x53 && + opt[3] != 0x63) + return -EINVAL; + + res = parse_options(&opt[4], len, &overload, &message_type, + cb, user_data); + if (res < 0) + return res; + + if (overload & DHCP_OVERLOAD_FILE) { + res = parse_options(message->file, sizeof(message->file), + NULL, &message_type, cb, user_data); + if (res < 0) + return res; + } + + if (overload & DHCP_OVERLOAD_SNAME) { + res = parse_options(message->sname, sizeof(message->sname), + NULL, &message_type, cb, user_data); + if (res < 0) + return res; + } + + if (message_type) + return message_type; + + return -ENOMSG; +} |