summaryrefslogtreecommitdiff
path: root/v6/apf_interpreter.h
blob: 71a760396cf71793aa4ed41fabd8dbb0a50cbf4f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/*
 * Copyright 2024, The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef APF_INTERPRETER_V6_H_
#define APF_INTERPRETER_V6_H_

#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
 * Returns the max version of the APF instruction set supported by apf_run().
 * APFv6 is a superset of APFv4. APFv6 interpreters are able to run APFv4 code.
 */
uint32_t apf_version(void);

/**
 * Allocates a buffer for the APF program to build a reply packet.
 *
 * Unless in a critical low memory state, the firmware must allow allocating at
 * least one 1514 byte buffer for every call to apf_run(). The interpreter will
 * have at most one active allocation at any given time, and will always either
 * transmit or deallocate the buffer before apf_run() returns.
 *
 * It is OK if the firmware decides to limit allocations to at most one per
 * apf_run() invocation. This allows the firmware to delay transmitting
 * the buffer until after apf_run() has returned (by keeping track of whether
 * a buffer was allocated/deallocated/scheduled for transmit) and may
 * allow the use of a single statically allocated 1514+ byte buffer.
 *
 * The firmware MAY choose to allocate a larger buffer than requested, and
 * give the apf_interpreter a pointer to the middle of the buffer. This will
 * allow firmware to later (during or after apf_transmit_buffer call) populate
 * any required headers, trailers, etc.
 *
 * @param ctx - unmodified ctx pointer passed into apf_run().
 * @param size - the minimum size of buffer to allocate
 * @return the pointer to the allocated region. The function can return NULL to
 *         indicate allocation failure, for example if too many buffers are
 *         pending transmit. Returning NULL will most likely result in
 *         apf_run() returning PASS.
 */
uint8_t* apf_allocate_buffer(void* ctx, uint32_t size);

/**
 * Transmits the allocated buffer and deallocates it.
 *
 * The apf_interpreter will not read/write from/to the buffer once it calls
 * this function.
 *
 * The content of the buffer between [ptr, ptr + len) are the bytes to be
 * transmitted, starting from the ethernet header and not including any
 * ethernet CRC bytes at the end.
 *
 * The firmware is expected to make its best effort to transmit. If it
 * exhausts retries, or if there is no channel for too long and the transmit
 * queue is full, then it is OK for the packet to be dropped. The firmware should
 * prefer to fail allocation if it can predict transmit will fail.
 *
 * apf_transmit_buffer() may be asynchronous, which means the actual packet
 * transmission can happen sometime after the function returns.
 *
 * @param ctx - unmodified ctx pointer passed into apf_run().
 * @param ptr - pointer to the transmit buffer, must have been previously
 *              returned by apf_allocate_buffer() and not deallocated.
 * @param len - the number of bytes to be transmitted (possibly less than
 *              the allocated buffer), 0 means don't transmit the buffer
 *              but only deallocate it.
 * @param dscp - value in [0..63] - the upper 6 bits of the TOS field in
 *               the IPv4 header or traffic class field in the IPv6 header.
 * @return non-zero if the firmware *knows* the transmit will fail, zero if
 *         the transmit succeeded or the firmware thinks it will succeed.
 *         Returning an error will likely result in apf_run() returning PASS.
 */
int apf_transmit_buffer(void* ctx, uint8_t* ptr, uint32_t len, uint8_t dscp);

/**
 * Runs an APF program over a packet.
 *
 * The return value of apf_run indicates whether the packet should
 * be passed or dropped. As a part of apf_run() execution, the APF
 * program can call apf_allocate_buffer()/apf_transmit_buffer() to construct
 * a reply packet and transmit it.
 *
 * The text section containing the program instructions starts at address
 * program and stops at + program_len - 1, and the writable data section
 * begins at program + program_len and ends at program + ram_len - 1,
 * as described in the following diagram:
 *
 *     program         program + program_len    program + ram_len
 *        |    text section    |      data section      |
 *        +--------------------+------------------------+
 *
 * @param ctx - pointer to any additional context required for allocation and transmit.
 *              May be NULL if no such context is required. This is opaque to
 *              the interpreter and will be passed through unmodified
 *              to apf_allocate_buffer() and apf_transmit_buffer() calls.
 * @param program - the program bytecode, followed by the writable data region.
 *                  Note: this *MUST* be a 4 byte aligned region.
 * @param program_len - the length in bytes of the read-only portion of the APF
 *                    buffer pointed to by {@code program}.
 *                    This is determined by the size of the loaded APF program.
 * @param ram_len - total length of the APF buffer pointed to by
 *                  {@code program}, including the read-only bytecode
 *                  portion and the read-write data portion.
 *                  This is expected to be a constant which doesn't change
 *                  value even when a new APF program is loaded.
 *                  Note: this *MUST* be a multiple of 4.
 * @param packet - the packet bytes, starting from the ethernet header.
 * @param packet_len - the length of {@code packet} in bytes, not
 *                     including trailers/CRC.
 * @param filter_age_16384ths - the number of 1/16384 seconds since the filter
 *                     was programmed.
 *
 * @return zero if packet should be dropped,
 *         non-zero if packet should be passed, specifically:
 *         - 1 on normal apf program execution,
 *         - 2 on exceptional circumstances (caused by bad firmware integration),
 *           these include:
 *             - program pointer which is not aligned to 4 bytes
 *             - ram_len which is not a multiple of 4 bytes
 *             - excessively large (>= 2GiB) program_len or ram_len
 *             - packet pointer which is a null pointer
 *             - packet_len shorter than ETH_HLEN (14)
 *           As such, you may want to log a firmware exception if 2 is ever returned...
 *
 *
 * NOTE: How to calculate filter_age_16384ths:
 *
 * - if you have a u64 clock source counting nanoseconds:
 *     u64 nanoseconds = current_nanosecond_time_u64() - filter_installation_nanosecond_time_u64;
 *     u32 filter_age_16384ths = (u32)((nanoseconds << 5) / 1953125);
 *
 * - if you have a u64 clock source counting microseconds:
 *     u64 microseconds = current_microsecond_time_u64() - filter_installation_microsecond_time_u64;
 *     u32 filter_age_16384ths = (u32)((microseconds << 8) / 15625);
 *
 * - if you have a u64 clock source counting milliseconds:
 *     u64 milliseconds = current_millisecond_time_u64() - filter_installation_millisecond_time_u64;
 *     u32 filter_age_16384ths = (u32)((milliseconds << 11) / 125);
 *
 * - if you have a u32 clock source counting milliseconds and cannot use 64-bit arithmetic:
 *     u32 milliseconds = current_millisecond_time_u32() - filter_installation_millisecond_time_u32;
 *     u32 filter_age_16384ths = ((((((milliseconds << 4) / 5) << 2) / 5) << 2) / 5) << 3;
 *   or the less precise:
 *     u32 filter_age_16384ths = ((milliseconds << 4) / 125) << 7;
 *
 * - if you have a u32 clock source counting seconds:
 *     u32 seconds = current_second_time_u32() - filter_installation_second_time_u32;
 *     u32 filter_age_16384ths = seconds << 14;
 */
int apf_run(void* ctx, uint32_t* const program, const uint32_t program_len,
            const uint32_t ram_len, const uint8_t* const packet,
            const uint32_t packet_len, const uint32_t filter_age_16384ths);

#ifdef __cplusplus
}
#endif

#endif  /* APF_INTERPRETER_V6_H_ */