Reputation: 21
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,
EAGAIN
in non-blocking mode, which means the UDP send socket buffer is full. However, the full qdisc just drop the packet when the packet was enqueued, why UDP send buffer would be full? ENOBUFS
returned, even I set the length of qdisc smaller by ifconfig txqueuelen 10
. The program did setsockopt(sockfd, SOL_IP, IP_RECVERR,(char*)&val, sizeof(val));
, but no error returned. Because there is a few example about the usage of IP_RECVERR, if there is any wrong usage in my code, please tell me. Thank so much!#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
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