Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 69 additions & 3 deletions MODULE.bazel.lock

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions acceptance/router_priority/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
load("@scion_python_deps//:requirements.bzl", "requirement")
load("//:scion.bzl", "scion_go_binary")
load("//acceptance/common:topogen.bzl", "topogen_test")

topogen_test(
name = "test",
src = "test.py",
args = [
"--executable=sender:$(location //acceptance/router_priority/sender)",
],
data = [
"tc_setup.sh",
"//acceptance/router_priority/sender",
],
topo = "//topology:tiny.topo",
deps = [
requirement("prometheus-client"),
requirement("requests"),
],
)
86 changes: 86 additions & 0 deletions acceptance/router_priority/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Border Router Forwarding Priority Test

The test ensures that the priorities at the border router work as expected:
this means, that the packets flagged as priority are forwarded without losses,
with some caveats:
- All packets incoming to an AS can be read and processed by the border router.
- Any possible packet drops are due to lack of bandwidth on the egress interface
(this is a consequence of the previous point).
- Priority traffic does not exceed the capacity of the egress interface.

Under these conditions, the packet prioritization done in the border router should
prevent any packet drops for priority traffic.

The test checks for BFD packet drops, which are always flagged as priority,
in a controlled scenario:
- The border router has enough processing capacity:
- The test will limit the capacity of the network interfaces to a small bandwidth.
- The test uses the very small `Tiny.topo` topology,
which needs a very small number of processes, which in turn do not consume much CPU.
- The priority traffic does not exceed the egress capacity:
- The amount of BFD traffic is configured in the test to be very small.

This test uses the tiny topology:
```text
+-----------------+
| |
| AS 1-ff00:0:110 |
| |
+-----------------+
|
+--------------------+--------------------+
| |
| <---- capped interface |
+-----------------+ +-----------------+
| | | |
| AS 1-ff00:0:111 | | AS 1-ff00:0:112 |
| | | |
| tester-111 | | tester-112 |
| | | |
+-----------------+ +-----------------+
```

## Components of the test
Out of the box from the tiny topology we have:
- 4 SCION border routers (the AS 1-ff00:0:110 has two BRs).
- 3 SCION control services.
- 3 SCION dispatchers for the control services.
- 3 SCION daemons.
- 3 tester applications.
- 3 SCION dispatchers for the tester applications.

For a total of 19 docker containers.
Additionally, the tiny topology defines 5 networks. Here is the list and the containers using them:
- scn_000: Inter-AS 110 <-> 111
- `br-1 @ 1-ff00:0:110`
- `br-1 @ 1-ff00:0:111` <--- This is the one whose capacity we want to limit.
- scn_001: Intra-AS 110
- `br-1 @ 1-ff00:0:110`
- `br-2 @ 1-ff00:0:110`
- `daemon @ 1-ff00:0:110`
- `disp-cs-1 @ 1-ff00:0:110`
- `disp-tester @ 1-ff00:0:110`
- scn_002: Intra-AS 111
- `br-1 @ 1-ff00:0:111`
- `daemon @ 1-ff00:0:111`
- `disp-cs-1 @ 1-ff00:0:111`
- `disp-tester @ 1-ff00:0:111`
- scn_003: Inter-AS 110 <-> 112
- `br-2 @ 1-ff00:0:110`
- `br-1 @ 1-ff00:0:112`
- scn_004: Intra-AS 112
- `br-1 @ 1-ff00:0:112`
- `daemon @ 1-ff00:0:112`
- `disp-cs-1 @ 1-ff00:0:112`
- `disp-tester @ 1-ff00:0:112`

The test requires a sender binary (automatically built if run with bazel).
It is used at the tester-111 container to blast tester-112 with SCION UDP packets,
without any kind of flow control.
The regular `scion ping` does not work here, as it paces itself if responses are not received.


## How to run the test independently

1. [Set up the development environment](https://docs.scion.org/en/latest/build/setup.html)
2. `bazel test --test_output=streamed --cache_test_results=no //acceptance/router_priority:test`
20 changes: 20 additions & 0 deletions acceptance/router_priority/sender/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
load("@rules_go//go:def.bzl", "go_binary", "go_library")

go_library(
name = "go_default_library",
srcs = ["main.go"],
importpath = "github.com/scionproto/scion/acceptance/router_priority/sender",
visibility = ["//visibility:private"],
deps = [
"//pkg/daemon:go_default_library",
"//pkg/daemon/types:go_default_library",
"//pkg/snet:go_default_library",
"//pkg/snet/metrics:go_default_library",
],
)

go_binary(
name = "sender",
embed = [":go_default_library"],
visibility = ["//visibility:public"],
)
110 changes: 110 additions & 0 deletions acceptance/router_priority/sender/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// Copyright 2026 ETH Zurich
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"context"
"flag"
"fmt"
"net"
"time"

"github.com/scionproto/scion/pkg/daemon"
daemontypes "github.com/scionproto/scion/pkg/daemon/types"
"github.com/scionproto/scion/pkg/snet"
"github.com/scionproto/scion/pkg/snet/metrics"
)

// -daemon 127.0.0.19:30255 -remote 1-ff00:0:110,172.20.0.18:12345
func main() {
ctx, cancelF := context.WithTimeout(context.Background(), 10*time.Second)
defer cancelF()

var daemonAddr, localAddr, blastDuration string
var remote snet.UDPAddr
var payloadSize int

flag.StringVar(&daemonAddr, "daemon", "", "local daemon address")
flag.StringVar(&localAddr, "local", "127.0.0.1:0", "local address")
flag.Var(&remote, "remote", "address to send to")
flag.StringVar(&blastDuration, "duration", "1s", "duration of the SCION UDP blast")
flag.IntVar(&payloadSize, "payloadsize", 1100, "size of the payload in bytes")
flag.Parse()

// Duration:
duration, err := time.ParseDuration(blastDuration)
panicOnError(err)

// Find daemon.
daemonConn, err := daemon.NewService(daemonAddr).Connect(ctx)
panicOnError(err)

fmt.Printf("remote: %s\n", &remote)

// Where am I?
localIA, err := daemonConn.LocalIA(ctx)
panicOnError(err)
// local := net.UDPAddrFromAddrPort(
// netip.AddrPortFrom(netip.AddrFrom4([4]byte{127, 0, 0, 1}),
// 0))
local, err := net.ResolveUDPAddr("udp", localAddr)
panicOnError(err)
fmt.Printf("On local IA: %s, host: %s\n", localIA, local)

// Network:
metrics := metrics.NewSCIONPacketConnMetrics()
topo, err := daemon.LoadTopology(ctx, daemonConn)
panicOnError(err)
network := &snet.SCIONNetwork{
SCMPHandler: snet.DefaultSCMPHandler{
RevocationHandler: daemon.RevHandler{Connector: daemonConn},
SCMPErrors: metrics.SCMPErrors,
},
PacketConnMetrics: metrics,
Topology: topo,
}

// Get paths.
paths, err := daemonConn.Paths(ctx, remote.IA, localIA, daemontypes.PathReqFlags{})
panicOnError(err)
if len(paths) == 0 {
panic("no paths")
}
path := paths[0]
remote.Path = path.Dataplane()
remote.NextHop = path.UnderlayNextHop()

// Blast the remote endpoint with packets.
conn, err := network.Dial(ctx, "udp", local, &remote)
panicOnError(err)
payload := make([]byte, payloadSize)
var packetCount int
t0 := time.Now()
for packetCount = 0; ; packetCount++ {
n, err := conn.Write(payload)
panicOnError(err)
if n != len(payload) {
panic(fmt.Errorf("only sent %d out of %d", n, len(payload)))
}
if time.Since(t0) >= duration {
break
}
}
fmt.Printf("sent %d packets (%d bytes payload) in total to %s.\n",
packetCount, len(payload), remote.String())
}

func panicOnError(err error) {
if err != nil {
panic(err)
}
}
12 changes: 12 additions & 0 deletions acceptance/router_priority/tc_setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/bash
set -ex

NETWORK=$1
RATE=$2

veths=$(bridge link show | awk "/$NETWORK/{print \$2}")
for veth in $veths
do
echo $veth
tc qdisc add dev $veth root tbf rate $RATE latency 1ms burst 50kb mtu 10000
done
Loading
Loading