Skip to content

Commit

Permalink
Merge branch 'master' into v6_address_design
Browse files Browse the repository at this point in the history
  • Loading branch information
jiceatscion authored Jan 7, 2025
2 parents aa52157 + f49d3cd commit 31b85ee
Show file tree
Hide file tree
Showing 20 changed files with 469 additions and 158 deletions.
39 changes: 16 additions & 23 deletions control/cmd/control/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ func realMain(ctx context.Context) error {
SCIONNetworkMetrics: metrics.SCIONNetworkMetrics,
SCIONPacketConnMetrics: metrics.SCIONPacketConnMetrics,
MTU: topo.MTU(),
Topology: cpInfoProvider{topo: topo},
Topology: adaptTopology(topo),
}
quicStack, err := nc.QUICStack()
if err != nil {
Expand Down Expand Up @@ -945,29 +945,22 @@ func (h *healther) GetCAHealth(ctx context.Context) (api.CAHealthStatus, bool) {
return api.Unavailable, false
}

type cpInfoProvider struct {
topo *topology.Loader
}

func (c cpInfoProvider) LocalIA(_ context.Context) (addr.IA, error) {
return c.topo.IA(), nil
}

func (c cpInfoProvider) PortRange(_ context.Context) (uint16, uint16, error) {
start, end := c.topo.PortRange()
return start, end, nil
}

func (c cpInfoProvider) Interfaces(_ context.Context) (map[uint16]netip.AddrPort, error) {
ifMap := c.topo.InterfaceInfoMap()
ifsToUDP := make(map[uint16]netip.AddrPort, len(ifMap))
for i, v := range ifMap {
if i > (1<<16)-1 {
return nil, serrors.New("invalid interface id", "id", i)
}
ifsToUDP[uint16(i)] = v.InternalAddr
func adaptTopology(topo *topology.Loader) snet.Topology {
start, end := topo.PortRange()
return snet.Topology{
LocalIA: topo.IA(),
PortRange: snet.TopologyPortRange{
Start: start,
End: end,
},
Interface: func(ifID uint16) (netip.AddrPort, bool) {
a := topo.UnderlayNextHop(ifID)
if a == nil {
return netip.AddrPort{}, false
}
return a.AddrPort(), true
},
}
return ifsToUDP, nil
}

func getCAHealth(
Expand Down
84 changes: 43 additions & 41 deletions doc/sig.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,47 @@
SCION-IP Gateway
****************

The SCION-IP Gateway (SIG) tunnels IP packets over the SCION Internet.
Introduction
============

An ingress SIG encapsulates IP packets in a SCION packet and sends it to an egress SIG determined
by the configured routing rules, where the packet is decapsulated and forwarded toward its
destination IP address.
From the perspective of IP, a SIG looks like a router.
From the perspective of SCION, a SIG is a regular application.
The SCION IP Gateway (SIG) enables IP packets to be tunneled over SCION to support communication between hosts that do not run a SCION implementation. A SIG acts as a router from the perspective of IP, whilst acting as SCION endpoint from the perspective of the SCION network. It is typically deployed inside the same AS-internal network as its non-SCION hosts, or at the edge of an enterprise network.

.. admonition:: TODO
Tunneling IP traffic over SCION requires a pair of SIGs and it involves the following steps:

1. A sender sends an IP packet towards an IP destination.

2. The IP packet reaches a SIG in the sender’s network via standard IP routing.

3. Based on the destination IP address, the source (ingress) SIG determines the destination (egress) SIG's ISD-AS endpoint address. To achieve this, SIGs are administratively configured with a set of partner ASes and discover SIGs present at these ASes. They then exchange IP prefixes. The description of that protocol is yet to be written.

4. The ingress SIG encapsulates the original IP packet within one or more SCION packets and sends them to the egress SIG. If necessary, the ingress SIG performs SCION path lookups and selects a SCION path to the egress SIG.

5. The egress SIG receives the SCION packet or packets and decapsulates the original IP packet. It then forwards the packet to the final IP destination using standard IP routing.

This protocol is designed to:

- provide independence from the underlying SCION path MTU which can increase and decrease over time.
- provide fast detection of packet loss and subsequent recovery of decapsulation for packets that weren't lost.
- support for multiple streams within a framing session such that independent packet sequences be tunneled in parallel.

SIG Overview and introduction

SIG Framing Protocol
====================

SIG Framing Protocol describes frames sent between two SIG instances.
The IP packets transported via SIG are encapsulated in SIG frames.
There can be multiple IP packets in a single SIG frame.
A single IP packet can also be split into multiple SIG frames.
IP packets are encapsulated into SIG frames, which are sent as SCION/UDP datagrams.

SIG traffic can be sent over multiple SIG sessions. SIG uses different
sessions to transport different classes of traffic (e.g. priority vs. normal.)
There may be multiple IP packets in a single SIG frame, and a single IP packet may be split into multiple SIG frames.

Within each session there may be multiple streams. Streams are useful to
distinguish between traffic sent by different SIG instances. For example,
if SIG is restarted, it will create a new stream ID for each session. That way,
the peer SIG will know that the new frame with a new stream ID does not
carry trailing part of the unfinished IP packet from a different stream.
The ingress SIG initiates unidirectional packet flows to the egress SIG simply by sending the corresponding SIG frames. There is no handshake. The egress SIG, should it accept the traffic, instantiates the necessary resources on-demand to process each flow. Each such flow forms an independent sequence of packets (a stream) ordered by an incrementing sequence number. Between a given SIG ingress/egress pair, a (session ID, stream ID) pair uniquely identifies a stream.

Each SIG frame has a sequence number. The remote SIG uses the sequence
number to reassemble the contained IP packets.
To preserve performance, IP packets that form a sequence leave the egress SIG in the order in which they entered the ingress SIG. To that end:

- The ingress SIG encapsulates IP packets that cannot be proven independent (e.g., with the same IP 6-tuple) in the same stream.
- The ingress SIG encapsulates IP packets to a given stream in the order in which they were received.
- The ingress SIG sends all frames of a given stream over the same SCION path.
- The egress SIG reassembles and forward packets from each stream, ordered by frame sequence number and by packet within each frame.

The session ID part of the (session ID, stream ID) pair has an implementation defined meaning. Existing implementations use different session IDs for different traffic classes: the ingress SIG is responsible for assigning a traffic class. On the egress SIG side, the session ID may inform the processing of frames and enables per-class metrics.

The Stack
---------
Expand All @@ -57,9 +67,9 @@ Each SIG frame starts with SIG frame header with the following format::
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Version | Session | Index |
| Version | Session ID | Index |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Reserved (12 bits) | Stream (20 bits) |
| Reserved (12 bits) | Stream ID (20 bits) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ Sequence number +
Expand All @@ -68,35 +78,27 @@ Each SIG frame starts with SIG frame header with the following format::

All fields within SIG frame header are in network byte order.

- The ``Version`` field indicates the SIG framing version. It must be set to zero.

- The ``Session`` field indicates the SIG session to be used.

- The ``Index`` field is the byte offset of the first beginning of an IP packet
within the payload. If no IP packet starts in the payload, for example, if
the frame contains only a trailing part of an IP packet, the field must be set
to 0xFFFF.

- The ``Reserved`` field is reserved and must be set to zero.

- The ``Stream`` field, along with the session identifies a unique sequence of
SIG frames.
- ``Version`` (8 bits) indicates the SIG framing version. It MUST be set to zero if following this specification.
- ``Session ID`` (8 bits) identifies a tunneling session between a pair of SIGs.
- ``Index`` (16 bits) is the byte offset of the first beginning of an IP packet within the payload. If no IP packet starts in the payload, e.g. if the frame contains only the middle or trailing part of an IP packet, the field MUST be set to 0xFFFF.
- ``Reserved`` (12 bits): it MUST be set to zero.
- ``Stream ID`` (20 bits), along with the session, it identifies a unique sequence of SIG frames. Frames from the same stream are, on the egress SIG, put into the same reassembly queue. There may be multiple streams per session.
- ``Sequence Number`` (64 bits) indicates the position of the frame within a stream. Consecutive frames of a given stream have consecutive sequence numbers. IP packets split among multiple frames are re-assembled by concatenating the payloads of consecutive frames.

- The ``Sequence number`` field indicates a position of the frame within a
stream. Consecutive frames can be used to reassemble IP packets split among
multiple frames.
A SIG MAY drop frames. In the current implementation, the egress SIG does not buffer frames that are received out-ot-order. Instead it drops any out-of-order and following frames until it finds the begining of a new encapsulated IP packet.

SIG frame payload
-----------------

SIG frame payload may contain multiple IPv4 or IPv6 packets, or parts
The SIG frame payload may contain multiple IPv4 or IPv6 packets, or parts
thereof. No other types of packets can be encapsulated. The packets are
placed one directly after another, with no padding.
Multicast traffic is not supported yet.

SIG uses IPv4/6 "payload length" field to determine the size of the packet.
To make the processing easier, it is required that the fixed part of the IP header
is in the frame where the IP packet begins. In other words, the initial fragment
of an IPv4 packet must be at least 20 bytes long. Initial fragment of an IPv6
of an IPv4 packet must be at least 20 bytes long. The initial fragment of an IPv6
packet must be at least 40 bytes long.

Example
Expand Down
16 changes: 11 additions & 5 deletions gateway/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,10 +240,16 @@ func (g *Gateway) Run(ctx context.Context) error {
// *********************************************
// Initialize base SCION network information: IA
// *********************************************
localIA, err := g.Daemon.LocalIA(context.Background())
topoReloader, err := daemon.NewReloadingTopology(ctx, g.Daemon)
if err != nil {
return serrors.Wrap("unable to learn local ISD-AS number", err)
return serrors.Wrap("loading topology", err)
}
topo := topoReloader.Topology()
go func() {
defer log.HandlePanic()
topoReloader.Run(ctx, 10*time.Second)
}()
localIA := topo.LocalIA
logger.Info("Learned local IA from SCION Daemon", "ia", localIA)

// *************************************************************************
Expand Down Expand Up @@ -299,7 +305,7 @@ func (g *Gateway) Run(ctx context.Context) error {
ProbesSendErrors: probesSendErrors,
SCMPErrors: g.Metrics.SCMPErrors,
SCIONPacketConnMetrics: g.Metrics.SCIONPacketConnMetrics,
Topology: g.Daemon,
Topology: topo,
},
PathUpdateInterval: PathUpdateInterval(ctx),
PathFetchTimeout: 0, // using default for now
Expand Down Expand Up @@ -409,7 +415,7 @@ func (g *Gateway) Run(ctx context.Context) error {
// scionNetworkNoSCMP is the network for the QUIC server connection. Because SCMP errors
// will cause the server's accepts to fail, we ignore SCMP.
scionNetworkNoSCMP := &snet.SCIONNetwork{
Topology: g.Daemon,
Topology: topo,
// Discard all SCMP propagation, to avoid accept/read errors on the
// QUIC server/client.
SCMPHandler: snet.SCMPPropagationStopper{
Expand Down Expand Up @@ -472,7 +478,7 @@ func (g *Gateway) Run(ctx context.Context) error {
// scionNetwork is the network for all SCION connections, with the exception of the QUIC server
// and client connection.
scionNetwork := &snet.SCIONNetwork{
Topology: g.Daemon,
Topology: topo,
SCMPHandler: snet.DefaultSCMPHandler{
RevocationHandler: revocationHandler,
SCMPErrors: g.Metrics.SCMPErrors,
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ require (
go.uber.org/zap v1.27.0
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
golang.org/x/crypto v0.31.0
golang.org/x/net v0.25.0
golang.org/x/net v0.33.0
golang.org/x/sync v0.10.0
golang.org/x/sys v0.28.0
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -354,8 +354,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand Down
4 changes: 2 additions & 2 deletions go_deps.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -1602,8 +1602,8 @@ def go_deps():
go_repository(
name = "org_golang_x_net",
importpath = "golang.org/x/net",
sum = "h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=",
version = "v0.25.0",
sum = "h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=",
version = "v0.33.0",
)
go_repository(
name = "org_golang_x_oauth2",
Expand Down
4 changes: 2 additions & 2 deletions licenses/data/org_golang_x_net/LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Copyright 2009 The Go Authors.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
Expand All @@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer.
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
* Neither the name of Google LLC nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

Expand Down
18 changes: 17 additions & 1 deletion pkg/daemon/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
load("//tools/lint:go.bzl", "go_library")
load("//tools/lint:go.bzl", "go_library", "go_test")

go_library(
name = "go_default_library",
Expand All @@ -7,6 +7,7 @@ go_library(
"daemon.go",
"grpc.go",
"metrics.go",
"topology.go",
],
importpath = "github.com/scionproto/scion/pkg/daemon",
visibility = ["//visibility:public"],
Expand All @@ -15,6 +16,7 @@ go_library(
"//pkg/daemon/internal/metrics:go_default_library",
"//pkg/drkey:go_default_library",
"//pkg/grpc:go_default_library",
"//pkg/log:go_default_library",
"//pkg/metrics:go_default_library",
"//pkg/private/ctrl/path_mgmt:go_default_library",
"//pkg/private/prom:go_default_library",
Expand All @@ -27,7 +29,21 @@ go_library(
"//pkg/snet/path:go_default_library",
"//private/topology:go_default_library",
"@org_golang_google_grpc//:go_default_library",
"@org_golang_google_grpc//credentials/insecure:go_default_library",
"@org_golang_google_protobuf//types/known/emptypb:go_default_library",
"@org_golang_google_protobuf//types/known/timestamppb:go_default_library",
],
)

go_test(
name = "go_default_test",
srcs = ["topology_test.go"],
deps = [
":go_default_library",
"//pkg/addr:go_default_library",
"//pkg/daemon/mock_daemon:go_default_library",
"//pkg/snet:go_default_library",
"@com_github_golang_mock//gomock:go_default_library",
"@com_github_stretchr_testify//assert:go_default_library",
],
)
14 changes: 7 additions & 7 deletions pkg/daemon/grpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"time"

"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/protobuf/types/known/emptypb"
"google.golang.org/protobuf/types/known/timestamppb"

Expand Down Expand Up @@ -48,15 +49,14 @@ type Service struct {
}

func (s Service) Connect(ctx context.Context) (Connector, error) {
a, err := net.ResolveTCPAddr("tcp", s.Address)
conn, err := grpc.NewClient(s.Address,
grpc.WithTransportCredentials(insecure.NewCredentials()),
libgrpc.UnaryClientInterceptor(),
libgrpc.StreamClientInterceptor(),
)
if err != nil {
s.Metrics.incConnects(err)
return nil, serrors.Wrap("resolving addr", err)
}
conn, err := libgrpc.SimpleDialer{}.Dial(ctx, a)
if err != nil {
s.Metrics.incConnects(err)
return nil, serrors.Wrap("dialing", err)
return nil, serrors.Wrap("creating client", err)
}
s.Metrics.incConnects(nil)
return grpcConn{conn: conn, metrics: s.Metrics}, nil
Expand Down
Loading

0 comments on commit 31b85ee

Please sign in to comment.