diff options
author | Helge Deller <deller@gmx.de> | 2025-01-19 05:26:10 +0100 |
---|---|---|
committer | Helge Deller <deller@gmx.de> | 2025-01-24 13:58:40 +0100 |
commit | f65464ce6d00fdec726dbd9f8c8c543c3feb2924 (patch) | |
tree | b7c4ff30f6c39ba6de03242eb83b5d75d7fd0dd5 | |
parent | 017fc6620f2513c1d0217289c48be4c51e0167a7 (diff) | |
download | qemu-f65464ce6d00fdec726dbd9f8c8c543c3feb2924.zip qemu-f65464ce6d00fdec726dbd9f8c8c543c3feb2924.tar.gz qemu-f65464ce6d00fdec726dbd9f8c8c543c3feb2924.tar.bz2 |
linux-user: netlink: Add emulation of IP_MULTICAST_IF
Add IP_MULTICAST_IF and share the code with IP_ADD_MEMBERSHIP / IP_DROP_MEMBERSHIP.
Sharing the code makes sense, because the manpage of ip(7) says:
IP_MULTICAST_IF (since Linux 1.2)
Set the local device for a multicast socket. The argument
for setsockopt(2) is an ip_mreqn or (since Linux 3.5)
ip_mreq structure similar to IP_ADD_MEMBERSHIP, or an
in_addr structure. (The kernel determines which structure
is being passed based on the size passed in optlen.) For
getsockopt(2), the argument is an in_addr structure.
Signed-off-by: Helge Deller <deller@gmx.de>
Reviewed-by: Laurent Vivier <laurent@vivier.eu>
-rw-r--r-- | linux-user/syscall.c | 20 |
1 files changed, 14 insertions, 6 deletions
diff --git a/linux-user/syscall.c b/linux-user/syscall.c index df8609b..6ee0238 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -2130,16 +2130,23 @@ static abi_long do_setsockopt(int sockfd, int level, int optname, } ret = get_errno(setsockopt(sockfd, level, optname, &val, sizeof(val))); break; + case IP_MULTICAST_IF: case IP_ADD_MEMBERSHIP: case IP_DROP_MEMBERSHIP: { struct ip_mreqn ip_mreq; struct target_ip_mreqn *target_smreqn; + int min_size; QEMU_BUILD_BUG_ON(sizeof(struct ip_mreq) != sizeof(struct target_ip_mreq)); - if (optlen < sizeof (struct target_ip_mreq) || + if (optname == IP_MULTICAST_IF) { + min_size = sizeof(struct in_addr); + } else { + min_size = sizeof(struct target_ip_mreq); + } + if (optlen < min_size || optlen > sizeof (struct target_ip_mreqn)) { return -TARGET_EINVAL; } @@ -2149,13 +2156,14 @@ static abi_long do_setsockopt(int sockfd, int level, int optname, return -TARGET_EFAULT; } ip_mreq.imr_multiaddr.s_addr = target_smreqn->imr_multiaddr.s_addr; - ip_mreq.imr_address.s_addr = target_smreqn->imr_address.s_addr; - if (optlen == sizeof(struct target_ip_mreqn)) { - ip_mreq.imr_ifindex = tswapal(target_smreqn->imr_ifindex); - optlen = sizeof(struct ip_mreqn); + if (optlen >= sizeof(struct target_ip_mreq)) { + ip_mreq.imr_address.s_addr = target_smreqn->imr_address.s_addr; + if (optlen >= sizeof(struct target_ip_mreqn)) { + __put_user(target_smreqn->imr_ifindex, &ip_mreq.imr_ifindex); + optlen = sizeof(struct ip_mreqn); + } } unlock_user(target_smreqn, optval_addr, 0); - ret = get_errno(setsockopt(sockfd, level, optname, &ip_mreq, optlen)); break; } |