• 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 package com.android.server.net;
17 
18 import android.content.Context;
19 import android.net.INetd;
20 import android.os.Handler;
21 import android.os.IBinder;
22 import android.os.RemoteException;
23 import android.os.ServiceSpecificException;
24 import android.system.ErrnoException;
25 import android.util.IndentingPrintWriter;
26 import android.util.Log;
27 
28 import com.android.internal.annotations.VisibleForTesting;
29 import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
30 import com.android.net.module.util.BpfDump;
31 import com.android.net.module.util.BpfMap;
32 import com.android.net.module.util.IBpfMap;
33 import com.android.net.module.util.InterfaceParams;
34 import com.android.net.module.util.Struct.S32;
35 
36 /**
37  * Monitor interface added (without removed) and right interface name and its index to bpf map.
38  */
39 public class BpfInterfaceMapUpdater {
40     private static final String TAG = BpfInterfaceMapUpdater.class.getSimpleName();
41     // This is current path but may be changed soon.
42     private static final String IFACE_INDEX_NAME_MAP_PATH =
43             "/sys/fs/bpf/netd_shared/map_netd_iface_index_name_map";
44     private final IBpfMap<S32, InterfaceMapValue> mBpfMap;
45     private final INetd mNetd;
46     private final Handler mHandler;
47     private final Dependencies mDeps;
48 
BpfInterfaceMapUpdater(Context ctx, Handler handler)49     public BpfInterfaceMapUpdater(Context ctx, Handler handler) {
50         this(ctx, handler, new Dependencies());
51     }
52 
53     @VisibleForTesting
BpfInterfaceMapUpdater(Context ctx, Handler handler, Dependencies deps)54     public BpfInterfaceMapUpdater(Context ctx, Handler handler, Dependencies deps) {
55         mDeps = deps;
56         mBpfMap = deps.getInterfaceMap();
57         mNetd = deps.getINetd(ctx);
58         mHandler = handler;
59     }
60 
61     /**
62      * Dependencies of BpfInerfaceMapUpdater, for injection in tests.
63      */
64     @VisibleForTesting
65     public static class Dependencies {
66         /** Create BpfMap for updating interface and index mapping. */
getInterfaceMap()67         public IBpfMap<S32, InterfaceMapValue> getInterfaceMap() {
68             try {
69                 return new BpfMap<>(IFACE_INDEX_NAME_MAP_PATH, BpfMap.BPF_F_RDWR,
70                     S32.class, InterfaceMapValue.class);
71             } catch (ErrnoException e) {
72                 Log.e(TAG, "Cannot create interface map: " + e);
73                 return null;
74             }
75         }
76 
77         /** Get InterfaceParams for giving interface name. */
getInterfaceParams(String ifaceName)78         public InterfaceParams getInterfaceParams(String ifaceName) {
79             return InterfaceParams.getByName(ifaceName);
80         }
81 
82         /** Get INetd binder object. */
getINetd(Context ctx)83         public INetd getINetd(Context ctx) {
84             return INetd.Stub.asInterface((IBinder) ctx.getSystemService(Context.NETD_SERVICE));
85         }
86     }
87 
88     /**
89      * Start listening interface update event.
90      * Query current interface names before listening.
91      */
start()92     public void start() {
93         mHandler.post(() -> {
94             if (mBpfMap == null) {
95                 Log.wtf(TAG, "Fail to start: Null bpf map");
96                 return;
97             }
98 
99             try {
100                 // TODO: use a NetlinkMonitor and listen for RTM_NEWLINK messages instead.
101                 mNetd.registerUnsolicitedEventListener(new InterfaceChangeObserver());
102             } catch (RemoteException e) {
103                 Log.wtf(TAG, "Unable to register netd UnsolicitedEventListener, " + e);
104             }
105 
106             final String[] ifaces;
107             try {
108                 // TODO: use a netlink dump to get the current interface list.
109                 ifaces = mNetd.interfaceGetList();
110             } catch (RemoteException | ServiceSpecificException e) {
111                 Log.wtf(TAG, "Unable to query interface names by netd, " + e);
112                 return;
113             }
114 
115             for (String ifaceName : ifaces) {
116                 addInterface(ifaceName);
117             }
118         });
119     }
120 
addInterface(String ifaceName)121     private void addInterface(String ifaceName) {
122         final InterfaceParams iface = mDeps.getInterfaceParams(ifaceName);
123         if (iface == null) {
124             Log.e(TAG, "Unable to get InterfaceParams for " + ifaceName);
125             return;
126         }
127 
128         try {
129             mBpfMap.updateEntry(new S32(iface.index), new InterfaceMapValue(ifaceName));
130         } catch (ErrnoException e) {
131             Log.e(TAG, "Unable to update entry for " + ifaceName + ", " + e);
132         }
133     }
134 
135     private class InterfaceChangeObserver extends BaseNetdUnsolicitedEventListener {
136         @Override
onInterfaceAdded(String ifName)137         public void onInterfaceAdded(String ifName) {
138             mHandler.post(() -> addInterface(ifName));
139         }
140     }
141 
142     /** get interface name by interface index from bpf map */
getIfNameByIndex(final int index)143     public String getIfNameByIndex(final int index) {
144         try {
145             final InterfaceMapValue value = mBpfMap.getValue(new S32(index));
146             if (value == null) {
147                 Log.e(TAG, "No if name entry for index " + index);
148                 return null;
149             }
150             return value.getInterfaceNameString();
151         } catch (ErrnoException e) {
152             Log.e(TAG, "Failed to get entry for index " + index + ": " + e);
153             return null;
154         }
155     }
156 
157     /**
158      * Dump BPF map
159      *
160      * @param pw print writer
161      */
dump(final IndentingPrintWriter pw)162     public void dump(final IndentingPrintWriter pw) {
163         pw.println("BPF map status:");
164         pw.increaseIndent();
165         BpfDump.dumpMapStatus(mBpfMap, pw, "IfaceIndexNameMap", IFACE_INDEX_NAME_MAP_PATH);
166         pw.decreaseIndent();
167         pw.println("BPF map content:");
168         pw.increaseIndent();
169         BpfDump.dumpMap(mBpfMap, pw, "IfaceIndexNameMap",
170                 (key, value) -> "ifaceIndex=" + key.val
171                         + " ifaceName=" + value.getInterfaceNameString());
172         pw.decreaseIndent();
173     }
174 }
175