2017-03-06 143 views
0

我试图发送一个UDP数据包,其TMS值设置为使用内核2.6.18的Linux上的sendmsg()。但是调用失败,错误'无效参数'。 如果我禁用辅助数据部分(请参阅条件编译标志USE_IP_TOS),则sendmsg()成功。以下是代码和输出:发送带有IP_TOS辅助数据的UDP数据包时sendmsg()失败,并且发送UDP数据包

#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的()部分用于测试目的。但这并没有帮助

我不知道我在做什么错。任何帮助找到问题的代码或一些指针进一步调试问题是非常感谢。

感谢 Raveendra

回答

-1

我想你的代码,稍微改变(2个变化:其它IP和宏观USE_IP_TOS定义),在我的Centos [内核= 3.10.0] =>的Windows测试sendings - 它的工作原理。我在我的Wireshark UPD包中看到DSCP字段设置为'快速转发'。

您的代码有效。

虽然,您无法使用无符号变量[size_t sendBytes]作为签名的sys-call返回值。 memset -s也是无用的。 也许你的问题是由安全原因(但奇怪的errno)造成的。 或者你的内核太旧了;或者支持这个功能是从内核中编译出来的。 虽然系统可能会抱怨使用EINVAL的坏ToS(例如= 2)。

+1

您是如何更改代码的?请尝试优化您的答案。 –

+1

这不提供问题的答案。一旦你有足够的[声誉](https://stackoverflow.com/help/whats-reputation),你将可以[对任何帖子发表评论](https://stackoverflow.com/help/privileges/comment);相反,[提供不需要提问者澄清的答案](https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-c​​an- I-DO-代替)。 - [来自评论](/ review/low-quality-posts/16968281) – Milap