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
This commit is contained in:
wangyuwei 2021-02-16 21:21:32 +08:00 committed by Chris Vest
parent b29df0f87a
commit dbee8c9b89
4 changed files with 59 additions and 13 deletions

View File

@ -35,7 +35,7 @@ public abstract class AbstractDatagramTest extends AbstractComboTestsuiteTest<Bo
@Override
protected List<TestsuitePermutation.BootstrapComboFactory<Bootstrap, Bootstrap>> newFactories() {
return SocketTestPermutation.INSTANCE.datagram(internetProtocolFamily());
return SocketTestPermutation.INSTANCE.datagram(socketInternetProtocalFamily());
}
@Override
@ -45,7 +45,7 @@ public abstract class AbstractDatagramTest extends AbstractComboTestsuiteTest<Bo
}
protected SocketAddress newSocketAddress() {
switch (internetProtocolFamily()) {
switch (socketInternetProtocalFamily()) {
case IPv4:
return new InetSocketAddress(NetUtil.LOCALHOST4, 0);
case IPv6:
@ -58,4 +58,12 @@ public abstract class AbstractDatagramTest extends AbstractComboTestsuiteTest<Bo
protected InternetProtocolFamily internetProtocolFamily() {
return InternetProtocolFamily.IPv4;
}
protected InternetProtocolFamily groupInternetProtocalFamily() {
return internetProtocolFamily();
}
protected InternetProtocolFamily socketInternetProtocalFamily() {
return internetProtocolFamily();
}
}

View File

@ -51,7 +51,7 @@ public class DatagramMulticastTest extends AbstractDatagramTest {
public void testMulticast(Bootstrap sb, Bootstrap cb) throws Throwable {
NetworkInterface iface = multicastNetworkInterface();
Assume.assumeNotNull("No NetworkInterface found that supports multicast and " +
internetProtocolFamily(), iface);
socketInternetProtocalFamily(), iface);
MulticastTestHandler mhandler = new MulticastTestHandler();
@ -156,11 +156,11 @@ public class DatagramMulticastTest extends AbstractDatagramTest {
@Override
protected List<TestsuitePermutation.BootstrapComboFactory<Bootstrap, Bootstrap>> 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:
@ -174,7 +174,7 @@ public class DatagramMulticastTest extends AbstractDatagramTest {
Enumeration<InetAddress> 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);
}
}
@ -189,13 +189,13 @@ public class DatagramMulticastTest extends AbstractDatagramTest {
Enumeration<InetAddress> 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
@ -210,7 +210,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";
}
}

View File

@ -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));
}
}

View File

@ -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;
}
}