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