2323#include " NUClearNet.hpp"
2424
2525#include < algorithm>
26+ #include < array>
2627#include < cstring>
2728#include < stdexcept>
2829#include < system_error>
@@ -258,13 +259,12 @@ namespace network {
258259 header.flags = req.flags ;
259260 header.hash = req.hash ;
260261
261- iovec iov[2 ];
262- iov[0 ].iov_base = reinterpret_cast <void *>(&header);
263- iov[0 ].iov_len = sizeof (DataPacket) - 1 ;
264- iov[1 ].iov_base = const_cast <void *>(static_cast <const void *>(req.data .data ()));
265- iov[1 ].iov_len = req.data .size ();
262+ std::array<iovec, 2 > iov{{
263+ {reinterpret_cast <void *>(&header), sizeof (DataPacket) - 1 }, // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
264+ {const_cast <void *>(static_cast <const void *>(req.data .data ())), req.data .size ()}, // NOLINT(cppcoreguidelines-pro-type-const-cast)
265+ }};
266266
267- send_iov (data_fd, req.target , iov, 2 );
267+ send_iov (data_fd, req.target , iov. data (), static_cast < int >(iov. size ()) );
268268 }
269269
270270 // Clean up expired fragment assemblies
@@ -312,13 +312,12 @@ namespace network {
312312 std::size_t offset = static_cast <std::size_t >(i) * packet_mtu;
313313 std::size_t frag_len = std::min (static_cast <std::size_t >(packet_mtu), length - offset);
314314
315- iovec iov[2 ];
316- iov[0 ].iov_base = reinterpret_cast <void *>(&header);
317- iov[0 ].iov_len = sizeof (DataPacket) - 1 ;
318- iov[1 ].iov_base = const_cast <void *>(static_cast <const void *>(payload + offset));
319- iov[1 ].iov_len = frag_len;
315+ std::array<iovec, 2 > iov{{
316+ {reinterpret_cast <void *>(&header), sizeof (DataPacket) - 1 }, // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
317+ {const_cast <void *>(static_cast <const void *>(payload + offset)), frag_len}, // NOLINT(cppcoreguidelines-pro-type-const-cast)
318+ }};
320319
321- send_iov (data_fd, announce_target, iov, 2 );
320+ send_iov (data_fd, announce_target, iov. data (), static_cast < int >(iov. size ()) );
322321 }
323322 return ;
324323 }
@@ -364,13 +363,12 @@ namespace network {
364363 std::size_t offset = static_cast <std::size_t >(i) * packet_mtu;
365364 std::size_t frag_len = std::min (static_cast <std::size_t >(packet_mtu), length - offset);
366365
367- iovec iov[2 ];
368- iov[0 ].iov_base = reinterpret_cast <void *>(&header);
369- iov[0 ].iov_len = sizeof (DataPacket) - 1 ;
370- iov[1 ].iov_base = const_cast <void *>(static_cast <const void *>(payload + offset));
371- iov[1 ].iov_len = frag_len;
366+ std::array<iovec, 2 > iov{{
367+ {reinterpret_cast <void *>(&header), sizeof (DataPacket) - 1 }, // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
368+ {const_cast <void *>(static_cast <const void *>(payload + offset)), frag_len}, // NOLINT(cppcoreguidelines-pro-type-const-cast)
369+ }};
372370
373- send_iov (data_fd, tgt, iov, 2 );
371+ send_iov (data_fd, tgt, iov. data (), static_cast < int >(iov. size ()) );
374372 }
375373
376374 // If reliable, track for retransmission (single copy stored internally)
@@ -435,20 +433,24 @@ namespace network {
435433 // Drain all pending datagrams from the socket
436434 // The data socket is non-blocking, so recvfrom returns -1/EWOULDBLOCK when empty
437435 // For the announce socket, MSG_DONTWAIT provides the same behavior on POSIX
438- for (;;) {
439- socklen_t source_len = sizeof (source.storage );
440- ssize_t received = ::recvfrom (fd,
441- reinterpret_cast <char *>(buffer),
442- sizeof (buffer),
443- MSG_DONTWAIT ,
444- &source.sock ,
445- &source_len);
446-
447- if (received <= 0 ) {
448- break ;
449- }
450-
436+ socklen_t source_len = sizeof (source.storage );
437+ ssize_t received = ::recvfrom (fd,
438+ reinterpret_cast <char *>(buffer),
439+ sizeof (buffer),
440+ MSG_DONTWAIT ,
441+ &source.sock ,
442+ &source_len);
443+
444+ while (received > 0 ) {
451445 process_packet (source, buffer, static_cast <std::size_t >(received));
446+
447+ source_len = sizeof (source.storage );
448+ received = ::recvfrom (fd,
449+ reinterpret_cast <char *>(buffer),
450+ sizeof (buffer),
451+ MSG_DONTWAIT ,
452+ &source.sock ,
453+ &source_len);
452454 }
453455 }
454456
@@ -457,158 +459,168 @@ namespace network {
457459 return ;
458460 }
459461
460- const auto * header = reinterpret_cast <const PacketHeader*>(data);
462+ const auto * header = reinterpret_cast <const PacketHeader*>(data); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
461463
462464 // Touch the peer to reset timeout
463465 discovery->touch_peer (source);
464466
465- switch (header->type ) {
466- case ANNOUNCE : {
467- auto announce_result = discovery->process_announce (source, data, length);
468-
469- if (announce_result.is_new ) {
470- // Force an immediate announce to the multicast/broadcast group
471- // so the new peer hears us on the announce channel (confirms our_d→their_a)
472- announce ();
473- last_announce = std::chrono::steady_clock::now ();
474- }
467+ if (header->type == ANNOUNCE ) {
468+ process_announce_packet (source, data, length);
469+ }
470+ else if (header->type == LEAVE ) {
471+ process_leave_packet (source);
472+ }
473+ else if (header->type == CONNECT ) {
474+ process_connect_packet (source, data, length);
475+ }
476+ else if (header->type == DATA ) {
477+ process_data_packet (source, data, length);
478+ }
479+ else if (header->type == ACK ) {
480+ process_ack_packet (source, data, length);
481+ }
482+ }
475483
476- // Send CONNECT packet if the handshake needs it (initial SYN or retransmit)
477- if (announce_result.response_flags != 0 ) {
478- auto pkt = Discovery::build_connect_packet (announce_result.response_flags );
479- send_buf (data_fd, source, pkt.data (), pkt.size ());
484+ void NUClearNet::process_announce_packet (const sock_t & source, const uint8_t * data, std::size_t length) {
485+ auto announce_result = discovery->process_announce (source, data, length);
480486
481- // Mark SYN_SENT if we're sending a SYN (only advances from IDLE)
482- if ((announce_result. response_flags & SYN ) != 0 ) {
483- discovery-> mark_syn_sent (source);
484- }
485- }
486- } break ;
487+ if (announce_result. is_new ) {
488+ // Force an immediate announce to the multicast/broadcast group
489+ // so the new peer hears us on the announce channel (confirms our_d→their_a)
490+ announce ();
491+ last_announce = std::chrono::steady_clock::now ();
492+ }
487493
488- case LEAVE : {
489- discovery->process_leave (source);
490- } break ;
494+ // Send CONNECT packet if the handshake needs it (initial SYN or retransmit)
495+ if (announce_result.response_flags != 0 ) {
496+ auto pkt = Discovery::build_connect_packet (announce_result.response_flags );
497+ send_buf (data_fd, source, pkt.data (), pkt.size ());
491498
492- case CONNECT : {
493- if (length < sizeof (ConnectPacket) ) {
494- return ;
495- }
496- const auto * pkt = reinterpret_cast < const ConnectPacket*>(data);
497- auto result = discovery-> process_connect (source, pkt-> flags );
499+ // Mark SYN_SENT if we're sending a SYN (only advances from IDLE)
500+ if ((announce_result. response_flags & SYN ) != 0 ) {
501+ discovery-> mark_syn_sent (source) ;
502+ }
503+ }
504+ }
498505
499- // Send response if the state machine requires one
500- if (result.response_flags != 0 ) {
501- auto response = Discovery::build_connect_packet (result.response_flags );
502- send_buf (data_fd, source, response.data (), response.size ());
503- }
504- } break ;
506+ void NUClearNet::process_leave_packet (const sock_t & source) {
507+ discovery->process_leave (source);
508+ }
505509
506- case DATA : {
507- // Only accept data from connected peers
508- if (!discovery->is_connected (source)) {
509- return ;
510- }
510+ void NUClearNet::process_connect_packet (const sock_t & source, const uint8_t * data, std::size_t length) {
511+ if (length < sizeof (ConnectPacket)) {
512+ return ;
513+ }
514+ const auto * pkt = reinterpret_cast <const ConnectPacket*>(data); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
515+ auto result = discovery->process_connect (source, pkt->flags );
511516
512- if (length < sizeof (DataPacket)) {
513- return ;
514- }
517+ // Send response if the state machine requires one
518+ if (result.response_flags != 0 ) {
519+ auto response = Discovery::build_connect_packet (result.response_flags );
520+ send_buf (data_fd, source, response.data (), response.size ());
521+ }
522+ }
515523
516- const auto * pkt = reinterpret_cast <const DataPacket*>(data);
524+ void NUClearNet::process_data_packet (const sock_t & source, const uint8_t * data, std::size_t length) {
525+ // Only accept data from connected peers
526+ if (!discovery->is_connected (source)) {
527+ return ;
528+ }
517529
518- // Drop messages we are not subscribed to (relevant for multicast broadcast data)
519- if (!routing.is_locally_subscribed (pkt->hash )) {
520- return ;
521- }
530+ if (length < sizeof (DataPacket)) {
531+ return ;
532+ }
522533
523- // Check for duplicates (at the packet group level)
524- auto & dedup = deduplicators[source];
534+ const auto * pkt = reinterpret_cast <const DataPacket*>(data); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
525535
526- if (dedup.is_duplicate (pkt->packet_id )) {
527- // Already processed this packet group — send ACK if reliable
528- if ((pkt->flags & RELIABLE ) != 0 ) {
529- std::vector<bool > all_received (pkt->packet_count , true );
530- auto ack = Reliability::build_ack_packet (pkt->packet_id , pkt->packet_count , all_received);
531- send_buf (data_fd, source, ack.data (), ack.size ());
532- }
533- return ;
534- }
536+ // Drop messages we are not subscribed to (relevant for multicast broadcast data)
537+ if (!routing.is_locally_subscribed (pkt->hash )) {
538+ return ;
539+ }
535540
536- // Extract fragment data
537- const uint8_t * frag_data = data + sizeof (DataPacket) - 1 ;
538- std::size_t frag_length = length - (sizeof (DataPacket) - 1 );
541+ // Check for duplicates (at the packet group level)
542+ auto & dedup = deduplicators[source];
539543
540- // Use a hash of the full source address as the source key for fragmentation
541- // This ensures IPv6 addresses are properly distinguished
542- uint64_t source_key = 0 ;
543- const auto * bytes = reinterpret_cast <const uint8_t *>(&source.storage );
544- for (std::size_t i = 0 ; i < sizeof (source.storage ); ++i) {
545- source_key ^= static_cast <uint64_t >(bytes[i]) << ((i % 8 ) * 8 );
546- }
544+ if (dedup.is_duplicate (pkt->packet_id )) {
545+ // Already processed this packet group — send ACK if reliable
546+ if ((pkt->flags & RELIABLE ) != 0 ) {
547+ std::vector<bool > all_received (pkt->packet_count , true );
548+ auto ack = Reliability::build_ack_packet (pkt->packet_id , pkt->packet_count , all_received);
549+ send_buf (data_fd, source, ack.data (), ack.size ());
550+ }
551+ return ;
552+ }
547553
548- // Submit to fragmentation
549- Fragmentation::AssembledPacket assembled;
550- bool has_assembled = fragmentation->submit_fragment (source_key,
551- pkt->packet_id ,
552- pkt->packet_no ,
553- pkt->packet_count ,
554- pkt->hash ,
555- pkt->flags ,
556- frag_data,
557- frag_length,
558- assembled);
559-
560- // Send ACK for reliable packets
561- if ((pkt->flags & RELIABLE ) != 0 ) {
562- // Build a partial ACK (we'd need to track received fragments per assembly)
563- // For now, send an ACK indicating we have this fragment
564- std::vector<bool > received (pkt->packet_count , false );
565- received[pkt->packet_no ] = true ;
566- auto ack = Reliability::build_ack_packet (pkt->packet_id , pkt->packet_count , received);
567- send_buf (data_fd, source, ack.data (), ack.size ());
568- }
554+ // Extract fragment data
555+ const uint8_t * frag_data = data + sizeof (DataPacket) - 1 ;
556+ std::size_t frag_length = length - (sizeof (DataPacket) - 1 );
569557
570- // If we have a complete message, deliver it
571- if (has_assembled) {
572- dedup.add_packet (assembled.packet_id );
558+ // Use a hash of the full source address as the source key for fragmentation
559+ // This ensures IPv6 addresses are properly distinguished
560+ uint64_t source_key = 0 ;
561+ const auto * bytes = reinterpret_cast <const uint8_t *>(&source.storage ); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
562+ for (std::size_t i = 0 ; i < sizeof (source.storage ); ++i) {
563+ source_key ^= static_cast <uint64_t >(bytes[i]) << ((i % 8 ) * 8 );
564+ }
573565
574- if (packet_callback) {
575- // Look up peer name
576- std::string peer_name;
577- const auto * peer = discovery->get_peer (source);
578- if (peer != nullptr ) {
579- peer_name = peer->name ;
580- }
566+ // Submit to fragmentation
567+ Fragmentation::AssembledPacket assembled;
568+ bool has_assembled = fragmentation->submit_fragment (source_key,
569+ pkt->packet_id ,
570+ pkt->packet_no ,
571+ pkt->packet_count ,
572+ pkt->hash ,
573+ pkt->flags ,
574+ frag_data,
575+ frag_length,
576+ assembled);
577+
578+ // Send ACK for reliable packets
579+ if ((pkt->flags & RELIABLE ) != 0 ) {
580+ std::vector<bool > received (pkt->packet_count , false );
581+ received[pkt->packet_no ] = true ;
582+ auto ack = Reliability::build_ack_packet (pkt->packet_id , pkt->packet_count , received);
583+ send_buf (data_fd, source, ack.data (), ack.size ());
584+ }
581585
582- bool reliable = (assembled. flags & RELIABLE ) != 0 ;
583- packet_callback (source, peer_name, assembled. hash , reliable, std::move (assembled. payload ));
584- }
586+ // If we have a complete message, deliver it
587+ if (has_assembled) {
588+ dedup. add_packet (assembled. packet_id );
585589
586- // Send full ACK for reliable
587- if ((assembled. flags & RELIABLE ) != 0 ) {
588- std::vector< bool > all_received (pkt-> packet_count , true ) ;
589- auto ack = Reliability::build_ack_packet (pkt-> packet_id , pkt-> packet_count , all_received );
590- send_buf (data_fd, source, ack. data (), ack. size ());
591- }
590+ if (packet_callback) {
591+ // Look up peer name
592+ std::string peer_name ;
593+ const auto * peer = discovery-> get_peer (source );
594+ if (peer != nullptr ) {
595+ peer_name = peer-> name ;
592596 }
593- } break ;
594597
595- case ACK : {
596- // Only accept ACKs from connected peers
597- if (!discovery->is_connected (source)) {
598- return ;
599- }
598+ bool reliable_flag = (assembled.flags & RELIABLE ) != 0 ;
599+ packet_callback (source, peer_name, assembled.hash , reliable_flag, std::move (assembled.payload ));
600+ }
600601
601- if (length < sizeof (ACKPacket)) {
602- return ;
603- }
604- const auto * pkt = reinterpret_cast <const ACKPacket*>(data);
605- const uint8_t * bitset = data + sizeof (ACKPacket) - 1 ;
606- std::size_t bitset_size = length - (sizeof (ACKPacket) - 1 );
607- reliability->process_ack (source, pkt->packet_id , pkt->packet_count , bitset, bitset_size);
608- } break ;
602+ // Send full ACK for reliable
603+ if ((assembled.flags & RELIABLE ) != 0 ) {
604+ std::vector<bool > all_received (pkt->packet_count , true );
605+ auto ack = Reliability::build_ack_packet (pkt->packet_id , pkt->packet_count , all_received);
606+ send_buf (data_fd, source, ack.data (), ack.size ());
607+ }
608+ }
609+ }
610+
611+ void NUClearNet::process_ack_packet (const sock_t & source, const uint8_t * data, std::size_t length) {
612+ // Only accept ACKs from connected peers
613+ if (!discovery->is_connected (source)) {
614+ return ;
615+ }
609616
610- default : break ;
617+ if (length < sizeof (ACKPacket)) {
618+ return ;
611619 }
620+ const auto * pkt = reinterpret_cast <const ACKPacket*>(data); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
621+ const uint8_t * bitset = data + sizeof (ACKPacket) - 1 ;
622+ std::size_t bitset_size = length - (sizeof (ACKPacket) - 1 );
623+ reliability->process_ack (source, pkt->packet_id , pkt->packet_count , bitset, bitset_size);
612624 }
613625
614626 void NUClearNet::send_iov (fd_t fd, const sock_t & target, const iovec* iov, int iovcnt) {
0 commit comments