join 1.0
lightweight network framework library
Loading...
Searching...
No Matches
dhcp.hpp
Go to the documentation of this file.
1
24#ifndef JOIN_FABRIC_DHCP_HPP
25#define JOIN_FABRIC_DHCP_HPP
26
27// libjoin.
29#include <join/dhcp_message.hpp>
30#include <join/condition.hpp>
31#include <join/function.hpp>
32#include <join/reactor.hpp>
33#include <join/utils.hpp>
34#include <join/error.hpp>
35#include <join/arp.hpp>
36
37// C++.
38#include <unordered_map>
39#include <system_error>
40#include <chrono>
41#include <memory>
42#include <string>
43
44// C.
45#include <net/ethernet.h>
46#include <netinet/udp.h>
47#include <netinet/ip.h>
48#include <sys/uio.h>
49#include <net/if.h>
50
51namespace join
52{
56 template <class Protocol>
57 class BasicDhcp : public EventHandler
58 {
59 public:
60 using Socket = typename Protocol::Socket;
61
65 BasicDhcp () = delete;
66
73 explicit BasicDhcp (const std::string& interface, Reactor& reactor = ReactorThread::reactor ())
74 : _buffer (std::make_unique<char[]> (Protocol::maxMsgSize))
77 , _reactor (reactor)
78 {
79 if (::if_nametoindex (_interface.c_str ()) == 0)
80 {
81 throw std::system_error (errno, std::system_category (), "dhcp interface lookup failed");
82 }
83
84 if (_socket.bind (_interface) == -1 || _socket.setOption (Socket::Broadcast, 1) == -1)
85 {
86 throw std::system_error (lastError, "dhcp socket setup failed"); // LCOV_EXCL_LINE
87 }
88 }
89
94 BasicDhcp (const BasicDhcp& other) = delete;
95
101 BasicDhcp& operator= (const BasicDhcp& other) = delete;
102
107 BasicDhcp (BasicDhcp&& other) = delete;
108
114 BasicDhcp& operator= (BasicDhcp&& other) = delete;
115
119 virtual ~BasicDhcp () = default;
120
125 const std::string& interface () const noexcept
126 {
127 return _interface;
128 }
129
134 const MacAddress& hardware () const noexcept
135 {
136 return _hardware;
137 }
138
139 protected:
143 struct __attribute__ ((packed, aligned (2))) Frame
144 {
145 struct ethhdr eth;
146 struct iphdr ip;
147 struct udphdr udp;
148 };
149
153 struct __attribute__ ((packed, aligned (2))) Pseudo
154 {
155 uint32_t source;
156 uint32_t destination;
157 uint8_t zero;
158 uint8_t protocol;
159 uint16_t length;
160 };
161
166 void attach ()
167 {
168 if (_reactor.addHandler (_socket.handle (), this) == -1)
169 {
170 throw std::system_error (lastError, "dhcp reactor registration failed"); // LCOV_EXCL_LINE
171 }
172 }
173
177 void detach () noexcept
178 {
179 _reactor.delHandler (_socket.handle ());
180 }
181
189 static uint16_t udpChecksum (const Frame& frame, const char* payload, size_t size) noexcept
190 {
191 Pseudo pseudo = {};
192 pseudo.source = frame.ip.saddr;
193 pseudo.destination = frame.ip.daddr;
194 pseudo.protocol = IPPROTO_UDP;
195 pseudo.length = frame.udp.len;
196
197 uint16_t sum = join::checksum (reinterpret_cast<const uint16_t*> (&pseudo), sizeof (pseudo));
198 sum = join::checksum (reinterpret_cast<const uint16_t*> (&frame.udp), sizeof (frame.udp),
199 static_cast<uint16_t> (~sum));
200 sum = join::checksum (reinterpret_cast<const uint16_t*> (payload), size, static_cast<uint16_t> (~sum));
201
202 return sum ? sum : 0xffff;
203 }
204
214 int send (const char* data, size_t size, const MacAddress& to, const IpAddress& source,
215 const IpAddress& destination) noexcept
216 {
217 if (size > Protocol::maxMsgSize)
218 {
219 // LCOV_EXCL_START
221 return -1;
222 // LCOV_EXCL_STOP
223 }
224
225 Frame frame = {};
226
227 const bool boot = (size > 0) && (static_cast<uint8_t> (data[0]) == DhcpMessage::BootRequest);
228 const uint16_t datagram = static_cast<uint16_t> (sizeof (frame.udp) + size);
229
230 frame.udp.source = htons (boot ? Protocol::clientPort : Protocol::serverPort);
231 frame.udp.dest = htons (boot ? Protocol::serverPort : Protocol::clientPort);
232 frame.udp.len = htons (datagram);
233
234 frame.ip.version = IPVERSION;
235 frame.ip.ihl = sizeof (frame.ip) >> 2;
236 frame.ip.tos = IPTOS_CLASS_CS6 | IPTOS_ECN_NOT_ECT;
237 frame.ip.tot_len = htons (static_cast<uint16_t> (sizeof (frame.ip) + datagram));
238 frame.ip.frag_off = htons (IP_DF);
239 frame.ip.ttl = IPDEFTTL;
240 frame.ip.protocol = IPPROTO_UDP;
241 ::memcpy (&frame.ip.saddr, source.addr (), sizeof (frame.ip.saddr));
242 ::memcpy (&frame.ip.daddr, destination.addr (), sizeof (frame.ip.daddr));
243 frame.ip.check = join::checksum (reinterpret_cast<const uint16_t*> (&frame.ip), sizeof (frame.ip));
244
245 frame.udp.check = udpChecksum (frame, data, size);
246
247 ::memcpy (frame.eth.h_dest, to.addr (), ETH_ALEN);
248 ::memcpy (frame.eth.h_source, _hardware.addr (), ETH_ALEN);
249 frame.eth.h_proto = htons (ETH_P_IP);
250
251 struct iovec iov[2];
252 iov[0].iov_base = &frame;
253 iov[0].iov_len = sizeof (frame);
254 iov[1].iov_base = const_cast<char*> (data);
255 iov[1].iov_len = size;
256
257 struct msghdr message = {};
258 message.msg_iov = iov;
259 message.msg_iovlen = 2;
260
261 if (::sendmsg (_socket.handle (), &message, 0) == -1)
262 {
263 // LCOV_EXCL_START
264 lastError = std::error_code (errno, std::generic_category ());
265 return -1;
266 // LCOV_EXCL_STOP
267 }
268
269 return 0;
270 }
271
280 ssize_t receive (char* data, size_t maxSize, MacAddress& from, MacAddress& to) noexcept
281 {
282 Frame frame;
283
284 struct iovec iov[2];
285 iov[0].iov_base = &frame;
286 iov[0].iov_len = sizeof (frame);
287 iov[1].iov_base = data;
288 iov[1].iov_len = maxSize;
289
290 struct msghdr message = {};
291 message.msg_iov = iov;
292 message.msg_iovlen = 2;
293
294 ssize_t size = ::recvmsg (_socket.handle (), &message, 0);
295 if (size == -1)
296 {
297 // LCOV_EXCL_START
298 lastError = std::error_code (errno, std::generic_category ());
299 return -1;
300 // LCOV_EXCL_STOP
301 }
302
303 if (message.msg_flags & MSG_TRUNC)
304 {
305 // LCOV_EXCL_START
307 return -1;
308 // LCOV_EXCL_STOP
309 }
310
311 if (size < static_cast<ssize_t> (sizeof (Frame) + DhcpMessage::headerSize))
312 {
313 return -1;
314 }
315
316 if ((frame.eth.h_proto != htons (ETH_P_IP)) || (frame.ip.version != IPVERSION) ||
317 (frame.ip.ihl != (sizeof (frame.ip) >> 2)) || (frame.ip.protocol != IPPROTO_UDP))
318 {
319 return -1;
320 }
321
322 struct iphdr header = frame.ip;
323 const uint16_t check = header.check;
324 header.check = 0;
325
326 if (check != join::checksum (reinterpret_cast<const uint16_t*> (&header), sizeof (header)))
327 {
328 return -1;
329 }
330
331 if (((frame.udp.source != htons (Protocol::serverPort)) ||
332 (frame.udp.dest != htons (Protocol::clientPort))) &&
333 ((frame.udp.source != htons (Protocol::clientPort)) ||
334 (frame.udp.dest != htons (Protocol::serverPort))))
335 {
336 return -1;
337 }
338
339 const size_t datagram = ntohs (frame.udp.len);
340 const size_t available = static_cast<size_t> (size) - sizeof (frame.eth) - sizeof (frame.ip);
341
342 if ((datagram < sizeof (frame.udp)) || (datagram > available))
343 {
344 return -1;
345 }
346
347 const size_t payloadSize = datagram - sizeof (frame.udp);
348
349 if (frame.udp.check)
350 {
351 Frame probe = frame;
352 probe.udp.check = 0;
353
354 if (frame.udp.check != udpChecksum (probe, data, payloadSize))
355 {
356 return -1;
357 }
358 }
359
360 from = MacAddress (frame.eth.h_source, ETH_ALEN);
361 to = MacAddress (frame.eth.h_dest, ETH_ALEN);
362
363 return static_cast<ssize_t> (payloadSize);
364 }
365
368
371
373 std::unique_ptr<char[]> _buffer;
374
376 const std::string _interface;
377
380
383 };
384
388 template <class Protocol>
389 class BasicDhcpClient final : protected BasicDhcp<Protocol>
390 {
391 public:
392 using BasicDhcp<Protocol>::hardware;
393 using BasicDhcp<Protocol>::interface;
394
398 BasicDhcpClient () = delete;
399
409 explicit BasicDhcpClient (const std::string& interface, uint16_t maxSize = 0, const std::string& hostname = {},
410 Reactor& reactor = ReactorThread::reactor ())
411 : BasicDhcp<Protocol> (interface, reactor)
412 , _hostname (hostname)
413 , _maxSize (maxSize)
414 {
416 {
417 throw std::system_error (make_error_code (Errc::InvalidParam),
418 "dhcp maximum message size is too small");
419 }
420
421 this->attach ();
422 }
423
428 {
429 this->detach ();
430 }
431
437 DhcpPacket compose (uint8_t type) const
438 {
439 DhcpPacket packet;
440
442 packet.id = randomize<uint32_t> ();
443 packet.hardware = hardware ();
445
448
449 if (!_hostname.empty ())
450 {
451 packet.options.insert (DhcpOption::HostName, _hostname);
452 }
453
454 if (_maxSize)
455 {
457 }
458
459 return packet;
460 }
461
471 int exchange (DhcpPacket& request, DhcpPacket& answer, const IpAddress& destination, uint8_t expected,
472 std::chrono::milliseconds timeout = std::chrono::seconds (1)) noexcept
473 {
474 if (this->_reactor.isReactorThread ())
475 {
476 lastError = std::make_error_code (std::errc::resource_deadlock_would_occur);
477 return -1;
478 }
479
480 char payload[Protocol::maxMsgSize];
481 ssize_t size = this->_message.serialize (request, payload, sizeof (payload));
482 if (size == -1)
483 {
484 return -1; // LCOV_EXCL_LINE
485 }
486
487 ScopedLock<Mutex> lock (_syncMutex);
488
489 _reason.clear ();
490
491 PendingRequest pending;
492 pending.answer = &answer;
493
494 if (!_pending.emplace (request.id, &pending).second)
495 {
496 // LCOV_EXCL_START
497 lastError = make_error_code (Errc::InUse);
498 return -1;
499 // LCOV_EXCL_STOP
500 }
501
502 if (this->send (payload, static_cast<size_t> (size), request.dest, request.client, destination) == -1)
503 {
504 // LCOV_EXCL_START
505 _pending.erase (request.id);
506 return -1;
507 // LCOV_EXCL_STOP
508 }
509
510 bool answered = pending.cond.timedWait (lock, timeout, [&pending] {
511 return pending.done;
512 });
513
514 _pending.erase (request.id);
515
516 if (!answered)
517 {
518 lastError = make_error_code (Errc::TimedOut);
519 return -1;
520 }
521
522 const uint8_t* type = answer.options.getIf<uint8_t> (DhcpOption::DhcpMessageType);
523 if (type == nullptr)
524 {
526 return -1;
527 }
528
529 if (*type == DhcpMessage::Nak)
530 {
531 const std::string* message = answer.options.getIf<std::string> (DhcpOption::Message);
532 if (message != nullptr)
533 {
534 _reason = *message;
535 }
536
538 return -1;
539 }
540
541 if (*type != expected)
542 {
544 return -1;
545 }
546
547 return 0;
548 }
549
554 std::string reason () const
555 {
556 ScopedLock<Mutex> lock (_syncMutex);
557
558 return _reason;
559 }
560
569 std::chrono::milliseconds timeout = std::chrono::seconds (1)) noexcept
570 {
572
573 out.options.insert (DhcpOption::ParameterRequestList, _defaultParams);
574
575 if (!wants.isWildcard ())
576 {
578 }
579
580 return exchange (out, offer, IpAddress::ipv4Broadcast, DhcpMessage::Offer, timeout);
581 }
582
591 int request (DhcpPacket& ack, const IpAddress& wants, const IpAddress& server,
592 std::chrono::milliseconds timeout = std::chrono::seconds (1)) noexcept
593 {
595
596 out.options.insert (DhcpOption::ParameterRequestList, _defaultParams);
599
600 return exchange (out, ack, IpAddress::ipv4Broadcast, DhcpMessage::Ack, timeout);
601 }
602
611 int renew (DhcpPacket& ack, const IpAddress& client, const IpAddress& server,
612 std::chrono::milliseconds timeout = std::chrono::seconds (1)) noexcept
613 {
614 if (this->_reactor.isReactorThread ())
615 {
616 lastError = std::make_error_code (std::errc::resource_deadlock_would_occur);
617 return -1;
618 }
619
620 MacAddress mac = Arp::get (interface (), server, timeout);
621 if (mac.isWildcard ())
622 {
623 return -1;
624 }
625
627
628 out.dest = mac;
629 out.client = client;
630 out.options.insert (DhcpOption::ParameterRequestList, _defaultParams);
631
632 return exchange (out, ack, server, DhcpMessage::Ack, timeout);
633 }
634
642 int inform (DhcpPacket& ack, const IpAddress& client,
643 std::chrono::milliseconds timeout = std::chrono::seconds (1)) noexcept
644 {
646
647 out.client = client;
648 out.options.insert (DhcpOption::ParameterRequestList, _defaultParams);
649
650 return exchange (out, ack, IpAddress::ipv4Broadcast, DhcpMessage::Ack, timeout);
651 }
652
660 int release (const IpAddress& client, const IpAddress& server,
661 std::chrono::milliseconds timeout = std::chrono::seconds (1)) noexcept
662 {
663 if (this->_reactor.isReactorThread ())
664 {
665 lastError = std::make_error_code (std::errc::resource_deadlock_would_occur);
666 return -1;
667 }
668
669 MacAddress mac = Arp::get (interface (), server, timeout);
670 if (mac.isWildcard ())
671 {
672 return -1;
673 }
674
676
677 out.dest = mac;
678 out.client = client;
680
681 char payload[Protocol::maxMsgSize];
682 ssize_t size = this->_message.serialize (out, payload, sizeof (payload));
683 if (size == -1)
684 {
685 return -1; // LCOV_EXCL_LINE
686 }
687
688 return this->send (payload, static_cast<size_t> (size), out.dest, client, server);
689 }
690
698 int decline (const IpAddress& address, const IpAddress& server, const std::string& message = {}) noexcept
699 {
701
704
705 if (!message.empty ())
706 {
707 out.options.insert (DhcpOption::Message, message);
708 }
709
710 char payload[Protocol::maxMsgSize];
711 ssize_t size = this->_message.serialize (out, payload, sizeof (payload));
712 if (size == -1)
713 {
714 return -1; // LCOV_EXCL_LINE
715 }
716
717 return this->send (payload, static_cast<size_t> (size), out.dest, IpAddress::ipv4Wildcard,
719 }
720
721 private:
726 void onReadable ([[maybe_unused]] int fd) override final
727 {
728 MacAddress from, to;
729 ssize_t size = this->receive (this->_buffer.get (), Protocol::maxMsgSize, from, to);
730 if (size == -1)
731 {
732 return;
733 }
734
735 DhcpPacket packet;
736 if ((this->_message.deserialize (packet, this->_buffer.get (), static_cast<size_t> (size)) == -1) ||
737 (packet.op != DhcpMessage::BootReply))
738 {
739 return;
740 }
741
742 packet.src = from;
743 packet.dest = to;
744
745 ScopedLock<Mutex> lock (_syncMutex);
746
747 auto it = _pending.find (packet.id);
748 if ((it != _pending.end ()) && !it->second->done)
749 {
750 *it->second->answer = std::move (packet);
751 it->second->done = true;
752 it->second->cond.signal ();
753 }
754 }
755
759 struct PendingRequest
760 {
762 Condition cond;
763
765 DhcpPacket* answer = nullptr;
766
768 bool done = false;
769 };
770
772 static const ByteList _defaultParams;
773
775 std::unordered_map<uint32_t, PendingRequest*> _pending;
776
778 mutable Mutex _syncMutex;
779
781 std::string _reason;
782
784 const std::string _hostname;
785
787 const uint16_t _maxSize;
788 };
789
790 template <class Protocol>
791 const ByteList BasicDhcpClient<Protocol>::_defaultParams = {DhcpOption::SubnetMask, DhcpOption::Router,
794
798 template <class Protocol>
799 class BasicDhcpServer final : protected BasicDhcp<Protocol>
800 {
801 public:
802 using BasicDhcp<Protocol>::hardware;
803 using BasicDhcp<Protocol>::interface;
804
806 using RequestNotify = Function<void (DhcpMessage::MessageType type, const DhcpPacket& request)>;
807
811 BasicDhcpServer () = delete;
812
819 explicit BasicDhcpServer (const std::string& interface, Reactor& reactor = ReactorThread::reactor ())
820 : BasicDhcp<Protocol> (interface, reactor)
821 {
822 this->attach ();
823 }
824
829 {
830 this->detach ();
831 }
832
840 int offer (const DhcpPacket& request, const IpAddress& address, const DhcpOption& options = {}) noexcept
841 {
842 return reply (request, DhcpMessage::Offer, address, options);
843 }
844
852 int ack (const DhcpPacket& request, const IpAddress& address, const DhcpOption& options = {}) noexcept
853 {
854 return reply (request, DhcpMessage::Ack, address, options);
855 }
856
863 int nak (const DhcpPacket& request, const std::string& message = {}) noexcept
864 {
865 DhcpOption options;
866
867 if (!message.empty ())
868 {
869 options.insert (DhcpOption::Message, message);
870 }
871
872 return reply (request, DhcpMessage::Nak, IpAddress::ipv4Wildcard, options);
873 }
874
881 {
882 bool busy = false;
883
884 Reactor::InvokeHandler fn = [this, &cb, &busy] () {
885 busy = _calling || _listener;
886 if (!busy)
887 {
888 _listener = std::move (cb);
889 }
890 };
891
892 if (this->_reactor.invoke (&fn) == -1)
893 {
894 return -1; // LCOV_EXCL_LINE
895 }
896
897 if (busy)
898 {
899 lastError = make_error_code (Errc::InUse);
900 return -1;
901 }
902
903 return 0;
904 }
905
910 int unsetRequestListener () noexcept
911 {
912 Reactor::InvokeHandler fn = [this] () {
913 _listener = nullptr;
914 _calling = false;
915 };
916
917 return this->_reactor.invoke (&fn);
918 }
919
920 private:
925 void onReadable ([[maybe_unused]] int fd) override final
926 {
927 MacAddress from, to;
928 ssize_t size = this->receive (this->_buffer.get (), Protocol::maxMsgSize, from, to);
929 if (size == -1)
930 {
931 return;
932 }
933
934 DhcpPacket packet;
935 if ((this->_message.deserialize (packet, this->_buffer.get (), static_cast<size_t> (size)) == -1) ||
936 (packet.op != DhcpMessage::BootRequest))
937 {
938 return;
939 }
940
941 packet.src = from;
942 packet.dest = to;
943
944 const uint8_t* type = packet.options.getIf<uint8_t> (DhcpOption::DhcpMessageType);
945 if (type == nullptr)
946 {
947 return;
948 }
949
950 if (_listener)
951 {
952 RequestNotify listener = std::move (_listener);
953
954 _calling = true;
955 listener (static_cast<DhcpMessage::MessageType> (*type), packet);
956
957 if (_calling)
958 {
959 _listener = std::move (listener);
960 _calling = false;
961 }
962 }
963 }
964
973 int reply (const DhcpPacket& request, uint8_t type, const IpAddress& address,
974 const DhcpOption& options) noexcept
975 {
976 const IpAddress server = IpAddress::ipv4Address (interface ());
977 if (server.isWildcard ())
978 {
979 lastError = std::make_error_code (std::errc::address_not_available);
980 return -1;
981 }
982
983 DhcpPacket out;
985 out.id = request.id;
986 out.flags = request.flags;
987 out.hardware = request.hardware;
988
989 if (type == DhcpMessage::Ack)
990 {
991 out.client = request.client;
992 }
993 out.your = address;
994 out.server = server;
995 out.gateway = request.gateway;
996
997 out.options.insert (DhcpOption::DhcpMessageType, type);
998 out.options.insert (DhcpOption::ServerIdentifier, server);
999 out.options.insert (options.begin (), options.end ());
1000
1001 const bool broadcast =
1002 (type == DhcpMessage::Nak) || (request.client.isWildcard () &&
1003 ((request.flags & DhcpMessage::BroadcastFlag) || address.isWildcard ()));
1004
1005 out.dest = broadcast ? MacAddress::broadcast : request.hardware;
1006
1007 const IpAddress& unicast = request.client.isWildcard () ? address : request.client;
1008
1009 char payload[Protocol::maxMsgSize];
1010 ssize_t size = this->_message.serialize (out, payload, sizeof (payload));
1011 if (size == -1)
1012 {
1013 return -1; // LCOV_EXCL_LINE
1014 }
1015
1016 return this->send (payload, static_cast<size_t> (size), out.dest, server,
1017 broadcast ? IpAddress::ipv4Broadcast : unicast);
1018 }
1019
1021 RequestNotify _listener;
1022
1024 bool _calling = false;
1025 };
1026}
1027
1028#endif
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 exchange(DhcpPacket &request, DhcpPacket &answer, const IpAddress &destination, uint8_t expected, std::chrono::milliseconds timeout=std::chrono::seconds(1)) noexcept
send a message and wait for its answer.
Definition dhcp.hpp:471
int inform(DhcpPacket &ack, const IpAddress &client, std::chrono::milliseconds timeout=std::chrono::seconds(1)) noexcept
ask a server for the parameters of an externally configured address.
Definition dhcp.hpp:642
int release(const IpAddress &client, const IpAddress &server, std::chrono::milliseconds timeout=std::chrono::seconds(1)) noexcept
give a lease back to the server holding it.
Definition dhcp.hpp:660
~BasicDhcpClient()
stop receiving messages and destroy the instance.
Definition dhcp.hpp:427
int decline(const IpAddress &address, const IpAddress &server, const std::string &message={}) noexcept
tell the server that the address it offered is already in use.
Definition dhcp.hpp:698
BasicDhcpClient()=delete
create the BasicDhcpClient instance.
int discover(DhcpPacket &offer, const IpAddress &wants=IpAddress::ipv4Wildcard, std::chrono::milliseconds timeout=std::chrono::seconds(1)) noexcept
broadcast a DISCOVER message and wait for an OFFER.
Definition dhcp.hpp:568
DhcpPacket compose(uint8_t type) const
build a message carrying what every message a client sends has in common.
Definition dhcp.hpp:437
int request(DhcpPacket &ack, const IpAddress &wants, const IpAddress &server, std::chrono::milliseconds timeout=std::chrono::seconds(1)) noexcept
broadcast a REQUEST message and wait for an ACK.
Definition dhcp.hpp:591
std::string reason() const
get the reason the server gave for the last refusal.
Definition dhcp.hpp:554
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:409
int renew(DhcpPacket &ack, const IpAddress &client, const IpAddress &server, std::chrono::milliseconds timeout=std::chrono::seconds(1)) noexcept
send a REQUEST message to the server holding the lease and wait for an ACK.
Definition dhcp.hpp:611
Function< void(DhcpMessage::MessageType type, const DhcpPacket &request)> RequestNotify
request notification callback.
Definition dhcp.hpp:806
BasicDhcpServer()=delete
create the BasicDhcpServer instance.
int nak(const DhcpPacket &request, const std::string &message={}) noexcept
refuse a REQUEST message with a NAK.
Definition dhcp.hpp:863
int offer(const DhcpPacket &request, const IpAddress &address, const DhcpOption &options={}) noexcept
answer a DISCOVER message with an OFFER.
Definition dhcp.hpp:840
int setRequestListener(RequestNotify cb) noexcept
set the callback called on every request received, whatever its message type.
Definition dhcp.hpp:880
int unsetRequestListener() noexcept
unset the request callback, it is no longer called once this returns.
Definition dhcp.hpp:910
~BasicDhcpServer()
stop receiving messages and destroy the instance.
Definition dhcp.hpp:828
BasicDhcpServer(const std::string &interface, Reactor &reactor=ReactorThread::reactor())
create the instance bound to the given interface.
Definition dhcp.hpp:819
int ack(const DhcpPacket &request, const IpAddress &address, const DhcpOption &options={}) noexcept
answer a REQUEST message with an ACK.
Definition dhcp.hpp:852
carries DHCP messages over a packet socket.
Definition dhcp_protocol.hpp:34
struct __attribute__((packed, aligned(2))) Frame
link, internet and transport headers a DHCP message is framed with.
Definition dhcp.hpp:143
Reactor & _reactor
event loop reactor.
Definition dhcp.hpp:382
const std::string & interface() const noexcept
get the name of the interface the instance is bound to.
Definition dhcp.hpp:125
void detach() noexcept
unregister from the reactor, to be called by the final class before destruction starts.
Definition dhcp.hpp:177
BasicDhcp & operator=(const BasicDhcp &other)=delete
assign instance by copy.
std::unique_ptr< char[]> _buffer
receive buffer.
Definition dhcp.hpp:373
BasicDhcp(BasicDhcp &&other)=delete
create instance by move.
int send(const char *data, size_t size, const MacAddress &to, const IpAddress &source, const IpAddress &destination) noexcept
frame a message and write it on the wire.
Definition dhcp.hpp:214
BasicDhcp(const std::string &interface, Reactor &reactor=ReactorThread::reactor())
create the instance bound to the given interface.
Definition dhcp.hpp:73
typename Protocol::Socket Socket
Definition dhcp.hpp:60
const std::string _interface
interface name.
Definition dhcp.hpp:376
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:379
ssize_t receive(char *data, size_t maxSize, MacAddress &from, MacAddress &to) noexcept
read a message that passed the link, internet and transport checks.
Definition dhcp.hpp:280
void attach()
register with the reactor, to be called by the final class once fully constructed.
Definition dhcp.hpp:166
static uint16_t udpChecksum(const Frame &frame, const char *payload, size_t size) noexcept
compute the checksum of a UDP datagram, RFC 768.
Definition dhcp.hpp:189
DhcpMessage _message
DHCP message codec.
Definition dhcp.hpp:370
virtual ~BasicDhcp()=default
destroy the instance.
const MacAddress & hardware() const noexcept
get the hardware address of the interface the instance is bound to.
Definition dhcp.hpp:134
Socket _socket
underlying socket.
Definition dhcp.hpp:367
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:88
@ BootReply
Definition dhcp_message.hpp:111
@ BootRequest
Definition dhcp_message.hpp:110
ssize_t serialize(const DhcpPacket &packet, char *data, size_t maxSize) const
serialize a DHCP message into a buffer.
Definition dhcp_message.hpp:138
MessageType
DHCP message types, RFC 2132 section 9.6.
Definition dhcp_message.hpp:94
@ Request
Definition dhcp_message.hpp:97
@ Nak
Definition dhcp_message.hpp:100
@ Discover
Definition dhcp_message.hpp:95
@ Ack
Definition dhcp_message.hpp:99
@ Decline
Definition dhcp_message.hpp:98
@ Offer
Definition dhcp_message.hpp:96
@ Inform
Definition dhcp_message.hpp:102
@ Release
Definition dhcp_message.hpp:101
@ BroadcastFlag
Definition dhcp_message.hpp:119
int deserialize(DhcpPacket &packet, const char *data, size_t size) const
deserialize a DHCP message from a buffer.
Definition dhcp_message.hpp:202
static constexpr size_t headerSize
size of the fixed part of a DHCP message, magic cookie included.
Definition dhcp_message.hpp:291
DHCP option list.
Definition dhcp_option.hpp:64
static bool isValid(uint8_t code, T &&value)
check that a value can be carried by the given option.
Definition dhcp_option.hpp:413
@ Router
Definition dhcp_option.hpp:74
@ Message
Definition dhcp_option.hpp:127
@ ServerIdentifier
Definition dhcp_option.hpp:125
@ BroadcastAddress
Definition dhcp_option.hpp:99
@ DomainNameServer
Definition dhcp_option.hpp:77
@ ParameterRequestList
Definition dhcp_option.hpp:126
@ DomainName
Definition dhcp_option.hpp:86
@ SubnetMask
Definition dhcp_option.hpp:72
@ ClientIdentifier
Definition dhcp_option.hpp:132
@ MaximumDhcpMessageSize
Definition dhcp_option.hpp:128
@ RequestedIpAddress
Definition dhcp_option.hpp:121
@ DhcpMessageType
Definition dhcp_option.hpp:124
@ HostName
Definition dhcp_option.hpp:83
@ InterfaceMtu
Definition dhcp_option.hpp:97
const_iterator end() const
get an iterator to the element following the last element of the list.
Definition dhcp_option.hpp:294
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:351
const T * getIf(uint8_t code) const
get the value identified by code if it is present and holds the requested type.
Definition dhcp_option.hpp:246
const_iterator begin() const
get an iterator to the first element of the list.
Definition dhcp_option.hpp:276
IPv6, IPv4 address class.
Definition ip_address.hpp:52
static const IpAddress ipv4Wildcard
wildcard IPv4 address.
Definition ip_address.hpp:370
static const IpAddress ipv4Broadcast
broadcast IPv4 address.
Definition ip_address.hpp:373
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
MAC address class.
Definition mac_address.hpp:47
static const MacAddress broadcast
broadcast MAC address.
Definition mac_address.hpp:310
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
static Reactor & reactor()
get the global Reactor instance.
Definition reactor.cpp:612
Reactor class.
Definition reactor.hpp:159
bool isReactorThread() const noexcept
check if the calling thread is the reactor thread.
Definition reactor.cpp:337
int delHandler(int fd, bool sync=true) noexcept
delete handler from reactor.
Definition reactor.cpp:156
int addHandler(int fd, EventHandler *handler, bool wantRead=true, bool wantWrite=false, bool sync=true) noexcept
add handler to reactor.
Definition reactor.cpp:101
int invoke(InvokeHandler *fn, bool sync=true) noexcept
invoke a function from the reactor thread.
Definition reactor.cpp:199
class owning a mutex for the duration of a scoped block.
Definition mutex.hpp:249
Definition acceptor.hpp:32
uint16_t checksum(const uint16_t *data, size_t len, uint16_t current=0) noexcept
get standard 1s complement checksum.
Definition utils.hpp:319
Type randomize()
create a random number.
Definition utils.hpp:402
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:48
Definition error.hpp:144
DHCP message.
Definition dhcp_message.hpp:46
uint16_t flags
message flags.
Definition dhcp_message.hpp:75
MacAddress hardware
client hardware address, carried by the message itself.
Definition dhcp_message.hpp:54
IpAddress client
client IP address, set when the client already owns a lease.
Definition dhcp_message.hpp:57
uint8_t op
operation code.
Definition dhcp_message.hpp:78
DhcpOption options
message options.
Definition dhcp_message.hpp:81
MacAddress dest
link layer destination address, set by the transport.
Definition dhcp_message.hpp:51
IpAddress gateway
IP address of the relay agent the message went through.
Definition dhcp_message.hpp:66
uint32_t id
transaction identifier.
Definition dhcp_message.hpp:69
IpAddress address
Definition tcp_acceptor_test.cpp:35