Tutorial :Linux: Can Recvmsg be used to receive the IP_TOS of every incoming packet



Question:

Can one use recvmsg() to obtain the IP_TOS field of every incoming packet or does it just show the IP_TOS value that is set for the particular socket. If not, does anyone know of a solution to obtain the IP_TOS values of every incoming packets. I am using a UDP application and therefore do not get to view the IP_TOS field at the application layer as is the case with TCP. Thanks.

Adding the code that I have written so far, incase it helps:

struct msghdr msg;   struct iovec iov[1];    memset(&msg, '\0', sizeof(msg));  msg.msg_iov = iov;  msg.msg_iovlen = 1;  iov[0].iov_base = (char *) &pkt;  iov[0].iov_len = sizeof(pkt);    struct cmsghdr cmsgcmsg[1];    msg.msg_control = cmsgcmsg;  msg.msg_controllen = sizeof(struct cmsghdr);      nRet = recvmsg(udpSocket, &msg, 0);    if (nRet > 0) {      struct cmsghdr *cmsg;      for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg,cmsg)) {        if ((cmsg->cmsg_level == IPPROTO_IP) && (cmsg->cmsg_type == IP_TOS) &&      (cmsg->cmsg_len) ){                int tos = *(uint8_t *)CMSG_DATA(cmsg);                int isecn =  ((tos & INET_ECN_MASK) == INET_ECN_CE);              printf("the tos = %i , is ecn = %d \n", tos, isecn);          }      }  


Solution:1

I have finally managed to solve the problem and am adding the code here for others to use. I hope this is of help to others. This one is for IP_TTL:

Set the UDPSocket to receive IP_TTL values:

int ttl = 60;  if(setsockopt(udpSocket, IPPROTO_IP, IP_RECVTTL, &ttl,sizeof(ttl))<0)   {      printf("cannot set recvttl\n");  }   else  {      printf("socket set to recvttl\n");  }  

And retrieve the IP_TTL values from every packet in the following manner(The following program can retrieve the data message via iov[0], code snippet given below):

struct msghdr msg;   struct iovec iov[1];    memset(&msg, '\0', sizeof(msg));  msg.msg_iov = iov;  msg.msg_iovlen = 1;  iov[0].iov_base = (char *) &pkt;  iov[0].iov_len = sizeof(pkt);    int *ttlptr=NULL;  int received_ttl = 0;    int cmsg_size = sizeof(struct cmsghdr)+sizeof(received_ttl); // NOTE: Size of header + size of data  char buf[CMSG_SPACE(sizeof(received_ttl))];  msg.msg_control = buf; // Assign buffer space for control header + header data/value  msg.msg_controllen = sizeof(buf); //just initializing it    nRet = recvmsg(udpSocket, &msg, 0);        if (nRet > 0) {      struct cmsghdr *cmsg;      for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg,cmsg)) {              if ((cmsg->cmsg_level == IPPROTO_IP) && (cmsg->cmsg_type == IP_TTL) &&            (cmsg->cmsg_len) ){                  ttlptr = (int *) CMSG_DATA(cmsg);                  received_ttl = *ttlptr;                  printf("received_ttl = %i and %d \n", ttlptr, received_ttl);                  break;             }      }  }  

The data message can be send and obtained in the following manner:

Sender Side:

struct DATA_to_SEND pkt;  struct msghdr msg;   struct iovec iov[1];    memset(&msg, '\0', sizeof(msg));  msg.msg_iov = iov;  msg.msg_iovlen = 1;  iov[0].iov_base = (char *) &pkt;  iov[0].iov_len = sizeof(pkt);  nRet = sendmsg(udpSocket, &msg,0);   

Receiver Side (assumption DATA_To_SEND has a parameter named "seq"):

struct DATA_to_SEND pkt;  seqNum = ((struct DATA_to_SEND *) iov[0].iov_base)->seq;  

The following is for IP_TOS. Set the socket to receive IP_TOS:

unsigned char set = 0x03;  if(setsockopt(udpSocket, IPPROTO_IP, IP_RECVTOS, &set,sizeof(set))<0)   {      printf("cannot set recvtos\n");  }   else  {          printf("socket set to recvtos\n");  

and Retrieve the IP_TOS value from every packet header by:

 struct PC_Pkt pkt;   int *ecnptr;   unsigned char received_ecn;     struct msghdr msg;    struct iovec iov[1];     memset(&msg, '\0', sizeof(msg));   msg.msg_iov = iov;    msg.msg_iovlen = 1;   iov[0].iov_base = (char *) &pkt;   iov[0].iov_len = sizeof(pkt);     int cmsg_size = sizeof(struct cmsghdr)+sizeof(received_ecn);   char buf[CMSG_SPACE(sizeof(received_ecn))];   msg.msg_control = buf;   msg.msg_controllen = sizeof(buf);      nRet = recvmsg(udpSocket, &msg, 0);     if (nRet > 0) {  struct cmsghdr *cmsg;  for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;  cmsg = CMSG_NXTHDR(&msg,cmsg)) {           if ((cmsg->cmsg_level == IPPROTO_IP) &&            (cmsg->cmsg_type == IP_TOS) && (cmsg->cmsg_len) ){                  ecnptr = (int *) CMSG_DATA(cmsg);          received_ecn = *ecnptr;          int isecn =  ((received_ecn & INET_ECN_MASK) == INET_ECN_CE);                    printf("received_ecn = %i and %d, is ECN CE marked = %d \n", ecnptr, received_ecn, isecn);                      break;      }       }  }  


Solution:2

I created simple example of using and setsockopt() to send ECN capable packets and get ECN bits from received packets using recvmsg() as well getsockopt(). You can find it at:

https://gist.github.com/jirihnidek/95c369996a81be1b854e

Using getsockopt() probably will not work at other platforms then Linux, but you can use it with recv() and recvfrom() functions.

BTW: INET_ECN_MASK, INET_ECN_CE, etc. are not defined in in.h. Thus you will need to include linux kernel headers (IMHO overshoot) or you can (re)define your own constants:

#define INET_ECN_NOT_ECT    0x00    /* ECN was not enabled */  #define INET_ECN_ECT_1      0x01    /* ECN capable packet */  #define INET_ECN_ECT_0      0x02    /* ECN capable packet */  #define INET_ECN_CE         0x03    /* ECN congestion */  #define INET_ECN_MASK       0x03    /* Mask of ECN bits */  


Solution:3

You normally access to the ToS field through getsockopt() / setsockopt(), but it seems to be very implementation dependent. You might want to look around do_ip_setsockopt() in the kernel sources, in the Linux kernel tree at linux/net/ipv4/ip_sockglue.c

Your best friend to navigate the source is there.


Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »