Messages in this thread Patches in this message | | | Date | Sun, 17 Mar 2024 09:55:50 -0400 | From | Oliver Crumrine <> | 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 <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 #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; }
| |