[systemd-devel] [PATCH] dhcp: Move src/dhcp to src/libsystemd-dhcp
Patrik Flykt
patrik.flykt at linux.intel.com
Thu Dec 12 00:29:18 PST 2013
---
Makefile.am | 42 +-
src/dhcp/Makefile | 1 -
src/dhcp/client.c | 1002 ---------------------
src/dhcp/internal.h | 41 -
src/dhcp/network.c | 65 --
src/dhcp/option.c | 184 ----
src/dhcp/protocol.h | 119 ---
src/dhcp/test-dhcp-client.c | 224 -----
src/dhcp/test-dhcp-option.c | 377 --------
src/libsystemd-dhcp/Makefile | 1 +
src/libsystemd-dhcp/client.c | 1002 +++++++++++++++++++++
src/libsystemd-dhcp/internal.h | 41 +
src/libsystemd-dhcp/network.c | 65 ++
src/libsystemd-dhcp/option.c | 184 ++++
src/libsystemd-dhcp/protocol.h | 119 +++
src/libsystemd-dhcp/test-libsystemd-dhcp-client.c | 224 +++++
src/libsystemd-dhcp/test-libsystemd-dhcp-option.c | 377 ++++++++
17 files changed, 2034 insertions(+), 2034 deletions(-)
delete mode 120000 src/dhcp/Makefile
delete mode 100644 src/dhcp/client.c
delete mode 100644 src/dhcp/internal.h
delete mode 100644 src/dhcp/network.c
delete mode 100644 src/dhcp/option.c
delete mode 100644 src/dhcp/protocol.h
delete mode 100644 src/dhcp/test-dhcp-client.c
delete mode 100644 src/dhcp/test-dhcp-option.c
create mode 120000 src/libsystemd-dhcp/Makefile
create mode 100644 src/libsystemd-dhcp/client.c
create mode 100644 src/libsystemd-dhcp/internal.h
create mode 100644 src/libsystemd-dhcp/network.c
create mode 100644 src/libsystemd-dhcp/option.c
create mode 100644 src/libsystemd-dhcp/protocol.h
create mode 100644 src/libsystemd-dhcp/test-libsystemd-dhcp-client.c
create mode 100644 src/libsystemd-dhcp/test-libsystemd-dhcp-option.c
diff --git a/Makefile.am b/Makefile.am
index 9b3b48a..bfa3df7 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3849,11 +3849,11 @@ endif
# ------------------------------------------------------------------------------
libsystemd_dhcp_la_SOURCES = \
src/systemd/sd-dhcp-client.h \
- src/dhcp/protocol.h \
- src/dhcp/internal.h \
- src/dhcp/network.c \
- src/dhcp/option.c \
- src/dhcp/client.c
+ src/libsystemd-dhcp/protocol.h \
+ src/libsystemd-dhcp/internal.h \
+ src/libsystemd-dhcp/network.c \
+ src/libsystemd-dhcp/option.c \
+ src/libsystemd-dhcp/client.c
noinst_LTLIBRARIES += \
libsystemd-dhcp.la
@@ -3868,31 +3868,31 @@ libsystemd_dhcp_la_LIBADD = \
libsystemd-shared.la \
libsystemd-bus.la
-test_dhcp_option_SOURCES = \
- src/dhcp/protocol.h \
- src/dhcp/internal.h \
- src/dhcp/option.c \
- src/dhcp/test-dhcp-option.c
+test_libsystemd_dhcp_option_SOURCES = \
+ src/libsystemd-dhcp/protocol.h \
+ src/libsystemd-dhcp/internal.h \
+ src/libsystemd-dhcp/option.c \
+ src/libsystemd-dhcp/test-libsystemd-dhcp-option.c
-test_dhcp_option_LDADD = \
+test_libsystemd_dhcp_option_LDADD = \
libsystemd-shared.la
-test_dhcp_client_SOURCES = \
- src/dhcp/protocol.h \
+test_libsystemd_dhcp_client_SOURCES = \
+ src/libsystemd-dhcp/protocol.h \
src/systemd/sd-dhcp-client.h \
- src/dhcp/internal.h \
- src/dhcp/option.h \
- src/dhcp/option.c \
- src/dhcp/client.c \
- src/dhcp/test-dhcp-client.c
+ src/libsystemd-dhcp/internal.h \
+ src/libsystemd-dhcp/option.h \
+ src/libsystemd-dhcp/option.c \
+ src/libsystemd-dhcp/client.c \
+ src/libsystemd-dhcp/test-libsystemd-dhcp-client.c
-test_dhcp_client_LDADD = \
+test_libsystemd_dhcp_client_LDADD = \
libsystemd-shared.la \
libsystemd-bus.la
tests += \
- test-dhcp-option \
- test-dhcp-client
+ test-libsystemd-dhcp-option \
+ test-libsystemd-dhcp-client
# ------------------------------------------------------------------------------
if ENABLE_MACHINED
diff --git a/src/dhcp/Makefile b/src/dhcp/Makefile
deleted file mode 120000
index d0b0e8e..0000000
--- a/src/dhcp/Makefile
+++ /dev/null
@@ -1 +0,0 @@
-../Makefile
\ No newline at end of file
diff --git a/src/dhcp/client.c b/src/dhcp/client.c
deleted file mode 100644
index 2a479e4..0000000
--- a/src/dhcp/client.c
+++ /dev/null
@@ -1,1002 +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 "util.h"
-#include "list.h"
-
-#include "protocol.h"
-#include "internal.h"
-#include "sd-dhcp-client.h"
-
-#define DHCP_CLIENT_MIN_OPTIONS_SIZE 312
-
-struct DHCPLease {
- uint32_t t1;
- uint32_t t2;
- uint32_t lifetime;
- uint32_t address;
- uint32_t server_address;
- uint32_t subnet_mask;
- uint32_t router;
-};
-
-typedef struct DHCPLease DHCPLease;
-
-struct sd_dhcp_client {
- DHCPState state;
- sd_event *event;
- 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_size;
- uint32_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,
-};
-
-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_size,
- client->req_opts_size + 1))
- return -ENOMEM;
-
- client->req_opts[client->req_opts_size - 1] = 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);
-
- switch (client->state) {
- case DHCP_STATE_INIT:
- case DHCP_STATE_SELECTING:
- case DHCP_STATE_INIT_REBOOT:
- case DHCP_STATE_REBOOTING:
- case DHCP_STATE_REQUESTING:
- return -EADDRNOTAVAIL;
-
- case DHCP_STATE_BOUND:
- case DHCP_STATE_RENEWING:
- case DHCP_STATE_REBINDING:
- addr->s_addr = client->lease->address;
-
- break;
- }
-
- return 0;
-}
-
-int sd_dhcp_client_get_netmask(sd_dhcp_client *client, struct in_addr *addr)
-{
- assert_return(client, -EINVAL);
- assert_return(addr, -EINVAL);
-
- switch (client->state) {
- case DHCP_STATE_INIT:
- case DHCP_STATE_SELECTING:
- case DHCP_STATE_INIT_REBOOT:
- case DHCP_STATE_REBOOTING:
- case DHCP_STATE_REQUESTING:
- return -EADDRNOTAVAIL;
-
- case DHCP_STATE_BOUND:
- case DHCP_STATE_RENEWING:
- case DHCP_STATE_REBINDING:
- addr->s_addr = client->lease->subnet_mask;
-
- break;
- }
-
- 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);
-
- switch (client->state) {
- case DHCP_STATE_INIT:
- case DHCP_STATE_SELECTING:
- case DHCP_STATE_INIT_REBOOT:
- case DHCP_STATE_REBOOTING:
- case DHCP_STATE_REQUESTING:
- return -EADDRNOTAVAIL;
-
- case DHCP_STATE_BOUND:
- case DHCP_STATE_RENEWING:
- case DHCP_STATE_REBINDING:
- addr->s_addr = client->lease->router;
-
- break;
- }
-
- return 0;
-}
-
-static int client_notify(sd_dhcp_client *client, int event)
-{
- if (client->cb)
- client->cb(client, event, client->userdata);
-
- return 0;
-}
-
-static int client_stop(sd_dhcp_client *client, int error)
-{
- assert_return(client, -EINVAL);
- assert_return(client->state != DHCP_STATE_INIT &&
- client->state != DHCP_STATE_INIT_REBOOT, -EALREADY);
-
- 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) {
- 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);
-
- 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];
- }
-
- return ~((sum & 0xffff) + (sum >> 16));
-}
-
-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;
-
- 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;
- uint16_t secs;
- int err = 0;
-
- secs = (usec - client->start_time) / USEC_PER_SEC;
-
- if (client->attempt < 64)
- client->attempt *= 2;
-
- next_timeout = usec + (client->attempt - 1) * USEC_PER_SEC +
- (random_u() & 0x1fffff);
-
- err = sd_event_add_monotonic(client->event, next_timeout,
- 10 * USEC_PER_MSEC,
- client_timeout_resend, client,
- &client->timeout_resend);
- if (err < 0)
- goto error;
-
- switch (client->state) {
- case DHCP_STATE_INIT:
- err = client_send_discover(client, secs);
- if (err >= 0) {
- client->state = DHCP_STATE_SELECTING;
- client->attempt = 1;
- } else {
- if (client->attempt >= 64)
- goto error;
- }
-
- break;
-
- case DHCP_STATE_SELECTING:
- err = client_send_discover(client, secs);
- if (err < 0 && client->attempt >= 64)
- goto error;
-
- break;
-
- case DHCP_STATE_REQUESTING:
- err = client_send_request(client, secs);
- if (err < 0 && client->attempt >= 64)
- goto error;
-
- client->request_sent = usec;
-
- break;
-
- case DHCP_STATE_INIT_REBOOT:
- case DHCP_STATE_REBOOTING:
- case DHCP_STATE_BOUND:
- case DHCP_STATE_RENEWING:
- case DHCP_STATE_REBINDING:
-
- break;
- }
-
- return 0;
-
-error:
- client_stop(client, err);
-
- /* Errors were dealt with when stopping the client, don't spill
- errors into the event loop handler */
- 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)
-{
- return 0;
-}
-
-static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata)
-{
- return 0;
-}
-
-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_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)
-{
- int err;
- DHCPLease *lease;
-
- err = client_verify_headers(client, offer, len);
- if (err < 0)
- return err;
-
- lease = new0(DHCPLease, 1);
- if (!lease)
- return -ENOMEM;
-
- len = len - DHCP_IP_UDP_SIZE;
- if (dhcp_option_parse(&offer->dhcp, len, client_parse_offer,
- lease) != DHCP_OFFER)
- goto error;
-
- lease->address = offer->dhcp.yiaddr;
-
- if (lease->address == INADDR_ANY ||
- lease->server_address == INADDR_ANY ||
- lease->subnet_mask == INADDR_ANY ||
- lease->lifetime == 0)
- goto error;
-
- client->lease = lease;
-
- return 0;
-
-error:
- free(lease);
-
- return -ENOMSG;
-}
-
-static int client_receive_ack(sd_dhcp_client *client, DHCPPacket *offer,
- size_t len)
-{
- int r;
- DHCPLease *lease;
-
- r = client_verify_headers(client, offer, len);
- if (r < 0)
- return r;
-
- lease = new0(DHCPLease, 1);
- if (!lease)
- return -ENOBUFS;
-
- len = len - DHCP_IP_UDP_SIZE;
- r = dhcp_option_parse(&offer->dhcp, len, client_parse_offer, lease);
-
- if (r != DHCP_ACK)
- goto error;
-
- lease->address = offer->dhcp.yiaddr;
-
- if (lease->address == INADDR_ANY ||
- lease->server_address == INADDR_ANY ||
- lease->subnet_mask == INADDR_ANY || lease->lifetime == 0) {
- r = -ENOMSG;
- goto error;
- }
-
- 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;
- }
-
- free(client->lease);
- }
-
- client->lease = lease;
-
- return r;
-
-error:
- free(lease);
-
- return r;
-}
-
-static uint64_t client_compute_timeout(uint64_t request_sent,
- uint32_t lifetime)
-{
- return request_sent + (lifetime - 3) * USEC_PER_SEC +
- + (random_u() & 0x1fffff);
-}
-
-static int client_set_lease_timeouts(sd_dhcp_client *client, uint64_t usec)
-{
- int err;
- uint64_t next_timeout;
-
- if (client->lease->lifetime < 10)
- return -EINVAL;
-
- 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;
-
- err = sd_event_add_monotonic(client->event, next_timeout,
- 10 * USEC_PER_MSEC,
- client_timeout_t1, client,
- &client->timeout_t1);
- if (err < 0)
- return err;
-
- 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;
-
- err = sd_event_add_monotonic(client->event, next_timeout,
- 10 * USEC_PER_MSEC,
- client_timeout_t2, client,
- &client->timeout_t2);
- if (err < 0)
- return err;
-
- next_timeout = client_compute_timeout(client->request_sent,
- client->lease->lifetime);
- if (next_timeout < usec)
- return -EINVAL;
-
- err = sd_event_add_monotonic(client->event, next_timeout,
- 10 * USEC_PER_MSEC,
- client_timeout_expire, client,
- &client->timeout_expire);
- if (err < 0)
- return err;
-
- return 0;
-}
-
-static int client_receive_raw_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;
- DHCPPacket *message;
- usec_t time_now;
-
- 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;
-
- message = (DHCPPacket *)&buf;
-
- switch (client->state) {
- case DHCP_STATE_SELECTING:
-
- 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;
- }
-
- break;
-
- case DHCP_STATE_REQUESTING:
-
- r = client_receive_ack(client, message, len);
- if (r == DHCP_EVENT_NO_LEASE)
- goto error;
-
- if (r >= 0) {
- client->timeout_resend =
- sd_event_source_unref(client->timeout_resend);
-
- 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;
-
- client_notify(client, DHCP_EVENT_IP_ACQUIRE);
-
- close(client->fd);
- client->fd = -1;
- client->receive_message =
- sd_event_source_unref(client->receive_message);
- }
- break;
-
- case DHCP_STATE_INIT:
- case DHCP_STATE_INIT_REBOOT:
- case DHCP_STATE_REBOOTING:
- case DHCP_STATE_BOUND:
- case DHCP_STATE_RENEWING:
- case DHCP_STATE_REBINDING:
-
- break;
- }
-
-error:
- if (r < 0)
- return client_stop(client, r);
-
- return 0;
-}
-
-int sd_dhcp_client_start(sd_dhcp_client *client)
-{
- int err;
-
- assert_return(client, -EINVAL);
- assert_return(client->index >= 0, -EINVAL);
- assert_return(client->state == DHCP_STATE_INIT ||
- client->state == DHCP_STATE_INIT_REBOOT, -EBUSY);
-
- client->xid = random_u();
-
- client->fd = dhcp_network_bind_raw_socket(client->index,
- &client->link);
-
- if (client->fd < 0) {
- err = client->fd;
- goto error;
- }
-
- err = sd_event_add_io(client->event, client->fd, EPOLLIN,
- client_receive_raw_message, client,
- &client->receive_message);
- if (err < 0)
- goto error;
-
- client->start_time = now(CLOCK_MONOTONIC);
- err = sd_event_add_monotonic(client->event, client->start_time, 0,
- client_timeout_resend, client,
- &client->timeout_resend);
- if (err < 0)
- goto error;
-
- return 0;
-
-error:
- client_stop(client, err);
-
- return err;
-}
-
-int sd_dhcp_client_stop(sd_dhcp_client *client)
-{
- return client_stop(client, DHCP_EVENT_STOP);
-}
-
-sd_dhcp_client *sd_dhcp_client_free(sd_dhcp_client *client)
-{
- assert_return(client, NULL);
-
- sd_dhcp_client_stop(client);
-
- sd_event_unref(client->event);
- free(client->req_opts);
- free(client);
-
- return NULL;
-}
-
-sd_dhcp_client *sd_dhcp_client_new(sd_event *event)
-{
- sd_dhcp_client *client;
-
- assert_return(event, NULL);
-
- client = new0(sd_dhcp_client, 1);
- if (!client)
- return NULL;
-
- client->event = sd_event_ref(event);
- 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) {
- free(client);
- return NULL;
- }
-
- return client;
-}
diff --git a/src/dhcp/internal.h b/src/dhcp/internal.h
deleted file mode 100644
index 85bb908..0000000
--- a/src/dhcp/internal.h
+++ /dev/null
@@ -1,41 +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 "protocol.h"
-
-int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link);
-int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
- 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/dhcp/network.c b/src/dhcp/network.c
deleted file mode 100644
index 3a16947..0000000
--- a/src/dhcp/network.c
+++ /dev/null
@@ -1,65 +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 "internal.h"
-
-int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link)
- {
- int s;
-
- 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(s);
- return -errno;
- }
-
- return s;
-}
-
-int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
- const void *packet, size_t len)
-{
- int err = 0;
-
- if (sendto(s, packet, len, 0, &link->sa, sizeof(link->ll)) < 0)
- err = -errno;
-
- return err;
-}
diff --git a/src/dhcp/option.c b/src/dhcp/option.c
deleted file mode 100644
index 235b387..0000000
--- a/src/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 "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/dhcp/protocol.h b/src/dhcp/protocol.h
deleted file mode 100644
index 1e59965..0000000
--- a/src/dhcp/protocol.h
+++ /dev/null
@@ -1,119 +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;
- uint32_t ciaddr;
- uint32_t yiaddr;
- uint32_t siaddr;
- uint32_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_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/dhcp/test-dhcp-client.c b/src/dhcp/test-dhcp-client.c
deleted file mode 100644
index ee0848d..0000000
--- a/src/dhcp/test-dhcp-client.c
+++ /dev/null
@@ -1,224 +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 "protocol.h"
-#include "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)
-{
- sd_dhcp_client *client;
-
- client = sd_dhcp_client_new(e);
-
- assert(client);
-
- 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);
-}
-
-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];
- }
-
- return ~((sum & 0xffff) + (sum >> 16));
-}
-
-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
- };
-
- uint8_t check[2] = {
- 0x78, 0xae
- };
-
- uint16_t *val = (uint16_t *)check;
-
- assert(client_checksum(&buf, 20) == *val);
-}
-
-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];
-}
-
-static void test_discover_message(sd_event *e)
-{
- sd_dhcp_client *client;
- int res;
-
- client = sd_dhcp_client_new(e);
- assert(client);
-
- 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/dhcp/test-dhcp-option.c b/src/dhcp/test-dhcp-option.c
deleted file mode 100644
index b85da79..0000000
--- a/src/dhcp/test-dhcp-option.c
+++ /dev/null
@@ -1,377 +0,0 @@
-
-#include <stdio.h>
-#include <stdbool.h>
-#include <errno.h>
-#include <string.h>
-#include <assert.h>
-
-#include "util.h"
-#include "macro.h"
-
-#include "protocol.h"
-#include "internal.h"
-
-struct option_desc {
- uint8_t sname[64];
- int snamelen;
- uint8_t file[128];
- int filelen;
- uint8_t options[128];
- int len;
- bool success;
- int filepos;
- int snamepos;
- int pos;
-};
-
-static bool verbose = false;
-
-static struct option_desc option_tests[] = {
- { {}, 0, {}, 0, { 42, 5, 65, 66, 67, 68, 69 }, 7, false, },
- { {}, 0, {}, 0, { 42, 5, 65, 66, 67, 68, 69, 0, 0,
- DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 12, true, },
- { {}, 0, {}, 0, { 8, 255, 70, 71, 72 }, 5, false, },
- { {}, 0, {}, 0, { 0x35, 0x01, 0x05, 0x36, 0x04, 0x01, 0x00, 0xa8,
- 0xc0, 0x33, 0x04, 0x00, 0x01, 0x51, 0x80, 0x01,
- 0x04, 0xff, 0xff, 0xff, 0x00, 0x03, 0x04, 0xc0,
- 0xa8, 0x00, 0x01, 0x06, 0x04, 0xc0, 0xa8, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
- 40, true, },
- { {}, 0, {}, 0, { DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_OFFER,
- 42, 3, 0, 0, 0 }, 8, true, },
- { {}, 0, {}, 0, { 42, 2, 1, 2, 44 }, 5, false, },
-
- { {}, 0,
- { 222, 3, 1, 2, 3, DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_NAK }, 8,
- { DHCP_OPTION_OVERLOAD, 1, DHCP_OVERLOAD_FILE }, 3, true, },
-
- { { 1, 4, 1, 2, 3, 4, DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 9,
- { 222, 3, 1, 2, 3 }, 5,
- { DHCP_OPTION_OVERLOAD, 1,
- DHCP_OVERLOAD_FILE|DHCP_OVERLOAD_SNAME }, 3, true, },
-};
-
-static const char *dhcp_type(int type)
-{
- switch(type) {
- case DHCP_DISCOVER:
- return "DHCPDISCOVER";
- case DHCP_OFFER:
- return "DHCPOFFER";
- case DHCP_REQUEST:
- return "DHCPREQUEST";
- case DHCP_DECLINE:
- return "DHCPDECLINE";
- case DHCP_ACK:
- return "DHCPACK";
- case DHCP_NAK:
- return "DHCPNAK";
- case DHCP_RELEASE:
- return "DHCPRELEASE";
- default:
- return "unknown";
- }
-}
-
-static void test_invalid_buffer_length(void)
-{
- DHCPMessage message;
-
- 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 != -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/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/client.c b/src/libsystemd-dhcp/client.c
new file mode 100644
index 0000000..2a479e4
--- /dev/null
+++ b/src/libsystemd-dhcp/client.c
@@ -0,0 +1,1002 @@
+/***
+ 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 "util.h"
+#include "list.h"
+
+#include "protocol.h"
+#include "internal.h"
+#include "sd-dhcp-client.h"
+
+#define DHCP_CLIENT_MIN_OPTIONS_SIZE 312
+
+struct DHCPLease {
+ uint32_t t1;
+ uint32_t t2;
+ uint32_t lifetime;
+ uint32_t address;
+ uint32_t server_address;
+ uint32_t subnet_mask;
+ uint32_t router;
+};
+
+typedef struct DHCPLease DHCPLease;
+
+struct sd_dhcp_client {
+ DHCPState state;
+ sd_event *event;
+ 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_size;
+ uint32_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,
+};
+
+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_size,
+ client->req_opts_size + 1))
+ return -ENOMEM;
+
+ client->req_opts[client->req_opts_size - 1] = 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);
+
+ switch (client->state) {
+ case DHCP_STATE_INIT:
+ case DHCP_STATE_SELECTING:
+ case DHCP_STATE_INIT_REBOOT:
+ case DHCP_STATE_REBOOTING:
+ case DHCP_STATE_REQUESTING:
+ return -EADDRNOTAVAIL;
+
+ case DHCP_STATE_BOUND:
+ case DHCP_STATE_RENEWING:
+ case DHCP_STATE_REBINDING:
+ addr->s_addr = client->lease->address;
+
+ break;
+ }
+
+ return 0;
+}
+
+int sd_dhcp_client_get_netmask(sd_dhcp_client *client, struct in_addr *addr)
+{
+ assert_return(client, -EINVAL);
+ assert_return(addr, -EINVAL);
+
+ switch (client->state) {
+ case DHCP_STATE_INIT:
+ case DHCP_STATE_SELECTING:
+ case DHCP_STATE_INIT_REBOOT:
+ case DHCP_STATE_REBOOTING:
+ case DHCP_STATE_REQUESTING:
+ return -EADDRNOTAVAIL;
+
+ case DHCP_STATE_BOUND:
+ case DHCP_STATE_RENEWING:
+ case DHCP_STATE_REBINDING:
+ addr->s_addr = client->lease->subnet_mask;
+
+ break;
+ }
+
+ 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);
+
+ switch (client->state) {
+ case DHCP_STATE_INIT:
+ case DHCP_STATE_SELECTING:
+ case DHCP_STATE_INIT_REBOOT:
+ case DHCP_STATE_REBOOTING:
+ case DHCP_STATE_REQUESTING:
+ return -EADDRNOTAVAIL;
+
+ case DHCP_STATE_BOUND:
+ case DHCP_STATE_RENEWING:
+ case DHCP_STATE_REBINDING:
+ addr->s_addr = client->lease->router;
+
+ break;
+ }
+
+ return 0;
+}
+
+static int client_notify(sd_dhcp_client *client, int event)
+{
+ if (client->cb)
+ client->cb(client, event, client->userdata);
+
+ return 0;
+}
+
+static int client_stop(sd_dhcp_client *client, int error)
+{
+ assert_return(client, -EINVAL);
+ assert_return(client->state != DHCP_STATE_INIT &&
+ client->state != DHCP_STATE_INIT_REBOOT, -EALREADY);
+
+ 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) {
+ 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);
+
+ 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];
+ }
+
+ return ~((sum & 0xffff) + (sum >> 16));
+}
+
+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;
+
+ 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;
+ uint16_t secs;
+ int err = 0;
+
+ secs = (usec - client->start_time) / USEC_PER_SEC;
+
+ if (client->attempt < 64)
+ client->attempt *= 2;
+
+ next_timeout = usec + (client->attempt - 1) * USEC_PER_SEC +
+ (random_u() & 0x1fffff);
+
+ err = sd_event_add_monotonic(client->event, next_timeout,
+ 10 * USEC_PER_MSEC,
+ client_timeout_resend, client,
+ &client->timeout_resend);
+ if (err < 0)
+ goto error;
+
+ switch (client->state) {
+ case DHCP_STATE_INIT:
+ err = client_send_discover(client, secs);
+ if (err >= 0) {
+ client->state = DHCP_STATE_SELECTING;
+ client->attempt = 1;
+ } else {
+ if (client->attempt >= 64)
+ goto error;
+ }
+
+ break;
+
+ case DHCP_STATE_SELECTING:
+ err = client_send_discover(client, secs);
+ if (err < 0 && client->attempt >= 64)
+ goto error;
+
+ break;
+
+ case DHCP_STATE_REQUESTING:
+ err = client_send_request(client, secs);
+ if (err < 0 && client->attempt >= 64)
+ goto error;
+
+ client->request_sent = usec;
+
+ break;
+
+ case DHCP_STATE_INIT_REBOOT:
+ case DHCP_STATE_REBOOTING:
+ case DHCP_STATE_BOUND:
+ case DHCP_STATE_RENEWING:
+ case DHCP_STATE_REBINDING:
+
+ break;
+ }
+
+ return 0;
+
+error:
+ client_stop(client, err);
+
+ /* Errors were dealt with when stopping the client, don't spill
+ errors into the event loop handler */
+ 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)
+{
+ return 0;
+}
+
+static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata)
+{
+ return 0;
+}
+
+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_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)
+{
+ int err;
+ DHCPLease *lease;
+
+ err = client_verify_headers(client, offer, len);
+ if (err < 0)
+ return err;
+
+ lease = new0(DHCPLease, 1);
+ if (!lease)
+ return -ENOMEM;
+
+ len = len - DHCP_IP_UDP_SIZE;
+ if (dhcp_option_parse(&offer->dhcp, len, client_parse_offer,
+ lease) != DHCP_OFFER)
+ goto error;
+
+ lease->address = offer->dhcp.yiaddr;
+
+ if (lease->address == INADDR_ANY ||
+ lease->server_address == INADDR_ANY ||
+ lease->subnet_mask == INADDR_ANY ||
+ lease->lifetime == 0)
+ goto error;
+
+ client->lease = lease;
+
+ return 0;
+
+error:
+ free(lease);
+
+ return -ENOMSG;
+}
+
+static int client_receive_ack(sd_dhcp_client *client, DHCPPacket *offer,
+ size_t len)
+{
+ int r;
+ DHCPLease *lease;
+
+ r = client_verify_headers(client, offer, len);
+ if (r < 0)
+ return r;
+
+ lease = new0(DHCPLease, 1);
+ if (!lease)
+ return -ENOBUFS;
+
+ len = len - DHCP_IP_UDP_SIZE;
+ r = dhcp_option_parse(&offer->dhcp, len, client_parse_offer, lease);
+
+ if (r != DHCP_ACK)
+ goto error;
+
+ lease->address = offer->dhcp.yiaddr;
+
+ if (lease->address == INADDR_ANY ||
+ lease->server_address == INADDR_ANY ||
+ lease->subnet_mask == INADDR_ANY || lease->lifetime == 0) {
+ r = -ENOMSG;
+ goto error;
+ }
+
+ 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;
+ }
+
+ free(client->lease);
+ }
+
+ client->lease = lease;
+
+ return r;
+
+error:
+ free(lease);
+
+ return r;
+}
+
+static uint64_t client_compute_timeout(uint64_t request_sent,
+ uint32_t lifetime)
+{
+ return request_sent + (lifetime - 3) * USEC_PER_SEC +
+ + (random_u() & 0x1fffff);
+}
+
+static int client_set_lease_timeouts(sd_dhcp_client *client, uint64_t usec)
+{
+ int err;
+ uint64_t next_timeout;
+
+ if (client->lease->lifetime < 10)
+ return -EINVAL;
+
+ 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;
+
+ err = sd_event_add_monotonic(client->event, next_timeout,
+ 10 * USEC_PER_MSEC,
+ client_timeout_t1, client,
+ &client->timeout_t1);
+ if (err < 0)
+ return err;
+
+ 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;
+
+ err = sd_event_add_monotonic(client->event, next_timeout,
+ 10 * USEC_PER_MSEC,
+ client_timeout_t2, client,
+ &client->timeout_t2);
+ if (err < 0)
+ return err;
+
+ next_timeout = client_compute_timeout(client->request_sent,
+ client->lease->lifetime);
+ if (next_timeout < usec)
+ return -EINVAL;
+
+ err = sd_event_add_monotonic(client->event, next_timeout,
+ 10 * USEC_PER_MSEC,
+ client_timeout_expire, client,
+ &client->timeout_expire);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int client_receive_raw_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;
+ DHCPPacket *message;
+ usec_t time_now;
+
+ 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;
+
+ message = (DHCPPacket *)&buf;
+
+ switch (client->state) {
+ case DHCP_STATE_SELECTING:
+
+ 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;
+ }
+
+ break;
+
+ case DHCP_STATE_REQUESTING:
+
+ r = client_receive_ack(client, message, len);
+ if (r == DHCP_EVENT_NO_LEASE)
+ goto error;
+
+ if (r >= 0) {
+ client->timeout_resend =
+ sd_event_source_unref(client->timeout_resend);
+
+ 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;
+
+ client_notify(client, DHCP_EVENT_IP_ACQUIRE);
+
+ close(client->fd);
+ client->fd = -1;
+ client->receive_message =
+ sd_event_source_unref(client->receive_message);
+ }
+ break;
+
+ case DHCP_STATE_INIT:
+ case DHCP_STATE_INIT_REBOOT:
+ case DHCP_STATE_REBOOTING:
+ case DHCP_STATE_BOUND:
+ case DHCP_STATE_RENEWING:
+ case DHCP_STATE_REBINDING:
+
+ break;
+ }
+
+error:
+ if (r < 0)
+ return client_stop(client, r);
+
+ return 0;
+}
+
+int sd_dhcp_client_start(sd_dhcp_client *client)
+{
+ int err;
+
+ assert_return(client, -EINVAL);
+ assert_return(client->index >= 0, -EINVAL);
+ assert_return(client->state == DHCP_STATE_INIT ||
+ client->state == DHCP_STATE_INIT_REBOOT, -EBUSY);
+
+ client->xid = random_u();
+
+ client->fd = dhcp_network_bind_raw_socket(client->index,
+ &client->link);
+
+ if (client->fd < 0) {
+ err = client->fd;
+ goto error;
+ }
+
+ err = sd_event_add_io(client->event, client->fd, EPOLLIN,
+ client_receive_raw_message, client,
+ &client->receive_message);
+ if (err < 0)
+ goto error;
+
+ client->start_time = now(CLOCK_MONOTONIC);
+ err = sd_event_add_monotonic(client->event, client->start_time, 0,
+ client_timeout_resend, client,
+ &client->timeout_resend);
+ if (err < 0)
+ goto error;
+
+ return 0;
+
+error:
+ client_stop(client, err);
+
+ return err;
+}
+
+int sd_dhcp_client_stop(sd_dhcp_client *client)
+{
+ return client_stop(client, DHCP_EVENT_STOP);
+}
+
+sd_dhcp_client *sd_dhcp_client_free(sd_dhcp_client *client)
+{
+ assert_return(client, NULL);
+
+ sd_dhcp_client_stop(client);
+
+ sd_event_unref(client->event);
+ free(client->req_opts);
+ free(client);
+
+ return NULL;
+}
+
+sd_dhcp_client *sd_dhcp_client_new(sd_event *event)
+{
+ sd_dhcp_client *client;
+
+ assert_return(event, NULL);
+
+ client = new0(sd_dhcp_client, 1);
+ if (!client)
+ return NULL;
+
+ client->event = sd_event_ref(event);
+ 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) {
+ free(client);
+ return NULL;
+ }
+
+ return client;
+}
diff --git a/src/libsystemd-dhcp/internal.h b/src/libsystemd-dhcp/internal.h
new file mode 100644
index 0000000..85bb908
--- /dev/null
+++ b/src/libsystemd-dhcp/internal.h
@@ -0,0 +1,41 @@
+/*-*- 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 "protocol.h"
+
+int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link);
+int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
+ 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
new file mode 100644
index 0000000..3a16947
--- /dev/null
+++ b/src/libsystemd-dhcp/network.c
@@ -0,0 +1,65 @@
+/***
+ 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 "internal.h"
+
+int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link)
+ {
+ int s;
+
+ 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(s);
+ return -errno;
+ }
+
+ return s;
+}
+
+int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
+ const void *packet, size_t len)
+{
+ int err = 0;
+
+ if (sendto(s, packet, len, 0, &link->sa, sizeof(link->ll)) < 0)
+ err = -errno;
+
+ return err;
+}
diff --git a/src/libsystemd-dhcp/option.c b/src/libsystemd-dhcp/option.c
new file mode 100644
index 0000000..235b387
--- /dev/null
+++ b/src/libsystemd-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 "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
new file mode 100644
index 0000000..1e59965
--- /dev/null
+++ b/src/libsystemd-dhcp/protocol.h
@@ -0,0 +1,119 @@
+/*-*- 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;
+ uint32_t ciaddr;
+ uint32_t yiaddr;
+ uint32_t siaddr;
+ uint32_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_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/test-libsystemd-dhcp-client.c b/src/libsystemd-dhcp/test-libsystemd-dhcp-client.c
new file mode 100644
index 0000000..ee0848d
--- /dev/null
+++ b/src/libsystemd-dhcp/test-libsystemd-dhcp-client.c
@@ -0,0 +1,224 @@
+/*-*- 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 "protocol.h"
+#include "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)
+{
+ sd_dhcp_client *client;
+
+ client = sd_dhcp_client_new(e);
+
+ assert(client);
+
+ 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);
+}
+
+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];
+ }
+
+ return ~((sum & 0xffff) + (sum >> 16));
+}
+
+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
+ };
+
+ uint8_t check[2] = {
+ 0x78, 0xae
+ };
+
+ uint16_t *val = (uint16_t *)check;
+
+ assert(client_checksum(&buf, 20) == *val);
+}
+
+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];
+}
+
+static void test_discover_message(sd_event *e)
+{
+ sd_dhcp_client *client;
+ int res;
+
+ client = sd_dhcp_client_new(e);
+ assert(client);
+
+ 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-libsystemd-dhcp-option.c b/src/libsystemd-dhcp/test-libsystemd-dhcp-option.c
new file mode 100644
index 0000000..b85da79
--- /dev/null
+++ b/src/libsystemd-dhcp/test-libsystemd-dhcp-option.c
@@ -0,0 +1,377 @@
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <string.h>
+#include <assert.h>
+
+#include "util.h"
+#include "macro.h"
+
+#include "protocol.h"
+#include "internal.h"
+
+struct option_desc {
+ uint8_t sname[64];
+ int snamelen;
+ uint8_t file[128];
+ int filelen;
+ uint8_t options[128];
+ int len;
+ bool success;
+ int filepos;
+ int snamepos;
+ int pos;
+};
+
+static bool verbose = false;
+
+static struct option_desc option_tests[] = {
+ { {}, 0, {}, 0, { 42, 5, 65, 66, 67, 68, 69 }, 7, false, },
+ { {}, 0, {}, 0, { 42, 5, 65, 66, 67, 68, 69, 0, 0,
+ DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 12, true, },
+ { {}, 0, {}, 0, { 8, 255, 70, 71, 72 }, 5, false, },
+ { {}, 0, {}, 0, { 0x35, 0x01, 0x05, 0x36, 0x04, 0x01, 0x00, 0xa8,
+ 0xc0, 0x33, 0x04, 0x00, 0x01, 0x51, 0x80, 0x01,
+ 0x04, 0xff, 0xff, 0xff, 0x00, 0x03, 0x04, 0xc0,
+ 0xa8, 0x00, 0x01, 0x06, 0x04, 0xc0, 0xa8, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
+ 40, true, },
+ { {}, 0, {}, 0, { DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_OFFER,
+ 42, 3, 0, 0, 0 }, 8, true, },
+ { {}, 0, {}, 0, { 42, 2, 1, 2, 44 }, 5, false, },
+
+ { {}, 0,
+ { 222, 3, 1, 2, 3, DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_NAK }, 8,
+ { DHCP_OPTION_OVERLOAD, 1, DHCP_OVERLOAD_FILE }, 3, true, },
+
+ { { 1, 4, 1, 2, 3, 4, DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 9,
+ { 222, 3, 1, 2, 3 }, 5,
+ { DHCP_OPTION_OVERLOAD, 1,
+ DHCP_OVERLOAD_FILE|DHCP_OVERLOAD_SNAME }, 3, true, },
+};
+
+static const char *dhcp_type(int type)
+{
+ switch(type) {
+ case DHCP_DISCOVER:
+ return "DHCPDISCOVER";
+ case DHCP_OFFER:
+ return "DHCPOFFER";
+ case DHCP_REQUEST:
+ return "DHCPREQUEST";
+ case DHCP_DECLINE:
+ return "DHCPDECLINE";
+ case DHCP_ACK:
+ return "DHCPACK";
+ case DHCP_NAK:
+ return "DHCPNAK";
+ case DHCP_RELEASE:
+ return "DHCPRELEASE";
+ default:
+ return "unknown";
+ }
+}
+
+static void test_invalid_buffer_length(void)
+{
+ DHCPMessage message;
+
+ 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 != -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;
+}
--
1.7.10.4
More information about the systemd-devel
mailing list