Skip to content

Commit aa18eb8

Browse files
committed
bwtester: convert to pan
Add default path policy options to command line for bwtestclient. For the data channel, we want to ensure to use the path used for the control channel and stick to this, i.e. no more fail-over once connection is established. On the client side, we use the Pin policy to, well, pin the path for the data channel. On the server side, we initialize a default reply path selector with the path known from the control channel. With this approach, we stick to the original forwarding path, but the path can still be refreshed if it expires (however unlikely that is, given the short max duration of tests). We could perhaps go a bit further and use dumb selectors that always return the fixed, initial path.
1 parent a8ebd2d commit aa18eb8

File tree

3 files changed

+129
-78
lines changed

3 files changed

+129
-78
lines changed

bwtester/bwtestclient/bwtestclient.go

Lines changed: 59 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ import (
3333
"time"
3434
"unicode"
3535

36+
"inet.af/netaddr"
37+
3638
. "github.com/netsec-ethz/scion-apps/bwtester/bwtestlib"
37-
"github.com/netsec-ethz/scion-apps/pkg/appnet"
38-
"github.com/scionproto/scion/go/lib/addr"
39-
"github.com/scionproto/scion/go/lib/snet"
39+
"github.com/netsec-ethz/scion-apps/pkg/pan"
4040
)
4141

4242
const (
@@ -128,7 +128,7 @@ func parseBwtestParameters(s string, defaultPktSize int64) (BwtestParameters, er
128128
a4 = parseBandwidth(a[3])
129129
a2 = (a4 * a1) / (a3 * 8)
130130
} else {
131-
a2 = int64(defaultPktSize)
131+
a2 = defaultPktSize
132132
}
133133
} else {
134134
a2 = getPacketSize(a[1], defaultPktSize)
@@ -248,83 +248,83 @@ func getPacketCount(count string) int64 {
248248
return a3
249249
}
250250

251+
func usageErr(msg string) {
252+
printUsage()
253+
if msg != "" {
254+
fmt.Println("\nError:", msg)
255+
}
256+
os.Exit(2)
257+
}
258+
259+
func checkUsageErr(err error) {
260+
if err != nil {
261+
usageErr(err.Error())
262+
}
263+
}
264+
251265
func main() {
252266
var (
253-
serverCCAddrStr string
254-
clientBwpStr string
255-
serverBwpStr string
256-
interactive bool
257-
pathAlgo string
267+
local pan.IPPortValue
268+
serverCCAddr pan.UDPAddr
269+
clientBwpStr string
270+
serverBwpStr string
271+
interactive bool
272+
sequence string
273+
preference string
258274
)
259275

260276
flag.Usage = printUsage
261-
flag.StringVar(&serverCCAddrStr, "s", "", "Server SCION Address")
277+
flag.Var(&local, "local", "Local address")
278+
flag.Var(&serverCCAddr, "s", "Server SCION Address")
262279
flag.StringVar(&serverBwpStr, "sc", DefaultBwtestParameters, "Server->Client test parameter")
263280
flag.StringVar(&clientBwpStr, "cs", DefaultBwtestParameters, "Client->Server test parameter")
264281
flag.BoolVar(&interactive, "i", false, "Interactive path selection, prompt to choose path")
265-
flag.StringVar(&pathAlgo, "pathAlgo", "", "Path selection algorithm / metric (\"shortest\", \"mtu\")")
282+
flag.StringVar(&sequence, "sequence", "", "Sequence of space separated hop predicates to specify path")
283+
flag.StringVar(&preference, "preference", "", "Preference sorting order for paths. "+
284+
"Comma-separated list of available sorting options: "+
285+
strings.Join(pan.AvailablePreferencePolicies, "|"))
266286

267287
flag.Parse()
268288
flagset := make(map[string]bool)
269289
// record if flags were set or if default value was used
270290
flag.Visit(func(f *flag.Flag) { flagset[f.Name] = true })
271291

272292
if flag.NFlag() == 0 {
273-
// no flag was set, only print usage and exit
274-
printUsage()
275-
os.Exit(2)
293+
usageErr("")
276294
}
277-
if len(serverCCAddrStr) == 0 {
278-
printUsage()
279-
fmt.Println("\nServer address needs to be specified with -s")
280-
os.Exit(2)
295+
if !serverCCAddr.IsValid() {
296+
usageErr("server address needs to be specified with -s")
281297
}
282-
serverCCAddr, err := appnet.ResolveUDPAddr(serverCCAddrStr)
283-
Check(err)
298+
policy, err := pan.PolicyFromCommandline(sequence, preference, interactive)
299+
checkUsageErr(err)
284300

285-
var path snet.Path
286-
if interactive {
287-
path, err = appnet.ChoosePathInteractive(serverCCAddr.IA)
288-
Check(err)
289-
} else {
290-
var metric int
291-
if pathAlgo == "mtu" {
292-
metric = appnet.MTU
293-
} else if pathAlgo == "shortest" {
294-
metric = appnet.Shortest
295-
}
296-
path, err = appnet.ChoosePathByMetric(metric, serverCCAddr.IA)
297-
Check(err)
298-
}
299-
if path != nil {
300-
appnet.SetPath(serverCCAddr, path)
301-
}
302-
303-
// use default packet size when within same AS and pathEntry is not set
301+
// use default packet size when within same AS
304302
inferedPktSize := int64(DefaultPktSize)
305303
// update default packet size to max MTU on the selected path
306-
if path != nil {
304+
// TODO(matzf): evaluate policy, set pkt size to MTU of most preferred path,
305+
// append filter to policy to allow only paths with MTU >= pkt size.
306+
/*if path != nil {
307307
inferedPktSize = int64(path.Metadata().MTU)
308-
}
308+
}*/
309309
if !flagset["cs"] && flagset["sc"] { // Only one direction set, used same for reverse
310310
clientBwpStr = serverBwpStr
311311
fmt.Println("Only sc parameter set, using same values for cs")
312312
}
313313
clientBwp, err := parseBwtestParameters(clientBwpStr, inferedPktSize)
314-
Check(err)
314+
checkUsageErr(err)
315315
if !flagset["sc"] && flagset["cs"] { // Only one direction set, used same for reverse
316316
serverBwpStr = clientBwpStr
317317
fmt.Println("Only cs parameter set, using same values for sc")
318318
}
319319
serverBwp, err := parseBwtestParameters(serverBwpStr, inferedPktSize)
320-
Check(err)
320+
checkUsageErr(err)
321321
fmt.Println("\nTest parameters:")
322322
fmt.Printf("client->server: %d seconds, %d bytes, %d packets\n",
323323
int(clientBwp.BwtestDuration/time.Second), clientBwp.PacketSize, clientBwp.NumPackets)
324324
fmt.Printf("server->client: %d seconds, %d bytes, %d packets\n",
325325
int(serverBwp.BwtestDuration/time.Second), serverBwp.PacketSize, serverBwp.NumPackets)
326326

327-
clientRes, serverRes, err := runBwtest(serverCCAddr, clientBwp, serverBwp)
327+
clientRes, serverRes, err := runBwtest(local.Get(), serverCCAddr, policy, clientBwp, serverBwp)
328328
Check(err)
329329

330330
fmt.Println("\nS->C results")
@@ -334,31 +334,29 @@ func main() {
334334
}
335335

336336
// runBwtest runs the bandwidth test with the given parameters against the server at serverCCAddr.
337-
func runBwtest(serverCCAddr *snet.UDPAddr,
337+
func runBwtest(local netaddr.IPPort, serverCCAddr pan.UDPAddr, policy pan.Policy,
338338
clientBwp, serverBwp BwtestParameters) (clientRes, serverRes BwtestResult, err error) {
339339

340340
// Control channel connection
341-
ccConn, err := appnet.DialAddr(serverCCAddr)
341+
ccSelector := pan.NewDefaultSelector()
342+
ccConn, err := pan.DialUDP(context.Background(), local, serverCCAddr, policy, ccSelector)
342343
if err != nil {
343344
return
344345
}
345-
clientCCAddr := ccConn.LocalAddr().(*net.UDPAddr)
346346

347-
// Address of client data channel (DC)
348-
clientDCAddr := &net.UDPAddr{IP: clientCCAddr.IP, Port: clientCCAddr.Port + 1}
347+
dcLocal := local.WithPort(0)
349348
// Address of server data channel (DC)
350-
serverDCAddr := serverCCAddr.Copy()
351-
serverDCAddr.Host.Port = serverCCAddr.Host.Port + 1
352-
// DC ports are passed in the request
353-
clientBwp.Port = uint16(clientDCAddr.Port)
354-
serverBwp.Port = uint16(serverDCAddr.Host.Port)
349+
serverDCAddr := serverCCAddr.WithPort(serverCCAddr.Port + 1)
355350

356351
// Data channel connection
357-
dcConn, err := appnet.DefNetwork().Dial(
358-
context.TODO(), "udp", clientDCAddr, serverDCAddr, addr.SvcNone)
352+
dcConn, err := pan.DialUDP(context.Background(), dcLocal, serverDCAddr, policy, nil)
359353
if err != nil {
360354
return
361355
}
356+
clientDCAddr := dcConn.LocalAddr().(pan.UDPAddr)
357+
// DC ports are passed in the request
358+
clientBwp.Port = clientDCAddr.Port
359+
serverBwp.Port = serverDCAddr.Port
362360

363361
// Start receiver before even sending the request so it will be ready.
364362
receiveRes := make(chan BwtestResult, 1)
@@ -383,6 +381,11 @@ func runBwtest(serverCCAddr *snet.UDPAddr,
383381
return
384382
}
385383

384+
// Pin DC to path used for request
385+
if serverDCAddr.IA != clientDCAddr.IA {
386+
dcConn.SetPolicy(pan.Pinned{ccSelector.Path().Fingerprint})
387+
}
388+
386389
// Start blasting client->server
387390
err = HandleDCConnSend(clientBwp, dcConn)
388391
if err != nil && !errors.Is(err, os.ErrDeadlineExceeded) {

bwtester/bwtestclient/bwtestclient_integration_test.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,9 @@ func TestIntegrationBwtestclient(t *testing.T) {
3636
serverCmd := integration.AppBinPath(serverBin)
3737

3838
// Server
39-
serverPort := "40002"
40-
serverArgs := []string{"-p", serverPort}
39+
serverArgs := []string{}
4140
// Client
42-
clientArgs := []string{"-s", integration.DstAddrPattern + ":" + serverPort, "-cs", "1,?,?,1Mbps"}
41+
clientArgs := []string{"-s", integration.DstAddrPattern + ":40002", "-cs", "1,?,?,1Mbps"}
4342

4443
in := integration.NewAppsIntegration(clientCmd, serverCmd, clientArgs, serverArgs)
4544
in.ServerOutMatch = integration.Contains("Received request")

bwtester/bwtestserver/bwtestserver.go

Lines changed: 68 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -22,31 +22,31 @@ import (
2222
"bytes"
2323
"context"
2424
"errors"
25-
"flag"
2625
"fmt"
2726
"net"
2827
"time"
2928

29+
"gopkg.in/alecthomas/kingpin.v2"
30+
"inet.af/netaddr"
31+
3032
. "github.com/netsec-ethz/scion-apps/bwtester/bwtestlib"
31-
"github.com/netsec-ethz/scion-apps/pkg/appnet"
32-
"github.com/scionproto/scion/go/lib/addr"
33-
"github.com/scionproto/scion/go/lib/snet"
33+
"github.com/netsec-ethz/scion-apps/pkg/pan"
3434
)
3535

3636
const (
3737
resultExpiry = time.Minute
3838
)
3939

4040
func main() {
41-
// Fetch arguments from command line
42-
serverPort := flag.Uint("p", 40002, "Port")
43-
flag.Parse()
41+
var listen pan.IPPortValue
42+
kingpin.Flag("listen", "Address to listen on").Default(":40002").SetValue(&listen)
43+
kingpin.Parse()
4444

45-
err := runServer(uint16(*serverPort))
45+
err := runServer(listen.Get())
4646
Check(err)
4747
}
4848

49-
func runServer(port uint16) error {
49+
func runServer(listen netaddr.IPPort) error {
5050
receivePacketBuffer := make([]byte, 2500)
5151

5252
var currentBwtest string
@@ -55,11 +55,12 @@ func runServer(port uint16) error {
5555

5656
results := make(resultsMap)
5757

58-
ccConn, err := appnet.ListenPort(port)
58+
ccSelector := pan.NewDefaultReplySelector()
59+
ccConn, err := pan.ListenUDP(context.Background(), listen, ccSelector)
5960
if err != nil {
6061
return err
6162
}
62-
serverCCAddr := ccConn.LocalAddr().(*net.UDPAddr)
63+
serverCCAddr := ccConn.LocalAddr().(pan.UDPAddr)
6364
for {
6465
// Handle client requests
6566
n, clientCCAddr, err := ccConn.ReadFrom(receivePacketBuffer)
@@ -104,7 +105,8 @@ func runServer(port uint16) error {
104105
if err != nil {
105106
continue
106107
}
107-
finishTime, err := startBwtestBackground(serverCCAddr, clientCCAddr.(*snet.UDPAddr),
108+
path := ccSelector.Path(clientCCAddr.(pan.UDPAddr))
109+
finishTime, err := startBwtestBackground(serverCCAddr, clientCCAddr.(pan.UDPAddr), path,
108110
clientBwp, serverBwp, currentResult)
109111
if err != nil {
110112
// Ask the client to try again in 1 second
@@ -135,17 +137,17 @@ func runServer(port uint16) error {
135137

136138
// startBwtestBackground starts a bandwidth test, in the background.
137139
// Returns the expected finish time of the test, or any error during the setup.
138-
func startBwtestBackground(serverCCAddr *net.UDPAddr, clientCCAddr *snet.UDPAddr,
139-
clientBwp, serverBwp BwtestParameters, res chan<- BwtestResult) (time.Time, error) {
140+
func startBwtestBackground(serverCCAddr pan.UDPAddr, clientCCAddr pan.UDPAddr,
141+
path *pan.Path, clientBwp, serverBwp BwtestParameters, res chan<- BwtestResult) (time.Time, error) {
140142

141143
// Data Connection addresses:
142-
clientDCAddr := clientCCAddr.Copy()
143-
clientDCAddr.Host.Port = int(clientBwp.Port)
144-
serverDCAddr := &net.UDPAddr{IP: serverCCAddr.IP, Port: int(serverBwp.Port)}
144+
clientDCAddr := clientCCAddr
145+
clientDCAddr.Port = clientBwp.Port
146+
serverDCAddr := netaddr.IPPortFrom(serverCCAddr.IP, serverBwp.Port)
145147

146148
// Open Data Connection
147-
dcConn, err := appnet.DefNetwork().Dial(
148-
context.TODO(), "udp", serverDCAddr, clientDCAddr, addr.SvcNone)
149+
dcSelector := initializedReplySelector(clientDCAddr, path)
150+
dcConn, err := listenConnected(serverDCAddr, clientDCAddr, dcSelector)
149151
if err != nil {
150152
return time.Time{}, err
151153
}
@@ -287,3 +289,50 @@ func (r resultsMap) purgeExpired() {
287289
}
288290
}
289291
}
292+
293+
func listenConnected(local netaddr.IPPort, remote pan.UDPAddr, selector pan.ReplySelector) (net.Conn, error) {
294+
conn, err := pan.ListenUDP(context.Background(), local, selector)
295+
return connectedPacketConn{
296+
ListenConn: conn,
297+
remote: remote,
298+
}, err
299+
}
300+
301+
// connectedPacketConn connects a net.PacketConn to a fixed remote address,
302+
// i.e. it uses the fixed remote address to wrap the ReadFrom/WriteTo of a
303+
// net.PacketConn into Read/Write of a net.Conn.
304+
type connectedPacketConn struct {
305+
pan.ListenConn
306+
remote pan.UDPAddr
307+
}
308+
309+
func (c connectedPacketConn) Read(buf []byte) (int, error) {
310+
for {
311+
n, addr, err := c.ListenConn.ReadFrom(buf)
312+
if err != nil {
313+
return n, err
314+
}
315+
if c.remote != addr.(pan.UDPAddr) {
316+
continue
317+
}
318+
return n, err
319+
}
320+
}
321+
322+
func (c connectedPacketConn) Write(buf []byte) (int, error) {
323+
return c.ListenConn.WriteTo(buf, c.remote)
324+
}
325+
326+
func (c connectedPacketConn) RemoteAddr() net.Addr {
327+
return c.remote
328+
}
329+
330+
// initializedReplySelector creates a pan.DefaultReplySelector, initialized with path for dst.
331+
func initializedReplySelector(remote pan.UDPAddr, path *pan.Path) pan.ReplySelector {
332+
if path != nil && path.Destination != remote.IA {
333+
panic("path destination should match address")
334+
}
335+
selector := pan.NewDefaultReplySelector()
336+
selector.Record(remote, path)
337+
return selector
338+
}

0 commit comments

Comments
 (0)