← Cloudflare DDoS Protection / ddos-protection / advanced-ddos-systems / overview
Programmable Flow Protection (Beta)
Programmable Flow Protection представляет собой систему защиты от DDoS, которая противодействует DDoS-атакам на пользовательские или стандартизированные протоколы уровня 7 на базе UDP, такие как игровые протоколы, протоколы финансовых сервисов, VoIP, телеком и потоковую передачу. По топологии система поддерживает как асимметричные, так и симметричные конфигурации, но анализирует только входящий трафик.
Programmable Flow Protection сейчас находится в закрытой бета-версии и доступна как дополнение для Magic Transit (BYOIP или арендованных у Cloudflare IP-адресов) только для этой службы. Если вы хотите включить эту систему, свяжитесь с вашей командой по работе с аккаунтом или заполните эту форма ↗.
Как это работает
Система Programmable Flow Protection позволяет писать и запускать собственную программу с отслеживанием состояния на уровне пакетов на языке C в глобальной anycast-сети Cloudflare, используя программы extended Berkeley Packet Filter (eBPF), выполняемые в пользовательском пространстве. программа eBPF ↗ представляет собой систему фильтрации пакетов, позволяющую разработчику писать производительную собственную сетевую логику.
Programmable Flow Protection проверяет и разбирает протоколы вашего UDP-приложения (глубокий анализ пакетов, DPI) и определяет судьбу пакетов на основе вашей программы. Логика вашей собственной программы позволяет пропускать авторизованных пользователей и одновременно активно блокировать атаки.
Система построена на основе flowtrackd платформу, платформу Cloudflare для отслеживания состояния при смягчении атак. Система Programmable Flow Protection использует возможности системы DDoS Advanced Protection в отношении общие настройки для работы. Она учитывает префиксы, которые вы выбрали для маршрутизации через системы Advanced Protection, а также allowlist. Система Advanced DDoS Protection должна быть включено для работы системы Programmable Flow Protection.
Пока функция находится в бета-версии, Cloudflare будет помогать пользователям и давать рекомендации по написанию собственного кода. Готовые фрагменты кода (шаблоны) для популярных игровых и VoIP-протоколов могут быть добавлены позже.
Начало работы
После того как для вашего аккаунта включат Programmable Flow Protection, перейдите в Сеть > L3/4 DDoS Protection > Расширенная защита в дашборде Cloudflare. В разделе Programmable Flow Protection на вкладке:
-
Загрузите свою программу eBPF, написанную на языке C.
Система проверяет программу и сохраняет ее в вашем аккаунте. API компилирует программу, а затем запускает верификатор для скомпилированной программы, чтобы обеспечить проверку памяти и подтвердить корректное завершение программы. Если компиляция или проверка не пройдены, панель управления Cloudflare вернет подробное сообщение об ошибке.
-
Создайте правило
-
Чтобы отследить поведение программы, откройте панель Network Analytics и выберите Programmable Flow Protection на вкладке.
Вы можете создать дополнительные правила с другими настройки правила с ограниченной областью действия в различные регионы и локации Cloudflare, чтобы изменить режим (Mitigation или Monitoring), чтобы учесть особенности вашего трафика и бизнес-задачи.
Система Programmable Flow Protection поддерживает Data Localization suite.
Написание базовой программы
Приведенные ниже шаги описывают написание примера программы, которая отбрасывает весь трафик User Datagram Protocol (UDP) с заголовком IPv6. Она также отбрасывает трафик, направленный на порт 66, а также трафик, в полезной нагрузке UDP которого отсутствует определенное пользовательское значение заголовка приложения.
-
Добавьте директиву define, чтобы указать используемые версии вспомогательных функций.
По мере добавления новых функций в API Programmable Flow Protection компания Cloudflare будет публиковать новые версии этого API. Обратная совместимость версий гарантируется.
#define CF_EBPF_HELPER_V0 -
Подключите файлы заголовков Cloudflare eBPF.
Эти файлы имеют вспомогательные функции чтобы разобрать данные входящего пакета для BPF-программы.
#include <cf_ebpf_defs.h> #include <cf_ebpf_helper.h> -
Определите входную функцию для обработки пакетов.
Чтобы успешно пройти проверку программы в Cloudflare, ваша программа должна иметь точно такую сигнатуру функции, как указано ниже.
Тип возвращаемого значения
uint64_tопределяет, пропустит ли Cloudflare пакет или отбросит его. Имя функцииcf_ebpf_mainиспользуется как точка входа в программу. Аргументvoid *stateобозначает данные, которые Cloudflare передает на вход вашей программе BPF.uint64_t cf_ebpf_main(void *state) -
Преобразует входной аргумент в пригодные для использования структуры.
Преобразуйте входные данные в
cf_ebpf_generic_ctx, который сообщает Cloudflare границы данных в считываемой области памяти.Затем объявите переменные для разбора данных.
cf_ebpf_parsed_headersбудет содержать заголовки IPv4, IPv6 и UDP.cf_ebpf_packet_dataбудет содержать копию исходного IP-пакета, полученного Cloudflare (максимум 1,500 байт), а также длину пакета и длину IP-заголовка.struct cf_ebpf_generic_ctx *ctx = state; struct cf_ebpf_parsed_headers headers; struct cf_ebpf_packet_data *p; -
Заполните переменные, вызвав вспомогательную функцию.
Заполните переменные, вызвав вспомогательную функцию
parse_packet_data, который Cloudflare предоставила в файле заголовка, указанном в шаге 2.parse_packet_dataфункция выполняет проверки памяти, необходимые для прохождения верификатора программы. Функцияparse_packet_dataвозвращает0в случае успеха. Если операция выполнена успешно, входные параметры будут корректно заполнены.parse_packet_dataвозвращает1в случае ошибки. Еслиparse_packet_dataзавершается неудачно, программа должна вернутьCF_EBPF_DROPчтобы отбросить пакет и пройти проверку верификатором.if (parse_packet_data(ctx, &p, &headers) != 0) { return CF_EBPF_DROP; }Доступные значения после успешного разбора:
struct cf_ebpf_packet_data { /* Total length of the packet. */ size_t total_packet_length; /* Size of the IP header. Supports IPv4 (including options) and IPv6. */ size_t ip_header_length; /* Bytes of the packet, starting with the IP header. */ uint8_t packet_buffer[1500]; }; struct cf_ebpf_parsed_headers { /* Pointer to the parsed IPv4 header, if present (otherwise null). */ struct iphdr *ipv4; /* Pointer to the parsed IPv6 header, if present (otherwise null). */ struct ipv6hdr *ipv6; /* Pointer to the parsed UDP header. */ struct udphdr *udp; /* Raw pointer to the last valid byte of the packet context data. */ uint8_t *data_end; };Полное описание вспомогательных функций и структур см. в Вспомогательные функции и структуры BPF.
-
Напишите собственную логику.
На предыдущих шагах был определен код, который остается неизменным для любой создаваемой вами программы, независимо от ее логики.
Теперь вы можете писать собственную логику.
В приведённом ниже примере программа отбрасывает любой пакет, в котором присутствует заголовок IPv6 или порт назначения UDP равен 66.
Затем она проверяет значение заголовка приложения в полезной нагрузке UDP и убеждается, что его последний байт равен фиксированному значению
0xCF.struct ipv6hdr *ipv6_hdr; struct udphdr *udp_hdr; ipv6_hdr = (struct ipv6hdr *)headers.ipv6; if (ipv6_hdr != NULL) { return CF_EBPF_DROP; } udp_hdr = (struct udphdr *)headers.udp; if (ntohs(udp_hdr->dest) == 66) { return CF_EBPF_DROP; } struct apphdr *app = (struct apphdr *)(udp_hdr + 1); if ((uint8_t *)(app + 1) > headers.data_end) { return CF_EBPF_DROP; } // The verifier has a special limit that it will not allow offsets // beyond 65535. We need this check (token_len > 64000) in order // to satisfy that, even though it is not possible. uint16_t token_len = app->length; if (token_len > 64000) { return CF_EBPF_DROP; } if ((uint8_t *)(app->token + token_len) > headers.data_end) { return CF_EBPF_DROP; } uint8_t *last_byte = app->token + token_len - 1; if (*last_byte != 0xCF) { return CF_EBPF_DROP; } -
Пропускайте все пакеты, которые не были отброшены логикой программы, вернув
CF_EBPF_PASS.В настоящее время поддерживаются следующие возвращаемые значения:
CF_EBPF_PASS = return value 0CF_EBPF_DROP = return value 1
Верификатор, который запускается при загрузке программы в API, обеспечивает, чтобы программа возвращала только известные типы значений.
return CF_EBPF_PASS;
Для справки: ниже приведён базовый пример программы целиком:
#define CF_EBPF_HELPER_V0
#include <cf_ebpf_defs.h>
#include <cf_ebpf_helper.h>
struct apphdr {
uint8_t version;
uint16_t length; // Length of the variable-length token
unsigned char token[0]; // Variable-length token
} __attribute__((packed));
uint64_t
cf_ebpf_main(void *state)
{
struct cf_ebpf_generic_ctx *ctx = state;
struct cf_ebpf_parsed_headers headers;
struct cf_ebpf_packet_data *p;
if (parse_packet_data(ctx, &p, &headers) != 0) {
return CF_EBPF_DROP;
}
struct ipv6hdr *ipv6_hdr;
struct udphdr *udp_hdr;
ipv6_hdr = (struct ipv6hdr *)headers.ipv6;
if (ipv6_hdr != NULL) {
return CF_EBPF_DROP;
}
udp_hdr = (struct udphdr *)headers.udp;
if (ntohs(udp_hdr->dest) == 66) {
return CF_EBPF_DROP;
}
struct apphdr *app = (struct apphdr *)(udp_hdr + 1);
if ((uint8_t *)(app + 1) > headers.data_end) {
return CF_EBPF_DROP;
}
// The verifier has a special limit that it will not allow offsets
// beyond 65535. We need this check (token_len > 64000) in order
// to satisfy that, even though it is not possible.
uint16_t token_len = app->length;
if (token_len > 64000) {
return CF_EBPF_DROP;
}
if ((uint8_t *)(app->token + token_len) > headers.data_end) {
return CF_EBPF_DROP;
}
uint8_t *last_byte = app->token + token_len - 1;
if (*last_byte != 0xCF) {
return CF_EBPF_DROP;
}
return CF_EBPF_PASS;
}Написание сложной программы: ответ на основе challenge
Пример программы ниже реализует механизм проверки с ответом на основе UDP, используя вспомогательные функции для хранения состояния между пакетами от одного и того же исходного IP-адреса. Это полезно для нейтрализации DDoS-атак, поскольку клиенты должны доказать, что могут получать проверочные запросы и отвечать на них, прежде чем их трафик будет пропущен.
Механизм проверки работает следующим образом:
Когда пакет поступает с неизвестного исходного IP-адреса, программа генерирует challenge-пакет со случайным nonce и помечает исходный IP-адрес как "challenged" в таблице состояний. Исходный пакет отбрасывается.
Если пакет приходит с исходного IP-адреса, которому уже был отправлен challenge, программа проверяет, содержит ли пакет правильный ответ challenge (nonce, к которому применена операция XOR с секретным значением). Если ответ верный, исходный IP-адрес помечается как «verified». Если ответ неверный, исходный IP-адрес немедленно блокируется.
Пакеты с проверенных исходных IP-адресов пропускаются без дальнейших проверок.
-
Подключите файлы заголовков Cloudflare eBPF и укажите версию хелпера.
#define CF_EBPF_HELPER_V0 #include <cf_ebpf_defs.h> #include <cf_ebpf_helper.h> -
Определите константы для протокола запрос-ответ (challenge-response).
Ответ на проверку вычисляется путём применения операции XOR к nonce и секретному значению. Время истечения определяет, как долго остаётся действительным статус challenged или verified.
#define CHALLENGE_SECRET 0xDEADBEEFCAFEBABEULL #define CHALLENGE_EXPIRY_SECS 60 #define VERIFIED_EXPIRY_SECS 3600 -
Определите структуру проверочных пакетов.
Пакет проверки содержит nonce, на который должен ответить клиент, а также место для ответа клиента.
struct challenge_packet { uint64_t nonce; // Random nonce for this challenge uint64_t response; // Expected: nonce XOR CHALLENGE_SECRET }; -
Определите входную функцию и разберите пакет.
uint64_t cf_ebpf_main(void *state) { struct cf_ebpf_generic_ctx *ctx = state; struct cf_ebpf_parsed_headers headers; struct cf_ebpf_packet_data *p; if (parse_packet_data(ctx, &p, &headers) != 0) { return CF_EBPF_DROP; } struct udphdr *udp_hdr = headers.udp; -
Проверьте статус IP-адреса источника с помощью
get_src_ip_status.Статус показывает, является ли исходный IP-адрес новым, проверяемым, подтвержденным или заблокированным. Метка времени истечения срока действия показывает, когда статус перестанет действовать.
uint8_t status; uint64_t expiry; int ret = get_src_ip_status(&status, &expiry); // Check if status has expired int64_t now = timestamp(); if (ret == 0 && expiry > 0 && (uint64_t)now > expiry) { // Status expired, treat as new connection ret = -1; } -
Обработайте проверенные исходные IP-адреса.
Платформа Programmable Flow Protection отбрасывает пакеты с заблокированных IP-адресов ещё до запуска программы, поэтому обрабатывать случай блокировки в самой программе не требуется.
Если исходный IP-адрес уже был подтверждён (verified, то есть ранее прошёл challenge), пропустите пакет.
if (ret == 0 && status == CF_EBPF_SRC_IP_STATUS_VERIFIED) { return CF_EBPF_PASS; } -
Проверяет, является ли это ответом на challenge от IP-адреса источника, которому был отправлен challenge.
Если исходному IP-адресу ранее уже отправлялся challenge, проверьте, содержит ли текущий пакет верный ответ challenge. Если ответ верный, пометьте исходный IP-адрес как verified. Если ответ неверный, немедленно заблокируйте исходный IP-адрес.
if (ret == 0 && status == CF_EBPF_SRC_IP_STATUS_CHALLENGED) { // Get the stored nonce from user data uint64_t stored_nonce; if (get_src_ip_data(&stored_nonce) != 0) { return CF_EBPF_DROP; } // Parse the challenge response from the packet payload struct challenge_packet *resp = (struct challenge_packet *)(udp_hdr + 1); if ((uint8_t *)(resp + 1) > headers.data_end) { return CF_EBPF_DROP; } // Verify the response: should be nonce XOR secret uint64_t expected_response = stored_nonce ^ CHALLENGE_SECRET; if (resp->response == expected_response) { // Correct response - mark as verified set_src_ip_status(CF_EBPF_SRC_IP_STATUS_VERIFIED, VERIFIED_EXPIRY_SECS); set_src_ip_data(0); // Clear the nonce return CF_EBPF_PASS; } // Wrong response - blocklist immediately set_src_ip_status(CF_EBPF_SRC_IP_STATUS_BLOCKLISTED, 0); return CF_EBPF_DROP; } -
Выдавать новую проверку для новых исходных IP-адресов.
Сгенерируйте случайный nonce, сохраните его в таблице состояний, создайте challenge-пакет и отправьте его с помощью
set_challenge.// Generate a new challenge for this source IP uint64_t nonce = rand(); // Store the nonce and mark as challenged set_src_ip_status(CF_EBPF_SRC_IP_STATUS_CHALLENGED, CHALLENGE_EXPIRY_SECS); set_src_ip_data(nonce); // Build the challenge packet to send back struct challenge_packet challenge; challenge.nonce = nonce; challenge.response = 0; // Client will fill this in // Set the challenge packet buffer set_challenge((uint8_t *)&challenge, sizeof(challenge)); // Drop the original packet until client responds to challenge return CF_EBPF_DROP; }
Для справки: ниже приведён сложный пример программы целиком:
#define CF_EBPF_HELPER_V0
#include <cf_ebpf_defs.h>
#include <cf_ebpf_helper.h>
// Challenge-response protocol constants
#define CHALLENGE_SECRET 0xDEADBEEFCAFEBABEULL
#define CHALLENGE_EXPIRY_SECS 60
#define VERIFIED_EXPIRY_SECS 3600
// Challenge packet structure
struct challenge_packet {
uint64_t nonce;
uint64_t response;
};
uint64_t cf_ebpf_main(void *state)
{
struct cf_ebpf_generic_ctx *ctx = state;
struct cf_ebpf_parsed_headers headers;
struct cf_ebpf_packet_data *p;
if (parse_packet_data(ctx, &p, &headers) != 0) {
return CF_EBPF_DROP;
}
struct udphdr *udp_hdr = headers.udp;
// Check source IP status
uint8_t status;
uint64_t expiry;
int ret = get_src_ip_status(&status, &expiry);
// Check if status has expired
int64_t now = timestamp();
if (ret == 0 && expiry > 0 && (uint64_t)now > expiry) {
ret = -1; // Treat as new connection
}
// Handle verified source IPs - allow through
if (ret == 0 && status == CF_EBPF_SRC_IP_STATUS_VERIFIED) {
return CF_EBPF_PASS;
}
// Handle challenged source IPs - check for valid response
if (ret == 0 && status == CF_EBPF_SRC_IP_STATUS_CHALLENGED) {
uint64_t stored_nonce;
if (get_src_ip_data(&stored_nonce) != 0) {
return CF_EBPF_DROP;
}
// Parse challenge response from packet payload
struct challenge_packet *resp = (struct challenge_packet *)(udp_hdr + 1);
if ((uint8_t *)(resp + 1) > headers.data_end) {
return CF_EBPF_DROP;
}
// Check response using XOR
uint64_t expected_response = stored_nonce ^ CHALLENGE_SECRET;
if (resp->response == expected_response) {
// Correct response - mark as verified
set_src_ip_status(CF_EBPF_SRC_IP_STATUS_VERIFIED, VERIFIED_EXPIRY_SECS);
set_src_ip_data(0);
return CF_EBPF_PASS;
}
// Wrong response - blocklist immediately
set_src_ip_status(CF_EBPF_SRC_IP_STATUS_BLOCKLISTED, 0);
return CF_EBPF_DROP;
}
// New source IP - issue initial challenge
uint64_t nonce = rand();
set_src_ip_status(CF_EBPF_SRC_IP_STATUS_CHALLENGED, CHALLENGE_EXPIRY_SECS);
set_src_ip_data(nonce);
struct challenge_packet challenge;
challenge.nonce = nonce;
challenge.response = 0;
set_challenge((uint8_t *)&challenge, sizeof(challenge));
return CF_EBPF_DROP;
}Эта программа демонстрирует несколько ключевых концепций:
- State management: С помощью
get_src_ip_status,set_src_ip_status,get_src_ip_data, а такжеset_src_ip_dataчтобы отслеживать состояние challenge для каждого исходного IP-адреса. - Отправка Challenge: С помощью
set_challengeчтобы отправить клиенту ответный challenge-пакет. - Криптографическая проверка: Используется общий секрет для проверки того, что клиент правильно ответил на challenge.
- Обработка истечения срока действия: Используются метки времени для удаления устаревших записей состояния.
Написание сложной программы: ограничение частоты запросов (rate limiting)
Пример программы ниже реализует ограничитель частоты запросов для каждого исходного IP-адреса на основе алгоритма фиксированного окна. Это полезно для нейтрализации объёмных DDoS-атак, поскольку ограничивает количество пакетов, которые один исходный IP-адрес может отправить в течение временного окна.
Механизм ограничения скорости запросов работает следующим образом:
При поступлении пакета программа получает сохраненное состояние для этого исходного IP-адреса. Состояние содержит метку времени начала окна и счетчик пакетов, упакованные в одно 64-битное значение. Если текущее время еще находится в пределах окна, счетчик увеличивается. Если счетчик превышает заданный лимит, пакет отбрасывается. По истечении окна счетчик сбрасывается.
-
Подключите файлы заголовков Cloudflare eBPF и укажите версию хелпера.
#include <cf_ebpf_defs.h> #define CF_EBPF_HELPER_V0 #include <cf_ebpf_helper.h> -
Определите константы для конфигурации ограничения частоты запросов.
RATE_LIMITзадает максимальное количество пакетов, разрешенное в течение окна.WINDOW_SECONDSзадаёт длительность каждого временного окна в секундах.#define RATE_LIMIT 100 // Maximum packets allowed per window #define WINDOW_SECONDS 60 // Time window in seconds -
Определите макросы для упаковки и распаковки данных состояния.
Таблица состояний исходных IP-адресов хранит одну
u64значение на каждый исходный IP-адрес. Чтобы хранить одновременно временную метку и счётчик, упакуйте их в это значение: временную метку в старших 32 битах, а счётчик в младших 32 битах.#define PACK_STATE(ts, count) (((uint64_t)(ts) << 32) | ((uint64_t)(count) & 0xFFFFFFFF)) #define UNPACK_TIMESTAMP(data) ((uint32_t)((data) >> 32)) #define UNPACK_COUNTER(data) ((uint32_t)((data) & 0xFFFFFFFF)) -
Определите входную функцию и получите текущую метку времени.
Если вспомогательная функция получения временной метки завершается ошибкой, пропустите пакет, чтобы избежать ложных срабатываний.
uint64_t cf_ebpf_main(void *state) { // Get current timestamp int64_t now = timestamp(); if (now < 0) { return CF_EBPF_PASS; // If timestamp fails, allow the packet } uint32_t now_secs = (uint32_t)now; -
Получить текущее состояние для этого исходного IP-адреса.
Используйте
get_src_ip_dataчтобы проверить, встречался ли этот исходный IP-адрес ранее.// Try to get existing state for this source IP uint64_t data; int ret = get_src_ip_data(&data); uint32_t window_start; uint32_t counter; -
Обработайте случай, когда это новый исходный IP-адрес.
Если запись не существует (возвращаемое значение равно
-1), это первый пакет от данного исходного IP-адреса. Инициализируйте окно, установив его начало на текущий момент со счётчиком, равным 1.if (ret == -1) { // No existing entry - first packet from this IP // Initialize: window starts now, counter = 1 window_start = now_secs; counter = 1; } -
Обработайте существующие исходные IP-адреса и проверьте временное окно.
Если запись существует, извлеките сохранённые временную метку и счётчик. Если окно истекло, сбросьте оба значения. В противном случае увеличьте счётчик и проверьте, не превышает ли он лимит скорости.
} else if (ret != 0) { // If there's other unknown error with getting src_ip_data, pass packet return CF_EBPF_PASS; } else { // Entry exists - unpack the state window_start = UNPACK_TIMESTAMP(data); counter = UNPACK_COUNTER(data); // Check if we're still in the same time window if (now_secs - window_start >= WINDOW_SECONDS) { // Window expired - reset counter and start new window window_start = now_secs; counter = 1; } else { // Still in same window - increment counter counter++; // Check if rate limit exceeded if (counter > RATE_LIMIT) { // Drop packet without updating state return CF_EBPF_DROP; } } } -
Сохраните обновлённое состояние и пропустите пакет.
Упакуйте временную метку начала окна и счётчик обратно в одно значение и сохраните его в таблице состояний исходных IP-адресов.
// Store updated state uint64_t new_data = PACK_STATE(window_start, counter); set_src_ip_data(new_data); return CF_EBPF_PASS; }
Для справки: ниже приведён пример программы ограничения частоты запросов целиком:
#include <cf_ebpf_defs.h>
#define CF_EBPF_HELPER_V0
#include <cf_ebpf_helper.h>
// Rate limit configuration
// This program implements a fixed (not sliding) window ratelimit.
#define RATE_LIMIT 100 // Maximum packets allowed per window
#define WINDOW_SECONDS 60 // Time window in seconds
// The source IP table holds a mapping from source IP -> custom u64. We will make the custom u64 value in the
// table hold a timestamp and a counter to accomplish a ratelimit.
//
// NOTE: the source IP table is effectively a LRU cache. If it is full, old values will be evicted.
// Values are also garbage collected from the table every 1hr.
//
// The macros below pack the timestamp (upper 32 bits) and counter (lower 32 bits) into 64-bit data
// into a value that we can store into the source IP table.
#define PACK_STATE(ts, count) (((uint64_t)(ts) << 32) | ((uint64_t)(count) & 0xFFFFFFFF))
#define UNPACK_TIMESTAMP(data) ((uint32_t)((data) >> 32))
#define UNPACK_COUNTER(data) ((uint32_t)((data) & 0xFFFFFFFF))
uint64_t cf_ebpf_main(void *state)
{
// Get current timestamp
int64_t now = timestamp();
if (now < 0) {
return CF_EBPF_PASS; // If timestamp fails, allow the packet
}
uint32_t now_secs = (uint32_t)now;
// Try to get existing state for this source IP
uint64_t data;
int ret = get_src_ip_data(&data);
uint32_t window_start;
uint32_t counter;
if (ret == -1) {
// No existing entry - first packet from this IP
// Initialize: window starts now, counter = 1
window_start = now_secs;
counter = 1;
} else if (ret != 0) {
// If there's other unknown error with getting src_ip_data, pass packet
return CF_EBPF_PASS;
} else {
// Entry exists - unpack the state
window_start = UNPACK_TIMESTAMP(data);
counter = UNPACK_COUNTER(data);
// Check if we're still in the same time window
if (now_secs - window_start >= WINDOW_SECONDS) {
// Window expired - reset counter and start new window
window_start = now_secs;
counter = 1;
} else {
// Still in same window - increment counter
counter++;
// Check if rate limit exceeded
if (counter > RATE_LIMIT) {
// Drop packet without updating state
// Here is where the actual ratelimit occurs.
return CF_EBPF_DROP;
}
}
}
// Store updated state
uint64_t new_data = PACK_STATE(window_start, counter);
set_src_ip_data(new_data);
return CF_EBPF_PASS;
}Эта программа демонстрирует несколько ключевых концепций:
- Упаковка бит: Хранение нескольких значений (метки времени и счётчика) в одном
u64с помощью битового сдвига. - Ограничение частоты запросов с фиксированным окном: Подсчёт пакетов в пределах дискретных временных окон со сбросом по истечении окна.
- Корректная обработка ошибок: Пропуск пакетов при сбое вспомогательных функций, чтобы избежать ложных срабатываний в пограничных случаях.
- Поведение таблицы состояний: Таблица состояний исходных IP-адресов представляет собой LRU-кеш. При достижении предельного объёма старые записи вытесняются. Записи также удаляются сборщиком мусора после часа неактивности.
Состояние
У каждой программы есть доступ к собственному локальному состоянию. Состояние привязано к конкретному серверу и не передаётся между дата-центрами.
Состояние привязано к конкретной программе. Если вы измените режим правила (disabled, monitoring или enabled), содержимое таблиц состояний сохранится. Но если вы измените программу правила или содержимое самой программы, таблицы состояний будут очищены.
Для вашей программы доступны две таблицы состояний.
Таблица состояний по IP-адресу источника
Таблица состояний исходных IP-адресов хранит состояние с ключом в виде исходного IP-адреса. Каждая запись содержит:
| Поле | Тип | Описание |
|---|---|---|
| Статус | Enum | Статус исходного IP-адреса: None (0), Challenged (1), Verified (2) или Blocklisted (3). |
| Данные пользователя | u64 |
Значение, которое пользователь может задать для любых целей. |
Максимальная ёмкость по умолчанию составляет 1,000 записей.
Используйте следующие вспомогательные функции для работы с этой таблицей:
get_src_ip_status: получить статус исходного IP-адреса текущего пакета.set_src_ip_status: задать статус исходного IP-адреса текущего пакета.get_src_ip_data: получить пользовательские данные исходного IP-адреса текущего пакета.set_src_ip_data: сохранить пользовательские данные исходного IP-адреса текущего пакета.
Запись в таблице состояний исходных IP-адресов создаётся при следующих условиях:
- программа вызывает
set_src_ip_statusчтобы пометить исходный IP-адрес статусом Challenged, Verified или Blocklisted. - программа вызывает
set_src_ip_dataчтобы сохранить произвольные данные u64 для исходного IP-адреса. - программа вызывает
set_challengeдля нового исходного IP-адреса, у которого ещё нет записи в таблице.
Таблица состояний потоков
Таблица состояний потоков хранит состояние по ключу из 4 значений: исходный IP-адрес, исходный порт, целевой IP-адрес и целевой порт. Каждая запись содержит u64 значение, которое можно задать для любых целей.
Максимальная ёмкость по умолчанию составляет 10,000 записей.
Используйте следующие вспомогательные функции для работы с этой таблицей:
get_flow_data: получить пользовательские данные текущего потока.set_flow_data: сохранить пользовательские данные текущего потока.
Запись в таблице состояний потоков создаётся при следующих условиях:
- программа вызывает
set_flow_dataчтобы сохранить произвольные данные u64 для потока.
Поведение кеша
Обе таблицы состояний представляют собой LRU-кеши (вытесняются давно не использовавшиеся записи). Когда таблица достигает максимальной ёмкости, самая старая запись вытесняется, чтобы освободить место для новых. Записи также удаляются сборщиком мусора, если к ним не обращались в течение часа.
Вспомогательные функции и структуры BPF
Вспомогательная функция: функция среды выполнения Cloudflare, которую вызывает клиентская программа.
Вспомогательные функции крайне важны, поскольку архитектура набора команд BPF (ISA) поддерживает лишь ограниченный набор системных вызовов. Из соображений безопасности Cloudflare компилирует объектный файл BPF только с заранее определённым списком известных библиотек, которые разработчик программы изменить не может.
Определения вспомогательных функций и исходный код обёртки верификатора доступны на GitHub ↗.
Вспомогательные функции
parse_packet_data
Создаёт cf_ebpf_parsed_headers от cf_ebpf_generic_ctx и cf_ebpf_packet_data. Выполняет необходимые проверки памяти для прохождения верификатора.
static inline int parse_packet_data(
struct cf_ebpf_generic_ctx *ctx,
struct cf_ebpf_packet_data **out_p,
struct cf_ebpf_parsed_headers *out_headers
);Аргументы:
ctx: указатель на общий контекст, передаваемый в BPF-программу.out_p: указатель для получения структуры данных пакета.out_headers: указатель для получения структуры разобранных заголовков.
Возвращает: 0 в случае успеха, 1 в случае ошибки (например, слишком короткий пакет или недопустимая длина). В случае успеха, out_headers содержит корректные указатели заголовков IP и UDP.
rand
Генерирует случайное беззнаковое целое число.
uint64_t rand(void);Возвращает: Случайный uint64_t значение.
timestamp
Возвращает текущую метку времени UNIX (количество секунд, без учета високосных, с 1 января 1970 года 0:00:00 UTC).
int64_t timestamp(void);Возвращает: Текущая временная метка в виде int64_t.
hash_md5
Вычисляет хеш MD5 исходного буфера и сохраняет результат в буфере назначения.
int hash_md5(uint8_t *src, size_t src_len, uint8_t *dest, size_t dest_len);Аргументы:
src: указатель на исходный буфер.src_len: длина исходного буфера в байтах.dest: указатель на буфер назначения (не менее 16 байт).dest_len: длина буфера назначения в байтах.
Возвращает:
- Положительное значение (количество записанных байт) при успешном выполнении.
-1если исходный буфер недействителен.-2если конечный буфер равен null или слишком мал.
hash_sha256
Вычисляет хеш SHA-256 исходного буфера и сохраняет результат в буфере назначения.
int hash_sha256(uint8_t *src, size_t src_len, uint8_t *dest, size_t dest_len);Аргументы:
src: указатель на исходный буфер.src_len: длина исходного буфера в байтах.dest: указатель на буфер назначения (не менее 32 байт).dest_len: длина буфера назначения в байтах.
Возвращает:
- Положительное значение (количество записанных байт) при успешном выполнении.
-1если исходный буфер недействителен.-2если конечный буфер равен null или слишком мал.
hash_sha512
Вычисляет хеш SHA-512 исходного буфера и сохраняет результат в буфере назначения.
int hash_sha512(uint8_t *src, size_t src_len, uint8_t *dest, size_t dest_len);Аргументы:
src: указатель на исходный буфер.src_len: длина исходного буфера в байтах.dest: указатель на буфер назначения (не менее 64 байт).dest_len: длина буфера назначения в байтах.
Возвращает:
- Положительное значение (количество записанных байт) при успешном выполнении.
-1если исходный буфер недействителен.-2если конечный буфер равен null или слишком мал.
hash_crc32
Вычисляет хеш CRC32 исходного буфера и сохраняет результат в виде 64-битного целого числа. Это удобная обёртка, которая самостоятельно выполняет преобразование байтов в целое число.
int hash_crc32(uint8_t *src, size_t src_len, uint64_t *dest);Аргументы:
src: указатель на исходный буфер.src_len: длина исходного буфера в байтах.dest: указатель наuint64_tдля получения результата CRC32.
Возвращает:
- Положительное значение (количество внутренне записанных байт, всегда 8) при успешном выполнении.
-1если исходный буфер недействителен.-2если конечный буфер равен null.
hash_blake2b512
Вычисляет хеш BLAKE2B-512 исходного буфера и сохраняет результат в буфере назначения.
int hash_blake2b512(const uint8_t *src, size_t src_len, uint8_t *dest, size_t dest_len);Аргументы:
src: указатель на исходный буфер.src_len: длина исходного буфера в байтах.dest: указатель на буфер назначения (не менее 64 байт).dest_len: длина буфера назначения в байтах.
Возвращает:
- Положительное значение (количество записанных байт) при успешном выполнении.
-1если исходный буфер недействителен.-2если конечный буфер равен null или слишком мал.
hmac_sha256
Вычисляет HMAC-SHA256 исходного буфера и сохраняет результат в буфере назначения. Приватный ключ настраивается на уровне платформы и не передаётся напрямую программе BPF. Ключ уникален для каждого сервера и каждого клиента.
int hmac_sha256(uint8_t *src, size_t src_len, uint8_t *dest, size_t dest_len);Аргументы:
src: указатель на исходный буфер.src_len: длина исходного буфера в байтах.dest: указатель на буфер назначения (не менее 32 байт).dest_len: длина буфера назначения в байтах.
Возвращает:
- Положительное значение (количество записанных байт) при успешном выполнении.
-1если исходный буфер недействителен.-2если конечный буфер равен null или слишком мал.
hmac_sha512
Вычисляет HMAC-SHA512 исходного буфера и сохраняет результат в буфере назначения. Приватный ключ настраивается на уровне платформы и не передаётся напрямую программе BPF. Ключ уникален для каждого сервера и каждого клиента.
int hmac_sha512(uint8_t *src, size_t src_len, uint8_t *dest, size_t dest_len);Аргументы:
src: указатель на исходный буфер.src_len: длина исходного буфера в байтах.dest: указатель на буфер назначения (не менее 64 байт).dest_len: длина буфера назначения в байтах.
Возвращает:
- Положительное значение (количество записанных байт) при успешном выполнении.
-1если исходный буфер недействителен.-2если конечный буфер равен null или слишком мал.
hmac_blake2b512
Вычисляет BLAKE2B-512 HMAC исходного буфера и сохраняет результат в буфере назначения. Приватный ключ настраивается на уровне платформы и не передаётся напрямую программе BPF. Ключ уникален для каждого сервера и каждого клиента.
int hmac_blake2b512(const uint8_t *src, size_t src_len, uint8_t *dest, size_t dest_len);Аргументы:
src: указатель на исходный буфер.src_len: длина исходного буфера в байтах.dest: указатель на буфер назначения (не менее 64 байт).dest_len: длина буфера назначения в байтах.
Возвращает:
- Положительное значение (количество записанных байт) при успешном выполнении.
-1если исходный буфер недействителен.-2если конечный буфер равен null или слишком мал.
set_challenge
Задает данные проверки (challenge) для текущего пакета. Используется для отправки пакета проверки обратно клиенту.
int set_challenge(uint8_t *src, size_t src_len);Аргументы:
src: указатель на буфер данных challenge.src_len: длина данных challenge в байтах. Если0, буфер проверки сбрасывается.
Возвращает:
0в случае успеха.-4если исходный буфер недействителен или превышает максимально допустимый размер.-5если проверки не включены.-6если проверка недавно уже отправлялась на этот исходный IP-адрес или превышен глобальный лимит скорости.
get_src_ip_status
Извлекает значение статуса, связанное с исходным IP-адресом, из таблицы состояния.
int get_src_ip_status(uint8_t *status, uint64_t *expiry);Аргументы:
status: указатель для получения значения статуса (CF_EBPF_SRC_IP_STATUS_CHALLENGED,CF_EBPF_SRC_IP_STATUS_VERIFIED, илиCF_EBPF_SRC_IP_STATUS_BLOCKLISTED). Может быть null, если требуется только истечение срока действия.expiry: указатель для получения временной метки истечения срока действия. Может быть null, если нужен только статус.
Возвращает:
0в случае успеха.-1если для исходного IP-адреса нет записи.-2если для текущего пакета не задан контекст исходного IP-адреса.-3если предоставленный буфер слишком мал.-4если обаstatusиexpiryравны null.-5если таблица состояний исходных IP-адресов не включена.
set_src_ip_status
Задает значение статуса, связанное с IP-адресом источника, в таблице состояний.
int set_src_ip_status(uint8_t status, uint64_t expiry_secs);Аргументы:
status: значение статуса для установки (CF_EBPF_SRC_IP_STATUS_CHALLENGED,CF_EBPF_SRC_IP_STATUS_VERIFIED, илиCF_EBPF_SRC_IP_STATUS_BLOCKLISTED).expiry_secs: количество секунд до истечения срока действия статуса. Если0, срок действия статуса не истекает.
Возвращает:
0в случае успеха.-2если для текущего пакета не задан контекст исходного IP-адреса.-5если таблица состояний исходных IP-адресов не включена.
get_src_ip_data
Извлекает пользовательские данные, связанные с исходным IP-адресом, из таблицы состояния.
int get_src_ip_data(uint64_t *data);Аргументы:
data: указатель для получения сохранённого значения данных.
Возвращает:
0в случае успеха.-1если для исходного IP-адреса нет записи.-2если для текущего пакета не задан контекст исходного IP-адреса.-3если предоставленный буфер слишком мал.-4еслиdataравен null.-5если таблица состояний исходных IP-адресов не включена.
set_src_ip_data
Сохраняет в таблице состояний пользовательские данные, связанные с исходным IP-адресом.
int set_src_ip_data(uint64_t data);Аргументы:
data: значение данных для сохранения.
Возвращает:
0в случае успеха.-2если для текущего пакета не задан контекст исходного IP-адреса.-5если таблица состояний исходных IP-адресов не включена.
get_flow_data
Извлекает пользовательские данные, связанные с текущим потоком, из таблицы состояния.
int get_flow_data(uint64_t *data);Аргументы:
data: указатель для получения сохранённого значения данных.
Возвращает:
0в случае успеха.-1если для потока нет записи.-2если для текущего пакета не задан контекст потока.-3если предоставленный буфер слишком мал.-4еслиdataравен null или не выровнен.-5если таблица состояний потоков не включена.
set_flow_data
Сохраняет в таблице состояний пользовательские данные, связанные с текущим потоком.
int set_flow_data(uint64_t data);Аргументы:
data: значение данных для сохранения.
Возвращает:
0в случае успеха.-2если для текущего пакета не задан контекст потока.-5если таблица состояний потоков не включена.
entropy
Вычисляет энтропию Шеннона исходного буфера. Результат возвращается в миллибитах и находится в диапазоне от 0 (все байты одинаковые) до 8000 (все 256 значений байта распределены равномерно).
int64_t entropy(uint8_t *src, size_t src_len);Аргументы:
src: указатель на исходный буфер.src_len: длина исходного буфера в байтах.
Возвращает:
- Значение энтропии в миллибитах (0-8000) при успешном выполнении.
-1если исходный буфер недействителен.
set_network_analytics_tag
Задает пользовательский тег для отчетов сетевой аналитики. Тег отображается вместе с образцом пакета на панели Network Analytics. По умолчанию пакеты выбираются с частотой 1/10,000.
За один запуск программы задается только один тег. Если при выполнении программы вызывается set_network_analytics_tag несколько раз, к выборке пакета применяется значение последнего тега.
int set_network_analytics_tag(uint64_t tag);Аргументы:
tag: значение тега для установки. По умолчанию0если не задано.
Возвращает: 0 в случае успеха.
ntohs
Преобразует 16-битное целое число из сетевого порядка байтов в порядок байтов узла.
uint16_t ntohs(uint16_t netshort);Аргументы:
netshort: 16-битное значение в сетевом порядке байт.
Возвращает: Значение в порядке байтов узла.
htons
Преобразует 16-битное целое число из порядка байтов узла в сетевой порядок байтов.
uint16_t htons(uint16_t hostshort);Аргументы:
hostshort: 16-битное значение в порядке байт хоста.
Возвращает: Значение в сетевом порядке байтов.
ntohl
Преобразует 32-битное целое число из сетевого порядка байтов в порядок байтов узла.
uint32_t ntohl(uint32_t netlong);Аргументы:
netlong: 32-битное значение в сетевом порядке байт.
Возвращает: Значение в порядке байтов узла.
htonl
Преобразует 32-битное целое число из порядка байтов узла в сетевой порядок байтов.
uint32_t htonl(uint32_t hostlong);Аргументы:
hostlong: 32-битное значение в порядке байт хоста.
Возвращает: Значение в сетевом порядке байтов.
ntohll
Преобразует 64-битное целое число из сетевого порядка байтов в порядок байтов узла.
uint64_t ntohll(uint64_t netlonglong);Аргументы:
netlonglong: 64-битное значение в сетевом порядке байт.
Возвращает: Значение в порядке байтов узла.
htonll
Преобразует 64-битное целое число из порядка байтов узла в сетевой порядок байтов.
uint64_t htonll(uint64_t hostlonglong);Аргументы:
hostlonglong: 64-битное значение в порядке байт хоста.
Возвращает: Значение в сетевом порядке байтов.
Структуры
cf_ebpf_generic_ctx
Общая структура контекста, передаваемая в программу BPF.
struct cf_ebpf_generic_ctx {
/* Pointer to the beginning of the context data. */
uint64_t data;
/* Pointer to the end of the context data. */
uint64_t data_end;
/* Space for the program to store metadata. */
uint64_t meta_data;
};cf_ebpf_packet_data
Содержит необработанные данные пакета, переданные в программу BPF.
struct cf_ebpf_packet_data {
/* Total length of the packet. */
size_t total_packet_length;
/* Size of the IP header. Supports IPv4 (including options) and IPv6. */
size_t ip_header_length;
/* Bytes of the packet, starting with the IP header. */
uint8_t packet_buffer[1500];
};cf_ebpf_parsed_headers
Содержит указатели на разобранные заголовки IP и UDP. Заполняется при вызове parse_packet_data.
struct cf_ebpf_parsed_headers {
/* Pointer to the parsed IPv4 header, if present (otherwise null). */
struct iphdr *ipv4;
/* Pointer to the parsed IPv6 header, if present (otherwise null). */
struct ipv6hdr *ipv6;
/* Pointer to the parsed UDP header. */
struct udphdr *udp;
/* Raw pointer to the last valid byte of the packet context data. */
uint8_t *data_end;
};iphdr
Структура заголовка IPv4. Источник: Ядро Linux ↗.
struct iphdr {
#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
uint8_t version:4,
ihl:4;
#else
uint8_t ihl:4,
version:4;
#endif
uint8_t tos;
uint16_t tot_len;
uint16_t id;
uint16_t frag_off;
uint8_t ttl;
uint8_t protocol;
uint16_t check;
uint32_t saddr;
uint32_t daddr;
};ipv6hdr
Структура заголовка IPv6. Источник: Ядро Linux ↗.
struct ipv6hdr {
#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
uint8_t version:4,
priority:4;
#else
uint8_t priority:4,
version:4;
#endif
uint8_t flow_lbl[3];
uint16_t payload_len;
uint8_t nexthdr;
uint8_t hop_limit;
uint8_t saddr[16];
uint8_t daddr[16];
};udphdr
Структура заголовка UDP. Источник: Ядро Linux ↗.
struct udphdr {
uint16_t source;
uint16_t dest;
uint16_t len;
uint16_t check;
};Конечные точки программы
Загрузить программу
Чтобы загрузить программу, перейдите в раздел Networking > L3/4 DDoS protection > Advanced Protection в панели управления Cloudflare. Затем откройте вкладку Programmable Flow Protection.
В разделе Программы, нажмите кнопку "Upload new program." После этого будет предложено выбрать файл для загрузки с вашим C исходный код.
Cloudflare API получит исходный код в C файл, скомпилировать его в байткод BPF и запустить проверку верификатором.
Если компиляция или проверка завершается ошибкой, API возвращает подробное сообщение об ошибке.
Если компиляция и проверка проходят успешно, Cloudflare сохраняет исходный код и объектный файл в аккаунте и возвращает ID программы.
Обновить программу
В процессе разработки может быть удобнее обновлять одну и ту же программу (с одинаковым ID программы), а не создавать каждый раз новую программу как отдельный ресурс.
Чтобы обновить программу, нажмите на три точки рядом с ней. Затем выберите Перезаписать. После этого будет предложено выбрать файл для загрузки в качестве C исходный код.
Просмотреть все программы
Чтобы просмотреть все загруженные программы и статус их выполнения, откройте таблицу в разделе Программы.
Значок ссылки рядом с именем программы означает, что программа используется в активном правиле и не может быть удалена.
Удаление программы
Чтобы удалить программу, нажмите на три точки рядом с программой, которую нужно удалить. Затем выберите Удалить.
Обратите внимание: программу, на которую ссылается активное правило, удалить нельзя.
Обратите внимание: программы со статусом «failed» (то есть не прошедшие компиляцию или проверку) автоматически и безвозвратно удаляются через 30 дней бездействия.
Правила
Для каждого пакета выполняется только одно правило. Если в вашем аккаунте настроено несколько правил, применяется правило с наиболее точным область действия выполняется. Например, правило, ограниченное конкретным дата-центром, имеет приоритет над правилом, ограниченным регионом, а оно, в свою очередь, имеет приоритет над глобальным правилом. Поэтому вы не можете создать более одного глобального правила.
Получить список всех правил
Чтобы просмотреть правила и связанные с ними идентификаторы, перейдите в Сеть > защита от L3/4 DDoS-атак > Расширенная защита в дашборде Cloudflare. Затем выберите Programmable Flow Protection.
Создание правила
Чтобы создать правило, перейдите в Сеть > защита от L3/4 DDoS-атак > Расширенная защита в дашборде Cloudflare. Затем выберите Programmable Flow Protection.
В разделе Правила, выберите Создать правило. Заполните соответствующие поля нового правила. Вам будет предложено выбрать программу, режим и область действия для этого правила.
Обновить правило
Чтобы обновить существующее правило, перейдите в раздел Rules. Нажмите на три точки рядом с правилом и выберите Изменить.
Вам будет предложено изменить режим и область действия правила. Изменить саму программу правила нельзя, поскольку это небезопасный способ развертывания.
Удаление правила
Чтобы удалить существующее правило, перейдите в раздел Rules. Нажмите на три точки рядом с правилом и выберите Удалить.
Отладочный захват пакетов (PCAP)
Эта конечная точка API отлаживает программу, принимая следующие данные:
- Локальный путь к входному файлу PCAP, передаваемому в качестве запрошенных данных в двоичном формате. Максимальный размер входного файла PCAP составляет 5 МБ, файлы большего размера будут отклонены.
- Идентификатор программы, указанный в пути запроса.
- Необязательный параметр запроса
ip_offset=<value>чтобы указать IP offset. Это количество байт, на которое смещён IP-заголовок в каждом пакете входного PCAP-файла. Если параметр запроса ip offset не указан, API попытается определить нужное значение смещения самостоятельно. Например, если PCAP-файл содержит Ethernet-пакеты, обнаруженное значение IP offset будет равно 14. Эта конечная точка исходит из того, что все пакеты в PCAP имеют одинаковое значение IP offset, иначе пакеты будут разобраны неверно.
Эта конечная точка запускает указанную программу BPF на входном файле PCAP и создает новый аннотированный файл PCAP. Выходной файл PCAP будет содержать те же пакеты, что и входной файл PCAP, а также вердикт программы, указанный в Комментарий к пакету каждого пакета.
curl "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/magic/programmable_flow_protection/configs/programs/$PROGRAM_ID/pcap" \
--header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
--header "Content-Type: application/vnd.tcpdump.pcap" \
--data-binary "@<PATH_TO_INPUT_PCAP_FILE>" \
--output output.pcapАннотация Packet Comment может содержать:
- Возвращаемое значение программы:
CF_EBPF_PASSилиCF_EBPF_DROP Ignored: если входящий пакет не является UDP-пакетомAnalytics tag: пользовательский тег Network Analytics, заданный программой для этого пакета, если он есть
Выходной файл PCAP может также содержать:
Challenge packet: challenge-пакет, отправленный программой обратно клиенту, если он есть
Рекомендации по безопасному развертыванию программ и правил
Вам нужно будет безопасно развертывать и тестировать программы, не затрагивая существующий production-трафик. Один из вариантов первоначального развертывания: задать для правила с глобальной областью действия disabled и задайте правило с областью действия на уровне colo или региона со значением monitoring с выражением фильтра, применяемым только к части IP-трафика.
Каждый регион или дата-центр (colo) Cloudflare применяет наиболее детализированное правило. Поэтому в описанном выше сценарии дата-центры или регионы, указанные в monitoring правило запустит программу разработчика в monitoring режиме, тогда как во всех остальных локациях Cloudflare программа вообще не будет выполняться. monitoring правило будет выполняться только для трафика, соответствующего выражению фильтра.
Затем, убедившись в корректном поведении с помощью Network Analytics, вы можете обновить и расширить monitoring область действия и выражение фильтра правила. В дальнейшем вы можете удалить disabled и monitoring и применить глобальный enabled правило.
С помощью Expression поле, чтобы ограничить программы подмножеством IP-адресов или префиксов, и Mode поле, чтобы определить, действительно ли программа отбрасывает пакеты, обеспечивает её безопасность и точную настройку при развёртывании.
Network Analytics
Трафик, проходящий через Programmable Flow Protection, можно найти в Network Analytics на дашборде.
В панели управления Network Analytics выберите Programmable Flow Protection, чтобы отфильтровать трафик на основе этой функции. Вы можете фильтровать трафик по ID программы, пользовательским тегам network analytics, действиям, IP-адресам и портам. По умолчанию пакеты выбираются с частотой 1/10,000.