From ddc93db612b51179d8b62da931c2080708c8bd68 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 23 Mar 2010 17:18:18 +0100 Subject: DHCP plugin framework, send DHCP Discover upon IP request --- src/libcharon/plugins/dhcp/dhcp_socket.c | 329 +++++++++++++++++++++++++++++++ 1 file changed, 329 insertions(+) create mode 100644 src/libcharon/plugins/dhcp/dhcp_socket.c (limited to 'src/libcharon/plugins/dhcp/dhcp_socket.c') diff --git a/src/libcharon/plugins/dhcp/dhcp_socket.c b/src/libcharon/plugins/dhcp/dhcp_socket.c new file mode 100644 index 000000000..6ad600f1a --- /dev/null +++ b/src/libcharon/plugins/dhcp/dhcp_socket.c @@ -0,0 +1,329 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program 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 General Public License + * for more details. + */ + +#include "dhcp_socket.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#define DHCP_SERVER_PORT 67 +#define DHCP_CLIENT_PORT 68 + +typedef struct private_dhcp_socket_t private_dhcp_socket_t; + +/** + * Private data of an dhcp_socket_t object. + */ +struct private_dhcp_socket_t { + + /** + * Public dhcp_socket_t interface. + */ + dhcp_socket_t public; + + /** + * Random number generator + */ + rng_t *rng; + + /** + * List of active transactions + */ + linked_list_t *active; + + /** + * List of successfully completed transactions + */ + linked_list_t *completed; + + /** + * Lock for transactions + */ + mutex_t *mutex; + + /** + * Condvar to wait for transaction completion + */ + condvar_t *condvar; + + /** + * Threads waiting in condvar + */ + int waiting; + + /** + * RAW socket + */ + int skt; + + /** + * DHCP server address, or broadcast + */ + host_t *dst; + + /** + * Callback job receiving DHCP responses + */ + callback_job_t *job; +}; + +typedef enum { + BOOTREQUEST = 1, + BOOTREPLY = 2, +} dhcp_opcode_t; + +typedef enum { + DHCP_HOST_NAME = 12, + DHCP_MESSAGE_TYPE = 53, + DHCP_PARAM_REQ_LIST = 55, +} dhcp_option_type_t; + +typedef enum { + DHCP_DISCOVER = 1, +} dhcp_message_type_t; + +typedef enum { + DHCP_ROUTER = 3, + DHCP_DNS_SERVER = 6, +} dhcp_parameter_t; + +typedef struct __attribute__((packed)) { + u_int8_t type; + u_int8_t len; + char data[]; +} dhcp_option_t; + +typedef struct __attribute__((packed)) { + u_int8_t opcode; + u_int8_t hw_type; + u_int8_t hw_addr_len; + u_int8_t hop_count; + u_int32_t transaction_id; + u_int16_t number_of_seconds; + u_int16_t flags; + u_int32_t client_address; + u_int32_t your_address; + u_int32_t server_address; + u_int32_t gateway_address; + char client_hw_addr[6]; + char client_hw_padding[10]; + char server_hostname[64]; + char boot_filename[128]; + u_int32_t magic_cookie; + char options[252]; +} dhcp_t; + +/** + * Send DHCP discover using a given transaction + */ +static void discover(private_dhcp_socket_t *this, + dhcp_transaction_t *transaction) +{ + chunk_t id_data, broadcast = chunk_from_chars(0xFF,0xFF,0xFF,0xFF); + identification_t *identity; + dhcp_option_t *option; + dhcp_t dhcp; + int optlen = 0; + u_int hash; + host_t *src; + ssize_t len; + + memset(&dhcp, 0, sizeof(dhcp)); + dhcp.opcode = BOOTREQUEST; + dhcp.hw_type = ARPHRD_ETHER; + dhcp.hw_addr_len = 6; + dhcp.transaction_id = transaction->get_id(transaction); + if (chunk_equals(broadcast, this->dst->get_address(this->dst))) + { + /* TODO: send with 0.0.0.0 source address */ + } + else + { + /* act as relay agent */ + src = charon->kernel_interface->get_source_addr( + charon->kernel_interface, this->dst, NULL); + if (src) + { + memcpy(&dhcp.gateway_address, src->get_address(src).ptr, + sizeof(dhcp.gateway_address)); + src->destroy(src); + } + } + + identity = transaction->get_identity(transaction); + id_data = identity->get_encoding(identity); + + /* magic bytes, a locally administered unicast MAC */ + dhcp.client_hw_addr[0] = 0x7A; + dhcp.client_hw_addr[1] = 0xA7; + /* with ID specific postfix */ + hash = htonl(chunk_hash(id_data)); + memcpy(&dhcp.client_hw_addr[2], &hash, 4); + + dhcp.magic_cookie = htonl(0x63825363); + + option = (dhcp_option_t*)&dhcp.options[optlen]; + option->type = DHCP_MESSAGE_TYPE; + option->len = 1; + option->data[0] = DHCP_DISCOVER; + optlen += sizeof(dhcp_option_t) + option->len; + + option = (dhcp_option_t*)&dhcp.options[optlen]; + option->type = DHCP_HOST_NAME; + option->len = min(id_data.len, 64); + memcpy(option->data, id_data.ptr, option->len); + optlen += sizeof(dhcp_option_t) + option->len; + + option = (dhcp_option_t*)&dhcp.options[optlen]; + option->type = DHCP_PARAM_REQ_LIST; + option->len = 2; + option->data[0] = DHCP_ROUTER; + option->data[1] = DHCP_DNS_SERVER; + optlen += sizeof(dhcp_option_t) + option->len; + + dhcp.options[optlen++] = 0xFF; + + len = offsetof(dhcp_t, magic_cookie) + ((optlen + 4) / 64 * 64 + 64); + if (sendto(this->skt, &dhcp, len, 0, this->dst->get_sockaddr(this->dst), + *this->dst->get_sockaddr_len(this->dst)) != len) + { + DBG1(DBG_CFG, "sending DHCP DISCOVER failed: %s", strerror(errno)); + } +} + +METHOD(dhcp_socket_t, enroll, dhcp_transaction_t*, + private_dhcp_socket_t *this, identification_t *identity) +{ + dhcp_transaction_t *transaction; + u_int32_t id; + + this->rng->get_bytes(this->rng, sizeof(id), (u_int8_t*)&id); + transaction = dhcp_transaction_create(id, identity); + discover(this, transaction); + transaction->destroy(transaction); + + return NULL; +} + +METHOD(dhcp_socket_t, destroy, void, + private_dhcp_socket_t *this) +{ + if (this->job) + { + this->job->cancel(this->job); + } + while (this->waiting) + { + this->condvar->signal(this->condvar); + } + if (this->skt > 0) + { + close(this->skt); + } + this->mutex->destroy(this->mutex); + this->condvar->destroy(this->condvar); + this->active->destroy(this->active); + this->completed->destroy(this->completed); + DESTROY_IF(this->rng); + DESTROY_IF(this->dst); + free(this); +} + +/** + * See header + */ +dhcp_socket_t *dhcp_socket_create() +{ + private_dhcp_socket_t *this; + struct sockaddr_in src; + int on = 1; + + INIT(this, + .public = { + .enroll = _enroll, + .destroy = _destroy, + }, + .rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK), + .mutex = mutex_create(MUTEX_TYPE_DEFAULT), + .condvar = condvar_create(CONDVAR_TYPE_DEFAULT), + .active = linked_list_create(), + .completed = linked_list_create(), + ); + + if (!this->rng) + { + DBG1(DBG_CFG, "unable to create RNG"); + destroy(this); + return NULL; + } + + this->dst = host_create_from_string(lib->settings->get_str(lib->settings, + "charon.plugins.dhcp.server", "255.255.255.255"), + DHCP_SERVER_PORT); + if (!this->dst) + { + DBG1(DBG_CFG, "configured DHCP server address invalid"); + destroy(this); + return NULL; + } + + this->skt = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (this->skt == -1) + { + DBG1(DBG_CFG, "unable to create DHCP send socket: %s", strerror(errno)); + destroy(this); + return NULL; + } + + if (setsockopt(this->skt, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) + { + DBG1(DBG_CFG, "unable to reuse DHCP socket address: %s", strerror(errno)); + destroy(this); + return NULL; + } + if (setsockopt(this->skt, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) == -1) + { + DBG1(DBG_CFG, "unable to broadcast on DHCP socket: %s", strerror(errno)); + destroy(this); + return NULL; + } + + src.sin_family = AF_INET; + src.sin_port = htons(DHCP_CLIENT_PORT); + src.sin_addr.s_addr = INADDR_ANY; + if (bind(this->skt, (struct sockaddr*)&src, sizeof(src)) == -1) + { + DBG1(DBG_CFG, "unable to bind DHCP send socket: %s", strerror(errno)); + destroy(this); + return NULL; + } + + return &this->public; +} + -- cgit v1.2.3