1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.net; 18 19 import static android.system.OsConstants.EPERM; 20 21 import static org.junit.Assert.assertEquals; 22 import static org.junit.Assert.assertNull; 23 import static org.junit.Assert.assertTrue; 24 import static org.mockito.Matchers.eq; 25 import static org.mockito.Mockito.doThrow; 26 import static org.mockito.Mockito.spy; 27 import static org.mockito.Mockito.verify; 28 import static org.mockito.Mockito.verifyNoMoreInteractions; 29 import static org.mockito.Mockito.when; 30 31 import android.content.Context; 32 import android.net.INetd; 33 import android.net.MacAddress; 34 import android.os.Build; 35 import android.os.Handler; 36 import android.os.test.TestLooper; 37 import android.system.ErrnoException; 38 import android.util.IndentingPrintWriter; 39 40 import androidx.test.filters.SmallTest; 41 42 import com.android.net.module.util.BaseNetdUnsolicitedEventListener; 43 import com.android.net.module.util.IBpfMap; 44 import com.android.net.module.util.InterfaceParams; 45 import com.android.net.module.util.Struct.S32; 46 import com.android.testutils.DevSdkIgnoreRule; 47 import com.android.testutils.DevSdkIgnoreRunner; 48 import com.android.testutils.TestBpfMap; 49 50 import org.junit.Before; 51 import org.junit.Test; 52 import org.junit.runner.RunWith; 53 import org.mockito.ArgumentCaptor; 54 import org.mockito.Mock; 55 import org.mockito.MockitoAnnotations; 56 57 import java.io.PrintWriter; 58 import java.io.StringWriter; 59 60 @SmallTest 61 @RunWith(DevSdkIgnoreRunner.class) 62 @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2) 63 public final class BpfInterfaceMapUpdaterTest { 64 private static final int TEST_INDEX = 1; 65 private static final int TEST_INDEX2 = 2; 66 private static final String TEST_INTERFACE_NAME = "test1"; 67 private static final String TEST_INTERFACE_NAME2 = "test2"; 68 69 private final TestLooper mLooper = new TestLooper(); 70 private BaseNetdUnsolicitedEventListener mListener; 71 private BpfInterfaceMapUpdater mUpdater; 72 private IBpfMap<S32, InterfaceMapValue> mBpfMap = 73 spy(new TestBpfMap<>(S32.class, InterfaceMapValue.class)); 74 @Mock private INetd mNetd; 75 @Mock private Context mContext; 76 77 private class TestDependencies extends BpfInterfaceMapUpdater.Dependencies { 78 @Override getInterfaceMap()79 public IBpfMap<S32, InterfaceMapValue> getInterfaceMap() { 80 return mBpfMap; 81 } 82 83 @Override getInterfaceParams(String ifaceName)84 public InterfaceParams getInterfaceParams(String ifaceName) { 85 if (ifaceName.equals(TEST_INTERFACE_NAME)) { 86 return new InterfaceParams(TEST_INTERFACE_NAME, TEST_INDEX, 87 MacAddress.ALL_ZEROS_ADDRESS); 88 } else if (ifaceName.equals(TEST_INTERFACE_NAME2)) { 89 return new InterfaceParams(TEST_INTERFACE_NAME2, TEST_INDEX2, 90 MacAddress.ALL_ZEROS_ADDRESS); 91 } 92 93 return null; 94 } 95 96 @Override getINetd(Context ctx)97 public INetd getINetd(Context ctx) { 98 return mNetd; 99 } 100 } 101 102 @Before setUp()103 public void setUp() throws Exception { 104 MockitoAnnotations.initMocks(this); 105 when(mNetd.interfaceGetList()).thenReturn(new String[] {TEST_INTERFACE_NAME}); 106 mUpdater = new BpfInterfaceMapUpdater(mContext, new Handler(mLooper.getLooper()), 107 new TestDependencies()); 108 } 109 verifyStartUpdater()110 private void verifyStartUpdater() throws Exception { 111 mUpdater.start(); 112 mLooper.dispatchAll(); 113 final ArgumentCaptor<BaseNetdUnsolicitedEventListener> listenerCaptor = 114 ArgumentCaptor.forClass(BaseNetdUnsolicitedEventListener.class); 115 verify(mNetd).registerUnsolicitedEventListener(listenerCaptor.capture()); 116 mListener = listenerCaptor.getValue(); 117 verify(mBpfMap).updateEntry(eq(new S32(TEST_INDEX)), 118 eq(new InterfaceMapValue(TEST_INTERFACE_NAME))); 119 } 120 121 @Test testUpdateInterfaceMap()122 public void testUpdateInterfaceMap() throws Exception { 123 verifyStartUpdater(); 124 125 mListener.onInterfaceAdded(TEST_INTERFACE_NAME2); 126 mLooper.dispatchAll(); 127 verify(mBpfMap).updateEntry(eq(new S32(TEST_INDEX2)), 128 eq(new InterfaceMapValue(TEST_INTERFACE_NAME2))); 129 130 // Check that when onInterfaceRemoved is called, nothing happens. 131 mListener.onInterfaceRemoved(TEST_INTERFACE_NAME); 132 mLooper.dispatchAll(); 133 verifyNoMoreInteractions(mBpfMap); 134 } 135 136 @Test testGetIfNameByIndex()137 public void testGetIfNameByIndex() throws Exception { 138 mBpfMap.updateEntry(new S32(TEST_INDEX), new InterfaceMapValue(TEST_INTERFACE_NAME)); 139 assertEquals(TEST_INTERFACE_NAME, mUpdater.getIfNameByIndex(TEST_INDEX)); 140 } 141 142 @Test testGetIfNameByIndexNoEntry()143 public void testGetIfNameByIndexNoEntry() { 144 assertNull(mUpdater.getIfNameByIndex(TEST_INDEX)); 145 } 146 147 @Test testGetIfNameByIndexException()148 public void testGetIfNameByIndexException() throws Exception { 149 doThrow(new ErrnoException("", EPERM)).when(mBpfMap).getValue(new S32(TEST_INDEX)); 150 assertNull(mUpdater.getIfNameByIndex(TEST_INDEX)); 151 } 152 assertDumpContains(final String dump, final String message)153 private void assertDumpContains(final String dump, final String message) { 154 assertTrue(String.format("dump(%s) does not contain '%s'", dump, message), 155 dump.contains(message)); 156 } 157 getDump()158 private String getDump() { 159 final StringWriter sw = new StringWriter(); 160 mUpdater.dump(new IndentingPrintWriter(new PrintWriter(sw), " ")); 161 return sw.toString(); 162 } 163 164 @Test testDump()165 public void testDump() throws ErrnoException { 166 mBpfMap.updateEntry(new S32(TEST_INDEX), new InterfaceMapValue(TEST_INTERFACE_NAME)); 167 mBpfMap.updateEntry(new S32(TEST_INDEX2), new InterfaceMapValue(TEST_INTERFACE_NAME2)); 168 169 final String dump = getDump(); 170 assertDumpContains(dump, "IfaceIndexNameMap: OK"); 171 assertDumpContains(dump, "ifaceIndex=1 ifaceName=test1"); 172 assertDumpContains(dump, "ifaceIndex=2 ifaceName=test2"); 173 } 174 } 175