1 /* 2 * Copyright (C) 2024 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 static android.net.apf.ApfConstants.IPV4_FRAGMENT_MORE_FRAGS_MASK; 20 import static android.net.apf.ApfConstants.IPV4_FRAGMENT_OFFSET_MASK; 21 import static android.net.apf.ApfConstants.IPV4_FRAGMENT_OFFSET_OFFSET; 22 import static android.net.apf.ApfCounterTracker.Counter.PASSED_IPV6_ICMP; 23 import static android.net.apf.BaseApfGenerator.Rbit.Rbit0; 24 import static android.net.apf.BaseApfGenerator.Register.R0; 25 import static android.net.apf.BaseApfGenerator.Register.R1; 26 27 28 import android.annotation.NonNull; 29 30 import com.android.internal.annotations.VisibleForTesting; 31 32 import java.util.List; 33 import java.util.Set; 34 35 /** 36 * APF assembler/generator. A tool for generating an APF program. 37 * 38 * Call add*() functions to add instructions to the program, then call 39 * {@link BaseApfGenerator#generate} to get the APF bytecode for the program. 40 * <p> 41 * Choose between these approaches for your instruction helper methods: If the functionality must 42 * be identical across APF versions, make it a final method within the base class. If it needs 43 * version-specific adjustments, use an abstract method in the base class with final 44 * implementations in generator instances. 45 * 46 * @param <Type> the generator class 47 * 48 * @hide 49 */ 50 public abstract class ApfV4GeneratorBase<Type extends ApfV4GeneratorBase<Type>> extends 51 BaseApfGenerator { 52 53 /** 54 * Creates an ApfV4GeneratorBase instance which is able to emit instructions for the specified 55 * {@code version} of the APF interpreter. Throws {@code IllegalInstructionException} if 56 * the requested version is unsupported. 57 */ 58 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) ApfV4GeneratorBase(int version, int ramSize, int clampSize, boolean disableCounterRangeCheck)59 public ApfV4GeneratorBase(int version, int ramSize, int clampSize, 60 boolean disableCounterRangeCheck) throws IllegalInstructionException { 61 super(version, ramSize, clampSize, disableCounterRangeCheck); 62 requireApfVersion(APF_VERSION_2); 63 } 64 self()65 final Type self() { 66 return (Type) this; 67 } 68 append(Instruction instruction)69 final Type append(Instruction instruction) { 70 if (mGenerated) { 71 throw new IllegalStateException("Program already generated"); 72 } 73 mInstructions.add(instruction); 74 return self(); 75 } 76 77 /** 78 * Define a label at the current end of the program. Jumps can jump to this label. Labels are 79 * their own separate instructions, though with size 0. This facilitates having labels with 80 * no corresponding code to execute, for example a label at the end of a program. For example 81 * an {@link ApfV4GeneratorBase} might be passed to a function that adds a filter like so: 82 * <pre> 83 * load from packet 84 * compare loaded data, jump if not equal to "next_filter" 85 * load from packet 86 * compare loaded data, jump if not equal to "next_filter" 87 * jump to drop label 88 * define "next_filter" here 89 * </pre> 90 * In this case "next_filter" may not have any generated code associated with it. 91 */ defineLabel(short name)92 public final Type defineLabel(short name) throws IllegalInstructionException { 93 return append(new Instruction(Opcodes.LABEL).setLabel(name)); 94 } 95 96 /** 97 * Add an unconditional jump instruction to the end of the program. 98 */ addJump(short target)99 public final Type addJump(short target) { 100 return append(new Instruction(Opcodes.JMP).setTargetLabel(target)); 101 } 102 103 /** 104 * Add an unconditional jump instruction to the next instruction - ie. a no-op. 105 */ addNop()106 public final Type addNop() { 107 return append(new Instruction(Opcodes.JMP).addUnsigned(0)); 108 } 109 110 /** 111 * Add an instruction to the end of the program to load the byte at offset {@code offset} 112 * bytes from the beginning of the packet into {@code register}. 113 */ addLoad8intoR0(int ofs)114 public final Type addLoad8intoR0(int ofs) { 115 return append(new Instruction(Opcodes.LDB, R0).addPacketOffset(ofs)); 116 } 117 118 /** 119 * Add an instruction to the end of the program to load 16-bits at offset {@code offset} 120 * bytes from the beginning of the packet into {@code register}. 121 */ addLoad16intoR0(int ofs)122 public final Type addLoad16intoR0(int ofs) { 123 return append(new Instruction(Opcodes.LDH, R0).addPacketOffset(ofs)); 124 } 125 126 /** 127 * Add an instruction to the end of the program to load 32-bits at offset {@code offset} 128 * bytes from the beginning of the packet into {@code register}. 129 */ addLoad32intoR0(int ofs)130 public final Type addLoad32intoR0(int ofs) { 131 return append(new Instruction(Opcodes.LDW, R0).addPacketOffset(ofs)); 132 } 133 134 /** 135 * Add an instruction to the end of the program to load a byte from the packet into 136 * {@code register}. The offset of the loaded byte from the beginning of the packet is 137 * the sum of {@code offset} and the value in register R1. 138 */ addLoad8R1IndexedIntoR0(int ofs)139 public final Type addLoad8R1IndexedIntoR0(int ofs) { 140 return append(new Instruction(Opcodes.LDBX, R0).addTwosCompUnsigned(ofs)); 141 } 142 143 /** 144 * Add an instruction to the end of the program to load 16-bits from the packet into 145 * {@code register}. The offset of the loaded 16-bits from the beginning of the packet is 146 * the sum of {@code offset} and the value in register R1. 147 */ addLoad16R1IndexedIntoR0(int ofs)148 public final Type addLoad16R1IndexedIntoR0(int ofs) { 149 return append(new Instruction(Opcodes.LDHX, R0).addTwosCompUnsigned(ofs)); 150 } 151 152 /** 153 * Add an instruction to the end of the program to load 32-bits from the packet into 154 * {@code register}. The offset of the loaded 32-bits from the beginning of the packet is 155 * the sum of {@code offset} and the value in register R1. 156 */ addLoad32R1IndexedIntoR0(int ofs)157 public final Type addLoad32R1IndexedIntoR0(int ofs) { 158 return append(new Instruction(Opcodes.LDWX, R0).addTwosCompUnsigned(ofs)); 159 } 160 161 /** 162 * Add an instruction to the end of the program to add {@code value} to register R0. 163 */ addAdd(long val)164 public abstract Type addAdd(long val); 165 166 /** 167 * Add an instruction to the end of the program to subtract {@code value} from register R0. 168 */ addSub(long val)169 public final Type addSub(long val) { 170 return addAdd(-val); // note: addSub(4 billion) isn't valid, as addAdd(-4 billion) isn't 171 } 172 173 /** 174 * Add an instruction to the end of the program to multiply register R0 by {@code value}. 175 */ addMul(long val)176 public final Type addMul(long val) { 177 if (val == 0) return addLoadImmediate(R0, 0); // equivalent, as APFv6 would '*= R1' 178 return append(new Instruction(Opcodes.MUL).addUnsigned(val)); 179 } 180 181 /** 182 * Add an instruction to the end of the program to divide register R0 by {@code value}. 183 */ addDiv(long val)184 public final Type addDiv(long val) { 185 if (val == 0) return addPass(); // equivalent, as APFv6 would '/= R1' 186 return append(new Instruction(Opcodes.DIV).addUnsigned(val)); 187 } 188 189 /** 190 * Add an instruction to the end of the program to logically and register R0 with {@code value}. 191 */ addAnd(long val)192 public abstract Type addAnd(long val); 193 194 /** 195 * Add an instruction to the end of the program to logically or register R0 with {@code value}. 196 */ addOr(long val)197 public final Type addOr(long val) { 198 if (val == 0) return self(); // nop, as APFv6 would '|= R1' 199 return append(new Instruction(Opcodes.OR).addTwosCompUnsigned(val)); 200 } 201 202 /** 203 * Add an instruction to the end of the program to shift left register R0 by {@code value} bits. 204 */ 205 // TODO: consider whether should change the argument type to byte addLeftShift(int val)206 public final Type addLeftShift(int val) { 207 if (val == 0) return self(); // nop, as APFv6 would '<<= R1' 208 return append(new Instruction(Opcodes.SH).addSigned(val)); 209 } 210 211 /** 212 * Add an instruction to the end of the program to shift right register R0 by {@code value} 213 * bits. 214 */ 215 // TODO: consider whether should change the argument type to byte addRightShift(int val)216 public final Type addRightShift(int val) { 217 return addLeftShift(-val); 218 } 219 220 // R0 op= R1, where op should be one of Opcodes.{ADD,MUL,DIV,AND,OR,SH} addR0ArithR1(Opcodes opcode)221 abstract void addR0ArithR1(Opcodes opcode); 222 223 /** 224 * Add an instruction to the end of the program to add register R1 to register R0. 225 */ addAddR1ToR0()226 public final Type addAddR1ToR0() { 227 addR0ArithR1(Opcodes.ADD); // R0 += R1 228 return self(); 229 } 230 231 /** 232 * Add an instruction to the end of the program to multiply register R0 by register R1. 233 */ addMulR0ByR1()234 public final Type addMulR0ByR1() { 235 addR0ArithR1(Opcodes.MUL); // R0 *= R1 236 return self(); 237 } 238 239 /** 240 * Add an instruction to the end of the program to divide register R0 by register R1. 241 */ addDivR0ByR1()242 public final Type addDivR0ByR1() { 243 addR0ArithR1(Opcodes.DIV); // R0 /= R1 244 return self(); 245 } 246 247 /** 248 * Add an instruction to the end of the program to logically and register R0 with register R1 249 * and store the result back into register R0. 250 */ addAndR0WithR1()251 public final Type addAndR0WithR1() { 252 addR0ArithR1(Opcodes.AND); // R0 &= R1 253 return self(); 254 } 255 256 /** 257 * Add an instruction to the end of the program to logically or register R0 with register R1 258 * and store the result back into register R0. 259 */ addOrR0WithR1()260 public final Type addOrR0WithR1() { 261 addR0ArithR1(Opcodes.OR); // R0 |= R1 262 return self(); 263 } 264 265 /** 266 * Add an instruction to the end of the program to shift register R0 left by the value in 267 * register R1. 268 */ addLeftShiftR0ByR1()269 public final Type addLeftShiftR0ByR1() { 270 addR0ArithR1(Opcodes.SH); // R0 <<= R1 271 return self(); 272 } 273 274 /** 275 * Add an instruction to the end of the program to move {@code value} into {@code register}. 276 */ addLoadImmediate(Register register, int value)277 public final Type addLoadImmediate(Register register, int value) { 278 return append(new Instruction(Opcodes.LI, register).addSigned(value)); 279 } 280 281 /** 282 * Add an instruction to the end of the program to jump to {@code target} if register R0's 283 * value equals {@code value}. 284 */ addJumpIfR0Equals(long val, short tgt)285 public final Type addJumpIfR0Equals(long val, short tgt) { 286 return append(new Instruction(Opcodes.JEQ).addTwosCompUnsigned(val).setTargetLabel(tgt)); 287 } 288 289 /** 290 * Add instructions to the end of the program to increase counter and drop packet if R0 equals 291 * {@code val} 292 * WARNING: may modify R1 293 */ addCountAndDropIfR0Equals(long val, ApfCounterTracker.Counter cnt)294 public abstract Type addCountAndDropIfR0Equals(long val, ApfCounterTracker.Counter cnt) 295 throws IllegalInstructionException; 296 297 /** 298 * Add instructions to the end of the program to increase counter and pass packet if R0 equals 299 * {@code val} 300 * WARNING: may modify R1 301 */ addCountAndPassIfR0Equals(long val, ApfCounterTracker.Counter cnt)302 public abstract Type addCountAndPassIfR0Equals(long val, ApfCounterTracker.Counter cnt) 303 throws IllegalInstructionException; 304 305 /** 306 * Add an instruction to the end of the program to jump to {@code target} if register R0's 307 * value does not equal {@code value}. 308 */ addJumpIfR0NotEquals(long val, short tgt)309 public final Type addJumpIfR0NotEquals(long val, short tgt) { 310 return append(new Instruction(Opcodes.JNE).addTwosCompUnsigned(val).setTargetLabel(tgt)); 311 } 312 313 /** 314 * Add instructions to the end of the program to increase counter and drop packet if R0 not 315 * equals {@code val} 316 * WARNING: may modify R1 317 */ addCountAndDropIfR0NotEquals(long val, ApfCounterTracker.Counter cnt)318 public abstract Type addCountAndDropIfR0NotEquals(long val, ApfCounterTracker.Counter cnt) 319 throws IllegalInstructionException; 320 321 /** 322 * Add instructions to the end of the program to increase counter and pass packet if R0 not 323 * equals {@code val} 324 * WARNING: may modify R1 325 */ addCountAndPassIfR0NotEquals(long val, ApfCounterTracker.Counter cnt)326 public abstract Type addCountAndPassIfR0NotEquals(long val, ApfCounterTracker.Counter cnt) 327 throws IllegalInstructionException; 328 329 /** 330 * Add an instruction to the end of the program to jump to {@code target} if register R0's 331 * value is greater than {@code value}. 332 */ addJumpIfR0GreaterThan(long val, short tgt)333 public final Type addJumpIfR0GreaterThan(long val, short tgt) { 334 return append(new Instruction(Opcodes.JGT).addUnsigned(val).setTargetLabel(tgt)); 335 } 336 337 /** 338 * Add instructions to the end of the program to increase counter and drop packet if R0 greater 339 * than {@code val} 340 * WARNING: may modify R1 341 */ addCountAndDropIfR0GreaterThan(long val, ApfCounterTracker.Counter cnt)342 public abstract Type addCountAndDropIfR0GreaterThan(long val, ApfCounterTracker.Counter cnt) 343 throws IllegalInstructionException; 344 345 /** 346 * Add instructions to the end of the program to increase counter and pass packet if R0 greater 347 * than {@code val} 348 * WARNING: may modify R1 349 */ addCountAndPassIfR0GreaterThan(long val, ApfCounterTracker.Counter cnt)350 public abstract Type addCountAndPassIfR0GreaterThan(long val, ApfCounterTracker.Counter cnt) 351 throws IllegalInstructionException; 352 353 /** 354 * Add an instruction to the end of the program to jump to {@code target} if register R0's 355 * value is less than {@code value}. 356 */ addJumpIfR0LessThan(long val, short tgt)357 public final Type addJumpIfR0LessThan(long val, short tgt) { 358 return append(new Instruction(Opcodes.JLT).addUnsigned(val).setTargetLabel(tgt)); 359 } 360 361 /** 362 * Add instructions to the end of the program to increase counter and drop packet if R0 less 363 * than {@code val} 364 * WARNING: may modify R1 365 */ addCountAndDropIfR0LessThan(long val, ApfCounterTracker.Counter cnt)366 public abstract Type addCountAndDropIfR0LessThan(long val, ApfCounterTracker.Counter cnt) 367 throws IllegalInstructionException; 368 369 /** 370 * Add instructions to the end of the program to increase counter and pass packet if R0 less 371 * than {@code val} 372 * WARNING: may modify R1 373 */ addCountAndPassIfR0LessThan(long val, ApfCounterTracker.Counter cnt)374 public abstract Type addCountAndPassIfR0LessThan(long val, ApfCounterTracker.Counter cnt) 375 throws IllegalInstructionException; 376 377 /** 378 * Add an instruction to the end of the program to jump to {@code target} if register R0's 379 * value has any bits set that are also set in {@code value}. 380 */ addJumpIfR0AnyBitsSet(long val, short tgt)381 public final Type addJumpIfR0AnyBitsSet(long val, short tgt) { 382 return append(new Instruction(Opcodes.JSET).addTwosCompUnsigned(val).setTargetLabel(tgt)); 383 } 384 385 /** 386 * Add an instruction to the end of the program to count and drop packet if register R0's 387 * value has any bits set that are also set in {@code value}. 388 * WARNING: may modify R1 389 */ addCountAndDropIfR0AnyBitsSet(long val, ApfCounterTracker.Counter cnt)390 public abstract Type addCountAndDropIfR0AnyBitsSet(long val, ApfCounterTracker.Counter cnt) 391 throws IllegalInstructionException; 392 393 /** 394 * Add an instruction to the end of the program to count and pass packet if register R0's 395 * value has any bits set that are also set in {@code value}. 396 * WARNING: may modify R1 397 */ addCountAndPassIfR0AnyBitsSet(long val, ApfCounterTracker.Counter cnt)398 public abstract Type addCountAndPassIfR0AnyBitsSet(long val, ApfCounterTracker.Counter cnt) 399 throws IllegalInstructionException; 400 401 /** 402 * Add an instruction to the end of the program to count and drop if the bytes of the 403 * packet at an offset specified by register R0 match any of the elements in {@code bytesList}. 404 * WARNING: may modify R1 405 */ addCountAndDropIfBytesAtR0EqualsAnyOf(@onNull List<byte[]> bytesList, ApfCounterTracker.Counter cnt)406 public abstract Type addCountAndDropIfBytesAtR0EqualsAnyOf(@NonNull List<byte[]> bytesList, 407 ApfCounterTracker.Counter cnt) throws IllegalInstructionException; 408 409 /** 410 * Add an instruction to the end of the program to count and pass if the bytes of the 411 * packet at an offset specified by register R0 match any of the elements in {@code bytesList}. 412 * WARNING: may modify R1 413 */ addCountAndPassIfBytesAtR0EqualsAnyOf(@onNull List<byte[]> bytesList, ApfCounterTracker.Counter cnt)414 public abstract Type addCountAndPassIfBytesAtR0EqualsAnyOf(@NonNull List<byte[]> bytesList, 415 ApfCounterTracker.Counter cnt) throws IllegalInstructionException; 416 417 /** 418 * Add an instruction to the end of the program to count and drop if the bytes of the 419 * packet at an offset specified by register R0 match none the elements in {@code bytesList}. 420 * WARNING: may modify R1 421 */ addCountAndDropIfBytesAtR0EqualsNoneOf(@onNull List<byte[]> bytesList, ApfCounterTracker.Counter cnt)422 public abstract Type addCountAndDropIfBytesAtR0EqualsNoneOf(@NonNull List<byte[]> bytesList, 423 ApfCounterTracker.Counter cnt) throws IllegalInstructionException; 424 425 /** 426 * Add an instruction to the end of the program to count and pass if the bytes of the 427 * packet at an offset specified by register R0 match none of the elements in {@code bytesList}. 428 * WARNING: may modify R1 429 */ addCountAndPassIfBytesAtR0EqualsNoneOf(@onNull List<byte[]> bytesList, ApfCounterTracker.Counter cnt)430 public abstract Type addCountAndPassIfBytesAtR0EqualsNoneOf(@NonNull List<byte[]> bytesList, 431 ApfCounterTracker.Counter cnt) throws IllegalInstructionException; 432 433 /** 434 * Add an instruction to the end of the program to jump to {@code target} if register R0's 435 * value equals register R1's value. 436 */ addJumpIfR0EqualsR1(short tgt)437 public final Type addJumpIfR0EqualsR1(short tgt) { 438 return append(new Instruction(Opcodes.JEQ, R1).setTargetLabel(tgt)); 439 } 440 441 /** 442 * Add an instruction to the end of the program to jump to {@code target} if register R0's 443 * value does not equal register R1's value. 444 */ addJumpIfR0NotEqualsR1(short tgt)445 public final Type addJumpIfR0NotEqualsR1(short tgt) { 446 return append(new Instruction(Opcodes.JNE, R1).setTargetLabel(tgt)); 447 } 448 449 /** 450 * Add an instruction to the end of the program to jump to {@code target} if register R0's 451 * value is greater than register R1's value. 452 */ addJumpIfR0GreaterThanR1(short tgt)453 public final Type addJumpIfR0GreaterThanR1(short tgt) { 454 return append(new Instruction(Opcodes.JGT, R1).setTargetLabel(tgt)); 455 } 456 457 /** 458 * Add an instruction to the end of the program to jump to {@code target} if register R0's 459 * value is less than register R1's value. 460 */ addJumpIfR0LessThanR1(short target)461 public final Type addJumpIfR0LessThanR1(short target) { 462 return append(new Instruction(Opcodes.JLT, R1).setTargetLabel(target)); 463 } 464 465 /** 466 * Add an instruction to the end of the program to jump to {@code target} if register R0's 467 * value has any bits set that are also set in R1's value. 468 */ addJumpIfR0AnyBitsSetR1(short tgt)469 public final Type addJumpIfR0AnyBitsSetR1(short tgt) { 470 return append(new Instruction(Opcodes.JSET, R1).setTargetLabel(tgt)); 471 } 472 473 /** 474 * Add an instruction to the end of the program to jump to {@code tgt} if the bytes of the 475 * packet at an offset specified by register0 don't match {@code bytes}. 476 * R=0 means check for not equal. 477 */ addJumpIfBytesAtR0NotEqual(@onNull byte[] bytes, short tgt)478 public final Type addJumpIfBytesAtR0NotEqual(@NonNull byte[] bytes, short tgt) { 479 validateBytes(bytes); 480 return append(new Instruction(Opcodes.JBSMATCH).addUnsigned( 481 bytes.length).setTargetLabel(tgt).setBytesImm(bytes)); 482 } 483 484 /** 485 * Add an instruction to the end of the program to jump to {@code tgt} if the bytes of the 486 * packet at an offset specified by {@code offset} don't match {@code bytes}. 487 * This method needs to be non-final because APFv4 and APFv6 share the same implementation, 488 * but in APFv6.1, this method will be overridden to use the JBSPTRMATCH instruction. 489 */ addJumpIfBytesAtOffsetNotEqual(int offset, @NonNull byte[] bytes, short tgt)490 public Type addJumpIfBytesAtOffsetNotEqual(int offset, @NonNull byte[] bytes, short tgt) 491 throws IllegalInstructionException { 492 return addLoadImmediate(R0, offset).addJumpIfBytesAtR0NotEqual(bytes, tgt); 493 } 494 495 /** 496 * Add instructions to the end of the program to increase counter and drop packet if the 497 * bytes of the packet at an offset specified by register0 don't match {@code bytes}. 498 * WARNING: may modify R1 499 */ addCountAndDropIfBytesAtR0NotEqual(byte[] bytes, ApfCounterTracker.Counter cnt)500 public abstract Type addCountAndDropIfBytesAtR0NotEqual(byte[] bytes, 501 ApfCounterTracker.Counter cnt) throws IllegalInstructionException; 502 503 /** 504 * Add instructions to the end of the program to increase counter and pass packet if the 505 * bytes of the packet at an offset specified by register0 don't match {@code bytes}. 506 * WARNING: may modify R1 507 */ addCountAndPassIfBytesAtR0NotEqual(byte[] bytes, ApfCounterTracker.Counter cnt)508 public abstract Type addCountAndPassIfBytesAtR0NotEqual(byte[] bytes, 509 ApfCounterTracker.Counter cnt) throws IllegalInstructionException; 510 511 /** 512 * Add instructions to the end of the program to increase counter and drop packet if the 513 * bytes of the packet at an offset specified by {@code offset} don't match {@code bytes}. 514 * This method needs to be non-final because APFv4 and APFv6 share the same implementation, 515 * but in APFv6.1, this method will be overridden to use the JBSPTRMATCH instruction. 516 */ addCountAndDropIfBytesAtOffsetNotEqual(int offset, byte[] bytes, ApfCounterTracker.Counter cnt)517 public Type addCountAndDropIfBytesAtOffsetNotEqual(int offset, byte[] bytes, 518 ApfCounterTracker.Counter cnt) throws IllegalInstructionException { 519 return addLoadImmediate(R0, offset).addCountAndDropIfBytesAtR0NotEqual(bytes, cnt); 520 } 521 522 /** 523 * Add instructions to the end of the program to increase counter and pass packet if the 524 * bytes of the packet at an offset specified by {@code offset} don't match {@code bytes}. 525 * This method needs to be non-final because APFv4 and APFv6 share the same implementation, 526 * but in APFv6.1, this method will be overridden to use the JBSPTRMATCH instruction. 527 */ addCountAndPassIfBytesAtOffsetNotEqual(int offset, byte[] bytes, ApfCounterTracker.Counter cnt)528 public Type addCountAndPassIfBytesAtOffsetNotEqual(int offset, byte[] bytes, 529 ApfCounterTracker.Counter cnt) throws IllegalInstructionException { 530 return addLoadImmediate(R0, offset).addCountAndPassIfBytesAtR0NotEqual(bytes, cnt); 531 } 532 533 /** 534 * Add instructions to the end of the program to increase counter and drop packet if the 535 * bytes of the packet at an offset specified by {@code offset} does match {@code bytes}. 536 * This method needs to be non-final because APFv4 and APFv6 share the same implementation, 537 * but in APFv6.1, this method will be overridden to use the JBSPTRMATCH instruction. 538 */ addCountAndDropIfBytesAtOffsetEqual(int offset, byte[] bytes, ApfCounterTracker.Counter cnt)539 public Type addCountAndDropIfBytesAtOffsetEqual(int offset, byte[] bytes, 540 ApfCounterTracker.Counter cnt) throws IllegalInstructionException { 541 return addLoadImmediate(R0, offset).addCountAndDropIfBytesAtR0Equal(bytes, cnt); 542 } 543 544 /** 545 * Add instructions to the end of the program to increase counter and pass packet if the 546 * bytes of the packet at an offset specified by {@code offset} does match {@code bytes}. 547 * This method needs to be non-final because APFv4 and APFv6 share the same implementation, 548 * but in APFv6.1, this method will be overridden to use the JBSPTRMATCH instruction. 549 */ addCountAndPassIfBytesAtOffsetEqual(int offset, byte[] bytes, ApfCounterTracker.Counter cnt)550 public Type addCountAndPassIfBytesAtOffsetEqual(int offset, byte[] bytes, 551 ApfCounterTracker.Counter cnt) throws IllegalInstructionException { 552 return addLoadImmediate(R0, offset).addCountAndPassIfBytesAtR0Equal(bytes, cnt); 553 } 554 555 /** 556 * Add instructions to the end of the program to increase counter and drop packet if the 557 * bytes of the packet at an offset specified by register0 match {@code bytes}. 558 * WARNING: may modify R1 559 */ addCountAndDropIfBytesAtR0Equal(byte[] bytes, ApfCounterTracker.Counter cnt)560 public final Type addCountAndDropIfBytesAtR0Equal(byte[] bytes, 561 ApfCounterTracker.Counter cnt) throws IllegalInstructionException { 562 final short tgt = getUniqueLabel(); 563 return addJumpIfBytesAtR0NotEqual(bytes, tgt).addCountAndDrop(cnt).defineLabel(tgt); 564 } 565 566 567 /** 568 * Add instructions to the end of the program to increase counter and pass packet if the 569 * bytes of the packet at an offset specified by register0 match {@code bytes}. 570 * WARNING: may modify R1 571 */ addCountAndPassIfBytesAtR0Equal(byte[] bytes, ApfCounterTracker.Counter cnt)572 public final Type addCountAndPassIfBytesAtR0Equal(byte[] bytes, 573 ApfCounterTracker.Counter cnt) throws IllegalInstructionException { 574 final short tgt = getUniqueLabel(); 575 return addJumpIfBytesAtR0NotEqual(bytes, tgt).addCountAndPass(cnt).defineLabel(tgt); 576 } 577 578 /** 579 * Add instructions to the end of the program to increase counter and pass packet if the 580 * value in register0 is one of {@code values}. 581 * WARNING: may modify R1 582 */ addCountAndPassIfR0IsOneOf(@onNull Set<Long> values, ApfCounterTracker.Counter cnt)583 public abstract Type addCountAndPassIfR0IsOneOf(@NonNull Set<Long> values, 584 ApfCounterTracker.Counter cnt) throws IllegalInstructionException; 585 586 /** 587 * Add instructions to the end of the program to increase counter and drop packet if the 588 * value in register0 is one of {@code values}. 589 * WARNING: may modify R1 590 */ addCountAndDropIfR0IsOneOf(@onNull Set<Long> values, ApfCounterTracker.Counter cnt)591 public abstract Type addCountAndDropIfR0IsOneOf(@NonNull Set<Long> values, 592 ApfCounterTracker.Counter cnt) throws IllegalInstructionException; 593 594 /** 595 * Add instructions to the end of the program to increase counter and pass packet if the 596 * value in register0 is none of {@code values}. 597 * WARNING: may modify R1 598 */ addCountAndPassIfR0IsNoneOf(@onNull Set<Long> values, ApfCounterTracker.Counter cnt)599 public abstract Type addCountAndPassIfR0IsNoneOf(@NonNull Set<Long> values, 600 ApfCounterTracker.Counter cnt) throws IllegalInstructionException; 601 602 /** 603 * Add instructions to the end of the program to increase counter and drop packet if the 604 * value in register0 is none of {@code values}. 605 * WARNING: may modify R1 606 */ addCountAndDropIfR0IsNoneOf(@onNull Set<Long> values, ApfCounterTracker.Counter cnt)607 public abstract Type addCountAndDropIfR0IsNoneOf(@NonNull Set<Long> values, 608 ApfCounterTracker.Counter cnt) throws IllegalInstructionException; 609 610 /** 611 * Add an instruction to the end of the program to load memory slot {@code slot} into 612 * {@code register}. 613 */ addLoadFromMemory(Register r, MemorySlot slot)614 public final Type addLoadFromMemory(Register r, MemorySlot slot) 615 throws IllegalInstructionException { 616 return append(new BaseApfGenerator.Instruction(ExtendedOpcodes.LDM, slot.value, r)); 617 } 618 619 /** 620 * Add an instruction to the end of the program to store {@code register} into memory slot 621 * {@code slot}. 622 */ addStoreToMemory(MemorySlot slot, Register r)623 public final Type addStoreToMemory(MemorySlot slot, Register r) 624 throws IllegalInstructionException { 625 return append(new Instruction(ExtendedOpcodes.STM, slot.value, r)); 626 } 627 628 /** 629 * Add instructions to the end of the program to check whether the packet is an unfragmented 630 * IPv4 packet with the specified protocol, and jump to the target if it is not. 631 * WARNING: this helper method will modify R0 632 */ addJumpIfNotUnfragmentedIPv4Protocol(long protocol, short tgt)633 public Type addJumpIfNotUnfragmentedIPv4Protocol(long protocol, short tgt) { 634 // Mask out all but the reserved and don't fragment bits, plus the TTL field. 635 // Because: 636 // IPV4_FRAGMENT_OFFSET_MASK = 0x1fff 637 // IPV4_FRAGMENT_MORE_FRAGS_MASK = 0x2000 638 // hence this constant ends up being 0x3FFF00FF. 639 // We want the more flag bit and offset to be 0 (ie. not a fragment), 640 // so after this masking we end up with just the ip protocol. 641 return addLoad32intoR0(IPV4_FRAGMENT_OFFSET_OFFSET) 642 .addAnd((IPV4_FRAGMENT_MORE_FRAGS_MASK | IPV4_FRAGMENT_OFFSET_MASK) << 16 | 0xFF) 643 .addJumpIfR0NotEquals(protocol, tgt); 644 } 645 646 /** 647 * Add an instruction to the end of the program to logically not {@code register}. 648 */ addNot(Register r)649 public final Type addNot(Register r) { 650 return append(new Instruction(ExtendedOpcodes.NOT, r)); 651 } 652 653 /** 654 * Add an instruction to the end of the program to negate {@code register}. 655 */ addNeg(Register r)656 public final Type addNeg(Register r) { 657 return append(new Instruction(ExtendedOpcodes.NEG, r)); 658 } 659 660 /** 661 * Add an instruction to swap the values in register R0 and register R1. 662 */ addSwap()663 public final Type addSwap() { 664 return append(new Instruction(ExtendedOpcodes.SWAP)); 665 } 666 667 /** 668 * Add an instruction to the end of the program to move the value into 669 * {@code register} from the other register. 670 */ addMove(Register r)671 public final Type addMove(Register r) { 672 return append(new Instruction(ExtendedOpcodes.MOVE, r)); 673 } 674 675 /** 676 * Add an instruction to the end of the program to let the program immediately return PASS. 677 */ addPass()678 public final Type addPass() { 679 // PASS requires using Rbit0 because it shares opcode with DROP 680 return append(new Instruction(Opcodes.PASSDROP, Rbit0)); 681 } 682 683 /** 684 * Abstract method for adding instructions to increment the counter and return PASS. 685 */ addCountAndPass(ApfCounterTracker.Counter counter)686 public abstract Type addCountAndPass(ApfCounterTracker.Counter counter); 687 688 /** 689 * Abstract method for adding instructions to increment the counter and return DROP. 690 */ addCountAndDrop(ApfCounterTracker.Counter counter)691 public abstract Type addCountAndDrop(ApfCounterTracker.Counter counter); 692 693 /** 694 * Add an instruction to the end of the program to load 32 bits from the data memory into 695 * {@code register}. 696 * In APFv2, it is a noop. 697 * WARNING: clobbers the *other* register. 698 */ addLoadCounter(Register register, ApfCounterTracker.Counter counter)699 public abstract Type addLoadCounter(Register register, ApfCounterTracker.Counter counter) 700 throws IllegalInstructionException; 701 702 /** 703 * Add an instruction to the end of the program to store 32 bits from {@code register} into the 704 * data memory. 705 * In APFv2, it is a noop. 706 * WARNING: clobbers the *other* register. 707 */ addStoreCounter(ApfCounterTracker.Counter counter, Register register)708 public abstract Type addStoreCounter(ApfCounterTracker.Counter counter, Register register) 709 throws IllegalInstructionException; 710 711 /** 712 * Add an instruction to the end of the program to increment counter value by {@code val). 713 * In APFv2, it is a noop. 714 * WARNING: clobbers both registers. 715 */ addIncrementCounter(ApfCounterTracker.Counter counter, int val)716 public final Type addIncrementCounter(ApfCounterTracker.Counter counter, int val) 717 throws IllegalInstructionException { 718 if (mVersion <= 2) return self(); 719 return addLoadCounter(R0, counter).addAdd(val).addStoreCounter(counter, R0); 720 } 721 722 /** 723 * Add an instruction to the end of the program to increment counter value by one. 724 * In APFv2, it is a noop. 725 * WARNING: clobbers both registers. 726 */ addIncrementCounter(ApfCounterTracker.Counter counter)727 public final Type addIncrementCounter(ApfCounterTracker.Counter counter) 728 throws IllegalInstructionException { 729 return addIncrementCounter(counter, 1); 730 } 731 732 /** 733 * The abstract method to generate count trampoline instructions. 734 * @return 735 * @throws IllegalInstructionException 736 */ addCountTrampoline()737 public abstract Type addCountTrampoline() throws IllegalInstructionException; 738 739 /** 740 * Returns the base program size when the interpreter is initialized. 741 */ getBaseProgramSize()742 public abstract int getBaseProgramSize(); 743 744 /** 745 * Appends default packet handling and counting to the APF program. 746 * This method adds logic to: 747 * 1. Increment the {@code PASSED_IPV6_ICMP} counter and pass the packet. 748 * 3. Add trampoline logic for counter processing. 749 * 750 * 751 * @return The type resulting from the appended instructions. 752 * @throws IllegalInstructionException If an error occurs while adding instructions. 753 */ addDefaultPacketHandling()754 public final Type addDefaultPacketHandling() throws IllegalInstructionException { 755 addCountAndPass(PASSED_IPV6_ICMP); 756 return addCountTrampoline(); 757 } 758 759 /** 760 * Returns an overestimate of the size of the default packet handling logic. 761 */ getDefaultPacketHandlingSizeOverEstimate()762 public abstract int getDefaultPacketHandlingSizeOverEstimate(); 763 } 764 765