Немного контекста :

Технология eBPF (extended Berkeley Packet Filter) берет свое начало от классического BPF, разработанного в начале 1990-х годов как механизма фильтрации сетевых пакетов, который впоследствии был реализован в Linux и других UNIX-подобных системах. Со временем возможности BPF значительно расширились, и появился eBPF — универсальная виртуальная машина, позволяющая безопасно выполнять пользовательские программы непосредственно внутри ядра без необходимости разрабатывать отдельные модули ядра. Сегодня eBPF применяется далеко не только для обработки сетевого трафика. Он используется для трассировки, мониторинга производительности, обеспечения безопасности, балансировки нагрузки, реализации сетевых политик и многих других задач. Перед загрузкой программа проходит проверку специальным верификатором ядра, гарантируя ее завершаемость, корректность обращений к памяти и соблюдение требований безопасности ядра. Благодаря этому eBPF позволяет расширять функциональность ядра без необходимости писать или загружать собственные модули ядра. Одной из ключевых особенностей является возможность выполнять программы на различных этапах обработки сетевого трафика. Каждая точка подключения (hook) предоставляет собственный набор возможностей и предназначена для решения определенного класса задач. Выбор подходящего уровня зависит от требований к производительности, доступному контексту пакета и необходимой функциональности. Чтобы наглядно показать, где именно располагаются эти точки подключения, рассмотрим путь сетевого пакета в ядре Linux.

alt
На приведённой схеме показано прохождение RX/TX-трафика от сетевого устройства до сетевого стека ядра и обратно. При этом XDP располагается только на пути приёма (RX), тогда как TC предоставляет две точки подключения — ingress и egress — и может обрабатывать трафик в обоих направлениях.

Наиболее ранней точкой обработки является XDP (eXpress Data Path). Программа XDP выполняется между драйвером сетевого устройства и сетевым стеком ядра Linux, еще до выделения структуры sk_buff( socket buffer - это основная структура данных в ядре Linux. Она представляет сетевой пакет на всех этапах его прохождения через сетевой стек.). Благодаря этому XDP обеспечивает минимальную задержку обработки и позволяет эффективно реализовывать высокопроизводительную фильтрацию, ACL, защиту от DDoS и перенаправление пакетов. После формирования sk_buff пакет попадает в сетевой стек Linux, где eBPF-программы могут быть подключены через подсистему Traffic Control (TC). В отличие от XDP, TC работает уже с объектом sk_buff, благодаря чему получает доступ к дополнительному контексту пакета , отсутствующему на уровне XDP, а также тесно интегрирован с механизмами маршрутизации, QoS, conntrack, туннелированием и другими подсистемами ядра. Это делает TC удобным уровнем для реализации NAT, балансировки нагрузки, изменения содержимого пакетов и более сложной обработки сетевого трафика. Помимо XDP и TC, eBPF поддерживает множество других точек подключения. Cgroup hooks позволяют реализовывать политики для процессов и контейнеров, Socket Filter - анализировать трафик отдельных сокетов, а tracepoints, kprobes и uprobes широко используются для трассировки и мониторинга. Несмотря на то что eBPF поддерживает множество точек подключения, они не являются взаимозаменяемыми. Чем раньше программа выполняется в сетевом стеке, тем выше производительность обработки, однако тем меньше информации доступно программе. По мере продвижения пакета по стеку ядро предоставляет все больше контекста и функциональных возможностей, но возрастает стоимость обработки. Поэтому при проектировании современных сетевых решений выбор точки подключения представляет собой компромисс между производительностью и функциональностью.

В рамках проекта Pulsar в качестве основного dataplane был выбран XDP, поскольку именно этот уровень позволяет принимать решение о судьбе пакета максимально рано, еще до прохождения большей части сетевого стека Linux. Это делает XDP оптимальным выбором для реализации высокопроизводительных ACL и других механизмов ранней фильтрации. Вместе с тем архитектура проекта не ограничивается одним уровнем обработки. Функции, которым требуется более богатый контекст пакета или интеграция с подсистемами ядра, планируется реализовать на уровне TC.

Основная часть.

За годы развития сетевого стека Linux появилось множество механизмов управления сетевой политикой. Помимо iptables и nftables существуют firewalld, ufw, Docker, Kubernetes и другие системы, которые предоставляют собственные интерфейсы управления политиками или автоматически изменяют существующие правила фильтрации и маршрутизации. В результате администратору приходится учитывать не только саму сетевую политику, но и особенности её реализации в конкретной системе. Одна и та же политика может быть описана различными способами в зависимости от используемого инструмента. При переходе между различными платформами или внедрении дополнительных компонентов, например Docker или Kubernetes, может изменяться не только синтаксис описания политики, но и точки её применения, порядок обработки трафика и взаимодействие между различными механизмами.

Аналогичная ситуация наблюдается и за пределами Linux: многие BSD-системы и сетевые устройства используют собственные модели описания политики.

Например, одна и та же политика сетевой фильтрации: запретить TCP-трафик от 192.168.0.3 к 192.168.0.202:80 - может выглядеть совершенно по-разному.

В iptables:

    iptables -A INPUT -s 192.168.0.3 -d 192.168.0.202 -p tcp --dport 80 -j DROP    

В nftables:

    ip saddr 192.168.0.3 ip daddr 192.168.0.202 tcp dport 80 drop.   

В pf используется уже другой синтаксис:

    block in proto tcp from 192.168.0.3 to 192.168.0.202 port 80.    

Во всех приведённых примерах описывается одна и та же сетевая политика, однако её представление зависит от конкретного механизма исполнения.

И в разрабатываемом мной dataplane та же политика описывается через собственный единый интерфейс:

./ebpf-ctl acl add drop  src 192.168.0.3:any  dst 192.168.0.202:80  proto=tcp.   

Или через файл конфигурации:

acl:
  drop:
    - src:
        addresses:
          - 192.168.0.3
        dst:
          addresses:
            - 192.168.0.202
        ports:
          number: 80
          proto: tcp

Цель — не в том, чтобы создать ещё один синтаксис, а в том, чтобы абстрагировать описание политики от механизма исполнения. Пользователь работает с единой декларативной моделью сетевой политики, не зависящей от конкретного механизма её исполнения. Control plane преобразует описание политики во внутреннее представление, используемое dataplane, а CLI и YAML являются лишь различными способами описания одной и той же политики.

Архитектура Pulsar

alt
На схеме показан путь политики от описания до исполнения. Система построена по классической схеме разделения control plane и dataplane: control plane отвечает за формирование и доставку состояния политики, а dataplane - за обработку каждого пакета.

Основным механизмом взаимодействия control plane и dataplane служат BPF maps, через которые control plane передаёт dataplane подготовленное состояние политики: control plane записывает в них состояние политики, dataplane читает его при обработке пакета. Интерфейсы управления. Описание политики поступает в систему через декларативный конфигурационный файл (config.yml) или клиент командной строки. Оба интерфейса намеренно сделаны «тонкими»: они передают описание политики в daemon и не содержат собственной логики интерпретации. Daemon — ядро control plane. Он преобразует полученное описание во внутреннюю модель политики, выполняет валидацию и нормализацию, после чего компилирует политику в представление, пригодное для dataplane. На последнем этапе готовые структуры записываются в BPF maps. Dataplane. XDP-программа, прикреплённая к интерфейсу, на каждом пакете обращается к BPF maps и принимает решение: пропустить или отбросить пакет, не обращаясь непосредственно к control plane. Поток конфигурации на схеме направлен сверху вниз; в runtime данные движутся иначе: XDP читает политику из карт.

Единая семантика политики

Одна и та же политика должна иметь единое семантическое представление независимо от того, получена ли она через CLI, YAML или другой интерфейс управления. Для этого вся смысловая работа - парсинг, валидация, нормализация, компиляция - сосредоточена в daemon, а интерфейсы управления остаются лишь адаптерами к внутренней модели. Такое разделение позволяет развивать интерфейсы управления независимо от dataplane и в дальнейшем добавлять новые механизмы исполнения, не изменяя саму модель политики. BPF maps как контракт между плоскостями BPF maps в этой архитектуре выступают не просто структурами данных, а контрактом между control plane и dataplane. Dataplane не знает, откуда пришла политика; control plane не знает, как именно будет обработан пакет. Пока обе стороны соблюдают контракт, точку исполнения политики можно заменить (например, реализовать её через TC/eBPF), а набор интерфейсов управления — расширить, не затрагивая соседнюю плоскость. Отсюда следует важное эксплуатационное свойство: отказ control plane не затрагивает dataplane. Если daemon завершится, XDP-программа продолжит применять последние загруженные карты — фильтрация не прекратится.

Пока Pulsar реализует L3/L4 ACL, фильтрацию IPv4 и IPv6, VLAN/QinQ ACL и rate limiting. Управление выполняется через userspace control plane, поддерживающий декларативное описание политики.
Благодаря этому независимо от выбранного интерфейса управления dataplane получает одинаковое представление политики.

Отдельное внимание уделено обработке событий. eBPF dataplane может передавать выбранные события в userspace через Ring Buffer, где они преобразуются в логируемые события. Набор логируемых событий является настраиваемым, что позволяет отделить механизм генерации событий от их конечного представления и хранения. Таким образом, основное отличие Pulsar заключается не в количестве реализованных сетевых функций, а в архитектурном разделении control plane и dataplane. Control plane отвечает за описание, проверку и преобразование политики, тогда как dataplane использует уже подготовленное состояние для обработки пакетов.

alt
На схеме показаны основные потоки между control plane, dataplane и системой обработки событий.

Плоскость управления находится в user space: консольный клиент передаёт команды демону через Unix-socket, а daemon записывает правила в BPF-мапы — ACL IP (разрешение или блокировка по адресу), ACL VLAN и Rate Limit (ограничение интенсивности потока). Благодаря этому правила применяются «на лету», без перезагрузки XDP-программы. Через конфигурационную мапу Select Event daemon также управляет логированием: задаёт, какие события должны фиксироваться, и модуль XDP отправляет в Ring Buffer только их, откуда daemon читает и обрабатывает события.

Плоскость данных работает непосредственно на receive path драйвера: пакет с NIC попадает в XDP до выделения sk_buff и до передачи в сетевой стек Linux. Сопоставляя пакет с правилами в мапах, модуль принимает решение. При разрешении пакет передаётся дальше в обычный receive path Linux и продолжает обработку сетевым стеком, где в зависимости от конфигурации может пройти последующие точки обработки, включая TC ingress.При блокировке пакет отбрасывается на максимально раннем этапе, что позволяет выполнять ранний drop и снижать нагрузку на последующие уровни сетевого стека при интенсивном входящем трафике.

Механизмы обработки трафика в XDP

Сейчас основная логика принятия решений в Pulsar реализована в XDP-модуле.При обработке пакета он обращается к подготовленным control plane’ом BPF-мапам и на их основе выполняет проверку и применение политик. Реализованы три базовых механизма: ACL IP, ACL VLAN и Rate Limit.

ACL IP

ACL IP отвечает за фильтрацию трафика на уровне IPv4/IPv6 и параметров L4. Именно этот механизм реализует базовые политики разрешения или запрета трафика между источниками и получателями.

При обработке пакета XDP-модуль извлекает из него необходимые поля и формирует ключ для поиска в ACL-мапе. Для IP-политик такими полями могут быть: source IP address; destination IP address; transport protocol, например TCP или UDP; source port; destination port. На основании найденного правила dataplane принимает решение: разрешить пакет, отбросить его и зафиксировать событие через Ring Buffer, если для данного правила включено логирование.

IPv4 и IPv6 обрабатываются в рамках единой модели политики, а control plane преобразует её в соответствующие записи BPF maps, поэтому dataplane не занимается парсингом конфигурации и работает уже с нормализованным состоянием политики. Важно, что ACL IP в Pulsar является stateless-механизмом: решение по пакету принимается на основании текущих полей пакета и загруженных правил, без обязательного отслеживания состояния соединения. Это позволяет сохранить высокую скорость обработки на уровне XDP.

ACL VLAN

ACL VLAN предназначен для фильтрации трафика по меткам 802.1Q , QinQ. В отличие от IP ACL, этот механизм работает не с IP-адресами и портами, а с VLAN-тегами, которые могут присутствовать в Ethernet-кадре.

Такая фильтрация нужна не только для «учёта VLAN», но и для решения практических задач: Изоляция сетевых сегментов. Если на интерфейс приходит трафик из нескольких VLAN, можно на уровне хоста запретить обработку нежелательных VLAN ещё до входа пакета в сетевой стек. Защита от ошибок конфигурации L2.

Например, если из-за неверной настройки коммутатора трафик одного сегмента неожиданно попал в другой, ACL VLAN позволяет отбросить его на раннем этапе.

Снижение риска VLAN hopping и обработка некорректной QinQ-инкапсуляции. Пакеты с неожидаемыми или запрещёнными VLAN-тегами могут быть отброшены до передачи в сетевой стек Linux.

Единая декларативная политика.

В классическом Linux фильтрация L2 и L3 часто выполняется разными инструментами и с разной семантикой. В Pulsar VLAN-политика описывается в той же модели, что и IP-политика, и загружается в dataplane через общий control plane.

ACL VLAN особенно полезен на bare-metal серверах, гипервизорах, edge-узлах и в инфраструктурах, где один физический или логический интерфейс может получать трафик из нескольких изолированных сегментов.

При этом важно учитывать особенности драйверов сетевых устройств: в некоторых режимах NIC может снимать VLAN-тег аппаратно и передавать информацию о VLAN через метаданные. Поэтому корректная реализация VLAN ACL должна учитывать как наличие тега непосредственно в пакете, так и возможное представление VLAN в контексте XDP. Rate Limit

Rate Limit в Pulsar предназначен для ограничения интенсивности трафика за единицу времени. Для каждого ограничения в BPF map хранится отдельное состояние, связанное с соответствующим правилом политики. В состоянии хранятся параметры, необходимые для расчёта текущего лимита, например количество доступных токенов и временная метка последнего обновления.

Сравнение с существующими решениями : На сегодняшний день существует несколько различных подходов к реализации сетевой фильтрации и программируемых dataplane. К классическим решениям относятся iptables и nftables, а среди современных eBPF-based решений наиболее близкими по архитектуре являются Cilium и Calico. Отдельное место занимают специализированные eBPF dataplane, такие как Katran, ориентированные прежде всего на высокопроизводительную L4-балансировку.

Классические Linux firewall, такие как iptables и nftables, интегрированы с сетевым стеком Linux и используют механизмы подсистемы Netfilter для обработки сетевых пакетов. Управление выполняется через userspace-инструменты, которые преобразуют заданные правила в состояние подсистемы Netfilter. Такой подход является зрелым и хорошо интегрированным в Linux, однако модель правил и механизм их исполнения тесно связаны с конкретной реализацией firewall.

Cilium представляет наиболее близкий к Pulsar пример использования eBPF для построения программируемого сетевого dataplane. Cilium использует eBPF на различных этапах обработки сетевого трафика, включая точки XDP и TC, в зависимости от конкретной функции и конфигурации.

Помимо сетевой политики, Cilium уже реализует NAT, load balancing, routing, service networking и другие функции. Поэтому по количеству реализованных возможностей Pulsar на текущем этапе существенно уступает Cilium.

Calico предоставляет другой пример eBPF-based dataplane. Компоненты управления Calico преобразуют сетевую политику в состояние, используемое dataplane. eBPF используется для реализации filtering, routing и других сетевых функций. Как и Cilium,

Calico является значительно более зрелой платформой и ориентирован в первую очередь на cloud-native и Kubernetes-инфраструктуру.

Katran представляет ещё один интересный пример. В отличие от Cilium и Calico, он не является универсальной системой сетевой безопасности, а представляет собой высокопроизводительный L4 load-balancer dataplane на основе eBPF/XDP. Его архитектура хорошо демонстрирует преимущества переноса части сетевой обработки на ранние этапы packet path, однако основная задача Katran отличается от задачи Pulsar.

На этом фоне Pulsar находится на более раннем этапе развития и пока не пытается конкурировать с существующими платформами по количеству функций. Текущая реализация сосредоточена на построении программируемого security dataplane, в котором политика безопасности отделена от конкретного механизма исполнения. Ключевая идея архитектуры заключается в том, что политика описывается независимо от dataplane, преобразуется control plane в формализованное состояние и затем исполняется непосредственно в eBPF-программе. Это позволяет отделить семантику политики от механизма обработки пакетов и в дальнейшем развивать обе части системы независимо друг от друга.

Тестовый стенд состоял из двух одноплатных компьютеров Orange Pi RV2 и Orange Pi R2S, а также маршрутизатора MikroTik. Обе одноплатные системы используют SoC семейства SpacemiT K1 (Ky X1) и архитектуру RISC-V 64. 1)Orangepi RV2(192.168.0.202 ) –тестовая машина на которой собран и запущен Pulsar . 2)Orangepi R2S – 192.168.0.3-машина без Pulsar .

1.проверяем ,что нет никаких на данных момент правил :

root@orangepirv2:/home/orangepi/ebpf# ./target/debug/ebpf-ctl acl list
(l4_acl map is empty)
root@orangepirv2:/home/orangepi/ebpf#

2.Удостоверяемся,что логирование было выключено :

cat ./test.log 
2026-08-13T17:15:32.884095Z  WARN ebpf_daemon: Failed to load initial config config2.yml: io error: No such file or directory (os error 2)
2026-08-13T17:15:32.884379Z  WARN ebpf_daemon: Starting without configuration. Use 'ebpf-ctl reload <config>' to load later.
2026-08-13T17:15:32.995098Z  INFO ebpf_daemon::monitoring: Trying XDP program id: 237
2026-08-13T17:15:33.049185Z  INFO ebpf_daemon::monitoring: bpftool prog show id 237: 237: xdp  name xdp_dataplane  tag fa484001870657d5
	loaded_at 2026-08-12T07:52:31+0300  uid 0
	xlated 19776B  jited 8972B  memlock 20480B  map_ids 470,471,472,474,480,479
	btf_id 351
2026-08-13T17:15:33.103932Z  WARN ebpf_daemon::monitoring: Skipping map id 470: Map id 470 is not events ringbuf (name=counters, type=percpu_array)
2026-08-13T17:15:33.158030Z  WARN ebpf_daemon::monitoring: Skipping map id 471: Map id 471 is not events ringbuf (name=event_mask, type=array)
2026-08-13T17:15:33.212209Z  INFO ebpf_daemon::monitoring: Opened events map
2026-08-13T17:15:33.212382Z  INFO ebpf_daemon::monitoring: Events ringbuf size: 16777216 bytes
2026-08-13T17:15:33.219468Z  INFO ebpf_daemon::monitoring: Starting ringbuf event loop
root@orangepirv2:/home/orangepi/ebpf# 
./target/debug/ebpf-ctl event-log list
event-log mask: 0x0
enabled events:
  [ ] PacketDrop
  [ ] RateLimited
  [ ] ConntrackMiss
  [ ] BackendSelected
  [ ] SlowPath
  [ ] ServiceMatched
  [ ] VlanDetected
  [ ] PacketAllow
./target/debug/ebpf-ctl event-log enable  PacketDrop
enabled event-log: PacketDrop
root@orangepirv2:/home/orangepi/ebpf# ./target/debug/ebpf-ctl event-log list
event-log mask: 0x2
enabled events:
  [x] PacketDrop
  [ ] RateLimited
  [ ] ConntrackMiss
  [ ] BackendSelected
  [ ] SlowPath
  [ ] ServiceMatched
  [ ] VlanDetected
  [ ] PacketAllow

2.Проверяем Deny IP ACL правила
ICMP:

root@orangepirv2:/home/orangepi/ebpf# ./target/debug/ebpf-ctl acl add drop src 192.168.0.0/24:any dst 192.168.0.202:any proto icmp
ACL rule added
added rule Drop: icmp 192.168.0.0:any -> 192.168.0.202:any (prefix_len=304)
root@orangepirv2:~sudo tcpdump -ni end0 icmp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on end0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
^C
0 packets captured
0 packets received by filter
0 packets dropped by kernel

И читаем лог файл:

root@orangepirv2:/home/orangepi/ebpf# cat ./test.log 
…
2026-08-13T17:21:35.257433Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=545048648274746 event_kind=PacketDrop ifindex=2 src=192.168.0.3 dst=192.168.0.202 src_port=0 dst_port=0 protocol=ICMP aux0=0 aux1=0
2026-08-13T17:21:36.285353Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=545049676208365 event_kind=PacketDrop ifindex=2 src=192.168.0.3 dst=192.168.0.202 src_port=0 dst_port=0 protocol=ICMP aux0=0 aux1=0
2026-08-13T17:21:37.313289Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=545050704135651 event_kind=PacketDrop ifindex=2 src=192.168.0.3 dst=192.168.0.202 src_port=0 dst_port=0 protocol=ICMP aux0=0 aux1=0
2026-08-13T17:21:38.333321Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=545051724172595 event_kind=PacketDrop ifindex=2 src=192.168.0.3 dst=192.168.0.202 src_port=0 dst_port=0 protocol=ICMP aux0=0 aux1=0
2026-08-13T17:21:39.357436Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=545052748286770 event_kind=PacketDrop ifindex=2 src=192.168.0.3 dst=192.168.0.202 src_port=0 dst_port=0 protocol=ICMP aux0=0 aux1=0
2026-08-13T17:21:40.381334Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=545053772177407 event_kind=PacketDrop ifindex=2 src=192.168.0.3 dst=192.168.0.202 src_port=0 dst_port=0 protocol=ICMP aux0=0 aux1=0
2026-08-13T17:21:41.405289Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=545054796140251 event_kind=PacketDrop ifindex=2 src=192.168.0.3 dst=192.168.0.202 src_port=0 dst_port=0 protocol=ICMP aux0=0 aux1=0
2026-08-13T17:21:42.429270Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=545055820124679 event_kind=PacketDrop ifindex=2 src=192.168.0.3 dst=192.168.0.202 src_port=0 dst_port=0 protocol=ICMP aux0=0 aux1=0
2026-08-13T17:21:43.453263Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=545056844116064 event_kind=PacketDrop ifindex=2 src=192.168.0.3 dst=192.168.0.202 src_port=0 dst_port=0 protocol=ICMP aux0=0 aux1=0

На самом OrangepiR2S соответственно :

$ ping 192.168.0.202
PING 192.168.0.202 (192.168.0.202) 56(84) bytes of data.
From 192.168.0.1: icmp_seq=2 Redirect Host(New nexthop: 192.168.0.202)
^C
--- 192.168.0.202 ping statistics ---
9 packets transmitted, 0 received, 100% packet loss, time 8196ms

Проверяем TCP при помощи iperf3 (например будет 80 порт ): На orangepi RV2:

root@orangepirv2:/home/orangepi/ebpf# ./target/debug/ebpf-ctl acl add drop src 192.168.0.0/24:any dst 192.168.0.202:80 proto tcp
ACL rule added

./target/debug/ebpf-ctl acl list
id  plen  fam  src                                dst                                proto sport  dport  rate  action
0   304   4    192.168.0.0                        192.168.0.202                      icmp any    any    -     Drop
1   320   4    192.168.0.0                        192.168.0.202                      tcp  any    80     -     Drop

root@orangepirv2:~# iperf3 -s -p 80
-----------------------------------------------------------
Server listening on 80 (test #1)
-----------------------------------------------------------
^Ciperf3: interrupt - the server has terminated

root@orangepirv2:/home/orangepi/ebpf# cat ./test.log 
…
src=192.168.0.3 dst=192.168.0.202 src_port=47284 dst_port=80 protocol=TCP aux0=0 aux1=0
2026-08-13T17:36:23.293385Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=545936684167882 event_kind=PacketDrop ifindex=2 src=192.168.0.3 dst=192.168.0.202 src_port=47284 dst_port=80 protocol=TCP aux0=0 aux1=0
2026-08-13T17:36:24.317258Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=545937708103844 event_kind=PacketDrop ifindex=2 src=192.168.0.3 dst=192.168.0.202 src_port=47284 dst_port=80 protocol=TCP aux0=0 aux1=0
2026-08-13T17:36:25.341274Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=545938732107054 event_kind=PacketDrop ifindex=2 src=192.168.0.3 dst=192.168.0.202 src_port=47284 dst_port=80 protocol=TCP aux0=0 aux1=0
2026-08-13T17:36:26.365267Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=545939756097723 event_kind=PacketDrop ifindex=2 src=192.168.0.3 dst=192.168.0.202 src_port=47284 dst_port=80 protocol=TCP aux0=0 aux1=0
2026-08-13T17:36:27.389307Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=545940780144350 event_kind=PacketDrop ifindex=2 src=192.168.0.3 dst=192.168.0.202 src_port=47284 dst_port=80 protocol=TCP aux0=0 aux1=0
2026-08-13T17:36:29.405287Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=545942796125120 event_kind=PacketDrop ifindex=2 src=192.168.0.3 dst=192.168.0.202 src_port=47284 dst_port=80 protocol=TCP aux0=0 aux1=0
root@orangepirv2:/home/orangepi/ebpf# 

естесственно у Orangepi r2s:

root@orangepir2s:/home/orangepi# iperf3 -c 192.168.0.202 -p80
^C- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
iperf3: interrupt - the client has terminated

UDP:

root@orangepirv2:/home/orangepi/ebpf# ./target/debug/ebpf-ctl acl add drop src 192.168.0.0/24:any dst 192.168.0.202:80 proto udp
ACL rule added
added rule Drop: udp 192.168.0.0:any -> 192.168.0.202:80 (prefix_len=320)
root@orangepirv2:~# iperf3 -s -p 80 
-----------------------------------------------------------
Server listening on 80 (test #1)
-----------------------------------------------------------
^Ciperf3: interrupt - the server has terminated
root@orangepirv2:/home/orangepi/ebpf# cat ./test.log 
…
2026-08-13T17:54:34.709969Z  WARN ebpf_daemon::monitoring: Skipping map id 470: Map id 470 is not events ringbuf (name=counters, type=percpu_array)
2026-08-13T17:54:34.763748Z  WARN ebpf_daemon::monitoring: Skipping map id 471: Map id 471 is not events ringbuf (name=event_mask, type=array)
2026-08-13T17:54:34.817658Z  INFO ebpf_daemon::monitoring: Opened events map
2026-08-13T17:54:34.817834Z  INFO ebpf_daemon::monitoring: Events ringbuf size: 16777216 bytes
2026-08-13T17:54:34.825118Z  INFO ebpf_daemon::monitoring: Starting ringbuf event loop
2026-08-13T17:54:50.010419Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=547043401254143 event_kind=PacketDrop ifindex=2 src=192.168.0.3 dst=192.168.0.202 src_port=59158 dst_port=80 protocol=UDP aux0=0 aux1=0
root@orangepirv2:/home/orangepi/ebpf# 

OrangePI R2S:

iperf3 -c 192.168.0.202 -p80 -u
Connecting to host 192.168.0.202, port 80
iperf3: error - unable to read from stream socket: Resource temporarily unavailable
root@orangepir2s:/home/orangepi# 

3.Проверяем Allow IP ACL.Здесь мы делаем разрешающее правило ,но уже для конкретного хоста (с rate limit и без): Включаем логирование :

root@orangepirv2:/home/orangepi/ebpf# ./target/debug/ebpf-ctl event-log enable PacketAllow
enabled event-log: PacketAllow
root@orangepirv2:/home/orangepi/ebpf# ./target/debug/ebpf-ctl event-log list
event-log mask: 0x102
enabled events:
  [x] PacketDrop
  [ ] RateLimited
  [ ] ConntrackMiss
  [ ] BackendSelected
  [ ] SlowPath
  [ ] ServiceMatched
  [ ] VlanDetected
  [x] PacketAllow

UDP-трафик отдельно не использовался для проверки разрешающего сценария через iperf3, поскольку UDP-тест iperf3 требует дополнительного TCP-соединения для управления сессией. При этом обработка UDP на уровне ACL была непосредственно проверена с помощью правила Drop: в eBPF-журнале зафиксирован пакет protocol=UDP, dst_port=80, после чего пакет был отброшен.

./target/debug/ebpf-ctl acl add allow src 192.168.0.3:any dst 192.168.0.202:80 proto tcp
ACL rule added
added rule Allow: tcp 192.168.0.3:any -> 192.168.0.202:80 (prefix_len=320)
root@orangepirv2:/home/orangepi/ebpf# ./target/debug/ebpf-ctl acl list
id  plen  fam  src                                dst                                proto sport  dport  rate  action
0   304   4    192.168.0.0                        192.168.0.202                      icmp any    any    -     Drop
1   320   4    192.168.0.0                        192.168.0.202                      tcp  any    80     -     Drop
2   320   4    192.168.0.0                        192.168.0.202                      udp  any    80     -     Drop
3   320   4    192.168.0.3                        192.168.0.202                      tcp  any    80     -     Allow
root@orangepirv2:/home/orangepi/ebpf# iperf3 -s -p 80  
-----------------------------------------------------------
Server listening on 80 (test #1)
-----------------------------------------------------------
Accepted connection from 192.168.0.3, port 48720
[  5] local 192.168.0.202 port 80 connected to 192.168.0.3 port 48730
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-1.00   sec  58.0 MBytes   486 Mbits/sec                  
[  5]   1.00-2.00   sec  59.9 MBytes   502 Mbits/sec                  
[  5]   2.00-3.00   sec  60.1 MBytes   504 Mbits/sec                  
[  5]   3.00-4.00   sec  59.5 MBytes   499 Mbits/sec                  
[  5]   4.00-5.00   sec  58.6 MBytes   492 Mbits/sec                  
[  5]   5.00-6.00   sec  58.5 MBytes   491 Mbits/sec                  
[  5]   6.00-7.00   sec  59.2 MBytes   497 Mbits/sec                  
[  5]   7.00-8.00   sec  60.1 MBytes   504 Mbits/sec                  
[  5]   8.00-9.00   sec  60.2 MBytes   505 Mbits/sec                  
[  5]   9.00-10.00  sec  59.4 MBytes   498 Mbits/sec                  
[  5]  10.00-10.02  sec  1.25 MBytes   510 Mbits/sec                  
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-10.02  sec   595 MBytes   498 Mbits/sec                  receiver
-----------------------------------------------------------
Server listening on 80 (test #2)
-----------------------------------------------------------
^Ciperf3: interrupt - the server has terminated
root@orangepirv2:/home/orangepi/ebpf# cat ./test.log | grep "192.168.0.3"
…
2026-08-13T18:24:00.442171Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=548786152157138 event_kind=PacketAllow ifindex=2 src=192.168.0.3 dst=192.168.0.202 src_port=32988 dst_port=80 protocol=TCP aux0=0 aux1=0
2026-08-13T18:24:00.442250Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=548786152166221 event_kind=PacketAllow ifindex=2 src=192.168.0.3 dst=192.168.0.202 src_port=32988 dst_port=80 protocol=TCP aux0=0 aux1=0
2026-08-13T18:24:00.442329Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=548786152174721 event_kind=PacketAllow ifindex=2 src=192.168.0.3 dst=192.168.0.202 src_port=32988 dst_port=80 protocol=TCP aux0=0 aux1=0
2026-08-13T18:24:00.442407Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=548786152184263 event_kind=PacketAllow ifindex=2 src=192.168.0.3 dst=192.168.0.202 src_port=32988 dst_port=80 protocol=TCP aux0=0 aux1=0
2026-08-13T18:24:00.442486Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=548786152192096 event_kind=PacketAllow ifindex=2 src=192.168.0.3 dst=192.168.0.202 src_port=32988 dst_port=80 protocol=TCP aux0=0 aux1=0
2026-08-13T18:24:00.442565Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=548786152200096 event_kind=PacketAllow ifindex=2 src=192.168.0.3 dst=192.168.0.202 src_port=32988 dst_port=80 protocol=TCP aux0=0 aux1=0

По поводу rate limit(здесь просто будет проверка через iperf3):

./target/debug/ebpf-ctl acl list
id  plen  fam  src                                dst                                proto sport  dport  rate  action
0   304   4    192.168.0.0                        192.168.0.202                      icmp any    any    -     Drop
1   320   4    192.168.0.0                        192.168.0.202                      tcp  any    80     -     Drop
2   320   4    192.168.0.0                        192.168.0.202                      udp  any    80     -     Drop
3   320   4    192.168.0.3                        192.168.0.202                      tcp  any    80     -     Allow
root@orangepirv2:/home/orangepi/ebpf# ./target/debug/ebpf-ctl acl del --id 3
Deleted ACL rule 3
deleted rule id=3
root@orangepirv2:/home/orangepi/ebpf# ./target/debug/ebpf-ctl acl add allow src 192.168.0.3:any dst 192.168.0.202:80 proto tcp rate 1
ACL rule added
added rule Allow: tcp 192.168.0.3:any -> 192.168.0.202:80 (prefix_len=320 rate=1)
root@orangepirv2:/home/orangepi/ebpf# ./target/debug/ebpf-ctl acl list
id  plen  fam  src                                dst                                proto sport  dport  rate  action
0   304   4    192.168.0.0                        192.168.0.202                      icmp any    any    -     Drop
1   320   4    192.168.0.0                        192.168.0.202                      tcp  any    80     -     Drop
2   320   4    192.168.0.0                        192.168.0.202                      udp  any    80     -     Drop
3   320   4    192.168.0.3                        192.168.0.202                      tcp  any    80     1     Allow
root@orangepirv2:/home/orangepi/ebpf# 

и по тестам тогда будет:

root@orangepirv2:~# iperf3 -s -p 80  
-----------------------------------------------------------
Server listening on 80 (test #1)
-----------------------------------------------------------
Accepted connection from 192.168.0.3, port 45004
[  5] local 192.168.0.202 port 80 connected to 192.168.0.3 port 45010
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-1.00   sec   256 KBytes  2.09 Mbits/sec                  
[  5]   1.00-2.00   sec  0.00 Bytes  0.00 bits/sec                  
[  5]   2.00-3.00   sec  0.00 Bytes  0.00 bits/sec                  
[  5]   3.00-4.00   sec  0.00 Bytes  0.00 bits/sec                  
[  5]   4.00-5.00   sec  0.00 Bytes  0.00 bits/sec                  
[  5]   5.00-6.00   sec  0.00 Bytes  0.00 bits/sec                  
[  5]   6.00-7.00   sec  0.00 Bytes  0.00 bits/sec                  
[  5]   7.00-8.00   sec  0.00 Bytes  0.00 bits/sec                  
[  5]   8.00-9.00   sec  0.00 Bytes  0.00 bits/sec                  
[  5]   9.00-10.00  sec  0.00 Bytes  0.00 bits/sec                  
[  5]  10.00-10.21  sec   100 KBytes  3.90 Mbits/sec                  
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-10.21  sec   356 KBytes   286 Kbits/sec                  receiver
-----------------------------------------------------------
Server listening on 80 (test #2)
-----------------------------------------------------------

Теперь сравните что было ранее на аналогичном тесте.

4.Проверяем Deny и allow VLAN ACL: Для функциональной проверки VLAN ACL достаточно изолированного стенда из двух-трех узлов. Масштабный стенд с большим количеством VLAN, хостов и политик потребовался бы уже для оценки производительности и масштабируемости dataplane при увеличении количества правил и сетевых сегментов. Проверка VLAN выполнялась на физическом интерфейсе end0. VLAN-подинтерфейсы end0.300 и end0.100.200 использовались для настройки IP-адресации и формирования тестового трафика, тогда как обработка VLAN-заголовков выполнялась XDP-программой на входящем трафике физического интерфейса.

Как я настраивал vlan на микротик:

/interface vlan
add interface=bridge0 name=vlan300 vlan-id=300
/ip address
add address=192.168.100.1/24 interface=vlan300

Как я настраивал vlan на orangepi rv2(На R2S -было настроено по аналогии): Обычный Vlan: ip link add link end0 name end0.300 type vlan id 300 ip addr add 192.168.100.2/24 dev end0.300 ip link set end0.300 up QinQ на микротик: Внешний тег :

/interface vlan
add interface=bridge0 name=qinq100 vlan-id=100 

Внутренний тег:

interface vlan
add interface=qinq100 name=vlan200 vlan-id=200
/ip address
add address=192.168.200.1/24 interface=vlan200

На orangepi :

ip link add link end0 name end0.100 type vlan id 100 ip link add link end0.100 name end0.100.200 type vlan id 200 ip addr add 192.168.200.2/24 dev end0.100.200
и получаем на orangepi :
6: end0.100@end0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether c0:74:2b:fa:72:0c brd ff:ff:ff:ff:ff:ff
    inet6 fe80::c274:2bff:fefa:720c/64 scope link 
       valid_lft forever preferred_lft forever
7: end0.100.200@end0.100: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether c0:74:2b:fa:72:0c brd ff:ff:ff:ff:ff:ff
    inet 192.168.200.2/24 scope global end0.100.200
       valid_lft forever preferred_lft forever
    inet6 fe80::c274:2bff:fefa:720c/64 scope link 
       valid_lft forever preferred_lft forever
9: end0.300@end0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether c0:74:2b:fa:72:0c brd ff:ff:ff:ff:ff:ff
    inet 192.168.100.2/24 scope global end0.300
       valid_lft forever preferred_lft forever
    inet6 fe80::c274:2bff:fefa:720c/64 scope link 
       valid_lft forever preferred_lft forever

4.1 Deny VLAN ACL:

Для чистоты эксперимента :

root@orangepirv2:/home/orangepi/ebpf# ./target/debug/ebpf-ctl acl del --id 0
error: Failed to delete rule: Failed to delete ACL rule
root@orangepirv2:/home/orangepi/ebpf# ./target/debug/ebpf-ctl acl list
(l4_acl map is empty)
root@orangepirv2:/home/orangepi/ebpf# ./target/debug/ebpf-ctl acl vlan list
(vlan_acl map is empty)
root@orangepirv2:/home/orangepi/ebpf# ./target/debug/ebpf-ctl event-log list
event-log mask: 0x2
enabled events:
  [x] PacketDrop
  [ ] RateLimited
  [ ] ConntrackMiss
  [ ] BackendSelected
  [ ] SlowPath
  [ ] ServiceMatched
  [ ] VlanDetected
  [ ] PacketAllow
root@orangepirv2:/home/orangepi/ebpf# ./target/debug/ebpf-ctl event-log enable VlanDetected
enabled event-log: VlanDetected
root@orangepirv2:/home/orangepi/ebpf# ./target/debug/ebpf-ctl acl vlan add 300 drop
Added VLAN rule: outer_vlan=300
added vlan rule (outer=300): drop
root@orangepirv2:/home/orangepi/ebpf#
cat ./test.log 
…
src=192.168.100.1 dst=192.168.100.2 src_port=0 dst_port=0 protocol=ICMP aux0=300 aux1=1
2026-08-13T19:19:27.799742Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=552121190282679 event_kind=PacketDrop ifindex=2 src=192.168.100.1 dst=192.168.100.2 src_port=0 dst_port=0 protocol=ICMP aux0=0 aux1=0
2026-08-13T19:19:28.813405Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=552122204262459 event_kind=VlanDetected ifindex=2 src=192.168.100.1 dst=192.168.100.2 src_port=0 dst_port=0 protocol=ICMP aux0=300 aux1=1
2026-08-13T19:19:28.813658Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=552122204286126 event_kind=PacketDrop ifindex=2 src=192.168.100.1 dst=192.168.100.2 src_port=0 dst_port=0 protocol=ICMP aux0=0 aux1=0
2026-08-13T19:19:29.837427Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=552123228281300 event_kind=VlanDetected ifindex=2 src=192.168.100.1 dst=192.168.100.2 src_port=0 dst_port=0 protocol=ICMP aux0=300 aux1=1
2026-08-13T19:19:29.837677Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=552123228302716 event_kind=PacketDrop ifindex=2 src=192.168.100.1 dst=192.168.100.2 src_port=0 dst_port=0 protocol=ICMP aux0=0 aux1=0
2026-08-13T19:19:30.861526Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=552124252362097 event_kind=VlanDetected ifindex=2 src=192.168.100.1 dst=192.168.100.2 src_port=0 dst_port=0 protocol=ICMP aux0=300 aux1=1
2026-08-13T19:19:30.861864Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=552124252401680 event_kind=PacketDrop ifindex=2 src=192.168.100.1 dst=192.168.100.2 src_port=0 dst_port=0 protocol=ICMP aux0=0 aux1=0
2026-08-13T19:19:31.885517Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=552125276358813 event_kind=VlanDetected ifindex=2 src=192.168.100.1 dst=192.168.100.2 src_port=0 dst_port=0 protocol=ICMP aux0=300 aux1=1
2026-08-13T19:19:31.885760Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=552125276389521 event_kind=PacketDrop ifindex=2 src=192.168.100.1 dst=192.168.100.2 src_port=0 dst_port=0 protocol=ICMP aux0=0 aux1=0
2026-08-13T19:19:32.909459Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=552126300305529 event_kind=VlanDetected ifindex=2 src=192.168.100.1 dst=192.168.100.2 src_port=0 dst_port=0 protocol=ICMP aux0=300 aux1=1
2026-08-13T19:19:32.909704Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=552126300335362 event_kind=PacketDrop ifindex=2 src=192.168.100.1 dst=192.168.100.2 src_port=0 dst_port=0 protocol=ICMP aux0=0 aux1=0
root@orangepirv2:/home/orangepi/ebpf# 
root@orangepirv2:/home/orangepi/ebpf# ./target/debug/ebpf-ctl acl vlan add 100 drop inner 200

2026-08-13T19:21:11.197702Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=552224588524734 event_kind=VlanDetected ifindex=2 src=192.168.200.1 dst=192.168.200.2 src_port=0 dst_port=0 protocol=ICMP aux0=100 aux1=2
2026-08-13T19:21:11.198016Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=552224588554442 event_kind=PacketDrop ifindex=2 src=192.168.200.1 dst=192.168.200.2 src_port=0 dst_port=0 protocol=ICMP aux0=0 aux1=0
2026-08-13T19:21:12.209419Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=552225600272714 event_kind=VlanDetected ifindex=2 src=192.168.200.1 dst=192.168.200.2 src_port=0 dst_port=0 protocol=ICMP aux0=100 aux1=2
2026-08-13T19:21:12.209665Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=552225600299630 event_kind=PacketDrop ifindex=2 src=192.168.200.1 dst=192.168.200.2 src_port=0 dst_port=0 protocol=ICMP aux0=0 aux1=0
2026-08-13T19:21:13.233435Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=552226624286596 event_kind=VlanDetected ifindex=2 src=192.168.200.1 dst=192.168.200.2 src_port=0 dst_port=0 protocol=ICMP aux0=100 aux1=2
2026-08-13T19:21:13.233753Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=552226624313137 event_kind=PacketDrop ifindex=2 src=192.168.200.1 dst=192.168.200.2 src_port=0 dst_port=0 protocol=ICMP aux0=0 aux1=0
2026-08-13T19:21:14.257513Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=552227648361060 event_kind=VlanDetected ifindex=2 src=192.168.200.1 dst=192.168.200.2 src_port=0 dst_port=0 protocol=ICMP aux0=100 aux1=2
2026-08-13T19:21:14.257769Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=552227648388352 event_kind=PacketDrop ifindex=2 src=192.168.200.1 dst=192.168.200.2 src_port=0 dst_port=0 protocol=ICMP aux0=0 aux1=0
2026-08-13T19:21:15.277840Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=552228668675580 event_kind=VlanDetected ifindex=2 src=192.168.200.1 dst=192.168.200.2 src_port=0 dst_port=0 protocol=ICMP aux0=100 aux1=2
2026-08-13T19:21:15.278091Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=552228668710455 event_kind=PacketDrop ifindex=2 src=192.168.200.1 dst=192.168.200.2 src_port=0 dst_port=0 protocol=ICMP aux0=0 aux1=0
2026-08-13T19:21:16.301488Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=552229692338134 event_kind=VlanDetected ifindex=2 src=192.168.200.1 dst=192.168.200.2 src_port=0 dst_port=0 protocol=ICMP aux0=100 aux1=2
2026-08-13T19:21:16.301757Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=552229692367675 event_kind=PacketDrop ifindex=2 src=192.168.200.1 dst=192.168.200.2 src_port=0 dst_port=0 protocol=ICMP aux0=0 aux1=0
2026-08-13T19:21:17.325564Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=552230716399223 event_kind=VlanDetected ifindex=2 src=192.168.200.1 dst=192.168.200.2 src_port=0 dst_port=0 protocol=ICMP aux0=100 aux1=2
2026-08-13T19:21:17.325825Z  INFO ebpf_daemon::monitoring: eBPF event timestamp_ns=552230716434556 event_kind=PacketDrop ifindex=2 src=192.168.200.1 dst=192.168.200.2 src_port=0 dst_port=0 protocol=ICMP aux0=0 aux1=0

Проверяем rate лимит на vlan:

./target/debug/ebpf-ctl acl vlan add 100 allow inner 200 rate 1
Added VLAN rule: outer_vlan=100
added vlan rule (outer=100 inner=200 rate=1): allow

iperf3 -s -p 80 
-----------------------------------------------------------
Server listening on 80 (test #1)
-----------------------------------------------------------
Accepted connection from 192.168.200.3, port 39500
[  5] local 192.168.200.2 port 80 connected to 192.168.200.3 port 39508
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-1.00   sec  0.00 Bytes  0.00 bits/sec                  
[  5]   1.00-2.00   sec  0.00 Bytes  0.00 bits/sec                  
[  5]   2.00-3.00   sec  0.00 Bytes  0.00 bits/sec                  
[  5]   3.00-4.00   sec  0.00 Bytes  0.00 bits/sec                  
[  5]   4.00-5.00   sec  0.00 Bytes  0.00 bits/sec                  
[  5]   5.00-6.00   sec  0.00 Bytes  0.00 bits/sec                  
[  5]   6.00-7.00   sec  0.00 Bytes  0.00 bits/sec                  
[  5]   7.00-8.00   sec  0.00 Bytes  0.00 bits/sec                  
[  5]   8.00-9.00   sec  0.00 Bytes  0.00 bits/sec                  
[  5]   9.00-10.00  sec  0.00 Bytes  0.00 bits/sec                  
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-10.00  sec  0.00 Bytes  0.00 bits/sec                  receiver
-----------------------------------------------------------
Server listening on 80 (test #2)
-----------------------------------------------------------

6.проверяем применение правил из конфигурационного файла:

cat ./config2.yml 
acl:
  allow:
    - vlan:
        - 300
        - outer: 200
          inner:
            - 100
            - 101
      rate: 1
    - src:
        addresses:
          - 192.168.0.2/32
          - 192.168.100.0/24
      dst:
        addresses: 192.168.0.202
        ports:
          number: 80
          proto: tcp
      rate: 1

  drop:
    - vlan:
        - 300
        - outer: 130
          inner:
            - 100
    - src:
        addresses:
          - fe80::/64
      dst:
        addresses:
          - fe80::c274:2bff:fefa:720c
        ports:
          number: any
          proto: icmp
      rate: 1
    - src:
        addresses:
          - 10.0.0.0/8
          - 192.168.200.1/24
      dst:
        addresses:
          - 192.168.0.2
        ports:
          number: any
          proto: icmp
      rate: 1

logging:
  PacketDrop: true
  RateLimited: false
  ConntrackMiss: false
  BackendSelected: false
  SlowPath: false
  ServiceMatched: true
  VlanDetected: true
  PacketAllow: false

И так читается уже самим Pulsar:

cat ./test.log 
2026-08-13T20:11:00.641182Z  INFO ebpf_daemon::manager::maps: rate_limit: global max_tokens=65536, refill_per_sec=64000, per_rule_multiplier=256
2026-08-13T20:11:00.641442Z  INFO ebpf_daemon::manager::maps: monitoring: ringbuf_bytes=16777216, poll_interval_ms=100
2026-08-13T20:11:00.641509Z  INFO ebpf_daemon::manager::maps: acl.default_action: allow
2026-08-13T20:11:00.760323Z  INFO ebpf_daemon::monitoring: Trying XDP program id: 245
2026-08-13T20:11:00.814464Z  INFO ebpf_daemon::monitoring: bpftool prog show id 245: 245: xdp  name xdp_dataplane  tag fa484001870657d5
	loaded_at 2026-08-13T23:09:05+0300  uid 0
	xlated 19776B  jited 9000B  memlock 20480B  map_ids 483,484,485,487,493,492
	btf_id 360
2026-08-13T20:11:00.868271Z  WARN ebpf_daemon::monitoring: Skipping map id 483: Map id 483 is not events ringbuf (name=counters, type=percpu_array)
2026-08-13T20:11:00.922074Z  WARN ebpf_daemon::monitoring: Skipping map id 484: Map id 484 is not events ringbuf (name=event_mask, type=array)
2026-08-13T20:11:00.975972Z  INFO ebpf_daemon::monitoring: Opened events map
2026-08-13T20:11:00.976150Z  INFO ebpf_daemon::monitoring: Events ringbuf size: 16777216 bytes
2026-08-13T20:11:00.983324Z  INFO ebpf_daemon::monitoring: Starting ringbuf event loop
root@orangepirv2:/home/orangepi/ebpf# ./target/debug/ebpf-ctl acl list
id  plen  fam  src                                dst                                proto sport  dport  rate  action
0   304   4    10.0.0.0                           192.168.0.2                        icmp any    any    -     Drop
1   320   4    192.168.0.2                        192.168.0.202                      tcp  any    80     1     Allow
2   320   4    192.168.100.0                      192.168.0.202                      tcp  any    80     1     Allow
3   304   4    192.168.200.0                      192.168.0.2                        icmp any    any    -     Drop
4   304   6    fe80:0:0:0:0:0:0:0                 fe80:0:0:0:c274:2bff:fefa:720c     icmp any    any    -     Drop
root@orangepirv2:/home/orangepi/ebpf# ./target/debug/ebpf-ctl acl vlan list
id    outer_vlan inner_vlan action   rate  
0     100        101        allow    1     
1     130        100        drop     0     
2     100        200        allow    1     
3     250        -          drop     0     
4     300        -          allow    1 
root@orangepirv2:/home/orangepi/ebpf# ./target/debug/ebpf-ctl event-log list
event-log mask: 0xc2
enabled events:
  [x] PacketDrop
  [ ] RateLimited
  [ ] ConntrackMiss
  [ ] BackendSelected
  [ ] SlowPath
  [x] ServiceMatched
  [x] VlanDetected
  [ ] PacketAllow
root@orangepirv2:/home/orangepi/ebpf#

7.Над чем мне пока предстоит поработать :

Ограничения текущей реализации На текущем этапе не завершена реализация событий и функций, связанных с stateful processing:

  • ConntrackMiss;

  • BackendSelected;

  • SlowPath;

  • ServiceMatched.

Данный функционал связан с состоянием соединения, выбором backend и обработкой сервисного трафика. Его реализация планируется на уровне Traffic Control, где будет выполняться stateful processing, включая conntrack, Load Balancing и NAT. На текущем этапе перечисленные события используются как заглушки и не отражают завершённую реализацию соответствующей логики.

Преокт выложен под открытой лицензией : https://github.com/AlexRoot00/Pulsar/tree/master