genode/repos/os/include/net/dhcp.h
Martin Stein 30a96706cb nic_router: dhcp server functionality
One can configure the NIC router to act as DHCP server at interfaces of a
domain by adding the <dhcp> tag to the configuration of the domain like
this:

<domain name="vbox" interface="10.0.1.1/24">
    <dhcp-server ip_first="10.0.1.80"
                 ip_last="10.0.1.100"
                 ip_lease_time_sec="3600"
                 dns_server="10.0.0.2"/>
    ...
</domain>

The attributes ip_first and ip_last define the available IPv4 address
range while ip_lease_time_sec defines the lifetime of an IPv4 address
assignment in seconds. The IPv4 address range must be in the subnet
defined by the interface attribute of the domain tag and must not cover
the IPv4 address in this attribute. The dns_server attribute gives the
IPv4 address of the DNS server that might also be in another subnet.
The lifetime of an offered assignment is the configured round trip time of
the router while the ip_lease_time_sec is applied only if the offer is
requested by the client in time.

The ports/run/virtualbox_nic_router.run script is an example of how to
use the new DHCP server functionality.

Ref #2490
2017-10-19 13:31:15 +02:00

416 lines
11 KiB
C++

/*
* \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 _DHCP_H_
#define _DHCP_H_
/* Genode */
#include <base/exception.h>
#include <base/stdint.h>
#include <util/construct_at.h>
#include <util/endian.h>
#include <net/ethernet.h>
#include <net/ipv4.h>
#include <net/udp.h>
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
{
public:
struct No_dhcp_packet : Genode::Exception { };
struct Option_not_found : Genode::Exception { };
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
};
Dhcp_packet(Genode::size_t size) {
/* dhcp packet needs to fit in */
if (size < sizeof(Dhcp_packet))
throw No_dhcp_packet();
}
void default_magic_cookie() {
_magic_cookie = host_to_big_endian(0x63825363);
}
void zero_fill_sname() { Genode::memset(_sname, 0, sizeof(_sname)); }
void zero_fill_file() { Genode::memset(_file, 0, sizeof(_file)); }
/*******************************
** 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,
IP_LEASE_TIME = 51,
OPT_OVERLOAD = 52,
MSG_TYPE = 53,
SERVER = 54,
REQ_PARAMETER = 55,
MESSAGE = 56,
MAX_MSG_SZ = 57,
RENEWAL = 58,
REBINDING = 59,
VENDOR = 60,
CLI_ID = 61,
TFTP_SRV_NAME = 66,
BOOT_FILE = 67,
END = 255,
};
Option(Code code, Genode::uint8_t len)
: _code((Genode::uint8_t)code), _len(len) { }
Option() { }
Code code() const { return (Code)_code; }
Genode::uint8_t len() const { return _len; }
/*********
** 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; }
};
/**
* 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;
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<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());
}
}
/***************
** Accessors **
***************/
Genode::uint8_t op() const { return _op; }
Htype htype() const { return (Htype)_htype; }
Genode::uint8_t hlen() const { return _hlen; }
Genode::uint8_t hops() const { return _hops; }
Genode::uint32_t xid() const { return host_to_big_endian(_xid); }
Genode::uint16_t secs() const { return host_to_big_endian(_secs); }
bool broadcast() const { return _flags & BROADCAST; }
Ipv4_address ciaddr() const { return Ipv4_address((void *)_ciaddr); }
Ipv4_address yiaddr() const { return Ipv4_address((void *)_yiaddr); }
Ipv4_address siaddr() const { return Ipv4_address((void *)_siaddr); }
Ipv4_address giaddr() const { return Ipv4_address((void *)_giaddr); }
Mac_address client_mac() const { return Mac_address((void *)&_chaddr); }
char const *server_name() const { return (char const *)&_sname; }
char const *file() const { return (char const *)&_file; }
Genode::uint32_t magic_cookie() const { return host_to_big_endian(_magic_cookie); }
Genode::uint16_t flags() const { return host_to_big_endian(_flags); }
void *opts() const { return (void *)_opts; }
void flags(Genode::uint16_t v) { _flags = host_to_big_endian(v); }
void file(const char* v) { Genode::memcpy(_file, v, sizeof(_file)); }
void op(Genode::uint8_t v) { _op = v; }
void htype(Htype v) { _htype = (Genode::uint8_t)v; }
void hlen(Genode::uint8_t v) { _hlen = v; }
void hops(Genode::uint8_t v) { _hops = v; }
void xid(Genode::uint32_t v) { _xid = host_to_big_endian(v); }
void secs(Genode::uint16_t v) { _secs = host_to_big_endian(v); }
void broadcast(bool v) { _flags = v ? BROADCAST : 0; }
void ciaddr(Ipv4_address v) { v.copy(&_ciaddr); }
void yiaddr(Ipv4_address v) { v.copy(&_yiaddr); }
void siaddr(Ipv4_address v) { v.copy(&_siaddr); }
void giaddr(Ipv4_address v) { v.copy(&_giaddr); }
void client_mac(Mac_address v) { v.copy(&_chaddr); }
/*************************
** Convenience methods **
*************************/
static bool is_dhcp(Udp_packet const *udp)
{
return ((udp->src_port() == Port(Dhcp_packet::BOOTPC) ||
udp->src_port() == Port(Dhcp_packet::BOOTPS)) &&
(udp->dst_port() == Port(Dhcp_packet::BOOTPC) ||
udp->dst_port() == Port(Dhcp_packet::BOOTPS)));
}
/***************
** Operators **
***************/
/**
* Placement new.
*/
void * operator new(__SIZE_TYPE__ size, void* addr) { return addr; }
/*********
** log **
*********/
void print(Genode::Output &output) const;
} __attribute__((packed));
#endif /* _DHCP_H_ */