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;
}

Reply via email to