From 5d012954fd61a722090b0637aa109595659eb927 Mon Sep 17 00:00:00 2001 From: wangyuwei Date: Tue, 16 Feb 2021 21:21:32 +0800 Subject: [PATCH] Avoid letting ipv6 addresses join ipv4 groups (#11015) Motivation: #10995 when `io.netty.channel.unix.Socket` is ipv6 and join a multicast group with ipv4 address will cause `io.netty.channel.ChannelException: setsockopt() failed: Invalid argument` (at least in `Linux centos.dev 4.18.0-240.10.1.el8_3.x86_64 #1 SMP Mon Jan 18 17:05:51 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux`) Modification: check if target group address is ipv6 before call `io.netty.channel.epoll.LinuxSocket#joinGroup(java.net.InetAddress, java.net.NetworkInterface, java.net.InetAddress)` I'm not sure if this modification is currect, but i checked source code of java NIO ``` Java_sun_nio_ch_Net_canJoin6WithIPv4Group0(JNIEnv* env, jclass cl) { #if defined(__APPLE__) /* IPV6_ADD_MEMBERSHIP can be used to join IPv4 multicast groups */ return JNI_TRUE; #else /* IPV6_ADD_MEMBERSHIP cannot be used to join IPv4 multicast groups */ return JNI_FALSE; #endif } ``` seems ipv6 address can't join ipv4 group except osx Result: test on `Linux 3.10.0-514.el7.x86_64 #1 SMP Tue Nov 22 16:42:41 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux` exception ` setsockopt() failed: Invalid argument` has fixed Fixes #10995 --- .../socket/AbstractDatagramTest.java | 12 +++++-- .../socket/DatagramMulticastTest.java | 14 ++++---- .../io/netty/channel/epoll/LinuxSocket.java | 14 +++++--- ...DatagramMulticastIpv6WithIpv4AddrTest.java | 32 +++++++++++++++++++ 4 files changed, 59 insertions(+), 13 deletions(-) create mode 100644 transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDatagramMulticastIpv6WithIpv4AddrTest.java diff --git a/testsuite/src/main/java/io/netty/testsuite/transport/socket/AbstractDatagramTest.java b/testsuite/src/main/java/io/netty/testsuite/transport/socket/AbstractDatagramTest.java index 003b786846..2076358120 100644 --- a/testsuite/src/main/java/io/netty/testsuite/transport/socket/AbstractDatagramTest.java +++ b/testsuite/src/main/java/io/netty/testsuite/transport/socket/AbstractDatagramTest.java @@ -35,7 +35,7 @@ public abstract class AbstractDatagramTest extends AbstractComboTestsuiteTest> newFactories() { - return SocketTestPermutation.INSTANCE.datagram(internetProtocolFamily()); + return SocketTestPermutation.INSTANCE.datagram(socketInternetProtocalFamily()); } @Override @@ -45,7 +45,7 @@ public abstract class AbstractDatagramTest extends AbstractComboTestsuiteTest> newFactories() { - return SocketTestPermutation.INSTANCE.datagram(internetProtocolFamily()); + return SocketTestPermutation.INSTANCE.datagram(socketInternetProtocalFamily()); } private InetSocketAddress newAnySocketAddress() throws UnknownHostException { - switch (internetProtocolFamily()) { + switch (socketInternetProtocalFamily()) { case IPv4: return new InetSocketAddress(InetAddress.getByName("0.0.0.0"), 0); case IPv6: @@ -182,7 +182,7 @@ public class DatagramMulticastTest extends AbstractDatagramTest { Enumeration addresses = iface.getInetAddresses(); while (addresses.hasMoreElements()) { InetAddress address = addresses.nextElement(); - if (internetProtocolFamily().addressType().isAssignableFrom(address.getClass())) { + if (socketInternetProtocalFamily().addressType().isAssignableFrom(address.getClass())) { return new InetSocketAddress(address, 0); } } @@ -197,13 +197,13 @@ public class DatagramMulticastTest extends AbstractDatagramTest { Enumeration addresses = iface.getInetAddresses(); while (addresses.hasMoreElements()) { InetAddress address = addresses.nextElement(); - if (internetProtocolFamily().addressType().isAssignableFrom(address.getClass())) { + if (socketInternetProtocalFamily().addressType().isAssignableFrom(address.getClass())) { MulticastSocket socket = new MulticastSocket(newAnySocketAddress()); socket.setReuseAddress(true); socket.setNetworkInterface(iface); try { socket.send(new java.net.DatagramPacket(new byte[] { 1, 2, 3, 4 }, 4, - new InetSocketAddress(groupAddress(), 12345))); + new InetSocketAddress(groupAddress(), 12345))); return iface; } catch (IOException ignore) { // Try the next interface @@ -218,7 +218,7 @@ public class DatagramMulticastTest extends AbstractDatagramTest { } private String groupAddress() { - return internetProtocolFamily() == InternetProtocolFamily.IPv4 ? + return groupInternetProtocalFamily() == InternetProtocolFamily.IPv4? "230.0.0.1" : "FF01:0:0:0:0:0:0:101"; } } diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/LinuxSocket.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/LinuxSocket.java index 62283fb5dc..4296d79898 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/LinuxSocket.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/LinuxSocket.java @@ -117,11 +117,14 @@ final class LinuxSocket extends Socket { final boolean isIpv6 = group instanceof Inet6Address; final NativeInetAddress i = NativeInetAddress.newInstance(deriveInetAddress(netInterface, isIpv6)); if (source != null) { + if (source.getClass() != group.getClass()) { + throw new IllegalArgumentException("Source address is different type to group"); + } final NativeInetAddress s = NativeInetAddress.newInstance(source); - joinSsmGroup(intValue(), ipv6, g.address(), i.address(), + joinSsmGroup(intValue(), ipv6 && isIpv6, g.address(), i.address(), g.scopeId(), interfaceIndex(netInterface), s.address()); } else { - joinGroup(intValue(), ipv6, g.address(), i.address(), g.scopeId(), interfaceIndex(netInterface)); + joinGroup(intValue(), ipv6 && isIpv6, g.address(), i.address(), g.scopeId(), interfaceIndex(netInterface)); } } @@ -130,11 +133,14 @@ final class LinuxSocket extends Socket { final boolean isIpv6 = group instanceof Inet6Address; final NativeInetAddress i = NativeInetAddress.newInstance(deriveInetAddress(netInterface, isIpv6)); if (source != null) { + if (source.getClass() != group.getClass()) { + throw new IllegalArgumentException("Source address is different type to group"); + } final NativeInetAddress s = NativeInetAddress.newInstance(source); - leaveSsmGroup(intValue(), ipv6, g.address(), i.address(), + leaveSsmGroup(intValue(), ipv6 && isIpv6, g.address(), i.address(), g.scopeId(), interfaceIndex(netInterface), s.address()); } else { - leaveGroup(intValue(), ipv6, g.address(), i.address(), g.scopeId(), interfaceIndex(netInterface)); + leaveGroup(intValue(), ipv6 && isIpv6, g.address(), i.address(), g.scopeId(), interfaceIndex(netInterface)); } } diff --git a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDatagramMulticastIpv6WithIpv4AddrTest.java b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDatagramMulticastIpv6WithIpv4AddrTest.java new file mode 100644 index 0000000000..ef77fa3490 --- /dev/null +++ b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDatagramMulticastIpv6WithIpv4AddrTest.java @@ -0,0 +1,32 @@ +/* + * Copyright 2021 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.channel.epoll; + +import io.netty.channel.socket.InternetProtocolFamily; +import io.netty.testsuite.transport.socket.DatagramMulticastTest; + +public class EpollDatagramMulticastIpv6WithIpv4AddrTest extends DatagramMulticastTest { + + @Override + protected InternetProtocolFamily groupInternetProtocalFamily() { + return InternetProtocolFamily.IPv4; + } + + @Override + protected InternetProtocolFamily socketInternetProtocalFamily() { + return InternetProtocolFamily.IPv6; + } +}