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