TinyPPP
PPP networking over any Arduino Stream
Loading...
Searching...
No Matches
PPPEngine.h
Go to the documentation of this file.
1#pragma once
12#include <stdarg.h>
13#include <stdio.h>
14#include <string.h>
15
16#include <Arduino.h>
17
18#include "HDLC.h"
19
20#ifndef TINYPPP_MRU
21#define TINYPPP_MRU 1500
22#endif
23
24#ifndef TINYPPP_RESTART_MS
25#define TINYPPP_RESTART_MS 2000
26#endif
27
29namespace tinyppp {
30
32static constexpr uint16_t PPP_IP = 0x0021;
33static constexpr uint16_t PPP_IPV6 = 0x0057;
34static constexpr uint16_t PPP_LCP = 0xC021;
35static constexpr uint16_t PPP_PAP = 0xC023;
36static constexpr uint16_t PPP_IPCP = 0x8021;
37
52
62
70
72enum class CPState : uint8_t {
73 Initial, // not started / closed by the user
74 Stopped, // waiting for the peer (or the restart timer)
75 Closing, // Terminate-Request sent
76 ReqSent,
77 AckRcvd,
78 AckSent,
79 Opened,
80};
81
83enum class PPPPhase : uint8_t {
84 Dead,
87 Network,
88 Running,
90};
91
93inline const char *toStr(PPPPhase p) {
94 switch (p) {
95 case PPPPhase::Dead: return "Dead";
96 case PPPPhase::Establish: return "Establish";
97 case PPPPhase::Authenticate: return "Authenticate";
98 case PPPPhase::Network: return "Network";
99 case PPPPhase::Running: return "Running";
100 case PPPPhase::Terminate: return "Terminate";
101 }
102 return "?";
103}
104
106using ip4_t = uint32_t;
107
110 uint8_t v[4] = {a, b, c, d};
111 ip4_t r;
112 memcpy(&r, v, 4);
113 return r;
114}
115
125
135 public:
136 virtual ~PPPEngine() = default;
137
139 void setLocalIP(ip4_t ip) { _cfgLocal = ip; }
141 void setPeerIP(ip4_t ip) { _cfgPeer = ip; }
144 _cfgOfferDns1 = dns1;
145 _cfgOfferDns2 = dns2;
146 }
148 void setRequestDNS(bool flag) { _cfgRequestDns = flag; }
150 void setCredentials(const char *user, const char *password) {
151 snprintf(_user, sizeof(_user), "%s", user ? user : "");
152 snprintf(_password, sizeof(_password), "%s", password ? password : "");
153 }
155 void setPassive(bool passive) { _passive = passive; }
158 _echoInterval = ms;
159 _echoMaxFail = maxFailures;
160 }
162 void setLogger(Print *log) { _log = log; }
163
165 void start(Stream &io) {
166 _io = &io;
167 _enc.setOutput(&io);
168 _started = true;
169 resetLink();
170 _magic = newMagic();
171 setPhase(PPPPhase::Establish);
172 if (_passive) {
173 _lcp.state = CPState::Stopped;
174 } else {
175 lcpOpen();
176 }
177 }
178
180 void stop() {
181 if (!_started) return;
182 if (_lcp.state == CPState::Opened || _lcp.state == CPState::AckSent ||
183 _lcp.state == CPState::AckRcvd || _lcp.state == CPState::ReqSent) {
184 static const uint8_t reason[] = "bye";
185 sendCP(PPP_LCP, TERM_REQ, ++_lcp.id, reason, sizeof(reason) - 1);
186 }
187 ipcpDown();
188 _lcp.state = CPState::Initial;
189 _ipcp.state = CPState::Initial;
190 _lcp.timer = _ipcp.timer = _papTimer = 0;
191 _started = false;
192 setPhase(PPPPhase::Dead);
193 }
194
196 void process() {
197 if (!_started || _io == nullptr) return;
198 uint8_t buf[128];
199 // bound the work per call so that we do not block for too long
200 for (int loops = 0; loops < 32; loops++) {
201 int avail = _io->available();
202 if (avail <= 0) break;
203 size_t n = _io->readBytes(
204 buf, (size_t)avail < sizeof(buf) ? (size_t)avail : sizeof(buf));
205 if (n == 0) break;
206 for (size_t i = 0; i < n; i++) {
207 if (_dec.add(buf[i])) handleFrame(_dec.frame(), _dec.frameLen());
208 }
209 }
211 }
212
215 void input(const uint8_t *data, size_t len) {
216 for (size_t i = 0; i < len; i++) {
217 if (_dec.add(data[i])) handleFrame(_dec.frame(), _dec.frameLen());
218 }
219 }
220
223 if (!_started) return;
224 if (_lcp.timer && (int32_t)(now - _lcp.timer) >= 0) {
225 _lcp.timer = 0;
226 lcpTimeout();
227 }
228 if (_ipcp.timer && (int32_t)(now - _ipcp.timer) >= 0) {
229 _ipcp.timer = 0;
230 ipcpTimeout();
231 }
232 if (_papTimer && (int32_t)(now - _papTimer) >= 0) {
233 _papTimer = 0;
234 papTimeout();
235 }
236 if (_echoInterval && _lcp.state == CPState::Opened &&
237 (int32_t)(now - _echoNext) >= 0) {
238 _echoNext = now + _echoInterval;
239 if (_echoPending >= _echoMaxFail) {
240 logf("LCP echo timeout: restarting link");
241 lcpDown();
242 lcpOpen();
243 return;
244 }
245 _echoPending++;
246 uint8_t data[4];
247 memcpy(data, &_magic, 4);
248 sendCP(PPP_LCP, ECHO_REQ, ++_echoId, data, 4);
249 }
250 }
251
253 bool sendIPv4(const uint8_t *data, size_t len) {
254 if (!beginIPv4()) return false;
255 writeIPv4(data, len);
256 return endIPv4();
257 }
258
260 bool beginIPv4() {
261 if (!isUp()) return false;
262 _enc.begin(PPP_IP);
263 return true;
264 }
266 void writeIPv4(const uint8_t *data, size_t len) { _enc.data(data, len); }
268 bool endIPv4() {
269 bool ok = _enc.end();
270 _stats.txFrames++;
271 if (ok) {
272 _stats.txIP++;
273 } else {
274 _stats.txErrors++;
275 }
276 return ok;
277 }
278
280 bool isUp() const { return _ipcp.state == CPState::Opened; }
282 bool isStarted() const { return _started; }
284 PPPPhase phase() const { return _phase; }
286 ip4_t localIP() const { return _ourIP; }
288 ip4_t peerIP() const { return _peerIP; }
290 ip4_t dns1() const { return _dns1; }
292 ip4_t dns2() const { return _dns2; }
294 uint16_t mtu() const { return _peerMRU; }
296 const PPPStats &stats() const { return _stats; }
298 uint32_t fcsErrors() const { return _dec.fcsErrors(); }
299
300 protected:
302 virtual void onIPv4Packet(const uint8_t *data, size_t len) {
303 (void)data;
304 (void)len;
305 }
307 virtual void onLinkUp() {}
309 virtual void onLinkDown() {}
311 virtual void onPhase(PPPPhase phase) { (void)phase; }
312
313 void logf(const char *fmt, ...) {
314 if (_log == nullptr) return;
315 char msg[160];
317 va_start(args, fmt);
318 vsnprintf(msg, sizeof(msg), fmt, args);
319 va_end(args);
320 _log->print("PPP: ");
321 _log->println(msg);
322 }
323
324 private:
325 struct ControlProtocol {
326 explicit ControlProtocol(uint16_t p) : proto(p) {}
327 uint16_t proto;
329 uint8_t id = 0; // id of the last request we sent
330 uint8_t retries = 0; // timeouts in a row
331 uint32_t timer = 0; // deadline in ms, 0 = stopped
332 };
333
334 Stream *_io = nullptr;
335 Print *_log = nullptr;
336 HDLCEncoder _enc;
337 HDLCDecoder<TINYPPP_MRU + 4> _dec;
338 PPPStats _stats;
339 PPPPhase _phase = PPPPhase::Dead;
340 bool _started = false;
341 bool _passive = false;
342
343 ControlProtocol _lcp{PPP_LCP};
344 ControlProtocol _ipcp{PPP_IPCP};
345
346 // LCP: our options
347 bool _reqAccm = true;
348 bool _reqMagic = true;
349 uint32_t _magic = 0;
350 // LCP: peer options
351 uint16_t _peerMRU = 1500;
352 uint32_t _peerAccm = 0xFFFFFFFF;
353 bool _needPap = false;
354
355 // PAP
356 char _user[64] = "";
357 char _password[64] = "";
358 uint8_t _papId = 0;
359 uint8_t _papRetries = 0;
360 uint32_t _papTimer = 0;
361
362 // echo
363 uint32_t _echoInterval = 0;
364 uint8_t _echoMaxFail = 3;
365 uint8_t _echoPending = 0;
366 uint8_t _echoId = 0;
367 uint32_t _echoNext = 0;
368
369 // IPCP configuration
370 ip4_t _cfgLocal = 0;
371 ip4_t _cfgPeer = 0;
372 ip4_t _cfgOfferDns1 = 0;
373 ip4_t _cfgOfferDns2 = 0;
374 bool _cfgRequestDns = true;
375 // IPCP negotiated values
376 ip4_t _ourIP = 0;
377 ip4_t _peerIP = 0;
378 ip4_t _dns1 = 0;
379 ip4_t _dns2 = 0;
380 bool _reqAddr = true;
381 bool _reqDns1 = true;
382 bool _reqDns2 = true;
383
384 // ---------------------------------------------------------------- helpers
385
386 static uint16_t get16(const uint8_t *p) { return (uint16_t)(p[0] << 8) | p[1]; }
387 static void put16(uint8_t *p, uint16_t v) {
388 p[0] = v >> 8;
389 p[1] = v & 0xFF;
390 }
391
392 uint32_t newMagic() {
393 uint32_t m = (uint32_t)micros() * 2654435761u ^ (uint32_t)random(0x7FFFFFFF) ^ _magic;
394 return m == 0 ? 0x5A5A1234 : m;
395 }
396
397 void setPhase(PPPPhase p) {
398 if (p == _phase) return;
399 _phase = p;
400 logf("phase %s", toStr(p));
401 onPhase(p);
402 }
403
404 void armTimer(ControlProtocol &cp, uint32_t ms = TINYPPP_RESTART_MS) {
405 uint32_t t = millis() + ms;
406 cp.timer = t == 0 ? 1 : t;
407 }
408
409 void resetLink() {
410 _lcp.state = _ipcp.state = CPState::Initial;
411 _lcp.timer = _ipcp.timer = _papTimer = 0;
412 _reqAccm = _reqMagic = true;
413 _peerMRU = 1500;
414 _peerAccm = 0xFFFFFFFF;
415 _enc.setACCM(_peerAccm);
416 _needPap = false;
417 _echoPending = 0;
418 ipcpReset();
419 }
420
421 void ipcpReset() {
422 _ourIP = _cfgLocal;
423 _peerIP = 0;
424 _dns1 = _dns2 = 0;
425 _reqAddr = true;
426 _reqDns1 = _reqDns2 = _cfgRequestDns;
427 }
428
429 void sendFrame(uint16_t proto, const uint8_t *data, size_t len) {
430 // LCP frames always use the default ACCM (RFC 1662 7.1)
431 _enc.begin(proto, proto == PPP_LCP);
432 _enc.data(data, len);
433 if (!_enc.end()) _stats.txErrors++;
434 _stats.txFrames++;
435 }
436
437 void sendCP(uint16_t proto, uint8_t code, uint8_t id, const uint8_t *data,
438 size_t len) {
439 uint8_t pkt[4 + 128];
440 if (len > sizeof(pkt) - 4) len = sizeof(pkt) - 4;
441 pkt[0] = code;
442 pkt[1] = id;
443 put16(pkt + 2, (uint16_t)(len + 4));
444 if (len) memcpy(pkt + 4, data, len);
445 sendFrame(proto, pkt, len + 4);
446 }
447
448 // ------------------------------------------------------------- receiving
449
450 void handleFrame(uint8_t *frame, size_t len) {
451 _stats.rxFrames++;
452 // address and control field may be compressed (ACFC)
453 if (len >= 2 && frame[0] == HDLC_ALL_STATIONS && frame[1] == HDLC_UI) {
454 frame += 2;
455 len -= 2;
456 }
457 if (len < 1) return;
458 // protocol field may be compressed (PFC): odd first byte
459 uint16_t proto;
460 if (frame[0] & 0x01) {
461 proto = frame[0];
462 frame += 1;
463 len -= 1;
464 } else {
465 if (len < 2) return;
466 proto = get16(frame);
467 frame += 2;
468 len -= 2;
469 }
470
471 switch (proto) {
472 case PPP_IP:
473 if (isUp()) {
474 _stats.rxIP++;
475 onIPv4Packet(frame, len);
476 } else {
477 _stats.rxDropped++;
478 }
479 return;
480 case PPP_LCP:
481 handleCP(_lcp, frame, len);
482 return;
483 case PPP_IPCP:
484 if (_lcp.state == CPState::Opened && _phase >= PPPPhase::Network) {
485 handleCP(_ipcp, frame, len);
486 } else {
487 _stats.rxDropped++;
488 }
489 return;
490 case PPP_PAP:
491 if (_lcp.state == CPState::Opened) handlePap(frame, len);
492 return;
493 default:
494 _stats.rxDropped++;
495 // unsupported protocol (CCP, IPV6CP, ...)
496 if (_lcp.state == CPState::Opened) sendProtocolReject(proto, frame, len);
497 return;
498 }
499 }
500
501 void sendProtocolReject(uint16_t proto, const uint8_t *info, size_t len) {
502 logf("protocol reject 0x%04x", proto);
503 uint8_t data[2 + 64];
504 put16(data, proto);
505 if (len > sizeof(data) - 2) len = sizeof(data) - 2;
506 memcpy(data + 2, info, len);
507 sendCP(PPP_LCP, PROT_REJ, ++_lcp.id, data, len + 2);
508 }
509
510 void handleCP(ControlProtocol &cp, const uint8_t *pkt, size_t len) {
511 if (len < 4) return;
512 uint8_t code = pkt[0];
513 uint8_t id = pkt[1];
514 size_t plen = get16(pkt + 2);
515 if (plen < 4 || plen > len) return; // malformed
516 const uint8_t *data = pkt + 4;
517 size_t dlen = plen - 4;
518 bool isLcp = cp.proto == PPP_LCP;
519
520 if (cp.state == CPState::Initial) {
521 // not started (e.g. IPCP before LCP is up or after stop())
522 return;
523 }
524
525 switch (code) {
526 case CONF_REQ:
527 receiveConfigRequest(cp, id, data, dlen);
528 break;
529 case CONF_ACK:
530 if (id != cp.id) return;
531 receiveConfigAck(cp);
532 break;
533 case CONF_NAK:
534 case CONF_REJ:
535 if (id != cp.id) return;
536 if (isLcp) {
537 lcpApplyNakRej(code, data, dlen);
538 } else {
539 ipcpApplyNakRej(code, data, dlen);
540 }
541 receiveConfigNak(cp);
542 break;
543 case TERM_REQ:
544 logf("%s terminate request", isLcp ? "LCP" : "IPCP");
545 sendCP(cp.proto, TERM_ACK, id, nullptr, 0);
546 if (isLcp) {
547 lcpDown();
548 // wait for the peer to come back
549 _lcp.state = CPState::Stopped;
550 if (!_passive) armTimer(_lcp, TINYPPP_RESTART_MS);
551 } else {
552 if (cp.state == CPState::Opened) ipcpDown();
553 cp.state = CPState::Stopped;
554 armTimer(cp, TINYPPP_RESTART_MS);
555 }
556 break;
557 case TERM_ACK:
558 if (cp.state == CPState::Opened) {
559 if (isLcp) {
560 lcpDown();
561 lcpOpen();
562 } else {
563 ipcpDown();
564 ipcpOpen();
565 }
566 } else if (cp.state == CPState::Closing) {
567 cp.state = CPState::Stopped;
568 if (isLcp && !_passive) armTimer(cp, TINYPPP_RESTART_MS);
569 }
570 break;
571 case CODE_REJ:
572 logf("code reject");
573 break;
574 case PROT_REJ:
575 if (isLcp && dlen >= 2) {
576 uint16_t p = get16(data);
577 logf("peer rejected protocol 0x%04x", p);
578 if (p == PPP_IPCP) {
579 ipcpDown();
580 _ipcp.state = CPState::Initial;
581 _ipcp.timer = 0;
582 }
583 }
584 break;
585 case ECHO_REQ:
586 if (isLcp && cp.state == CPState::Opened) {
587 uint8_t reply[64];
588 size_t n = dlen < sizeof(reply) ? dlen : sizeof(reply);
589 memcpy(reply, data, n);
590 if (n >= 4) memcpy(reply, &_magic, 4);
591 sendCP(PPP_LCP, ECHO_REP, id, reply, n);
592 }
593 break;
594 case ECHO_REP:
595 _echoPending = 0;
596 break;
597 case DISCARD_REQ:
598 break;
599 default: {
600 // unknown code: Code-Reject
601 sendCP(cp.proto, CODE_REJ, ++cp.id, pkt, plen);
602 break;
603 }
604 }
605 }
606
607 // RFC 1661 automaton (simplified)
608
609 void receiveConfigRequest(ControlProtocol &cp, uint8_t id,
610 const uint8_t *data, size_t len) {
611 uint8_t reply[128];
612 size_t replyLen = 0;
613 uint8_t code = cp.proto == PPP_LCP
614 ? lcpCheckRequest(data, len, reply, replyLen)
615 : ipcpCheckRequest(data, len, reply, replyLen);
616 if (code == 0) return; // malformed
617 bool ack = code == CONF_ACK;
618
619 switch (cp.state) {
620 case CPState::Opened:
621 // renegotiation
622 layerDown(cp);
623 sendConfigRequest(cp);
624 cp.state = ack ? CPState::AckSent : CPState::ReqSent;
625 break;
626 case CPState::Stopped:
627 case CPState::Closing:
628 sendConfigRequest(cp);
629 cp.state = ack ? CPState::AckSent : CPState::ReqSent;
630 break;
631 case CPState::ReqSent:
632 case CPState::AckSent:
633 cp.state = ack ? CPState::AckSent : CPState::ReqSent;
634 break;
635 case CPState::AckRcvd:
636 if (ack) {
637 sendCP(cp.proto, code, id, reply, replyLen);
638 cp.state = CPState::Opened;
639 layerUp(cp);
640 return;
641 }
642 break;
643 default:
644 break;
645 }
646 sendCP(cp.proto, code, id, reply, replyLen);
647 }
648
649 void receiveConfigAck(ControlProtocol &cp) {
650 cp.retries = 0;
651 switch (cp.state) {
652 case CPState::ReqSent:
653 cp.state = CPState::AckRcvd;
654 armTimer(cp);
655 break;
656 case CPState::AckSent:
657 cp.state = CPState::Opened;
658 cp.timer = 0;
659 layerUp(cp);
660 break;
661 case CPState::AckRcvd:
662 // crossed connection
663 sendConfigRequest(cp);
664 cp.state = CPState::ReqSent;
665 break;
666 case CPState::Opened:
667 layerDown(cp);
668 sendConfigRequest(cp);
669 cp.state = CPState::ReqSent;
670 break;
671 default:
672 break;
673 }
674 }
675
676 void receiveConfigNak(ControlProtocol &cp) {
677 switch (cp.state) {
678 case CPState::ReqSent:
679 case CPState::AckSent:
680 sendConfigRequest(cp);
681 break;
682 case CPState::AckRcvd:
683 sendConfigRequest(cp);
684 cp.state = CPState::ReqSent;
685 break;
686 case CPState::Opened:
687 layerDown(cp);
688 sendConfigRequest(cp);
689 cp.state = CPState::ReqSent;
690 break;
691 default:
692 break;
693 }
694 }
695
696 void sendConfigRequest(ControlProtocol &cp) {
697 uint8_t opts[32];
698 size_t len = cp.proto == PPP_LCP ? lcpBuildRequest(opts) : ipcpBuildRequest(opts);
699 sendCP(cp.proto, CONF_REQ, ++cp.id, opts, len);
700 armTimer(cp);
701 }
702
703 void layerUp(ControlProtocol &cp) {
704 if (cp.proto == PPP_LCP) {
705 lcpUp();
706 } else {
707 ipcpUp();
708 }
709 }
710
711 void layerDown(ControlProtocol &cp) {
712 if (cp.proto == PPP_LCP) {
713 lcpDown();
714 } else {
715 ipcpDown();
716 }
717 }
718
719 // ------------------------------------------------------------------- LCP
720
721 void lcpOpen() {
722 _lcp.retries = 0;
723 _reqAccm = _reqMagic = true;
724 setPhase(PPPPhase::Establish);
725 sendConfigRequest(_lcp);
726 _lcp.state = CPState::ReqSent;
727 }
728
729 void lcpTimeout() {
730 switch (_lcp.state) {
731 case CPState::ReqSent:
732 case CPState::AckRcvd:
733 case CPState::AckSent:
734 // keep on trying forever: the host might start pppd later
735 if (++_lcp.retries > 10) _lcp.retries = 10;
736 sendConfigRequest(_lcp);
737 if (_lcp.state == CPState::AckRcvd) _lcp.state = CPState::ReqSent;
738 // back off to reduce noise on the line while nobody is listening
739 if (_lcp.retries >= 10) armTimer(_lcp, 5 * TINYPPP_RESTART_MS);
740 break;
741 case CPState::Stopped:
742 case CPState::Closing:
743 if (!_passive) lcpOpen();
744 break;
745 default:
746 break;
747 }
748 }
749
750 void lcpUp() {
751 logf("LCP up (peer MRU %u)", _peerMRU);
752 _enc.setACCM(_peerAccm);
753 _echoPending = 0;
754 _echoNext = millis() + _echoInterval;
755 if (_needPap) {
756 setPhase(PPPPhase::Authenticate);
757 _papRetries = 0;
758 papSend();
759 } else {
760 setPhase(PPPPhase::Network);
761 ipcpOpen();
762 }
763 }
764
765 void lcpDown() {
766 if (_ipcp.state == CPState::Opened) ipcpDown();
767 _ipcp.state = CPState::Initial;
768 _ipcp.timer = 0;
769 _papTimer = 0;
770 _peerAccm = 0xFFFFFFFF;
771 _enc.setACCM(_peerAccm);
772 _needPap = false;
773 if (_lcp.state == CPState::Opened) logf("LCP down");
774 setPhase(_started ? PPPPhase::Establish : PPPPhase::Dead);
775 }
776
777 size_t lcpBuildRequest(uint8_t *p) {
778 size_t n = 0;
779 if (_reqAccm) {
780 p[n++] = LCP_ACCM;
781 p[n++] = 6;
782 memset(p + n, 0, 4); // nothing needs to be escaped
783 n += 4;
784 }
785 if (_reqMagic) {
786 p[n++] = LCP_MAGIC;
787 p[n++] = 6;
788 memcpy(p + n, &_magic, 4);
789 n += 4;
790 }
791 return n;
792 }
793
794 void lcpApplyNakRej(uint8_t code, const uint8_t *p, size_t len) {
795 size_t pos = 0;
796 while (pos + 2 <= len) {
797 uint8_t type = p[pos];
798 uint8_t olen = p[pos + 1];
799 if (olen < 2 || pos + olen > len) return;
800 if (type == LCP_ACCM) {
801 _reqAccm = false; // keep the default map
802 } else if (type == LCP_MAGIC) {
803 if (code == CONF_REJ) {
804 _reqMagic = false;
805 } else {
806 _magic = newMagic();
807 }
808 }
809 pos += olen;
810 }
811 }
812
813 uint8_t lcpCheckRequest(const uint8_t *p, size_t len, uint8_t *reply,
814 size_t &replyLen) {
815 uint8_t naks[32];
816 size_t nakLen = 0;
817 size_t rejLen = 0;
818 uint16_t mru = 1500;
819 uint32_t accm = 0xFFFFFFFF;
820 bool needPap = false;
821
822 size_t pos = 0;
823 while (pos < len) {
824 if (pos + 2 > len) return 0;
825 uint8_t type = p[pos];
826 uint8_t olen = p[pos + 1];
827 if (olen < 2 || pos + olen > len) return 0;
828 const uint8_t *opt = p + pos;
829 bool reject = false;
830 switch (type) {
831 case LCP_MRU:
832 if (olen != 4) {
833 reject = true;
834 } else {
835 mru = get16(opt + 2);
836 if (mru > TINYPPP_MRU) mru = TINYPPP_MRU;
837 }
838 break;
839 case LCP_ACCM:
840 if (olen != 6) {
841 reject = true;
842 } else {
843 accm = (uint32_t)opt[2] << 24 | (uint32_t)opt[3] << 16 |
844 (uint32_t)opt[4] << 8 | opt[5];
845 }
846 break;
847 case LCP_AUTH:
848 if (olen >= 4 && get16(opt + 2) == PPP_PAP) {
849 needPap = true;
850 } else if (nakLen + 4 <= sizeof(naks)) {
851 // we only support PAP
852 naks[nakLen++] = LCP_AUTH;
853 naks[nakLen++] = 4;
854 put16(naks + nakLen, PPP_PAP);
855 nakLen += 2;
856 }
857 break;
858 case LCP_MAGIC:
859 if (olen != 6) {
860 reject = true;
861 } else if (_reqMagic && memcmp(opt + 2, &_magic, 4) == 0 &&
862 nakLen + 6 <= sizeof(naks)) {
863 // looped back line or same magic: suggest another value
864 uint32_t m = newMagic();
865 naks[nakLen++] = LCP_MAGIC;
866 naks[nakLen++] = 6;
867 memcpy(naks + nakLen, &m, 4);
868 nakLen += 4;
869 }
870 break;
871 case LCP_PFC:
872 case LCP_ACFC:
873 // we accept compressed frames but never send them
874 if (olen != 2) reject = true;
875 break;
876 default:
877 reject = true;
878 break;
879 }
880 if (reject && rejLen + olen <= 128) {
881 memcpy(reply + rejLen, opt, olen);
882 rejLen += olen;
883 }
884 pos += olen;
885 }
886
887 if (rejLen > 0) {
888 replyLen = rejLen;
889 return CONF_REJ;
890 }
891 if (nakLen > 0) {
892 memcpy(reply, naks, nakLen);
893 replyLen = nakLen;
894 return CONF_NAK;
895 }
896 // Ack: commit the peer's options
897 _peerMRU = mru;
898 _peerAccm = accm;
899 _needPap = needPap;
900 size_t n = len < 128 ? len : 128;
901 memcpy(reply, p, n);
902 replyLen = n;
903 return CONF_ACK;
904 }
905
906 // ------------------------------------------------------------------- PAP
907
908 void papSend() {
909 uint8_t data[2 + sizeof(_user) + sizeof(_password)];
910 size_t ul = strlen(_user);
911 size_t pl = strlen(_password);
912 size_t n = 0;
913 data[n++] = (uint8_t)ul;
914 memcpy(data + n, _user, ul);
915 n += ul;
916 data[n++] = (uint8_t)pl;
917 memcpy(data + n, _password, pl);
918 n += pl;
919 uint8_t pkt[4 + sizeof(data)];
920 pkt[0] = 1; // Authenticate-Request
921 pkt[1] = ++_papId;
922 put16(pkt + 2, (uint16_t)(n + 4));
923 memcpy(pkt + 4, data, n);
924 sendFrame(PPP_PAP, pkt, n + 4);
925 _papTimer = millis() + TINYPPP_RESTART_MS;
926 if (_papTimer == 0) _papTimer = 1;
927 }
928
929 void papTimeout() {
930 if (_phase != PPPPhase::Authenticate) return;
931 if (++_papRetries > 10) {
932 logf("PAP: no response");
933 lcpClose();
934 return;
935 }
936 papSend();
937 }
938
939 void handlePap(const uint8_t *pkt, size_t len) {
940 if (len < 4 || _phase != PPPPhase::Authenticate) return;
941 uint8_t code = pkt[0];
942 if (pkt[1] != _papId) return;
943 if (code == 2) { // Authenticate-Ack
944 logf("PAP: authenticated");
945 _papTimer = 0;
946 setPhase(PPPPhase::Network);
947 ipcpOpen();
948 } else if (code == 3) { // Authenticate-Nak
949 logf("PAP: authentication failed");
950 _papTimer = 0;
951 lcpClose();
952 }
953 }
954
955 void lcpClose() {
956 static const uint8_t reason[] = "close";
957 lcpDown();
958 sendCP(PPP_LCP, TERM_REQ, ++_lcp.id, reason, sizeof(reason) - 1);
959 _lcp.state = CPState::Closing;
960 setPhase(PPPPhase::Terminate);
961 armTimer(_lcp, 3 * TINYPPP_RESTART_MS);
962 }
963
964 // ------------------------------------------------------------------ IPCP
965
966 void ipcpOpen() {
967 ipcpReset();
968 _ipcp.retries = 0;
969 sendConfigRequest(_ipcp);
970 _ipcp.state = CPState::ReqSent;
971 }
972
973 void ipcpTimeout() {
974 switch (_ipcp.state) {
975 case CPState::ReqSent:
976 case CPState::AckRcvd:
977 case CPState::AckSent:
978 if (++_ipcp.retries > 10) {
979 logf("IPCP: no response, restarting LCP");
980 lcpClose();
981 return;
982 }
983 sendConfigRequest(_ipcp);
984 if (_ipcp.state == CPState::AckRcvd) _ipcp.state = CPState::ReqSent;
985 break;
986 case CPState::Stopped:
987 if (_lcp.state == CPState::Opened) ipcpOpen();
988 break;
989 default:
990 break;
991 }
992 }
993
994 void ipcpUp() {
995 if (_ourIP == 0) {
996 logf("IPCP: no local address");
997 lcpClose();
998 return;
999 }
1000 setPhase(PPPPhase::Running);
1001 char a[16], b[16];
1002 logf("IPCP up: local %s peer %s", ipStr(_ourIP, a), ipStr(_peerIP, b));
1003 onLinkUp();
1004 }
1005
1006 void ipcpDown() {
1007 if (_ipcp.state != CPState::Opened) return;
1008 logf("IPCP down");
1009 _ipcp.state = CPState::Stopped;
1010 onLinkDown();
1011 if (_phase == PPPPhase::Running) setPhase(PPPPhase::Network);
1012 }
1013
1014 static void putAddr(uint8_t *p, size_t &n, uint8_t type, ip4_t addr) {
1015 p[n++] = type;
1016 p[n++] = 6;
1017 memcpy(p + n, &addr, 4);
1018 n += 4;
1019 }
1020
1021 size_t ipcpBuildRequest(uint8_t *p) {
1022 size_t n = 0;
1023 if (_reqAddr) putAddr(p, n, IPCP_ADDRESS, _ourIP);
1024 if (_reqDns1) putAddr(p, n, IPCP_DNS1, _dns1);
1025 if (_reqDns2) putAddr(p, n, IPCP_DNS2, _dns2);
1026 return n;
1027 }
1028
1029 void ipcpApplyNakRej(uint8_t code, const uint8_t *p, size_t len) {
1030 size_t pos = 0;
1031 while (pos + 2 <= len) {
1032 uint8_t type = p[pos];
1033 uint8_t olen = p[pos + 1];
1034 if (olen < 2 || pos + olen > len) return;
1035 bool rej = code == CONF_REJ;
1036 ip4_t v = 0;
1037 if (olen == 6) memcpy(&v, p + pos + 2, 4);
1038 switch (type) {
1039 case IPCP_ADDRESS:
1040 if (rej) {
1041 _reqAddr = false;
1042 } else {
1043 _ourIP = v;
1044 }
1045 break;
1046 case IPCP_DNS1:
1047 if (rej) {
1048 _reqDns1 = false;
1049 } else {
1050 _dns1 = v;
1051 }
1052 break;
1053 case IPCP_DNS2:
1054 if (rej) {
1055 _reqDns2 = false;
1056 } else {
1057 _dns2 = v;
1058 }
1059 break;
1060 default:
1061 break;
1062 }
1063 pos += olen;
1064 }
1065 }
1066
1067 uint8_t ipcpCheckRequest(const uint8_t *p, size_t len, uint8_t *reply,
1068 size_t &replyLen) {
1069 uint8_t naks[32];
1070 size_t nakLen = 0;
1071 size_t rejLen = 0;
1072 ip4_t peer = 0;
1073
1074 size_t pos = 0;
1075 while (pos < len) {
1076 if (pos + 2 > len) return 0;
1077 uint8_t type = p[pos];
1078 uint8_t olen = p[pos + 1];
1079 if (olen < 2 || pos + olen > len) return 0;
1080 const uint8_t *opt = p + pos;
1081 ip4_t v = 0;
1082 if (olen == 6) memcpy(&v, opt + 2, 4);
1083 bool reject = false;
1084 switch (type) {
1085 case IPCP_ADDRESS:
1086 if (olen != 6) {
1087 reject = true;
1088 } else if (v == 0 || (v == _ourIP && _ourIP != 0)) {
1089 // the peer wants an address from us
1090 if (_cfgPeer != 0 && _cfgPeer != _ourIP) {
1091 putAddr(naks, nakLen, IPCP_ADDRESS, _cfgPeer);
1092 } else {
1093 reject = true;
1094 }
1095 } else {
1096 peer = v;
1097 }
1098 break;
1099 case IPCP_DNS1:
1100 case IPCP_DNS2: {
1101 ip4_t offer = type == IPCP_DNS1 ? _cfgOfferDns1 : _cfgOfferDns2;
1102 if (olen != 6 || offer == 0) {
1103 reject = true;
1104 } else if (v != offer) {
1105 putAddr(naks, nakLen, type, offer);
1106 }
1107 break;
1108 }
1109 default:
1110 // IP compression (VJ) etc. are not supported
1111 reject = true;
1112 break;
1113 }
1114 if (reject && rejLen + olen <= 128) {
1115 memcpy(reply + rejLen, opt, olen);
1116 rejLen += olen;
1117 }
1118 pos += olen;
1119 }
1120
1121 if (rejLen > 0) {
1122 replyLen = rejLen;
1123 return CONF_REJ;
1124 }
1125 if (nakLen > 0) {
1126 memcpy(reply, naks, nakLen);
1127 replyLen = nakLen;
1128 return CONF_NAK;
1129 }
1130 _peerIP = peer != 0 ? peer : _cfgPeer;
1131 size_t n = len < 128 ? len : 128;
1132 memcpy(reply, p, n);
1133 replyLen = n;
1134 return CONF_ACK;
1135 }
1136
1137 protected:
1138 static const char *ipStr(ip4_t ip, char *buf) {
1139 const uint8_t *b = (const uint8_t *)&ip;
1140 snprintf(buf, 16, "%u.%u.%u.%u", b[0], b[1], b[2], b[3]);
1141 return buf;
1142 }
1143};
1144
1147 const uint8_t *b = (const uint8_t *)&ip;
1148 return IPAddress(b[0], b[1], b[2], b[3]);
1149}
1150
1153 return toIp4(ip[0], ip[1], ip[2], ip[3]);
1154}
1155
1156} // namespace tinyppp
#define TINYPPP_MRU
Definition PPPEngine.h:21
#define TINYPPP_RESTART_MS
Definition PPPEngine.h:25
uint32_t fcsErrors() const
Number of frames dropped because of a wrong FCS.
Definition HDLC.h:177
uint8_t * frame()
Frame content without FCS (address/control included if sent)
Definition HDLC.h:173
bool add(uint8_t b)
Feed one byte: returns true when a valid frame is available.
Definition HDLC.h:140
size_t frameLen() const
Length of the frame returned by frame()
Definition HDLC.h:175
void data(uint8_t b)
Adds one payload byte.
Definition HDLC.h:66
void begin(uint16_t protocol, bool escapeAllControl=false)
Starts a frame with address/control header and the protocol number.
Definition HDLC.h:53
void setOutput(Print *out)
Defines the output (e.g. the serial port)
Definition HDLC.h:47
bool end()
Appends FCS and closing flag; returns false if the output dropped bytes.
Definition HDLC.h:77
Portable PPP engine: LCP, PAP client and IPCP over an Arduino Stream.
Definition PPPEngine.h:134
void writeIPv4(const uint8_t *data, size_t len)
Adds data to the packet started with beginIPv4()
Definition PPPEngine.h:266
bool sendIPv4(const uint8_t *data, size_t len)
Sends a complete IPv4 packet.
Definition PPPEngine.h:253
void start(Stream &io)
Starts the PPP negotiation on the indicated stream.
Definition PPPEngine.h:165
static const char * ipStr(ip4_t ip, char *buf)
Definition PPPEngine.h:1138
void process()
Reads available input and runs the timers: call frequently.
Definition PPPEngine.h:196
void setOfferedDNS(ip4_t dns1, ip4_t dns2=0)
DNS servers to offer the peer if it asks for them (server role)
Definition PPPEngine.h:143
void setLocalIP(ip4_t ip)
Optional static address configuration. 0 = negotiate with the peer.
Definition PPPEngine.h:139
uint32_t fcsErrors() const
Number of received frames with a wrong FCS.
Definition PPPEngine.h:298
ip4_t localIP() const
Our IP address (0 if not negotiated yet)
Definition PPPEngine.h:286
void processTimers(uint32_t now)
Runs the retransmission and keep-alive timers.
Definition PPPEngine.h:222
void logf(const char *fmt,...)
Definition PPPEngine.h:313
void setLogger(Print *log)
Debug output (nullptr to disable)
Definition PPPEngine.h:162
void setEchoInterval(uint32_t ms, uint8_t maxFailures=3)
LCP echo keep-alive: 0 disables it.
Definition PPPEngine.h:157
ip4_t dns2() const
Second DNS server received from the peer.
Definition PPPEngine.h:292
virtual void onLinkDown()
Called when the IP link goes down.
Definition PPPEngine.h:309
PPPPhase phase() const
Current link phase.
Definition PPPEngine.h:284
ip4_t dns1() const
First DNS server received from the peer.
Definition PPPEngine.h:290
void setPeerIP(ip4_t ip)
Address to assign to the peer if it requests one (server role)
Definition PPPEngine.h:141
void stop()
Closes the link gracefully (sends LCP Terminate-Request)
Definition PPPEngine.h:180
void setCredentials(const char *user, const char *password)
PAP credentials (used only if the peer requires authentication)
Definition PPPEngine.h:150
ip4_t peerIP() const
IP address of the peer.
Definition PPPEngine.h:288
virtual void onPhase(PPPPhase phase)
Called on phase changes.
Definition PPPEngine.h:311
bool beginIPv4()
Streaming API for chained buffers (e.g. lwIP pbuf)
Definition PPPEngine.h:260
void setRequestDNS(bool flag)
Request DNS servers from the peer (default true)
Definition PPPEngine.h:148
bool endIPv4()
Sends the packet started with beginIPv4()
Definition PPPEngine.h:268
virtual ~PPPEngine()=default
void input(const uint8_t *data, size_t len)
Definition PPPEngine.h:215
bool isStarted() const
true after start() until stop()
Definition PPPEngine.h:282
void setPassive(bool passive)
Passive: do not send LCP requests before the peer starts negotiating.
Definition PPPEngine.h:155
virtual void onLinkUp()
Called when IPCP is opened: addresses are available.
Definition PPPEngine.h:307
bool isUp() const
true when IPCP is open and IPv4 packets can be exchanged
Definition PPPEngine.h:280
virtual void onIPv4Packet(const uint8_t *data, size_t len)
Called for each received IPv4 packet.
Definition PPPEngine.h:302
uint16_t mtu() const
MTU we may use for sending (peer MRU)
Definition PPPEngine.h:294
const PPPStats & stats() const
Frame and packet counters.
Definition PPPEngine.h:296
Byte ring buffer with a fixed capacity of N bytes.
Definition IPStack.h:76
size_t available() const
Number of stored bytes.
Definition IPStack.h:79
TinyPPP internals: PPP engine, HDLC framing and the IP stack.
Definition HDLC.h:11
static constexpr uint16_t PPP_IPV6
Definition PPPEngine.h:33
static constexpr uint16_t PPP_LCP
Definition PPPEngine.h:34
PPPPhase
Overall link phase.
Definition PPPEngine.h:83
CPCode
Control protocol (LCP, IPCP) packet codes.
Definition PPPEngine.h:39
@ CONF_REQ
Definition PPPEngine.h:40
@ DISCARD_REQ
Definition PPPEngine.h:50
@ CONF_NAK
Definition PPPEngine.h:42
@ CONF_REJ
Definition PPPEngine.h:43
@ CODE_REJ
Definition PPPEngine.h:46
@ TERM_ACK
Definition PPPEngine.h:45
@ ECHO_REQ
Definition PPPEngine.h:48
@ TERM_REQ
Definition PPPEngine.h:44
@ PROT_REJ
Definition PPPEngine.h:47
@ CONF_ACK
Definition PPPEngine.h:41
@ ECHO_REP
Definition PPPEngine.h:49
IPAddress toIPAddress(ip4_t ip)
Converts an engine address to an Arduino IPAddress.
Definition PPPEngine.h:1146
uint32_t ip4_t
IPv4 address in network byte order as stored in memory.
Definition IPStack.h:72
LCPOption
LCP configuration options.
Definition PPPEngine.h:54
@ LCP_ACCM
Definition PPPEngine.h:56
@ LCP_MRU
Definition PPPEngine.h:55
@ LCP_AUTH
Definition PPPEngine.h:57
@ LCP_MAGIC
Definition PPPEngine.h:58
@ LCP_ACFC
Definition PPPEngine.h:60
@ LCP_PFC
Definition PPPEngine.h:59
static constexpr uint16_t PPP_IPCP
Definition PPPEngine.h:36
CPState
Simplified RFC 1661 automaton states.
Definition PPPEngine.h:72
static constexpr uint16_t PPP_IP
PPP protocol numbers.
Definition PPPEngine.h:32
ip4_t fromIPAddress(const IPAddress &ip)
Converts an Arduino IPAddress to an engine address (0 if not set)
Definition PPPEngine.h:1152
static constexpr uint16_t PPP_PAP
Definition PPPEngine.h:35
static constexpr uint8_t HDLC_ALL_STATIONS
Definition HDLC.h:16
ip4_t toIp4(uint8_t a, uint8_t b, uint8_t c, uint8_t d)
Builds an address from its 4 bytes (a.b.c.d)
Definition PPPEngine.h:109
const char * toStr(PPPPhase p)
Name of a phase (for logging)
Definition PPPEngine.h:93
IPCPOption
IPCP configuration options.
Definition PPPEngine.h:64
@ IPCP_DNS2
Definition PPPEngine.h:68
@ IPCP_COMPRESSION
Definition PPPEngine.h:65
@ IPCP_ADDRESS
Definition PPPEngine.h:66
@ IPCP_DNS1
Definition PPPEngine.h:67
static constexpr uint8_t HDLC_UI
Definition HDLC.h:17
Frame and packet counters of a PPPEngine.
Definition PPPEngine.h:117
uint32_t rxFrames
received frames with a valid FCS
Definition PPPEngine.h:118
uint32_t rxIP
received IPv4 packets
Definition PPPEngine.h:120
uint32_t txFrames
sent frames
Definition PPPEngine.h:119
uint32_t txErrors
frames the output did not accept completely
Definition PPPEngine.h:123
uint32_t rxDropped
received frames that were dropped
Definition PPPEngine.h:122
uint32_t txIP
sent IPv4 packets
Definition PPPEngine.h:121