/* * Copyright (C) 2019 The Android Open Source Project * * 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 android.net.util; import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL; import static android.net.TetheringManager.TETHERING_USB; import static android.net.TetheringManager.TETHERING_WIFI; import static android.system.OsConstants.AF_UNIX; import static android.system.OsConstants.EAGAIN; import static android.system.OsConstants.SOCK_CLOEXEC; import static android.system.OsConstants.SOCK_DGRAM; import static android.system.OsConstants.SOCK_NONBLOCK; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; import android.net.LinkAddress; import android.net.MacAddress; import android.net.TetheringRequestParcel; import android.system.ErrnoException; import android.system.Os; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.net.module.util.Ipv6Utils; import com.android.net.module.util.NetworkStackConstants; import com.android.net.module.util.Struct; import com.android.net.module.util.structs.EthernetHeader; import com.android.net.module.util.structs.Icmpv6Header; import com.android.net.module.util.structs.Ipv6Header; import com.android.testutils.MiscAsserts; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.io.FileDescriptor; import java.net.Inet6Address; import java.net.InetAddress; import java.nio.ByteBuffer; @RunWith(AndroidJUnit4.class) @SmallTest public class TetheringUtilsTest { private static final LinkAddress TEST_SERVER_ADDR = new LinkAddress("192.168.43.1/24"); private static final LinkAddress TEST_CLIENT_ADDR = new LinkAddress("192.168.43.5/24"); private static final int PACKET_SIZE = 1500; private TetheringRequestParcel mTetheringRequest; @Before public void setUp() { mTetheringRequest = makeTetheringRequestParcel(); } public TetheringRequestParcel makeTetheringRequestParcel() { final TetheringRequestParcel request = new TetheringRequestParcel(); request.tetheringType = TETHERING_WIFI; request.localIPv4Address = TEST_SERVER_ADDR; request.staticClientAddress = TEST_CLIENT_ADDR; request.exemptFromEntitlementCheck = false; request.showProvisioningUi = true; return request; } @Test public void testIsTetheringRequestEquals() { TetheringRequestParcel request = makeTetheringRequestParcel(); assertTrue(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, mTetheringRequest)); assertTrue(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request)); assertTrue(TetheringUtils.isTetheringRequestEquals(null, null)); assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, null)); assertFalse(TetheringUtils.isTetheringRequestEquals(null, mTetheringRequest)); request = makeTetheringRequestParcel(); request.tetheringType = TETHERING_USB; assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request)); request = makeTetheringRequestParcel(); request.localIPv4Address = null; request.staticClientAddress = null; assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request)); request = makeTetheringRequestParcel(); request.exemptFromEntitlementCheck = true; assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request)); request = makeTetheringRequestParcel(); request.showProvisioningUi = false; assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request)); request = makeTetheringRequestParcel(); request.connectivityScope = CONNECTIVITY_SCOPE_LOCAL; assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request)); MiscAsserts.assertFieldCountEquals(6, TetheringRequestParcel.class); } // Writes the specified packet to a filedescriptor, skipping the Ethernet header. // Needed because the Ipv6Utils methods for building packets always include the Ethernet header, // but socket filters applied by TetheringUtils expect the packet to start from the IP header. private int writePacket(FileDescriptor fd, ByteBuffer pkt) throws Exception { pkt.flip(); int offset = Struct.getSize(EthernetHeader.class); int len = pkt.capacity() - offset; return Os.write(fd, pkt.array(), offset, len); } // Reads a packet from the filedescriptor. private ByteBuffer readIpPacket(FileDescriptor fd) throws Exception { ByteBuffer buf = ByteBuffer.allocate(PACKET_SIZE); Os.read(fd, buf); return buf; } private interface SocketFilter { void apply(FileDescriptor fd) throws Exception; } private ByteBuffer checkIcmpSocketFilter(ByteBuffer passed, ByteBuffer dropped, SocketFilter filter) throws Exception { FileDescriptor in = new FileDescriptor(); FileDescriptor out = new FileDescriptor(); Os.socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, in, out); // Before the filter is applied, it doesn't drop anything. int len = writePacket(out, dropped); ByteBuffer received = readIpPacket(in); assertEquals(len, received.position()); // Install the socket filter. Then write two packets, the first expected to be dropped and // the second expected to be passed. Check that only the second makes it through. filter.apply(in); writePacket(out, dropped); len = writePacket(out, passed); received = readIpPacket(in); assertEquals(len, received.position()); received.flip(); // Check there are no more packets to read. try { readIpPacket(in); } catch (ErrnoException expected) { assertEquals(EAGAIN, expected.errno); } return received; } @Test public void testIcmpSocketFilters() throws Exception { MacAddress mac1 = MacAddress.fromString("11:22:33:44:55:66"); MacAddress mac2 = MacAddress.fromString("aa:bb:cc:dd:ee:ff"); Inet6Address ll1 = (Inet6Address) InetAddress.getByName("fe80::1"); Inet6Address ll2 = (Inet6Address) InetAddress.getByName("fe80::abcd"); Inet6Address allRouters = NetworkStackConstants.IPV6_ADDR_ALL_ROUTERS_MULTICAST; final ByteBuffer na = Ipv6Utils.buildNaPacket(mac1, mac2, ll1, ll2, 0, ll1); final ByteBuffer ns = Ipv6Utils.buildNsPacket(mac1, mac2, ll1, ll2, ll1); final ByteBuffer rs = Ipv6Utils.buildRsPacket(mac1, mac2, ll1, allRouters); ByteBuffer received = checkIcmpSocketFilter(na /* passed */, rs /* dropped */, TetheringUtils::setupNaSocket); Struct.parse(Ipv6Header.class, received); // Skip IPv6 header. Icmpv6Header icmpv6 = Struct.parse(Icmpv6Header.class, received); assertEquals(NetworkStackConstants.ICMPV6_NEIGHBOR_ADVERTISEMENT, icmpv6.type); received = checkIcmpSocketFilter(ns /* passed */, rs /* dropped */, TetheringUtils::setupNsSocket); Struct.parse(Ipv6Header.class, received); // Skip IPv6 header. icmpv6 = Struct.parse(Icmpv6Header.class, received); assertEquals(NetworkStackConstants.ICMPV6_NEIGHBOR_SOLICITATION, icmpv6.type); } }