1 /* 2 * Copyright (C) 2023 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 17 package android.net.apf; 18 19 import android.util.ArrayMap; 20 import android.util.Log; 21 22 import com.android.internal.annotations.VisibleForTesting; 23 24 import java.util.Arrays; 25 import java.util.List; 26 import java.util.Map; 27 28 /** 29 * Counter class for {@code ApfFilter}. 30 * 31 * @hide 32 */ 33 public class ApfCounterTracker { 34 /** 35 * APF packet counters. 36 * 37 * Packet counters are 32bit big-endian values, and allocated near the end of the APF data 38 * buffer, using negative byte offsets, where -4 is equivalent to maximumApfProgramSize - 4, 39 * the last writable 32bit word. 40 */ 41 public enum Counter { 42 RESERVED_OOB, // Points to offset 0 from the end of the buffer (out-of-bounds) 43 ENDIANNESS, // APFv6 interpreter stores 0x12345678 here 44 TOTAL_PACKETS, // hardcoded in APFv6 interpreter 45 PASSED_ALLOCATE_FAILURE, // hardcoded in APFv6 interpreter 46 PASSED_TRANSMIT_FAILURE, // hardcoded in APFv6 interpreter 47 CORRUPT_DNS_PACKET, // hardcoded in APFv6 interpreter 48 EXCEPTIONS, // hardcoded in APFv6.1 interpreter 49 FILTER_AGE_SECONDS, 50 FILTER_AGE_16384THS, 51 APF_VERSION, 52 APF_PROGRAM_ID, 53 // The counter sequence should keep the same as ApfSessionInfoMetrics.java 54 PASSED_ARP_BROADCAST_REPLY, // see also MIN_PASS_COUNTER below. 55 PASSED_ARP_REQUEST, 56 PASSED_ARP_UNICAST_REPLY, 57 PASSED_DHCP, 58 PASSED_ETHER_OUR_SRC_MAC, 59 PASSED_IPV4, 60 PASSED_IPV4_FROM_DHCPV4_SERVER, 61 PASSED_IPV4_UNICAST, 62 PASSED_IPV6_HOPOPTS, 63 PASSED_IPV6_ICMP, 64 PASSED_IPV6_NON_ICMP, 65 PASSED_IPV6_UNICAST_NON_ICMP, 66 PASSED_NON_IP_UNICAST, 67 PASSED_MDNS, // see also MAX_PASS_COUNTER below 68 DROPPED_ETH_BROADCAST, // see also MIN_DROP_COUNTER below 69 DROPPED_ETHER_OUR_SRC_MAC, 70 DROPPED_RA, 71 DROPPED_IPV4_L2_BROADCAST, 72 DROPPED_IPV4_BROADCAST_ADDR, 73 DROPPED_IPV4_BROADCAST_NET, 74 DROPPED_IPV4_ICMP_INVALID, 75 DROPPED_IPV4_MULTICAST, 76 DROPPED_IPV4_NON_DHCP4, 77 DROPPED_IPV4_PING_REQUEST_REPLIED, 78 DROPPED_IPV6_ICMP6_ECHO_REQUEST_INVALID, 79 DROPPED_IPV6_ICMP6_ECHO_REQUEST_REPLIED, 80 DROPPED_IPV6_ROUTER_SOLICITATION, 81 DROPPED_IPV6_MLD_INVALID, 82 DROPPED_IPV6_MLD_REPORT, 83 DROPPED_IPV6_MLD_V1_GENERAL_QUERY_REPLIED, 84 DROPPED_IPV6_MLD_V2_GENERAL_QUERY_REPLIED, 85 DROPPED_IPV6_MULTICAST_NA, 86 DROPPED_IPV6_NON_ICMP_MULTICAST, 87 DROPPED_IPV6_NS_INVALID, 88 DROPPED_IPV6_NS_OTHER_HOST, 89 DROPPED_IPV6_NS_REPLIED_NON_DAD, 90 DROPPED_802_3_FRAME, 91 DROPPED_ETHERTYPE_NOT_ALLOWED, 92 DROPPED_IPV4_KEEPALIVE_ACK, 93 DROPPED_IPV4_NATT_KEEPALIVE, 94 DROPPED_MDNS, 95 DROPPED_MDNS_REPLIED, 96 DROPPED_NON_UNICAST_TDLS, 97 DROPPED_IPV4_TCP_PORT7_UNICAST, 98 DROPPED_ARP_NON_IPV4, 99 DROPPED_ARP_OTHER_HOST, 100 DROPPED_ARP_REPLY_SPA_NO_HOST, 101 DROPPED_ARP_REQUEST_REPLIED, 102 DROPPED_ARP_UNKNOWN, 103 DROPPED_ARP_V6_ONLY, 104 DROPPED_IGMP_V2_GENERAL_QUERY_REPLIED, 105 DROPPED_IGMP_V3_GENERAL_QUERY_REPLIED, 106 DROPPED_IGMP_INVALID, 107 DROPPED_IGMP_REPORT, 108 DROPPED_GARP_REPLY; // see also MAX_DROP_COUNTER below 109 110 /** 111 * Returns the negative byte offset from the end of the APF data segment for 112 * a given counter. 113 */ offset()114 public int offset() { 115 return -this.value() * 4; // Currently, all counters are 32bit long. 116 } 117 118 /** 119 * Returns the counter sequence number from the end of the APF data segment for 120 * a given counter. 121 */ value()122 public int value() { 123 return this.ordinal(); 124 } 125 126 /** 127 * Returns the total size of the data segment in bytes. 128 */ totalSize()129 public static int totalSize() { 130 return (Counter.class.getEnumConstants().length - 1) * 4; 131 } 132 133 /** 134 * Returns the counter enum based on the offset. 135 */ 136 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) getCounterEnumFromOffset(int offset)137 public static Counter getCounterEnumFromOffset(int offset) { 138 for (Counter cnt : Counter.class.getEnumConstants()) { 139 if (cnt.offset() == offset) { 140 return cnt; 141 } 142 } 143 return RESERVED_OOB; 144 } 145 checkCounterRange(Counter lowerBound, Counter upperBound)146 private void checkCounterRange(Counter lowerBound, Counter upperBound) { 147 if (value() < lowerBound.value() || value() > upperBound.value()) { 148 throw new IllegalArgumentException( 149 String.format("Counter %s, is not in range [%s, %s]", this, 150 lowerBound, upperBound)); 151 } 152 } 153 154 /** 155 * Return the label such that if we jump to it, the counter will be increased by 1 and 156 * the packet will be passed. 157 */ getJumpPassLabel()158 public short getJumpPassLabel() { 159 checkCounterRange(MIN_PASS_COUNTER, MAX_PASS_COUNTER); 160 return (short) (2 * this.value()); 161 } 162 163 /** 164 * Return the label such that if we jump to it, the counter will be increased by 1 and 165 * the packet will be dropped. 166 */ getJumpDropLabel()167 public short getJumpDropLabel() { 168 checkCounterRange(MIN_DROP_COUNTER, MAX_DROP_COUNTER); 169 return (short) (2 * this.value() + 1); 170 } 171 } 172 173 public static final Counter MIN_DROP_COUNTER = Counter.DROPPED_ETH_BROADCAST; 174 public static final Counter MAX_DROP_COUNTER = Counter.DROPPED_GARP_REPLY; 175 public static final Counter MIN_PASS_COUNTER = Counter.PASSED_ARP_BROADCAST_REPLY; 176 public static final Counter MAX_PASS_COUNTER = Counter.PASSED_MDNS; 177 178 private static final String TAG = ApfCounterTracker.class.getSimpleName(); 179 180 private final List<Counter> mCounterList; 181 // Store the counters' value 182 private final Map<Counter, Long> mCounters = new ArrayMap<>(); 183 ApfCounterTracker()184 public ApfCounterTracker() { 185 Counter[] counters = Counter.class.getEnumConstants(); 186 mCounterList = Arrays.asList(counters).subList(1, counters.length); 187 } 188 189 /** 190 * Get the value of a counter from APF data. 191 */ getCounterValue(byte[] data, Counter counter)192 public static long getCounterValue(byte[] data, Counter counter) 193 throws ArrayIndexOutOfBoundsException { 194 int offset = data.length + Counter.ENDIANNESS.offset(); 195 int endianness = 0; 196 for (int i = 0; i < 4; i++) { 197 endianness = endianness << 8 | (data[offset + i] & 0xff); 198 } 199 // Follow the same wrap-around addressing scheme of the interpreter. 200 offset = data.length + counter.offset(); 201 202 boolean isBe = true; 203 switch (endianness) { 204 case 0: 205 case 0x12345678: 206 isBe = true; 207 break; 208 case 0x78563412: 209 isBe = false; 210 break; 211 default: 212 Log.wtf(TAG, "Unknown endianness: 0x" + Integer.toHexString(endianness)); 213 } 214 215 // Decode 32bit big-endian integer into a long so we can count up beyond 2^31. 216 long value = 0; 217 for (int i = 0; i < 4; i++) { 218 value = value << 8 | (data[offset + (isBe ? i : 3 - i)] & 0xff); 219 } 220 return value; 221 } 222 223 /** 224 * Update counters from APF data. 225 */ updateCountersFromData(byte[] data)226 public void updateCountersFromData(byte[] data) { 227 if (data == null) return; 228 for (Counter counter : mCounterList) { 229 long value; 230 try { 231 value = getCounterValue(data, counter); 232 } catch (ArrayIndexOutOfBoundsException e) { 233 value = 0; 234 } 235 long oldValue = mCounters.getOrDefault(counter, 0L); 236 // All counters are incremental 237 if (value > oldValue) { 238 mCounters.put(counter, value); 239 } 240 } 241 } 242 243 /** 244 * Get counters map. 245 */ getCounters()246 public Map<Counter, Long> getCounters() { 247 return mCounters; 248 } 249 250 /** 251 * Clear all counters. 252 */ clearCounters()253 public void clearCounters() { 254 mCounters.clear(); 255 } 256 } 257