rslai
rslai

Reputation: 21

Would UDP sendto in linux return ENOBUFS?

I have traced the linux source code along the whole udp sending process.

I think the return value of sendto() is returned by rc = q->enqueue(skb, q, &to_free) & NET_XMIT_MASK;. When the qdisc is full, it will drop the packet and return NET_XMIT_DROP, then the value will be converted to ENOBUFS.

Below is my question. I wrote a udp client application. It sent out udp packet, and I let it continuously received PFC pause frame, which caused packet buffer of NIC, txring of driver,and qdisc get full. However,

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/errno.h>
#include <fcntl.h>
#include <time.h>
#include <linux/errqueue.h>


#define UDP_TEST_PORT       5000
#define UDP_SERVER_IP       "10.0.10.34"

int ip_control_msg( struct cmsghdr *msg )
{
    int ret = 0;
    switch( msg->cmsg_type ){
    case IP_RECVERR:
        {
            struct sock_extended_err *exterr;
            exterr = (struct sock_extended_err *)(CMSG_DATA(msg));
            printf("ee_errno: %u\n", exterr->ee_errno );
            printf("ee_origin: %u\n", exterr->ee_origin );
            printf("ee_type: %u\n", exterr->ee_type );
            printf("ee_code: %u\n", exterr->ee_code );
            printf("ee_pad: %u\n", exterr->ee_pad );
            printf("ee_info: %u\n", exterr->ee_info );
            printf("ee_data: %u\n", exterr->ee_data );
        }
        ret = -1;
        break;
    default:
        break;
    }
    return ret;
}

int control_msg( struct msghdr *msg )
{
    int ret = 0;
    struct cmsghdr *control_msg = CMSG_FIRSTHDR( msg );
    while( control_msg != NULL ){
        switch( control_msg->cmsg_level ){
        case SOL_IP:
            ret = ip_control_msg( control_msg );
            break;
        default:
            break;
        }
        control_msg = CMSG_NXTHDR( msg, control_msg );
    }
    return ret;
}

int main(int argC, char* arg[])
{
    struct sockaddr_in addr;
    int sockfd, len = 0;    
    int addr_len = sizeof(struct sockaddr_in);      
    char buffer[4096];

    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        perror("socket");
        exit(1);
    }

    fcntl(sockfd,F_SETFL,O_NONBLOCK);

    int val = 1;
    setsockopt(sockfd, SOL_IP, IP_RECVERR,(char*)&val, sizeof(val));

    bzero(&addr, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(UDP_TEST_PORT);
    addr.sin_addr.s_addr = inet_addr(UDP_SERVER_IP);

    int rtn = 0;
    while(1) {
        bzero(buffer, sizeof(buffer));
        sprintf(buffer,"%d",cnt);
        rtn = sendto(sockfd, buffer, strlen(buffer), 0, (struct sockaddr *)&addr, addr_len);
        if(rtn<0){
           printf("%d\n",errno);
        }

        char buf[1024];
        char control_buf[1024];
        struct msghdr msg;
        struct iovec iov = { buf, 1024 };
        memset( &msg, 0, sizeof(msg) );
        msg.msg_iov = &iov;
        msg.msg_iovlen = 1;
        msg.msg_control = &control_buf;
        msg.msg_controllen = 1024;

        int bread = recvmsg( sockfd, &msg, MSG_ERRQUEUE );
        if( bread == -1 ){
            continue;
        }
        if( control_msg( &msg ) >= 0 )
            printf("successed!\n");
        else
            printf("failed!\n");
    }

    return 0;
}

Upvotes: 2

Views: 1925

Answers (1)

the kamilz
the kamilz

Reputation: 1988

Yes it can and there may be nothing wrong with your code but you can still get ENOBUFS. Because the system may be overloaded by other processes. Check this out: https://www.unix.com/programming/237027-how-avoid-no-buffer-space-available-c-socket.html

Upvotes: 1

Related Questions