What is a BSD (or portable) way to get ToS bytes (e.g. IP_RECVTOS from linux)?

What is the correct (portable, stable) way to get the ToS byte of the received packet? I am doing UDP with recvmsg () and linux. I can get ToS if I set () IP_RECVTOS / IPV6_RECVTCLASS, but IP_RECVTOS does not seem to be available on my BSD systems. What is the right way to do this?

First of all, I want this to work on BSD and Solaris.

Edit: To clarify: I am currently using recvmsg () where I get TTL and TOS in the msg_control field on Linux, but in order to get TTL and TOS, I need to set setsopop () - enable IP_RECVTTL and IP_RECVTOS. And since Solaris and BSD (currently working with FreeBSD) do not have IP_RECVTOS from what I see, I do not get TOS when navigating through CMSG data.

I tried to enable IP_RECVOPTS and IP_RECVRETOPTS, but I still do not get a CMSG of type IP_TOS.

Edit 2: I want ToS to be able to verify (as much as possible) that it was not overwritten in transit. If, for example, a VoIP application suddenly notices that it is not receiving packets marked with EF, then something is wrong and there should be an alarm. (and no, I do not expect EF to be respected or stored on the public Internet)

I want TTL mostly just because I can. Hypothetically, this can be used to trigger “something has changed on the network between me and the other side” alerts, which can be useful to know if something stops working at the same time.

+4
source share
4 answers

I thought if you can create two sockets.

  • One DGRAM socket used exclusively for sending

  • One Raw socket used exclusively for receiving.

Since you are using UDP, you can call bind + recvFrom on Raw Sock Fd and then manually unzip the IP header to determine TOS or TTL.

When you want to send, use DOCAM SockFd, so you don’t have to worry about actually creating the UDP and IP packet.

It is possible that problems such as the kernel can transfer the received buffer to both sockets or to a UDP socket instead of a Raw socket or only to a Raw socket. If so (or if it depends on the implementation), we will return to the square. However, you can try calling bind on the Raw socket and see if it helps. I know this can be a hack, but a network search for setsockopt for BSD returns nothing.

EDIT : I wrote an example program. It's kind of achieving a goal.

In the code below, two sockets are created (one raw and one udp). The udp socket is bound to the actual port that I expect to receive data, while the raw socket is bound to port 0. I tested this on Linux and, as I expected, any data for port 2905 is accepted by both sockets. However, I can get the TTL and TOS values. Do not reduce the quality of the code. I'm just experimenting if this will work.

Further EDIT: Disabled receiving through UDP socket. I further improved the code to disable UDP packet reception. Using setsockopt, I set the UDP socket receive buffer to 0 .. This ensures that the kernel does not forward the packet to the UDP socket. IMHO, now you can use the UDP socket exclusively for sending and the raw socket for reading. This should work for you in BSD and Solaris.

#include<stdio.h> #include<stdlib.h> #include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h> #include<netinet/ip.h> #include<arpa/inet.h> #include<string.h> #include "protHeaders.x" #include "gen.h" int main(void) { S32 rawSockFd; S32 udpSockFd; struct sockaddr_in rsin; struct sockaddr_in usin; S32 one = 1; const S32* val = &one; struct timeval tv; fd_set rfds; S32 maxFd; S16 ret; S8 rawBuffer[2048]; S8 udpBuffer[2048]; struct sockaddr udpFrom,rawFrom; socklen_t rLen,uLen; memset(rawBuffer,0,sizeof(rawBuffer)); memset(udpBuffer,0,sizeof(udpBuffer)); memset(udpFrom,0,sizeof(udpFrom)); memset(rawFrom,0,sizeof(rawFrom)); if ((rawSockFd = socket(PF_INET,SOCK_RAW,IPPROTO_UDP)) < 0) { perror("socket:create"); RETVALUE(RFAILED); } /* doing the IP_HDRINCL call */ if (setsockopt(rawSockFd,IPPROTO_IP,IP_HDRINCL,val,sizeof(one)) < 0) { perror("Server:setsockopt"); RETVALUE(RFAILED); } rsin.sin_family = AF_INET; rsin.sin_addr.s_addr = htonl(INADDR_ANY); rsin.sin_port = htons(0); usin.sin_family = AF_INET; usin.sin_addr.s_addr = htons(INADDR_ANY); usin.sin_port = htons(2905); if(bind(rawSockFd,(struct sockaddr *)&rsin, sizeof(rsin)) < 0 ) { perror("Server: bind failed"); RETVALUE(RFAILED); } if ((udpSockFd = socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP)) < 0) { perror("socket:create"); RETVALUE(RFAILED); } if(bind(udpSockFd,(struct sockaddr *)&usin, sizeof(usin)) < 0 ) { perror("Server: bind failed on udpsocket"); RETVALUE(RFAILED); } /*set upd socket receive buffer to 0 */ one = 0; if (setsockopt(udpSockFd,SOL_SOCKET,SO_RCVBUF,(char *)&one,sizeof(one)) < 0) { perror("Server:setsockopt on udpsocket failed"); RETVALUE(RFAILED); } tv.tv_sec = 0; tv.tv_usec = 0; maxFd = (rawSockFd > udpSockFd)? rawSockFd:udpSockFd; while(1) { FD_ZERO(&rfds); FD_SET(rawSockFd,&rfds); FD_SET(udpSockFd,&rfds); ret = select(maxFd+1,&rfds,0,0,&tv); if ( ret == -1) { perror("Select Failed"); RETVALUE(RFAILED); } if(FD_ISSET(rawSockFd,&rfds)) { printf("Raw Socked Received Message\n"); if(recvfrom(rawSockFd,rawBuffer,sizeof(rawBuffer),0,&rawFrom,&rLen) == -1) { perror("Raw socket recvfrom failed"); RETVALUE(RFAILED); } /*print the tos */ printf("TOS:%x\n",*(rawBuffer+1)); printf("TTL:%x\n",*(rawBuffer+8)); } if(FD_ISSET(udpSockFd,&rfds)) { printf("UDP Socked Received Message\n"); if(recvfrom(udpSockFd,udpBuffer,sizeof(udpBuffer),0,&udpFrom,&uLen) == -1) { perror("Udp socket recvfrom failed"); RETVALUE(RFAILED); } printf("%s\n",udpBuffer); } } RETVALUE(ROK); } 
+2
source

Unfortunately, this thing usually changes in different * ixs. On Solaris, you want to use getsockopt with IP_TOS ; I do not know about BSD.

See man 7 ip for more details.

0
source

The “correct” and standard solution is probably to use cmsg(3) . You will find the full description in the Stevens ' 'Unix network programming' book, required reading.

A google code search found me a usage example .

0
source

My understanding is that, firstly, BSD does not support IP_RECVTOS functions, and secondly, incompatible BSD sockets do not support receiving UDP or TCP packets. However, there are two other ways to do this, firstly, using the / dev / bpf interface - either directly or via libpcap. Or, secondly, using DIVERT sockets, which allow you to redirect specified traffic flows to the user area.

Has anyone really checked the code above in the BSD box? (it can work on Solaris ...)

On Linux, this approach will work, but as already mentioned, it is also possible (and more convenient) to use setsockopt () with IP_TOS in the outgoing socket to set the outgoing TOS byte and setsockopt () with IP_RECVTOS on the incoming socket and use recvmsg (), to get the TOS byte.

0
source

Source: https://habr.com/ru/post/1286602/


All Articles