send.2: Document EACCES error case for UDP

It seems sendto() can return EACCES for UDP as well; the current
man page in git only says it can return EACCES for Unix sockets.

I was able to make sendto() return EACCES if I try to send from
192.168.1.1/24 to 192.168.1.0. I think the relevant code (in
kernel 2.6.38, but also present in 2.6.7 and 2.6.32, the 2 kernels
we use) is this (net/ipv4/udp.c, udp_sendmsg()):

 910                err = -EACCES;
 911                if ((rt->rt_flags & RTCF_BROADCAST) &&
 912                    !sock_flag(sk, SOCK_BROADCAST))
 913                        goto out;

So I guess if the kernel finds a route to the destination and
it's a broadcast route (and the socket doesn't have the broadcast
flag), then it returns EACCES.

I can verify the behavior with a very simple program (attached).
I've run it on my Ubuntu 10.10 (2.6.35 kernel) and got this:

stefan@spuiu-vml2:~/src/test/broadcast$ ./broadcast_test 10.205.20.94
10.205.20.1
sendto() returned 4
stefan@spuiu-vml2:~/src/test/broadcast$ ./broadcast_test 10.205.20.94
10.205.20.0
sendto() returned negative, errno: 13/Permission denied

(10.205.20.94 is my local IP, of course).

=====
    #include <stdio.h>

    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <errno.h>
    #include <stdlib.h>

    int main(int argc, char **argv)
    {
        int sock;

        if (argc < 2) {
            printf("Usage: %s local_address destination_address\n", argv[0]);
            exit(1);
        }

        sock = socket(AF_INET, SOCK_DGRAM, 0);
        if (sock < 0) {
            perror("socket");
            return -1;
        }

        struct sockaddr_in local_addr;
        local_addr.sin_family = AF_INET;
        local_addr.sin_port = htons(1234);
        local_addr.sin_addr.s_addr = inet_addr(argv[1]);
        int ret = bind(sock, (struct sockaddr *) &local_addr, sizeof(local_addr));
        if (ret < 0) {
            perror("bind");
            return -1;
        }

        struct sockaddr_in remote_addr;
        remote_addr.sin_family = AF_INET;
        remote_addr.sin_port = htons(1234);
        remote_addr.sin_addr.s_addr = inet_addr(argv[2]);
        ret = sendto(sock, "blah", 4, 0, (struct sockaddr *)&remote_addr, sizeof(remote_addr));
        if (ret < 0) {
            printf("sendto() returned negative, errno: %d/%m\n", errno);
        }
        else {
            printf("sendto() returned %d\n", ret);
        }

        return 0;
    }
=====

Signed-off-by: Michael Kerrisk <mtk.manpages@gmail.com>
This commit is contained in:
Stefan Puiu 2012-04-23 08:15:01 +12:00 committed by Michael Kerrisk
parent df21098dee
commit 4c49bf908c
1 changed files with 4 additions and 1 deletions

View File

@ -35,7 +35,7 @@
.\" Modified Oct 2003 by aeb
.\" Modified 2004-07-01 by mtk
.\"
.TH SEND 2 2012-02-27 "Linux" "Linux Programmer's Manual"
.TH SEND 2 2012-04-23 "Linux" "Linux Programmer's Manual"
.SH NAME
send, sendto, sendmsg \- send a message on a socket
.SH SYNOPSIS
@ -288,6 +288,9 @@ or search permission is denied for one of the directories
the path prefix.
(See
.BR path_resolution (7).)
.sp
(For UDP sockets) An attempt was made to send to a
network/broadcast address as though it was a unicast address.
.TP
.BR EAGAIN " or " EWOULDBLOCK
.\" Actually EAGAIN on Linux