• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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