-
Notifications
You must be signed in to change notification settings - Fork 156
/
svc.go
216 lines (192 loc) · 6.76 KB
/
svc.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
// Copyright 2019 ETH Zurich, 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 svc implements support for SVC Resolution.
package svc
import (
"context"
"net"
"net/netip"
"github.com/scionproto/scion/pkg/addr"
"github.com/scionproto/scion/pkg/private/common"
"github.com/scionproto/scion/pkg/private/serrors"
"github.com/scionproto/scion/pkg/snet"
)
const ErrHandler common.ErrMsg = "Unable to handle SVC request"
// Result is used to inform Handler users on the outcome of handler execution.
type Result int
const (
// Error means that the handler experience an error during processing.
Error Result = iota
// Handled means that the handler completed successfully.
Handled
// Forward means that the packet should be forwarded to the application.
Forward
)
// NewResolverPacketDispatcher creates a dispatcher service that returns
// sockets with built-in SVC address resolution capabilities.
//
// RequestHandler results during connection read operations are handled in the
// following way:
// - on error result, the error is sent back to the reader
// - on forwarding result, the packet is sent back to the app for processing.
// - on handled result, the packet is discarded after processing, and a new
// read is attempted from the connection, and the entire decision process
// repeats.
func NewResolverPacketDispatcher(d snet.PacketDispatcherService,
h RequestHandler) *ResolverPacketDispatcher {
return &ResolverPacketDispatcher{dispService: d, handler: h}
}
var _ snet.PacketDispatcherService = (*ResolverPacketDispatcher)(nil)
// ResolverPacketDispatcher is a dispatcher service that returns sockets with
// built-in SVC address resolution capabilities. Every packet received with a
// destination SVC address is intercepted inside the socket, and sent to an SVC
// resolution handler which responds back to the client.
//
// Redirected packets are not returned by the connection, so they cannot be
// seen via ReadFrom. After redirecting a packet, the connection attempts to
// read another packet before returning, until a non SVC packet is received or
// an error occurs.
type ResolverPacketDispatcher struct {
dispService snet.PacketDispatcherService
handler RequestHandler
}
func (d *ResolverPacketDispatcher) Register(ctx context.Context, ia addr.IA,
registration *net.UDPAddr, svc addr.SVC) (snet.PacketConn, uint16, error) {
registrationIP, ok := netip.AddrFromSlice(registration.IP)
if !ok {
return nil, 0, serrors.New("invalid registration IP", "ip", registration.IP)
}
c, port, err := d.dispService.Register(ctx, ia, registration, svc)
if err != nil {
return nil, 0, err
}
packetConn := &resolverPacketConn{
PacketConn: c,
source: snet.SCIONAddress{
IA: ia,
Host: addr.HostIP(registrationIP),
},
handler: d.handler,
}
return packetConn, port, err
}
// resolverPacketConn redirects SVC destination packets to SVC resolution
// handler logic.
type resolverPacketConn struct {
// PacketConn is the conn to receive and send packets.
snet.PacketConn
// source contains the address from which packets should be sent.
source snet.SCIONAddress
// handler handles packets for SVC destinations.
handler RequestHandler
}
func (c *resolverPacketConn) ReadFrom(pkt *snet.Packet, ov *net.UDPAddr) error {
for {
if err := c.PacketConn.ReadFrom(pkt, ov); err != nil {
return err
}
// XXX(scrye): destination address is guaranteed to not be nil
if pkt.Destination.Host.Type() != addr.HostTypeSVC {
// Normal packet, return to caller because data is already parsed and ready
return nil
}
svc := pkt.Destination.Host.SVC()
// Multicasts do not trigger SVC resolution logic
if svc.IsMulticast() {
return nil
}
// XXX(scrye): This might block, causing the read to wait for the
// write to go through. The solution would be to run the logic in a
// goroutine, but because UDP writes rarely block, the current
// solution should be good enough for now.
r := &Request{
Conn: c.PacketConn,
Source: c.source,
Packet: pkt,
Underlay: ov,
}
switch result, err := c.handler.Handle(r); result {
case Error:
return serrors.Wrap(ErrHandler, err)
case Forward:
return nil
default:
// Message handled, read new packet
}
}
}
// RequestHandler handles SCION packets with SVC destination addresses.
type RequestHandler interface {
// Handle replies to SCION packets with SVC destinations coming from the
// specified underlay address.
//
// Handle implementantions might panic if the destination is not an SVC
// address, so callers should perform the check beforehand.
Handle(*Request) (Result, error)
}
type Request struct {
// Source is the override value for the source address of the reply packet.
Source snet.SCIONAddress
// Conn is the connection to send the reply on. Conn must not be nil.
Conn snet.PacketConn
Packet *snet.Packet
Underlay *net.UDPAddr
}
var _ RequestHandler = (*BaseHandler)(nil)
// BaseHandler reverses a SCION packet, replaces the source address with the
// one in the struct and then sends the message on the connection.
type BaseHandler struct {
// Message is the payload data to send in the reply. Nil and zero-length
// payloads are supported.
Message []byte
}
func (h *BaseHandler) Handle(request *Request) (Result, error) {
path, err := h.reversePath(request.Packet.Path)
if err != nil {
return Error, err
}
udp, ok := request.Packet.Payload.(snet.UDPPayload)
if !ok {
return Error, serrors.New("invalid payload in request",
"expected", "UDP", "type", common.TypeOf(request.Packet.Payload))
}
replyPacket := &snet.Packet{
PacketInfo: snet.PacketInfo{
Destination: request.Packet.Source,
Source: request.Source,
Path: path,
Payload: snet.UDPPayload{
DstPort: udp.SrcPort,
SrcPort: udp.DstPort,
Payload: h.Message,
},
},
}
err = request.Conn.WriteTo(replyPacket, request.Underlay)
if err != nil {
return Error, err
}
return Handled, nil
}
func (h *BaseHandler) reversePath(path snet.DataplanePath) (snet.DataplanePath, error) {
rpath, ok := path.(snet.RawPath)
if !ok {
return nil, serrors.New("unexpected path", "type", common.TypeOf(path))
}
replyPath, err := snet.DefaultReplyPather{}.ReplyPath(rpath)
if err != nil {
return nil, serrors.WrapStr("creating reply path", err)
}
return replyPath, nil
}