1 /* 2 * Copyright (C) 2025 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 android.net.apf; 17 18 import static android.net.apf.BaseApfGenerator.Rbit.Rbit0; 19 import static android.net.apf.BaseApfGenerator.Rbit.Rbit1; 20 import static android.net.apf.BaseApfGenerator.Register.R0; 21 22 import androidx.annotation.NonNull; 23 24 import java.util.ArrayList; 25 import java.util.List; 26 import java.util.Set; 27 28 /** 29 * The abstract class for APFv6.1 assembler/generator. 30 * 31 * @param <Type> the generator class 32 * 33 * @hide 34 */ 35 public abstract class ApfV61GeneratorBase<Type extends ApfV61GeneratorBase<Type>> extends 36 ApfV6GeneratorBase<Type> { 37 38 /** 39 * Creates an ApfV61GeneratorBase instance. 40 */ ApfV61GeneratorBase(byte[] bytes, int version, int ramSize, int clampSize)41 public ApfV61GeneratorBase(byte[] bytes, int version, int ramSize, int clampSize) 42 throws IllegalInstructionException { 43 super(bytes, version, ramSize, clampSize); 44 } 45 46 @Override addCountAndDropIfR0Equals(long val, ApfCounterTracker.Counter cnt)47 public final Type addCountAndDropIfR0Equals(long val, ApfCounterTracker.Counter cnt) { 48 return addJumpIfR0Equals(val, cnt.getJumpDropLabel()); 49 } 50 51 @Override addCountAndPassIfR0Equals(long val, ApfCounterTracker.Counter cnt)52 public final Type addCountAndPassIfR0Equals(long val, ApfCounterTracker.Counter cnt) { 53 return addJumpIfR0Equals(val, cnt.getJumpPassLabel()); 54 } 55 56 @Override addCountAndDropIfR0NotEquals(long val, ApfCounterTracker.Counter cnt)57 public final Type addCountAndDropIfR0NotEquals(long val, ApfCounterTracker.Counter cnt) { 58 return addJumpIfR0NotEquals(val, cnt.getJumpDropLabel()); 59 } 60 61 @Override addCountAndPassIfR0NotEquals(long val, ApfCounterTracker.Counter cnt)62 public final Type addCountAndPassIfR0NotEquals(long val, ApfCounterTracker.Counter cnt) { 63 return addJumpIfR0NotEquals(val, cnt.getJumpPassLabel()); 64 } 65 66 @Override addCountAndDropIfR0AnyBitsSet(long val, ApfCounterTracker.Counter cnt)67 public Type addCountAndDropIfR0AnyBitsSet(long val, ApfCounterTracker.Counter cnt) { 68 return addJumpIfR0AnyBitsSet(val, cnt.getJumpDropLabel()); 69 } 70 71 @Override addCountAndPassIfR0AnyBitsSet(long val, ApfCounterTracker.Counter cnt)72 public Type addCountAndPassIfR0AnyBitsSet(long val, ApfCounterTracker.Counter cnt) { 73 return addJumpIfR0AnyBitsSet(val, cnt.getJumpPassLabel()); 74 } 75 76 @Override addCountAndDropIfR0LessThan(long val, ApfCounterTracker.Counter cnt)77 public final Type addCountAndDropIfR0LessThan(long val, ApfCounterTracker.Counter cnt) { 78 if (val <= 0) { 79 throw new IllegalArgumentException("val must > 0, current val: " + val); 80 } 81 return addJumpIfR0LessThan(val, cnt.getJumpDropLabel()); 82 } 83 84 @Override addCountAndPassIfR0LessThan(long val, ApfCounterTracker.Counter cnt)85 public final Type addCountAndPassIfR0LessThan(long val, ApfCounterTracker.Counter cnt) { 86 if (val <= 0) { 87 throw new IllegalArgumentException("val must > 0, current val: " + val); 88 } 89 return addJumpIfR0LessThan(val, cnt.getJumpPassLabel()); 90 } 91 92 @Override addCountAndDropIfR0GreaterThan(long val, ApfCounterTracker.Counter cnt)93 public Type addCountAndDropIfR0GreaterThan(long val, ApfCounterTracker.Counter cnt) { 94 if (val < 0 || val >= 4294967295L) { 95 throw new IllegalArgumentException("val must >= 0 and < 2^32-1, current val: " + val); 96 } 97 return addJumpIfR0GreaterThan(val, cnt.getJumpDropLabel()); 98 } 99 100 @Override addCountAndPassIfR0GreaterThan(long val, ApfCounterTracker.Counter cnt)101 public Type addCountAndPassIfR0GreaterThan(long val, ApfCounterTracker.Counter cnt) { 102 if (val < 0 || val >= 4294967295L) { 103 throw new IllegalArgumentException("val must >= 0 and < 2^32-1, current val: " + val); 104 } 105 return addJumpIfR0GreaterThan(val, cnt.getJumpPassLabel()); 106 } 107 108 @Override addCountAndDropIfBytesAtR0NotEqual(byte[] bytes, ApfCounterTracker.Counter cnt)109 public final Type addCountAndDropIfBytesAtR0NotEqual(byte[] bytes, 110 ApfCounterTracker.Counter cnt) { 111 return addJumpIfBytesAtR0NotEqual(bytes, cnt.getJumpDropLabel()); 112 } 113 114 @Override addCountAndPassIfBytesAtR0NotEqual(byte[] bytes, ApfCounterTracker.Counter cnt)115 public final Type addCountAndPassIfBytesAtR0NotEqual(byte[] bytes, 116 ApfCounterTracker.Counter cnt) { 117 return addJumpIfBytesAtR0NotEqual(bytes, cnt.getJumpPassLabel()); 118 } 119 120 @Override addCountAndPassIfR0IsOneOf(@onNull Set<Long> values, ApfCounterTracker.Counter cnt)121 public Type addCountAndPassIfR0IsOneOf(@NonNull Set<Long> values, 122 ApfCounterTracker.Counter cnt) { 123 if (values.isEmpty()) { 124 throw new IllegalArgumentException("values cannot be empty"); 125 } 126 if (values.size() == 1) { 127 return addCountAndPassIfR0Equals(values.iterator().next(), cnt); 128 } 129 return addJumpIfOneOf(R0, values, cnt.getJumpPassLabel()); 130 } 131 132 @Override addCountAndDropIfR0IsOneOf(@onNull Set<Long> values, ApfCounterTracker.Counter cnt)133 public Type addCountAndDropIfR0IsOneOf(@NonNull Set<Long> values, 134 ApfCounterTracker.Counter cnt) { 135 if (values.isEmpty()) { 136 throw new IllegalArgumentException("values cannot be empty"); 137 } 138 if (values.size() == 1) { 139 return addCountAndDropIfR0Equals(values.iterator().next(), cnt); 140 } 141 return addJumpIfOneOf(R0, values, cnt.getJumpDropLabel()); 142 } 143 144 @Override addCountAndPassIfR0IsNoneOf(@onNull Set<Long> values, ApfCounterTracker.Counter cnt)145 public Type addCountAndPassIfR0IsNoneOf(@NonNull Set<Long> values, 146 ApfCounterTracker.Counter cnt) { 147 if (values.isEmpty()) { 148 throw new IllegalArgumentException("values cannot be empty"); 149 } 150 if (values.size() == 1) { 151 return addCountAndPassIfR0NotEquals(values.iterator().next(), cnt); 152 } 153 return addJumpIfNoneOf(R0, values, cnt.getJumpPassLabel()); 154 } 155 156 @Override addCountAndDropIfBytesAtR0EqualsAnyOf(@onNull List<byte[]> bytesList, ApfCounterTracker.Counter cnt)157 public Type addCountAndDropIfBytesAtR0EqualsAnyOf(@NonNull List<byte[]> bytesList, 158 ApfCounterTracker.Counter cnt) { 159 return addJumpIfBytesAtR0EqualsAnyOf(bytesList, cnt.getJumpDropLabel()); 160 } 161 162 @Override addCountAndPassIfBytesAtR0EqualsAnyOf(@onNull List<byte[]> bytesList, ApfCounterTracker.Counter cnt)163 public Type addCountAndPassIfBytesAtR0EqualsAnyOf(@NonNull List<byte[]> bytesList, 164 ApfCounterTracker.Counter cnt) { 165 return addJumpIfBytesAtR0EqualsAnyOf(bytesList, cnt.getJumpPassLabel()); 166 } 167 168 @Override addCountAndDropIfBytesAtR0EqualsNoneOf(@onNull List<byte[]> bytesList, ApfCounterTracker.Counter cnt)169 public Type addCountAndDropIfBytesAtR0EqualsNoneOf(@NonNull List<byte[]> bytesList, 170 ApfCounterTracker.Counter cnt) { 171 return addJumpIfBytesAtR0EqualsNoneOf(bytesList, cnt.getJumpDropLabel()); 172 } 173 174 @Override addCountAndPassIfBytesAtR0EqualsNoneOf(@onNull List<byte[]> bytesList, ApfCounterTracker.Counter cnt)175 public Type addCountAndPassIfBytesAtR0EqualsNoneOf(@NonNull List<byte[]> bytesList, 176 ApfCounterTracker.Counter cnt) { 177 return addJumpIfBytesAtR0EqualsNoneOf(bytesList, cnt.getJumpPassLabel()); 178 } 179 180 @Override addCountAndDropIfR0IsNoneOf(@onNull Set<Long> values, ApfCounterTracker.Counter cnt)181 public Type addCountAndDropIfR0IsNoneOf(@NonNull Set<Long> values, 182 ApfCounterTracker.Counter cnt) { 183 if (values.isEmpty()) { 184 throw new IllegalArgumentException("values cannot be empty"); 185 } 186 if (values.size() == 1) { 187 return addCountAndDropIfR0NotEquals(values.iterator().next(), cnt); 188 } 189 return addJumpIfNoneOf(R0, values, cnt.getJumpDropLabel()); 190 } 191 192 @Override addJumpIfPktAtR0ContainDnsQ(byte[] qnames, int[] qtypes, short tgt)193 public final Type addJumpIfPktAtR0ContainDnsQ(byte[] qnames, int[] qtypes, short tgt) { 194 for (int i = 0; i < qtypes.length; i += 2) { 195 if (i == qtypes.length - 1) { 196 addJumpIfPktAtR0ContainDnsQ(qnames, qtypes[i], tgt); 197 } else { 198 addJumpIfPktAtR0ContainDnsQ2(qnames, qtypes[i], qtypes[i + 1], tgt); 199 } 200 } 201 return self(); 202 } 203 204 @Override addAllocate(int size)205 public Type addAllocate(int size) { 206 final int imm = (size > 266) ? (size - 266 + 7) / 8 : 0; 207 return append(new Instruction(Opcodes.ALLOC_XMIT, Rbit1).addUnsigned(imm)); 208 } 209 210 @Override addTransmitWithoutChecksum()211 public Type addTransmitWithoutChecksum() { 212 return append(new Instruction(Opcodes.ALLOC_XMIT, Rbit0)); 213 } 214 215 @Override handleOptimizedTransmit(int ipOfs, int csumOfs, int csumStart, int partialCsum, boolean isUdp)216 protected boolean handleOptimizedTransmit(int ipOfs, int csumOfs, int csumStart, 217 int partialCsum, boolean isUdp) { 218 if (ipOfs != 14) return false; 219 int v = -1; 220 if ( isUdp && csumStart == 26 && csumOfs == 40) v = 0; // ether/ipv4/udp 221 if (!isUdp && csumStart == 26 && csumOfs == 44) v = 1; // ether/ipv4/tcp 222 if (!isUdp && csumStart == 34 && csumOfs == 36) v = 2; // ether/ipv4/icmp 223 if (!isUdp && csumStart == 38 && csumOfs == 40) v = 3; // ether/ipv4/routeralert/icmp 224 if ( isUdp && csumStart == 22 && csumOfs == 60) v = 4; // ether/ipv6/udp 225 if (!isUdp && csumStart == 22 && csumOfs == 64) v = 5; // ether/ipv6/tcp 226 if (!isUdp && csumStart == 22 && csumOfs == 56) v = 6; // ether/ipv6/icmp 227 if (!isUdp && csumStart == 22 && csumOfs == 64) v = 7; // ether/ipv6/routeralert/icmp 228 if (v < 0) return false; 229 v |= partialCsum << 3; 230 append(new Instruction(Opcodes.ALLOC_XMIT, Rbit0).addUnsigned(v)); 231 return true; 232 } 233 addJumpIfBytesAtOffsetEqualsHelper(int offset, @NonNull List<byte[]> bytesList, short tgt, boolean jumpOnMatch)234 private List<byte[]> addJumpIfBytesAtOffsetEqualsHelper(int offset, 235 @NonNull List<byte[]> bytesList, short tgt, boolean jumpOnMatch) 236 throws IllegalInstructionException { 237 final List<byte[]> deduplicatedList = 238 bytesList.size() == 1 ? bytesList : validateDeduplicateBytesList(bytesList); 239 if (offset < 0 || offset > 255) { 240 return deduplicatedList; 241 } 242 final int count = deduplicatedList.size(); 243 final int compareLength = deduplicatedList.get(0).length; 244 if (compareLength > 16) { 245 return deduplicatedList; 246 } 247 final List<byte[]> failbackList = new ArrayList<>(); 248 final List<Integer> ptrs = new ArrayList<>(); 249 for (int i = 0; i < count; ++i) { 250 final byte[] bytes = deduplicatedList.get(i); 251 int relativeOffset = mInstructions.get(0).findMatchInDataBytes(bytes, 0, bytes.length); 252 if (relativeOffset < 0 || relativeOffset % 2 == 1 || relativeOffset > 510) { 253 failbackList.add(bytes); 254 continue; 255 } 256 ptrs.add(relativeOffset / 2); 257 } 258 final Rbit rbit = jumpOnMatch ? Rbit1 : Rbit0; 259 int totalPtrs = ptrs.size(); 260 for (int i = 0; i < totalPtrs; i += 16) { 261 final int currentCount = Math.min(totalPtrs - i, 16); 262 final Instruction instruction = new Instruction(Opcodes.JBSPTRMATCH, rbit) 263 .addU8(offset) 264 .addU8((currentCount - 1) * 16 + (compareLength - 1)) 265 .setTargetLabel(tgt); 266 for (int j = 0; j < currentCount; j++) { 267 instruction.addU8(ptrs.get(i + j)); 268 } 269 append(instruction); 270 } 271 return failbackList; 272 } 273 274 /** 275 * Add an instruction to the end of the program to jump to {@code tgt} if the bytes of the 276 * packet at an offset specified by {@code offset} match any of the elements in 277 * {@code bytesList}. 278 */ addJumpIfBytesAtOffsetEqualsAnyOf(int offset, @NonNull List<byte[]> bytesList, short tgt)279 public Type addJumpIfBytesAtOffsetEqualsAnyOf(int offset, @NonNull List<byte[]> bytesList, 280 short tgt) throws IllegalInstructionException { 281 final List<byte[]> failbackList = addJumpIfBytesAtOffsetEqualsHelper(offset, bytesList, tgt, 282 true /* jumpOnMatch */); 283 if (failbackList.isEmpty()) { 284 return self(); 285 } 286 return addLoadImmediate(R0, offset).addJumpIfBytesAtR0EqualsAnyOf(failbackList, tgt); 287 } 288 289 /** 290 * Add an instruction to the end of the program to jump to {@code tgt} if the bytes of the 291 * packet at an offset specified by {@code offset} match none of the elements in 292 * {@code bytesList}. 293 */ addJumpIfBytesAtOffsetEqualsNoneOf(int offset, @NonNull List<byte[]> bytesList, short tgt)294 public Type addJumpIfBytesAtOffsetEqualsNoneOf(int offset, @NonNull List<byte[]> bytesList, 295 short tgt) throws IllegalInstructionException { 296 final List<byte[]> failbackList = addJumpIfBytesAtOffsetEqualsHelper(offset, bytesList, tgt, 297 false /* jumpOnMatch */); 298 if (failbackList.isEmpty()) { 299 return self(); 300 } 301 return addLoadImmediate(R0, offset).addJumpIfBytesAtR0EqualsNoneOf(failbackList, tgt); 302 } 303 304 @Override addCountAndDropIfBytesAtOffsetEqualsAnyOf(int offset, List<byte[]> bytesList, ApfCounterTracker.Counter cnt)305 public Type addCountAndDropIfBytesAtOffsetEqualsAnyOf(int offset, List<byte[]> bytesList, 306 ApfCounterTracker.Counter cnt) throws IllegalInstructionException { 307 return addJumpIfBytesAtOffsetEqualsAnyOf(offset, bytesList, cnt.getJumpDropLabel()); 308 } 309 310 @Override addCountAndPassIfBytesAtOffsetEqualsAnyOf(int offset, List<byte[]> bytesList, ApfCounterTracker.Counter cnt)311 public Type addCountAndPassIfBytesAtOffsetEqualsAnyOf(int offset, List<byte[]> bytesList, 312 ApfCounterTracker.Counter cnt) throws IllegalInstructionException { 313 return addJumpIfBytesAtOffsetEqualsAnyOf(offset, bytesList, cnt.getJumpPassLabel()); 314 } 315 316 @Override addCountAndDropIfBytesAtOffsetEqualsNoneOf(int offset, List<byte[]> bytesList, ApfCounterTracker.Counter cnt)317 public Type addCountAndDropIfBytesAtOffsetEqualsNoneOf(int offset, List<byte[]> bytesList, 318 ApfCounterTracker.Counter cnt) throws IllegalInstructionException { 319 return addJumpIfBytesAtOffsetEqualsNoneOf(offset, bytesList, cnt.getJumpDropLabel()); 320 } 321 322 @Override addCountAndPassIfBytesAtOffsetEqualsNoneOf(int offset, List<byte[]> bytesList, ApfCounterTracker.Counter cnt)323 public Type addCountAndPassIfBytesAtOffsetEqualsNoneOf(int offset, List<byte[]> bytesList, 324 ApfCounterTracker.Counter cnt) throws IllegalInstructionException { 325 return addJumpIfBytesAtOffsetEqualsNoneOf(offset, bytesList, cnt.getJumpPassLabel()); 326 } 327 328 @Override addCountAndPassIfBytesAtOffsetNotEqual(int offset, byte[] bytes, ApfCounterTracker.Counter cnt)329 public Type addCountAndPassIfBytesAtOffsetNotEqual(int offset, byte[] bytes, 330 ApfCounterTracker.Counter cnt) throws IllegalInstructionException { 331 return addJumpIfBytesAtOffsetEqualsNoneOf(offset, List.of(bytes), cnt.getJumpPassLabel()); 332 } 333 334 @Override addCountAndDropIfBytesAtOffsetNotEqual(int offset, byte[] bytes, ApfCounterTracker.Counter cnt)335 public Type addCountAndDropIfBytesAtOffsetNotEqual(int offset, byte[] bytes, 336 ApfCounterTracker.Counter cnt) throws IllegalInstructionException { 337 return addJumpIfBytesAtOffsetEqualsNoneOf(offset, List.of(bytes), cnt.getJumpDropLabel()); 338 } 339 340 @Override addCountAndPassIfBytesAtOffsetEqual(int offset, byte[] bytes, ApfCounterTracker.Counter cnt)341 public Type addCountAndPassIfBytesAtOffsetEqual(int offset, byte[] bytes, 342 ApfCounterTracker.Counter cnt) throws IllegalInstructionException { 343 return addJumpIfBytesAtOffsetEqualsAnyOf(offset, List.of(bytes), cnt.getJumpPassLabel()); 344 } 345 346 @Override addCountAndDropIfBytesAtOffsetEqual(int offset, byte[] bytes, ApfCounterTracker.Counter cnt)347 public Type addCountAndDropIfBytesAtOffsetEqual(int offset, byte[] bytes, 348 ApfCounterTracker.Counter cnt) throws IllegalInstructionException { 349 return addJumpIfBytesAtOffsetEqualsAnyOf(offset, List.of(bytes), cnt.getJumpDropLabel()); 350 } 351 352 @Override addJumpIfBytesAtOffsetNotEqual(int offset, @NonNull byte[] bytes, short tgt)353 public Type addJumpIfBytesAtOffsetNotEqual(int offset, @NonNull byte[] bytes, short tgt) 354 throws IllegalInstructionException { 355 return addJumpIfBytesAtOffsetEqualsNoneOf(offset, List.of(bytes), tgt); 356 } 357 358 /** 359 * Appends a conditional jump instruction to the program: Jumps to {@code tgt} if the UDP 360 * payload's DNS questions contain the QNAMEs specified in {@code qnames} and qtype 361 * equals {@code qtype1} or {@code qtype2}. Examines the payload starting at the offset in R0. 362 * Drops packets if packets are corrupted. 363 */ addJumpIfPktAtR0ContainDnsQ2(@ndroid.annotation.NonNull byte[] qnames, int qtype1, int qtype2, short tgt)364 public final Type addJumpIfPktAtR0ContainDnsQ2(@android.annotation.NonNull byte[] qnames, 365 int qtype1, int qtype2, short tgt) { 366 validateNames(qnames); 367 return append(new Instruction(ExtendedOpcodes.JDNSQMATCH2, Rbit1).setTargetLabel(tgt) 368 .addU8(qtype1).addU8(qtype2).setBytesImm(qnames)); 369 } 370 371 /** 372 * Preload the content of the data region. 373 */ addPreloadData(@onNull byte[] data)374 public Type addPreloadData(@NonNull byte[] data) throws IllegalInstructionException { 375 mInstructions.get(0).maybeUpdateBytesImm(data, 0, data.length); 376 return self(); 377 } 378 } 379