ESPHome 2025.5.0
Loading...
Searching...
No Matches
lwip_raw_tcp_impl.cpp
Go to the documentation of this file.
1#include "socket.h"
3
4#ifdef USE_SOCKET_IMPL_LWIP_TCP
5
6#include "lwip/ip.h"
7#include "lwip/netif.h"
8#include "lwip/opt.h"
9#include "lwip/tcp.h"
10#include <cerrno>
11#include <cstring>
12#include <queue>
13
15#include "esphome/core/log.h"
16
17namespace esphome {
18namespace socket {
19
20static const char *const TAG = "socket.lwip";
21
22// set to 1 to enable verbose lwip logging
23#if 0
24#define LWIP_LOG(msg, ...) ESP_LOGVV(TAG, "socket %p: " msg, this, ##__VA_ARGS__)
25#else
26#define LWIP_LOG(msg, ...)
27#endif
28
29class LWIPRawImpl : public Socket {
30 public:
31 LWIPRawImpl(sa_family_t family, struct tcp_pcb *pcb) : pcb_(pcb), family_(family) {}
32 ~LWIPRawImpl() override {
33 if (pcb_ != nullptr) {
34 LWIP_LOG("tcp_abort(%p)", pcb_);
35 tcp_abort(pcb_);
36 pcb_ = nullptr;
37 }
38 }
39
40 void init() {
41 LWIP_LOG("init(%p)", pcb_);
42 tcp_arg(pcb_, this);
43 tcp_accept(pcb_, LWIPRawImpl::s_accept_fn);
44 tcp_recv(pcb_, LWIPRawImpl::s_recv_fn);
45 tcp_err(pcb_, LWIPRawImpl::s_err_fn);
46 }
47
48 std::unique_ptr<Socket> accept(struct sockaddr *addr, socklen_t *addrlen) override {
49 if (pcb_ == nullptr) {
50 errno = EBADF;
51 return nullptr;
52 }
53 if (accepted_sockets_.empty()) {
54 errno = EWOULDBLOCK;
55 return nullptr;
56 }
57 std::unique_ptr<LWIPRawImpl> sock = std::move(accepted_sockets_.front());
58 accepted_sockets_.pop();
59 if (addr != nullptr) {
60 sock->getpeername(addr, addrlen);
61 }
62 LWIP_LOG("accept(%p)", sock.get());
63 return std::unique_ptr<Socket>(std::move(sock));
64 }
65 int bind(const struct sockaddr *name, socklen_t addrlen) override {
66 if (pcb_ == nullptr) {
67 errno = EBADF;
68 return -1;
69 }
70 if (name == nullptr) {
71 errno = EINVAL;
72 return 0;
73 }
74 ip_addr_t ip;
75 in_port_t port;
76#if LWIP_IPV6
77 if (family_ == AF_INET) {
78 if (addrlen < sizeof(sockaddr_in)) {
79 errno = EINVAL;
80 return -1;
81 }
82 auto *addr4 = reinterpret_cast<const sockaddr_in *>(name);
83 port = ntohs(addr4->sin_port);
84 ip.type = IPADDR_TYPE_V4;
85 ip.u_addr.ip4.addr = addr4->sin_addr.s_addr;
86 LWIP_LOG("tcp_bind(%p ip=%s port=%u)", pcb_, ip4addr_ntoa(&ip.u_addr.ip4), port);
87 } else if (family_ == AF_INET6) {
88 if (addrlen < sizeof(sockaddr_in6)) {
89 errno = EINVAL;
90 return -1;
91 }
92 auto *addr6 = reinterpret_cast<const sockaddr_in6 *>(name);
93 port = ntohs(addr6->sin6_port);
94 ip.type = IPADDR_TYPE_ANY;
95 memcpy(&ip.u_addr.ip6.addr, &addr6->sin6_addr.un.u8_addr, 16);
96 LWIP_LOG("tcp_bind(%p ip=%s port=%u)", pcb_, ip6addr_ntoa(&ip.u_addr.ip6), port);
97 } else {
98 errno = EINVAL;
99 return -1;
100 }
101#else
102 if (family_ != AF_INET) {
103 errno = EINVAL;
104 return -1;
105 }
106 auto *addr4 = reinterpret_cast<const sockaddr_in *>(name);
107 port = ntohs(addr4->sin_port);
108 ip.addr = addr4->sin_addr.s_addr;
109 LWIP_LOG("tcp_bind(%p ip=%u port=%u)", pcb_, ip.addr, port);
110#endif
111 err_t err = tcp_bind(pcb_, &ip, port);
112 if (err == ERR_USE) {
113 LWIP_LOG(" -> err ERR_USE");
114 errno = EADDRINUSE;
115 return -1;
116 }
117 if (err == ERR_VAL) {
118 LWIP_LOG(" -> err ERR_VAL");
119 errno = EINVAL;
120 return -1;
121 }
122 if (err != ERR_OK) {
123 LWIP_LOG(" -> err %d", err);
124 errno = EIO;
125 return -1;
126 }
127 return 0;
128 }
129 int close() override {
130 if (pcb_ == nullptr) {
131 errno = ECONNRESET;
132 return -1;
133 }
134 LWIP_LOG("tcp_close(%p)", pcb_);
135 err_t err = tcp_close(pcb_);
136 if (err != ERR_OK) {
137 LWIP_LOG(" -> err %d", err);
138 tcp_abort(pcb_);
139 pcb_ = nullptr;
140 errno = err == ERR_MEM ? ENOMEM : EIO;
141 return -1;
142 }
143 pcb_ = nullptr;
144 return 0;
145 }
146 int shutdown(int how) override {
147 if (pcb_ == nullptr) {
148 errno = ECONNRESET;
149 return -1;
150 }
151 bool shut_rx = false, shut_tx = false;
152 if (how == SHUT_RD) {
153 shut_rx = true;
154 } else if (how == SHUT_WR) {
155 shut_tx = true;
156 } else if (how == SHUT_RDWR) {
157 shut_rx = shut_tx = true;
158 } else {
159 errno = EINVAL;
160 return -1;
161 }
162 LWIP_LOG("tcp_shutdown(%p shut_rx=%d shut_tx=%d)", pcb_, shut_rx ? 1 : 0, shut_tx ? 1 : 0);
163 err_t err = tcp_shutdown(pcb_, shut_rx, shut_tx);
164 if (err != ERR_OK) {
165 LWIP_LOG(" -> err %d", err);
166 errno = err == ERR_MEM ? ENOMEM : EIO;
167 return -1;
168 }
169 return 0;
170 }
171
172 int getpeername(struct sockaddr *name, socklen_t *addrlen) override {
173 if (pcb_ == nullptr) {
174 errno = ECONNRESET;
175 return -1;
176 }
177 if (name == nullptr || addrlen == nullptr) {
178 errno = EINVAL;
179 return -1;
180 }
181 return this->ip2sockaddr_(&pcb_->local_ip, pcb_->local_port, name, addrlen);
182 }
183 std::string getpeername() override {
184 if (pcb_ == nullptr) {
185 errno = ECONNRESET;
186 return "";
187 }
188 char buffer[50] = {};
189 if (IP_IS_V4_VAL(pcb_->remote_ip)) {
190 inet_ntoa_r(pcb_->remote_ip, buffer, sizeof(buffer));
191 }
192#if LWIP_IPV6
193 else if (IP_IS_V6_VAL(pcb_->remote_ip)) {
194 inet6_ntoa_r(pcb_->remote_ip, buffer, sizeof(buffer));
195 }
196#endif
197 return std::string(buffer);
198 }
199 int getsockname(struct sockaddr *name, socklen_t *addrlen) override {
200 if (pcb_ == nullptr) {
201 errno = ECONNRESET;
202 return -1;
203 }
204 if (name == nullptr || addrlen == nullptr) {
205 errno = EINVAL;
206 return -1;
207 }
208 return this->ip2sockaddr_(&pcb_->local_ip, pcb_->local_port, name, addrlen);
209 }
210 std::string getsockname() override {
211 if (pcb_ == nullptr) {
212 errno = ECONNRESET;
213 return "";
214 }
215 char buffer[50] = {};
216 if (IP_IS_V4_VAL(pcb_->local_ip)) {
217 inet_ntoa_r(pcb_->local_ip, buffer, sizeof(buffer));
218 }
219#if LWIP_IPV6
220 else if (IP_IS_V6_VAL(pcb_->local_ip)) {
221 inet6_ntoa_r(pcb_->local_ip, buffer, sizeof(buffer));
222 }
223#endif
224 return std::string(buffer);
225 }
226 int getsockopt(int level, int optname, void *optval, socklen_t *optlen) override {
227 if (pcb_ == nullptr) {
228 errno = ECONNRESET;
229 return -1;
230 }
231 if (optlen == nullptr || optval == nullptr) {
232 errno = EINVAL;
233 return -1;
234 }
235 if (level == SOL_SOCKET && optname == SO_REUSEADDR) {
236 if (*optlen < 4) {
237 errno = EINVAL;
238 return -1;
239 }
240
241 // lwip doesn't seem to have this feature. Don't send an error
242 // to prevent warnings
243 *reinterpret_cast<int *>(optval) = 1;
244 *optlen = 4;
245 return 0;
246 }
247 if (level == IPPROTO_TCP && optname == TCP_NODELAY) {
248 if (*optlen < 4) {
249 errno = EINVAL;
250 return -1;
251 }
252 *reinterpret_cast<int *>(optval) = nodelay_;
253 *optlen = 4;
254 return 0;
255 }
256
257 errno = EINVAL;
258 return -1;
259 }
260 int setsockopt(int level, int optname, const void *optval, socklen_t optlen) override {
261 if (pcb_ == nullptr) {
262 errno = ECONNRESET;
263 return -1;
264 }
265 if (level == SOL_SOCKET && optname == SO_REUSEADDR) {
266 if (optlen != 4) {
267 errno = EINVAL;
268 return -1;
269 }
270
271 // lwip doesn't seem to have this feature. Don't send an error
272 // to prevent warnings
273 return 0;
274 }
275 if (level == IPPROTO_TCP && optname == TCP_NODELAY) {
276 if (optlen != 4) {
277 errno = EINVAL;
278 return -1;
279 }
280 int val = *reinterpret_cast<const int *>(optval);
281 nodelay_ = val;
282 return 0;
283 }
284
285 errno = EINVAL;
286 return -1;
287 }
288 int listen(int backlog) override {
289 if (pcb_ == nullptr) {
290 errno = EBADF;
291 return -1;
292 }
293 LWIP_LOG("tcp_listen_with_backlog(%p backlog=%d)", pcb_, backlog);
294 struct tcp_pcb *listen_pcb = tcp_listen_with_backlog(pcb_, backlog);
295 if (listen_pcb == nullptr) {
296 tcp_abort(pcb_);
297 pcb_ = nullptr;
298 errno = EOPNOTSUPP;
299 return -1;
300 }
301 // tcp_listen reallocates the pcb, replace ours
302 pcb_ = listen_pcb;
303 // set callbacks on new pcb
304 LWIP_LOG("tcp_arg(%p)", pcb_);
305 tcp_arg(pcb_, this);
306 tcp_accept(pcb_, LWIPRawImpl::s_accept_fn);
307 return 0;
308 }
309 ssize_t read(void *buf, size_t len) override {
310 if (pcb_ == nullptr) {
311 errno = ECONNRESET;
312 return -1;
313 }
314 if (rx_closed_ && rx_buf_ == nullptr) {
315 return 0;
316 }
317 if (len == 0) {
318 return 0;
319 }
320 if (rx_buf_ == nullptr) {
321 errno = EWOULDBLOCK;
322 return -1;
323 }
324
325 size_t read = 0;
326 uint8_t *buf8 = reinterpret_cast<uint8_t *>(buf);
327 while (len && rx_buf_ != nullptr) {
328 size_t pb_len = rx_buf_->len;
329 size_t pb_left = pb_len - rx_buf_offset_;
330 if (pb_left == 0)
331 break;
332 size_t copysize = std::min(len, pb_left);
333 memcpy(buf8, reinterpret_cast<uint8_t *>(rx_buf_->payload) + rx_buf_offset_, copysize);
334
335 if (pb_left == copysize) {
336 // full pb copied, free it
337 if (rx_buf_->next == nullptr) {
338 // last buffer in chain
339 pbuf_free(rx_buf_);
340 rx_buf_ = nullptr;
341 rx_buf_offset_ = 0;
342 } else {
343 auto *old_buf = rx_buf_;
344 rx_buf_ = rx_buf_->next;
345 pbuf_ref(rx_buf_);
346 pbuf_free(old_buf);
347 rx_buf_offset_ = 0;
348 }
349 } else {
350 rx_buf_offset_ += copysize;
351 }
352 LWIP_LOG("tcp_recved(%p %u)", pcb_, copysize);
353 tcp_recved(pcb_, copysize);
354
355 buf8 += copysize;
356 len -= copysize;
357 read += copysize;
358 }
359
360 if (read == 0) {
361 errno = EWOULDBLOCK;
362 return -1;
363 }
364
365 return read;
366 }
367 ssize_t readv(const struct iovec *iov, int iovcnt) override {
368 ssize_t ret = 0;
369 for (int i = 0; i < iovcnt; i++) {
370 ssize_t err = read(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len);
371 if (err == -1) {
372 if (ret != 0)
373 // if we already read some don't return an error
374 break;
375 return err;
376 }
377 ret += err;
378 if ((size_t) err != iov[i].iov_len)
379 break;
380 }
381 return ret;
382 }
383 ssize_t internal_write(const void *buf, size_t len) {
384 if (pcb_ == nullptr) {
385 errno = ECONNRESET;
386 return -1;
387 }
388 if (len == 0)
389 return 0;
390 if (buf == nullptr) {
391 errno = EINVAL;
392 return 0;
393 }
394 auto space = tcp_sndbuf(pcb_);
395 if (space == 0) {
396 errno = EWOULDBLOCK;
397 return -1;
398 }
399 size_t to_send = std::min((size_t) space, len);
400 LWIP_LOG("tcp_write(%p buf=%p %u)", pcb_, buf, to_send);
401 err_t err = tcp_write(pcb_, buf, to_send, TCP_WRITE_FLAG_COPY);
402 if (err == ERR_MEM) {
403 LWIP_LOG(" -> err ERR_MEM");
404 errno = EWOULDBLOCK;
405 return -1;
406 }
407 if (err != ERR_OK) {
408 LWIP_LOG(" -> err %d", err);
409 errno = ECONNRESET;
410 return -1;
411 }
412 return to_send;
413 }
414 int internal_output() {
415 LWIP_LOG("tcp_output(%p)", pcb_);
416 err_t err = tcp_output(pcb_);
417 if (err == ERR_ABRT) {
418 LWIP_LOG(" -> err ERR_ABRT");
419 // sometimes lwip returns ERR_ABRT for no apparent reason
420 // the connection works fine afterwards, and back with ESPAsyncTCP we
421 // indirectly also ignored this error
422 // FIXME: figure out where this is returned and what it means in this context
423 return 0;
424 }
425 if (err != ERR_OK) {
426 LWIP_LOG(" -> err %d", err);
427 errno = ECONNRESET;
428 return -1;
429 }
430 return 0;
431 }
432 ssize_t write(const void *buf, size_t len) override {
433 ssize_t written = internal_write(buf, len);
434 if (written == -1)
435 return -1;
436 if (written == 0)
437 // no need to output if nothing written
438 return 0;
439 if (nodelay_) {
440 int err = internal_output();
441 if (err == -1)
442 return -1;
443 }
444 return written;
445 }
446 ssize_t writev(const struct iovec *iov, int iovcnt) override {
447 ssize_t written = 0;
448 for (int i = 0; i < iovcnt; i++) {
449 ssize_t err = internal_write(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len);
450 if (err == -1) {
451 if (written != 0)
452 // if we already read some don't return an error
453 break;
454 return err;
455 }
456 written += err;
457 if ((size_t) err != iov[i].iov_len)
458 break;
459 }
460 if (written == 0)
461 // no need to output if nothing written
462 return 0;
463 if (nodelay_) {
464 int err = internal_output();
465 if (err == -1)
466 return -1;
467 }
468 return written;
469 }
470 ssize_t sendto(const void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen) override {
471 // return ::sendto(fd_, buf, len, flags, to, tolen);
472 errno = ENOSYS;
473 return -1;
474 }
475 int setblocking(bool blocking) override {
476 if (pcb_ == nullptr) {
477 errno = ECONNRESET;
478 return -1;
479 }
480 if (blocking) {
481 // blocking operation not supported
482 errno = EINVAL;
483 return -1;
484 }
485 return 0;
486 }
487
488 err_t accept_fn(struct tcp_pcb *newpcb, err_t err) {
489 LWIP_LOG("accept(newpcb=%p err=%d)", newpcb, err);
490 if (err != ERR_OK || newpcb == nullptr) {
491 // "An error code if there has been an error accepting. Only return ERR_ABRT if you have
492 // called tcp_abort from within the callback function!"
493 // https://www.nongnu.org/lwip/2_1_x/tcp_8h.html#a00517abce6856d6c82f0efebdafb734d
494 // nothing to do here, we just don't push it to the queue
495 return ERR_OK;
496 }
497 auto sock = make_unique<LWIPRawImpl>(family_, newpcb);
498 sock->init();
499 accepted_sockets_.push(std::move(sock));
500 return ERR_OK;
501 }
502 void err_fn(err_t err) {
503 LWIP_LOG("err(err=%d)", err);
504 // "If a connection is aborted because of an error, the application is alerted of this event by
505 // the err callback."
506 // pcb is already freed when this callback is called
507 // ERR_RST: connection was reset by remote host
508 // ERR_ABRT: aborted through tcp_abort or TCP timer
509 pcb_ = nullptr;
510 }
511 err_t recv_fn(struct pbuf *pb, err_t err) {
512 LWIP_LOG("recv(pb=%p err=%d)", pb, err);
513 if (err != 0) {
514 // "An error code if there has been an error receiving Only return ERR_ABRT if you have
515 // called tcp_abort from within the callback function!"
516 rx_closed_ = true;
517 return ERR_OK;
518 }
519 if (pb == nullptr) {
520 rx_closed_ = true;
521 return ERR_OK;
522 }
523 if (rx_buf_ == nullptr) {
524 // no need to copy because lwIP gave control of it to us
525 rx_buf_ = pb;
526 rx_buf_offset_ = 0;
527 } else {
528 pbuf_cat(rx_buf_, pb);
529 }
530 return ERR_OK;
531 }
532
533 static err_t s_accept_fn(void *arg, struct tcp_pcb *newpcb, err_t err) {
534 LWIPRawImpl *arg_this = reinterpret_cast<LWIPRawImpl *>(arg);
535 return arg_this->accept_fn(newpcb, err);
536 }
537
538 static void s_err_fn(void *arg, err_t err) {
539 LWIPRawImpl *arg_this = reinterpret_cast<LWIPRawImpl *>(arg);
540 arg_this->err_fn(err);
541 }
542
543 static err_t s_recv_fn(void *arg, struct tcp_pcb *pcb, struct pbuf *pb, err_t err) {
544 LWIPRawImpl *arg_this = reinterpret_cast<LWIPRawImpl *>(arg);
545 return arg_this->recv_fn(pb, err);
546 }
547
548 protected:
549 int ip2sockaddr_(ip_addr_t *ip, uint16_t port, struct sockaddr *name, socklen_t *addrlen) {
550 if (family_ == AF_INET) {
551 if (*addrlen < sizeof(struct sockaddr_in)) {
552 errno = EINVAL;
553 return -1;
554 }
555
556 struct sockaddr_in *addr = reinterpret_cast<struct sockaddr_in *>(name);
557 addr->sin_family = AF_INET;
558 *addrlen = addr->sin_len = sizeof(struct sockaddr_in);
559 addr->sin_port = port;
560 inet_addr_from_ip4addr(&addr->sin_addr, ip_2_ip4(ip));
561 return 0;
562 }
563#if LWIP_IPV6
564 else if (family_ == AF_INET6) {
565 if (*addrlen < sizeof(struct sockaddr_in6)) {
566 errno = EINVAL;
567 return -1;
568 }
569
570 struct sockaddr_in6 *addr = reinterpret_cast<struct sockaddr_in6 *>(name);
571 addr->sin6_family = AF_INET6;
572 *addrlen = addr->sin6_len = sizeof(struct sockaddr_in6);
573 addr->sin6_port = port;
574
575 // AF_INET6 sockets are bound to IPv4 as well, so we may encounter IPv4 addresses that must be converted to IPv6.
576 if (IP_IS_V4(ip)) {
577 ip_addr_t mapped;
578 ip4_2_ipv4_mapped_ipv6(ip_2_ip6(&mapped), ip_2_ip4(ip));
579 inet6_addr_from_ip6addr(&addr->sin6_addr, ip_2_ip6(&mapped));
580 } else {
581 inet6_addr_from_ip6addr(&addr->sin6_addr, ip_2_ip6(ip));
582 }
583 return 0;
584 }
585#endif
586 return -1;
587 }
588
589 struct tcp_pcb *pcb_;
590 std::queue<std::unique_ptr<LWIPRawImpl>> accepted_sockets_;
591 bool rx_closed_ = false;
592 pbuf *rx_buf_ = nullptr;
593 size_t rx_buf_offset_ = 0;
594 // don't use lwip nodelay flag, it sometimes causes reconnect
595 // instead use it for determining whether to call lwip_output
596 bool nodelay_ = false;
597 sa_family_t family_ = 0;
598};
599
600std::unique_ptr<Socket> socket(int domain, int type, int protocol) {
601 auto *pcb = tcp_new();
602 if (pcb == nullptr)
603 return nullptr;
604 auto *sock = new LWIPRawImpl((sa_family_t) domain, pcb); // NOLINT(cppcoreguidelines-owning-memory)
605 sock->init();
606 return std::unique_ptr<Socket>{sock};
607}
608
609} // namespace socket
610} // namespace esphome
611
612#endif // USE_SOCKET_IMPL_LWIP_TCP
uint8_t type
uint16_t in_port_t
Definition headers.h:58
uint32_t socklen_t
Definition headers.h:97
uint8_t sa_family_t
Definition headers.h:57
__int64 ssize_t
Definition httplib.h:175
in_addr ip_addr_t
Definition ip_address.h:22
mopeka_std_values val[4]
std::unique_ptr< Socket > socket(int domain, int type, int protocol)
Create a socket of the given domain, type and protocol.
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
std::string size_t len
Definition helpers.h:301
std::unique_ptr< T > make_unique(Args &&...args)
Definition helpers.h:85
uint8_t sin6_len
Definition headers.h:73
in_port_t sin6_port
Definition headers.h:75
struct in6_addr sin6_addr
Definition headers.h:77
sa_family_t sin6_family
Definition headers.h:74
struct in_addr sin_addr
Definition headers.h:65
uint8_t sin_len
Definition headers.h:62
sa_family_t sin_family
Definition headers.h:63
in_port_t sin_port
Definition headers.h:64