Skip to content

Commit

Permalink
write packets according to a certain "dump" rule into IPC Shared Memory
Browse files Browse the repository at this point in the history
  • Loading branch information
saushew committed Sep 21, 2023
1 parent 5f9edf8 commit 4cd4494
Show file tree
Hide file tree
Showing 66 changed files with 1,434 additions and 64 deletions.
200 changes: 196 additions & 4 deletions autotests/src/autotest.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <arpa/inet.h>
#include <pcap.h>
#include <string.h>
#include <sys/shm.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
Expand Down Expand Up @@ -65,12 +66,24 @@ eResult tAutotest::init(const std::string& binaryPath,
{
(void)binaryPath;
this->dumpPackets = dumpPackets;
this->configFilePaths = configFilePaths;

if (auto ret = initSockets(); ret != eResult::success)
{
return ret;
}

eResult result = eResult::success;
if (auto ret = initSharedMemory(); ret != eResult::success)
{
return ret;
}

this->configFilePaths = configFilePaths;
return eResult::success;
}

dataPlaneConfig = dataPlane.getConfig();
eResult tAutotest::initSockets()
{
dataPlaneConfig = dataPlane.getConfig();

for (const auto& port : std::get<0>(dataPlaneConfig))
{
Expand Down Expand Up @@ -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<key_t, void*> 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()
Expand Down Expand Up @@ -1188,6 +1259,8 @@ void tAutotest::mainThread()
fflush(stdout);
fflush(stderr);

fflushSharedMemory();

try
{
{
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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>();
std::string expectFilePath = path + "/" + yamlDump["expect"].as<std::string>();
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);
}
10 changes: 10 additions & 0 deletions autotests/src/autotest.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -85,11 +91,15 @@ class tAutotest
interface::controlPlane controlPlane;

common::idp::getConfig::response dataPlaneConfig;
common::idp::get_shm_info::response dataPlaneSharedMemory;

std::map<std::string, ///< interfaceName
int>
pcaps;

std::tuple<size_t, void*> rawShmInfo;
std::map<std::string, common::bufferring> dumpRings;

std::vector<std::thread> threads;
volatile bool flagStop;

Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
10 changes: 10 additions & 0 deletions autotests/units/001_one_port/067_dump_after_term/autotest.yaml
Original file line number Diff line number Diff line change
@@ -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
42 changes: 42 additions & 0 deletions autotests/units/001_one_port/067_dump_after_term/controlplane.conf
Original file line number Diff line number Diff line change
@@ -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"
}
}
}
}
}
11 changes: 11 additions & 0 deletions autotests/units/001_one_port/067_dump_after_term/firewall.txt
Original file line number Diff line number Diff line change
@@ -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

39 changes: 39 additions & 0 deletions autotests/units/001_one_port/067_dump_after_term/gen.py
Original file line number Diff line number Diff line change
@@ -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),
])
Binary file not shown.
Binary file not shown.
Binary file not shown.
10 changes: 10 additions & 0 deletions autotests/units/001_one_port/067_dump_default/autotest.yaml
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit 4cd4494

Please sign in to comment.