Windows as a SCION Host

SCION is usually deployed on Linux hosts. Since the open-source version of SCION is implemented in Go, other platforms such as Windows work as well. There are some limitations, but support is good enough to run SCION applications. This guide will walk you through running a Windows 11 PC as an end host in a SCION network.

Connecting to SCIERA

A simple and quick way to connect to SCIERA points is the scion-orchestrator. If all you need is a one-off connection to a SCIERA network and you institution is in the supported list, you can run SCION directly using scion-orchestrator. Just follow the instructions in the README. This guide is about installing SCION on Windows manually for a more permanent setup that is currently not possible with scion-orchestrator.

However, scion-orchestrator is still the easiest way to obtain the configuration files needed by every SCION host. The minimum configuration requires a file called topology.json that contains the IP addresses of local SCION routers among other critical information, and a set of certificates known as the Trust Root Certificate (TRC). The scion-orchestrator performs a bootstrapping process to locate and download these files. If you run the orchestrator and everything goes well, you will find the topology.json file in its config directory config/ and one or more *.trc files in config/certs.

Instead of running the orchestrator you can also grab the host configuration from a SCION bootstrapping server manually. Ask an administrator for the address of the bootstrapping server in your network or look it up in the scion-orchestrator-releases repository. Assuming the bootstrap server is at 127.0.0.1:8000, you can simply go to http://127.0.0.1:8000/topology in a webbrowser to obtain topology.json. A list of available certificates is returned from http://127.0.0.1:8000/trcs. Each certificate has an associated ISD, base number, and serial number. The certificates are available at http://127.0.0.1:8000/trcs/isd71-b1-s2 for a hypothetical TRC for ISD 71, with base number 1, and serial number 2. Save the certificates to files named ISD71-B1-S2.trc and so on. In practice you don’t have to download all TRCs offered, you only need to latest one from your ISD which is ISD71 in SCIERA’s case. Here is a copy of SCIERA's current TRC (base 1, serial 4).

It really doesn’t matter how you obtain the topology.json and latest TRC, as long as you have them. TRCs are updated from time to time (maybe once a year in SCIERA) and you will have to download the newest one when the old certificates expire. scion-orchestrator can automate this process, but this is not fully supported on Windows yet.

Example of how topology.json can look like:

{
  "attributes": [],
  "isd_as": "1-ff00:0:111",
  "mtu": 1472,
  "test_dispatcher": true,
  "dispatched_ports": "31000-32767",
  "control_service": {
    "cs1-ff00_0_111-1": {
      "addr": "127.0.0.18:31006"
    }
  },
  "discovery_service": {
    "cs1-ff00_0_111-1": {
      "addr": "127.0.0.18:31006"
    }
  },
  "border_routers": {
    "br1-ff00_0_111-1": {
      "internal_addr": "127.0.0.17:31008",
      "interfaces": {
        "41": {
          "underlay": {
            "local": "127.0.0.5:50000",
            "remote": "127.0.0.4:50000"
          },
          "isd_as": "1-ff00:0:110",
          "link_to": "parent",
          "mtu": 1280
        }
      }
    }
  }
}

Compile SCION

Since we’re running a SCION end host on Windows and not a router, there are only two executables needed, daemon.exe and scion.exe. “daemon” is a background process that manages SCION paths. We’ll install it as a service later in this guide. “scion” is a command line tool that offers ping and traceroute functionality among other functions. SCION hosts usually also run a “dispatcher”, but this component relies on exotic socket features and does not compile on Windows. Fortunately the dispatcher is no longer strictly required and is only kept to provide backwards compatibility. It also enables SCION hosts to respond to SCION ping messages, but this is also not strictly necessary to run applications.

In order to compile SCION you need to have a recent version of Go installed. You also need git to clone the SCION repository. Check out a recent commit from the SCION repository and build the executables

go build -o ..\bin\daemon.exe .\daemon\cmd\daemon\
go build -o ..\bin\scion.exe .\scion\cmd\scion\

Note that SCION release v0.12.0 is not recent enough for this guide. Use the current master branch or v0.13.0 when it releases.

Here is a PowerShell script that automates the process:

param (
    [string]$Repository = "https://github.com/scionproto/scion.git",
    [string]$Tag = "master",
    [string]$Path = "scion"
)
Set-StrictMode -Version Latest

function Clone-Scion {
    param (
        [string]$Repository = "https://github.com/scionproto/scion.git",
        [string]$Tag = "master",
        [string]$Path
    )
    git clone --branch $Tag $Repository $Path
}

function Build-Scion {
    param (
        [string]$Path
    )
    $out = "$(Get-Location)\bin"
    $env:CGO_ENABLED = 0
    Push-Location $Path
    try {
        New-Item -Path $out -ItemType "directory" -Force | Out-Null
        # go build -o $out .\control\cmd\control\
        # Check-Error
        go build -o $out .\daemon\cmd\daemon\
        Check-Error
        # Dispatcher doesn't build on Windows
        #go build -o $out .\dispatcher\cmd\dispatcher\
        #Check-Error
        # go build -o $out .\router\cmd\router\
        # Check-Error
        go build -o $out .\scion\cmd\scion\
        Check-Error
        # go build -o $out .\scion-pki\cmd\scion-pki\
        # Check-Error
    } finally {
        Remove-Item Env:\CGO_ENABLED
        Pop-Location
    }
}

function Check-Error {
    if ($lastexitcode -ne 0) {
        throw "Go build failed"
    }
}

try {
    if (-Not (Test-Path -Path $Path)) {
        Clone-Scion -Path $Path -Repository $Repository -Tag $Tag
    }
    Build-SCION -Path $Path
    Copy-Item -Path bin -Destination .\integration -Recurse -Force
} catch {
    Write-Host "Error building SCION:"
    Write-Host $_
}

Install the resulting executables in %ProgramFiles%\scion and add this directory to the PATH environment variable.

Install the SCION Configuration

The recommended location of the SCION configuration in %ProgramData%\scion. Create a directory structure the look as follows:

scion
├── config
│   ├── certs
│   │   ├── ISD64-B1-S8.trc
│   │   ├── ISD71-B1-S1.trc
│   │   ├── ISD71-B1-S2.trc
│   │   ├── ISD71-B1-S3.trc
│   │   ├── ISD71-B1-S4.trc
│   │   └── ...
│   ├── sd.toml
│   └── topology.json
├── log
│   └── sd.log
└── run
    ├── sd.path.db
    └── sd.trust.db

The files in scion/log/ and scion/run/ will be created by the daemon once it runs, just create empty directories for now.

sd.toml is the SCION daemon’s configuration file. At minimum it should look like this:

[general]
id = "sd"
config_dir = "C:/ProgramData/scion/config"

[path_db]
connection = "C:/ProgramData/scion/run/sd.path.db"

[trust_db]
connection = "C:/ProgramData/scion/run/sd.trust.db"

[log.console]
level = "info"

It is important that topology.json and certs/ are both in config_dir, beyond that you can change the paths if you like.

Install the SCION Daemon as a Service

We’ll install the daemon as a service that can run automatically on system boot.

SCION services make use of the Windows event log through Go’s eventlog package. Log messages written by the daemon have the event source SCION Daemon using %SystemRoot%\System32\EventCreate.exe as the event message file. To add the SCION Daemon to the Application log, you can import the following registry file or use the program provided at the end of this section:

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\Application\SCION Daemon]
"CustomSource"=dword:00000001
"EventMessageFile"=hex(2):25,00,53,00,79,00,73,00,74,00,65,00,6d,00,52,00,6f,\
  00,6f,00,74,00,25,00,5c,00,53,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,\
  5c,00,45,00,76,00,65,00,6e,00,74,00,43,00,72,00,65,00,61,00,74,00,65,00,2e,\
  00,65,00,78,00,65,00,00,00
"TypesSupported"=dword:00000007

Installing the daemon as a service can be done through the sc.exe command line tool or the PowerShell cmdlet New-Service. There is also a Go program at the end of the section performing the same action. Remember to execute the commands from an elevated command prompt.

sc.exe example (replace demand with auto for start on boot):

$COMMAND="${env:ProgramFiles}\scion\daemon.exe"
$CONFIG="${env:ProgramData}\scion\config\sd.toml"
$LOGFILE="${env:ProgramData}\scion\log\sd.log"
sc.exe create "SCION Daemon" start=demand binPath="$COMMAND --config $CONFIG --logfile $LOGFILE"

New-Service example (replace Manual with Automatic for start on boot):

$COMMAND="${env:ProgramFiles}\scion\daemon.exe"
$CONFIG="${env:ProgramData}\scion\config\sd.toml"
$LOGFILE="${env:ProgramData}\scion\log\sd.log"
$DESC="Facilitates communication of SCION applications with the control service."
New-Service -Name "SCION Daemon" -StartupType "Manual" -BinaryPathName "$COMMAND --config $CONFIG --logfile $LOGFILE" -Description "$DESC"

Once the service is installed you can also manage it from the service control panel. You can remove the service again using sc.exe or the cmdlet Remove-Service (requires at least PowerShell 6.0):

sc.exe delete "SCION Daemon"
Remove-Service -Name "SCION Daemon"

Automatic Service Installation

The steps above are also carried out by the following Go program:

package main

import (
    "fmt"
    "os"
    "syscall"

    "golang.org/x/sys/windows/svc"
    "golang.org/x/sys/windows/svc/eventlog"
    "golang.org/x/sys/windows/svc/mgr"
)

const ERROR_INVALID_NAME = 123

type Service struct {
    Name    string
    Exepath string
    Desc    string
    Config  string
    Logfile string
}

var services = []Service{
    {
        Name:    "SCION Daemon",
        Exepath: os.ExpandEnv("$ProgramFiles\\scion\\daemon.exe"),
        Desc:    "Facilitates communication of SCION applications with the control service.",
        Config:  os.ExpandEnv("$ProgramData\\scion\\config\\sd.toml"),
        Logfile: os.ExpandEnv("$ProgramData\\scion\\log\\sd.log"),
    },
}

func main() {
    var err error
    if len(os.Args) != 2 {
        fmt.Println("invalid number of arguments")
        return
    }
    if os.Args[1] == "install" {
        fmt.Println("Installing service")
        err = installServices()
    } else if os.Args[1] == "remove" {
        fmt.Println("Removing service")
        err = removeServices()
    } else if os.Args[1] == "start" {
        fmt.Println("Starting SCION")
        err = startServices()
    } else if os.Args[1] == "stop" {
        fmt.Println("Stopping SCION")
        err = stopServices()
    }
    if err != nil {
        fmt.Printf("error: %v\n", err)
    }
}

func installServices() error {
    m, err := mgr.Connect()
    if err != nil {
        return err
    }
    defer m.Disconnect()

    for _, service := range services {
        c := mgr.Config{
            StartType:   mgr.StartManual,
            Description: service.Desc,
        }
        s, err := m.CreateService(service.Name, service.Exepath, c,
            "--config", service.Config,
            "--logfile", service.Logfile)
        if err != nil {
            if errno, ok := err.(syscall.Errno); ok {
                if uintptr(errno) == ERROR_INVALID_NAME {
                    fmt.Printf("service %s already exists\n", service.Name)
                    continue
                }
            }
            return err
        }
        defer s.Close()

        err = eventlog.InstallAsEventCreate(service.Name, eventlog.Error|eventlog.Warning|eventlog.Info)
        if err != nil {
            s.Delete()
            return fmt.Errorf("installing event log source failed: %v", err)
        }
    }

    return nil
}

func removeServices() error {
    return applyToServices(func(s *mgr.Service) error {
        err := eventlog.Remove(s.Name)
        if err != nil {
            return fmt.Errorf("removing event log source failed: %v", err)
        }
        err = s.Delete()
        if err != nil {
            return err
        }
        return nil
    })
}

func startServices() error {
    return applyToServices(func(s *mgr.Service) error {
        return s.Start()
    })
}

func stopServices() error {
    return applyToServices(func(s *mgr.Service) error {
        _, err := s.Control(svc.Stop)
        return err
    })
}

func applyToServices(f func(*mgr.Service) error) error {
    m, err := mgr.Connect()
    if err != nil {
        return err
    }
    defer m.Disconnect()

    for _, service := range services {
        s, err := m.OpenService(service.Name)
        if err != nil {
            fmt.Printf("service %s not found\n", service.Name)
            continue
        }
        defer s.Close()
        err = f(s)
        if err != nil {
            return err
        }
    }
    return nil
}

Use as follows:

go build -o ./svcctrl.exe
.\svcctrl.exe install # install
.\svcctrl.exe start   # start service
.\svcctrl.exe stop    # stop service
.\svcctrl.exe remove  # uninstall

Running the Service

After you have installed the daemon service, start it from the command line or the control panel. In the event viewer (eventvwr) you should see a message in the Application protocol from SCION Daemon that says “Service started”. If not check the logs to see what went wrong.

As a next step you can try the scion program. Running scion address in a terminal window prints your local SCION address. scion showpaths <ISD-ASN> lists available paths to a remote AS. The output should look like this:

> scion showpaths 71-2:0:48
Available paths to 71-2:0:48
4 Hops:
[0] Hops: [71-2:0:4a 2>31 71-20965 7>3 71-2:0:35 6>1 71-2:0:48] MTU: 1452 NextHop: 141.44.25.151:30001 Status: alive LocalIP: 10.44.25.3
[1] Hops: [71-2:0:4a 2>31 71-20965 8>9 71-2:0:35 6>1 71-2:0:48] MTU: 1452 NextHop: 141.44.25.151:30001 Status: alive LocalIP: 10.44.25.3
[2] Hops: [71-2:0:4a 2>31 71-20965 9>8 71-2:0:35 6>1 71-2:0:48] MTU: 1452 NextHop: 141.44.25.151:30001 Status: alive LocalIP: 10.44.25.3
[3] Hops: [71-2:0:4a 2>31 71-20965 12>11 71-2:0:35 6>1 71-2:0:48] MTU: 1452 NextHop: 141.44.25.151:30001 Status: alive LocalIP: 10.44.25.3
[4] Hops: [71-2:0:4a 2>31 71-20965 17>7 71-2:0:35 6>1 71-2:0:48] MTU: 1452 NextHop: 141.44.25.151:30001 Status: alive LocalIP: 10.44.25.3
[5] Hops: [71-2:0:4a 2>31 71-20965 18>10 71-2:0:35 6>1 71-2:0:48] MTU: 1452 NextHop: 141.44.25.151:30001 Status: alive LocalIP: 10.44.25.3

If you get a warning from the Windows Firewall about scion.exe accessing the newtwork, just click Abort. The scion command only needs outgoing connetions to make it through the Firewall, which should already be the case in the default Firewall configuration. You only need to add a firewall exception for a SCION program if you whish to run it as a server reachable to other hosts on the network. That being said, if scion showpaths gives you timeouts it might be worth checking the Firewall configuration. A useful tool for analyzing which packets make it through is Wireshark for which a SCION plugin is available. If you want the interact with SCION packets on a deeper level, there is also a SCION layer for Scapy.