Arduino Forum

Using Arduino => Networking, Protocols, and Devices => Topic started by: Philgaskin on Dec 09, 2011, 03:56 pm

Title: How to obtain the remote client IP address when using the Ethernet Shield
Post by: Philgaskin on Dec 09, 2011, 03:56 pm
This had baffled me for some time. There are many examples of how to do this in previous versions of the ID; however none appeared to work with v1.0 - so I thought I'd share my workaround.
I wanted to record the remote IP address of clients using my Arduino as a web server.

To make it work, I did the following:

I added the following lines to the end of the EthernetClient.cpp file:
uint8_t *EthernetClient::getRemoteIP(uint8_t remoteIP[])
{
  W5100.readSnDIPR(_sock, remoteIP);
  return remoteIP;
}


I then added the following line (under the virtual void stop(); line)to the EthernetClient.h file:
uint8_t *getRemoteIP(uint8_t RemoteIP[]);//adds remote ip address

Finally I used the following code in my sketch to access the remote IP:
client.getRemoteIP(rip); // where rip is defined as byte rip[] = {0,0,0,0 };

to display the IP in the serial monitor, I used:

for (int bcount= 0; bcount < 4; bcount++)
     {
        Serial.print(rip[bcount], DEC);
        if (bcount<3) Serial.print(".");
     }


I'm not sure that this is the most elegant way, but it works in the IDE v1.0
Title: Re: How to obtain the remote client IP address when using the Ethernet Shield
Post by: zoomkat on Dec 09, 2011, 06:50 pm
I tried your example, but the "rip" in the below arduino code needs to be declared some where per the compiler. Where/how is this done?

Code: [Select]
Serial.print(rip[bcount], DEC);
Title: Re: How to obtain the remote client IP address when using the Ethernet Shield
Post by: Philgaskin on Dec 09, 2011, 09:30 pm
Zoomcat, it's in the comments of the sketch code.
Basically you need to define rip as a byte array:

either byte rip[] = {0,0,0,0 }; which assigns it as 0.0.0.0 by default or byte rip[4]; which defines it as null before use.
Title: Re: How to obtain the remote client IP address when using the Ethernet Shield
Post by: zoomkat on Dec 09, 2011, 10:12 pm
Adding to some server code I already have below, I just get 0.0.0.0 printed to the serial monitor. Bottom is where your code is pasted in the .ccp and .h files. Such is the world of copy/paste. This is with the 1.0 IDE.

Code: [Select]

//zoomkat 12-8-11
//simple button GET with iframe code
//for use with IDE 1.0
//open serial monitor to see what the arduino receives
//use the \ slash to escape the " in the html
//address will look like http://192.168.1.102:84 when submited
//for use with W5100 based ethernet shields

#include <SPI.h>
#include <Ethernet.h>

byte rip[4];
//byte rip[] = {0,0,0,0};
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //physical mac address
byte ip[] = { 192, 168, 1, 102 }; // ip in lan
byte gateway[] = { 192, 168, 1, 1 }; // internet access via router
byte subnet[] = { 255, 255, 255, 0 }; //subnet mask
EthernetServer server(84); //server port

String readString;

//////////////////////

void setup(){

  pinMode(4, OUTPUT); //pin selected to control
  //start Ethernet
  Ethernet.begin(mac, ip, gateway, subnet);
  server.begin();

  //enable serial data print
  Serial.begin(9600);
  Serial.println("server LED test 1.0"); // so I can keep track of what is loaded
}

void loop(){
  // Create a client connection
  EthernetClient client = server.available();
  if (client) {
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();

        //read char by char HTTP request
        if (readString.length() < 100) {

          //store characters to string
          readString += c;
          //Serial.print(c);
        }

        //if HTTP request has ended
        if (c == '\n') {

          ///////////////
          Serial.println(readString); //print to serial monitor for debuging

for (int bcount= 0; bcount < 4; bcount++)
     {
        Serial.print(rip[bcount], DEC);
        if (bcount<3) Serial.print(".");
     }

Serial.println();
          //now output HTML data header
             if(readString.indexOf('?') >=0) { //don't send new page
               client.println("HTTP/1.1 204 Zoomkat");
               client.println();
               client.println(); 
             }
             else {
          client.println("HTTP/1.1 200 OK"); //send new page
          client.println("Content-Type: text/html");
          client.println();

          client.println("<HTML>");
          client.println("<HEAD>");
          client.println("<TITLE>Arduino GET test page</TITLE>");
          client.println("</HEAD>");
          client.println("<BODY>");

          client.println("<H1>Zoomkat's simple Arduino button</H1>");
         
          client.println("<a href=\"/?on\" target=\"inlineframe\">ON</a>");
          client.println("<a href=\"/?off\" target=\"inlineframe\">OFF</a>");

          //client.println("<IFRAME name=inlineframe src=\"res://D:/WINDOWS/dnserror.htm\" width=1 height=1\">");
          client.println("<IFRAME name=inlineframe style=\"display:none\" >");         
          client.println("</IFRAME>");

          client.println("</BODY>");
          client.println("</HTML>");
             }

          delay(1);
          //stopping client
          client.stop();

          ///////////////////// control arduino pin
          if(readString.indexOf("on") >0)//checks for on
          {
            digitalWrite(4, HIGH);    // set pin 4 high
            Serial.println("Led On");
          }
          if(readString.indexOf("off") >0)//checks for off
          {
            digitalWrite(4, LOW);    // set pin 4 low
            Serial.println("Led Off");
          }
          //clearing string for next read
          readString="";

        }
      }
    }
  }
}


EthernetClient.h file:

Code: [Select]
  virtual void flush();
  virtual void stop();
  uint8_t *getRemoteIP(uint8_t RemoteIP[]);//adds remote ip address
  virtual uint8_t connected();
  virtual operator bool();


EthernetClient.cpp file:

Code: [Select]

// the next function allows us to use the client returned by
// EthernetServer::available() as the condition in an if-statement.

EthernetClient::operator bool() {
  return _sock != MAX_SOCK_NUM;
}
uint8_t *EthernetClient::getRemoteIP(uint8_t remoteIP[])
{
W5100.readSnDIPR(_sock, remoteIP);
return remoteIP;
}
Title: Re: How to obtain the remote client IP address when using the Ethernet Shield
Post by: Philgaskin on Dec 11, 2011, 11:15 pm
Zoomcat,

You've not put the client.getRemoteIP(rip); line in to actually read the remote IP prior to printing it.

client.getRemoteIP(rip); // this is the code that actually puts the IP into the variable rip
for (int bcount= 0; bcount < 4; bcount++)
     {
        Serial.print(rip[bcount], DEC);
        if (bcount<3) Serial.print("."); else Serial.println();
     }
Title: Re: How to obtain the remote client IP address when using the Ethernet Shield
Post by: samlro2 on Dec 12, 2011, 01:17 am
I am a super newbee. I am not sure where to connected the ethernet cable if I want to get remote data from home. The router?

Title: Re: How to obtain the remote client IP address when using the Ethernet Shield
Post by: zoomkat on Dec 12, 2011, 01:53 am
Added the client.getRemoteIP(rip); line, and voila!, it works.
Title: Re: How to obtain the remote client IP address when using the Ethernet Shield
Post by: Targettio on Dec 14, 2011, 10:24 am

I am a super newbee. I am not sure where to connected the ethernet cable if I want to get remote data from home. The router?



samlro2, your question isn't directly related to this topic, but I will help you out.

If you want to connect to your Arduino via a network or internet you will need to plug it into a router.

Then, from within the network, you can access the arduino simply by entering the arduino's IP into a browser (the IP you set within the sketch).

Connecting via the internet, things get a little more complicated:
* First you will need to set up your router so it routes all port 80 requests to the arduino. google port forwarding and your router model.
* Second you will need some way of contacting your router from the internet, your external IP address is likely to vary, so the best way is to get a domain name. google about dynamic domain name providers (i.e. dyndns, no-ip etc).

There are a few guides that go into a lot more detail and give full sketches. Google should be able to help there.
Title: Re: How to obtain the remote client IP address when using the Ethernet Shield
Post by: grossman on Sep 13, 2013, 07:29 am
I spend a lot of time to find a solution of error
Code: [Select]
no matching function for call to 'EthernetClient::getRemoteIP(byte* [4])'

The right way is to replace the line of code
Code: [Select]
byte *rip[4];
with
Code: [Select]
uint8_t rip[4];

Really thanks for the new Ethernet library, but why is the example wrong?

So there is an error on the 164 line in file EthernetClient.cpp too, the line must be
Code: [Select]

uint8_t *EthernetClient::getRemoteIP(uint8_t *remoteIP)


Could this solve your problem?
Title: Re: How to obtain the remote client IP address when using the Ethernet Shield
Post by: zoomkat on Sep 13, 2013, 07:33 am
Discussion that might be of interest.

http://forum.arduino.cc/index.php/topic,82416.0.html
Title: Re: How to obtain the remote client IP address when using the Ethernet Shield
Post by: karlok on Oct 28, 2014, 12:56 am
Even if this discussion is old, it is still relevant.

Improvement:

EthernetClient.cpp
Code: [Select]
IPAddress EthernetClient::getRemoteIP()
{
  byte rip[4];
  W5100.readSnDIPR(_sock, rip);
  return rip;
}


EthernetClient.h
Code: [Select]
IPAddress getRemoteIP(); // get Remote IP address

Reason:
You don't need to use some pass by reference and use return combined, that is redundant.
Here you just use return value.

Example:
Code: [Select]
  EthernetClient client = server.available();

  if (client)
  {
    Serial.println("new client");
   
    IPAddress remoteip = client.getRemoteIP();
Serial.print("IP: ");
    Serial.println(remoteip);
    client.stop();
}



Regards.
Title: Re: How to obtain the remote client IP address when using the Ethernet Shield
Post by: giampiero7 on Mar 27, 2015, 01:49 pm
@karlok returning a pointer declared in the local scope of a function is not the right thing to do.
The "pass by reference" method is the right one.
Title: Re: How to obtain the remote client IP address when using the Ethernet Shield
Post by: SurferTim on Mar 27, 2015, 01:51 pm
@karlok returning a variable declared in the local scope of a function is not the right thing to do.
The "pass by reference" method is the right one.
+1
Title: Re: How to obtain the remote client IP address when using the Ethernet Shield
Post by: AWOL on Mar 27, 2015, 02:20 pm
Or you could qualify the local "rip" as "static".
This isn't ideal either, though.
Title: Re: How to obtain the remote client IP address when using the Ethernet Shield
Post by: hilltop on Oct 24, 2015, 03:17 am
Why is this code not incorporated into the Ethernet library?  It's very short but quite helpful!
Title: Re: How to obtain the remote client IP address when using the Ethernet Shield
Post by: pert on Nov 01, 2015, 09:04 am
Why is this code not incorporated into the Ethernet library?  It's very short but quite helpful!
See: https://github.com/arduino/Arduino/pull/1700 (https://github.com/arduino/Arduino/pull/1700)
As for why was this function not accepted in the pull request, I've read the associated discussion but still don't understand. I get that compatibility with the Client API is essential but why can't additional functions be added? If you want an easy way to add the remoteIP() function to the stock Ethernet library then you might find this library useful: https://github.com/Chris--A/EthernetClientEx (https://github.com/Chris--A/EthernetClientEx)
Note that remoteIP() in the pull request and the EthernetClientEx library as well as EthernetUDP all use an IPAddress return type instead of the "pass by reference" method.
Title: Re: How to obtain the remote client IP address when using the Ethernet Shield
Post by: fxfever on Mar 02, 2016, 09:20 pm
Hi! :)

So is it to be understood,that it is still not possible to use the remoteIP() in official releases on incoming TCP connections, and somewhere someone is still pondering if it is I good idea to implement it, but no one can tell where it lands?

And that there are ways to implement it yourself, but it was loosely discussed which way was best and that didn't got settled either ?

I'm puzzled !

Best regards
Title: Re: How to obtain the remote client IP address when using the Ethernet Shield
Post by: pert on Mar 02, 2016, 10:02 pm
So is it to be understood,that it is still not possible to use the remoteIP() in official releases on incoming TCP connections
Correct, you need to either modify your Ethernet library(as explained in this thread), install+use EthernetClientEx (https://github.com/Chris--A/EthernetClientEx), or install a library that's already modified for you (https://github.com/per1234/EthernetMod).

, and somewhere someone is still pondering if it is I good idea to implement it, but no one can tell where it lands?
From the discussion on the attempt to add this function to the stock Ethernet library it seems it was definitely decided against, though I don't understand why. Of course it's been a while so it might be reconsidered if someone brought it up with good reasoning why the previous decision is wrong.

there are ways to implement it yourself, but it was loosely discussed which way was best and that didn't got settled either ?
I use the IPAddress return type because that's how it was done in the official EthernetUDP remoteIP() and is more user friendly but the "pass by reference" method will work fine also.
Title: Re: How to obtain the remote client IP address when using the Ethernet Shield
Post by: fxfever on Mar 04, 2016, 01:11 pm
Thanx for the sum up !
Must do the work around then - since I have no idea how to get anyone to consider anything around here ! :)
Title: Re: How to obtain the remote client IP address when using the Ethernet Shield
Post by: krasimir on Dec 27, 2017, 05:48 pm
Add this to your sketch (do not change Ethernet library files):


extern "C" {
 #include "utility/w5100.h"
}

void getRemoteIP(EthernetClient *client, uint8_t *ptRemoteIP)
{
W5100.readSnDIPR(client->getSocketNumber(), ptRemoteIP);
}

..
uint8_t remoteIP[4];
getRemoteIP(client,remoteIP);
..
Title: Re: How to obtain the remote client IP address when using the Ethernet Shield
Post by: Banjo5 on May 19, 2018, 06:23 pm
Thanks for this, it seems like a lot more civilized way of reaching in to see the remote IP address.
The get the result out I had to use:

getRemoteIP(&client,remoteIP);

rather than

getRemoteIP(client_1,remoteIP);


Add this to your sketch (do not change Ethernet library files):


extern "C" {
 #include "utility/w5100.h"
}

void getRemoteIP(EthernetClient *client, uint8_t *ptRemoteIP)
{
W5100.readSnDIPR(client->getSocketNumber(), ptRemoteIP);
}

..
uint8_t remoteIP[4];
getRemoteIP(client,remoteIP);
..
Title: Re: How to obtain the remote client IP address when using the Ethernet Shield
Post by: Stephen_Kurth on Feb 09, 2019, 04:29 am
Hello all, this is my first post. I've been working to add this remote IP functionality to my project. All the suggestions here and elsewhere have not worked. My project uses an UNO with an Ethernet2 shield. The original Ethernet libray worked fine except for the remote IP functionality. So, I dowmloaded the Ethernet2 library from somewhere and it did not have any code pertaining to remote IP. So I started poking at it with a stick. I got it to work today, finally. Here's what I did:


Ethernet2 Library Modifications

Modifications made to two files:

...\Arduino\libraries\Ethernet2-1.0.4\src\ EthernetClient.cpp

I added the follow bit of code to the very end of the file:

   // Added by Stephen Kurth Feb 8, 2019
   // returns the remote IP address:
   IPAddress EthernetClient::getRemoteIP()
   {
      if (_sock >= MAX_SOCK_NUM) return IPAddress((uint32_t)0);
      uint8_t remoteIParray[4];
      SPI.beginTransaction(SPISettings(14000000, MSBFIRST, SPI_MODE0));
      w5500.readSnDIPR(_sock, remoteIParray);
      SPI.endTransaction();
      return IPAddress(remoteIParray);
   }

I originallly pasted a copy of this code from the same .cpp file in the original Ethernet Library. Had to change a bunch of stuff in it though to get it compile. The shown code compiled and worked.

...\Arduino\libraries\Ethernet2-1.0.4\src\ EthernetClient.h

I added this code to this file in this location:

   
  virtual bool operator!=(const EthernetClient& rhs) { return !this->operator==(rhs); };
  // added by Stephen Kurth Feb 8, 2019
  IPAddress getRemoteIP();

  friend class EthernetServer;

Most of the other lines start with virtual but I don't really know what it does. By that point I was just poking it with a stick and seeing if it would compile. It did, finally.

The code in my Arduino sketch is:

  client = server.available();  // try to get client

  if (client) // got client?
  {
    IPAddress remoteIp = client.getRemoteIP();
    Serial.print(F("Remote IP: "));
    Serial.println(remoteIp);
      boolean currentLineIsBlank = true;
      while (client.connected())
      {
          if (client.available()) // client data available to read
          {

I made all the library edits in Visual Studio 2017.

I just thought I'd share in case anyone else is trying to do this with the same hardware.
Title: Re: How to obtain the remote client IP address when using the Ethernet Shield
Post by: pert on Feb 09, 2019, 06:02 pm
The original Ethernet libray worked fine except for the remote IP functionality.
What problem did you encounter with the remote IP functionality?
Title: Re: How to obtain the remote client IP address when using the Ethernet Shield
Post by: Stephen_Kurth on Feb 10, 2019, 04:53 pm
Hello pert,

To be honest, I can't really say why none of the suggestions were working for me... It is entirely probable that I was overlooking something, or made a mistake somewhere... In hindsight, I should have paid more attention to the compiler errors. Having edited the Ethernet 2 library to add the remote IP function, I suppose I could've done the same for the original library...

However, I am using the Ethernet 2 shield in my project so I thought it best to concentrate on a library written specifically for the w5500 chip. I had run into this issue with another sketch (did not involve remote IP) where the original Ethernet library did not work but the Ehternet 2 library did ...

As I am rethinking this, I wonder if I could've added a w5500.h utility file to the original library?..

One thing, among many, concerned me with the original Ethernet library. The EthernetClient.h file has nothing in it except an include line: #include "Ethernet.h". That's it. I don't know if that's an issue or not but the same file in the Ethernet 2 library has a ton more stuff in it.

BTW, I downloaded the Ethernet 2 library from https://www.arduinolibraries.info/libraries/ethernet2

Sorry I can't give you a better answer, I'm just a guy poking at this with a stick. I'm amazed that I actually got this to work.
Title: Re: How to obtain the remote client IP address when using the Ethernet Shield
Post by: Stephen_Kurth on Feb 10, 2019, 05:20 pm
hello again, pert

I just remembered... I believe the compiler error I was getting with the original Ethernet library was something like: EthernetClicent:: no function remoteIP() to call ... or something to that effect..


Stephen
Title: Re: How to obtain the remote client IP address when using the Ethernet Shield
Post by: Stephen_Kurth on Feb 10, 2019, 05:51 pm
So I just swapped out the Ethernet2 library with the original Ethernet library and tried to compile my sketch. The error I get is:

'class EthernetClient' has no member named 'getRemoteIP'

Looking at the EthernetClient.cpp file I found that I was calling the wrong function. The function name is "remoteIP()" not "getRemoteIP()". There is no function named getRemoteIP(), which is what the compiler error message says! I wonder if i'd gotten mixed up somewhere along the line with the function name and the name of the instance of the EthernetClient class used to call that function?...

So I changed the sketch code to call remoteIP() and it compiled!

A classic beginner mistake I guess... but I did learn a lot about library files!

Stephen

Title: Re: How to obtain the remote client IP address when using the Ethernet Shield
Post by: Stephen_Kurth on Feb 10, 2019, 06:29 pm
So, there's a silver lining here to my blunder...

Since I got my sketch to work with the modified Ethernet2 library which is in my original post, my sketch has grown larger.

So having discovered this morning the that original Ethernet library remoteIP() does indeed work, I further discovered that my sketch is then too large for an UNO! So I swapped the EThernet2 library, with my mods, back in and it fit into the UNO at 93% of program storage space.

Same sketch, different Ethernet libraries.  I compiled my sketch with the Ethernet2 library for a Mega and it compiled just fine.

So, the silver lining is that all the bumbling about modifying the EThernet2 library was useful in the end because it all fits into my UNO...

Stephen
Title: Re: How to obtain the remote client IP address when using the Ethernet Shield
Post by: pert on Feb 12, 2019, 02:08 am
However, I am using the Ethernet 2 shield in my project so I thought it best to concentrate on a library written specifically for the w5500 chip. I had run into this issue with another sketch (did not involve remote IP) where the original Ethernet library did not work but the Ehternet 2 library did ...

As I am rethinking this, I wonder if I could've added a w5500.h utility file to the original library?..
The latest 2.0.0 release of the official Arduino Ethernet library has been updated to automatically support the W5100, W5200, and W5500. Previous versions of the library did only work with the W5100 and that was why the Ethernet2 library was created.

One thing, among many, concerned me with the original Ethernet library. The EthernetClient.h file has nothing in it except an include line: #include "Ethernet.h". That's it. I don't know if that's an issue or not but the same file in the Ethernet 2 library has a ton more stuff in it.
That was an intentional decision by the person who updated the library. There was some discussion of it at the time which explained the reasoning, but I didn't find it after a quick search.

So I changed the sketch code to call remoteIP() and it compiled!

A classic beginner mistake I guess... but I did learn a lot about library files!
Good job finding the issue, and learning along the way!

So having discovered this morning the that original Ethernet library remoteIP() does indeed work, I further discovered that my sketch is then too large for an UNO! So I swapped the EThernet2 library, with my mods, back in and it fit into the UNO at 93% of program storage space.

Same sketch, different Ethernet libraries.  I compiled my sketch with the Ethernet2 library for a Mega and it compiled just fine.
Yes, this is the unfortunate thing about the updated Ethernet library. The increase in memory usage is pretty significant. I think some of that extra memory is used for the automatic detection of which Wiznet chip is being used. This change was really needed because it was creating a lot of problems by people assuming that the Ethernet library provided by Arduino would work with the W5500-based official products they were selling, but there are still a lot of people using the W5100 who expect the Ethernet library to continue to work for them. The person who updated the library definitely knows there stuff and I'm confident that the new code was written to the highest of standards but if it means the sketch will no longer fit in your Uno, none of that really matters. I do recommend that people use the Ethernet library if possible, as there is a lot of improvements in it beyond just the W5x00 support when compared with the Ethernet2 library. However, the Ethernet2 library should also work when you need it.