TinyPPP
PPP networking over any Arduino Stream
Loading...
Searching...
No Matches
IPStack.h
Go to the documentation of this file.
1#pragma once
17#include <stdint.h>
18#include <string.h>
19
20#include <Arduino.h>
21
22#ifndef TINYPPP_TCP_SOCKETS
23#define TINYPPP_TCP_SOCKETS 4
24#endif
25
26#ifndef TINYPPP_TCP_RX_BUFFER
27#define TINYPPP_TCP_RX_BUFFER 1024
28#endif
29
30#ifndef TINYPPP_TCP_TX_BUFFER
31#define TINYPPP_TCP_TX_BUFFER 1024
32#endif
33
34#ifndef TINYPPP_TCP_LISTENERS
35#define TINYPPP_TCP_LISTENERS 2
36#endif
37
38#ifndef TINYPPP_UDP_SOCKETS
39#define TINYPPP_UDP_SOCKETS 2
40#endif
41
42#ifndef TINYPPP_UDP_RX_BUFFER
43#define TINYPPP_UDP_RX_BUFFER 1024
44#endif
45
46#ifndef TINYPPP_UDP_TX_BUFFER
47#define TINYPPP_UDP_TX_BUFFER 576
48#endif
49
51#ifndef TINYPPP_TCP_RTO
52#define TINYPPP_TCP_RTO 1000
53#endif
54
55#ifndef TINYPPP_TCP_MAX_RETRIES
56#define TINYPPP_TCP_MAX_RETRIES 8
57#endif
58
60#ifndef TINYPPP_TCP_TIME_WAIT
61#define TINYPPP_TCP_TIME_WAIT 2000
62#endif
63
65#ifndef TINYPPP_TCP_LINGER
66#define TINYPPP_TCP_LINGER 10000
67#endif
68
69namespace tinyppp {
70
72using ip4_t = uint32_t;
73
75template <size_t N>
77 public:
79 size_t available() const { return _len; }
81 size_t free() const { return N - _len; }
83 size_t size() const { return N; }
85 void clear() { _head = _len = 0; }
86
88 size_t write(const uint8_t *data, size_t len) {
89 if (len > free()) len = free();
90 for (size_t i = 0; i < len; i++) _buf[(_head + _len + i) % N] = data[i];
91 _len += len;
92 return len;
93 }
94
96 int read() {
97 if (_len == 0) return -1;
98 uint8_t b = _buf[_head];
99 discard(1);
100 return b;
101 }
102
104 size_t read(uint8_t *data, size_t len) {
105 len = peek(0, data, len);
106 discard(len);
107 return len;
108 }
109
111 int peek() const { return _len == 0 ? -1 : _buf[_head]; }
112
114 size_t peek(size_t offset, uint8_t *data, size_t len) const {
115 if (offset >= _len) return 0;
116 if (len > _len - offset) len = _len - offset;
117 for (size_t i = 0; i < len; i++) data[i] = _buf[(_head + offset + i) % N];
118 return len;
119 }
120
122 size_t contiguous(size_t offset, size_t len, const uint8_t *&p) const {
123 if (offset >= _len) return 0;
124 if (len > _len - offset) len = _len - offset;
125 size_t pos = (_head + offset) % N;
126 if (len > N - pos) len = N - pos;
127 p = _buf + pos;
128 return len;
129 }
130
132 void discard(size_t len) {
133 if (len > _len) len = _len;
134 _head = (_head + len) % N;
135 _len -= len;
136 if (_len == 0) _head = 0;
137 }
138
139 private:
140 uint8_t _buf[N];
141 size_t _head = 0;
142 size_t _len = 0;
143};
144
147class Checksum {
148 public:
150 void add(const uint8_t *p, size_t len) {
151 for (size_t i = 0; i < len; i++) {
152 _sum += _odd ? p[i] : (uint32_t)p[i] << 8;
153 _odd = !_odd;
154 }
155 }
157 void add16(uint16_t v) {
158 uint8_t b[2] = {(uint8_t)(v >> 8), (uint8_t)v};
159 add(b, 2);
160 }
162 uint16_t result() const {
163 uint32_t s = _sum;
164 while (s >> 16) s = (s & 0xFFFF) + (s >> 16);
165 return (uint16_t)~s;
166 }
167
168 private:
169 uint32_t _sum = 0;
170 bool _odd = false;
171};
172
174enum class TCPState : uint8_t {
175 Closed,
176 SynSent,
177 SynRcvd,
179 FinWait1,
180 FinWait2,
181 CloseWait,
182 Closing,
183 LastAck,
184 TimeWait,
185};
186
195class IPStack {
196 public:
197 static constexpr uint8_t IP_ICMP = 1;
198 static constexpr uint8_t IP_TCP = 6;
199 static constexpr uint8_t IP_UDP = 17;
200
201 static constexpr uint8_t TCP_FIN = 0x01;
202 static constexpr uint8_t TCP_SYN = 0x02;
203 static constexpr uint8_t TCP_RST = 0x04;
204 static constexpr uint8_t TCP_PSH = 0x08;
205 static constexpr uint8_t TCP_ACK = 0x10;
206
208 if (current() == nullptr) current() = this;
209 }
210 virtual ~IPStack() {
211 if (current() == this) current() = nullptr;
212 }
213
215 static IPStack *&current() {
216 static IPStack *stack = nullptr;
217 return stack;
218 }
219
221 void poll() {
222 if (_inPoll) return;
223 _inPoll = true;
224 ipPoll();
225 processTimers();
226 flushAcks();
227 _inPoll = false;
228 }
229
231 using IdleCallback = void (*)(void *ref);
232
235 void onIdle(IdleCallback cb, void *ref = nullptr) {
236 _idle = cb;
237 _idleRef = ref;
238 }
239
241 void setRespondToPing(bool flag) { _ping = flag; }
242
244 bool hostByName(const char *host, IPAddress &result,
245 uint32_t timeoutMs = 5000) {
246 if (result.fromString(host)) return true;
247 if (!ipIsUp()) return false;
248 for (int n = 0; n < 2; n++) {
249 ip4_t server = ipDns(n);
250 if (server == 0) continue;
251 if (dnsQuery(server, host, timeoutMs / 2, result)) return true;
252 }
253 return false;
254 }
255
256 // ---- TCP socket API (used by PPPClient / PPPServer) ----
257
259 int tcpConnect(ip4_t ip, uint16_t port, uint32_t timeoutMs) {
260 poll();
261 if (!ipIsUp()) return -1;
262 int s = allocTcp();
263 if (s < 0) return -1;
264 TCPSocket &t = _tcp[s];
265 t.remoteIP = ip;
266 t.remotePort = port;
267 t.localPort = ephemeralPort();
268 t.state = TCPState::SynSent;
269 t.accepted = true;
270 sendSyn(t);
271 uint32_t start = millis();
272 while (t.state == TCPState::SynSent) {
273 if (millis() - start >= timeoutMs) {
274 sendReset(t);
275 freeTcp(t);
276 return -1;
277 }
278 idle();
279 }
280 if (t.state != TCPState::Established && t.state != TCPState::CloseWait) {
281 freeTcp(t);
282 return -1;
283 }
284 return s;
285 }
286
288 bool tcpValid(int s, uint8_t gen) const {
289 return s >= 0 && s < TINYPPP_TCP_SOCKETS && _tcp[s].inUse &&
290 !_tcp[s].orphan && _tcp[s].gen == gen;
291 }
293 uint8_t tcpGen(int s) const { return _tcp[s].gen; }
295 TCPState tcpState(int s) const { return _tcp[s].state; }
297 uint16_t tcpLocalPort(int s) const { return _tcp[s].localPort; }
299 uint16_t tcpRemotePort(int s) const { return _tcp[s].remotePort; }
301 ip4_t tcpRemoteIP(int s) const { return _tcp[s].remoteIP; }
302
304 int tcpAvailable(int s) {
305 poll();
306 return (int)_tcp[s].rx.available();
307 }
308
310 int tcpPeek(int s) {
311 poll();
312 return _tcp[s].rx.peek();
313 }
314
316 int tcpRead(int s, uint8_t *data, size_t len) {
317 poll();
318 TCPSocket &t = _tcp[s];
319 size_t n = t.rx.read(data, len);
320 if (n > 0) windowUpdate(t);
321 return (int)n;
322 }
323
325 size_t tcpWrite(int s, const uint8_t *data, size_t len, uint32_t timeoutMs) {
326 TCPSocket &t = _tcp[s];
327 uint8_t gen = t.gen;
328 size_t done = 0;
329 uint32_t start = millis();
330 while (done < len) {
331 poll();
332 if (!t.inUse || t.gen != gen || !canSend(t)) break;
333 size_t n = t.tx.write(data + done, len - done);
334 if (n > 0) {
335 done += n;
336 output(t);
337 start = millis();
338 } else {
339 if (millis() - start >= timeoutMs) break;
340 idle();
341 }
342 }
343 return done;
344 }
345
347 void tcpFlush(int s, uint32_t timeoutMs) {
348 TCPSocket &t = _tcp[s];
349 uint8_t gen = t.gen;
350 uint32_t start = millis();
351 while (t.inUse && t.gen == gen && t.tx.available() > 0 && canSend(t) &&
352 millis() - start < timeoutMs) {
353 idle();
354 }
355 }
356
359 void tcpClose(int s, uint32_t timeoutMs) {
360 TCPSocket &t = _tcp[s];
361 uint8_t gen = t.gen;
362 t.rx.clear();
363 switch (t.state) {
366 t.finQueued = true;
367 output(t);
368 break;
371 sendReset(t);
372 freeTcp(t);
373 return;
374 case TCPState::Closed:
375 freeTcp(t);
376 return;
377 default:
378 break;
379 }
380 uint32_t start = millis();
381 while (t.inUse && t.gen == gen && millis() - start < timeoutMs &&
382 t.state != TCPState::Closed && t.state != TCPState::TimeWait &&
383 t.state != TCPState::FinWait2) {
384 idle();
385 }
386 if (!t.inUse || t.gen != gen) return;
387 if (t.state == TCPState::Closed) {
388 freeTcp(t);
389 return;
390 }
391 t.orphan = true;
392 if (t.state != TCPState::TimeWait) t.deadline = millis() + TINYPPP_TCP_LINGER;
393 }
394
396 bool tcpListen(uint16_t port) {
397 for (auto &l : _listeners)
398 if (l == port) return true;
399 for (auto &l : _listeners) {
400 if (l == 0) {
401 l = port;
402 return true;
403 }
404 }
405 return false;
406 }
407
409 void tcpUnlisten(uint16_t port) {
410 for (auto &l : _listeners)
411 if (l == port) l = 0;
412 }
413
416 int tcpAccept(uint16_t port, bool withData) {
417 poll();
418 for (int i = 0; i < TINYPPP_TCP_SOCKETS; i++) {
419 TCPSocket &t = _tcp[i];
420 if (!t.inUse || t.orphan || t.localPort != port) continue;
421 if (t.state == TCPState::SynRcvd || t.state == TCPState::SynSent) continue;
422 if (withData) {
423 if (t.rx.available() == 0) continue;
424 } else if (t.accepted) {
425 continue;
426 }
427 t.accepted = true;
428 return i;
429 }
430 return -1;
431 }
432
434 size_t tcpWriteAll(uint16_t port, const uint8_t *data, size_t len,
435 uint32_t timeoutMs) {
436 size_t result = 0;
437 for (int i = 0; i < TINYPPP_TCP_SOCKETS; i++) {
438 TCPSocket &t = _tcp[i];
439 if (t.inUse && !t.orphan && t.accepted && t.localPort == port &&
440 canSend(t))
441 result = tcpWrite(i, data, len, timeoutMs);
442 }
443 return result;
444 }
445
446 // ---- UDP socket API (used by PPPUDP) ----
447
449 int udpOpen(uint16_t port) {
450 if (port == 0) port = ephemeralPort();
451 for (int i = 0; i < TINYPPP_UDP_SOCKETS; i++) {
452 if (_udp[i].inUse && _udp[i].localPort == port) return -1;
453 }
454 for (int i = 0; i < TINYPPP_UDP_SOCKETS; i++) {
455 UDPSocket &u = _udp[i];
456 if (!u.inUse) {
457 u.inUse = true;
458 u.localPort = port;
459 u.rx.clear();
460 u.rxRemaining = 0;
461 return i;
462 }
463 }
464 return -1;
465 }
466
468 void udpClose(int s) {
469 _udp[s].inUse = false;
470 _udp[s].rx.clear();
471 _udp[s].rxRemaining = 0;
472 }
473
475 uint16_t udpLocalPort(int s) const { return _udp[s].localPort; }
476
478 bool udpSend(int s, ip4_t ip, uint16_t port, const uint8_t *data,
479 size_t len) {
480 poll();
481 return sendUdp(_udp[s].localPort, ip, port, data, len);
482 }
483
485 int udpParsePacket(int s, ip4_t &ip, uint16_t &port) {
486 poll();
487 UDPSocket &u = _udp[s];
488 u.rx.discard(u.rxRemaining);
489 u.rxRemaining = 0;
490 if (u.rx.available() < 8) return 0;
491 uint8_t hdr[8];
492 u.rx.read(hdr, 8);
493 memcpy(&ip, hdr, 4);
494 port = get16(hdr + 4);
495 u.rxRemaining = get16(hdr + 6);
496 return (int)u.rxRemaining;
497 }
498
500 int udpAvailable(int s) const { return (int)_udp[s].rxRemaining; }
501
503 int udpRead(int s, uint8_t *data, size_t len) {
504 UDPSocket &u = _udp[s];
505 if (len > u.rxRemaining) len = u.rxRemaining;
506 size_t n = u.rx.read(data, len);
507 u.rxRemaining -= n;
508 return (int)n;
509 }
510
512 int udpPeek(int s) const {
513 return _udp[s].rxRemaining > 0 ? _udp[s].rx.peek() : -1;
514 }
515
517 void udpDiscard(int s) {
518 UDPSocket &u = _udp[s];
519 u.rx.discard(u.rxRemaining);
520 u.rxRemaining = 0;
521 }
522
523 protected:
524 // ---- interface to the link layer ----
525
527 virtual bool ipBegin() = 0;
529 virtual void ipWrite(const uint8_t *data, size_t len) = 0;
531 virtual bool ipEnd() = 0;
533 virtual void ipPoll() = 0;
534 virtual bool ipIsUp() const = 0;
535 virtual ip4_t ipLocal() const = 0;
536 virtual ip4_t ipDns(int n) const = 0;
537 virtual uint16_t ipMtu() const = 0;
538
542 bool ipInput(const uint8_t *data, size_t len, bool reply = true) {
543 if (len < 20 || (data[0] >> 4) != 4) return false;
544 size_t ihl = (data[0] & 0x0F) * 4;
545 size_t total = get16(data + 2);
546 if (ihl < 20 || total > len || total < ihl) return false;
547 ip4_t dst;
548 memcpy(&dst, data + 16, 4);
549 if (dst != ipLocal()) return false;
550 if ((get16(data + 6) & 0x3FFF) != 0) return false; // fragment
551 Checksum c;
552 c.add(data, ihl);
553 if (c.result() != 0) return false;
554 ip4_t src;
555 memcpy(&src, data + 12, 4);
556 const uint8_t *payload = data + ihl;
557 size_t plen = total - ihl;
558 switch (data[9]) {
559 case IP_ICMP:
560 return icmpInput(data, total, ihl);
561 case IP_UDP:
562 return udpInput(src, dst, payload, plen);
563 case IP_TCP:
564 return tcpInput(src, dst, payload, plen, reply);
565 default:
566 return false;
567 }
568 }
569
571 void ipLinkDown() {
572 for (auto &t : _tcp) {
573 if (!t.inUse) continue;
574 t.state = TCPState::Closed;
575 t.timer = 0;
576 if (t.orphan || !t.accepted) freeTcp(t);
577 }
578 }
579
580 private:
581 struct TCPSocket {
582 bool inUse = false;
583 bool orphan = false; // closed by the user, finishing in background
584 bool accepted = false; // handed out to the user
585 bool ackPending = false;
586 bool finQueued = false; // send FIN after the queued data
587 bool finSent = false;
588 uint8_t gen = 0;
589 uint8_t retries = 0;
591 uint16_t localPort = 0;
592 uint16_t remotePort = 0;
593 ip4_t remoteIP = 0;
594 uint32_t iss = 0;
595 uint32_t sndUna = 0;
596 uint32_t sndNxt = 0;
597 uint32_t rcvNxt = 0;
598 uint32_t sndWnd = 0;
599 uint16_t mss = 536;
600 uint16_t advWnd = 0; // last advertised window
601 uint32_t rto = TINYPPP_TCP_RTO;
602 uint32_t timer = 0; // retransmission deadline, 0 = stopped
603 uint32_t deadline = 0; // TIME_WAIT / linger deadline, 0 = none
605 RingBuffer<TINYPPP_TCP_TX_BUFFER> tx; // unacknowledged and unsent data
606 };
607
608 struct UDPSocket {
609 bool inUse = false;
610 uint16_t localPort = 0;
611 size_t rxRemaining = 0;
612 // datagrams: ip(4) port(2) len(2) data
613 RingBuffer<TINYPPP_UDP_RX_BUFFER> rx;
614 };
615
616 TCPSocket _tcp[TINYPPP_TCP_SOCKETS];
617 UDPSocket _udp[TINYPPP_UDP_SOCKETS];
618 uint16_t _listeners[TINYPPP_TCP_LISTENERS] = {0};
619 uint16_t _nextPort = 0;
620 uint16_t _ipId = 0;
621 bool _ping = true;
622 bool _inPoll = false;
623 IdleCallback _idle = nullptr;
624 void *_idleRef = nullptr;
625
626 // DNS request in progress
627 uint16_t _dnsPort = 0;
628 uint16_t _dnsId = 0;
629 bool _dnsDone = false;
630 ip4_t _dnsResult = 0;
631
632 static uint16_t get16(const uint8_t *p) {
633 return (uint16_t)((p[0] << 8) | p[1]);
634 }
635 static uint32_t get32(const uint8_t *p) {
636 return (uint32_t)p[0] << 24 | (uint32_t)p[1] << 16 | (uint32_t)p[2] << 8 |
637 p[3];
638 }
639 static void put16(uint8_t *p, uint16_t v) {
640 p[0] = v >> 8;
641 p[1] = v & 0xFF;
642 }
643 static void put32(uint8_t *p, uint32_t v) {
644 p[0] = v >> 24;
645 p[1] = (v >> 16) & 0xFF;
646 p[2] = (v >> 8) & 0xFF;
647 p[3] = v & 0xFF;
648 }
649 // sequence number comparison
650 static bool seqLT(uint32_t a, uint32_t b) { return (int32_t)(a - b) < 0; }
651 static bool seqGT(uint32_t a, uint32_t b) { return (int32_t)(a - b) > 0; }
652
653 void idle() {
654 if (_idle) _idle(_idleRef);
655 poll();
656 delay(1);
657 }
658
659 uint16_t ephemeralPort() {
660 if (_nextPort == 0) _nextPort = 49152 + (uint16_t)(random(16000));
661 for (int tries = 0; tries < 100; tries++) {
662 uint16_t p = _nextPort++;
663 if (_nextPort < 49152) _nextPort = 49152;
664 if (!portInUse(p)) return p;
665 }
666 return _nextPort;
667 }
668
669 bool portInUse(uint16_t port) const {
670 for (const auto &t : _tcp)
671 if (t.inUse && t.localPort == port) return true;
672 for (const auto &u : _udp)
673 if (u.inUse && u.localPort == port) return true;
674 return port == _dnsPort;
675 }
676
677 bool listening(uint16_t port) const {
678 for (auto l : _listeners)
679 if (l != 0 && l == port) return true;
680 return false;
681 }
682
683 // ---- IP output ----
684
687 bool sendIP(uint8_t proto, ip4_t dst, const uint8_t *hdr, size_t hdrLen,
688 const uint8_t *d1, size_t n1, const uint8_t *d2 = nullptr,
689 size_t n2 = 0) {
690 uint8_t ip[20];
691 ip[0] = 0x45;
692 ip[1] = 0;
693 put16(ip + 2, (uint16_t)(20 + hdrLen + n1 + n2));
694 put16(ip + 4, _ipId++);
695 put16(ip + 6, 0x4000); // don't fragment
696 ip[8] = 64;
697 ip[9] = proto;
698 ip[10] = ip[11] = 0;
699 ip4_t src = ipLocal();
700 memcpy(ip + 12, &src, 4);
701 memcpy(ip + 16, &dst, 4);
702 Checksum c;
703 c.add(ip, 20);
704 put16(ip + 10, c.result());
705 if (!ipBegin()) return false;
706 ipWrite(ip, 20);
707 ipWrite(hdr, hdrLen);
708 if (n1 > 0) ipWrite(d1, n1);
709 if (n2 > 0) ipWrite(d2, n2);
710 return ipEnd();
711 }
712
713 void pseudoHeader(Checksum &c, uint8_t proto, ip4_t src, ip4_t dst,
714 size_t len) {
715 c.add((const uint8_t *)&src, 4);
716 c.add((const uint8_t *)&dst, 4);
717 c.add16(proto);
718 c.add16((uint16_t)len);
719 }
720
721 // ---- ICMP ----
722
723 bool icmpInput(const uint8_t *data, size_t total, size_t ihl) {
724 if (total < ihl + 8 || data[ihl] != 8) return false; // echo request only
725 if (!_ping) return false;
726 const uint8_t *icmp = data + ihl;
727 size_t len = total - ihl;
728 Checksum c;
729 c.add(icmp, len);
730 if (c.result() != 0) return true;
731 // echo reply: same content, type 0, new checksum
732 uint8_t hdr[4] = {0, 0, 0, 0};
733 Checksum r;
734 r.add(hdr, 4);
735 r.add(icmp + 4, len - 4);
736 put16(hdr + 2, r.result());
737 ip4_t src;
738 memcpy(&src, data + 12, 4);
739 sendIP(IP_ICMP, src, hdr, 4, icmp + 4, len - 4);
740 return true;
741 }
742
743 // ---- UDP ----
744
745 bool sendUdp(uint16_t srcPort, ip4_t dst, uint16_t dstPort,
746 const uint8_t *data, size_t len) {
747 if (!ipIsUp() || len + 28 > ipMtu()) return false;
748 uint8_t hdr[8];
749 put16(hdr, srcPort);
750 put16(hdr + 2, dstPort);
751 put16(hdr + 4, (uint16_t)(len + 8));
752 put16(hdr + 6, 0);
753 Checksum c;
754 pseudoHeader(c, IP_UDP, ipLocal(), dst, len + 8);
755 c.add(hdr, 8);
756 c.add(data, len);
757 uint16_t sum = c.result();
758 put16(hdr + 6, sum == 0 ? 0xFFFF : sum);
759 return sendIP(IP_UDP, dst, hdr, 8, data, len);
760 }
761
762 bool udpInput(ip4_t src, ip4_t dst, const uint8_t *p, size_t len) {
763 if (len < 8) return false;
764 size_t ulen = get16(p + 4);
765 if (ulen < 8 || ulen > len) return false;
766 if (get16(p + 6) != 0) {
767 Checksum c;
768 pseudoHeader(c, IP_UDP, src, dst, ulen);
769 c.add(p, ulen);
770 if (c.result() != 0) return true; // corrupted: drop
771 }
772 uint16_t srcPort = get16(p);
773 uint16_t dstPort = get16(p + 2);
774 const uint8_t *data = p + 8;
775 size_t dlen = ulen - 8;
776 if (_dnsPort != 0 && dstPort == _dnsPort) {
777 dnsInput(data, dlen);
778 return true;
779 }
780 for (auto &u : _udp) {
781 if (!u.inUse || u.localPort != dstPort) continue;
782 if (u.rx.free() >= dlen + 8) {
783 uint8_t hdr[8];
784 memcpy(hdr, &src, 4);
785 put16(hdr + 4, srcPort);
786 put16(hdr + 6, (uint16_t)dlen);
787 u.rx.write(hdr, 8);
788 u.rx.write(data, dlen);
789 }
790 return true;
791 }
792 return false;
793 }
794
795 // ---- DNS ----
796
797 bool dnsQuery(ip4_t server, const char *host, uint32_t timeoutMs,
798 IPAddress &result) {
799 uint8_t q[300];
800 size_t n = 0;
801 _dnsId = (uint16_t)random(0xFFFF);
802 put16(q, _dnsId);
803 put16(q + 2, 0x0100); // recursion desired
804 put16(q + 4, 1); // one question
805 memset(q + 6, 0, 6);
806 n = 12;
807 const char *label = host;
808 while (*label) {
809 const char *dot = strchr(label, '.');
810 size_t l = dot ? (size_t)(dot - label) : strlen(label);
811 if (l == 0 || l > 63 || n + l + 6 > sizeof(q)) return false;
812 q[n++] = (uint8_t)l;
813 memcpy(q + n, label, l);
814 n += l;
815 label += l;
816 if (*label == '.') label++;
817 }
818 q[n++] = 0;
819 put16(q + n, 1); // type A
820 put16(q + n + 2, 1); // class IN
821 n += 4;
822
823 _dnsPort = ephemeralPort();
824 _dnsDone = false;
825 _dnsResult = 0;
826 bool ok = false;
827 uint32_t start = millis();
828 uint32_t sent = 0;
829 while (millis() - start < timeoutMs) {
830 if (sent == 0 || millis() - sent >= 1000) {
831 sendUdp(_dnsPort, server, 53, q, n);
832 sent = millis();
833 if (sent == 0) sent = 1;
834 }
835 idle();
836 if (_dnsDone) {
837 ok = _dnsResult != 0;
838 break;
839 }
840 }
841 _dnsPort = 0;
842 if (ok) {
843 const uint8_t *b = (const uint8_t *)&_dnsResult;
844 result = IPAddress(b[0], b[1], b[2], b[3]);
845 }
846 return ok;
847 }
848
850 static size_t dnsSkipName(const uint8_t *p, size_t len, size_t pos) {
851 while (pos < len) {
852 uint8_t l = p[pos];
853 if (l == 0) return pos + 1;
854 if ((l & 0xC0) == 0xC0) return pos + 2;
855 pos += l + 1;
856 }
857 return 0;
858 }
859
860 void dnsInput(const uint8_t *p, size_t len) {
861 if (len < 12 || get16(p) != _dnsId || !(p[2] & 0x80)) return;
862 _dnsDone = true;
863 if ((p[3] & 0x0F) != 0) return; // error
864 size_t qd = get16(p + 4), an = get16(p + 6);
865 size_t pos = 12;
866 for (size_t i = 0; i < qd; i++) {
867 pos = dnsSkipName(p, len, pos);
868 if (pos == 0) return;
869 pos += 4;
870 }
871 for (size_t i = 0; i < an; i++) {
872 pos = dnsSkipName(p, len, pos);
873 if (pos == 0 || pos + 10 > len) return;
874 uint16_t type = get16(p + pos), cls = get16(p + pos + 2);
875 size_t rdlen = get16(p + pos + 8);
876 pos += 10;
877 if (pos + rdlen > len) return;
878 if (type == 1 && cls == 1 && rdlen == 4) {
879 memcpy(&_dnsResult, p + pos, 4);
880 return;
881 }
882 pos += rdlen;
883 }
884 }
885
886 // ---- TCP ----
887
888 int allocTcp() {
889 int found = -1;
890 for (int i = 0; i < TINYPPP_TCP_SOCKETS && found < 0; i++)
891 if (!_tcp[i].inUse) found = i;
892 // reuse a socket that is closing in the background
893 for (int i = 0; i < TINYPPP_TCP_SOCKETS && found < 0; i++)
894 if (_tcp[i].orphan && _tcp[i].state == TCPState::TimeWait) found = i;
895 for (int i = 0; i < TINYPPP_TCP_SOCKETS && found < 0; i++) {
896 if (_tcp[i].orphan) {
897 sendReset(_tcp[i]);
898 found = i;
899 }
900 }
901 if (found < 0) return -1;
902 TCPSocket &t = _tcp[found];
903 uint8_t gen = t.gen + 1;
904 t.inUse = true;
905 t.orphan = t.accepted = t.ackPending = t.finQueued = t.finSent = false;
906 t.gen = gen;
907 t.retries = 0;
908 t.state = TCPState::Closed;
909 t.rx.clear();
910 t.tx.clear();
911 t.iss = (uint32_t)micros() * 2654435761u ^ (uint32_t)random(0x7FFFFFFF);
912 t.sndUna = t.iss;
913 t.sndNxt = t.iss + 1;
914 t.rcvNxt = 0;
915 t.sndWnd = 0;
916 uint16_t mtu = ipMtu();
917 t.mss = mtu > 40 ? mtu - 40 : 536;
918 t.rto = TINYPPP_TCP_RTO;
919 t.timer = t.deadline = 0;
920 t.advWnd = 0;
921 return found;
922 }
923
924 void freeTcp(TCPSocket &t) {
925 t.inUse = false;
926 t.orphan = false;
927 t.state = TCPState::Closed;
928 t.timer = t.deadline = 0;
929 t.rx.clear();
930 t.tx.clear();
931 }
932
933 static bool canSend(const TCPSocket &t) {
934 return (t.state == TCPState::Established ||
935 t.state == TCPState::CloseWait) &&
936 !t.finQueued;
937 }
938
939 uint16_t window(TCPSocket &t) {
940 size_t w = t.rx.free();
941 if (w > 0xFFFF) w = 0xFFFF;
942 t.advWnd = (uint16_t)w;
943 return (uint16_t)w;
944 }
945
947 uint16_t rcvMss() const {
948 uint16_t mtu = ipMtu();
949 uint16_t mss = mtu > 40 ? mtu - 40 : 536;
951 return mss;
952 }
953
955 bool sendSegment(TCPSocket &t, uint32_t seq, uint8_t flags, size_t offset,
956 size_t len) {
957 const uint8_t *p1 = nullptr, *p2 = nullptr;
958 size_t n1 = len > 0 ? t.tx.contiguous(offset, len, p1) : 0;
959 size_t n2 = n1 < len ? t.tx.contiguous(offset + n1, len - n1, p2) : 0;
960 if (n1 + n2 != len) return false;
961 t.ackPending = false;
962 return sendTcp(t.localPort, t.remotePort, t.remoteIP, seq, t.rcvNxt, flags,
963 window(t), p1, n1, p2, n2);
964 }
965
966 bool sendTcp(uint16_t localPort, uint16_t remotePort, ip4_t remoteIP,
967 uint32_t seq, uint32_t ack, uint8_t flags, uint16_t wnd,
968 const uint8_t *p1 = nullptr, size_t n1 = 0,
969 const uint8_t *p2 = nullptr, size_t n2 = 0) {
970 uint8_t hdr[24];
971 size_t hlen = (flags & TCP_SYN) ? 24 : 20;
972 put16(hdr, localPort);
973 put16(hdr + 2, remotePort);
974 put32(hdr + 4, seq);
975 put32(hdr + 8, (flags & TCP_ACK) ? ack : 0);
976 hdr[12] = (uint8_t)((hlen / 4) << 4);
977 hdr[13] = flags;
978 put16(hdr + 14, wnd);
979 put16(hdr + 16, 0);
980 put16(hdr + 18, 0);
981 if (hlen == 24) {
982 hdr[20] = 2; // MSS option
983 hdr[21] = 4;
984 put16(hdr + 22, rcvMss());
985 }
986 Checksum c;
987 pseudoHeader(c, IP_TCP, ipLocal(), remoteIP, hlen + n1 + n2);
988 c.add(hdr, hlen);
989 if (n1) c.add(p1, n1);
990 if (n2) c.add(p2, n2);
991 put16(hdr + 16, c.result());
992 return sendIP(IP_TCP, remoteIP, hdr, hlen, p1, n1, p2, n2);
993 }
994
995 void armTimer(TCPSocket &t) {
996 t.timer = millis() + t.rto;
997 if (t.timer == 0) t.timer = 1;
998 }
999
1000 void sendSyn(TCPSocket &t) {
1001 uint8_t flags = TCP_SYN;
1002 if (t.state == TCPState::SynRcvd) flags |= TCP_ACK;
1003 sendSegment(t, t.iss, flags, 0, 0);
1004 armTimer(t);
1005 }
1006
1007 void sendAck(TCPSocket &t) { sendSegment(t, t.sndNxt, TCP_ACK, 0, 0); }
1008
1009 void sendReset(TCPSocket &t) {
1010 if (t.state == TCPState::Closed || t.state == TCPState::TimeWait) return;
1011 sendSegment(t, t.sndNxt, TCP_RST | TCP_ACK, 0, 0);
1012 }
1013
1015 void replyReset(ip4_t src, const uint8_t *p, size_t segLen) {
1016 uint8_t flags = p[13];
1017 if (flags & TCP_RST) return;
1018 if (flags & TCP_ACK) {
1019 sendTcp(get16(p + 2), get16(p), src, get32(p + 8), 0, TCP_RST, 0);
1020 } else {
1021 sendTcp(get16(p + 2), get16(p), src, 0, get32(p + 4) + segLen,
1022 TCP_RST | TCP_ACK, 0);
1023 }
1024 }
1025
1027 void output(TCPSocket &t) {
1028 if (t.state != TCPState::Established && t.state != TCPState::CloseWait &&
1029 t.state != TCPState::FinWait1 && t.state != TCPState::LastAck &&
1030 t.state != TCPState::Closing)
1031 return;
1032 size_t queued = t.tx.available();
1033 while (!t.finSent) {
1034 size_t offset = t.sndNxt - t.sndUna;
1035 if (offset >= queued) break;
1036 uint32_t inFlight = t.sndNxt - t.sndUna;
1037 if (inFlight >= t.sndWnd) break;
1038 size_t len = queued - offset;
1039 if (len > t.mss) len = t.mss;
1040 if (len > t.sndWnd - inFlight) len = t.sndWnd - inFlight;
1041 if (!sendSegment(t, t.sndNxt, TCP_ACK | TCP_PSH, offset, len)) break;
1042 t.sndNxt += len;
1043 if (t.timer == 0) armTimer(t);
1044 }
1045 if (t.finQueued && !t.finSent && t.sndNxt - t.sndUna == queued) {
1046 sendSegment(t, t.sndNxt, TCP_FIN | TCP_ACK, 0, 0);
1047 t.sndNxt++;
1048 t.finSent = true;
1049 if (t.state == TCPState::Established) t.state = TCPState::FinWait1;
1050 else if (t.state == TCPState::CloseWait) t.state = TCPState::LastAck;
1051 if (t.timer == 0) armTimer(t);
1052 }
1053 // zero window: the retransmission timer sends probes
1054 if (t.timer == 0 && queued > 0 && t.sndWnd == 0) armTimer(t);
1055 }
1056
1058 void retransmit(TCPSocket &t) {
1059 switch (t.state) {
1060 case TCPState::SynSent:
1061 case TCPState::SynRcvd:
1062 sendSyn(t);
1063 return;
1064 default:
1065 break;
1066 }
1067 size_t queued = t.tx.available();
1068 if (queued > 0) {
1069 size_t len = queued < t.mss ? queued : t.mss;
1070 // one byte probes into a zero window
1071 if (t.sndWnd == 0) len = 1;
1072 sendSegment(t, t.sndUna, TCP_ACK | TCP_PSH, 0, len);
1073 if (seqLT(t.sndNxt, t.sndUna + len)) t.sndNxt = t.sndUna + len;
1074 } else if (t.finSent) {
1075 sendSegment(t, t.sndUna, TCP_FIN | TCP_ACK, 0, 0);
1076 } else {
1077 t.timer = 0;
1078 return;
1079 }
1080 armTimer(t);
1081 }
1082
1083 void windowUpdate(TCPSocket &t) {
1084 if (t.state != TCPState::Established && t.state != TCPState::FinWait1 &&
1085 t.state != TCPState::FinWait2)
1086 return;
1087 size_t free = t.rx.free();
1088 size_t threshold = t.rx.size() / 2;
1089 if (threshold > rcvMss()) threshold = rcvMss();
1090 if (t.advWnd < threshold && free >= threshold) sendAck(t);
1091 }
1092
1093 void processTimers() {
1094 uint32_t now = millis();
1095 for (auto &t : _tcp) {
1096 if (!t.inUse) continue;
1097 if (t.deadline != 0 && (int32_t)(now - t.deadline) >= 0) {
1098 if (t.orphan) {
1099 if (t.state != TCPState::TimeWait) sendReset(t);
1100 freeTcp(t);
1101 continue;
1102 }
1103 t.deadline = 0;
1104 t.state = TCPState::Closed;
1105 }
1106 if (t.timer != 0 && (int32_t)(now - t.timer) >= 0) {
1107 if (++t.retries > TINYPPP_TCP_MAX_RETRIES) {
1108 sendReset(t);
1109 t.state = TCPState::Closed;
1110 t.timer = 0;
1111 if (t.orphan || !t.accepted) freeTcp(t);
1112 continue;
1113 }
1114 t.rto = t.rto * 2 > 16000 ? 16000 : t.rto * 2;
1115 retransmit(t);
1116 }
1117 }
1118 }
1119
1120 void flushAcks() {
1121 for (auto &t : _tcp) {
1122 if (t.inUse && t.ackPending) sendAck(t);
1123 }
1124 }
1125
1126 void enterTimeWait(TCPSocket &t) {
1127 t.state = TCPState::TimeWait;
1128 t.timer = 0;
1129 t.deadline = millis() + TINYPPP_TCP_TIME_WAIT;
1130 if (t.deadline == 0) t.deadline = 1;
1131 }
1132
1133 void parseOptions(TCPSocket &t, const uint8_t *opt, size_t len) {
1134 size_t i = 0;
1135 while (i < len) {
1136 uint8_t kind = opt[i];
1137 if (kind == 0) break;
1138 if (kind == 1) {
1139 i++;
1140 continue;
1141 }
1142 if (i + 1 >= len || opt[i + 1] < 2 || i + opt[i + 1] > len) break;
1143 if (kind == 2 && opt[i + 1] == 4) {
1144 uint16_t mss = get16(opt + i + 2);
1145 if (mss >= 64 && mss < t.mss) t.mss = mss;
1146 }
1147 i += opt[i + 1];
1148 }
1149 }
1150
1151 bool tcpInput(ip4_t src, ip4_t dst, const uint8_t *p, size_t len,
1152 bool reply) {
1153 if (len < 20) return false;
1154 size_t hlen = (p[12] >> 4) * 4;
1155 if (hlen < 20 || hlen > len) return false;
1156 Checksum c;
1157 pseudoHeader(c, IP_TCP, src, dst, len);
1158 c.add(p, len);
1159 if (c.result() != 0) return true; // corrupted: drop
1160
1161 uint16_t srcPort = get16(p), dstPort = get16(p + 2);
1162 uint32_t seq = get32(p + 4), ack = get32(p + 8);
1163 uint8_t flags = p[13];
1164 uint32_t wnd = get16(p + 14);
1165 const uint8_t *data = p + hlen;
1166 size_t dlen = len - hlen;
1167 size_t segLen = dlen + ((flags & TCP_SYN) ? 1 : 0) + ((flags & TCP_FIN) ? 1 : 0);
1168
1169 TCPSocket *ts = nullptr;
1170 for (auto &t : _tcp) {
1171 if (t.inUse && t.state != TCPState::Closed && t.localPort == dstPort &&
1172 t.remotePort == srcPort && t.remoteIP == src) {
1173 ts = &t;
1174 break;
1175 }
1176 }
1177
1178 if (ts == nullptr) {
1179 if ((flags & (TCP_SYN | TCP_ACK | TCP_RST)) == TCP_SYN &&
1180 listening(dstPort)) {
1181 int s = allocTcp();
1182 if (s < 0) return true; // no socket: the peer will retry
1183 TCPSocket &t = _tcp[s];
1184 t.localPort = dstPort;
1185 t.remotePort = srcPort;
1186 t.remoteIP = src;
1187 t.rcvNxt = seq + 1;
1188 t.sndWnd = wnd;
1189 parseOptions(t, p + 20, hlen - 20);
1190 t.state = TCPState::SynRcvd;
1191 sendSyn(t);
1192 return true;
1193 }
1194 if (!reply) return false;
1195 replyReset(src, p, segLen);
1196 return true;
1197 }
1198
1199 TCPSocket &t = *ts;
1200
1201 if (t.state == TCPState::SynSent) {
1202 bool ackOk = (flags & TCP_ACK) && ack == t.iss + 1;
1203 if ((flags & TCP_ACK) && !ackOk) {
1204 replyReset(src, p, segLen);
1205 return true;
1206 }
1207 if (flags & TCP_RST) {
1208 if (ackOk) t.state = TCPState::Closed; // connection refused
1209 return true;
1210 }
1211 if ((flags & TCP_SYN) && ackOk) {
1212 t.rcvNxt = seq + 1;
1213 t.sndUna = ack;
1214 t.sndWnd = wnd;
1215 parseOptions(t, p + 20, hlen - 20);
1216 t.state = TCPState::Established;
1217 t.timer = 0;
1218 t.retries = 0;
1219 t.rto = TINYPPP_TCP_RTO;
1220 sendAck(t);
1221 }
1222 return true;
1223 }
1224
1225 // sequence number check: in-order segments only
1226 if (flags & TCP_RST) {
1227 if (seq == t.rcvNxt) {
1228 t.state = TCPState::Closed;
1229 t.timer = 0;
1230 if (t.orphan || !t.accepted) freeTcp(t);
1231 }
1232 return true;
1233 }
1234 if (flags & TCP_SYN) {
1235 // retransmitted SYN: our SYN-ACK was lost
1236 if (t.state == TCPState::SynRcvd && seq + 1 == t.rcvNxt) sendSyn(t);
1237 else t.ackPending = true;
1238 return true;
1239 }
1240 if (segLen > 0 && seq != t.rcvNxt) {
1241 if (seqLT(seq, t.rcvNxt) && seqGT(seq + dlen, t.rcvNxt)) {
1242 // partly old: skip what we already have
1243 size_t skip = t.rcvNxt - seq;
1244 data += skip;
1245 dlen -= skip;
1246 seq = t.rcvNxt;
1247 } else {
1248 t.ackPending = true; // out of order or duplicate
1249 return true;
1250 }
1251 }
1252 if (!(flags & TCP_ACK)) return true;
1253
1254 if (t.state == TCPState::SynRcvd) {
1255 if (ack != t.iss + 1) {
1256 replyReset(src, p, segLen);
1257 return true;
1258 }
1259 t.state = TCPState::Established;
1260 t.sndUna = ack;
1261 t.sndWnd = wnd;
1262 t.timer = 0;
1263 t.retries = 0;
1264 t.rto = TINYPPP_TCP_RTO;
1265 }
1266
1267 // acknowledgment
1268 if (seqGT(ack, t.sndNxt)) {
1269 t.ackPending = true;
1270 return true;
1271 }
1272 t.sndWnd = wnd;
1273 bool finAcked = false;
1274 if (seqGT(ack, t.sndUna)) {
1275 size_t acked = ack - t.sndUna;
1276 size_t dataAcked = acked < t.tx.available() ? acked : t.tx.available();
1277 t.tx.discard(dataAcked);
1278 t.sndUna = ack;
1279 t.retries = 0;
1280 t.rto = TINYPPP_TCP_RTO;
1281 t.timer = 0;
1282 if (t.sndUna != t.sndNxt) armTimer(t);
1283 }
1284 if (t.finSent && t.sndUna == t.sndNxt) finAcked = true;
1285
1286 switch (t.state) {
1287 case TCPState::FinWait1:
1288 if (finAcked) {
1289 t.state = TCPState::FinWait2;
1290 if (t.orphan) t.deadline = millis() + TINYPPP_TCP_LINGER;
1291 }
1292 break;
1293 case TCPState::Closing:
1294 if (finAcked) enterTimeWait(t);
1295 break;
1296 case TCPState::LastAck:
1297 if (finAcked) {
1298 t.state = TCPState::Closed;
1299 t.timer = 0;
1300 if (t.orphan) freeTcp(t);
1301 return true;
1302 }
1303 break;
1304 case TCPState::TimeWait:
1305 if (flags & TCP_FIN) {
1306 t.ackPending = true;
1307 enterTimeWait(t);
1308 }
1309 return true;
1310 default:
1311 break;
1312 }
1313
1314 // data
1315 if (dlen > 0 && (t.state == TCPState::Established ||
1316 t.state == TCPState::FinWait1 ||
1317 t.state == TCPState::FinWait2)) {
1318 size_t n = t.orphan ? dlen : t.rx.write(data, dlen);
1319 t.rcvNxt += n;
1320 t.ackPending = true;
1321 if (n < dlen) flags &= ~TCP_FIN; // FIN comes after the dropped data
1322 }
1323
1324 if ((flags & TCP_FIN) && seq + dlen == t.rcvNxt) {
1325 t.rcvNxt++;
1326 t.ackPending = true;
1327 switch (t.state) {
1329 t.state = TCPState::CloseWait;
1330 break;
1331 case TCPState::FinWait1:
1332 if (finAcked) enterTimeWait(t);
1333 else t.state = TCPState::Closing;
1334 break;
1335 case TCPState::FinWait2:
1336 enterTimeWait(t);
1337 break;
1338 default:
1339 break;
1340 }
1341 }
1342
1343 output(t);
1344 return true;
1345 }
1346};
1347
1348} // namespace tinyppp
#define TINYPPP_TCP_SOCKETS
Definition IPStack.h:23
#define TINYPPP_UDP_SOCKETS
Definition IPStack.h:39
#define TINYPPP_TCP_LISTENERS
Definition IPStack.h:35
#define TINYPPP_TCP_MAX_RETRIES
Definition IPStack.h:56
#define TINYPPP_TCP_LINGER
Max time in ms a socket closed by stop() is kept to complete the close.
Definition IPStack.h:66
#define TINYPPP_TCP_RTO
Initial TCP retransmission timeout in ms (doubled on each retry)
Definition IPStack.h:52
#define TINYPPP_TCP_TIME_WAIT
Time in ms a closed socket stays in TIME_WAIT.
Definition IPStack.h:61
#define TINYPPP_TCP_RX_BUFFER
Definition IPStack.h:27
Internet checksum (RFC 1071) over several (possibly odd sized) pieces.
Definition IPStack.h:147
void add16(uint16_t v)
Adds a 16 bit value.
Definition IPStack.h:157
void add(const uint8_t *p, size_t len)
Adds data.
Definition IPStack.h:150
uint16_t result() const
The checksum of all added data.
Definition IPStack.h:162
Minimal IPv4 stack (ICMP echo, UDP, TCP, DNS resolver) for platforms without lwIP.
Definition IPStack.h:195
static constexpr uint8_t IP_ICMP
Definition IPStack.h:197
bool udpSend(int s, ip4_t ip, uint16_t port, const uint8_t *data, size_t len)
Sends a datagram.
Definition IPStack.h:478
int tcpRead(int s, uint8_t *data, size_t len)
Reads up to len received bytes from socket s.
Definition IPStack.h:316
static constexpr uint8_t TCP_SYN
Definition IPStack.h:202
uint16_t udpLocalPort(int s) const
Local port of UDP socket s.
Definition IPStack.h:475
bool ipInput(const uint8_t *data, size_t len, bool reply=true)
Definition IPStack.h:542
void(*)(void *ref) IdleCallback
Callback that is called while a socket method waits.
Definition IPStack.h:231
int tcpPeek(int s)
Next received byte of socket s without removing it (-1 if none)
Definition IPStack.h:310
virtual uint16_t ipMtu() const =0
virtual bool ipBegin()=0
Starts an outgoing IPv4 packet.
bool hostByName(const char *host, IPAddress &result, uint32_t timeoutMs=5000)
Resolves a host name (or a dotted IP address) via DNS.
Definition IPStack.h:244
void onIdle(IdleCallback cb, void *ref=nullptr)
Definition IPStack.h:235
virtual bool ipIsUp() const =0
bool tcpListen(uint16_t port)
Starts listening on a port.
Definition IPStack.h:396
void setRespondToPing(bool flag)
Answer ICMP echo requests addressed to us (default true)
Definition IPStack.h:241
virtual bool ipEnd()=0
Sends the packet.
virtual ~IPStack()
Definition IPStack.h:210
static constexpr uint8_t TCP_PSH
Definition IPStack.h:204
IPStack()
Definition IPStack.h:207
uint16_t tcpLocalPort(int s) const
Local port of socket s.
Definition IPStack.h:297
int udpOpen(uint16_t port)
Opens a UDP socket on the port (0: ephemeral): returns it or -1.
Definition IPStack.h:449
void ipLinkDown()
Called when the link goes down: all connections are reset.
Definition IPStack.h:571
int udpAvailable(int s) const
Unread bytes of the current datagram.
Definition IPStack.h:500
static IPStack *& current()
The stack used by sockets that were created without an explicit stack.
Definition IPStack.h:215
int udpPeek(int s) const
Next byte of the current datagram without removing it (-1 if none)
Definition IPStack.h:512
ip4_t tcpRemoteIP(int s) const
Remote address of socket s.
Definition IPStack.h:301
void udpDiscard(int s)
Discards the rest of the current datagram.
Definition IPStack.h:517
bool tcpValid(int s, uint8_t gen) const
true if socket s is open and still has the generation gen
Definition IPStack.h:288
int tcpConnect(ip4_t ip, uint16_t port, uint32_t timeoutMs)
Opens a connection: returns the socket number or -1.
Definition IPStack.h:259
virtual ip4_t ipDns(int n) const =0
void poll()
Processes the link (input and timers): called by all socket methods.
Definition IPStack.h:221
int udpParsePacket(int s, ip4_t &ip, uint16_t &port)
Moves to the next datagram: returns its size (0 if none)
Definition IPStack.h:485
int tcpAvailable(int s)
Number of received bytes that can be read from socket s.
Definition IPStack.h:304
int udpRead(int s, uint8_t *data, size_t len)
Reads from the current datagram.
Definition IPStack.h:503
uint16_t tcpRemotePort(int s) const
Remote port of socket s.
Definition IPStack.h:299
uint8_t tcpGen(int s) const
Generation of socket s: changes each time the socket is reused.
Definition IPStack.h:293
void tcpUnlisten(uint16_t port)
Stops listening on a port.
Definition IPStack.h:409
static constexpr uint8_t IP_TCP
Definition IPStack.h:198
size_t tcpWriteAll(uint16_t port, const uint8_t *data, size_t len, uint32_t timeoutMs)
Sends data to all connected clients of a server port.
Definition IPStack.h:434
virtual void ipPoll()=0
Processes link input: received packets are passed to ipInput()
TCPState tcpState(int s) const
Connection state of socket s.
Definition IPStack.h:295
static constexpr uint8_t TCP_ACK
Definition IPStack.h:205
size_t tcpWrite(int s, const uint8_t *data, size_t len, uint32_t timeoutMs)
Queues data for sending (waits for buffer space up to timeoutMs)
Definition IPStack.h:325
void tcpFlush(int s, uint32_t timeoutMs)
Waits until all queued data was acknowledged.
Definition IPStack.h:347
static constexpr uint8_t TCP_FIN
Definition IPStack.h:201
virtual void ipWrite(const uint8_t *data, size_t len)=0
Adds data to the outgoing packet.
int tcpAccept(uint16_t port, bool withData)
Definition IPStack.h:416
void udpClose(int s)
Closes UDP socket s.
Definition IPStack.h:468
static constexpr uint8_t TCP_RST
Definition IPStack.h:203
static constexpr uint8_t IP_UDP
Definition IPStack.h:199
virtual ip4_t ipLocal() const =0
void tcpClose(int s, uint32_t timeoutMs)
Definition IPStack.h:359
Byte ring buffer with a fixed capacity of N bytes.
Definition IPStack.h:76
size_t size() const
Capacity in bytes.
Definition IPStack.h:83
size_t peek(size_t offset, uint8_t *data, size_t len) const
Copies data at offset without removing it.
Definition IPStack.h:114
size_t contiguous(size_t offset, size_t len, const uint8_t *&p) const
Contiguous data at offset (max len): returns the number of bytes at p.
Definition IPStack.h:122
size_t read(uint8_t *data, size_t len)
Removes up to len bytes into data.
Definition IPStack.h:104
size_t available() const
Number of stored bytes.
Definition IPStack.h:79
int read()
Removes and returns one byte (-1 if empty)
Definition IPStack.h:96
size_t free() const
Free space in bytes.
Definition IPStack.h:81
void clear()
Removes all data.
Definition IPStack.h:85
void discard(size_t len)
Removes up to len bytes.
Definition IPStack.h:132
int peek() const
Returns the next byte without removing it (-1 if empty)
Definition IPStack.h:111
size_t write(const uint8_t *data, size_t len)
Appends data: returns the number of bytes stored.
Definition IPStack.h:88
TinyPPP internals: PPP engine, HDLC framing and the IP stack.
Definition HDLC.h:11
uint32_t ip4_t
IPv4 address in network byte order as stored in memory.
Definition IPStack.h:72
TCPState
TCP connection states (RFC 793)
Definition IPStack.h:174