Linux-man Archive mirror
 help / color / mirror / Atom feed
From: Oliver Crumrine <ozlinuxc@gmail.com>
To: alx@kernel.org
Cc: linux-kernel@vger.kernel.org, linux-man@vger.kernel.org
Subject: [PATCH] ip.7: Add not supported by SOCK_STREAM to socket options
Date: Sun, 17 Mar 2024 09:55:50 -0400	[thread overview]
Message-ID: <ejhphmjh74ebtk4br3id66f27a4yoh4aukrcz7m6dp7acsu6zr@crtueyadqzmp> (raw)

[-- Attachment #1: Type: text/plain, Size: 5007 bytes --]

It was not made clear in several socket options that they were not
supported by SOCK_STREAM; this patch fixes that.

Socket options not supported by SOCK_STREAM are handled in the
ip_cmsg_recv_offset function in net/ipv4/ip_sockglue.c. The function is
called for udp sockets, and indirectly by ping and raw sockets, but not
for STREAM sockets, as they don't support these options.

Signed-off-by: Oliver Crumrine <ozlinuxc@gmail.com>

v1->v2: Add IP_RETOPTS to the socket options without support on
SOCK_STREAM

Alex, I have attached the two test programs below, updated for support
with IP_RETOPTS.

I couldn't get an ip option out of netcat, so I'm attaching the client
programs, also updated with support for IP_OPTIONS, so they put an ip
option onto the packet for the server program to recieve.

Here is the diff between the two servers:
--- testDgramSocketServer.c     2024-03-17 08:32:27.623451419 -0400
+++ testStreamSocketServer.c    2024-03-17 08:21:11.860109033 -0400
@@ -23,7 +23,7 @@
        struct sockaddr_in local_addr;
        int s;

-       s = socket(AF_INET, SOCK_DGRAM, 0);
+       s = socket(AF_INET, SOCK_STREAM, 0);
        if (s == -1){
                err(1, "error creating socket");
        }
@@ -43,8 +43,20 @@
        if(bind(s, (struct sockaddr*)&local_addr, sizeof(local_addr) ) == -1){
                err(1, "error binding to port. try changing it or running as root");
        }
+
+       if(listen(s, 10) == -1){ //10 is the backlog of un-accepted connections. its just an arbitrary number
+               err(1, "error listening on port");
+       }

        while(1){
+               int connfd = accept(s, (struct sockaddr*)NULL, NULL);
+               if(connfd == -1){
+                       err(1, "error accepting connection");
+               }
+               if(setsockopt(s, IPPROTO_IP, SOCKOPT, &yes, sizeof(yes)) == -1){ //stream sockets should have this set on the connected socket as well. I left it above for uniformity between the two programs.
+                       err(1, "error setting socket option");
+               }
+
                struct msghdr mhdr;
                struct iovec iov[1];
                struct cmsghdr *cmhdr;
@@ -63,7 +75,7 @@
                memset(databuf, 0, sizeof(databuf));

                //this is blocking
-               int msglen = recvmsg(s, &mhdr, 0);
+               int msglen = recvmsg(connfd, &mhdr, 0);
                if (msglen == -1){
                        err(1, "recvmsg");
                }
@@ -78,6 +90,7 @@
                }
                //print out the first byte of data recieved in hex. You can verify this in wireshark if you like.
                printf("data read: %sbyte = %02X\n", databuf, tos);
+               close(connfd);
        }

        close(s);

And the clients in case you're interested:
--- testDgramSocketClient.c     2024-03-17 08:24:07.640111430 -0400
+++ testStreamSocketClient.c    2024-03-17 08:23:02.883443865 -0400
@@ -15,7 +15,7 @@

        char buf[] = "testing 1 2 3\n";

-       s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+       s = socket(AF_INET, SOCK_STREAM, 0);
        if(s == -1){
                err(1, "error creating socket");
        }
@@ -34,7 +34,10 @@
                err(1, "error converting network address");
        }

-       if(sendto(s, buf, strlen(buf), 0, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1){
+       if(connect(s, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1){
+               err(1, "error connecting");
+       }
+       if(send(s, buf, strlen(buf), 0) == -1){
                err(1, "error sending data");
        }

---
 man7/ip.7 | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/man7/ip.7 b/man7/ip.7
index 2b4b06324..39055d3cf 100644
--- a/man7/ip.7
+++ b/man7/ip.7
@@ -828,6 +828,9 @@ is not zero, the primary local address of the interface specified by the
 index overwrites
 .I ipi_spec_dst
 for the routing table lookup.
+Not supported for
+.B SOCK_STREAM
+sockets.
 .TP
 .BR IP_RECVERR " (since Linux 2.2)"
 .\" Precisely: since Linux 2.1.15
@@ -989,6 +992,9 @@ in which the kernel returns the original destination address
 of the datagram being received.
 The ancillary message contains a
 .IR "struct sockaddr_in" .
+Not supported for
+.B SOCK_STREAM
+sockets.
 .TP
 .BR IP_RECVTOS " (since Linux 2.2)"
 .\" Precisely: since Linux 2.1.68
@@ -998,6 +1004,9 @@ ancillary message is passed with incoming packets.
 It contains a byte which specifies the Type of Service/Precedence
 field of the packet header.
 Expects a boolean integer flag.
+Not supported for
+.B SOCK_STREAM
+sockets.
 .TP
 .BR IP_RECVTTL " (since Linux 2.2)"
 .\" Precisely: since Linux 2.1.68
@@ -1015,6 +1024,9 @@ Identical to
 .BR IP_RECVOPTS ,
 but returns raw unprocessed options with timestamp and route record
 options not filled in for this hop.
+Not supported for
+.B SOCK_STREAM
+sockets.
 .TP
 .BR IP_ROUTER_ALERT " (since Linux 2.2)"
 .\" Precisely: since Linux 2.1.68
-- 
2.44.0


[-- Attachment #2: testDgramSocketClient.c --]
[-- Type: text/plain, Size: 1119 bytes --]

#include <stdio.h>
#include <err.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>

#define PORT 8888 //The port on which to send data
#define ADDR "127.0.0.1" //The internet address to send packets to

int main(void){
	int s;
	struct sockaddr_in server_addr;

	char buf[] = "testing 1 2 3\n";

	s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if(s == -1){
		err(1, "error creating socket");
	}

	uint8_t options = 1; //noop in the language of ip options

	if(setsockopt(s, IPPROTO_IP, IP_OPTIONS, &options, 1) == -1){
		err(1, "error setting socket options");
	}

	memset(&server_addr, 0, sizeof(server_addr));
	
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(PORT);
	if(inet_pton(AF_INET, ADDR, &server_addr.sin_addr) != 1){ // I realize I'm checking the return value differently here. If you read the man page for inet_pton, it'll make sense.
		err(1, "error converting network address");
	}

	if(sendto(s, buf, strlen(buf), 0, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1){
		err(1, "error sending data");
	}
	
	close(s);
}

[-- Attachment #3: testDgramSocketServer.c --]
[-- Type: text/plain, Size: 2462 bytes --]

#include<stdio.h>
#include<err.h>
#include<string.h>
#include<stdlib.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<unistd.h>

#define PORT 8888	//The port on which to listen for incoming data


//Hi Alex,
//These are the two lines that allow you to switch between the three socket options outlined in my patch
//The socket options tell the kernel to add a control message (cmsg), allowing the program
//to recieve the data it is requesting. The three options are: IP_RECVTOS for the type of service byte,
//IP_RECVORIGDSTADDR for the orignial dst address, and IP_PKTINFO for some random packet info, and IP_RETOPTS
//for some random ip packet options
#define SOCKOPT IP_RECVORIGDSTADDR
//This field is synonymous with the above one. Valid options are: IP_TOS, IP_ORIGDSTADDR, IP_PKTINFO, and IP_OPTIONS
#define RECIVEOPTION IP_ORIGDSTADDR

int main(void){
	struct sockaddr_in local_addr;
	int s;
	
	s = socket(AF_INET, SOCK_DGRAM, 0);
	if (s == -1){
		err(1, "error creating socket");
	}
	
	memset(&local_addr, 0, sizeof(local_addr));
	
	local_addr.sin_family = AF_INET;
	local_addr.sin_port = htons(PORT);
	local_addr.sin_addr.s_addr = htonl(INADDR_ANY);

	int yes = 1;
	if(setsockopt(s, IPPROTO_IP, SOCKOPT, &yes, sizeof(yes)) == -1){
		err(1, "error setting socket option");
	}


	if(bind(s, (struct sockaddr*)&local_addr, sizeof(local_addr) ) == -1){
		err(1, "error binding to port. try changing it or running as root");
	}

	while(1){
		struct msghdr mhdr;
		struct iovec iov[1];
		struct cmsghdr *cmhdr;
		char control[1000];
		char databuf[BUFSIZ];
		unsigned char tos = 0;

		mhdr.msg_name = &local_addr;
		mhdr.msg_namelen = sizeof(local_addr);
		mhdr.msg_iov = iov;
		mhdr.msg_iovlen = 1;
		mhdr.msg_control = &control;
		mhdr.msg_controllen = sizeof(control);
		iov[0].iov_base = databuf;
		iov[0].iov_len = sizeof(databuf);
		memset(databuf, 0, sizeof(databuf));	
		
		//this is blocking
		int msglen = recvmsg(s, &mhdr, 0);
		if (msglen == -1){
			err(1, "recvmsg");
		}
		cmhdr = CMSG_FIRSTHDR(&mhdr);
		while (cmhdr) {
			printf("cmsg recieved\n");
			if (cmhdr->cmsg_level == IPPROTO_IP && cmhdr->cmsg_type == RECIVEOPTION) {
				//read the byte recieved
				tos = ((unsigned char *)CMSG_DATA(cmhdr))[0];
			}
			cmhdr = CMSG_NXTHDR(&mhdr, cmhdr);
		}
		//print out the first byte of data recieved in hex. You can verify this in wireshark if you like.
		printf("data read: %sbyte = %02X\n", databuf, tos);
	}

	close(s);
	return 0;
}

[-- Attachment #4: testStreamSocketClient.c --]
[-- Type: text/plain, Size: 1164 bytes --]

#include <stdio.h>
#include <err.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>

#define PORT 8888 //The port on which to send data
#define ADDR "127.0.0.1" //The internet address to send packets to

int main(void){
	int s;
	struct sockaddr_in server_addr;

	char buf[] = "testing 1 2 3\n";

	s = socket(AF_INET, SOCK_STREAM, 0);
	if(s == -1){
		err(1, "error creating socket");
	}

	uint8_t options = 1; //noop in the language of ip options

	if(setsockopt(s, IPPROTO_IP, IP_OPTIONS, &options, 1) == -1){
		err(1, "error setting socket options");
	}

	memset(&server_addr, 0, sizeof(server_addr));
	
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(PORT);
	if(inet_pton(AF_INET, ADDR, &server_addr.sin_addr) != 1){ // I realize I'm checking the return value differently here. If you read the man page for inet_pton, it'll make sense.
		err(1, "error converting network address");
	}

	if(connect(s, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1){
		err(1, "error connecting");
	}
	if(send(s, buf, strlen(buf), 0) == -1){
		err(1, "error sending data");
	}
	
	close(s);
}

[-- Attachment #5: testStreamSocketServer.c --]
[-- Type: text/plain, Size: 2994 bytes --]

#include<stdio.h>
#include<err.h>
#include<string.h>
#include<stdlib.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<unistd.h>

#define PORT 8888	//The port on which to listen for incoming data


//Hi Alex,
//These are the two lines that allow you to switch between the three socket options outlined in my patch
//The socket options tell the kernel to add a control message (cmsg), allowing the program
//to recieve the data it is requesting. The three options are: IP_RECVTOS for the type of service byte,
//IP_RECVORIGDSTADDR for the orignial dst address, and IP_PKTINFO for some random packet info, and IP_RETOPTS
//for some random ip packet options
#define SOCKOPT IP_RECVORIGDSTADDR
//This field is synonymous with the above one. Valid options are: IP_TOS, IP_ORIGDSTADDR, IP_PKTINFO, and IP_OPTIONS
#define RECIVEOPTION IP_ORIGDSTADDR

int main(void){
	struct sockaddr_in local_addr;
	int s;
	
	s = socket(AF_INET, SOCK_STREAM, 0);
	if (s == -1){
		err(1, "error creating socket");
	}
	
	memset(&local_addr, 0, sizeof(local_addr));
	
	local_addr.sin_family = AF_INET;
	local_addr.sin_port = htons(PORT);
	local_addr.sin_addr.s_addr = htonl(INADDR_ANY);

	int yes = 1;
	if(setsockopt(s, IPPROTO_IP, SOCKOPT, &yes, sizeof(yes)) == -1){
		err(1, "error setting socket option");
	}


	if(bind(s, (struct sockaddr*)&local_addr, sizeof(local_addr) ) == -1){
		err(1, "error binding to port. try changing it or running as root");
	}
	
	if(listen(s, 10) == -1){ //10 is the backlog of un-accepted connections. its just an arbitrary number
		err(1, "error listening on port");
	}

	while(1){
		int connfd = accept(s, (struct sockaddr*)NULL, NULL);
		if(connfd == -1){
			err(1, "error accepting connection");
		}
		if(setsockopt(s, IPPROTO_IP, SOCKOPT, &yes, sizeof(yes)) == -1){ //stream sockets should have this set on the connected socket as well. I left it above for uniformity between the two programs.
			err(1, "error setting socket option");
		}
	
		struct msghdr mhdr;
		struct iovec iov[1];
		struct cmsghdr *cmhdr;
		char control[1000];
		char databuf[BUFSIZ];
		unsigned char tos = 0;

		mhdr.msg_name = &local_addr;
		mhdr.msg_namelen = sizeof(local_addr);
		mhdr.msg_iov = iov;
		mhdr.msg_iovlen = 1;
		mhdr.msg_control = &control;
		mhdr.msg_controllen = sizeof(control);
		iov[0].iov_base = databuf;
		iov[0].iov_len = sizeof(databuf);
		memset(databuf, 0, sizeof(databuf));	
		
		//this is blocking
		int msglen = recvmsg(connfd, &mhdr, 0);
		if (msglen == -1){
			err(1, "recvmsg");
		}
		cmhdr = CMSG_FIRSTHDR(&mhdr);
		while (cmhdr) {
			printf("cmsg recieved\n");
			if (cmhdr->cmsg_level == IPPROTO_IP && cmhdr->cmsg_type == RECIVEOPTION) {
				//read the byte recieved
				tos = ((unsigned char *)CMSG_DATA(cmhdr))[0];
			}
			cmhdr = CMSG_NXTHDR(&mhdr, cmhdr);
		}
		//print out the first byte of data recieved in hex. You can verify this in wireshark if you like.
		printf("data read: %sbyte = %02X\n", databuf, tos);
		close(connfd);
	}

	close(s);
	return 0;
}

             reply	other threads:[~2024-03-17 17:55 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-03-17 13:55 Oliver Crumrine [this message]
2024-03-17 18:28 ` [PATCH] ip.7: Add not supported by SOCK_STREAM to socket options Alejandro Colomar
  -- strict thread matches above, loose matches on Subject: below --
2024-03-02 18:19 Oliver Crumrine
2024-03-04 16:15 ` Alejandro Colomar
2024-03-05 19:31   ` Oliver Crumrine
2024-03-06 10:50     ` Alejandro Colomar
2024-03-06 10:58     ` Alejandro Colomar
2024-03-06 13:02       ` Oliver Crumrine
2024-03-06 13:12         ` Alejandro Colomar
     [not found]           ` <CAK1VsR3MsyphK+=rA7XcEigiSd6J_-QsVW+8hH1fU9xmRY3nGQ@mail.gmail.com>
2024-03-13 18:27             ` Oliver Crumrine
2024-03-13 22:35               ` Alejandro Colomar
2024-03-16 19:33               ` Alejandro Colomar
2024-03-16 18:41                 ` Oliver Crumrine
2024-03-17  2:02                   ` Alejandro Colomar
2024-03-17  9:02                     ` Oliver Crumrine
2024-03-17 13:55                       ` Alejandro Colomar
     [not found]                         ` <hxkixsi6uymkjmt4ughda2xmh6guzcaccrjbvsuasndyuvq5rz@36oqfaepfiql>
2024-03-17 15:17                           ` Alejandro Colomar
2024-03-17 11:31           ` Peter Seiderer
2024-03-17 18:27             ` Alejandro Colomar

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=ejhphmjh74ebtk4br3id66f27a4yoh4aukrcz7m6dp7acsu6zr@crtueyadqzmp \
    --to=ozlinuxc@gmail.com \
    --cc=alx@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-man@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).