1 /* 2 * Copyright (C) 2021 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.networkstack.tethering; 17 18 import static android.system.OsConstants.ETH_P_IP; 19 import static android.system.OsConstants.ETH_P_IPV6; 20 21 import static com.android.networkstack.tethering.util.TetheringUtils.getTetheringJniLibraryName; 22 23 import androidx.annotation.NonNull; 24 25 import com.android.net.module.util.InterfaceParams; 26 import com.android.net.module.util.TcUtils; 27 28 import java.io.IOException; 29 30 /** 31 * The classes and the methods for BPF utilization. 32 * 33 * {@hide} 34 */ 35 public class BpfUtils { 36 static { getTetheringJniLibraryName()37 System.loadLibrary(getTetheringJniLibraryName()); 38 } 39 40 // For better code clarity when used for 'bool ingress' parameter. 41 static final boolean EGRESS = false; 42 static final boolean INGRESS = true; 43 44 // For better code clarify when used for 'bool downstream' parameter. 45 // 46 // This is talking about the direction of travel of the offloaded packets. 47 // 48 // Upstream means packets heading towards the internet/uplink (upload), 49 // thus for tethering this is attached to ingress on the downstream interface, 50 // while for clat this is attached to egress on the v4-* clat interface. 51 // 52 // Downstream means packets coming from the internet/uplink (download), thus 53 // for both clat and tethering this is attached to ingress on the upstream interface. 54 static final boolean DOWNSTREAM = true; 55 static final boolean UPSTREAM = false; 56 57 // The priority of tether hooks - smaller is higher priority. 58 // TC tether is higher priority then TC clat to match XDP winning over TC. 59 // Sync from system/netd/server/TcUtils.h. 60 static final short PRIO_TETHER6 = 2; 61 static final short PRIO_TETHER4 = 3; 62 // note that the above must be lower than PRIO_CLAT from netd's OffloadUtils.cpp 63 makeProgPath(boolean downstream, int ipVersion, boolean ether)64 private static String makeProgPath(boolean downstream, int ipVersion, boolean ether) { 65 String path = "/sys/fs/bpf/tethering/prog_offload_schedcls_tether_" 66 + (downstream ? "downstream" : "upstream") 67 + ipVersion + "_" 68 + (ether ? "ether" : "rawip"); 69 return path; 70 } 71 72 /** 73 * Attach BPF program 74 * 75 * TODO: use interface index to replace interface name. 76 */ attachProgram(@onNull String iface, boolean downstream)77 public static void attachProgram(@NonNull String iface, boolean downstream) 78 throws IOException { 79 final InterfaceParams params = InterfaceParams.getByName(iface); 80 if (params == null) { 81 throw new IOException("Fail to get interface params for interface " + iface); 82 } 83 84 boolean ether; 85 try { 86 ether = TcUtils.isEthernet(iface); 87 } catch (IOException e) { 88 throw new IOException("isEthernet(" + params.index + "[" + iface + "]) failure: " + e); 89 } 90 91 try { 92 // tc filter add dev .. ingress prio 1 protocol ipv6 bpf object-pinned /sys/fs/bpf/... 93 // direct-action 94 TcUtils.tcFilterAddDevBpf(params.index, INGRESS, PRIO_TETHER6, (short) ETH_P_IPV6, 95 makeProgPath(downstream, 6, ether)); 96 } catch (IOException e) { 97 throw new IOException("tc filter add dev (" + params.index + "[" + iface 98 + "]) ingress prio PRIO_TETHER6 protocol ipv6 failure: " + e); 99 } 100 101 try { 102 // tc filter add dev .. ingress prio 2 protocol ip bpf object-pinned /sys/fs/bpf/... 103 // direct-action 104 TcUtils.tcFilterAddDevBpf(params.index, INGRESS, PRIO_TETHER4, (short) ETH_P_IP, 105 makeProgPath(downstream, 4, ether)); 106 } catch (IOException e) { 107 throw new IOException("tc filter add dev (" + params.index + "[" + iface 108 + "]) ingress prio PRIO_TETHER4 protocol ip failure: " + e); 109 } 110 } 111 112 /** 113 * Detach BPF program 114 * 115 * TODO: use interface index to replace interface name. 116 */ detachProgram(@onNull String iface)117 public static void detachProgram(@NonNull String iface) throws IOException { 118 final InterfaceParams params = InterfaceParams.getByName(iface); 119 if (params == null) { 120 throw new IOException("Fail to get interface params for interface " + iface); 121 } 122 123 try { 124 // tc filter del dev .. ingress prio 1 protocol ipv6 125 TcUtils.tcFilterDelDev(params.index, INGRESS, PRIO_TETHER6, (short) ETH_P_IPV6); 126 } catch (IOException e) { 127 throw new IOException("tc filter del dev (" + params.index + "[" + iface 128 + "]) ingress prio PRIO_TETHER6 protocol ipv6 failure: " + e); 129 } 130 131 try { 132 // tc filter del dev .. ingress prio 2 protocol ip 133 TcUtils.tcFilterDelDev(params.index, INGRESS, PRIO_TETHER4, (short) ETH_P_IP); 134 } catch (IOException e) { 135 throw new IOException("tc filter del dev (" + params.index + "[" + iface 136 + "]) ingress prio PRIO_TETHER4 protocol ip failure: " + e); 137 } 138 } 139 } 140