/* * Copyright 2014-2017 Real Logic Ltd. * * Licensed 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 * * http://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.aeron.driver.media; import org.junit.Test; import org.agrona.LangUtil; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.net.*; import java.util.*; import static java.lang.Short.parseShort; import static java.net.InetAddress.getByName; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.sameInstance; import static org.junit.Assert.*; import static io.aeron.driver.media.NetworkUtil.filterBySubnet; import static io.aeron.driver.media.NetworkUtil.isMatchWithPrefix; public class NetworkUtilTest { @Test public void shouldNotMatchIfLengthsAreDifferent() { assertFalse(isMatchWithPrefix(new byte[0], new byte[3], 0)); assertFalse(isMatchWithPrefix(new byte[1], new byte[2], 0)); assertFalse(isMatchWithPrefix(new byte[5], new byte[5000], 0)); } @Test public void shouldMatchIfAllBytesMatch() throws Exception { final byte[] a = { 'a', 'b', 'c', 'd' }; final byte[] b = { 'a', 'b', 'c', 'd' }; assertTrue(isMatchWithPrefix(a, b, 32)); } @Test public void shouldMatchIfAllBytesWithPrefixMatch() throws Exception { final byte[] a = { 'a', 'b', 'c', 'd' }; final byte[] b = { 'a', 'b', 'c', 'e' }; assertTrue(isMatchWithPrefix(a, b, 24)); } @Test public void shouldNotMatchIfNotAllBytesWithPrefixMatch() throws Exception { final byte[] a = { 'a', 'b', 'c', 'd' }; final byte[] b = { 'a', 'b', 'd', 'd' }; assertFalse(isMatchWithPrefix(a, b, 24)); } @Test public void shouldMatchIfAllBytesWithPrefixUnalignedMatch() throws Exception { assertTrue(isMatchWithPrefix( asBytes(0b10101010_11111111_00000000_00000000), asBytes(0b10101010_11111110_00000000_00000000), 15)); } @Test public void shouldNotMatchIfNotAllBytesWithUnalignedPrefixMatch() throws Exception { assertFalse(isMatchWithPrefix( asBytes(0b10101010_11111111_00000000_00000000), asBytes(0b10101010_11111111_10000000_00000000), 17)); } @Test public void shouldFilterBySubnetAndFindOneResult() throws Exception { final NetworkInterfaceStub stub = new NetworkInterfaceStub(); final NetworkInterface ifc1 = stub.add("192.168.0.1/24"); stub.add("10.0.0.2/8"); final Collection<NetworkInterface> filteredBySubnet = filterBySubnet(stub, getByName("192.168.0.0"), 24); assertThat(filteredBySubnet.size(), is(1)); assertThat(first(filteredBySubnet), is(ifc1)); } @Test public void shouldFilterBySubnetAndFindNoResults() throws Exception { final NetworkInterfaceStub stub = new NetworkInterfaceStub(); stub.add("192.168.0.1/24"); stub.add("10.0.0.2/8"); final Collection<NetworkInterface> filteredBySubnet = filterBySubnet(stub, getByName("192.169.0.0"), 24); assertThat(filteredBySubnet.size(), is(0)); } @Test public void shouldFilterBySubnetAndFindMultipleResultsOrderedByMatchLength() throws Exception { final NetworkInterfaceStub stub = new NetworkInterfaceStub(); stub.add("10.0.0.2/8"); final NetworkInterface ifc1 = stub.add("192.0.0.0/8"); final NetworkInterface ifc2 = stub.add("192.168.1.1/24"); final NetworkInterface ifc3 = stub.add("192.168.0.0/16"); final Collection<NetworkInterface> filteredBySubnet = filterBySubnet(stub, getByName("192.0.0.0"), 8); assertThat(filteredBySubnet.size(), is(3)); final Iterator<NetworkInterface> it = filteredBySubnet.iterator(); assertThat(it.next(), sameInstance(ifc2)); assertThat(it.next(), sameInstance(ifc3)); assertThat(it.next(), sameInstance(ifc1)); } @Test public void shouldFilterBySubnetAndFindOneIpV6Result() throws Exception { final NetworkInterfaceStub stub = new NetworkInterfaceStub(); final NetworkInterface ifc1 = stub.add("fe80:0:0:0001:0002:0:0:1/80"); stub.add("fe80:0:0:0002:0003:0:0:1/80"); final Collection<NetworkInterface> filteredBySubnet = filterBySubnet(stub, getByName("fe80:0:0:0001:0002:0:0:0"), 80); assertThat(filteredBySubnet.size(), is(1)); assertThat(first(filteredBySubnet), is(ifc1)); } @Test public void shouldFilterBySubnetAndFindNoIpV6Results() throws Exception { final NetworkInterfaceStub stub = new NetworkInterfaceStub(); stub.add("fe80:0:0:0001:0:0:0:1/64"); stub.add("fe80:0:0:0002:0:0:0:1/64"); final Collection<NetworkInterface> filteredBySubnet = filterBySubnet(stub, getByName("fe80:0:0:0004:0:0:0:0"), 64); assertThat(filteredBySubnet.size(), is(0)); } @Test public void shouldFilterBySubnetAndFindMultipleIpV6ResultsOrderedByMatchLength() throws Exception { final NetworkInterfaceStub stub = new NetworkInterfaceStub(); stub.add("ee80:0:0:0001:0:0:0:1/64"); final NetworkInterface ifc1 = stub.add("fe80:0:0:0:0:0:0:1/16"); final NetworkInterface ifc2 = stub.add("fe80:0001:0:0:0:0:0:1/32"); final NetworkInterface ifc3 = stub.add("fe80:0001:abcd:0:0:0:0:1/48"); final Collection<NetworkInterface> filteredBySubnet = filterBySubnet(stub, getByName("fe80:0:0:0:0:0:0:0"), 16); assertThat(filteredBySubnet.size(), is(3)); final Iterator<NetworkInterface> it = filteredBySubnet.iterator(); assertThat(it.next(), sameInstance(ifc3)); assertThat(it.next(), sameInstance(ifc2)); assertThat(it.next(), sameInstance(ifc1)); } private static <T> T first(final Collection<T> c) { return c.iterator().next(); } private static class NetworkInterfaceStub implements NetworkInterfaceShim { private int counter = 0; private final IdentityHashMap<NetworkInterface, List<InterfaceAddress>> addressesByInterface = new IdentityHashMap<>(); public Enumeration<NetworkInterface> getNetworkInterfaces() throws SocketException { return Collections.enumeration(addressesByInterface.keySet()); } public List<InterfaceAddress> getInterfaceAddresses(final NetworkInterface ifc) { return addressesByInterface.get(ifc); } public boolean isLoopback(final NetworkInterface ifc) throws SocketException { return false; } public NetworkInterface add(final String...ips) throws UnknownHostException { final List<InterfaceAddress> ias = new ArrayList<>(); for (final String ip : ips) { final String[] parts = ip.split("/"); ias.add(newInterfaceAddress(getByName(parts[0]), parseShort(parts[1]))); } final NetworkInterface ifc = newNetworkInterface(String.valueOf(counter++)); addressesByInterface.put(ifc, ias); return ifc; } } private static NetworkInterface newNetworkInterface(final String name) { NetworkInterface networkInterface = null; try { final Constructor<NetworkInterface> ctor = NetworkInterface.class.getDeclaredConstructor(); ctor.setAccessible(true); final Field nameField = NetworkInterface.class.getDeclaredField("name"); nameField.setAccessible(true); networkInterface = ctor.newInstance(); nameField.set(networkInterface, name); } catch (final Exception ex) { LangUtil.rethrowUnchecked(ex); } return networkInterface; } private static InterfaceAddress newInterfaceAddress(final InetAddress inetAddress, final short maskLength) { InterfaceAddress interfaceAddress = null; try { final Constructor<InterfaceAddress> ctor = InterfaceAddress.class.getDeclaredConstructor(); ctor.setAccessible(true); final Field addressField = InterfaceAddress.class.getDeclaredField("address"); addressField.setAccessible(true); final Field maskLengthField = InterfaceAddress.class.getDeclaredField("maskLength"); maskLengthField.setAccessible(true); interfaceAddress = ctor.newInstance(); addressField.set(interfaceAddress, inetAddress); maskLengthField.set(interfaceAddress, maskLength); } catch (final Exception ex) { LangUtil.rethrowUnchecked(ex); } return interfaceAddress; } private static byte[] asBytes(final int i) { final byte[] bs = new byte[4]; bs[0] = (byte) ((i >> 24) & 0xFF); bs[1] = (byte) ((i >> 16) & 0xFF); bs[2] = (byte) ((i >> 8) & 0xFF); bs[3] = (byte) (i & 0xFF); return bs; } }