1 /* 2 * Copyright (c) 2007, 2020, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* @test 25 * @bug 4527345 7026376 6633549 8233435 26 * @summary Unit test for DatagramChannel's multicast support 27 * @library /test/lib 28 * @build jdk.test.lib.NetworkConfiguration 29 * jdk.test.lib.Platform 30 * jdk.test.lib.net.IPSupport 31 * MulticastSendReceiveTests 32 * @run main MulticastSendReceiveTests 33 * @run main/othervm -Djava.net.preferIPv4Stack=true MulticastSendReceiveTests 34 * @key randomness 35 */ 36 37 import java.nio.ByteBuffer; 38 import java.nio.channels.*; 39 import java.net.*; 40 import static java.net.StandardProtocolFamily.*; 41 import java.util.*; 42 import java.io.IOException; 43 import java.util.stream.Collectors; 44 45 import jdk.test.lib.Platform; 46 import jdk.test.lib.NetworkConfiguration; 47 import jdk.test.lib.net.IPSupport; 48 49 public class MulticastSendReceiveTests { 50 51 static final Random rand = new Random(); 52 53 static final ProtocolFamily UNSPEC = new ProtocolFamily() { 54 public String name() { 55 return "UNSPEC"; 56 } 57 }; 58 59 /** 60 * Send datagram from given local address to given multicast 61 * group. 62 */ 63 static int sendDatagram(InetAddress local, 64 NetworkInterface nif, 65 InetAddress group, 66 int port) 67 throws IOException 68 { 69 ProtocolFamily family = (group instanceof Inet6Address) ? 70 StandardProtocolFamily.INET6 : StandardProtocolFamily.INET; 71 int id = rand.nextInt(); 72 try (DatagramChannel dc = DatagramChannel.open(family)) { 73 dc.bind(new InetSocketAddress(local, 0)); 74 dc.setOption(StandardSocketOptions.IP_MULTICAST_IF, nif); 75 byte[] msg = Integer.toString(id).getBytes("UTF-8"); 76 ByteBuffer buf = ByteBuffer.wrap(msg); 77 System.out.format("Send message from %s -> group %s (id=0x%x)\n", 78 local.getHostAddress(), group.getHostAddress(), id); 79 dc.send(buf, new InetSocketAddress(group, port)); 80 } 81 return id; 82 } 83 84 /** 85 * Wait (with timeout) for datagram. 86 * 87 * @param expectedSender - expected sender address, or 88 * null if no datagram expected 89 * @param id - expected id of datagram 90 */ 91 static void receiveDatagram(DatagramChannel dc, 92 InetAddress expectedSender, 93 int id) 94 throws IOException 95 { 96 Selector sel = Selector.open(); 97 dc.configureBlocking(false); 98 dc.register(sel, SelectionKey.OP_READ); 99 ByteBuffer buf = ByteBuffer.allocateDirect(100); 100 101 try { 102 for (;;) { 103 System.out.println("Waiting to receive message"); 104 sel.select(5*1000); 105 SocketAddress sa = dc.receive(buf); 106 107 // no datagram received 108 if (sa == null) { 109 if (expectedSender != null) { 110 throw new RuntimeException("Expected message not received"); 111 } 112 System.out.println("No message received (correct)"); 113 return; 114 } 115 116 // datagram received 117 118 InetAddress sender = ((InetSocketAddress)sa).getAddress(); 119 buf.flip(); 120 byte[] bytes = new byte[buf.remaining()]; 121 buf.get(bytes); 122 String s = new String(bytes, "UTF-8"); 123 int receivedId = -1; 124 try { 125 receivedId = Integer.parseInt(s); 126 System.out.format("Received message from %s (id=0x%x)\n", 127 sender, receivedId); 128 } catch (NumberFormatException x) { 129 System.out.format("Received message from %s (msg=%s)\n", sender, s); 130 } 131 132 if (expectedSender == null) { 133 if (receivedId == id) 134 throw new RuntimeException("Message not expected"); 135 System.out.println("Message ignored (has wrong id)"); 136 } else { 137 if (sender.equals(expectedSender)) { 138 System.out.println("Message expected"); 139 return; 140 } 141 System.out.println("Message ignored (wrong sender)"); 142 } 143 144 sel.selectedKeys().clear(); 145 buf.rewind(); 146 } 147 } finally { 148 sel.close(); 149 } 150 } 151 152 153 /** 154 * Exercise multicast send/receive on given group/interface 155 */ 156 static void test(ProtocolFamily family, 157 NetworkInterface nif, 158 InetAddress group, 159 InetAddress source) 160 throws IOException 161 { 162 System.out.format("\nTest DatagramChannel to %s socket\n", family.name()); 163 try (DatagramChannel dc = (family == UNSPEC) ? 164 DatagramChannel.open() : DatagramChannel.open(family)) { 165 dc.setOption(StandardSocketOptions.SO_REUSEADDR, true) 166 .bind(new InetSocketAddress(0)); 167 168 // join group 169 System.out.format("join %s @ %s\n", group.getHostAddress(), 170 nif.getName()); 171 MembershipKey key; 172 try { 173 key = dc.join(group, nif); 174 } catch (IllegalArgumentException iae) { 175 if (family == UNSPEC) { 176 System.out.println("Not supported"); 177 return; 178 } 179 throw iae; 180 } 181 182 // send message to group 183 int port = ((InetSocketAddress)dc.getLocalAddress()).getPort(); 184 int id = sendDatagram(source, nif, group, port); 185 186 // receive message and check id matches 187 receiveDatagram(dc, source, id); 188 189 // exclude-mode filtering 190 191 try { 192 System.out.format("block %s\n", source.getHostAddress()); 193 194 // may throw UOE 195 key.block(source); 196 id = sendDatagram(source, nif, group, port); 197 receiveDatagram(dc, null, id); 198 199 // unblock source, send message, message should be received 200 System.out.format("unblock %s\n", source.getHostAddress()); 201 key.unblock(source); 202 id = sendDatagram(source, nif, group, port); 203 receiveDatagram(dc, source, id); 204 } catch (UnsupportedOperationException x) { 205 String os = System.getProperty("os.name"); 206 // Exclude-mode filtering supported on these platforms so UOE should never be thrown 207 if (os.equals("SunOS") || os.equals("Linux")) 208 throw x; 209 System.out.println("Exclude-mode filtering not supported!"); 210 } 211 212 key.drop(); 213 214 // include-mode filtering 215 216 InetAddress bogus = (group instanceof Inet6Address) ? 217 InetAddress.getByName("fe80::1234") : 218 InetAddress.getByName("1.2.3.4"); 219 System.out.format("join %s @ %s only-source %s\n", group.getHostAddress(), 220 nif.getName(), bogus.getHostAddress()); 221 try { 222 // may throw UOE 223 key = dc.join(group, nif, bogus); 224 225 id = sendDatagram(source, nif, group, port); 226 receiveDatagram(dc, null, id); 227 228 System.out.format("join %s @ %s only-source %s\n", group.getHostAddress(), 229 nif.getName(), source.getHostAddress()); 230 key = dc.join(group, nif, source); 231 232 id = sendDatagram(source, nif, group, port); 233 receiveDatagram(dc, source, id); 234 } catch (UnsupportedOperationException x) { 235 String os = System.getProperty("os.name"); 236 // Include-mode filtering supported on these platforms so UOE should never be thrown 237 if (os.equals("SunOS") || os.equals("Linux")) 238 throw x; 239 System.out.println("Include-mode filtering not supported!"); 240 } 241 } 242 } 243 244 public static void main(String[] args) throws IOException { 245 IPSupport.throwSkippedExceptionIfNonOperational(); 246 247 // IPv4 and IPv6 interfaces that support multicasting 248 NetworkConfiguration config = NetworkConfiguration.probe(); 249 List<NetworkInterface> ip4MulticastInterfaces = config.ip4MulticastInterfaces() 250 .collect(Collectors.toList()); 251 List<NetworkInterface> ip6MulticastInterfaces = config.ip6MulticastInterfaces() 252 .collect(Collectors.toList()); 253 254 // multicast groups used for the test 255 InetAddress ip4Group = InetAddress.getByName("225.4.5.6"); 256 InetAddress ip6Group = InetAddress.getByName("ff02::a"); 257 258 // Platforms that allow dual sockets join IPv4 multicast groups 259 boolean canIPv6JoinIPv4Group = 260 Platform.isLinux() || 261 Platform.isOSX() || 262 Platform.isSolaris() || 263 Platform.isWindows(); 264 265 for (NetworkInterface nif : ip4MulticastInterfaces) { 266 InetAddress source = config.ip4Addresses(nif).iterator().next(); 267 test(UNSPEC, nif, ip4Group, source); 268 test(INET, nif, ip4Group, source); 269 if (IPSupport.hasIPv6() && canIPv6JoinIPv4Group) { 270 test(INET6, nif, ip4Group, source); 271 } 272 } 273 274 for (NetworkInterface nif : ip6MulticastInterfaces) { 275 InetAddress source = config.ip6Addresses(nif).iterator().next(); 276 test(UNSPEC, nif, ip6Group, source); 277 test(INET6, nif, ip6Group, source); 278 } 279 } 280 }