blob: 10821bfded01c7bc7f47e80371fb53ebc3fab9de [file] [log] [blame]
/*
* 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_V5_H_
#define APF_INTERPRETER_V5_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_V5_H_ */