Skip to content

Commit

Permalink
hackathon: beacon sender
Browse files Browse the repository at this point in the history
  • Loading branch information
oncilla committed Dec 13, 2023
1 parent 8f7d1af commit a4396ea
Show file tree
Hide file tree
Showing 8 changed files with 287 additions and 4 deletions.
12 changes: 11 additions & 1 deletion control/beaconing/connect/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,22 @@ load("//tools/lint:go.bzl", "go_library")

go_library(
name = "go_default_library",
srcs = ["server.go"],
srcs = [
"sender.go",
"server.go",
],
importpath = "github.com/scionproto/scion/control/beaconing/connect",
visibility = ["//visibility:public"],
deps = [
"//bufgen/proto/control_plane/v1/control_planeconnect:go_default_library",
"//control/beaconing:go_default_library",
"//control/beaconing/grpc:go_default_library",
"//control/onehop:go_default_library",
"//pkg/addr:go_default_library",
"//pkg/proto/control_plane:go_default_library",
"//pkg/segment:go_default_library",
"//pkg/snet/squic:go_default_library",
"@com_connectrpc_connect//:go_default_library",
"@com_github_quic_go_quic_go//http3:go_default_library",
],
)
71 changes: 71 additions & 0 deletions control/beaconing/connect/sender.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package connect

import (
"context"
"net"
"net/http"

"connectrpc.com/connect"
"github.com/quic-go/quic-go/http3"
"github.com/scionproto/scion/bufgen/proto/control_plane/v1/control_planeconnect"
"github.com/scionproto/scion/control/beaconing"
"github.com/scionproto/scion/control/onehop"
"github.com/scionproto/scion/pkg/addr"
control_plane "github.com/scionproto/scion/pkg/proto/control_plane"
seg "github.com/scionproto/scion/pkg/segment"
"github.com/scionproto/scion/pkg/snet/squic"
)

type BeaconSenderFactory struct {
Dialer func(net.Addr) squic.EarlyDialer
}

func (f *BeaconSenderFactory) NewSender(
ctx context.Context,
dstIA addr.IA,
egIfId uint16,
nextHop *net.UDPAddr,
) (beaconing.Sender, error) {
addr := &onehop.Addr{
IA: dstIA,
Egress: egIfId,
SVC: addr.SvcCS,
NextHop: nextHop,
}
dialer := f.Dialer(addr)
return &BeaconSender{
Addr: addr.String(),
Client: &HTTPClient{
RoundTripper: &http3.RoundTripper{
Dial: dialer.DialEarly,
},
},
}, nil

}

type BeaconSender struct {
Addr string
Client *HTTPClient
}

func (s BeaconSender) Send(ctx context.Context, b *seg.PathSegment) error {
client := control_planeconnect.NewSegmentCreationServiceClient(s.Client, s.Addr)
_, err := client.Beacon(ctx, connect.NewRequest(&control_plane.BeaconRequest{
Segment: seg.PathSegmentToPB(b),
}))
return err
}

// Close closes the BeaconSender and releases all underlying resources.
func (s BeaconSender) Close() error {
return s.Client.RoundTripper.Close()
}

type HTTPClient struct {
RoundTripper *http3.RoundTripper
}

func (c HTTPClient) Do(req *http.Request) (*http.Response, error) {
return c.RoundTripper.RoundTrip(req)
}
15 changes: 15 additions & 0 deletions control/beaconing/happy/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
load("//tools/lint:go.bzl", "go_library")

go_library(
name = "go_default_library",
srcs = ["sender.go"],
importpath = "github.com/scionproto/scion/control/beaconing/happy",
visibility = ["//visibility:public"],
deps = [
"//control/beaconing:go_default_library",
"//pkg/addr:go_default_library",
"//pkg/log:go_default_library",
"//pkg/private/serrors:go_default_library",
"//pkg/segment:go_default_library",
],
)
90 changes: 90 additions & 0 deletions control/beaconing/happy/sender.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package happy

import (
"context"
"net"
"time"

"github.com/scionproto/scion/control/beaconing"
"github.com/scionproto/scion/pkg/addr"
"github.com/scionproto/scion/pkg/log"
"github.com/scionproto/scion/pkg/private/serrors"
seg "github.com/scionproto/scion/pkg/segment"
)

// BeaconSenderFactory can be used to create beacon senders.
type BeaconSenderFactory struct {
Connect beaconing.SenderFactory
Grpc beaconing.SenderFactory
}

// NewSender returns a beacon sender that can be used to send beacons to a remote CS.
func (f *BeaconSenderFactory) NewSender(
ctx context.Context,
dstIA addr.IA,
egIfId uint16,
nextHop *net.UDPAddr,
) (beaconing.Sender, error) {
connectSender, err := f.Connect.NewSender(ctx, dstIA, egIfId, nextHop)
if err != nil {
return nil, err
}
grpcSender, err := f.Grpc.NewSender(ctx, dstIA, egIfId, nextHop)
if err != nil {
return nil, err
}
return BeaconSender{
Connect: connectSender,
Grpc: grpcSender,
}, nil
}

type BeaconSender struct {
Connect beaconing.Sender
Grpc beaconing.Sender
}

func (s BeaconSender) Send(ctx context.Context, b *seg.PathSegment) error {
abortCtx, cancel := context.WithCancel(ctx)
defer cancel()

connectCh := make(chan error, 1)
grpcCh := make(chan error, 1)

go func() {
defer log.HandlePanic()
err := s.Connect.Send(abortCtx, b)
if abortCtx.Err() == nil {
log.Debug("Sent beacon via connect")
}
connectCh <- err
}()

go func() {
defer log.HandlePanic()
time.Sleep(500 * time.Millisecond)
err := s.Grpc.Send(abortCtx, b)
if abortCtx.Err() == nil {
log.Debug("Sent beacon via gRPC")
}
grpcCh <- err
}()

select {
case err := <-connectCh:
return err
case err := <-grpcCh:
return err
}
}

func (s BeaconSender) Close() error {
var errs serrors.List
if err := s.Connect.Close(); err != nil {
errs = append(errs, err)
}
if err := s.Grpc.Close(); err != nil {
errs = append(errs, err)
}
return errs.ToError()
}
1 change: 1 addition & 0 deletions control/cmd/control/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ go_library(
"//control/beaconing:go_default_library",
"//control/beaconing/connect:go_default_library",
"//control/beaconing/grpc:go_default_library",
"//control/beaconing/happy:go_default_library",
"//control/config:go_default_library",
"//control/drkey:go_default_library",
"//control/drkey/grpc:go_default_library",
Expand Down
13 changes: 11 additions & 2 deletions control/cmd/control/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,10 @@ import (
cs "github.com/scionproto/scion/control"
"github.com/scionproto/scion/control/beacon"
"github.com/scionproto/scion/control/beaconing"
"github.com/scionproto/scion/control/beaconing/connect"
beaconingconnect "github.com/scionproto/scion/control/beaconing/connect"
beaconinggrpc "github.com/scionproto/scion/control/beaconing/grpc"
"github.com/scionproto/scion/control/beaconing/happy"
"github.com/scionproto/scion/control/config"
"github.com/scionproto/scion/control/drkey"
drkeygrpc "github.com/scionproto/scion/control/drkey/grpc"
Expand Down Expand Up @@ -825,8 +827,15 @@ func realMain(ctx context.Context) error {
TrustDB: trustDB,
PathDB: pathDB,
RevCache: revCache,
BeaconSenderFactory: &beaconinggrpc.BeaconSenderFactory{
Dialer: dialer,
BeaconSenderFactory: &happy.BeaconSenderFactory{
Connect: &connect.BeaconSenderFactory{
Dialer: (&squic.EarlyDialerFactory{
Transport: quicStack.InsecureDialer.Transport,
}).NewDialer,
},
Grpc: &beaconinggrpc.BeaconSenderFactory{
Dialer: dialer,
},
},
SegmentRegister: beaconinggrpc.Registrar{Dialer: dialer},
BeaconStore: beaconStore,
Expand Down
5 changes: 4 additions & 1 deletion pkg/snet/squic/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ load("//tools/lint:go.bzl", "go_library", "go_test")

go_library(
name = "go_default_library",
srcs = ["net.go"],
srcs = [
"early.go",
"net.go",
],
importpath = "github.com/scionproto/scion/pkg/snet/squic",
visibility = ["//visibility:public"],
deps = [
Expand Down
84 changes: 84 additions & 0 deletions pkg/snet/squic/early.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright 2020 Anapaya Systems
//
// 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 squic

import (
"context"
"crypto/tls"
"errors"
mrand "math/rand"
"net"
"time"

"github.com/quic-go/quic-go"

"github.com/scionproto/scion/pkg/private/serrors"
)

type EarlyDialerFactory struct {
Transport *quic.Transport
}

func (f *EarlyDialerFactory) NewDialer(a net.Addr) EarlyDialer {
return EarlyDialer{
Transport: f.Transport,
Addr: a,
}
}

type EarlyDialer struct {
Transport *quic.Transport
Addr net.Addr
}

func (d *EarlyDialer) DialEarly(ctx context.Context, _ string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error) {
serverName := tlsCfg.ServerName
if serverName == "" {
serverName = computeServerName(d.Addr)
}

var session quic.EarlyConnection
for sleep := 2 * time.Millisecond; ctx.Err() == nil; sleep = sleep * 2 {
// Clone TLS config to avoid data races.
tlsConfig := tlsCfg.Clone()
tlsConfig.ServerName = serverName
// Clone QUIC config to avoid data races, if it exists.
var quicConfig *quic.Config
if cfg != nil {
quicConfig = cfg.Clone()
}

var err error
session, err = d.Transport.DialEarly(ctx, d.Addr, tlsConfig, quicConfig)
if err == nil {
break
}
var transportErr *quic.TransportError
if !errors.As(err, &transportErr) || transportErr.ErrorCode != quic.ConnectionRefused {
return nil, serrors.WrapStr("dialing QUIC/SCION", err)
}

jitter := time.Duration(mrand.Int63n(int64(5 * time.Millisecond)))
select {
case <-time.After(sleep + jitter):
case <-ctx.Done():
return nil, serrors.WrapStr("timed out connecting to busy server", err)
}
}
if err := ctx.Err(); err != nil {
return nil, serrors.WrapStr("dialing QUIC/SCION, after loop", err)
}
return session, nil
}

0 comments on commit a4396ea

Please sign in to comment.