2024-03-17 17:56:04

by Oliver Crumrine

[permalink] [raw]
Subject: [PATCH] ip.7: Add not supported by SOCK_STREAM to socket options

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 <[email protected]>

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


Attachments:
(No filename) (5.03 kB)
testDgramSocketClient.c (1.13 kB)
testDgramSocketServer.c (2.49 kB)
testStreamSocketClient.c (1.18 kB)
testStreamSocketServer.c (3.02 kB)
Download all attachments

2024-03-17 18:28:40

by Alejandro Colomar

[permalink] [raw]
Subject: Re: [PATCH] ip.7: Add not supported by SOCK_STREAM to socket options

Hi Oliver,

On Sun, Mar 17, 2024 at 09:55:50AM -0400, Oliver Crumrine wrote:
> 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 <[email protected]>

Patch applied; thanks.
<https://www.alejandro-colomar.es/src/alx/linux/man-pages/man-pages.git/commit/?h=contrib&id=5675cf3b048ec65b241d51c1130b55420a5d2456>

Have a lovely day!
Alex

>
> 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
>

> #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);
> }

> #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;
> }

> #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);
> }

> #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;
> }


--
<https://www.alejandro-colomar.es/>
Looking for a remote C programming job at the moment.


Attachments:
(No filename) (14.04 kB)
signature.asc (849.00 B)
Download all attachments