162 lines
4.5 KiB
C++
162 lines
4.5 KiB
C++
#include <string.h>
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include "dhcp4.h"
|
|
|
|
namespace dhcp4 {
|
|
|
|
int add_parameter_request_list(message * msg, int index)
|
|
{
|
|
uint8_t * opt = &msg->options[index];
|
|
int length = 3;
|
|
index += 2 + length;
|
|
opt[0] = option::parameter_request_list;
|
|
opt[1] = length;
|
|
opt[2] = option::subnet_mask;
|
|
opt[3] = option::router;
|
|
opt[4] = option::domain_name_server;
|
|
return index;
|
|
}
|
|
|
|
int add_uint32(message * msg, int index, uint8_t option, uint32_t value)
|
|
{
|
|
uint8_t * opt = &msg->options[index];
|
|
int length = 4;
|
|
index += 2 + length;
|
|
opt[0] = option;
|
|
opt[1] = length;
|
|
memcpy(&opt[2], &value, 4);
|
|
return index;
|
|
}
|
|
|
|
int make_message(void * buf,
|
|
uint32_t bootp_message_type,
|
|
uint32_t transaction_id,
|
|
uint8_t const * chaddr,
|
|
uint32_t dhcp_message_type,
|
|
message_parameters const * params)
|
|
{
|
|
memset(buf, 0, (sizeof (message)));
|
|
message * msg = reinterpret_cast<message *>(buf);
|
|
|
|
msg->op = bootp_message_type;
|
|
msg->htype = htype::ethernet;
|
|
msg->hlen = 6;
|
|
msg->xid = htonl(transaction_id);
|
|
|
|
msg->yiaddr = params->your_ip_address;
|
|
msg->siaddr = params->next_server_ip_address;
|
|
|
|
memcpy(msg->chaddr, chaddr, 6);
|
|
|
|
int index = 0;
|
|
|
|
uint8_t * cookie = &msg->options[index];
|
|
index += 4;
|
|
cookie[0] = 0x63;
|
|
cookie[1] = 0x82;
|
|
cookie[2] = 0x53;
|
|
cookie[3] = 0x63;
|
|
|
|
uint8_t * message_type = &msg->options[index];
|
|
index += 2 + 1;
|
|
message_type[0] = option::message_type;
|
|
message_type[1] = 1; // length
|
|
message_type[2] = dhcp_message_type;
|
|
|
|
if (dhcp_message_type == message_type::dhcpdiscover ||
|
|
dhcp_message_type == message_type::dhcprequest) {
|
|
index = add_parameter_request_list(msg, index);
|
|
}
|
|
|
|
if (params->subnet_mask != 0) {
|
|
index = add_uint32(msg, index, option::subnet_mask, params->subnet_mask);
|
|
}
|
|
|
|
if (params->server_identifier != 0) {
|
|
index = add_uint32(msg, index, option::server_identifier, params->server_identifier);
|
|
|
|
index = add_uint32(msg, index, option::ip_address_lease_time, htonl(86400));
|
|
}
|
|
|
|
if (params->router != 0) {
|
|
index = add_uint32(msg, index, option::router, params->router);
|
|
}
|
|
|
|
if (params->domain_name_server != 0) {
|
|
index = add_uint32(msg, index, option::domain_name_server, params->domain_name_server);
|
|
}
|
|
|
|
if (params->requested_ip_address != 0) {
|
|
index = add_uint32(msg, index, option::requested_ip_address, params->requested_ip_address);
|
|
}
|
|
|
|
uint8_t * end = &msg->options[index];
|
|
index += 1;
|
|
end[0] = 255;
|
|
|
|
return (sizeof (message)) + index;
|
|
}
|
|
|
|
char const * message_type_tostr(int message_type)
|
|
{
|
|
switch (message_type) {
|
|
case message_type::dhcpdiscover: return "dhcpdiscover";
|
|
case message_type::dhcpoffer: return "dhcpoffer";
|
|
case message_type::dhcprequest: return "dhcprequest";
|
|
case message_type::dhcpdecline: return "dhcpdecline";
|
|
case message_type::dhcpack: return "dhcpack";
|
|
case message_type::dhcpnak: return "dhcpnak";
|
|
case message_type::dhcprelease: return "dhcprelease";
|
|
case message_type::dhcpinform: return "dhcpinform";
|
|
default:
|
|
assert(false);
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
void parse_options(message const * m, int options_length, options * o)
|
|
{
|
|
uint8_t const * options = m->options;
|
|
|
|
int index = 4;
|
|
while (index + 2 <= options_length) {
|
|
int option = options[index + 0];
|
|
if (option == option::end) {
|
|
break;
|
|
}
|
|
int option_length = options[index + 1];
|
|
|
|
switch (option) {
|
|
case option::message_type:
|
|
o->message_type = options[index + 2];
|
|
break;
|
|
case option::subnet_mask:
|
|
o->subnet_mask = *reinterpret_cast<uint32_t const *>(&options[index + 2]);
|
|
break;
|
|
case option::router:
|
|
o->router = *reinterpret_cast<uint32_t const *>(&options[index + 2]);
|
|
break;
|
|
case option::domain_name_server:
|
|
o->domain_name_server = *reinterpret_cast<uint32_t const *>(&options[index + 2]);
|
|
break;
|
|
case option::requested_ip_address:
|
|
o->requested_ip_address = *reinterpret_cast<uint32_t const *>(&options[index + 2]);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
index += 2 + option_length;
|
|
}
|
|
|
|
assert(options[index] == option::end);
|
|
for (index = index + 1; index < options_length; index++) {
|
|
assert(options[index] == 0);
|
|
}
|
|
}
|
|
}
|