/* * 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 #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_ */