/*
* \brief DHCP related definitions
* \author Stefan Kalkowski
* \date 2010-08-19
*/
/*
* Copyright (C) 2010-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _NET__DHCP_H_
#define _NET__DHCP_H_
/* Genode */
#include
#include
#include
#include
#include
#include
#include
namespace Net { class Dhcp_packet; }
/**
* Data layout of this class conforms to an DHCP packet (RFC 2131)
*
* DHCP packet layout:
*
* ===================================
* | 1 byte | 1 byte | 1 byte | 1 byte |
* ===================================
* | op | htype | hlen | hops |
* -----------------------------------
* | connection-id (xid) |
* -----------------------------------
* | seconds | flags |
* -----------------------------------
* | client-ip-address |
* -----------------------------------
* | your-ip-address |
* -----------------------------------
* | server-ip-address |
* -----------------------------------
* | relay-agent-ip-address |
* -----------------------------------
* | client-hw-address |
* | (16 bytes) |
* -----------------------------------
* | sname |
* | (64 bytes) |
* -----------------------------------
* | file |
* | (128 bytes) |
* -----------------------------------
* | options |
* | (312 bytes, optional) |
* -----------------------------------
*/
class Net::Dhcp_packet
{
private:
Genode::uint8_t _op;
Genode::uint8_t _htype;
Genode::uint8_t _hlen;
Genode::uint8_t _hops;
Genode::uint32_t _xid;
Genode::uint16_t _secs;
Genode::uint16_t _flags;
Genode::uint8_t _ciaddr[Ipv4_packet::ADDR_LEN];
Genode::uint8_t _yiaddr[Ipv4_packet::ADDR_LEN];
Genode::uint8_t _siaddr[Ipv4_packet::ADDR_LEN];
Genode::uint8_t _giaddr[Ipv4_packet::ADDR_LEN];
Genode::uint8_t _chaddr[16];
Genode::uint8_t _sname[64];
Genode::uint8_t _file[128];
Genode::uint32_t _magic_cookie;
Genode::uint8_t _opts[0];
enum Flag { BROADCAST = 0x80 };
public:
enum class Htype : Genode::uint8_t { ETH = 1 };
enum Opcode {
REQUEST = 1,
REPLY = 2,
INVALID
};
enum Udp_port {
BOOTPS = 67,
BOOTPC = 68
};
void default_magic_cookie() {
_magic_cookie = host_to_big_endian(0x63825363);
}
/*******************************
** 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,
DOMAIN_NAME = 15,
BROADCAST_ADDR = 28,
REQ_IP_ADDR = 50,
IP_LEASE_TIME = 51,
MSG_TYPE = 53,
SERVER = 54,
PARAM_REQ_LIST = 55,
MAX_MSG_SZ = 57,
CLI_ID = 61,
END = 255,
};
Option(Code code, Genode::uint8_t len)
: _code((Genode::uint8_t)code), _len(len) { }
Code code() const { return (Code)_code; }
Genode::uint8_t len() const { return _len; }
/*********
** log **
*********/
void print(Genode::Output &output) const;
} __attribute__((packed));
struct Option_not_found : Genode::Exception
{
Option::Code const code;
Option_not_found(Option::Code code) : code(code) { }
};
/**
* DHCP option that contains a payload of type T
*/
template
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
{
static constexpr Code CODE = Code::IP_LEASE_TIME;
Ip_lease_time(Genode::uint32_t time)
: Option_tpl(CODE, host_to_big_endian(time)) { }
unsigned long value() const { return host_to_big_endian(_value); }
};
/**
* DHCP option to request specific option type values from the server
*/
struct Parameter_request_list : Option
{
static constexpr Code CODE = Code::PARAM_REQ_LIST;
Parameter_request_list(Genode::size_t len) : Option(CODE, len) { }
};
/**
* Domain name server option
*/
class Dns_server : public Option
{
private:
Ipv4_address _dns_servers[0];
public:
static constexpr Code CODE = Code::DNS_SERVER;
Dns_server(Genode::size_t len) : Option(CODE, len) { }
template
void for_each_address(FUNC && func) const
{
for (unsigned idx = 0;
idx < len() / sizeof(_dns_servers[0]); idx++) {
func(_dns_servers[idx]);
}
}
};
/**
* Domain name option
*/
class Domain_name : public Option
{
private:
char _name[0];
public:
static constexpr Code CODE = Code::DOMAIN_NAME;
Domain_name (Genode::size_t len) : Option(CODE, len) { }
template
void with_string(FUNC && func) const
{
func(_name, Option::len());
}
};
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
{
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; }
};
/**
* DHCP options that have only one IPv4 address as payload
*/
template
struct Ipv4_option : Option_tpl
{
static constexpr Code CODE = _CODE;
Ipv4_option(Ipv4_address value)
: Option_tpl(CODE, value.to_uint32_big_endian()) { }
Ipv4_address value() const {
return Ipv4_address::from_uint32_big_endian(_value); }
};
using Dns_server_ipv4 = Ipv4_option;
using Subnet_mask = Ipv4_option;
using Broadcast_addr = Ipv4_option;
using Router_ipv4 = Ipv4_option;
using Server_ipv4 = Ipv4_option;
using Requested_addr = Ipv4_option;
class Client_id
{
private:
Genode::uint8_t _code;
Genode::uint8_t _len;
Genode::uint8_t _value[7];
public:
Client_id(Mac_address value)
: _code((Genode::uint8_t)Option::Code::CLI_ID), _len(7)
{
_value[0] = 1;
_value[1] = value.addr[0];
_value[2] = value.addr[1];
_value[3] = value.addr[2];
_value[4] = value.addr[3];
_value[5] = value.addr[4];
_value[6] = value.addr[5];
}
Genode::uint8_t code() const { return _code; }
Genode::uint8_t len() const { return _len; }
} __attribute__((packed));
struct Max_msg_size : Option_tpl
{
static constexpr Code CODE = Code::MAX_MSG_SZ;
Max_msg_size(Genode::uint16_t size)
: Option_tpl(CODE, host_to_big_endian(size)) { }
};
/**
* 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
class Options_aggregator
{
private:
Genode::addr_t _base;
SIZE_GUARD &_size_guard;
public:
class Parameter_request_list_data
{
private:
Genode::uint8_t *const _base;
Genode::size_t _size { 0 };
SIZE_GUARD &_size_guard;
public:
Parameter_request_list_data(Genode::uint8_t *base,
SIZE_GUARD &size_guard)
:
_base { base },
_size_guard { size_guard }
{ }
template
void append_param_req()
{
_size_guard.consume_head(sizeof(_base[0]));
_base[_size] = (Genode::uint8_t)OPTION::CODE;
_size++;
}
Genode::size_t size() const { return _size; }
};
class Dns_server_data
{
private:
Ipv4_address *const _addr_array;
Genode::size_t _addr_idx { 0 };
SIZE_GUARD &_pkt_size_guard;
public:
Dns_server_data(Ipv4_address *addr_array,
SIZE_GUARD &pkt_size_guard)
:
_addr_array { addr_array },
_pkt_size_guard { pkt_size_guard }
{ }
void append_address(Ipv4_address const &addr)
{
_pkt_size_guard.consume_head(sizeof(_addr_array[0]));
_addr_array[_addr_idx] = addr;
_addr_idx++;
}
Genode::size_t size() const
{
return _addr_idx * sizeof(Ipv4_address);
}
};
Options_aggregator(Dhcp_packet &packet,
SIZE_GUARD &size_guard)
:
_base((Genode::addr_t)packet.opts()),
_size_guard(size_guard)
{ }
template
void append_option(ARGS &&... args)
{
_size_guard.consume_head(sizeof(OPTION));
Genode::construct_at