oveRTOS C++ API
C++20 RAII wrappers for the oveRTOS C API
Loading...
Searching...
No Matches
net.hpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2026 Kamil Lulko <kamil.lulko@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-3.0-or-later
5 *
6 * This file is part of oveRTOS.
7 */
8
14#pragma once
15
16#include <ove/net.h>
17#include <ove/types.hpp>
18#include <ove/error.hpp>
19
20#ifdef CONFIG_OVE_NET
21
22namespace ove
23{
24
25/* ── Address (value type, copyable) ─────────────────────────────── */
26
36{
37 public:
42 {
43 }
44
54 static Address ipv4(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint16_t port)
55 {
56 Address addr;
57 ove_sockaddr_ipv4(&addr.raw, a, b, c, d, port);
58 return addr;
59 }
60
65 uint16_t port() const
66 {
67 return raw.port;
68 }
69
82 Address &set_port(uint16_t p)
83 {
84 raw.port = p;
85 return *this;
86 }
87
89 ove_sockaddr_t raw;
90};
91
92/* ── NetIfConfig (builder, copyable) ────────────────────────────── */
93
102{
103 public:
108 {
109 }
110
118 NetIfConfig &static_ip(const Address &ip, const Address &mask, const Address &gw)
119 {
120 raw.use_dhcp = 0;
121 raw.static_ip = ip.raw;
122 raw.netmask = mask.raw;
123 raw.gateway = gw.raw;
124 return *this;
125 }
126
132 {
133 raw.use_dhcp = 1;
134 return *this;
135 }
136
143 {
144 raw.dns = d.raw;
145 return *this;
146 }
147
149 ove_netif_config_t raw;
150};
151
152/* ── NetIf (RAII network interface) ─────────────────────────────── */
153
164class NetIf
165{
166 public:
173 {
174 int err = ove_netif_init(&handle_, &storage_);
175 OVE_STATIC_INIT_ASSERT(err == OVE_OK);
176 }
177
181 ~NetIf() noexcept
182 {
183 if (!handle_)
184 return;
185 ove_netif_deinit(handle_);
186 }
187
188 NetIf(const NetIf &) = delete;
189 NetIf &operator=(const NetIf &) = delete;
190
191#ifdef CONFIG_OVE_ZERO_HEAP
192 NetIf(NetIf &&) = delete;
193 NetIf &operator=(NetIf &&) = delete;
194#else
199 NetIf(NetIf &&other) noexcept : handle_(other.handle_)
200 {
201 other.handle_ = nullptr;
202 }
203
209 NetIf &operator=(NetIf &&other) noexcept
210 {
211 if (this != &other) {
212 if (handle_)
213 ove_netif_deinit(handle_);
214 handle_ = other.handle_;
215 other.handle_ = nullptr;
216 }
217 return *this;
218 }
219#endif
220
227 [[nodiscard]] Result<void> up(const NetIfConfig &cfg) noexcept
228 {
229 return from_rc(ove_netif_up(handle_, &cfg.raw));
230 }
231
252 void down()
253 {
254 ove_netif_down(handle_);
255 }
256
270 [[nodiscard]] Result<void> get_addr(Address *ip = nullptr, Address *gateway = nullptr,
271 Address *netmask = nullptr) noexcept
272 {
273 return from_rc(ove_netif_get_addr(handle_, ip ? &ip->raw : nullptr,
274 gateway ? &gateway->raw : nullptr,
275 netmask ? &netmask->raw : nullptr));
276 }
277
282 bool valid() const
283 {
284 return handle_ != nullptr;
285 }
286
291 ove_netif_t handle() const
292 {
293 return handle_;
294 }
295
296 private:
297 ove_netif_t handle_{};
298 ove_netif_storage_t storage_{};
299};
300
301/* ── Forward declaration for friend access ──────────────────────── */
302
303class TcpListener;
304
305/* ── TcpSocket (RAII TCP socket) ────────────────────────────────── */
306
318{
319 public:
326 {
327 int err = ove_socket_open(&handle_, &storage_, OVE_AF_INET, OVE_SOCK_STREAM);
328 OVE_STATIC_INIT_ASSERT(err == OVE_OK);
329 open_ = true;
330 }
331
335 ~TcpSocket() noexcept
336 {
337 if (open_)
338 ove_socket_close(handle_);
339 }
340
341 TcpSocket(const TcpSocket &) = delete;
342 TcpSocket &operator=(const TcpSocket &) = delete;
343
344#ifdef CONFIG_OVE_ZERO_HEAP
345 TcpSocket(TcpSocket &&) = delete;
346 TcpSocket &operator=(TcpSocket &&) = delete;
347#else
352 TcpSocket(TcpSocket &&other) noexcept
353 : handle_(other.handle_), storage_(other.storage_), open_(other.open_)
354 {
355 other.handle_ = nullptr;
356 other.open_ = false;
357 }
358
364 TcpSocket &operator=(TcpSocket &&other) noexcept
365 {
366 if (this != &other) {
367 if (open_)
368 ove_socket_close(handle_);
369 handle_ = other.handle_;
370 storage_ = other.storage_;
371 open_ = other.open_;
372 other.handle_ = nullptr;
373 other.open_ = false;
374 }
375 return *this;
376 }
377#endif
378
388 [[nodiscard]] Result<void> connect(const Address &addr,
389 std::chrono::nanoseconds timeout = wait_forever) noexcept
390 {
391 return from_rc(ove_socket_connect(handle_, &addr.raw, to_timeout_ns(timeout)));
392 }
393
404 [[nodiscard]] Result<size_t> send(const void *data, size_t len) noexcept
405 {
406 size_t sent = 0;
407 const int rc = ove_socket_send(handle_, data, len, &sent);
408 return from_rc(rc, sent);
409 }
410
422 [[nodiscard]] Result<size_t> recv(void *buf, size_t len,
423 std::chrono::nanoseconds timeout = wait_forever) noexcept
424 {
425 size_t received = 0;
426 const int rc =
427 ove_socket_recv(handle_, buf, len, &received, to_timeout_ns(timeout));
428 return from_rc(rc, received);
429 }
430
454 void close()
455 {
456 if (open_) {
457 ove_socket_close(handle_);
458 open_ = false;
459 }
460 }
461
466 bool is_open() const
467 {
468 return open_;
469 }
470
475 ove_socket_t handle() const
476 {
477 return handle_;
478 }
479
480 private:
481 friend class TcpListener;
482
488 TcpSocket(ove_socket_t h, ove_socket_storage_t s) : handle_(h), storage_(s), open_(true)
489 {
490 }
491
492 ove_socket_t handle_{};
493 ove_socket_storage_t storage_{};
494 bool open_{false};
495};
496
497/* ── UdpSocket (RAII UDP socket) ────────────────────────────────── */
498
508{
509 public:
516 {
517 int err = ove_socket_open(&handle_, &storage_, OVE_AF_INET, OVE_SOCK_DGRAM);
518 OVE_STATIC_INIT_ASSERT(err == OVE_OK);
519 open_ = true;
520 }
521
525 ~UdpSocket() noexcept
526 {
527 if (open_)
528 ove_socket_close(handle_);
529 }
530
531 UdpSocket(const UdpSocket &) = delete;
532 UdpSocket &operator=(const UdpSocket &) = delete;
533
534#ifdef CONFIG_OVE_ZERO_HEAP
535 UdpSocket(UdpSocket &&) = delete;
536 UdpSocket &operator=(UdpSocket &&) = delete;
537#else
542 UdpSocket(UdpSocket &&other) noexcept
543 : handle_(other.handle_), storage_(other.storage_), open_(other.open_)
544 {
545 other.handle_ = nullptr;
546 other.open_ = false;
547 }
548
554 UdpSocket &operator=(UdpSocket &&other) noexcept
555 {
556 if (this != &other) {
557 if (open_)
558 ove_socket_close(handle_);
559 handle_ = other.handle_;
560 storage_ = other.storage_;
561 open_ = other.open_;
562 other.handle_ = nullptr;
563 other.open_ = false;
564 }
565 return *this;
566 }
567#endif
568
576 [[nodiscard]] Result<void> bind(const Address &addr) noexcept
577 {
578 return from_rc(ove_socket_bind(handle_, &addr.raw));
579 }
580
590 [[nodiscard]] Result<size_t> send_to(const void *data, size_t len,
591 const Address &dest) noexcept
592 {
593 size_t sent = 0;
594 const int rc = ove_socket_sendto(handle_, data, len, &sent, &dest.raw);
595 return from_rc(rc, sent);
596 }
597
613 [[nodiscard]] Result<size_t>
614 recv_from(void *buf, size_t len, Address *src = nullptr,
615 std::chrono::nanoseconds timeout = wait_forever) noexcept
616 {
617 size_t received = 0;
618 const int rc = ove_socket_recvfrom(handle_, buf, len, &received,
619 src ? &src->raw : nullptr,
620 to_timeout_ns(timeout));
621 return from_rc(rc, received);
622 }
623
645 void close()
646 {
647 if (open_) {
648 ove_socket_close(handle_);
649 open_ = false;
650 }
651 }
652
657 bool is_open() const
658 {
659 return open_;
660 }
661
666 ove_socket_t handle() const
667 {
668 return handle_;
669 }
670
671 private:
672 ove_socket_t handle_{};
673 ove_socket_storage_t storage_{};
674 bool open_{false};
675};
676
677/* ── TcpListener (RAII listening socket) ────────────────────────── */
678
690{
691 public:
698 {
699 int err = ove_socket_open(&handle_, &storage_, OVE_AF_INET, OVE_SOCK_STREAM);
700 OVE_STATIC_INIT_ASSERT(err == OVE_OK);
701 open_ = true;
702 }
703
707 ~TcpListener() noexcept
708 {
709 if (open_)
710 ove_socket_close(handle_);
711 }
712
713 TcpListener(const TcpListener &) = delete;
714 TcpListener &operator=(const TcpListener &) = delete;
715
716#ifdef CONFIG_OVE_ZERO_HEAP
717 TcpListener(TcpListener &&) = delete;
718 TcpListener &operator=(TcpListener &&) = delete;
719#else
724 TcpListener(TcpListener &&other) noexcept
725 : handle_(other.handle_), storage_(other.storage_), open_(other.open_)
726 {
727 other.handle_ = nullptr;
728 other.open_ = false;
729 }
730
737 {
738 if (this != &other) {
739 if (open_)
740 ove_socket_close(handle_);
741 handle_ = other.handle_;
742 storage_ = other.storage_;
743 open_ = other.open_;
744 other.handle_ = nullptr;
745 other.open_ = false;
746 }
747 return *this;
748 }
749#endif
750
758 [[nodiscard]] Result<void> bind(const Address &addr) noexcept
759 {
760 return from_rc(ove_socket_bind(handle_, &addr.raw));
761 }
762
769 [[nodiscard]] Result<void> listen(int backlog = 4) noexcept
770 {
771 return from_rc(ove_socket_listen(handle_, backlog));
772 }
773
791 [[nodiscard]] Result<void> accept(TcpSocket &client,
792 std::chrono::nanoseconds timeout = wait_forever) noexcept
793 {
794 ove_socket_t cli_handle{};
795 ove_socket_storage_t cli_storage{};
796 const int rc = ove_socket_accept(handle_, &cli_handle, &cli_storage,
797 to_timeout_ns(timeout));
798 if (rc == OVE_OK) {
799 client.close();
800 client.handle_ = cli_handle;
801 client.storage_ = cli_storage;
802 client.open_ = true;
803 }
804 return from_rc(rc);
805 }
806
829 void close()
830 {
831 if (open_) {
832 ove_socket_close(handle_);
833 open_ = false;
834 }
835 }
836
841 bool is_open() const
842 {
843 return open_;
844 }
845
850 ove_socket_t handle() const
851 {
852 return handle_;
853 }
854
855 private:
856 ove_socket_t handle_{};
857 ove_socket_storage_t storage_{};
858 bool open_{false};
859};
860
861/* ── DNS ────────────────────────────────────────────────────────── */
862
869namespace dns
870{
871
882[[nodiscard]] inline Result<Address>
883resolve(const char *hostname, std::chrono::nanoseconds timeout = std::chrono::seconds{5}) noexcept
884{
885 Address addr;
886 const int rc = ove_dns_resolve(hostname, &addr.raw, to_timeout_ns(timeout));
887 return from_rc(rc, addr);
888}
889
890} /* namespace dns */
891
892} /* namespace ove */
893
894#endif /* CONFIG_OVE_NET */
Lightweight wrapper around ove_sockaddr_t.
Definition net.hpp:36
static Address ipv4(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint16_t port)
Creates an IPv4 address from individual octets and a port.
Definition net.hpp:54
Address()
Default-constructs a zeroed address.
Definition net.hpp:41
ove_sockaddr_t raw
The underlying C sockaddr.
Definition net.hpp:89
Address & set_port(uint16_t p)
Sets the port, returning a reference for chaining.
Definition net.hpp:82
uint16_t port() const
Returns the port in host byte order.
Definition net.hpp:65
Builder for ove_netif_config_t.
Definition net.hpp:102
NetIfConfig & dhcp()
Enables DHCP.
Definition net.hpp:131
ove_netif_config_t raw
The underlying C configuration struct.
Definition net.hpp:149
NetIfConfig & static_ip(const Address &ip, const Address &mask, const Address &gw)
Configures a static IP address.
Definition net.hpp:118
NetIfConfig()
Default-constructs a zeroed configuration.
Definition net.hpp:107
NetIfConfig & dns(const Address &d)
Sets the DNS server address.
Definition net.hpp:142
RAII wrapper around an oveRTOS network interface.
Definition net.hpp:165
NetIf & operator=(NetIf &&other) noexcept
Move-assignment operator — transfers ownership of the handle.
Definition net.hpp:209
~NetIf() noexcept
De-initialises the network interface.
Definition net.hpp:181
void down()
Tears down the network interface.
Definition net.hpp:252
NetIf()
Constructs and initialises the network interface.
Definition net.hpp:172
NetIf(NetIf &&other) noexcept
Move constructor — transfers ownership of the handle.
Definition net.hpp:199
Result< void > get_addr(Address *ip=nullptr, Address *gateway=nullptr, Address *netmask=nullptr) noexcept
Query the current addresses of the network interface.
Definition net.hpp:270
Result< void > up(const NetIfConfig &cfg) noexcept
Brings the network interface up.
Definition net.hpp:227
bool valid() const
Returns true if the underlying handle is non-null.
Definition net.hpp:282
ove_netif_t handle() const
Returns the raw oveRTOS network interface handle.
Definition net.hpp:291
RAII wrapper around an oveRTOS TCP listening socket.
Definition net.hpp:690
void close()
Closes the listening socket.
Definition net.hpp:829
Result< void > accept(TcpSocket &client, std::chrono::nanoseconds timeout=wait_forever) noexcept
Accepts an incoming connection.
Definition net.hpp:791
TcpListener()
Opens a new TCP socket for listening.
Definition net.hpp:697
ove_socket_t handle() const
Returns the raw oveRTOS socket handle.
Definition net.hpp:850
TcpListener & operator=(TcpListener &&other) noexcept
Move-assignment operator — transfers ownership of the socket.
Definition net.hpp:736
bool is_open() const
Returns true if the listening socket is open.
Definition net.hpp:841
TcpListener(TcpListener &&other) noexcept
Move constructor — transfers ownership of the socket.
Definition net.hpp:724
Result< void > bind(const Address &addr) noexcept
Binds the socket to a local address.
Definition net.hpp:758
Result< void > listen(int backlog=4) noexcept
Marks the socket as listening for incoming connections.
Definition net.hpp:769
~TcpListener() noexcept
Closes the listening socket if it is still open.
Definition net.hpp:707
RAII wrapper around an oveRTOS TCP (stream) socket.
Definition net.hpp:318
ove_socket_t handle() const
Returns the raw oveRTOS socket handle.
Definition net.hpp:475
Result< size_t > recv(void *buf, size_t len, std::chrono::nanoseconds timeout=wait_forever) noexcept
Receives data from the connected socket.
Definition net.hpp:422
bool is_open() const
Returns true if the socket is open.
Definition net.hpp:466
TcpSocket & operator=(TcpSocket &&other) noexcept
Move-assignment operator — transfers ownership of the socket.
Definition net.hpp:364
TcpSocket(TcpSocket &&other) noexcept
Move constructor — transfers ownership of the socket.
Definition net.hpp:352
TcpSocket()
Opens a new TCP socket.
Definition net.hpp:325
~TcpSocket() noexcept
Closes the socket if it is still open.
Definition net.hpp:335
Result< void > connect(const Address &addr, std::chrono::nanoseconds timeout=wait_forever) noexcept
Connects to a remote address.
Definition net.hpp:388
void close()
Closes the TCP socket.
Definition net.hpp:454
Result< size_t > send(const void *data, size_t len) noexcept
Sends data on the connected socket.
Definition net.hpp:404
RAII wrapper around an oveRTOS UDP (datagram) socket.
Definition net.hpp:508
~UdpSocket() noexcept
Closes the socket if it is still open.
Definition net.hpp:525
Result< size_t > send_to(const void *data, size_t len, const Address &dest) noexcept
Sends a datagram to a specific destination.
Definition net.hpp:590
UdpSocket()
Opens a new UDP socket.
Definition net.hpp:515
bool is_open() const
Returns true if the socket is open.
Definition net.hpp:657
UdpSocket(UdpSocket &&other) noexcept
Move constructor — transfers ownership of the socket.
Definition net.hpp:542
Result< size_t > recv_from(void *buf, size_t len, Address *src=nullptr, std::chrono::nanoseconds timeout=wait_forever) noexcept
Receives a datagram and the sender's address.
Definition net.hpp:614
void close()
Closes the UDP socket.
Definition net.hpp:645
Result< void > bind(const Address &addr) noexcept
Binds the socket to a local address.
Definition net.hpp:576
UdpSocket & operator=(UdpSocket &&other) noexcept
Move-assignment operator — transfers ownership of the socket.
Definition net.hpp:554
ove_socket_t handle() const
Returns the raw oveRTOS socket handle.
Definition net.hpp:666
Strong ove::Error type, Result<T> alias, and std::error_code interop for the oveRTOS C++ binding.
Top-level namespace for all oveRTOS C++ abstractions.
Definition app.hpp:20
constexpr uint64_t to_timeout_ns(std::chrono::duration< Rep, Period > d) noexcept
Convert a chrono duration to uint64_t nanoseconds for the C API.
Definition types.hpp:129
constexpr std::chrono::nanoseconds wait_forever
Sentinel duration meaning "block indefinitely".
Definition types.hpp:119
Result< void > from_rc(int rc) noexcept
Lifts a substrate rc-code into a Result<void>.
Definition error.hpp:254
std::expected< T, Error > Result
std::expected-based result alias.
Definition error.hpp:139
Common type definitions and concepts for the C++ wrapper layer.