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, boolean ipv4)77 public static void attachProgram(@NonNull String iface, boolean downstream, boolean ipv4) 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 if (ipv4) { 92 try { 93 // tc filter add dev .. ingress prio 2 protocol ip bpf object-pinned /sys/fs/bpf/... 94 // direct-action 95 TcUtils.tcFilterAddDevBpf(params.index, INGRESS, PRIO_TETHER4, (short) ETH_P_IP, 96 makeProgPath(downstream, 4, ether)); 97 } catch (IOException e) { 98 throw new IOException("tc filter add dev (" + params.index + "[" + iface 99 + "]) ingress prio PRIO_TETHER4 protocol ip failure: " + e); 100 } 101 } else { 102 try { 103 // tc filter add dev .. ingress prio 1 protocol ipv6 bpf object-pinned 104 // /sys/fs/bpf/... direct-action 105 TcUtils.tcFilterAddDevBpf(params.index, INGRESS, PRIO_TETHER6, (short) ETH_P_IPV6, 106 makeProgPath(downstream, 6, ether)); 107 } catch (IOException e) { 108 throw new IOException("tc filter add dev (" + params.index + "[" + iface 109 + "]) ingress prio PRIO_TETHER6 protocol ipv6 failure: " + e); 110 } 111 } 112 } 113 114 /** 115 * Detach BPF program 116 * 117 * TODO: use interface index to replace interface name. 118 */ detachProgram(@onNull String iface, boolean ipv4)119 public static void detachProgram(@NonNull String iface, boolean ipv4) throws IOException { 120 final InterfaceParams params = InterfaceParams.getByName(iface); 121 if (params == null) { 122 throw new IOException("Fail to get interface params for interface " + iface); 123 } 124 125 if (ipv4) { 126 try { 127 // tc filter del dev .. ingress prio 2 protocol ip 128 TcUtils.tcFilterDelDev(params.index, INGRESS, PRIO_TETHER4, (short) ETH_P_IP); 129 } catch (IOException e) { 130 throw new IOException("tc filter del dev (" + params.index + "[" + iface 131 + "]) ingress prio PRIO_TETHER4 protocol ip failure: " + e); 132 } 133 } else { 134 try { 135 // tc filter del dev .. ingress prio 1 protocol ipv6 136 TcUtils.tcFilterDelDev(params.index, INGRESS, PRIO_TETHER6, (short) ETH_P_IPV6); 137 } catch (IOException e) { 138 throw new IOException("tc filter del dev (" + params.index + "[" + iface 139 + "]) ingress prio PRIO_TETHER6 protocol ipv6 failure: " + e); 140 } 141 } 142 } 143 } 144