Я пытаюсь отправить пакет UDP со значением TOS, установленным с помощью sendmsg() в Linux с ядром 2.6.18. Но вызов завершается с ошибкой «Неверный аргумент». Если я отключу вспомогательную часть данных (см. флаг компиляции условий USE_IP_TOS), то sendmsg() завершится успешно. Ниже приведен код и вывод:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <net/if.h>
#define UDP_PORT 45897
int SendMsgWithIPTOS(int sockfd, unsigned char *pBuf, size_t bufLen, int tos)
{
unsigned int toIp;
struct sockaddr_in to_addr;
struct msghdr msg;
struct iovec iov[1];
unsigned char cmsg[ CMSG_SPACE(sizeof(int)) ];
struct cmsghdr *cmsgptr = NULL;
size_t sendBytes = 0;
toIp = 0x0a214401;
to_addr.sin_addr.s_addr = htonl(toIp);
to_addr.sin_family = AF_INET;
to_addr.sin_port = htons(UDP_PORT);
memset(&iov, 0, sizeof(iov));
memset(&cmsg, 0, sizeof(cmsg));
iov[0].iov_base = pBuf;
iov[0].iov_len = bufLen;
memset(&msg, 0, sizeof (struct msghdr));
msg.msg_name = &to_addr;
msg.msg_namelen = sizeof(to_addr);
msg.msg_iov = iov;
msg.msg_iovlen = 1;
#ifndef USE_IP_TOS
msg.msg_control = NULL;
msg.msg_controllen = 0;
#else
msg.msg_control = cmsg;
msg.msg_controllen = sizeof(cmsg);
cmsgptr = CMSG_FIRSTHDR(&msg);
cmsgptr->cmsg_level = IPPROTO_IP;
cmsgptr->cmsg_type = IP_TOS;
cmsgptr->cmsg_len = CMSG_LEN(sizeof(int));
memcpy(CMSG_DATA(cmsgptr), (unsigned char*)&tos, sizeof(int));
//*((int *) CMSG_DATA (cmsgptr)) = tos;
msg.msg_controllen = CMSG_SPACE(sizeof(int));
#endif
msg.msg_flags = 0;
if ((sendBytes = sendmsg(sockfd, &msg, 0)) == -1)
{
fprintf(stderr, "sendmsg() failed (%s) (%d)\n", strerror(errno), errno);
}
else
{
fprintf(stderr, "sent %d bytes \n", sendBytes);
}
return sendBytes;
}
int main()
{
int32_t sockfd, retCode;
struct sockaddr_in sockaddr;
socklen_t addrlen = sizeof(sockaddr);
unsigned char buf[1024] = { 0 };
size_t len = sizeof(buf);
int optval;
/* Create a UDP socket */
if((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
{
fprintf(stderr, "socket failed (%s)\n", strerror(errno));
return sockfd;
}
/* Bind the socket to the given port */
memset(&sockaddr, 0, addrlen);
sockaddr.sin_addr.s_addr = INADDR_ANY;
sockaddr.sin_family = AF_INET;
sockaddr.sin_port = htons(UDP_PORT);
if ((retCode = bind(sockfd, (const struct sockaddr *)&sockaddr, addrlen)) < 0)
{
fprintf(stderr, "bind failed (%s)\n", strerror(errno));
close(sockfd);
return retCode;
}
#if 0 // set the DSCP value using setsockopt
/* Set the DSCP value to Expedited Forwarding (0x2e) */
optval = 0xb8;
if (setsockopt(sockfd, IPPROTO_IP, IP_TOS, (void*)&optval, sizeof(optval)) < 0)
{
fprintf(stderr, "Failed to set DSCP value (%s)\n", strerror(errno));
close(sockfd);
return retCode;
}
else
{
socklen_t optlen = sizeof(optval);
fprintf(stderr, "DSCP value to set to %u\n", (optval >> 2));
optval = 0;
if (getsockopt(sockfd, IPPROTO_IP, IP_TOS, (void*)&optval, &optlen) < 0)
{
fprintf(stderr, "Failed to retrieve ip_tos value\n");
}
else
{
fprintf(stderr, "Retrieved dscp value %u optlen (%d)\n", (optval >> 2), optlen);
}
}
#endif
optval = 0xb8; // Expedited Forwarding
while (1)
{
retCode = SendMsgWithIPTOS(sockfd, buf, len, optval);
sleep(1);
}
return retCode;
}
Output:
-------
$
$ gcc -o ip_tos sockopt_test.c -Wall -DUSE_IP_TOS
$
$ ./ip_tos
sendmsg() failed (Invalid argument) (22)
sendmsg() failed (Invalid argument) (22)
$
$ gcc -o ip_tos sockopt_test.c -Wall
sockopt_test.c: In function ‘SendMsgWithIPTOS’:
sockopt_test.c:22: warning: unused variable ‘cmsgptr’
$
$ ./ip_tos
sent 1024 bytes
sent 1024 bytes
$
Обратите внимание, что я пытался включить/отключить часть кода setsockopt() для целей тестирования. Но это не помогает
Я не знаю, что я делаю неправильно. Любая помощь в поиске проблемы с кодом или какой-либо указатель для дальнейшей отладки проблемы приветствуется.
Спасибо, Равендра.