Commit 22bf4ee9 authored by Neng Chen's avatar Neng Chen Committed by Laurent Vivier
Browse files

linux-user: Add support for setsockopt() options IPV6_<ADD|DROP>_MEMBERSHIP

Add support for the option IPV6_<ADD|DROP>_MEMBERSHIP of the syscall
setsockopt(). This option controls membership in multicast groups.
Argument is a pointer to a struct ipv6_mreq.

The glibc <netinet/in.h> header defines the ipv6_mreq structure,
which includes the following members:

  struct in6_addr  ipv6mr_multiaddr;
  unsigned int     ipv6mr_interface;

Whereas the kernel in its <linux/in6.h> header defines following
members of the same structure:

  struct in6_addr  ipv6mr_multiaddr;
  int              ipv6mr_ifindex;

POSIX defines ipv6mr_interface [1].

__UAPI_DEF_IVP6_MREQ appears in kernel headers with v3.12:

  cfd280c91253 net: sync some IP headers with glibc

Without __UAPI_DEF_IVP6_MREQ, kernel defines ipv6mr_ifindex, and
this is explained in cfd280c91253:

  "If you include the kernel headers first you get those,
  and if you include the glibc headers first you get those,
  and the following patch arranges a coordination and
  synchronization between the two."

So before 3.12, a program can't include both <netinet/in.h> and
<linux/in6.h>.

In linux-user/syscall.c, we only include <netinet/in.h> (glibc) and
not <linux/in6.h> (kernel headers), so ipv6mr_interface is the one
to use.

[1] http://pubs.opengroup.org/onlinepubs/009695399/basedefs/netinet/in.h.html



Signed-off-by: default avatarNeng Chen <nchen@wavecomp.com>
Signed-off-by: default avatarAleksandar Markovic <amarkovic@wavecomp.com>
Reviewed-by: default avatarLaurent Vivier <laurent@vivier.eu>
Message-Id: <1560953834-29584-2-git-send-email-aleksandar.markovic@rt-rk.com>
Signed-off-by: default avatarLaurent Vivier <laurent@vivier.eu>
parent f31dddd2
Loading
Loading
Loading
Loading
+19 −0
Original line number Diff line number Diff line
@@ -1892,6 +1892,25 @@ static abi_long do_setsockopt(int sockfd, int level, int optname,
                                       &pki, sizeof(pki)));
            break;
        }
        case IPV6_ADD_MEMBERSHIP:
        case IPV6_DROP_MEMBERSHIP:
        {
            struct ipv6_mreq ipv6mreq;

            if (optlen < sizeof(ipv6mreq)) {
                return -TARGET_EINVAL;
            }

            if (copy_from_user(&ipv6mreq, optval_addr, sizeof(ipv6mreq))) {
                return -TARGET_EFAULT;
            }

            ipv6mreq.ipv6mr_interface = tswap32(ipv6mreq.ipv6mr_interface);

            ret = get_errno(setsockopt(sockfd, level, optname,
                                       &ipv6mreq, sizeof(ipv6mreq)));
            break;
        }
        default:
            goto unimplemented;
        }