• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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