Skip to content

Commit

Permalink
chore: add tests for the dscp module
Browse files Browse the repository at this point in the history
  • Loading branch information
sakateka committed Feb 10, 2025
1 parent 576e1aa commit c8e964f
Show file tree
Hide file tree
Showing 10 changed files with 259 additions and 28 deletions.
4 changes: 2 additions & 2 deletions common/checksum.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

#include <stdint.h>

inline uint16_t
static inline uint16_t
csum_plus(uint16_t val0, uint16_t val1) {
uint16_t sum = val0 + val1;

Expand All @@ -12,7 +12,7 @@ csum_plus(uint16_t val0, uint16_t val1) {
return sum;
}

inline uint16_t
static inline uint16_t
csum_minus(uint16_t val0, uint16_t val1) {
uint16_t sum = val0 - val1;

Expand Down
6 changes: 6 additions & 0 deletions lib/dataplane/module/module.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ packet_front_switch(struct packet_front *packet_front) {
packet_list_init(&packet_front->output);
}

static inline void
packet_front_pass(struct packet_front *packet_front) {
packet_front->output = packet_front->input;
packet_list_init(&packet_front->input);
}

struct module;
struct module_config;

Expand Down
15 changes: 8 additions & 7 deletions lib/dataplane/packet/dscp.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "dscp.h"
#include <rte_ip.h>

#include "checksum.h"
#include "dscp.h"

int
dscp_mark_v4(struct rte_ipv4_hdr *ip4_hdr, struct dscp_config config) {
Expand All @@ -11,9 +13,8 @@ dscp_mark_v4(struct rte_ipv4_hdr *ip4_hdr, struct dscp_config config) {

uint16_t checksum = ~rte_be_to_cpu_16(ip4_hdr->hdr_checksum);
checksum = csum_minus(checksum, mark);
uint8_t new_mark = config.tc & DSCP_MARK_MASK;
uint8_t new_mark = config.mark << DSCP_MARK_SHIFT;
checksum = csum_plus(checksum, new_mark);

ip4_hdr->hdr_checksum = ~rte_cpu_to_be_16(checksum);

uint8_t ecn = ip4_hdr->type_of_service & DSCP_ECN_MASK;
Expand All @@ -28,10 +29,10 @@ get_ipv6_tc(rte_be32_t vtc_flow) {
}

static inline rte_be32_t
set_ipv6_tc(rte_be32_t vtc_flow, uint32_t dscp) {
uint32_t v = rte_cpu_to_be_32(dscp << RTE_IPV6_HDR_TC_SHIFT);
set_ipv6_tc(rte_be32_t vtc_flow, uint32_t tc) {
// Shift by the length of the Flow Label - 20-bit.
uint32_t v = rte_cpu_to_be_32(tc << RTE_IPV6_HDR_TC_SHIFT);
vtc_flow &= ~rte_cpu_to_be_32(RTE_IPV6_HDR_TC_MASK);

return (v | vtc_flow);
}

Expand All @@ -43,7 +44,7 @@ dscp_mark_v6(struct rte_ipv6_hdr *ip6_hdr, struct dscp_config config) {
// do not remark
return -1;
}
uint8_t new_mark = config.tc & DSCP_MARK_MASK;
uint8_t new_mark = config.mark << DSCP_MARK_SHIFT;
ip6_hdr->vtc_flow = set_ipv6_tc(ip6_hdr->vtc_flow, new_mark);
return 0;
}
16 changes: 11 additions & 5 deletions lib/dataplane/packet/dscp.h
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
#include <stdint.h>
#pragma once

#include <rte_ip.h>
#include <stdint.h>

#define DSCP_MARK_NEVER 0
#define DSCP_MARK_DEFAULT 1
#define DSCP_MARK_ALWAYS 2

#define DSCP_MARK_MASK 0xFC
#define DSCP_MARK_SHIFT 2
#define DSCP_ECN_MASK 0x03

// #include <rte_ip.h>
struct rte_ipv4_hdr;
struct rte_ipv6_hdr;

struct dscp_config {
uint8_t flag;
uint8_t tc;
uint8_t mark;
};

int
dscp_mark_v4(struct rte_ipv4_hdr *header, struct dscp_config config);
dscp_mark_v4(struct rte_ipv4_hdr *ip4_hdr, struct dscp_config config);

int
dscp_mark_v6(struct rte_ipv6_hdr *header, struct dscp_config config);
dscp_mark_v6(struct rte_ipv6_hdr *ip6_hdr, struct dscp_config config);
16 changes: 4 additions & 12 deletions modules/dscp/dataplane.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,10 @@
#include <rte_ether.h>
#include <rte_ip.h>

#include "lpm.h"

#include "dataplane/module/module.h"
#include "dataplane/packet/dscp.h"
#include "dataplane/packet/packet.h"

struct dscp_module_config {
struct module_config config;

struct lpm lpm_v4;
struct lpm lpm_v6;
struct dscp_config dscp;
};

static int
dscp_handle_v4(struct dscp_module_config *config, struct packet *packet) {
struct rte_mbuf *mbuf = packet_to_mbuf(packet);
Expand Down Expand Up @@ -61,7 +51,7 @@ dscp_handle(struct dscp_module_config *config, struct packet *packet) {
return result;
}

static void
void
dscp_handle_packets(
struct module *module,
struct module_config *config,
Expand All @@ -76,10 +66,12 @@ dscp_handle_packets(
while ((packet = packet_list_pop(&packet_front->input)) != NULL
) {
dscp_handle(dscp_config, packet);
packet_list_add(&packet_front->output, packet);
}
} else {
packet_front_pass(packet_front);
}

packet_front_switch(packet_front);
return;
}

Expand Down
12 changes: 10 additions & 2 deletions modules/dscp/dataplane.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
#pragma once

#include <stdint.h>

#include "dataplane/module/module.h"
#include "dataplane/packet/dscp.h"
#include "lpm.h"

struct dscp_module_config {
struct module_config config;

struct lpm lpm_v4;
struct lpm lpm_v6;
struct dscp_config dscp;
};

struct module *
new_module_kernel();
58 changes: 58 additions & 0 deletions tests/go/modules/dscp/dscp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package dscp_test

//#cgo CFLAGS: -I../../../.. -I../../../../lib -I../../../../common
//#cgo LDFLAGS: -L../../../../build/modules/dscp -ldscp_dp
//#cgo LDFLAGS: -L../../../../build/lib/dataplane/packet -lpacket
/*
#include "lib/dataplane/packet/dscp.h"
#include "modules/dscp/dataplane.h"
uint8_t dscp_mark_never = DSCP_MARK_NEVER;
uint8_t dscp_mark_default = DSCP_MARK_DEFAULT;
uint8_t dscp_mark_always = DSCP_MARK_ALWAYS;
void
dscp_handle_packets(
struct module *module,
struct module_config *config,
struct packet_front *packet_front
);
*/
import "C"
import (
"net/netip"
"unsafe"

"tests/common"

"github.com/gopacket/gopacket"
)

var (
DSCPMarkNever uint8 = uint8(C.dscp_mark_never)
DSCPMarkAlways uint8 = uint8(C.dscp_mark_always)
DSCPMarkDefault uint8 = uint8(C.dscp_mark_default)
)

func dscpModuleConfig(prefixes []netip.Prefix, flag, dscp uint8) C.struct_dscp_module_config {
m := C.struct_dscp_module_config{
config: C.struct_module_config{},
}
lpm4, lpm6 := common.BuildLPMs(prefixes)
m.lpm_v4 = *(*C.struct_lpm)(unsafe.Pointer(&lpm4))
m.lpm_v6 = *(*C.struct_lpm)(unsafe.Pointer(&lpm6))
m.flag = C.uint8_t(flag)
m.dscp = C.uint8_t(dscp)

return m
}

func dscpHandlePackets(mc *C.struct_dscp_module_config, packets ...gopacket.Packet) common.PacketFrontResult {
payload := common.PacketsToPaylod(packets)
pinner, pf := common.PacketFrontFromPayload(payload)
common.ParsePackets(pf)
C.dscp_handle_packets(nil, &mc.config, (*C.struct_packet_front)(unsafe.Pointer(pf)))
result := common.PacketFrontToPayload(pf)
pinner.Unpin()
return result
}
149 changes: 149 additions & 0 deletions tests/go/modules/dscp/dscp_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package dscp_test

import (
"fmt"
"net"
"net/netip"
"testing"

"tests/common"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/gopacket/gopacket"
"github.com/gopacket/gopacket/layers"
"github.com/stretchr/testify/require"
)

func mark(t *testing.T, pkt gopacket.Packet, dscp uint8) gopacket.Packet {
network := pkt.NetworkLayer()
tc := dscp << 2 // ECN in the first two bits
switch network.LayerType() {
case layers.LayerTypeIPv4:
network.(*layers.IPv4).TOS = tc
case layers.LayerTypeIPv6:
network.(*layers.IPv6).TrafficClass = tc
default:
t.Logf("unexpected network layer type: %v", network.LayerType())
t.FailNow()
}
// Serialize and then Deserialize the packet so, the gopacket will recalculate checksum for us.
buf := gopacket.NewSerializeBuffer()
opts := gopacket.SerializeOptions{
FixLengths: true,
ComputeChecksums: true,
}
require.NoError(t, gopacket.SerializePacket(buf, opts, pkt))

pkt = gopacket.NewPacket(
buf.Bytes(),
layers.LayerTypeEthernet,
gopacket.Default,
)
require.Empty(t, pkt.ErrorLayer())
return pkt
}

func TestDSCP(t *testing.T) {
eth := layers.Ethernet{
SrcMAC: common.Unwrap(net.ParseMAC("00:00:00:00:00:01")),
DstMAC: common.Unwrap(net.ParseMAC("00:11:22:33:44:55")),
EthernetType: layers.EthernetTypeDot1Q,
}
eth4 := eth
eth4.EthernetType = layers.EthernetTypeIPv4
eth6 := eth
eth6.EthernetType = layers.EthernetTypeIPv6

vlan4 := layers.Dot1Q{
VLANIdentifier: 100,
Type: layers.EthernetTypeIPv4,
}
vlan6 := vlan4
vlan6.Type = layers.EthernetTypeIPv6

ip6 := layers.IPv6{
Version: 6,
NextHeader: layers.IPProtocolICMPv4,
HopLimit: 64,
SrcIP: net.IPv6zero,
DstIP: net.ParseIP("1:2:3:4::abcd"),
}
ip4 := layers.IPv4{
Version: 4,
Id: 1,
TTL: 64,
Protocol: layers.IPProtocolICMPv4,
SrcIP: net.IPv4zero,
DstIP: net.ParseIP("1.1.0.0"),
}
icmp := layers.ICMPv4{
TypeCode: layers.CreateICMPv4TypeCode(
layers.ICMPv4TypeEchoRequest,
layers.ICMPv4CodeNet,
),
}

prefixes := []netip.Prefix{
common.Unwrap(netip.ParsePrefix("1:2:3:4::/64")),
common.Unwrap(netip.ParsePrefix("1.1.0.0/32")),
}

testData := []struct {
name string
pkt gopacket.Packet
}{
{"v4", common.LayersToPacket(t, &eth4, &ip4, &icmp)},
{"v6", common.LayersToPacket(t, &eth6, &ip6, &icmp)},
{"vlan>v4", common.LayersToPacket(t, &eth, &vlan4, &ip4, &icmp)},
{"vlan>v6", common.LayersToPacket(t, &eth, &vlan6, &ip6, &icmp)},
}

cases := []struct {
name string
flag uint8
mark uint8 // DSCP value in the module config
from uint8 // original DSCP value of the packet
expt uint8 // expected DSCP value
}{
{"Never remark 0->10=10", DSCPMarkNever, 0, 10, 10},
{"Never remark 0-> 0= 0", DSCPMarkNever, 0, 0, 0},
{"Never remark 7->60=60", DSCPMarkNever, 7, 60, 60},
{"Never remark 7->70=70", DSCPMarkNever, 7, 70, 70},

{"Always mark 40-> 0=40", DSCPMarkAlways, 40, 0, 40},
{"Always mark 7 ->16= 7", DSCPMarkAlways, 7, 16, 7},
{"Always mark 10->26=10", DSCPMarkAlways, 10, 26, 10},
{"Always mark 0 ->36= 0", DSCPMarkAlways, 0, 36, 0},

{"Default mark 7 ->13=13", DSCPMarkDefault, 7, 13, 13},
{"Default mark 7 -> 0= 7", DSCPMarkDefault, 7, 0, 7},
{"Default mark 0 -> 1= 1", DSCPMarkDefault, 0, 1, 1},
{"Default mark 0 -> 0= 0", DSCPMarkDefault, 0, 0, 0},
}

for _, c := range cases {
for _, p := range testData {
t.Run(fmt.Sprintf("pkt=%s: %s", p.name, c.name), func(t *testing.T) {
pkt := mark(t, p.pkt, c.from)
t.Log("Origin packet", pkt)

m := dscpModuleConfig(prefixes, c.flag, c.mark)
result := dscpHandlePackets(&m, pkt)
require.NotEmpty(t, result.Output, "result.Output")

resultPkt := common.ParseEtherPacket(result.Output[0])
t.Log("Result packet", resultPkt)

expectedPkt := mark(t, p.pkt, c.expt)
t.Log("Expected packet", expectedPkt)

diff := cmp.Diff(expectedPkt.Layers(), resultPkt.Layers(),
cmpopts.IgnoreUnexported(layers.IPv6{}, layers.ICMPv6{}),
)
require.Empty(t, diff)
})
}
}

}
10 changes: 10 additions & 0 deletions tests/go/modules/dscp/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
sources = files(
'dscp.go',
'dscp_test.go',
)

test('dscp',
find_program('go'),
args: ['test', './...'],
workdir: meson.current_source_dir(),
)
1 change: 1 addition & 0 deletions tests/go/modules/meson.build
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
subdir('decap')
subdir('dscp')

0 comments on commit c8e964f

Please sign in to comment.