991 lines
28 KiB
C
991 lines
28 KiB
C
/******************************************************************************
|
|
* Copyright (c) 2004, 2008 IBM Corporation
|
|
* All rights reserved.
|
|
* This program and the accompanying materials
|
|
* are made available under the terms of the BSD License
|
|
* which accompanies this distribution, and is available at
|
|
* http://www.opensource.org/licenses/bsd-license.php
|
|
*
|
|
* Contributors:
|
|
* IBM Corporation - initial implementation
|
|
*****************************************************************************/
|
|
|
|
|
|
/******************************* ALGORITHMS ******************************/
|
|
|
|
/** \file dhcp.c <pre>
|
|
* **************** State-transition diagram for DHCP client *************
|
|
*
|
|
* +---------+ Note: DHCP-server msg / DHCP-client msg
|
|
* | INIT |
|
|
* +---------+
|
|
* |
|
|
* | - / Discover
|
|
* V
|
|
* +---------+
|
|
* | SELECT | Timeout
|
|
* +---------+ |
|
|
* | |
|
|
* | Offer / Request |
|
|
* | |
|
|
* V V
|
|
* +---------+ NACK / - ***********
|
|
* | REQUEST | ----------------> * FAULT *
|
|
* +---------+ ***********
|
|
* |
|
|
* | ACK / - ***********
|
|
* +----------------------> * SUCCESS *
|
|
* ***********
|
|
*
|
|
* ************************************************************************
|
|
* </pre> */
|
|
|
|
|
|
/********************** DEFINITIONS & DECLARATIONS ***********************/
|
|
|
|
#include <dhcp.h>
|
|
#include <ethernet.h>
|
|
#include <ipv4.h>
|
|
#include <udp.h>
|
|
#include <dns.h>
|
|
#include <args.h>
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <sys/socket.h>
|
|
#include <ctype.h>
|
|
#include <stdlib.h>
|
|
|
|
/* DHCP Message Types */
|
|
#define DHCPDISCOVER 1
|
|
#define DHCPOFFER 2
|
|
#define DHCPREQUEST 3
|
|
#define DHCPDECLINE 4
|
|
#define DHCPACK 5
|
|
#define DHCPNACK 6
|
|
#define DHCPRELEASE 7
|
|
#define DHCPINFORM 8
|
|
|
|
/* DHCP Option Codes */
|
|
#define DHCP_MASK 1
|
|
#define DHCP_ROUTER 3
|
|
#define DHCP_DNS 6
|
|
#define DHCP_REQUESTED_IP 50
|
|
#define DHCP_OVERLOAD 52
|
|
#define DHCP_MSG_TYPE 53
|
|
#define DHCP_SERVER_ID 54
|
|
#define DHCP_REQUEST_LIST 55
|
|
#define DHCP_TFTP_SERVER 66
|
|
#define DHCP_BOOTFILE 67
|
|
#define DHCP_CLIENT_ARCH 93
|
|
#define DHCP_PXELINUX_CFGFILE 209 /* See RFC 5071 */
|
|
#define DHCP_PXELINUX_PREFIX 210
|
|
#define DHCP_ENDOPT 0xFF
|
|
#define DHCP_PADOPT 0x00
|
|
|
|
/* "file/sname" overload option values */
|
|
#define DHCP_OVERLOAD_FILE 1
|
|
#define DHCP_OVERLOAD_SNAME 2
|
|
#define DHCP_OVERLOAD_BOTH 3
|
|
|
|
/* DHCP states codes */
|
|
#define DHCP_STATE_SELECT 1
|
|
#define DHCP_STATE_REQUEST 2
|
|
#define DHCP_STATE_SUCCESS 3
|
|
#define DHCP_STATE_FAULT 4
|
|
|
|
/* DHCP Client Architecture */
|
|
#ifndef DHCPARCH
|
|
#define USE_DHCPARCH 0
|
|
#define DHCPARCH 0
|
|
#else
|
|
#define USE_DHCPARCH 1
|
|
#endif
|
|
|
|
static uint8_t dhcp_magic[] = {0x63, 0x82, 0x53, 0x63};
|
|
/**< DHCP_magic is a cookie, that identifies DHCP options (see RFC 2132) */
|
|
|
|
/** \struct dhcp_options_t
|
|
* This structure is used to fill options in DHCP-msg during transmitting
|
|
* or to retrieve options from DHCP-msg during receiving.
|
|
* <p>
|
|
* If flag[i] == TRUE then field for i-th option retains valid value and
|
|
* information from this field may retrived (in case of receiving) or will
|
|
* be transmitted (in case of transmitting).
|
|
*
|
|
*/
|
|
typedef struct {
|
|
uint8_t flag[256]; /**< Show if corresponding opt. is valid */
|
|
uint8_t request_list[256]; /**< o.55 If i-th member is TRUE, then i-th
|
|
option will be requested from server */
|
|
uint32_t server_ID; /**< o.54 Identifies DHCP-server */
|
|
uint32_t requested_IP; /**< o.50 Must be filled in DHCP-Request */
|
|
uint32_t dns_IP; /**< o. 6 DNS IP */
|
|
uint32_t router_IP; /**< o. 3 Router IP */
|
|
uint32_t subnet_mask; /**< o. 1 Subnet mask */
|
|
uint8_t msg_type; /**< o.53 DHCP-message type */
|
|
uint8_t overload; /**< o.52 Overload sname/file fields */
|
|
char tftp_server[256]; /**< o.66 TFTP server name */
|
|
char bootfile[256]; /**< o.67 Boot file name */
|
|
uint16_t client_arch; /**< o.93 Client architecture type */
|
|
} dhcp_options_t;
|
|
|
|
/** Stores state of DHCP-client (refer to State-transition diagram) */
|
|
static uint8_t dhcp_state;
|
|
|
|
|
|
/***************************** PROTOTYPES ********************************/
|
|
|
|
static int32_t dhcp_attempt(int fd);
|
|
|
|
static int32_t dhcp_encode_options(uint8_t * opt_field, dhcp_options_t * opt_struct);
|
|
|
|
static int32_t dhcp_decode_options(uint8_t opt_field[], uint32_t opt_len,
|
|
dhcp_options_t * opt_struct);
|
|
|
|
static int8_t dhcp_merge_options(uint8_t dst_options[], uint32_t * dst_len,
|
|
uint8_t src_options[], uint32_t src_len);
|
|
|
|
static int8_t dhcp_find_option(uint8_t options[], uint32_t len,
|
|
uint8_t op_code, uint32_t * op_offset);
|
|
|
|
static void dhcp_append_option(uint8_t dst_options[], uint32_t * dst_len,
|
|
uint8_t * new_option);
|
|
|
|
static void dhcp_combine_option(uint8_t dst_options[], uint32_t * dst_len,
|
|
uint32_t dst_offset, uint8_t * new_option);
|
|
|
|
static void dhcp_send_discover(int fd);
|
|
|
|
static void dhcp_send_request(int fd);
|
|
|
|
/***************************** LOCAL VARIABLES ***************************/
|
|
|
|
static uint8_t ether_packet[ETH_MTU_SIZE];
|
|
static uint32_t dhcp_own_ip = 0;
|
|
static uint32_t dhcp_server_ip = 0;
|
|
static uint32_t dhcp_siaddr_ip = 0;
|
|
static char dhcp_filename[256];
|
|
static char dhcp_tftp_name[256];
|
|
static uint32_t dhcp_xid;
|
|
static char *pxelinux_cfgfile;
|
|
static char *pxelinux_prefix;
|
|
|
|
static char * response_buffer;
|
|
|
|
/***************************** IMPLEMENTATION ****************************/
|
|
|
|
void dhcpv4_generate_transaction_id(void)
|
|
{
|
|
dhcp_xid = (rand() << 16) ^ rand();
|
|
}
|
|
|
|
int32_t dhcpv4(char *ret_buffer, filename_ip_t *fn_ip)
|
|
{
|
|
uint32_t dhcp_tftp_ip = 0;
|
|
int fd = fn_ip->fd;
|
|
|
|
strcpy(dhcp_filename, "");
|
|
strcpy(dhcp_tftp_name, "");
|
|
|
|
pxelinux_cfgfile = pxelinux_prefix = NULL;
|
|
|
|
response_buffer = ret_buffer;
|
|
|
|
if (dhcp_attempt(fd) == 0)
|
|
return -1;
|
|
|
|
if (fn_ip->own_ip) {
|
|
dhcp_own_ip = fn_ip->own_ip;
|
|
}
|
|
if (fn_ip->server_ip) {
|
|
dhcp_siaddr_ip = fn_ip->server_ip;
|
|
}
|
|
if(fn_ip->filename[0] != 0) {
|
|
strcpy(dhcp_filename, fn_ip->filename);
|
|
}
|
|
|
|
// TFTP SERVER
|
|
if (!strlen(dhcp_tftp_name)) {
|
|
if (!dhcp_siaddr_ip) {
|
|
// ERROR: TFTP name is not presented
|
|
return -3;
|
|
}
|
|
|
|
// take TFTP-ip from siaddr field
|
|
dhcp_tftp_ip = dhcp_siaddr_ip;
|
|
}
|
|
else {
|
|
// TFTP server defined by its name
|
|
if (!strtoip(dhcp_tftp_name, (char *)&dhcp_tftp_ip)) {
|
|
if (!dns_get_ip(fd, dhcp_tftp_name, (uint8_t *)&dhcp_tftp_ip, 4)) {
|
|
// DNS error - can't obtain TFTP-server name
|
|
// Use TFTP-ip from siaddr field, if presented
|
|
if (dhcp_siaddr_ip) {
|
|
dhcp_tftp_ip = dhcp_siaddr_ip;
|
|
}
|
|
else {
|
|
// ERROR: Can't obtain TFTP server IP
|
|
return -4;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Store configuration info into filename_ip strucutre
|
|
fn_ip -> own_ip = dhcp_own_ip;
|
|
fn_ip -> server_ip = dhcp_tftp_ip;
|
|
strcpy(fn_ip->filename, dhcp_filename);
|
|
|
|
fn_ip->pl_cfgfile = pxelinux_cfgfile;
|
|
fn_ip->pl_prefix = pxelinux_prefix;
|
|
pxelinux_cfgfile = pxelinux_prefix = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* DHCP: Tries o obtain DHCP parameters, refer to state-transition diagram
|
|
*/
|
|
static int32_t dhcp_attempt(int fd)
|
|
{
|
|
int sec;
|
|
|
|
// Send DISCOVER message and switch DHCP-client to SELECT state
|
|
dhcp_send_discover(fd);
|
|
|
|
dhcp_state = DHCP_STATE_SELECT;
|
|
|
|
// setting up a timer with a timeout of two seconds
|
|
for (sec = 0; sec < 2; sec++) {
|
|
set_timer(TICKS_SEC);
|
|
do {
|
|
receive_ether(fd);
|
|
|
|
// Wait until client will switch to Final state or Timeout occurs
|
|
switch (dhcp_state) {
|
|
case DHCP_STATE_SUCCESS :
|
|
return 1;
|
|
case DHCP_STATE_FAULT :
|
|
return 0;
|
|
}
|
|
} while (get_timer() > 0);
|
|
}
|
|
|
|
// timeout
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* DHCP: Supplements DHCP-message with options stored in structure.
|
|
* For more information about option coding see dhcp_options_t.
|
|
*
|
|
* @param opt_field Points to the "vend" field of DHCP-message
|
|
* (destination)
|
|
* @param opt_struct this structure stores info about the options which
|
|
* will be added to DHCP-message (source)
|
|
* @return TRUE - options packed;
|
|
* FALSE - error condition occurs.
|
|
* @see dhcp_options_t
|
|
*/
|
|
static int32_t dhcp_encode_options(uint8_t * opt_field, dhcp_options_t * opt_struct)
|
|
{
|
|
uint8_t * options = opt_field;
|
|
uint16_t i, sum; // used to define is any options set
|
|
|
|
// magic
|
|
memcpy(options, dhcp_magic, 4);
|
|
options += 4;
|
|
|
|
// fill message type
|
|
switch (opt_struct -> msg_type) {
|
|
case DHCPDISCOVER :
|
|
case DHCPREQUEST :
|
|
case DHCPDECLINE :
|
|
case DHCPINFORM :
|
|
case DHCPRELEASE :
|
|
options[0] = DHCP_MSG_TYPE;
|
|
options[1] = 1;
|
|
options[2] = opt_struct -> msg_type;
|
|
options += 3;
|
|
break;
|
|
default :
|
|
return 0; // Unsupported DHCP-message
|
|
}
|
|
|
|
if (opt_struct -> overload) {
|
|
options[0] = DHCP_OVERLOAD;
|
|
options[1] = 0x01;
|
|
options[2] = opt_struct -> overload;
|
|
options +=3;
|
|
}
|
|
|
|
if (opt_struct -> flag[DHCP_REQUESTED_IP]) {
|
|
options[0] = DHCP_REQUESTED_IP;
|
|
options[1] = 0x04;
|
|
* (uint32_t *) (options + 2) = htonl (opt_struct -> requested_IP);
|
|
options +=6;
|
|
}
|
|
|
|
if (opt_struct -> flag[DHCP_SERVER_ID]) {
|
|
options[0] = DHCP_SERVER_ID;
|
|
options[1] = 0x04;
|
|
* (uint32_t *) (options + 2) = htonl (opt_struct -> server_ID);
|
|
options +=6;
|
|
}
|
|
|
|
sum = 0;
|
|
for (i = 0; i < 256; i++)
|
|
sum += opt_struct -> request_list[i];
|
|
|
|
if (sum) {
|
|
options[0] = DHCP_REQUEST_LIST;
|
|
options[1] = sum;
|
|
options += 2;
|
|
for (i = 0; i < 256; i++) {
|
|
if (opt_struct -> request_list[i]) {
|
|
options[0] = i; options++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (opt_struct -> flag[DHCP_TFTP_SERVER]) {
|
|
options[0] = DHCP_TFTP_SERVER;
|
|
options[1] = strlen(opt_struct->tftp_server) + 1;
|
|
memcpy(options + 2, opt_struct -> tftp_server, options[1]);
|
|
options += options[1] + 2;
|
|
}
|
|
|
|
if (opt_struct -> flag[DHCP_BOOTFILE]) {
|
|
options[0] = DHCP_BOOTFILE;
|
|
options[1] = strlen(opt_struct->bootfile) + 1;
|
|
memcpy(options + 2, opt_struct -> bootfile, options[1]);
|
|
options += options[1] + 2;
|
|
}
|
|
|
|
if (opt_struct -> flag[DHCP_CLIENT_ARCH]) {
|
|
options[0] = DHCP_CLIENT_ARCH;
|
|
options[1] = 2;
|
|
options[2] = (DHCPARCH >> 8);
|
|
options[3] = DHCPARCH & 0xff;
|
|
options += 4;
|
|
}
|
|
|
|
// end options
|
|
options[0] = 0xFF;
|
|
options++;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* DHCP: Extracts encoded options from DHCP-message into the structure.
|
|
* For more information about option coding see dhcp_options_t.
|
|
*
|
|
* @param opt_field Points to the "options" field of DHCP-message
|
|
* (source).
|
|
* @param opt_len Length of "options" field.
|
|
* @param opt_struct this structure stores info about the options which
|
|
* was extracted from DHCP-message (destination).
|
|
* @return TRUE - options extracted;
|
|
* FALSE - error condition occurs.
|
|
* @see dhcp_options_t
|
|
*/
|
|
static int32_t dhcp_decode_options(uint8_t opt_field[], uint32_t opt_len,
|
|
dhcp_options_t * opt_struct)
|
|
{
|
|
uint32_t offset = 0;
|
|
|
|
memset(opt_struct, 0, sizeof(dhcp_options_t));
|
|
|
|
// magic
|
|
if (memcmp(opt_field, dhcp_magic, 4)) {
|
|
return 0;
|
|
}
|
|
|
|
offset += 4;
|
|
while (offset < opt_len) {
|
|
opt_struct -> flag[opt_field[offset]] = 1;
|
|
switch(opt_field[offset]) {
|
|
case DHCP_OVERLOAD :
|
|
opt_struct -> overload = opt_field[offset + 2];
|
|
offset += 2 + opt_field[offset + 1];
|
|
break;
|
|
|
|
case DHCP_REQUESTED_IP :
|
|
opt_struct -> requested_IP = htonl(* (uint32_t *) (opt_field + offset + 2));
|
|
offset += 2 + opt_field[offset + 1];
|
|
break;
|
|
|
|
case DHCP_MASK :
|
|
opt_struct -> flag[DHCP_MASK] = 1;
|
|
opt_struct -> subnet_mask = htonl(* (uint32_t *) (opt_field + offset + 2));
|
|
offset += 2 + opt_field[offset + 1];
|
|
break;
|
|
|
|
case DHCP_DNS :
|
|
opt_struct -> flag[DHCP_DNS] = 1;
|
|
opt_struct -> dns_IP = htonl(* (uint32_t *) (opt_field + offset + 2));
|
|
offset += 2 + opt_field[offset + 1];
|
|
break;
|
|
|
|
case DHCP_ROUTER :
|
|
opt_struct -> flag[DHCP_ROUTER] = 1;
|
|
opt_struct -> router_IP = htonl(* (uint32_t *) (opt_field + offset + 2));
|
|
offset += 2 + opt_field[offset + 1];
|
|
break;
|
|
|
|
case DHCP_MSG_TYPE :
|
|
if ((opt_field[offset + 2] > 0) && (opt_field[offset + 2] < 9))
|
|
opt_struct -> msg_type = opt_field[offset + 2];
|
|
else
|
|
return 0;
|
|
offset += 2 + opt_field[offset + 1];
|
|
break;
|
|
|
|
case DHCP_SERVER_ID :
|
|
opt_struct -> server_ID = htonl(* (uint32_t *) (opt_field + offset + 2));
|
|
offset += 2 + opt_field[offset + 1];
|
|
break;
|
|
|
|
case DHCP_TFTP_SERVER :
|
|
memcpy(opt_struct -> tftp_server, opt_field + offset + 2, opt_field[offset + 1]);
|
|
(opt_struct -> tftp_server)[opt_field[offset + 1]] = 0;
|
|
offset += 2 + opt_field[offset + 1];
|
|
break;
|
|
|
|
case DHCP_BOOTFILE :
|
|
memcpy(opt_struct -> bootfile, opt_field + offset + 2, opt_field[offset + 1]);
|
|
(opt_struct -> bootfile)[opt_field[offset + 1]] = 0;
|
|
offset += 2 + opt_field[offset + 1];
|
|
break;
|
|
|
|
case DHCP_CLIENT_ARCH :
|
|
opt_struct -> client_arch = ((opt_field[offset + 2] << 8) & 0xFF00) | (opt_field[offset + 3] & 0xFF);
|
|
offset += 4;
|
|
break;
|
|
|
|
case DHCP_PXELINUX_CFGFILE:
|
|
pxelinux_cfgfile = malloc(opt_field[offset + 1] + 1);
|
|
if (pxelinux_cfgfile) {
|
|
memcpy(pxelinux_cfgfile, opt_field + offset + 2,
|
|
opt_field[offset + 1]);
|
|
pxelinux_cfgfile[opt_field[offset + 1]] = 0;
|
|
}
|
|
offset += 2 + opt_field[offset + 1];
|
|
break;
|
|
|
|
case DHCP_PXELINUX_PREFIX:
|
|
pxelinux_prefix = malloc(opt_field[offset + 1] + 1);
|
|
if (pxelinux_prefix) {
|
|
memcpy(pxelinux_prefix, opt_field + offset + 2,
|
|
opt_field[offset + 1]);
|
|
pxelinux_prefix[opt_field[offset + 1]] = 0;
|
|
}
|
|
offset += 2 + opt_field[offset + 1];
|
|
break;
|
|
|
|
case DHCP_PADOPT :
|
|
offset++;
|
|
break;
|
|
|
|
case DHCP_ENDOPT : // End of options
|
|
return 1;
|
|
|
|
default :
|
|
offset += 2 + opt_field[offset + 1]; // Unsupported opt. - do nothing
|
|
}
|
|
}
|
|
if (offset == opt_len)
|
|
return 1; // options finished without 0xFF
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* DHCP: Appends information from source "options" into dest "options".
|
|
* This function is used to support "file/sname" overloading.
|
|
*
|
|
* @param dst_options destanation "options" field
|
|
* @param dst_len size of dst_options (modified by this function)
|
|
* @param src_options source "options" field
|
|
* @param src_len size of src_options
|
|
* @return TRUE - options merged;
|
|
* FALSE - error condition occurs.
|
|
*/
|
|
static int8_t dhcp_merge_options(uint8_t dst_options[], uint32_t * dst_len,
|
|
uint8_t src_options[], uint32_t src_len)
|
|
{
|
|
uint32_t dst_offset, src_offset = 0;
|
|
|
|
// remove ENDOPT if presented
|
|
if (dhcp_find_option(dst_options, * dst_len, DHCP_ENDOPT, &dst_offset))
|
|
* dst_len = dst_offset;
|
|
|
|
while (src_offset < src_len) {
|
|
switch(src_options[src_offset]) {
|
|
case DHCP_PADOPT:
|
|
src_offset++;
|
|
break;
|
|
case DHCP_ENDOPT:
|
|
return 1;
|
|
default:
|
|
if (dhcp_find_option(dst_options, * dst_len,
|
|
src_options[src_offset],
|
|
&dst_offset)) {
|
|
dhcp_combine_option(dst_options, dst_len,
|
|
dst_offset,
|
|
(uint8_t *) src_options +
|
|
src_offset);
|
|
}
|
|
else {
|
|
dhcp_append_option(dst_options, dst_len, src_options + src_offset);
|
|
}
|
|
src_offset += 2 + src_options[src_offset + 1];
|
|
}
|
|
}
|
|
|
|
if (src_offset == src_len)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* DHCP: Finds given occurrence of the option with the given code (op_code)
|
|
* in "options" field of DHCP-message.
|
|
*
|
|
* @param options "options" field of DHCP-message
|
|
* @param len length of the "options" field
|
|
* @param op_code code of the option to find
|
|
* @param op_offset SUCCESS - offset to an option occurrence;
|
|
* FAULT - offset is set to zero.
|
|
* @return TRUE - option was find;
|
|
* FALSE - option wasn't find.
|
|
*/
|
|
static int8_t dhcp_find_option(uint8_t options[], uint32_t len,
|
|
uint8_t op_code, uint32_t * op_offset)
|
|
{
|
|
uint32_t srch_offset = 0;
|
|
* op_offset = 0;
|
|
|
|
while (srch_offset < len) {
|
|
if (options[srch_offset] == op_code) {
|
|
* op_offset = srch_offset;
|
|
return 1;
|
|
}
|
|
if (options[srch_offset] == DHCP_ENDOPT)
|
|
return 0;
|
|
|
|
if (options[srch_offset] == DHCP_PADOPT)
|
|
srch_offset++;
|
|
else
|
|
srch_offset += 2 + options[srch_offset + 1];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* DHCP: Appends new option from one list (src) into the tail
|
|
* of another option list (dst)
|
|
*
|
|
* @param dst_options "options" field of DHCP-message
|
|
* @param dst_len length of the "options" field (modified)
|
|
* @param new_option points to an option in another list (src)
|
|
*/
|
|
static void dhcp_append_option(uint8_t dst_options[], uint32_t * dst_len,
|
|
uint8_t * new_option)
|
|
{
|
|
memcpy(dst_options + ( * dst_len), new_option, 2 + (* (new_option + 1)));
|
|
* dst_len += 2 + *(new_option + 1);
|
|
}
|
|
|
|
/**
|
|
* DHCP: This function is used when options with the same code are
|
|
* presented in both merged lists. In this case information
|
|
* about the option from one list (src) is combined (complemented)
|
|
* with information about the option in another list (dst).
|
|
*
|
|
* @param dst_options "options" field of DHCP-message
|
|
* @param dst_len length of the "options" field (modified)
|
|
* @param dst_offset offset of the option from beginning of the list
|
|
* @param new_option points to an option in another list (src)
|
|
*/
|
|
static void dhcp_combine_option(uint8_t dst_options[], uint32_t * dst_len,
|
|
uint32_t dst_offset, uint8_t * new_option)
|
|
{
|
|
uint8_t tmp_buffer[1024]; // use to provide safe memcpy
|
|
uint32_t tail_len;
|
|
|
|
// move all subsequent options (allocate size for additional info)
|
|
tail_len = (* dst_len) - dst_offset - 2 - dst_options[dst_offset + 1];
|
|
|
|
memcpy(tmp_buffer, dst_options + (* dst_len) - tail_len, tail_len);
|
|
memcpy(dst_options + (* dst_len) - tail_len + (* (new_option + 1)),
|
|
tmp_buffer, tail_len);
|
|
|
|
// add new_content to option
|
|
memcpy(dst_options + (* dst_len) - tail_len, new_option + 2,
|
|
* (new_option + 1));
|
|
dst_options[dst_offset + 1] += * (new_option + 1);
|
|
|
|
// correct dst_len
|
|
* dst_len += * (new_option + 1);
|
|
}
|
|
|
|
/**
|
|
* DHCP: Sends DHCP-Discover message. Looks for DHCP servers.
|
|
*/
|
|
static void dhcp_send_discover(int fd)
|
|
{
|
|
uint32_t packetsize = sizeof(struct iphdr) +
|
|
sizeof(struct udphdr) + sizeof(struct btphdr);
|
|
struct btphdr *btph;
|
|
dhcp_options_t opt;
|
|
|
|
memset(ether_packet, 0, packetsize);
|
|
|
|
btph = (struct btphdr *) (ðer_packet[
|
|
sizeof(struct iphdr) + sizeof(struct udphdr)]);
|
|
|
|
btph -> op = 1;
|
|
btph -> htype = 1;
|
|
btph -> hlen = 6;
|
|
btph -> xid = dhcp_xid;
|
|
memcpy(btph -> chaddr, get_mac_address(), 6);
|
|
|
|
memset(&opt, 0, sizeof(dhcp_options_t));
|
|
|
|
opt.msg_type = DHCPDISCOVER;
|
|
|
|
opt.request_list[DHCP_MASK] = 1;
|
|
opt.request_list[DHCP_DNS] = 1;
|
|
opt.request_list[DHCP_ROUTER] = 1;
|
|
opt.request_list[DHCP_TFTP_SERVER] = 1;
|
|
opt.request_list[DHCP_BOOTFILE] = 1;
|
|
opt.request_list[DHCP_CLIENT_ARCH] = USE_DHCPARCH;
|
|
|
|
dhcp_encode_options(btph -> vend, &opt);
|
|
|
|
fill_udphdr(ðer_packet[sizeof(struct iphdr)],
|
|
sizeof(struct btphdr) + sizeof(struct udphdr),
|
|
UDPPORT_BOOTPC, UDPPORT_BOOTPS);
|
|
fill_iphdr(ether_packet, sizeof(struct btphdr) +
|
|
sizeof(struct udphdr) + sizeof(struct iphdr),
|
|
IPTYPE_UDP, dhcp_own_ip, 0xFFFFFFFF);
|
|
|
|
send_ipv4(fd, ether_packet, packetsize);
|
|
}
|
|
|
|
/**
|
|
* DHCP: Sends DHCP-Request message. Asks for acknowledgment to occupy IP.
|
|
*/
|
|
static void dhcp_send_request(int fd)
|
|
{
|
|
uint32_t packetsize = sizeof(struct iphdr) +
|
|
sizeof(struct udphdr) + sizeof(struct btphdr);
|
|
struct btphdr *btph;
|
|
dhcp_options_t opt;
|
|
|
|
memset(ether_packet, 0, packetsize);
|
|
|
|
btph = (struct btphdr *) (ðer_packet[
|
|
sizeof(struct iphdr) + sizeof(struct udphdr)]);
|
|
|
|
btph -> op = 1;
|
|
btph -> htype = 1;
|
|
btph -> hlen = 6;
|
|
btph -> xid = dhcp_xid;
|
|
memcpy(btph -> chaddr, get_mac_address(), 6);
|
|
|
|
memset(&opt, 0, sizeof(dhcp_options_t));
|
|
|
|
opt.msg_type = DHCPREQUEST;
|
|
memcpy(&(opt.requested_IP), &dhcp_own_ip, 4);
|
|
opt.flag[DHCP_REQUESTED_IP] = 1;
|
|
memcpy(&(opt.server_ID), &dhcp_server_ip, 4);
|
|
opt.flag[DHCP_SERVER_ID] = 1;
|
|
|
|
opt.request_list[DHCP_MASK] = 1;
|
|
opt.request_list[DHCP_DNS] = 1;
|
|
opt.request_list[DHCP_ROUTER] = 1;
|
|
opt.request_list[DHCP_TFTP_SERVER] = 1;
|
|
opt.request_list[DHCP_BOOTFILE] = 1;
|
|
opt.request_list[DHCP_PXELINUX_CFGFILE] = 1;
|
|
opt.request_list[DHCP_PXELINUX_PREFIX] = 1;
|
|
|
|
opt.request_list[DHCP_CLIENT_ARCH] = USE_DHCPARCH;
|
|
opt.flag[DHCP_CLIENT_ARCH] = USE_DHCPARCH;
|
|
|
|
dhcp_encode_options(btph -> vend, &opt);
|
|
|
|
fill_udphdr(ðer_packet[sizeof(struct iphdr)],
|
|
sizeof(struct btphdr) + sizeof(struct udphdr),
|
|
UDPPORT_BOOTPC, UDPPORT_BOOTPS);
|
|
fill_iphdr(ether_packet, sizeof(struct btphdr) +
|
|
sizeof(struct udphdr) + sizeof(struct iphdr),
|
|
IPTYPE_UDP, 0, 0xFFFFFFFF);
|
|
|
|
send_ipv4(fd, ether_packet, packetsize);
|
|
}
|
|
|
|
|
|
/**
|
|
* DHCP: Sends DHCP-Release message. Releases occupied IP.
|
|
*/
|
|
void dhcp_send_release(int fd)
|
|
{
|
|
uint32_t packetsize = sizeof(struct iphdr) +
|
|
sizeof(struct udphdr) + sizeof(struct btphdr);
|
|
struct btphdr *btph;
|
|
dhcp_options_t opt;
|
|
|
|
btph = (struct btphdr *) (ðer_packet[
|
|
sizeof(struct iphdr) + sizeof(struct udphdr)]);
|
|
|
|
memset(ether_packet, 0, packetsize);
|
|
|
|
btph -> op = 1;
|
|
btph -> htype = 1;
|
|
btph -> hlen = 6;
|
|
btph -> xid = dhcp_xid;
|
|
btph -> file[0] = 0;
|
|
memcpy(btph -> chaddr, get_mac_address(), 6);
|
|
btph -> ciaddr = htonl(dhcp_own_ip);
|
|
|
|
memset(&opt, 0, sizeof(dhcp_options_t));
|
|
|
|
opt.msg_type = DHCPRELEASE;
|
|
opt.server_ID = dhcp_server_ip;
|
|
opt.flag[DHCP_SERVER_ID] = 1;
|
|
|
|
dhcp_encode_options(btph -> vend, &opt);
|
|
|
|
fill_udphdr(ðer_packet[sizeof(struct iphdr)],
|
|
sizeof(struct btphdr) + sizeof(struct udphdr),
|
|
UDPPORT_BOOTPC, UDPPORT_BOOTPS);
|
|
fill_iphdr(ether_packet, sizeof(struct btphdr) +
|
|
sizeof(struct udphdr) + sizeof(struct iphdr), IPTYPE_UDP,
|
|
dhcp_own_ip, dhcp_server_ip);
|
|
|
|
send_ipv4(fd, ether_packet, packetsize);
|
|
}
|
|
|
|
/**
|
|
* DHCP: Handles DHCP-messages according to Receive-handle diagram.
|
|
* Changes the state of DHCP-client.
|
|
*
|
|
* @param fd socket descriptor
|
|
* @param packet BootP/DHCP-packet to be handled
|
|
* @param packetsize length of the packet
|
|
* @return ZERO - packet handled successfully;
|
|
* NON ZERO - packet was not handled (e.g. bad format)
|
|
* @see receive_ether
|
|
* @see btphdr
|
|
*/
|
|
|
|
int8_t handle_dhcp(int fd, uint8_t * packet, int32_t packetsize)
|
|
{
|
|
struct btphdr * btph;
|
|
struct iphdr * iph;
|
|
dhcp_options_t opt;
|
|
|
|
memset(&opt, 0, sizeof(dhcp_options_t));
|
|
btph = (struct btphdr *) packet;
|
|
iph = (struct iphdr *) packet - sizeof(struct udphdr) -
|
|
sizeof(struct iphdr);
|
|
|
|
if (btph->op != 2)
|
|
return -1; /* It is not a Bootp/DHCP reply */
|
|
if (btph->xid != dhcp_xid)
|
|
return -1; /* The transaction ID does not match */
|
|
|
|
if (memcmp(btph -> vend, dhcp_magic, 4)) {
|
|
// It is BootP - RFC 951
|
|
dhcp_own_ip = htonl(btph -> yiaddr);
|
|
dhcp_siaddr_ip = htonl(btph -> siaddr);
|
|
dhcp_server_ip = htonl(iph -> ip_src);
|
|
|
|
if (strlen((char *) btph -> sname) && !dhcp_siaddr_ip) {
|
|
strncpy(dhcp_tftp_name, (char *)btph->sname,
|
|
sizeof(btph->sname));
|
|
dhcp_tftp_name[sizeof(btph -> sname)] = 0;
|
|
}
|
|
|
|
if (strlen((char *) btph -> file)) {
|
|
strncpy(dhcp_filename, (char *)btph->file,
|
|
sizeof(btph->file));
|
|
dhcp_filename[sizeof(btph -> file)] = 0;
|
|
}
|
|
|
|
dhcp_state = DHCP_STATE_SUCCESS;
|
|
return 0;
|
|
}
|
|
|
|
|
|
// decode options
|
|
if (!dhcp_decode_options(btph -> vend, packetsize -
|
|
sizeof(struct btphdr) + sizeof(btph -> vend),
|
|
&opt)) {
|
|
return -1; // can't decode options
|
|
}
|
|
|
|
if (opt.overload) {
|
|
int16_t decode_res = 0;
|
|
uint8_t options[1024]; // buffer for merged options
|
|
uint32_t opt_len;
|
|
|
|
// move 1-st part of options from vend field into buffer
|
|
opt_len = packetsize - sizeof(struct btphdr) +
|
|
sizeof(btph -> vend) - 4;
|
|
memcpy(options, btph -> vend, opt_len + 4);
|
|
|
|
// add other parts
|
|
switch (opt.overload) {
|
|
case DHCP_OVERLOAD_FILE:
|
|
decode_res = dhcp_merge_options(options + 4, &opt_len,
|
|
btph -> file,
|
|
sizeof(btph -> file));
|
|
break;
|
|
case DHCP_OVERLOAD_SNAME:
|
|
decode_res = dhcp_merge_options(options + 4, &opt_len,
|
|
btph -> sname,
|
|
sizeof(btph -> sname));
|
|
break;
|
|
case DHCP_OVERLOAD_BOTH:
|
|
decode_res = dhcp_merge_options(options + 4, &opt_len,
|
|
btph -> file,
|
|
sizeof(btph -> file));
|
|
if (!decode_res)
|
|
break;
|
|
decode_res = dhcp_merge_options(options + 4, &opt_len,
|
|
btph -> sname,
|
|
sizeof(btph -> sname));
|
|
break;
|
|
}
|
|
|
|
if (!decode_res)
|
|
return -1; // bad options in sname/file fields
|
|
|
|
// decode merged options
|
|
if (!dhcp_decode_options(options, opt_len + 4, &opt)) {
|
|
return -1; // can't decode options
|
|
}
|
|
}
|
|
|
|
if (!opt.msg_type) {
|
|
// It is BootP with Extensions - RFC 1497
|
|
// retrieve conf. settings from BootP - reply
|
|
dhcp_own_ip = htonl(btph -> yiaddr);
|
|
dhcp_siaddr_ip = htonl(btph -> siaddr);
|
|
if (strlen((char *) btph -> sname) && !dhcp_siaddr_ip) {
|
|
strncpy(dhcp_tftp_name, (char *)btph->sname,
|
|
sizeof(btph->sname));
|
|
dhcp_tftp_name[sizeof(btph -> sname)] = 0;
|
|
}
|
|
|
|
if (strlen((char *) btph -> file)) {
|
|
strncpy(dhcp_filename, (char *)btph->file,
|
|
sizeof(btph->file));
|
|
dhcp_filename[sizeof(btph -> file)] = 0;
|
|
}
|
|
|
|
// retrieve DHCP-server IP from IP-header
|
|
dhcp_server_ip = iph -> htonl(ip_src);
|
|
|
|
dhcp_state = DHCP_STATE_SUCCESS;
|
|
}
|
|
else {
|
|
// It is DHCP - RFC 2131 & RFC 2132
|
|
// opt contains parameters from server
|
|
switch (dhcp_state) {
|
|
case DHCP_STATE_SELECT :
|
|
if (opt.msg_type == DHCPOFFER) {
|
|
dhcp_own_ip = htonl(btph -> yiaddr);
|
|
dhcp_server_ip = opt.server_ID;
|
|
dhcp_send_request(fd);
|
|
dhcp_state = DHCP_STATE_REQUEST;
|
|
}
|
|
return 0;
|
|
case DHCP_STATE_REQUEST :
|
|
switch (opt.msg_type) {
|
|
case DHCPNACK :
|
|
dhcp_own_ip = 0;
|
|
dhcp_server_ip = 0;
|
|
dhcp_state = DHCP_STATE_FAULT;
|
|
break;
|
|
case DHCPACK :
|
|
dhcp_own_ip = htonl(btph -> yiaddr);
|
|
dhcp_server_ip = opt.server_ID;
|
|
dhcp_siaddr_ip = htonl(btph -> siaddr);
|
|
if (opt.flag[DHCP_TFTP_SERVER]) {
|
|
strcpy(dhcp_tftp_name, opt.tftp_server);
|
|
}
|
|
else {
|
|
dhcp_filename[0] = 0;
|
|
if ((opt.overload != DHCP_OVERLOAD_SNAME &&
|
|
opt.overload != DHCP_OVERLOAD_BOTH) &&
|
|
!dhcp_siaddr_ip) {
|
|
strncpy(dhcp_tftp_name,
|
|
(char *) btph->sname,
|
|
sizeof(btph -> sname));
|
|
dhcp_tftp_name[sizeof(btph->sname)] = 0;
|
|
}
|
|
}
|
|
|
|
if (opt.flag[DHCP_BOOTFILE]) {
|
|
strcpy(dhcp_filename, opt.bootfile);
|
|
}
|
|
else {
|
|
dhcp_filename[0] = 0;
|
|
if (opt.overload != DHCP_OVERLOAD_FILE &&
|
|
opt.overload != DHCP_OVERLOAD_BOTH &&
|
|
strlen((char *)btph->file)) {
|
|
strncpy(dhcp_filename,
|
|
(char *) btph->file,
|
|
sizeof(btph->file));
|
|
dhcp_filename[sizeof(btph -> file)] = 0;
|
|
}
|
|
}
|
|
|
|
dhcp_state = DHCP_STATE_SUCCESS;
|
|
break;
|
|
default:
|
|
break; // Unused DHCP-message - do nothing
|
|
}
|
|
break;
|
|
default :
|
|
return -1; // Illegal DHCP-client state
|
|
}
|
|
}
|
|
|
|
if (dhcp_state == DHCP_STATE_SUCCESS) {
|
|
|
|
// initialize network entity with real own_ip
|
|
// to be able to answer for foreign requests
|
|
set_ipv4_address(dhcp_own_ip);
|
|
|
|
if(response_buffer) {
|
|
if(packetsize <= 1720)
|
|
memcpy(response_buffer, packet, packetsize);
|
|
else
|
|
memcpy(response_buffer, packet, 1720);
|
|
}
|
|
|
|
/* Subnet mask */
|
|
if (opt.flag[DHCP_MASK]) {
|
|
/* Router */
|
|
if (opt.flag[DHCP_ROUTER]) {
|
|
set_ipv4_router(opt.router_IP);
|
|
set_ipv4_netmask(opt.subnet_mask);
|
|
}
|
|
}
|
|
|
|
/* DNS-server */
|
|
if (opt.flag[DHCP_DNS]) {
|
|
dns_init(opt.dns_IP, 0, 4);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|