← Cloudflare DDoS Protection / ddos-protection / advanced-ddos-systems / overview
Programmable Flow Protection (Beta)
Programmable Flow Protection je systém ochrany před DDoS útoky vedenými přes vlastní i standardizované protokoly vrstvy 7 postavené na UDP, například herní protokoly, protokoly finančních služeb, VoIP, telekomunikace a streaming. Z hlediska topologie podporuje asymetrické i symetrické konfigurace, kontroluje ale pouze příchozí provoz.
Programmable Flow Protection je momentálně v uzavřené beta verzi a je k dispozici jako doplněk k Magic Transit (BYOIP nebo IP adresy pronajaté od Cloudflare). Systém je k dispozici pouze pro tuto službu. Pokud jej chcete zapnout, obraťte se na svůj tým pro správu účtu nebo vyplňte tento formulář ↗.
Jak to funguje
Systém Programmable Flow Protection umožňuje napsat vlastní stavový program pracující na úrovni paketů v jazyce C a spustit jej v globální anycast síti Cloudflare jako program extended Berkeley Packet Filter (eBPF) běžící v uživatelském prostoru. program eBPF ↗ je systém pro filtrování paketů, který vývojáři umožňuje psát vlastní výkonnou síťovou logiku.
Programmable Flow Protection kontroluje a analyzuje protokoly vaší aplikace postavené na UDP (hloubková inspekce paketů) a podle vašeho programu rozhoduje, jak s pakety naloží. Logika vlastního programu vám umožní propouštět oprávněné uživatele a zároveň aktivně blokovat útoky.
Systém je postaven na flowtrackd platformě, stavové mitigační platformě Cloudflare. Systém Programmable Flow Protection staví na systému DDoS Advanced Protection a jeho obecná nastavení ke svému fungování. Respektuje prefixy které jste vybrali ke směrování přes systémy Advanced Protection, a také allowlist. Systém Advanced DDoS Protection by měl být povoleno pro fungování systému Programmable Flow Protection.
V průběhu bety Cloudflare uživatelům s psaním vlastního kódu pomáhá a poskytuje jim doporučení. Hotové ukázky kódu (šablony) pro běžné herní protokoly a protokoly VoIP mohou přibýt později.
Začínáme
Jakmile máte pro svůj účet zapnutou Programmable Flow Protection, přejděte na Sítě > L3/4 DDoS Protection > Advanced Protection v dashboardu Cloudflare. V části Programmable Flow Protection kartě:
-
Nahrajte svůj program eBPF napsaný v jazyce C.
Systém program ověří a uloží jej do vašeho účtu. Rozhraní API program zkompiluje a nad zkompilovaným programem poté spustí verifikátor, který vynutí kontroly paměti a ověří, že se program ukončí. Pokud program neprojde kompilací nebo ověřením, vrátí Cloudflare dashboard podrobnou chybovou zprávu.
-
Vytvořte pravidlo
-
Chování programu ověříte na dashboardu Network Analytics, kde vyberete Programmable Flow Protection kartě.
Můžete vytvořit další pravidla s odlišnou hodnotou parametru nastavení pravidla s vymezeným rozsahem do různých regionů a lokalit Cloudflare, čímž změníte režim (Mitigation nebo Monitoring) tak, aby odpovídal vašim vzorcům provozu a obchodním potřebám.
Systém Programmable Flow Protection podporuje Data Localization suite.
Napsání základního programu
Následující kroky vytvoří ukázkový program, který zahazuje veškerý provoz protokolu User Datagram Protocol (UDP) s hlavičkou IPv6. Zahazuje také provoz směřovaný na port 66 a provoz, který v datové části UDP nemá určitou vlastní hodnotu aplikační hlavičky.
-
Přidejte direktivu define, která určí použité verzované pomocné funkce.
S tím, jak bude Cloudflare do rozhraní Programmable Flow Protection API přidávat další funkce, budeme vydávat jeho nové verze. Zpětná kompatibilita verzí je zaručena.
#define CF_EBPF_HELPER_V0 -
Vložte hlavičkové soubory Cloudflare eBPF.
Tyto soubory mají pomocné funkce k analýze vstupních dat paketu předaných BPF programu.
#include <cf_ebpf_defs.h> #include <cf_ebpf_helper.h> -
Definujte vstupní funkci pro zpracování paketů.
Aby program prošel ověřením u Cloudflare, musí mít přesně tuto signaturu funkce.
Návratový typ
uint64_turčuje, zda Cloudflare paket propustí, nebo zahodí. Název funkcecf_ebpf_mainse používá jako vstupní bod programu. Argumentvoid *stateoznačuje data, která Cloudflare předává na vstup vašemu programu BPF.uint64_t cf_ebpf_main(void *state) -
Přetypuje vstupní argument na použitelné struktury.
Převeďte vstupní data na
cf_ebpf_generic_ctx, který Cloudflare sděluje hranice dat v paměti, ze které čteme.Dále deklarujte proměnné pro parsování dat.
cf_ebpf_parsed_headersbude obsahovat hlavičky IPv4, IPv6 a UDP.cf_ebpf_packet_databude obsahovat kopii původního IP paketu přijatého sítí Cloudflare (maximálně 1,500 bajtů) a dále délku paketu a délku IP hlavičky.struct cf_ebpf_generic_ctx *ctx = state; struct cf_ebpf_parsed_headers headers; struct cf_ebpf_packet_data *p; -
Proměnné naplňte voláním pomocné funkce.
Proměnné musíte doplnit voláním pomocné funkce
parse_packet_data, který Cloudflare poskytuje v hlavičkovém souboru přiloženém v kroku 2.parse_packet_dataprovádí kontroly paměti nutné k tomu, aby program prošel verifikátorem. Funkceparse_packet_datavrací0při úspěchu. Pokud volání uspěje, jsou vstupní parametry správně vyplněné.parse_packet_datavrací1při selhání. Pokudparse_packet_dataselže, program musí vrátitCF_EBPF_DROPk zahození paketu, aby program prošel verifierem.if (parse_packet_data(ctx, &p, &headers) != 0) { return CF_EBPF_DROP; }Hodnoty dostupné po úspěšném zpracování:
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; };Úplnou definici pomocných funkcí a struktur uvádí Pomocné funkce a struktury BPF.
-
Napište vlastní logiku.
V předchozích krocích jsme sestavili kód, který zůstává stejný pro každý program, který napíšete, bez ohledu na jeho logiku.
Nyní můžete napsat vlastní logiku.
V ukázce níže program zahodí každý paket, který obsahuje hlavičku IPv6 nebo má cílový UDP port 66.
Následně zkontroluje hodnotu aplikační hlavičky v UDP payloadu a ověří, že jejím posledním bajtem je pevná hodnota
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; } -
Pakety, které logika programu nezahodila, propustíte tím, že vrátíte
CF_EBPF_PASS.Aktuálně jsou podporovány tyto návratové hodnoty:
CF_EBPF_PASS = return value 0CF_EBPF_DROP = return value 1
Verifikátor, který se spustí při nahrání programu do rozhraní API, vynutí, aby program vracel pouze známé typy hodnot.
return CF_EBPF_PASS;
Pro přehlednost uvádíme níže celý základní program:
#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;
}Napsání složitějšího programu: odpověď založená na výzvě
Ukázkový program níže implementuje mechanismus výzvy a odpovědi nad UDP a pomocí pomocných funkcí udržuje stav mezi pakety ze stejné zdrojové IP adresy. Hodí se ke zmírňování útoků DDoS: klient musí nejprve prokázat, že výzvu přijal a dokáže na ni odpovědět, teprve pak systém jeho provoz propustí.
Mechanismus výzvy funguje takto:
Když dorazí paket z neznámé zdrojové IP adresy, program vygeneruje výzvový paket s náhodným nonce a zdrojovou IP adresu označí ve stavové tabulce jako "challenged". Původní paket se zahodí.
Pokud paket dorazí ze zdrojové IP adresy, které už byla výzva odeslána, program ověří, zda obsahuje správnou odpověď na výzvu (hodnota nonce zkombinovaná operací XOR s tajnou hodnotou). Při správné odpovědi se zdrojová IP adresa označí jako „ověřená“, při nesprávné se okamžitě přidá na blocklist.
Pakety z ověřených zdrojových IP adres projdou bez dalších kontrol.
-
Vložte hlavičkové soubory Cloudflare eBPF a definujte verzi helperu.
#define CF_EBPF_HELPER_V0 #include <cf_ebpf_defs.h> #include <cf_ebpf_helper.h> -
Definujte konstanty protokolu výzva-odpověď.
Odpověď na výzvu vznikne operací XOR mezi hodnotou nonce a tajnou hodnotou. Doba platnosti určuje, jak dlouho zůstává stav challenged nebo verified v platnosti.
#define CHALLENGE_SECRET 0xDEADBEEFCAFEBABEULL #define CHALLENGE_EXPIRY_SECS 60 #define VERIFIED_EXPIRY_SECS 3600 -
Definujte strukturu paketů s výzvou.
Paket s výzvou obsahuje nonce, na který musí klient odpovědět, a místo pro jeho odpověď.
struct challenge_packet { uint64_t nonce; // Random nonce for this challenge uint64_t response; // Expected: nonce XOR CHALLENGE_SECRET }; -
Definujte vstupní funkci a zpracujte paket.
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; -
Stav zdrojové IP adresy ověříte pomocí
get_src_ip_status.Stav udává, zda je tato zdrojová IP adresa nová, vyzvaná, ověřená, nebo na seznamu blokovaných. Časové razítko vypršení udává, kdy platnost stavu skončí.
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; } -
Zpracujte ověřené zdrojové IP adresy.
Platforma Programmable Flow Protection zahodí pakety z IP adres na blokovacím seznamu ještě předtím, než se program spustí. Tento případ proto ve svém kódu nemusíte nijak ošetřovat.
Pokud je zdrojová IP adresa ověřená, tedy prošla dřívější výzvou, paket propusťte.
if (ret == 0 && status == CF_EBPF_SRC_IP_STATUS_VERIFIED) { return CF_EBPF_PASS; } -
Zjistí, zda jde o odpověď na výzvu ze zdrojové IP adresy, které byla výzva odeslána.
Pokud už zdrojová IP adresa výzvu dostala, zkontrolujte, zda aktuální paket obsahuje platnou odpověď. Při správné odpovědi označte zdrojovou IP adresu jako ověřenou, při nesprávné ji okamžitě přidejte na blocklist.
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; } -
Vydá novou výzvu pro nové zdrojové IP adresy.
Vygenerujte náhodný nonce, uložte jej do stavové tabulky, vytvořte challenge paket a odešlete jej pomocí
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; }
Pro přehlednost uvádíme níže celý složitý program:
#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;
}Tento program ukazuje několik klíčových principů:
- Správa stavu: Použití
get_src_ip_status,set_src_ip_status,get_src_ip_data, aset_src_ip_datake sledování stavu výzvy pro každou zdrojovou IP adresu. - Odeslání výzvy: Použití
set_challengek odeslání paketu s výzvou zpět klientovi. - Kryptografické ověření: Použití sdíleného tajemství k ověření, že klient na výzvu odpověděl správně.
- Zpracování expirace: Použití časových razítek k vypršení zastaralých stavových záznamů.
Napsání složitějšího programu: omezování rychlosti
Ukázkový program níže implementuje omezovač rychlosti pro jednotlivé zdrojové IP adresy pomocí algoritmu pevného okna. Hodí se ke zmírňování objemových útoků DDoS, protože omezuje, kolik paketů může jedna zdrojová IP adresa odeslat v daném časovém okně.
Mechanismus omezování rychlosti funguje takto:
Po příchodu paketu načte program uložený stav pro danou zdrojovou IP adresu. Stav obsahuje časové razítko začátku okna a čítač paketů, obojí sbalené do jediné 64bitové hodnoty. Pokud aktuální čas stále spadá do okna, čítač se zvýší. Jakmile čítač překročí nastavený limit, paket se zahodí. Po vypršení okna se čítač vynuluje.
-
Vložte hlavičkové soubory Cloudflare eBPF a definujte verzi helperu.
#include <cf_ebpf_defs.h> #define CF_EBPF_HELPER_V0 #include <cf_ebpf_helper.h> -
Definujte konstanty pro konfiguraci rate limitu.
RATE_LIMITnastavuje maximální počet paketů povolených za okno.WINDOW_SECONDSurčuje délku každého časového okna v sekundách.#define RATE_LIMIT 100 // Maximum packets allowed per window #define WINDOW_SECONDS 60 // Time window in seconds -
Definujte makra pro zabalení a rozbalení stavových dat.
Stavová tabulka zdrojových IP adres ukládá jeden
u64hodnotu na jednu zdrojovou IP adresu. Chcete-li sledovat zároveň časové razítko i čítač, uložte je do jedné hodnoty: časové razítko do horních 32 bitů a čítač do dolních 32 bitů.#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)) -
Definujte vstupní funkci a získejte aktuální časové razítko.
Pokud pomocná funkce pro časový údaj selže, paket propusťte, abyste předešli falešně pozitivním detekcím.
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; -
Načte existující stav pro tuto zdrojovou IP adresu.
Použijte
get_src_ip_datak ověření, zda už byla tato zdrojová IP adresa dříve zaznamenána.// 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; -
Ošetřete případ, kdy jde o novou zdrojovou IP adresu.
Pokud žádný záznam neexistuje (návratová hodnota je
-1), jde o první paket z této zdrojové IP. Inicializujte okno tak, aby začínalo nyní, s čítačem 1.if (ret == -1) { // No existing entry - first packet from this IP // Initialize: window starts now, counter = 1 window_start = now_secs; counter = 1; } -
Zpracujte již známé zdrojové IP adresy a zkontrolujte časové okno.
Pokud záznam existuje, rozbalte uložený časový údaj a čítač. Pokud okno vypršelo, obě hodnoty vynulujte. V opačném případě čítač zvyšte a zkontrolujte, zda nepřekročil limit.
} 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; } } } -
Uložte aktualizovaný stav a paket propusťte.
Časovou značku začátku okna a čítač zabalte zpět do jedné hodnoty a tu uložte do stavové tabulky zdrojových IP adres.
// Store updated state uint64_t new_data = PACK_STATE(window_start, counter); set_src_ip_data(new_data); return CF_EBPF_PASS; }
Pro přehlednost uvádíme níže celý program pro omezení rychlosti:
#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;
}Tento program ukazuje několik klíčových principů:
- Zabalení bitů: Uložení více hodnot (časové razítko a čítač) do jediného
u64pomocí bitového posunu. - Omezení rychlosti s pevným oknem: Sledování počtu paketů v samostatných časových oknech a vynulování po vypršení okna.
- Korektní ošetření chyb: Propouštění paketů při selhání pomocných funkcí, aby v hraničních situacích nevznikaly falešně pozitivní nálezy.
- Chování stavové tabulky: Stavová tabulka zdrojových IP adres je cache typu LRU. Po naplnění kapacity se staré záznamy odstraňují. Záznamy se navíc uvolňují po hodině nečinnosti.
Stav
Každý program má přístup ke svému vlastnímu lokálnímu stavu. Stav je lokální pro každý server a mezi datovými centry se nesdílí.
Stav je vázaný na konkrétní program. Když změníte režim pravidla (disabled, monitoring nebo enabled), obsah stavových tabulek zůstane zachovaný. Pokud ale změníte program pravidla nebo obsah samotného programu, stavové tabulky se vymažou.
Vašemu programu jsou k dispozici dvě stavové tabulky.
Stavová tabulka zdrojových IP adres
Stavová tabulka zdrojových IP adres ukládá stav podle klíče, kterým je zdrojová IP adresa. Každý záznam obsahuje:
| Pole | Typ | Popis |
|---|---|---|
| Stav | Enum | Stav zdrojové IP adresy: None (0), Challenged (1), Verified (2) nebo Blocklisted (3). |
| Uživatelská data | u64 |
Uživatelsky definovaná hodnota, kterou můžete nastavit k libovolnému účelu. |
Výchozí maximální kapacita je 1 000 záznamů.
K práci s touto tabulkou použijte následující pomocné funkce:
get_src_ip_statusvrací stav zdrojové IP adresy aktuálního paketu.set_src_ip_statusnastavuje stav zdrojové IP adresy aktuálního paketu.get_src_ip_datavrací uživatelská data pro zdrojovou IP adresu aktuálního paketu.set_src_ip_dataukládá uživatelská data pro zdrojovou IP adresu aktuálního paketu.
Záznam v tabulce stavů zdrojových IP adres vzniká za těchto podmínek:
- program volá
set_src_ip_statusk označení zdrojové IP adresy jako Challenged, Verified nebo Blocklisted. - program volá
set_src_ip_datak uložení vlastních u64 dat pro zdrojovou IP adresu. - program volá
set_challengepro novou zdrojovou IP, která v tabulce dosud nemá záznam.
Tabulka stavů toků
Stavová tabulka toků ukládá stav pod klíčem tvořeným čtveřicí: zdrojová IP, zdrojový port, cílová IP a cílový port. Každý záznam obsahuje u64 hodnota, kterou můžete nastavit k libovolnému účelu.
Výchozí maximální kapacita je 10 000 záznamů.
K práci s touto tabulkou použijte následující pomocné funkce:
get_flow_datavrací uživatelská data pro aktuální tok.set_flow_dataukládá uživatelská data pro aktuální tok.
Záznam v tabulce stavů toků vzniká za těchto podmínek:
- program volá
set_flow_datak uložení vlastních u64 dat pro daný tok.
Chování cache
Obě stavové tabulky jsou cache typu LRU (least recently used). Jakmile tabulka dosáhne maximální kapacity, nejstarší záznam se zahodí, aby uvolnil místo novým. Záznamy se navíc uvolňují, pokud k nim hodinu nikdo nepřistoupil.
Pomocné funkce a struktury BPF
Pomocná funkce je funkce, kterou poskytuje běhové prostředí Cloudflare a kterou volá zákaznický program.
Pomocné funkce jsou zásadní, protože architektura instrukční sady BPF (ISA) podporuje jen některá systémová volání. Z bezpečnostních důvodů Cloudflare zkompiluje objektový soubor BPF pouze s předem daným seznamem známých knihoven, který vývojář programu nemůže změnit.
Definice pomocných funkcí a zdrojové kódy obalu verifikátoru najdete na GitHub ↗.
Pomocné funkce
parse_packet_data
Vytvoří cf_ebpf_parsed_headers z cf_ebpf_generic_ctx a cf_ebpf_packet_data. Provádí povinné kontroly paměti, aby program prošel verifikátorem.
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
);Argumenty:
ctxje ukazatel na obecný kontext předaný BPF programu.out_pje ukazatel pro uložení struktury s daty paketu.out_headersje ukazatel pro uložení struktury s rozparsovanými hlavičkami.
Vrací: 0 při úspěchu, 1 při selhání (například příliš krátký paket nebo neplatná délka). Při úspěchu out_headers obsahuje platné ukazatele na hlavičky IP a UDP.
rand
Vygeneruje náhodné celé číslo bez znaménka.
uint64_t rand(void);Vrací: Náhodný uint64_t hodnota.
timestamp
Vrátí aktuální UNIX timestamp (počet nepřestupných sekund od 1. ledna 1970 0:00:00 UTC).
int64_t timestamp(void);Vrací: Aktuální časové razítko jako int64_t.
hash_md5
Vypočítá hash MD5 zdrojového bufferu a výsledek uloží do cílového bufferu.
int hash_md5(uint8_t *src, size_t src_len, uint8_t *dest, size_t dest_len);Argumenty:
srcje ukazatel na zdrojový buffer.src_lenje délka zdrojového bufferu v bajtech.destje ukazatel na cílový buffer (musí mít alespoň 16 bajtů).dest_lenje délka cílového bufferu v bajtech.
Vrací:
- Při úspěchu kladná hodnota (počet zapsaných bajtů).
-1v případě, že je zdrojový buffer neplatný.-2v případě, že je cílový buffer null nebo příliš malý.
hash_sha256
Vypočítá hash SHA-256 zdrojového bufferu a výsledek uloží do cílového bufferu.
int hash_sha256(uint8_t *src, size_t src_len, uint8_t *dest, size_t dest_len);Argumenty:
srcje ukazatel na zdrojový buffer.src_lenje délka zdrojového bufferu v bajtech.destje ukazatel na cílový buffer (musí mít alespoň 32 bajtů).dest_lenje délka cílového bufferu v bajtech.
Vrací:
- Při úspěchu kladná hodnota (počet zapsaných bajtů).
-1v případě, že je zdrojový buffer neplatný.-2v případě, že je cílový buffer null nebo příliš malý.
hash_sha512
Vypočítá hash SHA-512 zdrojového bufferu a výsledek uloží do cílového bufferu.
int hash_sha512(uint8_t *src, size_t src_len, uint8_t *dest, size_t dest_len);Argumenty:
srcje ukazatel na zdrojový buffer.src_lenje délka zdrojového bufferu v bajtech.destje ukazatel na cílový buffer (musí mít alespoň 64 bajtů).dest_lenje délka cílového bufferu v bajtech.
Vrací:
- Při úspěchu kladná hodnota (počet zapsaných bajtů).
-1v případě, že je zdrojový buffer neplatný.-2v případě, že je cílový buffer null nebo příliš malý.
hash_crc32
Vypočítá hash CRC32 zdrojového bufferu a výsledek uloží jako 64bitové celé číslo. Jde o pomocnou funkci, která si interně zajišťuje převod z bajtů na celé číslo.
int hash_crc32(uint8_t *src, size_t src_len, uint64_t *dest);Argumenty:
srcje ukazatel na zdrojový buffer.src_lenje délka zdrojového bufferu v bajtech.destje ukazatel nauint64_tk uložení výsledku CRC32.
Vrací:
- Při úspěchu kladná hodnota (počet interně zapsaných bajtů, vždy 8).
-1v případě, že je zdrojový buffer neplatný.-2v případě, že je cílový buffer null.
hash_blake2b512
Vypočítá hash BLAKE2B-512 zdrojového bufferu a výsledek uloží do cílového bufferu.
int hash_blake2b512(const uint8_t *src, size_t src_len, uint8_t *dest, size_t dest_len);Argumenty:
srcje ukazatel na zdrojový buffer.src_lenje délka zdrojového bufferu v bajtech.destje ukazatel na cílový buffer (musí mít alespoň 64 bajtů).dest_lenje délka cílového bufferu v bajtech.
Vrací:
- Při úspěchu kladná hodnota (počet zapsaných bajtů).
-1v případě, že je zdrojový buffer neplatný.-2v případě, že je cílový buffer null nebo příliš malý.
hmac_sha256
Vypočítá HMAC-SHA256 zdrojového bufferu a výsledek uloží do cílového bufferu. Privátní klíč se nastavuje na úrovni platformy a program BPF k němu nemá přímý přístup. Klíč je jedinečný pro každý server a každého zákazníka.
int hmac_sha256(uint8_t *src, size_t src_len, uint8_t *dest, size_t dest_len);Argumenty:
srcje ukazatel na zdrojový buffer.src_lenje délka zdrojového bufferu v bajtech.destje ukazatel na cílový buffer (musí mít alespoň 32 bajtů).dest_lenje délka cílového bufferu v bajtech.
Vrací:
- Při úspěchu kladná hodnota (počet zapsaných bajtů).
-1v případě, že je zdrojový buffer neplatný.-2v případě, že je cílový buffer null nebo příliš malý.
hmac_sha512
Vypočítá HMAC-SHA512 zdrojového bufferu a výsledek uloží do cílového bufferu. Privátní klíč se nastavuje na úrovni platformy a program BPF k němu nemá přímý přístup. Klíč je jedinečný pro každý server a každého zákazníka.
int hmac_sha512(uint8_t *src, size_t src_len, uint8_t *dest, size_t dest_len);Argumenty:
srcje ukazatel na zdrojový buffer.src_lenje délka zdrojového bufferu v bajtech.destje ukazatel na cílový buffer (musí mít alespoň 64 bajtů).dest_lenje délka cílového bufferu v bajtech.
Vrací:
- Při úspěchu kladná hodnota (počet zapsaných bajtů).
-1v případě, že je zdrojový buffer neplatný.-2v případě, že je cílový buffer null nebo příliš malý.
hmac_blake2b512
Vypočítá BLAKE2B-512 HMAC zdrojového bufferu a výsledek uloží do cílového bufferu. Privátní klíč se nastavuje na úrovni platformy a program BPF k němu nemá přímý přístup. Klíč je jedinečný pro každý server a každého zákazníka.
int hmac_blake2b512(const uint8_t *src, size_t src_len, uint8_t *dest, size_t dest_len);Argumenty:
srcje ukazatel na zdrojový buffer.src_lenje délka zdrojového bufferu v bajtech.destje ukazatel na cílový buffer (musí mít alespoň 64 bajtů).dest_lenje délka cílového bufferu v bajtech.
Vrací:
- Při úspěchu kladná hodnota (počet zapsaných bajtů).
-1v případě, že je zdrojový buffer neplatný.-2v případě, že je cílový buffer null nebo příliš malý.
set_challenge
Nastaví data výzvy pro aktuální paket. Použijte, když chcete klientovi odeslat paket s výzvou.
int set_challenge(uint8_t *src, size_t src_len);Argumenty:
srcje ukazatel na buffer s daty výzvy.src_lenje délka dat výzvy v bajtech. Pokud0, buffer pro challenge se resetuje.
Vrací:
0při úspěchu.-4v případě, že je zdrojový buffer neplatný nebo překračuje maximální povolenou velikost.-5v případě, že výzvy nejsou zapnuté.-6v případě, že této zdrojové IP byla výzva odeslána příliš nedávno nebo je překročen globální limit rychlosti.
get_src_ip_status
Načte ze stavové tabulky hodnotu status spojenou se zdrojovou IP adresou.
int get_src_ip_status(uint8_t *status, uint64_t *expiry);Argumenty:
statusje ukazatel pro uložení hodnoty stavu (CF_EBPF_SRC_IP_STATUS_CHALLENGED,CF_EBPF_SRC_IP_STATUS_VERIFIED, neboCF_EBPF_SRC_IP_STATUS_BLOCKLISTED). Může být null, pokud je potřeba pouze expirace.expiryje ukazatel pro uložení časového razítka vypršení platnosti. Může být null, pokud potřebujete jen stav.
Vrací:
0při úspěchu.-1v případě, že pro danou zdrojovou IP neexistuje žádný záznam.-2v případě, že pro aktuální paket není nastaven kontext zdrojové IP.-3v případě, že je zadaný buffer příliš malý.-4v případě, že jsou zároveňstatusaexpiryjsou null.-5v případě, že stavová tabulka zdrojových IP není zapnutá.
set_src_ip_status
Nastaví hodnotu stavu přiřazenou zdrojové IP adrese ve stavové tabulce.
int set_src_ip_status(uint8_t status, uint64_t expiry_secs);Argumenty:
statusje hodnota stavu, která se má nastavit (CF_EBPF_SRC_IP_STATUS_CHALLENGED,CF_EBPF_SRC_IP_STATUS_VERIFIED, neboCF_EBPF_SRC_IP_STATUS_BLOCKLISTED).expiry_secsje počet sekund do vypršení platnosti stavu. Pokud0, stav nikdy nevyprší.
Vrací:
0při úspěchu.-2v případě, že pro aktuální paket není nastaven kontext zdrojové IP.-5v případě, že stavová tabulka zdrojových IP není zapnutá.
get_src_ip_data
Načte ze stavové tabulky vlastní data spojená se zdrojovou IP adresou.
int get_src_ip_data(uint64_t *data);Argumenty:
dataje ukazatel pro převzetí uložené datové hodnoty.
Vrací:
0při úspěchu.-1v případě, že pro danou zdrojovou IP neexistuje žádný záznam.-2v případě, že pro aktuální paket není nastaven kontext zdrojové IP.-3v případě, že je zadaný buffer příliš malý.-4v případě, žedataje null.-5v případě, že stavová tabulka zdrojových IP není zapnutá.
set_src_ip_data
Ukládá do stavové tabulky vlastní data přiřazená ke zdrojové IP adrese.
int set_src_ip_data(uint64_t data);Argumenty:
dataje datová hodnota, která se má uložit.
Vrací:
0při úspěchu.-2v případě, že pro aktuální paket není nastaven kontext zdrojové IP.-5v případě, že stavová tabulka zdrojových IP není zapnutá.
get_flow_data
Načte ze stavové tabulky vlastní data spojená s aktuálním tokem.
int get_flow_data(uint64_t *data);Argumenty:
dataje ukazatel pro převzetí uložené datové hodnoty.
Vrací:
0při úspěchu.-1v případě, že pro daný tok neexistuje žádný záznam.-2v případě, že pro aktuální paket není nastaven kontext toku.-3v případě, že je zadaný buffer příliš malý.-4v případě, žedataje null nebo není zarovnaný.-5v případě, že stavová tabulka toků není zapnutá.
set_flow_data
Ukládá do stavové tabulky vlastní data přiřazená k aktuálnímu toku.
int set_flow_data(uint64_t data);Argumenty:
dataje datová hodnota, která se má uložit.
Vrací:
0při úspěchu.-2v případě, že pro aktuální paket není nastaven kontext toku.-5v případě, že stavová tabulka toků není zapnutá.
entropy
Vypočítá Shannonovu entropii zdrojového bufferu. Výsledek se vrací v milibitech, v rozsahu od 0 (všechny bajty stejné) do 8000 (všech 256 hodnot bajtů rovnoměrně rozložených).
int64_t entropy(uint8_t *src, size_t src_len);Argumenty:
srcje ukazatel na zdrojový buffer.src_lenje délka zdrojového bufferu v bajtech.
Vrací:
- Při úspěchu hodnota entropie v milibitech (0-8000).
-1v případě, že je zdrojový buffer neplatný.
set_network_analytics_tag
Nastaví vlastní značku pro reporting v Network Analytics. Značka se zobrazí spolu se vzorkem paketu v dashboardu Network Analytics. Ve výchozím nastavení se pakety vzorkují v poměru 1/10,000.
Během jednoho spuštění programu se nastaví jen jedna značka. Pokud spuštění programu volá set_network_analytics_tag vícekrát, na vzorek paketu se použije hodnota posledního tagu.
int set_network_analytics_tag(uint64_t tag);Argumenty:
tagje hodnota tagu, která se má nastavit. Výchozí hodnota je0v případě, že hodnota není nastavena.
Vrací: 0 při úspěchu.
ntohs
Převede 16bitové celé číslo ze síťového pořadí bajtů na pořadí bajtů hostitele.
uint16_t ntohs(uint16_t netshort);Argumenty:
netshortje 16bitová hodnota v síťovém pořadí bajtů.
Vrací: Hodnota v pořadí bajtů hostitele.
htons
Převede 16bitové celé číslo z pořadí bajtů hostitele na síťové pořadí bajtů.
uint16_t htons(uint16_t hostshort);Argumenty:
hostshortje 16bitová hodnota v pořadí bajtů hostitele.
Vrací: Hodnota v síťovém pořadí bajtů.
ntohl
Převede 32bitové celé číslo ze síťového pořadí bajtů na pořadí bajtů hostitele.
uint32_t ntohl(uint32_t netlong);Argumenty:
netlongje 32bitová hodnota v síťovém pořadí bajtů.
Vrací: Hodnota v pořadí bajtů hostitele.
htonl
Převede 32bitové celé číslo z pořadí bajtů hostitele na síťové pořadí bajtů.
uint32_t htonl(uint32_t hostlong);Argumenty:
hostlongje 32bitová hodnota v pořadí bajtů hostitele.
Vrací: Hodnota v síťovém pořadí bajtů.
ntohll
Převede 64bitové celé číslo ze síťového pořadí bajtů na pořadí bajtů hostitele.
uint64_t ntohll(uint64_t netlonglong);Argumenty:
netlonglongje 64bitová hodnota v síťovém pořadí bajtů.
Vrací: Hodnota v pořadí bajtů hostitele.
htonll
Převede 64bitové celé číslo z pořadí bajtů hostitele na síťové pořadí bajtů.
uint64_t htonll(uint64_t hostlonglong);Argumenty:
hostlonglongje 64bitová hodnota v pořadí bajtů hostitele.
Vrací: Hodnota v síťovém pořadí bajtů.
Struktury
cf_ebpf_generic_ctx
Obecná struktura kontextu předávaná programu 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
Obsahuje nezpracovaná data paketu předaná programu 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
Obsahuje ukazatele na parsované hlavičky IP a UDP. Plní se voláním 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
Struktura hlavičky IPv4. Zdroj: Jádro Linuxu ↗.
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
Struktura hlavičky IPv6. Zdroj: Jádro Linuxu ↗.
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
Struktura hlavičky UDP. Zdroj: Jádro Linuxu ↗.
struct udphdr {
uint16_t source;
uint16_t dest;
uint16_t len;
uint16_t check;
};Koncové body programu
Nahrání programu
Program nahrajete v dashboardu Cloudflare v části Networking > L3/4 DDoS protection > Advanced Protection. Potom vyberte kartu Programmable Flow Protection.
V části Programy, klikněte na tlačítko "Upload new program." Zobrazí se výzva k výběru souboru, který nahrajete se svým C zdrojový kód.
Rozhraní Cloudflare API obdrží zdrojový kód v C souboru, zkompilujte jej do bajtkódu BPF a spusťte na něj verifikátor.
Pokud kompilace nebo ověření selže, API vrátí podrobnou chybovou zprávu.
Pokud kompilace a ověření proběhnou úspěšně, Cloudflare uloží k účtu zdrojový kód i objektový soubor a vrátí ID programu.
Aktualizace programu
Při vývoji se vám může hodit aktualizovat stále stejný program (identifikovaný stejným program ID) místo toho, abyste opakovaně vytvářeli nové programy jako nové zdroje.
Program aktualizujete kliknutím na tři tečky vedle něj. Poté vyberte Overwrite. Zobrazí se výzva k výběru souboru, který nahrajete jako C zdrojový kód.
Zobrazení všech programů
Všechny nahrané programy i stav jejich nasazení najdete v tabulce v sekci Programy.
Ikona odkazu vedle názvu programu značí, že program se právě používá v aktivním pravidle a nelze jej smazat.
Smazat program
Chcete-li program odstranit, vyberte tři tečky vedle programu, který chcete odstranit. Potom vyberte Smazat.
Program, na který odkazuje aktivní Rule, nelze smazat.
Programy se stavem "failed" (tedy ty, které se nepodařilo zkompilovat nebo neprošly ověřením) se po 30 dnech nečinnosti automaticky a trvale smažou.
Pravidla
Na každý paket se uplatní jen jedno pravidlo. Pokud máte v účtu nakonfigurováno více pravidel, použije se pravidlo s nejkonkrétnějším rozsah spustí. Například pravidlo s rozsahem konkrétního colo má přednost před pravidlem s rozsahem regionu a to má přednost před globálním pravidlem. Proto nemůžete vytvořit více než jedno globální pravidlo.
Výpis všech pravidel
Pravidla a jejich ID zobrazíte v Sítě > Ochrana před DDoS na vrstvě L3/4 > Advanced Protection v dashboardu Cloudflare. Poté vyberte Programmable Flow Protection.
Vytvoření pravidla
Chcete-li vytvořit pravidlo, přejděte do Sítě > Ochrana před DDoS na vrstvě L3/4 > Advanced Protection v dashboardu Cloudflare. Poté vyberte Programmable Flow Protection.
V části Pravidla, vyberte Vytvořit pravidlo. Vyplňte příslušná pole nového pravidla. Budete vyzváni k výběru programu, režimu a rozsahu pravidla.
Aktualizace pravidla
Existující pravidlo upravíte v sekci Rules. Klikněte na tři tečky vedle pravidla a vyberte Úprava.
Systém vás vyzve k úpravě režimu a rozsahu pravidla. Program pravidla upravit nelze, protože takový způsob zavádění změn není bezpečný.
Smazat pravidlo
Chcete-li odstranit existující pravidlo, přejděte do sekce Rules. Klikněte na tři tečky vedle pravidla a vyberte Smazat.
Debug Packet CAPture (PCAP)
Tento koncový bod API ladí program na základě těchto vstupů:
- Místní cesta ke vstupnímu souboru PCAP předanému jako požadovaná data v binárním formátu. Vstupní soubor PCAP smí mít nejvýše 5 MB, větší soubory budou odmítnuty.
- ID programu uvedené v cestě požadavku.
- Volitelný parametr dotazu
ip_offset=<value>k určení IP offsetu. Jde o počet bajtů, o které je IP hlavička posunuta v každém paketu vstupního souboru PCAP. Pokud parametr dotazu ip offset vynecháte, API správnou hodnotu offsetu odhadne. Pokud například soubor PCAP zachycuje ethernetové pakety, zjištěná hodnota IP offsetu bude 14. Tento endpoint předpokládá, že všechny pakety v souboru PCAP mají stejnou hodnotu IP offsetu, jinak je zpracuje nesprávně.
Tento koncový bod spustí odkazovaný program BPF nad vstupním souborem PCAP a vytvoří nový soubor PCAP s anotacemi. Výstupní soubor PCAP obsahuje přesně stejné pakety jako vstupní soubor a navíc verdikt programu zapsaný v Packet Comment sekci každého paketu.
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.pcapAnotace Packet Comment může obsahovat:
- Návratová hodnota programu:
CF_EBPF_PASSneboCF_EBPF_DROP Ignored: pokud příchozí paket není UDPAnalytics tag: vlastní značka network analytics, kterou program tomuto paketu přiřadil, pokud nějakou přiřadil
Výstupní soubor PCAP může dále obsahovat:
Challenge packet: paket s výzvou, který program odeslal zpět klientovi, pokud nějaký vznikl
Doporučené postupy pro bezpečné nasazení programů a pravidel
Programy budete chtít nasazovat a testovat bezpečně, bez dopadu na stávající produkční provoz. Při prvním nasazení se nabízí nastavit pravidlo s globálním rozsahem na disabled a nastavte pravidlo s rozsahem colo nebo region na monitoring s filtrovacím výrazem, který se vztahuje jen na určitou podmnožinu IP provozu.
Každý region nebo colo Cloudflare použije nejgranulárnější pravidlo. Ve výše popsaném scénáři tedy colo nebo regiony uvedené v monitoring pravidlo spustí vývojářský program v monitoring režimu, zatímco všechny ostatní lokality Cloudflare program vůbec nespustí. monitoring pravidlo by se spustilo pouze na provoz odpovídající výrazu filtru.
Poté, co si správné chování ověříte v Network Analytics, můžete aktualizovat a rozšířit monitoring pravidla rozsah a výraz filtru. Nakonec můžete smazat disabled a monitoring pravidla a použít globální enabled pravidlo.
Pomocí Expression k omezení programů na podmnožinu IP adres nebo prefixů a Mode k určení, zda program pakety skutečně zahazuje, zajišťuje bezpečné a jemně odstupňované zavádění programu.
Network Analytics
Provoz procházející přes Programmable Flow Protection najdete v Network Analytics dashboardu.
V dashboardu Network Analytics vyberte Programmable Flow Protection kartu použijte k filtrování provozu podle této funkce. Provoz lze filtrovat podle ID programu, vlastních tagů network analytics, akcí, IP adres a portů. Ve výchozím nastavení se pakety vzorkují v poměru 1/10,000.