Hi,
actually you are right that this issue is related to control messages and not
to the send buffer length. But the length of the control message is checked in
combination with the data to be sent in uip_socket.c:
Let's assume we are sending data with the maximum send buffer size (e.g. 1024).
Since clen is NOT evaluated in the first if statement, EMSGSIZE is NOT returned.
But the second if statement becomes true, since clen is considered here. Hence
either EWOULDBLOCK (snderr) is returned or sbwait() is called.
if ((atomic && resid > so->so_snd.sb_hiwat) ||
(so->so_proto->pr_domain->dom_family != AF_LOCAL &&
clen > so->so_snd.sb_hiwat))
snderr(EMSGSIZE);
if (space < clen ||
(space - clen < resid &&
(atomic || space < so->so_snd.sb_lowat))) {
if ((so->so_state & SS_NBIO) || (flags & MSG_DONTWAIT))
snderr(EWOULDBLOCK);
sbunlock(&so->so_snd);
error = sbwait(&so->so_snd);
I attached a small example, which
- sets the sockets maximum buffer length
- enables/disables non-blocking IO and
- calls sendmsg with the maximum buffer size including a control message
(IP_SENDSRCADDR).
-----Original Message-----
From: Jeremie Courreges-Anglas [mailto:[email protected]]
Sent: Dienstag, 25. April 2017 15:00
To: Markert, Alexander (DF FA AS DH FTH 2)
Cc: [email protected]
Subject: Re: uip_socket.c: issues when using sendmsg() with small send buffers
and the new 6.1 control message (IP_SENDSRCADDR)
"Markert, Alexander" <[email protected]> writes:
> Hi,
Hi,
> the length of the data to be sent and in addition the length of the
> associated control message (e.g. the source address: IP_SENDSRCADDR) musn't
> exceed the maximum size of the socket's send buffer (SO_SNDBUF).
> Otherwise EMSGSIZE is returned. That is ok.
>
> However, if the data to be sent fits into the send buffer, but exceeds in
> combination with the control message:
> - either EWOULDBLOCK is returned when using non-blocking IO or
> - send operation blocks in sbwait when using blocking IO
>
> It seems that this situation will never dissolve, i.e. the sending
> application will be blocked forever.
>
> In our opinion either EMSGSIZE should be returned instead in this case
> (like e.g. FreeBSD 11.0 does) or OpenBSD should reserve some space
> (comparable to MSG_OOB) in addition to the maximum size of the
> socket's send buffer for the control messages in order to avoid such problems.
>
> What do you think about that behavior and the described alternatives?
Control messages are tricky. One port (net/dnsmasq) is currently failing, and
appears to hang like you describe. The issue is related to control messages,
but it's not a lack of send buffer space.
So how can one reproduce the bug you're talking about? Do you have a test case?
--
jca | PGP : 0x1524E7EE / 5135 92C1 AD36 5293 2BDF DDCC 0DFA 74AE 1524 E7EE
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <netinet/in.h>
// change configuration data to your needs
#define CFG_SOURCE_ADDRESS "192.168.88.135"
#define CFG_DESTINATION_ADDRESS "192.168.57.44"
#define CFG_PORT 5000
#define CFG_SO_MAX_SEND_BUFFER 1024
int main(int argc, char* argv[])
{
// open socket
int so = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (so < 0)
{
printf("Opening socket failed: %d\n", errno);
return -1;
}
// set maximum size of socket's send buffer
unsigned int max_size_send_buffer = CFG_SO_MAX_SEND_BUFFER;
if (setsockopt(so, SOL_SOCKET, SO_SNDBUF, (char*)(&max_size_send_buffer),
sizeof(max_size_send_buffer)) < 0)
{
printf("Adjusting socket send buffer failed: %d\n", errno);
close(so);
return -1;
}
// if non-blocking IO is disabled, sendmsg hangs is returned by sendmsg
// unsigned long enable_non_blocking_io = 0;
// if non-blocking IO is enabled EWOULDBLOCK is returned by sendmsg
unsigned long enable_non_blocking_io = 1;
if (ioctl(so, FIONBIO, &enable_non_blocking_io) < 0)
{
printf("Enabling/disabling non-blocking IO failed: %d\n", errno);
close(so);
return -1;
}
msghdr message_header = { 0 };
iovec io = { 0 };
char control_message[CMSG_SPACE(sizeof(struct in_addr))] = { 0 };
sockaddr_in remote = { 0 };
char data[CFG_SO_MAX_SEND_BUFFER] = {0};
// use this buffer, if you want to send a smaller buffer than
CFG_SO_MAX_SEND_BUFFER
// char data[256] = {0};
// setup buffer to be sent
io.iov_base = data;
io.iov_len = sizeof(data);
message_header.msg_iovlen = 1;
message_header.msg_iov = &io;
// setup remote address
remote.sin_family = AF_INET;
remote.sin_addr.s_addr = inet_addr(CFG_DESTINATION_ADDRESS);
remote.sin_port = htons(CFG_PORT);
message_header.msg_name = &remote;
message_header.msg_namelen = sizeof(remote);
// setup configuration for source address
message_header.msg_control = control_message;
message_header.msg_controllen = sizeof(control_message);
cmsghdr *cmsg = CMSG_FIRSTHDR(&message_header);
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_SENDSRCADDR;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
struct in_addr* source_address = (in_addr*)(CMSG_DATA(cmsg));
source_address->s_addr = inet_addr(CFG_SOURCE_ADDRESS);
// for blocking IO sendmsg hangs
// for non-blocking IO sendmsg returns EWOULDBLOCK (instead of expected
EMSGSIZE)
printf("calling sendmsg...\n");
int sent_bytes = sendmsg(so, &message_header, 0);
if (sent_bytes < 0)
{
printf("sendmsg failed: %d\n", errno);
}
else
{
printf("Bytes sent: %d\n", sent_bytes);
}
close(so);
printf("Leaving main...\n");
return 0;
}