2017-03-06 198 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