[systemd-commits] Makefile.am src/libsystemd src/libsystemd-dhcp
Tom Gundersen
tomegun at kemper.freedesktop.org
Mon Jan 20 15:50:57 PST 2014
Makefile.am | 74 +
src/libsystemd-dhcp/Makefile | 1
src/libsystemd-dhcp/dhcp-internal.h | 44 +
src/libsystemd-dhcp/dhcp-network.c | 106 ++
src/libsystemd-dhcp/dhcp-option.c | 184 ++++
src/libsystemd-dhcp/dhcp-protocol.h | 120 +++
src/libsystemd-dhcp/sd-dhcp-client.c | 1254 +++++++++++++++++++++++++++++++++
src/libsystemd-dhcp/test-dhcp-client.c | 243 ++++++
src/libsystemd-dhcp/test-dhcp-option.c | 378 +++++++++
src/libsystemd/dhcp-internal.h | 44 -
src/libsystemd/dhcp-network.c | 106 --
src/libsystemd/dhcp-option.c | 184 ----
src/libsystemd/dhcp-protocol.h | 120 ---
src/libsystemd/sd-dhcp-client.c | 1254 ---------------------------------
src/libsystemd/test-dhcp-client.c | 243 ------
src/libsystemd/test-dhcp-option.c | 378 ---------
16 files changed, 2375 insertions(+), 2358 deletions(-)
New commits:
commit c61be55d23f7603a986dc2937dd7f9aa390ae79c
Author: Tom Gundersen <teg at jklm.no>
Date: Mon Jan 20 23:40:30 2014 +0100
libsystemd-dhcp: revert merge into libsystemd
Unlike the other merged libs, the rest of libsystemd will never depend on
sd-dhcp-client, so there is no reason not to keep it separate.
diff --git a/Makefile.am b/Makefile.am
index e6f411c..8a897cd 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1982,7 +1982,6 @@ libsystemd_la_SOURCES = \
src/systemd/sd-memfd.h \
src/systemd/sd-utf8.h \
src/systemd/sd-event.h \
- src/systemd/sd-dhcp-client.h \
src/systemd/sd-rtnl.h \
src/systemd/sd-resolve.h \
src/libsystemd/sd-bus.c \
@@ -2025,11 +2024,6 @@ libsystemd_la_SOURCES = \
src/libsystemd/sd-event.c \
src/libsystemd/event-util.h \
src/libsystemd/bus-protocol.h \
- src/libsystemd/sd-dhcp-client.c \
- src/libsystemd/dhcp-network.c \
- src/libsystemd/dhcp-option.c \
- src/libsystemd/dhcp-internal.h \
- src/libsystemd/dhcp-protocol.h \
src/libsystemd/sd-rtnl.c \
src/libsystemd/rtnl-internal.h \
src/libsystemd/rtnl-message.c \
@@ -2112,8 +2106,6 @@ tests += \
test-bus-creds \
test-bus-gvariant \
test-event \
- test-dhcp-option \
- test-dhcp-client \
test-rtnl \
test-resolve
@@ -2319,27 +2311,6 @@ test_event_LDADD = \
libsystemd-daemon-internal.la \
libsystemd-shared.la
-test_dhcp_option_SOURCES = \
- src/libsystemd/dhcp-protocol.h \
- src/libsystemd/dhcp-internal.h \
- src/libsystemd/test-dhcp-option.c
-
-test_dhcp_option_LDADD = \
- libsystemd-internal.la \
- libsystemd-shared.la
-
-test_dhcp_client_SOURCES = \
- src/systemd/sd-dhcp-client.h \
- src/libsystemd/dhcp-protocol.h \
- src/libsystemd/dhcp-internal.h \
- src/libsystemd/test-dhcp-client.c
-
-test_dhcp_client_LDADD = \
- libsystemd-internal.la \
- libsystemd-daemon-internal.la \
- libsystemd-id128-internal.la \
- libsystemd-shared.la
-
test_rtnl_SOURCES = \
src/libsystemd/test-rtnl.c
@@ -2378,6 +2349,49 @@ busctl_CFLAGS = \
$(CAP_CFLAGS)
# ------------------------------------------------------------------------------
+noinst_LTLIBRARIES += \
+ libsystemd-dhcp.la
+
+libsystemd_dhcp_la_SOURCES = \
+ src/systemd/sd-dhcp-client.h \
+ src/libsystemd-dhcp/sd-dhcp-client.c \
+ src/libsystemd-dhcp/dhcp-network.c \
+ src/libsystemd-dhcp/dhcp-option.c \
+ src/libsystemd-dhcp/dhcp-internal.h \
+ src/libsystemd-dhcp/dhcp-protocol.h
+
+libsystemd_dhcp_la_LIBADD = \
+ libsystemd-internal.la \
+ libsystemd-shared.la
+
+test_dhcp_option_SOURCES = \
+ src/libsystemd-dhcp/dhcp-protocol.h \
+ src/libsystemd-dhcp/dhcp-internal.h \
+ src/libsystemd-dhcp/test-dhcp-option.c
+
+test_dhcp_option_LDADD = \
+ libsystemd-dhcp.la \
+ libsystemd-internal.la \
+ libsystemd-shared.la
+
+test_dhcp_client_SOURCES = \
+ src/systemd/sd-dhcp-client.h \
+ src/libsystemd-dhcp/dhcp-protocol.h \
+ src/libsystemd-dhcp/dhcp-internal.h \
+ src/libsystemd-dhcp/test-dhcp-client.c
+
+test_dhcp_client_LDADD = \
+ libsystemd-dhcp.la \
+ libsystemd-internal.la \
+ libsystemd-daemon-internal.la \
+ libsystemd-id128-internal.la \
+ libsystemd-shared.la
+
+tests += \
+ test-dhcp-option \
+ test-dhcp-client
+
+# ------------------------------------------------------------------------------
if ENABLE_GTK_DOC
SUBDIRS += \
docs/libudev
@@ -4079,6 +4093,7 @@ systemd_networkd_LDADD = \
libudev-internal.la \
libsystemd-daemon-internal.la \
libsystemd-internal.la \
+ libsystemd-dhcp.la \
libsystemd-id128-internal.la \
libsystemd-label.la \
libsystemd-shared.la
@@ -4103,6 +4118,7 @@ test_network_SOURCES = \
test_network_LDADD = \
libudev-internal.la \
libsystemd-internal.la \
+ libsystemd-dhcp.la \
libsystemd-id128-internal.la \
libsystemd-daemon-internal.la \
libsystemd-label.la \
diff --git a/src/libsystemd-dhcp/Makefile b/src/libsystemd-dhcp/Makefile
new file mode 120000
index 0000000..d0b0e8e
--- /dev/null
+++ b/src/libsystemd-dhcp/Makefile
@@ -0,0 +1 @@
+../Makefile
\ No newline at end of file
diff --git a/src/libsystemd-dhcp/dhcp-internal.h b/src/libsystemd-dhcp/dhcp-internal.h
new file mode 100644
index 0000000..43b5b1d
--- /dev/null
+++ b/src/libsystemd-dhcp/dhcp-internal.h
@@ -0,0 +1,44 @@
+/*-*- 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 <linux/if_packet.h>
+
+#include "socket-util.h"
+
+#include "dhcp-protocol.h"
+
+int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link);
+int dhcp_network_bind_udp_socket(int index, be32_t client_address);
+int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
+ const void *packet, size_t len);
+int dhcp_network_send_udp_socket(int s, be32_t server_address,
+ const void *packet, size_t len);
+
+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-network.c b/src/libsystemd-dhcp/dhcp-network.c
new file mode 100644
index 0000000..b2de67e
--- /dev/null
+++ b/src/libsystemd-dhcp/dhcp-network.c
@@ -0,0 +1,106 @@
+/***
+ 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 <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <string.h>
+#include <linux/if_packet.h>
+#include <net/ethernet.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "socket-util.h"
+
+#include "dhcp-internal.h"
+
+int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link)
+{
+ int s;
+
+ assert(index > 0);
+ assert(link);
+
+ s = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+ htons(ETH_P_IP));
+ if (s < 0)
+ return -errno;
+
+ link->ll.sll_family = AF_PACKET;
+ link->ll.sll_protocol = htons(ETH_P_IP);
+ link->ll.sll_ifindex = index;
+ link->ll.sll_halen = ETH_ALEN;
+ memset(link->ll.sll_addr, 0xff, ETH_ALEN);
+
+ if (bind(s, &link->sa, sizeof(link->ll)) < 0) {
+ close_nointr_nofail(s);
+ return -errno;
+ }
+
+ return s;
+}
+
+int dhcp_network_bind_udp_socket(int index, be32_t client_address)
+{
+ int s;
+ union sockaddr_union src = {
+ .in.sin_family = AF_INET,
+ .in.sin_port = htobe16(DHCP_PORT_CLIENT),
+ .in.sin_addr.s_addr = client_address,
+ };
+
+ s = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
+ if (s < 0)
+ return -errno;
+
+ if (bind(s, &src.sa, sizeof(src.in)) < 0) {
+ close_nointr_nofail(s);
+ return -errno;
+ }
+
+ return s;
+}
+
+int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
+ const void *packet, size_t len)
+{
+ assert(link);
+ assert(packet);
+ assert(len);
+
+ if (sendto(s, packet, len, 0, &link->sa, sizeof(link->ll)) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int dhcp_network_send_udp_socket(int s, be32_t server_address,
+ const void *packet, size_t len)
+{
+ union sockaddr_union dest = {
+ .in.sin_family = AF_INET,
+ .in.sin_port = htobe16(DHCP_PORT_SERVER),
+ .in.sin_addr.s_addr = server_address,
+ };
+
+ if (sendto(s, packet, len, 0, &dest.sa, sizeof(dest.in)) < 0)
+ return -errno;
+
+ return 0;
+}
diff --git a/src/libsystemd-dhcp/dhcp-option.c b/src/libsystemd-dhcp/dhcp-option.c
new file mode 100644
index 0000000..4d45b3b
--- /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;
+}
diff --git a/src/libsystemd-dhcp/dhcp-protocol.h b/src/libsystemd-dhcp/dhcp-protocol.h
new file mode 100644
index 0000000..abdfe8d
--- /dev/null
+++ b/src/libsystemd-dhcp/dhcp-protocol.h
@@ -0,0 +1,120 @@
+/*-*- 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 <netinet/udp.h>
+#include <netinet/ip.h>
+#include <stdint.h>
+
+#include "macro.h"
+#include "sparse-endian.h"
+
+struct DHCPMessage {
+ uint8_t op;
+ uint8_t htype;
+ uint8_t hlen;
+ uint8_t hops;
+ be32_t xid;
+ be16_t secs;
+ be16_t flags;
+ be32_t ciaddr;
+ be32_t yiaddr;
+ be32_t siaddr;
+ be32_t giaddr;
+ uint8_t chaddr[16];
+ uint8_t sname[64];
+ uint8_t file[128];
+} _packed_;
+
+typedef struct DHCPMessage DHCPMessage;
+
+struct DHCPPacket {
+ struct iphdr ip;
+ struct udphdr udp;
+ DHCPMessage dhcp;
+} _packed_;
+
+typedef struct DHCPPacket DHCPPacket;
+
+#define DHCP_IP_SIZE (int32_t)(sizeof(struct iphdr))
+#define DHCP_IP_UDP_SIZE (int32_t)(sizeof(struct udphdr) + DHCP_IP_SIZE)
+#define DHCP_MESSAGE_SIZE (int32_t)(sizeof(DHCPMessage))
+
+enum {
+ DHCP_PORT_SERVER = 67,
+ DHCP_PORT_CLIENT = 68,
+};
+
+enum DHCPState {
+ DHCP_STATE_INIT = 0,
+ DHCP_STATE_SELECTING = 1,
+ DHCP_STATE_INIT_REBOOT = 2,
+ DHCP_STATE_REBOOTING = 3,
+ DHCP_STATE_REQUESTING = 4,
+ DHCP_STATE_BOUND = 5,
+ DHCP_STATE_RENEWING = 6,
+ DHCP_STATE_REBINDING = 7,
+};
+
+typedef enum DHCPState DHCPState;
+
+enum {
+ BOOTREQUEST = 1,
+ BOOTREPLY = 2,
+};
+
+enum {
+ DHCP_DISCOVER = 1,
+ DHCP_OFFER = 2,
+ DHCP_REQUEST = 3,
+ DHCP_DECLINE = 4,
+ DHCP_ACK = 5,
+ DHCP_NAK = 6,
+ DHCP_RELEASE = 7,
+};
+
+enum {
+ DHCP_OVERLOAD_FILE = 1,
+ DHCP_OVERLOAD_SNAME = 2,
+};
+
+enum {
+ DHCP_OPTION_PAD = 0,
+ DHCP_OPTION_SUBNET_MASK = 1,
+ DHCP_OPTION_ROUTER = 3,
+ DHCP_OPTION_DOMAIN_NAME_SERVER = 6,
+ DHCP_OPTION_HOST_NAME = 12,
+ DHCP_OPTION_DOMAIN_NAME = 15,
+ DHCP_OPTION_INTERFACE_MTU = 26,
+ DHCP_OPTION_NTP_SERVER = 42,
+ DHCP_OPTION_REQUESTED_IP_ADDRESS = 50,
+ DHCP_OPTION_IP_ADDRESS_LEASE_TIME = 51,
+ DHCP_OPTION_OVERLOAD = 52,
+ DHCP_OPTION_MESSAGE_TYPE = 53,
+ DHCP_OPTION_SERVER_IDENTIFIER = 54,
+ DHCP_OPTION_PARAMETER_REQUEST_LIST = 55,
+ DHCP_OPTION_MAXIMUM_MESSAGE_SIZE = 57,
+ DHCP_OPTION_RENEWAL_T1_TIME = 58,
+ DHCP_OPTION_REBINDING_T2_TIME = 59,
+ DHCP_OPTION_CLIENT_IDENTIFIER = 61,
+ DHCP_OPTION_END = 255,
+};
diff --git a/src/libsystemd-dhcp/sd-dhcp-client.c b/src/libsystemd-dhcp/sd-dhcp-client.c
new file mode 100644
index 0000000..3b7b9f4
--- /dev/null
+++ b/src/libsystemd-dhcp/sd-dhcp-client.c
@@ -0,0 +1,1254 @@
+/***
+ 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 <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <net/ethernet.h>
+#include <sys/param.h>
+
+#include "util.h"
+#include "list.h"
+
+#include "dhcp-protocol.h"
+#include "dhcp-internal.h"
+#include "sd-dhcp-client.h"
+
+#define DHCP_CLIENT_MIN_OPTIONS_SIZE 312
+
+#define client_state_machine_check(s, r) \
+ do { \
+ if (s != DHCP_STATE_BOUND && \
+ s != DHCP_STATE_RENEWING && \
+ s != DHCP_STATE_REBINDING) { \
+ return (r); \
+ } \
+ } while (false)
+
+struct DHCPLease {
+ uint32_t t1;
+ uint32_t t2;
+ uint32_t lifetime;
+ be32_t address;
+ be32_t server_address;
+ be32_t subnet_mask;
+ be32_t router;
+ struct in_addr *dns;
+ size_t dns_size;
+ uint16_t mtu;
+ char *domainname;
+ char *hostname;
+};
+
+typedef struct DHCPLease DHCPLease;
+
+struct sd_dhcp_client {
+ DHCPState state;
+ sd_event *event;
+ int event_priority;
+ sd_event_source *timeout_resend;
+ int index;
+ int fd;
+ union sockaddr_union link;
+ sd_event_source *receive_message;
+ uint8_t *req_opts;
+ size_t req_opts_allocated;
+ size_t req_opts_size;
+ be32_t last_addr;
+ struct ether_addr mac_addr;
+ uint32_t xid;
+ usec_t start_time;
+ unsigned int attempt;
+ usec_t request_sent;
+ sd_event_source *timeout_t1;
+ sd_event_source *timeout_t2;
+ sd_event_source *timeout_expire;
+ sd_dhcp_client_cb_t cb;
+ void *userdata;
+ DHCPLease *lease;
+};
+
+static const uint8_t default_req_opts[] = {
+ DHCP_OPTION_SUBNET_MASK,
+ DHCP_OPTION_ROUTER,
+ DHCP_OPTION_HOST_NAME,
+ DHCP_OPTION_DOMAIN_NAME,
+ DHCP_OPTION_DOMAIN_NAME_SERVER,
+ DHCP_OPTION_NTP_SERVER,
+};
+
+static int client_receive_message(sd_event_source *s, int fd,
+ uint32_t revents, void *userdata);
+
+int sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_cb_t cb,
+ void *userdata) {
+ assert_return(client, -EINVAL);
+
+ client->cb = cb;
+ client->userdata = userdata;
+
+ return 0;
+}
+
+int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) {
+ size_t i;
+
+ assert_return(client, -EINVAL);
+ assert_return (client->state == DHCP_STATE_INIT, -EBUSY);
+
+ switch(option) {
+ case DHCP_OPTION_PAD:
+ case DHCP_OPTION_OVERLOAD:
+ case DHCP_OPTION_MESSAGE_TYPE:
+ case DHCP_OPTION_PARAMETER_REQUEST_LIST:
+ case DHCP_OPTION_END:
+ return -EINVAL;
+
+ default:
+ break;
+ }
+
+ for (i = 0; i < client->req_opts_size; i++)
+ if (client->req_opts[i] == option)
+ return -EEXIST;
+
+ if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
+ client->req_opts_size + 1))
+ return -ENOMEM;
+
+ client->req_opts[client->req_opts_size++] = option;
+
+ return 0;
+}
+
+int sd_dhcp_client_set_request_address(sd_dhcp_client *client,
+ const struct in_addr *last_addr) {
+ assert_return(client, -EINVAL);
+ assert_return(client->state == DHCP_STATE_INIT, -EBUSY);
+
+ if (last_addr)
+ client->last_addr = last_addr->s_addr;
+ else
+ client->last_addr = INADDR_ANY;
+
+ return 0;
+}
+
+int sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index) {
+ assert_return(client, -EINVAL);
+ assert_return(client->state == DHCP_STATE_INIT, -EBUSY);
+ assert_return(interface_index >= -1, -EINVAL);
+
+ client->index = interface_index;
+
+ return 0;
+}
+
+int sd_dhcp_client_set_mac(sd_dhcp_client *client,
+ const struct ether_addr *addr) {
+ assert_return(client, -EINVAL);
+ assert_return(client->state == DHCP_STATE_INIT, -EBUSY);
+
+ memcpy(&client->mac_addr, addr, ETH_ALEN);
+
+ return 0;
+}
+
+int sd_dhcp_client_get_address(sd_dhcp_client *client, struct in_addr *addr) {
+ assert_return(client, -EINVAL);
+ assert_return(addr, -EINVAL);
+
+ client_state_machine_check (client->state, -EADDRNOTAVAIL);
+
+ addr->s_addr = client->lease->address;
+
+ return 0;
+}
+
+int sd_dhcp_client_get_mtu(sd_dhcp_client *client, uint16_t *mtu) {
+ assert_return(client, -EINVAL);
+ assert_return(mtu, -EINVAL);
+
+ client_state_machine_check (client->state, -EADDRNOTAVAIL);
+
+ if (client->lease->mtu)
+ *mtu = client->lease->mtu;
+ else
+ return -ENOENT;
+
+ return 0;
+}
+
+int sd_dhcp_client_get_dns(sd_dhcp_client *client, struct in_addr **addr, size_t *addr_size) {
+ assert_return(client, -EINVAL);
+ assert_return(addr, -EINVAL);
+ assert_return(addr_size, -EINVAL);
+
+ client_state_machine_check (client->state, -EADDRNOTAVAIL);
+
+ if (client->lease->dns_size) {
+ *addr_size = client->lease->dns_size;
+ *addr = client->lease->dns;
+ } else
+ return -ENOENT;
+
+ return 0;
+}
+
+int sd_dhcp_client_get_domainname(sd_dhcp_client *client, const char **domainname) {
+ assert_return(client, -EINVAL);
+ assert_return(domainname, -EINVAL);
+
+ client_state_machine_check (client->state, -EADDRNOTAVAIL);
+
+ if (client->lease->domainname)
+ *domainname = client->lease->domainname;
+ else
+ return -ENOENT;
+
+ return 0;
+}
+
+int sd_dhcp_client_get_hostname(sd_dhcp_client *client, const char **hostname) {
+ assert_return(client, -EINVAL);
+ assert_return(hostname, -EINVAL);
+
+ client_state_machine_check (client->state, -EADDRNOTAVAIL);
+
+ if (client->lease->hostname)
+ *hostname = client->lease->hostname;
+ else
+ return -ENOENT;
+
+ return 0;
+}
+
+int sd_dhcp_client_prefixlen(const struct in_addr *addr) {
+ int len = 0;
+ uint32_t mask;
+
+ assert_return(addr, -EADDRNOTAVAIL);
+
+ mask = be32toh(addr->s_addr);
+ while (mask) {
+ len++;
+ mask = mask << 1;
+ }
+
+ return len;
+}
+
+int sd_dhcp_client_get_router(sd_dhcp_client *client, struct in_addr *addr) {
+ assert_return(client, -EINVAL);
+ assert_return(addr, -EINVAL);
+
+ client_state_machine_check (client->state, -EADDRNOTAVAIL);
+
+ addr->s_addr = client->lease->router;
+
+ return 0;
+}
+
+int sd_dhcp_client_get_netmask(sd_dhcp_client *client, struct in_addr *addr) {
+ assert_return(client, -EINVAL);
+ assert_return(addr, -EINVAL);
+
+ client_state_machine_check (client->state, -EADDRNOTAVAIL);
+
+ addr->s_addr = client->lease->subnet_mask;
+
+ return 0;
+}
+
+static int client_notify(sd_dhcp_client *client, int event) {
+ if (client->cb)
+ client->cb(client, event, client->userdata);
+
+ return 0;
+}
+
+static void lease_free(DHCPLease *lease) {
+ if (!lease)
+ return;
+
+ free(lease->hostname);
+ free(lease->domainname);
+ free(lease->dns);
+ free(lease);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPLease*, lease_free);
+#define _cleanup_lease_free_ _cleanup_(lease_freep)
+
+static int client_stop(sd_dhcp_client *client, int error) {
+ assert_return(client, -EINVAL);
+
+ client->receive_message =
+ sd_event_source_unref(client->receive_message);
+
+ if (client->fd >= 0)
+ close(client->fd);
+ client->fd = -1;
+
+ client->timeout_resend = sd_event_source_unref(client->timeout_resend);
+
+ client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
+ client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
+ client->timeout_expire = sd_event_source_unref(client->timeout_expire);
+
+ client->attempt = 1;
+
+ client_notify(client, error);
+
+ switch (client->state) {
+
+ case DHCP_STATE_INIT:
+ case DHCP_STATE_SELECTING:
+ case DHCP_STATE_REQUESTING:
+ case DHCP_STATE_BOUND:
+
+ client->start_time = 0;
+ client->state = DHCP_STATE_INIT;
+ break;
+
+ case DHCP_STATE_INIT_REBOOT:
+ case DHCP_STATE_REBOOTING:
+ case DHCP_STATE_RENEWING:
+ case DHCP_STATE_REBINDING:
+
+ break;
+ }
+
+ if (client->lease) {
+ lease_free(client->lease);
+ client->lease = NULL;
+ }
+
+ return 0;
+}
+
+static int client_packet_init(sd_dhcp_client *client, uint8_t type,
+ DHCPMessage *message, uint16_t secs,
+ uint8_t **opt, size_t *optlen) {
+ int err;
+ be16_t max_size;
+
+ *opt = (uint8_t *)(message + 1);
+
+ if (*optlen < 4)
+ return -ENOBUFS;
+ *optlen -= 4;
+
+ message->op = BOOTREQUEST;
+ message->htype = 1;
+ message->hlen = ETHER_ADDR_LEN;
+ message->xid = htobe32(client->xid);
+
+ /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers
+ refuse to issue an DHCP lease if 'secs' is set to zero */
+ message->secs = htobe16(secs);
+
+ if (client->state == DHCP_STATE_RENEWING ||
+ client->state == DHCP_STATE_REBINDING)
+ message->ciaddr = client->lease->address;
+
+ memcpy(&message->chaddr, &client->mac_addr, ETH_ALEN);
+ (*opt)[0] = 0x63;
+ (*opt)[1] = 0x82;
+ (*opt)[2] = 0x53;
+ (*opt)[3] = 0x63;
+
+ *opt += 4;
+
+ err = dhcp_option_append(opt, optlen, DHCP_OPTION_MESSAGE_TYPE, 1,
+ &type);
+ if (err < 0)
+ return err;
+
+ /* Some DHCP servers will refuse to issue an DHCP lease if the Cliient
+ Identifier option is not set */
+ err = dhcp_option_append(opt, optlen, DHCP_OPTION_CLIENT_IDENTIFIER,
+ ETH_ALEN, &client->mac_addr);
+ if (err < 0)
+ return err;
+
+ if (type == DHCP_DISCOVER || type == DHCP_REQUEST) {
+ err = dhcp_option_append(opt, optlen,
+ DHCP_OPTION_PARAMETER_REQUEST_LIST,
+ client->req_opts_size,
+ client->req_opts);
+ if (err < 0)
+ return err;
+
+ /* Some DHCP servers will send bigger DHCP packets than the
+ defined default size unless the Maximum Messge Size option
+ is explicitely set */
+ max_size = htobe16(DHCP_IP_UDP_SIZE + DHCP_MESSAGE_SIZE +
+ DHCP_CLIENT_MIN_OPTIONS_SIZE);
+ err = dhcp_option_append(opt, optlen,
+ DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
+ 2, &max_size);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static uint16_t client_checksum(void *buf, int len) {
+ uint32_t sum;
+ uint16_t *check;
+ int i;
+ uint8_t *odd;
+
+ sum = 0;
+ check = buf;
+
+ for (i = 0; i < len / 2 ; i++)
+ sum += check[i];
+
+ if (len & 0x01) {
+ odd = buf;
+ sum += odd[len - 1];
+ }
+
+ while (sum >> 16)
+ sum = (sum & 0xffff) + (sum >> 16);
+
+ return ~sum;
+}
+
+static void client_append_ip_headers(DHCPPacket *packet, uint16_t len) {
+ packet->ip.version = IPVERSION;
+ packet->ip.ihl = DHCP_IP_SIZE / 4;
+ packet->ip.tot_len = htobe16(len);
+
+ packet->ip.protocol = IPPROTO_UDP;
+ packet->ip.saddr = INADDR_ANY;
+ packet->ip.daddr = INADDR_BROADCAST;
+
+ packet->udp.source = htobe16(DHCP_PORT_CLIENT);
+ packet->udp.dest = htobe16(DHCP_PORT_SERVER);
+ packet->udp.len = htobe16(len - DHCP_IP_SIZE);
+
+ packet->ip.check = packet->udp.len;
+ packet->udp.check = client_checksum(&packet->ip.ttl, len - 8);
+
+ packet->ip.ttl = IPDEFTTL;
+ packet->ip.check = 0;
+ packet->ip.check = client_checksum(&packet->ip, DHCP_IP_SIZE);
+}
+
+static int client_send_discover(sd_dhcp_client *client, uint16_t secs) {
+ int err = 0;
+ _cleanup_free_ DHCPPacket *discover;
+ size_t optlen, len;
+ uint8_t *opt;
+
+ optlen = DHCP_CLIENT_MIN_OPTIONS_SIZE;
+ len = sizeof(DHCPPacket) + optlen;
+
+ discover = malloc0(len);
+
+ if (!discover)
+ return -ENOMEM;
+
+ err = client_packet_init(client, DHCP_DISCOVER, &discover->dhcp,
+ secs, &opt, &optlen);
+ if (err < 0)
+ return err;
+
+ if (client->last_addr != INADDR_ANY) {
+ err = dhcp_option_append(&opt, &optlen,
+ DHCP_OPTION_REQUESTED_IP_ADDRESS,
+ 4, &client->last_addr);
+ if (err < 0)
+ return err;
+ }
+
+ err = dhcp_option_append(&opt, &optlen, DHCP_OPTION_END, 0, NULL);
+ if (err < 0)
+ return err;
+
+ client_append_ip_headers(discover, len);
+
+ err = dhcp_network_send_raw_socket(client->fd, &client->link,
+ discover, len);
+
+ return err;
+}
+
+static int client_send_request(sd_dhcp_client *client, uint16_t secs) {
+ _cleanup_free_ DHCPPacket *request;
+ size_t optlen, len;
+ int err;
+ uint8_t *opt;
+
+ optlen = DHCP_CLIENT_MIN_OPTIONS_SIZE;
+ len = DHCP_MESSAGE_SIZE + optlen;
+
+ request = malloc0(len);
+ if (!request)
+ return -ENOMEM;
+
+ err = client_packet_init(client, DHCP_REQUEST, &request->dhcp, secs,
+ &opt, &optlen);
+ if (err < 0)
+ return err;
+
+ if (client->state == DHCP_STATE_REQUESTING) {
+ err = dhcp_option_append(&opt, &optlen,
+ DHCP_OPTION_REQUESTED_IP_ADDRESS,
+ 4, &client->lease->address);
+ if (err < 0)
+ return err;
+
+ err = dhcp_option_append(&opt, &optlen,
+ DHCP_OPTION_SERVER_IDENTIFIER,
+ 4, &client->lease->server_address);
+ if (err < 0)
+ return err;
+ }
+
+ err = dhcp_option_append(&opt, &optlen, DHCP_OPTION_END, 0, NULL);
+ if (err < 0)
+ return err;
+
+ if (client->state == DHCP_STATE_RENEWING) {
+ err = dhcp_network_send_udp_socket(client->fd,
+ client->lease->server_address,
+ &request->dhcp,
+ len - DHCP_IP_UDP_SIZE);
+ } else {
+ client_append_ip_headers(request, len);
+
+ err = dhcp_network_send_raw_socket(client->fd, &client->link,
+ request, len);
+ }
+
+ return err;
+}
+
+static int client_timeout_resend(sd_event_source *s, uint64_t usec,
+ void *userdata) {
+ sd_dhcp_client *client = userdata;
+ usec_t next_timeout = 0;
+ uint32_t time_left;
+ uint16_t secs;
+ int r = 0;
+
+ assert(s);
+ assert(client);
+ assert(client->event);
+
+ switch (client->state) {
+ case DHCP_STATE_RENEWING:
+
+ time_left = (client->lease->t2 - client->lease->t1)/2;
+ if (time_left < 60)
+ time_left = 60;
+
+ next_timeout = usec + time_left * USEC_PER_SEC;
+
+ break;
+
+ case DHCP_STATE_REBINDING:
+
+ time_left = (client->lease->lifetime - client->lease->t2)/2;
+ if (time_left < 60)
+ time_left = 60;
+
+ next_timeout = usec + time_left * USEC_PER_SEC;
+ break;
+
+ case DHCP_STATE_INIT:
+ case DHCP_STATE_INIT_REBOOT:
+ case DHCP_STATE_REBOOTING:
+ case DHCP_STATE_SELECTING:
+ case DHCP_STATE_REQUESTING:
+ case DHCP_STATE_BOUND:
+
+ if (client->attempt < 64)
+ client->attempt *= 2;
+
+ next_timeout = usec + (client->attempt - 1) * USEC_PER_SEC;
+
+ break;
+ }
+
+ next_timeout += (random_u32() & 0x1fffff);
+
+ r = sd_event_add_monotonic(client->event, next_timeout,
+ 10 * USEC_PER_MSEC,
+ client_timeout_resend, client,
+ &client->timeout_resend);
+ if (r < 0)
+ goto error;
+
+ r = sd_event_source_set_priority(client->timeout_resend, client->event_priority);
+ if (r < 0)
+ goto error;
+
+ secs = (usec - client->start_time) / USEC_PER_SEC;
+
+ switch (client->state) {
+ case DHCP_STATE_INIT:
+ r = client_send_discover(client, secs);
+ if (r >= 0) {
+ client->state = DHCP_STATE_SELECTING;
+ client->attempt = 1;
+ } else {
+ if (client->attempt >= 64)
+ goto error;
+ }
+
+ break;
+
+ case DHCP_STATE_SELECTING:
+ r = client_send_discover(client, secs);
+ if (r < 0 && client->attempt >= 64)
+ goto error;
+
+ break;
+
+ case DHCP_STATE_REQUESTING:
+ case DHCP_STATE_RENEWING:
+ case DHCP_STATE_REBINDING:
+ r = client_send_request(client, secs);
+ if (r < 0 && client->attempt >= 64)
+ goto error;
+
+ client->request_sent = usec;
+
+ break;
+
+ case DHCP_STATE_INIT_REBOOT:
+ case DHCP_STATE_REBOOTING:
+ case DHCP_STATE_BOUND:
+
+ break;
+ }
+
+ return 0;
+
+error:
+ client_stop(client, r);
+
+ /* Errors were dealt with when stopping the client, don't spill
+ errors into the event loop handler */
+ return 0;
+}
+
+static int client_initialize_events(sd_dhcp_client *client, usec_t usec) {
+ int r;
+
+ assert(client);
+ assert(client->event);
+
+ r = sd_event_add_io(client->event, client->fd, EPOLLIN,
+ client_receive_message, client,
+ &client->receive_message);
+ if (r < 0)
+ goto error;
+
+ r = sd_event_source_set_priority(client->receive_message, client->event_priority);
+ if (r < 0)
+ goto error;
+
+ r = sd_event_add_monotonic(client->event, usec, 0,
+ client_timeout_resend, client,
+ &client->timeout_resend);
+ if (r < 0)
+ goto error;
+
+ r = sd_event_source_set_priority(client->timeout_resend, client->event_priority);
+
+error:
+ if (r < 0)
+ client_stop(client, r);
+
+ return 0;
+
+}
+
+static int client_timeout_expire(sd_event_source *s, uint64_t usec,
+ void *userdata) {
+ sd_dhcp_client *client = userdata;
+
+ client_stop(client, DHCP_EVENT_EXPIRED);
+
+ return 0;
+}
+
+static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
+ sd_dhcp_client *client = userdata;
+ int r;
+
+ if (client->fd >= 0) {
+ client->receive_message =
+ sd_event_source_unref(client->receive_message);
+ close(client->fd);
+ client->fd = -1;
+ }
+
+ client->state = DHCP_STATE_REBINDING;
+ client->attempt = 1;
+
+ r = dhcp_network_bind_raw_socket(client->index, &client->link);
+ if (r < 0) {
+ client_stop(client, r);
+ return 0;
+ }
+
+ client->fd = r;
+
+ return client_initialize_events(client, usec);
+}
+
+static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) {
+ sd_dhcp_client *client = userdata;
+ int r;
+
+ client->state = DHCP_STATE_RENEWING;
+ client->attempt = 1;
+
+ r = dhcp_network_bind_udp_socket(client->index,
+ client->lease->address);
+ if (r < 0) {
+ client_stop(client, r);
+ return 0;
+ }
+
+ client->fd = r;
+
+ return client_initialize_events(client, usec);
+}
+
+static int client_parse_offer(uint8_t code, uint8_t len, const uint8_t *option,
+ void *user_data) {
+ DHCPLease *lease = user_data;
+ be32_t val;
+
+ switch(code) {
+
+ case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
+ if (len == 4) {
+ memcpy(&val, option, 4);
+ lease->lifetime = be32toh(val);
+ }
+
+ break;
+
+ case DHCP_OPTION_SERVER_IDENTIFIER:
+ if (len >= 4)
+ memcpy(&lease->server_address, option, 4);
+
+ break;
+
+ case DHCP_OPTION_SUBNET_MASK:
+ if (len >= 4)
+ memcpy(&lease->subnet_mask, option, 4);
+
+ break;
+
+ case DHCP_OPTION_ROUTER:
+ if (len >= 4)
+ memcpy(&lease->router, option, 4);
+
+ break;
+
+ case DHCP_OPTION_DOMAIN_NAME_SERVER:
+ if (len >= 4) {
+ unsigned i;
+
+ lease->dns_size = len / 4;
+
+ free(lease->dns);
+ lease->dns = new0(struct in_addr, lease->dns_size);
+ if (!lease->dns)
+ return -ENOMEM;
+
+ for (i = 0; i < lease->dns_size; i++) {
+ memcpy(&lease->dns[i].s_addr, option + 4 * i, 4);
+ }
+ }
+
+ break;
+
+ case DHCP_OPTION_INTERFACE_MTU:
+ if (len >= 2) {
+ be16_t mtu;
+
+ memcpy(&mtu, option, 2);
+ lease->mtu = be16toh(mtu);
+
+ if (lease->mtu < 68)
+ lease->mtu = 0;
+ }
+
+ break;
+
+ case DHCP_OPTION_DOMAIN_NAME:
+ if (len >= 1) {
+ free(lease->domainname);
+ lease->domainname = strndup((const char *)option, len);
+ }
+
+ break;
+
+ case DHCP_OPTION_HOST_NAME:
+ if (len >= 1) {
+ free(lease->hostname);
+ lease->hostname = strndup((const char *)option, len);
+ }
+
+ break;
+
+ case DHCP_OPTION_RENEWAL_T1_TIME:
+ if (len == 4) {
+ memcpy(&val, option, 4);
+ lease->t1 = be32toh(val);
+ }
+
+ break;
+
+ case DHCP_OPTION_REBINDING_T2_TIME:
+ if (len == 4) {
+ memcpy(&val, option, 4);
+ lease->t2 = be32toh(val);
+ }
+
+ break;
+ }
+
+ return 0;
+}
+
+static int client_verify_headers(sd_dhcp_client *client, DHCPPacket *message,
+ size_t len) {
+ size_t hdrlen;
+
+ if (len < (DHCP_IP_UDP_SIZE + DHCP_MESSAGE_SIZE))
+ return -EINVAL;
+
+ hdrlen = message->ip.ihl * 4;
+ if (hdrlen < 20 || hdrlen > len || client_checksum(&message->ip,
+ hdrlen))
+ return -EINVAL;
+
+ message->ip.check = message->udp.len;
+ message->ip.ttl = 0;
+
+ if (hdrlen + be16toh(message->udp.len) > len ||
+ client_checksum(&message->ip.ttl, be16toh(message->udp.len) + 12))
+ return -EINVAL;
+
+ if (be16toh(message->udp.source) != DHCP_PORT_SERVER ||
+ be16toh(message->udp.dest) != DHCP_PORT_CLIENT)
+ return -EINVAL;
+
+ if (message->dhcp.op != BOOTREPLY)
+ return -EINVAL;
+
+ if (be32toh(message->dhcp.xid) != client->xid)
+ return -EINVAL;
+
+ if (memcmp(&message->dhcp.chaddr[0], &client->mac_addr.ether_addr_octet,
+ ETHER_ADDR_LEN))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int client_receive_offer(sd_dhcp_client *client, DHCPPacket *offer,
+ size_t len) {
+ _cleanup_lease_free_ DHCPLease *lease = NULL;
+ int r;
+
+ r = client_verify_headers(client, offer, len);
+ if (r < 0)
+ return r;
+
+ lease = new0(DHCPLease, 1);
+ if (!lease)
+ return -ENOMEM;
+
+ len = len - DHCP_IP_UDP_SIZE;
+ r = dhcp_option_parse(&offer->dhcp, len, client_parse_offer,
+ lease);
+ if (r != DHCP_OFFER)
+ return -ENOMSG;
+
+ lease->address = offer->dhcp.yiaddr;
+
+ if (lease->address == INADDR_ANY ||
+ lease->server_address == INADDR_ANY ||
+ lease->subnet_mask == INADDR_ANY ||
+ lease->lifetime == 0)
+ return -ENOMSG;
+
+ client->lease = lease;
+ lease = NULL;
+
+ return 0;
+}
+
+static int client_receive_ack(sd_dhcp_client *client, const uint8_t *buf,
+ size_t len) {
+ DHCPPacket *ack;
+ DHCPMessage *dhcp;
+ _cleanup_lease_free_ DHCPLease *lease = NULL;
+ int r;
+
+ if (client->state == DHCP_STATE_RENEWING) {
+ dhcp = (DHCPMessage *)buf;
+ } else {
+ ack = (DHCPPacket *)buf;
+
+ r = client_verify_headers(client, ack, len);
+ if (r < 0)
+ return r;
+
+ dhcp = &ack->dhcp;
+ len -= DHCP_IP_UDP_SIZE;
+ }
+
+ lease = new0(DHCPLease, 1);
+ if (!lease)
+ return -ENOMEM;
+
+ r = dhcp_option_parse(dhcp, len, client_parse_offer, lease);
+ if (r == DHCP_NAK)
+ return DHCP_EVENT_NO_LEASE;
+
+ if (r != DHCP_ACK)
+ return -ENOMSG;
+
+ lease->address = dhcp->yiaddr;
+
+ if (lease->address == INADDR_ANY ||
+ lease->server_address == INADDR_ANY ||
+ lease->subnet_mask == INADDR_ANY || lease->lifetime == 0)
+ return -ENOMSG;
+
+ r = DHCP_EVENT_IP_ACQUIRE;
+ if (client->lease) {
+ if (client->lease->address != lease->address ||
+ client->lease->subnet_mask != lease->subnet_mask ||
+ client->lease->router != lease->router) {
+ r = DHCP_EVENT_IP_CHANGE;
+ }
+
+ lease_free(client->lease);
+ }
+
+ client->lease = lease;
+ lease = NULL;
+
+ return r;
+}
+
+static uint64_t client_compute_timeout(uint64_t request_sent,
+ uint32_t lifetime) {
+ return request_sent + (lifetime - 3) * USEC_PER_SEC +
+ + (random_u32() & 0x1fffff);
+}
+
+static int client_set_lease_timeouts(sd_dhcp_client *client, uint64_t usec) {
+ uint64_t next_timeout;
+ int r;
+
+ assert(client);
+ assert(client->event);
+
+ if (client->lease->lifetime < 10)
+ return -EINVAL;
+
+ client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
+ client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
+ client->timeout_expire = sd_event_source_unref(client->timeout_expire);
+
+ if (!client->lease->t1)
+ client->lease->t1 = client->lease->lifetime / 2;
+
+ next_timeout = client_compute_timeout(client->request_sent,
+ client->lease->t1);
+ if (next_timeout < usec)
+ return -EINVAL;
+
+ r = sd_event_add_monotonic(client->event, next_timeout,
+ 10 * USEC_PER_MSEC,
+ client_timeout_t1, client,
+ &client->timeout_t1);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_priority(client->timeout_t1, client->event_priority);
+ if (r < 0)
+ return r;
+
+ if (!client->lease->t2)
+ client->lease->t2 = client->lease->lifetime * 7 / 8;
+
+ if (client->lease->t2 < client->lease->t1)
+ return -EINVAL;
+
+ if (client->lease->lifetime < client->lease->t2)
+ return -EINVAL;
+
+ next_timeout = client_compute_timeout(client->request_sent,
+ client->lease->t2);
+ if (next_timeout < usec)
+ return -EINVAL;
+
+ r = sd_event_add_monotonic(client->event, next_timeout,
+ 10 * USEC_PER_MSEC,
+ client_timeout_t2, client,
+ &client->timeout_t2);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_priority(client->timeout_t2, client->event_priority);
+ if (r < 0)
+ return r;
+
+ next_timeout = client_compute_timeout(client->request_sent,
+ client->lease->lifetime);
+ if (next_timeout < usec)
+ return -EINVAL;
+
+ r = sd_event_add_monotonic(client->event, next_timeout,
+ 10 * USEC_PER_MSEC,
+ client_timeout_expire, client,
+ &client->timeout_expire);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_priority(client->timeout_expire, client->event_priority);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static int client_receive_message(sd_event_source *s, int fd,
+ uint32_t revents, void *userdata) {
+ sd_dhcp_client *client = userdata;
+ uint8_t buf[sizeof(DHCPPacket) + DHCP_CLIENT_MIN_OPTIONS_SIZE];
+ int buflen = sizeof(buf);
+ int len, r = 0, notify_event = 0;
+ DHCPPacket *message;
+ usec_t time_now;
+
+ assert(s);
+ assert(client);
+ assert(client->event);
+
+ len = read(fd, &buf, buflen);
+ if (len < 0)
+ return 0;
+
+ r = sd_event_get_now_monotonic(client->event, &time_now);
+ if (r < 0)
+ goto error;
+
+ switch (client->state) {
+ case DHCP_STATE_SELECTING:
+
+ message = (DHCPPacket *)&buf;
+
+ if (client_receive_offer(client, message, len) >= 0) {
+
+ client->timeout_resend =
+ sd_event_source_unref(client->timeout_resend);
+
+ client->state = DHCP_STATE_REQUESTING;
+ client->attempt = 1;
+
+ r = sd_event_add_monotonic(client->event, time_now, 0,
+ client_timeout_resend,
+ client,
+ &client->timeout_resend);
+ if (r < 0)
+ goto error;
+
+ r = sd_event_source_set_priority(client->timeout_resend, client->event_priority);
+ if (r < 0)
+ goto error;
+ }
+
+ break;
+
+ case DHCP_STATE_REQUESTING:
+ case DHCP_STATE_RENEWING:
+ case DHCP_STATE_REBINDING:
+
+ r = client_receive_ack(client, buf, len);
+
+ if (r == DHCP_EVENT_NO_LEASE)
+ goto error;
+
+ if (r >= 0) {
+ client->timeout_resend =
+ sd_event_source_unref(client->timeout_resend);
+
+ if (client->state == DHCP_STATE_REQUESTING)
+ notify_event = DHCP_EVENT_IP_ACQUIRE;
+ else if (r != DHCP_EVENT_IP_ACQUIRE)
+ notify_event = r;
+
+ client->state = DHCP_STATE_BOUND;
+ client->attempt = 1;
+
+ client->last_addr = client->lease->address;
+
+ r = client_set_lease_timeouts(client, time_now);
+ if (r < 0)
+ goto error;
+
+ if (notify_event)
+ client_notify(client, notify_event);
+
+ client->receive_message =
+ sd_event_source_unref(client->receive_message);
+ close(client->fd);
+ client->fd = -1;
+ }
+
+ r = 0;
+
+ break;
+
+ case DHCP_STATE_INIT:
+ case DHCP_STATE_INIT_REBOOT:
+ case DHCP_STATE_REBOOTING:
+ case DHCP_STATE_BOUND:
+
+ break;
+ }
+
+error:
+ if (r < 0 || r == DHCP_EVENT_NO_LEASE)
+ return client_stop(client, r);
+
+ return 0;
+}
+
+int sd_dhcp_client_start(sd_dhcp_client *client) {
+ int r;
+
+ assert_return(client, -EINVAL);
+ assert_return(client->event, -EINVAL);
+ assert_return(client->index > 0, -EINVAL);
+ assert_return(client->state == DHCP_STATE_INIT ||
+ client->state == DHCP_STATE_INIT_REBOOT, -EBUSY);
+
+ client->xid = random_u32();
+
+ r = dhcp_network_bind_raw_socket(client->index, &client->link);
+
+ if (r < 0) {
+ client_stop(client, r);
+ return r;
+ }
+
+ client->fd = r;
+ client->start_time = now(CLOCK_MONOTONIC);
+
+ return client_initialize_events(client, client->start_time);
+}
+
+int sd_dhcp_client_stop(sd_dhcp_client *client) {
+ return client_stop(client, DHCP_EVENT_STOP);
+}
+
+int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event, int priority) {
+ int r;
+
+ assert_return(client, -EINVAL);
+ assert_return(!client->event, -EBUSY);
+
+ if (event)
+ client->event = sd_event_ref(event);
+ else {
+ r = sd_event_default(&client->event);
+ if (r < 0)
+ return 0;
+ }
+
+ client->event_priority = priority;
+
+ return 0;
+}
+
+int sd_dhcp_client_detach_event(sd_dhcp_client *client) {
+ assert_return(client, -EINVAL);
+
+ client->event = sd_event_unref(client->event);
+
+ return 0;
+}
+
+sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client) {
+ if (!client)
+ return NULL;
+
+ return client->event;
+}
+
+void sd_dhcp_client_free(sd_dhcp_client *client) {
+ if (!client)
+ return;
+
+ sd_dhcp_client_stop(client);
+ sd_dhcp_client_detach_event(client);
+
+ free(client->req_opts);
+ free(client);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_client*, sd_dhcp_client_free);
+#define _cleanup_dhcp_client_free_ _cleanup_(sd_dhcp_client_freep)
+
+int sd_dhcp_client_new(sd_dhcp_client **ret) {
+ _cleanup_dhcp_client_free_ sd_dhcp_client *client = NULL;
+
+ assert_return(ret, -EINVAL);
+
+ client = new0(sd_dhcp_client, 1);
+ if (!client)
+ return -ENOMEM;
+
+ client->state = DHCP_STATE_INIT;
+ client->index = -1;
+ client->fd = -1;
+ client->attempt = 1;
+
+ client->req_opts_size = ELEMENTSOF(default_req_opts);
+
+ client->req_opts = memdup(default_req_opts, client->req_opts_size);
+ if (!client->req_opts)
+ return -ENOMEM;
+
+ *ret = client;
+ client = NULL;
+
+ return 0;
+}
diff --git a/src/libsystemd-dhcp/test-dhcp-client.c b/src/libsystemd-dhcp/test-dhcp-client.c
new file mode 100644
index 0000000..56a10b3
--- /dev/null
+++ b/src/libsystemd-dhcp/test-dhcp-client.c
@@ -0,0 +1,243 @@
+/*-*- 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 <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include "util.h"
+#include "socket-util.h"
+
+#include "dhcp-protocol.h"
+#include "dhcp-internal.h"
+#include "sd-dhcp-client.h"
+
+static struct ether_addr mac_addr = {
+ .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
+};
+
+static int test_fd[2];
+
+static void test_request_basic(sd_event *e)
+{
+ int r;
+
+ sd_dhcp_client *client;
+
+ r = sd_dhcp_client_new(&client);
+
+ assert(r >= 0);
+ assert(client);
+
+ r = sd_dhcp_client_attach_event(client, e, 0);
+ assert(r >= 0);
+
+ assert(sd_dhcp_client_set_request_option(NULL, 0) == -EINVAL);
+ assert(sd_dhcp_client_set_request_address(NULL, NULL) == -EINVAL);
+ assert(sd_dhcp_client_set_index(NULL, 0) == -EINVAL);
+
+ assert(sd_dhcp_client_set_index(client, 15) == 0);
+ assert(sd_dhcp_client_set_index(client, -42) == -EINVAL);
+ assert(sd_dhcp_client_set_index(client, -1) == 0);
+
+ assert(sd_dhcp_client_set_request_option(client,
+ DHCP_OPTION_SUBNET_MASK) == -EEXIST);
+ assert(sd_dhcp_client_set_request_option(client,
+ DHCP_OPTION_ROUTER) == -EEXIST);
+ assert(sd_dhcp_client_set_request_option(client,
+ DHCP_OPTION_HOST_NAME) == -EEXIST);
+ assert(sd_dhcp_client_set_request_option(client,
+ DHCP_OPTION_DOMAIN_NAME) == -EEXIST);
+ assert(sd_dhcp_client_set_request_option(client,
+ DHCP_OPTION_DOMAIN_NAME_SERVER)
+ == -EEXIST);
+ assert(sd_dhcp_client_set_request_option(client,
+ DHCP_OPTION_NTP_SERVER) == -EEXIST);
+
+ assert(sd_dhcp_client_set_request_option(client,
+ DHCP_OPTION_PAD) == -EINVAL);
+ assert(sd_dhcp_client_set_request_option(client,
+ DHCP_OPTION_END) == -EINVAL);
+ assert(sd_dhcp_client_set_request_option(client,
+ DHCP_OPTION_MESSAGE_TYPE) == -EINVAL);
+ assert(sd_dhcp_client_set_request_option(client,
+ DHCP_OPTION_OVERLOAD) == -EINVAL);
+ assert(sd_dhcp_client_set_request_option(client,
+ DHCP_OPTION_PARAMETER_REQUEST_LIST)
+ == -EINVAL);
+
+ assert(sd_dhcp_client_set_request_option(client, 33) == 0);
+ assert(sd_dhcp_client_set_request_option(client, 33) == -EEXIST);
+ assert(sd_dhcp_client_set_request_option(client, 44) == 0);
+ assert(sd_dhcp_client_set_request_option(client, 33) == -EEXIST);
+}
+
+static uint16_t client_checksum(void *buf, int len)
+{
+ uint32_t sum;
+ uint16_t *check;
+ int i;
+ uint8_t *odd;
+
+ sum = 0;
+ check = buf;
+
+ for (i = 0; i < len / 2 ; i++)
+ sum += check[i];
+
+ if (len & 0x01) {
+ odd = buf;
+ sum += odd[len - 1];
+ }
+
+ while (sum >> 16)
+ sum = (sum & 0xffff) + (sum >> 16);
+
+ return ~sum;
+}
+
+static void test_checksum(void)
+{
+ uint8_t buf[20] = {
+ 0x45, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff
+ };
+
+ assert(client_checksum(&buf, 20) == be16toh(0x78ae));
+}
+
+static int check_options(uint8_t code, uint8_t len, const uint8_t *option,
+ void *user_data)
+{
+ return 0;
+}
+
+int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
+ const void *packet, size_t len)
+{
+ size_t size;
+ _cleanup_free_ DHCPPacket *discover;
+ uint16_t ip_check, udp_check;
+ int res;
+
+ assert(s >= 0);
+ assert(packet);
+
+ size = sizeof(DHCPPacket) + 4;
+ assert(len > size);
+
+ discover = memdup(packet, len);
+
+ assert(memcmp(discover->dhcp.chaddr,
+ &mac_addr.ether_addr_octet, 6) == 0);
+ assert(discover->ip.ttl == IPDEFTTL);
+ assert(discover->ip.protocol == IPPROTO_UDP);
+ assert(discover->ip.saddr == INADDR_ANY);
+ assert(discover->ip.daddr == INADDR_BROADCAST);
+ assert(discover->udp.source == be16toh(DHCP_PORT_CLIENT));
+ assert(discover->udp.dest == be16toh(DHCP_PORT_SERVER));
+
+ ip_check = discover->ip.check;
+
+ discover->ip.ttl = 0;
+ discover->ip.check = discover->udp.len;
+
+ udp_check = ~client_checksum(&discover->ip.ttl, len - 8);
+ assert(udp_check == 0xffff);
+
+ discover->ip.ttl = IPDEFTTL;
+ discover->ip.check = ip_check;
+
+ ip_check = ~client_checksum(&discover->ip, sizeof(discover->ip));
+ assert(ip_check == 0xffff);
+
+ size = len - sizeof(struct iphdr) - sizeof(struct udphdr);
+
+ res = dhcp_option_parse(&discover->dhcp, size, check_options, NULL);
+ if (res < 0)
+ return res;
+
+ return 575;
+}
+
+int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link)
+{
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_fd) < 0)
+ return -errno;
+
+ return test_fd[0];
+}
+
+int dhcp_network_bind_udp_socket(int index, be32_t client_address)
+{
+ return 0;
+}
+
+int dhcp_network_send_udp_socket(int s, be32_t server_address,
+ const void *packet, size_t len)
+{
+ return 0;
+}
+
+static void test_discover_message(sd_event *e)
+{
+ sd_dhcp_client *client;
+ int res, r;
+
+ r = sd_dhcp_client_new(&client);
+ assert(r >= 0);
+ assert(client);
+
+ r = sd_dhcp_client_attach_event(client, e, 0);
+ assert(r >= 0);
+
+ assert(sd_dhcp_client_set_index(client, 42) >= 0);
+ assert(sd_dhcp_client_set_mac(client, &mac_addr) >= 0);
+
+ assert(sd_dhcp_client_set_request_option(client, 248) >= 0);
+
+ res = sd_dhcp_client_start(client);
+
+ assert(res == 0 || res == -EINPROGRESS);
+
+ close(test_fd[0]);
+ close(test_fd[1]);
+}
+
+int main(int argc, char *argv[])
+{
+ sd_event *e;
+
+ assert(sd_event_new(&e) >= 0);
+
+ test_request_basic(e);
+ test_checksum();
+
+ test_discover_message(e);
+ sd_event_run(e, (uint64_t) -1);
+
+ return 0;
+}
diff --git a/src/libsystemd-dhcp/test-dhcp-option.c b/src/libsystemd-dhcp/test-dhcp-option.c
new file mode 100644
index 0000000..ac0b0a4
--- /dev/null
+++ b/src/libsystemd-dhcp/test-dhcp-option.c
@@ -0,0 +1,378 @@
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <string.h>
+#include <assert.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;
+
+ assert(dhcp_option_parse(&message, 0, NULL, NULL) == -EINVAL);
+ assert(dhcp_option_parse(&message, sizeof(DHCPMessage), NULL, NULL)
+ == -EINVAL);
+}
+
+static void test_cookie(void)
+{
+ _cleanup_free_ DHCPMessage *message;
+ size_t len = sizeof(DHCPMessage) + 4;
+ uint8_t *opt;
+
+ message = malloc0(len);
+
+ opt = (uint8_t *)(message + 1);
+ opt[0] = 0xff;
+
+ assert(dhcp_option_parse(message, len, NULL, NULL) == -EINVAL);
+
+ opt[0] = 99;
+ opt[1] = 130;
+ opt[2] = 83;
+ 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;
+
+ 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);
+ 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));
+}
+
+static uint8_t result[64] = {
+ 'A', 'B', 'C', 'D',
+};
+
+static uint8_t options[64] = {
+ 'A', 'B', 'C', 'D',
+ 160, 2, 0x11, 0x12,
+ 0,
+ 31, 8, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
+ 0,
+ 55, 3, 0x51, 0x52, 0x53,
+ 17, 7, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+ 255
+};
+
+static void test_option_set(void)
+{
+ size_t len, oldlen;
+ int pos, i;
+ uint8_t *opt;
+
+ assert(dhcp_option_append(NULL, NULL, 0, 0, NULL) == -EINVAL);
+
+ len = 0;
+ opt = &result[0];
+ assert(dhcp_option_append(&opt, NULL, 0, 0, NULL) == -EINVAL);
+ assert(opt == &result[0] && len == 0);
+
+ assert(dhcp_option_append(&opt, &len, DHCP_OPTION_PAD,
+ 0, NULL) == -ENOBUFS);
+ assert(opt == &result[0] && len == 0);
+
+ opt = &result[4];
+ len = 1;
+ assert(dhcp_option_append(&opt, &len, DHCP_OPTION_PAD,
+ 0, NULL) >= 0);
+ assert(opt == &result[5] && len == 0);
+
+ pos = 4;
+ len = 60;
+ while (pos < 64 && options[pos] != DHCP_OPTION_END) {
+ opt = &result[pos];
+ oldlen = len;
+
+ assert(dhcp_option_append(&opt, &len, options[pos],
+ options[pos + 1],
+ &options[pos + 2]) >= 0);
+
+ if (options[pos] == DHCP_OPTION_PAD) {
+ assert(opt == &result[pos + 1]);
+ assert(len == oldlen - 1);
+ pos++;
+ } else {
+ assert(opt == &result[pos + 2 + options[pos + 1]]);
+ assert(len == oldlen - 2 - options[pos + 1]);
+ pos += 2 + options[pos + 1];
+ }
+ }
+
+ for (i = 0; i < pos; i++) {
+ if (verbose)
+ printf("%2d: 0x%02x(0x%02x)\n", i, result[i],
+ options[i]);
+ assert(result[i] == options[i]);
+ }
+
+ if (verbose)
+ printf ("\n");
+}
+
+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]);
+
+ test_option_set();
+
+ return 0;
+}
diff --git a/src/libsystemd/dhcp-internal.h b/src/libsystemd/dhcp-internal.h
deleted file mode 100644
index 43b5b1d..0000000
--- a/src/libsystemd/dhcp-internal.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*-*- 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 <linux/if_packet.h>
-
-#include "socket-util.h"
-
-#include "dhcp-protocol.h"
-
-int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link);
-int dhcp_network_bind_udp_socket(int index, be32_t client_address);
-int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
- const void *packet, size_t len);
-int dhcp_network_send_udp_socket(int s, be32_t server_address,
- const void *packet, size_t len);
-
-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-network.c b/src/libsystemd/dhcp-network.c
deleted file mode 100644
index b2de67e..0000000
--- a/src/libsystemd/dhcp-network.c
+++ /dev/null
@@ -1,106 +0,0 @@
-/***
- 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 <errno.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <string.h>
-#include <linux/if_packet.h>
-#include <net/ethernet.h>
-#include <stdio.h>
-#include <unistd.h>
-
-#include "socket-util.h"
-
-#include "dhcp-internal.h"
-
-int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link)
-{
- int s;
-
- assert(index > 0);
- assert(link);
-
- s = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
- htons(ETH_P_IP));
- if (s < 0)
- return -errno;
-
- link->ll.sll_family = AF_PACKET;
- link->ll.sll_protocol = htons(ETH_P_IP);
- link->ll.sll_ifindex = index;
- link->ll.sll_halen = ETH_ALEN;
- memset(link->ll.sll_addr, 0xff, ETH_ALEN);
-
- if (bind(s, &link->sa, sizeof(link->ll)) < 0) {
- close_nointr_nofail(s);
- return -errno;
- }
-
- return s;
-}
-
-int dhcp_network_bind_udp_socket(int index, be32_t client_address)
-{
- int s;
- union sockaddr_union src = {
- .in.sin_family = AF_INET,
- .in.sin_port = htobe16(DHCP_PORT_CLIENT),
- .in.sin_addr.s_addr = client_address,
- };
-
- s = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
- if (s < 0)
- return -errno;
-
- if (bind(s, &src.sa, sizeof(src.in)) < 0) {
- close_nointr_nofail(s);
- return -errno;
- }
-
- return s;
-}
-
-int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
- const void *packet, size_t len)
-{
- assert(link);
- assert(packet);
- assert(len);
-
- if (sendto(s, packet, len, 0, &link->sa, sizeof(link->ll)) < 0)
- return -errno;
-
- return 0;
-}
-
-int dhcp_network_send_udp_socket(int s, be32_t server_address,
- const void *packet, size_t len)
-{
- union sockaddr_union dest = {
- .in.sin_family = AF_INET,
- .in.sin_port = htobe16(DHCP_PORT_SERVER),
- .in.sin_addr.s_addr = server_address,
- };
-
- if (sendto(s, packet, len, 0, &dest.sa, sizeof(dest.in)) < 0)
- return -errno;
-
- return 0;
-}
diff --git a/src/libsystemd/dhcp-option.c b/src/libsystemd/dhcp-option.c
deleted file mode 100644
index 4d45b3b..0000000
--- a/src/libsystemd/dhcp-option.c
+++ /dev/null
@@ -1,184 +0,0 @@
-/*-*- 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;
-}
diff --git a/src/libsystemd/dhcp-protocol.h b/src/libsystemd/dhcp-protocol.h
deleted file mode 100644
index abdfe8d..0000000
--- a/src/libsystemd/dhcp-protocol.h
+++ /dev/null
@@ -1,120 +0,0 @@
-/*-*- 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 <netinet/udp.h>
-#include <netinet/ip.h>
-#include <stdint.h>
-
-#include "macro.h"
-#include "sparse-endian.h"
-
-struct DHCPMessage {
- uint8_t op;
- uint8_t htype;
- uint8_t hlen;
- uint8_t hops;
- be32_t xid;
- be16_t secs;
- be16_t flags;
- be32_t ciaddr;
- be32_t yiaddr;
- be32_t siaddr;
- be32_t giaddr;
- uint8_t chaddr[16];
- uint8_t sname[64];
- uint8_t file[128];
-} _packed_;
-
-typedef struct DHCPMessage DHCPMessage;
-
-struct DHCPPacket {
- struct iphdr ip;
- struct udphdr udp;
- DHCPMessage dhcp;
-} _packed_;
-
-typedef struct DHCPPacket DHCPPacket;
-
-#define DHCP_IP_SIZE (int32_t)(sizeof(struct iphdr))
-#define DHCP_IP_UDP_SIZE (int32_t)(sizeof(struct udphdr) + DHCP_IP_SIZE)
-#define DHCP_MESSAGE_SIZE (int32_t)(sizeof(DHCPMessage))
-
-enum {
- DHCP_PORT_SERVER = 67,
- DHCP_PORT_CLIENT = 68,
-};
-
-enum DHCPState {
- DHCP_STATE_INIT = 0,
- DHCP_STATE_SELECTING = 1,
- DHCP_STATE_INIT_REBOOT = 2,
- DHCP_STATE_REBOOTING = 3,
- DHCP_STATE_REQUESTING = 4,
- DHCP_STATE_BOUND = 5,
- DHCP_STATE_RENEWING = 6,
- DHCP_STATE_REBINDING = 7,
-};
-
-typedef enum DHCPState DHCPState;
-
-enum {
- BOOTREQUEST = 1,
- BOOTREPLY = 2,
-};
-
-enum {
- DHCP_DISCOVER = 1,
- DHCP_OFFER = 2,
- DHCP_REQUEST = 3,
- DHCP_DECLINE = 4,
- DHCP_ACK = 5,
- DHCP_NAK = 6,
- DHCP_RELEASE = 7,
-};
-
-enum {
- DHCP_OVERLOAD_FILE = 1,
- DHCP_OVERLOAD_SNAME = 2,
-};
-
-enum {
- DHCP_OPTION_PAD = 0,
- DHCP_OPTION_SUBNET_MASK = 1,
- DHCP_OPTION_ROUTER = 3,
- DHCP_OPTION_DOMAIN_NAME_SERVER = 6,
- DHCP_OPTION_HOST_NAME = 12,
- DHCP_OPTION_DOMAIN_NAME = 15,
- DHCP_OPTION_INTERFACE_MTU = 26,
- DHCP_OPTION_NTP_SERVER = 42,
- DHCP_OPTION_REQUESTED_IP_ADDRESS = 50,
- DHCP_OPTION_IP_ADDRESS_LEASE_TIME = 51,
- DHCP_OPTION_OVERLOAD = 52,
- DHCP_OPTION_MESSAGE_TYPE = 53,
- DHCP_OPTION_SERVER_IDENTIFIER = 54,
- DHCP_OPTION_PARAMETER_REQUEST_LIST = 55,
- DHCP_OPTION_MAXIMUM_MESSAGE_SIZE = 57,
- DHCP_OPTION_RENEWAL_T1_TIME = 58,
- DHCP_OPTION_REBINDING_T2_TIME = 59,
- DHCP_OPTION_CLIENT_IDENTIFIER = 61,
- DHCP_OPTION_END = 255,
-};
diff --git a/src/libsystemd/sd-dhcp-client.c b/src/libsystemd/sd-dhcp-client.c
deleted file mode 100644
index 3b7b9f4..0000000
--- a/src/libsystemd/sd-dhcp-client.c
+++ /dev/null
@@ -1,1254 +0,0 @@
-/***
- 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 <stdlib.h>
-#include <errno.h>
-#include <string.h>
-#include <stdio.h>
-#include <net/ethernet.h>
-#include <sys/param.h>
-
-#include "util.h"
-#include "list.h"
-
-#include "dhcp-protocol.h"
-#include "dhcp-internal.h"
-#include "sd-dhcp-client.h"
-
-#define DHCP_CLIENT_MIN_OPTIONS_SIZE 312
-
-#define client_state_machine_check(s, r) \
- do { \
- if (s != DHCP_STATE_BOUND && \
- s != DHCP_STATE_RENEWING && \
- s != DHCP_STATE_REBINDING) { \
- return (r); \
- } \
- } while (false)
-
-struct DHCPLease {
- uint32_t t1;
- uint32_t t2;
- uint32_t lifetime;
- be32_t address;
- be32_t server_address;
- be32_t subnet_mask;
- be32_t router;
- struct in_addr *dns;
- size_t dns_size;
- uint16_t mtu;
- char *domainname;
- char *hostname;
-};
-
-typedef struct DHCPLease DHCPLease;
-
-struct sd_dhcp_client {
- DHCPState state;
- sd_event *event;
- int event_priority;
- sd_event_source *timeout_resend;
- int index;
- int fd;
- union sockaddr_union link;
- sd_event_source *receive_message;
- uint8_t *req_opts;
- size_t req_opts_allocated;
- size_t req_opts_size;
- be32_t last_addr;
- struct ether_addr mac_addr;
- uint32_t xid;
- usec_t start_time;
- unsigned int attempt;
- usec_t request_sent;
- sd_event_source *timeout_t1;
- sd_event_source *timeout_t2;
- sd_event_source *timeout_expire;
- sd_dhcp_client_cb_t cb;
- void *userdata;
- DHCPLease *lease;
-};
-
-static const uint8_t default_req_opts[] = {
- DHCP_OPTION_SUBNET_MASK,
- DHCP_OPTION_ROUTER,
- DHCP_OPTION_HOST_NAME,
- DHCP_OPTION_DOMAIN_NAME,
- DHCP_OPTION_DOMAIN_NAME_SERVER,
- DHCP_OPTION_NTP_SERVER,
-};
-
-static int client_receive_message(sd_event_source *s, int fd,
- uint32_t revents, void *userdata);
-
-int sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_cb_t cb,
- void *userdata) {
- assert_return(client, -EINVAL);
-
- client->cb = cb;
- client->userdata = userdata;
-
- return 0;
-}
-
-int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) {
- size_t i;
-
- assert_return(client, -EINVAL);
- assert_return (client->state == DHCP_STATE_INIT, -EBUSY);
-
- switch(option) {
- case DHCP_OPTION_PAD:
- case DHCP_OPTION_OVERLOAD:
- case DHCP_OPTION_MESSAGE_TYPE:
- case DHCP_OPTION_PARAMETER_REQUEST_LIST:
- case DHCP_OPTION_END:
- return -EINVAL;
-
- default:
- break;
- }
-
- for (i = 0; i < client->req_opts_size; i++)
- if (client->req_opts[i] == option)
- return -EEXIST;
-
- if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
- client->req_opts_size + 1))
- return -ENOMEM;
-
- client->req_opts[client->req_opts_size++] = option;
-
- return 0;
-}
-
-int sd_dhcp_client_set_request_address(sd_dhcp_client *client,
- const struct in_addr *last_addr) {
- assert_return(client, -EINVAL);
- assert_return(client->state == DHCP_STATE_INIT, -EBUSY);
-
- if (last_addr)
- client->last_addr = last_addr->s_addr;
- else
- client->last_addr = INADDR_ANY;
-
- return 0;
-}
-
-int sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index) {
- assert_return(client, -EINVAL);
- assert_return(client->state == DHCP_STATE_INIT, -EBUSY);
- assert_return(interface_index >= -1, -EINVAL);
-
- client->index = interface_index;
-
- return 0;
-}
-
-int sd_dhcp_client_set_mac(sd_dhcp_client *client,
- const struct ether_addr *addr) {
- assert_return(client, -EINVAL);
- assert_return(client->state == DHCP_STATE_INIT, -EBUSY);
-
- memcpy(&client->mac_addr, addr, ETH_ALEN);
-
- return 0;
-}
-
-int sd_dhcp_client_get_address(sd_dhcp_client *client, struct in_addr *addr) {
- assert_return(client, -EINVAL);
- assert_return(addr, -EINVAL);
-
- client_state_machine_check (client->state, -EADDRNOTAVAIL);
-
- addr->s_addr = client->lease->address;
-
- return 0;
-}
-
-int sd_dhcp_client_get_mtu(sd_dhcp_client *client, uint16_t *mtu) {
- assert_return(client, -EINVAL);
- assert_return(mtu, -EINVAL);
-
- client_state_machine_check (client->state, -EADDRNOTAVAIL);
-
- if (client->lease->mtu)
- *mtu = client->lease->mtu;
- else
- return -ENOENT;
-
- return 0;
-}
-
-int sd_dhcp_client_get_dns(sd_dhcp_client *client, struct in_addr **addr, size_t *addr_size) {
- assert_return(client, -EINVAL);
- assert_return(addr, -EINVAL);
- assert_return(addr_size, -EINVAL);
-
- client_state_machine_check (client->state, -EADDRNOTAVAIL);
-
- if (client->lease->dns_size) {
- *addr_size = client->lease->dns_size;
- *addr = client->lease->dns;
- } else
- return -ENOENT;
-
- return 0;
-}
-
-int sd_dhcp_client_get_domainname(sd_dhcp_client *client, const char **domainname) {
- assert_return(client, -EINVAL);
- assert_return(domainname, -EINVAL);
-
- client_state_machine_check (client->state, -EADDRNOTAVAIL);
-
- if (client->lease->domainname)
- *domainname = client->lease->domainname;
- else
- return -ENOENT;
-
- return 0;
-}
-
-int sd_dhcp_client_get_hostname(sd_dhcp_client *client, const char **hostname) {
- assert_return(client, -EINVAL);
- assert_return(hostname, -EINVAL);
-
- client_state_machine_check (client->state, -EADDRNOTAVAIL);
-
- if (client->lease->hostname)
- *hostname = client->lease->hostname;
- else
- return -ENOENT;
-
- return 0;
-}
-
-int sd_dhcp_client_prefixlen(const struct in_addr *addr) {
- int len = 0;
- uint32_t mask;
-
- assert_return(addr, -EADDRNOTAVAIL);
-
- mask = be32toh(addr->s_addr);
- while (mask) {
- len++;
- mask = mask << 1;
- }
-
- return len;
-}
-
-int sd_dhcp_client_get_router(sd_dhcp_client *client, struct in_addr *addr) {
- assert_return(client, -EINVAL);
- assert_return(addr, -EINVAL);
-
- client_state_machine_check (client->state, -EADDRNOTAVAIL);
-
- addr->s_addr = client->lease->router;
-
- return 0;
-}
-
-int sd_dhcp_client_get_netmask(sd_dhcp_client *client, struct in_addr *addr) {
- assert_return(client, -EINVAL);
- assert_return(addr, -EINVAL);
-
- client_state_machine_check (client->state, -EADDRNOTAVAIL);
-
- addr->s_addr = client->lease->subnet_mask;
-
- return 0;
-}
-
-static int client_notify(sd_dhcp_client *client, int event) {
- if (client->cb)
- client->cb(client, event, client->userdata);
-
- return 0;
-}
-
-static void lease_free(DHCPLease *lease) {
- if (!lease)
- return;
-
- free(lease->hostname);
- free(lease->domainname);
- free(lease->dns);
- free(lease);
-}
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPLease*, lease_free);
-#define _cleanup_lease_free_ _cleanup_(lease_freep)
-
-static int client_stop(sd_dhcp_client *client, int error) {
- assert_return(client, -EINVAL);
-
- client->receive_message =
- sd_event_source_unref(client->receive_message);
-
- if (client->fd >= 0)
- close(client->fd);
- client->fd = -1;
-
- client->timeout_resend = sd_event_source_unref(client->timeout_resend);
-
- client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
- client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
- client->timeout_expire = sd_event_source_unref(client->timeout_expire);
-
- client->attempt = 1;
-
- client_notify(client, error);
-
- switch (client->state) {
-
- case DHCP_STATE_INIT:
- case DHCP_STATE_SELECTING:
- case DHCP_STATE_REQUESTING:
- case DHCP_STATE_BOUND:
-
- client->start_time = 0;
- client->state = DHCP_STATE_INIT;
- break;
-
- case DHCP_STATE_INIT_REBOOT:
- case DHCP_STATE_REBOOTING:
- case DHCP_STATE_RENEWING:
- case DHCP_STATE_REBINDING:
-
- break;
- }
-
- if (client->lease) {
- lease_free(client->lease);
- client->lease = NULL;
- }
-
- return 0;
-}
-
-static int client_packet_init(sd_dhcp_client *client, uint8_t type,
- DHCPMessage *message, uint16_t secs,
- uint8_t **opt, size_t *optlen) {
- int err;
- be16_t max_size;
-
- *opt = (uint8_t *)(message + 1);
-
- if (*optlen < 4)
- return -ENOBUFS;
- *optlen -= 4;
-
- message->op = BOOTREQUEST;
- message->htype = 1;
- message->hlen = ETHER_ADDR_LEN;
- message->xid = htobe32(client->xid);
-
- /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers
- refuse to issue an DHCP lease if 'secs' is set to zero */
- message->secs = htobe16(secs);
-
- if (client->state == DHCP_STATE_RENEWING ||
- client->state == DHCP_STATE_REBINDING)
- message->ciaddr = client->lease->address;
-
- memcpy(&message->chaddr, &client->mac_addr, ETH_ALEN);
- (*opt)[0] = 0x63;
- (*opt)[1] = 0x82;
- (*opt)[2] = 0x53;
- (*opt)[3] = 0x63;
-
- *opt += 4;
-
- err = dhcp_option_append(opt, optlen, DHCP_OPTION_MESSAGE_TYPE, 1,
- &type);
- if (err < 0)
- return err;
-
- /* Some DHCP servers will refuse to issue an DHCP lease if the Cliient
- Identifier option is not set */
- err = dhcp_option_append(opt, optlen, DHCP_OPTION_CLIENT_IDENTIFIER,
- ETH_ALEN, &client->mac_addr);
- if (err < 0)
- return err;
-
- if (type == DHCP_DISCOVER || type == DHCP_REQUEST) {
- err = dhcp_option_append(opt, optlen,
- DHCP_OPTION_PARAMETER_REQUEST_LIST,
- client->req_opts_size,
- client->req_opts);
- if (err < 0)
- return err;
-
- /* Some DHCP servers will send bigger DHCP packets than the
- defined default size unless the Maximum Messge Size option
- is explicitely set */
- max_size = htobe16(DHCP_IP_UDP_SIZE + DHCP_MESSAGE_SIZE +
- DHCP_CLIENT_MIN_OPTIONS_SIZE);
- err = dhcp_option_append(opt, optlen,
- DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
- 2, &max_size);
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
-static uint16_t client_checksum(void *buf, int len) {
- uint32_t sum;
- uint16_t *check;
- int i;
- uint8_t *odd;
-
- sum = 0;
- check = buf;
-
- for (i = 0; i < len / 2 ; i++)
- sum += check[i];
-
- if (len & 0x01) {
- odd = buf;
- sum += odd[len - 1];
- }
-
- while (sum >> 16)
- sum = (sum & 0xffff) + (sum >> 16);
-
- return ~sum;
-}
-
-static void client_append_ip_headers(DHCPPacket *packet, uint16_t len) {
- packet->ip.version = IPVERSION;
- packet->ip.ihl = DHCP_IP_SIZE / 4;
- packet->ip.tot_len = htobe16(len);
-
- packet->ip.protocol = IPPROTO_UDP;
- packet->ip.saddr = INADDR_ANY;
- packet->ip.daddr = INADDR_BROADCAST;
-
- packet->udp.source = htobe16(DHCP_PORT_CLIENT);
- packet->udp.dest = htobe16(DHCP_PORT_SERVER);
- packet->udp.len = htobe16(len - DHCP_IP_SIZE);
-
- packet->ip.check = packet->udp.len;
- packet->udp.check = client_checksum(&packet->ip.ttl, len - 8);
-
- packet->ip.ttl = IPDEFTTL;
- packet->ip.check = 0;
- packet->ip.check = client_checksum(&packet->ip, DHCP_IP_SIZE);
-}
-
-static int client_send_discover(sd_dhcp_client *client, uint16_t secs) {
- int err = 0;
- _cleanup_free_ DHCPPacket *discover;
- size_t optlen, len;
- uint8_t *opt;
-
- optlen = DHCP_CLIENT_MIN_OPTIONS_SIZE;
- len = sizeof(DHCPPacket) + optlen;
-
- discover = malloc0(len);
-
- if (!discover)
- return -ENOMEM;
-
- err = client_packet_init(client, DHCP_DISCOVER, &discover->dhcp,
- secs, &opt, &optlen);
- if (err < 0)
- return err;
-
- if (client->last_addr != INADDR_ANY) {
- err = dhcp_option_append(&opt, &optlen,
- DHCP_OPTION_REQUESTED_IP_ADDRESS,
- 4, &client->last_addr);
- if (err < 0)
- return err;
- }
-
- err = dhcp_option_append(&opt, &optlen, DHCP_OPTION_END, 0, NULL);
- if (err < 0)
- return err;
-
- client_append_ip_headers(discover, len);
-
- err = dhcp_network_send_raw_socket(client->fd, &client->link,
- discover, len);
-
- return err;
-}
-
-static int client_send_request(sd_dhcp_client *client, uint16_t secs) {
- _cleanup_free_ DHCPPacket *request;
- size_t optlen, len;
- int err;
- uint8_t *opt;
-
- optlen = DHCP_CLIENT_MIN_OPTIONS_SIZE;
- len = DHCP_MESSAGE_SIZE + optlen;
-
- request = malloc0(len);
- if (!request)
- return -ENOMEM;
-
- err = client_packet_init(client, DHCP_REQUEST, &request->dhcp, secs,
- &opt, &optlen);
- if (err < 0)
- return err;
-
- if (client->state == DHCP_STATE_REQUESTING) {
- err = dhcp_option_append(&opt, &optlen,
- DHCP_OPTION_REQUESTED_IP_ADDRESS,
- 4, &client->lease->address);
- if (err < 0)
- return err;
-
- err = dhcp_option_append(&opt, &optlen,
- DHCP_OPTION_SERVER_IDENTIFIER,
- 4, &client->lease->server_address);
- if (err < 0)
- return err;
- }
-
- err = dhcp_option_append(&opt, &optlen, DHCP_OPTION_END, 0, NULL);
- if (err < 0)
- return err;
-
- if (client->state == DHCP_STATE_RENEWING) {
- err = dhcp_network_send_udp_socket(client->fd,
- client->lease->server_address,
- &request->dhcp,
- len - DHCP_IP_UDP_SIZE);
- } else {
- client_append_ip_headers(request, len);
-
- err = dhcp_network_send_raw_socket(client->fd, &client->link,
- request, len);
- }
-
- return err;
-}
-
-static int client_timeout_resend(sd_event_source *s, uint64_t usec,
- void *userdata) {
- sd_dhcp_client *client = userdata;
- usec_t next_timeout = 0;
- uint32_t time_left;
- uint16_t secs;
- int r = 0;
-
- assert(s);
- assert(client);
- assert(client->event);
-
- switch (client->state) {
- case DHCP_STATE_RENEWING:
-
- time_left = (client->lease->t2 - client->lease->t1)/2;
- if (time_left < 60)
- time_left = 60;
-
- next_timeout = usec + time_left * USEC_PER_SEC;
-
- break;
-
- case DHCP_STATE_REBINDING:
-
- time_left = (client->lease->lifetime - client->lease->t2)/2;
- if (time_left < 60)
- time_left = 60;
-
- next_timeout = usec + time_left * USEC_PER_SEC;
- break;
-
- case DHCP_STATE_INIT:
- case DHCP_STATE_INIT_REBOOT:
- case DHCP_STATE_REBOOTING:
- case DHCP_STATE_SELECTING:
- case DHCP_STATE_REQUESTING:
- case DHCP_STATE_BOUND:
-
- if (client->attempt < 64)
- client->attempt *= 2;
-
- next_timeout = usec + (client->attempt - 1) * USEC_PER_SEC;
-
- break;
- }
-
- next_timeout += (random_u32() & 0x1fffff);
-
- r = sd_event_add_monotonic(client->event, next_timeout,
- 10 * USEC_PER_MSEC,
- client_timeout_resend, client,
- &client->timeout_resend);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_priority(client->timeout_resend, client->event_priority);
- if (r < 0)
- goto error;
-
- secs = (usec - client->start_time) / USEC_PER_SEC;
-
- switch (client->state) {
- case DHCP_STATE_INIT:
- r = client_send_discover(client, secs);
- if (r >= 0) {
- client->state = DHCP_STATE_SELECTING;
- client->attempt = 1;
- } else {
- if (client->attempt >= 64)
- goto error;
- }
-
- break;
-
- case DHCP_STATE_SELECTING:
- r = client_send_discover(client, secs);
- if (r < 0 && client->attempt >= 64)
- goto error;
-
- break;
-
- case DHCP_STATE_REQUESTING:
- case DHCP_STATE_RENEWING:
- case DHCP_STATE_REBINDING:
- r = client_send_request(client, secs);
- if (r < 0 && client->attempt >= 64)
- goto error;
-
- client->request_sent = usec;
-
- break;
-
- case DHCP_STATE_INIT_REBOOT:
- case DHCP_STATE_REBOOTING:
- case DHCP_STATE_BOUND:
-
- break;
- }
-
- return 0;
-
-error:
- client_stop(client, r);
-
- /* Errors were dealt with when stopping the client, don't spill
- errors into the event loop handler */
- return 0;
-}
-
-static int client_initialize_events(sd_dhcp_client *client, usec_t usec) {
- int r;
-
- assert(client);
- assert(client->event);
-
- r = sd_event_add_io(client->event, client->fd, EPOLLIN,
- client_receive_message, client,
- &client->receive_message);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_priority(client->receive_message, client->event_priority);
- if (r < 0)
- goto error;
-
- r = sd_event_add_monotonic(client->event, usec, 0,
- client_timeout_resend, client,
- &client->timeout_resend);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_priority(client->timeout_resend, client->event_priority);
-
-error:
- if (r < 0)
- client_stop(client, r);
-
- return 0;
-
-}
-
-static int client_timeout_expire(sd_event_source *s, uint64_t usec,
- void *userdata) {
- sd_dhcp_client *client = userdata;
-
- client_stop(client, DHCP_EVENT_EXPIRED);
-
- return 0;
-}
-
-static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
- sd_dhcp_client *client = userdata;
- int r;
-
- if (client->fd >= 0) {
- client->receive_message =
- sd_event_source_unref(client->receive_message);
- close(client->fd);
- client->fd = -1;
- }
-
- client->state = DHCP_STATE_REBINDING;
- client->attempt = 1;
-
- r = dhcp_network_bind_raw_socket(client->index, &client->link);
- if (r < 0) {
- client_stop(client, r);
- return 0;
- }
-
- client->fd = r;
-
- return client_initialize_events(client, usec);
-}
-
-static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) {
- sd_dhcp_client *client = userdata;
- int r;
-
- client->state = DHCP_STATE_RENEWING;
- client->attempt = 1;
-
- r = dhcp_network_bind_udp_socket(client->index,
- client->lease->address);
- if (r < 0) {
- client_stop(client, r);
- return 0;
- }
-
- client->fd = r;
-
- return client_initialize_events(client, usec);
-}
-
-static int client_parse_offer(uint8_t code, uint8_t len, const uint8_t *option,
- void *user_data) {
- DHCPLease *lease = user_data;
- be32_t val;
-
- switch(code) {
-
- case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
- if (len == 4) {
- memcpy(&val, option, 4);
- lease->lifetime = be32toh(val);
- }
-
- break;
-
- case DHCP_OPTION_SERVER_IDENTIFIER:
- if (len >= 4)
- memcpy(&lease->server_address, option, 4);
-
- break;
-
- case DHCP_OPTION_SUBNET_MASK:
- if (len >= 4)
- memcpy(&lease->subnet_mask, option, 4);
-
- break;
-
- case DHCP_OPTION_ROUTER:
- if (len >= 4)
- memcpy(&lease->router, option, 4);
-
- break;
-
- case DHCP_OPTION_DOMAIN_NAME_SERVER:
- if (len >= 4) {
- unsigned i;
-
- lease->dns_size = len / 4;
-
- free(lease->dns);
- lease->dns = new0(struct in_addr, lease->dns_size);
- if (!lease->dns)
- return -ENOMEM;
-
- for (i = 0; i < lease->dns_size; i++) {
- memcpy(&lease->dns[i].s_addr, option + 4 * i, 4);
- }
- }
-
- break;
-
- case DHCP_OPTION_INTERFACE_MTU:
- if (len >= 2) {
- be16_t mtu;
-
- memcpy(&mtu, option, 2);
- lease->mtu = be16toh(mtu);
-
- if (lease->mtu < 68)
- lease->mtu = 0;
- }
-
- break;
-
- case DHCP_OPTION_DOMAIN_NAME:
- if (len >= 1) {
- free(lease->domainname);
- lease->domainname = strndup((const char *)option, len);
- }
-
- break;
-
- case DHCP_OPTION_HOST_NAME:
- if (len >= 1) {
- free(lease->hostname);
- lease->hostname = strndup((const char *)option, len);
- }
-
- break;
-
- case DHCP_OPTION_RENEWAL_T1_TIME:
- if (len == 4) {
- memcpy(&val, option, 4);
- lease->t1 = be32toh(val);
- }
-
- break;
-
- case DHCP_OPTION_REBINDING_T2_TIME:
- if (len == 4) {
- memcpy(&val, option, 4);
- lease->t2 = be32toh(val);
- }
-
- break;
- }
-
- return 0;
-}
-
-static int client_verify_headers(sd_dhcp_client *client, DHCPPacket *message,
- size_t len) {
- size_t hdrlen;
-
- if (len < (DHCP_IP_UDP_SIZE + DHCP_MESSAGE_SIZE))
- return -EINVAL;
-
- hdrlen = message->ip.ihl * 4;
- if (hdrlen < 20 || hdrlen > len || client_checksum(&message->ip,
- hdrlen))
- return -EINVAL;
-
- message->ip.check = message->udp.len;
- message->ip.ttl = 0;
-
- if (hdrlen + be16toh(message->udp.len) > len ||
- client_checksum(&message->ip.ttl, be16toh(message->udp.len) + 12))
- return -EINVAL;
-
- if (be16toh(message->udp.source) != DHCP_PORT_SERVER ||
- be16toh(message->udp.dest) != DHCP_PORT_CLIENT)
- return -EINVAL;
-
- if (message->dhcp.op != BOOTREPLY)
- return -EINVAL;
-
- if (be32toh(message->dhcp.xid) != client->xid)
- return -EINVAL;
-
- if (memcmp(&message->dhcp.chaddr[0], &client->mac_addr.ether_addr_octet,
- ETHER_ADDR_LEN))
- return -EINVAL;
-
- return 0;
-}
-
-static int client_receive_offer(sd_dhcp_client *client, DHCPPacket *offer,
- size_t len) {
- _cleanup_lease_free_ DHCPLease *lease = NULL;
- int r;
-
- r = client_verify_headers(client, offer, len);
- if (r < 0)
- return r;
-
- lease = new0(DHCPLease, 1);
- if (!lease)
- return -ENOMEM;
-
- len = len - DHCP_IP_UDP_SIZE;
- r = dhcp_option_parse(&offer->dhcp, len, client_parse_offer,
- lease);
- if (r != DHCP_OFFER)
- return -ENOMSG;
-
- lease->address = offer->dhcp.yiaddr;
-
- if (lease->address == INADDR_ANY ||
- lease->server_address == INADDR_ANY ||
- lease->subnet_mask == INADDR_ANY ||
- lease->lifetime == 0)
- return -ENOMSG;
-
- client->lease = lease;
- lease = NULL;
-
- return 0;
-}
-
-static int client_receive_ack(sd_dhcp_client *client, const uint8_t *buf,
- size_t len) {
- DHCPPacket *ack;
- DHCPMessage *dhcp;
- _cleanup_lease_free_ DHCPLease *lease = NULL;
- int r;
-
- if (client->state == DHCP_STATE_RENEWING) {
- dhcp = (DHCPMessage *)buf;
- } else {
- ack = (DHCPPacket *)buf;
-
- r = client_verify_headers(client, ack, len);
- if (r < 0)
- return r;
-
- dhcp = &ack->dhcp;
- len -= DHCP_IP_UDP_SIZE;
- }
-
- lease = new0(DHCPLease, 1);
- if (!lease)
- return -ENOMEM;
-
- r = dhcp_option_parse(dhcp, len, client_parse_offer, lease);
- if (r == DHCP_NAK)
- return DHCP_EVENT_NO_LEASE;
-
- if (r != DHCP_ACK)
- return -ENOMSG;
-
- lease->address = dhcp->yiaddr;
-
- if (lease->address == INADDR_ANY ||
- lease->server_address == INADDR_ANY ||
- lease->subnet_mask == INADDR_ANY || lease->lifetime == 0)
- return -ENOMSG;
-
- r = DHCP_EVENT_IP_ACQUIRE;
- if (client->lease) {
- if (client->lease->address != lease->address ||
- client->lease->subnet_mask != lease->subnet_mask ||
- client->lease->router != lease->router) {
- r = DHCP_EVENT_IP_CHANGE;
- }
-
- lease_free(client->lease);
- }
-
- client->lease = lease;
- lease = NULL;
-
- return r;
-}
-
-static uint64_t client_compute_timeout(uint64_t request_sent,
- uint32_t lifetime) {
- return request_sent + (lifetime - 3) * USEC_PER_SEC +
- + (random_u32() & 0x1fffff);
-}
-
-static int client_set_lease_timeouts(sd_dhcp_client *client, uint64_t usec) {
- uint64_t next_timeout;
- int r;
-
- assert(client);
- assert(client->event);
-
- if (client->lease->lifetime < 10)
- return -EINVAL;
-
- client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
- client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
- client->timeout_expire = sd_event_source_unref(client->timeout_expire);
-
- if (!client->lease->t1)
- client->lease->t1 = client->lease->lifetime / 2;
-
- next_timeout = client_compute_timeout(client->request_sent,
- client->lease->t1);
- if (next_timeout < usec)
- return -EINVAL;
-
- r = sd_event_add_monotonic(client->event, next_timeout,
- 10 * USEC_PER_MSEC,
- client_timeout_t1, client,
- &client->timeout_t1);
- if (r < 0)
- return r;
-
- r = sd_event_source_set_priority(client->timeout_t1, client->event_priority);
- if (r < 0)
- return r;
-
- if (!client->lease->t2)
- client->lease->t2 = client->lease->lifetime * 7 / 8;
-
- if (client->lease->t2 < client->lease->t1)
- return -EINVAL;
-
- if (client->lease->lifetime < client->lease->t2)
- return -EINVAL;
-
- next_timeout = client_compute_timeout(client->request_sent,
- client->lease->t2);
- if (next_timeout < usec)
- return -EINVAL;
-
- r = sd_event_add_monotonic(client->event, next_timeout,
- 10 * USEC_PER_MSEC,
- client_timeout_t2, client,
- &client->timeout_t2);
- if (r < 0)
- return r;
-
- r = sd_event_source_set_priority(client->timeout_t2, client->event_priority);
- if (r < 0)
- return r;
-
- next_timeout = client_compute_timeout(client->request_sent,
- client->lease->lifetime);
- if (next_timeout < usec)
- return -EINVAL;
-
- r = sd_event_add_monotonic(client->event, next_timeout,
- 10 * USEC_PER_MSEC,
- client_timeout_expire, client,
- &client->timeout_expire);
- if (r < 0)
- return r;
-
- r = sd_event_source_set_priority(client->timeout_expire, client->event_priority);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-static int client_receive_message(sd_event_source *s, int fd,
- uint32_t revents, void *userdata) {
- sd_dhcp_client *client = userdata;
- uint8_t buf[sizeof(DHCPPacket) + DHCP_CLIENT_MIN_OPTIONS_SIZE];
- int buflen = sizeof(buf);
- int len, r = 0, notify_event = 0;
- DHCPPacket *message;
- usec_t time_now;
-
- assert(s);
- assert(client);
- assert(client->event);
-
- len = read(fd, &buf, buflen);
- if (len < 0)
- return 0;
-
- r = sd_event_get_now_monotonic(client->event, &time_now);
- if (r < 0)
- goto error;
-
- switch (client->state) {
- case DHCP_STATE_SELECTING:
-
- message = (DHCPPacket *)&buf;
-
- if (client_receive_offer(client, message, len) >= 0) {
-
- client->timeout_resend =
- sd_event_source_unref(client->timeout_resend);
-
- client->state = DHCP_STATE_REQUESTING;
- client->attempt = 1;
-
- r = sd_event_add_monotonic(client->event, time_now, 0,
- client_timeout_resend,
- client,
- &client->timeout_resend);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_priority(client->timeout_resend, client->event_priority);
- if (r < 0)
- goto error;
- }
-
- break;
-
- case DHCP_STATE_REQUESTING:
- case DHCP_STATE_RENEWING:
- case DHCP_STATE_REBINDING:
-
- r = client_receive_ack(client, buf, len);
-
- if (r == DHCP_EVENT_NO_LEASE)
- goto error;
-
- if (r >= 0) {
- client->timeout_resend =
- sd_event_source_unref(client->timeout_resend);
-
- if (client->state == DHCP_STATE_REQUESTING)
- notify_event = DHCP_EVENT_IP_ACQUIRE;
- else if (r != DHCP_EVENT_IP_ACQUIRE)
- notify_event = r;
-
- client->state = DHCP_STATE_BOUND;
- client->attempt = 1;
-
- client->last_addr = client->lease->address;
-
- r = client_set_lease_timeouts(client, time_now);
- if (r < 0)
- goto error;
-
- if (notify_event)
- client_notify(client, notify_event);
-
- client->receive_message =
- sd_event_source_unref(client->receive_message);
- close(client->fd);
- client->fd = -1;
- }
-
- r = 0;
-
- break;
-
- case DHCP_STATE_INIT:
- case DHCP_STATE_INIT_REBOOT:
- case DHCP_STATE_REBOOTING:
- case DHCP_STATE_BOUND:
-
- break;
- }
-
-error:
- if (r < 0 || r == DHCP_EVENT_NO_LEASE)
- return client_stop(client, r);
-
- return 0;
-}
-
-int sd_dhcp_client_start(sd_dhcp_client *client) {
- int r;
-
- assert_return(client, -EINVAL);
- assert_return(client->event, -EINVAL);
- assert_return(client->index > 0, -EINVAL);
- assert_return(client->state == DHCP_STATE_INIT ||
- client->state == DHCP_STATE_INIT_REBOOT, -EBUSY);
-
- client->xid = random_u32();
-
- r = dhcp_network_bind_raw_socket(client->index, &client->link);
-
- if (r < 0) {
- client_stop(client, r);
- return r;
- }
-
- client->fd = r;
- client->start_time = now(CLOCK_MONOTONIC);
-
- return client_initialize_events(client, client->start_time);
-}
-
-int sd_dhcp_client_stop(sd_dhcp_client *client) {
- return client_stop(client, DHCP_EVENT_STOP);
-}
-
-int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event, int priority) {
- int r;
-
- assert_return(client, -EINVAL);
- assert_return(!client->event, -EBUSY);
-
- if (event)
- client->event = sd_event_ref(event);
- else {
- r = sd_event_default(&client->event);
- if (r < 0)
- return 0;
- }
-
- client->event_priority = priority;
-
- return 0;
-}
-
-int sd_dhcp_client_detach_event(sd_dhcp_client *client) {
- assert_return(client, -EINVAL);
-
- client->event = sd_event_unref(client->event);
-
- return 0;
-}
-
-sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client) {
- if (!client)
- return NULL;
-
- return client->event;
-}
-
-void sd_dhcp_client_free(sd_dhcp_client *client) {
- if (!client)
- return;
-
- sd_dhcp_client_stop(client);
- sd_dhcp_client_detach_event(client);
-
- free(client->req_opts);
- free(client);
-}
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_client*, sd_dhcp_client_free);
-#define _cleanup_dhcp_client_free_ _cleanup_(sd_dhcp_client_freep)
-
-int sd_dhcp_client_new(sd_dhcp_client **ret) {
- _cleanup_dhcp_client_free_ sd_dhcp_client *client = NULL;
-
- assert_return(ret, -EINVAL);
-
- client = new0(sd_dhcp_client, 1);
- if (!client)
- return -ENOMEM;
-
- client->state = DHCP_STATE_INIT;
- client->index = -1;
- client->fd = -1;
- client->attempt = 1;
-
- client->req_opts_size = ELEMENTSOF(default_req_opts);
-
- client->req_opts = memdup(default_req_opts, client->req_opts_size);
- if (!client->req_opts)
- return -ENOMEM;
-
- *ret = client;
- client = NULL;
-
- return 0;
-}
diff --git a/src/libsystemd/test-dhcp-client.c b/src/libsystemd/test-dhcp-client.c
deleted file mode 100644
index 56a10b3..0000000
--- a/src/libsystemd/test-dhcp-client.c
+++ /dev/null
@@ -1,243 +0,0 @@
-/*-*- 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 <stdlib.h>
-#include <assert.h>
-#include <errno.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <unistd.h>
-
-#include "util.h"
-#include "socket-util.h"
-
-#include "dhcp-protocol.h"
-#include "dhcp-internal.h"
-#include "sd-dhcp-client.h"
-
-static struct ether_addr mac_addr = {
- .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
-};
-
-static int test_fd[2];
-
-static void test_request_basic(sd_event *e)
-{
- int r;
-
- sd_dhcp_client *client;
-
- r = sd_dhcp_client_new(&client);
-
- assert(r >= 0);
- assert(client);
-
- r = sd_dhcp_client_attach_event(client, e, 0);
- assert(r >= 0);
-
- assert(sd_dhcp_client_set_request_option(NULL, 0) == -EINVAL);
- assert(sd_dhcp_client_set_request_address(NULL, NULL) == -EINVAL);
- assert(sd_dhcp_client_set_index(NULL, 0) == -EINVAL);
-
- assert(sd_dhcp_client_set_index(client, 15) == 0);
- assert(sd_dhcp_client_set_index(client, -42) == -EINVAL);
- assert(sd_dhcp_client_set_index(client, -1) == 0);
-
- assert(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_SUBNET_MASK) == -EEXIST);
- assert(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_ROUTER) == -EEXIST);
- assert(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_HOST_NAME) == -EEXIST);
- assert(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_DOMAIN_NAME) == -EEXIST);
- assert(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_DOMAIN_NAME_SERVER)
- == -EEXIST);
- assert(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_NTP_SERVER) == -EEXIST);
-
- assert(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_PAD) == -EINVAL);
- assert(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_END) == -EINVAL);
- assert(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_MESSAGE_TYPE) == -EINVAL);
- assert(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_OVERLOAD) == -EINVAL);
- assert(sd_dhcp_client_set_request_option(client,
- DHCP_OPTION_PARAMETER_REQUEST_LIST)
- == -EINVAL);
-
- assert(sd_dhcp_client_set_request_option(client, 33) == 0);
- assert(sd_dhcp_client_set_request_option(client, 33) == -EEXIST);
- assert(sd_dhcp_client_set_request_option(client, 44) == 0);
- assert(sd_dhcp_client_set_request_option(client, 33) == -EEXIST);
-}
-
-static uint16_t client_checksum(void *buf, int len)
-{
- uint32_t sum;
- uint16_t *check;
- int i;
- uint8_t *odd;
-
- sum = 0;
- check = buf;
-
- for (i = 0; i < len / 2 ; i++)
- sum += check[i];
-
- if (len & 0x01) {
- odd = buf;
- sum += odd[len - 1];
- }
-
- while (sum >> 16)
- sum = (sum & 0xffff) + (sum >> 16);
-
- return ~sum;
-}
-
-static void test_checksum(void)
-{
- uint8_t buf[20] = {
- 0x45, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00,
- 0x40, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0xff, 0xff, 0xff, 0xff
- };
-
- assert(client_checksum(&buf, 20) == be16toh(0x78ae));
-}
-
-static int check_options(uint8_t code, uint8_t len, const uint8_t *option,
- void *user_data)
-{
- return 0;
-}
-
-int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
- const void *packet, size_t len)
-{
- size_t size;
- _cleanup_free_ DHCPPacket *discover;
- uint16_t ip_check, udp_check;
- int res;
-
- assert(s >= 0);
- assert(packet);
-
- size = sizeof(DHCPPacket) + 4;
- assert(len > size);
-
- discover = memdup(packet, len);
-
- assert(memcmp(discover->dhcp.chaddr,
- &mac_addr.ether_addr_octet, 6) == 0);
- assert(discover->ip.ttl == IPDEFTTL);
- assert(discover->ip.protocol == IPPROTO_UDP);
- assert(discover->ip.saddr == INADDR_ANY);
- assert(discover->ip.daddr == INADDR_BROADCAST);
- assert(discover->udp.source == be16toh(DHCP_PORT_CLIENT));
- assert(discover->udp.dest == be16toh(DHCP_PORT_SERVER));
-
- ip_check = discover->ip.check;
-
- discover->ip.ttl = 0;
- discover->ip.check = discover->udp.len;
-
- udp_check = ~client_checksum(&discover->ip.ttl, len - 8);
- assert(udp_check == 0xffff);
-
- discover->ip.ttl = IPDEFTTL;
- discover->ip.check = ip_check;
-
- ip_check = ~client_checksum(&discover->ip, sizeof(discover->ip));
- assert(ip_check == 0xffff);
-
- size = len - sizeof(struct iphdr) - sizeof(struct udphdr);
-
- res = dhcp_option_parse(&discover->dhcp, size, check_options, NULL);
- if (res < 0)
- return res;
-
- return 575;
-}
-
-int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link)
-{
- if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_fd) < 0)
- return -errno;
-
- return test_fd[0];
-}
-
-int dhcp_network_bind_udp_socket(int index, be32_t client_address)
-{
- return 0;
-}
-
-int dhcp_network_send_udp_socket(int s, be32_t server_address,
- const void *packet, size_t len)
-{
- return 0;
-}
-
-static void test_discover_message(sd_event *e)
-{
- sd_dhcp_client *client;
- int res, r;
-
- r = sd_dhcp_client_new(&client);
- assert(r >= 0);
- assert(client);
-
- r = sd_dhcp_client_attach_event(client, e, 0);
- assert(r >= 0);
-
- assert(sd_dhcp_client_set_index(client, 42) >= 0);
- assert(sd_dhcp_client_set_mac(client, &mac_addr) >= 0);
-
- assert(sd_dhcp_client_set_request_option(client, 248) >= 0);
-
- res = sd_dhcp_client_start(client);
-
- assert(res == 0 || res == -EINPROGRESS);
-
- close(test_fd[0]);
- close(test_fd[1]);
-}
-
-int main(int argc, char *argv[])
-{
- sd_event *e;
-
- assert(sd_event_new(&e) >= 0);
-
- test_request_basic(e);
- test_checksum();
-
- test_discover_message(e);
- sd_event_run(e, (uint64_t) -1);
-
- return 0;
-}
diff --git a/src/libsystemd/test-dhcp-option.c b/src/libsystemd/test-dhcp-option.c
deleted file mode 100644
index ac0b0a4..0000000
--- a/src/libsystemd/test-dhcp-option.c
+++ /dev/null
@@ -1,378 +0,0 @@
-
-#include <stdio.h>
-#include <stdbool.h>
-#include <errno.h>
-#include <string.h>
-#include <assert.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;
-
- assert(dhcp_option_parse(&message, 0, NULL, NULL) == -EINVAL);
- assert(dhcp_option_parse(&message, sizeof(DHCPMessage), NULL, NULL)
- == -EINVAL);
-}
-
-static void test_cookie(void)
-{
- _cleanup_free_ DHCPMessage *message;
- size_t len = sizeof(DHCPMessage) + 4;
- uint8_t *opt;
-
- message = malloc0(len);
-
- opt = (uint8_t *)(message + 1);
- opt[0] = 0xff;
-
- assert(dhcp_option_parse(message, len, NULL, NULL) == -EINVAL);
-
- opt[0] = 99;
- opt[1] = 130;
- opt[2] = 83;
- 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;
-
- 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);
- 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));
-}
-
-static uint8_t result[64] = {
- 'A', 'B', 'C', 'D',
-};
-
-static uint8_t options[64] = {
- 'A', 'B', 'C', 'D',
- 160, 2, 0x11, 0x12,
- 0,
- 31, 8, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
- 0,
- 55, 3, 0x51, 0x52, 0x53,
- 17, 7, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
- 255
-};
-
-static void test_option_set(void)
-{
- size_t len, oldlen;
- int pos, i;
- uint8_t *opt;
-
- assert(dhcp_option_append(NULL, NULL, 0, 0, NULL) == -EINVAL);
-
- len = 0;
- opt = &result[0];
- assert(dhcp_option_append(&opt, NULL, 0, 0, NULL) == -EINVAL);
- assert(opt == &result[0] && len == 0);
-
- assert(dhcp_option_append(&opt, &len, DHCP_OPTION_PAD,
- 0, NULL) == -ENOBUFS);
- assert(opt == &result[0] && len == 0);
-
- opt = &result[4];
- len = 1;
- assert(dhcp_option_append(&opt, &len, DHCP_OPTION_PAD,
- 0, NULL) >= 0);
- assert(opt == &result[5] && len == 0);
-
- pos = 4;
- len = 60;
- while (pos < 64 && options[pos] != DHCP_OPTION_END) {
- opt = &result[pos];
- oldlen = len;
-
- assert(dhcp_option_append(&opt, &len, options[pos],
- options[pos + 1],
- &options[pos + 2]) >= 0);
-
- if (options[pos] == DHCP_OPTION_PAD) {
- assert(opt == &result[pos + 1]);
- assert(len == oldlen - 1);
- pos++;
- } else {
- assert(opt == &result[pos + 2 + options[pos + 1]]);
- assert(len == oldlen - 2 - options[pos + 1]);
- pos += 2 + options[pos + 1];
- }
- }
-
- for (i = 0; i < pos; i++) {
- if (verbose)
- printf("%2d: 0x%02x(0x%02x)\n", i, result[i],
- options[i]);
- assert(result[i] == options[i]);
- }
-
- if (verbose)
- printf ("\n");
-}
-
-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]);
-
- test_option_set();
-
- return 0;
-}
More information about the systemd-commits
mailing list