21#define TINYPPP_MRU 1500
24#ifndef TINYPPP_RESTART_MS
25#define TINYPPP_RESTART_MS 2000
106using ip4_t = uint32_t;
144 _cfgOfferDns1 =
dns1;
145 _cfgOfferDns2 =
dns2;
181 if (!_started)
return;
190 _lcp.timer = _ipcp.timer = _papTimer = 0;
197 if (!_started || _io ==
nullptr)
return;
202 if (
avail <= 0)
break;
203 size_t n = _io->readBytes(
206 for (
size_t i = 0;
i <
n;
i++) {
216 for (
size_t i = 0;
i <
len;
i++) {
223 if (!_started)
return;
224 if (_lcp.timer && (
int32_t)(
now - _lcp.timer) >= 0) {
228 if (_ipcp.timer && (
int32_t)(
now - _ipcp.timer) >= 0) {
232 if (_papTimer && (
int32_t)(
now - _papTimer) >= 0) {
238 _echoNext =
now + _echoInterval;
239 if (_echoPending >= _echoMaxFail) {
240 logf(
"LCP echo timeout: restarting link");
261 if (!
isUp())
return false;
269 bool ok = _enc.
end();
314 if (_log ==
nullptr)
return;
320 _log->print(
"PPP: ");
325 struct ControlProtocol {
326 explicit ControlProtocol(
uint16_t p) : proto(
p) {}
334 Stream *_io =
nullptr;
335 Print *_log =
nullptr;
337 HDLCDecoder<TINYPPP_MRU + 4> _dec;
340 bool _started =
false;
341 bool _passive =
false;
347 bool _reqAccm =
true;
348 bool _reqMagic =
true;
351 uint16_t _peerMRU = 1500;
352 uint32_t _peerAccm = 0xFFFFFFFF;
353 bool _needPap =
false;
357 char _password[64] =
"";
359 uint8_t _papRetries = 0;
360 uint32_t _papTimer = 0;
363 uint32_t _echoInterval = 0;
364 uint8_t _echoMaxFail = 3;
365 uint8_t _echoPending = 0;
367 uint32_t _echoNext = 0;
372 ip4_t _cfgOfferDns1 = 0;
373 ip4_t _cfgOfferDns2 = 0;
374 bool _cfgRequestDns =
true;
380 bool _reqAddr =
true;
381 bool _reqDns1 =
true;
382 bool _reqDns2 =
true;
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) {
392 uint32_t newMagic() {
393 uint32_t m = (uint32_t)micros() * 2654435761u ^ (uint32_t)random(0x7FFFFFFF) ^ _magic;
394 return m == 0 ? 0x5A5A1234 : m;
398 if (p == _phase)
return;
405 uint32_t t = millis() + ms;
406 cp.timer = t == 0 ? 1 : t;
411 _lcp.timer = _ipcp.timer = _papTimer = 0;
412 _reqAccm = _reqMagic =
true;
414 _peerAccm = 0xFFFFFFFF;
415 _enc.setACCM(_peerAccm);
426 _reqDns1 = _reqDns2 = _cfgRequestDns;
429 void sendFrame(uint16_t proto,
const uint8_t *data,
size_t len) {
431 _enc.begin(proto, proto ==
PPP_LCP);
432 _enc.data(data, len);
433 if (!_enc.end()) _stats.txErrors++;
437 void sendCP(uint16_t proto, uint8_t code, uint8_t
id,
const uint8_t *data,
439 uint8_t pkt[4 + 128];
440 if (len >
sizeof(pkt) - 4) len =
sizeof(pkt) - 4;
443 put16(pkt + 2, (uint16_t)(len + 4));
444 if (len) memcpy(pkt + 4, data, len);
445 sendFrame(proto, pkt, len + 4);
450 void handleFrame(uint8_t *frame,
size_t len) {
460 if (frame[0] & 0x01) {
466 proto = get16(frame);
481 handleCP(_lcp, frame, len);
485 handleCP(_ipcp, frame, len);
496 if (_lcp.state ==
CPState::Opened) sendProtocolReject(proto, frame, len);
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];
505 if (len >
sizeof(data) - 2) len =
sizeof(data) - 2;
506 memcpy(data + 2, info, len);
510 void handleCP(ControlProtocol &cp,
const uint8_t *pkt,
size_t len) {
512 uint8_t code = pkt[0];
514 size_t plen = get16(pkt + 2);
515 if (plen < 4 || plen > len)
return;
516 const uint8_t *data = pkt + 4;
517 size_t dlen = plen - 4;
518 bool isLcp = cp.proto ==
PPP_LCP;
527 receiveConfigRequest(cp,
id, data, dlen);
530 if (
id != cp.id)
return;
531 receiveConfigAck(cp);
535 if (
id != cp.id)
return;
537 lcpApplyNakRej(code, data, dlen);
539 ipcpApplyNakRej(code, data, dlen);
541 receiveConfigNak(cp);
544 logf(
"%s terminate request", isLcp ?
"LCP" :
"IPCP");
545 sendCP(cp.proto,
TERM_ACK,
id,
nullptr, 0);
575 if (isLcp && dlen >= 2) {
576 uint16_t p = get16(data);
577 logf(
"peer rejected protocol 0x%04x", p);
588 size_t n = dlen <
sizeof(reply) ? dlen : sizeof(reply);
589 memcpy(reply, data, n);
590 if (n >= 4) memcpy(reply, &_magic, 4);
601 sendCP(cp.proto,
CODE_REJ, ++cp.id, pkt, plen);
609 void receiveConfigRequest(ControlProtocol &cp, uint8_t
id,
610 const uint8_t *data,
size_t len) {
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;
623 sendConfigRequest(cp);
628 sendConfigRequest(cp);
637 sendCP(cp.proto, code,
id, reply, replyLen);
646 sendCP(cp.proto, code,
id, reply, replyLen);
649 void receiveConfigAck(ControlProtocol &cp) {
663 sendConfigRequest(cp);
668 sendConfigRequest(cp);
676 void receiveConfigNak(ControlProtocol &cp) {
680 sendConfigRequest(cp);
683 sendConfigRequest(cp);
688 sendConfigRequest(cp);
696 void sendConfigRequest(ControlProtocol &cp) {
698 size_t len = cp.proto ==
PPP_LCP ? lcpBuildRequest(opts) : ipcpBuildRequest(opts);
699 sendCP(cp.proto,
CONF_REQ, ++cp.id, opts, len);
703 void layerUp(ControlProtocol &cp) {
711 void layerDown(ControlProtocol &cp) {
723 _reqAccm = _reqMagic =
true;
725 sendConfigRequest(_lcp);
730 switch (_lcp.state) {
735 if (++_lcp.retries > 10) _lcp.retries = 10;
736 sendConfigRequest(_lcp);
743 if (!_passive) lcpOpen();
751 logf(
"LCP up (peer MRU %u)", _peerMRU);
752 _enc.setACCM(_peerAccm);
754 _echoNext = millis() + _echoInterval;
770 _peerAccm = 0xFFFFFFFF;
771 _enc.setACCM(_peerAccm);
777 size_t lcpBuildRequest(uint8_t *p) {
788 memcpy(p + n, &_magic, 4);
794 void lcpApplyNakRej(uint8_t code,
const uint8_t *p,
size_t len) {
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;
813 uint8_t lcpCheckRequest(
const uint8_t *p,
size_t len, uint8_t *reply,
819 uint32_t accm = 0xFFFFFFFF;
820 bool needPap =
false;
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;
835 mru = get16(opt + 2);
843 accm = (uint32_t)opt[2] << 24 | (uint32_t)opt[3] << 16 |
844 (uint32_t)opt[4] << 8 | opt[5];
848 if (olen >= 4 && get16(opt + 2) ==
PPP_PAP) {
850 }
else if (nakLen + 4 <=
sizeof(naks)) {
861 }
else if (_reqMagic && memcmp(opt + 2, &_magic, 4) == 0 &&
862 nakLen + 6 <=
sizeof(naks)) {
864 uint32_t m = newMagic();
867 memcpy(naks + nakLen, &m, 4);
874 if (olen != 2) reject =
true;
880 if (reject && rejLen + olen <= 128) {
881 memcpy(reply + rejLen, opt, olen);
892 memcpy(reply, naks, nakLen);
900 size_t n = len < 128 ? len : 128;
909 uint8_t data[2 +
sizeof(_user) +
sizeof(_password)];
910 size_t ul = strlen(_user);
911 size_t pl = strlen(_password);
913 data[n++] = (uint8_t)ul;
914 memcpy(data + n, _user, ul);
916 data[n++] = (uint8_t)pl;
917 memcpy(data + n, _password, pl);
919 uint8_t pkt[4 +
sizeof(data)];
922 put16(pkt + 2, (uint16_t)(n + 4));
923 memcpy(pkt + 4, data, n);
924 sendFrame(
PPP_PAP, pkt, n + 4);
926 if (_papTimer == 0) _papTimer = 1;
931 if (++_papRetries > 10) {
932 logf(
"PAP: no response");
939 void handlePap(
const uint8_t *pkt,
size_t len) {
941 uint8_t code = pkt[0];
942 if (pkt[1] != _papId)
return;
944 logf(
"PAP: authenticated");
948 }
else if (code == 3) {
949 logf(
"PAP: authentication failed");
956 static const uint8_t reason[] =
"close";
969 sendConfigRequest(_ipcp);
974 switch (_ipcp.state) {
978 if (++_ipcp.retries > 10) {
979 logf(
"IPCP: no response, restarting LCP");
983 sendConfigRequest(_ipcp);
996 logf(
"IPCP: no local address");
1002 logf(
"IPCP up: local %s peer %s",
ipStr(_ourIP, a),
ipStr(_peerIP, b));
1014 static void putAddr(uint8_t *p,
size_t &n, uint8_t type,
ip4_t addr) {
1017 memcpy(p + n, &addr, 4);
1021 size_t ipcpBuildRequest(uint8_t *p) {
1024 if (_reqDns1) putAddr(p, n,
IPCP_DNS1, _dns1);
1025 if (_reqDns2) putAddr(p, n,
IPCP_DNS2, _dns2);
1029 void ipcpApplyNakRej(uint8_t code,
const uint8_t *p,
size_t len) {
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;
1037 if (olen == 6) memcpy(&v, p + pos + 2, 4);
1067 uint8_t ipcpCheckRequest(
const uint8_t *p,
size_t len, uint8_t *reply,
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;
1082 if (olen == 6) memcpy(&v, opt + 2, 4);
1083 bool reject =
false;
1088 }
else if (v == 0 || (v == _ourIP && _ourIP != 0)) {
1090 if (_cfgPeer != 0 && _cfgPeer != _ourIP) {
1102 if (olen != 6 || offer == 0) {
1104 }
else if (v != offer) {
1105 putAddr(naks, nakLen, type, offer);
1114 if (reject && rejLen + olen <= 128) {
1115 memcpy(reply + rejLen, opt, olen);
1126 memcpy(reply, naks, nakLen);
1130 _peerIP = peer != 0 ? peer : _cfgPeer;
1131 size_t n = len < 128 ? len : 128;
1132 memcpy(reply, p, n);
#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