ESP8266-based WiFi serial modem emulator ROM
at main 208 lines 4.8 kB view raw
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}