24#ifndef JOIN_FABRIC_DHCP_HPP
25#define JOIN_FABRIC_DHCP_HPP
37#include <unordered_map>
38#include <system_error>
44#include <net/ethernet.h>
45#include <netinet/ip.h>
46#include <netinet/udp.h>
54 template <
class Protocol>
55 class BasicDhcp :
public EventHandler
58 using Socket =
typename Protocol::Socket;
72 :
_buffer (
std::make_unique<char[]> (sizeof (Frame) + Protocol::maxMsgSize))
77 if (::if_nametoindex (
_interface.c_str ()) == 0)
79 throw std::system_error (errno, std::system_category (),
"dhcp interface lookup failed");
84 throw std::system_error (lastError,
"dhcp socket setup failed");
159 uint32_t destination;
177 ssize_t size =
_socket.read (
_buffer.get (), sizeof (Frame) + Protocol::maxMsgSize);
184 if (packet !=
nullptr)
197 static uint16_t
udpChecksum (
const Frame& frame,
const char* payload,
size_t size)
200 pseudo.source = frame.ip.saddr;
201 pseudo.destination = frame.ip.daddr;
202 pseudo.protocol = IPPROTO_UDP;
203 pseudo.length = frame.udp.len;
205 std::vector<uint8_t> scratch (
sizeof (pseudo) +
sizeof (frame.udp) + size, 0);
206 ::memcpy (scratch.data (), &pseudo, sizeof (pseudo));
207 ::memcpy (scratch.data () + sizeof (pseudo), &frame.udp, sizeof (frame.udp));
208 ::memcpy (scratch.data () + sizeof (pseudo) +
sizeof (frame.udp), payload, size);
210 uint16_t sum =
join::checksum (
reinterpret_cast<const uint16_t*
> (scratch.data ()), scratch.size ());
212 return sum ? sum : 0xffff;
224 std::stringstream data;
230 const std::string payload = data.str ();
232 if (payload.size () > Protocol::maxMsgSize)
240 const size_t size =
sizeof (Frame) + payload.size ();
242 std::vector<char> buffer (size, 0);
243 Frame* frame =
reinterpret_cast<Frame*
> (buffer.data ());
244 ::memcpy (buffer.data () + sizeof (Frame), payload.data (), payload.size ());
247 const uint16_t datagram =
static_cast<uint16_t
> (
sizeof (frame->udp) + payload.size ());
249 frame->udp.source = htons (boot ? Protocol::clientPort : Protocol::serverPort);
250 frame->udp.dest = htons (boot ? Protocol::serverPort : Protocol::clientPort);
251 frame->udp.len = htons (datagram);
252 frame->udp.check = 0;
254 frame->ip.version = IPVERSION;
255 frame->ip.ihl =
sizeof (frame->ip) >> 2;
256 frame->ip.tos = IPTOS_CLASS_CS6 | IPTOS_ECN_NOT_ECT;
257 frame->ip.tot_len = htons (
static_cast<uint16_t
> (
sizeof (frame->ip) + datagram));
258 frame->ip.frag_off = htons (IP_DF);
259 frame->ip.ttl = IPDEFTTL;
260 frame->ip.protocol = IPPROTO_UDP;
261 ::memcpy (&frame->ip.saddr, source.
addr (), sizeof (frame->ip.saddr));
262 ::memcpy (&frame->ip.daddr, destination.
addr (), sizeof (frame->ip.daddr));
264 frame->ip.check =
join::checksum (
reinterpret_cast<const uint16_t*
> (&frame->ip), sizeof (frame->ip));
266 frame->udp.check =
udpChecksum (*frame, payload.data (), payload.size ());
268 ::memcpy (frame->eth.h_dest, packet.
dest.
addr (), ETH_ALEN);
269 ::memcpy (frame->eth.h_source, packet.
src.
addr (), ETH_ALEN);
270 frame->eth.h_proto = htons (ETH_P_IP);
272 return (
_socket.write (buffer.data (), size) == -1) ? -1 : 0;
289 ::memcpy (&frame, data,
sizeof (frame));
291 if ((frame.eth.h_proto != htons (ETH_P_IP)) || (frame.ip.version != IPVERSION) ||
292 (frame.ip.ihl != (sizeof (frame.ip) >> 2)) || (frame.ip.protocol != IPPROTO_UDP))
297 struct iphdr header = frame.ip;
298 const uint16_t check = header.check;
301 if (check !=
join::checksum (
reinterpret_cast<const uint16_t*
> (&header),
sizeof (header)))
306 if (((frame.udp.source != htons (Protocol::serverPort)) ||
307 (frame.udp.dest != htons (Protocol::clientPort))) &&
308 ((frame.udp.source != htons (Protocol::clientPort)) ||
309 (frame.udp.dest != htons (Protocol::serverPort))))
314 const size_t datagram = ntohs (frame.udp.len);
315 const size_t available = size -
sizeof (frame.eth) -
sizeof (frame.ip);
317 if ((datagram <
sizeof (frame.udp)) || (datagram > available))
322 const char* payload = data +
sizeof (Frame);
323 const size_t payloadSize = datagram -
sizeof (frame.udp);
330 if (frame.udp.check !=
udpChecksum (probe, payload, payloadSize))
336 std::stringstream stream;
337 stream.rdbuf ()->pubsetbuf (
const_cast<char*
> (payload), payloadSize);
345 packet->src =
MacAddress (frame.eth.h_source, ETH_ALEN);
346 packet->dest =
MacAddress (frame.eth.h_dest, ETH_ALEN);
373 template <
class Protocol>
404 "dhcp maximum message size is too small");
453 std::chrono::milliseconds timeout = std::chrono::seconds (1))
459 auto inserted =
_pending.emplace (
request.id, std::make_unique<PendingRequest> ());
460 if (!inserted.second)
479 return pending->answer != nullptr;
500 if (message !=
nullptr)
509 if (*type != expected)
536 std::chrono::milliseconds timeout = std::chrono::seconds (1))
542 if (!wants.isWildcard ())
558 std::chrono::milliseconds timeout = std::chrono::seconds (1))
577 std::chrono::milliseconds timeout = std::chrono::seconds (1))
618 std::chrono::milliseconds timeout = std::chrono::seconds (1))
632 return transmit (out, client, server);
649 if (!message.empty ())
671 auto it =
_pending.find (packet->id);
674 it->second->answer = std::move (packet);
675 it->second->cond.signal ();
695 std::unordered_map<uint32_t, std::unique_ptr<PendingRequest>>
_pending;
710 template <
class Protocol>
718 template <
class Protocol>
719 class BasicDhcpServer :
public BasicDhcp<Protocol>
781 if (!message.empty ())
877 lastError = std::make_error_code (std::errc::address_not_available);
900 const bool broadcast =
MacAddress get(const IpAddress &ip, std::chrono::duration< Rep, Period > timeout)
get the MAC address for the given IP address using netlink neighbor cache or ARP request.
Definition arp.hpp:102
DHCP client.
Definition dhcp_protocol.hpp:37
int decline(const IpAddress &address, const IpAddress &server, const std::string &message={})
tell the server that the address it offered is already in use.
Definition dhcp.hpp:642
std::string _reason
reason the server gave for the last refusal.
Definition dhcp.hpp:701
DhcpPacket::Ptr renew(const IpAddress &client, const IpAddress &server, std::chrono::milliseconds timeout=std::chrono::seconds(1))
send a REQUEST message to the server holding the lease and wait for an ACK.
Definition dhcp.hpp:576
std::unordered_map< uint32_t, std::unique_ptr< PendingRequest > > _pending
messages waiting for their answer, indexed by transaction identifier.
Definition dhcp.hpp:695
int release(const IpAddress &client, const IpAddress &server, std::chrono::milliseconds timeout=std::chrono::seconds(1))
give a lease back to the server holding it.
Definition dhcp.hpp:617
DhcpPacket::Ptr discover(const IpAddress &wants=IpAddress::ipv4Wildcard, std::chrono::milliseconds timeout=std::chrono::seconds(1))
broadcast a DISCOVER message and wait for an OFFER.
Definition dhcp.hpp:535
const std::string _hostname
host name to advertise.
Definition dhcp.hpp:704
static const ByteList _defaultParams
options a client asks for by default.
Definition dhcp.hpp:692
BasicDhcpClient()=delete
create the BasicDhcpClient instance.
Mutex _syncMutex
mutex for synchronous operations.
Definition dhcp.hpp:698
DhcpPacket::Ptr request(const IpAddress &wants, const IpAddress &server, std::chrono::milliseconds timeout=std::chrono::seconds(1))
broadcast a REQUEST message and wait for an ACK.
Definition dhcp.hpp:557
DhcpPacket::Ptr inform(const IpAddress &client, std::chrono::milliseconds timeout=std::chrono::seconds(1))
ask a server for the parameters of an externally configured address.
Definition dhcp.hpp:600
void onMessage(DhcpPacket::Ptr packet) override final
hand a received message to the request waiting for it.
Definition dhcp.hpp:662
DhcpPacket::Ptr exchange(DhcpPacket &request, const IpAddress &destination, uint8_t expected, std::chrono::milliseconds timeout=std::chrono::seconds(1))
send a message and wait for its answer.
Definition dhcp.hpp:452
DhcpPacket compose(uint8_t type) const
build a message carrying what every message a client sends has in common.
Definition dhcp.hpp:418
std::string reason() const
get the reason the server gave for the last refusal.
Definition dhcp.hpp:522
BasicDhcpClient(const std::string &interface, uint16_t maxSize=0, const std::string &hostname={}, Reactor &reactor=ReactorThread::reactor())
create the instance bound to the given interface.
Definition dhcp.hpp:395
const uint16_t _maxSize
biggest message the server may send back.
Definition dhcp.hpp:707
virtual ~BasicDhcpClient()=default
destroy the instance.
BasicDhcpServer()=delete
create the BasicDhcpServer instance.
virtual void onRelease(const DhcpPacket &request)=0
method called when a RELEASE message is received.
int ack(const DhcpPacket &request, const IpAddress &address, const DhcpOption &options={})
answer a REQUEST message with an ACK.
Definition dhcp.hpp:766
int nak(const DhcpPacket &request, const std::string &message={})
refuse a REQUEST message with a NAK.
Definition dhcp.hpp:777
virtual void onDiscover(const DhcpPacket &request)=0
method called when a DISCOVER message is received.
virtual void onDecline(const DhcpPacket &request)=0
method called when a DECLINE message is received.
virtual ~BasicDhcpServer()=default
destroy the instance.
void onMessage(DhcpPacket::Ptr packet) override final
dispatch a received message to the handler for its type.
Definition dhcp.hpp:824
int offer(const DhcpPacket &request, const IpAddress &address, const DhcpOption &options={})
answer a DISCOVER message with an OFFER.
Definition dhcp.hpp:754
virtual void onInform(const DhcpPacket &request)=0
method called when an INFORM message is received.
virtual void onRequest(const DhcpPacket &request)=0
method called when a REQUEST message is received.
BasicDhcpServer(const std::string &interface, Reactor &reactor=ReactorThread::reactor())
create the instance bound to the given interface.
Definition dhcp.hpp:737
int reply(const DhcpPacket &request, uint8_t type, const IpAddress &address, const DhcpOption &options)
answer a message received from a client.
Definition dhcp.hpp:872
carries DHCP messages over a packet socket.
Definition dhcp_protocol.hpp:34
Reactor & _reactor
event loop reactor.
Definition dhcp.hpp:367
const std::string & interface() const noexcept
get the name of the interface the instance is bound to.
Definition dhcp.hpp:128
virtual ~BasicDhcp()
destroy the instance.
Definition dhcp.hpp:119
BasicDhcp & operator=(const BasicDhcp &other)=delete
assign instance by copy.
static uint16_t udpChecksum(const Frame &frame, const char *payload, size_t size)
compute the checksum of a UDP datagram, RFC 768.
Definition dhcp.hpp:197
int transmit(const DhcpPacket &packet, const IpAddress &source, const IpAddress &destination)
frame a message and write it on the wire.
Definition dhcp.hpp:222
struct __attribute__((packed)) Frame
link, internet and transport headers a DHCP message is framed with.
Definition dhcp.hpp:146
std::unique_ptr< char[]> _buffer
receive buffer.
Definition dhcp.hpp:358
BasicDhcp(BasicDhcp &&other)=delete
create instance by move.
DhcpPacket::Ptr receive(const char *data, size_t size) const
decode a frame received on the wire.
Definition dhcp.hpp:281
virtual void onMessage(DhcpPacket::Ptr packet)=0
method called when a DHCP message is received.
BasicDhcp(const std::string &interface, Reactor &reactor=ReactorThread::reactor())
create the instance bound to the given interface.
Definition dhcp.hpp:71
typename Protocol::Socket Socket
Definition dhcp.hpp:58
const std::string _interface
interface name.
Definition dhcp.hpp:361
BasicDhcp(const BasicDhcp &other)=delete
create instance by copy.
BasicDhcp()=delete
create the BasicDhcp instance.
const MacAddress _hardware
hardware address of the interface.
Definition dhcp.hpp:364
DhcpMessage _message
DHCP message codec.
Definition dhcp.hpp:355
void onReadable(int fd) override final
method called when data are ready to be read.
Definition dhcp.hpp:175
const MacAddress & hardware() const noexcept
get the hardware address of the interface the instance is bound to.
Definition dhcp.hpp:137
Socket _socket
underlying socket.
Definition dhcp.hpp:352
condition variable class.
Definition condition.hpp:42
bool timedWait(Lock &lock, std::chrono::duration< Rep, Period > timeout)
wait on a condition until timeout expire.
Definition condition.hpp:121
DHCP message codec.
Definition dhcp_message.hpp:92
@ BootReply
Definition dhcp_message.hpp:115
@ BootRequest
Definition dhcp_message.hpp:114
@ Request
Definition dhcp_message.hpp:101
@ Nak
Definition dhcp_message.hpp:104
@ Discover
Definition dhcp_message.hpp:99
@ Ack
Definition dhcp_message.hpp:103
@ Decline
Definition dhcp_message.hpp:102
@ Offer
Definition dhcp_message.hpp:100
@ Inform
Definition dhcp_message.hpp:106
@ Release
Definition dhcp_message.hpp:105
int deserialize(DhcpPacket &packet, std::stringstream &data) const
deserialize a DHCP message from a byte stream.
Definition dhcp_message.hpp:199
@ BroadcastFlag
Definition dhcp_message.hpp:123
static constexpr size_t headerSize
size of the fixed part of a DHCP message, magic cookie included.
Definition dhcp_message.hpp:285
int serialize(const DhcpPacket &packet, std::stringstream &data) const
serialize a DHCP message into a byte stream.
Definition dhcp_message.hpp:141
DHCP option list.
Definition dhcp_option.hpp:61
static bool isValid(uint8_t code, T &&value)
check that a value can be carried by the given option.
Definition dhcp_option.hpp:410
@ Router
Definition dhcp_option.hpp:71
@ Message
Definition dhcp_option.hpp:124
@ ServerIdentifier
Definition dhcp_option.hpp:122
@ BroadcastAddress
Definition dhcp_option.hpp:96
@ DomainNameServer
Definition dhcp_option.hpp:74
@ ParameterRequestList
Definition dhcp_option.hpp:123
@ DomainName
Definition dhcp_option.hpp:83
@ SubnetMask
Definition dhcp_option.hpp:69
@ ClientIdentifier
Definition dhcp_option.hpp:129
@ MaximumDhcpMessageSize
Definition dhcp_option.hpp:125
@ RequestedIpAddress
Definition dhcp_option.hpp:118
@ DhcpMessageType
Definition dhcp_option.hpp:121
@ HostName
Definition dhcp_option.hpp:80
@ InterfaceMtu
Definition dhcp_option.hpp:94
const_iterator end() const
get an iterator to the element following the last element of the list.
Definition dhcp_option.hpp:291
bool insert(uint8_t code, T &&value)
add an option to the list, converting the value to the type the option carries.
Definition dhcp_option.hpp:348
const_iterator begin() const
get an iterator to the first element of the list.
Definition dhcp_option.hpp:273
IPv6, IPv4 address class.
Definition ip_address.hpp:51
static const IpAddress ipv4Wildcard
wildcard IPv4 address.
Definition ip_address.hpp:369
static const IpAddress ipv4Broadcast
broadcast IPv4 address.
Definition ip_address.hpp:372
static IpAddress ipv4Address(const std::string &interface)
get the specified interface IPv4 address.
Definition ip_address.cpp:1526
bool isWildcard() const
check if IP address is a wildcard address.
Definition ip_address.cpp:1295
const void * addr() const
get the internal address structure.
Definition ip_address.cpp:1259
MAC address class.
Definition mac_address.hpp:46
static const MacAddress broadcast
broadcast MAC address.
Definition mac_address.hpp:309
bool isWildcard() const
check if MAC address is a wildcard address.
Definition mac_address.cpp:182
const uint8_t * addr() const
get the internal MAC address array address.
Definition mac_address.cpp:164
class used to protect shared data from being simultaneously accessed by multiple threads.
Definition mutex.hpp:37
static Reactor & reactor()
get the global Reactor instance.
Definition reactor.cpp:584
Reactor class.
Definition reactor.hpp:156
int delHandler(int fd, bool sync=true) noexcept
delete handler from reactor.
Definition reactor.cpp:155
int addHandler(int fd, EventHandler *handler, bool wantRead=true, bool wantWrite=false, bool sync=true) noexcept
add handler to reactor.
Definition reactor.cpp:100
class owning a mutex for the duration of a scoped block.
Definition mutex.hpp:246
Definition acceptor.hpp:32
std::enable_if_t< std::numeric_limits< Type >::is_integer, Type > randomize()
create a random number.
Definition utils.hpp:434
uint16_t checksum(const uint16_t *data, size_t len, uint16_t current=0) noexcept
get standard 1s complement checksum.
Definition utils.hpp:357
std::error_code make_error_code(join::Errc code) noexcept
Create an std::error_code object.
Definition error.cpp:195
std::vector< uint8_t > ByteList
list of bytes.
Definition dhcp_option.hpp:45
message waiting for its answer.
Definition dhcp.hpp:683
Condition cond
answer notification.
Definition dhcp.hpp:685
DhcpPacket::Ptr answer
answer received.
Definition dhcp.hpp:688
DHCP message.
Definition dhcp_message.hpp:47
uint16_t flags
message flags.
Definition dhcp_message.hpp:79
MacAddress hardware
client hardware address, carried by the message itself.
Definition dhcp_message.hpp:58
IpAddress client
client IP address, set when the client already owns a lease.
Definition dhcp_message.hpp:61
uint8_t op
operation code.
Definition dhcp_message.hpp:82
MacAddress src
link layer source address, set by the transport.
Definition dhcp_message.hpp:52
std::unique_ptr< DhcpPacket > Ptr
pointer to a DHCP message.
Definition dhcp_message.hpp:49
IpAddress your
IP address the server assigns to the client.
Definition dhcp_message.hpp:64
DhcpOption options
message options.
Definition dhcp_message.hpp:85
MacAddress dest
link layer destination address, set by the transport.
Definition dhcp_message.hpp:55
IpAddress gateway
IP address of the relay agent the message went through.
Definition dhcp_message.hpp:70
IpAddress server
IP address of the next server to use at boot time.
Definition dhcp_message.hpp:67
uint32_t id
transaction identifier.
Definition dhcp_message.hpp:73
IpAddress address
Definition tcp_acceptor_test.cpp:35