[systemd-devel] [PATCH 21/28] dhcp: Handle received DHCP Offer message
Patrik Flykt
patrik.flykt at linux.intel.com
Wed Nov 13 13:22:49 PST 2013
Create a function for handling the full IP, UDP and DHCP packet
and tie it to the main loop. Verify IP and UDP headers and checksum.
Creat a new lease structure with using the values supplied in the
DHCP message. Free the lease structure when client is stopped.
Split out socket handling into a creation and a sending part. As a
result modify the test code.
---
src/dhcp/client.c | 207 ++++++++++++++++++++++++++++++++++++++++++-
src/dhcp/internal.h | 4 +
src/dhcp/network.c | 33 +++----
src/dhcp/protocol.h | 6 ++
src/dhcp/test-dhcp-client.c | 21 ++++-
5 files changed, 253 insertions(+), 18 deletions(-)
diff --git a/src/dhcp/client.c b/src/dhcp/client.c
index cb3d41b..84d5c25 100644
--- a/src/dhcp/client.c
+++ b/src/dhcp/client.c
@@ -32,17 +32,31 @@
#define DHCP_CLIENT_MIN_OPTIONS_SIZE 312
+struct DHCPLease {
+ uint32_t lifetime;
+ struct in_addr address;
+ struct in_addr server_address;
+ struct in_addr subnet_mask;
+ struct in_addr router;
+};
+
+typedef struct DHCPLease DHCPLease;
+
struct DHCPClient {
DHCPState state;
sd_event *event;
sd_event_source *timeout_resend;
int index;
+ int fd;
+ struct sockaddr_ll link;
+ sd_event_source *receive_message;
uint8_t *req_opts;
int req_opts_size;
struct in_addr *last_addr;
struct ether_addr mac_addr;
uint32_t xid;
uint64_t start_time;
+ DHCPLease *lease;
};
static uint8_t default_req_opts[] = {
@@ -145,6 +159,14 @@ static int client_stop(DHCPClient *client, int error)
client->state == DHCP_STATE_INIT_REBOOT)
return -EALREADY;
+ if (client->fd > 0)
+ close(client->fd);
+ client->fd = 0;
+
+ if (client->receive_message)
+ client->receive_message =
+ sd_event_source_unref(client->receive_message);
+
if (client->timeout_resend)
client->timeout_resend =
sd_event_source_unref(client->timeout_resend);
@@ -154,6 +176,7 @@ static int client_stop(DHCPClient *client, int error)
case DHCP_STATE_INIT:
case DHCP_STATE_SELECTING:
+ client->start_time = 0;
client->state = DHCP_STATE_INIT;
break;
@@ -167,6 +190,11 @@ static int client_stop(DHCPClient *client, int error)
break;
}
+ if (client->lease) {
+ free(client->lease);
+ client->lease = NULL;
+ }
+
return 0;
}
@@ -293,7 +321,8 @@ static int client_send_discover(DHCPClient *client, uint16_t secs)
discover->ip.check = client_checksum(&discover->ip,
sizeof(discover->ip));
- err = __dhcp_network_send_raw_packet(client->index, discover, len);
+ err = __dhcp_network_send_raw_socket(client->fd, &client->link,
+ discover, len);
error:
free(discover);
@@ -350,6 +379,168 @@ error:
return 0;
}
+static int client_parse_offer(uint8_t code, uint8_t len, uint8_t *option,
+ void *user_data)
+{
+ DHCPLease *lease = user_data;
+ uint32_t val;
+
+ switch(code) {
+
+ case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
+ if (len == 4) {
+ memcpy(&val, option, 4);
+ lease->lifetime = ntohl(val);
+ }
+
+ break;
+
+ case DHCP_OPTION_SERVER_IDENTIFIER:
+ if (len >= 4)
+ memcpy(&lease->server_address.s_addr, option, 4);
+
+ break;
+
+ case DHCP_OPTION_SUBNET_MASK:
+ if (len >= 4)
+ memcpy(&lease->subnet_mask.s_addr, option, 4);
+
+ break;
+
+ case DHCP_OPTION_ROUTER:
+ if (len >= 4)
+ memcpy(&lease->router.s_addr, option, 4);
+
+ break;
+ }
+
+ return 0;
+}
+
+static int client_receive_offer(DHCPClient *client,
+ DHCPPacket *offer, int len)
+{
+ int hdrlen;
+ DHCPLease *lease;
+
+ if (len < (DHCP_IP_UDP_SIZE + DHCP_MESSAGE_SIZE))
+ return -EINVAL;
+
+ hdrlen = offer->ip.ihl * 4;
+ if (hdrlen < 20 || hdrlen > len || client_checksum(&offer->ip,
+ hdrlen))
+ return -EINVAL;
+
+ offer->ip.check = offer->udp.len;
+ offer->ip.ttl = 0;
+
+ if (hdrlen + ntohs(offer->udp.len) > len ||
+ client_checksum(&offer->ip.ttl, ntohs(offer->udp.len) + 12))
+ return -EINVAL;
+
+ if (ntohs(offer->udp.source) != DHCP_PORT_SERVER ||
+ ntohs(offer->udp.dest) != DHCP_PORT_CLIENT)
+ return -EINVAL;
+
+ if (offer->dhcp.op != BOOTREPLY)
+ return -EINVAL;
+
+ if (ntohl(offer->dhcp.xid) != client->xid)
+ return -EINVAL;
+
+ if (memcmp(&offer->dhcp.chaddr[0], &client->mac_addr.ether_addr_octet,
+ ETHER_ADDR_LEN))
+ return -EINVAL;
+
+ lease = new0(DHCPLease, 1);
+ if (!lease)
+ return -ENOBUFS;
+
+ len = len - DHCP_IP_UDP_SIZE;
+ if (__dhcp_option_parse(&offer->dhcp, len, client_parse_offer,
+ lease) != DHCP_OFFER)
+ goto error;
+
+ lease->address.s_addr = offer->dhcp.yiaddr;
+
+ if (lease->address.s_addr == INADDR_ANY ||
+ lease->server_address.s_addr == INADDR_ANY ||
+ lease->subnet_mask.s_addr == INADDR_ANY ||
+ lease->lifetime == 0)
+ goto error;
+
+ client->lease = lease;
+
+ return 0;
+
+error:
+ free(lease);
+
+ return -ENOMSG;
+}
+
+static int client_receive_raw_message(sd_event_source *s, int fd,
+ uint32_t revents, void *userdata)
+{
+ DHCPClient *client = userdata;
+ int err = 0;
+ int len, buflen;
+ uint8_t *buf;
+ DHCPPacket *message;
+
+ buflen = sizeof(DHCPPacket) + DHCP_CLIENT_MIN_OPTIONS_SIZE;
+ buf = malloc0(buflen);
+ if (!buf)
+ goto error;
+
+ len = read(fd, buf, buflen);
+ if (len < 0) {
+ err = -errno;
+ goto error;
+ }
+
+ message = (DHCPPacket *)buf;
+
+ switch (client->state) {
+ case DHCP_STATE_SELECTING:
+
+ if (client_receive_offer(client, message, len) >= 0) {
+
+ close(client->fd);
+ client->fd = 0;
+ client->receive_message =
+ sd_event_source_unref(client->receive_message);
+
+ client->timeout_resend =
+ sd_event_source_unref(client->timeout_resend);
+
+ client->state = DHCP_STATE_REQUESTING;
+ }
+
+ break;
+
+ case DHCP_STATE_INIT:
+ case DHCP_STATE_INIT_REBOOT:
+ case DHCP_STATE_REBOOTING:
+ case DHCP_STATE_REQUESTING:
+ case DHCP_STATE_BOUND:
+ case DHCP_STATE_RENEWING:
+ case DHCP_STATE_REBINDING:
+
+ break;
+ }
+
+ free(buf);
+
+ return 0;
+
+error:
+ if (!err)
+ read(fd, buf, 1);
+
+ return 0;
+}
+
int dhcp_client_start(DHCPClient *client)
{
int err;
@@ -363,6 +554,20 @@ int dhcp_client_start(DHCPClient *client)
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;
+
err = sd_event_add_monotonic(client->event, now(CLOCK_MONOTONIC), 0,
client_timeout_resend, client,
&client->timeout_resend);
diff --git a/src/dhcp/internal.h b/src/dhcp/internal.h
index 92bd2a1..07bd677 100644
--- a/src/dhcp/internal.h
+++ b/src/dhcp/internal.h
@@ -22,9 +22,13 @@
***/
#include <stdint.h>
+#include <linux/if_packet.h>
#include "protocol.h"
+int __dhcp_network_bind_raw_socket(int index, struct sockaddr_ll *link);
+int __dhcp_network_send_raw_socket(int s, struct sockaddr_ll *link,
+ void *packet, int len);
int __dhcp_network_send_raw_packet(int index, void *packet, int len);
int __dhcp_option_append(uint8_t **buf, int *buflen, uint8_t code,
diff --git a/src/dhcp/network.c b/src/dhcp/network.c
index 623251d..1b25f86 100644
--- a/src/dhcp/network.c
+++ b/src/dhcp/network.c
@@ -28,36 +28,39 @@
#include "internal.h"
-int __dhcp_network_send_raw_packet(int index, void *packet, int len)
+int __dhcp_network_bind_raw_socket(int index, struct sockaddr_ll *link)
{
int err, s;
- struct sockaddr_ll link;
-
- err = 0;
s = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_IP));
if (s < 0)
return -errno;
- memset(&link, 0, sizeof(link));
+ memset(link, 0, sizeof(struct sockaddr_ll));
- link.sll_family = AF_PACKET;
- link.sll_protocol = htons(ETH_P_IP);
- link.sll_ifindex = index;
- link.sll_halen = ETH_ALEN;
- memset(&link.sll_addr, 0xff, ETH_ALEN);
+ link->sll_family = AF_PACKET;
+ link->sll_protocol = htons(ETH_P_IP);
+ link->sll_ifindex = index;
+ link->sll_halen = ETH_ALEN;
+ memset(link->sll_addr, 0xff, ETH_ALEN);
- if (bind(s, (struct sockaddr *)&link, sizeof(link)) < 0) {
+ if (bind(s, (struct sockaddr *)link, sizeof(struct sockaddr_ll)) < 0) {
err = -errno;
close(s);
return err;
}
- if (sendto(s, packet, len, 0, (struct sockaddr *)&link,
- sizeof(link)) < 0)
- err = -errno;
+ return s;
+}
- close(s);
+int __dhcp_network_send_raw_socket(int s, struct sockaddr_ll *link,
+ void *packet, int len)
+{
+ int err = 0;
+
+ if (sendto(s, packet, len, 0, (struct sockaddr *)link,
+ sizeof(struct sockaddr_ll)) < 0)
+ err = -errno;
return err;
}
diff --git a/src/dhcp/protocol.h b/src/dhcp/protocol.h
index a2c5216..014a6f8 100644
--- a/src/dhcp/protocol.h
+++ b/src/dhcp/protocol.h
@@ -52,6 +52,10 @@ struct DHCPPacket {
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))
+
#define DHCP_PORT_SERVER 67
#define DHCP_PORT_CLIENT 68
@@ -90,8 +94,10 @@ typedef enum DHCPState DHCPState;
#define DHCP_OPTION_DOMAIN_NAME 15
#define DHCP_OPTION_NTP_SERVER 42
#define DHCP_OPTION_REQUESTED_IP_ADDRESS 50
+#define DHCP_OPTION_IP_ADDRESS_LEASE_TIME 51
#define DHCP_OPTION_OVERLOAD 52
#define DHCP_OPTION_MESSAGE_TYPE 53
+#define DHCP_OPTION_SERVER_IDENTIFIER 54
#define DHCP_OPTION_PARAMETER_REQUEST_LIST 55
#define DHCP_OPTION_CLIENT_IDENTIFIER 61
#define DHCP_OPTION_END 255
diff --git a/src/dhcp/test-dhcp-client.c b/src/dhcp/test-dhcp-client.c
index 9cad04b..dc2420b 100644
--- a/src/dhcp/test-dhcp-client.c
+++ b/src/dhcp/test-dhcp-client.c
@@ -24,6 +24,9 @@
#include <errno.h>
#include <stdio.h>
#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
#include "protocol.h"
#include "internal.h"
@@ -33,6 +36,8 @@ 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)
{
DHCPClient *client;
@@ -124,14 +129,15 @@ static int check_options(uint8_t code, uint8_t len, uint8_t *option,
return 0;
}
-int __dhcp_network_send_raw_packet(int index, void *packet, int len)
+int __dhcp_network_send_raw_socket(int s, struct sockaddr_ll *link,
+ void *packet, int len)
{
int size;
DHCPPacket *discover;
uint16_t ip_check, udp_check;
int res;
- assert(index == 42);
+ assert(s > 0);
assert(packet);
size = sizeof(DHCPPacket) + 4;
@@ -171,6 +177,14 @@ int __dhcp_network_send_raw_packet(int index, void *packet, int len)
return 575;
}
+int __dhcp_network_bind_raw_socket(int index, struct sockaddr_ll *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)
{
DHCPClient *client;
@@ -187,6 +201,9 @@ static void test_discover_message(sd_event *e)
res = dhcp_client_start(client);
assert(res == 575 || res == 0);
+
+ close(test_fd[0]);
+ close(test_fd[1]);
}
int main(int argc, char *argv[])
--
1.7.10.4
More information about the systemd-devel
mailing list