sendto() on UDP can return EACCES

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

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:

EACCES
(For  UNIX  domain  sockets, which are identified by pathname) Write
permission is denied on the destination socket file, or
              search permission is denied for one of the directories
the path prefix.  (See path_resolution(7).)

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).

How about updating the send.2 page like this?

diff --git a/man2/send.2 b/man2/send.2
index 69287fb..4d2faed 100644
--- a/man2/send.2
+++ b/man2/send.2
@@ -288,6 +288,8 @@ or search permission is denied for one of the directories
 the path prefix.
 (See
 .BR path_resolution (7).)
+(For UDP sockets) An attempt was made to sendto() to a
+network/broadcast address like it was a unicast address.
 .TP
 .BR EAGAIN " or " EWOULDBLOCK
 .\" Actually EAGAIN on Linux

Thanks,
Stefan.
#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;
}

[Index of Archives]     [Kernel Documentation]     [Netdev]     [Linux Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux