Linux: Can Recvmsg be used to receive the IP_TOS of every incoming packet
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);
}
}
开发者_Go百科
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;
}
}
}
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 */
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.
精彩评论