genode/repos/os/include/net/dhcp.h

584 lines
15 KiB
C
Raw Normal View History

2011-12-22 15:19:25 +00:00
/*
* \brief DHCP related definitions
* \author Stefan Kalkowski
* \date 2010-08-19
*/
/*
* Copyright (C) 2010-2017 Genode Labs GmbH
2011-12-22 15:19:25 +00:00
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
2011-12-22 15:19:25 +00:00
*/
#ifndef _NET__DHCP_H_
#define _NET__DHCP_H_
2011-12-22 15:19:25 +00:00
/* Genode */
#include <base/exception.h>
#include <base/stdint.h>
#include <util/construct_at.h>
2011-12-22 15:19:25 +00:00
#include <util/endian.h>
#include <net/ethernet.h>
#include <net/ipv4.h>
#include <net/udp.h>
namespace Net { class Dhcp_packet; }
2011-12-22 15:19:25 +00:00
/**
* 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 <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)) { }
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 <typename FUNC>
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 <typename FUNC>
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<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>;
using Requested_addr = Ipv4_option<Option::Code::REQ_IP_ADDR>;
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<Genode::uint16_t>
{
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 <typename SIZE_GUARD>
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 <typename OPTION>
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 <typename OPTION, typename... ARGS>
void append_option(ARGS &&... args)
{
_size_guard.consume_head(sizeof(OPTION));
Genode::construct_at<OPTION>((void *)_base,
static_cast<ARGS &&>(args)...);
_base += sizeof(OPTION);
}
template <typename INIT_DATA>
void append_param_req_list(INIT_DATA && init_data)
{
_size_guard.consume_head(sizeof(Parameter_request_list));
Parameter_request_list_data
data((Genode::uint8_t *)(_base + sizeof(Parameter_request_list)),
_size_guard);
init_data(data);
Genode::construct_at<Parameter_request_list>((void *)_base,
data.size());
_base += sizeof(Parameter_request_list) + data.size();
}
template <typename INIT_DATA>
void append_dns_server(INIT_DATA && init_data)
{
Genode::size_t const header_size { sizeof(Dns_server) };
_size_guard.consume_head(header_size);
Dns_server_data data {
(Ipv4_address *)(_base + header_size), _size_guard };
init_data(data);
Genode::construct_at<Dns_server>(
(void *)_base, data.size());
_base += header_size + data.size();
}
void append_domain_name(char const *data_src,
Genode::size_t data_size)
{
Genode::size_t const header_size { sizeof(Domain_name) };
_size_guard.consume_head(header_size + data_size);
Genode::memcpy((char *)(_base + header_size), data_src, data_size);
Genode::construct_at<Domain_name>((void *)_base, data_size);
_base += header_size + data_size;
}
};
/*
* 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) {
Follow practices suggested by "Effective C++" The patch adjust the code of the base, base-<kernel>, and os repository. To adapt existing components to fix violations of the best practices suggested by "Effective C++" as reported by the -Weffc++ compiler argument. The changes follow the patterns outlined below: * A class with virtual functions can no longer publicly inherit base classed without a vtable. The inherited object may either be moved to a member variable, or inherited privately. The latter would be used for classes that inherit 'List::Element' or 'Avl_node'. In order to enable the 'List' and 'Avl_tree' to access the meta data, the 'List' must become a friend. * Instead of adding a virtual destructor to abstract base classes, we inherit the new 'Interface' class, which contains a virtual destructor. This way, single-line abstract base classes can stay as compact as they are now. The 'Interface' utility resides in base/include/util/interface.h. * With the new warnings enabled, all member variables must be explicitly initialized. Basic types may be initialized with '='. All other types are initialized with braces '{ ... }' or as class initializers. If basic types and non-basic types appear in a row, it is nice to only use the brace syntax (also for basic types) and align the braces. * If a class contains pointers as members, it must now also provide a copy constructor and assignment operator. In the most cases, one would make them private, effectively disallowing the objects to be copied. Unfortunately, this warning cannot be fixed be inheriting our existing 'Noncopyable' class (the compiler fails to detect that the inheriting class cannot be copied and still gives the error). For now, we have to manually add declarations for both the copy constructor and assignment operator as private class members. Those declarations should be prepended with a comment like this: /* * Noncopyable */ Thread(Thread const &); Thread &operator = (Thread const &); In the future, we should revisit these places and try to replace the pointers with references. In the presence of at least one reference member, the compiler would no longer implicitly generate a copy constructor. So we could remove the manual declaration. Issue #465
2017-12-21 14:42:15 +00:00
Option &opt = *reinterpret_cast<Option *>(ptr);
if (opt.code() == Option::Code::INVALID ||
opt.code() == Option::Code::END)
{
throw Option_not_found(T::CODE);
}
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 **
*************************/
2011-12-22 15:19:25 +00:00
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)));
}
2011-12-22 15:19:25 +00:00
/*********
** log **
*********/
void print(Genode::Output &output) const;
} __attribute__((packed));
2011-12-22 15:19:25 +00:00
#endif /* _NET__DHCP_H_ */