Subject: kern/29750: send/sendto/sendmsg don't block when no buffer space is available
To: None <kern-bug-people@netbsd.org, gnats-admin@netbsd.org,>
From: None <manu@netbsd.org>
List: netbsd-bugs
Date: 03/20/2005 22:23:00
>Number: 29750
>Category: kern
>Synopsis: send/sendto/sendmsg don't block when no buffer space is available
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: kern-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Sun Mar 20 22:23:00 +0000 2005
>Originator: Emmanuel Dreyfus
>Release: NetBSD-current (3.99.1)
>Organization:
The NetBSD Project
>Environment:
>Description:
NetBSD man page and Single Unix Specification say that if there is no kernel buffer space available, send/sendto/sendmsg should block if the socket was not set for non blocking I/O.
Currently, we don't block, we return ENOBUFS, regardless if non blocking I/O was set. This breaks the standard.
>How-To-Repeat:
Use this test program with the numeric IP of a host near you as the first argument (NB: this will flood the network to get out of buffers. At mine it fails after sending 317 kB)
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <netdb.h>
#include <time.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
int
main(ac, av)
int ac;
char **av;
{
int sd;
int i;
struct sockaddr_in src_addr;
struct sockaddr_in send_addr;
char *packet;
size_t packet_size;
if (ac != 2) {
printf("Usage: test remote-host-ip\n");
return 1;
}
if ((sd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
printf("Cannot create UDP/IP socket: %s\n", strerror(errno));
exit(-1);
}
(void)memset(&src_addr, 0, sizeof(src_addr));
src_addr.sin_family = AF_INET;
src_addr.sin_len = sizeof(src_addr);
src_addr.sin_addr.s_addr = htonl(0x00000000);
src_addr.sin_port = htons(5002);
(void)memset(&send_addr, 0, sizeof(send_addr));
send_addr.sin_family = AF_INET;
send_addr.sin_len = sizeof(send_addr);
if (inet_aton(av[1], &send_addr.sin_addr) != 1) {
printf("inet_aton: %s\n", strerror(errno));
exit(-1);
}
send_addr.sin_port = htons(5002);
if ((bind(sd, (struct sockaddr *)&src_addr, sizeof(src_addr))) == -1) {
printf("Cannot bind to UDP port 5002: %s\n", strerror(errno));
exit(-1);
}
packet_size = 1024;
if ((packet = malloc(packet_size)) == NULL) {
perror("Cannot allocate buffer");
exit(-1);
}
bzero(packet, sizeof(*packet));
for (i = 0; i < (1024 * 1024); i++) {
if (sendto(sd, packet, packet_size, 0,
(struct sockaddr *)&send_addr, sizeof(send_addr)) == -1) {
if (errno == ENOBUFS) {
printf("TEST FAILED (%d packets)\n", i);
exit(1);
} else {
perror("Send failed");
}
}
}
printf("TEST PASSED\n");
return 0;
}
>Fix:
None yet. Maybe in src/sys/kern/uipc_syscalls.c:sendit() loop around so->so_send(), sleeping when we get ENOBUFS? Of course that require the code that frees the buffer to wake us up.