From ab060930e992eb13da1a1da702fe6d6b2e2b2b90 Mon Sep 17 00:00:00 2001 From: saushew Date: Wed, 20 Sep 2023 19:51:40 +0300 Subject: [PATCH] write packets according to a certain "dump" rule into IPC Shared Memory --- autotests/src/autotest.cpp | 200 +++++++++++++++- autotests/src/autotest.h | 10 + .../001-expect-dump-ring1.pcap | Bin 0 -> 2816 bytes .../067_dump_after_term/001-expect.pcap | Bin 0 -> 98 bytes .../067_dump_after_term/001-send.pcap | Bin 0 -> 2890 bytes .../067_dump_after_term/autotest.yaml | 10 + .../067_dump_after_term/controlplane.conf | 42 ++++ .../067_dump_after_term/firewall.txt | 11 + .../001_one_port/067_dump_after_term/gen.py | 39 ++++ .../001-expect-dump-ring1.pcap | Bin 0 -> 98 bytes .../067_dump_default/001-expect.pcap | Bin 0 -> 98 bytes .../067_dump_default/001-send.pcap | Bin 0 -> 2890 bytes .../067_dump_default/autotest.yaml | 10 + .../067_dump_default/controlplane.conf | 42 ++++ .../067_dump_default/firewall.txt | 11 + .../001_one_port/067_dump_default/gen.py | 38 +++ .../001-expect-dump-ring1.pcap | Bin 0 -> 2890 bytes .../067_dump_with_deny/001-expect.pcap | Bin 0 -> 98 bytes .../067_dump_with_deny/001-send.pcap | Bin 0 -> 2890 bytes .../067_dump_with_deny/autotest.yaml | 10 + .../067_dump_with_deny/controlplane.conf | 42 ++++ .../067_dump_with_deny/firewall.txt | 11 + .../001_one_port/067_dump_with_deny/gen.py | 40 ++++ .../001-expect-dump-ring1.pcap | Bin 0 -> 2890 bytes .../001-expect-dump-ring2.pcap | Bin 0 -> 98 bytes .../001-expect.pcap | Bin 0 -> 98 bytes .../067_intersecting_dump_rules/001-send.pcap | Bin 0 -> 2890 bytes .../067_intersecting_dump_rules/autotest.yaml | 12 + .../controlplane.conf | 42 ++++ .../067_intersecting_dump_rules/firewall.txt | 12 + .../067_intersecting_dump_rules/gen.py | 45 ++++ autotests/units/001_one_port/dataplane.conf | 14 +- cli/src/main.cpp | 2 + cli/src/show.h | 23 ++ common/acl.h | 57 ++++- common/bufferring.h | 63 +++++ common/config.release.h | 4 + common/idataplane.h | 5 + common/idp.h | 29 ++- common/result.h | 3 + common/stream.h | 8 +- controlplane/src/acl.cpp | 89 +++++--- controlplane/src/acl.h | 3 + controlplane/src/acl/rule.h | 31 ++- controlplane/src/acl_compiler.cpp | 18 +- controlplane/src/acl_rule.h | 1 + controlplane/src/acl_total_table.cpp | 38 ++- controlplane/src/acl_value.cpp | 42 +++- controlplane/src/acl_value.h | 3 +- controlplane/src/base.h | 1 + controlplane/src/configconverter.cpp | 2 + dataplane/meson.build | 1 + dataplane/src/bus.cpp | 4 + dataplane/src/controlplane.cpp | 11 + dataplane/src/controlplane.h | 1 + dataplane/src/dataplane.cpp | 216 ++++++++++++++++++ dataplane/src/dataplane.h | 11 + dataplane/src/globalbase.cpp | 20 ++ dataplane/src/globalbase.h | 3 + dataplane/src/sharedmemory.cpp | 40 ++++ dataplane/src/sharedmemory.h | 18 ++ dataplane/src/worker.cpp | 64 ++++++ dataplane/src/worker.h | 3 + libfwparser/fw_config.cpp | 14 ++ libfwparser/fw_config.h | 2 + libfwparser/fw_parser.y | 18 +- libfwparser/token.l | 1 + 67 files changed, 1430 insertions(+), 60 deletions(-) create mode 100644 autotests/units/001_one_port/067_dump_after_term/001-expect-dump-ring1.pcap create mode 100644 autotests/units/001_one_port/067_dump_after_term/001-expect.pcap create mode 100644 autotests/units/001_one_port/067_dump_after_term/001-send.pcap create mode 100644 autotests/units/001_one_port/067_dump_after_term/autotest.yaml create mode 100644 autotests/units/001_one_port/067_dump_after_term/controlplane.conf create mode 100644 autotests/units/001_one_port/067_dump_after_term/firewall.txt create mode 100644 autotests/units/001_one_port/067_dump_after_term/gen.py create mode 100644 autotests/units/001_one_port/067_dump_default/001-expect-dump-ring1.pcap create mode 100644 autotests/units/001_one_port/067_dump_default/001-expect.pcap create mode 100644 autotests/units/001_one_port/067_dump_default/001-send.pcap create mode 100644 autotests/units/001_one_port/067_dump_default/autotest.yaml create mode 100644 autotests/units/001_one_port/067_dump_default/controlplane.conf create mode 100644 autotests/units/001_one_port/067_dump_default/firewall.txt create mode 100644 autotests/units/001_one_port/067_dump_default/gen.py create mode 100644 autotests/units/001_one_port/067_dump_with_deny/001-expect-dump-ring1.pcap create mode 100644 autotests/units/001_one_port/067_dump_with_deny/001-expect.pcap create mode 100644 autotests/units/001_one_port/067_dump_with_deny/001-send.pcap create mode 100644 autotests/units/001_one_port/067_dump_with_deny/autotest.yaml create mode 100644 autotests/units/001_one_port/067_dump_with_deny/controlplane.conf create mode 100644 autotests/units/001_one_port/067_dump_with_deny/firewall.txt create mode 100644 autotests/units/001_one_port/067_dump_with_deny/gen.py create mode 100644 autotests/units/001_one_port/067_intersecting_dump_rules/001-expect-dump-ring1.pcap create mode 100644 autotests/units/001_one_port/067_intersecting_dump_rules/001-expect-dump-ring2.pcap create mode 100644 autotests/units/001_one_port/067_intersecting_dump_rules/001-expect.pcap create mode 100644 autotests/units/001_one_port/067_intersecting_dump_rules/001-send.pcap create mode 100644 autotests/units/001_one_port/067_intersecting_dump_rules/autotest.yaml create mode 100644 autotests/units/001_one_port/067_intersecting_dump_rules/controlplane.conf create mode 100644 autotests/units/001_one_port/067_intersecting_dump_rules/firewall.txt create mode 100644 autotests/units/001_one_port/067_intersecting_dump_rules/gen.py create mode 100644 common/bufferring.h create mode 100644 dataplane/src/sharedmemory.cpp create mode 100644 dataplane/src/sharedmemory.h diff --git a/autotests/src/autotest.cpp b/autotests/src/autotest.cpp index 14a4b8fe..ab45d85a 100644 --- a/autotests/src/autotest.cpp +++ b/autotests/src/autotest.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -65,12 +66,24 @@ eResult tAutotest::init(const std::string& binaryPath, { (void)binaryPath; this->dumpPackets = dumpPackets; + this->configFilePaths = configFilePaths; - eResult result = eResult::success; + if (auto ret = initSockets(); ret != eResult::success) + { + return ret; + } - this->configFilePaths = configFilePaths; + if (auto ret = initSharedMemory(); ret != eResult::success) + { + return ret; + } - dataPlaneConfig = dataPlane.getConfig(); + return eResult::success; +} + +eResult tAutotest::initSockets() +{ + dataPlaneConfig = dataPlane.getConfig(); for (const auto& port : std::get<0>(dataPlaneConfig)) { @@ -100,7 +113,65 @@ eResult tAutotest::init(const std::string& binaryPath, pcaps[interfaceName] = fd; } - return result; + return eResult::success; +} + +eResult tAutotest::initSharedMemory() +{ + dataPlaneSharedMemory = dataPlane.get_shm_info(); + + std::map shm_by_key; + key_t ipcKey; + int shmid; + void* shmaddr; + + for (const auto& shmInfo : dataPlaneSharedMemory) + { + ipcKey = std::get<6>(shmInfo); + + shmid = shmget(ipcKey, 0, 0); + if (shmid == -1) { + YANET_LOG_ERROR("shmget(%d, 0, 0) = %d\n", ipcKey, errno); + return eResult::errorInitSharedMemory; + } + + shmaddr = shmat(shmid, NULL, 0); + if (shmaddr == (void*) -1) { + YANET_LOG_ERROR("shmat(%d, NULL, 0) = %d\n", shmid, errno); + return eResult::errorInitSharedMemory; + } + + if (auto it = shm_by_key.find(ipcKey); it == shm_by_key.end()) + { + shm_by_key[ipcKey] = shmaddr; + } + } + + if (shm_by_key.size() > 0) + { + struct shmid_ds shm_info; + if (shmctl(shmid, IPC_STAT, &shm_info) == -1) { + YANET_LOG_ERROR("shmctl(%d, IPC_STAT, &shm_info) = %d\n", shmid, errno); + return eResult::errorInitSharedMemory; + } + + rawShmInfo = {shm_info.shm_segsz, shmaddr}; + } + + for (const auto& shmInfo : dataPlaneSharedMemory) + { + std::string tag = std::get<1>(shmInfo); + unsigned int unitSize = std::get<2>(shmInfo); + unsigned int unitsNumber = std::get<3>(shmInfo); + key_t ipcKey = std::get<6>(shmInfo); + uint64_t offset = std::get<7>(shmInfo); + + void* shm = shm_by_key[ipcKey]; + auto memaddr = (void*)((intptr_t)shm + offset); + dumpRings[tag] = common::bufferring(memaddr, unitSize, unitsNumber); + } + + return eResult::success; } void tAutotest::start() @@ -1188,6 +1259,8 @@ void tAutotest::mainThread() fflush(stdout); fflush(stderr); + fflushSharedMemory(); + try { { @@ -1341,6 +1414,12 @@ void tAutotest::mainThread() result = step_echo(yamlStep["echo"]); } + else if (yamlStep["dumpPackets"]) + { + YANET_LOG_DEBUG("step: dumpPackets\n"); + + result = step_dumpPackets(yamlStep["dumpPackets"], configFilePath); + } else { YANET_LOG_ERROR("unknown step\n"); @@ -1750,3 +1829,116 @@ bool tAutotest::step_cli_check(const YAML::Node& yamlStep) return true; } + +common::bufferring::item_t* read_shm_packet(common::bufferring* buffer, uint64_t position) +{ + if (position >= buffer->ring->header.after) + { + return nullptr; + } + common::bufferring::item_t* item = (common::bufferring::item_t*)((uintptr_t)buffer->ring->memory + (position * buffer->unit_size)); + return item; +} + +bool tAutotest::step_dumpPackets(const YAML::Node& yamlStep, + const std::string& path) +{ + TextDumper dumper; + for (const auto& yamlDump : yamlStep) + { + std::string tag = yamlDump["ringTag"].as(); + std::string expectFilePath = path + "/" + yamlDump["expect"].as(); + bool success = true; + + common::bufferring* ring; + { /// searching memory ring by tag + auto it = dumpRings.find(tag); + if (it == dumpRings.end()) + { + YANET_LOG_ERROR("dump [%s]: error: dump ring not found\n", tag.data()); + throw ""; + } + ring = &it->second; + } + + pcap_t* pcap; + { /// open pcap file with expected data + char pcap_errbuf[PCAP_ERRBUF_SIZE]; + pcap = pcap_open_offline(expectFilePath.data(), pcap_errbuf); + if (!pcap) + { + YANET_LOG_ERROR("dump [%s]: error: pcap_open_offline(): %s\n", tag.data(), pcap_errbuf); + throw ""; + } + } + + struct pcap_pkthdr header; + const u_char* pcap_packet; + common::bufferring::item_t* shm_packet; + uint64_t position = 0; + + /// read packets from pcap and compare them with packets from memory ring + while ((pcap_packet = pcap_next(pcap, &header))) + { + shm_packet = read_shm_packet(ring, position); + position++; + + if (shm_packet && header.len == shm_packet->header.size && + memcmp(shm_packet->memory, pcap_packet, header.len) == 0) + { /// packets are the same + continue; + } + + /// packets are different, so... + success = false; + YANET_LOG_ERROR("dump [%s]: error: wrong packet #%lu (%s)\n", + tag.data(), + position, + expectFilePath.data()); + + if (dumpPackets && shm_packet) + { + YANET_LOG_DEBUG("dump [%s]: expected %u, got %u\n", tag.data(), header.len, shm_packet->header.size); + dumper.dump(pcap_packet, pcap_packet + shm_packet->header.size, shm_packet->memory, shm_packet->memory + header.len); + } + } + + /// read the remaining packets from memory ring + for (;;) + { + shm_packet = read_shm_packet(ring, position); + if (!shm_packet) + { + break; + } + position++; + + success = false; + + if (dumpPackets) + { + YANET_LOG_DEBUG("dump [%s]: unexpected %u\n", tag.data(), shm_packet->header.size); + dumper.dump(NULL, NULL, shm_packet->memory, shm_packet->memory + header.len); + } + } + + YANET_LOG_DEBUG("dump [%s]: recv %lu packets\n", tag.data(), position); + + pcap_close(pcap); + + if (!success) + { + YANET_LOG_ERROR("dump [%s]: error: unknown packet (%s)\n", tag.data(), expectFilePath.data()); + throw ""; + } + } + + return true; +} + +void tAutotest::fflushSharedMemory() +{ + size_t size = std::get<0>(rawShmInfo); + void* memaddr = std::get<1>(rawShmInfo); + memset(memaddr, 0, size); +} diff --git a/autotests/src/autotest.h b/autotests/src/autotest.h index 71821d25..d3bf61b4 100644 --- a/autotests/src/autotest.h +++ b/autotests/src/autotest.h @@ -10,6 +10,7 @@ #include "common/result.h" #include "common/idataplane.h" #include "common/icontrolplane.h" +#include "common/bufferring.h" using ipv4_address_t = common::ipv4_address_t; using ipv6_address_t = common::ipv6_address_t; @@ -60,6 +61,11 @@ class tAutotest bool step_cli_check(const YAML::Node& yamlStep); bool step_reload_async(const YAML::Node& yamlStep); bool step_echo(const YAML::Node& yamlStep); + bool step_dumpPackets(const YAML::Node& yamlStep, const std::string& path); + + eResult initSockets(); + eResult initSharedMemory(); + void fflushSharedMemory(); bool step_memorize_counter_value(const YAML::Node& yamlStep); bool step_diff_with_kept_counter_value(const YAML::Node& yamlStep); @@ -85,11 +91,15 @@ class tAutotest interface::controlPlane controlPlane; common::idp::getConfig::response dataPlaneConfig; + common::idp::get_shm_info::response dataPlaneSharedMemory; std::map pcaps; + std::tuple rawShmInfo; + std::map dumpRings; + std::vector threads; volatile bool flagStop; diff --git a/autotests/units/001_one_port/067_dump_after_term/001-expect-dump-ring1.pcap b/autotests/units/001_one_port/067_dump_after_term/001-expect-dump-ring1.pcap new file mode 100644 index 0000000000000000000000000000000000000000..1c1e74b177dd156dd45be57c40e70ff89042fec5 GIT binary patch literal 2816 zcmca|c+)~A1{MYw`2U}Qff2~rqsX0lP?n3~9#9H|83dJ#T|z;$pkN~dLkb6jD+9|J z21W%22SE=hE}$kxCT13(LQ^h|nW>IW&MvNQ?jD9l#wLz1Fe>Ia8ZN-ZFq#fV!vIt! z45D;EbZIx;LD0P!R@x0FAB?(XaHXix_!x~3P$4%M(*dls+XrmUfiSkV9m5)6i%!o$ sFq;Qf+6~sW9LP7fmADx6fF1#1tiF)}HEJCMb56m018S){a~0{|JK4T1mw literal 0 HcmV?d00001 diff --git a/autotests/units/001_one_port/067_dump_after_term/001-send.pcap b/autotests/units/001_one_port/067_dump_after_term/001-send.pcap new file mode 100644 index 0000000000000000000000000000000000000000..e5e96fb7af52fe331fc4fbdcec892a5818cbb2e7 GIT binary patch literal 2890 zcmca|c+)~A1{MYw`2U}Qff2~rqsX0F#>K^O4=4q~41!9=E}S&R?E^OFKp0!wj$sY3MW^Q= zn9Tz#?FMUG4&)m>K`sV8phrL$t8ZjLjamo6oKrC0fLf{y9B&tabQcJ5F<1dLf-qLy Y8erXQIVVwcgH!`S0Fwek?Q#YN0Kx^SP5=M^ literal 0 HcmV?d00001 diff --git a/autotests/units/001_one_port/067_dump_after_term/autotest.yaml b/autotests/units/001_one_port/067_dump_after_term/autotest.yaml new file mode 100644 index 00000000..e6eb3fca --- /dev/null +++ b/autotests/units/001_one_port/067_dump_after_term/autotest.yaml @@ -0,0 +1,10 @@ +steps: +- ipv4Update: "0.0.0.0/0 -> 200.0.0.1" +- ipv6Update: "::/0 -> fe80::1" +- sendPackets: + - port: kni0 + send: 001-send.pcap + expect: 001-expect.pcap +- dumpPackets: + - ringTag: ring1 + expect: 001-expect-dump-ring1.pcap diff --git a/autotests/units/001_one_port/067_dump_after_term/controlplane.conf b/autotests/units/001_one_port/067_dump_after_term/controlplane.conf new file mode 100644 index 00000000..3c0da4fd --- /dev/null +++ b/autotests/units/001_one_port/067_dump_after_term/controlplane.conf @@ -0,0 +1,42 @@ +{ + "modules": { + "lp0.100": { + "type": "logicalPort", + "physicalPort": "kni0", + "vlanId": "100", + "macAddress": "00:11:22:33:44:55", + "nextModule": "acl0" + }, + "lp0.200": { + "type": "logicalPort", + "physicalPort": "kni0", + "vlanId": "200", + "macAddress": "00:11:22:33:44:55", + "nextModule": "acl0" + }, + "acl0": { + "type": "acl", + "firewall": "firewall.txt", + "nextModules": [ + "vrf0" + ] + }, + "vrf0": { + "type": "route", + "interfaces": { + "kni0.100": { + "ipv6Prefix": "fe80::2/64", + "neighborIPv6Address": "fe80::1", + "neighborMacAddress": "00:00:00:11:11:11", + "nextModule": "lp0.100" + }, + "kni0.200": { + "ipv4Prefix": "200.0.0.2/24", + "neighborIPv4Address": "200.0.0.1", + "neighborMacAddress": "00:00:00:22:22:22", + "nextModule": "lp0.200" + } + } + } + } +} diff --git a/autotests/units/001_one_port/067_dump_after_term/firewall.txt b/autotests/units/001_one_port/067_dump_after_term/firewall.txt new file mode 100644 index 00000000..a55a9ab8 --- /dev/null +++ b/autotests/units/001_one_port/067_dump_after_term/firewall.txt @@ -0,0 +1,11 @@ +:BEGIN +add skipto :IN ip from any to any in + +:IN +add allow tcp from 10.0.0.0/24 to 1.2.3.4 53 +add dump ring1 ip from any to any +add deny ip from any to any + +add allow udp from 10.0.0.0/24 to 1.2.3.4 53 +add allow ip from any to any frag + diff --git a/autotests/units/001_one_port/067_dump_after_term/gen.py b/autotests/units/001_one_port/067_dump_after_term/gen.py new file mode 100644 index 00000000..7085b0f9 --- /dev/null +++ b/autotests/units/001_one_port/067_dump_after_term/gen.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +from typing import List + +from scapy.layers.inet import UDP, TCP, IP, fragment +from scapy.layers.inet6 import IPv6 +from scapy.layers.l2 import Ether, Dot1Q +from scapy.packet import Packet +from scapy.utils import PcapWriter + + +def write_pcap(path: str, packets: List[Packet]) -> None: + with PcapWriter(path) as fh: + for p in packets: + fh.write(p) + + +def ipv4_send(src: str, dst: str) -> Packet: + return Ether(dst="00:11:22:33:44:55", src="00:00:00:11:11:11") / Dot1Q(vlan=100) / IP(src=src, dst=dst, ttl=64) + + +def ipv4_recv(src: str, dst: str) -> Packet: + return Ether(dst="00:00:00:22:22:22", src="00:11:22:33:44:55") / Dot1Q(vlan=200) / IP(src=src, dst=dst, ttl=63) + + +write_pcap("001-send.pcap", [ + fragment(ipv4_send("10.0.0.1", "1.2.3.4") / UDP(sport=1024, dport=53)/("ABCDEFGH1234AAAAAAAA"*128), fragsize=1208), + ipv4_send("10.0.0.1", "1.2.3.4") / UDP(sport=1024, dport=53), + ipv4_send("10.0.0.1", "1.2.3.4") / TCP(sport=1024, dport=53), +]) + +write_pcap("001-expect.pcap", [ + ipv4_recv("10.0.0.1", "1.2.3.4") / TCP(sport=1024, dport=53), +]) + +write_pcap("001-expect-dump-ring1.pcap", [ + fragment(ipv4_send("10.0.0.1", "1.2.3.4") / UDP(sport=1024, dport=53)/("ABCDEFGH1234AAAAAAAA"*128), fragsize=1208), + ipv4_send("10.0.0.1", "1.2.3.4") / UDP(sport=1024, dport=53), +]) diff --git a/autotests/units/001_one_port/067_dump_default/001-expect-dump-ring1.pcap b/autotests/units/001_one_port/067_dump_default/001-expect-dump-ring1.pcap new file mode 100644 index 0000000000000000000000000000000000000000..87c0dafe0b22542d29ca5c7924ae9a63d2155852 GIT binary patch literal 98 zcmca|c+)~A1{MYw`2U}Qff2}AugIO6(aFPL1!RLTgP@YJODKpI6l`Q*Na0{`Wnj<% gDRp4WImrdo#K^?V0yM`Iq!?LjaQkL+x?~1^{EF4wC=? literal 0 HcmV?d00001 diff --git a/autotests/units/001_one_port/067_dump_default/001-send.pcap b/autotests/units/001_one_port/067_dump_default/001-send.pcap new file mode 100644 index 0000000000000000000000000000000000000000..ce55c9cc77ae095f4a9b08754115bdfc9c59338c GIT binary patch literal 2890 zcmca|c+)~A1{MYw`2U}Qff2}AugINhS;oU~4=4q~41!9=E}S&R?E^OFKp0!wj$sY3MW^Q= zn9Tz#?FMUG4&aINmM->9(!oVXy*f1YxYY YHNd*ra!#V?2B`*u044>7+T{!k00_>j6951J literal 0 HcmV?d00001 diff --git a/autotests/units/001_one_port/067_dump_default/autotest.yaml b/autotests/units/001_one_port/067_dump_default/autotest.yaml new file mode 100644 index 00000000..e6eb3fca --- /dev/null +++ b/autotests/units/001_one_port/067_dump_default/autotest.yaml @@ -0,0 +1,10 @@ +steps: +- ipv4Update: "0.0.0.0/0 -> 200.0.0.1" +- ipv6Update: "::/0 -> fe80::1" +- sendPackets: + - port: kni0 + send: 001-send.pcap + expect: 001-expect.pcap +- dumpPackets: + - ringTag: ring1 + expect: 001-expect-dump-ring1.pcap diff --git a/autotests/units/001_one_port/067_dump_default/controlplane.conf b/autotests/units/001_one_port/067_dump_default/controlplane.conf new file mode 100644 index 00000000..3c0da4fd --- /dev/null +++ b/autotests/units/001_one_port/067_dump_default/controlplane.conf @@ -0,0 +1,42 @@ +{ + "modules": { + "lp0.100": { + "type": "logicalPort", + "physicalPort": "kni0", + "vlanId": "100", + "macAddress": "00:11:22:33:44:55", + "nextModule": "acl0" + }, + "lp0.200": { + "type": "logicalPort", + "physicalPort": "kni0", + "vlanId": "200", + "macAddress": "00:11:22:33:44:55", + "nextModule": "acl0" + }, + "acl0": { + "type": "acl", + "firewall": "firewall.txt", + "nextModules": [ + "vrf0" + ] + }, + "vrf0": { + "type": "route", + "interfaces": { + "kni0.100": { + "ipv6Prefix": "fe80::2/64", + "neighborIPv6Address": "fe80::1", + "neighborMacAddress": "00:00:00:11:11:11", + "nextModule": "lp0.100" + }, + "kni0.200": { + "ipv4Prefix": "200.0.0.2/24", + "neighborIPv4Address": "200.0.0.1", + "neighborMacAddress": "00:00:00:22:22:22", + "nextModule": "lp0.200" + } + } + } + } +} diff --git a/autotests/units/001_one_port/067_dump_default/firewall.txt b/autotests/units/001_one_port/067_dump_default/firewall.txt new file mode 100644 index 00000000..49095043 --- /dev/null +++ b/autotests/units/001_one_port/067_dump_default/firewall.txt @@ -0,0 +1,11 @@ +:BEGIN +add skipto :IN ip from any to any in + +:IN +add dump ring1 tcp from 10.0.0.0/24 to 1.2.3.4 53 +add allow tcp from 10.0.0.0/24 to 1.2.3.4 53 +add deny ip from any to any + +add allow udp from 10.0.0.0/24 to 1.2.3.4 53 +add allow ip from any to any frag + diff --git a/autotests/units/001_one_port/067_dump_default/gen.py b/autotests/units/001_one_port/067_dump_default/gen.py new file mode 100644 index 00000000..7afd4196 --- /dev/null +++ b/autotests/units/001_one_port/067_dump_default/gen.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +from typing import List + +from scapy.layers.inet import UDP, TCP, IP, fragment +from scapy.layers.inet6 import IPv6 +from scapy.layers.l2 import Ether, Dot1Q +from scapy.packet import Packet +from scapy.utils import PcapWriter + + +def write_pcap(path: str, packets: List[Packet]) -> None: + with PcapWriter(path) as fh: + for p in packets: + fh.write(p) + + +def ipv4_send(src: str, dst: str) -> Packet: + return Ether(dst="00:11:22:33:44:55", src="00:00:00:11:11:11") / Dot1Q(vlan=100) / IP(src=src, dst=dst, ttl=64) + + +def ipv4_recv(src: str, dst: str) -> Packet: + return Ether(dst="00:00:00:22:22:22", src="00:11:22:33:44:55") / Dot1Q(vlan=200) / IP(src=src, dst=dst, ttl=63) + + +write_pcap("001-send.pcap", [ + fragment(ipv4_send("10.0.0.1", "1.2.3.4") / UDP(sport=1024, dport=53)/("ABCDEFGH1234AAAAAAAA"*128), fragsize=1208), + ipv4_send("10.0.0.1", "1.2.3.4") / UDP(sport=1024, dport=53), + ipv4_send("10.0.0.1", "1.2.3.4") / TCP(sport=1024, dport=53), +]) + +write_pcap("001-expect.pcap", [ + ipv4_recv("10.0.0.1", "1.2.3.4") / TCP(sport=1024, dport=53), +]) + +write_pcap("001-expect-dump-ring1.pcap", [ + ipv4_send("10.0.0.1", "1.2.3.4") / TCP(sport=1024, dport=53), +]) diff --git a/autotests/units/001_one_port/067_dump_with_deny/001-expect-dump-ring1.pcap b/autotests/units/001_one_port/067_dump_with_deny/001-expect-dump-ring1.pcap new file mode 100644 index 0000000000000000000000000000000000000000..94863c4f6f3efcfe2e702e9ac802b6e415b124f1 GIT binary patch literal 2890 zcmca|c+)~A1{MYw`2U}Qff2|#tH_;taWfCYJ)jf_GYBdfyM%&hLBU1_h7=A4R|b|d z42%j44uT$1TtH2XOw24mg{E8_GgBR%oLyYq+&v79j7=P2U{uU;G+cm*VKg0#h5@Kd z7)0rS=+bVwgP?meth5_UJ{Wb&;7U=W@i7`7ph9jirUO`Mw-4By17U1!JBBsD7M-4h zU^Wk|v>U8#IgoD-?BHS019}96vHC^^)Tnh3%sB<~4XCBc!0~nwNcX3mJPcMqjUbFw Zw+2`@Th2)o-5}LK5Wu9sP`jLg0RS={uNwdW literal 0 HcmV?d00001 diff --git a/autotests/units/001_one_port/067_dump_with_deny/001-expect.pcap b/autotests/units/001_one_port/067_dump_with_deny/001-expect.pcap new file mode 100644 index 0000000000000000000000000000000000000000..f40c2e46366cbd86de081ca795d725a87e9863e0 GIT binary patch literal 98 zcmca|c+)~A1{MYw`2U}Qff2|#tH_-ivVn)e3djawAW%|LVh~g^b_s1{U^v0S;L5?LjaQkL+x?~1^`n24pjgE literal 0 HcmV?d00001 diff --git a/autotests/units/001_one_port/067_dump_with_deny/001-send.pcap b/autotests/units/001_one_port/067_dump_with_deny/001-send.pcap new file mode 100644 index 0000000000000000000000000000000000000000..fc747e77ca6dec839c744d92a9569860c8137e44 GIT binary patch literal 2890 zcmca|c+)~A1{MYw`2U}Qff2|#tH_=Dd;t%`J)jf_GYBdfyM%&hLBU1_h7=A4R|b|d z42%j44uT$1TtH2XOw24mg{E8_GgBR%oLyYq+&v79j7=P2U{uU;G+cm*VKg0#h5@Kd z7)0rS=+bVwgP?meth5_UJ{Wb&;7U=W@i7`7ph9jirUO`Mw-4By17U1!JBBsD7M-4h zU^Wk|v>U8#IgoGGF6Uv;19}96vHC^^)Tnh3%sB<~4XCBc!0~nwNcXQ5JPcMqjUbFw Zw+2`@Th2)o-5}LK5Wu9sP`jLg0RUCAuJ-@{ literal 0 HcmV?d00001 diff --git a/autotests/units/001_one_port/067_dump_with_deny/autotest.yaml b/autotests/units/001_one_port/067_dump_with_deny/autotest.yaml new file mode 100644 index 00000000..e6eb3fca --- /dev/null +++ b/autotests/units/001_one_port/067_dump_with_deny/autotest.yaml @@ -0,0 +1,10 @@ +steps: +- ipv4Update: "0.0.0.0/0 -> 200.0.0.1" +- ipv6Update: "::/0 -> fe80::1" +- sendPackets: + - port: kni0 + send: 001-send.pcap + expect: 001-expect.pcap +- dumpPackets: + - ringTag: ring1 + expect: 001-expect-dump-ring1.pcap diff --git a/autotests/units/001_one_port/067_dump_with_deny/controlplane.conf b/autotests/units/001_one_port/067_dump_with_deny/controlplane.conf new file mode 100644 index 00000000..3c0da4fd --- /dev/null +++ b/autotests/units/001_one_port/067_dump_with_deny/controlplane.conf @@ -0,0 +1,42 @@ +{ + "modules": { + "lp0.100": { + "type": "logicalPort", + "physicalPort": "kni0", + "vlanId": "100", + "macAddress": "00:11:22:33:44:55", + "nextModule": "acl0" + }, + "lp0.200": { + "type": "logicalPort", + "physicalPort": "kni0", + "vlanId": "200", + "macAddress": "00:11:22:33:44:55", + "nextModule": "acl0" + }, + "acl0": { + "type": "acl", + "firewall": "firewall.txt", + "nextModules": [ + "vrf0" + ] + }, + "vrf0": { + "type": "route", + "interfaces": { + "kni0.100": { + "ipv6Prefix": "fe80::2/64", + "neighborIPv6Address": "fe80::1", + "neighborMacAddress": "00:00:00:11:11:11", + "nextModule": "lp0.100" + }, + "kni0.200": { + "ipv4Prefix": "200.0.0.2/24", + "neighborIPv4Address": "200.0.0.1", + "neighborMacAddress": "00:00:00:22:22:22", + "nextModule": "lp0.200" + } + } + } + } +} diff --git a/autotests/units/001_one_port/067_dump_with_deny/firewall.txt b/autotests/units/001_one_port/067_dump_with_deny/firewall.txt new file mode 100644 index 00000000..d94c910f --- /dev/null +++ b/autotests/units/001_one_port/067_dump_with_deny/firewall.txt @@ -0,0 +1,11 @@ +:BEGIN +add skipto :IN ip from any to any in + +:IN +add dump ring1 ip from any to any +add allow tcp from 10.0.0.0/24 to 1.2.3.4 53 +add deny ip from any to any + +add allow udp from 10.0.0.0/24 to 1.2.3.4 53 +add allow ip from any to any frag + diff --git a/autotests/units/001_one_port/067_dump_with_deny/gen.py b/autotests/units/001_one_port/067_dump_with_deny/gen.py new file mode 100644 index 00000000..a53995ba --- /dev/null +++ b/autotests/units/001_one_port/067_dump_with_deny/gen.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +from typing import List + +from scapy.layers.inet import UDP, TCP, IP, fragment +from scapy.layers.inet6 import IPv6 +from scapy.layers.l2 import Ether, Dot1Q +from scapy.packet import Packet +from scapy.utils import PcapWriter + + +def write_pcap(path: str, packets: List[Packet]) -> None: + with PcapWriter(path) as fh: + for p in packets: + fh.write(p) + + +def ipv4_send(src: str, dst: str) -> Packet: + return Ether(dst="00:11:22:33:44:55", src="00:00:00:11:11:11") / Dot1Q(vlan=100) / IP(src=src, dst=dst, ttl=64) + + +def ipv4_recv(src: str, dst: str) -> Packet: + return Ether(dst="00:00:00:22:22:22", src="00:11:22:33:44:55") / Dot1Q(vlan=200) / IP(src=src, dst=dst, ttl=63) + + +write_pcap("001-send.pcap", [ + fragment(ipv4_send("10.0.0.1", "1.2.3.4") / UDP(sport=1024, dport=53)/("ABCDEFGH1234AAAAAAAA"*128), fragsize=1208), + ipv4_send("10.0.0.1", "1.2.3.4") / UDP(sport=1024, dport=53), + ipv4_send("10.0.0.1", "1.2.3.4") / TCP(sport=1024, dport=53), +]) + +write_pcap("001-expect.pcap", [ + ipv4_recv("10.0.0.1", "1.2.3.4") / TCP(sport=1024, dport=53), +]) + +write_pcap("001-expect-dump-ring1.pcap", [ + fragment(ipv4_send("10.0.0.1", "1.2.3.4") / UDP(sport=1024, dport=53)/("ABCDEFGH1234AAAAAAAA"*128), fragsize=1208), + ipv4_send("10.0.0.1", "1.2.3.4") / UDP(sport=1024, dport=53), + ipv4_send("10.0.0.1", "1.2.3.4") / TCP(sport=1024, dport=53), +]) diff --git a/autotests/units/001_one_port/067_intersecting_dump_rules/001-expect-dump-ring1.pcap b/autotests/units/001_one_port/067_intersecting_dump_rules/001-expect-dump-ring1.pcap new file mode 100644 index 0000000000000000000000000000000000000000..73a1c6e0e419cb1e318a605bd1485894bc055cf3 GIT binary patch literal 2890 zcmca|c+)~A1{MYw`2U}Qff2}gsmPtG+04#x4=4q~41!9=E}S&R?E^OFKp0!wj$sY3MW^Q= zn9Tz#?FMUG4&<8!o$L&HK#zbhR^P~g8nq6BIj3O00ku>aINmM->6YzgXRrcl1YxYY YHNd*ra!#V?2B`*u044>7+T{!k0JLPQv;Y7A literal 0 HcmV?d00001 diff --git a/autotests/units/001_one_port/067_intersecting_dump_rules/001-expect-dump-ring2.pcap b/autotests/units/001_one_port/067_intersecting_dump_rules/001-expect-dump-ring2.pcap new file mode 100644 index 0000000000000000000000000000000000000000..a2ba4eb774c43b29348286aadbdfa06cdd5f225c GIT binary patch literal 98 zcmca|c+)~A1{MYw`2U}Qff2}gsmPtWcrrVK6_5?W41!9=E}kYW%BU{YYHUCzJ&08qyc2LJ#7 literal 0 HcmV?d00001 diff --git a/autotests/units/001_one_port/067_intersecting_dump_rules/001-expect.pcap b/autotests/units/001_one_port/067_intersecting_dump_rules/001-expect.pcap new file mode 100644 index 0000000000000000000000000000000000000000..2f841e453f0223972a940556e3ae423cf769e32f GIT binary patch literal 98 zcmca|c+)~A1{MYw`2U}Qff2}gsmPsrwvL^_3djawAW%|LVh~g^b_s1{U^v0S;L5?LjaQkL+x?~1^{hd4zmCN literal 0 HcmV?d00001 diff --git a/autotests/units/001_one_port/067_intersecting_dump_rules/001-send.pcap b/autotests/units/001_one_port/067_intersecting_dump_rules/001-send.pcap new file mode 100644 index 0000000000000000000000000000000000000000..7db1d2dd7a34eee693b626b95a87c245c3682e86 GIT binary patch literal 2890 zcmca|c+)~A1{MYw`2U}Qff2}gsmPuBBb%M!9#9H|83dJ#T|z;$pkN~dLkb6jD+9|J z21W%22SE=hE}$kxCT13(LQ^h|nW>IW&MvNQ?jD9l#wLz1Fe>Ia8ZN-ZFq#fV!vIt! z45D;EbZIx;LD0P!R@x0FAB?(XaHXix_!x~3P$4%M(*dls+XrmUfiSkV9m5)6i%!o$ zFq;Qf+6~sW9LP6vCF~4(K#zbhR^P~g8nq6BIj3O00ku>aINmM->0VmO&R_-92*Ox( YYk+mL<(x#(4N?sR0Za-EwaXb80PZTSv;Y7A literal 0 HcmV?d00001 diff --git a/autotests/units/001_one_port/067_intersecting_dump_rules/autotest.yaml b/autotests/units/001_one_port/067_intersecting_dump_rules/autotest.yaml new file mode 100644 index 00000000..0cdfb29e --- /dev/null +++ b/autotests/units/001_one_port/067_intersecting_dump_rules/autotest.yaml @@ -0,0 +1,12 @@ +steps: +- ipv4Update: "0.0.0.0/0 -> 200.0.0.1" +- ipv6Update: "::/0 -> fe80::1" +- sendPackets: + - port: kni0 + send: 001-send.pcap + expect: 001-expect.pcap +- dumpPackets: + - ringTag: ring1 + expect: 001-expect-dump-ring1.pcap + - ringTag: ring2 + expect: 001-expect-dump-ring2.pcap diff --git a/autotests/units/001_one_port/067_intersecting_dump_rules/controlplane.conf b/autotests/units/001_one_port/067_intersecting_dump_rules/controlplane.conf new file mode 100644 index 00000000..3c0da4fd --- /dev/null +++ b/autotests/units/001_one_port/067_intersecting_dump_rules/controlplane.conf @@ -0,0 +1,42 @@ +{ + "modules": { + "lp0.100": { + "type": "logicalPort", + "physicalPort": "kni0", + "vlanId": "100", + "macAddress": "00:11:22:33:44:55", + "nextModule": "acl0" + }, + "lp0.200": { + "type": "logicalPort", + "physicalPort": "kni0", + "vlanId": "200", + "macAddress": "00:11:22:33:44:55", + "nextModule": "acl0" + }, + "acl0": { + "type": "acl", + "firewall": "firewall.txt", + "nextModules": [ + "vrf0" + ] + }, + "vrf0": { + "type": "route", + "interfaces": { + "kni0.100": { + "ipv6Prefix": "fe80::2/64", + "neighborIPv6Address": "fe80::1", + "neighborMacAddress": "00:00:00:11:11:11", + "nextModule": "lp0.100" + }, + "kni0.200": { + "ipv4Prefix": "200.0.0.2/24", + "neighborIPv4Address": "200.0.0.1", + "neighborMacAddress": "00:00:00:22:22:22", + "nextModule": "lp0.200" + } + } + } + } +} diff --git a/autotests/units/001_one_port/067_intersecting_dump_rules/firewall.txt b/autotests/units/001_one_port/067_intersecting_dump_rules/firewall.txt new file mode 100644 index 00000000..098d8b7e --- /dev/null +++ b/autotests/units/001_one_port/067_intersecting_dump_rules/firewall.txt @@ -0,0 +1,12 @@ +:BEGIN +add skipto :IN ip from any to any in + +:IN +add dump ring1 ip from any to any +add dump ring2 tcp from any to any +add allow tcp from 10.0.0.0/24 to 1.2.3.4 53 +add deny ip from any to any + +add allow udp from 10.0.0.0/24 to 1.2.3.4 53 +add allow ip from any to any frag + diff --git a/autotests/units/001_one_port/067_intersecting_dump_rules/gen.py b/autotests/units/001_one_port/067_intersecting_dump_rules/gen.py new file mode 100644 index 00000000..30cd8aef --- /dev/null +++ b/autotests/units/001_one_port/067_intersecting_dump_rules/gen.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +from typing import List + +from scapy.layers.inet import UDP, TCP, IP, fragment +from scapy.layers.inet6 import IPv6 +from scapy.layers.l2 import Ether, Dot1Q +from scapy.packet import Packet +from scapy.utils import PcapWriter + + +def write_pcap(path: str, packets: List[Packet]) -> None: + with PcapWriter(path) as fh: + for p in packets: + fh.write(p) + + +def ipv4_send(src: str, dst: str) -> Packet: + return Ether(dst="00:11:22:33:44:55", src="00:00:00:11:11:11") / Dot1Q(vlan=100) / IP(src=src, dst=dst, ttl=64) + + +def ipv4_recv(src: str, dst: str) -> Packet: + return Ether(dst="00:00:00:22:22:22", src="00:11:22:33:44:55") / Dot1Q(vlan=200) / IP(src=src, dst=dst, ttl=63) + + +write_pcap("001-send.pcap", [ + fragment(ipv4_send("10.0.0.1", "1.2.3.4") / UDP(sport=1024, dport=53)/("ABCDEFGH1234AAAAAAAA"*128), fragsize=1208), + ipv4_send("10.0.0.1", "1.2.3.4") / UDP(sport=1024, dport=53), + ipv4_send("10.0.0.1", "1.2.3.4") / TCP(sport=1024, dport=53), +]) + +write_pcap("001-expect.pcap", [ + ipv4_recv("10.0.0.1", "1.2.3.4") / TCP(sport=1024, dport=53), +]) + +write_pcap("001-expect-dump-ring1.pcap", [ + fragment(ipv4_send("10.0.0.1", "1.2.3.4") / UDP(sport=1024, dport=53)/("ABCDEFGH1234AAAAAAAA"*128), fragsize=1208), + ipv4_send("10.0.0.1", "1.2.3.4") / UDP(sport=1024, dport=53), + ipv4_send("10.0.0.1", "1.2.3.4") / TCP(sport=1024, dport=53), +]) + +write_pcap("001-expect-dump-ring2.pcap", [ + ipv4_send("10.0.0.1", "1.2.3.4") / TCP(sport=1024, dport=53), +]) + diff --git a/autotests/units/001_one_port/dataplane.conf b/autotests/units/001_one_port/dataplane.conf index e1557402..d2643c80 100644 --- a/autotests/units/001_one_port/dataplane.conf +++ b/autotests/units/001_one_port/dataplane.conf @@ -26,5 +26,17 @@ "stateful_firewall_udp_timeout": 16, "stateful_firewall_tcp_timeout": 16 }, - "memory": 2048 + "memory": 2048, + "sharedMemory": [ + { + "tag": "ring1", + "dump_size": 16384, + "dump_count": 64 + }, + { + "tag": "ring2", + "dump_size": 16384, + "dump_count": 64 + } + ] } diff --git a/cli/src/main.cpp b/cli/src/main.cpp index 6b8ef39d..e2ba958e 100644 --- a/cli/src/main.cpp +++ b/cli/src/main.cpp @@ -92,6 +92,8 @@ commands = {{"help", "", [](const auto& args){call(help, args);}}, {"fw show", "", [](const auto& args){call(show::fw, args);}}, {"fw list", "", [](const auto& args){call(show::fwlist, args);}}, {}, + {"show shm info", "", [](const auto& args){call(show::shm_info, args);}}, + {}, {"samples show", "", [](const auto& args){call(show::samples, args);}}, {"samples dump", "", [](const auto& args){call(show::samples_dump, args);}}, {}, diff --git a/cli/src/show.h b/cli/src/show.h index 20e92ae9..eeab5d4e 100644 --- a/cli/src/show.h +++ b/cli/src/show.h @@ -816,4 +816,27 @@ void counter_by_name(std::string counter_name, table.print(); } +void shm_info() +{ + interface::dataPlane dataplane; + const auto response = dataplane.get_shm_info(); + + table_t table; + table.insert("ring name", + "dump tag", + "dump size", + "dump count", + "core id", + "socket id", + "ipc key", + "offset"); + + for (const auto& [name, tag, size, count, core, socket, ipc_key, offset] : response) + { + table.insert(name, tag, size, count, core, socket, ipc_key, offset); + } + + table.print(); +} + } diff --git a/common/acl.h b/common/acl.h index f8f99d5c..3b48e461 100644 --- a/common/acl.h +++ b/common/acl.h @@ -1,8 +1,8 @@ #pragma once -#include "uint128.h" #include "stream.h" #include "type.h" +#include "uint128.h" namespace common::acl { @@ -115,6 +115,53 @@ struct transport_key_t tAclGroupId network_flags : 8; }; +// class action_t is used to store all non-terminating rule data that +// shouldn't be stored in common::globalBase::tFlow +class action_t { +public: + action_t() : + dump_id(0), + dump_tag("") + {} + + action_t(std::string dump_tag) : + dump_id(0), + dump_tag(dump_tag) + {} + + inline bool operator==(const action_t& o) const + { + return std::tie(dump_id, dump_tag) == + std::tie(o.dump_id, o.dump_tag); + } + + inline bool operator!=(const action_t& o) const + { + return !operator==(o); + } + + constexpr bool operator<(const action_t& o) const + { + return std::tie(dump_id, dump_tag) < + std::tie(o.dump_id, o.dump_tag); + } + + void pop(stream_in_t& stream) + { + stream.pop(dump_id); + stream.pop(dump_tag); + } + + void push(stream_out_t& stream) const + { + stream.push(dump_id); + stream.push(dump_tag); + } + + uint64_t dump_id; + std::string dump_tag; +}; + struct total_key_t { constexpr bool operator<(const total_key_t& second) const @@ -139,6 +186,11 @@ struct total_key_t struct value_t { + value_t() + { + memset(dump_ids, 0, sizeof(dump_ids)); + } + constexpr bool operator<(const value_t& second) const { return flow < second.flow; @@ -147,14 +199,17 @@ struct value_t void pop(stream_in_t& stream) { stream.pop(flow); + stream.pop(dump_ids); } void push(stream_out_t& stream) const { stream.push(flow); + stream.push(dump_ids); } common::globalBase::tFlow flow; + uint32_t dump_ids[YANET_CONFIG_DUMP_ID_SIZE]; }; template diff --git a/common/bufferring.h b/common/bufferring.h new file mode 100644 index 00000000..7ed16d1a --- /dev/null +++ b/common/bufferring.h @@ -0,0 +1,63 @@ +#include + +namespace common +{ + +// Each buffer ring has the following structure: +// +// _________memory_to_store_the_packets_______________ +// | | +// | __________item_t_________ | +// | | | | +// [["b","a",...]({}{}{}...{["s","t",...]............}...........{}{})] +// |___________| |___________|____________| +// ^ ^ \__memory[] +// | | +// | item_header_t: "s" -- size +// | "t" -- tag +// ring_header_t: "b" -- before ... -- padding +// "a" -- after +// ... -- padding +class bufferring +{ +public: + bufferring() + { + } + bufferring(void* memory, int unit_size, int units_number) : + unit_size(unit_size), + units_number(units_number) + { + ring = (ring_t*)memory; + } + + struct ring_header_t + { + uint64_t before; + uint64_t after; + }__attribute__((__aligned__(64))); + + struct ring_t + { + ring_header_t header; + uint8_t memory[]; + }; + + struct item_header_t + { + uint32_t size; + uint32_t tag; + }__attribute__((__aligned__(64))); + + struct item_t + { + item_header_t header; + uint8_t memory[]; + }; + + int unit_size; + int units_number; + ring_t* ring; +}; + +} diff --git a/common/config.release.h b/common/config.release.h index b57b5a83..fae811ef 100644 --- a/common/config.release.h +++ b/common/config.release.h @@ -68,3 +68,7 @@ #define YANET_CONFIG_STATE_TIMEOUT_DEFAULT (180) #define YANET_CONFIG_STATE_TIMEOUT_MAX (32 * 1024) #define YANET_CONFIG_ACL_TREE_CHUNKS_BUCKET_SIZE (64 * 1024) +#define YANET_CONFIG_DUMP_ID_SIZE (8) +#define YANET_CONFIG_DUMP_ID_TO_TAG_SIZE (1024 * 1024) +#define YANET_CONFIG_SHARED_RINGS_NUMBER (32) +#define YANET_DEFAULT_IPC_SHMKEY (12345) diff --git a/common/idataplane.h b/common/idataplane.h index 9c3e6595..f881967d 100644 --- a/common/idataplane.h +++ b/common/idataplane.h @@ -204,6 +204,11 @@ class dataPlane return get(request); } + auto get_shm_info() const + { + return get(); + } + protected: void connectToDataPlane() const { diff --git a/common/idp.h b/common/idp.h index 4fdb6cb4..75719f68 100644 --- a/common/idp.h +++ b/common/idp.h @@ -69,7 +69,8 @@ enum class requestType : uint32_t update_vip_vport_proto, version, get_counter_by_name, - size + get_shm_info, + size, // size should always be at the bottom of the list, this enum allows us to find out the size of the enum list }; using labelExp = std::tuple; } + namespace dump_tags_ids + { + using request = std::vector; + } + namespace route_lpm_update { using request = lpm::request; @@ -478,6 +485,7 @@ namespace updateGlobalBase acl_transport_table::request, acl_total_table::request, acl_values::request, + dump_tags_ids::request, lpm::request, route_value_update::request, route_tunnel_value_update::request, @@ -826,6 +834,20 @@ namespace get_counter_by_name using response = std::map; } +namespace get_shm_info +{ + using dump_meta = std::tuple; /// offset + + using response = std::vector; +} + namespace limits { using limit = std::tuple, version::response, limits::response, samples::response, - get_counter_by_name::response>; + get_counter_by_name::response, + get_shm_info::response>; } diff --git a/common/result.h b/common/result.h index b1959596..fa62aa41 100644 --- a/common/result.h +++ b/common/result.h @@ -60,6 +60,7 @@ enum class result_e : uint32_t invalidJson, missingRequiredOption, invalidTun64Id, + errorInitSharedMemory, }; static constexpr const char* result_to_c_str(common::result_e e) @@ -174,6 +175,8 @@ static constexpr const char* result_to_c_str(common::result_e e) return "missingRequiredOption"; case result_e::invalidTun64Id: return "invalidTun64Id"; + case result_e::errorInitSharedMemory: + return "errorInitSharedMemory"; } return "?"; diff --git a/common/stream.h b/common/stream.h index 3698b8a2..398e84fb 100644 --- a/common/stream.h +++ b/common/stream.h @@ -344,18 +344,14 @@ inline void stream_in_t::pop(std::vector& vector) pop(count); - vector.reserve(count); + vector.resize(count); for (integer_t i = 0; i < count; i++) { - TType vectorValue; - - pop(vectorValue); + pop(vector[i]); if (isFailed()) { return; } - - vector.emplace_back(std::move(vectorValue)); } } diff --git a/controlplane/src/acl.cpp b/controlplane/src/acl.cpp index 91fe0bc9..40f5039f 100644 --- a/controlplane/src/acl.cpp +++ b/controlplane/src/acl.cpp @@ -306,6 +306,7 @@ struct firewall_rules_t // simple skipto rules are handled as usual [[fallthrough]]; case ipfw::rule_action_t::ALLOW: + case ipfw::rule_action_t::DUMP: case ipfw::rule_action_t::DENY: { // handle only meaning rules auto& ruleref = yanet_rules.emplace_back(rulep, configp); @@ -363,6 +364,15 @@ static inline auto is_term_filter(const ref_t& filter) return (!filter || (!filter->src && !filter->dst && !filter->flags && !filter->proto)); } +static inline auto is_nonterm_action(const std::variant& action) +{ + if (std::holds_alternative(action)) + { + return true; + } + return false; +} + // gather matching rules from dispatcher static bool unwind_dispatcher(const dispatcher_rules_t& dispatcher, const ref_t& filter, @@ -393,8 +403,9 @@ static bool unwind_dispatcher(const dispatcher_rules_t& dispatcher, ids.resize(idSize); ACL_DBGMSG("gathered..."); - if (is_term_filter(rule.filter)) + if (is_term_filter(rule.filter) && !is_nonterm_action(rule.action)) { + ACL_DBGMSG("terminating filter..."); break; } } @@ -468,18 +479,26 @@ static bool unwind(int64_t start_from, firewall_rules_t& fw, iface, ids, rules, log || rule.log); } } - else + else if (std::holds_alternative(rule.action)) { // handle tFlows rules.emplace_back(std::move(result_filter), std::get(rule.action), ids, log || rule.log); - ACL_DBGMSG("gathered..."); + ACL_DBGMSG("tFlow gathered..."); + } + else + { + rules.emplace_back(std::move(result_filter), + std::get(rule.action), + ids, log || rule.log); + ACL_DBGMSG("action_t gathered..."); } ids.resize(idSize); - if (is_term_filter(rule.filter)) + if (is_term_filter(rule.filter) && !is_nonterm_action(rule.action)) { + ACL_DBGMSG("terminating filter..."); return true; } } @@ -856,35 +875,51 @@ std::vector unwind_used_rules(const std::map(rule.action); + if (std::holds_alternative(rule.action)) { + auto &flow = std::get(rule.action); - if (rule.filter->keepstate) - { - flow.flags |= (int)common::globalBase::eFlowFlags::keepstate; - } - if (rule.log) - { - flow.flags |= (int)common::globalBase::eFlowFlags::log; - } + if (rule.filter->keepstate) + { + flow.flags |= (int)common::globalBase::eFlowFlags::keepstate; + } + if (rule.log) + { + flow.flags |= (int)common::globalBase::eFlowFlags::log; + } - auto it = ids_map_map.find(rule.ids); - if (it != ids_map_map.end()) - { - flow.counter_id = it->second; - } - else - { - if (result.ids_map.size() < YANET_CONFIG_ACL_COUNTERS_SIZE) + auto it = ids_map_map.find(rule.ids); + if (it != ids_map_map.end()) { - auto id = result.ids_map.size(); - flow.counter_id = id; - result.ids_map.push_back(rule.ids); - ids_map_map.emplace(rule.ids, id); + flow.counter_id = it->second; } else { - flow.counter_id = 0; - ids_overflow.insert(rule.ids); + if (result.ids_map.size() < YANET_CONFIG_ACL_COUNTERS_SIZE) + { + auto id = result.ids_map.size(); + flow.counter_id = id; + result.ids_map.push_back(rule.ids); + ids_map_map.emplace(rule.ids, id); + } + else + { + flow.counter_id = 0; + ids_overflow.insert(rule.ids); + } + } + } + else if (std::holds_alternative(rule.action)) + { + auto& action = std::get(rule.action); + if (!action.dump_tag.empty()) + { + auto it = result.tag_to_dump_id.find(action.dump_tag); + if (it == result.tag_to_dump_id.end()) + { + result.dump_id_to_tag.emplace_back(action.dump_tag); + it = result.tag_to_dump_id.emplace_hint(it, action.dump_tag, result.dump_id_to_tag.size()); + } + action.dump_id = it->second; } } } diff --git a/controlplane/src/acl.h b/controlplane/src/acl.h index 54d08262..629ed2fd 100644 --- a/controlplane/src/acl.h +++ b/controlplane/src/acl.h @@ -34,6 +34,9 @@ struct result_t std::map in_iface_map; std::map out_iface_map; std::map> acl_map; + + std::vector dump_id_to_tag; + std::map tag_to_dump_id; }; iface_map_t ifaceMapping(std::map logicalPorts, diff --git a/controlplane/src/acl/rule.h b/controlplane/src/acl/rule.h index cf1b3d85..680c264f 100644 --- a/controlplane/src/acl/rule.h +++ b/controlplane/src/acl/rule.h @@ -1031,7 +1031,7 @@ const int64_t DISPATCHER = -1; struct rule_t { ref_t filter; - std::variant action; + std::variant action; ids_t ids; int64_t ruleno; mutable std::string text; @@ -1048,10 +1048,19 @@ struct rule_t ruleno = DISPATCHER; } + rule_t(const ref_t& _filter, common::acl::action_t action, const ids_t& ids, bool log) : + filter(_filter), + action(action), + ids(ids), + log(log) + { + ruleno = DISPATCHER; + } + rule_t(const ref_t& _filter, int64_t num, int64_t skipto) : filter(_filter), action(skipto), - ruleno(num), + ruleno(num), log(false) {} @@ -1089,6 +1098,9 @@ struct rule_t case ipfw::rule_action_t::ALLOW: action = DISPATCHER; break; + case ipfw::rule_action_t::DUMP: + action = common::acl::action_t(std::get(rulep->action_arg)); + break; default: YANET_LOG_WARNING("unexpected rule action in rule '%s'\n", rulep->text.data()); return; @@ -1154,6 +1166,14 @@ struct rule_t text = "flow " + std::string(eFlowType_toString(flow.type)) + "(" + std::to_string(flow.data.atomic) + ")"; } } + else if (std::holds_alternative(action)) + { + auto rule_action = std::get(action); + if (!rule_action.dump_tag.empty()) + { + text = "dump(" + rule_action.dump_tag + ")"; + } + } else { auto arg = std::get(action); @@ -1316,11 +1336,16 @@ struct hash const auto& act = std::get(r.action); hash_combine(h, act); } - else + else if (std::holds_alternative(r.action)) { auto flow = std::get(r.action); hash_combine(h, 1, (uint64_t(flow.type) << 32) & flow.data.atomic); } + else + { + auto action = std::get(r.action); + hash_combine(h, action.dump_id); + } if (r.filter) { hash_combine(h, **r.filter); diff --git a/controlplane/src/acl_compiler.cpp b/controlplane/src/acl_compiler.cpp index 28efc088..d57b429f 100644 --- a/controlplane/src/acl_compiler.cpp +++ b/controlplane/src/acl_compiler.cpp @@ -274,11 +274,23 @@ void compiler_t::collect(const std::vector& unwind_rules) /// value { - common::globalBase::tFlow flow = std::get(unwind_rule.action); - rule.value_filter_id = value.collect(flow); + if (auto flow = std::get_if(&unwind_rule.action)) + { + rule.value_filter_id = value.collect({*flow}); + } + else if (auto action = std::get_if(&unwind_rule.action)) + { + rule.value_filter_id = value.collect({*action}); + } + } + + /// terminating + { + rule.terminating = std::holds_alternative(unwind_rule.action); } YANET_LOG_DEBUG("acl::compile: rule: %s\n", unwind_rule.to_string().data()); + YANET_LOG_DEBUG("acl::compile: terminating: %s\n", rule.terminating ? "true" : "false"); YANET_LOG_DEBUG("acl::compile: rule_id: %u\n", rule.rule_id); YANET_LOG_DEBUG("acl::compile: network_ipv4_source_filter_id: %u\n", rule.network_ipv4_source_filter_id); YANET_LOG_DEBUG("acl::compile: network_ipv4_destination_filter_id: %u\n", rule.network_ipv4_destination_filter_id); @@ -462,4 +474,6 @@ void compiler_t::total_table_compile() void compiler_t::value_compile() { value.compile(); + YANET_LOG_INFO("acl::compile: size: %lu\n", + value.vector.size()); } diff --git a/controlplane/src/acl_rule.h b/controlplane/src/acl_rule.h index 627b7818..1b500ec9 100644 --- a/controlplane/src/acl_rule.h +++ b/controlplane/src/acl_rule.h @@ -24,6 +24,7 @@ class rule_t unsigned int via_filter_id; unsigned int total_table_filter_id; unsigned int value_filter_id; + bool terminating; }; } diff --git a/controlplane/src/acl_total_table.cpp b/controlplane/src/acl_total_table.cpp index a8a48e94..58f443b6 100644 --- a/controlplane/src/acl_total_table.cpp +++ b/controlplane/src/acl_total_table.cpp @@ -63,27 +63,47 @@ void total_table_t::compile() /// @todo: acl_id -> via_filter_id key.acl_id = acl_id; - - bool used = true; + bool used = false; for (const auto& thread : compiler->transport_table.threads) { for (const auto transport_table_group_id : thread.transport_table_filter_id_group_ids[transport_table_filter_id]) { key.transport_id = transport_table_group_id; - auto it = table.find(key); if (it == table.end()) { - table.emplace_hint(it, key, group_id); - filter_id_group_ids[filter_id].emplace(group_id); - - if (used) + // If there is no such key in table, then we save [key, group_id] + // without any additional checks. + it = table.emplace_hint(it, key, group_id); + used = true; + } + else + { + // If table already has such key, then we are trying to collect + // new combination of the previous group_id and the current group_id. + // If new_group_id differs from the current group_id, then we replace + // the previous group_id with the new one. Otherwise we don't do anything. + const auto new_group_id = compiler->value.collect(it->second, group_id); + if (new_group_id != it->second) { - compiler->used_rules.emplace_back(rule.rule_id); - used = false; + it->second = new_group_id; + used = true; } } + + if (rule.terminating && used) + { + // If the rule is termineting and has been used, then we mark filter_id + // as filled in to prevent further additional checks. + filter_id_group_ids[filter_id].emplace(it->second); + } } } + + + if (used) + { + compiler->used_rules.emplace_back(rule.rule_id); + } } } diff --git a/controlplane/src/acl_value.cpp b/controlplane/src/acl_value.cpp index 7ee112f5..83984e4b 100644 --- a/controlplane/src/acl_value.cpp +++ b/controlplane/src/acl_value.cpp @@ -18,7 +18,7 @@ void value_t::clear() /// @todo: find default_flow common::globalBase::flow_t default_flow; default_flow.type = common::globalBase::eFlowType::drop; - collect(default_flow); + collect({default_flow}); } } @@ -34,12 +34,50 @@ unsigned int value_t::collect(const filter& filter) return it->second; } +// This collect function is only used in acl_total_table compiler to squash +// non-terminating and terminating rules into one unique group_id. +unsigned int value_t::collect(const tAclGroupId prev_id, const tAclGroupId id) +{ + unsigned int ret; + auto prev_filter = filters[prev_id]; + if (std::holds_alternative(prev_filter.back())) + { + ret = prev_id; + } + else + { + const auto& filter = filters[id]; + prev_filter.emplace_back(filter.back()); + ret = collect(prev_filter); + } + + return ret; +} + void value_t::compile() { for (const auto& filter : filters) { + int dumps_counter = 0; common::acl::value_t value; - value.flow = filter; + for (const auto& it : filter) + { + if (auto action = std::get_if(&it)) + { + if (dumps_counter >= YANET_CONFIG_DUMP_ID_SIZE || + action->dump_id >= YANET_CONFIG_DUMP_ID_TO_TAG_SIZE) + { + continue; + } + value.dump_ids[dumps_counter] = action->dump_id; + dumps_counter++; + } + else + { + value.flow = std::get(it); + } + } + vector.emplace_back(std::move(value)); } } diff --git a/controlplane/src/acl_value.h b/controlplane/src/acl_value.h index 10a9260b..2f5d471f 100644 --- a/controlplane/src/acl_value.h +++ b/controlplane/src/acl_value.h @@ -14,10 +14,11 @@ class value_t value_t(); public: - using filter = common::globalBase::flow_t; + using filter = std::vector>; void clear(); unsigned int collect(const filter& filter); + unsigned int collect(const tAclGroupId prev_id, const tAclGroupId id); void compile(); public: diff --git a/controlplane/src/base.h b/controlplane/src/base.h index 53b42d7d..cbf128f6 100644 --- a/controlplane/src/base.h +++ b/controlplane/src/base.h @@ -455,6 +455,7 @@ class base_t std::vector dispatcher; acl::iface_map_t iface_map; acl::iface_map_t result_iface_map; + std::vector dump_id_to_tag; std::map logicalport_id_to_name; bool storeSamples; uint32_t serial; diff --git a/controlplane/src/configconverter.cpp b/controlplane/src/configconverter.cpp index 755f8598..9b7af5a6 100644 --- a/controlplane/src/configconverter.cpp +++ b/controlplane/src/configconverter.cpp @@ -1736,6 +1736,7 @@ void config_converter_t::buildAcl() globalbase.emplace_back(common::idp::updateGlobalBase::requestType::acl_total_table, std::move(result.acl_total_table)); globalbase.emplace_back(common::idp::updateGlobalBase::requestType::acl_values, std::move(result.acl_values)); + globalbase.emplace_back(common::idp::updateGlobalBase::requestType::dump_tags_ids, std::move(result.dump_id_to_tag)); common::idp::updateGlobalBase::fwstate_synchronization_update::request fwstate_sync_request; for (const auto& [moduleName, acl] : baseNext.acls) @@ -1791,4 +1792,5 @@ void config_converter_t::buildAcl() baseNext.ids_map = std::move(result.ids_map); baseNext.rules = std::move(result.rules); baseNext.dispatcher = std::move(result.dispatcher); + baseNext.dump_id_to_tag = std::move(result.dump_id_to_tag); } diff --git a/dataplane/meson.build b/dataplane/meson.build index 7b6ae76c..9bce74ae 100644 --- a/dataplane/meson.build +++ b/dataplane/meson.build @@ -12,6 +12,7 @@ sources = files('src/bus.cpp', 'src/globalbase.cpp', 'src/main.cpp', 'src/report.cpp', + 'src/sharedmemory.cpp', 'src/sock_dev.cpp', 'src/worker.cpp', 'src/worker_gc.cpp') diff --git a/dataplane/src/bus.cpp b/dataplane/src/bus.cpp index de90034b..1298442c 100644 --- a/dataplane/src/bus.cpp +++ b/dataplane/src/bus.cpp @@ -334,6 +334,10 @@ void cBus::clientThread(int clientSocket) { response = callWithResponse(&cControlPlane::nat64stateful_state, request); } + else if (type == common::idp::requestType::get_shm_info) + { + response = callWithResponse(&cControlPlane::get_shm_info, request); + } else { stats.errors[(uint32_t) common::idp::errorType::busParse]++; diff --git a/dataplane/src/controlplane.cpp b/dataplane/src/controlplane.cpp index 65928ff5..6fd76f63 100644 --- a/dataplane/src/controlplane.cpp +++ b/dataplane/src/controlplane.cpp @@ -1223,6 +1223,17 @@ common::idp::get_counter_by_name::response cControlPlane::get_counter_by_name(co return response; } +common::idp::get_shm_info::response cControlPlane::get_shm_info() +{ + common::idp::get_shm_info::response response; + for (const auto& key : dataPlane->getShmInfo()) + { + response.emplace_back(key); + } + + return response; +} + common::idp::nat64stateful_state::response cControlPlane::nat64stateful_state(const common::idp::nat64stateful_state::request& request) { common::idp::nat64stateful_state::response response; diff --git a/dataplane/src/controlplane.h b/dataplane/src/controlplane.h index 5c16fcf1..796c9d49 100644 --- a/dataplane/src/controlplane.h +++ b/dataplane/src/controlplane.h @@ -70,6 +70,7 @@ class cControlPlane ///< @todo: move to cDataPlane common::idp::version::response version(); common::idp::get_counter_by_name::response get_counter_by_name(const common::idp::get_counter_by_name::request& request); common::idp::nat64stateful_state::response nat64stateful_state(const common::idp::nat64stateful_state::request& request); + common::idp::get_shm_info::response get_shm_info(); void switchBase(); void switchGlobalBase(); diff --git a/dataplane/src/dataplane.cpp b/dataplane/src/dataplane.cpp index b2e5539d..652dc561 100644 --- a/dataplane/src/dataplane.cpp +++ b/dataplane/src/dataplane.cpp @@ -17,6 +17,15 @@ #include #include +#include +#include +#include +#include + +#include +#include +#include + #include "common.h" #include "dataplane.h" #include "report.h" @@ -152,6 +161,12 @@ eResult cDataPlane::init(const std::string& binaryPath, } } + result = allocateSharedMemory(); + if (result != eResult::success) + { + return result; + } + result = initEal(binaryPath, filePrefix); if (result != eResult::success) { @@ -190,6 +205,12 @@ eResult cDataPlane::init(const std::string& binaryPath, return result; } + result = splitSharedMemoryPerWorkers(); + if (result != eResult::success) + { + return result; + } + /// sanity check if (rte_lcore_count() != workers.size() + worker_gcs.size()) { @@ -983,6 +1004,172 @@ uint64_t cDataPlane::getConfigValue(const eConfigType& type) const return configValues.find(type)->second; } +eResult cDataPlane::allocateSharedMemory() +{ + /// precalculation of shared memory size for each numa + std::map number_of_workers_per_socket; + for (const auto& worker : config.workers) + { + const int coreId = worker.first; + + auto socket_id = numa_node_of_cpu(coreId); + if (socket_id == -1) + { + socket_id = 0; + } + + if (number_of_workers_per_socket.find(socket_id) == number_of_workers_per_socket.end()) + { + number_of_workers_per_socket[socket_id] = 1; + } + else + { + number_of_workers_per_socket[socket_id]++; + } + } + + std::map shm_size_per_socket; + for (const auto& ring_cfg : config.shared_memory) + { + const auto& [dump_size, dump_count] = ring_cfg.second; + + auto unit_size = sizeof(cSharedMemory::item_header_t) + dump_size; + if (unit_size % RTE_CACHE_LINE_SIZE != 0) + { + unit_size += RTE_CACHE_LINE_SIZE - unit_size % RTE_CACHE_LINE_SIZE; /// round up + } + + auto size = sizeof(cSharedMemory::ring_header_t) + unit_size * dump_count; + + for (const auto& [socket_id, num] : number_of_workers_per_socket) + { + auto it = shm_size_per_socket.find(socket_id); + if (it == shm_size_per_socket.end()) + { + it = shm_size_per_socket.emplace_hint(it, socket_id, 0); + } + it->second += size * num; + } + } + + /// allocating IPC shared memory + key_t key = YANET_DEFAULT_IPC_SHMKEY; + for (const auto& [socket_id, size] : shm_size_per_socket) + { + numa_run_on_node(socket_id); + + // deleting old shared memory if exists + if (int shmid = shmget(key, 0, 0) != -1) + { + shmctl(shmid, IPC_RMID, NULL); + } + + int flags = IPC_CREAT | 0666; + if (config.useHugeMem) + { + flags |= SHM_HUGETLB; + } + + int shmid = shmget(key, size, flags); + if (shmid == -1) { + YADECAP_LOG_ERROR("shmget(%d, %lu, %d) = %d\n", key, size, flags, errno); + return eResult::errorInitSharedMemory; + } + + void* shmaddr = shmat(shmid, NULL, 0); + if (shmaddr == (void*) -1) { + YADECAP_LOG_ERROR("shmat(%d, NULL, %d) = %d\n", shmid, 0, errno); + return eResult::errorInitSharedMemory; + } + + shm_by_socket_id[socket_id] = std::make_tuple(key, shmaddr); + + key++; + } + + return eResult::success; +} + +eResult cDataPlane::splitSharedMemoryPerWorkers() +{ + std::map offsets; + for (const auto& it : shm_by_socket_id) + { + const auto& addr = std::get<1>(it.second); + offsets[addr] = 0; + } + + /// split memory per worker + for (auto& [core_id, worker] : workers) + { + if (core_id == 0) + { + continue; + } + + const auto& socket_id = worker->socketId; + const auto& it = shm_by_socket_id.find(socket_id); + if (it == shm_by_socket_id.end()) + { + continue; + } + + const auto& [key, shm] = it->second; + + int ring_id = 0; + for (const auto& [tag, ring_cfg] : config.shared_memory) + { + const auto& [dump_size, units_number] = ring_cfg; + + auto unit_size = sizeof(cSharedMemory::item_header_t) + dump_size; + if (unit_size % RTE_CACHE_LINE_SIZE != 0) + { + unit_size += RTE_CACHE_LINE_SIZE - unit_size % RTE_CACHE_LINE_SIZE; /// round up + } + + auto size = sizeof(cSharedMemory::ring_header_t) + unit_size * units_number; + if (size % RTE_CACHE_LINE_SIZE != 0) + { + size += RTE_CACHE_LINE_SIZE - size % RTE_CACHE_LINE_SIZE; /// round up + } + + auto name = "shm_" + std::to_string(core_id) + "_" + std::to_string(ring_id); + + auto offset = offsets[shm]; + + auto memaddr = (void*)((intptr_t)shm + offset); + + cSharedMemory ring; + + ring.init(memaddr, unit_size, units_number); + + offsets[shm] += size; + + worker->dumpRings[ring_id] = ring; + + auto meta = common::idp::get_shm_info::dump_meta(name, tag, unit_size, units_number, core_id, socket_id, key, offset); + dumps_meta.emplace_back(meta); + + tag_to_id[tag] = ring_id; + + ring_id++; + } + } + + return eResult::success; +} + + +common::idp::get_shm_info::response cDataPlane::getShmInfo() +{ + common::idp::get_shm_info::response result; + result.reserve(dumps_meta.size()); + + std::copy(dumps_meta.begin(), dumps_meta.end(), std::back_inserter(result)); + + return result; +} + std::map cDataPlane::getPortStats(const tPortId& portId) const { /// unsafe @@ -1149,6 +1336,15 @@ eResult cDataPlane::parseConfig(const std::string& configFilePath) config.memory = rootJson.value("memory", 0); + if (rootJson.find("sharedMemory") != rootJson.end()) + { + result = parseSharedMemory(rootJson.find("sharedMemory").value()); + if (result != eResult::success) + { + return result; + } + } + auto it = rootJson.find("ealArgs"); if (it != rootJson.end()) { @@ -1355,6 +1551,26 @@ eResult cDataPlane::parseRateLimits(const nlohmann::json& json) return eResult::success; } +eResult cDataPlane::parseSharedMemory(const nlohmann::json& json) +{ + for (const auto& shmJson : json) + { + std::string tag = shmJson["tag"]; + unsigned int size = shmJson["dump_size"]; + unsigned int count = shmJson["dump_count"]; + + if (exist(config.shared_memory, tag)) + { + YADECAP_LOG_ERROR("tag '%s' already exist\n", tag.data()); + return eResult::invalidConfigurationFile; + } + + config.shared_memory[tag] = {size, count}; + } + + return eResult::success; +} + eResult cDataPlane::checkConfig() { if (config.ports.size() > CONFIG_YADECAP_PORTS_SIZE) diff --git a/dataplane/src/dataplane.h b/dataplane/src/dataplane.h index dd8be993..eecf37d0 100644 --- a/dataplane/src/dataplane.h +++ b/dataplane/src/dataplane.h @@ -76,6 +76,7 @@ struct tDataPlaneConfig uint32_t SWICMPOutRateLimit = 0; uint32_t rateLimitDivisor = 1; unsigned int memory = 0; + std::map> shared_memory; std::vector ealArgs; }; @@ -120,6 +121,7 @@ class cDataPlane eResult parseJsonPorts(const nlohmann::json& json); eResult parseConfigValues(const nlohmann::json& json); eResult parseRateLimits(const nlohmann::json& json); + eResult parseSharedMemory(const nlohmann::json& json); eResult checkConfig(); eResult initEal(const std::string& binaryPath, const std::string& filePrefix); @@ -129,7 +131,11 @@ class cDataPlane eResult initWorkers(); eResult initQueues(); + eResult allocateSharedMemory(); + eResult splitSharedMemoryPerWorkers(); + std::optional getCounterValueByName(const std::string &counter_name, uint32_t coreId); + common::idp::get_shm_info::response getShmInfo(); template @@ -312,6 +318,9 @@ class cDataPlane rte_mempool* mempool_log; + common::idp::get_shm_info::response dumps_meta; + std::map tag_to_id; + /// modules cReport report; std::unique_ptr controlPlane; @@ -320,6 +329,8 @@ class cDataPlane // array instead of the table - how many coreIds can be there? std::unordered_map> coreId_to_stats_tables; + std::map> shm_by_socket_id; + std::mutex hugepage_pointers_mutex; std::map hugepage_pointers; }; diff --git a/dataplane/src/globalbase.cpp b/dataplane/src/globalbase.cpp index cabc94db..762db440 100644 --- a/dataplane/src/globalbase.cpp +++ b/dataplane/src/globalbase.cpp @@ -179,6 +179,10 @@ eResult generation::update(const common::idp::updateGlobalBase::request& request { result = acl_values(std::get(data)); } + else if (type == common::idp::updateGlobalBase::requestType::dump_tags_ids) + { + result = dump_tags_ids(std::get(data)); + } else if (type == common::idp::updateGlobalBase::requestType::dregress_prefix_update) { result = dregress_prefix_update(std::get(data)); @@ -1976,6 +1980,22 @@ eResult generation::acl_values(const common::idp::updateGlobalBase::acl_values:: return eResult::success; } +eResult generation::dump_tags_ids(const common::idp::updateGlobalBase::dump_tags_ids::request& request) +{ + memset(dump_id_to_tag, -1, sizeof(dump_id_to_tag)); + + for (size_t i = 0; i < request.size(); i++) + { + auto it = dataPlane->tag_to_id.find(request[i]); + if (it != dataPlane->tag_to_id.end()) + { + dump_id_to_tag[i + 1] = it->second; + } + } + + return eResult::success; +} + eResult generation::dregress_prefix_update(const common::idp::updateGlobalBase::dregress_prefix_update::request& request) { eResult result = eResult::success; diff --git a/dataplane/src/globalbase.h b/dataplane/src/globalbase.h index fddd98df..35520d36 100644 --- a/dataplane/src/globalbase.h +++ b/dataplane/src/globalbase.h @@ -166,6 +166,7 @@ class generation eResult acl_transport_table(const common::idp::updateGlobalBase::acl_transport_table::request& request); eResult acl_total_table(const common::idp::updateGlobalBase::acl_total_table::request& request); eResult acl_values(const common::idp::updateGlobalBase::acl_values::request& request); + eResult dump_tags_ids(const common::idp::updateGlobalBase::dump_tags_ids::request& request); eResult dregress_prefix_update(const common::idp::updateGlobalBase::dregress_prefix_update::request& request); eResult dregress_prefix_remove(const common::idp::updateGlobalBase::dregress_prefix_remove::request& request); eResult dregress_prefix_clear(); @@ -291,6 +292,8 @@ class generation balancer_real_state_t balancer_real_states[YANET_CONFIG_BALANCER_REALS_SIZE]; uint32_t balancer_service_ring_id; balancer_service_ring_t balancer_service_rings[2]; + + int64_t dump_id_to_tag[YANET_CONFIG_DUMP_ID_TO_TAG_SIZE]; }; } diff --git a/dataplane/src/sharedmemory.cpp b/dataplane/src/sharedmemory.cpp new file mode 100644 index 00000000..51bfdd88 --- /dev/null +++ b/dataplane/src/sharedmemory.cpp @@ -0,0 +1,40 @@ +#include "sharedmemory.h" +#include "metadata.h" +#include + +eResult cSharedMemory::init(void* memory, int unit_size, int units_number) +{ + buffer = common::bufferring(memory, unit_size, units_number); + + buffer.ring->header.before = 0; + buffer.ring->header.after = 0; + + return eResult::success; +} + +void cSharedMemory::write(rte_mbuf* mbuf) +{ + // Each ring has its own header, the header contains absolute position + // to which next packet should be written. Position has two state: + // -- "before" increments immediately before of copying data to memory; + // -- "after" increments after copying data. + + uint64_t wpos = (buffer.ring->header.before) % buffer.units_number; + buffer.ring->header.before++; + common::bufferring::item_t* item = (common::bufferring::item_t*)((uintptr_t)buffer.ring->memory + (wpos * buffer.unit_size)); + + dataplane::metadata* metadata = YADECAP_METADATA(mbuf); + + item->header.tag = metadata->hash; + item->header.size = mbuf->data_len; + + uint64_t copy_size = RTE_MIN(buffer.unit_size, mbuf->data_len); + + memcpy(item->memory, + rte_pktmbuf_mtod(mbuf, void*), + copy_size); + + YANET_MEMORY_BARRIER_COMPILE; + + buffer.ring->header.after++; +} diff --git a/dataplane/src/sharedmemory.h b/dataplane/src/sharedmemory.h new file mode 100644 index 00000000..29eaa5c4 --- /dev/null +++ b/dataplane/src/sharedmemory.h @@ -0,0 +1,18 @@ +#include "common/bufferring.h" +#include "common/result.h" +#include "rte_mbuf.h" +#include "type.h" + +class cSharedMemory +{ +public: + using ring_header_t = common::bufferring::ring_header_t; + using ring_t = common::bufferring::ring_t; + using item_header_t = common::bufferring::item_header_t; + using item_t = common::bufferring::item_t; + + eResult init(void* memory, int unit_size, int units_number); + void write(rte_mbuf* mbuf); + + common::bufferring buffer; +}; diff --git a/dataplane/src/worker.cpp b/dataplane/src/worker.cpp index e050b514..ead6a62c 100644 --- a/dataplane/src/worker.cpp +++ b/dataplane/src/worker.cpp @@ -1124,6 +1124,22 @@ inline void cWorker::acl_ingress_handle4() acl_create_keepstate(mbuf, metadata->flow.data.aclId, value.flow); } + for (auto dump_id : value.dump_ids) + { + if (dump_id == 0) + { + break; + } + + auto ring_id = base.globalBase->dump_id_to_tag[dump_id]; + if (ring_id == -1) + { + continue; + } + auto& ring = dumpRings[ring_id]; + ring.write(mbuf); + } + acl_ingress_flow(mbuf, value.flow); } @@ -1299,6 +1315,22 @@ inline void cWorker::acl_ingress_handle6() acl_create_keepstate(mbuf, metadata->flow.data.aclId, value.flow); } + for (auto dump_id : value.dump_ids) + { + if (dump_id == 0) + { + break; + } + + auto ring_id = base.globalBase->dump_id_to_tag[dump_id]; + if (ring_id == -1) + { + continue; + } + auto& ring = dumpRings[ring_id]; + ring.write(mbuf); + } + acl_ingress_flow(mbuf, value.flow); } @@ -4823,6 +4855,22 @@ inline void cWorker::acl_egress_handle4() acl_create_keepstate(mbuf, metadata->aclId, value.flow); } + for (auto dump_id : value.dump_ids) + { + if (dump_id == 0) + { + break; + } + + auto ring_id = base.globalBase->dump_id_to_tag[dump_id]; + if (ring_id == -1) + { + continue; + } + auto& ring = dumpRings[ring_id]; + ring.write(mbuf); + } + acl_egress_flow(mbuf, value.flow); } @@ -4991,6 +5039,22 @@ inline void cWorker::acl_egress_handle6() acl_create_keepstate(mbuf, metadata->aclId, value.flow); } + for (auto dump_id : value.dump_ids) + { + if (dump_id == 0) + { + break; + } + + auto ring_id = base.globalBase->dump_id_to_tag[dump_id]; + if (ring_id == -1) + { + continue; + } + auto& ring = dumpRings[ring_id]; + ring.write(mbuf); + } + acl_egress_flow(mbuf, value.flow); } diff --git a/dataplane/src/worker.h b/dataplane/src/worker.h index b7669779..e27f6170 100644 --- a/dataplane/src/worker.h +++ b/dataplane/src/worker.h @@ -15,6 +15,7 @@ #include "common.h" #include "globalbase.h" #include "samples.h" +#include "sharedmemory.h" class cDataPlane; class mControlPlane; @@ -307,6 +308,8 @@ class cWorker // will decrease with each new packet sent to slow worker, replenishes each N mseconds int32_t packetsToSWNPRemainder; + cSharedMemory dumpRings[YANET_CONFIG_SHARED_RINGS_NUMBER]; + samples::Sampler sampler; YADECAP_CACHE_ALIGNED(align3); diff --git a/libfwparser/fw_config.cpp b/libfwparser/fw_config.cpp index b673acb5..fb85fb15 100644 --- a/libfwparser/fw_config.cpp +++ b/libfwparser/fw_config.cpp @@ -764,6 +764,19 @@ void fw_config_t::set_rule_action_arg(const rule_t::action_arg_t& a) } } +void fw_config_t::set_dump_action_arg(const rule_t::action_arg_t& a) +{ + FW_CONF_DEBUG("index() = " << a.index()); + if (std::holds_alternative(a)) + { + m_curr_rule->action_arg = std::to_string(std::get(a)); + } + else + { + m_curr_rule->action_arg = a; + } +} + void fw_config_t::add_rule_ports(const rule_t::ports_arg_t& ports) { FW_CONF_DEBUG("index() = " << ports.index()); @@ -1070,6 +1083,7 @@ bool fw_config_t::validate_rule(rule_ptr_t rulep) case rule_action_t::ALLOW: case rule_action_t::DENY: case rule_action_t::SKIPTO: + case rule_action_t::DUMP: break; default: return true; diff --git a/libfwparser/fw_config.h b/libfwparser/fw_config.h index 3e53b951..fa44bcb3 100644 --- a/libfwparser/fw_config.h +++ b/libfwparser/fw_config.h @@ -155,6 +155,7 @@ namespace ipfw { UNREACH6, SRCPRJID, DSTPRJID, + DUMP, }; enum class rule_action_modifier_t { @@ -413,6 +414,7 @@ namespace ipfw { void fill_rule_number(unsigned int); void set_rule_action(rule_action_t); void set_rule_action_arg(const rule_t::action_arg_t&); + void set_dump_action_arg(const rule_t::action_arg_t&); void set_rule_log() { m_curr_rule->log = true; diff --git a/libfwparser/fw_parser.y b/libfwparser/fw_parser.y index 7b0a08c3..a5d2c9ab 100644 --- a/libfwparser/fw_parser.y +++ b/libfwparser/fw_parser.y @@ -131,7 +131,7 @@ NOTCHAR LOOKUP UID RULENUM OBRACE EBRACE LBRACE RBRACE SRCPRJID DSTPRJID RED ALL LMAX DSTIP6 SRCIP6 TCPSETMSS NAT64CLAT NAT64LSN NAT64STL NPTV6 SRCADDR QM DSTADDR - SRCPORT DSTPORT SRCIP DSTIP EQUAL COMMA MINUS EOL M4LQ M4RQ + SRCPORT DSTPORT SRCIP DSTIP EQUAL COMMA MINUS EOL M4LQ M4RQ DUMP // QUEUE could be an argument to *MASK %nonassoc QUEUE @@ -492,6 +492,11 @@ action: cfg.set_rule_action(rule_action_t::DENY); } | + DUMP dump_tag + { + cfg.set_rule_action(rule_action_t::DUMP); + } + | T_REJECT { cfg.set_rule_action(rule_action_t::UNREACH); @@ -2011,6 +2016,17 @@ nptv6config: | nptv6config nptv6token ; +dump_tag: + TOKEN + { + cfg.set_dump_action_arg($1); + } + | + NUMBER + { + cfg.set_dump_action_arg($1); + } + ; nptv6token: INT_PREFIX NETWORK6 | diff --git a/libfwparser/token.l b/libfwparser/token.l index 584bce68..1bdda844 100644 --- a/libfwparser/token.l +++ b/libfwparser/token.l @@ -109,6 +109,7 @@ allow|accept|pass return ipfw::fw_parser_t::make_ALLOW(*ploc); permit return ipfw::fw_parser_t::make_ALLOW(*ploc); deny_in return ipfw::fw_parser_t::make_DENY_IN(*ploc); deny|drop return ipfw::fw_parser_t::make_DENY(*ploc); +dump return ipfw::fw_parser_t::make_DUMP(*ploc); reject return ipfw::fw_parser_t::make_T_REJECT(*ploc); unreach return ipfw::fw_parser_t::make_UNREACH(*ploc); unreach6 return ipfw::fw_parser_t::make_UNREACH6(*ploc);