mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-21 06:33:31 +00:00
ac42ade48c
Remove the use of C++ exception as much as possible from the router as C++ exception handling can be resource intensive and can make code hard to understand. This also removes the garbage collection that the router used to do when a session ran out of quota. This is motivated by the fact that the garbage collection was rather simple and removed connection states regardless of their current state, thereby causing broken connections. The change is part of this commit as the approach to integrating garbage collection relied strongly on exception handling. The user story behind removing garbage collection: The router emergency-dropped an established TCP connection (with NAPT) and on the next matching packet re-created it with a different NAPT port, thereby breaking the connection. With this commit, existing connections are prioritized over new ones during resource exhaustion and the packets that attempt to create a new connection in such a state are dropped with a warning in the log (verbose_packet_drop="yes"). Note that the state resolves itself with time as existing connections time out or are closed by peers. Ref #4729
591 lines
15 KiB
C++
591 lines
15 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 _NET__DHCP_H_
|
|
#define _NET__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
|
|
{
|
|
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, (Genode::uint8_t)len) { }
|
|
};
|
|
|
|
/**
|
|
* Domain name server option
|
|
*/
|
|
struct Dns_server : public Option
|
|
{
|
|
static constexpr Code CODE = Code::DNS_SERVER;
|
|
|
|
Dns_server(Genode::size_t len) : Option(CODE, (Genode::uint8_t)len) { }
|
|
|
|
void for_each_address(auto const &fn) const
|
|
{
|
|
Ipv4_address const *dns_servers = (Ipv4_address const *)((Genode::addr_t)this + sizeof(*this));
|
|
for (unsigned idx = 0; idx < len() / sizeof(*dns_servers); idx++)
|
|
fn(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, (Genode::uint8_t)len) { }
|
|
|
|
void with_string(auto const &fn) const
|
|
{
|
|
fn(_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>
|
|
void append_option(auto &&... args)
|
|
{
|
|
_size_guard.consume_head(sizeof(OPTION));
|
|
Genode::construct_at<OPTION>((void *)_base, 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 'fn' of type 'FUNC' for each option (except END options)
|
|
*/
|
|
void for_each_option(auto const &fn) const
|
|
{
|
|
for (unsigned i = 0; ; ) {
|
|
Option &opt = *(Option*)&_opts[i];
|
|
if (opt.code() == Option::Code::INVALID ||
|
|
opt.code() == Option::Code::END)
|
|
{
|
|
return;
|
|
}
|
|
fn(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 = *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());
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
void with_option(auto const &found_fn, auto const ¬_found_fn) const
|
|
{
|
|
bool found = false;
|
|
for_each_option([&] (Option const &opt) {
|
|
if (found || opt.code() != T::CODE)
|
|
return;
|
|
|
|
found = true;
|
|
found_fn(*(T const *)(&opt));
|
|
});
|
|
if (!found)
|
|
not_found_fn();
|
|
}
|
|
|
|
template <typename T>
|
|
void with_option(auto const &fn) const { with_option<T>(fn, []{}); }
|
|
|
|
|
|
/***************
|
|
** 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)));
|
|
}
|
|
|
|
|
|
/*********
|
|
** log **
|
|
*********/
|
|
|
|
void print(Genode::Output &output) const;
|
|
|
|
} __attribute__((packed));
|
|
|
|
#endif /* _NET__DHCP_H_ */
|