Skip to content

Commit a85764b

Browse files
committed
WiFiServer IPv6 support and example, WiFiClient::remoteIP IPv6 support
This is one of useful examples of IPv6, such device can be reached from internet without any port forward on NAT and etc. Most of changes for WiFiServer is to use ip6 structures, and for ip4 we use ipv6-to-ipv4 mapped addresses (RFC 4291). For RemoteIP i added support ip4 to ip6 mapping as well. Scenarios tested: wifiMulti.IPv6(true); but set to listen on IPv4 only IPv6 disabled, with or without bind to specific IP4 AsyncUDPServer without IPv6 support, to check if remoteIP works properly v2: )Added RemoteIP6 option that return IPv6Address (compatible with IPv4 too) )Separate IPv6 example for WiFiTelnetToSerial (also compatible with IPv4) Signed-off-by: Denys Fedoryshchenko <[email protected]>
1 parent 4735f66 commit a85764b

File tree

6 files changed

+213
-15
lines changed

6 files changed

+213
-15
lines changed

cores/esp32/Client.h

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "Print.h"
2323
#include "Stream.h"
2424
#include "IPAddress.h"
25+
#include "IPv6Address.h"
2526

2627
class Client: public Stream
2728
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/*
2+
WiFiTelnetToSerial - Example Transparent UART to Telnet Server for ESP32
3+
4+
Copyright (c) 2017 Hristo Gochkov. All rights reserved.
5+
This file is part of the ESP32 WiFi library for Arduino environment.
6+
7+
This library is free software; you can redistribute it and/or
8+
modify it under the terms of the GNU Lesser General Public
9+
License as published by the Free Software Foundation; either
10+
version 2.1 of the License, or (at your option) any later version.
11+
12+
This library is distributed in the hope that it will be useful,
13+
but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+
Lesser General Public License for more details.
16+
17+
You should have received a copy of the GNU Lesser General Public
18+
License along with this library; if not, write to the Free Software
19+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20+
*/
21+
#include <WiFi.h>
22+
#include <WiFiMulti.h>
23+
24+
WiFiMulti wifiMulti;
25+
26+
//Even this example state IPv6, it is dual stack and compatible with IPv4 too
27+
28+
//how many clients should be able to telnet to this ESP32
29+
#define MAX_SRV_CLIENTS 1
30+
const char* ssid = "**********";
31+
const char* password = "**********";
32+
33+
WiFiServer server(23);
34+
WiFiClient serverClients[MAX_SRV_CLIENTS];
35+
36+
void setup() {
37+
Serial.begin(115200);
38+
Serial.println("\nConnecting");
39+
40+
wifiMulti.IPv6(true);
41+
wifiMulti.addAP(ssid, password);
42+
wifiMulti.addAP("ssid_from_AP_2", "your_password_for_AP_2");
43+
wifiMulti.addAP("ssid_from_AP_3", "your_password_for_AP_3");
44+
45+
Serial.println("Connecting Wifi ");
46+
for (int loops = 10; loops > 0; loops--) {
47+
if (wifiMulti.run() == WL_CONNECTED) {
48+
Serial.println("");
49+
Serial.print("WiFi connected ");
50+
Serial.print("IP address: ");
51+
Serial.println(WiFi.localIP());
52+
break;
53+
}
54+
else {
55+
Serial.println(loops);
56+
delay(1000);
57+
}
58+
}
59+
if (wifiMulti.run() != WL_CONNECTED) {
60+
Serial.println("WiFi connect failed");
61+
delay(1000);
62+
ESP.restart();
63+
}
64+
65+
//start UART and the server
66+
Serial1.begin(9600);
67+
server.begin();
68+
server.setNoDelay(true);
69+
70+
Serial.print("Ready! Use 'telnet ");
71+
Serial.print(WiFi.localIP());
72+
Serial.println(" 23' to connect");
73+
}
74+
75+
void loop() {
76+
uint8_t i;
77+
if (wifiMulti.run() == WL_CONNECTED) {
78+
//check if there are any new clients
79+
if (server.hasClient()){
80+
for(i = 0; i < MAX_SRV_CLIENTS; i++){
81+
//find free/disconnected spot
82+
if (!serverClients[i] || !serverClients[i].connected()){
83+
if(serverClients[i]) serverClients[i].stop();
84+
serverClients[i] = server.available();
85+
if (!serverClients[i]) Serial.println("available broken");
86+
Serial.print("New client: ");
87+
Serial.print(i); Serial.print(' ');
88+
Serial.println(serverClients[i].remoteIP6());
89+
break;
90+
}
91+
}
92+
if (i >= MAX_SRV_CLIENTS) {
93+
//no free/disconnected spot so reject
94+
server.available().stop();
95+
}
96+
}
97+
//check clients for data
98+
for(i = 0; i < MAX_SRV_CLIENTS; i++){
99+
if (serverClients[i] && serverClients[i].connected()){
100+
if(serverClients[i].available()){
101+
//get data from the telnet client and push it to the UART
102+
while(serverClients[i].available()) Serial1.write(serverClients[i].read());
103+
}
104+
}
105+
else {
106+
if (serverClients[i]) {
107+
serverClients[i].stop();
108+
}
109+
}
110+
}
111+
//check UART for data
112+
if(Serial1.available()){
113+
size_t len = Serial1.available();
114+
uint8_t sbuf[len];
115+
Serial1.readBytes(sbuf, len);
116+
//push UART data to all connected telnet clients
117+
for(i = 0; i < MAX_SRV_CLIENTS; i++){
118+
if (serverClients[i] && serverClients[i].connected()){
119+
serverClients[i].write(sbuf, len);
120+
delay(1);
121+
}
122+
}
123+
}
124+
}
125+
else {
126+
Serial.println("WiFi not connected!");
127+
for(i = 0; i < MAX_SRV_CLIENTS; i++) {
128+
if (serverClients[i]) serverClients[i].stop();
129+
}
130+
delay(1000);
131+
}
132+
}

libraries/WiFi/src/WiFiClient.cpp

+51-2
Original file line numberDiff line numberDiff line change
@@ -561,8 +561,52 @@ IPAddress WiFiClient::remoteIP(int fd) const
561561
struct sockaddr_storage addr;
562562
socklen_t len = sizeof addr;
563563
getpeername(fd, (struct sockaddr*)&addr, &len);
564-
struct sockaddr_in *s = (struct sockaddr_in *)&addr;
565-
return IPAddress((uint32_t)(s->sin_addr.s_addr));
564+
565+
// Old way, IPv4
566+
if (((struct sockaddr*)&addr)->sa_family == AF_INET) {
567+
struct sockaddr_in *s = (struct sockaddr_in *)&addr;
568+
return IPAddress((uint32_t)(s->sin_addr.s_addr));
569+
}
570+
// IPv6, but it might be IPv4 mapped address
571+
if (((struct sockaddr*)&addr)->sa_family == AF_INET6) {
572+
struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)&addr;
573+
if (IN6_IS_ADDR_V4MAPPED(saddr6->sin6_addr.un.u32_addr)) {
574+
struct sockaddr_in addr4;
575+
memset(&addr4, 0, sizeof(addr4));
576+
addr4.sin_family = AF_INET;
577+
addr4.sin_port = saddr6->sin6_port;
578+
memcpy(&addr4.sin_addr.s_addr, saddr6->sin6_addr.s6_addr+12, sizeof(addr4.sin_addr.s_addr));
579+
return IPAddress((uint32_t)(addr4.sin_addr.s_addr));
580+
}
581+
log_e("WiFiClient::remoteIP IPv6 to IPv4 cannot convert");
582+
return (IPAddress(0,0,0,0));
583+
}
584+
log_e("WiFiClient::remoteIP Not AF_INET or AF_INET6?");
585+
return (IPAddress(0,0,0,0));
586+
}
587+
588+
IPv6Address WiFiClient::remoteIP6(int fd) const
589+
{
590+
struct sockaddr_storage addr;
591+
socklen_t len = sizeof addr;
592+
getpeername(fd, (struct sockaddr*)&addr, &len);
593+
594+
// IPv4 socket we can print as IPv6 mapped
595+
if (((struct sockaddr*)&addr)->sa_family == AF_INET) {
596+
uint8_t buffer[16] = { 0 };
597+
buffer[12] = 0xFF;
598+
buffer[13] = 0xFF;
599+
struct sockaddr_in *s = (struct sockaddr_in *)&addr;
600+
memcpy(&buffer[14], (uint8_t*)s->sin_addr.s_addr, 4);
601+
return IPv6Address(buffer);
602+
}
603+
// IPv6
604+
if (((struct sockaddr*)&addr)->sa_family == AF_INET6) {
605+
struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)&addr;
606+
return (IPv6Address(saddr6->sin6_addr.s6_addr));
607+
}
608+
log_e("WiFiClient::remoteIP Not AF_INET or AF_INET6?");
609+
return (IPv6Address());
566610
}
567611

568612
uint16_t WiFiClient::remotePort(int fd) const
@@ -579,6 +623,11 @@ IPAddress WiFiClient::remoteIP() const
579623
return remoteIP(fd());
580624
}
581625

626+
IPv6Address WiFiClient::remoteIP6() const
627+
{
628+
return remoteIP6(fd());
629+
}
630+
582631
uint16_t WiFiClient::remotePort() const
583632
{
584633
return remotePort(fd());

libraries/WiFi/src/WiFiClient.h

+7
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ class WiFiClient : public ESPLwIPClient
9595

9696
IPAddress remoteIP() const;
9797
IPAddress remoteIP(int fd) const;
98+
IPv6Address remoteIP6() const;
99+
IPv6Address remoteIP6(int fd) const;
98100
uint16_t remotePort() const;
99101
uint16_t remotePort(int fd) const;
100102
IPAddress localIP() const;
@@ -106,4 +108,9 @@ class WiFiClient : public ESPLwIPClient
106108
using Print::write;
107109
};
108110

111+
#define IN6_IS_ADDR_V4MAPPED(a) \
112+
((((__const uint32_t *) (a))[0] == 0) \
113+
&& (((__const uint32_t *) (a))[1] == 0) \
114+
&& (((__const uint32_t *) (a))[2] == htonl (0xffff)))
115+
109116
#endif /* _WIFICLIENT_H_ */

libraries/WiFi/src/WiFiServer.cpp

+9-9
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ WiFiClient WiFiServer::available(){
4747
_accepted_sockfd = -1;
4848
}
4949
else {
50-
struct sockaddr_in _client;
51-
int cs = sizeof(struct sockaddr_in);
50+
struct sockaddr_in6 _client;
51+
int cs = sizeof(struct sockaddr_in6);
5252
#ifdef ESP_IDF_VERSION_MAJOR
5353
client_sock = lwip_accept(sockfd, (struct sockaddr *)&_client, (socklen_t*)&cs);
5454
#else
@@ -76,14 +76,14 @@ void WiFiServer::begin(uint16_t port, int enable){
7676
if(port){
7777
_port = port;
7878
}
79-
struct sockaddr_in server;
80-
sockfd = socket(AF_INET , SOCK_STREAM, 0);
79+
struct sockaddr_in6 server;
80+
sockfd = socket(AF_INET6 , SOCK_STREAM, 0);
8181
if (sockfd < 0)
8282
return;
8383
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
84-
server.sin_family = AF_INET;
85-
server.sin_addr.s_addr = _addr;
86-
server.sin_port = htons(_port);
84+
server.sin6_family = AF_INET6;
85+
server.sin6_addr = _addr;
86+
server.sin6_port = htons(_port);
8787
if(bind(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0)
8888
return;
8989
if(listen(sockfd , _max_clients) < 0)
@@ -106,8 +106,8 @@ bool WiFiServer::hasClient() {
106106
if (_accepted_sockfd >= 0) {
107107
return true;
108108
}
109-
struct sockaddr_in _client;
110-
int cs = sizeof(struct sockaddr_in);
109+
struct sockaddr_in6 _client;
110+
int cs = sizeof(struct sockaddr_in6);
111111
#ifdef ESP_IDF_VERSION_MAJOR
112112
_accepted_sockfd = lwip_accept(sockfd, (struct sockaddr *)&_client, (socklen_t*)&cs);
113113
#else

libraries/WiFi/src/WiFiServer.h

+13-4
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,14 @@
2323
#include "Server.h"
2424
#include "WiFiClient.h"
2525
#include "IPAddress.h"
26+
#include "lwip/inet.h"
27+
#include "lwip/netdb.h"
2628

2729
class WiFiServer : public Server {
2830
private:
2931
int sockfd;
3032
int _accepted_sockfd = -1;
31-
IPAddress _addr;
33+
in6_addr _addr;
3234
uint16_t _port;
3335
uint8_t _max_clients;
3436
bool _listening;
@@ -37,12 +39,19 @@ class WiFiServer : public Server {
3739
public:
3840
void listenOnLocalhost(){}
3941

40-
// _addr(INADDR_ANY) is the same as _addr() ==> 0.0.0.0
41-
WiFiServer(uint16_t port=80, uint8_t max_clients=4):sockfd(-1),_accepted_sockfd(-1),_addr(),_port(port),_max_clients(max_clients),_listening(false),_noDelay(false) {
42+
WiFiServer(uint16_t port=80, uint8_t max_clients=4):sockfd(-1),_accepted_sockfd(-1),_port(port),_max_clients(max_clients),_listening(false),_noDelay(false) {
4243
log_v("WiFiServer::WiFiServer(port=%d, ...)", port);
44+
_addr = IN6ADDR_ANY_INIT;
4345
}
44-
WiFiServer(const IPAddress& addr, uint16_t port=80, uint8_t max_clients=4):sockfd(-1),_accepted_sockfd(-1),_addr(addr),_port(port),_max_clients(max_clients),_listening(false),_noDelay(false) {
46+
WiFiServer(const IPAddress& addr, uint16_t port=80, uint8_t max_clients=4):sockfd(-1),_accepted_sockfd(-1),_port(port),_max_clients(max_clients),_listening(false),_noDelay(false) {
4547
log_v("WiFiServer::WiFiServer(addr=%s, port=%d, ...)", addr.toString().c_str(), port);
48+
char buffer[64] = { 0 };
49+
// Print IPv4 in IPv6 notation
50+
sprintf(buffer, "::FFFF:%s", addr.toString().c_str());
51+
int rc = inet_pton(AF_INET6, buffer, &_addr);
52+
if (rc != 1) {
53+
log_e("WiFiServer::WiFiServer unable to resolve address, rc=%d", rc);
54+
}
4655
}
4756
~WiFiServer(){ end();}
4857
WiFiClient available();

0 commit comments

Comments
 (0)