I'm trying to port my desktop app written in C and C++ to webassembly platform and am investigating if it is possible at all. One of important things the app does is communicate by sending and receiving UDP messages. I have implemented minimal UDP client which just creates UDP socket and sends packets to server (which is build natively and is running as separate executable at the same machine). socket, bind and sendto APIs return no error and everything looks working but no messages are receiving on server side and wireshark shows no activity on that port.
Is UDP socket just stubbed at webassembly libc port, or it is implemented on top of some web standard connection (e.g. WebRTC)?
The client code is below. I checked that native build is working properly.
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#define BUFLEN 512
#define NPACK 100
#define PORT 9930
void diep(char *s)
{
perror(s);
exit(1);
}
#define SRV_IP "127.0.0.1"
int main(void)
{
struct sockaddr_in si_other;
int s, i, slen=sizeof(si_other);
char buf[BUFLEN];
if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
diep("socket");
memset((char *) &si_other, 0, sizeof(si_other));
si_other.sin_family = AF_INET;
si_other.sin_port = htons(PORT);
if (inet_aton(SRV_IP, &si_other.sin_addr)==0) {
fprintf(stderr, "inet_aton() failed\n");
exit(1);
}
for (i=0; i<NPACK; i++) {
printf("Sending packet %d\n", i);
sprintf(buf, "This is packet %d\n", i);
if (sendto(s, buf, BUFLEN, 0, (struct sockaddr*)&si_other, slen)==-1)
diep("sendto()");
}
close(s);
return 0;
}
I followed instructions from http://webassembly.org/getting-started/developers-guide/ to build and run it.
Thanks in advance for any help or clues!
I found how UDP sockets are implemented at webassembly. Actually, they are emulated by websockets. It probably would work if both client and server were webassemblies, but my server is built natively. As wasm doesn't support dynamic linking, all the code (including libc implementation) is bundled to one JS file, were we can find UDP sendto implementation:
// if we're emulating a connection-less dgram socket and don't have
// a cached connection, queue the buffer to send upon connect and
// lie, saying the data was sent now.
if (sock.type === 2) {
if (!dest || dest.socket.readyState !== dest.socket.OPEN) {
// if we're not connected, open a new connection
if (!dest || dest.socket.readyState === dest.socket.CLOSING || dest.socket.readyState === dest.socket.CLOSED) {
dest = SOCKFS.websocket_sock_ops.createPeer(sock, addr, port);
}
dest.dgram_send_queue.push(data);
return length;
}
}
Anything that runs in the browser will not give you native socket access and I suspect that browser vendors would strongly object to any such access as it is a potential security violation.
Perhaps as more and more native applications move to the web as the performance difference shrinks due to webassembly and similar initiatives would make them change their stance, but until then, anything that wants direct socket control would have to remain a native app.
Related
Don't get confused by me talking about L2TP. Although my problem is related to L2TP it is not an L2TP problem per se. It's more of an networking problem.
Background
I'm writing an application working with L2TP. This is my first time working with L2TP and the linux L2TP subysytem, so I hope I got all this right.
When creating an L2TP Ethernet session the subsystem automatically creates a virtual network interface.
After bringing the interface up I can check with Wireshark and indeed the desired data is sent to the interface. This is without any packaging tho. It's not inside an ethernet frame or anything, but just the data bytes which were included in the L2TP packet.
I have no control over actually creating the device, but I can query its name and therefore its index etc., so so far so good.
The actual problem
My question is actually pretty simple: How do I get the data which is sent to a virtual interface into my userspace application?
I don't have a lot of experience with networking on unix but my expectation would be that this is a fairly simple problem, solvable by either obtaining an file descriptor with which I can use read / recv or somehow binding a socket to just that network interface.
I couldn't find any (gen-)netlink / ioctl API (or anything else) to do this or something comparable.
Although my application is written in GO not in C, a solution in C would be completely sufficient. Tbh at this point I would be happy about any approach to solve this issue programmatically. :)
Thanks a lot in advance
I just found a tutorial which answers my own question. It was actually really easy using AF_PACKET sockets.
There is a lovely tutorial on microhowto.info, which explains how AF_PACKET sockets work, better than I ever could. It even includes a section "Capture only from a particular network interface".
Here is a minimal example, which worked for my use case:
#include <stdlib.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <net/ethernet.h>
#include <linux/if_packet.h>
#include <sys/socket.h>
// [...]
// Create socket
int fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (fd == -1) {
perror("ERROR socket");
exit(1);
}
// Interface index (i.e. obtainable via ioctl SIOCGIFINDEX)
int ifindex = 1337;
// create link layer socket address
struct sockaddr_ll addr = {0};
addr.sll_family = AF_PACKET;
addr.sll_ifindex = ifindex;
addr.sll_protocol = htons(ETH_P_ALL)
if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
perror("ERROR bind");
exit(1);
}
char buffer[65535];
ssize_t len;
do {
len = recv(fd, buffer, sizeof(buffer) -1, 0);
if (len < 0) {
perror("ERROR recvfrom");
exit(1);
}
printf("recived data (length: %i)\n", (int) len);
} while (len > 0);
Before I get started. Yes, I could use leJOS, ev3dev, or some others, but I'd like to do it this way because that is how I learn.
I am using the CodeSourcery arm-2009q1 arm toolchain. I fetched the required libraries (bluetooth) from here: https://github.com/mindboards/ev3sources.
I am uploading the programs to the brick by using this tool: https://github.com/c4ev3/ev3duder
I have also fetched the brick's shared libraries, but I can not get them to work properly and there is 0 documentation on how to write a c program for the ev3 using the shared libraries. If I could get that working I might be able to use the c_com module to handle bluetooth, but right now bluez and rfcomm in conjunction with: https://github.com/c4ev3/EV3-API for motor and sensor control seems to be my best bet.
Now, with that out of the way:
I'd like to run the EV3 as a bluetooth "server" meaning that I start a program on it and the program opens a socket, binds it, listens for a connection, and then accepts a single connection.
I am able to do open a socket, bind it to anything but channel 1 (I believe this might be the crux of my issue), I am able to listen. These all return 0 (OK) and everything is fine.
Then I try to accept a connection. That instantly returns -1 and sets the remote to address 00:00:00:00:00:00.
My code is pretty much the same as can be found here: https://people.csail.mit.edu/albert/bluez-intro/x502.html
Here it is:
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/rfcomm.h>
#include <ev3.h>
int main(int argc, char **argv)
{
InitEV3();
struct sockaddr_rc loc_addr = { 0 }, rem_addr = { 0 };
char buf[1024] = { 0 };
int sock, client, bytes_read;
socklen_t opt = sizeof(rem_addr);
sock = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
loc_addr.rc_family = AF_BLUETOOTH;
loc_addr.rc_bdaddr = *BDADDR_ANY;
loc_addr.rc_channel = 2; // <-- Anything but 1. 1 seems to be taken
bind(sock, (struct sockaddr *)&loc_addr, sizeof(loc_addr));
listen(sock, 1);
// accept one connection <-- PROGRAM FAILS HERE AS accept() returns -1
client = accept(sock, (struct sockaddr *)&rem_addr, &opt);
// ---- All following code is irrelevant because accept fails ----
ba2str( &rem_addr.rc_bdaddr, buf );
fprintf(stderr, "accepted connection from %s\n", buf);
memset(buf, 0, sizeof(buf));
bytes_read = read(client, buf, sizeof(buf));
if( bytes_read > 0 )
printf("received [%s]\n", buf);
close(client);
close(sock);
FreeEV3();
return 0;
}
I am able to get the same code working on my pi. Even communication back and forth when the ev3api-specific functions are commented out. I just can't fathom why it won't work on the EV3.
I figured it out.
On my raspberry PI, the accept call worked as expected with no quirks. On the EV3 however, the accept call is non-blocking even if it has not been told to act like so.
The solution was to place the accept call in a loop until an incoming connection was in the queue.
while (errno == EAGAIN && ButtonIsUp(BTNEXIT) && client < 0)
client = accept(sock, (struct sockaddr*)&rem_addr, sizeof(rem_addr));
I'll upload the code to github. Contact me if you'd like to do something similar with the EV3.
I'm learning Unix Network Programming Volume 1, I want to reproduce the accept error for RST in Linux.
server: call socket(), bind(), listen(), and sleep(10)
client: call socket(), connect(), setsockopt() of LINGER, close() and return
server: call accept()
I think that the 3rd steps will get an error like ECONNABORTED, but not.
Do I want to know why?
I will appreciate it if you help me.
The follow is server code :
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <stdio.h>
#include <strings.h>
#include <unistd.h>
int main(int argc, char* argv[]) {
int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
struct sockaddr_in addr;
bzero(&addr, sizeof addr);
addr.sin_family = AF_INET;
addr.sin_port = htons(6666);
inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
bind(sock, (struct sockaddr*)(&addr), (socklen_t)(sizeof addr));
listen(sock, 5);
sleep(10);
if (accept(sock, NULL, NULL) < 0)
perror("error");
else
printf("right");
return 0;
}
The following is the client code
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <stdio.h>
#include <strings.h>
#include <unistd.h>
int main(int argc, char* argv[]) {
int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
struct sockaddr_in addr;
bzero(&addr, sizeof addr);
addr.sin_family = AF_INET;
addr.sin_port = htons(6666);
inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
connect(sock, (struct sockaddr*)(&addr), (socklen_t)(sizeof addr));
struct linger ling;
ling.l_onoff = 1;
ling.l_linger = 0;
setsockopt(sock, SOL_SOCKET, SO_LINGER, &ling, sizeof ling);
close(sock);
return 0;
}
Nope. I think you'll get an empty, but complete connection (with no data). The kernel will manage the complete connection establishment and then it'll get an immediate FIN packet (meaning EOF, not reset) and will handle it (or wait for user space process to close its side, to send the FIN to the other side) For a connection abort you need to reboot the client machine (or the server) without allowing it to send the FIN packets (or disconnecting it from the network before rebooting it) An ACK is never answered, so you won't get a RST sent from an ACK.
RST packets are sent automatically by the kernel when some state mismatch is in between two parties. For this to happen in a correct implementation you must force such a state mismatch (this is why the machine reboot is necessary)
Make a connection between both parties and stop it (with a sleep) to ensure the connection is in the ESTABLISHED state before disconnecting the cable.
disconnect physically one of the peers from the network, so you don't allow its traffic to go to the network.
reboot the machine, so all sockets are in the IDLE state.
reconnect the cable. As soon as the waiting machine gets out of the sleep and begins sending packets again, it will receive a RST segment from the other side, because it has been rebooted and TCP does not know about that connection.
Other ways of getting a RST segment involve bad implementations of TCP, or mangling the packets in transit (changing the sender or receiver sequence numbers in transit)
The purpose of RST packets is not to add functionality to TCP, but to detect misbehaviours, to there should be no means to get a reset with proper use of sockets. Listen syscall is there to allow you to reserve resources in kernel space to allow the user space process to prepare to handle the connection while the clients are trying to connect. If you do what you intend you'll get a connection with no data, but valid connection, SO_LINGER is there to force a loss of status when machines don't have the time to send the packets to each other... but being connected, the whole connection is handled in the kernel and no abort is to be expected.
Linux accept() (and accept4()) passes already-pending network errors
on the new socket as an error code from accept(). This behavior
differs from other BSD socket implementations. For reliable
operation the application should detect the network errors defined
for the protocol after accept() and treat them like EAGAIN by
retrying. In the case of TCP/IP, these are ENETDOWN, EPROTO,
ENOPROTOOPT, EHOSTDOWN, ENONET, EHOSTUNREACH, EOPNOTSUPP, and
ENETUNREACH.
http://man7.org/linux/man-pages/man2/accept.2.html
I'm writing a very small C UDP client. I know that a random port is chosen as source port when you send data to the server. I also know that you can use bind to specify yourself the port you want a response.
However, I don't know when is the port randomly chosen? For example, I would like to rely on the sender address to keep track of users. It currently works only if the client does not shutdown, the port is still the same then a simple memcmp is enough to detect the same client.
This small code will use the same source port until it exits:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>
int main(void)
{
int s, error, ch;
struct addrinfo hints, *res;
memset(&hints, 0, sizeof (struct addrinfo));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
if ((error = getaddrinfo("localhost", "9988", &hints, &res)))
errx(1, "%s", gai_strerror(error));
if ((s = socket(res->ai_family, res->ai_socktype, 0)) < 0)
err(1, "socket");
while ((ch = fgetc(stdin)) != EOF)
sendto(s, &ch, 1, 0, res->ai_addr, res->ai_addrlen);
}
And running something like : dmesg | ./client will use the same address until the program exits. However, when you run it again, the port is different.
So is it the socket function that choose a port? Or the system? Is it sure that the port will still be the same during the client lifetime?
If the socket is not explicitly bound, then the OS will bind it (with a random port) when you send the first packet. This binding will be active as long as the socket is open, once it's closed the socket is (of course) unbound.
And due to the connectionless nature of UDP sockets, the "server" (if done correctly) should not keep the address of all "clients" that send to it indefinitely. Instead it should use the source address as received in the recvfrom call, and use that for a reply. The only reason to store the source address for more than just a simple request/response, is if you have a more advanced protocol on top of UDP with your own "connection" handling.
I'm working on a real time project on my Debian Wheezy (with real-time patch), it needs a strong reactivity using TCP communication protocol.
When I send a request, the response time is too long (220us) and I don't understand why.
My problem is when I send a request, my application server answers too late for my needs.
So, I decided to write a short program using TCP socket to acquire my server's response time. (see code below)
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
int main(int argc, char* argv[])
{
char sendBuffer [] = "OK";
char buffer [10];
int socket1;
int workingSocket;
socklen_t len;
int nodelay = 1;
struct sockaddr_in sa1;
struct sockaddr sa2;
socket1 = 0;
workingSocket = 0;
len = sizeof(sa1);
memset(&sa1, 0, len);
sa1.sin_addr.s_addr = htonl (INADDR_ANY);
sa1.sin_family = AF_INET;
sa1.sin_port = htons(12345);
socket1 = socket(AF_INET, SOCK_STREAM, 0);
bind(socket1, (struct sockaddr *)&sa1, len);
listen(socket1, 10);
workingSocket = accept(socket1, &sa2, &len);
setsockopt (workingSocket, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay));
// receive and send message back
while (1)
{
recv(workingSocket, buffer, 5, MSG_WAITALL);
send(workingSocket, sendBuffer, 2, 0);
}
}
I check the response time doing the following procedure :
start a wireshark session to trace the network traffic
launch my C server.
send a TCP request for example : $echo 'abcde'|netcat 192.168.0.1 12345
I got a response time of around 200 µs between the moment the string is sent (abcde) and the moment when I receive the reponse on the socket (OK)
This time seems to be very high. I made the same experience on VxWorks and got a response time aproaching 10µs.
Is the Linux kernel really slow or is there a trick to increase the reactivity of the system ?
Thank you for your help and your advices.