1 /*
2  * Copyright (C) 2019 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.util;
17 
18 import android.net.TetherStatsParcel;
19 import android.net.TetheringRequestParcel;
20 import android.util.Log;
21 
22 import androidx.annotation.NonNull;
23 
24 import com.android.net.module.util.JniUtil;
25 import com.android.net.module.util.bpf.TetherStatsValue;
26 
27 import java.io.FileDescriptor;
28 import java.net.Inet6Address;
29 import java.net.SocketException;
30 import java.net.UnknownHostException;
31 import java.util.Arrays;
32 import java.util.Objects;
33 
34 /**
35  * The classes and the methods for tethering utilization.
36  *
37  * {@hide}
38  */
39 public class TetheringUtils {
40     static {
getTetheringJniLibraryName()41         System.loadLibrary(getTetheringJniLibraryName());
42     }
43 
44     public static final byte[] ALL_NODES = new byte[] {
45         (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
46     };
47 
48     /** The name should be com_android_networkstack_tethering_util_jni. */
getTetheringJniLibraryName()49     public static String getTetheringJniLibraryName() {
50         return JniUtil.getJniLibraryName(TetheringUtils.class.getPackage());
51     }
52 
53     /**
54      * Configures a socket for receiving and sending ICMPv6 neighbor advertisments.
55      * @param fd the socket's {@link FileDescriptor}.
56      */
setupNaSocket(FileDescriptor fd)57     public static native void setupNaSocket(FileDescriptor fd)
58             throws SocketException;
59 
60     /**
61      * Configures a socket for receiving and sending ICMPv6 neighbor solicitations.
62      * @param fd the socket's {@link FileDescriptor}.
63      */
setupNsSocket(FileDescriptor fd)64     public static native void setupNsSocket(FileDescriptor fd)
65             throws SocketException;
66 
67     /**
68      *  The object which records offload Tx/Rx forwarded bytes/packets.
69      *  TODO: Replace the inner class ForwardedStats of class OffloadHardwareInterface with
70      *  this class as well.
71      */
72     public static class ForwardedStats {
73         public final long rxBytes;
74         public final long rxPackets;
75         public final long txBytes;
76         public final long txPackets;
77 
ForwardedStats()78         public ForwardedStats() {
79             rxBytes = 0;
80             rxPackets = 0;
81             txBytes = 0;
82             txPackets = 0;
83         }
84 
ForwardedStats(long rxBytes, long txBytes)85         public ForwardedStats(long rxBytes, long txBytes) {
86             this.rxBytes = rxBytes;
87             this.rxPackets = 0;
88             this.txBytes = txBytes;
89             this.txPackets = 0;
90         }
91 
ForwardedStats(long rxBytes, long rxPackets, long txBytes, long txPackets)92         public ForwardedStats(long rxBytes, long rxPackets, long txBytes, long txPackets) {
93             this.rxBytes = rxBytes;
94             this.rxPackets = rxPackets;
95             this.txBytes = txBytes;
96             this.txPackets = txPackets;
97         }
98 
ForwardedStats(@onNull TetherStatsParcel tetherStats)99         public ForwardedStats(@NonNull TetherStatsParcel tetherStats) {
100             rxBytes = tetherStats.rxBytes;
101             rxPackets = tetherStats.rxPackets;
102             txBytes = tetherStats.txBytes;
103             txPackets = tetherStats.txPackets;
104         }
105 
ForwardedStats(@onNull TetherStatsValue tetherStats)106         public ForwardedStats(@NonNull TetherStatsValue tetherStats) {
107             rxBytes = tetherStats.rxBytes;
108             rxPackets = tetherStats.rxPackets;
109             txBytes = tetherStats.txBytes;
110             txPackets = tetherStats.txPackets;
111         }
112 
ForwardedStats(@onNull ForwardedStats other)113         public ForwardedStats(@NonNull ForwardedStats other) {
114             rxBytes = other.rxBytes;
115             rxPackets = other.rxPackets;
116             txBytes = other.txBytes;
117             txPackets = other.txPackets;
118         }
119 
120         /** Add Tx/Rx bytes/packets and return the result as a new object. */
121         @NonNull
add(@onNull ForwardedStats other)122         public ForwardedStats add(@NonNull ForwardedStats other) {
123             return new ForwardedStats(rxBytes + other.rxBytes, rxPackets + other.rxPackets,
124                     txBytes + other.txBytes, txPackets + other.txPackets);
125         }
126 
127         /** Subtract Tx/Rx bytes/packets and return the result as a new object. */
128         @NonNull
subtract(@onNull ForwardedStats other)129         public ForwardedStats subtract(@NonNull ForwardedStats other) {
130             // TODO: Perhaps throw an exception if any negative difference value just in case.
131             final long rxBytesDiff = Math.max(rxBytes - other.rxBytes, 0);
132             final long rxPacketsDiff = Math.max(rxPackets - other.rxPackets, 0);
133             final long txBytesDiff = Math.max(txBytes - other.txBytes, 0);
134             final long txPacketsDiff = Math.max(txPackets - other.txPackets, 0);
135             return new ForwardedStats(rxBytesDiff, rxPacketsDiff, txBytesDiff, txPacketsDiff);
136         }
137 
138         /** Returns the string representation of this object. */
139         @NonNull
toString()140         public String toString() {
141             return String.format("ForwardedStats(rxb: %d, rxp: %d, txb: %d, txp: %d)", rxBytes,
142                     rxPackets, txBytes, txPackets);
143         }
144     }
145 
146     /**
147      * Configures a socket for receiving ICMPv6 router solicitations and sending advertisements.
148      * @param fd the socket's {@link FileDescriptor}.
149      * @param ifIndex the interface index.
150      */
setupRaSocket(FileDescriptor fd, int ifIndex)151     public static native void setupRaSocket(FileDescriptor fd, int ifIndex)
152             throws SocketException;
153 
154     /**
155      * Read s as an unsigned 16-bit integer.
156      */
uint16(short s)157     public static int uint16(short s) {
158         return s & 0xffff;
159     }
160 
161     /** Check whether two TetheringRequestParcels are the same. */
isTetheringRequestEquals(final TetheringRequestParcel request, final TetheringRequestParcel otherRequest)162     public static boolean isTetheringRequestEquals(final TetheringRequestParcel request,
163             final TetheringRequestParcel otherRequest) {
164         if (request == otherRequest) return true;
165 
166         return request != null && otherRequest != null
167                 && request.tetheringType == otherRequest.tetheringType
168                 && Objects.equals(request.localIPv4Address, otherRequest.localIPv4Address)
169                 && Objects.equals(request.staticClientAddress, otherRequest.staticClientAddress)
170                 && request.exemptFromEntitlementCheck == otherRequest.exemptFromEntitlementCheck
171                 && request.showProvisioningUi == otherRequest.showProvisioningUi
172                 && request.connectivityScope == otherRequest.connectivityScope;
173     }
174 
175     /** Get inet6 address for all nodes given scope ID. */
getAllNodesForScopeId(int scopeId)176     public static Inet6Address getAllNodesForScopeId(int scopeId) {
177         try {
178             return Inet6Address.getByAddress("ff02::1", ALL_NODES, scopeId);
179         } catch (UnknownHostException uhe) {
180             Log.wtf("TetheringUtils", "Failed to construct Inet6Address from "
181                     + Arrays.toString(ALL_NODES) + " and scopedId " + scopeId);
182             return null;
183         }
184     }
185 }
186