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