From 1b8f6d2e8e2dc7008660732b96197fd1ae1a1439 Mon Sep 17 00:00:00 2001 From: 4m1g0 Date: Sun, 3 Apr 2016 03:31:57 +0200 Subject: [PATCH 01/46] Allow PSK instead of passphrase in WiFiSTA::begin In WPA protocol, the maximum length of the passphrases are 64 characters in order to distinguish them from the actual PSK who is 64 ASCII characters long, so in most systems if a 64 chars string is passed, it is assumed to be a PSK, otherwise is treated as a passphrase and is used to compute the PSK. --- libraries/ESP8266WiFi/src/ESP8266WiFiSTA.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.cpp index 0d34a5a64..e8f538dfe 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.cpp @@ -106,7 +106,7 @@ wl_status_t ESP8266WiFiSTAClass::begin(const char* ssid, const char *passphrase, return WL_CONNECT_FAILED; } - if(passphrase && strlen(passphrase) > 63) { + if(passphrase && strlen(passphrase) > 64) { // fail passphrase too long! return WL_CONNECT_FAILED; } @@ -115,7 +115,10 @@ wl_status_t ESP8266WiFiSTAClass::begin(const char* ssid, const char *passphrase, strcpy(reinterpret_cast(conf.ssid), ssid); if(passphrase) { - strcpy(reinterpret_cast(conf.password), passphrase); + if (strlen(passphrase) == 64) // it's not a passphrase, is the PSK + memcpy(reinterpret_cast(conf.password), passphrase, 64); + else + strcpy(reinterpret_cast(conf.password), passphrase); } else { *conf.password = 0; } From bda212552c102290110128e1d8b0c90acca62400 Mon Sep 17 00:00:00 2001 From: Marco Date: Mon, 4 Apr 2016 22:10:36 -0700 Subject: [PATCH 02/46] Fix SPI.transfer16() using wrong endianness --- libraries/SPI/SPI.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/SPI/SPI.cpp b/libraries/SPI/SPI.cpp index c8341b1fa..e9c6cf35d 100644 --- a/libraries/SPI/SPI.cpp +++ b/libraries/SPI/SPI.cpp @@ -246,13 +246,13 @@ uint16_t SPIClass::transfer16(uint16_t data) { in.val = data; if((SPI1C & (SPICWBO | SPICRBO))) { - //MSBFIRST - out.msb = transfer(in.msb); - out.lsb = transfer(in.lsb); - } else { //LSBFIRST out.lsb = transfer(in.lsb); out.msb = transfer(in.msb); + } else { + //MSBFIRST + out.msb = transfer(in.msb); + out.lsb = transfer(in.lsb); } return out.val; } From 8c675236c74189194e3170471e72dc9c2fe107b3 Mon Sep 17 00:00:00 2001 From: gpepe Date: Thu, 7 Apr 2016 22:22:16 +0200 Subject: [PATCH 03/46] Update Parsing.cpp Complete read POST/GET request. --- libraries/ESP8266WebServer/src/Parsing.cpp | 23 ++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/libraries/ESP8266WebServer/src/Parsing.cpp b/libraries/ESP8266WebServer/src/Parsing.cpp index 7eac20def..a92ed13d3 100644 --- a/libraries/ESP8266WebServer/src/Parsing.cpp +++ b/libraries/ESP8266WebServer/src/Parsing.cpp @@ -138,14 +138,21 @@ bool ESP8266WebServer::_parseRequest(WiFiClient& client) { if (!isForm){ if (searchStr != "") searchStr += '&'; - //some clients send headers first and data after (like we do) - //give them a chance - int tries = 100;//100ms max wait - while(!client.available() && tries--)delay(1); - size_t plainLen = client.available(); - char *plainBuf = (char*)malloc(plainLen+1); - client.readBytes(plainBuf, plainLen); - plainBuf[plainLen] = '\0'; + char *plainBuf = NULL; + size_t plainLen = 0; + while ( (plainLen == 0) || (plainLen < contentLength)) + { + //some clients send headers first and data after (like we do) + //give them a chance + int tries = 100;//100ms max wait + while(!client.available() && tries--)delay(1); + size_t newLen = client.available(); + if (!newLen) break; + plainBuf = (plainBuf == NULL) ? (char *) malloc(newLen + 1) : (char *) realloc(plainBuf, plainLen + newLen + 1); + client.readBytes(&plainBuf[plainLen], newLen); + plainLen += newLen; + plainBuf[plainLen] = '\0'; + } #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.print("Plain: "); DEBUG_OUTPUT.println(plainBuf); From 3a95fb5a94f5176b36ad2d4b545c0399495eeac7 Mon Sep 17 00:00:00 2001 From: gpepe Date: Thu, 7 Apr 2016 23:32:04 +0200 Subject: [PATCH 04/46] Update Parsing.cpp --- libraries/ESP8266WebServer/src/Parsing.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/ESP8266WebServer/src/Parsing.cpp b/libraries/ESP8266WebServer/src/Parsing.cpp index a92ed13d3..61e412910 100644 --- a/libraries/ESP8266WebServer/src/Parsing.cpp +++ b/libraries/ESP8266WebServer/src/Parsing.cpp @@ -138,7 +138,7 @@ bool ESP8266WebServer::_parseRequest(WiFiClient& client) { if (!isForm){ if (searchStr != "") searchStr += '&'; - char *plainBuf = NULL; + char *plainBuf = nullptr; size_t plainLen = 0; while ( (plainLen == 0) || (plainLen < contentLength)) { @@ -148,7 +148,7 @@ bool ESP8266WebServer::_parseRequest(WiFiClient& client) { while(!client.available() && tries--)delay(1); size_t newLen = client.available(); if (!newLen) break; - plainBuf = (plainBuf == NULL) ? (char *) malloc(newLen + 1) : (char *) realloc(plainBuf, plainLen + newLen + 1); + plainBuf = (plainBuf == nullptr) ? (char *) malloc(newLen + 1) : (char *) realloc(plainBuf, plainLen + newLen + 1); client.readBytes(&plainBuf[plainLen], newLen); plainLen += newLen; plainBuf[plainLen] = '\0'; From 502c45c157086566d749ba47feb72ba27fb8d43e Mon Sep 17 00:00:00 2001 From: gpepe Date: Fri, 8 Apr 2016 04:40:51 +0200 Subject: [PATCH 05/46] Update Parsing.cpp --- libraries/ESP8266WebServer/src/Parsing.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/ESP8266WebServer/src/Parsing.cpp b/libraries/ESP8266WebServer/src/Parsing.cpp index 61e412910..4b1f375fa 100644 --- a/libraries/ESP8266WebServer/src/Parsing.cpp +++ b/libraries/ESP8266WebServer/src/Parsing.cpp @@ -140,7 +140,7 @@ bool ESP8266WebServer::_parseRequest(WiFiClient& client) { if (searchStr != "") searchStr += '&'; char *plainBuf = nullptr; size_t plainLen = 0; - while ( (plainLen == 0) || (plainLen < contentLength)) + do { //some clients send headers first and data after (like we do) //give them a chance @@ -152,7 +152,7 @@ bool ESP8266WebServer::_parseRequest(WiFiClient& client) { client.readBytes(&plainBuf[plainLen], newLen); plainLen += newLen; plainBuf[plainLen] = '\0'; - } + } while (plainLen < contentLength); #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.print("Plain: "); DEBUG_OUTPUT.println(plainBuf); From 4684e44902f9fd8839029b7ee161324c5348606a Mon Sep 17 00:00:00 2001 From: Frank Sautter Date: Fri, 8 Apr 2016 10:26:11 +0200 Subject: [PATCH 06/46] Re-enable old behaviour if passphrase string is empty An empty passphrase string should enable AUTH_OPEN mode of softAP. This was the behaviour before commit 293e55c. Additionally make the type of checking for empty strings consistent. --- libraries/ESP8266WiFi/src/ESP8266WiFiAP.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiAP.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiAP.cpp index c5705234e..81f3c3a70 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiAP.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiAP.cpp @@ -89,13 +89,13 @@ bool ESP8266WiFiAPClass::softAP(const char* ssid, const char* passphrase, int ch return false; } - if(!ssid || *ssid == 0 || strlen(ssid) > 31) { + if(!ssid || strlen(ssid) == 0 || strlen(ssid) > 31) { // fail SSID too long or missing! DEBUG_WIFI("[AP] SSID too long or missing!\n"); return false; } - if(passphrase && (strlen(passphrase) > 63 || strlen(passphrase) < 8)) { + if(passphrase && strlen(passphrase) > 0 && (strlen(passphrase) > 63 || strlen(passphrase) < 8)) { // fail passphrase to long or short! DEBUG_WIFI("[AP] fail passphrase to long or short!\n"); return false; From 1bfec4ea8e8e956610a94f4f5cbfa807e40dbd0c Mon Sep 17 00:00:00 2001 From: Jacob Killelea Date: Fri, 8 Apr 2016 19:21:58 -0600 Subject: [PATCH 07/46] Spelling correction in comments --- libraries/ESP8266WiFi/src/ESP8266WiFiAP.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiAP.h b/libraries/ESP8266WiFi/src/ESP8266WiFiAP.h index e248d81f8..c9eff914a 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiAP.h +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiAP.h @@ -1,6 +1,6 @@ /* ESP8266WiFiAP.h - esp8266 Wifi support. - Based on WiFi.h from Ardiono WiFi shield library. + Based on WiFi.h from Arduino WiFi shield library. Copyright (c) 2011-2014 Arduino. All right reserved. Modified by Ivan Grokhotkov, December 2014 Reworked by Markus Sattler, December 2015 From 70018176f2ba59e3027d94dc8b4804a91cf81764 Mon Sep 17 00:00:00 2001 From: Hemal Chevli Date: Sat, 9 Apr 2016 11:56:57 +0530 Subject: [PATCH 08/46] added descriptions for fopen modes added fopen modes for easy reference Source:http://man7.org/linux/man-pages/man3/fopen.3.html --- doc/filesystem.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/doc/filesystem.md b/doc/filesystem.md index 6ee097513..d3dbebf5f 100644 --- a/doc/filesystem.md +++ b/doc/filesystem.md @@ -105,6 +105,28 @@ Opens a file. `path` should be an absolute path starting with a slash (e.g. `/dir/filename.txt`). `mode` is a string specifying access mode. It can be one of "r", "w", "a", "r+", "w+", "a+". Meaning of these modes is the same as for `fopen` C function. + + r Open text file for reading. The stream is positioned at the + beginning of the file. + + r+ Open for reading and writing. The stream is positioned at the + beginning of the file. + + w Truncate file to zero length or create text file for writing. + The stream is positioned at the beginning of the file. + + w+ Open for reading and writing. The file is created if it does + not exist, otherwise it is truncated. The stream is + positioned at the beginning of the file. + + a Open for appending (writing at end of file). The file is + created if it does not exist. The stream is positioned at the + end of the file. + + a+ Open for reading and appending (writing at end of file). The + file is created if it does not exist. The initial file + position for reading is at the beginning of the file, but + output is always appended to the end of the file. Returns *File* object. To check whether the file was opened successfully, use the boolean operator. From 93d57fabe23687d0e0bddb6739b1795d6fe92e3f Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 27 Jan 2016 13:45:10 +0300 Subject: [PATCH 09/46] Remove overloads of HTTPClient::begin which take const char* Since the data is stored as Strings internally, these methods do not serve as an optimisation --- .../src/ESP8266HTTPClient.cpp | 27 ++----------------- .../ESP8266HTTPClient/src/ESP8266HTTPClient.h | 5 +--- 2 files changed, 3 insertions(+), 29 deletions(-) diff --git a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp index fd11c0360..d39511140 100644 --- a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp +++ b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp @@ -78,15 +78,6 @@ HTTPClient::~HTTPClient() { } } -/** - * phasing the url for all needed informations - * @param url const char * - * @param httpsFingerprint const char * - */ -void HTTPClient::begin(const char *url, const char * httpsFingerprint) { - begin(String(url), String(httpsFingerprint)); -} - /** * phasing the url for all needed informations * @param url String @@ -158,18 +149,7 @@ void HTTPClient::begin(String url, String httpsFingerprint) { } -/** - * begin - * @param host const char * - * @param port uint16_t - * @param url const char * - * @param https bool - * @param httpsFingerprint const char * - */ -void HTTPClient::begin(const char *host, uint16_t port, const char * url, bool https, const char * httpsFingerprint) { - - DEBUG_HTTPCLIENT("[HTTP-Client][begin] host: %s port:%d url: %s https: %d httpsFingerprint: %s\n", host, port, url, https, httpsFingerprint); - +void HTTPClient::begin(String host, uint16_t port, String url, bool https, String httpsFingerprint) { _host = host; _port = port; _url = url; @@ -181,10 +161,7 @@ void HTTPClient::begin(const char *host, uint16_t port, const char * url, bool h _Headers = ""; -} - -void HTTPClient::begin(String host, uint16_t port, String url, bool https, String httpsFingerprint) { - begin(host.c_str(), port, url.c_str(), https, httpsFingerprint.c_str()); + DEBUG_HTTPCLIENT("[HTTP-Client][begin] host: %s port:%d url: %s https: %d httpsFingerprint: %s\n", host, port, url, https, httpsFingerprint); } /** diff --git a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h index 0b865c2a6..c83f482df 100644 --- a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h +++ b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h @@ -125,14 +125,11 @@ class HTTPClient { HTTPClient(); ~HTTPClient(); - void begin(const char *url, const char * httpsFingerprint = ""); void begin(String url, String httpsFingerprint = ""); - - void begin(const char *host, uint16_t port, const char * url = "/", bool https = false, const char * httpsFingerprint = ""); void begin(String host, uint16_t port, String url = "/", bool https = false, String httpsFingerprint = ""); void end(void); - + bool connected(void); void setReuse(bool reuse); /// keep-alive From cae40392255a5a26ddbfcba59d02b076d2e389d4 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 5 Apr 2016 00:25:13 +0300 Subject: [PATCH 10/46] HTTPClient: decouple transport layer handling --- .../src/ESP8266HTTPClient.cpp | 320 ++++++++++-------- .../ESP8266HTTPClient/src/ESP8266HTTPClient.h | 83 +++-- 2 files changed, 218 insertions(+), 185 deletions(-) diff --git a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp index d39511140..1829e9812 100644 --- a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp +++ b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp @@ -30,138 +30,178 @@ #include "ESP8266HTTPClient.h" +class TransportTraits { +public: + virtual std::unique_ptr create() + { + return std::unique_ptr(new WiFiClient()); + } + + virtual bool verify(WiFiClient& client, const char* host) + { + return true; + } +}; + +class TLSTraits : public TransportTraits { +public: + TLSTraits(const String& fingerprint) : + _fingerprint(fingerprint) + { + } + + std::unique_ptr create() override + { + return std::unique_ptr(new WiFiClientSecure()); + } + + bool verify(WiFiClient& client, const char* host) override + { + auto wcs = reinterpret_cast(client); + return wcs.verify(_fingerprint.c_str(), host); + } + +protected: + String _fingerprint; +}; + /** - * constractor + * constructor */ -HTTPClient::HTTPClient() { - _tcp = NULL; - _tcps = NULL; - - _port = 0; - - _reuse = false; - _tcpTimeout = HTTPCLIENT_DEFAULT_TCP_TIMEOUT; - _useHTTP10 = false; - - _https = false; - - _userAgent = "ESP8266HTTPClient"; - - _headerKeysCount = 0; - _currentHeaders = NULL; - - _returnCode = 0; - _size = -1; - _canReuse = false; - _transferEncoding = HTTPC_TE_IDENTITY; - +HTTPClient::HTTPClient() +{ } /** - * deconstractor + * destructor */ -HTTPClient::~HTTPClient() { - - if(_tcps) { - _tcps->stop(); - delete _tcps; - _tcps = NULL; - _tcp = NULL; - } else if(_tcp) { +HTTPClient::~HTTPClient() +{ + if(_tcp) { _tcp->stop(); - delete _tcp; - _tcp = NULL; } - if(_currentHeaders) { delete[] _currentHeaders; } } -/** - * phasing the url for all needed informations - * @param url String - * @param httpsFingerprint String - */ -void HTTPClient::begin(String url, String httpsFingerprint) { - - DEBUG_HTTPCLIENT("[HTTP-Client][begin] url: %s\n", url.c_str()); - - _httpsFingerprint = httpsFingerprint; - _returnCode = 0; - _size = -1; - - _Headers = ""; - - String protocol; - // check for : (http: or https: - int index = url.indexOf(':'); - //int index2; - bool hasPort = false; - if(index >= 0) { - protocol = url.substring(0, index); - url.remove(0, (index + 3)); // remove http:// or https:// - - index = url.indexOf('/'); - String host = url.substring(0, index); - url.remove(0, index); // remove host part - - // get Authorization - index = host.indexOf('@'); - if(index >= 0) { - // auth info - String auth = host.substring(0, index); - host.remove(0, index + 1); // remove auth part including @ - _base64Authorization = base64::encode(auth); - } - - // get port - index = host.indexOf(':'); - if(index >= 0) { - _host = host.substring(0, index); // hostname - host.remove(0, (index + 1)); // remove hostname + : - _port = host.toInt(); // get port - hasPort = true; - } else { - _host = host; - } - - _url = url; - - if(protocol.equalsIgnoreCase("http")) { - _https = false; - if(!hasPort) { - _port = 80; - } - } else if(protocol.equalsIgnoreCase("https")) { - _https = true; - if(!hasPort) { - _port = 443; - } - } else { - DEBUG_HTTPCLIENT("[HTTP-Client][begin] protocol: %s unknown?!\n", protocol.c_str()); - return; - } +bool HTTPClient::begin(String url, String httpsFingerprint) { + if (httpsFingerprint.length() == 0) { + return false; } - - DEBUG_HTTPCLIENT("[HTTP-Client][begin] host: %s port: %d url: %s https: %d httpsFingerprint: %s\n", _host.c_str(), _port, _url.c_str(), _https, _httpsFingerprint.c_str()); - + if (!begin(url)) { + return false; + } + _transportTraits = TransportTraitsPtr(new TLSTraits(httpsFingerprint)); + DEBUG_HTTPCLIENT("[HTTP-Client][begin] httpsFingerprint: %s\n", httpsFingerprint.c_str()); + return true; } -void HTTPClient::begin(String host, uint16_t port, String url, bool https, String httpsFingerprint) { - _host = host; - _port = port; - _url = url; - _https = https; - _httpsFingerprint = httpsFingerprint; - +void HTTPClient::clear() +{ _returnCode = 0; _size = -1; + _headers = ""; +} - _Headers = ""; - DEBUG_HTTPCLIENT("[HTTP-Client][begin] host: %s port:%d url: %s https: %d httpsFingerprint: %s\n", host, port, url, https, httpsFingerprint); +/** + * parsing the url for all needed parameters + * @param url String + */ +bool HTTPClient::begin(String url) +{ + DEBUG_HTTPCLIENT("[HTTP-Client][begin] url: %s\n", url.c_str()); + bool hasPort = false; + clear(); + + // check for : (http: or https: + int index = url.indexOf(':'); + if(index < 0) { + DEBUG_HTTPCLIENT("[HTTP-Client][begin] failed to parse protocol\n"); + return false; + } + + _protocol = url.substring(0, index); + url.remove(0, (index + 3)); // remove http:// or https:// + + index = url.indexOf('/'); + String host = url.substring(0, index); + url.remove(0, index); // remove host part + + // get Authorization + index = host.indexOf('@'); + if(index >= 0) { + // auth info + String auth = host.substring(0, index); + host.remove(0, index + 1); // remove auth part including @ + _base64Authorization = base64::encode(auth); + } + + // get port + index = host.indexOf(':'); + if(index >= 0) { + _host = host.substring(0, index); // hostname + host.remove(0, (index + 1)); // remove hostname + : + _port = host.toInt(); // get port + hasPort = true; + } else { + _host = host; + } + _uri = url; + + if(_protocol.equalsIgnoreCase("http")) { + if(!hasPort) { + _port = 80; + } + } else if(_protocol.equalsIgnoreCase("https")) { + if(!hasPort) { + _port = 443; + } + } else { + DEBUG_HTTPCLIENT("[HTTP-Client][begin] protocol: %s unknown?!\n", protocol.c_str()); + return false; + } + _transportTraits = TransportTraitsPtr(new TransportTraits()); + DEBUG_HTTPCLIENT("[HTTP-Client][begin] host: %s port: %d url: %s\n", _host.c_str(), _port, _uri.c_str()); + return true; +} + +bool HTTPClient::begin(String host, uint16_t port, String uri) +{ + clear(); + _host = host; + _port = port; + _uri = uri; + _transportTraits = TransportTraitsPtr(new TransportTraits()); + DEBUG_HTTPCLIENT("[HTTP-Client][begin] host: %s port: %d uri: %s\n", host.c_str(), port, uri.c_str()); + return true; +} + +bool HTTPClient::begin(String host, uint16_t port, String uri, bool https, String httpsFingerprint) +{ + if (https) { + return begin(host, port, uri, httpsFingerprint); + } + else { + return begin(host, port, uri); + } +} + +bool HTTPClient::begin(String host, uint16_t port, String uri, String httpsFingerprint) +{ + clear(); + _host = host; + _port = port; + _uri = uri; + + if (httpsFingerprint.length() == 0) { + return false; + } + _transportTraits = TransportTraitsPtr(new TLSTraits(httpsFingerprint)); + DEBUG_HTTPCLIENT("[HTTP-Client][begin] host: %s port: %d url: %s httpsFingerprint: %s\n", host.c_str(), port, uri.c_str(), httpsFingerprint.c_str()); + return true; } /** @@ -469,31 +509,30 @@ int HTTPClient::getSize(void) { } /** - * deprecated Note: this is not working with https! * returns the stream of the tcp connection * @return WiFiClient */ -WiFiClient & HTTPClient::getStream(void) { +WiFiClient& HTTPClient::getStream(void) { if(connected()) { return *_tcp; } - DEBUG_HTTPCLIENT("[HTTP-Client] no stream to return!?\n"); - - // todo return error? + DEBUG_HTTPCLIENT("[HTTP-Client] getStream: not connected\n"); + static WiFiClient empty; + return empty; } /** * returns the stream of the tcp connection * @return WiFiClient * */ -WiFiClient * HTTPClient::getStreamPtr(void) { +WiFiClient* HTTPClient::getStreamPtr(void) { if(connected()) { - return _tcp; + return _tcp.get(); } - DEBUG_HTTPCLIENT("[HTTP-Client] no stream to return!?\n"); - return NULL; + DEBUG_HTTPCLIENT("[HTTP-Client] getStreamPtr: not connected\n"); + return nullptr; } /** @@ -642,9 +681,9 @@ void HTTPClient::addHeader(const String& name, const String& value, bool first) headerLine += "\r\n"; if(first) { - _Headers = headerLine + _Headers; + _headers = headerLine + _headers; } else { - _Headers += headerLine; + _headers += headerLine; } } @@ -706,24 +745,13 @@ bool HTTPClient::connect(void) { return true; } - if(_https) { - DEBUG_HTTPCLIENT("[HTTP-Client] connect https...\n"); - if(_tcps) { - delete _tcps; - _tcps = NULL; - _tcp = NULL; - } - _tcps = new WiFiClientSecure(); - _tcp = _tcps; - } else { - DEBUG_HTTPCLIENT("[HTTP-Client] connect http...\n"); - if(_tcp) { - delete _tcp; - _tcp = NULL; - } - _tcp = new WiFiClient(); + if (!_transportTraits) { + DEBUG_HTTPCLIENT("[HTTP-Client] _transportTraits is null (HTTPClient::begin not called?)\n"); + return false; } + _tcp = _transportTraits->create(); + if(!_tcp->connect(_host.c_str(), _port)) { DEBUG_HTTPCLIENT("[HTTP-Client] failed connect to %s:%u\n", _host.c_str(), _port); return false; @@ -731,14 +759,10 @@ bool HTTPClient::connect(void) { DEBUG_HTTPCLIENT("[HTTP-Client] connected to %s:%u\n", _host.c_str(), _port); - if(_https && _httpsFingerprint.length() > 0) { - if(_tcps->verify(_httpsFingerprint.c_str(), _host.c_str())) { - DEBUG_HTTPCLIENT("[HTTP-Client] https certificate matches\n"); - } else { - DEBUG_HTTPCLIENT("[HTTP-Client] https certificate doesn't match!\n"); - _tcp->stop(); - return false; - } + if (!_transportTraits->verify(*_tcp, _host.c_str())) { + DEBUG_HTTPCLIENT("[HTTP-Client] transport level verify failed\n"); + _tcp->stop(); + return false; } // set Timeout for readBytesUntil and readStringUntil @@ -760,7 +784,7 @@ bool HTTPClient::sendHeader(const char * type) { return false; } - String header = String(type) + " " + _url + " HTTP/1."; + String header = String(type) + " " + _uri + " HTTP/1."; if(_useHTTP10) { header += "0"; @@ -788,7 +812,7 @@ bool HTTPClient::sendHeader(const char * type) { header += "Authorization: Basic " + _base64Authorization + "\r\n"; } - header += _Headers + "\r\n"; + header += _headers + "\r\n"; return (_tcp->write(header.c_str(), header.length()) == header.length()); } diff --git a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h index c83f482df..27db371a5 100644 --- a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h +++ b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h @@ -25,6 +25,10 @@ #ifndef ESP8266HTTPClient_H_ #define ESP8266HTTPClient_H_ +#include +#include +#include + #ifdef DEBUG_ESP_HTTP_CLIENT #ifdef DEBUG_ESP_PORT #define DEBUG_HTTPCLIENT(...) DEBUG_ESP_PORT.printf( __VA_ARGS__ ) @@ -120,16 +124,23 @@ typedef enum { HTTPC_TE_CHUNKED } transferEncoding_t; +class TransportTraits; +typedef std::unique_ptr TransportTraitsPtr; + class HTTPClient { public: HTTPClient(); ~HTTPClient(); - void begin(String url, String httpsFingerprint = ""); - void begin(String host, uint16_t port, String url = "/", bool https = false, String httpsFingerprint = ""); + bool begin(String url); + bool begin(String url, String httpsFingerprint); + bool begin(String host, uint16_t port, String uri = "/"); + bool begin(String host, uint16_t port, String uri, String httpsFingerprint); + // deprecated, use the overload above instead + bool begin(String host, uint16_t port, String uri, bool https, String httpsFingerprint) __attribute__ ((deprecated)); void end(void); - + bool connected(void); void setReuse(bool reuse); /// keep-alive @@ -161,53 +172,51 @@ class HTTPClient { int getSize(void); - WiFiClient & getStream(void) __attribute__ ((deprecated)) ; - WiFiClient * getStreamPtr(void); - int writeToStream(Stream * stream); + WiFiClient& getStream(void); + WiFiClient* getStreamPtr(void); + int writeToStream(Stream* stream); String getString(void); static String errorToString(int error); protected: - struct RequestArgument { - String key; - String value; + String key; + String value; }; - - WiFiClient * _tcp; - WiFiClientSecure * _tcps; - - /// request handling - String _host; - uint16_t _port; - bool _reuse; - uint16_t _tcpTimeout; - bool _useHTTP10; - - String _url; - bool _https; - String _httpsFingerprint; - - String _Headers; - String _userAgent; - String _base64Authorization; - - /// Response handling - RequestArgument* _currentHeaders; - size_t _headerKeysCount; - - int _returnCode; - int _size; - bool _canReuse; - transferEncoding_t _transferEncoding; - + void clear(); int returnError(int error); bool connect(void); bool sendHeader(const char * type); int handleHeaderResponse(); int writeToStreamDataBlock(Stream * stream, int len); + + + TransportTraitsPtr _transportTraits; + std::unique_ptr _tcp; + + /// request handling + String _host; + uint16_t _port = 0; + bool _reuse = false; + uint16_t _tcpTimeout = HTTPCLIENT_DEFAULT_TCP_TIMEOUT; + bool _useHTTP10 = false; + + String _uri; + String _protocol; + String _headers; + String _userAgent = "ESP8266HTTPClient"; + String _base64Authorization; + + /// Response handling + RequestArgument* _currentHeaders = nullptr; + size_t _headerKeysCount = 0; + + int _returnCode = 0; + int _size = -1; + bool _canReuse = false; + transferEncoding_t _transferEncoding = HTTPC_TE_IDENTITY; }; From c450023a321c4f375336e0ff56b833e7e3c5ca0d Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 5 Apr 2016 01:56:29 +0300 Subject: [PATCH 11/46] ESP8266httpUpdate: decouple HTTPS overloads --- .../src/ESP8266httpUpdate.cpp | 748 +++++++++--------- .../ESP8266httpUpdate/src/ESP8266httpUpdate.h | 190 +++-- 2 files changed, 489 insertions(+), 449 deletions(-) diff --git a/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp b/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp index 5d2fb2782..ee4365385 100644 --- a/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp +++ b/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp @@ -1,365 +1,383 @@ -/** - * - * @file ESP8266HTTPUpdate.cpp - * @date 21.06.2015 - * @author Markus Sattler - * - * Copyright (c) 2015 Markus Sattler. All rights reserved. - * This file is part of the ESP8266 Http Updater. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include "ESP8266httpUpdate.h" -#include - -extern "C" uint32_t _SPIFFS_start; -extern "C" uint32_t _SPIFFS_end; - -ESP8266HTTPUpdate::ESP8266HTTPUpdate(void) { -} - -ESP8266HTTPUpdate::~ESP8266HTTPUpdate(void) { -} - -/** - * - * @param url const char * - * @param current_version const char * - * @param httpsFingerprint const char * - * @return t_httpUpdate_return - */ -t_httpUpdate_return ESP8266HTTPUpdate::update(const char * url, const char * current_version, const char * httpsFingerprint, bool reboot) { - HTTPClient http; - http.begin(url, httpsFingerprint); - return handleUpdate(&http, current_version, reboot, false); -} - -/** - * - * @param url const char * - * @param current_version const char * - * @param httpsFingerprint const char * - * @return t_httpUpdate_return - */ -t_httpUpdate_return ESP8266HTTPUpdate::updateSpiffs(const char * url, const char * current_version, const char * httpsFingerprint, bool reboot) { - HTTPClient http; - http.begin(url, httpsFingerprint); - return handleUpdate(&http, current_version, reboot, true); -} - -/** - * - * @param host const char * - * @param port uint16_t - * @param url const char * - * @param current_version const char * - * @param httpsFingerprint const char * - * @return - */ -t_httpUpdate_return ESP8266HTTPUpdate::update(const char * host, uint16_t port, const char * url, const char * current_version, bool https, const char * httpsFingerprint, bool reboot) { - HTTPClient http; - http.begin(host, port, url, https, httpsFingerprint); - return handleUpdate(&http, current_version, reboot, false); -} - -t_httpUpdate_return ESP8266HTTPUpdate::update(String host, uint16_t port, String url, String current_version, bool https, String httpsFingerprint, bool reboot) { - HTTPClient http; - http.begin(host, port, url, https, httpsFingerprint); - return handleUpdate(&http, current_version.c_str(), reboot, false); -} - -/** - * return error code as int - * @return int error code - */ -int ESP8266HTTPUpdate::getLastError(void){ - return lastError; -} - -/** - * return error code as String - * @return String error - */ -String ESP8266HTTPUpdate::getLastErrorString(void) { - - if(lastError == 0) { - return String(); // no error - } - - // error from Update class - if(lastError > 0) { - StreamString error; - Update.printError(error); - error.trim(); // remove line ending - return "Update error: " + error; - } - - // error from http client - if(lastError > -100) { - return "HTTP error: " + HTTPClient::errorToString(lastError); - } - - switch(lastError) { - case HTTP_UE_TOO_LESS_SPACE: - return String("To less space"); - case HTTP_UE_SERVER_NOT_REPORT_SIZE: - return String("Server not Report Size"); - case HTTP_UE_SERVER_FILE_NOT_FOUND: - return String("File not Found (404)"); - case HTTP_UE_SERVER_FORBIDDEN: - return String("Forbidden (403)"); - case HTTP_UE_SERVER_WRONG_HTTP_CODE: - return String("Wrong HTTP code"); - case HTTP_UE_SERVER_FAULTY_MD5: - return String("Faulty MD5"); - case HTTP_UE_BIN_VERIFY_HEADER_FAILED: - return String("Verify bin header failed"); - case HTTP_UE_BIN_FOR_WRONG_FLASH: - return String("bin for wrong flash size"); - } - - return String(); -} - - -/** - * - * @param http HTTPClient * - * @param current_version const char * - * @return t_httpUpdate_return - */ -t_httpUpdate_return ESP8266HTTPUpdate::handleUpdate(HTTPClient * http, const char * current_version, bool reboot, bool spiffs) { - - t_httpUpdate_return ret = HTTP_UPDATE_FAILED; - - // use HTTP/1.0 for update since the update handler not support any transfer Encoding - http->useHTTP10(true); - http->setTimeout(8000); - http->setUserAgent("ESP8266-http-Update"); - http->addHeader("x-ESP8266-STA-MAC", WiFi.macAddress()); - http->addHeader("x-ESP8266-AP-MAC", WiFi.softAPmacAddress()); - http->addHeader("x-ESP8266-free-space", String(ESP.getFreeSketchSpace())); - http->addHeader("x-ESP8266-sketch-size", String(ESP.getSketchSize())); - http->addHeader("x-ESP8266-chip-size", String(ESP.getFlashChipRealSize())); - http->addHeader("x-ESP8266-sdk-version", ESP.getSdkVersion()); - - if(spiffs) { - http->addHeader("x-ESP8266-mode", "spiffs"); - } else { - http->addHeader("x-ESP8266-mode", "sketch"); - } - - if(current_version && current_version[0] != 0x00) { - http->addHeader("x-ESP8266-version", current_version); - } - - const char * headerkeys[] = { "x-MD5" }; - size_t headerkeyssize = sizeof(headerkeys) / sizeof(char*); - - // track these headers - http->collectHeaders(headerkeys, headerkeyssize); - - - int code = http->GET(); - int len = http->getSize(); - - if(code <= 0) { - DEBUG_HTTP_UPDATE("[httpUpdate] HTTP error: %s\n", http->errorToString(code).c_str()); - lastError = code; - http->end(); - return HTTP_UPDATE_FAILED; - } - - - DEBUG_HTTP_UPDATE("[httpUpdate] Header read fin.\n"); - DEBUG_HTTP_UPDATE("[httpUpdate] Server header:\n"); - DEBUG_HTTP_UPDATE("[httpUpdate] - code: %d\n", code); - DEBUG_HTTP_UPDATE("[httpUpdate] - len: %d\n", len); - - if(http->hasHeader("x-MD5")) { - DEBUG_HTTP_UPDATE("[httpUpdate] - MD5: %s\n", http->header("x-MD5").c_str()); - } - - DEBUG_HTTP_UPDATE("[httpUpdate] ESP8266 info:\n"); - DEBUG_HTTP_UPDATE("[httpUpdate] - free Space: %d\n", ESP.getFreeSketchSpace()); - DEBUG_HTTP_UPDATE("[httpUpdate] - current Sketch Size: %d\n", ESP.getSketchSize()); - - if(current_version && current_version[0] != 0x00) { - DEBUG_HTTP_UPDATE("[httpUpdate] - current version: %s\n", current_version); - } - - switch(code) { - case HTTP_CODE_OK: ///< OK (Start Update) - if(len > 0) { - bool startUpdate = true; - if(spiffs) { - size_t spiffsSize = ((size_t) &_SPIFFS_end - (size_t) &_SPIFFS_start); - if(len > (int) spiffsSize) { - DEBUG_HTTP_UPDATE("[httpUpdate] spiffsSize to low (%d) needed: %d\n", spiffsSize, len); - startUpdate = false; - } - } else { - if(len > (int) ESP.getFreeSketchSpace()) { - DEBUG_HTTP_UPDATE("[httpUpdate] FreeSketchSpace to low (%d) needed: %d\n", ESP.getFreeSketchSpace(), len); - startUpdate = false; - } - } - - if(!startUpdate) { - lastError = HTTP_UE_TOO_LESS_SPACE; - ret = HTTP_UPDATE_FAILED; - } else { - - WiFiClient * tcp = http->getStreamPtr(); - - WiFiUDP::stopAll(); - WiFiClient::stopAllExcept(tcp); - - delay(100); - - int command; - - if(spiffs) { - command = U_SPIFFS; - DEBUG_HTTP_UPDATE("[httpUpdate] runUpdate spiffs...\n"); - } else { - command = U_FLASH; - DEBUG_HTTP_UPDATE("[httpUpdate] runUpdate flash...\n"); - } - - if(!spiffs) { - uint8_t buf[4]; - if(tcp->peekBytes(&buf[0], 4) != 4) { - DEBUG_HTTP_UPDATE("[httpUpdate] peekBytes magic header failed\n"); - lastError = HTTP_UE_BIN_VERIFY_HEADER_FAILED; - http->end(); - return HTTP_UPDATE_FAILED; - } - - // check for valid first magic byte - if(buf[0] != 0xE9) { - DEBUG_HTTP_UPDATE("[httpUpdate] magic header not starts with 0xE9\n"); - lastError = HTTP_UE_BIN_VERIFY_HEADER_FAILED; - http->end(); - return HTTP_UPDATE_FAILED; - - } - - uint32_t bin_flash_size = ESP.magicFlashChipSize((buf[3] & 0xf0) >> 4); - - // check if new bin fits to SPI flash - if(bin_flash_size > ESP.getFlashChipRealSize()) { - DEBUG_HTTP_UPDATE("[httpUpdate] magic header, new bin not fits SPI Flash\n"); - lastError = HTTP_UE_BIN_FOR_WRONG_FLASH; - http->end(); - return HTTP_UPDATE_FAILED; - } - } - - if(runUpdate(*tcp, len, http->header("x-MD5"), command)) { - ret = HTTP_UPDATE_OK; - DEBUG_HTTP_UPDATE("[httpUpdate] Update ok\n"); - http->end(); - - if(reboot) { - ESP.restart(); - } - - } else { - ret = HTTP_UPDATE_FAILED; - DEBUG_HTTP_UPDATE("[httpUpdate] Update failed\n"); - } - } - } else { - lastError = HTTP_UE_SERVER_NOT_REPORT_SIZE; - ret = HTTP_UPDATE_FAILED; - DEBUG_HTTP_UPDATE("[httpUpdate] Content-Length is 0 or not set by Server?!\n"); - } - break; - case HTTP_CODE_NOT_MODIFIED: - ///< Not Modified (No updates) - ret = HTTP_UPDATE_NO_UPDATES; - break; - case HTTP_CODE_NOT_FOUND: - lastError = HTTP_UE_SERVER_FILE_NOT_FOUND; - ret = HTTP_UPDATE_FAILED; - break; - case HTTP_CODE_FORBIDDEN: - lastError = HTTP_UE_SERVER_FORBIDDEN; - ret = HTTP_UPDATE_FAILED; - break; - default: - lastError = HTTP_UE_SERVER_WRONG_HTTP_CODE; - ret = HTTP_UPDATE_FAILED; - DEBUG_HTTP_UPDATE("[httpUpdate] HTTP Code is (%d)\n", code); - //http->writeToStream(&Serial1); - break; - } - - http->end(); - return ret; -} - -/** - * write Update to flash - * @param in Stream& - * @param size uint32_t - * @param md5 String - * @return true if Update ok - */ -bool ESP8266HTTPUpdate::runUpdate(Stream& in, uint32_t size, String md5, int command) { - - StreamString error; - - if(!Update.begin(size, command)) { - lastError = Update.getError(); - Update.printError(error); - error.trim(); // remove line ending - DEBUG_HTTP_UPDATE("[httpUpdate] Update.begin failed! (%s)\n", error.c_str()); - return false; - } - - if(md5.length()) { - if(!Update.setMD5(md5.c_str())) { - lastError = HTTP_UE_SERVER_FAULTY_MD5; - DEBUG_HTTP_UPDATE("[httpUpdate] Update.setMD5 failed! (%s)\n", md5.c_str()); - return false; - } - } - - if(Update.writeStream(in) != size) { - lastError = Update.getError(); - Update.printError(error); - error.trim(); // remove line ending - DEBUG_HTTP_UPDATE("[httpUpdate] Update.writeStream failed! (%s)\n", error.c_str()); - return false; - } - - if(!Update.end()) { - lastError = Update.getError(); - Update.printError(error); - error.trim(); // remove line ending - DEBUG_HTTP_UPDATE("[httpUpdate] Update.end failed! (%s)\n", error.c_str()); - return false; - } - - return true; -} - - - -ESP8266HTTPUpdate ESPhttpUpdate; +/** + * + * @file ESP8266HTTPUpdate.cpp + * @date 21.06.2015 + * @author Markus Sattler + * + * Copyright (c) 2015 Markus Sattler. All rights reserved. + * This file is part of the ESP8266 Http Updater. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "ESP8266httpUpdate.h" +#include + +extern "C" uint32_t _SPIFFS_start; +extern "C" uint32_t _SPIFFS_end; + +ESP8266HTTPUpdate::ESP8266HTTPUpdate(void) { +} + +ESP8266HTTPUpdate::~ESP8266HTTPUpdate(void) { +} + +t_httpUpdate_return ESP8266HTTPUpdate::update(const String& url, const String& currentVersion, + const String& httpsFingerprint, bool reboot) +{ + rebootOnUpdate(reboot); + return update(url, currentVersion, httpsFingerprint); +} + +t_httpUpdate_return ESP8266HTTPUpdate::update(const String& url, const String& currentVersion) +{ + HTTPClient http; + http.begin(url); + return handleUpdate(http, currentVersion, false); +} + +t_httpUpdate_return ESP8266HTTPUpdate::update(const String& url, const String& currentVersion, + const String& httpsFingerprint) +{ + HTTPClient http; + http.begin(url, httpsFingerprint); + return handleUpdate(http, currentVersion, false); +} + + +t_httpUpdate_return ESP8266HTTPUpdate::updateSpiffs(const String& url, const String& currentVersion, const String& httpsFingerprint) +{ + HTTPClient http; + http.begin(url, httpsFingerprint); + return handleUpdate(http, currentVersion, true); +} + +t_httpUpdate_return ESP8266HTTPUpdate::updateSpiffs(const String& url, const String& currentVersion) +{ + HTTPClient http; + http.begin(url); + return handleUpdate(http, currentVersion, true); +} + +t_httpUpdate_return ESP8266HTTPUpdate::update(const String& host, uint16_t port, const String& uri, const String& currentVersion, + bool https, const String& httpsFingerprint, bool reboot) +{ + rebootOnUpdate(reboot); + if (httpsFingerprint.length() == 0) { + return update(host, port, uri, currentVersion); + } + else { + return update(host, port, uri, currentVersion, httpsFingerprint); + } +} + +t_httpUpdate_return ESP8266HTTPUpdate::update(const String& host, uint16_t port, const String& uri, + const String& currentVersion) +{ + HTTPClient http; + http.begin(host, port, uri); + return handleUpdate(http, currentVersion, false); +} +t_httpUpdate_return ESP8266HTTPUpdate::update(const String& host, uint16_t port, const String& url, + const String& currentVersion, const String& httpsFingerprint) +{ + HTTPClient http; + http.begin(host, port, url, httpsFingerprint); + return handleUpdate(http, currentVersion, false); + +} + +/** + * return error code as int + * @return int error code + */ +int ESP8266HTTPUpdate::getLastError(void){ + return _lastError; +} + +/** + * return error code as String + * @return String error + */ +String ESP8266HTTPUpdate::getLastErrorString(void) { + + if(_lastError == 0) { + return String(); // no error + } + + // error from Update class + if(_lastError > 0) { + StreamString error; + Update.printError(error); + error.trim(); // remove line ending + return "Update error: " + error; + } + + // error from http client + if(_lastError > -100) { + return "HTTP error: " + HTTPClient::errorToString(_lastError); + } + + switch(_lastError) { + case HTTP_UE_TOO_LESS_SPACE: + return String("To less space"); + case HTTP_UE_SERVER_NOT_REPORT_SIZE: + return String("Server not Report Size"); + case HTTP_UE_SERVER_FILE_NOT_FOUND: + return String("File not Found (404)"); + case HTTP_UE_SERVER_FORBIDDEN: + return String("Forbidden (403)"); + case HTTP_UE_SERVER_WRONG_HTTP_CODE: + return String("Wrong HTTP code"); + case HTTP_UE_SERVER_FAULTY_MD5: + return String("Faulty MD5"); + case HTTP_UE_BIN_VERIFY_HEADER_FAILED: + return String("Verify bin header failed"); + case HTTP_UE_BIN_FOR_WRONG_FLASH: + return String("bin for wrong flash size"); + } + + return String(); +} + + +/** + * + * @param http HTTPClient * + * @param currentVersion const char * + * @return t_httpUpdate_return + */ +t_httpUpdate_return ESP8266HTTPUpdate::handleUpdate(HTTPClient& http, const String& currentVersion, bool spiffs) { + + t_httpUpdate_return ret = HTTP_UPDATE_FAILED; + + // use HTTP/1.0 for update since the update handler not support any transfer Encoding + http.useHTTP10(true); + http.setTimeout(8000); + http.setUserAgent("ESP8266-http-Update"); + http.addHeader("x-ESP8266-STA-MAC", WiFi.macAddress()); + http.addHeader("x-ESP8266-AP-MAC", WiFi.softAPmacAddress()); + http.addHeader("x-ESP8266-free-space", String(ESP.getFreeSketchSpace())); + http.addHeader("x-ESP8266-sketch-size", String(ESP.getSketchSize())); + http.addHeader("x-ESP8266-chip-size", String(ESP.getFlashChipRealSize())); + http.addHeader("x-ESP8266-sdk-version", ESP.getSdkVersion()); + + if(spiffs) { + http.addHeader("x-ESP8266-mode", "spiffs"); + } else { + http.addHeader("x-ESP8266-mode", "sketch"); + } + + if(currentVersion && currentVersion[0] != 0x00) { + http.addHeader("x-ESP8266-version", currentVersion); + } + + const char * headerkeys[] = { "x-MD5" }; + size_t headerkeyssize = sizeof(headerkeys) / sizeof(char*); + + // track these headers + http.collectHeaders(headerkeys, headerkeyssize); + + + int code = http.GET(); + int len = http.getSize(); + + if(code <= 0) { + DEBUG_HTTP_UPDATE("[httpUpdate] HTTP error: %s\n", http.errorToString(code).c_str()); + _lastError = code; + http.end(); + return HTTP_UPDATE_FAILED; + } + + + DEBUG_HTTP_UPDATE("[httpUpdate] Header read fin.\n"); + DEBUG_HTTP_UPDATE("[httpUpdate] Server header:\n"); + DEBUG_HTTP_UPDATE("[httpUpdate] - code: %d\n", code); + DEBUG_HTTP_UPDATE("[httpUpdate] - len: %d\n", len); + + if(http.hasHeader("x-MD5")) { + DEBUG_HTTP_UPDATE("[httpUpdate] - MD5: %s\n", http.header("x-MD5").c_str()); + } + + DEBUG_HTTP_UPDATE("[httpUpdate] ESP8266 info:\n"); + DEBUG_HTTP_UPDATE("[httpUpdate] - free Space: %d\n", ESP.getFreeSketchSpace()); + DEBUG_HTTP_UPDATE("[httpUpdate] - current Sketch Size: %d\n", ESP.getSketchSize()); + + if(currentVersion && currentVersion[0] != 0x00) { + DEBUG_HTTP_UPDATE("[httpUpdate] - current version: %s\n", currentVersion); + } + + switch(code) { + case HTTP_CODE_OK: ///< OK (Start Update) + if(len > 0) { + bool startUpdate = true; + if(spiffs) { + size_t spiffsSize = ((size_t) &_SPIFFS_end - (size_t) &_SPIFFS_start); + if(len > (int) spiffsSize) { + DEBUG_HTTP_UPDATE("[httpUpdate] spiffsSize to low (%d) needed: %d\n", spiffsSize, len); + startUpdate = false; + } + } else { + if(len > (int) ESP.getFreeSketchSpace()) { + DEBUG_HTTP_UPDATE("[httpUpdate] FreeSketchSpace to low (%d) needed: %d\n", ESP.getFreeSketchSpace(), len); + startUpdate = false; + } + } + + if(!startUpdate) { + _lastError = HTTP_UE_TOO_LESS_SPACE; + ret = HTTP_UPDATE_FAILED; + } else { + + WiFiClient * tcp = http.getStreamPtr(); + + WiFiUDP::stopAll(); + WiFiClient::stopAllExcept(tcp); + + delay(100); + + int command; + + if(spiffs) { + command = U_SPIFFS; + DEBUG_HTTP_UPDATE("[httpUpdate] runUpdate spiffs...\n"); + } else { + command = U_FLASH; + DEBUG_HTTP_UPDATE("[httpUpdate] runUpdate flash...\n"); + } + + if(!spiffs) { + uint8_t buf[4]; + if(tcp->peekBytes(&buf[0], 4) != 4) { + DEBUG_HTTP_UPDATE("[httpUpdate] peekBytes magic header failed\n"); + _lastError = HTTP_UE_BIN_VERIFY_HEADER_FAILED; + http.end(); + return HTTP_UPDATE_FAILED; + } + + // check for valid first magic byte + if(buf[0] != 0xE9) { + DEBUG_HTTP_UPDATE("[httpUpdate] magic header not starts with 0xE9\n"); + _lastError = HTTP_UE_BIN_VERIFY_HEADER_FAILED; + http.end(); + return HTTP_UPDATE_FAILED; + + } + + uint32_t bin_flash_size = ESP.magicFlashChipSize((buf[3] & 0xf0) >> 4); + + // check if new bin fits to SPI flash + if(bin_flash_size > ESP.getFlashChipRealSize()) { + DEBUG_HTTP_UPDATE("[httpUpdate] magic header, new bin not fits SPI Flash\n"); + _lastError = HTTP_UE_BIN_FOR_WRONG_FLASH; + http.end(); + return HTTP_UPDATE_FAILED; + } + } + + if(runUpdate(*tcp, len, http.header("x-MD5"), command)) { + ret = HTTP_UPDATE_OK; + DEBUG_HTTP_UPDATE("[httpUpdate] Update ok\n"); + http.end(); + + if(_rebootOnUpdate) { + ESP.restart(); + } + + } else { + ret = HTTP_UPDATE_FAILED; + DEBUG_HTTP_UPDATE("[httpUpdate] Update failed\n"); + } + } + } else { + _lastError = HTTP_UE_SERVER_NOT_REPORT_SIZE; + ret = HTTP_UPDATE_FAILED; + DEBUG_HTTP_UPDATE("[httpUpdate] Content-Length is 0 or not set by Server?!\n"); + } + break; + case HTTP_CODE_NOT_MODIFIED: + ///< Not Modified (No updates) + ret = HTTP_UPDATE_NO_UPDATES; + break; + case HTTP_CODE_NOT_FOUND: + _lastError = HTTP_UE_SERVER_FILE_NOT_FOUND; + ret = HTTP_UPDATE_FAILED; + break; + case HTTP_CODE_FORBIDDEN: + _lastError = HTTP_UE_SERVER_FORBIDDEN; + ret = HTTP_UPDATE_FAILED; + break; + default: + _lastError = HTTP_UE_SERVER_WRONG_HTTP_CODE; + ret = HTTP_UPDATE_FAILED; + DEBUG_HTTP_UPDATE("[httpUpdate] HTTP Code is (%d)\n", code); + //http.writeToStream(&Serial1); + break; + } + + http.end(); + return ret; +} + +/** + * write Update to flash + * @param in Stream& + * @param size uint32_t + * @param md5 String + * @return true if Update ok + */ +bool ESP8266HTTPUpdate::runUpdate(Stream& in, uint32_t size, String md5, int command) { + + StreamString error; + + if(!Update.begin(size, command)) { + _lastError = Update.getError(); + Update.printError(error); + error.trim(); // remove line ending + DEBUG_HTTP_UPDATE("[httpUpdate] Update.begin failed! (%s)\n", error.c_str()); + return false; + } + + if(md5.length()) { + if(!Update.setMD5(md5.c_str())) { + _lastError = HTTP_UE_SERVER_FAULTY_MD5; + DEBUG_HTTP_UPDATE("[httpUpdate] Update.setMD5 failed! (%s)\n", md5.c_str()); + return false; + } + } + + if(Update.writeStream(in) != size) { + _lastError = Update.getError(); + Update.printError(error); + error.trim(); // remove line ending + DEBUG_HTTP_UPDATE("[httpUpdate] Update.writeStream failed! (%s)\n", error.c_str()); + return false; + } + + if(!Update.end()) { + _lastError = Update.getError(); + Update.printError(error); + error.trim(); // remove line ending + DEBUG_HTTP_UPDATE("[httpUpdate] Update.end failed! (%s)\n", error.c_str()); + return false; + } + + return true; +} + + + +ESP8266HTTPUpdate ESPhttpUpdate; diff --git a/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.h b/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.h index 81f03385f..765cc3569 100644 --- a/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.h +++ b/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.h @@ -1,84 +1,106 @@ -/** - * - * @file ESP8266HTTPUpdate.h - * @date 21.06.2015 - * @author Markus Sattler - * - * Copyright (c) 2015 Markus Sattler. All rights reserved. - * This file is part of the ESP8266 Http Updater. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifndef ESP8266HTTPUPDATE_H_ -#define ESP8266HTTPUPDATE_H_ - -#include -#include -#include -#include -#include - -#ifdef DEBUG_ESP_HTTP_UPDATE -#ifdef DEBUG_ESP_PORT -#define DEBUG_HTTP_UPDATE(...) DEBUG_ESP_PORT.printf( __VA_ARGS__ ) -#endif -#endif - -#ifndef DEBUG_HTTP_UPDATE -#define DEBUG_HTTP_UPDATE(...) -#endif - -/// note we use HTTP client errors too so we start at 100 -#define HTTP_UE_TOO_LESS_SPACE (-100) -#define HTTP_UE_SERVER_NOT_REPORT_SIZE (-101) -#define HTTP_UE_SERVER_FILE_NOT_FOUND (-102) -#define HTTP_UE_SERVER_FORBIDDEN (-103) -#define HTTP_UE_SERVER_WRONG_HTTP_CODE (-104) -#define HTTP_UE_SERVER_FAULTY_MD5 (-105) -#define HTTP_UE_BIN_VERIFY_HEADER_FAILED (-106) -#define HTTP_UE_BIN_FOR_WRONG_FLASH (-107) - -typedef enum { - HTTP_UPDATE_FAILED, - HTTP_UPDATE_NO_UPDATES, - HTTP_UPDATE_OK -} t_httpUpdate_return; - -class ESP8266HTTPUpdate { - public: - ESP8266HTTPUpdate(void); - ~ESP8266HTTPUpdate(void); - - t_httpUpdate_return update(const char * url, const char * current_version = "", const char * httpsFingerprint = "", bool reboot = true); - t_httpUpdate_return update(const char * host, uint16_t port, const char * url = "/", const char * current_version = "", bool https = false, const char * httpsFingerprint = "", bool reboot = true); - t_httpUpdate_return update(String host, uint16_t port, String url = "/", String current_version = "", bool https = false, String httpsFingerprint = "", bool reboot = true); - - t_httpUpdate_return updateSpiffs(const char * url, const char * current_version = "", const char * httpsFingerprint = "", bool reboot = false); - - int getLastError(void); - String getLastErrorString(void); - - protected: - t_httpUpdate_return handleUpdate(HTTPClient * http, const char * current_version, bool reboot = true, bool spiffs = false); - bool runUpdate(Stream& in, uint32_t size, String md5, int command = U_FLASH); - - int lastError; -}; - -extern ESP8266HTTPUpdate ESPhttpUpdate; - -#endif /* ESP8266HTTPUPDATE_H_ */ +/** + * + * @file ESP8266HTTPUpdate.h + * @date 21.06.2015 + * @author Markus Sattler + * + * Copyright (c) 2015 Markus Sattler. All rights reserved. + * This file is part of the ESP8266 Http Updater. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef ESP8266HTTPUPDATE_H_ +#define ESP8266HTTPUPDATE_H_ + +#include +#include +#include +#include +#include + +#ifdef DEBUG_ESP_HTTP_UPDATE +#ifdef DEBUG_ESP_PORT +#define DEBUG_HTTP_UPDATE(...) DEBUG_ESP_PORT.printf( __VA_ARGS__ ) +#endif +#endif + +#ifndef DEBUG_HTTP_UPDATE +#define DEBUG_HTTP_UPDATE(...) +#endif + +/// note we use HTTP client errors too so we start at 100 +#define HTTP_UE_TOO_LESS_SPACE (-100) +#define HTTP_UE_SERVER_NOT_REPORT_SIZE (-101) +#define HTTP_UE_SERVER_FILE_NOT_FOUND (-102) +#define HTTP_UE_SERVER_FORBIDDEN (-103) +#define HTTP_UE_SERVER_WRONG_HTTP_CODE (-104) +#define HTTP_UE_SERVER_FAULTY_MD5 (-105) +#define HTTP_UE_BIN_VERIFY_HEADER_FAILED (-106) +#define HTTP_UE_BIN_FOR_WRONG_FLASH (-107) + +enum HTTPUpdateResult { + HTTP_UPDATE_FAILED, + HTTP_UPDATE_NO_UPDATES, + HTTP_UPDATE_OK +}; + +typedef HTTPUpdateResult t_httpUpdate_return; // backward compatibility + +class ESP8266HTTPUpdate { + public: + ESP8266HTTPUpdate(void); + ~ESP8266HTTPUpdate(void); + + void rebootOnUpdate(bool reboot) { _rebootOnUpdate = reboot; } + + // This function is deprecated, use rebootOnUpdate and the next one instead + t_httpUpdate_return update(const String& url, const String& currentVersion, + const String& httpsFingerprint, bool reboot) __attribute__((deprecated)); + t_httpUpdate_return update(const String& url, const String& currentVersion = ""); + t_httpUpdate_return update(const String& url, const String& currentVersion, + const String& httpsFingerprint); + + // This function is deprecated, use one of the overloads below along with rebootOnUpdate + t_httpUpdate_return update(const String& host, uint16_t port, const String& uri, const String& currentVersion, + bool https, const String& httpsFingerprint, bool reboot) __attribute__((deprecated)); + + t_httpUpdate_return update(const String& host, uint16_t port, const String& uri = "/", + const String& currentVersion = ""); + t_httpUpdate_return update(const String& host, uint16_t port, const String& url, + const String& currentVersion, const String& httpsFingerprint); + + // This function is deprecated, use rebootOnUpdate and the next one instead + t_httpUpdate_return updateSpiffs(const String& url, const String& currentVersion, + const String& httpsFingerprint, bool reboot) __attribute__((deprecated)); + t_httpUpdate_return updateSpiffs(const String& url, const String& currentVersion = ""); + t_httpUpdate_return updateSpiffs(const String& url, const String& currentVersion, const String& httpsFingerprint); + + + int getLastError(void); + String getLastErrorString(void); + + protected: + t_httpUpdate_return handleUpdate(HTTPClient& http, const String& currentVersion, bool spiffs = false); + bool runUpdate(Stream& in, uint32_t size, String md5, int command = U_FLASH); + + int _lastError; + bool _rebootOnUpdate = true; +}; + +extern ESP8266HTTPUpdate ESPhttpUpdate; + +#endif /* ESP8266HTTPUPDATE_H_ */ From bbc5e9ba01009bb42a5463dff87f4b08e10b2c9f Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 5 Apr 2016 01:57:09 +0300 Subject: [PATCH 12/46] Update library versions --- libraries/ESP8266HTTPClient/library.properties | 2 +- libraries/ESP8266httpUpdate/library.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/ESP8266HTTPClient/library.properties b/libraries/ESP8266HTTPClient/library.properties index ab96e9a9e..871617105 100644 --- a/libraries/ESP8266HTTPClient/library.properties +++ b/libraries/ESP8266HTTPClient/library.properties @@ -1,5 +1,5 @@ name=ESP8266HTTPClient -version=1.0 +version=1.1 author=Markus Sattler maintainer=Markus Sattler sentence=http Client for ESP8266 diff --git a/libraries/ESP8266httpUpdate/library.properties b/libraries/ESP8266httpUpdate/library.properties index ca5f75f6e..2116c04a0 100644 --- a/libraries/ESP8266httpUpdate/library.properties +++ b/libraries/ESP8266httpUpdate/library.properties @@ -1,5 +1,5 @@ name=ESP8266httpUpdate -version=1.0 +version=1.1 author=Markus Sattler maintainer=Markus Sattler sentence=Http Update for ESP8266 From bf7f33d91847ef301b46e4842a253090bc59a62b Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 5 Apr 2016 02:04:42 +0300 Subject: [PATCH 13/46] Fix code formatting --- .../src/ESP8266HTTPClient.cpp | 180 ++++++----- .../ESP8266HTTPClient/src/ESP8266HTTPClient.h | 141 ++++----- .../src/ESP8266httpUpdate.cpp | 288 +++++++++--------- .../ESP8266httpUpdate/src/ESP8266httpUpdate.h | 64 ++-- 4 files changed, 361 insertions(+), 312 deletions(-) diff --git a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp index 1829e9812..033fd3e84 100644 --- a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp +++ b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp @@ -30,7 +30,8 @@ #include "ESP8266HTTPClient.h" -class TransportTraits { +class TransportTraits +{ public: virtual std::unique_ptr create() { @@ -43,7 +44,8 @@ public: } }; -class TLSTraits : public TransportTraits { +class TLSTraits : public TransportTraits +{ public: TLSTraits(const String& fingerprint) : _fingerprint(fingerprint) @@ -86,7 +88,8 @@ HTTPClient::~HTTPClient() } -bool HTTPClient::begin(String url, String httpsFingerprint) { +bool HTTPClient::begin(String url, String httpsFingerprint) +{ if (httpsFingerprint.length() == 0) { return false; } @@ -183,8 +186,7 @@ bool HTTPClient::begin(String host, uint16_t port, String uri, bool https, Strin { if (https) { return begin(host, port, uri, httpsFingerprint); - } - else { + } else { return begin(host, port, uri); } } @@ -208,7 +210,8 @@ bool HTTPClient::begin(String host, uint16_t port, String uri, String httpsFinge * end * called after the payload is handled */ -void HTTPClient::end(void) { +void HTTPClient::end(void) +{ if(connected()) { if(_tcp->available() > 0) { DEBUG_HTTPCLIENT("[HTTP-Client][end] still data in buffer (%d), clean up.\n", _tcp->available()); @@ -231,7 +234,8 @@ void HTTPClient::end(void) { * connected * @return connected status */ -bool HTTPClient::connected() { +bool HTTPClient::connected() +{ if(_tcp) { return (_tcp->connected() || (_tcp->available() > 0)); } @@ -243,7 +247,8 @@ bool HTTPClient::connected() { * keep-alive * @param reuse bool */ -void HTTPClient::setReuse(bool reuse) { +void HTTPClient::setReuse(bool reuse) +{ _reuse = reuse; } @@ -251,7 +256,8 @@ void HTTPClient::setReuse(bool reuse) { * set User Agent * @param userAgent const char * */ -void HTTPClient::setUserAgent(const char * userAgent) { +void HTTPClient::setUserAgent(const char * userAgent) +{ _userAgent = userAgent; } @@ -260,7 +266,8 @@ void HTTPClient::setUserAgent(const char * userAgent) { * @param user const char * * @param password const char * */ -void HTTPClient::setAuthorization(const char * user, const char * password) { +void HTTPClient::setAuthorization(const char * user, const char * password) +{ if(user && password) { String auth = user; auth += ":"; @@ -273,7 +280,8 @@ void HTTPClient::setAuthorization(const char * user, const char * password) { * set the Authorizatio for the http request * @param auth const char * base64 */ -void HTTPClient::setAuthorization(const char * auth) { +void HTTPClient::setAuthorization(const char * auth) +{ if(auth) { _base64Authorization = auth; } @@ -283,7 +291,8 @@ void HTTPClient::setAuthorization(const char * auth) { * set the timeout for the TCP connection * @param timeout unsigned int */ -void HTTPClient::setTimeout(uint16_t timeout) { +void HTTPClient::setTimeout(uint16_t timeout) +{ _tcpTimeout = timeout; if(connected()) { _tcp->setTimeout(timeout); @@ -294,7 +303,8 @@ void HTTPClient::setTimeout(uint16_t timeout) { * use HTTP1.0 * @param timeout */ -void HTTPClient::useHTTP10(bool useHTTP10) { +void HTTPClient::useHTTP10(bool useHTTP10) +{ _useHTTP10 = useHTTP10; } @@ -302,7 +312,8 @@ void HTTPClient::useHTTP10(bool useHTTP10) { * send a GET request * @return http code */ -int HTTPClient::GET() { +int HTTPClient::GET() +{ return sendRequest("GET"); } @@ -312,11 +323,13 @@ int HTTPClient::GET() { * @param size size_t * @return http code */ -int HTTPClient::POST(uint8_t * payload, size_t size) { +int HTTPClient::POST(uint8_t * payload, size_t size) +{ return sendRequest("POST", payload, size); } -int HTTPClient::POST(String payload) { +int HTTPClient::POST(String payload) +{ return POST((uint8_t *) payload.c_str(), payload.length()); } @@ -326,7 +339,8 @@ int HTTPClient::POST(String payload) { * @param payload String data for the message body * @return */ -int HTTPClient::sendRequest(const char * type, String payload) { +int HTTPClient::sendRequest(const char * type, String payload) +{ return sendRequest(type, (uint8_t *) payload.c_str(), payload.length()); } @@ -337,7 +351,8 @@ int HTTPClient::sendRequest(const char * type, String payload) { * @param size size_t size for the message body if 0 not send * @return -1 if no info or > 0 when Content-Length is set by server */ -int HTTPClient::sendRequest(const char * type, uint8_t * payload, size_t size) { +int HTTPClient::sendRequest(const char * type, uint8_t * payload, size_t size) +{ // connect to server if(!connect()) { return returnError(HTTPC_ERROR_CONNECTION_REFUSED); @@ -370,7 +385,8 @@ int HTTPClient::sendRequest(const char * type, uint8_t * payload, size_t size) { * @param size size_t size for the message body if 0 not Content-Length is send * @return -1 if no info or > 0 when Content-Length is set by server */ -int HTTPClient::sendRequest(const char * type, Stream * stream, size_t size) { +int HTTPClient::sendRequest(const char * type, Stream * stream, size_t size) +{ if(!stream) { return returnError(HTTPC_ERROR_NO_STREAM); @@ -485,7 +501,8 @@ int HTTPClient::sendRequest(const char * type, Stream * stream, size_t size) { free(buff); if(size && (int) size != bytesWritten) { - DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] Stream payload bytesWritten %d and size %d mismatch!.\n", bytesWritten, size); DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] ERROR SEND PAYLOAD FAILED!"); + DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] Stream payload bytesWritten %d and size %d mismatch!.\n", bytesWritten, size); + DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] ERROR SEND PAYLOAD FAILED!"); return returnError(HTTPC_ERROR_SEND_PAYLOAD_FAILED); } else { DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] Stream payload written: %d\n", bytesWritten); @@ -504,7 +521,8 @@ int HTTPClient::sendRequest(const char * type, Stream * stream, size_t size) { * size of message body / payload * @return -1 if no info or > 0 when Content-Length is set by server */ -int HTTPClient::getSize(void) { +int HTTPClient::getSize(void) +{ return _size; } @@ -512,7 +530,8 @@ int HTTPClient::getSize(void) { * returns the stream of the tcp connection * @return WiFiClient */ -WiFiClient& HTTPClient::getStream(void) { +WiFiClient& HTTPClient::getStream(void) +{ if(connected()) { return *_tcp; } @@ -526,7 +545,8 @@ WiFiClient& HTTPClient::getStream(void) { * returns the stream of the tcp connection * @return WiFiClient * */ -WiFiClient* HTTPClient::getStreamPtr(void) { +WiFiClient* HTTPClient::getStreamPtr(void) +{ if(connected()) { return _tcp.get(); } @@ -540,7 +560,8 @@ WiFiClient* HTTPClient::getStreamPtr(void) { * @param stream Stream * * @return bytes written ( negative values are error codes ) */ -int HTTPClient::writeToStream(Stream * stream) { +int HTTPClient::writeToStream(Stream * stream) +{ if(!stream) { return returnError(HTTPC_ERROR_NO_STREAM); @@ -616,7 +637,8 @@ int HTTPClient::writeToStream(Stream * stream) { * return all payload as String (may need lot of ram or trigger out of memory!) * @return String */ -String HTTPClient::getString(void) { +String HTTPClient::getString(void) +{ StreamString sstring; if(_size) { @@ -636,32 +658,33 @@ String HTTPClient::getString(void) { * @param error int * @return String */ -String HTTPClient::errorToString(int error) { +String HTTPClient::errorToString(int error) +{ switch(error) { - case HTTPC_ERROR_CONNECTION_REFUSED: - return String("connection refused"); - case HTTPC_ERROR_SEND_HEADER_FAILED: - return String("send header failed"); - case HTTPC_ERROR_SEND_PAYLOAD_FAILED: - return String("send payload failed"); - case HTTPC_ERROR_NOT_CONNECTED: - return String("not connected"); - case HTTPC_ERROR_CONNECTION_LOST: - return String("connection lost"); - case HTTPC_ERROR_NO_STREAM: - return String("no stream"); - case HTTPC_ERROR_NO_HTTP_SERVER: - return String("no HTTP server"); - case HTTPC_ERROR_TOO_LESS_RAM: - return String("too less ram"); - case HTTPC_ERROR_ENCODING: - return String("Transfer-Encoding not supported"); - case HTTPC_ERROR_STREAM_WRITE: - return String("Stream write error"); - case HTTPC_ERROR_READ_TIMEOUT: - return String("read Timeout"); - default: - return String(); + case HTTPC_ERROR_CONNECTION_REFUSED: + return String("connection refused"); + case HTTPC_ERROR_SEND_HEADER_FAILED: + return String("send header failed"); + case HTTPC_ERROR_SEND_PAYLOAD_FAILED: + return String("send payload failed"); + case HTTPC_ERROR_NOT_CONNECTED: + return String("not connected"); + case HTTPC_ERROR_CONNECTION_LOST: + return String("connection lost"); + case HTTPC_ERROR_NO_STREAM: + return String("no stream"); + case HTTPC_ERROR_NO_HTTP_SERVER: + return String("no HTTP server"); + case HTTPC_ERROR_TOO_LESS_RAM: + return String("too less ram"); + case HTTPC_ERROR_ENCODING: + return String("Transfer-Encoding not supported"); + case HTTPC_ERROR_STREAM_WRITE: + return String("Stream write error"); + case HTTPC_ERROR_READ_TIMEOUT: + return String("read Timeout"); + default: + return String(); } } @@ -671,7 +694,8 @@ String HTTPClient::errorToString(int error) { * @param value * @param first */ -void HTTPClient::addHeader(const String& name, const String& value, bool first) { +void HTTPClient::addHeader(const String& name, const String& value, bool first) +{ // not allow set of Header handled by code if(!name.equalsIgnoreCase("Connection") && !name.equalsIgnoreCase("User-Agent") && !name.equalsIgnoreCase("Host") && !(_base64Authorization.length() && name.equalsIgnoreCase("Authorization"))) { @@ -689,44 +713,55 @@ void HTTPClient::addHeader(const String& name, const String& value, bool first) } -void HTTPClient::collectHeaders(const char* headerKeys[], const size_t headerKeysCount) { +void HTTPClient::collectHeaders(const char* headerKeys[], const size_t headerKeysCount) +{ _headerKeysCount = headerKeysCount; - if(_currentHeaders) + if(_currentHeaders) { delete[] _currentHeaders; + } _currentHeaders = new RequestArgument[_headerKeysCount]; for(size_t i = 0; i < _headerKeysCount; i++) { _currentHeaders[i].key = headerKeys[i]; } } -String HTTPClient::header(const char* name) { +String HTTPClient::header(const char* name) +{ for(size_t i = 0; i < _headerKeysCount; ++i) { - if(_currentHeaders[i].key == name) + if(_currentHeaders[i].key == name) { return _currentHeaders[i].value; + } } return String(); } -String HTTPClient::header(size_t i) { - if(i < _headerKeysCount) +String HTTPClient::header(size_t i) +{ + if(i < _headerKeysCount) { return _currentHeaders[i].value; + } return String(); } -String HTTPClient::headerName(size_t i) { - if(i < _headerKeysCount) +String HTTPClient::headerName(size_t i) +{ + if(i < _headerKeysCount) { return _currentHeaders[i].key; + } return String(); } -int HTTPClient::headers() { +int HTTPClient::headers() +{ return _headerKeysCount; } -bool HTTPClient::hasHeader(const char* name) { +bool HTTPClient::hasHeader(const char* name) +{ for(size_t i = 0; i < _headerKeysCount; ++i) { - if((_currentHeaders[i].key == name) && (_currentHeaders[i].value.length() > 0)) + if((_currentHeaders[i].key == name) && (_currentHeaders[i].value.length() > 0)) { return true; + } } return false; } @@ -735,7 +770,8 @@ bool HTTPClient::hasHeader(const char* name) { * init TCP connection and handle ssl verify if needed * @return true if connection is ok */ -bool HTTPClient::connect(void) { +bool HTTPClient::connect(void) +{ if(connected()) { DEBUG_HTTPCLIENT("[HTTP-Client] connect. already connected, try reuse!\n"); @@ -779,7 +815,8 @@ bool HTTPClient::connect(void) { * @param type (GET, POST, ...) * @return status */ -bool HTTPClient::sendHeader(const char * type) { +bool HTTPClient::sendHeader(const char * type) +{ if(!connected()) { return false; } @@ -793,9 +830,9 @@ bool HTTPClient::sendHeader(const char * type) { } header += "\r\n" - "Host: " + _host + "\r\n" - "User-Agent: " + _userAgent + "\r\n" - "Connection: "; + "Host: " + _host + "\r\n" + "User-Agent: " + _userAgent + "\r\n" + "Connection: "; if(_reuse) { header += "keep-alive"; @@ -821,7 +858,8 @@ bool HTTPClient::sendHeader(const char * type) { * reads the response from the server * @return int http code */ -int HTTPClient::handleHeaderResponse() { +int HTTPClient::handleHeaderResponse() +{ if(!connected()) { return HTTPC_ERROR_NOT_CONNECTED; @@ -912,7 +950,8 @@ int HTTPClient::handleHeaderResponse() { * @param size int * @return < 0 = error >= 0 = size written */ -int HTTPClient::writeToStreamDataBlock(Stream * stream, int size) { +int HTTPClient::writeToStreamDataBlock(Stream * stream, int size) +{ int buff_size = HTTP_TCP_BUFFER_SIZE; int len = size; int bytesWritten = 0; @@ -1022,7 +1061,8 @@ int HTTPClient::writeToStreamDataBlock(Stream * stream, int size) { * @param error * @return error */ -int HTTPClient::returnError(int error) { +int HTTPClient::returnError(int error) +{ if(error < 0) { DEBUG_HTTPCLIENT("[HTTP-Client][returnError] error(%d): %s\n", error, errorToString(error).c_str()); if(connected()) { diff --git a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h index 27db371a5..d8c926359 100644 --- a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h +++ b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h @@ -127,96 +127,97 @@ typedef enum { class TransportTraits; typedef std::unique_ptr TransportTraitsPtr; -class HTTPClient { - public: - HTTPClient(); - ~HTTPClient(); +class HTTPClient +{ +public: + HTTPClient(); + ~HTTPClient(); - bool begin(String url); - bool begin(String url, String httpsFingerprint); - bool begin(String host, uint16_t port, String uri = "/"); - bool begin(String host, uint16_t port, String uri, String httpsFingerprint); - // deprecated, use the overload above instead - bool begin(String host, uint16_t port, String uri, bool https, String httpsFingerprint) __attribute__ ((deprecated)); + bool begin(String url); + bool begin(String url, String httpsFingerprint); + bool begin(String host, uint16_t port, String uri = "/"); + bool begin(String host, uint16_t port, String uri, String httpsFingerprint); + // deprecated, use the overload above instead + bool begin(String host, uint16_t port, String uri, bool https, String httpsFingerprint) __attribute__ ((deprecated)); - void end(void); + void end(void); - bool connected(void); + bool connected(void); - void setReuse(bool reuse); /// keep-alive - void setUserAgent(const char * userAgent); - void setAuthorization(const char * user, const char * password); - void setAuthorization(const char * auth); - void setTimeout(uint16_t timeout); + void setReuse(bool reuse); /// keep-alive + void setUserAgent(const char * userAgent); + void setAuthorization(const char * user, const char * password); + void setAuthorization(const char * auth); + void setTimeout(uint16_t timeout); - void useHTTP10(bool usehttp10 = true); + void useHTTP10(bool usehttp10 = true); - /// request handling - int GET(); - int POST(uint8_t * payload, size_t size); - int POST(String payload); - int sendRequest(const char * type, String payload); - int sendRequest(const char * type, uint8_t * payload = NULL, size_t size = 0); - int sendRequest(const char * type, Stream * stream, size_t size = 0); + /// request handling + int GET(); + int POST(uint8_t * payload, size_t size); + int POST(String payload); + int sendRequest(const char * type, String payload); + int sendRequest(const char * type, uint8_t * payload = NULL, size_t size = 0); + int sendRequest(const char * type, Stream * stream, size_t size = 0); - void addHeader(const String& name, const String& value, bool first = false); + void addHeader(const String& name, const String& value, bool first = false); - /// Response handling - void collectHeaders(const char* headerKeys[], const size_t headerKeysCount); - String header(const char* name); // get request header value by name - String header(size_t i); // get request header value by number - String headerName(size_t i); // get request header name by number - int headers(); // get header count - bool hasHeader(const char* name); // check if header exists + /// Response handling + void collectHeaders(const char* headerKeys[], const size_t headerKeysCount); + String header(const char* name); // get request header value by name + String header(size_t i); // get request header value by number + String headerName(size_t i); // get request header name by number + int headers(); // get header count + bool hasHeader(const char* name); // check if header exists - int getSize(void); + int getSize(void); - WiFiClient& getStream(void); - WiFiClient* getStreamPtr(void); - int writeToStream(Stream* stream); - String getString(void); + WiFiClient& getStream(void); + WiFiClient* getStreamPtr(void); + int writeToStream(Stream* stream); + String getString(void); - static String errorToString(int error); + static String errorToString(int error); - protected: - struct RequestArgument { - String key; - String value; - }; +protected: + struct RequestArgument { + String key; + String value; + }; - void clear(); - int returnError(int error); - bool connect(void); - bool sendHeader(const char * type); - int handleHeaderResponse(); - int writeToStreamDataBlock(Stream * stream, int len); + void clear(); + int returnError(int error); + bool connect(void); + bool sendHeader(const char * type); + int handleHeaderResponse(); + int writeToStreamDataBlock(Stream * stream, int len); - TransportTraitsPtr _transportTraits; - std::unique_ptr _tcp; + TransportTraitsPtr _transportTraits; + std::unique_ptr _tcp; - /// request handling - String _host; - uint16_t _port = 0; - bool _reuse = false; - uint16_t _tcpTimeout = HTTPCLIENT_DEFAULT_TCP_TIMEOUT; - bool _useHTTP10 = false; + /// request handling + String _host; + uint16_t _port = 0; + bool _reuse = false; + uint16_t _tcpTimeout = HTTPCLIENT_DEFAULT_TCP_TIMEOUT; + bool _useHTTP10 = false; - String _uri; - String _protocol; - String _headers; - String _userAgent = "ESP8266HTTPClient"; - String _base64Authorization; + String _uri; + String _protocol; + String _headers; + String _userAgent = "ESP8266HTTPClient"; + String _base64Authorization; - /// Response handling - RequestArgument* _currentHeaders = nullptr; - size_t _headerKeysCount = 0; + /// Response handling + RequestArgument* _currentHeaders = nullptr; + size_t _headerKeysCount = 0; - int _returnCode = 0; - int _size = -1; - bool _canReuse = false; - transferEncoding_t _transferEncoding = HTTPC_TE_IDENTITY; + int _returnCode = 0; + int _size = -1; + bool _canReuse = false; + transferEncoding_t _transferEncoding = HTTPC_TE_IDENTITY; }; diff --git a/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp b/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp index ee4365385..910d8f901 100644 --- a/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp +++ b/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp @@ -29,70 +29,70 @@ extern "C" uint32_t _SPIFFS_start; extern "C" uint32_t _SPIFFS_end; -ESP8266HTTPUpdate::ESP8266HTTPUpdate(void) { +ESP8266HTTPUpdate::ESP8266HTTPUpdate(void) +{ } -ESP8266HTTPUpdate::~ESP8266HTTPUpdate(void) { +ESP8266HTTPUpdate::~ESP8266HTTPUpdate(void) +{ } -t_httpUpdate_return ESP8266HTTPUpdate::update(const String& url, const String& currentVersion, - const String& httpsFingerprint, bool reboot) +HTTPUpdateResult ESP8266HTTPUpdate::update(const String& url, const String& currentVersion, + const String& httpsFingerprint, bool reboot) { rebootOnUpdate(reboot); return update(url, currentVersion, httpsFingerprint); } -t_httpUpdate_return ESP8266HTTPUpdate::update(const String& url, const String& currentVersion) +HTTPUpdateResult ESP8266HTTPUpdate::update(const String& url, const String& currentVersion) { HTTPClient http; http.begin(url); return handleUpdate(http, currentVersion, false); } -t_httpUpdate_return ESP8266HTTPUpdate::update(const String& url, const String& currentVersion, - const String& httpsFingerprint) +HTTPUpdateResult ESP8266HTTPUpdate::update(const String& url, const String& currentVersion, + const String& httpsFingerprint) { HTTPClient http; http.begin(url, httpsFingerprint); return handleUpdate(http, currentVersion, false); } - -t_httpUpdate_return ESP8266HTTPUpdate::updateSpiffs(const String& url, const String& currentVersion, const String& httpsFingerprint) +HTTPUpdateResult ESP8266HTTPUpdate::updateSpiffs(const String& url, const String& currentVersion, const String& httpsFingerprint) { HTTPClient http; http.begin(url, httpsFingerprint); return handleUpdate(http, currentVersion, true); } -t_httpUpdate_return ESP8266HTTPUpdate::updateSpiffs(const String& url, const String& currentVersion) +HTTPUpdateResult ESP8266HTTPUpdate::updateSpiffs(const String& url, const String& currentVersion) { HTTPClient http; http.begin(url); return handleUpdate(http, currentVersion, true); } -t_httpUpdate_return ESP8266HTTPUpdate::update(const String& host, uint16_t port, const String& uri, const String& currentVersion, - bool https, const String& httpsFingerprint, bool reboot) +HTTPUpdateResult ESP8266HTTPUpdate::update(const String& host, uint16_t port, const String& uri, const String& currentVersion, + bool https, const String& httpsFingerprint, bool reboot) { rebootOnUpdate(reboot); if (httpsFingerprint.length() == 0) { return update(host, port, uri, currentVersion); - } - else { + } else { return update(host, port, uri, currentVersion, httpsFingerprint); } } -t_httpUpdate_return ESP8266HTTPUpdate::update(const String& host, uint16_t port, const String& uri, - const String& currentVersion) +HTTPUpdateResult ESP8266HTTPUpdate::update(const String& host, uint16_t port, const String& uri, + const String& currentVersion) { HTTPClient http; http.begin(host, port, uri); return handleUpdate(http, currentVersion, false); } -t_httpUpdate_return ESP8266HTTPUpdate::update(const String& host, uint16_t port, const String& url, - const String& currentVersion, const String& httpsFingerprint) +HTTPUpdateResult ESP8266HTTPUpdate::update(const String& host, uint16_t port, const String& url, + const String& currentVersion, const String& httpsFingerprint) { HTTPClient http; http.begin(host, port, url, httpsFingerprint); @@ -104,15 +104,17 @@ t_httpUpdate_return ESP8266HTTPUpdate::update(const String& host, uint16_t port, * return error code as int * @return int error code */ -int ESP8266HTTPUpdate::getLastError(void){ - return _lastError; +int ESP8266HTTPUpdate::getLastError(void) +{ + return _lastError; } /** * return error code as String * @return String error */ -String ESP8266HTTPUpdate::getLastErrorString(void) { +String ESP8266HTTPUpdate::getLastErrorString(void) +{ if(_lastError == 0) { return String(); // no error @@ -132,22 +134,22 @@ String ESP8266HTTPUpdate::getLastErrorString(void) { } switch(_lastError) { - case HTTP_UE_TOO_LESS_SPACE: - return String("To less space"); - case HTTP_UE_SERVER_NOT_REPORT_SIZE: - return String("Server not Report Size"); - case HTTP_UE_SERVER_FILE_NOT_FOUND: - return String("File not Found (404)"); - case HTTP_UE_SERVER_FORBIDDEN: - return String("Forbidden (403)"); - case HTTP_UE_SERVER_WRONG_HTTP_CODE: - return String("Wrong HTTP code"); - case HTTP_UE_SERVER_FAULTY_MD5: - return String("Faulty MD5"); - case HTTP_UE_BIN_VERIFY_HEADER_FAILED: - return String("Verify bin header failed"); - case HTTP_UE_BIN_FOR_WRONG_FLASH: - return String("bin for wrong flash size"); + case HTTP_UE_TOO_LESS_SPACE: + return String("To less space"); + case HTTP_UE_SERVER_NOT_REPORT_SIZE: + return String("Server not Report Size"); + case HTTP_UE_SERVER_FILE_NOT_FOUND: + return String("File not Found (404)"); + case HTTP_UE_SERVER_FORBIDDEN: + return String("Forbidden (403)"); + case HTTP_UE_SERVER_WRONG_HTTP_CODE: + return String("Wrong HTTP code"); + case HTTP_UE_SERVER_FAULTY_MD5: + return String("Faulty MD5"); + case HTTP_UE_BIN_VERIFY_HEADER_FAILED: + return String("Verify bin header failed"); + case HTTP_UE_BIN_FOR_WRONG_FLASH: + return String("bin for wrong flash size"); } return String(); @@ -158,11 +160,12 @@ String ESP8266HTTPUpdate::getLastErrorString(void) { * * @param http HTTPClient * * @param currentVersion const char * - * @return t_httpUpdate_return + * @return HTTPUpdateResult */ -t_httpUpdate_return ESP8266HTTPUpdate::handleUpdate(HTTPClient& http, const String& currentVersion, bool spiffs) { +HTTPUpdateResult ESP8266HTTPUpdate::handleUpdate(HTTPClient& http, const String& currentVersion, bool spiffs) +{ - t_httpUpdate_return ret = HTTP_UPDATE_FAILED; + HTTPUpdateResult ret = HTTP_UPDATE_FAILED; // use HTTP/1.0 for update since the update handler not support any transfer Encoding http.useHTTP10(true); @@ -221,111 +224,111 @@ t_httpUpdate_return ESP8266HTTPUpdate::handleUpdate(HTTPClient& http, const Stri } switch(code) { - case HTTP_CODE_OK: ///< OK (Start Update) - if(len > 0) { - bool startUpdate = true; - if(spiffs) { - size_t spiffsSize = ((size_t) &_SPIFFS_end - (size_t) &_SPIFFS_start); - if(len > (int) spiffsSize) { - DEBUG_HTTP_UPDATE("[httpUpdate] spiffsSize to low (%d) needed: %d\n", spiffsSize, len); - startUpdate = false; - } - } else { - if(len > (int) ESP.getFreeSketchSpace()) { - DEBUG_HTTP_UPDATE("[httpUpdate] FreeSketchSpace to low (%d) needed: %d\n", ESP.getFreeSketchSpace(), len); - startUpdate = false; - } - } - - if(!startUpdate) { - _lastError = HTTP_UE_TOO_LESS_SPACE; - ret = HTTP_UPDATE_FAILED; - } else { - - WiFiClient * tcp = http.getStreamPtr(); - - WiFiUDP::stopAll(); - WiFiClient::stopAllExcept(tcp); - - delay(100); - - int command; - - if(spiffs) { - command = U_SPIFFS; - DEBUG_HTTP_UPDATE("[httpUpdate] runUpdate spiffs...\n"); - } else { - command = U_FLASH; - DEBUG_HTTP_UPDATE("[httpUpdate] runUpdate flash...\n"); - } - - if(!spiffs) { - uint8_t buf[4]; - if(tcp->peekBytes(&buf[0], 4) != 4) { - DEBUG_HTTP_UPDATE("[httpUpdate] peekBytes magic header failed\n"); - _lastError = HTTP_UE_BIN_VERIFY_HEADER_FAILED; - http.end(); - return HTTP_UPDATE_FAILED; - } - - // check for valid first magic byte - if(buf[0] != 0xE9) { - DEBUG_HTTP_UPDATE("[httpUpdate] magic header not starts with 0xE9\n"); - _lastError = HTTP_UE_BIN_VERIFY_HEADER_FAILED; - http.end(); - return HTTP_UPDATE_FAILED; - - } - - uint32_t bin_flash_size = ESP.magicFlashChipSize((buf[3] & 0xf0) >> 4); - - // check if new bin fits to SPI flash - if(bin_flash_size > ESP.getFlashChipRealSize()) { - DEBUG_HTTP_UPDATE("[httpUpdate] magic header, new bin not fits SPI Flash\n"); - _lastError = HTTP_UE_BIN_FOR_WRONG_FLASH; - http.end(); - return HTTP_UPDATE_FAILED; - } - } - - if(runUpdate(*tcp, len, http.header("x-MD5"), command)) { - ret = HTTP_UPDATE_OK; - DEBUG_HTTP_UPDATE("[httpUpdate] Update ok\n"); - http.end(); - - if(_rebootOnUpdate) { - ESP.restart(); - } - - } else { - ret = HTTP_UPDATE_FAILED; - DEBUG_HTTP_UPDATE("[httpUpdate] Update failed\n"); - } + case HTTP_CODE_OK: ///< OK (Start Update) + if(len > 0) { + bool startUpdate = true; + if(spiffs) { + size_t spiffsSize = ((size_t) &_SPIFFS_end - (size_t) &_SPIFFS_start); + if(len > (int) spiffsSize) { + DEBUG_HTTP_UPDATE("[httpUpdate] spiffsSize to low (%d) needed: %d\n", spiffsSize, len); + startUpdate = false; } } else { - _lastError = HTTP_UE_SERVER_NOT_REPORT_SIZE; - ret = HTTP_UPDATE_FAILED; - DEBUG_HTTP_UPDATE("[httpUpdate] Content-Length is 0 or not set by Server?!\n"); + if(len > (int) ESP.getFreeSketchSpace()) { + DEBUG_HTTP_UPDATE("[httpUpdate] FreeSketchSpace to low (%d) needed: %d\n", ESP.getFreeSketchSpace(), len); + startUpdate = false; + } } - break; - case HTTP_CODE_NOT_MODIFIED: - ///< Not Modified (No updates) - ret = HTTP_UPDATE_NO_UPDATES; - break; - case HTTP_CODE_NOT_FOUND: - _lastError = HTTP_UE_SERVER_FILE_NOT_FOUND; + + if(!startUpdate) { + _lastError = HTTP_UE_TOO_LESS_SPACE; + ret = HTTP_UPDATE_FAILED; + } else { + + WiFiClient * tcp = http.getStreamPtr(); + + WiFiUDP::stopAll(); + WiFiClient::stopAllExcept(tcp); + + delay(100); + + int command; + + if(spiffs) { + command = U_SPIFFS; + DEBUG_HTTP_UPDATE("[httpUpdate] runUpdate spiffs...\n"); + } else { + command = U_FLASH; + DEBUG_HTTP_UPDATE("[httpUpdate] runUpdate flash...\n"); + } + + if(!spiffs) { + uint8_t buf[4]; + if(tcp->peekBytes(&buf[0], 4) != 4) { + DEBUG_HTTP_UPDATE("[httpUpdate] peekBytes magic header failed\n"); + _lastError = HTTP_UE_BIN_VERIFY_HEADER_FAILED; + http.end(); + return HTTP_UPDATE_FAILED; + } + + // check for valid first magic byte + if(buf[0] != 0xE9) { + DEBUG_HTTP_UPDATE("[httpUpdate] magic header not starts with 0xE9\n"); + _lastError = HTTP_UE_BIN_VERIFY_HEADER_FAILED; + http.end(); + return HTTP_UPDATE_FAILED; + + } + + uint32_t bin_flash_size = ESP.magicFlashChipSize((buf[3] & 0xf0) >> 4); + + // check if new bin fits to SPI flash + if(bin_flash_size > ESP.getFlashChipRealSize()) { + DEBUG_HTTP_UPDATE("[httpUpdate] magic header, new bin not fits SPI Flash\n"); + _lastError = HTTP_UE_BIN_FOR_WRONG_FLASH; + http.end(); + return HTTP_UPDATE_FAILED; + } + } + + if(runUpdate(*tcp, len, http.header("x-MD5"), command)) { + ret = HTTP_UPDATE_OK; + DEBUG_HTTP_UPDATE("[httpUpdate] Update ok\n"); + http.end(); + + if(_rebootOnUpdate) { + ESP.restart(); + } + + } else { + ret = HTTP_UPDATE_FAILED; + DEBUG_HTTP_UPDATE("[httpUpdate] Update failed\n"); + } + } + } else { + _lastError = HTTP_UE_SERVER_NOT_REPORT_SIZE; ret = HTTP_UPDATE_FAILED; - break; - case HTTP_CODE_FORBIDDEN: - _lastError = HTTP_UE_SERVER_FORBIDDEN; - ret = HTTP_UPDATE_FAILED; - break; - default: - _lastError = HTTP_UE_SERVER_WRONG_HTTP_CODE; - ret = HTTP_UPDATE_FAILED; - DEBUG_HTTP_UPDATE("[httpUpdate] HTTP Code is (%d)\n", code); - //http.writeToStream(&Serial1); - break; + DEBUG_HTTP_UPDATE("[httpUpdate] Content-Length is 0 or not set by Server?!\n"); + } + break; + case HTTP_CODE_NOT_MODIFIED: + ///< Not Modified (No updates) + ret = HTTP_UPDATE_NO_UPDATES; + break; + case HTTP_CODE_NOT_FOUND: + _lastError = HTTP_UE_SERVER_FILE_NOT_FOUND; + ret = HTTP_UPDATE_FAILED; + break; + case HTTP_CODE_FORBIDDEN: + _lastError = HTTP_UE_SERVER_FORBIDDEN; + ret = HTTP_UPDATE_FAILED; + break; + default: + _lastError = HTTP_UE_SERVER_WRONG_HTTP_CODE; + ret = HTTP_UPDATE_FAILED; + DEBUG_HTTP_UPDATE("[httpUpdate] HTTP Code is (%d)\n", code); + //http.writeToStream(&Serial1); + break; } http.end(); @@ -339,7 +342,8 @@ t_httpUpdate_return ESP8266HTTPUpdate::handleUpdate(HTTPClient& http, const Stri * @param md5 String * @return true if Update ok */ -bool ESP8266HTTPUpdate::runUpdate(Stream& in, uint32_t size, String md5, int command) { +bool ESP8266HTTPUpdate::runUpdate(Stream& in, uint32_t size, String md5, int command) +{ StreamString error; diff --git a/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.h b/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.h index 765cc3569..af6931b15 100644 --- a/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.h +++ b/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.h @@ -60,45 +60,49 @@ enum HTTPUpdateResult { typedef HTTPUpdateResult t_httpUpdate_return; // backward compatibility -class ESP8266HTTPUpdate { - public: - ESP8266HTTPUpdate(void); - ~ESP8266HTTPUpdate(void); +class ESP8266HTTPUpdate +{ +public: + ESP8266HTTPUpdate(void); + ~ESP8266HTTPUpdate(void); - void rebootOnUpdate(bool reboot) { _rebootOnUpdate = reboot; } + void rebootOnUpdate(bool reboot) + { + _rebootOnUpdate = reboot; + } - // This function is deprecated, use rebootOnUpdate and the next one instead - t_httpUpdate_return update(const String& url, const String& currentVersion, - const String& httpsFingerprint, bool reboot) __attribute__((deprecated)); - t_httpUpdate_return update(const String& url, const String& currentVersion = ""); - t_httpUpdate_return update(const String& url, const String& currentVersion, - const String& httpsFingerprint); + // This function is deprecated, use rebootOnUpdate and the next one instead + t_httpUpdate_return update(const String& url, const String& currentVersion, + const String& httpsFingerprint, bool reboot) __attribute__((deprecated)); + t_httpUpdate_return update(const String& url, const String& currentVersion = ""); + t_httpUpdate_return update(const String& url, const String& currentVersion, + const String& httpsFingerprint); - // This function is deprecated, use one of the overloads below along with rebootOnUpdate - t_httpUpdate_return update(const String& host, uint16_t port, const String& uri, const String& currentVersion, - bool https, const String& httpsFingerprint, bool reboot) __attribute__((deprecated)); + // This function is deprecated, use one of the overloads below along with rebootOnUpdate + t_httpUpdate_return update(const String& host, uint16_t port, const String& uri, const String& currentVersion, + bool https, const String& httpsFingerprint, bool reboot) __attribute__((deprecated)); - t_httpUpdate_return update(const String& host, uint16_t port, const String& uri = "/", - const String& currentVersion = ""); - t_httpUpdate_return update(const String& host, uint16_t port, const String& url, - const String& currentVersion, const String& httpsFingerprint); + t_httpUpdate_return update(const String& host, uint16_t port, const String& uri = "/", + const String& currentVersion = ""); + t_httpUpdate_return update(const String& host, uint16_t port, const String& url, + const String& currentVersion, const String& httpsFingerprint); - // This function is deprecated, use rebootOnUpdate and the next one instead - t_httpUpdate_return updateSpiffs(const String& url, const String& currentVersion, - const String& httpsFingerprint, bool reboot) __attribute__((deprecated)); - t_httpUpdate_return updateSpiffs(const String& url, const String& currentVersion = ""); - t_httpUpdate_return updateSpiffs(const String& url, const String& currentVersion, const String& httpsFingerprint); + // This function is deprecated, use rebootOnUpdate and the next one instead + t_httpUpdate_return updateSpiffs(const String& url, const String& currentVersion, + const String& httpsFingerprint, bool reboot) __attribute__((deprecated)); + t_httpUpdate_return updateSpiffs(const String& url, const String& currentVersion = ""); + t_httpUpdate_return updateSpiffs(const String& url, const String& currentVersion, const String& httpsFingerprint); - int getLastError(void); - String getLastErrorString(void); + int getLastError(void); + String getLastErrorString(void); - protected: - t_httpUpdate_return handleUpdate(HTTPClient& http, const String& currentVersion, bool spiffs = false); - bool runUpdate(Stream& in, uint32_t size, String md5, int command = U_FLASH); +protected: + t_httpUpdate_return handleUpdate(HTTPClient& http, const String& currentVersion, bool spiffs = false); + bool runUpdate(Stream& in, uint32_t size, String md5, int command = U_FLASH); - int _lastError; - bool _rebootOnUpdate = true; + int _lastError; + bool _rebootOnUpdate = true; }; extern ESP8266HTTPUpdate ESPhttpUpdate; From a455f22587869fe58983cb5931072301c6242a46 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 5 Apr 2016 02:21:42 +0300 Subject: [PATCH 14/46] HTTPClient, HTTPUpdate: save some RAM by moving strings into flash --- .../src/ESP8266HTTPClient.cpp | 54 ++++++++++--------- .../ESP8266HTTPClient/src/ESP8266HTTPClient.h | 2 +- .../src/ESP8266httpUpdate.cpp | 40 +++++++------- 3 files changed, 50 insertions(+), 46 deletions(-) diff --git a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp index 033fd3e84..bad89074b 100644 --- a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp +++ b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp @@ -256,7 +256,7 @@ void HTTPClient::setReuse(bool reuse) * set User Agent * @param userAgent const char * */ -void HTTPClient::setUserAgent(const char * userAgent) +void HTTPClient::setUserAgent(const String& userAgent) { _userAgent = userAgent; } @@ -359,7 +359,7 @@ int HTTPClient::sendRequest(const char * type, uint8_t * payload, size_t size) } if(payload && size > 0) { - addHeader("Content-Length", String(size)); + addHeader(F("Content-Length"), String(size)); } // send Header @@ -644,8 +644,8 @@ String HTTPClient::getString(void) if(_size) { // try to reserve needed memmory if(!sstring.reserve((_size + 1))) { - DEBUG_HTTPCLIENT("[HTTP-Client][getString] too less memory to reserve as string! need: %d\n", (_size + 1)); - return String("--too less memory--"); + DEBUG_HTTPCLIENT("[HTTP-Client][getString] not enough memory to reserve a string! need: %d\n", (_size + 1)); + return ""; } } @@ -662,27 +662,27 @@ String HTTPClient::errorToString(int error) { switch(error) { case HTTPC_ERROR_CONNECTION_REFUSED: - return String("connection refused"); + return F("connection refused"); case HTTPC_ERROR_SEND_HEADER_FAILED: - return String("send header failed"); + return F("send header failed"); case HTTPC_ERROR_SEND_PAYLOAD_FAILED: - return String("send payload failed"); + return F("send payload failed"); case HTTPC_ERROR_NOT_CONNECTED: - return String("not connected"); + return F("not connected"); case HTTPC_ERROR_CONNECTION_LOST: - return String("connection lost"); + return F("connection lost"); case HTTPC_ERROR_NO_STREAM: - return String("no stream"); + return F("no stream"); case HTTPC_ERROR_NO_HTTP_SERVER: - return String("no HTTP server"); + return F("no HTTP server"); case HTTPC_ERROR_TOO_LESS_RAM: - return String("too less ram"); + return F("too less ram"); case HTTPC_ERROR_ENCODING: - return String("Transfer-Encoding not supported"); + return F("Transfer-Encoding not supported"); case HTTPC_ERROR_STREAM_WRITE: - return String("Stream write error"); + return F("Stream write error"); case HTTPC_ERROR_READ_TIMEOUT: - return String("read Timeout"); + return F("read Timeout"); default: return String(); } @@ -698,7 +698,10 @@ void HTTPClient::addHeader(const String& name, const String& value, bool first) { // not allow set of Header handled by code - if(!name.equalsIgnoreCase("Connection") && !name.equalsIgnoreCase("User-Agent") && !name.equalsIgnoreCase("Host") && !(_base64Authorization.length() && name.equalsIgnoreCase("Authorization"))) { + if(!name.equalsIgnoreCase(F("Connection")) && + !name.equalsIgnoreCase(F("User-Agent")) && + !name.equalsIgnoreCase(F("Host")) && + !(name.equalsIgnoreCase(F("Authorization")) && _base64Authorization.length())){ String headerLine = name; headerLine += ": "; headerLine += value; @@ -821,7 +824,7 @@ bool HTTPClient::sendHeader(const char * type) return false; } - String header = String(type) + " " + _uri + " HTTP/1."; + String header = String(type) + " " + _uri + F(" HTTP/1."); if(_useHTTP10) { header += "0"; @@ -829,24 +832,25 @@ bool HTTPClient::sendHeader(const char * type) header += "1"; } - header += "\r\n" - "Host: " + _host + "\r\n" - "User-Agent: " + _userAgent + "\r\n" - "Connection: "; + header += String(F("\r\nHost: ")) + _host + + F("\r\nUser-Agent: ") + _userAgent + + F("\r\nConnection: "); if(_reuse) { - header += "keep-alive"; + header += F("keep-alive"); } else { - header += "close"; + header += F("close"); } header += "\r\n"; if(!_useHTTP10) { - header += "Accept-Encoding: identity;q=1,chunked;q=0.1,*;q=0\r\n"; + header += F("Accept-Encoding: identity;q=1,chunked;q=0.1,*;q=0\r\n"); } if(_base64Authorization.length()) { - header += "Authorization: Basic " + _base64Authorization + "\r\n"; + header += F("Authorization: Basic "); + header += _base64Authorization; + header += "\r\n"; } header += _headers + "\r\n"; diff --git a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h index d8c926359..1f5ef7fbc 100644 --- a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h +++ b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h @@ -145,7 +145,7 @@ public: bool connected(void); void setReuse(bool reuse); /// keep-alive - void setUserAgent(const char * userAgent); + void setUserAgent(const String& userAgent); void setAuthorization(const char * user, const char * password); void setAuthorization(const char * auth); void setTimeout(uint16_t timeout); diff --git a/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp b/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp index 910d8f901..b0020aad7 100644 --- a/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp +++ b/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp @@ -125,31 +125,31 @@ String ESP8266HTTPUpdate::getLastErrorString(void) StreamString error; Update.printError(error); error.trim(); // remove line ending - return "Update error: " + error; + return String(F("Update error: ")) + error; } // error from http client if(_lastError > -100) { - return "HTTP error: " + HTTPClient::errorToString(_lastError); + return String(F("HTTP error: ")) + HTTPClient::errorToString(_lastError); } switch(_lastError) { case HTTP_UE_TOO_LESS_SPACE: - return String("To less space"); + return F("To less space"); case HTTP_UE_SERVER_NOT_REPORT_SIZE: - return String("Server not Report Size"); + return F("Server not Report Size"); case HTTP_UE_SERVER_FILE_NOT_FOUND: - return String("File not Found (404)"); + return F("File not Found (404)"); case HTTP_UE_SERVER_FORBIDDEN: - return String("Forbidden (403)"); + return F("Forbidden (403)"); case HTTP_UE_SERVER_WRONG_HTTP_CODE: - return String("Wrong HTTP code"); + return F("Wrong HTTP code"); case HTTP_UE_SERVER_FAULTY_MD5: - return String("Faulty MD5"); + return F("Faulty MD5"); case HTTP_UE_BIN_VERIFY_HEADER_FAILED: - return String("Verify bin header failed"); + return F("Verify bin header failed"); case HTTP_UE_BIN_FOR_WRONG_FLASH: - return String("bin for wrong flash size"); + return F("bin for wrong flash size"); } return String(); @@ -170,22 +170,22 @@ HTTPUpdateResult ESP8266HTTPUpdate::handleUpdate(HTTPClient& http, const String& // use HTTP/1.0 for update since the update handler not support any transfer Encoding http.useHTTP10(true); http.setTimeout(8000); - http.setUserAgent("ESP8266-http-Update"); - http.addHeader("x-ESP8266-STA-MAC", WiFi.macAddress()); - http.addHeader("x-ESP8266-AP-MAC", WiFi.softAPmacAddress()); - http.addHeader("x-ESP8266-free-space", String(ESP.getFreeSketchSpace())); - http.addHeader("x-ESP8266-sketch-size", String(ESP.getSketchSize())); - http.addHeader("x-ESP8266-chip-size", String(ESP.getFlashChipRealSize())); - http.addHeader("x-ESP8266-sdk-version", ESP.getSdkVersion()); + http.setUserAgent(F("ESP8266-http-Update")); + http.addHeader(F("x-ESP8266-STA-MAC"), WiFi.macAddress()); + http.addHeader(F("x-ESP8266-AP-MAC"), WiFi.softAPmacAddress()); + http.addHeader(F("x-ESP8266-free-space"), String(ESP.getFreeSketchSpace())); + http.addHeader(F("x-ESP8266-sketch-size"), String(ESP.getSketchSize())); + http.addHeader(F("x-ESP8266-chip-size"), String(ESP.getFlashChipRealSize())); + http.addHeader(F("x-ESP8266-sdk-version"), ESP.getSdkVersion()); if(spiffs) { - http.addHeader("x-ESP8266-mode", "spiffs"); + http.addHeader(F("x-ESP8266-mode"), F("spiffs")); } else { - http.addHeader("x-ESP8266-mode", "sketch"); + http.addHeader(F("x-ESP8266-mode"), F("sketch")); } if(currentVersion && currentVersion[0] != 0x00) { - http.addHeader("x-ESP8266-version", currentVersion); + http.addHeader(F("x-ESP8266-version"), currentVersion); } const char * headerkeys[] = { "x-MD5" }; From bfe9f7be690dc79cf6f116b4e06fbabd06848a9f Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Sun, 10 Apr 2016 21:55:22 +0300 Subject: [PATCH 15/46] Update and move lwIP headers, add options to use different lwIP build for generic device Makefile added to lwip source folder to build and install liblwip_gcc.a --- boards.txt | 46 + libraries/ArduinoOTA/ArduinoOTA.cpp | 2 + libraries/ESP8266WiFi/src/lwip/mem_manager.h | 81 - libraries/ESP8266mDNS/ESP8266mDNS.cpp | 2 + platform.txt | 10 +- tools/sdk/include/ets_sys.h | 1 + .../src => tools/sdk/lwip}/arch/cc.h | 0 .../src => tools/sdk/lwip}/arch/perf.h | 0 .../src => tools/sdk/lwip}/arch/sys_arch.h | 0 .../src => tools/sdk/lwip}/lwip/api.h | 0 .../src => tools/sdk/lwip}/lwip/api_msg.h | 0 tools/sdk/lwip/lwip/app/dhcpserver.h | 101 + tools/sdk/lwip/lwip/app/espconn.h | 664 +++++ tools/sdk/lwip/lwip/app/espconn_tcp.h | 55 + tools/sdk/lwip/lwip/app/espconn_udp.h | 64 + tools/sdk/lwip/lwip/app/ping.h | 85 + .../src => tools/sdk/lwip}/lwip/arch.h | 0 .../src => tools/sdk/lwip}/lwip/autoip.h | 0 .../src => tools/sdk/lwip}/lwip/debug.h | 0 .../src => tools/sdk/lwip}/lwip/def.h | 0 .../src => tools/sdk/lwip}/lwip/dhcp.h | 9 + .../src => tools/sdk/lwip}/lwip/dns.h | 0 .../src => tools/sdk/lwip}/lwip/err.h | 0 .../src => tools/sdk/lwip}/lwip/icmp.h | 0 .../src => tools/sdk/lwip}/lwip/igmp.h | 2 +- .../src => tools/sdk/lwip}/lwip/inet.h | 0 .../src => tools/sdk/lwip}/lwip/inet_chksum.h | 0 .../src => tools/sdk/lwip}/lwip/init.h | 0 .../src => tools/sdk/lwip}/lwip/ip.h | 0 .../src => tools/sdk/lwip}/lwip/ip_addr.h | 0 .../src => tools/sdk/lwip}/lwip/ip_frag.h | 0 tools/sdk/lwip/lwip/mdns.h | 114 + .../src => tools/sdk/lwip}/lwip/mem.h | 2 +- .../src => tools/sdk/lwip}/lwip/memp.h | 2 +- .../src => tools/sdk/lwip}/lwip/memp_std.h | 0 .../src => tools/sdk/lwip}/lwip/netbuf.h | 0 .../src => tools/sdk/lwip}/lwip/netdb.h | 0 .../src => tools/sdk/lwip}/lwip/netif.h | 5 + .../src => tools/sdk/lwip}/lwip/netifapi.h | 0 .../src => tools/sdk/lwip}/lwip/opt.h | 2 +- .../src => tools/sdk/lwip}/lwip/pbuf.h | 0 tools/sdk/lwip/lwip/puck_def.h | 44 + .../src => tools/sdk/lwip}/lwip/raw.h | 0 .../src => tools/sdk/lwip}/lwip/sio.h | 0 .../src => tools/sdk/lwip}/lwip/snmp.h | 0 .../src => tools/sdk/lwip}/lwip/snmp_asn1.h | 0 .../src => tools/sdk/lwip}/lwip/snmp_msg.h | 0 .../sdk/lwip}/lwip/snmp_structs.h | 0 tools/sdk/lwip/lwip/sntp.h | 56 + .../src => tools/sdk/lwip}/lwip/sockets.h | 0 .../src => tools/sdk/lwip}/lwip/stats.h | 0 .../src => tools/sdk/lwip}/lwip/sys.h | 0 .../src => tools/sdk/lwip}/lwip/tcp.h | 0 .../src => tools/sdk/lwip}/lwip/tcp_impl.h | 24 +- .../src => tools/sdk/lwip}/lwip/tcpip.h | 0 .../src => tools/sdk/lwip}/lwip/timers.h | 0 .../src => tools/sdk/lwip}/lwip/udp.h | 0 .../src/include => tools/sdk/lwip}/lwipopts.h | 59 +- tools/sdk/lwip/netif/etharp.h | 254 ++ tools/sdk/lwip/netif/if_llc.h | 173 ++ tools/sdk/lwip/netif/ppp_oe.h | 190 ++ tools/sdk/lwip/netif/wlan_lwip_if.h | 25 + variants/lwip/common.h | 67 + variants/lwip/lwip/Makefile | 36 + variants/lwip/lwip/api/api_lib.c | 740 ++++++ variants/lwip/lwip/api/api_msg.c | 1540 +++++++++++ variants/lwip/lwip/api/err.c | 75 + variants/lwip/lwip/api/netbuf.c | 245 ++ variants/lwip/lwip/api/netdb.c | 352 +++ variants/lwip/lwip/api/netifapi.c | 160 ++ variants/lwip/lwip/api/sockets.c | 2343 +++++++++++++++++ variants/lwip/lwip/api/tcpip.c | 460 ++++ variants/lwip/lwip/app/dhcpserver.c | 1144 ++++++++ variants/lwip/lwip/app/espconn.c | 1303 +++++++++ variants/lwip/lwip/app/espconn_mdns.c | 134 + variants/lwip/lwip/app/espconn_tcp.c | 1390 ++++++++++ variants/lwip/lwip/app/espconn_udp.c | 423 +++ variants/lwip/lwip/app/netio.c | 369 +++ variants/lwip/lwip/app/ping.c | 329 +++ variants/lwip/lwip/core/def.c | 108 + variants/lwip/lwip/core/dhcp.c | 1815 +++++++++++++ variants/lwip/lwip/core/dns.c | 980 +++++++ variants/lwip/lwip/core/init.c | 325 +++ variants/lwip/lwip/core/ipv4/autoip.c | 536 ++++ variants/lwip/lwip/core/ipv4/icmp.c | 337 +++ variants/lwip/lwip/core/ipv4/igmp.c | 810 ++++++ variants/lwip/lwip/core/ipv4/inet.c | 42 + variants/lwip/lwip/core/ipv4/inet_chksum.c | 450 ++++ variants/lwip/lwip/core/ipv4/ip.c | 910 +++++++ variants/lwip/lwip/core/ipv4/ip_addr.c | 329 +++ variants/lwip/lwip/core/ipv4/ip_frag.c | 863 ++++++ variants/lwip/lwip/core/mdns.c | 1106 ++++++++ variants/lwip/lwip/core/mem.c | 644 +++++ variants/lwip/lwip/core/memp.c | 490 ++++ variants/lwip/lwip/core/netif.c | 758 ++++++ variants/lwip/lwip/core/pbuf.c | 1257 +++++++++ variants/lwip/lwip/core/raw.c | 358 +++ variants/lwip/lwip/core/sntp.c | 1130 ++++++++ variants/lwip/lwip/core/stats.c | 176 ++ variants/lwip/lwip/core/sys.c | 66 + variants/lwip/lwip/core/sys_arch.c | 13 + variants/lwip/lwip/core/tcp.c | 1671 ++++++++++++ variants/lwip/lwip/core/tcp_in.c | 1637 ++++++++++++ variants/lwip/lwip/core/tcp_out.c | 1525 +++++++++++ variants/lwip/lwip/core/timers.c | 513 ++++ variants/lwip/lwip/core/udp.c | 977 +++++++ variants/lwip/lwip/netif/etharp.c | 1413 ++++++++++ variants/lwip/pins_arduino.h | 37 + 108 files changed, 34343 insertions(+), 115 deletions(-) delete mode 100644 libraries/ESP8266WiFi/src/lwip/mem_manager.h rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/arch/cc.h (100%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/arch/perf.h (100%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/arch/sys_arch.h (100%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/api.h (100%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/api_msg.h (100%) mode change 100644 => 100755 create mode 100755 tools/sdk/lwip/lwip/app/dhcpserver.h create mode 100755 tools/sdk/lwip/lwip/app/espconn.h create mode 100755 tools/sdk/lwip/lwip/app/espconn_tcp.h create mode 100755 tools/sdk/lwip/lwip/app/espconn_udp.h create mode 100755 tools/sdk/lwip/lwip/app/ping.h rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/arch.h (100%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/autoip.h (100%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/debug.h (100%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/def.h (100%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/dhcp.h (96%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/dns.h (100%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/err.h (100%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/icmp.h (100%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/igmp.h (99%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/inet.h (100%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/inet_chksum.h (100%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/init.h (100%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/ip.h (100%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/ip_addr.h (100%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/ip_frag.h (100%) mode change 100644 => 100755 create mode 100755 tools/sdk/lwip/lwip/mdns.h rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/mem.h (99%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/memp.h (98%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/memp_std.h (100%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/netbuf.h (100%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/netdb.h (100%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/netif.h (98%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/netifapi.h (100%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/opt.h (99%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/pbuf.h (100%) mode change 100644 => 100755 create mode 100755 tools/sdk/lwip/lwip/puck_def.h rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/raw.h (100%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/sio.h (100%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/snmp.h (100%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/snmp_asn1.h (100%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/snmp_msg.h (100%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/snmp_structs.h (100%) mode change 100644 => 100755 create mode 100755 tools/sdk/lwip/lwip/sntp.h rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/sockets.h (100%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/stats.h (100%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/sys.h (100%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/tcp.h (100%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/tcp_impl.h (96%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/tcpip.h (100%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/timers.h (100%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src => tools/sdk/lwip}/lwip/udp.h (100%) mode change 100644 => 100755 rename {libraries/ESP8266WiFi/src/include => tools/sdk/lwip}/lwipopts.h (97%) mode change 100644 => 100755 create mode 100755 tools/sdk/lwip/netif/etharp.h create mode 100755 tools/sdk/lwip/netif/if_llc.h create mode 100755 tools/sdk/lwip/netif/ppp_oe.h create mode 100755 tools/sdk/lwip/netif/wlan_lwip_if.h create mode 100644 variants/lwip/common.h create mode 100644 variants/lwip/lwip/Makefile create mode 100755 variants/lwip/lwip/api/api_lib.c create mode 100755 variants/lwip/lwip/api/api_msg.c create mode 100755 variants/lwip/lwip/api/err.c create mode 100755 variants/lwip/lwip/api/netbuf.c create mode 100755 variants/lwip/lwip/api/netdb.c create mode 100755 variants/lwip/lwip/api/netifapi.c create mode 100755 variants/lwip/lwip/api/sockets.c create mode 100755 variants/lwip/lwip/api/tcpip.c create mode 100755 variants/lwip/lwip/app/dhcpserver.c create mode 100755 variants/lwip/lwip/app/espconn.c create mode 100755 variants/lwip/lwip/app/espconn_mdns.c create mode 100755 variants/lwip/lwip/app/espconn_tcp.c create mode 100755 variants/lwip/lwip/app/espconn_udp.c create mode 100755 variants/lwip/lwip/app/netio.c create mode 100755 variants/lwip/lwip/app/ping.c create mode 100755 variants/lwip/lwip/core/def.c create mode 100755 variants/lwip/lwip/core/dhcp.c create mode 100755 variants/lwip/lwip/core/dns.c create mode 100755 variants/lwip/lwip/core/init.c create mode 100755 variants/lwip/lwip/core/ipv4/autoip.c create mode 100755 variants/lwip/lwip/core/ipv4/icmp.c create mode 100755 variants/lwip/lwip/core/ipv4/igmp.c create mode 100755 variants/lwip/lwip/core/ipv4/inet.c create mode 100755 variants/lwip/lwip/core/ipv4/inet_chksum.c create mode 100755 variants/lwip/lwip/core/ipv4/ip.c create mode 100755 variants/lwip/lwip/core/ipv4/ip_addr.c create mode 100755 variants/lwip/lwip/core/ipv4/ip_frag.c create mode 100755 variants/lwip/lwip/core/mdns.c create mode 100755 variants/lwip/lwip/core/mem.c create mode 100755 variants/lwip/lwip/core/memp.c create mode 100755 variants/lwip/lwip/core/netif.c create mode 100755 variants/lwip/lwip/core/pbuf.c create mode 100755 variants/lwip/lwip/core/raw.c create mode 100755 variants/lwip/lwip/core/sntp.c create mode 100755 variants/lwip/lwip/core/stats.c create mode 100755 variants/lwip/lwip/core/sys.c create mode 100755 variants/lwip/lwip/core/sys_arch.c create mode 100755 variants/lwip/lwip/core/tcp.c create mode 100755 variants/lwip/lwip/core/tcp_in.c create mode 100755 variants/lwip/lwip/core/tcp_out.c create mode 100755 variants/lwip/lwip/core/timers.c create mode 100755 variants/lwip/lwip/core/udp.c create mode 100755 variants/lwip/lwip/netif/etharp.c create mode 100644 variants/lwip/pins_arduino.h diff --git a/boards.txt b/boards.txt index 70ff0b8b0..18c0ac3fb 100644 --- a/boards.txt +++ b/boards.txt @@ -7,6 +7,7 @@ menu.ResetMethod=Reset Method menu.ESPModule=Module menu.Debug=Debug port menu.DebugLevel=Debug Level +menu.LwIPVariant=lwIP Variant ############################################################## generic.name=Generic ESP8266 Module @@ -29,6 +30,21 @@ generic.build.flash_mode=qio generic.build.spiffs_pagesize=256 generic.build.debug_port= generic.build.debug_level= +generic.build.lwip_lib=-llwip +generic.build.lwip_flags= + +generic.menu.LwIPVariant.Espressif=Espressif (xcc) +generic.menu.LwIPVariant.Espressif.build.lwip_lib=-llwip +generic.menu.LwIPVariant.Espressif.build.lwip_flags= +generic.menu.LwIPVariant.Espressif.build.variant=generic +generic.menu.LwIPVariant.Prebuilt=Prebuilt Source (gcc) +generic.menu.LwIPVariant.Prebuilt.build.lwip_lib=-llwip_gcc +generic.menu.LwIPVariant.Prebuilt.build.lwip_flags=-DLWIP_OPEN_SRC +generic.menu.LwIPVariant.Prebuilt.build.variant=generic +generic.menu.LwIPVariant.OpenSource=Open Source (gcc) +generic.menu.LwIPVariant.OpenSource.build.lwip_lib= +generic.menu.LwIPVariant.OpenSource.build.lwip_flags=-DLWIP_OPEN_SRC +generic.menu.LwIPVariant.OpenSource.build.variant=lwip generic.menu.CpuFrequency.80=80 MHz generic.menu.CpuFrequency.80.build.f_cpu=80000000L @@ -247,6 +263,8 @@ espduino.build.flash_size=4M espduino.build.flash_freq=40 espduino.build.debug_port= espduino.build.debug_level= +espduino.build.lwip_lib=-llwip +espduino.build.lwip_flags= espduino.menu.CpuFrequency.80=80 MHz espduino.menu.CpuFrequency.80.build.f_cpu=80000000L @@ -310,6 +328,8 @@ huzzah.build.flash_size=4M huzzah.build.flash_freq=40 huzzah.build.debug_port= huzzah.build.debug_level= +huzzah.build.lwip_lib=-llwip +huzzah.build.lwip_flags= huzzah.menu.CpuFrequency.80=80 MHz huzzah.menu.CpuFrequency.80.build.f_cpu=80000000L @@ -359,6 +379,8 @@ espresso_lite_v1.build.variant=espresso_lite_v1 espresso_lite_v1.build.flash_mode=dio espresso_lite_v1.build.flash_size=4M espresso_lite_v1.build.flash_freq=40 +espresso_lite_v1.build.lwip_lib=-llwip +espresso_lite_v1.build.lwip_flags= espresso_lite_v1.menu.CpuFrequency.80=80 MHz espresso_lite_v1.menu.CpuFrequency.80.build.f_cpu=80000000L @@ -464,6 +486,8 @@ espresso_lite_v2.build.variant=espresso_lite_v2 espresso_lite_v2.build.flash_mode=dio espresso_lite_v2.build.flash_size=4M espresso_lite_v2.build.flash_freq=40 +espresso_lite_v2.build.lwip_lib=-llwip +espresso_lite_v2.build.lwip_flags= espresso_lite_v2.menu.CpuFrequency.80=80 MHz espresso_lite_v2.menu.CpuFrequency.80.build.f_cpu=80000000L @@ -575,6 +599,8 @@ nodemcu.build.flash_size=4M nodemcu.build.flash_freq=40 nodemcu.build.debug_port= nodemcu.build.debug_level= +nodemcu.build.lwip_lib=-llwip +nodemcu.build.lwip_flags= nodemcu.menu.CpuFrequency.80=80 MHz nodemcu.menu.CpuFrequency.80.build.f_cpu=80000000L @@ -639,6 +665,8 @@ nodemcuv2.build.flash_size=4M nodemcuv2.build.flash_freq=40 nodemcuv2.build.debug_port= nodemcuv2.build.debug_level= +nodemcuv2.build.lwip_lib=-llwip +nodemcuv2.build.lwip_flags= nodemcuv2.menu.CpuFrequency.80=80 MHz nodemcuv2.menu.CpuFrequency.80.build.f_cpu=80000000L @@ -710,6 +738,8 @@ modwifi.build.spiffs_pagesize=256 modwifi.build.spiffs_blocksize=8192 modwifi.build.debug_port= modwifi.build.debug_level= +modwifi.build.lwip_lib=-llwip +modwifi.build.lwip_flags= modwifi.menu.CpuFrequency.80=80 MHz modwifi.menu.CpuFrequency.80.build.f_cpu=80000000L @@ -764,6 +794,8 @@ thing.build.spiffs_blocksize=4096 thing.build.spiffs_pagesize=256 thing.build.debug_port= thing.build.debug_level= +thing.build.lwip_lib=-llwip +thing.build.lwip_flags= thing.menu.CpuFrequency.80=80 MHz thing.menu.CpuFrequency.80.build.f_cpu=80000000L @@ -813,6 +845,8 @@ thingdev.build.flash_ld=eagle.flash.512k64.ld thingdev.build.flash_freq=40 thingdev.build.debug_port= thingdev.build.debug_level= +thingdev.build.lwip_lib=-llwip +thingdev.build.lwip_flags= thingdev.menu.CpuFrequency.80=80 MHz thingdev.menu.CpuFrequency.80.build.f_cpu=80000000L @@ -860,6 +894,8 @@ esp210.build.flash_size=4M esp210.build.flash_freq=40 esp210.build.debug_port= esp210.build.debug_level= +esp210.build.lwip_lib=-llwip +esp210.build.lwip_flags= esp210.menu.CpuFrequency.80=80 MHz esp210.menu.CpuFrequency.80.build.f_cpu=80000000L @@ -951,6 +987,8 @@ d1_mini.build.flash_size=4M d1_mini.build.flash_freq=40 d1_mini.build.debug_port= d1_mini.build.debug_level= +d1_mini.build.lwip_lib=-llwip +d1_mini.build.lwip_flags= d1_mini.menu.CpuFrequency.80=80 MHz d1_mini.menu.CpuFrequency.80.build.f_cpu=80000000L @@ -1017,6 +1055,8 @@ d1.build.flash_size=4M d1.build.flash_freq=40 d1.build.debug_port= d1.build.debug_level= +d1.build.lwip_lib=-llwip +d1.build.lwip_flags= d1.menu.CpuFrequency.80=80 MHz d1.menu.CpuFrequency.80.build.f_cpu=80000000L @@ -1085,6 +1125,8 @@ espino.build.flash_freq=40 espino.build.spiffs_pagesize=256 espino.build.debug_port= espino.build.debug_level= +espino.build.lwip_lib=-llwip +espino.build.lwip_flags= espino.menu.CpuFrequency.80=80 MHz espino.menu.CpuFrequency.80.build.f_cpu=80000000L @@ -1159,6 +1201,8 @@ espinotee.build.flash_size=4M espinotee.build.flash_freq=40 espinotee.build.debug_port= espinotee.build.debug_level= +espinotee.build.lwip_lib=-llwip +espinotee.build.lwip_flags= espinotee.menu.CpuFrequency.80=80 MHz espinotee.menu.CpuFrequency.80.build.f_cpu=80000000L @@ -1222,6 +1266,8 @@ wifinfo.build.board=ESP8266_ESP12 wifinfo.build.spiffs_pagesize=256 wifinfo.build.debug_port=Serial1 wifinfo.build.debug_level=Wifinfo +wifinfo.build.lwip_lib=-llwip +wifinfo.build.lwip_flags= wifinfo.menu.Debug.Disabled=Disabled wifinfo.menu.Debug.Disabled.build.debug_port= diff --git a/libraries/ArduinoOTA/ArduinoOTA.cpp b/libraries/ArduinoOTA/ArduinoOTA.cpp index 386e230e8..074853436 100644 --- a/libraries/ArduinoOTA/ArduinoOTA.cpp +++ b/libraries/ArduinoOTA/ArduinoOTA.cpp @@ -1,4 +1,6 @@ +#ifndef LWIP_OPEN_SRC #define LWIP_OPEN_SRC +#endif #include #include #include "ArduinoOTA.h" diff --git a/libraries/ESP8266WiFi/src/lwip/mem_manager.h b/libraries/ESP8266WiFi/src/lwip/mem_manager.h deleted file mode 100644 index 78c366c55..000000000 --- a/libraries/ESP8266WiFi/src/lwip/mem_manager.h +++ /dev/null @@ -1,81 +0,0 @@ -#ifndef __MEM_MANAGER_H__ -#define __MEM_MANAGER_H__ - -#include "c_types.h" - -/*------------------------��������------------------------*/ - -#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE -#ifndef IOT_SIP_MODE -//#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 0x3fffc000 - (uint32)&_heap_start ) )//fix 16000 to 24000 on 14.2.26 -#else -#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 8000 ) ) -#endif -#define portBYTE_ALIGNMENT 8 -#define pdFALSE 0 -#define pdTRUE 1 - -#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE -#if portBYTE_ALIGNMENT == 8 - #define portBYTE_ALIGNMENT_MASK ( 0x0007 ) -#endif - -#if portBYTE_ALIGNMENT == 4 - #define portBYTE_ALIGNMENT_MASK ( 0x0003 ) -#endif - -#if portBYTE_ALIGNMENT == 2 - #define portBYTE_ALIGNMENT_MASK ( 0x0001 ) -#endif - -#if portBYTE_ALIGNMENT == 1 - #define portBYTE_ALIGNMENT_MASK ( 0x0000 ) -#endif - -#ifndef portBYTE_ALIGNMENT_MASK - #error "Invalid portBYTE_ALIGNMENT definition" -#endif - -#define configUSE_MALLOC_FAILED_HOOK 1 -#define portPOINTER_SIZE_TYPE unsigned int - -#define heapMINIMUM_BLOCK_SIZE ( ( size_t ) ( heapSTRUCT_SIZE * 2 ) ) - -//#define configADJUSTED_HEAP_SIZE ( configTOTAL_HEAP_SIZE - portBYTE_ALIGNMENT ) - -//static unsigned char ucHeap[ configTOTAL_HEAP_SIZE ]; -//static unsigned char *ucHeap; - -typedef struct A_BLOCK_LINK -{ - struct A_BLOCK_LINK *pxNextFreeBlock; //The next free block in the list. - size_t xBlockSize; //The size of the free block. -} xBlockLink; - -static const unsigned short heapSTRUCT_SIZE = ( sizeof( xBlockLink ) + portBYTE_ALIGNMENT - ( sizeof( xBlockLink ) % portBYTE_ALIGNMENT ) ); - -//static const size_t xTotalHeapSize = ( ( size_t ) configADJUSTED_HEAP_SIZE ) & ( ( size_t ) ~portBYTE_ALIGNMENT_MASK ); - -//static xBlockLink xStart, *pxEnd = NULL; - -//static size_t xFreeBytesRemaining = ( ( size_t ) configADJUSTED_HEAP_SIZE ) & ( ( size_t ) ~portBYTE_ALIGNMENT_MASK ); - - -/*------------------------��������-----------------------------------*/ - -//static void prvInsertBlockIntoFreeList( xBlockLink *pxBlockToInsert ) ;//ICACHE_FLASH_ATTR; - -//static void prvHeapInit( void ) ;//ICACHE_FLASH_ATTR; - -void vApplicationMallocFailedHook( void ) ;//ICACHE_FLASH_ATTR; - -void *pvPortMalloc( size_t xWantedSize, const char* file, int line ) __attribute__((malloc, alloc_size(1)));//ICACHE_FLASH_ATTR; - -void vPortFree( void *pv, const char* file, int line ) ;//ICACHE_FLASH_ATTR; - -size_t xPortGetFreeHeapSize( void ) ;//ICACHE_FLASH_ATTR; - -void vPortInitialiseBlocks( void ) ;//ICACHE_FLASH_ATTR; -/*-----------------------------------------------------------*/ - -#endif diff --git a/libraries/ESP8266mDNS/ESP8266mDNS.cpp b/libraries/ESP8266mDNS/ESP8266mDNS.cpp index 44d67c671..8fd971781 100644 --- a/libraries/ESP8266mDNS/ESP8266mDNS.cpp +++ b/libraries/ESP8266mDNS/ESP8266mDNS.cpp @@ -34,7 +34,9 @@ License (MIT license): // - Multicast DNS: http://www.ietf.org/rfc/rfc6762.txt // - MDNS-SD: https://tools.ietf.org/html/rfc6763 +#ifndef LWIP_OPEN_SRC #define LWIP_OPEN_SRC +#endif #include "ESP8266mDNS.h" #include diff --git a/platform.txt b/platform.txt index 5aeb40465..17a7c08fc 100644 --- a/platform.txt +++ b/platform.txt @@ -19,7 +19,7 @@ compiler.warning_flags.all=-Wall -Wextra compiler.path={runtime.tools.xtensa-lx106-elf-gcc.path}/bin/ compiler.sdk.path={runtime.platform.path}/tools/sdk -compiler.cpreprocessor.flags=-D__ets__ -DICACHE_FLASH -U__STRICT_ANSI__ "-I{compiler.sdk.path}/include" +compiler.cpreprocessor.flags=-D__ets__ -DICACHE_FLASH -U__STRICT_ANSI__ "-I{compiler.sdk.path}/include" "-I{compiler.sdk.path}/lwip" compiler.c.cmd=xtensa-lx106-elf-gcc compiler.c.flags=-c {compiler.warning_flags} -Os -g -Wpointer-arith -Wno-implicit-function-declaration -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals -falign-functions=4 -MMD -std=gnu99 -ffunction-sections -fdata-sections @@ -30,7 +30,7 @@ compiler.S.flags=-c -g -x assembler-with-cpp -MMD -mlongcalls compiler.c.elf.flags=-g {compiler.warning_flags} -Os -nostdlib -Wl,--no-check-sections -u call_user_start -Wl,-static "-L{compiler.sdk.path}/lib" "-L{compiler.sdk.path}/ld" "-T{build.flash_ld}" -Wl,--gc-sections -Wl,-wrap,system_restart_local -Wl,-wrap,register_chipv6_phy compiler.c.elf.cmd=xtensa-lx106-elf-gcc -compiler.c.elf.libs=-lm -lgcc -lhal -lphy -lpp -lnet80211 -llwip -lwpa -lcrypto -lmain -lwps -laxtls -lsmartconfig -lmesh -lwpa2 +compiler.c.elf.libs=-lm -lgcc -lhal -lphy -lpp -lnet80211 -lwpa -lcrypto -lmain -lwps -laxtls -lsmartconfig -lmesh -lwpa2 {build.lwip_lib} compiler.cpp.cmd=xtensa-lx106-elf-g++ compiler.cpp.flags=-c {compiler.warning_flags} -Os -g -mlongcalls -mtext-section-literals -fno-exceptions -fno-rtti -falign-functions=4 -std=c++11 -MMD -ffunction-sections -fdata-sections @@ -61,13 +61,13 @@ compiler.objcopy.eep.extra_flags= compiler.elf2hex.extra_flags= ## Compile c files -recipe.c.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.cpreprocessor.flags} {compiler.c.flags} -DF_CPU={build.f_cpu} {build.debug_port} {build.debug_level} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.c.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}" +recipe.c.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.cpreprocessor.flags} {compiler.c.flags} -DF_CPU={build.f_cpu} {build.lwip_flags} {build.debug_port} {build.debug_level} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.c.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}" ## Compile c++ files -recipe.cpp.o.pattern="{compiler.path}{compiler.cpp.cmd}" {compiler.cpreprocessor.flags} {compiler.cpp.flags} -DF_CPU={build.f_cpu} {build.debug_port} {build.debug_level} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.cpp.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}" +recipe.cpp.o.pattern="{compiler.path}{compiler.cpp.cmd}" {compiler.cpreprocessor.flags} {compiler.cpp.flags} -DF_CPU={build.f_cpu} {build.lwip_flags} {build.debug_port} {build.debug_level} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.cpp.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}" ## Compile S files -recipe.S.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.cpreprocessor.flags} {compiler.S.flags} -DF_CPU={build.f_cpu} {build.debug_port} {build.debug_level} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.c.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}" +recipe.S.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.cpreprocessor.flags} {compiler.S.flags} -DF_CPU={build.f_cpu} {build.lwip_flags} {build.debug_port} {build.debug_level} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.c.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}" ## Create archives recipe.ar.pattern="{compiler.path}{compiler.ar.cmd}" {compiler.ar.flags} {compiler.ar.extra_flags} "{build.path}/arduino.ar" "{object_file}" diff --git a/tools/sdk/include/ets_sys.h b/tools/sdk/include/ets_sys.h index 847621f4f..813061ab7 100644 --- a/tools/sdk/include/ets_sys.h +++ b/tools/sdk/include/ets_sys.h @@ -10,6 +10,7 @@ #include "c_types.h" #include "eagle_soc.h" +#include typedef uint32_t ETSSignal; typedef uint32_t ETSParam; diff --git a/libraries/ESP8266WiFi/src/arch/cc.h b/tools/sdk/lwip/arch/cc.h old mode 100644 new mode 100755 similarity index 100% rename from libraries/ESP8266WiFi/src/arch/cc.h rename to tools/sdk/lwip/arch/cc.h diff --git a/libraries/ESP8266WiFi/src/arch/perf.h b/tools/sdk/lwip/arch/perf.h old mode 100644 new mode 100755 similarity index 100% rename from libraries/ESP8266WiFi/src/arch/perf.h rename to tools/sdk/lwip/arch/perf.h diff --git a/libraries/ESP8266WiFi/src/arch/sys_arch.h b/tools/sdk/lwip/arch/sys_arch.h old mode 100644 new mode 100755 similarity index 100% rename from libraries/ESP8266WiFi/src/arch/sys_arch.h rename to tools/sdk/lwip/arch/sys_arch.h diff --git a/libraries/ESP8266WiFi/src/lwip/api.h b/tools/sdk/lwip/lwip/api.h old mode 100644 new mode 100755 similarity index 100% rename from libraries/ESP8266WiFi/src/lwip/api.h rename to tools/sdk/lwip/lwip/api.h diff --git a/libraries/ESP8266WiFi/src/lwip/api_msg.h b/tools/sdk/lwip/lwip/api_msg.h old mode 100644 new mode 100755 similarity index 100% rename from libraries/ESP8266WiFi/src/lwip/api_msg.h rename to tools/sdk/lwip/lwip/api_msg.h diff --git a/tools/sdk/lwip/lwip/app/dhcpserver.h b/tools/sdk/lwip/lwip/app/dhcpserver.h new file mode 100755 index 000000000..6a82cefee --- /dev/null +++ b/tools/sdk/lwip/lwip/app/dhcpserver.h @@ -0,0 +1,101 @@ +#ifndef __DHCPS_H__ +#define __DHCPS_H__ + +#define USE_DNS + +typedef struct dhcps_state{ + sint16_t state; +} dhcps_state; + +typedef struct dhcps_msg { + uint8_t op, htype, hlen, hops; + uint8_t xid[4]; + uint16_t secs, flags; + uint8_t ciaddr[4]; + uint8_t yiaddr[4]; + uint8_t siaddr[4]; + uint8_t giaddr[4]; + uint8_t chaddr[16]; + uint8_t sname[64]; + uint8_t file[128]; + uint8_t options[312]; +}dhcps_msg; + +#ifndef LWIP_OPEN_SRC +struct dhcps_lease { + bool enable; + struct ip_addr start_ip; + struct ip_addr end_ip; +}; + +enum dhcps_offer_option{ + OFFER_START = 0x00, + OFFER_ROUTER = 0x01, + OFFER_END +}; +#endif + +struct dhcps_pool{ + struct ip_addr ip; + uint8 mac[6]; + uint32 lease_timer; +}; + +typedef struct _list_node{ + void *pnode; + struct _list_node *pnext; +}list_node; + +extern uint32 dhcps_lease_time; +#define DHCPS_LEASE_TIMER dhcps_lease_time //0x05A0 +#define DHCPS_MAX_LEASE 0x64 +#define BOOTP_BROADCAST 0x8000 + +#define DHCP_REQUEST 1 +#define DHCP_REPLY 2 +#define DHCP_HTYPE_ETHERNET 1 +#define DHCP_HLEN_ETHERNET 6 +#define DHCP_MSG_LEN 236 + +#define DHCPS_SERVER_PORT 67 +#define DHCPS_CLIENT_PORT 68 + +#define DHCPDISCOVER 1 +#define DHCPOFFER 2 +#define DHCPREQUEST 3 +#define DHCPDECLINE 4 +#define DHCPACK 5 +#define DHCPNAK 6 +#define DHCPRELEASE 7 + +#define DHCP_OPTION_SUBNET_MASK 1 +#define DHCP_OPTION_ROUTER 3 +#define DHCP_OPTION_DNS_SERVER 6 +#define DHCP_OPTION_REQ_IPADDR 50 +#define DHCP_OPTION_LEASE_TIME 51 +#define DHCP_OPTION_MSG_TYPE 53 +#define DHCP_OPTION_SERVER_ID 54 +#define DHCP_OPTION_INTERFACE_MTU 26 +#define DHCP_OPTION_PERFORM_ROUTER_DISCOVERY 31 +#define DHCP_OPTION_BROADCAST_ADDRESS 28 +#define DHCP_OPTION_REQ_LIST 55 +#define DHCP_OPTION_END 255 + +//#define USE_CLASS_B_NET 1 +#define DHCPS_DEBUG 0 +#define MAX_STATION_NUM 8 + +#define DHCPS_STATE_OFFER 1 +#define DHCPS_STATE_DECLINE 2 +#define DHCPS_STATE_ACK 3 +#define DHCPS_STATE_NAK 4 +#define DHCPS_STATE_IDLE 5 +#define DHCPS_STATE_RELEASE 6 + +#define dhcps_router_enabled(offer) ((offer & OFFER_ROUTER) != 0) + +void dhcps_start(struct ip_info *info); +void dhcps_stop(void); + +#endif + diff --git a/tools/sdk/lwip/lwip/app/espconn.h b/tools/sdk/lwip/lwip/app/espconn.h new file mode 100755 index 000000000..ae9ed5019 --- /dev/null +++ b/tools/sdk/lwip/lwip/app/espconn.h @@ -0,0 +1,664 @@ +#ifndef __ESPCONN_H__ +#define __ESPCONN_H__ + +#include "lwip/dns.h" +#include "os_type.h" + +#if 0 +#define espconn_printf(fmt, args...) os_printf(fmt,## args) +#else +#define espconn_printf(fmt, args...) +#endif + + +typedef void *espconn_handle; +typedef void (* espconn_connect_callback)(void *arg); +typedef void (* espconn_reconnect_callback)(void *arg, sint8 err); + +/* Definitions for error constants. */ + +#define ESPCONN_OK 0 /* No error, everything OK. */ +#define ESPCONN_MEM -1 /* Out of memory error. */ +#define ESPCONN_TIMEOUT -3 /* Timeout. */ +#define ESPCONN_RTE -4 /* Routing problem. */ +#define ESPCONN_INPROGRESS -5 /* Operation in progress */ +#define ESPCONN_MAXNUM -7 /* Total number exceeds the set maximum*/ + +#define ESPCONN_ABRT -8 /* Connection aborted. */ +#define ESPCONN_RST -9 /* Connection reset. */ +#define ESPCONN_CLSD -10 /* Connection closed. */ +#define ESPCONN_CONN -11 /* Not connected. */ + +#define ESPCONN_ARG -12 /* Illegal argument. */ +#define ESPCONN_IF -14 /* Low_level error */ +#define ESPCONN_ISCONN -15 /* Already connected. */ + +#define ESPCONN_HANDSHAKE -28 /* ssl handshake failed */ +#define ESPCONN_RESP_TIMEOUT -29 /* ssl handshake no response*/ +#define ESPCONN_PROTO_MSG -61 /* ssl application invalid */ + +#define ESPCONN_SSL 0x01 +#define ESPCONN_NORM 0x00 + +#define ESPCONN_STA 0x01 +#define ESPCONN_AP 0x02 +#define ESPCONN_AP_STA 0x03 + +#define STA_NETIF 0x00 +#define AP_NETIF 0x01 + +/** Protocol family and type of the espconn */ +enum espconn_type { + ESPCONN_INVALID = 0, + /* ESPCONN_TCP Group */ + ESPCONN_TCP = 0x10, + /* ESPCONN_UDP Group */ + ESPCONN_UDP = 0x20, +}; + +/** Current state of the espconn. Non-TCP espconn are always in state ESPCONN_NONE! */ +enum espconn_state { + ESPCONN_NONE, + ESPCONN_WAIT, + ESPCONN_LISTEN, + ESPCONN_CONNECT, + ESPCONN_WRITE, + ESPCONN_READ, + ESPCONN_CLOSE +}; + +typedef struct _esp_tcp { + int remote_port; + int local_port; + uint8 local_ip[4]; + uint8 remote_ip[4]; + espconn_connect_callback connect_callback; + espconn_reconnect_callback reconnect_callback; + espconn_connect_callback disconnect_callback; + espconn_connect_callback write_finish_fn; +} esp_tcp; + +typedef struct _esp_udp { + int remote_port; + int local_port; + uint8 local_ip[4]; + uint8 remote_ip[4]; +} esp_udp; + +typedef struct _remot_info{ + enum espconn_state state; + int remote_port; + uint8 remote_ip[4]; +}remot_info; + +/** A callback prototype to inform about events for a espconn */ +typedef void (* espconn_recv_callback)(void *arg, char *pdata, unsigned short len); +typedef void (* espconn_sent_callback)(void *arg); + +/** A espconn descriptor */ +struct espconn { + /** type of the espconn (TCP, UDP) */ + enum espconn_type type; + /** current state of the espconn */ + enum espconn_state state; + union { + esp_tcp *tcp; + esp_udp *udp; + } proto; + /** A callback function that is informed about events for this espconn */ + espconn_recv_callback recv_callback; + espconn_sent_callback sent_callback; + uint8 link_cnt; + void *reverse; +}; + +enum espconn_option{ + ESPCONN_START = 0x00, + ESPCONN_REUSEADDR = 0x01, + ESPCONN_NODELAY = 0x02, + ESPCONN_COPY = 0x04, + ESPCONN_KEEPALIVE = 0x08, + ESPCONN_END +}; + +enum espconn_level{ + ESPCONN_KEEPIDLE, + ESPCONN_KEEPINTVL, + ESPCONN_KEEPCNT +}; + +struct espconn_packet{ + uint16 sent_length; /* sent length successful*/ + uint16 snd_buf_size; /* Available buffer size for sending */ + uint16 snd_queuelen; /* Available buffer space for sending */ + uint16 total_queuelen; /* total Available buffer space for sending */ + uint32 packseqno; /* seqno to be sent */ + uint32 packseq_nxt; /* seqno expected */ + uint32 packnum; +}; + +typedef struct _espconn_buf{ + uint8 *payload; + uint8 *punsent; + uint16 unsent; + uint16 len; + uint16 tot_len; + struct _espconn_buf *pnext; +} espconn_buf; + +typedef struct _comon_pkt{ + void *pcb; + int remote_port; + uint8 remote_ip[4]; + uint32 local_port; + uint32 local_ip; + espconn_buf *pbuf; + espconn_buf *ptail; + uint8* ptrbuf; + uint16 cntr; + sint8 err; + uint32 timeout; + uint32 recv_check; + uint8 pbuf_num; + struct espconn_packet packet_info; + bool write_flag; + enum espconn_option espconn_opt; +}comon_pkt; + +typedef struct _espconn_msg{ + struct espconn *pespconn; + comon_pkt pcommon; + uint8 count_opt; + sint16_t hs_status; //the status of the handshake + void *preverse; + void *pssl; + struct _espconn_msg *pnext; + +//***********Code for WIFI_BLOCK from upper************** + uint8 recv_hold_flag; + uint16 recv_holded_buf_Len; +}espconn_msg; + +#ifndef _MDNS_INFO +#define _MDNS_INFO +struct mdns_info { + char *host_name; + char *server_name; + uint16 server_port; + unsigned long ipAddr; + char *txt_data[10]; +}; +#endif + +#define linkMax 15 + +#define espconn_delay_disabled(espconn) (((espconn)->pcommon.espconn_opt & ESPCONN_NODELAY) != 0) +#define espconn_delay_enabled(espconn) (((espconn)->pcommon.espconn_opt & ESPCONN_NODELAY) == 0) +#define espconn_reuse_disabled(espconn) (((espconn)->pcommon.espconn_opt & ESPCONN_REUSEADDR) != 0) +#define espconn_copy_disabled(espconn) (((espconn)->pcommon.espconn_opt & ESPCONN_COPY) != 0) +#define espconn_copy_enabled(espconn) (((espconn)->pcommon.espconn_opt & ESPCONN_COPY) == 0) +#define espconn_keepalive_disabled(espconn) (((espconn)->pcommon.espconn_opt & ESPCONN_KEEPALIVE) != 0) +#define espconn_keepalive_enabled(espconn) (((espconn)->pcommon.espconn_opt & ESPCONN_KEEPALIVE) == 0) + +#define espconn_TaskPrio 26 +#define espconn_TaskQueueLen 15 + +enum espconn_sig { + SIG_ESPCONN_NONE, + SIG_ESPCONN_ERRER, + SIG_ESPCONN_LISTEN, + SIG_ESPCONN_CONNECT, + SIG_ESPCONN_WRITE, + SIG_ESPCONN_SEND, + SIG_ESPCONN_READ, + SIG_ESPCONN_CLOSE +}; + +/****************************************************************************** + * FunctionName : espconn_copy_partial + * Description : reconnect with host + * Parameters : arg -- Additional argument to pass to the callback function + * Returns : none +*******************************************************************************/ + +void espconn_copy_partial(struct espconn *pesp_dest, struct espconn *pesp_source); + +/****************************************************************************** + * FunctionName : espconn_copy_partial + * Description : insert the node to the active connection list + * Parameters : arg -- Additional argument to pass to the callback function + * Returns : none +*******************************************************************************/ + +void espconn_list_creat(espconn_msg **phead, espconn_msg* pinsert); + +/****************************************************************************** + * FunctionName : espconn_list_delete + * Description : remove the node from the active connection list + * Parameters : arg -- Additional argument to pass to the callback function + * Returns : none +*******************************************************************************/ + +void espconn_list_delete(espconn_msg **phead, espconn_msg* pdelete); + +/****************************************************************************** + * FunctionName : espconn_find_connection + * Description : Initialize the server: set up a listening PCB and bind it to + * the defined port + * Parameters : espconn -- the espconn used to build server + * Returns : none + *******************************************************************************/ + +bool espconn_find_connection(struct espconn *pespconn, espconn_msg **pnode); + +/****************************************************************************** + * FunctionName : espconn_get_connection_info + * Description : used to specify the function that should be called when disconnect + * Parameters : espconn -- espconn to set the err callback + * discon_cb -- err callback function to call when err + * Returns : none +*******************************************************************************/ + +sint8 espconn_get_connection_info(struct espconn *pespconn, remot_info **pcon_info, uint8 typeflags); + +/****************************************************************************** + * FunctionName : espconn_get_packet_info + * Description : get the packet info with host + * Parameters : espconn -- the espconn used to disconnect the connection + * infoarg -- the packet info + * Returns : the errur code +*******************************************************************************/ + +sint8 espconn_get_packet_info(struct espconn *espconn, struct espconn_packet* infoarg); + +/****************************************************************************** + * FunctionName : espconn_connect + * Description : The function given as the connect + * Parameters : espconn -- the espconn used to listen the connection + * Returns : none +*******************************************************************************/ + +extern sint8 espconn_connect(struct espconn *espconn); + +/****************************************************************************** + * FunctionName : espconn_disconnect + * Description : disconnect with host + * Parameters : espconn -- the espconn used to disconnect the connection + * Returns : none +*******************************************************************************/ + +extern sint8 espconn_disconnect(struct espconn *espconn); + +/****************************************************************************** + * FunctionName : espconn_delete + * Description : disconnect with host + * Parameters : espconn -- the espconn used to disconnect the connection + * Returns : none +*******************************************************************************/ + +extern sint8 espconn_delete(struct espconn *espconn); + +/****************************************************************************** + * FunctionName : espconn_accept + * Description : The function given as the listen + * Parameters : espconn -- the espconn used to listen the connection + * Returns : none +*******************************************************************************/ + +extern sint8 espconn_accept(struct espconn *espconn); + +/****************************************************************************** + * FunctionName : espconn_create + * Description : sent data for client or server + * Parameters : espconn -- espconn to the data transmission + * Returns : result +*******************************************************************************/ + +extern sint8 espconn_create(struct espconn *espconn); + +/****************************************************************************** + * FunctionName : espconn_tcp_get_wnd + * Description : get the window size of simulatenously active TCP connections + * Parameters : none + * Returns : the number of TCP_MSS active TCP connections +*******************************************************************************/ +extern uint8 espconn_tcp_get_wnd(void); + +/****************************************************************************** + * FunctionName : espconn_tcp_set_max_con + * Description : set the window size simulatenously active TCP connections + * Parameters : num -- the number of TCP_MSS + * Returns : ESPCONN_ARG -- Illegal argument + * ESPCONN_OK -- No error +*******************************************************************************/ +extern sint8 espconn_tcp_set_wnd(uint8 num); + +/****************************************************************************** + * FunctionName : espconn_tcp_get_max_con + * Description : get the number of simulatenously active TCP connections + * Parameters : none + * Returns : none +*******************************************************************************/ + +extern uint8 espconn_tcp_get_max_con(void); + +/****************************************************************************** + * FunctionName : espconn_tcp_set_max_con + * Description : set the number of simulatenously active TCP connections + * Parameters : num -- total number + * Returns : none +*******************************************************************************/ + +extern sint8 espconn_tcp_set_max_con(uint8 num); + +/****************************************************************************** + * FunctionName : espconn_tcp_get_max_retran + * Description : get the Maximum number of retransmissions of data active TCP connections + * Parameters : none + * Returns : the Maximum number of retransmissions +*******************************************************************************/ +extern uint8 espconn_tcp_get_max_retran(void); + +/****************************************************************************** + * FunctionName : espconn_tcp_set_max_retran + * Description : set the Maximum number of retransmissions of data active TCP connections + * Parameters : num -- the Maximum number of retransmissions + * Returns : result +*******************************************************************************/ + +extern sint8 espconn_tcp_set_max_retran(uint8 num); + +/****************************************************************************** + * FunctionName : espconn_tcp_get_max_syn + * Description : get the Maximum number of retransmissions of SYN segments + * Parameters : none + * Returns : the Maximum number of retransmissions +*******************************************************************************/ + +extern uint8 espconn_tcp_get_max_syn(void); + +/****************************************************************************** + * FunctionName : espconn_tcp_set_max_syn + * Description : set the Maximum number of retransmissions of SYN segments + * Parameters : num -- the Maximum number of retransmissions + * Returns : result +*******************************************************************************/ + +extern sint8 espconn_tcp_set_max_syn(uint8 num); + +/****************************************************************************** + * FunctionName : espconn_tcp_get_max_con_allow + * Description : get the count of simulatenously active connections on the server + * Parameters : espconn -- espconn to get the count + * Returns : result +*******************************************************************************/ + +extern sint8 espconn_tcp_get_max_con_allow(struct espconn *espconn); + +/****************************************************************************** + * FunctionName : espconn_tcp_set_max_con_allow + * Description : set the count of simulatenously active connections on the server + * Parameters : espconn -- espconn to set the count + * Returns : result +*******************************************************************************/ + +extern sint8 espconn_tcp_set_max_con_allow(struct espconn *espconn, uint8 num); + +/****************************************************************************** + * FunctionName : espconn_tcp_set_buf_count + * Description : set the total number of espconn_buf on the unsent lists + * Parameters : espconn -- espconn to set the count + * num -- the total number of espconn_buf + * Returns : result +*******************************************************************************/ + +extern sint8 espconn_tcp_set_buf_count(struct espconn *espconn, uint8 num); + +/****************************************************************************** + * FunctionName : espconn_regist_time + * Description : used to specify the time that should be called when don't recv data + * Parameters : espconn -- the espconn used to the connection + * interval -- the timer when don't recv data + * Returns : none +*******************************************************************************/ + +extern sint8 espconn_regist_time(struct espconn *espconn, uint32 interval, uint8 type_flag); + +/****************************************************************************** + * FunctionName : espconn_regist_sentcb + * Description : Used to specify the function that should be called when data + * has been successfully delivered to the remote host. + * Parameters : struct espconn *espconn -- espconn to set the sent callback + * espconn_sent_callback sent_cb -- sent callback function to + * call for this espconn when data is successfully sent + * Returns : none +*******************************************************************************/ + +extern sint8 espconn_regist_sentcb(struct espconn *espconn, espconn_sent_callback sent_cb); + +/****************************************************************************** + * FunctionName : espconn_regist_sentcb + * Description : Used to specify the function that should be called when data + * has been successfully delivered to the remote host. + * Parameters : espconn -- espconn to set the sent callback + * sent_cb -- sent callback function to call for this espconn + * when data is successfully sent + * Returns : none +*******************************************************************************/ +extern sint8 espconn_regist_write_finish(struct espconn *espconn, espconn_connect_callback write_finish_fn); + +/****************************************************************************** + * FunctionName : espconn_sent + * Description : sent data for client or server + * Parameters : espconn -- espconn to set for client or server + * psent -- data to send + * length -- length of data to send + * Returns : none +*******************************************************************************/ + +extern sint8 espconn_sent(struct espconn *espconn, uint8 *psent, uint16 length); + +/****************************************************************************** + * FunctionName : espconn_regist_connectcb + * Description : used to specify the function that should be called when + * connects to host. + * Parameters : espconn -- espconn to set the connect callback + * connect_cb -- connected callback function to call when connected + * Returns : none +*******************************************************************************/ + +extern sint8 espconn_regist_connectcb(struct espconn *espconn, espconn_connect_callback connect_cb); + +/****************************************************************************** + * FunctionName : espconn_regist_recvcb + * Description : used to specify the function that should be called when recv + * data from host. + * Parameters : espconn -- espconn to set the recv callback + * recv_cb -- recv callback function to call when recv data + * Returns : none +*******************************************************************************/ + +extern sint8 espconn_regist_recvcb(struct espconn *espconn, espconn_recv_callback recv_cb); + +/****************************************************************************** + * FunctionName : espconn_regist_reconcb + * Description : used to specify the function that should be called when connection + * because of err disconnect. + * Parameters : espconn -- espconn to set the err callback + * recon_cb -- err callback function to call when err + * Returns : none +*******************************************************************************/ + +extern sint8 espconn_regist_reconcb(struct espconn *espconn, espconn_reconnect_callback recon_cb); + +/****************************************************************************** + * FunctionName : espconn_regist_disconcb + * Description : used to specify the function that should be called when disconnect + * Parameters : espconn -- espconn to set the err callback + * discon_cb -- err callback function to call when err + * Returns : none +*******************************************************************************/ + +extern sint8 espconn_regist_disconcb(struct espconn *espconn, espconn_connect_callback discon_cb); + +/****************************************************************************** + * FunctionName : espconn_port + * Description : access port value for client so that we don't end up bouncing + * all connections at the same time . + * Parameters : none + * Returns : access port value +*******************************************************************************/ + +extern uint32 espconn_port(void); + +/****************************************************************************** + * FunctionName : espconn_set_opt + * Description : access port value for client so that we don't end up bouncing + * all connections at the same time . + * Parameters : none + * Returns : access port value +*******************************************************************************/ +extern sint8 espconn_set_opt(struct espconn *espconn, uint8 opt); + +/****************************************************************************** + * FunctionName : espconn_set_keepalive + * Description : access level value for connection so that we set the value for + * keep alive + * Parameters : espconn -- the espconn used to set the connection + * level -- the connection's level + * value -- the value of time(s) + * Returns : access port value +*******************************************************************************/ +extern sint8 espconn_set_keepalive(struct espconn *espconn, uint8 level, void* optarg); + +/****************************************************************************** + * FunctionName : espconn_get_keepalive + * Description : access level value for connection so that we get the value for + * keep alive + * Parameters : espconn -- the espconn used to get the connection + * level -- the connection's level + * Returns : access keep alive value +*******************************************************************************/ +extern sint8 espconn_get_keepalive(struct espconn *espconn, uint8 level, void *optarg); + +/****************************************************************************** + * FunctionName : espconn_gethostbyname + * Description : Resolve a hostname (string) into an IP address. + * Parameters : pespconn -- espconn to resolve a hostname + * hostname -- the hostname that is to be queried + * addr -- pointer to a ip_addr_t where to store the address if + * it is already cached in the dns_table (only valid if + * ESPCONN_OK is returned!) + * found -- a callback function to be called on success, failure + * or timeout (only if ERR_INPROGRESS is returned!) + * Returns : err_t return code + * - ESPCONN_OK if hostname is a valid IP address string or the host + * name is already in the local names table. + * - ESPCONN_INPROGRESS enqueue a request to be sent to the DNS server + * for resolution if no errors are present. + * - ESPCONN_ARG: dns client not initialized or invalid hostname +*******************************************************************************/ + +extern sint8 espconn_gethostbyname(struct espconn *pespconn, const char *name, ip_addr_t *addr, dns_found_callback found); + +/****************************************************************************** + * FunctionName : espconn_igmp_join + * Description : join a multicast group + * Parameters : host_ip -- the ip address of udp server + * multicast_ip -- multicast ip given by user + * Returns : none +*******************************************************************************/ +extern sint8 espconn_igmp_join(ip_addr_t *host_ip, ip_addr_t *multicast_ip); + +/****************************************************************************** + * FunctionName : espconn_igmp_leave + * Description : leave a multicast group + * Parameters : host_ip -- the ip address of udp server + * multicast_ip -- multicast ip given by user + * Returns : none +*******************************************************************************/ +extern sint8 espconn_igmp_leave(ip_addr_t *host_ip, ip_addr_t *multicast_ip); + +/****************************************************************************** + * FunctionName : espconn_mdns_init + * Description : register a device with mdns + * Parameters : ipAddr -- the ip address of device + * hostname -- the hostname of device + * Returns : none +*******************************************************************************/ +extern void espconn_mdns_init(struct mdns_info *info); +/****************************************************************************** + * FunctionName : espconn_mdns_init + * Description : close mdns socket + * Parameters : void + * Returns : none +*******************************************************************************/ +extern void espconn_mdns_close(void); +/****************************************************************************** + * FunctionName : mdns_server_register + * Description : register a server and join a multicast group + * Parameters : none + * Returns : none +*******************************************************************************/ +extern void espconn_mdns_server_register(void); +/****************************************************************************** + * FunctionName : mdns_server_register + * Description : unregister server and leave multicast group + * Parameters : none + * Returns : none +*******************************************************************************/ +extern void espconn_mdns_server_unregister(void); +/****************************************************************************** + * FunctionName : espconn_mdns_get_servername + * Description : get server name + * Parameters : none + * Returns : server name +*******************************************************************************/ +extern char* espconn_mdns_get_servername(void); +/****************************************************************************** + * FunctionName : espconn_mdns_get_servername + * Description : set server name + * Parameters : server name + * Returns : none +*******************************************************************************/ +extern void espconn_mdns_set_servername(const char *name); +/****************************************************************************** + * FunctionName : espconn_mdns_set_hostname + * Description : set host name + * Parameters : host name + * Returns : none +*******************************************************************************/ +extern void espconn_mdns_set_hostname(char *name); +/****************************************************************************** + * FunctionName : espconn_mdns_init + * Description : get host name + * Parameters : void + * Returns : hostname +*******************************************************************************/ +extern char* espconn_mdns_get_hostname(void); +/****************************************************************************** + * FunctionName : espconn_mdns_disable + * Description : join a multicast group + * Parameters : host_ip -- the ip address of udp server + * multicast_ip -- multicast ip given by user + * Returns : none +*******************************************************************************/ +extern void espconn_mdns_disable(void); +/****************************************************************************** + * FunctionName : espconn_mdns_enable + * Description : enable mdns + * Parameters : void + * Returns : none +*******************************************************************************/ +extern void espconn_mdns_enable(void); +/****************************************************************************** + * FunctionName : espconn_dns_setserver + * Description : Initialize one of the DNS servers. + * Parameters : numdns -- the index of the DNS server to set must + * be < DNS_MAX_SERVERS = 2 + * dnsserver -- IP address of the DNS server to set + * Returns : none +*******************************************************************************/ +extern void espconn_dns_setserver(u8_t numdns, ip_addr_t *dnsserver); +#endif + diff --git a/tools/sdk/lwip/lwip/app/espconn_tcp.h b/tools/sdk/lwip/lwip/app/espconn_tcp.h new file mode 100755 index 000000000..5598a5cd3 --- /dev/null +++ b/tools/sdk/lwip/lwip/app/espconn_tcp.h @@ -0,0 +1,55 @@ +#ifndef __ESPCONN_TCP_H__ +#define __ESPCONN_TCP_H__ + +#ifndef ESPCONN_TCP_DEBUG +#define ESPCONN_TCP_DEBUG LWIP_DBG_OFF +#endif +#include "lwip/app/espconn.h" + +#ifndef ESPCONN_TCP_TIMER +#define ESPCONN_TCP_TIMER 40 +#endif + +#define espconn_keepalive_enable(pcb) ((pcb)->so_options |= SOF_KEEPALIVE) +#define espconn_keepalive_disable(pcb) ((pcb)->so_options &= ~SOF_KEEPALIVE) + +/****************************************************************************** + * FunctionName : espconn_kill_oldest_pcb + * Description : A oldest incoming connection has been killed. + * Parameters : none + * Returns : none +*******************************************************************************/ + +extern void espconn_kill_oldest_pcb(void); + +/****************************************************************************** + * FunctionName : espconn_tcp_disconnect + * Description : A new incoming connection has been disconnected. + * Parameters : espconn -- the espconn used to disconnect with host + * Returns : none +*******************************************************************************/ + +extern void espconn_tcp_disconnect(espconn_msg *pdiscon,u8 type); + +/****************************************************************************** + * FunctionName : espconn_tcp_client + * Description : Initialize the client: set up a connect PCB and bind it to + * the defined port + * Parameters : espconn -- the espconn used to build client + * Returns : none +*******************************************************************************/ + +extern sint8 espconn_tcp_client(struct espconn* espconn); + +/****************************************************************************** + * FunctionName : espconn_tcp_server + * Description : Initialize the server: set up a listening PCB and bind it to + * the defined port + * Parameters : espconn -- the espconn used to build server + * Returns : none +*******************************************************************************/ + +extern sint8 espconn_tcp_server(struct espconn *espconn); + +#endif /* __CLIENT_TCP_H__ */ + diff --git a/tools/sdk/lwip/lwip/app/espconn_udp.h b/tools/sdk/lwip/lwip/app/espconn_udp.h new file mode 100755 index 000000000..cdb312f73 --- /dev/null +++ b/tools/sdk/lwip/lwip/app/espconn_udp.h @@ -0,0 +1,64 @@ +#ifndef __ESPCONN_UDP_H__ +#define __ESPCONN_UDP_H__ + +#ifndef ESPCONN_UDP_DEBUG +#define ESPCONN_UDP_DEBUG LWIP_DBG_OFF +#endif + +#include "lwip/app/espconn.h" + +/****************************************************************************** + * FunctionName : espconn_udp_client + * Description : Initialize the client: set up a PCB and bind it to the port + * Parameters : pespconn -- the espconn used to build client + * Returns : none +*******************************************************************************/ + +extern sint8 espconn_udp_client(struct espconn *pespconn); + +/****************************************************************************** + * FunctionName : espconn_udp_disconnect + * Description : A new incoming connection has been disconnected. + * Parameters : espconn -- the espconn used to disconnect with host + * Returns : none +*******************************************************************************/ + +extern void espconn_udp_disconnect(espconn_msg *pdiscon); + +/****************************************************************************** + * FunctionName : espconn_udp_server + * Description : Initialize the server: set up a PCB and bind it to the port + * Parameters : pespconn -- the espconn used to build server + * Returns : none +*******************************************************************************/ + +extern sint8 espconn_udp_server(struct espconn *espconn); + +/****************************************************************************** + * FunctionName : espconn_udp_sent + * Description : sent data for client or server + * Parameters : void *arg -- client or server to send + * uint8* psent -- Data to send + * uint16 length -- Length of data to send + * Returns : none +*******************************************************************************/ + +extern err_t espconn_udp_sent(void *arg, uint8 *psent, uint16 length); + +/****************************************************************************** + * FunctionName : espconn_udp_sendto + * Description : sent data for UDP + * Parameters : void *arg -- UDP to send + * uint8* psent -- Data to send + * uint16 length -- Length of data to send + * Returns : return espconn error code. + * - ESPCONN_OK. Successful. No error occured. + * - ESPCONN_MEM. Out of memory. + * - ESPCONN_RTE. Could not find route to destination address. + * - More errors could be returned by lower protocol layers. +*******************************************************************************/ +extern err_t espconn_udp_sendto(void *arg, uint8 *psent, uint16 length); + +#endif /* __ESPCONN_UDP_H__ */ + + diff --git a/tools/sdk/lwip/lwip/app/ping.h b/tools/sdk/lwip/lwip/app/ping.h new file mode 100755 index 000000000..21e26e910 --- /dev/null +++ b/tools/sdk/lwip/lwip/app/ping.h @@ -0,0 +1,85 @@ +#ifndef __PING_H__ +#define __PING_H__ +#include "lwip/ip_addr.h" +#include "lwip/icmp.h" +/** + * PING_USE_SOCKETS: Set to 1 to use sockets, otherwise the raw api is used + */ +#ifndef PING_USE_SOCKETS +#define PING_USE_SOCKETS LWIP_SOCKET +#endif + +/** + * PING_DEBUG: Enable debugging for PING. + */ +#ifndef PING_DEBUG +#define PING_DEBUG LWIP_DBG_OFF +#endif + +/** ping receive timeout - in milliseconds */ +#ifndef PING_RCV_TIMEO +#define PING_RCV_TIMEO 1000 +#endif + +/** ping delay - in milliseconds */ +#ifndef PING_COARSE +#define PING_COARSE 1000 +#endif + +/** ping identifier - must fit on a u16_t */ +#ifndef PING_ID +#define PING_ID 0xAFAF +#endif + +/** ping additional data size to include in the packet */ +#ifndef PING_DATA_SIZE +#define PING_DATA_SIZE 32 +#endif + +/** ping result action - no default action */ +#ifndef PING_RESULT +#define PING_RESULT(ping_ok) +#endif + +#define DEFAULT_PING_MAX_COUNT 4 +#define PING_TIMEOUT_MS 1000 + +typedef void (* ping_recv_function)(void* arg, void *pdata); +typedef void (* ping_sent_function)(void* arg, void *pdata); + +struct ping_option{ + uint32 count; + uint32 ip; + uint32 coarse_time; + ping_recv_function recv_function; + ping_sent_function sent_function; + void* reverse; +}; + +struct ping_msg{ + struct ping_option *ping_opt; + struct raw_pcb *ping_pcb; + uint32 ping_start; + uint32 ping_sent; + uint32 timeout_count; + uint32 max_count; + uint32 sent_count; + uint32 coarse_time; +}; + +struct ping_resp{ + uint32 total_count; + uint32 resp_time; + uint32 seqno; + uint32 timeout_count; + uint32 bytes; + uint32 total_bytes; + uint32 total_time; + sint8 ping_err; +}; + +bool ping_start(struct ping_option *ping_opt); +bool ping_regist_recv(struct ping_option *ping_opt, ping_recv_function ping_recv); +bool ping_regist_sent(struct ping_option *ping_opt, ping_sent_function ping_sent); + +#endif /* __PING_H__ */ diff --git a/libraries/ESP8266WiFi/src/lwip/arch.h b/tools/sdk/lwip/lwip/arch.h old mode 100644 new mode 100755 similarity index 100% rename from libraries/ESP8266WiFi/src/lwip/arch.h rename to tools/sdk/lwip/lwip/arch.h diff --git a/libraries/ESP8266WiFi/src/lwip/autoip.h b/tools/sdk/lwip/lwip/autoip.h old mode 100644 new mode 100755 similarity index 100% rename from libraries/ESP8266WiFi/src/lwip/autoip.h rename to tools/sdk/lwip/lwip/autoip.h diff --git a/libraries/ESP8266WiFi/src/lwip/debug.h b/tools/sdk/lwip/lwip/debug.h old mode 100644 new mode 100755 similarity index 100% rename from libraries/ESP8266WiFi/src/lwip/debug.h rename to tools/sdk/lwip/lwip/debug.h diff --git a/libraries/ESP8266WiFi/src/lwip/def.h b/tools/sdk/lwip/lwip/def.h old mode 100644 new mode 100755 similarity index 100% rename from libraries/ESP8266WiFi/src/lwip/def.h rename to tools/sdk/lwip/lwip/def.h diff --git a/libraries/ESP8266WiFi/src/lwip/dhcp.h b/tools/sdk/lwip/lwip/dhcp.h old mode 100644 new mode 100755 similarity index 96% rename from libraries/ESP8266WiFi/src/lwip/dhcp.h rename to tools/sdk/lwip/lwip/dhcp.h index ba21068cb..9e8fd0ec2 --- a/libraries/ESP8266WiFi/src/lwip/dhcp.h +++ b/tools/sdk/lwip/lwip/dhcp.h @@ -207,6 +207,15 @@ void dhcp_fine_tmr(void); #define DHCP_OPTION_TCP_TTL 37 #define DHCP_OPTION_END 255 +/**add options for support more router by liuHan**/ +#define DHCP_OPTION_DOMAIN_NAME 15 +#define DHCP_OPTION_PRD 31 +#define DHCP_OPTION_STATIC_ROUTER 33 +#define DHCP_OPTION_VSN 43 +#define DHCP_OPTION_NB_TINS 44 +#define DHCP_OPTION_NB_TINT 46 +#define DHCP_OPTION_NB_TIS 47 +#define DHCP_OPTION_CLASSLESS_STATIC_ROUTER 121 /** DHCP options */ #define DHCP_OPTION_REQUESTED_IP 50 /* RFC 2132 9.1, requested IP address */ #define DHCP_OPTION_LEASE_TIME 51 /* RFC 2132 9.2, time in seconds, in 4 bytes */ diff --git a/libraries/ESP8266WiFi/src/lwip/dns.h b/tools/sdk/lwip/lwip/dns.h old mode 100644 new mode 100755 similarity index 100% rename from libraries/ESP8266WiFi/src/lwip/dns.h rename to tools/sdk/lwip/lwip/dns.h diff --git a/libraries/ESP8266WiFi/src/lwip/err.h b/tools/sdk/lwip/lwip/err.h old mode 100644 new mode 100755 similarity index 100% rename from libraries/ESP8266WiFi/src/lwip/err.h rename to tools/sdk/lwip/lwip/err.h diff --git a/libraries/ESP8266WiFi/src/lwip/icmp.h b/tools/sdk/lwip/lwip/icmp.h old mode 100644 new mode 100755 similarity index 100% rename from libraries/ESP8266WiFi/src/lwip/icmp.h rename to tools/sdk/lwip/lwip/icmp.h diff --git a/libraries/ESP8266WiFi/src/lwip/igmp.h b/tools/sdk/lwip/lwip/igmp.h old mode 100644 new mode 100755 similarity index 99% rename from libraries/ESP8266WiFi/src/lwip/igmp.h rename to tools/sdk/lwip/lwip/igmp.h index 8cf9a4810..c90adcdce --- a/libraries/ESP8266WiFi/src/lwip/igmp.h +++ b/tools/sdk/lwip/lwip/igmp.h @@ -96,7 +96,7 @@ void igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest)ICACHE_FLAS err_t igmp_joingroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr)ICACHE_FLASH_ATTR; err_t igmp_leavegroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr)ICACHE_FLASH_ATTR; void igmp_tmr(void)ICACHE_FLASH_ATTR; -#define LWIP_RAND() rand() +#define LWIP_RAND() r_rand() #ifdef __cplusplus } #endif diff --git a/libraries/ESP8266WiFi/src/lwip/inet.h b/tools/sdk/lwip/lwip/inet.h old mode 100644 new mode 100755 similarity index 100% rename from libraries/ESP8266WiFi/src/lwip/inet.h rename to tools/sdk/lwip/lwip/inet.h diff --git a/libraries/ESP8266WiFi/src/lwip/inet_chksum.h b/tools/sdk/lwip/lwip/inet_chksum.h old mode 100644 new mode 100755 similarity index 100% rename from libraries/ESP8266WiFi/src/lwip/inet_chksum.h rename to tools/sdk/lwip/lwip/inet_chksum.h diff --git a/libraries/ESP8266WiFi/src/lwip/init.h b/tools/sdk/lwip/lwip/init.h old mode 100644 new mode 100755 similarity index 100% rename from libraries/ESP8266WiFi/src/lwip/init.h rename to tools/sdk/lwip/lwip/init.h diff --git a/libraries/ESP8266WiFi/src/lwip/ip.h b/tools/sdk/lwip/lwip/ip.h old mode 100644 new mode 100755 similarity index 100% rename from libraries/ESP8266WiFi/src/lwip/ip.h rename to tools/sdk/lwip/lwip/ip.h diff --git a/libraries/ESP8266WiFi/src/lwip/ip_addr.h b/tools/sdk/lwip/lwip/ip_addr.h old mode 100644 new mode 100755 similarity index 100% rename from libraries/ESP8266WiFi/src/lwip/ip_addr.h rename to tools/sdk/lwip/lwip/ip_addr.h diff --git a/libraries/ESP8266WiFi/src/lwip/ip_frag.h b/tools/sdk/lwip/lwip/ip_frag.h old mode 100644 new mode 100755 similarity index 100% rename from libraries/ESP8266WiFi/src/lwip/ip_frag.h rename to tools/sdk/lwip/lwip/ip_frag.h diff --git a/tools/sdk/lwip/lwip/mdns.h b/tools/sdk/lwip/lwip/mdns.h new file mode 100755 index 000000000..08db68a67 --- /dev/null +++ b/tools/sdk/lwip/lwip/mdns.h @@ -0,0 +1,114 @@ +/** + * lwip MDNS resolver header file. + * + * Created on: Jul 29, 2010 + * Author: Daniel Toma + + + * ported from uIP resolv.c Copyright (c) 2002-2003, Adam Dunkels. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __LWIP_DNS_H__ +#define __LWIP_DNS_H__ + +#include "lwip/opt.h" + +#if LWIP_MDNS /* don't build if not configured for use in lwipopts.h */ + +/** DNS timer period */ +#define DNS_TMR_INTERVAL 1000 + +/** mDNS Address offset flag*/ +#define DNS_OFFSET_FLAG 0xC0 /* the offset flag in the DNS message */ +#define DNS_DEFAULT_OFFSET 0x0C /* the offset is set at the beginning of the DNS message */ + +#define DNS_IP_ADDR_LEN 4 + + +/** DNS field TYPE used for "Resource Records" */ +#define DNS_RRTYPE_A 1 /* a host address */ +#define DNS_RRTYPE_NS 2 /* an authoritative name server */ +#define DNS_RRTYPE_MD 3 /* a mail destination (Obsolete - use MX) */ +#define DNS_RRTYPE_MF 4 /* a mail forwarder (Obsolete - use MX) */ +#define DNS_RRTYPE_CNAME 5 /* the canonical name for an alias */ +#define DNS_RRTYPE_SOA 6 /* marks the start of a zone of authority */ +#define DNS_RRTYPE_MB 7 /* a mailbox domain name (EXPERIMENTAL) */ +#define DNS_RRTYPE_MG 8 /* a mail group member (EXPERIMENTAL) */ +#define DNS_RRTYPE_MR 9 /* a mail rename domain name (EXPERIMENTAL) */ +#define DNS_RRTYPE_NULL 10 /* a null RR (EXPERIMENTAL) */ +#define DNS_RRTYPE_WKS 11 /* a well known service description */ +#define DNS_RRTYPE_PTR 12 /* a domain name pointer */ +#define DNS_RRTYPE_HINFO 13 /* host information */ +#define DNS_RRTYPE_MINFO 14 /* mailbox or mail list information */ +#define DNS_RRTYPE_MX 15 /* mail exchange */ +#define DNS_RRTYPE_TXT 16 /* text strings */ +#define DNS_RRTYPE_SRV 33 /* Service record */ +#define DNS_RRTYPE_OPT 41 /* EDNS0 OPT record */ +#define DNS_RRTYPE_TSIG 250 /* Transaction Signature */ +#define DNS_RRTYPE_ANY 255 /*Not a DNS type, but a DNS query type, meaning "all types"*/ + +/* DNS field CLASS used for "Resource Records" */ +#define DNS_RRCLASS_IN 1 /* the Internet */ +#define DNS_RRCLASS_CS 2 /* the CSNET class (Obsolete - used only for examples in some obsolete RFCs) */ +#define DNS_RRCLASS_CH 3 /* the CHAOS class */ +#define DNS_RRCLASS_HS 4 /* Hesiod [Dyer 87] */ +#define DNS_RRCLASS_FLUSH 0x800 /* Flush bit */ +#define DNS_RRCLASS_FLUSH_IN 0x8001/* Flush bit and Internet*/ + +/** Callback which is invoked when a hostname is found. + * A function of this type must be implemented by the application using the DNS resolver. + * @param name pointer to the name that was looked up. + * @param ipaddr pointer to a struct ip_addr containing the IP address of the hostname, + * or NULL if the name could not be found (or on any other error). + * @param callback_arg a user-specified callback argument passed to dns_gethostbyname +*/ +#ifndef _MDNS_INFO +#define _MDNS_INFO +struct mdns_info { + char *host_name; + char *server_name; + uint16 server_port; + unsigned long ipAddr; + char *txt_data[10]; +}; +#endif +//void mdns_enable(void); +//void mdns_disable(void); +//void mdns_init(struct mdns_info *info); +//void mdns_close(void); +//char* mdns_get_hostname(void); +//void mdns_set_hostname(char *name); +//void mdns_set_servername(const char *name); +//char* mdns_get_servername(void); +//void mdns_server_unregister(void); +//void mdns_server_register(void) ; +//void mdns_tmr(void); +//void Delay(unsigned long ulSeconds); + +#endif /* LWIP_DNS */ + +#endif /* __LWIP_DNS_H__ */ diff --git a/libraries/ESP8266WiFi/src/lwip/mem.h b/tools/sdk/lwip/lwip/mem.h old mode 100644 new mode 100755 similarity index 99% rename from libraries/ESP8266WiFi/src/lwip/mem.h rename to tools/sdk/lwip/lwip/mem.h index 1d768856c..3709bcdaa --- a/libraries/ESP8266WiFi/src/lwip/mem.h +++ b/tools/sdk/lwip/lwip/mem.h @@ -33,7 +33,7 @@ #define __LWIP_MEM_H__ #include "lwip/opt.h" -#include "mem_manager.h" +//#include "mem_manager.h" #ifdef __cplusplus extern "C" { diff --git a/libraries/ESP8266WiFi/src/lwip/memp.h b/tools/sdk/lwip/lwip/memp.h old mode 100644 new mode 100755 similarity index 98% rename from libraries/ESP8266WiFi/src/lwip/memp.h rename to tools/sdk/lwip/lwip/memp.h index 764dedb10..6a2127db2 --- a/libraries/ESP8266WiFi/src/lwip/memp.h +++ b/tools/sdk/lwip/lwip/memp.h @@ -76,7 +76,7 @@ typedef enum { #endif /* MEM_USE_POOLS */ #if MEMP_MEM_MALLOC || MEM_USE_POOLS -extern const u16_t memp_sizes[MEMP_MAX]; +extern const u32_t memp_sizes[MEMP_MAX]; #endif /* MEMP_MEM_MALLOC || MEM_USE_POOLS */ #if MEMP_MEM_MALLOC diff --git a/libraries/ESP8266WiFi/src/lwip/memp_std.h b/tools/sdk/lwip/lwip/memp_std.h old mode 100644 new mode 100755 similarity index 100% rename from libraries/ESP8266WiFi/src/lwip/memp_std.h rename to tools/sdk/lwip/lwip/memp_std.h diff --git a/libraries/ESP8266WiFi/src/lwip/netbuf.h b/tools/sdk/lwip/lwip/netbuf.h old mode 100644 new mode 100755 similarity index 100% rename from libraries/ESP8266WiFi/src/lwip/netbuf.h rename to tools/sdk/lwip/lwip/netbuf.h diff --git a/libraries/ESP8266WiFi/src/lwip/netdb.h b/tools/sdk/lwip/lwip/netdb.h old mode 100644 new mode 100755 similarity index 100% rename from libraries/ESP8266WiFi/src/lwip/netdb.h rename to tools/sdk/lwip/lwip/netdb.h diff --git a/libraries/ESP8266WiFi/src/lwip/netif.h b/tools/sdk/lwip/lwip/netif.h old mode 100644 new mode 100755 similarity index 98% rename from libraries/ESP8266WiFi/src/lwip/netif.h rename to tools/sdk/lwip/lwip/netif.h index 8bf137521..04e4f7a0a --- a/libraries/ESP8266WiFi/src/lwip/netif.h +++ b/tools/sdk/lwip/lwip/netif.h @@ -130,6 +130,9 @@ typedef void (*netif_status_callback_fn)(struct netif *netif); typedef err_t (*netif_igmp_mac_filter_fn)(struct netif *netif, ip_addr_t *group, u8_t action); +/*add DHCP event processing by LiuHan*/ +typedef void (*dhcp_event_fn)(void); + /** Generic data structure used for all lwIP network interfaces. * The following fields should be filled in by the initialization * function for the device driver: hwaddr_len, hwaddr[], mtu, flags */ @@ -169,6 +172,8 @@ struct netif { #if LWIP_DHCP /** the DHCP client state information for this netif */ struct dhcp *dhcp; + struct udp_pcb *dhcps_pcb; //dhcps + dhcp_event_fn dhcp_event; #endif /* LWIP_DHCP */ #if LWIP_AUTOIP /** the AutoIP client state information for this netif */ diff --git a/libraries/ESP8266WiFi/src/lwip/netifapi.h b/tools/sdk/lwip/lwip/netifapi.h old mode 100644 new mode 100755 similarity index 100% rename from libraries/ESP8266WiFi/src/lwip/netifapi.h rename to tools/sdk/lwip/lwip/netifapi.h diff --git a/libraries/ESP8266WiFi/src/lwip/opt.h b/tools/sdk/lwip/lwip/opt.h old mode 100644 new mode 100755 similarity index 99% rename from libraries/ESP8266WiFi/src/lwip/opt.h rename to tools/sdk/lwip/lwip/opt.h index 1a681c997..0d2b3fcc5 --- a/libraries/ESP8266WiFi/src/lwip/opt.h +++ b/tools/sdk/lwip/lwip/opt.h @@ -42,7 +42,7 @@ * Include user defined options first. Anything not defined in these files * will be set to standard values. Override anything you dont like! */ -#include "include/lwipopts.h" +#include "lwipopts.h" #include "lwip/debug.h" /* diff --git a/libraries/ESP8266WiFi/src/lwip/pbuf.h b/tools/sdk/lwip/lwip/pbuf.h old mode 100644 new mode 100755 similarity index 100% rename from libraries/ESP8266WiFi/src/lwip/pbuf.h rename to tools/sdk/lwip/lwip/pbuf.h diff --git a/tools/sdk/lwip/lwip/puck_def.h b/tools/sdk/lwip/lwip/puck_def.h new file mode 100755 index 000000000..c20027a15 --- /dev/null +++ b/tools/sdk/lwip/lwip/puck_def.h @@ -0,0 +1,44 @@ +/* + * puck_def.h + * + * Created on: Jul 22, 2010 + * Author: dtoma + */ + +#ifndef PUCK_DEF_H_ +#define PUCK_DEF_H_ + + + +#define INSTRUMENT_PORT 8760 + +#define INSTRUMENT_LENGTH 80 + +#define MDNS_NAME_LENGTH 68 //68 + +char* PUCK_SERVICE = NULL; +//#define PUCK_SERVICE "_Escpressif._tcp.local" +#define DNS_SD_SERVICE "_services._dns-sd._udp.local" +#define SERVICE_DESCRIPTION "PUCK PROTOCOL" +#define PUCK_SERVICE_LENGTH 30 + +#define UUID_LEN 16 +#define DS_VERS_LEN 2 +#define DS_SIZE_LEN 2 +#define MAN_ID_LEN 4 +#define MAN_MODEL_LEN 2 +#define MAN_VERS_LEN 2 +#define SER_NUM_LEN 4 +#define NAME_LEN 64 +#define PUCK_DATASHEET_SIZE 96 + +#define UUID_OFFSET 0 +#define DS_VERS_OFFSET UUID_LEN + UUID_OFFSET +#define DS_SIZE_OFFSET DS_VERS_LEN + DS_VERS_OFFSET +#define MAN_ID_OFFSET DS_SIZE_LEN + DS_SIZE_OFFSET +#define MAN_MODEL_OFFSET MAN_ID_LEN + MAN_ID_OFFSET +#define MAN_VERS_OFFSET MAN_MODEL_LEN + MAN_MODEL_OFFSET +#define SER_NUM_OFFSET MAN_VERS_LEN + MAN_VERS_OFFSET +#define NAME_OFFSET SER_NUM_LEN + SER_NUM_OFFSET + +#endif /* __PUCK_DEF_H__ */ diff --git a/libraries/ESP8266WiFi/src/lwip/raw.h b/tools/sdk/lwip/lwip/raw.h old mode 100644 new mode 100755 similarity index 100% rename from libraries/ESP8266WiFi/src/lwip/raw.h rename to tools/sdk/lwip/lwip/raw.h diff --git a/libraries/ESP8266WiFi/src/lwip/sio.h b/tools/sdk/lwip/lwip/sio.h old mode 100644 new mode 100755 similarity index 100% rename from libraries/ESP8266WiFi/src/lwip/sio.h rename to tools/sdk/lwip/lwip/sio.h diff --git a/libraries/ESP8266WiFi/src/lwip/snmp.h b/tools/sdk/lwip/lwip/snmp.h old mode 100644 new mode 100755 similarity index 100% rename from libraries/ESP8266WiFi/src/lwip/snmp.h rename to tools/sdk/lwip/lwip/snmp.h diff --git a/libraries/ESP8266WiFi/src/lwip/snmp_asn1.h b/tools/sdk/lwip/lwip/snmp_asn1.h old mode 100644 new mode 100755 similarity index 100% rename from libraries/ESP8266WiFi/src/lwip/snmp_asn1.h rename to tools/sdk/lwip/lwip/snmp_asn1.h diff --git a/libraries/ESP8266WiFi/src/lwip/snmp_msg.h b/tools/sdk/lwip/lwip/snmp_msg.h old mode 100644 new mode 100755 similarity index 100% rename from libraries/ESP8266WiFi/src/lwip/snmp_msg.h rename to tools/sdk/lwip/lwip/snmp_msg.h diff --git a/libraries/ESP8266WiFi/src/lwip/snmp_structs.h b/tools/sdk/lwip/lwip/snmp_structs.h old mode 100644 new mode 100755 similarity index 100% rename from libraries/ESP8266WiFi/src/lwip/snmp_structs.h rename to tools/sdk/lwip/lwip/snmp_structs.h diff --git a/tools/sdk/lwip/lwip/sntp.h b/tools/sdk/lwip/lwip/sntp.h new file mode 100755 index 000000000..14e802e15 --- /dev/null +++ b/tools/sdk/lwip/lwip/sntp.h @@ -0,0 +1,56 @@ +#ifndef LWIP_SNTP_H +#define LWIP_SNTP_H + +#include "lwip/opt.h" +#include "lwip/ip_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** The maximum number of SNTP servers that can be set */ +#ifndef SNTP_MAX_SERVERS +#define SNTP_MAX_SERVERS 3 +#endif + +/** Set this to 1 to implement the callback function called by dhcp when + * NTP servers are received. */ +#ifndef SNTP_GET_SERVERS_FROM_DHCP +#define SNTP_GET_SERVERS_FROM_DHCP 0//LWIP_DHCP_GET_NTP_SRV +#endif + +/* Set this to 1 to support DNS names (or IP address strings) to set sntp servers */ +#ifndef SNTP_SERVER_DNS +#define SNTP_SERVER_DNS 1 +#endif + +/** One server address/name can be defined as default if SNTP_SERVER_DNS == 1: + * #define SNTP_SERVER_ADDRESS "pool.ntp.org" + */ +uint32 sntp_get_current_timestamp(); +char* sntp_get_real_time(long t); + +void sntp_init(void); +void sntp_stop(void); + +sint8 sntp_get_timezone(void); +bool sntp_set_timezone(sint8 timezone); +void sntp_setserver(u8_t idx, ip_addr_t *addr); +ip_addr_t sntp_getserver(u8_t idx); + +#if SNTP_SERVER_DNS +void sntp_setservername(u8_t idx, char *server); +char *sntp_getservername(u8_t idx); +#endif /* SNTP_SERVER_DNS */ + +#if SNTP_GET_SERVERS_FROM_DHCP +void sntp_servermode_dhcp(int set_servers_from_dhcp); +#else /* SNTP_GET_SERVERS_FROM_DHCP */ +#define sntp_servermode_dhcp(x) +#endif /* SNTP_GET_SERVERS_FROM_DHCP */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_SNTP_H */ diff --git a/libraries/ESP8266WiFi/src/lwip/sockets.h b/tools/sdk/lwip/lwip/sockets.h old mode 100644 new mode 100755 similarity index 100% rename from libraries/ESP8266WiFi/src/lwip/sockets.h rename to tools/sdk/lwip/lwip/sockets.h diff --git a/libraries/ESP8266WiFi/src/lwip/stats.h b/tools/sdk/lwip/lwip/stats.h old mode 100644 new mode 100755 similarity index 100% rename from libraries/ESP8266WiFi/src/lwip/stats.h rename to tools/sdk/lwip/lwip/stats.h diff --git a/libraries/ESP8266WiFi/src/lwip/sys.h b/tools/sdk/lwip/lwip/sys.h old mode 100644 new mode 100755 similarity index 100% rename from libraries/ESP8266WiFi/src/lwip/sys.h rename to tools/sdk/lwip/lwip/sys.h diff --git a/libraries/ESP8266WiFi/src/lwip/tcp.h b/tools/sdk/lwip/lwip/tcp.h old mode 100644 new mode 100755 similarity index 100% rename from libraries/ESP8266WiFi/src/lwip/tcp.h rename to tools/sdk/lwip/lwip/tcp.h diff --git a/libraries/ESP8266WiFi/src/lwip/tcp_impl.h b/tools/sdk/lwip/lwip/tcp_impl.h old mode 100644 new mode 100755 similarity index 96% rename from libraries/ESP8266WiFi/src/lwip/tcp_impl.h rename to tools/sdk/lwip/lwip/tcp_impl.h index f906c0d1f..24ca8bb95 --- a/libraries/ESP8266WiFi/src/lwip/tcp_impl.h +++ b/tools/sdk/lwip/lwip/tcp_impl.h @@ -113,7 +113,7 @@ u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb)ICACHE_FLASH_ATTR; #define TCP_HLEN 20 #ifndef TCP_TMR_INTERVAL -#define TCP_TMR_INTERVAL 250 /* The TCP timer interval in milliseconds. */ +#define TCP_TMR_INTERVAL 125 /* The TCP timer interval in milliseconds. */ #endif /* TCP_TMR_INTERVAL */ #ifndef TCP_FAST_INTERVAL @@ -135,15 +135,15 @@ u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb)ICACHE_FLASH_ATTR; /* Keepalive values, compliant with RFC 1122. Don't change this unless you know what you're doing */ #ifndef TCP_KEEPIDLE_DEFAULT -#define TCP_KEEPIDLE_DEFAULT 3000UL /* Default KEEPALIVE timer in milliseconds */ +#define TCP_KEEPIDLE_DEFAULT 120000UL /* Default KEEPALIVE timer in milliseconds */ #endif #ifndef TCP_KEEPINTVL_DEFAULT -#define TCP_KEEPINTVL_DEFAULT 1000UL /* Default Time between KEEPALIVE probes in milliseconds */ +#define TCP_KEEPINTVL_DEFAULT 10000UL /* Default Time between KEEPALIVE probes in milliseconds */ #endif #ifndef TCP_KEEPCNT_DEFAULT -#define TCP_KEEPCNT_DEFAULT 3U /* Default Counter for KEEPALIVE probes */ +#define TCP_KEEPCNT_DEFAULT 9U /* Default Counter for KEEPALIVE probes */ #endif #define TCP_MAXIDLE TCP_KEEPCNT_DEFAULT * TCP_KEEPINTVL_DEFAULT /* Maximum KEEPALIVE probe time */ @@ -156,14 +156,14 @@ u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb)ICACHE_FLASH_ATTR; #endif PACK_STRUCT_BEGIN struct tcp_hdr { - PACK_STRUCT_FIELD(u16_t src); //Ô´¶Ë¿Ú - PACK_STRUCT_FIELD(u16_t dest); //Ä¿µÄ¶Ë¿Ú - PACK_STRUCT_FIELD(u32_t seqno); //ÐòºÅ - PACK_STRUCT_FIELD(u32_t ackno); //Ó¦´ðÐòºÅ - PACK_STRUCT_FIELD(u16_t _hdrlen_rsvd_flags);//Êײ¿³¤¶È+±£Áôλ+±ê־λ - PACK_STRUCT_FIELD(u16_t wnd); //´°¿Ú´óС - PACK_STRUCT_FIELD(u16_t chksum); //УÑéºÍ - PACK_STRUCT_FIELD(u16_t urgp); //½ô¼±Ö¸Õë + PACK_STRUCT_FIELD(u16_t src); //Դ�˿� + PACK_STRUCT_FIELD(u16_t dest); //Ŀ�Ķ˿� + PACK_STRUCT_FIELD(u32_t seqno); //��� + PACK_STRUCT_FIELD(u32_t ackno); //Ӧ����� + PACK_STRUCT_FIELD(u16_t _hdrlen_rsvd_flags);//�ײ�����+����λ+��־λ + PACK_STRUCT_FIELD(u16_t wnd); //���ڴ�С + PACK_STRUCT_FIELD(u16_t chksum); //��� + PACK_STRUCT_FIELD(u16_t urgp); //����ָ�� } PACK_STRUCT_STRUCT; PACK_STRUCT_END #ifdef PACK_STRUCT_USE_INCLUDES diff --git a/libraries/ESP8266WiFi/src/lwip/tcpip.h b/tools/sdk/lwip/lwip/tcpip.h old mode 100644 new mode 100755 similarity index 100% rename from libraries/ESP8266WiFi/src/lwip/tcpip.h rename to tools/sdk/lwip/lwip/tcpip.h diff --git a/libraries/ESP8266WiFi/src/lwip/timers.h b/tools/sdk/lwip/lwip/timers.h old mode 100644 new mode 100755 similarity index 100% rename from libraries/ESP8266WiFi/src/lwip/timers.h rename to tools/sdk/lwip/lwip/timers.h diff --git a/libraries/ESP8266WiFi/src/lwip/udp.h b/tools/sdk/lwip/lwip/udp.h old mode 100644 new mode 100755 similarity index 100% rename from libraries/ESP8266WiFi/src/lwip/udp.h rename to tools/sdk/lwip/lwip/udp.h diff --git a/libraries/ESP8266WiFi/src/include/lwipopts.h b/tools/sdk/lwip/lwipopts.h old mode 100644 new mode 100755 similarity index 97% rename from libraries/ESP8266WiFi/src/include/lwipopts.h rename to tools/sdk/lwip/lwipopts.h index 1678a4025..1ddd9ec5b --- a/libraries/ESP8266WiFi/src/include/lwipopts.h +++ b/tools/sdk/lwip/lwipopts.h @@ -38,10 +38,9 @@ #ifndef __LWIPOPTS_H__ #define __LWIPOPTS_H__ +#define PBUF_RSV_FOR_WLAN +#define EBUF_LWIP -#define EBUF_LWIP 1 -#define LWIP_ESP 1 -#define EP_OFFSET 36 /* ----------------------------------------------- ---------- Platform specific locking ---------- @@ -245,7 +244,7 @@ * (requires the LWIP_TCP option) */ #ifndef MEMP_NUM_TCP_PCB -#define MEMP_NUM_TCP_PCB (*((volatile uint32*)0x600011FC)) +#define MEMP_NUM_TCP_PCB (*(volatile uint32*)0x600011FC) #endif /** @@ -308,7 +307,7 @@ * (requires NO_SYS==0) */ #ifndef MEMP_NUM_SYS_TIMEOUT -#define MEMP_NUM_SYS_TIMEOUT 6 +#define MEMP_NUM_SYS_TIMEOUT 8 #endif /** @@ -534,7 +533,7 @@ * in this time, the whole packet is discarded. */ #ifndef IP_REASS_MAXAGE -#define IP_REASS_MAXAGE 0 +#define IP_REASS_MAXAGE 3 #endif /** @@ -544,7 +543,7 @@ * packets even if the maximum amount of fragments is enqueued for reassembly! */ #ifndef IP_REASS_MAX_PBUFS -#define IP_REASS_MAX_PBUFS 0 +#define IP_REASS_MAX_PBUFS 10 #endif /** @@ -633,7 +632,7 @@ * LWIP_RAW==1: Enable application layer to hook into the IP layer itself. */ #ifndef LWIP_RAW -#define LWIP_RAW 0 +#define LWIP_RAW 1 #endif /** @@ -662,6 +661,13 @@ #define DHCP_DOES_ARP_CHECK ((LWIP_DHCP) && (LWIP_ARP)) #endif +/** + * DHCP_MAXRTX: Maximum number of retries of current request. + */ +#ifndef DHCP_MAXRTX +#define DHCP_MAXRTX (*(volatile uint32*)0x600011E0) +#endif + /* ------------------------------------ ---------- AUTOIP options ---------- @@ -774,6 +780,17 @@ #ifndef LWIP_IGMP #define LWIP_IGMP 1 #endif +/* + ---------------------------------- + ---------- MDNS options ---------- + ---------------------------------- +*/ +/** + * LWIP_MDNS==1: Turn on MDNS module. + */ +#ifndef LWIP_MDNS +#define LWIP_MDNS 1 +#endif /* ---------------------------------- @@ -890,21 +907,35 @@ * (2 * TCP_MSS) for things to work well */ #ifndef TCP_WND -#define TCP_WND (4 * TCP_MSS) +#define TCP_WND (*(volatile uint32*)0x600011F0) #endif /** * TCP_MAXRTX: Maximum number of retransmissions of data segments. */ #ifndef TCP_MAXRTX -#define TCP_MAXRTX 5 +#define TCP_MAXRTX (*(volatile uint32*)0x600011E8) #endif /** * TCP_SYNMAXRTX: Maximum number of retransmissions of SYN segments. */ #ifndef TCP_SYNMAXRTX -#define TCP_SYNMAXRTX 5 +#define TCP_SYNMAXRTX (*(volatile uint32*)0x600011E4) +#endif + +/** + * TCP_MAXRTO: Maximum retransmission timeout of data segments. + */ +#ifndef TCP_MAXRTO +#define TCP_MAXRTO 10 +#endif + +/** + * TCP_MINRTO: Minimum retransmission timeout of data segments. + */ +#ifndef TCP_MINRTO +#define TCP_MINRTO 2 #endif /** @@ -912,9 +943,10 @@ * Define to 0 if your device is low on memory. */ #ifndef TCP_QUEUE_OOSEQ -#define TCP_QUEUE_OOSEQ 0 +#define TCP_QUEUE_OOSEQ 1 #endif +#if 1 /** * TCP_MSS: TCP Maximum segment size. (default is 536, a conservative default, * you might want to increase this.) @@ -925,6 +957,7 @@ #ifndef TCP_MSS #define TCP_MSS 1460 #endif +#endif /** * TCP_CALCULATE_EFF_SEND_MSS: "The maximum size of a segment that TCP really @@ -1070,7 +1103,7 @@ * field. */ #ifndef LWIP_NETIF_HOSTNAME -#define LWIP_NETIF_HOSTNAME 0 +#define LWIP_NETIF_HOSTNAME 1 #endif /** diff --git a/tools/sdk/lwip/netif/etharp.h b/tools/sdk/lwip/netif/etharp.h new file mode 100755 index 000000000..2092ab7a1 --- /dev/null +++ b/tools/sdk/lwip/netif/etharp.h @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * Copyright (c) 2003-2004 Leon Woestenberg + * Copyright (c) 2003-2004 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#ifndef __NETIF_ETHARP_H__ +#define __NETIF_ETHARP_H__ + +#include "lwip/opt.h" + +#if LWIP_ARP || LWIP_ETHERNET /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/ip.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef ETHARP_HWADDR_LEN +#define ETHARP_HWADDR_LEN 6 +#endif + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct eth_addr { + PACK_STRUCT_FIELD(u8_t addr[ETHARP_HWADDR_LEN]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** Ethernet header */ +struct eth_hdr { +#if ETH_PAD_SIZE + PACK_STRUCT_FIELD(u8_t padding[ETH_PAD_SIZE]); +#endif + PACK_STRUCT_FIELD(struct eth_addr dest); + PACK_STRUCT_FIELD(struct eth_addr src); + PACK_STRUCT_FIELD(u16_t type); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define SIZEOF_ETH_HDR (14 + ETH_PAD_SIZE) + +#if ETHARP_SUPPORT_VLAN + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** VLAN header inserted between ethernet header and payload + * if 'type' in ethernet header is ETHTYPE_VLAN. + * See IEEE802.Q */ +struct eth_vlan_hdr { + PACK_STRUCT_FIELD(u16_t tpid); + PACK_STRUCT_FIELD(u16_t prio_vid); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define SIZEOF_VLAN_HDR 4 +#define VLAN_ID(vlan_hdr) (htons((vlan_hdr)->prio_vid) & 0xFFF) + +#endif /* ETHARP_SUPPORT_VLAN */ + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** the ARP message, see RFC 826 ("Packet format") */ +struct etharp_hdr { + PACK_STRUCT_FIELD(u16_t hwtype); + PACK_STRUCT_FIELD(u16_t proto); + PACK_STRUCT_FIELD(u8_t hwlen); + PACK_STRUCT_FIELD(u8_t protolen); + PACK_STRUCT_FIELD(u16_t opcode); + PACK_STRUCT_FIELD(struct eth_addr shwaddr); + PACK_STRUCT_FIELD(struct ip_addr2 sipaddr); + PACK_STRUCT_FIELD(struct eth_addr dhwaddr); + PACK_STRUCT_FIELD(struct ip_addr2 dipaddr); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define SIZEOF_ETHARP_HDR 28 +#define SIZEOF_ETHARP_MINSIZE 46 +#define SIZEOF_ETHARP_PACKET (SIZEOF_ETH_HDR + SIZEOF_ETHARP_HDR) +#define SIZEOF_ETHARP_WITHPAD (SIZEOF_ETH_HDR + SIZEOF_ETHARP_MINSIZE) + +/** 5 seconds period */ +#define ARP_TMR_INTERVAL 5000 + +#define ETHTYPE_ARP 0x0806 +#define ETHTYPE_IP 0x0800 +#define ETHTYPE_VLAN 0x8100 +#define ETHTYPE_PPPOEDISC 0x8863 /* PPP Over Ethernet Discovery Stage */ +#define ETHTYPE_PPPOE 0x8864 /* PPP Over Ethernet Session Stage */ +#define ETHTYPE_PAE 0x888e + +/** MEMCPY-like macro to copy to/from struct eth_addr's that are local variables + * or known to be 32-bit aligned within the protocol header. */ +#ifndef ETHADDR32_COPY +#define ETHADDR32_COPY(src, dst) SMEMCPY(src, dst, ETHARP_HWADDR_LEN) +#endif + +/** MEMCPY-like macro to copy to/from struct eth_addr's that are no local + * variables and known to be 16-bit aligned within the protocol header. */ +#ifndef ETHADDR16_COPY +#define ETHADDR16_COPY(src, dst) SMEMCPY(src, dst, ETHARP_HWADDR_LEN) +#endif + +#if LWIP_ARP /* don't build if not configured for use in lwipopts.h */ + +/** ARP message types (opcodes) */ +#define ARP_REQUEST 1 +#define ARP_REPLY 2 + +/** Define this to 1 and define LWIP_ARP_FILTER_NETIF_FN(pbuf, netif, type) + * to a filter function that returns the correct netif when using multiple + * netifs on one hardware interface where the netif's low-level receive + * routine cannot decide for the correct netif (e.g. when mapping multiple + * IP addresses to one hardware interface). + */ +#ifndef LWIP_ARP_FILTER_NETIF +#define LWIP_ARP_FILTER_NETIF 0 +#endif + +#if ARP_QUEUEING +/** struct for queueing outgoing packets for unknown address + * defined here to be accessed by memp.h + */ +struct etharp_q_entry { + struct etharp_q_entry *next; + struct pbuf *p; +}; +#endif /* ARP_QUEUEING */ + +#define etharp_init() /* Compatibility define, not init needed. */ +void etharp_tmr(void)ICACHE_FLASH_ATTR; +s8_t etharp_find_addr(struct netif *netif, ip_addr_t *ipaddr, + struct eth_addr **eth_ret, ip_addr_t **ip_ret)ICACHE_FLASH_ATTR; +err_t etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr)ICACHE_FLASH_ATTR; +err_t etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q)ICACHE_FLASH_ATTR; +err_t etharp_request(struct netif *netif, ip_addr_t *ipaddr)ICACHE_FLASH_ATTR; +/** For Ethernet network interfaces, we might want to send "gratuitous ARP"; + * this is an ARP packet sent by a node in order to spontaneously cause other + * nodes to update an entry in their ARP cache. + * From RFC 3220 "IP Mobility Support for IPv4" section 4.6. */ +#define etharp_gratuitous(netif) etharp_request((netif), &(netif)->ip_addr) +void etharp_cleanup_netif(struct netif *netif); + +#if ETHARP_SUPPORT_STATIC_ENTRIES +err_t etharp_add_static_entry(ip_addr_t *ipaddr, struct eth_addr *ethaddr)ICACHE_FLASH_ATTR; +err_t etharp_remove_static_entry(ip_addr_t *ipaddr)ICACHE_FLASH_ATTR; +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + +#if LWIP_AUTOIP +err_t etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr, + const struct eth_addr *ethdst_addr, + const struct eth_addr *hwsrc_addr, const ip_addr_t *ipsrc_addr, + const struct eth_addr *hwdst_addr, const ip_addr_t *ipdst_addr, + const u16_t opcode)ICACHE_FLASH_ATTR; +#endif /* LWIP_AUTOIP */ + +#endif /* LWIP_ARP */ + +err_t ethernet_input(struct pbuf *p, struct netif *netif)ICACHE_FLASH_ATTR; + +#define eth_addr_cmp(addr1, addr2) (memcmp((addr1)->addr, (addr2)->addr, ETHARP_HWADDR_LEN) == 0) + +extern const struct eth_addr ethbroadcast, ethzero; + +#endif /* LWIP_ARP || LWIP_ETHERNET */ + +#if 0 +/** Ethernet header */ +#ifndef ETHARP_HWADDR_LEN +#define ETHARP_HWADDR_LEN 6 +#endif + + +struct eth_addr { + PACK_STRUCT_FIELD(u8_t addr[ETHARP_HWADDR_LEN]); +} PACK_STRUCT_STRUCT; + + +struct eth_hdr { +#if ETH_PAD_SIZE + PACK_STRUCT_FIELD(u8_t padding[ETH_PAD_SIZE]); +#endif + PACK_STRUCT_FIELD(struct eth_addr dest); + PACK_STRUCT_FIELD(struct eth_addr src); + PACK_STRUCT_FIELD(u16_t type); +} PACK_STRUCT_STRUCT; + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define SIZEOF_ETH_HDR (14 + ETH_PAD_SIZE) + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __NETIF_ARP_H__ */ diff --git a/tools/sdk/lwip/netif/if_llc.h b/tools/sdk/lwip/netif/if_llc.h new file mode 100755 index 000000000..ca09b3868 --- /dev/null +++ b/tools/sdk/lwip/netif/if_llc.h @@ -0,0 +1,173 @@ +/* $NetBSD: if_llc.h,v 1.12 1999/11/19 20:41:19 thorpej Exp $ */ + +/*- + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)if_llc.h 8.1 (Berkeley) 6/10/93 + * $FreeBSD$ + */ + +#ifndef _NET_IF_LLC_H_ +#define _NET_IF_LLC_H_ + +/* + * IEEE 802.2 Link Level Control headers, for use in conjunction with + * 802.{3,4,5} media access control methods. + * + * Headers here do not use bit fields due to shortcommings in many + * compilers. + */ + +struct llc { + uint8_t llc_dsap; + uint8_t llc_ssap; + union { + struct { + uint8_t control; + uint8_t format_id; + uint8_t class; + uint8_t window_x2; + } __packed type_u; + struct { + uint8_t num_snd_x2; + uint8_t num_rcv_x2; + } __packed type_i; + struct { + uint8_t control; + uint8_t num_rcv_x2; + } __packed type_s; + struct { + uint8_t control; + /* + * We cannot put the following fields in a structure because + * the structure rounding might cause padding. + */ + uint8_t frmr_rej_pdu0; + uint8_t frmr_rej_pdu1; + uint8_t frmr_control; + uint8_t frmr_control_ext; + uint8_t frmr_cause; + } __packed type_frmr; + struct { + uint8_t control; + uint8_t org_code[3]; + uint16_t ether_type; + } __packed type_snap; + struct { + uint8_t control; + uint8_t control_ext; + } __packed type_raw; + } __packed llc_un; +} __packed; + +struct frmrinfo { + uint8_t frmr_rej_pdu0; + uint8_t frmr_rej_pdu1; + uint8_t frmr_control; + uint8_t frmr_control_ext; + uint8_t frmr_cause; +} __packed; + +#define llc_control llc_un.type_u.control +#define llc_control_ext llc_un.type_raw.control_ext +#define llc_fid llc_un.type_u.format_id +#define llc_class llc_un.type_u.class +#define llc_window llc_un.type_u.window_x2 +#define llc_frmrinfo llc_un.type_frmr.frmr_rej_pdu0 +#define llc_frmr_pdu0 llc_un.type_frmr.frmr_rej_pdu0 +#define llc_frmr_pdu1 llc_un.type_frmr.frmr_rej_pdu1 +#define llc_frmr_control llc_un.type_frmr.frmr_control +#define llc_frmr_control_ext llc_un.type_frmr.frmr_control_ext +#define llc_frmr_cause llc_un.type_frmr.frmr_cause +#define llc_snap llc_un.type_snap + +/* + * Don't use sizeof(struct llc_un) for LLC header sizes + */ +#define LLC_ISFRAMELEN 4 +#define LLC_UFRAMELEN 3 +#define LLC_FRMRLEN 7 +#define LLC_SNAPFRAMELEN 8 + +#ifdef CTASSERT +CTASSERT(sizeof (struct llc) == LLC_SNAPFRAMELEN); +#endif + +/* + * Unnumbered LLC format commands + */ +#define LLC_UI 0x3 +#define LLC_UI_P 0x13 +#define LLC_DISC 0x43 +#define LLC_DISC_P 0x53 +#define LLC_UA 0x63 +#define LLC_UA_P 0x73 +#define LLC_TEST 0xe3 +#define LLC_TEST_P 0xf3 +#define LLC_FRMR 0x87 +#define LLC_FRMR_P 0x97 +#define LLC_DM 0x0f +#define LLC_DM_P 0x1f +#define LLC_XID 0xaf +#define LLC_XID_P 0xbf +#define LLC_SABME 0x6f +#define LLC_SABME_P 0x7f + +/* + * Supervisory LLC commands + */ +#define LLC_RR 0x01 +#define LLC_RNR 0x05 +#define LLC_REJ 0x09 + +/* + * Info format - dummy only + */ +#define LLC_INFO 0x00 + +/* + * ISO PDTR 10178 contains among others + */ +#define LLC_8021D_LSAP 0x42 +#define LLC_X25_LSAP 0x7e +#define LLC_SNAP_LSAP 0xaa +#define LLC_ISO_LSAP 0xfe + +#define RFC1042_LEN 6 +#define RFC1042 {0xAA, 0xAA, 0x03, 0x00, 0x00, 0x00} +#define ETHERNET_TUNNEL {0xAA, 0xAA, 0x03, 0x00, 0x00, 0xF8} + +/* + * copied from sys/net/ethernet.h + */ +#define ETHERTYPE_AARP 0x80F3 /* AppleTalk AARP */ +#define ETHERTYPE_IPX 0x8137 /* Novell (old) NetWare IPX (ECONFIG E option) */ + + + +#endif /* _NET_IF_LLC_H_ */ diff --git a/tools/sdk/lwip/netif/ppp_oe.h b/tools/sdk/lwip/netif/ppp_oe.h new file mode 100755 index 000000000..e1cdfa519 --- /dev/null +++ b/tools/sdk/lwip/netif/ppp_oe.h @@ -0,0 +1,190 @@ +/***************************************************************************** +* ppp_oe.h - PPP Over Ethernet implementation for lwIP. +* +* Copyright (c) 2006 by Marc Boucher, Services Informatiques (MBSI) inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 06-01-01 Marc Boucher +* Ported to lwIP. +*****************************************************************************/ + + + +/* based on NetBSD: if_pppoe.c,v 1.64 2006/01/31 23:50:15 martin Exp */ + +/*- + * Copyright (c) 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Martin Husemann . + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef PPP_OE_H +#define PPP_OE_H + +#include "lwip/opt.h" + +#if PPPOE_SUPPORT > 0 + +#include "netif/etharp.h" + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct pppoehdr { + PACK_STRUCT_FIELD(u8_t vertype); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t session); + PACK_STRUCT_FIELD(u16_t plen); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct pppoetag { + PACK_STRUCT_FIELD(u16_t tag); + PACK_STRUCT_FIELD(u16_t len); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + + +#define PPPOE_STATE_INITIAL 0 +#define PPPOE_STATE_PADI_SENT 1 +#define PPPOE_STATE_PADR_SENT 2 +#define PPPOE_STATE_SESSION 3 +#define PPPOE_STATE_CLOSING 4 +/* passive */ +#define PPPOE_STATE_PADO_SENT 1 + +#define PPPOE_HEADERLEN sizeof(struct pppoehdr) +#define PPPOE_VERTYPE 0x11 /* VER=1, TYPE = 1 */ + +#define PPPOE_TAG_EOL 0x0000 /* end of list */ +#define PPPOE_TAG_SNAME 0x0101 /* service name */ +#define PPPOE_TAG_ACNAME 0x0102 /* access concentrator name */ +#define PPPOE_TAG_HUNIQUE 0x0103 /* host unique */ +#define PPPOE_TAG_ACCOOKIE 0x0104 /* AC cookie */ +#define PPPOE_TAG_VENDOR 0x0105 /* vendor specific */ +#define PPPOE_TAG_RELAYSID 0x0110 /* relay session id */ +#define PPPOE_TAG_SNAME_ERR 0x0201 /* service name error */ +#define PPPOE_TAG_ACSYS_ERR 0x0202 /* AC system error */ +#define PPPOE_TAG_GENERIC_ERR 0x0203 /* gerneric error */ + +#define PPPOE_CODE_PADI 0x09 /* Active Discovery Initiation */ +#define PPPOE_CODE_PADO 0x07 /* Active Discovery Offer */ +#define PPPOE_CODE_PADR 0x19 /* Active Discovery Request */ +#define PPPOE_CODE_PADS 0x65 /* Active Discovery Session confirmation */ +#define PPPOE_CODE_PADT 0xA7 /* Active Discovery Terminate */ + +#ifndef ETHERMTU +#define ETHERMTU 1500 +#endif + +/* two byte PPP protocol discriminator, then IP data */ +#define PPPOE_MAXMTU (ETHERMTU-PPPOE_HEADERLEN-2) + +#ifndef PPPOE_MAX_AC_COOKIE_LEN +#define PPPOE_MAX_AC_COOKIE_LEN 64 +#endif + +struct pppoe_softc { + struct pppoe_softc *next; + struct netif *sc_ethif; /* ethernet interface we are using */ + int sc_pd; /* ppp unit number */ + void (*sc_linkStatusCB)(int pd, int up); + + int sc_state; /* discovery phase or session connected */ + struct eth_addr sc_dest; /* hardware address of concentrator */ + u16_t sc_session; /* PPPoE session id */ + +#ifdef PPPOE_TODO + char *sc_service_name; /* if != NULL: requested name of service */ + char *sc_concentrator_name; /* if != NULL: requested concentrator id */ +#endif /* PPPOE_TODO */ + u8_t sc_ac_cookie[PPPOE_MAX_AC_COOKIE_LEN]; /* content of AC cookie we must echo back */ + size_t sc_ac_cookie_len; /* length of cookie data */ +#ifdef PPPOE_SERVER + u8_t *sc_hunique; /* content of host unique we must echo back */ + size_t sc_hunique_len; /* length of host unique */ +#endif + int sc_padi_retried; /* number of PADI retries already done */ + int sc_padr_retried; /* number of PADR retries already done */ +}; + + +#define pppoe_init() /* compatibility define, no initialization needed */ + +err_t pppoe_create(struct netif *ethif, int pd, void (*linkStatusCB)(int pd, int up), struct pppoe_softc **scptr); +err_t pppoe_destroy(struct netif *ifp); + +int pppoe_connect(struct pppoe_softc *sc); +void pppoe_disconnect(struct pppoe_softc *sc); + +void pppoe_disc_input(struct netif *netif, struct pbuf *p); +void pppoe_data_input(struct netif *netif, struct pbuf *p); + +err_t pppoe_xmit(struct pppoe_softc *sc, struct pbuf *pb); + +/** used in ppp.c */ +#define PPPOE_HDRLEN (sizeof(struct eth_hdr) + PPPOE_HEADERLEN) + +#endif /* PPPOE_SUPPORT */ + +#endif /* PPP_OE_H */ diff --git a/tools/sdk/lwip/netif/wlan_lwip_if.h b/tools/sdk/lwip/netif/wlan_lwip_if.h new file mode 100755 index 000000000..ed9c47756 --- /dev/null +++ b/tools/sdk/lwip/netif/wlan_lwip_if.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2010-2011 Espressif System + * +*/ + +#ifndef _WLAN_LWIP_IF_H_ +#define _WLAN_LWIP_IF_H_ + +#define LWIP_IF0_PRIO 28 +#define LWIP_IF1_PRIO 29 + +enum { + SIG_LWIP_RX = 0, +}; + +struct netif * eagle_lwip_if_alloc(struct ieee80211_conn *conn, const uint8 *macaddr, struct ip_info *info); +struct netif * eagle_lwip_getif(uint8 index); + +#ifndef IOT_SIP_MODE +sint8 ieee80211_output_pbuf(struct netif *ifp, struct pbuf* pb); +#else +sint8 ieee80211_output_pbuf(struct ieee80211_conn *conn, esf_buf *eb); +#endif + +#endif /* _WLAN_LWIP_IF_H_ */ diff --git a/variants/lwip/common.h b/variants/lwip/common.h new file mode 100644 index 000000000..c0029a70a --- /dev/null +++ b/variants/lwip/common.h @@ -0,0 +1,67 @@ +/* + common.h - Commoon pin definition functions for ESP8266 boards + Part of Arduino - http://www.arduino.cc/ + + Copyright (c) 2007 David A. Mellis + Modified for ESP8266 platform by Ivan Grokhotkov, 2014-2016. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + $Id: wiring.h 249 2007-02-03 16:52:51Z mellis $ +*/ + +#ifndef GENERIC_COMMON_H +#define GENERIC_COMMON_H + +#define EXTERNAL_NUM_INTERRUPTS 16 +#define NUM_DIGITAL_PINS 17 +#define NUM_ANALOG_INPUTS 1 + +// TODO: this should be <= 9 if flash is in DIO mode +#define isFlashInterfacePin(p) ((p) >= 6 && (p) <= 11) + +#define analogInputToDigitalPin(p) ((p > 0) ? NOT_A_PIN : 0) +#define digitalPinToInterrupt(p) (((p) < EXTERNAL_NUM_INTERRUPTS)? (p) : NOT_AN_INTERRUPT) +#define digitalPinHasPWM(p) (((p) < NUM_DIGITAL_PINS && !isFlashInterfacePin(p))? 1 : 0) + +static const uint8_t SS = 15; +static const uint8_t MOSI = 13; +static const uint8_t MISO = 12; +static const uint8_t SCK = 14; + +static const uint8_t A0 = 17; + +// These serial port names are intended to allow libraries and architecture-neutral +// sketches to automatically default to the correct port name for a particular type +// of use. For example, a GPS module would normally connect to SERIAL_PORT_HARDWARE_OPEN, +// the first hardware serial port whose RX/TX pins are not dedicated to another use. +// +// SERIAL_PORT_MONITOR Port which normally prints to the Arduino Serial Monitor +// +// SERIAL_PORT_USBVIRTUAL Port which is USB virtual serial +// +// SERIAL_PORT_LINUXBRIDGE Port which connects to a Linux system via Bridge library +// +// SERIAL_PORT_HARDWARE Hardware serial port, physical RX & TX pins. +// +// SERIAL_PORT_HARDWARE_OPEN Hardware serial ports which are open for use. Their RX & TX +// pins are NOT connected to anything by default. +#define SERIAL_PORT_MONITOR Serial +#define SERIAL_PORT_USBVIRTUAL Serial +#define SERIAL_PORT_HARDWARE Serial +#define SERIAL_PORT_HARDWARE_OPEN Serial1 + +#endif /* GENERIC_COMMON_H */ diff --git a/variants/lwip/lwip/Makefile b/variants/lwip/lwip/Makefile new file mode 100644 index 000000000..d894b7b02 --- /dev/null +++ b/variants/lwip/lwip/Makefile @@ -0,0 +1,36 @@ +TOOLS_PATH = $(abspath ../../../tools) +BUILD_PATH = $(abspath build) +LWIP_INCLUDE = -Ibuild -I$(TOOLS_PATH)/sdk/include -I$(TOOLS_PATH)/sdk/lwip +LWIP_SRCS = $(patsubst %.c,$(BUILD_PATH)/%.o,$(wildcard */*.c)) $(patsubst %.c,$(BUILD_PATH)/%.o,$(wildcard */*/*.c)) +LWIP_LIB = $(abspath liblwip_gcc.a) + +BUILD_FLAGS = -c -Os -g -Wpointer-arith -Wno-implicit-function-declaration -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals -falign-functions=4 -MMD -std=gnu99 -ffunction-sections -fdata-sections +BUILD_DEFINES = -D__ets__ -DICACHE_FLASH -U__STRICT_ANSI__ -DLWIP_OPEN_SRC + +CC=$(TOOLS_PATH)/xtensa-lx106-elf/bin/xtensa-lx106-elf-gcc +AR=$(TOOLS_PATH)/xtensa-lx106-elf/bin/xtensa-lx106-elf-ar + +$(BUILD_PATH)/%.h: + @echo "[CR]" $(notdir $@) + @mkdir -p $(dir $@) + @touch $@ + +$(BUILD_PATH)/%.o: %.c + @echo "[CC]" $(notdir $@) + @mkdir -p $(dir $@) + @$(CC) $(BUILD_FLAGS) $(BUILD_DEFINES) $(LWIP_INCLUDE) $< -o $@ + +$(LWIP_LIB): $(BUILD_PATH)/user_config.h $(LWIP_SRCS) + @echo "[AR]" $(notdir $(LWIP_LIB)) + @$(AR) cru $(LWIP_LIB) $(LWIP_SRCS) + +all: $(LWIP_LIB) + +install: all + @echo Installing $(notdir $(LWIP_LIB)) to $(TOOLS_PATH)/sdk/lib + @cp -f $(LWIP_LIB) $(TOOLS_PATH)/sdk/lib/$(notdir $(LWIP_LIB)) + +deploy: install clean + +clean: + @rm -rf $(BUILD_PATH) $(LWIP_LIB) diff --git a/variants/lwip/lwip/api/api_lib.c b/variants/lwip/lwip/api/api_lib.c new file mode 100755 index 000000000..158325b09 --- /dev/null +++ b/variants/lwip/lwip/api/api_lib.c @@ -0,0 +1,740 @@ +/** + * @file + * Sequential API External module + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +/* This is the part of the API that is linked with + the application */ + +#include "lwip/opt.h" + +#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/api.h" +#include "lwip/tcpip.h" +#include "lwip/memp.h" + +#include "lwip/ip.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/tcp.h" + +#include + +/** + * Create a new netconn (of a specific type) that has a callback function. + * The corresponding pcb is also created. + * + * @param t the type of 'connection' to create (@see enum netconn_type) + * @param proto the IP protocol for RAW IP pcbs + * @param callback a function to call on status changes (RX available, TX'ed) + * @return a newly allocated struct netconn or + * NULL on memory error + */ +struct netconn* +netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_callback callback) +{ + struct netconn *conn; + struct api_msg msg; + + conn = netconn_alloc(t, callback); + if (conn != NULL) { + msg.function = do_newconn; + msg.msg.msg.n.proto = proto; + msg.msg.conn = conn; + if (TCPIP_APIMSG(&msg) != ERR_OK) { + LWIP_ASSERT("freeing conn without freeing pcb", conn->pcb.tcp == NULL); + LWIP_ASSERT("conn has no op_completed", sys_sem_valid(&conn->op_completed)); + LWIP_ASSERT("conn has no recvmbox", sys_mbox_valid(&conn->recvmbox)); +#if LWIP_TCP + LWIP_ASSERT("conn->acceptmbox shouldn't exist", !sys_mbox_valid(&conn->acceptmbox)); +#endif /* LWIP_TCP */ + sys_sem_free(&conn->op_completed); + sys_mbox_free(&conn->recvmbox); + memp_free(MEMP_NETCONN, conn); + return NULL; + } + } + return conn; +} + +/** + * Close a netconn 'connection' and free its resources. + * UDP and RAW connection are completely closed, TCP pcbs might still be in a waitstate + * after this returns. + * + * @param conn the netconn to delete + * @return ERR_OK if the connection was deleted + */ +err_t +netconn_delete(struct netconn *conn) +{ + struct api_msg msg; + + /* No ASSERT here because possible to get a (conn == NULL) if we got an accept error */ + if (conn == NULL) { + return ERR_OK; + } + + msg.function = do_delconn; + msg.msg.conn = conn; + tcpip_apimsg(&msg); + + netconn_free(conn); + + /* don't care for return value of do_delconn since it only calls void functions */ + + return ERR_OK; +} + +/** + * Get the local or remote IP address and port of a netconn. + * For RAW netconns, this returns the protocol instead of a port! + * + * @param conn the netconn to query + * @param addr a pointer to which to save the IP address + * @param port a pointer to which to save the port (or protocol for RAW) + * @param local 1 to get the local IP address, 0 to get the remote one + * @return ERR_CONN for invalid connections + * ERR_OK if the information was retrieved + */ +err_t +netconn_getaddr(struct netconn *conn, ip_addr_t *addr, u16_t *port, u8_t local) +{ + struct api_msg msg; + err_t err; + + LWIP_ERROR("netconn_getaddr: invalid conn", (conn != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_getaddr: invalid addr", (addr != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_getaddr: invalid port", (port != NULL), return ERR_ARG;); + + msg.function = do_getaddr; + msg.msg.conn = conn; + msg.msg.msg.ad.ipaddr = addr; + msg.msg.msg.ad.port = port; + msg.msg.msg.ad.local = local; + err = TCPIP_APIMSG(&msg); + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +} + +/** + * Bind a netconn to a specific local IP address and port. + * Binding one netconn twice might not always be checked correctly! + * + * @param conn the netconn to bind + * @param addr the local IP address to bind the netconn to (use IP_ADDR_ANY + * to bind to all addresses) + * @param port the local port to bind the netconn to (not used for RAW) + * @return ERR_OK if bound, any other err_t on failure + */ +err_t +netconn_bind(struct netconn *conn, ip_addr_t *addr, u16_t port) +{ + struct api_msg msg; + err_t err; + + LWIP_ERROR("netconn_bind: invalid conn", (conn != NULL), return ERR_ARG;); + + msg.function = do_bind; + msg.msg.conn = conn; + msg.msg.msg.bc.ipaddr = addr; + msg.msg.msg.bc.port = port; + err = TCPIP_APIMSG(&msg); + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +} + +/** + * Connect a netconn to a specific remote IP address and port. + * + * @param conn the netconn to connect + * @param addr the remote IP address to connect to + * @param port the remote port to connect to (no used for RAW) + * @return ERR_OK if connected, return value of tcp_/udp_/raw_connect otherwise + */ +err_t +netconn_connect(struct netconn *conn, ip_addr_t *addr, u16_t port) +{ + struct api_msg msg; + err_t err; + + LWIP_ERROR("netconn_connect: invalid conn", (conn != NULL), return ERR_ARG;); + + msg.function = do_connect; + msg.msg.conn = conn; + msg.msg.msg.bc.ipaddr = addr; + msg.msg.msg.bc.port = port; + /* This is the only function which need to not block tcpip_thread */ + err = tcpip_apimsg(&msg); + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +} + +/** + * Disconnect a netconn from its current peer (only valid for UDP netconns). + * + * @param conn the netconn to disconnect + * @return TODO: return value is not set here... + */ +err_t +netconn_disconnect(struct netconn *conn) +{ + struct api_msg msg; + err_t err; + + LWIP_ERROR("netconn_disconnect: invalid conn", (conn != NULL), return ERR_ARG;); + + msg.function = do_disconnect; + msg.msg.conn = conn; + err = TCPIP_APIMSG(&msg); + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +} + +/** + * Set a TCP netconn into listen mode + * + * @param conn the tcp netconn to set to listen mode + * @param backlog the listen backlog, only used if TCP_LISTEN_BACKLOG==1 + * @return ERR_OK if the netconn was set to listen (UDP and RAW netconns + * don't return any error (yet?)) + */ +err_t +netconn_listen_with_backlog(struct netconn *conn, u8_t backlog) +{ +#if LWIP_TCP + struct api_msg msg; + err_t err; + + /* This does no harm. If TCP_LISTEN_BACKLOG is off, backlog is unused. */ + LWIP_UNUSED_ARG(backlog); + + LWIP_ERROR("netconn_listen: invalid conn", (conn != NULL), return ERR_ARG;); + + msg.function = do_listen; + msg.msg.conn = conn; +#if TCP_LISTEN_BACKLOG + msg.msg.msg.lb.backlog = backlog; +#endif /* TCP_LISTEN_BACKLOG */ + err = TCPIP_APIMSG(&msg); + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +#else /* LWIP_TCP */ + LWIP_UNUSED_ARG(conn); + LWIP_UNUSED_ARG(backlog); + return ERR_ARG; +#endif /* LWIP_TCP */ +} + +/** + * Accept a new connection on a TCP listening netconn. + * + * @param conn the TCP listen netconn + * @param new_conn pointer where the new connection is stored + * @return ERR_OK if a new connection has been received or an error + * code otherwise + */ +err_t +netconn_accept(struct netconn *conn, struct netconn **new_conn) +{ +#if LWIP_TCP + struct netconn *newconn; + err_t err; +#if TCP_LISTEN_BACKLOG + struct api_msg msg; +#endif /* TCP_LISTEN_BACKLOG */ + + LWIP_ERROR("netconn_accept: invalid pointer", (new_conn != NULL), return ERR_ARG;); + *new_conn = NULL; + LWIP_ERROR("netconn_accept: invalid conn", (conn != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_accept: invalid acceptmbox", sys_mbox_valid(&conn->acceptmbox), return ERR_ARG;); + + err = conn->last_err; + if (ERR_IS_FATAL(err)) { + /* don't recv on fatal errors: this might block the application task + waiting on acceptmbox forever! */ + return err; + } + +#if LWIP_SO_RCVTIMEO + if (sys_arch_mbox_fetch(&conn->acceptmbox, (void **)&newconn, conn->recv_timeout) == SYS_ARCH_TIMEOUT) { + NETCONN_SET_SAFE_ERR(conn, ERR_TIMEOUT); + return ERR_TIMEOUT; + } +#else + sys_arch_mbox_fetch(&conn->acceptmbox, (void **)&newconn, 0); +#endif /* LWIP_SO_RCVTIMEO*/ + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0); + + if (newconn == NULL) { + /* connection has been closed */ + NETCONN_SET_SAFE_ERR(conn, ERR_CLSD); + return ERR_CLSD; + } +#if TCP_LISTEN_BACKLOG + /* Let the stack know that we have accepted the connection. */ + msg.function = do_recv; + msg.msg.conn = conn; + /* don't care for the return value of do_recv */ + TCPIP_APIMSG(&msg); +#endif /* TCP_LISTEN_BACKLOG */ + + *new_conn = newconn; + /* don't set conn->last_err: it's only ERR_OK, anyway */ + return ERR_OK; +#else /* LWIP_TCP */ + LWIP_UNUSED_ARG(conn); + LWIP_UNUSED_ARG(new_conn); + return ERR_ARG; +#endif /* LWIP_TCP */ +} + +/** + * Receive data: actual implementation that doesn't care whether pbuf or netbuf + * is received + * + * @param conn the netconn from which to receive data + * @param new_buf pointer where a new pbuf/netbuf is stored when received data + * @return ERR_OK if data has been received, an error code otherwise (timeout, + * memory error or another error) + */ +static err_t +netconn_recv_data(struct netconn *conn, void **new_buf) +{ + void *buf = NULL; + u16_t len; + err_t err; +#if LWIP_TCP + struct api_msg msg; +#endif /* LWIP_TCP */ + + LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;); + *new_buf = NULL; + LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_accept: invalid recvmbox", sys_mbox_valid(&conn->recvmbox), return ERR_CONN;); + + err = conn->last_err; + if (ERR_IS_FATAL(err)) { + /* don't recv on fatal errors: this might block the application task + waiting on recvmbox forever! */ + /* @todo: this does not allow us to fetch data that has been put into recvmbox + before the fatal error occurred - is that a problem? */ + return err; + } + +#if LWIP_SO_RCVTIMEO + if (sys_arch_mbox_fetch(&conn->recvmbox, &buf, conn->recv_timeout) == SYS_ARCH_TIMEOUT) { + NETCONN_SET_SAFE_ERR(conn, ERR_TIMEOUT); + return ERR_TIMEOUT; + } +#else + sys_arch_mbox_fetch(&conn->recvmbox, &buf, 0); +#endif /* LWIP_SO_RCVTIMEO*/ + +#if LWIP_TCP + if (conn->type == NETCONN_TCP) { + if (!netconn_get_noautorecved(conn) || (buf == NULL)) { + /* Let the stack know that we have taken the data. */ + /* TODO: Speedup: Don't block and wait for the answer here + (to prevent multiple thread-switches). */ + msg.function = do_recv; + msg.msg.conn = conn; + if (buf != NULL) { + msg.msg.msg.r.len = ((struct pbuf *)buf)->tot_len; + } else { + msg.msg.msg.r.len = 1; + } + /* don't care for the return value of do_recv */ + TCPIP_APIMSG(&msg); + } + + /* If we are closed, we indicate that we no longer wish to use the socket */ + if (buf == NULL) { + API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0); + /* Avoid to lose any previous error code */ + NETCONN_SET_SAFE_ERR(conn, ERR_CLSD); + return ERR_CLSD; + } + len = ((struct pbuf *)buf)->tot_len; + } +#endif /* LWIP_TCP */ +#if LWIP_TCP && (LWIP_UDP || LWIP_RAW) + else +#endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */ +#if (LWIP_UDP || LWIP_RAW) + { + LWIP_ASSERT("buf != NULL", buf != NULL); + len = netbuf_len((struct netbuf *)buf); + } +#endif /* (LWIP_UDP || LWIP_RAW) */ + +#if LWIP_SO_RCVBUF + SYS_ARCH_DEC(conn->recv_avail, len); +#endif /* LWIP_SO_RCVBUF */ + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVMINUS, len); + + LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_recv_data: received %p, len=%"U16_F"\n", buf, len)); + + *new_buf = buf; + /* don't set conn->last_err: it's only ERR_OK, anyway */ + return ERR_OK; +} + +/** + * Receive data (in form of a pbuf) from a TCP netconn + * + * @param conn the netconn from which to receive data + * @param new_buf pointer where a new pbuf is stored when received data + * @return ERR_OK if data has been received, an error code otherwise (timeout, + * memory error or another error) + * ERR_ARG if conn is not a TCP netconn + */ +err_t +netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf) +{ + LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL) && + netconn_type(conn) == NETCONN_TCP, return ERR_ARG;); + + return netconn_recv_data(conn, (void **)new_buf); +} + +/** + * Receive data (in form of a netbuf containing a packet buffer) from a netconn + * + * @param conn the netconn from which to receive data + * @param new_buf pointer where a new netbuf is stored when received data + * @return ERR_OK if data has been received, an error code otherwise (timeout, + * memory error or another error) + */ +err_t +netconn_recv(struct netconn *conn, struct netbuf **new_buf) +{ +#if LWIP_TCP + struct netbuf *buf = NULL; + err_t err; +#endif /* LWIP_TCP */ + + LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;); + *new_buf = NULL; + LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_accept: invalid recvmbox", sys_mbox_valid(&conn->recvmbox), return ERR_CONN;); + +#if LWIP_TCP + if (conn->type == NETCONN_TCP) { + struct pbuf *p = NULL; + /* This is not a listening netconn, since recvmbox is set */ + + buf = (struct netbuf *)memp_malloc(MEMP_NETBUF); + if (buf == NULL) { + NETCONN_SET_SAFE_ERR(conn, ERR_MEM); + return ERR_MEM; + } + + err = netconn_recv_data(conn, (void **)&p); + if (err != ERR_OK) { + memp_free(MEMP_NETBUF, buf); + return err; + } + LWIP_ASSERT("p != NULL", p != NULL); + + buf->p = p; + buf->ptr = p; + buf->port = 0; + ip_addr_set_any(&buf->addr); + *new_buf = buf; + /* don't set conn->last_err: it's only ERR_OK, anyway */ + return ERR_OK; + } else +#endif /* LWIP_TCP */ + { +#if (LWIP_UDP || LWIP_RAW) + return netconn_recv_data(conn, (void **)new_buf); +#endif /* (LWIP_UDP || LWIP_RAW) */ + } +} + +/** + * TCP: update the receive window: by calling this, the application + * tells the stack that it has processed data and is able to accept + * new data. + * ATTENTION: use with care, this is mainly used for sockets! + * Can only be used when calling netconn_set_noautorecved(conn, 1) before. + * + * @param conn the netconn for which to update the receive window + * @param length amount of data processed (ATTENTION: this must be accurate!) + */ +void +netconn_recved(struct netconn *conn, u32_t length) +{ +#if LWIP_TCP + if ((conn != NULL) && (conn->type == NETCONN_TCP) && + (netconn_get_noautorecved(conn))) { + struct api_msg msg; + /* Let the stack know that we have taken the data. */ + /* TODO: Speedup: Don't block and wait for the answer here + (to prevent multiple thread-switches). */ + msg.function = do_recv; + msg.msg.conn = conn; + msg.msg.msg.r.len = length; + /* don't care for the return value of do_recv */ + TCPIP_APIMSG(&msg); + } +#else /* LWIP_TCP */ + LWIP_UNUSED_ARG(conn); + LWIP_UNUSED_ARG(length); +#endif /* LWIP_TCP */ +} + +/** + * Send data (in form of a netbuf) to a specific remote IP address and port. + * Only to be used for UDP and RAW netconns (not TCP). + * + * @param conn the netconn over which to send data + * @param buf a netbuf containing the data to send + * @param addr the remote IP address to which to send the data + * @param port the remote port to which to send the data + * @return ERR_OK if data was sent, any other err_t on error + */ +err_t +netconn_sendto(struct netconn *conn, struct netbuf *buf, ip_addr_t *addr, u16_t port) +{ + if (buf != NULL) { + ip_addr_set(&buf->addr, addr); + buf->port = port; + return netconn_send(conn, buf); + } + return ERR_VAL; +} + +/** + * Send data over a UDP or RAW netconn (that is already connected). + * + * @param conn the UDP or RAW netconn over which to send data + * @param buf a netbuf containing the data to send + * @return ERR_OK if data was sent, any other err_t on error + */ +err_t +netconn_send(struct netconn *conn, struct netbuf *buf) +{ + struct api_msg msg; + err_t err; + + LWIP_ERROR("netconn_send: invalid conn", (conn != NULL), return ERR_ARG;); + + LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_send: sending %"U16_F" bytes\n", buf->p->tot_len)); + msg.function = do_send; + msg.msg.conn = conn; + msg.msg.msg.b = buf; + err = TCPIP_APIMSG(&msg); + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +} + +/** + * Send data over a TCP netconn. + * + * @param conn the TCP netconn over which to send data + * @param dataptr pointer to the application buffer that contains the data to send + * @param size size of the application data to send + * @param apiflags combination of following flags : + * - NETCONN_COPY: data will be copied into memory belonging to the stack + * - NETCONN_MORE: for TCP connection, PSH flag will be set on last segment sent + * - NETCONN_DONTBLOCK: only write the data if all dat can be written at once + * @return ERR_OK if data was sent, any other err_t on error + */ +err_t +netconn_write(struct netconn *conn, const void *dataptr, size_t size, u8_t apiflags) +{ + struct api_msg msg; + err_t err; + + LWIP_ERROR("netconn_write: invalid conn", (conn != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_write: invalid conn->type", (conn->type == NETCONN_TCP), return ERR_VAL;); + if (size == 0) { + return ERR_OK; + } + + /* @todo: for non-blocking write, check if 'size' would ever fit into + snd_queue or snd_buf */ + msg.function = do_write; + msg.msg.conn = conn; + msg.msg.msg.w.dataptr = dataptr; + msg.msg.msg.w.apiflags = apiflags; + msg.msg.msg.w.len = size; + /* For locking the core: this _can_ be delayed on low memory/low send buffer, + but if it is, this is done inside api_msg.c:do_write(), so we can use the + non-blocking version here. */ + err = TCPIP_APIMSG(&msg); + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +} + +/** + * Close ot shutdown a TCP netconn (doesn't delete it). + * + * @param conn the TCP netconn to close or shutdown + * @param how fully close or only shutdown one side? + * @return ERR_OK if the netconn was closed, any other err_t on error + */ +static err_t +netconn_close_shutdown(struct netconn *conn, u8_t how) +{ + struct api_msg msg; + err_t err; + + LWIP_ERROR("netconn_close: invalid conn", (conn != NULL), return ERR_ARG;); + + msg.function = do_close; + msg.msg.conn = conn; + /* shutting down both ends is the same as closing */ + msg.msg.msg.sd.shut = how; + /* because of the LWIP_TCPIP_CORE_LOCKING implementation of do_close, + don't use TCPIP_APIMSG here */ + err = tcpip_apimsg(&msg); + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +} + +/** + * Close a TCP netconn (doesn't delete it). + * + * @param conn the TCP netconn to close + * @return ERR_OK if the netconn was closed, any other err_t on error + */ +err_t +netconn_close(struct netconn *conn) +{ + /* shutting down both ends is the same as closing */ + return netconn_close_shutdown(conn, NETCONN_SHUT_RDWR); +} + +/** + * Shut down one or both sides of a TCP netconn (doesn't delete it). + * + * @param conn the TCP netconn to shut down + * @return ERR_OK if the netconn was closed, any other err_t on error + */ +err_t +netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx) +{ + return netconn_close_shutdown(conn, (shut_rx ? NETCONN_SHUT_RD : 0) | (shut_tx ? NETCONN_SHUT_WR : 0)); +} + +#if LWIP_IGMP +/** + * Join multicast groups for UDP netconns. + * + * @param conn the UDP netconn for which to change multicast addresses + * @param multiaddr IP address of the multicast group to join or leave + * @param netif_addr the IP address of the network interface on which to send + * the igmp message + * @param join_or_leave flag whether to send a join- or leave-message + * @return ERR_OK if the action was taken, any err_t on error + */ +err_t +netconn_join_leave_group(struct netconn *conn, + ip_addr_t *multiaddr, + ip_addr_t *netif_addr, + enum netconn_igmp join_or_leave) +{ + struct api_msg msg; + err_t err; + + LWIP_ERROR("netconn_join_leave_group: invalid conn", (conn != NULL), return ERR_ARG;); + + msg.function = do_join_leave_group; + msg.msg.conn = conn; + msg.msg.msg.jl.multiaddr = multiaddr; + msg.msg.msg.jl.netif_addr = netif_addr; + msg.msg.msg.jl.join_or_leave = join_or_leave; + err = TCPIP_APIMSG(&msg); + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +} +#endif /* LWIP_IGMP */ + +#if LWIP_DNS +/** + * Execute a DNS query, only one IP address is returned + * + * @param name a string representation of the DNS host name to query + * @param addr a preallocated ip_addr_t where to store the resolved IP address + * @return ERR_OK: resolving succeeded + * ERR_MEM: memory error, try again later + * ERR_ARG: dns client not initialized or invalid hostname + * ERR_VAL: dns server response was invalid + */ +err_t +netconn_gethostbyname(const char *name, ip_addr_t *addr) +{ + struct dns_api_msg msg; + err_t err; + sys_sem_t sem; + + LWIP_ERROR("netconn_gethostbyname: invalid name", (name != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_gethostbyname: invalid addr", (addr != NULL), return ERR_ARG;); + + err = sys_sem_new(&sem, 0); + if (err != ERR_OK) { + return err; + } + + msg.name = name; + msg.addr = addr; + msg.err = &err; + msg.sem = &sem; + + tcpip_callback(do_gethostbyname, &msg); + sys_sem_wait(&sem); + sys_sem_free(&sem); + + return err; +} +#endif /* LWIP_DNS*/ + +#endif /* LWIP_NETCONN */ diff --git a/variants/lwip/lwip/api/api_msg.c b/variants/lwip/lwip/api/api_msg.c new file mode 100755 index 000000000..dcb07e9c6 --- /dev/null +++ b/variants/lwip/lwip/api/api_msg.c @@ -0,0 +1,1540 @@ +/** + * @file + * Sequential API Internal module + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/api_msg.h" + +#include "lwip/ip.h" +#include "lwip/udp.h" +#include "lwip/tcp.h" +#include "lwip/raw.h" + +#include "lwip/memp.h" +#include "lwip/tcpip.h" +#include "lwip/igmp.h" +#include "lwip/dns.h" + +#include + +#define SET_NONBLOCKING_CONNECT(conn, val) do { if(val) { \ + (conn)->flags |= NETCONN_FLAG_IN_NONBLOCKING_CONNECT; \ +} else { \ + (conn)->flags &= ~ NETCONN_FLAG_IN_NONBLOCKING_CONNECT; }} while(0) +#define IN_NONBLOCKING_CONNECT(conn) (((conn)->flags & NETCONN_FLAG_IN_NONBLOCKING_CONNECT) != 0) + +/* forward declarations */ +#if LWIP_TCP +static err_t do_writemore(struct netconn *conn); +static void do_close_internal(struct netconn *conn); +#endif + +#if LWIP_RAW +/** + * Receive callback function for RAW netconns. + * Doesn't 'eat' the packet, only references it and sends it to + * conn->recvmbox + * + * @see raw.h (struct raw_pcb.recv) for parameters and return value + */ +static u8_t +recv_raw(void *arg, struct raw_pcb *pcb, struct pbuf *p, + ip_addr_t *addr) +{ + struct pbuf *q; + struct netbuf *buf; + struct netconn *conn; + + LWIP_UNUSED_ARG(addr); + conn = (struct netconn *)arg; + + if ((conn != NULL) && sys_mbox_valid(&conn->recvmbox)) { +#if LWIP_SO_RCVBUF + int recv_avail; + SYS_ARCH_GET(conn->recv_avail, recv_avail); + if ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize) { + return 0; + } +#endif /* LWIP_SO_RCVBUF */ + /* copy the whole packet into new pbufs */ + q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); + if(q != NULL) { + if (pbuf_copy(q, p) != ERR_OK) { + pbuf_free(q); + q = NULL; + } + } + + if (q != NULL) { + u16_t len; + buf = (struct netbuf *)memp_malloc(MEMP_NETBUF); + if (buf == NULL) { + pbuf_free(q); + return 0; + } + + buf->p = q; + buf->ptr = q; + ip_addr_copy(buf->addr, *ip_current_src_addr()); + buf->port = pcb->protocol; + + len = q->tot_len; + if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) { + netbuf_delete(buf); + return 0; + } else { +#if LWIP_SO_RCVBUF + SYS_ARCH_INC(conn->recv_avail, len); +#endif /* LWIP_SO_RCVBUF */ + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, len); + } + } + } + + return 0; /* do not eat the packet */ +} +#endif /* LWIP_RAW*/ + +#if LWIP_UDP +/** + * Receive callback function for UDP netconns. + * Posts the packet to conn->recvmbox or deletes it on memory error. + * + * @see udp.h (struct udp_pcb.recv) for parameters + */ +static void +recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *addr, u16_t port) +{ + struct netbuf *buf; + struct netconn *conn; + u16_t len; +#if LWIP_SO_RCVBUF + int recv_avail; +#endif /* LWIP_SO_RCVBUF */ + + LWIP_UNUSED_ARG(pcb); /* only used for asserts... */ + LWIP_ASSERT("recv_udp must have a pcb argument", pcb != NULL); + LWIP_ASSERT("recv_udp must have an argument", arg != NULL); + conn = (struct netconn *)arg; + LWIP_ASSERT("recv_udp: recv for wrong pcb!", conn->pcb.udp == pcb); + +#if LWIP_SO_RCVBUF + SYS_ARCH_GET(conn->recv_avail, recv_avail); + if ((conn == NULL) || !sys_mbox_valid(&conn->recvmbox) || + ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize)) { +#else /* LWIP_SO_RCVBUF */ + if ((conn == NULL) || !sys_mbox_valid(&conn->recvmbox)) { +#endif /* LWIP_SO_RCVBUF */ + pbuf_free(p); + return; + } + + buf = (struct netbuf *)memp_malloc(MEMP_NETBUF); + if (buf == NULL) { + pbuf_free(p); + return; + } else { + buf->p = p; + buf->ptr = p; + ip_addr_set(&buf->addr, addr); + buf->port = port; +#if LWIP_NETBUF_RECVINFO + { + const struct ip_hdr* iphdr = ip_current_header(); + /* get the UDP header - always in the first pbuf, ensured by udp_input */ + const struct udp_hdr* udphdr = (void*)(((char*)iphdr) + IPH_LEN(iphdr)); +#if LWIP_CHECKSUM_ON_COPY + buf->flags = NETBUF_FLAG_DESTADDR; +#endif /* LWIP_CHECKSUM_ON_COPY */ + ip_addr_set(&buf->toaddr, ip_current_dest_addr()); + buf->toport_chksum = udphdr->dest; + } +#endif /* LWIP_NETBUF_RECVINFO */ + } + + len = p->tot_len; + if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) { + netbuf_delete(buf); + return; + } else { +#if LWIP_SO_RCVBUF + SYS_ARCH_INC(conn->recv_avail, len); +#endif /* LWIP_SO_RCVBUF */ + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, len); + } +} +#endif /* LWIP_UDP */ + +#if LWIP_TCP +/** + * Receive callback function for TCP netconns. + * Posts the packet to conn->recvmbox, but doesn't delete it on errors. + * + * @see tcp.h (struct tcp_pcb.recv) for parameters and return value + */ +static err_t +recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) +{ + struct netconn *conn; + u16_t len; + + LWIP_UNUSED_ARG(pcb); + LWIP_ASSERT("recv_tcp must have a pcb argument", pcb != NULL); + LWIP_ASSERT("recv_tcp must have an argument", arg != NULL); + conn = (struct netconn *)arg; + LWIP_ASSERT("recv_tcp: recv for wrong pcb!", conn->pcb.tcp == pcb); + + if (conn == NULL) { + return ERR_VAL; + } + if (!sys_mbox_valid(&conn->recvmbox)) { + /* recvmbox already deleted */ + if (p != NULL) { + tcp_recved(pcb, p->tot_len); + pbuf_free(p); + } + return ERR_OK; + } + /* Unlike for UDP or RAW pcbs, don't check for available space + using recv_avail since that could break the connection + (data is already ACKed) */ + + /* don't overwrite fatal errors! */ + NETCONN_SET_SAFE_ERR(conn, err); + + if (p != NULL) { + len = p->tot_len; + } else { + len = 0; + } + + if (sys_mbox_trypost(&conn->recvmbox, p) != ERR_OK) { + /* don't deallocate p: it is presented to us later again from tcp_fasttmr! */ + return ERR_MEM; + } else { +#if LWIP_SO_RCVBUF + SYS_ARCH_INC(conn->recv_avail, len); +#endif /* LWIP_SO_RCVBUF */ + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, len); + } + + return ERR_OK; +} + +/** + * Poll callback function for TCP netconns. + * Wakes up an application thread that waits for a connection to close + * or data to be sent. The application thread then takes the + * appropriate action to go on. + * + * Signals the conn->sem. + * netconn_close waits for conn->sem if closing failed. + * + * @see tcp.h (struct tcp_pcb.poll) for parameters and return value + */ +static err_t +poll_tcp(void *arg, struct tcp_pcb *pcb) +{ + struct netconn *conn = (struct netconn *)arg; + + LWIP_UNUSED_ARG(pcb); + LWIP_ASSERT("conn != NULL", (conn != NULL)); + + if (conn->state == NETCONN_WRITE) { + do_writemore(conn); + } else if (conn->state == NETCONN_CLOSE) { + do_close_internal(conn); + } + /* @todo: implement connect timeout here? */ + + /* Did a nonblocking write fail before? Then check available write-space. */ + if (conn->flags & NETCONN_FLAG_CHECK_WRITESPACE) { + /* If the queued byte- or pbuf-count drops below the configured low-water limit, + let select mark this pcb as writable again. */ + if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) && + (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) { + conn->flags &= ~NETCONN_FLAG_CHECK_WRITESPACE; + API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); + } + } + + return ERR_OK; +} + +/** + * Sent callback function for TCP netconns. + * Signals the conn->sem and calls API_EVENT. + * netconn_write waits for conn->sem if send buffer is low. + * + * @see tcp.h (struct tcp_pcb.sent) for parameters and return value + */ +static err_t +sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len) +{ + struct netconn *conn = (struct netconn *)arg; + + LWIP_UNUSED_ARG(pcb); + LWIP_ASSERT("conn != NULL", (conn != NULL)); + + if (conn->state == NETCONN_WRITE) { + do_writemore(conn); + } else if (conn->state == NETCONN_CLOSE) { + do_close_internal(conn); + } + + if (conn) { + /* If the queued byte- or pbuf-count drops below the configured low-water limit, + let select mark this pcb as writable again. */ + if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) && + (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) { + conn->flags &= ~NETCONN_FLAG_CHECK_WRITESPACE; + API_EVENT(conn, NETCONN_EVT_SENDPLUS, len); + } + } + + return ERR_OK; +} + +/** + * Error callback function for TCP netconns. + * Signals conn->sem, posts to all conn mboxes and calls API_EVENT. + * The application thread has then to decide what to do. + * + * @see tcp.h (struct tcp_pcb.err) for parameters + */ +static void +err_tcp(void *arg, err_t err) +{ + struct netconn *conn; + enum netconn_state old_state; + SYS_ARCH_DECL_PROTECT(lev); + + conn = (struct netconn *)arg; + LWIP_ASSERT("conn != NULL", (conn != NULL)); + + conn->pcb.tcp = NULL; + + /* no check since this is always fatal! */ + SYS_ARCH_PROTECT(lev); + conn->last_err = err; + SYS_ARCH_UNPROTECT(lev); + + /* reset conn->state now before waking up other threads */ + old_state = conn->state; + conn->state = NETCONN_NONE; + + /* Notify the user layer about a connection error. Used to signal + select. */ + API_EVENT(conn, NETCONN_EVT_ERROR, 0); + /* Try to release selects pending on 'read' or 'write', too. + They will get an error if they actually try to read or write. */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); + API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); + + /* pass NULL-message to recvmbox to wake up pending recv */ + if (sys_mbox_valid(&conn->recvmbox)) { + /* use trypost to prevent deadlock */ + sys_mbox_trypost(&conn->recvmbox, NULL); + } + /* pass NULL-message to acceptmbox to wake up pending accept */ + if (sys_mbox_valid(&conn->acceptmbox)) { + /* use trypost to preven deadlock */ + sys_mbox_trypost(&conn->acceptmbox, NULL); + } + + if ((old_state == NETCONN_WRITE) || (old_state == NETCONN_CLOSE) || + (old_state == NETCONN_CONNECT)) { + /* calling do_writemore/do_close_internal is not necessary + since the pcb has already been deleted! */ + int was_nonblocking_connect = IN_NONBLOCKING_CONNECT(conn); + SET_NONBLOCKING_CONNECT(conn, 0); + + if (!was_nonblocking_connect) { + /* set error return code */ + LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL); + conn->current_msg->err = err; + conn->current_msg = NULL; + /* wake up the waiting task */ + sys_sem_signal(&conn->op_completed); + } + } else { + LWIP_ASSERT("conn->current_msg == NULL", conn->current_msg == NULL); + } +} + +/** + * Setup a tcp_pcb with the correct callback function pointers + * and their arguments. + * + * @param conn the TCP netconn to setup + */ +static void +setup_tcp(struct netconn *conn) +{ + struct tcp_pcb *pcb; + + pcb = conn->pcb.tcp; + tcp_arg(pcb, conn); + tcp_recv(pcb, recv_tcp); + tcp_sent(pcb, sent_tcp); + tcp_poll(pcb, poll_tcp, 4); + tcp_err(pcb, err_tcp); +} + +/** + * Accept callback function for TCP netconns. + * Allocates a new netconn and posts that to conn->acceptmbox. + * + * @see tcp.h (struct tcp_pcb_listen.accept) for parameters and return value + */ +static err_t +accept_function(void *arg, struct tcp_pcb *newpcb, err_t err) +{ + struct netconn *newconn; + struct netconn *conn = (struct netconn *)arg; + + LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: newpcb->tate: %s\n", tcp_debug_state_str(newpcb->state))); + + if (!sys_mbox_valid(&conn->acceptmbox)) { + LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: acceptmbox already deleted\n")); + return ERR_VAL; + } + + /* We have to set the callback here even though + * the new socket is unknown. conn->socket is marked as -1. */ + newconn = netconn_alloc(conn->type, conn->callback); + if (newconn == NULL) { + return ERR_MEM; + } + newconn->pcb.tcp = newpcb; + setup_tcp(newconn); + /* no protection: when creating the pcb, the netconn is not yet known + to the application thread */ + newconn->last_err = err; + + if (sys_mbox_trypost(&conn->acceptmbox, newconn) != ERR_OK) { + /* When returning != ERR_OK, the pcb is aborted in tcp_process(), + so do nothing here! */ + newconn->pcb.tcp = NULL; + /* no need to drain since we know the recvmbox is empty. */ + sys_mbox_free(&newconn->recvmbox); + sys_mbox_set_invalid(&newconn->recvmbox); + netconn_free(newconn); + return ERR_MEM; + } else { + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); + } + + return ERR_OK; +} +#endif /* LWIP_TCP */ + +/** + * Create a new pcb of a specific type. + * Called from do_newconn(). + * + * @param msg the api_msg_msg describing the connection type + * @return msg->conn->err, but the return value is currently ignored + */ +static void +pcb_new(struct api_msg_msg *msg) +{ + LWIP_ASSERT("pcb_new: pcb already allocated", msg->conn->pcb.tcp == NULL); + + /* Allocate a PCB for this connection */ + switch(NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + msg->conn->pcb.raw = raw_new(msg->msg.n.proto); + if(msg->conn->pcb.raw == NULL) { + msg->err = ERR_MEM; + break; + } + raw_recv(msg->conn->pcb.raw, recv_raw, msg->conn); + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + msg->conn->pcb.udp = udp_new(); + if(msg->conn->pcb.udp == NULL) { + msg->err = ERR_MEM; + break; + } +#if LWIP_UDPLITE + if (msg->conn->type==NETCONN_UDPLITE) { + udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_UDPLITE); + } +#endif /* LWIP_UDPLITE */ + if (msg->conn->type==NETCONN_UDPNOCHKSUM) { + udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_NOCHKSUM); + } + udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + msg->conn->pcb.tcp = tcp_new(); + if(msg->conn->pcb.tcp == NULL) { + msg->err = ERR_MEM; + break; + } + setup_tcp(msg->conn); + break; +#endif /* LWIP_TCP */ + default: + /* Unsupported netconn type, e.g. protocol disabled */ + msg->err = ERR_VAL; + break; + } +} + +/** + * Create a new pcb of a specific type inside a netconn. + * Called from netconn_new_with_proto_and_callback. + * + * @param msg the api_msg_msg describing the connection type + */ +void +do_newconn(struct api_msg_msg *msg) +{ + msg->err = ERR_OK; + if(msg->conn->pcb.tcp == NULL) { + pcb_new(msg); + } + /* Else? This "new" connection already has a PCB allocated. */ + /* Is this an error condition? Should it be deleted? */ + /* We currently just are happy and return. */ + + TCPIP_APIMSG_ACK(msg); +} + +/** + * Create a new netconn (of a specific type) that has a callback function. + * The corresponding pcb is NOT created! + * + * @param t the type of 'connection' to create (@see enum netconn_type) + * @param proto the IP protocol for RAW IP pcbs + * @param callback a function to call on status changes (RX available, TX'ed) + * @return a newly allocated struct netconn or + * NULL on memory error + */ +struct netconn* +netconn_alloc(enum netconn_type t, netconn_callback callback) +{ + struct netconn *conn; + int size; + + conn = (struct netconn *)memp_malloc(MEMP_NETCONN); + if (conn == NULL) { + return NULL; + } + + conn->last_err = ERR_OK; + conn->type = t; + conn->pcb.tcp = NULL; + +#if (DEFAULT_RAW_RECVMBOX_SIZE == DEFAULT_UDP_RECVMBOX_SIZE) && \ + (DEFAULT_RAW_RECVMBOX_SIZE == DEFAULT_TCP_RECVMBOX_SIZE) + size = DEFAULT_RAW_RECVMBOX_SIZE; +#else + switch(NETCONNTYPE_GROUP(t)) { +#if LWIP_RAW + case NETCONN_RAW: + size = DEFAULT_RAW_RECVMBOX_SIZE; + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + size = DEFAULT_UDP_RECVMBOX_SIZE; + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + size = DEFAULT_TCP_RECVMBOX_SIZE; + break; +#endif /* LWIP_TCP */ + default: + LWIP_ASSERT("netconn_alloc: undefined netconn_type", 0); + break; + } +#endif + + if (sys_sem_new(&conn->op_completed, 0) != ERR_OK) { + memp_free(MEMP_NETCONN, conn); + return NULL; + } + if (sys_mbox_new(&conn->recvmbox, size) != ERR_OK) { + sys_sem_free(&conn->op_completed); + memp_free(MEMP_NETCONN, conn); + return NULL; + } + +#if LWIP_TCP + sys_mbox_set_invalid(&conn->acceptmbox); +#endif + conn->state = NETCONN_NONE; +#if LWIP_SOCKET + /* initialize socket to -1 since 0 is a valid socket */ + conn->socket = -1; +#endif /* LWIP_SOCKET */ + conn->callback = callback; +#if LWIP_TCP + conn->current_msg = NULL; + conn->write_offset = 0; +#endif /* LWIP_TCP */ +#if LWIP_SO_RCVTIMEO + conn->recv_timeout = 0; +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + conn->recv_bufsize = RECV_BUFSIZE_DEFAULT; + conn->recv_avail = 0; +#endif /* LWIP_SO_RCVBUF */ + conn->flags = 0; + return conn; +} + +/** + * Delete a netconn and all its resources. + * The pcb is NOT freed (since we might not be in the right thread context do this). + * + * @param conn the netconn to free + */ +void +netconn_free(struct netconn *conn) +{ + LWIP_ASSERT("PCB must be deallocated outside this function", conn->pcb.tcp == NULL); + LWIP_ASSERT("recvmbox must be deallocated before calling this function", + !sys_mbox_valid(&conn->recvmbox)); +#if LWIP_TCP + LWIP_ASSERT("acceptmbox must be deallocated before calling this function", + !sys_mbox_valid(&conn->acceptmbox)); +#endif /* LWIP_TCP */ + + sys_sem_free(&conn->op_completed); + sys_sem_set_invalid(&conn->op_completed); + + memp_free(MEMP_NETCONN, conn); +} + +/** + * Delete rcvmbox and acceptmbox of a netconn and free the left-over data in + * these mboxes + * + * @param conn the netconn to free + * @bytes_drained bytes drained from recvmbox + * @accepts_drained pending connections drained from acceptmbox + */ +static void +netconn_drain(struct netconn *conn) +{ + void *mem; +#if LWIP_TCP + struct pbuf *p; +#endif /* LWIP_TCP */ + + /* This runs in tcpip_thread, so we don't need to lock against rx packets */ + + /* Delete and drain the recvmbox. */ + if (sys_mbox_valid(&conn->recvmbox)) { + while (sys_mbox_tryfetch(&conn->recvmbox, &mem) != SYS_MBOX_EMPTY) { +#if LWIP_TCP + if (conn->type == NETCONN_TCP) { + if(mem != NULL) { + p = (struct pbuf*)mem; + /* pcb might be set to NULL already by err_tcp() */ + if (conn->pcb.tcp != NULL) { + tcp_recved(conn->pcb.tcp, p->tot_len); + } + pbuf_free(p); + } + } else +#endif /* LWIP_TCP */ + { + netbuf_delete((struct netbuf *)mem); + } + } + sys_mbox_free(&conn->recvmbox); + sys_mbox_set_invalid(&conn->recvmbox); + } + + /* Delete and drain the acceptmbox. */ +#if LWIP_TCP + if (sys_mbox_valid(&conn->acceptmbox)) { + while (sys_mbox_tryfetch(&conn->acceptmbox, &mem) != SYS_MBOX_EMPTY) { + struct netconn *newconn = (struct netconn *)mem; + /* Only tcp pcbs have an acceptmbox, so no need to check conn->type */ + /* pcb might be set to NULL already by err_tcp() */ + if (conn->pcb.tcp != NULL) { + tcp_accepted(conn->pcb.tcp); + } + /* drain recvmbox */ + netconn_drain(newconn); + if (newconn->pcb.tcp != NULL) { + tcp_abort(newconn->pcb.tcp); + newconn->pcb.tcp = NULL; + } + netconn_free(newconn); + } + sys_mbox_free(&conn->acceptmbox); + sys_mbox_set_invalid(&conn->acceptmbox); + } +#endif /* LWIP_TCP */ +} + +#if LWIP_TCP +/** + * Internal helper function to close a TCP netconn: since this sometimes + * doesn't work at the first attempt, this function is called from multiple + * places. + * + * @param conn the TCP netconn to close + */ +static void +do_close_internal(struct netconn *conn) +{ + err_t err; + u8_t shut, shut_rx, shut_tx, close; + + LWIP_ASSERT("invalid conn", (conn != NULL)); + LWIP_ASSERT("this is for tcp netconns only", (conn->type == NETCONN_TCP)); + LWIP_ASSERT("conn must be in state NETCONN_CLOSE", (conn->state == NETCONN_CLOSE)); + LWIP_ASSERT("pcb already closed", (conn->pcb.tcp != NULL)); + LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL); + + shut = conn->current_msg->msg.sd.shut; + shut_rx = shut & NETCONN_SHUT_RD; + shut_tx = shut & NETCONN_SHUT_WR; + /* shutting down both ends is the same as closing */ + close = shut == NETCONN_SHUT_RDWR; + + /* Set back some callback pointers */ + if (close) { + tcp_arg(conn->pcb.tcp, NULL); + } + if (conn->pcb.tcp->state == LISTEN) { + tcp_accept(conn->pcb.tcp, NULL); + } else { + /* some callbacks have to be reset if tcp_close is not successful */ + if (shut_rx) { + tcp_recv(conn->pcb.tcp, NULL); + tcp_accept(conn->pcb.tcp, NULL); + } + if (shut_tx) { + tcp_sent(conn->pcb.tcp, NULL); + } + if (close) { + tcp_poll(conn->pcb.tcp, NULL, 4); + tcp_err(conn->pcb.tcp, NULL); + } + } + /* Try to close the connection */ + if (shut == NETCONN_SHUT_RDWR) { + err = tcp_close(conn->pcb.tcp); + } else { + err = tcp_shutdown(conn->pcb.tcp, shut & NETCONN_SHUT_RD, shut & NETCONN_SHUT_WR); + } + if (err == ERR_OK) { + /* Closing succeeded */ + conn->current_msg->err = ERR_OK; + conn->current_msg = NULL; + conn->state = NETCONN_NONE; + /* Set back some callback pointers as conn is going away */ + conn->pcb.tcp = NULL; + /* Trigger select() in socket layer. Make sure everybody notices activity + on the connection, error first! */ + if (close) { + API_EVENT(conn, NETCONN_EVT_ERROR, 0); + } + if (shut_rx) { + API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); + } + if (shut_tx) { + API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); + } + /* wake up the application task */ + sys_sem_signal(&conn->op_completed); + } else { + /* Closing failed, restore some of the callbacks */ + /* Closing of listen pcb will never fail! */ + LWIP_ASSERT("Closing a listen pcb may not fail!", (conn->pcb.tcp->state != LISTEN)); + tcp_sent(conn->pcb.tcp, sent_tcp); + tcp_poll(conn->pcb.tcp, poll_tcp, 4); + tcp_err(conn->pcb.tcp, err_tcp); + tcp_arg(conn->pcb.tcp, conn); + /* don't restore recv callback: we don't want to receive any more data */ + } + /* If closing didn't succeed, we get called again either + from poll_tcp or from sent_tcp */ +} +#endif /* LWIP_TCP */ + +/** + * Delete the pcb inside a netconn. + * Called from netconn_delete. + * + * @param msg the api_msg_msg pointing to the connection + */ +void +do_delconn(struct api_msg_msg *msg) +{ + /* @todo TCP: abort running write/connect? */ + if ((msg->conn->state != NETCONN_NONE) && + (msg->conn->state != NETCONN_LISTEN) && + (msg->conn->state != NETCONN_CONNECT)) { + /* this only happens for TCP netconns */ + LWIP_ASSERT("msg->conn->type == NETCONN_TCP", msg->conn->type == NETCONN_TCP); + msg->err = ERR_INPROGRESS; + } else { + LWIP_ASSERT("blocking connect in progress", + (msg->conn->state != NETCONN_CONNECT) || IN_NONBLOCKING_CONNECT(msg->conn)); + /* Drain and delete mboxes */ + netconn_drain(msg->conn); + + if (msg->conn->pcb.tcp != NULL) { + + switch (NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + raw_remove(msg->conn->pcb.raw); + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + msg->conn->pcb.udp->recv_arg = NULL; + udp_remove(msg->conn->pcb.udp); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL && + msg->conn->write_offset == 0); + msg->conn->state = NETCONN_CLOSE; + msg->msg.sd.shut = NETCONN_SHUT_RDWR; + msg->conn->current_msg = msg; + do_close_internal(msg->conn); + /* API_EVENT is called inside do_close_internal, before releasing + the application thread, so we can return at this point! */ + return; +#endif /* LWIP_TCP */ + default: + break; + } + msg->conn->pcb.tcp = NULL; + } + /* tcp netconns don't come here! */ + + /* @todo: this lets select make the socket readable and writable, + which is wrong! errfd instead? */ + API_EVENT(msg->conn, NETCONN_EVT_RCVPLUS, 0); + API_EVENT(msg->conn, NETCONN_EVT_SENDPLUS, 0); + } + if (sys_sem_valid(&msg->conn->op_completed)) { + sys_sem_signal(&msg->conn->op_completed); + } +} + +/** + * Bind a pcb contained in a netconn + * Called from netconn_bind. + * + * @param msg the api_msg_msg pointing to the connection and containing + * the IP address and port to bind to + */ +void +do_bind(struct api_msg_msg *msg) +{ + if (ERR_IS_FATAL(msg->conn->last_err)) { + msg->err = msg->conn->last_err; + } else { + msg->err = ERR_VAL; + if (msg->conn->pcb.tcp != NULL) { + switch (NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + msg->err = raw_bind(msg->conn->pcb.raw, msg->msg.bc.ipaddr); + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + msg->err = udp_bind(msg->conn->pcb.udp, msg->msg.bc.ipaddr, msg->msg.bc.port); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + msg->err = tcp_bind(msg->conn->pcb.tcp, msg->msg.bc.ipaddr, msg->msg.bc.port); + break; +#endif /* LWIP_TCP */ + default: + break; + } + } + } + TCPIP_APIMSG_ACK(msg); +} + +#if LWIP_TCP +/** + * TCP callback function if a connection (opened by tcp_connect/do_connect) has + * been established (or reset by the remote host). + * + * @see tcp.h (struct tcp_pcb.connected) for parameters and return values + */ +static err_t +do_connected(void *arg, struct tcp_pcb *pcb, err_t err) +{ + struct netconn *conn; + int was_blocking; + + LWIP_UNUSED_ARG(pcb); + + conn = (struct netconn *)arg; + + if (conn == NULL) { + return ERR_VAL; + } + + LWIP_ASSERT("conn->state == NETCONN_CONNECT", conn->state == NETCONN_CONNECT); + LWIP_ASSERT("(conn->current_msg != NULL) || conn->in_non_blocking_connect", + (conn->current_msg != NULL) || IN_NONBLOCKING_CONNECT(conn)); + + if (conn->current_msg != NULL) { + conn->current_msg->err = err; + } + if ((conn->type == NETCONN_TCP) && (err == ERR_OK)) { + setup_tcp(conn); + } + was_blocking = !IN_NONBLOCKING_CONNECT(conn); + SET_NONBLOCKING_CONNECT(conn, 0); + conn->current_msg = NULL; + conn->state = NETCONN_NONE; + if (!was_blocking) { + SYS_ARCH_DECL_PROTECT(lev); + SYS_ARCH_PROTECT(lev); + if (conn->last_err == ERR_INPROGRESS) { + conn->last_err = ERR_OK; + } + SYS_ARCH_UNPROTECT(lev); + } + API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); + + if (was_blocking) { + sys_sem_signal(&conn->op_completed); + } + return ERR_OK; +} +#endif /* LWIP_TCP */ + +/** + * Connect a pcb contained inside a netconn + * Called from netconn_connect. + * + * @param msg the api_msg_msg pointing to the connection and containing + * the IP address and port to connect to + */ +void +do_connect(struct api_msg_msg *msg) +{ + if (msg->conn->pcb.tcp == NULL) { + /* This may happen when calling netconn_connect() a second time */ + msg->err = ERR_CLSD; + } else { + switch (NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + msg->err = raw_connect(msg->conn->pcb.raw, msg->msg.bc.ipaddr); + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + msg->err = udp_connect(msg->conn->pcb.udp, msg->msg.bc.ipaddr, msg->msg.bc.port); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + /* Prevent connect while doing any other action. */ + if (msg->conn->state != NETCONN_NONE) { + msg->err = ERR_ISCONN; + } else { + setup_tcp(msg->conn); + msg->err = tcp_connect(msg->conn->pcb.tcp, msg->msg.bc.ipaddr, + msg->msg.bc.port, do_connected); + if (msg->err == ERR_OK) { + u8_t non_blocking = netconn_is_nonblocking(msg->conn); + msg->conn->state = NETCONN_CONNECT; + SET_NONBLOCKING_CONNECT(msg->conn, non_blocking); + if (non_blocking) { + msg->err = ERR_INPROGRESS; + } else { + msg->conn->current_msg = msg; + /* sys_sem_signal() is called from do_connected (or err_tcp()), + * when the connection is established! */ + return; + } + } + } + break; +#endif /* LWIP_TCP */ + default: + LWIP_ERROR("Invalid netconn type", 0, do{ msg->err = ERR_VAL; }while(0)); + break; + } + } + sys_sem_signal(&msg->conn->op_completed); +} + +/** + * Connect a pcb contained inside a netconn + * Only used for UDP netconns. + * Called from netconn_disconnect. + * + * @param msg the api_msg_msg pointing to the connection to disconnect + */ +void +do_disconnect(struct api_msg_msg *msg) +{ +#if LWIP_UDP + if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) { + udp_disconnect(msg->conn->pcb.udp); + msg->err = ERR_OK; + } else +#endif /* LWIP_UDP */ + { + msg->err = ERR_VAL; + } + TCPIP_APIMSG_ACK(msg); +} + +#if LWIP_TCP +/** + * Set a TCP pcb contained in a netconn into listen mode + * Called from netconn_listen. + * + * @param msg the api_msg_msg pointing to the connection + */ +void +do_listen(struct api_msg_msg *msg) +{ + if (ERR_IS_FATAL(msg->conn->last_err)) { + msg->err = msg->conn->last_err; + } else { + msg->err = ERR_CONN; + if (msg->conn->pcb.tcp != NULL) { + if (msg->conn->type == NETCONN_TCP) { + if (msg->conn->state == NETCONN_NONE) { +#if TCP_LISTEN_BACKLOG + struct tcp_pcb* lpcb = tcp_listen_with_backlog(msg->conn->pcb.tcp, msg->msg.lb.backlog); +#else /* TCP_LISTEN_BACKLOG */ + struct tcp_pcb* lpcb = tcp_listen(msg->conn->pcb.tcp); +#endif /* TCP_LISTEN_BACKLOG */ + if (lpcb == NULL) { + /* in this case, the old pcb is still allocated */ + msg->err = ERR_MEM; + } else { + /* delete the recvmbox and allocate the acceptmbox */ + if (sys_mbox_valid(&msg->conn->recvmbox)) { + /** @todo: should we drain the recvmbox here? */ + sys_mbox_free(&msg->conn->recvmbox); + sys_mbox_set_invalid(&msg->conn->recvmbox); + } + msg->err = ERR_OK; + if (!sys_mbox_valid(&msg->conn->acceptmbox)) { + msg->err = sys_mbox_new(&msg->conn->acceptmbox, DEFAULT_ACCEPTMBOX_SIZE); + } + if (msg->err == ERR_OK) { + msg->conn->state = NETCONN_LISTEN; + msg->conn->pcb.tcp = lpcb; + tcp_arg(msg->conn->pcb.tcp, msg->conn); + tcp_accept(msg->conn->pcb.tcp, accept_function); + } else { + /* since the old pcb is already deallocated, free lpcb now */ + tcp_close(lpcb); + msg->conn->pcb.tcp = NULL; + } + } + } + } + } + } + TCPIP_APIMSG_ACK(msg); +} +#endif /* LWIP_TCP */ + +/** + * Send some data on a RAW or UDP pcb contained in a netconn + * Called from netconn_send + * + * @param msg the api_msg_msg pointing to the connection + */ +void +do_send(struct api_msg_msg *msg) +{ + if (ERR_IS_FATAL(msg->conn->last_err)) { + msg->err = msg->conn->last_err; + } else { + msg->err = ERR_CONN; + if (msg->conn->pcb.tcp != NULL) { + switch (NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + if (ip_addr_isany(&msg->msg.b->addr)) { + msg->err = raw_send(msg->conn->pcb.raw, msg->msg.b->p); + } else { + msg->err = raw_sendto(msg->conn->pcb.raw, msg->msg.b->p, &msg->msg.b->addr); + } + break; +#endif +#if LWIP_UDP + case NETCONN_UDP: +#if LWIP_CHECKSUM_ON_COPY + if (ip_addr_isany(&msg->msg.b->addr)) { + msg->err = udp_send_chksum(msg->conn->pcb.udp, msg->msg.b->p, + msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum); + } else { + msg->err = udp_sendto_chksum(msg->conn->pcb.udp, msg->msg.b->p, + &msg->msg.b->addr, msg->msg.b->port, + msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum); + } +#else /* LWIP_CHECKSUM_ON_COPY */ + if (ip_addr_isany(&msg->msg.b->addr)) { + msg->err = udp_send(msg->conn->pcb.udp, msg->msg.b->p); + } else { + msg->err = udp_sendto(msg->conn->pcb.udp, msg->msg.b->p, &msg->msg.b->addr, msg->msg.b->port); + } +#endif /* LWIP_CHECKSUM_ON_COPY */ + break; +#endif /* LWIP_UDP */ + default: + break; + } + } + } + TCPIP_APIMSG_ACK(msg); +} + +#if LWIP_TCP +/** + * Indicate data has been received from a TCP pcb contained in a netconn + * Called from netconn_recv + * + * @param msg the api_msg_msg pointing to the connection + */ +void +do_recv(struct api_msg_msg *msg) +{ + msg->err = ERR_OK; + if (msg->conn->pcb.tcp != NULL) { + if (msg->conn->type == NETCONN_TCP) { +#if TCP_LISTEN_BACKLOG + if (msg->conn->pcb.tcp->state == LISTEN) { + tcp_accepted(msg->conn->pcb.tcp); + } else +#endif /* TCP_LISTEN_BACKLOG */ + { + u32_t remaining = msg->msg.r.len; + do { + u16_t recved = (remaining > 0xffff) ? 0xffff : (u16_t)remaining; + tcp_recved(msg->conn->pcb.tcp, recved); + remaining -= recved; + }while(remaining != 0); + } + } + } + TCPIP_APIMSG_ACK(msg); +} + +/** + * See if more data needs to be written from a previous call to netconn_write. + * Called initially from do_write. If the first call can't send all data + * (because of low memory or empty send-buffer), this function is called again + * from sent_tcp() or poll_tcp() to send more data. If all data is sent, the + * blocking application thread (waiting in netconn_write) is released. + * + * @param conn netconn (that is currently in state NETCONN_WRITE) to process + * @return ERR_OK + * ERR_MEM if LWIP_TCPIP_CORE_LOCKING=1 and sending hasn't yet finished + */ +static err_t +do_writemore(struct netconn *conn) +{ + err_t err = ERR_OK; + void *dataptr; + u16_t len, available; + u8_t write_finished = 0; + size_t diff; + u8_t dontblock = netconn_is_nonblocking(conn) || + (conn->current_msg->msg.w.apiflags & NETCONN_DONTBLOCK); + u8_t apiflags = conn->current_msg->msg.w.apiflags; + + LWIP_ASSERT("conn != NULL", conn != NULL); + LWIP_ASSERT("conn->state == NETCONN_WRITE", (conn->state == NETCONN_WRITE)); + LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL); + LWIP_ASSERT("conn->pcb.tcp != NULL", conn->pcb.tcp != NULL); + LWIP_ASSERT("conn->write_offset < conn->current_msg->msg.w.len", + conn->write_offset < conn->current_msg->msg.w.len); + + dataptr = (u8_t*)conn->current_msg->msg.w.dataptr + conn->write_offset; + diff = conn->current_msg->msg.w.len - conn->write_offset; + if (diff > 0xffffUL) { /* max_u16_t */ + len = 0xffff; +#if LWIP_TCPIP_CORE_LOCKING + conn->flags |= NETCONN_FLAG_WRITE_DELAYED; +#endif + apiflags |= TCP_WRITE_FLAG_MORE; + } else { + len = (u16_t)diff; + } + available = tcp_sndbuf(conn->pcb.tcp); + if (available < len) { + /* don't try to write more than sendbuf */ + len = available; +#if LWIP_TCPIP_CORE_LOCKING + conn->flags |= NETCONN_FLAG_WRITE_DELAYED; +#endif + apiflags |= TCP_WRITE_FLAG_MORE; + } + if (dontblock && (len < conn->current_msg->msg.w.len)) { + /* failed to send all data at once -> nonblocking write not possible */ + err = ERR_MEM; + } + if (err == ERR_OK) { + LWIP_ASSERT("do_writemore: invalid length!", ((conn->write_offset + len) <= conn->current_msg->msg.w.len)); + err = tcp_write(conn->pcb.tcp, dataptr, len, apiflags); + } + if (dontblock && (err == ERR_MEM)) { + /* nonblocking write failed */ + write_finished = 1; + err = ERR_WOULDBLOCK; + /* let poll_tcp check writable space to mark the pcb + writable again */ + conn->flags |= NETCONN_FLAG_CHECK_WRITESPACE; + /* let select mark this pcb as non-writable. */ + API_EVENT(conn, NETCONN_EVT_SENDMINUS, len); + } else { + /* if OK or memory error, check available space */ + if (((err == ERR_OK) || (err == ERR_MEM)) && + ((tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT) || + (tcp_sndqueuelen(conn->pcb.tcp) >= TCP_SNDQUEUELOWAT))) { + /* The queued byte- or pbuf-count exceeds the configured low-water limit, + let select mark this pcb as non-writable. */ + API_EVENT(conn, NETCONN_EVT_SENDMINUS, len); + } + + if (err == ERR_OK) { + conn->write_offset += len; + if (conn->write_offset == conn->current_msg->msg.w.len) { + /* everything was written */ + write_finished = 1; + conn->write_offset = 0; + } + tcp_output(conn->pcb.tcp); + } else if (err == ERR_MEM) { + /* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called + we do NOT return to the application thread, since ERR_MEM is + only a temporary error! */ + + /* tcp_write returned ERR_MEM, try tcp_output anyway */ + tcp_output(conn->pcb.tcp); + + #if LWIP_TCPIP_CORE_LOCKING + conn->flags |= NETCONN_FLAG_WRITE_DELAYED; + #endif + } else { + /* On errors != ERR_MEM, we don't try writing any more but return + the error to the application thread. */ + write_finished = 1; + } + } + + if (write_finished) { + /* everything was written: set back connection state + and back to application task */ + conn->current_msg->err = err; + conn->current_msg = NULL; + conn->state = NETCONN_NONE; +#if LWIP_TCPIP_CORE_LOCKING + if ((conn->flags & NETCONN_FLAG_WRITE_DELAYED) != 0) +#endif + { + sys_sem_signal(&conn->op_completed); + } + } +#if LWIP_TCPIP_CORE_LOCKING + else + return ERR_MEM; +#endif + return ERR_OK; +} +#endif /* LWIP_TCP */ + +/** + * Send some data on a TCP pcb contained in a netconn + * Called from netconn_write + * + * @param msg the api_msg_msg pointing to the connection + */ +void +do_write(struct api_msg_msg *msg) +{ + if (ERR_IS_FATAL(msg->conn->last_err)) { + msg->err = msg->conn->last_err; + } else { + if (msg->conn->type == NETCONN_TCP) { +#if LWIP_TCP + if (msg->conn->state != NETCONN_NONE) { + /* netconn is connecting, closing or in blocking write */ + msg->err = ERR_INPROGRESS; + } else if (msg->conn->pcb.tcp != NULL) { + msg->conn->state = NETCONN_WRITE; + /* set all the variables used by do_writemore */ + LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL && + msg->conn->write_offset == 0); + LWIP_ASSERT("msg->msg.w.len != 0", msg->msg.w.len != 0); + msg->conn->current_msg = msg; + msg->conn->write_offset = 0; +#if LWIP_TCPIP_CORE_LOCKING + msg->conn->flags &= ~NETCONN_FLAG_WRITE_DELAYED; + if (do_writemore(msg->conn) != ERR_OK) { + LWIP_ASSERT("state!", msg->conn->state == NETCONN_WRITE); + UNLOCK_TCPIP_CORE(); + sys_arch_sem_wait(&msg->conn->op_completed, 0); + LOCK_TCPIP_CORE(); + LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE); + } +#else /* LWIP_TCPIP_CORE_LOCKING */ + do_writemore(msg->conn); +#endif /* LWIP_TCPIP_CORE_LOCKING */ + /* for both cases: if do_writemore was called, don't ACK the APIMSG + since do_writemore ACKs it! */ + return; + } else { + msg->err = ERR_CONN; + } +#else /* LWIP_TCP */ + msg->err = ERR_VAL; +#endif /* LWIP_TCP */ +#if (LWIP_UDP || LWIP_RAW) + } else { + msg->err = ERR_VAL; +#endif /* (LWIP_UDP || LWIP_RAW) */ + } + } + TCPIP_APIMSG_ACK(msg); +} + +/** + * Return a connection's local or remote address + * Called from netconn_getaddr + * + * @param msg the api_msg_msg pointing to the connection + */ +void +do_getaddr(struct api_msg_msg *msg) +{ + if (msg->conn->pcb.ip != NULL) { + *(msg->msg.ad.ipaddr) = (msg->msg.ad.local ? msg->conn->pcb.ip->local_ip : + msg->conn->pcb.ip->remote_ip); + + msg->err = ERR_OK; + switch (NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + if (msg->msg.ad.local) { + *(msg->msg.ad.port) = msg->conn->pcb.raw->protocol; + } else { + /* return an error as connecting is only a helper for upper layers */ + msg->err = ERR_CONN; + } + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + if (msg->msg.ad.local) { + *(msg->msg.ad.port) = msg->conn->pcb.udp->local_port; + } else { + if ((msg->conn->pcb.udp->flags & UDP_FLAGS_CONNECTED) == 0) { + msg->err = ERR_CONN; + } else { + *(msg->msg.ad.port) = msg->conn->pcb.udp->remote_port; + } + } + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + *(msg->msg.ad.port) = (msg->msg.ad.local?msg->conn->pcb.tcp->local_port:msg->conn->pcb.tcp->remote_port); + break; +#endif /* LWIP_TCP */ + default: + LWIP_ASSERT("invalid netconn_type", 0); + break; + } + } else { + msg->err = ERR_CONN; + } + TCPIP_APIMSG_ACK(msg); +} + +/** + * Close a TCP pcb contained in a netconn + * Called from netconn_close + * + * @param msg the api_msg_msg pointing to the connection + */ +void +do_close(struct api_msg_msg *msg) +{ +#if LWIP_TCP + /* @todo: abort running write/connect? */ + if ((msg->conn->state != NETCONN_NONE) && (msg->conn->state != NETCONN_LISTEN)) { + /* this only happens for TCP netconns */ + LWIP_ASSERT("msg->conn->type == NETCONN_TCP", msg->conn->type == NETCONN_TCP); + msg->err = ERR_INPROGRESS; + } else if ((msg->conn->pcb.tcp != NULL) && (msg->conn->type == NETCONN_TCP)) { + if ((msg->msg.sd.shut != NETCONN_SHUT_RDWR) && (msg->conn->state == NETCONN_LISTEN)) { + /* LISTEN doesn't support half shutdown */ + msg->err = ERR_CONN; + } else { + if (msg->msg.sd.shut & NETCONN_SHUT_RD) { + /* Drain and delete mboxes */ + netconn_drain(msg->conn); + } + LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL && + msg->conn->write_offset == 0); + msg->conn->state = NETCONN_CLOSE; + msg->conn->current_msg = msg; + do_close_internal(msg->conn); + /* for tcp netconns, do_close_internal ACKs the message */ + return; + } + } else +#endif /* LWIP_TCP */ + { + msg->err = ERR_VAL; + } + sys_sem_signal(&msg->conn->op_completed); +} + +#if LWIP_IGMP +/** + * Join multicast groups for UDP netconns. + * Called from netconn_join_leave_group + * + * @param msg the api_msg_msg pointing to the connection + */ +void +do_join_leave_group(struct api_msg_msg *msg) +{ + if (ERR_IS_FATAL(msg->conn->last_err)) { + msg->err = msg->conn->last_err; + } else { + if (msg->conn->pcb.tcp != NULL) { + if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) { +#if LWIP_UDP + if (msg->msg.jl.join_or_leave == NETCONN_JOIN) { + msg->err = igmp_joingroup(msg->msg.jl.netif_addr, msg->msg.jl.multiaddr); + } else { + msg->err = igmp_leavegroup(msg->msg.jl.netif_addr, msg->msg.jl.multiaddr); + } +#endif /* LWIP_UDP */ +#if (LWIP_TCP || LWIP_RAW) + } else { + msg->err = ERR_VAL; +#endif /* (LWIP_TCP || LWIP_RAW) */ + } + } else { + msg->err = ERR_CONN; + } + } + TCPIP_APIMSG_ACK(msg); +} +#endif /* LWIP_IGMP */ + +#if LWIP_DNS +/** + * Callback function that is called when DNS name is resolved + * (or on timeout). A waiting application thread is waked up by + * signaling the semaphore. + */ +static void +do_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) +{ + struct dns_api_msg *msg = (struct dns_api_msg*)arg; + + LWIP_ASSERT("DNS response for wrong host name", strcmp(msg->name, name) == 0); + LWIP_UNUSED_ARG(name); + + if (ipaddr == NULL) { + /* timeout or memory error */ + *msg->err = ERR_VAL; + } else { + /* address was resolved */ + *msg->err = ERR_OK; + *msg->addr = *ipaddr; + } + /* wake up the application task waiting in netconn_gethostbyname */ + sys_sem_signal(msg->sem); +} + +/** + * Execute a DNS query + * Called from netconn_gethostbyname + * + * @param arg the dns_api_msg pointing to the query + */ +void +do_gethostbyname(void *arg) +{ + struct dns_api_msg *msg = (struct dns_api_msg*)arg; + + *msg->err = dns_gethostbyname(msg->name, msg->addr, do_dns_found, msg); + if (*msg->err != ERR_INPROGRESS) { + /* on error or immediate success, wake up the application + * task waiting in netconn_gethostbyname */ + sys_sem_signal(msg->sem); + } +} +#endif /* LWIP_DNS */ + +#endif /* LWIP_NETCONN */ diff --git a/variants/lwip/lwip/api/err.c b/variants/lwip/lwip/api/err.c new file mode 100755 index 000000000..b0a4eb3e1 --- /dev/null +++ b/variants/lwip/lwip/api/err.c @@ -0,0 +1,75 @@ +/** + * @file + * Error Management module + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/err.h" + +#ifdef LWIP_DEBUG + +static const char *err_strerr[] = { + "Ok.", /* ERR_OK 0 */ + "Out of memory error.", /* ERR_MEM -1 */ + "Buffer error.", /* ERR_BUF -2 */ + "Timeout.", /* ERR_TIMEOUT -3 */ + "Routing problem.", /* ERR_RTE -4 */ + "Operation in progress.", /* ERR_INPROGRESS -5 */ + "Illegal value.", /* ERR_VAL -6 */ + "Operation would block.", /* ERR_WOULDBLOCK -7 */ + "Connection aborted.", /* ERR_ABRT -8 */ + "Connection reset.", /* ERR_RST -9 */ + "Connection closed.", /* ERR_CLSD -10 */ + "Not connected.", /* ERR_CONN -11 */ + "Illegal argument.", /* ERR_ARG -12 */ + "Address in use.", /* ERR_USE -13 */ + "Low-level netif error.", /* ERR_IF -14 */ + "Already connected.", /* ERR_ISCONN -15 */ +}; + +/** + * Convert an lwip internal error to a string representation. + * + * @param err an lwip internal err_t + * @return a string representation for err + */ +const char * +lwip_strerr(err_t err) +{ + return err_strerr[-err]; + +} + +#endif /* LWIP_DEBUG */ diff --git a/variants/lwip/lwip/api/netbuf.c b/variants/lwip/lwip/api/netbuf.c new file mode 100755 index 000000000..886f66b05 --- /dev/null +++ b/variants/lwip/lwip/api/netbuf.c @@ -0,0 +1,245 @@ +/** + * @file + * Network buffer management + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/netbuf.h" +#include "lwip/memp.h" + +#include + +/** + * Create (allocate) and initialize a new netbuf. + * The netbuf doesn't yet contain a packet buffer! + * ÉêÇëÒ»¸öеÄnetbuf¿Õ¼ä£¬µ«²»·ÖÅäÈκÎÊý¾Ý¿Õ¼ä + * @return a pointer to a new netbuf + * NULL on lack of memory + */ +struct +netbuf *netbuf_new(void) +{ + struct netbuf *buf; + + buf = (struct netbuf *)memp_malloc(MEMP_NETBUF); + if (buf != NULL) { + buf->p = NULL; + buf->ptr = NULL; + ip_addr_set_any(&buf->addr); + buf->port = 0; +#if LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY +#if LWIP_CHECKSUM_ON_COPY + buf->flags = 0; +#endif /* LWIP_CHECKSUM_ON_COPY */ + buf->toport_chksum = 0; +#if LWIP_NETBUF_RECVINFO + ip_addr_set_any(&buf->toaddr); +#endif /* LWIP_NETBUF_RECVINFO */ +#endif /* LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY */ + return buf; + } else { + return NULL; + } +} + +/** + * Deallocate a netbuf allocated by netbuf_new(). + * ÊÍ·ÅÒ»¸önetbuf¿Õ¼ä + * @param buf pointer to a netbuf allocated by netbuf_new() + */ +void +netbuf_delete(struct netbuf *buf) +{ + if (buf != NULL) { + if (buf->p != NULL) { + pbuf_free(buf->p); + buf->p = buf->ptr = NULL; + } + memp_free(MEMP_NETBUF, buf); + } +} + +/** + * Allocate memory for a packet buffer for a given netbuf. + *Ϊnetbuf½á¹¹·ÖÅäsize´óСµÄÊý¾Ý¿Õ¼ä + * @param buf the netbuf for which to allocate a packet buffer + * @param size the size of the packet buffer to allocate + * @return pointer to the allocated memory + * NULL if no memory could be allocated + */ +void * +netbuf_alloc(struct netbuf *buf, u16_t size) +{ + LWIP_ERROR("netbuf_alloc: invalid buf", (buf != NULL), return NULL;); + + /* Deallocate any previously allocated memory. */ + if (buf->p != NULL) { + pbuf_free(buf->p); + } + buf->p = pbuf_alloc(PBUF_TRANSPORT, size, PBUF_RAM); + if (buf->p == NULL) { + return NULL; + } + LWIP_ASSERT("check that first pbuf can hold size", + (buf->p->len >= size)); + buf->ptr = buf->p; + return buf->p->payload; +} + +/** + * Free the packet buffer included in a netbuf + *ÊÍ·Ånetbuf½á¹¹Ö¸ÏòµÄÊý¾Ýpbuf + * @param buf pointer to the netbuf which contains the packet buffer to free + */ +void +netbuf_free(struct netbuf *buf) +{ + LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;); + if (buf->p != NULL) { + pbuf_free(buf->p); + } + buf->p = buf->ptr = NULL; +} + +/** + * Let a netbuf reference existing (non-volatile) data. + * + * @param buf netbuf which should reference the data + * @param dataptr pointer to the data to reference + * @param size size of the data + * @return ERR_OK if data is referenced + * ERR_MEM if data couldn't be referenced due to lack of memory + */ +err_t +netbuf_ref(struct netbuf *buf, const void *dataptr, u16_t size) +{ + LWIP_ERROR("netbuf_ref: invalid buf", (buf != NULL), return ERR_ARG;); + if (buf->p != NULL) { + pbuf_free(buf->p); + } + buf->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF); + if (buf->p == NULL) { + buf->ptr = NULL; + return ERR_MEM; + } + buf->p->payload = (void*)dataptr; + buf->p->len = buf->p->tot_len = size; + buf->ptr = buf->p; + return ERR_OK; +} + +/** + * Chain one netbuf to another (@see pbuf_chain) + * + * @param head the first netbuf + * @param tail netbuf to chain after head, freed by this function, may not be reference after returning + */ +void +netbuf_chain(struct netbuf *head, struct netbuf *tail) +{ + LWIP_ERROR("netbuf_ref: invalid head", (head != NULL), return;); + LWIP_ERROR("netbuf_chain: invalid tail", (tail != NULL), return;); + pbuf_cat(head->p, tail->p); + head->ptr = head->p; + memp_free(MEMP_NETBUF, tail); +} + +/** + * Get the data pointer and length of the data inside a netbuf. + * + * @param buf netbuf to get the data from + * @param dataptr pointer to a void pointer where to store the data pointer + * @param len pointer to an u16_t where the length of the data is stored + * @return ERR_OK if the information was retreived, + * ERR_BUF on error. + */ +err_t +netbuf_data(struct netbuf *buf, void **dataptr, u16_t *len) +{ + LWIP_ERROR("netbuf_data: invalid buf", (buf != NULL), return ERR_ARG;); + LWIP_ERROR("netbuf_data: invalid dataptr", (dataptr != NULL), return ERR_ARG;); + LWIP_ERROR("netbuf_data: invalid len", (len != NULL), return ERR_ARG;); + + if (buf->ptr == NULL) { + return ERR_BUF; + } + *dataptr = buf->ptr->payload; + *len = buf->ptr->len; + return ERR_OK; +} + +/** + * Move the current data pointer of a packet buffer contained in a netbuf + * to the next part. + * The packet buffer itself is not modified. + * + * @param buf the netbuf to modify + * @return -1 if there is no next part + * 1 if moved to the next part but now there is no next part + * 0 if moved to the next part and there are still more parts + */ +s8_t +netbuf_next(struct netbuf *buf) +{ + LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return -1;); + if (buf->ptr->next == NULL) { + return -1; + } + buf->ptr = buf->ptr->next; + if (buf->ptr->next == NULL) { + return 1; + } + return 0; +} + +/** + * Move the current data pointer of a packet buffer contained in a netbuf + * to the beginning of the packet. + * The packet buffer itself is not modified. + * + * @param buf the netbuf to modify + */ +void +netbuf_first(struct netbuf *buf) +{ + LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;); + buf->ptr = buf->p; +} + +#endif /* LWIP_NETCONN */ diff --git a/variants/lwip/lwip/api/netdb.c b/variants/lwip/lwip/api/netdb.c new file mode 100755 index 000000000..a7e4e06bc --- /dev/null +++ b/variants/lwip/lwip/api/netdb.c @@ -0,0 +1,352 @@ +/** + * @file + * API functions for name resolving + * + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Simon Goldschmidt + * + */ + +#include "lwip/netdb.h" + +#if LWIP_DNS && LWIP_SOCKET + +#include "lwip/err.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/ip_addr.h" +#include "lwip/api.h" +#include "lwip/dns.h" + +#include +#include + +/** helper struct for gethostbyname_r to access the char* buffer */ +struct gethostbyname_r_helper { + ip_addr_t *addrs; + ip_addr_t addr; + char *aliases; +}; + +/** h_errno is exported in netdb.h for access by applications. */ +#if LWIP_DNS_API_DECLARE_H_ERRNO +int h_errno; +#endif /* LWIP_DNS_API_DECLARE_H_ERRNO */ + +/** define "hostent" variables storage: 0 if we use a static (but unprotected) + * set of variables for lwip_gethostbyname, 1 if we use a local storage */ +#ifndef LWIP_DNS_API_HOSTENT_STORAGE +#define LWIP_DNS_API_HOSTENT_STORAGE 0 +#endif + +/** define "hostent" variables storage */ +#if LWIP_DNS_API_HOSTENT_STORAGE +#define HOSTENT_STORAGE +#else +#define HOSTENT_STORAGE static +#endif /* LWIP_DNS_API_STATIC_HOSTENT */ + +/** + * Returns an entry containing addresses of address family AF_INET + * for the host with name name. + * Due to dns_gethostbyname limitations, only one address is returned. + * + * @param name the hostname to resolve + * @return an entry containing addresses of address family AF_INET + * for the host with name name + */ +struct hostent* +lwip_gethostbyname(const char *name) +{ + err_t err; + ip_addr_t addr; + + /* buffer variables for lwip_gethostbyname() */ + HOSTENT_STORAGE struct hostent s_hostent; + HOSTENT_STORAGE char *s_aliases; + HOSTENT_STORAGE ip_addr_t s_hostent_addr; + HOSTENT_STORAGE ip_addr_t *s_phostent_addr[2]; + + /* query host IP address */ + err = netconn_gethostbyname(name, &addr); + if (err != ERR_OK) { + LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err)); + h_errno = HOST_NOT_FOUND; + return NULL; + } + + /* fill hostent */ + s_hostent_addr = addr; + s_phostent_addr[0] = &s_hostent_addr; + s_phostent_addr[1] = NULL; + s_hostent.h_name = (char*)name; + s_hostent.h_aliases = &s_aliases; + s_hostent.h_addrtype = AF_INET; + s_hostent.h_length = sizeof(ip_addr_t); + s_hostent.h_addr_list = (char**)&s_phostent_addr; + +#if DNS_DEBUG + /* dump hostent */ + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_name == %s\n", s_hostent.h_name)); + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases == %p\n", s_hostent.h_aliases)); + if (s_hostent.h_aliases != NULL) { + u8_t idx; + for ( idx=0; s_hostent.h_aliases[idx]; idx++) { + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases[%i]-> == %p\n", idx, s_hostent.h_aliases[idx])); + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases[%i]-> == %s\n", idx, s_hostent.h_aliases[idx])); + } + } + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addrtype == %d\n", s_hostent.h_addrtype)); + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_length == %d\n", s_hostent.h_length)); + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list == %p\n", s_hostent.h_addr_list)); + if (s_hostent.h_addr_list != NULL) { + u8_t idx; + for ( idx=0; s_hostent.h_addr_list[idx]; idx++) { + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i] == %p\n", idx, s_hostent.h_addr_list[idx])); + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i]-> == %s\n", idx, ip_ntoa((ip_addr_t*)s_hostent.h_addr_list[idx]))); + } + } +#endif /* DNS_DEBUG */ + +#if LWIP_DNS_API_HOSTENT_STORAGE + /* this function should return the "per-thread" hostent after copy from s_hostent */ + return sys_thread_hostent(&s_hostent); +#else + return &s_hostent; +#endif /* LWIP_DNS_API_HOSTENT_STORAGE */ +} + +/** + * Thread-safe variant of lwip_gethostbyname: instead of using a static + * buffer, this function takes buffer and errno pointers as arguments + * and uses these for the result. + * + * @param name the hostname to resolve + * @param ret pre-allocated struct where to store the result + * @param buf pre-allocated buffer where to store additional data + * @param buflen the size of buf + * @param result pointer to a hostent pointer that is set to ret on success + * and set to zero on error + * @param h_errnop pointer to an int where to store errors (instead of modifying + * the global h_errno) + * @return 0 on success, non-zero on error, additional error information + * is stored in *h_errnop instead of h_errno to be thread-safe + */ +int +lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf, + size_t buflen, struct hostent **result, int *h_errnop) +{ + err_t err; + struct gethostbyname_r_helper *h; + char *hostname; + size_t namelen; + int lh_errno; + + if (h_errnop == NULL) { + /* ensure h_errnop is never NULL */ + h_errnop = &lh_errno; + } + + if (result == NULL) { + /* not all arguments given */ + *h_errnop = EINVAL; + return -1; + } + /* first thing to do: set *result to nothing */ + *result = NULL; + if ((name == NULL) || (ret == NULL) || (buf == 0)) { + /* not all arguments given */ + *h_errnop = EINVAL; + return -1; + } + + namelen = strlen(name); + if (buflen < (sizeof(struct gethostbyname_r_helper) + namelen + 1 + (MEM_ALIGNMENT - 1))) { + /* buf can't hold the data needed + a copy of name */ + *h_errnop = ERANGE; + return -1; + } + + h = (struct gethostbyname_r_helper*)LWIP_MEM_ALIGN(buf); + hostname = ((char*)h) + sizeof(struct gethostbyname_r_helper); + + /* query host IP address */ + err = netconn_gethostbyname(name, &(h->addr)); + if (err != ERR_OK) { + LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err)); + *h_errnop = ENSRNOTFOUND; + return -1; + } + + /* copy the hostname into buf */ + MEMCPY(hostname, name, namelen); + hostname[namelen] = 0; + + /* fill hostent */ + h->addrs = &(h->addr); + h->aliases = NULL; + ret->h_name = (char*)hostname; + ret->h_aliases = &(h->aliases); + ret->h_addrtype = AF_INET; + ret->h_length = sizeof(ip_addr_t); + ret->h_addr_list = (char**)&(h->addrs); + + /* set result != NULL */ + *result = ret; + + /* return success */ + return 0; +} + +/** + * Frees one or more addrinfo structures returned by getaddrinfo(), along with + * any additional storage associated with those structures. If the ai_next field + * of the structure is not null, the entire list of structures is freed. + * + * @param ai struct addrinfo to free + */ +void +lwip_freeaddrinfo(struct addrinfo *ai) +{ + struct addrinfo *next; + + while (ai != NULL) { + next = ai->ai_next; + memp_free(MEMP_NETDB, ai); + ai = next; + } +} + +/** + * Translates the name of a service location (for example, a host name) and/or + * a service name and returns a set of socket addresses and associated + * information to be used in creating a socket with which to address the + * specified service. + * Memory for the result is allocated internally and must be freed by calling + * lwip_freeaddrinfo()! + * + * Due to a limitation in dns_gethostbyname, only the first address of a + * host is returned. + * Also, service names are not supported (only port numbers)! + * + * @param nodename descriptive name or address string of the host + * (may be NULL -> local address) + * @param servname port number as string of NULL + * @param hints structure containing input values that set socktype and protocol + * @param res pointer to a pointer where to store the result (set to NULL on failure) + * @return 0 on success, non-zero on failure + */ +int +lwip_getaddrinfo(const char *nodename, const char *servname, + const struct addrinfo *hints, struct addrinfo **res) +{ + err_t err; + ip_addr_t addr; + struct addrinfo *ai; + struct sockaddr_in *sa = NULL; + int port_nr = 0; + size_t total_size; + size_t namelen = 0; + + if (res == NULL) { + return EAI_FAIL; + } + *res = NULL; + if ((nodename == NULL) && (servname == NULL)) { + return EAI_NONAME; + } + + if (servname != NULL) { + /* service name specified: convert to port number + * @todo?: currently, only ASCII integers (port numbers) are supported! */ + port_nr = atoi(servname); + if ((port_nr <= 0) || (port_nr > 0xffff)) { + return EAI_SERVICE; + } + } + + if (nodename != NULL) { + /* service location specified, try to resolve */ + err = netconn_gethostbyname(nodename, &addr); + if (err != ERR_OK) { + return EAI_FAIL; + } + } else { + /* service location specified, use loopback address */ + ip_addr_set_loopback(&addr); + } + + total_size = sizeof(struct addrinfo) + sizeof(struct sockaddr_in); + if (nodename != NULL) { + namelen = strlen(nodename); + LWIP_ASSERT("namelen is too long", (namelen + 1) <= (mem_size_t)-1); + total_size += namelen + 1; + } + /* If this fails, please report to lwip-devel! :-) */ + LWIP_ASSERT("total_size <= NETDB_ELEM_SIZE: please report this!", + total_size <= NETDB_ELEM_SIZE); + ai = (struct addrinfo *)memp_malloc(MEMP_NETDB); + if (ai == NULL) { + goto memerr; + } + memset(ai, 0, total_size); + sa = (struct sockaddr_in*)((u8_t*)ai + sizeof(struct addrinfo)); + /* set up sockaddr */ + inet_addr_from_ipaddr(&sa->sin_addr, &addr); + sa->sin_family = AF_INET; + sa->sin_len = sizeof(struct sockaddr_in); + sa->sin_port = htons((u16_t)port_nr); + + /* set up addrinfo */ + ai->ai_family = AF_INET; + if (hints != NULL) { + /* copy socktype & protocol from hints if specified */ + ai->ai_socktype = hints->ai_socktype; + ai->ai_protocol = hints->ai_protocol; + } + if (nodename != NULL) { + /* copy nodename to canonname if specified */ + ai->ai_canonname = ((char*)ai + sizeof(struct addrinfo) + sizeof(struct sockaddr_in)); + MEMCPY(ai->ai_canonname, nodename, namelen); + ai->ai_canonname[namelen] = 0; + } + ai->ai_addrlen = sizeof(struct sockaddr_in); + ai->ai_addr = (struct sockaddr*)sa; + + *res = ai; + + return 0; +memerr: + if (ai != NULL) { + memp_free(MEMP_NETDB, ai); + } + return EAI_MEMORY; +} + +#endif /* LWIP_DNS && LWIP_SOCKET */ diff --git a/variants/lwip/lwip/api/netifapi.c b/variants/lwip/lwip/api/netifapi.c new file mode 100755 index 000000000..43e47203a --- /dev/null +++ b/variants/lwip/lwip/api/netifapi.c @@ -0,0 +1,160 @@ +/** + * @file + * Network Interface Sequential API module + * + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#include "lwip/opt.h" + +#if LWIP_NETIF_API /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/netifapi.h" +#include "lwip/tcpip.h" + +/** + * Call netif_add() inside the tcpip_thread context. + */ +void +do_netifapi_netif_add(struct netifapi_msg_msg *msg) +{ + if (!netif_add( msg->netif, + msg->msg.add.ipaddr, + msg->msg.add.netmask, + msg->msg.add.gw, + msg->msg.add.state, + msg->msg.add.init, + msg->msg.add.input)) { + msg->err = ERR_IF; + } else { + msg->err = ERR_OK; + } + TCPIP_NETIFAPI_ACK(msg); +} + +/** + * Call netif_set_addr() inside the tcpip_thread context. + */ +void +do_netifapi_netif_set_addr(struct netifapi_msg_msg *msg) +{ + netif_set_addr( msg->netif, + msg->msg.add.ipaddr, + msg->msg.add.netmask, + msg->msg.add.gw); + msg->err = ERR_OK; + TCPIP_NETIFAPI_ACK(msg); +} + +/** + * Call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) inside the + * tcpip_thread context. + */ +void +do_netifapi_netif_common(struct netifapi_msg_msg *msg) +{ + if (msg->msg.common.errtfunc != NULL) { + msg->err = msg->msg.common.errtfunc(msg->netif); + } else { + msg->err = ERR_OK; + msg->msg.common.voidfunc(msg->netif); + } + TCPIP_NETIFAPI_ACK(msg); +} + +/** + * Call netif_add() in a thread-safe way by running that function inside the + * tcpip_thread context. + * + * @note for params @see netif_add() + */ +err_t +netifapi_netif_add(struct netif *netif, + ip_addr_t *ipaddr, + ip_addr_t *netmask, + ip_addr_t *gw, + void *state, + netif_init_fn init, + netif_input_fn input) +{ + struct netifapi_msg msg; + msg.function = do_netifapi_netif_add; + msg.msg.netif = netif; + msg.msg.msg.add.ipaddr = ipaddr; + msg.msg.msg.add.netmask = netmask; + msg.msg.msg.add.gw = gw; + msg.msg.msg.add.state = state; + msg.msg.msg.add.init = init; + msg.msg.msg.add.input = input; + TCPIP_NETIFAPI(&msg); + return msg.msg.err; +} + +/** + * Call netif_set_addr() in a thread-safe way by running that function inside the + * tcpip_thread context. + * + * @note for params @see netif_set_addr() + */ +err_t +netifapi_netif_set_addr(struct netif *netif, + ip_addr_t *ipaddr, + ip_addr_t *netmask, + ip_addr_t *gw) +{ + struct netifapi_msg msg; + msg.function = do_netifapi_netif_set_addr; + msg.msg.netif = netif; + msg.msg.msg.add.ipaddr = ipaddr; + msg.msg.msg.add.netmask = netmask; + msg.msg.msg.add.gw = gw; + TCPIP_NETIFAPI(&msg); + return msg.msg.err; +} + +/** + * call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) in a thread-safe + * way by running that function inside the tcpip_thread context. + * + * @note use only for functions where there is only "netif" parameter. + */ +err_t +netifapi_netif_common(struct netif *netif, netifapi_void_fn voidfunc, + netifapi_errt_fn errtfunc) +{ + struct netifapi_msg msg; + msg.function = do_netifapi_netif_common; + msg.msg.netif = netif; + msg.msg.msg.common.voidfunc = voidfunc; + msg.msg.msg.common.errtfunc = errtfunc; + TCPIP_NETIFAPI(&msg); + return msg.msg.err; +} + +#endif /* LWIP_NETIF_API */ diff --git a/variants/lwip/lwip/api/sockets.c b/variants/lwip/lwip/api/sockets.c new file mode 100755 index 000000000..f3afd6306 --- /dev/null +++ b/variants/lwip/lwip/api/sockets.c @@ -0,0 +1,2343 @@ +/** + * @file + * Sockets BSD-Like API module + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + * Improved by Marc Boucher and David Haas + * + */ + +#include "lwip/opt.h" + +#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/sockets.h" +#include "lwip/api.h" +#include "lwip/sys.h" +#include "lwip/igmp.h" +#include "lwip/inet.h" +#include "lwip/tcp.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/tcpip.h" +#include "lwip/pbuf.h" +#if LWIP_CHECKSUM_ON_COPY +#include "lwip/inet_chksum.h" +#endif + +#include + +#define NUM_SOCKETS MEMP_NUM_NETCONN + +/** Contains all internal pointers and states used for a socket */ +struct lwip_sock { + /** sockets currently are built on netconns, each socket has one netconn */ + struct netconn *conn; + /** data that was left from the previous read */ + void *lastdata; + /** offset in the data that was left from the previous read */ + u16_t lastoffset; + /** number of times data was received, set by event_callback(), + tested by the receive and select functions */ + s16_t rcvevent; + /** number of times data was ACKed (free send buffer), set by event_callback(), + tested by select */ + u16_t sendevent; + /** error happened for this socket, set by event_callback(), tested by select */ + u16_t errevent; + /** last error that occurred on this socket */ + int err; + /** counter of how many threads are waiting for this socket using select */ + int select_waiting; +}; + +/** Description for a task waiting in select */ +struct lwip_select_cb { + /** Pointer to the next waiting task */ + struct lwip_select_cb *next; + /** Pointer to the previous waiting task */ + struct lwip_select_cb *prev; + /** readset passed to select */ + fd_set *readset; + /** writeset passed to select */ + fd_set *writeset; + /** unimplemented: exceptset passed to select */ + fd_set *exceptset; + /** don't signal the same semaphore twice: set to 1 when signalled */ + int sem_signalled; + /** semaphore to wake up a task waiting for select */ + sys_sem_t sem; +}; + +/** This struct is used to pass data to the set/getsockopt_internal + * functions running in tcpip_thread context (only a void* is allowed) */ +struct lwip_setgetsockopt_data { + /** socket struct for which to change options */ + struct lwip_sock *sock; +#ifdef LWIP_DEBUG + /** socket index for which to change options */ + int s; +#endif /* LWIP_DEBUG */ + /** level of the option to process */ + int level; + /** name of the option to process */ + int optname; + /** set: value to set the option to + * get: value of the option is stored here */ + void *optval; + /** size of *optval */ + socklen_t *optlen; + /** if an error occures, it is temporarily stored here */ + err_t err; +}; + +/** The global array of available sockets */ +static struct lwip_sock sockets[NUM_SOCKETS]; +/** The global list of tasks waiting for select */ +static struct lwip_select_cb *select_cb_list; +/** This counter is increased from lwip_select when the list is chagned + and checked in event_callback to see if it has changed. */ +static volatile int select_cb_ctr; + +/** Table to quickly map an lwIP error (err_t) to a socket error + * by using -err as an index */ +static const int err_to_errno_table[] = { + 0, /* ERR_OK 0 No error, everything OK. */ + ENOMEM, /* ERR_MEM -1 Out of memory error. */ + ENOBUFS, /* ERR_BUF -2 Buffer error. */ + EWOULDBLOCK, /* ERR_TIMEOUT -3 Timeout */ + EHOSTUNREACH, /* ERR_RTE -4 Routing problem. */ + EINPROGRESS, /* ERR_INPROGRESS -5 Operation in progress */ + EINVAL, /* ERR_VAL -6 Illegal value. */ + EWOULDBLOCK, /* ERR_WOULDBLOCK -7 Operation would block. */ + ECONNABORTED, /* ERR_ABRT -8 Connection aborted. */ + ECONNRESET, /* ERR_RST -9 Connection reset. */ + ESHUTDOWN, /* ERR_CLSD -10 Connection closed. */ + ENOTCONN, /* ERR_CONN -11 Not connected. */ + EIO, /* ERR_ARG -12 Illegal argument. */ + EADDRINUSE, /* ERR_USE -13 Address in use. */ + -1, /* ERR_IF -14 Low-level netif error */ + -1, /* ERR_ISCONN -15 Already connected. */ +}; + +#define ERR_TO_ERRNO_TABLE_SIZE \ + (sizeof(err_to_errno_table)/sizeof(err_to_errno_table[0])) + +#define err_to_errno(err) \ + ((unsigned)(-(err)) < ERR_TO_ERRNO_TABLE_SIZE ? \ + err_to_errno_table[-(err)] : EIO) + +#ifdef ERRNO +#ifndef set_errno +#define set_errno(err) errno = (err) +#endif +#else /* ERRNO */ +#define set_errno(err) +#endif /* ERRNO */ + +#define sock_set_errno(sk, e) do { \ + sk->err = (e); \ + set_errno(sk->err); \ +} while (0) + +/* Forward delcaration of some functions */ +static void event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len); +static void lwip_getsockopt_internal(void *arg); +static void lwip_setsockopt_internal(void *arg); + +/** + * Initialize this module. This function has to be called before any other + * functions in this module! + */ +void +lwip_socket_init(void) +{ +} + +/** + * Map a externally used socket index to the internal socket representation. + * + * @param s externally used socket index + * @return struct lwip_sock for the socket or NULL if not found + */ +static struct lwip_sock * +get_socket(int s) +{ + struct lwip_sock *sock; + + if ((s < 0) || (s >= NUM_SOCKETS)) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): invalid\n", s)); + set_errno(EBADF); + return NULL; + } + + sock = &sockets[s]; + + if (!sock->conn) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): not active\n", s)); + set_errno(EBADF); + return NULL; + } + + return sock; +} + +/** + * Same as get_socket but doesn't set errno + * + * @param s externally used socket index + * @return struct lwip_sock for the socket or NULL if not found + */ +static struct lwip_sock * +tryget_socket(int s) +{ + if ((s < 0) || (s >= NUM_SOCKETS)) { + return NULL; + } + if (!sockets[s].conn) { + return NULL; + } + return &sockets[s]; +} + +/** + * Allocate a new socket for a given netconn. + * + * @param newconn the netconn for which to allocate a socket + * @param accepted 1 if socket has been created by accept(), + * 0 if socket has been created by socket() + * @return the index of the new socket; -1 on error + */ +static int +alloc_socket(struct netconn *newconn, int accepted) +{ + int i; + SYS_ARCH_DECL_PROTECT(lev); + + /* allocate a new socket identifier */ + for (i = 0; i < NUM_SOCKETS; ++i) { + /* Protect socket array */ + SYS_ARCH_PROTECT(lev); + if (!sockets[i].conn) { + sockets[i].conn = newconn; + /* The socket is not yet known to anyone, so no need to protect + after having marked it as used. */ + SYS_ARCH_UNPROTECT(lev); + sockets[i].lastdata = NULL; + sockets[i].lastoffset = 0; + sockets[i].rcvevent = 0; + /* TCP sendbuf is empty, but the socket is not yet writable until connected + * (unless it has been created by accept()). */ + sockets[i].sendevent = (newconn->type == NETCONN_TCP ? (accepted != 0) : 1); + sockets[i].errevent = 0; + sockets[i].err = 0; + sockets[i].select_waiting = 0; + return i; + } + SYS_ARCH_UNPROTECT(lev); + } + return -1; +} + +/** Free a socket. The socket's netconn must have been + * delete before! + * + * @param sock the socket to free + * @param is_tcp != 0 for TCP sockets, used to free lastdata + */ +static void +free_socket(struct lwip_sock *sock, int is_tcp) +{ + void *lastdata; + SYS_ARCH_DECL_PROTECT(lev); + + lastdata = sock->lastdata; + sock->lastdata = NULL; + sock->lastoffset = 0; + sock->err = 0; + + /* Protect socket array */ + SYS_ARCH_PROTECT(lev); + sock->conn = NULL; + SYS_ARCH_UNPROTECT(lev); + /* don't use 'sock' after this line, as another task might have allocated it */ + + if (lastdata != NULL) { + if (is_tcp) { + pbuf_free((struct pbuf *)lastdata); + } else { + netbuf_delete((struct netbuf *)lastdata); + } + } +} + +/* Below this, the well-known socket functions are implemented. + * Use google.com or opengroup.org to get a good description :-) + * + * Exceptions are documented! + */ + +int +lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen) +{ + struct lwip_sock *sock, *nsock; + struct netconn *newconn; + ip_addr_t naddr; + u16_t port; + int newsock; + struct sockaddr_in sin; + err_t err; + SYS_ARCH_DECL_PROTECT(lev); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d)...\n", s)); + sock = get_socket(s); + if (!sock) { + return -1; + } + + if (netconn_is_nonblocking(sock->conn) && (sock->rcvevent <= 0)) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): returning EWOULDBLOCK\n", s)); + sock_set_errno(sock, EWOULDBLOCK); + return -1; + } + + /* wait for a new connection */ + err = netconn_accept(sock->conn, &newconn); + if (err != ERR_OK) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_acept failed, err=%d\n", s, err)); + sock_set_errno(sock, err_to_errno(err)); + return -1; + } + LWIP_ASSERT("newconn != NULL", newconn != NULL); + /* Prevent automatic window updates, we do this on our own! */ + netconn_set_noautorecved(newconn, 1); + + /* get the IP address and port of the remote host */ + err = netconn_peer(newconn, &naddr, &port); + if (err != ERR_OK) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_peer failed, err=%d\n", s, err)); + netconn_delete(newconn); + sock_set_errno(sock, err_to_errno(err)); + return -1; + } + + /* Note that POSIX only requires us to check addr is non-NULL. addrlen must + * not be NULL if addr is valid. + */ + if (NULL != addr) { + LWIP_ASSERT("addr valid but addrlen NULL", addrlen != NULL); + memset(&sin, 0, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + inet_addr_from_ipaddr(&sin.sin_addr, &naddr); + + if (*addrlen > sizeof(sin)) + *addrlen = sizeof(sin); + + MEMCPY(addr, &sin, *addrlen); + } + + newsock = alloc_socket(newconn, 1); + if (newsock == -1) { + netconn_delete(newconn); + sock_set_errno(sock, ENFILE); + return -1; + } + LWIP_ASSERT("invalid socket index", (newsock >= 0) && (newsock < NUM_SOCKETS)); + LWIP_ASSERT("newconn->callback == event_callback", newconn->callback == event_callback); + nsock = &sockets[newsock]; + + /* See event_callback: If data comes in right away after an accept, even + * though the server task might not have created a new socket yet. + * In that case, newconn->socket is counted down (newconn->socket--), + * so nsock->rcvevent is >= 1 here! + */ + SYS_ARCH_PROTECT(lev); + nsock->rcvevent += (s16_t)(-1 - newconn->socket); + newconn->socket = newsock; + SYS_ARCH_UNPROTECT(lev); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) returning new sock=%d addr=", s, newsock)); + ip_addr_debug_print(SOCKETS_DEBUG, &naddr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", port)); + + sock_set_errno(sock, 0); + return newsock; +} + +int +lwip_bind(int s, const struct sockaddr *name, socklen_t namelen) +{ + struct lwip_sock *sock; + ip_addr_t local_addr; + u16_t local_port; + err_t err; + const struct sockaddr_in *name_in; + + sock = get_socket(s); + if (!sock) { + return -1; + } + + /* check size, familiy and alignment of 'name' */ + LWIP_ERROR("lwip_bind: invalid address", ((namelen == sizeof(struct sockaddr_in)) && + ((name->sa_family) == AF_INET) && ((((mem_ptr_t)name) % 4) == 0)), + sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); + name_in = (const struct sockaddr_in *)(void*)name; + + inet_addr_to_ipaddr(&local_addr, &name_in->sin_addr); + local_port = name_in->sin_port; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d, addr=", s)); + ip_addr_debug_print(SOCKETS_DEBUG, &local_addr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", ntohs(local_port))); + + err = netconn_bind(sock->conn, &local_addr, ntohs(local_port)); + + if (err != ERR_OK) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) failed, err=%d\n", s, err)); + sock_set_errno(sock, err_to_errno(err)); + return -1; + } + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) succeeded\n", s)); + sock_set_errno(sock, 0); + return 0; +} + +int +lwip_close(int s) +{ + struct lwip_sock *sock; + int is_tcp = 0; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_close(%d)\n", s)); + + sock = get_socket(s); + if (!sock) { + return -1; + } + + if(sock->conn != NULL) { + is_tcp = netconn_type(sock->conn) == NETCONN_TCP; + } else { + LWIP_ASSERT("sock->lastdata == NULL", sock->lastdata == NULL); + } + + netconn_delete(sock->conn); + + free_socket(sock, is_tcp); + set_errno(0); + return 0; +} + +int +lwip_connect(int s, const struct sockaddr *name, socklen_t namelen) +{ + struct lwip_sock *sock; + err_t err; + const struct sockaddr_in *name_in; + + sock = get_socket(s); + if (!sock) { + return -1; + } + + /* check size, familiy and alignment of 'name' */ + LWIP_ERROR("lwip_connect: invalid address", ((namelen == sizeof(struct sockaddr_in)) && + ((name->sa_family) == AF_INET) && ((((mem_ptr_t)name) % 4) == 0)), + sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); + name_in = (const struct sockaddr_in *)(void*)name; + + if (name_in->sin_family == AF_UNSPEC) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, AF_UNSPEC)\n", s)); + err = netconn_disconnect(sock->conn); + } else { + ip_addr_t remote_addr; + u16_t remote_port; + + inet_addr_to_ipaddr(&remote_addr, &name_in->sin_addr); + remote_port = name_in->sin_port; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, addr=", s)); + ip_addr_debug_print(SOCKETS_DEBUG, &remote_addr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", ntohs(remote_port))); + + err = netconn_connect(sock->conn, &remote_addr, ntohs(remote_port)); + } + + if (err != ERR_OK) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) failed, err=%d\n", s, err)); + sock_set_errno(sock, err_to_errno(err)); + return -1; + } + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) succeeded\n", s)); + sock_set_errno(sock, 0); + return 0; +} + +/** + * Set a socket into listen mode. + * The socket may not have been used for another connection previously. + * + * @param s the socket to set to listening mode + * @param backlog (ATTENTION: needs TCP_LISTEN_BACKLOG=1) + * @return 0 on success, non-zero on failure + */ +int +lwip_listen(int s, int backlog) +{ + struct lwip_sock *sock; + err_t err; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d, backlog=%d)\n", s, backlog)); + + sock = get_socket(s); + if (!sock) { + return -1; + } + + /* limit the "backlog" parameter to fit in an u8_t */ + backlog = LWIP_MIN(LWIP_MAX(backlog, 0), 0xff); + + err = netconn_listen_with_backlog(sock->conn, (u8_t)backlog); + + if (err != ERR_OK) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d) failed, err=%d\n", s, err)); + sock_set_errno(sock, err_to_errno(err)); + return -1; + } + + sock_set_errno(sock, 0); + return 0; +} + +int +lwip_recvfrom(int s, void *mem, size_t len, int flags, + struct sockaddr *from, socklen_t *fromlen) +{ + struct lwip_sock *sock; + void *buf = NULL; + struct pbuf *p; + u16_t buflen, copylen; + int off = 0; + ip_addr_t *addr; + u16_t port; + u8_t done = 0; + err_t err; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d, %p, %"SZT_F", 0x%x, ..)\n", s, mem, len, flags)); + sock = get_socket(s); + if (!sock) { + return -1; + } + + do { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: top while sock->lastdata=%p\n", sock->lastdata)); + /* Check if there is data left from the last recv operation. */ + if (sock->lastdata) { + buf = sock->lastdata; + } else { + /* If this is non-blocking call, then check first */ + if (((flags & MSG_DONTWAIT) || netconn_is_nonblocking(sock->conn)) && + (sock->rcvevent <= 0)) { + if (off > 0) { + /* update receive window */ + netconn_recved(sock->conn, (u32_t)off); + /* already received data, return that */ + sock_set_errno(sock, 0); + return off; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): returning EWOULDBLOCK\n", s)); + sock_set_errno(sock, EWOULDBLOCK); + return -1; + } + + /* No data was left from the previous operation, so we try to get + some from the network. */ + if (netconn_type(sock->conn) == NETCONN_TCP) { + err = netconn_recv_tcp_pbuf(sock->conn, (struct pbuf **)&buf); + } else { + err = netconn_recv(sock->conn, (struct netbuf **)&buf); + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: netconn_recv err=%d, netbuf=%p\n", + err, buf)); + + if (err != ERR_OK) { + if (off > 0) { + /* update receive window */ + netconn_recved(sock->conn, (u32_t)off); + /* already received data, return that */ + sock_set_errno(sock, 0); + return off; + } + /* We should really do some error checking here. */ + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): buf == NULL, error is \"%s\"!\n", + s, lwip_strerr(err))); + sock_set_errno(sock, err_to_errno(err)); + if (err == ERR_CLSD) { + return 0; + } else { + return -1; + } + } + LWIP_ASSERT("buf != NULL", buf != NULL); + sock->lastdata = buf; + } + + if (netconn_type(sock->conn) == NETCONN_TCP) { + p = (struct pbuf *)buf; + } else { + p = ((struct netbuf *)buf)->p; + } + buflen = p->tot_len; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: buflen=%"U16_F" len=%"SZT_F" off=%d sock->lastoffset=%"U16_F"\n", + buflen, len, off, sock->lastoffset)); + + buflen -= sock->lastoffset; + + if (len > buflen) { + copylen = buflen; + } else { + copylen = (u16_t)len; + } + + /* copy the contents of the received buffer into + the supplied memory pointer mem */ + pbuf_copy_partial(p, (u8_t*)mem + off, copylen, sock->lastoffset); + + off += copylen; + + if (netconn_type(sock->conn) == NETCONN_TCP) { + LWIP_ASSERT("invalid copylen, len would underflow", len >= copylen); + len -= copylen; + if ( (len <= 0) || + (p->flags & PBUF_FLAG_PUSH) || + (sock->rcvevent <= 0) || + ((flags & MSG_PEEK)!=0)) { + done = 1; + } + } else { + done = 1; + } + + /* Check to see from where the data was.*/ + if (done) { + ip_addr_t fromaddr; + if (from && fromlen) { + struct sockaddr_in sin; + + if (netconn_type(sock->conn) == NETCONN_TCP) { + addr = &fromaddr; + netconn_getaddr(sock->conn, addr, &port, 0); + } else { + addr = netbuf_fromaddr((struct netbuf *)buf); + port = netbuf_fromport((struct netbuf *)buf); + } + + memset(&sin, 0, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + inet_addr_from_ipaddr(&sin.sin_addr, addr); + + if (*fromlen > sizeof(sin)) { + *fromlen = sizeof(sin); + } + + MEMCPY(from, &sin, *fromlen); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): addr=", s)); + ip_addr_debug_print(SOCKETS_DEBUG, addr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", port, off)); + } else { +#if SOCKETS_DEBUG + if (netconn_type(sock->conn) == NETCONN_TCP) { + addr = &fromaddr; + netconn_getaddr(sock->conn, addr, &port, 0); + } else { + addr = netbuf_fromaddr((struct netbuf *)buf); + port = netbuf_fromport((struct netbuf *)buf); + } + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): addr=", s)); + ip_addr_debug_print(SOCKETS_DEBUG, addr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", port, off)); +#endif /* SOCKETS_DEBUG */ + } + } + + /* If we don't peek the incoming message... */ + if ((flags & MSG_PEEK) == 0) { + /* If this is a TCP socket, check if there is data left in the + buffer. If so, it should be saved in the sock structure for next + time around. */ + if ((netconn_type(sock->conn) == NETCONN_TCP) && (buflen - copylen > 0)) { + sock->lastdata = buf; + sock->lastoffset += copylen; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: lastdata now netbuf=%p\n", buf)); + } else { + sock->lastdata = NULL; + sock->lastoffset = 0; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: deleting netbuf=%p\n", buf)); + if (netconn_type(sock->conn) == NETCONN_TCP) { + pbuf_free((struct pbuf *)buf); + } else { + netbuf_delete((struct netbuf *)buf); + } + } + } + } while (!done); + + if (off > 0) { + /* update receive window */ + netconn_recved(sock->conn, (u32_t)off); + } + sock_set_errno(sock, 0); + return off; +} + +int +lwip_read(int s, void *mem, size_t len) +{ + return lwip_recvfrom(s, mem, len, 0, NULL, NULL); +} + +int +lwip_recv(int s, void *mem, size_t len, int flags) +{ + return lwip_recvfrom(s, mem, len, flags, NULL, NULL); +} + +int +lwip_send(int s, const void *data, size_t size, int flags) +{ + struct lwip_sock *sock; + err_t err; + u8_t write_flags; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d, data=%p, size=%"SZT_F", flags=0x%x)\n", + s, data, size, flags)); + + sock = get_socket(s); + if (!sock) { + return -1; + } + + if (sock->conn->type != NETCONN_TCP) { +#if (LWIP_UDP || LWIP_RAW) + return lwip_sendto(s, data, size, flags, NULL, 0); +#else /* (LWIP_UDP || LWIP_RAW) */ + sock_set_errno(sock, err_to_errno(ERR_ARG)); + return -1; +#endif /* (LWIP_UDP || LWIP_RAW) */ + } + + if ((flags & MSG_DONTWAIT) || netconn_is_nonblocking(sock->conn)) { + if ((size > TCP_SND_BUF) || ((size / TCP_MSS) > TCP_SND_QUEUELEN)) { + /* too much data to ever send nonblocking! */ + sock_set_errno(sock, EMSGSIZE); + return -1; + } + } + + write_flags = NETCONN_COPY | + ((flags & MSG_MORE) ? NETCONN_MORE : 0) | + ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0); + err = netconn_write(sock->conn, data, size, write_flags); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) err=%d size=%"SZT_F"\n", s, err, size)); + sock_set_errno(sock, err_to_errno(err)); + return (err == ERR_OK ? (int)size : -1); +} + +int +lwip_sendto(int s, const void *data, size_t size, int flags, + const struct sockaddr *to, socklen_t tolen) +{ + struct lwip_sock *sock; + err_t err; + u16_t short_size; + const struct sockaddr_in *to_in; + u16_t remote_port; +#if !LWIP_TCPIP_CORE_LOCKING + struct netbuf buf; +#endif + + sock = get_socket(s); + if (!sock) { + return -1; + } + + if (sock->conn->type == NETCONN_TCP) { +#if LWIP_TCP + return lwip_send(s, data, size, flags); +#else /* LWIP_TCP */ + LWIP_UNUSED_ARG(flags); + sock_set_errno(sock, err_to_errno(ERR_ARG)); + return -1; +#endif /* LWIP_TCP */ + } + + /* @todo: split into multiple sendto's? */ + LWIP_ASSERT("lwip_sendto: size must fit in u16_t", size <= 0xffff); + short_size = (u16_t)size; + LWIP_ERROR("lwip_sendto: invalid address", (((to == NULL) && (tolen == 0)) || + ((tolen == sizeof(struct sockaddr_in)) && + ((to->sa_family) == AF_INET) && ((((mem_ptr_t)to) % 4) == 0))), + sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); + to_in = (const struct sockaddr_in *)(void*)to; + +#if LWIP_TCPIP_CORE_LOCKING + /* Should only be consider like a sample or a simple way to experiment this option (no check of "to" field...) */ + { + struct pbuf* p; + ip_addr_t *remote_addr; + +#if LWIP_NETIF_TX_SINGLE_PBUF + p = pbuf_alloc(PBUF_TRANSPORT, short_size, PBUF_RAM); + if (p != NULL) { +#if LWIP_CHECKSUM_ON_COPY + u16_t chksum = 0; + if (sock->conn->type != NETCONN_RAW) { + chksum = LWIP_CHKSUM_COPY(p->payload, data, short_size); + } else +#endif /* LWIP_CHECKSUM_ON_COPY */ + MEMCPY(p->payload, data, size); +#else /* LWIP_NETIF_TX_SINGLE_PBUF */ + p = pbuf_alloc(PBUF_TRANSPORT, short_size, PBUF_REF); + if (p != NULL) { + p->payload = (void*)data; +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ + + if (to_in != NULL) { + inet_addr_to_ipaddr_p(remote_addr, &to_in->sin_addr); + remote_port = ntohs(to_in->sin_port); + } else { + remote_addr = IP_ADDR_ANY; + remote_port = 0; + } + + LOCK_TCPIP_CORE(); + if (sock->conn->type == NETCONN_RAW) { + err = sock->conn->last_err = raw_sendto(sock->conn->pcb.raw, p, remote_addr); + } else { +#if LWIP_UDP +#if LWIP_CHECKSUM_ON_COPY && LWIP_NETIF_TX_SINGLE_PBUF + err = sock->conn->last_err = udp_sendto_chksum(sock->conn->pcb.udp, p, + remote_addr, remote_port, 1, chksum); +#else /* LWIP_CHECKSUM_ON_COPY && LWIP_NETIF_TX_SINGLE_PBUF */ + err = sock->conn->last_err = udp_sendto(sock->conn->pcb.udp, p, + remote_addr, remote_port); +#endif /* LWIP_CHECKSUM_ON_COPY && LWIP_NETIF_TX_SINGLE_PBUF */ +#else /* LWIP_UDP */ + err = ERR_ARG; +#endif /* LWIP_UDP */ + } + UNLOCK_TCPIP_CORE(); + + pbuf_free(p); + } else { + err = ERR_MEM; + } + } +#else /* LWIP_TCPIP_CORE_LOCKING */ + /* initialize a buffer */ + buf.p = buf.ptr = NULL; +#if LWIP_CHECKSUM_ON_COPY + buf.flags = 0; +#endif /* LWIP_CHECKSUM_ON_COPY */ + if (to) { + inet_addr_to_ipaddr(&buf.addr, &to_in->sin_addr); + remote_port = ntohs(to_in->sin_port); + netbuf_fromport(&buf) = remote_port; + } else { + remote_port = 0; + ip_addr_set_any(&buf.addr); + netbuf_fromport(&buf) = 0; + } + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_sendto(%d, data=%p, short_size=%d"U16_F", flags=0x%x to=", + s, data, short_size, flags)); + ip_addr_debug_print(SOCKETS_DEBUG, &buf.addr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", remote_port)); + + /* make the buffer point to the data that should be sent */ +#if LWIP_NETIF_TX_SINGLE_PBUF + /* Allocate a new netbuf and copy the data into it. */ + if (netbuf_alloc(&buf, short_size) == NULL) { + err = ERR_MEM; + } else { +#if LWIP_CHECKSUM_ON_COPY + if (sock->conn->type != NETCONN_RAW) { + u16_t chksum = LWIP_CHKSUM_COPY(buf.p->payload, data, short_size); + netbuf_set_chksum(&buf, chksum); + err = ERR_OK; + } else +#endif /* LWIP_CHECKSUM_ON_COPY */ + { + err = netbuf_take(&buf, data, short_size); + } + } +#else /* LWIP_NETIF_TX_SINGLE_PBUF */ + err = netbuf_ref(&buf, data, short_size); +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ + if (err == ERR_OK) { + /* send the data */ + err = netconn_send(sock->conn, &buf); + } + + /* deallocated the buffer */ + netbuf_free(&buf); +#endif /* LWIP_TCPIP_CORE_LOCKING */ + sock_set_errno(sock, err_to_errno(err)); + return (err == ERR_OK ? short_size : -1); +} + +int +lwip_socket(int domain, int type, int protocol) +{ + struct netconn *conn; + int i; + + LWIP_UNUSED_ARG(domain); + + /* create a netconn */ + switch (type) { + case SOCK_RAW: + conn = netconn_new_with_proto_and_callback(NETCONN_RAW, (u8_t)protocol, event_callback); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_RAW, %d) = ", + domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol)); + break; + case SOCK_DGRAM: + conn = netconn_new_with_callback( (protocol == IPPROTO_UDPLITE) ? + NETCONN_UDPLITE : NETCONN_UDP, event_callback); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_DGRAM, %d) = ", + domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol)); + break; + case SOCK_STREAM: + conn = netconn_new_with_callback(NETCONN_TCP, event_callback); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_STREAM, %d) = ", + domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol)); + if (conn != NULL) { + /* Prevent automatic window updates, we do this on our own! */ + netconn_set_noautorecved(conn, 1); + } + break; + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%d, %d/UNKNOWN, %d) = -1\n", + domain, type, protocol)); + set_errno(EINVAL); + return -1; + } + + if (!conn) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("-1 / ENOBUFS (could not create netconn)\n")); + set_errno(ENOBUFS); + return -1; + } + + i = alloc_socket(conn, 0); + + if (i == -1) { + netconn_delete(conn); + set_errno(ENFILE); + return -1; + } + conn->socket = i; + LWIP_DEBUGF(SOCKETS_DEBUG, ("%d\n", i)); + set_errno(0); + return i; +} + +int +lwip_write(int s, const void *data, size_t size) +{ + return lwip_send(s, data, size, 0); +} + +/** + * Go through the readset and writeset lists and see which socket of the sockets + * set in the sets has events. On return, readset, writeset and exceptset have + * the sockets enabled that had events. + * + * exceptset is not used for now!!! + * + * @param maxfdp1 the highest socket index in the sets + * @param readset_in: set of sockets to check for read events + * @param writeset_in: set of sockets to check for write events + * @param exceptset_in: set of sockets to check for error events + * @param readset_out: set of sockets that had read events + * @param writeset_out: set of sockets that had write events + * @param exceptset_out: set os sockets that had error events + * @return number of sockets that had events (read/write/exception) (>= 0) + */ +static int +lwip_selscan(int maxfdp1, fd_set *readset_in, fd_set *writeset_in, fd_set *exceptset_in, + fd_set *readset_out, fd_set *writeset_out, fd_set *exceptset_out) +{ + int i, nready = 0; + fd_set lreadset, lwriteset, lexceptset; + struct lwip_sock *sock; + SYS_ARCH_DECL_PROTECT(lev); + + FD_ZERO(&lreadset); + FD_ZERO(&lwriteset); + FD_ZERO(&lexceptset); + + /* Go through each socket in each list to count number of sockets which + currently match */ + for(i = 0; i < maxfdp1; i++) { + void* lastdata = NULL; + s16_t rcvevent = 0; + u16_t sendevent = 0; + u16_t errevent = 0; + /* First get the socket's status (protected)... */ + SYS_ARCH_PROTECT(lev); + sock = tryget_socket(i); + if (sock != NULL) { + lastdata = sock->lastdata; + rcvevent = sock->rcvevent; + sendevent = sock->sendevent; + errevent = sock->errevent; + } + SYS_ARCH_UNPROTECT(lev); + /* ... then examine it: */ + /* See if netconn of this socket is ready for read */ + if (readset_in && FD_ISSET(i, readset_in) && ((lastdata != NULL) || (rcvevent > 0))) { + FD_SET(i, &lreadset); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for reading\n", i)); + nready++; + } + /* See if netconn of this socket is ready for write */ + if (writeset_in && FD_ISSET(i, writeset_in) && (sendevent != 0)) { + FD_SET(i, &lwriteset); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for writing\n", i)); + nready++; + } + /* See if netconn of this socket had an error */ + if (exceptset_in && FD_ISSET(i, exceptset_in) && (errevent != 0)) { + FD_SET(i, &lexceptset); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for exception\n", i)); + nready++; + } + } + /* copy local sets to the ones provided as arguments */ + *readset_out = lreadset; + *writeset_out = lwriteset; + *exceptset_out = lexceptset; + + LWIP_ASSERT("nready >= 0", nready >= 0); + return nready; +} + +/** + * Processing exceptset is not yet implemented. + */ +int +lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, + struct timeval *timeout) +{ + u32_t waitres = 0; + int nready; + fd_set lreadset, lwriteset, lexceptset; + u32_t msectimeout; + struct lwip_select_cb select_cb; + err_t err; + int i; + SYS_ARCH_DECL_PROTECT(lev); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select(%d, %p, %p, %p, tvsec=%"S32_F" tvusec=%"S32_F")\n", + maxfdp1, (void *)readset, (void *) writeset, (void *) exceptset, + timeout ? (s32_t)timeout->tv_sec : (s32_t)-1, + timeout ? (s32_t)timeout->tv_usec : (s32_t)-1)); + + /* Go through each socket in each list to count number of sockets which + currently match */ + nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset); + + /* If we don't have any current events, then suspend if we are supposed to */ + if (!nready) { + if (timeout && timeout->tv_sec == 0 && timeout->tv_usec == 0) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: no timeout, returning 0\n")); + /* This is OK as the local fdsets are empty and nready is zero, + or we would have returned earlier. */ + goto return_copy_fdsets; + } + + /* None ready: add our semaphore to list: + We don't actually need any dynamic memory. Our entry on the + list is only valid while we are in this function, so it's ok + to use local variables. */ + + select_cb.next = NULL; + select_cb.prev = NULL; + select_cb.readset = readset; + select_cb.writeset = writeset; + select_cb.exceptset = exceptset; + select_cb.sem_signalled = 0; + err = sys_sem_new(&select_cb.sem, 0); + if (err != ERR_OK) { + /* failed to create semaphore */ + set_errno(ENOMEM); + return -1; + } + + /* Protect the select_cb_list */ + SYS_ARCH_PROTECT(lev); + + /* Put this select_cb on top of list */ + select_cb.next = select_cb_list; + if (select_cb_list != NULL) { + select_cb_list->prev = &select_cb; + } + select_cb_list = &select_cb; + /* Increasing this counter tells even_callback that the list has changed. */ + select_cb_ctr++; + + /* Now we can safely unprotect */ + SYS_ARCH_UNPROTECT(lev); + + /* Increase select_waiting for each socket we are interested in */ + for(i = 0; i < maxfdp1; i++) { + if ((readset && FD_ISSET(i, readset)) || + (writeset && FD_ISSET(i, writeset)) || + (exceptset && FD_ISSET(i, exceptset))) { + struct lwip_sock *sock = tryget_socket(i); + LWIP_ASSERT("sock != NULL", sock != NULL); + SYS_ARCH_PROTECT(lev); + sock->select_waiting++; + LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0); + SYS_ARCH_UNPROTECT(lev); + } + } + + /* Call lwip_selscan again: there could have been events between + the last scan (whithout us on the list) and putting us on the list! */ + nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset); + if (!nready) { + /* Still none ready, just wait to be woken */ + if (timeout == 0) { + /* Wait forever */ + msectimeout = 0; + } else { + msectimeout = ((timeout->tv_sec * 1000) + ((timeout->tv_usec + 500)/1000)); + if (msectimeout == 0) { + /* Wait 1ms at least (0 means wait forever) */ + msectimeout = 1; + } + } + + waitres = sys_arch_sem_wait(&select_cb.sem, msectimeout); + } + /* Increase select_waiting for each socket we are interested in */ + for(i = 0; i < maxfdp1; i++) { + if ((readset && FD_ISSET(i, readset)) || + (writeset && FD_ISSET(i, writeset)) || + (exceptset && FD_ISSET(i, exceptset))) { + struct lwip_sock *sock = tryget_socket(i); + LWIP_ASSERT("sock != NULL", sock != NULL); + SYS_ARCH_PROTECT(lev); + sock->select_waiting--; + LWIP_ASSERT("sock->select_waiting >= 0", sock->select_waiting >= 0); + SYS_ARCH_UNPROTECT(lev); + } + } + /* Take us off the list */ + SYS_ARCH_PROTECT(lev); + if (select_cb.next != NULL) { + select_cb.next->prev = select_cb.prev; + } + if (select_cb_list == &select_cb) { + LWIP_ASSERT("select_cb.prev == NULL", select_cb.prev == NULL); + select_cb_list = select_cb.next; + } else { + LWIP_ASSERT("select_cb.prev != NULL", select_cb.prev != NULL); + select_cb.prev->next = select_cb.next; + } + /* Increasing this counter tells even_callback that the list has changed. */ + select_cb_ctr++; + SYS_ARCH_UNPROTECT(lev); + + sys_sem_free(&select_cb.sem); + if (waitres == SYS_ARCH_TIMEOUT) { + /* Timeout */ + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: timeout expired\n")); + /* This is OK as the local fdsets are empty and nready is zero, + or we would have returned earlier. */ + goto return_copy_fdsets; + } + + /* See what's set */ + nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset); + } + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: nready=%d\n", nready)); +return_copy_fdsets: + set_errno(0); + if (readset) { + *readset = lreadset; + } + if (writeset) { + *writeset = lwriteset; + } + if (exceptset) { + *exceptset = lexceptset; + } + + + return nready; +} + +/** + * Callback registered in the netconn layer for each socket-netconn. + * Processes recvevent (data available) and wakes up tasks waiting for select. + */ +static void +event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len) +{ + int s; + struct lwip_sock *sock; + struct lwip_select_cb *scb; + int last_select_cb_ctr; + SYS_ARCH_DECL_PROTECT(lev); + + LWIP_UNUSED_ARG(len); + + /* Get socket */ + if (conn) { + s = conn->socket; + if (s < 0) { + /* Data comes in right away after an accept, even though + * the server task might not have created a new socket yet. + * Just count down (or up) if that's the case and we + * will use the data later. Note that only receive events + * can happen before the new socket is set up. */ + SYS_ARCH_PROTECT(lev); + if (conn->socket < 0) { + if (evt == NETCONN_EVT_RCVPLUS) { + conn->socket--; + } + SYS_ARCH_UNPROTECT(lev); + return; + } + s = conn->socket; + SYS_ARCH_UNPROTECT(lev); + } + + sock = get_socket(s); + if (!sock) { + return; + } + } else { + return; + } + + SYS_ARCH_PROTECT(lev); + /* Set event as required */ + switch (evt) { + case NETCONN_EVT_RCVPLUS: + sock->rcvevent++; + break; + case NETCONN_EVT_RCVMINUS: + sock->rcvevent--; + break; + case NETCONN_EVT_SENDPLUS: + sock->sendevent = 1; + break; + case NETCONN_EVT_SENDMINUS: + sock->sendevent = 0; + break; + case NETCONN_EVT_ERROR: + sock->errevent = 1; + break; + default: + LWIP_ASSERT("unknown event", 0); + break; + } + + if (sock->select_waiting == 0) { + /* noone is waiting for this socket, no need to check select_cb_list */ + SYS_ARCH_UNPROTECT(lev); + return; + } + + /* Now decide if anyone is waiting for this socket */ + /* NOTE: This code goes through the select_cb_list list multiple times + ONLY IF a select was actually waiting. We go through the list the number + of waiting select calls + 1. This list is expected to be small. */ + + /* At this point, SYS_ARCH is still protected! */ +again: + for (scb = select_cb_list; scb != NULL; scb = scb->next) { + if (scb->sem_signalled == 0) { + /* semaphore not signalled yet */ + int do_signal = 0; + /* Test this select call for our socket */ + if (sock->rcvevent > 0) { + if (scb->readset && FD_ISSET(s, scb->readset)) { + do_signal = 1; + } + } + if (sock->sendevent != 0) { + if (!do_signal && scb->writeset && FD_ISSET(s, scb->writeset)) { + do_signal = 1; + } + } + if (sock->errevent != 0) { + if (!do_signal && scb->exceptset && FD_ISSET(s, scb->exceptset)) { + do_signal = 1; + } + } + if (do_signal) { + scb->sem_signalled = 1; + /* Don't call SYS_ARCH_UNPROTECT() before signaling the semaphore, as this might + lead to the select thread taking itself off the list, invalidagin the semaphore. */ + sys_sem_signal(&scb->sem); + } + } + /* unlock interrupts with each step */ + last_select_cb_ctr = select_cb_ctr; + SYS_ARCH_UNPROTECT(lev); + /* this makes sure interrupt protection time is short */ + SYS_ARCH_PROTECT(lev); + if (last_select_cb_ctr != select_cb_ctr) { + /* someone has changed select_cb_list, restart at the beginning */ + goto again; + } + } + SYS_ARCH_UNPROTECT(lev); +} + +/** + * Unimplemented: Close one end of a full-duplex connection. + * Currently, the full connection is closed. + */ +int +lwip_shutdown(int s, int how) +{ + struct lwip_sock *sock; + err_t err; + u8_t shut_rx = 0, shut_tx = 0; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_shutdown(%d, how=%d)\n", s, how)); + + sock = get_socket(s); + if (!sock) { + return -1; + } + + if (sock->conn != NULL) { + if (netconn_type(sock->conn) != NETCONN_TCP) { + sock_set_errno(sock, EOPNOTSUPP); + return EOPNOTSUPP; + } + } else { + sock_set_errno(sock, ENOTCONN); + return ENOTCONN; + } + + if (how == SHUT_RD) { + shut_rx = 1; + } else if (how == SHUT_WR) { + shut_tx = 1; + } else if(how == SHUT_RDWR) { + shut_rx = 1; + shut_tx = 1; + } else { + sock_set_errno(sock, EINVAL); + return EINVAL; + } + err = netconn_shutdown(sock->conn, shut_rx, shut_tx); + + sock_set_errno(sock, err_to_errno(err)); + return (err == ERR_OK ? 0 : -1); +} + +static int +lwip_getaddrname(int s, struct sockaddr *name, socklen_t *namelen, u8_t local) +{ + struct lwip_sock *sock; + struct sockaddr_in sin; + ip_addr_t naddr; + + sock = get_socket(s); + if (!sock) { + return -1; + } + + memset(&sin, 0, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + + /* get the IP address and port */ + netconn_getaddr(sock->conn, &naddr, &sin.sin_port, local); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getaddrname(%d, addr=", s)); + ip_addr_debug_print(SOCKETS_DEBUG, &naddr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", sin.sin_port)); + + sin.sin_port = htons(sin.sin_port); + inet_addr_from_ipaddr(&sin.sin_addr, &naddr); + + if (*namelen > sizeof(sin)) { + *namelen = sizeof(sin); + } + + MEMCPY(name, &sin, *namelen); + sock_set_errno(sock, 0); + return 0; +} + +int +lwip_getpeername(int s, struct sockaddr *name, socklen_t *namelen) +{ + return lwip_getaddrname(s, name, namelen, 0); +} + +int +lwip_getsockname(int s, struct sockaddr *name, socklen_t *namelen) +{ + return lwip_getaddrname(s, name, namelen, 1); +} + +int +lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen) +{ + err_t err = ERR_OK; + struct lwip_sock *sock = get_socket(s); + struct lwip_setgetsockopt_data data; + + if (!sock) { + return -1; + } + + if ((NULL == optval) || (NULL == optlen)) { + sock_set_errno(sock, EFAULT); + return -1; + } + + /* Do length and type checks for the various options first, to keep it readable. */ + switch (level) { + +/* Level: SOL_SOCKET */ + case SOL_SOCKET: + switch (optname) { + + case SO_ACCEPTCONN: + case SO_BROADCAST: + /* UNIMPL case SO_DEBUG: */ + /* UNIMPL case SO_DONTROUTE: */ + case SO_ERROR: + case SO_KEEPALIVE: + /* UNIMPL case SO_CONTIMEO: */ + /* UNIMPL case SO_SNDTIMEO: */ +#if LWIP_SO_RCVTIMEO + case SO_RCVTIMEO: +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + case SO_RCVBUF: +#endif /* LWIP_SO_RCVBUF */ + /* UNIMPL case SO_OOBINLINE: */ + /* UNIMPL case SO_SNDBUF: */ + /* UNIMPL case SO_RCVLOWAT: */ + /* UNIMPL case SO_SNDLOWAT: */ +#if SO_REUSE + case SO_REUSEADDR: + case SO_REUSEPORT: +#endif /* SO_REUSE */ + case SO_TYPE: + /* UNIMPL case SO_USELOOPBACK: */ + if (*optlen < sizeof(int)) { + err = EINVAL; + } + break; + + case SO_NO_CHECK: + if (*optlen < sizeof(int)) { + err = EINVAL; + } +#if LWIP_UDP + if ((sock->conn->type != NETCONN_UDP) || + ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0)) { + /* this flag is only available for UDP, not for UDP lite */ + err = EAFNOSUPPORT; + } +#endif /* LWIP_UDP */ + break; + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; + +/* Level: IPPROTO_IP */ + case IPPROTO_IP: + switch (optname) { + /* UNIMPL case IP_HDRINCL: */ + /* UNIMPL case IP_RCVDSTADDR: */ + /* UNIMPL case IP_RCVIF: */ + case IP_TTL: + case IP_TOS: + if (*optlen < sizeof(int)) { + err = EINVAL; + } + break; +#if LWIP_IGMP + case IP_MULTICAST_TTL: + if (*optlen < sizeof(u8_t)) { + err = EINVAL; + } + break; + case IP_MULTICAST_IF: + if (*optlen < sizeof(struct in_addr)) { + err = EINVAL; + } + break; + case IP_MULTICAST_LOOP: + if (*optlen < sizeof(u8_t)) { + err = EINVAL; + } + if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) { + err = EAFNOSUPPORT; + } + break; +#endif /* LWIP_IGMP */ + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; + +#if LWIP_TCP +/* Level: IPPROTO_TCP */ + case IPPROTO_TCP: + if (*optlen < sizeof(int)) { + err = EINVAL; + break; + } + + /* If this is no TCP socket, ignore any options. */ + if (sock->conn->type != NETCONN_TCP) + return 0; + + switch (optname) { + case TCP_NODELAY: + case TCP_KEEPALIVE: +#if LWIP_TCP_KEEPALIVE + case TCP_KEEPIDLE: + case TCP_KEEPINTVL: + case TCP_KEEPCNT: +#endif /* LWIP_TCP_KEEPALIVE */ + break; + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; +#endif /* LWIP_TCP */ +#if LWIP_UDP && LWIP_UDPLITE +/* Level: IPPROTO_UDPLITE */ + case IPPROTO_UDPLITE: + if (*optlen < sizeof(int)) { + err = EINVAL; + break; + } + + /* If this is no UDP lite socket, ignore any options. */ + if (sock->conn->type != NETCONN_UDPLITE) { + return 0; + } + + switch (optname) { + case UDPLITE_SEND_CSCOV: + case UDPLITE_RECV_CSCOV: + break; + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; +#endif /* LWIP_UDP && LWIP_UDPLITE*/ +/* UNDEFINED LEVEL */ + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n", + s, level, optname)); + err = ENOPROTOOPT; + } /* switch */ + + + if (err != ERR_OK) { + sock_set_errno(sock, err); + return -1; + } + + /* Now do the actual option processing */ + data.sock = sock; +#ifdef LWIP_DEBUG + data.s = s; +#endif /* LWIP_DEBUG */ + data.level = level; + data.optname = optname; + data.optval = optval; + data.optlen = optlen; + data.err = err; + tcpip_callback(lwip_getsockopt_internal, &data); + sys_arch_sem_wait(&sock->conn->op_completed, 0); + /* maybe lwip_getsockopt_internal has changed err */ + err = data.err; + + sock_set_errno(sock, err); + return err ? -1 : 0; +} + +static void +lwip_getsockopt_internal(void *arg) +{ + struct lwip_sock *sock; +#ifdef LWIP_DEBUG + int s; +#endif /* LWIP_DEBUG */ + int level, optname; + void *optval; + struct lwip_setgetsockopt_data *data; + + LWIP_ASSERT("arg != NULL", arg != NULL); + + data = (struct lwip_setgetsockopt_data*)arg; + sock = data->sock; +#ifdef LWIP_DEBUG + s = data->s; +#endif /* LWIP_DEBUG */ + level = data->level; + optname = data->optname; + optval = data->optval; + + switch (level) { + +/* Level: SOL_SOCKET */ + case SOL_SOCKET: + switch (optname) { + + /* The option flags */ + case SO_ACCEPTCONN: + case SO_BROADCAST: + /* UNIMPL case SO_DEBUG: */ + /* UNIMPL case SO_DONTROUTE: */ + case SO_KEEPALIVE: + /* UNIMPL case SO_OOBINCLUDE: */ +#if SO_REUSE + case SO_REUSEADDR: + case SO_REUSEPORT: +#endif /* SO_REUSE */ + /*case SO_USELOOPBACK: UNIMPL */ + *(int*)optval = sock->conn->pcb.ip->so_options & optname; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, optname=0x%x, ..) = %s\n", + s, optname, (*(int*)optval?"on":"off"))); + break; + + case SO_TYPE: + switch (NETCONNTYPE_GROUP(sock->conn->type)) { + case NETCONN_RAW: + *(int*)optval = SOCK_RAW; + break; + case NETCONN_TCP: + *(int*)optval = SOCK_STREAM; + break; + case NETCONN_UDP: + *(int*)optval = SOCK_DGRAM; + break; + default: /* unrecognized socket type */ + *(int*)optval = sock->conn->type; + LWIP_DEBUGF(SOCKETS_DEBUG, + ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE): unrecognized socket type %d\n", + s, *(int *)optval)); + } /* switch (sock->conn->type) */ + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE) = %d\n", + s, *(int *)optval)); + break; + + case SO_ERROR: + /* only overwrite ERR_OK or tempoary errors */ + if ((sock->err == 0) || (sock->err == EINPROGRESS)) { + sock_set_errno(sock, err_to_errno(sock->conn->last_err)); + } + *(int *)optval = sock->err; + sock->err = 0; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_ERROR) = %d\n", + s, *(int *)optval)); + break; + +#if LWIP_SO_RCVTIMEO + case SO_RCVTIMEO: + *(int *)optval = netconn_get_recvtimeout(sock->conn); + break; +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + case SO_RCVBUF: + *(int *)optval = netconn_get_recvbufsize(sock->conn); + break; +#endif /* LWIP_SO_RCVBUF */ +#if LWIP_UDP + case SO_NO_CHECK: + *(int*)optval = (udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_NOCHKSUM) ? 1 : 0; + break; +#endif /* LWIP_UDP*/ + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; + +/* Level: IPPROTO_IP */ + case IPPROTO_IP: + switch (optname) { + case IP_TTL: + *(int*)optval = sock->conn->pcb.ip->ttl; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TTL) = %d\n", + s, *(int *)optval)); + break; + case IP_TOS: + *(int*)optval = sock->conn->pcb.ip->tos; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TOS) = %d\n", + s, *(int *)optval)); + break; +#if LWIP_IGMP + case IP_MULTICAST_TTL: + *(u8_t*)optval = sock->conn->pcb.ip->ttl; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_TTL) = %d\n", + s, *(int *)optval)); + break; + case IP_MULTICAST_IF: + inet_addr_from_ipaddr((struct in_addr*)optval, &sock->conn->pcb.udp->multicast_ip); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_IF) = 0x%"X32_F"\n", + s, *(u32_t *)optval)); + break; + case IP_MULTICAST_LOOP: + if ((sock->conn->pcb.udp->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) { + *(u8_t*)optval = 1; + } else { + *(u8_t*)optval = 0; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_LOOP) = %d\n", + s, *(int *)optval)); + break; +#endif /* LWIP_IGMP */ + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; + +#if LWIP_TCP +/* Level: IPPROTO_TCP */ + case IPPROTO_TCP: + switch (optname) { + case TCP_NODELAY: + *(int*)optval = tcp_nagle_disabled(sock->conn->pcb.tcp); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_NODELAY) = %s\n", + s, (*(int*)optval)?"on":"off") ); + break; + case TCP_KEEPALIVE: + *(int*)optval = (int)sock->conn->pcb.tcp->keep_idle; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPALIVE) = %d\n", + s, *(int *)optval)); + break; + +#if LWIP_TCP_KEEPALIVE + case TCP_KEEPIDLE: + *(int*)optval = (int)(sock->conn->pcb.tcp->keep_idle/1000); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPIDLE) = %d\n", + s, *(int *)optval)); + break; + case TCP_KEEPINTVL: + *(int*)optval = (int)(sock->conn->pcb.tcp->keep_intvl/1000); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPINTVL) = %d\n", + s, *(int *)optval)); + break; + case TCP_KEEPCNT: + *(int*)optval = (int)sock->conn->pcb.tcp->keep_cnt; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPCNT) = %d\n", + s, *(int *)optval)); + break; +#endif /* LWIP_TCP_KEEPALIVE */ + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; +#endif /* LWIP_TCP */ +#if LWIP_UDP && LWIP_UDPLITE + /* Level: IPPROTO_UDPLITE */ + case IPPROTO_UDPLITE: + switch (optname) { + case UDPLITE_SEND_CSCOV: + *(int*)optval = sock->conn->pcb.udp->chksum_len_tx; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) = %d\n", + s, (*(int*)optval)) ); + break; + case UDPLITE_RECV_CSCOV: + *(int*)optval = sock->conn->pcb.udp->chksum_len_rx; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) = %d\n", + s, (*(int*)optval)) ); + break; + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; +#endif /* LWIP_UDP */ + default: + LWIP_ASSERT("unhandled level", 0); + break; + } /* switch (level) */ + sys_sem_signal(&sock->conn->op_completed); +} + +int +lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen) +{ + struct lwip_sock *sock = get_socket(s); + err_t err = ERR_OK; + struct lwip_setgetsockopt_data data; + + if (!sock) { + return -1; + } + + if (NULL == optval) { + sock_set_errno(sock, EFAULT); + return -1; + } + + /* Do length and type checks for the various options first, to keep it readable. */ + switch (level) { + +/* Level: SOL_SOCKET */ + case SOL_SOCKET: + switch (optname) { + + case SO_BROADCAST: + /* UNIMPL case SO_DEBUG: */ + /* UNIMPL case SO_DONTROUTE: */ + case SO_KEEPALIVE: + /* UNIMPL case case SO_CONTIMEO: */ + /* UNIMPL case case SO_SNDTIMEO: */ +#if LWIP_SO_RCVTIMEO + case SO_RCVTIMEO: +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + case SO_RCVBUF: +#endif /* LWIP_SO_RCVBUF */ + /* UNIMPL case SO_OOBINLINE: */ + /* UNIMPL case SO_SNDBUF: */ + /* UNIMPL case SO_RCVLOWAT: */ + /* UNIMPL case SO_SNDLOWAT: */ +#if SO_REUSE + case SO_REUSEADDR: + case SO_REUSEPORT: +#endif /* SO_REUSE */ + /* UNIMPL case SO_USELOOPBACK: */ + if (optlen < sizeof(int)) { + err = EINVAL; + } + break; + case SO_NO_CHECK: + if (optlen < sizeof(int)) { + err = EINVAL; + } +#if LWIP_UDP + if ((sock->conn->type != NETCONN_UDP) || + ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0)) { + /* this flag is only available for UDP, not for UDP lite */ + err = EAFNOSUPPORT; + } +#endif /* LWIP_UDP */ + break; + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; + +/* Level: IPPROTO_IP */ + case IPPROTO_IP: + switch (optname) { + /* UNIMPL case IP_HDRINCL: */ + /* UNIMPL case IP_RCVDSTADDR: */ + /* UNIMPL case IP_RCVIF: */ + case IP_TTL: + case IP_TOS: + if (optlen < sizeof(int)) { + err = EINVAL; + } + break; +#if LWIP_IGMP + case IP_MULTICAST_TTL: + if (optlen < sizeof(u8_t)) { + err = EINVAL; + } + if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) { + err = EAFNOSUPPORT; + } + break; + case IP_MULTICAST_IF: + if (optlen < sizeof(struct in_addr)) { + err = EINVAL; + } + if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) { + err = EAFNOSUPPORT; + } + break; + case IP_MULTICAST_LOOP: + if (optlen < sizeof(u8_t)) { + err = EINVAL; + } + if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) { + err = EAFNOSUPPORT; + } + break; + case IP_ADD_MEMBERSHIP: + case IP_DROP_MEMBERSHIP: + if (optlen < sizeof(struct ip_mreq)) { + err = EINVAL; + } + if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP) { + err = EAFNOSUPPORT; + } + break; +#endif /* LWIP_IGMP */ + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; + +#if LWIP_TCP +/* Level: IPPROTO_TCP */ + case IPPROTO_TCP: + if (optlen < sizeof(int)) { + err = EINVAL; + break; + } + + /* If this is no TCP socket, ignore any options. */ + if (sock->conn->type != NETCONN_TCP) + return 0; + + switch (optname) { + case TCP_NODELAY: + case TCP_KEEPALIVE: +#if LWIP_TCP_KEEPALIVE + case TCP_KEEPIDLE: + case TCP_KEEPINTVL: + case TCP_KEEPCNT: +#endif /* LWIP_TCP_KEEPALIVE */ + break; + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; +#endif /* LWIP_TCP */ +#if LWIP_UDP && LWIP_UDPLITE +/* Level: IPPROTO_UDPLITE */ + case IPPROTO_UDPLITE: + if (optlen < sizeof(int)) { + err = EINVAL; + break; + } + + /* If this is no UDP lite socket, ignore any options. */ + if (sock->conn->type != NETCONN_UDPLITE) + return 0; + + switch (optname) { + case UDPLITE_SEND_CSCOV: + case UDPLITE_RECV_CSCOV: + break; + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; +#endif /* LWIP_UDP && LWIP_UDPLITE */ +/* UNDEFINED LEVEL */ + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n", + s, level, optname)); + err = ENOPROTOOPT; + } /* switch (level) */ + + + if (err != ERR_OK) { + sock_set_errno(sock, err); + return -1; + } + + + /* Now do the actual option processing */ + data.sock = sock; +#ifdef LWIP_DEBUG + data.s = s; +#endif /* LWIP_DEBUG */ + data.level = level; + data.optname = optname; + data.optval = (void*)optval; + data.optlen = &optlen; + data.err = err; + tcpip_callback(lwip_setsockopt_internal, &data); + sys_arch_sem_wait(&sock->conn->op_completed, 0); + /* maybe lwip_setsockopt_internal has changed err */ + err = data.err; + + sock_set_errno(sock, err); + return err ? -1 : 0; +} + +static void +lwip_setsockopt_internal(void *arg) +{ + struct lwip_sock *sock; +#ifdef LWIP_DEBUG + int s; +#endif /* LWIP_DEBUG */ + int level, optname; + const void *optval; + struct lwip_setgetsockopt_data *data; + + LWIP_ASSERT("arg != NULL", arg != NULL); + + data = (struct lwip_setgetsockopt_data*)arg; + sock = data->sock; +#ifdef LWIP_DEBUG + s = data->s; +#endif /* LWIP_DEBUG */ + level = data->level; + optname = data->optname; + optval = data->optval; + + switch (level) { + +/* Level: SOL_SOCKET */ + case SOL_SOCKET: + switch (optname) { + + /* The option flags */ + case SO_BROADCAST: + /* UNIMPL case SO_DEBUG: */ + /* UNIMPL case SO_DONTROUTE: */ + case SO_KEEPALIVE: + /* UNIMPL case SO_OOBINCLUDE: */ +#if SO_REUSE + case SO_REUSEADDR: + case SO_REUSEPORT: +#endif /* SO_REUSE */ + /* UNIMPL case SO_USELOOPBACK: */ + if (*(int*)optval) { + sock->conn->pcb.ip->so_options |= optname; + } else { + sock->conn->pcb.ip->so_options &= ~optname; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, optname=0x%x, ..) -> %s\n", + s, optname, (*(int*)optval?"on":"off"))); + break; +#if LWIP_SO_RCVTIMEO + case SO_RCVTIMEO: + netconn_set_recvtimeout(sock->conn, *(int*)optval); + break; +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + case SO_RCVBUF: + netconn_set_recvbufsize(sock->conn, *(int*)optval); + break; +#endif /* LWIP_SO_RCVBUF */ +#if LWIP_UDP + case SO_NO_CHECK: + if (*(int*)optval) { + udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_NOCHKSUM); + } else { + udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_NOCHKSUM); + } + break; +#endif /* LWIP_UDP */ + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; + +/* Level: IPPROTO_IP */ + case IPPROTO_IP: + switch (optname) { + case IP_TTL: + sock->conn->pcb.ip->ttl = (u8_t)(*(int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TTL, ..) -> %d\n", + s, sock->conn->pcb.ip->ttl)); + break; + case IP_TOS: + sock->conn->pcb.ip->tos = (u8_t)(*(int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TOS, ..)-> %d\n", + s, sock->conn->pcb.ip->tos)); + break; +#if LWIP_IGMP + case IP_MULTICAST_TTL: + sock->conn->pcb.udp->ttl = (u8_t)(*(u8_t*)optval); + break; + case IP_MULTICAST_IF: + inet_addr_to_ipaddr(&sock->conn->pcb.udp->multicast_ip, (struct in_addr*)optval); + break; + case IP_MULTICAST_LOOP: + if (*(u8_t*)optval) { + udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_MULTICAST_LOOP); + } else { + udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_MULTICAST_LOOP); + } + break; + case IP_ADD_MEMBERSHIP: + case IP_DROP_MEMBERSHIP: + { + /* If this is a TCP or a RAW socket, ignore these options. */ + struct ip_mreq *imr = (struct ip_mreq *)optval; + ip_addr_t if_addr; + ip_addr_t multi_addr; + inet_addr_to_ipaddr(&if_addr, &imr->imr_interface); + inet_addr_to_ipaddr(&multi_addr, &imr->imr_multiaddr); + if(optname == IP_ADD_MEMBERSHIP){ + data->err = igmp_joingroup(&if_addr, &multi_addr); + } else { + data->err = igmp_leavegroup(&if_addr, &multi_addr); + } + if(data->err != ERR_OK) { + data->err = EADDRNOTAVAIL; + } + } + break; +#endif /* LWIP_IGMP */ + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; + +#if LWIP_TCP +/* Level: IPPROTO_TCP */ + case IPPROTO_TCP: + switch (optname) { + case TCP_NODELAY: + if (*(int*)optval) { + tcp_nagle_disable(sock->conn->pcb.tcp); + } else { + tcp_nagle_enable(sock->conn->pcb.tcp); + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_NODELAY) -> %s\n", + s, (*(int *)optval)?"on":"off") ); + break; + case TCP_KEEPALIVE: + sock->conn->pcb.tcp->keep_idle = (u32_t)(*(int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) -> %"U32_F"\n", + s, sock->conn->pcb.tcp->keep_idle)); + break; + +#if LWIP_TCP_KEEPALIVE + case TCP_KEEPIDLE: + sock->conn->pcb.tcp->keep_idle = 1000*(u32_t)(*(int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPIDLE) -> %"U32_F"\n", + s, sock->conn->pcb.tcp->keep_idle)); + break; + case TCP_KEEPINTVL: + sock->conn->pcb.tcp->keep_intvl = 1000*(u32_t)(*(int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPINTVL) -> %"U32_F"\n", + s, sock->conn->pcb.tcp->keep_intvl)); + break; + case TCP_KEEPCNT: + sock->conn->pcb.tcp->keep_cnt = (u32_t)(*(int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPCNT) -> %"U32_F"\n", + s, sock->conn->pcb.tcp->keep_cnt)); + break; +#endif /* LWIP_TCP_KEEPALIVE */ + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; +#endif /* LWIP_TCP*/ +#if LWIP_UDP && LWIP_UDPLITE + /* Level: IPPROTO_UDPLITE */ + case IPPROTO_UDPLITE: + switch (optname) { + case UDPLITE_SEND_CSCOV: + if ((*(int*)optval != 0) && ((*(int*)optval < 8)) || (*(int*)optval > 0xffff)) { + /* don't allow illegal values! */ + sock->conn->pcb.udp->chksum_len_tx = 8; + } else { + sock->conn->pcb.udp->chksum_len_tx = (u16_t)*(int*)optval; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) -> %d\n", + s, (*(int*)optval)) ); + break; + case UDPLITE_RECV_CSCOV: + if ((*(int*)optval != 0) && ((*(int*)optval < 8)) || (*(int*)optval > 0xffff)) { + /* don't allow illegal values! */ + sock->conn->pcb.udp->chksum_len_rx = 8; + } else { + sock->conn->pcb.udp->chksum_len_rx = (u16_t)*(int*)optval; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) -> %d\n", + s, (*(int*)optval)) ); + break; + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; +#endif /* LWIP_UDP */ + default: + LWIP_ASSERT("unhandled level", 0); + break; + } /* switch (level) */ + sys_sem_signal(&sock->conn->op_completed); +} + +int +lwip_ioctl(int s, long cmd, void *argp) +{ + struct lwip_sock *sock = get_socket(s); + u8_t val; +#if LWIP_SO_RCVBUF + u16_t buflen = 0; + s16_t recv_avail; +#endif /* LWIP_SO_RCVBUF */ + + if (!sock) { + return -1; + } + + switch (cmd) { +#if LWIP_SO_RCVBUF + case FIONREAD: + if (!argp) { + sock_set_errno(sock, EINVAL); + return -1; + } + + SYS_ARCH_GET(sock->conn->recv_avail, recv_avail); + if (recv_avail < 0) { + recv_avail = 0; + } + *((u16_t*)argp) = (u16_t)recv_avail; + + /* Check if there is data left from the last recv operation. /maq 041215 */ + if (sock->lastdata) { + struct pbuf *p = (struct pbuf *)sock->lastdata; + if (netconn_type(sock->conn) != NETCONN_TCP) { + p = ((struct netbuf *)p)->p; + } + buflen = p->tot_len; + buflen -= sock->lastoffset; + + *((u16_t*)argp) += buflen; + } + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONREAD, %p) = %"U16_F"\n", s, argp, *((u16_t*)argp))); + sock_set_errno(sock, 0); + return 0; +#endif /* LWIP_SO_RCVBUF */ + + case FIONBIO: + val = 0; + if (argp && *(u32_t*)argp) { + val = 1; + } + netconn_set_nonblocking(sock->conn, val); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONBIO, %d)\n", s, val)); + sock_set_errno(sock, 0); + return 0; + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, UNIMPL: 0x%lx, %p)\n", s, cmd, argp)); + sock_set_errno(sock, ENOSYS); /* not yet implemented */ + return -1; + } /* switch (cmd) */ +} + +/** A minimal implementation of fcntl. + * Currently only the commands F_GETFL and F_SETFL are implemented. + * Only the flag O_NONBLOCK is implemented. + */ +int +lwip_fcntl(int s, int cmd, int val) +{ + struct lwip_sock *sock = get_socket(s); + int ret = -1; + + if (!sock || !sock->conn) { + return -1; + } + + switch (cmd) { + case F_GETFL: + ret = netconn_is_nonblocking(sock->conn) ? O_NONBLOCK : 0; + break; + case F_SETFL: + if ((val & ~O_NONBLOCK) == 0) { + /* only O_NONBLOCK, all other bits are zero */ + netconn_set_nonblocking(sock->conn, val & O_NONBLOCK); + ret = 0; + } + break; + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_fcntl(%d, UNIMPL: %d, %d)\n", s, cmd, val)); + break; + } + return ret; +} + +#endif /* LWIP_SOCKET */ diff --git a/variants/lwip/lwip/api/tcpip.c b/variants/lwip/lwip/api/tcpip.c new file mode 100755 index 000000000..01a49d562 --- /dev/null +++ b/variants/lwip/lwip/api/tcpip.c @@ -0,0 +1,460 @@ +/** + * @file + * Sequential API Main thread module + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if !NO_SYS /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/sys.h" +#include "lwip/memp.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/tcpip.h" +#include "lwip/init.h" +#include "netif/etharp.h" +#include "netif/ppp_oe.h" + +/* global variables */ +static tcpip_init_done_fn tcpip_init_done; +static void *tcpip_init_done_arg; +static sys_mbox_t mbox; + +#if LWIP_TCPIP_CORE_LOCKING +/** The global semaphore to lock the stack. */ +sys_mutex_t lock_tcpip_core; +#endif /* LWIP_TCPIP_CORE_LOCKING */ + + +/** + * The main lwIP thread. This thread has exclusive access to lwIP core functions + * (unless access to them is not locked). Other threads communicate with this + * thread using message boxes. + * + * It also starts all the timers to make sure they are running in the right + * thread context. + * + * @param arg unused argument + */ +static void +tcpip_thread(void *arg) +{ + struct tcpip_msg *msg; + LWIP_UNUSED_ARG(arg); + + if (tcpip_init_done != NULL) {//Óû§×¢²áÁË×Ô¶¨Òå³õʼ»¯º¯Êý + tcpip_init_done(tcpip_init_done_arg); + } + + LOCK_TCPIP_CORE(); + while (1) { /* MAIN Loop */ + UNLOCK_TCPIP_CORE(); + LWIP_TCPIP_THREAD_ALIVE(); + /* wait for a message, timeouts are processed while waiting */ + sys_timeouts_mbox_fetch(&mbox, (void **)&msg); + LOCK_TCPIP_CORE(); + switch (msg->type) { +#if LWIP_NETCONN + case TCPIP_MSG_API://APIµ÷Óà + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API message %p\n", (void *)msg)); + msg->msg.apimsg->function(&(msg->msg.apimsg->msg)); + break; +#endif /* LWIP_NETCONN */ + +#if !LWIP_TCPIP_CORE_LOCKING_INPUT + case TCPIP_MSG_INPKT://µ×²ãÊý¾Ý°üÊäÈë + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p\n", (void *)msg)); +#if LWIP_ETHERNET + if (msg->msg.inp.netif->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) {//Ö§³ÖARP + ethernet_input(msg->msg.inp.p, msg->msg.inp.netif);//½»¸øARP´¦Àí + } else +#endif /* LWIP_ETHERNET */ + { + ip_input(msg->msg.inp.p, msg->msg.inp.netif);//½»¸øIP´¦Àí + } + memp_free(MEMP_TCPIP_MSG_INPKT, msg); + break; +#endif /* LWIP_TCPIP_CORE_LOCKING_INPUT */ + +#if LWIP_NETIF_API + case TCPIP_MSG_NETIFAPI: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: Netif API message %p\n", (void *)msg)); + msg->msg.netifapimsg->function(&(msg->msg.netifapimsg->msg)); + break; +#endif /* LWIP_NETIF_API */ + + case TCPIP_MSG_CALLBACK://ÉÏ²ã»Øµ÷·½Ê½Ö´ÐÐÒ»¸öº¯Êý + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK %p\n", (void *)msg)); + msg->msg.cb.function(msg->msg.cb.ctx); + memp_free(MEMP_TCPIP_MSG_API, msg); + break; + +#if LWIP_TCPIP_TIMEOUT + case TCPIP_MSG_TIMEOUT://Éϲã×¢²áÒ»¸ö¶¨Ê±Ê¼þ + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: TIMEOUT %p\n", (void *)msg)); + sys_timeout(msg->msg.tmo.msecs, msg->msg.tmo.h, msg->msg.tmo.arg); + memp_free(MEMP_TCPIP_MSG_API, msg); + break; + case TCPIP_MSG_UNTIMEOUT://Éϲãɾ³ýÒ»¸ö¶¨Ê±Ê¼þ + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: UNTIMEOUT %p\n", (void *)msg)); + sys_untimeout(msg->msg.tmo.h, msg->msg.tmo.arg); + memp_free(MEMP_TCPIP_MSG_API, msg); + break; +#endif /* LWIP_TCPIP_TIMEOUT */ + + default: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: %d\n", msg->type)); + LWIP_ASSERT("tcpip_thread: invalid message", 0); + break; + } + } +} + +/** + * Pass a received packet to tcpip_thread for input processing + * + * @param p the received packet, p->payload pointing to the Ethernet header or + * to an IP header (if inp doesn't have NETIF_FLAG_ETHARP or + * NETIF_FLAG_ETHERNET flags) + * @param inp the network interface on which the packet was received + */ +err_t +tcpip_input(struct pbuf *p, struct netif *inp) +{ +#if LWIP_TCPIP_CORE_LOCKING_INPUT + err_t ret; + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_input: PACKET %p/%p\n", (void *)p, (void *)inp)); + LOCK_TCPIP_CORE(); +#if LWIP_ETHERNET + if (inp->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) { + ret = ethernet_input(p, inp); + } else +#endif /* LWIP_ETHERNET */ + { + ret = ip_input(p, inp); + } + UNLOCK_TCPIP_CORE(); + return ret; +#else /* LWIP_TCPIP_CORE_LOCKING_INPUT */ + struct tcpip_msg *msg; + + if (sys_mbox_valid(&mbox)) { + msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_INPKT); + if (msg == NULL) { + return ERR_MEM; + } + + msg->type = TCPIP_MSG_INPKT; + msg->msg.inp.p = p; + msg->msg.inp.netif = inp; + if (sys_mbox_trypost(&mbox, msg) != ERR_OK) { + memp_free(MEMP_TCPIP_MSG_INPKT, msg); + return ERR_MEM; + } + return ERR_OK; + } + return ERR_VAL; +#endif /* LWIP_TCPIP_CORE_LOCKING_INPUT */ +} + +/** + * Call a specific function in the thread context of + * tcpip_thread for easy access synchronization. + * A function called in that way may access lwIP core code + * without fearing concurrent access. + * + * @param f the function to call + * @param ctx parameter passed to f + * @param block 1 to block until the request is posted, 0 to non-blocking mode + * @return ERR_OK if the function was called, another err_t if not + */ +err_t +tcpip_callback_with_block(tcpip_callback_fn function, void *ctx, u8_t block) +{ + struct tcpip_msg *msg; + + if (sys_mbox_valid(&mbox)) { + msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API); + if (msg == NULL) { + return ERR_MEM; + } + + msg->type = TCPIP_MSG_CALLBACK; + msg->msg.cb.function = function; + msg->msg.cb.ctx = ctx; + if (block) { + sys_mbox_post(&mbox, msg); + } else { + if (sys_mbox_trypost(&mbox, msg) != ERR_OK) { + memp_free(MEMP_TCPIP_MSG_API, msg); + return ERR_MEM; + } + } + return ERR_OK; + } + return ERR_VAL; +} + +#if LWIP_TCPIP_TIMEOUT +/** + * call sys_timeout in tcpip_thread + * + * @param msec time in milliseconds for timeout + * @param h function to be called on timeout + * @param arg argument to pass to timeout function h + * @return ERR_MEM on memory error, ERR_OK otherwise + */ +err_t +tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg) +{ + struct tcpip_msg *msg; + + if (sys_mbox_valid(&mbox)) { + msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API); + if (msg == NULL) { + return ERR_MEM; + } + + msg->type = TCPIP_MSG_TIMEOUT; + msg->msg.tmo.msecs = msecs; + msg->msg.tmo.h = h; + msg->msg.tmo.arg = arg; + sys_mbox_post(&mbox, msg); + return ERR_OK; + } + return ERR_VAL; +} + +/** + * call sys_untimeout in tcpip_thread + * + * @param msec time in milliseconds for timeout + * @param h function to be called on timeout + * @param arg argument to pass to timeout function h + * @return ERR_MEM on memory error, ERR_OK otherwise + */ +err_t +tcpip_untimeout(sys_timeout_handler h, void *arg) +{ + struct tcpip_msg *msg; + + if (sys_mbox_valid(&mbox)) { + msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API); + if (msg == NULL) { + return ERR_MEM; + } + + msg->type = TCPIP_MSG_UNTIMEOUT; + msg->msg.tmo.h = h; + msg->msg.tmo.arg = arg; + sys_mbox_post(&mbox, msg); + return ERR_OK; + } + return ERR_VAL; +} +#endif /* LWIP_TCPIP_TIMEOUT */ + +#if LWIP_NETCONN +/** + * Call the lower part of a netconn_* function + * This function is then running in the thread context + * of tcpip_thread and has exclusive access to lwIP core code. + * + * @param apimsg a struct containing the function to call and its parameters + * @return ERR_OK if the function was called, another err_t if not + */ +err_t +tcpip_apimsg(struct api_msg *apimsg) +{ + struct tcpip_msg msg; +#ifdef LWIP_DEBUG + /* catch functions that don't set err */ + apimsg->msg.err = ERR_VAL; +#endif + + if (sys_mbox_valid(&mbox)) {//ÄÚºËÓÊÏäÓÐЧ + msg.type = TCPIP_MSG_API; + msg.msg.apimsg = apimsg; + sys_mbox_post(&mbox, &msg);//ͶµÝÏûÏ¢ + sys_arch_sem_wait(&apimsg->msg.conn->op_completed, 0);//µÈ´ýÏûÏ¢´¦ÀíÍê±Ï + return apimsg->msg.err; + } + return ERR_VAL; +} + +#if LWIP_TCPIP_CORE_LOCKING +/** + * Call the lower part of a netconn_* function + * This function has exclusive access to lwIP core code by locking it + * before the function is called. + * + * @param apimsg a struct containing the function to call and its parameters + * @return ERR_OK (only for compatibility fo tcpip_apimsg()) + */ +err_t +tcpip_apimsg_lock(struct api_msg *apimsg) +{ +#ifdef LWIP_DEBUG + /* catch functions that don't set err */ + apimsg->msg.err = ERR_VAL; +#endif + + LOCK_TCPIP_CORE(); + apimsg->function(&(apimsg->msg)); + UNLOCK_TCPIP_CORE(); + return apimsg->msg.err; + +} +#endif /* LWIP_TCPIP_CORE_LOCKING */ +#endif /* LWIP_NETCONN */ + +#if LWIP_NETIF_API +#if !LWIP_TCPIP_CORE_LOCKING +/** + * Much like tcpip_apimsg, but calls the lower part of a netifapi_* + * function. + * + * @param netifapimsg a struct containing the function to call and its parameters + * @return error code given back by the function that was called + */ +err_t +tcpip_netifapi(struct netifapi_msg* netifapimsg) +{ + struct tcpip_msg msg; + + if (sys_mbox_valid(&mbox)) { + err_t err = sys_sem_new(&netifapimsg->msg.sem, 0); + if (err != ERR_OK) { + netifapimsg->msg.err = err; + return err; + } + + msg.type = TCPIP_MSG_NETIFAPI; + msg.msg.netifapimsg = netifapimsg; + sys_mbox_post(&mbox, &msg); + sys_sem_wait(&netifapimsg->msg.sem); + sys_sem_free(&netifapimsg->msg.sem); + return netifapimsg->msg.err; + } + return ERR_VAL; +} +#else /* !LWIP_TCPIP_CORE_LOCKING */ +/** + * Call the lower part of a netifapi_* function + * This function has exclusive access to lwIP core code by locking it + * before the function is called. + * + * @param netifapimsg a struct containing the function to call and its parameters + * @return ERR_OK (only for compatibility fo tcpip_netifapi()) + */ +err_t +tcpip_netifapi_lock(struct netifapi_msg* netifapimsg) +{ + LOCK_TCPIP_CORE(); + netifapimsg->function(&(netifapimsg->msg)); + UNLOCK_TCPIP_CORE(); + return netifapimsg->msg.err; +} +#endif /* !LWIP_TCPIP_CORE_LOCKING */ +#endif /* LWIP_NETIF_API */ + +/** + * Initialize this module: + * - initialize all sub modules + * - start the tcpip_thread + * + * @param initfunc a function to call when tcpip_thread is running and finished initializing + * @param arg argument to pass to initfunc + */ +void +tcpip_init(tcpip_init_done_fn initfunc, void *arg) +{ + lwip_init();//³õʼ»¯ÄÚºË + + tcpip_init_done = initfunc;//×¢²áÓû§×Ô¶¨Ò庯Êý + tcpip_init_done_arg = arg;//º¯Êý²ÎÊý + if(sys_mbox_new(&mbox, TCPIP_MBOX_SIZE) != ERR_OK) {//´´½¨ÄÚºËÓÊÏä + LWIP_ASSERT("failed to create tcpip_thread mbox", 0); + } +#if LWIP_TCPIP_CORE_LOCKING + if(sys_mutex_new(&lock_tcpip_core) != ERR_OK) { + LWIP_ASSERT("failed to create lock_tcpip_core", 0); + } +#endif /* LWIP_TCPIP_CORE_LOCKING */ + + sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO);//´´½¨Äں˽ø³Ì +} + +/** + * Simple callback function used with tcpip_callback to free a pbuf + * (pbuf_free has a wrong signature for tcpip_callback) + * + * @param p The pbuf (chain) to be dereferenced. + */ +static void +pbuf_free_int(void *p) +{ + struct pbuf *q = (struct pbuf *)p; + pbuf_free(q); +} + +/** + * A simple wrapper function that allows you to free a pbuf from interrupt context. + * + * @param p The pbuf (chain) to be dereferenced. + * @return ERR_OK if callback could be enqueued, an err_t if not + */ +err_t +pbuf_free_callback(struct pbuf *p) +{ + return tcpip_callback_with_block(pbuf_free_int, p, 0); +} + +/** + * A simple wrapper function that allows you to free heap memory from + * interrupt context. + * + * @param m the heap memory to free + * @return ERR_OK if callback could be enqueued, an err_t if not + */ +err_t +mem_free_callback(void *m) +{ + return tcpip_callback_with_block(mem_free, m, 0); +} + +#endif /* !NO_SYS */ diff --git a/variants/lwip/lwip/app/dhcpserver.c b/variants/lwip/lwip/app/dhcpserver.c new file mode 100755 index 000000000..ba6fd14b1 --- /dev/null +++ b/variants/lwip/lwip/app/dhcpserver.c @@ -0,0 +1,1144 @@ +#include "lwip/inet.h" +#include "lwip/err.h" +#include "lwip/pbuf.h" +#include "lwip/udp.h" +#include "lwip/mem.h" +#include "osapi.h" +#include "lwip/app/dhcpserver.h" + +#ifndef LWIP_OPEN_SRC +#include "net80211/ieee80211_var.h" +#endif + +#include "user_interface.h" + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +//////////////////////////////////////////////////////////////////////////////////// +//static const uint8_t xid[4] = {0xad, 0xde, 0x12, 0x23}; +//static u8_t old_xid[4] = {0}; +static const uint32 magic_cookie ICACHE_RODATA_ATTR = 0x63538263; +static struct udp_pcb *pcb_dhcps = NULL; +static struct ip_addr broadcast_dhcps; +static struct ip_addr server_address; +static struct ip_addr client_address;//added +static struct ip_addr client_address_plus; + +static struct dhcps_lease dhcps_lease; +//static bool dhcps_lease_flag = true; +static list_node *plist = NULL; +static uint8 offer = 0xFF; +static bool renew = false; +#define DHCPS_LEASE_TIME_DEF (120) +uint32 dhcps_lease_time = DHCPS_LEASE_TIME_DEF; //minute +/****************************************************************************** + * FunctionName : node_insert_to_list + * Description : insert the node to the list + * Parameters : arg -- Additional argument to pass to the callback function + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR node_insert_to_list(list_node **phead, list_node* pinsert) +{ + list_node *plist = NULL; + struct dhcps_pool *pdhcps_pool = NULL; + struct dhcps_pool *pdhcps_node = NULL; + if (*phead == NULL) + *phead = pinsert; + else { + plist = *phead; + pdhcps_node = pinsert->pnode; + pdhcps_pool = plist->pnode; + + if(pdhcps_node->ip.addr < pdhcps_pool->ip.addr) { + pinsert->pnext = plist; + *phead = pinsert; + } else { + while (plist->pnext != NULL) { + pdhcps_pool = plist->pnext->pnode; + if (pdhcps_node->ip.addr < pdhcps_pool->ip.addr) { + pinsert->pnext = plist->pnext; + plist->pnext = pinsert; + break; + } + plist = plist->pnext; + } + + if(plist->pnext == NULL) { + plist->pnext = pinsert; + } + } + } +// pinsert->pnext = NULL; +} + +/****************************************************************************** + * FunctionName : node_delete_from_list + * Description : remove the node from list + * Parameters : arg -- Additional argument to pass to the callback function + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR node_remove_from_list(list_node **phead, list_node* pdelete) +{ + list_node *plist = NULL; + + plist = *phead; + if (plist == NULL){ + *phead = NULL; + } else { + if (plist == pdelete){ + *phead = plist->pnext; + } else { + while (plist != NULL) { + if (plist->pnext == pdelete){ + plist->pnext = pdelete->pnext; + } + plist = plist->pnext; + } + } + } +} +/////////////////////////////////////////////////////////////////////////////////// +/* + * ��DHCP msg��Ϣ�ṹ���������� + * + * @param optptr -- DHCP msg��Ϣλ�� + * @param type -- Ҫ��ӵ�����option + * + * @return uint8_t* ����DHCP msgƫ�Ƶ�ַ + */ +/////////////////////////////////////////////////////////////////////////////////// +static uint8_t* ICACHE_FLASH_ATTR add_msg_type(uint8_t *optptr, uint8_t type) +{ + + *optptr++ = DHCP_OPTION_MSG_TYPE; + *optptr++ = 1; + *optptr++ = type; + return optptr; +} +/////////////////////////////////////////////////////////////////////////////////// +/* + * ��DHCP msg�ṹ������offerӦ������ + * + * @param optptr -- DHCP msg��Ϣλ�� + * + * @return uint8_t* ����DHCP msgƫ�Ƶ�ַ + */ +/////////////////////////////////////////////////////////////////////////////////// +static uint8_t* ICACHE_FLASH_ATTR add_offer_options(uint8_t *optptr) +{ + struct ip_addr ipadd; + + ipadd.addr = *( (uint32_t *) &server_address); + +#ifdef USE_CLASS_B_NET + *optptr++ = DHCP_OPTION_SUBNET_MASK; + *optptr++ = 4; //length + *optptr++ = 255; + *optptr++ = 240; + *optptr++ = 0; + *optptr++ = 0; +#else + *optptr++ = DHCP_OPTION_SUBNET_MASK; + *optptr++ = 4; + *optptr++ = 255; + *optptr++ = 255; + *optptr++ = 255; + *optptr++ = 0; +#endif + + *optptr++ = DHCP_OPTION_LEASE_TIME; + *optptr++ = 4; + *optptr++ = ((DHCPS_LEASE_TIMER * 60) >> 24) & 0xFF; + *optptr++ = ((DHCPS_LEASE_TIMER * 60) >> 16) & 0xFF; + *optptr++ = ((DHCPS_LEASE_TIMER * 60) >> 8) & 0xFF; + *optptr++ = ((DHCPS_LEASE_TIMER * 60) >> 0) & 0xFF; + + *optptr++ = DHCP_OPTION_SERVER_ID; + *optptr++ = 4; + *optptr++ = ip4_addr1( &ipadd); + *optptr++ = ip4_addr2( &ipadd); + *optptr++ = ip4_addr3( &ipadd); + *optptr++ = ip4_addr4( &ipadd); + + if (dhcps_router_enabled(offer)){ + struct ip_info if_ip; + os_bzero(&if_ip, sizeof(struct ip_info)); + wifi_get_ip_info(SOFTAP_IF, &if_ip); + + *optptr++ = DHCP_OPTION_ROUTER; + *optptr++ = 4; + *optptr++ = ip4_addr1( &if_ip.gw); + *optptr++ = ip4_addr2( &if_ip.gw); + *optptr++ = ip4_addr3( &if_ip.gw); + *optptr++ = ip4_addr4( &if_ip.gw); + } + +#ifdef USE_DNS + *optptr++ = DHCP_OPTION_DNS_SERVER; + *optptr++ = 4; + *optptr++ = ip4_addr1( &ipadd); + *optptr++ = ip4_addr2( &ipadd); + *optptr++ = ip4_addr3( &ipadd); + *optptr++ = ip4_addr4( &ipadd); +#endif + +#ifdef CLASS_B_NET + *optptr++ = DHCP_OPTION_BROADCAST_ADDRESS; + *optptr++ = 4; + *optptr++ = ip4_addr1( &ipadd); + *optptr++ = 255; + *optptr++ = 255; + *optptr++ = 255; +#else + *optptr++ = DHCP_OPTION_BROADCAST_ADDRESS; + *optptr++ = 4; + *optptr++ = ip4_addr1( &ipadd); + *optptr++ = ip4_addr2( &ipadd); + *optptr++ = ip4_addr3( &ipadd); + *optptr++ = 255; +#endif + + *optptr++ = DHCP_OPTION_INTERFACE_MTU; + *optptr++ = 2; +#ifdef CLASS_B_NET + *optptr++ = 0x05; + *optptr++ = 0xdc; +#else + *optptr++ = 0x02; + *optptr++ = 0x40; +#endif + + *optptr++ = DHCP_OPTION_PERFORM_ROUTER_DISCOVERY; + *optptr++ = 1; + *optptr++ = 0x00; + + *optptr++ = 43; + *optptr++ = 6; + + *optptr++ = 0x01; + *optptr++ = 4; + *optptr++ = 0x00; + *optptr++ = 0x00; + *optptr++ = 0x00; + *optptr++ = 0x02; + + return optptr; +} +/////////////////////////////////////////////////////////////////////////////////// +/* + * ��DHCP msg�ṹ����ӽ����־���� + * + * @param optptr -- DHCP msg��Ϣλ�� + * + * @return uint8_t* ����DHCP msgƫ�Ƶ�ַ + */ +/////////////////////////////////////////////////////////////////////////////////// +static uint8_t* ICACHE_FLASH_ATTR add_end(uint8_t *optptr) +{ + + *optptr++ = DHCP_OPTION_END; + return optptr; +} +/////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////// +static void ICACHE_FLASH_ATTR create_msg(struct dhcps_msg *m) +{ + struct ip_addr client; + + client.addr = *( (uint32_t *) &client_address); + + m->op = DHCP_REPLY; + m->htype = DHCP_HTYPE_ETHERNET; + m->hlen = 6; + m->hops = 0; +// os_memcpy((char *) xid, (char *) m->xid, sizeof(m->xid)); + m->secs = 0; + m->flags = htons(BOOTP_BROADCAST); + + os_memcpy((char *) m->yiaddr, (char *) &client.addr, sizeof(m->yiaddr)); + + os_memset((char *) m->ciaddr, 0, sizeof(m->ciaddr)); + os_memset((char *) m->siaddr, 0, sizeof(m->siaddr)); + os_memset((char *) m->giaddr, 0, sizeof(m->giaddr)); + os_memset((char *) m->sname, 0, sizeof(m->sname)); + os_memset((char *) m->file, 0, sizeof(m->file)); + + os_memset((char *) m->options, 0, sizeof(m->options)); + os_memcpy((char *) m->options, &magic_cookie, sizeof(magic_cookie)); +} +/////////////////////////////////////////////////////////////////////////////////// +/* + * ����һ��OFFER + * + * @param -- m ָ����Ҫ���͵�DHCP msg���� + */ +/////////////////////////////////////////////////////////////////////////////////// +static void ICACHE_FLASH_ATTR send_offer(struct dhcps_msg *m) +{ + uint8_t *end; + struct pbuf *p, *q; + u8_t *data; + u16_t cnt=0; + u16_t i; + err_t SendOffer_err_t; + create_msg(m); + + end = add_msg_type(&m->options[4], DHCPOFFER); + end = add_offer_options(end); + end = add_end(end); + + p = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcps_msg), PBUF_RAM); +#if DHCPS_DEBUG + os_printf("udhcp: send_offer>>p->ref = %d\n", p->ref); +#endif + if(p != NULL){ + +#if DHCPS_DEBUG + os_printf("dhcps: send_offer>>pbuf_alloc succeed\n"); + os_printf("dhcps: send_offer>>p->tot_len = %d\n", p->tot_len); + os_printf("dhcps: send_offer>>p->len = %d\n", p->len); +#endif + q = p; + while(q != NULL){ + data = (u8_t *)q->payload; + for(i=0; ilen; i++) + { + data[i] = ((u8_t *) m)[cnt++]; +#if DHCPS_DEBUG + os_printf("%02x ",data[i]); + if((i+1)%16 == 0){ + os_printf("\n"); + } +#endif + } + + q = q->next; + } + }else{ + +#if DHCPS_DEBUG + os_printf("dhcps: send_offer>>pbuf_alloc failed\n"); +#endif + return; + } + SendOffer_err_t = udp_sendto( pcb_dhcps, p, &broadcast_dhcps, DHCPS_CLIENT_PORT ); +#if DHCPS_DEBUG + os_printf("dhcps: send_offer>>udp_sendto result %x\n",SendOffer_err_t); +#endif + if(p->ref != 0){ +#if DHCPS_DEBUG + os_printf("udhcp: send_offer>>free pbuf\n"); +#endif + pbuf_free(p); + } +} +/////////////////////////////////////////////////////////////////////////////////// +/* + * ����һ��NAK��Ϣ + * + * @param m ָ����Ҫ���͵�DHCP msg���� + */ +/////////////////////////////////////////////////////////////////////////////////// +static void ICACHE_FLASH_ATTR send_nak(struct dhcps_msg *m) +{ + + u8_t *end; + struct pbuf *p, *q; + u8_t *data; + u16_t cnt=0; + u16_t i; + err_t SendNak_err_t; + create_msg(m); + + end = add_msg_type(&m->options[4], DHCPNAK); + end = add_end(end); + + p = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcps_msg), PBUF_RAM); +#if DHCPS_DEBUG + os_printf("udhcp: send_nak>>p->ref = %d\n", p->ref); +#endif + if(p != NULL){ + +#if DHCPS_DEBUG + os_printf("dhcps: send_nak>>pbuf_alloc succeed\n"); + os_printf("dhcps: send_nak>>p->tot_len = %d\n", p->tot_len); + os_printf("dhcps: send_nak>>p->len = %d\n", p->len); +#endif + q = p; + while(q != NULL){ + data = (u8_t *)q->payload; + for(i=0; ilen; i++) + { + data[i] = ((u8_t *) m)[cnt++]; +#if DHCPS_DEBUG + os_printf("%02x ",data[i]); + if((i+1)%16 == 0){ + os_printf("\n"); + } +#endif + } + + q = q->next; + } + }else{ + +#if DHCPS_DEBUG + os_printf("dhcps: send_nak>>pbuf_alloc failed\n"); +#endif + return; + } + SendNak_err_t = udp_sendto( pcb_dhcps, p, &broadcast_dhcps, DHCPS_CLIENT_PORT ); +#if DHCPS_DEBUG + os_printf("dhcps: send_nak>>udp_sendto result %x\n",SendNak_err_t); +#endif + if(p->ref != 0){ +#if DHCPS_DEBUG + os_printf("udhcp: send_nak>>free pbuf\n"); +#endif + pbuf_free(p); + } +} +/////////////////////////////////////////////////////////////////////////////////// +/* + * ����һ��ACK��DHCP�ͻ��� + * + * @param m ָ����Ҫ���͵�DHCP msg���� + */ +/////////////////////////////////////////////////////////////////////////////////// +static void ICACHE_FLASH_ATTR send_ack(struct dhcps_msg *m) +{ + + u8_t *end; + struct pbuf *p, *q; + u8_t *data; + u16_t cnt=0; + u16_t i; + err_t SendAck_err_t; + create_msg(m); + + end = add_msg_type(&m->options[4], DHCPACK); + end = add_offer_options(end); + end = add_end(end); + + p = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcps_msg), PBUF_RAM); +#if DHCPS_DEBUG + os_printf("udhcp: send_ack>>p->ref = %d\n", p->ref); +#endif + if(p != NULL){ + +#if DHCPS_DEBUG + os_printf("dhcps: send_ack>>pbuf_alloc succeed\n"); + os_printf("dhcps: send_ack>>p->tot_len = %d\n", p->tot_len); + os_printf("dhcps: send_ack>>p->len = %d\n", p->len); +#endif + q = p; + while(q != NULL){ + data = (u8_t *)q->payload; + for(i=0; ilen; i++) + { + data[i] = ((u8_t *) m)[cnt++]; +#if DHCPS_DEBUG + os_printf("%02x ",data[i]); + if((i+1)%16 == 0){ + os_printf("\n"); + } +#endif + } + + q = q->next; + } + }else{ + +#if DHCPS_DEBUG + os_printf("dhcps: send_ack>>pbuf_alloc failed\n"); +#endif + return; + } + SendAck_err_t = udp_sendto( pcb_dhcps, p, &broadcast_dhcps, DHCPS_CLIENT_PORT ); +#if DHCPS_DEBUG + os_printf("dhcps: send_ack>>udp_sendto result %x\n",SendAck_err_t); +#endif + + if(p->ref != 0){ +#if DHCPS_DEBUG + os_printf("udhcp: send_ack>>free pbuf\n"); +#endif + pbuf_free(p); + } +} +/////////////////////////////////////////////////////////////////////////////////// +/* + * ����DHCP�ͻ��˷�����DHCP����������Ϣ�����Բ�ͬ��DHCP��������������Ӧ��Ӧ�� + * + * @param optptr DHCP msg��������� + * @param len ��������Ĵ��?(byte) + * + * @return uint8_t ���ش�����DHCP Server״ֵ̬ + */ +/////////////////////////////////////////////////////////////////////////////////// +static uint8_t ICACHE_FLASH_ATTR parse_options(uint8_t *optptr, sint16_t len) +{ + struct ip_addr client; + bool is_dhcp_parse_end = false; + struct dhcps_state s; + + client.addr = *( (uint32_t *) &client_address); + + u8_t *end = optptr + len; + u16_t type = 0; + + s.state = DHCPS_STATE_IDLE; + + while (optptr < end) { +#if DHCPS_DEBUG + os_printf("dhcps: (sint16_t)*optptr = %d\n", (sint16_t)*optptr); +#endif + switch ((sint16_t) *optptr) { + + case DHCP_OPTION_MSG_TYPE: //53 + type = *(optptr + 2); + break; + + case DHCP_OPTION_REQ_IPADDR://50 + if( os_memcmp( (char *) &client.addr, (char *) optptr+2,4)==0 ) { +#if DHCPS_DEBUG + os_printf("dhcps: DHCP_OPTION_REQ_IPADDR = 0 ok\n"); +#endif + s.state = DHCPS_STATE_ACK; + }else { +#if DHCPS_DEBUG + os_printf("dhcps: DHCP_OPTION_REQ_IPADDR != 0 err\n"); +#endif + s.state = DHCPS_STATE_NAK; + } + break; + case DHCP_OPTION_END: + { + is_dhcp_parse_end = true; + } + break; + } + + if(is_dhcp_parse_end){ + break; + } + + optptr += optptr[1] + 2; + } + + switch (type){ + + case DHCPDISCOVER://1 + s.state = DHCPS_STATE_OFFER; +#if DHCPS_DEBUG + os_printf("dhcps: DHCPD_STATE_OFFER\n"); +#endif + break; + + case DHCPREQUEST://3 + if ( !(s.state == DHCPS_STATE_ACK || s.state == DHCPS_STATE_NAK) ) { + if(renew == true) { + s.state = DHCPS_STATE_ACK; + } else { + s.state = DHCPS_STATE_NAK; + } +#if DHCPS_DEBUG + os_printf("dhcps: DHCPD_STATE_NAK\n"); +#endif + } + break; + + case DHCPDECLINE://4 + s.state = DHCPS_STATE_IDLE; +#if DHCPS_DEBUG + os_printf("dhcps: DHCPD_STATE_IDLE\n"); +#endif + break; + + case DHCPRELEASE://7 + s.state = DHCPS_STATE_RELEASE; +#if DHCPS_DEBUG + os_printf("dhcps: DHCPD_STATE_IDLE\n"); +#endif + break; + } +#if DHCPS_DEBUG + os_printf("dhcps: return s.state = %d\n", s.state); +#endif + return s.state; +} +/////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////// +static sint16_t ICACHE_FLASH_ATTR parse_msg(struct dhcps_msg *m, u16_t len) +{ + if(os_memcmp((char *)m->options, + &magic_cookie, + sizeof(magic_cookie)) == 0){ +#if DHCPS_DEBUG + os_printf("dhcps: len = %d\n", len); +#endif + /* + * ��¼��ǰ��xid���ﴦ���? + * �˺�ΪDHCP�ͻ����������û�ͳһ��ȡIPʱ�� + */ +// if((old_xid[0] == 0) && +// (old_xid[1] == 0) && +// (old_xid[2] == 0) && +// (old_xid[3] == 0)){ +// /* +// * old_xidδ��¼�κ����? +// * �϶��ǵ�һ��ʹ�� +// */ +// os_memcpy((char *)old_xid, (char *)m->xid, sizeof(m->xid)); +// }else{ +// /* +// * ������DHCP msg�����xid���ϴμ�¼�IJ�ͬ�� +// * �϶�Ϊ��ͬ��DHCP�ͻ��˷��ͣ���ʱ����Ҫ����Ŀͻ���IP +// * ���� 192.168.4.100(0x6404A8C0) <--> 192.168.4.200(0xC804A8C0) +// * +// */ +// if(os_memcmp((char *)old_xid, (char *)m->xid, sizeof(m->xid)) != 0){ + /* + * ��¼���ε�xid�ţ�ͬʱ�����IP���� + */ + struct ip_addr addr_tmp; +// os_memcpy((char *)old_xid, (char *)m->xid, sizeof(m->xid)); + +// { + struct dhcps_pool *pdhcps_pool = NULL; + list_node *pnode = NULL; + list_node *pback_node = NULL; + struct ip_addr first_address; + bool flag = false; + +// POOL_START: + first_address.addr = dhcps_lease.start_ip.addr; + client_address.addr = client_address_plus.addr; + renew = false; +// addr_tmp.addr = htonl(client_address_plus.addr); +// addr_tmp.addr++; +// client_address_plus.addr = htonl(addr_tmp.addr); + for (pback_node = plist; pback_node != NULL;pback_node = pback_node->pnext) { + pdhcps_pool = pback_node->pnode; + if (os_memcmp(pdhcps_pool->mac, m->chaddr, sizeof(pdhcps_pool->mac)) == 0){ +// os_printf("the same device request ip\n"); + if (os_memcmp(&pdhcps_pool->ip.addr, m->ciaddr, sizeof(pdhcps_pool->ip.addr)) == 0) { + renew = true; + } + client_address.addr = pdhcps_pool->ip.addr; + pdhcps_pool->lease_timer = DHCPS_LEASE_TIMER; + pnode = pback_node; + goto POOL_CHECK; + } else if (pdhcps_pool->ip.addr == client_address_plus.addr){ +// client_address.addr = client_address_plus.addr; +// os_printf("the ip addr has been request\n"); + addr_tmp.addr = htonl(client_address_plus.addr); + addr_tmp.addr++; + client_address_plus.addr = htonl(addr_tmp.addr); + client_address.addr = client_address_plus.addr; + } + + if(flag == false) { // search the fisrt unused ip + if(first_address.addr < pdhcps_pool->ip.addr) { + flag = true; + } else { + addr_tmp.addr = htonl(first_address.addr); + addr_tmp.addr++; + first_address.addr = htonl(addr_tmp.addr); + } + } + } + if (client_address_plus.addr > dhcps_lease.end_ip.addr) { + client_address.addr = first_address.addr; + } + if (client_address.addr > dhcps_lease.end_ip.addr) { + client_address_plus.addr = dhcps_lease.start_ip.addr; + pdhcps_pool = NULL; + pnode = NULL; + } else { + pdhcps_pool = (struct dhcps_pool *)os_zalloc(sizeof(struct dhcps_pool)); + pdhcps_pool->ip.addr = client_address.addr; + os_memcpy(pdhcps_pool->mac, m->chaddr, sizeof(pdhcps_pool->mac)); + pdhcps_pool->lease_timer = DHCPS_LEASE_TIMER; + pnode = (list_node *)os_zalloc(sizeof(list_node )); + pnode->pnode = pdhcps_pool; + pnode->pnext = NULL; + node_insert_to_list(&plist,pnode); + if (client_address.addr == dhcps_lease.end_ip.addr) { + client_address_plus.addr = dhcps_lease.start_ip.addr; + } else { + addr_tmp.addr = htonl(client_address.addr); + addr_tmp.addr++; + client_address_plus.addr = htonl(addr_tmp.addr); + } + } + + POOL_CHECK: + if ((client_address.addr > dhcps_lease.end_ip.addr) || (ip_addr_isany(&client_address))){ + os_printf("client_address_plus.addr %x %d\n", client_address_plus.addr, system_get_free_heap_size()); + if(pnode != NULL) { + node_remove_from_list(&plist,pnode); + os_free(pnode); + pnode = NULL; + } + + if (pdhcps_pool != NULL) { + os_free(pdhcps_pool); + pdhcps_pool = NULL; + } +// client_address_plus.addr = dhcps_lease.start_ip.addr; + return 4; + } + + sint16_t ret = parse_options(&m->options[4], len);; + + if(ret == DHCPS_STATE_RELEASE) { + if(pnode != NULL) { + node_remove_from_list(&plist,pnode); + os_free(pnode); + pnode = NULL; + } + + if (pdhcps_pool != NULL) { + os_free(pdhcps_pool); + pdhcps_pool = NULL; + } + os_memset(&client_address,0x0,sizeof(client_address)); + } + + if (wifi_softap_set_station_info(m->chaddr, &client_address) == false) { + return 0; + } +// } + +#if DHCPS_DEBUG + os_printf("dhcps: xid changed\n"); + os_printf("dhcps: client_address.addr = %x\n", client_address.addr); +#endif + +// } + +// } + return ret; + } + return 0; +} +/////////////////////////////////////////////////////////////////////////////////// +/* + * DHCP ��������ݰ���մ���ص�����˺�����LWIP UDPģ������ʱ������ + * ��Ҫ����udp_recv()������LWIP����ע��. + * + * @param arg + * @param pcb ���յ�UDP��Ŀ��ƿ�? + * @param p ���յ���UDP��������? + * @param addr ���ʹ�UDP���Դ�����IP��ַ + * @param port ���ʹ�UDP���Դ�����UDPͨ���˿ں� + */ +/////////////////////////////////////////////////////////////////////////////////// +static void ICACHE_FLASH_ATTR handle_dhcp(void *arg, + struct udp_pcb *pcb, + struct pbuf *p, + struct ip_addr *addr, + uint16_t port) +{ + struct dhcps_msg *pmsg_dhcps = NULL; + sint16_t tlen = 0; + u16_t i = 0; + u16_t dhcps_msg_cnt = 0; + u8_t *p_dhcps_msg = NULL; + u8_t *data = NULL; + +#if DHCPS_DEBUG + os_printf("dhcps: handle_dhcp-> receive a packet\n"); +#endif + if (p==NULL) return; + + pmsg_dhcps = (struct dhcps_msg *)os_zalloc(sizeof(struct dhcps_msg)); + if (NULL == pmsg_dhcps){ + pbuf_free(p); + return; + } + p_dhcps_msg = (u8_t *)pmsg_dhcps; + tlen = p->tot_len; + data = p->payload; + +#if DHCPS_DEBUG + os_printf("dhcps: handle_dhcp-> p->tot_len = %d\n", tlen); + os_printf("dhcps: handle_dhcp-> p->len = %d\n", p->len); +#endif + + for(i=0; ilen; i++){ + p_dhcps_msg[dhcps_msg_cnt++] = data[i]; +#if DHCPS_DEBUG + os_printf("%02x ",data[i]); + if((i+1)%16 == 0){ + os_printf("\n"); + } +#endif + } + + if(p->next != NULL) { +#if DHCPS_DEBUG + os_printf("dhcps: handle_dhcp-> p->next != NULL\n"); + os_printf("dhcps: handle_dhcp-> p->next->tot_len = %d\n",p->next->tot_len); + os_printf("dhcps: handle_dhcp-> p->next->len = %d\n",p->next->len); +#endif + + data = p->next->payload; + for(i=0; inext->len; i++){ + p_dhcps_msg[dhcps_msg_cnt++] = data[i]; +#if DHCPS_DEBUG + os_printf("%02x ",data[i]); + if((i+1)%16 == 0){ + os_printf("\n"); + } +#endif + } + } + + /* + * DHCP �ͻ���������Ϣ���� + */ +#if DHCPS_DEBUG + os_printf("dhcps: handle_dhcp-> parse_msg(p)\n"); +#endif + + switch(parse_msg(pmsg_dhcps, tlen - 240)) { + + case DHCPS_STATE_OFFER://1 +#if DHCPS_DEBUG + os_printf("dhcps: handle_dhcp-> DHCPD_STATE_OFFER\n"); +#endif + send_offer(pmsg_dhcps); + break; + case DHCPS_STATE_ACK://3 +#if DHCPS_DEBUG + os_printf("dhcps: handle_dhcp-> DHCPD_STATE_ACK\n"); +#endif + send_ack(pmsg_dhcps); + break; + case DHCPS_STATE_NAK://4 +#if DHCPS_DEBUG + os_printf("dhcps: handle_dhcp-> DHCPD_STATE_NAK\n"); +#endif + send_nak(pmsg_dhcps); + break; + default : + break; + } +#if DHCPS_DEBUG + os_printf("dhcps: handle_dhcp-> pbuf_free(p)\n"); +#endif + pbuf_free(p); + os_free(pmsg_dhcps); + pmsg_dhcps = NULL; +} +/////////////////////////////////////////////////////////////////////////////////// +static void ICACHE_FLASH_ATTR wifi_softap_init_dhcps_lease(uint32 ip) +{ + uint32 softap_ip = 0,local_ip = 0; + uint32 start_ip = 0; + uint32 end_ip = 0; +// if (dhcps_lease_flag) { + if (dhcps_lease.enable == TRUE) { + softap_ip = htonl(ip); + start_ip = htonl(dhcps_lease.start_ip.addr); + end_ip = htonl(dhcps_lease.end_ip.addr); + /*config ip information can't contain local ip*/ + if ((start_ip <= softap_ip) && (softap_ip <= end_ip)) { + dhcps_lease.enable = FALSE; + } else { + /*config ip information must be in the same segment as the local ip*/ + softap_ip >>= 8; + if (((start_ip >> 8 != softap_ip) || (end_ip >> 8 != softap_ip)) + || (end_ip - start_ip > DHCPS_MAX_LEASE)) { + dhcps_lease.enable = FALSE; + } + } + } + + if (dhcps_lease.enable == FALSE) { + local_ip = softap_ip = htonl(ip); + softap_ip &= 0xFFFFFF00; + local_ip &= 0xFF; + if (local_ip >= 0x80) + local_ip -= DHCPS_MAX_LEASE; + else + local_ip ++; + + os_bzero(&dhcps_lease, sizeof(dhcps_lease)); + dhcps_lease.start_ip.addr = softap_ip | local_ip; + dhcps_lease.end_ip.addr = softap_ip | (local_ip + DHCPS_MAX_LEASE - 1); + dhcps_lease.start_ip.addr = htonl(dhcps_lease.start_ip.addr); + dhcps_lease.end_ip.addr= htonl(dhcps_lease.end_ip.addr); + } +// dhcps_lease.start_ip.addr = htonl(dhcps_lease.start_ip.addr); +// dhcps_lease.end_ip.addr= htonl(dhcps_lease.end_ip.addr); +// os_printf("start_ip = 0x%x, end_ip = 0x%x\n",dhcps_lease.start_ip, dhcps_lease.end_ip); +} +/////////////////////////////////////////////////////////////////////////////////// +void ICACHE_FLASH_ATTR dhcps_start(struct ip_info *info) +{ + struct netif * apnetif = (struct netif *)eagle_lwip_getif(0x01); + + if(apnetif->dhcps_pcb != NULL) { + udp_remove(apnetif->dhcps_pcb); + } + + pcb_dhcps = udp_new(); + if (pcb_dhcps == NULL || info ==NULL) { + os_printf("dhcps_start(): could not obtain pcb\n"); + } + + apnetif->dhcps_pcb = pcb_dhcps; + + IP4_ADDR(&broadcast_dhcps, 255, 255, 255, 255); + + server_address = info->ip; + wifi_softap_init_dhcps_lease(server_address.addr); + client_address_plus.addr = dhcps_lease.start_ip.addr; + + udp_bind(pcb_dhcps, IP_ADDR_ANY, DHCPS_SERVER_PORT); + udp_recv(pcb_dhcps, handle_dhcp, NULL); +#if DHCPS_DEBUG + os_printf("dhcps:dhcps_start->udp_recv function Set a receive callback handle_dhcp for UDP_PCB pcb_dhcps\n"); +#endif + +} + +void ICACHE_FLASH_ATTR dhcps_stop(void) +{ + struct netif * apnetif = (struct netif *)eagle_lwip_getif(0x01); + + udp_disconnect(pcb_dhcps); +// dhcps_lease_flag = true; + if(apnetif->dhcps_pcb != NULL) { + udp_remove(apnetif->dhcps_pcb); + apnetif->dhcps_pcb = NULL; + } + + //udp_remove(pcb_dhcps); + list_node *pnode = NULL; + list_node *pback_node = NULL; + pnode = plist; + while (pnode != NULL) { + pback_node = pnode; + pnode = pback_node->pnext; + node_remove_from_list(&plist, pback_node); + os_free(pback_node->pnode); + pback_node->pnode = NULL; + os_free(pback_node); + pback_node = NULL; + } +} + +/****************************************************************************** + * FunctionName : wifi_softap_set_dhcps_lease + * Description : set the lease information of DHCP server + * Parameters : please -- Additional argument to set the lease information, + * Little-Endian. + * Returns : true or false +*******************************************************************************/ +bool ICACHE_FLASH_ATTR wifi_softap_set_dhcps_lease(struct dhcps_lease *please) +{ + struct ip_info info; + uint32 softap_ip = 0; + uint32 start_ip = 0; + uint32 end_ip = 0; + + uint8 opmode = wifi_get_opmode(); + + if (opmode == STATION_MODE || opmode == NULL_MODE) { + return false; + } + + if (please == NULL || wifi_softap_dhcps_status() == DHCP_STARTED) + return false; + + if(please->enable) { + os_bzero(&info, sizeof(struct ip_info)); + wifi_get_ip_info(SOFTAP_IF, &info); + softap_ip = htonl(info.ip.addr); + start_ip = htonl(please->start_ip.addr); + end_ip = htonl(please->end_ip.addr); + + /*config ip information can't contain local ip*/ + if ((start_ip <= softap_ip) && (softap_ip <= end_ip)) + return false; + + /*config ip information must be in the same segment as the local ip*/ + softap_ip >>= 8; + if ((start_ip >> 8 != softap_ip) + || (end_ip >> 8 != softap_ip)) { + return false; + } + + if (end_ip - start_ip > DHCPS_MAX_LEASE) + return false; + + os_bzero(&dhcps_lease, sizeof(dhcps_lease)); +// dhcps_lease.start_ip.addr = start_ip; +// dhcps_lease.end_ip.addr = end_ip; + dhcps_lease.start_ip.addr = please->start_ip.addr; + dhcps_lease.end_ip.addr = please->end_ip.addr; + } + dhcps_lease.enable = please->enable; +// dhcps_lease_flag = false; + return true; +} + +/****************************************************************************** + * FunctionName : wifi_softap_get_dhcps_lease + * Description : get the lease information of DHCP server + * Parameters : please -- Additional argument to get the lease information, + * Little-Endian. + * Returns : true or false +*******************************************************************************/ +bool ICACHE_FLASH_ATTR wifi_softap_get_dhcps_lease(struct dhcps_lease *please) +{ + uint8 opmode = wifi_get_opmode(); + + if (opmode == STATION_MODE || opmode == NULL_MODE) { + return false; + } + + if (NULL == please) + return false; + +// if (dhcps_lease_flag){ + if (dhcps_lease.enable == FALSE){ + if (wifi_softap_dhcps_status() == DHCP_STOPPED) + return false; + } else { +// os_bzero(please, sizeof(dhcps_lease)); +// if (wifi_softap_dhcps_status() == DHCP_STOPPED){ +// please->start_ip.addr = htonl(dhcps_lease.start_ip.addr); +// please->end_ip.addr = htonl(dhcps_lease.end_ip.addr); +// } + } + +// if (wifi_softap_dhcps_status() == DHCP_STARTED){ +// os_bzero(please, sizeof(dhcps_lease)); +// please->start_ip.addr = dhcps_lease.start_ip.addr; +// please->end_ip.addr = dhcps_lease.end_ip.addr; +// } + please->start_ip.addr = dhcps_lease.start_ip.addr; + please->end_ip.addr = dhcps_lease.end_ip.addr; + return true; +} + +static void ICACHE_FLASH_ATTR kill_oldest_dhcps_pool(void) +{ + list_node *pre = NULL, *p = NULL; + list_node *minpre = NULL, *minp = NULL; + struct dhcps_pool *pdhcps_pool = NULL, *pmin_pool = NULL; + pre = plist; + p = pre->pnext; + minpre = pre; + minp = p; + while (p != NULL){ + pdhcps_pool = p->pnode; + pmin_pool = minp->pnode; + if (pdhcps_pool->lease_timer < pmin_pool->lease_timer){ + minp = p; + minpre = pre; + } + pre = p; + p = p->pnext; + } + minpre->pnext = minp->pnext; + os_free(minp->pnode); + minp->pnode = NULL; + os_free(minp); + minp = NULL; +} + +void ICACHE_FLASH_ATTR dhcps_coarse_tmr(void) +{ + uint8 num_dhcps_pool = 0; + list_node *pback_node = NULL; + list_node *pnode = NULL; + struct dhcps_pool *pdhcps_pool = NULL; + pnode = plist; + while (pnode != NULL) { + pdhcps_pool = pnode->pnode; + pdhcps_pool->lease_timer --; + if (pdhcps_pool->lease_timer == 0){ + pback_node = pnode; + pnode = pback_node->pnext; + node_remove_from_list(&plist,pback_node); + os_free(pback_node->pnode); + pback_node->pnode = NULL; + os_free(pback_node); + pback_node = NULL; + } else { + pnode = pnode ->pnext; + num_dhcps_pool ++; + } + } + + if (num_dhcps_pool >= MAX_STATION_NUM) + kill_oldest_dhcps_pool(); +} + +bool ICACHE_FLASH_ATTR wifi_softap_set_dhcps_offer_option(uint8 level, void* optarg) +{ + bool offer_flag = true; + if (optarg == NULL && wifi_softap_dhcps_status() == false) + return false; + + if (level <= OFFER_START || level >= OFFER_END) + return false; + + switch (level){ + case OFFER_ROUTER: + offer = (*(uint8 *)optarg) & 0x01; + offer_flag = true; + break; + default : + offer_flag = false; + break; + } + return offer_flag; +} + +bool ICACHE_FLASH_ATTR wifi_softap_set_dhcps_lease_time(uint32 minute) +{ + uint8 opmode = wifi_get_opmode(); + + if (opmode == STATION_MODE || opmode == NULL_MODE) { + return false; + } + + if (wifi_softap_dhcps_status() == DHCP_STARTED) { + return false; + } + + if(minute == 0) { + return false; + } + dhcps_lease_time = minute; + return true; +} + +bool ICACHE_FLASH_ATTR wifi_softap_reset_dhcps_lease_time(void) +{ + uint8 opmode = wifi_get_opmode(); + + if (opmode == STATION_MODE || opmode == NULL_MODE) { + return false; + } + + if (wifi_softap_dhcps_status() == DHCP_STARTED) { + return false; + } + dhcps_lease_time = DHCPS_LEASE_TIME_DEF; + return true; +} + +uint32 ICACHE_FLASH_ATTR wifi_softap_get_dhcps_lease_time(void) // minute +{ + return dhcps_lease_time; +} diff --git a/variants/lwip/lwip/app/espconn.c b/variants/lwip/lwip/app/espconn.c new file mode 100755 index 000000000..179fa174e --- /dev/null +++ b/variants/lwip/lwip/app/espconn.c @@ -0,0 +1,1303 @@ +/****************************************************************************** + * Copyright 2013-2014 Espressif Systems (Wuxi) + * + * FileName: espconn.c + * + * Description: espconn interface for user + * + * Modification history: + * 2014/3/31, v1.0 create this file. +*******************************************************************************/ + +#include "lwip/netif.h" +#include "lwip/inet.h" +#include "netif/etharp.h" +#include "lwip/tcp.h" +#include "lwip/ip.h" +#include "lwip/init.h" +#include "ets_sys.h" +#include "os_type.h" +//#include "os.h" +#include "lwip/mem.h" + +#include "lwip/app/espconn_tcp.h" +#include "lwip/app/espconn_udp.h" +#include "lwip/app/espconn.h" +#include "user_interface.h" + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +espconn_msg *plink_active = NULL; +espconn_msg *pserver_list = NULL; +remot_info premot[linkMax]; + +struct espconn_packet pktinfo[2]; + +static uint8 espconn_tcp_get_buf_count(espconn_buf *pesp_buf); +/****************************************************************************** + * FunctionName : espconn_copy_partial + * Description : reconnect with host + * Parameters : arg -- Additional argument to pass to the callback function + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR +espconn_copy_partial(struct espconn *pesp_dest, struct espconn *pesp_source) +{ + pesp_dest->type = pesp_source->type; + pesp_dest->state = pesp_source->state; + if (pesp_source->type == ESPCONN_TCP){ + pesp_dest->proto.tcp->remote_port = pesp_source->proto.tcp->remote_port; + pesp_dest->proto.tcp->local_port = pesp_source->proto.tcp->local_port; + os_memcpy(pesp_dest->proto.tcp->remote_ip, pesp_source->proto.tcp->remote_ip, 4); + os_memcpy(pesp_dest->proto.tcp->local_ip, pesp_source->proto.tcp->local_ip, 4); + pesp_dest->proto.tcp->connect_callback = pesp_source->proto.tcp->connect_callback; + pesp_dest->proto.tcp->reconnect_callback = pesp_source->proto.tcp->reconnect_callback; + pesp_dest->proto.tcp->disconnect_callback = pesp_source->proto.tcp->disconnect_callback; + } else { + pesp_dest->proto.udp->remote_port = pesp_source->proto.udp->remote_port; + pesp_dest->proto.udp->local_port = pesp_source->proto.udp->local_port; + os_memcpy(pesp_dest->proto.udp->remote_ip, pesp_source->proto.udp->remote_ip, 4); + os_memcpy(pesp_dest->proto.udp->local_ip, pesp_source->proto.udp->local_ip, 4); + } + pesp_dest->recv_callback = pesp_source->recv_callback; + pesp_dest->sent_callback = pesp_source->sent_callback; + pesp_dest->link_cnt = pesp_source->link_cnt; + pesp_dest->reverse = pesp_source->reverse; +} + +/****************************************************************************** + * FunctionName : espconn_copy_partial + * Description : insert the node to the active connection list + * Parameters : arg -- Additional argument to pass to the callback function + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR espconn_list_creat(espconn_msg **phead, espconn_msg* pinsert) +{ + espconn_msg *plist = NULL; +// espconn_msg *ptest = NULL; + if (*phead == NULL) + *phead = pinsert; + else { + plist = *phead; + while (plist->pnext != NULL) { + plist = plist->pnext; + } + plist->pnext = pinsert; + } + pinsert->pnext = NULL; + +/* ptest = *phead; + while(ptest != NULL){ + os_printf("espconn_list_creat %p\n", ptest); + ptest = ptest->pnext; + }*/ +} + +/****************************************************************************** + * FunctionName : espconn_list_delete + * Description : remove the node from the active connection list + * Parameters : arg -- Additional argument to pass to the callback function + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR espconn_list_delete(espconn_msg **phead, espconn_msg* pdelete) +{ + espconn_msg *plist = NULL; +// espconn_msg *ptest = NULL; + plist = *phead; + if (plist == NULL){ + *phead = NULL; + } else { + if (plist == pdelete){ + *phead = plist->pnext; + } else { + while (plist != NULL) { + if (plist->pnext == pdelete){ + plist->pnext = pdelete->pnext; + } + plist = plist->pnext; + } + } + } +/* ptest = *phead; + while(ptest != NULL){ + os_printf("espconn_list_delete %p\n", ptest); + ptest = ptest->pnext; + }*/ +} + +/****************************************************************************** + * FunctionName : espconn_pbuf_create + * Description : insert the node to the active connection list + * Parameters : arg -- Additional argument to pass to the callback function + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR espconn_pbuf_create(espconn_buf **phead, espconn_buf* pinsert) +{ + espconn_buf *plist = NULL; + + if (*phead == NULL) + *phead = pinsert; + else { + plist = *phead; + while (plist->pnext != NULL) { + plist = plist->pnext; + } + plist->pnext = pinsert; + } + pinsert->pnext = NULL; +} + +/****************************************************************************** + * FunctionName : espconn_pbuf_delete + * Description : remove the node from the active connection list + * Parameters : arg -- Additional argument to pass to the callback function + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR espconn_pbuf_delete(espconn_buf **phead, espconn_buf* pdelete) +{ + espconn_buf *plist = NULL; + + plist = *phead; + if (plist == NULL){ + *phead = NULL; + } else { + if (plist == pdelete){ + *phead = plist->pnext; + } else { + while (plist != NULL) { + if (plist->pnext == pdelete){ + plist->pnext = pdelete->pnext; + } + plist = plist->pnext; + } + } + } +} + +/****************************************************************************** + * FunctionName : espconn_find_connection + * Description : Initialize the server: set up a listening PCB and bind it to + * the defined port + * Parameters : espconn -- the espconn used to build server + * Returns : true or false + *******************************************************************************/ +bool ICACHE_FLASH_ATTR espconn_find_connection(struct espconn *pespconn, espconn_msg **pnode) +{ + espconn_msg *plist = NULL; + struct ip_addr ip_remot; + struct ip_addr ip_list; + + if (pespconn == NULL) + return false; + + /*find the active connection node*/ + for (plist = plink_active; plist != NULL; plist = plist->pnext){ + if (pespconn == plist->pespconn) { + *pnode = plist; + return true; + } + } + + /*find the active server node*/ + for (plist = pserver_list; plist != NULL; plist = plist->pnext){ + if (pespconn == plist->pespconn) { + if (pespconn->proto.tcp == NULL) + return false; + + IP4_ADDR(&ip_remot, pespconn->proto.tcp->remote_ip[0], + pespconn->proto.tcp->remote_ip[1], + pespconn->proto.tcp->remote_ip[2], + pespconn->proto.tcp->remote_ip[3]); + if ((ip_remot.addr == IPADDR_ANY) || (pespconn->proto.tcp->remote_port == 0)) + return false; + + /*find the active connection node*/ + for (plist = plink_active; plist != NULL; plist = plist->pnext){ + IP4_ADDR(&ip_list, plist->pcommon.remote_ip[0], + plist->pcommon.remote_ip[1], plist->pcommon.remote_ip[2], + plist->pcommon.remote_ip[3]); + if ((ip_list.addr == ip_remot.addr) && (pespconn->proto.tcp->remote_port == plist->pcommon.remote_port)) { + *pnode = plist; + return true; + } + } + return false; + } + } + return false; +} + +/****************************************************************************** + * FunctionName : espconn_get_acticve_num + * Description : get the count of simulatenously active connections + * Parameters : type -- the type + * Returns : the count of simulatenously active connections + *******************************************************************************/ +static uint8 ICACHE_FLASH_ATTR +espconn_get_acticve_num(uint8 type) +{ + espconn_msg *plist = NULL; + uint8 num_tcp_active = 0; + + for (plist = plink_active; plist != NULL; plist = plist->pnext) { + if (plist->pespconn != NULL && plist->pespconn->type == type) { + num_tcp_active++; + } + } + + return num_tcp_active; +} + +/****************************************************************************** + * FunctionName : espconn_connect + * Description : The function given as the connect + * Parameters : espconn -- the espconn used to listen the connection + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_connect(struct espconn *espconn) +{ + struct ip_addr ipaddr; + struct ip_info ipinfo; + uint8 connect_status = 0; + sint8 value = ESPCONN_OK; + espconn_msg *plist = NULL; + + if (espconn == NULL) { + return ESPCONN_ARG; + } else if (espconn ->type != ESPCONN_TCP) + return ESPCONN_ARG; + + /*Check the active node count whether is the limit or not*/ + if (espconn_get_acticve_num(ESPCONN_TCP) >= espconn_tcp_get_max_con()) + return ESPCONN_ISCONN; + + /*Check the IP address whether is zero or not in different mode*/ + if (wifi_get_opmode() == ESPCONN_STA){ + wifi_get_ip_info(STA_NETIF,&ipinfo); + if (ipinfo.ip.addr == 0){ + return ESPCONN_RTE; + } + } else if(wifi_get_opmode() == ESPCONN_AP){ + wifi_get_ip_info(AP_NETIF,&ipinfo); + if (ipinfo.ip.addr == 0){ + return ESPCONN_RTE; + } + } else if(wifi_get_opmode() == ESPCONN_AP_STA){ + IP4_ADDR(&ipaddr, espconn->proto.tcp->remote_ip[0], + espconn->proto.tcp->remote_ip[1], + espconn->proto.tcp->remote_ip[2], + espconn->proto.tcp->remote_ip[3]); + ipaddr.addr <<= 8; + wifi_get_ip_info(AP_NETIF,&ipinfo); + ipinfo.ip.addr <<= 8; + espconn_printf("softap_addr = %x, remote_addr = %x\n", ipinfo.ip.addr, ipaddr.addr); + + if (ipaddr.addr != ipinfo.ip.addr){ + connect_status = wifi_station_get_connect_status(); + if (connect_status == STATION_GOT_IP){ + wifi_get_ip_info(STA_NETIF,&ipinfo); + if (ipinfo.ip.addr == 0) + return ESPCONN_RTE; + } else if (connect_status == STATION_IDLE){ + return ESPCONN_RTE; + } else { + return connect_status; + } + } + } + + /*check the active node information whether is the same as the entity or not*/ + for (plist = plink_active; plist != NULL; plist = plist->pnext){ + if (plist->pespconn && plist->pespconn->type == ESPCONN_TCP){ + if (espconn->proto.tcp->local_port == plist->pespconn->proto.tcp->local_port){ + return ESPCONN_ISCONN; + } + } + } + + value = espconn_tcp_client(espconn); + + return value; +} + +/****************************************************************************** + * FunctionName : espconn_create + * Description : sent data for client or server + * Parameters : espconn -- espconn to the data transmission + * Returns : result +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_create(struct espconn *espconn) +{ + sint8 value = ESPCONN_OK; + espconn_msg *plist = NULL; + + if (espconn == NULL) { + return ESPCONN_ARG; + } else if (espconn ->type != ESPCONN_UDP){ + return ESPCONN_ARG; + } + + /*check the active node information whether is the same as the entity or not*/ + for (plist = plink_active; plist != NULL; plist = plist->pnext){ + if (plist->pespconn && plist->pespconn->type == ESPCONN_UDP){ + if (espconn->proto.udp->local_port == plist->pespconn->proto.udp->local_port){ + return ESPCONN_ISCONN; + } + } + } + + value = espconn_udp_server(espconn); + + return value; +} + +/****************************************************************************** + * FunctionName : espconn_sent + * Description : sent data for client or server + * Parameters : espconn -- espconn to set for client or server + * psent -- data to send + * length -- length of data to send + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_sent(struct espconn *espconn, uint8 *psent, uint16 length) +{ + espconn_msg *pnode = NULL; + bool value = false; + err_t error = ESPCONN_OK; + + if (espconn == NULL || psent == NULL || length == 0) { + return ESPCONN_ARG; + } + + /*Find the node depend on the espconn message*/ + value = espconn_find_connection(espconn, &pnode); + + if (value){ + espconn ->state = ESPCONN_WRITE; + switch (espconn ->type) { + case ESPCONN_TCP: + /* calling sent function frequently,make sure last packet has been backup or sent fully*/ + if (pnode->pcommon.write_flag){ + espconn_buf *pbuf = NULL; + /*If total number of espconn_buf on the unsent lists exceeds the set maximum, return an error */ + if (espconn_copy_enabled(pnode)){ + if (espconn_tcp_get_buf_count(pnode->pcommon.pbuf) >= pnode ->pcommon.pbuf_num) + return ESPCONN_MAXNUM; + } else { + struct tcp_pcb *pcb = pnode->pcommon.pcb; + if (pcb->snd_queuelen >= TCP_SND_QUEUELEN) + return ESPCONN_MAXNUM; + } + + pbuf = (espconn_buf*) os_zalloc(sizeof(espconn_buf)); + if (pbuf == NULL) + return ESPCONN_MEM; + else { + /*Backup the application packet information for send more data*/ + pbuf->payload = psent; + pbuf->punsent = pbuf->payload; + pbuf->unsent = length; + pbuf->len = length; + /*insert the espconn_pbuf to the list*/ + espconn_pbuf_create(&pnode->pcommon.pbuf, pbuf); + if (pnode->pcommon.ptail == NULL) + pnode->pcommon.ptail = pbuf; + } + /*when set the data copy option. change the flag for next packet*/ + if (espconn_copy_disabled(pnode)) + pnode->pcommon.write_flag = false; + error = espconn_tcp_write(pnode); +// if (error != ESPCONN_OK){ +// /*send the application packet fail, +// * ensure that each allocated is deleted*/ +// espconn_pbuf_delete(&pnode->pcommon.pbuf, pbuf); +// os_free(pbuf); +// pbuf = NULL; +// } + return error; + } else + return ESPCONN_ARG; + break; + + case ESPCONN_UDP: + return espconn_udp_sent(pnode, psent, length); + break; + + default : + break; + } + } + return ESPCONN_ARG; +} + +/****************************************************************************** + * FunctionName : espconn_sendto + * Description : send data for UDP + * Parameters : espconn -- espconn to set for UDP + * psent -- data to send + * length -- length of data to send + * Returns : error +*******************************************************************************/ +sint16 ICACHE_FLASH_ATTR +espconn_sendto(struct espconn *espconn, uint8 *psent, uint16 length) +{ + espconn_msg *pnode = NULL; + bool value = false; + + if (espconn == NULL || psent == NULL || length == 0) { + return ESPCONN_ARG; + } + + /*Find the node depend on the espconn message*/ + value = espconn_find_connection(espconn, &pnode); + if (value && espconn->type == ESPCONN_UDP) + return espconn_udp_sendto(pnode, psent, length); + else + return ESPCONN_ARG; +} + +/****************************************************************************** + * FunctionName : espconn_send + * Description : sent data for client or server + * Parameters : espconn -- espconn to set for client or server + * psent -- data to send + * length -- length of data to send + * Returns : none +*******************************************************************************/ + +sint8 espconn_send(struct espconn *espconn, uint8 *psent, uint16 length) __attribute__((alias("espconn_sent"))); + +/****************************************************************************** + * FunctionName : espconn_tcp_get_wnd + * Description : get the window size of simulatenously active TCP connections + * Parameters : none + * Returns : the number of TCP_MSS active TCP connections +*******************************************************************************/ +uint8 ICACHE_FLASH_ATTR espconn_tcp_get_wnd(void) +{ + uint8 tcp_num = 0; + + tcp_num = (TCP_WND / TCP_MSS); + + return tcp_num; +} +/****************************************************************************** + * FunctionName : espconn_tcp_set_max_con + * Description : set the window size simulatenously active TCP connections + * Parameters : num -- the number of TCP_MSS + * Returns : ESPCONN_ARG -- Illegal argument + * ESPCONN_OK -- No error +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR espconn_tcp_set_wnd(uint8 num) +{ + if (num == 0 || num > linkMax) + return ESPCONN_ARG; + + TCP_WND = (num * TCP_MSS); + return ESPCONN_OK; +} + +/****************************************************************************** + * FunctionName : espconn_tcp_get_mss + * Description : get the mss size of simulatenously active TCP connections + * Parameters : none + * Returns : the size of TCP_MSS active TCP connections +*******************************************************************************/ +uint16 ICACHE_FLASH_ATTR espconn_tcp_get_mss(void) +{ + uint16 tcp_num = 0; + + tcp_num = TCP_MSS; + + return tcp_num; +} + +/****************************************************************************** + * FunctionName : espconn_tcp_get_max_con + * Description : get the number of simulatenously active TCP connections + * Parameters : espconn -- espconn to set the connect callback + * Returns : none +*******************************************************************************/ +uint8 ICACHE_FLASH_ATTR espconn_tcp_get_max_con(void) +{ + uint8 tcp_num = 0; + + tcp_num = MEMP_NUM_TCP_PCB; + + return tcp_num; +} + +/****************************************************************************** + * FunctionName : espconn_tcp_set_max_con + * Description : set the number of simulatenously active TCP connections + * Parameters : espconn -- espconn to set the connect callback + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR espconn_tcp_set_max_con(uint8 num) +{ + if (num == 0 || num > linkMax) + return ESPCONN_ARG; + + MEMP_NUM_TCP_PCB = num; + return ESPCONN_OK; +} + +/****************************************************************************** + * FunctionName : espconn_tcp_get_max_retran + * Description : get the Maximum number of retransmissions of data active TCP connections + * Parameters : none + * Returns : the Maximum number of retransmissions +*******************************************************************************/ +uint8 ICACHE_FLASH_ATTR espconn_tcp_get_max_retran(void) +{ + uint8 tcp_num = 0; + + tcp_num = TCP_MAXRTX; + + return tcp_num; +} + +/****************************************************************************** + * FunctionName : espconn_tcp_set_max_retran + * Description : set the Maximum number of retransmissions of data active TCP connections + * Parameters : num -- the Maximum number of retransmissions + * Returns : result +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR espconn_tcp_set_max_retran(uint8 num) +{ + if (num == 0 || num > 12) + return ESPCONN_ARG; + + TCP_MAXRTX = num; + return ESPCONN_OK; +} + +/****************************************************************************** + * FunctionName : espconn_tcp_get_max_syn + * Description : get the Maximum number of retransmissions of SYN segments + * Parameters : none + * Returns : the Maximum number of retransmissions +*******************************************************************************/ +uint8 ICACHE_FLASH_ATTR espconn_tcp_get_max_syn(void) +{ + uint8 tcp_num = 0; + + tcp_num = TCP_SYNMAXRTX; + + return tcp_num; +} + +/****************************************************************************** + * FunctionName : espconn_tcp_set_max_syn + * Description : set the Maximum number of retransmissions of SYN segments + * Parameters : num -- the Maximum number of retransmissions + * Returns : result +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR espconn_tcp_set_max_syn(uint8 num) +{ + if (num == 0 || num > 12) + return ESPCONN_ARG; + + TCP_SYNMAXRTX = num; + return ESPCONN_OK; +} + +/****************************************************************************** + * FunctionName : espconn_tcp_get_max_con_allow + * Description : get the count of simulatenously active connections on the server + * Parameters : espconn -- espconn to get the count + * Returns : result +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR espconn_tcp_get_max_con_allow(struct espconn *espconn) +{ + espconn_msg *pget_msg = NULL; + if ((espconn == NULL) || (espconn->type == ESPCONN_UDP)) + return ESPCONN_ARG; + + pget_msg = pserver_list; + while (pget_msg != NULL){ + if (pget_msg->pespconn == espconn){ + return pget_msg->count_opt; + } + pget_msg = pget_msg->pnext; + } + return ESPCONN_ARG; +} + +/****************************************************************************** + * FunctionName : espconn_tcp_set_max_con_allow + * Description : set the count of simulatenously active connections on the server + * Parameters : espconn -- espconn to set the count + * Returns : result +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR espconn_tcp_set_max_con_allow(struct espconn *espconn, uint8 num) +{ + espconn_msg *pset_msg = NULL; + if ((espconn == NULL) || (num > MEMP_NUM_TCP_PCB) || (espconn->type == ESPCONN_UDP)) + return ESPCONN_ARG; + + pset_msg = pserver_list; + while (pset_msg != NULL){ + if (pset_msg->pespconn == espconn){ + pset_msg->count_opt = num; + return ESPCONN_OK; + } + pset_msg = pset_msg->pnext; + } + return ESPCONN_ARG; +} + +/****************************************************************************** + * FunctionName : espconn_tcp_set_buf_count + * Description : set the total number of espconn_buf on the unsent lists for one + * activate connection + * Parameters : espconn -- espconn to set the count + * num -- the total number of espconn_buf + * Returns : result +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR espconn_tcp_set_buf_count(struct espconn *espconn, uint8 num) +{ + espconn_msg *plist = NULL; + if (espconn == NULL || (num > TCP_SND_QUEUELEN)) + return ESPCONN_ARG; + + /*find the node from the active connection list*/ + for (plist = plink_active; plist != NULL; plist = plist->pnext){ + if (plist->pespconn && plist->pespconn == espconn && espconn->type == ESPCONN_TCP){ + plist->pcommon.pbuf_num = num; + return ESPCONN_OK; + } + } + + return ESPCONN_ARG; +} + +/****************************************************************************** + * FunctionName : espconn_tcp_get_buf_count + * Description : get the count of the current node which has espconn_buf + * Parameters : pesp_buf -- the list head of espconn_buf type + * Returns : the count of the current node which has espconn_buf +*******************************************************************************/ +static uint8 ICACHE_FLASH_ATTR espconn_tcp_get_buf_count(espconn_buf *pesp_buf) +{ + espconn_buf *pbuf_list = pesp_buf; + uint8 pbuf_num = 0; + + /*polling the list get the count of the current node*/ + while (pbuf_list != NULL){ + pbuf_list = pbuf_list->pnext; + pbuf_num ++; + } + return pbuf_num; +} + +/****************************************************************************** + * FunctionName : espconn_regist_sentcb + * Description : Used to specify the function that should be called when data + * has been successfully delivered to the remote host. + * Parameters : espconn -- espconn to set the sent callback + * sent_cb -- sent callback function to call for this espconn + * when data is successfully sent + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_regist_sentcb(struct espconn *espconn, espconn_sent_callback sent_cb) +{ + if (espconn == NULL) { + return ESPCONN_ARG; + } + + espconn ->sent_callback = sent_cb; + return ESPCONN_OK; +} + +/****************************************************************************** + * FunctionName : espconn_regist_sentcb + * Description : Used to specify the function that should be called when data + * has been successfully delivered to the remote host. + * Parameters : espconn -- espconn to set the sent callback + * sent_cb -- sent callback function to call for this espconn + * when data is successfully sent + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_regist_write_finish(struct espconn *espconn, espconn_connect_callback write_finish_fn) +{ + if (espconn == NULL || espconn ->proto.tcp == NULL || espconn->type == ESPCONN_UDP) { + return ESPCONN_ARG; + } + + espconn ->proto.tcp->write_finish_fn = write_finish_fn; + return ESPCONN_OK; +} + +/****************************************************************************** + * FunctionName : espconn_regist_connectcb + * Description : used to specify the function that should be called when + * connects to host. + * Parameters : espconn -- espconn to set the connect callback + * connect_cb -- connected callback function to call when connected + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_regist_connectcb(struct espconn *espconn, espconn_connect_callback connect_cb) +{ + if (espconn == NULL) { + return ESPCONN_ARG; + } + + espconn->proto.tcp->connect_callback = connect_cb; + return ESPCONN_OK; +} + +/****************************************************************************** + * FunctionName : espconn_regist_recvcb + * Description : used to specify the function that should be called when recv + * data from host. + * Parameters : espconn -- espconn to set the recv callback + * recv_cb -- recv callback function to call when recv data + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_regist_recvcb(struct espconn *espconn, espconn_recv_callback recv_cb) +{ + if (espconn == NULL) { + return ESPCONN_ARG; + } + + espconn ->recv_callback = recv_cb; + return ESPCONN_OK; +} + +/****************************************************************************** + * FunctionName : espconn_regist_reconcb + * Description : used to specify the function that should be called when connection + * because of err disconnect. + * Parameters : espconn -- espconn to set the err callback + * recon_cb -- err callback function to call when err + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_regist_reconcb(struct espconn *espconn, espconn_reconnect_callback recon_cb) +{ + if (espconn == NULL) { + return ESPCONN_ARG; + } + + espconn ->proto.tcp->reconnect_callback = recon_cb; + return ESPCONN_OK; +} + +/****************************************************************************** + * FunctionName : espconn_regist_disconcb + * Description : used to specify the function that should be called when disconnect + * Parameters : espconn -- espconn to set the err callback + * discon_cb -- err callback function to call when err + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_regist_disconcb(struct espconn *espconn, espconn_connect_callback discon_cb) +{ + if (espconn == NULL) { + return ESPCONN_ARG; + } + + espconn ->proto.tcp->disconnect_callback = discon_cb; + return ESPCONN_OK; +} + +/****************************************************************************** + * FunctionName : espconn_get_connection_info + * Description : used to specify the function that should be called when disconnect + * Parameters : espconn -- espconn to set the err callback + * discon_cb -- err callback function to call when err + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_get_connection_info(struct espconn *pespconn, remot_info **pcon_info, uint8 typeflags) +{ + espconn_msg *plist = NULL; + + if (pespconn == NULL) + return ESPCONN_ARG; + + os_memset(premot, 0, sizeof(premot)); + pespconn->link_cnt = 0; + plist = plink_active; + switch (pespconn->type){ + case ESPCONN_TCP: + while(plist != NULL){ + if (plist->preverse == pespconn){ + premot[pespconn->link_cnt].state = plist->pespconn->state; + premot[pespconn->link_cnt].remote_port = plist->pcommon.remote_port; + os_memcpy(premot[pespconn->link_cnt].remote_ip, plist->pcommon.remote_ip, 4); + pespconn->link_cnt ++; + } + plist = plist->pnext; + } + + break; + case ESPCONN_UDP: + while(plist != NULL){ + if (plist->pespconn == pespconn){ + premot[pespconn->link_cnt].state = plist->pespconn->state; + premot[pespconn->link_cnt].remote_port = plist->pcommon.remote_port; + os_memcpy(premot[pespconn->link_cnt].remote_ip, plist->pcommon.remote_ip, 4); + pespconn->link_cnt ++; + } + plist = plist->pnext; + } + break; + default: + break; + } + *pcon_info = premot; + if (pespconn->link_cnt == 0) + return ESPCONN_ARG; + return ESPCONN_OK; +} + +/****************************************************************************** + * FunctionName : espconn_accept + * Description : The function given as the listen + * Parameters : espconn -- the espconn used to listen the connection + * Returns : +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_accept(struct espconn *espconn) +{ + sint8 value = ESPCONN_OK; + espconn_msg *plist = NULL; + + if (espconn == NULL) { + return ESPCONN_ARG; + } else if (espconn ->type != ESPCONN_TCP) + return ESPCONN_ARG; + + /*check the active node information whether is the same as the entity or not*/ + for (plist = plink_active; plist != NULL; plist = plist->pnext){ + if (plist->pespconn && plist->pespconn->type == ESPCONN_TCP){ + if (espconn->proto.tcp->local_port == plist->pespconn->proto.tcp->local_port){ + return ESPCONN_ISCONN; + } + } + } + value = espconn_tcp_server(espconn); + + return value; +} + +/****************************************************************************** + * FunctionName : espconn_regist_time + * Description : used to specify the time that should be called when don't recv data + * Parameters : espconn -- the espconn used to the connection + * interval -- the timer when don't recv data + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR espconn_regist_time(struct espconn *espconn, uint32 interval, uint8 type_flag) +{ + espconn_msg *pnode = NULL; + espconn_msg *ptime_msg = NULL; + bool value = false; + if ((espconn == NULL) || (type_flag > 0x01)) + return ESPCONN_ARG; + + if (type_flag == 0x01){ + /*set the timeout time for one active connection of the server*/ + value = espconn_find_connection(espconn, &pnode); + if (value){ + pnode->pcommon.timeout = interval; + return ESPCONN_OK; + } else + return ESPCONN_ARG; + } else { + /*set the timeout time for all active connection of the server*/ + ptime_msg = pserver_list; + while (ptime_msg != NULL){ + if (ptime_msg->pespconn == espconn){ + ptime_msg->pcommon.timeout = interval; + return ESPCONN_OK; + } + ptime_msg = ptime_msg->pnext; + } + } + return ESPCONN_ARG; +} + +/****************************************************************************** + * FunctionName : espconn_disconnect + * Description : disconnect with host + * Parameters : espconn -- the espconn used to disconnect the connection + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_disconnect(struct espconn *espconn) +{ + espconn_msg *pnode = NULL; + bool value = false; + + if (espconn == NULL) { + return ESPCONN_ARG;; + } else if (espconn ->type != ESPCONN_TCP) + return ESPCONN_ARG; + + /*Find the node depend on the espconn message*/ + value = espconn_find_connection(espconn, &pnode); + + if (value){ + /*protect for redisconnection*/ + if (espconn->state == ESPCONN_CLOSE) + return ESPCONN_INPROGRESS; + espconn_tcp_disconnect(pnode,0); //1 force, 0 normal + return ESPCONN_OK; + } else + return ESPCONN_ARG; +} + +/****************************************************************************** + * FunctionName : espconn_abort + * Description : Forcely abort with host + * Parameters : espconn -- the espconn used to disconnect the connection + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_abort(struct espconn *espconn) +{ + espconn_msg *pnode = NULL; + bool value = false; + + if (espconn == NULL) { + return ESPCONN_ARG;; + } else if (espconn ->type != ESPCONN_TCP) + return ESPCONN_ARG; + + /*Find the node depend on the espconn message*/ + value = espconn_find_connection(espconn, &pnode); + + if (value){ + /*protect for redisconnection*/ + if (espconn->state == ESPCONN_CLOSE) + return ESPCONN_INPROGRESS; + espconn_tcp_disconnect(pnode,1); //1 force, 0 normal + return ESPCONN_OK; + } else + return ESPCONN_ARG; +} + + +/****************************************************************************** + * FunctionName : espconn_get_packet_info + * Description : get the packet info with host + * Parameters : espconn -- the espconn used to disconnect the connection + * infoarg -- the packet info + * Returns : the errur code +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_get_packet_info(struct espconn *espconn, struct espconn_packet* infoarg) +{ + espconn_msg *pnode = NULL; + err_t err; + bool value = false; + + if (espconn == NULL || infoarg == NULL) { + return ESPCONN_ARG;; + } else if (espconn->type != ESPCONN_TCP) + return ESPCONN_ARG; + + /*Find the node depend on the espconn message*/ + value = espconn_find_connection(espconn, &pnode); + if (value) { + struct tcp_pcb *pcb = pnode->pcommon.pcb; + if (pcb == NULL) + return ESPCONN_ARG; + + pnode->pcommon.packet_info.packseq_nxt = pcb->rcv_nxt; + pnode->pcommon.packet_info.packseqno = pcb->snd_nxt; + pnode->pcommon.packet_info.snd_buf_size = pcb->snd_buf; + pnode->pcommon.packet_info.total_queuelen = TCP_SND_QUEUELEN; + pnode->pcommon.packet_info.snd_queuelen = pnode->pcommon.packet_info.total_queuelen - pcb->snd_queuelen; + os_memcpy(infoarg,(void*)&pnode->pcommon.packet_info, sizeof(struct espconn_packet)); + return ESPCONN_OK; + } else { + switch (espconn->state){ + case ESPCONN_CLOSE: + os_memcpy(infoarg,(void*)&pktinfo[0], sizeof(struct espconn_packet)); + err = ESPCONN_OK; + break; + case ESPCONN_NONE: + os_memcpy(infoarg,(void*)&pktinfo[1], sizeof(struct espconn_packet)); + err = ESPCONN_OK; + break; + default: + err = ESPCONN_ARG; + break; + } + return err; + } +} + +/****************************************************************************** + * FunctionName : espconn_set_opt + * Description : set the option for connections so that we don't end up bouncing + * all connections at the same time . + * Parameters : espconn -- the espconn used to set the connection + * opt -- the option for set + * Returns : the result +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_set_opt(struct espconn *espconn, uint8 opt) +{ + espconn_msg *pnode = NULL; + struct tcp_pcb *tpcb; + bool value = false; + + if (espconn == NULL) { + return ESPCONN_ARG;; + } else if (espconn->type != ESPCONN_TCP) + return ESPCONN_ARG; + + /*Find the node depend on the espconn message*/ + value = espconn_find_connection(espconn, &pnode); + if (value) { + pnode->pcommon.espconn_opt |= opt; + tpcb = pnode->pcommon.pcb; + if (espconn_delay_disabled(pnode)) + tcp_nagle_disable(tpcb); + + if (espconn_keepalive_disabled(pnode)) + espconn_keepalive_enable(tpcb); + + return ESPCONN_OK; + } else + return ESPCONN_ARG; +} + +/****************************************************************************** + * FunctionName : espconn_clear_opt + * Description : clear the option for connections so that we don't end up bouncing + * all connections at the same time . + * Parameters : espconn -- the espconn used to set the connection + * opt -- the option for clear + * Returns : the result +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_clear_opt(struct espconn *espconn, uint8 opt) +{ + espconn_msg *pnode = NULL; + struct tcp_pcb *tpcb; + bool value = false; + + if (espconn == NULL) { + return ESPCONN_ARG;; + } else if (espconn->type != ESPCONN_TCP) + return ESPCONN_ARG; + + /*Find the node depend on the espconn message*/ + value = espconn_find_connection(espconn, &pnode); + if (value) { + pnode->pcommon.espconn_opt &= ~opt; + tpcb = pnode->pcommon.pcb; + if (espconn_keepalive_enabled(pnode)) + espconn_keepalive_disable(tpcb); + + if (espconn_delay_enabled(pnode)) + tcp_nagle_enable(tpcb); + + return ESPCONN_OK; + } else + return ESPCONN_ARG; +} + +/****************************************************************************** + * FunctionName : espconn_set_keepalive + * Description : access level value for connection so that we set the value for + * keep alive + * Parameters : espconn -- the espconn used to set the connection + * level -- the connection's level + * value -- the value of time(s) + * Returns : access port value +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR espconn_set_keepalive(struct espconn *espconn, uint8 level, void* optarg) +{ + espconn_msg *pnode = NULL; + bool value = false; + sint8 ret = ESPCONN_OK; + + if (espconn == NULL || optarg == NULL) { + return ESPCONN_ARG;; + } else if (espconn->type != ESPCONN_TCP) + return ESPCONN_ARG; + + /*Find the node depend on the espconn message*/ + value = espconn_find_connection(espconn, &pnode); + if (value && espconn_keepalive_disabled(pnode)) { + struct tcp_pcb *pcb = pnode->pcommon.pcb; + switch (level){ + case ESPCONN_KEEPIDLE: + pcb->keep_idle = 1000 * (u32_t)(*(int*)optarg); + ret = ESPCONN_OK; + break; + case ESPCONN_KEEPINTVL: + pcb->keep_intvl = 1000 * (u32_t)(*(int*)optarg); + ret = ESPCONN_OK; + break; + case ESPCONN_KEEPCNT: + pcb->keep_cnt = (u32_t)(*(int*)optarg); + ret = ESPCONN_OK; + break; + default: + ret = ESPCONN_ARG; + break; + } + return ret; + } else + return ESPCONN_ARG; +} + +/****************************************************************************** + * FunctionName : espconn_get_keepalive + * Description : access level value for connection so that we get the value for + * keep alive + * Parameters : espconn -- the espconn used to get the connection + * level -- the connection's level + * Returns : access keep alive value +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR espconn_get_keepalive(struct espconn *espconn, uint8 level, void *optarg) +{ + espconn_msg *pnode = NULL; + bool value = false; + sint8 ret = ESPCONN_OK; + + if (espconn == NULL || optarg == NULL) { + return ESPCONN_ARG;; + } else if (espconn->type != ESPCONN_TCP) + return ESPCONN_ARG; + + /*Find the node depend on the espconn message*/ + value = espconn_find_connection(espconn, &pnode); + if (value && espconn_keepalive_disabled(pnode)) { + struct tcp_pcb *pcb = pnode->pcommon.pcb; + switch (level) { + case ESPCONN_KEEPIDLE: + *(int*)optarg = (int)(pcb->keep_idle/1000); + ret = ESPCONN_OK; + break; + case ESPCONN_KEEPINTVL: + *(int*)optarg = (int)(pcb->keep_intvl/1000); + ret = ESPCONN_OK; + break; + case ESPCONN_KEEPCNT: + *(int*)optarg = (int)(pcb->keep_cnt); + ret = ESPCONN_OK; + break; + default: + ret = ESPCONN_ARG; + break; + } + return ret; + } else + return ESPCONN_ARG; +} + +/****************************************************************************** + * FunctionName : espconn_delete + * Description : disconnect with host + * Parameters : espconn -- the espconn used to disconnect the connection + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_delete(struct espconn *espconn) +{ + espconn_msg *pnode = NULL; + bool value = false; + + if (espconn == NULL) { + return ESPCONN_ARG; + } else if (espconn ->type != ESPCONN_UDP) + return espconn_tcp_delete(espconn); + + /*Find the node depend on the espconn message*/ + value = espconn_find_connection(espconn, &pnode); + + if (value){ + espconn_udp_disconnect(pnode); + return ESPCONN_OK; + } else + return ESPCONN_ARG; +} + +/****************************************************************************** + * FunctionName : espconn_port + * Description : access port value for client so that we don't end up bouncing + * all connections at the same time . + * Parameters : none + * Returns : access port value +*******************************************************************************/ +uint32 ICACHE_FLASH_ATTR +espconn_port(void) +{ + uint32 port = 0; + static uint32 randnum = 0; + + do { + port = os_random(); + + if (port < 0) { + port = os_random() - port; + } + + port %= 0xc350; + + if (port < 0x400) { + port += 0x400; + } + + } while (port == randnum); + + randnum = port; + + return port; +} + +/****************************************************************************** + * FunctionName : espconn_gethostbyname + * Description : Resolve a hostname (string) into an IP address. + * Parameters : pespconn -- espconn to resolve a hostname + * hostname -- the hostname that is to be queried + * addr -- pointer to a ip_addr_t where to store the address if + * it is already cached in the dns_table (only valid if + * ESPCONN_OK is returned!) + * found -- a callback function to be called on success, failure + * or timeout (only if ERR_INPROGRESS is returned!) + * Returns : err_t return code + * - ESPCONN_OK if hostname is a valid IP address string or the host + * name is already in the local names table. + * - ESPCONN_INPROGRESS enqueue a request to be sent to the DNS server + * for resolution if no errors are present. + * - ESPCONN_ARG: dns client not initialized or invalid hostname +*******************************************************************************/ +err_t ICACHE_FLASH_ATTR +espconn_gethostbyname(struct espconn *pespconn, const char *hostname, ip_addr_t *addr, dns_found_callback found) +{ + return dns_gethostbyname(hostname, addr, found, pespconn); +} + +/****************************************************************************** + * FunctionName : espconn_dns_setserver + * Description : Initialize one of the DNS servers. + * Parameters : numdns -- the index of the DNS server to set must + * be < DNS_MAX_SERVERS = 2 + * dnsserver -- IP address of the DNS server to set + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR +espconn_dns_setserver(u8_t numdns, ip_addr_t *dnsserver) +{ + dns_setserver(numdns,dnsserver); +} + diff --git a/variants/lwip/lwip/app/espconn_mdns.c b/variants/lwip/lwip/app/espconn_mdns.c new file mode 100755 index 000000000..a29c64a54 --- /dev/null +++ b/variants/lwip/lwip/app/espconn_mdns.c @@ -0,0 +1,134 @@ +/****************************************************************************** + * Copyright 2013-2014 Espressif Systems (Wuxi) + * + * FileName: espconn_mdns.c + * + * Description: udp proto interface + * + * Modification history: + * 2014/3/31, v1.0 create this file. +*******************************************************************************/ + +#include "ets_sys.h" +#include "os_type.h" + +#include "lwip/mdns.h" + +/****************************************************************************** + * FunctionName : espconn_mdns_enable + * Description : join a multicast group + * Parameters : host_ip -- the ip address of udp server + * multicast_ip -- multicast ip given by user + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR +espconn_mdns_enable(void) +{ + mdns_enable(); +} +/****************************************************************************** + * FunctionName : espconn_mdns_disable + * Description : join a multicast group + * Parameters : host_ip -- the ip address of udp server + * multicast_ip -- multicast ip given by user + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR +espconn_mdns_disable(void) +{ + mdns_disable(); +} + +/****************************************************************************** + * FunctionName : espconn_mdns_set_hostname + * Description : join a multicast group + * Parameters : host_ip -- the ip address of udp server + * multicast_ip -- multicast ip given by user + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR +espconn_mdns_set_hostname(char *name) +{ + mdns_set_hostname(name); +} + +/****************************************************************************** + * FunctionName : espconn_mdns_init + * Description : join a multicast group + * Parameters : host_ip -- the ip address of udp server + * multicast_ip -- multicast ip given by user + * Returns : none +*******************************************************************************/ +char* ICACHE_FLASH_ATTR +espconn_mdns_get_hostname(void) +{ + return (char *)mdns_get_hostname(); +} +/****************************************************************************** + * FunctionName : espconn_mdns_get_servername + * Description : join a multicast group + * Parameters : info -- the info of mdns + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR +espconn_mdns_set_servername(const char *name) +{ + mdns_set_servername(name); +} +/****************************************************************************** + * FunctionName : espconn_mdns_get_servername + * Description : join a multicast group + * Parameters : info -- the info of mdns + * Returns : none +*******************************************************************************/ +char* ICACHE_FLASH_ATTR +espconn_mdns_get_servername(void) +{ + return (char *)mdns_get_servername(); +} +/****************************************************************************** + * FunctionName : mdns_server_register + * Description : join a multicast group + * Parameters : info -- the info of mdns + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR +espconn_mdns_server_register(void) +{ + mdns_server_register(); +} +/****************************************************************************** + * FunctionName : mdns_server_register + * Description : join a multicast group + * Parameters : info -- the info of mdns + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR +espconn_mdns_server_unregister(void) +{ + mdns_server_unregister(); +} +/****************************************************************************** + * FunctionName : espconn_mdns_init + * Description : join a multicast group + * Parameters : host_ip -- the ip address of udp server + * multicast_ip -- multicast ip given by user + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR +espconn_mdns_close(void) +{ + mdns_close(); +} +/****************************************************************************** + * FunctionName : espconn_mdns_init + * Description : join a multicast group + * Parameters : host_ip -- the ip address of udp server + * multicast_ip -- multicast ip given by user + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR +espconn_mdns_init(struct mdns_info *info) +{ + mdns_init(info); +} diff --git a/variants/lwip/lwip/app/espconn_tcp.c b/variants/lwip/lwip/app/espconn_tcp.c new file mode 100755 index 000000000..e21075070 --- /dev/null +++ b/variants/lwip/lwip/app/espconn_tcp.c @@ -0,0 +1,1390 @@ +/****************************************************************************** + * Copyright 2013-2014 Espressif Systems (Wuxi) + * + * FileName: espconn_tcp.c + * + * Description: tcp proto interface + * + * Modification history: + * 2014/3/31, v1.0 create this file. +*******************************************************************************/ + +#include "lwip/netif.h" +#include "lwip/inet.h" +#include "netif/etharp.h" +#include "lwip/tcp.h" +#include "lwip/ip.h" +#include "lwip/init.h" +#include "lwip/tcp_impl.h" +#include "lwip/memp.h" + +#include "ets_sys.h" +#include "os_type.h" +//#include "os.h" +#include "lwip/mem.h" +#include "lwip/app/espconn_tcp.h" + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +extern espconn_msg *plink_active; +extern espconn_msg *pserver_list; +extern struct espconn_packet pktinfo[2]; +extern struct tcp_pcb ** const tcp_pcb_lists[]; + +os_event_t espconn_TaskQueue[espconn_TaskQueueLen]; + +static err_t +espconn_client_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err); +static void +espconn_client_close(void *arg, struct tcp_pcb *pcb,u8 type); + +static err_t +espconn_server_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err); +static void +espconn_server_close(void *arg, struct tcp_pcb *pcb,u8 type); + +///////////////////////////////common function///////////////////////////////// +/****************************************************************************** + * FunctionName : espconn_kill_oldest + * Description : kill the oldest TCP block + * Parameters : none + * Returns : none +*******************************************************************************/ +static void ICACHE_FLASH_ATTR +espconn_kill_oldest(void) +{ + struct tcp_pcb *pcb, *inactive; + u32_t inactivity; + + inactivity = 0; + inactive = NULL; + /* Go through the list of TIME_WAIT pcbs and get the oldest pcb. */ + for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + if ((u32_t) (tcp_ticks - pcb->tmr) >= inactivity) { + inactivity = tcp_ticks - pcb->tmr; + inactive = pcb; + } + } + if (inactive != NULL) { + tcp_abort(inactive); + } + + /* Go through the list of FIN_WAIT_2 pcbs and get the oldest pcb. */ + inactivity = 0; + inactive = NULL; + for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + if (pcb->state == FIN_WAIT_1 || pcb->state == FIN_WAIT_2){ + if ((u32_t) (tcp_ticks - pcb->tmr) >= inactivity) { + inactivity = tcp_ticks - pcb->tmr; + inactive = pcb; + } + } + } + /*Purges the PCB, removes it from a PCB list and frees the memory*/ + if (inactive != NULL) { + tcp_pcb_remove(&tcp_active_pcbs, inactive); + memp_free(MEMP_TCP_PCB, inactive); + } + + /* Go through the list of LAST_ACK pcbs and get the oldest pcb. */ + inactivity = 0; + inactive = NULL; + for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + if (pcb->state == LAST_ACK) { + if ((u32_t) (tcp_ticks - pcb->tmr) >= inactivity) { + inactivity = tcp_ticks - pcb->tmr; + inactive = pcb; + } + } + } + /*Purges the PCB, removes it from a PCB list and frees the memory*/ + if (inactive != NULL) { + tcp_pcb_remove(&tcp_active_pcbs, inactive); + memp_free(MEMP_TCP_PCB, inactive); + } +} + +/****************************************************************************** + * FunctionName : espconn_kill_oldest_pcb + * Description : find the oldest TCP block by state + * Parameters : none + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR espconn_kill_oldest_pcb(void) +{ + struct tcp_pcb *cpcb = NULL; + uint8 i = 0; + uint8 num_tcp_fin = 0; + for(i = 2; i < 4; i ++){ + for (cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) { + if (cpcb->state == TIME_WAIT){ + num_tcp_fin ++; + if (num_tcp_fin == MEMP_NUM_TCP_PCB) + break; + } + + if (cpcb->state == FIN_WAIT_1 || cpcb->state == FIN_WAIT_2 || cpcb->state == LAST_ACK){ + num_tcp_fin++; + if (num_tcp_fin == MEMP_NUM_TCP_PCB) + break; + } + } + + if (num_tcp_fin == MEMP_NUM_TCP_PCB){ + num_tcp_fin = 0; + espconn_kill_oldest(); + } else if (cpcb == NULL){ + num_tcp_fin = 0; + } + } +} + +/****************************************************************************** + * FunctionName : espconn_kill_pcb + * Description : kill all the TCP block by port + * Parameters : none + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR espconn_kill_pcb(u16_t port) +{ + struct tcp_pcb *cpcb = NULL; + uint8 i = 0; + struct tcp_pcb *inactive = NULL; + u8_t pcb_remove; + /* Check if the address already is in use (on all lists) */ + for (i = 1; i < 4; i++) { + cpcb = *tcp_pcb_lists[i]; + while(cpcb != NULL){ + pcb_remove = 0; + if (cpcb->local_port == port) { + ++pcb_remove; + } + /* If the PCB should be removed, do it. */ + if (pcb_remove) { + /* Remove PCB from tcp_pcb_lists list. */ + inactive = cpcb; + cpcb = inactive->next; + tcp_pcb_remove(tcp_pcb_lists[i], inactive); + memp_free(MEMP_TCP_PCB, inactive); + } else { + cpcb = cpcb->next; + } + } + } +} + +/****************************************************************************** + * FunctionName : espconn_find_current_pcb + * Description : find the TCP block which option + * Parameters : pcurrent_msg -- the node in the list which active + * Returns : TCP block point +*******************************************************************************/ +struct tcp_pcb *ICACHE_FLASH_ATTR espconn_find_current_pcb(espconn_msg *pcurrent_msg) +{ + uint16 local_port = pcurrent_msg->pcommon.local_port; + uint32 local_ip = pcurrent_msg->pcommon.local_ip; + uint16 remote_port = pcurrent_msg->pcommon.remote_port; + uint32 remote_ip = *((uint32*)(pcurrent_msg->pcommon.remote_ip)); + struct tcp_pcb *find_pcb = NULL; + if (pcurrent_msg ->preverse == NULL){/*Find the server's TCP block*/ + if (local_ip == 0|| local_port == 0) return pcurrent_msg->pcommon.pcb; + + for (find_pcb = tcp_active_pcbs; find_pcb != NULL; find_pcb = find_pcb->next){ + if ((find_pcb->remote_port == remote_port) && (find_pcb->remote_ip.addr == remote_ip) && + (find_pcb->local_port == local_port) && (find_pcb->local_ip.addr == local_ip)) + return find_pcb; + } + + for (find_pcb = tcp_tw_pcbs; find_pcb != NULL; find_pcb = find_pcb->next){ + if ((find_pcb->remote_port == remote_port) && (find_pcb->remote_ip.addr == remote_ip) && + (find_pcb->local_port == local_port) && (find_pcb->local_ip.addr == local_ip)) + return find_pcb; + } + } else {/*Find the client's TCP block*/ + if (remote_ip == 0|| remote_port == 0) return pcurrent_msg->pcommon.pcb; + + for (find_pcb = tcp_active_pcbs; find_pcb != NULL; find_pcb = find_pcb->next){ + if ((find_pcb->remote_port == remote_port) && (find_pcb->remote_ip.addr == remote_ip)) + return find_pcb; + } + + for (find_pcb = tcp_tw_pcbs; find_pcb != NULL; find_pcb = find_pcb->next){ + if ((find_pcb->remote_port == remote_port) && (find_pcb->remote_ip.addr == remote_ip)) + return find_pcb; + } + } + return NULL; +} + +/****************************************************************************** + * FunctionName : espconn_tcp_reconnect + * Description : reconnect with host + * Parameters : arg -- Additional argument to pass to the callback function + * Returns : none +*******************************************************************************/ +static void ICACHE_FLASH_ATTR +espconn_tcp_reconnect(void *arg) +{ + espconn_msg *precon_cb = arg; + sint8 re_err = 0; + espconn_buf *perr_buf = NULL; + espconn_buf *perr_back = NULL; + espconn_kill_oldest_pcb(); + if (precon_cb != NULL) { + struct espconn *espconn = precon_cb->preverse; + re_err = precon_cb->pcommon.err; + if (precon_cb->pespconn != NULL){ + if (espconn != NULL){/*Process the server's message block*/ + if (precon_cb->pespconn->proto.tcp != NULL){ + espconn_copy_partial(espconn, precon_cb->pespconn); + espconn_printf("server: %d.%d.%d.%d : %d reconnection\n", espconn->proto.tcp->remote_ip[0], + espconn->proto.tcp->remote_ip[1],espconn->proto.tcp->remote_ip[2], + espconn->proto.tcp->remote_ip[3],espconn->proto.tcp->remote_port); + os_free(precon_cb->pespconn->proto.tcp); + precon_cb->pespconn->proto.tcp = NULL; + } + os_free(precon_cb->pespconn); + precon_cb->pespconn = NULL; + } else {/*Process the client's message block*/ + espconn = precon_cb->pespconn; + espconn_printf("client: %d.%d.%d.%d : %d reconnection\n", espconn->proto.tcp->local_ip[0], + espconn->proto.tcp->local_ip[1],espconn->proto.tcp->local_ip[2], + espconn->proto.tcp->local_ip[3],espconn->proto.tcp->local_port); + } + } + + /*to prevent memory leaks, ensure that each allocated is deleted*/ + perr_buf = precon_cb->pcommon.pbuf; + while (perr_buf != NULL){ + perr_back = perr_buf; + perr_buf = perr_back->pnext; + espconn_pbuf_delete(&precon_cb->pcommon.pbuf,perr_back); + os_free(perr_back); + perr_back = NULL; + } + os_bzero(&pktinfo[1], sizeof(struct espconn_packet)); + os_memcpy(&pktinfo[1], (void*)&precon_cb->pcommon.packet_info, sizeof(struct espconn_packet)); + os_free(precon_cb); + precon_cb = NULL; + if (espconn && espconn->proto.tcp && espconn->proto.tcp->reconnect_callback != NULL) { + espconn->proto.tcp->reconnect_callback(espconn, re_err); + } + } else { + espconn_printf("espconn_tcp_reconnect err\n"); + } +} + +/****************************************************************************** + * FunctionName : espconn_tcp_disconnect + * Description : disconnect with host + * Parameters : arg -- Additional argument to pass to the callback function + * Returns : none +*******************************************************************************/ +static void ICACHE_FLASH_ATTR +espconn_tcp_disconnect_successful(void *arg) +{ + espconn_msg *pdiscon_cb = arg; + espconn_buf *pdis_buf = NULL; + espconn_buf *pdis_back = NULL; + espconn_kill_oldest_pcb(); + if (pdiscon_cb != NULL) { + struct espconn *espconn = pdiscon_cb->preverse; + + if (pdiscon_cb->pespconn != NULL){ + struct tcp_pcb *pcb = NULL; + if (espconn != NULL){/*Process the server's message block*/ + if (pdiscon_cb->pespconn->proto.tcp != NULL && espconn->proto.tcp){ + espconn_copy_partial(espconn, pdiscon_cb->pespconn); + espconn_printf("server: %d.%d.%d.%d : %d disconnect\n", espconn->proto.tcp->remote_ip[0], + espconn->proto.tcp->remote_ip[1],espconn->proto.tcp->remote_ip[2], + espconn->proto.tcp->remote_ip[3],espconn->proto.tcp->remote_port); + os_free(pdiscon_cb->pespconn->proto.tcp); + pdiscon_cb->pespconn->proto.tcp = NULL; + } + os_free(pdiscon_cb->pespconn); + pdiscon_cb->pespconn = NULL; + } else {/*Process the client's message block*/ + espconn = pdiscon_cb->pespconn; + espconn_printf("client: %d.%d.%d.%d : %d disconnect\n", espconn->proto.tcp->local_ip[0], + espconn->proto.tcp->local_ip[1],espconn->proto.tcp->local_ip[2], + espconn->proto.tcp->local_ip[3],espconn->proto.tcp->local_port); + } + /*process the current TCP block*/ + pcb = espconn_find_current_pcb(pdiscon_cb); + if (pcb != NULL){ + if (espconn_reuse_disabled(pdiscon_cb)) { + struct tcp_pcb *cpcb = NULL; + struct tcp_pcb *prev = NULL; + u8_t pcb_remove; + espconn_printf("espconn_tcp_disconnect_successful %d, %d\n", pcb->state, pcb->local_port); + cpcb = tcp_tw_pcbs; + while (cpcb != NULL) { + pcb_remove = 0; + if (cpcb->local_port == pcb->local_port) { + ++pcb_remove; + } + /* If the PCB should be removed, do it. */ + if (pcb_remove) { + struct tcp_pcb *backup_pcb = NULL; + tcp_pcb_purge(cpcb); + /* Remove PCB from tcp_tw_pcbs list. */ + if (prev != NULL) { + LWIP_ASSERT("espconn_tcp_delete: middle cpcb != tcp_tw_pcbs",cpcb != tcp_tw_pcbs); + prev->next = cpcb->next; + } else { + /* This PCB was the first. */ + LWIP_ASSERT("espconn_tcp_delete: first cpcb == tcp_tw_pcbs",tcp_tw_pcbs == cpcb); + tcp_tw_pcbs = cpcb->next; + } + backup_pcb = cpcb; + cpcb = cpcb->next; + memp_free(MEMP_TCP_PCB, backup_pcb); + } else { + prev = cpcb; + cpcb = cpcb->next; + } + } + + } else { + tcp_arg(pcb, NULL); + tcp_err(pcb, NULL); + } + } + } + + /*to prevent memory leaks, ensure that each allocated is deleted*/ + pdis_buf = pdiscon_cb->pcommon.pbuf; + while (pdis_buf != NULL) { + pdis_back = pdis_buf; + pdis_buf = pdis_back->pnext; + espconn_pbuf_delete(&pdiscon_cb->pcommon.pbuf, pdis_back); + os_free(pdis_back); + pdis_back = NULL; + } + os_bzero(&pktinfo[0], sizeof(struct espconn_packet)); + os_memcpy(&pktinfo[0], (void*)&pdiscon_cb->pcommon.packet_info, sizeof(struct espconn_packet)); + os_free(pdiscon_cb); + pdiscon_cb = NULL; + if (espconn->proto.tcp && espconn->proto.tcp->disconnect_callback != NULL) { + espconn->proto.tcp->disconnect_callback(espconn); + } + } else { + espconn_printf("espconn_tcp_disconnect err\n"); + } +} + +/****************************************************************************** + * FunctionName : espconn_Task + * Description : espconn processing task + * Parameters : events -- contain the espconn processing data + * Returns : none +*******************************************************************************/ +static void ICACHE_FLASH_ATTR +espconn_Task(os_event_t *events) +{ + espconn_msg *task_msg = NULL; + struct espconn *pespconn = NULL; + + task_msg = (espconn_msg *) events->par; + switch (events->sig) { + case SIG_ESPCONN_WRITE: { + pespconn = task_msg->pespconn; + if (pespconn == NULL) { + return; + } + + if (pespconn->proto.tcp->write_finish_fn != NULL) { + pespconn->proto.tcp->write_finish_fn(pespconn); + } + } + break; + case SIG_ESPCONN_ERRER: + /*remove the node from the client's active connection list*/ + espconn_list_delete(&plink_active, task_msg); + espconn_tcp_reconnect(task_msg); + break; + case SIG_ESPCONN_CLOSE: + /*remove the node from the client's active connection list*/ + espconn_list_delete(&plink_active, task_msg); + espconn_tcp_disconnect_successful(task_msg); + break; + default: + break; + } +} + +/****************************************************************************** + * FunctionName : espconn_tcp_sent + * Description : sent data for client or server + * Parameters : void *arg -- client or server to send + * uint8* psent -- Data to send + * uint16 length -- Length of data to send + * Returns : return espconn error code. + * - ESPCONN_OK. Successful. No error occured. + * - ESPCONN_MEM. Out of memory. + * - ESPCONN_RTE. Could not find route to destination address. + * - More errors could be returned by lower protocol layers. +*******************************************************************************/ +err_t ICACHE_FLASH_ATTR +espconn_tcp_sent(void *arg, uint8 *psent, uint16 length) +{ + espconn_msg *ptcp_sent = arg; + struct tcp_pcb *pcb = NULL; + err_t err = 0; + u16_t len = 0; + + espconn_printf("espconn_tcp_sent ptcp_sent %p psent %p length %d\n", ptcp_sent, psent, length); + + /*Check the parameters*/ + if (ptcp_sent == NULL || psent == NULL || length == 0) { + return ESPCONN_ARG; + } + + /*Set the packet length depend on the sender buffer space*/ + pcb = ptcp_sent->pcommon.pcb; + if (tcp_sndbuf(pcb) < length) { + len = tcp_sndbuf(pcb); + } else { + len = length; + LWIP_ASSERT("length did not fit into uint16!", (len == length)); + } + + if (len > (2*pcb->mss)) { + len = 2*pcb->mss; + } + + /*Write data for sending, but does not send it immediately*/ + do { + espconn_printf("espconn_tcp_sent writing %d bytes %p\n", len, pcb); + if (espconn_copy_disabled(ptcp_sent)) + err = tcp_write(pcb, psent, len, 1); + else + err = tcp_write(pcb, psent, len, 0); + + if (err == ERR_MEM) { + len /= 2; + } + } while (err == ERR_MEM && len > 1); + + /*Find out what we can send and send it, offset the buffer point for next send*/ + if (err == ERR_OK) { + ptcp_sent->pcommon.ptail->punsent = psent + len; + ptcp_sent->pcommon.ptail->unsent = length - len; + err = tcp_output(pcb); + /*If enable the copy option, change the flag for next write*/ + if (espconn_copy_disabled(ptcp_sent)){ + if (ptcp_sent->pcommon.ptail->unsent == 0) { + ptcp_sent->pcommon.write_flag = true; + ets_post(espconn_TaskPrio, SIG_ESPCONN_WRITE, (uint32_t)ptcp_sent); + } + } + espconn_printf("espconn_tcp_sent %d\n", err); + } + return err; +} + +/****************************************************************************** + * FunctionName : espconn_close + * Description : The connection has been successfully closed. + * Parameters : arg -- Additional argument to pass to the callback function + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR espconn_tcp_disconnect(espconn_msg *pdiscon,u8 type) +{ + if (pdiscon != NULL){ + /*disconnect with the host by send the FIN frame*/ + if (pdiscon->preverse != NULL) + espconn_server_close(pdiscon, pdiscon->pcommon.pcb,type); + else + espconn_client_close(pdiscon, pdiscon->pcommon.pcb,type); + } else{ + espconn_printf("espconn_tcp_disconnect err.\n"); + } +} + +///////////////////////////////client function///////////////////////////////// +/****************************************************************************** + * FunctionName : espconn_client_close + * Description : The connection shall be actively closed. + * Parameters : pcb -- Additional argument to pass to the callback function + * pcb -- the pcb to close + * Returns : none +*******************************************************************************/ +static void ICACHE_FLASH_ATTR +espconn_client_close(void *arg, struct tcp_pcb *pcb, u8 type) +{ + err_t err; + espconn_msg *pclose = arg; + + pclose->pcommon.pcb = pcb; + /*avoid recalling the disconnect function*/ + tcp_recv(pcb, NULL); + + if(type == 0) + err = tcp_close(pcb); + else { + tcp_sent(pcb, NULL); + tcp_err(pcb, NULL); + tcp_abort(pcb); + err = ERR_OK; + } + + if (err != ERR_OK) { + /* closing failed, try again later */ + tcp_recv(pcb, espconn_client_recv); + } else { + /* closing succeeded */ + if (type == 0) { + tcp_sent(pcb, NULL); + tcp_err(pcb, NULL); + } + /*switch the state of espconn for application process*/ + pclose->pespconn->state = ESPCONN_CLOSE; + ets_post(espconn_TaskPrio, SIG_ESPCONN_CLOSE, (uint32_t)pclose); + } +} + +//***********Code for WIFI_BLOCK from upper************** +sint8 ICACHE_FLASH_ATTR +espconn_recv_hold(struct espconn *pespconn) +{ + //1st, according to espconn code, have to find out the escpconn_msg by pespconn; + espconn_msg *pnode = NULL; + bool value = false; + if (pespconn == NULL) { + return ESPCONN_ARG; + } + value = espconn_find_connection(pespconn, &pnode); + if(value != true) + { + os_printf("RecvHold, By pespconn,find conn_msg fail\n"); + return ESPCONN_ARG; + } + + //2nd, the actual operation + if(pnode->recv_hold_flag == 0) + { + pnode->recv_hold_flag = 1; + pnode->recv_holded_buf_Len = 0; + } + return ESPCONN_OK; +} + +sint8 ICACHE_FLASH_ATTR +espconn_recv_unhold(struct espconn *pespconn) +{ + //1st, according to espconn code, have to find out the escpconn_msg by pespconn; + espconn_msg *pnode = NULL; + bool value = false; + if (pespconn == NULL) { + return ESPCONN_ARG; + } + value = espconn_find_connection(pespconn, &pnode); + if(value != true) + { + os_printf("RecvHold, By pespconn,find conn_msg fail\n"); + return ESPCONN_ARG; + } + + //2nd, the actual operation + if(pnode->recv_hold_flag == 1) + { + if(pespconn->type == ESPCONN_TCP) { + tcp_recved(pnode->pcommon.pcb, pnode->recv_holded_buf_Len); + } + pnode->recv_holded_buf_Len = 0; + pnode->recv_hold_flag = 0; + } + return ESPCONN_OK; +} + +//***********Code for WIFI_BLOCK from upper************** + +/****************************************************************************** + * FunctionName : espconn_client_recv + * Description : Data has been received on this pcb. + * Parameters : arg -- Additional argument to pass to the callback function + * pcb -- The connection pcb which received data + * p -- The received data (or NULL when the connection has been closed!) + * err -- An error code if there has been an error receiving + * Returns : ERR_ABRT: if you have called tcp_abort from within the function! +*******************************************************************************/ +static err_t ICACHE_FLASH_ATTR +espconn_client_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) +{ + espconn_msg *precv_cb = arg; + + tcp_arg(pcb, arg); + + if (p != NULL) { + /*To update and advertise a larger window*/ + if(precv_cb->recv_hold_flag == 0) + tcp_recved(pcb, p->tot_len); + else + precv_cb->recv_holded_buf_Len += p->tot_len; + } + + if (err == ERR_OK && p != NULL) { + char *pdata = NULL; + u16_t length = 0; + /*Copy the contents of a packet buffer to an application buffer. + *to prevent memory leaks, ensure that each allocated is deleted*/ + pdata = (char *)os_zalloc(p ->tot_len + 1); + length = pbuf_copy_partial(p, pdata, p ->tot_len, 0); + pbuf_free(p); + + if (length != 0) { + /*switch the state of espconn for application process*/ + precv_cb->pespconn ->state = ESPCONN_READ; + precv_cb->pcommon.pcb = pcb; + if (precv_cb->pespconn->recv_callback != NULL) { + precv_cb->pespconn->recv_callback(precv_cb->pespconn, pdata, length); + } + /*switch the state of espconn for next packet copy*/ + if (pcb->state == ESTABLISHED) + precv_cb->pespconn ->state = ESPCONN_CONNECT; + } + + /*to prevent memory leaks, ensure that each allocated is deleted*/ + os_free(pdata); + pdata = NULL; + } + + if (err == ERR_OK && p == NULL) { + espconn_client_close(precv_cb, pcb,0); + } + + return ERR_OK; +} + +/****************************************************************************** + * FunctionName : espconn_tcp_write + * Description : write the packet which in the active connection's list. + * Parameters : arg -- the node pointer which reverse the packet + * Returns : ESPCONN_MEM: memory error + * ESPCONN_OK:have enough space for write packet +*******************************************************************************/ +err_t ICACHE_FLASH_ATTR espconn_tcp_write(void *arg) +{ + espconn_msg *pwrite = arg; + err_t err = ERR_OK; + struct tcp_pcb *pcb = pwrite->pcommon.pcb; + /*for one active connection,limit the sender buffer space*/ + if (tcp_nagle_disabled(pcb) && (pcb->snd_queuelen >= TCP_SND_QUEUELEN)) + return ESPCONN_MEM; + + while (tcp_sndbuf(pcb) != 0){ + if (pwrite->pcommon.ptail != NULL) { + /*Find the node whether in the list's tail or not*/ + if (pwrite->pcommon.ptail->unsent == 0) { + pwrite->pcommon.ptail = pwrite->pcommon.ptail->pnext; + continue; + } + + /*Send the packet for the active connection*/ + err = espconn_tcp_sent(pwrite, pwrite->pcommon.ptail->punsent,pwrite->pcommon.ptail->unsent); + if (err != ERR_OK) + break; + } else + break; + } + return err; +} + +/****************************************************************************** + * FunctionName : espconn_tcp_reconnect + * Description : reconnect with host + * Parameters : arg -- Additional argument to pass to the callback function + * Returns : none +*******************************************************************************/ +static void ICACHE_FLASH_ATTR espconn_tcp_finish(void *arg) +{ + espconn_msg *pfinish = arg; + espconn_buf *premove = NULL; + uint16 len = 0; + espconn_tcp_write(pfinish); + while (pfinish->pcommon.pbuf != NULL){ + premove = pfinish->pcommon.pbuf; + pfinish->pcommon.pbuf->tot_len += len; + /*application packet has been sent and acknowledged by the remote host, + * to prevent memory leaks, ensure that each allocated is deleted*/ + if (premove->tot_len >= premove->len){ + espconn_pbuf_delete(&pfinish->pcommon.pbuf,premove); + len = premove->tot_len - premove->len; + pfinish->pcommon.packet_info.sent_length = premove->len; + os_free(premove); + premove = NULL; + pfinish->pespconn->state = ESPCONN_CONNECT; + if (pfinish->pespconn->sent_callback != NULL) { + pfinish->pespconn->sent_callback(pfinish->pespconn); + } + pfinish->pcommon.packet_info.sent_length = len; + } else + break; + } +} + +/****************************************************************************** + * FunctionName : espconn_client_sent + * Description : Data has been sent and acknowledged by the remote host. + * This means that more data can be sent. + * Parameters : arg -- Additional argument to pass to the callback function + * pcb -- The connection pcb for which data has been acknowledged + * len -- The amount of bytes acknowledged + * Returns : ERR_OK: try to send some data by calling tcp_output + * ERR_ABRT: if you have called tcp_abort from within the function! +*******************************************************************************/ +static err_t ICACHE_FLASH_ATTR +espconn_client_sent(void *arg, struct tcp_pcb *pcb, u16_t len) +{ + espconn_msg *psent_cb = arg; + + psent_cb->pcommon.pcb = pcb; + psent_cb->pcommon.pbuf->tot_len += len; + psent_cb->pcommon.packet_info.sent_length = len; + + /*Send more data for one active connection*/ + espconn_tcp_finish(psent_cb); + + return ERR_OK; +} + +/****************************************************************************** + * FunctionName : espconn_client_err + * Description : The pcb had an error and is already deallocated. + * The argument might still be valid (if != NULL). + * Parameters : arg -- Additional argument to pass to the callback function + * err -- Error code to indicate why the pcb has been closed + * Returns : none +*******************************************************************************/ +static void ICACHE_FLASH_ATTR +espconn_client_err(void *arg, err_t err) +{ + espconn_msg *perr_cb = arg; + struct tcp_pcb *pcb = NULL; + LWIP_UNUSED_ARG(err); + + if (perr_cb != NULL) { + pcb = perr_cb->pcommon.pcb; + perr_cb->pespconn->state = ESPCONN_CLOSE; + espconn_printf("espconn_client_err %d %d %d\n", pcb->state, pcb->nrtx, err); + +// /*remove the node from the client's active connection list*/ +// espconn_list_delete(&plink_active, perr_cb); + + /*Set the error code depend on the error type and control block state*/ + if (err == ERR_ABRT) { + switch (pcb->state) { + case SYN_SENT: + if (pcb->nrtx == TCP_SYNMAXRTX) { + perr_cb->pcommon.err = ESPCONN_CONN; + } else { + perr_cb->pcommon.err = err; + } + + break; + + case ESTABLISHED: + if (pcb->nrtx == TCP_MAXRTX) { + perr_cb->pcommon.err = ESPCONN_TIMEOUT; + } else { + perr_cb->pcommon.err = err; + } + break; + + case FIN_WAIT_1: + if (pcb->nrtx == TCP_MAXRTX) { + perr_cb->pcommon.err = ESPCONN_CLSD; + } else { + perr_cb->pcommon.err = err; + } + break; + case FIN_WAIT_2: + perr_cb->pcommon.err = ESPCONN_CLSD; + break; + case CLOSED: + perr_cb->pcommon.err = ESPCONN_CONN; + break; + default: + break; + } + } else { + perr_cb->pcommon.err = err; + } + /*post the singer to the task for processing the connection*/ + ets_post(espconn_TaskPrio, SIG_ESPCONN_ERRER, (uint32_t)perr_cb); + } +} + +/****************************************************************************** + * FunctionName : espconn_client_connect + * Description : A new incoming connection has been connected. + * Parameters : arg -- Additional argument to pass to the callback function + * tpcb -- The connection pcb which is connected + * err -- An unused error code, always ERR_OK currently + * Returns : connection result +*******************************************************************************/ +static err_t ICACHE_FLASH_ATTR +espconn_client_connect(void *arg, struct tcp_pcb *tpcb, err_t err) +{ + espconn_msg *pcon = arg; + + espconn_printf("espconn_client_connect pcon %p tpcb %p\n", pcon, tpcb); + if (err == ERR_OK){ + /*Reserve the remote information for current active connection*/ + pcon->pespconn->state = ESPCONN_CONNECT; + pcon->pcommon.err = err; + pcon->pcommon.pcb = tpcb; + pcon->pcommon.local_port = tpcb->local_port; + pcon->pcommon.local_ip = tpcb->local_ip.addr; + pcon->pcommon.remote_port = tpcb->remote_port; + pcon->pcommon.remote_ip[0] = ip4_addr1_16(&tpcb->remote_ip); + pcon->pcommon.remote_ip[1] = ip4_addr2_16(&tpcb->remote_ip); + pcon->pcommon.remote_ip[2] = ip4_addr3_16(&tpcb->remote_ip); + pcon->pcommon.remote_ip[3] = ip4_addr4_16(&tpcb->remote_ip); + pcon->pcommon.write_flag = true; + tcp_arg(tpcb, (void *) pcon); + + /*Set the specify function that should be called + * when TCP data has been successfully delivered, + * when active connection receives data*/ + tcp_sent(tpcb, espconn_client_sent); + tcp_recv(tpcb, espconn_client_recv); + /*Disable Nagle algorithm default*/ + tcp_nagle_disable(tpcb); + /*Default set the total number of espconn_buf on the unsent lists for one*/ + espconn_tcp_set_buf_count(pcon->pespconn, 1); + + if (pcon->pespconn->proto.tcp->connect_callback != NULL) { + pcon->pespconn->proto.tcp->connect_callback(pcon->pespconn); + } + + /*Enable keep alive option*/ + if (espconn_keepalive_disabled(pcon)) + espconn_keepalive_enable(tpcb); + + } else{ + os_printf("err in host connected (%s)\n",lwip_strerr(err)); + } + return err; +} + +/****************************************************************************** + * FunctionName : espconn_tcp_client + * Description : Initialize the client: set up a connect PCB and bind it to + * the defined port + * Parameters : espconn -- the espconn used to build client + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_tcp_client(struct espconn *espconn) +{ + struct tcp_pcb *pcb = NULL; + struct ip_addr ipaddr; + espconn_msg *pclient = NULL; + + /*Creates a new client control message*/ + pclient = (espconn_msg *)os_zalloc(sizeof(espconn_msg)); + if (pclient == NULL){ + return ESPCONN_MEM; + } + + /*Set an IP address given for Little-endian.*/ + IP4_ADDR(&ipaddr, espconn->proto.tcp->remote_ip[0], + espconn->proto.tcp->remote_ip[1], + espconn->proto.tcp->remote_ip[2], + espconn->proto.tcp->remote_ip[3]); + + /*Creates a new TCP protocol control block*/ + pcb = tcp_new(); + + if (pcb == NULL) { + /*to prevent memory leaks, ensure that each allocated is deleted*/ + os_free(pclient); + pclient = NULL; + return ESPCONN_MEM; + } else { + + /*insert the node to the active connection list*/ + espconn_list_creat(&plink_active, pclient); + tcp_arg(pcb, (void *)pclient); + tcp_err(pcb, espconn_client_err); + pclient->preverse = NULL; + pclient->pespconn = espconn; + pclient->pespconn->state = ESPCONN_WAIT; + pclient->pcommon.pcb = pcb; + tcp_bind(pcb, IP_ADDR_ANY, pclient->pespconn->proto.tcp->local_port); +#if 0 + pclient->pcommon.err = tcp_bind(pcb, IP_ADDR_ANY, pclient->pespconn->proto.tcp->local_port); + if (pclient->pcommon.err != ERR_OK){ + /*remove the node from the client's active connection list*/ + espconn_list_delete(&plink_active, pclient); + memp_free(MEMP_TCP_PCB, pcb); + os_free(pclient); + pclient = NULL; + return ERR_USE; + } +#endif + /*Establish the connection*/ + pclient->pcommon.err = tcp_connect(pcb, &ipaddr, + pclient->pespconn->proto.tcp->remote_port, espconn_client_connect); + if (pclient->pcommon.err == ERR_RTE){ + /*remove the node from the client's active connection list*/ + espconn_list_delete(&plink_active, pclient); + espconn_kill_pcb(pcb->local_port); + os_free(pclient); + pclient = NULL; + return ESPCONN_RTE; + } + return pclient->pcommon.err; + } +} + +///////////////////////////////server function///////////////////////////////// +/****************************************************************************** + * FunctionName : espconn_server_close + * Description : The connection shall be actively closed. + * Parameters : arg -- Additional argument to pass to the callback function + * pcb -- the pcb to close + * Returns : none +*******************************************************************************/ +static void ICACHE_FLASH_ATTR +espconn_server_close(void *arg, struct tcp_pcb *pcb,u8 type) +{ + err_t err; + espconn_msg *psclose = arg; + + psclose->pcommon.pcb = pcb; + /*avoid recalling the disconnect function*/ + tcp_recv(pcb, NULL); + + if(type ==0) + err = tcp_close(pcb); + else { + tcp_poll(pcb, NULL, 0); + tcp_sent(pcb, NULL); + tcp_err(pcb, NULL); + tcp_abort(pcb); + err = ERR_OK; + } + + if (err != ERR_OK) { + /* closing failed, try again later */ + tcp_recv(pcb, espconn_server_recv); + } else { + /* closing succeeded */ + if (type == 0) { + tcp_poll(pcb, NULL, 0); + tcp_sent(pcb, NULL); + tcp_err(pcb, NULL); + } + /*switch the state of espconn for application process*/ + psclose->pespconn->state = ESPCONN_CLOSE; + ets_post(espconn_TaskPrio, SIG_ESPCONN_CLOSE, (uint32_t)psclose); + } +} + +/****************************************************************************** + * FunctionName : espconn_server_recv + * Description : Data has been received on this pcb. + * Parameters : arg -- Additional argument to pass to the callback function + * pcb -- The connection pcb which received data + * p -- The received data (or NULL when the connection has been closed!) + * err -- An error code if there has been an error receiving + * Returns : ERR_ABRT: if you have called tcp_abort from within the function! +*******************************************************************************/ +static err_t ICACHE_FLASH_ATTR +espconn_server_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) +{ + espconn_msg *precv_cb = arg; + + tcp_arg(pcb, arg); + espconn_printf("server has application data received: %d\n", system_get_free_heap_size()); + if (p != NULL) { + /*To update and advertise a larger window*/ + if(precv_cb->recv_hold_flag == 0) + tcp_recved(pcb, p->tot_len); + else + precv_cb->recv_holded_buf_Len += p->tot_len; + } + + if (err == ERR_OK && p != NULL) { + u8_t *data_ptr = NULL; + u32_t data_cntr = 0; + /*clear the count for connection timeout*/ + precv_cb->pcommon.recv_check = 0; + /*Copy the contents of a packet buffer to an application buffer. + *to prevent memory leaks, ensure that each allocated is deleted*/ + data_ptr = (u8_t *)os_zalloc(p ->tot_len + 1); + data_cntr = pbuf_copy_partial(p, data_ptr, p ->tot_len, 0); + pbuf_free(p); + + if (data_cntr != 0) { + /*switch the state of espconn for application process*/ + precv_cb->pespconn ->state = ESPCONN_READ; + precv_cb->pcommon.pcb = pcb; + if (precv_cb->pespconn->recv_callback != NULL) { + precv_cb->pespconn->recv_callback(precv_cb->pespconn, (char*)data_ptr, data_cntr); + } + + /*switch the state of espconn for next packet copy*/ + if (pcb->state == ESTABLISHED) + precv_cb->pespconn ->state = ESPCONN_CONNECT; + } + + /*to prevent memory leaks, ensure that each allocated is deleted*/ + os_free(data_ptr); + data_ptr = NULL; + espconn_printf("server's application data has been processed: %d\n", system_get_free_heap_size()); + } else { + if (p != NULL) { + pbuf_free(p); + } + + espconn_server_close(precv_cb, pcb,0); + } + + return ERR_OK; +} + +/****************************************************************************** + * FunctionName : espconn_server_sent + * Description : Data has been sent and acknowledged by the remote host. + * This means that more data can be sent. + * Parameters : arg -- Additional argument to pass to the callback function + * pcb -- The connection pcb for which data has been acknowledged + * len -- The amount of bytes acknowledged + * Returns : ERR_OK: try to send some data by calling tcp_output + * ERR_ABRT: if you have called tcp_abort from within the function! +*******************************************************************************/ +static err_t ICACHE_FLASH_ATTR +espconn_server_sent(void *arg, struct tcp_pcb *pcb, u16_t len) +{ + espconn_msg *psent_cb = arg; + + psent_cb->pcommon.pcb = pcb; + psent_cb->pcommon.recv_check = 0; + psent_cb->pcommon.pbuf->tot_len += len; + psent_cb->pcommon.packet_info.sent_length = len; + + /*Send more data for one active connection*/ + espconn_tcp_finish(psent_cb); + return ERR_OK; +} + +/****************************************************************************** + * FunctionName : espconn_server_poll + * Description : The poll function is called every 3nd second. + * If there has been no data sent (which resets the retries) in 3 seconds, close. + * If the last portion of a file has not been sent in 3 seconds, close. + * + * This could be increased, but we don't want to waste resources for bad connections. + * Parameters : arg -- Additional argument to pass to the callback function + * pcb -- The connection pcb for which data has been acknowledged + * Returns : ERR_OK: try to send some data by calling tcp_output + * ERR_ABRT: if you have called tcp_abort from within the function! +*******************************************************************************/ +static err_t ICACHE_FLASH_ATTR +espconn_server_poll(void *arg, struct tcp_pcb *pcb) +{ + espconn_msg *pspoll_cb = arg; + + /*exception calling abandon the connection for send a RST frame*/ + if (arg == NULL) { + tcp_abandon(pcb, 0); + tcp_poll(pcb, NULL, 0); + return ERR_OK; + } + + espconn_printf("espconn_server_poll %d %d\n", pspoll_cb->pcommon.recv_check, pcb->state); + pspoll_cb->pcommon.pcb = pcb; + if (pcb->state == ESTABLISHED) { + pspoll_cb->pcommon.recv_check++; + if (pspoll_cb->pcommon.timeout != 0){/*no data sent in one active connection's set timeout, close.*/ + if (pspoll_cb->pcommon.recv_check >= pspoll_cb->pcommon.timeout) { + pspoll_cb->pcommon.recv_check = 0; + espconn_server_close(pspoll_cb, pcb,0); + } + } else { + espconn_msg *ptime_msg = pserver_list; + while (ptime_msg != NULL) { + if (ptime_msg->pespconn == pspoll_cb->preverse){ + if (ptime_msg->pcommon.timeout != 0){/*no data sent in server's set timeout, close.*/ + if (pspoll_cb->pcommon.recv_check >= ptime_msg->pcommon.timeout){ + pspoll_cb->pcommon.recv_check = 0; + espconn_server_close(pspoll_cb, pcb,0); + } + } else {/*don't close for ever*/ + pspoll_cb->pcommon.recv_check = 0; + } + break; + } + ptime_msg = ptime_msg->pnext; + } + } + } else { + espconn_server_close(pspoll_cb, pcb,0); + } + + return ERR_OK; +} + +/****************************************************************************** + * FunctionName : esponn_server_err + * Description : The pcb had an error and is already deallocated. + * The argument might still be valid (if != NULL). + * Parameters : arg -- Additional argument to pass to the callback function + * err -- Error code to indicate why the pcb has been closed + * Returns : none +*******************************************************************************/ +static void ICACHE_FLASH_ATTR +esponn_server_err(void *arg, err_t err) +{ + espconn_msg *pserr_cb = arg; + struct tcp_pcb *pcb = NULL; + if (pserr_cb != NULL) { + + pcb = pserr_cb->pcommon.pcb; + pserr_cb->pespconn->state = ESPCONN_CLOSE; + +// /*remove the node from the server's active connection list*/ +// espconn_list_delete(&plink_active, pserr_cb); + + /*Set the error code depend on the error type and control block state*/ + if (err == ERR_ABRT) { + switch (pcb->state) { + case SYN_RCVD: + if (pcb->nrtx == TCP_SYNMAXRTX) { + pserr_cb->pcommon.err = ESPCONN_CONN; + } else { + pserr_cb->pcommon.err = err; + } + + break; + + case ESTABLISHED: + if (pcb->nrtx == TCP_MAXRTX) { + pserr_cb->pcommon.err = ESPCONN_TIMEOUT; + } else { + pserr_cb->pcommon.err = err; + } + + break; + + case CLOSE_WAIT: + if (pcb->nrtx == TCP_MAXRTX) { + pserr_cb->pcommon.err = ESPCONN_CLSD; + } else { + pserr_cb->pcommon.err = err; + } + break; + case LAST_ACK: + pserr_cb->pcommon.err = ESPCONN_CLSD; + break; + + case CLOSED: + pserr_cb->pcommon.err = ESPCONN_CONN; + break; + default : + break; + } + } else { + pserr_cb->pcommon.err = err; + } + /*post the singer to the task for processing the connection*/ + ets_post(espconn_TaskPrio, SIG_ESPCONN_ERRER, (uint32_t)pserr_cb); + } +} + +/****************************************************************************** + * FunctionName : espconn_tcp_accept + * Description : A new incoming connection has been accepted. + * Parameters : arg -- Additional argument to pass to the callback function + * pcb -- The connection pcb which is accepted + * err -- An unused error code, always ERR_OK currently + * Returns : acception result +*******************************************************************************/ +static err_t ICACHE_FLASH_ATTR +espconn_tcp_accept(void *arg, struct tcp_pcb *pcb, err_t err) +{ + struct espconn *espconn = arg; + espconn_msg *paccept = NULL; + remot_info *pinfo = NULL; + LWIP_UNUSED_ARG(err); + + if (!espconn || !espconn->proto.tcp) { + return ERR_ARG; + } + + tcp_arg(pcb, paccept); + tcp_err(pcb, esponn_server_err); + /*Ensure the active connection is less than the count of active connections on the server*/ + espconn_get_connection_info(espconn, &pinfo , 0); + espconn_printf("espconn_tcp_accept link_cnt: %d\n", espconn->link_cnt); + if (espconn->link_cnt == espconn_tcp_get_max_con_allow(espconn)) + return ERR_ISCONN; + + /*Creates a new active connect control message*/ + paccept = (espconn_msg *)os_zalloc(sizeof(espconn_msg)); + tcp_arg(pcb, paccept); + + if (paccept == NULL) + return ERR_MEM; + /*Insert the node to the active connection list*/ + espconn_list_creat(&plink_active, paccept); + + paccept->preverse = espconn; + paccept->pespconn = (struct espconn *)os_zalloc(sizeof(struct espconn)); + if (paccept->pespconn == NULL) + return ERR_MEM; + paccept->pespconn->proto.tcp = (esp_tcp *)os_zalloc(sizeof(esp_tcp)); + if (paccept->pespconn->proto.tcp == NULL) + return ERR_MEM; + + /*Reserve the remote information for current active connection*/ + paccept->pcommon.pcb = pcb; + + paccept->pcommon.remote_port = pcb->remote_port; + paccept->pcommon.remote_ip[0] = ip4_addr1_16(&pcb->remote_ip); + paccept->pcommon.remote_ip[1] = ip4_addr2_16(&pcb->remote_ip); + paccept->pcommon.remote_ip[2] = ip4_addr3_16(&pcb->remote_ip); + paccept->pcommon.remote_ip[3] = ip4_addr4_16(&pcb->remote_ip); + paccept->pcommon.write_flag = true; + + os_memcpy(espconn->proto.tcp->remote_ip, paccept->pcommon.remote_ip, 4); + espconn->proto.tcp->remote_port = pcb->remote_port; + espconn->state = ESPCONN_CONNECT; + espconn_copy_partial(paccept->pespconn, espconn); + + /*Set the specify function that should be called + * when TCP data has been successfully delivered, + * when active connection receives data, + * or periodically from active connection*/ + tcp_sent(pcb, espconn_server_sent); + tcp_recv(pcb, espconn_server_recv); + tcp_poll(pcb, espconn_server_poll, 4); /* every 1 seconds */ + /*Disable Nagle algorithm default*/ + tcp_nagle_disable(pcb); + /*Default set the total number of espconn_buf on the unsent lists for one*/ + espconn_tcp_set_buf_count(paccept->pespconn, 1); + + if (paccept->pespconn->proto.tcp->connect_callback != NULL) { + paccept->pespconn->proto.tcp->connect_callback(paccept->pespconn); + } + + /*Enable keep alive option*/ + if (espconn_keepalive_disabled(paccept)) + espconn_keepalive_enable(pcb); + + return ERR_OK; +} + +/****************************************************************************** + * FunctionName : espconn_tcp_server + * Description : Initialize the server: set up a listening PCB and bind it to + * the defined port + * Parameters : espconn -- the espconn used to build server + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_tcp_server(struct espconn *espconn) +{ + struct tcp_pcb *pcb = NULL; + espconn_msg *pserver = NULL; + + /*Creates a new server control message*/ + pserver = (espconn_msg *)os_zalloc(sizeof(espconn_msg)); + if (pserver == NULL){ + return ESPCONN_MEM; + } + + /*Creates a new TCP protocol control block*/ + pcb = tcp_new(); + if (pcb == NULL) { + /*to prevent memory leaks, ensure that each allocated is deleted*/ + os_free(pserver); + pserver = NULL; + return ESPCONN_MEM; + } else { + struct tcp_pcb *lpcb = NULL; + /*Binds the connection to a local port number and any IP address*/ + tcp_bind(pcb, IP_ADDR_ANY, espconn->proto.tcp->local_port); + lpcb = pcb; + /*malloc and set the state of the connection to be LISTEN*/ + pcb = tcp_listen(pcb); + if (pcb != NULL) { + /*insert the node to the active connection list*/ + espconn_list_creat(&pserver_list, pserver); + pserver->preverse = pcb; + pserver->pespconn = espconn; + pserver->count_opt = MEMP_NUM_TCP_PCB; + pserver->pcommon.timeout = 0x0a; + espconn ->state = ESPCONN_LISTEN; + /*set the specify argument that should be passed callback function*/ + tcp_arg(pcb, (void *)espconn); + /*accept callback function to call for this control block*/ + tcp_accept(pcb, espconn_tcp_accept); + return ESPCONN_OK; + } else { + /*to prevent memory leaks, ensure that each allocated is deleted*/ + memp_free(MEMP_TCP_PCB,lpcb); + os_free(pserver); + pserver = NULL; + return ESPCONN_MEM; + } + } +} + +/****************************************************************************** + * FunctionName : espconn_tcp_delete + * Description : delete the server: delete a listening PCB and free it + * Parameters : pdeletecon -- the espconn used to delete a server + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR espconn_tcp_delete(struct espconn *pdeletecon) +{ + err_t err = ESPCONN_ARG; + remot_info *pinfo = NULL; + espconn_msg *pdelete_msg = NULL; + struct tcp_pcb *pcb = NULL; + + if (pdeletecon == NULL) + return err; + + espconn_get_connection_info(pdeletecon, &pinfo , 0); + /*make sure all the active connection have been disconnect*/ + if (pdeletecon->link_cnt != 0) + return ESPCONN_INPROGRESS; + else { + espconn_printf("espconn_tcp_delete %p\n",pdeletecon); + pdelete_msg = pserver_list; + while (pdelete_msg != NULL){ + if (pdelete_msg->pespconn == pdeletecon){ + /*remove the node from the client's active connection list*/ + espconn_list_delete(&pserver_list, pdelete_msg); + pcb = pdelete_msg->preverse; + os_printf("espconn_tcp_delete %d, %d\n",pcb->state, pcb->local_port); + espconn_kill_pcb(pcb->local_port); + err = tcp_close(pcb); + os_free(pdelete_msg); + pdelete_msg = NULL; + break; + } + pdelete_msg = pdelete_msg->pnext; + } + return err; + } +} + +/****************************************************************************** + * FunctionName : espconn_init + * Description : used to init the function that should be used when + * Parameters : none + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR espconn_init(void) +{ + ets_task(espconn_Task, espconn_TaskPrio, espconn_TaskQueue, espconn_TaskQueueLen); +} diff --git a/variants/lwip/lwip/app/espconn_udp.c b/variants/lwip/lwip/app/espconn_udp.c new file mode 100755 index 000000000..bff756ae9 --- /dev/null +++ b/variants/lwip/lwip/app/espconn_udp.c @@ -0,0 +1,423 @@ +/****************************************************************************** + * Copyright 2013-2014 Espressif Systems (Wuxi) + * + * FileName: espconn_udp.c + * + * Description: udp proto interface + * + * Modification history: + * 2014/3/31, v1.0 create this file. +*******************************************************************************/ + +#include "ets_sys.h" +#include "os_type.h" +//#include "os.h" + +#include "lwip/inet.h" +#include "lwip/err.h" +#include "lwip/pbuf.h" +#include "lwip/mem.h" +#include "lwip/tcp_impl.h" +#include "lwip/udp.h" + +#include "lwip/app/espconn_udp.h" + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +extern espconn_msg *plink_active; +extern uint8 default_interface; + +enum send_opt{ + ESPCONN_SENDTO, + ESPCONN_SEND +}; +static void ICACHE_FLASH_ATTR espconn_data_sentcb(struct espconn *pespconn) +{ + if (pespconn == NULL) { + return; + } + + if (pespconn->sent_callback != NULL) { + pespconn->sent_callback(pespconn); + } +} + +static void ICACHE_FLASH_ATTR espconn_data_sent(void *arg, enum send_opt opt) +{ + espconn_msg *psent = arg; + + if (psent == NULL) { + return; + } + + if (psent->pcommon.cntr == 0) { + psent->pespconn->state = ESPCONN_CONNECT; + if (psent->pcommon.err == 0) + espconn_data_sentcb(psent->pespconn); + } else { + if (opt == ESPCONN_SEND){ + espconn_udp_sent(arg, psent->pcommon.ptrbuf, psent->pcommon.cntr); + } else { + espconn_udp_sendto(arg, psent->pcommon.ptrbuf, psent->pcommon.cntr); + } + } +} + +/****************************************************************************** + * FunctionName : espconn_udp_sent + * Description : sent data for client or server + * Parameters : void *arg -- client or server to send + * uint8* psent -- Data to send + * uint16 length -- Length of data to send + * Returns : return espconn error code. + * - ESPCONN_OK. Successful. No error occured. + * - ESPCONN_MEM. Out of memory. + * - ESPCONN_RTE. Could not find route to destination address. + * - More errors could be returned by lower protocol layers. +*******************************************************************************/ +err_t ICACHE_FLASH_ATTR +espconn_udp_sent(void *arg, uint8 *psent, uint16 length) +{ + espconn_msg *pudp_sent = arg; + struct udp_pcb *upcb = pudp_sent->pcommon.pcb; + struct pbuf *p, *q ,*p_temp; + u8_t *data = NULL; + u16_t cnt = 0; + u16_t datalen = 0; + u16_t i = 0; + err_t err; + LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %d %p\n", __LINE__, length, upcb)); + + if (pudp_sent == NULL || upcb == NULL || psent == NULL || length == 0) { + return ESPCONN_ARG; + } + + if (1470 < length) { + datalen = 1470; + } else { + datalen = length; + } + + p = pbuf_alloc(PBUF_TRANSPORT, datalen, PBUF_RAM); + LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %p\n", __LINE__, p)); + + if (p != NULL) { + q = p; + + while (q != NULL) { + data = (u8_t *)q->payload; + LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %p\n", __LINE__, data)); + + for (i = 0; i < q->len; i++) { + data[i] = ((u8_t *) psent)[cnt++]; + } + + q = q->next; + } + } else { + return ESPCONN_MEM; + } + + upcb->remote_port = pudp_sent->pespconn->proto.udp->remote_port; + IP4_ADDR(&upcb->remote_ip, pudp_sent->pespconn->proto.udp->remote_ip[0], + pudp_sent->pespconn->proto.udp->remote_ip[1], + pudp_sent->pespconn->proto.udp->remote_ip[2], + pudp_sent->pespconn->proto.udp->remote_ip[3]); + + LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %x %d\n", __LINE__, upcb->remote_ip, upcb->remote_port)); + + struct netif *sta_netif = (struct netif *)eagle_lwip_getif(0x00); + struct netif *ap_netif = (struct netif *)eagle_lwip_getif(0x01); + + if(wifi_get_opmode() == ESPCONN_AP_STA && default_interface == ESPCONN_AP_STA && sta_netif != NULL && ap_netif != NULL) + { + if(netif_is_up(sta_netif) && netif_is_up(ap_netif) && \ + ip_addr_isbroadcast(&upcb->remote_ip, sta_netif) && \ + ip_addr_isbroadcast(&upcb->remote_ip, ap_netif)) { + + p_temp = pbuf_alloc(PBUF_TRANSPORT, datalen, PBUF_RAM); + if (pbuf_copy (p_temp,p) != ERR_OK) { + LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent: copying to new pbuf failed\n")); + return ESPCONN_ARG; + } + netif_set_default(sta_netif); + err = udp_send(upcb, p_temp); + pbuf_free(p_temp); + netif_set_default(ap_netif); + } + } + err = udp_send(upcb, p); + + LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %d\n", __LINE__, err)); + + if (p->ref != 0) { + LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %p\n", __LINE__, p)); + pbuf_free(p); + pudp_sent->pcommon.ptrbuf = psent + datalen; + pudp_sent->pcommon.cntr = length - datalen; + pudp_sent->pcommon.err = err; + espconn_data_sent(pudp_sent, ESPCONN_SEND); + if (err > 0) + return ESPCONN_IF; + return err; + } else { + pbuf_free(p); + return ESPCONN_RTE; + } +} + +/****************************************************************************** + * FunctionName : espconn_udp_sendto + * Description : sent data for UDP + * Parameters : void *arg -- UDP to send + * uint8* psent -- Data to send + * uint16 length -- Length of data to send + * Returns : return espconn error code. + * - ESPCONN_OK. Successful. No error occured. + * - ESPCONN_MEM. Out of memory. + * - ESPCONN_RTE. Could not find route to destination address. + * - More errors could be returned by lower protocol layers. +*******************************************************************************/ +err_t ICACHE_FLASH_ATTR +espconn_udp_sendto(void *arg, uint8 *psent, uint16 length) +{ + espconn_msg *pudp_sent = arg; + struct udp_pcb *upcb = pudp_sent->pcommon.pcb; + struct espconn *pespconn = pudp_sent->pespconn; + struct pbuf *p, *q ,*p_temp; + struct ip_addr dst_ip; + u16_t dst_port; + u8_t *data = NULL; + u16_t cnt = 0; + u16_t datalen = 0; + u16_t i = 0; + err_t err; + LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %d %p\n", __LINE__, length, upcb)); + + if (pudp_sent == NULL || upcb == NULL || psent == NULL || length == 0) { + return ESPCONN_ARG; + } + + if (1470 < length) { + datalen = 1470; + } else { + datalen = length; + } + + p = pbuf_alloc(PBUF_TRANSPORT, datalen, PBUF_RAM); + LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %p\n", __LINE__, p)); + + if (p != NULL) { + q = p; + + while (q != NULL) { + data = (u8_t *)q->payload; + LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %p\n", __LINE__, data)); + + for (i = 0; i < q->len; i++) { + data[i] = ((u8_t *) psent)[cnt++]; + } + + q = q->next; + } + } else { + return ESPCONN_MEM; + } + + dst_port = pespconn->proto.udp->remote_port; + IP4_ADDR(&dst_ip, pespconn->proto.udp->remote_ip[0], + pespconn->proto.udp->remote_ip[1], pespconn->proto.udp->remote_ip[2], + pespconn->proto.udp->remote_ip[3]); + LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %x %d\n", __LINE__, upcb->remote_ip, upcb->remote_port)); + + struct netif *sta_netif = (struct netif *)eagle_lwip_getif(0x00); + struct netif *ap_netif = (struct netif *)eagle_lwip_getif(0x01); + + if(wifi_get_opmode() == ESPCONN_AP_STA && default_interface == ESPCONN_AP_STA && sta_netif != NULL && ap_netif != NULL) + { + if(netif_is_up(sta_netif) && netif_is_up(ap_netif) && \ + ip_addr_isbroadcast(&upcb->remote_ip, sta_netif) && \ + ip_addr_isbroadcast(&upcb->remote_ip, ap_netif)) { + + p_temp = pbuf_alloc(PBUF_TRANSPORT, datalen, PBUF_RAM); + if (pbuf_copy (p_temp,p) != ERR_OK) { + LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sendto: copying to new pbuf failed\n")); + return ESPCONN_ARG; + } + netif_set_default(sta_netif); + err = udp_sendto(upcb, p_temp, &dst_ip, dst_port); + pbuf_free(p_temp); + netif_set_default(ap_netif); + } + } + err = udp_sendto(upcb, p, &dst_ip, dst_port); + + if (p->ref != 0) { + pbuf_free(p); + pudp_sent->pcommon.ptrbuf = psent + datalen; + pudp_sent->pcommon.cntr = length - datalen; + pudp_sent->pcommon.err = err; + espconn_data_sent(pudp_sent, ESPCONN_SENDTO); + + if (err > 0) + return ESPCONN_IF; + return err; + } else { + pbuf_free(p); + return ESPCONN_RTE; + } +} + +/****************************************************************************** + * FunctionName : espconn_udp_server_recv + * Description : This callback will be called when receiving a datagram. + * Parameters : arg -- user supplied argument + * upcb -- the udp_pcb which received data + * p -- the packet buffer that was received + * addr -- the remote IP address from which the packet was received + * port -- the remote port from which the packet was received + * Returns : none +*******************************************************************************/ +static void ICACHE_FLASH_ATTR +espconn_udp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, + struct ip_addr *addr, u16_t port) +{ + espconn_msg *precv = arg; + u8_t *pdata = NULL; + u16_t length = 0; + struct ip_info ipconfig; + + LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_server_recv %d %p\n", __LINE__, upcb)); + + precv->pcommon.remote_ip[0] = ip4_addr1_16(addr); + precv->pcommon.remote_ip[1] = ip4_addr2_16(addr); + precv->pcommon.remote_ip[2] = ip4_addr3_16(addr); + precv->pcommon.remote_ip[3] = ip4_addr4_16(addr); + precv->pcommon.remote_port = port; + precv->pcommon.pcb = upcb; + + if (wifi_get_opmode() != 1) { + wifi_get_ip_info(1, &ipconfig); + + if (!ip_addr_netcmp(addr, &ipconfig.ip, &ipconfig.netmask)) { + wifi_get_ip_info(0, &ipconfig); + } + } else { + wifi_get_ip_info(0, &ipconfig); + } + + precv->pespconn->proto.udp->local_ip[0] = ip4_addr1_16(&ipconfig.ip); + precv->pespconn->proto.udp->local_ip[1] = ip4_addr2_16(&ipconfig.ip); + precv->pespconn->proto.udp->local_ip[2] = ip4_addr3_16(&ipconfig.ip); + precv->pespconn->proto.udp->local_ip[3] = ip4_addr4_16(&ipconfig.ip); + + if (p != NULL) { + pdata = (u8_t *)os_zalloc(p ->tot_len + 1); + length = pbuf_copy_partial(p, pdata, p ->tot_len, 0); + precv->pcommon.pcb = upcb; + pbuf_free(p); + if (length != 0) { + if (precv->pespconn->recv_callback != NULL) { + precv->pespconn->recv_callback(precv->pespconn, (char *)pdata, length); + } + } + os_free(pdata); + } else { + return; + } +} + +/****************************************************************************** + * FunctionName : espconn_udp_disconnect + * Description : A new incoming connection has been disconnected. + * Parameters : espconn -- the espconn used to disconnect with host + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR espconn_udp_disconnect(espconn_msg *pdiscon) +{ + if (pdiscon == NULL) { + return; + } + + struct udp_pcb *upcb = pdiscon->pcommon.pcb; + + udp_disconnect(upcb); + + udp_remove(upcb); + + espconn_list_delete(&plink_active, pdiscon); + + os_free(pdiscon); + pdiscon = NULL; +} + +/****************************************************************************** + * FunctionName : espconn_udp_server + * Description : Initialize the server: set up a PCB and bind it to the port + * Parameters : pespconn -- the espconn used to build server + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_udp_server(struct espconn *pespconn) +{ + struct udp_pcb *upcb = NULL; + espconn_msg *pserver = NULL; + upcb = udp_new(); + + if (upcb == NULL) { + return ESPCONN_MEM; + } else { + pserver = (espconn_msg *)os_zalloc(sizeof(espconn_msg)); + + if (pserver == NULL) { + udp_remove(upcb); + return ESPCONN_MEM; + } + + pserver->pcommon.pcb = upcb; + pserver->pespconn = pespconn; + espconn_list_creat(&plink_active, pserver); + udp_bind(upcb, IP_ADDR_ANY, pserver->pespconn->proto.udp->local_port); + udp_recv(upcb, espconn_udp_recv, (void *)pserver); + return ESPCONN_OK; + } +} + +/****************************************************************************** + * FunctionName : espconn_igmp_leave + * Description : leave a multicast group + * Parameters : host_ip -- the ip address of udp server + * multicast_ip -- multicast ip given by user + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_igmp_leave(ip_addr_t *host_ip, ip_addr_t *multicast_ip) +{ + if (igmp_leavegroup(host_ip, multicast_ip) != ERR_OK) { + LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("udp_leave_multigrup failed!\n")); + return -1; + }; + + return ESPCONN_OK; +} + +/****************************************************************************** + * FunctionName : espconn_igmp_join + * Description : join a multicast group + * Parameters : host_ip -- the ip address of udp server + * multicast_ip -- multicast ip given by user + * Returns : none +*******************************************************************************/ +sint8 ICACHE_FLASH_ATTR +espconn_igmp_join(ip_addr_t *host_ip, ip_addr_t *multicast_ip) +{ + if (igmp_joingroup(host_ip, multicast_ip) != ERR_OK) { + LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("udp_join_multigrup failed!\n")); + return -1; + }; + + /* join to any IP address at the port */ + return ESPCONN_OK; +} diff --git a/variants/lwip/lwip/app/netio.c b/variants/lwip/lwip/app/netio.c new file mode 100755 index 000000000..47b359934 --- /dev/null +++ b/variants/lwip/lwip/app/netio.c @@ -0,0 +1,369 @@ +/** + * @file + * MetIO Server + * + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ +#include "lwip/opt.h" + +#if LWIP_TCP +#include "lwip/tcp.h" + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +/* + * This implements a netio server. + * The client sends a command word (4 bytes) then a data length word (4 bytes). + * If the command is "receive", the server is to consume "data length" bytes into + * a circular buffer until the first byte is non-zero, then it is to consume + * another command/data pair. + * If the command is "send", the server is to send "data length" bytes from a circular + * buffer with the first byte being zero, until "some time" (6 seconds in the + * current netio126.zip download) has passed and then send one final buffer with + * the first byte being non-zero. Then it is to consume another command/data pair. + */ + +/* See http://www.nwlab.net/art/netio/netio.html to get the netio tool */ + +/* implementation options */ +#define NETIO_BUF_SIZE (4 * 1024) +#define NETIO_USE_STATIC_BUF 0 + +/* NetIO server state definition */ +#define NETIO_STATE_WAIT_FOR_CMD 0 +#define NETIO_STATE_RECV_DATA 1 +#define NETIO_STATE_SEND_DATA 2 +#define NETIO_STATE_SEND_DATA_LAST 3 +#define NETIO_STATE_DONE 4 + +struct netio_state { + u32_t state; + u32_t cmd; + u32_t data_len; + u32_t cntr; + u8_t * buf_ptr; + u32_t buf_pos; + u32_t first_byte; + u32_t time_stamp; +}; + +/* NetIO command protocol definition */ +#define NETIO_CMD_QUIT 0 +#define NETIO_CMD_C2S 1 +#define NETIO_CMD_S2C 2 +#define NETIO_CMD_RES 3 + +static err_t netio_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err); + +static void ICACHE_FLASH_ATTR +netio_close(void *arg, struct tcp_pcb *pcb) +{ + err_t err; + + struct netio_state *ns = arg; + ns->state = NETIO_STATE_DONE; + tcp_recv(pcb, NULL); + err = tcp_close(pcb); + + if (err != ERR_OK) { + /* closing failed, try again later */ + tcp_recv(pcb, netio_recv); + } else { + /* closing succeeded */ +#if NETIO_USE_STATIC_BUF != 1 + if(ns->buf_ptr != NULL){ + mem_free(ns->buf_ptr); + } +#endif + tcp_arg(pcb, NULL); + tcp_poll(pcb, NULL, 0); + tcp_sent(pcb, NULL); + if (arg != NULL) { + mem_free(arg); + } + } +} + +static err_t ICACHE_FLASH_ATTR +netio_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) +{ + struct netio_state *ns = arg; + u8_t * data_ptr; + u32_t data_cntr; + struct pbuf *q = p; + u16_t len; + + if (p != NULL) { + tcp_recved(pcb, p->tot_len); + } + + if (err == ERR_OK && q != NULL) { + + while (q != NULL) { + data_cntr = q->len; + data_ptr = q->payload; + while (data_cntr--) { + if (ns->state == NETIO_STATE_DONE){ + netio_close(ns, pcb); + break; + } else if (ns->state == NETIO_STATE_WAIT_FOR_CMD) { + if (ns->cntr < 4) { + /* build up the CMD field */ + ns->cmd <<= 8; + ns->cmd |= *data_ptr++; + ns->cntr++; + } else if (ns->cntr < 8) { + /* build up the DATA field */ + ns->data_len <<= 8; + ns->data_len |= *data_ptr++; + ns->cntr++; + + if (ns->cntr == 8) { + /* now we have full command and data words */ + ns->cntr = 0; + ns->buf_pos = 0; + ns->buf_ptr[0] = 0; + if (ns->cmd == NETIO_CMD_C2S) { + ns->state = NETIO_STATE_RECV_DATA; + } else if (ns->cmd == NETIO_CMD_S2C) { + ns->state = NETIO_STATE_SEND_DATA; + /* start timer */ + ns->time_stamp = sys_now(); + /* send first round of data */ + + len = tcp_sndbuf(pcb); + len = LWIP_MIN(len, ns->data_len - ns->cntr); + len = LWIP_MIN(len, NETIO_BUF_SIZE - ns->buf_pos); + + do { + err = tcp_write(pcb, ns->buf_ptr + ns->buf_pos, len, TCP_WRITE_FLAG_COPY); + if (err == ERR_MEM) { + len /= 2; + } + } while ((err == ERR_MEM) && (len > 1)); + + ns->buf_pos += len; + ns->cntr += len; + + } else { + /* unrecognized command, punt */ + ns->cntr = 0; + ns->buf_pos = 0; + ns->buf_ptr[0] = 0; + netio_close(ns, pcb); + break; + } + } + } else { + /* in trouble... shouldn't be in this state! */ + } + + } else if (ns->state == NETIO_STATE_RECV_DATA) { + + if(ns->cntr == 0){ + /* save the first byte of this new round of data + * this will not match ns->buf_ptr[0] in the case that + * NETIO_BUF_SIZE is less than ns->data_len. + */ + ns->first_byte = *data_ptr; + } + + ns->buf_ptr[ns->buf_pos++] = *data_ptr++; + ns->cntr++; + + if (ns->buf_pos == NETIO_BUF_SIZE) { + /* circularize the buffer */ + ns->buf_pos = 0; + } + + if(ns->cntr == ns->data_len){ + ns->cntr = 0; + if (ns->first_byte != 0) { + /* if this last round did not start with 0, + * go look for another command */ + ns->state = NETIO_STATE_WAIT_FOR_CMD; + ns->data_len = 0; + ns->cmd = 0; + /* TODO LWIP_DEBUGF( print out some throughput calculation results... ); */ + } else { + /* stay here and wait on more data */ + } + } + + } else if (ns->state == NETIO_STATE_SEND_DATA + || ns->state == NETIO_STATE_SEND_DATA_LAST) { + /* I don't think this should happen... */ + } else { + /* done / quit */ + netio_close(ns, pcb); + break; + } /* end of ns->state condition */ + } /* end of while data still in this pbuf */ + + q = q->next; + } + + pbuf_free(p); + + } else { + + /* error or closed by other side */ + if (p != NULL) { + pbuf_free(p); + } + + /* close the connection */ + netio_close(ns, pcb); + + } + return ERR_OK; + +} + +static err_t ICACHE_FLASH_ATTR +netio_sent(void *arg, struct tcp_pcb *pcb, u16_t len) +{ + struct netio_state *ns = arg; + err_t err = ERR_OK; + + if (ns->cntr >= ns->data_len && ns->state == NETIO_STATE_SEND_DATA) { + /* done with this round of sending */ + ns->buf_pos = 0; + ns->cntr = 0; + + /* check if timer expired */ + if (sys_now() - ns->time_stamp > 600) { + ns->buf_ptr[0] = 1; + ns->state = NETIO_STATE_SEND_DATA_LAST; + } else { + ns->buf_ptr[0] = 0; + } + } + + if(ns->state == NETIO_STATE_SEND_DATA_LAST || ns->state == NETIO_STATE_SEND_DATA){ + len = tcp_sndbuf(pcb); + len = LWIP_MIN(len, ns->data_len - ns->cntr); + len = LWIP_MIN(len, NETIO_BUF_SIZE - ns->buf_pos); + + if(ns->cntr < ns->data_len){ + do { + err = tcp_write(pcb, ns->buf_ptr + ns->buf_pos, len, TCP_WRITE_FLAG_COPY); + if (err == ERR_MEM) { + len /= 2; + } + } while ((err == ERR_MEM) && (len > 1)); + + ns->buf_pos += len; + if(ns->buf_pos >= NETIO_BUF_SIZE){ + ns->buf_pos = 0; + } + + ns->cntr += len; + } + } + + if(ns->cntr >= ns->data_len && ns->state == NETIO_STATE_SEND_DATA_LAST){ + /* we have buffered up all our data to send this last round, go look for a command */ + ns->state = NETIO_STATE_WAIT_FOR_CMD; + ns->cntr = 0; + /* TODO LWIP_DEBUGF( print out some throughput calculation results... ); */ + } + + return ERR_OK; +} + +static err_t ICACHE_FLASH_ATTR +netio_poll(void *arg, struct tcp_pcb *pcb) +{ + struct netio_state * ns = arg; + if(ns->state == NETIO_STATE_SEND_DATA){ + + } else if(ns->state == NETIO_STATE_DONE){ + netio_close(ns, pcb); + } + + return ERR_OK; + +} + +#if NETIO_USE_STATIC_BUF == 1 +static u8_t netio_buf[NETIO_BUF_SIZE]; +#endif + +static err_t ICACHE_FLASH_ATTR +netio_accept(void *arg, struct tcp_pcb *pcb, err_t err) +{ + struct netio_state * ns; + + LWIP_UNUSED_ARG(err); + + ns = (struct netio_state *)mem_malloc(sizeof(struct netio_state)); + + if(ns == NULL){ + return ERR_MEM; + } + + ns->state = NETIO_STATE_WAIT_FOR_CMD; + ns->data_len = 0; + ns->cmd = 0; + ns->cntr = 0; + ns->buf_pos = 0; +#if NETIO_USE_STATIC_BUF == 1 + ns->buf_ptr = netio_buf; +#else + ns->buf_ptr = (u8_t *)mem_malloc(NETIO_BUF_SIZE); + + if(ns->buf_ptr == NULL){ + mem_free(ns); + return ERR_MEM; + } +#endif + + ns->buf_ptr[0] = 0; + + tcp_arg(pcb, ns); + tcp_sent(pcb, netio_sent); + tcp_recv(pcb, netio_recv); + tcp_poll(pcb, netio_poll, 4); /* every 2 seconds */ + return ERR_OK; +} + +void ICACHE_FLASH_ATTR netio_init(void) +{ + struct tcp_pcb *pcb; + + pcb = tcp_new(); + tcp_bind(pcb, IP_ADDR_ANY, 18767); + pcb = tcp_listen(pcb); + tcp_accept(pcb, netio_accept); +} + +#endif /* LWIP_TCP */ diff --git a/variants/lwip/lwip/app/ping.c b/variants/lwip/lwip/app/ping.c new file mode 100755 index 000000000..ae863a6e1 --- /dev/null +++ b/variants/lwip/lwip/app/ping.c @@ -0,0 +1,329 @@ +/** + * @file + * Ping sender module + * + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +/** + * This is an example of a "ping" sender (with raw API and socket API). + * It can be used as a start point to maintain opened a network connection, or + * like a network "watchdog" for your device. + * + */ + +/* + * copyright (c) 2010 - 2011 Espressif System + */ + +#include "lwip/opt.h" + +#if LWIP_RAW /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/mem.h" +#include "lwip/raw.h" +#include "lwip/icmp.h" +#include "lwip/netif.h" +#include "lwip/sys.h" +#include "lwip/timers.h" +#include "lwip/inet_chksum.h" +#include "os_type.h" +#include "osapi.h" + +#include "lwip/app/ping.h" + +#if PING_USE_SOCKETS +#include "lwip/sockets.h" +#include "lwip/inet.h" +#endif /* PING_USE_SOCKETS */ + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +/* ping variables */ +static u16_t ping_seq_num = 0; +static u32_t ping_time; + +static void ICACHE_FLASH_ATTR ping_timeout(void* arg) +{ + struct ping_msg *pingmsg = (struct ping_msg *)arg; + pingmsg->timeout_count ++; + if (pingmsg->ping_opt->recv_function == NULL){ + os_printf("ping timeout\n"); + } else { + struct ping_resp pingresp; + os_bzero(&pingresp, sizeof(struct ping_resp)); + pingresp.ping_err = -1; + pingmsg->ping_opt->recv_function(pingmsg->ping_opt, (void*)&pingresp); + } +} + +/** Prepare a echo ICMP request */ +static void ICACHE_FLASH_ATTR +ping_prepare_echo( struct icmp_echo_hdr *iecho, u16_t len) +{ + size_t i = 0; + size_t data_len = len - sizeof(struct icmp_echo_hdr); + + ICMPH_TYPE_SET(iecho, ICMP_ECHO); + ICMPH_CODE_SET(iecho, 0); + iecho->chksum = 0; + iecho->id = PING_ID; + ++ ping_seq_num; + if (ping_seq_num == 0x7fff) + ping_seq_num = 0; + + iecho->seqno = htons(ping_seq_num); + + /* fill the additional data buffer with some data */ + for(i = 0; i < data_len; i++) { + ((char*)iecho)[sizeof(struct icmp_echo_hdr) + i] = (char)i; + } + + iecho->chksum = inet_chksum(iecho, len); +} +/* +static void ICACHE_FLASH_ATTR +ping_prepare_er(struct icmp_echo_hdr *iecho, u16_t len) +{ + + ICMPH_TYPE_SET(iecho, ICMP_ER); + ICMPH_CODE_SET(iecho, 0); + iecho->chksum = 0; + + iecho->chksum = inet_chksum(iecho, len); +} +*/ +/* Ping using the raw ip */ +static u8_t ICACHE_FLASH_ATTR +ping_recv(void *arg, struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *addr) +{ + struct icmp_echo_hdr *iecho = NULL; + static u16_t seqno = 0; + struct ping_msg *pingmsg = (struct ping_msg*)arg; + + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(addr); + LWIP_ASSERT("p != NULL", p != NULL); + + if (pbuf_header( p, -PBUF_IP_HLEN)==0) { + iecho = (struct icmp_echo_hdr *)p->payload; + + if ((iecho->id == PING_ID) && (iecho->seqno == htons(ping_seq_num)) && iecho->type == ICMP_ER) { + LWIP_DEBUGF( PING_DEBUG, ("ping: recv ")); + ip_addr_debug_print(PING_DEBUG, addr); + LWIP_DEBUGF( PING_DEBUG, (" %"U32_F" ms\n", (sys_now()-ping_time))); + if (iecho->seqno != seqno){ + /* do some ping result processing */ + { + struct ip_hdr *iphdr = NULL; + char ipaddrstr[16]; + ip_addr_t source_ip; + sys_untimeout(ping_timeout, pingmsg); + os_bzero(&source_ip, sizeof(ip_addr_t)); + os_bzero(ipaddrstr, sizeof(ipaddrstr)); + uint32 delay = system_relative_time(pingmsg->ping_sent); + delay /= PING_COARSE; + iphdr = (struct ip_hdr*)((u8*)iecho - PBUF_IP_HLEN); + source_ip.addr = iphdr->src.addr; + ipaddr_ntoa_r(&source_ip,ipaddrstr, sizeof(ipaddrstr)); + if (pingmsg->ping_opt->recv_function == NULL){ + os_printf("recv %s: byte = %d, time = %d ms, seq = %d\n",ipaddrstr, PING_DATA_SIZE, delay, ntohs(iecho->seqno)); + } else { + struct ping_resp pingresp; + os_bzero(&pingresp, sizeof(struct ping_resp)); + pingresp.bytes = PING_DATA_SIZE; + pingresp.resp_time = delay; + pingresp.seqno = ntohs(iecho->seqno); + pingresp.ping_err = 0; + pingmsg->ping_opt->recv_function(pingmsg->ping_opt,(void*) &pingresp); + } + } + seqno = iecho->seqno; + } + + PING_RESULT(1); + pbuf_free(p); + return 1; /* eat the packet */ + } +// } else if(iecho->type == ICMP_ECHO){ +// struct pbuf *q = NULL; +// os_printf("receive ping request:seq=%d\n", ntohs(iecho->seqno)); +// q = pbuf_alloc(PBUF_IP, (u16_t)p->tot_len, PBUF_RAM); +// if (q!=NULL) { +// pbuf_copy(q, p); +// iecho = (struct icmp_echo_hdr *)q->payload; +// ping_prepare_er(iecho, q->tot_len); +// raw_sendto(pcb, q, addr); +// pbuf_free(q); +// } +// pbuf_free(p); +// return 1; +// } + } + + return 0; /* don't eat the packet */ +} + +static void ICACHE_FLASH_ATTR +ping_send(struct raw_pcb *raw, ip_addr_t *addr) +{ + struct pbuf *p = NULL; + struct icmp_echo_hdr *iecho = NULL; + size_t ping_size = sizeof(struct icmp_echo_hdr) + PING_DATA_SIZE; + + LWIP_DEBUGF( PING_DEBUG, ("ping: send ")); + ip_addr_debug_print(PING_DEBUG, addr); + LWIP_DEBUGF( PING_DEBUG, ("\n")); + LWIP_ASSERT("ping_size <= 0xffff", ping_size <= 0xffff); + + p = pbuf_alloc(PBUF_IP, (u16_t)ping_size, PBUF_RAM); + if (!p) { + return; + } + if ((p->len == p->tot_len) && (p->next == NULL)) { + iecho = (struct icmp_echo_hdr *)p->payload; + + ping_prepare_echo(iecho, (u16_t)ping_size); + + raw_sendto(raw, p, addr); + ping_time = sys_now(); + } + pbuf_free(p); +} + +static void ICACHE_FLASH_ATTR +ping_coarse_tmr(void *arg) +{ + struct ping_msg *pingmsg = (struct ping_msg*)arg; + struct ping_option *ping_opt= NULL; + struct ping_resp pingresp; + ip_addr_t ping_target; + + LWIP_ASSERT("ping_timeout: no pcb given!", pingmsg != NULL); + ping_target.addr = pingmsg->ping_opt->ip; + ping_opt = pingmsg->ping_opt; + if (--pingmsg->sent_count != 0){ + pingmsg ->ping_sent = system_get_time(); + ping_send(pingmsg->ping_pcb, &ping_target); + + sys_timeout(PING_TIMEOUT_MS, ping_timeout, pingmsg); + sys_timeout(pingmsg->coarse_time, ping_coarse_tmr, pingmsg); + } else { + uint32 delay = system_relative_time(pingmsg->ping_start); + delay /= PING_COARSE; +// ping_seq_num = 0; + if (ping_opt->sent_function == NULL){ + os_printf("ping %d, timeout %d, total payload %d bytes, %d ms\n", + pingmsg->max_count, pingmsg->timeout_count, PING_DATA_SIZE*(pingmsg->max_count - pingmsg->timeout_count),delay); + } else { + os_bzero(&pingresp, sizeof(struct ping_resp)); + pingresp.total_count = pingmsg->max_count; + pingresp.timeout_count = pingmsg->timeout_count; + pingresp.total_bytes = PING_DATA_SIZE*(pingmsg->max_count - pingmsg->timeout_count); + pingresp.total_time = delay; + pingresp.ping_err = 0; + } + sys_untimeout(ping_coarse_tmr, pingmsg); + raw_remove(pingmsg->ping_pcb); + os_free(pingmsg); + if (ping_opt->sent_function != NULL) + ping_opt->sent_function(ping_opt,(uint8*)&pingresp); + } +} + +static bool ICACHE_FLASH_ATTR +ping_raw_init(struct ping_msg *pingmsg) +{ + if (pingmsg == NULL) + return false; + + ip_addr_t ping_target; + pingmsg->ping_pcb = raw_new(IP_PROTO_ICMP); + LWIP_ASSERT("ping_pcb != NULL", pingmsg->ping_pcb != NULL); + + raw_recv(pingmsg->ping_pcb, ping_recv, pingmsg); + raw_bind(pingmsg->ping_pcb, IP_ADDR_ANY); + + ping_target.addr = pingmsg->ping_opt->ip; + pingmsg ->ping_sent = system_get_time(); + ping_send(pingmsg->ping_pcb, &ping_target); + + sys_timeout(PING_TIMEOUT_MS, ping_timeout, pingmsg); + sys_timeout(pingmsg->coarse_time, ping_coarse_tmr, pingmsg); + return true; +} + +bool ICACHE_FLASH_ATTR +ping_start(struct ping_option *ping_opt) +{ + struct ping_msg *pingmsg = NULL; + pingmsg = (struct ping_msg *)os_zalloc(sizeof(struct ping_msg)); + if (pingmsg == NULL || ping_opt == NULL) + return false; + + pingmsg->ping_opt = ping_opt; + if (ping_opt->count != 0) + pingmsg->max_count = ping_opt->count; + else + pingmsg->max_count = DEFAULT_PING_MAX_COUNT; + + if (ping_opt->coarse_time != 0) + pingmsg->coarse_time = ping_opt->coarse_time * PING_COARSE; + else + pingmsg->coarse_time = PING_COARSE; + + pingmsg->ping_start = system_get_time(); + pingmsg->sent_count = pingmsg->max_count; + return ping_raw_init(pingmsg); +} + +bool ICACHE_FLASH_ATTR +ping_regist_recv(struct ping_option *ping_opt, ping_recv_function ping_recv) +{ + if (ping_opt == NULL) + return false; + + ping_opt ->recv_function = ping_recv; + return true; +} + +bool ICACHE_FLASH_ATTR +ping_regist_sent(struct ping_option *ping_opt, ping_sent_function ping_sent) +{ + if (ping_opt == NULL) + return false; + + ping_opt ->sent_function = ping_sent; + return true; +} + +#endif /* LWIP_RAW */ diff --git a/variants/lwip/lwip/core/def.c b/variants/lwip/lwip/core/def.c new file mode 100755 index 000000000..352b55241 --- /dev/null +++ b/variants/lwip/lwip/core/def.c @@ -0,0 +1,108 @@ +/** + * @file + * Common functions used throughout the stack. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Simon Goldschmidt + * + */ + +#include "lwip/opt.h" +#include "lwip/def.h" + +/** + * These are reference implementations of the byte swapping functions. + * Again with the aim of being simple, correct and fully portable. + * Byte swapping is the second thing you would want to optimize. You will + * need to port it to your architecture and in your cc.h: + * + * #define LWIP_PLATFORM_BYTESWAP 1 + * #define LWIP_PLATFORM_HTONS(x) + * #define LWIP_PLATFORM_HTONL(x) + * + * Note ntohs() and ntohl() are merely references to the htonx counterparts. + */ + +#if (LWIP_PLATFORM_BYTESWAP == 0) && (BYTE_ORDER == LITTLE_ENDIAN) + +/** + * Convert an u16_t from host- to network byte order. + * + * @param n u16_t in host byte order + * @return n in network byte order + */ +u16_t +lwip_htons(u16_t n) +{ + return ((n & 0xff) << 8) | ((n & 0xff00) >> 8); +} + +/** + * Convert an u16_t from network- to host byte order. + * + * @param n u16_t in network byte order + * @return n in host byte order + */ +u16_t +lwip_ntohs(u16_t n) +{ + return lwip_htons(n); +} + +/** + * Convert an u32_t from host- to network byte order. + * + * @param n u32_t in host byte order + * @return n in network byte order + */ +u32_t +lwip_htonl(u32_t n) +{ + return ((n & 0xff) << 24) | + ((n & 0xff00) << 8) | + ((n & 0xff0000UL) >> 8) | + ((n & 0xff000000UL) >> 24); +} + +/** + * Convert an u32_t from network- to host byte order. + * + * @param n u32_t in network byte order + * @return n in host byte order + */ +u32_t +lwip_ntohl(u32_t n) +{ + return lwip_htonl(n); +} + +#endif /* (LWIP_PLATFORM_BYTESWAP == 0) && (BYTE_ORDER == LITTLE_ENDIAN) */ diff --git a/variants/lwip/lwip/core/dhcp.c b/variants/lwip/lwip/core/dhcp.c new file mode 100755 index 000000000..342543ea0 --- /dev/null +++ b/variants/lwip/lwip/core/dhcp.c @@ -0,0 +1,1815 @@ +/** + * @file + * Dynamic Host Configuration Protocol client + * + */ + +/* + * + * Copyright (c) 2001-2004 Leon Woestenberg + * Copyright (c) 2001-2004 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is a contribution to the lwIP TCP/IP stack. + * The Swedish Institute of Computer Science and Adam Dunkels + * are specifically granted permission to redistribute this + * source code. + * + * Author: Leon Woestenberg + * + * This is a DHCP client for the lwIP TCP/IP stack. It aims to conform + * with RFC 2131 and RFC 2132. + * + * TODO: + * - Support for interfaces other than Ethernet (SLIP, PPP, ...) + * + * Please coordinate changes and requests with Leon Woestenberg + * + * + * Integration with your code: + * + * In lwip/dhcp.h + * #define DHCP_COARSE_TIMER_SECS (recommended 60 which is a minute) + * #define DHCP_FINE_TIMER_MSECS (recommended 500 which equals TCP coarse timer) + * + * Then have your application call dhcp_coarse_tmr() and + * dhcp_fine_tmr() on the defined intervals. + * + * dhcp_start(struct netif *netif); + * starts a DHCP client instance which configures the interface by + * obtaining an IP address lease and maintaining it. + * + * Use dhcp_release(netif) to end the lease and use dhcp_stop(netif) + * to remove the DHCP client. + * + */ + +#include "lwip/opt.h" + +#if LWIP_DHCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/stats.h" +#include "lwip/mem.h" +#include "lwip/udp.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/def.h" +#include "lwip/sys.h" +#include "lwip/dhcp.h" +#include "lwip/autoip.h" +#include "lwip/dns.h" +#include "netif/etharp.h" + +#include + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +/** Default for DHCP_GLOBAL_XID is 0xABCD0000 + * This can be changed by defining DHCP_GLOBAL_XID and DHCP_GLOBAL_XID_HEADER, e.g. + * #define DHCP_GLOBAL_XID_HEADER "stdlib.h" + * #define DHCP_GLOBAL_XID rand() + */ +#ifdef DHCP_GLOBAL_XID_HEADER +#include DHCP_GLOBAL_XID_HEADER /* include optional starting XID generation prototypes */ +#endif + +/** DHCP_OPTION_MAX_MSG_SIZE is set to the MTU + * MTU is checked to be big enough in dhcp_start */ +#define DHCP_MAX_MSG_LEN(netif) (netif->mtu) +#define DHCP_MAX_MSG_LEN_MIN_REQUIRED 576 +/** Minimum length for reply before packet is parsed */ +#define DHCP_MIN_REPLY_LEN 44 + +#define REBOOT_TRIES 2 + +/** Option handling: options are parsed in dhcp_parse_reply + * and saved in an array where other functions can load them from. + * This might be moved into the struct dhcp (not necessarily since + * lwIP is single-threaded and the array is only used while in recv + * callback). */ +#define DHCP_OPTION_IDX_OVERLOAD 0 +#define DHCP_OPTION_IDX_MSG_TYPE 1 +#define DHCP_OPTION_IDX_SERVER_ID 2 +#define DHCP_OPTION_IDX_LEASE_TIME 3 +#define DHCP_OPTION_IDX_T1 4 +#define DHCP_OPTION_IDX_T2 5 +#define DHCP_OPTION_IDX_SUBNET_MASK 6 +#define DHCP_OPTION_IDX_ROUTER 7 +#define DHCP_OPTION_IDX_DNS_SERVER 8 +#define DHCP_OPTION_IDX_MAX (DHCP_OPTION_IDX_DNS_SERVER + DNS_MAX_SERVERS) + +/** Holds the decoded option values, only valid while in dhcp_recv. + @todo: move this into struct dhcp? */ +u32_t dhcp_rx_options_val[DHCP_OPTION_IDX_MAX]; +/** Holds a flag which option was received and is contained in dhcp_rx_options_val, + only valid while in dhcp_recv. + @todo: move this into struct dhcp? */ +u8_t dhcp_rx_options_given[DHCP_OPTION_IDX_MAX]; + +#define dhcp_option_given(dhcp, idx) (dhcp_rx_options_given[idx] != 0) +#define dhcp_got_option(dhcp, idx) (dhcp_rx_options_given[idx] = 1) +#define dhcp_clear_option(dhcp, idx) (dhcp_rx_options_given[idx] = 0) +#define dhcp_clear_all_options(dhcp) (os_memset(dhcp_rx_options_given, 0, sizeof(dhcp_rx_options_given))) +#define dhcp_get_option_value(dhcp, idx) (dhcp_rx_options_val[idx]) +#define dhcp_set_option_value(dhcp, idx, val) (dhcp_rx_options_val[idx] = (val)) + + +/* DHCP client state machine functions */ +static err_t dhcp_discover(struct netif *netif); +static err_t dhcp_select(struct netif *netif); +static void dhcp_bind(struct netif *netif); +#if DHCP_DOES_ARP_CHECK +static err_t dhcp_decline(struct netif *netif); +#endif /* DHCP_DOES_ARP_CHECK */ +static err_t dhcp_rebind(struct netif *netif); +static err_t dhcp_reboot(struct netif *netif); +static void dhcp_set_state(struct dhcp *dhcp, u8_t new_state); + +/* receive, unfold, parse and free incoming messages */ +static void dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port); + +/* set the DHCP timers */ +static void dhcp_timeout(struct netif *netif); +static void dhcp_t1_timeout(struct netif *netif); +static void dhcp_t2_timeout(struct netif *netif); + +/* build outgoing messages */ +/* create a DHCP message, fill in common headers */ +static err_t dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type); +/* free a DHCP request */ +static void dhcp_delete_msg(struct dhcp *dhcp); +/* add a DHCP option (type, then length in bytes) */ +static void dhcp_option(struct dhcp *dhcp, u8_t option_type, u8_t option_len); +/* add option values */ +static void dhcp_option_byte(struct dhcp *dhcp, u8_t value); +static void dhcp_option_short(struct dhcp *dhcp, u16_t value); +static void dhcp_option_long(struct dhcp *dhcp, u32_t value); +/* always add the DHCP options trailer to end and pad */ +static void dhcp_option_trailer(struct dhcp *dhcp); + +/** + * Back-off the DHCP client (because of a received NAK response). + * + * Back-off the DHCP client because of a received NAK. Receiving a + * NAK means the client asked for something non-sensible, for + * example when it tries to renew a lease obtained on another network. + * + * We clear any existing set IP address and restart DHCP negotiation + * afresh (as per RFC2131 3.2.3). + * + * @param netif the netif under DHCP control + */ +static void ICACHE_FLASH_ATTR +dhcp_handle_nak(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_handle_nak(netif=%p) %c%c%"U16_F"\n", + (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + /* Set the interface down since the address must no longer be used, as per RFC2131 */ + netif_set_down(netif); + /* remove IP address from interface */ + netif_set_ipaddr(netif, IP_ADDR_ANY); + netif_set_gw(netif, IP_ADDR_ANY); + netif_set_netmask(netif, IP_ADDR_ANY); + /* Change to a defined state */ + dhcp_set_state(dhcp, DHCP_BACKING_OFF); + /* We can immediately restart discovery */ + dhcp_discover(netif); +} + +#if DHCP_DOES_ARP_CHECK +/** + * Checks if the offered IP address is already in use. + * + * It does so by sending an ARP request for the offered address and + * entering CHECKING state. If no ARP reply is received within a small + * interval, the address is assumed to be free for use by us. + * + * @param netif the netif under DHCP control + */ +static void ICACHE_FLASH_ATTR +dhcp_check(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_check(netif=%p) %c%c\n", (void *)netif, (s16_t)netif->name[0], + (s16_t)netif->name[1])); + dhcp_set_state(dhcp, DHCP_CHECKING); + /* create an ARP query for the offered IP address, expecting that no host + responds, as the IP address should not be in use. */ + result = etharp_query(netif, &dhcp->offered_ip_addr, NULL); + if (result != ERR_OK) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("dhcp_check: could not perform ARP query\n")); + } + dhcp->tries++; + msecs = 500; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_check(): set request timeout %"U16_F" msecs\n", msecs)); +} +#endif /* DHCP_DOES_ARP_CHECK */ + +/** + * Remember the configuration offered by a DHCP server. + * + * @param netif the netif under DHCP control + */ +static void ICACHE_FLASH_ATTR +dhcp_handle_offer(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_handle_offer(netif=%p) %c%c%"U16_F"\n", + (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + /* obtain the server address */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_SERVER_ID)) { + ip4_addr_set_u32(&dhcp->server_ip_addr, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_SERVER_ID))); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_handle_offer(): server 0x%08"X32_F"\n", + ip4_addr_get_u32(&dhcp->server_ip_addr))); + /* remember offered address */ + ip_addr_copy(dhcp->offered_ip_addr, dhcp->msg_in->yiaddr); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_handle_offer(): offer for 0x%08"X32_F"\n", + ip4_addr_get_u32(&dhcp->offered_ip_addr))); + + dhcp_select(netif); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("dhcp_handle_offer(netif=%p) did not get server ID!\n", (void*)netif)); + } +} + +/** + * Select a DHCP server offer out of all offers. + * + * Simply select the first offer received. + * + * @param netif the netif under DHCP control + * @return lwIP specific error (see error.h) + */ +static err_t ICACHE_FLASH_ATTR +dhcp_select(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_select(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + dhcp_set_state(dhcp, DHCP_REQUESTING); + + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST); + if (result == ERR_OK) { + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif)); + + /* MUST request the offered IP address */ + dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); + dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr))); + + dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4); + dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->server_ip_addr))); + + dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, 12/*num options*/); + dhcp_option_byte(dhcp, DHCP_OPTION_SUBNET_MASK); + dhcp_option_byte(dhcp, DHCP_OPTION_ROUTER); + dhcp_option_byte(dhcp, DHCP_OPTION_BROADCAST); + dhcp_option_byte(dhcp, DHCP_OPTION_DNS_SERVER); + dhcp_option_byte(dhcp, DHCP_OPTION_DOMAIN_NAME); + dhcp_option_byte(dhcp, DHCP_OPTION_NB_TINS); + dhcp_option_byte(dhcp, DHCP_OPTION_NB_TINT); + dhcp_option_byte(dhcp, DHCP_OPTION_NB_TIS); + dhcp_option_byte(dhcp, DHCP_OPTION_PRD); + dhcp_option_byte(dhcp, DHCP_OPTION_STATIC_ROUTER); + dhcp_option_byte(dhcp, DHCP_OPTION_CLASSLESS_STATIC_ROUTER); + dhcp_option_byte(dhcp, DHCP_OPTION_VSN); + +#if LWIP_NETIF_HOSTNAME + if (netif->hostname != NULL) { + const char *p = (const char*)netif->hostname; + u8_t namelen = (u8_t)os_strlen(p); + if (namelen > 0) { + LWIP_ASSERT("DHCP: hostname is too long!", namelen < 255); + dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, namelen); + while (*p) { + dhcp_option_byte(dhcp, *p++); + } + } + } +#endif /* LWIP_NETIF_HOSTNAME */ + + dhcp_option_trailer(dhcp); + /* shrink the pbuf to the actual content length */ + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + /* send broadcast to any DHCP server */ + udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); + dhcp_delete_msg(dhcp); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_select: REQUESTING\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("dhcp_select: could not allocate DHCP request\n")); + } + dhcp->tries++; + msecs = (dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_select(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} + +/** + * The DHCP timer that checks for lease renewal/rebind timeouts. + */ +void ICACHE_FLASH_ATTR +dhcp_coarse_tmr() +{ + struct netif *netif = netif_list; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_coarse_tmr()\n")); + /* iterate through all network interfaces */ + while (netif != NULL) { + /* only act on DHCP configured interfaces */ + if (netif->dhcp != NULL) { + /* timer is active (non zero), and triggers (zeroes) now? */ + if (netif->dhcp->t2_timeout-- == 1) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t2 timeout\n")); + /* this clients' rebind timeout triggered */ + dhcp_t2_timeout(netif); + /* timer is active (non zero), and triggers (zeroes) now */ + } else if (netif->dhcp->t1_timeout-- == 1) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t1 timeout\n")); + /* this clients' renewal timeout triggered */ + dhcp_t1_timeout(netif); + } + } + /* proceed to next netif */ + netif = netif->next; + } +} + +/** + * DHCP transaction timeout handling + * + * A DHCP server is expected to respond within a short period of time. + * This timer checks whether an outstanding DHCP request is timed out. + */ +void ICACHE_FLASH_ATTR +dhcp_fine_tmr() +{ + struct netif *netif = netif_list; + /* loop through netif's */ + while (netif != NULL) { + /* only act on DHCP configured interfaces */ + if (netif->dhcp != NULL) { + /*add DHCP retries processing by LiuHan*/ + if (DHCP_MAXRTX != 0) { + if (netif->dhcp->tries >= DHCP_MAXRTX){ + os_printf("DHCP timeout\n"); + if (netif->dhcp_event != NULL) + netif->dhcp_event(); + break; + } + } + /* timer is active (non zero), and is about to trigger now */ + if (netif->dhcp->request_timeout > 1) { + netif->dhcp->request_timeout--; + } + else if (netif->dhcp->request_timeout == 1) { + netif->dhcp->request_timeout--; + /* { netif->dhcp->request_timeout == 0 } */ + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_fine_tmr(): request timeout\n")); + /* this client's request timeout triggered */ + dhcp_timeout(netif); + } + } + /* proceed to next network interface */ + netif = netif->next; + } + +} + +/** + * A DHCP negotiation transaction, or ARP request, has timed out. + * + * The timer that was started with the DHCP or ARP request has + * timed out, indicating no response was received in time. + * + * @param netif the netif under DHCP control + */ +static void ICACHE_FLASH_ATTR +dhcp_timeout(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_timeout()\n")); + /* back-off period has passed, or server selection timed out */ + if ((dhcp->state == DHCP_BACKING_OFF) || (dhcp->state == DHCP_SELECTING)) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_timeout(): restarting discovery\n")); + dhcp_discover(netif); + /* receiving the requested lease timed out */ + } else if (dhcp->state == DHCP_REQUESTING) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REQUESTING, DHCP request timed out\n")); + if (dhcp->tries <= 5) { + dhcp_select(netif); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REQUESTING, releasing, restarting\n")); + dhcp_release(netif); + dhcp_discover(netif); + } +#if DHCP_DOES_ARP_CHECK + /* received no ARP reply for the offered address (which is good) */ + } else if (dhcp->state == DHCP_CHECKING) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): CHECKING, ARP request timed out\n")); + if (dhcp->tries <= 1) { + dhcp_check(netif); + /* no ARP replies on the offered address, + looks like the IP address is indeed free */ + } else { + /* bind the interface to the offered address */ + dhcp_bind(netif); + } +#endif /* DHCP_DOES_ARP_CHECK */ + } + /* did not get response to renew request? */ + else if (dhcp->state == DHCP_RENEWING) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): RENEWING, DHCP request timed out\n")); + /* just retry renewal */ + /* note that the rebind timer will eventually time-out if renew does not work */ + dhcp_renew(netif); + /* did not get response to rebind request? */ + } else if (dhcp->state == DHCP_REBINDING) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REBINDING, DHCP request timed out\n")); + if (dhcp->tries <= 8) { + dhcp_rebind(netif); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): RELEASING, DISCOVERING\n")); + dhcp_release(netif); + dhcp_discover(netif); + } + } else if (dhcp->state == DHCP_REBOOTING) { + if (dhcp->tries < REBOOT_TRIES) { + dhcp_reboot(netif); + } else { + dhcp_discover(netif); + } + } +} + +/** + * The renewal period has timed out. + * + * @param netif the netif under DHCP control + */ +static void ICACHE_FLASH_ATTR +dhcp_t1_timeout(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_t1_timeout()\n")); + if ((dhcp->state == DHCP_REQUESTING) || (dhcp->state == DHCP_BOUND) || + (dhcp->state == DHCP_RENEWING)) { + /* just retry to renew - note that the rebind timer (t2) will + * eventually time-out if renew tries fail. */ + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("dhcp_t1_timeout(): must renew\n")); + /* This slightly different to RFC2131: DHCPREQUEST will be sent from state + DHCP_RENEWING, not DHCP_BOUND */ + dhcp_renew(netif); + } +} + +/** + * The rebind period has timed out. + * + * @param netif the netif under DHCP control + */ +static void ICACHE_FLASH_ATTR +dhcp_t2_timeout(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_t2_timeout()\n")); + if ((dhcp->state == DHCP_REQUESTING) || (dhcp->state == DHCP_BOUND) || + (dhcp->state == DHCP_RENEWING)) { + /* just retry to rebind */ + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("dhcp_t2_timeout(): must rebind\n")); + /* This slightly different to RFC2131: DHCPREQUEST will be sent from state + DHCP_REBINDING, not DHCP_BOUND */ + dhcp_rebind(netif); + } +} + +/** + * Handle a DHCP ACK packet + * + * @param netif the netif under DHCP control + */ +static void ICACHE_FLASH_ATTR +dhcp_handle_ack(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; +#if LWIP_DNS + u8_t n; +#endif /* LWIP_DNS */ + + /* clear options we might not get from the ACK */ + ip_addr_set_zero(&dhcp->offered_sn_mask); + ip_addr_set_zero(&dhcp->offered_gw_addr); +#if LWIP_DHCP_BOOTP_FILE + ip_addr_set_zero(&dhcp->offered_si_addr); +#endif /* LWIP_DHCP_BOOTP_FILE */ + + /* lease time given? */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_LEASE_TIME)) { + /* remember offered lease time */ + dhcp->offered_t0_lease = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_LEASE_TIME); + } + /* renewal period given? */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_T1)) { + /* remember given renewal period */ + dhcp->offered_t1_renew = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_T1); + } else { + /* calculate safe periods for renewal */ + dhcp->offered_t1_renew = dhcp->offered_t0_lease / 2; + } + + /* renewal period given? */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_T2)) { + /* remember given rebind period */ + dhcp->offered_t2_rebind = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_T2); + } else { + /* calculate safe periods for rebinding */ + dhcp->offered_t2_rebind = dhcp->offered_t0_lease; + } + + /* (y)our internet address */ + ip_addr_copy(dhcp->offered_ip_addr, dhcp->msg_in->yiaddr); + +#if LWIP_DHCP_BOOTP_FILE + /* copy boot server address, + boot file name copied in dhcp_parse_reply if not overloaded */ + ip_addr_copy(dhcp->offered_si_addr, dhcp->msg_in->siaddr); +#endif /* LWIP_DHCP_BOOTP_FILE */ + + /* subnet mask given? */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_SUBNET_MASK)) { + /* remember given subnet mask */ + ip4_addr_set_u32(&dhcp->offered_sn_mask, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_SUBNET_MASK))); + dhcp->subnet_mask_given = 1; + } else { + dhcp->subnet_mask_given = 0; + } + + /* gateway router */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_ROUTER)) { + ip4_addr_set_u32(&dhcp->offered_gw_addr, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_ROUTER))); + } + +#if LWIP_DNS + /* DNS servers */ + n = 0; + while(dhcp_option_given(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n) && (n < DNS_MAX_SERVERS)) { + ip_addr_t dns_addr; + ip4_addr_set_u32(&dns_addr, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n))); + dns_setserver(n, &dns_addr); + n++; + } +#endif /* LWIP_DNS */ +} + +/** Set a statically allocated struct dhcp to work with. + * Using this prevents dhcp_start to allocate it using mem_malloc. + * + * @param netif the netif for which to set the struct dhcp + * @param dhcp (uninitialised) dhcp struct allocated by the application + */ +void ICACHE_FLASH_ATTR +dhcp_set_struct(struct netif *netif, struct dhcp *dhcp) +{ + LWIP_ASSERT("netif != NULL", netif != NULL); + LWIP_ASSERT("dhcp != NULL", dhcp != NULL); + LWIP_ASSERT("netif already has a struct dhcp set", netif->dhcp == NULL); + + /* clear data structure */ + os_memset(dhcp, 0, sizeof(struct dhcp)); + /* dhcp_set_state(&dhcp, DHCP_OFF); */ + netif->dhcp = dhcp; +} + +/** Removes a struct dhcp from a netif. + * + * ATTENTION: Only use this when not using dhcp_set_struct() to allocate the + * struct dhcp since the memory is passed back to the heap. + * + * @param netif the netif from which to remove the struct dhcp + */ +void ICACHE_FLASH_ATTR dhcp_cleanup(struct netif *netif) +{ + LWIP_ASSERT("netif != NULL", netif != NULL); + + if (netif->dhcp != NULL) { + mem_free(netif->dhcp); + netif->dhcp = NULL; + } +} + +/** + * Start DHCP negotiation for a network interface. + * + * If no DHCP client instance was attached to this interface, + * a new client is created first. If a DHCP client instance + * was already present, it restarts negotiation. + * + * @param netif The lwIP network interface + * @return lwIP error code + * - ERR_OK - No error + * - ERR_MEM - Out of memory + */ +err_t ICACHE_FLASH_ATTR +dhcp_start(struct netif *netif) +{ + struct dhcp *dhcp; + err_t result = ERR_OK; + LWIP_ERROR("netif != NULL", (netif != NULL), return ERR_ARG;); + dhcp = netif->dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + /* Remove the flag that says this netif is handled by DHCP, + it is set when we succeeded starting. */ + netif->flags &= ~NETIF_FLAG_DHCP; + + /* check hwtype of the netif */ + if ((netif->flags & NETIF_FLAG_ETHARP) == 0) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): No ETHARP netif\n")); + return ERR_ARG; + } + + /* check MTU of the netif */ + if (netif->mtu < DHCP_MAX_MSG_LEN_MIN_REQUIRED) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): Cannot use this netif with DHCP: MTU is too small\n")); + return ERR_MEM; + } + + /* no DHCP client attached yet? */ + if (dhcp == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): starting new DHCP client\n")); + dhcp = (struct dhcp *)mem_malloc(sizeof(struct dhcp)); + if (dhcp == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): could not allocate dhcp\n")); + return ERR_MEM; + } + /* store this dhcp client in the netif */ + netif->dhcp = dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): allocated dhcp")); + /* already has DHCP client attached */ + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(): restarting DHCP configuration\n")); + if (dhcp->pcb != NULL) { + udp_remove(dhcp->pcb); + } + LWIP_ASSERT("pbuf p_out wasn't freed", dhcp->p_out == NULL); + LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL ); + } + + /* clear data structure */ + os_memset(dhcp, 0, sizeof(struct dhcp)); + /* dhcp_set_state(&dhcp, DHCP_OFF); */ + /* allocate UDP PCB */ + dhcp->pcb = udp_new(); + if (dhcp->pcb == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): could not obtain pcb\n")); + return ERR_MEM; + } + dhcp->pcb->so_options |= SOF_BROADCAST; + /* set up local and remote port for the pcb */ + udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT); + udp_connect(dhcp->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT); + /* set up the recv callback and argument */ + udp_recv(dhcp->pcb, dhcp_recv, netif); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): starting DHCP configuration\n")); + /* (re)start the DHCP negotiation */ + result = dhcp_discover(netif); + if (result != ERR_OK) { + /* free resources allocated above */ + dhcp_stop(netif); + return ERR_MEM; + } + /* Set the flag that says this netif is handled by DHCP. */ + netif->flags |= NETIF_FLAG_DHCP; + return result; +} + +/** + * Inform a DHCP server of our manual configuration. + * + * This informs DHCP servers of our fixed IP address configuration + * by sending an INFORM message. It does not involve DHCP address + * configuration, it is just here to be nice to the network. + * + * @param netif The lwIP network interface + */ +void ICACHE_FLASH_ATTR +dhcp_inform(struct netif *netif) +{ + struct dhcp dhcp; + err_t result = ERR_OK; + struct udp_pcb *pcb; + + LWIP_ERROR("netif != NULL", (netif != NULL), return;); + + os_memset(&dhcp, 0, sizeof(struct dhcp)); + dhcp_set_state(&dhcp, DHCP_INFORM); + + if ((netif->dhcp != NULL) && (netif->dhcp->pcb != NULL)) { + /* re-use existing pcb */ + pcb = netif->dhcp->pcb; + } else { + pcb = udp_new(); + if (pcb == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_inform(): could not obtain pcb")); + return; + } + dhcp.pcb = pcb; + dhcp.pcb->so_options |= SOF_BROADCAST; + udp_bind(dhcp.pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_inform(): created new udp pcb\n")); + } + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, &dhcp, DHCP_INFORM); + if (result == ERR_OK) { + dhcp_option(&dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(&dhcp, DHCP_MAX_MSG_LEN(netif)); + + dhcp_option_trailer(&dhcp); + + pbuf_realloc(dhcp.p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp.options_out_len); + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_inform: INFORMING\n")); + udp_sendto_if(pcb, dhcp.p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); + dhcp_delete_msg(&dhcp); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_inform: could not allocate DHCP request\n")); + } + + if (dhcp.pcb != NULL) { + /* otherwise, the existing pcb was used */ + udp_remove(dhcp.pcb); + } +} + +/** Handle a possible change in the network configuration. + * + * This enters the REBOOTING state to verify that the currently bound + * address is still valid. + */ +void ICACHE_FLASH_ATTR +dhcp_network_changed(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + if (!dhcp) + return; + switch (dhcp->state) { + case DHCP_REBINDING: + case DHCP_RENEWING: + case DHCP_BOUND: + case DHCP_REBOOTING: + netif_set_down(netif); + dhcp->tries = 0; + dhcp_reboot(netif); + break; + case DHCP_OFF: + /* stay off */ + break; + default: + dhcp->tries = 0; +#if LWIP_DHCP_AUTOIP_COOP + if(dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) { + autoip_stop(netif); + dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF; + } +#endif /* LWIP_DHCP_AUTOIP_COOP */ + dhcp_discover(netif); + break; + } +} + +#if DHCP_DOES_ARP_CHECK +/** + * Match an ARP reply with the offered IP address. + * + * @param netif the network interface on which the reply was received + * @param addr The IP address we received a reply from + */ +void ICACHE_FLASH_ATTR dhcp_arp_reply(struct netif *netif, ip_addr_t *addr) +{ + LWIP_ERROR("netif != NULL", (netif != NULL), return;); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_arp_reply()\n")); + /* is a DHCP client doing an ARP check? */ + if ((netif->dhcp != NULL) && (netif->dhcp->state == DHCP_CHECKING)) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_arp_reply(): CHECKING, arp reply for 0x%08"X32_F"\n", + ip4_addr_get_u32(addr))); + /* did a host respond with the address we + were offered by the DHCP server? */ + if (ip_addr_cmp(addr, &netif->dhcp->offered_ip_addr)) { + /* we will not accept the offered address */ + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING, + ("dhcp_arp_reply(): arp reply matched with offered address, declining\n")); + dhcp_decline(netif); + } + } +} + +/** + * Decline an offered lease. + * + * Tell the DHCP server we do not accept the offered address. + * One reason to decline the lease is when we find out the address + * is already in use by another host (through ARP). + * + * @param netif the netif under DHCP control + */ +static err_t ICACHE_FLASH_ATTR +dhcp_decline(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result = ERR_OK; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_decline()\n")); + dhcp_set_state(dhcp, DHCP_BACKING_OFF); + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, dhcp, DHCP_DECLINE); + if (result == ERR_OK) { + dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); + dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr))); + + dhcp_option_trailer(dhcp); + /* resize pbuf to reflect true size of options */ + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + /* per section 4.4.4, broadcast DECLINE messages */ + udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); + dhcp_delete_msg(dhcp); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_decline: BACKING OFF\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("dhcp_decline: could not allocate DHCP request\n")); + } + dhcp->tries++; + msecs = 10*1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_decline(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} +#endif /* DHCP_DOES_ARP_CHECK */ + + +/** + * Start the DHCP process, discover a DHCP server. + * + * @param netif the netif under DHCP control + */ +static err_t ICACHE_FLASH_ATTR +dhcp_discover(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result = ERR_OK; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover()\n")); + ip_addr_set_any(&dhcp->offered_ip_addr); + dhcp_set_state(dhcp, DHCP_SELECTING); + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, dhcp, DHCP_DISCOVER); + if (result == ERR_OK) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: making request\n")); + + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif)); + +#if LWIP_NETIF_HOSTNAME + if (netif->hostname != NULL) { + const char *p = (const char*)netif->hostname; + u8_t namelen = (u8_t)os_strlen(p); + if (namelen > 0) { + LWIP_ASSERT("DHCP: hostname is too long!", namelen < 255); + dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, namelen); + while (*p) { + dhcp_option_byte(dhcp, *p++); + } + } + } +#endif /* LWIP_NETIF_HOSTNAME */ + dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, 12/*num options*/); + dhcp_option_byte(dhcp, DHCP_OPTION_SUBNET_MASK); + dhcp_option_byte(dhcp, DHCP_OPTION_ROUTER); + dhcp_option_byte(dhcp, DHCP_OPTION_BROADCAST); + dhcp_option_byte(dhcp, DHCP_OPTION_DNS_SERVER); + dhcp_option_byte(dhcp, DHCP_OPTION_DOMAIN_NAME); + dhcp_option_byte(dhcp, DHCP_OPTION_NB_TINS); + dhcp_option_byte(dhcp, DHCP_OPTION_NB_TINT); + dhcp_option_byte(dhcp, DHCP_OPTION_NB_TIS); + dhcp_option_byte(dhcp, DHCP_OPTION_PRD); + dhcp_option_byte(dhcp, DHCP_OPTION_STATIC_ROUTER); + dhcp_option_byte(dhcp, DHCP_OPTION_CLASSLESS_STATIC_ROUTER); + dhcp_option_byte(dhcp, DHCP_OPTION_VSN); + + dhcp_option_trailer(dhcp); + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: realloc()ing\n")); + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: sendto(DISCOVER, IP_ADDR_BROADCAST, DHCP_SERVER_PORT)\n")); + udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: deleting()ing\n")); + dhcp_delete_msg(dhcp); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover: SELECTING\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_discover: could not allocate DHCP request\n")); + } + dhcp->tries++; +#if LWIP_DHCP_AUTOIP_COOP + if(dhcp->tries >= LWIP_DHCP_AUTOIP_COOP_TRIES && dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_OFF) { + dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_ON; + autoip_start(netif); + } +#endif /* LWIP_DHCP_AUTOIP_COOP */ + msecs = (dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} + + +/** + * Bind the interface to the offered IP address. + * + * @param netif network interface to bind to the offered address + */ +static void ICACHE_FLASH_ATTR +dhcp_bind(struct netif *netif) +{ + u32_t timeout; + struct dhcp *dhcp; + ip_addr_t sn_mask, gw_addr; + LWIP_ERROR("dhcp_bind: netif != NULL", (netif != NULL), return;); + dhcp = netif->dhcp; + LWIP_ERROR("dhcp_bind: dhcp != NULL", (dhcp != NULL), return;); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + + /* temporary DHCP lease? */ + if (dhcp->offered_t1_renew != 0xffffffffUL) { + /* set renewal period timer */ + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t1 renewal timer %"U32_F" secs\n", dhcp->offered_t1_renew)); + timeout = (dhcp->offered_t1_renew + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS; + if(timeout > 0xffff) { + timeout = 0xffff; + } + dhcp->t1_timeout = (u16_t)timeout; + if (dhcp->t1_timeout == 0) { + dhcp->t1_timeout = 1; + } + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t1_renew*1000)); + } + /* set renewal period timer */ + if (dhcp->offered_t2_rebind != 0xffffffffUL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t2 rebind timer %"U32_F" secs\n", dhcp->offered_t2_rebind)); + timeout = (dhcp->offered_t2_rebind + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS; + if(timeout > 0xffff) { + timeout = 0xffff; + } + dhcp->t2_timeout = (u16_t)timeout; + if (dhcp->t2_timeout == 0) { + dhcp->t2_timeout = 1; + } + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t2_rebind*1000)); + } + + /* If we have sub 1 minute lease, t2 and t1 will kick in at the same time. modify by ives at 2014.4.22*/ + if ((dhcp->t1_timeout >= dhcp->t2_timeout) && (dhcp->t2_timeout > 0)) { + dhcp->t1_timeout = 0; + } + + if (dhcp->subnet_mask_given) { + /* copy offered network mask */ + ip_addr_copy(sn_mask, dhcp->offered_sn_mask); + } else { + /* subnet mask not given, choose a safe subnet mask given the network class */ + u8_t first_octet = ip4_addr1(&dhcp->offered_ip_addr); + if (first_octet <= 127) { + ip4_addr_set_u32(&sn_mask, PP_HTONL(0xff000000)); + } else if (first_octet >= 192) { + ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffffff00)); + } else { + ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffff0000)); + } + } + + ip_addr_copy(gw_addr, dhcp->offered_gw_addr); + /* gateway address not given? */ + if (ip_addr_isany(&gw_addr)) { + /* copy network address */ + ip_addr_get_network(&gw_addr, &dhcp->offered_ip_addr, &sn_mask); + /* use first host address on network as gateway */ + ip4_addr_set_u32(&gw_addr, ip4_addr_get_u32(&gw_addr) | PP_HTONL(0x00000001)); + } + +#if LWIP_DHCP_AUTOIP_COOP + if(dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) { + autoip_stop(netif); + dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF; + } +#endif /* LWIP_DHCP_AUTOIP_COOP */ + + // wjg:back up old ip/netmask/gw + ip_addr_t ip, mask, gw; + ip = netif->ip_addr; + mask = netif->netmask; + gw = netif->gw; + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): IP: 0x%08"X32_F"\n", + ip4_addr_get_u32(&dhcp->offered_ip_addr))); + netif_set_ipaddr(netif, &dhcp->offered_ip_addr); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): SN: 0x%08"X32_F"\n", + ip4_addr_get_u32(&sn_mask))); + netif_set_netmask(netif, &sn_mask); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): GW: 0x%08"X32_F"\n", + ip4_addr_get_u32(&gw_addr))); + netif_set_gw(netif, &gw_addr); + + /* bring the interface up */ + netif_set_up(netif); + + // wjg: use old ip/mask/gw to check whether ip/mask/gw changed + system_station_got_ip_set(&ip, &mask, &gw); + + /* netif is now bound to DHCP leased address */ + dhcp_set_state(dhcp, DHCP_BOUND); +} + +/** + * Renew an existing DHCP lease at the involved DHCP server. + * + * @param netif network interface which must renew its lease + */ +err_t ICACHE_FLASH_ATTR +dhcp_renew(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_renew()\n")); + dhcp_set_state(dhcp, DHCP_RENEWING); + + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST); + if (result == ERR_OK) { + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif)); + +#if LWIP_NETIF_HOSTNAME + if (netif->hostname != NULL) { + const char *p = (const char*)netif->hostname; + u8_t namelen = (u8_t)os_strlen(p); + if (namelen > 0) { + LWIP_ASSERT("DHCP: hostname is too long!", namelen < 255); + dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, namelen); + while (*p) { + dhcp_option_byte(dhcp, *p++); + } + } + } +#endif /* LWIP_NETIF_HOSTNAME */ + +#if 0 + dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); + dhcp_option_long(dhcp, ntohl(dhcp->offered_ip_addr.addr)); +#endif + +#if 0 + dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4); + dhcp_option_long(dhcp, ntohl(dhcp->server_ip_addr.addr)); +#endif + /* append DHCP message trailer */ + dhcp_option_trailer(dhcp); + + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + udp_sendto_if(dhcp->pcb, dhcp->p_out, &dhcp->server_ip_addr, DHCP_SERVER_PORT, netif); + dhcp_delete_msg(dhcp); + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_renew: RENEWING\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_renew: could not allocate DHCP request\n")); + } + dhcp->tries++; + /* back-off on retries, but to a maximum of 20 seconds */ + msecs = dhcp->tries < 10 ? dhcp->tries * 2000 : 20 * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_renew(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} + +/** + * Rebind with a DHCP server for an existing DHCP lease. + * + * @param netif network interface which must rebind with a DHCP server + */ +static err_t ICACHE_FLASH_ATTR +dhcp_rebind(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind()\n")); + dhcp_set_state(dhcp, DHCP_REBINDING); + + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST); + if (result == ERR_OK) { + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif)); + +#if LWIP_NETIF_HOSTNAME + if (netif->hostname != NULL) { + const char *p = (const char*)netif->hostname; + u8_t namelen = (u8_t)os_strlen(p); + if (namelen > 0) { + LWIP_ASSERT("DHCP: hostname is too long!", namelen < 255); + dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, namelen); + while (*p) { + dhcp_option_byte(dhcp, *p++); + } + } + } +#endif /* LWIP_NETIF_HOSTNAME */ + +#if 0 + dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); + dhcp_option_long(dhcp, ntohl(dhcp->offered_ip_addr.addr)); + + dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4); + dhcp_option_long(dhcp, ntohl(dhcp->server_ip_addr.addr)); +#endif + + dhcp_option_trailer(dhcp); + + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + /* broadcast to server */ + udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); + dhcp_delete_msg(dhcp); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind: REBINDING\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_rebind: could not allocate DHCP request\n")); + } + dhcp->tries++; + msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} + +/** + * Enter REBOOTING state to verify an existing lease + * + * @param netif network interface which must reboot + */ +static err_t ICACHE_FLASH_ATTR +dhcp_reboot(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot()\n")); + dhcp_set_state(dhcp, DHCP_REBOOTING); + + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST); + if (result == ERR_OK) { + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(dhcp, 576); + + dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); + dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr))); + + dhcp_option_trailer(dhcp); + + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + /* broadcast to server */ + udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); + dhcp_delete_msg(dhcp); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot: REBOOTING\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_reboot: could not allocate DHCP request\n")); + } + dhcp->tries++; + msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} + + +/** + * Release a DHCP lease. + * + * @param netif network interface which must release its lease + */ +err_t ICACHE_FLASH_ATTR +dhcp_release(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_release()\n")); + if (dhcp == NULL) { + return ERR_ARG; + } + + /* idle DHCP client */ + dhcp_set_state(dhcp, DHCP_OFF); + /* clean old DHCP offer */ + ip_addr_set_zero(&dhcp->server_ip_addr); + ip_addr_set_zero(&dhcp->offered_ip_addr); + ip_addr_set_zero(&dhcp->offered_sn_mask); + ip_addr_set_zero(&dhcp->offered_gw_addr); +#if LWIP_DHCP_BOOTP_FILE + ip_addr_set_zero(&dhcp->offered_si_addr); +#endif /* LWIP_DHCP_BOOTP_FILE */ + dhcp->offered_t0_lease = dhcp->offered_t1_renew = dhcp->offered_t2_rebind = 0; + + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, dhcp, DHCP_RELEASE); + if (result == ERR_OK) { + dhcp_option_trailer(dhcp); + + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + udp_sendto_if(dhcp->pcb, dhcp->p_out, &dhcp->server_ip_addr, DHCP_SERVER_PORT, netif); + dhcp_delete_msg(dhcp); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_release: RELEASED, DHCP_OFF\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_release: could not allocate DHCP request\n")); + } + dhcp->tries++; + msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_release(): set request timeout %"U16_F" msecs\n", msecs)); + /* bring the interface down */ + netif_set_down(netif); + /* remove IP address from interface */ + netif_set_ipaddr(netif, IP_ADDR_ANY); + netif_set_gw(netif, IP_ADDR_ANY); + netif_set_netmask(netif, IP_ADDR_ANY); + + return result; +} + +/** + * Remove the DHCP client from the interface. + * + * @param netif The network interface to stop DHCP on + */ +void ICACHE_FLASH_ATTR +dhcp_stop(struct netif *netif) +{ + struct dhcp *dhcp; + LWIP_ERROR("dhcp_stop: netif != NULL", (netif != NULL), return;); + dhcp = netif->dhcp; + /* Remove the flag that says this netif is handled by DHCP. */ + netif->flags &= ~NETIF_FLAG_DHCP; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_stop()\n")); + /* netif is DHCP configured? */ + if (dhcp != NULL) { +#if LWIP_DHCP_AUTOIP_COOP + if(dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) { + autoip_stop(netif); + dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF; + } +#endif /* LWIP_DHCP_AUTOIP_COOP */ + + if (dhcp->pcb != NULL) { + udp_remove(dhcp->pcb); + dhcp->pcb = NULL; + } + LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL); + dhcp_set_state(dhcp, DHCP_OFF); + } +} + +/* + * Set the DHCP state of a DHCP client. + * + * If the state changed, reset the number of tries. + */ +static void ICACHE_FLASH_ATTR +dhcp_set_state(struct dhcp *dhcp, u8_t new_state) +{ + if (new_state != dhcp->state) { + dhcp->state = new_state; + dhcp->tries = 0; + dhcp->request_timeout = 0; + } +} + +/* + * Concatenate an option type and length field to the outgoing + * DHCP message. + * + */ +static void ICACHE_FLASH_ATTR +dhcp_option(struct dhcp *dhcp, u8_t option_type, u8_t option_len) +{ + LWIP_ASSERT("dhcp_option: dhcp->options_out_len + 2 + option_len <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 2U + option_len <= DHCP_OPTIONS_LEN); + dhcp->msg_out->options[dhcp->options_out_len++] = option_type; + dhcp->msg_out->options[dhcp->options_out_len++] = option_len; +} +/* + * Concatenate a single byte to the outgoing DHCP message. + * + */ +static void ICACHE_FLASH_ATTR +dhcp_option_byte(struct dhcp *dhcp, u8_t value) +{ + LWIP_ASSERT("dhcp_option_byte: dhcp->options_out_len < DHCP_OPTIONS_LEN", dhcp->options_out_len < DHCP_OPTIONS_LEN); + dhcp->msg_out->options[dhcp->options_out_len++] = value; +} + +static void ICACHE_FLASH_ATTR +dhcp_option_short(struct dhcp *dhcp, u16_t value) +{ + LWIP_ASSERT("dhcp_option_short: dhcp->options_out_len + 2 <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 2U <= DHCP_OPTIONS_LEN); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0xff00U) >> 8); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t) (value & 0x00ffU); +} + +static void ICACHE_FLASH_ATTR +dhcp_option_long(struct dhcp *dhcp, u32_t value) +{ + LWIP_ASSERT("dhcp_option_long: dhcp->options_out_len + 4 <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 4U <= DHCP_OPTIONS_LEN); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0xff000000UL) >> 24); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x00ff0000UL) >> 16); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x0000ff00UL) >> 8); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x000000ffUL)); +} + +/** + * Extract the DHCP message and the DHCP options. + * + * Extract the DHCP message and the DHCP options, each into a contiguous + * piece of memory. As a DHCP message is variable sized by its options, + * and also allows overriding some fields for options, the easy approach + * is to first unfold the options into a conitguous piece of memory, and + * use that further on. + * + */ +static err_t ICACHE_FLASH_ATTR +dhcp_parse_reply(struct dhcp *dhcp, struct pbuf *p) +{ + u8_t *options; + u16_t offset; + u16_t offset_max; + u16_t options_idx; + u16_t options_idx_max; + struct pbuf *q; + int parse_file_as_options = 0; + int parse_sname_as_options = 0; + + /* clear received options */ + dhcp_clear_all_options(dhcp); + /* check that beginning of dhcp_msg (up to and including chaddr) is in first pbuf */ + if (p->len < DHCP_SNAME_OFS) { + return ERR_BUF; + } + dhcp->msg_in = (struct dhcp_msg *)p->payload; +#if LWIP_DHCP_BOOTP_FILE + /* clear boot file name */ + dhcp->boot_file_name[0] = 0; +#endif /* LWIP_DHCP_BOOTP_FILE */ + + /* parse options */ + + /* start with options field */ + options_idx = DHCP_OPTIONS_OFS; + /* parse options to the end of the received packet */ + options_idx_max = p->tot_len; +again: + q = p; + while((q != NULL) && (options_idx >= q->len)) { + options_idx -= q->len; + options_idx_max -= q->len; + q = q->next; + } + if (q == NULL) { + return ERR_BUF; + } + offset = options_idx; + offset_max = options_idx_max; + options = (u8_t*)q->payload; + /* at least 1 byte to read and no end marker, then at least 3 bytes to read? */ + while((q != NULL) && (options[offset] != DHCP_OPTION_END) && (offset < offset_max)) { + u8_t op = options[offset]; + u8_t len; + u8_t decode_len = 0; + int decode_idx = -1; + u16_t val_offset = offset + 2; + /* len byte might be in the next pbuf */ + if (offset + 1 < q->len) { + len = options[offset + 1]; + } else { + len = (q->next != NULL ? ((u8_t*)q->next->payload)[0] : 0); + } + /* LWIP_DEBUGF(DHCP_DEBUG, ("msg_offset=%"U16_F", q->len=%"U16_F, msg_offset, q->len)); */ + decode_len = len; + switch(op) { + /* case(DHCP_OPTION_END): handled above */ + case(DHCP_OPTION_PAD): + /* special option: no len encoded */ + decode_len = len = 0; + /* will be increased below */ + offset--; + break; + case(DHCP_OPTION_SUBNET_MASK): + LWIP_ASSERT("len == 4", len == 4); + decode_idx = DHCP_OPTION_IDX_SUBNET_MASK; + break; + case(DHCP_OPTION_ROUTER): + decode_len = 4; /* only copy the first given router */ + LWIP_ASSERT("len >= decode_len", len >= decode_len); + decode_idx = DHCP_OPTION_IDX_ROUTER; + break; + case(DHCP_OPTION_DNS_SERVER): + /* special case: there might be more than one server */ + LWIP_ASSERT("len % 4 == 0", len % 4 == 0); + /* limit number of DNS servers */ + decode_len = LWIP_MIN(len, 4 * DNS_MAX_SERVERS); + LWIP_ASSERT("len >= decode_len", len >= decode_len); + decode_idx = DHCP_OPTION_IDX_DNS_SERVER; + break; + case(DHCP_OPTION_LEASE_TIME): + LWIP_ASSERT("len == 4", len == 4); + decode_idx = DHCP_OPTION_IDX_LEASE_TIME; + break; + case(DHCP_OPTION_OVERLOAD): + LWIP_ASSERT("len == 1", len == 1); + decode_idx = DHCP_OPTION_IDX_OVERLOAD; + break; + case(DHCP_OPTION_MESSAGE_TYPE): + LWIP_ASSERT("len == 1", len == 1); + decode_idx = DHCP_OPTION_IDX_MSG_TYPE; + break; + case(DHCP_OPTION_SERVER_ID): + LWIP_ASSERT("len == 4", len == 4); + decode_idx = DHCP_OPTION_IDX_SERVER_ID; + break; + case(DHCP_OPTION_T1): + LWIP_ASSERT("len == 4", len == 4); + decode_idx = DHCP_OPTION_IDX_T1; + break; + case(DHCP_OPTION_T2): + LWIP_ASSERT("len == 4", len == 4); + decode_idx = DHCP_OPTION_IDX_T2; + break; + default: + decode_len = 0; + LWIP_DEBUGF(DHCP_DEBUG, ("skipping option %"U16_F" in options\n", op)); + break; + } + offset += len + 2; + if (decode_len > 0) { + u32_t value = 0; + u16_t copy_len; +decode_next: + LWIP_ASSERT("check decode_idx", decode_idx >= 0 && decode_idx < DHCP_OPTION_IDX_MAX); + LWIP_ASSERT("option already decoded", !dhcp_option_given(dhcp, decode_idx)); + copy_len = LWIP_MIN(decode_len, 4); + pbuf_copy_partial(q, &value, copy_len, val_offset); + if (decode_len > 4) { + /* decode more than one u32_t */ + LWIP_ASSERT("decode_len % 4 == 0", decode_len % 4 == 0); + dhcp_got_option(dhcp, decode_idx); + dhcp_set_option_value(dhcp, decode_idx, htonl(value)); + decode_len -= 4; + val_offset += 4; + decode_idx++; + goto decode_next; + } else if (decode_len == 4) { + value = ntohl(value); + } else { + LWIP_ASSERT("invalid decode_len", decode_len == 1); + value = ((u8_t*)&value)[0]; + } + dhcp_got_option(dhcp, decode_idx); + dhcp_set_option_value(dhcp, decode_idx, value); + } + if (offset >= q->len) { + offset -= q->len; + offset_max -= q->len; + if ((offset < offset_max) && offset_max) { //modify by ives at 2014.4.22 + q = q->next; + LWIP_ASSERT("next pbuf was null", q); + options = (u8_t*)q->payload; + } else { + /* We've run out of bytes, probably no end marker. Don't proceed. */ + break; + } + } + } + /* is this an overloaded message? */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_OVERLOAD)) { + u32_t overload = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_OVERLOAD); + dhcp_clear_option(dhcp, DHCP_OPTION_IDX_OVERLOAD); + if (overload == DHCP_OVERLOAD_FILE) { + parse_file_as_options = 1; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded file field\n")); + } else if (overload == DHCP_OVERLOAD_SNAME) { + parse_sname_as_options = 1; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname field\n")); + } else if (overload == DHCP_OVERLOAD_SNAME_FILE) { + parse_sname_as_options = 1; + parse_file_as_options = 1; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname and file field\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("invalid overload option: %d\n", (int)overload)); + } +#if LWIP_DHCP_BOOTP_FILE + if (!parse_file_as_options) { + /* only do this for ACK messages */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE) && + (dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE) == DHCP_ACK)) + /* copy bootp file name, don't care for sname (server hostname) */ + pbuf_copy_partial(p, dhcp->boot_file_name, DHCP_FILE_LEN-1, DHCP_FILE_OFS); + /* make sure the string is really NULL-terminated */ + dhcp->boot_file_name[DHCP_FILE_LEN-1] = 0; + } +#endif /* LWIP_DHCP_BOOTP_FILE */ + } + if (parse_file_as_options) { + /* if both are overloaded, parse file first and then sname (RFC 2131 ch. 4.1) */ + parse_file_as_options = 0; + options_idx = DHCP_FILE_OFS; + options_idx_max = DHCP_FILE_OFS + DHCP_FILE_LEN; + goto again; + } else if (parse_sname_as_options) { + parse_sname_as_options = 0; + options_idx = DHCP_SNAME_OFS; + options_idx_max = DHCP_SNAME_OFS + DHCP_SNAME_LEN; + goto again; + } + return ERR_OK; +} + +/** + * If an incoming DHCP message is in response to us, then trigger the state machine + */ +static void ICACHE_FLASH_ATTR +dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port) +{ + struct netif *netif = (struct netif *)arg; + struct dhcp *dhcp = netif->dhcp; + struct dhcp_msg *reply_msg = (struct dhcp_msg *)p->payload; + u8_t msg_type; + u8_t i; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_recv(pbuf = %p) from DHCP server %"U16_F".%"U16_F".%"U16_F".%"U16_F" port %"U16_F"\n", (void*)p, + ip4_addr1_16(addr), ip4_addr2_16(addr), ip4_addr3_16(addr), ip4_addr4_16(addr), port)); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("pbuf->len = %"U16_F"\n", p->len)); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("pbuf->tot_len = %"U16_F"\n", p->tot_len)); + /* prevent warnings about unused arguments */ + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(addr); + LWIP_UNUSED_ARG(port); + + LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL); + + if (p->len < DHCP_MIN_REPLY_LEN) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCP reply message or pbuf too short\n")); + goto free_pbuf_and_return; + } + + if (reply_msg->op != DHCP_BOOTREPLY) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("not a DHCP reply message, but type %"U16_F"\n", (u16_t)reply_msg->op)); + goto free_pbuf_and_return; + } + /* iterate through hardware address and match against DHCP message */ + for (i = 0; i < netif->hwaddr_len; i++) { + if (netif->hwaddr[i] != reply_msg->chaddr[i]) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, + ("netif->hwaddr[%"U16_F"]==%02"X16_F" != reply_msg->chaddr[%"U16_F"]==%02"X16_F"\n", + (u16_t)i, (u16_t)netif->hwaddr[i], (u16_t)i, (u16_t)reply_msg->chaddr[i])); + goto free_pbuf_and_return; + } + } + /* match transaction ID against what we expected */ + if (ntohl(reply_msg->xid) != dhcp->xid) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, + ("transaction id mismatch reply_msg->xid(%"X32_F")!=dhcp->xid(%"X32_F")\n",ntohl(reply_msg->xid),dhcp->xid)); + goto free_pbuf_and_return; + } + /* option fields could be unfold? */ + if (dhcp_parse_reply(dhcp, p) != ERR_OK) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("problem unfolding DHCP message - too short on memory?\n")); + goto free_pbuf_and_return; + } + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("searching DHCP_OPTION_MESSAGE_TYPE\n")); + /* obtain pointer to DHCP message type */ + if (!dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE)) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCP_OPTION_MESSAGE_TYPE option not found\n")); + goto free_pbuf_and_return; + } + + /* read DHCP message type */ + msg_type = (u8_t)dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE); + /* message type is DHCP ACK? */ + if (msg_type == DHCP_ACK) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_ACK received\n")); + /* in requesting state? */ + if (dhcp->state == DHCP_REQUESTING) { + dhcp_handle_ack(netif); +#if DHCP_DOES_ARP_CHECK + /* check if the acknowledged lease address is already in use */ + dhcp_check(netif); +#else + /* bind interface to the acknowledged lease address */ + dhcp_bind(netif); +#endif + } + /* already bound to the given lease address? */ + else if ((dhcp->state == DHCP_REBOOTING) || (dhcp->state == DHCP_REBINDING) || (dhcp->state == DHCP_RENEWING)) { + dhcp_bind(netif); + } + } + /* received a DHCP_NAK in appropriate state? */ + else if ((msg_type == DHCP_NAK) && + ((dhcp->state == DHCP_REBOOTING) || (dhcp->state == DHCP_REQUESTING) || + (dhcp->state == DHCP_REBINDING) || (dhcp->state == DHCP_RENEWING ))) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_NAK received\n")); + dhcp_handle_nak(netif); + } + /* received a DHCP_OFFER in DHCP_SELECTING state? */ + else if ((msg_type == DHCP_OFFER) && (dhcp->state == DHCP_SELECTING)) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_OFFER received in DHCP_SELECTING state\n")); + dhcp->request_timeout = 0; + /* remember offered lease */ + dhcp_handle_offer(netif); + } +free_pbuf_and_return: + dhcp->msg_in = NULL; + pbuf_free(p); +} + +/** + * Create a DHCP request, fill in common headers + * + * @param netif the netif under DHCP control + * @param dhcp dhcp control struct + * @param message_type message type of the request + */ +static err_t ICACHE_FLASH_ATTR +dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type) +{ + u16_t i; +#ifndef DHCP_GLOBAL_XID + /** default global transaction identifier starting value (easy to match + * with a packet analyser). We simply increment for each new request. + * Predefine DHCP_GLOBAL_XID to a better value or a function call to generate one + * at runtime, any supporting function prototypes can be defined in DHCP_GLOBAL_XID_HEADER */ + static u32_t xid = 0xABCD0000; +#else + static u32_t xid; + static u8_t xid_initialised = 0; + if (!xid_initialised) { + xid = DHCP_GLOBAL_XID; + xid_initialised = !xid_initialised; + } +#endif + LWIP_ERROR("dhcp_create_msg: netif != NULL", (netif != NULL), return ERR_ARG;); + LWIP_ERROR("dhcp_create_msg: dhcp != NULL", (dhcp != NULL), return ERR_VAL;); + LWIP_ASSERT("dhcp_create_msg: dhcp->p_out == NULL", dhcp->p_out == NULL); + LWIP_ASSERT("dhcp_create_msg: dhcp->msg_out == NULL", dhcp->msg_out == NULL); + dhcp->p_out = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcp_msg), PBUF_RAM); + if (dhcp->p_out == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("dhcp_create_msg(): could not allocate pbuf\n")); + return ERR_MEM; + } + LWIP_ASSERT("dhcp_create_msg: check that first pbuf can hold struct dhcp_msg", + (dhcp->p_out->len >= sizeof(struct dhcp_msg))); + + /* DHCP_REQUEST should reuse 'xid' from DHCPOFFER modify by ives at 2014.4.22*/ + if (message_type != DHCP_REQUEST) { + /* reuse transaction identifier in retransmissions */ + if (dhcp->tries == 0) { + xid++; + } + dhcp->xid = xid; + } + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, + ("transaction id xid(%"X32_F")\n", xid)); + + dhcp->msg_out = (struct dhcp_msg *)dhcp->p_out->payload; + + dhcp->msg_out->op = DHCP_BOOTREQUEST; + /* TODO: make link layer independent */ + dhcp->msg_out->htype = DHCP_HTYPE_ETH; + dhcp->msg_out->hlen = netif->hwaddr_len; + dhcp->msg_out->hops = 0; + dhcp->msg_out->xid = htonl(dhcp->xid); + dhcp->msg_out->secs = 0; + /* we don't need the broadcast flag since we can receive unicast traffic + before being fully configured! */ + dhcp->msg_out->flags = 0; + ip_addr_set_zero(&dhcp->msg_out->ciaddr); + /* set ciaddr to netif->ip_addr based on message_type and state */ + if ((message_type == DHCP_INFORM) || (message_type == DHCP_DECLINE) || + ((message_type == DHCP_REQUEST) && /* DHCP_BOUND not used for sending! */ + ((dhcp->state==DHCP_RENEWING) || dhcp->state==DHCP_REBINDING))) { + ip_addr_copy(dhcp->msg_out->ciaddr, netif->ip_addr); + } + ip_addr_set_zero(&dhcp->msg_out->yiaddr); + ip_addr_set_zero(&dhcp->msg_out->siaddr); + ip_addr_set_zero(&dhcp->msg_out->giaddr); + for (i = 0; i < DHCP_CHADDR_LEN; i++) { + /* copy netif hardware address, pad with zeroes */ + dhcp->msg_out->chaddr[i] = (i < netif->hwaddr_len && i < NETIF_MAX_HWADDR_LEN) ? netif->hwaddr[i] : 0/* pad byte*/; + } + for (i = 0; i < DHCP_SNAME_LEN; i++) { + dhcp->msg_out->sname[i] = 0; + } + for (i = 0; i < DHCP_FILE_LEN; i++) { + dhcp->msg_out->file[i] = 0; + } + dhcp->msg_out->cookie = PP_HTONL(DHCP_MAGIC_COOKIE); + dhcp->options_out_len = 0; + /* fill options field with an incrementing array (for debugging purposes) */ + for (i = 0; i < DHCP_OPTIONS_LEN; i++) { + dhcp->msg_out->options[i] = (u8_t)i; /* for debugging only, no matter if truncated */ + } + /* Add option MESSAGE_TYPE */ + dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN); + dhcp_option_byte(dhcp, message_type); + return ERR_OK; +} + +/** + * Free previously allocated memory used to send a DHCP request. + * + * @param dhcp the dhcp struct to free the request from + */ +static void ICACHE_FLASH_ATTR +dhcp_delete_msg(struct dhcp *dhcp) +{ + LWIP_ERROR("dhcp_delete_msg: dhcp != NULL", (dhcp != NULL), return;); + LWIP_ASSERT("dhcp_delete_msg: dhcp->p_out != NULL", dhcp->p_out != NULL); + LWIP_ASSERT("dhcp_delete_msg: dhcp->msg_out != NULL", dhcp->msg_out != NULL); + if (dhcp->p_out != NULL) { + pbuf_free(dhcp->p_out); + } + dhcp->p_out = NULL; + dhcp->msg_out = NULL; +} + +/** + * Add a DHCP message trailer + * + * Adds the END option to the DHCP message, and if + * necessary, up to three padding bytes. + * + * @param dhcp DHCP state structure + */ +static void ICACHE_FLASH_ATTR +dhcp_option_trailer(struct dhcp *dhcp) +{ + LWIP_ERROR("dhcp_option_trailer: dhcp != NULL", (dhcp != NULL), return;); + LWIP_ASSERT("dhcp_option_trailer: dhcp->msg_out != NULL\n", dhcp->msg_out != NULL); + LWIP_ASSERT("dhcp_option_trailer: dhcp->options_out_len < DHCP_OPTIONS_LEN\n", dhcp->options_out_len < DHCP_OPTIONS_LEN); + dhcp->msg_out->options[dhcp->options_out_len++] = DHCP_OPTION_END; + /* packet is too small, or not 4 byte aligned? */ + while (((dhcp->options_out_len < DHCP_MIN_OPTIONS_LEN) || (dhcp->options_out_len & 3)) && + (dhcp->options_out_len < DHCP_OPTIONS_LEN)) { + /* LWIP_DEBUGF(DHCP_DEBUG,("dhcp_option_trailer:dhcp->options_out_len=%"U16_F", DHCP_OPTIONS_LEN=%"U16_F, dhcp->options_out_len, DHCP_OPTIONS_LEN)); */ + LWIP_ASSERT("dhcp_option_trailer: dhcp->options_out_len < DHCP_OPTIONS_LEN\n", dhcp->options_out_len < DHCP_OPTIONS_LEN); + /* add a fill/padding byte */ + dhcp->msg_out->options[dhcp->options_out_len++] = 0; + } +} + +#endif /* LWIP_DHCP */ diff --git a/variants/lwip/lwip/core/dns.c b/variants/lwip/lwip/core/dns.c new file mode 100755 index 000000000..4225d83f2 --- /dev/null +++ b/variants/lwip/lwip/core/dns.c @@ -0,0 +1,980 @@ +/** + * @file + * DNS - host name to IP address resolver. + * + */ + +/** + + * This file implements a DNS host name to IP address resolver. + + * Port to lwIP from uIP + * by Jim Pettinato April 2007 + + * uIP version Copyright (c) 2002-2003, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * DNS.C + * + * The lwIP DNS resolver functions are used to lookup a host name and + * map it to a numerical IP address. It maintains a list of resolved + * hostnames that can be queried with the dns_lookup() function. + * New hostnames can be resolved using the dns_query() function. + * + * The lwIP version of the resolver also adds a non-blocking version of + * gethostbyname() that will work with a raw API application. This function + * checks for an IP address string first and converts it if it is valid. + * gethostbyname() then does a dns_lookup() to see if the name is + * already in the table. If so, the IP is returned. If not, a query is + * issued and the function returns with a ERR_INPROGRESS status. The app + * using the dns client must then go into a waiting state. + * + * Once a hostname has been resolved (or found to be non-existent), + * the resolver code calls a specified callback function (which + * must be implemented by the module that uses the resolver). + */ + +/*----------------------------------------------------------------------------- + * RFC 1035 - Domain names - implementation and specification + * RFC 2181 - Clarifications to the DNS Specification + *----------------------------------------------------------------------------*/ + +/** @todo: define good default values (rfc compliance) */ +/** @todo: improve answer parsing, more checkings... */ +/** @todo: check RFC1035 - 7.3. Processing responses */ + +/*----------------------------------------------------------------------------- + * Includes + *----------------------------------------------------------------------------*/ + +#include "lwip/opt.h" + +#if LWIP_DNS /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/udp.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/dns.h" + +#include + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +/** DNS server IP address */ +#ifndef DNS_SERVER_ADDRESS +#define DNS_SERVER_ADDRESS(ipaddr) (ip4_addr_set_u32(ipaddr, 0xDEDE43D0)) /* resolver1.opendns.com(208.67.222.222) */ +#endif + +/** DNS server port address */ +#ifndef DNS_SERVER_PORT +#define DNS_SERVER_PORT 53 +#endif + +/** DNS maximum number of retries when asking for a name, before "timeout". */ +#ifndef DNS_MAX_RETRIES +#define DNS_MAX_RETRIES 4 +#endif + +/** DNS resource record max. TTL (one week as default) */ +#ifndef DNS_MAX_TTL +#define DNS_MAX_TTL 604800 +#endif + +/* DNS protocol flags */ +#define DNS_FLAG1_RESPONSE 0x80 +#define DNS_FLAG1_OPCODE_STATUS 0x10 +#define DNS_FLAG1_OPCODE_INVERSE 0x08 +#define DNS_FLAG1_OPCODE_STANDARD 0x00 +#define DNS_FLAG1_AUTHORATIVE 0x04 +#define DNS_FLAG1_TRUNC 0x02 +#define DNS_FLAG1_RD 0x01 +#define DNS_FLAG2_RA 0x80 +#define DNS_FLAG2_ERR_MASK 0x0f +#define DNS_FLAG2_ERR_NONE 0x00 +#define DNS_FLAG2_ERR_NAME 0x03 + +/* DNS protocol states */ +#define DNS_STATE_UNUSED 0 +#define DNS_STATE_NEW 1 +#define DNS_STATE_ASKING 2 +#define DNS_STATE_DONE 3 + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** DNS message header */ +struct dns_hdr { + PACK_STRUCT_FIELD(u16_t id); + PACK_STRUCT_FIELD(u8_t flags1); + PACK_STRUCT_FIELD(u8_t flags2); + PACK_STRUCT_FIELD(u16_t numquestions); + PACK_STRUCT_FIELD(u16_t numanswers); + PACK_STRUCT_FIELD(u16_t numauthrr); + PACK_STRUCT_FIELD(u16_t numextrarr); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif +#define SIZEOF_DNS_HDR 12 + +/** DNS query message structure. + No packing needed: only used locally on the stack. */ +struct dns_query { + /* DNS query record starts with either a domain name or a pointer + to a name already present somewhere in the packet. */ + u16_t type; + u16_t cls; +}; +#define SIZEOF_DNS_QUERY 4 + +/** DNS answer message structure. + No packing needed: only used locally on the stack. */ +struct dns_answer { + /* DNS answer record starts with either a domain name or a pointer + to a name already present somewhere in the packet. */ + u16_t type; + u16_t cls; + u32_t ttl; + u16_t len; +}; +#define SIZEOF_DNS_ANSWER 10 + +/** DNS table entry */ +struct dns_table_entry { + u8_t state; + u8_t numdns; + u8_t tmr; + u8_t retries; + u8_t seqno; + u8_t err; + u32_t ttl; + char name[DNS_MAX_NAME_LENGTH]; + ip_addr_t ipaddr; + /* pointer to callback on DNS query done */ + dns_found_callback found; + void *arg; +}; + +#if DNS_LOCAL_HOSTLIST + +#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC +/** Local host-list. For hostnames in this list, no + * external name resolution is performed */ +static struct local_hostlist_entry *local_hostlist_dynamic; +#else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + +/** Defining this allows the local_hostlist_static to be placed in a different + * linker section (e.g. FLASH) */ +#ifndef DNS_LOCAL_HOSTLIST_STORAGE_PRE +#define DNS_LOCAL_HOSTLIST_STORAGE_PRE static +#endif /* DNS_LOCAL_HOSTLIST_STORAGE_PRE */ +/** Defining this allows the local_hostlist_static to be placed in a different + * linker section (e.g. FLASH) */ +#ifndef DNS_LOCAL_HOSTLIST_STORAGE_POST +#define DNS_LOCAL_HOSTLIST_STORAGE_POST +#endif /* DNS_LOCAL_HOSTLIST_STORAGE_POST */ +DNS_LOCAL_HOSTLIST_STORAGE_PRE struct local_hostlist_entry local_hostlist_static[] + DNS_LOCAL_HOSTLIST_STORAGE_POST = DNS_LOCAL_HOSTLIST_INIT; + +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + +static void dns_init_local(); +#endif /* DNS_LOCAL_HOSTLIST */ + + +/* forward declarations */ +static void dns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port); +static void dns_check_entries(void); + +/*----------------------------------------------------------------------------- + * Globales + *----------------------------------------------------------------------------*/ + +/* DNS variables */ +static struct udp_pcb *dns_pcb; +static u8_t dns_seqno; +static struct dns_table_entry dns_table[DNS_TABLE_SIZE]; +static ip_addr_t dns_servers[DNS_MAX_SERVERS]; +/** Contiguous buffer for processing responses */ +//static u8_t dns_payload_buffer[LWIP_MEM_ALIGN_BUFFER(DNS_MSG_SIZE)]; +static u8_t* dns_payload; +static u8_t dns_random; +/** + * Initialize the resolver: set up the UDP pcb and configure the default server + * (DNS_SERVER_ADDRESS). + */ +void ICACHE_FLASH_ATTR +dns_init() +{ + ip_addr_t dnsserver; + +// dns_payload = (u8_t *)LWIP_MEM_ALIGN(dns_payload_buffer); + + /* initialize default DNS server address */ + DNS_SERVER_ADDRESS(&dnsserver); + + LWIP_DEBUGF(DNS_DEBUG, ("dns_init: initializing\n")); + + /* if dns client not yet initialized... */ + if (dns_pcb == NULL) { + dns_pcb = udp_new(); + + if (dns_pcb != NULL) { + /* initialize DNS table not needed (initialized to zero since it is a + * global variable) */ + LWIP_ASSERT("For implicit initialization to work, DNS_STATE_UNUSED needs to be 0", + DNS_STATE_UNUSED == 0); + + /* initialize DNS client */ + udp_bind(dns_pcb, IP_ADDR_ANY, 0); + udp_recv(dns_pcb, dns_recv, NULL); + + /* initialize default DNS primary server */ + dns_setserver(0, &dnsserver); + } + } +#if DNS_LOCAL_HOSTLIST + dns_init_local(); +#endif +} + +/** + * Initialize one of the DNS servers. + * + * @param numdns the index of the DNS server to set must be < DNS_MAX_SERVERS + * @param dnsserver IP address of the DNS server to set + */ +void ICACHE_FLASH_ATTR +dns_setserver(u8_t numdns, ip_addr_t *dnsserver) +{ + if ((numdns < DNS_MAX_SERVERS) && (dns_pcb != NULL) && + (dnsserver != NULL) && !ip_addr_isany(dnsserver)) { + dns_servers[numdns] = (*dnsserver); + } +} + +/** + * Obtain one of the currently configured DNS server. + * + * @param numdns the index of the DNS server + * @return IP address of the indexed DNS server or "ip_addr_any" if the DNS + * server has not been configured. + */ +ip_addr_t ICACHE_FLASH_ATTR +dns_getserver(u8_t numdns) +{ + if (numdns < DNS_MAX_SERVERS) { + return dns_servers[numdns]; + } else { + return *IP_ADDR_ANY; + } +} + +/** + * The DNS resolver client timer - handle retries and timeouts and should + * be called every DNS_TMR_INTERVAL milliseconds (every second by default). + */ +void ICACHE_FLASH_ATTR +dns_tmr(void) +{ + if (dns_pcb != NULL) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_tmr: dns_check_entries\n")); + dns_check_entries(); + } +} + +#if DNS_LOCAL_HOSTLIST +static void ICACHE_FLASH_ATTR +dns_init_local() +{ +#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT) + int i; + struct local_hostlist_entry *entry; + /* Dynamic: copy entries from DNS_LOCAL_HOSTLIST_INIT to list */ + struct local_hostlist_entry local_hostlist_init[] = DNS_LOCAL_HOSTLIST_INIT; + size_t namelen; + for (i = 0; i < sizeof(local_hostlist_init) / sizeof(struct local_hostlist_entry); i++) { + struct local_hostlist_entry *init_entry = &local_hostlist_init[i]; + LWIP_ASSERT("invalid host name (NULL)", init_entry->name != NULL); + namelen = os_strlen(init_entry->name); + LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN); + entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST); + LWIP_ASSERT("mem-error in dns_init_local", entry != NULL); + if (entry != NULL) { + entry->name = (char*)entry + sizeof(struct local_hostlist_entry); + MEMCPY((char*)entry->name, init_entry->name, namelen); + ((char*)entry->name)[namelen] = 0; + entry->addr = init_entry->addr; + entry->next = local_hostlist_dynamic; + local_hostlist_dynamic = entry; + } + } +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT) */ +} + +/** + * Scans the local host-list for a hostname. + * + * @param hostname Hostname to look for in the local host-list + * @return The first IP address for the hostname in the local host-list or + * IPADDR_NONE if not found. + */ +static u32_t ICACHE_FLASH_ATTR +dns_lookup_local(const char *hostname) +{ +#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC + struct local_hostlist_entry *entry = local_hostlist_dynamic; + while(entry != NULL) { + if(strcmp(entry->name, hostname) == 0) { + return ip4_addr_get_u32(&entry->addr); + } + entry = entry->next; + } +#else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + int i; + for (i = 0; i < sizeof(local_hostlist_static) / sizeof(struct local_hostlist_entry); i++) { + if(strcmp(local_hostlist_static[i].name, hostname) == 0) { + return ip4_addr_get_u32(&local_hostlist_static[i].addr); + } + } +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + return IPADDR_NONE; +} + +#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC +/** Remove all entries from the local host-list for a specific hostname + * and/or IP addess + * + * @param hostname hostname for which entries shall be removed from the local + * host-list + * @param addr address for which entries shall be removed from the local host-list + * @return the number of removed entries + */ +int ICACHE_FLASH_ATTR +dns_local_removehost(const char *hostname, const ip_addr_t *addr) +{ + int removed = 0; + struct local_hostlist_entry *entry = local_hostlist_dynamic; + struct local_hostlist_entry *last_entry = NULL; + while (entry != NULL) { + if (((hostname == NULL) || !strcmp(entry->name, hostname)) && + ((addr == NULL) || ip_addr_cmp(&entry->addr, addr))) { + struct local_hostlist_entry *free_entry; + if (last_entry != NULL) { + last_entry->next = entry->next; + } else { + local_hostlist_dynamic = entry->next; + } + free_entry = entry; + entry = entry->next; + memp_free(MEMP_LOCALHOSTLIST, free_entry); + removed++; + } else { + last_entry = entry; + entry = entry->next; + } + } + return removed; +} + +/** + * Add a hostname/IP address pair to the local host-list. + * Duplicates are not checked. + * + * @param hostname hostname of the new entry + * @param addr IP address of the new entry + * @return ERR_OK if succeeded or ERR_MEM on memory error + */ +err_t ICACHE_FLASH_ATTR +dns_local_addhost(const char *hostname, const ip_addr_t *addr) +{ + struct local_hostlist_entry *entry; + size_t namelen; + LWIP_ASSERT("invalid host name (NULL)", hostname != NULL); + namelen = os_strlen(hostname); + LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN); + entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST); + if (entry == NULL) { + return ERR_MEM; + } + entry->name = (char*)entry + sizeof(struct local_hostlist_entry); + MEMCPY((char*)entry->name, hostname, namelen); + ((char*)entry->name)[namelen] = 0; + ip_addr_copy(entry->addr, *addr); + entry->next = local_hostlist_dynamic; + local_hostlist_dynamic = entry; + return ERR_OK; +} +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC*/ +#endif /* DNS_LOCAL_HOSTLIST */ + +/** + * Look up a hostname in the array of known hostnames. + * + * @note This function only looks in the internal array of known + * hostnames, it does not send out a query for the hostname if none + * was found. The function dns_enqueue() can be used to send a query + * for a hostname. + * + * @param name the hostname to look up + * @return the hostname's IP address, as u32_t (instead of ip_addr_t to + * better check for failure: != IPADDR_NONE) or IPADDR_NONE if the hostname + * was not found in the cached dns_table. + */ +/* +static u32_t ICACHE_FLASH_ATTR +dns_lookup(const char *name) +{ + u8_t i; +#if DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN) + u32_t addr; +#endif +#if DNS_LOCAL_HOSTLIST + if ((addr = dns_lookup_local(name)) != IPADDR_NONE) { + return addr; + } +#endif +#ifdef DNS_LOOKUP_LOCAL_EXTERN + if((addr = DNS_LOOKUP_LOCAL_EXTERN(name)) != IPADDR_NONE) { + return addr; + } +#endif + + // Walk through name list, return entry if found. If not, return NULL. + for (i = 0; i < DNS_TABLE_SIZE; ++i) { + if ((dns_table[i].state == DNS_STATE_DONE) && + (strcmp(name, dns_table[i].name) == 0)) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_lookup: \"%s\": found = ", name)); + ip_addr_debug_print(DNS_DEBUG, &(dns_table[i].ipaddr)); + LWIP_DEBUGF(DNS_DEBUG, ("\n")); + return ip4_addr_get_u32(&dns_table[i].ipaddr); + } + } + + return IPADDR_NONE; +} +*/ +#if DNS_DOES_NAME_CHECK +/** + * Compare the "dotted" name "query" with the encoded name "response" + * to make sure an answer from the DNS server matches the current dns_table + * entry (otherwise, answers might arrive late for hostname not on the list + * any more). + * + * @param query hostname (not encoded) from the dns_table + * @param response encoded hostname in the DNS response + * @return 0: names equal; 1: names differ + */ +static u8_t ICACHE_FLASH_ATTR +dns_compare_name(unsigned char *query, unsigned char *response) +{ + unsigned char n; + + do { + n = *response++; + /** @see RFC 1035 - 4.1.4. Message compression */ + if ((n & 0xc0) == 0xc0) { + /* Compressed name */ + break; + } else { + /* Not compressed name */ + while (n > 0) { + if ((*query) != (*response)) { + return 1; + } + ++response; + ++query; + --n; + }; + ++query; + } + } while (*response != 0); + + return 0; +} +#endif /* DNS_DOES_NAME_CHECK */ + +/** + * Walk through a compact encoded DNS name and return the end of the name. + * + * @param query encoded DNS name in the DNS server response + * @return end of the name + */ +static unsigned char * ICACHE_FLASH_ATTR +dns_parse_name(unsigned char *query) +{ + unsigned char n; + + do { + n = *query++; + /** @see RFC 1035 - 4.1.4. Message compression */ + if ((n & 0xc0) == 0xc0) { + /* Compressed name */ + break; + } else { + /* Not compressed name */ + while (n > 0) { + ++query; + --n; + }; + } + } while (*query != 0); + + return query + 1; +} + +/** + * Send a DNS query packet. + * + * @param numdns index of the DNS server in the dns_servers table + * @param name hostname to query + * @param id index of the hostname in dns_table, used as transaction ID in the + * DNS query packet + * @return ERR_OK if packet is sent; an err_t indicating the problem otherwise + */ +static err_t ICACHE_FLASH_ATTR +dns_send(u8_t numdns, const char* name, u8_t id) +{ + err_t err; + struct dns_hdr *hdr; + struct dns_query qry; + struct pbuf *p; + char *query, *nptr; + const char *pHostname; + u8_t n; + dns_random = os_random()%250; + LWIP_DEBUGF(DNS_DEBUG, ("dns_send: dns_servers[%"U16_F"] \"%s\": request\n", + (u16_t)(numdns), name)); + LWIP_ASSERT("dns server out of array", numdns < DNS_MAX_SERVERS); + LWIP_ASSERT("dns server has no IP address set", !ip_addr_isany(&dns_servers[numdns])); + + /* if here, we have either a new query or a retry on a previous query to process */ + p = pbuf_alloc(PBUF_TRANSPORT, SIZEOF_DNS_HDR + DNS_MAX_NAME_LENGTH + + SIZEOF_DNS_QUERY, PBUF_RAM); + if (p != NULL) { + LWIP_ASSERT("pbuf must be in one piece", p->next == NULL); + /* fill dns header */ + hdr = (struct dns_hdr*)p->payload; + os_memset(hdr, 0, SIZEOF_DNS_HDR); + hdr->id = htons(id + dns_random); + hdr->flags1 = DNS_FLAG1_RD; + hdr->numquestions = PP_HTONS(1); + query = (char*)hdr + SIZEOF_DNS_HDR; + pHostname = name; + --pHostname; + + /* convert hostname into suitable query format. */ + do { + ++pHostname; + nptr = query; + ++query; + for(n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) { + *query = *pHostname; + ++query; + ++n; + } + *nptr = n; + } while(*pHostname != 0); + *query++='\0'; + + /* fill dns query */ + qry.type = PP_HTONS(DNS_RRTYPE_A); + qry.cls = PP_HTONS(DNS_RRCLASS_IN); + SMEMCPY(query, &qry, SIZEOF_DNS_QUERY); + + /* resize pbuf to the exact dns query */ + pbuf_realloc(p, (u16_t)((query + SIZEOF_DNS_QUERY) - ((char*)(p->payload)))); + + /* connect to the server for faster receiving */ + udp_connect(dns_pcb, &dns_servers[numdns], DNS_SERVER_PORT); + /* send dns packet */ + err = udp_sendto(dns_pcb, p, &dns_servers[numdns], DNS_SERVER_PORT); + + /* free pbuf */ + pbuf_free(p); + } else { + err = ERR_MEM; + } + + return err; +} + +/** + * dns_check_entry() - see if pEntry has not yet been queried and, if so, sends out a query. + * Check an entry in the dns_table: + * - send out query for new entries + * - retry old pending entries on timeout (also with different servers) + * - remove completed entries from the table if their TTL has expired + * + * @param i index of the dns_table entry to check + */ +static void ICACHE_FLASH_ATTR +dns_check_entry(u8_t i) +{ + err_t err; + struct dns_table_entry *pEntry = &dns_table[i]; + + LWIP_ASSERT("array index out of bounds", i < DNS_TABLE_SIZE); + + switch(pEntry->state) { + + case DNS_STATE_NEW: { + /* initialize new entry */ + pEntry->state = DNS_STATE_ASKING; + pEntry->numdns = 0; + pEntry->tmr = 1; + pEntry->retries = 0; + + /* send DNS packet for this entry */ + err = dns_send(pEntry->numdns, pEntry->name, i); + if (err != ERR_OK) { + LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING, + ("dns_send returned error: %s\n", lwip_strerr(err))); + } + break; + } + + case DNS_STATE_ASKING: { + if (--pEntry->tmr == 0) { + if (++pEntry->retries == DNS_MAX_RETRIES) { + if ((pEntry->numdns+1numdns+1])) { + /* change of server */ + pEntry->numdns++; + pEntry->tmr = 1; + pEntry->retries = 0; + break; + } else { + LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": timeout\n", pEntry->name)); + /* call specified callback function if provided */ + if (pEntry->found) + (*pEntry->found)(pEntry->name, NULL, pEntry->arg); + /* flush this entry */ + pEntry->state = DNS_STATE_UNUSED; + pEntry->found = NULL; + break; + } + } + + /* wait longer for the next retry */ + pEntry->tmr = pEntry->retries; + + /* send DNS packet for this entry */ + err = dns_send(pEntry->numdns, pEntry->name, i); + if (err != ERR_OK) { + LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING, + ("dns_send returned error: %s\n", lwip_strerr(err))); + } + } + break; + } + + case DNS_STATE_DONE: { + /* if the time to live is nul */ + if (--pEntry->ttl == 0) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": flush\n", pEntry->name)); + /* flush this entry */ + pEntry->state = DNS_STATE_UNUSED; + pEntry->found = NULL; + } + break; + } + case DNS_STATE_UNUSED: + /* nothing to do */ + break; + default: + LWIP_ASSERT("unknown dns_table entry state:", 0); + break; + } +} + +/** + * Call dns_check_entry for each entry in dns_table - check all entries. + */ +static void ICACHE_FLASH_ATTR +dns_check_entries(void) +{ + u8_t i; + + for (i = 0; i < DNS_TABLE_SIZE; ++i) { + dns_check_entry(i); + } +} + +/** + * Receive input function for DNS response packets arriving for the dns UDP pcb. + * + * @params see udp.h + */ +static void ICACHE_FLASH_ATTR +dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port) +{ + u16_t i; + char *pHostname; + struct dns_hdr *hdr; + struct dns_answer ans; + struct dns_table_entry *pEntry; + u16_t nquestions, nanswers; + + u8_t* dns_payload_buffer = (u8_t* )os_zalloc(LWIP_MEM_ALIGN_BUFFER(DNS_MSG_SIZE)); + dns_payload = (u8_t *)LWIP_MEM_ALIGN(dns_payload_buffer); + + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(addr); + LWIP_UNUSED_ARG(port); + + /* is the dns message too big ? */ + if (p->tot_len > DNS_MSG_SIZE) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too big\n")); + /* free pbuf and return */ + goto memerr; + } + + /* is the dns message big enough ? */ + if (p->tot_len < (SIZEOF_DNS_HDR + SIZEOF_DNS_QUERY + SIZEOF_DNS_ANSWER)) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too small\n")); + /* free pbuf and return */ + goto memerr; + } + + /* copy dns payload inside static buffer for processing */ + if (pbuf_copy_partial(p, dns_payload, p->tot_len, 0) == p->tot_len) { + /* The ID in the DNS header should be our entry into the name table. */ + hdr = (struct dns_hdr*)dns_payload; + i = htons(hdr->id); + i = i - dns_random; + if (i < DNS_TABLE_SIZE) { + pEntry = &dns_table[i]; + if(pEntry->state == DNS_STATE_ASKING) { + /* This entry is now completed. */ + pEntry->state = DNS_STATE_DONE; + pEntry->err = hdr->flags2 & DNS_FLAG2_ERR_MASK; + + /* We only care about the question(s) and the answers. The authrr + and the extrarr are simply discarded. */ + nquestions = htons(hdr->numquestions); + nanswers = htons(hdr->numanswers); + + /* Check for error. If so, call callback to inform. */ + if (((hdr->flags1 & DNS_FLAG1_RESPONSE) == 0) || (pEntry->err != 0) || (nquestions != 1)) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in flags\n", pEntry->name)); + /* call callback to indicate error, clean up memory and return */ + goto responseerr; + } + +#if DNS_DOES_NAME_CHECK + /* Check if the name in the "question" part match with the name in the entry. */ + if (dns_compare_name((unsigned char *)(pEntry->name), (unsigned char *)dns_payload + SIZEOF_DNS_HDR) != 0) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", pEntry->name)); + /* call callback to indicate error, clean up memory and return */ + goto responseerr; + } +#endif /* DNS_DOES_NAME_CHECK */ + + /* Skip the name in the "question" part */ + pHostname = (char *) dns_parse_name((unsigned char *)dns_payload + SIZEOF_DNS_HDR) + SIZEOF_DNS_QUERY; + + while (nanswers > 0) { + /* skip answer resource record's host name */ + pHostname = (char *) dns_parse_name((unsigned char *)pHostname); + + /* Check for IP address type and Internet class. Others are discarded. */ + SMEMCPY(&ans, pHostname, SIZEOF_DNS_ANSWER); + if((ans.type == PP_HTONS(DNS_RRTYPE_A)) && (ans.cls == PP_HTONS(DNS_RRCLASS_IN)) && + (ans.len == PP_HTONS(sizeof(ip_addr_t))) ) { + /* read the answer resource record's TTL, and maximize it if needed */ + pEntry->ttl = ntohl(ans.ttl); + if (pEntry->ttl > DNS_MAX_TTL) { + pEntry->ttl = DNS_MAX_TTL; + } + /* read the IP address after answer resource record's header */ + SMEMCPY(&(pEntry->ipaddr), (pHostname+SIZEOF_DNS_ANSWER), sizeof(ip_addr_t)); + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response = ", pEntry->name)); + ip_addr_debug_print(DNS_DEBUG, (&(pEntry->ipaddr))); + LWIP_DEBUGF(DNS_DEBUG, ("\n")); + /* call specified callback function if provided */ + if (pEntry->found) { + (*pEntry->found)(pEntry->name, &pEntry->ipaddr, pEntry->arg); + } + /* deallocate memory and return */ + goto memerr; + } else { + pHostname = pHostname + SIZEOF_DNS_ANSWER + htons(ans.len); + } + --nanswers; + } + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in response\n", pEntry->name)); + /* call callback to indicate error, clean up memory and return */ + goto responseerr; + } + } + } + + /* deallocate memory and return */ + goto memerr; + +responseerr: + /* ERROR: call specified callback function with NULL as name to indicate an error */ + if (pEntry->found) { + (*pEntry->found)(pEntry->name, NULL, pEntry->arg); + } + /* flush this entry */ + pEntry->state = DNS_STATE_UNUSED; + pEntry->found = NULL; + +memerr: + /* free pbuf */ + pbuf_free(p); + os_free(dns_payload_buffer); + return; +} + +/** + * Queues a new hostname to resolve and sends out a DNS query for that hostname + * + * @param name the hostname that is to be queried + * @param found a callback founction to be called on success, failure or timeout + * @param callback_arg argument to pass to the callback function + * @return @return a err_t return code. + */ +static err_t ICACHE_FLASH_ATTR +dns_enqueue(const char *name, dns_found_callback found, void *callback_arg) +{ + u8_t i; + u8_t lseq, lseqi; + struct dns_table_entry *pEntry = NULL; + size_t namelen; + + /* search an unused entry, or the oldest one */ + lseq = lseqi = 0; + for (i = 0; i < DNS_TABLE_SIZE; ++i) { + pEntry = &dns_table[i]; + /* is it an unused entry ? */ + if (pEntry->state == DNS_STATE_UNUSED) + break; + + /* check if this is the oldest completed entry */ + if (pEntry->state == DNS_STATE_DONE) { + if ((dns_seqno - pEntry->seqno) > lseq) { + lseq = dns_seqno - pEntry->seqno; + lseqi = i; + } + } + } + + /* if we don't have found an unused entry, use the oldest completed one */ + if (i == DNS_TABLE_SIZE) { + if ((lseqi >= DNS_TABLE_SIZE) || (dns_table[lseqi].state != DNS_STATE_DONE)) { + /* no entry can't be used now, table is full */ + LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS entries table is full\n", name)); + return ERR_MEM; + } else { + /* use the oldest completed one */ + i = lseqi; + pEntry = &dns_table[i]; + } + } + + /* use this entry */ + LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS entry %"U16_F"\n", name, (u16_t)(i))); + + /* fill the entry */ + pEntry->state = DNS_STATE_NEW; + pEntry->seqno = dns_seqno++; + pEntry->found = found; + pEntry->arg = callback_arg; + namelen = LWIP_MIN(os_strlen(name), DNS_MAX_NAME_LENGTH-1); + MEMCPY(pEntry->name, name, namelen); + pEntry->name[namelen] = 0; + + /* force to send query without waiting timer */ + dns_check_entry(i); + + /* dns query is enqueued */ + return ERR_INPROGRESS; +} + +/** + * Resolve a hostname (string) into an IP address. + * NON-BLOCKING callback version for use with raw API!!! + * + * Returns immediately with one of err_t return codes: + * - ERR_OK if hostname is a valid IP address string or the host + * name is already in the local names table. + * - ERR_INPROGRESS enqueue a request to be sent to the DNS server + * for resolution if no errors are present. + * - ERR_ARG: dns client not initialized or invalid hostname + * + * @param hostname the hostname that is to be queried + * @param addr pointer to a ip_addr_t where to store the address if it is already + * cached in the dns_table (only valid if ERR_OK is returned!) + * @param found a callback function to be called on success, failure or timeout (only if + * ERR_INPROGRESS is returned!) + * @param callback_arg argument to pass to the callback function + * @return a err_t return code. + */ +err_t ICACHE_FLASH_ATTR +dns_gethostbyname(const char *hostname, ip_addr_t *addr, dns_found_callback found, + void *callback_arg) +{ + u32_t ipaddr; + /* not initialized or no valid server yet, or invalid addr pointer + * or invalid hostname or invalid hostname length */ + if ((dns_pcb == NULL) || (addr == NULL) || + (!hostname) || (!hostname[0]) || + (os_strlen(hostname) >= DNS_MAX_NAME_LENGTH)) { + return ERR_ARG; + } + +#if LWIP_HAVE_LOOPIF + if (strcmp(hostname, "localhost")==0) { + ip_addr_set_loopback(addr); + return ERR_OK; + } +#endif /* LWIP_HAVE_LOOPIF */ + + /* host name already in octet notation? set ip addr and return ERR_OK */ + ipaddr = ipaddr_addr(hostname); + if (ipaddr == IPADDR_NONE) { + /* already have this address cached? */ +// ipaddr = dns_lookup(hostname); + } + if (ipaddr != IPADDR_NONE) { + ip4_addr_set_u32(addr, ipaddr); + return ERR_OK; + } + + /* queue query with specified callback */ + return dns_enqueue(hostname, found, callback_arg); +} + +#endif /* LWIP_DNS */ diff --git a/variants/lwip/lwip/core/init.c b/variants/lwip/lwip/core/init.c new file mode 100755 index 000000000..aa403f482 --- /dev/null +++ b/variants/lwip/lwip/core/init.c @@ -0,0 +1,325 @@ +/** + * @file + * Modules initialization + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/init.h" +#include "lwip/stats.h" +#include "lwip/sys.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/pbuf.h" +#include "lwip/netif.h" +#include "lwip/sockets.h" +#include "lwip/ip.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/tcp_impl.h" +#include "lwip/snmp_msg.h" +#include "lwip/autoip.h" +#include "lwip/igmp.h" +#include "lwip/dns.h" +#include "lwip/timers.h" +#include "netif/etharp.h" + +/* Compile-time sanity checks for configuration errors. + * These can be done independently of LWIP_DEBUG, without penalty. + */ +#ifndef BYTE_ORDER + #error "BYTE_ORDER is not defined, you have to define it in your cc.h" +#endif +#if (!IP_SOF_BROADCAST && IP_SOF_BROADCAST_RECV) + #error "If you want to use broadcast filter per pcb on recv operations, you have to define IP_SOF_BROADCAST=1 in your lwipopts.h" +#endif +#if (!LWIP_ARP && ARP_QUEUEING) + #error "If you want to use ARP Queueing, you have to define LWIP_ARP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_UDPLITE) + #error "If you want to use UDP Lite, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_SNMP) + #error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_DHCP) + #error "If you want to use DHCP, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_IGMP) + #error "If you want to use IGMP, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_SNMP) + #error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_DNS) + #error "If you want to use DNS, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (LWIP_ARP && ARP_QUEUEING && (MEMP_NUM_ARP_QUEUE<=0)) + #error "If you want to use ARP Queueing, you have to define MEMP_NUM_ARP_QUEUE>=1 in your lwipopts.h" +#endif +#if (LWIP_RAW && (MEMP_NUM_RAW_PCB<=0)) + #error "If you want to use RAW, you have to define MEMP_NUM_RAW_PCB>=1 in your lwipopts.h" +#endif +#if (LWIP_UDP && (MEMP_NUM_UDP_PCB<=0)) + #error "If you want to use UDP, you have to define MEMP_NUM_UDP_PCB>=1 in your lwipopts.h" +#endif +//#if (LWIP_TCP && (MEMP_NUM_TCP_PCB<=0)) +// #error "If you want to use TCP, you have to define MEMP_NUM_TCP_PCB>=1 in your lwipopts.h" +//#endif +//#if (LWIP_TCP && (TCP_WND > 0xffff)) +// #error "If you want to use TCP, TCP_WND must fit in an u16_t, so, you have to reduce it in your lwipopts.h" +//#endif +#if (LWIP_TCP && (TCP_SND_QUEUELEN > 0xffff)) + #error "If you want to use TCP, TCP_SND_QUEUELEN must fit in an u16_t, so, you have to reduce it in your lwipopts.h" +#endif +#if (LWIP_TCP && (TCP_SND_QUEUELEN < 2)) + #error "TCP_SND_QUEUELEN must be at least 2 for no-copy TCP writes to work" +#endif +//#if (LWIP_TCP && ((TCP_MAXRTX > 12) || (TCP_SYNMAXRTX > 12))) +// #error "If you want to use TCP, TCP_MAXRTX and TCP_SYNMAXRTX must less or equal to 12 (due to tcp_backoff table), so, you have to reduce them in your lwipopts.h" +//#endif +#if (LWIP_TCP && TCP_LISTEN_BACKLOG && (TCP_DEFAULT_LISTEN_BACKLOG < 0) || (TCP_DEFAULT_LISTEN_BACKLOG > 0xff)) + #error "If you want to use TCP backlog, TCP_DEFAULT_LISTEN_BACKLOG must fit into an u8_t" +#endif +#if (LWIP_IGMP && (MEMP_NUM_IGMP_GROUP<=1)) + #error "If you want to use IGMP, you have to define MEMP_NUM_IGMP_GROUP>1 in your lwipopts.h" +#endif +#if (LWIP_NETIF_API && (NO_SYS==1)) + #error "If you want to use NETIF API, you have to define NO_SYS=0 in your lwipopts.h" +#endif +#if ((LWIP_SOCKET || LWIP_NETCONN) && (NO_SYS==1)) + #error "If you want to use Sequential API, you have to define NO_SYS=0 in your lwipopts.h" +#endif +#if ((LWIP_NETCONN || LWIP_SOCKET) && (MEMP_NUM_TCPIP_MSG_API<=0)) + #error "If you want to use Sequential API, you have to define MEMP_NUM_TCPIP_MSG_API>=1 in your lwipopts.h" +#endif +#if (!LWIP_NETCONN && LWIP_SOCKET) + #error "If you want to use Socket API, you have to define LWIP_NETCONN=1 in your lwipopts.h" +#endif +#if (((!LWIP_DHCP) || (!LWIP_AUTOIP)) && LWIP_DHCP_AUTOIP_COOP) + #error "If you want to use DHCP/AUTOIP cooperation mode, you have to define LWIP_DHCP=1 and LWIP_AUTOIP=1 in your lwipopts.h" +#endif +#if (((!LWIP_DHCP) || (!LWIP_ARP)) && DHCP_DOES_ARP_CHECK) + #error "If you want to use DHCP ARP checking, you have to define LWIP_DHCP=1 and LWIP_ARP=1 in your lwipopts.h" +#endif +#if (!LWIP_ARP && LWIP_AUTOIP) + #error "If you want to use AUTOIP, you have to define LWIP_ARP=1 in your lwipopts.h" +#endif +#if (LWIP_SNMP && (SNMP_CONCURRENT_REQUESTS<=0)) + #error "If you want to use SNMP, you have to define SNMP_CONCURRENT_REQUESTS>=1 in your lwipopts.h" +#endif +#if (LWIP_SNMP && (SNMP_TRAP_DESTINATIONS<=0)) + #error "If you want to use SNMP, you have to define SNMP_TRAP_DESTINATIONS>=1 in your lwipopts.h" +#endif +#if (LWIP_TCP && ((LWIP_EVENT_API && LWIP_CALLBACK_API) || (!LWIP_EVENT_API && !LWIP_CALLBACK_API))) + #error "One and exactly one of LWIP_EVENT_API and LWIP_CALLBACK_API has to be enabled in your lwipopts.h" +#endif +/* There must be sufficient timeouts, taking into account requirements of the subsystems. */ +#if LWIP_TIMERS && (MEMP_NUM_SYS_TIMEOUT < (LWIP_TCP + IP_REASSEMBLY + LWIP_ARP + (2*LWIP_DHCP) + LWIP_AUTOIP + LWIP_IGMP + LWIP_DNS + PPP_SUPPORT)) + #error "MEMP_NUM_SYS_TIMEOUT is too low to accomodate all required timeouts" +#endif +#if (IP_REASSEMBLY && (MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS)) + #error "MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS doesn't make sense since each struct ip_reassdata must hold 2 pbufs at least!" +#endif +#if (MEM_LIBC_MALLOC && MEM_USE_POOLS) + #error "MEM_LIBC_MALLOC and MEM_USE_POOLS may not both be simultaneously enabled in your lwipopts.h" +#endif +#if (MEM_USE_POOLS && !MEMP_USE_CUSTOM_POOLS) + #error "MEM_USE_POOLS requires custom pools (MEMP_USE_CUSTOM_POOLS) to be enabled in your lwipopts.h" +#endif +#if (PBUF_POOL_BUFSIZE <= MEM_ALIGNMENT) + #error "PBUF_POOL_BUFSIZE must be greater than MEM_ALIGNMENT or the offset may take the full first pbuf" +#endif +#if (TCP_QUEUE_OOSEQ && !LWIP_TCP) + #error "TCP_QUEUE_OOSEQ requires LWIP_TCP" +#endif +#if (DNS_LOCAL_HOSTLIST && !DNS_LOCAL_HOSTLIST_IS_DYNAMIC && !(defined(DNS_LOCAL_HOSTLIST_INIT))) + #error "you have to define define DNS_LOCAL_HOSTLIST_INIT {{'host1', 0x123}, {'host2', 0x234}} to initialize DNS_LOCAL_HOSTLIST" +#endif +#if PPP_SUPPORT && !PPPOS_SUPPORT & !PPPOE_SUPPORT + #error "PPP_SUPPORT needs either PPPOS_SUPPORT or PPPOE_SUPPORT turned on" +#endif +#if !LWIP_ETHERNET && (LWIP_ARP || PPPOE_SUPPORT) + #error "LWIP_ETHERNET needs to be turned on for LWIP_ARP or PPPOE_SUPPORT" +#endif +#if LWIP_IGMP && !defined(LWIP_RAND) + #error "When using IGMP, LWIP_RAND() needs to be defined to a random-function returning an u32_t random value" +#endif +#if LWIP_TCPIP_CORE_LOCKING_INPUT && !LWIP_TCPIP_CORE_LOCKING + #error "When using LWIP_TCPIP_CORE_LOCKING_INPUT, LWIP_TCPIP_CORE_LOCKING must be enabled, too" +#endif +#if LWIP_TCP && LWIP_NETIF_TX_SINGLE_PBUF && !TCP_OVERSIZE + #error "LWIP_NETIF_TX_SINGLE_PBUF needs TCP_OVERSIZE enabled to create single-pbuf TCP packets" +#endif +#if IP_FRAG && IP_FRAG_USES_STATIC_BUF && LWIP_NETIF_TX_SINGLE_PBUF + #error "LWIP_NETIF_TX_SINGLE_PBUF does not work with IP_FRAG_USES_STATIC_BUF==1 as that creates pbuf queues" +#endif + + +/* Compile-time checks for deprecated options. + */ +#ifdef MEMP_NUM_TCPIP_MSG + #error "MEMP_NUM_TCPIP_MSG option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef MEMP_NUM_API_MSG + #error "MEMP_NUM_API_MSG option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef TCP_REXMIT_DEBUG + #error "TCP_REXMIT_DEBUG option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef RAW_STATS + #error "RAW_STATS option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef ETHARP_QUEUE_FIRST + #error "ETHARP_QUEUE_FIRST option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef ETHARP_ALWAYS_INSERT + #error "ETHARP_ALWAYS_INSERT option is deprecated. Remove it from your lwipopts.h." +#endif + +#ifdef LWIP_DEBUG +static void ICACHE_FLASH_ATTR +lwip_sanity_check(void) +{ + /* Warnings */ +#if LWIP_NETCONN + if (MEMP_NUM_NETCONN > (MEMP_NUM_TCP_PCB+MEMP_NUM_TCP_PCB_LISTEN+MEMP_NUM_UDP_PCB+MEMP_NUM_RAW_PCB)) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: MEMP_NUM_NETCONN should be less than the sum of MEMP_NUM_{TCP,RAW,UDP}_PCB+MEMP_NUM_TCP_PCB_LISTEN\n")); +#endif /* LWIP_NETCONN */ +#if LWIP_TCP + if (MEMP_NUM_TCP_SEG < TCP_SND_QUEUELEN) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: MEMP_NUM_TCP_SEG should be at least as big as TCP_SND_QUEUELEN\n")); + if (TCP_SND_BUF < 2 * TCP_MSS) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_SND_BUF must be at least as much as (2 * TCP_MSS) for things to work smoothly\n")); + if (TCP_SND_QUEUELEN < (2 * (TCP_SND_BUF/TCP_MSS))) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_SND_QUEUELEN must be at least as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work\n")); + if (TCP_SNDLOWAT >= TCP_SND_BUF) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_SNDLOWAT must be less than TCP_SND_BUF.\n")); + if (TCP_SNDQUEUELOWAT >= TCP_SND_QUEUELEN) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_SNDQUEUELOWAT must be less than TCP_SND_QUEUELEN.\n")); + if (TCP_WND > (PBUF_POOL_SIZE*PBUF_POOL_BUFSIZE)) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_WND is larger than space provided by PBUF_POOL_SIZE*PBUF_POOL_BUFSIZE\n")); + if (TCP_WND < TCP_MSS) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: TCP_WND is smaller than MSS\n")); +#endif /* LWIP_TCP */ +#if LWIP_SOCKET + /* Check that the SO_* socket options and SOF_* lwIP-internal flags match */ + if (SO_ACCEPTCONN != SOF_ACCEPTCONN) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: SO_ACCEPTCONN != SOF_ACCEPTCONN\n")); + if (SO_REUSEADDR != SOF_REUSEADDR) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: SO_REUSEADDR != SOF_REUSEADDR\n")); + if (SO_KEEPALIVE != SOF_KEEPALIVE) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: SO_KEEPALIVE != SOF_KEEPALIVE\n")); + if (SO_BROADCAST != SOF_BROADCAST) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: SO_BROADCAST != SOF_BROADCAST\n")); + if (SO_LINGER != SOF_LINGER) + LWIP_PLATFORM_DIAG(("lwip_sanity_check: WARNING: SO_LINGER != SOF_LINGER\n")); +#endif /* LWIP_SOCKET */ +} +#else /* LWIP_DEBUG */ +#define lwip_sanity_check() +#endif /* LWIP_DEBUG */ + +/** + * Perform Sanity check of user-configurable values, and initialize all modules. + */ +void +lwip_init(void) +{ + MEMP_NUM_TCP_PCB = 5; + TCP_WND = (4 * TCP_MSS); + TCP_MAXRTX = 12; + TCP_SYNMAXRTX = 6; + + /* Sanity check user-configurable values */ + lwip_sanity_check(); + + /* Modules initialization */ + stats_init(); +#if !NO_SYS + sys_init(); +#endif /* !NO_SYS */ +#if 0 + mem_init(&_bss_end); +#endif + memp_init(); + + pbuf_init(); + + netif_init(); + +#if LWIP_SOCKET + lwip_socket_init(); +#endif /* LWIP_SOCKET */ + ip_init(); + +#if LWIP_ARP + etharp_init(); + +#endif /* LWIP_ARP */ +#if LWIP_RAW + raw_init(); + +#endif /* LWIP_RAW */ +#if LWIP_UDP + udp_init(); + +#endif /* LWIP_UDP */ +#if LWIP_TCP + tcp_init(); + +#endif /* LWIP_TCP */ +#if LWIP_SNMP + snmp_init(); + +#endif /* LWIP_SNMP */ +#if LWIP_AUTOIP + autoip_init(); + +#endif /* LWIP_AUTOIP */ +#if LWIP_IGMP + igmp_init(); + +#endif /* LWIP_IGMP */ +#if LWIP_DNS + dns_init(); + +#endif /* LWIP_DNS */ + +#if LWIP_TIMERS + sys_timeouts_init(); +#endif /* LWIP_TIMERS */ +} diff --git a/variants/lwip/lwip/core/ipv4/autoip.c b/variants/lwip/lwip/core/ipv4/autoip.c new file mode 100755 index 000000000..cdba69d6b --- /dev/null +++ b/variants/lwip/lwip/core/ipv4/autoip.c @@ -0,0 +1,536 @@ +/** + * @file + * AutoIP Automatic LinkLocal IP Configuration + * + */ + +/* + * + * Copyright (c) 2007 Dominik Spies + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Dominik Spies + * + * This is a AutoIP implementation for the lwIP TCP/IP stack. It aims to conform + * with RFC 3927. + * + * + * Please coordinate changes and requests with Dominik Spies + * + */ + +/******************************************************************************* + * USAGE: + * + * define LWIP_AUTOIP 1 in your lwipopts.h + * + * If you don't use tcpip.c (so, don't call, you don't call tcpip_init): + * - First, call autoip_init(). + * - call autoip_tmr() all AUTOIP_TMR_INTERVAL msces, + * that should be defined in autoip.h. + * I recommend a value of 100. The value must divide 1000 with a remainder almost 0. + * Possible values are 1000, 500, 333, 250, 200, 166, 142, 125, 111, 100 .... + * + * Without DHCP: + * - Call autoip_start() after netif_add(). + * + * With DHCP: + * - define LWIP_DHCP_AUTOIP_COOP 1 in your lwipopts.h. + * - Configure your DHCP Client. + * + */ + +#include "lwip/opt.h" + +#if LWIP_AUTOIP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/mem.h" +#include "lwip/udp.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/autoip.h" +#include "netif/etharp.h" + +#include +#include + +/* 169.254.0.0 */ +#define AUTOIP_NET 0xA9FE0000 +/* 169.254.1.0 */ +#define AUTOIP_RANGE_START (AUTOIP_NET | 0x0100) +/* 169.254.254.255 */ +#define AUTOIP_RANGE_END (AUTOIP_NET | 0xFEFF) + + +/** Pseudo random macro based on netif informations. + * You could use "rand()" from the C Library if you define LWIP_AUTOIP_RAND in lwipopts.h */ +#ifndef LWIP_AUTOIP_RAND +#define LWIP_AUTOIP_RAND(netif) ( (((u32_t)((netif->hwaddr[5]) & 0xff) << 24) | \ + ((u32_t)((netif->hwaddr[3]) & 0xff) << 16) | \ + ((u32_t)((netif->hwaddr[2]) & 0xff) << 8) | \ + ((u32_t)((netif->hwaddr[4]) & 0xff))) + \ + (netif->autoip?netif->autoip->tried_llipaddr:0)) +#endif /* LWIP_AUTOIP_RAND */ + +/** + * Macro that generates the initial IP address to be tried by AUTOIP. + * If you want to override this, define it to something else in lwipopts.h. + */ +#ifndef LWIP_AUTOIP_CREATE_SEED_ADDR +#define LWIP_AUTOIP_CREATE_SEED_ADDR(netif) \ + htonl(AUTOIP_RANGE_START + ((u32_t)(((u8_t)(netif->hwaddr[4])) | \ + ((u32_t)((u8_t)(netif->hwaddr[5]))) << 8))) +#endif /* LWIP_AUTOIP_CREATE_SEED_ADDR */ + +/* static functions */ +static void autoip_handle_arp_conflict(struct netif *netif); + +/* creates a pseudo random LL IP-Address for a network interface */ +static void autoip_create_addr(struct netif *netif, ip_addr_t *ipaddr); + +/* sends an ARP probe */ +static err_t autoip_arp_probe(struct netif *netif); + +/* sends an ARP announce */ +static err_t autoip_arp_announce(struct netif *netif); + +/* configure interface for use with current LL IP-Address */ +static err_t autoip_bind(struct netif *netif); + +/* start sending probes for llipaddr */ +static void autoip_start_probing(struct netif *netif); + +/** + * Initialize this module + */ +void +autoip_init(void) +{ + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_init()\n")); +} + +/** Set a statically allocated struct autoip to work with. + * Using this prevents autoip_start to allocate it using mem_malloc. + * + * @param netif the netif for which to set the struct autoip + * @param dhcp (uninitialised) dhcp struct allocated by the application + */ +void +autoip_set_struct(struct netif *netif, struct autoip *autoip) +{ + LWIP_ASSERT("netif != NULL", netif != NULL); + LWIP_ASSERT("autoip != NULL", autoip != NULL); + LWIP_ASSERT("netif already has a struct autoip set", netif->autoip == NULL); + + /* clear data structure */ + os_memset(autoip, 0, sizeof(struct autoip)); + /* autoip->state = AUTOIP_STATE_OFF; */ + netif->autoip = autoip; +} + +/** Restart AutoIP client and check the next address (conflict detected) + * + * @param netif The netif under AutoIP control + */ +static void +autoip_restart(struct netif *netif) +{ + netif->autoip->tried_llipaddr++; + autoip_start(netif); +} + +/** + * Handle a IP address conflict after an ARP conflict detection + */ +static void +autoip_handle_arp_conflict(struct netif *netif) +{ + /* Somehow detect if we are defending or retreating */ + unsigned char defend = 1; /* tbd */ + + if(defend) { + if(netif->autoip->lastconflict > 0) { + /* retreat, there was a conflicting ARP in the last + * DEFEND_INTERVAL seconds + */ + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_handle_arp_conflict(): we are defending, but in DEFEND_INTERVAL, retreating\n")); + + /* TODO: close all TCP sessions */ + autoip_restart(netif); + } else { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_handle_arp_conflict(): we are defend, send ARP Announce\n")); + autoip_arp_announce(netif); + netif->autoip->lastconflict = DEFEND_INTERVAL * AUTOIP_TICKS_PER_SECOND; + } + } else { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_handle_arp_conflict(): we do not defend, retreating\n")); + /* TODO: close all TCP sessions */ + autoip_restart(netif); + } +} + +/** + * Create an IP-Address out of range 169.254.1.0 to 169.254.254.255 + * + * @param netif network interface on which create the IP-Address + * @param ipaddr ip address to initialize + */ +static void +autoip_create_addr(struct netif *netif, ip_addr_t *ipaddr) +{ + /* Here we create an IP-Address out of range 169.254.1.0 to 169.254.254.255 + * compliant to RFC 3927 Section 2.1 + * We have 254 * 256 possibilities */ + + u32_t addr = ntohl(LWIP_AUTOIP_CREATE_SEED_ADDR(netif)); + addr += netif->autoip->tried_llipaddr; + addr = AUTOIP_NET | (addr & 0xffff); + /* Now, 169.254.0.0 <= addr <= 169.254.255.255 */ + + if (addr < AUTOIP_RANGE_START) { + addr += AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1; + } + if (addr > AUTOIP_RANGE_END) { + addr -= AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1; + } + LWIP_ASSERT("AUTOIP address not in range", (addr >= AUTOIP_RANGE_START) && + (addr <= AUTOIP_RANGE_END)); + ip4_addr_set_u32(ipaddr, htonl(addr)); + + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_create_addr(): tried_llipaddr=%"U16_F", %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + (u16_t)(netif->autoip->tried_llipaddr), ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), + ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr))); +} + +/** + * Sends an ARP probe from a network interface + * + * @param netif network interface used to send the probe + */ +static err_t +autoip_arp_probe(struct netif *netif) +{ + return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, ðbroadcast, + (struct eth_addr *)netif->hwaddr, IP_ADDR_ANY, ðzero, + &netif->autoip->llipaddr, ARP_REQUEST); +} + +/** + * Sends an ARP announce from a network interface + * + * @param netif network interface used to send the announce + */ +static err_t +autoip_arp_announce(struct netif *netif) +{ + return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, ðbroadcast, + (struct eth_addr *)netif->hwaddr, &netif->autoip->llipaddr, ðzero, + &netif->autoip->llipaddr, ARP_REQUEST); +} + +/** + * Configure interface for use with current LL IP-Address + * + * @param netif network interface to configure with current LL IP-Address + */ +static err_t +autoip_bind(struct netif *netif) +{ + struct autoip *autoip = netif->autoip; + ip_addr_t sn_mask, gw_addr; + + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_bind(netif=%p) %c%c%"U16_F" %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num, + ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr), + ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr))); + + IP4_ADDR(&sn_mask, 255, 255, 0, 0); + IP4_ADDR(&gw_addr, 0, 0, 0, 0); + + netif_set_ipaddr(netif, &autoip->llipaddr); + netif_set_netmask(netif, &sn_mask); + netif_set_gw(netif, &gw_addr); + + /* bring the interface up */ + netif_set_up(netif); + + return ERR_OK; +} + +/** + * Start AutoIP client + * + * @param netif network interface on which start the AutoIP client + */ +err_t +autoip_start(struct netif *netif) +{ + struct autoip *autoip = netif->autoip; + err_t result = ERR_OK; + + if(netif_is_up(netif)) { + netif_set_down(netif); + } + + /* Set IP-Address, Netmask and Gateway to 0 to make sure that + * ARP Packets are formed correctly + */ + ip_addr_set_zero(&netif->ip_addr); + ip_addr_set_zero(&netif->netmask); + ip_addr_set_zero(&netif->gw); + + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], + netif->name[1], (u16_t)netif->num)); + if(autoip == NULL) { + /* no AutoIP client attached yet? */ + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_start(): starting new AUTOIP client\n")); + autoip = (struct autoip *)mem_malloc(sizeof(struct autoip)); + if(autoip == NULL) { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_start(): could not allocate autoip\n")); + return ERR_MEM; + } + os_memset(autoip, 0, sizeof(struct autoip)); + /* store this AutoIP client in the netif */ + netif->autoip = autoip; + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_start(): allocated autoip")); + } else { + autoip->state = AUTOIP_STATE_OFF; + autoip->ttw = 0; + autoip->sent_num = 0; + ip_addr_set_zero(&autoip->llipaddr); + autoip->lastconflict = 0; + } + + autoip_create_addr(netif, &(autoip->llipaddr)); + autoip_start_probing(netif); + + return result; +} + +static void +autoip_start_probing(struct netif *netif) +{ + struct autoip *autoip = netif->autoip; + + autoip->state = AUTOIP_STATE_PROBING; + autoip->sent_num = 0; + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_start_probing(): changing state to PROBING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(&netif->autoip->llipaddr), ip4_addr2_16(&netif->autoip->llipaddr), + ip4_addr3_16(&netif->autoip->llipaddr), ip4_addr4_16(&netif->autoip->llipaddr))); + + /* time to wait to first probe, this is randomly + * choosen out of 0 to PROBE_WAIT seconds. + * compliant to RFC 3927 Section 2.2.1 + */ + autoip->ttw = (u16_t)(LWIP_AUTOIP_RAND(netif) % (PROBE_WAIT * AUTOIP_TICKS_PER_SECOND)); + + /* + * if we tried more then MAX_CONFLICTS we must limit our rate for + * accquiring and probing address + * compliant to RFC 3927 Section 2.2.1 + */ + if(autoip->tried_llipaddr > MAX_CONFLICTS) { + autoip->ttw = RATE_LIMIT_INTERVAL * AUTOIP_TICKS_PER_SECOND; + } +} + +/** + * Handle a possible change in the network configuration. + * + * If there is an AutoIP address configured, take the interface down + * and begin probing with the same address. + */ +void +autoip_network_changed(struct netif *netif) +{ + if (netif->autoip && netif->autoip->state != AUTOIP_STATE_OFF) { + netif_set_down(netif); + autoip_start_probing(netif); + } +} + +/** + * Stop AutoIP client + * + * @param netif network interface on which stop the AutoIP client + */ +err_t +autoip_stop(struct netif *netif) +{ + netif->autoip->state = AUTOIP_STATE_OFF; + netif_set_down(netif); + return ERR_OK; +} + +/** + * Has to be called in loop every AUTOIP_TMR_INTERVAL milliseconds + */ +void +autoip_tmr() +{ + struct netif *netif = netif_list; + /* loop through netif's */ + while (netif != NULL) { + /* only act on AutoIP configured interfaces */ + if (netif->autoip != NULL) { + if(netif->autoip->lastconflict > 0) { + netif->autoip->lastconflict--; + } + + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_tmr() AutoIP-State: %"U16_F", ttw=%"U16_F"\n", + (u16_t)(netif->autoip->state), netif->autoip->ttw)); + + switch(netif->autoip->state) { + case AUTOIP_STATE_PROBING: + if(netif->autoip->ttw > 0) { + netif->autoip->ttw--; + } else { + if(netif->autoip->sent_num >= PROBE_NUM) { + netif->autoip->state = AUTOIP_STATE_ANNOUNCING; + netif->autoip->sent_num = 0; + netif->autoip->ttw = ANNOUNCE_WAIT * AUTOIP_TICKS_PER_SECOND; + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_tmr(): changing state to ANNOUNCING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(&netif->autoip->llipaddr), ip4_addr2_16(&netif->autoip->llipaddr), + ip4_addr3_16(&netif->autoip->llipaddr), ip4_addr4_16(&netif->autoip->llipaddr))); + } else { + autoip_arp_probe(netif); + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_tmr() PROBING Sent Probe\n")); + netif->autoip->sent_num++; + /* calculate time to wait to next probe */ + netif->autoip->ttw = (u16_t)((LWIP_AUTOIP_RAND(netif) % + ((PROBE_MAX - PROBE_MIN) * AUTOIP_TICKS_PER_SECOND) ) + + PROBE_MIN * AUTOIP_TICKS_PER_SECOND); + } + } + break; + + case AUTOIP_STATE_ANNOUNCING: + if(netif->autoip->ttw > 0) { + netif->autoip->ttw--; + } else { + if(netif->autoip->sent_num == 0) { + /* We are here the first time, so we waited ANNOUNCE_WAIT seconds + * Now we can bind to an IP address and use it. + * + * autoip_bind calls netif_set_up. This triggers a gratuitous ARP + * which counts as an announcement. + */ + autoip_bind(netif); + } else { + autoip_arp_announce(netif); + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_tmr() ANNOUNCING Sent Announce\n")); + } + netif->autoip->ttw = ANNOUNCE_INTERVAL * AUTOIP_TICKS_PER_SECOND; + netif->autoip->sent_num++; + + if(netif->autoip->sent_num >= ANNOUNCE_NUM) { + netif->autoip->state = AUTOIP_STATE_BOUND; + netif->autoip->sent_num = 0; + netif->autoip->ttw = 0; + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_tmr(): changing state to BOUND: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(&netif->autoip->llipaddr), ip4_addr2_16(&netif->autoip->llipaddr), + ip4_addr3_16(&netif->autoip->llipaddr), ip4_addr4_16(&netif->autoip->llipaddr))); + } + } + break; + } + } + /* proceed to next network interface */ + netif = netif->next; + } +} + +/** + * Handles every incoming ARP Packet, called by etharp_arp_input. + * + * @param netif network interface to use for autoip processing + * @param hdr Incoming ARP packet + */ +void +autoip_arp_reply(struct netif *netif, struct etharp_hdr *hdr) +{ + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_arp_reply()\n")); + if ((netif->autoip != NULL) && (netif->autoip->state != AUTOIP_STATE_OFF)) { + /* when ip.src == llipaddr && hw.src != netif->hwaddr + * + * when probing ip.dst == llipaddr && hw.src != netif->hwaddr + * we have a conflict and must solve it + */ + ip_addr_t sipaddr, dipaddr; + struct eth_addr netifaddr; + ETHADDR16_COPY(netifaddr.addr, netif->hwaddr); + + /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without + * structure packing (not using structure copy which breaks strict-aliasing rules). + */ + IPADDR2_COPY(&sipaddr, &hdr->sipaddr); + IPADDR2_COPY(&dipaddr, &hdr->dipaddr); + + if ((netif->autoip->state == AUTOIP_STATE_PROBING) || + ((netif->autoip->state == AUTOIP_STATE_ANNOUNCING) && + (netif->autoip->sent_num == 0))) { + /* RFC 3927 Section 2.2.1: + * from beginning to after ANNOUNCE_WAIT + * seconds we have a conflict if + * ip.src == llipaddr OR + * ip.dst == llipaddr && hw.src != own hwaddr + */ + if ((ip_addr_cmp(&sipaddr, &netif->autoip->llipaddr)) || + (ip_addr_cmp(&dipaddr, &netif->autoip->llipaddr) && + !eth_addr_cmp(&netifaddr, &hdr->shwaddr))) { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING, + ("autoip_arp_reply(): Probe Conflict detected\n")); + autoip_restart(netif); + } + } else { + /* RFC 3927 Section 2.5: + * in any state we have a conflict if + * ip.src == llipaddr && hw.src != own hwaddr + */ + if (ip_addr_cmp(&sipaddr, &netif->autoip->llipaddr) && + !eth_addr_cmp(&netifaddr, &hdr->shwaddr)) { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING, + ("autoip_arp_reply(): Conflicting ARP-Packet detected\n")); + autoip_handle_arp_conflict(netif); + } + } + } +} + +#endif /* LWIP_AUTOIP */ diff --git a/variants/lwip/lwip/core/ipv4/icmp.c b/variants/lwip/lwip/core/ipv4/icmp.c new file mode 100755 index 000000000..7b11d06b3 --- /dev/null +++ b/variants/lwip/lwip/core/ipv4/icmp.c @@ -0,0 +1,337 @@ +/** + * @file + * ICMP - Internet Control Message Protocol + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +/* Some ICMP messages should be passed to the transport protocols. This + is not implemented. */ + +#include "lwip/opt.h" + +#if LWIP_ICMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/icmp.h" +#include "lwip/inet_chksum.h" +#include "lwip/ip.h" +#include "lwip/def.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" + +#include + +/** Small optimization: set to 0 if incoming PBUF_POOL pbuf always can be + * used to modify and send a response packet (and to 1 if this is not the case, + * e.g. when link header is stripped of when receiving) */ +#ifndef LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN +#define LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN 1 +#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */ + +/* The amount of data from the original packet to return in a dest-unreachable */ +#define ICMP_DEST_UNREACH_DATASIZE 8 + +static void icmp_send_response(struct pbuf *p, u8_t type, u8_t code); + +/** + * Processes ICMP input packets, called from ip_input(). + * + * Currently only processes icmp echo requests and sends + * out the echo response. + * + * @param p the icmp echo request packet, p->payload pointing to the ip header + * @param inp the netif on which this packet was received + */ +void +icmp_input(struct pbuf *p, struct netif *inp) +{ + u8_t type; +#ifdef LWIP_DEBUG + u8_t code; +#endif /* LWIP_DEBUG */ + struct icmp_echo_hdr *iecho; + struct ip_hdr *iphdr; + s16_t hlen; + + ICMP_STATS_INC(icmp.recv); + snmp_inc_icmpinmsgs(); + + + iphdr = (struct ip_hdr *)p->payload; + hlen = IPH_HL(iphdr) * 4; + if (pbuf_header(p, -hlen) || (p->tot_len < sizeof(u16_t)*2)) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: short ICMP (%"U16_F" bytes) received\n", p->tot_len)); + goto lenerr; + } + + type = *((u8_t *)p->payload); +#ifdef LWIP_DEBUG + code = *(((u8_t *)p->payload)+1); +#endif /* LWIP_DEBUG */ + switch (type) { + case ICMP_ER: + /* This is OK, echo reply might have been parsed by a raw PCB + (as obviously, an echo request has been sent, too). */ + break; + case ICMP_ECHO: +#if !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING + { + int accepted = 1; +#if !LWIP_MULTICAST_PING + /* multicast destination address? */ + if (ip_addr_ismulticast(¤t_iphdr_dest)) { + accepted = 0; + } +#endif /* LWIP_MULTICAST_PING */ +#if !LWIP_BROADCAST_PING + /* broadcast destination address? */ + if (ip_addr_isbroadcast(¤t_iphdr_dest, inp)) { + accepted = 0; + } +#endif /* LWIP_BROADCAST_PING */ + /* broadcast or multicast destination address not acceptd? */ + if (!accepted) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: Not echoing to multicast or broadcast pings\n")); + ICMP_STATS_INC(icmp.err); + pbuf_free(p); + return; + } + } +#endif /* !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING */ + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ping\n")); + if (p->tot_len < sizeof(struct icmp_echo_hdr)) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: bad ICMP echo received\n")); + goto lenerr; + } + if (inet_chksum_pbuf(p) != 0) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo\n")); + pbuf_free(p); + ICMP_STATS_INC(icmp.chkerr); + snmp_inc_icmpinerrors(); + return; + } +#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN + if (pbuf_header(p, (PBUF_IP_HLEN + PBUF_LINK_HLEN))) { + /* p is not big enough to contain link headers + * allocate a new one and copy p into it + */ + struct pbuf *r; + /* switch p->payload to ip header */ + if (pbuf_header(p, hlen)) { + LWIP_ASSERT("icmp_input: moving p->payload to ip header failed\n", 0); + goto memerr; + } + /* allocate new packet buffer with space for link headers */ + r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM); + if (r == NULL) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: allocating new pbuf failed\n")); + goto memerr; + } + LWIP_ASSERT("check that first pbuf can hold struct the ICMP header", + (r->len >= hlen + sizeof(struct icmp_echo_hdr))); + /* copy the whole packet including ip header */ + if (pbuf_copy(r, p) != ERR_OK) { + LWIP_ASSERT("icmp_input: copying to new pbuf failed\n", 0); + goto memerr; + } + iphdr = (struct ip_hdr *)r->payload; + /* switch r->payload back to icmp header */ + if (pbuf_header(r, -hlen)) { + LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0); + goto memerr; + } + /* free the original p */ + pbuf_free(p); + /* we now have an identical copy of p that has room for link headers */ + p = r; + } else { + /* restore p->payload to point to icmp header */ + if (pbuf_header(p, -(s16_t)(PBUF_IP_HLEN + PBUF_LINK_HLEN))) { + LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0); + goto memerr; + } + } +#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */ + /* At this point, all checks are OK. */ + /* We generate an answer by switching the dest and src ip addresses, + * setting the icmp type to ECHO_RESPONSE and updating the checksum. */ + iecho = (struct icmp_echo_hdr *)p->payload; + ip_addr_copy(iphdr->src, *ip_current_dest_addr()); + ip_addr_copy(iphdr->dest, *ip_current_src_addr()); + ICMPH_TYPE_SET(iecho, ICMP_ER); + /* adjust the checksum */ + if (iecho->chksum >= PP_HTONS(0xffff - (ICMP_ECHO << 8))) { + iecho->chksum += PP_HTONS(ICMP_ECHO << 8) + 1; + } else { + iecho->chksum += PP_HTONS(ICMP_ECHO << 8); + } + + /* Set the correct TTL and recalculate the header checksum. */ + IPH_TTL_SET(iphdr, ICMP_TTL); + IPH_CHKSUM_SET(iphdr, 0); +#if CHECKSUM_GEN_IP + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN)); +#endif /* CHECKSUM_GEN_IP */ + + ICMP_STATS_INC(icmp.xmit); + /* increase number of messages attempted to send */ + snmp_inc_icmpoutmsgs(); + /* increase number of echo replies attempted to send */ + snmp_inc_icmpoutechoreps(); + + if(pbuf_header(p, hlen)) { + LWIP_ASSERT("Can't move over header in packet", 0); + } else { + err_t ret; + /* send an ICMP packet, src addr is the dest addr of the curren packet */ + ret = ip_output_if(p, ip_current_dest_addr(), IP_HDRINCL, + ICMP_TTL, 0, IP_PROTO_ICMP, inp); + if (ret != ERR_OK) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ip_output_if returned an error: %c.\n", ret)); + } + } + break; + default: + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ICMP type %"S16_F" code %"S16_F" not supported.\n", + (s16_t)type, (s16_t)code)); + ICMP_STATS_INC(icmp.proterr); + ICMP_STATS_INC(icmp.drop); + } + pbuf_free(p); + return; +lenerr: + pbuf_free(p); + ICMP_STATS_INC(icmp.lenerr); + snmp_inc_icmpinerrors(); + return; +#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN +memerr: + pbuf_free(p); + ICMP_STATS_INC(icmp.err); + snmp_inc_icmpinerrors(); + return; +#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */ +} + +/** + * Send an icmp 'destination unreachable' packet, called from ip_input() if + * the transport layer protocol is unknown and from udp_input() if the local + * port is not bound. + * + * @param p the input packet for which the 'unreachable' should be sent, + * p->payload pointing to the IP header + * @param t type of the 'unreachable' packet + */ +void +icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t) +{ + icmp_send_response(p, ICMP_DUR, t); +} + +#if IP_FORWARD || IP_REASSEMBLY +/** + * Send a 'time exceeded' packet, called from ip_forward() if TTL is 0. + * + * @param p the input packet for which the 'time exceeded' should be sent, + * p->payload pointing to the IP header + * @param t type of the 'time exceeded' packet + */ +void +icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t) +{ + icmp_send_response(p, ICMP_TE, t); +} + +#endif /* IP_FORWARD || IP_REASSEMBLY */ + +/** + * Send an icmp packet in response to an incoming packet. + * + * @param p the input packet for which the 'unreachable' should be sent, + * p->payload pointing to the IP header + * @param type Type of the ICMP header + * @param code Code of the ICMP header + */ +static void ICACHE_FLASH_ATTR +icmp_send_response(struct pbuf *p, u8_t type, u8_t code) +{ + struct pbuf *q; + struct ip_hdr *iphdr; + /* we can use the echo header here */ + struct icmp_echo_hdr *icmphdr; + ip_addr_t iphdr_src; + + /* ICMP header + IP header + 8 bytes of data */ + //Ϊ²î´í±¨ÎÄÉêÇëpbuf¿Õ¼ä£¬pbufÖÐÔ¤ÁôIPÊײ¿ºÍÒÔÌ«ÍøÊײ¿¿Õ¼ä£¬pbufÊý¾ÝÇø + //³¤¶È=²î´í±¨ÎÄÊײ¿+²î´í±¨ÎÄÊý¾Ý³¤¶È(IPÊײ¿³¤¶È+8) + q = pbuf_alloc(PBUF_IP, sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE, + PBUF_RAM); + if (q == NULL) {//ʧ°Ü£¬·µ»Ø + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMP packet.\n")); + return; + } + LWIP_ASSERT("check that first pbuf can hold icmp message", + (q->len >= (sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE))); + + iphdr = (struct ip_hdr *)p->payload;//Ö¸ÏòÒýÆð²î´íµÄIPÊý¾Ý°üÊײ¿ + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded from ")); + ip_addr_debug_print(ICMP_DEBUG, &(iphdr->src)); + LWIP_DEBUGF(ICMP_DEBUG, (" to ")); + ip_addr_debug_print(ICMP_DEBUG, &(iphdr->dest)); + LWIP_DEBUGF(ICMP_DEBUG, ("\n")); + + icmphdr = (struct icmp_echo_hdr *)q->payload;//Ö¸Ïò²î´í±¨ÎÄÊײ¿ + icmphdr->type = type;//ÌîдÀàÐÍ×Ö¶Î + icmphdr->code = code;//Ìîд´úÂë×Ö¶Î + icmphdr->id = 0;//¶ÔÓÚÄ¿µÄ²»¿É´ïºÍÊý¾Ý±¨³¬Ê± + icmphdr->seqno = 0;//±¨ÎÄ£¬Êײ¿Ê£ÓàµÄ4¸ö×Ö½Ú¶¼Îª0 + + /* copy fields from original packet ½«ÒýÆð²î´íµÄIPÊý¾Ý±¨µÄIPÊײ¿+8×Ö½ÚÊý¾Ý¿½±´µ½²î´í±¨ÎÄÊý¾ÝÇø*/ + SMEMCPY((u8_t *)q->payload + sizeof(struct icmp_echo_hdr), (u8_t *)p->payload, + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE); + + /* calculate checksum */ + icmphdr->chksum = 0;//±¨ÎÄУÑéºÍ×Ö¶ÎÇå0 + icmphdr->chksum = inet_chksum(icmphdr, q->len);//¼ÆËãÌîдУÑéºÍ + ICMP_STATS_INC(icmp.xmit); + /* increase number of messages attempted to send */ + snmp_inc_icmpoutmsgs(); + /* increase number of destination unreachable messages attempted to send */ + snmp_inc_icmpouttimeexcds(); + ip_addr_copy(iphdr_src, iphdr->src); + ip_output(q, NULL, &iphdr_src, ICMP_TTL, 0, IP_PROTO_ICMP);//µ÷ÓÃIP²ãº¯ÊýÊä³öICMP±¨ÎÄ + pbuf_free(q); +} + +#endif /* LWIP_ICMP */ diff --git a/variants/lwip/lwip/core/ipv4/igmp.c b/variants/lwip/lwip/core/ipv4/igmp.c new file mode 100755 index 000000000..2e937ca60 --- /dev/null +++ b/variants/lwip/lwip/core/ipv4/igmp.c @@ -0,0 +1,810 @@ +/** + * @file + * IGMP - Internet Group Management Protocol + * + */ + +/* + * Copyright (c) 2002 CITEL Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is a contribution to the lwIP TCP/IP stack. + * The Swedish Institute of Computer Science and Adam Dunkels + * are specifically granted permission to redistribute this + * source code. +*/ + +/*------------------------------------------------------------- +Note 1) +Although the rfc requires V1 AND V2 capability +we will only support v2 since now V1 is very old (August 1989) +V1 can be added if required + +a debug print and statistic have been implemented to +show this up. +------------------------------------------------------------- +------------------------------------------------------------- +Note 2) +A query for a specific group address (as opposed to ALLHOSTS) +has now been implemented as I am unsure if it is required + +a debug print and statistic have been implemented to +show this up. +------------------------------------------------------------- +------------------------------------------------------------- +Note 3) +The router alert rfc 2113 is implemented in outgoing packets +but not checked rigorously incoming +------------------------------------------------------------- +Steve Reynolds +------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------- + * RFC 988 - Host extensions for IP multicasting - V0 + * RFC 1054 - Host extensions for IP multicasting - + * RFC 1112 - Host extensions for IP multicasting - V1 + * RFC 2236 - Internet Group Management Protocol, Version 2 - V2 <- this code is based on this RFC (it's the "de facto" standard) + * RFC 3376 - Internet Group Management Protocol, Version 3 - V3 + * RFC 4604 - Using Internet Group Management Protocol Version 3... - V3+ + * RFC 2113 - IP Router Alert Option - + *----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------- + * Includes + *----------------------------------------------------------------------------*/ + +#include "lwip/opt.h" + +#if LWIP_IGMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/igmp.h" +#include "lwip/debug.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/ip.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/icmp.h" +#include "lwip/udp.h" +#include "lwip/tcp.h" +#include "lwip/stats.h" + +#include "string.h" + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +/* + * IGMP constants + */ +#define IGMP_TTL 1 +#define IGMP_MINLEN 8 +#define ROUTER_ALERT 0x9404 +#define ROUTER_ALERTLEN 4 + +/* + * IGMP message types, including version number. + */ +#define IGMP_MEMB_QUERY 0x11 /* Membership query */ +#define IGMP_V1_MEMB_REPORT 0x12 /* Ver. 1 membership report */ +#define IGMP_V2_MEMB_REPORT 0x16 /* Ver. 2 membership report */ +#define IGMP_LEAVE_GROUP 0x17 /* Leave-group message */ + +/* Group membership states */ +#define IGMP_GROUP_NON_MEMBER 0 +#define IGMP_GROUP_DELAYING_MEMBER 1 +#define IGMP_GROUP_IDLE_MEMBER 2 + +/** + * IGMP packet format. + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct igmp_msg { + PACK_STRUCT_FIELD(u8_t igmp_msgtype); + PACK_STRUCT_FIELD(u8_t igmp_maxresp); + PACK_STRUCT_FIELD(u16_t igmp_checksum); + PACK_STRUCT_FIELD(ip_addr_p_t igmp_group_address); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + + +static struct igmp_group *igmp_lookup_group(struct netif *ifp, ip_addr_t *addr)ICACHE_FLASH_ATTR; +static err_t igmp_remove_group(struct igmp_group *group)ICACHE_FLASH_ATTR; +static void igmp_timeout( struct igmp_group *group)ICACHE_FLASH_ATTR; +static void igmp_start_timer(struct igmp_group *group, u8_t max_time)ICACHE_FLASH_ATTR; +static void igmp_stop_timer(struct igmp_group *group)ICACHE_FLASH_ATTR; +static void igmp_delaying_member(struct igmp_group *group, u8_t maxresp)ICACHE_FLASH_ATTR; +static err_t igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif)ICACHE_FLASH_ATTR; +static void igmp_send(struct igmp_group *group, u8_t type)ICACHE_FLASH_ATTR; + + +static struct igmp_group* igmp_group_list; +static ip_addr_t allsystems; +static ip_addr_t allrouters; + + +/** + * Initialize the IGMP module + */ +void +igmp_init(void) +{ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_init: initializing\n")); + + IP4_ADDR(&allsystems, 224, 0, 0, 1); + IP4_ADDR(&allrouters, 224, 0, 0, 2); +} + +#ifdef LWIP_DEBUG +/** + * Dump global IGMP groups list + */ +void +igmp_dump_group_list() +{ + struct igmp_group *group = igmp_group_list; + + while (group != NULL) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_dump_group_list: [%"U32_F"] ", (u32_t)(group->group_state))); + ip_addr_debug_print(IGMP_DEBUG, &group->group_address); + LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->netif)); + group = group->next; + } + LWIP_DEBUGF(IGMP_DEBUG, ("\n")); +} +#else +#define igmp_dump_group_list() +#endif /* LWIP_DEBUG */ + +/** + * Start IGMP processing on interface + * + * @param netif network interface on which start IGMP processing + */ +err_t +igmp_start(struct netif *netif) +{ + struct igmp_group* group; + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: starting IGMP processing on if %p\n", netif)); + + group = igmp_lookup_group(netif, &allsystems); + + if (group != NULL) { + group->group_state = IGMP_GROUP_IDLE_MEMBER; + group->use++; + + /* Allow the igmp messages at the MAC level */ + if (netif->igmp_mac_filter != NULL) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: igmp_mac_filter(ADD ")); + ip_addr_debug_print(IGMP_DEBUG, &allsystems); + LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif)); + netif->igmp_mac_filter(netif, &allsystems, IGMP_ADD_MAC_FILTER); + } + + return ERR_OK; + } + + return ERR_MEM; +} + +/** + * Stop IGMP processing on interface + * + * @param netif network interface on which stop IGMP processing + */ +err_t +igmp_stop(struct netif *netif) +{ + struct igmp_group *group = igmp_group_list; + struct igmp_group *prev = NULL; + struct igmp_group *next; + + /* look for groups joined on this interface further down the list */ + while (group != NULL) { + next = group->next; + /* is it a group joined on this interface? */ + if (group->netif == netif) { + /* is it the first group of the list? */ + if (group == igmp_group_list) { + igmp_group_list = next; + } + /* is there a "previous" group defined? */ + if (prev != NULL) { + prev->next = next; + } + /* disable the group at the MAC level */ + if (netif->igmp_mac_filter != NULL) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_stop: igmp_mac_filter(DEL ")); + ip_addr_debug_print(IGMP_DEBUG, &group->group_address); + LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif)); + netif->igmp_mac_filter(netif, &(group->group_address), IGMP_DEL_MAC_FILTER); + } + /* free group */ + memp_free(MEMP_IGMP_GROUP, group); + } else { + /* change the "previous" */ + prev = group; + } + /* move to "next" */ + group = next; + } + return ERR_OK; +} + +/** + * Report IGMP memberships for this interface + * + * @param netif network interface on which report IGMP memberships + */ +void +igmp_report_groups(struct netif *netif) +{ + struct igmp_group *group = igmp_group_list; + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_report_groups: sending IGMP reports on if %p\n", netif)); + + while (group != NULL) { + if (group->netif == netif) { + igmp_delaying_member(group, IGMP_JOIN_DELAYING_MEMBER_TMR); + } + group = group->next; + } +} + +/** + * Search for a group in the global igmp_group_list + * + * @param ifp the network interface for which to look + * @param addr the group ip address to search for + * @return a struct igmp_group* if the group has been found, + * NULL if the group wasn't found. + */ +struct igmp_group * +igmp_lookfor_group(struct netif *ifp, ip_addr_t *addr) +{ + struct igmp_group *group = igmp_group_list; + + while (group != NULL) { + if ((group->netif == ifp) && (ip_addr_cmp(&(group->group_address), addr))) { + return group; + } + group = group->next; + } + + /* to be clearer, we return NULL here instead of + * 'group' (which is also NULL at this point). + */ + return NULL; +} + +/** + * Search for a specific igmp group and create a new one if not found- + * + * @param ifp the network interface for which to look + * @param addr the group ip address to search + * @return a struct igmp_group*, + * NULL on memory error. + */ +struct igmp_group * +igmp_lookup_group(struct netif *ifp, ip_addr_t *addr) +{ + struct igmp_group *group = igmp_group_list; + + /* Search if the group already exists */ + group = igmp_lookfor_group(ifp, addr); + if (group != NULL) { + /* Group already exists. */ + return group; + } + + /* Group doesn't exist yet, create a new one */ + group = (struct igmp_group *)memp_malloc(MEMP_IGMP_GROUP); + if (group != NULL) { + group->netif = ifp; + ip_addr_set(&(group->group_address), addr); + group->timer = 0; /* Not running */ + group->group_state = IGMP_GROUP_NON_MEMBER; + group->last_reporter_flag = 0; + group->use = 0; + group->next = igmp_group_list; + + igmp_group_list = group; + } + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_lookup_group: %sallocated a new group with address ", (group?"":"impossible to "))); + ip_addr_debug_print(IGMP_DEBUG, addr); + LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", ifp)); + + return group; +} + +/** + * Remove a group in the global igmp_group_list + * + * @param group the group to remove from the global igmp_group_list + * @return ERR_OK if group was removed from the list, an err_t otherwise + */ +static err_t +igmp_remove_group(struct igmp_group *group) +{ + err_t err = ERR_OK; + + /* Is it the first group? */ + if (igmp_group_list == group) { + igmp_group_list = group->next; + } else { + /* look for group further down the list */ + struct igmp_group *tmpGroup; + for (tmpGroup = igmp_group_list; tmpGroup != NULL; tmpGroup = tmpGroup->next) { + if (tmpGroup->next == group) { + tmpGroup->next = group->next; + break; + } + } + /* Group not found in the global igmp_group_list */ + if (tmpGroup == NULL) + err = ERR_ARG; + } + /* free group */ + memp_free(MEMP_IGMP_GROUP, group); + + return err; +} + +/** + * Called from ip_input() if a new IGMP packet is received. + * + * @param p received igmp packet, p->payload pointing to the ip header + * @param inp network interface on which the packet was received + * @param dest destination ip address of the igmp packet + */ +void +igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest) +{ + struct ip_hdr * iphdr; + struct igmp_msg* igmp; + struct igmp_group* group; + struct igmp_group* groupref; + + IGMP_STATS_INC(igmp.recv); + + /* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */ + iphdr = (struct ip_hdr *)p->payload; + if (pbuf_header(p, -(s16_t)(IPH_HL(iphdr) * 4)) || (p->len < IGMP_MINLEN)) { + pbuf_free(p); + IGMP_STATS_INC(igmp.lenerr); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n")); + return; + } + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message from ")); + ip_addr_debug_print(IGMP_DEBUG, &(iphdr->src)); + LWIP_DEBUGF(IGMP_DEBUG, (" to address ")); + ip_addr_debug_print(IGMP_DEBUG, &(iphdr->dest)); + LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", inp)); + + /* Now calculate and check the checksum */ + igmp = (struct igmp_msg *)p->payload; + if (inet_chksum(igmp, p->len)) { + pbuf_free(p); + IGMP_STATS_INC(igmp.chkerr); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: checksum error\n")); + return; + } + + /* Packet is ok so find an existing group */ + group = igmp_lookfor_group(inp, dest); /* use the destination IP address of incoming packet */ + + /* If group can be found or create... */ + if (!group) { + pbuf_free(p); + IGMP_STATS_INC(igmp.drop); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP frame not for us\n")); + return; + } + + /* NOW ACT ON THE INCOMING MESSAGE TYPE... */ + switch (igmp->igmp_msgtype) { + case IGMP_MEMB_QUERY: { + /* IGMP_MEMB_QUERY to the "all systems" address ? */ + if ((ip_addr_cmp(dest, &allsystems)) && ip_addr_isany(&igmp->igmp_group_address)) { + /* THIS IS THE GENERAL QUERY */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: General IGMP_MEMB_QUERY on \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); + + if (igmp->igmp_maxresp == 0) { + IGMP_STATS_INC(igmp.rx_v1); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got an all hosts query with time== 0 - this is V1 and not implemented - treat as v2\n")); + igmp->igmp_maxresp = IGMP_V1_DELAYING_MEMBER_TMR; + } else { + IGMP_STATS_INC(igmp.rx_general); + } + + groupref = igmp_group_list; + while (groupref) { + /* Do not send messages on the all systems group address! */ + if ((groupref->netif == inp) && (!(ip_addr_cmp(&(groupref->group_address), &allsystems)))) { + igmp_delaying_member(groupref, igmp->igmp_maxresp); + } + groupref = groupref->next; + } + } else { + /* IGMP_MEMB_QUERY to a specific group ? */ + if (!ip_addr_isany(&igmp->igmp_group_address)) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group ")); + ip_addr_debug_print(IGMP_DEBUG, &igmp->igmp_group_address); + if (ip_addr_cmp(dest, &allsystems)) { + ip_addr_t groupaddr; + LWIP_DEBUGF(IGMP_DEBUG, (" using \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); + /* we first need to re-look for the group since we used dest last time */ + ip_addr_copy(groupaddr, igmp->igmp_group_address); + group = igmp_lookfor_group(inp, &groupaddr); + } else { + LWIP_DEBUGF(IGMP_DEBUG, (" with the group address as destination [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); + } + + if (group != NULL) { + IGMP_STATS_INC(igmp.rx_group); + igmp_delaying_member(group, igmp->igmp_maxresp); + } else { + IGMP_STATS_INC(igmp.drop); + } + } else { + IGMP_STATS_INC(igmp.proterr); + } + } + break; + } + case IGMP_V2_MEMB_REPORT: { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_V2_MEMB_REPORT\n")); + IGMP_STATS_INC(igmp.rx_report); + if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) { + /* This is on a specific group we have already looked up */ + group->timer = 0; /* stopped */ + group->group_state = IGMP_GROUP_IDLE_MEMBER; + group->last_reporter_flag = 0; + } + break; + } + default: { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %d in state %d on group %p on if %p\n", + igmp->igmp_msgtype, group->group_state, &group, group->netif)); + IGMP_STATS_INC(igmp.proterr); + break; + } + } + + pbuf_free(p); + return; +} + +/** + * Join a group on one network interface. + * + * @param ifaddr ip address of the network interface which should join a new group + * @param groupaddr the ip address of the group which to join + * @return ERR_OK if group was joined on the netif(s), an err_t otherwise + */ +err_t +igmp_joingroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr) +{ + err_t err = ERR_VAL; /* no matching interface */ + struct igmp_group *group; + struct netif *netif; + + /* make sure it is multicast address */ + LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;); + LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;); + + /* loop through netif's */ + netif = netif_list; + while (netif != NULL) { + /* Should we join this interface ? */ + if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) { + /* find group or create a new one if not found */ + group = igmp_lookup_group(netif, groupaddr); + + if (group != NULL) { + /* This should create a new group, check the state to make sure */ + if (group->group_state != IGMP_GROUP_NON_MEMBER) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to group not in state IGMP_GROUP_NON_MEMBER\n")); + } else { + /* OK - it was new group */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to new group: ")); + ip_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, ("\n")); + + /* If first use of the group, allow the group at the MAC level */ + if ((group->use==0) && (netif->igmp_mac_filter != NULL)) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: igmp_mac_filter(ADD ")); + ip_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif)); + netif->igmp_mac_filter(netif, groupaddr, IGMP_ADD_MAC_FILTER); + } + + IGMP_STATS_INC(igmp.tx_join); + igmp_send(group, IGMP_V2_MEMB_REPORT); + + igmp_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR); + + /* Need to work out where this timer comes from */ + group->group_state = IGMP_GROUP_DELAYING_MEMBER; + } + /* Increment group use */ + group->use++; + /* Join on this interface */ + err = ERR_OK; + } else { + /* Return an error even if some network interfaces are joined */ + /** @todo undo any other netif already joined */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: Not enought memory to join to group\n")); + return ERR_MEM; + } + } + /* proceed to next network interface */ + netif = netif->next; + } + + return err; +} + +/** + * Leave a group on one network interface. + * + * @param ifaddr ip address of the network interface which should leave a group + * @param groupaddr the ip address of the group which to leave + * @return ERR_OK if group was left on the netif(s), an err_t otherwise + */ +err_t +igmp_leavegroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr) +{ + err_t err = ERR_VAL; /* no matching interface */ + struct igmp_group *group; + struct netif *netif; + + /* make sure it is multicast address */ + LWIP_ERROR("igmp_leavegroup: attempt to leave non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;); + LWIP_ERROR("igmp_leavegroup: attempt to leave allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;); + + /* loop through netif's */ + netif = netif_list; + while (netif != NULL) { + /* Should we leave this interface ? */ + if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) { + /* find group */ + group = igmp_lookfor_group(netif, groupaddr); + + if (group != NULL) { + /* Only send a leave if the flag is set according to the state diagram */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: Leaving group: ")); + ip_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, ("\n")); + + /* If there is no other use of the group */ + if (group->use <= 1) { + /* If we are the last reporter for this group */ + if (group->last_reporter_flag) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: sending leaving group\n")); + IGMP_STATS_INC(igmp.tx_leave); + igmp_send(group, IGMP_LEAVE_GROUP); + } + + /* Disable the group at the MAC level */ + if (netif->igmp_mac_filter != NULL) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: igmp_mac_filter(DEL ")); + ip_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif)); + netif->igmp_mac_filter(netif, groupaddr, IGMP_DEL_MAC_FILTER); + } + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: remove group: ")); + ip_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, ("\n")); + + /* Free the group */ + igmp_remove_group(group); + } else { + /* Decrement group use */ + group->use--; + } + /* Leave on this interface */ + err = ERR_OK; + } else { + /* It's not a fatal error on "leavegroup" */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: not member of group\n")); + } + } + /* proceed to next network interface */ + netif = netif->next; + } + + return err; +} + +/** + * The igmp timer function (both for NO_SYS=1 and =0) + * Should be called every IGMP_TMR_INTERVAL milliseconds (100 ms is default). + */ +void +igmp_tmr(void) +{ + struct igmp_group *group = igmp_group_list; + + while (group != NULL) { + if (group->timer > 0) { + group->timer--; + if (group->timer == 0) { + igmp_timeout(group); + } + } + group = group->next; + } +} + +/** + * Called if a timeout for one group is reached. + * Sends a report for this group. + * + * @param group an igmp_group for which a timeout is reached + */ +static void +igmp_timeout(struct igmp_group *group) +{ + /* If the state is IGMP_GROUP_DELAYING_MEMBER then we send a report for this group */ + if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_timeout: report membership for group with address ")); + ip_addr_debug_print(IGMP_DEBUG, &(group->group_address)); + LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->netif)); + + IGMP_STATS_INC(igmp.tx_report); + igmp_send(group, IGMP_V2_MEMB_REPORT); + } +} + +/** + * Start a timer for an igmp group + * + * @param group the igmp_group for which to start a timer + * @param max_time the time in multiples of IGMP_TMR_INTERVAL (decrease with + * every call to igmp_tmr()) + */ +static void +igmp_start_timer(struct igmp_group *group, u8_t max_time) +{ + /* ensure the input value is > 0 */ + if (max_time == 0) { + max_time = 1; + } + /* ensure the random value is > 0 */ + group->timer = (LWIP_RAND() % (max_time - 1)) + 1; +} + +/** + * Delaying membership report for a group if necessary + * + * @param group the igmp_group for which "delaying" membership report + * @param maxresp query delay + */ +static void +igmp_delaying_member(struct igmp_group *group, u8_t maxresp) +{ + if ((group->group_state == IGMP_GROUP_IDLE_MEMBER) || + ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) && + ((group->timer == 0) || (maxresp < group->timer)))) { + igmp_start_timer(group, maxresp); + group->group_state = IGMP_GROUP_DELAYING_MEMBER; + } +} + + +/** + * Sends an IP packet on a network interface. This function constructs the IP header + * and calculates the IP header checksum. If the source IP address is NULL, + * the IP address of the outgoing network interface is filled in as source address. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an IP + header and p->payload points to that IP header) + * @param src the source IP address to send from (if src == IP_ADDR_ANY, the + * IP address of the netif used to send is used as source address) + * @param dest the destination IP address to send the packet to + * @param ttl the TTL value to be set in the IP header + * @param proto the PROTOCOL to be set in the IP header + * @param netif the netif on which to send this packet + * @return ERR_OK if the packet was sent OK + * ERR_BUF if p doesn't have enough space for IP/LINK headers + * returns errors returned by netif->output + */ +static err_t +igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif) +{ + /* This is the "router alert" option */ + u16_t ra[2]; + ra[0] = PP_HTONS(ROUTER_ALERT); + ra[1] = 0x0000; /* Router shall examine packet */ + IGMP_STATS_INC(igmp.xmit); + return ip_output_if_opt(p, src, dest, IGMP_TTL, 0, IP_PROTO_IGMP, netif, ra, ROUTER_ALERTLEN); +} + +/** + * Send an igmp packet to a specific group. + * + * @param group the group to which to send the packet + * @param type the type of igmp packet to send + */ +static void +igmp_send(struct igmp_group *group, u8_t type) +{ + struct pbuf* p = NULL; + struct igmp_msg* igmp = NULL; + ip_addr_t src = *IP_ADDR_ANY; + ip_addr_t* dest = NULL; + + /* IP header + "router alert" option + IGMP header */ + p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM); + + if (p) { + igmp = (struct igmp_msg *)p->payload; + LWIP_ASSERT("igmp_send: check that first pbuf can hold struct igmp_msg", + (p->len >= sizeof(struct igmp_msg))); + ip_addr_copy(src, group->netif->ip_addr); + + if (type == IGMP_V2_MEMB_REPORT) { + dest = &(group->group_address); + ip_addr_copy(igmp->igmp_group_address, group->group_address); + group->last_reporter_flag = 1; /* Remember we were the last to report */ + } else { + if (type == IGMP_LEAVE_GROUP) { + dest = &allrouters; + ip_addr_copy(igmp->igmp_group_address, group->group_address); + } + } + + if ((type == IGMP_V2_MEMB_REPORT) || (type == IGMP_LEAVE_GROUP)) { + igmp->igmp_msgtype = type; + igmp->igmp_maxresp = 0; + igmp->igmp_checksum = 0; + igmp->igmp_checksum = inet_chksum(igmp, IGMP_MINLEN); + + igmp_ip_output_if(p, &src, dest, group->netif); + } + + pbuf_free(p); + } else { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_send: not enough memory for igmp_send\n")); + IGMP_STATS_INC(igmp.memerr); + } +} + +#endif /* LWIP_IGMP */ diff --git a/variants/lwip/lwip/core/ipv4/inet.c b/variants/lwip/lwip/core/ipv4/inet.c new file mode 100755 index 000000000..e283a5766 --- /dev/null +++ b/variants/lwip/lwip/core/ipv4/inet.c @@ -0,0 +1,42 @@ +/** + * @file + * Functions common to all TCP/IPv4 modules, such as the byte order functions. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/inet.h" + diff --git a/variants/lwip/lwip/core/ipv4/inet_chksum.c b/variants/lwip/lwip/core/ipv4/inet_chksum.c new file mode 100755 index 000000000..bfcc40d5b --- /dev/null +++ b/variants/lwip/lwip/core/ipv4/inet_chksum.c @@ -0,0 +1,450 @@ +/** + * @file + * Incluse internet checksum functions. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/inet_chksum.h" +#include "lwip/def.h" + +#include +#include + +/* These are some reference implementations of the checksum algorithm, with the + * aim of being simple, correct and fully portable. Checksumming is the + * first thing you would want to optimize for your platform. If you create + * your own version, link it in and in your cc.h put: + * + * #define LWIP_CHKSUM + * + * Or you can select from the implementations below by defining + * LWIP_CHKSUM_ALGORITHM to 1, 2 or 3. + */ + +#ifndef LWIP_CHKSUM +# define LWIP_CHKSUM lwip_standard_chksum +# ifndef LWIP_CHKSUM_ALGORITHM +# define LWIP_CHKSUM_ALGORITHM 2 +# endif +#endif +/* If none set: */ +#ifndef LWIP_CHKSUM_ALGORITHM +# define LWIP_CHKSUM_ALGORITHM 0 +#endif + +#if (LWIP_CHKSUM_ALGORITHM == 1) /* Version #1 */ +/** + * lwip checksum + * + * @param dataptr points to start of data to be summed at any boundary + * @param len length of data to be summed + * @return host order (!) lwip checksum (non-inverted Internet sum) + * + * @note accumulator size limits summable length to 64k + * @note host endianess is irrelevant (p3 RFC1071) + */ +static u16_t ICACHE_FLASH_ATTR +lwip_standard_chksum(void *dataptr, u16_t len) +{ + u32_t acc; + u16_t src; + u8_t *octetptr; + + acc = 0; + /* dataptr may be at odd or even addresses */ + octetptr = (u8_t*)dataptr; + while (len > 1) { + /* declare first octet as most significant + thus assume network order, ignoring host order */ + src = (*octetptr) << 8; + octetptr++; + /* declare second octet as least significant */ + src |= (*octetptr); + octetptr++; + acc += src; + len -= 2; + } + if (len > 0) { + /* accumulate remaining octet */ + src = (*octetptr) << 8; + acc += src; + } + /* add deferred carry bits */ + acc = (acc >> 16) + (acc & 0x0000ffffUL); + if ((acc & 0xffff0000UL) != 0) { + acc = (acc >> 16) + (acc & 0x0000ffffUL); + } + /* This maybe a little confusing: reorder sum using htons() + instead of ntohs() since it has a little less call overhead. + The caller must invert bits for Internet sum ! */ + return htons((u16_t)acc); +} +#endif + +#if (LWIP_CHKSUM_ALGORITHM == 2) /* Alternative version #2 */ +/* + * Curt McDowell + * Broadcom Corp. + * csm@broadcom.com + * + * IP checksum two bytes at a time with support for + * unaligned buffer. + * Works for len up to and including 0x20000. + * by Curt McDowell, Broadcom Corp. 12/08/2005 + * + * @param dataptr points to start of data to be summed at any boundary + * @param len length of data to be summed + * @return host order (!) lwip checksum (non-inverted Internet sum) + */ + +static u16_t ICACHE_FLASH_ATTR +lwip_standard_chksum(void *dataptr, int len) +{ + u8_t *pb = (u8_t *)dataptr; + u16_t *ps, t = 0; + u32_t sum = 0; + int odd = ((mem_ptr_t)pb & 1); + + /* Get aligned to u16_t */ + if (odd && len > 0) { + ((u8_t *)&t)[1] = *pb++; + len--; + } + + /* Add the bulk of the data */ + ps = (u16_t *)(void *)pb; + while (len > 1) { + sum += *ps++; + len -= 2; + } + + /* Consume left-over byte, if any */ + if (len > 0) { + ((u8_t *)&t)[0] = *(u8_t *)ps; + } + + /* Add end bytes */ + sum += t; + + /* Fold 32-bit sum to 16 bits + calling this twice is propably faster than if statements... */ + sum = FOLD_U32T(sum); + sum = FOLD_U32T(sum); + + /* Swap if alignment was odd */ + if (odd) { + sum = SWAP_BYTES_IN_WORD(sum); + } + + return (u16_t)sum; +} +#endif + +#if (LWIP_CHKSUM_ALGORITHM == 3) /* Alternative version #3 */ +/** + * An optimized checksum routine. Basically, it uses loop-unrolling on + * the checksum loop, treating the head and tail bytes specially, whereas + * the inner loop acts on 8 bytes at a time. + * + * @arg start of buffer to be checksummed. May be an odd byte address. + * @len number of bytes in the buffer to be checksummed. + * @return host order (!) lwip checksum (non-inverted Internet sum) + * + * by Curt McDowell, Broadcom Corp. December 8th, 2005 + */ + +static u16_t ICACHE_FLASH_ATTR +lwip_standard_chksum(void *dataptr, int len) +{ + u8_t *pb = (u8_t *)dataptr; + u16_t *ps, t = 0; + u32_t *pl; + u32_t sum = 0, tmp; + /* starts at odd byte address? */ + int odd = ((mem_ptr_t)pb & 1); + + if (odd && len > 0) { + ((u8_t *)&t)[1] = *pb++; + len--; + } + + ps = (u16_t *)pb; + + if (((mem_ptr_t)ps & 3) && len > 1) { + sum += *ps++; + len -= 2; + } + + pl = (u32_t *)ps; + + while (len > 7) { + tmp = sum + *pl++; /* ping */ + if (tmp < sum) { + tmp++; /* add back carry */ + } + + sum = tmp + *pl++; /* pong */ + if (sum < tmp) { + sum++; /* add back carry */ + } + + len -= 8; + } + + /* make room in upper bits */ + sum = FOLD_U32T(sum); + + ps = (u16_t *)pl; + + /* 16-bit aligned word remaining? */ + while (len > 1) { + sum += *ps++; + len -= 2; + } + + /* dangling tail byte remaining? */ + if (len > 0) { /* include odd byte */ + ((u8_t *)&t)[0] = *(u8_t *)ps; + } + + sum += t; /* add end bytes */ + + /* Fold 32-bit sum to 16 bits + calling this twice is propably faster than if statements... */ + sum = FOLD_U32T(sum); + sum = FOLD_U32T(sum); + + if (odd) { + sum = SWAP_BYTES_IN_WORD(sum); + } + + return (u16_t)sum; +} +#endif + +/* inet_chksum_pseudo: + * + * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain. + * IP addresses are expected to be in network byte order. + * + * @param p chain of pbufs over that a checksum should be calculated (ip data part) + * @param src source ip address (used for checksum of pseudo header) + * @param dst destination ip address (used for checksum of pseudo header) + * @param proto ip protocol (used for checksum of pseudo header) + * @param proto_len length of the ip data part (used for checksum of pseudo header) + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +inet_chksum_pseudo(struct pbuf *p, + ip_addr_t *src, ip_addr_t *dest, + u8_t proto, u16_t proto_len) +{ + u32_t acc; + u32_t addr; + struct pbuf *q; + u8_t swapped; + + acc = 0; + swapped = 0; + /* iterate through all pbuf in chain */ + for(q = p; q != NULL; q = q->next) { + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n", + (void *)q, (void *)q->next)); + acc += LWIP_CHKSUM(q->payload, q->len); + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/ + /* just executing this next line is probably faster that the if statement needed + to check whether we really need to execute it, and does no harm */ + acc = FOLD_U32T(acc); + if (q->len % 2 != 0) { + swapped = 1 - swapped; + acc = SWAP_BYTES_IN_WORD(acc); + } + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/ + } + + if (swapped) { + acc = SWAP_BYTES_IN_WORD(acc); + } + addr = ip4_addr_get_u32(src); + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + addr = ip4_addr_get_u32(dest); + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + acc += (u32_t)htons((u16_t)proto); + acc += (u32_t)htons(proto_len); + + /* Fold 32-bit sum to 16 bits + calling this twice is propably faster than if statements... */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc)); + return (u16_t)~(acc & 0xffffUL); +} + +/* inet_chksum_pseudo: + * + * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain. + * IP addresses are expected to be in network byte order. + * + * @param p chain of pbufs over that a checksum should be calculated (ip data part) + * @param src source ip address (used for checksum of pseudo header) + * @param dst destination ip address (used for checksum of pseudo header) + * @param proto ip protocol (used for checksum of pseudo header) + * @param proto_len length of the ip data part (used for checksum of pseudo header) + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +inet_chksum_pseudo_partial(struct pbuf *p, + ip_addr_t *src, ip_addr_t *dest, + u8_t proto, u16_t proto_len, u16_t chksum_len) +{ + u32_t acc; + u32_t addr; + struct pbuf *q; + u8_t swapped; + u16_t chklen; + + acc = 0; + swapped = 0; + /* iterate through all pbuf in chain */ + for(q = p; (q != NULL) && (chksum_len > 0); q = q->next) { + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n", + (void *)q, (void *)q->next)); + chklen = q->len; + if (chklen > chksum_len) { + chklen = chksum_len; + } + acc += LWIP_CHKSUM(q->payload, chklen); + chksum_len -= chklen; + LWIP_ASSERT("delete me", chksum_len < 0x7fff); + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/ + /* fold the upper bit down */ + acc = FOLD_U32T(acc); + if (q->len % 2 != 0) { + swapped = 1 - swapped; + acc = SWAP_BYTES_IN_WORD(acc); + } + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/ + } + + if (swapped) { + acc = SWAP_BYTES_IN_WORD(acc); + } + addr = ip4_addr_get_u32(src); + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + addr = ip4_addr_get_u32(dest); + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + acc += (u32_t)htons((u16_t)proto); + acc += (u32_t)htons(proto_len); + + /* Fold 32-bit sum to 16 bits + calling this twice is propably faster than if statements... */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc)); + return (u16_t)~(acc & 0xffffUL); +} + +/* inet_chksum: + * + * Calculates the Internet checksum over a portion of memory. Used primarily for IP + * and ICMP. + * + * @param dataptr start of the buffer to calculate the checksum (no alignment needed) + * @param len length of the buffer to calculate the checksum + * @return checksum (as u16_t) to be saved directly in the protocol header + */ + +u16_t +inet_chksum(void *dataptr, u16_t len) +{ + return ~LWIP_CHKSUM(dataptr, len); +} + +/** + * Calculate a checksum over a chain of pbufs (without pseudo-header, much like + * inet_chksum only pbufs are used). + * + * @param p pbuf chain over that the checksum should be calculated + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +inet_chksum_pbuf(struct pbuf *p) +{ + u32_t acc; + struct pbuf *q; + u8_t swapped; + + acc = 0; + swapped = 0; + for(q = p; q != NULL; q = q->next) { + acc += LWIP_CHKSUM(q->payload, q->len); + acc = FOLD_U32T(acc); + if (q->len % 2 != 0) { + swapped = 1 - swapped; + acc = SWAP_BYTES_IN_WORD(acc); + } + } + + if (swapped) { + acc = SWAP_BYTES_IN_WORD(acc); + } + return (u16_t)~(acc & 0xffffUL); +} + +/* These are some implementations for LWIP_CHKSUM_COPY, which copies data + * like MEMCPY but generates a checksum at the same time. Since this is a + * performance-sensitive function, you might want to create your own version + * in assembly targeted at your hardware by defining it in lwipopts.h: + * #define LWIP_CHKSUM_COPY(dst, src, len) your_chksum_copy(dst, src, len) + */ + +#if (LWIP_CHKSUM_COPY_ALGORITHM == 1) /* Version #1 */ +/** Safe but slow: first call MEMCPY, then call LWIP_CHKSUM. + * For architectures with big caches, data might still be in cache when + * generating the checksum after copying. + */ +u16_t +lwip_chksum_copy(void *dst, const void *src, u16_t len) +{ + MEMCPY(dst, src, len); + return LWIP_CHKSUM(dst, len); +} +#endif /* (LWIP_CHKSUM_COPY_ALGORITHM == 1) */ diff --git a/variants/lwip/lwip/core/ipv4/ip.c b/variants/lwip/lwip/core/ipv4/ip.c new file mode 100755 index 000000000..89d3f11cf --- /dev/null +++ b/variants/lwip/lwip/core/ipv4/ip.c @@ -0,0 +1,910 @@ +/** + * @file + * This is the IPv4 layer implementation for incoming and outgoing IP traffic. + * + * @see ip_frag.c + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" +#include "lwip/ip.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/ip_frag.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/icmp.h" +#include "lwip/igmp.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/tcp_impl.h" +#include "lwip/snmp.h" +#include "lwip/dhcp.h" +#include "lwip/autoip.h" +#include "lwip/stats.h" +#include "arch/perf.h" + +#include + +/** Set this to 0 in the rare case of wanting to call an extra function to + * generate the IP checksum (in contrast to calculating it on-the-fly). */ +#ifndef LWIP_INLINE_IP_CHKSUM +#define LWIP_INLINE_IP_CHKSUM 1 +#endif +#if LWIP_INLINE_IP_CHKSUM && CHECKSUM_GEN_IP +#define CHECKSUM_GEN_IP_INLINE 1 +#else +#define CHECKSUM_GEN_IP_INLINE 0 +#endif + +#if LWIP_DHCP || defined(LWIP_IP_ACCEPT_UDP_PORT) +#define IP_ACCEPT_LINK_LAYER_ADDRESSING 1 + +/** Some defines for DHCP to let link-layer-addressed packets through while the + * netif is down. + * To use this in your own application/protocol, define LWIP_IP_ACCEPT_UDP_PORT + * to return 1 if the port is accepted and 0 if the port is not accepted. + */ +#if LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) +/* accept DHCP client port and custom port */ +#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) (((port) == PP_NTOHS(DHCP_CLIENT_PORT)) \ + || (LWIP_IP_ACCEPT_UDP_PORT(port))) +#elif defined(LWIP_IP_ACCEPT_UDP_PORT) /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */ +/* accept custom port only */ +#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) (LWIP_IP_ACCEPT_UDP_PORT(dst_port)) +#else /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */ +/* accept DHCP client port only */ +#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) ((port) == PP_NTOHS(DHCP_CLIENT_PORT)) +#endif /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */ + +#else /* LWIP_DHCP */ +#define IP_ACCEPT_LINK_LAYER_ADDRESSING 0 +#endif /* LWIP_DHCP */ + +/** + * The interface that provided the packet for the current callback + * invocation. + */ +struct netif *current_netif; + +/** + * Header of the input packet currently being processed. + */ +const struct ip_hdr *current_header; +/** Source IP address of current_header */ +ip_addr_t current_iphdr_src; +/** Destination IP address of current_header */ +ip_addr_t current_iphdr_dest; + +/** The IP header ID of the next outgoing IP packet */ +static u16_t ip_id; + +/** + * Finds the appropriate network interface for a given IP address. It + * searches the list of network interfaces linearly. A match is found + * if the masked IP address of the network interface equals the masked + * IP address given to the function. + * + * @param dest the destination IP address for which to find the route + * @return the netif on which to send to reach dest + */ +struct netif * +ip_route(ip_addr_t *dest) +{ + struct netif *netif; + + /* iterate through netifs */ + for(netif = netif_list; netif != NULL; netif = netif->next) { + /* network mask matches? */ + if (netif_is_up(netif)) { + if (ip_addr_netcmp(dest, &(netif->ip_addr), &(netif->netmask))) { + /* return netif on which to forward IP packet */ + return netif; + } + } + } + /* iterate through netifs */ + for(netif = netif_list; netif != NULL; netif = netif->next) { + /* network mask matches? */ + if (netif_is_up(netif)) { + if (!ip_addr_isbroadcast(dest, netif) && netif == (struct netif *)eagle_lwip_getif(0)) { + return netif; + } + } + } + if ((netif_default == NULL) || (!netif_is_up(netif_default))) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_route: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest))); + IP_STATS_INC(ip.rterr); + snmp_inc_ipoutnoroutes(); + return NULL; + } + /* no matching netif found, use default netif */ + return netif_default; +} + +/** + * Finds the appropriate network interface for a source IP address. It + * searches the list of network interfaces linearly. A match is found + * if the masked IP address of the network interface equals the masked + * IP address given to the function. + * + * @param source the sourcination IP address for which to find the route + * @return the netif on which to send to reach source + */ + +struct netif *ICACHE_FLASH_ATTR +ip_router(ip_addr_t *dest, ip_addr_t *source){ + struct netif *netif; + /* iterate through netifs */ + for(netif = netif_list; netif != NULL; netif = netif->next) { + /* network mask matches? */ + + if (netif_is_up(netif)) { + if (ip_addr_netcmp(dest, &(netif->ip_addr), &(netif->netmask))) { + /* return netif on which to forward IP packet */ + return netif; + } + } + + if (netif_is_up(netif)) { + if (ip_addr_netcmp(source, &(netif->ip_addr), &(netif->netmask))) { + /* return netif on which to forward IP packet */ + return netif; + } + } + } + + if ((netif_default == NULL) || (!netif_is_up(netif_default))) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_route: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest))); + IP_STATS_INC(ip.rterr); + snmp_inc_ipoutnoroutes(); + return NULL; + } + /* no matching netif found, use default netif */ + os_printf("ip_router %d %p\n", __LINE__, netif_default); + return netif_default; +} + +#if IP_FORWARD +/** + * Forwards an IP packet. It finds an appropriate route for the + * packet, decrements the TTL value of the packet, adjusts the + * checksum and outputs the packet on the appropriate interface. + * + * @param p the packet to forward (p->payload points to IP header) + * @param iphdr the IP header of the input packet + * @param inp the netif on which this packet was received + */ +static void ICACHE_FLASH_ATTR +ip_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp) +{ + struct netif *netif; + + PERF_START; + + /* RFC3927 2.7: do not forward link-local addresses */ + if (ip_addr_islinklocal(¤t_iphdr_dest)) { + LWIP_DEBUGF(IP_DEBUG, ("ip_forward: not forwarding LLA %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(¤t_iphdr_dest), ip4_addr2_16(¤t_iphdr_dest), + ip4_addr3_16(¤t_iphdr_dest), ip4_addr4_16(¤t_iphdr_dest))); + goto return_noroute; + } + + /* Find network interface where to forward this IP packet to. */ + netif = ip_route(¤t_iphdr_dest); + if (netif == NULL) { + LWIP_DEBUGF(IP_DEBUG, ("ip_forward: no forwarding route for %"U16_F".%"U16_F".%"U16_F".%"U16_F" found\n", + ip4_addr1_16(¤t_iphdr_dest), ip4_addr2_16(¤t_iphdr_dest), + ip4_addr3_16(¤t_iphdr_dest), ip4_addr4_16(¤t_iphdr_dest))); + goto return_noroute; + } + /* Do not forward packets onto the same network interface on which + * they arrived. */ + if (netif == inp) { + LWIP_DEBUGF(IP_DEBUG, ("ip_forward: not bouncing packets back on incoming interface.\n")); + goto return_noroute; + } + + /* decrement TTL */ + IPH_TTL_SET(iphdr, IPH_TTL(iphdr) - 1); + /* send ICMP if TTL == 0 */ + if (IPH_TTL(iphdr) == 0) { + snmp_inc_ipinhdrerrors(); +#if LWIP_ICMP + /* Don't send ICMP messages in response to ICMP messages */ + if (IPH_PROTO(iphdr) != IP_PROTO_ICMP) { + icmp_time_exceeded(p, ICMP_TE_TTL); + } +#endif /* LWIP_ICMP */ + return; + } + + /* Incrementally update the IP checksum. */ + if (IPH_CHKSUM(iphdr) >= PP_HTONS(0xffff - 0x100)) { + IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + PP_HTONS(0x100) + 1); + } else { + IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + PP_HTONS(0x100)); + } + + LWIP_DEBUGF(IP_DEBUG, ("ip_forward: forwarding packet to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(¤t_iphdr_dest), ip4_addr2_16(¤t_iphdr_dest), + ip4_addr3_16(¤t_iphdr_dest), ip4_addr4_16(¤t_iphdr_dest))); + + IP_STATS_INC(ip.fw); + IP_STATS_INC(ip.xmit); + snmp_inc_ipforwdatagrams(); + + PERF_STOP("ip_forward"); + /* transmit pbuf on chosen interface */ + netif->output(netif, p, ¤t_iphdr_dest); + return; +return_noroute: + snmp_inc_ipoutnoroutes(); +} +#endif /* IP_FORWARD */ + +/** + * This function is called by the network interface device driver when + * an IP packet is received. The function does the basic checks of the + * IP header such as packet size being at least larger than the header + * size etc. If the packet was not destined for us, the packet is + * forwarded (using ip_forward). The IP checksum is always checked. + * + * Finally, the packet is sent to the upper layer protocol input function. + * + * @param p the received IP packet (p->payload points to IP header) + * @param inp the netif on which this packet was received + * @return ERR_OK if the packet was processed (could return ERR_* if it wasn't + * processed, but currently always returns ERR_OK) + */ +err_t +ip_input(struct pbuf *p, struct netif *inp) +{ + struct ip_hdr *iphdr; + struct netif *netif; + u16_t iphdr_hlen; + u16_t iphdr_len; +#if IP_ACCEPT_LINK_LAYER_ADDRESSING + int check_ip_src=1; +#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */ + + IP_STATS_INC(ip.recv); + snmp_inc_ipinreceives(); + + /* identify the IP header */ + iphdr = (struct ip_hdr *)p->payload; + if (IPH_V(iphdr) != 4) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_WARNING, ("IP packet dropped due to bad version number %"U16_F"\n", IPH_V(iphdr))); + ip_debug_print(p); + pbuf_free(p); + IP_STATS_INC(ip.err); + IP_STATS_INC(ip.drop); + snmp_inc_ipinhdrerrors(); + return ERR_OK; + } + + /* obtain IP header length in number of 32-bit words */ + iphdr_hlen = IPH_HL(iphdr); + /* calculate IP header length in bytes */ + iphdr_hlen *= 4; + /* obtain ip length in bytes */ + iphdr_len = ntohs(IPH_LEN(iphdr)); + + /* header length exceeds first pbuf length, or ip length exceeds total pbuf length? */ + if ((iphdr_hlen > p->len) || (iphdr_len > p->tot_len)) { + if (iphdr_hlen > p->len) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IP header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet dropped.\n", + iphdr_hlen, p->len)); + } + if (iphdr_len > p->tot_len) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IP (len %"U16_F") is longer than pbuf (len %"U16_F"), IP packet dropped.\n", + iphdr_len, p->tot_len)); + } + /* free (drop) packet pbufs */ + pbuf_free(p); + IP_STATS_INC(ip.lenerr); + IP_STATS_INC(ip.drop); + snmp_inc_ipindiscards(); + return ERR_OK; + } + + /* verify checksum */ +#if CHECKSUM_CHECK_IP + if (inet_chksum(iphdr, iphdr_hlen) != 0) { + + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("Checksum (0x%"X16_F") failed, IP packet dropped.\n", inet_chksum(iphdr, iphdr_hlen))); + ip_debug_print(p); + pbuf_free(p); + IP_STATS_INC(ip.chkerr); + IP_STATS_INC(ip.drop); + snmp_inc_ipinhdrerrors(); + return ERR_OK; + } +#endif + + /* Trim pbuf. This should have been done at the netif layer, + * but we'll do it anyway just to be sure that its done. */ + pbuf_realloc(p, iphdr_len); + + /* copy IP addresses to aligned ip_addr_t */ + ip_addr_copy(current_iphdr_dest, iphdr->dest); + ip_addr_copy(current_iphdr_src, iphdr->src); + + /* match packet against an interface, i.e. is this packet for us? */ +#if LWIP_IGMP + if (ip_addr_ismulticast(¤t_iphdr_dest)) { + if ((inp->flags & NETIF_FLAG_IGMP) && (igmp_lookfor_group(inp, ¤t_iphdr_dest))) { + netif = inp; + } else { + netif = NULL; + } + } else +#endif /* LWIP_IGMP */ + { + /* start trying with inp. if that's not acceptable, start walking the + list of configured netifs. + 'first' is used as a boolean to mark whether we started walking the list */ + int first = 1; + netif = inp; + do { + LWIP_DEBUGF(IP_DEBUG, ("ip_input: iphdr->dest 0x%"X32_F" netif->ip_addr 0x%"X32_F" (0x%"X32_F", 0x%"X32_F", 0x%"X32_F")\n", + ip4_addr_get_u32(&iphdr->dest), ip4_addr_get_u32(&netif->ip_addr), + ip4_addr_get_u32(&iphdr->dest) & ip4_addr_get_u32(&netif->netmask), + ip4_addr_get_u32(&netif->ip_addr) & ip4_addr_get_u32(&netif->netmask), + ip4_addr_get_u32(&iphdr->dest) & ~ip4_addr_get_u32(&netif->netmask))); + + /* interface is up and configured? */ + if ((netif_is_up(netif)) && (!ip_addr_isany(&(netif->ip_addr)))) { + /* unicast to this interface address? */ + if (ip_addr_cmp(¤t_iphdr_dest, &(netif->ip_addr)) || + /* or broadcast on this interface network address? */ + ip_addr_isbroadcast(¤t_iphdr_dest, netif)) { + LWIP_DEBUGF(IP_DEBUG, ("ip_input: packet accepted on interface %c%c\n", + netif->name[0], netif->name[1])); + /* break out of for loop */ + break; + } +#if LWIP_AUTOIP + /* connections to link-local addresses must persist after changing + the netif's address (RFC3927 ch. 1.9) */ + if ((netif->autoip != NULL) && + ip_addr_cmp(¤t_iphdr_dest, &(netif->autoip->llipaddr))) { + LWIP_DEBUGF(IP_DEBUG, ("ip_input: LLA packet accepted on interface %c%c\n", + netif->name[0], netif->name[1])); + /* break out of for loop */ + break; + } +#endif /* LWIP_AUTOIP */ + } + if (first) { + first = 0; + netif = netif_list; + } else { + netif = netif->next; + } + if (netif == inp) { + netif = netif->next; + } + } while(netif != NULL); + } + +#if IP_ACCEPT_LINK_LAYER_ADDRESSING + /* Pass DHCP messages regardless of destination address. DHCP traffic is addressed + * using link layer addressing (such as Ethernet MAC) so we must not filter on IP. + * According to RFC 1542 section 3.1.1, referred by RFC 2131). + * + * If you want to accept private broadcast communication while a netif is down, + * define LWIP_IP_ACCEPT_UDP_PORT(dst_port), e.g.: + * + * #define LWIP_IP_ACCEPT_UDP_PORT(dst_port) ((dst_port) == PP_NTOHS(12345)) + */ + if (netif == NULL) { + /* remote port is DHCP server? */ + if (IPH_PROTO(iphdr) == IP_PROTO_UDP) { + struct udp_hdr *udphdr = (struct udp_hdr *)((u8_t *)iphdr + iphdr_hlen); + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: UDP packet to DHCP client port %"U16_F"\n", + ntohs(udphdr->dest))); + if (IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(udphdr->dest)) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: DHCP packet accepted.\n")); + netif = inp; + check_ip_src = 0; + } + } + } +#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */ + + /* broadcast or multicast packet source address? Compliant with RFC 1122: 3.2.1.3 */ +#if IP_ACCEPT_LINK_LAYER_ADDRESSING + /* DHCP servers need 0.0.0.0 to be allowed as source address (RFC 1.1.2.2: 3.2.1.3/a) */ + if (check_ip_src && !ip_addr_isany(¤t_iphdr_src)) +#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */ + { if ((ip_addr_isbroadcast(¤t_iphdr_src, inp)) || + (ip_addr_ismulticast(¤t_iphdr_src))) { + /* packet source is not valid */ + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("ip_input: packet source is not valid.\n")); + /* free (drop) packet pbufs */ + pbuf_free(p); + IP_STATS_INC(ip.drop); + snmp_inc_ipinaddrerrors(); + snmp_inc_ipindiscards(); + return ERR_OK; + } + } + + /* packet not for us? */ + if (netif == NULL) { + /* packet not for us, route or discard */ + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: packet not for us.\n")); +#if IP_FORWARD + /* non-broadcast packet? */ + if (!ip_addr_isbroadcast(¤t_iphdr_dest, inp)) { + /* try to forward IP packet on (other) interfaces */ + ip_forward(p, iphdr, inp); + } else +#endif /* IP_FORWARD */ + { + snmp_inc_ipinaddrerrors(); + snmp_inc_ipindiscards(); + } + pbuf_free(p); + return ERR_OK; + } + /* packet consists of multiple fragments? */ + if ((IPH_OFFSET(iphdr) & PP_HTONS(IP_OFFMASK | IP_MF)) != 0) { +#if IP_REASSEMBLY /* packet fragment reassembly code present? */ + LWIP_DEBUGF(IP_DEBUG, ("IP packet is a fragment (id=0x%04"X16_F" tot_len=%"U16_F" len=%"U16_F" MF=%"U16_F" offset=%"U16_F"), calling ip_reass()\n", + ntohs(IPH_ID(iphdr)), p->tot_len, ntohs(IPH_LEN(iphdr)), !!(IPH_OFFSET(iphdr) & PP_HTONS(IP_MF)), (ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)*8)); + /* reassemble the packet*/ + p = ip_reass(p); + /* packet not fully reassembled yet? */ + if (p == NULL) { + return ERR_OK; + } + iphdr = (struct ip_hdr *)p->payload; +#else /* IP_REASSEMBLY == 0, no packet fragment reassembly code present */ + pbuf_free(p); + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IP packet dropped since it was fragmented (0x%"X16_F") (while IP_REASSEMBLY == 0).\n", + ntohs(IPH_OFFSET(iphdr)))); + IP_STATS_INC(ip.opterr); + IP_STATS_INC(ip.drop); + /* unsupported protocol feature */ + snmp_inc_ipinunknownprotos(); + return ERR_OK; +#endif /* IP_REASSEMBLY */ + } + +#if IP_OPTIONS_ALLOWED == 0 /* no support for IP options in the IP header? */ + +#if LWIP_IGMP + /* there is an extra "router alert" option in IGMP messages which we allow for but do not police */ + if((iphdr_hlen > IP_HLEN) && (IPH_PROTO(iphdr) != IP_PROTO_IGMP)) { +#else + if (iphdr_hlen > IP_HLEN) { +#endif /* LWIP_IGMP */ + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IP packet dropped since there were IP options (while IP_OPTIONS_ALLOWED == 0).\n")); + pbuf_free(p); + IP_STATS_INC(ip.opterr); + IP_STATS_INC(ip.drop); + /* unsupported protocol feature */ + snmp_inc_ipinunknownprotos(); + return ERR_OK; + } +#endif /* IP_OPTIONS_ALLOWED == 0 */ + + /* send to upper layers */ + LWIP_DEBUGF(IP_DEBUG, ("ip_input: \n")); + ip_debug_print(p); + LWIP_DEBUGF(IP_DEBUG, ("ip_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len)); + + current_netif = inp; + current_header = iphdr; + +#if LWIP_RAW + /* raw input did not eat the packet? */ + if (raw_input(p, inp) == 0) +#endif /* LWIP_RAW */ + { + + switch (IPH_PROTO(iphdr)) { +#if LWIP_UDP + case IP_PROTO_UDP: +#if LWIP_UDPLITE + case IP_PROTO_UDPLITE: +#endif /* LWIP_UDPLITE */ + snmp_inc_ipindelivers(); + udp_input(p, inp); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case IP_PROTO_TCP: + snmp_inc_ipindelivers(); + tcp_input(p, inp); + break; +#endif /* LWIP_TCP */ +#if LWIP_ICMP + case IP_PROTO_ICMP: + snmp_inc_ipindelivers(); + icmp_input(p, inp); + break; +#endif /* LWIP_ICMP */ +#if LWIP_IGMP + case IP_PROTO_IGMP: + igmp_input(p, inp, ¤t_iphdr_dest); + break; +#endif /* LWIP_IGMP */ + default: +#if LWIP_ICMP + /* send ICMP destination protocol unreachable unless is was a broadcast */ + if (!ip_addr_isbroadcast(¤t_iphdr_dest, inp) && + !ip_addr_ismulticast(¤t_iphdr_dest)) { + p->payload = iphdr; + icmp_dest_unreach(p, ICMP_DUR_PROTO); + } +#endif /* LWIP_ICMP */ + pbuf_free(p); + + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("Unsupported transport protocol %"U16_F"\n", IPH_PROTO(iphdr))); + + IP_STATS_INC(ip.proterr); + IP_STATS_INC(ip.drop); + snmp_inc_ipinunknownprotos(); + } + } + + current_netif = NULL; + current_header = NULL; + ip_addr_set_any(¤t_iphdr_src); + ip_addr_set_any(¤t_iphdr_dest); + + return ERR_OK; +} + +/** + * Sends an IP packet on a network interface. This function constructs + * the IP header and calculates the IP header checksum. If the source + * IP address is NULL, the IP address of the outgoing network + * interface is filled in as source address. + * If the destination IP address is IP_HDRINCL, p is assumed to already + * include an IP header and p->payload points to it instead of the data. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an IP + header and p->payload points to that IP header) + * @param src the source IP address to send from (if src == IP_ADDR_ANY, the + * IP address of the netif used to send is used as source address) + * @param dest the destination IP address to send the packet to + * @param ttl the TTL value to be set in the IP header + * @param tos the TOS value to be set in the IP header + * @param proto the PROTOCOL to be set in the IP header + * @param netif the netif on which to send this packet + * @return ERR_OK if the packet was sent OK + * ERR_BUF if p doesn't have enough space for IP/LINK headers + * returns errors returned by netif->output + * + * @note ip_id: RFC791 "some host may be able to simply use + * unique identifiers independent of destination" + */ +err_t +ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, + u8_t proto, struct netif *netif) +{ +#if IP_OPTIONS_SEND + return ip_output_if_opt(p, src, dest, ttl, tos, proto, netif, NULL, 0); +} + +/** + * Same as ip_output_if() but with the possibility to include IP options: + * + * @ param ip_options pointer to the IP options, copied into the IP header + * @ param optlen length of ip_options + */ +err_t ip_output_if_opt(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options, + u16_t optlen) +{ +#endif /* IP_OPTIONS_SEND */ + struct ip_hdr *iphdr; + ip_addr_t dest_addr; +#if CHECKSUM_GEN_IP_INLINE + u32_t chk_sum = 0; +#endif /* CHECKSUM_GEN_IP_INLINE */ + + /* pbufs passed to IP must have a ref-count of 1 as their payload pointer + gets altered as the packet is passed down the stack */ + LWIP_ASSERT("p->ref == 1", p->ref == 1); + + snmp_inc_ipoutrequests(); + + /* Should the IP header be generated or is it already included in p? */ + if (dest != IP_HDRINCL) { + u16_t ip_hlen = IP_HLEN; +#if IP_OPTIONS_SEND + u16_t optlen_aligned = 0; + if (optlen != 0) { +#if CHECKSUM_GEN_IP_INLINE + int i; +#endif /* CHECKSUM_GEN_IP_INLINE */ + /* round up to a multiple of 4 */ + optlen_aligned = ((optlen + 3) & ~3); + ip_hlen += optlen_aligned; + /* First write in the IP options */ + if (pbuf_header(p, optlen_aligned)) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_output_if_opt: not enough room for IP options in pbuf\n")); + IP_STATS_INC(ip.err); + snmp_inc_ipoutdiscards(); + return ERR_BUF; + } + MEMCPY(p->payload, ip_options, optlen); + if (optlen < optlen_aligned) { + /* zero the remaining bytes */ + os_memset(((char*)p->payload) + optlen, 0, optlen_aligned - optlen); + } +#if CHECKSUM_GEN_IP_INLINE + for (i = 0; i < optlen_aligned/2; i++) { + chk_sum += ((u16_t*)p->payload)[i]; + } +#endif /* CHECKSUM_GEN_IP_INLINE */ + } +#endif /* IP_OPTIONS_SEND */ + /* generate IP header */ + if (pbuf_header(p, IP_HLEN)) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_output: not enough room for IP header in pbuf\n")); + + IP_STATS_INC(ip.err); + snmp_inc_ipoutdiscards(); + return ERR_BUF; + } + + iphdr = (struct ip_hdr *)p->payload; + LWIP_ASSERT("check that first pbuf can hold struct ip_hdr", + (p->len >= sizeof(struct ip_hdr))); + + IPH_TTL_SET(iphdr, ttl); + IPH_PROTO_SET(iphdr, proto); +#if CHECKSUM_GEN_IP_INLINE + chk_sum += LWIP_MAKE_U16(proto, ttl); +#endif /* CHECKSUM_GEN_IP_INLINE */ + + /* dest cannot be NULL here */ + ip_addr_copy(iphdr->dest, *dest); +#if CHECKSUM_GEN_IP_INLINE + chk_sum += ip4_addr_get_u32(&iphdr->dest) & 0xFFFF; + chk_sum += ip4_addr_get_u32(&iphdr->dest) >> 16; +#endif /* CHECKSUM_GEN_IP_INLINE */ + + IPH_VHLTOS_SET(iphdr, 4, ip_hlen / 4, tos); +#if CHECKSUM_GEN_IP_INLINE + chk_sum += iphdr->_v_hl_tos; +#endif /* CHECKSUM_GEN_IP_INLINE */ + IPH_LEN_SET(iphdr, htons(p->tot_len)); +#if CHECKSUM_GEN_IP_INLINE + chk_sum += iphdr->_len; +#endif /* CHECKSUM_GEN_IP_INLINE */ + IPH_OFFSET_SET(iphdr, 0); + IPH_ID_SET(iphdr, htons(ip_id)); +#if CHECKSUM_GEN_IP_INLINE + chk_sum += iphdr->_id; +#endif /* CHECKSUM_GEN_IP_INLINE */ + ++ip_id; + + if (ip_addr_isany(src)) { + ip_addr_copy(iphdr->src, netif->ip_addr); + } else { + /* src cannot be NULL here */ + ip_addr_copy(iphdr->src, *src); + } + +#if CHECKSUM_GEN_IP_INLINE + chk_sum += ip4_addr_get_u32(&iphdr->src) & 0xFFFF; + chk_sum += ip4_addr_get_u32(&iphdr->src) >> 16; + chk_sum = (chk_sum >> 16) + (chk_sum & 0xFFFF); + chk_sum = (chk_sum >> 16) + chk_sum; + chk_sum = ~chk_sum; + iphdr->_chksum = chk_sum; /* network order */ +#else /* CHECKSUM_GEN_IP_INLINE */ + IPH_CHKSUM_SET(iphdr, 0); +#if CHECKSUM_GEN_IP + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, ip_hlen)); +#endif +#endif /* CHECKSUM_GEN_IP_INLINE */ + } else { + /* IP header already included in p */ + iphdr = (struct ip_hdr *)p->payload; + ip_addr_copy(dest_addr, iphdr->dest); + dest = &dest_addr; + } + + IP_STATS_INC(ip.xmit); + + LWIP_DEBUGF(IP_DEBUG, ("ip_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], netif->num)); + ip_debug_print(p); + +#if ENABLE_LOOPBACK + if (ip_addr_cmp(dest, &netif->ip_addr)) { + /* Packet to self, enqueue it for loopback */ + LWIP_DEBUGF(IP_DEBUG, ("netif_loop_output()")); + return netif_loop_output(netif, p, dest); + } +#if LWIP_IGMP + if ((p->flags & PBUF_FLAG_MCASTLOOP) != 0) { + netif_loop_output(netif, p, dest); + } +#endif /* LWIP_IGMP */ +#endif /* ENABLE_LOOPBACK */ +#if IP_FRAG + /* don't fragment if interface has mtu set to 0 [loopif] */ + if (netif->mtu && (p->tot_len > netif->mtu)) { + return ip_frag(p, netif, dest); + } +#endif /* IP_FRAG */ + + LWIP_DEBUGF(IP_DEBUG, ("netif->output()\n")); + return netif->output(netif, p, dest); +} + +/** + * Simple interface to ip_output_if. It finds the outgoing network + * interface and calls upon ip_output_if to do the actual work. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an IP + header and p->payload points to that IP header) + * @param src the source IP address to send from (if src == IP_ADDR_ANY, the + * IP address of the netif used to send is used as source address) + * @param dest the destination IP address to send the packet to + * @param ttl the TTL value to be set in the IP header + * @param tos the TOS value to be set in the IP header + * @param proto the PROTOCOL to be set in the IP header + * + * @return ERR_RTE if no route is found + * see ip_output_if() for more return values + */ +err_t +ip_output(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto) +{ + struct netif *netif; + + /* pbufs passed to IP must have a ref-count of 1 as their payload pointer + gets altered as the packet is passed down the stack */ + LWIP_ASSERT("p->ref == 1", p->ref == 1); + + if ((netif = ip_route(dest)) == NULL) { + LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest))); + IP_STATS_INC(ip.rterr); + return ERR_RTE; + } + + return ip_output_if(p, src, dest, ttl, tos, proto, netif); +} + +#if LWIP_NETIF_HWADDRHINT +/** Like ip_output, but takes and addr_hint pointer that is passed on to netif->addr_hint + * before calling ip_output_if. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an IP + header and p->payload points to that IP header) + * @param src the source IP address to send from (if src == IP_ADDR_ANY, the + * IP address of the netif used to send is used as source address) + * @param dest the destination IP address to send the packet to + * @param ttl the TTL value to be set in the IP header + * @param tos the TOS value to be set in the IP header + * @param proto the PROTOCOL to be set in the IP header + * @param addr_hint address hint pointer set to netif->addr_hint before + * calling ip_output_if() + * + * @return ERR_RTE if no route is found + * see ip_output_if() for more return values + */ +err_t +ip_output_hinted(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto, u8_t *addr_hint) +{ + struct netif *netif; + err_t err; + + /* pbufs passed to IP must have a ref-count of 1 as their payload pointer + gets altered as the packet is passed down the stack */ + LWIP_ASSERT("p->ref == 1", p->ref == 1); + + if ((netif = ip_route(dest)) == NULL) { + LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest))); + IP_STATS_INC(ip.rterr); + return ERR_RTE; + } + + netif->addr_hint = addr_hint; + err = ip_output_if(p, src, dest, ttl, tos, proto, netif); + netif->addr_hint = NULL; + + return err; +} +#endif /* LWIP_NETIF_HWADDRHINT*/ + +#if IP_DEBUG +/* Print an IP header by using LWIP_DEBUGF + * @param p an IP packet, p->payload pointing to the IP header + */ +void +ip_debug_print(struct pbuf *p) +{ + struct ip_hdr *iphdr = (struct ip_hdr *)p->payload; + u8_t *payload; + + payload = (u8_t *)iphdr + IP_HLEN; + + LWIP_DEBUGF(IP_DEBUG, ("IP header:\n")); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("|%2"S16_F" |%2"S16_F" | 0x%02"X16_F" | %5"U16_F" | (v, hl, tos, len)\n", + IPH_V(iphdr), + IPH_HL(iphdr), + IPH_TOS(iphdr), + ntohs(IPH_LEN(iphdr)))); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %5"U16_F" |%"U16_F"%"U16_F"%"U16_F"| %4"U16_F" | (id, flags, offset)\n", + ntohs(IPH_ID(iphdr)), + ntohs(IPH_OFFSET(iphdr)) >> 15 & 1, + ntohs(IPH_OFFSET(iphdr)) >> 14 & 1, + ntohs(IPH_OFFSET(iphdr)) >> 13 & 1, + ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | 0x%04"X16_F" | (ttl, proto, chksum)\n", + IPH_TTL(iphdr), + IPH_PROTO(iphdr), + ntohs(IPH_CHKSUM(iphdr)))); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | %3"U16_F" | %3"U16_F" | (src)\n", + ip4_addr1_16(&iphdr->src), + ip4_addr2_16(&iphdr->src), + ip4_addr3_16(&iphdr->src), + ip4_addr4_16(&iphdr->src))); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | %3"U16_F" | %3"U16_F" | (dest)\n", + ip4_addr1_16(&iphdr->dest), + ip4_addr2_16(&iphdr->dest), + ip4_addr3_16(&iphdr->dest), + ip4_addr4_16(&iphdr->dest))); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); +} +#endif /* IP_DEBUG */ diff --git a/variants/lwip/lwip/core/ipv4/ip_addr.c b/variants/lwip/lwip/core/ipv4/ip_addr.c new file mode 100755 index 000000000..e2df58d35 --- /dev/null +++ b/variants/lwip/lwip/core/ipv4/ip_addr.c @@ -0,0 +1,329 @@ +/** + * @file + * This is the IPv4 address tools implementation. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" + +/* used by IP_ADDR_ANY and IP_ADDR_BROADCAST in ip_addr.h */ +const ip_addr_t ip_addr_any ICACHE_RODATA_ATTR = { IPADDR_ANY }; +const ip_addr_t ip_addr_broadcast ICACHE_RODATA_ATTR = { IPADDR_BROADCAST }; + +/** + * Determine if an address is a broadcast address on a network interface + * + * @param addr address to be checked + * @param netif the network interface against which the address is checked + * @return returns non-zero if the address is a broadcast address + */ +u8_t +ip4_addr_isbroadcast(u32_t addr, const struct netif *netif) +{ + ip_addr_t ipaddr; + ip4_addr_set_u32(&ipaddr, addr); + + /* all ones (broadcast) or all zeroes (old skool broadcast) */ + if ((~addr == IPADDR_ANY) || + (addr == IPADDR_ANY)) { + return 1; + /* no broadcast support on this network interface? */ + } else if ((netif->flags & NETIF_FLAG_BROADCAST) == 0) { + /* the given address cannot be a broadcast address + * nor can we check against any broadcast addresses */ + return 0; + /* address matches network interface address exactly? => no broadcast */ + } else if (addr == ip4_addr_get_u32(&netif->ip_addr)) { + return 0; + /* on the same (sub) network... */ + } else if (ip_addr_netcmp(&ipaddr, &(netif->ip_addr), &(netif->netmask)) + /* ...and host identifier bits are all ones? =>... */ + && ((addr & ~ip4_addr_get_u32(&netif->netmask)) == + (IPADDR_BROADCAST & ~ip4_addr_get_u32(&netif->netmask)))) { + /* => network broadcast address */ + return 1; + } else { + return 0; + } +} + +/** Checks if a netmask is valid (starting with ones, then only zeros) + * + * @param netmask the IPv4 netmask to check (in network byte order!) + * @return 1 if the netmask is valid, 0 if it is not + */ +u8_t +ip4_addr_netmask_valid(u32_t netmask) +{ + u32_t mask; + u32_t nm_hostorder = lwip_htonl(netmask); + + /* first, check for the first zero */ + for (mask = 1U << 31 ; mask != 0; mask >>= 1) { + if ((nm_hostorder & mask) == 0) { + break; + } + } + /* then check that there is no one */ + for (; mask != 0; mask >>= 1) { + if ((nm_hostorder & mask) != 0) { + /* there is a one after the first zero -> invalid */ + return 0; + } + } + /* no one after the first zero -> valid */ + return 1; +} + +/* Here for now until needed in other places in lwIP */ +#ifndef isprint +#define in_range(c, lo, up) ((u8_t)c >= lo && (u8_t)c <= up) +#define isprint(c) in_range(c, 0x20, 0x7f) +//#define isdigit(c) in_range(c, '0', '9') +//#define isxdigit(c) (isdigit(c) || in_range(c, 'a', 'f') || in_range(c, 'A', 'F')) +#define islower(c) in_range(c, 'a', 'z') +#define isspace(c) (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v') +#endif + +/** + * Ascii internet address interpretation routine. + * The value returned is in network order. + * + * @param cp IP address in ascii represenation (e.g. "127.0.0.1") + * @return ip address in network order + */ +u32_t +ipaddr_addr(const char *cp) +{ + ip_addr_t val; + + if (ipaddr_aton(cp, &val)) { + return ip4_addr_get_u32(&val); + } + return (IPADDR_NONE); +} + +/** + * Check whether "cp" is a valid ascii representation + * of an Internet address and convert to a binary address. + * Returns 1 if the address is valid, 0 if not. + * This replaces inet_addr, the return value from which + * cannot distinguish between failure and a local broadcast address. + * + * @param cp IP address in ascii represenation (e.g. "127.0.0.1") + * @param addr pointer to which to save the ip address in network order + * @return 1 if cp could be converted to addr, 0 on failure + */ +int +ipaddr_aton(const char *cp, ip_addr_t *addr) +{ + u32_t val; + u8_t base; + char c; + char ch; + unsigned long cutoff; + int cutlim; + u32_t parts[4]; + u32_t *pp = parts; + + c = *cp; + for (;;) { + /* + * Collect number up to ``.''. + * Values are specified as for C: + * 0x=hex, 0=octal, 1-9=decimal. + */ + if (!isdigit(c)) + return (0); + val = 0; + base = 10; + if (c == '0') { + c = *++cp; + if (c == 'x' || c == 'X') { + base = 16; + c = *++cp; + } else + base = 8; + } + + cutoff =(unsigned long)0xffffffff / (unsigned long)base; + cutlim =(unsigned long)0xffffffff % (unsigned long)base; + + for (;;) { + if (isdigit(c)) { + ch = (int)(c - '0'); + + if (val > cutoff || (val == cutoff && ch > cutlim)) + return (0); + + val = (val * base) + (int)(c - '0'); + c = *++cp; + } else if (base == 16 && isxdigit(c)) { + ch = (int)(c + 10 - (islower(c) ? 'a' : 'A')); + + if (val > cutoff || (val == cutoff && ch > cutlim)) + return (0); + + val = (val << 4) | (int)(c + 10 - (islower(c) ? 'a' : 'A')); + c = *++cp; + } else + break; + } + if (c == '.') { + /* + * Internet format: + * a.b.c.d + * a.b.c (with c treated as 16 bits) + * a.b (with b treated as 24 bits) + */ + if (pp >= parts + 3) { + return (0); + } + *pp++ = val; + c = *++cp; + } else + break; + } + /* + * Check for trailing characters. + */ + if (c != '\0' && !isspace(c)) { + return (0); + } + /* + * Concoct the address according to + * the number of parts specified. + */ + switch (pp - parts + 1) { + + case 0: + return (0); /* initial nondigit */ + + case 1: /* a -- 32 bits */ + break; + + case 2: /* a.b -- 8.24 bits */ + if ((val > 0xffffffUL) || (parts[0] > 0xff)) { + return (0); + } + val |= parts[0] << 24; + break; + + case 3: /* a.b.c -- 8.8.16 bits */ + if ((val > 0xffff) || (parts[0] > 0xff) || (parts[1] > 0xff)) { + return (0); + } + val |= (parts[0] << 24) | (parts[1] << 16); + break; + + case 4: /* a.b.c.d -- 8.8.8.8 bits */ + if ((val > 0xff) || (parts[0] > 0xff) || (parts[1] > 0xff) || (parts[2] > 0xff)) { + return (0); + } + val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); + break; + default: + LWIP_ASSERT("unhandled", 0); + break; + } + if (addr) { + ip4_addr_set_u32(addr, htonl(val)); + } + return (1); +} + +/** + * Convert numeric IP address into decimal dotted ASCII representation. + * returns ptr to static buffer; not reentrant! + * + * @param addr ip address in network order to convert + * @return pointer to a global static (!) buffer that holds the ASCII + * represenation of addr + */ +char * +ipaddr_ntoa(const ip_addr_t *addr) +{ + static char str[16]; + return ipaddr_ntoa_r(addr, str, 16); +} + +/** + * Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used. + * + * @param addr ip address in network order to convert + * @param buf target buffer where the string is stored + * @param buflen length of buf + * @return either pointer to buf which now holds the ASCII + * representation of addr or NULL if buf was too small + */ +char *ipaddr_ntoa_r(const ip_addr_t *addr, char *buf, int buflen) +{ + u32_t s_addr; + char inv[3]; + char *rp; + u8_t *ap; + u8_t rem; + u8_t n; + u8_t i; + int len = 0; + + s_addr = ip4_addr_get_u32(addr); + + rp = buf; + ap = (u8_t *)&s_addr; + for(n = 0; n < 4; n++) { + i = 0; + do { + rem = *ap % (u8_t)10; + *ap /= (u8_t)10; + inv[i++] = '0' + rem; + } while(*ap); + while(i--) { + if (len++ >= buflen) { + return NULL; + } + *rp++ = inv[i]; + } + if (len++ >= buflen) { + return NULL; + } + *rp++ = '.'; + ap++; + } + *--rp = 0; + return buf; +} diff --git a/variants/lwip/lwip/core/ipv4/ip_frag.c b/variants/lwip/lwip/core/ipv4/ip_frag.c new file mode 100755 index 000000000..b89eeb587 --- /dev/null +++ b/variants/lwip/lwip/core/ipv4/ip_frag.c @@ -0,0 +1,863 @@ +/** + * @file + * This is the IPv4 packet segmentation and reassembly implementation. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Jani Monoses + * Simon Goldschmidt + * original reassembly code by Adam Dunkels + * + */ + +#include "lwip/opt.h" +#include "lwip/ip_frag.h" +#include "lwip/def.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/snmp.h" +#include "lwip/stats.h" +#include "lwip/icmp.h" + +#include + +#if IP_REASSEMBLY +/** + * The IP reassembly code currently has the following limitations: + * - IP header options are not supported + * - fragments must not overlap (e.g. due to different routes), + * currently, overlapping or duplicate fragments are thrown away + * if IP_REASS_CHECK_OVERLAP=1 (the default)! + * + * @todo: work with IP header options + */ + +/** Setting this to 0, you can turn off checking the fragments for overlapping + * regions. The code gets a little smaller. Only use this if you know that + * overlapping won't occur on your network! */ +#ifndef IP_REASS_CHECK_OVERLAP +#define IP_REASS_CHECK_OVERLAP 1 +#endif /* IP_REASS_CHECK_OVERLAP */ + +/** Set to 0 to prevent freeing the oldest datagram when the reassembly buffer is + * full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller. + * Datagrams will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA + * is set to 1, so one datagram can be reassembled at a time, only. */ +#ifndef IP_REASS_FREE_OLDEST +#define IP_REASS_FREE_OLDEST 1 +#endif /* IP_REASS_FREE_OLDEST */ + +#define IP_REASS_FLAG_LASTFRAG 0x01 + +/** This is a helper struct which holds the starting + * offset and the ending offset of this fragment to + * easily chain the fragments. + * It has the same packing requirements as the IP header, since it replaces + * the IP header in memory in incoming fragments (after copying it) to keep + * track of the various fragments. (-> If the IP header doesn't need packing, + * this struct doesn't need packing, too.) + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip_reass_helper { + PACK_STRUCT_FIELD(struct pbuf *next_pbuf); + PACK_STRUCT_FIELD(u16_t start); + PACK_STRUCT_FIELD(u16_t end); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define IP_ADDRESSES_AND_ID_MATCH(iphdrA, iphdrB) \ + (ip_addr_cmp(&(iphdrA)->src, &(iphdrB)->src) && \ + ip_addr_cmp(&(iphdrA)->dest, &(iphdrB)->dest) && \ + IPH_ID(iphdrA) == IPH_ID(iphdrB)) ? 1 : 0 + +/* global variables */ +static struct ip_reassdata *reassdatagrams; +static u16_t ip_reass_pbufcount; + +/* function prototypes */ +static void ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)ICACHE_FLASH_ATTR; +static int ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)ICACHE_FLASH_ATTR; + +/** + * Reassembly timer base function + * for both NO_SYS == 0 and 1 (!). + * + * Should be called every 1000 msec (defined by IP_TMR_INTERVAL). + */ +void +ip_reass_tmr(void) +{ + struct ip_reassdata *r, *prev = NULL; + + r = reassdatagrams; + while (r != NULL) { + /* Decrement the timer. Once it reaches 0, + * clean up the incomplete fragment assembly */ + if (r->timer > 0) { + r->timer--; + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer dec %"U16_F"\n",(u16_t)r->timer)); + prev = r; + r = r->next; + } else { + /* reassembly timed out */ + struct ip_reassdata *tmp; + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer timed out\n")); + tmp = r; + /* get the next pointer before freeing */ + r = r->next; + /* free the helper struct and all enqueued pbufs */ + ip_reass_free_complete_datagram(tmp, prev); + } + } +} + +/** + * Free a datagram (struct ip_reassdata) and all its pbufs. + * Updates the total count of enqueued pbufs (ip_reass_pbufcount), + * SNMP counters and sends an ICMP time exceeded packet. + * + * @param ipr datagram to free + * @param prev the previous datagram in the linked list + * @return the number of pbufs freed + */ +static int +ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev) +{ + u16_t pbufs_freed = 0; + u8_t clen; + struct pbuf *p; + struct ip_reass_helper *iprh; + + LWIP_ASSERT("prev != ipr", prev != ipr); + if (prev != NULL) { + LWIP_ASSERT("prev->next == ipr", prev->next == ipr); + } + + snmp_inc_ipreasmfails(); +#if LWIP_ICMP + iprh = (struct ip_reass_helper *)ipr->p->payload; + if (iprh->start == 0) { + /* The first fragment was received, send ICMP time exceeded. */ + /* First, de-queue the first pbuf from r->p. */ + p = ipr->p; + ipr->p = iprh->next_pbuf; + /* Then, copy the original header into it. */ + SMEMCPY(p->payload, &ipr->iphdr, IP_HLEN); + icmp_time_exceeded(p, ICMP_TE_FRAG); + clen = pbuf_clen(p); + LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); + pbufs_freed += clen; + pbuf_free(p); + } +#endif /* LWIP_ICMP */ + + /* First, free all received pbufs. The individual pbufs need to be released + separately as they have not yet been chained */ + p = ipr->p; + while (p != NULL) { + struct pbuf *pcur; + iprh = (struct ip_reass_helper *)p->payload; + pcur = p; + /* get the next pointer before freeing */ + p = iprh->next_pbuf; + clen = pbuf_clen(pcur); + LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); + pbufs_freed += clen; + pbuf_free(pcur); + } + /* Then, unchain the struct ip_reassdata from the list and free it. */ + ip_reass_dequeue_datagram(ipr, prev); + LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= pbufs_freed); + ip_reass_pbufcount -= pbufs_freed; + + return pbufs_freed; +} + +#if IP_REASS_FREE_OLDEST +/** + * Free the oldest datagram to make room for enqueueing new fragments. + * The datagram 'fraghdr' belongs to is not freed! + * + * @param fraghdr IP header of the current fragment + * @param pbufs_needed number of pbufs needed to enqueue + * (used for freeing other datagrams if not enough space) + * @return the number of pbufs freed + */ +static int +ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed) +{ + /* @todo Can't we simply remove the last datagram in the + * linked list behind reassdatagrams? + */ + struct ip_reassdata *r, *oldest, *prev; + int pbufs_freed = 0, pbufs_freed_current; + int other_datagrams; + + /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs, + * but don't free the datagram that 'fraghdr' belongs to! */ + do { + oldest = NULL; + prev = NULL; + other_datagrams = 0; + r = reassdatagrams; + while (r != NULL) { + if (!IP_ADDRESSES_AND_ID_MATCH(&r->iphdr, fraghdr)) { + /* Not the same datagram as fraghdr */ + other_datagrams++; + if (oldest == NULL) { + oldest = r; + } else if (r->timer <= oldest->timer) { + /* older than the previous oldest */ + oldest = r; + } + } + if (r->next != NULL) { + prev = r; + } + r = r->next; + } + if (oldest != NULL) { + pbufs_freed_current = ip_reass_free_complete_datagram(oldest, prev); + pbufs_freed += pbufs_freed_current; + } + } while ((pbufs_freed < pbufs_needed) && (other_datagrams > 1)); + return pbufs_freed; +} +#endif /* IP_REASS_FREE_OLDEST */ + +/** + * Enqueues a new fragment into the fragment queue + * @param fraghdr points to the new fragments IP hdr + * @param clen number of pbufs needed to enqueue (used for freeing other datagrams if not enough space) + * @return A pointer to the queue location into which the fragment was enqueued + */ +static struct ip_reassdata* ICACHE_FLASH_ATTR +ip_reass_enqueue_new_datagram(struct ip_hdr *fraghdr, int clen) +{ + struct ip_reassdata* ipr; + /* No matching previous fragment found, allocate a new reassdata struct */ + ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA); + if (ipr == NULL) { +#if IP_REASS_FREE_OLDEST + if (ip_reass_remove_oldest_datagram(fraghdr, clen) >= clen) { + ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA); + } + if (ipr == NULL) +#endif /* IP_REASS_FREE_OLDEST */ + { + IPFRAG_STATS_INC(ip_frag.memerr); + LWIP_DEBUGF(IP_REASS_DEBUG,("Failed to alloc reassdata struct\n")); + return NULL; + } + } + os_memset(ipr, 0, sizeof(struct ip_reassdata)); + ipr->timer = IP_REASS_MAXAGE; + + /* enqueue the new structure to the front of the list */ + ipr->next = reassdatagrams; + reassdatagrams = ipr; + /* copy the ip header for later tests and input */ + /* @todo: no ip options supported? */ + SMEMCPY(&(ipr->iphdr), fraghdr, IP_HLEN); + return ipr; +} + +/** + * Dequeues a datagram from the datagram queue. Doesn't deallocate the pbufs. + * @param ipr points to the queue entry to dequeue + */ +static void ICACHE_FLASH_ATTR +ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev) +{ + + /* dequeue the reass struct */ + if (reassdatagrams == ipr) { + /* it was the first in the list */ + reassdatagrams = ipr->next; + } else { + /* it wasn't the first, so it must have a valid 'prev' */ + LWIP_ASSERT("sanity check linked list", prev != NULL); + prev->next = ipr->next; + } + + /* now we can free the ip_reass struct */ + memp_free(MEMP_REASSDATA, ipr); +} + +/** + * Chain a new pbuf into the pbuf list that composes the datagram. The pbuf list + * will grow over time as new pbufs are rx. + * Also checks that the datagram passes basic continuity checks (if the last + * fragment was received at least once). + * @param root_p points to the 'root' pbuf for the current datagram being assembled. + * @param new_p points to the pbuf for the current fragment + * @return 0 if invalid, >0 otherwise + */ +static int ICACHE_FLASH_ATTR +ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct pbuf *new_p) +{ + struct ip_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL; + struct pbuf *q; + u16_t offset,len; + struct ip_hdr *fraghdr; + int valid = 1; + + /* Extract length and fragment offset from current fragment */ + fraghdr = (struct ip_hdr*)new_p->payload; + len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4; + offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8; + + /* overwrite the fragment's ip header from the pbuf with our helper struct, + * and setup the embedded helper structure. */ + /* make sure the struct ip_reass_helper fits into the IP header */ + LWIP_ASSERT("sizeof(struct ip_reass_helper) <= IP_HLEN", + sizeof(struct ip_reass_helper) <= IP_HLEN); + iprh = (struct ip_reass_helper*)new_p->payload; + iprh->next_pbuf = NULL; + iprh->start = offset; + iprh->end = offset + len; + + /* Iterate through until we either get to the end of the list (append), + * or we find on with a larger offset (insert). */ + for (q = ipr->p; q != NULL;) { + iprh_tmp = (struct ip_reass_helper*)q->payload; + if (iprh->start < iprh_tmp->start) { + /* the new pbuf should be inserted before this */ + iprh->next_pbuf = q; + if (iprh_prev != NULL) { + /* not the fragment with the lowest offset */ +#if IP_REASS_CHECK_OVERLAP + if ((iprh->start < iprh_prev->end) || (iprh->end > iprh_tmp->start)) { + /* fragment overlaps with previous or following, throw away */ + goto freepbuf; + } +#endif /* IP_REASS_CHECK_OVERLAP */ + iprh_prev->next_pbuf = new_p; + } else { + /* fragment with the lowest offset */ + ipr->p = new_p; + } + break; + } else if(iprh->start == iprh_tmp->start) { + /* received the same datagram twice: no need to keep the datagram */ + goto freepbuf; +#if IP_REASS_CHECK_OVERLAP + } else if(iprh->start < iprh_tmp->end) { + /* overlap: no need to keep the new datagram */ + goto freepbuf; +#endif /* IP_REASS_CHECK_OVERLAP */ + } else { + /* Check if the fragments received so far have no wholes. */ + if (iprh_prev != NULL) { + if (iprh_prev->end != iprh_tmp->start) { + /* There is a fragment missing between the current + * and the previous fragment */ + valid = 0; + } + } + } + q = iprh_tmp->next_pbuf; + iprh_prev = iprh_tmp; + } + + /* If q is NULL, then we made it to the end of the list. Determine what to do now */ + if (q == NULL) { + if (iprh_prev != NULL) { + /* this is (for now), the fragment with the highest offset: + * chain it to the last fragment */ +#if IP_REASS_CHECK_OVERLAP + LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start); +#endif /* IP_REASS_CHECK_OVERLAP */ + iprh_prev->next_pbuf = new_p; + if (iprh_prev->end != iprh->start) { + valid = 0; + } + } else { +#if IP_REASS_CHECK_OVERLAP + LWIP_ASSERT("no previous fragment, this must be the first fragment!", + ipr->p == NULL); +#endif /* IP_REASS_CHECK_OVERLAP */ + /* this is the first fragment we ever received for this ip datagram */ + ipr->p = new_p; + } + } + + /* At this point, the validation part begins: */ + /* If we already received the last fragment */ + if ((ipr->flags & IP_REASS_FLAG_LASTFRAG) != 0) { + /* and had no wholes so far */ + if (valid) { + /* then check if the rest of the fragments is here */ + /* Check if the queue starts with the first datagram */ + if (((struct ip_reass_helper*)ipr->p->payload)->start != 0) { + valid = 0; + } else { + /* and check that there are no wholes after this datagram */ + iprh_prev = iprh; + q = iprh->next_pbuf; + while (q != NULL) { + iprh = (struct ip_reass_helper*)q->payload; + if (iprh_prev->end != iprh->start) { + valid = 0; + break; + } + iprh_prev = iprh; + q = iprh->next_pbuf; + } + /* if still valid, all fragments are received + * (because to the MF==0 already arrived */ + if (valid) { + LWIP_ASSERT("sanity check", ipr->p != NULL); + LWIP_ASSERT("sanity check", + ((struct ip_reass_helper*)ipr->p->payload) != iprh); + LWIP_ASSERT("validate_datagram:next_pbuf!=NULL", + iprh->next_pbuf == NULL); + LWIP_ASSERT("validate_datagram:datagram end!=datagram len", + iprh->end == ipr->datagram_len); + } + } + } + /* If valid is 0 here, there are some fragments missing in the middle + * (since MF == 0 has already arrived). Such datagrams simply time out if + * no more fragments are received... */ + return valid; + } + /* If we come here, not all fragments were received, yet! */ + return 0; /* not yet valid! */ +#if IP_REASS_CHECK_OVERLAP +freepbuf: + ip_reass_pbufcount -= pbuf_clen(new_p); + pbuf_free(new_p); + return 0; +#endif /* IP_REASS_CHECK_OVERLAP */ +} + +/** + * Reassembles incoming IP fragments into an IP datagram. + * + * @param p points to a pbuf chain of the fragment + * @return NULL if reassembly is incomplete, ? otherwise + */ +struct pbuf * +ip_reass(struct pbuf *p) +{ + struct pbuf *r; + struct ip_hdr *fraghdr; + struct ip_reassdata *ipr; + struct ip_reass_helper *iprh; + u16_t offset, len; + u8_t clen; + struct ip_reassdata *ipr_prev = NULL; + + IPFRAG_STATS_INC(ip_frag.recv); + snmp_inc_ipreasmreqds(); + + fraghdr = (struct ip_hdr*)p->payload; + + if ((IPH_HL(fraghdr) * 4) != IP_HLEN) { + LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: IP options currently not supported!\n")); + IPFRAG_STATS_INC(ip_frag.err); + goto nullreturn; + } + + offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8; + len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4; + + /* Check if we are allowed to enqueue more datagrams. */ + clen = pbuf_clen(p); + if ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) { +#if IP_REASS_FREE_OLDEST + if (!ip_reass_remove_oldest_datagram(fraghdr, clen) || + ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS)) +#endif /* IP_REASS_FREE_OLDEST */ + { + /* No datagram could be freed and still too many pbufs enqueued */ + LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: Overflow condition: pbufct=%d, clen=%d, MAX=%d\n", + ip_reass_pbufcount, clen, IP_REASS_MAX_PBUFS)); + IPFRAG_STATS_INC(ip_frag.memerr); + /* @todo: send ICMP time exceeded here? */ + /* drop this pbuf */ + goto nullreturn; + } + } + + /* Look for the datagram the fragment belongs to in the current datagram queue, + * remembering the previous in the queue for later dequeueing. */ + for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) { + /* Check if the incoming fragment matches the one currently present + in the reassembly buffer. If so, we proceed with copying the + fragment into the buffer. */ + if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) { + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching previous fragment ID=%"X16_F"\n", + ntohs(IPH_ID(fraghdr)))); + IPFRAG_STATS_INC(ip_frag.cachehit); + break; + } + ipr_prev = ipr; + } + + if (ipr == NULL) { + /* Enqueue a new datagram into the datagram queue */ + ipr = ip_reass_enqueue_new_datagram(fraghdr, clen); + /* Bail if unable to enqueue */ + if(ipr == NULL) { + goto nullreturn; + } + } else { + if (((ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) && + ((ntohs(IPH_OFFSET(&ipr->iphdr)) & IP_OFFMASK) != 0)) { + /* ipr->iphdr is not the header from the first fragment, but fraghdr is + * -> copy fraghdr into ipr->iphdr since we want to have the header + * of the first fragment (for ICMP time exceeded and later, for copying + * all options, if supported)*/ + SMEMCPY(&ipr->iphdr, fraghdr, IP_HLEN); + } + } + /* Track the current number of pbufs current 'in-flight', in order to limit + the number of fragments that may be enqueued at any one time */ + ip_reass_pbufcount += clen; + + /* At this point, we have either created a new entry or pointing + * to an existing one */ + + /* check for 'no more fragments', and update queue entry*/ + if ((IPH_OFFSET(fraghdr) & PP_NTOHS(IP_MF)) == 0) { + ipr->flags |= IP_REASS_FLAG_LASTFRAG; + ipr->datagram_len = offset + len; + LWIP_DEBUGF(IP_REASS_DEBUG, + ("ip_reass: last fragment seen, total len %"S16_F"\n", + ipr->datagram_len)); + } + /* find the right place to insert this pbuf */ + /* @todo: trim pbufs if fragments are overlapping */ + if (ip_reass_chain_frag_into_datagram_and_validate(ipr, p)) { + /* the totally last fragment (flag more fragments = 0) was received at least + * once AND all fragments are received */ + ipr->datagram_len += IP_HLEN; + + /* save the second pbuf before copying the header over the pointer */ + r = ((struct ip_reass_helper*)ipr->p->payload)->next_pbuf; + + /* copy the original ip header back to the first pbuf */ + fraghdr = (struct ip_hdr*)(ipr->p->payload); + SMEMCPY(fraghdr, &ipr->iphdr, IP_HLEN); + IPH_LEN_SET(fraghdr, htons(ipr->datagram_len)); + IPH_OFFSET_SET(fraghdr, 0); + IPH_CHKSUM_SET(fraghdr, 0); + /* @todo: do we need to set calculate the correct checksum? */ + IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN)); + + p = ipr->p; + + /* chain together the pbufs contained within the reass_data list. */ + while(r != NULL) { + iprh = (struct ip_reass_helper*)r->payload; + + /* hide the ip header for every succeding fragment */ + pbuf_header(r, -IP_HLEN); + pbuf_cat(p, r); + r = iprh->next_pbuf; + } + /* release the sources allocate for the fragment queue entry */ + ip_reass_dequeue_datagram(ipr, ipr_prev); + + /* and adjust the number of pbufs currently queued for reassembly. */ + ip_reass_pbufcount -= pbuf_clen(p); + + /* Return the pbuf chain */ + return p; + } + /* the datagram is not (yet?) reassembled completely */ + LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass_pbufcount: %d out\n", ip_reass_pbufcount)); + return NULL; + +nullreturn: + LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: nullreturn\n")); + IPFRAG_STATS_INC(ip_frag.drop); + pbuf_free(p); + return NULL; +} +#endif /* IP_REASSEMBLY */ + +#if IP_FRAG +#if IP_FRAG_USES_STATIC_BUF +static u8_t buf[LWIP_MEM_ALIGN_SIZE(IP_FRAG_MAX_MTU + MEM_ALIGNMENT - 1)]; +#else /* IP_FRAG_USES_STATIC_BUF */ + +#if !LWIP_NETIF_TX_SINGLE_PBUF +/** Allocate a new struct pbuf_custom_ref */ +static struct pbuf_custom_ref* ICACHE_FLASH_ATTR +ip_frag_alloc_pbuf_custom_ref(void) +{ + return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF); +} + +/** Free a struct pbuf_custom_ref */ +static void ICACHE_FLASH_ATTR +ip_frag_free_pbuf_custom_ref(struct pbuf_custom_ref* p) +{ + LWIP_ASSERT("p != NULL", p != NULL); + memp_free(MEMP_FRAG_PBUF, p); +} + +/** Free-callback function to free a 'struct pbuf_custom_ref', called by + * pbuf_free. */ +static void ICACHE_FLASH_ATTR +ipfrag_free_pbuf_custom(struct pbuf *p) +{ + struct pbuf_custom_ref *pcr = (struct pbuf_custom_ref*)p; + LWIP_ASSERT("pcr != NULL", pcr != NULL); + LWIP_ASSERT("pcr == p", (void*)pcr == (void*)p); + if (pcr->original != NULL) { + pbuf_free(pcr->original); + } + ip_frag_free_pbuf_custom_ref(pcr); +} +#endif /* !LWIP_NETIF_TX_SINGLE_PBUF */ +#endif /* IP_FRAG_USES_STATIC_BUF */ + +/** + * Fragment an IP datagram if too large for the netif. + * + * Chop the datagram in MTU sized chunks and send them in order + * by using a fixed size static memory buffer (PBUF_REF) or + * point PBUF_REFs into p (depending on IP_FRAG_USES_STATIC_BUF). + * + * @param p ip packet to send + * @param netif the netif on which to send + * @param dest destination ip address to which to send + * + * @return ERR_OK if sent successfully, err_t otherwise + */ +err_t +ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest) +{ + struct pbuf *rambuf; +#if IP_FRAG_USES_STATIC_BUF + struct pbuf *header; +#else +#if !LWIP_NETIF_TX_SINGLE_PBUF + struct pbuf *newpbuf; +#endif + struct ip_hdr *original_iphdr; +#endif + struct ip_hdr *iphdr; + u16_t nfb; + u16_t left, cop; + u16_t mtu = netif->mtu; + u16_t ofo, omf; + u16_t last; + u16_t poff = IP_HLEN; + u16_t tmp; +#if !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF + u16_t newpbuflen = 0; + u16_t left_to_copy; +#endif + + /* Get a RAM based MTU sized pbuf */ +#if IP_FRAG_USES_STATIC_BUF + /* When using a static buffer, we use a PBUF_REF, which we will + * use to reference the packet (without link header). + * Layer and length is irrelevant. + */ + rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF); + if (rambuf == NULL) { + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc(PBUF_LINK, 0, PBUF_REF) failed\n")); + return ERR_MEM; + } + rambuf->tot_len = rambuf->len = mtu; + rambuf->payload = LWIP_MEM_ALIGN((void *)buf); + + /* Copy the IP header in it */ + iphdr = (struct ip_hdr *)rambuf->payload; + SMEMCPY(iphdr, p->payload, IP_HLEN); +#else /* IP_FRAG_USES_STATIC_BUF */ + original_iphdr = (struct ip_hdr *)p->payload; + iphdr = original_iphdr; +#endif /* IP_FRAG_USES_STATIC_BUF */ + + /* Save original offset */ + tmp = ntohs(IPH_OFFSET(iphdr)); + ofo = tmp & IP_OFFMASK; + omf = tmp & IP_MF; + + left = p->tot_len - IP_HLEN; + + nfb = (mtu - IP_HLEN) / 8; + + while (left) { + last = (left <= mtu - IP_HLEN); + + /* Set new offset and MF flag */ + tmp = omf | (IP_OFFMASK & (ofo)); + if (!last) { + tmp = tmp | IP_MF; + } + + /* Fill this fragment */ + cop = last ? left : nfb * 8; + +#if IP_FRAG_USES_STATIC_BUF + poff += pbuf_copy_partial(p, (u8_t*)iphdr + IP_HLEN, cop, poff); +#else /* IP_FRAG_USES_STATIC_BUF */ +#if LWIP_NETIF_TX_SINGLE_PBUF + rambuf = pbuf_alloc(PBUF_IP, cop, PBUF_RAM); + if (rambuf == NULL) { + return ERR_MEM; + } + LWIP_ASSERT("this needs a pbuf in one piece!", + (rambuf->len == rambuf->tot_len) && (rambuf->next == NULL)); + poff += pbuf_copy_partial(p, rambuf->payload, cop, poff); + /* make room for the IP header */ + if(pbuf_header(rambuf, IP_HLEN)) { + pbuf_free(rambuf); + return ERR_MEM; + } + /* fill in the IP header */ + SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN); + iphdr = rambuf->payload; +#else /* LWIP_NETIF_TX_SINGLE_PBUF */ + /* When not using a static buffer, create a chain of pbufs. + * The first will be a PBUF_RAM holding the link and IP header. + * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged, + * but limited to the size of an mtu. + */ + rambuf = pbuf_alloc(PBUF_LINK, IP_HLEN, PBUF_RAM); + if (rambuf == NULL) { + return ERR_MEM; + } + LWIP_ASSERT("this needs a pbuf in one piece!", + (p->len >= (IP_HLEN))); + SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN); + iphdr = (struct ip_hdr *)rambuf->payload; + + /* Can just adjust p directly for needed offset. */ + p->payload = (u8_t *)p->payload + poff; + p->len -= poff; + + left_to_copy = cop; + while (left_to_copy) { + struct pbuf_custom_ref *pcr; + newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len; + /* Is this pbuf already empty? */ + if (!newpbuflen) { + p = p->next; + continue; + } + pcr = ip_frag_alloc_pbuf_custom_ref(); + if (pcr == NULL) { + pbuf_free(rambuf); + return ERR_MEM; + } + /* Mirror this pbuf, although we might not need all of it. */ + newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc, p->payload, newpbuflen); + if (newpbuf == NULL) { + ip_frag_free_pbuf_custom_ref(pcr); + pbuf_free(rambuf); + return ERR_MEM; + } + pbuf_ref(p); + pcr->original = p; + pcr->pc.custom_free_function = ipfrag_free_pbuf_custom; + + /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain + * so that it is removed when pbuf_dechain is later called on rambuf. + */ + pbuf_cat(rambuf, newpbuf); + left_to_copy -= newpbuflen; + if (left_to_copy) { + p = p->next; + } + } + poff = newpbuflen; +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ +#endif /* IP_FRAG_USES_STATIC_BUF */ + + /* Correct header */ + IPH_OFFSET_SET(iphdr, htons(tmp)); + IPH_LEN_SET(iphdr, htons(cop + IP_HLEN)); + IPH_CHKSUM_SET(iphdr, 0); + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN)); + +#if IP_FRAG_USES_STATIC_BUF + if (last) { + pbuf_realloc(rambuf, left + IP_HLEN); + } + + /* This part is ugly: we alloc a RAM based pbuf for + * the link level header for each chunk and then + * free it.A PBUF_ROM style pbuf for which pbuf_header + * worked would make things simpler. + */ + header = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM); + if (header != NULL) { + pbuf_chain(header, rambuf); + netif->output(netif, header, dest); + IPFRAG_STATS_INC(ip_frag.xmit); + snmp_inc_ipfragcreates(); + pbuf_free(header); + } else { + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc() for header failed\n")); + pbuf_free(rambuf); + return ERR_MEM; + } +#else /* IP_FRAG_USES_STATIC_BUF */ + /* No need for separate header pbuf - we allowed room for it in rambuf + * when allocated. + */ + netif->output(netif, rambuf, dest); + IPFRAG_STATS_INC(ip_frag.xmit); + + /* Unfortunately we can't reuse rambuf - the hardware may still be + * using the buffer. Instead we free it (and the ensuing chain) and + * recreate it next time round the loop. If we're lucky the hardware + * will have already sent the packet, the free will really free, and + * there will be zero memory penalty. + */ + + pbuf_free(rambuf); +#endif /* IP_FRAG_USES_STATIC_BUF */ + left -= cop; + ofo += nfb; + } +#if IP_FRAG_USES_STATIC_BUF + pbuf_free(rambuf); +#endif /* IP_FRAG_USES_STATIC_BUF */ + snmp_inc_ipfragoks(); + return ERR_OK; +} +#endif /* IP_FRAG */ diff --git a/variants/lwip/lwip/core/mdns.c b/variants/lwip/lwip/core/mdns.c new file mode 100755 index 000000000..3267c2e45 --- /dev/null +++ b/variants/lwip/lwip/core/mdns.c @@ -0,0 +1,1106 @@ +/** + * lwip MDNS resolver file. + * + * Created on: Jul 29, 2010 + * Author: Daniel Toma + * + + * ported from uIP resolv.c Copyright (c) 2002-2003, Adam Dunkels. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + + * This file implements a MDNS host name and PUCK service registration. + + *----------------------------------------------------------------------------- + * Includes + *----------------------------------------------------------------------------*/ +#include "lwip/opt.h" +#if LWIP_MDNS /* don't build if not configured for use in lwipopts.h */ +#include "lwip/mdns.h" +#include "lwip/puck_def.h" +#include "lwip/udp.h" +#include "lwip/mem.h" +#include "lwip/igmp.h" +#include "osapi.h" +#include "os_type.h" +#include "user_interface.h" + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +/** DNS server IP address */ +#ifndef DNS_MULTICAST_ADDRESS +#define DNS_MULTICAST_ADDRESS ipaddr_addr("224.0.0.251") /* resolver1.opendns.com */ +#endif + +/** DNS server IP address */ +#ifndef MDNS_LOCAL +#define MDNS_LOCAL "local" /* resolver1.opendns.com */ +#endif + +/** DNS server port address */ +#ifndef DNS_MDNS_PORT +#define DNS_MDNS_PORT 5353 +#endif + +/** DNS maximum number of retries when asking for a name, before "timeout". */ +#ifndef DNS_MAX_RETRIES +#define DNS_MAX_RETRIES 4 +#endif + +/** DNS resource record max. TTL (one week as default) */ +#ifndef DNS_MAX_TTL +#define DNS_MAX_TTL 604800 +#endif + +/* DNS protocol flags */ +#define DNS_FLAG1_RESPONSE 0x84 +#define DNS_FLAG1_OPCODE_STATUS 0x10 +#define DNS_FLAG1_OPCODE_INVERSE 0x08 +#define DNS_FLAG1_OPCODE_STANDARD 0x00 +#define DNS_FLAG1_AUTHORATIVE 0x04 +#define DNS_FLAG1_TRUNC 0x02 +#define DNS_FLAG1_RD 0x01 +#define DNS_FLAG2_RA 0x80 +#define DNS_FLAG2_ERR_MASK 0x0f +#define DNS_FLAG2_ERR_NONE 0x00 +#define DNS_FLAG2_ERR_NAME 0x03 + +/* DNS protocol states */ +#define DNS_STATE_UNUSED 0 +#define DNS_STATE_NEW 1 +#define DNS_STATE_ASKING 2 +#define DNS_STATE_DONE 3 + +/* MDNS registration type */ +#define MDNS_HOSTNAME_REG 0 +#define MDNS_SERVICE_REG 1 + +/* MDNS registration type */ +#define MDNS_REG_ANSWER 1 +#define MDNS_SD_ANSWER 2 +#define MDNS_SERVICE_REG_ANSWER 3 + +/* MDNS registration time */ +#define MDNS_HOST_TIME 120 +#define MDNS_SERVICE_TIME 3600 + +/** MDNS name length with "." at the beginning and end of name*/ +#ifndef MDNS_LENGTH_ADD +#define MDNS_LENGTH_ADD 2 +#endif + +#ifdef MDNS_MAX_NAME_LENGTH +#undef MDNS_MAX_NAME_LENGTH +#endif +#define MDNS_MAX_NAME_LENGTH (256) + +PACK_STRUCT_BEGIN +/** DNS message header */ +struct mdns_hdr { + PACK_STRUCT_FIELD(u16_t id); + PACK_STRUCT_FIELD(u8_t flags1); + PACK_STRUCT_FIELD(u8_t flags2); + PACK_STRUCT_FIELD(u16_t numquestions); + PACK_STRUCT_FIELD(u16_t numanswers); + PACK_STRUCT_FIELD(u16_t numauthrr); + PACK_STRUCT_FIELD(u16_t numextrarr); +}PACK_STRUCT_STRUCT; +PACK_STRUCT_END + +#define SIZEOF_DNS_HDR 12 + +PACK_STRUCT_BEGIN +/** MDNS query message structure */ +struct mdns_query { + /* MDNS query record starts with either a domain name or a pointer + to a name already present somewhere in the packet. */PACK_STRUCT_FIELD(u16_t type); + PACK_STRUCT_FIELD(u16_t class); +}PACK_STRUCT_STRUCT; +PACK_STRUCT_END + +#define SIZEOF_DNS_QUERY 4 + +PACK_STRUCT_BEGIN +/** MDNS answer message structure */ +struct mdns_answer { + /* MDNS answer record starts with either a domain name or a pointer + to a name already present somewhere in the packet. */PACK_STRUCT_FIELD(u16_t type); + PACK_STRUCT_FIELD(u16_t class); + PACK_STRUCT_FIELD(u32_t ttl); + PACK_STRUCT_FIELD(u16_t len); +}PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#define SIZEOF_DNS_ANSWER 10 + +PACK_STRUCT_BEGIN +/** MDNS answer message structure */ +struct mdns_auth { + PACK_STRUCT_FIELD(u32_t src); +}PACK_STRUCT_STRUCT; +PACK_STRUCT_END + +#define SIZEOF_MDNS_AUTH 4 +PACK_STRUCT_BEGIN +/** MDNS service registration message structure */ +struct mdns_service { + PACK_STRUCT_FIELD(u16_t prior); + PACK_STRUCT_FIELD(u16_t weight); + PACK_STRUCT_FIELD(u16_t port); +}PACK_STRUCT_STRUCT; +PACK_STRUCT_END + +#define SIZEOF_MDNS_SERVICE 6 + +uint16 PUCK_PORT ; +os_timer_t mdns_timer; +/* forward declarations */ +static void mdns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p, + struct ip_addr *addr, u16_t port); + +/*----------------------------------------------------------------------------- + * Globales + *----------------------------------------------------------------------------*/ + +/* MDNS variables */ +static char host_name[MDNS_NAME_LENGTH]; +static char service_name[MDNS_NAME_LENGTH]; +static char server_name[MDNS_NAME_LENGTH]; +//static char puck_datasheet[PUCK_DATASHEET_SIZE]; +static struct udp_pcb *mdns_pcb = NULL; +static struct mdns_info * ms_info = NULL; +static struct ip_addr multicast_addr; +static struct ip_addr host_addr; +static uint8 register_flag = 0; +static uint8 mdns_flag = 0; +//#if (DNS_USES_STATIC_BUF == 1) +static u8_t mdns_payload[DNS_MSG_SIZE]; +//#endif /* (MDNS_USES_STATIC_BUF == 1) */ +/* + * Function to set the UDP pcb used to send the mDNS packages + */ +void ICACHE_FLASH_ATTR +getPcb(struct udp_pcb *pcb) { + mdns_pcb = pcb; +} + +#if DNS_DOES_NAME_CHECK +/** + * Compare the "dotted" name "query" with the encoded name "response" + * to make sure an answer from the DNS server matches the current mdns_table + * entry (otherwise, answers might arrive late for hostname not on the list + * any more). + * + * @param query hostname (not encoded) from the mdns_table + * @param response encoded hostname in the DNS response + * @return 0: names equal; 1: names differ + */ +static u8_t ICACHE_FLASH_ATTR +mdns_compare_name(unsigned char *query, unsigned char *response) { + unsigned char n; + + do { + n = *response++; + /** @see RFC 1035 - 4.1.4. Message compression */ + if ((n & 0xc0) == 0xc0) { + /* Compressed name */ + break; + } else { + /* Not compressed name */ + while (n > 0) { + if ((*query) != (*response)) { + return 1; + } + ++response; + ++query; + --n; + }; + ++query; + } + } while (*response != 0); + + return 0; +} +#endif /* DNS_DOES_NAME_CHECK */ +/** + * Send a mDNS answer packet. + * + * @param type of answer hostname and service registration or service + * @param name to query + * @param id transaction ID in the DNS query packet + * @return ERR_OK if packet is sent; an err_t indicating the problem otherwise + */ +static err_t ICACHE_FLASH_ATTR +mdns_answer(u16_t type, const char* name, u8_t id) { + err_t err; + struct mdns_hdr *hdr; + struct mdns_answer ans; + struct mdns_auth auth; + struct mdns_service serv; + struct pbuf *p ,*p_sta; + char *query, *nptr; + const char *pHostname; + struct netif * sta_netif = NULL; + struct netif * ap_netif = NULL; + static char tmpBuf[PUCK_DATASHEET_SIZE + PUCK_SERVICE_LENGTH]; + u8_t n; + u16_t length = 0; + /* if here, we have either a new query or a retry on a previous query to process */ + p = pbuf_alloc(PBUF_TRANSPORT, + SIZEOF_DNS_HDR + MDNS_MAX_NAME_LENGTH * 2 + SIZEOF_DNS_QUERY, PBUF_RAM); + if (p != NULL) { + LWIP_ASSERT("pbuf must be in one piece", p->next == NULL); + /* fill dns header */ + hdr = (struct mdns_hdr*) p->payload; + os_memset(hdr, 0, SIZEOF_DNS_HDR); + hdr->id = htons(id); + hdr->flags1 = DNS_FLAG1_RESPONSE; + + if (type == MDNS_SD_ANSWER) { + pHostname = DNS_SD_SERVICE; + hdr->numanswers = htons(1); + } else if (type == MDNS_SERVICE_REG_ANSWER) { + pHostname = PUCK_SERVICE; + hdr->numanswers = htons(type); + } else { + pHostname = name; + hdr->numanswers = htons(type); + } + query = (char*) hdr + SIZEOF_DNS_HDR; + --pHostname; + /* convert hostname into suitable query format. */ + do { + ++pHostname; + nptr = query; + ++query; + for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) { + *query = *pHostname; + ++query; + ++n; + } + *nptr = n; + } while (*pHostname != 0); + *query++ = '\0'; + /* fill dns query */ + + if (type == MDNS_REG_ANSWER) { + + ans.type = htons(DNS_RRTYPE_A); + ans.class = htons(DNS_RRCLASS_IN); + ans.ttl = htonl(MDNS_SERVICE_TIME); + ans.len = htons(DNS_IP_ADDR_LEN); + length = DNS_IP_ADDR_LEN; + + MEMCPY( query, &ans, SIZEOF_DNS_ANSWER); + + /* resize the query */ + query = query + SIZEOF_DNS_ANSWER; + + /* set the local IP address */ + auth.src = host_addr.addr; + MEMCPY( query, &auth, SIZEOF_MDNS_AUTH); + } + if (type == MDNS_SD_ANSWER) { + + ans.type = htons(DNS_RRTYPE_PTR); + ans.class = htons(DNS_RRCLASS_IN); + ans.ttl = htonl(300); + ans.len = htons(os_strlen(PUCK_SERVICE) + 1 +1 ); + length = 0; + + MEMCPY( query, &ans, SIZEOF_DNS_ANSWER); + + /* resize the query */ + query = query + SIZEOF_DNS_ANSWER; + pHostname = PUCK_SERVICE; + --pHostname; + + /* convert hostname into suitable query format. */ + do { + ++pHostname; + nptr = query; + ++query; + for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) { + *query = *pHostname; + ++query; + ++n; + } + *nptr = n; + } while (*pHostname != 0); + *query++ = '\0'; + } + + if (type == MDNS_SERVICE_REG_ANSWER) { + + ans.type = htons(DNS_RRTYPE_PTR); + ans.class = htons(DNS_RRCLASS_IN); + ans.ttl = htonl(MDNS_SERVICE_TIME); + os_strcpy(tmpBuf, name); + os_strcat(tmpBuf, "."); + os_strcat(tmpBuf, PUCK_SERVICE); + + length = os_strlen(tmpBuf) + MDNS_LENGTH_ADD; + ans.len = htons(length); + length = 0; + + MEMCPY( query, &ans, SIZEOF_DNS_ANSWER); + + /* resize the query */ + query = query + SIZEOF_DNS_ANSWER; + + pHostname = tmpBuf; + --pHostname; + + /* convert hostname into suitable query format. */ + do { + ++pHostname; + nptr = query; + ++query; + for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) { + *query = *pHostname; + ++query; + ++n; + } + *nptr = n; + } while (*pHostname != 0); + *query++ = '\0'; + + /* Service query*/ + pHostname = name; + --pHostname; + + /* convert hostname into suitable query format. */ + do { + ++pHostname; + nptr = query; + ++query; + for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) { + *query = *pHostname; + ++query; + ++n; + } + *nptr = n; + } while (*pHostname != 0); + + /* Add to the service name the service local + * pointing to the beginning of the mDNS message*/ + *query++ = DNS_OFFSET_FLAG; + *query++ = DNS_DEFAULT_OFFSET; + + /* fill the query */ + + ans.type = htons(DNS_RRTYPE_SRV); + ans.class = htons(DNS_RRCLASS_FLUSH_IN); + ans.ttl = htonl(MDNS_SERVICE_TIME); + os_strcpy(tmpBuf, host_name); + os_strcat(tmpBuf, "."); + os_strcat(tmpBuf, MDNS_LOCAL); + length = os_strlen(tmpBuf) + MDNS_LENGTH_ADD; + ans.len = htons(SIZEOF_MDNS_SERVICE + length); + length = 0; + MEMCPY( query, &ans, SIZEOF_DNS_ANSWER); + + /* resize the query */ + query = query + SIZEOF_DNS_ANSWER; + /* fill the service properties */ + + serv.prior = htons(0); + serv.weight = htons(0); + serv.port = htons(PUCK_PORT); + MEMCPY( query, &serv, SIZEOF_MDNS_SERVICE); + /* resize the query */ + query = query + SIZEOF_MDNS_SERVICE; + + pHostname = tmpBuf; + --pHostname; + + /* convert hostname into suitable query format. */ + do { + ++pHostname; + nptr = query; + ++query; + for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) { + *query = *pHostname; + ++query; + ++n; + } + *nptr = n; + } while (*pHostname != 0); + *query++ = '\0'; + + /* TXT answer */ + pHostname = name; + --pHostname; + + /* convert hostname into suitable query format. */ + do { + ++pHostname; + nptr = query; + ++query; + for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) { + *query = *pHostname; + ++query; + ++n; + } + *nptr = n; + } while (*pHostname != 0); + + /* Add to the service name the service local + * pointing to the beginning of the mDNS message*/ + *query++ = DNS_OFFSET_FLAG; + *query++ = DNS_DEFAULT_OFFSET; + + /* fill the answer */ + ans.type = htons(DNS_RRTYPE_TXT); + ans.class = htons(DNS_RRCLASS_IN); + ans.ttl = htonl(MDNS_SERVICE_TIME); + length = sizeof(SERVICE_DESCRIPTION); + ans.len = htons(length); + length = 0; + MEMCPY( query, &ans, SIZEOF_DNS_ANSWER); + + /* resize the query */ + query = query + SIZEOF_DNS_ANSWER; + + pHostname = SERVICE_DESCRIPTION; + --pHostname; + + /* convert hostname into suitable query format. */ + do { + ++pHostname; + nptr = query; + ++query; + for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) { + *query = *pHostname; + ++query; + ++n; + } + *nptr = n; + } while (*pHostname != 0); + *query++ = '\0'; + } + /* resize pbuf to the exact dns query */ + pbuf_realloc(p, (query + length) - ((char*) (p->payload))); + + /* send dns packet */ + /*add by tzx for AP + STA MDNS begin------*/ + sta_netif = (struct netif *)eagle_lwip_getif(0x00); + ap_netif = (struct netif *)eagle_lwip_getif(0x01); + if(wifi_get_opmode() == 0x03 && wifi_get_broadcast_if() == 0x03 &&\ + sta_netif != NULL && ap_netif != NULL) { + if(netif_is_up(sta_netif) && netif_is_up(ap_netif)) { + + p_sta = pbuf_alloc(PBUF_TRANSPORT, + SIZEOF_DNS_HDR + MDNS_MAX_NAME_LENGTH * 2 + SIZEOF_DNS_QUERY, PBUF_RAM); + if (pbuf_copy (p_sta,p) != ERR_OK) { + os_printf("mdns_answer copying to new pbuf failed\n"); + return -1; + } + netif_set_default(sta_netif); + err = udp_sendto(mdns_pcb, p_sta, &multicast_addr, DNS_MDNS_PORT); + pbuf_free(p_sta); + netif_set_default(ap_netif); + } + } + /*add by tzx for AP + STA MDNS end------*/ + err = udp_sendto(mdns_pcb, p, &multicast_addr, DNS_MDNS_PORT); + /* free pbuf */ + pbuf_free(p); + } else { + err = ERR_MEM; + } + + return err; +} + +/** + * Send a mDNS service answer packet. + * + * @param name service name to query + * @param id transaction ID in the DNS query packet + * @return ERR_OK if packet is sent; an err_t indicating the problem otherwise + */ +static err_t ICACHE_FLASH_ATTR +mdns_send_service(struct mdns_info *info, u8_t id) { + err_t err; + struct mdns_hdr *hdr; + struct mdns_answer ans; + struct mdns_service serv; + struct mdns_auth auth; + struct pbuf *p ,*p_sta; + char *query, *nptr; + const char *pHostname; + char *device_info; + const char *name = info->host_name; + u8_t n; + u8_t i = 0; + u16_t length = 0; + u8_t addr1 = 12, addr2 = 12; + struct netif * sta_netif = NULL; + struct netif * ap_netif = NULL; + static char tmpBuf[PUCK_DATASHEET_SIZE + PUCK_SERVICE_LENGTH]; + /* if here, we have either a new query or a retry on a previous query to process */ + p = pbuf_alloc(PBUF_TRANSPORT, + SIZEOF_DNS_HDR + MDNS_MAX_NAME_LENGTH * 2 + SIZEOF_DNS_QUERY, PBUF_RAM); + if (p != NULL) { + LWIP_ASSERT("pbuf must be in one piece", p->next == NULL); + /* fill dns header */ + hdr = (struct mdns_hdr*) p->payload; + os_memset(hdr, 0, SIZEOF_DNS_HDR); + hdr->id = htons(id); + hdr->flags1 = DNS_FLAG1_RESPONSE; + hdr->numanswers = htons(4); + query = (char*) hdr + SIZEOF_DNS_HDR; + os_strcpy(tmpBuf, PUCK_SERVICE); + + pHostname = tmpBuf; + --pHostname; + + /* convert hostname into suitable query format. */ + do { + ++pHostname; + nptr = query; + ++query; + ++addr1; + ++addr2; + for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) { + *query = *pHostname; + ++query; + ++addr1; + ++addr2; + ++n; + } + *nptr = n; + } while (*pHostname != 0); + *query++ = '\0'; + length = sizeof(MDNS_LOCAL); + addr1 -= length; + length = os_strlen(PUCK_SERVICE) + 1; + addr2 -= length; + + ans.type = htons(DNS_RRTYPE_PTR); + ans.class = htons(DNS_RRCLASS_IN); + ans.ttl = htonl(300); + os_strcpy(tmpBuf, name); + length = os_strlen(tmpBuf) + MDNS_LENGTH_ADD + 1; + ans.len = htons(length); + length = 0; + + MEMCPY( query, &ans, SIZEOF_DNS_ANSWER); + /* resize the query */ + query = query + SIZEOF_DNS_ANSWER; + + pHostname = tmpBuf; + --pHostname; + /* convert hostname into suitable query format. */ + do { + ++pHostname; + nptr = query; + ++query; + for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) { + *query = *pHostname; + ++query; + ++n; + } + *nptr = n; + } while (*pHostname != 0); + *query++ = DNS_OFFSET_FLAG; + *query++ = DNS_DEFAULT_OFFSET; + pHostname = name; + --pHostname; + /* convert hostname into suitable query format. */ + do { + ++pHostname; + nptr = query; + ++query; + for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) { + *query = *pHostname; + ++query; + ++n; + } + *nptr = n; + } while (*pHostname != 0); + //*query++ = '\0'; + *query++ = DNS_OFFSET_FLAG; + *query++ = DNS_DEFAULT_OFFSET; + + /* fill the answer */ + ans.type = htons(DNS_RRTYPE_TXT); + ans.class = htons(DNS_RRCLASS_FLUSH_IN); + ans.ttl = htonl(300); +// length = os_strlen(TXT_DATA) + MDNS_LENGTH_ADD + 1; + device_info = (char *)os_zalloc(50); + ets_sprintf(device_info,"vendor = %s","Espressif"); + for(i = 0; i < 10 &&(info->txt_data[i] != NULL);i++) { + length += os_strlen(info->txt_data[i]); + length++; + } + length += os_strlen(device_info)+ 1 ; + ans.len = htons(length); + length = 0; + MEMCPY( query, &ans, SIZEOF_DNS_ANSWER); + query = query + SIZEOF_DNS_ANSWER; + pHostname = device_info; + --pHostname; + /* convert hostname into suitable query format. */ + do { + ++pHostname; + nptr = query; + ++query; + for (n = 0; *pHostname != 0; ++pHostname) { + *query = *pHostname; + ++query; + ++n; + } + *nptr = n; + } while (*pHostname != 0); + i = 0; + while(info->txt_data[i] != NULL && i < 10) { + pHostname = info->txt_data[i]; + --pHostname; + /* convert hostname into suitable query format. */ + do { + ++pHostname; + nptr = query; + ++query; + for (n = 0; *pHostname != 0; ++pHostname) { + *query = *pHostname; + ++query; + ++n; + } + *nptr = n; + } while (*pHostname != 0); + i++; + } +// *query++ = '\0'; + os_free(device_info); + os_strcpy(tmpBuf, name); + pHostname = tmpBuf; + --pHostname; + do { + ++pHostname; + nptr = query; + ++query; + for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) { + *query = *pHostname; + ++query; + ++n; + } + *nptr = n; + } while (*pHostname != 0); + + *query++ = DNS_OFFSET_FLAG; + *query++ = DNS_DEFAULT_OFFSET; + + ans.type = htons(DNS_RRTYPE_SRV); + ans.class = htons(DNS_RRCLASS_FLUSH_IN); + ans.ttl = htonl(300); + os_strcpy(tmpBuf,service_name); + os_strcat(tmpBuf, "."); + os_strcat(tmpBuf, MDNS_LOCAL); + length = os_strlen(tmpBuf) + MDNS_LENGTH_ADD; + ans.len = htons(SIZEOF_MDNS_SERVICE + length); + length = 0; + MEMCPY( query, &ans, SIZEOF_DNS_ANSWER); + + /* resize the query */ + query = query + SIZEOF_DNS_ANSWER; + + serv.prior = htons(0); + serv.weight = htons(0); + serv.port = htons(PUCK_PORT); + MEMCPY( query, &serv, SIZEOF_MDNS_SERVICE); + /* resize the query */ + query = query + SIZEOF_MDNS_SERVICE; + + pHostname = tmpBuf; + --pHostname; + do { + ++pHostname; + nptr = query; + ++query; + for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) { + *query = *pHostname; + ++query; + ++n; + } + *nptr = n; + } while (*pHostname != 0); + *query++ = '\0'; + /* set the name of the authority field. + * The same name as the Query using the offset address*/ + os_strcpy(tmpBuf,service_name); + os_strcat(tmpBuf, "."); + os_strcat(tmpBuf, MDNS_LOCAL); + pHostname = tmpBuf; + --pHostname; + do { + ++pHostname; + nptr = query; + ++query; + for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) { + *query = *pHostname; + ++query; + ++n; + } + *nptr = n; + } while (*pHostname != 0); + *query++ = '\0'; + /* set the name of the authority field. + * The same name as the Query using the offset address*/ + //*query++ = DNS_OFFSET_FLAG; + //*query++ = DNS_DEFAULT_OFFSET; + ans.type = htons(DNS_RRTYPE_A); + ans.class = htons(DNS_RRCLASS_FLUSH_IN); + ans.ttl = htonl(300); + ans.len = htons(DNS_IP_ADDR_LEN); + + MEMCPY( query, &ans, SIZEOF_DNS_ANSWER); + + /* resize the query */ + query = query + SIZEOF_DNS_ANSWER; + + /* fill the payload of the mDNS message */ + /* set the local IP address */ + auth.src = host_addr.addr; //ipAddr; + MEMCPY( query, &auth, SIZEOF_MDNS_AUTH); + /* resize the query */ + query = query + SIZEOF_MDNS_AUTH; + + /* set the name of the authority field. + * The same name as the Query using the offset address*/ + + /* resize pbuf to the exact dns query */ + pbuf_realloc(p, (query) - ((char*) (p->payload))); + /* send dns packet */ + sta_netif = (struct netif *)eagle_lwip_getif(0x00); + ap_netif = (struct netif *)eagle_lwip_getif(0x01); + if(wifi_get_opmode() == 0x03 && wifi_get_broadcast_if() == 0x03 &&\ + sta_netif != NULL && ap_netif != NULL) { + if(netif_is_up(sta_netif) && netif_is_up(ap_netif)) { + + p_sta = pbuf_alloc(PBUF_TRANSPORT, + SIZEOF_DNS_HDR + MDNS_MAX_NAME_LENGTH * 2 + SIZEOF_DNS_QUERY, PBUF_RAM); + if (pbuf_copy (p_sta,p) != ERR_OK) { + os_printf("mdns_send_service copying to new pbuf failed\n"); + return -1; + } + netif_set_default(sta_netif); + err = udp_sendto(mdns_pcb, p_sta, &multicast_addr, DNS_MDNS_PORT); + pbuf_free(p_sta); + netif_set_default(ap_netif); + } + } + err = udp_sendto(mdns_pcb, p, &multicast_addr, DNS_MDNS_PORT); + + /* free pbuf */ + pbuf_free(p); + } else { + os_printf("ERR_MEM \n"); + err = ERR_MEM; + } + + return err; +} + +/** + * Receive input function for DNS response packets arriving for the dns UDP pcb. + * + * @params see udp.h + */ +static void ICACHE_FLASH_ATTR +mdns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, + u16_t port) { + u8_t i; + struct mdns_hdr *hdr; + u8_t nquestions; + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(addr); + LWIP_UNUSED_ARG(port); + struct mdns_info *info = (struct mdns_info *)arg; + /* is the dns message too big ? */ + if (p->tot_len > DNS_MSG_SIZE) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too big\n")); + /* free pbuf and return */ + goto memerr1; + } + + /* is the dns message big enough ? */ + if (p->tot_len < (SIZEOF_DNS_HDR + SIZEOF_DNS_QUERY + SIZEOF_DNS_ANSWER)) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too small\n")); + /* free pbuf and return */ + goto memerr1; + } + /* copy dns payload inside static buffer for processing */ + if (pbuf_copy_partial(p, mdns_payload, p->tot_len, 0) == p->tot_len) { + /* The ID in the DNS header should be our entry into the name table. */ + hdr = (struct mdns_hdr*) mdns_payload; + + i = htons(hdr->id); + if (i < DNS_TABLE_SIZE) { + + nquestions = htons(hdr->numquestions); + //nanswers = htons(hdr->numanswers); + /* if we have a question send an answer if necessary */ + if (nquestions > 0) { + /* MDNS_DS_DOES_NAME_CHECK */ + /* Check if the name in the "question" part match with the name of the MDNS DS service. */ + if (mdns_compare_name((unsigned char *) DNS_SD_SERVICE, + (unsigned char *) mdns_payload + SIZEOF_DNS_HDR) == 0) { + /* respond with the puck service*/ + mdns_answer(MDNS_SD_ANSWER, PUCK_SERVICE, 0); + } else if (mdns_compare_name((unsigned char *) PUCK_SERVICE, + (unsigned char *) mdns_payload + SIZEOF_DNS_HDR) == 0) { + /* respond with the puck service*/ + mdns_send_service(info, 0); + } else + goto memerr2; + } + } + } + goto memerr2; + memerr2: + os_memset(mdns_payload , 0 ,DNS_MSG_SIZE); + memerr1: + /* free pbuf */ + pbuf_free(p); + return; +} + +/** + * close the UDP pcb . + */ +void ICACHE_FLASH_ATTR +mdns_close(void) +{ + if (mdns_pcb != NULL && ms_info != NULL) + udp_remove(mdns_pcb); + os_free(ms_info); + mdns_pcb = NULL; + ms_info = NULL; + +} + +void ICACHE_FLASH_ATTR +mdns_set_name(const char *name) +{ + //strcpy(host_name, name); + os_strcpy(service_name, name); +} + +void ICACHE_FLASH_ATTR +mdns_enable(void) +{ + if(mdns_flag == 0) { + udp_recv(mdns_pcb, mdns_recv, NULL); + } +} + +void ICACHE_FLASH_ATTR +mdns_disable(void) +{ + if (mdns_flag == 1) { + udp_recv(mdns_pcb, NULL, NULL); + } +} + +/** + * close the UDP pcb . + */ +char* ICACHE_FLASH_ATTR +mdns_get_hostname(void) { + //strcpy(host_name, name); + char *name = host_name; + if (host_name[0] != 0 ) { + return name; + } else { + return ("Espressif"); + } +} + +void ICACHE_FLASH_ATTR +mdns_set_hostname(char *name) { + if (name == NULL) { + os_strncpy(host_name, "Espressif", os_strlen("Espressif")+3); + return; + } + if (os_strlen(name) + 3 <= MDNS_NAME_LENGTH ){ + os_strncpy(host_name, name, os_strlen(name) ); +// os_memset(host_name + os_strlen(host_name) ,0x00,3); + } else { + os_strncpy(host_name, name, MDNS_NAME_LENGTH); + } +} + +void ICACHE_FLASH_ATTR +mdns_set_servername(const char *name) { + if (name == NULL) { + PUCK_SERVICE = "_Espressif._tcp._local"; + }else { + os_sprintf(server_name ,"_%s._tcp.local",name); + PUCK_SERVICE = server_name; + } +} + +char* ICACHE_FLASH_ATTR +mdns_get_servername(void) { + char *name = PUCK_SERVICE; + if (name == NULL) { + PUCK_SERVICE = "_Espressif._tcp._local"; + } + return name; +} + +void ICACHE_FLASH_ATTR +mdns_server_unregister(void) { + struct ip_addr ap_host_addr; + struct ip_info ipconfig; + if(register_flag == 1){ + if (igmp_leavegroup(&host_addr, &multicast_addr) != ERR_OK) { + os_printf("sta udp_leave_multigrup failed!\n"); + return; + }; + if(wifi_get_opmode() == 0x03 || wifi_get_opmode() == 0x02) { + wifi_get_ip_info(SOFTAP_IF, &ipconfig); + ap_host_addr.addr = ipconfig.ip.addr; + if (igmp_leavegroup(&ap_host_addr, &multicast_addr) != ERR_OK) { + os_printf("ap udp_join_multigrup failed!\n"); + return; + }; + } + register_flag = 0; + } +} + +void ICACHE_FLASH_ATTR +mdns_server_register(void) { + + if (register_flag == 1) { + os_printf("mdns server is already registered !\n"); + return; + } else if (igmp_joingroup(&host_addr, &multicast_addr) != ERR_OK) { + os_printf("udp_join_multigrup failed!\n"); + return; + }; + register_flag = 1; +} + +void ICACHE_FLASH_ATTR +mdns_reg(struct mdns_info *info) { + + static uint8 i = 0; + if (i <= 3) { + mdns_send_service(info,0); + i++; + } else { + os_timer_disarm(&mdns_timer); + } +} + +/** + * Initialize the resolver: set up the UDP pcb and configure the default server + * (NEW IP). + */ +void ICACHE_FLASH_ATTR +mdns_init(struct mdns_info *info) { + /* initialize default DNS server address */ + multicast_addr.addr = DNS_MULTICAST_ADDRESS; + struct ip_addr ap_host_addr; + struct ip_info ipconfig; + ms_info = (struct mdns_info *)os_zalloc(sizeof(struct mdns_info)); + if (ms_info != NULL) { + os_memcpy(ms_info,info,sizeof(struct mdns_info)); + } else { + os_printf("ms_info alloc failed\n"); + return; + } + if (ms_info->ipAddr == 0) { + os_printf("mdns ip error!\n "); + return; + } + host_addr.addr = ms_info->ipAddr ; + LWIP_DEBUGF(DNS_DEBUG, ("dns_init: initializing\n")); + //get the datasheet from PUCK + mdns_set_hostname(ms_info->host_name); + mdns_set_servername(ms_info->server_name); + mdns_set_name(ms_info->host_name); + + // get the host name as instrumentName_serialNumber for MDNS + // set the name of the service, the same as host name + os_printf("host_name = %s\n", host_name); + os_printf("server_name = %s\n", PUCK_SERVICE); + if (ms_info->server_port == 0) + { + PUCK_PORT = 80; + } else { + PUCK_PORT = ms_info->server_port; + } + + /* initialize mDNS */ + mdns_pcb = udp_new(); + + if (mdns_pcb != NULL) { + /* join to the multicast address 224.0.0.251 */ + if(wifi_get_opmode() == 0x03 || wifi_get_opmode() == 0x01) { + if (igmp_joingroup(&host_addr, &multicast_addr) != ERR_OK) { + os_printf("sta udp_join_multigrup failed!\n"); + return; + }; + } + if(wifi_get_opmode() == 0x03 || wifi_get_opmode() == 0x02) { + wifi_get_ip_info(SOFTAP_IF, &ipconfig); + ap_host_addr.addr = ipconfig.ip.addr; + if (igmp_joingroup(&ap_host_addr, &multicast_addr) != ERR_OK) { + os_printf("ap udp_join_multigrup failed!\n"); + return; + }; + } + register_flag = 1; + /* join to any IP address at the port 5353 */ + if (udp_bind(mdns_pcb, IP_ADDR_ANY, DNS_MDNS_PORT) != ERR_OK) { + os_printf("udp_bind failed!\n"); + return; + }; + + /*loopback function for the multicast(224.0.0.251) messages received at port 5353*/ +// mdns_enable(); + udp_recv(mdns_pcb, mdns_recv, ms_info); + mdns_flag = 1; + /* + * Register the name of the instrument + */ + + os_timer_disarm(&mdns_timer); + os_timer_setfn(&mdns_timer, (os_timer_func_t *)mdns_reg,ms_info); + os_timer_arm(&mdns_timer, 1000, 1); + } +} + +#endif /* LWIP_MDNS */ diff --git a/variants/lwip/lwip/core/mem.c b/variants/lwip/lwip/core/mem.c new file mode 100755 index 000000000..bf6263d97 --- /dev/null +++ b/variants/lwip/lwip/core/mem.c @@ -0,0 +1,644 @@ +/** + * @file + * Dynamic memory manager + * + * This is a lightweight replacement for the standard C library malloc(). + * + * If you want to use the standard C library malloc() instead, define + * MEM_LIBC_MALLOC to 1 in your lwipopts.h + * + * To let mem_malloc() use pools (prevents fragmentation and is much faster than + * a heap but might waste some memory), define MEM_USE_POOLS to 1, define + * MEM_USE_CUSTOM_POOLS to 1 and create a file "lwippools.h" that includes a list + * of pools like this (more pools can be added between _START and _END): + * + * Define three pools with sizes 256, 512, and 1512 bytes + * LWIP_MALLOC_MEMPOOL_START + * LWIP_MALLOC_MEMPOOL(20, 256) + * LWIP_MALLOC_MEMPOOL(10, 512) + * LWIP_MALLOC_MEMPOOL(5, 1512) + * LWIP_MALLOC_MEMPOOL_END + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * Simon Goldschmidt + * + */ + +#include "lwip/opt.h" + +#if !MEM_LIBC_MALLOC /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/sys.h" +#include "lwip/stats.h" +#include "lwip/err.h" + +#include + +#if MEM_USE_POOLS +/* lwIP head implemented with different sized pools */ + +/** + * Allocate memory: determine the smallest pool that is big enough + * to contain an element of 'size' and get an element from that pool. + * + * @param size the size in bytes of the memory needed + * @return a pointer to the allocated memory or NULL if the pool is empty + */ +void * +mem_malloc(mem_size_t size) +{ + struct memp_malloc_helper *element; + memp_t poolnr; + mem_size_t required_size = size + sizeof(struct memp_malloc_helper); + + for (poolnr = MEMP_POOL_FIRST; poolnr <= MEMP_POOL_LAST; poolnr = (memp_t)(poolnr + 1)) { +#if MEM_USE_POOLS_TRY_BIGGER_POOL +again: +#endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */ + /* is this pool big enough to hold an element of the required size + plus a struct memp_malloc_helper that saves the pool this element came from? */ + if (required_size <= memp_sizes[poolnr]) { + break; + } + } + if (poolnr > MEMP_POOL_LAST) { + LWIP_ASSERT("mem_malloc(): no pool is that big!", 0); + return NULL; + } + element = (struct memp_malloc_helper*)memp_malloc(poolnr); + if (element == NULL) { + /* No need to DEBUGF or ASSERT: This error is already + taken care of in memp.c */ +#if MEM_USE_POOLS_TRY_BIGGER_POOL + /** Try a bigger pool if this one is empty! */ + if (poolnr < MEMP_POOL_LAST) { + poolnr++; + goto again; + } +#endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */ + return NULL; + } + + /* save the pool number this element came from */ + element->poolnr = poolnr; + /* and return a pointer to the memory directly after the struct memp_malloc_helper */ + element++; + + return element; +} + +/** + * Free memory previously allocated by mem_malloc. Loads the pool number + * and calls memp_free with that pool number to put the element back into + * its pool + * + * @param rmem the memory element to free + */ +void +mem_free(void *rmem) +{ + struct memp_malloc_helper *hmem = (struct memp_malloc_helper*)rmem; + + LWIP_ASSERT("rmem != NULL", (rmem != NULL)); + LWIP_ASSERT("rmem == MEM_ALIGN(rmem)", (rmem == LWIP_MEM_ALIGN(rmem))); + + /* get the original struct memp_malloc_helper */ + hmem--; + + LWIP_ASSERT("hmem != NULL", (hmem != NULL)); + LWIP_ASSERT("hmem == MEM_ALIGN(hmem)", (hmem == LWIP_MEM_ALIGN(hmem))); + LWIP_ASSERT("hmem->poolnr < MEMP_MAX", (hmem->poolnr < MEMP_MAX)); + + /* and put it in the pool we saved earlier */ + memp_free(hmem->poolnr, hmem); +} + +#else /* MEM_USE_POOLS */ +/* lwIP replacement for your libc malloc() */ + +/** + * The heap is made up as a list of structs of this type. + * This does not have to be aligned since for getting its size, + * we only use the macro SIZEOF_STRUCT_MEM, which automatically alignes. + */ +struct mem { + /** index (-> ram[next]) of the next struct */ + mem_size_t next; + /** index (-> ram[prev]) of the previous struct */ + mem_size_t prev; + /** 1: this area is used; 0: this area is unused */ + u8_t used; + u8_t pad[3]; /* XXX: pad here instead use global ALIGN */ +} __ATTRIB_PACK; + +/** All allocated blocks will be MIN_SIZE bytes big, at least! + * MIN_SIZE can be overridden to suit your needs. Smaller values save space, + * larger values could prevent too small blocks to fragment the RAM too much. */ +#ifndef MIN_SIZE +#define MIN_SIZE 12 +#endif /* MIN_SIZE */ +/* some alignment macros: we define them here for better source code layout */ +#define MIN_SIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(MIN_SIZE) +#define SIZEOF_STRUCT_MEM LWIP_MEM_ALIGN_SIZE(sizeof(struct mem)) +#define MEM_SIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(MEM_SIZE) + +/** If you want to relocate the heap to external memory, simply define + * LWIP_RAM_HEAP_POINTER as a void-pointer to that location. + * If so, make sure the memory at that location is big enough (see below on + * how that space is calculated). */ +#ifndef LWIP_RAM_HEAP_POINTER +/** the heap. we need one struct mem at the end and some room for alignment */ +/* enlarge heap as tx pbuf payload is allocate from heap as well */ +u8_t ram_heap[MEM_SIZE_ALIGNED + (2*SIZEOF_STRUCT_MEM) + MEM_ALIGNMENT] SHMEM_ATTR; +#define LWIP_RAM_HEAP_POINTER ram_heap +#endif /* LWIP_RAM_HEAP_POINTER */ + +/** pointer to the heap (ram_heap): for alignment, ram is now a pointer instead of an array */ +static u8_t *ram; +/** the last entry, always unused! */ +static struct mem *ram_end; +/** pointer to the lowest free block, this is used for faster search */ +static struct mem *lfree; + +/** concurrent access protection */ +//static sys_mutex_t mem_mutex; + +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + +static volatile u8_t mem_free_count; + +/* Allow mem_free from other (e.g. interrupt) context */ +#define LWIP_MEM_FREE_DECL_PROTECT() SYS_ARCH_DECL_PROTECT(lev_free) +#define LWIP_MEM_FREE_PROTECT() SYS_ARCH_PROTECT(lev_free) +#define LWIP_MEM_FREE_UNPROTECT() SYS_ARCH_UNPROTECT(lev_free) +#define LWIP_MEM_ALLOC_DECL_PROTECT() SYS_ARCH_DECL_PROTECT(lev_alloc) +#define LWIP_MEM_ALLOC_PROTECT() SYS_ARCH_PROTECT(lev_alloc) +#define LWIP_MEM_ALLOC_UNPROTECT() SYS_ARCH_UNPROTECT(lev_alloc) + +#else /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + +/* Protect the heap only by using a semaphore */ +#define LWIP_MEM_FREE_DECL_PROTECT() +#define LWIP_MEM_FREE_PROTECT() sys_mutex_lock(&mem_mutex) +#define LWIP_MEM_FREE_UNPROTECT() sys_mutex_unlock(&mem_mutex) +/* mem_malloc is protected using semaphore AND LWIP_MEM_ALLOC_PROTECT */ +#define LWIP_MEM_ALLOC_DECL_PROTECT() +#define LWIP_MEM_ALLOC_PROTECT() +#define LWIP_MEM_ALLOC_UNPROTECT() + +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + + +/** + * "Plug holes" by combining adjacent empty struct mems. + * After this function is through, there should not exist + * one empty struct mem pointing to another empty struct mem. + * + * @param mem this points to a struct mem which just has been freed + * @internal this function is only called by mem_free() and mem_trim() + * + * This assumes access to the heap is protected by the calling function + * already. + */ +static void ICACHE_FLASH_ATTR +plug_holes(struct mem *mem) +{ + struct mem *nmem; + struct mem *pmem; + + LWIP_ASSERT("plug_holes: mem >= ram", (u8_t *)mem >= ram); + LWIP_ASSERT("plug_holes: mem < ram_end", (u8_t *)mem < (u8_t *)ram_end); + LWIP_ASSERT("plug_holes: mem->used == 0", mem->used == 0); + + /* plug hole forward */ + LWIP_ASSERT("plug_holes: mem->next <= MEM_SIZE_ALIGNED", mem->next <= MEM_SIZE_ALIGNED); + + nmem = (struct mem *)(void *)&ram[mem->next]; + if (mem != nmem && nmem->used == 0 && (u8_t *)nmem != (u8_t *)ram_end) { + /* if mem->next is unused and not end of ram, combine mem and mem->next */ + if (lfree == nmem) { + lfree = mem; + } + mem->next = nmem->next; + ((struct mem *)(void *)&ram[nmem->next])->prev = (mem_size_t)((u8_t *)mem - ram); + } + + /* plug hole backward */ + pmem = (struct mem *)(void *)&ram[mem->prev]; + if (pmem != mem && pmem->used == 0) { + /* if mem->prev is unused, combine mem and mem->prev */ + if (lfree == mem) { + lfree = pmem; + } + pmem->next = mem->next; + ((struct mem *)(void *)&ram[mem->next])->prev = (mem_size_t)((u8_t *)pmem - ram); + } +} + +/** + * Zero the heap and initialize start, end and lowest-free + */ +void +mem_init(void) +{ + struct mem *mem; + + LWIP_ASSERT("Sanity check alignment", + (SIZEOF_STRUCT_MEM & (MEM_ALIGNMENT-1)) == 0); + + /* align the heap */ + ram = (u8_t *)LWIP_MEM_ALIGN(LWIP_RAM_HEAP_POINTER); + /* initialize the start of the heap */ + mem = (struct mem *)(void *)ram; + mem->next = MEM_SIZE_ALIGNED; + mem->prev = 0; + mem->used = 0; + /* initialize the end of the heap */ + ram_end = (struct mem *)(void *)&ram[MEM_SIZE_ALIGNED]; + ram_end->used = 1; + ram_end->next = MEM_SIZE_ALIGNED; + ram_end->prev = MEM_SIZE_ALIGNED; + + /* initialize the lowest-free pointer to the start of the heap */ + lfree = (struct mem *)(void *)ram; + + MEM_STATS_AVAIL(avail, MEM_SIZE_ALIGNED); + + if(sys_mutex_new(&mem_mutex) != ERR_OK) { + LWIP_ASSERT("failed to create mem_mutex", 0); + } +} + +/** + * Put a struct mem back on the heap + * + * @param rmem is the data portion of a struct mem as returned by a previous + * call to mem_malloc() + */ +void +mem_free(void *rmem) +{ + struct mem *mem; + LWIP_MEM_FREE_DECL_PROTECT(); + + if (rmem == NULL) { + LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("mem_free(p == NULL) was called.\n")); + return; + } + LWIP_ASSERT("mem_free: sanity check alignment", (((mem_ptr_t)rmem) & (MEM_ALIGNMENT-1)) == 0); + + LWIP_ASSERT("mem_free: legal memory", (u8_t *)rmem >= (u8_t *)ram && + (u8_t *)rmem < (u8_t *)ram_end); + + if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) { + SYS_ARCH_DECL_PROTECT(lev); + LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_free: illegal memory\n")); + /* protect mem stats from concurrent access */ + SYS_ARCH_PROTECT(lev); + MEM_STATS_INC(illegal); + SYS_ARCH_UNPROTECT(lev); + return; + } + /* protect the heap from concurrent access */ + LWIP_MEM_FREE_PROTECT(); + /* Get the corresponding struct mem ... */ + mem = (struct mem *)(void *)((u8_t *)rmem - SIZEOF_STRUCT_MEM); + /* ... which has to be in a used state ... */ + LWIP_ASSERT("mem_free: mem->used", mem->used); + /* ... and is now unused. */ + mem->used = 0; + + if (mem < lfree) { + /* the newly freed struct is now the lowest */ + lfree = mem; + } + + MEM_STATS_DEC_USED(used, mem->next - (mem_size_t)(((u8_t *)mem - ram))); + + /* finally, see if prev or next are free also */ + plug_holes(mem); +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + mem_free_count = 1; +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + LWIP_MEM_FREE_UNPROTECT(); +} + +/** + * Shrink memory returned by mem_malloc(). + * + * @param rmem pointer to memory allocated by mem_malloc the is to be shrinked + * @param newsize required size after shrinking (needs to be smaller than or + * equal to the previous size) + * @return for compatibility reasons: is always == rmem, at the moment + * or NULL if newsize is > old size, in which case rmem is NOT touched + * or freed! + */ +void * +mem_trim(void *rmem, mem_size_t newsize) +{ + mem_size_t size; + mem_size_t ptr, ptr2; + struct mem *mem, *mem2; + /* use the FREE_PROTECT here: it protects with sem OR SYS_ARCH_PROTECT */ + LWIP_MEM_FREE_DECL_PROTECT(); + + /* Expand the size of the allocated memory region so that we can + adjust for alignment. */ + newsize = LWIP_MEM_ALIGN_SIZE(newsize); + + if(newsize < MIN_SIZE_ALIGNED) { + /* every data block must be at least MIN_SIZE_ALIGNED long */ + newsize = MIN_SIZE_ALIGNED; + } + + if (newsize > MEM_SIZE_ALIGNED) { + return NULL; + } + + LWIP_ASSERT("mem_trim: legal memory", (u8_t *)rmem >= (u8_t *)ram && + (u8_t *)rmem < (u8_t *)ram_end); + + if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) { + SYS_ARCH_DECL_PROTECT(lev); + LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_trim: illegal memory\n")); + /* protect mem stats from concurrent access */ + SYS_ARCH_PROTECT(lev); + MEM_STATS_INC(illegal); + SYS_ARCH_UNPROTECT(lev); + return rmem; + } + /* Get the corresponding struct mem ... */ + mem = (struct mem *)(void *)((u8_t *)rmem - SIZEOF_STRUCT_MEM); + /* ... and its offset pointer */ + ptr = (mem_size_t)((u8_t *)mem - ram); + + size = mem->next - ptr - SIZEOF_STRUCT_MEM; + LWIP_ASSERT("mem_trim can only shrink memory", newsize <= size); + if (newsize > size) { + /* not supported */ + return NULL; + } + if (newsize == size) { + /* No change in size, simply return */ + return rmem; + } + + /* protect the heap from concurrent access */ + LWIP_MEM_FREE_PROTECT(); + + mem2 = (struct mem *)(void *)&ram[mem->next]; + if(mem2->used == 0) { + /* The next struct is unused, we can simply move it at little */ + mem_size_t next; + /* remember the old next pointer */ + next = mem2->next; + /* create new struct mem which is moved directly after the shrinked mem */ + ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize; + if (lfree == mem2) { + lfree = (struct mem *)(void *)&ram[ptr2]; + } + mem2 = (struct mem *)(void *)&ram[ptr2]; + mem2->used = 0; + /* restore the next pointer */ + mem2->next = next; + /* link it back to mem */ + mem2->prev = ptr; + /* link mem to it */ + mem->next = ptr2; + /* last thing to restore linked list: as we have moved mem2, + * let 'mem2->next->prev' point to mem2 again. but only if mem2->next is not + * the end of the heap */ + if (mem2->next != MEM_SIZE_ALIGNED) { + ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2; + } + MEM_STATS_DEC_USED(used, (size - newsize)); + /* no need to plug holes, we've already done that */ + } else if (newsize + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED <= size) { + /* Next struct is used but there's room for another struct mem with + * at least MIN_SIZE_ALIGNED of data. + * Old size ('size') must be big enough to contain at least 'newsize' plus a struct mem + * ('SIZEOF_STRUCT_MEM') with some data ('MIN_SIZE_ALIGNED'). + * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty + * region that couldn't hold data, but when mem->next gets freed, + * the 2 regions would be combined, resulting in more free memory */ + ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize; + mem2 = (struct mem *)(void *)&ram[ptr2]; + if (mem2 < lfree) { + lfree = mem2; + } + mem2->used = 0; + mem2->next = mem->next; + mem2->prev = ptr; + mem->next = ptr2; + if (mem2->next != MEM_SIZE_ALIGNED) { + ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2; + } + MEM_STATS_DEC_USED(used, (size - newsize)); + /* the original mem->next is used, so no need to plug holes! */ + } + /* else { + next struct mem is used but size between mem and mem2 is not big enough + to create another struct mem + -> don't do anyhting. + -> the remaining space stays unused since it is too small + } */ +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + mem_free_count = 1; +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + LWIP_MEM_FREE_UNPROTECT(); + return rmem; +} + +/** + * Adam's mem_malloc() plus solution for bug #17922 + * Allocate a block of memory with a minimum of 'size' bytes. + * + * @param size is the minimum size of the requested block in bytes. + * @return pointer to allocated memory or NULL if no free memory was found. + * + * Note that the returned value will always be aligned (as defined by MEM_ALIGNMENT). + */ +void * +mem_malloc(mem_size_t size) +{ + mem_size_t ptr, ptr2; + struct mem *mem, *mem2; +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + u8_t local_mem_free_count = 0; +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + LWIP_MEM_ALLOC_DECL_PROTECT(); + + if (size == 0) { + return NULL; + } + + /* Expand the size of the allocated memory region so that we can + adjust for alignment. */ + size = LWIP_MEM_ALIGN_SIZE(size); + + if(size < MIN_SIZE_ALIGNED) { + /* every data block must be at least MIN_SIZE_ALIGNED long */ + size = MIN_SIZE_ALIGNED; + } + + if (size > MEM_SIZE_ALIGNED) { + return NULL; + } + + /* protect the heap from concurrent access */ + sys_mutex_lock(&mem_mutex); + LWIP_MEM_ALLOC_PROTECT(); +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + /* run as long as a mem_free disturbed mem_malloc */ + do { + local_mem_free_count = 0; +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + + /* Scan through the heap searching for a free block that is big enough, + * beginning with the lowest free block. + */ + for (ptr = (mem_size_t)((u8_t *)lfree - ram); ptr < MEM_SIZE_ALIGNED - size; + ptr = ((struct mem *)(void *)&ram[ptr])->next) { + mem = (struct mem *)(void *)&ram[ptr]; +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + mem_free_count = 0; + LWIP_MEM_ALLOC_UNPROTECT(); + /* allow mem_free to run */ + LWIP_MEM_ALLOC_PROTECT(); + if (mem_free_count != 0) { + local_mem_free_count = mem_free_count; + } + mem_free_count = 0; +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + + if ((!mem->used) && + (mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size) { + /* mem is not used and at least perfect fit is possible: + * mem->next - (ptr + SIZEOF_STRUCT_MEM) gives us the 'user data size' of mem */ + + if (mem->next - (ptr + SIZEOF_STRUCT_MEM) >= (size + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED)) { + /* (in addition to the above, we test if another struct mem (SIZEOF_STRUCT_MEM) containing + * at least MIN_SIZE_ALIGNED of data also fits in the 'user data space' of 'mem') + * -> split large block, create empty remainder, + * remainder must be large enough to contain MIN_SIZE_ALIGNED data: if + * mem->next - (ptr + (2*SIZEOF_STRUCT_MEM)) == size, + * struct mem would fit in but no data between mem2 and mem2->next + * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty + * region that couldn't hold data, but when mem->next gets freed, + * the 2 regions would be combined, resulting in more free memory + */ + ptr2 = ptr + SIZEOF_STRUCT_MEM + size; + /* create mem2 struct */ + mem2 = (struct mem *)(void *)&ram[ptr2]; + mem2->used = 0; + mem2->next = mem->next; + mem2->prev = ptr; + /* and insert it between mem and mem->next */ + mem->next = ptr2; + mem->used = 1; + + if (mem2->next != MEM_SIZE_ALIGNED) { + ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2; + } + MEM_STATS_INC_USED(used, (size + SIZEOF_STRUCT_MEM)); + } else { + /* (a mem2 struct does no fit into the user data space of mem and mem->next will always + * be used at this point: if not we have 2 unused structs in a row, plug_holes should have + * take care of this). + * -> near fit or excact fit: do not split, no mem2 creation + * also can't move mem->next directly behind mem, since mem->next + * will always be used at this point! + */ + mem->used = 1; + MEM_STATS_INC_USED(used, mem->next - (mem_size_t)((u8_t *)mem - ram)); + } + + if (mem == lfree) { + /* Find next free block after mem and update lowest free pointer */ + while (lfree->used && lfree != ram_end) { + LWIP_MEM_ALLOC_UNPROTECT(); + /* prevent high interrupt latency... */ + LWIP_MEM_ALLOC_PROTECT(); + lfree = (struct mem *)(void *)&ram[lfree->next]; + } + LWIP_ASSERT("mem_malloc: !lfree->used", ((lfree == ram_end) || (!lfree->used))); + } + LWIP_MEM_ALLOC_UNPROTECT(); + sys_mutex_unlock(&mem_mutex); + LWIP_ASSERT("mem_malloc: allocated memory not above ram_end.", + (mem_ptr_t)mem + SIZEOF_STRUCT_MEM + size <= (mem_ptr_t)ram_end); + LWIP_ASSERT("mem_malloc: allocated memory properly aligned.", + ((mem_ptr_t)mem + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT == 0); + LWIP_ASSERT("mem_malloc: sanity check alignment", + (((mem_ptr_t)mem) & (MEM_ALIGNMENT-1)) == 0); + + return (u8_t *)mem + SIZEOF_STRUCT_MEM; + } + } +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + /* if we got interrupted by a mem_free, try again */ + } while(local_mem_free_count != 0); +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("mem_malloc: could not allocate %"S16_F" bytes\n", (s16_t)size)); + MEM_STATS_INC(err); + LWIP_MEM_ALLOC_UNPROTECT(); + sys_mutex_unlock(&mem_mutex); + return NULL; +} + +#endif /* MEM_USE_POOLS */ +/** + * Contiguously allocates enough space for count objects that are size bytes + * of memory each and returns a pointer to the allocated memory. + * + * The allocated memory is filled with bytes of value zero. + * + * @param count number of objects to allocate + * @param size size of the objects to allocate + * @return pointer to allocated memory / NULL pointer if there is an error + */ +void *mem_calloc(mem_size_t count, mem_size_t size) +{ + void *p; + + /* allocate 'count' objects of size 'size' */ + p = mem_malloc(count * size); + if (p) { + /* zero the memory */ + os_memset(p, 0, count * size); + } + return p; +} + +#endif /* !MEM_LIBC_MALLOC */ diff --git a/variants/lwip/lwip/core/memp.c b/variants/lwip/lwip/core/memp.c new file mode 100755 index 000000000..38bdd1bed --- /dev/null +++ b/variants/lwip/lwip/core/memp.c @@ -0,0 +1,490 @@ +/** + * @file + * Dynamic pool memory manager + * + * lwIP has dedicated pools for many structures (netconn, protocol control blocks, + * packet buffers, ...). All these pools are managed here. + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/memp.h" +#include "lwip/pbuf.h" +#include "lwip/udp.h" +#include "lwip/raw.h" +#include "lwip/tcp_impl.h" +#include "lwip/igmp.h" +#include "lwip/api.h" +#include "lwip/api_msg.h" +#include "lwip/tcpip.h" +#include "lwip/sys.h" +#include "lwip/timers.h" +#include "lwip/stats.h" +#include "netif/etharp.h" +#include "lwip/ip_frag.h" +#include "lwip/snmp_structs.h" +#include "lwip/snmp_msg.h" +#include "lwip/dns.h" +#include "netif/ppp_oe.h" + +#include + +#if !MEMP_MEM_MALLOC /* don't build if not configured for use in lwipopts.h */ + +struct memp { + struct memp *next; +#if MEMP_OVERFLOW_CHECK + const char *file; + int line; +#endif /* MEMP_OVERFLOW_CHECK */ +}; + +#if MEMP_OVERFLOW_CHECK +/* if MEMP_OVERFLOW_CHECK is turned on, we reserve some bytes at the beginning + * and at the end of each element, initialize them as 0xcd and check + * them later. */ +/* If MEMP_OVERFLOW_CHECK is >= 2, on every call to memp_malloc or memp_free, + * every single element in each pool is checked! + * This is VERY SLOW but also very helpful. */ +/* MEMP_SANITY_REGION_BEFORE and MEMP_SANITY_REGION_AFTER can be overridden in + * lwipopts.h to change the amount reserved for checking. */ +#ifndef MEMP_SANITY_REGION_BEFORE +#define MEMP_SANITY_REGION_BEFORE 16 +#endif /* MEMP_SANITY_REGION_BEFORE*/ +#if MEMP_SANITY_REGION_BEFORE > 0 +#define MEMP_SANITY_REGION_BEFORE_ALIGNED LWIP_MEM_ALIGN_SIZE(MEMP_SANITY_REGION_BEFORE) +#else +#define MEMP_SANITY_REGION_BEFORE_ALIGNED 0 +#endif /* MEMP_SANITY_REGION_BEFORE*/ +#ifndef MEMP_SANITY_REGION_AFTER +#define MEMP_SANITY_REGION_AFTER 16 +#endif /* MEMP_SANITY_REGION_AFTER*/ +#if MEMP_SANITY_REGION_AFTER > 0 +#define MEMP_SANITY_REGION_AFTER_ALIGNED LWIP_MEM_ALIGN_SIZE(MEMP_SANITY_REGION_AFTER) +#else +#define MEMP_SANITY_REGION_AFTER_ALIGNED 0 +#endif /* MEMP_SANITY_REGION_AFTER*/ + +/* MEMP_SIZE: save space for struct memp and for sanity check */ +#define MEMP_SIZE (LWIP_MEM_ALIGN_SIZE(sizeof(struct memp)) + MEMP_SANITY_REGION_BEFORE_ALIGNED) +#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x) + MEMP_SANITY_REGION_AFTER_ALIGNED) + +#else /* MEMP_OVERFLOW_CHECK */ + +/* No sanity checks + * We don't need to preserve the struct memp while not allocated, so we + * can save a little space and set MEMP_SIZE to 0. + */ +#define MEMP_SIZE 0 +#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x)) + +#endif /* MEMP_OVERFLOW_CHECK */ + +/** This array holds the first free element of each pool. + * Elements form a linked list. */ +static struct memp *memp_tab[MEMP_MAX]; + +#else /* MEMP_MEM_MALLOC */ + +#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x)) + +#endif /* MEMP_MEM_MALLOC */ + +/** This array holds the element sizes of each pool. */ +#if !MEM_USE_POOLS && !MEMP_MEM_MALLOC +static +#endif +const u32_t memp_sizes[MEMP_MAX] ICACHE_RODATA_ATTR = { //LWIP_MEM_ALIGN_SIZE +#define LWIP_MEMPOOL(name,num,size,desc,attr) LWIP_MEM_ALIGN_SIZE(size), +#include "lwip/memp_std.h" +}; + +u16_t memp_sizes_test[1] = {PBUF_POOL_BUFSIZE,}; + +#if !MEMP_MEM_MALLOC /* don't build if not configured for use in lwipopts.h */ + +/** This array holds the number of elements in each pool. */ +static const u16_t memp_num[MEMP_MAX] = { +#define LWIP_MEMPOOL(name,num,size,desc,attr) (num), +#include "lwip/memp_std.h" +}; + +/** This array holds a textual description of each pool. */ +//#ifdef LWIP_DEBUG +//static const char *memp_desc[MEMP_MAX] = { +const char *memp_desc[MEMP_MAX] = { +#define LWIP_MEMPOOL(name,num,size,desc,attr) (desc), +#include "lwip/memp_std.h" +}; +//#endif /* LWIP_DEBUG */ + +#if MEMP_SEPARATE_POOLS + +/** This creates each memory pool. These are named memp_memory_XXX_base (where + * XXX is the name of the pool defined in memp_std.h). + * To relocate a pool, declare it as extern in cc.h. Example for GCC: + * extern u8_t __attribute__((section(".onchip_mem"))) memp_memory_UDP_PCB_base[]; + */ +#define LWIP_MEMPOOL(name,num,size,desc,attr) u8_t memp_memory_ ## name ## _base \ + [((num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size)))] attr; +#include "lwip/memp_std.h" + +/** This array holds the base of each memory pool. */ +static u8_t *const memp_bases[] = { +#define LWIP_MEMPOOL(name,num,size,desc,attr) memp_memory_ ## name ## _base, +#include "lwip/memp_std.h" +}; + +#else /* MEMP_SEPARATE_POOLS */ + +/** This is the actual memory used by the pools (all pools in one big block). */ +static u8_t memp_memory[MEM_ALIGNMENT - 1 +#define LWIP_MEMPOOL(name,num,size,desc, attr) + ( (num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size) ) ) +#include "lwip/memp_std.h" +]; + +#endif /* MEMP_SEPARATE_POOLS */ + +#if MEMP_SANITY_CHECK +/** + * Check that memp-lists don't form a circle, modify by ives at 2014.4.23. + */ +static int ICACHE_FLASH_ATTR +memp_sanity(void) +{ + s16_t i; + struct memp *t, *h; + + for (i = 0; i < MEMP_MAX; i++) { + t = memp_tab[i]; + if(t != NULL) { + for (h = t->next; (t != NULL) && (h != NULL); t = t->next, + h = (((h->next != NULL) && (h->next->next != NULL)) ? h->next->next : NULL)) { + if (t == h) { + return 0; + } + } + } + } + return 1; +} +#endif /* MEMP_SANITY_CHECK*/ +#if MEMP_OVERFLOW_CHECK +#if defined(LWIP_DEBUG) && MEMP_STATS +static const char * memp_overflow_names[] = { +#define LWIP_MEMPOOL(name,num,size,desc,attr) "/"desc, +#include "lwip/memp_std.h" + }; +#endif + +/** + * Check if a memp element was victim of an overflow + * (e.g. the restricted area after it has been altered) + * + * @param p the memp element to check + * @param memp_type the pool p comes from + */ +static void ICACHE_FLASH_ATTR +memp_overflow_check_element_overflow(struct memp *p, u16_t memp_type) +{ + u16_t k; + u8_t *m; +#if MEMP_SANITY_REGION_AFTER_ALIGNED > 0 + m = (u8_t*)p + MEMP_SIZE + memp_sizes[memp_type]; + for (k = 0; k < MEMP_SANITY_REGION_AFTER_ALIGNED; k++) { + if (m[k] != 0xcd) { + char errstr[128] = "detected memp overflow in pool "; + char digit[] = "0"; + if(memp_type >= 10) { + digit[0] = '0' + (memp_type/10); + strcat(errstr, digit); + } + digit[0] = '0' + (memp_type%10); + strcat(errstr, digit); +#if defined(LWIP_DEBUG) && MEMP_STATS + strcat(errstr, memp_overflow_names[memp_type]); +#endif + LWIP_ASSERT(errstr, 0); + } + } +#endif +} + +/** + * Check if a memp element was victim of an underflow + * (e.g. the restricted area before it has been altered) + * + * @param p the memp element to check + * @param memp_type the pool p comes from + */ +static void ICACHE_FLASH_ATTR +memp_overflow_check_element_underflow(struct memp *p, u16_t memp_type) +{ + u16_t k; + u8_t *m; +#if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0 + m = (u8_t*)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED; + for (k = 0; k < MEMP_SANITY_REGION_BEFORE_ALIGNED; k++) { + if (m[k] != 0xcd) { + char errstr[128] = "detected memp underflow in pool "; + char digit[] = "0"; + if(memp_type >= 10) { + digit[0] = '0' + (memp_type/10); + strcat(errstr, digit); + } + digit[0] = '0' + (memp_type%10); + strcat(errstr, digit); +#if defined(LWIP_DEBUG) && MEMP_STATS + strcat(errstr, memp_overflow_names[memp_type]); +#endif + LWIP_ASSERT(errstr, 0); + } + } +#endif +} + +/** + * Do an overflow check for all elements in every pool. + * + * @see memp_overflow_check_element for a description of the check + */ +static void ICACHE_FLASH_ATTR +memp_overflow_check_all(void) +{ + u16_t i, j; + struct memp *p; + + p = (struct memp *)LWIP_MEM_ALIGN(memp_memory); + for (i = 0; i < MEMP_MAX; ++i) { + p = p; + for (j = 0; j < memp_num[i]; ++j) { + memp_overflow_check_element_overflow(p, i); + p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED); + } + } + p = (struct memp *)LWIP_MEM_ALIGN(memp_memory); + for (i = 0; i < MEMP_MAX; ++i) { + p = p; + for (j = 0; j < memp_num[i]; ++j) { + memp_overflow_check_element_underflow(p, i); + p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED); + } + } +} + +/** + * Initialize the restricted areas of all memp elements in every pool. + */ +static void ICACHE_FLASH_ATTR +memp_overflow_init(void) +{ + u16_t i, j; + struct memp *p; + u8_t *m; + + p = (struct memp *)LWIP_MEM_ALIGN(memp_memory); + for (i = 0; i < MEMP_MAX; ++i) { + p = p; + for (j = 0; j < memp_num[i]; ++j) { +#if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0 + m = (u8_t*)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED; + os_memset(m, 0xcd, MEMP_SANITY_REGION_BEFORE_ALIGNED); +#endif +#if MEMP_SANITY_REGION_AFTER_ALIGNED > 0 + m = (u8_t*)p + MEMP_SIZE + memp_sizes[i]; + os_memset(m, 0xcd, MEMP_SANITY_REGION_AFTER_ALIGNED); +#endif + p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED); + } + } +} +#endif /* MEMP_OVERFLOW_CHECK */ + +/** + * Initialize this module. + * + * Carves out memp_memory into linked lists for each pool-type. + */ +void +memp_init(void) +{ + struct memp *memp; + u16_t i, j; + + for (i = 0; i < MEMP_MAX; ++i) { + MEMP_STATS_AVAIL(used, i, 0); + MEMP_STATS_AVAIL(max, i, 0); + MEMP_STATS_AVAIL(err, i, 0); + MEMP_STATS_AVAIL(avail, i, memp_num[i]); + } + +#if !MEMP_SEPARATE_POOLS + memp = (struct memp *)LWIP_MEM_ALIGN(memp_memory); +#endif /* !MEMP_SEPARATE_POOLS */ + /* for every pool: */ + for (i = 0; i < MEMP_MAX; ++i) { + memp_tab[i] = NULL; +#if MEMP_SEPARATE_POOLS + memp = (struct memp*)memp_bases[i]; +#endif /* MEMP_SEPARATE_POOLS */ + /* create a linked list of memp elements */ + for (j = 0; j < memp_num[i]; ++j) { + memp->next = (struct memp *)memp_tab[i]; + memp_tab[i] = memp; + memp = (struct memp *)(void *)((u8_t *)memp + MEMP_SIZE + memp_sizes[i] +#if MEMP_OVERFLOW_CHECK + + MEMP_SANITY_REGION_AFTER_ALIGNED +#endif + ); + } + } +#if MEMP_OVERFLOW_CHECK + memp_overflow_init(); + /* check everything a first time to see if it worked */ + memp_overflow_check_all(); +#endif /* MEMP_OVERFLOW_CHECK */ +} + +/** + * Get an element from a specific pool. + * + * @param type the pool to get an element from + * + * the debug version has two more parameters: + * @param file file name calling this function + * @param line number of line where this function is called + * + * @return a pointer to the allocated memory or a NULL pointer on error + */ +void * +#if !MEMP_OVERFLOW_CHECK +memp_malloc(memp_t type) +#else +memp_malloc_fn(memp_t type, const char* file, const int line) +#endif +{ + struct memp *memp; + SYS_ARCH_DECL_PROTECT(old_level); + + LWIP_ERROR("memp_malloc: type < MEMP_MAX", (type < MEMP_MAX), return NULL;); + + SYS_ARCH_PROTECT(old_level); +#if MEMP_OVERFLOW_CHECK >= 2 + memp_overflow_check_all(); +#endif /* MEMP_OVERFLOW_CHECK >= 2 */ + + memp = memp_tab[type]; + + if (memp != NULL) { + memp_tab[type] = memp->next; +#if MEMP_OVERFLOW_CHECK + memp->next = NULL; + memp->file = file; + memp->line = line; +#endif /* MEMP_OVERFLOW_CHECK */ + MEMP_STATS_INC_USED(used, type); + LWIP_ASSERT("memp_malloc: memp properly aligned", + ((mem_ptr_t)memp % MEM_ALIGNMENT) == 0); + memp = (struct memp*)(void *)((u8_t*)memp + MEMP_SIZE); + } else { + LWIP_DEBUGF(MEMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("memp_malloc: out of memory in pool %s\n", memp_desc[type])); + MEMP_STATS_INC(err, type); + } + + SYS_ARCH_UNPROTECT(old_level); + + return memp; +} + +/** + * Put an element back into its pool. + * + * @param type the pool where to put mem + * @param mem the memp element to free + */ +void +memp_free(memp_t type, void *mem) +{ + struct memp *memp; + SYS_ARCH_DECL_PROTECT(old_level); + + if (mem == NULL) { + return; + } + LWIP_ASSERT("memp_free: mem properly aligned", + ((mem_ptr_t)mem % MEM_ALIGNMENT) == 0); + + memp = (struct memp *)(void *)((u8_t*)mem - MEMP_SIZE); + + SYS_ARCH_PROTECT(old_level); +#if MEMP_OVERFLOW_CHECK +#if MEMP_OVERFLOW_CHECK >= 2 + memp_overflow_check_all(); +#else + memp_overflow_check_element_overflow(memp, type); + memp_overflow_check_element_underflow(memp, type); +#endif /* MEMP_OVERFLOW_CHECK >= 2 */ +#endif /* MEMP_OVERFLOW_CHECK */ + + MEMP_STATS_DEC(used, type); + + memp->next = memp_tab[type]; + memp_tab[type] = memp; + +#if MEMP_SANITY_CHECK + LWIP_ASSERT("memp sanity", memp_sanity()); +#endif /* MEMP_SANITY_CHECK */ + + SYS_ARCH_UNPROTECT(old_level); +} + +#endif /* MEMP_MEM_MALLOC */ +#if 0 +void memp_dump(void) +{ + printf("sizeof raw_pcb %u, memp_s1 %u, %s\n", sizeof(struct raw_pcb), memp_sizes[0], memp_desc[0]); + printf("sizeof udp_pcb %u, memp_s2 %u, %s\n", sizeof(struct udp_pcb), memp_sizes[1], memp_desc[1]); + printf("sizeof tcp_pcb %u, memp_s3 %u, %s\n", sizeof(struct tcp_pcb), memp_sizes[2], memp_desc[2]); + printf("sizeof tcp_pcb_listen %u, memp_s4 %u, %s\n", sizeof(struct tcp_pcb_listen), memp_sizes[3], memp_desc[3]); + printf("sizeof tcp_seg %u, memp_s5 %u, %s\n", sizeof(struct tcp_seg), memp_sizes[4], memp_desc[4]); + printf("sizeof sys_timeo %u, memp_s6 %u, %s\n", sizeof(struct sys_timeo), memp_sizes[5], memp_desc[5]); + printf("sizeof pbuf %u, memp_s7 %u, %s\n", sizeof(struct pbuf), memp_sizes[6], memp_desc[6]); + printf("align pbuf size %u, memp_s8 %u, %s\n", (PBUF_POOL_BUFSIZE), memp_sizes[7], memp_desc[7]); + printf("TCP_MSS %d PBUF_LINK_HLEN %d ETH_PAD_SIZE %d\n", TCP_MSS, PBUF_LINK_HLEN, ETH_PAD_SIZE); + printf("TCP_MSS + PBUF_LINK_HLEN + ETH_PAD_SIZE %d \n", TCP_MSS+PBUF_LINK_HLEN+ETH_PAD_SIZE+40); + printf("test size %u\n",memp_sizes_test[0]); + printf("sizeof memp_memory_PBUF_pool %u \n", sizeof(memp_memory_PBUF_POOL_base)); +} +#endif //0000 diff --git a/variants/lwip/lwip/core/netif.c b/variants/lwip/lwip/core/netif.c new file mode 100755 index 000000000..6913b408f --- /dev/null +++ b/variants/lwip/lwip/core/netif.c @@ -0,0 +1,758 @@ +/** + * @file + * lwIP network interface abstraction + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/def.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/tcp_impl.h" +#include "lwip/snmp.h" +#include "lwip/igmp.h" +#include "netif/etharp.h" +#include "lwip/stats.h" +#if ENABLE_LOOPBACK +#include "lwip/sys.h" +#if LWIP_NETIF_LOOPBACK_MULTITHREADING +#include "lwip/tcpip.h" +#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */ +#endif /* ENABLE_LOOPBACK */ + +#if LWIP_AUTOIP +#include "lwip/autoip.h" +#endif /* LWIP_AUTOIP */ +#if LWIP_DHCP +#include "lwip/dhcp.h" +#endif /* LWIP_DHCP */ + +#if LWIP_NETIF_STATUS_CALLBACK +#define NETIF_STATUS_CALLBACK(n) do{ if (n->status_callback) { (n->status_callback)(n); }}while(0) +#else +#define NETIF_STATUS_CALLBACK(n) +#endif /* LWIP_NETIF_STATUS_CALLBACK */ + +#if LWIP_NETIF_LINK_CALLBACK +#define NETIF_LINK_CALLBACK(n) do{ if (n->link_callback) { (n->link_callback)(n); }}while(0) +#else +#define NETIF_LINK_CALLBACK(n) +#endif /* LWIP_NETIF_LINK_CALLBACK */ + +struct netif *netif_list; +struct netif *netif_default; + +#if LWIP_HAVE_LOOPIF +static struct netif loop_netif; + +/** + * Initialize a lwip network interface structure for a loopback interface + * + * @param netif the lwip network interface structure for this loopif + * @return ERR_OK if the loopif is initialized + * ERR_MEM if private data couldn't be allocated + */ + static err_t ICACHE_FLASH_ATTR +netif_loopif_init(struct netif *netif) +{ + /* initialize the snmp variables and counters inside the struct netif + * ifSpeed: no assumption can be made! + */ + NETIF_INIT_SNMP(netif, snmp_ifType_softwareLoopback, 0); + + netif->name[0] = 'l'; + netif->name[1] = 'o'; + netif->output = netif_loop_output; + return ERR_OK; +} +#endif /* LWIP_HAVE_LOOPIF */ + +void +netif_init(void) +{ +#if LWIP_HAVE_LOOPIF + ip_addr_t loop_ipaddr, loop_netmask, loop_gw; + IP4_ADDR(&loop_gw, 127,0,0,1); + IP4_ADDR(&loop_ipaddr, 127,0,0,1); + IP4_ADDR(&loop_netmask, 255,0,0,0); + +#if NO_SYS + netif_add(&loop_netif, &loop_ipaddr, &loop_netmask, &loop_gw, NULL, netif_loopif_init, ip_input); +#else /* NO_SYS */ + netif_add(&loop_netif, &loop_ipaddr, &loop_netmask, &loop_gw, NULL, netif_loopif_init, tcpip_input); +#endif /* NO_SYS */ + netif_set_up(&loop_netif); + +#endif /* LWIP_HAVE_LOOPIF */ +} + +/** + * Add a network interface to the list of lwIP netifs. + * + * @param netif a pre-allocated netif structure + * @param ipaddr IP address for the new netif + * @param netmask network mask for the new netif + * @param gw default gateway IP address for the new netif + * @param state opaque data passed to the new netif + * @param init callback function that initializes the interface + * @param input callback function that is called to pass + * ingress packets up in the protocol layer stack. + * + * @return netif, or NULL if failed. + */ +struct netif * +netif_add(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask, + ip_addr_t *gw, void *state, netif_init_fn init, netif_input_fn input) +{ + static u8_t netifnum = 0; + + LWIP_ASSERT("No init function given", init != NULL); + + /* reset new interface configuration state */ + ip_addr_set_zero(&netif->ip_addr); + ip_addr_set_zero(&netif->netmask); + ip_addr_set_zero(&netif->gw); + netif->flags = 0; +#if LWIP_DHCP + /* netif not under DHCP control by default */ + netif->dhcp = NULL; + netif->dhcps_pcb = NULL; +#endif /* LWIP_DHCP */ +#if LWIP_AUTOIP + /* netif not under AutoIP control by default */ + netif->autoip = NULL; +#endif /* LWIP_AUTOIP */ +#if LWIP_NETIF_STATUS_CALLBACK + netif->status_callback = NULL; +#endif /* LWIP_NETIF_STATUS_CALLBACK */ +#if LWIP_NETIF_LINK_CALLBACK + netif->link_callback = NULL; +#endif /* LWIP_NETIF_LINK_CALLBACK */ +#if LWIP_IGMP + netif->igmp_mac_filter = NULL; +#endif /* LWIP_IGMP */ +#if ENABLE_LOOPBACK + netif->loop_first = NULL; + netif->loop_last = NULL; +#endif /* ENABLE_LOOPBACK */ + + /* remember netif specific state information data */ + netif->state = state; + netif->num = netifnum++; + netif->input = input; +#if LWIP_NETIF_HWADDRHINT + netif->addr_hint = NULL; +#endif /* LWIP_NETIF_HWADDRHINT*/ +#if ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS + netif->loop_cnt_current = 0; +#endif /* ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS */ + + netif_set_addr(netif, ipaddr, netmask, gw); + + /* call user specified initialization function for netif */ + if (init(netif) != ERR_OK) { + return NULL; + } + + /* add this netif to the list */ + netif->next = netif_list; + netif_list = netif; + snmp_inc_iflist(); + +#if LWIP_IGMP + /* start IGMP processing */ + if (netif->flags & NETIF_FLAG_IGMP) { + igmp_start(netif); + } +#endif /* LWIP_IGMP */ + + LWIP_DEBUGF(NETIF_DEBUG, ("netif: added interface %c%c IP addr ", + netif->name[0], netif->name[1])); + ip_addr_debug_print(NETIF_DEBUG, ipaddr); + LWIP_DEBUGF(NETIF_DEBUG, (" netmask ")); + ip_addr_debug_print(NETIF_DEBUG, netmask); + LWIP_DEBUGF(NETIF_DEBUG, (" gw ")); + ip_addr_debug_print(NETIF_DEBUG, gw); + LWIP_DEBUGF(NETIF_DEBUG, ("\n")); + return netif; +} + +/** + * Change IP address configuration for a network interface (including netmask + * and default gateway). + * + * @param netif the network interface to change + * @param ipaddr the new IP address + * @param netmask the new netmask + * @param gw the new default gateway + */ +void +netif_set_addr(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask, + ip_addr_t *gw) +{ + netif_set_ipaddr(netif, ipaddr); + netif_set_netmask(netif, netmask); + netif_set_gw(netif, gw); +} + +/** + * Remove a network interface from the list of lwIP netifs. + * + * @param netif the network interface to remove + */ +void +netif_remove(struct netif *netif) +{ + if (netif == NULL) { + return; + } + +#if LWIP_IGMP + /* stop IGMP processing */ + if (netif->flags & NETIF_FLAG_IGMP) { + igmp_stop(netif); + } +#endif /* LWIP_IGMP */ + if (netif_is_up(netif)) { + /* set netif down before removing (call callback function) */ + netif_set_down(netif); + } + + snmp_delete_ipaddridx_tree(netif); + + /* is it the first netif? */ + if (netif_list == netif) { + netif_list = netif->next; + } else { + /* look for netif further down the list */ + struct netif * tmpNetif; + for (tmpNetif = netif_list; tmpNetif != NULL; tmpNetif = tmpNetif->next) { + if (tmpNetif->next == netif) { + tmpNetif->next = netif->next; + break; + } + } + if (tmpNetif == NULL) + return; /* we didn't find any netif today */ + } + snmp_dec_iflist(); + /* this netif is default? */ + if (netif_default == netif) { + /* reset default netif */ + netif_set_default(NULL); + } + LWIP_DEBUGF( NETIF_DEBUG, ("netif_remove: removed netif\n") ); +} + +/** + * Find a network interface by searching for its name + * + * @param name the name of the netif (like netif->name) plus concatenated number + * in ascii representation (e.g. 'en0') + */ +struct netif * +netif_find(char *name) +{ + struct netif *netif; + u8_t num; + + if (name == NULL) { + return NULL; + } + + num = name[2] - '0'; + + for(netif = netif_list; netif != NULL; netif = netif->next) { + if (num == netif->num && + name[0] == netif->name[0] && + name[1] == netif->name[1]) { + LWIP_DEBUGF(NETIF_DEBUG, ("netif_find: found %c%c\n", name[0], name[1])); + return netif; + } + } + LWIP_DEBUGF(NETIF_DEBUG, ("netif_find: didn't find %c%c\n", name[0], name[1])); + return NULL; +} + +/** + * Change the IP address of a network interface + * + * @param netif the network interface to change + * @param ipaddr the new IP address + * + * @note call netif_set_addr() if you also want to change netmask and + * default gateway + */ +void +netif_set_ipaddr(struct netif *netif, ip_addr_t *ipaddr) +{ + /* TODO: Handling of obsolete pcbs */ + /* See: http://mail.gnu.org/archive/html/lwip-users/2003-03/msg00118.html */ +#if LWIP_TCP + struct tcp_pcb *pcb; + struct tcp_pcb_listen *lpcb; + + /* address is actually being changed? */ + if (ipaddr && (ip_addr_cmp(ipaddr, &(netif->ip_addr))) == 0) { + /* extern struct tcp_pcb *tcp_active_pcbs; defined by tcp.h */ + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: netif address being changed\n")); + pcb = tcp_active_pcbs; + while (pcb != NULL) { + /* PCB bound to current local interface address? */ + if (ip_addr_cmp(&(pcb->local_ip), &(netif->ip_addr)) +#if LWIP_AUTOIP + /* connections to link-local addresses must persist (RFC3927 ch. 1.9) */ + && !ip_addr_islinklocal(&(pcb->local_ip)) +#endif /* LWIP_AUTOIP */ + ) { + /* this connection must be aborted */ + struct tcp_pcb *next = pcb->next; + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: aborting TCP pcb %p\n", (void *)pcb)); + tcp_abort(pcb); + pcb = next; + } else { + pcb = pcb->next; + } + } + for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { + /* PCB bound to current local interface address? */ + if ((!(ip_addr_isany(&(lpcb->local_ip)))) && + (ip_addr_cmp(&(lpcb->local_ip), &(netif->ip_addr)))) { + /* The PCB is listening to the old ipaddr and + * is set to listen to the new one instead */ + ip_addr_set(&(lpcb->local_ip), ipaddr); + } + } + } +#endif + snmp_delete_ipaddridx_tree(netif); + snmp_delete_iprteidx_tree(0,netif); + /* set new IP address to netif */ + ip_addr_set(&(netif->ip_addr), ipaddr); + snmp_insert_ipaddridx_tree(netif); + snmp_insert_iprteidx_tree(0,netif); + + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: IP address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + netif->name[0], netif->name[1], + ip4_addr1_16(&netif->ip_addr), + ip4_addr2_16(&netif->ip_addr), + ip4_addr3_16(&netif->ip_addr), + ip4_addr4_16(&netif->ip_addr))); +} + +/** + * Change the default gateway for a network interface + * + * @param netif the network interface to change + * @param gw the new default gateway + * + * @note call netif_set_addr() if you also want to change ip address and netmask + */ +void +netif_set_gw(struct netif *netif, ip_addr_t *gw) +{ + ip_addr_set(&(netif->gw), gw); + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: GW address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + netif->name[0], netif->name[1], + ip4_addr1_16(&netif->gw), + ip4_addr2_16(&netif->gw), + ip4_addr3_16(&netif->gw), + ip4_addr4_16(&netif->gw))); +} + +/** + * Change the netmask of a network interface + * + * @param netif the network interface to change + * @param netmask the new netmask + * + * @note call netif_set_addr() if you also want to change ip address and + * default gateway + */ +void +netif_set_netmask(struct netif *netif, ip_addr_t *netmask) +{ + snmp_delete_iprteidx_tree(0, netif); + /* set new netmask to netif */ + ip_addr_set(&(netif->netmask), netmask); + snmp_insert_iprteidx_tree(0, netif); + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: netmask of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + netif->name[0], netif->name[1], + ip4_addr1_16(&netif->netmask), + ip4_addr2_16(&netif->netmask), + ip4_addr3_16(&netif->netmask), + ip4_addr4_16(&netif->netmask))); +} + +/** + * Set a network interface as the default network interface + * (used to output all packets for which no specific route is found) + * + * @param netif the default network interface + */ +void +netif_set_default(struct netif *netif) +{ + if (netif == NULL) { + /* remove default route */ + snmp_delete_iprteidx_tree(1, netif); + } else { + /* install default route */ + snmp_insert_iprteidx_tree(1, netif); + } + netif_default = netif; + LWIP_DEBUGF(NETIF_DEBUG, ("netif: setting default interface %c%c\n", + netif ? netif->name[0] : '\'', netif ? netif->name[1] : '\'')); +} + +/** + * Bring an interface up, available for processing + * traffic. + * + * @note: Enabling DHCP on a down interface will make it come + * up once configured. + * + * @see dhcp_start() + */ +void netif_set_up(struct netif *netif) +{ + if (!(netif->flags & NETIF_FLAG_UP)) { + netif->flags |= NETIF_FLAG_UP; + +#if LWIP_SNMP + snmp_get_sysuptime(&netif->ts); +#endif /* LWIP_SNMP */ + + NETIF_STATUS_CALLBACK(netif); + + if (netif->flags & NETIF_FLAG_LINK_UP) { +#if LWIP_ARP + /* For Ethernet network interfaces, we would like to send a "gratuitous ARP" */ + if (netif->flags & (NETIF_FLAG_ETHARP)) { + etharp_gratuitous(netif); + } +#endif /* LWIP_ARP */ + +#if LWIP_IGMP + /* resend IGMP memberships */ + if (netif->flags & NETIF_FLAG_IGMP) { + igmp_report_groups( netif); + } +#endif /* LWIP_IGMP */ + } + } +} + +/** + * Bring an interface down, disabling any traffic processing. + * + * @note: Enabling DHCP on a down interface will make it come + * up once configured. + * + * @see dhcp_start() + */ +void netif_set_down(struct netif *netif) +{ + if (netif->flags & NETIF_FLAG_UP) { + netif->flags &= ~NETIF_FLAG_UP; +#if LWIP_SNMP + snmp_get_sysuptime(&netif->ts); +#endif + +#if LWIP_ARP + if (netif->flags & NETIF_FLAG_ETHARP) { + etharp_cleanup_netif(netif); + } +#endif /* LWIP_ARP */ + NETIF_STATUS_CALLBACK(netif); + } +} + +#if LWIP_NETIF_STATUS_CALLBACK +/** + * Set callback to be called when interface is brought up/down + */ +void netif_set_status_callback(struct netif *netif, netif_status_callback_fn status_callback) +{ + if (netif) { + netif->status_callback = status_callback; + } +} +#endif /* LWIP_NETIF_STATUS_CALLBACK */ + +/** + * Called by a driver when its link goes up + */ +void netif_set_link_up(struct netif *netif ) +{ + if (!(netif->flags & NETIF_FLAG_LINK_UP)) { + netif->flags |= NETIF_FLAG_LINK_UP; + +#if LWIP_DHCP + if (netif->dhcp) { + dhcp_network_changed(netif); + } +#endif /* LWIP_DHCP */ + +#if LWIP_AUTOIP + if (netif->autoip) { + autoip_network_changed(netif); + } +#endif /* LWIP_AUTOIP */ + + if (netif->flags & NETIF_FLAG_UP) { +#if LWIP_ARP + /* For Ethernet network interfaces, we would like to send a "gratuitous ARP" */ + if (netif->flags & NETIF_FLAG_ETHARP) { + etharp_gratuitous(netif); + } +#endif /* LWIP_ARP */ + +#if LWIP_IGMP + /* resend IGMP memberships */ + if (netif->flags & NETIF_FLAG_IGMP) { + igmp_report_groups( netif); + } +#endif /* LWIP_IGMP */ + } + NETIF_LINK_CALLBACK(netif); + } +} + +/** + * Called by a driver when its link goes down + */ +void netif_set_link_down(struct netif *netif ) +{ + if (netif->flags & NETIF_FLAG_LINK_UP) { + netif->flags &= ~NETIF_FLAG_LINK_UP; + NETIF_LINK_CALLBACK(netif); + } +} + +#if LWIP_NETIF_LINK_CALLBACK +/** + * Set callback to be called when link is brought up/down + */ +void netif_set_link_callback(struct netif *netif, netif_status_callback_fn link_callback) +{ + if (netif) { + netif->link_callback = link_callback; + } +} +#endif /* LWIP_NETIF_LINK_CALLBACK */ + +#if ENABLE_LOOPBACK +/** + * Send an IP packet to be received on the same netif (loopif-like). + * The pbuf is simply copied and handed back to netif->input. + * In multithreaded mode, this is done directly since netif->input must put + * the packet on a queue. + * In callback mode, the packet is put on an internal queue and is fed to + * netif->input by netif_poll(). + * + * @param netif the lwip network interface structure + * @param p the (IP) packet to 'send' + * @param ipaddr the ip address to send the packet to (not used) + * @return ERR_OK if the packet has been sent + * ERR_MEM if the pbuf used to copy the packet couldn't be allocated + */ +err_t +netif_loop_output(struct netif *netif, struct pbuf *p, + ip_addr_t *ipaddr) +{ + struct pbuf *r; + err_t err; + struct pbuf *last; +#if LWIP_LOOPBACK_MAX_PBUFS + u8_t clen = 0; +#endif /* LWIP_LOOPBACK_MAX_PBUFS */ + /* If we have a loopif, SNMP counters are adjusted for it, + * if not they are adjusted for 'netif'. */ +#if LWIP_SNMP +#if LWIP_HAVE_LOOPIF + struct netif *stats_if = &loop_netif; +#else /* LWIP_HAVE_LOOPIF */ + struct netif *stats_if = netif; +#endif /* LWIP_HAVE_LOOPIF */ +#endif /* LWIP_SNMP */ + SYS_ARCH_DECL_PROTECT(lev); + LWIP_UNUSED_ARG(ipaddr); + + /* Allocate a new pbuf */ + r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM); + if (r == NULL) { + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + snmp_inc_ifoutdiscards(stats_if); + return ERR_MEM; + } +#if LWIP_LOOPBACK_MAX_PBUFS + clen = pbuf_clen(r); + /* check for overflow or too many pbuf on queue */ + if(((netif->loop_cnt_current + clen) < netif->loop_cnt_current) || + ((netif->loop_cnt_current + clen) > LWIP_LOOPBACK_MAX_PBUFS)) { + pbuf_free(r); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + snmp_inc_ifoutdiscards(stats_if); + return ERR_MEM; + } + netif->loop_cnt_current += clen; +#endif /* LWIP_LOOPBACK_MAX_PBUFS */ + + /* Copy the whole pbuf queue p into the single pbuf r */ + if ((err = pbuf_copy(r, p)) != ERR_OK) { + pbuf_free(r); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + snmp_inc_ifoutdiscards(stats_if); + return err; + } + + /* Put the packet on a linked list which gets emptied through calling + netif_poll(). */ + + /* let last point to the last pbuf in chain r */ + for (last = r; last->next != NULL; last = last->next); + + SYS_ARCH_PROTECT(lev); + if(netif->loop_first != NULL) { + LWIP_ASSERT("if first != NULL, last must also be != NULL", netif->loop_last != NULL); + netif->loop_last->next = r; + netif->loop_last = last; + } else { + netif->loop_first = r; + netif->loop_last = last; + } + SYS_ARCH_UNPROTECT(lev); + + LINK_STATS_INC(link.xmit); + snmp_add_ifoutoctets(stats_if, p->tot_len); + snmp_inc_ifoutucastpkts(stats_if); + +#if LWIP_NETIF_LOOPBACK_MULTITHREADING + /* For multithreading environment, schedule a call to netif_poll */ + tcpip_callback((tcpip_callback_fn)netif_poll, netif); +#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */ + + return ERR_OK; +} + +/** + * Call netif_poll() in the main loop of your application. This is to prevent + * reentering non-reentrant functions like tcp_input(). Packets passed to + * netif_loop_output() are put on a list that is passed to netif->input() by + * netif_poll(). + */ +void +netif_poll(struct netif *netif) +{ + struct pbuf *in; + /* If we have a loopif, SNMP counters are adjusted for it, + * if not they are adjusted for 'netif'. */ +#if LWIP_SNMP +#if LWIP_HAVE_LOOPIF + struct netif *stats_if = &loop_netif; +#else /* LWIP_HAVE_LOOPIF */ + struct netif *stats_if = netif; +#endif /* LWIP_HAVE_LOOPIF */ +#endif /* LWIP_SNMP */ + SYS_ARCH_DECL_PROTECT(lev); + + do { + /* Get a packet from the list. With SYS_LIGHTWEIGHT_PROT=1, this is protected */ + SYS_ARCH_PROTECT(lev); + in = netif->loop_first; + if (in != NULL) { + struct pbuf *in_end = in; +#if LWIP_LOOPBACK_MAX_PBUFS + u8_t clen = pbuf_clen(in); + /* adjust the number of pbufs on queue */ + LWIP_ASSERT("netif->loop_cnt_current underflow", + ((netif->loop_cnt_current - clen) < netif->loop_cnt_current)); + netif->loop_cnt_current -= clen; +#endif /* LWIP_LOOPBACK_MAX_PBUFS */ + while (in_end->len != in_end->tot_len) { + LWIP_ASSERT("bogus pbuf: len != tot_len but next == NULL!", in_end->next != NULL); + in_end = in_end->next; + } + /* 'in_end' now points to the last pbuf from 'in' */ + if (in_end == netif->loop_last) { + /* this was the last pbuf in the list */ + netif->loop_first = netif->loop_last = NULL; + } else { + /* pop the pbuf off the list */ + netif->loop_first = in_end->next; + LWIP_ASSERT("should not be null since first != last!", netif->loop_first != NULL); + } + /* De-queue the pbuf from its successors on the 'loop_' list. */ + in_end->next = NULL; + } + SYS_ARCH_UNPROTECT(lev); + + if (in != NULL) { + LINK_STATS_INC(link.recv); + snmp_add_ifinoctets(stats_if, in->tot_len); + snmp_inc_ifinucastpkts(stats_if); + /* loopback packets are always IP packets! */ + if (ip_input(in, netif) != ERR_OK) { + pbuf_free(in); + } + /* Don't reference the packet any more! */ + in = NULL; + } + /* go on while there is a packet on the list */ + } while (netif->loop_first != NULL); +} + +#if !LWIP_NETIF_LOOPBACK_MULTITHREADING +/** + * Calls netif_poll() for every netif on the netif_list. + */ +void +netif_poll_all(void) +{ + struct netif *netif = netif_list; + /* loop through netifs */ + while (netif != NULL) { + netif_poll(netif); + /* proceed to next network interface */ + netif = netif->next; + } +} +#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */ +#endif /* ENABLE_LOOPBACK */ diff --git a/variants/lwip/lwip/core/pbuf.c b/variants/lwip/lwip/core/pbuf.c new file mode 100755 index 000000000..61967840d --- /dev/null +++ b/variants/lwip/lwip/core/pbuf.c @@ -0,0 +1,1257 @@ +/** + * @file + * Packet buffer management + * + * Packets are built from the pbuf data structure. It supports dynamic + * memory allocation for packet contents or can reference externally + * managed packet contents both in RAM and ROM. Quick allocation for + * incoming packets is provided through pools with fixed sized pbufs. + * + * A packet may span over multiple pbufs, chained as a singly linked + * list. This is called a "pbuf chain". + * + * Multiple packets may be queued, also using this singly linked list. + * This is called a "packet queue". + * + * So, a packet queue consists of one or more pbuf chains, each of + * which consist of one or more pbufs. CURRENTLY, PACKET QUEUES ARE + * NOT SUPPORTED!!! Use helper structs to queue multiple packets. + * + * The differences between a pbuf chain and a packet queue are very + * precise but subtle. + * + * The last pbuf of a packet has a ->tot_len field that equals the + * ->len field. It can be found by traversing the list. If the last + * pbuf of a packet has a ->next field other than NULL, more packets + * are on the queue. + * + * Therefore, looping through a pbuf of a single packet, has an + * loop end condition (tot_len == p->len), NOT (next == NULL). + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/stats.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/pbuf.h" +#include "lwip/sys.h" +#include "arch/perf.h" +#if TCP_QUEUE_OOSEQ +#include "lwip/tcp_impl.h" +#endif +#if LWIP_CHECKSUM_ON_COPY +#include "lwip/inet_chksum.h" +#endif + +#include + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +#ifdef EBUF_LWIP +#define EP_OFFSET 36 +#else +#define EP_OFFSET 0 +#endif /* ESF_LWIP */ + +#define SIZEOF_STRUCT_PBUF LWIP_MEM_ALIGN_SIZE(sizeof(struct pbuf)) +/* Since the pool is created in memp, PBUF_POOL_BUFSIZE will be automatically + aligned there. Therefore, PBUF_POOL_BUFSIZE_ALIGNED can be used here. */ +#define PBUF_POOL_BUFSIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(PBUF_POOL_BUFSIZE) + +/** + * Attempt to reclaim some memory from queued out-of-sequence TCP segments + * if we run out of pool pbufs. It's better to give priority to new packets + * if we're running out. + */ +#if TCP_QUEUE_OOSEQ +void ICACHE_FLASH_ATTR +pbuf_free_ooseq_new(void* arg) +{ + struct tcp_pcb* pcb; + struct tcp_seg *head = NULL; + struct tcp_seg *seg1 = NULL; + struct tcp_seg *seg2 = NULL; + for (pcb = tcp_active_pcbs; NULL != pcb; pcb = pcb->next) { + head = pcb->ooseq; + seg1 = head; + if (head != NULL) { + if (seg1->next == NULL){ + head = head->next; + tcp_seg_free(seg1); + pcb->ooseq = head; + } else { + while (seg1 != NULL){ + seg2 = seg1; + seg2 = seg2->next; + if (seg2 ->next == NULL){ + seg1->next = seg2->next; + tcp_seg_free(seg2); + break; + } + seg1 = seg1->next; + } + pcb->ooseq = head; + } + } + } +} +#endif + +#if !LWIP_TCP || !TCP_QUEUE_OOSEQ || NO_SYS +#define PBUF_POOL_IS_EMPTY() +#else /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || NO_SYS */ +/** Define this to 0 to prevent freeing ooseq pbufs when the PBUF_POOL is empty */ +#ifndef PBUF_POOL_FREE_OOSEQ +#define PBUF_POOL_FREE_OOSEQ 1 +#endif /* PBUF_POOL_FREE_OOSEQ */ + +#if PBUF_POOL_FREE_OOSEQ +#include "lwip/tcpip.h" +#define PBUF_POOL_IS_EMPTY() pbuf_pool_is_empty() +static u8_t pbuf_free_ooseq_queued; +/** + * Attempt to reclaim some memory from queued out-of-sequence TCP segments + * if we run out of pool pbufs. It's better to give priority to new packets + * if we're running out. + * + * This must be done in the correct thread context therefore this function + * can only be used with NO_SYS=0 and through tcpip_callback. + */ +static void ICACHE_FLASH_ATTR +pbuf_free_ooseq(void* arg) +{ + struct tcp_pcb* pcb; + SYS_ARCH_DECL_PROTECT(old_level); + LWIP_UNUSED_ARG(arg); + + SYS_ARCH_PROTECT(old_level); + pbuf_free_ooseq_queued = 0; + SYS_ARCH_UNPROTECT(old_level); + + for (pcb = tcp_active_pcbs; NULL != pcb; pcb = pcb->next) { + if (NULL != pcb->ooseq) { + /** Free the ooseq pbufs of one PCB only */ + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free_ooseq: freeing out-of-sequence pbufs\n")); + tcp_segs_free(pcb->ooseq); + pcb->ooseq = NULL; + return; + } + } +} + +/** Queue a call to pbuf_free_ooseq if not already queued. */ +static void ICACHE_FLASH_ATTR +pbuf_pool_is_empty(void) +{ + u8_t queued; + SYS_ARCH_DECL_PROTECT(old_level); + + SYS_ARCH_PROTECT(old_level); + queued = pbuf_free_ooseq_queued; + pbuf_free_ooseq_queued = 1; + SYS_ARCH_UNPROTECT(old_level); + + if(!queued) { + /* queue a call to pbuf_free_ooseq if not already queued */ + if(tcpip_callback_with_block(pbuf_free_ooseq, NULL, 0) != ERR_OK) { + SYS_ARCH_PROTECT(old_level); + pbuf_free_ooseq_queued = 0; + SYS_ARCH_UNPROTECT(old_level); + } + } +} +#endif /* PBUF_POOL_FREE_OOSEQ */ +#endif /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || NO_SYS */ + +/** + * Allocates a pbuf of the given type (possibly a chain for PBUF_POOL type). + * + * The actual memory allocated for the pbuf is determined by the + * layer at which the pbuf is allocated and the requested size + * (from the size parameter). + * + * @param layer flag to define header size + * @param length size of the pbuf's payload + * @param type this parameter decides how and where the pbuf + * should be allocated as follows: + * + * - PBUF_RAM: buffer memory for pbuf is allocated as one large + * chunk. This includes protocol headers as well. + * - PBUF_ROM: no buffer memory is allocated for the pbuf, even for + * protocol headers. Additional headers must be prepended + * by allocating another pbuf and chain in to the front of + * the ROM pbuf. It is assumed that the memory used is really + * similar to ROM in that it is immutable and will not be + * changed. Memory which is dynamic should generally not + * be attached to PBUF_ROM pbufs. Use PBUF_REF instead. + * - PBUF_REF: no buffer memory is allocated for the pbuf, even for + * protocol headers. It is assumed that the pbuf is only + * being used in a single thread. If the pbuf gets queued, + * then pbuf_take should be called to copy the buffer. + * - PBUF_POOL: the pbuf is allocated as a pbuf chain, with pbufs from + * the pbuf pool that is allocated during pbuf_init(). + * + * @return the allocated pbuf. If multiple pbufs where allocated, this + * is the first pbuf of a pbuf chain. + */ +struct pbuf * +pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type) +{ + struct pbuf *p, *q, *r; + u16_t offset; + s32_t rem_len; /* remaining length */ + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F")\n", length)); + + /* determine header offset */ + offset = 0; + switch (layer) { + case PBUF_TRANSPORT: + /* add room for transport (often TCP) layer header */ + offset += PBUF_TRANSPORT_HLEN; + /* FALLTHROUGH */ + case PBUF_IP: + /* add room for IP layer header */ + offset += PBUF_IP_HLEN; + /* FALLTHROUGH */ + case PBUF_LINK: + /* add room for link layer header */ + offset += PBUF_LINK_HLEN; + +#ifdef PBUF_RSV_FOR_WLAN + /* + * 1. LINK_HLEN 14Byte will be remove in WLAN layer + * 2. IEEE80211_HDR_MAX_LEN needs 40 bytes. + * 3. encryption needs exra 4 bytes ahead of actual data payload, and require + * DAddr and SAddr to be 4-byte aligned. + * 4. TRANSPORT and IP are all 20, 4 bytes aligned, nice... + * 5. LCC add 6 bytes more, We don't consider WAPI yet... + * 6. define LWIP_MEM_ALIGN to be 4 Byte aligned, pbuf struct is 16B, Only thing may be + * matter is ether_hdr is not 4B aligned. + * + * So, we need extra (40 + 4 - 14) = 30 and it's happen to be 4-Byte aligned + * + * 1. lwip + * | empty 30B | eth_hdr (14B) | payload ...| + * total: 44B ahead payload + * 2. net80211 + * | max 80211 hdr, 32B | ccmp/tkip iv (8B) | sec rsv(4B) | payload ...| + * total: 40B ahead sec_rsv and 44B ahead payload + * + */ + offset += EP_OFFSET; //remove LINK hdr in wlan +#endif /* PBUF_RSV_FOR_WLAN */ + + break; + case PBUF_RAW: +#ifdef PBUF_RSV_FOR_WLAN + /* + * RAW pbuf suppose + */ + offset += EP_OFFSET; //remove LINK hdr in wlan +#endif /* PBUF_RAW */ + break; + default: + LWIP_ASSERT("pbuf_alloc: bad pbuf layer", 0); + return NULL; + } + + switch (type) { + case PBUF_POOL: + /* allocate head of pbuf chain into p */ + p = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL); + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc: allocated pbuf %p\n", (void *)p)); + if (p == NULL) { + PBUF_POOL_IS_EMPTY(); + return NULL; + } + p->type = type; + p->next = NULL; + + /* make the payload pointer point 'offset' bytes into pbuf data memory */ + p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + (SIZEOF_STRUCT_PBUF + offset))); + LWIP_ASSERT("pbuf_alloc: pbuf p->payload properly aligned", + ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0); + /* the total length of the pbuf chain is the requested size */ + p->tot_len = length; + /* set the length of the first pbuf in the chain */ + p->len = LWIP_MIN(length, PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)); + LWIP_ASSERT("check p->payload + p->len does not overflow pbuf", + ((u8_t*)p->payload + p->len <= + (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED)); + LWIP_ASSERT("PBUF_POOL_BUFSIZE must be bigger than MEM_ALIGNMENT", + (PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)) > 0 ); + /* set reference count (needed here in case we fail) */ + p->ref = 1; + + /* now allocate the tail of the pbuf chain */ + + /* remember first pbuf for linkage in next iteration */ + r = p; + /* remaining length to be allocated */ + rem_len = length - p->len; + /* any remaining pbufs to be allocated? */ + while (rem_len > 0) { + q = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL); + if (q == NULL) { + PBUF_POOL_IS_EMPTY(); + /* free chain so far allocated */ + pbuf_free(p); + /* bail out unsuccesfully */ + return NULL; + } + q->type = type; + q->flags = 0; + q->next = NULL; + /* make previous pbuf point to this pbuf */ + r->next = q; + /* set total length of this pbuf and next in chain */ + LWIP_ASSERT("rem_len < max_u16_t", rem_len < 0xffff); + q->tot_len = (u16_t)rem_len; + /* this pbuf length is pool size, unless smaller sized tail */ + q->len = LWIP_MIN((u16_t)rem_len, PBUF_POOL_BUFSIZE_ALIGNED); + q->payload = (void *)((u8_t *)q + SIZEOF_STRUCT_PBUF); + LWIP_ASSERT("pbuf_alloc: pbuf q->payload properly aligned", + ((mem_ptr_t)q->payload % MEM_ALIGNMENT) == 0); + LWIP_ASSERT("check p->payload + p->len does not overflow pbuf", + ((u8_t*)p->payload + p->len <= + (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED)); + q->ref = 1; + /* calculate remaining length to be allocated */ + rem_len -= q->len; + /* remember this pbuf for linkage in next iteration */ + r = q; + } + /* end of chain */ + /*r->next = NULL;*/ + + break; + case PBUF_RAM: + /* If pbuf is to be allocated in RAM, allocate memory for it. */ + p = (struct pbuf*)mem_malloc(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF + offset) + LWIP_MEM_ALIGN_SIZE(length)); + if (p == NULL) { + return NULL; + } + /* Set up internal structure of the pbuf. */ + p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + SIZEOF_STRUCT_PBUF + offset)); + p->len = p->tot_len = length; + p->next = NULL; + p->type = type; + p->eb = NULL; + + LWIP_ASSERT("pbuf_alloc: pbuf->payload properly aligned", + ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0); + break; +#ifdef EBUF_LWIP + case PBUF_ESF_RX: +#endif /* ESF_LWIP */ + /* pbuf references existing (non-volatile static constant) ROM payload? */ + case PBUF_ROM: + /* pbuf references existing (externally allocated) RAM payload? */ + case PBUF_REF: + /* only allocate memory for the pbuf structure */ + p = (struct pbuf *)memp_malloc(MEMP_PBUF); + if (p == NULL) { + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("pbuf_alloc: Could not allocate MEMP_PBUF for PBUF_%s.\n", + (type == PBUF_ROM) ? "ROM" : "REF")); + return NULL; + } + /* caller must set this field properly, afterwards */ + p->payload = NULL; + p->len = p->tot_len = length; + p->next = NULL; + p->type = type; + break; + default: + LWIP_ASSERT("pbuf_alloc: erroneous type", 0); + return NULL; + } + /* set reference count */ + p->ref = 1; + /* set flags */ + p->flags = 0; + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F") == %p\n", length, (void *)p)); + + return p; +} + +#if LWIP_SUPPORT_CUSTOM_PBUF +/** Initialize a custom pbuf (already allocated). + * + * @param layer flag to define header size + * @param length size of the pbuf's payload + * @param type type of the pbuf (only used to treat the pbuf accordingly, as + * this function allocates no memory) + * @param p pointer to the custom pbuf to initialize (already allocated) + * @param payload_mem pointer to the buffer that is used for payload and headers, + * must be at least big enough to hold 'length' plus the header size, + * may be NULL if set later + * @param payload_mem_len the size of the 'payload_mem' buffer, must be at least + * big enough to hold 'length' plus the header size + */ +struct pbuf* +pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type, struct pbuf_custom *p, + void *payload_mem, u16_t payload_mem_len) +{ + u16_t offset; + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloced_custom(length=%"U16_F")\n", length)); + + /* determine header offset */ + offset = 0; + switch (l) { + case PBUF_TRANSPORT: + /* add room for transport (often TCP) layer header */ + offset += PBUF_TRANSPORT_HLEN; + /* FALLTHROUGH */ + case PBUF_IP: + /* add room for IP layer header */ + offset += PBUF_IP_HLEN; + /* FALLTHROUGH */ + case PBUF_LINK: + /* add room for link layer header */ + offset += PBUF_LINK_HLEN; + break; + case PBUF_RAW: + break; + default: + LWIP_ASSERT("pbuf_alloced_custom: bad pbuf layer", 0); + return NULL; + } + + if (LWIP_MEM_ALIGN_SIZE(offset) + length < payload_mem_len) { + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_WARNING, ("pbuf_alloced_custom(length=%"U16_F") buffer too short\n", length)); + return NULL; + } + + p->pbuf.next = NULL; + if (payload_mem != NULL) { + p->pbuf.payload = LWIP_MEM_ALIGN((void *)((u8_t *)payload_mem + offset)); + } else { + p->pbuf.payload = NULL; + } + p->pbuf.flags = PBUF_FLAG_IS_CUSTOM; + p->pbuf.len = p->pbuf.tot_len = length; + p->pbuf.type = type; + p->pbuf.ref = 1; + return &p->pbuf; +} +#endif /* LWIP_SUPPORT_CUSTOM_PBUF */ + +/** + * Shrink a pbuf chain to a desired length. + * + * @param p pbuf to shrink. + * @param new_len desired new length of pbuf chain + * + * Depending on the desired length, the first few pbufs in a chain might + * be skipped and left unchanged. The new last pbuf in the chain will be + * resized, and any remaining pbufs will be freed. + * + * @note If the pbuf is ROM/REF, only the ->tot_len and ->len fields are adjusted. + * @note May not be called on a packet queue. + * + * @note Despite its name, pbuf_realloc cannot grow the size of a pbuf (chain). + */ +void +pbuf_realloc(struct pbuf *p, u16_t new_len) +{ + struct pbuf *q; + u16_t rem_len; /* remaining length */ + s32_t grow; + + LWIP_ASSERT("pbuf_realloc: p != NULL", p != NULL); + LWIP_ASSERT("pbuf_realloc: sane p->type", p->type == PBUF_POOL || + p->type == PBUF_ROM || + p->type == PBUF_RAM || + p->type == PBUF_REF); + + /* desired length larger than current length? */ + if (new_len >= p->tot_len) { + /* enlarging not yet supported */ + return; + } + + /* the pbuf chain grows by (new_len - p->tot_len) bytes + * (which may be negative in case of shrinking) */ + grow = new_len - p->tot_len; + + /* first, step over any pbufs that should remain in the chain */ + rem_len = new_len; + q = p; + /* should this pbuf be kept? */ + while (rem_len > q->len) { + /* decrease remaining length by pbuf length */ + rem_len -= q->len; + /* decrease total length indicator */ + LWIP_ASSERT("grow < max_u16_t", grow < 0xffff); + q->tot_len += (u16_t)grow; + /* proceed to next pbuf in chain */ + q = q->next; + LWIP_ASSERT("pbuf_realloc: q != NULL", q != NULL); + } + /* we have now reached the new last pbuf (in q) */ + /* rem_len == desired length for pbuf q */ + + /* shrink allocated memory for PBUF_RAM */ + /* (other types merely adjust their length fields */ + if ((q->type == PBUF_RAM) && (rem_len != q->len)) { + /* reallocate and adjust the length of the pbuf that will be split */ + q = (struct pbuf *)mem_trim(q, (u16_t)((u8_t *)q->payload - (u8_t *)q) + rem_len); + LWIP_ASSERT("mem_trim returned q == NULL", q != NULL); + } + /* adjust length fields for new last pbuf */ + q->len = rem_len; + q->tot_len = q->len; + + /* any remaining pbufs in chain? */ + if (q->next != NULL) { + /* free remaining pbufs in chain */ + pbuf_free(q->next); + } + /* q is last packet in chain */ + q->next = NULL; + +} + +/** + * Adjusts the payload pointer to hide or reveal headers in the payload. + * + * Adjusts the ->payload pointer so that space for a header + * (dis)appears in the pbuf payload. + * + * The ->payload, ->tot_len and ->len fields are adjusted. + * + * @param p pbuf to change the header size. + * @param header_size_increment Number of bytes to increment header size which + * increases the size of the pbuf. New space is on the front. + * (Using a negative value decreases the header size.) + * If hdr_size_inc is 0, this function does nothing and returns succesful. + * + * PBUF_ROM and PBUF_REF type buffers cannot have their sizes increased, so + * the call will fail. A check is made that the increase in header size does + * not move the payload pointer in front of the start of the buffer. + * @return non-zero on failure, zero on success. + * + */ +u8_t +pbuf_header(struct pbuf *p, s16_t header_size_increment) +{ + u16_t type; + void *payload; + u16_t increment_magnitude; + + LWIP_ASSERT("p != NULL", p != NULL); + if ((header_size_increment == 0) || (p == NULL)) { + return 0; + } + + if (header_size_increment < 0){ + increment_magnitude = -header_size_increment; + /* Check that we aren't going to move off the end of the pbuf */ + LWIP_ERROR("increment_magnitude <= p->len", (increment_magnitude <= p->len), return 1;); + } else { + increment_magnitude = header_size_increment; +#if 0 + /* Can't assert these as some callers speculatively call + pbuf_header() to see if it's OK. Will return 1 below instead. */ + /* Check that we've got the correct type of pbuf to work with */ + LWIP_ASSERT("p->type == PBUF_RAM || p->type == PBUF_POOL", + p->type == PBUF_RAM || p->type == PBUF_POOL); + /* Check that we aren't going to move off the beginning of the pbuf */ + LWIP_ASSERT("p->payload - increment_magnitude >= p + SIZEOF_STRUCT_PBUF", + (u8_t *)p->payload - increment_magnitude >= (u8_t *)p + SIZEOF_STRUCT_PBUF); +#endif + } + + type = p->type; + /* remember current payload pointer */ + payload = p->payload; + + /* pbuf types containing payloads? */ + if (type == PBUF_RAM || type == PBUF_POOL) { + /* set new payload pointer */ + p->payload = (u8_t *)p->payload - header_size_increment; + /* boundary check fails? */ + if ((u8_t *)p->payload < (u8_t *)p + SIZEOF_STRUCT_PBUF + EP_OFFSET) { + LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("pbuf_header: failed as %p < %p (not enough space for new header size)\n", + (void *)p->payload, (void *)(p + 1))); + /* restore old payload pointer */ + p->payload = payload; + /* bail out unsuccesfully */ + return 1; + } + /* pbuf types refering to external payloads? */ + } else if (type == PBUF_REF || type == PBUF_ROM) { + /* hide a header in the payload? */ + if ((header_size_increment < 0) && (increment_magnitude <= p->len)) { + /* increase payload pointer */ + p->payload = (u8_t *)p->payload - header_size_increment; + } else { + /* cannot expand payload to front (yet!) + * bail out unsuccesfully */ + if (type == PBUF_REF) { + /* increase payload pointer */ + p->payload = (u8_t *)p->payload - header_size_increment; + } else { + return 1; + } + } + } else { + /* Unknown type */ + LWIP_ASSERT("bad pbuf type", 0); + return 1; + } + /* modify pbuf length fields */ + p->len += header_size_increment; + p->tot_len += header_size_increment; + + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_header: old %p new %p (%"S16_F")\n", + (void *)payload, (void *)p->payload, header_size_increment)); + + return 0; +} + +/** + * Dereference a pbuf chain or queue and deallocate any no-longer-used + * pbufs at the head of this chain or queue. + * + * Decrements the pbuf reference count. If it reaches zero, the pbuf is + * deallocated. + * + * For a pbuf chain, this is repeated for each pbuf in the chain, + * up to the first pbuf which has a non-zero reference count after + * decrementing. So, when all reference counts are one, the whole + * chain is free'd. + * + * @param p The pbuf (chain) to be dereferenced. + * + * @return the number of pbufs that were de-allocated + * from the head of the chain. + * + * @note MUST NOT be called on a packet queue (Not verified to work yet). + * @note the reference counter of a pbuf equals the number of pointers + * that refer to the pbuf (or into the pbuf). + * + * @internal examples: + * + * Assuming existing chains a->b->c with the following reference + * counts, calling pbuf_free(a) results in: + * + * 1->2->3 becomes ...1->3 + * 3->3->3 becomes 2->3->3 + * 1->1->2 becomes ......1 + * 2->1->1 becomes 1->1->1 + * 1->1->1 becomes ....... + * + */ +u8_t +pbuf_free(struct pbuf *p) +{ + u16_t type; + struct pbuf *q; + u8_t count; + + if (p == NULL) { + LWIP_ASSERT("p != NULL", p != NULL); + /* if assertions are disabled, proceed with debug output */ + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("pbuf_free(p == NULL) was called.\n")); + return 0; + } + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free(%p)\n", (void *)p)); + + PERF_START; + + LWIP_ASSERT("pbuf_free: sane type", + p->type == PBUF_RAM || p->type == PBUF_ROM || + p->type == PBUF_REF || p->type == PBUF_POOL +#ifdef EBUF_LWIP + || p->type == PBUF_ESF_RX +#endif //EBUF_LWIP + ); + + count = 0; + /* de-allocate all consecutive pbufs from the head of the chain that + * obtain a zero reference count after decrementing*/ + while (p != NULL) { + u16_t ref; + SYS_ARCH_DECL_PROTECT(old_level); + /* Since decrementing ref cannot be guaranteed to be a single machine operation + * we must protect it. We put the new ref into a local variable to prevent + * further protection. */ + SYS_ARCH_PROTECT(old_level); + /* all pbufs in a chain are referenced at least once */ + LWIP_ASSERT("pbuf_free: p->ref > 0", p->ref > 0); + /* decrease reference count (number of pointers to pbuf) */ + ref = --(p->ref); + SYS_ARCH_UNPROTECT(old_level); + /* this pbuf is no longer referenced to? */ + if (ref == 0) { + /* remember next pbuf in chain for next iteration */ + q = p->next; + LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: deallocating %p\n", (void *)p)); + type = p->type; +#if LWIP_SUPPORT_CUSTOM_PBUF + /* is this a custom pbuf? */ + if ((p->flags & PBUF_FLAG_IS_CUSTOM) != 0) { + struct pbuf_custom *pc = (struct pbuf_custom*)p; + LWIP_ASSERT("pc->custom_free_function != NULL", pc->custom_free_function != NULL); + pc->custom_free_function(p); + } else +#endif /* LWIP_SUPPORT_CUSTOM_PBUF */ + { + /* is this a pbuf from the pool? */ + if (type == PBUF_POOL) { + memp_free(MEMP_PBUF_POOL, p); + /* is this a ROM or RAM referencing pbuf? */ + } else if (type == PBUF_ROM || type == PBUF_REF +#ifdef EBUF_LWIP + || type == PBUF_ESF_RX +#endif //EBUF_LWIP + ) { +#ifdef EBUF_LWIP + system_pp_recycle_rx_pkt(p->eb); +#endif //EBUF_LWIP + memp_free(MEMP_PBUF, p); + /* type == PBUF_RAM */ + } else { + mem_free(p); + } + } + count++; + /* proceed to next pbuf */ + p = q; + /* p->ref > 0, this pbuf is still referenced to */ + /* (and so the remaining pbufs in chain as well) */ + } else { + LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: %p has ref %"U16_F", ending here.\n", (void *)p, ref)); + /* stop walking through the chain */ + p = NULL; + } + } + PERF_STOP("pbuf_free"); + /* return number of de-allocated pbufs */ + return count; +} + +/** + * Count number of pbufs in a chain + * + * @param p first pbuf of chain + * @return the number of pbufs in a chain + */ + +u8_t +pbuf_clen(struct pbuf *p) +{ + u8_t len; + + len = 0; + while (p != NULL) { + ++len; + p = p->next; + } + return len; +} + +/** + * Increment the reference count of the pbuf. + * + * @param p pbuf to increase reference counter of + * + */ +void +pbuf_ref(struct pbuf *p) +{ + SYS_ARCH_DECL_PROTECT(old_level); + /* pbuf given? */ + if (p != NULL) { + SYS_ARCH_PROTECT(old_level); + ++(p->ref); + SYS_ARCH_UNPROTECT(old_level); + } +} + +/** + * Concatenate two pbufs (each may be a pbuf chain) and take over + * the caller's reference of the tail pbuf. + * + * @note The caller MAY NOT reference the tail pbuf afterwards. + * Use pbuf_chain() for that purpose. + * + * @see pbuf_chain() + */ + +void +pbuf_cat(struct pbuf *h, struct pbuf *t) +{ + struct pbuf *p; + + LWIP_ERROR("(h != NULL) && (t != NULL) (programmer violates API)", + ((h != NULL) && (t != NULL)), return;); + + /* proceed to last pbuf of chain */ + for (p = h; p->next != NULL; p = p->next) { + /* add total length of second chain to all totals of first chain */ + p->tot_len += t->tot_len; + } + /* { p is last pbuf of first h chain, p->next == NULL } */ + LWIP_ASSERT("p->tot_len == p->len (of last pbuf in chain)", p->tot_len == p->len); + LWIP_ASSERT("p->next == NULL", p->next == NULL); + /* add total length of second chain to last pbuf total of first chain */ + p->tot_len += t->tot_len; + /* chain last pbuf of head (p) with first of tail (t) */ + p->next = t; + /* p->next now references t, but the caller will drop its reference to t, + * so netto there is no change to the reference count of t. + */ +} + +/** + * Chain two pbufs (or pbuf chains) together. + * + * The caller MUST call pbuf_free(t) once it has stopped + * using it. Use pbuf_cat() instead if you no longer use t. + * + * @param h head pbuf (chain) + * @param t tail pbuf (chain) + * @note The pbufs MUST belong to the same packet. + * @note MAY NOT be called on a packet queue. + * + * The ->tot_len fields of all pbufs of the head chain are adjusted. + * The ->next field of the last pbuf of the head chain is adjusted. + * The ->ref field of the first pbuf of the tail chain is adjusted. + * + */ +void +pbuf_chain(struct pbuf *h, struct pbuf *t) +{ + pbuf_cat(h, t); + /* t is now referenced by h */ + pbuf_ref(t); + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_chain: %p references %p\n", (void *)h, (void *)t)); +} + +/** + * Dechains the first pbuf from its succeeding pbufs in the chain. + * + * Makes p->tot_len field equal to p->len. + * @param p pbuf to dechain + * @return remainder of the pbuf chain, or NULL if it was de-allocated. + * @note May not be called on a packet queue. + */ +struct pbuf * +pbuf_dechain(struct pbuf *p) +{ + struct pbuf *q; + u8_t tail_gone = 1; + /* tail */ + q = p->next; + /* pbuf has successor in chain? */ + if (q != NULL) { + /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */ + LWIP_ASSERT("p->tot_len == p->len + q->tot_len", q->tot_len == p->tot_len - p->len); + /* enforce invariant if assertion is disabled */ + q->tot_len = p->tot_len - p->len; + /* decouple pbuf from remainder */ + p->next = NULL; + /* total length of pbuf p is its own length only */ + p->tot_len = p->len; + /* q is no longer referenced by p, free it */ + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_dechain: unreferencing %p\n", (void *)q)); + tail_gone = pbuf_free(q); + if (tail_gone > 0) { + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, + ("pbuf_dechain: deallocated %p (as it is no longer referenced)\n", (void *)q)); + } + /* return remaining tail or NULL if deallocated */ + } + /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */ + LWIP_ASSERT("p->tot_len == p->len", p->tot_len == p->len); + return ((tail_gone > 0) ? NULL : q); +} + +/** + * + * Create PBUF_RAM copies of pbufs. + * + * Used to queue packets on behalf of the lwIP stack, such as + * ARP based queueing. + * + * @note You MUST explicitly use p = pbuf_take(p); + * + * @note Only one packet is copied, no packet queue! + * + * @param p_to pbuf destination of the copy + * @param p_from pbuf source of the copy + * + * @return ERR_OK if pbuf was copied + * ERR_ARG if one of the pbufs is NULL or p_to is not big + * enough to hold p_from + */ +err_t +pbuf_copy(struct pbuf *p_to, struct pbuf *p_from) +{ + u16_t offset_to=0, offset_from=0, len; + + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy(%p, %p)\n", + (void*)p_to, (void*)p_from)); + + /* is the target big enough to hold the source? */ + LWIP_ERROR("pbuf_copy: target not big enough to hold source", ((p_to != NULL) && + (p_from != NULL) && (p_to->tot_len >= p_from->tot_len)), return ERR_ARG;); + + /* iterate through pbuf chain */ + do + { + LWIP_ASSERT("p_to != NULL", p_to != NULL); + /* copy one part of the original chain */ + if ((p_to->len - offset_to) >= (p_from->len - offset_from)) { + /* complete current p_from fits into current p_to */ + len = p_from->len - offset_from; + } else { + /* current p_from does not fit into current p_to */ + len = p_to->len - offset_to; + } + MEMCPY((u8_t*)p_to->payload + offset_to, (u8_t*)p_from->payload + offset_from, len); + offset_to += len; + offset_from += len; + LWIP_ASSERT("offset_to <= p_to->len", offset_to <= p_to->len); + if (offset_to == p_to->len) { + /* on to next p_to (if any) */ + offset_to = 0; + p_to = p_to->next; + } + LWIP_ASSERT("offset_from <= p_from->len", offset_from <= p_from->len); + if (offset_from >= p_from->len) { + /* on to next p_from (if any) */ + offset_from = 0; + p_from = p_from->next; + } + + if((p_from != NULL) && (p_from->len == p_from->tot_len)) { + /* don't copy more than one packet! */ + LWIP_ERROR("pbuf_copy() does not allow packet queues!\n", + (p_from->next == NULL), return ERR_VAL;); + } + if((p_to != NULL) && (p_to->len == p_to->tot_len)) { + /* don't copy more than one packet! */ + LWIP_ERROR("pbuf_copy() does not allow packet queues!\n", + (p_to->next == NULL), return ERR_VAL;); + } + } while (p_from); + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy: end of chain reached.\n")); + return ERR_OK; +} + +/** + * Copy (part of) the contents of a packet buffer + * to an application supplied buffer. + * + * @param buf the pbuf from which to copy data + * @param dataptr the application supplied buffer + * @param len length of data to copy (dataptr must be big enough). No more + * than buf->tot_len will be copied, irrespective of len + * @param offset offset into the packet buffer from where to begin copying len bytes + * @return the number of bytes copied, or 0 on failure + */ +u16_t +pbuf_copy_partial(struct pbuf *buf, void *dataptr, u16_t len, u16_t offset) +{ + struct pbuf *p; + u16_t left; + u16_t buf_copy_len; + u16_t copied_total = 0; + + LWIP_ERROR("pbuf_copy_partial: invalid buf", (buf != NULL), return 0;); + LWIP_ERROR("pbuf_copy_partial: invalid dataptr", (dataptr != NULL), return 0;); + + left = 0; + + if((buf == NULL) || (dataptr == NULL)) { + return 0; + } + + /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */ + for(p = buf; len != 0 && p != NULL; p = p->next) { + if ((offset != 0) && (offset >= p->len)) { + /* don't copy from this buffer -> on to the next */ + offset -= p->len; + } else { + /* copy from this buffer. maybe only partially. */ + buf_copy_len = p->len - offset; + if (buf_copy_len > len) + buf_copy_len = len; + /* copy the necessary parts of the buffer */ + MEMCPY(&((char*)dataptr)[left], &((char*)p->payload)[offset], buf_copy_len); + copied_total += buf_copy_len; + left += buf_copy_len; + len -= buf_copy_len; + offset = 0; + } + } + return copied_total; +} + +/** + * Copy application supplied data into a pbuf. + * This function can only be used to copy the equivalent of buf->tot_len data. + * + * @param buf pbuf to fill with data + * @param dataptr application supplied data buffer + * @param len length of the application supplied data buffer + * + * @return ERR_OK if successful, ERR_MEM if the pbuf is not big enough + */ +err_t +pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len) +{ + struct pbuf *p; + u16_t buf_copy_len; + u16_t total_copy_len = len; + u16_t copied_total = 0; + + LWIP_ERROR("pbuf_take: invalid buf", (buf != NULL), return 0;); + LWIP_ERROR("pbuf_take: invalid dataptr", (dataptr != NULL), return 0;); + + if ((buf == NULL) || (dataptr == NULL) || (buf->tot_len < len)) { + return ERR_ARG; + } + + /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */ + for(p = buf; total_copy_len != 0; p = p->next) { + LWIP_ASSERT("pbuf_take: invalid pbuf", p != NULL); + buf_copy_len = total_copy_len; + if (buf_copy_len > p->len) { + /* this pbuf cannot hold all remaining data */ + buf_copy_len = p->len; + } + /* copy the necessary parts of the buffer */ + MEMCPY(p->payload, &((char*)dataptr)[copied_total], buf_copy_len); + total_copy_len -= buf_copy_len; + copied_total += buf_copy_len; + } + LWIP_ASSERT("did not copy all data", total_copy_len == 0 && copied_total == len); + return ERR_OK; +} + +/** + * Creates a single pbuf out of a queue of pbufs. + * + * @remark: Either the source pbuf 'p' is freed by this function or the original + * pbuf 'p' is returned, therefore the caller has to check the result! + * + * @param p the source pbuf + * @param layer pbuf_layer of the new pbuf + * + * @return a new, single pbuf (p->next is NULL) + * or the old pbuf if allocation fails + */ +struct pbuf* +pbuf_coalesce(struct pbuf *p, pbuf_layer layer) +{ + struct pbuf *q; + err_t err; + if (p->next == NULL) { + return p; + } + q = pbuf_alloc(layer, p->tot_len, PBUF_RAM); + if (q == NULL) { + /* @todo: what do we do now? */ + return p; + } + err = pbuf_copy(q, p); + LWIP_ASSERT("pbuf_copy failed", err == ERR_OK); + pbuf_free(p); + return q; +} + +#if LWIP_CHECKSUM_ON_COPY +/** + * Copies data into a single pbuf (*not* into a pbuf queue!) and updates + * the checksum while copying + * + * @param p the pbuf to copy data into + * @param start_offset offset of p->payload where to copy the data to + * @param dataptr data to copy into the pbuf + * @param len length of data to copy into the pbuf + * @param chksum pointer to the checksum which is updated + * @return ERR_OK if successful, another error if the data does not fit + * within the (first) pbuf (no pbuf queues!) + */ +err_t +pbuf_fill_chksum(struct pbuf *p, u16_t start_offset, const void *dataptr, + u16_t len, u16_t *chksum) +{ + u32_t acc; + u16_t copy_chksum; + char *dst_ptr; + LWIP_ASSERT("p != NULL", p != NULL); + LWIP_ASSERT("dataptr != NULL", dataptr != NULL); + LWIP_ASSERT("chksum != NULL", chksum != NULL); + LWIP_ASSERT("len != 0", len != 0); + + if ((start_offset >= p->len) || (start_offset + len > p->len)) { + return ERR_ARG; + } + + dst_ptr = ((char*)p->payload) + start_offset; + copy_chksum = LWIP_CHKSUM_COPY(dst_ptr, dataptr, len); + if ((start_offset & 1) != 0) { + copy_chksum = SWAP_BYTES_IN_WORD(copy_chksum); + } + acc = *chksum; + acc += copy_chksum; + *chksum = FOLD_U32T(acc); + return ERR_OK; +} +#endif /* LWIP_CHECKSUM_ON_COPY */ + + /** Get one byte from the specified position in a pbuf + * WARNING: returns zero for offset >= p->tot_len + * + * @param p pbuf to parse + * @param offset offset into p of the byte to return + * @return byte at an offset into p OR ZERO IF 'offset' >= p->tot_len + */ +u8_t +pbuf_get_at(struct pbuf* p, u16_t offset) +{ + u16_t copy_from = offset; + struct pbuf* q = p; + + /* get the correct pbuf */ + while ((q != NULL) && (q->len <= copy_from)) { + copy_from -= q->len; + q = q->next; + } + /* return requested data if pbuf is OK */ + if ((q != NULL) && (q->len > copy_from)) { + return ((u8_t*)q->payload)[copy_from]; + } + return 0; +} + +/** Compare pbuf contents at specified offset with memory s2, both of length n + * + * @param p pbuf to compare + * @param offset offset into p at wich to start comparing + * @param s2 buffer to compare + * @param n length of buffer to compare + * @return zero if equal, nonzero otherwise + * (0xffff if p is too short, diffoffset+1 otherwise) + */ +u16_t +pbuf_memcmp(struct pbuf* p, u16_t offset, const void* s2, u16_t n) +{ + u16_t start = offset; + struct pbuf* q = p; + + /* get the correct pbuf */ + while ((q != NULL) && (q->len <= start)) { + start -= q->len; + q = q->next; + } + /* return requested data if pbuf is OK */ + if ((q != NULL) && (q->len > start)) { + u16_t i; + for(i = 0; i < n; i++) { + u8_t a = pbuf_get_at(q, start + i); + u8_t b = ((u8_t*)s2)[i]; + if (a != b) { + return i+1; + } + } + return 0; + } + return 0xffff; +} + +/** Find occurrence of mem (with length mem_len) in pbuf p, starting at offset + * start_offset. + * + * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as + * return value 'not found' + * @param mem search for the contents of this buffer + * @param mem_len length of 'mem' + * @param start_offset offset into p at which to start searching + * @return 0xFFFF if substr was not found in p or the index where it was found + */ +u16_t +pbuf_memfind(struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset) +{ + u16_t i; + u16_t max = p->tot_len - mem_len; + if (p->tot_len >= mem_len + start_offset) { + for(i = start_offset; i <= max; ) { + u16_t plus = pbuf_memcmp(p, i, mem, mem_len); + if (plus == 0) { + return i; + } else { + i += plus; + } + } + } + return 0xFFFF; +} + +/** Find occurrence of substr with length substr_len in pbuf p, start at offset + * start_offset + * WARNING: in contrast to strstr(), this one does not stop at the first \0 in + * the pbuf/source string! + * + * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as + * return value 'not found' + * @param substr string to search for in p, maximum length is 0xFFFE + * @return 0xFFFF if substr was not found in p or the index where it was found + */ +u16_t +pbuf_strstr(struct pbuf* p, const char* substr) +{ + size_t substr_len; + if ((substr == NULL) || (substr[0] == 0) || (p->tot_len == 0xFFFF)) { + return 0xFFFF; + } + substr_len = os_strlen(substr); + if (substr_len >= 0xFFFF) { + return 0xFFFF; + } + return pbuf_memfind(p, substr, (u16_t)substr_len, 0); +} diff --git a/variants/lwip/lwip/core/raw.c b/variants/lwip/lwip/core/raw.c new file mode 100755 index 000000000..b89a4f82f --- /dev/null +++ b/variants/lwip/lwip/core/raw.c @@ -0,0 +1,358 @@ +/** + * @file + * Implementation of raw protocol PCBs for low-level handling of + * different types of protocols besides (or overriding) those + * already available in lwIP. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_RAW /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/memp.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/raw.h" +#include "lwip/stats.h" +#include "arch/perf.h" + +#include + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +/** The list of RAW PCBs */ +static struct raw_pcb *raw_pcbs; + +/** + * Determine if in incoming IP packet is covered by a RAW PCB + * and if so, pass it to a user-provided receive callback function. + * + * Given an incoming IP datagram (as a chain of pbufs) this function + * finds a corresponding RAW PCB and calls the corresponding receive + * callback function. + * + * @param p pbuf to be demultiplexed to a RAW PCB. + * @param inp network interface on which the datagram was received. + * @return - 1 if the packet has been eaten by a RAW PCB receive + * callback function. The caller MAY NOT not reference the + * packet any longer, and MAY NOT call pbuf_free(). + * @return - 0 if packet is not eaten (pbuf is still referenced by the + * caller). + * + */ +u8_t ICACHE_FLASH_ATTR +raw_input(struct pbuf *p, struct netif *inp) +{ + struct raw_pcb *pcb, *prev; + struct ip_hdr *iphdr; + s16_t proto; + u8_t eaten = 0; + + LWIP_UNUSED_ARG(inp); + + iphdr = (struct ip_hdr *)p->payload; + proto = IPH_PROTO(iphdr); + + prev = NULL; + pcb = raw_pcbs; + /* loop through all raw pcbs until the packet is eaten by one */ + /* this allows multiple pcbs to match against the packet by design */ + while ((eaten == 0) && (pcb != NULL)) { + if ((pcb->protocol == proto) && + (ip_addr_isany(&pcb->local_ip) || + ip_addr_cmp(&(pcb->local_ip), ¤t_iphdr_dest))) { +#if IP_SOF_BROADCAST_RECV + /* broadcast filter? */ + if ((pcb->so_options & SOF_BROADCAST) || !ip_addr_isbroadcast(¤t_iphdr_dest, inp)) +#endif /* IP_SOF_BROADCAST_RECV */ + { + /* receive callback function available? */ + if (pcb->recv != NULL) { + /* the receive callback function did not eat the packet? */ + if (pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr()) != 0) { + /* receive function ate the packet */ + p = NULL; + eaten = 1; + if (prev != NULL) { + /* move the pcb to the front of raw_pcbs so that is + found faster next time */ + prev->next = pcb->next; + pcb->next = raw_pcbs; + raw_pcbs = pcb; + } + } + } + /* no receive callback function was set for this raw PCB */ + } + /* drop the packet */ + } + prev = pcb; + pcb = pcb->next; + } + return eaten; +} + +/** + * Bind a RAW PCB. + * + * @param pcb RAW PCB to be bound with a local address ipaddr. + * @param ipaddr local IP address to bind with. Use IP_ADDR_ANY to + * bind to all local interfaces. + * + * @return lwIP error code. + * - ERR_OK. Successful. No error occured. + * - ERR_USE. The specified IP address is already bound to by + * another RAW PCB. + * + * @see raw_disconnect() + */ +err_t ICACHE_FLASH_ATTR +raw_bind(struct raw_pcb *pcb, ip_addr_t *ipaddr) +{ + ip_addr_set(&pcb->local_ip, ipaddr); + return ERR_OK; +} + +/** + * Connect an RAW PCB. This function is required by upper layers + * of lwip. Using the raw api you could use raw_sendto() instead + * + * This will associate the RAW PCB with the remote address. + * + * @param pcb RAW PCB to be connected with remote address ipaddr and port. + * @param ipaddr remote IP address to connect with. + * + * @return lwIP error code + * + * @see raw_disconnect() and raw_sendto() + */ +err_t ICACHE_FLASH_ATTR +raw_connect(struct raw_pcb *pcb, ip_addr_t *ipaddr) +{ + ip_addr_set(&pcb->remote_ip, ipaddr); + return ERR_OK; +} + + +/** + * Set the callback function for received packets that match the + * raw PCB's protocol and binding. + * + * The callback function MUST either + * - eat the packet by calling pbuf_free() and returning non-zero. The + * packet will not be passed to other raw PCBs or other protocol layers. + * - not free the packet, and return zero. The packet will be matched + * against further PCBs and/or forwarded to another protocol layers. + * + * @return non-zero if the packet was free()d, zero if the packet remains + * available for others. + */ +void ICACHE_FLASH_ATTR +raw_recv(struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg) +{ + /* remember recv() callback and user data */ + pcb->recv = recv; + pcb->recv_arg = recv_arg; +} + +/** + * Send the raw IP packet to the given address. Note that actually you cannot + * modify the IP headers (this is inconsistent with the receive callback where + * you actually get the IP headers), you can only specify the IP payload here. + * It requires some more changes in lwIP. (there will be a raw_send() function + * then.) + * + * @param pcb the raw pcb which to send + * @param p the IP payload to send + * @param ipaddr the destination address of the IP packet + * + */ +err_t ICACHE_FLASH_ATTR +raw_sendto(struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr) +{ + err_t err; + struct netif *netif; + ip_addr_t *src_ip; + struct pbuf *q; /* q will be sent down the stack */ + + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_sendto\n")); + + /* not enough space to add an IP header to first pbuf in given p chain? */ + if (pbuf_header(p, IP_HLEN)) { + /* allocate header in new pbuf */ + q = pbuf_alloc(PBUF_IP, 0, PBUF_RAM); + /* new header pbuf could not be allocated? */ + if (q == NULL) { + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("raw_sendto: could not allocate header\n")); + return ERR_MEM; + } + if (p->tot_len != 0) { + /* chain header q in front of given pbuf p */ + pbuf_chain(q, p); + } + /* { first pbuf q points to header pbuf } */ + LWIP_DEBUGF(RAW_DEBUG, ("raw_sendto: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p)); + } else { + /* first pbuf q equals given pbuf */ + q = p; + if(pbuf_header(q, -IP_HLEN)) { + LWIP_ASSERT("Can't restore header we just removed!", 0); + return ERR_MEM; + } + } + + if ((netif = ip_route(ipaddr)) == NULL) { + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr))); + /* free any temporary header pbuf allocated by pbuf_header() */ + if (q != p) { + pbuf_free(q); + } + return ERR_RTE; + } + +#if IP_SOF_BROADCAST + /* broadcast filter? */ + if (((pcb->so_options & SOF_BROADCAST) == 0) && ip_addr_isbroadcast(ipaddr, netif)) { + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb)); + /* free any temporary header pbuf allocated by pbuf_header() */ + if (q != p) { + pbuf_free(q); + } + return ERR_VAL; + } +#endif /* IP_SOF_BROADCAST */ + + if (ip_addr_isany(&pcb->local_ip)) { + /* use outgoing network interface IP address as source address */ + src_ip = &(netif->ip_addr); + } else { + /* use RAW PCB local IP address as source address */ + src_ip = &(pcb->local_ip); + } + +#if LWIP_NETIF_HWADDRHINT + netif->addr_hint = &(pcb->addr_hint); +#endif /* LWIP_NETIF_HWADDRHINT*/ + err = ip_output_if (q, src_ip, ipaddr, pcb->ttl, pcb->tos, pcb->protocol, netif); +#if LWIP_NETIF_HWADDRHINT + netif->addr_hint = NULL; +#endif /* LWIP_NETIF_HWADDRHINT*/ + + /* did we chain a header earlier? */ + if (q != p) { + /* free the header */ + pbuf_free(q); + } + return err; +} + +/** + * Send the raw IP packet to the address given by raw_connect() + * + * @param pcb the raw pcb which to send + * @param p the IP payload to send + * + */ +err_t ICACHE_FLASH_ATTR +raw_send(struct raw_pcb *pcb, struct pbuf *p) +{ + return raw_sendto(pcb, p, &pcb->remote_ip); +} + +/** + * Remove an RAW PCB. + * + * @param pcb RAW PCB to be removed. The PCB is removed from the list of + * RAW PCB's and the data structure is freed from memory. + * + * @see raw_new() + */ +void ICACHE_FLASH_ATTR +raw_remove(struct raw_pcb *pcb) +{ + struct raw_pcb *pcb2; + /* pcb to be removed is first in list? */ + if (raw_pcbs == pcb) { + /* make list start at 2nd pcb */ + raw_pcbs = raw_pcbs->next; + /* pcb not 1st in list */ + } else { + for(pcb2 = raw_pcbs; pcb2 != NULL; pcb2 = pcb2->next) { + /* find pcb in raw_pcbs list */ + if (pcb2->next != NULL && pcb2->next == pcb) { + /* remove pcb from list */ + pcb2->next = pcb->next; + } + } + } + memp_free(MEMP_RAW_PCB, pcb); +} + +/** + * Create a RAW PCB. + * + * @return The RAW PCB which was created. NULL if the PCB data structure + * could not be allocated. + * + * @param proto the protocol number of the IPs payload (e.g. IP_PROTO_ICMP) + * + * @see raw_remove() + */ +struct raw_pcb * ICACHE_FLASH_ATTR +raw_new(u8_t proto) +{ + struct raw_pcb *pcb; + + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_new\n")); + + pcb = (struct raw_pcb *)memp_malloc(MEMP_RAW_PCB); + /* could allocate RAW PCB? */ + if (pcb != NULL) { + /* initialize PCB to all zeroes */ + os_memset(pcb, 0, sizeof(struct raw_pcb)); + pcb->protocol = proto; + pcb->ttl = RAW_TTL; + pcb->next = raw_pcbs; + raw_pcbs = pcb; + } + return pcb; +} + +#endif /* LWIP_RAW */ diff --git a/variants/lwip/lwip/core/sntp.c b/variants/lwip/lwip/core/sntp.c new file mode 100755 index 000000000..0a635a9a0 --- /dev/null +++ b/variants/lwip/lwip/core/sntp.c @@ -0,0 +1,1130 @@ +/** + * @file + * SNTP client module + * + * This is simple "SNTP" client for the lwIP raw API. + * It is a minimal implementation of SNTPv4 as specified in RFC 4330. + * + * For a list of some public NTP servers, see this link : + * http://support.ntp.org/bin/view/Servers/NTPPoolServers + * + * @todo: + * - set/change servers at runtime + * - complete SNTP_CHECK_RESPONSE checks 3 and 4 + * - support broadcast/multicast mode? + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Simon Goldschmidt (lwIP raw API part) + */ + +#include "lwip/sntp.h" +#include "osapi.h" +#include "os_type.h" +#include "lwip/opt.h" +#include "lwip/timers.h" +#include "lwip/udp.h" +#include "lwip/dns.h" +#include "lwip/ip_addr.h" +#include "lwip/pbuf.h" + +//#include +#if LWIP_UDP + +/** + * SNTP_DEBUG: Enable debugging for SNTP. + */ +#ifndef SNTP_DEBUG +#define SNTP_DEBUG LWIP_DBG_ON +#endif + +/** SNTP server port */ +#ifndef SNTP_PORT +#define SNTP_PORT 123 +#endif + +/** Set this to 1 to allow config of SNTP server(s) by DNS name */ +#ifndef SNTP_SERVER_DNS +#define SNTP_SERVER_DNS 0 +#endif + +/** Handle support for more than one server via NTP_MAX_SERVERS, + * but catch legacy style of setting SNTP_SUPPORT_MULTIPLE_SERVERS, probably outside of this file + */ +#ifndef SNTP_SUPPORT_MULTIPLE_SERVERS +#if SNTP_MAX_SERVERS > 1 +#define SNTP_SUPPORT_MULTIPLE_SERVERS 1 +#else /* NTP_MAX_SERVERS > 1 */ +#define SNTP_SUPPORT_MULTIPLE_SERVERS 0 +#endif /* NTP_MAX_SERVERS > 1 */ +#else /* SNTP_SUPPORT_MULTIPLE_SERVERS */ +/* The developer has defined SNTP_SUPPORT_MULTIPLE_SERVERS, probably from old code */ +#if SNTP_MAX_SERVERS <= 1 +#error "SNTP_MAX_SERVERS needs to be defined to the max amount of servers if SNTP_SUPPORT_MULTIPLE_SERVERS is defined" +#endif /* SNTP_MAX_SERVERS <= 1 */ +#endif /* SNTP_SUPPORT_MULTIPLE_SERVERS */ + + +/** Sanity check: + * Define this to + * - 0 to turn off sanity checks (default; smaller code) + * - >= 1 to check address and port of the response packet to ensure the + * response comes from the server we sent the request to. + * - >= 2 to check returned Originate Timestamp against Transmit Timestamp + * sent to the server (to ensure response to older request). + * - >= 3 @todo: discard reply if any of the LI, Stratum, or Transmit Timestamp + * fields is 0 or the Mode field is not 4 (unicast) or 5 (broadcast). + * - >= 4 @todo: to check that the Root Delay and Root Dispersion fields are each + * greater than or equal to 0 and less than infinity, where infinity is + * currently a cozy number like one second. This check avoids using a + * server whose synchronization source has expired for a very long time. + */ +#ifndef SNTP_CHECK_RESPONSE +#define SNTP_CHECK_RESPONSE 0 +#endif + +/** According to the RFC, this shall be a random delay + * between 1 and 5 minutes (in milliseconds) to prevent load peaks. + * This can be defined to a random generation function, + * which must return the delay in milliseconds as u32_t. + * Turned off by default. + */ +#ifndef SNTP_STARTUP_DELAY +#define SNTP_STARTUP_DELAY 0 +#endif + +/** If you want the startup delay to be a function, define this + * to a function (including the brackets) and define SNTP_STARTUP_DELAY to 1. + */ +#ifndef SNTP_STARTUP_DELAY_FUNC +#define SNTP_STARTUP_DELAY_FUNC SNTP_STARTUP_DELAY +#endif + +/** SNTP receive timeout - in milliseconds + * Also used as retry timeout - this shouldn't be too low. + * Default is 3 seconds. + */ +#ifndef SNTP_RECV_TIMEOUT +#define SNTP_RECV_TIMEOUT 3000 +#endif + +/** SNTP update delay - in milliseconds + * Default is 1 hour. + */ +#ifndef SNTP_UPDATE_DELAY +#define SNTP_UPDATE_DELAY 3600000 +#endif +#if (SNTP_UPDATE_DELAY < 15000) && !SNTP_SUPPRESS_DELAY_CHECK +#error "SNTPv4 RFC 4330 enforces a minimum update time of 15 seconds!" +#endif + +/** SNTP macro to change system time and/or the update the RTC clock */ +#ifndef SNTP_SET_SYSTEM_TIME +#define SNTP_SET_SYSTEM_TIME(sec) ((void)sec) +#endif + +/** SNTP macro to change system time including microseconds */ +#ifdef SNTP_SET_SYSTEM_TIME_US +#define SNTP_CALC_TIME_US 1 +#define SNTP_RECEIVE_TIME_SIZE 2 +#else +#define SNTP_SET_SYSTEM_TIME_US(sec, us) +#define SNTP_CALC_TIME_US 0 +#define SNTP_RECEIVE_TIME_SIZE 1 +#endif + +/** SNTP macro to get system time, used with SNTP_CHECK_RESPONSE >= 2 + * to send in request and compare in response. + */ +#ifndef SNTP_GET_SYSTEM_TIME +#define SNTP_GET_SYSTEM_TIME(sec, us) do { (sec) = 0; (us) = 0; } while(0) +#endif + +/** Default retry timeout (in milliseconds) if the response + * received is invalid. + * This is doubled with each retry until SNTP_RETRY_TIMEOUT_MAX is reached. + */ +#ifndef SNTP_RETRY_TIMEOUT +#define SNTP_RETRY_TIMEOUT SNTP_RECV_TIMEOUT +#endif + +/** Maximum retry timeout (in milliseconds). */ +#ifndef SNTP_RETRY_TIMEOUT_MAX +#define SNTP_RETRY_TIMEOUT_MAX (SNTP_RETRY_TIMEOUT * 10) +#endif + +/** Increase retry timeout with every retry sent + * Default is on to conform to RFC. + */ +#ifndef SNTP_RETRY_TIMEOUT_EXP +#define SNTP_RETRY_TIMEOUT_EXP 1 +#endif + +/* the various debug levels for this file */ +#define SNTP_DEBUG_TRACE (SNTP_DEBUG | LWIP_DBG_TRACE) +#define SNTP_DEBUG_STATE (SNTP_DEBUG | LWIP_DBG_STATE) +#define SNTP_DEBUG_WARN (SNTP_DEBUG | LWIP_DBG_LEVEL_WARNING) +#define SNTP_DEBUG_WARN_STATE (SNTP_DEBUG | LWIP_DBG_LEVEL_WARNING | LWIP_DBG_STATE) +#define SNTP_DEBUG_SERIOUS (SNTP_DEBUG | LWIP_DBG_LEVEL_SERIOUS) + +#define SNTP_ERR_KOD 1 + +/* SNTP protocol defines */ +#define SNTP_MSG_LEN 48 + +#define SNTP_OFFSET_LI_VN_MODE 0 +#define SNTP_LI_MASK 0xC0 +#define SNTP_LI_NO_WARNING 0x00 +#define SNTP_LI_LAST_MINUTE_61_SEC 0x01 +#define SNTP_LI_LAST_MINUTE_59_SEC 0x02 +#define SNTP_LI_ALARM_CONDITION 0x03 /* (clock not synchronized) */ + +#define SNTP_VERSION_MASK 0x38 +#define SNTP_VERSION (4/* NTP Version 4*/<<3) + +#define SNTP_MODE_MASK 0x07 +#define SNTP_MODE_CLIENT 0x03 +#define SNTP_MODE_SERVER 0x04 +#define SNTP_MODE_BROADCAST 0x05 + +#define SNTP_OFFSET_STRATUM 1 +#define SNTP_STRATUM_KOD 0x00 + +#define SNTP_OFFSET_ORIGINATE_TIME 24 +#define SNTP_OFFSET_RECEIVE_TIME 32 +#define SNTP_OFFSET_TRANSMIT_TIME 40 + +/* number of seconds between 1900 and 1970 */ +#define DIFF_SEC_1900_1970 (2208988800UL) + +/** + * SNTP packet format (without optional fields) + * Timestamps are coded as 64 bits: + * - 32 bits seconds since Jan 01, 1970, 00:00 + * - 32 bits seconds fraction (0-padded) + * For future use, if the MSB in the seconds part is set, seconds are based + * on Feb 07, 2036, 06:28:16. + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +#define PACK_STRUCT_FLD_8 PACK_STRUCT_FIELD +struct sntp_msg { + PACK_STRUCT_FLD_8(u8_t li_vn_mode); + PACK_STRUCT_FLD_8(u8_t stratum); + PACK_STRUCT_FLD_8(u8_t poll); + PACK_STRUCT_FLD_8(u8_t precision); + PACK_STRUCT_FIELD(u32_t root_delay); + PACK_STRUCT_FIELD(u32_t root_dispersion); + PACK_STRUCT_FIELD(u32_t reference_identifier); + PACK_STRUCT_FIELD(u32_t reference_timestamp[2]); + PACK_STRUCT_FIELD(u32_t originate_timestamp[2]); + PACK_STRUCT_FIELD(u32_t receive_timestamp[2]); + PACK_STRUCT_FIELD(u32_t transmit_timestamp[2]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* function prototypes */ +static void sntp_request(void *arg); + +/** The UDP pcb used by the SNTP client */ +static struct udp_pcb* sntp_pcb; + +sint8 time_zone = 8; +/** Names/Addresses of servers */ +struct sntp_server { +#if SNTP_SERVER_DNS + char* name; +#endif /* SNTP_SERVER_DNS */ + ip_addr_t addr; +}; +static struct sntp_server sntp_servers[SNTP_MAX_SERVERS]; + +#if SNTP_GET_SERVERS_FROM_DHCP +static u8_t sntp_set_servers_from_dhcp; +#endif + +#if SNTP_SUPPORT_MULTIPLE_SERVERS +/** The currently used server (initialized to 0) */ +static u8_t sntp_current_server; +#else /* SNTP_SUPPORT_MULTIPLE_SERVERS */ +#define sntp_current_server 0 +#endif /* SNTP_SUPPORT_MULTIPLE_SERVERS */ + +#if SNTP_RETRY_TIMEOUT_EXP +#define SNTP_RESET_RETRY_TIMEOUT() sntp_retry_timeout = SNTP_RETRY_TIMEOUT +/** Retry time, initialized with SNTP_RETRY_TIMEOUT and doubled with each retry. */ +static u32_t sntp_retry_timeout; +#else /* SNTP_RETRY_TIMEOUT_EXP */ +#define SNTP_RESET_RETRY_TIMEOUT() +#define sntp_retry_timeout SNTP_RETRY_TIMEOUT +#endif /* SNTP_RETRY_TIMEOUT_EXP */ + +#if SNTP_CHECK_RESPONSE >= 1 +/** Saves the last server address to compare with response */ +static ip_addr_t sntp_last_server_address; +#endif /* SNTP_CHECK_RESPONSE >= 1 */ + +#if SNTP_CHECK_RESPONSE >= 2 +/** Saves the last timestamp sent (which is sent back by the server) + * to compare against in response */ +static u32_t sntp_last_timestamp_sent[2]; +#endif /* SNTP_CHECK_RESPONSE >= 2 */ +typedef long time_t; +//uint32 current_stamp_1 = 0; +//uint32 current_stamp_2 = 0; +uint32 realtime_stamp = 0; +LOCAL os_timer_t sntp_timer; +/*****************************************/ +#define SECSPERMIN 60L +#define MINSPERHOUR 60L +#define HOURSPERDAY 24L +#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) +#define SECSPERDAY (SECSPERHOUR * HOURSPERDAY) +#define DAYSPERWEEK 7 +#define MONSPERYEAR 12 + +#define YEAR_BASE 1900 +#define EPOCH_YEAR 1970 +#define EPOCH_WDAY 4 +#define EPOCH_YEARS_SINCE_LEAP 2 +#define EPOCH_YEARS_SINCE_CENTURY 70 +#define EPOCH_YEARS_SINCE_LEAP_CENTURY 370 + +#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0) + +int __tznorth; +int __tzyear; +char reult[100]; +static const int mon_lengths[2][12] = { + {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, + {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} +} ; + +static const int year_lengths[2] = { + 365, + 366 +} ; +struct tm +{ + int tm_sec; + int tm_min; + int tm_hour; + int tm_mday; + int tm_mon; + int tm_year; + int tm_wday; + int tm_yday; + int tm_isdst; +}; + +struct tm res_buf; +typedef struct __tzrule_struct +{ + char ch; + int m; + int n; + int d; + int s; + time_t change; + int offset; +} __tzrule_type; + +__tzrule_type sntp__tzrule[2]; +struct tm * ICACHE_FLASH_ATTR +sntp_mktm_r(const time_t * tim_p ,struct tm *res ,int is_gmtime) +{ + long days, rem; + time_t lcltime; + int y; + int yleap; + const int *ip; + + /* base decision about std/dst time on current time */ + lcltime = *tim_p; + + days = ((long)lcltime) / SECSPERDAY; + rem = ((long)lcltime) % SECSPERDAY; + while (rem < 0) + { + rem += SECSPERDAY; + --days; + } + while (rem >= SECSPERDAY) + { + rem -= SECSPERDAY; + ++days; + } + + /* compute hour, min, and sec */ + res->tm_hour = (int) (rem / SECSPERHOUR); + rem %= SECSPERHOUR; + res->tm_min = (int) (rem / SECSPERMIN); + res->tm_sec = (int) (rem % SECSPERMIN); + + /* compute day of week */ + if ((res->tm_wday = ((EPOCH_WDAY + days) % DAYSPERWEEK)) < 0) + res->tm_wday += DAYSPERWEEK; + + /* compute year & day of year */ + y = EPOCH_YEAR; + if (days >= 0) + { + for (;;) + { + yleap = isleap(y); + if (days < year_lengths[yleap]) + break; + y++; + days -= year_lengths[yleap]; + } + } + else + { + do + { + --y; + yleap = isleap(y); + days += year_lengths[yleap]; + } while (days < 0); + } + + res->tm_year = y - YEAR_BASE; + res->tm_yday = days; + ip = mon_lengths[yleap]; + for (res->tm_mon = 0; days >= ip[res->tm_mon]; ++res->tm_mon) + days -= ip[res->tm_mon]; + res->tm_mday = days + 1; + + if (!is_gmtime) + { + int offset; + int hours, mins, secs; + +// TZ_LOCK; +// if (_daylight) +// { +// if (y == __tzyear || __tzcalc_limits (y)) +// res->tm_isdst = (__tznorth +// ? (*tim_p >= __tzrule[0].change && *tim_p < __tzrule[1].change) +// : (*tim_p >= __tzrule[0].change || *tim_p < __tzrule[1].change)); +// else +// res->tm_isdst = -1; +// } +// else + res->tm_isdst = 0; + + offset = (res->tm_isdst == 1 ? sntp__tzrule[1].offset : sntp__tzrule[0].offset); + + hours = offset / SECSPERHOUR; + offset = offset % SECSPERHOUR; + + mins = offset / SECSPERMIN; + secs = offset % SECSPERMIN; + + res->tm_sec -= secs; + res->tm_min -= mins; + res->tm_hour -= hours; + + if (res->tm_sec >= SECSPERMIN) + { + res->tm_min += 1; + res->tm_sec -= SECSPERMIN; + } + else if (res->tm_sec < 0) + { + res->tm_min -= 1; + res->tm_sec += SECSPERMIN; + } + if (res->tm_min >= MINSPERHOUR) + { + res->tm_hour += 1; + res->tm_min -= MINSPERHOUR; + } + else if (res->tm_min < 0) + { + res->tm_hour -= 1; + res->tm_min += MINSPERHOUR; + } + if (res->tm_hour >= HOURSPERDAY) + { + ++res->tm_yday; + ++res->tm_wday; + if (res->tm_wday > 6) + res->tm_wday = 0; + ++res->tm_mday; + res->tm_hour -= HOURSPERDAY; + if (res->tm_mday > ip[res->tm_mon]) + { + res->tm_mday -= ip[res->tm_mon]; + res->tm_mon += 1; + if (res->tm_mon == 12) + { + res->tm_mon = 0; + res->tm_year += 1; + res->tm_yday = 0; + } + } + } + else if (res->tm_hour < 0) + { + res->tm_yday -= 1; + res->tm_wday -= 1; + if (res->tm_wday < 0) + res->tm_wday = 6; + res->tm_mday -= 1; + res->tm_hour += 24; + if (res->tm_mday == 0) + { + res->tm_mon -= 1; + if (res->tm_mon < 0) + { + res->tm_mon = 11; + res->tm_year -= 1; + res->tm_yday = 365 + isleap(res->tm_year); + } + res->tm_mday = ip[res->tm_mon]; + } + } +// TZ_UNLOCK; + } + else + res->tm_isdst = 0; +// os_printf("res %d %d %d %d %d\n",res->tm_year,res->tm_mon,res->tm_mday,res->tm_yday,res->tm_hour); + return (res); +} +struct tm * ICACHE_FLASH_ATTR +sntp_localtime_r(const time_t * tim_p , + struct tm *res) +{ + return sntp_mktm_r (tim_p, res, 0); +} + +struct tm * ICACHE_FLASH_ATTR +sntp_localtime(const time_t * tim_p) +{ + return sntp_localtime_r (tim_p, &res_buf); +} + + +int ICACHE_FLASH_ATTR +sntp__tzcalc_limits(int year) +{ + int days, year_days, years; + int i, j; + + if (year < EPOCH_YEAR) + return 0; + + __tzyear = year; + + years = (year - EPOCH_YEAR); + + year_days = years * 365 + + (years - 1 + EPOCH_YEARS_SINCE_LEAP) / 4 - (years - 1 + EPOCH_YEARS_SINCE_CENTURY) / 100 + + (years - 1 + EPOCH_YEARS_SINCE_LEAP_CENTURY) / 400; + + for (i = 0; i < 2; ++i) + { + if (sntp__tzrule[i].ch == 'J') + days = year_days + sntp__tzrule[i].d + (isleap(year) && sntp__tzrule[i].d >= 60); + else if (sntp__tzrule[i].ch == 'D') + days = year_days + sntp__tzrule[i].d; + else + { + int yleap = isleap(year); + int m_day, m_wday, wday_diff; + const int *ip = mon_lengths[yleap]; + + days = year_days; + + for (j = 1; j < sntp__tzrule[i].m; ++j) + days += ip[j-1]; + + m_wday = (EPOCH_WDAY + days) % DAYSPERWEEK; + + wday_diff = sntp__tzrule[i].d - m_wday; + if (wday_diff < 0) + wday_diff += DAYSPERWEEK; + m_day = (sntp__tzrule[i].n - 1) * DAYSPERWEEK + wday_diff; + + while (m_day >= ip[j-1]) + m_day -= DAYSPERWEEK; + + days += m_day; + } + + /* store the change-over time in GMT form by adding offset */ + sntp__tzrule[i].change = days * SECSPERDAY + sntp__tzrule[i].s + sntp__tzrule[i].offset; + } + + __tznorth = (sntp__tzrule[0].change < sntp__tzrule[1].change); + + return 1; +} + +char * ICACHE_FLASH_ATTR +sntp_asctime_r(struct tm *tim_p ,char *result) +{ + static const char day_name[7][4] = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" + }; + static const char mon_name[12][4] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + os_sprintf (result, "%s %s %02d %02d:%02d:%02d %02d\n", + day_name[tim_p->tm_wday], + mon_name[tim_p->tm_mon], + tim_p->tm_mday, tim_p->tm_hour, tim_p->tm_min, + tim_p->tm_sec, 1900 + tim_p->tm_year); + return result; +} +char *ICACHE_FLASH_ATTR +sntp_asctime(struct tm *tim_p) +{ + + return sntp_asctime_r (tim_p, reult); +} + +uint32 sntp_get_current_timestamp() +{ + if(realtime_stamp == 0){ + os_printf("please start sntp first !\n"); + return 0; + } else { + return realtime_stamp; + } +} + +char* sntp_get_real_time(time_t t) +{ + return sntp_asctime(sntp_localtime (&t)); +} +/** + * SNTP get time_zone default GMT + 8 + */ +sint8 ICACHE_FLASH_ATTR +sntp_get_timezone(void) +{ + return time_zone; +} +/** + * SNTP set time_zone default GMT + 8 + */ + +bool ICACHE_FLASH_ATTR +sntp_set_timezone(sint8 timezone) +{ + if(timezone >= -11 || timezone <= 13) { + time_zone = timezone; + return true; + } else { + return false; + } + +} +void ICACHE_FLASH_ATTR +sntp_time_inc(void) +{ + realtime_stamp++; +} +/** + * SNTP processing of received timestamp + */ +static void ICACHE_FLASH_ATTR +sntp_process(u32_t *receive_timestamp) +{ + /* convert SNTP time (1900-based) to unix GMT time (1970-based) + * @todo: if MSB is 1, SNTP time is 2036-based! + */ + time_t t = (ntohl(receive_timestamp[0]) - DIFF_SEC_1900_1970); + +#if SNTP_CALC_TIME_US + u32_t us = ntohl(receive_timestamp[1]) / 4295; + SNTP_SET_SYSTEM_TIME_US(t, us); + /* display local time from GMT time */ + LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_process: %s, %"U32_F" us", ctime(&t), us)); + +#else /* SNTP_CALC_TIME_US */ + + /* change system time and/or the update the RTC clock */ + SNTP_SET_SYSTEM_TIME(t); + /* display local time from GMT time */ + t += time_zone * 60 * 60;// format GMT + time_zone TIME ZONE + realtime_stamp = t; + os_timer_disarm(&sntp_timer); + os_timer_setfn(&sntp_timer, (os_timer_func_t *)sntp_time_inc, NULL); + os_timer_arm(&sntp_timer, 1000, 1); + os_printf("%s\n",sntp_asctime(sntp_localtime (&t))); +// os_printf("%s\n",ctime(&t)); +// LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_process: %s", ctime(&t))); +#endif /* SNTP_CALC_TIME_US */ +} + +/** + * Initialize request struct to be sent to server. + */ +static void ICACHE_FLASH_ATTR +sntp_initialize_request(struct sntp_msg *req) +{ + os_memset(req, 0, SNTP_MSG_LEN); + req->li_vn_mode = SNTP_LI_NO_WARNING | SNTP_VERSION | SNTP_MODE_CLIENT; + +#if SNTP_CHECK_RESPONSE >= 2 + { + u32_t sntp_time_sec, sntp_time_us; + /* fill in transmit timestamp and save it in 'sntp_last_timestamp_sent' */ + SNTP_GET_SYSTEM_TIME(sntp_time_sec, sntp_time_us); + sntp_last_timestamp_sent[0] = htonl(sntp_time_sec + DIFF_SEC_1900_1970); + req->transmit_timestamp[0] = sntp_last_timestamp_sent[0]; + /* we send/save us instead of fraction to be faster... */ + sntp_last_timestamp_sent[1] = htonl(sntp_time_us); + req->transmit_timestamp[1] = sntp_last_timestamp_sent[1]; + } +#endif /* SNTP_CHECK_RESPONSE >= 2 */ +} + +/** + * Retry: send a new request (and increase retry timeout). + * + * @param arg is unused (only necessary to conform to sys_timeout) + */ +static void ICACHE_FLASH_ATTR +sntp_retry(void* arg) +{ + LWIP_UNUSED_ARG(arg); + + LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_retry: Next request will be sent in %"U32_F" ms\n", + sntp_retry_timeout)); + + /* set up a timer to send a retry and increase the retry delay */ + sys_timeout(sntp_retry_timeout, sntp_request, NULL); + +#if SNTP_RETRY_TIMEOUT_EXP + { + u32_t new_retry_timeout; + /* increase the timeout for next retry */ + new_retry_timeout = sntp_retry_timeout << 1; + /* limit to maximum timeout and prevent overflow */ + if ((new_retry_timeout <= SNTP_RETRY_TIMEOUT_MAX) && + (new_retry_timeout > sntp_retry_timeout)) { + sntp_retry_timeout = new_retry_timeout; + } + } +#endif /* SNTP_RETRY_TIMEOUT_EXP */ +} + +#if SNTP_SUPPORT_MULTIPLE_SERVERS +/** + * If Kiss-of-Death is received (or another packet parsing error), + * try the next server or retry the current server and increase the retry + * timeout if only one server is available. + * (implicitly, SNTP_MAX_SERVERS > 1) + * + * @param arg is unused (only necessary to conform to sys_timeout) + */ +static void +sntp_try_next_server(void* arg) +{ + u8_t old_server, i; + LWIP_UNUSED_ARG(arg); + + old_server = sntp_current_server; + for (i = 0; i < SNTP_MAX_SERVERS - 1; i++) { + sntp_current_server++; + if (sntp_current_server >= SNTP_MAX_SERVERS) { + sntp_current_server = 0; + } + if (!ip_addr_isany(&sntp_servers[sntp_current_server].addr) +#if SNTP_SERVER_DNS + || (sntp_servers[sntp_current_server].name != NULL) +#endif + ) { + LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_try_next_server: Sending request to server %"U16_F"\n", + (u16_t)sntp_current_server)); + /* new server: reset retry timeout */ + SNTP_RESET_RETRY_TIMEOUT(); + /* instantly send a request to the next server */ + sntp_request(NULL); + return; + } + } + /* no other valid server found */ + sntp_current_server = old_server; + sntp_retry(NULL); +} +#else /* SNTP_SUPPORT_MULTIPLE_SERVERS */ +/* Always retry on error if only one server is supported */ +#define sntp_try_next_server sntp_retry +#endif /* SNTP_SUPPORT_MULTIPLE_SERVERS */ + +/** UDP recv callback for the sntp pcb */ +static void ICACHE_FLASH_ATTR +sntp_recv(void *arg, struct udp_pcb* pcb, struct pbuf *p, ip_addr_t *addr, u16_t port) +{ + u8_t mode; + u8_t stratum; + u32_t receive_timestamp[SNTP_RECEIVE_TIME_SIZE]; + err_t err; +//os_printf("sntp_recv\n"); + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(pcb); + + /* packet received: stop retry timeout */ + sys_untimeout(sntp_try_next_server, NULL); + sys_untimeout(sntp_request, NULL); + + err = ERR_ARG; +#if SNTP_CHECK_RESPONSE >= 1 + /* check server address and port */ + if (ip_addr_cmp(addr, &sntp_last_server_address) && + (port == SNTP_PORT)) +#else /* SNTP_CHECK_RESPONSE >= 1 */ + LWIP_UNUSED_ARG(addr); + LWIP_UNUSED_ARG(port); +#endif /* SNTP_CHECK_RESPONSE >= 1 */ + { + /* process the response */ + if (p->tot_len == SNTP_MSG_LEN) { + pbuf_copy_partial(p, &mode, 1, SNTP_OFFSET_LI_VN_MODE); + mode &= SNTP_MODE_MASK; + /* if this is a SNTP response... */ + if ((mode == SNTP_MODE_SERVER) || + (mode == SNTP_MODE_BROADCAST)) { + pbuf_copy_partial(p, &stratum, 1, SNTP_OFFSET_STRATUM); + if (stratum == SNTP_STRATUM_KOD) { + /* Kiss-of-death packet. Use another server or increase UPDATE_DELAY. */ + err = SNTP_ERR_KOD; + LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_recv: Received Kiss-of-Death\n")); + } else { +#if SNTP_CHECK_RESPONSE >= 2 + /* check originate_timetamp against sntp_last_timestamp_sent */ + u32_t originate_timestamp[2]; + pbuf_copy_partial(p, &originate_timestamp, 8, SNTP_OFFSET_ORIGINATE_TIME); + if ((originate_timestamp[0] != sntp_last_timestamp_sent[0]) || + (originate_timestamp[1] != sntp_last_timestamp_sent[1])) + { + LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid originate timestamp in response\n")); + } else +#endif /* SNTP_CHECK_RESPONSE >= 2 */ + /* @todo: add code for SNTP_CHECK_RESPONSE >= 3 and >= 4 here */ + { + /* correct answer */ + err = ERR_OK; + pbuf_copy_partial(p, &receive_timestamp, SNTP_RECEIVE_TIME_SIZE * 4, SNTP_OFFSET_RECEIVE_TIME); + } + } + } else { + LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid mode in response: %"U16_F"\n", (u16_t)mode)); + } + } else { + LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid packet length: %"U16_F"\n", p->tot_len)); + } + } + pbuf_free(p); + if (err == ERR_OK) { + /* Correct response, reset retry timeout */ + SNTP_RESET_RETRY_TIMEOUT(); + + sntp_process(receive_timestamp); + + /* Set up timeout for next request */ + sys_timeout((u32_t)SNTP_UPDATE_DELAY, sntp_request, NULL); + LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_recv: Scheduled next time request: %"U32_F" ms\n", + (u32_t)SNTP_UPDATE_DELAY)); + } else if (err == SNTP_ERR_KOD) { + /* Kiss-of-death packet. Use another server or increase UPDATE_DELAY. */ + sntp_try_next_server(NULL); + } else { + /* another error, try the same server again */ + sntp_retry(NULL); + } +} + +/** Actually send an sntp request to a server. + * + * @param server_addr resolved IP address of the SNTP server + */ +static void ICACHE_FLASH_ATTR +sntp_send_request(ip_addr_t *server_addr) +{ + struct pbuf* p; +// os_printf("sntp_send_request\n"); + p = pbuf_alloc(PBUF_TRANSPORT, SNTP_MSG_LEN, PBUF_RAM); + if (p != NULL) { + struct sntp_msg *sntpmsg = (struct sntp_msg *)p->payload; + LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_send_request: Sending request to server\n")); + /* initialize request message */ + sntp_initialize_request(sntpmsg); + /* send request */ + udp_sendto(sntp_pcb, p, server_addr, SNTP_PORT); + /* free the pbuf after sending it */ + pbuf_free(p); + /* set up receive timeout: try next server or retry on timeout */ + sys_timeout((u32_t)SNTP_RECV_TIMEOUT, sntp_try_next_server, NULL); +#if SNTP_CHECK_RESPONSE >= 1 + /* save server address to verify it in sntp_recv */ + ip_addr_set(&sntp_last_server_address, server_addr); +#endif /* SNTP_CHECK_RESPONSE >= 1 */ + } else { + LWIP_DEBUGF(SNTP_DEBUG_SERIOUS, ("sntp_send_request: Out of memory, trying again in %"U32_F" ms\n", + (u32_t)SNTP_RETRY_TIMEOUT)); + /* out of memory: set up a timer to send a retry */ + sys_timeout((u32_t)SNTP_RETRY_TIMEOUT, sntp_request, NULL); + } +} + +#if SNTP_SERVER_DNS +/** + * DNS found callback when using DNS names as server address. + */ +static void +sntp_dns_found(const char* hostname, ip_addr_t *ipaddr, void *arg) +{ + LWIP_UNUSED_ARG(hostname); + LWIP_UNUSED_ARG(arg); + + if (ipaddr != NULL) { + /* Address resolved, send request */ + LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_dns_found: Server address resolved, sending request\n")); + sntp_send_request(ipaddr); + } else { + /* DNS resolving failed -> try another server */ + LWIP_DEBUGF(SNTP_DEBUG_WARN_STATE, ("sntp_dns_found: Failed to resolve server address resolved, trying next server\n")); + sntp_try_next_server(NULL); + } +} +#endif /* SNTP_SERVER_DNS */ + +/** + * Send out an sntp request. + * + * @param arg is unused (only necessary to conform to sys_timeout) + */ +static void ICACHE_FLASH_ATTR +sntp_request(void *arg) +{ + ip_addr_t sntp_server_address; + err_t err; + + LWIP_UNUSED_ARG(arg); + + /* initialize SNTP server address */ +#if SNTP_SERVER_DNS + + if (sntp_servers[sntp_current_server].name) { + /* always resolve the name and rely on dns-internal caching & timeout */ + ip_addr_set_any(&sntp_servers[sntp_current_server].addr); + err = dns_gethostbyname(sntp_servers[sntp_current_server].name, &sntp_server_address, + sntp_dns_found, NULL); + if (err == ERR_INPROGRESS) { + /* DNS request sent, wait for sntp_dns_found being called */ + LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_request: Waiting for server address to be resolved.\n")); + return; + } else if (err == ERR_OK) { + sntp_servers[sntp_current_server].addr = sntp_server_address; + } + } else +#endif /* SNTP_SERVER_DNS */ + { + sntp_server_address = sntp_servers[sntp_current_server].addr; +// os_printf("sntp_server_address ip %d\n",sntp_server_address.addr); + err = (ip_addr_isany(&sntp_server_address)) ? ERR_ARG : ERR_OK; + } + + if (err == ERR_OK) { + LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_request: current server address is %u.%u.%u.%u\n", + ip4_addr1(&sntp_server_address), ip4_addr2(&sntp_server_address), ip4_addr3(&sntp_server_address), ip4_addr4(&sntp_server_address))); + sntp_send_request(&sntp_server_address); + } else { + /* address conversion failed, try another server */ + LWIP_DEBUGF(SNTP_DEBUG_WARN_STATE, ("sntp_request: Invalid server address, trying next server.\n")); + sys_timeout((u32_t)SNTP_RETRY_TIMEOUT, sntp_try_next_server, NULL); + } +} + +/** + * Initialize this module. + * Send out request instantly or after SNTP_STARTUP_DELAY(_FUNC). + */ +void ICACHE_FLASH_ATTR +sntp_init(void) +{ +#ifdef SNTP_SERVER_ADDRESS +#if SNTP_SERVER_DNS + sntp_setservername(0, SNTP_SERVER_ADDRESS); +#else +#error SNTP_SERVER_ADDRESS string not supported SNTP_SERVER_DNS==0 +#endif +#endif /* SNTP_SERVER_ADDRESS */ + + if (sntp_pcb == NULL) { + SNTP_RESET_RETRY_TIMEOUT(); + sntp_pcb = udp_new(); + LWIP_ASSERT("Failed to allocate udp pcb for sntp client", sntp_pcb != NULL); + if (sntp_pcb != NULL) { + udp_recv(sntp_pcb, sntp_recv, NULL); +#if SNTP_STARTUP_DELAY + sys_timeout((u32_t)SNTP_STARTUP_DELAY_FUNC, sntp_request, NULL); +#else + sntp_request(NULL); +#endif + } + } +} + +/** + * Stop this module. + */ +void ICACHE_FLASH_ATTR +sntp_stop(void) +{ + if (sntp_pcb != NULL) { + sys_untimeout(sntp_request, NULL); + udp_remove(sntp_pcb); + sntp_pcb = NULL; + } + os_timer_disarm(&sntp_timer); + realtime_stamp = 0; +} + +#if SNTP_GET_SERVERS_FROM_DHCP +/** + * Config SNTP server handling by IP address, name, or DHCP; clear table + * @param set_servers_from_dhcp enable or disable getting server addresses from dhcp + */ +void +sntp_servermode_dhcp(int set_servers_from_dhcp) +{ + u8_t new_mode = set_servers_from_dhcp ? 1 : 0; + if (sntp_set_servers_from_dhcp != new_mode) { + sntp_set_servers_from_dhcp = new_mode; + } +} +#endif /* SNTP_GET_SERVERS_FROM_DHCP */ + +/** + * Initialize one of the NTP servers by IP address + * + * @param numdns the index of the NTP server to set must be < SNTP_MAX_SERVERS + * @param dnsserver IP address of the NTP server to set + */ +void ICACHE_FLASH_ATTR +sntp_setserver(u8_t idx, ip_addr_t *server) +{ + if (idx < SNTP_MAX_SERVERS) { + if (server != NULL) { + sntp_servers[idx].addr = (*server); +// os_printf("server ip %d\n",server->addr); + } else { + ip_addr_set_any(&sntp_servers[idx].addr); + } +#if SNTP_SERVER_DNS + sntp_servers[idx].name = NULL; +#endif + } +} + +#if LWIP_DHCP && SNTP_GET_SERVERS_FROM_DHCP +/** + * Initialize one of the NTP servers by IP address, required by DHCP + * + * @param numdns the index of the NTP server to set must be < SNTP_MAX_SERVERS + * @param dnsserver IP address of the NTP server to set + */ +void +dhcp_set_ntp_servers(u8_t num, ip_addr_t *server) +{ + LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp: %s %u.%u.%u.%u as NTP server #%u via DHCP\n", + (sntp_set_servers_from_dhcp ? "Got" : "Rejected"), + ip4_addr1(server), ip4_addr2(server), ip4_addr3(server), ip4_addr4(server), num)); + if (sntp_set_servers_from_dhcp && num) { + u8_t i; + for (i = 0; (i < num) && (i < SNTP_MAX_SERVERS); i++) { + sntp_setserver(i, &server[i]); + } + for (i = num; i < SNTP_MAX_SERVERS; i++) { + sntp_setserver(i, NULL); + } + } +} +#endif /* LWIP_DHCP && SNTP_GET_SERVERS_FROM_DHCP */ + +/** + * Obtain one of the currently configured by IP address (or DHCP) NTP servers + * + * @param numdns the index of the NTP server + * @return IP address of the indexed NTP server or "ip_addr_any" if the NTP + * server has not been configured by address (or at all). + */ +ip_addr_t ICACHE_FLASH_ATTR +sntp_getserver(u8_t idx) +{ + if (idx < SNTP_MAX_SERVERS) { + return sntp_servers[idx].addr; + } + return *IP_ADDR_ANY; +} + +#if SNTP_SERVER_DNS +/** + * Initialize one of the NTP servers by name + * + * @param numdns the index of the NTP server to set must be < SNTP_MAX_SERVERS + * @param dnsserver DNS name of the NTP server to set, to be resolved at contact time + */ +void ICACHE_FLASH_ATTR +sntp_setservername(u8_t idx, char *server) +{ + if (idx < SNTP_MAX_SERVERS) { + sntp_servers[idx].name = server; + } +} + +/** + * Obtain one of the currently configured by name NTP servers. + * + * @param numdns the index of the NTP server + * @return IP address of the indexed NTP server or NULL if the NTP + * server has not been configured by name (or at all) + */ +char * ICACHE_FLASH_ATTR +sntp_getservername(u8_t idx) +{ + if (idx < SNTP_MAX_SERVERS) { + return sntp_servers[idx].name; + } + return NULL; +} +#endif /* SNTP_SERVER_DNS */ + +#endif /* LWIP_UDP */ diff --git a/variants/lwip/lwip/core/stats.c b/variants/lwip/lwip/core/stats.c new file mode 100755 index 000000000..69f97d41f --- /dev/null +++ b/variants/lwip/lwip/core/stats.c @@ -0,0 +1,176 @@ +/** + * @file + * Statistics module + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_STATS /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/stats.h" +#include "lwip/mem.h" + +#include + +struct stats_ lwip_stats; + +void stats_init(void) +{ +#ifdef LWIP_DEBUG +#if MEMP_STATS + const char * memp_names[] = { +#define LWIP_MEMPOOL(name,num,size,desc) desc, +#include "lwip/memp_std.h" + }; + int i; + for (i = 0; i < MEMP_MAX; i++) { + lwip_stats.memp[i].name = memp_names[i]; + } +#endif /* MEMP_STATS */ +#if MEM_STATS + lwip_stats.mem.name = "MEM"; +#endif /* MEM_STATS */ +#endif /* LWIP_DEBUG */ +} + +#if LWIP_STATS_DISPLAY +void +stats_display_proto(struct stats_proto *proto, char *name) +{ + LWIP_PLATFORM_DIAG(("\n%s\n\t", name)); + LWIP_PLATFORM_DIAG(("xmit: %"STAT_COUNTER_F"\n\t", proto->xmit)); + LWIP_PLATFORM_DIAG(("recv: %"STAT_COUNTER_F"\n\t", proto->recv)); + LWIP_PLATFORM_DIAG(("fw: %"STAT_COUNTER_F"\n\t", proto->fw)); + LWIP_PLATFORM_DIAG(("drop: %"STAT_COUNTER_F"\n\t", proto->drop)); + LWIP_PLATFORM_DIAG(("chkerr: %"STAT_COUNTER_F"\n\t", proto->chkerr)); + LWIP_PLATFORM_DIAG(("lenerr: %"STAT_COUNTER_F"\n\t", proto->lenerr)); + LWIP_PLATFORM_DIAG(("memerr: %"STAT_COUNTER_F"\n\t", proto->memerr)); + LWIP_PLATFORM_DIAG(("rterr: %"STAT_COUNTER_F"\n\t", proto->rterr)); + LWIP_PLATFORM_DIAG(("proterr: %"STAT_COUNTER_F"\n\t", proto->proterr)); + LWIP_PLATFORM_DIAG(("opterr: %"STAT_COUNTER_F"\n\t", proto->opterr)); + LWIP_PLATFORM_DIAG(("err: %"STAT_COUNTER_F"\n\t", proto->err)); + LWIP_PLATFORM_DIAG(("cachehit: %"STAT_COUNTER_F"\n", proto->cachehit)); +} + +#if IGMP_STATS +void +stats_display_igmp(struct stats_igmp *igmp) +{ + LWIP_PLATFORM_DIAG(("\nIGMP\n\t")); + LWIP_PLATFORM_DIAG(("xmit: %"STAT_COUNTER_F"\n\t", igmp->xmit)); + LWIP_PLATFORM_DIAG(("recv: %"STAT_COUNTER_F"\n\t", igmp->recv)); + LWIP_PLATFORM_DIAG(("drop: %"STAT_COUNTER_F"\n\t", igmp->drop)); + LWIP_PLATFORM_DIAG(("chkerr: %"STAT_COUNTER_F"\n\t", igmp->chkerr)); + LWIP_PLATFORM_DIAG(("lenerr: %"STAT_COUNTER_F"\n\t", igmp->lenerr)); + LWIP_PLATFORM_DIAG(("memerr: %"STAT_COUNTER_F"\n\t", igmp->memerr)); + LWIP_PLATFORM_DIAG(("proterr: %"STAT_COUNTER_F"\n\t", igmp->proterr)); + LWIP_PLATFORM_DIAG(("rx_v1: %"STAT_COUNTER_F"\n\t", igmp->rx_v1)); + LWIP_PLATFORM_DIAG(("rx_group: %"STAT_COUNTER_F"\n", igmp->rx_group)); + LWIP_PLATFORM_DIAG(("rx_general: %"STAT_COUNTER_F"\n", igmp->rx_general)); + LWIP_PLATFORM_DIAG(("rx_report: %"STAT_COUNTER_F"\n\t", igmp->rx_report)); + LWIP_PLATFORM_DIAG(("tx_join: %"STAT_COUNTER_F"\n\t", igmp->tx_join)); + LWIP_PLATFORM_DIAG(("tx_leave: %"STAT_COUNTER_F"\n\t", igmp->tx_leave)); + LWIP_PLATFORM_DIAG(("tx_report: %"STAT_COUNTER_F"\n\t", igmp->tx_report)); +} +#endif /* IGMP_STATS */ + +#if MEM_STATS || MEMP_STATS +void +stats_display_mem(struct stats_mem *mem, char *name) +{ + LWIP_PLATFORM_DIAG(("\nMEM %s\n\t", name)); + LWIP_PLATFORM_DIAG(("avail: %"U32_F"\n\t", (u32_t)mem->avail)); + LWIP_PLATFORM_DIAG(("used: %"U32_F"\n\t", (u32_t)mem->used)); + LWIP_PLATFORM_DIAG(("max: %"U32_F"\n\t", (u32_t)mem->max)); + LWIP_PLATFORM_DIAG(("err: %"U32_F"\n", (u32_t)mem->err)); +} + +#if MEMP_STATS +void +stats_display_memp(struct stats_mem *mem, int index) +{ + char * memp_names[] = { +#define LWIP_MEMPOOL(name,num,size,desc) desc, +#include "lwip/memp_std.h" + }; + if(index < MEMP_MAX) { + stats_display_mem(mem, memp_names[index]); + } +} +#endif /* MEMP_STATS */ +#endif /* MEM_STATS || MEMP_STATS */ + +#if SYS_STATS +void +stats_display_sys(struct stats_sys *sys) +{ + LWIP_PLATFORM_DIAG(("\nSYS\n\t")); + LWIP_PLATFORM_DIAG(("sem.used: %"U32_F"\n\t", (u32_t)sys->sem.used)); + LWIP_PLATFORM_DIAG(("sem.max: %"U32_F"\n\t", (u32_t)sys->sem.max)); + LWIP_PLATFORM_DIAG(("sem.err: %"U32_F"\n\t", (u32_t)sys->sem.err)); + LWIP_PLATFORM_DIAG(("mutex.used: %"U32_F"\n\t", (u32_t)sys->mutex.used)); + LWIP_PLATFORM_DIAG(("mutex.max: %"U32_F"\n\t", (u32_t)sys->mutex.max)); + LWIP_PLATFORM_DIAG(("mutex.err: %"U32_F"\n\t", (u32_t)sys->mutex.err)); + LWIP_PLATFORM_DIAG(("mbox.used: %"U32_F"\n\t", (u32_t)sys->mbox.used)); + LWIP_PLATFORM_DIAG(("mbox.max: %"U32_F"\n\t", (u32_t)sys->mbox.max)); + LWIP_PLATFORM_DIAG(("mbox.err: %"U32_F"\n\t", (u32_t)sys->mbox.err)); +} +#endif /* SYS_STATS */ + +void +stats_display(void) +{ + s16_t i; + + LINK_STATS_DISPLAY(); + ETHARP_STATS_DISPLAY(); + IPFRAG_STATS_DISPLAY(); + IP_STATS_DISPLAY(); + IGMP_STATS_DISPLAY(); + ICMP_STATS_DISPLAY(); + UDP_STATS_DISPLAY(); + TCP_STATS_DISPLAY(); + MEM_STATS_DISPLAY(); + for (i = 0; i < MEMP_MAX; i++) { + MEMP_STATS_DISPLAY(i); + } + SYS_STATS_DISPLAY(); +} +#endif /* LWIP_STATS_DISPLAY */ + +#endif /* LWIP_STATS */ + diff --git a/variants/lwip/lwip/core/sys.c b/variants/lwip/lwip/core/sys.c new file mode 100755 index 000000000..d3a77deb2 --- /dev/null +++ b/variants/lwip/lwip/core/sys.c @@ -0,0 +1,66 @@ +/** + * @file + * lwIP Operating System abstraction + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/sys.h" + +/* Most of the functions defined in sys.h must be implemented in the + * architecture-dependent file sys_arch.c */ + +#if !NO_SYS + +/** + * Sleep for some ms. Timeouts are NOT processed while sleeping. + * + * @param ms number of milliseconds to sleep + */ +void +sys_msleep(u32_t ms) +{ + if (ms > 0) { + sys_sem_t delaysem; + err_t err = sys_sem_new(&delaysem, 0); + if (err == ERR_OK) { + sys_arch_sem_wait(&delaysem, ms); + sys_sem_free(&delaysem); + } + } +} + +#endif /* !NO_SYS */ diff --git a/variants/lwip/lwip/core/sys_arch.c b/variants/lwip/lwip/core/sys_arch.c new file mode 100755 index 000000000..e79042f22 --- /dev/null +++ b/variants/lwip/lwip/core/sys_arch.c @@ -0,0 +1,13 @@ +/* + * copyright (c) 2010 - 2011 espressif system + */ + +#include "c_types.h" +#include "ets_sys.h" +#include "osapi.h" +#include "os_type.h" + +#include "lwip/opt.h" +#include "lwip/sys.h" + +#include "eagle_soc.h" diff --git a/variants/lwip/lwip/core/tcp.c b/variants/lwip/lwip/core/tcp.c new file mode 100755 index 000000000..ee50cebd7 --- /dev/null +++ b/variants/lwip/lwip/core/tcp.c @@ -0,0 +1,1671 @@ +/** + * @file + * Transmission Control Protocol for IP + * + * This file contains common functions for the TCP implementation, such as functinos + * for manipulating the data structures and the TCP timer functions. TCP functions + * related to input and output is found in tcp_in.c and tcp_out.c respectively. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/snmp.h" +#include "lwip/tcp.h" +#include "lwip/tcp_impl.h" +#include "lwip/debug.h" +#include "lwip/stats.h" + +#include + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +#if TCP_DEBUG +const char tcp_state_str_rodata[][12] ICACHE_RODATA_ATTR = { + "CLOSED", + "LISTEN", + "SYN_SENT", + "SYN_RCVD", + "ESTABLISHED", + "FIN_WAIT_1", + "FIN_WAIT_2", + "CLOSE_WAIT", + "CLOSING", + "LAST_ACK", + "TIME_WAIT" +}; + +char tcp_state_str[12]; +#endif + +/* Incremented every coarse grained timer shot (typically every 500 ms). */ +u32_t tcp_ticks; +const u8_t tcp_backoff[13] ICACHE_RODATA_ATTR = + { 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7}; + /* Times per slowtmr hits */ +const u8_t tcp_persist_backoff[7] ICACHE_RODATA_ATTR = { 3, 6, 12, 24, 48, 96, 120 }; + +/* The TCP PCB lists. */ + +/** List of all TCP PCBs bound but not yet (connected || listening) */ +struct tcp_pcb *tcp_bound_pcbs; +/** List of all TCP PCBs in LISTEN state */ +union tcp_listen_pcbs_t tcp_listen_pcbs; +/** List of all TCP PCBs that are in a state in which + * they accept or send data. */ +struct tcp_pcb *tcp_active_pcbs; +/** List of all TCP PCBs in TIME-WAIT state */ +struct tcp_pcb *tcp_tw_pcbs; + +#define NUM_TCP_PCB_LISTS 4 +#define NUM_TCP_PCB_LISTS_NO_TIME_WAIT 3 +/** An array with all (non-temporary) PCB lists, mainly used for smaller code size */ +struct tcp_pcb ** const tcp_pcb_lists[] ICACHE_RODATA_ATTR = {&tcp_listen_pcbs.pcbs, &tcp_bound_pcbs, + &tcp_active_pcbs, &tcp_tw_pcbs}; + +/** Only used for temporary storage. */ +struct tcp_pcb *tcp_tmp_pcb; + +/** Timer counter to handle calling slow-timer from tcp_tmr() */ +static u8_t tcp_timer; +static u16_t tcp_new_port(void);//����µ�tcp���ض˿� + +/** + * Called periodically to dispatch TCP timers. + * + */ +void +tcp_tmr(void) +{ + /* Call tcp_fasttmr() every 250 ms */ + tcp_fasttmr(); + + if (++tcp_timer & 1) { + /* Call tcp_tmr() every 500 ms, i.e., every other timer + tcp_tmr() is called. */ + tcp_slowtmr(); + } +} + +/** + * Closes the TX side of a connection held by the PCB. + * For tcp_close(), a RST is sent if the application didn't receive all data + * (tcp_recved() not called for all data passed to recv callback). + * + * Listening pcbs are freed and may not be referenced any more. + * Connection pcbs are freed if not yet connected and may not be referenced + * any more. If a connection is established (at least SYN received or in + * a closing state), the connection is closed, and put in a closing state. + * The pcb is then automatically freed in tcp_slowtmr(). It is therefore + * unsafe to reference it. + * + * @param pcb the tcp_pcb to close + * @return ERR_OK if connection has been closed + * another err_t if closing failed and pcb is not freed + */ +static err_t +tcp_close_shutdown(struct tcp_pcb *pcb, u8_t rst_on_unacked_data) +{ + err_t err; + + if (rst_on_unacked_data && (pcb->state != LISTEN)) { + if ((pcb->refused_data != NULL) || (pcb->rcv_wnd != TCP_WND)) { + /* Not all data received by application, send RST to tell the remote + side about this. */ + LWIP_ASSERT("pcb->flags & TF_RXCLOSED", pcb->flags & TF_RXCLOSED); + + /* don't call tcp_abort here: we must not deallocate the pcb since + that might not be expected when calling tcp_close */ + tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip, + pcb->local_port, pcb->remote_port); + + tcp_pcb_purge(pcb); + + /* TODO: to which state do we move now? */ + + /* move to TIME_WAIT since we close actively */ + TCP_RMV(&tcp_active_pcbs, pcb); + pcb->state = TIME_WAIT; + TCP_REG(&tcp_tw_pcbs, pcb); + + return ERR_OK; + } + } + + switch (pcb->state) { + case CLOSED: + /* Closing a pcb in the CLOSED state might seem erroneous, + * however, it is in this state once allocated and as yet unused + * and the user needs some way to free it should the need arise. + * Calling tcp_close() with a pcb that has already been closed, (i.e. twice) + * or for a pcb that has been used and then entered the CLOSED state + * is erroneous, but this should never happen as the pcb has in those cases + * been freed, and so any remaining handles are bogus. */ + /*��CLOSED״̬�¹ر�һ��pcb�ƺ��Ǵ���ģ� + *������ˣ�һ�������״̬�·����˶��һ�û��ʹ��,�û���ҪһЩ�취���ͷ��� + *����һ���Ѿ����رյ�pcb��tcp_close(),(��2��)����һ���Ѿ���ʹ����֮�󣬽���CLOSE״̬�Ǵ���� + *������Щ����±��ͷŵ�pcb�Dz�����ڵ�,��ˣ��κ�ʣ��ľ���Ǽٵ� + */ + err = ERR_OK;//�趨����ֵ + if (pcb->local_port != 0) { + TCP_RMV(&tcp_bound_pcbs, pcb); + } + memp_free(MEMP_TCP_PCB, pcb);//��MEMP_TCP_PCB�ڴ���趨�ͷŵ���pcb��Ӧ�ĵ�Ԫֵ,�ͷ��ڴ� + pcb = NULL; + break; + case LISTEN: + err = ERR_OK; + tcp_pcb_remove(&tcp_listen_pcbs.pcbs, pcb);//�Ӽ����PCB�б���ɾ���Ӧ��pcb + memp_free(MEMP_TCP_PCB_LISTEN, pcb);//��MEMP_TCP_PCB_LISTEN�ڴ�����趨�ͷŵ�pcb��Ԫֵ ,�ͷ��ڴ� + pcb = NULL; + break; + case SYN_SENT: + err = ERR_OK; + tcp_pcb_remove(&tcp_active_pcbs, pcb); + memp_free(MEMP_TCP_PCB, pcb); + pcb = NULL; + snmp_inc_tcpattemptfails(); + break; + case SYN_RCVD: + err = tcp_send_fin(pcb);//���������ر�FIN���ֱ��� + if (err == ERR_OK) { + snmp_inc_tcpattemptfails(); + pcb->state = FIN_WAIT_1;//ת��FIN_WAIT_1״̬ + } + break; + case ESTABLISHED: + err = tcp_send_fin(pcb); + if (err == ERR_OK) { + snmp_inc_tcpestabresets(); + pcb->state = FIN_WAIT_1; + } + break; + case CLOSE_WAIT: + err = tcp_send_fin(pcb); + if (err == ERR_OK) { + snmp_inc_tcpestabresets(); + pcb->state = LAST_ACK;//����LAST_ACK�ȴ�ACK��ʱ + } + break; + default: + /* Has already been closed, do nothing. */ + err = ERR_OK; + pcb = NULL; + break; + } + + if (pcb != NULL && err == ERR_OK) { + /* To ensure all data has been sent when tcp_close returns, we have + to make sure tcp_output doesn't fail. + Since we don't really have to ensure all data has been sent when tcp_close + returns (unsent data is sent from tcp timer functions, also), we don't care + for the return value of tcp_output for now. */ + /* @todo: When implementing SO_LINGER, this must be changed somehow: + If SOF_LINGER is set, the data should be sent and acked before close returns. + This can only be valid for sequential APIs, not for the raw API. */ + tcp_output(pcb);//���ú����Ϳ��ƿ������ʣ��ı��ģ�����FIN���ֱ��Ķ� + } + return err; +} + +/** + * Closes the connection held by the PCB. + * + * Listening pcbs are freed and may not be referenced any more. + * Connection pcbs are freed if not yet connected and may not be referenced + * any more. If a connection is established (at least SYN received or in + * a closing state), the connection is closed, and put in a closing state. + * The pcb is then automatically freed in tcp_slowtmr(). It is therefore + * unsafe to reference it (unless an error is returned). + * + * @param pcb the tcp_pcb to close + * @return ERR_OK if connection has been closed + * another err_t if closing failed and pcb is not freed + */ + /* + *ͨ��PCB�ر��������� + *�����е�pcbӦ�ñ��ͷŵģ�Ҳ����ԶҲ���ᱻʹ���� + *���û�����ӻ�����Ҳû�б�����,���ӵ�pcbӦ�ñ��ͷŵ� + *���һ�����ӱ�����(����SYN�Ѿ������ջ�����һ���ر��е�״̬) + *���ӱ��ر��ˣ�����������һ�����ڹرյ�״̬ + *pcb�Զ���tcp_slowtmr()�ͷ�,�����������Dz���ȫ�� + */ +err_t +tcp_close(struct tcp_pcb *pcb) +{ +#if TCP_DEBUG //TCP debug��Ϣ����ӡpcb��״̬ + LWIP_DEBUGF(TCP_DEBUG, ("tcp_close: closing in ")); + tcp_debug_print_state(pcb->state); +#endif /* TCP_DEBUG */ + + if (pcb->state != LISTEN) { + /* Set a flag not to receive any more data... */ + pcb->flags |= TF_RXCLOSED; + } + /* ... and close */ + return tcp_close_shutdown(pcb, 1); +} + +/** + * Causes all or part of a full-duplex connection of this PCB to be shut down. + * This doesn't deallocate the PCB! + * + * @param pcb PCB to shutdown + * @param shut_rx shut down receive side if this is != 0 + * @param shut_tx shut down send side if this is != 0 + * @return ERR_OK if shutdown succeeded (or the PCB has already been shut down) + * another err_t on error. + */ +err_t +tcp_shutdown(struct tcp_pcb *pcb, int shut_rx, int shut_tx) +{ + if (pcb->state == LISTEN) { + return ERR_CONN; + } + if (shut_rx) { + /* shut down the receive side: free buffered data... */ + if (pcb->refused_data != NULL) { + pbuf_free(pcb->refused_data); + pcb->refused_data = NULL; + } + /* ... and set a flag not to receive any more data */ + pcb->flags |= TF_RXCLOSED; + } + if (shut_tx) { + /* This can't happen twice since if it succeeds, the pcb's state is changed. + Only close in these states as the others directly deallocate the PCB */ + switch (pcb->state) { + case SYN_RCVD: + case ESTABLISHED: + case CLOSE_WAIT: + return tcp_close_shutdown(pcb, 0); + default: + /* don't shut down other states */ + break; + } + } + /* @todo: return another err_t if not in correct state or already shut? */ + return ERR_OK; +} + +/** + * Abandons a connection and optionally sends a RST to the remote + * host. Deletes the local protocol control block. This is done when + * a connection is killed because of shortage of memory. + * + * @param pcb the tcp_pcb to abort + * @param reset boolean to indicate whether a reset should be sent + */ +void +tcp_abandon(struct tcp_pcb *pcb, int reset) +{ + u32_t seqno, ackno; + u16_t remote_port, local_port; + ip_addr_t remote_ip, local_ip; +#if LWIP_CALLBACK_API + tcp_err_fn errf; +#endif /* LWIP_CALLBACK_API */ + void *errf_arg; + + /* pcb->state LISTEN not allowed here */ + LWIP_ASSERT("don't call tcp_abort/tcp_abandon for listen-pcbs", + pcb->state != LISTEN); + /* Figure out on which TCP PCB list we are, and remove us. If we + are in an active state, call the receive function associated with + the PCB with a NULL argument, and send an RST to the remote end. */ + if (pcb->state == TIME_WAIT) { + tcp_pcb_remove(&tcp_tw_pcbs, pcb); + memp_free(MEMP_TCP_PCB, pcb); + } else { + seqno = pcb->snd_nxt; + ackno = pcb->rcv_nxt; + ip_addr_copy(local_ip, pcb->local_ip); + ip_addr_copy(remote_ip, pcb->remote_ip); + local_port = pcb->local_port; + remote_port = pcb->remote_port; +#if LWIP_CALLBACK_API + errf = pcb->errf; +#endif /* LWIP_CALLBACK_API */ + errf_arg = pcb->callback_arg; + tcp_pcb_remove(&tcp_active_pcbs, pcb); + if (pcb->unacked != NULL) { + tcp_segs_free(pcb->unacked); + } + if (pcb->unsent != NULL) { + tcp_segs_free(pcb->unsent); + } +#if TCP_QUEUE_OOSEQ + if (pcb->ooseq != NULL) { + tcp_segs_free(pcb->ooseq); + } +#endif /* TCP_QUEUE_OOSEQ */ + if (reset) { + LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_abandon: sending RST\n")); + tcp_rst(seqno, ackno, &local_ip, &remote_ip, local_port, remote_port); + } + TCP_EVENT_ERR(errf, errf_arg, ERR_ABRT); + memp_free(MEMP_TCP_PCB, pcb); + } +} + +/** + * Aborts the connection by sending a RST (reset) segment to the remote + * host. The pcb is deallocated. This function never fails. + * + * ATTENTION: When calling this from one of the TCP callbacks, make + * sure you always return ERR_ABRT (and never return ERR_ABRT otherwise + * or you will risk accessing deallocated memory or memory leaks! + * + * @param pcb the tcp pcb to abort + */ +void +tcp_abort(struct tcp_pcb *pcb) +{ + tcp_abandon(pcb, 1); +} + +/** + * Binds the connection to a local portnumber and IP address. If the + * IP address is not given (i.e., ipaddr == NULL), the IP address of + * the outgoing network interface is used instead. + * + * @param pcb the tcp_pcb to bind (no check is done whether this pcb is + * already bound!) + * @param ipaddr the local ip address to bind to (use IP_ADDR_ANY to bind + * to any local address + * @param port the local port to bind to + * @return ERR_USE if the port is already in use + * ERR_OK if bound + */ +err_t +tcp_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port) +{ + int i; + int max_pcb_list = NUM_TCP_PCB_LISTS; + struct tcp_pcb *cpcb; + + LWIP_ERROR("tcp_bind: can only bind in state CLOSED", pcb->state == CLOSED, return ERR_ISCONN); + +#if SO_REUSE + /* Unless the REUSEADDR flag is set, + we have to check the pcbs in TIME-WAIT state, also. + We do not dump TIME_WAIT pcb's; they can still be matched by incoming + packets using both local and remote IP addresses and ports to distinguish. + */ + if ((pcb->so_options & SOF_REUSEADDR) != 0) { + max_pcb_list = NUM_TCP_PCB_LISTS_NO_TIME_WAIT; + } +#endif /* SO_REUSE */ + + if (port == 0) { + port = tcp_new_port(); + } + + /* Check if the address already is in use (on all lists) */ + for (i = 0; i < max_pcb_list; i++) { + for(cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) { + if (cpcb->local_port == port) { +#if SO_REUSE + /* Omit checking for the same port if both pcbs have REUSEADDR set. + For SO_REUSEADDR, the duplicate-check for a 5-tuple is done in + tcp_connect. */ + if (((pcb->so_options & SOF_REUSEADDR) == 0) || + ((cpcb->so_options & SOF_REUSEADDR) == 0)) +#endif /* SO_REUSE */ + { + if (ip_addr_isany(&(cpcb->local_ip)) || + ip_addr_isany(ipaddr) || + ip_addr_cmp(&(cpcb->local_ip), ipaddr)) { + //os_printf("Address in use\n"); + return ERR_USE; + } + } + } + } + } + + if (!ip_addr_isany(ipaddr)) { + pcb->local_ip = *ipaddr; + } + pcb->local_port = port; + TCP_REG(&tcp_bound_pcbs, pcb); + LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind: bind to port %"U16_F"\n", port)); + return ERR_OK; +} +#if LWIP_CALLBACK_API +/** + * Default accept callback if no accept callback is specified by the user. + */ +static err_t +tcp_accept_null(void *arg, struct tcp_pcb *pcb, err_t err) +{ + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(err); + + return ERR_ABRT; +} +#endif /* LWIP_CALLBACK_API */ + +/** + * Set the state of the connection to be LISTEN, which means that it + * is able to accept incoming connections. The protocol control block + * is reallocated in order to consume less memory. Setting the + * connection to LISTEN is an irreversible process. + *��ij���󶨵Ŀ��ƿ���Ϊ����״̬ + * @param pcb the original tcp_pcb �����Ŀ��ƿ���� + * @param backlog the incoming connections queue limit + * @return tcp_pcb used for listening, consumes less memory.ָ������״̬�Ŀ��ƿ� + * + * @note The original tcp_pcb is freed. This function therefore has to be + * called like this: + * tpcb = tcp_listen(tpcb); + */ +struct tcp_pcb * +tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog) +{ + struct tcp_pcb_listen *lpcb; + + LWIP_UNUSED_ARG(backlog); + LWIP_ERROR("tcp_listen: pcb already connected", pcb->state == CLOSED, return NULL); + + /* already listening? */ + if (pcb->state == LISTEN) { + return pcb; + } +#if SO_REUSE + if ((pcb->so_options & SOF_REUSEADDR) != 0) { + /* Since SOF_REUSEADDR allows reusing a local address before the pcb's usage + is declared (listen-/connection-pcb), we have to make sure now that + this port is only used once for every local IP. */ + for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { + if (lpcb->local_port == pcb->local_port) { + if (ip_addr_cmp(&lpcb->local_ip, &pcb->local_ip)) { + /* this address/port is already used */ + return NULL; + } + } + } + } +#endif /* SO_REUSE */ + lpcb = (struct tcp_pcb_listen *)memp_malloc(MEMP_TCP_PCB_LISTEN);//�����ڴ�ؿռ� + if (lpcb == NULL) { + return NULL; + } + lpcb->callback_arg = pcb->callback_arg; + lpcb->local_port = pcb->local_port; + lpcb->state = LISTEN; + lpcb->prio = pcb->prio; + lpcb->so_options = pcb->so_options; + lpcb->so_options |= SOF_ACCEPTCONN; + lpcb->ttl = pcb->ttl; + lpcb->tos = pcb->tos; + ip_addr_copy(lpcb->local_ip, pcb->local_ip); + if (pcb->local_port != 0) { + TCP_RMV(&tcp_bound_pcbs, pcb); + } + memp_free(MEMP_TCP_PCB, pcb); +#if LWIP_CALLBACK_API + lpcb->accept = tcp_accept_null;//���ܿͻ������ӵ�Ĭ�ϻص����� +#endif /* LWIP_CALLBACK_API */ +#if TCP_LISTEN_BACKLOG + lpcb->accepts_pending = 0; + lpcb->backlog = (backlog ? backlog : 1); +#endif /* TCP_LISTEN_BACKLOG */ + TCP_REG(&tcp_listen_pcbs.pcbs, (struct tcp_pcb *)lpcb);//���ƿ����tcp_listen_pcbs�����ײ� + return (struct tcp_pcb *)lpcb; +} + +/** + * Update the state that tracks the available window space to advertise. + * + * Returns how much extra window would be advertised if we sent an + * update now. + */ +u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb) +{ + u32_t new_right_edge = pcb->rcv_nxt + pcb->rcv_wnd; + + if (TCP_SEQ_GEQ(new_right_edge, pcb->rcv_ann_right_edge + LWIP_MIN((TCP_WND / 2), pcb->mss))) { + /* we can advertise more window */ + pcb->rcv_ann_wnd = pcb->rcv_wnd; + return new_right_edge - pcb->rcv_ann_right_edge; + } else { + if (TCP_SEQ_GT(pcb->rcv_nxt, pcb->rcv_ann_right_edge)) { + /* Can happen due to other end sending out of advertised window, + * but within actual available (but not yet advertised) window */ + pcb->rcv_ann_wnd = 0; + } else { + /* keep the right edge of window constant */ + u32_t new_rcv_ann_wnd = pcb->rcv_ann_right_edge - pcb->rcv_nxt; + LWIP_ASSERT("new_rcv_ann_wnd <= 0xffff", new_rcv_ann_wnd <= 0xffff); + pcb->rcv_ann_wnd = (u16_t)new_rcv_ann_wnd; + } + return 0; + } +} + +/** + * This function should be called by the application when it has + * processed the data. The purpose is to advertise a larger window + * when the data has been processed. + *Ӧ�ó�����ݴ�����Ϻ�֪ͨ�ں˸��½��մ��� + * @param pcb the tcp_pcb for which data is read + * @param len the amount of bytes that have been read by the application + */ +void +tcp_recved(struct tcp_pcb *pcb, u16_t len) +{ + int wnd_inflation; + + LWIP_ASSERT("tcp_recved: len would wrap rcv_wnd\n", + len <= 0xffff - pcb->rcv_wnd ); + + pcb->rcv_wnd += len; + if (pcb->rcv_wnd > TCP_WND) { + pcb->rcv_wnd = TCP_WND; + } + + wnd_inflation = tcp_update_rcv_ann_wnd(pcb); + + /* If the change in the right edge of window is significant (default + * watermark is TCP_WND/4), then send an explicit update now. + * Otherwise wait for a packet to be sent in the normal course of + * events (or more window to be available later) */ + if (wnd_inflation >= TCP_WND_UPDATE_THRESHOLD) { + tcp_ack_now(pcb); + tcp_output(pcb); + } + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_recved: recveived %"U16_F" bytes, wnd %"U16_F" (%"U16_F").\n", + len, pcb->rcv_wnd, TCP_WND - pcb->rcv_wnd)); +} + +/** + * A nastly hack featuring 'goto' statements that allocates a + * new TCP local port. + * + * @return a new (free) local TCP port number + */ +static u16_t +tcp_new_port(void) +{ + int i; + struct tcp_pcb *pcb; +#ifndef TCP_LOCAL_PORT_RANGE_START +#define TCP_LOCAL_PORT_RANGE_START 1024 +#define TCP_LOCAL_PORT_RANGE_END 0x7fff +#endif + static u16_t port = TCP_LOCAL_PORT_RANGE_START; + + again: +// if (++port >= TCP_LOCAL_PORT_RANGE_END) { +// port = TCP_LOCAL_PORT_RANGE_START; +// } + port = os_random(); + port %= TCP_LOCAL_PORT_RANGE_END; + if (port < TCP_LOCAL_PORT_RANGE_START) + port += TCP_LOCAL_PORT_RANGE_START; + /* Check all PCB lists. */ + for (i = 0; i < NUM_TCP_PCB_LISTS; i++) { + for(pcb = *tcp_pcb_lists[i]; pcb != NULL; pcb = pcb->next) { + if (pcb->local_port == port) { + goto again; + } + } + } + return port; +} + +/** + * Connects to another host. The function given as the "connected" + * argument will be called when the connection has been established. + *�����������һ��SYN���ֱ��� + * @param pcb the tcp_pcb used to establish the connection �����Ŀ��ƿ���� + * @param ipaddr the remote ip address to connect to ������IP��ַ + * @param port the remote tcp port to connect to �������˿ں� + * @param connected callback function to call when connected (or on error) + * @return ERR_VAL if invalid arguments are given + * ERR_OK if connect request has been sent + * other err_t values if connect request couldn't be sent + */ +err_t +tcp_connect(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port, + tcp_connected_fn connected) +{ + err_t ret; + u32_t iss; + u16_t old_local_port; + + LWIP_ERROR("tcp_connect: can only connected from state CLOSED", pcb->state == CLOSED, return ERR_ISCONN); + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_connect to port %"U16_F"\n", port)); + if (ipaddr != NULL) { + pcb->remote_ip = *ipaddr;//������IP��ַ��Ч�������Ӽ�¼�м�¼��IP��ַ�����ò·µ»Ø´ï¿½ï¿½ï¿½ + } else { + return ERR_VAL; + } + pcb->remote_port = port;//��¼�������˿�(Ŀ�Ķ˿�) + + /* check if we have a route to the remote host */ + if (ip_addr_isany(&(pcb->local_ip))) { + /* no local IP address set, yet. */ + struct netif *netif = ip_route(&(pcb->remote_ip)); + if (netif == NULL) { + /* Don't even try to send a SYN packet if we have no route + since that will fail. */ + return ERR_RTE; + } + /* Use the netif's IP address as local address. */ + ip_addr_copy(pcb->local_ip, netif->ip_addr); + } + + old_local_port = pcb->local_port; + if (pcb->local_port == 0) { + pcb->local_port = tcp_new_port(); + + } +#if SO_REUSE + if ((pcb->so_options & SOF_REUSEADDR) != 0) { + /* Since SOF_REUSEADDR allows reusing a local address, we have to make sure + now that the 5-tuple is unique. */ + struct tcp_pcb *cpcb; + int i; + /* Don't check listen- and bound-PCBs, check active- and TIME-WAIT PCBs. */ + for (i = 2; i < NUM_TCP_PCB_LISTS; i++) { + for(cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) { + if ((cpcb->local_port == pcb->local_port) && + (cpcb->remote_port == port) && + ip_addr_cmp(&cpcb->local_ip, &pcb->local_ip) && + ip_addr_cmp(&cpcb->remote_ip, ipaddr)) { + /* linux returns EISCONN here, but ERR_USE should be OK for us */ + return ERR_USE; + } + } + } + } +#endif /* SO_REUSE */ + iss = tcp_next_iss();//��ʼ����� + pcb->rcv_nxt = 0;//���÷��ʹ��ڵĸ����ֶ� + pcb->snd_nxt = iss; + pcb->lastack = iss - 1; + pcb->snd_lbb = iss - 1; + pcb->rcv_wnd = TCP_WND;//����Ĭ�Ͻ��մ��ڸ����ֶ�ֵ + pcb->rcv_ann_wnd = TCP_WND; + pcb->rcv_ann_right_edge = pcb->rcv_nxt; + pcb->snd_wnd = TCP_WND; + /* As initial send MSS, we use TCP_MSS but limit it to 536. + The send MSS is updated when an MSS option is received. */ + pcb->mss = (TCP_MSS > 536) ? 536 : TCP_MSS;//��ʼ������Ķδ�С +#if TCP_CALCULATE_EFF_SEND_MSS + pcb->mss = tcp_eff_send_mss(pcb->mss, ipaddr); +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + pcb->cwnd = 1;//��ʼ������� + pcb->ssthresh = pcb->mss * 10; +#if LWIP_CALLBACK_API + pcb->connected = connected;//ע��connected�ص����� +#else /* LWIP_CALLBACK_API */ + LWIP_UNUSED_ARG(connected); +#endif /* LWIP_CALLBACK_API */ + + /* Send a SYN together with the MSS option. */ + ret = tcp_enqueue_flags(pcb, TCP_SYN); + if (ret == ERR_OK) { + /* SYN segment was enqueued, changed the pcbs state now ���ƿ�����ΪSYN_SENT ״̬*/ + pcb->state = SYN_SENT; + if (old_local_port != 0) { + TCP_RMV(&tcp_bound_pcbs, pcb); + } + TCP_REG(&tcp_active_pcbs, pcb); + snmp_inc_tcpactiveopens(); + + tcp_output(pcb);//�����ƿ������ӵı��ķ��ͳ�ȥ + } + return ret; +} + +/** + * Called every 500 ms and implements the retransmission timer and the timer that + * removes PCBs that have been in TIME-WAIT for enough time. It also increments + * various timers such as the inactivity timer in each PCB. + * + * Automatically called from tcp_tmr(). + */ +void +tcp_slowtmr(void) +{ + struct tcp_pcb *pcb, *prev; + u16_t eff_wnd; + u8_t pcb_remove; /* flag if a PCB should be removed */ + u8_t pcb_reset; /* flag if a RST should be sent when removing */ + err_t err; + + err = ERR_OK; + + ++tcp_ticks; + + /* Steps through all of the active PCBs. */ + prev = NULL; + pcb = tcp_active_pcbs; + if (pcb == NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: no active pcbs\n")); + } + while (pcb != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: processing active pcb\n")); + LWIP_ASSERT("tcp_slowtmr: active pcb->state != CLOSED\n", pcb->state != CLOSED); + LWIP_ASSERT("tcp_slowtmr: active pcb->state != LISTEN\n", pcb->state != LISTEN); + LWIP_ASSERT("tcp_slowtmr: active pcb->state != TIME-WAIT\n", pcb->state != TIME_WAIT); + + pcb_remove = 0; + pcb_reset = 0; + + if (pcb->state == SYN_SENT && pcb->nrtx == TCP_SYNMAXRTX) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max SYN retries reached\n")); + } + else if (pcb->nrtx == TCP_MAXRTX) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max DATA retries reached\n")); + } else { + if (pcb->persist_backoff > 0) { + /* If snd_wnd is zero, use persist timer to send 1 byte probes + * instead of using the standard retransmission mechanism. */ + pcb->persist_cnt++; + if (pcb->persist_cnt >= system_get_data_of_array_8(tcp_persist_backoff, pcb->persist_backoff-1)) { + pcb->persist_cnt = 0; + if (pcb->persist_backoff < sizeof(tcp_persist_backoff)) { + pcb->persist_backoff++; + } + tcp_zero_window_probe(pcb); + } + } else { + /* Increase the retransmission timer if it is running */ + if(pcb->rtime >= 0) + ++pcb->rtime; + + if (pcb->unacked != NULL && pcb->rtime >= pcb->rto) { + /* Time for a retransmission. */ + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_slowtmr: rtime %"S16_F + " pcb->rto %"S16_F"\n", + pcb->rtime, pcb->rto)); + + /* Double retransmission time-out unless we are trying to + * connect to somebody (i.e., we are in SYN_SENT). */ + if (pcb->state != SYN_SENT) { + pcb->rto = ((pcb->sa >> 3) + pcb->sv) << system_get_data_of_array_8(tcp_backoff, pcb->nrtx); +// if (pcb->rto >= TCP_MAXRTO) +// pcb->rto >>= 1; + } + + /* Reset the retransmission timer. */ + pcb->rtime = 0; + + /* Reduce congestion window and ssthresh. */ + eff_wnd = LWIP_MIN(pcb->cwnd, pcb->snd_wnd); + pcb->ssthresh = eff_wnd >> 1; + if (pcb->ssthresh < (pcb->mss << 1)) { + pcb->ssthresh = (pcb->mss << 1); + } + pcb->cwnd = pcb->mss; + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: cwnd %"U16_F + " ssthresh %"U16_F"\n", + pcb->cwnd, pcb->ssthresh)); + + /* The following needs to be called AFTER cwnd is set to one + mss - STJ */ + tcp_rexmit_rto(pcb); + } + } + } + /* Check if this PCB has stayed too long in FIN-WAIT-2 */ + if (pcb->state == FIN_WAIT_2) { + if ((u32_t)(tcp_ticks - pcb->tmr) > + TCP_FIN_WAIT_TIMEOUT / TCP_SLOW_INTERVAL) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in FIN-WAIT-2\n")); + } + } + + /* Check if KEEPALIVE should be sent */ + if((pcb->so_options & SOF_KEEPALIVE) && + ((pcb->state == ESTABLISHED) || + (pcb->state == CLOSE_WAIT))) { +#if LWIP_TCP_KEEPALIVE + if((u32_t)(tcp_ticks - pcb->tmr) > + (pcb->keep_idle + (pcb->keep_cnt*pcb->keep_intvl)) + / TCP_SLOW_INTERVAL) +#else + if((u32_t)(tcp_ticks - pcb->tmr) > + (pcb->keep_idle + TCP_MAXIDLE) / TCP_SLOW_INTERVAL) +#endif /* LWIP_TCP_KEEPALIVE */ + { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: KEEPALIVE timeout. Aborting connection to %"U16_F".%"U16_F".%"U16_F".%"U16_F".\n", + ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip), + ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip))); + + ++pcb_remove; + ++pcb_reset; + } +#if LWIP_TCP_KEEPALIVE + else if((u32_t)(tcp_ticks - pcb->tmr) > + (pcb->keep_idle + pcb->keep_cnt_sent * pcb->keep_intvl) + / TCP_SLOW_INTERVAL) +#else + else if((u32_t)(tcp_ticks - pcb->tmr) > + (pcb->keep_idle + pcb->keep_cnt_sent * TCP_KEEPINTVL_DEFAULT) + / TCP_SLOW_INTERVAL) +#endif /* LWIP_TCP_KEEPALIVE */ + { + tcp_keepalive(pcb); + pcb->keep_cnt_sent++; + } + } + + /* If this PCB has queued out of sequence data, but has been + inactive for too long, will drop the data (it will eventually + be retransmitted). */ +#if TCP_QUEUE_OOSEQ + if (pcb->ooseq != NULL && + (u32_t)tcp_ticks - pcb->tmr >= pcb->rto * TCP_OOSEQ_TIMEOUT) { + tcp_segs_free(pcb->ooseq); + pcb->ooseq = NULL; + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: dropping OOSEQ queued data\n")); + } +#endif /* TCP_QUEUE_OOSEQ */ + + /* Check if this PCB has stayed too long in SYN-RCVD */ + if (pcb->state == SYN_RCVD) { + if ((u32_t)(tcp_ticks - pcb->tmr) > + TCP_SYN_RCVD_TIMEOUT / TCP_SLOW_INTERVAL) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in SYN-RCVD\n")); + } + } + + /* Check if this PCB has stayed too long in LAST-ACK */ + if (pcb->state == LAST_ACK) { + if ((u32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in LAST-ACK\n")); + } + } + + /* If the PCB should be removed, do it. */ + if (pcb_remove) { + struct tcp_pcb *pcb2; + tcp_pcb_purge(pcb); + /* Remove PCB from tcp_active_pcbs list. */ + if (prev != NULL) { + LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_active_pcbs", pcb != tcp_active_pcbs); + prev->next = pcb->next; + } else { + /* This PCB was the first. */ + LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_active_pcbs", tcp_active_pcbs == pcb); + tcp_active_pcbs = pcb->next; + } + + if (pcb_reset) { + tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip, + pcb->local_port, pcb->remote_port); + } + + TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_ABRT); + pcb2 = pcb; + pcb = pcb->next; + memp_free(MEMP_TCP_PCB, pcb2); + } else { + /* get the 'next' element now and work with 'prev' below (in case of abort) */ + prev = pcb; + pcb = pcb->next; + + /* We check if we should poll the connection. */ + ++prev->polltmr; + if (prev->polltmr >= prev->pollinterval) { + prev->polltmr = 0; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: polling application\n")); + TCP_EVENT_POLL(prev, err); + /* if err == ERR_ABRT, 'prev' is already deallocated */ + if (err == ERR_OK) { + tcp_output(prev); + } + } + } + } + + + /* Steps through all of the TIME-WAIT PCBs. */ + prev = NULL; + pcb = tcp_tw_pcbs; + while (pcb != NULL) { + LWIP_ASSERT("tcp_slowtmr: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT); + pcb_remove = 0; + + /* Check if this PCB has stayed long enough in TIME-WAIT */ + if ((u32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) { + ++pcb_remove; + } + + + + /* If the PCB should be removed, do it. */ + if (pcb_remove) { + struct tcp_pcb *pcb2; + tcp_pcb_purge(pcb); + /* Remove PCB from tcp_tw_pcbs list. */ + if (prev != NULL) { + LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_tw_pcbs", pcb != tcp_tw_pcbs); + prev->next = pcb->next; + } else { + /* This PCB was the first. */ + LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_tw_pcbs", tcp_tw_pcbs == pcb); + tcp_tw_pcbs = pcb->next; + } + pcb2 = pcb; + pcb = pcb->next; + memp_free(MEMP_TCP_PCB, pcb2); + } else { + prev = pcb; + pcb = pcb->next; + } + } +} + +/** + * Is called every TCP_FAST_INTERVAL (250 ms) and process data previously + * "refused" by upper layer (application) and sends delayed ACKs. + * + * Automatically called from tcp_tmr(). + */ +void +tcp_fasttmr(void) +{ + struct tcp_pcb *pcb = tcp_active_pcbs; + + while(pcb != NULL) { + struct tcp_pcb *next = pcb->next; + /* If there is data which was previously "refused" by upper layer */ + if (pcb->refused_data != NULL) { + /* Notify again application with data previously received. */ + err_t err; + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_fasttmr: notify kept packet\n")); + TCP_EVENT_RECV(pcb, pcb->refused_data, ERR_OK, err); + if (err == ERR_OK) { + pcb->refused_data = NULL; + } else if (err == ERR_ABRT) { + /* if err == ERR_ABRT, 'pcb' is already deallocated */ + pcb = NULL; + } + } + + /* send delayed ACKs */ + if (pcb && (pcb->flags & TF_ACK_DELAY)) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_fasttmr: delayed ACK\n")); + tcp_ack_now(pcb); + tcp_output(pcb); + pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); + } + + pcb = next; + } +} + +/** + * Deallocates a list of TCP segments (tcp_seg structures). + * + * @param seg tcp_seg list of TCP segments to free + */ +void +tcp_segs_free(struct tcp_seg *seg) +{ + while (seg != NULL) { + struct tcp_seg *next = seg->next; + tcp_seg_free(seg); + seg = next; + } +} + +/** + * Frees a TCP segment (tcp_seg structure). + * + * @param seg single tcp_seg to free + */ +void +tcp_seg_free(struct tcp_seg *seg) +{ + if (seg != NULL) { + if (seg->p != NULL) { + pbuf_free(seg->p); +#if TCP_DEBUG + seg->p = NULL; +#endif /* TCP_DEBUG */ + } + memp_free(MEMP_TCP_SEG, seg); + } +} + +/** + * Sets the priority of a connection. + * + * @param pcb the tcp_pcb to manipulate + * @param prio new priority + */ +void +tcp_setprio(struct tcp_pcb *pcb, u8_t prio) +{ + pcb->prio = prio; +} + +#if TCP_QUEUE_OOSEQ +/** + * Returns a copy of the given TCP segment. + * The pbuf and data are not copied, only the pointers + * + * @param seg the old tcp_seg + * @return a copy of seg + */ +struct tcp_seg * +tcp_seg_copy(struct tcp_seg *seg) +{ + struct tcp_seg *cseg; + + cseg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG); + if (cseg == NULL) { + return NULL; + } + SMEMCPY((u8_t *)cseg, (const u8_t *)seg, sizeof(struct tcp_seg)); + pbuf_ref(cseg->p); + return cseg; +} +#endif /* TCP_QUEUE_OOSEQ */ + +#if LWIP_CALLBACK_API +/** + * Default receive callback that is called if the user didn't register + * a recv callback for the pcb. + */ +err_t +tcp_recv_null(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) +{ + LWIP_UNUSED_ARG(arg); + if (p != NULL) { + tcp_recved(pcb, p->tot_len); + pbuf_free(p); + } else if (err == ERR_OK) { + return tcp_close(pcb); + } + return ERR_OK; +} +#endif /* LWIP_CALLBACK_API */ + +/** + * Kills the oldest active connection that has lower priority than prio. + * + * @param prio minimum priority + */ +static void ICACHE_FLASH_ATTR +tcp_kill_prio(u8_t prio) +{ + struct tcp_pcb *pcb, *inactive; + u32_t inactivity; + u8_t mprio; + + + mprio = TCP_PRIO_MAX; + + /* We kill the oldest active connection that has lower priority than prio. */ + inactivity = 0; + inactive = NULL; + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + if (pcb->prio <= prio && + pcb->prio <= mprio && + (u32_t)(tcp_ticks - pcb->tmr) >= inactivity) { + inactivity = tcp_ticks - pcb->tmr; + inactive = pcb; + mprio = pcb->prio; + } + } + if (inactive != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_prio: killing oldest PCB %p (%"S32_F")\n", + (void *)inactive, inactivity)); + tcp_abort(inactive); + } +} + +/** + * Kills the oldest connection that is in TIME_WAIT state. + * Called from tcp_alloc() if no more connections are available. + */ +static void ICACHE_FLASH_ATTR +tcp_kill_timewait(void) +{ + struct tcp_pcb *pcb, *inactive; + u32_t inactivity; + + inactivity = 0; + inactive = NULL; + /* Go through the list of TIME_WAIT pcbs and get the oldest pcb. */ + for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + if ((u32_t)(tcp_ticks - pcb->tmr) >= inactivity) { + inactivity = tcp_ticks - pcb->tmr; + inactive = pcb; + } + } + if (inactive != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_timewait: killing oldest TIME-WAIT PCB %p (%"S32_F")\n", + (void *)inactive, inactivity)); + tcp_abort(inactive); + } +} + +/** + * Allocate a new tcp_pcb structure. + *����һ��TCP���ƿ�ṹ������ʼ������ֶ� + * @param prio priority for the new pcb �¿��ƿ�����ȼ� + * @return a new tcp_pcb that initially is in state CLOSED ָ���¿��ƿ��ָ�� + */ +struct tcp_pcb * +tcp_alloc(u8_t prio) +{ + struct tcp_pcb *pcb; + u32_t iss; + + pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);//�����ڴ�ؿռ� + if (pcb == NULL) { + //os_printf("tcp_pcb memory is fail\n"); + /* Try killing oldest connection in TIME-WAIT. */ + LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing off oldest TIME-WAIT connection\n")); + tcp_kill_timewait(); + /* Try to allocate a tcp_pcb again. */ + pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB); + if (pcb == NULL) { + /* Try killing active connections with lower priority than the new one. */ + LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing connection with prio lower than %d\n", prio)); + tcp_kill_prio(prio); + /* Try to allocate a tcp_pcb again. */ + pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB); + if (pcb != NULL) { + /* adjust err stats: memp_malloc failed twice before */ + MEMP_STATS_DEC(err, MEMP_TCP_PCB); + } + } + if (pcb != NULL) { + /* adjust err stats: timewait PCB was freed above */ + MEMP_STATS_DEC(err, MEMP_TCP_PCB); + } + } + if (pcb != NULL) { + os_memset(pcb, 0, sizeof(struct tcp_pcb)); //��0 + pcb->prio = prio; //�������ȼ� + pcb->snd_buf = TCP_SND_BUF; //��ʹ�õķ��ͻ������С + pcb->snd_queuelen = 0; //��������ռ�õ�pbuf���� + pcb->rcv_wnd = TCP_WND; //���մ��� + pcb->rcv_ann_wnd = TCP_WND; //ͨ����մ��� + pcb->tos = 0; //�������� + pcb->ttl = TCP_TTL; //ttl�ֶ� + /* As initial send MSS, we use TCP_MSS but limit it to 536. + The send MSS is updated when an MSS option is received. */ + pcb->mss = (TCP_MSS > 536) ? 536 : TCP_MSS; //��ʼ������Ķ� + pcb->rto = 1000 / TCP_SLOW_INTERVAL; //��ʼ����ʱʱ�� + pcb->sa = 0; //��ʼ����RTT��صIJ��� + pcb->sv = 1000 / TCP_SLOW_INTERVAL; + pcb->rtime = -1; + pcb->cwnd = 1; //��ʼ������� + iss = tcp_next_iss(); //��ó�ʼ���к� + pcb->snd_wl2 = iss; //��ʼ�����ʹ��ڸ����ֶ� + pcb->snd_nxt = iss; + pcb->lastack = iss; + pcb->snd_lbb = iss; + pcb->tmr = tcp_ticks; //��¼���ƿ鴴��ϵͳʱ�� + + pcb->polltmr = 0; //����������¼���ʱ�� + +#if LWIP_CALLBACK_API + pcb->recv = tcp_recv_null; //ע�������ݵ�Ĭ���ϲ㺯�� +#endif /* LWIP_CALLBACK_API */ + + /* Init KEEPALIVE timer */ + pcb->keep_idle = TCP_KEEPIDLE_DEFAULT; + +#if LWIP_TCP_KEEPALIVE + pcb->keep_intvl = TCP_KEEPINTVL_DEFAULT; + pcb->keep_cnt = TCP_KEEPCNT_DEFAULT; +#endif /* LWIP_TCP_KEEPALIVE */ + + pcb->keep_cnt_sent = 0; //���ķ��ʹ��� + } + return pcb; +} + +/** + * Creates a new TCP protocol control block but doesn't place it on + * any of the TCP PCB lists. + * The pcb is not put on any list until binding using tcp_bind(). + * + * @internal: Maybe there should be a idle TCP PCB list where these + * PCBs are put on. Port reservation using tcp_bind() is implemented but + * allocated pcbs that are not bound can't be killed automatically if wanting + * to allocate a pcb with higher prio (@see tcp_kill_prio()) + * + * @return a new tcp_pcb that initially is in state CLOSED + */ +struct tcp_pcb * +tcp_new(void) +{ + return tcp_alloc(TCP_PRIO_NORMAL); +} + +/** + * Used to specify the argument that should be passed callback + * functions. + *����ƿ��callback_arg�ֶ�ע���û���ݣ���tcp_recv�Ⱥ���ص�ʱ�� +* ���ֶν���Ϊ����ݸ�������� + * @param pcb tcp_pcb to set the callback argument + * @param arg void pointer argument to pass to callback functions + */ +void +tcp_arg(struct tcp_pcb *pcb, void *arg) +{ + pcb->callback_arg = arg; +} +#if LWIP_CALLBACK_API + +/** + * Used to specify the function that should be called when a TCP + * connection receives data. + *����ƿ��recv�ֶ�ע�ắ���յ����ʱ�ص� + * @param pcb tcp_pcb to set the recv callback + * @param recv callback function to call for this pcb when data is received + */ +void +tcp_recv(struct tcp_pcb *pcb, tcp_recv_fn recv) +{ + pcb->recv = recv; +} + +/** + * Used to specify the function that should be called when TCP data + * has been successfully delivered to the remote host. + *����ƿ�send �ֶ�ע�ắ����ݷ��ͳɹ���ص� + * @param pcb tcp_pcb to set the sent callback + * @param sent callback function to call for this pcb when data is successfully sent + */ +void +tcp_sent(struct tcp_pcb *pcb, tcp_sent_fn sent) +{ + pcb->sent = sent; +} + +/** + * Used to specify the function that should be called when a fatal error + * has occured on the connection. + *����ƿ�err �ֶ�ע�ắ�����������ص� + * @param pcb tcp_pcb to set the err callback + * @param err callback function to call for this pcb when a fatal error + * has occured on the connection + */ +void +tcp_err(struct tcp_pcb *pcb, tcp_err_fn err) +{ + pcb->errf = err; +} + +/** + * Used for specifying the function that should be called when a + * LISTENing connection has been connected to another host. + *����ƿ��accept�ֶ�ע�ắ����������ʱ�ص� + * @param pcb tcp_pcb to set the accept callback + * @param accept callback function to call for this pcb when LISTENing + * connection has been connected to another host + */ +void +tcp_accept(struct tcp_pcb *pcb, tcp_accept_fn accept) +{ + pcb->accept = accept; +} +#endif /* LWIP_CALLBACK_API */ + + +/** + * Used to specify the function that should be called periodically + * from TCP. The interval is specified in terms of the TCP coarse + * timer interval, which is called twice a second. + *����ƿ��POLL�ֶ�ע�ắ��ú��������Ա����� + */ +void +tcp_poll(struct tcp_pcb *pcb, tcp_poll_fn poll, u8_t interval) +{ +#if LWIP_CALLBACK_API + pcb->poll = poll; +#else /* LWIP_CALLBACK_API */ + LWIP_UNUSED_ARG(poll); +#endif /* LWIP_CALLBACK_API */ + pcb->pollinterval = interval; +} + +/** + * Purges a TCP PCB. Removes any buffered data and frees the buffer memory + * (pcb->ooseq, pcb->unsent and pcb->unacked are freed). + * + * @param pcb tcp_pcb to purge. The pcb itself is not deallocated! + */ +void +tcp_pcb_purge(struct tcp_pcb *pcb) +{ + if (pcb->state != CLOSED && + pcb->state != TIME_WAIT && + pcb->state != LISTEN) { + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge\n")); + +#if TCP_LISTEN_BACKLOG + if (pcb->state == SYN_RCVD) { + /* Need to find the corresponding listen_pcb and decrease its accepts_pending */ + struct tcp_pcb_listen *lpcb; + LWIP_ASSERT("tcp_pcb_purge: pcb->state == SYN_RCVD but tcp_listen_pcbs is NULL", + tcp_listen_pcbs.listen_pcbs != NULL); + for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { + if ((lpcb->local_port == pcb->local_port) && + (ip_addr_isany(&lpcb->local_ip) || + ip_addr_cmp(&pcb->local_ip, &lpcb->local_ip))) { + /* port and address of the listen pcb match the timed-out pcb */ + LWIP_ASSERT("tcp_pcb_purge: listen pcb does not have accepts pending", + lpcb->accepts_pending > 0); + lpcb->accepts_pending--; + break; + } + } + } +#endif /* TCP_LISTEN_BACKLOG */ + + + if (pcb->refused_data != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->refused_data\n")); + pbuf_free(pcb->refused_data); + pcb->refused_data = NULL; + } + if (pcb->unsent != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: not all data sent\n")); + } + if (pcb->unacked != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->unacked\n")); + } +#if TCP_QUEUE_OOSEQ + if (pcb->ooseq != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->ooseq\n")); + } + tcp_segs_free(pcb->ooseq); + pcb->ooseq = NULL; +#endif /* TCP_QUEUE_OOSEQ */ + + /* Stop the retransmission timer as it will expect data on unacked + queue if it fires */ + pcb->rtime = -1; + + tcp_segs_free(pcb->unsent); + tcp_segs_free(pcb->unacked); + pcb->unacked = pcb->unsent = NULL; +#if TCP_OVERSIZE + pcb->unsent_oversize = 0; +#endif /* TCP_OVERSIZE */ + } +} + +/** + * Purges the PCB and removes it from a PCB list. Any delayed ACKs are sent first. + * + * @param pcblist PCB list to purge. + * @param pcb tcp_pcb to purge. The pcb itself is NOT deallocated! + */ +void +tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb) +{ + TCP_RMV(pcblist, pcb); + + tcp_pcb_purge(pcb); + + /* if there is an outstanding delayed ACKs, send it */ + if (pcb->state != TIME_WAIT && + pcb->state != LISTEN && + pcb->flags & TF_ACK_DELAY) { + pcb->flags |= TF_ACK_NOW; + tcp_output(pcb); + } + + if (pcb->state != LISTEN) { + LWIP_ASSERT("unsent segments leaking", pcb->unsent == NULL); + LWIP_ASSERT("unacked segments leaking", pcb->unacked == NULL); +#if TCP_QUEUE_OOSEQ + LWIP_ASSERT("ooseq segments leaking", pcb->ooseq == NULL); +#endif /* TCP_QUEUE_OOSEQ */ + } + + pcb->state = CLOSED; + + LWIP_ASSERT("tcp_pcb_remove: tcp_pcbs_sane()", tcp_pcbs_sane()); +} + +/** + * Calculates a new initial sequence number for new connections. + * + * @return u32_t pseudo random sequence number + */ +u32_t +tcp_next_iss(void) +{ + static u32_t iss = 6510; + + again: + iss += tcp_ticks; /* XXX */ + if (iss == 0) + goto again; + + return iss; +} + +#if TCP_CALCULATE_EFF_SEND_MSS +/** + * Calcluates the effective send mss that can be used for a specific IP address + * by using ip_route to determin the netif used to send to the address and + * calculating the minimum of TCP_MSS and that netif's mtu (if set). + */ +u16_t +tcp_eff_send_mss(u16_t sendmss, ip_addr_t *addr) +{ + u16_t mss_s; + struct netif *outif; + + outif = ip_route(addr); + if ((outif != NULL) && (outif->mtu != 0)) { + mss_s = outif->mtu - IP_HLEN - TCP_HLEN; + /* RFC 1122, chap 4.2.2.6: + * Eff.snd.MSS = min(SendMSS+20, MMS_S) - TCPhdrsize - IPoptionsize + * We correct for TCP options in tcp_write(), and don't support IP options. + */ + sendmss = LWIP_MIN(sendmss, mss_s); + } + return sendmss; +} +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + +#if TCP_DEBUG +const char* +tcp_debug_state_str(enum tcp_state s) +{ + system_get_string_from_flash(tcp_state_str_rodata[s], tcp_state_str, 12); + + return tcp_state_str; +} +#endif + +#if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG +/** + * Print a tcp header for debugging purposes. + * + * @param tcphdr pointer to a struct tcp_hdr + */ +void +tcp_debug_print(struct tcp_hdr *tcphdr) +{ + LWIP_DEBUGF(TCP_DEBUG, ("TCP header:\n")); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| %5"U16_F" | %5"U16_F" | (src port, dest port)\n", + ntohs(tcphdr->src), ntohs(tcphdr->dest))); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| %010"U32_F" | (seq no)\n", + ntohl(tcphdr->seqno))); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| %010"U32_F" | (ack no)\n", + ntohl(tcphdr->ackno))); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| %2"U16_F" | |%"U16_F"%"U16_F"%"U16_F"%"U16_F"%"U16_F"%"U16_F"| %5"U16_F" | (hdrlen, flags (", + TCPH_HDRLEN(tcphdr), + TCPH_FLAGS(tcphdr) >> 5 & 1, + TCPH_FLAGS(tcphdr) >> 4 & 1, + TCPH_FLAGS(tcphdr) >> 3 & 1, + TCPH_FLAGS(tcphdr) >> 2 & 1, + TCPH_FLAGS(tcphdr) >> 1 & 1, + TCPH_FLAGS(tcphdr) & 1, + ntohs(tcphdr->wnd))); + tcp_debug_print_flags(TCPH_FLAGS(tcphdr)); + LWIP_DEBUGF(TCP_DEBUG, ("), win)\n")); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| 0x%04"X16_F" | %5"U16_F" | (chksum, urgp)\n", + ntohs(tcphdr->chksum), ntohs(tcphdr->urgp))); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); +} + +/** + * Print a tcp state for debugging purposes. + * + * @param s enum tcp_state to print + */ +void +tcp_debug_print_state(enum tcp_state s) +{ + LWIP_DEBUGF(TCP_DEBUG, ("State: %s\n", tcp_state_str[s])); +} + +/** + * Print tcp flags for debugging purposes. + * + * @param flags tcp flags, all active flags are printed + */ +void +tcp_debug_print_flags(u8_t flags) +{ + if (flags & TCP_FIN) { + LWIP_DEBUGF(TCP_DEBUG, ("FIN ")); + } + if (flags & TCP_SYN) { + LWIP_DEBUGF(TCP_DEBUG, ("SYN ")); + } + if (flags & TCP_RST) { + LWIP_DEBUGF(TCP_DEBUG, ("RST ")); + } + if (flags & TCP_PSH) { + LWIP_DEBUGF(TCP_DEBUG, ("PSH ")); + } + if (flags & TCP_ACK) { + LWIP_DEBUGF(TCP_DEBUG, ("ACK ")); + } + if (flags & TCP_URG) { + LWIP_DEBUGF(TCP_DEBUG, ("URG ")); + } + if (flags & TCP_ECE) { + LWIP_DEBUGF(TCP_DEBUG, ("ECE ")); + } + if (flags & TCP_CWR) { + LWIP_DEBUGF(TCP_DEBUG, ("CWR ")); + } + LWIP_DEBUGF(TCP_DEBUG, ("\n")); +} + +/** + * Print all tcp_pcbs in every list for debugging purposes. + */ +void +tcp_debug_print_pcbs(void) +{ + struct tcp_pcb *pcb; + LWIP_DEBUGF(TCP_DEBUG, ("Active PCB states:\n")); + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ", + pcb->local_port, pcb->remote_port, + pcb->snd_nxt, pcb->rcv_nxt)); + tcp_debug_print_state(pcb->state); + } + LWIP_DEBUGF(TCP_DEBUG, ("Listen PCB states:\n")); + for(pcb = (struct tcp_pcb *)tcp_listen_pcbs.pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ", + pcb->local_port, pcb->remote_port, + pcb->snd_nxt, pcb->rcv_nxt)); + tcp_debug_print_state(pcb->state); + } + LWIP_DEBUGF(TCP_DEBUG, ("TIME-WAIT PCB states:\n")); + for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ", + pcb->local_port, pcb->remote_port, + pcb->snd_nxt, pcb->rcv_nxt)); + tcp_debug_print_state(pcb->state); + } +} + +/** + * Check state consistency of the tcp_pcb lists. + */ +s16_t +tcp_pcbs_sane(void) +{ + struct tcp_pcb *pcb; + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != CLOSED", pcb->state != CLOSED); + LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != LISTEN", pcb->state != LISTEN); + LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT); + } + for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_ASSERT("tcp_pcbs_sane: tw pcb->state == TIME-WAIT", pcb->state == TIME_WAIT); + } + return 1; +} +#endif /* TCP_DEBUG */ + +#endif /* LWIP_TCP */ diff --git a/variants/lwip/lwip/core/tcp_in.c b/variants/lwip/lwip/core/tcp_in.c new file mode 100755 index 000000000..be74a28ac --- /dev/null +++ b/variants/lwip/lwip/core/tcp_in.c @@ -0,0 +1,1637 @@ +/** + * @file + * Transmission Control Protocol, incoming traffic + * + * The input processing functions of the TCP layer. + * + * These functions are generally called in the order (ip_input() ->) + * tcp_input() -> * tcp_process() -> tcp_receive() (-> application). + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/tcp_impl.h" +#include "lwip/def.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/inet_chksum.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "arch/perf.h" + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +/* These variables are global to all functions involved in the input + processing of TCP segments. They are set by the tcp_input() + function. */ +static struct tcp_seg inseg; //tcp_seg�ṹ����������ı��Ķ� +static struct tcp_hdr *tcphdr; //������TCP�ײ� +static struct ip_hdr *iphdr; //IP��ݰ��ײ� +static u32_t seqno, ackno; //TCP�ײ�������ֶ���ȷ�Ϻ��ֶ� +static u8_t flags; //�ײ���־�ֶ� +static u16_t tcplen; //TCP���ij��� + +static u8_t recv_flags; //��ǰ���Ĵ����� +static struct pbuf *recv_data; //�������pbuf + +struct tcp_pcb *tcp_input_pcb; //��ǰ���Ŀ��ƿ� + +/* Forward declarations. */ +static err_t tcp_process(struct tcp_pcb *pcb)ICACHE_FLASH_ATTR; +static void tcp_receive(struct tcp_pcb *pcb)ICACHE_FLASH_ATTR; +static void tcp_parseopt(struct tcp_pcb *pcb)ICACHE_FLASH_ATTR; + +static err_t tcp_listen_input(struct tcp_pcb_listen *pcb)ICACHE_FLASH_ATTR; +static err_t tcp_timewait_input(struct tcp_pcb *pcb)ICACHE_FLASH_ATTR; + +/** + * The initial input processing of TCP. It verifies the TCP header, demultiplexes + * the segment between the PCBs and passes it on to tcp_process(), which implements + * the TCP finite state machine. This function is called by the IP layer (in + * ip_input()). + * + * @param p received TCP segment to process (p->payload pointing to the IP header) + * @param inp network interface on which this segment was received + */ + /** + * TCP��ʼ�����봦�?��֤��TCPͷ���������IP����� + + * @����p:������յ�TCP��(ָ��IPͷ�ĸ���) + * @����inp:���նε�����ӿ� + */ +void +tcp_input(struct pbuf *p, struct netif *inp) +{ + struct tcp_pcb *pcb, *prev; + struct tcp_pcb_listen *lpcb; +#if SO_REUSE + struct tcp_pcb *lpcb_prev = NULL; + struct tcp_pcb_listen *lpcb_any = NULL; +#endif /* SO_REUSE */ + u8_t hdrlen; + err_t err; + + PERF_START; + + TCP_STATS_INC(tcp.recv); //״̬��1 + snmp_inc_tcpinsegs(); //tcp����μ�1 + + iphdr = (struct ip_hdr *)p->payload;// pointer to the actual data in the buffer + /* + *��ͷ����(IHL)��4�IPЭ���ͷ�ij��ȣ�ָ��IPv4Э���ͷ���ȵ��ֽ������ٸ�32� + *����IPv4�İ�ͷ���ܰ�ɱ������Ŀ�ѡ ���������ֶο�������ȷ��IPv4��ݱ�����ݲ��ֵ�ƫ������ + *IPv4��ͷ����С������20���ֽڣ����IHL����ֶε���Сֵ��ʮ���Ʊ�ʾ����5 (5x4 = 20�ֽ�)�� + *����˵�����ʾ�İ�ͷ�����ֽ�����4�ֽڵı��� + */ + tcphdr = (struct tcp_hdr *)((u8_t *)p->payload + IPH_HL(iphdr) * 4); + +#if TCP_INPUT_DEBUG + tcp_debug_print(tcphdr); +#endif + + /* remove header from payload */ + if (pbuf_header(p, -((s16_t)(IPH_HL(iphdr) * 4))) || (p->tot_len < sizeof(struct tcp_hdr))) { + /* drop short packets */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet (%"U16_F" bytes) discarded\n", p->tot_len)); + TCP_STATS_INC(tcp.lenerr);//���󳤶ȼ��� + TCP_STATS_INC(tcp.drop);//��ֹ���� + snmp_inc_tcpinerrs(); + pbuf_free(p);//�ͷ�buffer + return; + } + + /* Don't even process incoming broadcasts/multicasts. */ + if (ip_addr_isbroadcast(¤t_iphdr_dest, inp) || + ip_addr_ismulticast(¤t_iphdr_dest)) { + TCP_STATS_INC(tcp.proterr);//�������� + TCP_STATS_INC(tcp.drop); + snmp_inc_tcpinerrs(); + pbuf_free(p); + return; + } + +#if CHECKSUM_CHECK_TCP + /* Verify TCP checksum. */ + if (inet_chksum_pseudo(p, ip_current_src_addr(), ip_current_dest_addr(), + IP_PROTO_TCP, p->tot_len) != 0) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packet discarded due to failing checksum 0x%04"X16_F"\n", + inet_chksum_pseudo(p, ip_current_src_addr(), ip_current_dest_addr(), + IP_PROTO_TCP, p->tot_len))); +#if TCP_DEBUG + tcp_debug_print(tcphdr); +#endif /* TCP_DEBUG */ + TCP_STATS_INC(tcp.chkerr);//�������� + TCP_STATS_INC(tcp.drop); + snmp_inc_tcpinerrs(); + pbuf_free(p); + return; + } +#endif + + /* Move the payload pointer in the pbuf so that it points to the + TCP data instead of the TCP header. */ + hdrlen = TCPH_HDRLEN(tcphdr);//����ͷ�ij��� + if(pbuf_header(p, -(hdrlen * 4))){//���TCPͷ������0Ϊ�ɹ������� + /* drop short packets */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet\n")); + TCP_STATS_INC(tcp.lenerr);//tcp���ȴ������ + TCP_STATS_INC(tcp.drop); + snmp_inc_tcpinerrs(); + pbuf_free(p); + return; + } + + /* Convert fields in TCP header to host byte order. */ + tcphdr->src = ntohs(tcphdr->src); //ת��Դ��ַ + tcphdr->dest = ntohs(tcphdr->dest); //ת��Ŀ�ĵ�ַ + seqno = tcphdr->seqno = ntohl(tcphdr->seqno); //ת�����к� + ackno = tcphdr->ackno = ntohl(tcphdr->ackno); //ת��Ӧ��� + tcphdr->wnd = ntohs(tcphdr->wnd); //ת��tcp���� + + flags = TCPH_FLAGS(tcphdr);//�õ�tcp header�ı�־ + /* + *��־��3λ�����ֶΣ��� + * �����1λ + * ���ֶ�λ��1λ��ȡֵ��0��������ݱ��ֶΣ���1����ݱ����ֶܷΣ� + * �����1λ��ȡֵ��0����ݰ����û�а�1����ݰ�����и��İ� + */ + tcplen = p->tot_len + ((flags & (TCP_FIN | TCP_SYN)) ? 1 : 0);//TCP_FIN �� TCP_SYN ���1�������0 + + /* Demultiplex an incoming segment. First, we check if it is destined + for an active connection. ���ȣ�����Ƿ�һ��Ҫ����һ������*/ + //////////////////////////////////////////////////////////////////////////////////////// + prev = NULL; + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {//������б� + LWIP_ASSERT("tcp_input: active pcb->state != CLOSED", pcb->state != CLOSED); + LWIP_ASSERT("tcp_input: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT); + LWIP_ASSERT("tcp_input: active pcb->state != LISTEN", pcb->state != LISTEN); + if (pcb->remote_port == tcphdr->src && + pcb->local_port == tcphdr->dest && + ip_addr_cmp(&(pcb->remote_ip), ¤t_iphdr_src) && + ip_addr_cmp(&(pcb->local_ip), ¤t_iphdr_dest)) {//�����صĵ�ַ + + /* Move this PCB to the front of the list so that subsequent + lookups will be faster (we exploit locality in TCP segment + arrivals). */ + LWIP_ASSERT("tcp_input: pcb->next != pcb (before cache)", pcb->next != pcb); + if (prev != NULL) {//���ǰһ���ڵ㲻Ϊ�� + prev->next = pcb->next; + pcb->next = tcp_active_pcbs; + tcp_active_pcbs = pcb;//pcb������ǰ�� + } + LWIP_ASSERT("tcp_input: pcb->next != pcb (after cache)", pcb->next != pcb); + break; + } + prev = pcb;//prevָ��pcb + } + + if (pcb == NULL) { + /* If it did not go to an active connection, we check the connections + in the TIME-WAIT state. */ + for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {//����ȴ�״̬�µ�pcb + LWIP_ASSERT("tcp_input: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT); + if (pcb->remote_port == tcphdr->src && + pcb->local_port == tcphdr->dest && + ip_addr_cmp(&(pcb->remote_ip), ¤t_iphdr_src) && + ip_addr_cmp(&(pcb->local_ip), ¤t_iphdr_dest)) { + /* We don't really care enough to move this PCB to the front + of the list since we are not very likely to receive that + many segments for connections in TIME-WAIT. */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for TIME_WAITing connection.\n")); + tcp_timewait_input(pcb);//����tcp timewait �� + pbuf_free(p); + return; + } + } + + /* Finally, if we still did not get a match, we check all PCBs that + are LISTENing for incoming connections. */ + prev = NULL; + for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {//�������״̬�����е�pcb + if (lpcb->local_port == tcphdr->dest) { +#if SO_REUSE + if (ip_addr_cmp(&(lpcb->local_ip), ¤t_iphdr_dest)) { + /* found an exact match */ + break; + } else if(ip_addr_isany(&(lpcb->local_ip))) { + /* found an ANY-match */ + lpcb_any = lpcb; + lpcb_prev = prev; + } +#else /* SO_REUSE */ + if (ip_addr_cmp(&(lpcb->local_ip), ¤t_iphdr_dest) || + ip_addr_isany(&(lpcb->local_ip))) { + /* found a match */ + break; + } +#endif /* SO_REUSE */ + } + prev = (struct tcp_pcb *)lpcb; + } +#if SO_REUSE + /* first try specific local IP */ + if (lpcb == NULL) { + /* only pass to ANY if no specific local IP has been found */ + lpcb = lpcb_any; + prev = lpcb_prev; + } +#endif /* SO_REUSE */ + if (lpcb != NULL) { + /* Move this PCB to the front of the list so that subsequent + lookups will be faster (we exploit locality in TCP segment + arrivals). */ + if (prev != NULL) { + ((struct tcp_pcb_listen *)prev)->next = lpcb->next; + /* our successor is the remainder of the listening list */ + lpcb->next = tcp_listen_pcbs.listen_pcbs; + /* put this listening pcb at the head of the listening list */ + tcp_listen_pcbs.listen_pcbs = lpcb; + } + + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for LISTENing connection.\n")); + tcp_listen_input(lpcb);//����tcp������ݰ� + pbuf_free(p); + return; + } + } + +#if TCP_INPUT_DEBUG + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("+-+-+-+-+-+-+-+-+-+-+-+-+-+- tcp_input: flags ")); + tcp_debug_print_flags(TCPH_FLAGS(tcphdr)); + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n")); +#endif /* TCP_INPUT_DEBUG */ + + + if (pcb != NULL) { + /* The incoming segment belongs to a connection. */ +#if TCP_INPUT_DEBUG +#if TCP_DEBUG + tcp_debug_print_state(pcb->state); +#endif /* TCP_DEBUG */ +#endif /* TCP_INPUT_DEBUG */ + + /* Set up a tcp_seg structure. */ + inseg.next = NULL; + inseg.len = p->tot_len; + inseg.p = p; + inseg.tcphdr = tcphdr; + + recv_data = NULL; + recv_flags = 0; + + /* If there is data which was previously "refused" by upper layer */ + if (pcb->refused_data != NULL) { + /* Notify again application with data previously received. */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: notify kept packet\n")); + TCP_EVENT_RECV(pcb, pcb->refused_data, ERR_OK, err);//pcb������� + if (err == ERR_OK) { + pcb->refused_data = NULL; + } else if ((err == ERR_ABRT) || (tcplen > 0)) { + /* if err == ERR_ABRT, 'pcb' is already deallocated */ + /* Drop incoming packets because pcb is "full" (only if the incoming + segment contains data). */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: drop incoming packets, because pcb is \"full\"\n")); + TCP_STATS_INC(tcp.drop);//tcp������� + snmp_inc_tcpinerrs(); + pbuf_free(p); + return; + } + } + tcp_input_pcb = pcb;//��¼��ǰ���Ĵ���Ŀ��ƿ� + err = tcp_process(pcb);//���?�� + /* A return value of ERR_ABRT means that tcp_abort() was called + and that the pcb has been freed. If so, we don't do anything. */ + if (err != ERR_ABRT) { + if (recv_flags & TF_RESET) { + /* TF_RESET means that the connection was reset by the other + end. We then call the error callback to inform the + application that the connection is dead before we + deallocate the PCB. */ + TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_RST); + tcp_pcb_remove(&tcp_active_pcbs, pcb);//ɾ���pcb�б��е�pcb + memp_free(MEMP_TCP_PCB, pcb); + } else if (recv_flags & TF_CLOSED) { + /* The connection has been closed and we will deallocate the + PCB. */ + if (!(pcb->flags & TF_RXCLOSED)) { + /* Connection closed although the application has only shut down the + tx side: call the PCB's err callback and indicate the closure to + ensure the application doesn't continue using the PCB. */ + TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_CLSD); + } + tcp_pcb_remove(&tcp_active_pcbs, pcb); + memp_free(MEMP_TCP_PCB, pcb); + } else { + err = ERR_OK; + /* If the application has registered a "sent" function to be + called when new send buffer space is available, we call it + now. */ + if (pcb->acked > 0) { + TCP_EVENT_SENT(pcb, pcb->acked, err);//����ݱ�ȷ�ϣ��ص��û���send���� + if (err == ERR_ABRT) { + goto aborted; + } + } + + if (recv_data != NULL) {//����ݽ��յ� + LWIP_ASSERT("pcb->refused_data == NULL", pcb->refused_data == NULL); + if (pcb->flags & TF_RXCLOSED) { + /* received data although already closed -> abort (send RST) to + notify the remote host that not all data has been processed */ + pbuf_free(recv_data); + tcp_abort(pcb); + goto aborted; + } + + //PSH��־ PSH ����� + //��PSH=1ʱ��Ҫ���ͷ����Ϸ��͸÷ֶΣ� + //����շ�����Ľ����Ľ���Ӧ�ò㣬�������д��? + + if (flags & TCP_PSH) { + recv_data->flags |= PBUF_FLAG_PUSH;//���bufferӦ������������ + } + + /* Notify application that data has been received. */ + TCP_EVENT_RECV(pcb, recv_data, ERR_OK, err); + if (err == ERR_ABRT) { + goto aborted; + } + + /* If the upper layer can't receive this data, store it */ + if (err != ERR_OK) { + pcb->refused_data = recv_data; + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: keep incoming packet, because pcb is \"full\"\n")); + } + } + + /* If a FIN segment was received, we call the callback + function with a NULL buffer to indicate EOF. */ + if (recv_flags & TF_GOT_FIN) { + /* correct rcv_wnd as the application won't call tcp_recved() + for the FIN's seqno */ + if (pcb->rcv_wnd != TCP_WND) { + pcb->rcv_wnd++; + } + + TCP_EVENT_CLOSED(pcb, err); + if (err == ERR_ABRT) { + goto aborted; + } + } + + tcp_input_pcb = NULL;//���ȫ�ֱ��� + /* Try to send something out. */ + tcp_output(pcb);//����������� +#if TCP_INPUT_DEBUG +#if TCP_DEBUG + tcp_debug_print_state(pcb->state); +#endif /* TCP_DEBUG */ +#endif /* TCP_INPUT_DEBUG */ + } + } + /* Jump target if pcb has been aborted in a callback (by calling tcp_abort()). + Below this line, 'pcb' may not be dereferenced! */ +aborted: + tcp_input_pcb = NULL; + recv_data = NULL; + + /* give up our reference to inseg.p */ + if (inseg.p != NULL) + { + pbuf_free(inseg.p);//�ͷ�buffer + inseg.p = NULL; + } + + /*add processing queue segments that arrive out of order by LiuHan*/ +#if TCP_QUEUE_OOSEQ + extern char RxNodeNum(void); + if (RxNodeNum() < 2){ + extern void pbuf_free_ooseq_new(void* arg); +// os_printf("reclaim some memory from queued\n"); + pbuf_free_ooseq_new(NULL); + } +#endif + } else { + + /* If no matching PCB was found, send a TCP RST (reset) to the + sender. */ + LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_input: no PCB match found, resetting.\n")); + if (!(TCPH_FLAGS(tcphdr) & TCP_RST)) { + TCP_STATS_INC(tcp.proterr);//�������� + TCP_STATS_INC(tcp.drop);//tcp������� + tcp_rst(ackno, seqno + tcplen, + ip_current_dest_addr(), ip_current_src_addr(), + tcphdr->dest, tcphdr->src);//����TCP��λ + } + pbuf_free(p); + } + + LWIP_ASSERT("tcp_input: tcp_pcbs_sane()", tcp_pcbs_sane()); + PERF_STOP("tcp_input"); +} + +/** + * Called by tcp_input() when a segment arrives for a listening + * connection (from tcp_input()). + * + * @param pcb the tcp_pcb_listen for which a segment arrived + * @return ERR_OK if the segment was processed + * another err_t on error + * + * @note the return value is not (yet?) used in tcp_input() + * @note the segment which arrived is saved in global variables, therefore only the pcb + * involved is passed as a parameter to this function + */ + /* +*����LISTEN״̬�Ŀ��ƿ���øú��� +*ͨ���Ƿ�������������һ���˿ڲ�����ͻ���SYN�������� +* +*/ +static err_t +tcp_listen_input(struct tcp_pcb_listen *pcb) +{ + struct tcp_pcb *npcb; + struct tcp_pcb *pactive_pcb; + u8_t active_pcb_num = 0; + err_t rc; + + /* In the LISTEN state, we check for incoming SYN segments, + creates a new PCB, and responds with a SYN|ACK. */ + if (flags & TCP_ACK) { + /* For incoming segments with the ACK flag set, respond with a + RST. */ + LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_listen_input: ACK in LISTEN, sending reset\n")); + tcp_rst(ackno + 1, seqno + tcplen, + ip_current_dest_addr(), ip_current_src_addr(), + tcphdr->dest, tcphdr->src); + } else if (flags & TCP_SYN) {//�յ�SYN���� + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection request %"U16_F" -> %"U16_F".\n", tcphdr->src, tcphdr->dest)); +#if TCP_LISTEN_BACKLOG + if (pcb->accepts_pending >= pcb->backlog) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: listen backlog exceeded for port %"U16_F"\n", tcphdr->dest)); + return ERR_ABRT; + } +#endif /* TCP_LISTEN_BACKLOG */ + for(pactive_pcb = tcp_active_pcbs; pactive_pcb != NULL; pactive_pcb = pactive_pcb->next){ + if (pactive_pcb->state == ESTABLISHED){ + active_pcb_num ++; + } + } + if (active_pcb_num == MEMP_NUM_TCP_PCB){ + LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: exceed the number of active TCP connections\n")); + TCP_STATS_INC(tcp.memerr); + return ERR_MEM; + } + npcb = tcp_alloc(pcb->prio);//�������ƿ� + /* If a new PCB could not be created (probably due to lack of memory), + we don't do anything, but rely on the sender will retransmit the + SYN at a time when we have more memory available. */ + if (npcb == NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: could not allocate PCB\n")); + TCP_STATS_INC(tcp.memerr);//TCP�ڴ������� + return ERR_MEM; + } + +#if TCP_LISTEN_BACKLOG + pcb->accepts_pending++; +#endif /* TCP_LISTEN_BACKLOG */ + /* Set up the new PCB. */ + //���ƿ���������ص�4���ֶ� + ip_addr_copy(npcb->local_ip, current_iphdr_dest); + npcb->local_port = pcb->local_port; + ip_addr_copy(npcb->remote_ip, current_iphdr_src); + npcb->remote_port = tcphdr->src; + + //���ƿ��������ֶ� + npcb->state = SYN_RCVD;//��������״̬ + npcb->rcv_nxt = seqno + 1;//������һ������������ + npcb->rcv_ann_right_edge = npcb->rcv_nxt; + npcb->snd_wnd = tcphdr->wnd;//���÷��ʹ��� + npcb->ssthresh = npcb->snd_wnd; + npcb->snd_wl1 = seqno - 1;/* initialise to seqno-1 to force window update */ + npcb->callback_arg = pcb->callback_arg; +#if LWIP_CALLBACK_API + npcb->accept = pcb->accept; +#endif /* LWIP_CALLBACK_API */ + /* inherit socket options */ + npcb->so_options = pcb->so_options & SOF_INHERITED; + /* Register the new PCB so that we can begin receiving segments + for it. */ + TCP_REG(&tcp_active_pcbs, npcb); + + /* Parse any options in the SYN. */ + tcp_parseopt(npcb); +#if TCP_CALCULATE_EFF_SEND_MSS + npcb->mss = tcp_eff_send_mss(npcb->mss, &(npcb->remote_ip)); +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + + snmp_inc_tcppassiveopens(); + + /* Send a SYN|ACK together with the MSS option. */ + rc = tcp_enqueue_flags(npcb, TCP_SYN | TCP_ACK); + if (rc != ERR_OK) {//��������ͷ��¿��ƿ� + tcp_abandon(npcb, 0); + return rc; + } + return tcp_output(npcb);//���ͱ��� + } + return ERR_OK; +} + +/** + * Called by tcp_input() when a segment arrives for a connection in + * TIME_WAIT. + * + * @param pcb the tcp_pcb for which a segment arrived + * + * @note the segment which arrived is saved in global variables, therefore only the pcb + * involved is passed as a parameter to this function + */ + /* +*����TIME_WAIT״̬�Ŀ��ƿ���øú������յ��ı��ĶΣ� +*��״̬�£��ر����ӵ����ֹ���Ѿ��������ڵȴ�2MSL��ʱ�� +*��״̬�µı��Ķ����������еľ���ݣ�ֱ��ɾ��ɡ� +*����Ҫ���ͷ�����ACK���� +*/ +static err_t +tcp_timewait_input(struct tcp_pcb *pcb) +{ + + if (flags & TCP_RST) { //RST��λ��ֱ�ӷ��� + return ERR_OK; + } + + if (flags & TCP_SYN) { //��SYN������Ϣ����������ݱ���ڽ��մ����ڣ����ͷ�����RST���� + + if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt+pcb->rcv_wnd)) { + + tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(), ip_current_src_addr(), + tcphdr->dest, tcphdr->src); + return ERR_OK; + } + } else if (flags & TCP_FIN) { //����FIN������Ϣ + + pcb->tmr = tcp_ticks; //��λ�ȴ�2MSLʱ�䣬���ƿ����µȴ�2MSL + } + + if ((tcplen > 0)) { //��������ݵı��Ļ����ڽ��մ������SYN���� + pcb->flags |= TF_ACK_NOW;//����һ��ACK���� + return tcp_output(pcb); + } + return ERR_OK; +} + +/** + * Implements the TCP state machine. Called by tcp_input. In some + * states tcp_receive() is called to receive data. The tcp_seg + * argument will be freed by the caller (tcp_input()) unless the + * recv_data pointer in the pcb is set. + * + * @param pcb the tcp_pcb for which a segment arrived + * + * @note the segment which arrived is saved in global variables, therefore only the pcb + * involved is passed as a parameter to this function + */ +static err_t +tcp_process(struct tcp_pcb *pcb) +{ + struct tcp_seg *rseg; + u8_t acceptable = 0; + err_t err; + + err = ERR_OK; + + /* Process incoming RST segments. */ + if (flags & TCP_RST) { + /* First, determine if the reset is acceptable. */ + if (pcb->state == SYN_SENT) { + if (ackno == pcb->snd_nxt) { + acceptable = 1; + } + } else { + if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, + pcb->rcv_nxt+pcb->rcv_wnd)) { + acceptable = 1; + } + } + + if (acceptable) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: Connection RESET\n")); + LWIP_ASSERT("tcp_input: pcb->state != CLOSED", pcb->state != CLOSED); + recv_flags |= TF_RESET; + pcb->flags &= ~TF_ACK_DELAY; + return ERR_RST; + } else { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n", + seqno, pcb->rcv_nxt)); + LWIP_DEBUGF(TCP_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n", + seqno, pcb->rcv_nxt)); + return ERR_OK; + } + } + + if ((flags & TCP_SYN) && (pcb->state != SYN_SENT && pcb->state != SYN_RCVD)) { + /* Cope with new connection attempt after remote end crashed */ + tcp_ack_now(pcb); + return ERR_OK; + } + + if ((pcb->flags & TF_RXCLOSED) == 0) { + /* Update the PCB (in)activity timer unless rx is closed (see tcp_shutdown) */ + pcb->tmr = tcp_ticks; + } + pcb->keep_cnt_sent = 0; + + tcp_parseopt(pcb); + + /* Do different things depending on the TCP state. */ + switch (pcb->state) { + case SYN_SENT: + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("SYN-SENT: ackno %"U32_F" pcb->snd_nxt %"U32_F" unacked %"U32_F"\n", ackno, + pcb->snd_nxt, ntohl(pcb->unacked->tcphdr->seqno))); + /* received SYN ACK with expected sequence number? */ + if ((flags & TCP_ACK) && (flags & TCP_SYN) + && ackno == ntohl(pcb->unacked->tcphdr->seqno) + 1) { + pcb->snd_buf++; + pcb->rcv_nxt = seqno + 1; + pcb->rcv_ann_right_edge = pcb->rcv_nxt; + pcb->lastack = ackno; + pcb->snd_wnd = tcphdr->wnd; + pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */ + pcb->state = ESTABLISHED; + +#if TCP_CALCULATE_EFF_SEND_MSS + pcb->mss = tcp_eff_send_mss(pcb->mss, &(pcb->remote_ip)); +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + + /* Set ssthresh again after changing pcb->mss (already set in tcp_connect + * but for the default value of pcb->mss) */ + pcb->ssthresh = pcb->mss * 10; + + pcb->cwnd = ((pcb->cwnd == 1) ? (pcb->mss * 2) : pcb->mss); + LWIP_ASSERT("pcb->snd_queuelen > 0", (pcb->snd_queuelen > 0)); + --pcb->snd_queuelen; + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_process: SYN-SENT --queuelen %"U16_F"\n", (u16_t)pcb->snd_queuelen)); + rseg = pcb->unacked; + pcb->unacked = rseg->next; + + /* If there's nothing left to acknowledge, stop the retransmit + timer, otherwise reset it to start again */ + if(pcb->unacked == NULL) + pcb->rtime = -1; + else { + pcb->rtime = 0; +// pcb->nrtx = 0; + } + pcb->nrtx = 0; + + tcp_seg_free(rseg); + + /* Call the user specified function to call when sucessfully + * connected. */ + TCP_EVENT_CONNECTED(pcb, ERR_OK, err); + if (err == ERR_ABRT) { + return ERR_ABRT; + } + tcp_ack_now(pcb); + } + /* received ACK? possibly a half-open connection */ + else if (flags & TCP_ACK) { + /* send a RST to bring the other side in a non-synchronized state. */ + tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(), ip_current_src_addr(), + tcphdr->dest, tcphdr->src); + } + break; + case SYN_RCVD: + if (flags & TCP_ACK) { + /* expected ACK number? */ + if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)) { + u16_t old_cwnd; + pcb->state = ESTABLISHED; + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection established %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); +#if LWIP_CALLBACK_API + LWIP_ASSERT("pcb->accept != NULL", pcb->accept != NULL); +#endif + /* Call the accept function. */ + TCP_EVENT_ACCEPT(pcb, ERR_OK, err); + if (err != ERR_OK) { + /* If the accept function returns with an error, we abort + * the connection. */ + /* Already aborted? */ + if (err != ERR_ABRT) { + tcp_abort(pcb); + } + return ERR_ABRT; + } + old_cwnd = pcb->cwnd; + /* If there was any data contained within this ACK, + * we'd better pass it on to the application as well. */ + tcp_receive(pcb); + + /* Prevent ACK for SYN to generate a sent event */ + if (pcb->acked != 0) { + pcb->acked--; + } + + pcb->cwnd = ((old_cwnd == 1) ? (pcb->mss * 2) : pcb->mss); + + if (recv_flags & TF_GOT_FIN) { + tcp_ack_now(pcb); + pcb->state = CLOSE_WAIT; + } + } else { + /* incorrect ACK number, send RST */ + tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(), ip_current_src_addr(), + tcphdr->dest, tcphdr->src); + } + } else if ((flags & TCP_SYN) && (seqno == pcb->rcv_nxt - 1)) { + /* Looks like another copy of the SYN - retransmit our SYN-ACK */ + tcp_rexmit(pcb); + } + break; + case CLOSE_WAIT: + /* FALLTHROUGH */ + case ESTABLISHED: + tcp_receive(pcb); + if (recv_flags & TF_GOT_FIN) { /* passive close */ + tcp_ack_now(pcb); + pcb->state = CLOSE_WAIT; + } + break; + case FIN_WAIT_1: + tcp_receive(pcb); + if (recv_flags & TF_GOT_FIN) { + if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt)) { + LWIP_DEBUGF(TCP_DEBUG, + ("TCP connection closed: FIN_WAIT_1 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + tcp_ack_now(pcb); + tcp_pcb_purge(pcb); + TCP_RMV(&tcp_active_pcbs, pcb); + pcb->state = TIME_WAIT; + TCP_REG(&tcp_tw_pcbs, pcb); + } else { + tcp_ack_now(pcb); + pcb->state = CLOSING; + } + } else if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt)) { + pcb->state = FIN_WAIT_2; + } + break; + case FIN_WAIT_2: + tcp_receive(pcb); + if (recv_flags & TF_GOT_FIN) { + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: FIN_WAIT_2 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + tcp_ack_now(pcb); + tcp_pcb_purge(pcb); + TCP_RMV(&tcp_active_pcbs, pcb); + pcb->state = TIME_WAIT; + TCP_REG(&tcp_tw_pcbs, pcb); + } + break; + case CLOSING: + tcp_receive(pcb); + if (flags & TCP_ACK && ackno == pcb->snd_nxt) { + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: CLOSING %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + tcp_pcb_purge(pcb); + TCP_RMV(&tcp_active_pcbs, pcb); + pcb->state = TIME_WAIT; + TCP_REG(&tcp_tw_pcbs, pcb); + } + break; + case LAST_ACK: + tcp_receive(pcb); + if (flags & TCP_ACK && ackno == pcb->snd_nxt) { + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: LAST_ACK %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + /* bugfix #21699: don't set pcb->state to CLOSED here or we risk leaking segments */ + recv_flags |= TF_CLOSED; + } + break; + default: + break; + } + return ERR_OK; +} + +#if TCP_QUEUE_OOSEQ +/** + * Insert segment into the list (segments covered with new one will be deleted) + * + * Called from tcp_receive() + */ +static void ICACHE_FLASH_ATTR +tcp_oos_insert_segment(struct tcp_seg *cseg, struct tcp_seg *next) +{ + struct tcp_seg *old_seg; + + if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) { + /* received segment overlaps all following segments */ + tcp_segs_free(next); + next = NULL; + } + else { + /* delete some following segments + oos queue may have segments with FIN flag */ + while (next && + TCP_SEQ_GEQ((seqno + cseg->len), + (next->tcphdr->seqno + next->len))) { + /* cseg with FIN already processed */ + if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) { + TCPH_SET_FLAG(cseg->tcphdr, TCP_FIN); + } + old_seg = next; + next = next->next; + tcp_seg_free(old_seg); + } + if (next && + TCP_SEQ_GT(seqno + cseg->len, next->tcphdr->seqno)) { + /* We need to trim the incoming segment. */ + cseg->len = (u16_t)(next->tcphdr->seqno - seqno); + pbuf_realloc(cseg->p, cseg->len); + } + } + cseg->next = next; +} +#endif /* TCP_QUEUE_OOSEQ */ + +/** + * Called by tcp_process. Checks if the given segment is an ACK for outstanding + * data, and if so frees the memory of the buffered data. Next, is places the + * segment on any of the receive queues (pcb->recved or pcb->ooseq). If the segment + * is buffered, the pbuf is referenced by pbuf_ref so that it will not be freed until + * i it has been removed from the buffer. + * + * If the incoming segment constitutes an ACK for a segment that was used for RTT + * estimation, the RTT is estimated here as well. + * + * Called from tcp_process(). + */ +static void +tcp_receive(struct tcp_pcb *pcb) +{ + struct tcp_seg *next; +#if TCP_QUEUE_OOSEQ + struct tcp_seg *prev, *cseg; +#endif /* TCP_QUEUE_OOSEQ */ + struct pbuf *p; + s32_t off; + s16_t m; + u32_t right_wnd_edge; + u16_t new_tot_len; + int found_dupack = 0; + + if (flags & TCP_ACK) {//����ACK + right_wnd_edge = pcb->snd_wnd + pcb->snd_wl2;//���ʹ��� + ����Ӧ����󴰿ڸ��� + + // first /* Update window. */ + /*seqno > snd_wl1���������ֹ�̲��ô��ָ���; + *seqno = snd_wl1����ackno > snd_wl2;��ʱ���Է�û�з�����ݣ�ֻ���յ���ݵ�ȷ��; + *ackno = snd_wl2�ұ����ײ��б�snd_wnd���Ĵ���.����������Ӧֵ + */ + if (TCP_SEQ_LT(pcb->snd_wl1, seqno) || + (pcb->snd_wl1 == seqno && TCP_SEQ_LT(pcb->snd_wl2, ackno)) || + (pcb->snd_wl2 == ackno && tcphdr->wnd > pcb->snd_wnd)) { + pcb->snd_wnd = tcphdr->wnd; + pcb->snd_wl1 = seqno; + pcb->snd_wl2 = ackno; + if (pcb->snd_wnd > 0 && pcb->persist_backoff > 0) { + pcb->persist_backoff = 0;//�����ʱ���˳� + } + LWIP_DEBUGF(TCP_WND_DEBUG, ("tcp_receive: window update %"U16_F"\n", pcb->snd_wnd)); +#if TCP_WND_DEBUG + } else { + if (pcb->snd_wnd != tcphdr->wnd) { + LWIP_DEBUGF(TCP_WND_DEBUG, + ("tcp_receive: no window update lastack %"U32_F" ackno %" + U32_F" wl1 %"U32_F" seqno %"U32_F" wl2 %"U32_F"\n", + pcb->lastack, ackno, pcb->snd_wl1, seqno, pcb->snd_wl2)); + } +#endif /* TCP_WND_DEBUG */ + } + + /* (From Stevens TCP/IP Illustrated Vol II, p970.) Its only a + * duplicate ack if: + * 1) It doesn't ACK new data û��ȷ������� + * 2) length of received packet is zero (i.e. no payload) ���Ķ����κ���� + * 3) the advertised window hasn't changed ���ش���û�и��� + * 4) There is outstanding unacknowledged data (retransmission timer running)������ݵȴ�ȷ�� + * 5) The ACK is == biggest ACK sequence number so far seen (snd_una) ackno = lastack + * + * If it passes all five, should process as a dupack: + * a) dupacks < 3: do nothing + * b) dupacks == 3: fast retransmit + * c) dupacks > 3: increase cwnd + * + * If it only passes 1-3, should reset dupack counter (and add to + * stats, which we don't do in lwIP) + * + * If it only passes 1, should reset dupack counter + * + */ + + /* Clause 1 */ + if (TCP_SEQ_LEQ(ackno, pcb->lastack)) {//���ظ�ACK? + pcb->acked = 0; + /* Clause 2 */ + if (tcplen == 0) { + /* Clause 3 */ + if (pcb->snd_wl2 + pcb->snd_wnd == right_wnd_edge){ + /* Clause 4 */ + if (pcb->rtime >= 0) { + /* Clause 5 */ + if (pcb->lastack == ackno) { + found_dupack = 1; + if (pcb->dupacks + 1 > pcb->dupacks) + ++pcb->dupacks; + if (pcb->dupacks > 3) { + /* Inflate the congestion window, but not if it means that + the value overflows. */ + if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) { + pcb->cwnd += pcb->mss; + } + } else if (pcb->dupacks == 3) {//���ظ�ACK + /* Do fast retransmit */ + tcp_rexmit_fast(pcb); + } + } + } + } + } + /* If Clause (1) or more is true, but not a duplicate ack, reset + * count of consecutive duplicate acks */ + if (!found_dupack) { + pcb->dupacks = 0; + } + } else if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)){//ackno��lastack+1��snd_nxt֮�䣬�жϷ��ʹ�������� + /* We come here when the ACK acknowledges new data. */ + + if (pcb->flags & TF_INFR) { + pcb->flags &= ~TF_INFR;// Reset the "IN Fast Retransmit" flag,since we are no longer in fast retransmit + pcb->cwnd = pcb->ssthresh;//Reset the congestion window to the "slow start threshold". + } + + /* Reset the number of retransmissions. */ + pcb->nrtx = 0; + + /* Reset the retransmission time-out. */ + pcb->rto = (pcb->sa >> 3) + pcb->sv; + + /* Update the send buffer space. Diff between the two can never exceed 64K? */ + pcb->acked = (u16_t)(ackno - pcb->lastack); + + pcb->snd_buf += pcb->acked; + + /* Reset the fast retransmit variables. */ + pcb->dupacks = 0; + pcb->lastack = ackno; + + /* Update the congestion control variables (cwnd and + ssthresh). */ + if (pcb->state >= ESTABLISHED) {//״̬Ϊ�������ӱ�־ + if (pcb->cwnd < pcb->ssthresh) { + if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) { + pcb->cwnd += pcb->mss; + } + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: slow start cwnd %"U16_F"\n", pcb->cwnd)); + } else { + u16_t new_cwnd = (pcb->cwnd + pcb->mss * pcb->mss / pcb->cwnd); + if (new_cwnd > pcb->cwnd) { + pcb->cwnd = new_cwnd; + } + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: congestion avoidance cwnd %"U16_F"\n", pcb->cwnd)); + } + } + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: ACK for %"U32_F", unacked->seqno %"U32_F":%"U32_F"\n", + ackno, + pcb->unacked != NULL? + ntohl(pcb->unacked->tcphdr->seqno): 0, + pcb->unacked != NULL? + ntohl(pcb->unacked->tcphdr->seqno) + TCP_TCPLEN(pcb->unacked): 0)); + + /* Remove segment from the unacknowledged list if the incoming + ACK acknowlegdes them. + *�ͷ�unacked�����ϱ�ȷ�ϵı��ĶΣ� + *ֱ��unacked����Ϊ��ֹͣ*/ + while (pcb->unacked != NULL && + TCP_SEQ_LEQ(ntohl(pcb->unacked->tcphdr->seqno) + + TCP_TCPLEN(pcb->unacked), ackno)) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unacked\n", + ntohl(pcb->unacked->tcphdr->seqno), + ntohl(pcb->unacked->tcphdr->seqno) + + TCP_TCPLEN(pcb->unacked))); + + next = pcb->unacked;//pcb unacked��־ + pcb->unacked = pcb->unacked->next;//pcb unacked ��һ����־ + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"U16_F" ... ", (u16_t)pcb->snd_queuelen)); + LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p))); + /* Prevent ACK for FIN to generate a sent event */ + if ((pcb->acked != 0) && ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0)) { + pcb->acked--; + } + + pcb->snd_queuelen -= pbuf_clen(next->p);//�������������pbufs���� + tcp_seg_free(next);//�ͷ�tcp�� + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"U16_F" (after freeing unacked)\n", (u16_t)pcb->snd_queuelen)); + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_receive: valid queue length", pcb->unacked != NULL || + pcb->unsent != NULL); + } + } + + /* If there's nothing left to acknowledge, stop the retransmit + timer, otherwise reset it to start again */ + if(pcb->unacked == NULL) //����ݵȴ�ȷ�� + pcb->rtime = -1; //ֹͣ�ش���ʱ�� + else + pcb->rtime = 0; //��λ�ش���ʱ�� + + pcb->polltmr = 0; + } else { + /* Fix bug bug #21582: out of sequence ACK, didn't really ack anything */ + pcb->acked = 0; + } + + /* We go through the ->unsent list to see if any of the segments + on the list are acknowledged by the ACK. This may seem + strange since an "unsent" segment shouldn't be acked. The + rationale is that lwIP puts all outstanding segments on the + ->unsent list after a retransmission, so these segments may + in fact have been sent once. */ + /** unsent�������Ƿ��ܱ�acknoȷ�ϵı��ĶΣ������ͷ�**/ + while (pcb->unsent != NULL && + TCP_SEQ_BETWEEN(ackno, ntohl(pcb->unsent->tcphdr->seqno) + + TCP_TCPLEN(pcb->unsent), pcb->snd_nxt)) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unsent\n", + ntohl(pcb->unsent->tcphdr->seqno), ntohl(pcb->unsent->tcphdr->seqno) + + TCP_TCPLEN(pcb->unsent))); + + next = pcb->unsent;//pcbδ���ͱ�־ + pcb->unsent = pcb->unsent->next;//δ���͵���һ�� + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"U16_F" ... ", (u16_t)pcb->snd_queuelen)); + LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p))); + /* Prevent ACK for FIN to generate a sent event */ + if ((pcb->acked != 0) && ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0)) { + pcb->acked--; + } + pcb->snd_queuelen -= pbuf_clen(next->p);//������pbuf�ĸ��� + tcp_seg_free(next);//�ͷŶ� + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"U16_F" (after freeing unsent)\n", (u16_t)pcb->snd_queuelen)); + if (pcb->snd_queuelen != 0) {//��������� + LWIP_ASSERT("tcp_receive: valid queue length", + pcb->unacked != NULL || pcb->unsent != NULL); + } + } + /* End of ACK for new data processing. */ + + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: pcb->rttest %"U32_F" rtseq %"U32_F" ackno %"U32_F"\n", + pcb->rttest, pcb->rtseq, ackno)); + + /* RTT estimation calculations. This is done by checking if the + incoming segment acknowledges the segment we use to take a + round-trip time measurement. */ + if (pcb->rttest && TCP_SEQ_LT(pcb->rtseq, ackno)) {//RTT���ڽ����Ҹñ��Ķα�ȷ�� + /* diff between this shouldn't exceed 32K since this are tcp timer ticks + and a round-trip shouldn't be that long... */ + m = (s16_t)(tcp_ticks - pcb->rttest);//����MÖµ + + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: experienced rtt %"U16_F" ticks (%"U16_F" msec).\n", + m, m * TCP_SLOW_INTERVAL)); + + /* This is taken directly from VJs original code in his paper �����RTT���㹫ʽ*/ + m = m - (pcb->sa >> 3); + pcb->sa += m; + if (m < 0) { + m = -m; + } + m = m - (pcb->sv >> 2); + pcb->sv += m; + pcb->rto = (pcb->sa >> 3) + pcb->sv; + + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: RTO %"U16_F" (%"U16_F" milliseconds)\n", + pcb->rto, pcb->rto * TCP_SLOW_INTERVAL)); + + pcb->rttest = 0; + } + } + + /* If the incoming segment contains data, we must process it + further. */ + if (tcplen > 0) { + /* This code basically does three things: + + +) If the incoming segment contains data that is the next + in-sequence data, this data is passed to the application. This + might involve trimming the first edge of the data. The rcv_nxt + variable and the advertised window are adjusted. + + +) If the incoming segment has data that is above the next + sequence number expected (->rcv_nxt), the segment is placed on + the ->ooseq queue. This is done by finding the appropriate + place in the ->ooseq queue (which is ordered by sequence + number) and trim the segment in both ends if needed. An + immediate ACK is sent to indicate that we received an + out-of-sequence segment. + + +) Finally, we check if the first segment on the ->ooseq queue + now is in sequence (i.e., if rcv_nxt >= ooseq->seqno). If + rcv_nxt > ooseq->seqno, we must trim the first edge of the + segment on ->ooseq before we adjust rcv_nxt. The data in the + segments that are now on sequence are chained onto the + incoming segment so that we only need to call the application + once. + */ + + /* First, we check if we must trim the first edge. We have to do + this if the sequence number of the incoming segment is less + than rcv_nxt, and the sequence number plus the length of the + segment is larger than rcv_nxt. */ + /* if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)){ + if (TCP_SEQ_LT(pcb->rcv_nxt, seqno + tcplen)) {*/ + if (TCP_SEQ_BETWEEN(pcb->rcv_nxt, seqno + 1, seqno + tcplen - 1)){// seqno < rcv_nxt < seqno + tcplen + /* Trimming the first edge is done by pushing the payload + pointer in the pbuf downwards. This is somewhat tricky since + we do not want to discard the full contents of the pbuf up to + the new starting point of the data since we have to keep the + TCP header which is present in the first pbuf in the chain. + + What is done is really quite a nasty hack: the first pbuf in + the pbuf chain is pointed to by inseg.p. Since we need to be + able to deallocate the whole pbuf, we cannot change this + inseg.p pointer to point to any of the later pbufs in the + chain. Instead, we point the ->payload pointer in the first + pbuf to data in one of the later pbufs. We also set the + inseg.data pointer to point to the right place. This way, the + ->p pointer will still point to the first pbuf, but the + ->p->payload pointer will point to data in another pbuf. + + After we are done with adjusting the pbuf pointers we must + adjust the ->data pointer in the seg and the segment + length.*/ + //ȥ�����Ķ�����ݱ�ŵ���rcv_nxt����� + off = pcb->rcv_nxt - seqno; + p = inseg.p; + LWIP_ASSERT("inseg.p != NULL", inseg.p); + LWIP_ASSERT("insane offset!", (off < 0x7fff)); + if (inseg.p->len < off) { + LWIP_ASSERT("pbuf too short!", (((s32_t)inseg.p->tot_len) >= off)); + new_tot_len = (u16_t)(inseg.p->tot_len - off); + while (p->len < off) { + off -= p->len; + /* KJM following line changed (with addition of new_tot_len var) + to fix bug #9076 + inseg.p->tot_len -= p->len; */ + p->tot_len = new_tot_len; + p->len = 0; + p = p->next; + } + if(pbuf_header(p, (s16_t)-off)) { + /* Do we need to cope with this failing? Assert for now */ + LWIP_ASSERT("pbuf_header failed", 0); + } + } else { + if(pbuf_header(inseg.p, (s16_t)-off)) { + /* Do we need to cope with this failing? Assert for now */ + LWIP_ASSERT("pbuf_header failed", 0); + } + } + inseg.len -= (u16_t)(pcb->rcv_nxt - seqno); + inseg.tcphdr->seqno = seqno = pcb->rcv_nxt; + } + else { + if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)){//seqno < rcv_nxt + /* the whole segment is < rcv_nxt */ + /* must be a duplicate of a packet that has already been correctly handled */ + //���Ķ���������ݱ�ž�С��rcv_nxt����˱������ظ����ģ� + //ֱ����Դ����ӦACK���Ĵ��� + + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: duplicate seqno %"U32_F"\n", seqno)); + tcp_ack_now(pcb); + } + } + + /* The sequence number must be within the window (above rcv_nxt + and below rcv_nxt + rcv_wnd) in order to be further + processed. */ + if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, + pcb->rcv_nxt + pcb->rcv_wnd - 1)){//rcv_nxt < seqno < rcv_nxt + rcv_wnd - 1,������ڽ��շ�Χ�� + if (pcb->rcv_nxt == seqno) { + /* The incoming segment is the next in sequence. We check if + we have to trim the end of the segment and update rcv_nxt + and pass the data to the application. */ + tcplen = TCP_TCPLEN(&inseg);//���㱨�Ķγ��� + + if (tcplen > pcb->rcv_wnd) {//������մ��ڴ�С��������β���ض� + LWIP_DEBUGF(TCP_INPUT_DEBUG, + ("tcp_receive: other end overran receive window" + "seqno %"U32_F" len %"U16_F" right edge %"U32_F"\n", + seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd)); + if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) { + /* Must remove the FIN from the header as we're trimming + * that byte of sequence-space from the packet */ + TCPH_FLAGS_SET(inseg.tcphdr, TCPH_FLAGS(inseg.tcphdr) &~ TCP_FIN); + } + /* Adjust length of segment to fit in the window. */ + inseg.len = pcb->rcv_wnd; + if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) { + inseg.len -= 1; + } + pbuf_realloc(inseg.p, inseg.len); + tcplen = TCP_TCPLEN(&inseg); + LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd\n", + (seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd)); + } +#if TCP_QUEUE_OOSEQ + /* Received in-sequence data, adjust ooseq data if: + - FIN has been received or + - inseq overlaps with ooseq */ + if (pcb->ooseq != NULL) { + if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, + ("tcp_receive: received in-order FIN, binning ooseq queue\n")); + /* Received in-order FIN means anything that was received + * out of order must now have been received in-order, so + * bin the ooseq queue */ + while (pcb->ooseq != NULL) { + struct tcp_seg *old_ooseq = pcb->ooseq; + pcb->ooseq = pcb->ooseq->next; + tcp_seg_free(old_ooseq); + } + } + else { + next = pcb->ooseq; + /* Remove all segments on ooseq that are covered by inseg already. + * FIN is copied from ooseq to inseg if present. */ + while (next && + TCP_SEQ_GEQ(seqno + tcplen, + next->tcphdr->seqno + next->len)) { + /* inseg cannot have FIN here (already processed above) */ + if (TCPH_FLAGS(next->tcphdr) & TCP_FIN && + (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) == 0) { + TCPH_SET_FLAG(inseg.tcphdr, TCP_FIN); + tcplen = TCP_TCPLEN(&inseg); + } + prev = next; + next = next->next; + tcp_seg_free(prev); + } + /* Now trim right side of inseg if it overlaps with the first + * segment on ooseq */ + if (next && + TCP_SEQ_GT(seqno + tcplen, + next->tcphdr->seqno)) { + /* inseg cannot have FIN here (already processed above) */ + inseg.len = (u16_t)(next->tcphdr->seqno - seqno); + if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) { + inseg.len -= 1; + } + pbuf_realloc(inseg.p, inseg.len); + tcplen = TCP_TCPLEN(&inseg); + LWIP_ASSERT("tcp_receive: segment not trimmed correctly to ooseq queue\n", + (seqno + tcplen) == next->tcphdr->seqno); + } + pcb->ooseq = next; + } + } +#endif /* TCP_QUEUE_OOSEQ */ + + pcb->rcv_nxt = seqno + tcplen; + + /* Update the receiver's (our) window. */ + LWIP_ASSERT("tcp_receive: tcplen > rcv_wnd\n", pcb->rcv_wnd >= tcplen); + pcb->rcv_wnd -= tcplen; + + tcp_update_rcv_ann_wnd(pcb); + + /* If there is data in the segment, we make preparations to + pass this up to the application. The ->recv_data variable + is used for holding the pbuf that goes to the + application. The code for reassembling out-of-sequence data + chains its data on this pbuf as well. + + If the segment was a FIN, we set the TF_GOT_FIN flag that will + be used to indicate to the application that the remote side has + closed its end of the connection. */ + if (inseg.p->tot_len > 0) { + recv_data = inseg.p; + /* Since this pbuf now is the responsibility of the + application, we delete our reference to it so that we won't + (mistakingly) deallocate it. */ + inseg.p = NULL; + } + if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: received FIN.\n")); + recv_flags |= TF_GOT_FIN; + } + +#if TCP_QUEUE_OOSEQ + /* We now check if we have segments on the ->ooseq queue that + are now in sequence. */ + while (pcb->ooseq != NULL && + pcb->ooseq->tcphdr->seqno == pcb->rcv_nxt) { + + cseg = pcb->ooseq; + seqno = pcb->ooseq->tcphdr->seqno; + + pcb->rcv_nxt += TCP_TCPLEN(cseg); + LWIP_ASSERT("tcp_receive: ooseq tcplen > rcv_wnd\n", + pcb->rcv_wnd >= TCP_TCPLEN(cseg)); + pcb->rcv_wnd -= TCP_TCPLEN(cseg); + + tcp_update_rcv_ann_wnd(pcb); + + if (cseg->p->tot_len > 0) { + /* Chain this pbuf onto the pbuf that we will pass to + the application. */ + if (recv_data) { + pbuf_cat(recv_data, cseg->p); + } else { + recv_data = cseg->p; + } + cseg->p = NULL; + } + if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: dequeued FIN.\n")); + recv_flags |= TF_GOT_FIN; + if (pcb->state == ESTABLISHED) { /* force passive close or we can move to active close */ + pcb->state = CLOSE_WAIT; + } + } + + pcb->ooseq = cseg->next; + tcp_seg_free(cseg); + } +#endif /* TCP_QUEUE_OOSEQ */ + + + /* Acknowledge the segment(s). */ + tcp_ack(pcb); + + } else { + /* We get here if the incoming segment is out-of-sequence. */ + tcp_send_empty_ack(pcb); +#if TCP_QUEUE_OOSEQ + /* We queue the segment on the ->ooseq queue. */ + if (pcb->ooseq == NULL) { + pcb->ooseq = tcp_seg_copy(&inseg); + } else { + /* If the queue is not empty, we walk through the queue and + try to find a place where the sequence number of the + incoming segment is between the sequence numbers of the + previous and the next segment on the ->ooseq queue. That is + the place where we put the incoming segment. If needed, we + trim the second edges of the previous and the incoming + segment so that it will fit into the sequence. + + If the incoming segment has the same sequence number as a + segment on the ->ooseq queue, we discard the segment that + contains less data. */ + + prev = NULL; + for(next = pcb->ooseq; next != NULL; next = next->next) {//��ooseqȡ�µ�M�����ĶΣ��ñ��Ķηǿգ�M++ + if (seqno == next->tcphdr->seqno) {//�ñ��Ķ���ʼ���== Ҫ����ı��Ķα�� + /* The sequence number of the incoming segment is the + same as the sequence number of the segment on + ->ooseq. We check the lengths to see which one to + discard. */ + if (inseg.len > next->len) {//Ҫ����ı��Ķα�Ÿ� + /* The incoming segment is larger than the old + segment. We replace some segments with the new + one. */ + cseg = tcp_seg_copy(&inseg);//Ҫ����ı��Ķδ����M������ + if (cseg != NULL) { + if (prev != NULL) { + prev->next = cseg; + } else { + pcb->ooseq = cseg; + } + tcp_oos_insert_segment(cseg, next); + } + break; + } else { + /* Either the lenghts are the same or the incoming + segment was smaller than the old one; in either + case, we ditch the incoming segment. */ + break; + } + } else { + if (prev == NULL) { + if (TCP_SEQ_LT(seqno, next->tcphdr->seqno)) { + /* The sequence number of the incoming segment is lower + than the sequence number of the first segment on the + queue. We put the incoming segment first on the + queue. */ + cseg = tcp_seg_copy(&inseg); + if (cseg != NULL) { + pcb->ooseq = cseg; + tcp_oos_insert_segment(cseg, next); + } + break; + } + } else { + /*if (TCP_SEQ_LT(prev->tcphdr->seqno, seqno) && + TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {*/ + if (TCP_SEQ_BETWEEN(seqno, prev->tcphdr->seqno+1, next->tcphdr->seqno-1)) { + /* The sequence number of the incoming segment is in + between the sequence numbers of the previous and + the next segment on ->ooseq. We trim trim the previous + segment, delete next segments that included in received segment + and trim received, if needed. */ + cseg = tcp_seg_copy(&inseg); + if (cseg != NULL) { + if (TCP_SEQ_GT(prev->tcphdr->seqno + prev->len, seqno)) { + /* We need to trim the prev segment. */ + prev->len = (u16_t)(seqno - prev->tcphdr->seqno); + pbuf_realloc(prev->p, prev->len); + } + prev->next = cseg; + tcp_oos_insert_segment(cseg, next); + } + break; + } + } + /* If the "next" segment is the last segment on the + ooseq queue, we add the incoming segment to the end + of the list. */ + if (next->next == NULL && + TCP_SEQ_GT(seqno, next->tcphdr->seqno)) { + if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) { + /* segment "next" already contains all data */ + break; + } + next->next = tcp_seg_copy(&inseg); + if (next->next != NULL) { + if (TCP_SEQ_GT(next->tcphdr->seqno + next->len, seqno)) { + /* We need to trim the last segment. */ + next->len = (u16_t)(seqno - next->tcphdr->seqno); + pbuf_realloc(next->p, next->len); + } + /* check if the remote side overruns our receive window */ + if ((u32_t)tcplen + seqno > pcb->rcv_nxt + (u32_t)pcb->rcv_wnd) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, + ("tcp_receive: other end overran receive window" + "seqno %"U32_F" len %"U16_F" right edge %"U32_F"\n", + seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd)); + if (TCPH_FLAGS(next->next->tcphdr) & TCP_FIN) { + /* Must remove the FIN from the header as we're trimming + * that byte of sequence-space from the packet */ + TCPH_FLAGS_SET(next->next->tcphdr, TCPH_FLAGS(next->next->tcphdr) &~ TCP_FIN); + } + /* Adjust length of segment to fit in the window. */ + next->next->len = pcb->rcv_nxt + pcb->rcv_wnd - seqno; + pbuf_realloc(next->next->p, next->next->len); + tcplen = TCP_TCPLEN(next->next); + LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd\n", + (seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd)); + } + } + break; + } + } + prev = next; + } + } +#endif /* TCP_QUEUE_OOSEQ */ + + } + } else { + /* The incoming segment is not withing the window. */ + tcp_send_empty_ack(pcb); + } + } else { + /* Segments with length 0 is taken care of here. Segments that + fall out of the window are ACKed. */ + /*if (TCP_SEQ_GT(pcb->rcv_nxt, seqno) || + TCP_SEQ_GEQ(seqno, pcb->rcv_nxt + pcb->rcv_wnd)) {*/ + if(!TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_wnd-1)){ + tcp_ack_now(pcb);//��Դ�˷���һ������ȷ�ϱ��� + } + } +} + +/** + * Parses the options contained in the incoming segment. + * + * Called from tcp_listen_input() and tcp_process(). + * Currently, only the MSS option is supported! + * + * @param pcb the tcp_pcb for which a segment arrived + */ +static void +tcp_parseopt(struct tcp_pcb *pcb) +{ + u16_t c, max_c; + u16_t mss; + u8_t *opts, opt; +#if LWIP_TCP_TIMESTAMPS + u32_t tsval; +#endif + + opts = (u8_t *)tcphdr + TCP_HLEN; + + /* Parse the TCP MSS option, if present. */ + if(TCPH_HDRLEN(tcphdr) > 0x5) { + max_c = (TCPH_HDRLEN(tcphdr) - 5) << 2; + for (c = 0; c < max_c; ) { + opt = opts[c]; + switch (opt) { + case 0x00: + /* End of options. */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: EOL\n")); + return; + case 0x01: + /* NOP option. */ + ++c; + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: NOP\n")); + break; + case 0x02: + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: MSS\n")); + if (opts[c + 1] != 0x04 || c + 0x04 > max_c) { + /* Bad length */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n")); + return; + } + /* An MSS option with the right option length. */ + mss = (opts[c + 2] << 8) | opts[c + 3]; + /* Limit the mss to the configured TCP_MSS and prevent division by zero */ + pcb->mss = ((mss > TCP_MSS) || (mss == 0)) ? TCP_MSS : mss; + /* Advance to next option */ + c += 0x04; + break; +#if LWIP_TCP_TIMESTAMPS + case 0x08: + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: TS\n")); + if (opts[c + 1] != 0x0A || c + 0x0A > max_c) { + /* Bad length */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n")); + return; + } + /* TCP timestamp option with valid length */ + tsval = (opts[c+2]) | (opts[c+3] << 8) | + (opts[c+4] << 16) | (opts[c+5] << 24); + if (flags & TCP_SYN) { + pcb->ts_recent = ntohl(tsval); + pcb->flags |= TF_TIMESTAMP; + } else if (TCP_SEQ_BETWEEN(pcb->ts_lastacksent, seqno, seqno+tcplen)) { + pcb->ts_recent = ntohl(tsval); + } + /* Advance to next option */ + c += 0x0A; + break; +#endif + default: + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: other\n")); + if (opts[c + 1] == 0) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n")); + /* If the length field is zero, the options are malformed + and we don't process them further. */ + return; + } + /* All other options have a length field, so that we easily + can skip past them. */ + c += opts[c + 1]; + } + } + } +} + +#endif /* LWIP_TCP */ diff --git a/variants/lwip/lwip/core/tcp_out.c b/variants/lwip/lwip/core/tcp_out.c new file mode 100755 index 000000000..e2f8e9aa5 --- /dev/null +++ b/variants/lwip/lwip/core/tcp_out.c @@ -0,0 +1,1525 @@ +/** + * @file + * Transmission Control Protocol, outgoing traffic + * + * The output functions of TCP. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/tcp_impl.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/sys.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/inet_chksum.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "netif/etharp.h" + +#include + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +/* Define some copy-macros for checksum-on-copy so that the code looks + nicer by preventing too many ifdef's. */ +#if TCP_CHECKSUM_ON_COPY +#define TCP_DATA_COPY(dst, src, len, seg) do { \ + tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), \ + len, &seg->chksum, &seg->chksum_swapped); \ + seg->flags |= TF_SEG_DATA_CHECKSUMMED; } while(0) +#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped) \ + tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), len, chksum, chksum_swapped); +#else /* TCP_CHECKSUM_ON_COPY*/ +#define TCP_DATA_COPY(dst, src, len, seg) MEMCPY(dst, src, len) +#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped) MEMCPY(dst, src, len) +#endif /* TCP_CHECKSUM_ON_COPY*/ + +/** Define this to 1 for an extra check that the output checksum is valid + * (usefule when the checksum is generated by the application, not the stack) */ +#ifndef TCP_CHECKSUM_ON_COPY_SANITY_CHECK +#define TCP_CHECKSUM_ON_COPY_SANITY_CHECK 0 +#endif + +/* Forward declarations.*/ +static void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb); + +/** Allocate a pbuf and create a tcphdr at p->payload, used for output + * functions other than the default tcp_output -> tcp_output_segment + * (e.g. tcp_send_empty_ack, etc.) + * + * @param pcb tcp pcb for which to send a packet (used to initialize tcp_hdr) + * @param optlen length of header-options + * @param datalen length of tcp data to reserve in pbuf + * @param seqno_be seqno in network byte order (big-endian) + * @return pbuf with p->payload being the tcp_hdr + */ +static struct pbuf *ICACHE_FLASH_ATTR +tcp_output_alloc_header(struct tcp_pcb *pcb, u16_t optlen, u16_t datalen, + u32_t seqno_be /* already in network byte order */) +{ + struct tcp_hdr *tcphdr; + struct pbuf *p = pbuf_alloc(PBUF_IP, TCP_HLEN + optlen + datalen, PBUF_RAM); + if (p != NULL) { + LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr", + (p->len >= TCP_HLEN + optlen)); + tcphdr = (struct tcp_hdr *)p->payload; + tcphdr->src = htons(pcb->local_port); + tcphdr->dest = htons(pcb->remote_port); + tcphdr->seqno = seqno_be; + tcphdr->ackno = htonl(pcb->rcv_nxt); + TCPH_HDRLEN_FLAGS_SET(tcphdr, (5 + optlen / 4), TCP_ACK); + tcphdr->wnd = htons(pcb->rcv_ann_wnd); + tcphdr->chksum = 0; + tcphdr->urgp = 0; + + /* If we're sending a packet, update the announced right window edge */ + pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd; + } + return p; +} + +/** + * Called by tcp_close() to send a segment including FIN flag but not data. + * + * @param pcb the tcp_pcb over which to send a segment + * @return ERR_OK if sent, another err_t otherwise + */ +err_t +tcp_send_fin(struct tcp_pcb *pcb) +{ + /* first, try to add the fin to the last unsent segment */ + if (pcb->unsent != NULL) { + struct tcp_seg *last_unsent; + for (last_unsent = pcb->unsent; last_unsent->next != NULL; + last_unsent = last_unsent->next); + + if ((TCPH_FLAGS(last_unsent->tcphdr) & (TCP_SYN | TCP_FIN | TCP_RST)) == 0) { + /* no SYN/FIN/RST flag in the header, we can add the FIN flag */ + TCPH_SET_FLAG(last_unsent->tcphdr, TCP_FIN); + return ERR_OK; + } + } + /* no data, no length, flags, copy=1, no optdata */ + return tcp_enqueue_flags(pcb, TCP_FIN); +} + +/** + * Create a TCP segment with prefilled header. + * + * Called by tcp_write and tcp_enqueue_flags. + * + * @param pcb Protocol control block for the TCP connection. + * @param p pbuf that is used to hold the TCP header. + * @param flags TCP flags for header. + * @param seqno TCP sequence number of this packet + * @param optflags options to include in TCP header + * @return a new tcp_seg pointing to p, or NULL. + * The TCP header is filled in except ackno and wnd. + * p is freed on failure. + */ +static struct tcp_seg *ICACHE_FLASH_ATTR +tcp_create_segment(struct tcp_pcb *pcb, struct pbuf *p, u8_t flags, u32_t seqno, u8_t optflags) +{ + struct tcp_seg *seg; + u8_t optlen = LWIP_TCP_OPT_LENGTH(optflags); + + if ((seg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_create_segment: no memory.\n")); + pbuf_free(p); + return NULL; + } + seg->flags = optflags; + seg->next = NULL; + seg->p = p; + seg->len = p->tot_len - optlen; +#if TCP_OVERSIZE_DBGCHECK + seg->oversize_left = 0; +#endif /* TCP_OVERSIZE_DBGCHECK */ +#if TCP_CHECKSUM_ON_COPY + seg->chksum = 0; + seg->chksum_swapped = 0; + /* check optflags */ + LWIP_ASSERT("invalid optflags passed: TF_SEG_DATA_CHECKSUMMED", + (optflags & TF_SEG_DATA_CHECKSUMMED) == 0); +#endif /* TCP_CHECKSUM_ON_COPY */ + + /* build TCP header */ + if (pbuf_header(p, TCP_HLEN)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_create_segment: no room for TCP header in pbuf.\n")); + TCP_STATS_INC(tcp.err); + tcp_seg_free(seg); + return NULL; + } + seg->tcphdr = (struct tcp_hdr *)seg->p->payload; + seg->tcphdr->src = htons(pcb->local_port); + seg->tcphdr->dest = htons(pcb->remote_port); + seg->tcphdr->seqno = htonl(seqno); + /* ackno is set in tcp_output */ + TCPH_HDRLEN_FLAGS_SET(seg->tcphdr, (5 + optlen / 4), flags); + /* wnd and chksum are set in tcp_output */ + seg->tcphdr->urgp = 0; + return seg; +} + +/** + * Allocate a PBUF_RAM pbuf, perhaps with extra space at the end. + * + * This function is like pbuf_alloc(layer, length, PBUF_RAM) except + * there may be extra bytes available at the end. + * + * @param layer flag to define header size. + * @param length size of the pbuf's payload. + * @param max_length maximum usable size of payload+oversize. + * @param oversize pointer to a u16_t that will receive the number of usable tail bytes. + * @param pcb The TCP connection that willo enqueue the pbuf. + * @param apiflags API flags given to tcp_write. + * @param first_seg true when this pbuf will be used in the first enqueued segment. + * @param + */ +#if TCP_OVERSIZE +static struct pbuf *ICACHE_FLASH_ATTR +tcp_pbuf_prealloc(pbuf_layer layer, u16_t length, u16_t max_length, + u16_t *oversize, struct tcp_pcb *pcb, u8_t apiflags, + u8_t first_seg) +{ + struct pbuf *p; + u16_t alloc = length; + +#if LWIP_NETIF_TX_SINGLE_PBUF + LWIP_UNUSED_ARG(max_length); + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(apiflags); + LWIP_UNUSED_ARG(first_seg); + /* always create MSS-sized pbufs */ + alloc = TCP_MSS; +#else /* LWIP_NETIF_TX_SINGLE_PBUF */ + if (length < max_length) { + /* Should we allocate an oversized pbuf, or just the minimum + * length required? If tcp_write is going to be called again + * before this segment is transmitted, we want the oversized + * buffer. If the segment will be transmitted immediately, we can + * save memory by allocating only length. We use a simple + * heuristic based on the following information: + * + * Did the user set TCP_WRITE_FLAG_MORE? + * + * Will the Nagle algorithm defer transmission of this segment? + */ + if ((apiflags & TCP_WRITE_FLAG_MORE) || + (!(pcb->flags & TF_NODELAY) && + (!first_seg || + pcb->unsent != NULL || + pcb->unacked != NULL))) { + alloc = LWIP_MIN(max_length, LWIP_MEM_ALIGN_SIZE(length + TCP_OVERSIZE)); + } + } +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ + p = pbuf_alloc(layer, alloc, PBUF_RAM); + if (p == NULL) { + return NULL; + } + LWIP_ASSERT("need unchained pbuf", p->next == NULL); + *oversize = p->len - length; + /* trim p->len to the currently used size */ + p->len = p->tot_len = length; + return p; +} +#else /* TCP_OVERSIZE */ +#define tcp_pbuf_prealloc(layer, length, mx, os, pcb, api, fst) pbuf_alloc((layer), (length), PBUF_RAM) +#endif /* TCP_OVERSIZE */ + +#if TCP_CHECKSUM_ON_COPY +/** Add a checksum of newly added data to the segment */ +static void ICACHE_FLASH_ATTR +tcp_seg_add_chksum(u16_t chksum, u16_t len, u16_t *seg_chksum, + u8_t *seg_chksum_swapped) +{ + u32_t helper; + /* add chksum to old chksum and fold to u16_t */ + helper = chksum + *seg_chksum; + chksum = FOLD_U32T(helper); + if ((len & 1) != 0) { + *seg_chksum_swapped = 1 - *seg_chksum_swapped; + chksum = SWAP_BYTES_IN_WORD(chksum); + } + *seg_chksum = chksum; +} +#endif /* TCP_CHECKSUM_ON_COPY */ + +/** Checks if tcp_write is allowed or not (checks state, snd_buf and snd_queuelen). + * + * @param pcb the tcp pcb to check for + * @param len length of data to send (checked agains snd_buf) + * @return ERR_OK if tcp_write is allowed to proceed, another err_t otherwise + */ +static err_t ICACHE_FLASH_ATTR +tcp_write_checks(struct tcp_pcb *pcb, u16_t len) +{ + /* connection is in invalid state for data transmission? */ + if ((pcb->state != ESTABLISHED) && + (pcb->state != CLOSE_WAIT) && + (pcb->state != SYN_SENT) && + (pcb->state != SYN_RCVD)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_STATE | LWIP_DBG_LEVEL_SEVERE, ("tcp_write() called in invalid state\n")); + return ERR_CONN; + } else if (len == 0) { + return ERR_OK; + } + + /* fail on too much data */ + if (len > pcb->snd_buf) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_write: too much data (len=%"U16_F" > snd_buf=%"U16_F")\n", + len, pcb->snd_buf)); + pcb->flags |= TF_NAGLEMEMERR; + return ERR_MEM; + } + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: queuelen: %"U16_F"\n", (u16_t)pcb->snd_queuelen)); + + /* If total number of pbufs on the unsent/unacked queues exceeds the + * configured maximum, return an error */ + /* check for configured max queuelen and possible overflow */ + if ((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_write: too long queue %"U16_F" (max %"U16_F")\n", + pcb->snd_queuelen, TCP_SND_QUEUELEN)); + TCP_STATS_INC(tcp.memerr); + pcb->flags |= TF_NAGLEMEMERR; + return ERR_MEM; + } + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_write: pbufs on queue => at least one queue non-empty", + pcb->unacked != NULL || pcb->unsent != NULL); + } else { + LWIP_ASSERT("tcp_write: no pbufs on queue => both queues empty", + pcb->unacked == NULL && pcb->unsent == NULL); + } + return ERR_OK; +} + +/** + * Write data for sending (but does not send it immediately). + *��������һ��������ݣ��ú�����һ�����Ķβ����ڿ��ƿ黺������� + * It waits in the expectation of more data being sent soon (as + * it can send them more efficiently by combining them together). + * To prompt the system to send data now, call tcp_output() after + * calling tcp_write(). + * + * @param pcb Protocol control block for the TCP connection to enqueue data for.��Ӧ���ӿ��ƿ� + * @param arg Pointer to the data to be enqueued for sending.���������ʼ��ַ + * @param len Data length in bytes������ݳ��� + * @param apiflags combination of following flags :����Ƿ���п������Լ����Ķ��ײ��Ƿ������PSH��־ + * - TCP_WRITE_FLAG_COPY (0x01) data will be copied into memory belonging to the stack + * - TCP_WRITE_FLAG_MORE (0x02) for TCP connection, PSH flag will be set on last segment sent, + * @return ERR_OK if enqueued, another err_t on error + */ +err_t +tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags) +{ + struct pbuf *concat_p = NULL; + struct tcp_seg *last_unsent = NULL, *seg = NULL, *prev_seg = NULL, *queue = NULL; + u16_t pos = 0; /* position in 'arg' data */ + u16_t queuelen; + u8_t optlen = 0; + u8_t optflags = 0; +#if TCP_OVERSIZE + u16_t oversize = 0; + u16_t oversize_used = 0; +#endif /* TCP_OVERSIZE */ +#if TCP_CHECKSUM_ON_COPY + u16_t concat_chksum = 0; + u8_t concat_chksum_swapped = 0; + u16_t concat_chksummed = 0; +#endif /* TCP_CHECKSUM_ON_COPY */ + err_t err; + +#if LWIP_NETIF_TX_SINGLE_PBUF + /* Always copy to try to create single pbufs for TX */ + apiflags |= TCP_WRITE_FLAG_COPY; +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ + + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_write(pcb=%p, data=%p, len=%"U16_F", apiflags=%"U16_F")\n", + (void *)pcb, arg, len, (u16_t)apiflags)); + LWIP_ERROR("tcp_write: arg == NULL (programmer violates API)", + arg != NULL, return ERR_ARG;); + + err = tcp_write_checks(pcb, len); + if (err != ERR_OK) { + return err; + } + queuelen = pcb->snd_queuelen; + +#if LWIP_TCP_TIMESTAMPS + if ((pcb->flags & TF_TIMESTAMP)) { + optflags = TF_SEG_OPTS_TS; + optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS); + } +#endif /* LWIP_TCP_TIMESTAMPS */ + + + /* + * TCP segmentation is done in three phases with increasing complexity: + * + * 1. Copy data directly into an oversized pbuf. + * 2. Chain a new pbuf to the end of pcb->unsent. + * 3. Create new segments. + * + * We may run out of memory at any point. In that case we must + * return ERR_MEM and not change anything in pcb. Therefore, all + * changes are recorded in local variables and committed at the end + * of the function. Some pcb fields are maintained in local copies: + * + * queuelen = pcb->snd_queuelen + * oversize = pcb->unsent_oversize + * + * These variables are set consistently by the phases: + * + * seg points to the last segment tampered with. + * + * pos records progress as data is segmented. + */ + + /* Find the tail of the unsent queue. */ + if (pcb->unsent != NULL) { + u16_t space; + u16_t unsent_optlen; + + /* @todo: this could be sped up by keeping last_unsent in the pcb */ + for (last_unsent = pcb->unsent; last_unsent->next != NULL; + last_unsent = last_unsent->next); + + /* Usable space at the end of the last unsent segment */ + unsent_optlen = LWIP_TCP_OPT_LENGTH(last_unsent->flags); + space = pcb->mss - (last_unsent->len + unsent_optlen); + + /* + * Phase 1: Copy data directly into an oversized pbuf. + * + * The number of bytes copied is recorded in the oversize_used + * variable. The actual copying is done at the bottom of the + * function. + */ +#if TCP_OVERSIZE +#if TCP_OVERSIZE_DBGCHECK + /* check that pcb->unsent_oversize matches last_unsent->unsent_oversize */ + LWIP_ASSERT("unsent_oversize mismatch (pcb vs. last_unsent)", + pcb->unsent_oversize == last_unsent->oversize_left); +#endif /* TCP_OVERSIZE_DBGCHECK */ + oversize = pcb->unsent_oversize; + if (oversize > 0) { + LWIP_ASSERT("inconsistent oversize vs. space", oversize_used <= space); + seg = last_unsent; + oversize_used = oversize < len ? oversize : len; + pos += oversize_used; + oversize -= oversize_used; + space -= oversize_used; + } + /* now we are either finished or oversize is zero */ + LWIP_ASSERT("inconsistend oversize vs. len", (oversize == 0) || (pos == len)); +#endif /* TCP_OVERSIZE */ + + /* + * Phase 2: Chain a new pbuf to the end of pcb->unsent. + * + * We don't extend segments containing SYN/FIN flags or options + * (len==0). The new pbuf is kept in concat_p and pbuf_cat'ed at + * the end. + */ + if ((pos < len) && (space > 0) && (last_unsent->len > 0)) { + u16_t seglen = space < len - pos ? space : len - pos; + seg = last_unsent; + + /* Create a pbuf with a copy or reference to seglen bytes. We + * can use PBUF_RAW here since the data appears in the middle of + * a segment. A header will never be prepended. */ + if (apiflags & TCP_WRITE_FLAG_COPY) { + /* Data is copied */ + if ((concat_p = tcp_pbuf_prealloc(PBUF_RAW, seglen, space, &oversize, pcb, apiflags, 1)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, + ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n", + seglen)); + goto memerr; + } +#if TCP_OVERSIZE_DBGCHECK + last_unsent->oversize_left = oversize; +#endif /* TCP_OVERSIZE_DBGCHECK */ + TCP_DATA_COPY2(concat_p->payload, (u8_t*)arg + pos, seglen, &concat_chksum, &concat_chksum_swapped); +#if TCP_CHECKSUM_ON_COPY + concat_chksummed += seglen; +#endif /* TCP_CHECKSUM_ON_COPY */ + } else { + /* Data is not copied */ + if ((concat_p = pbuf_alloc(PBUF_RAW, seglen, PBUF_ROM)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, + ("tcp_write: could not allocate memory for zero-copy pbuf\n")); + goto memerr; + } +#if TCP_CHECKSUM_ON_COPY + /* calculate the checksum of nocopy-data */ + tcp_seg_add_chksum(~inet_chksum((u8_t*)arg + pos, seglen), seglen, + &concat_chksum, &concat_chksum_swapped); + concat_chksummed += seglen; +#endif /* TCP_CHECKSUM_ON_COPY */ + /* reference the non-volatile payload data */ + concat_p->payload = (u8_t*)arg + pos; + } + + pos += seglen; + queuelen += pbuf_clen(concat_p); + } + } else { +#if TCP_OVERSIZE + LWIP_ASSERT("unsent_oversize mismatch (pcb->unsent is NULL)", + pcb->unsent_oversize == 0); +#endif /* TCP_OVERSIZE */ + } + + /* + * Phase 3: Create new segments. + * + * The new segments are chained together in the local 'queue' + * variable, ready to be appended to pcb->unsent. + */ + while (pos < len) { + struct pbuf *p; + u16_t left = len - pos; + u16_t max_len = pcb->mss - optlen; + u16_t seglen = left > max_len ? max_len : left; +#if TCP_CHECKSUM_ON_COPY + u16_t chksum = 0; + u8_t chksum_swapped = 0; +#endif /* TCP_CHECKSUM_ON_COPY */ + + if (apiflags & TCP_WRITE_FLAG_COPY) { + /* If copy is set, memory should be allocated and data copied + * into pbuf */ + if ((p = tcp_pbuf_prealloc(PBUF_TRANSPORT, seglen + optlen, pcb->mss, &oversize, pcb, apiflags, queue == NULL)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n", seglen)); + goto memerr; + } + LWIP_ASSERT("tcp_write: check that first pbuf can hold the complete seglen", + (p->len >= seglen)); + TCP_DATA_COPY2((char *)p->payload + optlen, (u8_t*)arg + pos, seglen, &chksum, &chksum_swapped); + } else { + /* Copy is not set: First allocate a pbuf for holding the data. + * Since the referenced data is available at least until it is + * sent out on the link (as it has to be ACKed by the remote + * party) we can safely use PBUF_ROM instead of PBUF_REF here. + */ + struct pbuf *p2; +#if TCP_OVERSIZE + LWIP_ASSERT("oversize == 0", oversize == 0); +#endif /* TCP_OVERSIZE */ + if ((p2 = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_ROM)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: could not allocate memory for zero-copy pbuf\n")); + goto memerr; + } +#if TCP_CHECKSUM_ON_COPY + /* calculate the checksum of nocopy-data */ + chksum = ~inet_chksum((u8_t*)arg + pos, seglen); +#endif /* TCP_CHECKSUM_ON_COPY */ + /* reference the non-volatile payload data */ + p2->payload = (u8_t*)arg + pos; + + /* Second, allocate a pbuf for the headers. */ + if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) { + /* If allocation fails, we have to deallocate the data pbuf as + * well. */ + pbuf_free(p2); + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: could not allocate memory for header pbuf\n")); + goto memerr; + } + /* Concatenate the headers and data pbufs together. */ + pbuf_cat(p/*header*/, p2/*data*/); + } + + queuelen += pbuf_clen(p); + + /* Now that there are more segments queued, we check again if the + * length of the queue exceeds the configured maximum or + * overflows. */ + if ((queuelen > TCP_SND_QUEUELEN) || (queuelen > TCP_SNDQUEUELEN_OVERFLOW)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: queue too long %"U16_F" (%"U16_F")\n", queuelen, TCP_SND_QUEUELEN)); + pbuf_free(p); + goto memerr; + } + + if ((seg = tcp_create_segment(pcb, p, 0, pcb->snd_lbb + pos, optflags)) == NULL) { + goto memerr; + } +#if TCP_OVERSIZE_DBGCHECK + seg->oversize_left = oversize; +#endif /* TCP_OVERSIZE_DBGCHECK */ +#if TCP_CHECKSUM_ON_COPY + seg->chksum = chksum; + seg->chksum_swapped = chksum_swapped; + seg->flags |= TF_SEG_DATA_CHECKSUMMED; +#endif /* TCP_CHECKSUM_ON_COPY */ + + /* first segment of to-be-queued data? */ + if (queue == NULL) { + queue = seg; + } else { + /* Attach the segment to the end of the queued segments */ + LWIP_ASSERT("prev_seg != NULL", prev_seg != NULL); + prev_seg->next = seg; + } + /* remember last segment of to-be-queued data for next iteration */ + prev_seg = seg; + + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE, ("tcp_write: queueing %"U32_F":%"U32_F"\n", + ntohl(seg->tcphdr->seqno), + ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg))); + + pos += seglen; + } + + /* + * All three segmentation phases were successful. We can commit the + * transaction. + */ + + /* + * Phase 1: If data has been added to the preallocated tail of + * last_unsent, we update the length fields of the pbuf chain. + */ +#if TCP_OVERSIZE + if (oversize_used > 0) { + struct pbuf *p; + /* Bump tot_len of whole chain, len of tail */ + for (p = last_unsent->p; p; p = p->next) { + p->tot_len += oversize_used; + if (p->next == NULL) { + TCP_DATA_COPY((char *)p->payload + p->len, arg, oversize_used, last_unsent); + p->len += oversize_used; + } + } + last_unsent->len += oversize_used; +#if TCP_OVERSIZE_DBGCHECK + last_unsent->oversize_left -= oversize_used; +#endif /* TCP_OVERSIZE_DBGCHECK */ + } + pcb->unsent_oversize = oversize; +#endif /* TCP_OVERSIZE */ + + /* + * Phase 2: concat_p can be concatenated onto last_unsent->p + */ + if (concat_p != NULL) { + LWIP_ASSERT("tcp_write: cannot concatenate when pcb->unsent is empty", + (last_unsent != NULL)); + pbuf_cat(last_unsent->p, concat_p); + last_unsent->len += concat_p->tot_len; +#if TCP_CHECKSUM_ON_COPY + if (concat_chksummed) { + tcp_seg_add_chksum(concat_chksum, concat_chksummed, &last_unsent->chksum, + &last_unsent->chksum_swapped); + last_unsent->flags |= TF_SEG_DATA_CHECKSUMMED; + } +#endif /* TCP_CHECKSUM_ON_COPY */ + } + + /* + * Phase 3: Append queue to pcb->unsent. Queue may be NULL, but that + * is harmless + */ + if (last_unsent == NULL) { + pcb->unsent = queue; + } else { + last_unsent->next = queue; + } + + /* + * Finally update the pcb state. + */ + pcb->snd_lbb += len; + pcb->snd_buf -= len; + pcb->snd_queuelen = queuelen; + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: %"S16_F" (after enqueued)\n", + pcb->snd_queuelen)); + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_write: valid queue length", + pcb->unacked != NULL || pcb->unsent != NULL); + } + + /* Set the PSH flag in the last segment that we enqueued. */ + if (seg != NULL && seg->tcphdr != NULL && ((apiflags & TCP_WRITE_FLAG_MORE)==0)) { + TCPH_SET_FLAG(seg->tcphdr, TCP_PSH); + } + + return ERR_OK; +memerr: + pcb->flags |= TF_NAGLEMEMERR; + TCP_STATS_INC(tcp.memerr); + + if (concat_p != NULL) { + pbuf_free(concat_p); + } + if (queue != NULL) { + tcp_segs_free(queue); + } + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_write: valid queue length", pcb->unacked != NULL || + pcb->unsent != NULL); + } + LWIP_DEBUGF(TCP_QLEN_DEBUG | LWIP_DBG_STATE, ("tcp_write: %"S16_F" (with mem err)\n", pcb->snd_queuelen)); + return ERR_MEM; +} + +/** + * Enqueue TCP options for transmission. + * + * Called by tcp_connect(), tcp_listen_input(), and tcp_send_ctrl(). + * + * @param pcb Protocol control block for the TCP connection. + * @param flags TCP header flags to set in the outgoing segment. + * @param optdata pointer to TCP options, or NULL. + * @param optlen length of TCP options in bytes. + */ +err_t +tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags) +{ + struct pbuf *p; + struct tcp_seg *seg; + u8_t optflags = 0; + u8_t optlen = 0; + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue_flags: queuelen: %"U16_F"\n", (u16_t)pcb->snd_queuelen)); + + LWIP_ASSERT("tcp_enqueue_flags: need either TCP_SYN or TCP_FIN in flags (programmer violates API)", + (flags & (TCP_SYN | TCP_FIN)) != 0); + + /* check for configured max queuelen and possible overflow */ + if ((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue_flags: too long queue %"U16_F" (max %"U16_F")\n", + pcb->snd_queuelen, TCP_SND_QUEUELEN)); + TCP_STATS_INC(tcp.memerr); + pcb->flags |= TF_NAGLEMEMERR; + return ERR_MEM; + } + + if (flags & TCP_SYN) { + optflags = TF_SEG_OPTS_MSS; + } +#if LWIP_TCP_TIMESTAMPS + if ((pcb->flags & TF_TIMESTAMP)) { + optflags |= TF_SEG_OPTS_TS; + } +#endif /* LWIP_TCP_TIMESTAMPS */ + optlen = LWIP_TCP_OPT_LENGTH(optflags); + + /* tcp_enqueue_flags is always called with either SYN or FIN in flags. + * We need one available snd_buf byte to do that. + * This means we can't send FIN while snd_buf==0. A better fix would be to + * not include SYN and FIN sequence numbers in the snd_buf count. */ + if (pcb->snd_buf == 0) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue_flags: no send buffer available\n")); + TCP_STATS_INC(tcp.memerr); + return ERR_MEM; + } + + /* Allocate pbuf with room for TCP header + options */ + if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) { + pcb->flags |= TF_NAGLEMEMERR; + TCP_STATS_INC(tcp.memerr); + return ERR_MEM; + } + LWIP_ASSERT("tcp_enqueue_flags: check that first pbuf can hold optlen", + (p->len >= optlen)); + + /* Allocate memory for tcp_seg, and fill in fields. */ + if ((seg = tcp_create_segment(pcb, p, flags, pcb->snd_lbb, optflags)) == NULL) { + pcb->flags |= TF_NAGLEMEMERR; + TCP_STATS_INC(tcp.memerr); + return ERR_MEM; + } + LWIP_ASSERT("seg->tcphdr not aligned", ((mem_ptr_t)seg->tcphdr % MEM_ALIGNMENT) == 0); + LWIP_ASSERT("tcp_enqueue_flags: invalid segment length", seg->len == 0); + + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE, + ("tcp_enqueue_flags: queueing %"U32_F":%"U32_F" (0x%"X16_F")\n", + ntohl(seg->tcphdr->seqno), + ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg), + (u16_t)flags)); + + /* Now append seg to pcb->unsent queue */ + if (pcb->unsent == NULL) { + pcb->unsent = seg; + } else { + struct tcp_seg *useg; + for (useg = pcb->unsent; useg->next != NULL; useg = useg->next); + useg->next = seg; + } +#if TCP_OVERSIZE + /* The new unsent tail has no space */ + pcb->unsent_oversize = 0; +#endif /* TCP_OVERSIZE */ + + /* SYN and FIN bump the sequence number */ + if ((flags & TCP_SYN) || (flags & TCP_FIN)) { + pcb->snd_lbb++; + /* optlen does not influence snd_buf */ + pcb->snd_buf--; + } + if (flags & TCP_FIN) { + pcb->flags |= TF_FIN; + } + + /* update number of segments on the queues */ + pcb->snd_queuelen += pbuf_clen(seg->p); + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue_flags: %"S16_F" (after enqueued)\n", pcb->snd_queuelen)); + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_enqueue_flags: invalid queue length", + pcb->unacked != NULL || pcb->unsent != NULL); + } + + return ERR_OK; +} + + +#if LWIP_TCP_TIMESTAMPS +/* Build a timestamp option (12 bytes long) at the specified options pointer) + * + * @param pcb tcp_pcb + * @param opts option pointer where to store the timestamp option + */ +static void ICACHE_FLASH_ATTR +tcp_build_timestamp_option(struct tcp_pcb *pcb, u32_t *opts) +{ + /* Pad with two NOP options to make everything nicely aligned */ + opts[0] = PP_HTONL(0x0101080A); + opts[1] = htonl(sys_now()); + opts[2] = htonl(pcb->ts_recent); +} +#endif + +/** Send an ACK without data. + * + * @param pcb Protocol control block for the TCP connection to send the ACK + */ +err_t +tcp_send_empty_ack(struct tcp_pcb *pcb) +{ + struct pbuf *p; + struct tcp_hdr *tcphdr; + u8_t optlen = 0; + +#if LWIP_TCP_TIMESTAMPS + if (pcb->flags & TF_TIMESTAMP) { + optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS); + } +#endif + + p = tcp_output_alloc_header(pcb, optlen, 0, htonl(pcb->snd_nxt)); + if (p == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: (ACK) could not allocate pbuf\n")); + return ERR_BUF; + } + tcphdr = (struct tcp_hdr *)p->payload; + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, + ("tcp_output: sending ACK for %"U32_F"\n", pcb->rcv_nxt)); + /* remove ACK flags from the PCB, as we send an empty ACK now */ + pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); + + /* NB. MSS option is only sent on SYNs, so ignore it here */ +#if LWIP_TCP_TIMESTAMPS + pcb->ts_lastacksent = pcb->rcv_nxt; + + if (pcb->flags & TF_TIMESTAMP) { + tcp_build_timestamp_option(pcb, (u32_t *)(tcphdr + 1)); + } +#endif + +#if CHECKSUM_GEN_TCP + tcphdr->chksum = inet_chksum_pseudo(p, &(pcb->local_ip), &(pcb->remote_ip), + IP_PROTO_TCP, p->tot_len); +#endif +#if LWIP_NETIF_HWADDRHINT + ip_output_hinted(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos, + IP_PROTO_TCP, &(pcb->addr_hint)); +#else /* LWIP_NETIF_HWADDRHINT*/ + ip_output(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos, + IP_PROTO_TCP); +#endif /* LWIP_NETIF_HWADDRHINT*/ + pbuf_free(p); + + return ERR_OK; +} + +/** + * Find out what we can send and send it + *���Ϳ��ƿ黺����еı��Ķ� + * @param pcb Protocol control block for the TCP connection to send data + * @return ERR_OK if data has been sent or nothing to send + * another err_t on error + */ +err_t ICACHE_FLASH_ATTR +tcp_output(struct tcp_pcb *pcb) +{ + struct tcp_seg *seg, *useg; + u32_t wnd, snd_nxt; +#if TCP_CWND_DEBUG + s16_t i = 0; +#endif /* TCP_CWND_DEBUG */ + /* First, check if we are invoked by the TCP input processing + code. If so, we do not output anything. Instead, we rely on the + input processing code to call us when input processing is done + with. �����ƿ鵱ǰ������ݱ����?ֱ�ӷ���*/ + if (tcp_input_pcb == pcb) { + return ERR_OK; + } + + wnd = LWIP_MIN(pcb->snd_wnd, pcb->cwnd);//�ӷ��ʹ��ں������ȡС�ߵõ���Ч���ʹ��� + + seg = pcb->unsent; + + /* If the TF_ACK_NOW flag is set and no data will be sent (either + * because the ->unsent queue is empty or because the window does + * not allow it), construct an empty ACK segment and send it. + * + * If data is to be sent, we will just piggyback the ACK (see below). + */ + if (pcb->flags & TF_ACK_NOW && + (seg == NULL || + ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd)) { + return tcp_send_empty_ack(pcb);//����ֻ��ACK�ı��Ķ� + } + + /* useg should point to last segment on unacked queue */ + useg = pcb->unacked; + if (useg != NULL) { + for (; useg->next != NULL; useg = useg->next);//�õ�β�� + } + +#if TCP_OUTPUT_DEBUG + if (seg == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: nothing to send (%p)\n", + (void*)pcb->unsent)); + } +#endif /* TCP_OUTPUT_DEBUG */ +#if TCP_CWND_DEBUG + if (seg == NULL) { + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U16_F + ", cwnd %"U16_F", wnd %"U32_F + ", seg == NULL, ack %"U32_F"\n", + pcb->snd_wnd, pcb->cwnd, wnd, pcb->lastack)); + } else { + LWIP_DEBUGF(TCP_CWND_DEBUG, + ("tcp_output: snd_wnd %"U16_F", cwnd %"U16_F", wnd %"U32_F + ", effwnd %"U32_F", seq %"U32_F", ack %"U32_F"\n", + pcb->snd_wnd, pcb->cwnd, wnd, + ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len, + ntohl(seg->tcphdr->seqno), pcb->lastack)); + } +#endif /* TCP_CWND_DEBUG */ + /* data available and window allows it to be sent? + *��ǰ��Ч�������?�ķ��ͣ�ѭ�����ͱ��ģ�ֱ�������*/ + while (seg != NULL && + ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) { + LWIP_ASSERT("RST not expected here!", + (TCPH_FLAGS(seg->tcphdr) & TCP_RST) == 0); + /* Stop sending if the nagle algorithm would prevent it + * Don't stop: + * - if tcp_write had a memory error before (prevent delayed ACK timeout) or + * - if FIN was already enqueued for this PCB (SYN is always alone in a segment - + * either seg->next != NULL or pcb->unacked == NULL; + * RST is no sent using tcp_write/tcp_output. + */ + if((tcp_do_output_nagle(pcb) == 0) && + ((pcb->flags & (TF_NAGLEMEMERR | TF_FIN)) == 0)){ + break; + } +#if TCP_CWND_DEBUG + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U16_F", cwnd %"U16_F", wnd %"U32_F", effwnd %"U32_F", seq %"U32_F", ack %"U32_F", i %"S16_F"\n", + pcb->snd_wnd, pcb->cwnd, wnd, + ntohl(seg->tcphdr->seqno) + seg->len - + pcb->lastack, + ntohl(seg->tcphdr->seqno), pcb->lastack, i)); + ++i; +#endif /* TCP_CWND_DEBUG */ + + pcb->unsent = seg->next; + + if (pcb->state != SYN_SENT) { + TCPH_SET_FLAG(seg->tcphdr, TCP_ACK);//��д�ײ�ACK��־ + pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);//����־λ + } + + tcp_output_segment(seg, pcb);//���ú����ͱ��Ķ� + + snd_nxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg);//����snd_nxt + if (TCP_SEQ_LT(pcb->snd_nxt, snd_nxt)) { + pcb->snd_nxt = snd_nxt;//����Ҫ���͵���ݱ�� + } + /* put segment on unacknowledged list if length > 0 + */ + if (TCP_TCPLEN(seg) > 0) { + seg->next = NULL; + /* unacked list is empty? ֱ�ӹҽ�*/ + if (pcb->unacked == NULL) { + pcb->unacked = seg; + useg = seg; + /* unacked list is not empty?����ǰ���İ�˳����֯�ڶ����� */ + } else { + /* In the case of fast retransmit, the packet should not go to the tail + * of the unacked queue, but rather somewhere before it. We need to check for + * this case. -STJ Jul 27, 2004 */ //���ǰ���ĵ����кŵ��ڶ���β���������кţ� + //�Ӷ����ײ���ʼ + if (TCP_SEQ_LT(ntohl(seg->tcphdr->seqno), ntohl(useg->tcphdr->seqno))) { + /* add segment to before tail of unacked list, keeping the list sorted */ + struct tcp_seg **cur_seg = &(pcb->unacked); + while (*cur_seg && + TCP_SEQ_LT(ntohl((*cur_seg)->tcphdr->seqno), ntohl(seg->tcphdr->seqno))) { + cur_seg = &((*cur_seg)->next ); + } + seg->next = (*cur_seg); + (*cur_seg) = seg; + } else {//���������ߣ������δȷ�϶���ĩβ + /* add segment to tail of unacked list */ + useg->next = seg; + useg = useg->next; + } + } + /* do not queue empty segments on the unacked list */ + } else {//���Ķγ���Ϊ0��ֱ��ɾ�������ش� + tcp_seg_free(seg); + } + seg = pcb->unsent;//������һ�����Ķ� + } +#if TCP_OVERSIZE + if (pcb->unsent == NULL) { + /* last unsent has been removed, reset unsent_oversize */ + pcb->unsent_oversize = 0; + } +#endif /* TCP_OVERSIZE */ + +//���ʹ��������±��IJ��ܷ��ͣ��������㴰��̽�⡣ + if (seg != NULL && pcb->persist_backoff == 0 && + ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > pcb->snd_wnd) { + /* prepare for persist timer */ + pcb->persist_cnt = 0; + pcb->persist_backoff = 1; + } + + pcb->flags &= ~TF_NAGLEMEMERR;//���ڴ�����־ + return ERR_OK; +} + +/** + * Called by tcp_output() to actually send a TCP segment over IP. + * + * @param seg the tcp_seg to send + * @param pcb the tcp_pcb for the TCP connection used to send the segment + */ + +static void +tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb) +{ + u16_t len; + struct netif *netif; + u32_t *opts; + /** @bug Exclude retransmitted segments from this count. */ + snmp_inc_tcpoutsegs(); + + /* The TCP header has already been constructed, but the ackno and + wnd fields remain. */ + seg->tcphdr->ackno = htonl(pcb->rcv_nxt); + + /* advertise our receive window size in this TCP segment */ + seg->tcphdr->wnd = htons(pcb->rcv_ann_wnd); + + pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd; + + /* Add any requested options. NB MSS option is only set on SYN + packets, so ignore it here */ + LWIP_ASSERT("seg->tcphdr not aligned", ((mem_ptr_t)seg->tcphdr % MEM_ALIGNMENT) == 0); + opts = (u32_t *)(void *)(seg->tcphdr + 1); + if (seg->flags & TF_SEG_OPTS_MSS) { + TCP_BUILD_MSS_OPTION(*opts); + opts += 1; + } +#if LWIP_TCP_TIMESTAMPS + pcb->ts_lastacksent = pcb->rcv_nxt; + + if (seg->flags & TF_SEG_OPTS_TS) { + tcp_build_timestamp_option(pcb, opts); + opts += 3; + } +#endif + + /* Set retransmission timer running if it is not currently enabled + This must be set before checking the route. modify by ives at 2014.4.24*/ + if (pcb->rtime == -1) { + pcb->rtime = 0; + } + + /* If we don't have a local IP address, we get one by + calling ip_route(). */ + if (ip_addr_isany(&(pcb->local_ip))) { + netif = ip_route(&(pcb->remote_ip)); + if (netif == NULL) { + return; + } + ip_addr_copy(pcb->local_ip, netif->ip_addr); + } + + if (pcb->rttest == 0) { + pcb->rttest = tcp_ticks; + pcb->rtseq = ntohl(seg->tcphdr->seqno); + + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_output_segment: rtseq %"U32_F"\n", pcb->rtseq)); + } + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output_segment: %"U32_F":%"U32_F"\n", + htonl(seg->tcphdr->seqno), htonl(seg->tcphdr->seqno) + + seg->len)); + + len = (u16_t)((u8_t *)seg->tcphdr - (u8_t *)seg->p->payload); + + seg->p->len -= len; + seg->p->tot_len -= len; + + seg->p->payload = seg->tcphdr; + + seg->tcphdr->chksum = 0; +#if CHECKSUM_GEN_TCP +#if TCP_CHECKSUM_ON_COPY + { + u32_t acc; +#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK + u16_t chksum_slow = inet_chksum_pseudo(seg->p, &(pcb->local_ip), + &(pcb->remote_ip), + IP_PROTO_TCP, seg->p->tot_len); +#endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */ + if ((seg->flags & TF_SEG_DATA_CHECKSUMMED) == 0) { + LWIP_ASSERT("data included but not checksummed", + seg->p->tot_len == (TCPH_HDRLEN(seg->tcphdr) * 4)); + } + + /* rebuild TCP header checksum (TCP header changes for retransmissions!) */ + acc = inet_chksum_pseudo_partial(seg->p, &(pcb->local_ip), + &(pcb->remote_ip), + IP_PROTO_TCP, seg->p->tot_len, TCPH_HDRLEN(seg->tcphdr) * 4); + /* add payload checksum */ + if (seg->chksum_swapped) { + seg->chksum = SWAP_BYTES_IN_WORD(seg->chksum); + seg->chksum_swapped = 0; + } + acc += (u16_t)~(seg->chksum); + seg->tcphdr->chksum = FOLD_U32T(acc); +#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK + if (chksum_slow != seg->tcphdr->chksum) { + LWIP_DEBUGF(TCP_DEBUG | LWIP_DBG_LEVEL_WARNING, + ("tcp_output_segment: calculated checksum is %"X16_F" instead of %"X16_F"\n", + seg->tcphdr->chksum, chksum_slow)); + seg->tcphdr->chksum = chksum_slow; + } +#endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */ + } +#else /* TCP_CHECKSUM_ON_COPY */ + seg->tcphdr->chksum = inet_chksum_pseudo(seg->p, &(pcb->local_ip), + &(pcb->remote_ip), + IP_PROTO_TCP, seg->p->tot_len); +#endif /* TCP_CHECKSUM_ON_COPY */ +#endif /* CHECKSUM_GEN_TCP */ + TCP_STATS_INC(tcp.xmit); + +#if LWIP_NETIF_HWADDRHINT + ip_output_hinted(seg->p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos, + IP_PROTO_TCP, &(pcb->addr_hint)); +#else /* LWIP_NETIF_HWADDRHINT*/ + ip_output(seg->p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos, + IP_PROTO_TCP); +#endif /* LWIP_NETIF_HWADDRHINT*/ +} + +/** + * Send a TCP RESET packet (empty segment with RST flag set) either to + * abort a connection or to show that there is no matching local connection + * for a received segment. + * + * Called by tcp_abort() (to abort a local connection), tcp_input() (if no + * matching local pcb was found), tcp_listen_input() (if incoming segment + * has ACK flag set) and tcp_process() (received segment in the wrong state) + * + * Since a RST segment is in most cases not sent for an active connection, + * tcp_rst() has a number of arguments that are taken from a tcp_pcb for + * most other segment output functions. + * + * @param seqno the sequence number to use for the outgoing segment + * @param ackno the acknowledge number to use for the outgoing segment + * @param local_ip the local IP address to send the segment from + * @param remote_ip the remote IP address to send the segment to + * @param local_port the local TCP port to send the segment from + * @param remote_port the remote TCP port to send the segment to + */ +void +tcp_rst(u32_t seqno, u32_t ackno, + ip_addr_t *local_ip, ip_addr_t *remote_ip, + u16_t local_port, u16_t remote_port) +{ + struct pbuf *p; + struct tcp_hdr *tcphdr; + p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM); + if (p == NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_rst: could not allocate memory for pbuf\n")); + return; + } + LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr", + (p->len >= sizeof(struct tcp_hdr))); + + tcphdr = (struct tcp_hdr *)p->payload; + tcphdr->src = htons(local_port); + tcphdr->dest = htons(remote_port); + tcphdr->seqno = htonl(seqno); + tcphdr->ackno = htonl(ackno); + TCPH_HDRLEN_FLAGS_SET(tcphdr, TCP_HLEN/4, TCP_RST | TCP_ACK); + tcphdr->wnd = PP_HTONS(TCP_WND); + tcphdr->chksum = 0; + tcphdr->urgp = 0; + +#if CHECKSUM_GEN_TCP + tcphdr->chksum = inet_chksum_pseudo(p, local_ip, remote_ip, + IP_PROTO_TCP, p->tot_len); +#endif + TCP_STATS_INC(tcp.xmit); + snmp_inc_tcpoutrsts(); + /* Send output with hardcoded TTL since we have no access to the pcb */ + ip_output(p, local_ip, remote_ip, TCP_TTL, 0, IP_PROTO_TCP); + pbuf_free(p); + LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_rst: seqno %"U32_F" ackno %"U32_F".\n", seqno, ackno)); +} + +/** + * Requeue all unacked segments for retransmission + * + * Called by tcp_slowtmr() for slow retransmission. + * + * @param pcb the tcp_pcb for which to re-enqueue all unacked segments + */ +void +tcp_rexmit_rto(struct tcp_pcb *pcb) +{ + struct tcp_seg *seg; + struct tcp_seg *t0_head = NULL, *t0_tail = NULL; /* keep in unacked */ + struct tcp_seg *t1_head = NULL, *t1_tail = NULL; /* link to unsent */ + bool t0_1st = true, t1_1st = true; + + if (pcb->unacked == NULL) { + return; + } + +#if 1 /* by Snake: resolve the bug of pbuf reuse */ + seg = pcb->unacked; + while (seg != NULL) { + if (seg->p->eb) { + if (t0_1st) { + t0_head = t0_tail = seg; + t0_1st = false; + } else { + t0_tail->next = seg; + t0_tail = seg; + } + seg = seg->next; + t0_tail->next = NULL; + } else { + if (t1_1st) { + t1_head = t1_tail = seg; + t1_1st = false; + } else { + t1_tail->next = seg; + t1_tail = seg; + } + seg = seg->next; + t1_tail->next = NULL; + } + } + if (t1_head && t1_tail) { + t1_tail->next = pcb->unsent; + pcb->unsent = t1_head; + } + pcb->unacked = t0_head; + +#else + + /* Move all unacked segments to the head of the unsent queue */ + for (seg = pcb->unacked; seg->next != NULL; seg = seg->next); + /* concatenate unsent queue after unacked queue */ + seg->next = pcb->unsent; + /* unsent queue is the concatenated queue (of unacked, unsent) */ + pcb->unsent = pcb->unacked; + /* unacked queue is now empty */ + pcb->unacked = NULL; +#endif + + /* increment number of retransmissions */ + ++pcb->nrtx; + + /* Don't take any RTT measurements after retransmitting. */ + pcb->rttest = 0; + + /* Do the actual retransmission */ + tcp_output(pcb); +} + +/** + * Requeue the first unacked segment for retransmission + * + * Called by tcp_receive() for fast retramsmit. + * + * @param pcb the tcp_pcb for which to retransmit the first unacked segment + */ +void +tcp_rexmit(struct tcp_pcb *pcb) +{ + struct tcp_seg *seg; + struct tcp_seg **cur_seg; + + if (pcb->unacked == NULL) { + return; + } + + /* Move the first unacked segment to the unsent queue */ + /* Keep the unsent queue sorted. */ + seg = pcb->unacked; + pcb->unacked = seg->next; + + cur_seg = &(pcb->unsent); + while (*cur_seg && + TCP_SEQ_LT(ntohl((*cur_seg)->tcphdr->seqno), ntohl(seg->tcphdr->seqno))) { + cur_seg = &((*cur_seg)->next ); + } + seg->next = *cur_seg; + *cur_seg = seg; + + ++pcb->nrtx; + + /* Don't take any rtt measurements after retransmitting. */ + pcb->rttest = 0; + + /* Do the actual retransmission. */ + snmp_inc_tcpretranssegs(); + /* No need to call tcp_output: we are always called from tcp_input() + and thus tcp_output directly returns. */ +} + + +/** + * Handle retransmission after three dupacks received + * + * @param pcb the tcp_pcb for which to retransmit the first unacked segment + */ +void +tcp_rexmit_fast(struct tcp_pcb *pcb) +{ + if (pcb->unacked != NULL && !(pcb->flags & TF_INFR)) { + /* This is fast retransmit. Retransmit the first unacked segment. */ + LWIP_DEBUGF(TCP_FR_DEBUG, + ("tcp_receive: dupacks %"U16_F" (%"U32_F + "), fast retransmit %"U32_F"\n", + (u16_t)pcb->dupacks, pcb->lastack, + ntohl(pcb->unacked->tcphdr->seqno))); + tcp_rexmit(pcb); + + /* Set ssthresh to half of the minimum of the current + * cwnd and the advertised window */ + if (pcb->cwnd > pcb->snd_wnd) { + pcb->ssthresh = pcb->snd_wnd / 2; + } else { + pcb->ssthresh = pcb->cwnd / 2; + } + + /* The minimum value for ssthresh should be 2 MSS */ + if (pcb->ssthresh < 2*pcb->mss) { + LWIP_DEBUGF(TCP_FR_DEBUG, + ("tcp_receive: The minimum value for ssthresh %"U16_F + " should be min 2 mss %"U16_F"...\n", + pcb->ssthresh, 2*pcb->mss)); + pcb->ssthresh = 2*pcb->mss; + } + + pcb->cwnd = pcb->ssthresh + 3 * pcb->mss; + pcb->flags |= TF_INFR; + } +} + + +/** + * Send keepalive packets to keep a connection active although + * no data is sent over it. + * + * Called by tcp_slowtmr() + * + * @param pcb the tcp_pcb for which to send a keepalive packet + */ +void +tcp_keepalive(struct tcp_pcb *pcb) +{ + struct pbuf *p; + struct tcp_hdr *tcphdr; + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: sending KEEPALIVE probe to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip), + ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip))); + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: tcp_ticks %"U32_F" pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n", + tcp_ticks, pcb->tmr, pcb->keep_cnt_sent)); + + p = tcp_output_alloc_header(pcb, 0, 0, htonl(pcb->snd_nxt - 1)); + if(p == NULL) { + LWIP_DEBUGF(TCP_DEBUG, + ("tcp_keepalive: could not allocate memory for pbuf\n")); + return; + } + tcphdr = (struct tcp_hdr *)p->payload; + +#if CHECKSUM_GEN_TCP + tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip, &pcb->remote_ip, + IP_PROTO_TCP, p->tot_len); +#endif + TCP_STATS_INC(tcp.xmit); + + /* Send output to IP */ +#if LWIP_NETIF_HWADDRHINT + ip_output_hinted(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP, + &(pcb->addr_hint)); +#else /* LWIP_NETIF_HWADDRHINT*/ + ip_output(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP); +#endif /* LWIP_NETIF_HWADDRHINT*/ + + pbuf_free(p); + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: seqno %"U32_F" ackno %"U32_F".\n", + pcb->snd_nxt - 1, pcb->rcv_nxt)); +} + + +/** + * Send persist timer zero-window probes to keep a connection active + * when a window update is lost. + * + * Called by tcp_slowtmr() + * + * @param pcb the tcp_pcb for which to send a zero-window probe packet + */ +void +tcp_zero_window_probe(struct tcp_pcb *pcb) +{ + struct pbuf *p; + struct tcp_hdr *tcphdr; + struct tcp_seg *seg; + u16_t offset = 0; + u16_t len; + u8_t is_fin; + + LWIP_DEBUGF(TCP_DEBUG, + ("tcp_zero_window_probe: sending ZERO WINDOW probe to %" + U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip), + ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip))); + + LWIP_DEBUGF(TCP_DEBUG, + ("tcp_zero_window_probe: tcp_ticks %"U32_F + " pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n", + tcp_ticks, pcb->tmr, pcb->keep_cnt_sent)); + + seg = pcb->unacked; + + if(seg == NULL) { + seg = pcb->unsent; + } else { + struct ip_hdr *iphdr = NULL; + iphdr = (struct ip_hdr *)((char*)seg->p->payload + SIZEOF_ETH_HDR); + offset = IPH_HL(iphdr)*4; + offset += SIZEOF_ETH_HDR; + } + if(seg == NULL) { + return; + } + + is_fin = ((TCPH_FLAGS(seg->tcphdr) & TCP_FIN) != 0) && (seg->len == 0); + /* we want to send one seqno: either FIN or data (no options) */ + len = is_fin ? 0 : 1; + + p = tcp_output_alloc_header(pcb, 0, len, seg->tcphdr->seqno); + if(p == NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: no memory for pbuf\n")); + return; + } + tcphdr = (struct tcp_hdr *)p->payload; + + if (is_fin) { + /* FIN segment, no data */ + TCPH_FLAGS_SET(tcphdr, TCP_ACK | TCP_FIN); + } else { + /* Data segment, copy in one byte from the head of the unacked queue */ + struct tcp_hdr *thdr = (struct tcp_hdr *)seg->p->payload; + char *d = ((char *)p->payload + TCP_HLEN); + if (pcb->unacked == NULL) + pbuf_copy_partial(seg->p, d, 1, TCPH_HDRLEN(thdr) * 4); + else { + thdr = (struct tcp_hdr *)((char*)seg->p->payload + offset); + pbuf_copy_partial(seg->p, d, 1, TCPH_HDRLEN(thdr) * 4 + offset); + } + } + +#if CHECKSUM_GEN_TCP + tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip, &pcb->remote_ip, + IP_PROTO_TCP, p->tot_len); +#endif + TCP_STATS_INC(tcp.xmit); + + /* Send output to IP */ +#if LWIP_NETIF_HWADDRHINT + ip_output_hinted(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP, + &(pcb->addr_hint)); +#else /* LWIP_NETIF_HWADDRHINT*/ + ip_output(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP); +#endif /* LWIP_NETIF_HWADDRHINT*/ + + pbuf_free(p); + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: seqno %"U32_F + " ackno %"U32_F".\n", + pcb->snd_nxt - 1, pcb->rcv_nxt)); +} +#endif /* LWIP_TCP */ diff --git a/variants/lwip/lwip/core/timers.c b/variants/lwip/lwip/core/timers.c new file mode 100755 index 000000000..a2cc13554 --- /dev/null +++ b/variants/lwip/lwip/core/timers.c @@ -0,0 +1,513 @@ +/** + * @file + * Stack-internal timers implementation. + * This file includes timer callbacks for stack-internal timers as well as + * functions to set up or stop timers and check for expired timers. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * Simon Goldschmidt + * + */ + +#include "lwip/opt.h" + +#include "lwip/timers.h" +#include "lwip/tcp_impl.h" + +#if LWIP_TIMERS + +#include "lwip/def.h" +#include "lwip/memp.h" +#include "lwip/tcpip.h" + +#include "lwip/ip_frag.h" +#include "netif/etharp.h" +#include "lwip/dhcp.h" +#include "lwip/autoip.h" +#include "lwip/igmp.h" +#include "lwip/dns.h" + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +/** The one and only timeout list */ +static struct sys_timeo *next_timeout = NULL; +#if NO_SYS +static u32_t timeouts_last_time; +#endif /* NO_SYS */ + +#if LWIP_TCP +/** global variable that shows if the tcp timer is currently scheduled or not */ +static int tcpip_tcp_timer_active; + +/** + * Timer callback function that calls tcp_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void ICACHE_FLASH_ATTR +tcpip_tcp_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + + /* call TCP timer handler */ + tcp_tmr(); + /* timer still needed? */ + if (tcp_active_pcbs || tcp_tw_pcbs) { + /* restart timer */ + sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL); + } else { + /* disable timer */ + tcpip_tcp_timer_active = 0; + } +} + +/** + * Called from TCP_REG when registering a new PCB: + * the reason is to have the TCP timer only running when + * there are active (or time-wait) PCBs. + */ +void +tcp_timer_needed(void) +{ + /* timer is off but needed again? */ + if (!tcpip_tcp_timer_active && (tcp_active_pcbs || tcp_tw_pcbs)) { + /* enable and start timer */ + tcpip_tcp_timer_active = 1; + sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL); + } +} + +/** + * Timer callback function that calls tcp_tmr() and reschedules itself. + * + * @param arg unused argument + */ +/* +static void ICACHE_FLASH_ATTR +tcp_timer_coarse(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: tcp_tmr()\n")); + tcp_tmr(); + sys_timeout(TCP_TMR_INTERVAL, tcp_timer_coarse, NULL); +} +*/ +#endif /* LWIP_TCP */ + +#if IP_REASSEMBLY +/** + * Timer callback function that calls ip_reass_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void ICACHE_FLASH_ATTR +ip_reass_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: ip_reass_tmr()\n")); + ip_reass_tmr(); + sys_timeout(IP_TMR_INTERVAL, ip_reass_timer, NULL); +} +#endif /* IP_REASSEMBLY */ + +#if LWIP_ARP +/** + * Timer callback function that calls etharp_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void ICACHE_FLASH_ATTR +arp_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: etharp_tmr()\n")); + etharp_tmr(); + sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); +} +#endif /* LWIP_ARP */ + +#if LWIP_DHCP +/** + * Timer callback function that calls dhcp_coarse_tmr() and reschedules itself. + * + * @param arg unused argument + */ +extern void dhcps_coarse_tmr(void); +static void +dhcp_timer_coarse(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dhcp_coarse_tmr()\n")); + dhcp_coarse_tmr(); + dhcps_coarse_tmr(); + sys_timeout(DHCP_COARSE_TIMER_MSECS, dhcp_timer_coarse, NULL); +} + +/** + * Timer callback function that calls dhcp_fine_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +dhcp_timer_fine(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dhcp_fine_tmr()\n")); + dhcp_fine_tmr(); + sys_timeout(DHCP_FINE_TIMER_MSECS, dhcp_timer_fine, NULL); +} +#endif /* LWIP_DHCP */ + +#if LWIP_AUTOIP +/** + * Timer callback function that calls autoip_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +autoip_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: autoip_tmr()\n")); + autoip_tmr(); + sys_timeout(AUTOIP_TMR_INTERVAL, autoip_timer, NULL); +} +#endif /* LWIP_AUTOIP */ + +#if LWIP_IGMP +/** + * Timer callback function that calls igmp_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +igmp_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: igmp_tmr()\n")); + igmp_tmr(); + sys_timeout(IGMP_TMR_INTERVAL, igmp_timer, NULL); +} +#endif /* LWIP_IGMP */ + +#if LWIP_DNS +/** + * Timer callback function that calls dns_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +dns_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dns_tmr()\n")); + dns_tmr(); + sys_timeout(DNS_TMR_INTERVAL, dns_timer, NULL); +} +#endif /* LWIP_DNS */ + +/** Initialize this module */ +void sys_timeouts_init(void) +{ +#if IP_REASSEMBLY + sys_timeout(IP_TMR_INTERVAL, ip_reass_timer, NULL); +#endif /* IP_REASSEMBLY */ +#if LWIP_ARP + sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); +#endif /* LWIP_ARP */ +#if LWIP_DHCP + DHCP_MAXRTX = 0; + sys_timeout(DHCP_COARSE_TIMER_MSECS, dhcp_timer_coarse, NULL); + sys_timeout(DHCP_FINE_TIMER_MSECS, dhcp_timer_fine, NULL); +#endif /* LWIP_DHCP */ +#if LWIP_AUTOIP + sys_timeout(AUTOIP_TMR_INTERVAL, autoip_timer, NULL); +#endif /* LWIP_AUTOIP */ +#if LWIP_IGMP + sys_timeout(IGMP_TMR_INTERVAL, igmp_timer, NULL); +#endif /* LWIP_IGMP */ +#if LWIP_DNS + sys_timeout(DNS_TMR_INTERVAL, dns_timer, NULL); +#endif /* LWIP_DNS */ + +#if LWIP_TCP + sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL); +// sys_timeout(TCP_TMR_INTERVAL, tcp_timer_coarse, NULL); +#endif + +#if NO_SYS + /* Initialise timestamp for sys_check_timeouts */ + timeouts_last_time = NOW(); +#endif +} + +/** + * Create a one-shot timer (aka timeout). Timeouts are processed in the + * following cases: + * - while waiting for a message using sys_timeouts_mbox_fetch() + * - by calling sys_check_timeouts() (NO_SYS==1 only) + * + * @param msecs time in milliseconds after that the timer should expire + * @param handler callback function to call when msecs have elapsed + * @param arg argument to pass to the callback function + */ +#if LWIP_DEBUG_TIMERNAMES +void +sys_timeout_debug(u32_t msecs, sys_timeout_handler handler, void *arg, const char* handler_name) +#else /* LWIP_DEBUG_TIMERNAMES */ +void +sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg) +#endif /* LWIP_DEBUG_TIMERNAMES */ +{ + struct sys_timeo *timeout, *t; + + timeout = (struct sys_timeo *)memp_malloc(MEMP_SYS_TIMEOUT); + if (timeout == NULL) { + LWIP_ASSERT("sys_timeout: timeout != NULL, pool MEMP_SYS_TIMEOUT is empty", timeout != NULL); + return; + } + timeout->next = NULL; + timeout->h = handler; + timeout->arg = arg; + timeout->time = msecs; +#if LWIP_DEBUG_TIMERNAMES + timeout->handler_name = handler_name; + LWIP_DEBUGF(TIMERS_DEBUG, ("sys_timeout: %p msecs=%"U32_F" handler=%s arg=%p\n", + (void *)timeout, msecs, handler_name, (void *)arg)); +#endif /* LWIP_DEBUG_TIMERNAMES */ + + if (next_timeout == NULL) { + next_timeout = timeout; + return; + } + + if (next_timeout->time > msecs) { + next_timeout->time -= msecs; + timeout->next = next_timeout; + next_timeout = timeout; + } else { + for(t = next_timeout; t != NULL; t = t->next) { + timeout->time -= t->time; + if (t->next == NULL || t->next->time > timeout->time) { + if (t->next != NULL) { + t->next->time -= timeout->time; + } + timeout->next = t->next; + t->next = timeout; + break; + } + } + } +} + +/** + * Go through timeout list (for this task only) and remove the first matching + * entry, even though the timeout has not triggered yet. + * + * @note This function only works as expected if there is only one timeout + * calling 'handler' in the list of timeouts. + * + * @param handler callback function that would be called by the timeout + * @param arg callback argument that would be passed to handler +*/ +void +sys_untimeout(sys_timeout_handler handler, void *arg) +{ + struct sys_timeo *prev_t, *t; + + if (next_timeout == NULL) { + return; + } + + for (t = next_timeout, prev_t = NULL; t != NULL; prev_t = t, t = t->next) { + if ((t->h == handler) && (t->arg == arg)) { + /* We have a match */ + /* Unlink from previous in list */ + if (prev_t == NULL) { + next_timeout = t->next; + } else { + prev_t->next = t->next; + } + /* If not the last one, add time of this one back to next */ + if (t->next != NULL) { + t->next->time += t->time; + } + memp_free(MEMP_SYS_TIMEOUT, t); + return; + } + } + return; +} + +#if NO_SYS +extern uint8 timer2_ms_flag; +/** Handle timeouts for NO_SYS==1 (i.e. without using + * tcpip_thread/sys_timeouts_mbox_fetch(). Uses sys_now() to call timeout + * handler functions when timeouts expire. + * + * Must be called periodically from your main loop. + */ +void +sys_check_timeouts(void) +{ + struct sys_timeo *tmptimeout; + u32_t diff; + sys_timeout_handler handler; + void *arg; + int had_one; + u32_t now; + + now = NOW(); + if (next_timeout) { + /* this cares for wraparounds */ + if (timer2_ms_flag == 0) { + diff = LWIP_U32_DIFF(now, timeouts_last_time)/((APB_CLK_FREQ>>4)/1000); + } else { + diff = LWIP_U32_DIFF(now, timeouts_last_time)/((APB_CLK_FREQ>>8)/1000); + } + do + { + had_one = 0; + tmptimeout = next_timeout; + if (tmptimeout->time <= diff) { + /* timeout has expired */ + had_one = 1; + timeouts_last_time = now; + diff -= tmptimeout->time; + next_timeout = tmptimeout->next; + handler = tmptimeout->h; + arg = tmptimeout->arg; +#if LWIP_DEBUG_TIMERNAMES + if (handler != NULL) { + LWIP_DEBUGF(TIMERS_DEBUG, ("sct calling h=%s arg=%p\n", + tmptimeout->handler_name, arg)); + } +#endif /* LWIP_DEBUG_TIMERNAMES */ + memp_free(MEMP_SYS_TIMEOUT, tmptimeout); + if (handler != NULL) { + handler(arg); + } + } + /* repeat until all expired timers have been called */ + }while(had_one); + } +} + +/** Set back the timestamp of the last call to sys_check_timeouts() + * This is necessary if sys_check_timeouts() hasn't been called for a long + * time (e.g. while saving energy) to prevent all timer functions of that + * period being called. + */ +void +sys_restart_timeouts(void) +{ + timeouts_last_time = NOW(); +} + +#else /* NO_SYS */ + +/** + * Wait (forever) for a message to arrive in an mbox. + * While waiting, timeouts are processed. + * + * @param mbox the mbox to fetch the message from + * @param msg the place to store the message + */ +void +sys_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg) +{ + u32_t time_needed; + struct sys_timeo *tmptimeout; + sys_timeout_handler handler; + void *arg; + + again: + if (!next_timeout) { + time_needed = sys_arch_mbox_fetch(mbox, msg, 0); + } else { + if (next_timeout->time > 0) { + time_needed = sys_arch_mbox_fetch(mbox, msg, next_timeout->time); + } else { + time_needed = SYS_ARCH_TIMEOUT; + } + + if (time_needed == SYS_ARCH_TIMEOUT) { + /* If time == SYS_ARCH_TIMEOUT, a timeout occured before a message + could be fetched. We should now call the timeout handler and + deallocate the memory allocated for the timeout. */ + tmptimeout = next_timeout; + next_timeout = tmptimeout->next; + handler = tmptimeout->h; + arg = tmptimeout->arg; +#if LWIP_DEBUG_TIMERNAMES + if (handler != NULL) { + LWIP_DEBUGF(TIMERS_DEBUG, ("stmf calling h=%s arg=%p\n", + tmptimeout->handler_name, arg)); + } +#endif /* LWIP_DEBUG_TIMERNAMES */ + memp_free(MEMP_SYS_TIMEOUT, tmptimeout); + if (handler != NULL) { + /* For LWIP_TCPIP_CORE_LOCKING, lock the core before calling the + timeout handler function. */ + LOCK_TCPIP_CORE(); + handler(arg); + UNLOCK_TCPIP_CORE(); + } + LWIP_TCPIP_THREAD_ALIVE(); + + /* We try again to fetch a message from the mbox. */ + goto again; + } else { + /* If time != SYS_ARCH_TIMEOUT, a message was received before the timeout + occured. The time variable is set to the number of + milliseconds we waited for the message. */ + if (time_needed < next_timeout->time) { + next_timeout->time -= time_needed; + } else { + next_timeout->time = 0; + } + } + } +} + +#endif /* NO_SYS */ + +#else /* LWIP_TIMERS */ +/* Satisfy the TCP code which calls this function */ +void +tcp_timer_needed(void) +{ +} +#endif /* LWIP_TIMERS */ diff --git a/variants/lwip/lwip/core/udp.c b/variants/lwip/lwip/core/udp.c new file mode 100755 index 000000000..db12c48e9 --- /dev/null +++ b/variants/lwip/lwip/core/udp.c @@ -0,0 +1,977 @@ +/** + * @file + * User Datagram Protocol module + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + + +/* udp.c + * + * The code for the User Datagram Protocol UDP & UDPLite (RFC 3828). + * + */ + +/* @todo Check the use of '(struct udp_pcb).chksum_len_rx'! + */ + +#include "lwip/opt.h" + +#if LWIP_UDP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/udp.h" +#include "lwip/def.h" +#include "lwip/memp.h" +#include "lwip/inet_chksum.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/icmp.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "arch/perf.h" +#include "lwip/dhcp.h" + +#include + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +/* The list of UDP PCBs */ +/* exported in udp.h (was static) */ +struct udp_pcb *udp_pcbs; + +/** + * Process an incoming UDP datagram. + * + * Given an incoming UDP datagram (as a chain of pbufs) this function + * finds a corresponding UDP PCB and hands over the pbuf to the pcbs + * recv function. If no pcb is found or the datagram is incorrect, the + * pbuf is freed. + * + * @param p pbuf to be demultiplexed to a UDP PCB. + * @param inp network interface on which the datagram was received. + * + */ +void ICACHE_FLASH_ATTR +udp_input(struct pbuf *p, struct netif *inp) +{ + struct udp_hdr *udphdr; + struct udp_pcb *pcb, *prev; + struct udp_pcb *uncon_pcb; + struct ip_hdr *iphdr; + u16_t src, dest; + u8_t local_match; + u8_t broadcast; + + PERF_START; + + UDP_STATS_INC(udp.recv); + + iphdr = (struct ip_hdr *)p->payload; + + /* Check minimum length (IP header + UDP header) + * and move payload pointer to UDP header */ + if (p->tot_len < (IPH_HL(iphdr) * 4 + UDP_HLEN) || pbuf_header(p, -(s16_t)(IPH_HL(iphdr) * 4))) { + /* drop short packets */ + LWIP_DEBUGF(UDP_DEBUG, + ("udp_input: short UDP datagram (%"U16_F" bytes) discarded\n", p->tot_len)); + UDP_STATS_INC(udp.lenerr); + UDP_STATS_INC(udp.drop); + snmp_inc_udpinerrors(); + pbuf_free(p); + goto end; + } + + udphdr = (struct udp_hdr *)p->payload; + + /* is broadcast packet ? */ + broadcast = ip_addr_isbroadcast(¤t_iphdr_dest, inp); + + LWIP_DEBUGF(UDP_DEBUG, ("udp_input: received datagram of length %"U16_F"\n", p->tot_len)); + + /* convert src and dest ports to host byte order */ + src = ntohs(udphdr->src); + dest = ntohs(udphdr->dest); + + udp_debug_print(udphdr); + + /* print the UDP source and destination */ + LWIP_DEBUGF(UDP_DEBUG, + ("udp (%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F") <-- " + "(%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F")\n", + ip4_addr1_16(&iphdr->dest), ip4_addr2_16(&iphdr->dest), + ip4_addr3_16(&iphdr->dest), ip4_addr4_16(&iphdr->dest), ntohs(udphdr->dest), + ip4_addr1_16(&iphdr->src), ip4_addr2_16(&iphdr->src), + ip4_addr3_16(&iphdr->src), ip4_addr4_16(&iphdr->src), ntohs(udphdr->src))); + +#if LWIP_DHCP + pcb = NULL; + /* when LWIP_DHCP is active, packets to DHCP_CLIENT_PORT may only be processed by + the dhcp module, no other UDP pcb may use the local UDP port DHCP_CLIENT_PORT */ + if (dest == DHCP_CLIENT_PORT) { + /* all packets for DHCP_CLIENT_PORT not coming from DHCP_SERVER_PORT are dropped! */ + if (src == DHCP_SERVER_PORT) { + if ((inp->dhcp != NULL) && (inp->dhcp->pcb != NULL)) { + /* accept the packe if + (- broadcast or directed to us) -> DHCP is link-layer-addressed, local ip is always ANY! + - inp->dhcp->pcb->remote == ANY or iphdr->src */ + if ((ip_addr_isany(&inp->dhcp->pcb->remote_ip) || + ip_addr_cmp(&(inp->dhcp->pcb->remote_ip), ¤t_iphdr_src))) { + pcb = inp->dhcp->pcb; + } + } + } + } else if (dest == DHCP_SERVER_PORT) { + if (src == DHCP_CLIENT_PORT) { + if ( inp->dhcps_pcb != NULL ) { + if ((ip_addr_isany(&inp->dhcps_pcb->local_ip) || + ip_addr_cmp(&(inp->dhcps_pcb->local_ip), ¤t_iphdr_dest))) { + pcb = inp->dhcps_pcb; + } + } + } + } else +#endif /* LWIP_DHCP */ + { + prev = NULL; + local_match = 0; + uncon_pcb = NULL; + /* Iterate through the UDP pcb list for a matching pcb. + * 'Perfect match' pcbs (connected to the remote port & ip address) are + * preferred. If no perfect match is found, the first unconnected pcb that + * matches the local port and ip address gets the datagram. */ + for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) { + local_match = 0; + /* print the PCB local and remote address */ + LWIP_DEBUGF(UDP_DEBUG, + ("pcb (%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F") --- " + "(%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F")\n", + ip4_addr1_16(&pcb->local_ip), ip4_addr2_16(&pcb->local_ip), + ip4_addr3_16(&pcb->local_ip), ip4_addr4_16(&pcb->local_ip), pcb->local_port, + ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip), + ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip), pcb->remote_port)); + + /* compare PCB local addr+port to UDP destination addr+port */ + if ((pcb->local_port == dest) && + ((!broadcast && ip_addr_isany(&pcb->local_ip)) || + ip_addr_cmp(&(pcb->local_ip), ¤t_iphdr_dest) || +#if LWIP_IGMP + ip_addr_ismulticast(¤t_iphdr_dest) || +#endif /* LWIP_IGMP */ +#if IP_SOF_BROADCAST_RECV + (broadcast && (pcb->so_options & SOF_BROADCAST)))) { +#else /* IP_SOF_BROADCAST_RECV */ + (broadcast))) { +#endif /* IP_SOF_BROADCAST_RECV */ + local_match = 1; + if ((uncon_pcb == NULL) && + ((pcb->flags & UDP_FLAGS_CONNECTED) == 0)) { + /* the first unconnected matching PCB */ + uncon_pcb = pcb; + } + } + /* compare PCB remote addr+port to UDP source addr+port */ + if ((local_match != 0) && + (pcb->remote_port == src) && + (ip_addr_isany(&pcb->remote_ip) || + ip_addr_cmp(&(pcb->remote_ip), ¤t_iphdr_src))) { + /* the first fully matching PCB */ + if (prev != NULL) { + /* move the pcb to the front of udp_pcbs so that is + found faster next time */ + prev->next = pcb->next; + pcb->next = udp_pcbs; + udp_pcbs = pcb; + } else { + UDP_STATS_INC(udp.cachehit); + } + break; + } + prev = pcb; + } + /* no fully matching pcb found? then look for an unconnected pcb */ + if (pcb == NULL) { + pcb = uncon_pcb; + } + } + + /* Check checksum if this is a match or if it was directed at us. */ + if (pcb != NULL || ip_addr_cmp(&inp->ip_addr, ¤t_iphdr_dest)) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: calculating checksum\n")); +#if LWIP_UDPLITE + if (IPH_PROTO(iphdr) == IP_PROTO_UDPLITE) { + /* Do the UDP Lite checksum */ +#if CHECKSUM_CHECK_UDP + u16_t chklen = ntohs(udphdr->len); + if (chklen < sizeof(struct udp_hdr)) { + if (chklen == 0) { + /* For UDP-Lite, checksum length of 0 means checksum + over the complete packet (See RFC 3828 chap. 3.1) */ + chklen = p->tot_len; + } else { + /* At least the UDP-Lite header must be covered by the + checksum! (Again, see RFC 3828 chap. 3.1) */ + UDP_STATS_INC(udp.chkerr); + UDP_STATS_INC(udp.drop); + snmp_inc_udpinerrors(); + pbuf_free(p); + goto end; + } + } + if (inet_chksum_pseudo_partial(p, ¤t_iphdr_src, ¤t_iphdr_dest, + IP_PROTO_UDPLITE, p->tot_len, chklen) != 0) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("udp_input: UDP Lite datagram discarded due to failing checksum\n")); + UDP_STATS_INC(udp.chkerr); + UDP_STATS_INC(udp.drop); + snmp_inc_udpinerrors(); + pbuf_free(p); + goto end; + } +#endif /* CHECKSUM_CHECK_UDP */ + } else +#endif /* LWIP_UDPLITE */ + { +#if CHECKSUM_CHECK_UDP + if (udphdr->chksum != 0) { + if (inet_chksum_pseudo(p, ip_current_src_addr(), ip_current_dest_addr(), + IP_PROTO_UDP, p->tot_len) != 0) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("udp_input: UDP datagram discarded due to failing checksum\n")); + UDP_STATS_INC(udp.chkerr); + UDP_STATS_INC(udp.drop); + snmp_inc_udpinerrors(); + pbuf_free(p); + goto end; + } + } +#endif /* CHECKSUM_CHECK_UDP */ + } + if(pbuf_header(p, -UDP_HLEN)) { + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + UDP_STATS_INC(udp.drop); + snmp_inc_udpinerrors(); + pbuf_free(p); + goto end; + } + if (pcb != NULL) { + snmp_inc_udpindatagrams(); +#if SO_REUSE && SO_REUSE_RXTOALL + if ((broadcast || ip_addr_ismulticast(¤t_iphdr_dest)) && + ((pcb->so_options & SOF_REUSEADDR) != 0)) { + /* pass broadcast- or multicast packets to all multicast pcbs + if SOF_REUSEADDR is set on the first match */ + struct udp_pcb *mpcb; + u8_t p_header_changed = 0; + for (mpcb = udp_pcbs; mpcb != NULL; mpcb = mpcb->next) { + if (mpcb != pcb) { + /* compare PCB local addr+port to UDP destination addr+port */ + if ((mpcb->local_port == dest) && + ((!broadcast && ip_addr_isany(&mpcb->local_ip)) || + ip_addr_cmp(&(mpcb->local_ip), ¤t_iphdr_dest) || +#if LWIP_IGMP + ip_addr_ismulticast(¤t_iphdr_dest) || +#endif /* LWIP_IGMP */ +#if IP_SOF_BROADCAST_RECV + (broadcast && (mpcb->so_options & SOF_BROADCAST)))) { +#else /* IP_SOF_BROADCAST_RECV */ + (broadcast))) { +#endif /* IP_SOF_BROADCAST_RECV */ + /* pass a copy of the packet to all local matches */ + if (mpcb->recv != NULL) { + struct pbuf *q; + /* for that, move payload to IP header again */ + if (p_header_changed == 0) { + pbuf_header(p, (s16_t)((IPH_HL(iphdr) * 4) + UDP_HLEN)); + p_header_changed = 1; + } + q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); + if (q != NULL) { + err_t err = pbuf_copy(q, p); + if (err == ERR_OK) { + /* move payload to UDP data */ + pbuf_header(q, -(s16_t)((IPH_HL(iphdr) * 4) + UDP_HLEN)); + mpcb->recv(mpcb->recv_arg, mpcb, q, ip_current_src_addr(), src); + } + } + } + } + } + } + if (p_header_changed) { + /* and move payload to UDP data again */ + pbuf_header(p, -(s16_t)((IPH_HL(iphdr) * 4) + UDP_HLEN)); + } + } +#endif /* SO_REUSE && SO_REUSE_RXTOALL */ + /* callback */ + if (pcb->recv != NULL) { + /* now the recv function is responsible for freeing p */ + pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr(), src); + } else { + /* no recv function registered? then we have to free the pbuf! */ + pbuf_free(p); + goto end; + } + } else { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: not for us.\n")); + +#if LWIP_ICMP + /* No match was found, send ICMP destination port unreachable unless + destination address was broadcast/multicast. */ + if (!broadcast && + !ip_addr_ismulticast(¤t_iphdr_dest)) { + /* move payload pointer back to ip header */ + pbuf_header(p, (IPH_HL(iphdr) * 4) + UDP_HLEN); + LWIP_ASSERT("p->payload == iphdr", (p->payload == iphdr)); + icmp_dest_unreach(p, ICMP_DUR_PORT); + } +#endif /* LWIP_ICMP */ + UDP_STATS_INC(udp.proterr); + UDP_STATS_INC(udp.drop); + snmp_inc_udpnoports(); + pbuf_free(p); + } + } else { + pbuf_free(p); + } +end: + PERF_STOP("udp_input"); +} + +/** + * Send data using UDP. + * + * @param pcb UDP PCB used to send the data. + * @param p chain of pbuf's to be sent. + * + * The datagram will be sent to the current remote_ip & remote_port + * stored in pcb. If the pcb is not bound to a port, it will + * automatically be bound to a random port. + * + * @return lwIP error code. + * - ERR_OK. Successful. No error occured. + * - ERR_MEM. Out of memory. + * - ERR_RTE. Could not find route to destination address. + * - More errors could be returned by lower protocol layers. + * + * @see udp_disconnect() udp_sendto() + */ +err_t ICACHE_FLASH_ATTR +udp_send(struct udp_pcb *pcb, struct pbuf *p) +{ + /* send to the packet using remote ip and port stored in the pcb */ + return udp_sendto(pcb, p, &pcb->remote_ip, pcb->remote_port); +} + +#if LWIP_CHECKSUM_ON_COPY +/** Same as udp_send() but with checksum + */ +err_t ICACHE_FLASH_ATTR +udp_send_chksum(struct udp_pcb *pcb, struct pbuf *p, + u8_t have_chksum, u16_t chksum) +{ + /* send to the packet using remote ip and port stored in the pcb */ + return udp_sendto_chksum(pcb, p, &pcb->remote_ip, pcb->remote_port, + have_chksum, chksum); +} +#endif /* LWIP_CHECKSUM_ON_COPY */ + +/** + * Send data to a specified address using UDP. + * + * @param pcb UDP PCB used to send the data. + * @param p chain of pbuf's to be sent. + * @param dst_ip Destination IP address. + * @param dst_port Destination UDP port. + * + * dst_ip & dst_port are expected to be in the same byte order as in the pcb. + * + * If the PCB already has a remote address association, it will + * be restored after the data is sent. + * + * @return lwIP error code (@see udp_send for possible error codes) + * + * @see udp_disconnect() udp_send() + */ +err_t ICACHE_FLASH_ATTR +udp_sendto(struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *dst_ip, u16_t dst_port) +{ +#if LWIP_CHECKSUM_ON_COPY + return udp_sendto_chksum(pcb, p, dst_ip, dst_port, 0, 0); +} + +/** Same as udp_sendto(), but with checksum */ +err_t ICACHE_FLASH_ATTR +udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip, + u16_t dst_port, u8_t have_chksum, u16_t chksum) +{ +#endif /* LWIP_CHECKSUM_ON_COPY */ + struct netif *netif; + + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send\n")); + + /* find the outgoing network interface for this packet */ +#if LWIP_IGMP + netif = ip_route((ip_addr_ismulticast(dst_ip))?(&(pcb->multicast_ip)):(dst_ip)); +#else + netif = ip_route(dst_ip); +#endif /* LWIP_IGMP */ + + /* no outgoing network interface could be found? */ + if (netif == NULL) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(dst_ip), ip4_addr2_16(dst_ip), ip4_addr3_16(dst_ip), ip4_addr4_16(dst_ip))); + UDP_STATS_INC(udp.rterr); + return ERR_RTE; + } +#if LWIP_CHECKSUM_ON_COPY + return udp_sendto_if_chksum(pcb, p, dst_ip, dst_port, netif, have_chksum, chksum); +#else /* LWIP_CHECKSUM_ON_COPY */ + return udp_sendto_if(pcb, p, dst_ip, dst_port, netif); +#endif /* LWIP_CHECKSUM_ON_COPY */ +} + +/** + * Send data to a specified address using UDP. + * The netif used for sending can be specified. + * + * This function exists mainly for DHCP, to be able to send UDP packets + * on a netif that is still down. + * + * @param pcb UDP PCB used to send the data. + * @param p chain of pbuf's to be sent. + * @param dst_ip Destination IP address. + * @param dst_port Destination UDP port. + * @param netif the netif used for sending. + * + * dst_ip & dst_port are expected to be in the same byte order as in the pcb. + * + * @return lwIP error code (@see udp_send for possible error codes) + * + * @see udp_disconnect() udp_send() + */ +err_t ICACHE_FLASH_ATTR +udp_sendto_if(struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *dst_ip, u16_t dst_port, struct netif *netif) +{ +#if LWIP_CHECKSUM_ON_COPY + return udp_sendto_if_chksum(pcb, p, dst_ip, dst_port, netif, 0, 0); +} + +/** Same as udp_sendto_if(), but with checksum */ +err_t ICACHE_FLASH_ATTR +udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip, + u16_t dst_port, struct netif *netif, u8_t have_chksum, + u16_t chksum) +{ +#endif /* LWIP_CHECKSUM_ON_COPY */ + struct udp_hdr *udphdr; + ip_addr_t *src_ip; + err_t err; + struct pbuf *q; /* q will be sent down the stack */ + +#if IP_SOF_BROADCAST + /* broadcast filter? */ + if ( ((pcb->so_options & SOF_BROADCAST) == 0) && ip_addr_isbroadcast(dst_ip, netif) ) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("udp_sendto_if: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb)); + return ERR_VAL; + } +#endif /* IP_SOF_BROADCAST */ + + /* if the PCB is not yet bound to a port, bind it here */ + if (pcb->local_port == 0) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send: not yet bound to a port, binding now\n")); + err = udp_bind(pcb, &pcb->local_ip, pcb->local_port); + if (err != ERR_OK) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: forced port bind failed\n")); + return err; + } + } + + /* not enough space to add an UDP header to first pbuf in given p chain? */ + if (pbuf_header(p, UDP_HLEN)) { + /* allocate header in a separate new pbuf */ + q = pbuf_alloc(PBUF_IP, UDP_HLEN, PBUF_RAM); + /* new header pbuf could not be allocated? */ + if (q == NULL) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: could not allocate header\n")); + return ERR_MEM; + } + if (p->tot_len != 0) { + /* chain header q in front of given pbuf p (only if p contains data) */ + pbuf_chain(q, p); + } + /* first pbuf q points to header pbuf */ + LWIP_DEBUGF(UDP_DEBUG, + ("udp_send: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p)); + } else { + /* adding space for header within p succeeded */ + /* first pbuf q equals given pbuf */ + q = p; + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: added header in given pbuf %p\n", (void *)p)); + } + LWIP_ASSERT("check that first pbuf can hold struct udp_hdr", + (q->len >= sizeof(struct udp_hdr))); + /* q now represents the packet to be sent */ + udphdr = (struct udp_hdr *)q->payload; + udphdr->src = htons(pcb->local_port); + udphdr->dest = htons(dst_port); + /* in UDP, 0 checksum means 'no checksum' */ + udphdr->chksum = 0x0000; + + /* Multicast Loop? */ +#if LWIP_IGMP + if (ip_addr_ismulticast(dst_ip) && ((pcb->flags & UDP_FLAGS_MULTICAST_LOOP) != 0)) { + q->flags |= PBUF_FLAG_MCASTLOOP; + } +#endif /* LWIP_IGMP */ + + + /* PCB local address is IP_ANY_ADDR? */ + if (ip_addr_isany(&pcb->local_ip)) { + /* use outgoing network interface IP address as source address */ + src_ip = &(netif->ip_addr); + } else { + /* check if UDP PCB local IP address is correct + * this could be an old address if netif->ip_addr has changed */ + if (!ip_addr_cmp(&(pcb->local_ip), &(netif->ip_addr))) { + /* local_ip doesn't match, drop the packet */ + if (q != p) { + /* free the header pbuf */ + pbuf_free(q); + q = NULL; + /* p is still referenced by the caller, and will live on */ + } + return ERR_VAL; + } + /* use UDP PCB local IP address as source address */ + src_ip = &(pcb->local_ip); + } + + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: sending datagram of length %"U16_F"\n", q->tot_len)); + +#if LWIP_UDPLITE + /* UDP Lite protocol? */ + if (pcb->flags & UDP_FLAGS_UDPLITE) { + u16_t chklen, chklen_hdr; + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE packet length %"U16_F"\n", q->tot_len)); + /* set UDP message length in UDP header */ + chklen_hdr = chklen = pcb->chksum_len_tx; + if ((chklen < sizeof(struct udp_hdr)) || (chklen > q->tot_len)) { + if (chklen != 0) { + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE pcb->chksum_len is illegal: %"U16_F"\n", chklen)); + } + /* For UDP-Lite, checksum length of 0 means checksum + over the complete packet. (See RFC 3828 chap. 3.1) + At least the UDP-Lite header must be covered by the + checksum, therefore, if chksum_len has an illegal + value, we generate the checksum over the complete + packet to be safe. */ + chklen_hdr = 0; + chklen = q->tot_len; + } + udphdr->len = htons(chklen_hdr); + /* calculate checksum */ +#if CHECKSUM_GEN_UDP + udphdr->chksum = inet_chksum_pseudo_partial(q, src_ip, dst_ip, + IP_PROTO_UDPLITE, q->tot_len, +#if !LWIP_CHECKSUM_ON_COPY + chklen); +#else /* !LWIP_CHECKSUM_ON_COPY */ + (have_chksum ? UDP_HLEN : chklen)); + if (have_chksum) { + u32_t acc; + acc = udphdr->chksum + (u16_t)~(chksum); + udphdr->chksum = FOLD_U32T(acc); + } +#endif /* !LWIP_CHECKSUM_ON_COPY */ + + /* chksum zero must become 0xffff, as zero means 'no checksum' */ + if (udphdr->chksum == 0x0000) { + udphdr->chksum = 0xffff; + } +#endif /* CHECKSUM_GEN_UDP */ + /* output to IP */ + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,IP_PROTO_UDPLITE,)\n")); +#if LWIP_NETIF_HWADDRHINT + netif->addr_hint = &(pcb->addr_hint); +#endif /* LWIP_NETIF_HWADDRHINT*/ + err = ip_output_if(q, src_ip, dst_ip, pcb->ttl, pcb->tos, IP_PROTO_UDPLITE, netif); +#if LWIP_NETIF_HWADDRHINT + netif->addr_hint = NULL; +#endif /* LWIP_NETIF_HWADDRHINT*/ + } else +#endif /* LWIP_UDPLITE */ + { /* UDP */ + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP packet length %"U16_F"\n", q->tot_len)); + udphdr->len = htons(q->tot_len); + /* calculate checksum */ +#if CHECKSUM_GEN_UDP + if ((pcb->flags & UDP_FLAGS_NOCHKSUM) == 0) { + u16_t udpchksum; +#if LWIP_CHECKSUM_ON_COPY + if (have_chksum) { + u32_t acc; + udpchksum = inet_chksum_pseudo_partial(q, src_ip, dst_ip, IP_PROTO_UDP, + q->tot_len, UDP_HLEN); + acc = udpchksum + (u16_t)~(chksum); + udpchksum = FOLD_U32T(acc); + } else +#endif /* LWIP_CHECKSUM_ON_COPY */ + { + udpchksum = inet_chksum_pseudo(q, src_ip, dst_ip, IP_PROTO_UDP, q->tot_len); + } + + /* chksum zero must become 0xffff, as zero means 'no checksum' */ + if (udpchksum == 0x0000) { + udpchksum = 0xffff; + } + udphdr->chksum = udpchksum; + } +#endif /* CHECKSUM_GEN_UDP */ + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP checksum 0x%04"X16_F"\n", udphdr->chksum)); + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,IP_PROTO_UDP,)\n")); + /* output to IP */ +#if LWIP_NETIF_HWADDRHINT + netif->addr_hint = &(pcb->addr_hint); +#endif /* LWIP_NETIF_HWADDRHINT*/ + err = ip_output_if(q, src_ip, dst_ip, pcb->ttl, pcb->tos, IP_PROTO_UDP, netif); +#if LWIP_NETIF_HWADDRHINT + netif->addr_hint = NULL; +#endif /* LWIP_NETIF_HWADDRHINT*/ + } + /* TODO: must this be increased even if error occured? */ + snmp_inc_udpoutdatagrams(); + + /* did we chain a separate header pbuf earlier? */ + if (q != p) { + /* free the header pbuf */ + pbuf_free(q); + q = NULL; + /* p is still referenced by the caller, and will live on */ + } + + UDP_STATS_INC(udp.xmit); + return err; +} + +/** + * Bind an UDP PCB. + * + * @param pcb UDP PCB to be bound with a local address ipaddr and port. + * @param ipaddr local IP address to bind with. Use IP_ADDR_ANY to + * bind to all local interfaces. + * @param port local UDP port to bind with. Use 0 to automatically bind + * to a random port between UDP_LOCAL_PORT_RANGE_START and + * UDP_LOCAL_PORT_RANGE_END. + * + * ipaddr & port are expected to be in the same byte order as in the pcb. + * + * @return lwIP error code. + * - ERR_OK. Successful. No error occured. + * - ERR_USE. The specified ipaddr and port are already bound to by + * another UDP PCB. + * + * @see udp_disconnect() + */ +err_t ICACHE_FLASH_ATTR +udp_bind(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port) +{ + struct udp_pcb *ipcb; + u8_t rebind; + + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_bind(ipaddr = ")); + ip_addr_debug_print(UDP_DEBUG, ipaddr); + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, (", port = %"U16_F")\n", port)); + + rebind = 0; + /* Check for double bind and rebind of the same pcb */ + for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) { + /* is this UDP PCB already on active list? */ + if (pcb == ipcb) { + /* pcb may occur at most once in active list */ + LWIP_ASSERT("rebind == 0", rebind == 0); + /* pcb already in list, just rebind */ + rebind = 1; + } + + /* By default, we don't allow to bind to a port that any other udp + PCB is alread bound to, unless *all* PCBs with that port have tha + REUSEADDR flag set. */ +#if SO_REUSE + else if (((pcb->so_options & SOF_REUSEADDR) == 0) && + ((ipcb->so_options & SOF_REUSEADDR) == 0)) { +#else /* SO_REUSE */ + /* port matches that of PCB in list and REUSEADDR not set -> reject */ + else { +#endif /* SO_REUSE */ + if ((ipcb->local_port == port) && + /* IP address matches, or one is IP_ADDR_ANY? */ + (ip_addr_isany(&(ipcb->local_ip)) || + ip_addr_isany(ipaddr) || + ip_addr_cmp(&(ipcb->local_ip), ipaddr))) { + /* other PCB already binds to this local IP and port */ + LWIP_DEBUGF(UDP_DEBUG, + ("udp_bind: local port %"U16_F" already bound by another pcb\n", port)); + return ERR_USE; + } + } + } + + ip_addr_set(&pcb->local_ip, ipaddr); + + /* no port specified? */ + if (port == 0) { +#ifndef UDP_LOCAL_PORT_RANGE_START +#define UDP_LOCAL_PORT_RANGE_START 4096 +#define UDP_LOCAL_PORT_RANGE_END 0x7fff +#endif + port = UDP_LOCAL_PORT_RANGE_START; + ipcb = udp_pcbs; + while ((ipcb != NULL) && (port != UDP_LOCAL_PORT_RANGE_END)) { + if (ipcb->local_port == port) { + /* port is already used by another udp_pcb */ + port++; + /* restart scanning all udp pcbs */ + ipcb = udp_pcbs; + } else { + /* go on with next udp pcb */ + ipcb = ipcb->next; + } + } + if (ipcb != NULL) { + /* no more ports available in local range */ + LWIP_DEBUGF(UDP_DEBUG, ("udp_bind: out of free UDP ports\n")); + return ERR_USE; + } + } + pcb->local_port = port; + snmp_insert_udpidx_tree(pcb); + /* pcb not active yet? */ + if (rebind == 0) { + /* place the PCB on the active list if not already there */ + pcb->next = udp_pcbs; + udp_pcbs = pcb; + } + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("udp_bind: bound to %"U16_F".%"U16_F".%"U16_F".%"U16_F", port %"U16_F"\n", + ip4_addr1_16(&pcb->local_ip), ip4_addr2_16(&pcb->local_ip), + ip4_addr3_16(&pcb->local_ip), ip4_addr4_16(&pcb->local_ip), + pcb->local_port)); + return ERR_OK; +} +/** + * Connect an UDP PCB. + * + * This will associate the UDP PCB with the remote address. + * + * @param pcb UDP PCB to be connected with remote address ipaddr and port. + * @param ipaddr remote IP address to connect with. + * @param port remote UDP port to connect with. + * + * @return lwIP error code + * + * ipaddr & port are expected to be in the same byte order as in the pcb. + * + * The udp pcb is bound to a random local port if not already bound. + * + * @see udp_disconnect() + */ +err_t ICACHE_FLASH_ATTR +udp_connect(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port) +{ + struct udp_pcb *ipcb; + + if (pcb->local_port == 0) { + err_t err = udp_bind(pcb, &pcb->local_ip, pcb->local_port); + if (err != ERR_OK) { + return err; + } + } + + ip_addr_set(&pcb->remote_ip, ipaddr); + pcb->remote_port = port; + pcb->flags |= UDP_FLAGS_CONNECTED; +/** TODO: this functionality belongs in upper layers */ +#ifdef LWIP_UDP_TODO + /* Nail down local IP for netconn_addr()/getsockname() */ + if (ip_addr_isany(&pcb->local_ip) && !ip_addr_isany(&pcb->remote_ip)) { + struct netif *netif; + + if ((netif = ip_route(&(pcb->remote_ip))) == NULL) { + LWIP_DEBUGF(UDP_DEBUG, ("udp_connect: No route to 0x%lx\n", pcb->remote_ip.addr)); + UDP_STATS_INC(udp.rterr); + return ERR_RTE; + } + /** TODO: this will bind the udp pcb locally, to the interface which + is used to route output packets to the remote address. However, we + might want to accept incoming packets on any interface! */ + pcb->local_ip = netif->ip_addr; + } else if (ip_addr_isany(&pcb->remote_ip)) { + pcb->local_ip.addr = 0; + } +#endif + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("udp_connect: connected to %"U16_F".%"U16_F".%"U16_F".%"U16_F",port %"U16_F"\n", + ip4_addr1_16(&pcb->local_ip), ip4_addr2_16(&pcb->local_ip), + ip4_addr3_16(&pcb->local_ip), ip4_addr4_16(&pcb->local_ip), + pcb->local_port)); + + /* Insert UDP PCB into the list of active UDP PCBs. */ + for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) { + if (pcb == ipcb) { + /* already on the list, just return */ + return ERR_OK; + } + } + /* PCB not yet on the list, add PCB now */ + pcb->next = udp_pcbs; + udp_pcbs = pcb; + return ERR_OK; +} + +/** + * Disconnect a UDP PCB + * + * @param pcb the udp pcb to disconnect. + */ +void ICACHE_FLASH_ATTR +udp_disconnect(struct udp_pcb *pcb) +{ + /* reset remote address association */ + ip_addr_set_any(&pcb->remote_ip); + pcb->remote_port = 0; + /* mark PCB as unconnected */ + pcb->flags &= ~UDP_FLAGS_CONNECTED; +} + +/** + * Set a receive callback for a UDP PCB + * + * This callback will be called when receiving a datagram for the pcb. + * + * @param pcb the pcb for wich to set the recv callback + * @param recv function pointer of the callback function + * @param recv_arg additional argument to pass to the callback function + */ +void ICACHE_FLASH_ATTR +udp_recv(struct udp_pcb *pcb, udp_recv_fn recv, void *recv_arg) +{ + /* remember recv() callback and user data */ + pcb->recv = recv; + pcb->recv_arg = recv_arg; +} + +/** + * Remove an UDP PCB. + * + * @param pcb UDP PCB to be removed. The PCB is removed from the list of + * UDP PCB's and the data structure is freed from memory. + * + * @see udp_new() + */ +void ICACHE_FLASH_ATTR +udp_remove(struct udp_pcb *pcb) +{ + struct udp_pcb *pcb2; + + snmp_delete_udpidx_tree(pcb); + /* pcb to be removed is first in list? */ + if (udp_pcbs == pcb) { + /* make list start at 2nd pcb */ + udp_pcbs = udp_pcbs->next; + /* pcb not 1st in list */ + } else { + for (pcb2 = udp_pcbs; pcb2 != NULL; pcb2 = pcb2->next) { + /* find pcb in udp_pcbs list */ + if (pcb2->next != NULL && pcb2->next == pcb) { + /* remove pcb from list */ + pcb2->next = pcb->next; + } + } + } + memp_free(MEMP_UDP_PCB, pcb); +} + +/** + * Create a UDP PCB. + * + * @return The UDP PCB which was created. NULL if the PCB data structure + * could not be allocated. + * + * @see udp_remove() + */ +struct udp_pcb * ICACHE_FLASH_ATTR +udp_new(void) +{ + struct udp_pcb *pcb; + pcb = (struct udp_pcb *)memp_malloc(MEMP_UDP_PCB); + /* could allocate UDP PCB? */ + if (pcb != NULL) { + /* UDP Lite: by initializing to all zeroes, chksum_len is set to 0 + * which means checksum is generated over the whole datagram per default + * (recommended as default by RFC 3828). */ + /* initialize PCB to all zeroes */ + os_memset(pcb, 0, sizeof(struct udp_pcb)); + pcb->ttl = UDP_TTL; + } + return pcb; +} + +#if UDP_DEBUG +/** + * Print UDP header information for debug purposes. + * + * @param udphdr pointer to the udp header in memory. + */ +void ICACHE_FLASH_ATTR +udp_debug_print(struct udp_hdr *udphdr) +{ + LWIP_DEBUGF(UDP_DEBUG, ("UDP header:\n")); + LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(UDP_DEBUG, ("| %5"U16_F" | %5"U16_F" | (src port, dest port)\n", + ntohs(udphdr->src), ntohs(udphdr->dest))); + LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(UDP_DEBUG, ("| %5"U16_F" | 0x%04"X16_F" | (len, chksum)\n", + ntohs(udphdr->len), ntohs(udphdr->chksum))); + LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n")); +} +#endif /* UDP_DEBUG */ + +#endif /* LWIP_UDP */ diff --git a/variants/lwip/lwip/netif/etharp.c b/variants/lwip/lwip/netif/etharp.c new file mode 100755 index 000000000..7eb328ace --- /dev/null +++ b/variants/lwip/lwip/netif/etharp.c @@ -0,0 +1,1413 @@ +/** + * @file + * Address Resolution Protocol module for IP over Ethernet + * + * Functionally, ARP is divided into two parts. The first maps an IP address + * to a physical address when sending a packet, and the second part answers + * requests from other machines for our physical address. + * + * This implementation complies with RFC 826 (Ethernet ARP). It supports + * Gratuitious ARP from RFC3220 (IP Mobility Support for IPv4) section 4.6 + * if an interface calls etharp_gratuitous(our_netif) upon address change. + */ + +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * Copyright (c) 2003-2004 Leon Woestenberg + * Copyright (c) 2003-2004 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#include "lwip/opt.h" + +#if LWIP_ARP || LWIP_ETHERNET + +#include "lwip/ip_addr.h" +#include "lwip/def.h" +#include "lwip/ip.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "lwip/dhcp.h" +#include "lwip/autoip.h" +#include "netif/etharp.h" + +#if PPPOE_SUPPORT +#include "netif/ppp_oe.h" +#endif /* PPPOE_SUPPORT */ + +#include + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +const struct eth_addr ethbroadcast = {{0xff,0xff,0xff,0xff,0xff,0xff}}; +const struct eth_addr ethzero = {{0,0,0,0,0,0}}; + +#if LWIP_ARP /* don't build if not configured for use in lwipopts.h */ + +/** the time an ARP entry stays valid after its last update, + * for ARP_TMR_INTERVAL = 5000, this is + * (240 * 5) seconds = 20 minutes. + */ +#define ARP_MAXAGE 240 +/** Re-request a used ARP entry 1 minute before it would expire to prevent + * breaking a steadily used connection because the ARP entry timed out. */ +#define ARP_AGE_REREQUEST_USED (ARP_MAXAGE - 12) +/** the time an ARP entry stays pending after first request, + * for ARP_TMR_INTERVAL = 5000, this is + * (2 * 5) seconds = 10 seconds. + * + * @internal Keep this number at least 2, otherwise it might + * run out instantly if the timeout occurs directly after a request. + */ +#define ARP_MAXPENDING 2 + +#define HWTYPE_ETHERNET 1 + +enum etharp_state { + ETHARP_STATE_EMPTY = 0, + ETHARP_STATE_PENDING, + ETHARP_STATE_STABLE, + ETHARP_STATE_STABLE_REREQUESTING +}; + +struct etharp_entry { +#if ARP_QUEUEING + /** Pointer to queue of pending outgoing packets on this ARP entry. */ + struct etharp_q_entry *q; +#else /* ARP_QUEUEING */ + /** Pointer to a single pending outgoing packet on this ARP entry. */ + struct pbuf *q; +#endif /* ARP_QUEUEING */ + ip_addr_t ipaddr; + struct eth_addr ethaddr; +#if LWIP_SNMP || LWIP_ARP + struct netif *netif; +#endif /* LWIP_SNMP */ + u8_t state; + u8_t ctime; +#if ETHARP_SUPPORT_STATIC_ENTRIES + u8_t static_entry; +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ +}; + +static struct etharp_entry arp_table[ARP_TABLE_SIZE]; + +#if !LWIP_NETIF_HWADDRHINT +static u8_t etharp_cached_entry; +#endif /* !LWIP_NETIF_HWADDRHINT */ + +/** Try hard to create a new entry - we want the IP address to appear in + the cache (even if this means removing an active entry or so). */ +#define ETHARP_FLAG_TRY_HARD 1 +#define ETHARP_FLAG_FIND_ONLY 2 +#define ETHARP_FLAG_STATIC_ENTRY 4 + +#if LWIP_NETIF_HWADDRHINT +#define ETHARP_SET_HINT(netif, hint) if (((netif) != NULL) && ((netif)->addr_hint != NULL)) \ + *((netif)->addr_hint) = (hint); +#else /* LWIP_NETIF_HWADDRHINT */ +#define ETHARP_SET_HINT(netif, hint) (etharp_cached_entry = (hint)) +#endif /* LWIP_NETIF_HWADDRHINT */ + +static err_t update_arp_entry(struct netif *netif, ip_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags); + + +/* Some checks, instead of etharp_init(): */ +#if (LWIP_ARP && (ARP_TABLE_SIZE > 0x7f)) + #error "ARP_TABLE_SIZE must fit in an s8_t, you have to reduce it in your lwipopts.h" +#endif + + +#if ARP_QUEUEING +/** + * Free a complete queue of etharp entries + * + * @param q a qeueue of etharp_q_entry's to free + */ +static void +free_etharp_q(struct etharp_q_entry *q) +{ + struct etharp_q_entry *r; + LWIP_ASSERT("q != NULL", q != NULL); + LWIP_ASSERT("q->p != NULL", q->p != NULL); + while (q) { + r = q; + q = q->next; + LWIP_ASSERT("r->p != NULL", (r->p != NULL)); + pbuf_free(r->p); + memp_free(MEMP_ARP_QUEUE, r); + } +} +#else /* ARP_QUEUEING */ + +/** Compatibility define: free the queued pbuf */ +#define free_etharp_q(q) pbuf_free(q) + +#endif /* ARP_QUEUEING */ + +/** Clean up ARP table entries */ +static void ICACHE_FLASH_ATTR +free_entry(int i) +{ + /* remove from SNMP ARP index tree */ + snmp_delete_arpidx_tree(arp_table[i].netif, &arp_table[i].ipaddr); + /* and empty packet queue */ + if (arp_table[i].q != NULL) { + /* remove all queued packets */ + LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: freeing entry %"U16_F", packet queue %p.\n", (u16_t)i, (void *)(arp_table[i].q))); + free_etharp_q(arp_table[i].q); + arp_table[i].q = NULL; + } + /* recycle entry for re-use */ + arp_table[i].state = ETHARP_STATE_EMPTY; +#if ETHARP_SUPPORT_STATIC_ENTRIES + arp_table[i].static_entry = 0; +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ +#ifdef LWIP_DEBUG + /* for debugging, clean out the complete entry */ + arp_table[i].ctime = 0; +#if LWIP_SNMP + arp_table[i].netif = NULL; +#endif /* LWIP_SNMP */ + ip_addr_set_zero(&arp_table[i].ipaddr); + arp_table[i].ethaddr = ethzero; +#endif /* LWIP_DEBUG */ +} + +/** + * Clears expired entries in the ARP table. + * + * This function should be called every ETHARP_TMR_INTERVAL milliseconds (5 seconds), + * in order to expire entries in the ARP table. + */ +void +etharp_tmr(void) +{ + u8_t i; + + LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer\n")); + /* remove expired entries from the ARP table */ + for (i = 0; i < ARP_TABLE_SIZE; ++i) { + u8_t state = arp_table[i].state; + if (state != ETHARP_STATE_EMPTY +#if ETHARP_SUPPORT_STATIC_ENTRIES + && (arp_table[i].static_entry == 0) +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + ) { + arp_table[i].ctime++; + if ((arp_table[i].ctime >= ARP_MAXAGE) || + ((arp_table[i].state == ETHARP_STATE_PENDING) && + (arp_table[i].ctime >= ARP_MAXPENDING))) { + /* pending or stable entry has become old! */ + LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: expired %s entry %"U16_F".\n", + arp_table[i].state >= ETHARP_STATE_STABLE ? "stable" : "pending", (u16_t)i)); + /* clean up entries that have just been expired */ + free_entry(i); + } + else if (arp_table[i].state == ETHARP_STATE_STABLE_REREQUESTING) { + /* Reset state to stable, so that the next transmitted packet will + re-send an ARP request. */ + arp_table[i].state = ETHARP_STATE_STABLE; + } +#if ARP_QUEUEING + /* still pending entry? (not expired) */ + if (arp_table[i].state == ETHARP_STATE_PENDING) { + /* resend an ARP query here? */ + } +#endif /* ARP_QUEUEING */ + } + } +} + +/** + * Search the ARP table for a matching or new entry. + * + * If an IP address is given, return a pending or stable ARP entry that matches + * the address. If no match is found, create a new entry with this address set, + * but in state ETHARP_EMPTY. The caller must check and possibly change the + * state of the returned entry. + * + * If ipaddr is NULL, return a initialized new entry in state ETHARP_EMPTY. + * + * In all cases, attempt to create new entries from an empty entry. If no + * empty entries are available and ETHARP_FLAG_TRY_HARD flag is set, recycle + * old entries. Heuristic choose the least important entry for recycling. + * + * @param ipaddr IP address to find in ARP cache, or to add if not found. + * @param flags @see definition of ETHARP_FLAG_* + * @param netif netif related to this address (used for NETIF_HWADDRHINT) + * + * @return The ARP entry index that matched or is created, ERR_MEM if no + * entry is found or could be recycled. + */ +static s8_t ICACHE_FLASH_ATTR +find_entry(ip_addr_t *ipaddr, u8_t flags) +{ + s8_t old_pending = ARP_TABLE_SIZE, old_stable = ARP_TABLE_SIZE; + s8_t empty = ARP_TABLE_SIZE; + u8_t i = 0, age_pending = 0, age_stable = 0; + /* oldest entry with packets on queue */ + s8_t old_queue = ARP_TABLE_SIZE; + /* its age */ + u8_t age_queue = 0; + + /** + * a) do a search through the cache, remember candidates + * b) select candidate entry + * c) create new entry + */ + + /* a) in a single search sweep, do all of this + * 1) remember the first empty entry (if any) + * 2) remember the oldest stable entry (if any) + * 3) remember the oldest pending entry without queued packets (if any) + * 4) remember the oldest pending entry with queued packets (if any) + * 5) search for a matching IP entry, either pending or stable + * until 5 matches, or all entries are searched for. + */ + + for (i = 0; i < ARP_TABLE_SIZE; ++i) { + u8_t state = arp_table[i].state; + /* no empty entry found yet and now we do find one? */ + if ((empty == ARP_TABLE_SIZE) && (state == ETHARP_STATE_EMPTY)) { + LWIP_DEBUGF(ETHARP_DEBUG, ("find_entry: found empty entry %"U16_F"\n", (u16_t)i)); + /* remember first empty entry */ + empty = i; + } else if (state != ETHARP_STATE_EMPTY) { + LWIP_ASSERT("state == ETHARP_STATE_PENDING || state >= ETHARP_STATE_STABLE", + state == ETHARP_STATE_PENDING || state >= ETHARP_STATE_STABLE); + /* if given, does IP address match IP address in ARP entry? */ + if (ipaddr && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: found matching entry %"U16_F"\n", (u16_t)i)); + /* found exact IP address match, simply bail out */ + return i; + } + /* pending entry? */ + if (state == ETHARP_STATE_PENDING) { + /* pending with queued packets? */ + if (arp_table[i].q != NULL) { + if (arp_table[i].ctime >= age_queue) { + old_queue = i; + age_queue = arp_table[i].ctime; + } + } else + /* pending without queued packets? */ + { + if (arp_table[i].ctime >= age_pending) { + old_pending = i; + age_pending = arp_table[i].ctime; + } + } + /* stable entry? */ + } else if (state >= ETHARP_STATE_STABLE) { +#if ETHARP_SUPPORT_STATIC_ENTRIES + /* don't record old_stable for static entries since they never expire */ + if (arp_table[i].static_entry == 0) +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + { + /* remember entry with oldest stable entry in oldest, its age in maxtime */ + if (arp_table[i].ctime >= age_stable) { + old_stable = i; + age_stable = arp_table[i].ctime; + } + } + } + } + } + /* { we have no match } => try to create a new entry */ + + /* don't create new entry, only search? */ + if (((flags & ETHARP_FLAG_FIND_ONLY) != 0) || + /* or no empty entry found and not allowed to recycle? */ + ((empty == ARP_TABLE_SIZE) && ((flags & ETHARP_FLAG_TRY_HARD) == 0))) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: no empty entry found and not allowed to recycle\n")); + return (s8_t)ERR_MEM; + } + + /* b) choose the least destructive entry to recycle: + * 1) empty entry + * 2) oldest stable entry + * 3) oldest pending entry without queued packets + * 4) oldest pending entry with queued packets + * + * { ETHARP_FLAG_TRY_HARD is set at this point } + */ + + /* 1) empty entry available? */ + if (empty < ARP_TABLE_SIZE) { + i = empty; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting empty entry %"U16_F"\n", (u16_t)i)); + } else { + /* 2) found recyclable stable entry? */ + if (old_stable < ARP_TABLE_SIZE) { + /* recycle oldest stable*/ + i = old_stable; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting oldest stable entry %"U16_F"\n", (u16_t)i)); + /* no queued packets should exist on stable entries */ + LWIP_ASSERT("arp_table[i].q == NULL", arp_table[i].q == NULL); + /* 3) found recyclable pending entry without queued packets? */ + } else if (old_pending < ARP_TABLE_SIZE) { + /* recycle oldest pending */ + i = old_pending; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting oldest pending entry %"U16_F" (without queue)\n", (u16_t)i)); + /* 4) found recyclable pending entry with queued packets? */ + } else if (old_queue < ARP_TABLE_SIZE) { + /* recycle oldest pending (queued packets are free in free_entry) */ + i = old_queue; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting oldest pending entry %"U16_F", freeing packet queue %p\n", (u16_t)i, (void *)(arp_table[i].q))); + /* no empty or recyclable entries found */ + } else { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("find_entry: no empty or recyclable entries found\n")); + return (s8_t)ERR_MEM; + } + + /* { empty or recyclable entry found } */ + LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE); + free_entry(i); + } + + LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE); + LWIP_ASSERT("arp_table[i].state == ETHARP_STATE_EMPTY", + arp_table[i].state == ETHARP_STATE_EMPTY); + + /* IP address given? */ + if (ipaddr != NULL) { + /* set IP address */ + ip_addr_copy(arp_table[i].ipaddr, *ipaddr); + } + arp_table[i].ctime = 0; +#if ETHARP_SUPPORT_STATIC_ENTRIES + arp_table[i].static_entry = 0; +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + return (err_t)i; +} + +/** + * Send an IP packet on the network using netif->linkoutput + * The ethernet header is filled in before sending. + * + * @params netif the lwIP network interface on which to send the packet + * @params p the packet to send, p->payload pointing to the (uninitialized) ethernet header + * @params src the source MAC address to be copied into the ethernet header + * @params dst the destination MAC address to be copied into the ethernet header + * @return ERR_OK if the packet was sent, any other err_t on failure + */ +static err_t ICACHE_FLASH_ATTR +etharp_send_ip(struct netif *netif, struct pbuf *p, struct eth_addr *src, struct eth_addr *dst) +{ + struct eth_hdr *ethhdr = (struct eth_hdr *)p->payload; + + LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!", + (netif->hwaddr_len == ETHARP_HWADDR_LEN)); + ETHADDR32_COPY(ðhdr->dest, dst); + ETHADDR16_COPY(ðhdr->src, src); + ethhdr->type = PP_HTONS(ETHTYPE_IP); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_send_ip: sending packet %p\n", (void *)p)); + /* send the packet */ + return netif->linkoutput(netif, p); +} + +/** + * Update (or insert) a IP/MAC address pair in the ARP cache. + * + * If a pending entry is resolved, any queued packets will be sent + * at this point. + * + * @param netif netif related to this entry (used for NETIF_ADDRHINT) + * @param ipaddr IP address of the inserted ARP entry. + * @param ethaddr Ethernet address of the inserted ARP entry. + * @param flags @see definition of ETHARP_FLAG_* + * + * @return + * - ERR_OK Succesfully updated ARP cache. + * - ERR_MEM If we could not add a new ARP entry when ETHARP_FLAG_TRY_HARD was set. + * - ERR_ARG Non-unicast address given, those will not appear in ARP cache. + * + * @see pbuf_free() + */ +static err_t ICACHE_FLASH_ATTR +update_arp_entry(struct netif *netif, ip_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags) +{ + s8_t i; + LWIP_ASSERT("netif->hwaddr_len == ETHARP_HWADDR_LEN", netif->hwaddr_len == ETHARP_HWADDR_LEN); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n", + ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr), + ethaddr->addr[0], ethaddr->addr[1], ethaddr->addr[2], + ethaddr->addr[3], ethaddr->addr[4], ethaddr->addr[5])); + /* non-unicast address? */ + if (ip_addr_isany(ipaddr) || + ip_addr_isbroadcast(ipaddr, netif) || + ip_addr_ismulticast(ipaddr)) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry: will not add non-unicast IP address to ARP cache\n")); + return ERR_ARG; + } + /* find or create ARP entry */ + i = find_entry(ipaddr, flags); + /* bail out if no entry could be found */ + if (i < 0) { + return (err_t)i; + } + +#if ETHARP_SUPPORT_STATIC_ENTRIES + if (flags & ETHARP_FLAG_STATIC_ENTRY) { + /* record static type */ + arp_table[i].static_entry = 1; + } +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + + /* mark it stable */ + arp_table[i].state = ETHARP_STATE_STABLE; + +#if LWIP_SNMP + /* record network interface */ + arp_table[i].netif = netif; +#endif /* LWIP_SNMP */ + /* insert in SNMP ARP index tree */ + snmp_insert_arpidx_tree(netif, &arp_table[i].ipaddr); + + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry: updating stable entry %"S16_F"\n", (s16_t)i)); + /* update address */ + ETHADDR32_COPY(&arp_table[i].ethaddr, ethaddr); + /* reset time stamp */ + arp_table[i].ctime = 0; + /* this is where we will send out queued packets! */ +#if ARP_QUEUEING + while (arp_table[i].q != NULL) { + struct pbuf *p; + /* remember remainder of queue */ + struct etharp_q_entry *q = arp_table[i].q; + /* pop first item off the queue */ + arp_table[i].q = q->next; + /* get the packet pointer */ + p = q->p; + /* now queue entry can be freed */ + memp_free(MEMP_ARP_QUEUE, q); +#else /* ARP_QUEUEING */ + if (arp_table[i].q != NULL) { + struct pbuf *p = arp_table[i].q; + arp_table[i].q = NULL; +#endif /* ARP_QUEUEING */ + /* send the queued IP packet */ + etharp_send_ip(netif, p, (struct eth_addr*)(netif->hwaddr), ethaddr); + /* free the queued IP packet */ + pbuf_free(p); + } + return ERR_OK; +} + +#if ETHARP_SUPPORT_STATIC_ENTRIES +/** Add a new static entry to the ARP table. If an entry exists for the + * specified IP address, this entry is overwritten. + * If packets are queued for the specified IP address, they are sent out. + * + * @param ipaddr IP address for the new static entry + * @param ethaddr ethernet address for the new static entry + * @return @see return values of etharp_add_static_entry + */ +err_t +etharp_add_static_entry(ip_addr_t *ipaddr, struct eth_addr *ethaddr) +{ + struct netif *netif; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_add_static_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n", + ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr), + ethaddr->addr[0], ethaddr->addr[1], ethaddr->addr[2], + ethaddr->addr[3], ethaddr->addr[4], ethaddr->addr[5])); + + netif = ip_route(ipaddr); + if (netif == NULL) { + return ERR_RTE; + } + + return update_arp_entry(netif, ipaddr, ethaddr, ETHARP_FLAG_TRY_HARD | ETHARP_FLAG_STATIC_ENTRY); +} + +/** Remove a static entry from the ARP table previously added with a call to + * etharp_add_static_entry. + * + * @param ipaddr IP address of the static entry to remove + * @return ERR_OK: entry removed + * ERR_MEM: entry wasn't found + * ERR_ARG: entry wasn't a static entry but a dynamic one + */ +err_t +etharp_remove_static_entry(ip_addr_t *ipaddr) +{ + s8_t i; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_remove_static_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr))); + + /* find or create ARP entry */ + i = find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY); + /* bail out if no entry could be found */ + if (i < 0) { + return (err_t)i; + } + + if ((arp_table[i].state != ETHARP_STATE_STABLE) || + (arp_table[i].static_entry == 0)) { + /* entry wasn't a static entry, cannot remove it */ + return ERR_ARG; + } + /* entry found, free it */ + free_entry(i); + return ERR_OK; +} +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + +/** + * Remove all ARP table entries of the specified netif. + * + * @param netif points to a network interface + */ +void ICACHE_FLASH_ATTR etharp_cleanup_netif(struct netif *netif) +{ + u8_t i; + + for (i = 0; i < ARP_TABLE_SIZE; ++i) { + u8_t state = arp_table[i].state; + if ((state != ETHARP_STATE_EMPTY) && (arp_table[i].netif == netif)) { + free_entry(i); + } + } +} + +/** + * Finds (stable) ethernet/IP address pair from ARP table + * using interface and IP address index. + * @note the addresses in the ARP table are in network order! + * + * @param netif points to interface index + * @param ipaddr points to the (network order) IP address index + * @param eth_ret points to return pointer + * @param ip_ret points to return pointer + * @return table index if found, -1 otherwise + */ +s8_t +etharp_find_addr(struct netif *netif, ip_addr_t *ipaddr, + struct eth_addr **eth_ret, ip_addr_t **ip_ret) +{ + s8_t i; + + LWIP_ASSERT("eth_ret != NULL && ip_ret != NULL", + eth_ret != NULL && ip_ret != NULL); + + LWIP_UNUSED_ARG(netif); + + i = find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY); + if((i >= 0) && (arp_table[i].state >= ETHARP_STATE_STABLE)) { + *eth_ret = &arp_table[i].ethaddr; + *ip_ret = &arp_table[i].ipaddr; + return i; + } + return -1; +} + +#if ETHARP_TRUST_IP_MAC +/** + * Updates the ARP table using the given IP packet. + * + * Uses the incoming IP packet's source address to update the + * ARP cache for the local network. The function does not alter + * or free the packet. This function must be called before the + * packet p is passed to the IP layer. + * + * @param netif The lwIP network interface on which the IP packet pbuf arrived. + * @param p The IP packet that arrived on netif. + * + * @return NULL + * + * @see pbuf_free() + */ +static void ICACHE_FLASH_ATTR +etharp_ip_input(struct netif *netif, struct pbuf *p) +{ + struct eth_hdr *ethhdr; + struct ip_hdr *iphdr; + ip_addr_t iphdr_src; + LWIP_ERROR("netif != NULL", (netif != NULL), return;); + + /* Only insert an entry if the source IP address of the + incoming IP packet comes from a host on the local network. */ + ethhdr = (struct eth_hdr *)p->payload; + iphdr = (struct ip_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR); +#if ETHARP_SUPPORT_VLAN + if (ethhdr->type == ETHTYPE_VLAN) { + iphdr = (struct ip_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR); + } +#endif /* ETHARP_SUPPORT_VLAN */ + + ip_addr_copy(iphdr_src, iphdr->src); + + /* source is not on the local network? */ + if (!ip_addr_netcmp(&iphdr_src, &(netif->ip_addr), &(netif->netmask))) { + /* do nothing */ + return; + } + + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_ip_input: updating ETHARP table.\n")); + /* update the source IP address in the cache, if present */ + /* @todo We could use ETHARP_FLAG_TRY_HARD if we think we are going to talk + * back soon (for example, if the destination IP address is ours. */ + update_arp_entry(netif, &iphdr_src, &(ethhdr->src), ETHARP_FLAG_FIND_ONLY); +} +#endif /* ETHARP_TRUST_IP_MAC */ + +/** + * Responds to ARP requests to us. Upon ARP replies to us, add entry to cache + * send out queued IP packets. Updates cache with snooped address pairs. + * + * Should be called for incoming ARP packets. The pbuf in the argument + * is freed by this function. + * + * @param netif The lwIP network interface on which the ARP packet pbuf arrived. + * @param ethaddr Ethernet address of netif. + * @param p The ARP packet that arrived on netif. Is freed by this function. + * + * @return NULL + * + * @see pbuf_free() + */ +static void ICACHE_FLASH_ATTR +etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p) +{ + struct etharp_hdr *hdr; + struct eth_hdr *ethhdr; + /* these are aligned properly, whereas the ARP header fields might not be */ + ip_addr_t sipaddr, dipaddr; + u8_t for_us; +#if LWIP_AUTOIP + const u8_t * ethdst_hwaddr; +#endif /* LWIP_AUTOIP */ +#ifdef EBUF_LWIP + struct pbuf *q; +#endif /* EBUF_LWIP */ + + LWIP_ERROR("netif != NULL", (netif != NULL), return;); + + /* drop short ARP packets: we have to check for p->len instead of p->tot_len here + since a struct etharp_hdr is pointed to p->payload, so it musn't be chained! */ + if (p->len < SIZEOF_ETHARP_PACKET) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, + ("etharp_arp_input: packet dropped, too short (%"S16_F"/%"S16_F")\n", p->tot_len, + (s16_t)SIZEOF_ETHARP_PACKET)); + ETHARP_STATS_INC(etharp.lenerr); + ETHARP_STATS_INC(etharp.drop); + pbuf_free(p); + return; + } + + ethhdr = (struct eth_hdr *)p->payload; + hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR); +#if ETHARP_SUPPORT_VLAN + if (ethhdr->type == ETHTYPE_VLAN) { + hdr = (struct etharp_hdr *)(((u8_t*)ethhdr) + SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR); + } +#endif /* ETHARP_SUPPORT_VLAN */ + + /* RFC 826 "Packet Reception": */ + if ((hdr->hwtype != PP_HTONS(HWTYPE_ETHERNET)) || + (hdr->hwlen != ETHARP_HWADDR_LEN) || + (hdr->protolen != sizeof(ip_addr_t)) || + (hdr->proto != PP_HTONS(ETHTYPE_IP)) || + (ethhdr->type != PP_HTONS(ETHTYPE_ARP))) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, + ("etharp_arp_input: packet dropped, wrong hw type, hwlen, proto, protolen or ethernet type (%"U16_F"/%"U16_F"/%"U16_F"/%"U16_F"/%"U16_F")\n", + hdr->hwtype, hdr->hwlen, hdr->proto, hdr->protolen, ethhdr->type)); + ETHARP_STATS_INC(etharp.proterr); + ETHARP_STATS_INC(etharp.drop); + pbuf_free(p); + return; + } + ETHARP_STATS_INC(etharp.recv); + +#if LWIP_AUTOIP + /* We have to check if a host already has configured our random + * created link local address and continously check if there is + * a host with this IP-address so we can detect collisions */ + autoip_arp_reply(netif, hdr); +#endif /* LWIP_AUTOIP */ + + /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without + * structure packing (not using structure copy which breaks strict-aliasing rules). */ + IPADDR2_COPY(&sipaddr, &hdr->sipaddr); + IPADDR2_COPY(&dipaddr, &hdr->dipaddr); + + /* this interface is not configured? */ + if (ip_addr_isany(&netif->ip_addr)) { + for_us = 0; + } else { + /* ARP packet directed to us? */ + for_us = (u8_t)ip_addr_cmp(&dipaddr, &(netif->ip_addr)); + } + + /* ARP message directed to us? + -> add IP address in ARP cache; assume requester wants to talk to us, + can result in directly sending the queued packets for this host. + ARP message not directed to us? + -> update the source IP address in the cache, if present */ + update_arp_entry(netif, &sipaddr, &(hdr->shwaddr), + for_us ? ETHARP_FLAG_TRY_HARD : ETHARP_FLAG_FIND_ONLY); + + /* now act on the message itself */ + switch (hdr->opcode) { + /* ARP request? */ + case PP_HTONS(ARP_REQUEST): + /* ARP request. If it asked for our address, we send out a + * reply. In any case, we time-stamp any existing ARP entry, + * and possiby send out an IP packet that was queued on it. */ + + LWIP_DEBUGF (ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: incoming ARP request\n")); + /* ARP request for our address? */ + if (for_us) { + + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: replying to ARP request for our IP address\n")); + /* Re-use pbuf to send ARP reply. + Since we are re-using an existing pbuf, we can't call etharp_raw since + that would allocate a new pbuf. */ + hdr->opcode = htons(ARP_REPLY); + + IPADDR2_COPY(&hdr->dipaddr, &hdr->sipaddr); + IPADDR2_COPY(&hdr->sipaddr, &netif->ip_addr); + + LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!", + (netif->hwaddr_len == ETHARP_HWADDR_LEN)); +#if LWIP_AUTOIP + /* If we are using Link-Local, all ARP packets that contain a Link-Local + * 'sender IP address' MUST be sent using link-layer broadcast instead of + * link-layer unicast. (See RFC3927 Section 2.5, last paragraph) */ + ethdst_hwaddr = ip_addr_islinklocal(&netif->ip_addr) ? (u8_t*)(ethbroadcast.addr) : hdr->shwaddr.addr; +#endif /* LWIP_AUTOIP */ + + ETHADDR16_COPY(&hdr->dhwaddr, &hdr->shwaddr); +#if LWIP_AUTOIP + ETHADDR16_COPY(ðhdr->dest, ethdst_hwaddr); +#else /* LWIP_AUTOIP */ + ETHADDR16_COPY(ðhdr->dest, &hdr->shwaddr); +#endif /* LWIP_AUTOIP */ + ETHADDR16_COPY(&hdr->shwaddr, ethaddr); + ETHADDR16_COPY(ðhdr->src, ethaddr); + + /* hwtype, hwaddr_len, proto, protolen and the type in the ethernet header + are already correct, we tested that before */ +#ifdef EBUF_LWIP + /* + * don't do flip-flop here... do a copy here. + * otherwise, we need to handle existing pbuf->eb in ieee80211_output.c + */ + + q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); + if (q != NULL) { + pbuf_copy(q, p); + //pbuf_free(p); + } else { + LWIP_ASSERT("q != NULL", q != NULL); + } + + netif->linkoutput(netif, q); + pbuf_free(q); +#else + + /* return ARP reply */ + netif->linkoutput(netif, p); +#endif /* ESF_LWIP */ + /* we are not configured? */ + } else if (ip_addr_isany(&netif->ip_addr)) { + /* { for_us == 0 and netif->ip_addr.addr == 0 } */ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: we are unconfigured, ARP request ignored.\n")); + /* request was not directed to us */ + } else { + /* { for_us == 0 and netif->ip_addr.addr != 0 } */ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: ARP request was not for us.\n")); + } + break; + case PP_HTONS(ARP_REPLY): + /* ARP reply. We already updated the ARP cache earlier. */ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: incoming ARP reply\n")); +#if (LWIP_DHCP && DHCP_DOES_ARP_CHECK) + /* DHCP wants to know about ARP replies from any host with an + * IP address also offered to us by the DHCP server. We do not + * want to take a duplicate IP address on a single network. + * @todo How should we handle redundant (fail-over) interfaces? */ + dhcp_arp_reply(netif, &sipaddr); +#endif /* (LWIP_DHCP && DHCP_DOES_ARP_CHECK) */ + break; + default: + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: ARP unknown opcode type %"S16_F"\n", htons(hdr->opcode))); + ETHARP_STATS_INC(etharp.err); + break; + } + /* free ARP packet */ + pbuf_free(p); +} + +/** Just a small helper function that sends a pbuf to an ethernet address + * in the arp_table specified by the index 'arp_idx'. + */ +static err_t ICACHE_FLASH_ATTR +etharp_output_to_arp_index(struct netif *netif, struct pbuf *q, u8_t arp_idx) +{ + LWIP_ASSERT("arp_table[arp_idx].state >= ETHARP_STATE_STABLE", + arp_table[arp_idx].state >= ETHARP_STATE_STABLE); + /* if arp table entry is about to expire: re-request it, + but only if its state is ETHARP_STATE_STABLE to prevent flooding the + network with ARP requests if this address is used frequently. */ + if ((arp_table[arp_idx].state == ETHARP_STATE_STABLE) && + (arp_table[arp_idx].ctime >= ARP_AGE_REREQUEST_USED)) { + if (etharp_request(netif, &arp_table[arp_idx].ipaddr) == ERR_OK) { + arp_table[arp_idx].state = ETHARP_STATE_STABLE_REREQUESTING; + } + } + + return etharp_send_ip(netif, q, (struct eth_addr*)(netif->hwaddr), + &arp_table[arp_idx].ethaddr); +} + +/** + * Resolve and fill-in Ethernet address header for outgoing IP packet. + * + * For IP multicast and broadcast, corresponding Ethernet addresses + * are selected and the packet is transmitted on the link. + * + * For unicast addresses, the packet is submitted to etharp_query(). In + * case the IP address is outside the local network, the IP address of + * the gateway is used. + * + * @param netif The lwIP network interface which the IP packet will be sent on. + * @param q The pbuf(s) containing the IP packet to be sent. + * @param ipaddr The IP address of the packet destination. + * + * @return + * - ERR_RTE No route to destination (no gateway to external networks), + * or the return type of either etharp_query() or etharp_send_ip(). + */ +err_t +etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr) +{ + struct eth_addr *dest, mcastaddr; + + /* make room for Ethernet header - should not fail */ + if (pbuf_header(q, sizeof(struct eth_hdr)) != 0) { + /* bail out */ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("etharp_output: could not allocate room for header.\n")); + LINK_STATS_INC(link.lenerr); + return ERR_BUF; + } + + /* assume unresolved Ethernet address */ + dest = NULL; + /* Determine on destination hardware address. Broadcasts and multicasts + * are special, other IP addresses are looked up in the ARP table. */ + + /* broadcast destination IP address? */ + if (ip_addr_isbroadcast(ipaddr, netif)) { + /* broadcast on Ethernet also */ + dest = (struct eth_addr *)ðbroadcast; + /* multicast destination IP address? */ + } else if (ip_addr_ismulticast(ipaddr)) { + /* Hash IP multicast address to MAC address.*/ + mcastaddr.addr[0] = 0x01; + mcastaddr.addr[1] = 0x00; + mcastaddr.addr[2] = 0x5e; + mcastaddr.addr[3] = ip4_addr2(ipaddr) & 0x7f; + mcastaddr.addr[4] = ip4_addr3(ipaddr); + mcastaddr.addr[5] = ip4_addr4(ipaddr); + /* destination Ethernet address is multicast */ + dest = &mcastaddr; + /* unicast destination IP address? */ + } else { + s8_t i; + /* outside local network? if so, this can neither be a global broadcast nor + a subnet broadcast. */ + if (!ip_addr_netcmp(ipaddr, &(netif->ip_addr), &(netif->netmask)) && + !ip_addr_islinklocal(ipaddr)) { +#if LWIP_AUTOIP + struct ip_hdr *iphdr = (struct ip_hdr*)((u8_t*)q->payload + + sizeof(struct eth_hdr)); + /* According to RFC 3297, chapter 2.6.2 (Forwarding Rules), a packet with + a link-local source address must always be "directly to its destination + on the same physical link. The host MUST NOT send the packet to any + router for forwarding". */ + if (!ip_addr_islinklocal(&iphdr->src)) +#endif /* LWIP_AUTOIP */ + { + /* interface has default gateway? */ + if (!ip_addr_isany(&netif->gw)) { + /* send to hardware address of default gateway IP address */ + ipaddr = &(netif->gw); + /* no default gateway available */ + } else { + /* no route to destination error (default gateway missing) */ + return ERR_RTE; + } + } + } +#if LWIP_NETIF_HWADDRHINT + if (netif->addr_hint != NULL) { + /* per-pcb cached entry was given */ + u8_t etharp_cached_entry = *(netif->addr_hint); + if (etharp_cached_entry < ARP_TABLE_SIZE) { +#endif /* LWIP_NETIF_HWADDRHINT */ + if ((arp_table[etharp_cached_entry].state >= ETHARP_STATE_STABLE) && + (ip_addr_cmp(ipaddr, &arp_table[etharp_cached_entry].ipaddr))) { + /* the per-pcb-cached entry is stable and the right one! */ + ETHARP_STATS_INC(etharp.cachehit); + return etharp_output_to_arp_index(netif, q, etharp_cached_entry); + } +#if LWIP_NETIF_HWADDRHINT + } + } +#endif /* LWIP_NETIF_HWADDRHINT */ + /* find stable entry: do this here since this is a critical path for + throughput and etharp_find_entry() is kind of slow */ + for (i = 0; i < ARP_TABLE_SIZE; i++) { + if ((arp_table[i].state >= ETHARP_STATE_STABLE) && + (ip_addr_cmp(ipaddr, &arp_table[i].ipaddr))) { + /* found an existing, stable entry */ + ETHARP_SET_HINT(netif, i); + return etharp_output_to_arp_index(netif, q, i); + } + } + /* queue on destination Ethernet address belonging to ipaddr */ + return etharp_query(netif, ipaddr, q); + } + + /* continuation for multicast/broadcast destinations */ + /* obtain source Ethernet address of the given interface */ + /* send packet directly on the link */ + return etharp_send_ip(netif, q, (struct eth_addr*)(netif->hwaddr), dest); +} + +/** + * Send an ARP request for the given IP address and/or queue a packet. + * + * If the IP address was not yet in the cache, a pending ARP cache entry + * is added and an ARP request is sent for the given address. The packet + * is queued on this entry. + * + * If the IP address was already pending in the cache, a new ARP request + * is sent for the given address. The packet is queued on this entry. + * + * If the IP address was already stable in the cache, and a packet is + * given, it is directly sent and no ARP request is sent out. + * + * If the IP address was already stable in the cache, and no packet is + * given, an ARP request is sent out. + * + * @param netif The lwIP network interface on which ipaddr + * must be queried for. + * @param ipaddr The IP address to be resolved. + * @param q If non-NULL, a pbuf that must be delivered to the IP address. + * q is not freed by this function. + * + * @note q must only be ONE packet, not a packet queue! + * + * @return + * - ERR_BUF Could not make room for Ethernet header. + * - ERR_MEM Hardware address unknown, and no more ARP entries available + * to query for address or queue the packet. + * - ERR_MEM Could not queue packet due to memory shortage. + * - ERR_RTE No route to destination (no gateway to external networks). + * - ERR_ARG Non-unicast address given, those will not appear in ARP cache. + * + */ +err_t +etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q) +{ + struct eth_addr * srcaddr = (struct eth_addr *)netif->hwaddr; + err_t result = ERR_MEM; + s8_t i; /* ARP entry index */ + + /* non-unicast address? */ + if (ip_addr_isbroadcast(ipaddr, netif) || + ip_addr_ismulticast(ipaddr) || + ip_addr_isany(ipaddr)) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: will not add non-unicast IP address to ARP cache\n")); + return ERR_ARG; + } + + /* find entry in ARP cache, ask to create entry if queueing packet */ + i = find_entry(ipaddr, ETHARP_FLAG_TRY_HARD); + + /* could not find or create entry? */ + if (i < 0) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not create ARP entry\n")); + if (q) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: packet dropped\n")); + ETHARP_STATS_INC(etharp.memerr); + } + return (err_t)i; + } + + /* mark a fresh entry as pending (we just sent a request) */ + if (arp_table[i].state == ETHARP_STATE_EMPTY) { + arp_table[i].state = ETHARP_STATE_PENDING; + } + + /* { i is either a STABLE or (new or existing) PENDING entry } */ + LWIP_ASSERT("arp_table[i].state == PENDING or STABLE", + ((arp_table[i].state == ETHARP_STATE_PENDING) || + (arp_table[i].state >= ETHARP_STATE_STABLE))); + + /* do we have a pending entry? or an implicit query request? */ + if ((arp_table[i].state == ETHARP_STATE_PENDING) || (q == NULL)) { + /* try to resolve it; send out ARP request */ + result = etharp_request(netif, ipaddr); + if (result != ERR_OK) { + /* ARP request couldn't be sent */ + /* We don't re-send arp request in etharp_tmr, but we still queue packets, + since this failure could be temporary, and the next packet calling + etharp_query again could lead to sending the queued packets. */ + } + if (q == NULL) { + return result; + } + } + + /* packet given? */ + LWIP_ASSERT("q != NULL", q != NULL); + /* stable entry? */ + if (arp_table[i].state >= ETHARP_STATE_STABLE) { + /* we have a valid IP->Ethernet address mapping */ + ETHARP_SET_HINT(netif, i); + /* send the packet */ + result = etharp_send_ip(netif, q, srcaddr, &(arp_table[i].ethaddr)); + /* pending entry? (either just created or already pending */ + } else if (arp_table[i].state == ETHARP_STATE_PENDING) { + /* entry is still pending, queue the given packet 'q' */ + struct pbuf *p; + int copy_needed = 0; + /* IF q includes a PBUF_REF, PBUF_POOL or PBUF_RAM, we have no choice but + * to copy the whole queue into a new PBUF_RAM (see bug #11400) + * PBUF_ROMs can be left as they are, since ROM must not get changed. */ + p = q; + while (p) { + LWIP_ASSERT("no packet queues allowed!", (p->len != p->tot_len) || (p->next == 0)); + if(p->type != PBUF_ROM) { + copy_needed = 1; + break; + } + p = p->next; + } + if(copy_needed) { + /* copy the whole packet into new pbufs */ + p = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); + if(p != NULL) { + if (pbuf_copy(p, q) != ERR_OK) { + pbuf_free(p); + p = NULL; + } + } + } else { + /* referencing the old pbuf is enough */ + p = q; + pbuf_ref(p); + } + /* packet could be taken over? */ + if (p != NULL) { + /* queue packet ... */ +#if ARP_QUEUEING + struct etharp_q_entry *new_entry; + /* allocate a new arp queue entry */ + new_entry = (struct etharp_q_entry *)memp_malloc(MEMP_ARP_QUEUE); + if (new_entry != NULL) { + unsigned int qlen = 0; + new_entry->next = 0; + new_entry->p = p; + if(arp_table[i].q != NULL) { + /* queue was already existent, append the new entry to the end */ + struct etharp_q_entry *r; + r = arp_table[i].q; + qlen++; + while (r->next != NULL) { + r = r->next; + qlen++; + } + r->next = new_entry; + } else { + /* queue did not exist, first item in queue */ + arp_table[i].q = new_entry; + } + if(qlen >= 3) { + struct etharp_q_entry *old; + old = arp_table[i].q; + arp_table[i].q = arp_table[i].q->next; + pbuf_free(old->p); + memp_free(MEM_ARP_QUEUE, old); + } + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i)); + result = ERR_OK; + } else { + /* the pool MEMP_ARP_QUEUE is empty */ + pbuf_free(p); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q)); + result = ERR_MEM; + } +#else /* ARP_QUEUEING */ + /* always queue one packet per ARP request only, freeing a previously queued packet */ + if (arp_table[i].q != NULL) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: dropped previously queued packet %p for ARP entry %"S16_F"\n", (void *)q, (s16_t)i)); + pbuf_free(arp_table[i].q); + } + arp_table[i].q = p; + result = ERR_OK; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i)); +#endif /* ARP_QUEUEING */ + } else { + ETHARP_STATS_INC(etharp.memerr); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q)); + result = ERR_MEM; + } + } + return result; +} + +/** + * Send a raw ARP packet (opcode and all addresses can be modified) + * + * @param netif the lwip network interface on which to send the ARP packet + * @param ethsrc_addr the source MAC address for the ethernet header + * @param ethdst_addr the destination MAC address for the ethernet header + * @param hwsrc_addr the source MAC address for the ARP protocol header + * @param ipsrc_addr the source IP address for the ARP protocol header + * @param hwdst_addr the destination MAC address for the ARP protocol header + * @param ipdst_addr the destination IP address for the ARP protocol header + * @param opcode the type of the ARP packet + * @return ERR_OK if the ARP packet has been sent + * ERR_MEM if the ARP packet couldn't be allocated + * any other err_t on failure + */ +#if !LWIP_AUTOIP +static +#endif /* LWIP_AUTOIP */ +err_t ICACHE_FLASH_ATTR +etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr, + const struct eth_addr *ethdst_addr, + const struct eth_addr *hwsrc_addr, const ip_addr_t *ipsrc_addr, + const struct eth_addr *hwdst_addr, const ip_addr_t *ipdst_addr, + const u16_t opcode) +{ + struct pbuf *p; + err_t result = ERR_OK; + struct eth_hdr *ethhdr; + struct etharp_hdr *hdr; +#if LWIP_AUTOIP + const u8_t * ethdst_hwaddr; +#endif /* LWIP_AUTOIP */ + + /* allocate a pbuf for the outgoing ARP request packet */ + p = pbuf_alloc(PBUF_RAW, SIZEOF_ETHARP_PACKET, PBUF_RAM); + /* could allocate a pbuf for an ARP request? */ + if (p == NULL) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("etharp_raw: could not allocate pbuf for ARP request.\n")); + ETHARP_STATS_INC(etharp.memerr); + return ERR_MEM; + } + LWIP_ASSERT("check that first pbuf can hold struct etharp_hdr", + (p->len >= SIZEOF_ETHARP_PACKET)); + + ethhdr = (struct eth_hdr *)p->payload; + hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_raw: sending raw ARP packet.\n")); + hdr->opcode = htons(opcode); + + LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!", + (netif->hwaddr_len == ETHARP_HWADDR_LEN)); +#if LWIP_AUTOIP + /* If we are using Link-Local, all ARP packets that contain a Link-Local + * 'sender IP address' MUST be sent using link-layer broadcast instead of + * link-layer unicast. (See RFC3927 Section 2.5, last paragraph) */ + ethdst_hwaddr = ip_addr_islinklocal(ipsrc_addr) ? (u8_t*)(ethbroadcast.addr) : ethdst_addr->addr; +#endif /* LWIP_AUTOIP */ + /* Write the ARP MAC-Addresses */ + ETHADDR16_COPY(&hdr->shwaddr, hwsrc_addr); + ETHADDR16_COPY(&hdr->dhwaddr, hwdst_addr); + /* Write the Ethernet MAC-Addresses */ +#if LWIP_AUTOIP + ETHADDR16_COPY(ðhdr->dest, ethdst_hwaddr); +#else /* LWIP_AUTOIP */ + ETHADDR16_COPY(ðhdr->dest, ethdst_addr); +#endif /* LWIP_AUTOIP */ + ETHADDR16_COPY(ðhdr->src, ethsrc_addr); + /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without + * structure packing. */ + IPADDR2_COPY(&hdr->sipaddr, ipsrc_addr); + IPADDR2_COPY(&hdr->dipaddr, ipdst_addr); + + hdr->hwtype = PP_HTONS(HWTYPE_ETHERNET); + hdr->proto = PP_HTONS(ETHTYPE_IP); + /* set hwlen and protolen */ + hdr->hwlen = ETHARP_HWADDR_LEN; + hdr->protolen = sizeof(ip_addr_t); + + ethhdr->type = PP_HTONS(ETHTYPE_ARP); + /* send ARP query */ + result = netif->linkoutput(netif, p); + ETHARP_STATS_INC(etharp.xmit); + /* free ARP query packet */ + pbuf_free(p); + p = NULL; + /* could not allocate pbuf for ARP request */ + + return result; +} + +/** + * Send an ARP request packet asking for ipaddr. + * + * @param netif the lwip network interface on which to send the request + * @param ipaddr the IP address for which to ask + * @return ERR_OK if the request has been sent + * ERR_MEM if the ARP packet couldn't be allocated + * any other err_t on failure + */ +err_t +etharp_request(struct netif *netif, ip_addr_t *ipaddr) +{ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_request: sending ARP request.\n")); + return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, ðbroadcast, + (struct eth_addr *)netif->hwaddr, &netif->ip_addr, ðzero, + ipaddr, ARP_REQUEST); +} +#endif /* LWIP_ARP */ + +/** + * Process received ethernet frames. Using this function instead of directly + * calling ip_input and passing ARP frames through etharp in ethernetif_input, + * the ARP cache is protected from concurrent access. + * + * @param p the recevied packet, p->payload pointing to the ethernet header + * @param netif the network interface on which the packet was received + */ +err_t +ethernet_input(struct pbuf *p, struct netif *netif) +{ + struct eth_hdr* ethhdr; + u16_t type; + s16_t ip_hdr_offset = SIZEOF_ETH_HDR; + + if (p->len <= SIZEOF_ETH_HDR) { + /* a packet with only an ethernet header (or less) is not valid for us modify by ives at 2014.4.24*/ + ETHARP_STATS_INC(etharp.proterr); + ETHARP_STATS_INC(etharp.drop); + goto free_and_return; + } + + /* points to packet payload, which starts with an Ethernet header */ + ethhdr = (struct eth_hdr *)p->payload; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, + ("ethernet_input: dest:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", src:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", type:%"X16_F"\n", + (unsigned)ethhdr->dest.addr[0], (unsigned)ethhdr->dest.addr[1], (unsigned)ethhdr->dest.addr[2], + (unsigned)ethhdr->dest.addr[3], (unsigned)ethhdr->dest.addr[4], (unsigned)ethhdr->dest.addr[5], + (unsigned)ethhdr->src.addr[0], (unsigned)ethhdr->src.addr[1], (unsigned)ethhdr->src.addr[2], + (unsigned)ethhdr->src.addr[3], (unsigned)ethhdr->src.addr[4], (unsigned)ethhdr->src.addr[5], + (unsigned)htons(ethhdr->type))); + + type = ethhdr->type; +#if ETHARP_SUPPORT_VLAN + if (type == PP_HTONS(ETHTYPE_VLAN)) { + struct eth_vlan_hdr *vlan = (struct eth_vlan_hdr*)(((char*)ethhdr) + SIZEOF_ETH_HDR); + if (p->len <= SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR) { + /* a packet with only an ethernet/vlan header (or less) is not valid for us modify by ives at 2014.4.24*/ + ETHARP_STATS_INC(etharp.proterr); + ETHARP_STATS_INC(etharp.drop); + goto free_and_return; + } +#ifdef ETHARP_VLAN_CHECK /* if not, allow all VLANs */ + if (VLAN_ID(vlan) != ETHARP_VLAN_CHECK) { + /* silently ignore this packet: not for our VLAN */ + pbuf_free(p); + return ERR_OK; + } +#endif /* ETHARP_VLAN_CHECK */ + type = vlan->tpid; + ip_hdr_offset = SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR; + } +#endif /* ETHARP_SUPPORT_VLAN */ + +#if LWIP_ARP_FILTER_NETIF + netif = LWIP_ARP_FILTER_NETIF_FN(p, netif, htons(type)); +#endif /* LWIP_ARP_FILTER_NETIF*/ + + switch (type) { +#if LWIP_ARP + /* IP packet? */ + case PP_HTONS(ETHTYPE_IP): + if (!(netif->flags & NETIF_FLAG_ETHARP)) { + goto free_and_return; + } +#if ETHARP_TRUST_IP_MAC + /* update ARP table */ + etharp_ip_input(netif, p); +#endif /* ETHARP_TRUST_IP_MAC */ + /* skip Ethernet header */ + if(pbuf_header(p, -ip_hdr_offset)) { + LWIP_ASSERT("Can't move over header in packet", 0); + goto free_and_return; + } else { + /* pass to IP layer */ + ip_input(p, netif); + } + break; + + case PP_HTONS(ETHTYPE_ARP): + if (!(netif->flags & NETIF_FLAG_ETHARP)) { + goto free_and_return; + } + /* pass p to ARP module */ + etharp_arp_input(netif, (struct eth_addr*)(netif->hwaddr), p); + break; +#endif /* LWIP_ARP */ +#if PPPOE_SUPPORT + case PP_HTONS(ETHTYPE_PPPOEDISC): /* PPP Over Ethernet Discovery Stage */ + pppoe_disc_input(netif, p); + break; + + case PP_HTONS(ETHTYPE_PPPOE): /* PPP Over Ethernet Session Stage */ + pppoe_data_input(netif, p); + break; +#endif /* PPPOE_SUPPORT */ + + default: + ETHARP_STATS_INC(etharp.proterr); + ETHARP_STATS_INC(etharp.drop); + goto free_and_return; + } + + /* This means the pbuf is freed or consumed, + so the caller doesn't have to free it again */ + return ERR_OK; + +free_and_return: + pbuf_free(p); + return ERR_OK; +} +#endif /* LWIP_ARP || LWIP_ETHERNET */ diff --git a/variants/lwip/pins_arduino.h b/variants/lwip/pins_arduino.h new file mode 100644 index 000000000..0541019da --- /dev/null +++ b/variants/lwip/pins_arduino.h @@ -0,0 +1,37 @@ +/* + pins_arduino.h - Pin definition functions for Arduino + Part of Arduino - http://www.arduino.cc/ + + Copyright (c) 2007 David A. Mellis + Modified for ESP8266 platform by Ivan Grokhotkov, 2014-2015. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + $Id: wiring.h 249 2007-02-03 16:52:51Z mellis $ +*/ + +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include "common.h" + +static const uint8_t SDA = 4; +static const uint8_t SCL = 5; + +static const uint8_t BUILTIN_LED = 1; +static const uint8_t LED_BUILTIN = 1; + +#endif /* Pins_Arduino_h */ From 74358a24b0f97e52f35b29800fe8ee63442b5c5a Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Sun, 10 Apr 2016 22:48:05 +0300 Subject: [PATCH 16/46] Add coredev device on the bottom --- boards.txt | 232 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 219 insertions(+), 13 deletions(-) diff --git a/boards.txt b/boards.txt index 18c0ac3fb..46b0e7191 100644 --- a/boards.txt +++ b/boards.txt @@ -33,19 +33,6 @@ generic.build.debug_level= generic.build.lwip_lib=-llwip generic.build.lwip_flags= -generic.menu.LwIPVariant.Espressif=Espressif (xcc) -generic.menu.LwIPVariant.Espressif.build.lwip_lib=-llwip -generic.menu.LwIPVariant.Espressif.build.lwip_flags= -generic.menu.LwIPVariant.Espressif.build.variant=generic -generic.menu.LwIPVariant.Prebuilt=Prebuilt Source (gcc) -generic.menu.LwIPVariant.Prebuilt.build.lwip_lib=-llwip_gcc -generic.menu.LwIPVariant.Prebuilt.build.lwip_flags=-DLWIP_OPEN_SRC -generic.menu.LwIPVariant.Prebuilt.build.variant=generic -generic.menu.LwIPVariant.OpenSource=Open Source (gcc) -generic.menu.LwIPVariant.OpenSource.build.lwip_lib= -generic.menu.LwIPVariant.OpenSource.build.lwip_flags=-DLWIP_OPEN_SRC -generic.menu.LwIPVariant.OpenSource.build.variant=lwip - generic.menu.CpuFrequency.80=80 MHz generic.menu.CpuFrequency.80.build.f_cpu=80000000L generic.menu.CpuFrequency.160=160 MHz @@ -1378,3 +1365,222 @@ wifinfo.menu.UploadSpeed.512000.windows=512000 wifinfo.menu.UploadSpeed.512000.upload.speed=512000 wifinfo.menu.UploadSpeed.921600=921600 wifinfo.menu.UploadSpeed.921600.upload.speed=921600 + + +############################################################## +coredev.name=Generic ESP8266 Module + +coredev.upload.tool=esptool +coredev.upload.speed=115200 +coredev.upload.resetmethod=ck +coredev.upload.maximum_size=434160 +coredev.upload.maximum_data_size=81920 +coredev.upload.wait_for_upload_port=true +coredev.serial.disableDTR=true +coredev.serial.disableRTS=true + +coredev.build.mcu=esp8266 +coredev.build.f_cpu=80000000L +coredev.build.board=ESP8266_ESP01 +coredev.build.core=esp8266 +coredev.build.variant=generic +coredev.build.flash_mode=qio +coredev.build.spiffs_pagesize=256 +coredev.build.debug_port= +coredev.build.debug_level= +coredev.build.lwip_lib=-llwip +coredev.build.lwip_flags= + +coredev.menu.LwIPVariant.Espressif=Espressif (xcc) +coredev.menu.LwIPVariant.Espressif.build.lwip_lib=-llwip +coredev.menu.LwIPVariant.Espressif.build.lwip_flags= +coredev.menu.LwIPVariant.Espressif.build.variant=generic +coredev.menu.LwIPVariant.Prebuilt=Prebuilt Source (gcc) +coredev.menu.LwIPVariant.Prebuilt.build.lwip_lib=-llwip_gcc +coredev.menu.LwIPVariant.Prebuilt.build.lwip_flags=-DLWIP_OPEN_SRC +coredev.menu.LwIPVariant.Prebuilt.build.variant=generic +coredev.menu.LwIPVariant.OpenSource=Open Source (gcc) +coredev.menu.LwIPVariant.OpenSource.build.lwip_lib= +coredev.menu.LwIPVariant.OpenSource.build.lwip_flags=-DLWIP_OPEN_SRC +coredev.menu.LwIPVariant.OpenSource.build.variant=lwip + +coredev.menu.CpuFrequency.80=80 MHz +coredev.menu.CpuFrequency.80.build.f_cpu=80000000L +coredev.menu.CpuFrequency.160=160 MHz +coredev.menu.CpuFrequency.160.build.f_cpu=160000000L + +coredev.menu.FlashFreq.40=40MHz +coredev.menu.FlashFreq.40.build.flash_freq=40 +coredev.menu.FlashFreq.80=80MHz +coredev.menu.FlashFreq.80.build.flash_freq=80 + +coredev.menu.FlashMode.dio=DIO +coredev.menu.FlashMode.dio.build.flash_mode=dio +coredev.menu.FlashMode.qio=QIO +coredev.menu.FlashMode.qio.build.flash_mode=qio + +coredev.menu.UploadSpeed.115200=115200 +coredev.menu.UploadSpeed.115200.upload.speed=115200 +coredev.menu.UploadSpeed.9600=9600 +coredev.menu.UploadSpeed.9600.upload.speed=9600 +coredev.menu.UploadSpeed.57600=57600 +coredev.menu.UploadSpeed.57600.upload.speed=57600 +coredev.menu.UploadSpeed.256000.windows=256000 +coredev.menu.UploadSpeed.256000.upload.speed=256000 +coredev.menu.UploadSpeed.230400.linux=230400 +coredev.menu.UploadSpeed.230400.macosx=230400 +coredev.menu.UploadSpeed.230400.upload.speed=230400 +coredev.menu.UploadSpeed.460800.linux=460800 +coredev.menu.UploadSpeed.460800.macosx=460800 +coredev.menu.UploadSpeed.460800.upload.speed=460800 +coredev.menu.UploadSpeed.512000.windows=512000 +coredev.menu.UploadSpeed.512000.upload.speed=512000 +coredev.menu.UploadSpeed.921600=921600 +coredev.menu.UploadSpeed.921600.upload.speed=921600 + +coredev.menu.FlashSize.512K64=512K (64K SPIFFS) +coredev.menu.FlashSize.512K64.build.flash_size=512K +coredev.menu.FlashSize.512K64.build.flash_ld=eagle.flash.512k64.ld +coredev.menu.FlashSize.512K64.build.spiffs_start=0x6B000 +coredev.menu.FlashSize.512K64.build.spiffs_end=0x7B000 +coredev.menu.FlashSize.512K64.build.spiffs_blocksize=4096 +coredev.menu.FlashSize.512K64.upload.maximum_size=434160 + +coredev.menu.FlashSize.512K128=512K (128K SPIFFS) +coredev.menu.FlashSize.512K128.build.flash_size=512K +coredev.menu.FlashSize.512K128.build.flash_ld=eagle.flash.512k128.ld +coredev.menu.FlashSize.512K128.build.spiffs_start=0x5B000 +coredev.menu.FlashSize.512K128.build.spiffs_end=0x7B000 +coredev.menu.FlashSize.512K128.build.spiffs_blocksize=4096 +coredev.menu.FlashSize.512K128.upload.maximum_size=368624 + +coredev.menu.FlashSize.512K0=512K (no SPIFFS) +coredev.menu.FlashSize.512K0.build.flash_size=512K +coredev.menu.FlashSize.512K0.build.flash_ld=eagle.flash.512k0.ld +coredev.menu.FlashSize.512K0.upload.maximum_size=499696 + +coredev.menu.FlashSize.1M512=1M (512K SPIFFS) +coredev.menu.FlashSize.1M512.build.flash_size=1M +coredev.menu.FlashSize.1M512.build.flash_ld=eagle.flash.1m512.ld +coredev.menu.FlashSize.1M512.build.spiffs_start=0x7B000 +coredev.menu.FlashSize.1M512.build.spiffs_end=0xFB000 +coredev.menu.FlashSize.1M512.build.spiffs_blocksize=8192 +coredev.menu.FlashSize.1M512.upload.maximum_size=499696 + +coredev.menu.FlashSize.1M256=1M (256K SPIFFS) +coredev.menu.FlashSize.1M256.build.flash_size=1M +coredev.menu.FlashSize.1M256.build.flash_ld=eagle.flash.1m256.ld +coredev.menu.FlashSize.1M256.build.spiffs_start=0xBB000 +coredev.menu.FlashSize.1M256.build.spiffs_end=0xFB000 +coredev.menu.FlashSize.1M256.build.spiffs_blocksize=4096 +coredev.menu.FlashSize.1M256.upload.maximum_size=761840 + +coredev.menu.FlashSize.1M192=1M (192K SPIFFS) +coredev.menu.FlashSize.1M192.build.flash_size=1M +coredev.menu.FlashSize.1M192.build.flash_ld=eagle.flash.1m192.ld +coredev.menu.FlashSize.1M192.build.spiffs_start=0xCB000 +coredev.menu.FlashSize.1M192.build.spiffs_end=0xFB000 +coredev.menu.FlashSize.1M192.build.spiffs_blocksize=4096 +coredev.menu.FlashSize.1M192.upload.maximum_size=827376 + +coredev.menu.FlashSize.1M160=1M (160K SPIFFS) +coredev.menu.FlashSize.1M160.build.flash_size=1M +coredev.menu.FlashSize.1M160.build.flash_ld=eagle.flash.1m160.ld +coredev.menu.FlashSize.1M160.build.spiffs_start=0xD3000 +coredev.menu.FlashSize.1M160.build.spiffs_end=0xFB000 +coredev.menu.FlashSize.1M160.build.spiffs_blocksize=4096 +coredev.menu.FlashSize.1M160.upload.maximum_size=860144 + +coredev.menu.FlashSize.1M144=1M (144K SPIFFS) +coredev.menu.FlashSize.1M144.build.flash_size=1M +coredev.menu.FlashSize.1M144.build.flash_ld=eagle.flash.1m144.ld +coredev.menu.FlashSize.1M144.build.spiffs_start=0xD7000 +coredev.menu.FlashSize.1M144.build.spiffs_end=0xFB000 +coredev.menu.FlashSize.1M144.build.spiffs_blocksize=4096 +coredev.menu.FlashSize.1M144.upload.maximum_size=876528 + +coredev.menu.FlashSize.1M128=1M (128K SPIFFS) +coredev.menu.FlashSize.1M128.build.flash_size=1M +coredev.menu.FlashSize.1M128.build.flash_ld=eagle.flash.1m128.ld +coredev.menu.FlashSize.1M128.build.spiffs_start=0xDB000 +coredev.menu.FlashSize.1M128.build.spiffs_end=0xFB000 +coredev.menu.FlashSize.1M128.build.spiffs_blocksize=4096 +coredev.menu.FlashSize.1M128.upload.maximum_size=892912 + +coredev.menu.FlashSize.1M64=1M (64K SPIFFS) +coredev.menu.FlashSize.1M64.build.flash_size=1M +coredev.menu.FlashSize.1M64.build.flash_ld=eagle.flash.1m64.ld +coredev.menu.FlashSize.1M64.build.spiffs_start=0xEB000 +coredev.menu.FlashSize.1M64.build.spiffs_end=0xFB000 +coredev.menu.FlashSize.1M64.build.spiffs_blocksize=4096 +coredev.menu.FlashSize.1M64.upload.maximum_size=958448 + +coredev.menu.FlashSize.2M=2M (1M SPIFFS) +coredev.menu.FlashSize.2M.build.flash_size=2M +coredev.menu.FlashSize.2M.build.flash_ld=eagle.flash.2m.ld +coredev.menu.FlashSize.2M.build.spiffs_start=0x100000 +coredev.menu.FlashSize.2M.build.spiffs_end=0x1FB000 +coredev.menu.FlashSize.2M.build.spiffs_blocksize=8192 +coredev.menu.FlashSize.2M.upload.maximum_size=1044464 + +coredev.menu.FlashSize.4M1M=4M (1M SPIFFS) +coredev.menu.FlashSize.4M1M.build.flash_size=4M +coredev.menu.FlashSize.4M1M.build.flash_ld=eagle.flash.4m1m.ld +coredev.menu.FlashSize.4M1M.build.spiffs_start=0x300000 +coredev.menu.FlashSize.4M1M.build.spiffs_end=0x3FB000 +coredev.menu.FlashSize.4M1M.build.spiffs_blocksize=8192 +coredev.menu.FlashSize.4M1M.build.spiffs_pagesize=256 +coredev.menu.FlashSize.4M1M.upload.maximum_size=1044464 + +coredev.menu.FlashSize.4M3M=4M (3M SPIFFS) +coredev.menu.FlashSize.4M3M.build.flash_size=4M +coredev.menu.FlashSize.4M3M.build.flash_ld=eagle.flash.4m.ld +coredev.menu.FlashSize.4M3M.build.spiffs_start=0x100000 +coredev.menu.FlashSize.4M3M.build.spiffs_end=0x3FB000 +coredev.menu.FlashSize.4M3M.build.spiffs_blocksize=8192 +coredev.menu.FlashSize.4M3M.upload.maximum_size=1044464 + +coredev.menu.ResetMethod.ck=ck +coredev.menu.ResetMethod.ck.upload.resetmethod=ck +coredev.menu.ResetMethod.nodemcu=nodemcu +coredev.menu.ResetMethod.nodemcu.upload.resetmethod=nodemcu + +coredev.menu.Debug.Disabled=Disabled +coredev.menu.Debug.Disabled.build.debug_port= +coredev.menu.Debug.Serial=Serial +coredev.menu.Debug.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +coredev.menu.Debug.Serial1=Serial1 +coredev.menu.Debug.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 + +coredev.menu.DebugLevel.None____=None +coredev.menu.DebugLevel.None____.build.debug_level= +coredev.menu.DebugLevel.Core____=Core +coredev.menu.DebugLevel.Core____.build.debug_level=-DDEBUG_ESP_CORE +coredev.menu.DebugLevel.SSL_____=Core + SSL +coredev.menu.DebugLevel.SSL_____.build.debug_level=-DDEBUG_ESP_CORE -DDEBUG_ESP_SSL +coredev.menu.DebugLevel.SSL_MEM_=Core + SSL + TLS Mem +coredev.menu.DebugLevel.SSL_MEM_.build.debug_level=-DDEBUG_ESP_CORE -DDEBUG_ESP_SSL -DDEBUG_TLS_MEM +coredev.menu.DebugLevel.WiFic___=Core + WiFi +coredev.menu.DebugLevel.WiFic___.build.debug_level=-DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI +coredev.menu.DebugLevel.WiFi____=WiFi +coredev.menu.DebugLevel.WiFi____.build.debug_level=-DDEBUG_ESP_WIFI +coredev.menu.DebugLevel.HTTPClient=HTTPClient +coredev.menu.DebugLevel.HTTPClient.build.debug_level=-DDEBUG_ESP_HTTP_CLIENT +coredev.menu.DebugLevel.HTTPClient2=HTTPClient + SSL +coredev.menu.DebugLevel.HTTPClient2.build.debug_level=-DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_SSL +coredev.menu.DebugLevel.HTTPUpdate=HTTPUpdate +coredev.menu.DebugLevel.HTTPUpdate.build.debug_level=-DDEBUG_ESP_HTTP_UPDATE +coredev.menu.DebugLevel.HTTPUpdate2=HTTPClient + HTTPUpdate +coredev.menu.DebugLevel.HTTPUpdate2.build.debug_level=-DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_UPDATE +coredev.menu.DebugLevel.HTTPUpdate3=HTTPClient + HTTPUpdate + Updater +coredev.menu.DebugLevel.HTTPUpdate3.build.debug_level=-DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER +coredev.menu.DebugLevel.HTTPServer=HTTPServer +coredev.menu.DebugLevel.HTTPServer.build.debug_level=-DDEBUG_ESP_HTTP_SERVER +coredev.menu.DebugLevel.UPDATER=Updater +coredev.menu.DebugLevel.UPDATER.build.debug_level=-DDEBUG_ESP_UPDATER +coredev.menu.DebugLevel.OTA_____=OTA +coredev.menu.DebugLevel.OTA_____.build.debug_level=-DDEBUG_ESP_OTA +coredev.menu.DebugLevel.OTA2____=OTA + Updater +coredev.menu.DebugLevel.OTA2____.build.debug_level=-DDEBUG_ESP_OTA -DDEBUG_ESP_UPDATER +coredev.menu.DebugLevel.all_____=All +coredev.menu.DebugLevel.all_____.build.debug_level=-DDEBUG_ESP_CORE -DDEBUG_ESP_SSL -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_TLS_MEM From 589b86b9bfee1f26abda1aaba29b57d585cab04c Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Sun, 10 Apr 2016 22:52:39 +0300 Subject: [PATCH 17/46] fix headers and source permissions --- tools/sdk/lwip/arch/cc.h | 0 tools/sdk/lwip/arch/perf.h | 0 tools/sdk/lwip/arch/sys_arch.h | 0 tools/sdk/lwip/lwip/api.h | 0 tools/sdk/lwip/lwip/api_msg.h | 0 tools/sdk/lwip/lwip/app/dhcpserver.h | 0 tools/sdk/lwip/lwip/app/espconn.h | 0 tools/sdk/lwip/lwip/app/espconn_tcp.h | 0 tools/sdk/lwip/lwip/app/espconn_udp.h | 0 tools/sdk/lwip/lwip/app/ping.h | 0 tools/sdk/lwip/lwip/arch.h | 0 tools/sdk/lwip/lwip/autoip.h | 0 tools/sdk/lwip/lwip/debug.h | 0 tools/sdk/lwip/lwip/def.h | 0 tools/sdk/lwip/lwip/dhcp.h | 0 tools/sdk/lwip/lwip/dns.h | 0 tools/sdk/lwip/lwip/err.h | 0 tools/sdk/lwip/lwip/icmp.h | 0 tools/sdk/lwip/lwip/igmp.h | 0 tools/sdk/lwip/lwip/inet.h | 0 tools/sdk/lwip/lwip/inet_chksum.h | 0 tools/sdk/lwip/lwip/init.h | 0 tools/sdk/lwip/lwip/ip.h | 0 tools/sdk/lwip/lwip/ip_addr.h | 0 tools/sdk/lwip/lwip/ip_frag.h | 0 tools/sdk/lwip/lwip/mdns.h | 0 tools/sdk/lwip/lwip/mem.h | 0 tools/sdk/lwip/lwip/memp.h | 0 tools/sdk/lwip/lwip/memp_std.h | 0 tools/sdk/lwip/lwip/netbuf.h | 0 tools/sdk/lwip/lwip/netdb.h | 0 tools/sdk/lwip/lwip/netif.h | 0 tools/sdk/lwip/lwip/netifapi.h | 0 tools/sdk/lwip/lwip/opt.h | 0 tools/sdk/lwip/lwip/pbuf.h | 0 tools/sdk/lwip/lwip/puck_def.h | 0 tools/sdk/lwip/lwip/raw.h | 0 tools/sdk/lwip/lwip/sio.h | 0 tools/sdk/lwip/lwip/snmp.h | 0 tools/sdk/lwip/lwip/snmp_asn1.h | 0 tools/sdk/lwip/lwip/snmp_msg.h | 0 tools/sdk/lwip/lwip/snmp_structs.h | 0 tools/sdk/lwip/lwip/sntp.h | 0 tools/sdk/lwip/lwip/sockets.h | 0 tools/sdk/lwip/lwip/stats.h | 0 tools/sdk/lwip/lwip/sys.h | 0 tools/sdk/lwip/lwip/tcp.h | 0 tools/sdk/lwip/lwip/tcp_impl.h | 0 tools/sdk/lwip/lwip/tcpip.h | 0 tools/sdk/lwip/lwip/timers.h | 0 tools/sdk/lwip/lwip/udp.h | 0 tools/sdk/lwip/lwipopts.h | 0 tools/sdk/lwip/netif/etharp.h | 0 tools/sdk/lwip/netif/if_llc.h | 0 tools/sdk/lwip/netif/ppp_oe.h | 0 tools/sdk/lwip/netif/wlan_lwip_if.h | 0 variants/lwip/lwip/api/api_lib.c | 0 variants/lwip/lwip/api/api_msg.c | 0 variants/lwip/lwip/api/err.c | 0 variants/lwip/lwip/api/netbuf.c | 0 variants/lwip/lwip/api/netdb.c | 0 variants/lwip/lwip/api/netifapi.c | 0 variants/lwip/lwip/api/sockets.c | 0 variants/lwip/lwip/api/tcpip.c | 0 variants/lwip/lwip/app/dhcpserver.c | 0 variants/lwip/lwip/app/espconn.c | 0 variants/lwip/lwip/app/espconn_mdns.c | 0 variants/lwip/lwip/app/espconn_tcp.c | 0 variants/lwip/lwip/app/espconn_udp.c | 0 variants/lwip/lwip/app/netio.c | 0 variants/lwip/lwip/app/ping.c | 0 variants/lwip/lwip/core/def.c | 0 variants/lwip/lwip/core/dhcp.c | 0 variants/lwip/lwip/core/dns.c | 0 variants/lwip/lwip/core/init.c | 0 variants/lwip/lwip/core/ipv4/autoip.c | 0 variants/lwip/lwip/core/ipv4/icmp.c | 0 variants/lwip/lwip/core/ipv4/igmp.c | 0 variants/lwip/lwip/core/ipv4/inet.c | 0 variants/lwip/lwip/core/ipv4/inet_chksum.c | 0 variants/lwip/lwip/core/ipv4/ip.c | 0 variants/lwip/lwip/core/ipv4/ip_addr.c | 0 variants/lwip/lwip/core/ipv4/ip_frag.c | 0 variants/lwip/lwip/core/mdns.c | 0 variants/lwip/lwip/core/mem.c | 0 variants/lwip/lwip/core/memp.c | 0 variants/lwip/lwip/core/netif.c | 0 variants/lwip/lwip/core/pbuf.c | 0 variants/lwip/lwip/core/raw.c | 0 variants/lwip/lwip/core/sntp.c | 0 variants/lwip/lwip/core/stats.c | 0 variants/lwip/lwip/core/sys.c | 0 variants/lwip/lwip/core/sys_arch.c | 0 variants/lwip/lwip/core/tcp.c | 0 variants/lwip/lwip/core/tcp_in.c | 0 variants/lwip/lwip/core/tcp_out.c | 0 variants/lwip/lwip/core/timers.c | 0 variants/lwip/lwip/core/udp.c | 0 variants/lwip/lwip/netif/etharp.c | 0 99 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 tools/sdk/lwip/arch/cc.h mode change 100755 => 100644 tools/sdk/lwip/arch/perf.h mode change 100755 => 100644 tools/sdk/lwip/arch/sys_arch.h mode change 100755 => 100644 tools/sdk/lwip/lwip/api.h mode change 100755 => 100644 tools/sdk/lwip/lwip/api_msg.h mode change 100755 => 100644 tools/sdk/lwip/lwip/app/dhcpserver.h mode change 100755 => 100644 tools/sdk/lwip/lwip/app/espconn.h mode change 100755 => 100644 tools/sdk/lwip/lwip/app/espconn_tcp.h mode change 100755 => 100644 tools/sdk/lwip/lwip/app/espconn_udp.h mode change 100755 => 100644 tools/sdk/lwip/lwip/app/ping.h mode change 100755 => 100644 tools/sdk/lwip/lwip/arch.h mode change 100755 => 100644 tools/sdk/lwip/lwip/autoip.h mode change 100755 => 100644 tools/sdk/lwip/lwip/debug.h mode change 100755 => 100644 tools/sdk/lwip/lwip/def.h mode change 100755 => 100644 tools/sdk/lwip/lwip/dhcp.h mode change 100755 => 100644 tools/sdk/lwip/lwip/dns.h mode change 100755 => 100644 tools/sdk/lwip/lwip/err.h mode change 100755 => 100644 tools/sdk/lwip/lwip/icmp.h mode change 100755 => 100644 tools/sdk/lwip/lwip/igmp.h mode change 100755 => 100644 tools/sdk/lwip/lwip/inet.h mode change 100755 => 100644 tools/sdk/lwip/lwip/inet_chksum.h mode change 100755 => 100644 tools/sdk/lwip/lwip/init.h mode change 100755 => 100644 tools/sdk/lwip/lwip/ip.h mode change 100755 => 100644 tools/sdk/lwip/lwip/ip_addr.h mode change 100755 => 100644 tools/sdk/lwip/lwip/ip_frag.h mode change 100755 => 100644 tools/sdk/lwip/lwip/mdns.h mode change 100755 => 100644 tools/sdk/lwip/lwip/mem.h mode change 100755 => 100644 tools/sdk/lwip/lwip/memp.h mode change 100755 => 100644 tools/sdk/lwip/lwip/memp_std.h mode change 100755 => 100644 tools/sdk/lwip/lwip/netbuf.h mode change 100755 => 100644 tools/sdk/lwip/lwip/netdb.h mode change 100755 => 100644 tools/sdk/lwip/lwip/netif.h mode change 100755 => 100644 tools/sdk/lwip/lwip/netifapi.h mode change 100755 => 100644 tools/sdk/lwip/lwip/opt.h mode change 100755 => 100644 tools/sdk/lwip/lwip/pbuf.h mode change 100755 => 100644 tools/sdk/lwip/lwip/puck_def.h mode change 100755 => 100644 tools/sdk/lwip/lwip/raw.h mode change 100755 => 100644 tools/sdk/lwip/lwip/sio.h mode change 100755 => 100644 tools/sdk/lwip/lwip/snmp.h mode change 100755 => 100644 tools/sdk/lwip/lwip/snmp_asn1.h mode change 100755 => 100644 tools/sdk/lwip/lwip/snmp_msg.h mode change 100755 => 100644 tools/sdk/lwip/lwip/snmp_structs.h mode change 100755 => 100644 tools/sdk/lwip/lwip/sntp.h mode change 100755 => 100644 tools/sdk/lwip/lwip/sockets.h mode change 100755 => 100644 tools/sdk/lwip/lwip/stats.h mode change 100755 => 100644 tools/sdk/lwip/lwip/sys.h mode change 100755 => 100644 tools/sdk/lwip/lwip/tcp.h mode change 100755 => 100644 tools/sdk/lwip/lwip/tcp_impl.h mode change 100755 => 100644 tools/sdk/lwip/lwip/tcpip.h mode change 100755 => 100644 tools/sdk/lwip/lwip/timers.h mode change 100755 => 100644 tools/sdk/lwip/lwip/udp.h mode change 100755 => 100644 tools/sdk/lwip/lwipopts.h mode change 100755 => 100644 tools/sdk/lwip/netif/etharp.h mode change 100755 => 100644 tools/sdk/lwip/netif/if_llc.h mode change 100755 => 100644 tools/sdk/lwip/netif/ppp_oe.h mode change 100755 => 100644 tools/sdk/lwip/netif/wlan_lwip_if.h mode change 100755 => 100644 variants/lwip/lwip/api/api_lib.c mode change 100755 => 100644 variants/lwip/lwip/api/api_msg.c mode change 100755 => 100644 variants/lwip/lwip/api/err.c mode change 100755 => 100644 variants/lwip/lwip/api/netbuf.c mode change 100755 => 100644 variants/lwip/lwip/api/netdb.c mode change 100755 => 100644 variants/lwip/lwip/api/netifapi.c mode change 100755 => 100644 variants/lwip/lwip/api/sockets.c mode change 100755 => 100644 variants/lwip/lwip/api/tcpip.c mode change 100755 => 100644 variants/lwip/lwip/app/dhcpserver.c mode change 100755 => 100644 variants/lwip/lwip/app/espconn.c mode change 100755 => 100644 variants/lwip/lwip/app/espconn_mdns.c mode change 100755 => 100644 variants/lwip/lwip/app/espconn_tcp.c mode change 100755 => 100644 variants/lwip/lwip/app/espconn_udp.c mode change 100755 => 100644 variants/lwip/lwip/app/netio.c mode change 100755 => 100644 variants/lwip/lwip/app/ping.c mode change 100755 => 100644 variants/lwip/lwip/core/def.c mode change 100755 => 100644 variants/lwip/lwip/core/dhcp.c mode change 100755 => 100644 variants/lwip/lwip/core/dns.c mode change 100755 => 100644 variants/lwip/lwip/core/init.c mode change 100755 => 100644 variants/lwip/lwip/core/ipv4/autoip.c mode change 100755 => 100644 variants/lwip/lwip/core/ipv4/icmp.c mode change 100755 => 100644 variants/lwip/lwip/core/ipv4/igmp.c mode change 100755 => 100644 variants/lwip/lwip/core/ipv4/inet.c mode change 100755 => 100644 variants/lwip/lwip/core/ipv4/inet_chksum.c mode change 100755 => 100644 variants/lwip/lwip/core/ipv4/ip.c mode change 100755 => 100644 variants/lwip/lwip/core/ipv4/ip_addr.c mode change 100755 => 100644 variants/lwip/lwip/core/ipv4/ip_frag.c mode change 100755 => 100644 variants/lwip/lwip/core/mdns.c mode change 100755 => 100644 variants/lwip/lwip/core/mem.c mode change 100755 => 100644 variants/lwip/lwip/core/memp.c mode change 100755 => 100644 variants/lwip/lwip/core/netif.c mode change 100755 => 100644 variants/lwip/lwip/core/pbuf.c mode change 100755 => 100644 variants/lwip/lwip/core/raw.c mode change 100755 => 100644 variants/lwip/lwip/core/sntp.c mode change 100755 => 100644 variants/lwip/lwip/core/stats.c mode change 100755 => 100644 variants/lwip/lwip/core/sys.c mode change 100755 => 100644 variants/lwip/lwip/core/sys_arch.c mode change 100755 => 100644 variants/lwip/lwip/core/tcp.c mode change 100755 => 100644 variants/lwip/lwip/core/tcp_in.c mode change 100755 => 100644 variants/lwip/lwip/core/tcp_out.c mode change 100755 => 100644 variants/lwip/lwip/core/timers.c mode change 100755 => 100644 variants/lwip/lwip/core/udp.c mode change 100755 => 100644 variants/lwip/lwip/netif/etharp.c diff --git a/tools/sdk/lwip/arch/cc.h b/tools/sdk/lwip/arch/cc.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/arch/perf.h b/tools/sdk/lwip/arch/perf.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/arch/sys_arch.h b/tools/sdk/lwip/arch/sys_arch.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/api.h b/tools/sdk/lwip/lwip/api.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/api_msg.h b/tools/sdk/lwip/lwip/api_msg.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/app/dhcpserver.h b/tools/sdk/lwip/lwip/app/dhcpserver.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/app/espconn.h b/tools/sdk/lwip/lwip/app/espconn.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/app/espconn_tcp.h b/tools/sdk/lwip/lwip/app/espconn_tcp.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/app/espconn_udp.h b/tools/sdk/lwip/lwip/app/espconn_udp.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/app/ping.h b/tools/sdk/lwip/lwip/app/ping.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/arch.h b/tools/sdk/lwip/lwip/arch.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/autoip.h b/tools/sdk/lwip/lwip/autoip.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/debug.h b/tools/sdk/lwip/lwip/debug.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/def.h b/tools/sdk/lwip/lwip/def.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/dhcp.h b/tools/sdk/lwip/lwip/dhcp.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/dns.h b/tools/sdk/lwip/lwip/dns.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/err.h b/tools/sdk/lwip/lwip/err.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/icmp.h b/tools/sdk/lwip/lwip/icmp.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/igmp.h b/tools/sdk/lwip/lwip/igmp.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/inet.h b/tools/sdk/lwip/lwip/inet.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/inet_chksum.h b/tools/sdk/lwip/lwip/inet_chksum.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/init.h b/tools/sdk/lwip/lwip/init.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/ip.h b/tools/sdk/lwip/lwip/ip.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/ip_addr.h b/tools/sdk/lwip/lwip/ip_addr.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/ip_frag.h b/tools/sdk/lwip/lwip/ip_frag.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/mdns.h b/tools/sdk/lwip/lwip/mdns.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/mem.h b/tools/sdk/lwip/lwip/mem.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/memp.h b/tools/sdk/lwip/lwip/memp.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/memp_std.h b/tools/sdk/lwip/lwip/memp_std.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/netbuf.h b/tools/sdk/lwip/lwip/netbuf.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/netdb.h b/tools/sdk/lwip/lwip/netdb.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/netif.h b/tools/sdk/lwip/lwip/netif.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/netifapi.h b/tools/sdk/lwip/lwip/netifapi.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/opt.h b/tools/sdk/lwip/lwip/opt.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/pbuf.h b/tools/sdk/lwip/lwip/pbuf.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/puck_def.h b/tools/sdk/lwip/lwip/puck_def.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/raw.h b/tools/sdk/lwip/lwip/raw.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/sio.h b/tools/sdk/lwip/lwip/sio.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/snmp.h b/tools/sdk/lwip/lwip/snmp.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/snmp_asn1.h b/tools/sdk/lwip/lwip/snmp_asn1.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/snmp_msg.h b/tools/sdk/lwip/lwip/snmp_msg.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/snmp_structs.h b/tools/sdk/lwip/lwip/snmp_structs.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/sntp.h b/tools/sdk/lwip/lwip/sntp.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/sockets.h b/tools/sdk/lwip/lwip/sockets.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/stats.h b/tools/sdk/lwip/lwip/stats.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/sys.h b/tools/sdk/lwip/lwip/sys.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/tcp.h b/tools/sdk/lwip/lwip/tcp.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/tcp_impl.h b/tools/sdk/lwip/lwip/tcp_impl.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/tcpip.h b/tools/sdk/lwip/lwip/tcpip.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/timers.h b/tools/sdk/lwip/lwip/timers.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwip/udp.h b/tools/sdk/lwip/lwip/udp.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/lwipopts.h b/tools/sdk/lwip/lwipopts.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/netif/etharp.h b/tools/sdk/lwip/netif/etharp.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/netif/if_llc.h b/tools/sdk/lwip/netif/if_llc.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/netif/ppp_oe.h b/tools/sdk/lwip/netif/ppp_oe.h old mode 100755 new mode 100644 diff --git a/tools/sdk/lwip/netif/wlan_lwip_if.h b/tools/sdk/lwip/netif/wlan_lwip_if.h old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/api/api_lib.c b/variants/lwip/lwip/api/api_lib.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/api/api_msg.c b/variants/lwip/lwip/api/api_msg.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/api/err.c b/variants/lwip/lwip/api/err.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/api/netbuf.c b/variants/lwip/lwip/api/netbuf.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/api/netdb.c b/variants/lwip/lwip/api/netdb.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/api/netifapi.c b/variants/lwip/lwip/api/netifapi.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/api/sockets.c b/variants/lwip/lwip/api/sockets.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/api/tcpip.c b/variants/lwip/lwip/api/tcpip.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/app/dhcpserver.c b/variants/lwip/lwip/app/dhcpserver.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/app/espconn.c b/variants/lwip/lwip/app/espconn.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/app/espconn_mdns.c b/variants/lwip/lwip/app/espconn_mdns.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/app/espconn_tcp.c b/variants/lwip/lwip/app/espconn_tcp.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/app/espconn_udp.c b/variants/lwip/lwip/app/espconn_udp.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/app/netio.c b/variants/lwip/lwip/app/netio.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/app/ping.c b/variants/lwip/lwip/app/ping.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/core/def.c b/variants/lwip/lwip/core/def.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/core/dhcp.c b/variants/lwip/lwip/core/dhcp.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/core/dns.c b/variants/lwip/lwip/core/dns.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/core/init.c b/variants/lwip/lwip/core/init.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/core/ipv4/autoip.c b/variants/lwip/lwip/core/ipv4/autoip.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/core/ipv4/icmp.c b/variants/lwip/lwip/core/ipv4/icmp.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/core/ipv4/igmp.c b/variants/lwip/lwip/core/ipv4/igmp.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/core/ipv4/inet.c b/variants/lwip/lwip/core/ipv4/inet.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/core/ipv4/inet_chksum.c b/variants/lwip/lwip/core/ipv4/inet_chksum.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/core/ipv4/ip.c b/variants/lwip/lwip/core/ipv4/ip.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/core/ipv4/ip_addr.c b/variants/lwip/lwip/core/ipv4/ip_addr.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/core/ipv4/ip_frag.c b/variants/lwip/lwip/core/ipv4/ip_frag.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/core/mdns.c b/variants/lwip/lwip/core/mdns.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/core/mem.c b/variants/lwip/lwip/core/mem.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/core/memp.c b/variants/lwip/lwip/core/memp.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/core/netif.c b/variants/lwip/lwip/core/netif.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/core/pbuf.c b/variants/lwip/lwip/core/pbuf.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/core/raw.c b/variants/lwip/lwip/core/raw.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/core/sntp.c b/variants/lwip/lwip/core/sntp.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/core/stats.c b/variants/lwip/lwip/core/stats.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/core/sys.c b/variants/lwip/lwip/core/sys.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/core/sys_arch.c b/variants/lwip/lwip/core/sys_arch.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/core/tcp.c b/variants/lwip/lwip/core/tcp.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/core/tcp_in.c b/variants/lwip/lwip/core/tcp_in.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/core/tcp_out.c b/variants/lwip/lwip/core/tcp_out.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/core/timers.c b/variants/lwip/lwip/core/timers.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/core/udp.c b/variants/lwip/lwip/core/udp.c old mode 100755 new mode 100644 diff --git a/variants/lwip/lwip/netif/etharp.c b/variants/lwip/lwip/netif/etharp.c old mode 100755 new mode 100644 From 0addae0084f6c686d5ab31bca4a29b4c7945cf60 Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Sun, 10 Apr 2016 23:57:26 +0300 Subject: [PATCH 18/46] move lwIP source to sdk and add a build hook instead of variant --- .gitignore | 3 + boards.txt | 9 ++- platform.txt | 2 +- tools/sdk/lwip/{ => include}/arch/cc.h | 0 tools/sdk/lwip/{ => include}/arch/perf.h | 0 tools/sdk/lwip/{ => include}/arch/sys_arch.h | 0 tools/sdk/lwip/{ => include}/lwip/api.h | 0 tools/sdk/lwip/{ => include}/lwip/api_msg.h | 0 .../lwip/{ => include}/lwip/app/dhcpserver.h | 0 .../sdk/lwip/{ => include}/lwip/app/espconn.h | 0 .../lwip/{ => include}/lwip/app/espconn_tcp.h | 0 .../lwip/{ => include}/lwip/app/espconn_udp.h | 0 tools/sdk/lwip/{ => include}/lwip/app/ping.h | 0 tools/sdk/lwip/{ => include}/lwip/arch.h | 0 tools/sdk/lwip/{ => include}/lwip/autoip.h | 0 tools/sdk/lwip/{ => include}/lwip/debug.h | 0 tools/sdk/lwip/{ => include}/lwip/def.h | 0 tools/sdk/lwip/{ => include}/lwip/dhcp.h | 0 tools/sdk/lwip/{ => include}/lwip/dns.h | 0 tools/sdk/lwip/{ => include}/lwip/err.h | 0 tools/sdk/lwip/{ => include}/lwip/icmp.h | 0 tools/sdk/lwip/{ => include}/lwip/igmp.h | 0 tools/sdk/lwip/{ => include}/lwip/inet.h | 0 .../sdk/lwip/{ => include}/lwip/inet_chksum.h | 0 tools/sdk/lwip/{ => include}/lwip/init.h | 0 tools/sdk/lwip/{ => include}/lwip/ip.h | 0 tools/sdk/lwip/{ => include}/lwip/ip_addr.h | 0 tools/sdk/lwip/{ => include}/lwip/ip_frag.h | 0 tools/sdk/lwip/{ => include}/lwip/mdns.h | 0 tools/sdk/lwip/{ => include}/lwip/mem.h | 0 tools/sdk/lwip/{ => include}/lwip/memp.h | 0 tools/sdk/lwip/{ => include}/lwip/memp_std.h | 0 tools/sdk/lwip/{ => include}/lwip/netbuf.h | 0 tools/sdk/lwip/{ => include}/lwip/netdb.h | 0 tools/sdk/lwip/{ => include}/lwip/netif.h | 0 tools/sdk/lwip/{ => include}/lwip/netifapi.h | 0 tools/sdk/lwip/{ => include}/lwip/opt.h | 0 tools/sdk/lwip/{ => include}/lwip/pbuf.h | 0 tools/sdk/lwip/{ => include}/lwip/puck_def.h | 0 tools/sdk/lwip/{ => include}/lwip/raw.h | 0 tools/sdk/lwip/{ => include}/lwip/sio.h | 0 tools/sdk/lwip/{ => include}/lwip/snmp.h | 0 tools/sdk/lwip/{ => include}/lwip/snmp_asn1.h | 0 tools/sdk/lwip/{ => include}/lwip/snmp_msg.h | 0 .../lwip/{ => include}/lwip/snmp_structs.h | 0 tools/sdk/lwip/{ => include}/lwip/sntp.h | 0 tools/sdk/lwip/{ => include}/lwip/sockets.h | 0 tools/sdk/lwip/{ => include}/lwip/stats.h | 0 tools/sdk/lwip/{ => include}/lwip/sys.h | 0 tools/sdk/lwip/{ => include}/lwip/tcp.h | 0 tools/sdk/lwip/{ => include}/lwip/tcp_impl.h | 0 tools/sdk/lwip/{ => include}/lwip/tcpip.h | 0 tools/sdk/lwip/{ => include}/lwip/timers.h | 0 tools/sdk/lwip/{ => include}/lwip/udp.h | 0 tools/sdk/lwip/{ => include}/lwipopts.h | 0 tools/sdk/lwip/{ => include}/netif/etharp.h | 0 tools/sdk/lwip/{ => include}/netif/if_llc.h | 0 tools/sdk/lwip/{ => include}/netif/ppp_oe.h | 0 .../lwip/{ => include}/netif/wlan_lwip_if.h | 0 .../lwip/lwip => tools/sdk/lwip/src}/Makefile | 28 ++++---- .../lwip => tools/sdk/lwip/src}/api/api_lib.c | 0 .../lwip => tools/sdk/lwip/src}/api/api_msg.c | 0 .../lwip => tools/sdk/lwip/src}/api/err.c | 0 .../lwip => tools/sdk/lwip/src}/api/netbuf.c | 0 .../lwip => tools/sdk/lwip/src}/api/netdb.c | 0 .../sdk/lwip/src}/api/netifapi.c | 0 .../lwip => tools/sdk/lwip/src}/api/sockets.c | 0 .../lwip => tools/sdk/lwip/src}/api/tcpip.c | 0 .../sdk/lwip/src}/app/dhcpserver.c | 0 .../lwip => tools/sdk/lwip/src}/app/espconn.c | 0 .../sdk/lwip/src}/app/espconn_mdns.c | 0 .../sdk/lwip/src}/app/espconn_tcp.c | 0 .../sdk/lwip/src}/app/espconn_udp.c | 0 .../lwip => tools/sdk/lwip/src}/app/netio.c | 0 .../lwip => tools/sdk/lwip/src}/app/ping.c | 0 .../lwip => tools/sdk/lwip/src}/core/def.c | 0 .../lwip => tools/sdk/lwip/src}/core/dhcp.c | 0 .../lwip => tools/sdk/lwip/src}/core/dns.c | 0 .../lwip => tools/sdk/lwip/src}/core/init.c | 0 .../sdk/lwip/src}/core/ipv4/autoip.c | 0 .../sdk/lwip/src}/core/ipv4/icmp.c | 0 .../sdk/lwip/src}/core/ipv4/igmp.c | 0 .../sdk/lwip/src}/core/ipv4/inet.c | 0 .../sdk/lwip/src}/core/ipv4/inet_chksum.c | 0 .../sdk/lwip/src}/core/ipv4/ip.c | 0 .../sdk/lwip/src}/core/ipv4/ip_addr.c | 0 .../sdk/lwip/src}/core/ipv4/ip_frag.c | 0 .../lwip => tools/sdk/lwip/src}/core/mdns.c | 0 .../lwip => tools/sdk/lwip/src}/core/mem.c | 0 .../lwip => tools/sdk/lwip/src}/core/memp.c | 0 .../lwip => tools/sdk/lwip/src}/core/netif.c | 0 .../lwip => tools/sdk/lwip/src}/core/pbuf.c | 0 .../lwip => tools/sdk/lwip/src}/core/raw.c | 0 .../lwip => tools/sdk/lwip/src}/core/sntp.c | 0 .../lwip => tools/sdk/lwip/src}/core/stats.c | 0 .../lwip => tools/sdk/lwip/src}/core/sys.c | 0 .../sdk/lwip/src}/core/sys_arch.c | 0 .../lwip => tools/sdk/lwip/src}/core/tcp.c | 0 .../lwip => tools/sdk/lwip/src}/core/tcp_in.c | 0 .../sdk/lwip/src}/core/tcp_out.c | 0 .../lwip => tools/sdk/lwip/src}/core/timers.c | 0 .../lwip => tools/sdk/lwip/src}/core/udp.c | 0 .../sdk/lwip/src}/netif/etharp.c | 0 variants/lwip/common.h | 67 ------------------- variants/lwip/pins_arduino.h | 37 ---------- 105 files changed, 20 insertions(+), 126 deletions(-) rename tools/sdk/lwip/{ => include}/arch/cc.h (100%) rename tools/sdk/lwip/{ => include}/arch/perf.h (100%) rename tools/sdk/lwip/{ => include}/arch/sys_arch.h (100%) rename tools/sdk/lwip/{ => include}/lwip/api.h (100%) rename tools/sdk/lwip/{ => include}/lwip/api_msg.h (100%) rename tools/sdk/lwip/{ => include}/lwip/app/dhcpserver.h (100%) rename tools/sdk/lwip/{ => include}/lwip/app/espconn.h (100%) rename tools/sdk/lwip/{ => include}/lwip/app/espconn_tcp.h (100%) rename tools/sdk/lwip/{ => include}/lwip/app/espconn_udp.h (100%) rename tools/sdk/lwip/{ => include}/lwip/app/ping.h (100%) rename tools/sdk/lwip/{ => include}/lwip/arch.h (100%) rename tools/sdk/lwip/{ => include}/lwip/autoip.h (100%) rename tools/sdk/lwip/{ => include}/lwip/debug.h (100%) rename tools/sdk/lwip/{ => include}/lwip/def.h (100%) rename tools/sdk/lwip/{ => include}/lwip/dhcp.h (100%) rename tools/sdk/lwip/{ => include}/lwip/dns.h (100%) rename tools/sdk/lwip/{ => include}/lwip/err.h (100%) rename tools/sdk/lwip/{ => include}/lwip/icmp.h (100%) rename tools/sdk/lwip/{ => include}/lwip/igmp.h (100%) rename tools/sdk/lwip/{ => include}/lwip/inet.h (100%) rename tools/sdk/lwip/{ => include}/lwip/inet_chksum.h (100%) rename tools/sdk/lwip/{ => include}/lwip/init.h (100%) rename tools/sdk/lwip/{ => include}/lwip/ip.h (100%) rename tools/sdk/lwip/{ => include}/lwip/ip_addr.h (100%) rename tools/sdk/lwip/{ => include}/lwip/ip_frag.h (100%) rename tools/sdk/lwip/{ => include}/lwip/mdns.h (100%) rename tools/sdk/lwip/{ => include}/lwip/mem.h (100%) rename tools/sdk/lwip/{ => include}/lwip/memp.h (100%) rename tools/sdk/lwip/{ => include}/lwip/memp_std.h (100%) rename tools/sdk/lwip/{ => include}/lwip/netbuf.h (100%) rename tools/sdk/lwip/{ => include}/lwip/netdb.h (100%) rename tools/sdk/lwip/{ => include}/lwip/netif.h (100%) rename tools/sdk/lwip/{ => include}/lwip/netifapi.h (100%) rename tools/sdk/lwip/{ => include}/lwip/opt.h (100%) rename tools/sdk/lwip/{ => include}/lwip/pbuf.h (100%) rename tools/sdk/lwip/{ => include}/lwip/puck_def.h (100%) rename tools/sdk/lwip/{ => include}/lwip/raw.h (100%) rename tools/sdk/lwip/{ => include}/lwip/sio.h (100%) rename tools/sdk/lwip/{ => include}/lwip/snmp.h (100%) rename tools/sdk/lwip/{ => include}/lwip/snmp_asn1.h (100%) rename tools/sdk/lwip/{ => include}/lwip/snmp_msg.h (100%) rename tools/sdk/lwip/{ => include}/lwip/snmp_structs.h (100%) rename tools/sdk/lwip/{ => include}/lwip/sntp.h (100%) rename tools/sdk/lwip/{ => include}/lwip/sockets.h (100%) rename tools/sdk/lwip/{ => include}/lwip/stats.h (100%) rename tools/sdk/lwip/{ => include}/lwip/sys.h (100%) rename tools/sdk/lwip/{ => include}/lwip/tcp.h (100%) rename tools/sdk/lwip/{ => include}/lwip/tcp_impl.h (100%) rename tools/sdk/lwip/{ => include}/lwip/tcpip.h (100%) rename tools/sdk/lwip/{ => include}/lwip/timers.h (100%) rename tools/sdk/lwip/{ => include}/lwip/udp.h (100%) rename tools/sdk/lwip/{ => include}/lwipopts.h (100%) rename tools/sdk/lwip/{ => include}/netif/etharp.h (100%) rename tools/sdk/lwip/{ => include}/netif/if_llc.h (100%) rename tools/sdk/lwip/{ => include}/netif/ppp_oe.h (100%) rename tools/sdk/lwip/{ => include}/netif/wlan_lwip_if.h (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/Makefile (50%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/api/api_lib.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/api/api_msg.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/api/err.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/api/netbuf.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/api/netdb.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/api/netifapi.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/api/sockets.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/api/tcpip.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/app/dhcpserver.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/app/espconn.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/app/espconn_mdns.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/app/espconn_tcp.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/app/espconn_udp.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/app/netio.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/app/ping.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/core/def.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/core/dhcp.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/core/dns.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/core/init.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/core/ipv4/autoip.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/core/ipv4/icmp.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/core/ipv4/igmp.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/core/ipv4/inet.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/core/ipv4/inet_chksum.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/core/ipv4/ip.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/core/ipv4/ip_addr.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/core/ipv4/ip_frag.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/core/mdns.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/core/mem.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/core/memp.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/core/netif.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/core/pbuf.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/core/raw.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/core/sntp.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/core/stats.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/core/sys.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/core/sys_arch.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/core/tcp.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/core/tcp_in.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/core/tcp_out.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/core/timers.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/core/udp.c (100%) rename {variants/lwip/lwip => tools/sdk/lwip/src}/netif/etharp.c (100%) delete mode 100644 variants/lwip/common.h delete mode 100644 variants/lwip/pins_arduino.h diff --git a/.gitignore b/.gitignore index 2623c405a..b1b0b534e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,6 @@ tools/esptool/ tools/mkspiffs/ package/versions/ exclude.txt +tools/sdk/lib/liblwip_src.a +tools/sdk/lwip/src/build +tools/sdk/lwip/src/liblwip_src.a diff --git a/boards.txt b/boards.txt index 46b0e7191..934275987 100644 --- a/boards.txt +++ b/boards.txt @@ -1368,7 +1368,7 @@ wifinfo.menu.UploadSpeed.921600.upload.speed=921600 ############################################################## -coredev.name=Generic ESP8266 Module +coredev.name=Core Development Module coredev.upload.tool=esptool coredev.upload.speed=115200 @@ -1391,18 +1391,17 @@ coredev.build.debug_level= coredev.build.lwip_lib=-llwip coredev.build.lwip_flags= + coredev.menu.LwIPVariant.Espressif=Espressif (xcc) coredev.menu.LwIPVariant.Espressif.build.lwip_lib=-llwip coredev.menu.LwIPVariant.Espressif.build.lwip_flags= -coredev.menu.LwIPVariant.Espressif.build.variant=generic coredev.menu.LwIPVariant.Prebuilt=Prebuilt Source (gcc) coredev.menu.LwIPVariant.Prebuilt.build.lwip_lib=-llwip_gcc coredev.menu.LwIPVariant.Prebuilt.build.lwip_flags=-DLWIP_OPEN_SRC -coredev.menu.LwIPVariant.Prebuilt.build.variant=generic coredev.menu.LwIPVariant.OpenSource=Open Source (gcc) -coredev.menu.LwIPVariant.OpenSource.build.lwip_lib= +coredev.menu.LwIPVariant.OpenSource.build.lwip_lib=-llwip_src coredev.menu.LwIPVariant.OpenSource.build.lwip_flags=-DLWIP_OPEN_SRC -coredev.menu.LwIPVariant.OpenSource.build.variant=lwip +coredev.menu.LwIPVariant.OpenSource.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" coredev.menu.CpuFrequency.80=80 MHz coredev.menu.CpuFrequency.80.build.f_cpu=80000000L diff --git a/platform.txt b/platform.txt index 17a7c08fc..83f70c9f0 100644 --- a/platform.txt +++ b/platform.txt @@ -19,7 +19,7 @@ compiler.warning_flags.all=-Wall -Wextra compiler.path={runtime.tools.xtensa-lx106-elf-gcc.path}/bin/ compiler.sdk.path={runtime.platform.path}/tools/sdk -compiler.cpreprocessor.flags=-D__ets__ -DICACHE_FLASH -U__STRICT_ANSI__ "-I{compiler.sdk.path}/include" "-I{compiler.sdk.path}/lwip" +compiler.cpreprocessor.flags=-D__ets__ -DICACHE_FLASH -U__STRICT_ANSI__ "-I{compiler.sdk.path}/include" "-I{compiler.sdk.path}/lwip/include" compiler.c.cmd=xtensa-lx106-elf-gcc compiler.c.flags=-c {compiler.warning_flags} -Os -g -Wpointer-arith -Wno-implicit-function-declaration -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals -falign-functions=4 -MMD -std=gnu99 -ffunction-sections -fdata-sections diff --git a/tools/sdk/lwip/arch/cc.h b/tools/sdk/lwip/include/arch/cc.h similarity index 100% rename from tools/sdk/lwip/arch/cc.h rename to tools/sdk/lwip/include/arch/cc.h diff --git a/tools/sdk/lwip/arch/perf.h b/tools/sdk/lwip/include/arch/perf.h similarity index 100% rename from tools/sdk/lwip/arch/perf.h rename to tools/sdk/lwip/include/arch/perf.h diff --git a/tools/sdk/lwip/arch/sys_arch.h b/tools/sdk/lwip/include/arch/sys_arch.h similarity index 100% rename from tools/sdk/lwip/arch/sys_arch.h rename to tools/sdk/lwip/include/arch/sys_arch.h diff --git a/tools/sdk/lwip/lwip/api.h b/tools/sdk/lwip/include/lwip/api.h similarity index 100% rename from tools/sdk/lwip/lwip/api.h rename to tools/sdk/lwip/include/lwip/api.h diff --git a/tools/sdk/lwip/lwip/api_msg.h b/tools/sdk/lwip/include/lwip/api_msg.h similarity index 100% rename from tools/sdk/lwip/lwip/api_msg.h rename to tools/sdk/lwip/include/lwip/api_msg.h diff --git a/tools/sdk/lwip/lwip/app/dhcpserver.h b/tools/sdk/lwip/include/lwip/app/dhcpserver.h similarity index 100% rename from tools/sdk/lwip/lwip/app/dhcpserver.h rename to tools/sdk/lwip/include/lwip/app/dhcpserver.h diff --git a/tools/sdk/lwip/lwip/app/espconn.h b/tools/sdk/lwip/include/lwip/app/espconn.h similarity index 100% rename from tools/sdk/lwip/lwip/app/espconn.h rename to tools/sdk/lwip/include/lwip/app/espconn.h diff --git a/tools/sdk/lwip/lwip/app/espconn_tcp.h b/tools/sdk/lwip/include/lwip/app/espconn_tcp.h similarity index 100% rename from tools/sdk/lwip/lwip/app/espconn_tcp.h rename to tools/sdk/lwip/include/lwip/app/espconn_tcp.h diff --git a/tools/sdk/lwip/lwip/app/espconn_udp.h b/tools/sdk/lwip/include/lwip/app/espconn_udp.h similarity index 100% rename from tools/sdk/lwip/lwip/app/espconn_udp.h rename to tools/sdk/lwip/include/lwip/app/espconn_udp.h diff --git a/tools/sdk/lwip/lwip/app/ping.h b/tools/sdk/lwip/include/lwip/app/ping.h similarity index 100% rename from tools/sdk/lwip/lwip/app/ping.h rename to tools/sdk/lwip/include/lwip/app/ping.h diff --git a/tools/sdk/lwip/lwip/arch.h b/tools/sdk/lwip/include/lwip/arch.h similarity index 100% rename from tools/sdk/lwip/lwip/arch.h rename to tools/sdk/lwip/include/lwip/arch.h diff --git a/tools/sdk/lwip/lwip/autoip.h b/tools/sdk/lwip/include/lwip/autoip.h similarity index 100% rename from tools/sdk/lwip/lwip/autoip.h rename to tools/sdk/lwip/include/lwip/autoip.h diff --git a/tools/sdk/lwip/lwip/debug.h b/tools/sdk/lwip/include/lwip/debug.h similarity index 100% rename from tools/sdk/lwip/lwip/debug.h rename to tools/sdk/lwip/include/lwip/debug.h diff --git a/tools/sdk/lwip/lwip/def.h b/tools/sdk/lwip/include/lwip/def.h similarity index 100% rename from tools/sdk/lwip/lwip/def.h rename to tools/sdk/lwip/include/lwip/def.h diff --git a/tools/sdk/lwip/lwip/dhcp.h b/tools/sdk/lwip/include/lwip/dhcp.h similarity index 100% rename from tools/sdk/lwip/lwip/dhcp.h rename to tools/sdk/lwip/include/lwip/dhcp.h diff --git a/tools/sdk/lwip/lwip/dns.h b/tools/sdk/lwip/include/lwip/dns.h similarity index 100% rename from tools/sdk/lwip/lwip/dns.h rename to tools/sdk/lwip/include/lwip/dns.h diff --git a/tools/sdk/lwip/lwip/err.h b/tools/sdk/lwip/include/lwip/err.h similarity index 100% rename from tools/sdk/lwip/lwip/err.h rename to tools/sdk/lwip/include/lwip/err.h diff --git a/tools/sdk/lwip/lwip/icmp.h b/tools/sdk/lwip/include/lwip/icmp.h similarity index 100% rename from tools/sdk/lwip/lwip/icmp.h rename to tools/sdk/lwip/include/lwip/icmp.h diff --git a/tools/sdk/lwip/lwip/igmp.h b/tools/sdk/lwip/include/lwip/igmp.h similarity index 100% rename from tools/sdk/lwip/lwip/igmp.h rename to tools/sdk/lwip/include/lwip/igmp.h diff --git a/tools/sdk/lwip/lwip/inet.h b/tools/sdk/lwip/include/lwip/inet.h similarity index 100% rename from tools/sdk/lwip/lwip/inet.h rename to tools/sdk/lwip/include/lwip/inet.h diff --git a/tools/sdk/lwip/lwip/inet_chksum.h b/tools/sdk/lwip/include/lwip/inet_chksum.h similarity index 100% rename from tools/sdk/lwip/lwip/inet_chksum.h rename to tools/sdk/lwip/include/lwip/inet_chksum.h diff --git a/tools/sdk/lwip/lwip/init.h b/tools/sdk/lwip/include/lwip/init.h similarity index 100% rename from tools/sdk/lwip/lwip/init.h rename to tools/sdk/lwip/include/lwip/init.h diff --git a/tools/sdk/lwip/lwip/ip.h b/tools/sdk/lwip/include/lwip/ip.h similarity index 100% rename from tools/sdk/lwip/lwip/ip.h rename to tools/sdk/lwip/include/lwip/ip.h diff --git a/tools/sdk/lwip/lwip/ip_addr.h b/tools/sdk/lwip/include/lwip/ip_addr.h similarity index 100% rename from tools/sdk/lwip/lwip/ip_addr.h rename to tools/sdk/lwip/include/lwip/ip_addr.h diff --git a/tools/sdk/lwip/lwip/ip_frag.h b/tools/sdk/lwip/include/lwip/ip_frag.h similarity index 100% rename from tools/sdk/lwip/lwip/ip_frag.h rename to tools/sdk/lwip/include/lwip/ip_frag.h diff --git a/tools/sdk/lwip/lwip/mdns.h b/tools/sdk/lwip/include/lwip/mdns.h similarity index 100% rename from tools/sdk/lwip/lwip/mdns.h rename to tools/sdk/lwip/include/lwip/mdns.h diff --git a/tools/sdk/lwip/lwip/mem.h b/tools/sdk/lwip/include/lwip/mem.h similarity index 100% rename from tools/sdk/lwip/lwip/mem.h rename to tools/sdk/lwip/include/lwip/mem.h diff --git a/tools/sdk/lwip/lwip/memp.h b/tools/sdk/lwip/include/lwip/memp.h similarity index 100% rename from tools/sdk/lwip/lwip/memp.h rename to tools/sdk/lwip/include/lwip/memp.h diff --git a/tools/sdk/lwip/lwip/memp_std.h b/tools/sdk/lwip/include/lwip/memp_std.h similarity index 100% rename from tools/sdk/lwip/lwip/memp_std.h rename to tools/sdk/lwip/include/lwip/memp_std.h diff --git a/tools/sdk/lwip/lwip/netbuf.h b/tools/sdk/lwip/include/lwip/netbuf.h similarity index 100% rename from tools/sdk/lwip/lwip/netbuf.h rename to tools/sdk/lwip/include/lwip/netbuf.h diff --git a/tools/sdk/lwip/lwip/netdb.h b/tools/sdk/lwip/include/lwip/netdb.h similarity index 100% rename from tools/sdk/lwip/lwip/netdb.h rename to tools/sdk/lwip/include/lwip/netdb.h diff --git a/tools/sdk/lwip/lwip/netif.h b/tools/sdk/lwip/include/lwip/netif.h similarity index 100% rename from tools/sdk/lwip/lwip/netif.h rename to tools/sdk/lwip/include/lwip/netif.h diff --git a/tools/sdk/lwip/lwip/netifapi.h b/tools/sdk/lwip/include/lwip/netifapi.h similarity index 100% rename from tools/sdk/lwip/lwip/netifapi.h rename to tools/sdk/lwip/include/lwip/netifapi.h diff --git a/tools/sdk/lwip/lwip/opt.h b/tools/sdk/lwip/include/lwip/opt.h similarity index 100% rename from tools/sdk/lwip/lwip/opt.h rename to tools/sdk/lwip/include/lwip/opt.h diff --git a/tools/sdk/lwip/lwip/pbuf.h b/tools/sdk/lwip/include/lwip/pbuf.h similarity index 100% rename from tools/sdk/lwip/lwip/pbuf.h rename to tools/sdk/lwip/include/lwip/pbuf.h diff --git a/tools/sdk/lwip/lwip/puck_def.h b/tools/sdk/lwip/include/lwip/puck_def.h similarity index 100% rename from tools/sdk/lwip/lwip/puck_def.h rename to tools/sdk/lwip/include/lwip/puck_def.h diff --git a/tools/sdk/lwip/lwip/raw.h b/tools/sdk/lwip/include/lwip/raw.h similarity index 100% rename from tools/sdk/lwip/lwip/raw.h rename to tools/sdk/lwip/include/lwip/raw.h diff --git a/tools/sdk/lwip/lwip/sio.h b/tools/sdk/lwip/include/lwip/sio.h similarity index 100% rename from tools/sdk/lwip/lwip/sio.h rename to tools/sdk/lwip/include/lwip/sio.h diff --git a/tools/sdk/lwip/lwip/snmp.h b/tools/sdk/lwip/include/lwip/snmp.h similarity index 100% rename from tools/sdk/lwip/lwip/snmp.h rename to tools/sdk/lwip/include/lwip/snmp.h diff --git a/tools/sdk/lwip/lwip/snmp_asn1.h b/tools/sdk/lwip/include/lwip/snmp_asn1.h similarity index 100% rename from tools/sdk/lwip/lwip/snmp_asn1.h rename to tools/sdk/lwip/include/lwip/snmp_asn1.h diff --git a/tools/sdk/lwip/lwip/snmp_msg.h b/tools/sdk/lwip/include/lwip/snmp_msg.h similarity index 100% rename from tools/sdk/lwip/lwip/snmp_msg.h rename to tools/sdk/lwip/include/lwip/snmp_msg.h diff --git a/tools/sdk/lwip/lwip/snmp_structs.h b/tools/sdk/lwip/include/lwip/snmp_structs.h similarity index 100% rename from tools/sdk/lwip/lwip/snmp_structs.h rename to tools/sdk/lwip/include/lwip/snmp_structs.h diff --git a/tools/sdk/lwip/lwip/sntp.h b/tools/sdk/lwip/include/lwip/sntp.h similarity index 100% rename from tools/sdk/lwip/lwip/sntp.h rename to tools/sdk/lwip/include/lwip/sntp.h diff --git a/tools/sdk/lwip/lwip/sockets.h b/tools/sdk/lwip/include/lwip/sockets.h similarity index 100% rename from tools/sdk/lwip/lwip/sockets.h rename to tools/sdk/lwip/include/lwip/sockets.h diff --git a/tools/sdk/lwip/lwip/stats.h b/tools/sdk/lwip/include/lwip/stats.h similarity index 100% rename from tools/sdk/lwip/lwip/stats.h rename to tools/sdk/lwip/include/lwip/stats.h diff --git a/tools/sdk/lwip/lwip/sys.h b/tools/sdk/lwip/include/lwip/sys.h similarity index 100% rename from tools/sdk/lwip/lwip/sys.h rename to tools/sdk/lwip/include/lwip/sys.h diff --git a/tools/sdk/lwip/lwip/tcp.h b/tools/sdk/lwip/include/lwip/tcp.h similarity index 100% rename from tools/sdk/lwip/lwip/tcp.h rename to tools/sdk/lwip/include/lwip/tcp.h diff --git a/tools/sdk/lwip/lwip/tcp_impl.h b/tools/sdk/lwip/include/lwip/tcp_impl.h similarity index 100% rename from tools/sdk/lwip/lwip/tcp_impl.h rename to tools/sdk/lwip/include/lwip/tcp_impl.h diff --git a/tools/sdk/lwip/lwip/tcpip.h b/tools/sdk/lwip/include/lwip/tcpip.h similarity index 100% rename from tools/sdk/lwip/lwip/tcpip.h rename to tools/sdk/lwip/include/lwip/tcpip.h diff --git a/tools/sdk/lwip/lwip/timers.h b/tools/sdk/lwip/include/lwip/timers.h similarity index 100% rename from tools/sdk/lwip/lwip/timers.h rename to tools/sdk/lwip/include/lwip/timers.h diff --git a/tools/sdk/lwip/lwip/udp.h b/tools/sdk/lwip/include/lwip/udp.h similarity index 100% rename from tools/sdk/lwip/lwip/udp.h rename to tools/sdk/lwip/include/lwip/udp.h diff --git a/tools/sdk/lwip/lwipopts.h b/tools/sdk/lwip/include/lwipopts.h similarity index 100% rename from tools/sdk/lwip/lwipopts.h rename to tools/sdk/lwip/include/lwipopts.h diff --git a/tools/sdk/lwip/netif/etharp.h b/tools/sdk/lwip/include/netif/etharp.h similarity index 100% rename from tools/sdk/lwip/netif/etharp.h rename to tools/sdk/lwip/include/netif/etharp.h diff --git a/tools/sdk/lwip/netif/if_llc.h b/tools/sdk/lwip/include/netif/if_llc.h similarity index 100% rename from tools/sdk/lwip/netif/if_llc.h rename to tools/sdk/lwip/include/netif/if_llc.h diff --git a/tools/sdk/lwip/netif/ppp_oe.h b/tools/sdk/lwip/include/netif/ppp_oe.h similarity index 100% rename from tools/sdk/lwip/netif/ppp_oe.h rename to tools/sdk/lwip/include/netif/ppp_oe.h diff --git a/tools/sdk/lwip/netif/wlan_lwip_if.h b/tools/sdk/lwip/include/netif/wlan_lwip_if.h similarity index 100% rename from tools/sdk/lwip/netif/wlan_lwip_if.h rename to tools/sdk/lwip/include/netif/wlan_lwip_if.h diff --git a/variants/lwip/lwip/Makefile b/tools/sdk/lwip/src/Makefile similarity index 50% rename from variants/lwip/lwip/Makefile rename to tools/sdk/lwip/src/Makefile index d894b7b02..e03fda331 100644 --- a/variants/lwip/lwip/Makefile +++ b/tools/sdk/lwip/src/Makefile @@ -1,36 +1,32 @@ -TOOLS_PATH = $(abspath ../../../tools) -BUILD_PATH = $(abspath build) -LWIP_INCLUDE = -Ibuild -I$(TOOLS_PATH)/sdk/include -I$(TOOLS_PATH)/sdk/lwip -LWIP_SRCS = $(patsubst %.c,$(BUILD_PATH)/%.o,$(wildcard */*.c)) $(patsubst %.c,$(BUILD_PATH)/%.o,$(wildcard */*/*.c)) -LWIP_LIB = $(abspath liblwip_gcc.a) +TOOLS_PATH ?= ../../../xtensa-lx106-elf/bin/xtensa-lx106-elf- +LWIP_LIB ?= liblwip_src.a +SDK_PATH ?= $(abspath ../../) +BUILD_PATH = build +LWIP_SRCS = $(patsubst %.c,$(BUILD_PATH)/%.o,$(wildcard */*.c)) $(patsubst %.c,$(BUILD_PATH)/%.o,$(wildcard */*/*.c)) + +LWIP_INCLUDE = -Ibuild -I$(SDK_PATH)/include -I$(SDK_PATH)/lwip/include BUILD_FLAGS = -c -Os -g -Wpointer-arith -Wno-implicit-function-declaration -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals -falign-functions=4 -MMD -std=gnu99 -ffunction-sections -fdata-sections BUILD_DEFINES = -D__ets__ -DICACHE_FLASH -U__STRICT_ANSI__ -DLWIP_OPEN_SRC -CC=$(TOOLS_PATH)/xtensa-lx106-elf/bin/xtensa-lx106-elf-gcc -AR=$(TOOLS_PATH)/xtensa-lx106-elf/bin/xtensa-lx106-elf-ar +CC=$(TOOLS_PATH)gcc +AR=$(TOOLS_PATH)ar $(BUILD_PATH)/%.h: - @echo "[CR]" $(notdir $@) @mkdir -p $(dir $@) @touch $@ $(BUILD_PATH)/%.o: %.c - @echo "[CC]" $(notdir $@) @mkdir -p $(dir $@) - @$(CC) $(BUILD_FLAGS) $(BUILD_DEFINES) $(LWIP_INCLUDE) $< -o $@ + $(CC) $(BUILD_FLAGS) $(BUILD_DEFINES) $(LWIP_INCLUDE) $< -o $@ $(LWIP_LIB): $(BUILD_PATH)/user_config.h $(LWIP_SRCS) - @echo "[AR]" $(notdir $(LWIP_LIB)) - @$(AR) cru $(LWIP_LIB) $(LWIP_SRCS) + $(AR) cru $(LWIP_LIB) $(LWIP_SRCS) all: $(LWIP_LIB) install: all - @echo Installing $(notdir $(LWIP_LIB)) to $(TOOLS_PATH)/sdk/lib - @cp -f $(LWIP_LIB) $(TOOLS_PATH)/sdk/lib/$(notdir $(LWIP_LIB)) - -deploy: install clean + cp -f $(LWIP_LIB) $(SDK_PATH)/lib/$(LWIP_LIB) clean: @rm -rf $(BUILD_PATH) $(LWIP_LIB) diff --git a/variants/lwip/lwip/api/api_lib.c b/tools/sdk/lwip/src/api/api_lib.c similarity index 100% rename from variants/lwip/lwip/api/api_lib.c rename to tools/sdk/lwip/src/api/api_lib.c diff --git a/variants/lwip/lwip/api/api_msg.c b/tools/sdk/lwip/src/api/api_msg.c similarity index 100% rename from variants/lwip/lwip/api/api_msg.c rename to tools/sdk/lwip/src/api/api_msg.c diff --git a/variants/lwip/lwip/api/err.c b/tools/sdk/lwip/src/api/err.c similarity index 100% rename from variants/lwip/lwip/api/err.c rename to tools/sdk/lwip/src/api/err.c diff --git a/variants/lwip/lwip/api/netbuf.c b/tools/sdk/lwip/src/api/netbuf.c similarity index 100% rename from variants/lwip/lwip/api/netbuf.c rename to tools/sdk/lwip/src/api/netbuf.c diff --git a/variants/lwip/lwip/api/netdb.c b/tools/sdk/lwip/src/api/netdb.c similarity index 100% rename from variants/lwip/lwip/api/netdb.c rename to tools/sdk/lwip/src/api/netdb.c diff --git a/variants/lwip/lwip/api/netifapi.c b/tools/sdk/lwip/src/api/netifapi.c similarity index 100% rename from variants/lwip/lwip/api/netifapi.c rename to tools/sdk/lwip/src/api/netifapi.c diff --git a/variants/lwip/lwip/api/sockets.c b/tools/sdk/lwip/src/api/sockets.c similarity index 100% rename from variants/lwip/lwip/api/sockets.c rename to tools/sdk/lwip/src/api/sockets.c diff --git a/variants/lwip/lwip/api/tcpip.c b/tools/sdk/lwip/src/api/tcpip.c similarity index 100% rename from variants/lwip/lwip/api/tcpip.c rename to tools/sdk/lwip/src/api/tcpip.c diff --git a/variants/lwip/lwip/app/dhcpserver.c b/tools/sdk/lwip/src/app/dhcpserver.c similarity index 100% rename from variants/lwip/lwip/app/dhcpserver.c rename to tools/sdk/lwip/src/app/dhcpserver.c diff --git a/variants/lwip/lwip/app/espconn.c b/tools/sdk/lwip/src/app/espconn.c similarity index 100% rename from variants/lwip/lwip/app/espconn.c rename to tools/sdk/lwip/src/app/espconn.c diff --git a/variants/lwip/lwip/app/espconn_mdns.c b/tools/sdk/lwip/src/app/espconn_mdns.c similarity index 100% rename from variants/lwip/lwip/app/espconn_mdns.c rename to tools/sdk/lwip/src/app/espconn_mdns.c diff --git a/variants/lwip/lwip/app/espconn_tcp.c b/tools/sdk/lwip/src/app/espconn_tcp.c similarity index 100% rename from variants/lwip/lwip/app/espconn_tcp.c rename to tools/sdk/lwip/src/app/espconn_tcp.c diff --git a/variants/lwip/lwip/app/espconn_udp.c b/tools/sdk/lwip/src/app/espconn_udp.c similarity index 100% rename from variants/lwip/lwip/app/espconn_udp.c rename to tools/sdk/lwip/src/app/espconn_udp.c diff --git a/variants/lwip/lwip/app/netio.c b/tools/sdk/lwip/src/app/netio.c similarity index 100% rename from variants/lwip/lwip/app/netio.c rename to tools/sdk/lwip/src/app/netio.c diff --git a/variants/lwip/lwip/app/ping.c b/tools/sdk/lwip/src/app/ping.c similarity index 100% rename from variants/lwip/lwip/app/ping.c rename to tools/sdk/lwip/src/app/ping.c diff --git a/variants/lwip/lwip/core/def.c b/tools/sdk/lwip/src/core/def.c similarity index 100% rename from variants/lwip/lwip/core/def.c rename to tools/sdk/lwip/src/core/def.c diff --git a/variants/lwip/lwip/core/dhcp.c b/tools/sdk/lwip/src/core/dhcp.c similarity index 100% rename from variants/lwip/lwip/core/dhcp.c rename to tools/sdk/lwip/src/core/dhcp.c diff --git a/variants/lwip/lwip/core/dns.c b/tools/sdk/lwip/src/core/dns.c similarity index 100% rename from variants/lwip/lwip/core/dns.c rename to tools/sdk/lwip/src/core/dns.c diff --git a/variants/lwip/lwip/core/init.c b/tools/sdk/lwip/src/core/init.c similarity index 100% rename from variants/lwip/lwip/core/init.c rename to tools/sdk/lwip/src/core/init.c diff --git a/variants/lwip/lwip/core/ipv4/autoip.c b/tools/sdk/lwip/src/core/ipv4/autoip.c similarity index 100% rename from variants/lwip/lwip/core/ipv4/autoip.c rename to tools/sdk/lwip/src/core/ipv4/autoip.c diff --git a/variants/lwip/lwip/core/ipv4/icmp.c b/tools/sdk/lwip/src/core/ipv4/icmp.c similarity index 100% rename from variants/lwip/lwip/core/ipv4/icmp.c rename to tools/sdk/lwip/src/core/ipv4/icmp.c diff --git a/variants/lwip/lwip/core/ipv4/igmp.c b/tools/sdk/lwip/src/core/ipv4/igmp.c similarity index 100% rename from variants/lwip/lwip/core/ipv4/igmp.c rename to tools/sdk/lwip/src/core/ipv4/igmp.c diff --git a/variants/lwip/lwip/core/ipv4/inet.c b/tools/sdk/lwip/src/core/ipv4/inet.c similarity index 100% rename from variants/lwip/lwip/core/ipv4/inet.c rename to tools/sdk/lwip/src/core/ipv4/inet.c diff --git a/variants/lwip/lwip/core/ipv4/inet_chksum.c b/tools/sdk/lwip/src/core/ipv4/inet_chksum.c similarity index 100% rename from variants/lwip/lwip/core/ipv4/inet_chksum.c rename to tools/sdk/lwip/src/core/ipv4/inet_chksum.c diff --git a/variants/lwip/lwip/core/ipv4/ip.c b/tools/sdk/lwip/src/core/ipv4/ip.c similarity index 100% rename from variants/lwip/lwip/core/ipv4/ip.c rename to tools/sdk/lwip/src/core/ipv4/ip.c diff --git a/variants/lwip/lwip/core/ipv4/ip_addr.c b/tools/sdk/lwip/src/core/ipv4/ip_addr.c similarity index 100% rename from variants/lwip/lwip/core/ipv4/ip_addr.c rename to tools/sdk/lwip/src/core/ipv4/ip_addr.c diff --git a/variants/lwip/lwip/core/ipv4/ip_frag.c b/tools/sdk/lwip/src/core/ipv4/ip_frag.c similarity index 100% rename from variants/lwip/lwip/core/ipv4/ip_frag.c rename to tools/sdk/lwip/src/core/ipv4/ip_frag.c diff --git a/variants/lwip/lwip/core/mdns.c b/tools/sdk/lwip/src/core/mdns.c similarity index 100% rename from variants/lwip/lwip/core/mdns.c rename to tools/sdk/lwip/src/core/mdns.c diff --git a/variants/lwip/lwip/core/mem.c b/tools/sdk/lwip/src/core/mem.c similarity index 100% rename from variants/lwip/lwip/core/mem.c rename to tools/sdk/lwip/src/core/mem.c diff --git a/variants/lwip/lwip/core/memp.c b/tools/sdk/lwip/src/core/memp.c similarity index 100% rename from variants/lwip/lwip/core/memp.c rename to tools/sdk/lwip/src/core/memp.c diff --git a/variants/lwip/lwip/core/netif.c b/tools/sdk/lwip/src/core/netif.c similarity index 100% rename from variants/lwip/lwip/core/netif.c rename to tools/sdk/lwip/src/core/netif.c diff --git a/variants/lwip/lwip/core/pbuf.c b/tools/sdk/lwip/src/core/pbuf.c similarity index 100% rename from variants/lwip/lwip/core/pbuf.c rename to tools/sdk/lwip/src/core/pbuf.c diff --git a/variants/lwip/lwip/core/raw.c b/tools/sdk/lwip/src/core/raw.c similarity index 100% rename from variants/lwip/lwip/core/raw.c rename to tools/sdk/lwip/src/core/raw.c diff --git a/variants/lwip/lwip/core/sntp.c b/tools/sdk/lwip/src/core/sntp.c similarity index 100% rename from variants/lwip/lwip/core/sntp.c rename to tools/sdk/lwip/src/core/sntp.c diff --git a/variants/lwip/lwip/core/stats.c b/tools/sdk/lwip/src/core/stats.c similarity index 100% rename from variants/lwip/lwip/core/stats.c rename to tools/sdk/lwip/src/core/stats.c diff --git a/variants/lwip/lwip/core/sys.c b/tools/sdk/lwip/src/core/sys.c similarity index 100% rename from variants/lwip/lwip/core/sys.c rename to tools/sdk/lwip/src/core/sys.c diff --git a/variants/lwip/lwip/core/sys_arch.c b/tools/sdk/lwip/src/core/sys_arch.c similarity index 100% rename from variants/lwip/lwip/core/sys_arch.c rename to tools/sdk/lwip/src/core/sys_arch.c diff --git a/variants/lwip/lwip/core/tcp.c b/tools/sdk/lwip/src/core/tcp.c similarity index 100% rename from variants/lwip/lwip/core/tcp.c rename to tools/sdk/lwip/src/core/tcp.c diff --git a/variants/lwip/lwip/core/tcp_in.c b/tools/sdk/lwip/src/core/tcp_in.c similarity index 100% rename from variants/lwip/lwip/core/tcp_in.c rename to tools/sdk/lwip/src/core/tcp_in.c diff --git a/variants/lwip/lwip/core/tcp_out.c b/tools/sdk/lwip/src/core/tcp_out.c similarity index 100% rename from variants/lwip/lwip/core/tcp_out.c rename to tools/sdk/lwip/src/core/tcp_out.c diff --git a/variants/lwip/lwip/core/timers.c b/tools/sdk/lwip/src/core/timers.c similarity index 100% rename from variants/lwip/lwip/core/timers.c rename to tools/sdk/lwip/src/core/timers.c diff --git a/variants/lwip/lwip/core/udp.c b/tools/sdk/lwip/src/core/udp.c similarity index 100% rename from variants/lwip/lwip/core/udp.c rename to tools/sdk/lwip/src/core/udp.c diff --git a/variants/lwip/lwip/netif/etharp.c b/tools/sdk/lwip/src/netif/etharp.c similarity index 100% rename from variants/lwip/lwip/netif/etharp.c rename to tools/sdk/lwip/src/netif/etharp.c diff --git a/variants/lwip/common.h b/variants/lwip/common.h deleted file mode 100644 index c0029a70a..000000000 --- a/variants/lwip/common.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - common.h - Commoon pin definition functions for ESP8266 boards - Part of Arduino - http://www.arduino.cc/ - - Copyright (c) 2007 David A. Mellis - Modified for ESP8266 platform by Ivan Grokhotkov, 2014-2016. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General - Public License along with this library; if not, write to the - Free Software Foundation, Inc., 59 Temple Place, Suite 330, - Boston, MA 02111-1307 USA - - $Id: wiring.h 249 2007-02-03 16:52:51Z mellis $ -*/ - -#ifndef GENERIC_COMMON_H -#define GENERIC_COMMON_H - -#define EXTERNAL_NUM_INTERRUPTS 16 -#define NUM_DIGITAL_PINS 17 -#define NUM_ANALOG_INPUTS 1 - -// TODO: this should be <= 9 if flash is in DIO mode -#define isFlashInterfacePin(p) ((p) >= 6 && (p) <= 11) - -#define analogInputToDigitalPin(p) ((p > 0) ? NOT_A_PIN : 0) -#define digitalPinToInterrupt(p) (((p) < EXTERNAL_NUM_INTERRUPTS)? (p) : NOT_AN_INTERRUPT) -#define digitalPinHasPWM(p) (((p) < NUM_DIGITAL_PINS && !isFlashInterfacePin(p))? 1 : 0) - -static const uint8_t SS = 15; -static const uint8_t MOSI = 13; -static const uint8_t MISO = 12; -static const uint8_t SCK = 14; - -static const uint8_t A0 = 17; - -// These serial port names are intended to allow libraries and architecture-neutral -// sketches to automatically default to the correct port name for a particular type -// of use. For example, a GPS module would normally connect to SERIAL_PORT_HARDWARE_OPEN, -// the first hardware serial port whose RX/TX pins are not dedicated to another use. -// -// SERIAL_PORT_MONITOR Port which normally prints to the Arduino Serial Monitor -// -// SERIAL_PORT_USBVIRTUAL Port which is USB virtual serial -// -// SERIAL_PORT_LINUXBRIDGE Port which connects to a Linux system via Bridge library -// -// SERIAL_PORT_HARDWARE Hardware serial port, physical RX & TX pins. -// -// SERIAL_PORT_HARDWARE_OPEN Hardware serial ports which are open for use. Their RX & TX -// pins are NOT connected to anything by default. -#define SERIAL_PORT_MONITOR Serial -#define SERIAL_PORT_USBVIRTUAL Serial -#define SERIAL_PORT_HARDWARE Serial -#define SERIAL_PORT_HARDWARE_OPEN Serial1 - -#endif /* GENERIC_COMMON_H */ diff --git a/variants/lwip/pins_arduino.h b/variants/lwip/pins_arduino.h deleted file mode 100644 index 0541019da..000000000 --- a/variants/lwip/pins_arduino.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - pins_arduino.h - Pin definition functions for Arduino - Part of Arduino - http://www.arduino.cc/ - - Copyright (c) 2007 David A. Mellis - Modified for ESP8266 platform by Ivan Grokhotkov, 2014-2015. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General - Public License along with this library; if not, write to the - Free Software Foundation, Inc., 59 Temple Place, Suite 330, - Boston, MA 02111-1307 USA - - $Id: wiring.h 249 2007-02-03 16:52:51Z mellis $ -*/ - -#ifndef Pins_Arduino_h -#define Pins_Arduino_h - -#include "common.h" - -static const uint8_t SDA = 4; -static const uint8_t SCL = 5; - -static const uint8_t BUILTIN_LED = 1; -static const uint8_t LED_BUILTIN = 1; - -#endif /* Pins_Arduino_h */ From d9e48d36e59a53853a61279ca5a7915ef2c15caf Mon Sep 17 00:00:00 2001 From: Maksim Surguy Date: Sun, 10 Apr 2016 20:32:27 -0700 Subject: [PATCH 19/46] Added Fast LED library to the list of supported libraries This library has been working well with Arduino for ESP8266 and it deserves to be added here. --- doc/libraries.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/libraries.md b/doc/libraries.md index d511adc08..312eb4934 100644 --- a/doc/libraries.md +++ b/doc/libraries.md @@ -163,3 +163,4 @@ Libraries that don't rely on low-level access to AVR registers should work well. - [Adafruit-PCD8544-Nokia-5110-LCD-Library](https://github.com/WereCatf/Adafruit-PCD8544-Nokia-5110-LCD-library) - Port of the Adafruit PCD8544 - library for the ESP8266. - [PCF8574_ESP](https://github.com/WereCatf/PCF8574_ESP) - A very simplistic library for using the PCF8574/PCF8574A I2C 8-pin GPIO-expander. - [Dot Matrix Display Library 2](https://github.com/freetronics/DMD2) - Freetronics DMD & Generic 16 x 32 P10 style Dot Matrix Display Library +- [FastLED](https://github.com/FastLED/FastLED) - a library for easily & efficiently controlling a wide variety of LED chipsets, like the Neopixel (WS2812B), DotStar, LPD8806 and many more. Includes fading, gradient, color conversion functions. From 41bd7af07ebfae3cfd41c59d6feb5834c63fd80b Mon Sep 17 00:00:00 2001 From: Peter Date: Mon, 11 Apr 2016 16:36:37 +0200 Subject: [PATCH 20/46] slow client/network read fix --- libraries/ESP8266WebServer/src/Parsing.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/ESP8266WebServer/src/Parsing.cpp b/libraries/ESP8266WebServer/src/Parsing.cpp index 4b1f375fa..af2922365 100644 --- a/libraries/ESP8266WebServer/src/Parsing.cpp +++ b/libraries/ESP8266WebServer/src/Parsing.cpp @@ -144,15 +144,16 @@ bool ESP8266WebServer::_parseRequest(WiFiClient& client) { { //some clients send headers first and data after (like we do) //give them a chance - int tries = 100;//100ms max wait - while(!client.available() && tries--)delay(1); - size_t newLen = client.available(); + int tries = 1000;//1000ms max wait + size_t newLen; + while( !(newLen = client.available()) && tries--) delay(1); if (!newLen) break; plainBuf = (plainBuf == nullptr) ? (char *) malloc(newLen + 1) : (char *) realloc(plainBuf, plainLen + newLen + 1); client.readBytes(&plainBuf[plainLen], newLen); plainLen += newLen; plainBuf[plainLen] = '\0'; } while (plainLen < contentLength); + if (plainBuf == nullptr) return false; #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.print("Plain: "); DEBUG_OUTPUT.println(plainBuf); From 82582171f5924b539eb8a3f4fa8620dde179b8d6 Mon Sep 17 00:00:00 2001 From: WereCatf Date: Mon, 11 Apr 2016 19:44:43 +0300 Subject: [PATCH 21/46] Add SdFat-beta to supported libraries --- doc/libraries.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/libraries.md b/doc/libraries.md index d511adc08..a1dec51b9 100644 --- a/doc/libraries.md +++ b/doc/libraries.md @@ -163,3 +163,4 @@ Libraries that don't rely on low-level access to AVR registers should work well. - [Adafruit-PCD8544-Nokia-5110-LCD-Library](https://github.com/WereCatf/Adafruit-PCD8544-Nokia-5110-LCD-library) - Port of the Adafruit PCD8544 - library for the ESP8266. - [PCF8574_ESP](https://github.com/WereCatf/PCF8574_ESP) - A very simplistic library for using the PCF8574/PCF8574A I2C 8-pin GPIO-expander. - [Dot Matrix Display Library 2](https://github.com/freetronics/DMD2) - Freetronics DMD & Generic 16 x 32 P10 style Dot Matrix Display Library +- [SdFat-beta](https://github.com/greiman/SdFat-beta) - SD-card library with support for long filenames, software- and hardware-based SPI and lots more. From b72cf2cdcf178d844cc0019d60e414f85e20a2b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B3r=C3=A1sz=20P=C3=A9ter?= Date: Wed, 13 Apr 2016 08:20:07 +0200 Subject: [PATCH 22/46] if data loss, exit --- libraries/ESP8266WebServer/src/Parsing.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libraries/ESP8266WebServer/src/Parsing.cpp b/libraries/ESP8266WebServer/src/Parsing.cpp index af2922365..519918308 100644 --- a/libraries/ESP8266WebServer/src/Parsing.cpp +++ b/libraries/ESP8266WebServer/src/Parsing.cpp @@ -153,7 +153,13 @@ bool ESP8266WebServer::_parseRequest(WiFiClient& client) { plainLen += newLen; plainBuf[plainLen] = '\0'; } while (plainLen < contentLength); + /* if data loss, exit */ if (plainBuf == nullptr) return false; + if (plainLen < contentLength) + { + free(plainBuf); + return false; + } #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.print("Plain: "); DEBUG_OUTPUT.println(plainBuf); From 46380003d5f31a6a77029c4fb928dbb738ca4f19 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 13 Apr 2016 14:02:10 +0300 Subject: [PATCH 23/46] Fix compilation when debug is enabled --- libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp index bad89074b..70ac6450e 100644 --- a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp +++ b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp @@ -163,7 +163,7 @@ bool HTTPClient::begin(String url) _port = 443; } } else { - DEBUG_HTTPCLIENT("[HTTP-Client][begin] protocol: %s unknown?!\n", protocol.c_str()); + DEBUG_HTTPCLIENT("[HTTP-Client][begin] protocol: %s unknown?!\n", _protocol.c_str()); return false; } _transportTraits = TransportTraitsPtr(new TransportTraits()); From 84daa1a108d0d29b45df94a70da12298ef0beae8 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 13 Apr 2016 14:03:11 +0300 Subject: [PATCH 24/46] Add explicit cast (#1903) --- libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp index 70ac6450e..383af599a 100644 --- a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp +++ b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp @@ -855,7 +855,7 @@ bool HTTPClient::sendHeader(const char * type) header += _headers + "\r\n"; - return (_tcp->write(header.c_str(), header.length()) == header.length()); + return (_tcp->write((const uint8_t *) header.c_str(), header.length()) == header.length()); } /** From 90f933b9cef12d87d56a6a183fd5d9f3333ea5f6 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 13 Apr 2016 15:26:31 +0300 Subject: [PATCH 25/46] Change build.board property for boards which renumber pins like NodeMCU (#1878) --- boards.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/boards.txt b/boards.txt index 934275987..12beb85ad 100644 --- a/boards.txt +++ b/boards.txt @@ -578,7 +578,7 @@ nodemcu.serial.disableRTS=true nodemcu.build.mcu=esp8266 nodemcu.build.f_cpu=80000000L -nodemcu.build.board=ESP8266_ESP12 +nodemcu.build.board=ESP8266_NODEMCU nodemcu.build.core=esp8266 nodemcu.build.variant=nodemcu nodemcu.build.flash_mode=qio @@ -644,7 +644,7 @@ nodemcuv2.serial.disableRTS=true nodemcuv2.build.mcu=esp8266 nodemcuv2.build.f_cpu=80000000L -nodemcuv2.build.board=ESP8266_ESP12 +nodemcuv2.build.board=ESP8266_NODEMCU nodemcuv2.build.core=esp8266 nodemcuv2.build.variant=nodemcu nodemcuv2.build.flash_mode=dio @@ -966,7 +966,7 @@ d1_mini.serial.disableRTS=true d1_mini.build.mcu=esp8266 d1_mini.build.f_cpu=80000000L -d1_mini.build.board=ESP8266_ESP12 +d1_mini.build.board=ESP8266_NODEMCU d1_mini.build.core=esp8266 d1_mini.build.variant=d1_mini d1_mini.build.flash_mode=dio @@ -1034,7 +1034,7 @@ d1.serial.disableRTS=true d1.build.mcu=esp8266 d1.build.f_cpu=80000000L -d1.build.board=ESP8266_ESP12 +d1.build.board=ESP8266_NODEMCU d1.build.core=esp8266 d1.build.variant=d1 d1.build.flash_mode=dio @@ -1249,7 +1249,7 @@ wifinfo.build.f_cpu=160000000L wifinfo.build.core=esp8266 wifinfo.build.variant=wifinfo wifinfo.build.flash_mode=qio -wifinfo.build.board=ESP8266_ESP12 +wifinfo.build.board=ESP8266_NODEMCU wifinfo.build.spiffs_pagesize=256 wifinfo.build.debug_port=Serial1 wifinfo.build.debug_level=Wifinfo From d7ce823f9798e6cf49bd9a3303e462d399572cf9 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 13 Apr 2016 16:20:36 +0300 Subject: [PATCH 26/46] Update change log --- doc/changes.md | 42 ++++++++++++++++++++++++++++++++++++++++++ platform.txt | 2 +- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/doc/changes.md b/doc/changes.md index 5f6214bbf..587f38d76 100644 --- a/doc/changes.md +++ b/doc/changes.md @@ -5,8 +5,50 @@ title: Change Log ## Current version ### Core +- Leverage realloc() in String::changeBuffer() +- Clean up core files +- Add host side tests +- Fix possible null pointer in umm_malloc +- Remove "Upload Using" option from Tools menu +- Move attachInterrupt and detachInterrupt into IRAM (#1734) +- Implement strstr_P +- Allow indefinite duration for tone() +- Fix crashes when using tone() +- Fix RF_MODE and ADC_MODE +- Move micros, delayMicroseconds, millis to IRAM (#1326) +- Fix pulseIn (#1072, #1149) +- Accept both named constant and ADC channel number in analogRead (#1766) +- Enable heap poisoning only when debug options are enabled (#1800) +- Bootloader: don't touch RTC memory if it doesn't contain a valid command (#619) +- Update SDK to 1.5.2 (#1653) +- Clean up variants, fix digitalPinHasPWM definition (#1831) +- Don't set RF mode on boot unless it was overridden +- Change build.board property for boards which renumber pins like NodeMCU (#1878) ### Libraries +- Update axTLS to 5b4be7d +- WiFiClientSecure: implement connection timeout, fix connected method behavior +- WiFiClient: fix write behavior when connection is closed by remote side +- ESP8266HTTPServer: add font MIME types, fix #1601 +- ESP8266mDNS: add client support +- Update SPIFFS to 82aeac6 +- Servo: move some functions into IRAM (#1742) +- Update SoftwareSerial to version 3.1.0 +- ESP8266SSDP: change templates to include deviceType +- ESP8266WebServer: handle more file types +- SPI: add CPOL setting +- ESP8266WebServer: Fix buffer overflow in ESP8266WebServer::authenticate (#1790) +- ESP8266WiFi: fix undefined behavior in WiFiServer::setNoDelay (#1695) +- Servo: use peripheral clock frequency when calculating FRC1 tick count (#1789) +- ESP8266WiFi: avoid multiple instances of INADDR_NONE +- Add LwIP binary built with gcc +- ESP8266WiFi: Allow PSK instead of passphrase in WiFiSTA::begin +- SPI: Fix SPI.transfer16() using wrong endianness +- HTTPClient: decouple transport layer handling + save some RAM +ESP8266httpUpdate: decouple HTTPS overloads + save some RAM +- Update and move lwIP headers, add options to use different lwIP build for generic device +- ESP8266WebServer: wait for data to arrive + ### Tools diff --git a/platform.txt b/platform.txt index 83f70c9f0..c3ebf1160 100644 --- a/platform.txt +++ b/platform.txt @@ -6,7 +6,7 @@ # https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5---3rd-party-Hardware-specification name=ESP8266 Modules -version=2.1.0 +version=2.2.0 runtime.tools.xtensa-lx106-elf-gcc.path={runtime.platform.path}/tools/xtensa-lx106-elf runtime.tools.esptool.path={runtime.platform.path}/tools/esptool From 797c78ddadbbad1b86e65e615a6142c44a1662ce Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 14 Apr 2016 13:06:23 +0300 Subject: [PATCH 27/46] Add ARM tools (#269) --- .../package_esp8266com_index.template.json | 21 +++++++++++++++++++ tools/get.py | 9 +++++--- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/package/package_esp8266com_index.template.json b/package/package_esp8266com_index.template.json index 651203039..acd17151f 100644 --- a/package/package_esp8266com_index.template.json +++ b/package/package_esp8266com_index.template.json @@ -126,6 +126,13 @@ "archiveFileName": "esptool-0.4.8-linux32.tar.gz", "checksum": "SHA-256:b0d6e71e6f41d4ed7e167bb4b3f4f0b1b3e49d69af50ab7fbe952cbfed83f164", "size": "15444" + }, + { + "host": "arm-linux-gnueabihf", + "url": "https://github.com/igrr/esptool-ck/releases/download/0.4.8/esptool-0.4.8-linux-armhf.tar.gz", + "archiveFileName": "esptool-0.4.8-linux-armhf.tar.gz", + "checksum": "SHA-256:e9c4dfb81781610556a6af0377c8efc7cde359e0e2cda2fd48e0a32bae10f506", + "size": "13630" } ] }, @@ -167,6 +174,13 @@ "archiveFileName": "linux32-xtensa-lx106-elf.tar.gz", "checksum": "SHA-256:b24817819f0078fb05895a640e806e0aca9aa96b47b80d2390ac8e2d9ddc955a", "size": "32734156" + }, + { + "host": "arm-linux-gnueabihf", + "url": "http://arduino.esp8266.com/linuxarm-xtensa-lx106-elf-g46f160f.tar.gz", + "archiveFileName": "linuxarm-xtensa-lx106-elf-g46f160f.tar.gz", + "checksum": "SHA-256:1ac752bac7fff6be95ce10f56689f155fefea09e4ef710dbd75cb573798ff9c0", + "size": "34929527" } ] }, @@ -208,6 +222,13 @@ "archiveFileName": "mkspiffs-0.1.2-linux32.tar.gz", "checksum": "SHA-256:e990d545dfcae308aabaac5fa9e1db734cc2b08167969e7eedac88bd0839667c", "size": "45272" + }, + { + "host": "arm-linux-gnueabihf", + "url": "https://github.com/igrr/mkspiffs/releases/download/0.1.2/mkspiffs-0.1.2-linux-armhf.tar.gz", + "archiveFileName": "mkspiffs-0.1.2-linux-armhf.tar.gz", + "checksum": "SHA-256:5a8836932cd24325d69054cebdd46359eba02919ffaa87b130c54acfecc13f46", + "size": "41685" } ] } diff --git a/tools/get.py b/tools/get.py index eb8051ef8..d618f0b5b 100755 --- a/tools/get.py +++ b/tools/get.py @@ -90,13 +90,16 @@ def load_tools_list(filename, platform): return tools_to_download def identify_platform(): - arduino_platform_names = {'Darwin' : {32 : 'i386-apple-darwin', 64 : 'x86_64-apple-darwin'}, - 'Linux' : {32 : 'i686-pc-linux-gnu', 64 : 'x86_64-pc-linux-gnu'}, - 'Windows' : {32 : 'i686-mingw32', 64 : 'i686-mingw32'}} + arduino_platform_names = {'Darwin' : {32 : 'i386-apple-darwin', 64 : 'x86_64-apple-darwin'}, + 'Linux' : {32 : 'i686-pc-linux-gnu', 64 : 'x86_64-pc-linux-gnu'}, + 'LinuxARM': {32 : 'arm-linux-gnueabihf', 64 : 'aarch64-linux-gnu'}, + 'Windows' : {32 : 'i686-mingw32', 64 : 'i686-mingw32'}} bits = 32 if sys.maxsize > 2**32: bits = 64 sys_name = platform.system() + if 'Linux' in sys_name and platform.platform().find('arm') > 0: + sys_name = 'LinuxARM' if 'CYGWIN_NT' in sys_name: sys_name = 'Windows' return arduino_platform_names[sys_name][bits] From 703612927091453787cf933f37429fcc33ad785e Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Thu, 14 Apr 2016 21:25:16 +0300 Subject: [PATCH 28/46] Add async libraries to list of supported --- doc/libraries.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/libraries.md b/doc/libraries.md index c37bcf15f..14c496224 100644 --- a/doc/libraries.md +++ b/doc/libraries.md @@ -147,6 +147,8 @@ Libraries that don't rely on low-level access to AVR registers should work well. - [DimSwitch](https://github.com/krzychb/DimSwitch) - Control electronic dimmable ballasts for fluorescent light tubes remotely as if using a wall switch. - [Encoder](https://github.com/PaulStoffregen/Encoder) - Arduino library for rotary encoders. Version 1.4 supports ESP8266. - [esp8266_mdns](https://github.com/mrdunk/esp8266_mdns) - mDNS queries and responses on esp8266. Or to describe it another way: An mDNS Client or Bonjour Client library for the esp8266. +- [ESPAsyncTCP](https://github.com/me-no-dev/ESPAsyncTCP) - Asynchronous TCP Library for ESP8266 and ESP32/31B +- [ESPAsyncWebServer](https://github.com/me-no-dev/ESPAsyncWebServer) - Asynchronous Web Server Library for ESP8266 and ESP32/31B - [Homie for ESP8266](https://github.com/marvinroger/homie-esp8266) - Arduino framework for ESP8266 implementing Homie, an MQTT convention for the IoT. - [NeoPixel](https://github.com/adafruit/Adafruit_NeoPixel) - Adafruit's NeoPixel library, now with support for the ESP8266 (use version 1.0.2 or higher from Arduino's library manager). - [NeoPixelBus](https://github.com/Makuna/NeoPixelBus) - Arduino NeoPixel library compatible with ESP8266. Use the "DmaDriven" or "UartDriven" branches for ESP8266. Includes HSL color support and more. From 04263fabd146f966bb2663064906d934410d4c72 Mon Sep 17 00:00:00 2001 From: urmilparikh Date: Sat, 16 Apr 2016 16:45:01 +0530 Subject: [PATCH 29/46] Add reference to OLED display library --- doc/libraries.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/libraries.md b/doc/libraries.md index c37bcf15f..2cc5b7048 100644 --- a/doc/libraries.md +++ b/doc/libraries.md @@ -165,3 +165,4 @@ Libraries that don't rely on low-level access to AVR registers should work well. - [Dot Matrix Display Library 2](https://github.com/freetronics/DMD2) - Freetronics DMD & Generic 16 x 32 P10 style Dot Matrix Display Library - [SdFat-beta](https://github.com/greiman/SdFat-beta) - SD-card library with support for long filenames, software- and hardware-based SPI and lots more. - [FastLED](https://github.com/FastLED/FastLED) - a library for easily & efficiently controlling a wide variety of LED chipsets, like the Neopixel (WS2812B), DotStar, LPD8806 and many more. Includes fading, gradient, color conversion functions. +- [OLED](https://github.com/klarsys/esp8266-OLED) - a library for controlling I2C connected OLED displays. Tested with 0.96 inch OLED graphics display. From a64a694f6d1427e28bc55b0aa90ffbf3bccecc69 Mon Sep 17 00:00:00 2001 From: WereCatf Date: Sat, 16 Apr 2016 20:02:06 +0300 Subject: [PATCH 30/46] Speed up SPI.writePattern() --- libraries/SPI/SPI.cpp | 59 +++++++++++++++++++++++-------------------- libraries/SPI/SPI.h | 1 - 2 files changed, 32 insertions(+), 28 deletions(-) diff --git a/libraries/SPI/SPI.cpp b/libraries/SPI/SPI.cpp index e9c6cf35d..97457ef01 100644 --- a/libraries/SPI/SPI.cpp +++ b/libraries/SPI/SPI.cpp @@ -350,11 +350,7 @@ void SPIClass::writeBytes_(uint8_t * data, uint8_t size) { while(SPI1CMD & SPIBUSY) {} } - /** - * Note: - * data need to be aligned to 32Bit - * or you get an Fatal exception (9) * @param data uint8_t * * @param size uint8_t max for size is 64Byte * @param repeat uint32_t @@ -362,37 +358,46 @@ void SPIClass::writeBytes_(uint8_t * data, uint8_t size) { void SPIClass::writePattern(uint8_t * data, uint8_t size, uint32_t repeat) { if(size > 64) return; //max Hardware FIFO - uint32_t byte = (size * repeat); - uint8_t r = (64 / size); + while(SPI1CMD & SPIBUSY) {} - while(byte) { - if(byte > 64) { - writePattern_(data, size, r); - byte -= 64; - } else { - writePattern_(data, size, (byte / size)); - byte = 0; - } - } -} + uint32_t buffer[16]; + uint8_t *bufferPtr=(uint8_t *)&buffer; + uint8_t *dataPtr = data; + volatile uint32_t * fifoPtr = &SPI1W0; + uint8_t r = 64 / size; + uint32_t repeatRem = repeat % r; + repeat = repeat / r; -void SPIClass::writePattern_(uint8_t * data, uint8_t size, uint8_t repeat) { - uint8_t bytes = (size * repeat); - uint8_t buffer[64]; - uint8_t * bufferPtr = &buffer[0]; - uint8_t * dataPtr; - uint8_t dataSize = bytes; - for(uint8_t i = 0; i < repeat; i++) { - dataSize = size; + while(r--){ dataPtr = data; - while(dataSize--) { + for(uint8_t i=0; i Date: Sun, 17 Apr 2016 02:12:01 +0300 Subject: [PATCH 31/46] Fix Exception 2 when using printf or vprintf ets_vprintf signature is not the same as vprintf, it takes an output function as a first argument. --- cores/esp8266/libc_replacements.c | 5 ++--- tools/sdk/include/ets_sys.h | 5 +++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cores/esp8266/libc_replacements.c b/cores/esp8266/libc_replacements.c index 57fbed9c9..23180261d 100644 --- a/cores/esp8266/libc_replacements.c +++ b/cores/esp8266/libc_replacements.c @@ -52,10 +52,9 @@ int ICACHE_RAM_ATTR putchar(int c) { } int ICACHE_RAM_ATTR printf(const char* format, ...) { - int ret; va_list arglist; va_start(arglist, format); - ret = ets_vprintf(format, arglist); + int ret = ets_vprintf(ets_putc, format, arglist); va_end(arglist); return ret; } @@ -79,7 +78,7 @@ int ICACHE_RAM_ATTR snprintf(char* buffer, size_t size, const char* format, ...) } int ICACHE_RAM_ATTR vprintf(const char * format, va_list arg) { - return ets_vprintf(format, arg); + return ets_vprintf(ets_putc, format, arg); } int ICACHE_RAM_ATTR vsnprintf(char * buffer, size_t size, const char * format, va_list arg) { diff --git a/tools/sdk/include/ets_sys.h b/tools/sdk/include/ets_sys.h index 813061ab7..8eb64b343 100644 --- a/tools/sdk/include/ets_sys.h +++ b/tools/sdk/include/ets_sys.h @@ -155,7 +155,7 @@ inline uint32_t ETS_INTR_PENDING(void) #define ETS_SDIO_INTR_DISABLE() \ ETS_INTR_DISABLE(ETS_SDIO_INUM) - + void *pvPortMalloc(size_t xWantedSize, const char* file, int line) __attribute__((malloc, alloc_size(1))); void *pvPortRealloc(void* ptr, size_t xWantedSize, const char* file, int line) __attribute__((alloc_size(2))); @@ -183,7 +183,8 @@ void ets_isr_attach(int intr, int_handler_t handler, void *arg); void ets_intr_lock(); void ets_intr_unlock(); int ets_vsnprintf(char * s, size_t n, const char * format, va_list arg) __attribute__ ((format (printf, 3, 0))); -int ets_vprintf(const char * format, va_list arg) __attribute__ ((format (printf, 1, 0))); +int ets_vprintf(int (*print_function)(int), const char * format, va_list arg) __attribute__ ((format (printf, 2, 0))); +int ets_putc(int); bool ets_task(ETSTask task, uint8 prio, ETSEvent *queue, uint8 qlen); bool ets_post(uint8 prio, ETSSignal sig, ETSParam par); From 8ffe1aa2e2727c015d299efe5c8aef71835787b1 Mon Sep 17 00:00:00 2001 From: WereCatf Date: Mon, 18 Apr 2016 01:26:03 +0300 Subject: [PATCH 32/46] Speed up writePattern() a bit more --- libraries/SPI/SPI.cpp | 80 ++++++++++++++++++++++++++++--------------- 1 file changed, 53 insertions(+), 27 deletions(-) diff --git a/libraries/SPI/SPI.cpp b/libraries/SPI/SPI.cpp index 97457ef01..c062bfc08 100644 --- a/libraries/SPI/SPI.cpp +++ b/libraries/SPI/SPI.cpp @@ -364,36 +364,62 @@ void SPIClass::writePattern(uint8_t * data, uint8_t size, uint32_t repeat) { uint8_t *bufferPtr=(uint8_t *)&buffer; uint8_t *dataPtr = data; volatile uint32_t * fifoPtr = &SPI1W0; - uint8_t r = 64 / size; - uint32_t repeatRem = repeat % r; - repeat = repeat / r; + uint8_t r; + uint32_t repeatRem; + uint8_t i; - while(r--){ - dataPtr = data; - for(uint8_t i=0; i Date: Mon, 18 Apr 2016 07:49:14 +0200 Subject: [PATCH 33/46] Python 3 compatibility --- tools/get.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tools/get.py b/tools/get.py index eb8051ef8..f55905622 100755 --- a/tools/get.py +++ b/tools/get.py @@ -4,7 +4,6 @@ # Written by Ivan Grokhotkov, 2015. # from __future__ import print_function -import urllib import os import shutil import errno @@ -16,6 +15,11 @@ import sys import tarfile import zipfile import re +if sys.version_info[0] == 3: + from urllib.request import urlretrieve +else: + # Not Python 3 - today, it is most likely to be Python 2 + from urllib import urlretrieve dist_dir = 'dist/' @@ -54,7 +58,7 @@ def unpack(filename, destination): raise NotImplementedError('Unsupported archive type') # a little trick to rename tool directories so they don't contain version number - rename_to = re.match(r'^([a-z][^\-]*\-*)+', dirname).group(0).encode('ascii').strip('-') + rename_to = re.match(r'^([a-z][^\-]*\-*)+', dirname).group(0).strip('-') if rename_to != dirname: print('Renaming {0} to {1}'.format(dirname, rename_to)) if os.path.isdir(rename_to): @@ -68,7 +72,7 @@ def get_tool(tool): real_hash = tool['checksum'].split(':')[1] if not os.path.isfile(local_path): print('Downloading ' + archive_name); - urllib.urlretrieve(url, local_path, report_progress) + urlretrieve(url, local_path, report_progress) sys.stdout.write("\rDone\n") sys.stdout.flush() else: From f6516b004fca47a06ddcb3789434fc201d926566 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 18 Apr 2016 10:33:10 +0300 Subject: [PATCH 34/46] ESP8266WebServer: fix handling of empty POST Fix handling of case when Content-Length is 0. Change do {} while() loop into while(){} so that we don't wait 1000ms for data in case Content-Length is 0. Also fix handling of cases when malloc or realloc return null. --- .../ESP8266WebServer/src/ESP8266WebServer.h | 5 +- libraries/ESP8266WebServer/src/Parsing.cpp | 80 ++++++++++++------- 2 files changed, 52 insertions(+), 33 deletions(-) diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.h b/libraries/ESP8266WebServer/src/ESP8266WebServer.h index 4e291e5fb..f94cfb07f 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.h @@ -34,6 +34,7 @@ enum HTTPClientStatus { HC_NONE, HC_WAIT_READ, HC_WAIT_CLOSE }; #define HTTP_DOWNLOAD_UNIT_SIZE 1460 #define HTTP_UPLOAD_BUFLEN 2048 #define HTTP_MAX_DATA_WAIT 1000 //ms to wait for the client to send the request +#define HTTP_MAX_POST_WAIT 1000 //ms to wait for POST data to arrive #define HTTP_MAX_CLOSE_WAIT 2000 //ms to wait for the client to close the connection #define CONTENT_LENGTH_UNKNOWN ((size_t) -1) @@ -66,13 +67,13 @@ public: void begin(); void handleClient(); - + void close(); void stop(); bool authenticate(const char * username, const char * password); void requestAuthentication(); - + typedef std::function THandlerFunction; void on(const char* uri, THandlerFunction handler); void on(const char* uri, HTTPMethod method, THandlerFunction fn); diff --git a/libraries/ESP8266WebServer/src/Parsing.cpp b/libraries/ESP8266WebServer/src/Parsing.cpp index 519918308..f4fff1765 100644 --- a/libraries/ESP8266WebServer/src/Parsing.cpp +++ b/libraries/ESP8266WebServer/src/Parsing.cpp @@ -31,6 +31,38 @@ #define DEBUG_OUTPUT Serial #endif +static char* readBytesWithTimeout(WiFiClient& client, size_t maxLength, size_t& dataLength, int timeout_ms) +{ + char *buf = nullptr; + dataLength = 0; + while (dataLength < maxLength) { + int tries = timeout_ms; + size_t newLength; + while (!(newLength = client.available()) && tries--) delay(1); + if (!newLength) { + break; + } + if (!buf) { + buf = (char *) malloc(newLength + 1); + if (!buf) { + return nullptr; + } + } + else { + char* newBuf = (char *) realloc(buf, dataLength + newLength + 1); + if (!newBuf) { + free(buf); + return nullptr; + } + buf = newBuf; + } + client.readBytes(buf + dataLength, newLength); + dataLength += newLength; + buf[dataLength] = '\0'; + } + return buf; +} + bool ESP8266WebServer::_parseRequest(WiFiClient& client) { // Read the first line of HTTP request String req = client.readStringUntil('\r'); @@ -114,14 +146,14 @@ bool ESP8266WebServer::_parseRequest(WiFiClient& client) { headerValue = req.substring(headerDiv + 1); headerValue.trim(); _collectHeader(headerName.c_str(),headerValue.c_str()); - + #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.print("headerName: "); DEBUG_OUTPUT.println(headerName); DEBUG_OUTPUT.print("headerValue: "); DEBUG_OUTPUT.println(headerValue); #endif - + if (headerName == "Content-Type"){ if (headerValue.startsWith("text/plain")){ isForm = false; @@ -137,26 +169,9 @@ bool ESP8266WebServer::_parseRequest(WiFiClient& client) { } if (!isForm){ - if (searchStr != "") searchStr += '&'; - char *plainBuf = nullptr; - size_t plainLen = 0; - do - { - //some clients send headers first and data after (like we do) - //give them a chance - int tries = 1000;//1000ms max wait - size_t newLen; - while( !(newLen = client.available()) && tries--) delay(1); - if (!newLen) break; - plainBuf = (plainBuf == nullptr) ? (char *) malloc(newLen + 1) : (char *) realloc(plainBuf, plainLen + newLen + 1); - client.readBytes(&plainBuf[plainLen], newLen); - plainLen += newLen; - plainBuf[plainLen] = '\0'; - } while (plainLen < contentLength); - /* if data loss, exit */ - if (plainBuf == nullptr) return false; - if (plainLen < contentLength) - { + size_t plainLength; + char* plainBuf = readBytesWithTimeout(client, contentLength, plainLength, HTTP_MAX_POST_WAIT); + if (plainLength < contentLength) { free(plainBuf); return false; } @@ -164,14 +179,17 @@ bool ESP8266WebServer::_parseRequest(WiFiClient& client) { DEBUG_OUTPUT.print("Plain: "); DEBUG_OUTPUT.println(plainBuf); #endif - if(plainBuf[0] == '{' || plainBuf[0] == '[' || strstr(plainBuf, "=") == NULL){ - //plain post json or other data - searchStr += "plain="; - searchStr += plainBuf; - } else { - searchStr += plainBuf; + if (contentLength > 0) { + if (searchStr != "") searchStr += '&'; + if(plainBuf[0] == '{' || plainBuf[0] == '[' || strstr(plainBuf, "=") == NULL){ + //plain post json or other data + searchStr += "plain="; + searchStr += plainBuf; + } else { + searchStr += plainBuf; + } + free(plainBuf); } - free(plainBuf); } _parseArguments(searchStr); if (isForm){ @@ -194,14 +212,14 @@ bool ESP8266WebServer::_parseRequest(WiFiClient& client) { headerName = req.substring(0, headerDiv); headerValue = req.substring(headerDiv + 2); _collectHeader(headerName.c_str(),headerValue.c_str()); - + #ifdef DEBUG_ESP_HTTP_SERVER DEBUG_OUTPUT.print("headerName: "); DEBUG_OUTPUT.println(headerName); DEBUG_OUTPUT.print("headerValue: "); DEBUG_OUTPUT.println(headerValue); #endif - + if (headerName == "Host"){ _hostHeader = headerValue; } From 7450dd39ca93ca849f9198b0151e4c7c747e43ef Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 18 Apr 2016 10:43:00 +0300 Subject: [PATCH 35/46] ESP8266WebServer: save RAM by moving response strings to flash (#1732) --- .../ESP8266WebServer/src/ESP8266WebServer.cpp | 84 +++++++++---------- .../ESP8266WebServer/src/ESP8266WebServer.h | 2 +- 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp b/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp index 1ab80260f..353381bef 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp @@ -373,7 +373,7 @@ void ESP8266WebServer::sendContent_P(PGM_P content, size_t size) { String ESP8266WebServer::arg(String name) { for (int i = 0; i < _currentArgCount; ++i) { - if ( _currentArgs[i].key == name ) + if ( _currentArgs[i].key == name ) return _currentArgs[i].value; } return String(); @@ -487,48 +487,48 @@ void ESP8266WebServer::_handleRequest() { _currentUri = String(); } -const char* ESP8266WebServer::_responseCodeToString(int code) { +String ESP8266WebServer::_responseCodeToString(int code) { switch (code) { - case 100: return "Continue"; - case 101: return "Switching Protocols"; - case 200: return "OK"; - case 201: return "Created"; - case 202: return "Accepted"; - case 203: return "Non-Authoritative Information"; - case 204: return "No Content"; - case 205: return "Reset Content"; - case 206: return "Partial Content"; - case 300: return "Multiple Choices"; - case 301: return "Moved Permanently"; - case 302: return "Found"; - case 303: return "See Other"; - case 304: return "Not Modified"; - case 305: return "Use Proxy"; - case 307: return "Temporary Redirect"; - case 400: return "Bad Request"; - case 401: return "Unauthorized"; - case 402: return "Payment Required"; - case 403: return "Forbidden"; - case 404: return "Not Found"; - case 405: return "Method Not Allowed"; - case 406: return "Not Acceptable"; - case 407: return "Proxy Authentication Required"; - case 408: return "Request Time-out"; - case 409: return "Conflict"; - case 410: return "Gone"; - case 411: return "Length Required"; - case 412: return "Precondition Failed"; - case 413: return "Request Entity Too Large"; - case 414: return "Request-URI Too Large"; - case 415: return "Unsupported Media Type"; - case 416: return "Requested range not satisfiable"; - case 417: return "Expectation Failed"; - case 500: return "Internal Server Error"; - case 501: return "Not Implemented"; - case 502: return "Bad Gateway"; - case 503: return "Service Unavailable"; - case 504: return "Gateway Time-out"; - case 505: return "HTTP Version not supported"; + case 100: return F("Continue"); + case 101: return F("Switching Protocols"); + case 200: return F("OK"); + case 201: return F("Created"); + case 202: return F("Accepted"); + case 203: return F("Non-Authoritative Information"); + case 204: return F("No Content"); + case 205: return F("Reset Content"); + case 206: return F("Partial Content"); + case 300: return F("Multiple Choices"); + case 301: return F("Moved Permanently"); + case 302: return F("Found"); + case 303: return F("See Other"); + case 304: return F("Not Modified"); + case 305: return F("Use Proxy"); + case 307: return F("Temporary Redirect"); + case 400: return F("Bad Request"); + case 401: return F("Unauthorized"); + case 402: return F("Payment Required"); + case 403: return F("Forbidden"); + case 404: return F("Not Found"); + case 405: return F("Method Not Allowed"); + case 406: return F("Not Acceptable"); + case 407: return F("Proxy Authentication Required"); + case 408: return F("Request Time-out"); + case 409: return F("Conflict"); + case 410: return F("Gone"); + case 411: return F("Length Required"); + case 412: return F("Precondition Failed"); + case 413: return F("Request Entity Too Large"); + case 414: return F("Request-URI Too Large"); + case 415: return F("Unsupported Media Type"); + case 416: return F("Requested range not satisfiable"); + case 417: return F("Expectation Failed"); + case 500: return F("Internal Server Error"); + case 501: return F("Not Implemented"); + case 502: return F("Bad Gateway"); + case 503: return F("Service Unavailable"); + case 504: return F("Gateway Time-out"); + case 505: return F("HTTP Version not supported"); default: return ""; } } diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.h b/libraries/ESP8266WebServer/src/ESP8266WebServer.h index f94cfb07f..4cf3fcb3a 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.h @@ -134,7 +134,7 @@ protected: void _handleRequest(); bool _parseRequest(WiFiClient& client); void _parseArguments(String data); - static const char* _responseCodeToString(int code); + static String _responseCodeToString(int code); bool _parseForm(WiFiClient& client, String boundary, uint32_t len); bool _parseFormUploadAborted(); void _uploadWriteByte(uint8_t b); From de3058fc43f8a3298f472dfbea2b79854c3a08aa Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 18 Apr 2016 11:22:31 +0300 Subject: [PATCH 36/46] Update change log --- doc/changes.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/doc/changes.md b/doc/changes.md index 587f38d76..d04336a02 100644 --- a/doc/changes.md +++ b/doc/changes.md @@ -24,6 +24,8 @@ title: Change Log - Clean up variants, fix digitalPinHasPWM definition (#1831) - Don't set RF mode on boot unless it was overridden - Change build.board property for boards which renumber pins like NodeMCU (#1878) +- Fix Exception 2 when using printf or vprintf +- Add ARM tools (#269) ### Libraries - Update axTLS to 5b4be7d @@ -45,9 +47,11 @@ title: Change Log - ESP8266WiFi: Allow PSK instead of passphrase in WiFiSTA::begin - SPI: Fix SPI.transfer16() using wrong endianness - HTTPClient: decouple transport layer handling + save some RAM -ESP8266httpUpdate: decouple HTTPS overloads + save some RAM -- Update and move lwIP headers, add options to use different lwIP build for generic device +- ESP8266httpUpdate: decouple HTTPS overloads + save some RAM +- Update and move lwIP headers, add options to use different lwIP build - ESP8266WebServer: wait for data to arrive +- ESP8266WebServer: save RAM by moving response strings to flash (#1732) +- SPI: Speed up SPI.writePattern() ### Tools From b7c23c79de240c1ba4ee739edf78401fb410cf4f Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 18 Apr 2016 12:05:56 +0300 Subject: [PATCH 37/46] Update documentation links --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cbb7da28a..769588b83 100644 --- a/README.md +++ b/README.md @@ -34,12 +34,12 @@ If you find this forum or the ESP8266 Boards Manager package useful, please cons ##### Stable version ![](http://arduino.esp8266.com/stable/badge.svg) Boards manager link: `http://arduino.esp8266.com/stable/package_esp8266com_index.json` -Documentation: [http://esp8266.github.io/Arduino/versions/2.1.0/](http://esp8266.github.io/Arduino/versions/2.1.0/) +Documentation: [http://esp8266.github.io/Arduino/versions/2.2.0/](http://esp8266.github.io/Arduino/versions/2.2.0/) ##### Staging version ![](http://arduino.esp8266.com/staging/badge.svg) Boards manager link: `http://arduino.esp8266.com/staging/package_esp8266com_index.json` -Documentation: [http://esp8266.github.io/Arduino/versions/2.1.0-rc2/](http://esp8266.github.io/Arduino/versions/2.1.0-rc2/) +Documentation: [http://esp8266.github.io/Arduino/versions/2.2.0-rc1/](http://esp8266.github.io/Arduino/versions/2.2.0-rc1/) ### Using git version [![Linux build status](https://travis-ci.org/esp8266/Arduino.svg)](https://travis-ci.org/esp8266/Arduino) [![codecov.io](https://codecov.io/github/esp8266/Arduino/coverage.svg?branch=master)](https://codecov.io/github/esp8266/Arduino?branch=master) From 8c65f2fcd01b5a8de0e18144be19b15075e06ebf Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 19 Apr 2016 08:29:13 +0300 Subject: [PATCH 38/46] Update axTLS to fe4518d, SNI support in WiFiClientSecure (#1285) Fixes #1933 --- .../ESP8266WiFi/src/WiFiClientSecure.cpp | 19 +++++++++++------- libraries/ESP8266WiFi/src/WiFiClientSecure.h | 2 +- libraries/ESP8266WiFi/src/include/ssl.h | 3 ++- tools/sdk/lib/libaxtls.a | Bin 559326 -> 558692 bytes 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecure.cpp b/libraries/ESP8266WiFi/src/WiFiClientSecure.cpp index b4e8ce0bc..5c2408818 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecure.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClientSecure.cpp @@ -93,8 +93,8 @@ public: } } - void connect(ClientContext* ctx, uint32_t timeout_ms) { - _ssl = ssl_client_new(_ssl_ctx, reinterpret_cast(ctx), nullptr, 0); + void connect(ClientContext* ctx, const char* hostName, uint32_t timeout_ms) { + _ssl = ssl_client_new(_ssl_ctx, reinterpret_cast(ctx), nullptr, 0, hostName); uint32_t t = millis(); while (millis() - t < timeout_ms && ssl_handshake_status(_ssl) != SSL_OK) { @@ -242,16 +242,21 @@ int WiFiClientSecure::connect(IPAddress ip, uint16_t port) { if (!WiFiClient::connect(ip, port)) return 0; - return _connectSSL(); + return _connectSSL(nullptr); } int WiFiClientSecure::connect(const char* name, uint16_t port) { - if (!WiFiClient::connect(name, port)) + IPAddress remote_addr; + if (!WiFi.hostByName(name, remote_addr)) { return 0; - return 1; + } + if (!WiFiClient::connect(remote_addr, port)) { + return 0; + } + return _connectSSL(name); } -int WiFiClientSecure::_connectSSL() { +int WiFiClientSecure::_connectSSL(const char* hostName) { if (_ssl) { _ssl->unref(); _ssl = nullptr; @@ -259,7 +264,7 @@ int WiFiClientSecure::_connectSSL() { _ssl = new SSLContext; _ssl->ref(); - _ssl->connect(_client, 5000); + _ssl->connect(_client, hostName, 5000); auto status = ssl_handshake_status(*_ssl); if (status != SSL_OK) { diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecure.h b/libraries/ESP8266WiFi/src/WiFiClientSecure.h index a3ba73502..706994f35 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecure.h +++ b/libraries/ESP8266WiFi/src/WiFiClientSecure.h @@ -66,7 +66,7 @@ public: } protected: - int _connectSSL(); + int _connectSSL(const char* hostName); SSLContext* _ssl = nullptr; }; diff --git a/libraries/ESP8266WiFi/src/include/ssl.h b/libraries/ESP8266WiFi/src/include/ssl.h index 82cc84914..a18a756f7 100644 --- a/libraries/ESP8266WiFi/src/include/ssl.h +++ b/libraries/ESP8266WiFi/src/include/ssl.h @@ -243,10 +243,11 @@ EXP_FUNC SSL * STDCALL ssl_server_new(SSL_CTX *ssl_ctx, int client_fd); * can be null if no session resumption is being used or required. This option * is not used in skeleton mode. * @param sess_id_size The size of the session id (max 32) + * @param host_name If non-zero, host name to be sent to server for SNI support * @return An SSL object reference. Use ssl_handshake_status() to check * if a handshake succeeded. */ -EXP_FUNC SSL * STDCALL ssl_client_new(SSL_CTX *ssl_ctx, int client_fd, const uint8_t *session_id, uint8_t sess_id_size); +EXP_FUNC SSL * STDCALL ssl_client_new(SSL_CTX *ssl_ctx, int client_fd, const uint8_t *session_id, uint8_t sess_id_size, const char* host_name); /** * @brief Free any used resources on this connection. diff --git a/tools/sdk/lib/libaxtls.a b/tools/sdk/lib/libaxtls.a index 38dda0fddea5dcfb545ba81bf416c4c4c3a2a076..a3f03cdf38aa4847ff7bd2f8058f8340b5da375e 100644 GIT binary patch delta 85602 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 XBsi-^EE*QN!*4Z;{4^qzSLy!&3*p_z From c8a1507504cc5678af9a41a5082a6de46164240a Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 19 Apr 2016 09:32:51 +0300 Subject: [PATCH 39/46] Update axTLS to 69c757f Fix issue with handling of large certificates (#1816) --- tools/sdk/lib/libaxtls.a | Bin 558692 -> 559636 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/tools/sdk/lib/libaxtls.a b/tools/sdk/lib/libaxtls.a index a3f03cdf38aa4847ff7bd2f8058f8340b5da375e..6fd4763ff4b37231b1767973fcdb6ee1cac367fa 100644 GIT binary patch delta 66981 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 From 46fccac3e55852e4fc2e54f2c68d7c0a761cda2d Mon Sep 17 00:00:00 2001 From: Andronicus Riyono Date: Wed, 20 Apr 2016 14:09:41 +0800 Subject: [PATCH 40/46] fix bug #1940 --- libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp b/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp index b0020aad7..c4dc84164 100644 --- a/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp +++ b/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp @@ -220,7 +220,7 @@ HTTPUpdateResult ESP8266HTTPUpdate::handleUpdate(HTTPClient& http, const String& DEBUG_HTTP_UPDATE("[httpUpdate] - current Sketch Size: %d\n", ESP.getSketchSize()); if(currentVersion && currentVersion[0] != 0x00) { - DEBUG_HTTP_UPDATE("[httpUpdate] - current version: %s\n", currentVersion); + DEBUG_HTTP_UPDATE("[httpUpdate] - current version: %s\n", currentVersion.c_str() ); } switch(code) { From 81d3bb3e3a052027f8349db1c0f0380306897619 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 22 Apr 2016 19:21:57 +0800 Subject: [PATCH 41/46] Return error when HTTPClient::begin is called with HTTPS URL without certificate fingerprint (#1941) --- .../src/ESP8266HTTPClient.cpp | 55 +++++++++---------- .../ESP8266HTTPClient/src/ESP8266HTTPClient.h | 1 + 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp index 383af599a..af2386e86 100644 --- a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp +++ b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp @@ -87,20 +87,6 @@ HTTPClient::~HTTPClient() } } - -bool HTTPClient::begin(String url, String httpsFingerprint) -{ - if (httpsFingerprint.length() == 0) { - return false; - } - if (!begin(url)) { - return false; - } - _transportTraits = TransportTraitsPtr(new TLSTraits(httpsFingerprint)); - DEBUG_HTTPCLIENT("[HTTP-Client][begin] httpsFingerprint: %s\n", httpsFingerprint.c_str()); - return true; -} - void HTTPClient::clear() { _returnCode = 0; @@ -109,11 +95,35 @@ void HTTPClient::clear() } +bool HTTPClient::begin(String url, String httpsFingerprint) +{ + _transportTraits.reset(nullptr); + if (httpsFingerprint.length() == 0) { + return false; + } + if (!beginInternal(url, "https")) { + return false; + } + _transportTraits = TransportTraitsPtr(new TLSTraits(httpsFingerprint)); + DEBUG_HTTPCLIENT("[HTTP-Client][begin] httpsFingerprint: %s\n", httpsFingerprint.c_str()); + return true; +} + /** * parsing the url for all needed parameters * @param url String */ bool HTTPClient::begin(String url) +{ + _transportTraits.reset(nullptr); + if (!beginInternal(url, "http")) { + return false; + } + _transportTraits = TransportTraitsPtr(new TransportTraits()); + return true; +} + +bool HTTPClient::beginInternal(String url, const char* expectedProtocol) { DEBUG_HTTPCLIENT("[HTTP-Client][begin] url: %s\n", url.c_str()); bool hasPort = false; @@ -148,25 +158,14 @@ bool HTTPClient::begin(String url) _host = host.substring(0, index); // hostname host.remove(0, (index + 1)); // remove hostname + : _port = host.toInt(); // get port - hasPort = true; } else { _host = host; } _uri = url; - - if(_protocol.equalsIgnoreCase("http")) { - if(!hasPort) { - _port = 80; - } - } else if(_protocol.equalsIgnoreCase("https")) { - if(!hasPort) { - _port = 443; - } - } else { - DEBUG_HTTPCLIENT("[HTTP-Client][begin] protocol: %s unknown?!\n", _protocol.c_str()); + if (_protocol != expectedProtocol) { + DEBUG_HTTPCLIENT("[HTTP-Client][begin] unexpected protocol: %s, expected %s\n", _protocol.c_str(), expectedProtocol); return false; } - _transportTraits = TransportTraitsPtr(new TransportTraits()); DEBUG_HTTPCLIENT("[HTTP-Client][begin] host: %s port: %d url: %s\n", _host.c_str(), _port, _uri.c_str()); return true; } @@ -785,7 +784,7 @@ bool HTTPClient::connect(void) } if (!_transportTraits) { - DEBUG_HTTPCLIENT("[HTTP-Client] _transportTraits is null (HTTPClient::begin not called?)\n"); + DEBUG_HTTPCLIENT("[HTTP-Client] connect: HTTPClient::begin was not called or returned error\n"); return false; } diff --git a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h index 1f5ef7fbc..1619a3b58 100644 --- a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h +++ b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h @@ -186,6 +186,7 @@ protected: String value; }; + bool beginInternal(String url, const char* expectedProtocol); void clear(); int returnError(int error); bool connect(void); From edaae2c19417e038c5ed12fc692b013d9c7c7a2e Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 25 Apr 2016 21:03:39 +0800 Subject: [PATCH 42/46] HTTPClient: fix default port not being set --- libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp index af2386e86..3ce4c16de 100644 --- a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp +++ b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp @@ -98,6 +98,7 @@ void HTTPClient::clear() bool HTTPClient::begin(String url, String httpsFingerprint) { _transportTraits.reset(nullptr); + _port = 443; if (httpsFingerprint.length() == 0) { return false; } @@ -116,6 +117,7 @@ bool HTTPClient::begin(String url, String httpsFingerprint) bool HTTPClient::begin(String url) { _transportTraits.reset(nullptr); + _port = 80; if (!beginInternal(url, "http")) { return false; } From 76e322f2e7c726a489e7c5ee2c5e5aa350ee951e Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 26 Apr 2016 15:58:12 +0800 Subject: [PATCH 43/46] Fix handling of chunked transfer encoding (#1975) --- libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp index 3ce4c16de..28829742a 100644 --- a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp +++ b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp @@ -624,6 +624,13 @@ int HTTPClient::writeToStream(Stream * stream) break; } + // read trailing \r\n at the end of the chunk + char buf[2]; + auto trailing_seq_len = _tcp->readBytes((uint8_t*)buf, 2); + if (trailing_seq_len != 2 || buf[0] != '\r' || buf[1] != '\n') { + return returnError(HTTPC_ERROR_READ_TIMEOUT); + } + delay(0); } } else { From 334837533f4337929f7a8091261deed5561b2210 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 26 Apr 2016 16:00:46 +0800 Subject: [PATCH 44/46] Add missing virtual destructor in TransportTraits (#1944) --- libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp index 28829742a..2733e12e1 100644 --- a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp +++ b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp @@ -33,6 +33,10 @@ class TransportTraits { public: + virtual ~TransportTraits() + { + } + virtual std::unique_ptr create() { return std::unique_ptr(new WiFiClient()); From 33723a9b52a40ed10cdced4507dde63b42fa38ed Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 26 Apr 2016 16:02:07 +0800 Subject: [PATCH 45/46] Fix UdpContext::peek to return int (#1946) --- libraries/ESP8266WiFi/src/include/UdpContext.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/ESP8266WiFi/src/include/UdpContext.h b/libraries/ESP8266WiFi/src/include/UdpContext.h index f3b58f87e..c367492cd 100644 --- a/libraries/ESP8266WiFi/src/include/UdpContext.h +++ b/libraries/ESP8266WiFi/src/include/UdpContext.h @@ -213,7 +213,7 @@ public: return size; } - char peek() + int peek() { if (!_rx_buf || _rx_buf_offset == _rx_buf->len) return -1; From 234a8743c9533d1b12d7962802869fe68e83179f Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 26 Apr 2016 16:28:45 +0800 Subject: [PATCH 46/46] Fix NMI interrupt handler alignment as suggested at http://www.esp8266.com/viewtopic.php?f=6&t=4675&start=48#p46002 --- tools/sdk/ld/eagle.app.v6.common.ld | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/sdk/ld/eagle.app.v6.common.ld b/tools/sdk/ld/eagle.app.v6.common.ld index c731ece35..90d9fc18c 100644 --- a/tools/sdk/ld/eagle.app.v6.common.ld +++ b/tools/sdk/ld/eagle.app.v6.common.ld @@ -78,6 +78,8 @@ SECTIONS *(.sdata2.*) *(.gnu.linkonce.s2.*) *(.jcr) + . = ALIGN(4); + _Pri_3_HandlerAddress = ABSOLUTE(.); _data_end = ABSOLUTE(.); } >dram0_0_seg :dram0_0_phdr