Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c

linux-kernel

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

     }
    }
like image 655
Mayutan Avatar asked May 21 '10 10:05

Mayutan


2 Answers

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;
    }
     }
}
like image 151
Mayutan Avatar answered Oct 21 '22 18:10

Mayutan


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 */
like image 28
JiriHnidek Avatar answered Oct 21 '22 19:10

JiriHnidek