ESPHome  2022.12.8
lwip_raw_tcp_impl.cpp
Go to the documentation of this file.
1 #include "socket.h"
2 #include "esphome/core/defines.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 
14 #include "esphome/core/helpers.h"
15 #include "esphome/core/log.h"
16 
17 namespace esphome {
18 namespace socket {
19 
20 static 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 
29 class 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  int setblocking(bool blocking) override {
471  if (pcb_ == nullptr) {
472  errno = ECONNRESET;
473  return -1;
474  }
475  if (blocking) {
476  // blocking operation not supported
477  errno = EINVAL;
478  return -1;
479  }
480  return 0;
481  }
482 
483  err_t accept_fn(struct tcp_pcb *newpcb, err_t err) {
484  LWIP_LOG("accept(newpcb=%p err=%d)", newpcb, err);
485  if (err != ERR_OK || newpcb == nullptr) {
486  // "An error code if there has been an error accepting. Only return ERR_ABRT if you have
487  // called tcp_abort from within the callback function!"
488  // https://www.nongnu.org/lwip/2_1_x/tcp_8h.html#a00517abce6856d6c82f0efebdafb734d
489  // nothing to do here, we just don't push it to the queue
490  return ERR_OK;
491  }
492  auto sock = make_unique<LWIPRawImpl>(family_, newpcb);
493  sock->init();
494  accepted_sockets_.push(std::move(sock));
495  return ERR_OK;
496  }
497  void err_fn(err_t err) {
498  LWIP_LOG("err(err=%d)", err);
499  // "If a connection is aborted because of an error, the application is alerted of this event by
500  // the err callback."
501  // pcb is already freed when this callback is called
502  // ERR_RST: connection was reset by remote host
503  // ERR_ABRT: aborted through tcp_abort or TCP timer
504  pcb_ = nullptr;
505  }
506  err_t recv_fn(struct pbuf *pb, err_t err) {
507  LWIP_LOG("recv(pb=%p err=%d)", pb, err);
508  if (err != 0) {
509  // "An error code if there has been an error receiving Only return ERR_ABRT if you have
510  // called tcp_abort from within the callback function!"
511  rx_closed_ = true;
512  return ERR_OK;
513  }
514  if (pb == nullptr) {
515  rx_closed_ = true;
516  return ERR_OK;
517  }
518  if (rx_buf_ == nullptr) {
519  // no need to copy because lwIP gave control of it to us
520  rx_buf_ = pb;
521  rx_buf_offset_ = 0;
522  } else {
523  pbuf_cat(rx_buf_, pb);
524  }
525  return ERR_OK;
526  }
527 
528  static err_t s_accept_fn(void *arg, struct tcp_pcb *newpcb, err_t err) {
529  LWIPRawImpl *arg_this = reinterpret_cast<LWIPRawImpl *>(arg);
530  return arg_this->accept_fn(newpcb, err);
531  }
532 
533  static void s_err_fn(void *arg, err_t err) {
534  LWIPRawImpl *arg_this = reinterpret_cast<LWIPRawImpl *>(arg);
535  arg_this->err_fn(err);
536  }
537 
538  static err_t s_recv_fn(void *arg, struct tcp_pcb *pcb, struct pbuf *pb, err_t err) {
539  LWIPRawImpl *arg_this = reinterpret_cast<LWIPRawImpl *>(arg);
540  return arg_this->recv_fn(pb, err);
541  }
542 
543  protected:
544  int ip2sockaddr_(ip_addr_t *ip, uint16_t port, struct sockaddr *name, socklen_t *addrlen) {
545  if (family_ == AF_INET) {
546  if (*addrlen < sizeof(struct sockaddr_in)) {
547  errno = EINVAL;
548  return -1;
549  }
550 
551  struct sockaddr_in *addr = reinterpret_cast<struct sockaddr_in *>(name);
552  addr->sin_family = AF_INET;
553  *addrlen = addr->sin_len = sizeof(struct sockaddr_in);
554  addr->sin_port = port;
555  inet_addr_from_ip4addr(&addr->sin_addr, ip_2_ip4(ip));
556  return 0;
557  }
558 #if LWIP_IPV6
559  else if (family_ == AF_INET6) {
560  if (*addrlen < sizeof(struct sockaddr_in6)) {
561  errno = EINVAL;
562  return -1;
563  }
564 
565  struct sockaddr_in6 *addr = reinterpret_cast<struct sockaddr_in6 *>(name);
566  addr->sin6_family = AF_INET6;
567  *addrlen = addr->sin6_len = sizeof(struct sockaddr_in6);
568  addr->sin6_port = port;
569 
570  // AF_INET6 sockets are bound to IPv4 as well, so we may encounter IPv4 addresses that must be converted to IPv6.
571  if (IP_IS_V4(ip)) {
572  ip_addr_t mapped;
573  ip4_2_ipv4_mapped_ipv6(ip_2_ip6(&mapped), ip_2_ip4(ip));
574  inet6_addr_from_ip6addr(&addr->sin6_addr, ip_2_ip6(&mapped));
575  } else {
576  inet6_addr_from_ip6addr(&addr->sin6_addr, ip_2_ip6(ip));
577  }
578  return 0;
579  }
580 #endif
581  return -1;
582  }
583 
584  struct tcp_pcb *pcb_;
585  std::queue<std::unique_ptr<LWIPRawImpl>> accepted_sockets_;
586  bool rx_closed_ = false;
587  pbuf *rx_buf_ = nullptr;
588  size_t rx_buf_offset_ = 0;
589  // don't use lwip nodelay flag, it sometimes causes reconnect
590  // instead use it for determining whether to call lwip_output
591  bool nodelay_ = false;
592  sa_family_t family_ = 0;
593 };
594 
595 std::unique_ptr<Socket> socket(int domain, int type, int protocol) {
596  auto *pcb = tcp_new();
597  if (pcb == nullptr)
598  return nullptr;
599  auto *sock = new LWIPRawImpl((sa_family_t) domain, pcb); // NOLINT(cppcoreguidelines-owning-memory)
600  sock->init();
601  return std::unique_ptr<Socket>{sock};
602 }
603 
604 } // namespace socket
605 } // namespace esphome
606 
607 #endif // USE_SOCKET_IMPL_LWIP_TCP
const char * name
Definition: stm32flash.h:78
uint8_t sin6_len
Definition: headers.h:63
uint32_t socklen_t
Definition: headers.h:86
sa_family_t sin_family
Definition: headers.h:54
uint16_t in_port_t
Definition: headers.h:49
in_port_t sin_port
Definition: headers.h:55
uint8_t type
Definition: headers.h:89
struct in_addr sin_addr
Definition: headers.h:56
uint8_t sa_family_t
Definition: headers.h:48
std::string size_t len
Definition: helpers.h:281
Definition: a4988.cpp:4
void init()
Definition: core.cpp:85
uint32_t val
Definition: datatypes.h:85
sa_family_t sin6_family
Definition: headers.h:64
uint8_t sin_len
Definition: headers.h:53
in_port_t sin6_port
Definition: headers.h:65
struct in6_addr sin6_addr
Definition: headers.h:67
std::unique_ptr< Socket > socket(int domain, int type, int protocol)
Create a socket of the given domain, type and protocol.