CDC Badge OS
Firmware for the CDC Badge v1.0 hardware security key
Loading...
Searching...
No Matches
host_api_net.cpp
Go to the documentation of this file.
1
17
21#include "cdc_log.h"
22
23#include "lwip/sockets.h"
24
25#include <cerrno>
26#include <cstring>
27#include <fcntl.h>
28#include <unistd.h>
29
30namespace pm = cdc::plugin_manager;
31
32extern "C" void* plg_get_active_plugin(void);
33extern "C" int plg_socket_adopt(int fd, void* owner);
34
35namespace {
36
37const char* TAG = "NET";
38
39constexpr uint8_t ACCEPT_RING = 4;
40
41struct {
42 void* plugin = nullptr;
43 bool active = false;
44 uint16_t port = 0;
45 uint32_t action_id = 0;
46 int listen_fd = -1;
47 int ring[ACCEPT_RING];
48 uint8_t head = 0, tail = 0; // head=produced, tail=consumed
49} net_state;
50
51bool net_allowed() {
52 auto* p = static_cast<pm::Plugin*>(plg_get_active_plugin());
53 return p && p->manifest().capabilities.net_listen;
54}
55
56// Open a non-blocking listen socket on the port (INADDR_ANY). Returns fd or -1.
57int open_listen(uint16_t port) {
58 int fd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
59 if (fd < 0) return -1;
60 int one = 1;
61 ::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
62
63 struct sockaddr_in addr{};
64 addr.sin_family = AF_INET;
65 addr.sin_addr.s_addr = htonl(INADDR_ANY);
66 addr.sin_port = htons(port);
67 if (::bind(fd, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)) != 0 ||
68 ::listen(fd, 4) != 0) {
69 ::close(fd);
70 return -1;
71 }
72 int flags = ::fcntl(fd, F_GETFL, 0);
73 if (flags >= 0) ::fcntl(fd, F_SETFL, flags | O_NONBLOCK);
74 return fd;
75}
76
77void close_listener() {
78 if (net_state.listen_fd >= 0) { ::close(net_state.listen_fd); net_state.listen_fd = -1; }
79 while (net_state.tail != net_state.head) {
80 ::close(net_state.ring[net_state.tail]);
81 net_state.tail = static_cast<uint8_t>((net_state.tail + 1) % ACCEPT_RING);
82 }
83}
84
85} // namespace
86
87extern "C" {
88
89int host_net_listen(uint16_t port, uint32_t action_id) {
90 if (!net_allowed()) return HOST_ERR_NO_CAPABILITY;
91 if (port == 0) return HOST_ERR_INVALID_ARG;
92 void* plugin = plg_get_active_plugin();
93 if (net_state.active) {
94 if (net_state.plugin == plugin && net_state.port == port) {
95 net_state.action_id = action_id; // idempotent re-listen
96 return HOST_OK;
97 }
98 return HOST_ERR_BUSY; // one listener at a time (v1)
99 }
100 net_state = {};
101 net_state.plugin = plugin;
102 net_state.port = port;
103 net_state.action_id = action_id;
104 net_state.listen_fd = open_listen(port);
105 net_state.active = true;
106 // A failed bind (WiFi not up yet / port busy) is retried by the pump.
107 if (net_state.listen_fd < 0) {
108 LOG_W(TAG, "bind port %u pending (retrying)", static_cast<unsigned>(port));
109 } else {
110 LOG_I(TAG, "listening on port %u", static_cast<unsigned>(port));
111 }
112 return HOST_OK;
113}
114
116 if (!net_allowed()) return HOST_ERR_NO_CAPABILITY;
117 void* plugin = plg_get_active_plugin();
118 if (!net_state.active || net_state.plugin != plugin) return HOST_ERR_NOT_FOUND;
119 if (net_state.tail == net_state.head) return HOST_ERR_NOT_FOUND;
120 int fd = net_state.ring[net_state.tail];
121 net_state.tail = static_cast<uint8_t>((net_state.tail + 1) % ACCEPT_RING);
122 int handle = plg_socket_adopt(fd, plugin);
123 if (handle < 0) { ::close(fd); return handle; }
124 return handle;
125}
126
127int host_net_close(uint16_t port) {
128 if (!net_allowed()) return HOST_ERR_NO_CAPABILITY;
129 if (!net_state.active || net_state.plugin != plg_get_active_plugin() ||
130 (port != 0 && net_state.port != port)) {
131 return HOST_ERR_NOT_FOUND;
132 }
133 close_listener();
134 net_state = {};
135 net_state.listen_fd = -1;
136 return HOST_OK;
137}
138
139// Plugin tick task: (re)open the listen socket, drain pending connections into
140// the ring, and fire the owner's action while connections wait.
141void plg_net_pump(void) {
142 if (!net_state.active) return;
143 if (net_state.listen_fd < 0) {
144 net_state.listen_fd = open_listen(net_state.port);
145 if (net_state.listen_fd < 0) return; // WiFi still down / port busy
146 LOG_I(TAG, "listening on port %u", static_cast<unsigned>(net_state.port));
147 }
148 for (;;) {
149 int c = ::accept(net_state.listen_fd, nullptr, nullptr);
150 if (c < 0) {
151 if (errno == EAGAIN || errno == EWOULDBLOCK) break; // nothing pending
152 // Real error (e.g. socket dropped across sleep): reopen next tick.
153 ::close(net_state.listen_fd);
154 net_state.listen_fd = -1;
155 return;
156 }
157 uint8_t next = static_cast<uint8_t>((net_state.head + 1) % ACCEPT_RING);
158 if (next == net_state.tail) { ::close(c); break; } // ring full: drop
159 net_state.ring[net_state.head] = c;
160 net_state.head = next;
161 }
162 if (net_state.tail != net_state.head && net_state.action_id && net_state.plugin) {
164 static_cast<pm::Plugin*>(net_state.plugin), net_state.action_id, 0, 0);
165 }
166}
167
168void plg_net_on_unload(void* plugin) {
169 if (net_state.active && net_state.plugin == plugin) {
170 close_listener();
171 net_state = {};
172 net_state.listen_fd = -1;
173 }
174}
175
176} // extern "C"
static const char * TAG
Discovers, loads, runs and unloads WASM plugins on the badge.
Owned WAMR module instance + per-plugin state.
uint8_t flags
CDC Log: logging over TinyUSB CDC and UART.
#define LOG_W(tag, fmt,...)
Definition cdc_log.h:146
#define LOG_I(tag, fmt,...)
Definition cdc_log.h:147
static PluginManager & instance() noexcept
void dispatchActionTo(Plugin *plugin, uint32_t action_id, uint32_t idx, uint32_t user_data)
int host_net_accept(void)
Take the next accepted connection as a socket handle for host_socket_read/write/close.
int host_net_listen(uint16_t port, uint32_t action_id)
Start a TCP listener on port; fires action_id while clients wait.
int host_net_close(uint16_t port)
Stop the listener (pass 0 or the listening port). Closes pending un-accepted connections.
CDC Badge OS plugin host API - canonical C ABI contract.
#define HOST_ERR_NO_CAPABILITY
Definition host_api.h:40
#define HOST_OK
Definition host_api.h:37
#define HOST_ERR_INVALID_ARG
Definition host_api.h:39
#define HOST_ERR_NOT_FOUND
Definition host_api.h:41
#define HOST_ERR_BUSY
Definition host_api.h:44
void * plg_get_active_plugin(void)
void * plg_get_active_plugin(void)
void plg_net_pump(void)
int plg_socket_adopt(int fd, void *owner)
void plg_net_on_unload(void *plugin)
static const char * TAG