ESP8266-based WiFi serial modem emulator ROM
1/*
2 * WiFiPPP
3 * Copyright (c) 2021 joshua stein <jcs@jcs.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include <lwip/napt.h>
19#include <lwip/netif.h>
20#include <netif/ppp/ppp.h>
21#include <netif/ppp/pppos.h>
22
23#include "wifippp.h"
24
25#define PPP_BUF_SIZE 128
26static uint8_t ppp_buf[PPP_BUF_SIZE];
27static struct netif ppp_netif;
28static ppp_pcb *_ppp = NULL;
29
30#define PPP_TIMEOUT_SECS (60 * 10)
31long last_ppp_input = 0;
32
33u32_t ppp_output_cb(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx);
34void ppp_status_cb(ppp_pcb* pcb, int err_code, void *ctx);
35void ppp_setup_nat(struct netif *nif);
36
37bool
38ppp_start(void)
39{
40 ip4_addr_t s_addr, c_addr, d_addr;
41
42 _ppp = pppos_create(&ppp_netif, ppp_output_cb, ppp_status_cb, nullptr);
43 if (!_ppp) {
44 syslog.log(LOG_ERR, "pppos_create failed");
45 return false;
46 }
47
48 ip_addr_copy(s_addr, settings->ppp_server_ip);
49 ip_addr_copy(c_addr, settings->ppp_client_ip);
50
51 ppp_set_ipcp_ouraddr(_ppp, &s_addr);
52 ppp_set_ipcp_hisaddr(_ppp, &c_addr); /* or hers! */
53
54 ip4_addr_set_u32(&d_addr, WiFi.dnsIP());
55 ppp_set_ipcp_dnsaddr(_ppp, 0, &d_addr);
56
57 outputf("CONNECT %d %s:PPP\r\n", Serial.baudRate(),
58 ipaddr_ntoa(&s_addr));
59 serial_dcd(true);
60
61 last_ppp_input = millis();
62 ppp_listen(_ppp);
63
64#ifdef PPP_TRACE
65 syslog.log(LOG_INFO, "starting PPP negotiation");
66#endif
67
68 return true;
69}
70
71u32_t
72ppp_output_cb(__attribute__((unused)) ppp_pcb *pcb, u8_t *data, u32_t len,
73 __attribute__((unused)) void *ctx)
74{
75#ifdef PPP_TRACE
76 long now = millis();
77#endif
78
79 serial_write(data, len);
80
81#ifdef PPP_TRACE
82 long elap = millis();
83 syslog.logf(LOG_DEBUG, "forwarded %ld PPP bytes out in %ldms", len,
84 elap - now);
85#endif
86
87 return len;
88}
89
90void
91ppp_stop(bool wait)
92{
93 unsigned long now = millis();
94
95 if (wait) {
96 ppp_close(_ppp, 0);
97
98 while (state == STATE_PPP && now - millis() < 1000) {
99 pppos_input(_ppp, ppp_buf, 0);
100 yield();
101 }
102 }
103
104 if (state == STATE_PPP)
105 ppp_close(_ppp, 1);
106}
107
108void
109ppp_process(void)
110{
111 size_t bytes;
112 long now = millis();
113
114 if (state != STATE_PPP) {
115 syslog.logf(LOG_ERR, "%s but state is %d!", __func__, state);
116 return;
117 }
118
119 bytes = Serial.available();
120 if (!bytes) {
121 if (now - last_ppp_input > (1000 * PPP_TIMEOUT_SECS)) {
122 syslog.logf(LOG_WARNING, "no PPP input in %ld secs, "
123 "hanging up", (now - last_ppp_input) / 1000);
124 ppp_close(_ppp, 0);
125 last_ppp_input = now;
126 }
127 return;
128 }
129
130 last_ppp_input = now;
131 if (bytes > PPP_BUF_SIZE)
132 bytes = PPP_BUF_SIZE;
133
134 bytes = Serial.readBytes(ppp_buf, bytes);
135 pppos_input(_ppp, ppp_buf, bytes);
136
137#ifdef PPP_TRACE
138 long elap = millis();
139 syslog.logf(LOG_DEBUG, "processed %zu PPP input bytes in %ldms", bytes,
140 elap - now);
141#endif
142}
143
144void
145ppp_status_cb(ppp_pcb *pcb, int err, __attribute__((unused)) void *ctx)
146{
147 struct netif *nif = ppp_netif(pcb);
148
149 switch (err) {
150 case PPPERR_NONE:
151 ppp_setup_nat(nif);
152 syslog.logf(LOG_DEBUG, "PPP session established, free mem %d",
153 ESP.getFreeHeap());
154 return;
155 case PPPERR_USER:
156#ifdef PPP_TRACE
157 syslog.log(LOG_DEBUG, "ending PPP session, "
158 "returning to AT mode");
159#endif
160 ppp_free(_ppp);
161 _ppp = NULL;
162 state = STATE_AT;
163 serial_dcd(false);
164 outputf("\r\nNO CARRIER\r\n");
165 return;
166 default:
167#ifndef PPP_TRACE
168 if (err == PPPERR_USER)
169 break;
170#endif
171 syslog.logf(LOG_ERR, "%s: err %d", __func__, err);
172
173#ifdef PPP_TRACE
174 syslog.log(LOG_DEBUG, "closing PPP session");
175#endif
176 ppp_close(_ppp, 0);
177 break;
178 }
179}
180
181void
182ppp_setup_nat(struct netif *nif)
183{
184 err_t ret = ip_napt_init(IP_NAPT_MAX, IP_PORTMAP_MAX);
185 if (ret == ERR_BUF) {
186 /* already initialized */
187 } else if (ret != ERR_OK) {
188 syslog.logf(LOG_ERR, "NAPT initialization failed (%d)",
189 (int)ret);
190 return;
191 }
192
193 ret = ip_napt_enable_no(nif->num, 1);
194 if (ret != ERR_OK) {
195 syslog.logf(LOG_INFO, "ip_napt_enable(%d) failed: %d",
196 nif->num, (int)ret);
197 return;
198 }
199
200 /* forward port 22 on esp8266 to our client's PPP address */
201 ret = ip_portmap_add(IP_PROTO_TCP, WiFi.localIP(), 22,
202 ip_2_ip4(&nif->gw)->addr, 22);
203 if (ret != 1) {
204 syslog.logf(LOG_ERR, "failed setting up NAPT portmap: %d",
205 (int)ret);
206 return;
207 }
208}