"tcp session control protocol" in linux kernel? - c

I'm programming in Linux environment.
I want to establish a TCP socket between two PCs, and multiplexing it.
To be specific, here is the pseudo-code:
void func_in_process0()
{
socket s = socket(AF_INET, SOCK_STREAM, 0);
connect(s, "1.1.1.1", 8080);
socket s1 = ADD_CHANNEL(s, 1); // key part
SEND_FILE_DESCRIPTOR_TO_PROCESS(s1, process1);
socket s2 = ADD_CHANNEL(s, 2); // key part
SEND_FILE_DESCRIPTOR_TO_PROCESS(s2, process2);
WAIT_FOR_PROCESS_TO_FINISH(process1);
WAIT_FOR_PROCESS_TO_FINISH(process2);
close(s);
}
void func_in_process1()
{
socket s1 = GET_SOCKET_FROM_PROCESS0();
send(s1, "abcdefg");
close(s1);
}
void func_in_process2()
{
socket s2 = GET_SOCKET_FROM_PROCESS0();
send(s2, "abcdefg");
close(s2);
}
The key part is how to implement function ADD_CHANNEL.
I found a document TCP Session Control Protocol which provides exactly the function I want.
But I don't think it is implemented in Linux kernel.
I can implement this protocol in userspace, but variable "s1" and "s2" will not be file descriptor and can't be sent to other processses.
Edit:
process0, process1 and process2 are all on localhost.
socket "s" is a client socket.
1.1.1.1 is a remote PC, who is the server. so proc1 and proc2 both sends packet to 1.1.1.1 through the CHANNEL.
the server is CHANNEL aware too.
Edit2:
I read the SCP Protocol, but it still has no in-kernel implementation.
I learned the SCTP multi-streaming, which is implemented in kernel, but there's no API that creates sub-socket so the problem remains.

Sure, they can be file descriptors. Create two pipes and give one end to each of the other two processes. Your 'multiplexor' will read from, and write two, the other ends of the two bidirectional pipes.

Related

Understanding USBIP bind function

I am trying to understand the USBIP tool code from Linux (https://github.com/torvalds/linux/tree/master/tools/usb/usbip).
USBIP has a command that attaches a remote USB from an IP to a client system. If you look at the code (https://github.com/torvalds/linux/blob/master/tools/usb/usbip/src/usbip_bind.c#L130) below, which binds the USB, if the function calls close(sockfd); and close the socket, then how the communication is done between client and server for USB data.
From Docs (https://docs.kernel.org/usb/usbip_protocol.html):
Once the client knows the list of exported USB devices it may decide
to use one of them. First the client opens a TCP/IP connection to the
server and sends an OP_REQ_IMPORT packet. The server replies with
OP_REP_IMPORT. If the import was successful the TCP/IP connection
remains open and will be used to transfer the URB traffic between the
client and the server.
It says connection remains open if import is successful, then what is the purpose of close(sockfd);. I can also see sockfd is sent inside query_import_device(sockfd, busid); but if it is closed how this is used?
static int attach_device(char *host, char *busid)
{
int sockfd;
int rc;
int rhport;
// creates a TCP connection to the specified host on the specified port.
sockfd = usbip_net_tcp_connect(host, usbip_port_string);
if (sockfd < 0) {
err("tcp connect");
return -1;
}
// sends a query to the connected host to import the device specified by "busid".
rhport = query_import_device(sockfd, busid);
if (rhport < 0)
return -1;
// closes the previously established TCP connection.
close(sockfd);
// records details of the connection, such as the host and port, the busid of the device, and the assigned rhport.
rc = record_connection(host, usbip_port_string, busid, rhport);
if (rc < 0) {
err("record connection");
return -1;
}
return 0;
}
If you look at the source code of usbip helper program, query_import_device(), it establishes the connection and then calls import_device() to finish it.
In turn, import_device() calls usbip_vhci_attach_device(), that calls usbip_vhci_attach_device2()... that eventually writes the sockfd and some extra data to the /sys/*/attach pseudo file for the given device.
To see what happens next we need to go to kernel mode. So, looking at the source code of the usbip, at function attach_store(). This function parses the sockfd back into an integer file descriptor and, most notably, does this:
struct socket *socket;
socket = sockfd_lookup(sockfd, &err);
This converts a file descriptor into a real kernel socket object. And it increases the reference count. Note how if any of the further checks fail, it calls sockfd_put(socket); to decrease that reference count.
But if the function succeeds, the socket is stored into the device. It is just as if an userland program had done a call to dup(): it doesn't matter if the original sockfd is closed, the kernel keeps the actual socket opened as long as it is needed.
You can see that the value of sockfd is also stored in the device, but it isn't actually used for anything other than reading back from /sys, so it doesn't matter if it is no longer a valid file descriptor.
So the close is actually necessary because the kernel dups the socket internally, that keeps the connection opened. And you do not want to keep an extra FD referencing the same socket, if you try to use that socket for anything that could mess up the device connection. The responsible thing to do is just to close it.

systemd socket activation, sd_listen_fds return 0 fd

I am writing a sample systemd service to test few things.
unfortunately, the test code is not working.
(i expected it to be simple ..)
$>cat test.socket
[Unit]
Description=TEST-SERVICE
[Socket]
ListenStream=5575
Accept=yes
[Install]
WantedBy=sockets.target
$>cat test#.service
[Unit]
Description=TEST-SERVICE
Requires=test.socket
[Service]
StandardInput=socket
PIDFile=/var/run/test.pid
ExecStart=/usr/bin/server_d
Type=simple
[Install]
WantedBy=default.target
The server code looks like below
int main(int argc, char *argv[]){
char buffer[255]; //Temporary buffer to read and write data
int sockfd, newsockfd ; //File descriptor for Sockets
struct sockaddr_in cli_addr; //Structure to hold Client details
socklen_t cli_len; //Client length
int nfd = sd_listen_fds(0);
if (nfd != 1) {
fprintf(stderr, "No or too many file descriptors received.\n");
fprintf(stderr, "FD Count: %d.\n",nfd);
exit(1);
}
sockfd = SD_LISTEN_FDS_START + 0;
The output comes when I connect to the service as below.
>telnet 15.218.114.55 5575
Trying 15.218.114.55...
Connected to 15.218.114.55.
Escape character is '^]'.
No or too many file descriptors received.
FD Count: 0.
Connection closed by foreign host.
It looks to me like sd_listen_fds(0) is returning 0.
what could be the reason for it here?
Thanks for the help in advance.
There are 2 different ways a socket unit works. With Accept=yes, systemd will do the bind() and listen() calls for each LISTEN...=... socket, then wait with select() on them all. Whenever there is a connection, a socket becomes ready, and systemd will do an accept() call on the socket connection, then pass the new socket fd to the service unit ready to use with read/write etc. Systemd then carries on waiting for new connections, and will start a new service with each of them similarly.
In this case the (template) service does not use sd_listen_fds(). If you have StandardInput=socket, then you can simply read/write fd 0.
The second type of socket unit has Accept=no (the default if not specified). Systemd will do the bind() and listen() calls for each LISTEN...=... socket, then wait with select() on them all. As soon as the first socket is ready, it will start the service once only and pass all the open socket file descriptors to it. This is where the sd_listen_fds() api comes in. It says how many socket file descriptors got passed. They start at 3 (SD_LISTEN_FDS_START).
The service application now has to find out which of the passed sockets was ready. Typically, there is only one LISTEN line in the socket unit, so only one fd is passed, and so it is easy (otherwise simply do a select()).
The service application now has to do an accept() on the ready socket, and can then read/write to the new fd. Normally, it would also continue to wait for more connections on the listened-to sockets it was given, do more accepts, and so on.

3 sockets created for one libevent bind on Windows?

I'm writing a BitTorrent client to learn some more about networking, and I've got something that I'm struggling to Google for. As part of the BT spec, I need a server to listen for peer connections to my computer. In the win32 port, I have code like this to setup my server
struct sockaddr_in saServer;
struct hostent* localHost;
char* localIP;
// Get the local host information
localHost = gethostbyname("");
localIP = inet_ntoa(*(struct in_addr *)*localHost->h_addr_list);
saServer.sin_family = AF_INET;
saServer.sin_addr.s_addr = inet_addr(localIP);
saServer.sin_port = htons(6881);
struct evconnlistener *listener = evconnlistener_new_bind(base, accept_conn_cb, NULL,
LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, -1, (SOCKADDR*)&saServer, sizeof(saServer));
That seems to work, but if I look at netstat, I see the following,
BitTorrentClient.exe 6092 TCP hostname 50216 localhost 50217 ESTABLISHED
BitTorrentClient.exe 6092 TCP hostname 50217 localhost 50216 ESTABLISHED
BitTorrentClient.exe 6092 TCP hostname.home 6881 hostname 0 LISTENING
Why are there two other connections, one from port 50216->50217 and one looping back from 50217->50216? I was expected to have just one listening connection on port 6881. Is this a libevent quirk, or something more fundamental related to networking?
What can I search for to understand what the other two ports are used for?
This is most likely a result of libevent calling evutil_make_internal_pipe_ internally.
Libevent creates internal "pipes" for inter-thread communication and signal delivery using socketpair(2) on POSIX-compliant systems, whereas on Windows libevent has to resort to manually connecting two sockets together instead.

how to kill a tcp connection in a tcp server program if no FIN/ACK or RST received

I wrote a tcp server program(linux c) and run it on host B
if host A establishes a TCP connection with host B
then A shutdown without sending FIN/ACK
how do I write source codes inside tcp server to kill this tcp connection?
use raw socket to craft s fake RST?
or other ways?
thanks!
Just close() the server's end of the socket once it has determined that the connection is no longer available. Eventually, the socket will time out internally and start reporting errors to read/write operations, at which time you can then close it. If you do not want to wait that long then implement a timeout in your own code, either as a keepalive/ping command in your protocol, or just as a simple timer that keeps track of the last time the client exchanged any data with the server. If the timeout period expires, close the socket regardless of its actual state.
I'm not sure in this case, but if you create a binary file with all the open connections:
FILE *ptr_myfile;
ptr_myfile=fopen("test.bin","wb"); //opened sockets
//.. your code ..
sockfd[k] = socket(AF_INET, SOCK_STREAM, 0);
fwrite(&sockfd[k], sizeof(sockfd[k]), 1, ptr_myfile_soc);
//.. your code ..
close(ptr_myfile);
and if host A shuts down without sending FIN/ACK, in another executable you will read from that file and close the related sockets:
fread(&sockfd[k],sizeof(sockfd[k]),1,ptr_myfile_soc);
printf("closing %d \n",k );
close(sockfd[k]);

C pcap detecting inbound datagrams

In C I bind a datagram socket (AF_INET, SOCK_DGRAM) to INADDR_ANY. I then periodically use this socket to send and receive datagrams, and monitor the flow of packets with pcap. The problem is, I can't tell whether a packet is incoming or outgoing using pcap.
The transmission/receiving and the pcap monitoring are running in separate threads, and for synchronisation reasons they can't communicate. I only want to track the incoming packets, not the ones being sent, so does anyone have an idea as to how I can do that?
I thought already of testing the destination ip address, but I can't figure out any way to get my local ip. the machine this is running on doesn't have a static ip, much less an assigned domain name, and it seems that getsockname doesn't work on sockets bound to INADDR_ANY. Also tried using ioctl(sockfd, SIOCGIFCONF, &buffer), which didn't work either - sets buffer.ifc_len=0.
Found a solution. I can get my own ip using this:
char *command = malloc(100);
sprintf(command,"ifconfig %s|grep -o \"inet addr:[^ ]\"|grep -o -e \"[0-9]\.[0-9]\.[0-9]\.[0-9]*\"",device);
char path[1035];
FILE *fp;
fp = popen(command,"r");
fgets(path, 1034, fp);
pclose(fp);
my_ip = malloc(sizeof(char)*(1+strlen(path)));
memcpy(my_ip, path, strlen(path)-1);
my_ip[strlen(path)-1] = 0;

Resources