mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-11 15:33:04 +00:00
dhcp: extend options utilities
Provide utilities for appending new options to an existing DHCP packet and a utility for finding existing options that returns a typed option object. Remove old version that return untyped options. Ref #2490
This commit is contained in:
parent
791fd9806f
commit
d63c40af3e
@ -17,6 +17,7 @@
|
|||||||
/* Genode */
|
/* Genode */
|
||||||
#include <base/exception.h>
|
#include <base/exception.h>
|
||||||
#include <base/stdint.h>
|
#include <base/stdint.h>
|
||||||
|
#include <util/construct_at.h>
|
||||||
|
|
||||||
#include <util/endian.h>
|
#include <util/endian.h>
|
||||||
#include <net/ethernet.h>
|
#include <net/ethernet.h>
|
||||||
@ -65,7 +66,8 @@ class Net::Dhcp_packet
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
class No_dhcp_packet : Genode::Exception {};
|
struct No_dhcp_packet : Genode::Exception { };
|
||||||
|
struct Option_not_found : Genode::Exception { };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
@ -92,32 +94,6 @@ class Net::Dhcp_packet
|
|||||||
|
|
||||||
enum class Htype : Genode::uint8_t { ETH = 1 };
|
enum class Htype : Genode::uint8_t { ETH = 1 };
|
||||||
|
|
||||||
/**
|
|
||||||
* This class represents the data layout of an DHCP option.
|
|
||||||
*/
|
|
||||||
class Option
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
Genode::uint8_t _code;
|
|
||||||
Genode::uint8_t _len;
|
|
||||||
Genode::uint8_t _value[0];
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Option() {}
|
|
||||||
|
|
||||||
Genode::uint8_t code() { return _code; }
|
|
||||||
Genode::size_t length() { return _len; }
|
|
||||||
void* value() { return _value; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Placement new.
|
|
||||||
*/
|
|
||||||
void * operator new(__SIZE_TYPE__, void* addr) { return addr; }
|
|
||||||
} __attribute__((packed));
|
|
||||||
|
|
||||||
|
|
||||||
enum Opcode {
|
enum Opcode {
|
||||||
REQUEST = 1,
|
REQUEST = 1,
|
||||||
REPLY = 2,
|
REPLY = 2,
|
||||||
@ -129,12 +105,42 @@ class Net::Dhcp_packet
|
|||||||
BOOTPC = 68
|
BOOTPC = 68
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Option_type {
|
|
||||||
|
Dhcp_packet(Genode::size_t size) {
|
||||||
|
/* dhcp packet needs to fit in */
|
||||||
|
if (size < sizeof(Dhcp_packet))
|
||||||
|
throw No_dhcp_packet();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************
|
||||||
|
** Utilities for the options **
|
||||||
|
*******************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Header of a DHCP option or DHCP option without a payload
|
||||||
|
*/
|
||||||
|
class Option
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
Genode::uint8_t _code;
|
||||||
|
Genode::uint8_t _len;
|
||||||
|
Genode::uint8_t _value[0];
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum class Code : Genode::uint8_t {
|
||||||
|
INVALID = 0,
|
||||||
|
SUBNET_MASK = 1,
|
||||||
|
ROUTER = 3,
|
||||||
|
DNS_SERVER = 6,
|
||||||
|
BROADCAST_ADDR = 28,
|
||||||
REQ_IP_ADDR = 50,
|
REQ_IP_ADDR = 50,
|
||||||
IP_LEASE_TIME = 51,
|
IP_LEASE_TIME = 51,
|
||||||
OPT_OVERLOAD = 52,
|
OPT_OVERLOAD = 52,
|
||||||
MSG_TYPE = 53,
|
MSG_TYPE = 53,
|
||||||
SRV_ID = 54,
|
SERVER = 54,
|
||||||
REQ_PARAMETER = 55,
|
REQ_PARAMETER = 55,
|
||||||
MESSAGE = 56,
|
MESSAGE = 56,
|
||||||
MAX_MSG_SZ = 57,
|
MAX_MSG_SZ = 57,
|
||||||
@ -144,29 +150,188 @@ class Net::Dhcp_packet
|
|||||||
CLI_ID = 61,
|
CLI_ID = 61,
|
||||||
TFTP_SRV_NAME = 66,
|
TFTP_SRV_NAME = 66,
|
||||||
BOOT_FILE = 67,
|
BOOT_FILE = 67,
|
||||||
END = 255
|
END = 255,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Message_type {
|
Option(Code code, Genode::uint8_t len)
|
||||||
DHCP_DISCOVER = 1,
|
: _code((Genode::uint8_t)code), _len(len) { }
|
||||||
DHCP_OFFER = 2,
|
|
||||||
DHCP_REQUEST = 3,
|
Option() { }
|
||||||
DHCP_DECLINE = 4,
|
|
||||||
DHCP_ACK = 5,
|
Code code() const { return (Code)_code; }
|
||||||
DHCP_NAK = 6,
|
Genode::uint8_t len() const { return _len; }
|
||||||
DHCP_RELEASE = 7,
|
|
||||||
DHCP_INFORM = 8
|
|
||||||
|
/*********
|
||||||
|
** log **
|
||||||
|
*********/
|
||||||
|
|
||||||
|
void print(Genode::Output &output) const;
|
||||||
|
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DHCP option that contains a payload of type T
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
class Option_tpl : public Option
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
|
||||||
|
T _value;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Option_tpl(Code code, T value)
|
||||||
|
: Option(code, sizeof(T)), _value(value) { }
|
||||||
|
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DHCP option that specifies the IP packet lease time in seconds
|
||||||
|
*/
|
||||||
|
struct Ip_lease_time : Option_tpl<Genode::uint32_t>
|
||||||
|
{
|
||||||
|
static constexpr Code CODE = Code::IP_LEASE_TIME;
|
||||||
|
|
||||||
|
Ip_lease_time(Genode::uint32_t time)
|
||||||
|
: Option_tpl(CODE, host_to_big_endian(time)) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Message_type : Genode::uint8_t {
|
||||||
|
DISCOVER = 1,
|
||||||
|
OFFER = 2,
|
||||||
|
REQUEST = 3,
|
||||||
|
DECLINE = 4,
|
||||||
|
ACK = 5,
|
||||||
|
NAK = 6,
|
||||||
|
RELEASE = 7,
|
||||||
|
INFORM = 8
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DHCP option that specifies the DHCP message type
|
||||||
|
*/
|
||||||
|
struct Message_type_option : Option_tpl<Genode::uint8_t>
|
||||||
|
{
|
||||||
|
static constexpr Code CODE = Code::MSG_TYPE;
|
||||||
|
|
||||||
|
Message_type_option(Message_type value)
|
||||||
|
: Option_tpl(CODE, (Genode::uint8_t)value) { }
|
||||||
|
|
||||||
|
Message_type value() const { return (Message_type)_value; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/*****************
|
/**
|
||||||
** Constructor **
|
* DHCP options that have only one IPv4 address as payload
|
||||||
*****************/
|
*/
|
||||||
|
template <Option::Code _CODE>
|
||||||
|
struct Ipv4_option : Option_tpl<Genode::uint32_t>
|
||||||
|
{
|
||||||
|
static constexpr Code CODE = _CODE;
|
||||||
|
|
||||||
Dhcp_packet(Genode::size_t size) {
|
Ipv4_option(Ipv4_address value)
|
||||||
/* dhcp packet needs to fit in */
|
: Option_tpl(CODE, value.to_uint32_big_endian()) { }
|
||||||
if (size < sizeof(Dhcp_packet))
|
|
||||||
throw No_dhcp_packet();
|
Ipv4_address value() const {
|
||||||
|
return Ipv4_address::from_uint32_big_endian(_value); }
|
||||||
|
};
|
||||||
|
|
||||||
|
using Dns_server_ipv4 = Ipv4_option<Option::Code::DNS_SERVER>;
|
||||||
|
using Subnet_mask = Ipv4_option<Option::Code::SUBNET_MASK>;
|
||||||
|
using Broadcast_addr = Ipv4_option<Option::Code::BROADCAST_ADDR>;
|
||||||
|
using Router_ipv4 = Ipv4_option<Option::Code::ROUTER>;
|
||||||
|
using Server_ipv4 = Ipv4_option<Option::Code::SERVER>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DHCP option that marks the end of an options field
|
||||||
|
*/
|
||||||
|
struct Options_end : Option
|
||||||
|
{
|
||||||
|
static constexpr Code CODE = Code::END;
|
||||||
|
|
||||||
|
Options_end() : Option(CODE, 0) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility to append individual options to an existing DHCP packet
|
||||||
|
*
|
||||||
|
* \param SIZE_GUARD guard that may limit the options list size
|
||||||
|
*
|
||||||
|
* Overwrites existing options if any!
|
||||||
|
*/
|
||||||
|
template <typename SIZE_GUARD>
|
||||||
|
class Options_aggregator
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
Genode::addr_t _base;
|
||||||
|
SIZE_GUARD &_size_guard;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Options_aggregator(Dhcp_packet &packet,
|
||||||
|
SIZE_GUARD &size_guard)
|
||||||
|
:
|
||||||
|
_base((Genode::addr_t)packet.opts()),
|
||||||
|
_size_guard(size_guard)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
template <typename OPTION, typename... ARGS>
|
||||||
|
void append_option(ARGS &&... args)
|
||||||
|
{
|
||||||
|
_size_guard.add(sizeof(OPTION));
|
||||||
|
Genode::construct_at<OPTION>((void *)_base,
|
||||||
|
static_cast<ARGS &&>(args)...);
|
||||||
|
_base += sizeof(OPTION);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Call 'functor' of type 'FUNC' for each option (except END options)
|
||||||
|
*/
|
||||||
|
template <typename FUNC>
|
||||||
|
void for_each_option(FUNC && functor) const
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; ; ) {
|
||||||
|
Option &opt = *(Option*)&_opts[i];
|
||||||
|
if (opt.code() == Option::Code::INVALID ||
|
||||||
|
opt.code() == Option::Code::END)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
functor(opt);
|
||||||
|
i += 2 + opt.len();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find and return option of given type 'T'
|
||||||
|
*
|
||||||
|
* \throw Option_not_found
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
T &option()
|
||||||
|
{
|
||||||
|
void *ptr = &_opts;
|
||||||
|
while (true) {
|
||||||
|
Option &opt = *Genode::construct_at<Option>(ptr);
|
||||||
|
if (opt.code() == Option::Code::INVALID ||
|
||||||
|
opt.code() == Option::Code::END)
|
||||||
|
{
|
||||||
|
throw Option_not_found();
|
||||||
|
}
|
||||||
|
if (opt.code() == T::CODE) {
|
||||||
|
return *reinterpret_cast<T *>(ptr);
|
||||||
|
}
|
||||||
|
ptr = (void *)((Genode::addr_t)ptr + sizeof(opt) + opt.len());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -208,25 +373,6 @@ class Net::Dhcp_packet
|
|||||||
void client_mac(Mac_address v) { v.copy(&_chaddr); }
|
void client_mac(Mac_address v) { v.copy(&_chaddr); }
|
||||||
|
|
||||||
|
|
||||||
/**********************
|
|
||||||
** Option utilities **
|
|
||||||
**********************/
|
|
||||||
|
|
||||||
Option *option(Option_type op)
|
|
||||||
{
|
|
||||||
void *ptr = &_opts;
|
|
||||||
while (true) {
|
|
||||||
Option *ext = new (ptr) Option();
|
|
||||||
if (ext->code() == op)
|
|
||||||
return ext;
|
|
||||||
if (ext->code() == END || ext->code() == 0)
|
|
||||||
break;
|
|
||||||
ptr = ext + ext->length();
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*************************
|
/*************************
|
||||||
** Convenience methods **
|
** Convenience methods **
|
||||||
*************************/
|
*************************/
|
||||||
|
@ -82,14 +82,16 @@ bool Net::Nic::handle_ip(Ethernet_frame *eth, Genode::size_t size) {
|
|||||||
|
|
||||||
/* check for DHCP ACKs containing new client ips */
|
/* check for DHCP ACKs containing new client ips */
|
||||||
if (dhcp->op() == Dhcp_packet::REPLY) {
|
if (dhcp->op() == Dhcp_packet::REPLY) {
|
||||||
Dhcp_packet::Option *ext = dhcp->option(Dhcp_packet::MSG_TYPE);
|
|
||||||
if (ext) {
|
try {
|
||||||
|
Dhcp_packet::Message_type const msg_type =
|
||||||
|
dhcp->option<Dhcp_packet::Message_type_option>().value();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* extract the IP address and set it in the
|
* Extract the IP address and set it in the client's
|
||||||
* client's session-component
|
* session-component
|
||||||
*/
|
*/
|
||||||
Genode::uint8_t *msg_type = (Genode::uint8_t*) ext->value();
|
if (msg_type == Dhcp_packet::Message_type::ACK) {
|
||||||
if (*msg_type == Dhcp_packet::DHCP_ACK) {
|
|
||||||
Mac_address_node *node =
|
Mac_address_node *node =
|
||||||
vlan().mac_tree.first();
|
vlan().mac_tree.first();
|
||||||
if (node)
|
if (node)
|
||||||
@ -98,6 +100,7 @@ bool Net::Nic::handle_ip(Ethernet_frame *eth, Genode::size_t size) {
|
|||||||
node->component().set_ipv4_address(dhcp->yiaddr());
|
node->component().set_ipv4_address(dhcp->yiaddr());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Dhcp_packet::Option_not_found) { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user