0

I try to get a program running on OpenWRT (kernel ver.: 4.14.215, musl libc ver.: 1.1.24) which implements RFC8157 (a new tunneling protocol). Unfortunately the guy who wrote it doesn't seem to maintain it anymore.

At some point it writes its first message to a raw ipv6 socket via sendmsg(). Unfortunately sendmsg() returns EACCES. I am pretty new to system programming and do not really have a clue what to look for.

I have tried the following:

#> ls -l /proc/[pid]/fd/*
lrwx------    1 root     root            64 Jan 25 17:41 /proc/22727/fd/0 -> /dev/pts/0
lrwx------    1 root     root            64 Jan 25 17:41 /proc/22727/fd/1 -> /dev/pts/0
lrwx------    1 root     root            64 Jan 25 17:41 /proc/22727/fd/2 -> /dev/pts/0
lrwx------    1 root     root            64 Jan 25 17:41 /proc/22727/fd/3 -> socket:[1293688]

#> ls -l /proc/[pid]/fdinfo/*
pos:    0
flags:  02
mnt_id: 8

So the socket seems to be opened in read/write mode.

lsof lists the socket as well. But for some reason with an ipv6 address of 0.

#> lsof | grep [pid]
openhybri 18018                      root    3u     raw6                 0t0      92469 00000000000000000000000000000000:002F->00000000000000000000000000000000:0000 st=07

The man page lists the attempt to send an UDP packet from a broadcast address to an anycast address as possible cause. But this seems not the case here. A raw IPv6 socket is not a UDP socket (isn't it?) and the src IP is a public one.

Everything is executed as root user.

#> id
uid=0(root) gid=0(root) groups=0(root)

As I am not really sure what to look for, here is the whole function:

sendmsg() is used in the last if statement.

bool send_grecpmessage(uint8_t msgtype, uint8_t tuntype, void *attributes, int attributes_size) {
    unsigned char buffer[MAX_PKT_SIZE] = {};
    int size = 0;

    /* GRE header */
    struct grehdr *greh = (struct grehdr *)(buffer + size);
    greh->flags_and_version = htons(GRECP_FLAGSANDVERSION);
    greh->proto = htons(GRECP_PROTO);
    greh->key = htonl(runtime.haap.bonding_key);
    size += sizeof(struct grehdr);

    /* GRECP header */
    struct grecphdr *grecph = (struct grecphdr *)(buffer + size);
    grecph->msgtype_and_tuntype = (msgtype << 4) | tuntype;
    size += sizeof(struct grecphdr);

    /* Add GRECP attributes */
    memcpy(buffer + size, attributes, attributes_size);
    size += attributes_size;

    /* Source & Destination */
    struct sockaddr_in6 src = {};
    src.sin6_family = AF_INET6;
    if (tuntype == GRECP_TUNTYPE_LTE) {
        src.sin6_addr = runtime.lte.interface_ip;
    } else {
        src.sin6_addr = runtime.dsl.interface_ip;
    }
    struct sockaddr_in6 dst = {};
    dst.sin6_family = AF_INET6;
    dst.sin6_addr = runtime.haap.ip;

    /* Construct control information */
    struct msghdr msgh = {};
    struct iovec msgiov = {};
    struct cmsghdr *c;
    struct unp_in_pktinfo {
        struct in6_addr ipi6_addr;
        int ipi6_ifindex;
    } *pi;
    msgh.msg_name = &dst;
    msgh.msg_namelen = sizeof(struct sockaddr_in6);
    msgiov.iov_base = buffer;
    msgiov.iov_len = size;
    msgh.msg_iov = &msgiov;
    msgh.msg_iovlen = 1;
    unsigned char control_buf[CMSG_LEN(sizeof(struct unp_in_pktinfo))] = {};
    msgh.msg_control = &control_buf;
    msgh.msg_controllen = CMSG_LEN(sizeof(struct unp_in_pktinfo));
    c = CMSG_FIRSTHDR(&msgh);
    c->cmsg_level = IPPROTO_IPV6;
    c->cmsg_type = IPV6_PKTINFO;
    c->cmsg_len = CMSG_LEN(sizeof(struct unp_in_pktinfo));
    pi = (struct unp_in_pktinfo *)CMSG_DATA(c);
    pi->ipi6_addr = src.sin6_addr;
    msgh.msg_controllen = c->cmsg_len;

    bool res = true;
    if (memcmp(&src.sin6_addr, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16) != 0) {
        if (sendmsg(sockfd, &msgh, 0) <= 0) {
            logger(LOG_ERROR, "Raw socket send failed: %s\n", strerror(errno));
            res = false;
        }
    } else {
        /* if we don't set a source ip, sendmsg() will use the ip of the outgoing interface
        ** and since the haap doesn't verify source ip's we would still get replies for our hellos
        */
        res = false;
    }

    /* TODO: check if sending failed due to a link failure and call send_grecpnotify_linkfailure if it did */

    return res;
}
Community
  • 1
  • 1

2 Answers2

0

You need root (or a sufficient subset as capabilities) to perform raw packet io. This is because ability to construct and send or capture arbitrary packets allows you to spoof or intercept traffic that's part of a connection belonging to another user or system network facilities. EACCES is telling you that you don't have sufficient permissions.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • I am logged in as a root user on the router and run every command as root. The executable in question as well. But thanks for the answer. The question is edited. – user8614223 Jan 25 '21 at 17:57
0

The problem was that the ip route wasn't set for the specific address I tried to reach.

ip route add [IP] via [gateway] dev [interface]

solved it.