How to select which network interface to use in a ESP32 - wifi

I'm currently using a Esp32 which presents, in addition to wifi, an ethernet interface.
I'm using the esp-idf v3.3 with FreeRTOS.
To use it I included the "esp_eth.h" library (https://docs.espressif.com/projects/esp-idf/en/release-v3.1/api-reference/ethernet/esp_eth.html#api-reference-phy-lan8720).
I'd like to use both Wifi and ethernet interfaces basing on what I want to do but selecting which one to use, is there a way?
The practical use is to receive a command through the ethernet interface (for example a site to ping), ping the server through the wifi interface and answer back to the ethernet interface the ping result.
How can I select which interface to use (i dont want that the ping is made through the ethernet or that the response go through the wifi).
#edit: i found the method netif_set_default(struct netif * netif), but i dont know if it is the best way to select the interface to use for the specific action(i should swap from one interface to another)

Just get the IP of the Ethernet interface, then bind a socket to that IP address.
struct in_addr iaddr = {0};
#if USE_ANY_IF
// Bind the socket to any address
iaddr.s_addr = htonl(INADDR_ANY);
#else
// bind only to the Ethernet interface
tcpip_adapter_ip_info_t ip_info = {0};
tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_ETH, &ip_info);
inet_addr_from_ip4addr(&iaddr, &ip_info.ip);
#endif
Then use it to bind a socket, for example:
static int create_multicast_ipv4_socket(struct in_addr bind_iaddr)
{
struct sockaddr_in saddr = {0};
int sock = -1;
int err = 0;
char addrbuf[32] = {0};
sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
if (sock < 0)
{
ESP_LOGE(V4TAG, "Failed to create socket. Error %d", errno);
return -1;
}
saddr.sin_addr.s_addr = bind_iaddr.s_addr; // what interface IP to bind to. Can be htonl(INADDR_ANY)
saddr.sin_family = PF_INET;
saddr.sin_port = htons(UDP_PORT);
inet_ntoa_r(saddr.sin_addr.s_addr, addrbuf, sizeof(addrbuf) - 1);
ESP_LOGI(TAG, "Binding to interface %s...", addrbuf);
err = bind(sock, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in));
if (err < 0)
{
ESP_LOGE(V4TAG, "Failed to bind socket. Error %d", errno);
goto err;
}
// Assign multicast TTL (set separately from normal interface TTL)
uint8_t ttl = MULTICAST_TTL;
setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(uint8_t));
if (err < 0)
{
ESP_LOGE(V4TAG, "Failed to set IP_MULTICAST_TTL. Error %d", errno);
goto err;
}
// All set, socket is configured for sending
return sock;
err:
close(sock);
return -1;
}

Related

How do I initialize LwIP to use MQTT library

I've looked and followed documentation from LwIP 2.0.2. My project contains the MQTT library supplied in the LwIP 2.0.2. I also found some help at https://dzone.com/articles/mqtt-with-lwip-and-the-nxp-frdm-k64f. I'm unable to get a connection, I've used free public MQTT broker but no hope in connection. I can ping my device. Am I doing something wrong?
static void mqtt_test(mqtt_client_t *mqtt_client)
if (mqtt_client_is_connected(&mqtt_client) == 1)
{
example_subscribe(&mqtt_client, NULL);
} else {
mqtt_do_connect(&mqtt_client);
}
}
when I call this method it always enters mqtt_do_connect() never connecting. Here is mqtt_do_connect
static void mqtt_do_connect(mqtt_client_t *mqtt_client)
{
ip4_addr_t broker_ipaddr;
struct mqtt_connect_client_info_t ci;
err_t err;
if (ipaddr_aton("52.58.177.181", &broker_ipaddr))
{
err = ERR_OK;
}
/* Setup an empty client info structure */
memset( & ci, 0, sizeof(ci));
/* Minimal amount of information required is client identifier, so set it here */
ci.client_id = "test";
/* Initiate client and connect to server, if this fails immediately an error code is returned
otherwise mqtt_connection_cb will be called with connection result after attempting
to establish a connection with the server.
For now MQTT version 3.1.1 is always used */
err = mqtt_client_connect(mqtt_client, & broker_ipaddr, 1883, mqtt_connection_cb, 0, & ci);
/* For now just print the result code if something goes wrong */
if (err != ERR_OK) {
printf("mqtt_connect return %d\n", err);
}
}
I've also noticed in the method mqtt_client_connect in mqtt.c that this exists:
/* Any local address, pick random local port number */
err = tcp_bind(client->conn, IP_ADDR_ANY, 0);
why is this needed? If I replace IP_ADDR_ANY to my local address of the static IP of the device it runs through without throwing a error but no callback mqtt_connection_cb is then called.
I've also initialized the TCP/IP stack as mentioned with static IP. I'm using NO_SYS as 1 but will move it over to FreeRTOS but baby steps first.
I haven't found much support on MQTT in LwIP implementation, am I missing something obvious, any help will be appreciated.
I've used MQTTfx to run a test or two on the broker with good response but nothing from my embedded device (Atmel SAME54).
I've found the solution. I ran my TCP setup in a FreeRTOS thread and opened a socket
static void mqtt_start(void *p)
{
struct sockaddr_in address;
int s_create, new_socket;
int addrlen = sizeof(address);
int opt = 1;
int socket_check;
sys_sem_t sem;
err_t err_sem;
err_sem = sys_sem_new(&sem, 0); /* Create a new semaphore. */
tcpip_init(tcpip_init_done, &sem);
sys_sem_wait(&sem); /* Block until the lwIP stack is initialized. */
sys_sem_free(&sem); /* Free the semaphore. */
/*Create a socket*/
s_create = socket(AF_INET, 1, 0);
setsockopt(s_create, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
address.sin_family = AF_INET;
address.sin_addr.s_addr = htonl(IPADDR_ANY);
address.sin_port = htons(HTTP_PORT);
/* bind the connection to port */
socket_check = bind(s_create, (struct sockaddr *)&address, sizeof(address));
if (socket_check < 0) {
LWIP_DEBUGF(LWIP_DBG_ON, ("Bind error=%d\n", socket_check));
goto socket_close;
}
/* tell the connection to listen for incoming connection requests */
listen(s_create, 3);
mqtt_connect(&mqtt_client);
for (;;) {
new_socket = accept(s_create, (struct sockaddr *)&address, (socklen_t *)&addrlen);
socket_close:
close(new_socket);
}
}
I also had a problem with my initialization of my mqtt client, placed a watch on it and saw the struct wasn't initializing. I initialized it globally
mqtt_client_t mqtt_client;
Then use it later in mqtt_start.

Hook iOS BSD socket connect method to get local and remote port

I tried to hook iOS socket connect method to get the information of connected ports between the local host and remote server.
However, from the socket struct sockaddr, the sa_family is always AF_SYSTEM(kernel event message).
Hood code:
int (*origin_connect)(int socket, const struct sockaddr *address, socklen_t address_len);
int replaced_connect(int socket, const struct sockaddr *address, socklen_t address_len) {
int r = origin_connect(socket, address, address_len);
sa_family_t f = address->sa_family;
NSLog(#"CONNECT FAMILY %d", f);
if (f == AF_INET) {
struct sockaddr_in *addr = (struct sockaddr_in *)address;
NSString *remote_ip = [[NSString alloc]initWithCString:inet_ntoa(addr->sin_addr) encoding:NSUTF8StringEncoding];
uint16_t remote_port = ntohs(addr -> sin_port);
NSLog(#"The CONNECT ip = %# port = %u", remote_ip, remote_port);
struct sockaddr local_address;
socklen_t addr_size = sizeof(local_address);
getsockname(socket, &local_address, &addr_size);
struct sockaddr_in *laddr = (struct sockaddr_in*)&local_address;
NSString *local_ip = [[NSString alloc]initWithCString:inet_ntoa(laddr->sin_addr) encoding:NSUTF8StringEncoding];
uint16_t local_port = ntohs(laddr->sin_port);
NSLog(#"The CONNECT Local ip = %# port = %u", local_ip, local_port);
} else if (f == AF_SYSTEM) {
NSLog(#"hello there :(");
struct sockaddr_ctl * ctl = (struct sockaddr_ctl *)address;
}
return r;
}
Did I hook wrong method or is there any other way to get the connected ports information?
You're getting AF_SYSTEM hooked because when apps start, they also call on a system socket. If you allow your hook to just ignore that, the next calls should intercept TCP/IP Sockets.
There are, by the way, much better ways to do that. You can externally get information of connected sockets and even notifications, by using com.apple.network.statistics. A full sample of how to do so is at http://newosxbook.com/src.jl?tree=listings&file=lsock.c

How to Get Host Name connected to a Socket using CFSocketNativeHandle iOS?

I am using Socket programming to connect devices each other in iOS.
I want to get connected device's Host Name which is connected to a socket using CFSocketNativeHandle.
When other device connected to my Socket i get callback in following callback function:
static void serverAcceptCallback(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *info)
{
// We can only process "connection accepted" calls here
if ( type != kCFSocketAcceptCallBack )
{
return;
}
// for an AcceptCallBack, the data parameter is a pointer to a CFSocketNativeHandle
CFSocketNativeHandle nativeSocketHandle = *(CFSocketNativeHandle*)data;
uint8_t name[SOCK_MAXADDRLEN];
socklen_t namelen = sizeof(name);
NSData *peer = nil;
if (0 == getpeername(nativeSocketHandle, (struct sockaddr *)name, &namelen)) {
peer = [NSData dataWithBytes:name length:namelen];
NSString *hostName = [[NSString alloc] initWithData:peer encoding:NSUTF8StringEncoding];
NSLog(#"HostName=%#",hostName);
}
}
Here I am getting NSData for "peer" but i am getting NSString *hostName=null.
How can i get exact Host name with this NSData.
getpeername() returns the address of the peer connected to the socket. You can access it using code below:
struct sockaddr_in addr ;
socklen_t len = sizeof(addr) ;
int res = getpeername(nativeSocketHandle, (struct sockaddr *)&addr, &len) ;
if (res == 0) {
char strAddr[20] ;
inet_ntop(AF_INET, &addr.sin_addr, strAddr, sizeof(strAddr)) ;
printf("ip %s\n", strAddr) ;
}
The only thing the socket knows is the IP address of the other end of the connection, so when you use getpeername it returns you the IP address. You will need to look up the IP address using either CFhost or POSIX calls - there are a squillion references to how to accomplish this using the POSIX APIs.
There is a very good chance that the other end will not be the same as the name of the device.

WinSock UDP socket creation order in loopback.

I have a network application meant for a private LAN. I am doing my testing using loopback. When I test on the LAN the socket creation order does not matter. If I test using loop back 127.0.0.1 then there is a socket creation ordering issue. Why is it different on loop back?
Here are more details...
There is one server, and many client instances. The server is broad casting data over UDP. The clients receive the data and process it.
I need to have the network layer not care about the order in which either the server or clients start. It is hard to administer process creation for my case. The application instances should be able to start on the network in any order and just see the data broadcasted on the UDP port when it is sent.
But there is something in the way I setting up my UDP sockets which is forcing ordering to take place. I must start the clients, THEN start the server. If I start the clients AFTER the server doing the UDP broadcast, the client sockets do not receive the data. If I force a running server instance to tear down and rebuild its UDP socket, suddenly all the clients start receiving data.
There must be something wrong with how I creating the socket. The client and server code use a shared function library to make the UDP socket. So the server is sending on m_fdOut. Each instance of the client is receiving on m_fdIn.
What am I doing wrong here?
SOCKET m_fdIn;
SOCKET m_fdOut;
if ((m_fdIn = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
{
WARNF("socket failed, winsock error %d\n", WSAGetLastError());
exit(1);
}
if ((m_fdOut = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
{
WARNF("socket failed, winsock error %d\n", WSAGetLastError());
exit(1);
}
int sockopt = 1;
if (setsockopt(m_fdOut, SOL_SOCKET, SO_BROADCAST, (char *)&sockopt,
sizeof(sockopt)) < 0)
{
WARNF("setsockopt failed, winsock error %d\n", WSAGetLastError());
exit(1);
}
sockopt = readPreference<int>("SOL_RCVBUF", 512*1024);
if (setsockopt(m_fdIn, SOL_SOCKET, SO_RCVBUF, (char *)&sockopt, sizeof(sockopt)) < 0)
{
WARNF("setsockopt failed, winsock error %d\n", WSAGetLastError());
exit(1);
}
sockopt = 1;
if (setsockopt(m_fdIn, SOL_SOCKET, SO_REUSEADDR, (char *)&sockopt, sizeof(sockopt)) < 0)
{
WARNF("setsockopt failed, winsock error %d\n", WSAGetLastError());
exit(1);
}
sockopt = readPreference<int>("IP_MULTICAST_TTL", 32);
if (setsockopt(m_fdOut, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&sockopt, sizeof(sockopt)) < 0)
{
WARNF("setsockopt failed, winsock error %d\n", WSAGetLastError());
exit(1);
}
String destAdd = "255.255.255.255"
int portNumber = 1234;
int n1, n2, n3 ,n4;
if (sscanf(destAddr, "%d.%d.%d.%d", &n1, &n2, &n3, &n4) != 4)
{
n1 = n2 = n3 = n4 = 255;
}
u_long bcastAddr = (n1<<24) | (n2<<16) | (n3<<8) | n4;
outAddr.sin_family = AF_INET;
outAddr.sin_port = htons(portNumber);
outAddr.sin_addr.s_addr = htonl(bcastAddr);
struct sockaddr_in in_name;
in_name.sin_family = AF_INET;
in_name.sin_addr.s_addr = INADDR_ANY;
in_name.sin_port = htons(portNumber);
if (bind(m_fdIn, (struct sockaddr *)&in_name, sizeof(in_name)) < 0)
{
WARNF("bind failed, winsock error %d\n", WSAGetLastError());
exit(1);
}
So I did change the implementation from UDP broadcast to multicast. That seems to work in loopback so multiple processes can share the port.

Hops tracing ttl reciveform on ios

I'm trying to implement simple traceroute for the iOS. Everything seems to work fine, except that somehow when I run my application on simulator or on the device it finds only a few (6-7) first routers on the way when the CLI traceroute finds all 14 routers.
const char *c = "www.gmail.com";
struct hostent *host_entry = gethostbyname(c);
char *ip_addr;
ip_addr = inet_ntoa(*((struct in_addr *)host_entry->h_addr_list[0]));
struct sockaddr_in destination, fromAddr;
int recv_sock;
int send_sock;
// Creting Sockets///
if ((recv_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP)) <
0) // using UDP socket.
{
NSLog(#"Could not cretae recv_sock.\n");
}
if ((send_sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
NSLog(#"Could not cretae send_sock.\n");
}
memset(&destination, 0, sizeof(destination));
destination.sin_family = AF_INET;
destination.sin_addr.s_addr = inet_addr(ip_addr);
destination.sin_port = htons(80);
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 10000;
setsockopt(recv_sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,
sizeof(struct timeval));
char *cmsg = "GET / HTTP/1.1\r\n\r\n";
int max_ttl = 20;
int num_attempts = 5;
socklen_t n = sizeof(fromAddr);
char buf[100];
for (int ttl = 1; ttl <= max_ttl; ttl++) {
memset(&fromAddr, 0, sizeof(fromAddr));
if (setsockopt(send_sock, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) < 0)
NSLog(#"error in setsockopt\n");
for (int try = 0; try < num_attempts; try ++) {
if (sendto(send_sock, cmsg, sizeof(cmsg), 0,
(struct sockaddr *)&destination,
sizeof(destination)) != sizeof(cmsg))
NSLog(#"error in send to...\n#");
int res = 0;
if ((res = recvfrom(recv_sock, buf, 100, 0, (struct sockaddr *)&fromAddr,
&n)) < 0) {
NSLog(#"an error: %s; recvfrom returned %d\n", strerror(errno), res);
} else {
char display[16] = {0};
inet_ntop(AF_INET, &fromAddr.sin_addr.s_addr, display, sizeof(display));
NSLog(#"Received packet from%s for TTL=%d\n", display, ttl);
break;
}
}
}
I have tried to bind the send socket but have same results and I can't use Sock_raw on iOS. I tried to run it on my mac and got same results. The error I get is "Resource temporarily unavailable;" for the recvfrom(). Why is that? How can I fix it?
The EAGAIN error ( producing "Resource temporarily unavailable;" string) could be raised by the timeout of the receiving socket.
Since you set just 10000 microseconds as read timeout (that's really short IMHO) with this line...
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 10000;
setsockopt(recv_sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,sizeof(struct timeval));
... it's possibile that the longer the way (i mean the number of router you have to pass through), the more chance you have to incour in this situation.
Try to raise timeout value and let us know if it got better.
EDIT
I tried the source code under linux and i noticed two kind of problems.
As mentioned above: Timeouts
Problem with the 80 port
I just raised the timeout and used a port different than 80 (in my case i sent udp message to 40000 port) and i got back all the hops just like traceroute command.
I'm not sure why this behaviour occour. Maybe some kind of "possible malicious packet alarm" gets triggered by the router that discards it
FURTHER EDIT
Look at this link: man traceroute
In the List Of Available Methods section you can find many ways to achieve what you need. Your method is similar to the default one, stating:
Probe packets are udp datagrams with so-called "unlikely" destination ports. The "unlikely" port of the first probe is 33434, then for each next probe it is incremented by one. Since the ports are expected to be unused, the destination host normally returns "icmp unreach port" as a final response. (Nobody knows what happens when some application listens for such ports, though).
So, if you need to full emulate the behaviour of the common Linux traceroute you have to increase by 1 the destination port, everytime the TTL increase (or everytime you can't get a response IMHO)
MAYBE, sometimes your command doesn't work on certain ports because the router is listening to the latter (as suggested by Linux manual and underlined in bold by me).

Resources