join 1.0
lightweight network framework library
Loading...
Searching...
No Matches
ndp_message.hpp
Go to the documentation of this file.
1
25#ifndef JOIN_FABRIC_NDP_MESSAGE_HPP
26#define JOIN_FABRIC_NDP_MESSAGE_HPP
27
28// libjoin.
29#include <join/mac_address.hpp>
30#include <join/utils.hpp>
31#include <join/ip_address.hpp>
32#include <join/error.hpp>
33
34// C++.
35#include <vector>
36
37// C.
38#include <netinet/icmp6.h>
39#include <net/ethernet.h>
40#include <netinet/in.h>
41#include <cstring>
42
43namespace join
44{
48 struct NdpPrefix
49 {
51 IpAddress prefix{AF_INET6};
52
54 uint8_t length = 64;
55
57 uint8_t flags = ND_OPT_PI_FLAG_ONLINK | ND_OPT_PI_FLAG_AUTO;
58
60 uint32_t valid = 2592000;
61
63 uint32_t preferred = 604800;
64 };
65
69 struct NdpRdnss
70 {
72 uint32_t lifetime = 1800;
73
75 std::vector<IpAddress> servers;
76 };
77
82 {
84 IpAddress src{AF_INET6};
85
88 };
89
94 {
96 IpAddress src{AF_INET6};
97
99 uint8_t hopLimit = 0;
100
102 uint8_t flags = 0;
103
105 uint16_t lifetime = 0;
106
108 uint32_t reachable = 0;
109
111 uint32_t retransmit = 0;
112
115
117 uint32_t mtu = 0;
118
120 std::vector<NdpPrefix> prefixes;
121
123 std::vector<NdpRdnss> rdnss;
124 };
125
130 {
132 IpAddress src{AF_INET6};
133
135 IpAddress target{AF_INET6};
136
139 };
140
145 {
147 IpAddress src{AF_INET6};
148
150 uint32_t flags = 0;
151
153 IpAddress target{AF_INET6};
154
157 };
158
163 {
164 public:
168 enum MessageType : uint8_t
169 {
170 RouterSolicit = ND_ROUTER_SOLICIT,
171 RouterAdvert = ND_ROUTER_ADVERT,
172 NeighborSolicit = ND_NEIGHBOR_SOLICIT,
173 NeighborAdvert = ND_NEIGHBOR_ADVERT,
174 };
175
179 enum OptionType : uint8_t
180 {
181 SourceLinkAddress = ND_OPT_SOURCE_LINKADDR,
182 TargetLinkAddress = ND_OPT_TARGET_LINKADDR,
183 PrefixInformation = ND_OPT_PREFIX_INFORMATION,
184 Mtu = ND_OPT_MTU,
186 };
187
195 ssize_t serialize (const RouterSolicitation& packet, char* data, size_t maxSize) const
196 {
197 char* cur = data;
198 const char* end = data + maxSize;
199 bool ok = true;
200
201 struct nd_router_solicit rs = {};
202 rs.nd_rs_type = RouterSolicit;
203 ok &= writeBytes (cur, end, &rs, sizeof (rs));
204 ok &= writeLinkAddress (cur, end, packet.link);
205
206 return ok ? cur - data : -1;
207 }
208
216 ssize_t serialize (const RouterAdvertisement& packet, char* data, size_t maxSize) const
217 {
218 return serialize (packet, data, maxSize, packet.link);
219 }
220
229 ssize_t serialize (const RouterAdvertisement& packet, char* data, size_t maxSize, const MacAddress& link) const
230 {
231 for (auto const& prefix : packet.prefixes)
232 {
233 if ((prefix.prefix.family () != AF_INET6) || (prefix.length > 128))
234 {
236 return -1;
237 }
238 }
239
240 for (auto const& rdnss : packet.rdnss)
241 {
242 if (rdnss.servers.empty ())
243 {
245 return -1;
246 }
247
248 if (rdnss.servers.size () > _maxDnsServers)
249 {
251 return -1;
252 }
253
254 for (auto const& server : rdnss.servers)
255 {
256 if (server.family () != AF_INET6)
257 {
259 return -1;
260 }
261 }
262 }
263
264 char* cur = data;
265 const char* end = data + maxSize;
266 bool ok = true;
267
268 struct nd_router_advert ra = {};
269 ra.nd_ra_type = RouterAdvert;
270 ra.nd_ra_curhoplimit = packet.hopLimit;
271 ra.nd_ra_flags_reserved = packet.flags;
272 ra.nd_ra_router_lifetime = htons (packet.lifetime);
273 ra.nd_ra_reachable = htonl (packet.reachable);
274 ra.nd_ra_retransmit = htonl (packet.retransmit);
275 ok &= writeBytes (cur, end, &ra, sizeof (ra));
276
277 ok &= writeLinkAddress (cur, end, link);
278
279 if (packet.mtu)
280 {
281 struct nd_opt_mtu mtu = {};
282 mtu.nd_opt_mtu_type = Mtu;
283 mtu.nd_opt_mtu_len = sizeof (mtu) >> 3;
284 mtu.nd_opt_mtu_mtu = htonl (packet.mtu);
285 ok &= writeBytes (cur, end, &mtu, sizeof (mtu));
286 }
287
288 for (auto const& prefix : packet.prefixes)
289 {
290 IpAddress masked = prefix.prefix & IpAddress (prefix.length, AF_INET6);
291
292 struct nd_opt_prefix_info pi = {};
293 pi.nd_opt_pi_type = PrefixInformation;
294 pi.nd_opt_pi_len = sizeof (pi) >> 3;
295 pi.nd_opt_pi_prefix_len = prefix.length;
296 pi.nd_opt_pi_flags_reserved = prefix.flags;
297 pi.nd_opt_pi_valid_time = htonl (prefix.valid);
298 pi.nd_opt_pi_preferred_time = htonl (prefix.preferred);
299 ::memcpy (&pi.nd_opt_pi_prefix, masked.addr (), sizeof (pi.nd_opt_pi_prefix));
300 ok &= writeBytes (cur, end, &pi, sizeof (pi));
301 }
302
303 for (auto const& rdnss : packet.rdnss)
304 {
305 RdnssHeader header = {};
306 header.type = RecursiveDnsServer;
307 header.len = static_cast<uint8_t> (1 + (rdnss.servers.size () * 2));
308 header.lifetime = htonl (rdnss.lifetime);
309 ok &= writeBytes (cur, end, &header, sizeof (header));
310
311 for (auto const& server : rdnss.servers)
312 {
313 ok &= writeBytes (cur, end, server.addr (), sizeof (struct in6_addr));
314 }
315 }
316
317 return ok ? cur - data : -1;
318 }
319
327 ssize_t serialize (const NeighborSolicitation& packet, char* data, size_t maxSize) const
328 {
329 if ((packet.target.family () != AF_INET6) || packet.target.isWildcard () || packet.target.isMulticast ())
330 {
332 return -1;
333 }
334
335 char* cur = data;
336 const char* end = data + maxSize;
337 bool ok = true;
338
339 struct nd_neighbor_solicit ns = {};
340 ns.nd_ns_type = NeighborSolicit;
341 ::memcpy (&ns.nd_ns_target, packet.target.addr (), sizeof (ns.nd_ns_target));
342 ok &= writeBytes (cur, end, &ns, sizeof (ns));
343 ok &= writeLinkAddress (cur, end, packet.link);
344
345 return ok ? cur - data : -1;
346 }
347
355 ssize_t serialize (const NeighborAdvertisement& packet, char* data, size_t maxSize) const
356 {
357 return serialize (packet, data, maxSize, packet.link);
358 }
359
368 ssize_t serialize (const NeighborAdvertisement& packet, char* data, size_t maxSize,
369 const MacAddress& link) const
370 {
371 if ((packet.target.family () != AF_INET6) || packet.target.isWildcard () || packet.target.isMulticast ())
372 {
374 return -1;
375 }
376
377 char* cur = data;
378 const char* end = data + maxSize;
379 bool ok = true;
380
381 struct nd_neighbor_advert na = {};
382 na.nd_na_type = NeighborAdvert;
383 na.nd_na_flags_reserved = packet.flags;
384 ::memcpy (&na.nd_na_target, packet.target.addr (), sizeof (na.nd_na_target));
385 ok &= writeBytes (cur, end, &na, sizeof (na));
386 ok &= writeLinkAddress (cur, end, link, TargetLinkAddress);
387
388 return ok ? cur - data : -1;
389 }
390
398 int deserialize (RouterSolicitation& packet, const char* data, size_t size) const
399 {
400 const char* cur = data;
401 const char* end = data + size;
402
403 struct nd_router_solicit rs = {};
404 if (!readBytes (cur, end, &rs, sizeof (rs)))
405 {
406 return -1;
407 }
408
409 if ((rs.nd_rs_type != RouterSolicit) || (rs.nd_rs_code != 0))
410 {
412 return -1;
413 }
414
415 packet.link = MacAddress ();
416
417 return readOptions (cur, end, [&packet] (const uint8_t* option, size_t size) {
418 if ((option[0] == SourceLinkAddress) && (size >= _optionHeaderSize + ETH_ALEN))
419 {
420 packet.link = MacAddress (option + _optionHeaderSize, ETH_ALEN);
421 }
422 });
423 }
424
432 int deserialize (RouterAdvertisement& packet, const char* data, size_t size) const
433 {
434 const char* cur = data;
435 const char* end = data + size;
436
437 struct nd_router_advert ra = {};
438 if (!readBytes (cur, end, &ra, sizeof (ra)))
439 {
440 return -1;
441 }
442
443 if ((ra.nd_ra_type != RouterAdvert) || (ra.nd_ra_code != 0))
444 {
446 return -1;
447 }
448
449 packet.hopLimit = ra.nd_ra_curhoplimit;
450 packet.flags = ra.nd_ra_flags_reserved;
451 packet.lifetime = ntohs (ra.nd_ra_router_lifetime);
452 packet.reachable = ntohl (ra.nd_ra_reachable);
453 packet.retransmit = ntohl (ra.nd_ra_retransmit);
454 packet.link = MacAddress ();
455 packet.mtu = 0;
456 packet.prefixes.clear ();
457 packet.rdnss.clear ();
458
459 return readOptions (cur, end, [&packet] (const uint8_t* option, size_t size) {
460 switch (option[0])
461 {
463 if (size >= _optionHeaderSize + ETH_ALEN)
464 {
465 packet.link = MacAddress (option + _optionHeaderSize, ETH_ALEN);
466 }
467 break;
468
469 case Mtu:
470 if (size == sizeof (struct nd_opt_mtu))
471 {
472 struct nd_opt_mtu mtu;
473 ::memcpy (&mtu, option, sizeof (mtu));
474 packet.mtu = ntohl (mtu.nd_opt_mtu_mtu);
475 }
476 break;
477
479 if (size == sizeof (struct nd_opt_prefix_info))
480 {
481 struct nd_opt_prefix_info pi;
482 ::memcpy (&pi, option, sizeof (pi));
483
484 if (pi.nd_opt_pi_prefix_len <= 128)
485 {
486 NdpPrefix prefix;
487 prefix.prefix = IpAddress (&pi.nd_opt_pi_prefix, sizeof (pi.nd_opt_pi_prefix));
488 prefix.length = pi.nd_opt_pi_prefix_len;
489 prefix.flags = pi.nd_opt_pi_flags_reserved;
490 prefix.valid = ntohl (pi.nd_opt_pi_valid_time);
491 prefix.preferred = ntohl (pi.nd_opt_pi_preferred_time);
492 packet.prefixes.push_back (std::move (prefix));
493 }
494 }
495 break;
496
498 if ((size > sizeof (RdnssHeader)) &&
499 (((size - sizeof (RdnssHeader)) % sizeof (struct in6_addr)) == 0))
500 {
501 RdnssHeader header;
502 ::memcpy (&header, option, sizeof (header));
503
504 NdpRdnss rdnss;
505 rdnss.lifetime = ntohl (header.lifetime);
506 rdnss.servers.reserve ((size - sizeof (RdnssHeader)) / sizeof (struct in6_addr));
507
508 for (size_t offset = sizeof (RdnssHeader); offset < size;
509 offset += sizeof (struct in6_addr))
510 {
511 rdnss.servers.emplace_back (option + offset, sizeof (struct in6_addr));
512 }
513
514 packet.rdnss.push_back (std::move (rdnss));
515 }
516 break;
517
518 default:
519 break;
520 }
521 });
522 }
523
531 int deserialize (NeighborSolicitation& packet, const char* data, size_t size) const
532 {
533 const char* cur = data;
534 const char* end = data + size;
535
536 struct nd_neighbor_solicit ns = {};
537 if (!readBytes (cur, end, &ns, sizeof (ns)))
538 {
539 return -1;
540 }
541
542 if ((ns.nd_ns_type != NeighborSolicit) || (ns.nd_ns_code != 0))
543 {
545 return -1;
546 }
547
548 packet.target = IpAddress (&ns.nd_ns_target, sizeof (ns.nd_ns_target));
549 if (packet.target.isWildcard () || packet.target.isMulticast ())
550 {
552 return -1;
553 }
554
555 packet.link = MacAddress ();
556
557 return readOptions (cur, end, [&packet] (const uint8_t* option, size_t size) {
558 if ((option[0] == SourceLinkAddress) && (size >= _optionHeaderSize + ETH_ALEN))
559 {
560 packet.link = MacAddress (option + _optionHeaderSize, ETH_ALEN);
561 }
562 });
563 }
564
572 int deserialize (NeighborAdvertisement& packet, const char* data, size_t size) const
573 {
574 const char* cur = data;
575 const char* end = data + size;
576
577 struct nd_neighbor_advert na = {};
578 if (!readBytes (cur, end, &na, sizeof (na)))
579 {
580 return -1;
581 }
582
583 if ((na.nd_na_type != NeighborAdvert) || (na.nd_na_code != 0))
584 {
586 return -1;
587 }
588
589 packet.target = IpAddress (&na.nd_na_target, sizeof (na.nd_na_target));
590 if (packet.target.isWildcard () || packet.target.isMulticast ())
591 {
593 return -1;
594 }
595
596 packet.flags = na.nd_na_flags_reserved;
597 packet.link = MacAddress ();
598
599 return readOptions (cur, end, [&packet] (const uint8_t* option, size_t size) {
600 if ((option[0] == TargetLinkAddress) && (size >= _optionHeaderSize + ETH_ALEN))
601 {
602 packet.link = MacAddress (option + _optionHeaderSize, ETH_ALEN);
603 }
604 });
605 }
606
607 protected:
611 struct __attribute__ ((packed)) RdnssHeader
612 {
614 uint8_t type;
615
617 uint8_t len;
618
620 uint16_t reserved;
621
623 uint32_t lifetime;
624 };
625
634 static bool writeLinkAddress (char*& cur, const char* end, const MacAddress& link,
636 {
637 if (link.isWildcard ())
638 {
639 return true;
640 }
641
642 uint8_t option[_optionHeaderSize + ETH_ALEN] = {type, 1};
643 ::memcpy (option + _optionHeaderSize, link.addr (), ETH_ALEN);
644
645 return writeBytes (cur, end, option, sizeof (option));
646 }
647
655 template <typename Handler>
656 static int readOptions (const char* cur, const char* end, Handler&& handler)
657 {
658 uint8_t option[_maxOptionSize];
659
660 for (;;)
661 {
662 if (cur == end)
663 {
664 return 0;
665 }
666
667 if (!readBytes (cur, end, option, _optionHeaderSize) || (option[1] == 0))
668 {
670 return -1;
671 }
672
673 const size_t size = static_cast<size_t> (option[1]) << 3;
674 if (!readBytes (cur, end, option + _optionHeaderSize, size - _optionHeaderSize))
675 {
676 return -1;
677 }
678
679 handler (option, size);
680 }
681 }
682
684 static constexpr size_t _optionHeaderSize = 2;
685
687 static constexpr size_t _maxOptionSize = 255 << 3;
688
690 static constexpr size_t _maxDnsServers = 127;
691
692 public:
694 static constexpr size_t maxRouterSolicitationSize =
695 sizeof (struct nd_router_solicit) + _optionHeaderSize + ETH_ALEN;
696
698 static constexpr size_t maxNeighborSolicitationSize =
699 sizeof (struct nd_neighbor_solicit) + _optionHeaderSize + ETH_ALEN;
700
702 static constexpr size_t maxNeighborAdvertisementSize =
703 sizeof (struct nd_neighbor_advert) + _optionHeaderSize + ETH_ALEN;
704 };
705}
706
707#endif
IPv6, IPv4 address class.
Definition ip_address.hpp:52
int prefix() const
get prefix length from netmask address.
Definition ip_address.cpp:1286
int family() const
get address family.
Definition ip_address.cpp:1250
bool isMulticast() const
check if IP address is multicast.
Definition ip_address.cpp:1358
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:47
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
NDP message codec.
Definition ndp_message.hpp:163
static constexpr size_t maxNeighborAdvertisementSize
biggest neighbor advertisement the codec writes, link layer address option included.
Definition ndp_message.hpp:702
static constexpr size_t maxRouterSolicitationSize
biggest router solicitation the codec writes, link layer address option included.
Definition ndp_message.hpp:694
static constexpr size_t _maxOptionSize
biggest option, its length field counts units of 8 octets.
Definition ndp_message.hpp:687
static bool writeLinkAddress(char *&cur, const char *end, const MacAddress &link, OptionType type=SourceLinkAddress)
write a link layer address option into a buffer.
Definition ndp_message.hpp:634
static constexpr size_t _maxDnsServers
most recursive DNS servers a single option can carry.
Definition ndp_message.hpp:690
ssize_t serialize(const RouterSolicitation &packet, char *data, size_t maxSize) const
serialize a router solicitation into a buffer.
Definition ndp_message.hpp:195
OptionType
NDP option types.
Definition ndp_message.hpp:180
@ Mtu
Definition ndp_message.hpp:184
@ PrefixInformation
Definition ndp_message.hpp:183
@ SourceLinkAddress
Definition ndp_message.hpp:181
@ TargetLinkAddress
Definition ndp_message.hpp:182
@ RecursiveDnsServer
Definition ndp_message.hpp:185
ssize_t serialize(const NeighborAdvertisement &packet, char *data, size_t maxSize) const
serialize a neighbor advertisement into a buffer.
Definition ndp_message.hpp:355
int deserialize(RouterAdvertisement &packet, const char *data, size_t size) const
deserialize a router advertisement from a buffer.
Definition ndp_message.hpp:432
static int readOptions(const char *cur, const char *end, Handler &&handler)
walk through the options of a message, RFC 4861 section 4.6.
Definition ndp_message.hpp:656
ssize_t serialize(const NeighborAdvertisement &packet, char *data, size_t maxSize, const MacAddress &link) const
serialize a neighbor advertisement into a buffer with the given link layer address.
Definition ndp_message.hpp:368
MessageType
NDP message types, RFC 4861 section 4.
Definition ndp_message.hpp:169
@ RouterAdvert
Definition ndp_message.hpp:171
@ RouterSolicit
Definition ndp_message.hpp:170
@ NeighborAdvert
Definition ndp_message.hpp:173
@ NeighborSolicit
Definition ndp_message.hpp:172
ssize_t serialize(const RouterAdvertisement &packet, char *data, size_t maxSize) const
serialize a router advertisement into a buffer.
Definition ndp_message.hpp:216
int deserialize(NeighborSolicitation &packet, const char *data, size_t size) const
deserialize a neighbor solicitation from a buffer.
Definition ndp_message.hpp:531
ssize_t serialize(const RouterAdvertisement &packet, char *data, size_t maxSize, const MacAddress &link) const
serialize a router advertisement into a buffer with the given link layer address.
Definition ndp_message.hpp:229
static constexpr size_t maxNeighborSolicitationSize
biggest neighbor solicitation the codec writes, link layer address option included.
Definition ndp_message.hpp:698
ssize_t serialize(const NeighborSolicitation &packet, char *data, size_t maxSize) const
serialize a neighbor solicitation into a buffer.
Definition ndp_message.hpp:327
struct __attribute__((packed)) RdnssHeader
recursive DNS server option header, RFC 8106 section 5.1.
Definition ndp_message.hpp:611
static constexpr size_t _optionHeaderSize
size of an option type and length fields.
Definition ndp_message.hpp:684
int deserialize(RouterSolicitation &packet, const char *data, size_t size) const
deserialize a router solicitation from a buffer.
Definition ndp_message.hpp:398
Definition acceptor.hpp:32
bool readBytes(const char *&cur, const char *end, void *out, size_t size) noexcept
read bytes from a buffer.
Definition utils.hpp:276
std::error_code make_error_code(join::Errc code) noexcept
Create an std::error_code object.
Definition error.cpp:195
bool writeBytes(char *&cur, const char *end, const void *in, size_t size) noexcept
write bytes to a buffer.
Definition utils.hpp:298
prefix carried by a router advertisement, RFC 4861 section 4.6.2.
Definition ndp_message.hpp:49
uint32_t preferred
time in seconds addresses generated from the prefix remain preferred.
Definition ndp_message.hpp:63
uint8_t length
number of leading bits of the prefix that are valid.
Definition ndp_message.hpp:54
uint32_t valid
time in seconds the prefix is valid for on-link determination.
Definition ndp_message.hpp:60
uint8_t flags
prefix flags.
Definition ndp_message.hpp:57
IpAddress prefix
prefix.
Definition ndp_message.hpp:51
recursive DNS servers carried by a router advertisement, RFC 8106 section 5.1.
Definition ndp_message.hpp:70
uint32_t lifetime
time in seconds the servers may be used for.
Definition ndp_message.hpp:72
std::vector< IpAddress > servers
server addresses.
Definition ndp_message.hpp:75
neighbor advertisement, RFC 4861 section 4.4.
Definition ndp_message.hpp:145
IpAddress src
address the advertisement came from, set by the transport.
Definition ndp_message.hpp:147
IpAddress target
address the advertisement is about.
Definition ndp_message.hpp:153
uint32_t flags
advertisement flags, in network byte order as ND_NA_FLAG_* are defined.
Definition ndp_message.hpp:150
MacAddress link
link layer address of the target, wildcard if not carried.
Definition ndp_message.hpp:156
neighbor solicitation, RFC 4861 section 4.3.
Definition ndp_message.hpp:130
MacAddress link
link layer address of the sender, wildcard if not carried.
Definition ndp_message.hpp:138
IpAddress src
address the solicitation came from, set by the transport.
Definition ndp_message.hpp:132
IpAddress target
address of the solicited neighbor.
Definition ndp_message.hpp:135
router advertisement, RFC 4861 section 4.2.
Definition ndp_message.hpp:94
uint32_t retransmit
time in milliseconds between retransmitted neighbor solicitations, zero if unspecified.
Definition ndp_message.hpp:111
uint32_t mtu
link MTU, zero if not carried.
Definition ndp_message.hpp:117
IpAddress src
address the advertisement came from, set by the transport.
Definition ndp_message.hpp:96
uint8_t hopLimit
hop limit hosts should use, zero if unspecified.
Definition ndp_message.hpp:99
std::vector< NdpPrefix > prefixes
prefixes.
Definition ndp_message.hpp:120
uint8_t flags
router flags.
Definition ndp_message.hpp:102
uint32_t reachable
time in milliseconds a neighbor is assumed reachable, zero if unspecified.
Definition ndp_message.hpp:108
MacAddress link
link layer address of the router, wildcard if not carried.
Definition ndp_message.hpp:114
std::vector< NdpRdnss > rdnss
recursive DNS servers, one entry per option.
Definition ndp_message.hpp:123
uint16_t lifetime
lifetime in seconds of the router as a default router, zero if it is not one.
Definition ndp_message.hpp:105
router solicitation, RFC 4861 section 4.1.
Definition ndp_message.hpp:82
MacAddress link
link layer address of the sender, wildcard if not carried.
Definition ndp_message.hpp:87
IpAddress src
address the solicitation came from, set by the transport.
Definition ndp_message.hpp:84