From 2a814e3008b9529e26445529b38f55cd5a26fe86 Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Tue, 12 Sep 2023 09:44:22 +0200 Subject: [PATCH 001/100] BR for Hummingbird, squashed. added functions computing ak, vk added 16 bytes comparison with time measurements removed inefficient functions added comparing vk with measurements fixed mac and mac test correctness changed dstIA to addr.IA type fixed padding in deriveAuthKey, added a deriveAuthKey version using preinitialized block, added measurement for mac added tests for comparing vk while ignoring padding rewrote mac computation (except aes encryption) by hand added function that does not make a cbcEncrypter added copy-paste of cipher aes implementation to not perform make added separate measurements tests for the different function versions renamed mac function to FlyoverMac and added a 256bits version of the auth key computation added some comments now uses assembly functions (copy/pasted from go libs) for flyover mac removed obsolete functions, look at previous commits to look at them shortened computation of K2 added copy paste of ppc64 aes assembly implementation amd64 assembly not doing decryption keys expansion anymore, hopefully arm64 not either removed dec key expansion from arm and ppc64 assembly code, removed dummy array used for storing these added comment concerning removal of dec in expandKeyAsm fixed hopfield SerializeTo too short error message changed ak computation to take resId, bw, start- and endtime as values input rather than bytes removed error as possibly function output for ak and vk computations updated ak and vk computation to new standard, updated tests to compare to library cmac and cbc implementations, updated runtime measurements to use go Benchmarking moved humminbird to pkg/slayers/path/hummingbird. Removed unused aes_const.go file Extend the benchmarks with a manual computation of Ak. added flyoverHopField added support for hummingbird pathmetahdr. Kinda ugly but should work updated compare vk to compare 6 bytes of data added decoded and raw handling hummingbird data. added tests for hummingbird added basic forwarding in dataplane and tests fixed hummingbird path reversal added decoded reverse test for hummingbird in decoded_test fixed path reversal correctness, wrote test to verify flyover suppression in final result added mac deaggregation not happening on AStransit ingress. Fixed relevant tests Fix BAZEL build files. moved most hbird code into separate files --- pkg/slayers/BUILD.bazel | 1 + pkg/slayers/path/hopfield.go | 8 +- pkg/slayers/path/hummingbird/BUILD.bazel | 43 ++ pkg/slayers/path/hummingbird/asm_amd64.s | 257 +++++++ pkg/slayers/path/hummingbird/asm_arm64.s | 214 ++++++ pkg/slayers/path/hummingbird/asm_ppc64x.s | 654 +++++++++++++++++ pkg/slayers/path/hummingbird/base.go | 153 ++++ pkg/slayers/path/hummingbird/base_test.go | 146 ++++ pkg/slayers/path/hummingbird/decoded.go | 182 +++++ pkg/slayers/path/hummingbird/decoded_test.go | 240 +++++++ .../path/hummingbird/flyoverhopfield.go | 89 +++ .../path/hummingbird/flyoverhopfield_test.go | 33 + pkg/slayers/path/hummingbird/mac.go | 125 ++++ pkg/slayers/path/hummingbird/mac_test.go | 212 ++++++ pkg/slayers/path/hummingbird/raw.go | 207 ++++++ pkg/slayers/path/hummingbird/raw_test.go | 195 +++++ pkg/slayers/path/scion/BUILD.bazel | 1 + pkg/slayers/path/scion/base.go | 7 +- pkg/slayers/path/scion/decoded.go | 12 +- pkg/slayers/path/scion/raw.go | 5 + pkg/slayers/path/scion/raw_test.go | 1 + pkg/slayers/scion.go | 11 +- pkg/slayers/scmp_typecode.go | 2 + router/BUILD.bazel | 2 + router/connector.go | 2 + router/dataplane.go | 37 +- router/dataplane_hbird.go | 406 +++++++++++ router/dataplane_hbird_test.go | 666 ++++++++++++++++++ router/export_test.go | 5 +- 29 files changed, 3900 insertions(+), 16 deletions(-) create mode 100644 pkg/slayers/path/hummingbird/BUILD.bazel create mode 100644 pkg/slayers/path/hummingbird/asm_amd64.s create mode 100644 pkg/slayers/path/hummingbird/asm_arm64.s create mode 100644 pkg/slayers/path/hummingbird/asm_ppc64x.s create mode 100644 pkg/slayers/path/hummingbird/base.go create mode 100644 pkg/slayers/path/hummingbird/base_test.go create mode 100644 pkg/slayers/path/hummingbird/decoded.go create mode 100644 pkg/slayers/path/hummingbird/decoded_test.go create mode 100644 pkg/slayers/path/hummingbird/flyoverhopfield.go create mode 100644 pkg/slayers/path/hummingbird/flyoverhopfield_test.go create mode 100644 pkg/slayers/path/hummingbird/mac.go create mode 100644 pkg/slayers/path/hummingbird/mac_test.go create mode 100644 pkg/slayers/path/hummingbird/raw.go create mode 100644 pkg/slayers/path/hummingbird/raw_test.go create mode 100644 router/dataplane_hbird.go create mode 100644 router/dataplane_hbird_test.go diff --git a/pkg/slayers/BUILD.bazel b/pkg/slayers/BUILD.bazel index c375215d35..32a47bdd03 100644 --- a/pkg/slayers/BUILD.bazel +++ b/pkg/slayers/BUILD.bazel @@ -22,6 +22,7 @@ go_library( "//pkg/slayers/path:go_default_library", "//pkg/slayers/path/empty:go_default_library", "//pkg/slayers/path/epic:go_default_library", + "//pkg/slayers/path/hummingbird:go_default_library", "//pkg/slayers/path/onehop:go_default_library", "//pkg/slayers/path/scion:go_default_library", "@com_github_google_gopacket//:go_default_library", diff --git a/pkg/slayers/path/hopfield.go b/pkg/slayers/path/hopfield.go index c4415ce671..192debb2fc 100644 --- a/pkg/slayers/path/hopfield.go +++ b/pkg/slayers/path/hopfield.go @@ -26,8 +26,14 @@ import ( const ( // HopLen is the size of a HopField in bytes. HopLen = 12 + // FlyoverLen is the length of a FlyoverHopField in bytes + FlyoverLen = 20 + // LineLen is the length of a line for the computation of the hop offset of hummingbird packets + LineLen = 4 // MacLen is the size of the MAC of each HopField. MacLen = 6 + // MacOffset is the offset of the MAC field from the beginning of the HopField + MacOffset = 6 ) // MaxTTL is the maximum age of a HopField. @@ -111,7 +117,7 @@ func (h *HopField) DecodeFromBytes(raw []byte) (err error) { // @ decreases func (h *HopField) SerializeTo(b []byte) (err error) { if len(b) < HopLen { - return serrors.New("buffer for HopField too short", "expected", MacLen, "actual", len(b)) + return serrors.New("buffer for HopField too short", "expected", HopLen, "actual", len(b)) } b[0] = 0 if h.EgressRouterAlert { diff --git a/pkg/slayers/path/hummingbird/BUILD.bazel b/pkg/slayers/path/hummingbird/BUILD.bazel new file mode 100644 index 0000000000..2c5ddaf86f --- /dev/null +++ b/pkg/slayers/path/hummingbird/BUILD.bazel @@ -0,0 +1,43 @@ +load("//tools/lint:go.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = [ + "asm_amd64.s", + "asm_arm64.s", + "asm_ppc64x.s", + "mac.go", + ], + importpath = "github.com/scionproto/scion/pkg/slayers/path/hummingbird", + visibility = ["//visibility:public"], + deps = select({ + "@io_bazel_rules_go//go/platform:amd64": [ + "//pkg/addr:go_default_library", + "//pkg/slayers/path:go_default_library", + ], + "@io_bazel_rules_go//go/platform:arm64": [ + "//pkg/addr:go_default_library", + "//pkg/slayers/path:go_default_library", + ], + "@io_bazel_rules_go//go/platform:ppc64": [ + "//pkg/addr:go_default_library", + "//pkg/slayers/path:go_default_library", + ], + "@io_bazel_rules_go//go/platform:ppc64le": [ + "//pkg/addr:go_default_library", + "//pkg/slayers/path:go_default_library", + ], + "//conditions:default": [], + }), +) + +go_test( + name = "go_default_test", + srcs = ["mac_test.go"], + deps = [ + ":go_default_library", + "//pkg/addr:go_default_library", + "@com_github_dchest_cmac//:go_default_library", + "@com_github_stretchr_testify//require:go_default_library", + ], +) diff --git a/pkg/slayers/path/hummingbird/asm_amd64.s b/pkg/slayers/path/hummingbird/asm_amd64.s new file mode 100644 index 0000000000..79fffb37c6 --- /dev/null +++ b/pkg/slayers/path/hummingbird/asm_amd64.s @@ -0,0 +1,257 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +// func encryptBlockAsm(nr int, xk *uint32, dst, src *byte) +TEXT ·encryptBlockAsm(SB),NOSPLIT,$0 + MOVQ nr+0(FP), CX + MOVQ xk+8(FP), AX + MOVQ dst+16(FP), DX + MOVQ src+24(FP), BX + MOVUPS 0(AX), X1 + MOVUPS 0(BX), X0 + ADDQ $16, AX + PXOR X1, X0 + SUBQ $12, CX + JE Lenc192 + JB Lenc128 +Lenc256: + MOVUPS 0(AX), X1 + AESENC X1, X0 + MOVUPS 16(AX), X1 + AESENC X1, X0 + ADDQ $32, AX +Lenc192: + MOVUPS 0(AX), X1 + AESENC X1, X0 + MOVUPS 16(AX), X1 + AESENC X1, X0 + ADDQ $32, AX +Lenc128: + MOVUPS 0(AX), X1 + AESENC X1, X0 + MOVUPS 16(AX), X1 + AESENC X1, X0 + MOVUPS 32(AX), X1 + AESENC X1, X0 + MOVUPS 48(AX), X1 + AESENC X1, X0 + MOVUPS 64(AX), X1 + AESENC X1, X0 + MOVUPS 80(AX), X1 + AESENC X1, X0 + MOVUPS 96(AX), X1 + AESENC X1, X0 + MOVUPS 112(AX), X1 + AESENC X1, X0 + MOVUPS 128(AX), X1 + AESENC X1, X0 + MOVUPS 144(AX), X1 + AESENCLAST X1, X0 + MOVUPS X0, 0(DX) + RET + +// func decryptBlockAsm(nr int, xk *uint32, dst, src *byte) +TEXT ·decryptBlockAsm(SB),NOSPLIT,$0 + MOVQ nr+0(FP), CX + MOVQ xk+8(FP), AX + MOVQ dst+16(FP), DX + MOVQ src+24(FP), BX + MOVUPS 0(AX), X1 + MOVUPS 0(BX), X0 + ADDQ $16, AX + PXOR X1, X0 + SUBQ $12, CX + JE Ldec192 + JB Ldec128 +Ldec256: + MOVUPS 0(AX), X1 + AESDEC X1, X0 + MOVUPS 16(AX), X1 + AESDEC X1, X0 + ADDQ $32, AX +Ldec192: + MOVUPS 0(AX), X1 + AESDEC X1, X0 + MOVUPS 16(AX), X1 + AESDEC X1, X0 + ADDQ $32, AX +Ldec128: + MOVUPS 0(AX), X1 + AESDEC X1, X0 + MOVUPS 16(AX), X1 + AESDEC X1, X0 + MOVUPS 32(AX), X1 + AESDEC X1, X0 + MOVUPS 48(AX), X1 + AESDEC X1, X0 + MOVUPS 64(AX), X1 + AESDEC X1, X0 + MOVUPS 80(AX), X1 + AESDEC X1, X0 + MOVUPS 96(AX), X1 + AESDEC X1, X0 + MOVUPS 112(AX), X1 + AESDEC X1, X0 + MOVUPS 128(AX), X1 + AESDEC X1, X0 + MOVUPS 144(AX), X1 + AESDECLAST X1, X0 + MOVUPS X0, 0(DX) + RET + +// func expandKeyAsm(nr int, key *byte, enc) { +// Note that round keys are stored in uint128 format, not uint32 +TEXT ·expandKeyAsm(SB),NOSPLIT,$0 + MOVQ nr+0(FP), CX + MOVQ key+8(FP), AX + MOVQ enc+16(FP), BX + MOVUPS (AX), X0 + // enc + MOVUPS X0, (BX) + ADDQ $16, BX + PXOR X4, X4 // _expand_key_* expect X4 to be zero + CMPL CX, $12 + JE Lexp_enc192 + JB Lexp_enc128 +Lexp_enc256: + MOVUPS 16(AX), X2 + MOVUPS X2, (BX) + ADDQ $16, BX + AESKEYGENASSIST $0x01, X2, X1 + CALL _expand_key_256a<>(SB) + AESKEYGENASSIST $0x01, X0, X1 + CALL _expand_key_256b<>(SB) + AESKEYGENASSIST $0x02, X2, X1 + CALL _expand_key_256a<>(SB) + AESKEYGENASSIST $0x02, X0, X1 + CALL _expand_key_256b<>(SB) + AESKEYGENASSIST $0x04, X2, X1 + CALL _expand_key_256a<>(SB) + AESKEYGENASSIST $0x04, X0, X1 + CALL _expand_key_256b<>(SB) + AESKEYGENASSIST $0x08, X2, X1 + CALL _expand_key_256a<>(SB) + AESKEYGENASSIST $0x08, X0, X1 + CALL _expand_key_256b<>(SB) + AESKEYGENASSIST $0x10, X2, X1 + CALL _expand_key_256a<>(SB) + AESKEYGENASSIST $0x10, X0, X1 + CALL _expand_key_256b<>(SB) + AESKEYGENASSIST $0x20, X2, X1 + CALL _expand_key_256a<>(SB) + AESKEYGENASSIST $0x20, X0, X1 + CALL _expand_key_256b<>(SB) + AESKEYGENASSIST $0x40, X2, X1 + CALL _expand_key_256a<>(SB) + RET +Lexp_enc192: + MOVQ 16(AX), X2 + AESKEYGENASSIST $0x01, X2, X1 + CALL _expand_key_192a<>(SB) + AESKEYGENASSIST $0x02, X2, X1 + CALL _expand_key_192b<>(SB) + AESKEYGENASSIST $0x04, X2, X1 + CALL _expand_key_192a<>(SB) + AESKEYGENASSIST $0x08, X2, X1 + CALL _expand_key_192b<>(SB) + AESKEYGENASSIST $0x10, X2, X1 + CALL _expand_key_192a<>(SB) + AESKEYGENASSIST $0x20, X2, X1 + CALL _expand_key_192b<>(SB) + AESKEYGENASSIST $0x40, X2, X1 + CALL _expand_key_192a<>(SB) + AESKEYGENASSIST $0x80, X2, X1 + CALL _expand_key_192b<>(SB) + RET +Lexp_enc128: + AESKEYGENASSIST $0x01, X0, X1 + CALL _expand_key_128<>(SB) + AESKEYGENASSIST $0x02, X0, X1 + CALL _expand_key_128<>(SB) + AESKEYGENASSIST $0x04, X0, X1 + CALL _expand_key_128<>(SB) + AESKEYGENASSIST $0x08, X0, X1 + CALL _expand_key_128<>(SB) + AESKEYGENASSIST $0x10, X0, X1 + CALL _expand_key_128<>(SB) + AESKEYGENASSIST $0x20, X0, X1 + CALL _expand_key_128<>(SB) + AESKEYGENASSIST $0x40, X0, X1 + CALL _expand_key_128<>(SB) + AESKEYGENASSIST $0x80, X0, X1 + CALL _expand_key_128<>(SB) + AESKEYGENASSIST $0x1b, X0, X1 + CALL _expand_key_128<>(SB) + AESKEYGENASSIST $0x36, X0, X1 + CALL _expand_key_128<>(SB) + RET + +TEXT _expand_key_128<>(SB),NOSPLIT,$0 + PSHUFD $0xff, X1, X1 + SHUFPS $0x10, X0, X4 + PXOR X4, X0 + SHUFPS $0x8c, X0, X4 + PXOR X4, X0 + PXOR X1, X0 + MOVUPS X0, (BX) + ADDQ $16, BX + RET + +TEXT _expand_key_192a<>(SB),NOSPLIT,$0 + PSHUFD $0x55, X1, X1 + SHUFPS $0x10, X0, X4 + PXOR X4, X0 + SHUFPS $0x8c, X0, X4 + PXOR X4, X0 + PXOR X1, X0 + + MOVAPS X2, X5 + MOVAPS X2, X6 + PSLLDQ $0x4, X5 + PSHUFD $0xff, X0, X3 + PXOR X3, X2 + PXOR X5, X2 + + MOVAPS X0, X1 + SHUFPS $0x44, X0, X6 + MOVUPS X6, (BX) + SHUFPS $0x4e, X2, X1 + MOVUPS X1, 16(BX) + ADDQ $32, BX + RET + +TEXT _expand_key_192b<>(SB),NOSPLIT,$0 + PSHUFD $0x55, X1, X1 + SHUFPS $0x10, X0, X4 + PXOR X4, X0 + SHUFPS $0x8c, X0, X4 + PXOR X4, X0 + PXOR X1, X0 + + MOVAPS X2, X5 + PSLLDQ $0x4, X5 + PSHUFD $0xff, X0, X3 + PXOR X3, X2 + PXOR X5, X2 + + MOVUPS X0, (BX) + ADDQ $16, BX + RET + +TEXT _expand_key_256a<>(SB),NOSPLIT,$0 + JMP _expand_key_128<>(SB) + +TEXT _expand_key_256b<>(SB),NOSPLIT,$0 + PSHUFD $0xaa, X1, X1 + SHUFPS $0x10, X2, X4 + PXOR X4, X2 + SHUFPS $0x8c, X2, X4 + PXOR X4, X2 + PXOR X1, X2 + + MOVUPS X2, (BX) + ADDQ $16, BX + RET diff --git a/pkg/slayers/path/hummingbird/asm_arm64.s b/pkg/slayers/path/hummingbird/asm_arm64.s new file mode 100644 index 0000000000..ee91c605d5 --- /dev/null +++ b/pkg/slayers/path/hummingbird/asm_arm64.s @@ -0,0 +1,214 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" +DATA rotInvSRows<>+0x00(SB)/8, $0x080f0205040b0e01 +DATA rotInvSRows<>+0x08(SB)/8, $0x00070a0d0c030609 +GLOBL rotInvSRows<>(SB), (NOPTR+RODATA), $16 +DATA invSRows<>+0x00(SB)/8, $0x0b0e0104070a0d00 +DATA invSRows<>+0x08(SB)/8, $0x0306090c0f020508 +GLOBL invSRows<>(SB), (NOPTR+RODATA), $16 +// func encryptBlockAsm(nr int, xk *uint32, dst, src *byte) +TEXT ·encryptBlockAsm(SB),NOSPLIT,$0 + MOVD nr+0(FP), R9 + MOVD xk+8(FP), R10 + MOVD dst+16(FP), R11 + MOVD src+24(FP), R12 + + VLD1 (R12), [V0.B16] + + CMP $12, R9 + BLT enc128 + BEQ enc196 +enc256: + VLD1.P 32(R10), [V1.B16, V2.B16] + AESE V1.B16, V0.B16 + AESMC V0.B16, V0.B16 + AESE V2.B16, V0.B16 + AESMC V0.B16, V0.B16 +enc196: + VLD1.P 32(R10), [V3.B16, V4.B16] + AESE V3.B16, V0.B16 + AESMC V0.B16, V0.B16 + AESE V4.B16, V0.B16 + AESMC V0.B16, V0.B16 +enc128: + VLD1.P 64(R10), [V5.B16, V6.B16, V7.B16, V8.B16] + VLD1.P 64(R10), [V9.B16, V10.B16, V11.B16, V12.B16] + VLD1.P 48(R10), [V13.B16, V14.B16, V15.B16] + AESE V5.B16, V0.B16 + AESMC V0.B16, V0.B16 + AESE V6.B16, V0.B16 + AESMC V0.B16, V0.B16 + AESE V7.B16, V0.B16 + AESMC V0.B16, V0.B16 + AESE V8.B16, V0.B16 + AESMC V0.B16, V0.B16 + AESE V9.B16, V0.B16 + AESMC V0.B16, V0.B16 + AESE V10.B16, V0.B16 + AESMC V0.B16, V0.B16 + AESE V11.B16, V0.B16 + AESMC V0.B16, V0.B16 + AESE V12.B16, V0.B16 + AESMC V0.B16, V0.B16 + AESE V13.B16, V0.B16 + AESMC V0.B16, V0.B16 + AESE V14.B16, V0.B16 + VEOR V0.B16, V15.B16, V0.B16 + VST1 [V0.B16], (R11) + RET + +// func decryptBlockAsm(nr int, xk *uint32, dst, src *byte) +TEXT ·decryptBlockAsm(SB),NOSPLIT,$0 + MOVD nr+0(FP), R9 + MOVD xk+8(FP), R10 + MOVD dst+16(FP), R11 + MOVD src+24(FP), R12 + + VLD1 (R12), [V0.B16] + + CMP $12, R9 + BLT dec128 + BEQ dec196 +dec256: + VLD1.P 32(R10), [V1.B16, V2.B16] + AESD V1.B16, V0.B16 + AESIMC V0.B16, V0.B16 + AESD V2.B16, V0.B16 + AESIMC V0.B16, V0.B16 +dec196: + VLD1.P 32(R10), [V3.B16, V4.B16] + AESD V3.B16, V0.B16 + AESIMC V0.B16, V0.B16 + AESD V4.B16, V0.B16 + AESIMC V0.B16, V0.B16 +dec128: + VLD1.P 64(R10), [V5.B16, V6.B16, V7.B16, V8.B16] + VLD1.P 64(R10), [V9.B16, V10.B16, V11.B16, V12.B16] + VLD1.P 48(R10), [V13.B16, V14.B16, V15.B16] + AESD V5.B16, V0.B16 + AESIMC V0.B16, V0.B16 + AESD V6.B16, V0.B16 + AESIMC V0.B16, V0.B16 + AESD V7.B16, V0.B16 + AESIMC V0.B16, V0.B16 + AESD V8.B16, V0.B16 + AESIMC V0.B16, V0.B16 + AESD V9.B16, V0.B16 + AESIMC V0.B16, V0.B16 + AESD V10.B16, V0.B16 + AESIMC V0.B16, V0.B16 + AESD V11.B16, V0.B16 + AESIMC V0.B16, V0.B16 + AESD V12.B16, V0.B16 + AESIMC V0.B16, V0.B16 + AESD V13.B16, V0.B16 + AESIMC V0.B16, V0.B16 + AESD V14.B16, V0.B16 + VEOR V0.B16, V15.B16, V0.B16 + VST1 [V0.B16], (R11) + RET + +// func expandKeyAsm(nr int, key *byte, enc) { +// Note that round keys are stored in uint128 format, not uint32 +TEXT ·expandKeyAsm(SB),NOSPLIT,$0 + MOVD nr+0(FP), R8 + MOVD key+8(FP), R9 + MOVD enc+16(FP), R10 + LDP rotInvSRows<>(SB), (R0, R1) + VMOV R0, V3.D[0] + VMOV R1, V3.D[1] + VEOR V0.B16, V0.B16, V0.B16 // All zeroes + MOVW $1, R13 + TBZ $1, R8, ks192 + TBNZ $2, R8, ks256 + LDPW (R9), (R4, R5) + LDPW 8(R9), (R6, R7) + STPW.P (R4, R5), 8(R10) + STPW.P (R6, R7), 8(R10) + MOVW $0x1b, R14 +ks128Loop: + VMOV R7, V2.S[0] + WORD $0x4E030042 // TBL V3.B16, [V2.B16], V2.B16 + AESE V0.B16, V2.B16 // Use AES to compute the SBOX + EORW R13, R4 + LSLW $1, R13 // Compute next Rcon + ANDSW $0x100, R13, ZR + CSELW NE, R14, R13, R13 // Fake modulo + SUBS $1, R8 + VMOV V2.S[0], R0 + EORW R0, R4 + EORW R4, R5 + EORW R5, R6 + EORW R6, R7 + STPW.P (R4, R5), 8(R10) + STPW.P (R6, R7), 8(R10) + BNE ks128Loop + B ksDone // If dec is nil we are done +ks192: + LDPW (R9), (R2, R3) + LDPW 8(R9), (R4, R5) + LDPW 16(R9), (R6, R7) + STPW.P (R2, R3), 8(R10) + STPW.P (R4, R5), 8(R10) + SUB $4, R8 +ks192Loop: + STPW.P (R6, R7), 8(R10) + VMOV R7, V2.S[0] + WORD $0x4E030042 //TBL V3.B16, [V2.B16], V2.B16 + AESE V0.B16, V2.B16 + EORW R13, R2 + LSLW $1, R13 + SUBS $1, R8 + VMOV V2.S[0], R0 + EORW R0, R2 + EORW R2, R3 + EORW R3, R4 + EORW R4, R5 + EORW R5, R6 + EORW R6, R7 + STPW.P (R2, R3), 8(R10) + STPW.P (R4, R5), 8(R10) + BNE ks192Loop + B ksDone +ks256: + LDP invSRows<>(SB), (R0, R1) + VMOV R0, V4.D[0] + VMOV R1, V4.D[1] + LDPW (R9), (R0, R1) + LDPW 8(R9), (R2, R3) + LDPW 16(R9), (R4, R5) + LDPW 24(R9), (R6, R7) + STPW.P (R0, R1), 8(R10) + STPW.P (R2, R3), 8(R10) + SUB $7, R8 +ks256Loop: + STPW.P (R4, R5), 8(R10) + STPW.P (R6, R7), 8(R10) + VMOV R7, V2.S[0] + WORD $0x4E030042 //TBL V3.B16, [V2.B16], V2.B16 + AESE V0.B16, V2.B16 + EORW R13, R0 + LSLW $1, R13 + SUBS $1, R8 + VMOV V2.S[0], R9 + EORW R9, R0 + EORW R0, R1 + EORW R1, R2 + EORW R2, R3 + VMOV R3, V2.S[0] + WORD $0x4E040042 //TBL V3.B16, [V2.B16], V2.B16 + AESE V0.B16, V2.B16 + VMOV V2.S[0], R9 + EORW R9, R4 + EORW R4, R5 + EORW R5, R6 + EORW R6, R7 + STPW.P (R0, R1), 8(R10) + STPW.P (R2, R3), 8(R10) + BNE ks256Loop + B ksDone +ksDone: + RET diff --git a/pkg/slayers/path/hummingbird/asm_ppc64x.s b/pkg/slayers/path/hummingbird/asm_ppc64x.s new file mode 100644 index 0000000000..4ccd0533de --- /dev/null +++ b/pkg/slayers/path/hummingbird/asm_ppc64x.s @@ -0,0 +1,654 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build ppc64 || ppc64le + +// Based on CRYPTOGAMS code with the following comment: +// # ==================================================================== +// # Written by Andy Polyakov for the OpenSSL +// # project. The module is, however, dual licensed under OpenSSL and +// # CRYPTOGAMS licenses depending on where you obtain it. For further +// # details see http://www.openssl.org/~appro/cryptogams/. +// # ==================================================================== + +// Original code can be found at the link below: +// https://github.com/dot-asm/cryptogams/blob/master/ppc/aesp8-ppc.pl + +// Some function names were changed to be consistent with Go function +// names. For instance, function aes_p8_set_{en,de}crypt_key become +// set{En,De}cryptKeyAsm. I also split setEncryptKeyAsm in two parts +// and a new session was created (doEncryptKeyAsm). This was necessary to +// avoid arguments overwriting when setDecryptKeyAsm calls setEncryptKeyAsm. +// There were other modifications as well but kept the same functionality. + +#include "textflag.h" + +// For expandKeyAsm +#define INP R3 +#define BITS R4 +#define OUTENC R5 // Pointer to next expanded encrypt key +#define PTR R6 +#define CNT R7 +#define ROUNDS R8 +#define OUTDEC R9 // Pointer to next expanded decrypt key +#define TEMP R19 +#define ZERO V0 +#define IN0 V1 +#define IN1 V2 +#define KEY V3 +#define RCON V4 +#define MASK V5 +#define TMP V6 +#define STAGE V7 +#define OUTPERM V8 +#define OUTMASK V9 +#define OUTHEAD V10 +#define OUTTAIL V11 + +// For P9 instruction emulation +#define ESPERM V21 // Endian swapping permute into BE +#define TMP2 V22 // Temporary for P8_STXVB16X/P8_STXV + +// For {en,de}cryptBlockAsm +#define BLK_INP R3 +#define BLK_OUT R4 +#define BLK_KEY R5 +#define BLK_ROUNDS R6 +#define BLK_IDX R7 + +DATA ·rcon+0x00(SB)/8, $0x0f0e0d0c0b0a0908 // Permute for vector doubleword endian swap +DATA ·rcon+0x08(SB)/8, $0x0706050403020100 +DATA ·rcon+0x10(SB)/8, $0x0100000001000000 // RCON +DATA ·rcon+0x18(SB)/8, $0x0100000001000000 // RCON +DATA ·rcon+0x20(SB)/8, $0x1b0000001b000000 +DATA ·rcon+0x28(SB)/8, $0x1b0000001b000000 +DATA ·rcon+0x30(SB)/8, $0x0d0e0f0c0d0e0f0c // MASK +DATA ·rcon+0x38(SB)/8, $0x0d0e0f0c0d0e0f0c // MASK +DATA ·rcon+0x40(SB)/8, $0x0000000000000000 +DATA ·rcon+0x48(SB)/8, $0x0000000000000000 +GLOBL ·rcon(SB), RODATA, $80 + +// Emulate unaligned BE vector load/stores on LE targets +#ifdef GOARCH_ppc64le +#define P8_LXVB16X(RA,RB,VT) \ + LXVD2X (RA+RB), VT \ + VPERM VT, VT, ESPERM, VT + +#define P8_STXVB16X(VS,RA,RB) \ + VPERM VS, VS, ESPERM, TMP2 \ + STXVD2X TMP2, (RA+RB) + +#define LXSDX_BE(RA,RB,VT) \ + LXSDX (RA+RB), VT \ + VPERM VT, VT, ESPERM, VT +#else +#define P8_LXVB16X(RA,RB,VT) \ + LXVD2X (RA+RB), VT + +#define P8_STXVB16X(VS,RA,RB) \ + STXVD2X VS, (RA+RB) + +#define LXSDX_BE(RA,RB,VT) \ + LXSDX (RA+RB), VT +#endif + +// func setEncryptKeyAsm(nr int, key *byte, enc *uint32) +TEXT ·expandKeyAsm(SB), NOSPLIT|NOFRAME, $0 + // Load the arguments inside the registers + MOVD nr+0(FP), ROUNDS + MOVD key+8(FP), INP + MOVD enc+16(FP), OUTENC +// MOVD dec+24(FP), OUTDEC + +#ifdef GOARCH_ppc64le + MOVD $·rcon(SB), PTR // PTR point to rcon addr + LVX (PTR), ESPERM + ADD $0x10, PTR +#else + MOVD $·rcon+0x10(SB), PTR // PTR point to rcon addr (skipping permute vector) +#endif + + // Get key from memory and write aligned into VR + P8_LXVB16X(INP, R0, IN0) + ADD $0x10, INP, INP + MOVD $0x20, TEMP + + CMPW ROUNDS, $12 + LVX (PTR)(R0), RCON // lvx 4,0,6 Load first 16 bytes into RCON + LVX (PTR)(TEMP), MASK + ADD $0x10, PTR, PTR // addi 6,6,0x10 PTR to next 16 bytes of RCON + MOVD $8, CNT // li 7,8 CNT = 8 + VXOR ZERO, ZERO, ZERO // vxor 0,0,0 Zero to be zero :) + MOVD CNT, CTR // mtctr 7 Set the counter to 8 (rounds) + + // The expanded decrypt key is the expanded encrypt key stored in reverse order. + // Move OUTDEC to the last key location, and store in descending order. +// ADD $160, OUTDEC, OUTDEC + BLT loop128 +// ADD $32, OUTDEC, OUTDEC + BEQ l192 +// ADD $32, OUTDEC, OUTDEC + JMP l256 + +loop128: + // Key schedule (Round 1 to 8) + VPERM IN0, IN0, MASK, KEY // vperm 3,1,1,5 Rotate-n-splat + VSLDOI $12, ZERO, IN0, TMP // vsldoi 6,0,1,12 + STXVD2X IN0, (R0+OUTENC) +// STXVD2X IN0, (R0+OUTDEC) + VCIPHERLAST KEY, RCON, KEY // vcipherlast 3,3,4 + ADD $16, OUTENC, OUTENC +// ADD $-16, OUTDEC, OUTDEC + + VXOR IN0, TMP, IN0 // vxor 1,1,6 + VSLDOI $12, ZERO, TMP, TMP // vsldoi 6,0,6,12 + VXOR IN0, TMP, IN0 // vxor 1,1,6 + VSLDOI $12, ZERO, TMP, TMP // vsldoi 6,0,6,12 + VXOR IN0, TMP, IN0 // vxor 1,1,6 + VADDUWM RCON, RCON, RCON // vadduwm 4,4,4 + VXOR IN0, KEY, IN0 // vxor 1,1,3 + BC 0x10, 0, loop128 // bdnz .Loop128 + + LVX (PTR)(R0), RCON // lvx 4,0,6 Last two round keys + + // Key schedule (Round 9) + VPERM IN0, IN0, MASK, KEY // vperm 3,1,1,5 Rotate-n-spat + VSLDOI $12, ZERO, IN0, TMP // vsldoi 6,0,1,12 + STXVD2X IN0, (R0+OUTENC) +// STXVD2X IN0, (R0+OUTDEC) + VCIPHERLAST KEY, RCON, KEY // vcipherlast 3,3,4 + ADD $16, OUTENC, OUTENC +// ADD $-16, OUTDEC, OUTDEC + + // Key schedule (Round 10) + VXOR IN0, TMP, IN0 // vxor 1,1,6 + VSLDOI $12, ZERO, TMP, TMP // vsldoi 6,0,6,12 + VXOR IN0, TMP, IN0 // vxor 1,1,6 + VSLDOI $12, ZERO, TMP, TMP // vsldoi 6,0,6,12 + VXOR IN0, TMP, IN0 // vxor 1,1,6 + VADDUWM RCON, RCON, RCON // vadduwm 4,4,4 + VXOR IN0, KEY, IN0 // vxor 1,1,3 + + VPERM IN0, IN0, MASK, KEY // vperm 3,1,1,5 Rotate-n-splat + VSLDOI $12, ZERO, IN0, TMP // vsldoi 6,0,1,12 + STXVD2X IN0, (R0+OUTENC) +// STXVD2X IN0, (R0+OUTDEC) + VCIPHERLAST KEY, RCON, KEY // vcipherlast 3,3,4 + ADD $16, OUTENC, OUTENC +// ADD $-16, OUTDEC, OUTDEC + + // Key schedule (Round 11) + VXOR IN0, TMP, IN0 // vxor 1,1,6 + VSLDOI $12, ZERO, TMP, TMP // vsldoi 6,0,6,12 + VXOR IN0, TMP, IN0 // vxor 1,1,6 + VSLDOI $12, ZERO, TMP, TMP // vsldoi 6,0,6,12 + VXOR IN0, TMP, IN0 // vxor 1,1,6 + VXOR IN0, KEY, IN0 // vxor 1,1,3 + STXVD2X IN0, (R0+OUTENC) +// STXVD2X IN0, (R0+OUTDEC) + + RET + +l192: + LXSDX_BE(INP, R0, IN1) // Load next 8 bytes into upper half of VSR in BE order. + MOVD $4, CNT // li 7,4 + STXVD2X IN0, (R0+OUTENC) +// STXVD2X IN0, (R0+OUTDEC) + ADD $16, OUTENC, OUTENC +// ADD $-16, OUTDEC, OUTDEC + VSPLTISB $8, KEY // vspltisb 3,8 + MOVD CNT, CTR // mtctr 7 + VSUBUBM MASK, KEY, MASK // vsububm 5,5,3 + +loop192: + VPERM IN1, IN1, MASK, KEY // vperm 3,2,2,5 + VSLDOI $12, ZERO, IN0, TMP // vsldoi 6,0,1,12 + VCIPHERLAST KEY, RCON, KEY // vcipherlast 3,3,4 + + VXOR IN0, TMP, IN0 // vxor 1,1,6 + VSLDOI $12, ZERO, TMP, TMP // vsldoi 6,0,6,12 + VXOR IN0, TMP, IN0 // vxor 1,1,6 + VSLDOI $12, ZERO, TMP, TMP // vsldoi 6,0,6,12 + VXOR IN0, TMP, IN0 // vxor 1,1,6 + + VSLDOI $8, ZERO, IN1, STAGE // vsldoi 7,0,2,8 + VSPLTW $3, IN0, TMP // vspltw 6,1,3 + VXOR TMP, IN1, TMP // vxor 6,6,2 + VSLDOI $12, ZERO, IN1, IN1 // vsldoi 2,0,2,12 + VADDUWM RCON, RCON, RCON // vadduwm 4,4,4 + VXOR IN1, TMP, IN1 // vxor 2,2,6 + VXOR IN0, KEY, IN0 // vxor 1,1,3 + VXOR IN1, KEY, IN1 // vxor 2,2,3 + VSLDOI $8, STAGE, IN0, STAGE // vsldoi 7,7,1,8 + + VPERM IN1, IN1, MASK, KEY // vperm 3,2,2,5 + VSLDOI $12, ZERO, IN0, TMP // vsldoi 6,0,1,12 + STXVD2X STAGE, (R0+OUTENC) +// STXVD2X STAGE, (R0+OUTDEC) + VCIPHERLAST KEY, RCON, KEY // vcipherlast 3,3,4 + ADD $16, OUTENC, OUTENC +// ADD $-16, OUTDEC, OUTDEC + + VSLDOI $8, IN0, IN1, STAGE // vsldoi 7,1,2,8 + VXOR IN0, TMP, IN0 // vxor 1,1,6 + VSLDOI $12, ZERO, TMP, TMP // vsldoi 6,0,6,12 + STXVD2X STAGE, (R0+OUTENC) +// STXVD2X STAGE, (R0+OUTDEC) + VXOR IN0, TMP, IN0 // vxor 1,1,6 + VSLDOI $12, ZERO, TMP, TMP // vsldoi 6,0,6,12 + VXOR IN0, TMP, IN0 // vxor 1,1,6 + ADD $16, OUTENC, OUTENC +// ADD $-16, OUTDEC, OUTDEC + + VSPLTW $3, IN0, TMP // vspltw 6,1,3 + VXOR TMP, IN1, TMP // vxor 6,6,2 + VSLDOI $12, ZERO, IN1, IN1 // vsldoi 2,0,2,12 + VADDUWM RCON, RCON, RCON // vadduwm 4,4,4 + VXOR IN1, TMP, IN1 // vxor 2,2,6 + VXOR IN0, KEY, IN0 // vxor 1,1,3 + VXOR IN1, KEY, IN1 // vxor 2,2,3 + STXVD2X IN0, (R0+OUTENC) +// STXVD2X IN0, (R0+OUTDEC) + ADD $16, OUTENC, OUTENC +// ADD $-16, OUTDEC, OUTDEC + BC 0x10, 0, loop192 // bdnz .Loop192 + + RET + +l256: + P8_LXVB16X(INP, R0, IN1) + MOVD $7, CNT // li 7,7 + STXVD2X IN0, (R0+OUTENC) +// STXVD2X IN0, (R0+OUTDEC) + ADD $16, OUTENC, OUTENC +// ADD $-16, OUTDEC, OUTDEC + MOVD CNT, CTR // mtctr 7 + +loop256: + VPERM IN1, IN1, MASK, KEY // vperm 3,2,2,5 + VSLDOI $12, ZERO, IN0, TMP // vsldoi 6,0,1,12 + STXVD2X IN1, (R0+OUTENC) +// STXVD2X IN1, (R0+OUTDEC) + VCIPHERLAST KEY, RCON, KEY // vcipherlast 3,3,4 + ADD $16, OUTENC, OUTENC +// ADD $-16, OUTDEC, OUTDEC + + VXOR IN0, TMP, IN0 // vxor 1,1,6 + VSLDOI $12, ZERO, TMP, TMP // vsldoi 6,0,6,12 + VXOR IN0, TMP, IN0 // vxor 1,1,6 + VSLDOI $12, ZERO, TMP, TMP // vsldoi 6,0,6,12 + VXOR IN0, TMP, IN0 // vxor 1,1,6 + VADDUWM RCON, RCON, RCON // vadduwm 4,4,4 + VXOR IN0, KEY, IN0 // vxor 1,1,3 + STXVD2X IN0, (R0+OUTENC) +// STXVD2X IN0, (R0+OUTDEC) + ADD $16, OUTENC, OUTENC +// ADD $-16, OUTDEC, OUTDEC + BC 0x12, 0, done // bdz .Ldone + + VSPLTW $3, IN0, KEY // vspltw 3,1,3 + VSLDOI $12, ZERO, IN1, TMP // vsldoi 6,0,2,12 + VSBOX KEY, KEY // vsbox 3,3 + + VXOR IN1, TMP, IN1 // vxor 2,2,6 + VSLDOI $12, ZERO, TMP, TMP // vsldoi 6,0,6,12 + VXOR IN1, TMP, IN1 // vxor 2,2,6 + VSLDOI $12, ZERO, TMP, TMP // vsldoi 6,0,6,12 + VXOR IN1, TMP, IN1 // vxor 2,2,6 + + VXOR IN1, KEY, IN1 // vxor 2,2,3 + JMP loop256 // b .Loop256 + +done: + RET + +// func encryptBlockAsm(nr int, xk *uint32, dst, src *byte) +TEXT ·encryptBlockAsm(SB), NOSPLIT|NOFRAME, $0 + MOVD nr+0(FP), R6 // Round count/Key size + MOVD xk+8(FP), R5 // Key pointer + MOVD dst+16(FP), R3 // Dest pointer + MOVD src+24(FP), R4 // Src pointer +#ifdef GOARCH_ppc64le + MOVD $·rcon(SB), R7 + LVX (R7), ESPERM // Permute value for P8_ macros. +#endif + + // Set CR{1,2,3}EQ to hold the key size information. + CMPU R6, $10, CR1 + CMPU R6, $12, CR2 + CMPU R6, $14, CR3 + + MOVD $16, R6 + MOVD $32, R7 + MOVD $48, R8 + MOVD $64, R9 + MOVD $80, R10 + MOVD $96, R11 + MOVD $112, R12 + + // Load text in BE order + P8_LXVB16X(R4, R0, V0) + + // V1, V2 will hold keys, V0 is a temp. + // At completion, V2 will hold the ciphertext. + // Load xk[0:3] and xor with text + LXVD2X (R0+R5), V1 + VXOR V0, V1, V0 + + // Load xk[4:11] and cipher + LXVD2X (R6+R5), V1 + LXVD2X (R7+R5), V2 + VCIPHER V0, V1, V0 + VCIPHER V0, V2, V0 + + // Load xk[12:19] and cipher + LXVD2X (R8+R5), V1 + LXVD2X (R9+R5), V2 + VCIPHER V0, V1, V0 + VCIPHER V0, V2, V0 + + // Load xk[20:27] and cipher + LXVD2X (R10+R5), V1 + LXVD2X (R11+R5), V2 + VCIPHER V0, V1, V0 + VCIPHER V0, V2, V0 + + // Increment xk pointer to reuse constant offsets in R6-R12. + ADD $112, R5 + + // Load xk[28:35] and cipher + LXVD2X (R0+R5), V1 + LXVD2X (R6+R5), V2 + VCIPHER V0, V1, V0 + VCIPHER V0, V2, V0 + + // Load xk[36:43] and cipher + LXVD2X (R7+R5), V1 + LXVD2X (R8+R5), V2 + BEQ CR1, Ldec_tail // Key size 10? + VCIPHER V0, V1, V0 + VCIPHER V0, V2, V0 + + // Load xk[44:51] and cipher + LXVD2X (R9+R5), V1 + LXVD2X (R10+R5), V2 + BEQ CR2, Ldec_tail // Key size 12? + VCIPHER V0, V1, V0 + VCIPHER V0, V2, V0 + + // Load xk[52:59] and cipher + LXVD2X (R11+R5), V1 + LXVD2X (R12+R5), V2 + BNE CR3, Linvalid_key_len // Not key size 14? + // Fallthrough to final cipher + +Ldec_tail: + // Cipher last two keys such that key information is + // cleared from V1 and V2. + VCIPHER V0, V1, V1 + VCIPHERLAST V1, V2, V2 + + // Store the result in BE order. + P8_STXVB16X(V2, R3, R0) + RET + +Linvalid_key_len: + // Segfault, this should never happen. Only 3 keys sizes are created/used. + MOVD R0, 0(R0) + RET + +// func decryptBlockAsm(nr int, xk *uint32, dst, src *byte) +TEXT ·decryptBlockAsm(SB), NOSPLIT|NOFRAME, $0 + MOVD nr+0(FP), R6 // Round count/Key size + MOVD xk+8(FP), R5 // Key pointer + MOVD dst+16(FP), R3 // Dest pointer + MOVD src+24(FP), R4 // Src pointer +#ifdef GOARCH_ppc64le + MOVD $·rcon(SB), R7 + LVX (R7), ESPERM // Permute value for P8_ macros. +#endif + + // Set CR{1,2,3}EQ to hold the key size information. + CMPU R6, $10, CR1 + CMPU R6, $12, CR2 + CMPU R6, $14, CR3 + + MOVD $16, R6 + MOVD $32, R7 + MOVD $48, R8 + MOVD $64, R9 + MOVD $80, R10 + MOVD $96, R11 + MOVD $112, R12 + + // Load text in BE order + P8_LXVB16X(R4, R0, V0) + + // V1, V2 will hold keys, V0 is a temp. + // At completion, V2 will hold the text. + // Load xk[0:3] and xor with ciphertext + LXVD2X (R0+R5), V1 + VXOR V0, V1, V0 + + // Load xk[4:11] and cipher + LXVD2X (R6+R5), V1 + LXVD2X (R7+R5), V2 + VNCIPHER V0, V1, V0 + VNCIPHER V0, V2, V0 + + // Load xk[12:19] and cipher + LXVD2X (R8+R5), V1 + LXVD2X (R9+R5), V2 + VNCIPHER V0, V1, V0 + VNCIPHER V0, V2, V0 + + // Load xk[20:27] and cipher + LXVD2X (R10+R5), V1 + LXVD2X (R11+R5), V2 + VNCIPHER V0, V1, V0 + VNCIPHER V0, V2, V0 + + // Increment xk pointer to reuse constant offsets in R6-R12. + ADD $112, R5 + + // Load xk[28:35] and cipher + LXVD2X (R0+R5), V1 + LXVD2X (R6+R5), V2 + VNCIPHER V0, V1, V0 + VNCIPHER V0, V2, V0 + + // Load xk[36:43] and cipher + LXVD2X (R7+R5), V1 + LXVD2X (R8+R5), V2 + BEQ CR1, Ldec_tail // Key size 10? + VNCIPHER V0, V1, V0 + VNCIPHER V0, V2, V0 + + // Load xk[44:51] and cipher + LXVD2X (R9+R5), V1 + LXVD2X (R10+R5), V2 + BEQ CR2, Ldec_tail // Key size 12? + VNCIPHER V0, V1, V0 + VNCIPHER V0, V2, V0 + + // Load xk[52:59] and cipher + LXVD2X (R11+R5), V1 + LXVD2X (R12+R5), V2 + BNE CR3, Linvalid_key_len // Not key size 14? + // Fallthrough to final cipher + +Ldec_tail: + // Cipher last two keys such that key information is + // cleared from V1 and V2. + VNCIPHER V0, V1, V1 + VNCIPHERLAST V1, V2, V2 + + // Store the result in BE order. + P8_STXVB16X(V2, R3, R0) + RET + +Linvalid_key_len: + // Segfault, this should never happen. Only 3 keys sizes are created/used. + MOVD R0, 0(R0) + RET + +// Remove defines from above so they can be defined here +#undef INP +#undef OUTENC +#undef ROUNDS +#undef KEY +#undef TMP + +// CBC encrypt or decrypt +// R3 src +// R4 dst +// R5 len +// R6 key +// R7 iv +// R8 enc=1 dec=0 +// Ported from: aes_p8_cbc_encrypt +// Register usage: +// R9: ROUNDS +// R10: Index +// V4: IV +// V5: SRC +// V7: DST + +#define INP R3 +#define OUT R4 +#define LEN R5 +#define KEY R6 +#define IVP R7 +#define ENC R8 +#define ROUNDS R9 +#define IDX R10 + +#define RNDKEY0 V0 +#define INOUT V2 +#define TMP V3 + +#define IVEC V4 + +// Vector loads are done using LVX followed by +// a VPERM using mask generated from previous +// LVSL or LVSR instruction, to obtain the correct +// bytes if address is unaligned. + +// Encryption is done with VCIPHER and VCIPHERLAST +// Decryption is done with VNCIPHER and VNCIPHERLAST + +// Encrypt and decypt is done as follows: +// - INOUT value is initialized in outer loop. +// - ROUNDS value is adjusted for loop unrolling. +// - Encryption/decryption is done in loop based on +// adjusted ROUNDS value. +// - Final INOUT value is encrypted/decrypted and stored. + +// Note: original implementation had an 8X version +// for decryption which was omitted to avoid the +// complexity. + +// func cryptBlocksChain(src, dst *byte, length int, key *uint32, iv *byte, enc int, nr int) +TEXT ·cryptBlocksChain(SB), NOSPLIT|NOFRAME, $0 + MOVD src+0(FP), INP + MOVD dst+8(FP), OUT + MOVD length+16(FP), LEN + MOVD key+24(FP), KEY + MOVD iv+32(FP), IVP + MOVD enc+40(FP), ENC + MOVD nr+48(FP), ROUNDS + +#ifdef GOARCH_ppc64le + MOVD $·rcon(SB), R11 + LVX (R11), ESPERM // Permute value for P8_ macros. +#endif + + CMPU LEN, $16 // cmpldi r5,16 + BC 14, 0, LR // bltlr-, return if len < 16. + CMPW ENC, $0 // cmpwi r8,0 + + P8_LXVB16X(IVP, R0, IVEC) // load ivec in BE register order + + SRW $1, ROUNDS // rlwinm r9,r9,31,1,31 + MOVD $0, IDX // li r10,0 + ADD $-1, ROUNDS // addi r9,r9,-1 + BEQ Lcbc_dec // beq + PCALIGN $16 + + // Outer loop: initialize encrypted value (INOUT) + // Load input (INPTAIL) ivec (IVEC) +Lcbc_enc: + P8_LXVB16X(INP, R0, INOUT) // load text in BE vreg order + ADD $16, INP // addi r3,r3,16 + MOVD ROUNDS, CTR // mtctr r9 + ADD $-16, LEN // addi r5,r5,-16 + LXVD2X (KEY+IDX), RNDKEY0 // load first xkey + ADD $16, IDX // addi r10,r10,16 + VXOR INOUT, RNDKEY0, INOUT // vxor v2,v2,v0 + VXOR INOUT, IVEC, INOUT // vxor v2,v2,v4 + + // Encryption loop of INOUT using RNDKEY0 +Loop_cbc_enc: + LXVD2X (KEY+IDX), RNDKEY0 // load next xkey + VCIPHER INOUT, RNDKEY0, INOUT // vcipher v2,v2,v1 + ADD $16, IDX // addi r10,r10,16 + LXVD2X (KEY+IDX), RNDKEY0 // load next xkey + VCIPHER INOUT, RNDKEY0, INOUT // vcipher v2,v2,v1 + ADD $16, IDX // addi r10,r10,16 + BDNZ Loop_cbc_enc + + // Encrypt tail values and store INOUT + LXVD2X (KEY+IDX), RNDKEY0 // load next xkey + VCIPHER INOUT, RNDKEY0, INOUT // vcipher v2,v2,v1 + ADD $16, IDX // addi r10,r10,16 + LXVD2X (KEY+IDX), RNDKEY0 // load final xkey + VCIPHERLAST INOUT, RNDKEY0, IVEC // vcipherlast v4,v2,v0 + MOVD $0, IDX // reset key index for next block + CMPU LEN, $16 // cmpldi r5,16 + P8_STXVB16X(IVEC, OUT, R0) // store ciphertext in BE order + ADD $16, OUT // addi r4,r4,16 + BGE Lcbc_enc // bge Lcbc_enc + BR Lcbc_done // b Lcbc_done + + // Outer loop: initialize decrypted value (INOUT) + // Load input (INPTAIL) ivec (IVEC) +Lcbc_dec: + P8_LXVB16X(INP, R0, TMP) // load ciphertext in BE vreg order + ADD $16, INP // addi r3,r3,16 + MOVD ROUNDS, CTR // mtctr r9 + ADD $-16, LEN // addi r5,r5,-16 + LXVD2X (KEY+IDX), RNDKEY0 // load first xkey + ADD $16, IDX // addi r10,r10,16 + VXOR TMP, RNDKEY0, INOUT // vxor v2,v3,v0 + PCALIGN $16 + + // Decryption loop of INOUT using RNDKEY0 +Loop_cbc_dec: + LXVD2X (KEY+IDX), RNDKEY0 // load next xkey + ADD $16, IDX // addi r10,r10,16 + VNCIPHER INOUT, RNDKEY0, INOUT // vncipher v2,v2,v1 + LXVD2X (KEY+IDX), RNDKEY0 // load next xkey + ADD $16, IDX // addi r10,r10,16 + VNCIPHER INOUT, RNDKEY0, INOUT // vncipher v2,v2,v0 + BDNZ Loop_cbc_dec + + // Decrypt tail values and store INOUT + LXVD2X (KEY+IDX), RNDKEY0 // load next xkey + ADD $16, IDX // addi r10,r10,16 + VNCIPHER INOUT, RNDKEY0, INOUT // vncipher v2,v2,v1 + LXVD2X (KEY+IDX), RNDKEY0 // load final xkey + MOVD $0, IDX // li r10,0 + VNCIPHERLAST INOUT, RNDKEY0, INOUT // vncipherlast v2,v2,v0 + CMPU LEN, $16 // cmpldi r5,16 + VXOR INOUT, IVEC, INOUT // vxor v2,v2,v4 + VOR TMP, TMP, IVEC // vor v4,v3,v3 + P8_STXVB16X(INOUT, OUT, R0) // store text in BE order + ADD $16, OUT // addi r4,r4,16 + BGE Lcbc_dec // bge + +Lcbc_done: + VXOR RNDKEY0, RNDKEY0, RNDKEY0 // clear key register + P8_STXVB16X(IVEC, R0, IVP) // Save ivec in BE order for next round. + RET // bclr 20,lt,0 + diff --git a/pkg/slayers/path/hummingbird/base.go b/pkg/slayers/path/hummingbird/base.go new file mode 100644 index 0000000000..1d4ccdaf27 --- /dev/null +++ b/pkg/slayers/path/hummingbird/base.go @@ -0,0 +1,153 @@ +package hummingbird + +import ( + "encoding/binary" + "fmt" + + "github.com/scionproto/scion/pkg/private/serrors" + "github.com/scionproto/scion/pkg/slayers/path" +) + +const MetaLen = 12 + +func RegisterPath() { + path.RegisterPath(path.Metadata{ + Type: PathType, + Desc: "Hummingbird", + New: func() path.Path { + return &Raw{} + }, + }) +} + +// Base holds the basic information that is used by both raw and fully decoded paths. +type Base struct { + // PathMeta is the SCION path meta header. It is always instantiated when + // decoding a path from bytes. + PathMeta MetaHdr + // NumINF is the number of InfoFields in the path. + NumINF int + // NumHops is the number HopFields in the path. + // If IsHummingbird is true, NumHops is the number of 4 bytes lines in the path instead of the number of hops. + // TODO: rename and reevaluate? + NumHops int +} + +func (s *Base) DecodeFromBytes(data []byte) error { + // PathMeta takes care of bounds check. + err := s.PathMeta.DecodeFromBytes(data) + if err != nil { + return err + } + s.NumINF = 0 + s.NumHops = 0 + for i := 2; i >= 0; i-- { + if s.PathMeta.SegLen[i] == 0 && s.NumINF > 0 { + return serrors.New( + fmt.Sprintf("Meta.SegLen[%d] == 0, but Meta.SegLen[%d] > 0", i, s.NumINF-1)) + } + if s.PathMeta.SegLen[i] > 0 && s.NumINF == 0 { + s.NumINF = i + 1 + } + s.NumHops += int(s.PathMeta.SegLen[i]) + } + return nil +} + +func (s *Base) IncPath(n int) error { + if s.NumINF == 0 { + return serrors.New("empty path cannot be increased") + } + if int(s.PathMeta.CurrHF) >= s.NumHops-n { + s.PathMeta.CurrHF = uint8(s.NumHops - n) + return serrors.New("Incrementing path over end") + } + s.PathMeta.CurrHF += uint8(n) + s.PathMeta.CurrINF = s.infIndexForHF(s.PathMeta.CurrHF) + return nil +} + +// IsXover returns whether we are at a crossover point. +func (s *Base) IsXover() bool { + return s.PathMeta.CurrHF+5 < uint8(s.NumHops) && + (s.PathMeta.CurrINF != s.infIndexForHF(s.PathMeta.CurrHF+3) || s.PathMeta.CurrINF != s.infIndexForHF(s.PathMeta.CurrHF+5)) + +} + +// IsFirstHopAfterXover returns whether this is the first hop field after a crossover point. +func (s *Base) IsFirstHopAfterXover() bool { + return s.PathMeta.CurrINF > 0 && s.PathMeta.CurrHF > 0 && + s.PathMeta.CurrINF-1 == s.infIndexForHF(s.PathMeta.CurrHF-1) +} + +func (s *Base) infIndexForHF(hf uint8) uint8 { + switch { + case hf < s.PathMeta.SegLen[0]: + return 0 + case hf < s.PathMeta.SegLen[0]+s.PathMeta.SegLen[1]: + return 1 + default: + return 2 + } +} + +// Len returns the length of the path in bytes. +func (s *Base) Len() int { + return MetaLen + s.NumINF*path.InfoLen + s.NumHops*path.LineLen +} + +// Type returns the type of the path. +func (s *Base) Type() path.Type { + return PathType +} + +// MetaHdr is the PathMetaHdr of a SCION (data-plane) path type. +type MetaHdr struct { + CurrINF uint8 + CurrHF uint8 + SegLen [3]uint8 + BaseTS uint32 + HighResTS uint32 +} + +// DecodeFromBytes populates the fields from a raw buffer. The buffer must be of length >= +// hummingbird.MetaLen. +func (m *MetaHdr) DecodeFromBytes(raw []byte) error { + if len(raw) < MetaLen { + return serrors.New("MetaHdr raw too short", "expected", MetaLen, "actual", len(raw)) + } + line := binary.BigEndian.Uint32(raw[0:4]) + m.CurrINF = uint8(line >> 30) + m.CurrHF = uint8(line >> 22) + m.SegLen[0] = uint8(line>>14) & 0x7F + m.SegLen[1] = uint8(line>>7) & 0x7F + m.SegLen[2] = uint8(line) & 0x7F + + m.BaseTS = binary.BigEndian.Uint32(raw[4:8]) + m.HighResTS = binary.BigEndian.Uint32(raw[8:12]) + + return nil +} + +// SerializeTo writes the fields into the provided buffer. The buffer must be of length >= +// hummingbird.MetaLen. +func (m *MetaHdr) SerializeTo(b []byte) error { + if len(b) < MetaLen { + return serrors.New("buffer for MetaHdr too short", "expected", MetaLen, "actual", len(b)) + } + line := uint32(m.CurrINF)<<30 | uint32(m.CurrHF)<<22 + line |= uint32(m.SegLen[0]&0x7F) << 14 + line |= uint32(m.SegLen[1]&0x7F) << 7 + line |= uint32(m.SegLen[2] & 0x7F) + binary.BigEndian.PutUint32(b[0:4], line) + + binary.BigEndian.PutUint32(b[4:8], m.BaseTS) + binary.BigEndian.PutUint32(b[8:12], m.HighResTS) + + return nil +} + +func (m MetaHdr) String() string { + return fmt.Sprintf("{CurrInf: %d, CurrHF: %d, SegLen: %v, BaseTimestamp: %v, HighResTimestamp: %v}", + m.CurrINF, m.CurrHF, m.SegLen, m.BaseTS, m.HighResTS) +} diff --git a/pkg/slayers/path/hummingbird/base_test.go b/pkg/slayers/path/hummingbird/base_test.go new file mode 100644 index 0000000000..7799fc35f4 --- /dev/null +++ b/pkg/slayers/path/hummingbird/base_test.go @@ -0,0 +1,146 @@ +package hummingbird_test + +import ( + "fmt" + "testing" + + "github.com/scionproto/scion/pkg/slayers/path/hummingbird" + "github.com/stretchr/testify/assert" +) + +func TestIncPath(t *testing.T) { + testCases := map[string]struct { + nsegs, nhops int + segLens [3]uint8 + inIdxs, wantIdxs [][2]int + }{ + "1 segment, 2 hops": { + nsegs: 1, + nhops: 6, + segLens: [3]uint8{6, 0, 0}, + inIdxs: [][2]int{{0, 0}, {0, 3}}, + wantIdxs: [][2]int{{0, 3}, {0, 0}}, + }, + "1 segment, 5 hops": { + nsegs: 1, + nhops: 15, + segLens: [3]uint8{15, 0, 0}, + inIdxs: [][2]int{{0, 0}, {0, 3}, {0, 6}, {0, 9}, {0, 12}}, + wantIdxs: [][2]int{{0, 3}, {0, 6}, {0, 9}, {0, 12}, {0, 0}}, + }, + "2 segments, 5 hops": { + nsegs: 2, + nhops: 15, + segLens: [3]uint8{6, 9, 0}, + inIdxs: [][2]int{{0, 0}, {0, 3}, {1, 6}, {1, 9}, {1, 12}}, + wantIdxs: [][2]int{{0, 3}, {1, 6}, {1, 9}, {1, 12}, {0, 0}}, + }, + "3 segments, 9 hops": { + nsegs: 3, + nhops: 27, + segLens: [3]uint8{6, 12, 9}, + inIdxs: [][2]int{ + {0, 0}, {0, 3}, {1, 6}, {1, 9}, {1, 12}, {1, 15}, {2, 18}, {2, 21}, {2, 24}, + }, + wantIdxs: [][2]int{ + {0, 3}, {1, 6}, {1, 9}, {1, 12}, {1, 15}, {2, 18}, {2, 21}, {2, 24}, {0, 0}, + }, + }, + } + + for name, tc := range testCases { + name, tc := name, tc + for i := range tc.inIdxs { + i := i + t.Run(fmt.Sprintf("%s case %d", name, i+1), func(t *testing.T) { + t.Parallel() + s := hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrINF: uint8(tc.inIdxs[i][0]), + CurrHF: uint8(tc.inIdxs[i][1]), + SegLen: tc.segLens, + }, + NumINF: tc.nsegs, + NumHops: tc.nhops, + } + err := s.IncPath(3) + if tc.wantIdxs[i][0] == 0 && tc.wantIdxs[i][1] == 0 { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.Equal(t, uint8(tc.wantIdxs[i][0]), s.PathMeta.CurrINF, "CurrINF") + assert.Equal(t, uint8(tc.wantIdxs[i][1]), s.PathMeta.CurrHF, "CurrHF") + } + + }) + } + } +} + +func TestBaseIsXOver(t *testing.T) { + testCases := map[string]struct { + nsegs, nhops int + segLens [3]uint8 + inIdxs [][2]int + xover []bool + }{ + "1 segment, 2 hops": { + nsegs: 1, + nhops: 6, + segLens: [3]uint8{6, 0, 0}, + inIdxs: [][2]int{{0, 0}, {0, 3}}, + xover: []bool{false, false}, + }, + "1 segment, 5 hops": { + nsegs: 1, + nhops: 19, + segLens: [3]uint8{19, 0, 0}, + inIdxs: [][2]int{{0, 0}, {0, 3}, {0, 8}, {0, 13}, {0, 16}}, + xover: []bool{false, false, false, false, false}, + }, + "2 segments, 5 hops": { + nsegs: 2, + nhops: 17, + segLens: [3]uint8{8, 9, 0}, + inIdxs: [][2]int{{0, 0}, {0, 3}, {1, 8}, {1, 11}, {1, 14}}, + xover: []bool{false, true, false, false, false}, + }, + "3 segments, 9 hops": { + nsegs: 3, + nhops: 37, + segLens: [3]uint8{6, 16, 15}, + inIdxs: [][2]int{ + {0, 0}, {0, 3}, {1, 6}, {1, 11}, {1, 14}, {1, 19}, {2, 22}, {2, 27}, {2, 32}, + }, + xover: []bool{false, true, false, false, false, true, false, false, false}, + }, + } + + for name, tc := range testCases { + name, tc := name, tc + for i := range tc.xover { + i := i + s := hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrINF: uint8(tc.inIdxs[i][0]), + CurrHF: uint8(tc.inIdxs[i][1]), + SegLen: tc.segLens, + }, + NumINF: tc.nsegs, + NumHops: tc.nhops, + } + t.Run(fmt.Sprintf("%s case %d", name, i+1), func(t *testing.T) { + t.Parallel() + assert.Equal(t, tc.xover[i], s.IsXover()) + }) + t.Run(fmt.Sprintf("%s case %d IsFirstAfterXover", name, i+1), func(t *testing.T) { + t.Parallel() + firstHopAfterXover := false + if i > 0 { + firstHopAfterXover = tc.xover[i-1] + } + assert.Equal(t, firstHopAfterXover, s.IsFirstHopAfterXover()) + }) + } + } +} diff --git a/pkg/slayers/path/hummingbird/decoded.go b/pkg/slayers/path/hummingbird/decoded.go new file mode 100644 index 0000000000..a456579596 --- /dev/null +++ b/pkg/slayers/path/hummingbird/decoded.go @@ -0,0 +1,182 @@ +package hummingbird + +import ( + "github.com/scionproto/scion/pkg/private/serrors" + "github.com/scionproto/scion/pkg/slayers/path" +) + +const ( + // MaxINFs is the maximum number of info fields in a Hummingbird path. + MaxINFs = 3 + // MaxHops is the maximum number of hop fields in a Hummingbird path. + MaxHops = 85 +) + +// Decoded implements the Hummingbird (data-plane) path type. Decoded is intended to be used in +// non-performance critical code paths, where the convenience of having a fully parsed path trumps +// the loss of performance. +type Decoded struct { + Base + // InfoFields contains all the InfoFields of the path. + InfoFields []path.InfoField + // HopFields contains all the HopFields of the path. + HopFields []FlyoverHopField +} + +// DecodeFromBytes fully decodes the SCION path into the corresponding fields. +func (s *Decoded) DecodeFromBytes(data []byte) error { + if err := s.Base.DecodeFromBytes(data); err != nil { + return err + } + // fmt.Printf("s: %v\n", s) + // fmt.Print(s.Len()) + if minLen := s.Len(); len(data) < minLen { + return serrors.New("DecodedPath raw too short", "expected", minLen, "actual", len(data)) + } + + offset := MetaLen + s.InfoFields = make([]path.InfoField, s.NumINF) + for i := 0; i < s.NumINF; i++ { + if err := s.InfoFields[i].DecodeFromBytes(data[offset : offset+path.InfoLen]); err != nil { + return err + } + offset += path.InfoLen + } + + // Allocate maximum number of possible hopfields based on length + s.HopFields = make([]FlyoverHopField, s.NumHops/3) + i, j := 0, 0 + // If last hop is not a flyover hop, decode it with only 12 bytes slice + for ; j < s.NumHops-3; i++ { + if err := s.HopFields[i].DecodeFromBytes(data[offset : offset+path.FlyoverLen]); err != nil { + return err + } + if s.HopFields[i].Flyover { + offset += path.FlyoverLen + j += 5 + } else { + offset += path.HopLen + j += 3 + } + } + if j == s.NumHops-3 { + if err := s.HopFields[i].DecodeFromBytes(data[offset : offset+path.HopLen]); err != nil { + return err + } + i++ + } + s.HopFields = s.HopFields[:i] + return nil +} + +// SerializeTo writes the path to a slice. The slice must be big enough to hold the entire data, +// otherwise an error is returned. +func (s *Decoded) SerializeTo(b []byte) error { + if len(b) < s.Len() { + return serrors.New("buffer too small to serialize path.", "expected", s.Len(), + "actual", len(b)) + } + var offset int + + offset = MetaLen + if err := s.PathMeta.SerializeTo(b[:MetaLen]); err != nil { + return err + } + + for _, info := range s.InfoFields { + if err := info.SerializeTo(b[offset : offset+path.InfoLen]); err != nil { + return err + } + offset += path.InfoLen + } + for _, hop := range s.HopFields { + if hop.Flyover { + if err := hop.SerializeTo(b[offset : offset+path.FlyoverLen]); err != nil { + return err + } + offset += path.FlyoverLen + } else { + if err := hop.SerializeTo(b[offset : offset+path.HopLen]); err != nil { + return err + } + offset += path.HopLen + } + + } + return nil +} + +// Reverse reverses a SCION path. +// Removes all reservations from a Hummingbird path, as these are not bidirectional +func (s *Decoded) Reverse() (path.Path, error) { + if s.NumINF == 0 { + return nil, serrors.New("empty decoded path is invalid and cannot be reversed") + } + + if err := s.RemoveFlyovers(); err != nil { + return nil, err + } + // Reverse order of InfoFields and SegLens + for i, j := 0, s.NumINF-1; i < j; i, j = i+1, j-1 { + s.InfoFields[i], s.InfoFields[j] = s.InfoFields[j], s.InfoFields[i] + s.PathMeta.SegLen[i], s.PathMeta.SegLen[j] = s.PathMeta.SegLen[j], s.PathMeta.SegLen[i] + } + // Reverse cons dir flags + for i := 0; i < s.NumINF; i++ { + info := &s.InfoFields[i] + info.ConsDir = !info.ConsDir + } + // Reverse order of hop fields + for i, j := 0, len(s.HopFields)-1; i < j; i, j = i+1, j-1 { + s.HopFields[i], s.HopFields[j] = s.HopFields[j], s.HopFields[i] + } + // Update CurrINF and CurrHF and SegLens + s.PathMeta.CurrINF = uint8(s.NumINF) - s.PathMeta.CurrINF - 1 + s.PathMeta.CurrHF = uint8(s.NumHops) - s.PathMeta.CurrHF - 3 + + return s, nil +} + +// RemoveFlyovers removes all reservations from a decoded path and corrects SegLen and CurrHF accordingly +func (s *Decoded) RemoveFlyovers() error { + var idxInf uint8 = 0 + var offset uint8 = 0 + var segCount uint8 = 0 + + for i, hop := range s.HopFields { + if idxInf > 2 { + return serrors.New("path appears to have more than 3 segments during flyover removal") + } + if hop.Flyover { + s.HopFields[i].Flyover = false + + if s.PathMeta.CurrHF > offset { + s.PathMeta.CurrHF -= 2 + } + s.Base.NumHops -= 2 + s.PathMeta.SegLen[idxInf] -= 2 + } + segCount += 3 + if s.PathMeta.SegLen[idxInf] == segCount { + segCount = 0 + idxInf += 1 + } else if s.PathMeta.SegLen[idxInf] < segCount { + return serrors.New("new hopfields boundaries do not match new segment lengths after flyover removal") + } + offset += 3 + } + return nil +} + +// ToRaw tranforms scion.Decoded into scion.Raw. +func (s *Decoded) ToRaw() (*Raw, error) { + b := make([]byte, s.Len()) + if err := s.SerializeTo(b); err != nil { + return nil, err + } + raw := &Raw{} + if err := raw.DecodeFromBytes(b); err != nil { + return nil, err + } + return raw, nil +} diff --git a/pkg/slayers/path/hummingbird/decoded_test.go b/pkg/slayers/path/hummingbird/decoded_test.go new file mode 100644 index 0000000000..1615266861 --- /dev/null +++ b/pkg/slayers/path/hummingbird/decoded_test.go @@ -0,0 +1,240 @@ +package hummingbird_test + +import ( + "fmt" + "testing" + + "github.com/scionproto/scion/pkg/slayers/path" + "github.com/scionproto/scion/pkg/slayers/path/hummingbird" + "github.com/stretchr/testify/assert" +) + +var testInfoFields = []path.InfoField{ + { + Peer: false, + ConsDir: false, + SegID: 0x111, + Timestamp: 0x100, + }, + { + Peer: false, + ConsDir: true, + SegID: 0x222, + Timestamp: 0x100, + }, +} + +var testFlyoverFields = []hummingbird.FlyoverHopField{ + { + HopField: path.HopField{ + ExpTime: 63, + ConsIngress: 1, + ConsEgress: 0, + Mac: [path.MacLen]byte{1, 2, 3, 4, 5, 6}, + }, + Flyover: true, + ResID: 0, + Bw: 4, + ResStartTime: 2, + Duration: 1, + }, + { + HopField: path.HopField{ + ExpTime: 63, + ConsIngress: 3, + ConsEgress: 2, + Mac: [path.MacLen]byte{1, 2, 3, 4, 5, 6}, + }, + }, + { + HopField: path.HopField{ + ExpTime: 63, + ConsIngress: 0, + ConsEgress: 2, + Mac: [path.MacLen]byte{1, 2, 3, 4, 5, 6}, + }, + }, + { + HopField: path.HopField{ + ExpTime: 63, + ConsIngress: 1, + ConsEgress: 0, + Mac: [path.MacLen]byte{1, 2, 3, 4, 5, 6}, + }, + Flyover: true, + ResID: 0, + Bw: 4, + ResStartTime: 0, + Duration: 1, + }, +} + +var decodedHbirdTestPath = &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrINF: 0, + CurrHF: 0, + SegLen: [3]uint8{8, 8, 0}, + BaseTS: 808, + HighResTS: 1234, + }, + NumINF: 2, + NumHops: 16, + }, + InfoFields: testInfoFields, + HopFields: testFlyoverFields, +} + +var emptyDecodedTestPath = &hummingbird.Decoded{ + Base: hummingbird.Base{}, + InfoFields: []path.InfoField{}, + HopFields: []hummingbird.FlyoverHopField{}, +} + +var rawHbirdPath = []byte("\x00\x02\x04\x00\x00\x00\x03\x28\x00\x00\x04\xd2" + //Pathmeta header + "\x00\x00\x01\x11\x00\x00\x01\x00\x01\x00\x02\x22\x00\x00\x01\x00" + //Infofields + "\x80\x3f\x00\x01\x00\x00\x01\x02\x03\x04\x05\x06\x00\x00\x00\x04\x00\x02\x00\x01" + //flyoverfield 0 + "\x00\x3f\x00\x03\x00\x02\x01\x02\x03\x04\x05\x06" + //hopfield 1 + "\x00\x3f\x00\x00\x00\x02\x01\x02\x03\x04\x05\x06" + //hopfield 2 + "\x80\x3f\x00\x01\x00\x00\x01\x02\x03\x04\x05\x06\x00\x00\x00\x04\x00\x00\x00\x01") //flyoverfield 3 + +type hbirdPathCase struct { + infos []bool + hops [][][]uint16 +} + +var pathReverseCasesHbird = map[string]struct { + input hbirdPathCase + want hbirdPathCase + inIdxs [][2]int + wantIdxs [][2]int +}{ + "1 segment, 2 hops": { + input: hbirdPathCase{[]bool{true}, [][][]uint16{{{11, 0}, {12, 1}}}}, + want: hbirdPathCase{[]bool{false}, [][][]uint16{{{12, 0}, {11, 0}}}}, + inIdxs: [][2]int{{0, 0}, {0, 3}}, + wantIdxs: [][2]int{{0, 3}, {0, 0}}, + }, + "1 segment, 5 hops": { + input: hbirdPathCase{[]bool{true}, [][][]uint16{{{11, 1}, {12, 1}, {13, 0}, {14, 1}, {15, 0}}}}, + want: hbirdPathCase{[]bool{false}, [][][]uint16{{{15, 0}, {14, 0}, {13, 0}, {12, 0}, {11, 0}}}}, + inIdxs: [][2]int{{0, 0}, {0, 5}, {0, 10}, {0, 13}, {0, 18}}, + wantIdxs: [][2]int{{0, 12}, {0, 9}, {0, 6}, {0, 3}, {0, 0}}, + }, + "2 segments, 5 hops": { + input: hbirdPathCase{[]bool{true, false}, [][][]uint16{{{11, 0}, {12, 0}}, {{13, 1}, {14, 1}, {15, 0}}}}, + want: hbirdPathCase{[]bool{true, false}, [][][]uint16{{{15, 0}, {14, 0}, {13, 0}}, {{12, 0}, {11, 0}}}}, + inIdxs: [][2]int{{0, 0}, {0, 3}, {1, 6}, {1, 11}, {1, 16}}, + wantIdxs: [][2]int{{1, 12}, {1, 9}, {0, 6}, {0, 3}, {0, 0}}, + }, + "3 segments, 9 hops": { + input: hbirdPathCase{ + []bool{true, false, false}, + [][][]uint16{ + {{11, 1}, {12, 0}}, + {{13, 0}, {14, 1}, {15, 1}, {16, 0}}, + {{17, 0}, {18, 1}, {19, 1}}, + }, + }, + want: hbirdPathCase{ + []bool{true, true, false}, + [][][]uint16{ + {{19, 0}, {18, 0}, {17, 0}}, + {{16, 0}, {15, 0}, {14, 0}, {13, 0}}, + {{12, 0}, {11, 0}}, + }, + }, + inIdxs: [][2]int{ + {0, 0}, {0, 5}, {1, 8}, {1, 11}, {1, 16}, {1, 21}, {2, 24}, {2, 27}, {2, 32}, + }, + wantIdxs: [][2]int{ + {2, 24}, {2, 21}, {1, 18}, {1, 15}, {1, 12}, {1, 9}, {0, 6}, {0, 3}, {0, 0}, + }, + }, +} + +func TestDecodedSerializeHbird(t *testing.T) { + b := make([]byte, decodedHbirdTestPath.Len()) + assert.NoError(t, decodedHbirdTestPath.SerializeTo(b)) + assert.Equal(t, rawHbirdPath, b) +} + +func TestDecodedDecodeFromBytesHbird(t *testing.T) { + s := &hummingbird.Decoded{} + assert.NoError(t, s.DecodeFromBytes(rawHbirdPath)) + assert.Equal(t, decodedHbirdTestPath, s) +} + +func TestDecodedSerializeDecodeHbird(t *testing.T) { + b := make([]byte, decodedHbirdTestPath.Len()) + assert.NoError(t, decodedHbirdTestPath.SerializeTo(b)) + s := &hummingbird.Decoded{} + assert.NoError(t, s.DecodeFromBytes(b)) + assert.Equal(t, decodedHbirdTestPath, s) +} + +func TestDecodedReverseHbird(t *testing.T) { + for name, tc := range pathReverseCasesHbird { + name, tc := name, tc + for i := range tc.inIdxs { + i := i + t.Run(fmt.Sprintf("%s case %d", name, i+1), func(t *testing.T) { + t.Parallel() + inputPath := mkDecodedHbirdPath(t, tc.input, uint8(tc.inIdxs[i][0]), + uint8(tc.inIdxs[i][1])) + wantPath := mkDecodedHbirdPath(t, tc.want, uint8(tc.wantIdxs[i][0]), + uint8(tc.wantIdxs[i][1])) + revPath, err := inputPath.Reverse() + assert.NoError(t, err) + assert.Equal(t, wantPath, revPath) + }) + } + } +} + +func TestEmptyDecodedReverse(t *testing.T) { + _, err := emptyDecodedTestPath.Reverse() + assert.Error(t, err) +} + +func TestDecodedToRawHbird(t *testing.T) { + raw, err := decodedHbirdTestPath.ToRaw() + assert.NoError(t, err) + assert.Equal(t, rawHbirdTestPath, raw) +} + +func mkDecodedHbirdPath(t *testing.T, pcase hbirdPathCase, infIdx, hopIdx uint8) *hummingbird.Decoded { + t.Helper() + s := &hummingbird.Decoded{} + meta := hummingbird.MetaHdr{ + CurrINF: infIdx, + CurrHF: hopIdx, + BaseTS: 14, + HighResTS: 15, + } + for _, dir := range pcase.infos { + s.InfoFields = append(s.InfoFields, path.InfoField{ConsDir: dir}) + } + i := 0 + for j, hops := range pcase.hops { + for _, hop := range hops { + f := hop[1] == 1 + s.HopFields = append(s.HopFields, hummingbird.FlyoverHopField{ + HopField: path.HopField{ConsIngress: hop[0], ConsEgress: hop[0], Mac: [6]byte{1, 2, 3, 4, 5, 6}}, + Flyover: f, + Duration: 2}) + if f { + i += 5 + meta.SegLen[j] += 5 + } else { + i += 3 + meta.SegLen[j] += 3 + } + } + } + s.PathMeta = meta + s.NumINF = len(pcase.infos) + s.NumHops = i + + return s +} diff --git a/pkg/slayers/path/hummingbird/flyoverhopfield.go b/pkg/slayers/path/hummingbird/flyoverhopfield.go new file mode 100644 index 0000000000..840ee29fc4 --- /dev/null +++ b/pkg/slayers/path/hummingbird/flyoverhopfield.go @@ -0,0 +1,89 @@ +package hummingbird + +import ( + "encoding/binary" + + "github.com/scionproto/scion/pkg/private/serrors" + "github.com/scionproto/scion/pkg/slayers/path" +) + +const ( + LineLen = 4 + FlyoverLen = 20 +) + +type FlyoverHopField struct { + // SCiON Hopfield part of the FlyoverHopField + HopField path.HopField + // True if flyover is present + Flyover bool + // ResID is the Reservation ID of the flyover + ResID uint32 + // Bw is the reserved banwidth of the flyover + Bw uint16 + // ResStartTime is the start time of the reservation, as a negative offset from the BaseTimeStamp in the PathMetaHdr + ResStartTime uint16 + // Duration is the duration of the reservation + Duration uint16 +} + +// DecodeFromBytes populates the fields from a raw buffer. +// The buffer must be of length >= path.HopLen. +// @ requires len(raw) >= HopLen +// DecodeFromBytes modifies the fields of *h and reads (but does not modify) the contents of raw. +// @ preserves acc(h) && acc(raw, 1/2) +// When a call that satisfies the precondition (len(raw) >= HopLen) is made, +// the return value is guaranteed to be nil. +// @ ensures err == nil +// Calls to DecodeFromBytes are always guaranteed to terminate. +// @ decreases +func (h *FlyoverHopField) DecodeFromBytes(raw []byte) (err error) { + if err := h.HopField.DecodeFromBytes(raw); err != nil { + return err + } + h.Flyover = raw[0]&0x80 == 0x80 + if h.Flyover { + if len(raw) < FlyoverLen { + return serrors.New("FlyoverHopField raw too short", "expected", FlyoverLen, "actual", len(raw)) + } + //@ assert &raw[12:16][0] == &raw[12] && &raw[12:16][1] == &raw[12] && &raw[12:16][2] == &raw[14] && &raw[12:16][3] == &raw[15] + h.ResID = binary.BigEndian.Uint32(raw[12:16]) >> 10 + h.Bw = binary.BigEndian.Uint16(raw[14:16]) & 0x03ff + //@ assert &raw[16:18][0] == &raw[16] && &raw[16:18][1] == &raw[17] + h.ResStartTime = binary.BigEndian.Uint16(raw[16:18]) + //@ assert &raw[18:20][0] == &raw[18] && &raw[18:20][1] == &raw[19] + h.Duration = binary.BigEndian.Uint16(raw[18:20]) + } + return nil +} + +// SerializeTo writes the fields into the provided buffer. +// The buffer must be of length >= path.HopLen. +// @ requires len(b) >= HopLen +// SerializeTo reads (but does not modify) the fields of *h and writes to the contents of b. +// @ preserves acc(h, 1/2) && acc(b) +// When a call that satisfies the precondition (len(b) >= HopLen) is made, +// the return value is guaranteed to be nil. +// @ ensures err == nil +// Calls to SerializeTo are guaranteed to terminate. +// @ decreases +func (h *FlyoverHopField) SerializeTo(b []byte) (err error) { + if err := h.HopField.SerializeTo(b); err != nil { + return err + } + + if h.Flyover { + if len(b) < FlyoverLen { + return serrors.New("buffer for FlyoverHopField too short", "expected", FlyoverLen, "actual", len(b)) + } + b[0] |= 0x80 + //@ assert &b[12:16][0] == &b[12] && &b[12:16][1] == &b[12] && &b[12:16][2] == &b[14] && &b[12:16][3] == &b[15] + binary.BigEndian.PutUint32(b[12:16], h.ResID<<10+uint32(h.Bw)) + //@ assert &b[16:18][0] == &b[16] && &b[16:18][1] == &b[17] + binary.BigEndian.PutUint16(b[16:18], h.ResStartTime) + //@ assert &b[18:20][0] == &b[18] && &b[18:20][1] == &b[19] + binary.BigEndian.PutUint16(b[18:20], h.Duration) + } + + return nil +} diff --git a/pkg/slayers/path/hummingbird/flyoverhopfield_test.go b/pkg/slayers/path/hummingbird/flyoverhopfield_test.go new file mode 100644 index 0000000000..165864a8ca --- /dev/null +++ b/pkg/slayers/path/hummingbird/flyoverhopfield_test.go @@ -0,0 +1,33 @@ +package hummingbird_test + +import ( + "testing" + + "github.com/scionproto/scion/pkg/slayers/path" + "github.com/scionproto/scion/pkg/slayers/path/hummingbird" + "github.com/stretchr/testify/assert" +) + +func TestFlyoverHopSerializeDecode(t *testing.T) { + want := &hummingbird.FlyoverHopField{ + HopField: path.HopField{ + IngressRouterAlert: true, + EgressRouterAlert: true, + ExpTime: 63, + ConsIngress: 1, + ConsEgress: 0, + Mac: [path.MacLen]byte{1, 2, 3, 4, 5, 6}, + }, + Flyover: true, + ResID: 782, + Bw: 23, + ResStartTime: 233, + Duration: 11, + } + b := make([]byte, hummingbird.FlyoverLen) + assert.NoError(t, want.SerializeTo(b)) + + got := &hummingbird.FlyoverHopField{} + assert.NoError(t, got.DecodeFromBytes(b)) + assert.Equal(t, want, got) +} diff --git a/pkg/slayers/path/hummingbird/mac.go b/pkg/slayers/path/hummingbird/mac.go new file mode 100644 index 0000000000..6f79c902c5 --- /dev/null +++ b/pkg/slayers/path/hummingbird/mac.go @@ -0,0 +1,125 @@ +//go:build amd64 || arm64 || ppc64 || ppc64le + +package hummingbird + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/subtle" + "encoding/binary" + + "github.com/scionproto/scion/pkg/addr" + "github.com/scionproto/scion/pkg/slayers/path" +) + +// defined in asm_* assembly files + +//go:noescape +func encryptBlockAsm(nr int, xk *uint32, dst, src *byte) + +//TODO: test expandKeyAsm on arm64 and ppc64 machines. Compare with code in go/src/crypto/aes/asm_* if necessary + +//go:noescape +func expandKeyAsm(nr int, key *byte, enc *uint32) + +const AkBufferSize = 16 +const FlyoverMacBufferSize = 32 +const XkBufferSize = 44 +const PathType = 5 + +var ZeroBlock [aes.BlockSize]byte + +// Derive authentication key A_k +// block is expected to be initialized beforehand with aes.NewCipher(sv), where sv is this AS' secret value +func DeriveAuthKey(block cipher.Block, resId uint32, bw, in, eg uint16, startTime uint32, resDuration uint16, buffer []byte) []byte { + + if len(buffer) < AkBufferSize { + buffer = make([]byte, AkBufferSize) + } + //prepare input + binary.BigEndian.PutUint32(buffer[0:4], resId<<10|uint32(bw)) + binary.BigEndian.PutUint16(buffer[4:6], in) + binary.BigEndian.PutUint16(buffer[6:8], eg) + binary.BigEndian.PutUint32(buffer[8:12], startTime) + binary.BigEndian.PutUint16(buffer[12:14], resDuration) + binary.BigEndian.PutUint16(buffer[14:16], 0) //padding + + // should xor input with iv, but we use iv = 0 => identity + block.Encrypt(buffer[0:16], buffer[0:16]) + return buffer[0:16] +} + +// shifts left a 16 bytes array +func shiftLeft(in []byte) { + flag := (in[8]&byte(128))>>7 == 1 + binary.BigEndian.PutUint64(in[0:8], binary.BigEndian.Uint64(in[0:8])<<1) + binary.BigEndian.PutUint64(in[8:16], binary.BigEndian.Uint64(in[8:16])<<1) + if flag { + in[7] |= 0x01 + } +} + +func xor(a, b []byte) { + binary.BigEndian.PutUint64(a[0:8], binary.BigEndian.Uint64(a[0:8])^binary.BigEndian.Uint64(b[0:8])) + binary.BigEndian.PutUint64(a[8:16], binary.BigEndian.Uint64(a[8:16])^binary.BigEndian.Uint64(b[8:16])) +} + +// Computes full flyover mac vk +// Needs a xkbuffer of 44 uint32s to store the expanded keys for aes +// dummy buffer is memory used by key expansion to store decryption keys +func FullFlyoverMac(ak []byte, dstIA addr.IA, pktlen uint16, resStartTime uint16, highResTime uint32, buffer []byte, xkbuffer []uint32) []byte { + if len(buffer) < FlyoverMacBufferSize { + buffer = make([]byte, FlyoverMacBufferSize) + } + if len(xkbuffer) < XkBufferSize { + xkbuffer = make([]uint32, XkBufferSize) + } + + binary.BigEndian.PutUint64(buffer[0:8], uint64(dstIA)) + binary.BigEndian.PutUint16(buffer[8:10], pktlen) + binary.BigEndian.PutUint16(buffer[10:12], resStartTime) + binary.BigEndian.PutUint32(buffer[12:16], highResTime) + + expandKeyAsm(10, &ak[0], &xkbuffer[0]) + //compute subkeys + encryptBlockAsm(10, &xkbuffer[0], &buffer[16], &ZeroBlock[0]) + + // Compute K1. Ignore K2 since we will always use K1 + flag1 := buffer[16]&byte(128) == 0 + shiftLeft(buffer[16:32]) + if !flag1 { + buffer[31] ^= 0x87 + } + //Compute cmac + xor(buffer[0:16], buffer[16:32]) + + encryptBlockAsm(10, &xkbuffer[0], &buffer[0], &buffer[0]) + + return buffer[0:16] +} + +// Compares two 16 byte arrays. +// Always returns false if at least one input has a length different from 16 +// Returns true if equal, false otherwise +func CompareAk(a []byte, b []byte) bool { + if len(a) != 16 || len(b) != 16 { + return false + } + return binary.BigEndian.Uint64(a[0:8]) == binary.BigEndian.Uint64(b[0:8]) && binary.BigEndian.Uint64(a[8:16]) == binary.BigEndian.Uint64(b[8:16]) +} + +// around 800 ns + +// Compares two 6 byte arrays. +// Always returns false if at least one input is of a different length. +// Returns true if equal, false otherwise. +func CompareVk(a, b []byte) bool { + if len(a) != 6 || len(b) != 6 { + return false + } + return binary.BigEndian.Uint32(a) == binary.BigEndian.Uint32(b) && a[4] == b[4] && a[5] == b[5] +} + +func SubtleCompare(a, b []byte) bool { + return subtle.ConstantTimeCompare(a[:path.MacLen], b[:path.MacLen]) == 0 +} diff --git a/pkg/slayers/path/hummingbird/mac_test.go b/pkg/slayers/path/hummingbird/mac_test.go new file mode 100644 index 0000000000..c08c70b221 --- /dev/null +++ b/pkg/slayers/path/hummingbird/mac_test.go @@ -0,0 +1,212 @@ +package hummingbird_test + +import ( + "crypto/aes" + "crypto/cipher" + "encoding/binary" + "testing" + + "github.com/dchest/cmac" + "github.com/scionproto/scion/pkg/addr" + "github.com/scionproto/scion/pkg/slayers/path/hummingbird" + "github.com/stretchr/testify/require" +) + +func TestDeriveAuthKey(t *testing.T) { + sv := []byte{0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7} + var resId uint32 = 0x40 + var bw uint16 = 0x0203 + buffer := make([]byte, 16) + var in uint16 = 2 + var eg uint16 = 5 + var start uint32 = 0x0030001 + var duration uint16 = 0x0203 + + block, err := aes.NewCipher(sv) + if err != nil { + require.Fail(t, err.Error()) + } + + // Compute expected result with library CBC + expected := make([]byte, 16) + binary.BigEndian.PutUint32(expected[0:4], resId<<10) + expected[2] |= byte(bw >> 8) + expected[3] = byte(bw) + binary.BigEndian.PutUint16(expected[4:6], in) + binary.BigEndian.PutUint16(expected[6:8], eg) + binary.BigEndian.PutUint32(expected[8:12], start) + binary.BigEndian.PutUint16(expected[12:14], duration) + binary.BigEndian.PutUint16(expected[14:16], 0) + + var ZeroBlock [aes.BlockSize]byte + mode := cipher.NewCBCEncrypter(block, ZeroBlock[:]) + mode.CryptBlocks(expected, expected) + + // Run DeriveAuthKey Function + block, err = aes.NewCipher(sv) + if err != nil { + require.Fail(t, err.Error()) + } + + key := hummingbird.DeriveAuthKey(block, resId, bw, in, eg, start, duration, buffer) + require.Equal(t, expected, key) + + key = hummingbird.DeriveAuthKey(block, resId, bw, in, eg, start, duration, buffer) + require.Equal(t, expected, key) +} + +func BenchmarkDeriveAuthKey(b *testing.B) { + sv := []byte{0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7} + + var resId uint32 = 0x40 + var bw uint16 = 0x0203 + buffer := make([]byte, 16) + var in uint16 = 2 + var eg uint16 = 5 + var start uint32 = 0x0030001 + var duration uint16 = 0x0203 + + block, err := aes.NewCipher(sv) + if err != nil { + require.Fail(b, err.Error()) + } + b.ResetTimer() + + for i := 0; i < b.N; i++ { + hummingbird.DeriveAuthKey(block, resId, bw, in, eg, start, duration, buffer) + } +} + +// BenchmarkDeriveAuthKeyManually benchmarks obtaining Ak by just using the stdlib. +// Results in my machine of 5.987 ns/op. +func BenchmarkDeriveAuthKeyManually(b *testing.B) { + sv := []byte{0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7} + var resId uint32 = 0x40 + var bw uint16 = 0x0203 + var in uint16 = 2 + var eg uint16 = 5 + var start uint16 = 0x0001 + var end uint16 = 0x0203 + + src := make([]byte, hummingbird.AkBufferSize) + binary.BigEndian.PutUint32(src[0:4], resId<<10) + src[2] |= byte(bw >> 8) + src[3] = byte(bw) + binary.BigEndian.PutUint16(src[4:6], in) + binary.BigEndian.PutUint16(src[6:8], eg) + binary.BigEndian.PutUint16(src[8:10], start) + binary.BigEndian.PutUint16(src[10:12], end) + binary.BigEndian.PutUint32(src[12:16], 0) //padding + + buffer := make([]byte, 16) + block, err := aes.NewCipher(sv) + require.NoError(b, err) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + block.Encrypt(buffer[:], src) + } +} + +// verified with https://artjomb.github.io/cryptojs-extension/ + +func TestFlyOverMac(t *testing.T) { + ak := []byte{142, 19, 145, 119, 76, 2, 228, 18, 134, 111, 116, 45, 200, 172, 113, 219} + var dstIA addr.IA = 326 + var pktlen uint16 = 23 + var resStartTs uint16 = 1234 + var highResTs uint32 = 4321 + buffer := make([]byte, 32) + xkbuffer := make([]uint32, 44) + + // Compute expected output based on library cmac implementation + expected := make([]byte, 16) + binary.BigEndian.PutUint64(expected[0:8], uint64(dstIA)) + binary.BigEndian.PutUint16(expected[8:10], pktlen) + binary.BigEndian.PutUint16(expected[10:12], resStartTs) + binary.BigEndian.PutUint32(expected[12:16], highResTs) + + block, err := aes.NewCipher(ak) + if err != nil { + require.Fail(t, err.Error()) + } + c, err := cmac.New(block) + if err != nil { + require.Fail(t, err.Error()) + } + if _, err := c.Write(expected[0:16]); err != nil { + require.Fail(t, err.Error()) + } + + expected = c.Sum(expected[:0]) + + //expected with 0, 23, 1234, 4321: 726f7d9e 17e3cbe1 d47a32eb d8a5e26e + mac := hummingbird.FullFlyoverMac(ak, dstIA, pktlen, resStartTs, highResTs, buffer, xkbuffer) + require.Equal(t, expected, mac) + mac = hummingbird.FullFlyoverMac(ak, dstIA, pktlen, resStartTs, highResTs, buffer, xkbuffer) + require.Equal(t, expected, mac) +} + +func BenchmarkFlyoverMac(b *testing.B) { + ak := []byte{142, 19, 145, 119, 76, 2, 228, 18, 134, 111, 116, 45, 200, 172, 113, 219} + var dstIA addr.IA = 326 + var pktlen uint16 = 23 + var resStartTs uint16 = 1234 + var highResTs uint32 = 4321 + buffer := make([]byte, 32) + xkbuffer := make([]uint32, 44) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + hummingbird.FullFlyoverMac(ak, dstIA, pktlen, resStartTs, highResTs, buffer, xkbuffer) + } +} + +func TestCompareAk(t *testing.T) { + a := []byte{142, 19, 145, 119, 76, 2, 228, 18, 134, 111, 116, 45, 200, 172, 113, 219} + b := []byte{142, 19, 145, 151, 76, 2, 228, 18, 134, 111, 116, 45, 200, 172, 113, 219} + c := []byte{142, 19, 145, 119, 76, 2, 228, 18, 134, 111, 116, 45, 200, 172, 113, 218} + d := []byte{142, 19, 145, 119, 76, 2, 228, 18, 134, 111, 116, 45, 200, 172, 113, 219} + + require.True(t, hummingbird.CompareAk(a, d)) + require.False(t, hummingbird.CompareAk(a, b)) + require.False(t, hummingbird.CompareAk(a, c)) +} + +func BenchmarkCompareAk(b *testing.B) { + a := []byte{142, 19, 145, 119, 76, 2, 228, 18, 134, 111, 116, 45, 200, 172, 113, 219} + c := []byte{142, 19, 145, 119, 76, 2, 228, 18, 134, 111, 116, 45, 200, 172, 113, 218} + b.ResetTimer() + for i := 0; i < b.N; i++ { + hummingbird.CompareAk(a, c) + } +} + +func TestCompareVk(t *testing.T) { + a := []byte{1, 2, 3, 4, 5, 6} + b := []byte{1, 2, 3, 4, 5, 6} + c := []byte{2, 2, 3, 4, 5, 6} + d := []byte{1, 2, 3, 6, 5, 6} + + require.True(t, hummingbird.CompareVk(a, b)) + require.False(t, hummingbird.CompareVk(a, c)) + require.False(t, hummingbird.CompareVk(a, d)) +} + +func BenchmarkCompareVk(b *testing.B) { + a := []byte{1, 2, 3, 4, 5, 6} + c := []byte{1, 2, 4, 4, 5, 6} + b.ResetTimer() + for i := 0; i < b.N; i++ { + hummingbird.CompareVk(a, c) + } +} + +func BenchmarkSubtleCompare(b *testing.B) { + a := []byte{1, 2, 3, 4, 5, 6} + c := []byte{1, 2, 4, 4, 5, 6} + b.ResetTimer() + for i := 0; i < b.N; i++ { + hummingbird.SubtleCompare(a, c) + } +} diff --git a/pkg/slayers/path/hummingbird/raw.go b/pkg/slayers/path/hummingbird/raw.go new file mode 100644 index 0000000000..fbb360ce62 --- /dev/null +++ b/pkg/slayers/path/hummingbird/raw.go @@ -0,0 +1,207 @@ +package hummingbird + +import ( + "github.com/scionproto/scion/pkg/private/serrors" + "github.com/scionproto/scion/pkg/slayers/path" +) + +// Raw is a raw representation of the Hummingbird (data-plane) path type. It is designed to parse as +// little as possible and should be used if performance matters. +type Raw struct { + Base + Raw []byte +} + +// DecodeFromBytes only decodes the PathMetaHeader. Otherwise the nothing is decoded and simply kept +// as raw bytes. +func (s *Raw) DecodeFromBytes(data []byte) error { + if err := s.Base.DecodeFromBytes(data); err != nil { + return err + } + pathLen := s.Len() + if len(data) < pathLen { + return serrors.New("RawPath raw too short", "expected", pathLen, "actual", len(data)) + } + s.Raw = data[:pathLen] + return nil +} + +// SerializeTo writes the path to a slice. The slice must be big enough to hold the entire data, +// otherwise an error is returned. +func (s *Raw) SerializeTo(b []byte) error { + if s.Raw == nil { + return serrors.New("raw is nil") + } + if minLen := s.Len(); len(b) < minLen { + return serrors.New("buffer too small", "expected", minLen, "actual", len(b)) + } + // XXX(roosd): This modifies the underlying buffer. Consider writing to data + // directly. + if err := s.PathMeta.SerializeTo(s.Raw[:MetaLen]); err != nil { + return err + } + + copy(b, s.Raw) + return nil +} + +// Reverse reverses the path such that it can be used in the reverse direction. +func (s *Raw) Reverse() (path.Path, error) { + // XXX(shitz): The current implementation is not the most performant, since it parses the entire + // path first. If this becomes a performance bottleneck, the implementation should be changed to + // work directly on the raw representation. + + decoded, err := s.ToDecoded() + if err != nil { + return nil, err + } + reversed, err := decoded.Reverse() + if err != nil { + return nil, err + } + if err := reversed.SerializeTo(s.Raw); err != nil { + return nil, err + } + err = s.DecodeFromBytes(s.Raw) + return s, err +} + +// ToDecoded transforms a scion.Raw to a scion.Decoded. +func (s *Raw) ToDecoded() (*Decoded, error) { + // Serialize PathMeta to ensure potential changes are reflected Raw. + + if err := s.PathMeta.SerializeTo(s.Raw[:MetaLen]); err != nil { + return nil, err + } + + decoded := &Decoded{} + if err := decoded.DecodeFromBytes(s.Raw); err != nil { + return nil, err + } + return decoded, nil +} + +// IncPath increments the path and writes it to the buffer. +func (s *Raw) IncPath(n int) error { + if err := s.Base.IncPath(n); err != nil { + return err + } + + return s.PathMeta.SerializeTo(s.Raw[:MetaLen]) +} + +// GetInfoField returns the InfoField at a given index. +func (s *Raw) GetInfoField(idx int) (path.InfoField, error) { + if idx >= s.NumINF { + return path.InfoField{}, + serrors.New("InfoField index out of bounds", "max", s.NumINF-1, "actual", idx) + } + infOffset := MetaLen + idx*path.InfoLen + info := path.InfoField{} + if err := info.DecodeFromBytes(s.Raw[infOffset : infOffset+path.InfoLen]); err != nil { + return path.InfoField{}, err + } + return info, nil +} + +// GetCurrentInfoField is a convenience method that returns the current hop field pointed to by the +// CurrINF index in the path meta header. +func (s *Raw) GetCurrentInfoField() (path.InfoField, error) { + return s.GetInfoField(int(s.PathMeta.CurrINF)) +} + +// SetInfoField updates the InfoField at a given index. +func (s *Raw) SetInfoField(info path.InfoField, idx int) error { + if idx >= s.NumINF { + return serrors.New("InfoField index out of bounds", "max", s.NumINF-1, "actual", idx) + } + infOffset := MetaLen + idx*path.InfoLen + return info.SerializeTo(s.Raw[infOffset : infOffset+path.InfoLen]) +} + +// GetHopField returns the HopField at a given index. +func (s *Raw) GetHopField(idx int) (FlyoverHopField, error) { + if idx >= s.NumHops-2 { + return FlyoverHopField{}, + serrors.New("HopField index out of bounds", "max", s.NumHops-3, "actual", idx) + } + hopOffset := MetaLen + s.NumINF*path.InfoLen + idx*path.LineLen + hop := FlyoverHopField{} + // Let the decoder read a big enough slice in case it is a FlyoverHopField + maxHopLen := path.FlyoverLen + if idx > s.NumHops-5 { + if idx == s.NumHops-3 { + maxHopLen = path.HopLen + } else { + return FlyoverHopField{}, serrors.New("Invalid hopfield index", "NumHops", s.NumHops, "index", idx) + } + } + if err := hop.DecodeFromBytes(s.Raw[hopOffset : hopOffset+maxHopLen]); err != nil { + return FlyoverHopField{}, err + } + return hop, nil +} + +// GetCurrentHopField is a convenience method that returns the current hop field pointed to by the +// CurrHF index in the path meta header. +func (s *Raw) GetCurrentHopField() (FlyoverHopField, error) { + return s.GetHopField(int(s.PathMeta.CurrHF)) +} + +func (s *Raw) ReplacMac(idx int, mac []byte) error { + if idx >= s.NumHops-2 { + return serrors.New("HopField index out of bounds", "max", s.NumHops-3, "actual", idx) + } + offset := s.NumINF*path.InfoLen + path.MacOffset + offset += MetaLen + idx*path.LineLen + if n := copy(s.Raw[offset:offset+path.MacLen], mac[:path.MacLen]); n != path.MacLen { + return serrors.New("copied worng number of bytes for mac replacement", "expected", path.MacLen, "actual", n) + } + return nil +} + +// SetCurrentMac replaces the Mac of the current hopfield by a new mac +func (s *Raw) ReplaceCurrentMac(mac []byte) error { + return s.ReplacMac(int(s.PathMeta.CurrHF), mac) +} + +// SetHopField updates the HopField at a given index. +// For Hummingbird paths the index is the offset in 4 byte lines +// +// If replacing a FlyoverHopField with a Hopfield, it is replaced by a FlyoverHopField with dummy values. +// This works for SCMP packets as Flyover hops are removed later in the process of building a SCMP packet. +func (s *Raw) SetHopField(hop FlyoverHopField, idx int) error { + if idx >= s.NumHops-2 { + return serrors.New("HopField index out of bounds", "max", s.NumHops-3, "actual", idx) + } + hopOffset := MetaLen + s.NumINF*path.InfoLen + idx*path.LineLen + if s.Raw[hopOffset]&0x80 == 0x80 { + // IF the current hop is a flyover, the flyover bit of the new hop is set to 1 in order to preserve correctness of the path + // The reservation data of the new hop is dummy data and invalid. + // This works because SetHopField is currently only used to prepare a SCMP packet, and all flyovers are removed later in that process + // + // IF this is ever used for something else, this function needs to be re-written + hop.Flyover = true + } + if hop.Flyover { + if idx >= s.NumHops-4 { + return serrors.New("FlyoverHopField index out of bounds", "max", s.NumHops-5, "actual", idx) + } + hopOffset := MetaLen + s.NumINF*path.InfoLen + idx*path.LineLen + if s.Raw[hopOffset]&0x80 == 0x00 { + return serrors.New("Setting FlyoverHopField over Hopfield with setHopField not supported") + } + return hop.SerializeTo(s.Raw[hopOffset : hopOffset+path.FlyoverLen]) + } + return hop.SerializeTo(s.Raw[hopOffset : hopOffset+path.HopLen]) +} + +// IsFirstHop returns whether the current hop is the first hop on the path. +func (s *Raw) IsFirstHop() bool { + return s.PathMeta.CurrHF == 0 +} + +// IsLastHop returns whether the current hop is the last hop on the path. +func (s *Raw) IsLastHop() bool { + return int(s.PathMeta.CurrHF) == (s.NumHops-3) || int(s.PathMeta.CurrHF) == (s.NumHops-5) +} diff --git a/pkg/slayers/path/hummingbird/raw_test.go b/pkg/slayers/path/hummingbird/raw_test.go new file mode 100644 index 0000000000..a63208b1d4 --- /dev/null +++ b/pkg/slayers/path/hummingbird/raw_test.go @@ -0,0 +1,195 @@ +package hummingbird_test + +import ( + "fmt" + "testing" + + "github.com/scionproto/scion/pkg/slayers/path" + "github.com/scionproto/scion/pkg/slayers/path/hummingbird" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var emptyRawTestPath = &hummingbird.Raw{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrINF: 0, + CurrHF: 0, + SegLen: [3]uint8{0, 0, 0}, + }, + NumINF: 0, + NumHops: 0, + }, + Raw: make([]byte, hummingbird.MetaLen), +} + +var rawHbirdTestPath = &hummingbird.Raw{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrINF: 0, + CurrHF: 0, + SegLen: [3]uint8{8, 8, 0}, + BaseTS: 808, + HighResTS: 1234, + }, + NumINF: 2, + NumHops: 16, + }, + Raw: rawHbirdPath, +} + +func TestRawSerializeHbird(t *testing.T) { + b := make([]byte, rawHbirdTestPath.Len()) + assert.NoError(t, rawHbirdTestPath.SerializeTo(b)) + assert.Equal(t, rawHbirdPath, b) +} + +func TestRawDecodeFromBytesHbird(t *testing.T) { + s := &hummingbird.Raw{} + assert.NoError(t, s.DecodeFromBytes(rawHbirdPath)) + assert.Equal(t, rawHbirdTestPath, s) +} + +func TestRawSerializeDecodeHbird(t *testing.T) { + b := make([]byte, rawHbirdTestPath.Len()) + assert.NoError(t, rawHbirdTestPath.SerializeTo(b)) + s := &hummingbird.Raw{} + assert.NoError(t, s.DecodeFromBytes(b)) + assert.Equal(t, rawHbirdTestPath, s) +} + +func TestRawReverseHbird(t *testing.T) { + for name, tc := range pathReverseCasesHbird { + name, tc := name, tc + for i := range tc.inIdxs { + i := i + t.Run(fmt.Sprintf("%s case %d", name, i+1), func(t *testing.T) { + //t.Parallel() + input := mkRawHbirdPath(t, tc.input, uint8(tc.inIdxs[i][0]), uint8(tc.inIdxs[i][1])) + want := mkRawHbirdPath(t, tc.want, uint8(tc.wantIdxs[i][0]), uint8(tc.wantIdxs[i][1])) + revPath, err := input.Reverse() + assert.NoError(t, err) + assert.Equal(t, want, revPath) + }) + } + } +} + +func TestEmptyRawReverse(t *testing.T) { + _, err := emptyRawTestPath.Reverse() + assert.Error(t, err) +} + +func TestRawToDecodedHbird(t *testing.T) { + decoded, err := rawHbirdTestPath.ToDecoded() + assert.NoError(t, err) + assert.Equal(t, decodedHbirdTestPath, decoded) +} + +func TestGetInfoField(t *testing.T) { + testCases := map[string]struct { + idx int + want path.InfoField + errorFunc assert.ErrorAssertionFunc + }{ + "first info": { + idx: 0, + want: testInfoFields[0], + errorFunc: assert.NoError, + }, + "second info": { + idx: 1, + want: testInfoFields[1], + errorFunc: assert.NoError, + }, + "out of bounds": { + idx: 2, + want: path.InfoField{}, + errorFunc: assert.Error, + }, + } + + for name, tc := range testCases { + name, tc := name, tc + + t.Run(name+" hummingbird", func(t *testing.T) { + t.Parallel() + got, err := rawHbirdTestPath.GetInfoField(tc.idx) + tc.errorFunc(t, err) + assert.Equal(t, tc.want, got) + }) + } +} + +func TestGetHbirdHopField(t *testing.T) { + testCases := map[string]struct { + idx int + want hummingbird.FlyoverHopField + errorFunc assert.ErrorAssertionFunc + }{ + "first hop": { + idx: 0, + want: testFlyoverFields[0], + errorFunc: assert.NoError, + }, + "fourth hop": { + idx: 11, + want: testFlyoverFields[3], + errorFunc: assert.NoError, + }, + "invalid index": { + idx: 12, + errorFunc: assert.Error, + }, + "out of bounds": { + idx: 14, + errorFunc: assert.Error, + }, + } + + for name, tc := range testCases { + name, tc := name, tc + t.Run(name, func(t *testing.T) { + t.Parallel() + got, err := rawHbirdTestPath.GetHopField(tc.idx) + tc.errorFunc(t, err) + assert.Equal(t, tc.want, got) + }) + } +} + +//TODO: SetHopField test + +func TestLastHop(t *testing.T) { + testCases := map[*hummingbird.Raw]bool{ + createHbirdPath(3, 9): false, + createHbirdPath(3, 11): false, + createHbirdPath(3, 12): false, + createHbirdPath(6, 9): true, + createHbirdPath(6, 11): true, + } + for scionRaw, want := range testCases { + got := scionRaw.IsLastHop() + assert.Equal(t, want, got) + } +} + +func mkRawHbirdPath(t *testing.T, pcase hbirdPathCase, infIdx, hopIdx uint8) *hummingbird.Raw { + t.Helper() + decoded := mkDecodedHbirdPath(t, pcase, infIdx, hopIdx) + raw, err := decoded.ToRaw() + require.NoError(t, err) + return raw +} + +func createHbirdPath(currHF uint8, numHops int) *hummingbird.Raw { + hbirdRaw := &hummingbird.Raw{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrHF: currHF, + }, + NumHops: numHops, + }, + } + return hbirdRaw +} diff --git a/pkg/slayers/path/scion/BUILD.bazel b/pkg/slayers/path/scion/BUILD.bazel index a4a57739fa..7fbb1c7e65 100644 --- a/pkg/slayers/path/scion/BUILD.bazel +++ b/pkg/slayers/path/scion/BUILD.bazel @@ -12,6 +12,7 @@ go_library( deps = [ "//pkg/private/serrors:go_default_library", "//pkg/slayers/path:go_default_library", + "//pkg/slayers/path/hummingbird:go_default_library", ], ) diff --git a/pkg/slayers/path/scion/base.go b/pkg/slayers/path/scion/base.go index b2a7812239..4dbc574565 100644 --- a/pkg/slayers/path/scion/base.go +++ b/pkg/slayers/path/scion/base.go @@ -24,6 +24,7 @@ import ( // MetaLen is the length of the PathMetaHeader. const MetaLen = 4 +const MetaLenHBird = 12 const PathType path.Type = 1 @@ -45,6 +46,8 @@ type Base struct { // NumINF is the number of InfoFields in the path. NumINF int // NumHops is the number HopFields in the path. + // If IsHummingbird is true, NumHops is the number of 4 bytes lines in the path instead of the number of hops. + // TODO: rename and reevaluate? NumHops int } @@ -81,6 +84,7 @@ func (s *Base) IncPath() error { "num_hops", s.NumHops) } s.PathMeta.CurrHF++ + // Update CurrINF s.PathMeta.CurrINF = s.infIndexForHF(s.PathMeta.CurrHF) return nil @@ -162,5 +166,6 @@ func (m *MetaHdr) SerializeTo(b []byte) error { } func (m MetaHdr) String() string { - return fmt.Sprintf("{CurrInf: %d, CurrHF: %d, SegLen: %v}", m.CurrINF, m.CurrHF, m.SegLen) + return fmt.Sprintf("{CurrInf: %d, CurrHF: %d, SegLen: %v}", + m.CurrINF, m.CurrHF, m.SegLen) } diff --git a/pkg/slayers/path/scion/decoded.go b/pkg/slayers/path/scion/decoded.go index da183f2903..a365c0955f 100644 --- a/pkg/slayers/path/scion/decoded.go +++ b/pkg/slayers/path/scion/decoded.go @@ -42,6 +42,8 @@ func (s *Decoded) DecodeFromBytes(data []byte) error { if err := s.Base.DecodeFromBytes(data); err != nil { return err } + // fmt.Printf("s: %v\n", s) + // fmt.Print(s.Len()) if minLen := s.Len(); len(data) < minLen { return serrors.New("DecodedPath raw too short", "expected", minLen, "actual", len(data)) } @@ -54,6 +56,7 @@ func (s *Decoded) DecodeFromBytes(data []byte) error { } offset += path.InfoLen } + s.HopFields = make([]path.HopField, s.NumHops) for i := 0; i < s.NumHops; i++ { if err := s.HopFields[i].DecodeFromBytes(data[offset : offset+path.HopLen]); err != nil { @@ -71,10 +74,12 @@ func (s *Decoded) SerializeTo(b []byte) error { return serrors.New("buffer too small to serialize path.", "expected", s.Len(), "actual", len(b)) } + var offset int if err := s.PathMeta.SerializeTo(b[:MetaLen]); err != nil { return err } - offset := MetaLen + offset = MetaLen + for _, info := range s.InfoFields { if err := info.SerializeTo(b[offset : offset+path.InfoLen]); err != nil { return err @@ -86,11 +91,13 @@ func (s *Decoded) SerializeTo(b []byte) error { return err } offset += path.HopLen + } return nil } // Reverse reverses a SCION path. +// Removes all reservations from a Hummingbird path, as these are not bidirectional func (s *Decoded) Reverse() (path.Path, error) { if s.NumINF == 0 { return nil, serrors.New("empty decoded path is invalid and cannot be reversed") @@ -106,13 +113,12 @@ func (s *Decoded) Reverse() (path.Path, error) { info.ConsDir = !info.ConsDir } // Reverse order of hop fields - for i, j := 0, s.NumHops-1; i < j; i, j = i+1, j-1 { + for i, j := 0, len(s.HopFields)-1; i < j; i, j = i+1, j-1 { s.HopFields[i], s.HopFields[j] = s.HopFields[j], s.HopFields[i] } // Update CurrINF and CurrHF and SegLens s.PathMeta.CurrINF = uint8(s.NumINF) - s.PathMeta.CurrINF - 1 s.PathMeta.CurrHF = uint8(s.NumHops) - s.PathMeta.CurrHF - 1 - return s, nil } diff --git a/pkg/slayers/path/scion/raw.go b/pkg/slayers/path/scion/raw.go index 8591093774..73c0b0b1c7 100644 --- a/pkg/slayers/path/scion/raw.go +++ b/pkg/slayers/path/scion/raw.go @@ -130,6 +130,7 @@ func (s *Raw) SetInfoField(info path.InfoField, idx int) error { } // GetHopField returns the HopField at a given index. +// For Hummingbird paths the index is the offset in 4 byte lines func (s *Raw) GetHopField(idx int) (path.HopField, error) { if idx >= s.NumHops { return path.HopField{}, @@ -150,6 +151,10 @@ func (s *Raw) GetCurrentHopField() (path.HopField, error) { } // SetHopField updates the HopField at a given index. +// For Hummingbird paths the index is the offset in 4 byte lines +// +// If replacing a FlyoverHopField with a Hopfield, it is replaced by a FlyoverHopField with dummy values. +// This works for SCMP packets as Flyover hops are removed later in the process of building a SCMP packet. func (s *Raw) SetHopField(hop path.HopField, idx int) error { if idx >= s.NumHops { return serrors.New("HopField index out of bounds", "max", s.NumHops-1, "actual", idx) diff --git a/pkg/slayers/path/scion/raw_test.go b/pkg/slayers/path/scion/raw_test.go index ff527d9398..1f50a167b6 100644 --- a/pkg/slayers/path/scion/raw_test.go +++ b/pkg/slayers/path/scion/raw_test.go @@ -208,6 +208,7 @@ func TestSetInfoField(t *testing.T) { } } +// Will also need test to check entire RawPacket is correct func TestSetHopField(t *testing.T) { testCases := map[string]struct { idx int diff --git a/pkg/slayers/scion.go b/pkg/slayers/scion.go index 66ff20c755..ba8ca2843d 100644 --- a/pkg/slayers/scion.go +++ b/pkg/slayers/scion.go @@ -25,6 +25,7 @@ import ( "github.com/scionproto/scion/pkg/slayers/path" "github.com/scionproto/scion/pkg/slayers/path/empty" "github.com/scionproto/scion/pkg/slayers/path/epic" + "github.com/scionproto/scion/pkg/slayers/path/hummingbird" "github.com/scionproto/scion/pkg/slayers/path/onehop" "github.com/scionproto/scion/pkg/slayers/path/scion" ) @@ -46,6 +47,7 @@ func init() { scion.RegisterPath() onehop.RegisterPath() epic.RegisterPath() + hummingbird.RegisterPath() } // AddrType indicates the type of a host address in the SCION header. @@ -262,10 +264,11 @@ func (s *SCION) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { func (s *SCION) RecyclePaths() { if s.pathPool == nil { s.pathPool = []path.Path{ - empty.PathType: empty.Path{}, - onehop.PathType: &onehop.Path{}, - scion.PathType: &scion.Raw{}, - epic.PathType: &epic.Path{}, + empty.PathType: empty.Path{}, + onehop.PathType: &onehop.Path{}, + scion.PathType: &scion.Raw{}, + epic.PathType: &epic.Path{}, + hummingbird.PathType: &hummingbird.Raw{}, } s.pathPoolRaw = path.NewRawPath() } diff --git a/pkg/slayers/scmp_typecode.go b/pkg/slayers/scmp_typecode.go index eb61d1dcfc..564b7b0fc0 100644 --- a/pkg/slayers/scmp_typecode.go +++ b/pkg/slayers/scmp_typecode.go @@ -72,6 +72,8 @@ const ( SCMPCodeInvalidExtensionHeader SCMPCode = 64 SCMPCodeUnknownHopByHopOption SCMPCode = 65 SCMPCodeUnknownEndToEndOption SCMPCode = 66 + + SCMPCodeReservationExpired SCMPCode = 71 ) // SCMP informational messages. diff --git a/router/BUILD.bazel b/router/BUILD.bazel index d7ebcbc60e..ed1bb60539 100644 --- a/router/BUILD.bazel +++ b/router/BUILD.bazel @@ -23,6 +23,7 @@ go_library( "//pkg/slayers/path:go_default_library", "//pkg/slayers/path/empty:go_default_library", "//pkg/slayers/path/epic:go_default_library", + "//pkg/slayers/path/hummingbird:go_default_library", "//pkg/slayers/path/onehop:go_default_library", "//pkg/slayers/path/scion:go_default_library", "//pkg/spao:go_default_library", @@ -59,6 +60,7 @@ go_test( "//pkg/slayers/path:go_default_library", "//pkg/slayers/path/empty:go_default_library", "//pkg/slayers/path/epic:go_default_library", + "//pkg/slayers/path/hummingbird:go_default_library", "//pkg/slayers/path/onehop:go_default_library", "//pkg/slayers/path/scion:go_default_library", "//private/topology:go_default_library", diff --git a/router/connector.go b/router/connector.go index 7b744b2c41..dffc1bde94 100644 --- a/router/connector.go +++ b/router/connector.go @@ -177,6 +177,8 @@ func (c *Connector) SetKey(ia addr.IA, index int, key []byte) error { if index != 0 { return serrors.New("currently only index 0 key is supported") } + //TODO: have method to set secret value separately + c.DataPlane.SetSecretValue(key) return c.DataPlane.SetKey(key) } diff --git a/router/dataplane.go b/router/dataplane.go index 362c298c34..a253135b1a 100644 --- a/router/dataplane.go +++ b/router/dataplane.go @@ -18,8 +18,10 @@ package router import ( "bytes" "context" + "crypto/cipher" "crypto/rand" "crypto/subtle" + "encoding/binary" "errors" "fmt" "hash" @@ -47,6 +49,7 @@ import ( "github.com/scionproto/scion/pkg/slayers/path" "github.com/scionproto/scion/pkg/slayers/path/empty" "github.com/scionproto/scion/pkg/slayers/path/epic" + "github.com/scionproto/scion/pkg/slayers/path/hummingbird" "github.com/scionproto/scion/pkg/slayers/path/onehop" "github.com/scionproto/scion/pkg/slayers/path/scion" "github.com/scionproto/scion/pkg/spao" @@ -105,6 +108,7 @@ type DataPlane struct { internalNextHops map[uint16]*net.UDPAddr svc *services macFactory func() hash.Hash + prfFactory func() cipher.Block bfdSessions map[uint16]bfdSession localIA addr.IA mtx sync.Mutex @@ -990,8 +994,10 @@ func newPacketProcessor(d *DataPlane) *scionPacketProcessor { p := &scionPacketProcessor{ d: d, buffer: gopacket.NewSerializeBuffer(), + prf: d.prfFactory(), mac: d.macFactory(), - macInputBuffer: make([]byte, max(path.MACBufferSize, libepic.MACBufferSize)), + macInputBuffer: make([]byte, max(path.MACBufferSize, libepic.MACBufferSize,hummingbird.FlyoverMacBufferSize,hummingbird.AkBufferSize)), + hbirdXkbuffer: make([]uint32, hummingbird.XkBufferSize), } p.scionLayer.RecyclePaths() return p @@ -1007,6 +1013,8 @@ func (p *scionPacketProcessor) reset() error { p.infoField = path.InfoField{} p.effectiveXover = false p.peering = false + p.hasPriority = false + if err := p.buffer.Clear(); err != nil { return serrors.WrapStr("Failed to clear buffer", err) } @@ -1058,6 +1066,8 @@ func (p *scionPacketProcessor) processPkt(rawPkt []byte, return p.processSCION() case epic.PathType: return p.processEPIC() + case hummingbird.PathType: + return p.processHBIRD() default: return processResult{}, serrors.WithCtx(unsupportedPathType, "type", pathType) } @@ -1183,6 +1193,8 @@ type scionPacketProcessor struct { buffer gopacket.SerializeBuffer // mac is the hasher for the MAC computation. mac hash.Hash + // block is the keyed PRF for the hummingbird auth key computation + prf cipher.Block // scionLayer is the SCION gopacket layer. scionLayer slayers.SCION @@ -1193,6 +1205,8 @@ type scionPacketProcessor struct { // path is the raw SCION path. Will be set during processing. path *scion.Raw + // hbirdPath is the raw Hummingbird path. Will be set during processing + hbirdPath *hummingbird.Raw // hopField is the current hopField field, is updated during processing. hopField path.HopField // infoField is the current infoField field, is updated during processing. @@ -1201,6 +1215,10 @@ type scionPacketProcessor struct { effectiveXover bool // peering indicates that the hop field being processed is a peering hop field. peering bool + // flyoverField is the flyoverfield containing the current hopfield for hummingbird packets + flyoverField hummingbird.FlyoverHopField + // hasPriority indicates whether this packet has forwarding priority + hasPriority bool // cachedMac contains the full 16 bytes of the MAC. Will be set during processing. // For a hop performing an Xover, it is the MAC corresponding to the down segment. @@ -1515,6 +1533,16 @@ func (p *scionPacketProcessor) currentHopPointer() uint16 { scion.MetaLen + path.InfoLen*p.path.NumINF + path.HopLen*int(p.path.PathMeta.CurrHF)) } +// Compares two 6 byte arrays. +// Always returns false if at least one input is of a different length. +// Returns true if equal, false otherwise. +func CompareMacThisIsNotConstant(a, b []byte) bool { + if len(a) != 6 || len(b) != 6 { + return false + } + return binary.BigEndian.Uint32(a) == binary.BigEndian.Uint32(b) && a[4] == b[4] && a[5] == b[5] +} + func (p *scionPacketProcessor) verifyCurrentMAC() (processResult, error) { fullMac := path.FullMAC(p.mac, p.infoField, p.hopField, p.macInputBuffer[:path.MACBufferSize]) if subtle.ConstantTimeCompare(p.hopField.Mac[:path.MacLen], fullMac[:path.MacLen]) == 0 { @@ -1530,11 +1558,7 @@ func (p *scionPacketProcessor) verifyCurrentMAC() (processResult, error) { pointer: p.currentHopPointer(), cause: macVerificationFailed, } - return processResult{SlowPathRequest: slowPathRequest}, slowPathRequired - } - // Add the full MAC to the SCION packet processor, - // such that EPIC does not need to recalculate it. - p.cachedMac = fullMac + return processResult{SlowPathRequest: slowPathRequest}, slowPathRequired } return processResult{}, nil } @@ -1581,6 +1605,7 @@ func (p *scionPacketProcessor) doXover() (processResult, error) { // TODO parameter problem invalid path return processResult{}, serrors.WrapStr("incrementing path", err) } + var err error if p.hopField, err = p.path.GetCurrentHopField(); err != nil { // TODO parameter problem invalid path diff --git a/router/dataplane_hbird.go b/router/dataplane_hbird.go new file mode 100644 index 0000000000..bee02be3e8 --- /dev/null +++ b/router/dataplane_hbird.go @@ -0,0 +1,406 @@ +package router + +import ( + "crypto/aes" + "crypto/cipher" + "encoding/binary" + "fmt" + "time" + + "github.com/scionproto/scion/pkg/private/serrors" + "github.com/scionproto/scion/pkg/private/util" + "github.com/scionproto/scion/pkg/slayers" + "github.com/scionproto/scion/pkg/slayers/path" + "github.com/scionproto/scion/pkg/slayers/path/hummingbird" +) + +// SetSecretValue sets the secret value for the PRF function used to compute the Hummingbird Auth Key +func (d *DataPlane) SetSecretValue(key []byte) error { + d.mtx.Lock() + defer d.mtx.Unlock() + if d.running { + return modifyExisting + } + if len(key) == 0 { + return emptyValue + } + if d.prfFactory != nil { + return alreadySet + } + // First check for cipher creation errors + if _, err := aes.NewCipher(key); err != nil { + return err + } + d.prfFactory = func() cipher.Block { + prf, _ := aes.NewCipher(key) + return prf + } + return nil +} + +func (p *scionPacketProcessor) parseHbirdPath() (processResult, error) { + var err error + p.flyoverField, err = p.hbirdPath.GetCurrentHopField() + if err != nil { + return processResult{}, err + } + p.hopField = p.flyoverField.HopField + p.infoField, err = p.hbirdPath.GetCurrentInfoField() + if err != nil { + // TODO(lukedirtwalker) parameter problem invalid path? + return processResult{}, err + } + if p.flyoverField.Flyover { + p.hasPriority = true + } + + return processResult{}, nil +} + +func (p *scionPacketProcessor) validateReservationExpiry() (processResult, error) { + startTime := util.SecsToTime(p.hbirdPath.PathMeta.BaseTS - uint32(p.flyoverField.ResStartTime)) + endTime := startTime.Add(time.Duration(p.flyoverField.Duration) * time.Second) + now := time.Now() + if startTime.Before(now) && now.Before(endTime) { + return processResult{}, nil + } + return p.packSCMP(slayers.SCMPTypeParameterProblem, + slayers.SCMPCodeReservationExpired, + &slayers.SCMPParameterProblem{Pointer: p.currentHopPointer()}, + serrors.New("reservation not valid now", "reservation start", startTime, "reservation end", endTime, "now", now), + ) +} + +func (p *scionPacketProcessor) verifyCurrentHbirdMAC() (processResult, error) { + scionMac := path.FullMAC(p.mac, p.infoField, p.hopField, p.macBuffers.scionInput) + + var verified bool + if p.flyoverField.Flyover { + ak := hummingbird.DeriveAuthKey(p.prf, p.flyoverField.ResID, p.flyoverField.Bw, p.hopField.ConsIngress, p.hopField.ConsEgress, + p.hbirdPath.PathMeta.BaseTS-uint32(p.flyoverField.ResStartTime), p.flyoverField.Duration, p.macBuffers.hbirdAuthInput) + flyoverMac := hummingbird.FullFlyoverMac(ak, p.scionLayer.DstIA, p.scionLayer.PayloadLen, p.flyoverField.ResStartTime, + p.hbirdPath.PathMeta.HighResTS, p.macBuffers.hbirdMacInput, p.macBuffers.hbirdXkbuffer) + // Xor to Aggregate MACs + binary.BigEndian.PutUint64(flyoverMac[0:8], binary.BigEndian.Uint64(scionMac[0:8])^binary.BigEndian.Uint64(flyoverMac[0:8])) + binary.BigEndian.PutUint32(flyoverMac[8:12], binary.BigEndian.Uint32(scionMac[8:12])^binary.BigEndian.Uint32(flyoverMac[8:12])) + + verified = CompareMac(p.hopField.Mac[:path.MacLen], flyoverMac[:path.MacLen]) + } else { + verified = CompareMac(p.hopField.Mac[:path.MacLen], scionMac[:path.MacLen]) + } + // Add the full MAC to the SCION packet processor, + // such that EPIC and hummingbird mac de-aggregation do not need to recalculate it. + p.cachedMac = scionMac + if !verified { + return p.packSCMP( + slayers.SCMPTypeParameterProblem, + slayers.SCMPCodeInvalidHopFieldMAC, + &slayers.SCMPParameterProblem{Pointer: p.currentHopPointer()}, + serrors.New("MAC verification failed", "expected", fmt.Sprintf( + "%x", scionMac[:path.MacLen]), + "actual", fmt.Sprintf("%x", p.hopField.Mac[:path.MacLen]), + "aggregate with flyover", p.flyoverField.Flyover, + "cons_dir", p.infoField.ConsDir, + "if_id", p.ingressID, "curr_inf", p.path.PathMeta.CurrINF, + "curr_hf", p.path.PathMeta.CurrHF, "seg_id", p.infoField.SegID), + ) + } + return processResult{}, nil +} + +func (p *scionPacketProcessor) validateHbirdSrcDstIA() (processResult, error) { + srcIsLocal := (p.scionLayer.SrcIA == p.d.localIA) + dstIsLocal := (p.scionLayer.DstIA == p.d.localIA) + if p.ingressID == 0 { + // Outbound + // Only check SrcIA if first hop, for transit this already checked by ingress router. + // Note: SCMP error messages triggered by the sibling router may use paths that + // don't start with the first hop. + if p.hbirdPath.IsFirstHop() && !srcIsLocal { + return p.invalidSrcIA() + } + if dstIsLocal { + return p.invalidDstIA() + } + } else { + // Inbound + if srcIsLocal { + return p.invalidSrcIA() + } + if p.hbirdPath.IsLastHop() != dstIsLocal { + return p.invalidDstIA() + } + } + return processResult{}, nil +} + +// validateTransitUnderlaySrc checks that the source address of transit packets +// matches the expected sibling router. +// Provided that underlying network infrastructure prevents address spoofing, +// this check prevents malicious end hosts in the local AS from bypassing the +// SrcIA checks by disguising packets as transit traffic. +func (p *scionPacketProcessor) validateHbirdTransitUnderlaySrc() (processResult, error) { + if p.hbirdPath.IsFirstHop() || p.ingressID != 0 { + // not a transit packet, nothing to check + return processResult{}, nil + } + pktIngressID := p.ingressInterface() + expectedSrc, ok := p.d.internalNextHops[pktIngressID] + if !ok || !expectedSrc.IP.Equal(p.srcAddr.IP) { + // Drop + return processResult{}, invalidSrcAddrForTransit + } + return processResult{}, nil +} + +// Verifies the PathMetaHeader timestamp is recent +// Current implementation works with a nanosecond granularity HighResTS +func (p *scionPacketProcessor) validatePathMetaTimestamp() { + timestamp := util.SecsToTime(p.hbirdPath.PathMeta.BaseTS).Add(time.Duration(p.hbirdPath.PathMeta.HighResTS>>22) * time.Millisecond) + // TODO: make a configurable value instead of using a flat 1.5 seconds + if time.Until(timestamp).Abs() > time.Duration(1)*time.Second { + // Hummingbird specification explicitely says to forward best-effort is timestamp too old + p.hasPriority = false + } +} + +func (p *scionPacketProcessor) handleHbirdIngressRouterAlert() (processResult, error) { + if p.ingressID == 0 { + return processResult{}, nil + } + alert := p.ingressRouterAlertFlag() + if !*alert { + return processResult{}, nil + } + *alert = false + if err := p.hbirdPath.SetHopField(p.flyoverField, int(p.path.PathMeta.CurrHF)); err != nil { + return processResult{}, serrors.WrapStr("update hop field", err) + } + return p.handleSCMPTraceRouteRequest(p.ingressID) +} + +func (p *scionPacketProcessor) handleHbirdEgressRouterAlert() (processResult, error) { + alert := p.egressRouterAlertFlag() + if !*alert { + return processResult{}, nil + } + egressID := p.egressInterface() + if _, ok := p.d.external[egressID]; !ok { + return processResult{}, nil + } + *alert = false + if err := p.hbirdPath.SetHopField(p.flyoverField, int(p.path.PathMeta.CurrHF)); err != nil { + return processResult{}, serrors.WrapStr("update hop field", err) + } + return p.handleSCMPTraceRouteRequest(egressID) +} + +func (p *scionPacketProcessor) updateHbirdNonConsDirIngressSegID() error { + // against construction dir the ingress router updates the SegID, ifID == 0 + // means this comes from this AS itself, so nothing has to be done. + // TODO(lukedirtwalker): For packets destined to peer links this shouldn't + // be updated. + if !p.infoField.ConsDir && p.ingressID != 0 { + p.infoField.UpdateSegID(p.hopField.Mac) + if err := p.hbirdPath.SetInfoField(p.infoField, int(p.hbirdPath.PathMeta.CurrINF)); err != nil { + return serrors.WrapStr("update info field", err) + } + } + return nil +} + +func (p *scionPacketProcessor) deAggregateMac() (processResult, error) { + if !p.flyoverField.Flyover { + return processResult{}, nil + } + copy(p.hopField.Mac[:], p.cachedMac[:path.MacLen]) + if err := p.hbirdPath.ReplaceCurrentMac(p.cachedMac); err != nil { + //TODO: what SCMP packet should be returned here? Is that even necessary? + return p.packSCMP( + slayers.SCMPTypeParameterProblem, + slayers.SCMPCodeInvalidHopFieldMAC, + &slayers.SCMPParameterProblem{Pointer: p.currentHopPointer()}, + serrors.Join(err, serrors.New("Mac replacement failed")), + ) + } + return processResult{}, nil +} + +func (p *scionPacketProcessor) doHbirdXover() (processResult, error) { + p.segmentChange = true + n := 3 + if p.flyoverField.Flyover { + n = 5 + } + if err := p.hbirdPath.IncPath(n); err != nil { + // TODO parameter problem invalid path + return processResult{}, serrors.WrapStr("incrementing path", err) + } + + var err error + if p.flyoverField, err = p.hbirdPath.GetCurrentHopField(); err != nil { + // TODO parameter problem invalid path + return processResult{}, err + } + if p.infoField, err = p.hbirdPath.GetCurrentInfoField(); err != nil { + // TODO parameter problem invalid path + return processResult{}, err + } + p.hopField = p.flyoverField.HopField + //TODO: modify method once we have definite design for flyover Xover + return processResult{}, nil +} + +func (p *scionPacketProcessor) processHbirdEgress() error { + // we are the egress router and if we go in construction direction we + // need to update the SegID. + if p.infoField.ConsDir { + p.infoField.UpdateSegID(p.hopField.Mac) + if err := p.hbirdPath.SetInfoField(p.infoField, int(p.hbirdPath.PathMeta.CurrINF)); err != nil { + // TODO parameter problem invalid path + return serrors.WrapStr("update info field", err) + } + } + + n := 3 + if p.flyoverField.Flyover { + n = 5 + } + if err := p.hbirdPath.IncPath(n); err != nil { + // TODO parameter problem invalid path + return serrors.WrapStr("incrementing path", err) + } + return nil +} + +func (p *scionPacketProcessor) processHBIRD() (processResult, error) { + var ok bool + p.hbirdPath, ok = p.scionLayer.Path.(*hummingbird.Raw) + if !ok { + // TODO(lukedirtwalker) parameter problem invalid path? + return processResult{}, malformedPath + } + if r, err := p.parseHbirdPath(); err != nil { + return r, err + } + + if r, err := p.validateHopExpiry(); err != nil { + return r, err + } + if r, err := p.validateIngressID(); err != nil { + return r, err + } + if r, err := p.validatePktLen(); err != nil { + return r, err + } + if r, err := p.validateHbirdTransitUnderlaySrc(); err != nil { + return r, err + } + if r, err := p.validateHbirdSrcDstIA(); err != nil { + return r, err + } + if p.flyoverField.Flyover { + if r, err := p.validateReservationExpiry(); err != nil { + return r, err + } + } + if err := p.updateHbirdNonConsDirIngressSegID(); err != nil { + return processResult{}, err + } + if p.flyoverField.Flyover { + if r, err := p.verifyCurrentHbirdMAC(); err != nil { + return r, err + } + } else { + if r, err := p.verifyCurrentMAC(); err != nil { + return r, err + } + } + if p.hasPriority && p.flyoverField.Flyover { + // TODO: current implementation (which is in line with design) allows for an attack surface where packets with outdated TSs bypass bw check but claim priority to next router + p.validatePathMetaTimestamp() + } + if r, err := p.handleHbirdIngressRouterAlert(); err != nil { + return r, err + } + // Inbound: pkts destined to the local IA. + if p.scionLayer.DstIA == p.d.localIA { + + if r, err := p.deAggregateMac(); err != nil { + return r, err + } + a, r, err := p.resolveInbound() + if err != nil { + return r, err + } + return processResult{OutAddr: a, OutPkt: p.rawPkt}, nil + } + + // Outbound: pkts leaving the local IA. + // BRTransit: pkts leaving from the same BR different interface. + if p.hbirdPath.IsXover() { + if r, err := p.doHbirdXover(); err != nil { + return r, err + } + if r, err := p.validateHopExpiry(); err != nil { + return r, serrors.WithCtx(err, "info", "after xover") + } + // verify the new block + if p.flyoverField.Flyover { + // TODO: can possibly skip this once we modify flyover at Xover implementation. Will need to aggregate new MAC though + if r, err := p.verifyCurrentHbirdMAC(); err != nil { + return r, err + } + } else { + if r, err := p.verifyCurrentMAC(); err != nil { + return r, err + } + } + if p.flyoverField.Flyover { + //TODO: can skip repeating those if/once moving previous flyover at Xover is confirmed + if r, err := p.validateReservationExpiry(); err != nil { + return r, serrors.WithCtx(err, "info", "after xover") + } + if p.hasPriority { + p.validatePathMetaTimestamp() + } + } + } + if r, err := p.validateEgressID(); err != nil { + return r, err + } + // handle egress router alert before we check if it's up because we want to + // send the reply anyway, so that trace route can pinpoint the exact link + // that failed. + if r, err := p.handleHbirdEgressRouterAlert(); err != nil { + return r, err + } + if r, err := p.validateEgressUp(); err != nil { + return r, err + } + + egressID := p.egressInterface() + if _, ok := p.d.external[egressID]; ok { + if r, err := p.deAggregateMac(); err != nil { + return r, err + } + if err := p.processHbirdEgress(); err != nil { + return processResult{}, err + } + return processResult{EgressID: egressID, OutPkt: p.rawPkt}, nil + } + // ASTransit: pkts leaving from another AS BR. + if a, ok := p.d.internalNextHops[egressID]; ok { + return processResult{OutAddr: a, OutPkt: p.rawPkt}, nil + } + errCode := slayers.SCMPCodeUnknownHopFieldEgress + if !p.infoField.ConsDir { + errCode = slayers.SCMPCodeUnknownHopFieldIngress + } + return p.packSCMP( + slayers.SCMPTypeParameterProblem, + errCode, + &slayers.SCMPParameterProblem{Pointer: p.currentHopPointer()}, + cannotRoute, + ) +} diff --git a/router/dataplane_hbird_test.go b/router/dataplane_hbird_test.go new file mode 100644 index 0000000000..362b7f3a58 --- /dev/null +++ b/router/dataplane_hbird_test.go @@ -0,0 +1,666 @@ +package router_test + +import ( + "crypto/aes" + "net" + "testing" + "time" + + "github.com/golang/mock/gomock" + "github.com/scionproto/scion/pkg/addr" + "github.com/scionproto/scion/pkg/private/util" + "github.com/scionproto/scion/pkg/private/xtest" + "github.com/scionproto/scion/pkg/slayers" + "github.com/scionproto/scion/pkg/slayers/path" + "github.com/scionproto/scion/pkg/slayers/path/hummingbird" + "github.com/scionproto/scion/private/topology" + "github.com/scionproto/scion/router" + "github.com/scionproto/scion/router/mock_router" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/net/ipv4" +) + +func TestDataPlaneSetSecretValue(t *testing.T) { + t.Run("fails after serve", func(t *testing.T) { + d := &router.DataPlane{} + d.FakeStart() + assert.Error(t, d.SetSecretValue([]byte("dummy"))) + }) + t.Run("setting nil value is not allowed", func(t *testing.T) { + d := &router.DataPlane{} + d.FakeStart() + assert.Error(t, d.SetSecretValue(nil)) + }) + t.Run("single set works", func(t *testing.T) { + d := &router.DataPlane{} + assert.NoError(t, d.SetSecretValue([]byte("dummy key xxxxxx"))) + }) + t.Run("double set fails", func(t *testing.T) { + d := &router.DataPlane{} + assert.NoError(t, d.SetSecretValue([]byte("dummy key xxxxxx"))) + assert.Error(t, d.SetSecretValue([]byte("dummy key xxxxxx"))) + }) +} + +func TestProcessHbirdPacket(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + key := []byte("testkey_xxxxxxxx") + sv := []byte("test_secretvalue") + now := time.Now() + + testCases := map[string]struct { + mockMsg func(bool) *ipv4.Message + prepareDP func(*gomock.Controller) *router.DataPlane + srcInterface uint16 + assertFunc assert.ErrorAssertionFunc + }{ + "inbound": { + prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { + return router.NewDP(nil, nil, mock_router.NewMockBatchConn(ctrl), nil, + nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + }, + mockMsg: func(afterProcessing bool) *ipv4.Message { + spkt, dpath := prepHbirdMsg(now) + spkt.DstIA = xtest.MustParseIA("1-ff00:0:110") + dst := addr.MustParseHost("10.0.100.100") + _ = spkt.SetDstAddr(dst) + dpath.HopFields = []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 41, ConsEgress: 40}}, + {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, + {HopField: path.HopField{ConsIngress: 01, ConsEgress: 0}}, + } + dpath.Base.PathMeta.CurrHF = 6 + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[2].HopField) + ret := toMsg(t, spkt, dpath) + if afterProcessing { + ret.Addr = &net.UDPAddr{IP: dst.IP().AsSlice(), Port: topology.EndhostPort} + ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil + + } + return ret + }, + srcInterface: 1, + assertFunc: assert.NoError, + }, + "outbound": { + prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { + return router.NewDP( + map[uint16]router.BatchConn{ + uint16(1): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 1: topology.Child, + }, + nil, nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + }, + mockMsg: func(afterProcessing bool) *ipv4.Message { + spkt, dpath := prepHbirdMsg(now) + spkt.SrcIA = xtest.MustParseIA("1-ff00:0:110") + dpath.HopFields = []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 1}}, + {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, + {HopField: path.HopField{ConsIngress: 41, ConsEgress: 40}}, + } + dpath.Base.PathMeta.CurrHF = 0 + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + if !afterProcessing { + return toMsg(t, spkt, dpath) + } + _ = dpath.IncPath(3) + dpath.InfoFields[0].UpdateSegID(dpath.HopFields[0].HopField.Mac) + ret := toMsg(t, spkt, dpath) + ret.Addr = nil + ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil + return ret + }, + srcInterface: 0, + assertFunc: assert.NoError, + }, + "brtransit": { + prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { + return router.NewDP( + map[uint16]router.BatchConn{ + uint16(2): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 1: topology.Parent, + 2: topology.Child, + }, + nil, nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + }, + mockMsg: func(afterProcessing bool) *ipv4.Message { + spkt, dpath := prepHbirdMsg(now) + dpath.HopFields = []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, + {HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}}, + {HopField: path.HopField{ConsIngress: 40, ConsEgress: 41}}, + } + dpath.Base.PathMeta.CurrHF = 3 + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + if !afterProcessing { + return toMsg(t, spkt, dpath) + } + _ = dpath.IncPath(3) + dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.Mac) + ret := toMsg(t, spkt, dpath) + ret.Addr = nil + ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil + return ret + }, + srcInterface: 1, + assertFunc: assert.NoError, + }, + "brtransit non consdir": { + prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { + return router.NewDP( + map[uint16]router.BatchConn{ + uint16(2): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 2: topology.Parent, + 1: topology.Child, + }, nil, nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + }, + mockMsg: func(afterProcessing bool) *ipv4.Message { + spkt, dpath := prepHbirdMsg(now) + dpath.HopFields = []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, + {HopField: path.HopField{ConsIngress: 2, ConsEgress: 1}}, + {HopField: path.HopField{ConsIngress: 40, ConsEgress: 41}}, + } + dpath.Base.PathMeta.CurrHF = 3 + dpath.InfoFields[0].ConsDir = false + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + if !afterProcessing { + dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.Mac) + return toMsg(t, spkt, dpath) + } + require.NoError(t, dpath.IncPath(3)) + ret := toMsg(t, spkt, dpath) + ret.Addr = nil + ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil + return ret + }, + srcInterface: 1, + assertFunc: assert.NoError, + }, + "astransit direct": { + prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { + return router.NewDP(nil, + map[uint16]topology.LinkType{ + 1: topology.Core, + 3: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(3): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + }, + mockMsg: func(afterProcessing bool) *ipv4.Message { + spkt, dpath := prepHbirdMsg(now) + dpath.HopFields = []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, + {HopField: path.HopField{ConsIngress: 1, ConsEgress: 3}}, + {HopField: path.HopField{ConsIngress: 50, ConsEgress: 51}}, + } + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + ret := toMsg(t, spkt, dpath) + if afterProcessing { + ret.Addr = &net.UDPAddr{IP: net.ParseIP("10.0.200.200").To4(), Port: 30043} + ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil + } + return ret + }, + srcInterface: 1, + assertFunc: assert.NoError, + }, + "astransit xover": { + prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { + return router.NewDP(nil, + map[uint16]topology.LinkType{ + 51: topology.Child, + 3: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(3): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + }, + mockMsg: func(afterProcessing bool) *ipv4.Message { + spkt, _ := prepHbirdMsg(now) + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrHF: 6, + SegLen: [3]uint8{6, 6, 0}, + }, + NumINF: 2, + NumHops: 12, + }, + InfoFields: []path.InfoField{ + // up seg + {SegID: 0x111, ConsDir: false, Timestamp: util.TimeToSecs(now)}, + // core seg + {SegID: 0x222, ConsDir: false, Timestamp: util.TimeToSecs(now)}, + }, + HopFields: []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 1}}, // IA 110 + {HopField: path.HopField{ConsIngress: 31, ConsEgress: 0}}, // Src + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 51}}, // Dst + {HopField: path.HopField{ConsIngress: 3, ConsEgress: 0}}, // IA 110 + }, + } + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[2].HopField) + dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3].HopField) + + if !afterProcessing { + dpath.InfoFields[0].UpdateSegID(dpath.HopFields[2].HopField.Mac) + return toMsg(t, spkt, dpath) + } + require.NoError(t, dpath.IncPath(3)) + ret := toMsg(t, spkt, dpath) + ret.Addr = &net.UDPAddr{IP: net.ParseIP("10.0.200.200").To4(), Port: 30043} + ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil + return ret + }, + srcInterface: 51, + assertFunc: assert.NoError, + }, + "invalid dest": { + prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { + return router.NewDP(nil, nil, mock_router.NewMockBatchConn(ctrl), nil, + map[addr.SVC][]*net.UDPAddr{}, + xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + }, + mockMsg: func(afterProcessing bool) *ipv4.Message { + spkt, dpath := prepHbirdMsg(now) + spkt.DstIA = xtest.MustParseIA("1-ff00:0:f1") + dpath.HopFields = []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 41, ConsEgress: 40}}, + {HopField: path.HopField{ConsIngress: 31, ConsEgress: 404}}, + {HopField: path.HopField{ConsIngress: 1, ConsEgress: 0}}, + } + dpath.Base.PathMeta.CurrHF = 6 + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + ret := toMsg(t, spkt, dpath) + return ret + }, + srcInterface: 1, + assertFunc: assertIsSCMPError( + slayers.SCMPTypeParameterProblem, + slayers.SCMPCodeInvalidDestinationAddress, + ), + }, + "inbound flyover": { + prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { + return router.NewDP(nil, nil, mock_router.NewMockBatchConn(ctrl), nil, + nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + }, + mockMsg: func(afterProcessing bool) *ipv4.Message { + spkt, dpath := prepHbirdMsg(now) + spkt.DstIA = xtest.MustParseIA("1-ff00:0:110") + dst := addr.MustParseHost("10.0.100.100") + _ = spkt.SetDstAddr(dst) + dpath.HopFields = []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 41, ConsEgress: 40}}, + {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, + {Flyover: true, HopField: path.HopField{ConsIngress: 01, ConsEgress: 0}, ResStartTime: 123, Duration: 304}, + } + dpath.Base.PathMeta.SegLen[0] = 11 + dpath.Base.NumHops = 11 + dpath.Base.PathMeta.CurrHF = 6 + dpath.HopFields[2].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, dpath.InfoFields[0], dpath.HopFields[2], dpath.PathMeta) + if afterProcessing { + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[2].HopField) + ret := toMsg(t, spkt, dpath) + ret.Addr = &net.UDPAddr{IP: dst.IP().AsSlice(), Port: topology.EndhostPort} + ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil + return ret + } + return toMsg(t, spkt, dpath) + }, + srcInterface: 1, + assertFunc: assert.NoError, + }, + "outbound flyover": { + prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { + return router.NewDP( + map[uint16]router.BatchConn{ + uint16(1): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 1: topology.Child, + }, + nil, nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + }, + mockMsg: func(afterProcessing bool) *ipv4.Message { + spkt, dpath := prepHbirdMsg(now) + spkt.SrcIA = xtest.MustParseIA("1-ff00:0:110") + dpath.HopFields = []hummingbird.FlyoverHopField{ + {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 1}, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 41, ConsEgress: 40}, ResStartTime: 123, Duration: 304}, + } + dpath.Base.PathMeta.CurrHF = 0 + dpath.Base.PathMeta.SegLen[0] = 15 + dpath.NumHops = 15 + dpath.HopFields[0].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, dpath.InfoFields[0], dpath.HopFields[0], dpath.Base.PathMeta) + if !afterProcessing { + return toMsg(t, spkt, dpath) + } + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + _ = dpath.IncPath(5) + dpath.InfoFields[0].UpdateSegID(dpath.HopFields[0].HopField.Mac) + ret := toMsg(t, spkt, dpath) + ret.Addr = nil + ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil + return ret + }, + srcInterface: 0, + assertFunc: assert.NoError, + }, + "reservation expired": { + prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { + return router.NewDP(nil, nil, mock_router.NewMockBatchConn(ctrl), nil, + nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + }, + mockMsg: func(afterProcessing bool) *ipv4.Message { + spkt, dpath := prepHbirdMsg(now) + spkt.DstIA = xtest.MustParseIA("1-ff00:0:110") + dst := addr.MustParseHost("10.0.100.100") + _ = spkt.SetDstAddr(dst) + dpath.HopFields = []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 41, ConsEgress: 40}}, + {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, + {Flyover: true, HopField: path.HopField{ConsIngress: 01, ConsEgress: 0}, ResStartTime: 5, Duration: 2}, + } + dpath.Base.PathMeta.SegLen[0] = 11 + dpath.Base.NumHops = 11 + dpath.Base.PathMeta.CurrHF = 6 + dpath.HopFields[2].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, dpath.InfoFields[0], dpath.HopFields[2], dpath.PathMeta) + ret := toMsg(t, spkt, dpath) + if afterProcessing { + ret.Addr = &net.UDPAddr{IP: dst.IP().AsSlice(), Port: topology.EndhostPort} + ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil + } + return ret + }, + srcInterface: 1, + assertFunc: assertIsSCMPError( + slayers.SCMPTypeParameterProblem, + slayers.SCMPCodeReservationExpired, + ), + }, + "brtransit flyover": { + prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { + return router.NewDP( + map[uint16]router.BatchConn{ + uint16(2): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 1: topology.Parent, + 2: topology.Child, + }, + nil, nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + }, + mockMsg: func(afterProcessing bool) *ipv4.Message { + spkt, dpath := prepHbirdMsg(now) + dpath.HopFields = []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, + {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}, ResStartTime: 123, Duration: 304}, + {HopField: path.HopField{ConsIngress: 40, ConsEgress: 41}}, + } + dpath.Base.PathMeta.SegLen[0] = 11 + dpath.Base.PathMeta.CurrHF = 3 + dpath.Base.NumHops = 11 + dpath.HopFields[1].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, dpath.InfoFields[0], dpath.HopFields[1], dpath.Base.PathMeta) + if !afterProcessing { + return toMsg(t, spkt, dpath) + } + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + _ = dpath.IncPath(5) + dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.Mac) + ret := toMsg(t, spkt, dpath) + ret.Addr = nil + ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil + return ret + }, + srcInterface: 1, + assertFunc: assert.NoError, + }, + "astransit direct flyover": { + prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { + return router.NewDP(nil, + map[uint16]topology.LinkType{ + 1: topology.Core, + 3: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(3): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + }, + mockMsg: func(afterProcessing bool) *ipv4.Message { + spkt, dpath := prepHbirdMsg(now) + dpath.HopFields = []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, + {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 3}, ResStartTime: 5, Duration: 301}, + {HopField: path.HopField{ConsIngress: 50, ConsEgress: 51}}, + } + dpath.Base.PathMeta.SegLen[0] = 11 + dpath.Base.NumHops = 11 + dpath.HopFields[1].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, dpath.InfoFields[0], dpath.HopFields[1], dpath.Base.PathMeta) + if afterProcessing { + // dpath.HopFields[1].Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1]) + ret := toMsg(t, spkt, dpath) + ret.Addr = &net.UDPAddr{IP: net.ParseIP("10.0.200.200").To4(), Port: 30043} + ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil + return ret + } + return toMsg(t, spkt, dpath) + }, + srcInterface: 1, + assertFunc: assert.NoError, + }, + "astransit xover flyover": { + prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { + return router.NewDP(nil, + map[uint16]topology.LinkType{ + 51: topology.Child, + 3: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(3): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + }, + mockMsg: func(afterProcessing bool) *ipv4.Message { + spkt, _ := prepHbirdMsg(now) + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrHF: 6, + SegLen: [3]uint8{6, 10, 0}, + BaseTS: util.TimeToSecs(now), + }, + NumINF: 2, + NumHops: 16, + }, + InfoFields: []path.InfoField{ + // up seg + {SegID: 0x111, ConsDir: false, Timestamp: util.TimeToSecs(now)}, + // core seg + {SegID: 0x222, ConsDir: false, Timestamp: util.TimeToSecs(now)}, + }, + HopFields: []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 1}}, // IA 110 + {HopField: path.HopField{ConsIngress: 31, ConsEgress: 0}}, // Src + {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 51}, ResStartTime: 5, Duration: 310}, // Dst + {Flyover: true, HopField: path.HopField{ConsIngress: 3, ConsEgress: 0}, ResStartTime: 5, Duration: 410}, // IA 110 + }, + } + dpath.HopFields[2].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, dpath.InfoFields[0], dpath.HopFields[2], dpath.PathMeta) + dpath.HopFields[3].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, dpath.InfoFields[1], dpath.HopFields[3], dpath.PathMeta) + if !afterProcessing { + dpath.InfoFields[0].UpdateSegID(dpath.HopFields[2].HopField.Mac) + + return toMsg(t, spkt, dpath) + } + //dpath.HopFields[2].Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[2]) + //dpath.HopFields[3].Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3]) + require.NoError(t, dpath.IncPath(5)) + ret := toMsg(t, spkt, dpath) + ret.Addr = &net.UDPAddr{IP: net.ParseIP("10.0.200.200").To4(), Port: 30043} + ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil + return ret + }, + srcInterface: 51, + assertFunc: assert.NoError, + }, + } + + for name, tc := range testCases { + name, tc := name, tc + if name == "reservation expired" || name == "invalid dest" { + // TODO: make scmp packets work with hbird paths to re-enable these + continue + } + t.Run(name, func(t *testing.T) { + t.Parallel() + dp := tc.prepareDP(ctrl) + input, want := tc.mockMsg(false), tc.mockMsg(true) + result, err := dp.ProcessPkt(tc.srcInterface, input) + tc.assertFunc(t, err) + if err != nil { + return + } + outPkt := &ipv4.Message{ + Buffers: [][]byte{result.OutPkt}, + Addr: result.OutAddr, + } + if result.OutAddr == nil { + outPkt.Addr = nil + } + assert.Equal(t, want, outPkt) + }) + } +} + +// func TestFlyoverPathReverseLength(t *testing.T) { +// //Performs test by provoking an error and checking the length of the returned SCMP packet +// //Does this with two different length paths, which should be reduced to identical length due to flyover removal +// //TODO: Fix scmp packets for hbirdpath to make test work +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() + +// key := []byte("testkey_xxxxxxxx") +// sv := []byte("test_secretvalue") +// now := time.Now() + +// //prepare datalane +// dp := router.NewDP(nil, nil, mock_router.NewMockBatchConn(ctrl), nil, +// nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + +// //prepare input message 1 +// spkt, dpath := prepHbirdMsg(now) +// spkt.DstIA = xtest.MustParseIA("1-ff00:0:110") +// dst := addr.MustParseHost("10.0.100.100") +// _ = spkt.SetDstAddr(dst) +// dpath.HopFields = []hummingbird.FlyoverHopField{ +// {Flyover: true, HopField: path.HopField{ConsIngress: 41, ConsEgress: 40}, ResStartTime: 5, Duration: 200}, +// {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, +// {Flyover: true, HopField: path.HopField{ConsIngress: 01, ConsEgress: 0}, ResStartTime: 5, Duration: 2}, +// } +// dpath.Base.PathMeta.SegLen[0] = 13 +// dpath.Base.NumHops = 13 +// dpath.Base.PathMeta.CurrHF = 8 +// dpath.HopFields[2].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, dpath.InfoFields[0], dpath.HopFields[2], dpath.PathMeta) +// inputLong := toMsg(t, spkt, dpath) + +// //input message 2 +// spkt, dpath = prepHbirdMsg(now) +// spkt.DstIA = xtest.MustParseIA("1-ff00:0:110") +// dst = addr.MustParseHost("10.0.100.100") +// _ = spkt.SetDstAddr(dst) +// dpath.HopFields = []hummingbird.FlyoverHopField{ +// {HopField: path.HopField{ConsIngress: 41, ConsEgress: 40}}, +// {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, +// {Flyover: true, HopField: path.HopField{ConsIngress: 01, ConsEgress: 0}, ResStartTime: 5, Duration: 2}, +// } +// dpath.Base.PathMeta.SegLen[0] = 11 +// dpath.Base.NumHops = 11 +// dpath.Base.PathMeta.CurrHF = 6 +// dpath.HopFields[2].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, dpath.InfoFields[0], dpath.HopFields[2], dpath.PathMeta) +// inputShort := toMsg(t, spkt, dpath) +// fmt.Printf("inputShort: %x\n", inputShort) +// fmt.Printf("inputLong: %x\n", inputLong) + +// //set src interface +// var srcInterface uint16 = 1 +// res, _ := dp.ProcessPkt(srcInterface, inputLong) +// res2, _ := dp.ProcessPkt(srcInterface, inputShort) + +// layer1 := slayers.SCION{} +// layer1.RecyclePaths() +// layer1.DecodeFromBytes(res.OutPkt, gopacket.NilDecodeFeedback) + +// layer2 := slayers.SCION{} +// layer2.RecyclePaths() +// layer2.DecodeFromBytes(res2.OutPkt, gopacket.NilDecodeFeedback) +// assert.Equal(t, layer1.Path.Len(), layer2.Path.Len()) +// assert.Equal(t, 56, layer1.Path.Len()) +// } + +func prepHbirdMsg(now time.Time) (*slayers.SCION, *hummingbird.Decoded) { + spkt := &slayers.SCION{ + Version: 0, + TrafficClass: 0xb8, + FlowID: 0xdead, + NextHdr: slayers.L4UDP, + PathType: hummingbird.PathType, + DstIA: xtest.MustParseIA("4-ff00:0:411"), + SrcIA: xtest.MustParseIA("2-ff00:0:222"), + Path: &hummingbird.Raw{}, + PayloadLen: 18, + } + + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrHF: 3, + SegLen: [3]uint8{9, 0, 0}, + BaseTS: util.TimeToSecs(now), + HighResTS: 500 << 22, + }, + NumINF: 1, + NumHops: 9, + }, + InfoFields: []path.InfoField{ + {SegID: 0x111, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + }, + + HopFields: []hummingbird.FlyoverHopField{}, + } + return spkt, dpath +} + +func computeAggregateMac(t *testing.T, key, sv []byte, dst addr.IA, l uint16, info path.InfoField, hf hummingbird.FlyoverHopField, meta hummingbird.MetaHdr) [path.MacLen]byte { + + scionMac := computeMAC(t, key, info, hf.HopField) + + block, err := aes.NewCipher(sv) + require.NoError(t, err) + + ak := hummingbird.DeriveAuthKey(block, hf.ResID, hf.Bw, hf.HopField.ConsIngress, hf.HopField.ConsEgress, + meta.BaseTS-uint32(hf.ResStartTime), hf.Duration, nil) + flyoverMac := hummingbird.FullFlyoverMac(ak, dst, l, hf.ResStartTime, + meta.HighResTS, nil, nil) + + for i, b := range scionMac { + scionMac[i] = b ^ flyoverMac[i] + } + return scionMac +} diff --git a/router/export_test.go b/router/export_test.go index a82d01e5a0..5cb770aaf6 100644 --- a/router/export_test.go +++ b/router/export_test.go @@ -47,7 +47,7 @@ func NewDP( svc map[addr.SVC][]*net.UDPAddr, local addr.IA, neighbors map[uint16]addr.IA, - key []byte) *DataPlane { + key []byte, sv []byte) *DataPlane { dp := &DataPlane{ localIA: local, @@ -62,6 +62,9 @@ func NewDP( if err := dp.SetKey(key); err != nil { panic(err) } + if err := dp.SetSecretValue(sv); err != nil { + panic(err) + } return dp } From a57abab8b9da129d661bfa05bb88c1d2210fca6a Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Thu, 26 Oct 2023 16:14:10 +0200 Subject: [PATCH 002/100] fixed issues introduced in hummingbird rebase: reintroduced p.cachedMAC for usage by EPIC replaced segmentChange by effectiveXover in hbird functions fixed all tests fixed buffers for mac computation fixed other compilation issues --- router/connector.go | 15 +++- router/control/conf.go | 4 ++ router/dataplane.go | 19 ++++- router/dataplane_hbird.go | 114 +++++++++++++++++++----------- router/dataplane_hbird_test.go | 10 +-- router/dataplane_internal_test.go | 6 +- router/dataplane_test.go | 43 ++++++----- 7 files changed, 135 insertions(+), 76 deletions(-) diff --git a/router/connector.go b/router/connector.go index dffc1bde94..f5dce240af 100644 --- a/router/connector.go +++ b/router/connector.go @@ -177,11 +177,22 @@ func (c *Connector) SetKey(ia addr.IA, index int, key []byte) error { if index != 0 { return serrors.New("currently only index 0 key is supported") } - //TODO: have method to set secret value separately - c.DataPlane.SetSecretValue(key) return c.DataPlane.SetKey(key) } +func (c *Connector) SetSecretValue(ia addr.IA, index int, sv []byte) error { + c.mtx.Lock() + defer c.mtx.Unlock() + log.Debug("Setting secret value", "isd_as", ia, "index", index) + if !c.ia.Equal(ia) { + return serrors.WithCtx(errMultiIA, "current", c.ia, "new", ia) + } + if index != 0 { + return serrors.New("currently only index 0 secret value is supported") + } + return c.DataPlane.SetSecretValue(sv) +} + func (c *Connector) ListInternalInterfaces() ([]control.InternalInterface, error) { c.mtx.Lock() defer c.mtx.Unlock() diff --git a/router/control/conf.go b/router/control/conf.go index d5cf15a676..afb168544e 100644 --- a/router/control/conf.go +++ b/router/control/conf.go @@ -37,6 +37,7 @@ type Dataplane interface { AddSvc(ia addr.IA, svc addr.SVC, ip net.IP) error DelSvc(ia addr.IA, svc addr.SVC, ip net.IP) error SetKey(ia addr.IA, index int, key []byte) error + SetSecretValue(ia addr.IA, index int, key []byte) error } // LinkInfo contains the information about a link between an internal and @@ -124,6 +125,9 @@ func ConfigDataplane(dp Dataplane, cfg *Config) error { if err := dp.SetKey(cfg.IA, 0, key0); err != nil { return err } + if err := dp.SetSecretValue(cfg.IA, 0, key0); err != nil { + return err + } } // Add internal interfaces if cfg.BR != nil { diff --git a/router/dataplane.go b/router/dataplane.go index a253135b1a..e41e0ff656 100644 --- a/router/dataplane.go +++ b/router/dataplane.go @@ -148,6 +148,7 @@ var ( macVerificationFailed = errors.New("MAC verification failed") badPacketSize = errors.New("bad packet size") slowPathRequired = errors.New("slow-path required") + reservationExpired = errors.New("current time is outside of reservation validity window") // zeroBuffer will be used to reset the Authenticator option in the // scionPacketProcessor.OptAuth @@ -994,9 +995,9 @@ func newPacketProcessor(d *DataPlane) *scionPacketProcessor { p := &scionPacketProcessor{ d: d, buffer: gopacket.NewSerializeBuffer(), - prf: d.prfFactory(), + prf: d.prfFactory(), mac: d.macFactory(), - macInputBuffer: make([]byte, max(path.MACBufferSize, libepic.MACBufferSize,hummingbird.FlyoverMacBufferSize,hummingbird.AkBufferSize)), + macInputBuffer: make([]byte, max(path.MACBufferSize+hummingbird.FlyoverMacBufferSize+hummingbird.AkBufferSize, libepic.MACBufferSize)), hbirdXkbuffer: make([]uint32, hummingbird.XkBufferSize), } p.scionLayer.RecyclePaths() @@ -1009,8 +1010,10 @@ func (p *scionPacketProcessor) reset() error { p.ingressID = 0 //p.scionLayer // cannot easily be reset p.path = nil + p.hbirdPath = nil p.hopField = path.HopField{} p.infoField = path.InfoField{} + p.flyoverField = hummingbird.FlyoverHopField{} p.effectiveXover = false p.peering = false p.hasPriority = false @@ -1225,6 +1228,8 @@ type scionPacketProcessor struct { cachedMac []byte // macInputBuffer avoid allocating memory during processing. macInputBuffer []byte + // hbirdXkbuffer avoid allocating memory during aes computation for hummingbird flyover mac + hbirdXkbuffer []uint32 // bfdLayer is reusable buffer for parsing BFD messages bfdLayer layers.BFD @@ -1524,11 +1529,17 @@ func (p *scionPacketProcessor) updateNonConsDirIngressSegID() error { } func (p *scionPacketProcessor) currentInfoPointer() uint16 { + if p.path == nil { + return p.currentHbirdInfoPointer() + } return uint16(slayers.CmnHdrLen + p.scionLayer.AddrHdrLen() + scion.MetaLen + path.InfoLen*int(p.path.PathMeta.CurrINF)) } func (p *scionPacketProcessor) currentHopPointer() uint16 { + if p.path == nil { + return p.currentHbirdHopPointer() + } return uint16(slayers.CmnHdrLen + p.scionLayer.AddrHdrLen() + scion.MetaLen + path.InfoLen*p.path.NumINF + path.HopLen*int(p.path.PathMeta.CurrHF)) } @@ -1558,7 +1569,9 @@ func (p *scionPacketProcessor) verifyCurrentMAC() (processResult, error) { pointer: p.currentHopPointer(), cause: macVerificationFailed, } - return processResult{SlowPathRequest: slowPathRequest}, slowPathRequired } + return processResult{SlowPathRequest: slowPathRequest}, slowPathRequired + } + p.cachedMac = fullMac return processResult{}, nil } diff --git a/router/dataplane_hbird.go b/router/dataplane_hbird.go index bee02be3e8..1a3c10d575 100644 --- a/router/dataplane_hbird.go +++ b/router/dataplane_hbird.go @@ -3,10 +3,12 @@ package router import ( "crypto/aes" "crypto/cipher" + "crypto/subtle" "encoding/binary" "fmt" "time" + "github.com/scionproto/scion/pkg/log" "github.com/scionproto/scion/pkg/private/serrors" "github.com/scionproto/scion/pkg/private/util" "github.com/scionproto/scion/pkg/slayers" @@ -64,46 +66,70 @@ func (p *scionPacketProcessor) validateReservationExpiry() (processResult, error if startTime.Before(now) && now.Before(endTime) { return processResult{}, nil } - return p.packSCMP(slayers.SCMPTypeParameterProblem, - slayers.SCMPCodeReservationExpired, - &slayers.SCMPParameterProblem{Pointer: p.currentHopPointer()}, - serrors.New("reservation not valid now", "reservation start", startTime, "reservation end", endTime, "now", now), - ) + log.Debug("SCMP: Reservation is not valid at current time", "reservation start", startTime, + "reservation end", endTime, "now", now) + slowPathRequest := slowPathRequest{ + scmpType: slayers.SCMPTypeParameterProblem, + code: slayers.SCMPCodeReservationExpired, + pointer: p.currentHopPointer(), + cause: reservationExpired, + } + return processResult{SlowPathRequest: slowPathRequest}, slowPathRequired +} + +func (p *scionPacketProcessor) currentHbirdInfoPointer() uint16 { + return uint16(slayers.CmnHdrLen + p.scionLayer.AddrHdrLen() + + hummingbird.MetaLen + path.InfoLen*int(p.hbirdPath.PathMeta.CurrINF)) +} + +func (p *scionPacketProcessor) currentHbirdHopPointer() uint16 { + return uint16(slayers.CmnHdrLen + p.scionLayer.AddrHdrLen() + + hummingbird.MetaLen + path.InfoLen*p.hbirdPath.NumINF + hummingbird.LineLen*int(p.hbirdPath.PathMeta.CurrHF)) } func (p *scionPacketProcessor) verifyCurrentHbirdMAC() (processResult, error) { - scionMac := path.FullMAC(p.mac, p.infoField, p.hopField, p.macBuffers.scionInput) + scionMac := path.FullMAC(p.mac, p.infoField, p.hopField, p.macInputBuffer[:path.MACBufferSize]) - var verified bool + var verified int if p.flyoverField.Flyover { ak := hummingbird.DeriveAuthKey(p.prf, p.flyoverField.ResID, p.flyoverField.Bw, p.hopField.ConsIngress, p.hopField.ConsEgress, - p.hbirdPath.PathMeta.BaseTS-uint32(p.flyoverField.ResStartTime), p.flyoverField.Duration, p.macBuffers.hbirdAuthInput) + p.hbirdPath.PathMeta.BaseTS-uint32(p.flyoverField.ResStartTime), p.flyoverField.Duration, + p.macInputBuffer[path.MACBufferSize+hummingbird.FlyoverMacBufferSize:]) flyoverMac := hummingbird.FullFlyoverMac(ak, p.scionLayer.DstIA, p.scionLayer.PayloadLen, p.flyoverField.ResStartTime, - p.hbirdPath.PathMeta.HighResTS, p.macBuffers.hbirdMacInput, p.macBuffers.hbirdXkbuffer) + p.hbirdPath.PathMeta.HighResTS, p.macInputBuffer[path.MACBufferSize:], p.hbirdXkbuffer) // Xor to Aggregate MACs binary.BigEndian.PutUint64(flyoverMac[0:8], binary.BigEndian.Uint64(scionMac[0:8])^binary.BigEndian.Uint64(flyoverMac[0:8])) binary.BigEndian.PutUint32(flyoverMac[8:12], binary.BigEndian.Uint32(scionMac[8:12])^binary.BigEndian.Uint32(flyoverMac[8:12])) - verified = CompareMac(p.hopField.Mac[:path.MacLen], flyoverMac[:path.MacLen]) + verified = subtle.ConstantTimeCompare(p.hopField.Mac[:path.MacLen], flyoverMac[:path.MacLen]) + if verified == 0 { + log.Debug("SCMP: Aggregate MAC verification failed", "expected", fmt.Sprintf("%x", flyoverMac[:path.MacLen]), + "actual", fmt.Sprintf("%x", p.hopField.Mac[:path.MacLen]), "cons_dir", p.infoField.ConsDir, + "if_id", p.ingressID, "curr_inf", p.hbirdPath.PathMeta.CurrINF, + "curr_hf", p.hbirdPath.PathMeta.CurrHF, "seg_id", p.infoField.SegID) + } } else { - verified = CompareMac(p.hopField.Mac[:path.MacLen], scionMac[:path.MacLen]) - } - // Add the full MAC to the SCION packet processor, - // such that EPIC and hummingbird mac de-aggregation do not need to recalculate it. - p.cachedMac = scionMac - if !verified { - return p.packSCMP( - slayers.SCMPTypeParameterProblem, - slayers.SCMPCodeInvalidHopFieldMAC, - &slayers.SCMPParameterProblem{Pointer: p.currentHopPointer()}, - serrors.New("MAC verification failed", "expected", fmt.Sprintf( + verified = subtle.ConstantTimeCompare(p.hopField.Mac[:path.MacLen], scionMac[:path.MacLen]) + if verified == 0 { + log.Debug("SCMP: MAC verification failed", "expected", fmt.Sprintf( "%x", scionMac[:path.MacLen]), "actual", fmt.Sprintf("%x", p.hopField.Mac[:path.MacLen]), - "aggregate with flyover", p.flyoverField.Flyover, "cons_dir", p.infoField.ConsDir, "if_id", p.ingressID, "curr_inf", p.path.PathMeta.CurrINF, - "curr_hf", p.path.PathMeta.CurrHF, "seg_id", p.infoField.SegID), - ) + "curr_hf", p.path.PathMeta.CurrHF, "seg_id", p.infoField.SegID) + } + } + // Add the full MAC to the SCION packet processor, + // such that EPIC and hummingbird mac de-aggregation do not need to recalculate it. + p.cachedMac = scionMac + if verified == 0 { + slowPathRequest := slowPathRequest{ + scmpType: slayers.SCMPTypeParameterProblem, + code: slayers.SCMPCodeInvalidHopFieldMAC, + pointer: p.currentHopPointer(), + cause: macVerificationFailed, + } + return processResult{SlowPathRequest: slowPathRequest}, slowPathRequired } return processResult{}, nil } @@ -173,10 +199,14 @@ func (p *scionPacketProcessor) handleHbirdIngressRouterAlert() (processResult, e return processResult{}, nil } *alert = false - if err := p.hbirdPath.SetHopField(p.flyoverField, int(p.path.PathMeta.CurrHF)); err != nil { + if err := p.hbirdPath.SetHopField(p.flyoverField, int(p.hbirdPath.PathMeta.CurrHF)); err != nil { return processResult{}, serrors.WrapStr("update hop field", err) } - return p.handleSCMPTraceRouteRequest(p.ingressID) + slowPathRequest := slowPathRequest{ + typ: slowPathRouterAlert, + interfaceId: p.ingressID, + } + return processResult{SlowPathRequest: slowPathRequest}, slowPathRequired } func (p *scionPacketProcessor) handleHbirdEgressRouterAlert() (processResult, error) { @@ -189,10 +219,14 @@ func (p *scionPacketProcessor) handleHbirdEgressRouterAlert() (processResult, er return processResult{}, nil } *alert = false - if err := p.hbirdPath.SetHopField(p.flyoverField, int(p.path.PathMeta.CurrHF)); err != nil { + if err := p.hbirdPath.SetHopField(p.flyoverField, int(p.hbirdPath.PathMeta.CurrHF)); err != nil { return processResult{}, serrors.WrapStr("update hop field", err) } - return p.handleSCMPTraceRouteRequest(egressID) + slowPathRequest := slowPathRequest{ + typ: slowPathRouterAlert, + interfaceId: egressID, + } + return processResult{SlowPathRequest: slowPathRequest}, slowPathRequired } func (p *scionPacketProcessor) updateHbirdNonConsDirIngressSegID() error { @@ -216,18 +250,14 @@ func (p *scionPacketProcessor) deAggregateMac() (processResult, error) { copy(p.hopField.Mac[:], p.cachedMac[:path.MacLen]) if err := p.hbirdPath.ReplaceCurrentMac(p.cachedMac); err != nil { //TODO: what SCMP packet should be returned here? Is that even necessary? - return p.packSCMP( - slayers.SCMPTypeParameterProblem, - slayers.SCMPCodeInvalidHopFieldMAC, - &slayers.SCMPParameterProblem{Pointer: p.currentHopPointer()}, - serrors.Join(err, serrors.New("Mac replacement failed")), - ) + log.Debug("Failed to replace MAC after de-aggregation", "error", err.Error()) + return processResult{}, serrors.Join(err, serrors.New("Mac replacement failed")) } return processResult{}, nil } func (p *scionPacketProcessor) doHbirdXover() (processResult, error) { - p.segmentChange = true + p.effectiveXover = true n := 3 if p.flyoverField.Flyover { n = 5 @@ -397,10 +427,12 @@ func (p *scionPacketProcessor) processHBIRD() (processResult, error) { if !p.infoField.ConsDir { errCode = slayers.SCMPCodeUnknownHopFieldIngress } - return p.packSCMP( - slayers.SCMPTypeParameterProblem, - errCode, - &slayers.SCMPParameterProblem{Pointer: p.currentHopPointer()}, - cannotRoute, - ) + log.Debug("SCMP: cannot route") + slowPathRequest := slowPathRequest{ + scmpType: slayers.SCMPTypeParameterProblem, + code: errCode, + pointer: p.currentHopPointer(), + cause: cannotRoute, + } + return processResult{SlowPathRequest: slowPathRequest}, slowPathRequired } diff --git a/router/dataplane_hbird_test.go b/router/dataplane_hbird_test.go index 362b7f3a58..cfa9e34650 100644 --- a/router/dataplane_hbird_test.go +++ b/router/dataplane_hbird_test.go @@ -289,10 +289,7 @@ func TestProcessHbirdPacket(t *testing.T) { return ret }, srcInterface: 1, - assertFunc: assertIsSCMPError( - slayers.SCMPTypeParameterProblem, - slayers.SCMPCodeInvalidDestinationAddress, - ), + assertFunc: assert.Error, }, "inbound flyover": { prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { @@ -389,10 +386,7 @@ func TestProcessHbirdPacket(t *testing.T) { return ret }, srcInterface: 1, - assertFunc: assertIsSCMPError( - slayers.SCMPTypeParameterProblem, - slayers.SCMPCodeReservationExpired, - ), + assertFunc: assert.Error, }, "brtransit flyover": { prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { diff --git a/router/dataplane_internal_test.go b/router/dataplane_internal_test.go index 6e92a8219d..e3c75430c8 100644 --- a/router/dataplane_internal_test.go +++ b/router/dataplane_internal_test.go @@ -432,7 +432,7 @@ func TestSlowPathProcessing(t *testing.T) { prepareDP: func(ctrl *gomock.Controller) *DataPlane { return NewDP(nil, nil, mock_router.NewMockBatchConn(ctrl), nil, map[addr.SVC][]*net.UDPAddr{}, - xtest.MustParseIA("1-ff00:0:110"), nil, testKey) + xtest.MustParseIA("1-ff00:0:110"), nil, testKey, testKey) }, mockMsg: func() []byte { spkt := prepBaseMsg(t, payload, 0) @@ -454,7 +454,7 @@ func TestSlowPathProcessing(t *testing.T) { prepareDP: func(ctrl *gomock.Controller) *DataPlane { return NewDP(nil, nil, mock_router.NewMockBatchConn(ctrl), nil, map[addr.SVC][]*net.UDPAddr{}, - xtest.MustParseIA("1-ff00:0:110"), nil, testKey) + xtest.MustParseIA("1-ff00:0:110"), nil, testKey, testKey) }, mockMsg: func() []byte { spkt := prepBaseMsg(t, payload, 0) @@ -476,7 +476,7 @@ func TestSlowPathProcessing(t *testing.T) { prepareDP: func(ctrl *gomock.Controller) *DataPlane { return NewDP(nil, nil, mock_router.NewMockBatchConn(ctrl), nil, map[addr.SVC][]*net.UDPAddr{}, - xtest.MustParseIA("1-ff00:0:110"), nil, testKey) + xtest.MustParseIA("1-ff00:0:110"), nil, testKey, testKey) }, mockMsg: func() []byte { spkt := prepBaseMsg(t, payload, 0) diff --git a/router/dataplane_test.go b/router/dataplane_test.go index 57f3b20ddf..4193d027b7 100644 --- a/router/dataplane_test.go +++ b/router/dataplane_test.go @@ -261,6 +261,7 @@ func TestDataPlaneRun(t *testing.T) { _ = ret.SetIA(local) _ = ret.SetKey(key) + _ = ret.SetSecretValue(key) return ret }, }, @@ -336,6 +337,7 @@ func TestDataPlaneRun(t *testing.T) { local := &net.UDPAddr{IP: net.ParseIP("10.0.200.100").To4()} _ = ret.SetKey([]byte("randomkeyformacs")) + _ = ret.SetSecretValue([]byte("randomsvformacss")) _ = ret.AddInternalInterface(mInternal, net.IP{}) for remote, ifIDs := range routers { for _, ifID := range ifIDs { @@ -392,6 +394,7 @@ func TestDataPlaneRun(t *testing.T) { mInternal.EXPECT().ReadBatch(gomock.Any()).Return(0, nil).AnyTimes() _ = ret.SetKey([]byte("randomkeyformacs")) + _ = ret.SetSecretValue([]byte("randomsvformacss")) _ = ret.AddInternalInterface(mInternal, net.IP{}) _ = ret.AddNextHop(3, localAddr) _ = ret.AddNextHopBFD(3, localAddr, remoteAddr, bfd(), "") @@ -446,6 +449,7 @@ func TestDataPlaneRun(t *testing.T) { Addr: &net.UDPAddr{IP: net.ParseIP("10.0.0.200")}, } _ = ret.SetKey([]byte("randomkeyformacs")) + _ = ret.SetSecretValue([]byte("randomsvformacss")) _ = ret.AddInternalInterface(mInternal, net.IP{}) _ = ret.AddExternalInterface(ifID, mExternal) _ = ret.AddExternalInterfaceBFD(ifID, mExternal, local, remote, bfd()) @@ -526,6 +530,7 @@ func TestDataPlaneRun(t *testing.T) { Addr: &net.UDPAddr{IP: net.ParseIP("10.0.0.200")}, } _ = ret.SetKey([]byte("randomkeyformacs")) + _ = ret.SetSecretValue([]byte("randomsvformacss")) _ = ret.AddInternalInterface(mInternal, net.IP{}) _ = ret.AddExternalInterface(1, mExternal) _ = ret.AddExternalInterfaceBFD(1, mExternal, local, remote, bfd()) @@ -586,7 +591,7 @@ func TestProcessPkt(t *testing.T) { "inbound": { prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { return router.NewDP(nil, nil, mock_router.NewMockBatchConn(ctrl), nil, - nil, xtest.MustParseIA("1-ff00:0:110"), nil, key) + nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, key) }, mockMsg: func(afterProcessing bool) *ipv4.Message { spkt, dpath := prepBaseMsg(now) @@ -620,7 +625,7 @@ func TestProcessPkt(t *testing.T) { map[uint16]topology.LinkType{ 1: topology.Child, }, - nil, nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key) + nil, nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, key) }, mockMsg: func(afterProcessing bool) *ipv4.Message { spkt, dpath := prepBaseMsg(now) @@ -656,7 +661,7 @@ func TestProcessPkt(t *testing.T) { 1: topology.Parent, 2: topology.Child, }, - nil, nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key) + nil, nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, key) }, mockMsg: func(afterProcessing bool) *ipv4.Message { spkt, dpath := prepBaseMsg(now) @@ -690,7 +695,7 @@ func TestProcessPkt(t *testing.T) { map[uint16]topology.LinkType{ 2: topology.Parent, 1: topology.Child, - }, nil, nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key) + }, nil, nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, key) }, mockMsg: func(afterProcessing bool) *ipv4.Message { spkt, dpath := prepBaseMsg(now) @@ -726,7 +731,7 @@ func TestProcessPkt(t *testing.T) { 1: topology.Peer, 2: topology.Child, }, - nil, nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key) + nil, nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, key) }, mockMsg: func(afterProcessing bool) *ipv4.Message { // Story: the packet just left segment 0 which ends at @@ -797,7 +802,7 @@ func TestProcessPkt(t *testing.T) { 1: topology.Peer, 2: topology.Child, }, - nil, nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key) + nil, nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, key) }, mockMsg: func(afterProcessing bool) *ipv4.Message { // Story: the packet lands on the last (peering) hop of @@ -876,7 +881,7 @@ func TestProcessPkt(t *testing.T) { 1: topology.Peer, 2: topology.Child, }, - nil, nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key) + nil, nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, key) }, mockMsg: func(afterProcessing bool) *ipv4.Message { // Story: the packet just left hop 1 (the first hop @@ -951,7 +956,7 @@ func TestProcessPkt(t *testing.T) { 1: topology.Peer, 2: topology.Child, }, - nil, nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key) + nil, nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, key) }, mockMsg: func(afterProcessing bool) *ipv4.Message { // Story: the packet lands on the second (non-peering) hop of @@ -1037,7 +1042,7 @@ func TestProcessPkt(t *testing.T) { mock_router.NewMockBatchConn(ctrl), map[uint16]*net.UDPAddr{ uint16(3): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, - }, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key) + }, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, key) }, mockMsg: func(afterProcessing bool) *ipv4.Message { spkt, dpath := prepBaseMsg(now) @@ -1068,7 +1073,7 @@ func TestProcessPkt(t *testing.T) { mock_router.NewMockBatchConn(ctrl), map[uint16]*net.UDPAddr{ uint16(3): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, - }, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key) + }, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, key) }, mockMsg: func(afterProcessing bool) *ipv4.Message { spkt, _ := prepBaseMsg(now) @@ -1122,7 +1127,7 @@ func TestProcessPkt(t *testing.T) { }, }, }, - xtest.MustParseIA("1-ff00:0:110"), nil, key) + xtest.MustParseIA("1-ff00:0:110"), nil, key, key) }, mockMsg: func(afterProcessing bool) *ipv4.Message { spkt, dpath := prepBaseMsg(now) @@ -1162,7 +1167,7 @@ func TestProcessPkt(t *testing.T) { xtest.MustParseIA("1-ff00:0:110"), map[uint16]addr.IA{ uint16(1): xtest.MustParseIA("1-ff00:0:111"), - }, key) + }, key, key) }, mockMsg: func(afterProcessing bool) *ipv4.Message { spkt, _ := prepBaseMsg(now) @@ -1217,7 +1222,7 @@ func TestProcessPkt(t *testing.T) { xtest.MustParseIA("1-ff00:0:110"), map[uint16]addr.IA{ uint16(1): xtest.MustParseIA("1-ff00:0:111"), - }, key) + }, key, key) }, mockMsg: func(afterProcessing bool) *ipv4.Message { spkt, _ := prepBaseMsg(now) @@ -1261,7 +1266,7 @@ func TestProcessPkt(t *testing.T) { Port: topology.EndhostPort, }}, }, - xtest.MustParseIA("1-ff00:0:110"), nil, key) + xtest.MustParseIA("1-ff00:0:110"), nil, key, key) }, mockMsg: func(afterProcessing bool) *ipv4.Message { spkt, _ := prepBaseMsg(now) @@ -1319,7 +1324,7 @@ func TestProcessPkt(t *testing.T) { xtest.MustParseIA("1-ff00:0:110"), map[uint16]addr.IA{ uint16(2): xtest.MustParseIA("1-ff00:0:111"), - }, key) + }, key, key) }, mockMsg: func(afterProcessing bool) *ipv4.Message { spkt, _ := prepBaseMsg(now) @@ -1357,7 +1362,7 @@ func TestProcessPkt(t *testing.T) { "epic inbound": { prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { return router.NewDP(nil, nil, mock_router.NewMockBatchConn(ctrl), nil, - nil, xtest.MustParseIA("1-ff00:0:110"), nil, key) + nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, key) }, mockMsg: func(afterProcessing bool) *ipv4.Message { spkt, epicpath, dpath := prepEpicMsg(t, @@ -1373,7 +1378,7 @@ func TestProcessPkt(t *testing.T) { "epic malformed path": { prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { return router.NewDP(nil, nil, mock_router.NewMockBatchConn(ctrl), nil, - nil, xtest.MustParseIA("1-ff00:0:110"), nil, key) + nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, key) }, mockMsg: func(afterProcessing bool) *ipv4.Message { spkt, epicpath, dpath := prepEpicMsg(t, @@ -1390,7 +1395,7 @@ func TestProcessPkt(t *testing.T) { "epic invalid timestamp": { prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { return router.NewDP(nil, nil, mock_router.NewMockBatchConn(ctrl), nil, - nil, xtest.MustParseIA("1-ff00:0:110"), nil, key) + nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, key) }, mockMsg: func(afterProcessing bool) *ipv4.Message { spkt, epicpath, dpath := prepEpicMsg(t, @@ -1409,7 +1414,7 @@ func TestProcessPkt(t *testing.T) { "epic invalid LHVF": { prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { return router.NewDP(nil, nil, mock_router.NewMockBatchConn(ctrl), nil, - nil, xtest.MustParseIA("1-ff00:0:110"), nil, key) + nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, key) }, mockMsg: func(afterProcessing bool) *ipv4.Message { spkt, epicpath, dpath := prepEpicMsg(t, From 6026eeb9ff239e727855162351151f77295ac3d2 Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Thu, 26 Oct 2023 17:17:18 +0200 Subject: [PATCH 003/100] added support for hbird path type SCMP packets --- router/dataplane.go | 2 + router/dataplane_hbird.go | 196 +++++++++++++++++++++++++++++++++ router/dataplane_hbird_test.go | 4 - 3 files changed, 198 insertions(+), 4 deletions(-) diff --git a/router/dataplane.go b/router/dataplane.go index e41e0ff656..5f860c923d 100644 --- a/router/dataplane.go +++ b/router/dataplane.go @@ -2128,6 +2128,8 @@ func (p *slowPathPacketProcessor) prepareSCMP( "path type", pathType) } path = epicPath.ScionPath + case hummingbird.PathType: + return p.prepareHbirdSCMP(typ, code, scmpP, cause) default: return nil, serrors.WithCtx(cannotRoute, "details", "unsupported path type", "path type", pathType) diff --git a/router/dataplane_hbird.go b/router/dataplane_hbird.go index 1a3c10d575..1199ea0a33 100644 --- a/router/dataplane_hbird.go +++ b/router/dataplane_hbird.go @@ -8,12 +8,15 @@ import ( "fmt" "time" + "github.com/google/gopacket" + "github.com/scionproto/scion/pkg/addr" "github.com/scionproto/scion/pkg/log" "github.com/scionproto/scion/pkg/private/serrors" "github.com/scionproto/scion/pkg/private/util" "github.com/scionproto/scion/pkg/slayers" "github.com/scionproto/scion/pkg/slayers/path" "github.com/scionproto/scion/pkg/slayers/path/hummingbird" + "github.com/scionproto/scion/pkg/spao" ) // SetSecretValue sets the secret value for the PRF function used to compute the Hummingbird Auth Key @@ -436,3 +439,196 @@ func (p *scionPacketProcessor) processHBIRD() (processResult, error) { } return processResult{SlowPathRequest: slowPathRequest}, slowPathRequired } + +// Functions for SCMP packets preparation + +func determinePeerHbird(pathMeta hummingbird.MetaHdr, inf path.InfoField) (bool, error) { + if !inf.Peer { + return false, nil + } + + if pathMeta.SegLen[0] == 0 { + return false, errPeeringEmptySeg0 + } + if pathMeta.SegLen[1] == 0 { + return false, errPeeringEmptySeg1 + + } + if pathMeta.SegLen[2] != 0 { + return false, errPeeringNonemptySeg2 + } + + // The peer hop fields are the last hop field on the first path + // segment (at SegLen[0] - 1) and the first hop field of the second + // path segment (at SegLen[0]). The below check applies only + // because we already know this is a well-formed peering path. + currHF := pathMeta.CurrHF + segLen := pathMeta.SegLen[0] + return currHF == segLen-3 || currHF == segLen, nil +} + +func (p *slowPathPacketProcessor) prepareHbirdSCMP( + typ slayers.SCMPType, + code slayers.SCMPCode, + scmpP gopacket.SerializableLayer, + cause error, +) ([]byte, error) { + + path, ok := p.scionLayer.Path.(*hummingbird.Raw) + if !ok { + return nil, serrors.WithCtx(cannotRoute, "details", "unsupported path type", + "path type", hummingbird.PathType) + } + decPath, err := path.ToDecoded() + if err != nil { + return nil, serrors.Wrap(cannotRoute, err, "details", "decoding raw path") + } + revPathTmp, err := decPath.Reverse() + if err != nil { + return nil, serrors.Wrap(cannotRoute, err, "details", "reversing path for SCMP") + } + revPath := revPathTmp.(*hummingbird.Decoded) + + peering, err := determinePeerHbird(revPath.PathMeta, revPath.InfoFields[revPath.PathMeta.CurrINF]) + if err != nil { + return nil, serrors.Wrap(cannotRoute, err, "details", "peering cannot be determined") + } + + // Revert potential path segment switches that were done during processing. + if revPath.IsXover() && !peering { + // An effective cross-over is a change of segment other than at + // a peering hop. + if err := revPath.IncPath(3); err != nil { + return nil, serrors.Wrap(cannotRoute, err, "details", "reverting cross over for SCMP") + } + } + + // If the packet is sent to an external router, we need to increment the + // path to prepare it for the next hop. + _, external := p.d.external[p.ingressID] + if external { + infoField := &revPath.InfoFields[revPath.PathMeta.CurrINF] + if infoField.ConsDir && !peering { + hopField := revPath.HopFields[revPath.PathMeta.CurrHF] + infoField.UpdateSegID(hopField.HopField.Mac) + } + if err := revPath.IncPath(3); err != nil { + return nil, serrors.Wrap(cannotRoute, err, "details", "incrementing path for SCMP") + } + } //TODO else, make sure MAC is deaggregated? + + var scionL slayers.SCION + scionL.FlowID = p.scionLayer.FlowID + scionL.TrafficClass = p.scionLayer.TrafficClass + scionL.PathType = revPath.Type() + scionL.Path = revPath + scionL.DstIA = p.scionLayer.SrcIA + scionL.SrcIA = p.d.localIA + srcA, err := p.scionLayer.SrcAddr() + if err != nil { + return nil, serrors.Wrap(cannotRoute, err, "details", "extracting src addr") + } + if err := scionL.SetDstAddr(srcA); err != nil { + return nil, serrors.Wrap(cannotRoute, err, "details", "setting dest addr") + } + if err := scionL.SetSrcAddr(addr.HostIP(p.d.internalIP)); err != nil { + return nil, serrors.Wrap(cannotRoute, err, "details", "setting src addr") + } + scionL.NextHdr = slayers.L4SCMP + + typeCode := slayers.CreateSCMPTypeCode(typ, code) + scmpH := slayers.SCMP{TypeCode: typeCode} + scmpH.SetNetworkLayerForChecksum(&scionL) + + needsAuth := false + if p.d.ExperimentalSCMPAuthentication { + // Error messages must be authenticated. + // Traceroute are OPTIONALLY authenticated ONLY IF the request + // was authenticated. + // TODO(JordiSubira): Reuse the key computed in p.hasValidAuth + // if SCMPTypeTracerouteReply to create the response. + needsAuth = cause != nil || + (scmpH.TypeCode.Type() == slayers.SCMPTypeTracerouteReply && + p.hasValidAuth(time.Now())) + } + + var quote []byte + if cause != nil { + // add quote for errors. + hdrLen := slayers.CmnHdrLen + scionL.AddrHdrLen() + scionL.Path.Len() + if needsAuth { + hdrLen += e2eAuthHdrLen + } + switch scmpH.TypeCode.Type() { + case slayers.SCMPTypeExternalInterfaceDown: + hdrLen += 20 + case slayers.SCMPTypeInternalConnectivityDown: + hdrLen += 28 + default: + hdrLen += 8 + } + quote = p.rawPkt + maxQuoteLen := slayers.MaxSCMPPacketLen - hdrLen + if len(quote) > maxQuoteLen { + quote = quote[:maxQuoteLen] + } + } + + if err := p.buffer.Clear(); err != nil { + return nil, err + } + sopts := gopacket.SerializeOptions{ + ComputeChecksums: true, + FixLengths: true, + } + // First write the SCMP message only without the SCION header(s) to get a buffer that we + // can (re-)use as input in the MAC computation. + // XXX(matzf) could we use iovec gather to avoid copying quote? + err = gopacket.SerializeLayers(p.buffer, sopts, &scmpH, scmpP, gopacket.Payload(quote)) + if err != nil { + return nil, serrors.Wrap(cannotRoute, err, "details", "serializing SCMP message") + } + + if needsAuth { + var e2e slayers.EndToEndExtn + scionL.NextHdr = slayers.End2EndClass + + now := time.Now() + // srcA == scionL.DstAddr + key, err := p.drkeyProvider.GetASHostKey(now, scionL.DstIA, srcA) + if err != nil { + return nil, serrors.Wrap(cannotRoute, err, "details", "retrieving DRKey") + } + if err := p.resetSPAOMetadata(key, now); err != nil { + return nil, serrors.Wrap(cannotRoute, err, "details", "resetting SPAO header") + } + + e2e.Options = []*slayers.EndToEndOption{p.optAuth.EndToEndOption} + e2e.NextHdr = slayers.L4SCMP + _, err = spao.ComputeAuthCMAC( + spao.MACInput{ + Key: key.Key[:], + Header: p.optAuth, + ScionLayer: &scionL, + PldType: slayers.L4SCMP, + Pld: p.buffer.Bytes(), + }, + p.macInputBuffer, + p.optAuth.Authenticator(), + ) + if err != nil { + return nil, serrors.Wrap(cannotRoute, err, "details", "computing CMAC") + } + if err := e2e.SerializeTo(p.buffer, sopts); err != nil { + return nil, serrors.Wrap(cannotRoute, err, "details", "serializing SCION E2E headers") + } + } else { + scionL.NextHdr = slayers.L4SCMP + } + if err := scionL.SerializeTo(p.buffer, sopts); err != nil { + return nil, serrors.Wrap(cannotRoute, err, "details", "serializing SCION header") + } + + log.Debug("scmp", "typecode", scmpH.TypeCode, "cause", cause) + return p.buffer.Bytes(), nil +} diff --git a/router/dataplane_hbird_test.go b/router/dataplane_hbird_test.go index cfa9e34650..fc1c58617f 100644 --- a/router/dataplane_hbird_test.go +++ b/router/dataplane_hbird_test.go @@ -518,10 +518,6 @@ func TestProcessHbirdPacket(t *testing.T) { for name, tc := range testCases { name, tc := name, tc - if name == "reservation expired" || name == "invalid dest" { - // TODO: make scmp packets work with hbird paths to re-enable these - continue - } t.Run(name, func(t *testing.T) { t.Parallel() dp := tc.prepareDP(ctrl) From bfd8f144e26aeca2836b88bf7478161d28d31e3b Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Sat, 28 Oct 2023 10:53:01 +0200 Subject: [PATCH 004/100] updated bazel build files --- pkg/slayers/path/hummingbird/BUILD.bazel | 18 +++++++++++++++++- router/BUILD.bazel | 2 ++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/pkg/slayers/path/hummingbird/BUILD.bazel b/pkg/slayers/path/hummingbird/BUILD.bazel index 2c5ddaf86f..d245ab012d 100644 --- a/pkg/slayers/path/hummingbird/BUILD.bazel +++ b/pkg/slayers/path/hummingbird/BUILD.bazel @@ -6,7 +6,11 @@ go_library( "asm_amd64.s", "asm_arm64.s", "asm_ppc64x.s", + "base.go", + "decoded.go", + "flyoverhopfield.go", "mac.go", + "raw.go", ], importpath = "github.com/scionproto/scion/pkg/slayers/path/hummingbird", visibility = ["//visibility:public"], @@ -14,18 +18,22 @@ go_library( "@io_bazel_rules_go//go/platform:amd64": [ "//pkg/addr:go_default_library", "//pkg/slayers/path:go_default_library", + "//pkg/private/serrors:go_default_library", ], "@io_bazel_rules_go//go/platform:arm64": [ "//pkg/addr:go_default_library", "//pkg/slayers/path:go_default_library", + "//pkg/private/serrors:go_default_library", ], "@io_bazel_rules_go//go/platform:ppc64": [ "//pkg/addr:go_default_library", "//pkg/slayers/path:go_default_library", + "//pkg/private/serrors:go_default_library", ], "@io_bazel_rules_go//go/platform:ppc64le": [ "//pkg/addr:go_default_library", "//pkg/slayers/path:go_default_library", + "//pkg/private/serrors:go_default_library", ], "//conditions:default": [], }), @@ -33,11 +41,19 @@ go_library( go_test( name = "go_default_test", - srcs = ["mac_test.go"], + srcs = [ + "base_test.go", + "decoded_test.go", + "flyoverhopfield_test.go", + "mac_test.go", + "raw_test.go", + ], deps = [ ":go_default_library", "//pkg/addr:go_default_library", "@com_github_dchest_cmac//:go_default_library", "@com_github_stretchr_testify//require:go_default_library", + "@com_github_stretchr_testify//assert:go_default_library", + "//pkg/slayers/path:go_default_library", ], ) diff --git a/router/BUILD.bazel b/router/BUILD.bazel index ed1bb60539..64dc5cbcea 100644 --- a/router/BUILD.bazel +++ b/router/BUILD.bazel @@ -4,6 +4,7 @@ go_library( name = "go_default_library", srcs = [ "connector.go", + "dataplane_hbird.go", "dataplane.go", "metrics.go", "svc.go", @@ -43,6 +44,7 @@ go_library( go_test( name = "go_default_test", srcs = [ + "dataplane_hbird_test.go", "dataplane_internal_test.go", "dataplane_test.go", "export_test.go", From dd6c2f13d08ad1fdf503a934075013421f639860 Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Mon, 30 Oct 2023 14:34:02 +0100 Subject: [PATCH 005/100] Fix bazel build files. --- pkg/slayers/path/hummingbird/BUILD.bazel | 17 ++++++----------- pkg/slayers/path/scion/BUILD.bazel | 1 - router/BUILD.bazel | 2 +- 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/pkg/slayers/path/hummingbird/BUILD.bazel b/pkg/slayers/path/hummingbird/BUILD.bazel index d245ab012d..41354370ba 100644 --- a/pkg/slayers/path/hummingbird/BUILD.bazel +++ b/pkg/slayers/path/hummingbird/BUILD.bazel @@ -14,26 +14,21 @@ go_library( ], importpath = "github.com/scionproto/scion/pkg/slayers/path/hummingbird", visibility = ["//visibility:public"], - deps = select({ + deps = [ + "//pkg/private/serrors:go_default_library", + "//pkg/slayers/path:go_default_library", + ] + select({ "@io_bazel_rules_go//go/platform:amd64": [ "//pkg/addr:go_default_library", - "//pkg/slayers/path:go_default_library", - "//pkg/private/serrors:go_default_library", ], "@io_bazel_rules_go//go/platform:arm64": [ "//pkg/addr:go_default_library", - "//pkg/slayers/path:go_default_library", - "//pkg/private/serrors:go_default_library", ], "@io_bazel_rules_go//go/platform:ppc64": [ "//pkg/addr:go_default_library", - "//pkg/slayers/path:go_default_library", - "//pkg/private/serrors:go_default_library", ], "@io_bazel_rules_go//go/platform:ppc64le": [ "//pkg/addr:go_default_library", - "//pkg/slayers/path:go_default_library", - "//pkg/private/serrors:go_default_library", ], "//conditions:default": [], }), @@ -51,9 +46,9 @@ go_test( deps = [ ":go_default_library", "//pkg/addr:go_default_library", + "//pkg/slayers/path:go_default_library", "@com_github_dchest_cmac//:go_default_library", - "@com_github_stretchr_testify//require:go_default_library", "@com_github_stretchr_testify//assert:go_default_library", - "//pkg/slayers/path:go_default_library", + "@com_github_stretchr_testify//require:go_default_library", ], ) diff --git a/pkg/slayers/path/scion/BUILD.bazel b/pkg/slayers/path/scion/BUILD.bazel index 7fbb1c7e65..a4a57739fa 100644 --- a/pkg/slayers/path/scion/BUILD.bazel +++ b/pkg/slayers/path/scion/BUILD.bazel @@ -12,7 +12,6 @@ go_library( deps = [ "//pkg/private/serrors:go_default_library", "//pkg/slayers/path:go_default_library", - "//pkg/slayers/path/hummingbird:go_default_library", ], ) diff --git a/router/BUILD.bazel b/router/BUILD.bazel index 64dc5cbcea..b29eae6e90 100644 --- a/router/BUILD.bazel +++ b/router/BUILD.bazel @@ -4,8 +4,8 @@ go_library( name = "go_default_library", srcs = [ "connector.go", - "dataplane_hbird.go", "dataplane.go", + "dataplane_hbird.go", "metrics.go", "svc.go", ], From d81ee436e77fef4c6bc0b85621de287a87ffdd24 Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Mon, 30 Oct 2023 15:42:59 +0100 Subject: [PATCH 006/100] added very basic hbird e2e with no reservations --- BUILD.bazel | 2 + pkg/hummingbird/BUILD.bazel | 19 + pkg/hummingbird/hummingbird.go | 66 +++ pkg/slayers/path/hummingbird/BUILD.bazel | 1 + pkg/slayers/path/hummingbird/base.go | 4 +- pkg/slayers/path/hummingbird/decoded.go | 33 +- pkg/snet/path/BUILD.bazel | 2 + pkg/snet/path/hummingbird.go | 30 ++ tools/end2end_hbird/BUILD.bazel | 34 ++ tools/end2end_hbird/main.go | 487 ++++++++++++++++++++ tools/end2end_hbird_integration/BUILD.bazel | 24 + tools/end2end_hbird_integration/main.go | 364 +++++++++++++++ 12 files changed, 1063 insertions(+), 3 deletions(-) create mode 100644 pkg/hummingbird/BUILD.bazel create mode 100644 pkg/hummingbird/hummingbird.go create mode 100644 pkg/snet/path/hummingbird.go create mode 100644 tools/end2end_hbird/BUILD.bazel create mode 100644 tools/end2end_hbird/main.go create mode 100644 tools/end2end_hbird_integration/BUILD.bazel create mode 100644 tools/end2end_hbird_integration/main.go diff --git a/BUILD.bazel b/BUILD.bazel index 267914fd4b..a175cc7660 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -197,6 +197,8 @@ pkg_tar( "//tools/braccept", "//tools/buildkite/cmd/buildkite_artifacts", "//tools/end2end", + "//tools/end2end_hbird", + "//tools/end2end_hbird_integration", "//tools/end2end_integration", "//tools/pktgen/cmd/pktgen", "//tools/scion_integration", diff --git a/pkg/hummingbird/BUILD.bazel b/pkg/hummingbird/BUILD.bazel new file mode 100644 index 0000000000..15d4f828a3 --- /dev/null +++ b/pkg/hummingbird/BUILD.bazel @@ -0,0 +1,19 @@ +load("//tools/lint:go.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = [ + "hummingbird.go", + ], + importpath = "github.com/scionproto/scion/pkg/hummingbird", + visibility = ["//visibility:public"], + deps = [ + "//pkg/addr:go_default_library", + "//pkg/private/serrors:go_default_library", + "//pkg/slayers/path/hummingbird:go_default_library", + "//pkg/slayers/path/scion:go_default_library", + "//pkg/snet:go_default_library", + "//pkg/snet/path:go_default_library", + ], +) + diff --git a/pkg/hummingbird/hummingbird.go b/pkg/hummingbird/hummingbird.go new file mode 100644 index 0000000000..d849ea3276 --- /dev/null +++ b/pkg/hummingbird/hummingbird.go @@ -0,0 +1,66 @@ +package hummingbird + +import ( + "github.com/scionproto/scion/pkg/addr" + "github.com/scionproto/scion/pkg/private/serrors" + "github.com/scionproto/scion/pkg/slayers/path/hummingbird" + "github.com/scionproto/scion/pkg/slayers/path/scion" + "github.com/scionproto/scion/pkg/snet" + snetpath "github.com/scionproto/scion/pkg/snet/path" +) + +type Reservation struct { + // AS denotes the AS for which a reservation is valid + AS addr.IA + // ResID is the reservation ID of the reservation. It is unique PER AS + ResID uint32 + // Ak is the authentication key of the reservation + Ak [16]byte + // Bw is the reserved Bandwidth + Bw uint16 + // StartTime is the unix timestamp for teh start of the reservation + StartTime uint32 + // Duration is the duration of the reservation in seconds + Duration uint16 + // EndTime is the unix timestamp at which the reservation ends. Is not strictly necessary but included for simplicity + EndTime uint32 + // Ingress is the ingress interface for the reserved hop + Ingress uint16 + // Egress is the egress interface of the reserved hop + Egress uint16 +} + +// Adds a reservation to be used for transmission +func AddReservation(res Reservation) error { + return nil +} + +// Converts a SCiON path to a Hummingbird path without adding any reservations +// Relaces the SCiON dataplane path by a Hummingbird path +func ConvertToHbirdPath(p snet.Path) (snet.Path, error) { + if p == nil { + return nil, serrors.New("Cannot convert nil path") + } + dpath, ok := p.Dataplane().(snetpath.SCION) + if !ok { + return nil, serrors.New("Can only convert SCiON paths to Hummingbird") + } + dec := convertSCIONToHbirdDecoded(dpath.Raw) + + hbird, err := snetpath.NewHbirdFromDecoded(&dec) + if err != nil { + return nil, err + } + dpath.Raw = hbird.Raw + return p, nil +} + +func convertSCIONToHbirdDecoded(p []byte) hummingbird.Decoded { + + scionDec := scion.Decoded{} + scionDec.DecodeFromBytes(p) + + hbirdDec := hummingbird.Decoded{} + hbirdDec.ConvertFromScionDecoded(scionDec) + return hbirdDec +} diff --git a/pkg/slayers/path/hummingbird/BUILD.bazel b/pkg/slayers/path/hummingbird/BUILD.bazel index 41354370ba..558ac6c43b 100644 --- a/pkg/slayers/path/hummingbird/BUILD.bazel +++ b/pkg/slayers/path/hummingbird/BUILD.bazel @@ -16,6 +16,7 @@ go_library( visibility = ["//visibility:public"], deps = [ "//pkg/private/serrors:go_default_library", + "//pkg/slayers/path/scion:go_default_library", "//pkg/slayers/path:go_default_library", ] + select({ "@io_bazel_rules_go//go/platform:amd64": [ diff --git a/pkg/slayers/path/hummingbird/base.go b/pkg/slayers/path/hummingbird/base.go index 1d4ccdaf27..c6ebfa5e7b 100644 --- a/pkg/slayers/path/hummingbird/base.go +++ b/pkg/slayers/path/hummingbird/base.go @@ -22,7 +22,7 @@ func RegisterPath() { // Base holds the basic information that is used by both raw and fully decoded paths. type Base struct { - // PathMeta is the SCION path meta header. It is always instantiated when + // PathMeta is the Hummingbird path meta header. It is always instantiated when // decoding a path from bytes. PathMeta MetaHdr // NumINF is the number of InfoFields in the path. @@ -101,7 +101,7 @@ func (s *Base) Type() path.Type { return PathType } -// MetaHdr is the PathMetaHdr of a SCION (data-plane) path type. +// MetaHdr is the PathMetaHdr of a Hummingbird (data-plane) path type. type MetaHdr struct { CurrINF uint8 CurrHF uint8 diff --git a/pkg/slayers/path/hummingbird/decoded.go b/pkg/slayers/path/hummingbird/decoded.go index a456579596..3cfeb13d80 100644 --- a/pkg/slayers/path/hummingbird/decoded.go +++ b/pkg/slayers/path/hummingbird/decoded.go @@ -3,6 +3,7 @@ package hummingbird import ( "github.com/scionproto/scion/pkg/private/serrors" "github.com/scionproto/scion/pkg/slayers/path" + "github.com/scionproto/scion/pkg/slayers/path/scion" ) const ( @@ -23,7 +24,7 @@ type Decoded struct { HopFields []FlyoverHopField } -// DecodeFromBytes fully decodes the SCION path into the corresponding fields. +// DecodeFromBytes fully decodes the Hummingbird path into the corresponding fields. func (s *Decoded) DecodeFromBytes(data []byte) error { if err := s.Base.DecodeFromBytes(data); err != nil { return err @@ -180,3 +181,33 @@ func (s *Decoded) ToRaw() (*Raw, error) { } return raw, nil } + +// Converts a SCiON decoded path to a hummingbird decoded path +// Does NOT perform a deep copy of hop and info fields. +// Does NOT set the PathMeta Timestamps and counter +func (s *Decoded) ConvertFromScionDecoded(d scion.Decoded) { + // convert Base + s.convertBaseFromScion(d.Base) + // transfer Infofields + s.InfoFields = d.InfoFields + // convert HopFields + s.HopFields = make([]FlyoverHopField, d.NumHops) + for i, hop := range d.HopFields { + s.HopFields[i] = FlyoverHopField{ + HopField: hop, + Flyover: false, + } + } +} + +func (s *Decoded) convertBaseFromScion(d scion.Base) { + s.Base.NumINF = d.NumINF + s.Base.PathMeta.CurrINF = d.PathMeta.CurrINF + + s.Base.NumHops = d.NumHops * 3 + s.Base.PathMeta.CurrHF = d.PathMeta.CurrHF * 3 + + s.Base.PathMeta.SegLen[0] = d.PathMeta.SegLen[0] * 3 + s.Base.PathMeta.SegLen[1] = d.PathMeta.SegLen[1] * 3 + s.Base.PathMeta.SegLen[2] = d.PathMeta.SegLen[2] * 3 +} diff --git a/pkg/snet/path/BUILD.bazel b/pkg/snet/path/BUILD.bazel index 351e08c2e3..8d07e84076 100644 --- a/pkg/snet/path/BUILD.bazel +++ b/pkg/snet/path/BUILD.bazel @@ -5,6 +5,7 @@ go_library( srcs = [ "empty.go", "epic.go", + "hummingbird.go", "onehop.go", "path.go", "scion.go", @@ -20,6 +21,7 @@ go_library( "//pkg/slayers/path:go_default_library", "//pkg/slayers/path/empty:go_default_library", "//pkg/slayers/path/epic:go_default_library", + "//pkg/slayers/path/hummingbird:go_default_library", "//pkg/slayers/path/onehop:go_default_library", "//pkg/slayers/path/scion:go_default_library", "//pkg/snet:go_default_library", diff --git a/pkg/snet/path/hummingbird.go b/pkg/snet/path/hummingbird.go new file mode 100644 index 0000000000..00d3b3b9f7 --- /dev/null +++ b/pkg/snet/path/hummingbird.go @@ -0,0 +1,30 @@ +package path + +import ( + "github.com/scionproto/scion/pkg/private/serrors" + "github.com/scionproto/scion/pkg/slayers" + "github.com/scionproto/scion/pkg/slayers/path/hummingbird" +) + +type Hummingbird struct { + // Raw is the raw representation of this path. This data should not be + // modified because it is potentially shared. + Raw []byte +} + +func NewHbirdFromDecoded(d *hummingbird.Decoded) (Hummingbird, error) { + buf := make([]byte, d.Len()) + if err := d.SerializeTo(buf); err != nil { + return Hummingbird{}, serrors.WrapStr("serializing decoded Hummingbird path", err) + } + return Hummingbird{Raw: buf}, nil +} + +func (p Hummingbird) SetPath(s *slayers.SCION) error { + var sp hummingbird.Raw + if err := sp.DecodeFromBytes(p.Raw); err != nil { + return err + } + s.Path, s.PathType = &sp, sp.Type() + return nil +} diff --git a/tools/end2end_hbird/BUILD.bazel b/tools/end2end_hbird/BUILD.bazel new file mode 100644 index 0000000000..6d3c3a2927 --- /dev/null +++ b/tools/end2end_hbird/BUILD.bazel @@ -0,0 +1,34 @@ +load("//tools/lint:go.bzl", "go_library") +load("//:scion.bzl", "scion_go_binary") + +go_library( + name = "go_default_library", + srcs = ["main.go"], + importpath = "github.com/scionproto/scion/tools/end2end_hbird", + visibility = ["//visibility:private"], + deps = [ + "//pkg/addr:go_default_library", + "//pkg/daemon:go_default_library", + "//pkg/hummingbird:go_default_library", + "//pkg/log:go_default_library", + "//pkg/private/common:go_default_library", + "//pkg/private/serrors:go_default_library", + "//pkg/private/util:go_default_library", + "//pkg/snet:go_default_library", + "//pkg/snet/metrics:go_default_library", + "//pkg/snet/path:go_default_library", + "//pkg/sock/reliable:go_default_library", + "//private/topology:go_default_library", + "//private/tracing:go_default_library", + "//tools/integration:go_default_library", + "//tools/integration/integrationlib:go_default_library", + "@com_github_opentracing_opentracing_go//:go_default_library", + "@com_github_opentracing_opentracing_go//ext:go_default_library", + ], +) + +scion_go_binary( + name = "end2end_hbird", + embed = [":go_default_library"], + visibility = ["//visibility:public"], +) diff --git a/tools/end2end_hbird/main.go b/tools/end2end_hbird/main.go new file mode 100644 index 0000000000..9de7043e2b --- /dev/null +++ b/tools/end2end_hbird/main.go @@ -0,0 +1,487 @@ +// Copyright 2018 ETH Zurich +// 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 main + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "flag" + "fmt" + "net" + "net/netip" + "os" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + + "github.com/scionproto/scion/pkg/addr" + "github.com/scionproto/scion/pkg/daemon" + "github.com/scionproto/scion/pkg/hummingbird" + "github.com/scionproto/scion/pkg/log" + "github.com/scionproto/scion/pkg/private/common" + "github.com/scionproto/scion/pkg/private/serrors" + "github.com/scionproto/scion/pkg/private/util" + "github.com/scionproto/scion/pkg/snet" + "github.com/scionproto/scion/pkg/snet/metrics" + snetpath "github.com/scionproto/scion/pkg/snet/path" + "github.com/scionproto/scion/pkg/sock/reliable" + "github.com/scionproto/scion/private/topology" + "github.com/scionproto/scion/private/tracing" + libint "github.com/scionproto/scion/tools/integration" + integration "github.com/scionproto/scion/tools/integration/integrationlib" +) + +const ( + ping = "ping" + pong = "pong" +) + +type Ping struct { + Server addr.IA `json:"server"` + Message string `json:"message"` + Trace []byte `json:"trace"` +} + +type Pong struct { + Client addr.IA `json:"client"` + Server addr.IA `json:"server"` + Message string `json:"message"` + Trace []byte `json:"trace"` +} + +var ( + remote snet.UDPAddr + timeout = &util.DurWrap{Duration: 10 * time.Second} + scionPacketConnMetrics = metrics.NewSCIONPacketConnMetrics() + scmpErrorsCounter = scionPacketConnMetrics.SCMPErrors + epic bool +) + +func main() { + os.Exit(realMain()) +} + +func realMain() int { + defer log.HandlePanic() + defer log.Flush() + addFlags() + err := integration.Setup() + if err != nil { + log.Error("Parsing common flags failed", "err", err) + return 1 + } + validateFlags() + + closeTracer, err := integration.InitTracer("end2end_hbird-" + integration.Mode) + if err != nil { + log.Error("Tracer initialization failed", "err", err) + return 1 + } + defer closeTracer() + if integration.Mode == integration.ModeServer { + server{}.run() + return 0 + } + c := client{} + return c.run() +} + +func addFlags() { + flag.Var(&remote, "remote", "(Mandatory for clients) address to connect to") + flag.Var(timeout, "timeout", "The timeout for each attempt") + flag.BoolVar(&epic, "epic", false, "Enable EPIC.") +} + +func validateFlags() { + if integration.Mode == integration.ModeClient { + if remote.Host == nil { + integration.LogFatal("Missing remote address") + } + if remote.Host.Port == 0 { + integration.LogFatal("Invalid remote port", "remote port", remote.Host.Port) + } + if timeout.Duration == 0 { + integration.LogFatal("Invalid timeout provided", "timeout", timeout) + } + } +} + +type server struct{} + +func (s server) run() { + log.Info("Starting server", "isd_as", integration.Local.IA) + defer log.Info("Finished server", "isd_as", integration.Local.IA) + + sdConn := integration.SDConn() + defer sdConn.Close() + connFactory := &snet.DefaultPacketDispatcherService{ + Dispatcher: reliable.NewDispatcher(""), + SCMPHandler: snet.DefaultSCMPHandler{ + RevocationHandler: daemon.RevHandler{Connector: sdConn}, + SCMPErrors: scmpErrorsCounter, + }, + SCIONPacketConnMetrics: scionPacketConnMetrics, + } + conn, port, err := connFactory.Register(context.Background(), integration.Local.IA, + integration.Local.Host, addr.SvcNone) + if err != nil { + integration.LogFatal("Error listening", "err", err) + } + defer conn.Close() + if len(os.Getenv(libint.GoIntegrationEnv)) > 0 { + // Needed for integration test ready signal. + fmt.Printf("Port=%d\n", port) + fmt.Printf("%s%s\n\n", libint.ReadySignal, integration.Local.IA) + } + log.Info("Listening", "local", fmt.Sprintf("%v:%d", integration.Local.Host, port)) + + // Receive ping message + for { + if err := s.handlePing(conn); err != nil { + log.Error("Error handling ping", "err", err) + } + } +} + +func (s server) handlePing(conn snet.PacketConn) error { + var p snet.Packet + var ov net.UDPAddr + if err := readFrom(conn, &p, &ov); err != nil { + return serrors.WrapStr("reading packet", err) + } + udp, ok := p.Payload.(snet.UDPPayload) + if !ok { + return serrors.New("unexpected payload received", + "source", p.Source, + "destination", p.Destination, + "type", common.TypeOf(p.Payload), + ) + } + var pld Ping + if err := json.Unmarshal(udp.Payload, &pld); err != nil { + return serrors.New("invalid payload contents", + "source", p.Source, + "destination", p.Destination, + "data", string(udp.Payload), + ) + } + + spanCtx, err := opentracing.GlobalTracer().Extract( + opentracing.Binary, + bytes.NewReader(pld.Trace), + ) + if err != nil { + return serrors.WrapStr("extracting trace information", err) + } + span, _ := opentracing.StartSpanFromContext( + context.Background(), + "handle_ping", + ext.RPCServerOption(spanCtx), + ) + defer span.Finish() + withTag := func(err error) error { + tracing.Error(span, err) + return err + } + + if pld.Message != ping || !pld.Server.Equal(integration.Local.IA) { + return withTag(serrors.New("unexpected data in payload", + "source", p.Source, + "destination", p.Destination, + "data", pld, + )) + } + log.Info(fmt.Sprintf("Ping received from %s, sending pong.", p.Source)) + raw, err := json.Marshal(Pong{ + Client: p.Source.IA, + Server: integration.Local.IA, + Message: pong, + Trace: pld.Trace, + }) + if err != nil { + return withTag(serrors.WrapStr("packing pong", err)) + } + + p.Destination, p.Source = p.Source, p.Destination + p.Payload = snet.UDPPayload{ + DstPort: udp.SrcPort, + SrcPort: udp.DstPort, + Payload: raw, + } + // reverse path + rpath, ok := p.Path.(snet.RawPath) + if !ok { + return serrors.New("unecpected path", "type", common.TypeOf(p.Path)) + } + replypather := snet.DefaultReplyPather{} + replyPath, err := replypather.ReplyPath(rpath) + if err != nil { + return serrors.WrapStr("creating reply path", err) + } + p.Path = replyPath + // Send pong + if err := conn.WriteTo(&p, &ov); err != nil { + return withTag(serrors.WrapStr("sending reply", err)) + } + log.Info("Sent pong to", "client", p.Destination) + return nil +} + +type client struct { + conn snet.PacketConn + port uint16 + sdConn daemon.Connector + + errorPaths map[snet.PathFingerprint]struct{} +} + +func (c *client) run() int { + pair := fmt.Sprintf("%s -> %s", integration.Local.IA, remote.IA) + log.Info("Starting", "pair", pair) + defer log.Info("Finished", "pair", pair) + defer integration.Done(integration.Local.IA, remote.IA) + connFactory := &snet.DefaultPacketDispatcherService{ + Dispatcher: reliable.NewDispatcher(""), + SCMPHandler: snet.DefaultSCMPHandler{ + RevocationHandler: daemon.RevHandler{Connector: integration.SDConn()}, + SCMPErrors: scmpErrorsCounter, + }, + SCIONPacketConnMetrics: scionPacketConnMetrics, + } + + var err error + c.conn, c.port, err = connFactory.Register(context.Background(), integration.Local.IA, + integration.Local.Host, addr.SvcNone) + if err != nil { + integration.LogFatal("Unable to listen", "err", err) + } + log.Info("Send on", "local", + fmt.Sprintf("%v,[%v]:%d", integration.Local.IA, integration.Local.Host.IP, c.port)) + c.sdConn = integration.SDConn() + defer c.sdConn.Close() + c.errorPaths = make(map[snet.PathFingerprint]struct{}) + return integration.AttemptRepeatedly("End2End", c.attemptRequest) +} + +func (c *client) attemptRequest(n int) bool { + timeoutCtx, cancel := context.WithTimeout(context.Background(), timeout.Duration) + defer cancel() + span, ctx := tracing.CtxWith(timeoutCtx, "attempt") + span.SetTag("attempt", n) + span.SetTag("src", integration.Local.IA) + span.SetTag("dst", remote.IA) + defer span.Finish() + logger := log.FromCtx(ctx) + + path, err := c.getRemote(ctx, n) + if err != nil { + logger.Error("Could not get remote", "err", err) + return false + } + span, ctx = tracing.StartSpanFromCtx(ctx, "attempt.ping") + defer span.Finish() + + // Convert path to Hummingbird path + if path != nil { + path, err = hummingbird.ConvertToHbirdPath(path) + if err != nil { + logger.Error("Error converting path to Hummingbird", "err", err) + return false + } + remote.Path = path.Dataplane() + } + + // Send ping + if err := c.ping(ctx, n, path); err != nil { + tracing.Error(span, err) + logger.Error("Could not send packet", "err", err) + return false + } + // Receive pong + if err := c.pong(ctx); err != nil { + tracing.Error(span, err) + logger.Error("Error receiving pong", "err", err) + if path != nil { + c.errorPaths[snet.Fingerprint(path)] = struct{}{} + } + return false + } + return true +} + +func (c *client) ping(ctx context.Context, n int, path snet.Path) error { + rawPing, err := json.Marshal(Ping{ + Server: remote.IA, + Message: ping, + Trace: tracing.IDFromCtx(ctx), + }) + if err != nil { + return serrors.WrapStr("packing ping", err) + } + if err := c.conn.SetWriteDeadline(getDeadline(ctx)); err != nil { + return serrors.WrapStr("setting write deadline", err) + } + if remote.NextHop == nil { + remote.NextHop = &net.UDPAddr{ + IP: remote.Host.IP, + Port: topology.EndhostPort, + } + } + + remoteHostIP, ok := netip.AddrFromSlice(remote.Host.IP) + if !ok { + return serrors.New("invalid remote host IP", "ip", remote.Host.IP) + } + localHostIP, ok := netip.AddrFromSlice(integration.Local.Host.IP) + if !ok { + return serrors.New("invalid local host IP", "ip", integration.Local.Host.IP) + } + pkt := &snet.Packet{ + PacketInfo: snet.PacketInfo{ + Destination: snet.SCIONAddress{ + IA: remote.IA, + Host: addr.HostIP(remoteHostIP), + }, + Source: snet.SCIONAddress{ + IA: integration.Local.IA, + Host: addr.HostIP(localHostIP), + }, + Path: remote.Path, + Payload: snet.UDPPayload{ + SrcPort: c.port, + DstPort: uint16(remote.Host.Port), + Payload: rawPing, + }, + }, + } + log.Info("sending ping", "attempt", n, "path", path) + if err := c.conn.WriteTo(pkt, remote.NextHop); err != nil { + return err + } + return nil +} + +func (c *client) getRemote(ctx context.Context, n int) (snet.Path, error) { + if remote.IA.Equal(integration.Local.IA) { + remote.Path = snetpath.Empty{} + return nil, nil + } + span, ctx := tracing.StartSpanFromCtx(ctx, "attempt.get_remote") + defer span.Finish() + withTag := func(err error) error { + tracing.Error(span, err) + return err + } + + paths, err := c.sdConn.Paths(ctx, remote.IA, integration.Local.IA, + daemon.PathReqFlags{Refresh: n != 0}) + if err != nil { + return nil, withTag(serrors.WrapStr("requesting paths", err)) + } + // If all paths had an error, let's try them again. + if len(paths) <= len(c.errorPaths) { + c.errorPaths = make(map[snet.PathFingerprint]struct{}) + } + // Select first path that didn't error before. + var path snet.Path + for _, p := range paths { + if _, ok := c.errorPaths[snet.Fingerprint(p)]; ok { + continue + } + path = p + break + } + if path == nil { + return nil, withTag(serrors.New("no path found", + "candidates", len(paths), + "errors", len(c.errorPaths), + )) + } + + // Extract forwarding path from the SCION Daemon response. + // If the epic flag is set, try to use the EPIC path type header. + if epic { + scionPath, ok := path.Dataplane().(snetpath.SCION) + if !ok { + return nil, serrors.New("provided path must be of type scion") + } + epicPath, err := snetpath.NewEPICDataplanePath(scionPath, path.Metadata().EpicAuths) + if err != nil { + return nil, err + } + remote.Path = epicPath + } else { + remote.Path = path.Dataplane() + } + remote.NextHop = path.UnderlayNextHop() + return path, nil +} + +func (c *client) pong(ctx context.Context) error { + if err := c.conn.SetReadDeadline(getDeadline(ctx)); err != nil { + return serrors.WrapStr("setting read deadline", err) + } + var p snet.Packet + var ov net.UDPAddr + if err := readFrom(c.conn, &p, &ov); err != nil { + return serrors.WrapStr("reading packet", err) + } + + udp, ok := p.Payload.(snet.UDPPayload) + if !ok { + return serrors.New("unexpected payload received", "type", common.TypeOf(p.Payload)) + } + + var pld Pong + if err := json.Unmarshal(udp.Payload, &pld); err != nil { + return serrors.WrapStr("unpacking pong", err, "data", string(udp.Payload)) + } + + expected := Pong{ + Client: integration.Local.IA, + Server: remote.IA, + Message: pong, + } + if pld.Client != expected.Client || pld.Server != expected.Server || pld.Message != pong { + return serrors.New("unexpected contents received", "data", pld, "expected", expected) + } + log.Info("Received pong", "server", p.Source) + return nil +} + +func getDeadline(ctx context.Context) time.Time { + dl, ok := ctx.Deadline() + if !ok { + integration.LogFatal("No deadline in context") + } + return dl +} + +func readFrom(conn snet.PacketConn, pkt *snet.Packet, ov *net.UDPAddr) error { + err := conn.ReadFrom(pkt, ov) + // Attach more context to error + var opErr *snet.OpError + if !(errors.As(err, &opErr) && opErr.RevInfo() != nil) { + return err + } + return serrors.WithCtx(err, + "isd_as", opErr.RevInfo().IA(), + "interface", opErr.RevInfo().IfID, + ) +} diff --git a/tools/end2end_hbird_integration/BUILD.bazel b/tools/end2end_hbird_integration/BUILD.bazel new file mode 100644 index 0000000000..34ab53645b --- /dev/null +++ b/tools/end2end_hbird_integration/BUILD.bazel @@ -0,0 +1,24 @@ +load("//tools/lint:go.bzl", "go_library") +load("//:scion.bzl", "scion_go_binary") + +go_library( + name = "go_default_library", + srcs = ["main.go"], + importpath = "github.com/scionproto/scion/tools/end2end_hbird_integration", + visibility = ["//visibility:private"], + deps = [ + "//pkg/addr:go_default_library", + "//pkg/log:go_default_library", + "//pkg/private/serrors:go_default_library", + "//pkg/private/util:go_default_library", + "//pkg/snet:go_default_library", + "//private/app/feature:go_default_library", + "//tools/integration:go_default_library", + ], +) + +scion_go_binary( + name = "end2end_hbird_integration", + embed = [":go_default_library"], + visibility = ["//visibility:public"], +) \ No newline at end of file diff --git a/tools/end2end_hbird_integration/main.go b/tools/end2end_hbird_integration/main.go new file mode 100644 index 0000000000..f106c34767 --- /dev/null +++ b/tools/end2end_hbird_integration/main.go @@ -0,0 +1,364 @@ +// Copyright 2018 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 main + +import ( + "context" + "flag" + "fmt" + "os" + "path/filepath" + "strconv" + "strings" + "sync" + "time" + + "github.com/scionproto/scion/pkg/addr" + "github.com/scionproto/scion/pkg/log" + "github.com/scionproto/scion/pkg/private/serrors" + "github.com/scionproto/scion/pkg/private/util" + "github.com/scionproto/scion/pkg/snet" + "github.com/scionproto/scion/private/app/feature" + "github.com/scionproto/scion/tools/integration" +) + +var ( + subset string + attempts int + timeout = &util.DurWrap{Duration: 10 * time.Second} + parallelism int + name string + cmd string + features string + epic bool +) + +func getCmd() (string, bool) { + return cmd, strings.Contains(cmd, "end2end_hbird") +} + +func main() { + os.Exit(realMain()) +} + +func realMain() int { + addFlags() + if err := integration.Init(); err != nil { + fmt.Fprintf(os.Stderr, "Failed to init: %s\n", err) + return 1 + } + defer log.HandlePanic() + defer log.Flush() + if len(features) != 0 { + if _, err := feature.ParseDefault(strings.Split(features, ",")); err != nil { + fmt.Fprintf(os.Stderr, "Error parsing features: %s\n", err) + return 1 + } + } + + clientArgs := []string{ + "-log.console", "debug", + "-attempts", strconv.Itoa(attempts), + "-timeout", timeout.String(), + "-local", integration.SrcAddrPattern + ":0", + "-remote", integration.DstAddrPattern + ":" + integration.ServerPortReplace, + fmt.Sprintf("-epic=%t", epic), + } + serverArgs := []string{ + "-mode", "server", + "-local", integration.DstAddrPattern + ":0", + } + if len(features) != 0 { + clientArgs = append(clientArgs, "--features", features) + serverArgs = append(serverArgs, "--features", features) + } + if !*integration.Docker { + clientArgs = append(clientArgs, "-sciond", integration.Daemon) + serverArgs = append(serverArgs, "-sciond", integration.Daemon) + } + + in := integration.NewBinaryIntegration(name, cmd, clientArgs, serverArgs) + pairs, err := getPairs() + if err != nil { + log.Error("Error selecting tests", "err", err) + return 1 + } + if err := runTests(in, pairs); err != nil { + log.Error("Error during tests", "err", err) + return 1 + } + return 0 +} + +// addFlags adds the necessary flags. +func addFlags() { + flag.IntVar(&attempts, "attempts", 1, "Number of attempts per client before giving up.") + flag.StringVar(&cmd, "cmd", "./bin/end2end_hbird", + "The end2end binary to run (default: ./bin/end2end_hbird)") + flag.StringVar(&name, "name", "end2end_hbird_integration", + "The name of the test that is running (default: end2end_hbird_integration)") + flag.Var(timeout, "timeout", "The timeout for each attempt") + flag.StringVar(&subset, "subset", "all", "Subset of pairs to run (all|core#core|"+ + "noncore#localcore|noncore#core|noncore#noncore)") + flag.IntVar(¶llelism, "parallelism", 1, "How many end2end tests run in parallel.") + flag.StringVar(&features, "features", "", + fmt.Sprintf("enable development features (%v)", feature.String(&feature.Default{}, "|"))) + flag.BoolVar(&epic, "epic", false, "Enable EPIC.") +} + +// runTests runs the end2end tests for all pairs. In case of an error the +// function is terminated immediately. +func runTests(in integration.Integration, pairs []integration.IAPair) error { + return integration.ExecuteTimed(in.Name(), func() error { + // Make sure that all executed commands can write to the RPC server + // after shutdown. + defer time.Sleep(time.Second) + + // Estimating the timeout we should have is hard. CI will abort after 10 + // minutes anyway. Thus this value. + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) + defer cancel() + + // First run all servers + type srvResult struct { + cleaner func() + err error + } + // Start servers in parallel. + srvResults := make(chan srvResult) + for _, dst := range integration.ExtractUniqueDsts(pairs) { + go func(dst *snet.UDPAddr) { + defer log.HandlePanic() + + srvCtx, cancel := context.WithCancel(ctx) + waiter, err := in.StartServer(srvCtx, dst) + if err != nil { + log.Error(fmt.Sprintf("Error in server: %s", dst.String()), "err", err) + } + cleaner := func() { + cancel() + if waiter != nil { + _ = waiter.Wait() + } + } + srvResults <- srvResult{cleaner: cleaner, err: err} + }(dst) + } + // Wait for all servers being started. + var errs serrors.List + for range integration.ExtractUniqueDsts(pairs) { + res := <-srvResults + // We need to register a cleanup for all servers. + // Do not short-cut exit here. + if res.err != nil { + errs = append(errs, res.err) + } + defer res.cleaner() + } + if err := errs.ToError(); err != nil { + return err + } + + // Start a done signal listener. This is how the end2end binary + // communicates with this integration test. This is solely used to print + // the progress of the test. + var ctrMtx sync.Mutex + var ctr int + doneDir, err := filepath.Abs(filepath.Join(integration.LogDir(), "socks")) + if err != nil { + return serrors.WrapStr("determining abs path", err) + } + if err := os.MkdirAll(doneDir, os.ModePerm); err != nil { + return serrors.WrapStr("creating socks directory", err) + } + // this is a bit of a hack, socket file names have a max length of 108 + // and inside bazel tests we easily have longer paths, therefore we + // create a temporary symlink to the directory where we put the socket + // file. + tmpDir, err := os.MkdirTemp("", "e2e_integration") + if err != nil { + return serrors.WrapStr("creating temp dir", err) + } + if err := os.Remove(tmpDir); err != nil { + return serrors.WrapStr("deleting temp dir", err) + } + if err := os.Symlink(doneDir, tmpDir); err != nil { + return serrors.WrapStr("symlinking socks dir", err) + } + doneDir = tmpDir + defer os.Remove(doneDir) + socket, clean, err := integration.ListenDone(doneDir, func(src, dst addr.IA) { + ctrMtx.Lock() + defer ctrMtx.Unlock() + ctr++ + testInfo := fmt.Sprintf("%v -> %v (%v/%v)", src, dst, ctr, len(pairs)) + log.Info(fmt.Sprintf("Test %v: %s", in.Name(), testInfo)) + }) + if err != nil { + return serrors.WrapStr("creating done listener", err) + } + defer clean() + + if *integration.Docker { + socket = strings.Replace(socket, doneDir, "/share/logs/socks", -1) + } + + // CI collapses if parallelism is too high. + semaphore := make(chan struct{}, parallelism) + + // Docker exec comes with a 1 second overhead. We group all the pairs by + // the clients. And run all pairs for a given client in one execution. + // Thus, reducing the overhead dramatically. + groups := integration.GroupBySource(pairs) + clientResults := make(chan error, len(groups)) + for src, dsts := range groups { + go func(src *snet.UDPAddr, dsts []*snet.UDPAddr) { + defer log.HandlePanic() + + semaphore <- struct{}{} + defer func() { <-semaphore }() + // Aggregate all the commands that need to be run. + cmds := make([]integration.Cmd, 0, len(dsts)) + for _, dst := range dsts { + cmd, err := clientTemplate(socket).Template(src, dst) + if err != nil { + clientResults <- err + return + } + cmds = append(cmds, cmd) + } + var tester string + if *integration.Docker { + tester = integration.TesterID(src) + } + logFile := fmt.Sprintf("%s/client_%s.log", + logDir(), + addr.FormatIA(src.IA, addr.WithFileSeparator()), + ) + err := integration.Run(ctx, integration.RunConfig{ + Commands: cmds, + LogFile: logFile, + Tester: tester, + }) + if err != nil { + err = serrors.WithCtx(err, "file", relFile(logFile)) + } + clientResults <- err + }(src, dsts) + } + errs = nil + for range groups { + err := <-clientResults + if err != nil { + errs = append(errs, err) + } + } + return errs.ToError() + }) +} + +func clientTemplate(progressSock string) integration.Cmd { + bin, progress := getCmd() + cmd := integration.Cmd{ + Binary: bin, + Args: []string{ + "-log.console", "debug", + "-attempts", strconv.Itoa(attempts), + "-timeout", timeout.String(), + "-local", integration.SrcAddrPattern + ":0", + "-remote", integration.DstAddrPattern + ":" + integration.ServerPortReplace, + fmt.Sprintf("-epic=%t", epic), + }, + } + if len(features) != 0 { + cmd.Args = append(cmd.Args, "--features", features) + } + if progress { + cmd.Args = append(cmd.Args, "-progress", progressSock) + } + if !*integration.Docker { + cmd.Args = append(cmd.Args, "-sciond", integration.Daemon) + } + return cmd +} + +// getPairs returns the pairs to test according to the specified subset. +func getPairs() ([]integration.IAPair, error) { + pairs := integration.IAPairs(integration.DispAddr) + if subset == "all" { + return pairs, nil + } + parts := strings.Split(subset, "#") + if len(parts) != 2 { + return nil, serrors.New("Invalid subset", "subset", subset) + } + return filter(parts[0], parts[1], pairs, integration.LoadedASList), nil +} + +// filter returns the list of ASes that are part of the desired subset. +func filter( + src, dst string, + pairs []integration.IAPair, + ases *integration.ASList, +) []integration.IAPair { + + var res []integration.IAPair + s, err1 := addr.ParseIA(src) + d, err2 := addr.ParseIA(dst) + if err1 == nil && err2 == nil { + for _, pair := range pairs { + if pair.Src.IA.Equal(s) && pair.Dst.IA.Equal(d) { + res = append(res, pair) + return res + } + } + } + for _, pair := range pairs { + filter := !contains(ases, src != "noncore", pair.Src.IA) + filter = filter || !contains(ases, dst != "noncore", pair.Dst.IA) + if dst == "localcore" { + filter = filter || pair.Src.IA.ISD() != pair.Dst.IA.ISD() + } + if !filter { + res = append(res, pair) + } + } + return res +} + +func contains(ases *integration.ASList, core bool, ia addr.IA) bool { + l := ases.Core + if !core { + l = ases.NonCore + } + for _, as := range l { + if ia.Equal(as) { + return true + } + } + return false +} + +func logDir() string { + return filepath.Join(integration.LogDir(), name) +} + +func relFile(file string) string { + rel, err := filepath.Rel(filepath.Dir(integration.LogDir()), file) + if err != nil { + return file + } + return rel +} From aab07e3f366c8149c7bcc9fa12c4ba89aabbf693 Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Tue, 31 Oct 2023 13:37:31 +0100 Subject: [PATCH 007/100] fixed peering flag causing segfaults --- router/dataplane_hbird.go | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/router/dataplane_hbird.go b/router/dataplane_hbird.go index 1199ea0a33..962719a56e 100644 --- a/router/dataplane_hbird.go +++ b/router/dataplane_hbird.go @@ -118,8 +118,8 @@ func (p *scionPacketProcessor) verifyCurrentHbirdMAC() (processResult, error) { "%x", scionMac[:path.MacLen]), "actual", fmt.Sprintf("%x", p.hopField.Mac[:path.MacLen]), "cons_dir", p.infoField.ConsDir, - "if_id", p.ingressID, "curr_inf", p.path.PathMeta.CurrINF, - "curr_hf", p.path.PathMeta.CurrHF, "seg_id", p.infoField.SegID) + "if_id", p.ingressID, "curr_inf", p.hbirdPath.PathMeta.CurrINF, + "curr_hf", p.hbirdPath.PathMeta.CurrHF, "seg_id", p.infoField.SegID) } } // Add the full MAC to the SCION packet processor, @@ -163,6 +163,26 @@ func (p *scionPacketProcessor) validateHbirdSrcDstIA() (processResult, error) { return processResult{}, nil } +func (p *scionPacketProcessor) ingressInterfaceHbird() uint16 { + info := p.infoField + hop := p.flyoverField + if !p.peering && p.hbirdPath.IsFirstHopAfterXover() { + var err error + info, err = p.hbirdPath.GetInfoField(int(p.hbirdPath.PathMeta.CurrINF) - 1) + if err != nil { // cannot be out of range + panic(err) + } + hop, err = p.hbirdPath.GetHopField(int(p.hbirdPath.PathMeta.CurrHF) - 3) + if err != nil { // cannot be out of range + panic(err) + } + } + if info.ConsDir { + return hop.HopField.ConsIngress + } + return hop.HopField.ConsEgress +} + // validateTransitUnderlaySrc checks that the source address of transit packets // matches the expected sibling router. // Provided that underlying network infrastructure prevents address spoofing, @@ -173,7 +193,7 @@ func (p *scionPacketProcessor) validateHbirdTransitUnderlaySrc() (processResult, // not a transit packet, nothing to check return processResult{}, nil } - pktIngressID := p.ingressInterface() + pktIngressID := p.ingressInterfaceHbird() expectedSrc, ok := p.d.internalNextHops[pktIngressID] if !ok || !expectedSrc.IP.Equal(p.srcAddr.IP) { // Drop @@ -340,14 +360,8 @@ func (p *scionPacketProcessor) processHBIRD() (processResult, error) { if err := p.updateHbirdNonConsDirIngressSegID(); err != nil { return processResult{}, err } - if p.flyoverField.Flyover { - if r, err := p.verifyCurrentHbirdMAC(); err != nil { - return r, err - } - } else { - if r, err := p.verifyCurrentMAC(); err != nil { - return r, err - } + if r, err := p.verifyCurrentHbirdMAC(); err != nil { + return r, err } if p.hasPriority && p.flyoverField.Flyover { // TODO: current implementation (which is in line with design) allows for an attack surface where packets with outdated TSs bypass bw check but claim priority to next router From 855f997de16e36352465dc31f424b38103da4f16 Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Wed, 1 Nov 2023 16:14:04 +0100 Subject: [PATCH 008/100] moved call of updateHbirdNonConsDirIngressSegID into mac verification as it necessites deaggregated mac, added additional log output for mac verification failure --- router/dataplane_hbird.go | 99 +++++++++++++++++++++++++++++++--- router/dataplane_hbird_test.go | 3 ++ 2 files changed, 94 insertions(+), 8 deletions(-) diff --git a/router/dataplane_hbird.go b/router/dataplane_hbird.go index 962719a56e..4d4c1c3f59 100644 --- a/router/dataplane_hbird.go +++ b/router/dataplane_hbird.go @@ -91,16 +91,40 @@ func (p *scionPacketProcessor) currentHbirdHopPointer() uint16 { } func (p *scionPacketProcessor) verifyCurrentHbirdMAC() (processResult, error) { - scionMac := path.FullMAC(p.mac, p.infoField, p.hopField, p.macInputBuffer[:path.MACBufferSize]) - + var flyoverMac []byte var verified int + + // Compute flyovermac if p.flyoverField.Flyover { ak := hummingbird.DeriveAuthKey(p.prf, p.flyoverField.ResID, p.flyoverField.Bw, p.hopField.ConsIngress, p.hopField.ConsEgress, p.hbirdPath.PathMeta.BaseTS-uint32(p.flyoverField.ResStartTime), p.flyoverField.Duration, p.macInputBuffer[path.MACBufferSize+hummingbird.FlyoverMacBufferSize:]) - flyoverMac := hummingbird.FullFlyoverMac(ak, p.scionLayer.DstIA, p.scionLayer.PayloadLen, p.flyoverField.ResStartTime, + flyoverMac = hummingbird.FullFlyoverMac(ak, p.scionLayer.DstIA, p.scionLayer.PayloadLen, p.flyoverField.ResStartTime, p.hbirdPath.PathMeta.HighResTS, p.macInputBuffer[path.MACBufferSize:], p.hbirdXkbuffer) - // Xor to Aggregate MACs + } + // Perform updateHbirdNonConsDirIngressSegID + // This needs de-aggregated MAC but needs to be done before scionMac is computed + // Therefore, we check this here instead of before MAC computation like in standard SCiON + if p.flyoverField.Flyover { + // de-aggregate first two bytes of mac + p.hopField.Mac[0] ^= flyoverMac[0] + p.hopField.Mac[1] ^= flyoverMac[1] + err := p.updateHbirdNonConsDirIngressSegID() + // restore correct state of MAC field, even if error + p.hopField.Mac[0] ^= flyoverMac[0] + p.hopField.Mac[1] ^= flyoverMac[1] + if err != nil { + return processResult{}, err + } + } else { + if err := p.updateHbirdNonConsDirIngressSegID(); err != nil { + return processResult{}, err + } + } + // Compute scionMac + scionMac := path.FullMAC(p.mac, p.infoField, p.hopField, p.macInputBuffer[:path.MACBufferSize]) + // Aggregate MACS and verify if necessary + if p.flyoverField.Flyover { binary.BigEndian.PutUint64(flyoverMac[0:8], binary.BigEndian.Uint64(scionMac[0:8])^binary.BigEndian.Uint64(flyoverMac[0:8])) binary.BigEndian.PutUint32(flyoverMac[8:12], binary.BigEndian.Uint32(scionMac[8:12])^binary.BigEndian.Uint32(flyoverMac[8:12])) @@ -108,8 +132,13 @@ func (p *scionPacketProcessor) verifyCurrentHbirdMAC() (processResult, error) { if verified == 0 { log.Debug("SCMP: Aggregate MAC verification failed", "expected", fmt.Sprintf("%x", flyoverMac[:path.MacLen]), "actual", fmt.Sprintf("%x", p.hopField.Mac[:path.MacLen]), "cons_dir", p.infoField.ConsDir, + "scionMac", fmt.Sprintf("%x", scionMac[:path.MacLen]), "if_id", p.ingressID, "curr_inf", p.hbirdPath.PathMeta.CurrINF, - "curr_hf", p.hbirdPath.PathMeta.CurrHF, "seg_id", p.infoField.SegID) + "curr_hf", p.hbirdPath.PathMeta.CurrHF, "seg_id", p.infoField.SegID, "packet length", p.scionLayer.PayloadLen, + "dest", p.scionLayer.DstIA, "startTime", p.flyoverField.ResStartTime, "highResTS", p.hbirdPath.PathMeta.HighResTS, + "ResID", p.flyoverField.ResID, "Bw", p.flyoverField.Bw, "in", p.hopField.ConsIngress, + "Eg", p.hopField.ConsEgress, "start ak", p.hbirdPath.PathMeta.BaseTS-uint32(p.flyoverField.ResStartTime), + "Duration", p.flyoverField.Duration) } } else { verified = subtle.ConstantTimeCompare(p.hopField.Mac[:path.MacLen], scionMac[:path.MacLen]) @@ -137,6 +166,58 @@ func (p *scionPacketProcessor) verifyCurrentHbirdMAC() (processResult, error) { return processResult{}, nil } +// func (p *scionPacketProcessor) verifyCurrentHbirdMAC() (processResult, error) { +// scionMac := path.FullMAC(p.mac, p.infoField, p.hopField, p.macInputBuffer[:path.MACBufferSize]) + +// var verified int +// if p.flyoverField.Flyover { +// ak := hummingbird.DeriveAuthKey(p.prf, p.flyoverField.ResID, p.flyoverField.Bw, p.hopField.ConsIngress, p.hopField.ConsEgress, +// p.hbirdPath.PathMeta.BaseTS-uint32(p.flyoverField.ResStartTime), p.flyoverField.Duration, +// p.macInputBuffer[path.MACBufferSize+hummingbird.FlyoverMacBufferSize:]) +// flyoverMac := hummingbird.FullFlyoverMac(ak, p.scionLayer.DstIA, p.scionLayer.PayloadLen, p.flyoverField.ResStartTime, +// p.hbirdPath.PathMeta.HighResTS, p.macInputBuffer[path.MACBufferSize:], p.hbirdXkbuffer) +// // Xor to Aggregate MACs +// binary.BigEndian.PutUint64(flyoverMac[0:8], binary.BigEndian.Uint64(scionMac[0:8])^binary.BigEndian.Uint64(flyoverMac[0:8])) +// binary.BigEndian.PutUint32(flyoverMac[8:12], binary.BigEndian.Uint32(scionMac[8:12])^binary.BigEndian.Uint32(flyoverMac[8:12])) + +// verified = subtle.ConstantTimeCompare(p.hopField.Mac[:path.MacLen], flyoverMac[:path.MacLen]) +// if verified == 0 { +// log.Debug("SCMP: Aggregate MAC verification failed", "expected", fmt.Sprintf("%x", flyoverMac[:path.MacLen]), +// "actual", fmt.Sprintf("%x", p.hopField.Mac[:path.MacLen]), "cons_dir", p.infoField.ConsDir, +// "scionMac", fmt.Sprintf("%x", scionMac[:path.MacLen]), +// "if_id", p.ingressID, "curr_inf", p.hbirdPath.PathMeta.CurrINF, +// "curr_hf", p.hbirdPath.PathMeta.CurrHF, "seg_id", p.infoField.SegID, "packet length", p.scionLayer.PayloadLen, +// "dest", p.scionLayer.DstIA, "startTime", p.flyoverField.ResStartTime, "highResTS", p.hbirdPath.PathMeta.HighResTS, +// "ResID", p.flyoverField.ResID, "Bw", p.flyoverField.Bw, "in", p.hopField.ConsIngress, +// "Eg", p.hopField.ConsEgress, "start ak", p.hbirdPath.PathMeta.BaseTS-uint32(p.flyoverField.ResStartTime), +// "Duration", p.flyoverField.Duration) +// } +// } else { +// verified = subtle.ConstantTimeCompare(p.hopField.Mac[:path.MacLen], scionMac[:path.MacLen]) +// if verified == 0 { +// log.Debug("SCMP: MAC verification failed", "expected", fmt.Sprintf( +// "%x", scionMac[:path.MacLen]), +// "actual", fmt.Sprintf("%x", p.hopField.Mac[:path.MacLen]), +// "cons_dir", p.infoField.ConsDir, +// "if_id", p.ingressID, "curr_inf", p.hbirdPath.PathMeta.CurrINF, +// "curr_hf", p.hbirdPath.PathMeta.CurrHF, "seg_id", p.infoField.SegID) +// } +// } +// // Add the full MAC to the SCION packet processor, +// // such that EPIC and hummingbird mac de-aggregation do not need to recalculate it. +// p.cachedMac = scionMac +// if verified == 0 { +// slowPathRequest := slowPathRequest{ +// scmpType: slayers.SCMPTypeParameterProblem, +// code: slayers.SCMPCodeInvalidHopFieldMAC, +// pointer: p.currentHopPointer(), +// cause: macVerificationFailed, +// } +// return processResult{SlowPathRequest: slowPathRequest}, slowPathRequired +// } +// return processResult{}, nil +// } + func (p *scionPacketProcessor) validateHbirdSrcDstIA() (processResult, error) { srcIsLocal := (p.scionLayer.SrcIA == p.d.localIA) dstIsLocal := (p.scionLayer.DstIA == p.d.localIA) @@ -172,6 +253,7 @@ func (p *scionPacketProcessor) ingressInterfaceHbird() uint16 { if err != nil { // cannot be out of range panic(err) } + // Previous hop should always be a non-flyover field, as flyover is transferred to second hop on xover hop, err = p.hbirdPath.GetHopField(int(p.hbirdPath.PathMeta.CurrHF) - 3) if err != nil { // cannot be out of range panic(err) @@ -329,6 +411,7 @@ func (p *scionPacketProcessor) processHbirdEgress() error { func (p *scionPacketProcessor) processHBIRD() (processResult, error) { var ok bool p.hbirdPath, ok = p.scionLayer.Path.(*hummingbird.Raw) + log.Debug("raw path", "path", fmt.Sprintf("%x", p.hbirdPath.Raw)) if !ok { // TODO(lukedirtwalker) parameter problem invalid path? return processResult{}, malformedPath @@ -357,9 +440,9 @@ func (p *scionPacketProcessor) processHBIRD() (processResult, error) { return r, err } } - if err := p.updateHbirdNonConsDirIngressSegID(); err != nil { - return processResult{}, err - } + // if err := p.updateHbirdNonConsDirIngressSegID(); err != nil { + // return processResult{}, err + // } if r, err := p.verifyCurrentHbirdMAC(); err != nil { return r, err } diff --git a/router/dataplane_hbird_test.go b/router/dataplane_hbird_test.go index fc1c58617f..8bab7a8d72 100644 --- a/router/dataplane_hbird_test.go +++ b/router/dataplane_hbird_test.go @@ -518,6 +518,9 @@ func TestProcessHbirdPacket(t *testing.T) { for name, tc := range testCases { name, tc := name, tc + if name != "astransit xover flyover" { + continue + } t.Run(name, func(t *testing.T) { t.Parallel() dp := tc.prepareDP(ctrl) From e7af14d8fa14e98dff8abb814eca0bdddcf1c51a Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Wed, 1 Nov 2023 16:15:03 +0100 Subject: [PATCH 009/100] added successfull partial reserved path integration test --- pkg/hummingbird/BUILD.bazel | 3 + pkg/hummingbird/hummingbird.go | 247 ++++++++++++++++++- pkg/slayers/path/hummingbird/decoded.go | 11 + pkg/slayers/path/hummingbird/decoded_test.go | 5 +- tools/end2end_hbird/main.go | 66 ++++- 5 files changed, 321 insertions(+), 11 deletions(-) diff --git a/pkg/hummingbird/BUILD.bazel b/pkg/hummingbird/BUILD.bazel index 15d4f828a3..08a2c48b55 100644 --- a/pkg/hummingbird/BUILD.bazel +++ b/pkg/hummingbird/BUILD.bazel @@ -9,11 +9,14 @@ go_library( visibility = ["//visibility:public"], deps = [ "//pkg/addr:go_default_library", + "//pkg/log:go_default_library", "//pkg/private/serrors:go_default_library", "//pkg/slayers/path/hummingbird:go_default_library", "//pkg/slayers/path/scion:go_default_library", "//pkg/snet:go_default_library", "//pkg/snet/path:go_default_library", + "//private/keyconf:go_default_library", + "//router/control:go_default_library", ], ) diff --git a/pkg/hummingbird/hummingbird.go b/pkg/hummingbird/hummingbird.go index d849ea3276..a3155a21ca 100644 --- a/pkg/hummingbird/hummingbird.go +++ b/pkg/hummingbird/hummingbird.go @@ -1,12 +1,22 @@ package hummingbird import ( + "crypto/aes" + "encoding/binary" + "fmt" + "math/rand" + "strings" + "time" + "github.com/scionproto/scion/pkg/addr" + "github.com/scionproto/scion/pkg/log" "github.com/scionproto/scion/pkg/private/serrors" "github.com/scionproto/scion/pkg/slayers/path/hummingbird" "github.com/scionproto/scion/pkg/slayers/path/scion" "github.com/scionproto/scion/pkg/snet" snetpath "github.com/scionproto/scion/pkg/snet/path" + "github.com/scionproto/scion/private/keyconf" + "github.com/scionproto/scion/router/control" ) type Reservation struct { @@ -30,6 +40,35 @@ type Reservation struct { Egress uint16 } +// Temporary cheating function until the system to request keys is available +// return true if successfull +func cheat_auth_key(res *Reservation) (Reservation, error) { + // ResID is set by seller, pick random + res.ResID = uint32(rand.Int31() >> 10) + + asstr := res.AS.String() + asstr = strings.ReplaceAll(asstr, ":", "_") + asstr = strings.TrimLeft(asstr, "1234567890-") + fpath := "gen/AS" + asstr + "/keys" + mkeys, err := keyconf.LoadMaster(fpath) + if err != nil { + return *res, err + } + key0 := control.DeriveHFMacKey(mkeys.Key0) + prf, _ := aes.NewCipher(key0) + buffer := make([]byte, 16) + + log.Debug("Computing AK", "ResID", res.ResID, "bw", res.Bw, "ingress", res.Ingress, "Egress", res.Egress, "start", res.StartTime, "Duration", res.Duration) + ak := hummingbird.DeriveAuthKey(prf, res.ResID, res.Bw, res.Ingress, res.Egress, res.StartTime, res.Duration, buffer) + copy(res.Ak[:], ak[0:16]) + return *res, nil +} + +// Requests a reservation for each given reservation. Expects AS, Bw, StartTime, EndTime, Ingress and Egress to be filled in +func RequestReservations(rs []Reservation) { + +} + // Adds a reservation to be used for transmission func AddReservation(res Reservation) error { return nil @@ -51,7 +90,14 @@ func ConvertToHbirdPath(p snet.Path) (snet.Path, error) { if err != nil { return nil, err } - dpath.Raw = hbird.Raw + // update dataplane path + switch v := p.(type) { + case snetpath.Path: + v.DataplanePath = hbird + p = v + default: + return nil, serrors.New("Unsupported snet path struct", "path", p) + } return p, nil } @@ -64,3 +110,202 @@ func convertSCIONToHbirdDecoded(p []byte) hummingbird.Decoded { hbirdDec.ConvertFromScionDecoded(scionDec) return hbirdDec } + +type HummingbirdClient struct { + // caches a decoded path for multiple uses + dec hummingbird.Decoded + // Destination of the path + dest addr.IA + // caches the list of ASes on path + ases []addr.IA + // Cached scion MACs for each hop + macs [][6]byte + //TODO: replace by db + reservations []Reservation + // counter for duplicate detection + counter uint32 + // buffers for computing Vk + byteBuffer [16]byte + xkbuffer [44]uint32 +} + +func (c *HummingbirdClient) PrepareHbirdPath(p snet.Path) error { + if p == nil { + return serrors.New("Empty path") + } + c.dec = hummingbird.Decoded{} + switch v := p.Dataplane().(type) { + case snetpath.SCION: + // Convert path to decoded hbird path + scionDec := scion.Decoded{} + scionDec.DecodeFromBytes(v.Raw) + for _, hop := range scionDec.HopFields { + log.Debug("Decoded Scion", "mac", fmt.Sprintf("%x", hop.Mac[:])) + } + c.dec.ConvertFromScionDecoded(scionDec) + case snetpath.Hummingbird: + c.dec.DecodeFromBytes(v.Raw) + default: + return serrors.New("Unsupported path type") + } + // Parse the list of ASes on path + c.ases = make([]addr.IA, len(p.Metadata().Interfaces)) + for i, ia := range p.Metadata().Interfaces { + c.ases[i] = ia.IA + } + c.dest = c.ases[len(c.ases)-1] + // cache Scion Hopfield macs + c.macs = make([][6]byte, len(c.dec.HopFields)) + for i, hop := range c.dec.HopFields { + copy(c.macs[i][:], hop.HopField.Mac[:]) + log.Debug("Scion MAC in prepare", "c.macs i", fmt.Sprintf("%x", c.macs[i])) + } + // prepare reservations data structure + c.reservations = make([]Reservation, len(c.dec.HopFields)) + return nil +} + +func (c *HummingbirdClient) RequestReservationsAllHops(bw uint16, start uint32, duration uint16) error { + inf := 0 + currseg := 0 + for i := range c.dec.HopFields { + res := Reservation{ + AS: c.ases[i], + Bw: bw, + StartTime: start, + Duration: duration, + Ingress: c.dec.HopFields[i].HopField.ConsIngress, + Egress: c.dec.HopFields[i].HopField.ConsEgress, + } + var err error + c.reservations[i], err = cheat_auth_key(&res) + if err != nil { + return err + } + // set flyover to true and adapt metahdr + c.dec.HopFields[i].Flyover = true + c.dec.NumHops += 2 + c.dec.PathMeta.SegLen[inf] += 2 + currseg += 5 + if c.dec.PathMeta.SegLen[inf] <= uint8(currseg) { + currseg = 0 + inf += 1 + } + // set other fields + c.dec.HopFields[i].Bw = res.Bw + c.dec.HopFields[i].Duration = res.Duration + c.dec.HopFields[i].ResID = res.ResID + } + return nil +} + +// Returns a copy of all ASes on the current path in order +func (c *HummingbirdClient) GetPathASes() []addr.IA { + ascopy := make([]addr.IA, len(c.ases)) + copy(ascopy, c.ases) + return ascopy +} + +// Requests new reservations for this path for the listed ASes +// Expects them to be in order. +func (c *HummingbirdClient) RequestReservationForASes(asin []addr.IA, bw uint16, start uint32, duration uint16) error { + j := 0 + for i := range c.dec.HopFields { + if c.ases[i] == asin[j] { + c.reservations[i].AS = asin[j] + c.reservations[i].Bw = bw + c.reservations[i].StartTime = start + c.reservations[i].Duration = duration + c.reservations[i].Ingress = c.dec.HopFields[i].HopField.ConsIngress + c.reservations[i].Egress = c.dec.HopFields[i].HopField.ConsEgress + var err error + c.reservations[i], err = cheat_auth_key(&c.reservations[i]) + if err != nil { + return err + } + // set flyover + c.dec.HopFields[i].Flyover = true + c.dec.NumHops += 2 + if i < int(c.dec.FirstHopPerSeg[0]) { + c.dec.PathMeta.SegLen[0] += 2 + } else if i < int(c.dec.FirstHopPerSeg[1]) { + c.dec.PathMeta.SegLen[1] += 2 + } else { + c.dec.PathMeta.SegLen[2] += 2 + } + // set other fields + c.dec.HopFields[i].Bw = c.reservations[i].Bw + c.dec.HopFields[i].Duration = c.reservations[i].Duration + c.dec.HopFields[i].ResID = c.reservations[i].ResID + } + + } + return nil +} + +// Return all Reservations present in the database +func GetAllReservations() ([]Reservation, error) { + return nil, serrors.New("Not Implemented") +} + +// Returns all reservations in the database that are applicable to the current path +func (c *HummingbirdClient) GetAllReservations() ([]Reservation, error) { + // Return all reservations in the data base + return nil, serrors.New("Not Implemented") +} + +// Adds the listed reservations to the path +func (c *HummingbirdClient) ApplyReservations(res []Reservation) error { + return serrors.New("Not Implemented") +} + +// Sets pathmeta timestamps and increments duplicate detection counter. +// Updates MACs of all flyoverfields +// replaces the dataplane of the inut snet.path with the finished hummingbird path +func (c *HummingbirdClient) FinalizePath(p snet.Path, pktLen uint16) (snet.Path, error) { + if p == nil { + return nil, serrors.New("snet path is nil") + } + var dphb snetpath.Hummingbird + + // Update timestamps + now := time.Now() + secs := uint32(now.Unix()) + millis := uint32(now.Nanosecond()/1000) << 22 + millis |= c.counter + c.dec.Base.PathMeta.BaseTS = secs + c.dec.Base.PathMeta.HighResTS = millis + //increment counter for next packet + if c.counter >= 1<<22-1 { + c.counter = 0 + } else { + c.counter += 1 + } + // compute Macs for Flyovers + for i, _ := range c.dec.HopFields { + if !c.dec.HopFields[i].Flyover { + continue + } + res := c.reservations[i] + c.dec.HopFields[i].ResStartTime = uint16(secs - res.StartTime) + log.Debug("Computing flyoverMac", "AK", res.Ak[:], "dest", c.dest, "length", pktLen, "start", c.dec.HopFields[i].ResStartTime, "highResTS", millis) + flyovermac := hummingbird.FullFlyoverMac(res.Ak[:], c.dest, pktLen, c.dec.HopFields[i].ResStartTime, millis, c.byteBuffer[:], c.xkbuffer[:]) + log.Debug("Scion MAC in finalize", "c.macs i", fmt.Sprintf("%x", c.macs[i])) + binary.BigEndian.PutUint32(c.dec.HopFields[i].HopField.Mac[:4], binary.BigEndian.Uint32(flyovermac[:4])^binary.BigEndian.Uint32(c.macs[i][:4])) + binary.BigEndian.PutUint16(c.dec.HopFields[i].HopField.Mac[4:], binary.BigEndian.Uint16(flyovermac[4:])^binary.BigEndian.Uint16(c.macs[i][4:])) + log.Debug("Aggregated MAc", "mac", fmt.Sprintf("%x", c.dec.HopFields[i].HopField.Mac[:6])) + } + dphb.Raw = make([]byte, c.dec.Len()) + if err := c.dec.SerializeTo(dphb.Raw); err != nil { + return nil, err + } + switch v := p.(type) { + case snetpath.Path: + v.DataplanePath = dphb + p = v + default: + return nil, serrors.New("Unsupported snet path struct", "path", p) + } + + return p, nil +} diff --git a/pkg/slayers/path/hummingbird/decoded.go b/pkg/slayers/path/hummingbird/decoded.go index 3cfeb13d80..3236717783 100644 --- a/pkg/slayers/path/hummingbird/decoded.go +++ b/pkg/slayers/path/hummingbird/decoded.go @@ -22,6 +22,8 @@ type Decoded struct { InfoFields []path.InfoField // HopFields contains all the HopFields of the path. HopFields []FlyoverHopField + // FirstHopPerSeg notes the index of the first hopfield of the second and third segment + FirstHopPerSeg [2]uint8 } // DecodeFromBytes fully decodes the Hummingbird path into the corresponding fields. @@ -52,6 +54,13 @@ func (s *Decoded) DecodeFromBytes(data []byte) error { if err := s.HopFields[i].DecodeFromBytes(data[offset : offset+path.FlyoverLen]); err != nil { return err } + // Set FirstHopPerSeg + if j == int(s.PathMeta.SegLen[1]) { + s.FirstHopPerSeg[0] = uint8(i) + } else if j == int(s.PathMeta.SegLen[1])+int(s.PathMeta.SegLen[2]) { + s.FirstHopPerSeg[1] = uint8(i) + } + if s.HopFields[i].Flyover { offset += path.FlyoverLen j += 5 @@ -198,6 +207,8 @@ func (s *Decoded) ConvertFromScionDecoded(d scion.Decoded) { Flyover: false, } } + s.FirstHopPerSeg[0] = d.Base.PathMeta.SegLen[0] + s.FirstHopPerSeg[1] = d.Base.PathMeta.SegLen[0] + d.Base.PathMeta.SegLen[1] } func (s *Decoded) convertBaseFromScion(d scion.Base) { diff --git a/pkg/slayers/path/hummingbird/decoded_test.go b/pkg/slayers/path/hummingbird/decoded_test.go index 1615266861..99ff8a9ddb 100644 --- a/pkg/slayers/path/hummingbird/decoded_test.go +++ b/pkg/slayers/path/hummingbird/decoded_test.go @@ -81,8 +81,9 @@ var decodedHbirdTestPath = &hummingbird.Decoded{ NumINF: 2, NumHops: 16, }, - InfoFields: testInfoFields, - HopFields: testFlyoverFields, + InfoFields: testInfoFields, + HopFields: testFlyoverFields, + FirstHopPerSeg: [2]uint8{2, 0}, } var emptyDecodedTestPath = &hummingbird.Decoded{ diff --git a/tools/end2end_hbird/main.go b/tools/end2end_hbird/main.go index 9de7043e2b..dfced130fd 100644 --- a/tools/end2end_hbird/main.go +++ b/tools/end2end_hbird/main.go @@ -48,8 +48,9 @@ import ( ) const ( - ping = "ping" - pong = "pong" + ping = "ping" + pong = "pong" + pingPayloadLen = 113 ) type Ping struct { @@ -71,6 +72,8 @@ var ( scionPacketConnMetrics = metrics.NewSCIONPacketConnMetrics() scmpErrorsCounter = scionPacketConnMetrics.SCMPErrors epic bool + fullReservation bool + partialReservation bool ) func main() { @@ -296,15 +299,62 @@ func (c *client) attemptRequest(n int) bool { } span, ctx = tracing.StartSpanFromCtx(ctx, "attempt.ping") defer span.Finish() - + //TODO: use actaul flags to set + fullReservation = false + partialReservation = true // Convert path to Hummingbird path if path != nil { - path, err = hummingbird.ConvertToHbirdPath(path) - if err != nil { - logger.Error("Error converting path to Hummingbird", "err", err) - return false + if !fullReservation && !partialReservation { + // Standard path, no reservations at all + path, err = hummingbird.ConvertToHbirdPath(path) + if err != nil { + logger.Error("Error converting path to Hummingbird", "err", err) + return false + } + remote.Path = path.Dataplane() + } else if fullReservation { + hbirdClient := hummingbird.HummingbirdClient{} + if err := hbirdClient.PrepareHbirdPath(path); err != nil { + logger.Error("Error converting path to Hummingbird", "err", err) + return false + } + secs := uint32(time.Now().Unix()) + if err := hbirdClient.RequestReservationsAllHops(16, secs, 120); err != nil { + logger.Error("Error requesting reservations", "err", err) + return false + } + // TODO: add step to receive reservation??? + + path, err = hbirdClient.FinalizePath(path, pingPayloadLen) + if err != nil { + logger.Error("Error assembling hummingbird path", "err", err) + } + remote.Path = path.Dataplane() + } else { + //partial reservations, alternating resrved and not reserved + hbirdClient := hummingbird.HummingbirdClient{} + if err := hbirdClient.PrepareHbirdPath(path); err != nil { + logger.Error("Error converting path to Hummingbird", "err", err) + return false + } + ases := hbirdClient.GetPathASes() + n := len(ases) + for i := 1; i < n; i++ { + copy(ases[i:n-1], ases[i+1:n]) + n-- + } + ases = ases[:n] + secs := uint32(time.Now().Unix()) + if err := hbirdClient.RequestReservationForASes(ases, 16, secs, 120); err != nil { + logger.Error("Error requesting reservations", "err", err) + return false + } + path, err = hbirdClient.FinalizePath(path, pingPayloadLen) + if err != nil { + logger.Error("Error assembling hummingbird path", "err", err) + } + remote.Path = path.Dataplane() } - remote.Path = path.Dataplane() } // Send ping From da0e277073c76e2f3c325a5c05701f0e3e1a747f Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Thu, 2 Nov 2023 14:17:50 +0100 Subject: [PATCH 010/100] added moving flyover to next hop on crossover and reversing that later, integration test no longer fails beacause of that missing. Flags for integration test now functional --- pkg/hummingbird/hummingbird.go | 45 +---------- pkg/slayers/path/hummingbird/raw.go | 75 ++++++++++++++++- router/dataplane_hbird.go | 102 +++++++++++------------- router/dataplane_hbird_test.go | 2 +- tools/end2end_hbird/main.go | 14 ++-- tools/end2end_hbird_integration/main.go | 8 +- 6 files changed, 135 insertions(+), 111 deletions(-) diff --git a/pkg/hummingbird/hummingbird.go b/pkg/hummingbird/hummingbird.go index a3155a21ca..476203b3ef 100644 --- a/pkg/hummingbird/hummingbird.go +++ b/pkg/hummingbird/hummingbird.go @@ -3,13 +3,11 @@ package hummingbird import ( "crypto/aes" "encoding/binary" - "fmt" "math/rand" "strings" "time" "github.com/scionproto/scion/pkg/addr" - "github.com/scionproto/scion/pkg/log" "github.com/scionproto/scion/pkg/private/serrors" "github.com/scionproto/scion/pkg/slayers/path/hummingbird" "github.com/scionproto/scion/pkg/slayers/path/scion" @@ -57,8 +55,6 @@ func cheat_auth_key(res *Reservation) (Reservation, error) { key0 := control.DeriveHFMacKey(mkeys.Key0) prf, _ := aes.NewCipher(key0) buffer := make([]byte, 16) - - log.Debug("Computing AK", "ResID", res.ResID, "bw", res.Bw, "ingress", res.Ingress, "Egress", res.Egress, "start", res.StartTime, "Duration", res.Duration) ak := hummingbird.DeriveAuthKey(prf, res.ResID, res.Bw, res.Ingress, res.Egress, res.StartTime, res.Duration, buffer) copy(res.Ak[:], ak[0:16]) return *res, nil @@ -139,9 +135,6 @@ func (c *HummingbirdClient) PrepareHbirdPath(p snet.Path) error { // Convert path to decoded hbird path scionDec := scion.Decoded{} scionDec.DecodeFromBytes(v.Raw) - for _, hop := range scionDec.HopFields { - log.Debug("Decoded Scion", "mac", fmt.Sprintf("%x", hop.Mac[:])) - } c.dec.ConvertFromScionDecoded(scionDec) case snetpath.Hummingbird: c.dec.DecodeFromBytes(v.Raw) @@ -158,7 +151,6 @@ func (c *HummingbirdClient) PrepareHbirdPath(p snet.Path) error { c.macs = make([][6]byte, len(c.dec.HopFields)) for i, hop := range c.dec.HopFields { copy(c.macs[i][:], hop.HopField.Mac[:]) - log.Debug("Scion MAC in prepare", "c.macs i", fmt.Sprintf("%x", c.macs[i])) } // prepare reservations data structure c.reservations = make([]Reservation, len(c.dec.HopFields)) @@ -166,37 +158,7 @@ func (c *HummingbirdClient) PrepareHbirdPath(p snet.Path) error { } func (c *HummingbirdClient) RequestReservationsAllHops(bw uint16, start uint32, duration uint16) error { - inf := 0 - currseg := 0 - for i := range c.dec.HopFields { - res := Reservation{ - AS: c.ases[i], - Bw: bw, - StartTime: start, - Duration: duration, - Ingress: c.dec.HopFields[i].HopField.ConsIngress, - Egress: c.dec.HopFields[i].HopField.ConsEgress, - } - var err error - c.reservations[i], err = cheat_auth_key(&res) - if err != nil { - return err - } - // set flyover to true and adapt metahdr - c.dec.HopFields[i].Flyover = true - c.dec.NumHops += 2 - c.dec.PathMeta.SegLen[inf] += 2 - currseg += 5 - if c.dec.PathMeta.SegLen[inf] <= uint8(currseg) { - currseg = 0 - inf += 1 - } - // set other fields - c.dec.HopFields[i].Bw = res.Bw - c.dec.HopFields[i].Duration = res.Duration - c.dec.HopFields[i].ResID = res.ResID - } - return nil + return c.RequestReservationForASes(c.ases, bw, start, duration) } // Returns a copy of all ASes on the current path in order @@ -282,18 +244,15 @@ func (c *HummingbirdClient) FinalizePath(p snet.Path, pktLen uint16) (snet.Path, c.counter += 1 } // compute Macs for Flyovers - for i, _ := range c.dec.HopFields { + for i := range c.dec.HopFields { if !c.dec.HopFields[i].Flyover { continue } res := c.reservations[i] c.dec.HopFields[i].ResStartTime = uint16(secs - res.StartTime) - log.Debug("Computing flyoverMac", "AK", res.Ak[:], "dest", c.dest, "length", pktLen, "start", c.dec.HopFields[i].ResStartTime, "highResTS", millis) flyovermac := hummingbird.FullFlyoverMac(res.Ak[:], c.dest, pktLen, c.dec.HopFields[i].ResStartTime, millis, c.byteBuffer[:], c.xkbuffer[:]) - log.Debug("Scion MAC in finalize", "c.macs i", fmt.Sprintf("%x", c.macs[i])) binary.BigEndian.PutUint32(c.dec.HopFields[i].HopField.Mac[:4], binary.BigEndian.Uint32(flyovermac[:4])^binary.BigEndian.Uint32(c.macs[i][:4])) binary.BigEndian.PutUint16(c.dec.HopFields[i].HopField.Mac[4:], binary.BigEndian.Uint16(flyovermac[4:])^binary.BigEndian.Uint16(c.macs[i][4:])) - log.Debug("Aggregated MAc", "mac", fmt.Sprintf("%x", c.dec.HopFields[i].HopField.Mac[:6])) } dphb.Raw = make([]byte, c.dec.Len()) if err := c.dec.SerializeTo(dphb.Raw); err != nil { diff --git a/pkg/slayers/path/hummingbird/raw.go b/pkg/slayers/path/hummingbird/raw.go index fbb360ce62..7a34729ffe 100644 --- a/pkg/slayers/path/hummingbird/raw.go +++ b/pkg/slayers/path/hummingbird/raw.go @@ -152,8 +152,7 @@ func (s *Raw) ReplacMac(idx int, mac []byte) error { if idx >= s.NumHops-2 { return serrors.New("HopField index out of bounds", "max", s.NumHops-3, "actual", idx) } - offset := s.NumINF*path.InfoLen + path.MacOffset - offset += MetaLen + idx*path.LineLen + offset := s.NumINF*path.InfoLen + MetaLen + idx*path.LineLen + path.MacOffset if n := copy(s.Raw[offset:offset+path.MacLen], mac[:path.MacLen]); n != path.MacLen { return serrors.New("copied worng number of bytes for mac replacement", "expected", path.MacLen, "actual", n) } @@ -165,6 +164,15 @@ func (s *Raw) ReplaceCurrentMac(mac []byte) error { return s.ReplacMac(int(s.PathMeta.CurrHF), mac) } +// Returns a slice of the MAC of the hopfield starting at index idx +func (s *Raw) GetMac(idx int) ([]byte, error) { + if idx >= s.NumHops-2 { + return nil, serrors.New("HopField index out of bounds", "max", s.NumHops-3, "actual", idx) + } + offset := s.NumINF*path.InfoLen + MetaLen + idx*path.LineLen + path.MacOffset + return s.Raw[offset : offset+path.MacLen], nil +} + // SetHopField updates the HopField at a given index. // For Hummingbird paths the index is the offset in 4 byte lines // @@ -205,3 +213,66 @@ func (s *Raw) IsFirstHop() bool { func (s *Raw) IsLastHop() bool { return int(s.PathMeta.CurrHF) == (s.NumHops-3) || int(s.PathMeta.CurrHF) == (s.NumHops-5) } + +// Attaches current flyoverfield to next hopfield. +// DOES NOT adapt MACS. +func (s *Raw) DoFlyoverXover() error { + idx := int(s.Base.PathMeta.CurrHF) + if idx >= s.NumHops-7 { + return serrors.New("CurrHF out of bounds for flyover crossover", "max", s.NumHops-7, "actual", idx) + } + if s.PathMeta.CurrINF == 2 { + return serrors.New("Cannot do FlyoverXover if CurrINF = 2") + } + hopOffset := MetaLen + s.NumINF*path.InfoLen + idx*path.LineLen + if s.Raw[hopOffset]&0x80 == 0x00 { + return serrors.New("Current hop does not have a Flyover") + } + if s.Raw[hopOffset+FlyoverLen]&0x80 != 0x00 { + return serrors.New("Hop after Crossover has Flyover") + } + // buffer flyover and copy data + var t [2 * LineLen]byte + copy(t[:], s.Raw[hopOffset+path.HopLen:hopOffset+FlyoverLen]) + copy(s.Raw[hopOffset+path.HopLen:hopOffset+2*path.HopLen], s.Raw[hopOffset+path.FlyoverLen:hopOffset+path.FlyoverLen+path.HopLen]) + copy(s.Raw[hopOffset+2*path.HopLen:hopOffset:hopOffset+path.HopLen+path.FlyoverLen], t[:]) + + // Unset and Set Flyoverbits + s.Raw[hopOffset] &= 0x7f + s.Raw[hopOffset+path.HopLen] |= 0x80 + // Adatp seglens + s.Base.PathMeta.SegLen[s.PathMeta.CurrINF] -= 2 + s.Base.PathMeta.SegLen[s.PathMeta.CurrINF+1] += 2 + return nil +} + +// Attaches current flyoverfield to previous hopfield +// DOES NOT adapt MACs +func (s *Raw) ReverseFlyoverXover() error { + idx := int(s.Base.PathMeta.CurrHF) + if idx < 6 { + return serrors.New("CurrHF too small for reversing flyover crossover", "min", 6, "actual", idx) + } + if s.PathMeta.CurrINF == 0 { + return serrors.New("Cannot reverse Flyover Xover when CurrINF = 0") + } + hopOffset := MetaLen + s.NumINF*path.InfoLen + idx*path.LineLen + if s.Raw[hopOffset]&0x80 == 0x00 { + return serrors.New("Current hop does not have a Flyover") + } + if s.Raw[hopOffset-path.HopLen]&0x80 != 0x00 { + return serrors.New("Cannot Reverse Flyover Crossover, flyover bit set where previous hop should be") + } + var t [FlyoverLen - path.HopLen]byte + copy(t[:], s.Raw[hopOffset+path.HopLen:hopOffset+FlyoverLen]) + copy(s.Raw[hopOffset+FlyoverLen-path.HopLen:hopOffset+FlyoverLen], s.Raw[hopOffset:hopOffset+path.HopLen]) + copy(s.Raw[hopOffset:hopOffset+FlyoverLen-path.HopLen], t[:]) + // Set and Unset Flyoverbits + s.Raw[hopOffset-path.HopLen] |= 0x80 + s.Raw[hopOffset+FlyoverLen-path.HopLen] &= 0x7f + // Adapt Seglens and CurrHF + s.Base.PathMeta.SegLen[s.PathMeta.CurrINF] -= 2 + s.Base.PathMeta.SegLen[s.PathMeta.CurrINF-1] += 2 + s.Base.PathMeta.CurrHF += 2 + return nil +} diff --git a/router/dataplane_hbird.go b/router/dataplane_hbird.go index 4d4c1c3f59..f8e5e481a6 100644 --- a/router/dataplane_hbird.go +++ b/router/dataplane_hbird.go @@ -125,8 +125,8 @@ func (p *scionPacketProcessor) verifyCurrentHbirdMAC() (processResult, error) { scionMac := path.FullMAC(p.mac, p.infoField, p.hopField, p.macInputBuffer[:path.MACBufferSize]) // Aggregate MACS and verify if necessary if p.flyoverField.Flyover { - binary.BigEndian.PutUint64(flyoverMac[0:8], binary.BigEndian.Uint64(scionMac[0:8])^binary.BigEndian.Uint64(flyoverMac[0:8])) - binary.BigEndian.PutUint32(flyoverMac[8:12], binary.BigEndian.Uint32(scionMac[8:12])^binary.BigEndian.Uint32(flyoverMac[8:12])) + binary.BigEndian.PutUint32(flyoverMac[0:4], binary.BigEndian.Uint32(scionMac[0:4])^binary.BigEndian.Uint32(flyoverMac[0:4])) + binary.BigEndian.PutUint16(flyoverMac[4:6], binary.BigEndian.Uint16(scionMac[4:6])^binary.BigEndian.Uint16(flyoverMac[4:6])) verified = subtle.ConstantTimeCompare(p.hopField.Mac[:path.MacLen], flyoverMac[:path.MacLen]) if verified == 0 { @@ -166,58 +166,6 @@ func (p *scionPacketProcessor) verifyCurrentHbirdMAC() (processResult, error) { return processResult{}, nil } -// func (p *scionPacketProcessor) verifyCurrentHbirdMAC() (processResult, error) { -// scionMac := path.FullMAC(p.mac, p.infoField, p.hopField, p.macInputBuffer[:path.MACBufferSize]) - -// var verified int -// if p.flyoverField.Flyover { -// ak := hummingbird.DeriveAuthKey(p.prf, p.flyoverField.ResID, p.flyoverField.Bw, p.hopField.ConsIngress, p.hopField.ConsEgress, -// p.hbirdPath.PathMeta.BaseTS-uint32(p.flyoverField.ResStartTime), p.flyoverField.Duration, -// p.macInputBuffer[path.MACBufferSize+hummingbird.FlyoverMacBufferSize:]) -// flyoverMac := hummingbird.FullFlyoverMac(ak, p.scionLayer.DstIA, p.scionLayer.PayloadLen, p.flyoverField.ResStartTime, -// p.hbirdPath.PathMeta.HighResTS, p.macInputBuffer[path.MACBufferSize:], p.hbirdXkbuffer) -// // Xor to Aggregate MACs -// binary.BigEndian.PutUint64(flyoverMac[0:8], binary.BigEndian.Uint64(scionMac[0:8])^binary.BigEndian.Uint64(flyoverMac[0:8])) -// binary.BigEndian.PutUint32(flyoverMac[8:12], binary.BigEndian.Uint32(scionMac[8:12])^binary.BigEndian.Uint32(flyoverMac[8:12])) - -// verified = subtle.ConstantTimeCompare(p.hopField.Mac[:path.MacLen], flyoverMac[:path.MacLen]) -// if verified == 0 { -// log.Debug("SCMP: Aggregate MAC verification failed", "expected", fmt.Sprintf("%x", flyoverMac[:path.MacLen]), -// "actual", fmt.Sprintf("%x", p.hopField.Mac[:path.MacLen]), "cons_dir", p.infoField.ConsDir, -// "scionMac", fmt.Sprintf("%x", scionMac[:path.MacLen]), -// "if_id", p.ingressID, "curr_inf", p.hbirdPath.PathMeta.CurrINF, -// "curr_hf", p.hbirdPath.PathMeta.CurrHF, "seg_id", p.infoField.SegID, "packet length", p.scionLayer.PayloadLen, -// "dest", p.scionLayer.DstIA, "startTime", p.flyoverField.ResStartTime, "highResTS", p.hbirdPath.PathMeta.HighResTS, -// "ResID", p.flyoverField.ResID, "Bw", p.flyoverField.Bw, "in", p.hopField.ConsIngress, -// "Eg", p.hopField.ConsEgress, "start ak", p.hbirdPath.PathMeta.BaseTS-uint32(p.flyoverField.ResStartTime), -// "Duration", p.flyoverField.Duration) -// } -// } else { -// verified = subtle.ConstantTimeCompare(p.hopField.Mac[:path.MacLen], scionMac[:path.MacLen]) -// if verified == 0 { -// log.Debug("SCMP: MAC verification failed", "expected", fmt.Sprintf( -// "%x", scionMac[:path.MacLen]), -// "actual", fmt.Sprintf("%x", p.hopField.Mac[:path.MacLen]), -// "cons_dir", p.infoField.ConsDir, -// "if_id", p.ingressID, "curr_inf", p.hbirdPath.PathMeta.CurrINF, -// "curr_hf", p.hbirdPath.PathMeta.CurrHF, "seg_id", p.infoField.SegID) -// } -// } -// // Add the full MAC to the SCION packet processor, -// // such that EPIC and hummingbird mac de-aggregation do not need to recalculate it. -// p.cachedMac = scionMac -// if verified == 0 { -// slowPathRequest := slowPathRequest{ -// scmpType: slayers.SCMPTypeParameterProblem, -// code: slayers.SCMPCodeInvalidHopFieldMAC, -// pointer: p.currentHopPointer(), -// cause: macVerificationFailed, -// } -// return processResult{SlowPathRequest: slowPathRequest}, slowPathRequired -// } -// return processResult{}, nil -// } - func (p *scionPacketProcessor) validateHbirdSrcDstIA() (processResult, error) { srcIsLocal := (p.scionLayer.SrcIA == p.d.localIA) dstIsLocal := (p.scionLayer.DstIA == p.d.localIA) @@ -361,13 +309,48 @@ func (p *scionPacketProcessor) deAggregateMac() (processResult, error) { return processResult{}, nil } +func (p *scionPacketProcessor) doFlyoverXover() error { + // Move flyoverhopfield to next hop for benefit of egress router + if err := p.hbirdPath.DoFlyoverXover(); err != nil { + return err + } + + // Buffer flyover part of current mac by xoring it with cached scionMac + binary.BigEndian.PutUint32(p.macInputBuffer[6:10], binary.BigEndian.Uint32(p.flyoverField.HopField.Mac[0:4])^binary.BigEndian.Uint32(p.cachedMac[0:4])) + p.macInputBuffer[10] = p.flyoverField.HopField.Mac[4] ^ p.cachedMac[4] + p.macInputBuffer[11] = p.flyoverField.HopField.Mac[5] ^ p.cachedMac[5] + // deaggregate current mac + copy(p.hopField.Mac[:], p.cachedMac[0:6]) + p.hbirdPath.ReplaceCurrentMac(p.cachedMac) + // Aggregate Mac of second hop + mac, err := p.hbirdPath.GetMac(int(p.hbirdPath.PathMeta.CurrHF) + 3) + if err != nil { + return err + } + binary.BigEndian.PutUint32(mac[0:4], binary.BigEndian.Uint32(mac[0:4])^binary.BigEndian.Uint32(p.macInputBuffer[6:10])) + mac[4] ^= p.macInputBuffer[10] + mac[5] ^= p.macInputBuffer[11] + return nil +} + +func (p *scionPacketProcessor) reverseFlyoverXover() error { + if err := p.hbirdPath.ReverseFlyoverXover(); err != nil { + return err + } + // No MAC aggregation/de-aggregation, as these are already performed + p.flyoverField.Flyover = false + return nil +} + func (p *scionPacketProcessor) doHbirdXover() (processResult, error) { p.effectiveXover = true - n := 3 + if p.flyoverField.Flyover { - n = 5 + if err := p.doFlyoverXover(); err != nil { + return processResult{}, err + } } - if err := p.hbirdPath.IncPath(n); err != nil { + if err := p.hbirdPath.IncPath(3); err != nil { // TODO parameter problem invalid path return processResult{}, serrors.WrapStr("incrementing path", err) } @@ -514,6 +497,11 @@ func (p *scionPacketProcessor) processHBIRD() (processResult, error) { if r, err := p.deAggregateMac(); err != nil { return r, err } + if p.flyoverField.Flyover && p.hbirdPath.IsFirstHopAfterXover() { + if err := p.reverseFlyoverXover(); err != nil { + return processResult{}, err + } + } if err := p.processHbirdEgress(); err != nil { return processResult{}, err } diff --git a/router/dataplane_hbird_test.go b/router/dataplane_hbird_test.go index 8bab7a8d72..44801deb3e 100644 --- a/router/dataplane_hbird_test.go +++ b/router/dataplane_hbird_test.go @@ -518,7 +518,7 @@ func TestProcessHbirdPacket(t *testing.T) { for name, tc := range testCases { name, tc := name, tc - if name != "astransit xover flyover" { + if name == "astransit xover flyover" { continue } t.Run(name, func(t *testing.T) { diff --git a/tools/end2end_hbird/main.go b/tools/end2end_hbird/main.go index dfced130fd..d11763007a 100644 --- a/tools/end2end_hbird/main.go +++ b/tools/end2end_hbird/main.go @@ -72,8 +72,8 @@ var ( scionPacketConnMetrics = metrics.NewSCIONPacketConnMetrics() scmpErrorsCounter = scionPacketConnMetrics.SCMPErrors epic bool - fullReservation bool - partialReservation bool + flyovers bool + partial bool ) func main() { @@ -109,6 +109,8 @@ func addFlags() { flag.Var(&remote, "remote", "(Mandatory for clients) address to connect to") flag.Var(timeout, "timeout", "The timeout for each attempt") flag.BoolVar(&epic, "epic", false, "Enable EPIC.") + flag.BoolVar(&flyovers, "flyovers", true, "Enable Flyovers") + flag.BoolVar(&partial, "partial", false, "If true, only subset of hops have flyovers") } func validateFlags() { @@ -299,12 +301,9 @@ func (c *client) attemptRequest(n int) bool { } span, ctx = tracing.StartSpanFromCtx(ctx, "attempt.ping") defer span.Finish() - //TODO: use actaul flags to set - fullReservation = false - partialReservation = true // Convert path to Hummingbird path if path != nil { - if !fullReservation && !partialReservation { + if !flyovers { // Standard path, no reservations at all path, err = hummingbird.ConvertToHbirdPath(path) if err != nil { @@ -312,7 +311,8 @@ func (c *client) attemptRequest(n int) bool { return false } remote.Path = path.Dataplane() - } else if fullReservation { + } else if !partial { + // full path with reservations hbirdClient := hummingbird.HummingbirdClient{} if err := hbirdClient.PrepareHbirdPath(path); err != nil { logger.Error("Error converting path to Hummingbird", "err", err) diff --git a/tools/end2end_hbird_integration/main.go b/tools/end2end_hbird_integration/main.go index f106c34767..7077b1d7dc 100644 --- a/tools/end2end_hbird_integration/main.go +++ b/tools/end2end_hbird_integration/main.go @@ -43,6 +43,8 @@ var ( cmd string features string epic bool + flyovers bool + partial bool ) func getCmd() (string, bool) { @@ -75,6 +77,8 @@ func realMain() int { "-local", integration.SrcAddrPattern + ":0", "-remote", integration.DstAddrPattern + ":" + integration.ServerPortReplace, fmt.Sprintf("-epic=%t", epic), + fmt.Sprintf("-flyovers=%t", flyovers), + fmt.Sprintf("-partial=%t", partial), } serverArgs := []string{ "-mode", "server", @@ -116,9 +120,11 @@ func addFlags() { flag.StringVar(&features, "features", "", fmt.Sprintf("enable development features (%v)", feature.String(&feature.Default{}, "|"))) flag.BoolVar(&epic, "epic", false, "Enable EPIC.") + flag.BoolVar(&flyovers, "flyovers", true, "Enable Flyovers") + flag.BoolVar(&partial, "partial", false, "If true, only subset of hops have flyovers") } -// runTests runs the end2end tests for all pairs. In case of an error the +// runTests runs the end2end hbird tests for all pairs. In case of an error the // function is terminated immediately. func runTests(in integration.Integration, pairs []integration.IAPair) error { return integration.ExecuteTimed(in.Name(), func() error { From 8fcb6cdcd04c92c3d5602e8147fea4f32d12ad2f Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Mon, 6 Nov 2023 09:26:29 +0100 Subject: [PATCH 011/100] Fixed router not considering consdier for reservations. Added swapping flyovers to other hop on crossover. Adapted tests accordingly --- pkg/hummingbird/hummingbird.go | 62 ++++- pkg/slayers/path/hummingbird/BUILD.bazel | 2 +- pkg/slayers/path/hummingbird/base.go | 8 +- .../path/hummingbird/flyoverhopfield.go | 6 +- pkg/slayers/path/hummingbird/raw.go | 46 +++- router/dataplane_hbird.go | 22 +- router/dataplane_hbird_test.go | 246 +++++++++++++++++- 7 files changed, 361 insertions(+), 31 deletions(-) diff --git a/pkg/hummingbird/hummingbird.go b/pkg/hummingbird/hummingbird.go index 476203b3ef..95413f56de 100644 --- a/pkg/hummingbird/hummingbird.go +++ b/pkg/hummingbird/hummingbird.go @@ -174,12 +174,60 @@ func (c *HummingbirdClient) RequestReservationForASes(asin []addr.IA, bw uint16, j := 0 for i := range c.dec.HopFields { if c.ases[i] == asin[j] { + if j != 0 && asin[j] == asin[j-1] { + // Do not add flyover on second crossover hop + continue + } + var infIdx int + var firstHopAfterXover, lastHopBeforeXover bool + if i < int(c.dec.FirstHopPerSeg[0]) { + infIdx = 0 + lastHopBeforeXover = (i == int(c.dec.FirstHopPerSeg[0])-1) && i != len(c.dec.HopFields)-1 + } else if i < int(c.dec.FirstHopPerSeg[1]) { + infIdx = 1 + firstHopAfterXover = i == int(c.dec.FirstHopPerSeg[0]) + lastHopBeforeXover = i == int(c.dec.FirstHopPerSeg[1])-1 && i != len(c.dec.HopFields)-1 + } else { + infIdx = 2 + firstHopAfterXover = i == int(c.dec.FirstHopPerSeg[1]) && i != len(c.dec.HopFields)-1 + } + c.reservations[i].AS = asin[j] c.reservations[i].Bw = bw c.reservations[i].StartTime = start c.reservations[i].Duration = duration - c.reservations[i].Ingress = c.dec.HopFields[i].HopField.ConsIngress - c.reservations[i].Egress = c.dec.HopFields[i].HopField.ConsEgress + // Set Ingress and Egress interfaces of reservation + // If crossover, need to take next/previous hop into account + if lastHopBeforeXover { + if c.dec.InfoFields[infIdx].ConsDir { + c.reservations[i].Ingress = c.dec.HopFields[i].HopField.ConsIngress + } else { + c.reservations[i].Ingress = c.dec.HopFields[i].HopField.ConsEgress + } + if c.dec.InfoFields[infIdx+1].ConsDir { + c.reservations[i].Egress = c.dec.HopFields[i+1].HopField.ConsEgress + } else { + c.reservations[i].Egress = c.dec.HopFields[i+1].HopField.ConsIngress + } + } else if firstHopAfterXover { + if c.dec.InfoFields[infIdx-1].ConsDir { + c.reservations[i].Ingress = c.dec.HopFields[i-1].HopField.ConsIngress + } else { + c.reservations[i].Ingress = c.dec.HopFields[i-1].HopField.ConsEgress + } + if c.dec.InfoFields[infIdx].ConsDir { + c.reservations[i].Egress = c.dec.HopFields[i].HopField.ConsEgress + } else { + c.reservations[i].Egress = c.dec.HopFields[i].HopField.ConsIngress + } + } else if c.dec.InfoFields[infIdx].ConsDir { + c.reservations[i].Ingress = c.dec.HopFields[i].HopField.ConsIngress + c.reservations[i].Egress = c.dec.HopFields[i].HopField.ConsEgress + } else { + c.reservations[i].Ingress = c.dec.HopFields[i].HopField.ConsEgress + c.reservations[i].Egress = c.dec.HopFields[i].HopField.ConsIngress + } + var err error c.reservations[i], err = cheat_auth_key(&c.reservations[i]) if err != nil { @@ -188,17 +236,13 @@ func (c *HummingbirdClient) RequestReservationForASes(asin []addr.IA, bw uint16, // set flyover c.dec.HopFields[i].Flyover = true c.dec.NumHops += 2 - if i < int(c.dec.FirstHopPerSeg[0]) { - c.dec.PathMeta.SegLen[0] += 2 - } else if i < int(c.dec.FirstHopPerSeg[1]) { - c.dec.PathMeta.SegLen[1] += 2 - } else { - c.dec.PathMeta.SegLen[2] += 2 - } + c.dec.PathMeta.SegLen[infIdx] += 2 // set other fields c.dec.HopFields[i].Bw = c.reservations[i].Bw c.dec.HopFields[i].Duration = c.reservations[i].Duration c.dec.HopFields[i].ResID = c.reservations[i].ResID + + j++ } } diff --git a/pkg/slayers/path/hummingbird/BUILD.bazel b/pkg/slayers/path/hummingbird/BUILD.bazel index 558ac6c43b..083ea1e616 100644 --- a/pkg/slayers/path/hummingbird/BUILD.bazel +++ b/pkg/slayers/path/hummingbird/BUILD.bazel @@ -16,8 +16,8 @@ go_library( visibility = ["//visibility:public"], deps = [ "//pkg/private/serrors:go_default_library", - "//pkg/slayers/path/scion:go_default_library", "//pkg/slayers/path:go_default_library", + "//pkg/slayers/path/scion:go_default_library", ] + select({ "@io_bazel_rules_go//go/platform:amd64": [ "//pkg/addr:go_default_library", diff --git a/pkg/slayers/path/hummingbird/base.go b/pkg/slayers/path/hummingbird/base.go index c6ebfa5e7b..745074cf9c 100644 --- a/pkg/slayers/path/hummingbird/base.go +++ b/pkg/slayers/path/hummingbird/base.go @@ -63,24 +63,24 @@ func (s *Base) IncPath(n int) error { return serrors.New("Incrementing path over end") } s.PathMeta.CurrHF += uint8(n) - s.PathMeta.CurrINF = s.infIndexForHF(s.PathMeta.CurrHF) + s.PathMeta.CurrINF = s.InfIndexForHF(s.PathMeta.CurrHF) return nil } // IsXover returns whether we are at a crossover point. func (s *Base) IsXover() bool { return s.PathMeta.CurrHF+5 < uint8(s.NumHops) && - (s.PathMeta.CurrINF != s.infIndexForHF(s.PathMeta.CurrHF+3) || s.PathMeta.CurrINF != s.infIndexForHF(s.PathMeta.CurrHF+5)) + (s.PathMeta.CurrINF != s.InfIndexForHF(s.PathMeta.CurrHF+3) || s.PathMeta.CurrINF != s.InfIndexForHF(s.PathMeta.CurrHF+5)) } // IsFirstHopAfterXover returns whether this is the first hop field after a crossover point. func (s *Base) IsFirstHopAfterXover() bool { return s.PathMeta.CurrINF > 0 && s.PathMeta.CurrHF > 0 && - s.PathMeta.CurrINF-1 == s.infIndexForHF(s.PathMeta.CurrHF-1) + s.PathMeta.CurrINF-1 == s.InfIndexForHF(s.PathMeta.CurrHF-1) } -func (s *Base) infIndexForHF(hf uint8) uint8 { +func (s *Base) InfIndexForHF(hf uint8) uint8 { switch { case hf < s.PathMeta.SegLen[0]: return 0 diff --git a/pkg/slayers/path/hummingbird/flyoverhopfield.go b/pkg/slayers/path/hummingbird/flyoverhopfield.go index 840ee29fc4..1b2d5154a4 100644 --- a/pkg/slayers/path/hummingbird/flyoverhopfield.go +++ b/pkg/slayers/path/hummingbird/flyoverhopfield.go @@ -8,8 +8,10 @@ import ( ) const ( - LineLen = 4 - FlyoverLen = 20 + LineLen = 4 + FlyoverLen = 20 + HopLines = 3 + FlyoverLines = 5 ) type FlyoverHopField struct { diff --git a/pkg/slayers/path/hummingbird/raw.go b/pkg/slayers/path/hummingbird/raw.go index 7a34729ffe..c90630c3e1 100644 --- a/pkg/slayers/path/hummingbird/raw.go +++ b/pkg/slayers/path/hummingbird/raw.go @@ -1,6 +1,8 @@ package hummingbird import ( + "encoding/binary" + "github.com/scionproto/scion/pkg/private/serrors" "github.com/scionproto/scion/pkg/slayers/path" ) @@ -110,6 +112,14 @@ func (s *Raw) GetCurrentInfoField() (path.InfoField, error) { return s.GetInfoField(int(s.PathMeta.CurrINF)) } +// Returns whether the hopfield at the given index is in construction direction +func (s *Raw) isConsdir(idx uint8) bool { + hopIdx := s.Base.InfIndexForHF(idx) + infOffset := MetaLen + hopIdx*path.InfoLen + return s.Raw[infOffset]&0x1 == 0x1 + +} + // SetInfoField updates the InfoField at a given index. func (s *Raw) SetInfoField(info path.InfoField, idx int) error { if idx >= s.NumINF { @@ -214,6 +224,40 @@ func (s *Raw) IsLastHop() bool { return int(s.PathMeta.CurrHF) == (s.NumHops-3) || int(s.PathMeta.CurrHF) == (s.NumHops-5) } +// Returns the egress interface of the next hop +func (s *Raw) GetNextEgress() (uint16, error) { + idx := int(s.Base.PathMeta.CurrHF) + hopOffset := MetaLen + s.NumINF*path.InfoLen + idx*path.LineLen + if s.Raw[hopOffset]&0x80 == 0x80 { + idx += FlyoverLines + hopOffset += FlyoverLines * LineLen + } else { + idx += HopLines + hopOffset += HopLines * LineLen + } + if idx >= s.NumHops-2 { + return 0, serrors.New("HopField index out of bounds", "max", s.NumHops-3, "actual", idx) + } + if s.isConsdir(uint8(idx)) { + return binary.BigEndian.Uint16(s.Raw[hopOffset+4 : hopOffset+6]), nil + } + return binary.BigEndian.Uint16(s.Raw[hopOffset+2 : hopOffset+4]), nil +} + +// Returns the ingress interface of the previous hop +// Assumes the previous hop is NOT a flyoverhop +func (s *Raw) GetPreviousIngress() (uint16, error) { + idx := int(s.Base.PathMeta.CurrHF) - HopLines + if idx < 0 { + return 0, serrors.New("HopField index out of bounds", "min", 0, "actual", idx) + } + hopOffset := MetaLen + s.NumINF*path.InfoLen + idx*path.LineLen + if s.isConsdir(uint8(idx)) { + return binary.BigEndian.Uint16(s.Raw[hopOffset+2 : hopOffset+4]), nil + } + return binary.BigEndian.Uint16(s.Raw[hopOffset+4 : hopOffset+6]), nil +} + // Attaches current flyoverfield to next hopfield. // DOES NOT adapt MACS. func (s *Raw) DoFlyoverXover() error { @@ -235,7 +279,7 @@ func (s *Raw) DoFlyoverXover() error { var t [2 * LineLen]byte copy(t[:], s.Raw[hopOffset+path.HopLen:hopOffset+FlyoverLen]) copy(s.Raw[hopOffset+path.HopLen:hopOffset+2*path.HopLen], s.Raw[hopOffset+path.FlyoverLen:hopOffset+path.FlyoverLen+path.HopLen]) - copy(s.Raw[hopOffset+2*path.HopLen:hopOffset:hopOffset+path.HopLen+path.FlyoverLen], t[:]) + copy(s.Raw[hopOffset+2*path.HopLen:hopOffset+path.HopLen+path.FlyoverLen], t[:]) // Unset and Set Flyoverbits s.Raw[hopOffset] &= 0x7f diff --git a/router/dataplane_hbird.go b/router/dataplane_hbird.go index f8e5e481a6..79db77d952 100644 --- a/router/dataplane_hbird.go +++ b/router/dataplane_hbird.go @@ -96,7 +96,27 @@ func (p *scionPacketProcessor) verifyCurrentHbirdMAC() (processResult, error) { // Compute flyovermac if p.flyoverField.Flyover { - ak := hummingbird.DeriveAuthKey(p.prf, p.flyoverField.ResID, p.flyoverField.Bw, p.hopField.ConsIngress, p.hopField.ConsEgress, + ingress := p.hopField.ConsIngress + egress := p.hopField.ConsEgress + // Reservations are not bidirectional, reservation ingress and egress are always real ingress and egress + if !p.infoField.ConsDir { + ingress, egress = egress, ingress + } + // On crossovers, A Reservation goes from the ingress of the incoming hop to the egress of the outgoing one + var err error + if p.hbirdPath.IsXover() { + egress, err = p.hbirdPath.GetNextEgress() + if err != nil { + return processResult{}, err + } + } else if p.hbirdPath.IsFirstHopAfterXover() { + ingress, err = p.hbirdPath.GetPreviousIngress() + if err != nil { + return processResult{}, err + } + } + + ak := hummingbird.DeriveAuthKey(p.prf, p.flyoverField.ResID, p.flyoverField.Bw, ingress, egress, p.hbirdPath.PathMeta.BaseTS-uint32(p.flyoverField.ResStartTime), p.flyoverField.Duration, p.macInputBuffer[path.MACBufferSize+hummingbird.FlyoverMacBufferSize:]) flyoverMac = hummingbird.FullFlyoverMac(ak, p.scionLayer.DstIA, p.scionLayer.PayloadLen, p.flyoverField.ResStartTime, diff --git a/router/dataplane_hbird_test.go b/router/dataplane_hbird_test.go index 44801deb3e..2c74f6cb5d 100644 --- a/router/dataplane_hbird_test.go +++ b/router/dataplane_hbird_test.go @@ -425,6 +425,45 @@ func TestProcessHbirdPacket(t *testing.T) { srcInterface: 1, assertFunc: assert.NoError, }, + "brtransit non consdir flyover": { + prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { + return router.NewDP( + map[uint16]router.BatchConn{ + uint16(2): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 2: topology.Parent, + 1: topology.Child, + }, nil, nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + }, + mockMsg: func(afterProcessing bool) *ipv4.Message { + spkt, dpath := prepHbirdMsg(now) + dpath.HopFields = []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, + {Flyover: true, HopField: path.HopField{ConsIngress: 2, ConsEgress: 1}, ResID: 42, ResStartTime: 5, Duration: 301}, + {HopField: path.HopField{ConsIngress: 40, ConsEgress: 41}}, + } + dpath.Base.PathMeta.CurrHF = 3 + dpath.Base.NumHops = 11 + dpath.Base.PathMeta.SegLen[0] = 11 + + dpath.InfoFields[0].ConsDir = false + dpath.HopFields[1].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, + dpath.InfoFields[0], dpath.HopFields[1], dpath.PathMeta) + if !afterProcessing { + dpath.InfoFields[0].UpdateSegID(computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField)) + return toMsg(t, spkt, dpath) + } + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + require.NoError(t, dpath.IncPath(5)) + ret := toMsg(t, spkt, dpath) + ret.Addr = nil + ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil + return ret + }, + srcInterface: 1, + assertFunc: assert.NoError, + }, "astransit direct flyover": { prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { return router.NewDP(nil, @@ -441,7 +480,7 @@ func TestProcessHbirdPacket(t *testing.T) { spkt, dpath := prepHbirdMsg(now) dpath.HopFields = []hummingbird.FlyoverHopField{ {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, - {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 3}, ResStartTime: 5, Duration: 301}, + {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 3}, ResID: 42, ResStartTime: 5, Duration: 301}, {HopField: path.HopField{ConsIngress: 50, ConsEgress: 51}}, } dpath.Base.PathMeta.SegLen[0] = 11 @@ -459,7 +498,8 @@ func TestProcessHbirdPacket(t *testing.T) { srcInterface: 1, assertFunc: assert.NoError, }, - "astransit xover flyover": { + "astransit xover flyover non consdir": { + // Test currently not working prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { return router.NewDP(nil, map[uint16]topology.LinkType{ @@ -477,7 +517,7 @@ func TestProcessHbirdPacket(t *testing.T) { Base: hummingbird.Base{ PathMeta: hummingbird.MetaHdr{ CurrHF: 6, - SegLen: [3]uint8{6, 10, 0}, + SegLen: [3]uint8{6, 8, 0}, BaseTS: util.TimeToSecs(now), }, NumINF: 2, @@ -490,21 +530,32 @@ func TestProcessHbirdPacket(t *testing.T) { {SegID: 0x222, ConsDir: false, Timestamp: util.TimeToSecs(now)}, }, HopFields: []hummingbird.FlyoverHopField{ - {HopField: path.HopField{ConsIngress: 0, ConsEgress: 1}}, // IA 110 - {HopField: path.HopField{ConsIngress: 31, ConsEgress: 0}}, // Src - {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 51}, ResStartTime: 5, Duration: 310}, // Dst - {Flyover: true, HopField: path.HopField{ConsIngress: 3, ConsEgress: 0}, ResStartTime: 5, Duration: 410}, // IA 110 + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 1}}, // IA 110 + {HopField: path.HopField{ConsIngress: 31, ConsEgress: 0}}, // Src + {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 51}, ResID: 34, ResStartTime: 5, Duration: 310}, // Dst + {HopField: path.HopField{ConsIngress: 3, ConsEgress: 0}}, // IA 110 }, } - dpath.HopFields[2].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, dpath.InfoFields[0], dpath.HopFields[2], dpath.PathMeta) - dpath.HopFields[3].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, dpath.InfoFields[1], dpath.HopFields[3], dpath.PathMeta) + dpath.HopFields[2].HopField.Mac = computeAggregateMacXover(t, key, sv, spkt.DstIA, spkt.PayloadLen, 3, 51, + dpath.InfoFields[0], dpath.HopFields[2], dpath.PathMeta) + dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3].HopField) if !afterProcessing { - dpath.InfoFields[0].UpdateSegID(dpath.HopFields[2].HopField.Mac) + dpath.InfoFields[0].UpdateSegID(computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[2].HopField)) return toMsg(t, spkt, dpath) } - //dpath.HopFields[2].Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[2]) + dpath.HopFields[2].Flyover = false + dpath.HopFields[3].Flyover = true + dpath.HopFields[3].ResID = 34 + dpath.HopFields[3].ResStartTime = 5 + dpath.HopFields[3].Duration = 310 + + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[2].HopField) + dpath.HopFields[3].HopField.Mac = computeAggregateMacXover(t, key, sv, spkt.DstIA, spkt.PayloadLen, 3, 51, + dpath.InfoFields[1], dpath.HopFields[3], dpath.PathMeta) //dpath.HopFields[3].Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3]) + dpath.PathMeta.SegLen[0] -= 2 + dpath.PathMeta.SegLen[1] += 2 require.NoError(t, dpath.IncPath(5)) ret := toMsg(t, spkt, dpath) ret.Addr = &net.UDPAddr{IP: net.ParseIP("10.0.200.200").To4(), Port: 30043} @@ -514,11 +565,153 @@ func TestProcessHbirdPacket(t *testing.T) { srcInterface: 51, assertFunc: assert.NoError, }, + "astransit xover flyover ingress": { + prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { + return router.NewDP(nil, + map[uint16]topology.LinkType{ + 51: topology.Child, + 31: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(31): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + }, + mockMsg: func(afterProcessing bool) *ipv4.Message { + spkt, _ := prepHbirdMsg(now) + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrHF: 3, + SegLen: [3]uint8{8, 6, 0}, + BaseTS: util.TimeToSecs(now), + }, + NumINF: 2, + NumHops: 14, + }, + InfoFields: []path.InfoField{ + // up seg + {SegID: 0x111, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + // core seg + {SegID: 0x222, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + }, + HopFields: []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 1}}, // IA 110 + {Flyover: true, HopField: path.HopField{ConsIngress: 51, ConsEgress: 0}, Bw: 5, ResStartTime: 5, Duration: 310}, // Src + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 31}}, // Dst + {HopField: path.HopField{ConsIngress: 3, ConsEgress: 0}}, // IA 110 + }, + } + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[2].HopField) + dpath.HopFields[1].HopField.Mac = computeAggregateMacXover(t, key, sv, spkt.DstIA, spkt.PayloadLen, 51, 31, + dpath.InfoFields[0], dpath.HopFields[1], dpath.PathMeta) + // dpath.HopFields[3].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, dpath.InfoFields[1], dpath.HopFields[3], dpath.PathMeta) + if !afterProcessing { + //dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.Mac) + // dpath.PathMeta.CurrHF = 6 + // dpath.PathMeta.CurrINF = 1 + + return toMsg(t, spkt, dpath) + } + dpath.HopFields[1].Flyover = false + dpath.HopFields[2].Flyover = true + dpath.HopFields[2].Bw = 5 + dpath.HopFields[2].ResStartTime = 5 + dpath.HopFields[2].Duration = 310 + + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + dpath.HopFields[2].HopField.Mac = computeAggregateMacXover(t, key, sv, spkt.DstIA, spkt.PayloadLen, 51, 31, + dpath.InfoFields[1], dpath.HopFields[2], dpath.PathMeta) + //dpath.HopFields[2].Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[2]) + //dpath.HopFields[3].Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3]) + dpath.PathMeta.SegLen[0] -= 2 + dpath.PathMeta.SegLen[1] += 2 + require.NoError(t, dpath.IncPath(3)) + ret := toMsg(t, spkt, dpath) + ret.Addr = &net.UDPAddr{IP: net.ParseIP("10.0.200.200").To4(), Port: 30043} + ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil + return ret + }, + srcInterface: 51, + assertFunc: assert.NoError, + }, + "astransit xover flyover egress": { + prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { + return router.NewDP( + map[uint16]router.BatchConn{ + uint16(31): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 51: topology.Child, + 31: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(51): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + }, + mockMsg: func(afterProcessing bool) *ipv4.Message { + spkt, _ := prepHbirdMsg(now) + spkt.SrcIA = 109 + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrHF: 6, + CurrINF: 1, + SegLen: [3]uint8{6, 8, 0}, + BaseTS: util.TimeToSecs(now), + }, + NumINF: 2, + NumHops: 14, + }, + InfoFields: []path.InfoField{ + // up seg + {SegID: 0x111, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + // core seg + {SegID: 0x222, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + }, + HopFields: []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 1}}, // Src + {HopField: path.HopField{ConsIngress: 51, ConsEgress: 0}}, // IA 110 + {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 31}, Bw: 5, ResStartTime: 5, Duration: 310}, // IA 110 + {HopField: path.HopField{ConsIngress: 3, ConsEgress: 0}}, // Dst + }, + } + dpath.HopFields[2].HopField.Mac = computeAggregateMacXover(t, key, sv, spkt.DstIA, spkt.PayloadLen, 51, 31, + dpath.InfoFields[1], dpath.HopFields[2], dpath.PathMeta) + // dpath.HopFields[3].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, dpath.InfoFields[1], dpath.HopFields[3], dpath.PathMeta) + if !afterProcessing { + //dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.Mac) + // dpath.PathMeta.CurrHF = 6 + // dpath.PathMeta.CurrINF = 1 + ret := toMsg(t, spkt, dpath) + ret.Addr = &net.UDPAddr{IP: net.ParseIP("10.0.200.200").To4(), Port: 30043} + return ret + } + dpath.HopFields[2].Flyover = false + dpath.HopFields[1].Flyover = true + dpath.HopFields[1].Bw = 5 + dpath.HopFields[1].ResStartTime = 5 + dpath.HopFields[1].Duration = 310 + + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[2].HopField) + dpath.InfoFields[1].UpdateSegID(dpath.HopFields[2].HopField.Mac) + require.NoError(t, dpath.IncPath(5)) + dpath.PathMeta.SegLen[0] += 2 + dpath.PathMeta.SegLen[1] -= 2 + ret := toMsg(t, spkt, dpath) + ret.Addr = nil + ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil + return ret + }, + srcInterface: 0, + assertFunc: assert.NoError, + }, } for name, tc := range testCases { name, tc := name, tc - if name == "astransit xover flyover" { + if name == "astransit xover flyover non consdir" { continue } t.Run(name, func(t *testing.T) { @@ -647,7 +840,34 @@ func computeAggregateMac(t *testing.T, key, sv []byte, dst addr.IA, l uint16, in block, err := aes.NewCipher(sv) require.NoError(t, err) - ak := hummingbird.DeriveAuthKey(block, hf.ResID, hf.Bw, hf.HopField.ConsIngress, hf.HopField.ConsEgress, + ingress, egress := hf.HopField.ConsIngress, hf.HopField.ConsEgress + if !info.ConsDir { + ingress, egress = egress, ingress + } + + ak := hummingbird.DeriveAuthKey(block, hf.ResID, hf.Bw, ingress, egress, + meta.BaseTS-uint32(hf.ResStartTime), hf.Duration, nil) + flyoverMac := hummingbird.FullFlyoverMac(ak, dst, l, hf.ResStartTime, + meta.HighResTS, nil, nil) + + for i, b := range scionMac { + scionMac[i] = b ^ flyoverMac[i] + } + return scionMac +} + +func computeAggregateMacXover(t *testing.T, key, sv []byte, dst addr.IA, l, hin, heg uint16, info path.InfoField, hf hummingbird.FlyoverHopField, meta hummingbird.MetaHdr) [path.MacLen]byte { + + scionMac := computeMAC(t, key, info, hf.HopField) + + block, err := aes.NewCipher(sv) + require.NoError(t, err) + ingress, egress := hin, heg + if !info.ConsDir { + ingress, egress = egress, ingress + } + + ak := hummingbird.DeriveAuthKey(block, hf.ResID, hf.Bw, ingress, egress, meta.BaseTS-uint32(hf.ResStartTime), hf.Duration, nil) flyoverMac := hummingbird.FullFlyoverMac(ak, dst, l, hf.ResStartTime, meta.HighResTS, nil, nil) From 605583b42c649e910f03b88de6e94c647b18d62b Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Mon, 6 Nov 2023 15:01:32 +0100 Subject: [PATCH 012/100] added small multiple hops processing tests for hbird without reservations --- router/dataplane_hbird_test.go | 179 +++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) diff --git a/router/dataplane_hbird_test.go b/router/dataplane_hbird_test.go index 2c74f6cb5d..a9ba237aed 100644 --- a/router/dataplane_hbird_test.go +++ b/router/dataplane_hbird_test.go @@ -735,6 +735,170 @@ func TestProcessHbirdPacket(t *testing.T) { } } +func TestHbirdPacketPath(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + key := []byte("testkey_xxxxxxxx") + sv := []byte("test_secretvalue") + now := time.Now() + + testCases := map[string]struct { + mockMsg func() *ipv4.Message + prepareDPs func(*gomock.Controller) []*router.DataPlane + srcInterfaces []uint16 + }{ + "two hops consdir": { + mockMsg: func() *ipv4.Message { + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:111"), xtest.MustParseIA("1-ff00:0:110")) + dst := addr.MustParseHost("10.0.100.100") + _ = spkt.SetDstAddr(dst) + + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrINF: 0, + CurrHF: 0, + SegLen: [3]uint8{6, 0, 0}, + BaseTS: util.TimeToSecs(now), + HighResTS: 500 << 22, + }, + NumINF: 1, + NumHops: 6, + }, + InfoFields: []path.InfoField{ + {SegID: 0x111, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + }, + + HopFields: []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 40}}, + {HopField: path.HopField{ConsIngress: 01, ConsEgress: 0}}, + }, + } + // Compute MACs and increase SegID while doing so + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + dpath.InfoFields[0].UpdateSegID(dpath.HopFields[0].HopField.Mac) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + // Reset SegID to original value + dpath.InfoFields[0].SegID = 0x111 + ret := toMsg(t, spkt, dpath) + return ret + + }, + prepareDPs: func(*gomock.Controller) []*router.DataPlane { + var dps [2]*router.DataPlane + dps[0] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(40): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 40: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) + + dps[1] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(01): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 01: topology.Child, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + + return dps[:] + }, + srcInterfaces: []uint16{0, 01}, + }, + "two hops non consdir": { + mockMsg: func() *ipv4.Message { + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), xtest.MustParseIA("1-ff00:0:111")) + dst := addr.MustParseHost("10.0.100.100") + _ = spkt.SetDstAddr(dst) + + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrINF: 0, + CurrHF: 0, + SegLen: [3]uint8{6, 0, 0}, + BaseTS: util.TimeToSecs(now), + HighResTS: 500 << 22, + }, + NumINF: 1, + NumHops: 6, + }, + InfoFields: []path.InfoField{ + {SegID: 0x111, ConsDir: false, Timestamp: util.TimeToSecs(now)}, + }, + + HopFields: []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 01, ConsEgress: 0}}, + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 40}}, + }, + } + // Compute MACs and increase SegID while doing so + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.Mac) + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + //dpath.InfoFields[0].UpdateSegID(dpath.HopFields[0].HopField.Mac) + + ret := toMsg(t, spkt, dpath) + return ret + + }, + prepareDPs: func(*gomock.Controller) []*router.DataPlane { + var dps [2]*router.DataPlane + dps[0] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(01): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 01: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + + dps[1] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(40): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 40: topology.Child, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) + + return dps[:] + }, + srcInterfaces: []uint16{0, 40}, + }, + } + + for name, tc := range testCases { + if name != "two hops non consdir" { //Only run test we want to debug + continue + } + name, tc := name, tc + t.Run(name, func(t *testing.T) { + t.Parallel() + dps := tc.prepareDPs(ctrl) + input := tc.mockMsg() + for i, dp := range dps { + result, err := dp.ProcessPkt(tc.srcInterfaces[i], input) + assert.NoError(t, err) + + input = &ipv4.Message{ + Buffers: [][]byte{result.OutPkt}, + Addr: result.OutAddr, + N: len(result.OutPkt), + } + } + }) + } +} + // func TestFlyoverPathReverseLength(t *testing.T) { // //Performs test by provoking an error and checking the length of the returned SCMP packet // //Does this with two different length paths, which should be reduced to identical length due to flyover removal @@ -833,6 +997,21 @@ func prepHbirdMsg(now time.Time) (*slayers.SCION, *hummingbird.Decoded) { return spkt, dpath } +func prepHbirdSlayers(src, dst addr.IA) *slayers.SCION { + spkt := &slayers.SCION{ + Version: 0, + TrafficClass: 0xb8, + FlowID: 0xdead, + NextHdr: slayers.L4UDP, + PathType: hummingbird.PathType, + DstIA: dst, + SrcIA: src, + Path: &hummingbird.Raw{}, + PayloadLen: 18, + } + return spkt +} + func computeAggregateMac(t *testing.T, key, sv []byte, dst addr.IA, l uint16, info path.InfoField, hf hummingbird.FlyoverHopField, meta hummingbird.MetaHdr) [path.MacLen]byte { scionMac := computeMAC(t, key, info, hf.HopField) From 3ab42f869d70071fd09dbc86026825e1d81b0c75 Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Tue, 7 Nov 2023 16:55:13 +0100 Subject: [PATCH 013/100] added unit tests for nonconsdir and peering, fixed issues highlighted by these tests --- pkg/hummingbird/hummingbird.go | 1 + pkg/slayers/path/hummingbird/decoded.go | 11 +- router/dataplane_hbird.go | 105 +- router/dataplane_hbird_test.go | 1936 ++++++++++++++++++++--- 4 files changed, 1811 insertions(+), 242 deletions(-) diff --git a/pkg/hummingbird/hummingbird.go b/pkg/hummingbird/hummingbird.go index 95413f56de..bda28cd6f4 100644 --- a/pkg/hummingbird/hummingbird.go +++ b/pkg/hummingbird/hummingbird.go @@ -176,6 +176,7 @@ func (c *HummingbirdClient) RequestReservationForASes(asin []addr.IA, bw uint16, if c.ases[i] == asin[j] { if j != 0 && asin[j] == asin[j-1] { // Do not add flyover on second crossover hop + j++ continue } var infIdx int diff --git a/pkg/slayers/path/hummingbird/decoded.go b/pkg/slayers/path/hummingbird/decoded.go index 3236717783..b8b4525c18 100644 --- a/pkg/slayers/path/hummingbird/decoded.go +++ b/pkg/slayers/path/hummingbird/decoded.go @@ -55,9 +55,9 @@ func (s *Decoded) DecodeFromBytes(data []byte) error { return err } // Set FirstHopPerSeg - if j == int(s.PathMeta.SegLen[1]) { + if j == int(s.PathMeta.SegLen[0]) { s.FirstHopPerSeg[0] = uint8(i) - } else if j == int(s.PathMeta.SegLen[1])+int(s.PathMeta.SegLen[2]) { + } else if j == int(s.PathMeta.SegLen[0])+int(s.PathMeta.SegLen[1]) { s.FirstHopPerSeg[1] = uint8(i) } @@ -76,6 +76,13 @@ func (s *Decoded) DecodeFromBytes(data []byte) error { i++ } s.HopFields = s.HopFields[:i] + if s.PathMeta.SegLen[1] == 0 { + s.FirstHopPerSeg[0] = s.PathMeta.SegLen[0] + s.FirstHopPerSeg[1] = s.PathMeta.SegLen[0] + } else if s.PathMeta.SegLen[2] == 0 { + s.FirstHopPerSeg[1] = s.PathMeta.SegLen[0] + s.PathMeta.SegLen[1] + } + return nil } diff --git a/router/dataplane_hbird.go b/router/dataplane_hbird.go index 79db77d952..d1f515ab7a 100644 --- a/router/dataplane_hbird.go +++ b/router/dataplane_hbird.go @@ -62,6 +62,37 @@ func (p *scionPacketProcessor) parseHbirdPath() (processResult, error) { return processResult{}, nil } +func determinePeerHbird(pathMeta hummingbird.MetaHdr, inf path.InfoField) (bool, error) { + if !inf.Peer { + return false, nil + } + + if pathMeta.SegLen[0] == 0 { + return false, errPeeringEmptySeg0 + } + if pathMeta.SegLen[1] == 0 { + return false, errPeeringEmptySeg1 + + } + if pathMeta.SegLen[2] != 0 { + return false, errPeeringNonemptySeg2 + } + + // The peer hop fields are the last hop field on the first path + // segment (at SegLen[0] - 1) and the first hop field of the second + // path segment (at SegLen[0]). The below check applies only + // because we already know this is a well-formed peering path. + currHF := pathMeta.CurrHF + segLen := pathMeta.SegLen[0] + return currHF == segLen-3 || currHF == segLen-5 || currHF == segLen, nil +} + +func (p *scionPacketProcessor) determinePeerHbird() (processResult, error) { + peer, err := determinePeerHbird(p.hbirdPath.PathMeta, p.infoField) + p.peering = peer + return processResult{}, err +} + func (p *scionPacketProcessor) validateReservationExpiry() (processResult, error) { startTime := util.SecsToTime(p.hbirdPath.PathMeta.BaseTS - uint32(p.flyoverField.ResStartTime)) endTime := startTime.Add(time.Duration(p.flyoverField.Duration) * time.Second) @@ -104,12 +135,12 @@ func (p *scionPacketProcessor) verifyCurrentHbirdMAC() (processResult, error) { } // On crossovers, A Reservation goes from the ingress of the incoming hop to the egress of the outgoing one var err error - if p.hbirdPath.IsXover() { + if p.hbirdPath.IsXover() && !p.peering { egress, err = p.hbirdPath.GetNextEgress() if err != nil { return processResult{}, err } - } else if p.hbirdPath.IsFirstHopAfterXover() { + } else if p.hbirdPath.IsFirstHopAfterXover() && !p.peering { ingress, err = p.hbirdPath.GetPreviousIngress() if err != nil { return processResult{}, err @@ -125,20 +156,22 @@ func (p *scionPacketProcessor) verifyCurrentHbirdMAC() (processResult, error) { // Perform updateHbirdNonConsDirIngressSegID // This needs de-aggregated MAC but needs to be done before scionMac is computed // Therefore, we check this here instead of before MAC computation like in standard SCiON - if p.flyoverField.Flyover { - // de-aggregate first two bytes of mac - p.hopField.Mac[0] ^= flyoverMac[0] - p.hopField.Mac[1] ^= flyoverMac[1] - err := p.updateHbirdNonConsDirIngressSegID() - // restore correct state of MAC field, even if error - p.hopField.Mac[0] ^= flyoverMac[0] - p.hopField.Mac[1] ^= flyoverMac[1] - if err != nil { - return processResult{}, err - } - } else { - if err := p.updateHbirdNonConsDirIngressSegID(); err != nil { - return processResult{}, err + if !p.hbirdPath.IsFirstHopAfterXover() { + if p.flyoverField.Flyover { + // de-aggregate first two bytes of mac + p.hopField.Mac[0] ^= flyoverMac[0] + p.hopField.Mac[1] ^= flyoverMac[1] + err := p.updateHbirdNonConsDirIngressSegID() + // restore correct state of MAC field, even if error + p.hopField.Mac[0] ^= flyoverMac[0] + p.hopField.Mac[1] ^= flyoverMac[1] + if err != nil { + return processResult{}, err + } + } else { + if err := p.updateHbirdNonConsDirIngressSegID(); err != nil { + return processResult{}, err + } } } // Compute scionMac @@ -256,7 +289,7 @@ func (p *scionPacketProcessor) validateHbirdTransitUnderlaySrc() (processResult, // Current implementation works with a nanosecond granularity HighResTS func (p *scionPacketProcessor) validatePathMetaTimestamp() { timestamp := util.SecsToTime(p.hbirdPath.PathMeta.BaseTS).Add(time.Duration(p.hbirdPath.PathMeta.HighResTS>>22) * time.Millisecond) - // TODO: make a configurable value instead of using a flat 1.5 seconds + // TODO: make a configurable value instead of using a flat 1 seconds if time.Until(timestamp).Abs() > time.Duration(1)*time.Second { // Hummingbird specification explicitely says to forward best-effort is timestamp too old p.hasPriority = false @@ -307,7 +340,7 @@ func (p *scionPacketProcessor) updateHbirdNonConsDirIngressSegID() error { // means this comes from this AS itself, so nothing has to be done. // TODO(lukedirtwalker): For packets destined to peer links this shouldn't // be updated. - if !p.infoField.ConsDir && p.ingressID != 0 { + if !p.infoField.ConsDir && p.ingressID != 0 && !p.peering { p.infoField.UpdateSegID(p.hopField.Mac) if err := p.hbirdPath.SetInfoField(p.infoField, int(p.hbirdPath.PathMeta.CurrINF)); err != nil { return serrors.WrapStr("update info field", err) @@ -392,7 +425,7 @@ func (p *scionPacketProcessor) doHbirdXover() (processResult, error) { func (p *scionPacketProcessor) processHbirdEgress() error { // we are the egress router and if we go in construction direction we // need to update the SegID. - if p.infoField.ConsDir { + if p.infoField.ConsDir && !p.peering { p.infoField.UpdateSegID(p.hopField.Mac) if err := p.hbirdPath.SetInfoField(p.infoField, int(p.hbirdPath.PathMeta.CurrINF)); err != nil { // TODO parameter problem invalid path @@ -422,7 +455,9 @@ func (p *scionPacketProcessor) processHBIRD() (processResult, error) { if r, err := p.parseHbirdPath(); err != nil { return r, err } - + if r, err := p.determinePeerHbird(); err != nil { + return r, err + } if r, err := p.validateHopExpiry(); err != nil { return r, err } @@ -450,7 +485,6 @@ func (p *scionPacketProcessor) processHBIRD() (processResult, error) { return r, err } if p.hasPriority && p.flyoverField.Flyover { - // TODO: current implementation (which is in line with design) allows for an attack surface where packets with outdated TSs bypass bw check but claim priority to next router p.validatePathMetaTimestamp() } if r, err := p.handleHbirdIngressRouterAlert(); err != nil { @@ -471,7 +505,7 @@ func (p *scionPacketProcessor) processHBIRD() (processResult, error) { // Outbound: pkts leaving the local IA. // BRTransit: pkts leaving from the same BR different interface. - if p.hbirdPath.IsXover() { + if !p.peering && p.hbirdPath.IsXover() { if r, err := p.doHbirdXover(); err != nil { return r, err } @@ -517,7 +551,7 @@ func (p *scionPacketProcessor) processHBIRD() (processResult, error) { if r, err := p.deAggregateMac(); err != nil { return r, err } - if p.flyoverField.Flyover && p.hbirdPath.IsFirstHopAfterXover() { + if p.flyoverField.Flyover && p.hbirdPath.IsFirstHopAfterXover() && !p.peering { if err := p.reverseFlyoverXover(); err != nil { return processResult{}, err } @@ -547,31 +581,6 @@ func (p *scionPacketProcessor) processHBIRD() (processResult, error) { // Functions for SCMP packets preparation -func determinePeerHbird(pathMeta hummingbird.MetaHdr, inf path.InfoField) (bool, error) { - if !inf.Peer { - return false, nil - } - - if pathMeta.SegLen[0] == 0 { - return false, errPeeringEmptySeg0 - } - if pathMeta.SegLen[1] == 0 { - return false, errPeeringEmptySeg1 - - } - if pathMeta.SegLen[2] != 0 { - return false, errPeeringNonemptySeg2 - } - - // The peer hop fields are the last hop field on the first path - // segment (at SegLen[0] - 1) and the first hop field of the second - // path segment (at SegLen[0]). The below check applies only - // because we already know this is a well-formed peering path. - currHF := pathMeta.CurrHF - segLen := pathMeta.SegLen[0] - return currHF == segLen-3 || currHF == segLen, nil -} - func (p *slowPathPacketProcessor) prepareHbirdSCMP( typ slayers.SCMPType, code slayers.SCMPCode, diff --git a/router/dataplane_hbird_test.go b/router/dataplane_hbird_test.go index a9ba237aed..06f38be87c 100644 --- a/router/dataplane_hbird_test.go +++ b/router/dataplane_hbird_test.go @@ -52,10 +52,11 @@ func TestProcessHbirdPacket(t *testing.T) { now := time.Now() testCases := map[string]struct { - mockMsg func(bool) *ipv4.Message - prepareDP func(*gomock.Controller) *router.DataPlane - srcInterface uint16 - assertFunc assert.ErrorAssertionFunc + mockMsg func(bool) *ipv4.Message + prepareDP func(*gomock.Controller) *router.DataPlane + srcInterface uint16 + egressInterface uint16 + assertFunc assert.ErrorAssertionFunc }{ "inbound": { prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { @@ -82,8 +83,9 @@ func TestProcessHbirdPacket(t *testing.T) { } return ret }, - srcInterface: 1, - assertFunc: assert.NoError, + srcInterface: 1, + egressInterface: 0, + assertFunc: assert.NoError, }, "outbound": { prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { @@ -116,8 +118,9 @@ func TestProcessHbirdPacket(t *testing.T) { ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil return ret }, - srcInterface: 0, - assertFunc: assert.NoError, + srcInterface: 0, + egressInterface: 1, + assertFunc: assert.NoError, }, "brtransit": { prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { @@ -150,8 +153,9 @@ func TestProcessHbirdPacket(t *testing.T) { ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil return ret }, - srcInterface: 1, - assertFunc: assert.NoError, + srcInterface: 1, + egressInterface: 2, + assertFunc: assert.NoError, }, "brtransit non consdir": { prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { @@ -184,8 +188,9 @@ func TestProcessHbirdPacket(t *testing.T) { ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil return ret }, - srcInterface: 1, - assertFunc: assert.NoError, + srcInterface: 1, + egressInterface: 2, + assertFunc: assert.NoError, }, "astransit direct": { prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { @@ -214,8 +219,9 @@ func TestProcessHbirdPacket(t *testing.T) { } return ret }, - srcInterface: 1, - assertFunc: assert.NoError, + srcInterface: 1, + egressInterface: 0, + assertFunc: assert.NoError, }, "astransit xover": { prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { @@ -266,8 +272,9 @@ func TestProcessHbirdPacket(t *testing.T) { ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil return ret }, - srcInterface: 51, - assertFunc: assert.NoError, + srcInterface: 51, + egressInterface: 0, + assertFunc: assert.NoError, }, "invalid dest": { prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { @@ -291,6 +298,317 @@ func TestProcessHbirdPacket(t *testing.T) { srcInterface: 1, assertFunc: assert.Error, }, + "brtransit peering consdir": { + prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { + return router.NewDP( + map[uint16]router.BatchConn{ + uint16(2): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 1: topology.Peer, + 2: topology.Child, + }, + nil, nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + }, + mockMsg: func(afterProcessing bool) *ipv4.Message { + // Story: the packet just left segment 0 which ends at + // (peering) hop 0 and is landing on segment 1 which + // begins at (peering) hop 1. We do not care what hop 0 + // looks like. The forwarding code is looking at hop 1 and + // should leave the message in shape to be processed at hop 2. + spkt, _ := prepHbirdMsg(now) + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrHF: 3, + CurrINF: 1, + SegLen: [3]uint8{3, 6, 0}, + }, + NumINF: 2, + NumHops: 9, + }, + InfoFields: []path.InfoField{ + // up seg + {SegID: 0x111, ConsDir: true, Timestamp: util.TimeToSecs(now), Peer: true}, + // core seg + {SegID: 0x222, ConsDir: true, Timestamp: util.TimeToSecs(now), Peer: true}, + }, + HopFields: []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, + {HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}}, + {HopField: path.HopField{ConsIngress: 40, ConsEgress: 41}}, + }, + } + + // Make obvious the unusual aspect of the path: two + // hopfield MACs (1 and 2) derive from the same SegID + // accumulator value. However, the forwarding code isn't + // supposed to even look at the second one. The SegID + // accumulator value can be anything (it comes from the + // parent hop of HF[1] in the original beaconned segment, + // which is not in the path). So, we use one from an + // info field because computeMAC makes that easy. + dpath.HopFields[1].HopField.Mac = computeMAC( + t, key, dpath.InfoFields[1], dpath.HopFields[1].HopField) + dpath.HopFields[2].HopField.Mac = computeMAC( + t, sv, dpath.InfoFields[1], dpath.HopFields[2].HopField) + if !afterProcessing { + return toMsg(t, spkt, dpath) + } + _ = dpath.IncPath(3) + + // ... The SegID accumulator wasn't updated from HF[1], + // it is still the same. That is the key behavior. + + ret := toMsg(t, spkt, dpath) + ret.Addr = nil + ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil + return ret + }, + srcInterface: 1, // from peering link + egressInterface: 2, + assertFunc: assert.NoError, + }, + "brtransit peering non consdir": { + prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { + return router.NewDP( + map[uint16]router.BatchConn{ + uint16(1): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 1: topology.Peer, + 2: topology.Child, + }, + nil, nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + }, + mockMsg: func(afterProcessing bool) *ipv4.Message { + // Story: the packet lands on the last (peering) hop of + // segment 0. After processing, the packet is ready to + // be processed by the first (peering) hop of segment 1. + spkt, _ := prepHbirdMsg(now) + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrHF: 3, + CurrINF: 0, + SegLen: [3]uint8{6, 3, 0}, + }, + NumINF: 2, + NumHops: 9, + }, + InfoFields: []path.InfoField{ + // up seg + {SegID: 0x111, ConsDir: false, Timestamp: util.TimeToSecs(now), Peer: true}, + // down seg + {SegID: 0x222, ConsDir: true, Timestamp: util.TimeToSecs(now), Peer: true}, + }, + HopFields: []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, + {HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}}, + {HopField: path.HopField{ConsIngress: 40, ConsEgress: 41}}, + }, + } + + // Make obvious the unusual aspect of the path: two + // hopfield MACs (0 and 1) derive from the same SegID + // accumulator value. However, the forwarding code isn't + // supposed to even look at the first one. The SegID + // accumulator value can be anything (it comes from the + // parent hop of HF[1] in the original beaconned segment, + // which is not in the path). So, we use one from an + // info field because computeMAC makes that easy. + dpath.HopFields[0].HopField.Mac = computeMAC( + t, sv, dpath.InfoFields[0], dpath.HopFields[0].HopField) + dpath.HopFields[1].HopField.Mac = computeMAC( + t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + + // We're going against construction order, so the accumulator + // value is that of the previous hop in traversal order. The + // story starts with the packet arriving at hop 1, so the + // accumulator value must match hop field 0. In this case, + // it is identical to that for hop field 1, which we made + // identical to the original SegID. So, we're all set. + if !afterProcessing { + return toMsg(t, spkt, dpath) + } + + _ = dpath.IncPath(3) + + // The SegID should not get updated on arrival. If it is, then MAC validation + // of HF1 will fail. Otherwise, this isn't visible because we changed segment. + + ret := toMsg(t, spkt, dpath) + ret.Addr = nil + ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil + return ret + }, + srcInterface: 2, // from child link + egressInterface: 1, + assertFunc: assert.NoError, + }, + "peering consdir downstream": { + // Similar to previous test case but looking at what + // happens on the next hop. + prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { + return router.NewDP( + map[uint16]router.BatchConn{ + uint16(2): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 1: topology.Peer, + 2: topology.Child, + }, + nil, nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + }, + mockMsg: func(afterProcessing bool) *ipv4.Message { + // Story: the packet just left hop 1 (the first hop + // of peering down segment 1) and is processed at hop 2 + // which is not a peering hop. + spkt, _ := prepHbirdMsg(now) + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrHF: 6, + CurrINF: 1, + SegLen: [3]uint8{3, 9, 0}, + }, + NumINF: 2, + NumHops: 12, + }, + InfoFields: []path.InfoField{ + // up seg + {SegID: 0x111, ConsDir: true, Timestamp: util.TimeToSecs(now), Peer: true}, + // core seg + {SegID: 0x222, ConsDir: true, Timestamp: util.TimeToSecs(now), Peer: true}, + }, + HopFields: []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, + {HopField: path.HopField{ConsIngress: 40, ConsEgress: 41}}, + {HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}}, + {HopField: path.HopField{ConsIngress: 50, ConsEgress: 51}}, + // There has to be a 4th hop to make + // the 3rd router agree that the packet + // is not at destination yet. + }, + } + + // Make obvious the unusual aspect of the path: two + // hopfield MACs (1 and 2) derive from the same SegID + // accumulator value. The router shouldn't need to + // know this or do anything special. The SegID + // accumulator value can be anything (it comes from the + // parent hop of HF[1] in the original beaconned segment, + // which is not in the path). So, we use one from an + // info field because computeMAC makes that easy. + dpath.HopFields[1].HopField.Mac = computeMAC( + t, sv, dpath.InfoFields[1], dpath.HopFields[1].HopField) + dpath.HopFields[2].HopField.Mac = computeMAC( + t, key, dpath.InfoFields[1], dpath.HopFields[2].HopField) + if !afterProcessing { + // The SegID we provide is that of HF[2] which happens to be SEG[1]'s SegID, + // so, already set. + return toMsg(t, spkt, dpath) + } + _ = dpath.IncPath(3) + + // ... The SegID accumulator should have been updated. + dpath.InfoFields[1].UpdateSegID(dpath.HopFields[2].HopField.Mac) + + ret := toMsg(t, spkt, dpath) + ret.Addr = nil + ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil + return ret + }, + srcInterface: 1, + egressInterface: 2, + assertFunc: assert.NoError, + }, + "peering non consdir upstream": { + prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { + return router.NewDP( + map[uint16]router.BatchConn{ + uint16(1): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 1: topology.Peer, + 2: topology.Child, + }, + nil, nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + }, + mockMsg: func(afterProcessing bool) *ipv4.Message { + // Story: the packet lands on the second (non-peering) hop of + // segment 0 (a peering segment). After processing, the packet + // is ready to be processed by the third (peering) hop of segment 0. + spkt, _ := prepHbirdMsg(now) + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrHF: 3, + CurrINF: 0, + SegLen: [3]uint8{9, 3, 0}, + }, + NumINF: 2, + NumHops: 12, + }, + InfoFields: []path.InfoField{ + // up seg + {SegID: 0x111, ConsDir: false, Timestamp: util.TimeToSecs(now), Peer: true}, + // down seg + {SegID: 0x222, ConsDir: true, Timestamp: util.TimeToSecs(now), Peer: true}, + }, + HopFields: []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, + {HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}}, + {HopField: path.HopField{ConsIngress: 40, ConsEgress: 41}}, + {HopField: path.HopField{ConsIngress: 50, ConsEgress: 51}}, + // The second segment (4th hop) has to be + // there but the packet isn't processed + // at that hop for this test. + }, + } + + // Make obvious the unusual aspect of the path: two + // hopfield MACs (1 and 2) derive from the same SegID + // accumulator value. The SegID accumulator value can + // be anything (it comes from the parent hop of HF[1] + // in the original beaconned segment, which is not in + // the path). So, we use one from an info field because + // computeMAC makes that easy. + dpath.HopFields[1].HopField.Mac = computeMAC( + t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + dpath.HopFields[2].HopField.Mac = computeMAC( + t, sv, dpath.InfoFields[0], dpath.HopFields[2].HopField) + + if !afterProcessing { + // We're going against construction order, so the + // before-processing accumulator value is that of + // the previous hop in traversal order. The story + // starts with the packet arriving at hop 1, so the + // accumulator value must match hop field 0, which + // derives from hop field[1]. HopField[0]'s MAC is + // not checked during this test. + dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.Mac) + + return toMsg(t, spkt, dpath) + } + + _ = dpath.IncPath(3) + + // After-processing, the SegID should have been updated + // (on ingress) to be that of HF[1], which happens to be + // the Segment's SegID. That is what we already have as + // we only change it in the before-processing version + // of the packet. + + ret := toMsg(t, spkt, dpath) + ret.Addr = nil + ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil + return ret + }, + srcInterface: 2, // from child link + egressInterface: 1, + assertFunc: assert.NoError, + }, "inbound flyover": { prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { return router.NewDP(nil, nil, mock_router.NewMockBatchConn(ctrl), nil, @@ -319,8 +637,9 @@ func TestProcessHbirdPacket(t *testing.T) { } return toMsg(t, spkt, dpath) }, - srcInterface: 1, - assertFunc: assert.NoError, + srcInterface: 1, + egressInterface: 0, + assertFunc: assert.NoError, }, "outbound flyover": { prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { @@ -356,8 +675,9 @@ func TestProcessHbirdPacket(t *testing.T) { ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil return ret }, - srcInterface: 0, - assertFunc: assert.NoError, + srcInterface: 0, + egressInterface: 1, + assertFunc: assert.NoError, }, "reservation expired": { prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { @@ -404,7 +724,7 @@ func TestProcessHbirdPacket(t *testing.T) { spkt, dpath := prepHbirdMsg(now) dpath.HopFields = []hummingbird.FlyoverHopField{ {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, - {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}, Bw: 5, ResStartTime: 123, Duration: 304}, {HopField: path.HopField{ConsIngress: 40, ConsEgress: 41}}, } dpath.Base.PathMeta.SegLen[0] = 11 @@ -422,8 +742,9 @@ func TestProcessHbirdPacket(t *testing.T) { ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil return ret }, - srcInterface: 1, - assertFunc: assert.NoError, + srcInterface: 1, + egressInterface: 2, + assertFunc: assert.NoError, }, "brtransit non consdir flyover": { prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { @@ -461,8 +782,9 @@ func TestProcessHbirdPacket(t *testing.T) { ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil return ret }, - srcInterface: 1, - assertFunc: assert.NoError, + srcInterface: 1, + egressInterface: 2, + assertFunc: assert.NoError, }, "astransit direct flyover": { prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { @@ -495,8 +817,9 @@ func TestProcessHbirdPacket(t *testing.T) { } return toMsg(t, spkt, dpath) }, - srcInterface: 1, - assertFunc: assert.NoError, + srcInterface: 1, + egressInterface: 0, + assertFunc: assert.NoError, }, "astransit xover flyover non consdir": { // Test currently not working @@ -562,8 +885,9 @@ func TestProcessHbirdPacket(t *testing.T) { ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil return ret }, - srcInterface: 51, - assertFunc: assert.NoError, + srcInterface: 51, + egressInterface: 0, + assertFunc: assert.NoError, }, "astransit xover flyover ingress": { prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { @@ -632,8 +956,9 @@ func TestProcessHbirdPacket(t *testing.T) { ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil return ret }, - srcInterface: 51, - assertFunc: assert.NoError, + srcInterface: 51, + egressInterface: 0, + assertFunc: assert.NoError, }, "astransit xover flyover egress": { prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { @@ -704,116 +1029,1186 @@ func TestProcessHbirdPacket(t *testing.T) { ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil return ret }, - srcInterface: 0, - assertFunc: assert.NoError, + srcInterface: 0, + egressInterface: 31, + assertFunc: assert.NoError, }, - } - - for name, tc := range testCases { - name, tc := name, tc - if name == "astransit xover flyover non consdir" { - continue - } - t.Run(name, func(t *testing.T) { - t.Parallel() - dp := tc.prepareDP(ctrl) - input, want := tc.mockMsg(false), tc.mockMsg(true) - result, err := dp.ProcessPkt(tc.srcInterface, input) - tc.assertFunc(t, err) - if err != nil { - return - } - outPkt := &ipv4.Message{ - Buffers: [][]byte{result.OutPkt}, - Addr: result.OutAddr, - } - if result.OutAddr == nil { - outPkt.Addr = nil - } - assert.Equal(t, want, outPkt) - }) - } -} - -func TestHbirdPacketPath(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - key := []byte("testkey_xxxxxxxx") - sv := []byte("test_secretvalue") - now := time.Now() - - testCases := map[string]struct { - mockMsg func() *ipv4.Message - prepareDPs func(*gomock.Controller) []*router.DataPlane - srcInterfaces []uint16 - }{ - "two hops consdir": { - mockMsg: func() *ipv4.Message { - spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:111"), xtest.MustParseIA("1-ff00:0:110")) - dst := addr.MustParseHost("10.0.100.100") - _ = spkt.SetDstAddr(dst) - + "brtransit peering consdir flyovers": { + prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { + return router.NewDP( + map[uint16]router.BatchConn{ + uint16(2): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 1: topology.Peer, + 2: topology.Child, + }, + nil, nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + }, + mockMsg: func(afterProcessing bool) *ipv4.Message { + // Story: the packet just left segment 0 which ends at + // (peering) hop 0 and is landing on segment 1 which + // begins at (peering) hop 1. We do not care what hop 0 + // looks like. The forwarding code is looking at hop 1 and + // should leave the message in shape to be processed at hop 2. + spkt, _ := prepHbirdMsg(now) dpath := &hummingbird.Decoded{ Base: hummingbird.Base{ PathMeta: hummingbird.MetaHdr{ - CurrINF: 0, - CurrHF: 0, - SegLen: [3]uint8{6, 0, 0}, - BaseTS: util.TimeToSecs(now), - HighResTS: 500 << 22, + CurrHF: 3, + CurrINF: 1, + SegLen: [3]uint8{3, 8, 0}, + BaseTS: util.TimeToSecs(now), }, - NumINF: 1, - NumHops: 6, + NumINF: 2, + NumHops: 11, }, InfoFields: []path.InfoField{ - {SegID: 0x111, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + // up seg + {SegID: 0x111, ConsDir: true, Timestamp: util.TimeToSecs(now), Peer: true}, + // core seg + {SegID: 0x222, ConsDir: true, Timestamp: util.TimeToSecs(now), Peer: true}, }, - HopFields: []hummingbird.FlyoverHopField{ - {HopField: path.HopField{ConsIngress: 0, ConsEgress: 40}}, - {HopField: path.HopField{ConsIngress: 01, ConsEgress: 0}}, + {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, + {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}, Bw: 5, ResStartTime: 123, Duration: 304}, + {HopField: path.HopField{ConsIngress: 40, ConsEgress: 41}}, }, } - // Compute MACs and increase SegID while doing so - dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) - dpath.InfoFields[0].UpdateSegID(dpath.HopFields[0].HopField.Mac) - dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) - // Reset SegID to original value - dpath.InfoFields[0].SegID = 0x111 - ret := toMsg(t, spkt, dpath) - return ret - }, - prepareDPs: func(*gomock.Controller) []*router.DataPlane { - var dps [2]*router.DataPlane + // Make obvious the unusual aspect of the path: two + // hopfield MACs (1 and 2) derive from the same SegID + // accumulator value. However, the forwarding code isn't + // supposed to even look at the second one. The SegID + // accumulator value can be anything (it comes from the + // parent hop of HF[1] in the original beaconned segment, + // which is not in the path). So, we use one from an + // info field because computeMAC makes that easy. + dpath.HopFields[1].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, + dpath.InfoFields[1], dpath.HopFields[1], dpath.PathMeta) + dpath.HopFields[2].HopField.Mac = computeMAC( + t, sv, dpath.InfoFields[1], dpath.HopFields[2].HopField) + if !afterProcessing { + return toMsg(t, spkt, dpath) + } + _ = dpath.IncPath(5) + // deaggregate MAC + dpath.HopFields[1].HopField.Mac = computeMAC( + t, key, dpath.InfoFields[1], dpath.HopFields[1].HopField) + // ... The SegID accumulator wasn't updated from HF[1], + // it is still the same. That is the key behavior. + + ret := toMsg(t, spkt, dpath) + ret.Addr = nil + ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil + return ret + }, + srcInterface: 1, // from peering link + egressInterface: 2, + assertFunc: assert.NoError, + }, + "brtransit peering non consdir flyovers": { + prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { + return router.NewDP( + map[uint16]router.BatchConn{ + uint16(1): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 1: topology.Peer, + 2: topology.Child, + }, + nil, nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + }, + mockMsg: func(afterProcessing bool) *ipv4.Message { + // Story: the packet lands on the last (peering) hop of + // segment 0. After processing, the packet is ready to + // be processed by the first (peering) hop of segment 1. + spkt, _ := prepHbirdMsg(now) + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrHF: 3, + CurrINF: 0, + SegLen: [3]uint8{8, 3, 0}, + BaseTS: util.TimeToSecs(now), + }, + NumINF: 2, + NumHops: 11, + }, + InfoFields: []path.InfoField{ + // up seg + {SegID: 0x111, ConsDir: false, Timestamp: util.TimeToSecs(now), Peer: true}, + // down seg + {SegID: 0x222, ConsDir: true, Timestamp: util.TimeToSecs(now), Peer: true}, + }, + HopFields: []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, + {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}, Bw: 5, ResStartTime: 123, Duration: 304}, + {HopField: path.HopField{ConsIngress: 40, ConsEgress: 41}}, + }, + } + + // Make obvious the unusual aspect of the path: two + // hopfield MACs (0 and 1) derive from the same SegID + // accumulator value. However, the forwarding code isn't + // supposed to even look at the first one. The SegID + // accumulator value can be anything (it comes from the + // parent hop of HF[1] in the original beaconned segment, + // which is not in the path). So, we use one from an + // info field because computeMAC makes that easy. + dpath.HopFields[0].HopField.Mac = computeMAC( + t, sv, dpath.InfoFields[0], dpath.HopFields[0].HopField) + dpath.HopFields[1].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, + dpath.InfoFields[0], dpath.HopFields[1], dpath.PathMeta) + + // We're going against construction order, so the accumulator + // value is that of the previous hop in traversal order. The + // story starts with the packet arriving at hop 1, so the + // accumulator value must match hop field 0. In this case, + // it is identical to that for hop field 1, which we made + // identical to the original SegID. So, we're all set. + if !afterProcessing { + return toMsg(t, spkt, dpath) + } + + _ = dpath.IncPath(5) + // deaggregate MAc + dpath.HopFields[1].HopField.Mac = computeMAC( + t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + + // The SegID should not get updated on arrival. If it is, then MAC validation + // of HF1 will fail. Otherwise, this isn't visible because we changed segment. + + ret := toMsg(t, spkt, dpath) + ret.Addr = nil + ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil + return ret + }, + srcInterface: 2, // from child link + egressInterface: 1, + assertFunc: assert.NoError, + }, + "peering consdir downstream flyovers": { + // Similar to previous test case but looking at what + // happens on the next hop. + prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { + return router.NewDP( + map[uint16]router.BatchConn{ + uint16(2): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 1: topology.Peer, + 2: topology.Child, + }, + nil, nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + }, + mockMsg: func(afterProcessing bool) *ipv4.Message { + // Story: the packet just left hop 1 (the first hop + // of peering down segment 1) and is processed at hop 2 + // which is not a peering hop. + spkt, _ := prepHbirdMsg(now) + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrHF: 6, + CurrINF: 1, + SegLen: [3]uint8{3, 11, 0}, + BaseTS: util.TimeToSecs(now), + }, + NumINF: 2, + NumHops: 14, + }, + InfoFields: []path.InfoField{ + // up seg + {SegID: 0x111, ConsDir: true, Timestamp: util.TimeToSecs(now), Peer: true}, + // core seg + {SegID: 0x222, ConsDir: true, Timestamp: util.TimeToSecs(now), Peer: true}, + }, + HopFields: []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, + {HopField: path.HopField{ConsIngress: 40, ConsEgress: 41}}, + {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}, Bw: 5, ResStartTime: 123, Duration: 304}, + {HopField: path.HopField{ConsIngress: 50, ConsEgress: 51}}, + // There has to be a 4th hop to make + // the 3rd router agree that the packet + // is not at destination yet. + }, + } + + // Make obvious the unusual aspect of the path: two + // hopfield MACs (1 and 2) derive from the same SegID + // accumulator value. The router shouldn't need to + // know this or do anything special. The SegID + // accumulator value can be anything (it comes from the + // parent hop of HF[1] in the original beaconned segment, + // which is not in the path). So, we use one from an + // info field because computeMAC makes that easy. + dpath.HopFields[1].HopField.Mac = computeMAC( + t, sv, dpath.InfoFields[1], dpath.HopFields[1].HopField) + dpath.HopFields[2].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, + dpath.InfoFields[1], dpath.HopFields[2], dpath.PathMeta) + if !afterProcessing { + // The SegID we provide is that of HF[2] which happens to be SEG[1]'s SegID, + // so, already set. + return toMsg(t, spkt, dpath) + } + _ = dpath.IncPath(5) + // mac should be deaggregated, and used for updateSegID + dpath.HopFields[2].HopField.Mac = computeMAC( + t, key, dpath.InfoFields[1], dpath.HopFields[2].HopField) + + // ... The SegID accumulator should have been updated. + dpath.InfoFields[1].UpdateSegID(dpath.HopFields[2].HopField.Mac) + + ret := toMsg(t, spkt, dpath) + ret.Addr = nil + ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil + return ret + }, + srcInterface: 1, + egressInterface: 2, + assertFunc: assert.NoError, + }, + "peering non consdir upstream flyovers": { + prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { + return router.NewDP( + map[uint16]router.BatchConn{ + uint16(1): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 1: topology.Peer, + 2: topology.Child, + }, + nil, nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + }, + mockMsg: func(afterProcessing bool) *ipv4.Message { + // Story: the packet lands on the second (non-peering) hop of + // segment 0 (a peering segment). After processing, the packet + // is ready to be processed by the third (peering) hop of segment 0. + spkt, _ := prepHbirdMsg(now) + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrHF: 3, + CurrINF: 0, + SegLen: [3]uint8{11, 3, 0}, + BaseTS: util.TimeToSecs(now), + }, + NumINF: 2, + NumHops: 14, + }, + InfoFields: []path.InfoField{ + // up seg + {SegID: 0x111, ConsDir: false, Timestamp: util.TimeToSecs(now), Peer: true}, + // down seg + {SegID: 0x222, ConsDir: true, Timestamp: util.TimeToSecs(now), Peer: true}, + }, + HopFields: []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, + {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}, Bw: 5, ResStartTime: 123, Duration: 304}, + {HopField: path.HopField{ConsIngress: 40, ConsEgress: 41}}, + {HopField: path.HopField{ConsIngress: 50, ConsEgress: 51}}, + // The second segment (4th hop) has to be + // there but the packet isn't processed + // at that hop for this test. + }, + } + + // Make obvious the unusual aspect of the path: two + // hopfield MACs (1 and 2) derive from the same SegID + // accumulator value. The SegID accumulator value can + // be anything (it comes from the parent hop of HF[1] + // in the original beaconned segment, which is not in + // the path). So, we use one from an info field because + // computeMAC makes that easy. + dpath.HopFields[1].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, + dpath.InfoFields[0], dpath.HopFields[1], dpath.PathMeta) + dpath.HopFields[2].HopField.Mac = computeMAC( + t, sv, dpath.InfoFields[0], dpath.HopFields[2].HopField) + + if !afterProcessing { + // We're going against construction order, so the + // before-processing accumulator value is that of + // the previous hop in traversal order. The story + // starts with the packet arriving at hop 1, so the + // accumulator value must match hop field 0, which + // derives from hop field[1]. HopField[0]'s MAC is + // not checked during this test. + // Use de-aggregated MAC value for segID update + scionMac := computeMAC( + t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + dpath.InfoFields[0].UpdateSegID(scionMac) + + return toMsg(t, spkt, dpath) + } + + _ = dpath.IncPath(5) + // deaggregate MAC) + dpath.HopFields[1].HopField.Mac = computeMAC( + t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + + // After-processing, the SegID should have been updated + // (on ingress) to be that of HF[1], which happens to be + // the Segment's SegID. That is what we already have as + // we only change it in the before-processing version + // of the packet. + + ret := toMsg(t, spkt, dpath) + ret.Addr = nil + ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil + return ret + }, + srcInterface: 2, // from child link + egressInterface: 1, + assertFunc: assert.NoError, + }, + } + + for name, tc := range testCases { + name, tc := name, tc + if name == "astransit xover flyover non consdir" { + continue + } + t.Run(name, func(t *testing.T) { + t.Parallel() + dp := tc.prepareDP(ctrl) + input, want := tc.mockMsg(false), tc.mockMsg(true) + result, err := dp.ProcessPkt(tc.srcInterface, input) + tc.assertFunc(t, err) + if err != nil { + return + } + outPkt := &ipv4.Message{ + Buffers: [][]byte{result.OutPkt}, + Addr: result.OutAddr, + } + if result.OutAddr == nil { + outPkt.Addr = nil + } + assert.Equal(t, want, outPkt) + assert.Equal(t, tc.egressInterface, result.EgressID) + }) + } +} + +func TestHbirdPacketPath(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + key := []byte("testkey_xxxxxxxx") + sv := []byte("test_secretvalue") + now := time.Now() + + testCases := map[string]struct { + mockMsg func() *ipv4.Message + prepareDPs func(*gomock.Controller) []*router.DataPlane + srcInterfaces []uint16 + }{ + "two hops consdir": { + mockMsg: func() *ipv4.Message { + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:111"), xtest.MustParseIA("1-ff00:0:110")) + dst := addr.MustParseHost("10.0.100.100") + _ = spkt.SetDstAddr(dst) + + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrINF: 0, + CurrHF: 0, + SegLen: [3]uint8{6, 0, 0}, + BaseTS: util.TimeToSecs(now), + HighResTS: 500 << 22, + }, + NumINF: 1, + NumHops: 6, + }, + InfoFields: []path.InfoField{ + {SegID: 0x111, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + }, + + HopFields: []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 40}}, + {HopField: path.HopField{ConsIngress: 01, ConsEgress: 0}}, + }, + } + // Compute MACs and increase SegID while doing so + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + dpath.InfoFields[0].UpdateSegID(dpath.HopFields[0].HopField.Mac) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + // Reset SegID to original value + dpath.InfoFields[0].SegID = 0x111 + ret := toMsg(t, spkt, dpath) + return ret + + }, + prepareDPs: func(*gomock.Controller) []*router.DataPlane { + var dps [2]*router.DataPlane + dps[0] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(40): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 40: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) + + dps[1] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(01): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 01: topology.Child, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + + return dps[:] + }, + srcInterfaces: []uint16{0, 01}, + }, + "two hops non consdir": { + mockMsg: func() *ipv4.Message { + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), xtest.MustParseIA("1-ff00:0:111")) + dst := addr.MustParseHost("10.0.100.100") + _ = spkt.SetDstAddr(dst) + + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrINF: 0, + CurrHF: 0, + SegLen: [3]uint8{6, 0, 0}, + BaseTS: util.TimeToSecs(now), + HighResTS: 500 << 22, + }, + NumINF: 1, + NumHops: 6, + }, + InfoFields: []path.InfoField{ + {SegID: 0x111, ConsDir: false, Timestamp: util.TimeToSecs(now)}, + }, + + HopFields: []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 01, ConsEgress: 0}}, + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 40}}, + }, + } + // Compute MACs and increase SegID while doing so + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.Mac) + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + //dpath.InfoFields[0].UpdateSegID(dpath.HopFields[0].HopField.Mac) + + ret := toMsg(t, spkt, dpath) + return ret + + }, + prepareDPs: func(*gomock.Controller) []*router.DataPlane { + var dps [2]*router.DataPlane + dps[0] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(01): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 01: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + + dps[1] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(40): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 40: topology.Child, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) + + return dps[:] + }, + srcInterfaces: []uint16{0, 40}, + }, + "six hops astransit xover consdir": { + mockMsg: func() *ipv4.Message { + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:111"), xtest.MustParseIA("3-ff00:0:333")) + dst := addr.MustParseHost("10.0.100.100") + _ = spkt.SetDstAddr(dst) + + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrINF: 0, + CurrHF: 0, + SegLen: [3]uint8{9, 9, 0}, + BaseTS: util.TimeToSecs(now), + HighResTS: 500 << 22, + }, + NumINF: 2, + NumHops: 18, + }, + InfoFields: []path.InfoField{ + {SegID: 0x111, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + {SegID: 0x222, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + }, + + HopFields: []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 40}}, + {HopField: path.HopField{ConsIngress: 1, ConsEgress: 31}}, + {HopField: path.HopField{ConsIngress: 5, ConsEgress: 0}}, + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 7}}, + {HopField: path.HopField{ConsIngress: 11, ConsEgress: 8}}, + {HopField: path.HopField{ConsIngress: 3, ConsEgress: 0}}, + }, + } + // Compute MACs and increase SegID while doing so + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + dpath.InfoFields[0].UpdateSegID(dpath.HopFields[0].HopField.Mac) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.Mac) + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[2].HopField) + + dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3].HopField) + dpath.InfoFields[1].UpdateSegID(dpath.HopFields[3].HopField.Mac) + dpath.HopFields[4].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[4].HopField) + dpath.InfoFields[1].UpdateSegID(dpath.HopFields[4].HopField.Mac) + dpath.HopFields[5].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[5].HopField) + // Reset SegID to original value + dpath.InfoFields[0].SegID = 0x111 + dpath.InfoFields[1].SegID = 0x222 + ret := toMsg(t, spkt, dpath) + return ret + }, + prepareDPs: func(*gomock.Controller) []*router.DataPlane { + var dps [7]*router.DataPlane + dps[0] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(40): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 40: topology.Parent, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) + + dps[1] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(31): mock_router.NewMockBatchConn(ctrl), + uint16(1): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 31: topology.Parent, + 1: topology.Child, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:112"), nil, key, sv) + + dps[2] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(5): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 5: topology.Child, + 7: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(7): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:113"), nil, key, sv) + + dps[3] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(7): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 5: topology.Child, + 7: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(5): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:113"), nil, key, sv) + + dps[4] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(11): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 8: topology.Core, + 11: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(8): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("2-ff00:0:222"), nil, key, sv) + + dps[5] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(8): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 8: topology.Core, + 11: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(11): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("2-ff00:0:222"), nil, key, sv) + + dps[6] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(3): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 3: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("3-ff00:0:333"), nil, key, sv) + + return dps[:] + }, // middle hop of second segment is astransit + srcInterfaces: []uint16{0, 1, 5, 0, 11, 0, 3}, + }, + "six hops astransit xover non consdir": { + mockMsg: func() *ipv4.Message { + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:111"), xtest.MustParseIA("3-ff00:0:333")) + dst := addr.MustParseHost("10.0.100.100") + _ = spkt.SetDstAddr(dst) + + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrINF: 0, + CurrHF: 0, + SegLen: [3]uint8{9, 9, 0}, + BaseTS: util.TimeToSecs(now), + HighResTS: 500 << 22, + }, + NumINF: 2, + NumHops: 18, + }, + InfoFields: []path.InfoField{ + {SegID: 0x111, ConsDir: false, Timestamp: util.TimeToSecs(now)}, + {SegID: 0x222, ConsDir: false, Timestamp: util.TimeToSecs(now)}, + }, + + HopFields: []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 40, ConsEgress: 0}}, + {HopField: path.HopField{ConsIngress: 31, ConsEgress: 1}}, + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 5}}, + {HopField: path.HopField{ConsIngress: 7, ConsEgress: 0}}, + {HopField: path.HopField{ConsIngress: 8, ConsEgress: 11}}, + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 3}}, + }, + } + // Compute MACs and increase SegID while doing so + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[2].HopField) + dpath.InfoFields[0].UpdateSegID(dpath.HopFields[2].HopField.Mac) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.Mac) + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + + dpath.HopFields[5].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[5].HopField) + dpath.InfoFields[1].UpdateSegID(dpath.HopFields[5].HopField.Mac) + dpath.HopFields[4].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[4].HopField) + dpath.InfoFields[1].UpdateSegID(dpath.HopFields[4].HopField.Mac) + dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3].HopField) + // Reset SegID to original value + ret := toMsg(t, spkt, dpath) + return ret + }, + prepareDPs: func(*gomock.Controller) []*router.DataPlane { + var dps [7]*router.DataPlane + dps[0] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(40): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 40: topology.Parent, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) + + dps[1] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(31): mock_router.NewMockBatchConn(ctrl), + uint16(1): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 31: topology.Parent, + 1: topology.Child, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:112"), nil, key, sv) + + dps[2] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(5): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 5: topology.Child, + 7: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(7): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:113"), nil, key, sv) + + dps[3] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(7): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 5: topology.Child, + 7: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(5): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:113"), nil, key, sv) + + dps[4] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(11): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 8: topology.Core, + 11: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(8): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("2-ff00:0:222"), nil, key, sv) + + dps[5] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(8): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 8: topology.Core, + 11: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(11): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("2-ff00:0:222"), nil, key, sv) + + dps[6] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(3): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 3: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("3-ff00:0:333"), nil, key, sv) + + return dps[:] + }, // middle hop of second segment is astransit + srcInterfaces: []uint16{0, 1, 5, 0, 11, 0, 3}, + }, + "six hops three segs mixed consdir": { + // two crossovers, first crossover is brtransit, second one is astransit + // core segment is non consdir + mockMsg: func() *ipv4.Message { + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), xtest.MustParseIA("1-ff00:0:113")) + dst := addr.MustParseHost("10.0.100.100") + _ = spkt.SetDstAddr(dst) + + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrINF: 0, + CurrHF: 0, + SegLen: [3]uint8{6, 6, 6}, + BaseTS: util.TimeToSecs(now), + HighResTS: 500 << 22, + }, + NumINF: 3, + NumHops: 18, + }, + InfoFields: []path.InfoField{ + {SegID: 0x111, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + {SegID: 0x222, ConsDir: false, Timestamp: util.TimeToSecs(now)}, + {SegID: 0x333, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + }, + + HopFields: []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 40}}, + {HopField: path.HopField{ConsIngress: 1, ConsEgress: 0}}, + {HopField: path.HopField{ConsIngress: 5, ConsEgress: 0}}, + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 31}}, + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 8}}, + {HopField: path.HopField{ConsIngress: 3, ConsEgress: 0}}, + }, + } + // Compute MACs and increase SegID while doing so + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + dpath.InfoFields[0].UpdateSegID(dpath.HopFields[0].HopField.Mac) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + + dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3].HopField) + dpath.InfoFields[1].UpdateSegID(dpath.HopFields[3].HopField.Mac) + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[2].HopField) + + dpath.HopFields[4].HopField.Mac = computeMAC(t, key, dpath.InfoFields[2], dpath.HopFields[4].HopField) + dpath.InfoFields[2].UpdateSegID(dpath.HopFields[4].HopField.Mac) + dpath.HopFields[5].HopField.Mac = computeMAC(t, key, dpath.InfoFields[2], dpath.HopFields[5].HopField) + // Reset SegID to original value + dpath.InfoFields[0].SegID = 0x111 + dpath.InfoFields[2].SegID = 0x333 + ret := toMsg(t, spkt, dpath) + return ret + }, + prepareDPs: func(*gomock.Controller) []*router.DataPlane { + var dps [5]*router.DataPlane + dps[0] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(40): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 40: topology.Parent, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + dps[1] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(1): mock_router.NewMockBatchConn(ctrl), + uint16(5): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 1: topology.Child, + 5: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) + dps[2] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(31): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 8: topology.Child, + 31: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(8): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:112"), nil, key, sv) + dps[3] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(8): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 8: topology.Child, + 31: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(31): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:112"), nil, key, sv) + dps[4] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(3): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 3: topology.Parent, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:113"), nil, key, sv) + return dps[:] + }, + srcInterfaces: []uint16{0, 1, 31, 0, 3}, + }, + "two hops consdir flyovers": { + mockMsg: func() *ipv4.Message { + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:111"), xtest.MustParseIA("1-ff00:0:110")) + dst := addr.MustParseHost("10.0.100.100") + _ = spkt.SetDstAddr(dst) + + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrINF: 0, + CurrHF: 0, + SegLen: [3]uint8{10, 0, 0}, + BaseTS: util.TimeToSecs(now), + HighResTS: 500 << 22, + }, + NumINF: 1, + NumHops: 10, + }, + InfoFields: []path.InfoField{ + {SegID: 0x111, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + }, + + HopFields: []hummingbird.FlyoverHopField{ + {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 40}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 01, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, + }, + } + // Compute MACs and increase SegID while doing so + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + dpath.InfoFields[0].UpdateSegID(dpath.HopFields[0].HopField.Mac) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + // add flyover macs + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 0, 40, + dpath.InfoFields[0], &dpath.HopFields[0], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 1, 0, + dpath.InfoFields[0], &dpath.HopFields[1], dpath.PathMeta) + // Reset SegID to original value + dpath.InfoFields[0].SegID = 0x111 + ret := toMsg(t, spkt, dpath) + return ret + + }, + prepareDPs: func(*gomock.Controller) []*router.DataPlane { + var dps [2]*router.DataPlane + dps[0] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(40): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 40: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) + + dps[1] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(01): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 01: topology.Child, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + + return dps[:] + }, + srcInterfaces: []uint16{0, 01}, + }, + "two hops non consdir flyovers": { + mockMsg: func() *ipv4.Message { + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), xtest.MustParseIA("1-ff00:0:111")) + dst := addr.MustParseHost("10.0.100.100") + _ = spkt.SetDstAddr(dst) + + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrINF: 0, + CurrHF: 0, + SegLen: [3]uint8{10, 0, 0}, + BaseTS: util.TimeToSecs(now), + HighResTS: 500 << 22, + }, + NumINF: 1, + NumHops: 10, + }, + InfoFields: []path.InfoField{ + {SegID: 0x111, ConsDir: false, Timestamp: util.TimeToSecs(now)}, + }, + + HopFields: []hummingbird.FlyoverHopField{ + {Flyover: true, HopField: path.HopField{ConsIngress: 01, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 40}, Bw: 5, ResStartTime: 123, Duration: 304}, + }, + } + // Compute MACs and increase SegID while doing so + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.Mac) + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + // aggregate macs + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 0, 1, + dpath.InfoFields[0], &dpath.HopFields[0], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 40, 0, + dpath.InfoFields[0], &dpath.HopFields[1], dpath.PathMeta) + + ret := toMsg(t, spkt, dpath) + return ret + + }, + prepareDPs: func(*gomock.Controller) []*router.DataPlane { + var dps [2]*router.DataPlane dps[0] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(01): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 01: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + + dps[1] = router.NewDP( map[uint16]router.BatchConn{ uint16(40): mock_router.NewMockBatchConn(ctrl), }, map[uint16]topology.LinkType{ - 40: topology.Core, + 40: topology.Child, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) + + return dps[:] + }, + srcInterfaces: []uint16{0, 40}, + }, + "six hops astransit xover consdir flyovers": { + mockMsg: func() *ipv4.Message { + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:111"), xtest.MustParseIA("3-ff00:0:333")) + dst := addr.MustParseHost("10.0.100.100") + _ = spkt.SetDstAddr(dst) + + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrINF: 0, + CurrHF: 0, + SegLen: [3]uint8{15, 13, 0}, + BaseTS: util.TimeToSecs(now), + HighResTS: 500 << 22, + }, + NumINF: 2, + NumHops: 28, + }, + InfoFields: []path.InfoField{ + {SegID: 0x111, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + {SegID: 0x222, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + }, + + HopFields: []hummingbird.FlyoverHopField{ + {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 40}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 31}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 5, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 7}}, + {Flyover: true, HopField: path.HopField{ConsIngress: 11, ConsEgress: 8}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 3, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, + }, + } + // Compute MACs and increase SegID while doing so + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + dpath.InfoFields[0].UpdateSegID(dpath.HopFields[0].HopField.Mac) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.Mac) + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[2].HopField) + + dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3].HopField) + dpath.InfoFields[1].UpdateSegID(dpath.HopFields[3].HopField.Mac) + dpath.HopFields[4].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[4].HopField) + dpath.InfoFields[1].UpdateSegID(dpath.HopFields[4].HopField.Mac) + dpath.HopFields[5].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[5].HopField) + // Reset SegID to original value + dpath.InfoFields[0].SegID = 0x111 + dpath.InfoFields[1].SegID = 0x222 + // aggregate flyover macs + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 0, 40, + dpath.InfoFields[0], &dpath.HopFields[0], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 1, 31, + dpath.InfoFields[0], &dpath.HopFields[1], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 5, 7, + dpath.InfoFields[0], &dpath.HopFields[2], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 11, 8, + dpath.InfoFields[1], &dpath.HopFields[4], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 3, 0, + dpath.InfoFields[1], &dpath.HopFields[5], dpath.PathMeta) + ret := toMsg(t, spkt, dpath) + return ret + }, + prepareDPs: func(*gomock.Controller) []*router.DataPlane { + var dps [7]*router.DataPlane + dps[0] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(40): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 40: topology.Parent, }, mock_router.NewMockBatchConn(ctrl), nil, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) dps[1] = router.NewDP( map[uint16]router.BatchConn{ - uint16(01): mock_router.NewMockBatchConn(ctrl), + uint16(31): mock_router.NewMockBatchConn(ctrl), + uint16(1): mock_router.NewMockBatchConn(ctrl), }, map[uint16]topology.LinkType{ - 01: topology.Child, + 31: topology.Parent, + 1: topology.Child, }, mock_router.NewMockBatchConn(ctrl), - nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + nil, nil, xtest.MustParseIA("1-ff00:0:112"), nil, key, sv) + + dps[2] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(5): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 5: topology.Child, + 7: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(7): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:113"), nil, key, sv) + + dps[3] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(7): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 5: topology.Child, + 7: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(5): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:113"), nil, key, sv) + + dps[4] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(11): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 8: topology.Core, + 11: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(8): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("2-ff00:0:222"), nil, key, sv) + + dps[5] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(8): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 8: topology.Core, + 11: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(11): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("2-ff00:0:222"), nil, key, sv) + + dps[6] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(3): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 3: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("3-ff00:0:333"), nil, key, sv) return dps[:] - }, - srcInterfaces: []uint16{0, 01}, + }, // middle hop of second segment is astransit + srcInterfaces: []uint16{0, 1, 5, 0, 11, 0, 3}, }, - "two hops non consdir": { + "six hops astransit xover non consdir flyovers": { mockMsg: func() *ipv4.Message { - spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), xtest.MustParseIA("1-ff00:0:111")) + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:111"), xtest.MustParseIA("3-ff00:0:333")) dst := addr.MustParseHost("10.0.100.100") _ = spkt.SetDstAddr(dst) @@ -822,64 +2217,270 @@ func TestHbirdPacketPath(t *testing.T) { PathMeta: hummingbird.MetaHdr{ CurrINF: 0, CurrHF: 0, - SegLen: [3]uint8{6, 0, 0}, + SegLen: [3]uint8{15, 13, 0}, BaseTS: util.TimeToSecs(now), HighResTS: 500 << 22, }, - NumINF: 1, - NumHops: 6, + NumINF: 2, + NumHops: 28, }, InfoFields: []path.InfoField{ {SegID: 0x111, ConsDir: false, Timestamp: util.TimeToSecs(now)}, + {SegID: 0x222, ConsDir: false, Timestamp: util.TimeToSecs(now)}, }, HopFields: []hummingbird.FlyoverHopField{ - {HopField: path.HopField{ConsIngress: 01, ConsEgress: 0}}, - {HopField: path.HopField{ConsIngress: 0, ConsEgress: 40}}, + {Flyover: true, HopField: path.HopField{ConsIngress: 40, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 31, ConsEgress: 1}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 5}, Bw: 5, ResStartTime: 123, Duration: 304}, + {HopField: path.HopField{ConsIngress: 7, ConsEgress: 0}}, + {Flyover: true, HopField: path.HopField{ConsIngress: 8, ConsEgress: 11}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 3}, Bw: 5, ResStartTime: 123, Duration: 304}, }, } // Compute MACs and increase SegID while doing so + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[2].HopField) + dpath.InfoFields[0].UpdateSegID(dpath.HopFields[2].HopField.Mac) dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.Mac) dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) - //dpath.InfoFields[0].UpdateSegID(dpath.HopFields[0].HopField.Mac) + dpath.HopFields[5].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[5].HopField) + dpath.InfoFields[1].UpdateSegID(dpath.HopFields[5].HopField.Mac) + dpath.HopFields[4].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[4].HopField) + dpath.InfoFields[1].UpdateSegID(dpath.HopFields[4].HopField.Mac) + dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3].HopField) + // aggregate with flyover macs + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 0, 40, + dpath.InfoFields[0], &dpath.HopFields[0], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 1, 31, + dpath.InfoFields[0], &dpath.HopFields[1], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 5, 7, + dpath.InfoFields[0], &dpath.HopFields[2], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 11, 8, + dpath.InfoFields[1], &dpath.HopFields[4], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 3, 0, + dpath.InfoFields[1], &dpath.HopFields[5], dpath.PathMeta) ret := toMsg(t, spkt, dpath) return ret - }, prepareDPs: func(*gomock.Controller) []*router.DataPlane { - var dps [2]*router.DataPlane + var dps [7]*router.DataPlane dps[0] = router.NewDP( map[uint16]router.BatchConn{ - uint16(01): mock_router.NewMockBatchConn(ctrl), + uint16(40): mock_router.NewMockBatchConn(ctrl), }, map[uint16]topology.LinkType{ - 01: topology.Core, + 40: topology.Parent, }, mock_router.NewMockBatchConn(ctrl), - nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + nil, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) dps[1] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(31): mock_router.NewMockBatchConn(ctrl), + uint16(1): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 31: topology.Parent, + 1: topology.Child, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:112"), nil, key, sv) + + dps[2] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(5): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 5: topology.Child, + 7: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(7): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:113"), nil, key, sv) + + dps[3] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(7): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 5: topology.Child, + 7: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(5): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:113"), nil, key, sv) + + dps[4] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(11): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 8: topology.Core, + 11: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(8): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("2-ff00:0:222"), nil, key, sv) + + dps[5] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(8): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 8: topology.Core, + 11: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(11): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("2-ff00:0:222"), nil, key, sv) + + dps[6] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(3): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 3: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("3-ff00:0:333"), nil, key, sv) + + return dps[:] + }, // middle hop of second segment is astransit + srcInterfaces: []uint16{0, 1, 5, 0, 11, 0, 3}, + }, + "six hops three segs mixed consdir flyovers": { + // two crossovers, first crossover is brtransit, second one is astransit + // core segment is non consdir + mockMsg: func() *ipv4.Message { + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), xtest.MustParseIA("1-ff00:0:113")) + dst := addr.MustParseHost("10.0.100.100") + _ = spkt.SetDstAddr(dst) + + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrINF: 0, + CurrHF: 0, + SegLen: [3]uint8{10, 8, 8}, + BaseTS: util.TimeToSecs(now), + HighResTS: 500 << 22, + }, + NumINF: 3, + NumHops: 26, + }, + InfoFields: []path.InfoField{ + {SegID: 0x111, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + {SegID: 0x222, ConsDir: false, Timestamp: util.TimeToSecs(now)}, + {SegID: 0x333, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + }, + + HopFields: []hummingbird.FlyoverHopField{ + {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 40}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, + {HopField: path.HopField{ConsIngress: 5, ConsEgress: 0}}, + {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 31}, Bw: 5, ResStartTime: 123, Duration: 304}, + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 8}}, + {Flyover: true, HopField: path.HopField{ConsIngress: 3, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, + }, + } + // Compute MACs and increase SegID while doing so + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + dpath.InfoFields[0].UpdateSegID(dpath.HopFields[0].HopField.Mac) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + + dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3].HopField) + dpath.InfoFields[1].UpdateSegID(dpath.HopFields[3].HopField.Mac) + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[2].HopField) + + dpath.HopFields[4].HopField.Mac = computeMAC(t, key, dpath.InfoFields[2], dpath.HopFields[4].HopField) + dpath.InfoFields[2].UpdateSegID(dpath.HopFields[4].HopField.Mac) + dpath.HopFields[5].HopField.Mac = computeMAC(t, key, dpath.InfoFields[2], dpath.HopFields[5].HopField) + // Reset SegID to original value + dpath.InfoFields[0].SegID = 0x111 + dpath.InfoFields[2].SegID = 0x333 + // aggregate flyover macs + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 0, 40, + dpath.InfoFields[0], &dpath.HopFields[0], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 1, 5, + dpath.InfoFields[0], &dpath.HopFields[1], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 31, 8, + dpath.InfoFields[1], &dpath.HopFields[3], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 3, 0, + dpath.InfoFields[2], &dpath.HopFields[5], dpath.PathMeta) + ret := toMsg(t, spkt, dpath) + return ret + }, + prepareDPs: func(*gomock.Controller) []*router.DataPlane { + var dps [5]*router.DataPlane + dps[0] = router.NewDP( map[uint16]router.BatchConn{ uint16(40): mock_router.NewMockBatchConn(ctrl), }, map[uint16]topology.LinkType{ - 40: topology.Child, + 40: topology.Parent, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + dps[1] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(1): mock_router.NewMockBatchConn(ctrl), + uint16(5): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 1: topology.Child, + 5: topology.Core, }, mock_router.NewMockBatchConn(ctrl), nil, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) - + dps[2] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(31): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 8: topology.Child, + 31: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(8): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:112"), nil, key, sv) + dps[3] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(8): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 8: topology.Child, + 31: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(31): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:112"), nil, key, sv) + dps[4] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(3): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 3: topology.Parent, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:113"), nil, key, sv) return dps[:] }, - srcInterfaces: []uint16{0, 40}, + srcInterfaces: []uint16{0, 1, 31, 0, 3}, }, } for name, tc := range testCases { - if name != "two hops non consdir" { //Only run test we want to debug - continue - } + // if name != "six hops astransit xover non consdir" { //Only run test we want to debug + // continue + // } name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -899,71 +2500,6 @@ func TestHbirdPacketPath(t *testing.T) { } } -// func TestFlyoverPathReverseLength(t *testing.T) { -// //Performs test by provoking an error and checking the length of the returned SCMP packet -// //Does this with two different length paths, which should be reduced to identical length due to flyover removal -// //TODO: Fix scmp packets for hbirdpath to make test work -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() - -// key := []byte("testkey_xxxxxxxx") -// sv := []byte("test_secretvalue") -// now := time.Now() - -// //prepare datalane -// dp := router.NewDP(nil, nil, mock_router.NewMockBatchConn(ctrl), nil, -// nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) - -// //prepare input message 1 -// spkt, dpath := prepHbirdMsg(now) -// spkt.DstIA = xtest.MustParseIA("1-ff00:0:110") -// dst := addr.MustParseHost("10.0.100.100") -// _ = spkt.SetDstAddr(dst) -// dpath.HopFields = []hummingbird.FlyoverHopField{ -// {Flyover: true, HopField: path.HopField{ConsIngress: 41, ConsEgress: 40}, ResStartTime: 5, Duration: 200}, -// {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, -// {Flyover: true, HopField: path.HopField{ConsIngress: 01, ConsEgress: 0}, ResStartTime: 5, Duration: 2}, -// } -// dpath.Base.PathMeta.SegLen[0] = 13 -// dpath.Base.NumHops = 13 -// dpath.Base.PathMeta.CurrHF = 8 -// dpath.HopFields[2].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, dpath.InfoFields[0], dpath.HopFields[2], dpath.PathMeta) -// inputLong := toMsg(t, spkt, dpath) - -// //input message 2 -// spkt, dpath = prepHbirdMsg(now) -// spkt.DstIA = xtest.MustParseIA("1-ff00:0:110") -// dst = addr.MustParseHost("10.0.100.100") -// _ = spkt.SetDstAddr(dst) -// dpath.HopFields = []hummingbird.FlyoverHopField{ -// {HopField: path.HopField{ConsIngress: 41, ConsEgress: 40}}, -// {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, -// {Flyover: true, HopField: path.HopField{ConsIngress: 01, ConsEgress: 0}, ResStartTime: 5, Duration: 2}, -// } -// dpath.Base.PathMeta.SegLen[0] = 11 -// dpath.Base.NumHops = 11 -// dpath.Base.PathMeta.CurrHF = 6 -// dpath.HopFields[2].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, dpath.InfoFields[0], dpath.HopFields[2], dpath.PathMeta) -// inputShort := toMsg(t, spkt, dpath) -// fmt.Printf("inputShort: %x\n", inputShort) -// fmt.Printf("inputLong: %x\n", inputLong) - -// //set src interface -// var srcInterface uint16 = 1 -// res, _ := dp.ProcessPkt(srcInterface, inputLong) -// res2, _ := dp.ProcessPkt(srcInterface, inputShort) - -// layer1 := slayers.SCION{} -// layer1.RecyclePaths() -// layer1.DecodeFromBytes(res.OutPkt, gopacket.NilDecodeFeedback) - -// layer2 := slayers.SCION{} -// layer2.RecyclePaths() -// layer2.DecodeFromBytes(res2.OutPkt, gopacket.NilDecodeFeedback) -// assert.Equal(t, layer1.Path.Len(), layer2.Path.Len()) -// assert.Equal(t, 56, layer1.Path.Len()) -// } - func prepHbirdMsg(now time.Time) (*slayers.SCION, *hummingbird.Decoded) { spkt := &slayers.SCION{ Version: 0, @@ -1056,3 +2592,19 @@ func computeAggregateMacXover(t *testing.T, key, sv []byte, dst addr.IA, l, hin, } return scionMac } + +// Computes flyovermac and aggregates it to existing mac in hopfield +func aggregateOntoScionMac(t *testing.T, sv []byte, dst addr.IA, l, hin, heg uint16, info path.InfoField, hf *hummingbird.FlyoverHopField, meta hummingbird.MetaHdr) { + block, err := aes.NewCipher(sv) + require.NoError(t, err) + ingress, egress := hin, heg + + ak := hummingbird.DeriveAuthKey(block, hf.ResID, hf.Bw, ingress, egress, + meta.BaseTS-uint32(hf.ResStartTime), hf.Duration, nil) + flyoverMac := hummingbird.FullFlyoverMac(ak, dst, l, hf.ResStartTime, + meta.HighResTS, nil, nil) + + for i := range hf.HopField.Mac { + hf.HopField.Mac[i] ^= flyoverMac[i] + } +} From 80cfc36721ecd9b2add3bde4bb56b685411ecdbe Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Wed, 8 Nov 2023 10:41:33 +0100 Subject: [PATCH 014/100] added additional unti tests for peering --- router/dataplane_hbird_test.go | 1261 ++++++++++++++++++++++++++------ 1 file changed, 1017 insertions(+), 244 deletions(-) diff --git a/router/dataplane_hbird_test.go b/router/dataplane_hbird_test.go index 06f38be87c..6240040f74 100644 --- a/router/dataplane_hbird_test.go +++ b/router/dataplane_hbird_test.go @@ -1920,9 +1920,9 @@ func TestHbirdPacketPath(t *testing.T) { }, srcInterfaces: []uint16{0, 1, 31, 0, 3}, }, - "two hops consdir flyovers": { + "three hops peering brtransit consdir": { mockMsg: func() *ipv4.Message { - spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:111"), xtest.MustParseIA("1-ff00:0:110")) + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), xtest.MustParseIA("1-ff00:0:113")) dst := addr.MustParseHost("10.0.100.100") _ = spkt.SetDstAddr(dst) @@ -1931,66 +1931,72 @@ func TestHbirdPacketPath(t *testing.T) { PathMeta: hummingbird.MetaHdr{ CurrINF: 0, CurrHF: 0, - SegLen: [3]uint8{10, 0, 0}, + SegLen: [3]uint8{3, 6}, BaseTS: util.TimeToSecs(now), HighResTS: 500 << 22, }, - NumINF: 1, - NumHops: 10, + NumINF: 2, + NumHops: 9, }, InfoFields: []path.InfoField{ - {SegID: 0x111, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + {SegID: 0x111, Peer: true, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + {SegID: 0x222, Peer: true, ConsDir: true, Timestamp: util.TimeToSecs(now)}, }, HopFields: []hummingbird.FlyoverHopField{ - {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 40}, Bw: 5, ResStartTime: 123, Duration: 304}, - {Flyover: true, HopField: path.HopField{ConsIngress: 01, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 40}}, + {HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}}, + {HopField: path.HopField{ConsIngress: 5, ConsEgress: 0}}, }, } // Compute MACs and increase SegID while doing so dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) - dpath.InfoFields[0].UpdateSegID(dpath.HopFields[0].HopField.Mac) - dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) - // add flyover macs - aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 0, 40, - dpath.InfoFields[0], &dpath.HopFields[0], dpath.PathMeta) - aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 1, 0, - dpath.InfoFields[0], &dpath.HopFields[1], dpath.PathMeta) - // Reset SegID to original value - dpath.InfoFields[0].SegID = 0x111 + + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[1].HopField) + // No Segment update here as the second hop of a peering path uses the same segID as it's following hop + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[2].HopField) + ret := toMsg(t, spkt, dpath) return ret - }, prepareDPs: func(*gomock.Controller) []*router.DataPlane { - var dps [2]*router.DataPlane + var dps [3]*router.DataPlane dps[0] = router.NewDP( map[uint16]router.BatchConn{ uint16(40): mock_router.NewMockBatchConn(ctrl), }, map[uint16]topology.LinkType{ - 40: topology.Core, + 40: topology.Peer, }, mock_router.NewMockBatchConn(ctrl), - nil, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) - + nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) dps[1] = router.NewDP( map[uint16]router.BatchConn{ - uint16(01): mock_router.NewMockBatchConn(ctrl), + uint16(2): mock_router.NewMockBatchConn(ctrl), + uint16(1): mock_router.NewMockBatchConn(ctrl), }, map[uint16]topology.LinkType{ - 01: topology.Child, + 1: topology.Peer, + 2: topology.Child, }, mock_router.NewMockBatchConn(ctrl), - nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) - + nil, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) + dps[2] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(5): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 5: topology.Parent, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:113"), nil, key, sv) return dps[:] }, - srcInterfaces: []uint16{0, 01}, + srcInterfaces: []uint16{0, 1, 5}, }, - "two hops non consdir flyovers": { + "three hops peering brtransit non consdir": { mockMsg: func() *ipv4.Message { - spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), xtest.MustParseIA("1-ff00:0:111")) + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), xtest.MustParseIA("1-ff00:0:113")) dst := addr.MustParseHost("10.0.100.100") _ = spkt.SetDstAddr(dst) @@ -1999,65 +2005,73 @@ func TestHbirdPacketPath(t *testing.T) { PathMeta: hummingbird.MetaHdr{ CurrINF: 0, CurrHF: 0, - SegLen: [3]uint8{10, 0, 0}, + SegLen: [3]uint8{3, 6}, BaseTS: util.TimeToSecs(now), HighResTS: 500 << 22, }, - NumINF: 1, - NumHops: 10, + NumINF: 2, + NumHops: 9, }, InfoFields: []path.InfoField{ - {SegID: 0x111, ConsDir: false, Timestamp: util.TimeToSecs(now)}, + {SegID: 0x111, Peer: true, ConsDir: false, Timestamp: util.TimeToSecs(now)}, + {SegID: 0x222, Peer: true, ConsDir: false, Timestamp: util.TimeToSecs(now)}, }, HopFields: []hummingbird.FlyoverHopField{ - {Flyover: true, HopField: path.HopField{ConsIngress: 01, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, - {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 40}, Bw: 5, ResStartTime: 123, Duration: 304}, + {HopField: path.HopField{ConsIngress: 40, ConsEgress: 0}}, + {HopField: path.HopField{ConsIngress: 2, ConsEgress: 1}}, + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 5}}, }, } // Compute MACs and increase SegID while doing so - dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) - dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.Mac) dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) - // aggregate macs - aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 0, 1, - dpath.InfoFields[0], &dpath.HopFields[0], dpath.PathMeta) - aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 40, 0, - dpath.InfoFields[0], &dpath.HopFields[1], dpath.PathMeta) + + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[2].HopField) + dpath.InfoFields[1].UpdateSegID(dpath.HopFields[2].HopField.Mac) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[1].HopField) + // No Segment update here as the second hop of a peering path uses the same segID as it's following hop ret := toMsg(t, spkt, dpath) return ret - }, prepareDPs: func(*gomock.Controller) []*router.DataPlane { - var dps [2]*router.DataPlane + var dps [3]*router.DataPlane dps[0] = router.NewDP( map[uint16]router.BatchConn{ - uint16(01): mock_router.NewMockBatchConn(ctrl), + uint16(40): mock_router.NewMockBatchConn(ctrl), }, map[uint16]topology.LinkType{ - 01: topology.Core, + 40: topology.Peer, }, mock_router.NewMockBatchConn(ctrl), nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) - dps[1] = router.NewDP( map[uint16]router.BatchConn{ - uint16(40): mock_router.NewMockBatchConn(ctrl), + uint16(2): mock_router.NewMockBatchConn(ctrl), + uint16(1): mock_router.NewMockBatchConn(ctrl), }, map[uint16]topology.LinkType{ - 40: topology.Child, + 1: topology.Peer, + 2: topology.Child, }, mock_router.NewMockBatchConn(ctrl), nil, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) - + dps[2] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(5): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 5: topology.Parent, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:113"), nil, key, sv) return dps[:] }, - srcInterfaces: []uint16{0, 40}, + srcInterfaces: []uint16{0, 1, 5}, }, - "six hops astransit xover consdir flyovers": { + "four hops peering astransit consdir": { mockMsg: func() *ipv4.Message { - spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:111"), xtest.MustParseIA("3-ff00:0:333")) + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), xtest.MustParseIA("1-ff00:0:113")) dst := addr.MustParseHost("10.0.100.100") _ = spkt.SetDstAddr(dst) @@ -2066,58 +2080,41 @@ func TestHbirdPacketPath(t *testing.T) { PathMeta: hummingbird.MetaHdr{ CurrINF: 0, CurrHF: 0, - SegLen: [3]uint8{15, 13, 0}, + SegLen: [3]uint8{6, 6}, BaseTS: util.TimeToSecs(now), HighResTS: 500 << 22, }, NumINF: 2, - NumHops: 28, + NumHops: 12, }, InfoFields: []path.InfoField{ - {SegID: 0x111, ConsDir: true, Timestamp: util.TimeToSecs(now)}, - {SegID: 0x222, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + {SegID: 0x111, Peer: true, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + {SegID: 0x222, Peer: true, ConsDir: true, Timestamp: util.TimeToSecs(now)}, }, HopFields: []hummingbird.FlyoverHopField{ - {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 40}, Bw: 5, ResStartTime: 123, Duration: 304}, - {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 31}, Bw: 5, ResStartTime: 123, Duration: 304}, - {Flyover: true, HopField: path.HopField{ConsIngress: 5, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, - {HopField: path.HopField{ConsIngress: 0, ConsEgress: 7}}, - {Flyover: true, HopField: path.HopField{ConsIngress: 11, ConsEgress: 8}, Bw: 5, ResStartTime: 123, Duration: 304}, - {Flyover: true, HopField: path.HopField{ConsIngress: 3, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 40}}, + {HopField: path.HopField{ConsIngress: 31, ConsEgress: 7}}, + {HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}}, + {HopField: path.HopField{ConsIngress: 5, ConsEgress: 0}}, }, } // Compute MACs and increase SegID while doing so dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) dpath.InfoFields[0].UpdateSegID(dpath.HopFields[0].HopField.Mac) dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) - dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.Mac) - dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[2].HopField) + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[2].HopField) + // No Segment update here as the second hop of a peering path uses the same segID as it's following hop dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3].HopField) - dpath.InfoFields[1].UpdateSegID(dpath.HopFields[3].HopField.Mac) - dpath.HopFields[4].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[4].HopField) - dpath.InfoFields[1].UpdateSegID(dpath.HopFields[4].HopField.Mac) - dpath.HopFields[5].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[5].HopField) - // Reset SegID to original value + // reset segID dpath.InfoFields[0].SegID = 0x111 - dpath.InfoFields[1].SegID = 0x222 - // aggregate flyover macs - aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 0, 40, - dpath.InfoFields[0], &dpath.HopFields[0], dpath.PathMeta) - aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 1, 31, - dpath.InfoFields[0], &dpath.HopFields[1], dpath.PathMeta) - aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 5, 7, - dpath.InfoFields[0], &dpath.HopFields[2], dpath.PathMeta) - aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 11, 8, - dpath.InfoFields[1], &dpath.HopFields[4], dpath.PathMeta) - aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 3, 0, - dpath.InfoFields[1], &dpath.HopFields[5], dpath.PathMeta) + ret := toMsg(t, spkt, dpath) return ret }, prepareDPs: func(*gomock.Controller) []*router.DataPlane { - var dps [7]*router.DataPlane + var dps [6]*router.DataPlane dps[0] = router.NewDP( map[uint16]router.BatchConn{ uint16(40): mock_router.NewMockBatchConn(ctrl), @@ -2126,89 +2123,71 @@ func TestHbirdPacketPath(t *testing.T) { 40: topology.Parent, }, mock_router.NewMockBatchConn(ctrl), - nil, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) - + nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) dps[1] = router.NewDP( map[uint16]router.BatchConn{ uint16(31): mock_router.NewMockBatchConn(ctrl), - uint16(1): mock_router.NewMockBatchConn(ctrl), }, map[uint16]topology.LinkType{ - 31: topology.Parent, - 1: topology.Child, + 7: topology.Peer, + 31: topology.Child, }, mock_router.NewMockBatchConn(ctrl), - nil, nil, xtest.MustParseIA("1-ff00:0:112"), nil, key, sv) - + map[uint16]*net.UDPAddr{ + uint16(7): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) dps[2] = router.NewDP( map[uint16]router.BatchConn{ - uint16(5): mock_router.NewMockBatchConn(ctrl), + uint16(7): mock_router.NewMockBatchConn(ctrl), }, map[uint16]topology.LinkType{ - 5: topology.Child, - 7: topology.Core, + 7: topology.Peer, + 31: topology.Child, }, mock_router.NewMockBatchConn(ctrl), map[uint16]*net.UDPAddr{ - uint16(7): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, - }, nil, xtest.MustParseIA("1-ff00:0:113"), nil, key, sv) - + uint16(31): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) dps[3] = router.NewDP( map[uint16]router.BatchConn{ - uint16(7): mock_router.NewMockBatchConn(ctrl), + uint16(1): mock_router.NewMockBatchConn(ctrl), }, map[uint16]topology.LinkType{ - 5: topology.Child, - 7: topology.Core, + 1: topology.Peer, + 2: topology.Child, }, mock_router.NewMockBatchConn(ctrl), map[uint16]*net.UDPAddr{ - uint16(5): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, - }, nil, xtest.MustParseIA("1-ff00:0:113"), nil, key, sv) - + uint16(2): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:112"), nil, key, sv) dps[4] = router.NewDP( map[uint16]router.BatchConn{ - uint16(11): mock_router.NewMockBatchConn(ctrl), + uint16(2): mock_router.NewMockBatchConn(ctrl), }, map[uint16]topology.LinkType{ - 8: topology.Core, - 11: topology.Core, + 1: topology.Peer, + 2: topology.Child, }, mock_router.NewMockBatchConn(ctrl), map[uint16]*net.UDPAddr{ - uint16(8): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, - }, nil, xtest.MustParseIA("2-ff00:0:222"), nil, key, sv) - + uint16(1): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:112"), nil, key, sv) dps[5] = router.NewDP( map[uint16]router.BatchConn{ - uint16(8): mock_router.NewMockBatchConn(ctrl), - }, - map[uint16]topology.LinkType{ - 8: topology.Core, - 11: topology.Core, - }, - mock_router.NewMockBatchConn(ctrl), - map[uint16]*net.UDPAddr{ - uint16(11): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, - }, nil, xtest.MustParseIA("2-ff00:0:222"), nil, key, sv) - - dps[6] = router.NewDP( - map[uint16]router.BatchConn{ - uint16(3): mock_router.NewMockBatchConn(ctrl), + uint16(5): mock_router.NewMockBatchConn(ctrl), }, map[uint16]topology.LinkType{ - 3: topology.Core, + 5: topology.Parent, }, mock_router.NewMockBatchConn(ctrl), - nil, nil, xtest.MustParseIA("3-ff00:0:333"), nil, key, sv) - + nil, nil, xtest.MustParseIA("1-ff00:0:113"), nil, key, sv) return dps[:] - }, // middle hop of second segment is astransit - srcInterfaces: []uint16{0, 1, 5, 0, 11, 0, 3}, + }, + srcInterfaces: []uint16{0, 31, 0, 1, 0, 5}, }, - "six hops astransit xover non consdir flyovers": { + "four hops peering astransit non consdir": { mockMsg: func() *ipv4.Message { - spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:111"), xtest.MustParseIA("3-ff00:0:333")) + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), xtest.MustParseIA("1-ff00:0:113")) dst := addr.MustParseHost("10.0.100.100") _ = spkt.SetDstAddr(dst) @@ -2217,55 +2196,39 @@ func TestHbirdPacketPath(t *testing.T) { PathMeta: hummingbird.MetaHdr{ CurrINF: 0, CurrHF: 0, - SegLen: [3]uint8{15, 13, 0}, + SegLen: [3]uint8{6, 6}, BaseTS: util.TimeToSecs(now), HighResTS: 500 << 22, }, NumINF: 2, - NumHops: 28, + NumHops: 12, }, InfoFields: []path.InfoField{ - {SegID: 0x111, ConsDir: false, Timestamp: util.TimeToSecs(now)}, - {SegID: 0x222, ConsDir: false, Timestamp: util.TimeToSecs(now)}, + {SegID: 0x111, Peer: true, ConsDir: false, Timestamp: util.TimeToSecs(now)}, + {SegID: 0x222, Peer: true, ConsDir: false, Timestamp: util.TimeToSecs(now)}, }, HopFields: []hummingbird.FlyoverHopField{ - {Flyover: true, HopField: path.HopField{ConsIngress: 40, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, - {Flyover: true, HopField: path.HopField{ConsIngress: 31, ConsEgress: 1}, Bw: 5, ResStartTime: 123, Duration: 304}, - {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 5}, Bw: 5, ResStartTime: 123, Duration: 304}, - {HopField: path.HopField{ConsIngress: 7, ConsEgress: 0}}, - {Flyover: true, HopField: path.HopField{ConsIngress: 8, ConsEgress: 11}, Bw: 5, ResStartTime: 123, Duration: 304}, - {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 3}, Bw: 5, ResStartTime: 123, Duration: 304}, + {HopField: path.HopField{ConsIngress: 40, ConsEgress: 0}}, + {HopField: path.HopField{ConsIngress: 7, ConsEgress: 31}}, + {HopField: path.HopField{ConsIngress: 2, ConsEgress: 1}}, + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 5}}, }, } // Compute MACs and increase SegID while doing so - dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[2].HopField) - dpath.InfoFields[0].UpdateSegID(dpath.HopFields[2].HopField.Mac) dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) - dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.Mac) + // No Segment update here as the second hop of a peering path uses the same segID as it's following hop dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) - dpath.HopFields[5].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[5].HopField) - dpath.InfoFields[1].UpdateSegID(dpath.HopFields[5].HopField.Mac) - dpath.HopFields[4].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[4].HopField) - dpath.InfoFields[1].UpdateSegID(dpath.HopFields[4].HopField.Mac) dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3].HopField) - // aggregate with flyover macs - aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 0, 40, - dpath.InfoFields[0], &dpath.HopFields[0], dpath.PathMeta) - aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 1, 31, - dpath.InfoFields[0], &dpath.HopFields[1], dpath.PathMeta) - aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 5, 7, - dpath.InfoFields[0], &dpath.HopFields[2], dpath.PathMeta) - aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 11, 8, - dpath.InfoFields[1], &dpath.HopFields[4], dpath.PathMeta) - aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 3, 0, - dpath.InfoFields[1], &dpath.HopFields[5], dpath.PathMeta) + dpath.InfoFields[1].UpdateSegID(dpath.HopFields[3].HopField.Mac) + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[2].HopField) + ret := toMsg(t, spkt, dpath) return ret }, prepareDPs: func(*gomock.Controller) []*router.DataPlane { - var dps [7]*router.DataPlane + var dps [6]*router.DataPlane dps[0] = router.NewDP( map[uint16]router.BatchConn{ uint16(40): mock_router.NewMockBatchConn(ctrl), @@ -2274,91 +2237,139 @@ func TestHbirdPacketPath(t *testing.T) { 40: topology.Parent, }, mock_router.NewMockBatchConn(ctrl), - nil, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) - + nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) dps[1] = router.NewDP( map[uint16]router.BatchConn{ uint16(31): mock_router.NewMockBatchConn(ctrl), - uint16(1): mock_router.NewMockBatchConn(ctrl), }, map[uint16]topology.LinkType{ - 31: topology.Parent, - 1: topology.Child, + 7: topology.Peer, + 31: topology.Child, }, mock_router.NewMockBatchConn(ctrl), - nil, nil, xtest.MustParseIA("1-ff00:0:112"), nil, key, sv) - + map[uint16]*net.UDPAddr{ + uint16(7): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) dps[2] = router.NewDP( map[uint16]router.BatchConn{ - uint16(5): mock_router.NewMockBatchConn(ctrl), + uint16(7): mock_router.NewMockBatchConn(ctrl), }, map[uint16]topology.LinkType{ - 5: topology.Child, - 7: topology.Core, + 7: topology.Peer, + 31: topology.Child, }, mock_router.NewMockBatchConn(ctrl), map[uint16]*net.UDPAddr{ - uint16(7): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, - }, nil, xtest.MustParseIA("1-ff00:0:113"), nil, key, sv) - + uint16(31): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) dps[3] = router.NewDP( map[uint16]router.BatchConn{ - uint16(7): mock_router.NewMockBatchConn(ctrl), + uint16(1): mock_router.NewMockBatchConn(ctrl), }, map[uint16]topology.LinkType{ - 5: topology.Child, - 7: topology.Core, + 1: topology.Peer, + 2: topology.Child, }, mock_router.NewMockBatchConn(ctrl), map[uint16]*net.UDPAddr{ - uint16(5): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, - }, nil, xtest.MustParseIA("1-ff00:0:113"), nil, key, sv) - + uint16(2): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:112"), nil, key, sv) dps[4] = router.NewDP( map[uint16]router.BatchConn{ - uint16(11): mock_router.NewMockBatchConn(ctrl), + uint16(2): mock_router.NewMockBatchConn(ctrl), }, map[uint16]topology.LinkType{ - 8: topology.Core, - 11: topology.Core, + 1: topology.Peer, + 2: topology.Child, }, mock_router.NewMockBatchConn(ctrl), map[uint16]*net.UDPAddr{ - uint16(8): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, - }, nil, xtest.MustParseIA("2-ff00:0:222"), nil, key, sv) - + uint16(1): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:112"), nil, key, sv) dps[5] = router.NewDP( map[uint16]router.BatchConn{ - uint16(8): mock_router.NewMockBatchConn(ctrl), + uint16(5): mock_router.NewMockBatchConn(ctrl), }, map[uint16]topology.LinkType{ - 8: topology.Core, - 11: topology.Core, + 5: topology.Parent, }, mock_router.NewMockBatchConn(ctrl), - map[uint16]*net.UDPAddr{ - uint16(11): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, - }, nil, xtest.MustParseIA("2-ff00:0:222"), nil, key, sv) + nil, nil, xtest.MustParseIA("1-ff00:0:113"), nil, key, sv) + return dps[:] + }, + srcInterfaces: []uint16{0, 31, 0, 1, 0, 5}, + }, + "two hops consdir flyovers": { + mockMsg: func() *ipv4.Message { + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:111"), xtest.MustParseIA("1-ff00:0:110")) + dst := addr.MustParseHost("10.0.100.100") + _ = spkt.SetDstAddr(dst) - dps[6] = router.NewDP( + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrINF: 0, + CurrHF: 0, + SegLen: [3]uint8{10, 0, 0}, + BaseTS: util.TimeToSecs(now), + HighResTS: 500 << 22, + }, + NumINF: 1, + NumHops: 10, + }, + InfoFields: []path.InfoField{ + {SegID: 0x111, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + }, + + HopFields: []hummingbird.FlyoverHopField{ + {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 40}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 01, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, + }, + } + // Compute MACs and increase SegID while doing so + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + dpath.InfoFields[0].UpdateSegID(dpath.HopFields[0].HopField.Mac) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + // add flyover macs + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 0, 40, + dpath.InfoFields[0], &dpath.HopFields[0], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 1, 0, + dpath.InfoFields[0], &dpath.HopFields[1], dpath.PathMeta) + // Reset SegID to original value + dpath.InfoFields[0].SegID = 0x111 + ret := toMsg(t, spkt, dpath) + return ret + + }, + prepareDPs: func(*gomock.Controller) []*router.DataPlane { + var dps [2]*router.DataPlane + dps[0] = router.NewDP( map[uint16]router.BatchConn{ - uint16(3): mock_router.NewMockBatchConn(ctrl), + uint16(40): mock_router.NewMockBatchConn(ctrl), }, map[uint16]topology.LinkType{ - 3: topology.Core, + 40: topology.Core, }, mock_router.NewMockBatchConn(ctrl), - nil, nil, xtest.MustParseIA("3-ff00:0:333"), nil, key, sv) + nil, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) + + dps[1] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(01): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 01: topology.Child, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) return dps[:] - }, // middle hop of second segment is astransit - srcInterfaces: []uint16{0, 1, 5, 0, 11, 0, 3}, + }, + srcInterfaces: []uint16{0, 01}, }, - "six hops three segs mixed consdir flyovers": { - // two crossovers, first crossover is brtransit, second one is astransit - // core segment is non consdir + "two hops non consdir flyovers": { mockMsg: func() *ipv4.Message { - spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), xtest.MustParseIA("1-ff00:0:113")) + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), xtest.MustParseIA("1-ff00:0:111")) dst := addr.MustParseHost("10.0.100.100") _ = spkt.SetDstAddr(dst) @@ -2367,25 +2378,91 @@ func TestHbirdPacketPath(t *testing.T) { PathMeta: hummingbird.MetaHdr{ CurrINF: 0, CurrHF: 0, - SegLen: [3]uint8{10, 8, 8}, + SegLen: [3]uint8{10, 0, 0}, BaseTS: util.TimeToSecs(now), HighResTS: 500 << 22, }, - NumINF: 3, - NumHops: 26, + NumINF: 1, + NumHops: 10, + }, + InfoFields: []path.InfoField{ + {SegID: 0x111, ConsDir: false, Timestamp: util.TimeToSecs(now)}, + }, + + HopFields: []hummingbird.FlyoverHopField{ + {Flyover: true, HopField: path.HopField{ConsIngress: 01, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 40}, Bw: 5, ResStartTime: 123, Duration: 304}, + }, + } + // Compute MACs and increase SegID while doing so + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.Mac) + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + // aggregate macs + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 0, 1, + dpath.InfoFields[0], &dpath.HopFields[0], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 40, 0, + dpath.InfoFields[0], &dpath.HopFields[1], dpath.PathMeta) + + ret := toMsg(t, spkt, dpath) + return ret + + }, + prepareDPs: func(*gomock.Controller) []*router.DataPlane { + var dps [2]*router.DataPlane + dps[0] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(01): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 01: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + + dps[1] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(40): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 40: topology.Child, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) + + return dps[:] + }, + srcInterfaces: []uint16{0, 40}, + }, + "six hops astransit xover consdir flyovers": { + mockMsg: func() *ipv4.Message { + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:111"), xtest.MustParseIA("3-ff00:0:333")) + dst := addr.MustParseHost("10.0.100.100") + _ = spkt.SetDstAddr(dst) + + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrINF: 0, + CurrHF: 0, + SegLen: [3]uint8{15, 13, 0}, + BaseTS: util.TimeToSecs(now), + HighResTS: 500 << 22, + }, + NumINF: 2, + NumHops: 28, }, InfoFields: []path.InfoField{ {SegID: 0x111, ConsDir: true, Timestamp: util.TimeToSecs(now)}, - {SegID: 0x222, ConsDir: false, Timestamp: util.TimeToSecs(now)}, - {SegID: 0x333, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + {SegID: 0x222, ConsDir: true, Timestamp: util.TimeToSecs(now)}, }, HopFields: []hummingbird.FlyoverHopField{ {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 40}, Bw: 5, ResStartTime: 123, Duration: 304}, - {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, - {HopField: path.HopField{ConsIngress: 5, ConsEgress: 0}}, - {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 31}, Bw: 5, ResStartTime: 123, Duration: 304}, - {HopField: path.HopField{ConsIngress: 0, ConsEgress: 8}}, + {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 31}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 5, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 7}}, + {Flyover: true, HopField: path.HopField{ConsIngress: 11, ConsEgress: 8}, Bw: 5, ResStartTime: 123, Duration: 304}, {Flyover: true, HopField: path.HopField{ConsIngress: 3, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, }, } @@ -2393,31 +2470,33 @@ func TestHbirdPacketPath(t *testing.T) { dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) dpath.InfoFields[0].UpdateSegID(dpath.HopFields[0].HopField.Mac) dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.Mac) + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[2].HopField) dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3].HopField) dpath.InfoFields[1].UpdateSegID(dpath.HopFields[3].HopField.Mac) - dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[2].HopField) - - dpath.HopFields[4].HopField.Mac = computeMAC(t, key, dpath.InfoFields[2], dpath.HopFields[4].HopField) - dpath.InfoFields[2].UpdateSegID(dpath.HopFields[4].HopField.Mac) - dpath.HopFields[5].HopField.Mac = computeMAC(t, key, dpath.InfoFields[2], dpath.HopFields[5].HopField) + dpath.HopFields[4].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[4].HopField) + dpath.InfoFields[1].UpdateSegID(dpath.HopFields[4].HopField.Mac) + dpath.HopFields[5].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[5].HopField) // Reset SegID to original value dpath.InfoFields[0].SegID = 0x111 - dpath.InfoFields[2].SegID = 0x333 + dpath.InfoFields[1].SegID = 0x222 // aggregate flyover macs aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 0, 40, dpath.InfoFields[0], &dpath.HopFields[0], dpath.PathMeta) - aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 1, 5, + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 1, 31, dpath.InfoFields[0], &dpath.HopFields[1], dpath.PathMeta) - aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 31, 8, - dpath.InfoFields[1], &dpath.HopFields[3], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 5, 7, + dpath.InfoFields[0], &dpath.HopFields[2], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 11, 8, + dpath.InfoFields[1], &dpath.HopFields[4], dpath.PathMeta) aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 3, 0, - dpath.InfoFields[2], &dpath.HopFields[5], dpath.PathMeta) + dpath.InfoFields[1], &dpath.HopFields[5], dpath.PathMeta) ret := toMsg(t, spkt, dpath) return ret }, prepareDPs: func(*gomock.Controller) []*router.DataPlane { - var dps [5]*router.DataPlane + var dps [7]*router.DataPlane dps[0] = router.NewDP( map[uint16]router.BatchConn{ uint16(40): mock_router.NewMockBatchConn(ctrl), @@ -2426,61 +2505,755 @@ func TestHbirdPacketPath(t *testing.T) { 40: topology.Parent, }, mock_router.NewMockBatchConn(ctrl), - nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + nil, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) + dps[1] = router.NewDP( map[uint16]router.BatchConn{ - uint16(1): mock_router.NewMockBatchConn(ctrl), - uint16(5): mock_router.NewMockBatchConn(ctrl), + uint16(31): mock_router.NewMockBatchConn(ctrl), + uint16(1): mock_router.NewMockBatchConn(ctrl), }, map[uint16]topology.LinkType{ - 1: topology.Child, - 5: topology.Core, + 31: topology.Parent, + 1: topology.Child, }, mock_router.NewMockBatchConn(ctrl), - nil, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) + nil, nil, xtest.MustParseIA("1-ff00:0:112"), nil, key, sv) + dps[2] = router.NewDP( map[uint16]router.BatchConn{ - uint16(31): mock_router.NewMockBatchConn(ctrl), + uint16(5): mock_router.NewMockBatchConn(ctrl), }, map[uint16]topology.LinkType{ - 8: topology.Child, - 31: topology.Core, + 5: topology.Child, + 7: topology.Core, }, mock_router.NewMockBatchConn(ctrl), map[uint16]*net.UDPAddr{ - uint16(8): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, - }, nil, xtest.MustParseIA("1-ff00:0:112"), nil, key, sv) + uint16(7): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:113"), nil, key, sv) + dps[3] = router.NewDP( map[uint16]router.BatchConn{ - uint16(8): mock_router.NewMockBatchConn(ctrl), + uint16(7): mock_router.NewMockBatchConn(ctrl), }, map[uint16]topology.LinkType{ - 8: topology.Child, - 31: topology.Core, + 5: topology.Child, + 7: topology.Core, }, mock_router.NewMockBatchConn(ctrl), map[uint16]*net.UDPAddr{ - uint16(31): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, - }, nil, xtest.MustParseIA("1-ff00:0:112"), nil, key, sv) + uint16(5): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:113"), nil, key, sv) + dps[4] = router.NewDP( map[uint16]router.BatchConn{ - uint16(3): mock_router.NewMockBatchConn(ctrl), + uint16(11): mock_router.NewMockBatchConn(ctrl), }, map[uint16]topology.LinkType{ - 3: topology.Parent, + 8: topology.Core, + 11: topology.Core, }, mock_router.NewMockBatchConn(ctrl), - nil, nil, xtest.MustParseIA("1-ff00:0:113"), nil, key, sv) - return dps[:] - }, - srcInterfaces: []uint16{0, 1, 31, 0, 3}, + map[uint16]*net.UDPAddr{ + uint16(8): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("2-ff00:0:222"), nil, key, sv) + + dps[5] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(8): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 8: topology.Core, + 11: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(11): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("2-ff00:0:222"), nil, key, sv) + + dps[6] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(3): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 3: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("3-ff00:0:333"), nil, key, sv) + + return dps[:] + }, // middle hop of second segment is astransit + srcInterfaces: []uint16{0, 1, 5, 0, 11, 0, 3}, + }, + "six hops astransit xover non consdir flyovers": { + mockMsg: func() *ipv4.Message { + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:111"), xtest.MustParseIA("3-ff00:0:333")) + dst := addr.MustParseHost("10.0.100.100") + _ = spkt.SetDstAddr(dst) + + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrINF: 0, + CurrHF: 0, + SegLen: [3]uint8{15, 13, 0}, + BaseTS: util.TimeToSecs(now), + HighResTS: 500 << 22, + }, + NumINF: 2, + NumHops: 28, + }, + InfoFields: []path.InfoField{ + {SegID: 0x111, ConsDir: false, Timestamp: util.TimeToSecs(now)}, + {SegID: 0x222, ConsDir: false, Timestamp: util.TimeToSecs(now)}, + }, + + HopFields: []hummingbird.FlyoverHopField{ + {Flyover: true, HopField: path.HopField{ConsIngress: 40, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 31, ConsEgress: 1}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 5}, Bw: 5, ResStartTime: 123, Duration: 304}, + {HopField: path.HopField{ConsIngress: 7, ConsEgress: 0}}, + {Flyover: true, HopField: path.HopField{ConsIngress: 8, ConsEgress: 11}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 3}, Bw: 5, ResStartTime: 123, Duration: 304}, + }, + } + // Compute MACs and increase SegID while doing so + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[2].HopField) + dpath.InfoFields[0].UpdateSegID(dpath.HopFields[2].HopField.Mac) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.Mac) + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + + dpath.HopFields[5].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[5].HopField) + dpath.InfoFields[1].UpdateSegID(dpath.HopFields[5].HopField.Mac) + dpath.HopFields[4].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[4].HopField) + dpath.InfoFields[1].UpdateSegID(dpath.HopFields[4].HopField.Mac) + dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3].HopField) + // aggregate with flyover macs + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 0, 40, + dpath.InfoFields[0], &dpath.HopFields[0], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 1, 31, + dpath.InfoFields[0], &dpath.HopFields[1], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 5, 7, + dpath.InfoFields[0], &dpath.HopFields[2], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 11, 8, + dpath.InfoFields[1], &dpath.HopFields[4], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 3, 0, + dpath.InfoFields[1], &dpath.HopFields[5], dpath.PathMeta) + ret := toMsg(t, spkt, dpath) + return ret + }, + prepareDPs: func(*gomock.Controller) []*router.DataPlane { + var dps [7]*router.DataPlane + dps[0] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(40): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 40: topology.Parent, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) + + dps[1] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(31): mock_router.NewMockBatchConn(ctrl), + uint16(1): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 31: topology.Parent, + 1: topology.Child, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:112"), nil, key, sv) + + dps[2] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(5): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 5: topology.Child, + 7: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(7): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:113"), nil, key, sv) + + dps[3] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(7): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 5: topology.Child, + 7: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(5): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:113"), nil, key, sv) + + dps[4] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(11): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 8: topology.Core, + 11: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(8): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("2-ff00:0:222"), nil, key, sv) + + dps[5] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(8): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 8: topology.Core, + 11: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(11): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("2-ff00:0:222"), nil, key, sv) + + dps[6] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(3): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 3: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("3-ff00:0:333"), nil, key, sv) + + return dps[:] + }, // middle hop of second segment is astransit + srcInterfaces: []uint16{0, 1, 5, 0, 11, 0, 3}, + }, + "six hops three segs mixed consdir flyovers": { + // two crossovers, first crossover is brtransit, second one is astransit + // core segment is non consdir + mockMsg: func() *ipv4.Message { + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), xtest.MustParseIA("1-ff00:0:113")) + dst := addr.MustParseHost("10.0.100.100") + _ = spkt.SetDstAddr(dst) + + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrINF: 0, + CurrHF: 0, + SegLen: [3]uint8{10, 8, 8}, + BaseTS: util.TimeToSecs(now), + HighResTS: 500 << 22, + }, + NumINF: 3, + NumHops: 26, + }, + InfoFields: []path.InfoField{ + {SegID: 0x111, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + {SegID: 0x222, ConsDir: false, Timestamp: util.TimeToSecs(now)}, + {SegID: 0x333, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + }, + + HopFields: []hummingbird.FlyoverHopField{ + {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 40}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, + {HopField: path.HopField{ConsIngress: 5, ConsEgress: 0}}, + {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 31}, Bw: 5, ResStartTime: 123, Duration: 304}, + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 8}}, + {Flyover: true, HopField: path.HopField{ConsIngress: 3, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, + }, + } + // Compute MACs and increase SegID while doing so + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + dpath.InfoFields[0].UpdateSegID(dpath.HopFields[0].HopField.Mac) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + + dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3].HopField) + dpath.InfoFields[1].UpdateSegID(dpath.HopFields[3].HopField.Mac) + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[2].HopField) + + dpath.HopFields[4].HopField.Mac = computeMAC(t, key, dpath.InfoFields[2], dpath.HopFields[4].HopField) + dpath.InfoFields[2].UpdateSegID(dpath.HopFields[4].HopField.Mac) + dpath.HopFields[5].HopField.Mac = computeMAC(t, key, dpath.InfoFields[2], dpath.HopFields[5].HopField) + // Reset SegID to original value + dpath.InfoFields[0].SegID = 0x111 + dpath.InfoFields[2].SegID = 0x333 + // aggregate flyover macs + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 0, 40, + dpath.InfoFields[0], &dpath.HopFields[0], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 1, 5, + dpath.InfoFields[0], &dpath.HopFields[1], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 31, 8, + dpath.InfoFields[1], &dpath.HopFields[3], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 3, 0, + dpath.InfoFields[2], &dpath.HopFields[5], dpath.PathMeta) + ret := toMsg(t, spkt, dpath) + return ret + }, + prepareDPs: func(*gomock.Controller) []*router.DataPlane { + var dps [5]*router.DataPlane + dps[0] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(40): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 40: topology.Parent, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + dps[1] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(1): mock_router.NewMockBatchConn(ctrl), + uint16(5): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 1: topology.Child, + 5: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) + dps[2] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(31): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 8: topology.Child, + 31: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(8): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:112"), nil, key, sv) + dps[3] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(8): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 8: topology.Child, + 31: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(31): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:112"), nil, key, sv) + dps[4] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(3): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 3: topology.Parent, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:113"), nil, key, sv) + return dps[:] + }, + srcInterfaces: []uint16{0, 1, 31, 0, 3}, + }, + "three hops peering brtransit consdir flyovers": { + mockMsg: func() *ipv4.Message { + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), xtest.MustParseIA("1-ff00:0:113")) + dst := addr.MustParseHost("10.0.100.100") + _ = spkt.SetDstAddr(dst) + + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrINF: 0, + CurrHF: 0, + SegLen: [3]uint8{5, 10}, + BaseTS: util.TimeToSecs(now), + HighResTS: 500 << 22, + }, + NumINF: 2, + NumHops: 15, + }, + InfoFields: []path.InfoField{ + {SegID: 0x111, Peer: true, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + {SegID: 0x222, Peer: true, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + }, + + HopFields: []hummingbird.FlyoverHopField{ + {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 40}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 5, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, + }, + } + // Compute MACs and increase SegID while doing so + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[1].HopField) + // No Segment update here as the second hop of a peering path uses the same segID as it's following hop + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[2].HopField) + + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 0, 40, dpath.InfoFields[0], &dpath.HopFields[0], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 1, 2, dpath.InfoFields[1], &dpath.HopFields[1], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 5, 0, dpath.InfoFields[1], &dpath.HopFields[2], dpath.PathMeta) + + ret := toMsg(t, spkt, dpath) + return ret + }, + prepareDPs: func(*gomock.Controller) []*router.DataPlane { + var dps [3]*router.DataPlane + dps[0] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(40): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 40: topology.Peer, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + dps[1] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(2): mock_router.NewMockBatchConn(ctrl), + uint16(1): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 1: topology.Peer, + 2: topology.Child, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) + dps[2] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(5): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 5: topology.Parent, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:113"), nil, key, sv) + return dps[:] + }, + srcInterfaces: []uint16{0, 1, 5}, + }, + "three hops peering brtransit non consdir flyovers": { + mockMsg: func() *ipv4.Message { + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), xtest.MustParseIA("1-ff00:0:113")) + dst := addr.MustParseHost("10.0.100.100") + _ = spkt.SetDstAddr(dst) + + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrINF: 0, + CurrHF: 0, + SegLen: [3]uint8{5, 10}, + BaseTS: util.TimeToSecs(now), + HighResTS: 500 << 22, + }, + NumINF: 2, + NumHops: 15, + }, + InfoFields: []path.InfoField{ + {SegID: 0x111, Peer: true, ConsDir: false, Timestamp: util.TimeToSecs(now)}, + {SegID: 0x222, Peer: true, ConsDir: false, Timestamp: util.TimeToSecs(now)}, + }, + + HopFields: []hummingbird.FlyoverHopField{ + {Flyover: true, HopField: path.HopField{ConsIngress: 40, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 2, ConsEgress: 1}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 5}, Bw: 5, ResStartTime: 123, Duration: 304}, + }, + } + // Compute MACs and increase SegID while doing so + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[2].HopField) + dpath.InfoFields[1].UpdateSegID(dpath.HopFields[2].HopField.Mac) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[1].HopField) + // No Segment update here as the second hop of a peering path uses the same segID as it's following hop + + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 0, 40, dpath.InfoFields[0], &dpath.HopFields[0], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 1, 2, dpath.InfoFields[1], &dpath.HopFields[1], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 5, 0, dpath.InfoFields[1], &dpath.HopFields[2], dpath.PathMeta) + + ret := toMsg(t, spkt, dpath) + return ret + }, + prepareDPs: func(*gomock.Controller) []*router.DataPlane { + var dps [3]*router.DataPlane + dps[0] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(40): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 40: topology.Peer, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + dps[1] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(2): mock_router.NewMockBatchConn(ctrl), + uint16(1): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 1: topology.Peer, + 2: topology.Child, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) + dps[2] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(5): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 5: topology.Parent, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:113"), nil, key, sv) + return dps[:] + }, + srcInterfaces: []uint16{0, 1, 5}, + }, + "four hops peering astransit consdir flyovers": { + mockMsg: func() *ipv4.Message { + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), xtest.MustParseIA("1-ff00:0:113")) + dst := addr.MustParseHost("10.0.100.100") + _ = spkt.SetDstAddr(dst) + + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrINF: 0, + CurrHF: 0, + SegLen: [3]uint8{10, 10}, + BaseTS: util.TimeToSecs(now), + HighResTS: 500 << 22, + }, + NumINF: 2, + NumHops: 20, + }, + InfoFields: []path.InfoField{ + {SegID: 0x111, Peer: true, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + {SegID: 0x222, Peer: true, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + }, + + HopFields: []hummingbird.FlyoverHopField{ + {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 40}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 31, ConsEgress: 7}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 5, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, + }, + } + // Compute MACs and increase SegID while doing so + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + dpath.InfoFields[0].UpdateSegID(dpath.HopFields[0].HopField.Mac) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[2].HopField) + // No Segment update here as the second hop of a peering path uses the same segID as it's following hop + dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3].HopField) + // reset segID + dpath.InfoFields[0].SegID = 0x111 + + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 0, 40, dpath.InfoFields[0], &dpath.HopFields[0], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 31, 7, dpath.InfoFields[0], &dpath.HopFields[1], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 1, 2, dpath.InfoFields[1], &dpath.HopFields[2], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 5, 0, dpath.InfoFields[1], &dpath.HopFields[3], dpath.PathMeta) + + ret := toMsg(t, spkt, dpath) + return ret + }, + prepareDPs: func(*gomock.Controller) []*router.DataPlane { + var dps [6]*router.DataPlane + dps[0] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(40): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 40: topology.Parent, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + dps[1] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(31): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 7: topology.Peer, + 31: topology.Child, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(7): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) + dps[2] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(7): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 7: topology.Peer, + 31: topology.Child, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(31): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) + dps[3] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(1): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 1: topology.Peer, + 2: topology.Child, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(2): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:112"), nil, key, sv) + dps[4] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(2): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 1: topology.Peer, + 2: topology.Child, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(1): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:112"), nil, key, sv) + dps[5] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(5): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 5: topology.Parent, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:113"), nil, key, sv) + return dps[:] + }, + srcInterfaces: []uint16{0, 31, 0, 1, 0, 5}, + }, + "four hops peering astransit non consdir flyovers": { + mockMsg: func() *ipv4.Message { + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), xtest.MustParseIA("1-ff00:0:113")) + dst := addr.MustParseHost("10.0.100.100") + _ = spkt.SetDstAddr(dst) + + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrINF: 0, + CurrHF: 0, + SegLen: [3]uint8{10, 10}, + BaseTS: util.TimeToSecs(now), + HighResTS: 500 << 22, + }, + NumINF: 2, + NumHops: 20, + }, + InfoFields: []path.InfoField{ + {SegID: 0x111, Peer: true, ConsDir: false, Timestamp: util.TimeToSecs(now)}, + {SegID: 0x222, Peer: true, ConsDir: false, Timestamp: util.TimeToSecs(now)}, + }, + + HopFields: []hummingbird.FlyoverHopField{ + {Flyover: true, HopField: path.HopField{ConsIngress: 40, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 7, ConsEgress: 31}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 2, ConsEgress: 1}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 5}, Bw: 5, ResStartTime: 123, Duration: 304}, + }, + } + // Compute MACs and increase SegID while doing so + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + // No Segment update here as the second hop of a peering path uses the same segID as it's following hop + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + + dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3].HopField) + dpath.InfoFields[1].UpdateSegID(dpath.HopFields[3].HopField.Mac) + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[2].HopField) + + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 0, 40, dpath.InfoFields[0], &dpath.HopFields[0], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 31, 7, dpath.InfoFields[0], &dpath.HopFields[1], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 1, 2, dpath.InfoFields[1], &dpath.HopFields[2], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 5, 0, dpath.InfoFields[1], &dpath.HopFields[3], dpath.PathMeta) + + ret := toMsg(t, spkt, dpath) + return ret + }, + prepareDPs: func(*gomock.Controller) []*router.DataPlane { + var dps [6]*router.DataPlane + dps[0] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(40): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 40: topology.Parent, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + dps[1] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(31): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 7: topology.Peer, + 31: topology.Child, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(7): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) + dps[2] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(7): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 7: topology.Peer, + 31: topology.Child, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(31): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) + dps[3] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(1): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 1: topology.Peer, + 2: topology.Child, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(2): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:112"), nil, key, sv) + dps[4] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(2): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 1: topology.Peer, + 2: topology.Child, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(1): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:112"), nil, key, sv) + dps[5] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(5): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 5: topology.Parent, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:113"), nil, key, sv) + return dps[:] + }, + srcInterfaces: []uint16{0, 31, 0, 1, 0, 5}, }, } for name, tc := range testCases { - // if name != "six hops astransit xover non consdir" { //Only run test we want to debug - // continue - // } name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() From c9bdbd2b4c1b7252f1013debc5b7ceb0939dd79d Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Wed, 8 Nov 2023 10:45:37 +0100 Subject: [PATCH 015/100] fixed issues in hummingbird decoded FirstHopPerSeg --- pkg/slayers/path/hummingbird/decoded.go | 6 +++--- pkg/slayers/path/hummingbird/decoded_test.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/slayers/path/hummingbird/decoded.go b/pkg/slayers/path/hummingbird/decoded.go index b8b4525c18..e5a335b54c 100644 --- a/pkg/slayers/path/hummingbird/decoded.go +++ b/pkg/slayers/path/hummingbird/decoded.go @@ -77,10 +77,10 @@ func (s *Decoded) DecodeFromBytes(data []byte) error { } s.HopFields = s.HopFields[:i] if s.PathMeta.SegLen[1] == 0 { - s.FirstHopPerSeg[0] = s.PathMeta.SegLen[0] - s.FirstHopPerSeg[1] = s.PathMeta.SegLen[0] + s.FirstHopPerSeg[0] = uint8(i) + s.FirstHopPerSeg[1] = uint8(i) } else if s.PathMeta.SegLen[2] == 0 { - s.FirstHopPerSeg[1] = s.PathMeta.SegLen[0] + s.PathMeta.SegLen[1] + s.FirstHopPerSeg[1] = uint8(i) } return nil diff --git a/pkg/slayers/path/hummingbird/decoded_test.go b/pkg/slayers/path/hummingbird/decoded_test.go index 99ff8a9ddb..6e54c7ee78 100644 --- a/pkg/slayers/path/hummingbird/decoded_test.go +++ b/pkg/slayers/path/hummingbird/decoded_test.go @@ -83,7 +83,7 @@ var decodedHbirdTestPath = &hummingbird.Decoded{ }, InfoFields: testInfoFields, HopFields: testFlyoverFields, - FirstHopPerSeg: [2]uint8{2, 0}, + FirstHopPerSeg: [2]uint8{2, 4}, } var emptyDecodedTestPath = &hummingbird.Decoded{ From 5ddaf8bbdaa44b560de4304b9456a2b681efdeb8 Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Wed, 8 Nov 2023 15:51:45 +0100 Subject: [PATCH 016/100] added UT, fixed integration test for full flyovers path (but broke it for partially reserved path) --- pkg/hummingbird/hummingbird.go | 153 ++++++++++++--------- router/dataplane_hbird_test.go | 237 +++++++++++++++++++++++++++++++++ 2 files changed, 325 insertions(+), 65 deletions(-) diff --git a/pkg/hummingbird/hummingbird.go b/pkg/hummingbird/hummingbird.go index bda28cd6f4..50f293cbff 100644 --- a/pkg/hummingbird/hummingbird.go +++ b/pkg/hummingbird/hummingbird.go @@ -8,6 +8,7 @@ import ( "time" "github.com/scionproto/scion/pkg/addr" + "github.com/scionproto/scion/pkg/log" "github.com/scionproto/scion/pkg/private/serrors" "github.com/scionproto/scion/pkg/slayers/path/hummingbird" "github.com/scionproto/scion/pkg/slayers/path/scion" @@ -125,6 +126,33 @@ type HummingbirdClient struct { xkbuffer [44]uint32 } +func (c *HummingbirdClient) parseIAs(ifs []snet.PathInterface) error { + c.ases = make([]addr.IA, len(c.dec.HopFields)) + c.ases[0] = ifs[0].IA + i, j := 1, 1 + for i < len(c.ases) && j < len(ifs) { + + switch true { + // First hop after Crossover always has same as as previous hop + case (i == int(c.dec.FirstHopPerSeg[0]) && !c.dec.InfoFields[1].Peer) || (i == int(c.dec.FirstHopPerSeg[1])): + c.ases[i] = c.ases[i-1] + i++ + // Skip duplicates interfaces. Only duplicates we want are those for Xovers, and we already add these manually above + case ifs[j].IA == ifs[j-1].IA: + j++ + default: + c.ases[i] = ifs[j].IA + i++ + j++ + } + + } + if i < len(c.ases)-1 { + return serrors.New("Not enough ASes for this path") + } + return nil +} + func (c *HummingbirdClient) PrepareHbirdPath(p snet.Path) error { if p == nil { return serrors.New("Empty path") @@ -141,10 +169,10 @@ func (c *HummingbirdClient) PrepareHbirdPath(p snet.Path) error { default: return serrors.New("Unsupported path type") } + log.Debug("parsing AS") // Parse the list of ASes on path - c.ases = make([]addr.IA, len(p.Metadata().Interfaces)) - for i, ia := range p.Metadata().Interfaces { - c.ases[i] = ia.IA + if err := c.parseIAs(p.Metadata().Interfaces); err != nil { + return serrors.Join(err, serrors.New("Malformed path")) } c.dest = c.ases[len(c.ases)-1] // cache Scion Hopfield macs @@ -152,6 +180,7 @@ func (c *HummingbirdClient) PrepareHbirdPath(p snet.Path) error { for i, hop := range c.dec.HopFields { copy(c.macs[i][:], hop.HopField.Mac[:]) } + log.Debug("path ASes", "ASes", c.ases) // prepare reservations data structure c.reservations = make([]Reservation, len(c.dec.HopFields)) return nil @@ -169,84 +198,78 @@ func (c *HummingbirdClient) GetPathASes() []addr.IA { } // Requests new reservations for this path for the listed ASes -// Expects them to be in order. +// Expects them to be in order without duplicates func (c *HummingbirdClient) RequestReservationForASes(asin []addr.IA, bw uint16, start uint32, duration uint16) error { j := 0 for i := range c.dec.HopFields { - if c.ases[i] == asin[j] { - if j != 0 && asin[j] == asin[j-1] { - // Do not add flyover on second crossover hop - j++ - continue + + var infIdx int + var firstHopAfterXover, lastHopBeforeXover bool + if i < int(c.dec.FirstHopPerSeg[0]) { + infIdx = 0 + if !c.dec.InfoFields[0].Peer { + lastHopBeforeXover = (i == int(c.dec.FirstHopPerSeg[0])-1) && i < len(c.dec.HopFields)-1 } - var infIdx int - var firstHopAfterXover, lastHopBeforeXover bool - if i < int(c.dec.FirstHopPerSeg[0]) { - infIdx = 0 - lastHopBeforeXover = (i == int(c.dec.FirstHopPerSeg[0])-1) && i != len(c.dec.HopFields)-1 - } else if i < int(c.dec.FirstHopPerSeg[1]) { - infIdx = 1 + } else if i < int(c.dec.FirstHopPerSeg[1]) { + infIdx = 1 + if !c.dec.InfoFields[1].Peer { firstHopAfterXover = i == int(c.dec.FirstHopPerSeg[0]) - lastHopBeforeXover = i == int(c.dec.FirstHopPerSeg[1])-1 && i != len(c.dec.HopFields)-1 - } else { - infIdx = 2 - firstHopAfterXover = i == int(c.dec.FirstHopPerSeg[1]) && i != len(c.dec.HopFields)-1 + lastHopBeforeXover = i == int(c.dec.FirstHopPerSeg[1])-1 && i < len(c.dec.HopFields)-1 + } + } else { + infIdx = 2 + if c.dec.InfoFields[2].Peer { + return serrors.New("Invalid path, cannot have 3 segments on peering path") } + firstHopAfterXover = i == int(c.dec.FirstHopPerSeg[1]) && i < len(c.dec.HopFields)-1 + } + // Do not add a reservation to second hop after crossover + if firstHopAfterXover { + continue + } - c.reservations[i].AS = asin[j] - c.reservations[i].Bw = bw - c.reservations[i].StartTime = start - c.reservations[i].Duration = duration - // Set Ingress and Egress interfaces of reservation - // If crossover, need to take next/previous hop into account - if lastHopBeforeXover { - if c.dec.InfoFields[infIdx].ConsDir { - c.reservations[i].Ingress = c.dec.HopFields[i].HopField.ConsIngress - } else { - c.reservations[i].Ingress = c.dec.HopFields[i].HopField.ConsEgress - } - if c.dec.InfoFields[infIdx+1].ConsDir { - c.reservations[i].Egress = c.dec.HopFields[i+1].HopField.ConsEgress - } else { - c.reservations[i].Egress = c.dec.HopFields[i+1].HopField.ConsIngress - } - } else if firstHopAfterXover { - if c.dec.InfoFields[infIdx-1].ConsDir { - c.reservations[i].Ingress = c.dec.HopFields[i-1].HopField.ConsIngress - } else { - c.reservations[i].Ingress = c.dec.HopFields[i-1].HopField.ConsEgress - } - if c.dec.InfoFields[infIdx].ConsDir { - c.reservations[i].Egress = c.dec.HopFields[i].HopField.ConsEgress - } else { - c.reservations[i].Egress = c.dec.HopFields[i].HopField.ConsIngress - } - } else if c.dec.InfoFields[infIdx].ConsDir { + c.reservations[i].AS = c.ases[i] + c.reservations[i].Bw = bw + c.reservations[i].StartTime = start + c.reservations[i].Duration = duration + // Set Ingress and Egress interfaces of reservation + // If crossover, need to take next/previous hop into account + if lastHopBeforeXover { + if c.dec.InfoFields[infIdx].ConsDir { c.reservations[i].Ingress = c.dec.HopFields[i].HopField.ConsIngress - c.reservations[i].Egress = c.dec.HopFields[i].HopField.ConsEgress } else { c.reservations[i].Ingress = c.dec.HopFields[i].HopField.ConsEgress - c.reservations[i].Egress = c.dec.HopFields[i].HopField.ConsIngress } - - var err error - c.reservations[i], err = cheat_auth_key(&c.reservations[i]) - if err != nil { - return err + if c.dec.InfoFields[infIdx+1].ConsDir { + c.reservations[i].Egress = c.dec.HopFields[i+1].HopField.ConsEgress + } else { + c.reservations[i].Egress = c.dec.HopFields[i+1].HopField.ConsIngress } - // set flyover - c.dec.HopFields[i].Flyover = true - c.dec.NumHops += 2 - c.dec.PathMeta.SegLen[infIdx] += 2 - // set other fields - c.dec.HopFields[i].Bw = c.reservations[i].Bw - c.dec.HopFields[i].Duration = c.reservations[i].Duration - c.dec.HopFields[i].ResID = c.reservations[i].ResID + } else if c.dec.InfoFields[infIdx].ConsDir { + c.reservations[i].Ingress = c.dec.HopFields[i].HopField.ConsIngress + c.reservations[i].Egress = c.dec.HopFields[i].HopField.ConsEgress + } else { + c.reservations[i].Ingress = c.dec.HopFields[i].HopField.ConsEgress + c.reservations[i].Egress = c.dec.HopFields[i].HopField.ConsIngress + } - j++ + var err error + c.reservations[i], err = cheat_auth_key(&c.reservations[i]) + if err != nil { + return err } + // set flyover + c.dec.HopFields[i].Flyover = true + c.dec.NumHops += 2 + c.dec.PathMeta.SegLen[infIdx] += 2 + // set other fields + c.dec.HopFields[i].Bw = c.reservations[i].Bw + c.dec.HopFields[i].Duration = c.reservations[i].Duration + c.dec.HopFields[i].ResID = c.reservations[i].ResID + j++ } + return nil } diff --git a/router/dataplane_hbird_test.go b/router/dataplane_hbird_test.go index 6240040f74..e096f8afa2 100644 --- a/router/dataplane_hbird_test.go +++ b/router/dataplane_hbird_test.go @@ -1808,6 +1808,118 @@ func TestHbirdPacketPath(t *testing.T) { }, // middle hop of second segment is astransit srcInterfaces: []uint16{0, 1, 5, 0, 11, 0, 3}, }, + "six hops brtransit xover mixed consdir": { + // up segment non consdir, down segment consdir + mockMsg: func() *ipv4.Message { + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:111"), xtest.MustParseIA("3-ff00:0:333")) + dst := addr.MustParseHost("10.0.100.100") + _ = spkt.SetDstAddr(dst) + + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrINF: 0, + CurrHF: 0, + SegLen: [3]uint8{9, 9, 0}, + BaseTS: util.TimeToSecs(now), + HighResTS: 500 << 22, + }, + NumINF: 2, + NumHops: 18, + }, + InfoFields: []path.InfoField{ + {SegID: 0x111, ConsDir: false, Timestamp: util.TimeToSecs(now)}, + {SegID: 0x222, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + }, + + HopFields: []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 40, ConsEgress: 0}}, + {HopField: path.HopField{ConsIngress: 31, ConsEgress: 1}}, + {HopField: path.HopField{ConsIngress: 7, ConsEgress: 5}}, + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 7}}, + {HopField: path.HopField{ConsIngress: 11, ConsEgress: 8}}, + {HopField: path.HopField{ConsIngress: 3, ConsEgress: 0}}, + }, + } + // Compute MACs and increase SegID while doing so + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[2].HopField) + dpath.InfoFields[0].UpdateSegID(dpath.HopFields[2].HopField.Mac) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.Mac) + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + + dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3].HopField) + dpath.InfoFields[1].UpdateSegID(dpath.HopFields[3].HopField.Mac) + dpath.HopFields[4].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[4].HopField) + dpath.InfoFields[1].UpdateSegID(dpath.HopFields[4].HopField.Mac) + dpath.HopFields[5].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[5].HopField) + // Reset SegID to original value + dpath.InfoFields[1].SegID = 0x222 + ret := toMsg(t, spkt, dpath) + return ret + }, + prepareDPs: func(*gomock.Controller) []*router.DataPlane { + var dps [5]*router.DataPlane + dps[0] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(40): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 40: topology.Parent, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) + + dps[1] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(31): mock_router.NewMockBatchConn(ctrl), + uint16(1): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 31: topology.Parent, + 1: topology.Child, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:112"), nil, key, sv) + + dps[2] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(5): mock_router.NewMockBatchConn(ctrl), + uint16(7): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 5: topology.Child, + 7: topology.Child, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:113"), nil, key, sv) + + dps[3] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(8): mock_router.NewMockBatchConn(ctrl), + uint16(11): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 8: topology.Child, + 11: topology.Parent, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("2-ff00:0:222"), nil, key, sv) + + dps[4] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(3): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 3: topology.Parent, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("3-ff00:0:333"), nil, key, sv) + + return dps[:] + }, // middle hop of second segment is astransit + srcInterfaces: []uint16{0, 1, 5, 11, 3}, + }, "six hops three segs mixed consdir": { // two crossovers, first crossover is brtransit, second one is astransit // core segment is non consdir @@ -2733,6 +2845,131 @@ func TestHbirdPacketPath(t *testing.T) { }, // middle hop of second segment is astransit srcInterfaces: []uint16{0, 1, 5, 0, 11, 0, 3}, }, + "six hops brtransit xover mixed consdir flyovers": { + // up segment non consdir, down segment consdir + mockMsg: func() *ipv4.Message { + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:111"), xtest.MustParseIA("3-ff00:0:333")) + dst := addr.MustParseHost("10.0.100.100") + _ = spkt.SetDstAddr(dst) + + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrINF: 0, + CurrHF: 0, + SegLen: [3]uint8{15, 13, 0}, + BaseTS: util.TimeToSecs(now), + HighResTS: 500 << 22, + }, + NumINF: 2, + NumHops: 28, + }, + InfoFields: []path.InfoField{ + {SegID: 0x111, ConsDir: false, Timestamp: util.TimeToSecs(now)}, + {SegID: 0x222, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + }, + + HopFields: []hummingbird.FlyoverHopField{ + {Flyover: true, HopField: path.HopField{ConsIngress: 40, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 31, ConsEgress: 1}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 5}, Bw: 5, ResStartTime: 123, Duration: 304}, + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 7}}, + {Flyover: true, HopField: path.HopField{ConsIngress: 11, ConsEgress: 8}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 3, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, + }, + } + // Compute MACs and increase SegID while doing so + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[2].HopField) + dpath.InfoFields[0].UpdateSegID(dpath.HopFields[2].HopField.Mac) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.Mac) + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + + dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3].HopField) + dpath.InfoFields[1].UpdateSegID(dpath.HopFields[3].HopField.Mac) + dpath.HopFields[4].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[4].HopField) + dpath.InfoFields[1].UpdateSegID(dpath.HopFields[4].HopField.Mac) + dpath.HopFields[5].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[5].HopField) + // Reset SegID to original value + dpath.InfoFields[1].SegID = 0x222 + + //aggregate MACs + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 0, 40, + dpath.InfoFields[0], &dpath.HopFields[0], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 1, 31, + dpath.InfoFields[0], &dpath.HopFields[1], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 5, 7, + dpath.InfoFields[0], &dpath.HopFields[2], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 11, 8, + dpath.InfoFields[1], &dpath.HopFields[4], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 3, 0, + dpath.InfoFields[1], &dpath.HopFields[5], dpath.PathMeta) + + ret := toMsg(t, spkt, dpath) + return ret + }, + prepareDPs: func(*gomock.Controller) []*router.DataPlane { + var dps [5]*router.DataPlane + dps[0] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(40): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 40: topology.Parent, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) + + dps[1] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(31): mock_router.NewMockBatchConn(ctrl), + uint16(1): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 31: topology.Parent, + 1: topology.Child, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:112"), nil, key, sv) + + dps[2] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(5): mock_router.NewMockBatchConn(ctrl), + uint16(7): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 5: topology.Child, + 7: topology.Child, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("1-ff00:0:113"), nil, key, sv) + + dps[3] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(8): mock_router.NewMockBatchConn(ctrl), + uint16(11): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 8: topology.Child, + 11: topology.Parent, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("2-ff00:0:222"), nil, key, sv) + + dps[4] = router.NewDP( + map[uint16]router.BatchConn{ + uint16(3): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 3: topology.Parent, + }, + mock_router.NewMockBatchConn(ctrl), + nil, nil, xtest.MustParseIA("3-ff00:0:333"), nil, key, sv) + + return dps[:] + }, // middle hop of second segment is astransit + srcInterfaces: []uint16{0, 1, 5, 11, 3}, + }, "six hops three segs mixed consdir flyovers": { // two crossovers, first crossover is brtransit, second one is astransit // core segment is non consdir From eba4b011f2cdae1ca1880bd67b025c4626e13a6e Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Thu, 9 Nov 2023 10:03:55 +0100 Subject: [PATCH 017/100] fixed partial reservations integration test and integration test flags --- pkg/hummingbird/hummingbird.go | 3 +++ tools/end2end_hbird_integration/main.go | 2 ++ 2 files changed, 5 insertions(+) diff --git a/pkg/hummingbird/hummingbird.go b/pkg/hummingbird/hummingbird.go index 50f293cbff..dcbaf4e748 100644 --- a/pkg/hummingbird/hummingbird.go +++ b/pkg/hummingbird/hummingbird.go @@ -202,6 +202,9 @@ func (c *HummingbirdClient) GetPathASes() []addr.IA { func (c *HummingbirdClient) RequestReservationForASes(asin []addr.IA, bw uint16, start uint32, duration uint16) error { j := 0 for i := range c.dec.HopFields { + if j >= len(asin) || asin[j] != c.ases[i] { + continue + } var infIdx int var firstHopAfterXover, lastHopBeforeXover bool diff --git a/tools/end2end_hbird_integration/main.go b/tools/end2end_hbird_integration/main.go index 7077b1d7dc..9de511fe20 100644 --- a/tools/end2end_hbird_integration/main.go +++ b/tools/end2end_hbird_integration/main.go @@ -286,6 +286,8 @@ func clientTemplate(progressSock string) integration.Cmd { "-local", integration.SrcAddrPattern + ":0", "-remote", integration.DstAddrPattern + ":" + integration.ServerPortReplace, fmt.Sprintf("-epic=%t", epic), + fmt.Sprintf("-flyovers=%t", flyovers), + fmt.Sprintf("-partial=%t", partial), }, } if len(features) != 0 { From 550f2c075bcbe4be448e38271b72e1d6c3129da2 Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Thu, 9 Nov 2023 11:56:27 +0100 Subject: [PATCH 018/100] added derivation function deriving hbird sv from master key, minor changes --- pkg/hummingbird/hummingbird.go | 2 +- router/control/conf.go | 14 +++++++++++++- router/dataplane_hbird.go | 29 ++++++++++++++++++++--------- 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/pkg/hummingbird/hummingbird.go b/pkg/hummingbird/hummingbird.go index dcbaf4e748..46e2b08c54 100644 --- a/pkg/hummingbird/hummingbird.go +++ b/pkg/hummingbird/hummingbird.go @@ -53,7 +53,7 @@ func cheat_auth_key(res *Reservation) (Reservation, error) { if err != nil { return *res, err } - key0 := control.DeriveHFMacKey(mkeys.Key0) + key0 := control.DeriveHbirdSecretValue(mkeys.Key0) prf, _ := aes.NewCipher(key0) buffer := make([]byte, 16) ak := hummingbird.DeriveAuthKey(prf, res.ResID, res.Bw, res.Ingress, res.Egress, res.StartTime, res.Duration, buffer) diff --git a/router/control/conf.go b/router/control/conf.go index afb168544e..2279010bcf 100644 --- a/router/control/conf.go +++ b/router/control/conf.go @@ -125,7 +125,8 @@ func ConfigDataplane(dp Dataplane, cfg *Config) error { if err := dp.SetKey(cfg.IA, 0, key0); err != nil { return err } - if err := dp.SetSecretValue(cfg.IA, 0, key0); err != nil { + keySv := DeriveHbirdSecretValue(cfg.MasterKeys.Key0) + if err := dp.SetSecretValue(cfg.IA, 0, keySv); err != nil { return err } } @@ -160,6 +161,17 @@ func DeriveHFMacKey(k []byte) []byte { return pbkdf2.Key(k, hfMacSalt, 1000, 16, sha256.New) } +// DeriveHbirdSecretValue derives hummingbird AS secret value from the given key +func DeriveHbirdSecretValue(k []byte) []byte { + if len(k) == 0 { + panic("empty key") + } + hbirdSalt := []byte("Derive hbird sv") + // This uses 16B keys with 1000 hash iterations, which is the same as the + // defaults used by pycrypto. + return pbkdf2.Key(k, hbirdSalt, 1000, 16, sha256.New) +} + func confExternalInterfaces(dp Dataplane, cfg *Config) error { // Sort out keys/ifids to get deterministic order for unit testing infoMap := cfg.Topo.IFInfoMap() diff --git a/router/dataplane_hbird.go b/router/dataplane_hbird.go index d1f515ab7a..6b2d045888 100644 --- a/router/dataplane_hbird.go +++ b/router/dataplane_hbird.go @@ -158,13 +158,7 @@ func (p *scionPacketProcessor) verifyCurrentHbirdMAC() (processResult, error) { // Therefore, we check this here instead of before MAC computation like in standard SCiON if !p.hbirdPath.IsFirstHopAfterXover() { if p.flyoverField.Flyover { - // de-aggregate first two bytes of mac - p.hopField.Mac[0] ^= flyoverMac[0] - p.hopField.Mac[1] ^= flyoverMac[1] - err := p.updateHbirdNonConsDirIngressSegID() - // restore correct state of MAC field, even if error - p.hopField.Mac[0] ^= flyoverMac[0] - p.hopField.Mac[1] ^= flyoverMac[1] + err := p.updateHbirdNonConsDirIngressSegIDFlyover(flyoverMac) if err != nil { return processResult{}, err } @@ -335,11 +329,28 @@ func (p *scionPacketProcessor) handleHbirdEgressRouterAlert() (processResult, er return processResult{SlowPathRequest: slowPathRequest}, slowPathRequired } +func (p *scionPacketProcessor) updateHbirdNonConsDirIngressSegIDFlyover(flyoverMac []byte) error { + // against construction dir the ingress router updates the SegID, ifID == 0 + // means this comes from this AS itself, so nothing has to be done. + // If a flyover is presebt, need to first de-aggregate the first two bytes of the mac before updating segID + if !p.infoField.ConsDir && p.ingressID != 0 && !p.peering { + // de-aggregate first two bytes of mac + p.hopField.Mac[0] ^= flyoverMac[0] + p.hopField.Mac[1] ^= flyoverMac[1] + p.infoField.UpdateSegID(p.hopField.Mac) + // restore correct state of MAC field, even if error + p.hopField.Mac[0] ^= flyoverMac[0] + p.hopField.Mac[1] ^= flyoverMac[1] + if err := p.hbirdPath.SetInfoField(p.infoField, int(p.hbirdPath.PathMeta.CurrINF)); err != nil { + return serrors.WrapStr("update info field", err) + } + } + return nil +} + func (p *scionPacketProcessor) updateHbirdNonConsDirIngressSegID() error { // against construction dir the ingress router updates the SegID, ifID == 0 // means this comes from this AS itself, so nothing has to be done. - // TODO(lukedirtwalker): For packets destined to peer links this shouldn't - // be updated. if !p.infoField.ConsDir && p.ingressID != 0 && !p.peering { p.infoField.UpdateSegID(p.hopField.Mac) if err := p.hbirdPath.SetInfoField(p.infoField, int(p.hbirdPath.PathMeta.CurrINF)); err != nil { From 4b21e2caf56734b96fa945b379f04b03664a5d36 Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Thu, 9 Nov 2023 12:17:32 +0100 Subject: [PATCH 019/100] changed flyover mac from cmac to aes-cbc mac. Removed unused mac comparison functions --- pkg/slayers/path/hummingbird/mac.go | 39 -------------- pkg/slayers/path/hummingbird/mac_test.go | 68 ++---------------------- 2 files changed, 4 insertions(+), 103 deletions(-) diff --git a/pkg/slayers/path/hummingbird/mac.go b/pkg/slayers/path/hummingbird/mac.go index 6f79c902c5..db8767079d 100644 --- a/pkg/slayers/path/hummingbird/mac.go +++ b/pkg/slayers/path/hummingbird/mac.go @@ -5,11 +5,9 @@ package hummingbird import ( "crypto/aes" "crypto/cipher" - "crypto/subtle" "encoding/binary" "github.com/scionproto/scion/pkg/addr" - "github.com/scionproto/scion/pkg/slayers/path" ) // defined in asm_* assembly files @@ -81,45 +79,8 @@ func FullFlyoverMac(ak []byte, dstIA addr.IA, pktlen uint16, resStartTime uint16 binary.BigEndian.PutUint32(buffer[12:16], highResTime) expandKeyAsm(10, &ak[0], &xkbuffer[0]) - //compute subkeys - encryptBlockAsm(10, &xkbuffer[0], &buffer[16], &ZeroBlock[0]) - - // Compute K1. Ignore K2 since we will always use K1 - flag1 := buffer[16]&byte(128) == 0 - shiftLeft(buffer[16:32]) - if !flag1 { - buffer[31] ^= 0x87 - } - //Compute cmac - xor(buffer[0:16], buffer[16:32]) encryptBlockAsm(10, &xkbuffer[0], &buffer[0], &buffer[0]) return buffer[0:16] } - -// Compares two 16 byte arrays. -// Always returns false if at least one input has a length different from 16 -// Returns true if equal, false otherwise -func CompareAk(a []byte, b []byte) bool { - if len(a) != 16 || len(b) != 16 { - return false - } - return binary.BigEndian.Uint64(a[0:8]) == binary.BigEndian.Uint64(b[0:8]) && binary.BigEndian.Uint64(a[8:16]) == binary.BigEndian.Uint64(b[8:16]) -} - -// around 800 ns - -// Compares two 6 byte arrays. -// Always returns false if at least one input is of a different length. -// Returns true if equal, false otherwise. -func CompareVk(a, b []byte) bool { - if len(a) != 6 || len(b) != 6 { - return false - } - return binary.BigEndian.Uint32(a) == binary.BigEndian.Uint32(b) && a[4] == b[4] && a[5] == b[5] -} - -func SubtleCompare(a, b []byte) bool { - return subtle.ConstantTimeCompare(a[:path.MacLen], b[:path.MacLen]) == 0 -} diff --git a/pkg/slayers/path/hummingbird/mac_test.go b/pkg/slayers/path/hummingbird/mac_test.go index c08c70b221..edc3347d56 100644 --- a/pkg/slayers/path/hummingbird/mac_test.go +++ b/pkg/slayers/path/hummingbird/mac_test.go @@ -6,7 +6,6 @@ import ( "encoding/binary" "testing" - "github.com/dchest/cmac" "github.com/scionproto/scion/pkg/addr" "github.com/scionproto/scion/pkg/slayers/path/hummingbird" "github.com/stretchr/testify/require" @@ -79,6 +78,7 @@ func BenchmarkDeriveAuthKey(b *testing.B) { // BenchmarkDeriveAuthKeyManually benchmarks obtaining Ak by just using the stdlib. // Results in my machine of 5.987 ns/op. +// Does not take into account the process of moving data into the buffer func BenchmarkDeriveAuthKeyManually(b *testing.B) { sv := []byte{0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7} var resId uint32 = 0x40 @@ -108,9 +108,8 @@ func BenchmarkDeriveAuthKeyManually(b *testing.B) { } } -// verified with https://artjomb.github.io/cryptojs-extension/ - -func TestFlyOverMac(t *testing.T) { +// We use CBC-MAC using aes for the flyover mac. As we have only one block of input, this leads to the mac consisting of a single aes call. +func TestFlyoverMac(t *testing.T) { ak := []byte{142, 19, 145, 119, 76, 2, 228, 18, 134, 111, 116, 45, 200, 172, 113, 219} var dstIA addr.IA = 326 var pktlen uint16 = 23 @@ -125,22 +124,12 @@ func TestFlyOverMac(t *testing.T) { binary.BigEndian.PutUint16(expected[8:10], pktlen) binary.BigEndian.PutUint16(expected[10:12], resStartTs) binary.BigEndian.PutUint32(expected[12:16], highResTs) - block, err := aes.NewCipher(ak) if err != nil { require.Fail(t, err.Error()) } - c, err := cmac.New(block) - if err != nil { - require.Fail(t, err.Error()) - } - if _, err := c.Write(expected[0:16]); err != nil { - require.Fail(t, err.Error()) - } - - expected = c.Sum(expected[:0]) + block.Encrypt(expected[:], expected[:]) - //expected with 0, 23, 1234, 4321: 726f7d9e 17e3cbe1 d47a32eb d8a5e26e mac := hummingbird.FullFlyoverMac(ak, dstIA, pktlen, resStartTs, highResTs, buffer, xkbuffer) require.Equal(t, expected, mac) mac = hummingbird.FullFlyoverMac(ak, dstIA, pktlen, resStartTs, highResTs, buffer, xkbuffer) @@ -161,52 +150,3 @@ func BenchmarkFlyoverMac(b *testing.B) { hummingbird.FullFlyoverMac(ak, dstIA, pktlen, resStartTs, highResTs, buffer, xkbuffer) } } - -func TestCompareAk(t *testing.T) { - a := []byte{142, 19, 145, 119, 76, 2, 228, 18, 134, 111, 116, 45, 200, 172, 113, 219} - b := []byte{142, 19, 145, 151, 76, 2, 228, 18, 134, 111, 116, 45, 200, 172, 113, 219} - c := []byte{142, 19, 145, 119, 76, 2, 228, 18, 134, 111, 116, 45, 200, 172, 113, 218} - d := []byte{142, 19, 145, 119, 76, 2, 228, 18, 134, 111, 116, 45, 200, 172, 113, 219} - - require.True(t, hummingbird.CompareAk(a, d)) - require.False(t, hummingbird.CompareAk(a, b)) - require.False(t, hummingbird.CompareAk(a, c)) -} - -func BenchmarkCompareAk(b *testing.B) { - a := []byte{142, 19, 145, 119, 76, 2, 228, 18, 134, 111, 116, 45, 200, 172, 113, 219} - c := []byte{142, 19, 145, 119, 76, 2, 228, 18, 134, 111, 116, 45, 200, 172, 113, 218} - b.ResetTimer() - for i := 0; i < b.N; i++ { - hummingbird.CompareAk(a, c) - } -} - -func TestCompareVk(t *testing.T) { - a := []byte{1, 2, 3, 4, 5, 6} - b := []byte{1, 2, 3, 4, 5, 6} - c := []byte{2, 2, 3, 4, 5, 6} - d := []byte{1, 2, 3, 6, 5, 6} - - require.True(t, hummingbird.CompareVk(a, b)) - require.False(t, hummingbird.CompareVk(a, c)) - require.False(t, hummingbird.CompareVk(a, d)) -} - -func BenchmarkCompareVk(b *testing.B) { - a := []byte{1, 2, 3, 4, 5, 6} - c := []byte{1, 2, 4, 4, 5, 6} - b.ResetTimer() - for i := 0; i < b.N; i++ { - hummingbird.CompareVk(a, c) - } -} - -func BenchmarkSubtleCompare(b *testing.B) { - a := []byte{1, 2, 3, 4, 5, 6} - c := []byte{1, 2, 4, 4, 5, 6} - b.ResetTimer() - for i := 0; i < b.N; i++ { - hummingbird.SubtleCompare(a, c) - } -} From 05612cd3dc00c9b3602f572c60981b26fe138539 Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Thu, 9 Nov 2023 12:29:50 +0100 Subject: [PATCH 020/100] added golden data tests for flyover mac computation --- pkg/slayers/path/hummingbird/mac_test.go | 50 +++++++++++++++++++++++- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/pkg/slayers/path/hummingbird/mac_test.go b/pkg/slayers/path/hummingbird/mac_test.go index edc3347d56..4d31a98aa6 100644 --- a/pkg/slayers/path/hummingbird/mac_test.go +++ b/pkg/slayers/path/hummingbird/mac_test.go @@ -54,6 +54,33 @@ func TestDeriveAuthKey(t *testing.T) { require.Equal(t, expected, key) } +// Golden Data test used for cross verification with other implementations +func TestDeriveAuthKeyGoldenData(t *testing.T) { + sv := []byte{0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7} + var resId uint32 = 0x40 + var bw uint16 = 0x0203 + buffer := make([]byte, 16) + var in uint16 = 2 + var eg uint16 = 5 + var start uint32 = 0x0030001 + var duration uint16 = 0x0203 + + // Compute expected result with library CBC + expected := [16]byte{0x7e, 0x61, 0x4, 0x91, 0x30, 0x6b, 0x95, 0xec, 0xb5, 0x75, 0xc6, 0xe9, 0x4c, 0x5a, 0x89, 0x84} + + // Run DeriveAuthKey Function + block, err := aes.NewCipher(sv) + if err != nil { + require.Fail(t, err.Error()) + } + + key := hummingbird.DeriveAuthKey(block, resId, bw, in, eg, start, duration, buffer) + require.Equal(t, expected[:], key) + + key = hummingbird.DeriveAuthKey(block, resId, bw, in, eg, start, duration, buffer) + require.Equal(t, expected[:], key) +} + func BenchmarkDeriveAuthKey(b *testing.B) { sv := []byte{0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7} @@ -110,7 +137,7 @@ func BenchmarkDeriveAuthKeyManually(b *testing.B) { // We use CBC-MAC using aes for the flyover mac. As we have only one block of input, this leads to the mac consisting of a single aes call. func TestFlyoverMac(t *testing.T) { - ak := []byte{142, 19, 145, 119, 76, 2, 228, 18, 134, 111, 116, 45, 200, 172, 113, 219} + ak := []byte{0x7e, 0x61, 0x4, 0x91, 0x30, 0x6b, 0x95, 0xec, 0xb5, 0x75, 0xc6, 0xe9, 0x4c, 0x5a, 0x89, 0x84} var dstIA addr.IA = 326 var pktlen uint16 = 23 var resStartTs uint16 = 1234 @@ -136,8 +163,27 @@ func TestFlyoverMac(t *testing.T) { require.Equal(t, expected, mac) } +// Golden data test used for cross verification with other implementations +func TestFlyoverMacGoldenData(t *testing.T) { + ak := []byte{0x7e, 0x61, 0x4, 0x91, 0x30, 0x6b, 0x95, 0xec, 0xb5, 0x75, 0xc6, 0xe9, 0x4c, 0x5a, 0x89, 0x84} + var dstIA addr.IA = 326 + var pktlen uint16 = 23 + var resStartTs uint16 = 1234 + var highResTs uint32 = 4321 + buffer := make([]byte, 32) + xkbuffer := make([]uint32, 44) + + // Compute expected output based on library cmac implementation + expected := [16]byte{0xbe, 0xad, 0xcf, 0x70, 0xf, 0x75, 0xdf, 0x8, 0xde, 0x91, 0xe9, 0xda, 0xf5, 0xcb, 0x9f, 0x74} + + mac := hummingbird.FullFlyoverMac(ak, dstIA, pktlen, resStartTs, highResTs, buffer, xkbuffer) + require.Equal(t, expected[:], mac) + mac = hummingbird.FullFlyoverMac(ak, dstIA, pktlen, resStartTs, highResTs, buffer, xkbuffer) + require.Equal(t, expected[:], mac) +} + func BenchmarkFlyoverMac(b *testing.B) { - ak := []byte{142, 19, 145, 119, 76, 2, 228, 18, 134, 111, 116, 45, 200, 172, 113, 219} + ak := []byte{0x7e, 0x61, 0x4, 0x91, 0x30, 0x6b, 0x95, 0xec, 0xb5, 0x75, 0xc6, 0xe9, 0x4c, 0x5a, 0x89, 0x84} var dstIA addr.IA = 326 var pktlen uint16 = 23 var resStartTs uint16 = 1234 From 7390a752e81d72b47cde9aef0adc687db520e15a Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Thu, 9 Nov 2023 12:31:53 +0100 Subject: [PATCH 021/100] adapted comments --- pkg/slayers/path/hummingbird/mac_test.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pkg/slayers/path/hummingbird/mac_test.go b/pkg/slayers/path/hummingbird/mac_test.go index 4d31a98aa6..dc925ba22a 100644 --- a/pkg/slayers/path/hummingbird/mac_test.go +++ b/pkg/slayers/path/hummingbird/mac_test.go @@ -65,7 +65,6 @@ func TestDeriveAuthKeyGoldenData(t *testing.T) { var start uint32 = 0x0030001 var duration uint16 = 0x0203 - // Compute expected result with library CBC expected := [16]byte{0x7e, 0x61, 0x4, 0x91, 0x30, 0x6b, 0x95, 0xec, 0xb5, 0x75, 0xc6, 0xe9, 0x4c, 0x5a, 0x89, 0x84} // Run DeriveAuthKey Function @@ -145,7 +144,7 @@ func TestFlyoverMac(t *testing.T) { buffer := make([]byte, 32) xkbuffer := make([]uint32, 44) - // Compute expected output based on library cmac implementation + // Compute expected output based on library cbc-mac implementation expected := make([]byte, 16) binary.BigEndian.PutUint64(expected[0:8], uint64(dstIA)) binary.BigEndian.PutUint16(expected[8:10], pktlen) @@ -173,7 +172,6 @@ func TestFlyoverMacGoldenData(t *testing.T) { buffer := make([]byte, 32) xkbuffer := make([]uint32, 44) - // Compute expected output based on library cmac implementation expected := [16]byte{0xbe, 0xad, 0xcf, 0x70, 0xf, 0x75, 0xdf, 0x8, 0xde, 0x91, 0xe9, 0xda, 0xf5, 0xcb, 0x9f, 0x74} mac := hummingbird.FullFlyoverMac(ak, dstIA, pktlen, resStartTs, highResTs, buffer, xkbuffer) From bec9b4c4c704b5fc6712e58b0dfb7bc6ccbc8c63 Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Thu, 9 Nov 2023 13:40:12 +0100 Subject: [PATCH 022/100] Removed the path.FlyoverLen and Path.LineLen constants which were duplicate of the ones in the hummingbird package --- pkg/slayers/path/hopfield.go | 4 ---- pkg/slayers/path/hummingbird/base.go | 2 +- pkg/slayers/path/hummingbird/decoded.go | 8 ++++---- pkg/slayers/path/hummingbird/mac.go | 15 -------------- pkg/slayers/path/hummingbird/raw.go | 26 ++++++++++++------------- 5 files changed, 18 insertions(+), 37 deletions(-) diff --git a/pkg/slayers/path/hopfield.go b/pkg/slayers/path/hopfield.go index 192debb2fc..5803a951aa 100644 --- a/pkg/slayers/path/hopfield.go +++ b/pkg/slayers/path/hopfield.go @@ -26,10 +26,6 @@ import ( const ( // HopLen is the size of a HopField in bytes. HopLen = 12 - // FlyoverLen is the length of a FlyoverHopField in bytes - FlyoverLen = 20 - // LineLen is the length of a line for the computation of the hop offset of hummingbird packets - LineLen = 4 // MacLen is the size of the MAC of each HopField. MacLen = 6 // MacOffset is the offset of the MAC field from the beginning of the HopField diff --git a/pkg/slayers/path/hummingbird/base.go b/pkg/slayers/path/hummingbird/base.go index 745074cf9c..697f5c17b0 100644 --- a/pkg/slayers/path/hummingbird/base.go +++ b/pkg/slayers/path/hummingbird/base.go @@ -93,7 +93,7 @@ func (s *Base) InfIndexForHF(hf uint8) uint8 { // Len returns the length of the path in bytes. func (s *Base) Len() int { - return MetaLen + s.NumINF*path.InfoLen + s.NumHops*path.LineLen + return MetaLen + s.NumINF*path.InfoLen + s.NumHops*LineLen } // Type returns the type of the path. diff --git a/pkg/slayers/path/hummingbird/decoded.go b/pkg/slayers/path/hummingbird/decoded.go index e5a335b54c..d68002bf06 100644 --- a/pkg/slayers/path/hummingbird/decoded.go +++ b/pkg/slayers/path/hummingbird/decoded.go @@ -51,7 +51,7 @@ func (s *Decoded) DecodeFromBytes(data []byte) error { i, j := 0, 0 // If last hop is not a flyover hop, decode it with only 12 bytes slice for ; j < s.NumHops-3; i++ { - if err := s.HopFields[i].DecodeFromBytes(data[offset : offset+path.FlyoverLen]); err != nil { + if err := s.HopFields[i].DecodeFromBytes(data[offset : offset+FlyoverLen]); err != nil { return err } // Set FirstHopPerSeg @@ -62,7 +62,7 @@ func (s *Decoded) DecodeFromBytes(data []byte) error { } if s.HopFields[i].Flyover { - offset += path.FlyoverLen + offset += FlyoverLen j += 5 } else { offset += path.HopLen @@ -108,10 +108,10 @@ func (s *Decoded) SerializeTo(b []byte) error { } for _, hop := range s.HopFields { if hop.Flyover { - if err := hop.SerializeTo(b[offset : offset+path.FlyoverLen]); err != nil { + if err := hop.SerializeTo(b[offset : offset+FlyoverLen]); err != nil { return err } - offset += path.FlyoverLen + offset += FlyoverLen } else { if err := hop.SerializeTo(b[offset : offset+path.HopLen]); err != nil { return err diff --git a/pkg/slayers/path/hummingbird/mac.go b/pkg/slayers/path/hummingbird/mac.go index db8767079d..1fe0c2093a 100644 --- a/pkg/slayers/path/hummingbird/mac.go +++ b/pkg/slayers/path/hummingbird/mac.go @@ -47,21 +47,6 @@ func DeriveAuthKey(block cipher.Block, resId uint32, bw, in, eg uint16, startTim return buffer[0:16] } -// shifts left a 16 bytes array -func shiftLeft(in []byte) { - flag := (in[8]&byte(128))>>7 == 1 - binary.BigEndian.PutUint64(in[0:8], binary.BigEndian.Uint64(in[0:8])<<1) - binary.BigEndian.PutUint64(in[8:16], binary.BigEndian.Uint64(in[8:16])<<1) - if flag { - in[7] |= 0x01 - } -} - -func xor(a, b []byte) { - binary.BigEndian.PutUint64(a[0:8], binary.BigEndian.Uint64(a[0:8])^binary.BigEndian.Uint64(b[0:8])) - binary.BigEndian.PutUint64(a[8:16], binary.BigEndian.Uint64(a[8:16])^binary.BigEndian.Uint64(b[8:16])) -} - // Computes full flyover mac vk // Needs a xkbuffer of 44 uint32s to store the expanded keys for aes // dummy buffer is memory used by key expansion to store decryption keys diff --git a/pkg/slayers/path/hummingbird/raw.go b/pkg/slayers/path/hummingbird/raw.go index c90630c3e1..fb8e082edd 100644 --- a/pkg/slayers/path/hummingbird/raw.go +++ b/pkg/slayers/path/hummingbird/raw.go @@ -135,10 +135,10 @@ func (s *Raw) GetHopField(idx int) (FlyoverHopField, error) { return FlyoverHopField{}, serrors.New("HopField index out of bounds", "max", s.NumHops-3, "actual", idx) } - hopOffset := MetaLen + s.NumINF*path.InfoLen + idx*path.LineLen + hopOffset := MetaLen + s.NumINF*path.InfoLen + idx*LineLen hop := FlyoverHopField{} // Let the decoder read a big enough slice in case it is a FlyoverHopField - maxHopLen := path.FlyoverLen + maxHopLen := FlyoverLen if idx > s.NumHops-5 { if idx == s.NumHops-3 { maxHopLen = path.HopLen @@ -162,7 +162,7 @@ func (s *Raw) ReplacMac(idx int, mac []byte) error { if idx >= s.NumHops-2 { return serrors.New("HopField index out of bounds", "max", s.NumHops-3, "actual", idx) } - offset := s.NumINF*path.InfoLen + MetaLen + idx*path.LineLen + path.MacOffset + offset := s.NumINF*path.InfoLen + MetaLen + idx*LineLen + path.MacOffset if n := copy(s.Raw[offset:offset+path.MacLen], mac[:path.MacLen]); n != path.MacLen { return serrors.New("copied worng number of bytes for mac replacement", "expected", path.MacLen, "actual", n) } @@ -179,7 +179,7 @@ func (s *Raw) GetMac(idx int) ([]byte, error) { if idx >= s.NumHops-2 { return nil, serrors.New("HopField index out of bounds", "max", s.NumHops-3, "actual", idx) } - offset := s.NumINF*path.InfoLen + MetaLen + idx*path.LineLen + path.MacOffset + offset := s.NumINF*path.InfoLen + MetaLen + idx*LineLen + path.MacOffset return s.Raw[offset : offset+path.MacLen], nil } @@ -192,7 +192,7 @@ func (s *Raw) SetHopField(hop FlyoverHopField, idx int) error { if idx >= s.NumHops-2 { return serrors.New("HopField index out of bounds", "max", s.NumHops-3, "actual", idx) } - hopOffset := MetaLen + s.NumINF*path.InfoLen + idx*path.LineLen + hopOffset := MetaLen + s.NumINF*path.InfoLen + idx*LineLen if s.Raw[hopOffset]&0x80 == 0x80 { // IF the current hop is a flyover, the flyover bit of the new hop is set to 1 in order to preserve correctness of the path // The reservation data of the new hop is dummy data and invalid. @@ -205,11 +205,11 @@ func (s *Raw) SetHopField(hop FlyoverHopField, idx int) error { if idx >= s.NumHops-4 { return serrors.New("FlyoverHopField index out of bounds", "max", s.NumHops-5, "actual", idx) } - hopOffset := MetaLen + s.NumINF*path.InfoLen + idx*path.LineLen + hopOffset := MetaLen + s.NumINF*path.InfoLen + idx*LineLen if s.Raw[hopOffset]&0x80 == 0x00 { return serrors.New("Setting FlyoverHopField over Hopfield with setHopField not supported") } - return hop.SerializeTo(s.Raw[hopOffset : hopOffset+path.FlyoverLen]) + return hop.SerializeTo(s.Raw[hopOffset : hopOffset+FlyoverLen]) } return hop.SerializeTo(s.Raw[hopOffset : hopOffset+path.HopLen]) } @@ -227,7 +227,7 @@ func (s *Raw) IsLastHop() bool { // Returns the egress interface of the next hop func (s *Raw) GetNextEgress() (uint16, error) { idx := int(s.Base.PathMeta.CurrHF) - hopOffset := MetaLen + s.NumINF*path.InfoLen + idx*path.LineLen + hopOffset := MetaLen + s.NumINF*path.InfoLen + idx*LineLen if s.Raw[hopOffset]&0x80 == 0x80 { idx += FlyoverLines hopOffset += FlyoverLines * LineLen @@ -251,7 +251,7 @@ func (s *Raw) GetPreviousIngress() (uint16, error) { if idx < 0 { return 0, serrors.New("HopField index out of bounds", "min", 0, "actual", idx) } - hopOffset := MetaLen + s.NumINF*path.InfoLen + idx*path.LineLen + hopOffset := MetaLen + s.NumINF*path.InfoLen + idx*LineLen if s.isConsdir(uint8(idx)) { return binary.BigEndian.Uint16(s.Raw[hopOffset+2 : hopOffset+4]), nil } @@ -268,7 +268,7 @@ func (s *Raw) DoFlyoverXover() error { if s.PathMeta.CurrINF == 2 { return serrors.New("Cannot do FlyoverXover if CurrINF = 2") } - hopOffset := MetaLen + s.NumINF*path.InfoLen + idx*path.LineLen + hopOffset := MetaLen + s.NumINF*path.InfoLen + idx*LineLen if s.Raw[hopOffset]&0x80 == 0x00 { return serrors.New("Current hop does not have a Flyover") } @@ -278,8 +278,8 @@ func (s *Raw) DoFlyoverXover() error { // buffer flyover and copy data var t [2 * LineLen]byte copy(t[:], s.Raw[hopOffset+path.HopLen:hopOffset+FlyoverLen]) - copy(s.Raw[hopOffset+path.HopLen:hopOffset+2*path.HopLen], s.Raw[hopOffset+path.FlyoverLen:hopOffset+path.FlyoverLen+path.HopLen]) - copy(s.Raw[hopOffset+2*path.HopLen:hopOffset+path.HopLen+path.FlyoverLen], t[:]) + copy(s.Raw[hopOffset+path.HopLen:hopOffset+2*path.HopLen], s.Raw[hopOffset+FlyoverLen:hopOffset+FlyoverLen+path.HopLen]) + copy(s.Raw[hopOffset+2*path.HopLen:hopOffset+path.HopLen+FlyoverLen], t[:]) // Unset and Set Flyoverbits s.Raw[hopOffset] &= 0x7f @@ -300,7 +300,7 @@ func (s *Raw) ReverseFlyoverXover() error { if s.PathMeta.CurrINF == 0 { return serrors.New("Cannot reverse Flyover Xover when CurrINF = 0") } - hopOffset := MetaLen + s.NumINF*path.InfoLen + idx*path.LineLen + hopOffset := MetaLen + s.NumINF*path.InfoLen + idx*LineLen if s.Raw[hopOffset]&0x80 == 0x00 { return serrors.New("Current hop does not have a Flyover") } From 22613ec43ba66e838935e9c0088c0849e14a1024 Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Thu, 9 Nov 2023 14:05:56 +0100 Subject: [PATCH 023/100] better separation between hummingbird and scion path constants --- pkg/slayers/path/hopfield.go | 2 -- pkg/slayers/path/hummingbird/decoded.go | 8 ++--- .../path/hummingbird/flyoverhopfield.go | 14 +++++++-- pkg/slayers/path/hummingbird/raw.go | 30 +++++++++---------- 4 files changed, 30 insertions(+), 24 deletions(-) diff --git a/pkg/slayers/path/hopfield.go b/pkg/slayers/path/hopfield.go index 5803a951aa..3ac1425505 100644 --- a/pkg/slayers/path/hopfield.go +++ b/pkg/slayers/path/hopfield.go @@ -28,8 +28,6 @@ const ( HopLen = 12 // MacLen is the size of the MAC of each HopField. MacLen = 6 - // MacOffset is the offset of the MAC field from the beginning of the HopField - MacOffset = 6 ) // MaxTTL is the maximum age of a HopField. diff --git a/pkg/slayers/path/hummingbird/decoded.go b/pkg/slayers/path/hummingbird/decoded.go index d68002bf06..d349de764f 100644 --- a/pkg/slayers/path/hummingbird/decoded.go +++ b/pkg/slayers/path/hummingbird/decoded.go @@ -65,12 +65,12 @@ func (s *Decoded) DecodeFromBytes(data []byte) error { offset += FlyoverLen j += 5 } else { - offset += path.HopLen + offset += HopLen j += 3 } } if j == s.NumHops-3 { - if err := s.HopFields[i].DecodeFromBytes(data[offset : offset+path.HopLen]); err != nil { + if err := s.HopFields[i].DecodeFromBytes(data[offset : offset+HopLen]); err != nil { return err } i++ @@ -113,10 +113,10 @@ func (s *Decoded) SerializeTo(b []byte) error { } offset += FlyoverLen } else { - if err := hop.SerializeTo(b[offset : offset+path.HopLen]); err != nil { + if err := hop.SerializeTo(b[offset : offset+HopLen]); err != nil { return err } - offset += path.HopLen + offset += HopLen } } diff --git a/pkg/slayers/path/hummingbird/flyoverhopfield.go b/pkg/slayers/path/hummingbird/flyoverhopfield.go index 1b2d5154a4..9c040dd8db 100644 --- a/pkg/slayers/path/hummingbird/flyoverhopfield.go +++ b/pkg/slayers/path/hummingbird/flyoverhopfield.go @@ -8,9 +8,17 @@ import ( ) const ( - LineLen = 4 - FlyoverLen = 20 - HopLines = 3 + // LineLen is the number of bytes in a line as considered by CurrHF in the PathMEtaHeader + LineLen = 4 + // Length in bytes of a FlyoverHopField + FlyoverLen = 20 + // HopLen is the size of a HopField in bytes. + HopLen = 12 + // MacOffset is the offset of the MAC field from the beginning of the HopField + MacOffset = 6 + // The number of lines in a hopfield + HopLines = 3 + // The number of lines in a flyoverhopfield FlyoverLines = 5 ) diff --git a/pkg/slayers/path/hummingbird/raw.go b/pkg/slayers/path/hummingbird/raw.go index fb8e082edd..80f017f8db 100644 --- a/pkg/slayers/path/hummingbird/raw.go +++ b/pkg/slayers/path/hummingbird/raw.go @@ -141,7 +141,7 @@ func (s *Raw) GetHopField(idx int) (FlyoverHopField, error) { maxHopLen := FlyoverLen if idx > s.NumHops-5 { if idx == s.NumHops-3 { - maxHopLen = path.HopLen + maxHopLen = HopLen } else { return FlyoverHopField{}, serrors.New("Invalid hopfield index", "NumHops", s.NumHops, "index", idx) } @@ -162,7 +162,7 @@ func (s *Raw) ReplacMac(idx int, mac []byte) error { if idx >= s.NumHops-2 { return serrors.New("HopField index out of bounds", "max", s.NumHops-3, "actual", idx) } - offset := s.NumINF*path.InfoLen + MetaLen + idx*LineLen + path.MacOffset + offset := s.NumINF*path.InfoLen + MetaLen + idx*LineLen + MacOffset if n := copy(s.Raw[offset:offset+path.MacLen], mac[:path.MacLen]); n != path.MacLen { return serrors.New("copied worng number of bytes for mac replacement", "expected", path.MacLen, "actual", n) } @@ -179,7 +179,7 @@ func (s *Raw) GetMac(idx int) ([]byte, error) { if idx >= s.NumHops-2 { return nil, serrors.New("HopField index out of bounds", "max", s.NumHops-3, "actual", idx) } - offset := s.NumINF*path.InfoLen + MetaLen + idx*LineLen + path.MacOffset + offset := s.NumINF*path.InfoLen + MetaLen + idx*LineLen + MacOffset return s.Raw[offset : offset+path.MacLen], nil } @@ -211,7 +211,7 @@ func (s *Raw) SetHopField(hop FlyoverHopField, idx int) error { } return hop.SerializeTo(s.Raw[hopOffset : hopOffset+FlyoverLen]) } - return hop.SerializeTo(s.Raw[hopOffset : hopOffset+path.HopLen]) + return hop.SerializeTo(s.Raw[hopOffset : hopOffset+HopLen]) } // IsFirstHop returns whether the current hop is the first hop on the path. @@ -277,13 +277,13 @@ func (s *Raw) DoFlyoverXover() error { } // buffer flyover and copy data var t [2 * LineLen]byte - copy(t[:], s.Raw[hopOffset+path.HopLen:hopOffset+FlyoverLen]) - copy(s.Raw[hopOffset+path.HopLen:hopOffset+2*path.HopLen], s.Raw[hopOffset+FlyoverLen:hopOffset+FlyoverLen+path.HopLen]) - copy(s.Raw[hopOffset+2*path.HopLen:hopOffset+path.HopLen+FlyoverLen], t[:]) + copy(t[:], s.Raw[hopOffset+HopLen:hopOffset+FlyoverLen]) + copy(s.Raw[hopOffset+HopLen:hopOffset+2*HopLen], s.Raw[hopOffset+FlyoverLen:hopOffset+FlyoverLen+HopLen]) + copy(s.Raw[hopOffset+2*HopLen:hopOffset+HopLen+FlyoverLen], t[:]) // Unset and Set Flyoverbits s.Raw[hopOffset] &= 0x7f - s.Raw[hopOffset+path.HopLen] |= 0x80 + s.Raw[hopOffset+HopLen] |= 0x80 // Adatp seglens s.Base.PathMeta.SegLen[s.PathMeta.CurrINF] -= 2 s.Base.PathMeta.SegLen[s.PathMeta.CurrINF+1] += 2 @@ -304,16 +304,16 @@ func (s *Raw) ReverseFlyoverXover() error { if s.Raw[hopOffset]&0x80 == 0x00 { return serrors.New("Current hop does not have a Flyover") } - if s.Raw[hopOffset-path.HopLen]&0x80 != 0x00 { + if s.Raw[hopOffset-HopLen]&0x80 != 0x00 { return serrors.New("Cannot Reverse Flyover Crossover, flyover bit set where previous hop should be") } - var t [FlyoverLen - path.HopLen]byte - copy(t[:], s.Raw[hopOffset+path.HopLen:hopOffset+FlyoverLen]) - copy(s.Raw[hopOffset+FlyoverLen-path.HopLen:hopOffset+FlyoverLen], s.Raw[hopOffset:hopOffset+path.HopLen]) - copy(s.Raw[hopOffset:hopOffset+FlyoverLen-path.HopLen], t[:]) + var t [FlyoverLen - HopLen]byte + copy(t[:], s.Raw[hopOffset+HopLen:hopOffset+FlyoverLen]) + copy(s.Raw[hopOffset+FlyoverLen-HopLen:hopOffset+FlyoverLen], s.Raw[hopOffset:hopOffset+HopLen]) + copy(s.Raw[hopOffset:hopOffset+FlyoverLen-HopLen], t[:]) // Set and Unset Flyoverbits - s.Raw[hopOffset-path.HopLen] |= 0x80 - s.Raw[hopOffset+FlyoverLen-path.HopLen] &= 0x7f + s.Raw[hopOffset-HopLen] |= 0x80 + s.Raw[hopOffset+FlyoverLen-HopLen] &= 0x7f // Adapt Seglens and CurrHF s.Base.PathMeta.SegLen[s.PathMeta.CurrINF] -= 2 s.Base.PathMeta.SegLen[s.PathMeta.CurrINF-1] += 2 From e21a718cbf47f1420fb722690b067f89a22b44b9 Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Thu, 9 Nov 2023 14:16:10 +0100 Subject: [PATCH 024/100] added comments explaining origin and modifications to the hummingbird aes assembly files --- pkg/slayers/path/hummingbird/asm_amd64.s | 3 +++ pkg/slayers/path/hummingbird/asm_arm64.s | 3 +++ pkg/slayers/path/hummingbird/asm_ppc64x.s | 3 +++ 3 files changed, 9 insertions(+) diff --git a/pkg/slayers/path/hummingbird/asm_amd64.s b/pkg/slayers/path/hummingbird/asm_amd64.s index 79fffb37c6..c8884cd346 100644 --- a/pkg/slayers/path/hummingbird/asm_amd64.s +++ b/pkg/slayers/path/hummingbird/asm_amd64.s @@ -1,3 +1,6 @@ +// This file is mostly a copy of the file of the same name in the crypto/aes go package +// The key expansion for the decryption keys has been removed in this file + // Copyright 2012 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/pkg/slayers/path/hummingbird/asm_arm64.s b/pkg/slayers/path/hummingbird/asm_arm64.s index ee91c605d5..517074ddf7 100644 --- a/pkg/slayers/path/hummingbird/asm_arm64.s +++ b/pkg/slayers/path/hummingbird/asm_arm64.s @@ -1,3 +1,6 @@ +// This file is mostly a copy of the file of the same name in the crypto/aes go package +// The key expansion for the decryption keys has been removed in this file + // Copyright 2017 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/pkg/slayers/path/hummingbird/asm_ppc64x.s b/pkg/slayers/path/hummingbird/asm_ppc64x.s index 4ccd0533de..80fe2cfda0 100644 --- a/pkg/slayers/path/hummingbird/asm_ppc64x.s +++ b/pkg/slayers/path/hummingbird/asm_ppc64x.s @@ -1,3 +1,6 @@ +// This file is mostly a copy of the file of the same name in the crypto/aes go package +// The key expansion for the decryption keys has been removed in this file + // Copyright 2016 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. From a235186c94f94821b40473bd6f453e70ce86f629 Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Thu, 9 Nov 2023 14:53:54 +0100 Subject: [PATCH 025/100] renamed NumHops to NumLines for Hummingbird, made use of the FlyoverLines and HopLines hummingbird variables wherever possible --- pkg/hummingbird/hummingbird.go | 2 +- pkg/slayers/path/hummingbird/base.go | 24 +-- pkg/slayers/path/hummingbird/base_test.go | 8 +- pkg/slayers/path/hummingbird/decoded.go | 26 +-- pkg/slayers/path/hummingbird/decoded_test.go | 6 +- pkg/slayers/path/hummingbird/mac.go | 1 - pkg/slayers/path/hummingbird/raw.go | 38 ++-- pkg/slayers/path/hummingbird/raw_test.go | 10 +- pkg/slayers/path/scion/base.go | 1 - router/dataplane_hbird.go | 16 +- router/dataplane_hbird_test.go | 180 +++++++++---------- 11 files changed, 156 insertions(+), 156 deletions(-) diff --git a/pkg/hummingbird/hummingbird.go b/pkg/hummingbird/hummingbird.go index 46e2b08c54..fd59c7e98d 100644 --- a/pkg/hummingbird/hummingbird.go +++ b/pkg/hummingbird/hummingbird.go @@ -263,7 +263,7 @@ func (c *HummingbirdClient) RequestReservationForASes(asin []addr.IA, bw uint16, } // set flyover c.dec.HopFields[i].Flyover = true - c.dec.NumHops += 2 + c.dec.NumLines += 2 c.dec.PathMeta.SegLen[infIdx] += 2 // set other fields c.dec.HopFields[i].Bw = c.reservations[i].Bw diff --git a/pkg/slayers/path/hummingbird/base.go b/pkg/slayers/path/hummingbird/base.go index 697f5c17b0..d84021179b 100644 --- a/pkg/slayers/path/hummingbird/base.go +++ b/pkg/slayers/path/hummingbird/base.go @@ -27,12 +27,12 @@ type Base struct { PathMeta MetaHdr // NumINF is the number of InfoFields in the path. NumINF int - // NumHops is the number HopFields in the path. - // If IsHummingbird is true, NumHops is the number of 4 bytes lines in the path instead of the number of hops. - // TODO: rename and reevaluate? - NumHops int + // NumLines is the number of 4 bytes lines in the path. + NumLines int } +// DecodeFromBytes populates the fields from a raw buffer. The buffer must be of length >= +// hummingbird.MetaLen. func (s *Base) DecodeFromBytes(data []byte) error { // PathMeta takes care of bounds check. err := s.PathMeta.DecodeFromBytes(data) @@ -40,7 +40,7 @@ func (s *Base) DecodeFromBytes(data []byte) error { return err } s.NumINF = 0 - s.NumHops = 0 + s.NumLines = 0 for i := 2; i >= 0; i-- { if s.PathMeta.SegLen[i] == 0 && s.NumINF > 0 { return serrors.New( @@ -49,17 +49,18 @@ func (s *Base) DecodeFromBytes(data []byte) error { if s.PathMeta.SegLen[i] > 0 && s.NumINF == 0 { s.NumINF = i + 1 } - s.NumHops += int(s.PathMeta.SegLen[i]) + s.NumLines += int(s.PathMeta.SegLen[i]) } return nil } +// IncPath increases the currHF index by n and the currINF index if appropriate. func (s *Base) IncPath(n int) error { if s.NumINF == 0 { return serrors.New("empty path cannot be increased") } - if int(s.PathMeta.CurrHF) >= s.NumHops-n { - s.PathMeta.CurrHF = uint8(s.NumHops - n) + if int(s.PathMeta.CurrHF) >= s.NumLines-n { + s.PathMeta.CurrHF = uint8(s.NumLines - n) return serrors.New("Incrementing path over end") } s.PathMeta.CurrHF += uint8(n) @@ -69,8 +70,8 @@ func (s *Base) IncPath(n int) error { // IsXover returns whether we are at a crossover point. func (s *Base) IsXover() bool { - return s.PathMeta.CurrHF+5 < uint8(s.NumHops) && - (s.PathMeta.CurrINF != s.InfIndexForHF(s.PathMeta.CurrHF+3) || s.PathMeta.CurrINF != s.InfIndexForHF(s.PathMeta.CurrHF+5)) + return s.PathMeta.CurrHF+FlyoverLines < uint8(s.NumLines) && + (s.PathMeta.CurrINF != s.InfIndexForHF(s.PathMeta.CurrHF+HopLines) || s.PathMeta.CurrINF != s.InfIndexForHF(s.PathMeta.CurrHF+FlyoverLines)) } @@ -80,6 +81,7 @@ func (s *Base) IsFirstHopAfterXover() bool { s.PathMeta.CurrINF-1 == s.InfIndexForHF(s.PathMeta.CurrHF-1) } +// InfIndexForHF returns the segment to which the HopField hf belongs func (s *Base) InfIndexForHF(hf uint8) uint8 { switch { case hf < s.PathMeta.SegLen[0]: @@ -93,7 +95,7 @@ func (s *Base) InfIndexForHF(hf uint8) uint8 { // Len returns the length of the path in bytes. func (s *Base) Len() int { - return MetaLen + s.NumINF*path.InfoLen + s.NumHops*LineLen + return MetaLen + s.NumINF*path.InfoLen + s.NumLines*LineLen } // Type returns the type of the path. diff --git a/pkg/slayers/path/hummingbird/base_test.go b/pkg/slayers/path/hummingbird/base_test.go index 7799fc35f4..d54a62ea3f 100644 --- a/pkg/slayers/path/hummingbird/base_test.go +++ b/pkg/slayers/path/hummingbird/base_test.go @@ -60,8 +60,8 @@ func TestIncPath(t *testing.T) { CurrHF: uint8(tc.inIdxs[i][1]), SegLen: tc.segLens, }, - NumINF: tc.nsegs, - NumHops: tc.nhops, + NumINF: tc.nsegs, + NumLines: tc.nhops, } err := s.IncPath(3) if tc.wantIdxs[i][0] == 0 && tc.wantIdxs[i][1] == 0 { @@ -126,8 +126,8 @@ func TestBaseIsXOver(t *testing.T) { CurrHF: uint8(tc.inIdxs[i][1]), SegLen: tc.segLens, }, - NumINF: tc.nsegs, - NumHops: tc.nhops, + NumINF: tc.nsegs, + NumLines: tc.nhops, } t.Run(fmt.Sprintf("%s case %d", name, i+1), func(t *testing.T) { t.Parallel() diff --git a/pkg/slayers/path/hummingbird/decoded.go b/pkg/slayers/path/hummingbird/decoded.go index d349de764f..d55510ca50 100644 --- a/pkg/slayers/path/hummingbird/decoded.go +++ b/pkg/slayers/path/hummingbird/decoded.go @@ -47,10 +47,10 @@ func (s *Decoded) DecodeFromBytes(data []byte) error { } // Allocate maximum number of possible hopfields based on length - s.HopFields = make([]FlyoverHopField, s.NumHops/3) + s.HopFields = make([]FlyoverHopField, s.NumLines/3) i, j := 0, 0 // If last hop is not a flyover hop, decode it with only 12 bytes slice - for ; j < s.NumHops-3; i++ { + for ; j < s.NumLines-HopLines; i++ { if err := s.HopFields[i].DecodeFromBytes(data[offset : offset+FlyoverLen]); err != nil { return err } @@ -63,13 +63,13 @@ func (s *Decoded) DecodeFromBytes(data []byte) error { if s.HopFields[i].Flyover { offset += FlyoverLen - j += 5 + j += FlyoverLines } else { offset += HopLen - j += 3 + j += HopLines } } - if j == s.NumHops-3 { + if j == s.NumLines-HopLines { if err := s.HopFields[i].DecodeFromBytes(data[offset : offset+HopLen]); err != nil { return err } @@ -149,7 +149,7 @@ func (s *Decoded) Reverse() (path.Path, error) { } // Update CurrINF and CurrHF and SegLens s.PathMeta.CurrINF = uint8(s.NumINF) - s.PathMeta.CurrINF - 1 - s.PathMeta.CurrHF = uint8(s.NumHops) - s.PathMeta.CurrHF - 3 + s.PathMeta.CurrHF = uint8(s.NumLines) - s.PathMeta.CurrHF - 3 return s, nil } @@ -170,7 +170,7 @@ func (s *Decoded) RemoveFlyovers() error { if s.PathMeta.CurrHF > offset { s.PathMeta.CurrHF -= 2 } - s.Base.NumHops -= 2 + s.Base.NumLines -= 2 s.PathMeta.SegLen[idxInf] -= 2 } segCount += 3 @@ -180,7 +180,7 @@ func (s *Decoded) RemoveFlyovers() error { } else if s.PathMeta.SegLen[idxInf] < segCount { return serrors.New("new hopfields boundaries do not match new segment lengths after flyover removal") } - offset += 3 + offset += HopLines } return nil } @@ -222,10 +222,10 @@ func (s *Decoded) convertBaseFromScion(d scion.Base) { s.Base.NumINF = d.NumINF s.Base.PathMeta.CurrINF = d.PathMeta.CurrINF - s.Base.NumHops = d.NumHops * 3 - s.Base.PathMeta.CurrHF = d.PathMeta.CurrHF * 3 + s.Base.NumLines = d.NumHops * HopLines + s.Base.PathMeta.CurrHF = d.PathMeta.CurrHF * HopLines - s.Base.PathMeta.SegLen[0] = d.PathMeta.SegLen[0] * 3 - s.Base.PathMeta.SegLen[1] = d.PathMeta.SegLen[1] * 3 - s.Base.PathMeta.SegLen[2] = d.PathMeta.SegLen[2] * 3 + s.Base.PathMeta.SegLen[0] = d.PathMeta.SegLen[0] * HopLines + s.Base.PathMeta.SegLen[1] = d.PathMeta.SegLen[1] * HopLines + s.Base.PathMeta.SegLen[2] = d.PathMeta.SegLen[2] * HopLines } diff --git a/pkg/slayers/path/hummingbird/decoded_test.go b/pkg/slayers/path/hummingbird/decoded_test.go index 6e54c7ee78..1431e2a3cd 100644 --- a/pkg/slayers/path/hummingbird/decoded_test.go +++ b/pkg/slayers/path/hummingbird/decoded_test.go @@ -78,8 +78,8 @@ var decodedHbirdTestPath = &hummingbird.Decoded{ BaseTS: 808, HighResTS: 1234, }, - NumINF: 2, - NumHops: 16, + NumINF: 2, + NumLines: 16, }, InfoFields: testInfoFields, HopFields: testFlyoverFields, @@ -235,7 +235,7 @@ func mkDecodedHbirdPath(t *testing.T, pcase hbirdPathCase, infIdx, hopIdx uint8) } s.PathMeta = meta s.NumINF = len(pcase.infos) - s.NumHops = i + s.NumLines = i return s } diff --git a/pkg/slayers/path/hummingbird/mac.go b/pkg/slayers/path/hummingbird/mac.go index 1fe0c2093a..a8dec58fec 100644 --- a/pkg/slayers/path/hummingbird/mac.go +++ b/pkg/slayers/path/hummingbird/mac.go @@ -49,7 +49,6 @@ func DeriveAuthKey(block cipher.Block, resId uint32, bw, in, eg uint16, startTim // Computes full flyover mac vk // Needs a xkbuffer of 44 uint32s to store the expanded keys for aes -// dummy buffer is memory used by key expansion to store decryption keys func FullFlyoverMac(ak []byte, dstIA addr.IA, pktlen uint16, resStartTime uint16, highResTime uint32, buffer []byte, xkbuffer []uint32) []byte { if len(buffer) < FlyoverMacBufferSize { buffer = make([]byte, FlyoverMacBufferSize) diff --git a/pkg/slayers/path/hummingbird/raw.go b/pkg/slayers/path/hummingbird/raw.go index 80f017f8db..15d715eb3c 100644 --- a/pkg/slayers/path/hummingbird/raw.go +++ b/pkg/slayers/path/hummingbird/raw.go @@ -83,7 +83,7 @@ func (s *Raw) ToDecoded() (*Decoded, error) { return decoded, nil } -// IncPath increments the path and writes it to the buffer. +// IncPath increments the path by n and writes it to the buffer. func (s *Raw) IncPath(n int) error { if err := s.Base.IncPath(n); err != nil { return err @@ -131,19 +131,19 @@ func (s *Raw) SetInfoField(info path.InfoField, idx int) error { // GetHopField returns the HopField at a given index. func (s *Raw) GetHopField(idx int) (FlyoverHopField, error) { - if idx >= s.NumHops-2 { + if idx >= s.NumLines-HopLines+1 { return FlyoverHopField{}, - serrors.New("HopField index out of bounds", "max", s.NumHops-3, "actual", idx) + serrors.New("HopField index out of bounds", "max", s.NumLines-HopLines, "actual", idx) } hopOffset := MetaLen + s.NumINF*path.InfoLen + idx*LineLen hop := FlyoverHopField{} // Let the decoder read a big enough slice in case it is a FlyoverHopField maxHopLen := FlyoverLen - if idx > s.NumHops-5 { - if idx == s.NumHops-3 { + if idx > s.NumLines-FlyoverLines { + if idx == s.NumLines-HopLines { maxHopLen = HopLen } else { - return FlyoverHopField{}, serrors.New("Invalid hopfield index", "NumHops", s.NumHops, "index", idx) + return FlyoverHopField{}, serrors.New("Invalid hopfield index", "NumHops", s.NumLines, "index", idx) } } if err := hop.DecodeFromBytes(s.Raw[hopOffset : hopOffset+maxHopLen]); err != nil { @@ -159,8 +159,8 @@ func (s *Raw) GetCurrentHopField() (FlyoverHopField, error) { } func (s *Raw) ReplacMac(idx int, mac []byte) error { - if idx >= s.NumHops-2 { - return serrors.New("HopField index out of bounds", "max", s.NumHops-3, "actual", idx) + if idx >= s.NumLines-HopLines+1 { + return serrors.New("HopField index out of bounds", "max", s.NumLines-HopLines, "actual", idx) } offset := s.NumINF*path.InfoLen + MetaLen + idx*LineLen + MacOffset if n := copy(s.Raw[offset:offset+path.MacLen], mac[:path.MacLen]); n != path.MacLen { @@ -176,8 +176,8 @@ func (s *Raw) ReplaceCurrentMac(mac []byte) error { // Returns a slice of the MAC of the hopfield starting at index idx func (s *Raw) GetMac(idx int) ([]byte, error) { - if idx >= s.NumHops-2 { - return nil, serrors.New("HopField index out of bounds", "max", s.NumHops-3, "actual", idx) + if idx >= s.NumLines-HopLines+1 { + return nil, serrors.New("HopField index out of bounds", "max", s.NumLines-HopLines, "actual", idx) } offset := s.NumINF*path.InfoLen + MetaLen + idx*LineLen + MacOffset return s.Raw[offset : offset+path.MacLen], nil @@ -189,8 +189,8 @@ func (s *Raw) GetMac(idx int) ([]byte, error) { // If replacing a FlyoverHopField with a Hopfield, it is replaced by a FlyoverHopField with dummy values. // This works for SCMP packets as Flyover hops are removed later in the process of building a SCMP packet. func (s *Raw) SetHopField(hop FlyoverHopField, idx int) error { - if idx >= s.NumHops-2 { - return serrors.New("HopField index out of bounds", "max", s.NumHops-3, "actual", idx) + if idx >= s.NumLines-HopLines+1 { + return serrors.New("HopField index out of bounds", "max", s.NumLines-HopLines, "actual", idx) } hopOffset := MetaLen + s.NumINF*path.InfoLen + idx*LineLen if s.Raw[hopOffset]&0x80 == 0x80 { @@ -202,8 +202,8 @@ func (s *Raw) SetHopField(hop FlyoverHopField, idx int) error { hop.Flyover = true } if hop.Flyover { - if idx >= s.NumHops-4 { - return serrors.New("FlyoverHopField index out of bounds", "max", s.NumHops-5, "actual", idx) + if idx >= s.NumLines-FlyoverLines+1 { + return serrors.New("FlyoverHopField index out of bounds", "max", s.NumLines-FlyoverLines, "actual", idx) } hopOffset := MetaLen + s.NumINF*path.InfoLen + idx*LineLen if s.Raw[hopOffset]&0x80 == 0x00 { @@ -221,7 +221,7 @@ func (s *Raw) IsFirstHop() bool { // IsLastHop returns whether the current hop is the last hop on the path. func (s *Raw) IsLastHop() bool { - return int(s.PathMeta.CurrHF) == (s.NumHops-3) || int(s.PathMeta.CurrHF) == (s.NumHops-5) + return int(s.PathMeta.CurrHF) == (s.NumLines-HopLines) || int(s.PathMeta.CurrHF) == (s.NumLines-FlyoverLines) } // Returns the egress interface of the next hop @@ -235,8 +235,8 @@ func (s *Raw) GetNextEgress() (uint16, error) { idx += HopLines hopOffset += HopLines * LineLen } - if idx >= s.NumHops-2 { - return 0, serrors.New("HopField index out of bounds", "max", s.NumHops-3, "actual", idx) + if idx >= s.NumLines-2 { + return 0, serrors.New("HopField index out of bounds", "max", s.NumLines-HopLines, "actual", idx) } if s.isConsdir(uint8(idx)) { return binary.BigEndian.Uint16(s.Raw[hopOffset+4 : hopOffset+6]), nil @@ -262,8 +262,8 @@ func (s *Raw) GetPreviousIngress() (uint16, error) { // DOES NOT adapt MACS. func (s *Raw) DoFlyoverXover() error { idx := int(s.Base.PathMeta.CurrHF) - if idx >= s.NumHops-7 { - return serrors.New("CurrHF out of bounds for flyover crossover", "max", s.NumHops-7, "actual", idx) + if idx >= s.NumLines-7 { + return serrors.New("CurrHF out of bounds for flyover crossover", "max", s.NumLines-7, "actual", idx) } if s.PathMeta.CurrINF == 2 { return serrors.New("Cannot do FlyoverXover if CurrINF = 2") diff --git a/pkg/slayers/path/hummingbird/raw_test.go b/pkg/slayers/path/hummingbird/raw_test.go index a63208b1d4..b66c3ff7b4 100644 --- a/pkg/slayers/path/hummingbird/raw_test.go +++ b/pkg/slayers/path/hummingbird/raw_test.go @@ -17,8 +17,8 @@ var emptyRawTestPath = &hummingbird.Raw{ CurrHF: 0, SegLen: [3]uint8{0, 0, 0}, }, - NumINF: 0, - NumHops: 0, + NumINF: 0, + NumLines: 0, }, Raw: make([]byte, hummingbird.MetaLen), } @@ -32,8 +32,8 @@ var rawHbirdTestPath = &hummingbird.Raw{ BaseTS: 808, HighResTS: 1234, }, - NumINF: 2, - NumHops: 16, + NumINF: 2, + NumLines: 16, }, Raw: rawHbirdPath, } @@ -188,7 +188,7 @@ func createHbirdPath(currHF uint8, numHops int) *hummingbird.Raw { PathMeta: hummingbird.MetaHdr{ CurrHF: currHF, }, - NumHops: numHops, + NumLines: numHops, }, } return hbirdRaw diff --git a/pkg/slayers/path/scion/base.go b/pkg/slayers/path/scion/base.go index 4dbc574565..9ca2071d66 100644 --- a/pkg/slayers/path/scion/base.go +++ b/pkg/slayers/path/scion/base.go @@ -24,7 +24,6 @@ import ( // MetaLen is the length of the PathMetaHeader. const MetaLen = 4 -const MetaLenHBird = 12 const PathType path.Type = 1 diff --git a/router/dataplane_hbird.go b/router/dataplane_hbird.go index 6b2d045888..fbe79c7d00 100644 --- a/router/dataplane_hbird.go +++ b/router/dataplane_hbird.go @@ -84,7 +84,7 @@ func determinePeerHbird(pathMeta hummingbird.MetaHdr, inf path.InfoField) (bool, // because we already know this is a well-formed peering path. currHF := pathMeta.CurrHF segLen := pathMeta.SegLen[0] - return currHF == segLen-3 || currHF == segLen-5 || currHF == segLen, nil + return currHF == segLen-hummingbird.HopLines || currHF == segLen-hummingbird.FlyoverLines || currHF == segLen, nil } func (p *scionPacketProcessor) determinePeerHbird() (processResult, error) { @@ -249,7 +249,7 @@ func (p *scionPacketProcessor) ingressInterfaceHbird() uint16 { panic(err) } // Previous hop should always be a non-flyover field, as flyover is transferred to second hop on xover - hop, err = p.hbirdPath.GetHopField(int(p.hbirdPath.PathMeta.CurrHF) - 3) + hop, err = p.hbirdPath.GetHopField(int(p.hbirdPath.PathMeta.CurrHF) - hummingbird.HopLines) if err != nil { // cannot be out of range panic(err) } @@ -387,7 +387,7 @@ func (p *scionPacketProcessor) doFlyoverXover() error { copy(p.hopField.Mac[:], p.cachedMac[0:6]) p.hbirdPath.ReplaceCurrentMac(p.cachedMac) // Aggregate Mac of second hop - mac, err := p.hbirdPath.GetMac(int(p.hbirdPath.PathMeta.CurrHF) + 3) + mac, err := p.hbirdPath.GetMac(int(p.hbirdPath.PathMeta.CurrHF) + hummingbird.HopLines) if err != nil { return err } @@ -414,7 +414,7 @@ func (p *scionPacketProcessor) doHbirdXover() (processResult, error) { return processResult{}, err } } - if err := p.hbirdPath.IncPath(3); err != nil { + if err := p.hbirdPath.IncPath(hummingbird.HopLines); err != nil { // TODO parameter problem invalid path return processResult{}, serrors.WrapStr("incrementing path", err) } @@ -444,9 +444,9 @@ func (p *scionPacketProcessor) processHbirdEgress() error { } } - n := 3 + n := hummingbird.HopLines if p.flyoverField.Flyover { - n = 5 + n = hummingbird.FlyoverLines } if err := p.hbirdPath.IncPath(n); err != nil { // TODO parameter problem invalid path @@ -623,7 +623,7 @@ func (p *slowPathPacketProcessor) prepareHbirdSCMP( if revPath.IsXover() && !peering { // An effective cross-over is a change of segment other than at // a peering hop. - if err := revPath.IncPath(3); err != nil { + if err := revPath.IncPath(hummingbird.HopLines); err != nil { return nil, serrors.Wrap(cannotRoute, err, "details", "reverting cross over for SCMP") } } @@ -637,7 +637,7 @@ func (p *slowPathPacketProcessor) prepareHbirdSCMP( hopField := revPath.HopFields[revPath.PathMeta.CurrHF] infoField.UpdateSegID(hopField.HopField.Mac) } - if err := revPath.IncPath(3); err != nil { + if err := revPath.IncPath(hummingbird.HopLines); err != nil { return nil, serrors.Wrap(cannotRoute, err, "details", "incrementing path for SCMP") } } //TODO else, make sure MAC is deaggregated? diff --git a/router/dataplane_hbird_test.go b/router/dataplane_hbird_test.go index e096f8afa2..80bf8c7d4f 100644 --- a/router/dataplane_hbird_test.go +++ b/router/dataplane_hbird_test.go @@ -111,7 +111,7 @@ func TestProcessHbirdPacket(t *testing.T) { if !afterProcessing { return toMsg(t, spkt, dpath) } - _ = dpath.IncPath(3) + _ = dpath.IncPath(hummingbird.HopLines) dpath.InfoFields[0].UpdateSegID(dpath.HopFields[0].HopField.Mac) ret := toMsg(t, spkt, dpath) ret.Addr = nil @@ -146,7 +146,7 @@ func TestProcessHbirdPacket(t *testing.T) { if !afterProcessing { return toMsg(t, spkt, dpath) } - _ = dpath.IncPath(3) + _ = dpath.IncPath(hummingbird.HopLines) dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.Mac) ret := toMsg(t, spkt, dpath) ret.Addr = nil @@ -182,7 +182,7 @@ func TestProcessHbirdPacket(t *testing.T) { dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.Mac) return toMsg(t, spkt, dpath) } - require.NoError(t, dpath.IncPath(3)) + require.NoError(t, dpath.IncPath(hummingbird.HopLines)) ret := toMsg(t, spkt, dpath) ret.Addr = nil ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil @@ -243,8 +243,8 @@ func TestProcessHbirdPacket(t *testing.T) { CurrHF: 6, SegLen: [3]uint8{6, 6, 0}, }, - NumINF: 2, - NumHops: 12, + NumINF: 2, + NumLines: 12, }, InfoFields: []path.InfoField{ // up seg @@ -266,7 +266,7 @@ func TestProcessHbirdPacket(t *testing.T) { dpath.InfoFields[0].UpdateSegID(dpath.HopFields[2].HopField.Mac) return toMsg(t, spkt, dpath) } - require.NoError(t, dpath.IncPath(3)) + require.NoError(t, dpath.IncPath(hummingbird.HopLines)) ret := toMsg(t, spkt, dpath) ret.Addr = &net.UDPAddr{IP: net.ParseIP("10.0.200.200").To4(), Port: 30043} ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil @@ -324,8 +324,8 @@ func TestProcessHbirdPacket(t *testing.T) { CurrINF: 1, SegLen: [3]uint8{3, 6, 0}, }, - NumINF: 2, - NumHops: 9, + NumINF: 2, + NumLines: 9, }, InfoFields: []path.InfoField{ // up seg @@ -355,7 +355,7 @@ func TestProcessHbirdPacket(t *testing.T) { if !afterProcessing { return toMsg(t, spkt, dpath) } - _ = dpath.IncPath(3) + _ = dpath.IncPath(hummingbird.HopLines) // ... The SegID accumulator wasn't updated from HF[1], // it is still the same. That is the key behavior. @@ -393,8 +393,8 @@ func TestProcessHbirdPacket(t *testing.T) { CurrINF: 0, SegLen: [3]uint8{6, 3, 0}, }, - NumINF: 2, - NumHops: 9, + NumINF: 2, + NumLines: 9, }, InfoFields: []path.InfoField{ // up seg @@ -432,7 +432,7 @@ func TestProcessHbirdPacket(t *testing.T) { return toMsg(t, spkt, dpath) } - _ = dpath.IncPath(3) + _ = dpath.IncPath(hummingbird.HopLines) // The SegID should not get updated on arrival. If it is, then MAC validation // of HF1 will fail. Otherwise, this isn't visible because we changed segment. @@ -472,8 +472,8 @@ func TestProcessHbirdPacket(t *testing.T) { CurrINF: 1, SegLen: [3]uint8{3, 9, 0}, }, - NumINF: 2, - NumHops: 12, + NumINF: 2, + NumLines: 12, }, InfoFields: []path.InfoField{ // up seg @@ -509,7 +509,7 @@ func TestProcessHbirdPacket(t *testing.T) { // so, already set. return toMsg(t, spkt, dpath) } - _ = dpath.IncPath(3) + _ = dpath.IncPath(hummingbird.HopLines) // ... The SegID accumulator should have been updated. dpath.InfoFields[1].UpdateSegID(dpath.HopFields[2].HopField.Mac) @@ -547,8 +547,8 @@ func TestProcessHbirdPacket(t *testing.T) { CurrINF: 0, SegLen: [3]uint8{9, 3, 0}, }, - NumINF: 2, - NumHops: 12, + NumINF: 2, + NumLines: 12, }, InfoFields: []path.InfoField{ // up seg @@ -592,7 +592,7 @@ func TestProcessHbirdPacket(t *testing.T) { return toMsg(t, spkt, dpath) } - _ = dpath.IncPath(3) + _ = dpath.IncPath(hummingbird.HopLines) // After-processing, the SegID should have been updated // (on ingress) to be that of HF[1], which happens to be @@ -625,7 +625,7 @@ func TestProcessHbirdPacket(t *testing.T) { {Flyover: true, HopField: path.HopField{ConsIngress: 01, ConsEgress: 0}, ResStartTime: 123, Duration: 304}, } dpath.Base.PathMeta.SegLen[0] = 11 - dpath.Base.NumHops = 11 + dpath.Base.NumLines = 11 dpath.Base.PathMeta.CurrHF = 6 dpath.HopFields[2].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, dpath.InfoFields[0], dpath.HopFields[2], dpath.PathMeta) if afterProcessing { @@ -662,13 +662,13 @@ func TestProcessHbirdPacket(t *testing.T) { } dpath.Base.PathMeta.CurrHF = 0 dpath.Base.PathMeta.SegLen[0] = 15 - dpath.NumHops = 15 + dpath.NumLines = 15 dpath.HopFields[0].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, dpath.InfoFields[0], dpath.HopFields[0], dpath.Base.PathMeta) if !afterProcessing { return toMsg(t, spkt, dpath) } dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) - _ = dpath.IncPath(5) + _ = dpath.IncPath(hummingbird.FlyoverLines) dpath.InfoFields[0].UpdateSegID(dpath.HopFields[0].HopField.Mac) ret := toMsg(t, spkt, dpath) ret.Addr = nil @@ -695,7 +695,7 @@ func TestProcessHbirdPacket(t *testing.T) { {Flyover: true, HopField: path.HopField{ConsIngress: 01, ConsEgress: 0}, ResStartTime: 5, Duration: 2}, } dpath.Base.PathMeta.SegLen[0] = 11 - dpath.Base.NumHops = 11 + dpath.Base.NumLines = 11 dpath.Base.PathMeta.CurrHF = 6 dpath.HopFields[2].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, dpath.InfoFields[0], dpath.HopFields[2], dpath.PathMeta) ret := toMsg(t, spkt, dpath) @@ -729,13 +729,13 @@ func TestProcessHbirdPacket(t *testing.T) { } dpath.Base.PathMeta.SegLen[0] = 11 dpath.Base.PathMeta.CurrHF = 3 - dpath.Base.NumHops = 11 + dpath.Base.NumLines = 11 dpath.HopFields[1].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, dpath.InfoFields[0], dpath.HopFields[1], dpath.Base.PathMeta) if !afterProcessing { return toMsg(t, spkt, dpath) } dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) - _ = dpath.IncPath(5) + _ = dpath.IncPath(hummingbird.FlyoverLines) dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.Mac) ret := toMsg(t, spkt, dpath) ret.Addr = nil @@ -765,7 +765,7 @@ func TestProcessHbirdPacket(t *testing.T) { {HopField: path.HopField{ConsIngress: 40, ConsEgress: 41}}, } dpath.Base.PathMeta.CurrHF = 3 - dpath.Base.NumHops = 11 + dpath.Base.NumLines = 11 dpath.Base.PathMeta.SegLen[0] = 11 dpath.InfoFields[0].ConsDir = false @@ -776,7 +776,7 @@ func TestProcessHbirdPacket(t *testing.T) { return toMsg(t, spkt, dpath) } dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) - require.NoError(t, dpath.IncPath(5)) + require.NoError(t, dpath.IncPath(hummingbird.FlyoverLines)) ret := toMsg(t, spkt, dpath) ret.Addr = nil ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil @@ -806,7 +806,7 @@ func TestProcessHbirdPacket(t *testing.T) { {HopField: path.HopField{ConsIngress: 50, ConsEgress: 51}}, } dpath.Base.PathMeta.SegLen[0] = 11 - dpath.Base.NumHops = 11 + dpath.Base.NumLines = 11 dpath.HopFields[1].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, dpath.InfoFields[0], dpath.HopFields[1], dpath.Base.PathMeta) if afterProcessing { // dpath.HopFields[1].Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1]) @@ -843,8 +843,8 @@ func TestProcessHbirdPacket(t *testing.T) { SegLen: [3]uint8{6, 8, 0}, BaseTS: util.TimeToSecs(now), }, - NumINF: 2, - NumHops: 16, + NumINF: 2, + NumLines: 16, }, InfoFields: []path.InfoField{ // up seg @@ -879,7 +879,7 @@ func TestProcessHbirdPacket(t *testing.T) { //dpath.HopFields[3].Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3]) dpath.PathMeta.SegLen[0] -= 2 dpath.PathMeta.SegLen[1] += 2 - require.NoError(t, dpath.IncPath(5)) + require.NoError(t, dpath.IncPath(hummingbird.FlyoverLines)) ret := toMsg(t, spkt, dpath) ret.Addr = &net.UDPAddr{IP: net.ParseIP("10.0.200.200").To4(), Port: 30043} ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil @@ -910,8 +910,8 @@ func TestProcessHbirdPacket(t *testing.T) { SegLen: [3]uint8{8, 6, 0}, BaseTS: util.TimeToSecs(now), }, - NumINF: 2, - NumHops: 14, + NumINF: 2, + NumLines: 14, }, InfoFields: []path.InfoField{ // up seg @@ -950,7 +950,7 @@ func TestProcessHbirdPacket(t *testing.T) { //dpath.HopFields[3].Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3]) dpath.PathMeta.SegLen[0] -= 2 dpath.PathMeta.SegLen[1] += 2 - require.NoError(t, dpath.IncPath(3)) + require.NoError(t, dpath.IncPath(hummingbird.HopLines)) ret := toMsg(t, spkt, dpath) ret.Addr = &net.UDPAddr{IP: net.ParseIP("10.0.200.200").To4(), Port: 30043} ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil @@ -986,8 +986,8 @@ func TestProcessHbirdPacket(t *testing.T) { SegLen: [3]uint8{6, 8, 0}, BaseTS: util.TimeToSecs(now), }, - NumINF: 2, - NumHops: 14, + NumINF: 2, + NumLines: 14, }, InfoFields: []path.InfoField{ // up seg @@ -1021,7 +1021,7 @@ func TestProcessHbirdPacket(t *testing.T) { dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[2].HopField) dpath.InfoFields[1].UpdateSegID(dpath.HopFields[2].HopField.Mac) - require.NoError(t, dpath.IncPath(5)) + require.NoError(t, dpath.IncPath(hummingbird.FlyoverLines)) dpath.PathMeta.SegLen[0] += 2 dpath.PathMeta.SegLen[1] -= 2 ret := toMsg(t, spkt, dpath) @@ -1060,8 +1060,8 @@ func TestProcessHbirdPacket(t *testing.T) { SegLen: [3]uint8{3, 8, 0}, BaseTS: util.TimeToSecs(now), }, - NumINF: 2, - NumHops: 11, + NumINF: 2, + NumLines: 11, }, InfoFields: []path.InfoField{ // up seg @@ -1091,7 +1091,7 @@ func TestProcessHbirdPacket(t *testing.T) { if !afterProcessing { return toMsg(t, spkt, dpath) } - _ = dpath.IncPath(5) + _ = dpath.IncPath(hummingbird.FlyoverLines) // deaggregate MAC dpath.HopFields[1].HopField.Mac = computeMAC( t, key, dpath.InfoFields[1], dpath.HopFields[1].HopField) @@ -1132,8 +1132,8 @@ func TestProcessHbirdPacket(t *testing.T) { SegLen: [3]uint8{8, 3, 0}, BaseTS: util.TimeToSecs(now), }, - NumINF: 2, - NumHops: 11, + NumINF: 2, + NumLines: 11, }, InfoFields: []path.InfoField{ // up seg @@ -1171,7 +1171,7 @@ func TestProcessHbirdPacket(t *testing.T) { return toMsg(t, spkt, dpath) } - _ = dpath.IncPath(5) + _ = dpath.IncPath(hummingbird.FlyoverLines) // deaggregate MAc dpath.HopFields[1].HopField.Mac = computeMAC( t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) @@ -1215,8 +1215,8 @@ func TestProcessHbirdPacket(t *testing.T) { SegLen: [3]uint8{3, 11, 0}, BaseTS: util.TimeToSecs(now), }, - NumINF: 2, - NumHops: 14, + NumINF: 2, + NumLines: 14, }, InfoFields: []path.InfoField{ // up seg @@ -1252,7 +1252,7 @@ func TestProcessHbirdPacket(t *testing.T) { // so, already set. return toMsg(t, spkt, dpath) } - _ = dpath.IncPath(5) + _ = dpath.IncPath(hummingbird.FlyoverLines) // mac should be deaggregated, and used for updateSegID dpath.HopFields[2].HopField.Mac = computeMAC( t, key, dpath.InfoFields[1], dpath.HopFields[2].HopField) @@ -1294,8 +1294,8 @@ func TestProcessHbirdPacket(t *testing.T) { SegLen: [3]uint8{11, 3, 0}, BaseTS: util.TimeToSecs(now), }, - NumINF: 2, - NumHops: 14, + NumINF: 2, + NumLines: 14, }, InfoFields: []path.InfoField{ // up seg @@ -1342,7 +1342,7 @@ func TestProcessHbirdPacket(t *testing.T) { return toMsg(t, spkt, dpath) } - _ = dpath.IncPath(5) + _ = dpath.IncPath(hummingbird.FlyoverLines) // deaggregate MAC) dpath.HopFields[1].HopField.Mac = computeMAC( t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) @@ -1419,8 +1419,8 @@ func TestHbirdPacketPath(t *testing.T) { BaseTS: util.TimeToSecs(now), HighResTS: 500 << 22, }, - NumINF: 1, - NumHops: 6, + NumINF: 1, + NumLines: 6, }, InfoFields: []path.InfoField{ {SegID: 0x111, ConsDir: true, Timestamp: util.TimeToSecs(now)}, @@ -1482,8 +1482,8 @@ func TestHbirdPacketPath(t *testing.T) { BaseTS: util.TimeToSecs(now), HighResTS: 500 << 22, }, - NumINF: 1, - NumHops: 6, + NumINF: 1, + NumLines: 6, }, InfoFields: []path.InfoField{ {SegID: 0x111, ConsDir: false, Timestamp: util.TimeToSecs(now)}, @@ -1545,8 +1545,8 @@ func TestHbirdPacketPath(t *testing.T) { BaseTS: util.TimeToSecs(now), HighResTS: 500 << 22, }, - NumINF: 2, - NumHops: 18, + NumINF: 2, + NumLines: 18, }, InfoFields: []path.InfoField{ {SegID: 0x111, ConsDir: true, Timestamp: util.TimeToSecs(now)}, @@ -1685,8 +1685,8 @@ func TestHbirdPacketPath(t *testing.T) { BaseTS: util.TimeToSecs(now), HighResTS: 500 << 22, }, - NumINF: 2, - NumHops: 18, + NumINF: 2, + NumLines: 18, }, InfoFields: []path.InfoField{ {SegID: 0x111, ConsDir: false, Timestamp: util.TimeToSecs(now)}, @@ -1824,8 +1824,8 @@ func TestHbirdPacketPath(t *testing.T) { BaseTS: util.TimeToSecs(now), HighResTS: 500 << 22, }, - NumINF: 2, - NumHops: 18, + NumINF: 2, + NumLines: 18, }, InfoFields: []path.InfoField{ {SegID: 0x111, ConsDir: false, Timestamp: util.TimeToSecs(now)}, @@ -1937,8 +1937,8 @@ func TestHbirdPacketPath(t *testing.T) { BaseTS: util.TimeToSecs(now), HighResTS: 500 << 22, }, - NumINF: 3, - NumHops: 18, + NumINF: 3, + NumLines: 18, }, InfoFields: []path.InfoField{ {SegID: 0x111, ConsDir: true, Timestamp: util.TimeToSecs(now)}, @@ -2047,8 +2047,8 @@ func TestHbirdPacketPath(t *testing.T) { BaseTS: util.TimeToSecs(now), HighResTS: 500 << 22, }, - NumINF: 2, - NumHops: 9, + NumINF: 2, + NumLines: 9, }, InfoFields: []path.InfoField{ {SegID: 0x111, Peer: true, ConsDir: true, Timestamp: util.TimeToSecs(now)}, @@ -2121,8 +2121,8 @@ func TestHbirdPacketPath(t *testing.T) { BaseTS: util.TimeToSecs(now), HighResTS: 500 << 22, }, - NumINF: 2, - NumHops: 9, + NumINF: 2, + NumLines: 9, }, InfoFields: []path.InfoField{ {SegID: 0x111, Peer: true, ConsDir: false, Timestamp: util.TimeToSecs(now)}, @@ -2196,8 +2196,8 @@ func TestHbirdPacketPath(t *testing.T) { BaseTS: util.TimeToSecs(now), HighResTS: 500 << 22, }, - NumINF: 2, - NumHops: 12, + NumINF: 2, + NumLines: 12, }, InfoFields: []path.InfoField{ {SegID: 0x111, Peer: true, ConsDir: true, Timestamp: util.TimeToSecs(now)}, @@ -2312,8 +2312,8 @@ func TestHbirdPacketPath(t *testing.T) { BaseTS: util.TimeToSecs(now), HighResTS: 500 << 22, }, - NumINF: 2, - NumHops: 12, + NumINF: 2, + NumLines: 12, }, InfoFields: []path.InfoField{ {SegID: 0x111, Peer: true, ConsDir: false, Timestamp: util.TimeToSecs(now)}, @@ -2426,8 +2426,8 @@ func TestHbirdPacketPath(t *testing.T) { BaseTS: util.TimeToSecs(now), HighResTS: 500 << 22, }, - NumINF: 1, - NumHops: 10, + NumINF: 1, + NumLines: 10, }, InfoFields: []path.InfoField{ {SegID: 0x111, ConsDir: true, Timestamp: util.TimeToSecs(now)}, @@ -2494,8 +2494,8 @@ func TestHbirdPacketPath(t *testing.T) { BaseTS: util.TimeToSecs(now), HighResTS: 500 << 22, }, - NumINF: 1, - NumHops: 10, + NumINF: 1, + NumLines: 10, }, InfoFields: []path.InfoField{ {SegID: 0x111, ConsDir: false, Timestamp: util.TimeToSecs(now)}, @@ -2561,8 +2561,8 @@ func TestHbirdPacketPath(t *testing.T) { BaseTS: util.TimeToSecs(now), HighResTS: 500 << 22, }, - NumINF: 2, - NumHops: 28, + NumINF: 2, + NumLines: 28, }, InfoFields: []path.InfoField{ {SegID: 0x111, ConsDir: true, Timestamp: util.TimeToSecs(now)}, @@ -2712,8 +2712,8 @@ func TestHbirdPacketPath(t *testing.T) { BaseTS: util.TimeToSecs(now), HighResTS: 500 << 22, }, - NumINF: 2, - NumHops: 28, + NumINF: 2, + NumLines: 28, }, InfoFields: []path.InfoField{ {SegID: 0x111, ConsDir: false, Timestamp: util.TimeToSecs(now)}, @@ -2861,8 +2861,8 @@ func TestHbirdPacketPath(t *testing.T) { BaseTS: util.TimeToSecs(now), HighResTS: 500 << 22, }, - NumINF: 2, - NumHops: 28, + NumINF: 2, + NumLines: 28, }, InfoFields: []path.InfoField{ {SegID: 0x111, ConsDir: false, Timestamp: util.TimeToSecs(now)}, @@ -2987,8 +2987,8 @@ func TestHbirdPacketPath(t *testing.T) { BaseTS: util.TimeToSecs(now), HighResTS: 500 << 22, }, - NumINF: 3, - NumHops: 26, + NumINF: 3, + NumLines: 26, }, InfoFields: []path.InfoField{ {SegID: 0x111, ConsDir: true, Timestamp: util.TimeToSecs(now)}, @@ -3106,8 +3106,8 @@ func TestHbirdPacketPath(t *testing.T) { BaseTS: util.TimeToSecs(now), HighResTS: 500 << 22, }, - NumINF: 2, - NumHops: 15, + NumINF: 2, + NumLines: 15, }, InfoFields: []path.InfoField{ {SegID: 0x111, Peer: true, ConsDir: true, Timestamp: util.TimeToSecs(now)}, @@ -3184,8 +3184,8 @@ func TestHbirdPacketPath(t *testing.T) { BaseTS: util.TimeToSecs(now), HighResTS: 500 << 22, }, - NumINF: 2, - NumHops: 15, + NumINF: 2, + NumLines: 15, }, InfoFields: []path.InfoField{ {SegID: 0x111, Peer: true, ConsDir: false, Timestamp: util.TimeToSecs(now)}, @@ -3263,8 +3263,8 @@ func TestHbirdPacketPath(t *testing.T) { BaseTS: util.TimeToSecs(now), HighResTS: 500 << 22, }, - NumINF: 2, - NumHops: 20, + NumINF: 2, + NumLines: 20, }, InfoFields: []path.InfoField{ {SegID: 0x111, Peer: true, ConsDir: true, Timestamp: util.TimeToSecs(now)}, @@ -3384,8 +3384,8 @@ func TestHbirdPacketPath(t *testing.T) { BaseTS: util.TimeToSecs(now), HighResTS: 500 << 22, }, - NumINF: 2, - NumHops: 20, + NumINF: 2, + NumLines: 20, }, InfoFields: []path.InfoField{ {SegID: 0x111, Peer: true, ConsDir: false, Timestamp: util.TimeToSecs(now)}, @@ -3531,8 +3531,8 @@ func prepHbirdMsg(now time.Time) (*slayers.SCION, *hummingbird.Decoded) { BaseTS: util.TimeToSecs(now), HighResTS: 500 << 22, }, - NumINF: 1, - NumHops: 9, + NumINF: 1, + NumLines: 9, }, InfoFields: []path.InfoField{ {SegID: 0x111, ConsDir: true, Timestamp: util.TimeToSecs(now)}, From d62a04e5670450ea00499a57710ac041410aa3b7 Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Thu, 9 Nov 2023 15:14:38 +0100 Subject: [PATCH 026/100] fixed outdated data input order for auth key computation --- pkg/slayers/path/hummingbird/mac.go | 6 +++--- pkg/slayers/path/hummingbird/mac_test.go | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pkg/slayers/path/hummingbird/mac.go b/pkg/slayers/path/hummingbird/mac.go index a8dec58fec..2acbd1e5d4 100644 --- a/pkg/slayers/path/hummingbird/mac.go +++ b/pkg/slayers/path/hummingbird/mac.go @@ -35,9 +35,9 @@ func DeriveAuthKey(block cipher.Block, resId uint32, bw, in, eg uint16, startTim buffer = make([]byte, AkBufferSize) } //prepare input - binary.BigEndian.PutUint32(buffer[0:4], resId<<10|uint32(bw)) - binary.BigEndian.PutUint16(buffer[4:6], in) - binary.BigEndian.PutUint16(buffer[6:8], eg) + binary.BigEndian.PutUint16(buffer[0:2], in) + binary.BigEndian.PutUint16(buffer[2:4], eg) + binary.BigEndian.PutUint32(buffer[4:8], resId<<10|uint32(bw)) binary.BigEndian.PutUint32(buffer[8:12], startTime) binary.BigEndian.PutUint16(buffer[12:14], resDuration) binary.BigEndian.PutUint16(buffer[14:16], 0) //padding diff --git a/pkg/slayers/path/hummingbird/mac_test.go b/pkg/slayers/path/hummingbird/mac_test.go index dc925ba22a..fa66cbf08e 100644 --- a/pkg/slayers/path/hummingbird/mac_test.go +++ b/pkg/slayers/path/hummingbird/mac_test.go @@ -28,11 +28,11 @@ func TestDeriveAuthKey(t *testing.T) { // Compute expected result with library CBC expected := make([]byte, 16) - binary.BigEndian.PutUint32(expected[0:4], resId<<10) - expected[2] |= byte(bw >> 8) - expected[3] = byte(bw) - binary.BigEndian.PutUint16(expected[4:6], in) - binary.BigEndian.PutUint16(expected[6:8], eg) + binary.BigEndian.PutUint16(expected[0:2], in) + binary.BigEndian.PutUint16(expected[2:4], eg) + binary.BigEndian.PutUint32(expected[4:8], resId<<10) + expected[6] |= byte(bw >> 8) + expected[7] = byte(bw) binary.BigEndian.PutUint32(expected[8:12], start) binary.BigEndian.PutUint16(expected[12:14], duration) binary.BigEndian.PutUint16(expected[14:16], 0) @@ -65,7 +65,7 @@ func TestDeriveAuthKeyGoldenData(t *testing.T) { var start uint32 = 0x0030001 var duration uint16 = 0x0203 - expected := [16]byte{0x7e, 0x61, 0x4, 0x91, 0x30, 0x6b, 0x95, 0xec, 0xb5, 0x75, 0xc6, 0xe9, 0x4c, 0x5a, 0x89, 0x84} + expected := [16]byte{0x25, 0xe6, 0xb9, 0x75, 0x96, 0x7, 0x3, 0x93, 0xef, 0x54, 0x73, 0x67, 0x1b, 0xf6, 0x3a, 0x9a} // Run DeriveAuthKey Function block, err := aes.NewCipher(sv) From c84c144b09063ae3f81ae73769eba3447dc1543f Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Thu, 9 Nov 2023 16:33:10 +0100 Subject: [PATCH 027/100] fixing issues highlighted by linter --- pkg/hummingbird/hummingbird.go | 61 ++++++++---- pkg/slayers/path/hummingbird/base.go | 6 +- pkg/slayers/path/hummingbird/decoded.go | 6 +- pkg/slayers/path/hummingbird/decoded_test.go | 30 +++--- .../path/hummingbird/flyoverhopfield.go | 15 ++- pkg/slayers/path/hummingbird/mac.go | 12 ++- pkg/slayers/path/hummingbird/mac_test.go | 17 ++-- pkg/slayers/path/hummingbird/raw.go | 55 +++++++---- pkg/slayers/path/hummingbird/raw_test.go | 6 +- pkg/slayers/path/scion/base.go | 2 - pkg/slayers/path/scion/raw.go | 5 - router/dataplane.go | 16 ++-- router/dataplane_hbird.go | 94 ++++++++++++------- 13 files changed, 208 insertions(+), 117 deletions(-) diff --git a/pkg/hummingbird/hummingbird.go b/pkg/hummingbird/hummingbird.go index fd59c7e98d..e257db4d2a 100644 --- a/pkg/hummingbird/hummingbird.go +++ b/pkg/hummingbird/hummingbird.go @@ -27,11 +27,12 @@ type Reservation struct { Ak [16]byte // Bw is the reserved Bandwidth Bw uint16 - // StartTime is the unix timestamp for teh start of the reservation + // StartTime is the unix timestamp for the start of the reservation StartTime uint32 // Duration is the duration of the reservation in seconds Duration uint16 - // EndTime is the unix timestamp at which the reservation ends. Is not strictly necessary but included for simplicity + // EndTime is the unix timestamp at which the reservation ends. + // Is not strictly necessary but included for simplicity EndTime uint32 // Ingress is the ingress interface for the reserved hop Ingress uint16 @@ -40,7 +41,7 @@ type Reservation struct { } // Temporary cheating function until the system to request keys is available -// return true if successfull +// return true if successful func cheat_auth_key(res *Reservation) (Reservation, error) { // ResID is set by seller, pick random res.ResID = uint32(rand.Int31() >> 10) @@ -56,12 +57,14 @@ func cheat_auth_key(res *Reservation) (Reservation, error) { key0 := control.DeriveHbirdSecretValue(mkeys.Key0) prf, _ := aes.NewCipher(key0) buffer := make([]byte, 16) - ak := hummingbird.DeriveAuthKey(prf, res.ResID, res.Bw, res.Ingress, res.Egress, res.StartTime, res.Duration, buffer) + ak := hummingbird.DeriveAuthKey(prf, res.ResID, res.Bw, res.Ingress, res.Egress, + res.StartTime, res.Duration, buffer) copy(res.Ak[:], ak[0:16]) return *res, nil } -// Requests a reservation for each given reservation. Expects AS, Bw, StartTime, EndTime, Ingress and Egress to be filled in +// Requests a reservation for each given reservation. +// Expects AS, Bw, StartTime, EndTime, Ingress and Egress to be filled in func RequestReservations(rs []Reservation) { } @@ -81,7 +84,10 @@ func ConvertToHbirdPath(p snet.Path) (snet.Path, error) { if !ok { return nil, serrors.New("Can only convert SCiON paths to Hummingbird") } - dec := convertSCIONToHbirdDecoded(dpath.Raw) + dec, err := convertSCIONToHbirdDecoded(dpath.Raw) + if err != nil { + return nil, err + } hbird, err := snetpath.NewHbirdFromDecoded(&dec) if err != nil { @@ -98,14 +104,16 @@ func ConvertToHbirdPath(p snet.Path) (snet.Path, error) { return p, nil } -func convertSCIONToHbirdDecoded(p []byte) hummingbird.Decoded { +func convertSCIONToHbirdDecoded(p []byte) (hummingbird.Decoded, error) { scionDec := scion.Decoded{} - scionDec.DecodeFromBytes(p) + if err := scionDec.DecodeFromBytes(p); err != nil { + return hummingbird.Decoded{}, err + } hbirdDec := hummingbird.Decoded{} hbirdDec.ConvertFromScionDecoded(scionDec) - return hbirdDec + return hbirdDec, nil } type HummingbirdClient struct { @@ -134,10 +142,12 @@ func (c *HummingbirdClient) parseIAs(ifs []snet.PathInterface) error { switch true { // First hop after Crossover always has same as as previous hop - case (i == int(c.dec.FirstHopPerSeg[0]) && !c.dec.InfoFields[1].Peer) || (i == int(c.dec.FirstHopPerSeg[1])): + case (i == int(c.dec.FirstHopPerSeg[0]) && !c.dec.InfoFields[1].Peer) || + (i == int(c.dec.FirstHopPerSeg[1])): c.ases[i] = c.ases[i-1] i++ - // Skip duplicates interfaces. Only duplicates we want are those for Xovers, and we already add these manually above + // Skip duplicates interfaces. + // Only duplicates we want are those for Xovers, and we already add these manually above case ifs[j].IA == ifs[j-1].IA: j++ default: @@ -162,10 +172,14 @@ func (c *HummingbirdClient) PrepareHbirdPath(p snet.Path) error { case snetpath.SCION: // Convert path to decoded hbird path scionDec := scion.Decoded{} - scionDec.DecodeFromBytes(v.Raw) + if err := scionDec.DecodeFromBytes(v.Raw); err != nil { + return serrors.Join(err, serrors.New("Failed to Prepare Hummingbird Path")) + } c.dec.ConvertFromScionDecoded(scionDec) case snetpath.Hummingbird: - c.dec.DecodeFromBytes(v.Raw) + if err := c.dec.DecodeFromBytes(v.Raw); err != nil { + return serrors.Join(err, serrors.New("Failed to Prepare Hummingbird Path")) + } default: return serrors.New("Unsupported path type") } @@ -186,7 +200,8 @@ func (c *HummingbirdClient) PrepareHbirdPath(p snet.Path) error { return nil } -func (c *HummingbirdClient) RequestReservationsAllHops(bw uint16, start uint32, duration uint16) error { +func (c *HummingbirdClient) RequestReservationsAllHops( + bw uint16, start uint32, duration uint16) error { return c.RequestReservationForASes(c.ases, bw, start, duration) } @@ -199,7 +214,8 @@ func (c *HummingbirdClient) GetPathASes() []addr.IA { // Requests new reservations for this path for the listed ASes // Expects them to be in order without duplicates -func (c *HummingbirdClient) RequestReservationForASes(asin []addr.IA, bw uint16, start uint32, duration uint16) error { +func (c *HummingbirdClient) RequestReservationForASes( + asin []addr.IA, bw uint16, start uint32, duration uint16) error { j := 0 for i := range c.dec.HopFields { if j >= len(asin) || asin[j] != c.ases[i] { @@ -211,13 +227,15 @@ func (c *HummingbirdClient) RequestReservationForASes(asin []addr.IA, bw uint16, if i < int(c.dec.FirstHopPerSeg[0]) { infIdx = 0 if !c.dec.InfoFields[0].Peer { - lastHopBeforeXover = (i == int(c.dec.FirstHopPerSeg[0])-1) && i < len(c.dec.HopFields)-1 + lastHopBeforeXover = (i == int(c.dec.FirstHopPerSeg[0])-1) && + i < len(c.dec.HopFields)-1 } } else if i < int(c.dec.FirstHopPerSeg[1]) { infIdx = 1 if !c.dec.InfoFields[1].Peer { firstHopAfterXover = i == int(c.dec.FirstHopPerSeg[0]) - lastHopBeforeXover = i == int(c.dec.FirstHopPerSeg[1])-1 && i < len(c.dec.HopFields)-1 + lastHopBeforeXover = i == int(c.dec.FirstHopPerSeg[1])-1 && + i < len(c.dec.HopFields)-1 } } else { infIdx = 2 @@ -321,9 +339,12 @@ func (c *HummingbirdClient) FinalizePath(p snet.Path, pktLen uint16) (snet.Path, } res := c.reservations[i] c.dec.HopFields[i].ResStartTime = uint16(secs - res.StartTime) - flyovermac := hummingbird.FullFlyoverMac(res.Ak[:], c.dest, pktLen, c.dec.HopFields[i].ResStartTime, millis, c.byteBuffer[:], c.xkbuffer[:]) - binary.BigEndian.PutUint32(c.dec.HopFields[i].HopField.Mac[:4], binary.BigEndian.Uint32(flyovermac[:4])^binary.BigEndian.Uint32(c.macs[i][:4])) - binary.BigEndian.PutUint16(c.dec.HopFields[i].HopField.Mac[4:], binary.BigEndian.Uint16(flyovermac[4:])^binary.BigEndian.Uint16(c.macs[i][4:])) + flyovermac := hummingbird.FullFlyoverMac(res.Ak[:], c.dest, pktLen, + c.dec.HopFields[i].ResStartTime, millis, c.byteBuffer[:], c.xkbuffer[:]) + binary.BigEndian.PutUint32(c.dec.HopFields[i].HopField.Mac[:4], + binary.BigEndian.Uint32(flyovermac[:4])^binary.BigEndian.Uint32(c.macs[i][:4])) + binary.BigEndian.PutUint16(c.dec.HopFields[i].HopField.Mac[4:], + binary.BigEndian.Uint16(flyovermac[4:])^binary.BigEndian.Uint16(c.macs[i][4:])) } dphb.Raw = make([]byte, c.dec.Len()) if err := c.dec.SerializeTo(dphb.Raw); err != nil { diff --git a/pkg/slayers/path/hummingbird/base.go b/pkg/slayers/path/hummingbird/base.go index d84021179b..01fe672b89 100644 --- a/pkg/slayers/path/hummingbird/base.go +++ b/pkg/slayers/path/hummingbird/base.go @@ -71,7 +71,8 @@ func (s *Base) IncPath(n int) error { // IsXover returns whether we are at a crossover point. func (s *Base) IsXover() bool { return s.PathMeta.CurrHF+FlyoverLines < uint8(s.NumLines) && - (s.PathMeta.CurrINF != s.InfIndexForHF(s.PathMeta.CurrHF+HopLines) || s.PathMeta.CurrINF != s.InfIndexForHF(s.PathMeta.CurrHF+FlyoverLines)) + s.PathMeta.CurrINF != s.InfIndexForHF(s.PathMeta.CurrHF+HopLines) || + s.PathMeta.CurrINF != s.InfIndexForHF(s.PathMeta.CurrHF+FlyoverLines) } @@ -150,6 +151,7 @@ func (m *MetaHdr) SerializeTo(b []byte) error { } func (m MetaHdr) String() string { - return fmt.Sprintf("{CurrInf: %d, CurrHF: %d, SegLen: %v, BaseTimestamp: %v, HighResTimestamp: %v}", + return fmt.Sprintf( + "{CurrInf: %d, CurrHF: %d, SegLen: %v, BaseTimestamp: %v, HighResTimestamp: %v}", m.CurrINF, m.CurrHF, m.SegLen, m.BaseTS, m.HighResTS) } diff --git a/pkg/slayers/path/hummingbird/decoded.go b/pkg/slayers/path/hummingbird/decoded.go index d55510ca50..2982dccb52 100644 --- a/pkg/slayers/path/hummingbird/decoded.go +++ b/pkg/slayers/path/hummingbird/decoded.go @@ -154,7 +154,8 @@ func (s *Decoded) Reverse() (path.Path, error) { return s, nil } -// RemoveFlyovers removes all reservations from a decoded path and corrects SegLen and CurrHF accordingly +// RemoveFlyovers removes all reservations from a decoded path +// Corrects SegLen and CurrHF accordingly func (s *Decoded) RemoveFlyovers() error { var idxInf uint8 = 0 var offset uint8 = 0 @@ -178,7 +179,8 @@ func (s *Decoded) RemoveFlyovers() error { segCount = 0 idxInf += 1 } else if s.PathMeta.SegLen[idxInf] < segCount { - return serrors.New("new hopfields boundaries do not match new segment lengths after flyover removal") + return serrors.New( + "New hopfields boundaries do not match new segment lengths after flyover removal") } offset += HopLines } diff --git a/pkg/slayers/path/hummingbird/decoded_test.go b/pkg/slayers/path/hummingbird/decoded_test.go index 1431e2a3cd..768d565ac5 100644 --- a/pkg/slayers/path/hummingbird/decoded_test.go +++ b/pkg/slayers/path/hummingbird/decoded_test.go @@ -92,12 +92,12 @@ var emptyDecodedTestPath = &hummingbird.Decoded{ HopFields: []hummingbird.FlyoverHopField{}, } -var rawHbirdPath = []byte("\x00\x02\x04\x00\x00\x00\x03\x28\x00\x00\x04\xd2" + //Pathmeta header - "\x00\x00\x01\x11\x00\x00\x01\x00\x01\x00\x02\x22\x00\x00\x01\x00" + //Infofields - "\x80\x3f\x00\x01\x00\x00\x01\x02\x03\x04\x05\x06\x00\x00\x00\x04\x00\x02\x00\x01" + //flyoverfield 0 - "\x00\x3f\x00\x03\x00\x02\x01\x02\x03\x04\x05\x06" + //hopfield 1 - "\x00\x3f\x00\x00\x00\x02\x01\x02\x03\x04\x05\x06" + //hopfield 2 - "\x80\x3f\x00\x01\x00\x00\x01\x02\x03\x04\x05\x06\x00\x00\x00\x04\x00\x00\x00\x01") //flyoverfield 3 +var rawHbirdPath = []byte("\x00\x02\x04\x00\x00\x00\x03\x28\x00\x00\x04\xd2" + + "\x00\x00\x01\x11\x00\x00\x01\x00\x01\x00\x02\x22\x00\x00\x01\x00" + + "\x80\x3f\x00\x01\x00\x00\x01\x02\x03\x04\x05\x06\x00\x00\x00\x04\x00\x02\x00\x01" + + "\x00\x3f\x00\x03\x00\x02\x01\x02\x03\x04\x05\x06" + + "\x00\x3f\x00\x00\x00\x02\x01\x02\x03\x04\x05\x06" + + "\x80\x3f\x00\x01\x00\x00\x01\x02\x03\x04\x05\x06\x00\x00\x00\x04\x00\x00\x00\x01") type hbirdPathCase struct { infos []bool @@ -117,14 +117,18 @@ var pathReverseCasesHbird = map[string]struct { wantIdxs: [][2]int{{0, 3}, {0, 0}}, }, "1 segment, 5 hops": { - input: hbirdPathCase{[]bool{true}, [][][]uint16{{{11, 1}, {12, 1}, {13, 0}, {14, 1}, {15, 0}}}}, - want: hbirdPathCase{[]bool{false}, [][][]uint16{{{15, 0}, {14, 0}, {13, 0}, {12, 0}, {11, 0}}}}, + input: hbirdPathCase{[]bool{true}, + [][][]uint16{{{11, 1}, {12, 1}, {13, 0}, {14, 1}, {15, 0}}}}, + want: hbirdPathCase{[]bool{false}, + [][][]uint16{{{15, 0}, {14, 0}, {13, 0}, {12, 0}, {11, 0}}}}, inIdxs: [][2]int{{0, 0}, {0, 5}, {0, 10}, {0, 13}, {0, 18}}, wantIdxs: [][2]int{{0, 12}, {0, 9}, {0, 6}, {0, 3}, {0, 0}}, }, "2 segments, 5 hops": { - input: hbirdPathCase{[]bool{true, false}, [][][]uint16{{{11, 0}, {12, 0}}, {{13, 1}, {14, 1}, {15, 0}}}}, - want: hbirdPathCase{[]bool{true, false}, [][][]uint16{{{15, 0}, {14, 0}, {13, 0}}, {{12, 0}, {11, 0}}}}, + input: hbirdPathCase{[]bool{true, false}, + [][][]uint16{{{11, 0}, {12, 0}}, {{13, 1}, {14, 1}, {15, 0}}}}, + want: hbirdPathCase{[]bool{true, false}, + [][][]uint16{{{15, 0}, {14, 0}, {13, 0}}, {{12, 0}, {11, 0}}}}, inIdxs: [][2]int{{0, 0}, {0, 3}, {1, 6}, {1, 11}, {1, 16}}, wantIdxs: [][2]int{{1, 12}, {1, 9}, {0, 6}, {0, 3}, {0, 0}}, }, @@ -204,7 +208,8 @@ func TestDecodedToRawHbird(t *testing.T) { assert.Equal(t, rawHbirdTestPath, raw) } -func mkDecodedHbirdPath(t *testing.T, pcase hbirdPathCase, infIdx, hopIdx uint8) *hummingbird.Decoded { +func mkDecodedHbirdPath(t *testing.T, pcase hbirdPathCase, + infIdx, hopIdx uint8) *hummingbird.Decoded { t.Helper() s := &hummingbird.Decoded{} meta := hummingbird.MetaHdr{ @@ -221,7 +226,8 @@ func mkDecodedHbirdPath(t *testing.T, pcase hbirdPathCase, infIdx, hopIdx uint8) for _, hop := range hops { f := hop[1] == 1 s.HopFields = append(s.HopFields, hummingbird.FlyoverHopField{ - HopField: path.HopField{ConsIngress: hop[0], ConsEgress: hop[0], Mac: [6]byte{1, 2, 3, 4, 5, 6}}, + HopField: path.HopField{ConsIngress: hop[0], ConsEgress: hop[0], + Mac: [6]byte{1, 2, 3, 4, 5, 6}}, Flyover: f, Duration: 2}) if f { diff --git a/pkg/slayers/path/hummingbird/flyoverhopfield.go b/pkg/slayers/path/hummingbird/flyoverhopfield.go index 9c040dd8db..db9cdaca46 100644 --- a/pkg/slayers/path/hummingbird/flyoverhopfield.go +++ b/pkg/slayers/path/hummingbird/flyoverhopfield.go @@ -31,7 +31,8 @@ type FlyoverHopField struct { ResID uint32 // Bw is the reserved banwidth of the flyover Bw uint16 - // ResStartTime is the start time of the reservation, as a negative offset from the BaseTimeStamp in the PathMetaHdr + // ResStartTime is the start time of the reservation + // As a negative offset from the BaseTimeStamp in the PathMetaHdr ResStartTime uint16 // Duration is the duration of the reservation Duration uint16 @@ -54,9 +55,11 @@ func (h *FlyoverHopField) DecodeFromBytes(raw []byte) (err error) { h.Flyover = raw[0]&0x80 == 0x80 if h.Flyover { if len(raw) < FlyoverLen { - return serrors.New("FlyoverHopField raw too short", "expected", FlyoverLen, "actual", len(raw)) + return serrors.New("FlyoverHopField raw too short", "expected", + FlyoverLen, "actual", len(raw)) } - //@ assert &raw[12:16][0] == &raw[12] && &raw[12:16][1] == &raw[12] && &raw[12:16][2] == &raw[14] && &raw[12:16][3] == &raw[15] + //@ assert &raw[12:16][0] == &raw[12] && &raw[12:16][1] == &raw[12] + // && &raw[12:16][2] == &raw[14] && &raw[12:16][3] == &raw[15] h.ResID = binary.BigEndian.Uint32(raw[12:16]) >> 10 h.Bw = binary.BigEndian.Uint16(raw[14:16]) & 0x03ff //@ assert &raw[16:18][0] == &raw[16] && &raw[16:18][1] == &raw[17] @@ -84,10 +87,12 @@ func (h *FlyoverHopField) SerializeTo(b []byte) (err error) { if h.Flyover { if len(b) < FlyoverLen { - return serrors.New("buffer for FlyoverHopField too short", "expected", FlyoverLen, "actual", len(b)) + return serrors.New("buffer for FlyoverHopField too short", "expected", + FlyoverLen, "actual", len(b)) } b[0] |= 0x80 - //@ assert &b[12:16][0] == &b[12] && &b[12:16][1] == &b[12] && &b[12:16][2] == &b[14] && &b[12:16][3] == &b[15] + //@ assert &b[12:16][0] == &b[12] && &b[12:16][1] == &b[12] && &b[12:16][2] == &b[14] + // && &b[12:16][3] == &b[15] binary.BigEndian.PutUint32(b[12:16], h.ResID<<10+uint32(h.Bw)) //@ assert &b[16:18][0] == &b[16] && &b[16:18][1] == &b[17] binary.BigEndian.PutUint16(b[16:18], h.ResStartTime) diff --git a/pkg/slayers/path/hummingbird/mac.go b/pkg/slayers/path/hummingbird/mac.go index 2acbd1e5d4..b394a27947 100644 --- a/pkg/slayers/path/hummingbird/mac.go +++ b/pkg/slayers/path/hummingbird/mac.go @@ -15,7 +15,8 @@ import ( //go:noescape func encryptBlockAsm(nr int, xk *uint32, dst, src *byte) -//TODO: test expandKeyAsm on arm64 and ppc64 machines. Compare with code in go/src/crypto/aes/asm_* if necessary +// TODO: test expandKeyAsm on arm64 and ppc64 machines. +// Compare with code in go/src/crypto/aes/asm_* if necessary //go:noescape func expandKeyAsm(nr int, key *byte, enc *uint32) @@ -28,8 +29,10 @@ const PathType = 5 var ZeroBlock [aes.BlockSize]byte // Derive authentication key A_k -// block is expected to be initialized beforehand with aes.NewCipher(sv), where sv is this AS' secret value -func DeriveAuthKey(block cipher.Block, resId uint32, bw, in, eg uint16, startTime uint32, resDuration uint16, buffer []byte) []byte { +// block is expected to be initialized beforehand with aes.NewCipher(sv), +// where sv is this AS' secret value +func DeriveAuthKey(block cipher.Block, resId uint32, bw, in, eg uint16, + startTime uint32, resDuration uint16, buffer []byte) []byte { if len(buffer) < AkBufferSize { buffer = make([]byte, AkBufferSize) @@ -49,7 +52,8 @@ func DeriveAuthKey(block cipher.Block, resId uint32, bw, in, eg uint16, startTim // Computes full flyover mac vk // Needs a xkbuffer of 44 uint32s to store the expanded keys for aes -func FullFlyoverMac(ak []byte, dstIA addr.IA, pktlen uint16, resStartTime uint16, highResTime uint32, buffer []byte, xkbuffer []uint32) []byte { +func FullFlyoverMac(ak []byte, dstIA addr.IA, pktlen uint16, resStartTime uint16, + highResTime uint32, buffer []byte, xkbuffer []uint32) []byte { if len(buffer) < FlyoverMacBufferSize { buffer = make([]byte, FlyoverMacBufferSize) } diff --git a/pkg/slayers/path/hummingbird/mac_test.go b/pkg/slayers/path/hummingbird/mac_test.go index fa66cbf08e..f5d924491a 100644 --- a/pkg/slayers/path/hummingbird/mac_test.go +++ b/pkg/slayers/path/hummingbird/mac_test.go @@ -65,7 +65,8 @@ func TestDeriveAuthKeyGoldenData(t *testing.T) { var start uint32 = 0x0030001 var duration uint16 = 0x0203 - expected := [16]byte{0x25, 0xe6, 0xb9, 0x75, 0x96, 0x7, 0x3, 0x93, 0xef, 0x54, 0x73, 0x67, 0x1b, 0xf6, 0x3a, 0x9a} + expected := [16]byte{0x25, 0xe6, 0xb9, 0x75, 0x96, 0x7, 0x3, 0x93, 0xef, 0x54, 0x73, 0x67, + 0x1b, 0xf6, 0x3a, 0x9a} // Run DeriveAuthKey Function block, err := aes.NewCipher(sv) @@ -134,9 +135,10 @@ func BenchmarkDeriveAuthKeyManually(b *testing.B) { } } -// We use CBC-MAC using aes for the flyover mac. As we have only one block of input, this leads to the mac consisting of a single aes call. +// We use CBC-MAC using aes for the flyover mac. func TestFlyoverMac(t *testing.T) { - ak := []byte{0x7e, 0x61, 0x4, 0x91, 0x30, 0x6b, 0x95, 0xec, 0xb5, 0x75, 0xc6, 0xe9, 0x4c, 0x5a, 0x89, 0x84} + ak := []byte{0x7e, 0x61, 0x4, 0x91, 0x30, 0x6b, 0x95, 0xec, 0xb5, 0x75, 0xc6, 0xe9, + 0x4c, 0x5a, 0x89, 0x84} var dstIA addr.IA = 326 var pktlen uint16 = 23 var resStartTs uint16 = 1234 @@ -164,7 +166,8 @@ func TestFlyoverMac(t *testing.T) { // Golden data test used for cross verification with other implementations func TestFlyoverMacGoldenData(t *testing.T) { - ak := []byte{0x7e, 0x61, 0x4, 0x91, 0x30, 0x6b, 0x95, 0xec, 0xb5, 0x75, 0xc6, 0xe9, 0x4c, 0x5a, 0x89, 0x84} + ak := []byte{0x7e, 0x61, 0x4, 0x91, 0x30, 0x6b, 0x95, 0xec, 0xb5, 0x75, 0xc6, 0xe9, + 0x4c, 0x5a, 0x89, 0x84} var dstIA addr.IA = 326 var pktlen uint16 = 23 var resStartTs uint16 = 1234 @@ -172,7 +175,8 @@ func TestFlyoverMacGoldenData(t *testing.T) { buffer := make([]byte, 32) xkbuffer := make([]uint32, 44) - expected := [16]byte{0xbe, 0xad, 0xcf, 0x70, 0xf, 0x75, 0xdf, 0x8, 0xde, 0x91, 0xe9, 0xda, 0xf5, 0xcb, 0x9f, 0x74} + expected := [16]byte{0xbe, 0xad, 0xcf, 0x70, 0xf, 0x75, 0xdf, 0x8, 0xde, 0x91, 0xe9, 0xda, + 0xf5, 0xcb, 0x9f, 0x74} mac := hummingbird.FullFlyoverMac(ak, dstIA, pktlen, resStartTs, highResTs, buffer, xkbuffer) require.Equal(t, expected[:], mac) @@ -181,7 +185,8 @@ func TestFlyoverMacGoldenData(t *testing.T) { } func BenchmarkFlyoverMac(b *testing.B) { - ak := []byte{0x7e, 0x61, 0x4, 0x91, 0x30, 0x6b, 0x95, 0xec, 0xb5, 0x75, 0xc6, 0xe9, 0x4c, 0x5a, 0x89, 0x84} + ak := []byte{0x7e, 0x61, 0x4, 0x91, 0x30, 0x6b, 0x95, 0xec, 0xb5, 0x75, 0xc6, 0xe9, + 0x4c, 0x5a, 0x89, 0x84} var dstIA addr.IA = 326 var pktlen uint16 = 23 var resStartTs uint16 = 1234 diff --git a/pkg/slayers/path/hummingbird/raw.go b/pkg/slayers/path/hummingbird/raw.go index 15d715eb3c..84edfebe0c 100644 --- a/pkg/slayers/path/hummingbird/raw.go +++ b/pkg/slayers/path/hummingbird/raw.go @@ -143,7 +143,8 @@ func (s *Raw) GetHopField(idx int) (FlyoverHopField, error) { if idx == s.NumLines-HopLines { maxHopLen = HopLen } else { - return FlyoverHopField{}, serrors.New("Invalid hopfield index", "NumHops", s.NumLines, "index", idx) + return FlyoverHopField{}, serrors.New( + "Invalid hopfield index", "NumHops", s.NumLines, "index", idx) } } if err := hop.DecodeFromBytes(s.Raw[hopOffset : hopOffset+maxHopLen]); err != nil { @@ -160,11 +161,13 @@ func (s *Raw) GetCurrentHopField() (FlyoverHopField, error) { func (s *Raw) ReplacMac(idx int, mac []byte) error { if idx >= s.NumLines-HopLines+1 { - return serrors.New("HopField index out of bounds", "max", s.NumLines-HopLines, "actual", idx) + return serrors.New("HopField index out of bounds", "max", + s.NumLines-HopLines, "actual", idx) } offset := s.NumINF*path.InfoLen + MetaLen + idx*LineLen + MacOffset if n := copy(s.Raw[offset:offset+path.MacLen], mac[:path.MacLen]); n != path.MacLen { - return serrors.New("copied worng number of bytes for mac replacement", "expected", path.MacLen, "actual", n) + return serrors.New("copied worng number of bytes for mac replacement", + "expected", path.MacLen, "actual", n) } return nil } @@ -177,7 +180,8 @@ func (s *Raw) ReplaceCurrentMac(mac []byte) error { // Returns a slice of the MAC of the hopfield starting at index idx func (s *Raw) GetMac(idx int) ([]byte, error) { if idx >= s.NumLines-HopLines+1 { - return nil, serrors.New("HopField index out of bounds", "max", s.NumLines-HopLines, "actual", idx) + return nil, serrors.New("HopField index out of bounds", + "max", s.NumLines-HopLines, "actual", idx) } offset := s.NumINF*path.InfoLen + MetaLen + idx*LineLen + MacOffset return s.Raw[offset : offset+path.MacLen], nil @@ -186,28 +190,36 @@ func (s *Raw) GetMac(idx int) ([]byte, error) { // SetHopField updates the HopField at a given index. // For Hummingbird paths the index is the offset in 4 byte lines // -// If replacing a FlyoverHopField with a Hopfield, it is replaced by a FlyoverHopField with dummy values. -// This works for SCMP packets as Flyover hops are removed later in the process of building a SCMP packet. +// If replacing a FlyoverHopField with a Hopfield, +// it is replaced by a FlyoverHopField with dummy values. +// This works for SCMP packets as Flyover hops are removed later +// in the process of building a SCMP packet. func (s *Raw) SetHopField(hop FlyoverHopField, idx int) error { if idx >= s.NumLines-HopLines+1 { - return serrors.New("HopField index out of bounds", "max", s.NumLines-HopLines, "actual", idx) + return serrors.New("HopField index out of bounds", + "max", s.NumLines-HopLines, "actual", idx) } hopOffset := MetaLen + s.NumINF*path.InfoLen + idx*LineLen if s.Raw[hopOffset]&0x80 == 0x80 { - // IF the current hop is a flyover, the flyover bit of the new hop is set to 1 in order to preserve correctness of the path + // IF the current hop is a flyover, the flyover bit of the new hop is set to 1 + // in order to preserve correctness of the path + // // The reservation data of the new hop is dummy data and invalid. - // This works because SetHopField is currently only used to prepare a SCMP packet, and all flyovers are removed later in that process + // This works because SetHopField is currently only used to prepare a SCMP packet, + // and all flyovers are removed later in that process // // IF this is ever used for something else, this function needs to be re-written hop.Flyover = true } if hop.Flyover { if idx >= s.NumLines-FlyoverLines+1 { - return serrors.New("FlyoverHopField index out of bounds", "max", s.NumLines-FlyoverLines, "actual", idx) + return serrors.New("FlyoverHopField index out of bounds", + "max", s.NumLines-FlyoverLines, "actual", idx) } hopOffset := MetaLen + s.NumINF*path.InfoLen + idx*LineLen if s.Raw[hopOffset]&0x80 == 0x00 { - return serrors.New("Setting FlyoverHopField over Hopfield with setHopField not supported") + return serrors.New( + "Setting FlyoverHopField over Hopfield with setHopField not supported") } return hop.SerializeTo(s.Raw[hopOffset : hopOffset+FlyoverLen]) } @@ -221,7 +233,8 @@ func (s *Raw) IsFirstHop() bool { // IsLastHop returns whether the current hop is the last hop on the path. func (s *Raw) IsLastHop() bool { - return int(s.PathMeta.CurrHF) == (s.NumLines-HopLines) || int(s.PathMeta.CurrHF) == (s.NumLines-FlyoverLines) + return int(s.PathMeta.CurrHF) == (s.NumLines-HopLines) || + int(s.PathMeta.CurrHF) == (s.NumLines-FlyoverLines) } // Returns the egress interface of the next hop @@ -236,7 +249,8 @@ func (s *Raw) GetNextEgress() (uint16, error) { hopOffset += HopLines * LineLen } if idx >= s.NumLines-2 { - return 0, serrors.New("HopField index out of bounds", "max", s.NumLines-HopLines, "actual", idx) + return 0, serrors.New("HopField index out of bounds", "max", + s.NumLines-HopLines, "actual", idx) } if s.isConsdir(uint8(idx)) { return binary.BigEndian.Uint16(s.Raw[hopOffset+4 : hopOffset+6]), nil @@ -263,7 +277,8 @@ func (s *Raw) GetPreviousIngress() (uint16, error) { func (s *Raw) DoFlyoverXover() error { idx := int(s.Base.PathMeta.CurrHF) if idx >= s.NumLines-7 { - return serrors.New("CurrHF out of bounds for flyover crossover", "max", s.NumLines-7, "actual", idx) + return serrors.New("CurrHF out of bounds for flyover crossover", + "max", s.NumLines-7, "actual", idx) } if s.PathMeta.CurrINF == 2 { return serrors.New("Cannot do FlyoverXover if CurrINF = 2") @@ -278,7 +293,8 @@ func (s *Raw) DoFlyoverXover() error { // buffer flyover and copy data var t [2 * LineLen]byte copy(t[:], s.Raw[hopOffset+HopLen:hopOffset+FlyoverLen]) - copy(s.Raw[hopOffset+HopLen:hopOffset+2*HopLen], s.Raw[hopOffset+FlyoverLen:hopOffset+FlyoverLen+HopLen]) + copy(s.Raw[hopOffset+HopLen:hopOffset+2*HopLen], + s.Raw[hopOffset+FlyoverLen:hopOffset+FlyoverLen+HopLen]) copy(s.Raw[hopOffset+2*HopLen:hopOffset+HopLen+FlyoverLen], t[:]) // Unset and Set Flyoverbits @@ -295,7 +311,8 @@ func (s *Raw) DoFlyoverXover() error { func (s *Raw) ReverseFlyoverXover() error { idx := int(s.Base.PathMeta.CurrHF) if idx < 6 { - return serrors.New("CurrHF too small for reversing flyover crossover", "min", 6, "actual", idx) + return serrors.New("CurrHF too small for reversing flyover crossover", + "min", 6, "actual", idx) } if s.PathMeta.CurrINF == 0 { return serrors.New("Cannot reverse Flyover Xover when CurrINF = 0") @@ -305,11 +322,13 @@ func (s *Raw) ReverseFlyoverXover() error { return serrors.New("Current hop does not have a Flyover") } if s.Raw[hopOffset-HopLen]&0x80 != 0x00 { - return serrors.New("Cannot Reverse Flyover Crossover, flyover bit set where previous hop should be") + return serrors.New( + "Cannot Reverse Flyover Crossover, flyover bit set where previous hop should be") } var t [FlyoverLen - HopLen]byte copy(t[:], s.Raw[hopOffset+HopLen:hopOffset+FlyoverLen]) - copy(s.Raw[hopOffset+FlyoverLen-HopLen:hopOffset+FlyoverLen], s.Raw[hopOffset:hopOffset+HopLen]) + copy(s.Raw[hopOffset+FlyoverLen-HopLen:hopOffset+FlyoverLen], + s.Raw[hopOffset:hopOffset+HopLen]) copy(s.Raw[hopOffset:hopOffset+FlyoverLen-HopLen], t[:]) // Set and Unset Flyoverbits s.Raw[hopOffset-HopLen] |= 0x80 diff --git a/pkg/slayers/path/hummingbird/raw_test.go b/pkg/slayers/path/hummingbird/raw_test.go index b66c3ff7b4..e135bceb5b 100644 --- a/pkg/slayers/path/hummingbird/raw_test.go +++ b/pkg/slayers/path/hummingbird/raw_test.go @@ -65,8 +65,10 @@ func TestRawReverseHbird(t *testing.T) { i := i t.Run(fmt.Sprintf("%s case %d", name, i+1), func(t *testing.T) { //t.Parallel() - input := mkRawHbirdPath(t, tc.input, uint8(tc.inIdxs[i][0]), uint8(tc.inIdxs[i][1])) - want := mkRawHbirdPath(t, tc.want, uint8(tc.wantIdxs[i][0]), uint8(tc.wantIdxs[i][1])) + input := mkRawHbirdPath(t, tc.input, uint8(tc.inIdxs[i][0]), + uint8(tc.inIdxs[i][1])) + want := mkRawHbirdPath(t, tc.want, uint8(tc.wantIdxs[i][0]), + uint8(tc.wantIdxs[i][1])) revPath, err := input.Reverse() assert.NoError(t, err) assert.Equal(t, want, revPath) diff --git a/pkg/slayers/path/scion/base.go b/pkg/slayers/path/scion/base.go index 9ca2071d66..b46740d147 100644 --- a/pkg/slayers/path/scion/base.go +++ b/pkg/slayers/path/scion/base.go @@ -45,8 +45,6 @@ type Base struct { // NumINF is the number of InfoFields in the path. NumINF int // NumHops is the number HopFields in the path. - // If IsHummingbird is true, NumHops is the number of 4 bytes lines in the path instead of the number of hops. - // TODO: rename and reevaluate? NumHops int } diff --git a/pkg/slayers/path/scion/raw.go b/pkg/slayers/path/scion/raw.go index 73c0b0b1c7..8591093774 100644 --- a/pkg/slayers/path/scion/raw.go +++ b/pkg/slayers/path/scion/raw.go @@ -130,7 +130,6 @@ func (s *Raw) SetInfoField(info path.InfoField, idx int) error { } // GetHopField returns the HopField at a given index. -// For Hummingbird paths the index is the offset in 4 byte lines func (s *Raw) GetHopField(idx int) (path.HopField, error) { if idx >= s.NumHops { return path.HopField{}, @@ -151,10 +150,6 @@ func (s *Raw) GetCurrentHopField() (path.HopField, error) { } // SetHopField updates the HopField at a given index. -// For Hummingbird paths the index is the offset in 4 byte lines -// -// If replacing a FlyoverHopField with a Hopfield, it is replaced by a FlyoverHopField with dummy values. -// This works for SCMP packets as Flyover hops are removed later in the process of building a SCMP packet. func (s *Raw) SetHopField(hop path.HopField, idx int) error { if idx >= s.NumHops { return serrors.New("HopField index out of bounds", "max", s.NumHops-1, "actual", idx) diff --git a/router/dataplane.go b/router/dataplane.go index 5f860c923d..3c9e49e4b0 100644 --- a/router/dataplane.go +++ b/router/dataplane.go @@ -148,7 +148,7 @@ var ( macVerificationFailed = errors.New("MAC verification failed") badPacketSize = errors.New("bad packet size") slowPathRequired = errors.New("slow-path required") - reservationExpired = errors.New("current time is outside of reservation validity window") + reservationExpired = errors.New("current time is outside of reservation validity") // zeroBuffer will be used to reset the Authenticator option in the // scionPacketProcessor.OptAuth @@ -993,12 +993,14 @@ type processResult struct { func newPacketProcessor(d *DataPlane) *scionPacketProcessor { p := &scionPacketProcessor{ - d: d, - buffer: gopacket.NewSerializeBuffer(), - prf: d.prfFactory(), - mac: d.macFactory(), - macInputBuffer: make([]byte, max(path.MACBufferSize+hummingbird.FlyoverMacBufferSize+hummingbird.AkBufferSize, libepic.MACBufferSize)), - hbirdXkbuffer: make([]uint32, hummingbird.XkBufferSize), + d: d, + buffer: gopacket.NewSerializeBuffer(), + prf: d.prfFactory(), + mac: d.macFactory(), + macInputBuffer: make([]byte, + max(path.MACBufferSize+hummingbird.FlyoverMacBufferSize+hummingbird.AkBufferSize, + libepic.MACBufferSize)), + hbirdXkbuffer: make([]uint32, hummingbird.XkBufferSize), } p.scionLayer.RecyclePaths() return p diff --git a/router/dataplane_hbird.go b/router/dataplane_hbird.go index fbe79c7d00..e0baa75d98 100644 --- a/router/dataplane_hbird.go +++ b/router/dataplane_hbird.go @@ -19,7 +19,7 @@ import ( "github.com/scionproto/scion/pkg/spao" ) -// SetSecretValue sets the secret value for the PRF function used to compute the Hummingbird Auth Key +// SetSecretValue sets the key for the PRF function used to compute the Hummingbird Auth Key func (d *DataPlane) SetSecretValue(key []byte) error { d.mtx.Lock() defer d.mtx.Unlock() @@ -84,7 +84,9 @@ func determinePeerHbird(pathMeta hummingbird.MetaHdr, inf path.InfoField) (bool, // because we already know this is a well-formed peering path. currHF := pathMeta.CurrHF segLen := pathMeta.SegLen[0] - return currHF == segLen-hummingbird.HopLines || currHF == segLen-hummingbird.FlyoverLines || currHF == segLen, nil + peer := currHF == segLen-hummingbird.HopLines || currHF == segLen-hummingbird.FlyoverLines || + currHF == segLen + return peer, nil } func (p *scionPacketProcessor) determinePeerHbird() (processResult, error) { @@ -118,7 +120,8 @@ func (p *scionPacketProcessor) currentHbirdInfoPointer() uint16 { func (p *scionPacketProcessor) currentHbirdHopPointer() uint16 { return uint16(slayers.CmnHdrLen + p.scionLayer.AddrHdrLen() + - hummingbird.MetaLen + path.InfoLen*p.hbirdPath.NumINF + hummingbird.LineLen*int(p.hbirdPath.PathMeta.CurrHF)) + hummingbird.MetaLen + path.InfoLen*p.hbirdPath.NumINF + + hummingbird.LineLen*int(p.hbirdPath.PathMeta.CurrHF)) } func (p *scionPacketProcessor) verifyCurrentHbirdMAC() (processResult, error) { @@ -129,11 +132,13 @@ func (p *scionPacketProcessor) verifyCurrentHbirdMAC() (processResult, error) { if p.flyoverField.Flyover { ingress := p.hopField.ConsIngress egress := p.hopField.ConsEgress - // Reservations are not bidirectional, reservation ingress and egress are always real ingress and egress + // Reservations are not bidirectional, + // reservation ingress and egress are always real ingress and egress if !p.infoField.ConsDir { ingress, egress = egress, ingress } - // On crossovers, A Reservation goes from the ingress of the incoming hop to the egress of the outgoing one + // On crossovers, A Reservation goes from the ingress of the incoming hop to + // the egress of the outgoing one var err error if p.hbirdPath.IsXover() && !p.peering { egress, err = p.hbirdPath.GetNextEgress() @@ -147,11 +152,13 @@ func (p *scionPacketProcessor) verifyCurrentHbirdMAC() (processResult, error) { } } - ak := hummingbird.DeriveAuthKey(p.prf, p.flyoverField.ResID, p.flyoverField.Bw, ingress, egress, - p.hbirdPath.PathMeta.BaseTS-uint32(p.flyoverField.ResStartTime), p.flyoverField.Duration, + ak := hummingbird.DeriveAuthKey(p.prf, p.flyoverField.ResID, p.flyoverField.Bw, + ingress, egress, p.hbirdPath.PathMeta.BaseTS-uint32(p.flyoverField.ResStartTime), + p.flyoverField.Duration, p.macInputBuffer[path.MACBufferSize+hummingbird.FlyoverMacBufferSize:]) - flyoverMac = hummingbird.FullFlyoverMac(ak, p.scionLayer.DstIA, p.scionLayer.PayloadLen, p.flyoverField.ResStartTime, - p.hbirdPath.PathMeta.HighResTS, p.macInputBuffer[path.MACBufferSize:], p.hbirdXkbuffer) + flyoverMac = hummingbird.FullFlyoverMac(ak, p.scionLayer.DstIA, p.scionLayer.PayloadLen, + p.flyoverField.ResStartTime, p.hbirdPath.PathMeta.HighResTS, + p.macInputBuffer[path.MACBufferSize:], p.hbirdXkbuffer) } // Perform updateHbirdNonConsDirIngressSegID // This needs de-aggregated MAC but needs to be done before scionMac is computed @@ -172,19 +179,27 @@ func (p *scionPacketProcessor) verifyCurrentHbirdMAC() (processResult, error) { scionMac := path.FullMAC(p.mac, p.infoField, p.hopField, p.macInputBuffer[:path.MACBufferSize]) // Aggregate MACS and verify if necessary if p.flyoverField.Flyover { - binary.BigEndian.PutUint32(flyoverMac[0:4], binary.BigEndian.Uint32(scionMac[0:4])^binary.BigEndian.Uint32(flyoverMac[0:4])) - binary.BigEndian.PutUint16(flyoverMac[4:6], binary.BigEndian.Uint16(scionMac[4:6])^binary.BigEndian.Uint16(flyoverMac[4:6])) + binary.BigEndian.PutUint32(flyoverMac[0:4], + binary.BigEndian.Uint32(scionMac[0:4])^binary.BigEndian.Uint32(flyoverMac[0:4])) + binary.BigEndian.PutUint16(flyoverMac[4:6], + binary.BigEndian.Uint16(scionMac[4:6])^binary.BigEndian.Uint16(flyoverMac[4:6])) - verified = subtle.ConstantTimeCompare(p.hopField.Mac[:path.MacLen], flyoverMac[:path.MacLen]) + verified = subtle.ConstantTimeCompare(p.hopField.Mac[:path.MacLen], + flyoverMac[:path.MacLen]) if verified == 0 { - log.Debug("SCMP: Aggregate MAC verification failed", "expected", fmt.Sprintf("%x", flyoverMac[:path.MacLen]), - "actual", fmt.Sprintf("%x", p.hopField.Mac[:path.MacLen]), "cons_dir", p.infoField.ConsDir, + log.Debug("SCMP: Aggregate MAC verification failed", + "expected", fmt.Sprintf("%x", flyoverMac[:path.MacLen]), + "actual", fmt.Sprintf("%x", p.hopField.Mac[:path.MacLen]), + "cons_dir", p.infoField.ConsDir, "scionMac", fmt.Sprintf("%x", scionMac[:path.MacLen]), "if_id", p.ingressID, "curr_inf", p.hbirdPath.PathMeta.CurrINF, - "curr_hf", p.hbirdPath.PathMeta.CurrHF, "seg_id", p.infoField.SegID, "packet length", p.scionLayer.PayloadLen, - "dest", p.scionLayer.DstIA, "startTime", p.flyoverField.ResStartTime, "highResTS", p.hbirdPath.PathMeta.HighResTS, - "ResID", p.flyoverField.ResID, "Bw", p.flyoverField.Bw, "in", p.hopField.ConsIngress, - "Eg", p.hopField.ConsEgress, "start ak", p.hbirdPath.PathMeta.BaseTS-uint32(p.flyoverField.ResStartTime), + "curr_hf", p.hbirdPath.PathMeta.CurrHF, "seg_id", p.infoField.SegID, + "packet length", p.scionLayer.PayloadLen, + "dest", p.scionLayer.DstIA, "startTime", p.flyoverField.ResStartTime, + "highResTS", p.hbirdPath.PathMeta.HighResTS, + "ResID", p.flyoverField.ResID, "Bw", p.flyoverField.Bw, + "in", p.hopField.ConsIngress, "Eg", p.hopField.ConsEgress, + "start ak", p.hbirdPath.PathMeta.BaseTS-uint32(p.flyoverField.ResStartTime), "Duration", p.flyoverField.Duration) } } else { @@ -248,7 +263,8 @@ func (p *scionPacketProcessor) ingressInterfaceHbird() uint16 { if err != nil { // cannot be out of range panic(err) } - // Previous hop should always be a non-flyover field, as flyover is transferred to second hop on xover + // Previous hop should always be a non-flyover field, + // as flyover is transferred to second hop on xover hop, err = p.hbirdPath.GetHopField(int(p.hbirdPath.PathMeta.CurrHF) - hummingbird.HopLines) if err != nil { // cannot be out of range panic(err) @@ -282,10 +298,11 @@ func (p *scionPacketProcessor) validateHbirdTransitUnderlaySrc() (processResult, // Verifies the PathMetaHeader timestamp is recent // Current implementation works with a nanosecond granularity HighResTS func (p *scionPacketProcessor) validatePathMetaTimestamp() { - timestamp := util.SecsToTime(p.hbirdPath.PathMeta.BaseTS).Add(time.Duration(p.hbirdPath.PathMeta.HighResTS>>22) * time.Millisecond) + timestamp := util.SecsToTime(p.hbirdPath.PathMeta.BaseTS).Add( + time.Duration(p.hbirdPath.PathMeta.HighResTS>>22) * time.Millisecond) // TODO: make a configurable value instead of using a flat 1 seconds if time.Until(timestamp).Abs() > time.Duration(1)*time.Second { - // Hummingbird specification explicitely says to forward best-effort is timestamp too old + // Hummingbird specification explicitly says to forward best-effort is timestamp too old p.hasPriority = false } } @@ -299,7 +316,8 @@ func (p *scionPacketProcessor) handleHbirdIngressRouterAlert() (processResult, e return processResult{}, nil } *alert = false - if err := p.hbirdPath.SetHopField(p.flyoverField, int(p.hbirdPath.PathMeta.CurrHF)); err != nil { + err := p.hbirdPath.SetHopField(p.flyoverField, int(p.hbirdPath.PathMeta.CurrHF)) + if err != nil { return processResult{}, serrors.WrapStr("update hop field", err) } slowPathRequest := slowPathRequest{ @@ -319,7 +337,8 @@ func (p *scionPacketProcessor) handleHbirdEgressRouterAlert() (processResult, er return processResult{}, nil } *alert = false - if err := p.hbirdPath.SetHopField(p.flyoverField, int(p.hbirdPath.PathMeta.CurrHF)); err != nil { + err := p.hbirdPath.SetHopField(p.flyoverField, int(p.hbirdPath.PathMeta.CurrHF)) + if err != nil { return processResult{}, serrors.WrapStr("update hop field", err) } slowPathRequest := slowPathRequest{ @@ -332,7 +351,8 @@ func (p *scionPacketProcessor) handleHbirdEgressRouterAlert() (processResult, er func (p *scionPacketProcessor) updateHbirdNonConsDirIngressSegIDFlyover(flyoverMac []byte) error { // against construction dir the ingress router updates the SegID, ifID == 0 // means this comes from this AS itself, so nothing has to be done. - // If a flyover is presebt, need to first de-aggregate the first two bytes of the mac before updating segID + // If a flyover is present, need to first de-aggregate the first two bytes of the mac + // before updating SegID if !p.infoField.ConsDir && p.ingressID != 0 && !p.peering { // de-aggregate first two bytes of mac p.hopField.Mac[0] ^= flyoverMac[0] @@ -341,7 +361,8 @@ func (p *scionPacketProcessor) updateHbirdNonConsDirIngressSegIDFlyover(flyoverM // restore correct state of MAC field, even if error p.hopField.Mac[0] ^= flyoverMac[0] p.hopField.Mac[1] ^= flyoverMac[1] - if err := p.hbirdPath.SetInfoField(p.infoField, int(p.hbirdPath.PathMeta.CurrINF)); err != nil { + err := p.hbirdPath.SetInfoField(p.infoField, int(p.hbirdPath.PathMeta.CurrINF)) + if err != nil { return serrors.WrapStr("update info field", err) } } @@ -353,7 +374,8 @@ func (p *scionPacketProcessor) updateHbirdNonConsDirIngressSegID() error { // means this comes from this AS itself, so nothing has to be done. if !p.infoField.ConsDir && p.ingressID != 0 && !p.peering { p.infoField.UpdateSegID(p.hopField.Mac) - if err := p.hbirdPath.SetInfoField(p.infoField, int(p.hbirdPath.PathMeta.CurrINF)); err != nil { + err := p.hbirdPath.SetInfoField(p.infoField, int(p.hbirdPath.PathMeta.CurrINF)) + if err != nil { return serrors.WrapStr("update info field", err) } } @@ -380,18 +402,23 @@ func (p *scionPacketProcessor) doFlyoverXover() error { } // Buffer flyover part of current mac by xoring it with cached scionMac - binary.BigEndian.PutUint32(p.macInputBuffer[6:10], binary.BigEndian.Uint32(p.flyoverField.HopField.Mac[0:4])^binary.BigEndian.Uint32(p.cachedMac[0:4])) + binary.BigEndian.PutUint32(p.macInputBuffer[6:10], + binary.BigEndian.Uint32(p.flyoverField.HopField.Mac[0:4])^ + binary.BigEndian.Uint32(p.cachedMac[0:4])) p.macInputBuffer[10] = p.flyoverField.HopField.Mac[4] ^ p.cachedMac[4] p.macInputBuffer[11] = p.flyoverField.HopField.Mac[5] ^ p.cachedMac[5] // deaggregate current mac copy(p.hopField.Mac[:], p.cachedMac[0:6]) - p.hbirdPath.ReplaceCurrentMac(p.cachedMac) + if err := p.hbirdPath.ReplaceCurrentMac(p.cachedMac); err != nil { + return err + } // Aggregate Mac of second hop mac, err := p.hbirdPath.GetMac(int(p.hbirdPath.PathMeta.CurrHF) + hummingbird.HopLines) if err != nil { return err } - binary.BigEndian.PutUint32(mac[0:4], binary.BigEndian.Uint32(mac[0:4])^binary.BigEndian.Uint32(p.macInputBuffer[6:10])) + binary.BigEndian.PutUint32(mac[0:4], + binary.BigEndian.Uint32(mac[0:4])^binary.BigEndian.Uint32(p.macInputBuffer[6:10])) mac[4] ^= p.macInputBuffer[10] mac[5] ^= p.macInputBuffer[11] return nil @@ -438,7 +465,8 @@ func (p *scionPacketProcessor) processHbirdEgress() error { // need to update the SegID. if p.infoField.ConsDir && !p.peering { p.infoField.UpdateSegID(p.hopField.Mac) - if err := p.hbirdPath.SetInfoField(p.infoField, int(p.hbirdPath.PathMeta.CurrINF)); err != nil { + err := p.hbirdPath.SetInfoField(p.infoField, int(p.hbirdPath.PathMeta.CurrINF)) + if err != nil { // TODO parameter problem invalid path return serrors.WrapStr("update info field", err) } @@ -525,7 +553,8 @@ func (p *scionPacketProcessor) processHBIRD() (processResult, error) { } // verify the new block if p.flyoverField.Flyover { - // TODO: can possibly skip this once we modify flyover at Xover implementation. Will need to aggregate new MAC though + // TODO: can possibly skip this once we modify flyover at Xover implementation. + // Will need to aggregate new MAC though if r, err := p.verifyCurrentHbirdMAC(); err != nil { return r, err } @@ -614,7 +643,8 @@ func (p *slowPathPacketProcessor) prepareHbirdSCMP( } revPath := revPathTmp.(*hummingbird.Decoded) - peering, err := determinePeerHbird(revPath.PathMeta, revPath.InfoFields[revPath.PathMeta.CurrINF]) + peering, err := determinePeerHbird(revPath.PathMeta, + revPath.InfoFields[revPath.PathMeta.CurrINF]) if err != nil { return nil, serrors.Wrap(cannotRoute, err, "details", "peering cannot be determined") } From a36e0be1554587a8a7b40eac2dba0b66ccfe1f99 Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Thu, 9 Nov 2023 16:48:25 +0100 Subject: [PATCH 028/100] making RemoveFlyovers() private in decoded.co, replacing some integers by the HopLines constant --- pkg/slayers/path/hummingbird/decoded.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/pkg/slayers/path/hummingbird/decoded.go b/pkg/slayers/path/hummingbird/decoded.go index 2982dccb52..55afe9a23d 100644 --- a/pkg/slayers/path/hummingbird/decoded.go +++ b/pkg/slayers/path/hummingbird/decoded.go @@ -31,8 +31,6 @@ func (s *Decoded) DecodeFromBytes(data []byte) error { if err := s.Base.DecodeFromBytes(data); err != nil { return err } - // fmt.Printf("s: %v\n", s) - // fmt.Print(s.Len()) if minLen := s.Len(); len(data) < minLen { return serrors.New("DecodedPath raw too short", "expected", minLen, "actual", len(data)) } @@ -47,7 +45,7 @@ func (s *Decoded) DecodeFromBytes(data []byte) error { } // Allocate maximum number of possible hopfields based on length - s.HopFields = make([]FlyoverHopField, s.NumLines/3) + s.HopFields = make([]FlyoverHopField, s.NumLines/HopLines) i, j := 0, 0 // If last hop is not a flyover hop, decode it with only 12 bytes slice for ; j < s.NumLines-HopLines; i++ { @@ -130,7 +128,7 @@ func (s *Decoded) Reverse() (path.Path, error) { return nil, serrors.New("empty decoded path is invalid and cannot be reversed") } - if err := s.RemoveFlyovers(); err != nil { + if err := s.removeFlyovers(); err != nil { return nil, err } // Reverse order of InfoFields and SegLens @@ -149,14 +147,15 @@ func (s *Decoded) Reverse() (path.Path, error) { } // Update CurrINF and CurrHF and SegLens s.PathMeta.CurrINF = uint8(s.NumINF) - s.PathMeta.CurrINF - 1 - s.PathMeta.CurrHF = uint8(s.NumLines) - s.PathMeta.CurrHF - 3 + s.PathMeta.CurrHF = uint8(s.NumLines) - s.PathMeta.CurrHF - HopLines return s, nil } // RemoveFlyovers removes all reservations from a decoded path // Corrects SegLen and CurrHF accordingly -func (s *Decoded) RemoveFlyovers() error { +// Does not affect MACs +func (s *Decoded) removeFlyovers() error { var idxInf uint8 = 0 var offset uint8 = 0 var segCount uint8 = 0 @@ -174,7 +173,7 @@ func (s *Decoded) RemoveFlyovers() error { s.Base.NumLines -= 2 s.PathMeta.SegLen[idxInf] -= 2 } - segCount += 3 + segCount += HopLines if s.PathMeta.SegLen[idxInf] == segCount { segCount = 0 idxInf += 1 From a96eaa0515a719a576ae7bc6a752a53f93b9064b Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Thu, 9 Nov 2023 16:53:43 +0100 Subject: [PATCH 029/100] fixed isXover() error introduced two commits ago... --- pkg/slayers/path/hummingbird/base.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/slayers/path/hummingbird/base.go b/pkg/slayers/path/hummingbird/base.go index 01fe672b89..d1f80d5447 100644 --- a/pkg/slayers/path/hummingbird/base.go +++ b/pkg/slayers/path/hummingbird/base.go @@ -71,8 +71,8 @@ func (s *Base) IncPath(n int) error { // IsXover returns whether we are at a crossover point. func (s *Base) IsXover() bool { return s.PathMeta.CurrHF+FlyoverLines < uint8(s.NumLines) && - s.PathMeta.CurrINF != s.InfIndexForHF(s.PathMeta.CurrHF+HopLines) || - s.PathMeta.CurrINF != s.InfIndexForHF(s.PathMeta.CurrHF+FlyoverLines) + (s.PathMeta.CurrINF != s.InfIndexForHF(s.PathMeta.CurrHF+HopLines) || + s.PathMeta.CurrINF != s.InfIndexForHF(s.PathMeta.CurrHF+FlyoverLines)) } From fdd1900dc4e54e691a0964f4185d9c3fa35d7e09 Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Thu, 9 Nov 2023 16:58:42 +0100 Subject: [PATCH 030/100] added additional UT for flyoverhopfield testing non-flyover hops --- .../path/hummingbird/flyoverhopfield_test.go | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/pkg/slayers/path/hummingbird/flyoverhopfield_test.go b/pkg/slayers/path/hummingbird/flyoverhopfield_test.go index 165864a8ca..33824ded42 100644 --- a/pkg/slayers/path/hummingbird/flyoverhopfield_test.go +++ b/pkg/slayers/path/hummingbird/flyoverhopfield_test.go @@ -8,7 +8,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestFlyoverHopSerializeDecode(t *testing.T) { +func TestFlyoverHopSerializeDecodeFlyover(t *testing.T) { want := &hummingbird.FlyoverHopField{ HopField: path.HopField{ IngressRouterAlert: true, @@ -31,3 +31,27 @@ func TestFlyoverHopSerializeDecode(t *testing.T) { assert.NoError(t, got.DecodeFromBytes(b)) assert.Equal(t, want, got) } + +func TestFlyoverHopSerializeDecode(t *testing.T) { + want := &hummingbird.FlyoverHopField{ + HopField: path.HopField{ + IngressRouterAlert: true, + EgressRouterAlert: false, + ExpTime: 63, + ConsIngress: 1, + ConsEgress: 0, + Mac: [path.MacLen]byte{1, 2, 3, 4, 5, 6}, + }, + Flyover: false, + ResID: 0, + Bw: 0, + ResStartTime: 0, + Duration: 0, + } + b := make([]byte, hummingbird.FlyoverLen) + assert.NoError(t, want.SerializeTo(b)) + + got := &hummingbird.FlyoverHopField{} + assert.NoError(t, got.DecodeFromBytes(b)) + assert.Equal(t, want, got) +} From 5bf19dab398d8db6d1effb147b918cb2e7be9a8c Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Thu, 9 Nov 2023 17:04:48 +0100 Subject: [PATCH 031/100] changed bounds check to be more efficient --- pkg/slayers/path/hummingbird/mac.go | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/pkg/slayers/path/hummingbird/mac.go b/pkg/slayers/path/hummingbird/mac.go index b394a27947..d68c362215 100644 --- a/pkg/slayers/path/hummingbird/mac.go +++ b/pkg/slayers/path/hummingbird/mac.go @@ -34,9 +34,8 @@ var ZeroBlock [aes.BlockSize]byte func DeriveAuthKey(block cipher.Block, resId uint32, bw, in, eg uint16, startTime uint32, resDuration uint16, buffer []byte) []byte { - if len(buffer) < AkBufferSize { - buffer = make([]byte, AkBufferSize) - } + _ = buffer[AkBufferSize-1] + //prepare input binary.BigEndian.PutUint16(buffer[0:2], in) binary.BigEndian.PutUint16(buffer[2:4], eg) @@ -54,12 +53,9 @@ func DeriveAuthKey(block cipher.Block, resId uint32, bw, in, eg uint16, // Needs a xkbuffer of 44 uint32s to store the expanded keys for aes func FullFlyoverMac(ak []byte, dstIA addr.IA, pktlen uint16, resStartTime uint16, highResTime uint32, buffer []byte, xkbuffer []uint32) []byte { - if len(buffer) < FlyoverMacBufferSize { - buffer = make([]byte, FlyoverMacBufferSize) - } - if len(xkbuffer) < XkBufferSize { - xkbuffer = make([]uint32, XkBufferSize) - } + + _ = buffer[FlyoverMacBufferSize-1] + _ = xkbuffer[XkBufferSize-1] binary.BigEndian.PutUint64(buffer[0:8], uint64(dstIA)) binary.BigEndian.PutUint16(buffer[8:10], pktlen) From bcd1ea910af40244908fcba2557de76178062930 Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Thu, 9 Nov 2023 17:17:08 +0100 Subject: [PATCH 032/100] fixed tests that were relying on mac computation to provide buffer if not available --- pkg/slayers/path/hummingbird/mac.go | 5 ++++- router/dataplane_hbird_test.go | 23 +++++++++++++++++------ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/pkg/slayers/path/hummingbird/mac.go b/pkg/slayers/path/hummingbird/mac.go index d68c362215..58f9e51b36 100644 --- a/pkg/slayers/path/hummingbird/mac.go +++ b/pkg/slayers/path/hummingbird/mac.go @@ -31,6 +31,7 @@ var ZeroBlock [aes.BlockSize]byte // Derive authentication key A_k // block is expected to be initialized beforehand with aes.NewCipher(sv), // where sv is this AS' secret value +// Requires buffer to be of size at least AkBufferSize func DeriveAuthKey(block cipher.Block, resId uint32, bw, in, eg uint16, startTime uint32, resDuration uint16, buffer []byte) []byte { @@ -50,7 +51,9 @@ func DeriveAuthKey(block cipher.Block, resId uint32, bw, in, eg uint16, } // Computes full flyover mac vk -// Needs a xkbuffer of 44 uint32s to store the expanded keys for aes +// Requires buffer to be of size at least FlyoverMacBufferSize +// Requires xkbuffer to be of size at least XkBufferSize. +// It is used to store the aes expanded keys func FullFlyoverMac(ak []byte, dstIA addr.IA, pktlen uint16, resStartTime uint16, highResTime uint32, buffer []byte, xkbuffer []uint32) []byte { diff --git a/router/dataplane_hbird_test.go b/router/dataplane_hbird_test.go index 80bf8c7d4f..657ce1c1bd 100644 --- a/router/dataplane_hbird_test.go +++ b/router/dataplane_hbird_test.go @@ -3569,11 +3569,14 @@ func computeAggregateMac(t *testing.T, key, sv []byte, dst addr.IA, l uint16, in if !info.ConsDir { ingress, egress = egress, ingress } + akBuffer := make([]byte, hummingbird.AkBufferSize) + macBuffer := make([]byte, hummingbird.FlyoverMacBufferSize) + xkBuffer := make([]uint32, hummingbird.XkBufferSize) ak := hummingbird.DeriveAuthKey(block, hf.ResID, hf.Bw, ingress, egress, - meta.BaseTS-uint32(hf.ResStartTime), hf.Duration, nil) + meta.BaseTS-uint32(hf.ResStartTime), hf.Duration, akBuffer) flyoverMac := hummingbird.FullFlyoverMac(ak, dst, l, hf.ResStartTime, - meta.HighResTS, nil, nil) + meta.HighResTS, macBuffer, xkBuffer) for i, b := range scionMac { scionMac[i] = b ^ flyoverMac[i] @@ -3592,10 +3595,14 @@ func computeAggregateMacXover(t *testing.T, key, sv []byte, dst addr.IA, l, hin, ingress, egress = egress, ingress } + akBuffer := make([]byte, hummingbird.AkBufferSize) + macBuffer := make([]byte, hummingbird.FlyoverMacBufferSize) + xkBuffer := make([]uint32, hummingbird.XkBufferSize) + ak := hummingbird.DeriveAuthKey(block, hf.ResID, hf.Bw, ingress, egress, - meta.BaseTS-uint32(hf.ResStartTime), hf.Duration, nil) + meta.BaseTS-uint32(hf.ResStartTime), hf.Duration, akBuffer) flyoverMac := hummingbird.FullFlyoverMac(ak, dst, l, hf.ResStartTime, - meta.HighResTS, nil, nil) + meta.HighResTS, macBuffer, xkBuffer) for i, b := range scionMac { scionMac[i] = b ^ flyoverMac[i] @@ -3609,10 +3616,14 @@ func aggregateOntoScionMac(t *testing.T, sv []byte, dst addr.IA, l, hin, heg uin require.NoError(t, err) ingress, egress := hin, heg + akBuffer := make([]byte, hummingbird.AkBufferSize) + macBuffer := make([]byte, hummingbird.FlyoverMacBufferSize) + xkBuffer := make([]uint32, hummingbird.XkBufferSize) + ak := hummingbird.DeriveAuthKey(block, hf.ResID, hf.Bw, ingress, egress, - meta.BaseTS-uint32(hf.ResStartTime), hf.Duration, nil) + meta.BaseTS-uint32(hf.ResStartTime), hf.Duration, akBuffer) flyoverMac := hummingbird.FullFlyoverMac(ak, dst, l, hf.ResStartTime, - meta.HighResTS, nil, nil) + meta.HighResTS, macBuffer, xkBuffer) for i := range hf.HopField.Mac { hf.HopField.Mac[i] ^= flyoverMac[i] From c5e167832330960bbdab47ab1a81c4f51198ca5a Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Thu, 9 Nov 2023 17:18:21 +0100 Subject: [PATCH 033/100] made replaceMac private --- pkg/slayers/path/hummingbird/raw.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/slayers/path/hummingbird/raw.go b/pkg/slayers/path/hummingbird/raw.go index 84edfebe0c..b9dfd7029b 100644 --- a/pkg/slayers/path/hummingbird/raw.go +++ b/pkg/slayers/path/hummingbird/raw.go @@ -159,7 +159,7 @@ func (s *Raw) GetCurrentHopField() (FlyoverHopField, error) { return s.GetHopField(int(s.PathMeta.CurrHF)) } -func (s *Raw) ReplacMac(idx int, mac []byte) error { +func (s *Raw) replacMac(idx int, mac []byte) error { if idx >= s.NumLines-HopLines+1 { return serrors.New("HopField index out of bounds", "max", s.NumLines-HopLines, "actual", idx) @@ -174,7 +174,7 @@ func (s *Raw) ReplacMac(idx int, mac []byte) error { // SetCurrentMac replaces the Mac of the current hopfield by a new mac func (s *Raw) ReplaceCurrentMac(mac []byte) error { - return s.ReplacMac(int(s.PathMeta.CurrHF), mac) + return s.replacMac(int(s.PathMeta.CurrHF), mac) } // Returns a slice of the MAC of the hopfield starting at index idx From 904daf0ed4229e0e7b2b57981b58a78329391cb1 Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Sat, 11 Nov 2023 15:12:10 +0100 Subject: [PATCH 034/100] improved client library, split process of putting reservations on a path into multiple steps --- pkg/hummingbird/hummingbird.go | 203 +++++++++++++++++++++++---------- tools/end2end_hbird/main.go | 20 +++- 2 files changed, 159 insertions(+), 64 deletions(-) diff --git a/pkg/hummingbird/hummingbird.go b/pkg/hummingbird/hummingbird.go index e257db4d2a..9ca0a341d5 100644 --- a/pkg/hummingbird/hummingbird.go +++ b/pkg/hummingbird/hummingbird.go @@ -130,8 +130,8 @@ type HummingbirdClient struct { // counter for duplicate detection counter uint32 // buffers for computing Vk - byteBuffer [16]byte - xkbuffer [44]uint32 + byteBuffer [hummingbird.FlyoverMacBufferSize]byte + xkbuffer [hummingbird.XkBufferSize]uint32 } func (c *HummingbirdClient) parseIAs(ifs []snet.PathInterface) error { @@ -197,44 +197,25 @@ func (c *HummingbirdClient) PrepareHbirdPath(p snet.Path) error { log.Debug("path ASes", "ASes", c.ases) // prepare reservations data structure c.reservations = make([]Reservation, len(c.dec.HopFields)) - return nil -} - -func (c *HummingbirdClient) RequestReservationsAllHops( - bw uint16, start uint32, duration uint16) error { - return c.RequestReservationForASes(c.ases, bw, start, duration) -} - -// Returns a copy of all ASes on the current path in order -func (c *HummingbirdClient) GetPathASes() []addr.IA { - ascopy := make([]addr.IA, len(c.ases)) - copy(ascopy, c.ases) - return ascopy -} - -// Requests new reservations for this path for the listed ASes -// Expects them to be in order without duplicates -func (c *HummingbirdClient) RequestReservationForASes( - asin []addr.IA, bw uint16, start uint32, duration uint16) error { - j := 0 - for i := range c.dec.HopFields { - if j >= len(asin) || asin[j] != c.ases[i] { - continue - } - + // Initialize expected AS, ingress and egress for each reservation + // keep all fields 0 for second crossover hop where no reservation in allowed + for i, hop := range c.dec.HopFields { var infIdx int - var firstHopAfterXover, lastHopBeforeXover bool + var xover bool if i < int(c.dec.FirstHopPerSeg[0]) { infIdx = 0 if !c.dec.InfoFields[0].Peer { - lastHopBeforeXover = (i == int(c.dec.FirstHopPerSeg[0])-1) && + xover = (i == int(c.dec.FirstHopPerSeg[0])-1) && i < len(c.dec.HopFields)-1 } } else if i < int(c.dec.FirstHopPerSeg[1]) { infIdx = 1 if !c.dec.InfoFields[1].Peer { - firstHopAfterXover = i == int(c.dec.FirstHopPerSeg[0]) - lastHopBeforeXover = i == int(c.dec.FirstHopPerSeg[1])-1 && + if i == int(c.dec.FirstHopPerSeg[0]) { + // First hop after Crossover, nothing to be done + continue + } + xover = i == int(c.dec.FirstHopPerSeg[1])-1 && i < len(c.dec.HopFields)-1 } } else { @@ -242,52 +223,96 @@ func (c *HummingbirdClient) RequestReservationForASes( if c.dec.InfoFields[2].Peer { return serrors.New("Invalid path, cannot have 3 segments on peering path") } - firstHopAfterXover = i == int(c.dec.FirstHopPerSeg[1]) && i < len(c.dec.HopFields)-1 - } - // Do not add a reservation to second hop after crossover - if firstHopAfterXover { - continue + if i == int(c.dec.FirstHopPerSeg[1]) && i < len(c.dec.HopFields)-1 { + // First hop after Crossover, nothing to be done + continue + } } - + // set AS c.reservations[i].AS = c.ases[i] - c.reservations[i].Bw = bw - c.reservations[i].StartTime = start - c.reservations[i].Duration = duration - // Set Ingress and Egress interfaces of reservation - // If crossover, need to take next/previous hop into account - if lastHopBeforeXover { + // Set ingress/egress + if xover { if c.dec.InfoFields[infIdx].ConsDir { - c.reservations[i].Ingress = c.dec.HopFields[i].HopField.ConsIngress + c.reservations[i].Ingress = hop.HopField.ConsIngress } else { - c.reservations[i].Ingress = c.dec.HopFields[i].HopField.ConsEgress + c.reservations[i].Ingress = hop.HopField.ConsEgress } if c.dec.InfoFields[infIdx+1].ConsDir { c.reservations[i].Egress = c.dec.HopFields[i+1].HopField.ConsEgress } else { c.reservations[i].Egress = c.dec.HopFields[i+1].HopField.ConsIngress } - } else if c.dec.InfoFields[infIdx].ConsDir { - c.reservations[i].Ingress = c.dec.HopFields[i].HopField.ConsIngress - c.reservations[i].Egress = c.dec.HopFields[i].HopField.ConsEgress } else { - c.reservations[i].Ingress = c.dec.HopFields[i].HopField.ConsEgress - c.reservations[i].Egress = c.dec.HopFields[i].HopField.ConsIngress + if c.dec.InfoFields[infIdx].ConsDir { + c.reservations[i].Ingress = hop.HopField.ConsIngress + c.reservations[i].Egress = hop.HopField.ConsEgress + } else { + c.reservations[i].Ingress = hop.HopField.ConsEgress + c.reservations[i].Egress = hop.HopField.ConsIngress + } } + } + return nil +} + +// Returns a copy of all ASes on the current path in order without duplicates +func (c *HummingbirdClient) GetPathASes() []addr.IA { + ascopy := make([]addr.IA, len(c.ases)) + j := 0 + for i := range c.ases { + if i == 0 || c.ases[i] != c.ases[i-1] { + ascopy[j] = c.ases[i] + j++ + } + } + return ascopy[:j] +} + +func (c *HummingbirdClient) RequestReservationsAllHops( + bw uint16, start uint32, duration uint16) error { + ascopy := make([]addr.IA, len(c.ases)) + j := 0 + for i := range c.ases { + if i == 0 || c.ases[i] != c.ases[i-1] { + ascopy[j] = c.ases[i] + j++ + } + } + return c.RequestReservationForASes(ascopy[:j], bw, start, duration) +} + +// Requests new reservations for this path for the listed ASes +// Expects them to be in order without duplicates +func (c *HummingbirdClient) RequestReservationForASes( + asin []addr.IA, bw uint16, start uint32, duration uint16) error { + log.Debug("Requesting reservations for", "AS", asin) + j := 0 + for i := range c.dec.HopFields { + if j >= len(asin) { + break + } + if asin[j] != c.ases[i] { + continue + } + + if (i == int(c.dec.FirstHopPerSeg[0]) && !c.dec.InfoFields[1].Peer) || + (i == int(c.dec.FirstHopPerSeg[1])) { + // Ignore the second hops after crossover + continue + } + + // TODO: request reservations + // Current implementation cheats by writing data into c.reservations instead + //c.reservations[i].AS = c.ases[i] + c.reservations[i].Bw = bw + c.reservations[i].StartTime = start + c.reservations[i].Duration = duration var err error c.reservations[i], err = cheat_auth_key(&c.reservations[i]) if err != nil { return err } - // set flyover - c.dec.HopFields[i].Flyover = true - c.dec.NumLines += 2 - c.dec.PathMeta.SegLen[infIdx] += 2 - // set other fields - c.dec.HopFields[i].Bw = c.reservations[i].Bw - c.dec.HopFields[i].Duration = c.reservations[i].Duration - c.dec.HopFields[i].ResID = c.reservations[i].ResID - j++ } @@ -299,15 +324,67 @@ func GetAllReservations() ([]Reservation, error) { return nil, serrors.New("Not Implemented") } -// Returns all reservations in the database that are applicable to the current path -func (c *HummingbirdClient) GetAllReservations() ([]Reservation, error) { +// Returns all reservations in the database that can be used for the current path +func (c *HummingbirdClient) GetAvailableReservations() ([]Reservation, error) { // Return all reservations in the data base - return nil, serrors.New("Not Implemented") + //TODO: return all reservations in the db that fit the path (AS, ingress egress) + + // Unti we have access to the daemon db, we cheat and return the data written into c.reservations in request + res := make([]Reservation, len(c.reservations)) + j := 0 + for _, r := range c.reservations { + if r.Bw != 0 { + res[j] = r + j++ + } + } + log.Debug("getting available reservations", "reservations", res) + return res[:j], nil } // Adds the listed reservations to the path +// Expects them to be in order func (c *HummingbirdClient) ApplyReservations(res []Reservation) error { - return serrors.New("Not Implemented") + log.Debug("Applying reservations", "reservations", res) + for i, j := 0, 0; i < len(c.reservations) && j < len(res); i++ { + if res[j].AS == c.reservations[i].AS { + if res[j].Ingress == c.reservations[i].Ingress && res[j].Egress == c.reservations[i].Egress { + //TODO: handle case of multiple reservations for one hop (different validiy windows) + c.reservations[i] = res[j] + c.dec.HopFields[i].Flyover = true + c.dec.NumLines += 2 + // increment corresponding segLen + switch true { + case i < int(c.dec.FirstHopPerSeg[0]): + c.dec.PathMeta.SegLen[0] += 2 + case i < int(c.dec.FirstHopPerSeg[1]): + c.dec.PathMeta.SegLen[1] += 2 + default: + c.dec.PathMeta.SegLen[2] += 2 + } + // Set other fields + c.dec.HopFields[i].Bw = res[j].Bw + c.dec.HopFields[i].Duration = res[j].Duration + c.dec.HopFields[i].ResID = res[j].ResID + } + //TODO: else inform user at the end that this reservations does not fit he path + + j++ + } + } + return nil +} + +// Checks whether any current reservation has arrived at expiration +// If yes, disable reservation +// TODO: add mechanism to automatically replace it +func (c *HummingbirdClient) checkExpiry() { + now := uint32(time.Now().Unix()) + for i := range c.reservations { + if c.reservations[i].EndTime < now { + c.dec.HopFields[i].Flyover = false + } + } } // Sets pathmeta timestamps and increments duplicate detection counter. diff --git a/tools/end2end_hbird/main.go b/tools/end2end_hbird/main.go index d11763007a..ac66529476 100644 --- a/tools/end2end_hbird/main.go +++ b/tools/end2end_hbird/main.go @@ -323,7 +323,15 @@ func (c *client) attemptRequest(n int) bool { logger.Error("Error requesting reservations", "err", err) return false } - // TODO: add step to receive reservation??? + + res, err := hbirdClient.GetAvailableReservations() + if err != nil { + logger.Error("Error getting available reservations", "err", err) + } + + if err := hbirdClient.ApplyReservations(res); err != nil { + logger.Error("Error applying reservations", "err", err) + } path, err = hbirdClient.FinalizePath(path, pingPayloadLen) if err != nil { @@ -349,6 +357,16 @@ func (c *client) attemptRequest(n int) bool { logger.Error("Error requesting reservations", "err", err) return false } + + res, err := hbirdClient.GetAvailableReservations() + if err != nil { + logger.Error("Error getting available reservations", "err", err) + } + + if err := hbirdClient.ApplyReservations(res); err != nil { + logger.Error("Error applying reservations", "err", err) + } + path, err = hbirdClient.FinalizePath(path, pingPayloadLen) if err != nil { logger.Error("Error assembling hummingbird path", "err", err) From eb1b2e63c7ba97c08e48f8f9f6f5e2a31a2e9405 Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Sun, 12 Nov 2023 17:33:57 +0100 Subject: [PATCH 035/100] rewrote data structures used in client library to improve readability of code --- pkg/hummingbird/hummingbird.go | 298 ++++++++++++++++----------------- tools/end2end_hbird/main.go | 20 +-- 2 files changed, 149 insertions(+), 169 deletions(-) diff --git a/pkg/hummingbird/hummingbird.go b/pkg/hummingbird/hummingbird.go index 9ca0a341d5..6de19f3002 100644 --- a/pkg/hummingbird/hummingbird.go +++ b/pkg/hummingbird/hummingbird.go @@ -40,6 +40,30 @@ type Reservation struct { Egress uint16 } +// Describes a pair of Ingress and Egress interfaces in a specific AS +type Hop struct { + AS addr.IA + Ingress uint16 + Egress uint16 +} + +type hbirdHop struct { + // The FlyoverHopField in the path associated to this hop + hopfield *hummingbird.FlyoverHopField + // The Index of the Segment the aboe hopfield is part of + infIdx int + // The AS this hop traverses + as addr.IA + // The ingress used by packets traversing this hop + ingress uint16 + // The egress used by packets traversing this hop + egress uint16 + // The reservations that can be used for this hop + reservations []Reservation + // The original scion mac of the corresponding hopfield + scionMac [6]byte +} + // Temporary cheating function until the system to request keys is available // return true if successful func cheat_auth_key(res *Reservation) (Reservation, error) { @@ -121,12 +145,8 @@ type HummingbirdClient struct { dec hummingbird.Decoded // Destination of the path dest addr.IA - // caches the list of ASes on path - ases []addr.IA - // Cached scion MACs for each hop - macs [][6]byte - //TODO: replace by db - reservations []Reservation + // The hops for which it is possible to add reservations + hops []hbirdHop // counter for duplicate detection counter uint32 // buffers for computing Vk @@ -135,37 +155,29 @@ type HummingbirdClient struct { } func (c *HummingbirdClient) parseIAs(ifs []snet.PathInterface) error { - c.ases = make([]addr.IA, len(c.dec.HopFields)) - c.ases[0] = ifs[0].IA + c.hops[0].as = ifs[0].IA i, j := 1, 1 - for i < len(c.ases) && j < len(ifs) { - - switch true { - // First hop after Crossover always has same as as previous hop - case (i == int(c.dec.FirstHopPerSeg[0]) && !c.dec.InfoFields[1].Peer) || - (i == int(c.dec.FirstHopPerSeg[1])): - c.ases[i] = c.ases[i-1] - i++ - // Skip duplicates interfaces. - // Only duplicates we want are those for Xovers, and we already add these manually above - case ifs[j].IA == ifs[j-1].IA: + for i < len(c.hops) && j < len(ifs) { + if ifs[j].IA == ifs[j-1].IA { + // Ignore duplicates j++ - default: - c.ases[i] = ifs[j].IA + } else { + c.hops[i].as = ifs[j].IA i++ j++ } - } - if i < len(c.ases)-1 { + if i < len(c.hops) { return serrors.New("Not enough ASes for this path") } return nil } -func (c *HummingbirdClient) PrepareHbirdPath(p snet.Path) error { +// Prepares as hummingbird path and initializes the fields of the hummingbirdClient struct +// Returns an array of Hops containing the AS, Ingress and Egress of each hop on the path +func (c *HummingbirdClient) PrepareHbirdPath(p snet.Path) ([]Hop, error) { if p == nil { - return serrors.New("Empty path") + return nil, serrors.New("Empty path") } c.dec = hummingbird.Decoded{} switch v := p.Dataplane().(type) { @@ -173,35 +185,22 @@ func (c *HummingbirdClient) PrepareHbirdPath(p snet.Path) error { // Convert path to decoded hbird path scionDec := scion.Decoded{} if err := scionDec.DecodeFromBytes(v.Raw); err != nil { - return serrors.Join(err, serrors.New("Failed to Prepare Hummingbird Path")) + return nil, serrors.Join(err, serrors.New("Failed to Prepare Hummingbird Path")) } c.dec.ConvertFromScionDecoded(scionDec) case snetpath.Hummingbird: if err := c.dec.DecodeFromBytes(v.Raw); err != nil { - return serrors.Join(err, serrors.New("Failed to Prepare Hummingbird Path")) + return nil, serrors.Join(err, serrors.New("Failed to Prepare Hummingbird Path")) } default: - return serrors.New("Unsupported path type") - } - log.Debug("parsing AS") - // Parse the list of ASes on path - if err := c.parseIAs(p.Metadata().Interfaces); err != nil { - return serrors.Join(err, serrors.New("Malformed path")) + return nil, serrors.New("Unsupported path type") } - c.dest = c.ases[len(c.ases)-1] - // cache Scion Hopfield macs - c.macs = make([][6]byte, len(c.dec.HopFields)) - for i, hop := range c.dec.HopFields { - copy(c.macs[i][:], hop.HopField.Mac[:]) - } - log.Debug("path ASes", "ASes", c.ases) - // prepare reservations data structure - c.reservations = make([]Reservation, len(c.dec.HopFields)) - // Initialize expected AS, ingress and egress for each reservation - // keep all fields 0 for second crossover hop where no reservation in allowed - for i, hop := range c.dec.HopFields { - var infIdx int + // Initialize a hop for each traversed as + c.hops = make([]hbirdHop, len(c.dec.HopFields)) + j := 0 + for i := 0; i < len(c.dec.HopFields); i++ { var xover bool + var infIdx int if i < int(c.dec.FirstHopPerSeg[0]) { infIdx = 0 if !c.dec.InfoFields[0].Peer { @@ -221,102 +220,109 @@ func (c *HummingbirdClient) PrepareHbirdPath(p snet.Path) error { } else { infIdx = 2 if c.dec.InfoFields[2].Peer { - return serrors.New("Invalid path, cannot have 3 segments on peering path") + return nil, serrors.New("Invalid path, cannot have 3 segments on peering path") } if i == int(c.dec.FirstHopPerSeg[1]) && i < len(c.dec.HopFields)-1 { // First hop after Crossover, nothing to be done continue } } - // set AS - c.reservations[i].AS = c.ases[i] + c.hops[j].infIdx = infIdx + c.hops[j].hopfield = &c.dec.HopFields[i] // Set ingress/egress if xover { if c.dec.InfoFields[infIdx].ConsDir { - c.reservations[i].Ingress = hop.HopField.ConsIngress + c.hops[j].ingress = c.dec.HopFields[i].HopField.ConsIngress } else { - c.reservations[i].Ingress = hop.HopField.ConsEgress + c.hops[j].ingress = c.dec.HopFields[i].HopField.ConsEgress } if c.dec.InfoFields[infIdx+1].ConsDir { - c.reservations[i].Egress = c.dec.HopFields[i+1].HopField.ConsEgress + c.hops[j].egress = c.dec.HopFields[i+1].HopField.ConsEgress } else { - c.reservations[i].Egress = c.dec.HopFields[i+1].HopField.ConsIngress + c.hops[j].egress = c.dec.HopFields[i+1].HopField.ConsIngress } } else { if c.dec.InfoFields[infIdx].ConsDir { - c.reservations[i].Ingress = hop.HopField.ConsIngress - c.reservations[i].Egress = hop.HopField.ConsEgress + c.hops[j].ingress = c.dec.HopFields[i].HopField.ConsIngress + c.hops[j].egress = c.dec.HopFields[i].HopField.ConsEgress } else { - c.reservations[i].Ingress = hop.HopField.ConsEgress - c.reservations[i].Egress = hop.HopField.ConsIngress + c.hops[j].ingress = c.dec.HopFields[i].HopField.ConsEgress + c.hops[j].egress = c.dec.HopFields[i].HopField.ConsIngress } } + // cache scion mac + copy(c.hops[j].scionMac[:], c.hops[j].hopfield.HopField.Mac[:]) + // Initialiaze reservations + c.hops[j].reservations = make([]Reservation, 0, 2) + j++ } - return nil + c.hops = c.hops[:j] + // Parse the list of ASes on path + if err := c.parseIAs(p.Metadata().Interfaces); err != nil { + return nil, serrors.Join(err, serrors.New("Malformed path")) + } + c.dest = c.hops[len(c.hops)-1].as + + return c.GetPathASes(), nil } -// Returns a copy of all ASes on the current path in order without duplicates -func (c *HummingbirdClient) GetPathASes() []addr.IA { - ascopy := make([]addr.IA, len(c.ases)) - j := 0 - for i := range c.ases { - if i == 0 || c.ases[i] != c.ases[i-1] { - ascopy[j] = c.ases[i] - j++ - } +// For each hop in the path, returns a reservation containing the AS, Ingress and Egress of that hop +func (c *HummingbirdClient) GetPathASes() []Hop { + hops := make([]Hop, len(c.hops)) + for i, h := range c.hops { + hops[i].AS = h.as + hops[i].Ingress = h.ingress + hops[i].Egress = h.egress } - return ascopy[:j] + return hops } +// Request reservations for the full path +// bw: the bandwidth to request +// start: The start time of the reservation, in unix seconds +// duration: The duration of the reservation in seconds +// TODO: add async version once we have request api func (c *HummingbirdClient) RequestReservationsAllHops( - bw uint16, start uint32, duration uint16) error { - ascopy := make([]addr.IA, len(c.ases)) - j := 0 - for i := range c.ases { - if i == 0 || c.ases[i] != c.ases[i-1] { - ascopy[j] = c.ases[i] - j++ - } + bw uint16, start uint32, duration uint16) ([]Reservation, error) { + hops := make([]Hop, len(c.hops)) + for i, h := range c.hops { + hops[i].AS = h.as + hops[i].Ingress = h.ingress + hops[i].Egress = h.egress } - return c.RequestReservationForASes(ascopy[:j], bw, start, duration) + + return RequestReservationForASes(hops[:], bw, start, duration) } -// Requests new reservations for this path for the listed ASes -// Expects them to be in order without duplicates -func (c *HummingbirdClient) RequestReservationForASes( - asin []addr.IA, bw uint16, start uint32, duration uint16) error { - log.Debug("Requesting reservations for", "AS", asin) - j := 0 - for i := range c.dec.HopFields { - if j >= len(asin) { - break - } - if asin[j] != c.ases[i] { - continue - } +// Requests new reservations for the listed Hops and returns them once they are obtained +// TODO: add timeout after which already received reservations (if any) are returned once we have actual requests +// TODO: add fully async version of this +func RequestReservationForASes( + hops []Hop, bw uint16, start uint32, duration uint16) ([]Reservation, error) { - if (i == int(c.dec.FirstHopPerSeg[0]) && !c.dec.InfoFields[1].Peer) || - (i == int(c.dec.FirstHopPerSeg[1])) { - // Ignore the second hops after crossover - continue - } + log.Debug("Requesting reservations for", "Hops", hops) + reservations := make([]Reservation, len(hops)) + for i, h := range hops { + //TODO: Once we have API for requests + // Request (AS, ingress, egress, bw, start, duration) - // TODO: request reservations - // Current implementation cheats by writing data into c.reservations instead - //c.reservations[i].AS = c.ases[i] - c.reservations[i].Bw = bw - c.reservations[i].StartTime = start - c.reservations[i].Duration = duration + // Temporary Cheating + // Current implementation cheats by writing data directly into c.hops instead + + reservations[i].AS = h.AS + reservations[i].Ingress = h.Ingress + reservations[i].Egress = h.Egress + reservations[i].Bw = bw + reservations[i].StartTime = start + reservations[i].Duration = duration var err error - c.reservations[i], err = cheat_auth_key(&c.reservations[i]) + reservations[i], err = cheat_auth_key(&reservations[i]) if err != nil { - return err + return nil, err } - j++ } - - return nil + return reservations, nil } // Return all Reservations present in the database @@ -329,47 +335,33 @@ func (c *HummingbirdClient) GetAvailableReservations() ([]Reservation, error) { // Return all reservations in the data base //TODO: return all reservations in the db that fit the path (AS, ingress egress) - // Unti we have access to the daemon db, we cheat and return the data written into c.reservations in request - res := make([]Reservation, len(c.reservations)) - j := 0 - for _, r := range c.reservations { - if r.Bw != 0 { - res[j] = r - j++ - } - } - log.Debug("getting available reservations", "reservations", res) - return res[:j], nil + //TODO: incorporate into integration test once we have async version of previous functions + + return nil, serrors.New("Not Implemented") } // Adds the listed reservations to the path -// Expects them to be in order func (c *HummingbirdClient) ApplyReservations(res []Reservation) error { log.Debug("Applying reservations", "reservations", res) - for i, j := 0, 0; i < len(c.reservations) && j < len(res); i++ { - if res[j].AS == c.reservations[i].AS { - if res[j].Ingress == c.reservations[i].Ingress && res[j].Egress == c.reservations[i].Egress { - //TODO: handle case of multiple reservations for one hop (different validiy windows) - c.reservations[i] = res[j] - c.dec.HopFields[i].Flyover = true - c.dec.NumLines += 2 - // increment corresponding segLen - switch true { - case i < int(c.dec.FirstHopPerSeg[0]): - c.dec.PathMeta.SegLen[0] += 2 - case i < int(c.dec.FirstHopPerSeg[1]): - c.dec.PathMeta.SegLen[1] += 2 - default: - c.dec.PathMeta.SegLen[2] += 2 + for _, r := range res { + for j, h := range c.hops { + if r.AS == h.as { + if r.Ingress == h.ingress && r.Egress == h.egress { + // TODO: If there are already reservations present, order by validity/bandwidth? + c.hops[j].reservations = append(c.hops[j].reservations, r) + + // TODO: Only modify flyoverhopfield if new reservation becomes primary reservation + c.hops[j].hopfield.Flyover = true + c.dec.NumLines += 2 + c.dec.PathMeta.SegLen[h.infIdx] += 2 + c.hops[j].hopfield.Bw = r.Bw + c.hops[j].hopfield.Duration = r.Duration + c.hops[j].hopfield.ResID = r.ResID + } else { + // TODO: inform caller that this reservation cannot be set on this path + break } - // Set other fields - c.dec.HopFields[i].Bw = res[j].Bw - c.dec.HopFields[i].Duration = res[j].Duration - c.dec.HopFields[i].ResID = res[j].ResID } - //TODO: else inform user at the end that this reservations does not fit he path - - j++ } } return nil @@ -379,12 +371,7 @@ func (c *HummingbirdClient) ApplyReservations(res []Reservation) error { // If yes, disable reservation // TODO: add mechanism to automatically replace it func (c *HummingbirdClient) checkExpiry() { - now := uint32(time.Now().Unix()) - for i := range c.reservations { - if c.reservations[i].EndTime < now { - c.dec.HopFields[i].Flyover = false - } - } + } // Sets pathmeta timestamps and increments duplicate detection counter. @@ -410,18 +397,19 @@ func (c *HummingbirdClient) FinalizePath(p snet.Path, pktLen uint16) (snet.Path, c.counter += 1 } // compute Macs for Flyovers - for i := range c.dec.HopFields { - if !c.dec.HopFields[i].Flyover { + for _, h := range c.hops { + if !h.hopfield.Flyover { continue } - res := c.reservations[i] - c.dec.HopFields[i].ResStartTime = uint16(secs - res.StartTime) + res := h.reservations[0] + h.hopfield.ResStartTime = uint16(secs - res.StartTime) flyovermac := hummingbird.FullFlyoverMac(res.Ak[:], c.dest, pktLen, - c.dec.HopFields[i].ResStartTime, millis, c.byteBuffer[:], c.xkbuffer[:]) - binary.BigEndian.PutUint32(c.dec.HopFields[i].HopField.Mac[:4], - binary.BigEndian.Uint32(flyovermac[:4])^binary.BigEndian.Uint32(c.macs[i][:4])) - binary.BigEndian.PutUint16(c.dec.HopFields[i].HopField.Mac[4:], - binary.BigEndian.Uint16(flyovermac[4:])^binary.BigEndian.Uint16(c.macs[i][4:])) + h.hopfield.ResStartTime, millis, c.byteBuffer[:], c.xkbuffer[:]) + + binary.BigEndian.PutUint32(h.hopfield.HopField.Mac[:4], + binary.BigEndian.Uint32(flyovermac[:4])^binary.BigEndian.Uint32(h.scionMac[:4])) + binary.BigEndian.PutUint16(h.hopfield.HopField.Mac[4:], + binary.BigEndian.Uint16(flyovermac[4:])^binary.BigEndian.Uint16(h.scionMac[4:])) } dphb.Raw = make([]byte, c.dec.Len()) if err := c.dec.SerializeTo(dphb.Raw); err != nil { diff --git a/tools/end2end_hbird/main.go b/tools/end2end_hbird/main.go index ac66529476..9d7975b642 100644 --- a/tools/end2end_hbird/main.go +++ b/tools/end2end_hbird/main.go @@ -314,21 +314,17 @@ func (c *client) attemptRequest(n int) bool { } else if !partial { // full path with reservations hbirdClient := hummingbird.HummingbirdClient{} - if err := hbirdClient.PrepareHbirdPath(path); err != nil { + if _, err := hbirdClient.PrepareHbirdPath(path); err != nil { logger.Error("Error converting path to Hummingbird", "err", err) return false } secs := uint32(time.Now().Unix()) - if err := hbirdClient.RequestReservationsAllHops(16, secs, 120); err != nil { + res, err := hbirdClient.RequestReservationsAllHops(16, secs, 120) + if err != nil { logger.Error("Error requesting reservations", "err", err) return false } - res, err := hbirdClient.GetAvailableReservations() - if err != nil { - logger.Error("Error getting available reservations", "err", err) - } - if err := hbirdClient.ApplyReservations(res); err != nil { logger.Error("Error applying reservations", "err", err) } @@ -341,7 +337,7 @@ func (c *client) attemptRequest(n int) bool { } else { //partial reservations, alternating resrved and not reserved hbirdClient := hummingbird.HummingbirdClient{} - if err := hbirdClient.PrepareHbirdPath(path); err != nil { + if _, err := hbirdClient.PrepareHbirdPath(path); err != nil { logger.Error("Error converting path to Hummingbird", "err", err) return false } @@ -353,16 +349,12 @@ func (c *client) attemptRequest(n int) bool { } ases = ases[:n] secs := uint32(time.Now().Unix()) - if err := hbirdClient.RequestReservationForASes(ases, 16, secs, 120); err != nil { + res, err := hummingbird.RequestReservationForASes(ases, 16, secs, 120) + if err != nil { logger.Error("Error requesting reservations", "err", err) return false } - res, err := hbirdClient.GetAvailableReservations() - if err != nil { - logger.Error("Error getting available reservations", "err", err) - } - if err := hbirdClient.ApplyReservations(res); err != nil { logger.Error("Error applying reservations", "err", err) } From 38961c9b7a902ae546e8618f607293c01830ee45 Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Mon, 13 Nov 2023 10:05:14 +0100 Subject: [PATCH 036/100] added additional methods to client library --- pkg/hummingbird/hummingbird.go | 113 +++++++++++++++++++++++++++++++-- 1 file changed, 108 insertions(+), 5 deletions(-) diff --git a/pkg/hummingbird/hummingbird.go b/pkg/hummingbird/hummingbird.go index 6de19f3002..c256c8d285 100644 --- a/pkg/hummingbird/hummingbird.go +++ b/pkg/hummingbird/hummingbird.go @@ -58,7 +58,9 @@ type hbirdHop struct { ingress uint16 // The egress used by packets traversing this hop egress uint16 - // The reservations that can be used for this hop + // The reservations that can be used for this hop. + // The reservation at index 0 is the one used to build the path + // MUST be non-empty if hopfield.Flyiver == true reservations []Reservation // The original scion mac of the corresponding hopfield scionMac [6]byte @@ -367,11 +369,112 @@ func (c *HummingbirdClient) ApplyReservations(res []Reservation) error { return nil } -// Checks whether any current reservation has arrived at expiration -// If yes, disable reservation -// TODO: add mechanism to automatically replace it -func (c *HummingbirdClient) checkExpiry() { +// Returns all the reservations that the client may currently use +// If multiple reservations per hop are present, the one currently used is the first appearing in the returned array +func (c *HummingbirdClient) GetUsedReservations() []Reservation { + res := make([]Reservation, 0, len(c.hops)) + for _, h := range c.hops { + res = append(res, h.reservations...) + } + return res +} + +// Removes the reservation with the given resID from a hop +func (c *HummingbirdClient) removeReservation(hopIdx int, resID uint32) { + h := &c.hops[hopIdx] + for i, r := range h.reservations { + if r.ResID == resID { + if i == 0 { + if len(h.reservations) == 1 { + h.hopfield.Flyover = false + c.dec.NumLines -= 2 + c.dec.PathMeta.SegLen[c.hops[hopIdx].infIdx] -= 2 + h.reservations = []Reservation{} + } else { + copy(h.reservations[:], h.reservations[1:]) + h.reservations = h.reservations[:len(h.reservations)-1] + h.hopfield.Bw = h.reservations[0].Bw + h.hopfield.ResID = h.reservations[0].ResID + h.hopfield.Duration = h.reservations[0].Duration + } + } else { + if i < len(h.reservations)-1 { + copy(h.reservations[i:], h.reservations[i+1:]) + } + h.reservations = h.reservations[:len(h.reservations)-1] + } + break + } + } +} + +// Removes res from the reservations the client is allowed to use +// Reservations are identified based on their AS and ResID +// Does NOT check for validity of remaining reservations +func (c *HummingbirdClient) RemoveReservations(res []Reservation) error { + for _, r := range res { + for i, h := range c.hops { + if r.AS == h.as { + c.removeReservation(i, r.ResID) + break + } + } + } + return nil +} + +// Checks whether any current reservation that has expired or will expire in t seconds +// If yes, remove reservation from list of used reservations +func (c *HummingbirdClient) CheckExpiry(t uint32) { + now := uint32(time.Now().Unix()) + for i := range c.hops { + + // Remove expired reservations + for j := 0; j < len(c.hops[i].reservations); { + if c.hops[i].reservations[j].StartTime+uint32(c.hops[i].reservations[j].Duration) < (now + t) { + copy(c.hops[i].reservations[j:], c.hops[i].reservations[j+1:]) + c.hops[i].reservations = c.hops[i].reservations[:len(c.hops[i].reservations)-1] + } + } + + if len(c.hops[i].reservations) == 0 { + if c.hops[i].hopfield.Flyover { + c.hops[i].hopfield.Flyover = false + c.dec.NumLines -= 2 + c.dec.PathMeta.SegLen[c.hops[i].infIdx] -= 2 + } + continue + } + // If there's any currently valid reservation, put it to the front + if !(c.hops[i].reservations[0].StartTime <= now) { + for j := 1; j < len(c.hops[i].reservations); j++ { + if c.hops[i].reservations[j].StartTime <= now { + temp := c.hops[i].reservations[0] + c.hops[i].reservations[0] = c.hops[i].reservations[j] + c.hops[i].reservations[j] = temp + break + } + } + } + // Check whether reservation at the front is currently valid + if c.hops[i].reservations[0].StartTime <= now { + if !c.hops[i].hopfield.Flyover { + c.hops[i].hopfield.Flyover = true + c.dec.NumLines += 2 + c.dec.PathMeta.SegLen[c.hops[i].infIdx] += 2 + } + c.hops[i].hopfield.Bw = c.hops[i].reservations[0].Bw + c.hops[i].hopfield.Duration = c.hops[i].reservations[0].Duration + c.hops[i].hopfield.ResID = c.hops[i].reservations[0].ResID + } else { + if c.hops[i].hopfield.Flyover { + c.hops[i].hopfield.Flyover = false + c.dec.NumLines -= 2 + c.dec.PathMeta.SegLen[c.hops[i].infIdx] -= 2 + } + } + } } // Sets pathmeta timestamps and increments duplicate detection counter. From cc134e634072d0f7aecdf70741103bdca6703dc9 Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Tue, 14 Nov 2023 15:00:08 +0100 Subject: [PATCH 037/100] added additional test cases for case where last hop is not flyover --- pkg/slayers/path/hummingbird/decoded_test.go | 84 ++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/pkg/slayers/path/hummingbird/decoded_test.go b/pkg/slayers/path/hummingbird/decoded_test.go index 768d565ac5..b0ed3fc0a7 100644 --- a/pkg/slayers/path/hummingbird/decoded_test.go +++ b/pkg/slayers/path/hummingbird/decoded_test.go @@ -69,6 +69,46 @@ var testFlyoverFields = []hummingbird.FlyoverHopField{ }, } +var otherTestFlyoverFields = []hummingbird.FlyoverHopField{ + { + HopField: path.HopField{ + ExpTime: 63, + ConsIngress: 1, + ConsEgress: 0, + Mac: [path.MacLen]byte{1, 2, 3, 4, 5, 6}, + }, + Flyover: true, + ResID: 0, + Bw: 4, + ResStartTime: 2, + Duration: 1, + }, + { + HopField: path.HopField{ + ExpTime: 63, + ConsIngress: 3, + ConsEgress: 2, + Mac: [path.MacLen]byte{1, 2, 3, 4, 5, 6}, + }, + }, + { + HopField: path.HopField{ + ExpTime: 63, + ConsIngress: 0, + ConsEgress: 2, + Mac: [path.MacLen]byte{1, 2, 3, 4, 5, 6}, + }, + }, + { + HopField: path.HopField{ + ExpTime: 63, + ConsIngress: 1, + ConsEgress: 0, + Mac: [path.MacLen]byte{1, 2, 3, 4, 5, 6}, + }, + }, +} + var decodedHbirdTestPath = &hummingbird.Decoded{ Base: hummingbird.Base{ PathMeta: hummingbird.MetaHdr{ @@ -86,6 +126,23 @@ var decodedHbirdTestPath = &hummingbird.Decoded{ FirstHopPerSeg: [2]uint8{2, 4}, } +var otherDecodedHbirdTestPath = &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrINF: 0, + CurrHF: 0, + SegLen: [3]uint8{8, 6, 0}, + BaseTS: 808, + HighResTS: 1234, + }, + NumINF: 2, + NumLines: 14, + }, + InfoFields: testInfoFields, + HopFields: otherTestFlyoverFields, + FirstHopPerSeg: [2]uint8{2, 4}, +} + var emptyDecodedTestPath = &hummingbird.Decoded{ Base: hummingbird.Base{}, InfoFields: []path.InfoField{}, @@ -99,6 +156,13 @@ var rawHbirdPath = []byte("\x00\x02\x04\x00\x00\x00\x03\x28\x00\x00\x04\xd2" + "\x00\x3f\x00\x00\x00\x02\x01\x02\x03\x04\x05\x06" + "\x80\x3f\x00\x01\x00\x00\x01\x02\x03\x04\x05\x06\x00\x00\x00\x04\x00\x00\x00\x01") +var otherRawHbirdPath = []byte("\x00\x02\x03\x00\x00\x00\x03\x28\x00\x00\x04\xd2" + + "\x00\x00\x01\x11\x00\x00\x01\x00\x01\x00\x02\x22\x00\x00\x01\x00" + + "\x80\x3f\x00\x01\x00\x00\x01\x02\x03\x04\x05\x06\x00\x00\x00\x04\x00\x02\x00\x01" + + "\x00\x3f\x00\x03\x00\x02\x01\x02\x03\x04\x05\x06" + + "\x00\x3f\x00\x00\x00\x02\x01\x02\x03\x04\x05\x06" + + "\x00\x3f\x00\x01\x00\x00\x01\x02\x03\x04\x05\x06") + type hbirdPathCase struct { infos []bool hops [][][]uint16 @@ -178,6 +242,26 @@ func TestDecodedSerializeDecodeHbird(t *testing.T) { assert.Equal(t, decodedHbirdTestPath, s) } +func TestOtherDecodedSerializeHbird(t *testing.T) { + b := make([]byte, otherDecodedHbirdTestPath.Len()) + assert.NoError(t, otherDecodedHbirdTestPath.SerializeTo(b)) + assert.Equal(t, otherRawHbirdPath, b) +} + +func TestOtherDecodedDecodeFromBytesHbird(t *testing.T) { + s := &hummingbird.Decoded{} + assert.NoError(t, s.DecodeFromBytes(otherRawHbirdPath)) + assert.Equal(t, otherDecodedHbirdTestPath, s) +} + +func TestOtherDecodedSerializeDecodeHbird(t *testing.T) { + b := make([]byte, otherDecodedHbirdTestPath.Len()) + assert.NoError(t, otherDecodedHbirdTestPath.SerializeTo(b)) + s := &hummingbird.Decoded{} + assert.NoError(t, s.DecodeFromBytes(b)) + assert.Equal(t, otherDecodedHbirdTestPath, s) +} + func TestDecodedReverseHbird(t *testing.T) { for name, tc := range pathReverseCasesHbird { name, tc := name, tc From f54cf2393dfe0c1209d075dd14ac0fa751fc42b7 Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Tue, 14 Nov 2023 15:31:10 +0100 Subject: [PATCH 038/100] added UT to hummingbird library --- pkg/hummingbird/hummingbird.go | 35 +-- pkg/hummingbird/hummingbird_test.go | 351 ++++++++++++++++++++++++++++ pkg/hummingbird/utils_test.go | 289 +++++++++++++++++++++++ tools/end2end_hbird/main.go | 6 +- 4 files changed, 663 insertions(+), 18 deletions(-) create mode 100644 pkg/hummingbird/hummingbird_test.go create mode 100644 pkg/hummingbird/utils_test.go diff --git a/pkg/hummingbird/hummingbird.go b/pkg/hummingbird/hummingbird.go index c256c8d285..4067658289 100644 --- a/pkg/hummingbird/hummingbird.go +++ b/pkg/hummingbird/hummingbird.go @@ -102,7 +102,7 @@ func AddReservation(res Reservation) error { // Converts a SCiON path to a Hummingbird path without adding any reservations // Relaces the SCiON dataplane path by a Hummingbird path -func ConvertToHbirdPath(p snet.Path) (snet.Path, error) { +func ConvertToHbirdPath(p snet.Path, timeStamp time.Time) (snet.Path, error) { if p == nil { return nil, serrors.New("Cannot convert nil path") } @@ -114,6 +114,11 @@ func ConvertToHbirdPath(p snet.Path) (snet.Path, error) { if err != nil { return nil, err } + // set metaheader timestamps + secs := uint32(timeStamp.Unix()) + millis := uint32(timeStamp.Nanosecond()/1000) << 22 + dec.PathMeta.BaseTS = secs + dec.PathMeta.HighResTS = millis hbird, err := snetpath.NewHbirdFromDecoded(&dec) if err != nil { @@ -349,16 +354,15 @@ func (c *HummingbirdClient) ApplyReservations(res []Reservation) error { for j, h := range c.hops { if r.AS == h.as { if r.Ingress == h.ingress && r.Egress == h.egress { - // TODO: If there are already reservations present, order by validity/bandwidth? c.hops[j].reservations = append(c.hops[j].reservations, r) - - // TODO: Only modify flyoverhopfield if new reservation becomes primary reservation - c.hops[j].hopfield.Flyover = true - c.dec.NumLines += 2 - c.dec.PathMeta.SegLen[h.infIdx] += 2 - c.hops[j].hopfield.Bw = r.Bw - c.hops[j].hopfield.Duration = r.Duration - c.hops[j].hopfield.ResID = r.ResID + if len(c.hops[j].reservations) == 1 { + c.hops[j].hopfield.Flyover = true + c.dec.NumLines += 2 + c.dec.PathMeta.SegLen[h.infIdx] += 2 + c.hops[j].hopfield.Bw = r.Bw + c.hops[j].hopfield.Duration = r.Duration + c.hops[j].hopfield.ResID = r.ResID + } } else { // TODO: inform caller that this reservation cannot be set on this path break @@ -434,6 +438,8 @@ func (c *HummingbirdClient) CheckExpiry(t uint32) { if c.hops[i].reservations[j].StartTime+uint32(c.hops[i].reservations[j].Duration) < (now + t) { copy(c.hops[i].reservations[j:], c.hops[i].reservations[j+1:]) c.hops[i].reservations = c.hops[i].reservations[:len(c.hops[i].reservations)-1] + } else { + j++ } } @@ -479,17 +485,16 @@ func (c *HummingbirdClient) CheckExpiry(t uint32) { // Sets pathmeta timestamps and increments duplicate detection counter. // Updates MACs of all flyoverfields -// replaces the dataplane of the inut snet.path with the finished hummingbird path -func (c *HummingbirdClient) FinalizePath(p snet.Path, pktLen uint16) (snet.Path, error) { +// replaces the dataplane of the input snet.path with the finished hummingbird path +func (c *HummingbirdClient) FinalizePath(p snet.Path, pktLen uint16, timeStamp time.Time) (snet.Path, error) { if p == nil { return nil, serrors.New("snet path is nil") } var dphb snetpath.Hummingbird // Update timestamps - now := time.Now() - secs := uint32(now.Unix()) - millis := uint32(now.Nanosecond()/1000) << 22 + secs := uint32(timeStamp.Unix()) + millis := uint32(timeStamp.Nanosecond()/1000) << 22 millis |= c.counter c.dec.Base.PathMeta.BaseTS = secs c.dec.Base.PathMeta.HighResTS = millis diff --git a/pkg/hummingbird/hummingbird_test.go b/pkg/hummingbird/hummingbird_test.go new file mode 100644 index 0000000000..8c75cd349e --- /dev/null +++ b/pkg/hummingbird/hummingbird_test.go @@ -0,0 +1,351 @@ +package hummingbird_test + +import ( + "testing" + "time" + + "github.com/scionproto/scion/pkg/hummingbird" + snetpath "github.com/scionproto/scion/pkg/snet/path" + "github.com/stretchr/testify/assert" +) + +var testHops = []hummingbird.Hop{ + {AS: 12, Ingress: 0, Egress: 1}, + {AS: 13, Ingress: 2, Egress: 2}, + {AS: 16, Ingress: 1, Egress: 0}, +} + +var testReservatons = []hummingbird.Reservation{ + { + AS: 12, + ResID: 1234, + Ingress: 0, + Egress: 1, + Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + Bw: 16, + Duration: 120, + StartTime: uint32(fixedTime.Unix()) - 10, + }, + { + AS: 13, + ResID: 42, + Ingress: 2, + Egress: 2, + Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4, 3, 2, 1, 0}, + Bw: 16, + Duration: 180, + StartTime: uint32(fixedTime.Unix()) - 32, + }, + { + AS: 16, + ResID: 365, + Ingress: 1, + Egress: 0, + Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7}, + Bw: 20, + Duration: 150, + StartTime: uint32(fixedTime.Unix()) - 80, + }, + { + AS: 16, + ResID: 21, + Ingress: 1, + Egress: 0, + Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7}, + Bw: 20, + Duration: 150, + StartTime: uint32(fixedTime.Unix()) - 10, + }, +} + +func TestPrepareHbirdPath(t *testing.T) { + + now := time.Now() + + scionPath, err := getScionSnetPath() + assert.NoError(t, err) + + hbirdPath, err := getHbirdNoFlyoversSnetPath(now) + assert.NoError(t, err) + + out, err := hummingbird.ConvertToHbirdPath(scionPath, now) + assert.NoError(t, err) + assert.Equal(t, hbirdPath, out) + + c := hummingbird.HummingbirdClient{} + + scionPath, err = getScionSnetPath() + assert.NoError(t, err) + + hops, err := c.PrepareHbirdPath(scionPath) + + assert.NoError(t, err) + assert.Equal(t, testHops, hops) + + scionPath, err = getScionSnetPath() + assert.NoError(t, err) + + output, err := c.FinalizePath(scionPath, 0, now) + assert.NoError(t, err) + assert.Equal(t, hbirdPath, output) +} + +func TestGetPathASes(t *testing.T) { + + c := hummingbird.HummingbirdClient{} + + scionPath, err := getScionSnetPath() + assert.NoError(t, err) + + hops, err := c.PrepareHbirdPath(scionPath) + + assert.NoError(t, err) + assert.Equal(t, testHops, hops) + + hops = c.GetPathASes() + + assert.Equal(t, testHops, hops) +} + +func TestApplyReservations(t *testing.T) { + c := hummingbird.HummingbirdClient{} + + scionPath, err := getScionSnetPath() + assert.NoError(t, err) + + _, err = c.PrepareHbirdPath(scionPath) + + assert.NoError(t, err) + + err = c.ApplyReservations(testReservatons) + + assert.NoError(t, err) + + scionPath, err = getScionSnetPath() + assert.NoError(t, err) + + hbirdPath, err := getHbirdFlyoversSnetPath(fixedTime) + assert.NoError(t, err) + + output, err := c.FinalizePath(scionPath, 16, fixedTime) + assert.NoError(t, err) + assert.Equal(t, hbirdPath, output) +} + +func TestCheckReservationExpiry(t *testing.T) { + c := hummingbird.HummingbirdClient{} + + scionPath, err := getScionSnetPath() + assert.NoError(t, err) + + _, err = c.PrepareHbirdPath(scionPath) + + assert.NoError(t, err) + + tnow := time.Now() + now := uint32(tnow.Unix()) + + // hop1: first reservation expired, second ok + // hop2: first reservation expired, second not started, third expired (input 5 as tolerance), fourth ok + // hop3: first not yet valid, second expired + input := []hummingbird.Reservation{ + { + AS: 12, + ResID: 1234, + Ingress: 0, + Egress: 1, + Duration: 70, + StartTime: now - 80, + }, + { + AS: 12, + ResID: 34, + Ingress: 0, + Egress: 1, + Duration: 560, + StartTime: now - 10, + }, + { + AS: 13, + ResID: 42, + Ingress: 2, + Egress: 2, + Duration: 80, + StartTime: now - 100, + }, + { + AS: 13, + ResID: 31, + Ingress: 2, + Egress: 2, + Duration: 389, + StartTime: now + 50, + }, + { + AS: 13, + ResID: 12, + Ingress: 2, + Egress: 2, + Duration: 64, + StartTime: now - 60, + }, + { + AS: 13, + ResID: 5, + Ingress: 2, + Egress: 2, + Duration: 180, + StartTime: now - 30, + }, + { + AS: 16, + ResID: 365, + Ingress: 1, + Egress: 0, + Duration: 150, + StartTime: now + 60, + }, + { + AS: 16, + ResID: 345, + Ingress: 1, + Egress: 0, + Duration: 150, + StartTime: now - 345, + }, + } + + expected := []hummingbird.Reservation{ + { + AS: 12, + ResID: 34, + Ingress: 0, + Egress: 1, + Duration: 560, + StartTime: now - 10, + }, + { + AS: 13, + ResID: 5, + Ingress: 2, + Egress: 2, + Duration: 180, + StartTime: now - 30, + }, + { + AS: 13, + ResID: 31, + Ingress: 2, + Egress: 2, + Duration: 389, + StartTime: now + 50, + }, + { + AS: 16, + ResID: 365, + Ingress: 1, + Egress: 0, + Duration: 150, + StartTime: now + 60, + }, + } + + err = c.ApplyReservations(input) + assert.NoError(t, err) + + c.CheckExpiry(5) + + output := c.GetUsedReservations() + + assert.Equal(t, expected, output) + + // Verify last reservation is unused as it is not yet valid + scionPath, err = getScionSnetPath() + assert.NoError(t, err) + + outPath, err := c.FinalizePath(scionPath, 16, tnow) + assert.NoError(t, err) + + raw := outPath.Dataplane().(snetpath.Hummingbird).Raw + + dec, err := decodeDataplane(raw) + assert.NoError(t, err) + assert.True(t, dec.HopFields[0].Flyover) + assert.True(t, dec.HopFields[1].Flyover) + assert.False(t, dec.HopFields[2].Flyover) + assert.False(t, dec.HopFields[3].Flyover) +} + +func TestRemoveReservations(t *testing.T) { + c := hummingbird.HummingbirdClient{} + + scionPath, err := getScionSnetPath() + assert.NoError(t, err) + + _, err = c.PrepareHbirdPath(scionPath) + + assert.NoError(t, err) + + err = c.ApplyReservations(testReservatons) + assert.NoError(t, err) + + remove := []hummingbird.Reservation{ + { + AS: 12, + ResID: 1234, + }, + { + AS: 13, + ResID: 53, + }, + { + AS: 16, + ResID: 365, + }, + } + + expected := []hummingbird.Reservation{ + { + AS: 13, + ResID: 42, + Ingress: 2, + Egress: 2, + Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4, 3, 2, 1, 0}, + Bw: 16, + Duration: 180, + StartTime: uint32(fixedTime.Unix()) - 32, + }, + { + AS: 16, + ResID: 21, + Ingress: 1, + Egress: 0, + Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7}, + Bw: 20, + Duration: 150, + StartTime: uint32(fixedTime.Unix()) - 10, + }, + } + + err = c.RemoveReservations(remove) + assert.NoError(t, err) + + output := c.GetUsedReservations() + assert.Equal(t, expected, output) + + // Verify removal has resulted in correct path + scionPath, err = getScionSnetPath() + assert.NoError(t, err) + + outPath, err := c.FinalizePath(scionPath, 16, time.Now()) + assert.NoError(t, err) + + raw := outPath.Dataplane().(snetpath.Hummingbird).Raw + + dec, err := decodeDataplane(raw) + assert.NoError(t, err) + assert.False(t, dec.HopFields[0].Flyover) + assert.True(t, dec.HopFields[1].Flyover) + assert.False(t, dec.HopFields[2].Flyover) + assert.True(t, dec.HopFields[3].Flyover) +} diff --git a/pkg/hummingbird/utils_test.go b/pkg/hummingbird/utils_test.go new file mode 100644 index 0000000000..9a0df0d175 --- /dev/null +++ b/pkg/hummingbird/utils_test.go @@ -0,0 +1,289 @@ +package hummingbird_test + +import ( + "time" + + "github.com/scionproto/scion/pkg/slayers/path" + "github.com/scionproto/scion/pkg/slayers/path/hummingbird" + "github.com/scionproto/scion/pkg/slayers/path/scion" + "github.com/scionproto/scion/pkg/snet" + snetpath "github.com/scionproto/scion/pkg/snet/path" +) + +var fixedTime = time.Unix(1136239445, 432) + +var sv = []byte{0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4, 3, 2, 1, 0} + +var testInfoFields = []path.InfoField{ + { + Peer: false, + ConsDir: false, + SegID: 0x111, + Timestamp: 0x100, + }, + { + Peer: false, + ConsDir: true, + SegID: 0x222, + Timestamp: 0x100, + }, +} + +var testHopFields = []path.HopField{ + { + ExpTime: 63, + ConsIngress: 1, + ConsEgress: 0, + Mac: [path.MacLen]byte{1, 2, 3, 4, 5, 6}, + }, + { + ExpTime: 63, + ConsIngress: 3, + ConsEgress: 2, + Mac: [path.MacLen]byte{1, 2, 3, 4, 5, 6}, + }, + { + ExpTime: 63, + ConsIngress: 0, + ConsEgress: 2, + Mac: [path.MacLen]byte{1, 2, 3, 4, 5, 6}, + }, + { + ExpTime: 63, + ConsIngress: 1, + ConsEgress: 0, + Mac: [path.MacLen]byte{1, 2, 3, 4, 5, 6}, + }, +} + +var testFlyoverFields = []hummingbird.FlyoverHopField{ + { + HopField: testHopFields[0], + Flyover: false, + }, + { + HopField: testHopFields[1], + Flyover: false, + }, + { + HopField: testHopFields[2], + Flyover: false, + }, + { + HopField: testHopFields[3], + Flyover: false, + }, +} + +var testFlyoverFieldsReserved = []hummingbird.FlyoverHopField{ + { + HopField: testHopFields[0], + Flyover: true, + ResID: 1234, + Bw: 16, + Duration: 120, + ResStartTime: 10, + }, + { + HopField: testHopFields[1], + Flyover: true, + ResID: 42, + Bw: 16, + Duration: 180, + ResStartTime: 32, + }, + { + HopField: testHopFields[2], + Flyover: false, + }, + { + HopField: testHopFields[3], + Flyover: true, + ResID: 365, + Bw: 20, + Duration: 150, + ResStartTime: 80, + }, +} + +var decodedTestPath = &scion.Decoded{ + Base: scion.Base{ + PathMeta: scion.MetaHdr{ + CurrINF: 0, + CurrHF: 0, + SegLen: [3]uint8{2, 2, 0}, + }, + + NumINF: 2, + NumHops: 4, + }, + InfoFields: testInfoFields, + HopFields: testHopFields, +} + +var decodedHbirdTestPath = &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrINF: 0, + CurrHF: 0, + SegLen: [3]uint8{6, 6, 0}, + }, + NumINF: 2, + NumLines: 12, + }, + InfoFields: testInfoFields, + HopFields: testFlyoverFields, + FirstHopPerSeg: [2]uint8{2, 4}, +} + +var decodedHbirdTestPathFlyovers = &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrINF: 0, + CurrHF: 0, + SegLen: [3]uint8{10, 8, 0}, + }, + NumINF: 2, + NumLines: 18, + }, + InfoFields: testInfoFields, + HopFields: testFlyoverFieldsReserved, + FirstHopPerSeg: [2]uint8{2, 4}, +} + +func getRawScionPath(d scion.Decoded) ([]byte, error) { + b := make([]byte, d.Len()) + err := d.SerializeTo(b) + return b, err +} + +func getScionSnetPath() (snetpath.Path, error) { + rawScion, err := getRawScionPath(*decodedTestPath) + p := snetpath.Path{ + Src: 12, + Dst: 16, + DataplanePath: snetpath.SCION{ + Raw: rawScion, + }, + Meta: snet.PathMetadata{ + Interfaces: []snet.PathInterface{ + { + IA: 12, + }, + { + IA: 13, + }, + { + IA: 13, + }, + { + IA: 16, + }, + }, + }, + } + return p, err +} + +func getRawHbirdPath(h hummingbird.Decoded) ([]byte, error) { + b := make([]byte, h.Len()) + err := h.SerializeTo(b) + return b, err +} + +func getHbirdNoFlyoversSnetPath(t time.Time) (snetpath.Path, error) { + decoded := *decodedHbirdTestPath + secs := uint32(t.Unix()) + millis := uint32(t.Nanosecond()/1000) << 22 + decoded.Base.PathMeta.BaseTS = secs + decoded.Base.PathMeta.HighResTS = millis + + rawHbird, err := getRawHbirdPath(decoded) + p := snetpath.Path{ + Src: 12, + Dst: 16, + DataplanePath: snetpath.Hummingbird{ + Raw: rawHbird, + }, + Meta: snet.PathMetadata{ + Interfaces: []snet.PathInterface{ + { + IA: 12, + }, + { + IA: 13, + }, + { + IA: 13, + }, + { + IA: 16, + }, + }, + }, + } + return p, err +} + +func getHbirdFlyoversSnetPath(t time.Time) (snetpath.Path, error) { + decoded := *decodedHbirdTestPathFlyovers + secs := uint32(t.Unix()) + millis := uint32(t.Nanosecond()/1000) << 22 + decoded.Base.PathMeta.BaseTS = secs + decoded.Base.PathMeta.HighResTS = millis + + xkBuffer := make([]uint32, hummingbird.XkBufferSize) + macBuffer0 := make([]byte, hummingbird.FlyoverMacBufferSize) + macBuffer1 := make([]byte, hummingbird.FlyoverMacBufferSize) + macBuffer2 := make([]byte, hummingbird.FlyoverMacBufferSize) + + ak0 := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} + flyover0 := hummingbird.FullFlyoverMac(ak0, 16, 16, decoded.HopFields[0].ResStartTime, + millis, macBuffer0, xkBuffer) + + ak1 := []byte{0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4, 3, 2, 1, 0} + flyover1 := hummingbird.FullFlyoverMac(ak1, 16, 16, decoded.HopFields[1].ResStartTime, + millis, macBuffer1, xkBuffer) + + ak2 := []byte{0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7} + flyover2 := hummingbird.FullFlyoverMac(ak2, 16, 16, decoded.HopFields[3].ResStartTime, + millis, macBuffer2, xkBuffer) + + for i := 0; i < 6; i++ { + decoded.HopFields[0].HopField.Mac[i] = decoded.HopFields[0].HopField.Mac[i] ^ flyover0[i] + decoded.HopFields[1].HopField.Mac[i] = decoded.HopFields[1].HopField.Mac[i] ^ flyover1[i] + decoded.HopFields[3].HopField.Mac[i] = decoded.HopFields[3].HopField.Mac[i] ^ flyover2[i] + } + + rawHbird, err := getRawHbirdPath(decoded) + p := snetpath.Path{ + Src: 12, + Dst: 16, + DataplanePath: snetpath.Hummingbird{ + Raw: rawHbird, + }, + Meta: snet.PathMetadata{ + Interfaces: []snet.PathInterface{ + { + IA: 12, + }, + { + IA: 13, + }, + { + IA: 13, + }, + { + IA: 16, + }, + }, + }, + } + return p, err +} + +func decodeDataplane(raw []byte) (hummingbird.Decoded, error) { + dec := hummingbird.Decoded{} + err := dec.DecodeFromBytes(raw) + return dec, err +} diff --git a/tools/end2end_hbird/main.go b/tools/end2end_hbird/main.go index 9d7975b642..09bcce7760 100644 --- a/tools/end2end_hbird/main.go +++ b/tools/end2end_hbird/main.go @@ -305,7 +305,7 @@ func (c *client) attemptRequest(n int) bool { if path != nil { if !flyovers { // Standard path, no reservations at all - path, err = hummingbird.ConvertToHbirdPath(path) + path, err = hummingbird.ConvertToHbirdPath(path, time.Now()) if err != nil { logger.Error("Error converting path to Hummingbird", "err", err) return false @@ -329,7 +329,7 @@ func (c *client) attemptRequest(n int) bool { logger.Error("Error applying reservations", "err", err) } - path, err = hbirdClient.FinalizePath(path, pingPayloadLen) + path, err = hbirdClient.FinalizePath(path, pingPayloadLen, time.Now()) if err != nil { logger.Error("Error assembling hummingbird path", "err", err) } @@ -359,7 +359,7 @@ func (c *client) attemptRequest(n int) bool { logger.Error("Error applying reservations", "err", err) } - path, err = hbirdClient.FinalizePath(path, pingPayloadLen) + path, err = hbirdClient.FinalizePath(path, pingPayloadLen, time.Now()) if err != nil { logger.Error("Error assembling hummingbird path", "err", err) } From e4d708c0a113ff8bb1b5ddee3daed8d76b986acc Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Thu, 16 Nov 2023 15:15:30 +0100 Subject: [PATCH 039/100] added bandwidth check --- router/BUILD.bazel | 2 + router/dataplane.go | 3 + router/dataplane_hbird.go | 55 +++++++++++ router/dataplane_hbird_test.go | 87 +++++++++++++++-- router/tokenbucket/BUILD.bazel | 23 +++++ router/tokenbucket/tokenbucket.go | 70 ++++++++++++++ router/tokenbucket/tokenbucket_test.go | 127 +++++++++++++++++++++++++ 7 files changed, 359 insertions(+), 8 deletions(-) create mode 100644 router/tokenbucket/BUILD.bazel create mode 100644 router/tokenbucket/tokenbucket.go create mode 100644 router/tokenbucket/tokenbucket_test.go diff --git a/router/BUILD.bazel b/router/BUILD.bazel index b29eae6e90..174a4b5e2f 100644 --- a/router/BUILD.bazel +++ b/router/BUILD.bazel @@ -33,6 +33,7 @@ go_library( "//private/underlay/conn:go_default_library", "//router/bfd:go_default_library", "//router/control:go_default_library", + "//router/tokenbucket:go_default_library", "@com_github_google_gopacket//:go_default_library", "@com_github_google_gopacket//layers:go_default_library", "@com_github_prometheus_client_golang//prometheus:go_default_library", @@ -69,6 +70,7 @@ go_test( "//private/underlay/conn:go_default_library", "//router/control:go_default_library", "//router/mock_router:go_default_library", + "//router/tokenbucket:go_default_library", "@com_github_golang_mock//gomock:go_default_library", "@com_github_google_gopacket//:go_default_library", "@com_github_google_gopacket//layers:go_default_library", diff --git a/router/dataplane.go b/router/dataplane.go index 3c9e49e4b0..a57231a362 100644 --- a/router/dataplane.go +++ b/router/dataplane.go @@ -121,6 +121,9 @@ type DataPlane struct { // The pool that stores all the packet buffers as described in the design document. See // https://github.com/scionproto/scion/blob/master/doc/dev/design/BorderRouter.rst packetPool chan []byte + + // Contains the token buckets for hummingbird bandwidth check + tokenBuckets sync.Map } var ( diff --git a/router/dataplane_hbird.go b/router/dataplane_hbird.go index e0baa75d98..2ac8535621 100644 --- a/router/dataplane_hbird.go +++ b/router/dataplane_hbird.go @@ -17,6 +17,7 @@ import ( "github.com/scionproto/scion/pkg/slayers/path" "github.com/scionproto/scion/pkg/slayers/path/hummingbird" "github.com/scionproto/scion/pkg/spao" + "github.com/scionproto/scion/router/tokenbucket" ) // SetSecretValue sets the key for the PRF function used to compute the Hummingbird Auth Key @@ -307,6 +308,57 @@ func (p *scionPacketProcessor) validatePathMetaTimestamp() { } } +// Converts a flyover bandwidth value to bytes per second +func convertResBw(bw uint16) float64 { + + // In this implementation, we choose to allow reservations up to 64 kBps + // Since the bandwidth field has 10 bits, we multiply by 64 to reach the target range + return float64(bw * 64) +} + +func (p *scionPacketProcessor) checkReservationBandwidth() (processResult, error) { + + // Only check bandwidth if packet is given priority + // Bandwidth check is NOT performed for late packets that have flyover but no priority + if !p.hasPriority { + return processResult{}, nil + } + v, ok := p.d.tokenBuckets.Load(p.flyoverField.ResID) + if ok { + // Check bandwidth + tb, ok := v.(*tokenbucket.TokenBucket) + if !ok { + log.Error("Non-tokenbucket value found in tokenbucket map") + panic("tokenbucket map contains value of different type") + } + resBw := convertResBw(p.flyoverField.Bw) + if tb.CIR != resBw { + // It is possible for different reservations to share a resID + // if they do not overlap in time + tb.SetRate(resBw) + tb.SetBurstSize(resBw) + } + if tb.Apply(int(p.scionLayer.PayloadLen), time.Now()) { + return processResult{}, nil + } + // TODO: return scmp packet for reservation overuse + return processResult{}, serrors.New("Reservation bandwidth overuse", + "ResID", p.flyoverField.ResID, "Authorized Bandwidth", p.flyoverField.Bw) + } + // Initialize token bucket for given reservation + resBw := convertResBw(p.flyoverField.Bw) + now := time.Now() + tb := tokenbucket.NewTokenBucket(now, resBw, resBw) + p.d.tokenBuckets.Store(p.flyoverField.ResID, tb) + + if tb.Apply(int(p.scionLayer.PayloadLen), time.Now()) { + return processResult{}, nil + } + // TODO: return scmp packet for reservation overuse + return processResult{}, serrors.New("Reservation bandwidth overuse", + "ResID", p.flyoverField.ResID, "Authorized Bandwidth", p.flyoverField.Bw) +} + func (p *scionPacketProcessor) handleHbirdIngressRouterAlert() (processResult, error) { if p.ingressID == 0 { return processResult{}, nil @@ -526,6 +578,9 @@ func (p *scionPacketProcessor) processHBIRD() (processResult, error) { if p.hasPriority && p.flyoverField.Flyover { p.validatePathMetaTimestamp() } + if r, err := p.checkReservationBandwidth(); err != nil { + return r, err + } if r, err := p.handleHbirdIngressRouterAlert(); err != nil { return r, err } diff --git a/router/dataplane_hbird_test.go b/router/dataplane_hbird_test.go index 657ce1c1bd..4e691268d2 100644 --- a/router/dataplane_hbird_test.go +++ b/router/dataplane_hbird_test.go @@ -7,6 +7,7 @@ import ( "time" "github.com/golang/mock/gomock" + "github.com/google/gopacket" "github.com/scionproto/scion/pkg/addr" "github.com/scionproto/scion/pkg/private/util" "github.com/scionproto/scion/pkg/private/xtest" @@ -622,7 +623,8 @@ func TestProcessHbirdPacket(t *testing.T) { dpath.HopFields = []hummingbird.FlyoverHopField{ {HopField: path.HopField{ConsIngress: 41, ConsEgress: 40}}, {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, - {Flyover: true, HopField: path.HopField{ConsIngress: 01, ConsEgress: 0}, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 01, ConsEgress: 0}, + ResStartTime: 123, Duration: 304, Bw: 16}, } dpath.Base.PathMeta.SegLen[0] = 11 dpath.Base.NumLines = 11 @@ -656,9 +658,12 @@ func TestProcessHbirdPacket(t *testing.T) { spkt, dpath := prepHbirdMsg(now) spkt.SrcIA = xtest.MustParseIA("1-ff00:0:110") dpath.HopFields = []hummingbird.FlyoverHopField{ - {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 1}, ResStartTime: 123, Duration: 304}, - {Flyover: true, HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}, ResStartTime: 123, Duration: 304}, - {Flyover: true, HopField: path.HopField{ConsIngress: 41, ConsEgress: 40}, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 1}, + ResStartTime: 123, Duration: 304, Bw: 16}, + {Flyover: true, HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}, + ResStartTime: 123, Duration: 304, Bw: 16}, + {Flyover: true, HopField: path.HopField{ConsIngress: 41, ConsEgress: 40}, + ResStartTime: 123, Duration: 304, Bw: 16}, } dpath.Base.PathMeta.CurrHF = 0 dpath.Base.PathMeta.SegLen[0] = 15 @@ -761,7 +766,7 @@ func TestProcessHbirdPacket(t *testing.T) { spkt, dpath := prepHbirdMsg(now) dpath.HopFields = []hummingbird.FlyoverHopField{ {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, - {Flyover: true, HopField: path.HopField{ConsIngress: 2, ConsEgress: 1}, ResID: 42, ResStartTime: 5, Duration: 301}, + {Flyover: true, HopField: path.HopField{ConsIngress: 2, ConsEgress: 1}, ResID: 42, ResStartTime: 5, Duration: 301, Bw: 16}, {HopField: path.HopField{ConsIngress: 40, ConsEgress: 41}}, } dpath.Base.PathMeta.CurrHF = 3 @@ -802,14 +807,15 @@ func TestProcessHbirdPacket(t *testing.T) { spkt, dpath := prepHbirdMsg(now) dpath.HopFields = []hummingbird.FlyoverHopField{ {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, - {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 3}, ResID: 42, ResStartTime: 5, Duration: 301}, + {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 3}, + ResID: 42, ResStartTime: 5, Duration: 301, Bw: 16}, {HopField: path.HopField{ConsIngress: 50, ConsEgress: 51}}, } dpath.Base.PathMeta.SegLen[0] = 11 dpath.Base.NumLines = 11 - dpath.HopFields[1].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, dpath.InfoFields[0], dpath.HopFields[1], dpath.Base.PathMeta) + dpath.HopFields[1].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, + spkt.PayloadLen, dpath.InfoFields[0], dpath.HopFields[1], dpath.Base.PathMeta) if afterProcessing { - // dpath.HopFields[1].Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1]) ret := toMsg(t, spkt, dpath) ret.Addr = &net.UDPAddr{IP: net.ParseIP("10.0.200.200").To4(), Port: 30043} ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil @@ -3510,6 +3516,71 @@ func TestHbirdPacketPath(t *testing.T) { } } +func TestBandwidthCheck(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + key := []byte("testkey_xxxxxxxx") + sv := []byte("test_secretvalue") + now := time.Now() + + dp := router.NewDP( + map[uint16]router.BatchConn{ + uint16(2): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 1: topology.Parent, + 2: topology.Child, + }, + nil, nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + + spkt, dpath := prepHbirdMsg(now) + dpath.HopFields = []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, + {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}, Bw: 2, ResStartTime: 123, Duration: 304}, + {HopField: path.HopField{ConsIngress: 40, ConsEgress: 41}}, + } + dpath.Base.PathMeta.SegLen[0] = 11 + dpath.Base.PathMeta.CurrHF = 3 + dpath.Base.NumLines = 11 + + spkt.PayloadLen = 120 + dpath.HopFields[1].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, dpath.InfoFields[0], dpath.HopFields[1], dpath.Base.PathMeta) + + msg := toLongMsg(t, spkt, dpath) + + _, err := dp.ProcessPkt(1, msg) + assert.NoError(t, err) + + msg = toLongMsg(t, spkt, dpath) + _, err = dp.ProcessPkt(1, msg) + assert.Error(t, err) + + time.Sleep(time.Duration(1) * time.Second) + + msg = toLongMsg(t, spkt, dpath) + _, err = dp.ProcessPkt(1, msg) + assert.NoError(t, err) +} + +func toLongMsg(t *testing.T, spkt *slayers.SCION, dpath path.Path) *ipv4.Message { + t.Helper() + ret := &ipv4.Message{} + spkt.Path = dpath + buffer := gopacket.NewSerializeBuffer() + payload := [120]byte{} + err := gopacket.SerializeLayers(buffer, gopacket.SerializeOptions{FixLengths: true}, + spkt, gopacket.Payload(payload[:])) + require.NoError(t, err) + raw := buffer.Bytes() + ret.Buffers = make([][]byte, 1) + ret.Buffers[0] = make([]byte, 1500) + copy(ret.Buffers[0], raw) + ret.N = len(raw) + ret.Buffers[0] = ret.Buffers[0][:ret.N] + return ret +} + func prepHbirdMsg(now time.Time) (*slayers.SCION, *hummingbird.Decoded) { spkt := &slayers.SCION{ Version: 0, diff --git a/router/tokenbucket/BUILD.bazel b/router/tokenbucket/BUILD.bazel new file mode 100644 index 0000000000..616b2879a2 --- /dev/null +++ b/router/tokenbucket/BUILD.bazel @@ -0,0 +1,23 @@ +load("//tools/lint:go.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = [ + "tokenbucket.go", + ], + importpath = "github.com/scionproto/scion/router/tokenbucket", + visibility = ["//visibility:public"], + deps = [ + ], +) + +go_test( + name = "go_default_test", + srcs = [ + "tokenbucket_test.go", + ], + embed = [":go_default_library"], + deps = [ + "@com_github_stretchr_testify//assert:go_default_library", + ], +) \ No newline at end of file diff --git a/router/tokenbucket/tokenbucket.go b/router/tokenbucket/tokenbucket.go new file mode 100644 index 0000000000..85460e6be4 --- /dev/null +++ b/router/tokenbucket/tokenbucket.go @@ -0,0 +1,70 @@ +package tokenbucket + +import ( + "sync" + "time" +) + +type TokenBucket struct { + CurrentTokens float64 + LastTimeApplied time.Time + + //Burst Size + CBS float64 + + //In bytes per second + CIR float64 + + // Lock + lock sync.Mutex +} + +func NewTokenBucket(initialTime time.Time, burstSize float64, rate float64) *TokenBucket { + return &TokenBucket{ + CurrentTokens: rate, + CIR: rate, + CBS: burstSize, + LastTimeApplied: initialTime, + } +} + +func (t *TokenBucket) SetRate(rate float64) { + t.lock.Lock() + defer t.lock.Unlock() + t.CIR = rate +} + +func (t *TokenBucket) SetBurstSize(burstSize float64) { + t.lock.Lock() + defer t.lock.Unlock() + t.CBS = burstSize +} + +// Apply calculates the current available tokens and checks whether there +// are enough tokens available. The success is indicated by a bool. +func (t *TokenBucket) Apply(size int, now time.Time) bool { + t.lock.Lock() + defer t.lock.Unlock() + // Increase available tokens according to time passed since last call + // Apply() is expected to be called from different threads + // As a consequence, it is possible for now to be older than LastTimeApplied + if !now.Before(t.LastTimeApplied) { + t.CurrentTokens += now.Sub(t.LastTimeApplied).Seconds() * t.CIR + t.CurrentTokens = min(t.CurrentTokens, t.CBS) + t.LastTimeApplied = now + } + if t.CurrentTokens >= float64(size) { + t.CurrentTokens -= float64(size) + return true + } + return false +} + +// This function calculates the minimal value of two float64. +func min(a float64, b float64) float64 { + if a > b { + return b + } else { + return a + } +} diff --git a/router/tokenbucket/tokenbucket_test.go b/router/tokenbucket/tokenbucket_test.go new file mode 100644 index 0000000000..eb69a4ed88 --- /dev/null +++ b/router/tokenbucket/tokenbucket_test.go @@ -0,0 +1,127 @@ +package tokenbucket_test + +import ( + "testing" + "time" + + "github.com/scionproto/scion/router/tokenbucket" + "github.com/stretchr/testify/assert" +) + +type entry struct { + length int + arrivalTime time.Time + result bool +} +type test struct { + name string + entries []entry + bucket *tokenbucket.TokenBucket +} + +// Tests the Token Bucket Algorithm by running subtests. +func TestGroupForTokenBucketAlgorithm(t *testing.T) { + + var startTime = time.Unix(0, 0) + + tests := []test{ + { + name: "TestApplyDoesAllowArrivalBehindTheLastArrival", + bucket: tokenbucket.NewTokenBucket(startTime.Add(1), 1024, 1024), + entries: []entry{ + { + length: 1, + arrivalTime: startTime, + result: true, + }, + }, + }, + { + name: "FullBandwidthCanBeConsumedAtOnce", + bucket: tokenbucket.NewTokenBucket(startTime, 1024, 1024), + entries: []entry{ + { + length: 1024, + arrivalTime: startTime, + result: true, + }, + { + length: 1, + arrivalTime: startTime, + result: false, + }, + }, + }, + { + name: "FullBandwidthCanBeConsumedOverMultiplePackets", + bucket: tokenbucket.NewTokenBucket(startTime, 1024, 1024), + entries: []entry{ + { + length: 512, + arrivalTime: startTime, + result: true, + }, + { + length: 512, + arrivalTime: startTime, + result: true, + }, + { + length: 1, + arrivalTime: startTime, + result: false, + }, + }, + }, + { + name: "CurrentTokensRegenerate", + bucket: tokenbucket.NewTokenBucket(startTime, 1024, 1024), + entries: []entry{ + { + length: 1024, + arrivalTime: startTime, + result: true, + }, + { + length: 512, + arrivalTime: startTime.Add(500 * time.Millisecond), + result: true, + }, + { + length: 1, + arrivalTime: startTime.Add(500 * time.Millisecond), + result: false, + }, + }, + }, + { + name: "CurrentTokensIsLimitedByCBS", + bucket: tokenbucket.NewTokenBucket(startTime, 2048, 1024), + entries: []entry{ + { + length: 2049, + arrivalTime: startTime.Add(1 * time.Second), + result: false, + }, + { + length: 2048, + arrivalTime: startTime.Add(1 * time.Second), + result: true, + }, + { + length: 1, + arrivalTime: startTime.Add(1 * time.Second), + result: false, + }, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + for _, en := range tc.entries { + assert.Equal(t, en.result, tc.bucket.Apply(en.length, en.arrivalTime), tc.name) + } + }) + } +} From 3834b5a98cf2676c37459a311e97ac9497486c78 Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Fri, 17 Nov 2023 09:22:32 +0100 Subject: [PATCH 040/100] fixed most of the issues highlighted by linter --- pkg/hummingbird/hummingbird.go | 12 +- pkg/hummingbird/hummingbird_test.go | 2 +- pkg/hummingbird/utils_test.go | 2 - pkg/slayers/path/hummingbird/base_test.go | 3 +- pkg/slayers/path/hummingbird/decoded_test.go | 3 +- .../path/hummingbird/flyoverhopfield_test.go | 3 +- pkg/slayers/path/hummingbird/mac_test.go | 3 +- pkg/slayers/path/hummingbird/raw_test.go | 5 +- router/dataplane_hbird.go | 1 + router/dataplane_hbird_test.go | 687 +++++++++++------- router/tokenbucket/tokenbucket_test.go | 3 +- 11 files changed, 466 insertions(+), 258 deletions(-) diff --git a/pkg/hummingbird/hummingbird.go b/pkg/hummingbird/hummingbird.go index 4067658289..0ce7a95b52 100644 --- a/pkg/hummingbird/hummingbird.go +++ b/pkg/hummingbird/hummingbird.go @@ -302,7 +302,8 @@ func (c *HummingbirdClient) RequestReservationsAllHops( } // Requests new reservations for the listed Hops and returns them once they are obtained -// TODO: add timeout after which already received reservations (if any) are returned once we have actual requests +// TODO: add timeout after which already received reservations +// (if any) are returned once we have actual requests // TODO: add fully async version of this func RequestReservationForASes( hops []Hop, bw uint16, start uint32, duration uint16) ([]Reservation, error) { @@ -374,7 +375,8 @@ func (c *HummingbirdClient) ApplyReservations(res []Reservation) error { } // Returns all the reservations that the client may currently use -// If multiple reservations per hop are present, the one currently used is the first appearing in the returned array +// If there are multiple reservations for a hop, +// The one currently used is the first appearing in the returned array func (c *HummingbirdClient) GetUsedReservations() []Reservation { res := make([]Reservation, 0, len(c.hops)) for _, h := range c.hops { @@ -435,7 +437,8 @@ func (c *HummingbirdClient) CheckExpiry(t uint32) { // Remove expired reservations for j := 0; j < len(c.hops[i].reservations); { - if c.hops[i].reservations[j].StartTime+uint32(c.hops[i].reservations[j].Duration) < (now + t) { + if c.hops[i].reservations[j].StartTime+uint32(c.hops[i].reservations[j].Duration) < + (now + t) { copy(c.hops[i].reservations[j:], c.hops[i].reservations[j+1:]) c.hops[i].reservations = c.hops[i].reservations[:len(c.hops[i].reservations)-1] } else { @@ -486,7 +489,8 @@ func (c *HummingbirdClient) CheckExpiry(t uint32) { // Sets pathmeta timestamps and increments duplicate detection counter. // Updates MACs of all flyoverfields // replaces the dataplane of the input snet.path with the finished hummingbird path -func (c *HummingbirdClient) FinalizePath(p snet.Path, pktLen uint16, timeStamp time.Time) (snet.Path, error) { +func (c *HummingbirdClient) FinalizePath(p snet.Path, pktLen uint16, + timeStamp time.Time) (snet.Path, error) { if p == nil { return nil, serrors.New("snet path is nil") } diff --git a/pkg/hummingbird/hummingbird_test.go b/pkg/hummingbird/hummingbird_test.go index 8c75cd349e..a580e4c8e9 100644 --- a/pkg/hummingbird/hummingbird_test.go +++ b/pkg/hummingbird/hummingbird_test.go @@ -146,7 +146,7 @@ func TestCheckReservationExpiry(t *testing.T) { now := uint32(tnow.Unix()) // hop1: first reservation expired, second ok - // hop2: first reservation expired, second not started, third expired (input 5 as tolerance), fourth ok + // hop2: first reservation expired, second not started, third expired, fourth ok // hop3: first not yet valid, second expired input := []hummingbird.Reservation{ { diff --git a/pkg/hummingbird/utils_test.go b/pkg/hummingbird/utils_test.go index 9a0df0d175..e151eacad7 100644 --- a/pkg/hummingbird/utils_test.go +++ b/pkg/hummingbird/utils_test.go @@ -12,8 +12,6 @@ import ( var fixedTime = time.Unix(1136239445, 432) -var sv = []byte{0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4, 3, 2, 1, 0} - var testInfoFields = []path.InfoField{ { Peer: false, diff --git a/pkg/slayers/path/hummingbird/base_test.go b/pkg/slayers/path/hummingbird/base_test.go index d54a62ea3f..41b7c4ff01 100644 --- a/pkg/slayers/path/hummingbird/base_test.go +++ b/pkg/slayers/path/hummingbird/base_test.go @@ -4,8 +4,9 @@ import ( "fmt" "testing" - "github.com/scionproto/scion/pkg/slayers/path/hummingbird" "github.com/stretchr/testify/assert" + + "github.com/scionproto/scion/pkg/slayers/path/hummingbird" ) func TestIncPath(t *testing.T) { diff --git a/pkg/slayers/path/hummingbird/decoded_test.go b/pkg/slayers/path/hummingbird/decoded_test.go index b0ed3fc0a7..8ec3857575 100644 --- a/pkg/slayers/path/hummingbird/decoded_test.go +++ b/pkg/slayers/path/hummingbird/decoded_test.go @@ -4,9 +4,10 @@ import ( "fmt" "testing" + "github.com/stretchr/testify/assert" + "github.com/scionproto/scion/pkg/slayers/path" "github.com/scionproto/scion/pkg/slayers/path/hummingbird" - "github.com/stretchr/testify/assert" ) var testInfoFields = []path.InfoField{ diff --git a/pkg/slayers/path/hummingbird/flyoverhopfield_test.go b/pkg/slayers/path/hummingbird/flyoverhopfield_test.go index 33824ded42..804fbb797d 100644 --- a/pkg/slayers/path/hummingbird/flyoverhopfield_test.go +++ b/pkg/slayers/path/hummingbird/flyoverhopfield_test.go @@ -3,9 +3,10 @@ package hummingbird_test import ( "testing" + "github.com/stretchr/testify/assert" + "github.com/scionproto/scion/pkg/slayers/path" "github.com/scionproto/scion/pkg/slayers/path/hummingbird" - "github.com/stretchr/testify/assert" ) func TestFlyoverHopSerializeDecodeFlyover(t *testing.T) { diff --git a/pkg/slayers/path/hummingbird/mac_test.go b/pkg/slayers/path/hummingbird/mac_test.go index f5d924491a..d7f8b55166 100644 --- a/pkg/slayers/path/hummingbird/mac_test.go +++ b/pkg/slayers/path/hummingbird/mac_test.go @@ -6,9 +6,10 @@ import ( "encoding/binary" "testing" + "github.com/stretchr/testify/require" + "github.com/scionproto/scion/pkg/addr" "github.com/scionproto/scion/pkg/slayers/path/hummingbird" - "github.com/stretchr/testify/require" ) func TestDeriveAuthKey(t *testing.T) { diff --git a/pkg/slayers/path/hummingbird/raw_test.go b/pkg/slayers/path/hummingbird/raw_test.go index e135bceb5b..3d04bffe3f 100644 --- a/pkg/slayers/path/hummingbird/raw_test.go +++ b/pkg/slayers/path/hummingbird/raw_test.go @@ -4,10 +4,11 @@ import ( "fmt" "testing" - "github.com/scionproto/scion/pkg/slayers/path" - "github.com/scionproto/scion/pkg/slayers/path/hummingbird" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/scionproto/scion/pkg/slayers/path" + "github.com/scionproto/scion/pkg/slayers/path/hummingbird" ) var emptyRawTestPath = &hummingbird.Raw{ diff --git a/router/dataplane_hbird.go b/router/dataplane_hbird.go index 2ac8535621..690d6d0901 100644 --- a/router/dataplane_hbird.go +++ b/router/dataplane_hbird.go @@ -9,6 +9,7 @@ import ( "time" "github.com/google/gopacket" + "github.com/scionproto/scion/pkg/addr" "github.com/scionproto/scion/pkg/log" "github.com/scionproto/scion/pkg/private/serrors" diff --git a/router/dataplane_hbird_test.go b/router/dataplane_hbird_test.go index 4e691268d2..acf08172f0 100644 --- a/router/dataplane_hbird_test.go +++ b/router/dataplane_hbird_test.go @@ -8,6 +8,10 @@ import ( "github.com/golang/mock/gomock" "github.com/google/gopacket" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/net/ipv4" + "github.com/scionproto/scion/pkg/addr" "github.com/scionproto/scion/pkg/private/util" "github.com/scionproto/scion/pkg/private/xtest" @@ -17,9 +21,6 @@ import ( "github.com/scionproto/scion/private/topology" "github.com/scionproto/scion/router" "github.com/scionproto/scion/router/mock_router" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "golang.org/x/net/ipv4" ) func TestDataPlaneSetSecretValue(t *testing.T) { @@ -75,7 +76,8 @@ func TestProcessHbirdPacket(t *testing.T) { {HopField: path.HopField{ConsIngress: 01, ConsEgress: 0}}, } dpath.Base.PathMeta.CurrHF = 6 - dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[2].HopField) + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[2].HopField) ret := toMsg(t, spkt, dpath) if afterProcessing { ret.Addr = &net.UDPAddr{IP: dst.IP().AsSlice(), Port: topology.EndhostPort} @@ -108,7 +110,8 @@ func TestProcessHbirdPacket(t *testing.T) { {HopField: path.HopField{ConsIngress: 41, ConsEgress: 40}}, } dpath.Base.PathMeta.CurrHF = 0 - dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[0].HopField) if !afterProcessing { return toMsg(t, spkt, dpath) } @@ -143,7 +146,8 @@ func TestProcessHbirdPacket(t *testing.T) { {HopField: path.HopField{ConsIngress: 40, ConsEgress: 41}}, } dpath.Base.PathMeta.CurrHF = 3 - dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[1].HopField) if !afterProcessing { return toMsg(t, spkt, dpath) } @@ -178,7 +182,8 @@ func TestProcessHbirdPacket(t *testing.T) { } dpath.Base.PathMeta.CurrHF = 3 dpath.InfoFields[0].ConsDir = false - dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[1].HopField) if !afterProcessing { dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.Mac) return toMsg(t, spkt, dpath) @@ -212,7 +217,8 @@ func TestProcessHbirdPacket(t *testing.T) { {HopField: path.HopField{ConsIngress: 1, ConsEgress: 3}}, {HopField: path.HopField{ConsIngress: 50, ConsEgress: 51}}, } - dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[1].HopField) ret := toMsg(t, spkt, dpath) if afterProcessing { ret.Addr = &net.UDPAddr{IP: net.ParseIP("10.0.200.200").To4(), Port: 30043} @@ -260,8 +266,10 @@ func TestProcessHbirdPacket(t *testing.T) { {HopField: path.HopField{ConsIngress: 3, ConsEgress: 0}}, // IA 110 }, } - dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[2].HopField) - dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3].HopField) + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[2].HopField) + dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[3].HopField) if !afterProcessing { dpath.InfoFields[0].UpdateSegID(dpath.HopFields[2].HopField.Mac) @@ -292,7 +300,8 @@ func TestProcessHbirdPacket(t *testing.T) { {HopField: path.HopField{ConsIngress: 1, ConsEgress: 0}}, } dpath.Base.PathMeta.CurrHF = 6 - dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[1].HopField) ret := toMsg(t, spkt, dpath) return ret }, @@ -629,9 +638,11 @@ func TestProcessHbirdPacket(t *testing.T) { dpath.Base.PathMeta.SegLen[0] = 11 dpath.Base.NumLines = 11 dpath.Base.PathMeta.CurrHF = 6 - dpath.HopFields[2].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, dpath.InfoFields[0], dpath.HopFields[2], dpath.PathMeta) + dpath.HopFields[2].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, + spkt.PayloadLen, dpath.InfoFields[0], dpath.HopFields[2], dpath.PathMeta) if afterProcessing { - dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[2].HopField) + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[2].HopField) ret := toMsg(t, spkt, dpath) ret.Addr = &net.UDPAddr{IP: dst.IP().AsSlice(), Port: topology.EndhostPort} ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil @@ -668,11 +679,13 @@ func TestProcessHbirdPacket(t *testing.T) { dpath.Base.PathMeta.CurrHF = 0 dpath.Base.PathMeta.SegLen[0] = 15 dpath.NumLines = 15 - dpath.HopFields[0].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, dpath.InfoFields[0], dpath.HopFields[0], dpath.Base.PathMeta) + dpath.HopFields[0].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, + spkt.PayloadLen, dpath.InfoFields[0], dpath.HopFields[0], dpath.Base.PathMeta) if !afterProcessing { return toMsg(t, spkt, dpath) } - dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[0].HopField) _ = dpath.IncPath(hummingbird.FlyoverLines) dpath.InfoFields[0].UpdateSegID(dpath.HopFields[0].HopField.Mac) ret := toMsg(t, spkt, dpath) @@ -697,12 +710,14 @@ func TestProcessHbirdPacket(t *testing.T) { dpath.HopFields = []hummingbird.FlyoverHopField{ {HopField: path.HopField{ConsIngress: 41, ConsEgress: 40}}, {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, - {Flyover: true, HopField: path.HopField{ConsIngress: 01, ConsEgress: 0}, ResStartTime: 5, Duration: 2}, + {Flyover: true, HopField: path.HopField{ConsIngress: 01, ConsEgress: 0}, + ResStartTime: 5, Duration: 2}, } dpath.Base.PathMeta.SegLen[0] = 11 dpath.Base.NumLines = 11 dpath.Base.PathMeta.CurrHF = 6 - dpath.HopFields[2].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, dpath.InfoFields[0], dpath.HopFields[2], dpath.PathMeta) + dpath.HopFields[2].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, + spkt.PayloadLen, dpath.InfoFields[0], dpath.HopFields[2], dpath.PathMeta) ret := toMsg(t, spkt, dpath) if afterProcessing { ret.Addr = &net.UDPAddr{IP: dst.IP().AsSlice(), Port: topology.EndhostPort} @@ -729,17 +744,20 @@ func TestProcessHbirdPacket(t *testing.T) { spkt, dpath := prepHbirdMsg(now) dpath.HopFields = []hummingbird.FlyoverHopField{ {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, - {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}, + Bw: 5, ResStartTime: 123, Duration: 304}, {HopField: path.HopField{ConsIngress: 40, ConsEgress: 41}}, } dpath.Base.PathMeta.SegLen[0] = 11 dpath.Base.PathMeta.CurrHF = 3 dpath.Base.NumLines = 11 - dpath.HopFields[1].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, dpath.InfoFields[0], dpath.HopFields[1], dpath.Base.PathMeta) + dpath.HopFields[1].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, + spkt.PayloadLen, dpath.InfoFields[0], dpath.HopFields[1], dpath.Base.PathMeta) if !afterProcessing { return toMsg(t, spkt, dpath) } - dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[1].HopField) _ = dpath.IncPath(hummingbird.FlyoverLines) dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.Mac) ret := toMsg(t, spkt, dpath) @@ -766,7 +784,8 @@ func TestProcessHbirdPacket(t *testing.T) { spkt, dpath := prepHbirdMsg(now) dpath.HopFields = []hummingbird.FlyoverHopField{ {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, - {Flyover: true, HopField: path.HopField{ConsIngress: 2, ConsEgress: 1}, ResID: 42, ResStartTime: 5, Duration: 301, Bw: 16}, + {Flyover: true, HopField: path.HopField{ConsIngress: 2, ConsEgress: 1}, + ResID: 42, ResStartTime: 5, Duration: 301, Bw: 16}, {HopField: path.HopField{ConsIngress: 40, ConsEgress: 41}}, } dpath.Base.PathMeta.CurrHF = 3 @@ -774,13 +793,15 @@ func TestProcessHbirdPacket(t *testing.T) { dpath.Base.PathMeta.SegLen[0] = 11 dpath.InfoFields[0].ConsDir = false - dpath.HopFields[1].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, - dpath.InfoFields[0], dpath.HopFields[1], dpath.PathMeta) + dpath.HopFields[1].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, + spkt.PayloadLen, dpath.InfoFields[0], dpath.HopFields[1], dpath.PathMeta) if !afterProcessing { - dpath.InfoFields[0].UpdateSegID(computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField)) + dpath.InfoFields[0].UpdateSegID(computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[1].HopField)) return toMsg(t, spkt, dpath) } - dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[1].HopField) require.NoError(t, dpath.IncPath(hummingbird.FlyoverLines)) ret := toMsg(t, spkt, dpath) ret.Addr = nil @@ -859,17 +880,21 @@ func TestProcessHbirdPacket(t *testing.T) { {SegID: 0x222, ConsDir: false, Timestamp: util.TimeToSecs(now)}, }, HopFields: []hummingbird.FlyoverHopField{ - {HopField: path.HopField{ConsIngress: 0, ConsEgress: 1}}, // IA 110 - {HopField: path.HopField{ConsIngress: 31, ConsEgress: 0}}, // Src - {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 51}, ResID: 34, ResStartTime: 5, Duration: 310}, // Dst - {HopField: path.HopField{ConsIngress: 3, ConsEgress: 0}}, // IA 110 + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 1}}, // IA 110 + {HopField: path.HopField{ConsIngress: 31, ConsEgress: 0}}, // Src + {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 51}, + ResID: 34, ResStartTime: 5, Duration: 310}, // Dst + {HopField: path.HopField{ConsIngress: 3, ConsEgress: 0}}, // IA 110 }, } - dpath.HopFields[2].HopField.Mac = computeAggregateMacXover(t, key, sv, spkt.DstIA, spkt.PayloadLen, 3, 51, + dpath.HopFields[2].HopField.Mac = computeAggregateMacXover(t, key, sv, spkt.DstIA, + spkt.PayloadLen, 3, 51, dpath.InfoFields[0], dpath.HopFields[2], dpath.PathMeta) - dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3].HopField) + dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[3].HopField) if !afterProcessing { - dpath.InfoFields[0].UpdateSegID(computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[2].HopField)) + dpath.InfoFields[0].UpdateSegID(computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[2].HopField)) return toMsg(t, spkt, dpath) } @@ -879,10 +904,11 @@ func TestProcessHbirdPacket(t *testing.T) { dpath.HopFields[3].ResStartTime = 5 dpath.HopFields[3].Duration = 310 - dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[2].HopField) - dpath.HopFields[3].HopField.Mac = computeAggregateMacXover(t, key, sv, spkt.DstIA, spkt.PayloadLen, 3, 51, - dpath.InfoFields[1], dpath.HopFields[3], dpath.PathMeta) - //dpath.HopFields[3].Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3]) + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[2].HopField) + dpath.HopFields[3].HopField.Mac = computeAggregateMacXover(t, key, sv, + spkt.DstIA, spkt.PayloadLen, 3, 51, dpath.InfoFields[1], dpath.HopFields[3], + dpath.PathMeta) dpath.PathMeta.SegLen[0] -= 2 dpath.PathMeta.SegLen[1] += 2 require.NoError(t, dpath.IncPath(hummingbird.FlyoverLines)) @@ -926,20 +952,19 @@ func TestProcessHbirdPacket(t *testing.T) { {SegID: 0x222, ConsDir: true, Timestamp: util.TimeToSecs(now)}, }, HopFields: []hummingbird.FlyoverHopField{ - {HopField: path.HopField{ConsIngress: 0, ConsEgress: 1}}, // IA 110 - {Flyover: true, HopField: path.HopField{ConsIngress: 51, ConsEgress: 0}, Bw: 5, ResStartTime: 5, Duration: 310}, // Src - {HopField: path.HopField{ConsIngress: 0, ConsEgress: 31}}, // Dst - {HopField: path.HopField{ConsIngress: 3, ConsEgress: 0}}, // IA 110 + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 1}}, // IA 110 + {Flyover: true, HopField: path.HopField{ConsIngress: 51, ConsEgress: 0}, + Bw: 5, ResStartTime: 5, Duration: 310}, // Src + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 31}}, // Dst + {HopField: path.HopField{ConsIngress: 3, ConsEgress: 0}}, // IA 110 }, } - dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[2].HopField) - dpath.HopFields[1].HopField.Mac = computeAggregateMacXover(t, key, sv, spkt.DstIA, spkt.PayloadLen, 51, 31, + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[2].HopField) + dpath.HopFields[1].HopField.Mac = computeAggregateMacXover(t, key, sv, + spkt.DstIA, spkt.PayloadLen, 51, 31, dpath.InfoFields[0], dpath.HopFields[1], dpath.PathMeta) - // dpath.HopFields[3].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, dpath.InfoFields[1], dpath.HopFields[3], dpath.PathMeta) if !afterProcessing { - //dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.Mac) - // dpath.PathMeta.CurrHF = 6 - // dpath.PathMeta.CurrINF = 1 return toMsg(t, spkt, dpath) } @@ -949,11 +974,11 @@ func TestProcessHbirdPacket(t *testing.T) { dpath.HopFields[2].ResStartTime = 5 dpath.HopFields[2].Duration = 310 - dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) - dpath.HopFields[2].HopField.Mac = computeAggregateMacXover(t, key, sv, spkt.DstIA, spkt.PayloadLen, 51, 31, + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[1].HopField) + dpath.HopFields[2].HopField.Mac = computeAggregateMacXover(t, key, sv, + spkt.DstIA, spkt.PayloadLen, 51, 31, dpath.InfoFields[1], dpath.HopFields[2], dpath.PathMeta) - //dpath.HopFields[2].Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[2]) - //dpath.HopFields[3].Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3]) dpath.PathMeta.SegLen[0] -= 2 dpath.PathMeta.SegLen[1] += 2 require.NoError(t, dpath.IncPath(hummingbird.HopLines)) @@ -1002,19 +1027,17 @@ func TestProcessHbirdPacket(t *testing.T) { {SegID: 0x222, ConsDir: true, Timestamp: util.TimeToSecs(now)}, }, HopFields: []hummingbird.FlyoverHopField{ - {HopField: path.HopField{ConsIngress: 0, ConsEgress: 1}}, // Src - {HopField: path.HopField{ConsIngress: 51, ConsEgress: 0}}, // IA 110 - {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 31}, Bw: 5, ResStartTime: 5, Duration: 310}, // IA 110 - {HopField: path.HopField{ConsIngress: 3, ConsEgress: 0}}, // Dst + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 1}}, // Src + {HopField: path.HopField{ConsIngress: 51, ConsEgress: 0}}, // IA 110 + {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 31}, + Bw: 5, ResStartTime: 5, Duration: 310}, // IA 110 + {HopField: path.HopField{ConsIngress: 3, ConsEgress: 0}}, // Dst }, } - dpath.HopFields[2].HopField.Mac = computeAggregateMacXover(t, key, sv, spkt.DstIA, spkt.PayloadLen, 51, 31, + dpath.HopFields[2].HopField.Mac = computeAggregateMacXover(t, key, sv, + spkt.DstIA, spkt.PayloadLen, 51, 31, dpath.InfoFields[1], dpath.HopFields[2], dpath.PathMeta) - // dpath.HopFields[3].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, dpath.InfoFields[1], dpath.HopFields[3], dpath.PathMeta) if !afterProcessing { - //dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.Mac) - // dpath.PathMeta.CurrHF = 6 - // dpath.PathMeta.CurrINF = 1 ret := toMsg(t, spkt, dpath) ret.Addr = &net.UDPAddr{IP: net.ParseIP("10.0.200.200").To4(), Port: 30043} return ret @@ -1025,7 +1048,8 @@ func TestProcessHbirdPacket(t *testing.T) { dpath.HopFields[1].ResStartTime = 5 dpath.HopFields[1].Duration = 310 - dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[2].HopField) + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[2].HopField) dpath.InfoFields[1].UpdateSegID(dpath.HopFields[2].HopField.Mac) require.NoError(t, dpath.IncPath(hummingbird.FlyoverLines)) dpath.PathMeta.SegLen[0] += 2 @@ -1077,7 +1101,8 @@ func TestProcessHbirdPacket(t *testing.T) { }, HopFields: []hummingbird.FlyoverHopField{ {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, - {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}, + Bw: 5, ResStartTime: 123, Duration: 304}, {HopField: path.HopField{ConsIngress: 40, ConsEgress: 41}}, }, } @@ -1090,8 +1115,8 @@ func TestProcessHbirdPacket(t *testing.T) { // parent hop of HF[1] in the original beaconned segment, // which is not in the path). So, we use one from an // info field because computeMAC makes that easy. - dpath.HopFields[1].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, - dpath.InfoFields[1], dpath.HopFields[1], dpath.PathMeta) + dpath.HopFields[1].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, + spkt.PayloadLen, dpath.InfoFields[1], dpath.HopFields[1], dpath.PathMeta) dpath.HopFields[2].HopField.Mac = computeMAC( t, sv, dpath.InfoFields[1], dpath.HopFields[2].HopField) if !afterProcessing { @@ -1149,7 +1174,8 @@ func TestProcessHbirdPacket(t *testing.T) { }, HopFields: []hummingbird.FlyoverHopField{ {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, - {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}, + Bw: 5, ResStartTime: 123, Duration: 304}, {HopField: path.HopField{ConsIngress: 40, ConsEgress: 41}}, }, } @@ -1164,8 +1190,8 @@ func TestProcessHbirdPacket(t *testing.T) { // info field because computeMAC makes that easy. dpath.HopFields[0].HopField.Mac = computeMAC( t, sv, dpath.InfoFields[0], dpath.HopFields[0].HopField) - dpath.HopFields[1].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, - dpath.InfoFields[0], dpath.HopFields[1], dpath.PathMeta) + dpath.HopFields[1].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, + spkt.PayloadLen, dpath.InfoFields[0], dpath.HopFields[1], dpath.PathMeta) // We're going against construction order, so the accumulator // value is that of the previous hop in traversal order. The @@ -1233,7 +1259,8 @@ func TestProcessHbirdPacket(t *testing.T) { HopFields: []hummingbird.FlyoverHopField{ {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, {HopField: path.HopField{ConsIngress: 40, ConsEgress: 41}}, - {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}, + Bw: 5, ResStartTime: 123, Duration: 304}, {HopField: path.HopField{ConsIngress: 50, ConsEgress: 51}}, // There has to be a 4th hop to make // the 3rd router agree that the packet @@ -1251,8 +1278,8 @@ func TestProcessHbirdPacket(t *testing.T) { // info field because computeMAC makes that easy. dpath.HopFields[1].HopField.Mac = computeMAC( t, sv, dpath.InfoFields[1], dpath.HopFields[1].HopField) - dpath.HopFields[2].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, - dpath.InfoFields[1], dpath.HopFields[2], dpath.PathMeta) + dpath.HopFields[2].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, + spkt.PayloadLen, dpath.InfoFields[1], dpath.HopFields[2], dpath.PathMeta) if !afterProcessing { // The SegID we provide is that of HF[2] which happens to be SEG[1]'s SegID, // so, already set. @@ -1311,7 +1338,8 @@ func TestProcessHbirdPacket(t *testing.T) { }, HopFields: []hummingbird.FlyoverHopField{ {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, - {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}, + Bw: 5, ResStartTime: 123, Duration: 304}, {HopField: path.HopField{ConsIngress: 40, ConsEgress: 41}}, {HopField: path.HopField{ConsIngress: 50, ConsEgress: 51}}, // The second segment (4th hop) has to be @@ -1327,8 +1355,8 @@ func TestProcessHbirdPacket(t *testing.T) { // in the original beaconned segment, which is not in // the path). So, we use one from an info field because // computeMAC makes that easy. - dpath.HopFields[1].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, - dpath.InfoFields[0], dpath.HopFields[1], dpath.PathMeta) + dpath.HopFields[1].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, + spkt.PayloadLen, dpath.InfoFields[0], dpath.HopFields[1], dpath.PathMeta) dpath.HopFields[2].HopField.Mac = computeMAC( t, sv, dpath.InfoFields[0], dpath.HopFields[2].HopField) @@ -1412,7 +1440,8 @@ func TestHbirdPacketPath(t *testing.T) { }{ "two hops consdir": { mockMsg: func() *ipv4.Message { - spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:111"), xtest.MustParseIA("1-ff00:0:110")) + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:111"), + xtest.MustParseIA("1-ff00:0:110")) dst := addr.MustParseHost("10.0.100.100") _ = spkt.SetDstAddr(dst) @@ -1438,9 +1467,12 @@ func TestHbirdPacketPath(t *testing.T) { }, } // Compute MACs and increase SegID while doing so - dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[0].HopField) + dpath.InfoFields[0].UpdateSegID(dpath.HopFields[0].HopField.Mac) - dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[1].HopField) // Reset SegID to original value dpath.InfoFields[0].SegID = 0x111 ret := toMsg(t, spkt, dpath) @@ -1475,7 +1507,8 @@ func TestHbirdPacketPath(t *testing.T) { }, "two hops non consdir": { mockMsg: func() *ipv4.Message { - spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), xtest.MustParseIA("1-ff00:0:111")) + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), + xtest.MustParseIA("1-ff00:0:111")) dst := addr.MustParseHost("10.0.100.100") _ = spkt.SetDstAddr(dst) @@ -1501,9 +1534,12 @@ func TestHbirdPacketPath(t *testing.T) { }, } // Compute MACs and increase SegID while doing so - dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[1].HopField) + dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.Mac) - dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[0].HopField) //dpath.InfoFields[0].UpdateSegID(dpath.HopFields[0].HopField.Mac) ret := toMsg(t, spkt, dpath) @@ -1538,7 +1574,8 @@ func TestHbirdPacketPath(t *testing.T) { }, "six hops astransit xover consdir": { mockMsg: func() *ipv4.Message { - spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:111"), xtest.MustParseIA("3-ff00:0:333")) + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:111"), + xtest.MustParseIA("3-ff00:0:333")) dst := addr.MustParseHost("10.0.100.100") _ = spkt.SetDstAddr(dst) @@ -1569,17 +1606,23 @@ func TestHbirdPacketPath(t *testing.T) { }, } // Compute MACs and increase SegID while doing so - dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[0].HopField) dpath.InfoFields[0].UpdateSegID(dpath.HopFields[0].HopField.Mac) - dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[1].HopField) dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.Mac) - dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[2].HopField) + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[2].HopField) - dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3].HopField) + dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[3].HopField) dpath.InfoFields[1].UpdateSegID(dpath.HopFields[3].HopField.Mac) - dpath.HopFields[4].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[4].HopField) + dpath.HopFields[4].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[4].HopField) dpath.InfoFields[1].UpdateSegID(dpath.HopFields[4].HopField.Mac) - dpath.HopFields[5].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[5].HopField) + dpath.HopFields[5].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[5].HopField) // Reset SegID to original value dpath.InfoFields[0].SegID = 0x111 dpath.InfoFields[1].SegID = 0x222 @@ -1678,7 +1721,8 @@ func TestHbirdPacketPath(t *testing.T) { }, "six hops astransit xover non consdir": { mockMsg: func() *ipv4.Message { - spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:111"), xtest.MustParseIA("3-ff00:0:333")) + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:111"), + xtest.MustParseIA("3-ff00:0:333")) dst := addr.MustParseHost("10.0.100.100") _ = spkt.SetDstAddr(dst) @@ -1709,17 +1753,23 @@ func TestHbirdPacketPath(t *testing.T) { }, } // Compute MACs and increase SegID while doing so - dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[2].HopField) + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[2].HopField) dpath.InfoFields[0].UpdateSegID(dpath.HopFields[2].HopField.Mac) - dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[1].HopField) dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.Mac) - dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[0].HopField) - dpath.HopFields[5].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[5].HopField) + dpath.HopFields[5].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[5].HopField) dpath.InfoFields[1].UpdateSegID(dpath.HopFields[5].HopField.Mac) - dpath.HopFields[4].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[4].HopField) + dpath.HopFields[4].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[4].HopField) dpath.InfoFields[1].UpdateSegID(dpath.HopFields[4].HopField.Mac) - dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3].HopField) + dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[3].HopField) // Reset SegID to original value ret := toMsg(t, spkt, dpath) return ret @@ -1817,7 +1867,8 @@ func TestHbirdPacketPath(t *testing.T) { "six hops brtransit xover mixed consdir": { // up segment non consdir, down segment consdir mockMsg: func() *ipv4.Message { - spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:111"), xtest.MustParseIA("3-ff00:0:333")) + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:111"), + xtest.MustParseIA("3-ff00:0:333")) dst := addr.MustParseHost("10.0.100.100") _ = spkt.SetDstAddr(dst) @@ -1848,17 +1899,23 @@ func TestHbirdPacketPath(t *testing.T) { }, } // Compute MACs and increase SegID while doing so - dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[2].HopField) + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[2].HopField) dpath.InfoFields[0].UpdateSegID(dpath.HopFields[2].HopField.Mac) - dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[1].HopField) dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.Mac) - dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[0].HopField) - dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3].HopField) + dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[3].HopField) dpath.InfoFields[1].UpdateSegID(dpath.HopFields[3].HopField.Mac) - dpath.HopFields[4].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[4].HopField) + dpath.HopFields[4].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[4].HopField) dpath.InfoFields[1].UpdateSegID(dpath.HopFields[4].HopField.Mac) - dpath.HopFields[5].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[5].HopField) + dpath.HopFields[5].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[5].HopField) // Reset SegID to original value dpath.InfoFields[1].SegID = 0x222 ret := toMsg(t, spkt, dpath) @@ -1930,7 +1987,8 @@ func TestHbirdPacketPath(t *testing.T) { // two crossovers, first crossover is brtransit, second one is astransit // core segment is non consdir mockMsg: func() *ipv4.Message { - spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), xtest.MustParseIA("1-ff00:0:113")) + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), + xtest.MustParseIA("1-ff00:0:113")) dst := addr.MustParseHost("10.0.100.100") _ = spkt.SetDstAddr(dst) @@ -1962,17 +2020,23 @@ func TestHbirdPacketPath(t *testing.T) { }, } // Compute MACs and increase SegID while doing so - dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[0].HopField) dpath.InfoFields[0].UpdateSegID(dpath.HopFields[0].HopField.Mac) - dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[1].HopField) - dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3].HopField) + dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[3].HopField) dpath.InfoFields[1].UpdateSegID(dpath.HopFields[3].HopField.Mac) - dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[2].HopField) + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[2].HopField) - dpath.HopFields[4].HopField.Mac = computeMAC(t, key, dpath.InfoFields[2], dpath.HopFields[4].HopField) + dpath.HopFields[4].HopField.Mac = computeMAC(t, key, dpath.InfoFields[2], + dpath.HopFields[4].HopField) dpath.InfoFields[2].UpdateSegID(dpath.HopFields[4].HopField.Mac) - dpath.HopFields[5].HopField.Mac = computeMAC(t, key, dpath.InfoFields[2], dpath.HopFields[5].HopField) + dpath.HopFields[5].HopField.Mac = computeMAC(t, key, dpath.InfoFields[2], + dpath.HopFields[5].HopField) // Reset SegID to original value dpath.InfoFields[0].SegID = 0x111 dpath.InfoFields[2].SegID = 0x333 @@ -2040,7 +2104,8 @@ func TestHbirdPacketPath(t *testing.T) { }, "three hops peering brtransit consdir": { mockMsg: func() *ipv4.Message { - spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), xtest.MustParseIA("1-ff00:0:113")) + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), + xtest.MustParseIA("1-ff00:0:113")) dst := addr.MustParseHost("10.0.100.100") _ = spkt.SetDstAddr(dst) @@ -2068,11 +2133,15 @@ func TestHbirdPacketPath(t *testing.T) { }, } // Compute MACs and increase SegID while doing so - dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[0].HopField) - dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[1].HopField) - // No Segment update here as the second hop of a peering path uses the same segID as it's following hop - dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[2].HopField) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[1].HopField) + // No Segment update here as the second hop of a peering path + // Uses the same segID as it's following hop + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[2].HopField) ret := toMsg(t, spkt, dpath) return ret @@ -2114,7 +2183,8 @@ func TestHbirdPacketPath(t *testing.T) { }, "three hops peering brtransit non consdir": { mockMsg: func() *ipv4.Message { - spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), xtest.MustParseIA("1-ff00:0:113")) + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), + xtest.MustParseIA("1-ff00:0:113")) dst := addr.MustParseHost("10.0.100.100") _ = spkt.SetDstAddr(dst) @@ -2142,12 +2212,16 @@ func TestHbirdPacketPath(t *testing.T) { }, } // Compute MACs and increase SegID while doing so - dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[0].HopField) - dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[2].HopField) + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[2].HopField) dpath.InfoFields[1].UpdateSegID(dpath.HopFields[2].HopField.Mac) - dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[1].HopField) - // No Segment update here as the second hop of a peering path uses the same segID as it's following hop + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[1].HopField) + // No Segment update here as the second hop of a peering path + // Uses the same segID as it's following hop ret := toMsg(t, spkt, dpath) return ret @@ -2189,7 +2263,8 @@ func TestHbirdPacketPath(t *testing.T) { }, "four hops peering astransit consdir": { mockMsg: func() *ipv4.Message { - spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), xtest.MustParseIA("1-ff00:0:113")) + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), + xtest.MustParseIA("1-ff00:0:113")) dst := addr.MustParseHost("10.0.100.100") _ = spkt.SetDstAddr(dst) @@ -2218,13 +2293,18 @@ func TestHbirdPacketPath(t *testing.T) { }, } // Compute MACs and increase SegID while doing so - dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[0].HopField) dpath.InfoFields[0].UpdateSegID(dpath.HopFields[0].HopField.Mac) - dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) - - dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[2].HopField) - // No Segment update here as the second hop of a peering path uses the same segID as it's following hop - dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3].HopField) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[1].HopField) + + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[2].HopField) + // No Segment update here + // the second hop of a peering path uses the same segID as it's following hop + dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[3].HopField) // reset segID dpath.InfoFields[0].SegID = 0x111 @@ -2305,7 +2385,8 @@ func TestHbirdPacketPath(t *testing.T) { }, "four hops peering astransit non consdir": { mockMsg: func() *ipv4.Message { - spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), xtest.MustParseIA("1-ff00:0:113")) + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), + xtest.MustParseIA("1-ff00:0:113")) dst := addr.MustParseHost("10.0.100.100") _ = spkt.SetDstAddr(dst) @@ -2334,13 +2415,18 @@ func TestHbirdPacketPath(t *testing.T) { }, } // Compute MACs and increase SegID while doing so - dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) - // No Segment update here as the second hop of a peering path uses the same segID as it's following hop - dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) - - dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3].HopField) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[1].HopField) + // No Segment update here + // the second hop of a peering path uses the same segID as it's following hop + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[0].HopField) + + dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[3].HopField) dpath.InfoFields[1].UpdateSegID(dpath.HopFields[3].HopField.Mac) - dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[2].HopField) + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[2].HopField) ret := toMsg(t, spkt, dpath) return ret @@ -2419,7 +2505,8 @@ func TestHbirdPacketPath(t *testing.T) { }, "two hops consdir flyovers": { mockMsg: func() *ipv4.Message { - spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:111"), xtest.MustParseIA("1-ff00:0:110")) + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:111"), + xtest.MustParseIA("1-ff00:0:110")) dst := addr.MustParseHost("10.0.100.100") _ = spkt.SetDstAddr(dst) @@ -2440,14 +2527,18 @@ func TestHbirdPacketPath(t *testing.T) { }, HopFields: []hummingbird.FlyoverHopField{ - {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 40}, Bw: 5, ResStartTime: 123, Duration: 304}, - {Flyover: true, HopField: path.HopField{ConsIngress: 01, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 40}, + Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 01, ConsEgress: 0}, + Bw: 5, ResStartTime: 123, Duration: 304}, }, } // Compute MACs and increase SegID while doing so - dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[0].HopField) dpath.InfoFields[0].UpdateSegID(dpath.HopFields[0].HopField.Mac) - dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[1].HopField) // add flyover macs aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 0, 40, dpath.InfoFields[0], &dpath.HopFields[0], dpath.PathMeta) @@ -2487,7 +2578,8 @@ func TestHbirdPacketPath(t *testing.T) { }, "two hops non consdir flyovers": { mockMsg: func() *ipv4.Message { - spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), xtest.MustParseIA("1-ff00:0:111")) + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), + xtest.MustParseIA("1-ff00:0:111")) dst := addr.MustParseHost("10.0.100.100") _ = spkt.SetDstAddr(dst) @@ -2508,14 +2600,18 @@ func TestHbirdPacketPath(t *testing.T) { }, HopFields: []hummingbird.FlyoverHopField{ - {Flyover: true, HopField: path.HopField{ConsIngress: 01, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, - {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 40}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 01, ConsEgress: 0}, + Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 40}, + Bw: 5, ResStartTime: 123, Duration: 304}, }, } // Compute MACs and increase SegID while doing so - dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[1].HopField) dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.Mac) - dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[0].HopField) // aggregate macs aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 0, 1, dpath.InfoFields[0], &dpath.HopFields[0], dpath.PathMeta) @@ -2554,7 +2650,8 @@ func TestHbirdPacketPath(t *testing.T) { }, "six hops astransit xover consdir flyovers": { mockMsg: func() *ipv4.Message { - spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:111"), xtest.MustParseIA("3-ff00:0:333")) + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:111"), + xtest.MustParseIA("3-ff00:0:333")) dst := addr.MustParseHost("10.0.100.100") _ = spkt.SetDstAddr(dst) @@ -2576,26 +2673,37 @@ func TestHbirdPacketPath(t *testing.T) { }, HopFields: []hummingbird.FlyoverHopField{ - {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 40}, Bw: 5, ResStartTime: 123, Duration: 304}, - {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 31}, Bw: 5, ResStartTime: 123, Duration: 304}, - {Flyover: true, HopField: path.HopField{ConsIngress: 5, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 40}, + Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 31}, + Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 5, ConsEgress: 0}, + Bw: 5, ResStartTime: 123, Duration: 304}, {HopField: path.HopField{ConsIngress: 0, ConsEgress: 7}}, - {Flyover: true, HopField: path.HopField{ConsIngress: 11, ConsEgress: 8}, Bw: 5, ResStartTime: 123, Duration: 304}, - {Flyover: true, HopField: path.HopField{ConsIngress: 3, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 11, ConsEgress: 8}, + Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 3, ConsEgress: 0}, + Bw: 5, ResStartTime: 123, Duration: 304}, }, } // Compute MACs and increase SegID while doing so - dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[0].HopField) dpath.InfoFields[0].UpdateSegID(dpath.HopFields[0].HopField.Mac) - dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[1].HopField) dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.Mac) - dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[2].HopField) + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[2].HopField) - dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3].HopField) + dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[3].HopField) dpath.InfoFields[1].UpdateSegID(dpath.HopFields[3].HopField.Mac) - dpath.HopFields[4].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[4].HopField) + dpath.HopFields[4].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[4].HopField) dpath.InfoFields[1].UpdateSegID(dpath.HopFields[4].HopField.Mac) - dpath.HopFields[5].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[5].HopField) + dpath.HopFields[5].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[5].HopField) // Reset SegID to original value dpath.InfoFields[0].SegID = 0x111 dpath.InfoFields[1].SegID = 0x222 @@ -2705,7 +2813,8 @@ func TestHbirdPacketPath(t *testing.T) { }, "six hops astransit xover non consdir flyovers": { mockMsg: func() *ipv4.Message { - spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:111"), xtest.MustParseIA("3-ff00:0:333")) + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:111"), + xtest.MustParseIA("3-ff00:0:333")) dst := addr.MustParseHost("10.0.100.100") _ = spkt.SetDstAddr(dst) @@ -2727,26 +2836,37 @@ func TestHbirdPacketPath(t *testing.T) { }, HopFields: []hummingbird.FlyoverHopField{ - {Flyover: true, HopField: path.HopField{ConsIngress: 40, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, - {Flyover: true, HopField: path.HopField{ConsIngress: 31, ConsEgress: 1}, Bw: 5, ResStartTime: 123, Duration: 304}, - {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 5}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 40, ConsEgress: 0}, + Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 31, ConsEgress: 1}, + Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 5}, + Bw: 5, ResStartTime: 123, Duration: 304}, {HopField: path.HopField{ConsIngress: 7, ConsEgress: 0}}, - {Flyover: true, HopField: path.HopField{ConsIngress: 8, ConsEgress: 11}, Bw: 5, ResStartTime: 123, Duration: 304}, - {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 3}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 8, ConsEgress: 11}, + Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 3}, + Bw: 5, ResStartTime: 123, Duration: 304}, }, } // Compute MACs and increase SegID while doing so - dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[2].HopField) + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[2].HopField) dpath.InfoFields[0].UpdateSegID(dpath.HopFields[2].HopField.Mac) - dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[1].HopField) dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.Mac) - dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[0].HopField) - dpath.HopFields[5].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[5].HopField) + dpath.HopFields[5].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[5].HopField) dpath.InfoFields[1].UpdateSegID(dpath.HopFields[5].HopField.Mac) - dpath.HopFields[4].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[4].HopField) + dpath.HopFields[4].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[4].HopField) dpath.InfoFields[1].UpdateSegID(dpath.HopFields[4].HopField.Mac) - dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3].HopField) + dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[3].HopField) // aggregate with flyover macs aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 0, 40, dpath.InfoFields[0], &dpath.HopFields[0], dpath.PathMeta) @@ -2854,7 +2974,8 @@ func TestHbirdPacketPath(t *testing.T) { "six hops brtransit xover mixed consdir flyovers": { // up segment non consdir, down segment consdir mockMsg: func() *ipv4.Message { - spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:111"), xtest.MustParseIA("3-ff00:0:333")) + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:111"), + xtest.MustParseIA("3-ff00:0:333")) dst := addr.MustParseHost("10.0.100.100") _ = spkt.SetDstAddr(dst) @@ -2876,26 +2997,37 @@ func TestHbirdPacketPath(t *testing.T) { }, HopFields: []hummingbird.FlyoverHopField{ - {Flyover: true, HopField: path.HopField{ConsIngress: 40, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, - {Flyover: true, HopField: path.HopField{ConsIngress: 31, ConsEgress: 1}, Bw: 5, ResStartTime: 123, Duration: 304}, - {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 5}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 40, ConsEgress: 0}, + Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 31, ConsEgress: 1}, + Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 5}, + Bw: 5, ResStartTime: 123, Duration: 304}, {HopField: path.HopField{ConsIngress: 0, ConsEgress: 7}}, - {Flyover: true, HopField: path.HopField{ConsIngress: 11, ConsEgress: 8}, Bw: 5, ResStartTime: 123, Duration: 304}, - {Flyover: true, HopField: path.HopField{ConsIngress: 3, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 11, ConsEgress: 8}, + Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 3, ConsEgress: 0}, + Bw: 5, ResStartTime: 123, Duration: 304}, }, } // Compute MACs and increase SegID while doing so - dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[2].HopField) + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[2].HopField) dpath.InfoFields[0].UpdateSegID(dpath.HopFields[2].HopField.Mac) - dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[1].HopField) dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.Mac) - dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[0].HopField) - dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3].HopField) + dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[3].HopField) dpath.InfoFields[1].UpdateSegID(dpath.HopFields[3].HopField.Mac) - dpath.HopFields[4].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[4].HopField) + dpath.HopFields[4].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[4].HopField) dpath.InfoFields[1].UpdateSegID(dpath.HopFields[4].HopField.Mac) - dpath.HopFields[5].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[5].HopField) + dpath.HopFields[5].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[5].HopField) // Reset SegID to original value dpath.InfoFields[1].SegID = 0x222 @@ -2980,7 +3112,8 @@ func TestHbirdPacketPath(t *testing.T) { // two crossovers, first crossover is brtransit, second one is astransit // core segment is non consdir mockMsg: func() *ipv4.Message { - spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), xtest.MustParseIA("1-ff00:0:113")) + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), + xtest.MustParseIA("1-ff00:0:113")) dst := addr.MustParseHost("10.0.100.100") _ = spkt.SetDstAddr(dst) @@ -3003,26 +3136,36 @@ func TestHbirdPacketPath(t *testing.T) { }, HopFields: []hummingbird.FlyoverHopField{ - {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 40}, Bw: 5, ResStartTime: 123, Duration: 304}, - {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 40}, + Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 0}, + Bw: 5, ResStartTime: 123, Duration: 304}, {HopField: path.HopField{ConsIngress: 5, ConsEgress: 0}}, - {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 31}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 31}, + Bw: 5, ResStartTime: 123, Duration: 304}, {HopField: path.HopField{ConsIngress: 0, ConsEgress: 8}}, - {Flyover: true, HopField: path.HopField{ConsIngress: 3, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 3, ConsEgress: 0}, + Bw: 5, ResStartTime: 123, Duration: 304}, }, } // Compute MACs and increase SegID while doing so - dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[0].HopField) dpath.InfoFields[0].UpdateSegID(dpath.HopFields[0].HopField.Mac) - dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[1].HopField) - dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3].HopField) + dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[3].HopField) dpath.InfoFields[1].UpdateSegID(dpath.HopFields[3].HopField.Mac) - dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[2].HopField) + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[2].HopField) - dpath.HopFields[4].HopField.Mac = computeMAC(t, key, dpath.InfoFields[2], dpath.HopFields[4].HopField) + dpath.HopFields[4].HopField.Mac = computeMAC(t, key, dpath.InfoFields[2], + dpath.HopFields[4].HopField) dpath.InfoFields[2].UpdateSegID(dpath.HopFields[4].HopField.Mac) - dpath.HopFields[5].HopField.Mac = computeMAC(t, key, dpath.InfoFields[2], dpath.HopFields[5].HopField) + dpath.HopFields[5].HopField.Mac = computeMAC(t, key, dpath.InfoFields[2], + dpath.HopFields[5].HopField) // Reset SegID to original value dpath.InfoFields[0].SegID = 0x111 dpath.InfoFields[2].SegID = 0x333 @@ -3099,7 +3242,8 @@ func TestHbirdPacketPath(t *testing.T) { }, "three hops peering brtransit consdir flyovers": { mockMsg: func() *ipv4.Message { - spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), xtest.MustParseIA("1-ff00:0:113")) + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), + xtest.MustParseIA("1-ff00:0:113")) dst := addr.MustParseHost("10.0.100.100") _ = spkt.SetDstAddr(dst) @@ -3121,21 +3265,31 @@ func TestHbirdPacketPath(t *testing.T) { }, HopFields: []hummingbird.FlyoverHopField{ - {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 40}, Bw: 5, ResStartTime: 123, Duration: 304}, - {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}, Bw: 5, ResStartTime: 123, Duration: 304}, - {Flyover: true, HopField: path.HopField{ConsIngress: 5, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 40}, + Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}, + Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 5, ConsEgress: 0}, + Bw: 5, ResStartTime: 123, Duration: 304}, }, } // Compute MACs and increase SegID while doing so - dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[0].HopField) - dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[1].HopField) - // No Segment update here as the second hop of a peering path uses the same segID as it's following hop - dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[2].HopField) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[1].HopField) + // No Segment update here + // The second hop of a peering path uses the same segID as it's following hop + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[2].HopField) - aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 0, 40, dpath.InfoFields[0], &dpath.HopFields[0], dpath.PathMeta) - aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 1, 2, dpath.InfoFields[1], &dpath.HopFields[1], dpath.PathMeta) - aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 5, 0, dpath.InfoFields[1], &dpath.HopFields[2], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 0, 40, + dpath.InfoFields[0], &dpath.HopFields[0], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 1, 2, + dpath.InfoFields[1], &dpath.HopFields[1], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 5, 0, + dpath.InfoFields[1], &dpath.HopFields[2], dpath.PathMeta) ret := toMsg(t, spkt, dpath) return ret @@ -3177,7 +3331,8 @@ func TestHbirdPacketPath(t *testing.T) { }, "three hops peering brtransit non consdir flyovers": { mockMsg: func() *ipv4.Message { - spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), xtest.MustParseIA("1-ff00:0:113")) + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), + xtest.MustParseIA("1-ff00:0:113")) dst := addr.MustParseHost("10.0.100.100") _ = spkt.SetDstAddr(dst) @@ -3199,22 +3354,32 @@ func TestHbirdPacketPath(t *testing.T) { }, HopFields: []hummingbird.FlyoverHopField{ - {Flyover: true, HopField: path.HopField{ConsIngress: 40, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, - {Flyover: true, HopField: path.HopField{ConsIngress: 2, ConsEgress: 1}, Bw: 5, ResStartTime: 123, Duration: 304}, - {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 5}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 40, ConsEgress: 0}, + Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 2, ConsEgress: 1}, + Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 5}, + Bw: 5, ResStartTime: 123, Duration: 304}, }, } // Compute MACs and increase SegID while doing so - dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[0].HopField) - dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[2].HopField) + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[2].HopField) dpath.InfoFields[1].UpdateSegID(dpath.HopFields[2].HopField.Mac) - dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[1].HopField) - // No Segment update here as the second hop of a peering path uses the same segID as it's following hop + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[1].HopField) + // No Segment update here + // The second hop of a peering path uses the same segID as it's following hop - aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 0, 40, dpath.InfoFields[0], &dpath.HopFields[0], dpath.PathMeta) - aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 1, 2, dpath.InfoFields[1], &dpath.HopFields[1], dpath.PathMeta) - aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 5, 0, dpath.InfoFields[1], &dpath.HopFields[2], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 0, 40, + dpath.InfoFields[0], &dpath.HopFields[0], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 1, 2, + dpath.InfoFields[1], &dpath.HopFields[1], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 5, 0, + dpath.InfoFields[1], &dpath.HopFields[2], dpath.PathMeta) ret := toMsg(t, spkt, dpath) return ret @@ -3256,7 +3421,8 @@ func TestHbirdPacketPath(t *testing.T) { }, "four hops peering astransit consdir flyovers": { mockMsg: func() *ipv4.Message { - spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), xtest.MustParseIA("1-ff00:0:113")) + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), + xtest.MustParseIA("1-ff00:0:113")) dst := addr.MustParseHost("10.0.100.100") _ = spkt.SetDstAddr(dst) @@ -3278,27 +3444,40 @@ func TestHbirdPacketPath(t *testing.T) { }, HopFields: []hummingbird.FlyoverHopField{ - {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 40}, Bw: 5, ResStartTime: 123, Duration: 304}, - {Flyover: true, HopField: path.HopField{ConsIngress: 31, ConsEgress: 7}, Bw: 5, ResStartTime: 123, Duration: 304}, - {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}, Bw: 5, ResStartTime: 123, Duration: 304}, - {Flyover: true, HopField: path.HopField{ConsIngress: 5, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 40}, + Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 31, ConsEgress: 7}, + Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}, + Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 5, ConsEgress: 0}, + Bw: 5, ResStartTime: 123, Duration: 304}, }, } // Compute MACs and increase SegID while doing so - dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[0].HopField) dpath.InfoFields[0].UpdateSegID(dpath.HopFields[0].HopField.Mac) - dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) - - dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[2].HopField) - // No Segment update here as the second hop of a peering path uses the same segID as it's following hop - dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3].HopField) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[1].HopField) + + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[2].HopField) + // No Segment update here + // The second hop of a peering path uses the same segID as it's following hop + dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[3].HopField) // reset segID dpath.InfoFields[0].SegID = 0x111 - aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 0, 40, dpath.InfoFields[0], &dpath.HopFields[0], dpath.PathMeta) - aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 31, 7, dpath.InfoFields[0], &dpath.HopFields[1], dpath.PathMeta) - aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 1, 2, dpath.InfoFields[1], &dpath.HopFields[2], dpath.PathMeta) - aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 5, 0, dpath.InfoFields[1], &dpath.HopFields[3], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 0, 40, + dpath.InfoFields[0], &dpath.HopFields[0], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 31, 7, + dpath.InfoFields[0], &dpath.HopFields[1], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 1, 2, + dpath.InfoFields[1], &dpath.HopFields[2], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 5, 0, + dpath.InfoFields[1], &dpath.HopFields[3], dpath.PathMeta) ret := toMsg(t, spkt, dpath) return ret @@ -3377,7 +3556,8 @@ func TestHbirdPacketPath(t *testing.T) { }, "four hops peering astransit non consdir flyovers": { mockMsg: func() *ipv4.Message { - spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), xtest.MustParseIA("1-ff00:0:113")) + spkt := prepHbirdSlayers(xtest.MustParseIA("1-ff00:0:110"), + xtest.MustParseIA("1-ff00:0:113")) dst := addr.MustParseHost("10.0.100.100") _ = spkt.SetDstAddr(dst) @@ -3399,25 +3579,38 @@ func TestHbirdPacketPath(t *testing.T) { }, HopFields: []hummingbird.FlyoverHopField{ - {Flyover: true, HopField: path.HopField{ConsIngress: 40, ConsEgress: 0}, Bw: 5, ResStartTime: 123, Duration: 304}, - {Flyover: true, HopField: path.HopField{ConsIngress: 7, ConsEgress: 31}, Bw: 5, ResStartTime: 123, Duration: 304}, - {Flyover: true, HopField: path.HopField{ConsIngress: 2, ConsEgress: 1}, Bw: 5, ResStartTime: 123, Duration: 304}, - {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 5}, Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 40, ConsEgress: 0}, + Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 7, ConsEgress: 31}, + Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 2, ConsEgress: 1}, + Bw: 5, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 5}, + Bw: 5, ResStartTime: 123, Duration: 304}, }, } // Compute MACs and increase SegID while doing so - dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) - // No Segment update here as the second hop of a peering path uses the same segID as it's following hop - dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[0].HopField) - - dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[3].HopField) + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[1].HopField) + // No Segment update here + // The second hop of a peering path uses the same segID as it's following hop + dpath.HopFields[0].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[0].HopField) + + dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[3].HopField) dpath.InfoFields[1].UpdateSegID(dpath.HopFields[3].HopField.Mac) - dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[2].HopField) + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], + dpath.HopFields[2].HopField) - aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 0, 40, dpath.InfoFields[0], &dpath.HopFields[0], dpath.PathMeta) - aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 31, 7, dpath.InfoFields[0], &dpath.HopFields[1], dpath.PathMeta) - aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 1, 2, dpath.InfoFields[1], &dpath.HopFields[2], dpath.PathMeta) - aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 5, 0, dpath.InfoFields[1], &dpath.HopFields[3], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 0, 40, + dpath.InfoFields[0], &dpath.HopFields[0], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 31, 7, + dpath.InfoFields[0], &dpath.HopFields[1], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 1, 2, + dpath.InfoFields[1], &dpath.HopFields[2], dpath.PathMeta) + aggregateOntoScionMac(t, sv, spkt.DstIA, spkt.PayloadLen, 5, 0, + dpath.InfoFields[1], &dpath.HopFields[3], dpath.PathMeta) ret := toMsg(t, spkt, dpath) return ret @@ -3537,7 +3730,8 @@ func TestBandwidthCheck(t *testing.T) { spkt, dpath := prepHbirdMsg(now) dpath.HopFields = []hummingbird.FlyoverHopField{ {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, - {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}, Bw: 2, ResStartTime: 123, Duration: 304}, + {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}, + Bw: 2, ResStartTime: 123, Duration: 304}, {HopField: path.HopField{ConsIngress: 40, ConsEgress: 41}}, } dpath.Base.PathMeta.SegLen[0] = 11 @@ -3545,7 +3739,8 @@ func TestBandwidthCheck(t *testing.T) { dpath.Base.NumLines = 11 spkt.PayloadLen = 120 - dpath.HopFields[1].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, spkt.PayloadLen, dpath.InfoFields[0], dpath.HopFields[1], dpath.Base.PathMeta) + dpath.HopFields[1].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, + spkt.PayloadLen, dpath.InfoFields[0], dpath.HopFields[1], dpath.Base.PathMeta) msg := toLongMsg(t, spkt, dpath) @@ -3629,7 +3824,8 @@ func prepHbirdSlayers(src, dst addr.IA) *slayers.SCION { return spkt } -func computeAggregateMac(t *testing.T, key, sv []byte, dst addr.IA, l uint16, info path.InfoField, hf hummingbird.FlyoverHopField, meta hummingbird.MetaHdr) [path.MacLen]byte { +func computeAggregateMac(t *testing.T, key, sv []byte, dst addr.IA, l uint16, info path.InfoField, + hf hummingbird.FlyoverHopField, meta hummingbird.MetaHdr) [path.MacLen]byte { scionMac := computeMAC(t, key, info, hf.HopField) @@ -3655,7 +3851,9 @@ func computeAggregateMac(t *testing.T, key, sv []byte, dst addr.IA, l uint16, in return scionMac } -func computeAggregateMacXover(t *testing.T, key, sv []byte, dst addr.IA, l, hin, heg uint16, info path.InfoField, hf hummingbird.FlyoverHopField, meta hummingbird.MetaHdr) [path.MacLen]byte { +func computeAggregateMacXover(t *testing.T, key, sv []byte, dst addr.IA, l, hin, heg uint16, + info path.InfoField, hf hummingbird.FlyoverHopField, + meta hummingbird.MetaHdr) [path.MacLen]byte { scionMac := computeMAC(t, key, info, hf.HopField) @@ -3682,7 +3880,8 @@ func computeAggregateMacXover(t *testing.T, key, sv []byte, dst addr.IA, l, hin, } // Computes flyovermac and aggregates it to existing mac in hopfield -func aggregateOntoScionMac(t *testing.T, sv []byte, dst addr.IA, l, hin, heg uint16, info path.InfoField, hf *hummingbird.FlyoverHopField, meta hummingbird.MetaHdr) { +func aggregateOntoScionMac(t *testing.T, sv []byte, dst addr.IA, l, hin, heg uint16, + info path.InfoField, hf *hummingbird.FlyoverHopField, meta hummingbird.MetaHdr) { block, err := aes.NewCipher(sv) require.NoError(t, err) ingress, egress := hin, heg diff --git a/router/tokenbucket/tokenbucket_test.go b/router/tokenbucket/tokenbucket_test.go index eb69a4ed88..fa9740351c 100644 --- a/router/tokenbucket/tokenbucket_test.go +++ b/router/tokenbucket/tokenbucket_test.go @@ -4,8 +4,9 @@ import ( "testing" "time" - "github.com/scionproto/scion/router/tokenbucket" "github.com/stretchr/testify/assert" + + "github.com/scionproto/scion/router/tokenbucket" ) type entry struct { From 94577124f4dedbd694ad9c9fd454037fac7af6c9 Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Fri, 17 Nov 2023 09:26:22 +0100 Subject: [PATCH 041/100] simplified comments of flyoverhopfield --- .../path/hummingbird/flyoverhopfield.go | 22 ++++--------------- 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/pkg/slayers/path/hummingbird/flyoverhopfield.go b/pkg/slayers/path/hummingbird/flyoverhopfield.go index db9cdaca46..f693d3f4b6 100644 --- a/pkg/slayers/path/hummingbird/flyoverhopfield.go +++ b/pkg/slayers/path/hummingbird/flyoverhopfield.go @@ -39,15 +39,12 @@ type FlyoverHopField struct { } // DecodeFromBytes populates the fields from a raw buffer. -// The buffer must be of length >= path.HopLen. -// @ requires len(raw) >= HopLen +// The buffer must be of length >= HopLen if the Flyover bit is false +// The buffer must be of length >= FlyoverLen if the Flyover bit is set // DecodeFromBytes modifies the fields of *h and reads (but does not modify) the contents of raw. -// @ preserves acc(h) && acc(raw, 1/2) // When a call that satisfies the precondition (len(raw) >= HopLen) is made, // the return value is guaranteed to be nil. -// @ ensures err == nil // Calls to DecodeFromBytes are always guaranteed to terminate. -// @ decreases func (h *FlyoverHopField) DecodeFromBytes(raw []byte) (err error) { if err := h.HopField.DecodeFromBytes(raw); err != nil { return err @@ -58,28 +55,21 @@ func (h *FlyoverHopField) DecodeFromBytes(raw []byte) (err error) { return serrors.New("FlyoverHopField raw too short", "expected", FlyoverLen, "actual", len(raw)) } - //@ assert &raw[12:16][0] == &raw[12] && &raw[12:16][1] == &raw[12] - // && &raw[12:16][2] == &raw[14] && &raw[12:16][3] == &raw[15] h.ResID = binary.BigEndian.Uint32(raw[12:16]) >> 10 h.Bw = binary.BigEndian.Uint16(raw[14:16]) & 0x03ff - //@ assert &raw[16:18][0] == &raw[16] && &raw[16:18][1] == &raw[17] h.ResStartTime = binary.BigEndian.Uint16(raw[16:18]) - //@ assert &raw[18:20][0] == &raw[18] && &raw[18:20][1] == &raw[19] h.Duration = binary.BigEndian.Uint16(raw[18:20]) } return nil } // SerializeTo writes the fields into the provided buffer. -// The buffer must be of length >= path.HopLen. -// @ requires len(b) >= HopLen +// The buffer must be of length >= HopLen if the Flyover bit is false +// The buffer must be of length >= FlyoverLen if the Flyover bit is set // SerializeTo reads (but does not modify) the fields of *h and writes to the contents of b. -// @ preserves acc(h, 1/2) && acc(b) // When a call that satisfies the precondition (len(b) >= HopLen) is made, // the return value is guaranteed to be nil. -// @ ensures err == nil // Calls to SerializeTo are guaranteed to terminate. -// @ decreases func (h *FlyoverHopField) SerializeTo(b []byte) (err error) { if err := h.HopField.SerializeTo(b); err != nil { return err @@ -91,12 +81,8 @@ func (h *FlyoverHopField) SerializeTo(b []byte) (err error) { FlyoverLen, "actual", len(b)) } b[0] |= 0x80 - //@ assert &b[12:16][0] == &b[12] && &b[12:16][1] == &b[12] && &b[12:16][2] == &b[14] - // && &b[12:16][3] == &b[15] binary.BigEndian.PutUint32(b[12:16], h.ResID<<10+uint32(h.Bw)) - //@ assert &b[16:18][0] == &b[16] && &b[16:18][1] == &b[17] binary.BigEndian.PutUint16(b[16:18], h.ResStartTime) - //@ assert &b[18:20][0] == &b[18] && &b[18:20][1] == &b[19] binary.BigEndian.PutUint16(b[18:20], h.Duration) } From f5c9413fb5b8eb63d90380d6d8669f61be4c8656 Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Fri, 17 Nov 2023 09:52:30 +0100 Subject: [PATCH 042/100] added benchmark for standard library full flyover mac; added comment detailing reasons for use of assembly code --- pkg/slayers/path/hummingbird/mac.go | 16 +++++++++++++++ pkg/slayers/path/hummingbird/mac_test.go | 26 ++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/pkg/slayers/path/hummingbird/mac.go b/pkg/slayers/path/hummingbird/mac.go index 58f9e51b36..7fe8ec944b 100644 --- a/pkg/slayers/path/hummingbird/mac.go +++ b/pkg/slayers/path/hummingbird/mac.go @@ -10,6 +10,22 @@ import ( "github.com/scionproto/scion/pkg/addr" ) +// The FullFlyoverMac makes use of the assembly code in the asm_* files +// There are two main, related, reasons for that. +// First, the aes key expansion performed by these assembly files is +// much faster than what the library code does. +// BenchmarkFlyoverMac and BenchmarkFlyoverMacLib in mac_test.go show the difference +// +// Second, the library implementation of the aes key expansion performs calls to make() +// and allocates memory, which we would like to avoid +// This is also the main reason why the direct call to assembly is much faster +// +// A full implementation of aes written in go only without memory allocations +// has been attempted, but turned out to not be much more efficient than +// the library implementation. +// This is expectedt to be due to the fact that a go only implementation of aes +// is unable to make use of hardware accelerated aes instructions. + // defined in asm_* assembly files //go:noescape diff --git a/pkg/slayers/path/hummingbird/mac_test.go b/pkg/slayers/path/hummingbird/mac_test.go index d7f8b55166..a7a1e88b52 100644 --- a/pkg/slayers/path/hummingbird/mac_test.go +++ b/pkg/slayers/path/hummingbird/mac_test.go @@ -200,3 +200,29 @@ func BenchmarkFlyoverMac(b *testing.B) { hummingbird.FullFlyoverMac(ak, dstIA, pktlen, resStartTs, highResTs, buffer, xkbuffer) } } + +// Benchmark of the FLyover Mac if we use library code only +// Without using the assembly code in the asm_* files +func BenchmarkFlyoverMacLib(b *testing.B) { + ak := []byte{0x7e, 0x61, 0x4, 0x91, 0x30, 0x6b, 0x95, 0xec, 0xb5, 0x75, 0xc6, 0xe9, + 0x4c, 0x5a, 0x89, 0x84} + var dstIA addr.IA = 326 + var pktlen uint16 = 23 + var resStartTs uint16 = 1234 + var highResTs uint32 = 4321 + buffer := make([]byte, 16) + + b.ResetTimer() + // Compute expected output based on library cbc-mac implementation + for i := 0; i < b.N; i++ { + binary.BigEndian.PutUint64(buffer[0:8], uint64(dstIA)) + binary.BigEndian.PutUint16(buffer[8:10], pktlen) + binary.BigEndian.PutUint16(buffer[10:12], resStartTs) + binary.BigEndian.PutUint32(buffer[12:16], highResTs) + block, err := aes.NewCipher(ak) + if err != nil { + require.Fail(b, err.Error()) + } + block.Encrypt(buffer[:], buffer[:]) + } +} From 0e54f00ba2b2750881a013bb760ef58587c728d9 Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Fri, 17 Nov 2023 10:30:15 +0100 Subject: [PATCH 043/100] added UT for SetHopField --- pkg/slayers/path/hummingbird/raw.go | 1 + pkg/slayers/path/hummingbird/raw_test.go | 50 ++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/pkg/slayers/path/hummingbird/raw.go b/pkg/slayers/path/hummingbird/raw.go index b9dfd7029b..b96d440a6e 100644 --- a/pkg/slayers/path/hummingbird/raw.go +++ b/pkg/slayers/path/hummingbird/raw.go @@ -48,6 +48,7 @@ func (s *Raw) SerializeTo(b []byte) error { } // Reverse reverses the path such that it can be used in the reverse direction. +// Removes all flyovers in the process func (s *Raw) Reverse() (path.Path, error) { // XXX(shitz): The current implementation is not the most performant, since it parses the entire // path first. If this becomes a performance bottleneck, the implementation should be changed to diff --git a/pkg/slayers/path/hummingbird/raw_test.go b/pkg/slayers/path/hummingbird/raw_test.go index 3d04bffe3f..58e8364ee1 100644 --- a/pkg/slayers/path/hummingbird/raw_test.go +++ b/pkg/slayers/path/hummingbird/raw_test.go @@ -177,6 +177,56 @@ func TestLastHop(t *testing.T) { } } +func TestSetHopfield(t *testing.T) { + hop1 := hummingbird.FlyoverHopField{ + HopField: path.HopField{ + ConsIngress: 0, + ConsEgress: 1, + }, + } + hop2 := hummingbird.FlyoverHopField{ + HopField: path.HopField{ + ConsIngress: 2, + ConsEgress: 3, + }, + } + hop3 := hummingbird.FlyoverHopField{ + Flyover: true, + HopField: path.HopField{ + ConsIngress: 1, + ConsEgress: 0, + }, + ResID: 13, + Bw: 6, + ResStartTime: 0, + Duration: 45, + } + expected := decodedHbirdTestPath + expected.HopFields[0] = hop1 + expected.HopFields[0].Flyover = true + expected.HopFields[1] = hop2 + expected.HopFields[3] = hop3 + + buffer := make([]byte, expected.Len()) + expected.SerializeTo(buffer) + + testPath := rawHbirdTestPath + + err := testPath.SetHopField(hop1, 0) + assert.NoError(t, err) + + err = testPath.SetHopField(hop2, 5) + assert.NoError(t, err) + + err = testPath.SetHopField(hop3, 11) + assert.NoError(t, err) + + result, err := testPath.ToDecoded() + + assert.NoError(t, err) + require.Equal(t, expected, result) +} + func mkRawHbirdPath(t *testing.T, pcase hbirdPathCase, infIdx, hopIdx uint8) *hummingbird.Raw { t.Helper() decoded := mkDecodedHbirdPath(t, pcase, infIdx, hopIdx) From 24c13e51909482279e5ea2836195d547bfc0dcbe Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Fri, 17 Nov 2023 10:36:05 +0100 Subject: [PATCH 044/100] minor changes to conform to upstream --- pkg/slayers/path/hummingbird/raw_test.go | 4 +--- pkg/slayers/path/scion/base.go | 4 +--- pkg/slayers/path/scion/decoded.go | 12 +++--------- pkg/slayers/path/scion/raw_test.go | 1 - 4 files changed, 5 insertions(+), 16 deletions(-) diff --git a/pkg/slayers/path/hummingbird/raw_test.go b/pkg/slayers/path/hummingbird/raw_test.go index 58e8364ee1..20440bb6a6 100644 --- a/pkg/slayers/path/hummingbird/raw_test.go +++ b/pkg/slayers/path/hummingbird/raw_test.go @@ -65,7 +65,7 @@ func TestRawReverseHbird(t *testing.T) { for i := range tc.inIdxs { i := i t.Run(fmt.Sprintf("%s case %d", name, i+1), func(t *testing.T) { - //t.Parallel() + t.Parallel() input := mkRawHbirdPath(t, tc.input, uint8(tc.inIdxs[i][0]), uint8(tc.inIdxs[i][1])) want := mkRawHbirdPath(t, tc.want, uint8(tc.wantIdxs[i][0]), @@ -161,8 +161,6 @@ func TestGetHbirdHopField(t *testing.T) { } } -//TODO: SetHopField test - func TestLastHop(t *testing.T) { testCases := map[*hummingbird.Raw]bool{ createHbirdPath(3, 9): false, diff --git a/pkg/slayers/path/scion/base.go b/pkg/slayers/path/scion/base.go index b46740d147..b2a7812239 100644 --- a/pkg/slayers/path/scion/base.go +++ b/pkg/slayers/path/scion/base.go @@ -81,7 +81,6 @@ func (s *Base) IncPath() error { "num_hops", s.NumHops) } s.PathMeta.CurrHF++ - // Update CurrINF s.PathMeta.CurrINF = s.infIndexForHF(s.PathMeta.CurrHF) return nil @@ -163,6 +162,5 @@ func (m *MetaHdr) SerializeTo(b []byte) error { } func (m MetaHdr) String() string { - return fmt.Sprintf("{CurrInf: %d, CurrHF: %d, SegLen: %v}", - m.CurrINF, m.CurrHF, m.SegLen) + return fmt.Sprintf("{CurrInf: %d, CurrHF: %d, SegLen: %v}", m.CurrINF, m.CurrHF, m.SegLen) } diff --git a/pkg/slayers/path/scion/decoded.go b/pkg/slayers/path/scion/decoded.go index a365c0955f..da183f2903 100644 --- a/pkg/slayers/path/scion/decoded.go +++ b/pkg/slayers/path/scion/decoded.go @@ -42,8 +42,6 @@ func (s *Decoded) DecodeFromBytes(data []byte) error { if err := s.Base.DecodeFromBytes(data); err != nil { return err } - // fmt.Printf("s: %v\n", s) - // fmt.Print(s.Len()) if minLen := s.Len(); len(data) < minLen { return serrors.New("DecodedPath raw too short", "expected", minLen, "actual", len(data)) } @@ -56,7 +54,6 @@ func (s *Decoded) DecodeFromBytes(data []byte) error { } offset += path.InfoLen } - s.HopFields = make([]path.HopField, s.NumHops) for i := 0; i < s.NumHops; i++ { if err := s.HopFields[i].DecodeFromBytes(data[offset : offset+path.HopLen]); err != nil { @@ -74,12 +71,10 @@ func (s *Decoded) SerializeTo(b []byte) error { return serrors.New("buffer too small to serialize path.", "expected", s.Len(), "actual", len(b)) } - var offset int if err := s.PathMeta.SerializeTo(b[:MetaLen]); err != nil { return err } - offset = MetaLen - + offset := MetaLen for _, info := range s.InfoFields { if err := info.SerializeTo(b[offset : offset+path.InfoLen]); err != nil { return err @@ -91,13 +86,11 @@ func (s *Decoded) SerializeTo(b []byte) error { return err } offset += path.HopLen - } return nil } // Reverse reverses a SCION path. -// Removes all reservations from a Hummingbird path, as these are not bidirectional func (s *Decoded) Reverse() (path.Path, error) { if s.NumINF == 0 { return nil, serrors.New("empty decoded path is invalid and cannot be reversed") @@ -113,12 +106,13 @@ func (s *Decoded) Reverse() (path.Path, error) { info.ConsDir = !info.ConsDir } // Reverse order of hop fields - for i, j := 0, len(s.HopFields)-1; i < j; i, j = i+1, j-1 { + for i, j := 0, s.NumHops-1; i < j; i, j = i+1, j-1 { s.HopFields[i], s.HopFields[j] = s.HopFields[j], s.HopFields[i] } // Update CurrINF and CurrHF and SegLens s.PathMeta.CurrINF = uint8(s.NumINF) - s.PathMeta.CurrINF - 1 s.PathMeta.CurrHF = uint8(s.NumHops) - s.PathMeta.CurrHF - 1 + return s, nil } diff --git a/pkg/slayers/path/scion/raw_test.go b/pkg/slayers/path/scion/raw_test.go index 1f50a167b6..ff527d9398 100644 --- a/pkg/slayers/path/scion/raw_test.go +++ b/pkg/slayers/path/scion/raw_test.go @@ -208,7 +208,6 @@ func TestSetInfoField(t *testing.T) { } } -// Will also need test to check entire RawPacket is correct func TestSetHopField(t *testing.T) { testCases := map[string]struct { idx int From 131967d140ce61ee37357789baa7e0e79a79709b Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Fri, 17 Nov 2023 10:43:39 +0100 Subject: [PATCH 045/100] renamed setSecretValue to SetHbirdKey --- router/connector.go | 5 +++-- router/control/conf.go | 4 ++-- router/dataplane_hbird.go | 5 ++--- router/dataplane_hbird_test.go | 10 +++++----- router/dataplane_test.go | 10 +++++----- router/export_test.go | 2 +- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/router/connector.go b/router/connector.go index f5dce240af..ffac6e821c 100644 --- a/router/connector.go +++ b/router/connector.go @@ -180,7 +180,8 @@ func (c *Connector) SetKey(ia addr.IA, index int, key []byte) error { return c.DataPlane.SetKey(key) } -func (c *Connector) SetSecretValue(ia addr.IA, index int, sv []byte) error { +// SetHbirdKey sets the hummingbird key for the given ISD-AS at the given indey +func (c *Connector) SetHbirdKey(ia addr.IA, index int, sv []byte) error { c.mtx.Lock() defer c.mtx.Unlock() log.Debug("Setting secret value", "isd_as", ia, "index", index) @@ -190,7 +191,7 @@ func (c *Connector) SetSecretValue(ia addr.IA, index int, sv []byte) error { if index != 0 { return serrors.New("currently only index 0 secret value is supported") } - return c.DataPlane.SetSecretValue(sv) + return c.DataPlane.SetHbirdKey(sv) } func (c *Connector) ListInternalInterfaces() ([]control.InternalInterface, error) { diff --git a/router/control/conf.go b/router/control/conf.go index 2279010bcf..0498927181 100644 --- a/router/control/conf.go +++ b/router/control/conf.go @@ -37,7 +37,7 @@ type Dataplane interface { AddSvc(ia addr.IA, svc addr.SVC, ip net.IP) error DelSvc(ia addr.IA, svc addr.SVC, ip net.IP) error SetKey(ia addr.IA, index int, key []byte) error - SetSecretValue(ia addr.IA, index int, key []byte) error + SetHbirdKey(ia addr.IA, index int, key []byte) error } // LinkInfo contains the information about a link between an internal and @@ -126,7 +126,7 @@ func ConfigDataplane(dp Dataplane, cfg *Config) error { return err } keySv := DeriveHbirdSecretValue(cfg.MasterKeys.Key0) - if err := dp.SetSecretValue(cfg.IA, 0, keySv); err != nil { + if err := dp.SetHbirdKey(cfg.IA, 0, keySv); err != nil { return err } } diff --git a/router/dataplane_hbird.go b/router/dataplane_hbird.go index 690d6d0901..47ad51e189 100644 --- a/router/dataplane_hbird.go +++ b/router/dataplane_hbird.go @@ -21,8 +21,8 @@ import ( "github.com/scionproto/scion/router/tokenbucket" ) -// SetSecretValue sets the key for the PRF function used to compute the Hummingbird Auth Key -func (d *DataPlane) SetSecretValue(key []byte) error { +// SetHbirdKey sets the key for the PRF function used to compute the Hummingbird Auth Key +func (d *DataPlane) SetHbirdKey(key []byte) error { d.mtx.Lock() defer d.mtx.Unlock() if d.running { @@ -441,7 +441,6 @@ func (p *scionPacketProcessor) deAggregateMac() (processResult, error) { } copy(p.hopField.Mac[:], p.cachedMac[:path.MacLen]) if err := p.hbirdPath.ReplaceCurrentMac(p.cachedMac); err != nil { - //TODO: what SCMP packet should be returned here? Is that even necessary? log.Debug("Failed to replace MAC after de-aggregation", "error", err.Error()) return processResult{}, serrors.Join(err, serrors.New("Mac replacement failed")) } diff --git a/router/dataplane_hbird_test.go b/router/dataplane_hbird_test.go index acf08172f0..5a718953b4 100644 --- a/router/dataplane_hbird_test.go +++ b/router/dataplane_hbird_test.go @@ -27,21 +27,21 @@ func TestDataPlaneSetSecretValue(t *testing.T) { t.Run("fails after serve", func(t *testing.T) { d := &router.DataPlane{} d.FakeStart() - assert.Error(t, d.SetSecretValue([]byte("dummy"))) + assert.Error(t, d.SetHbirdKey([]byte("dummy"))) }) t.Run("setting nil value is not allowed", func(t *testing.T) { d := &router.DataPlane{} d.FakeStart() - assert.Error(t, d.SetSecretValue(nil)) + assert.Error(t, d.SetHbirdKey(nil)) }) t.Run("single set works", func(t *testing.T) { d := &router.DataPlane{} - assert.NoError(t, d.SetSecretValue([]byte("dummy key xxxxxx"))) + assert.NoError(t, d.SetHbirdKey([]byte("dummy key xxxxxx"))) }) t.Run("double set fails", func(t *testing.T) { d := &router.DataPlane{} - assert.NoError(t, d.SetSecretValue([]byte("dummy key xxxxxx"))) - assert.Error(t, d.SetSecretValue([]byte("dummy key xxxxxx"))) + assert.NoError(t, d.SetHbirdKey([]byte("dummy key xxxxxx"))) + assert.Error(t, d.SetHbirdKey([]byte("dummy key xxxxxx"))) }) } diff --git a/router/dataplane_test.go b/router/dataplane_test.go index 4193d027b7..4e98a8c45b 100644 --- a/router/dataplane_test.go +++ b/router/dataplane_test.go @@ -261,7 +261,7 @@ func TestDataPlaneRun(t *testing.T) { _ = ret.SetIA(local) _ = ret.SetKey(key) - _ = ret.SetSecretValue(key) + _ = ret.SetHbirdKey(key) return ret }, }, @@ -337,7 +337,7 @@ func TestDataPlaneRun(t *testing.T) { local := &net.UDPAddr{IP: net.ParseIP("10.0.200.100").To4()} _ = ret.SetKey([]byte("randomkeyformacs")) - _ = ret.SetSecretValue([]byte("randomsvformacss")) + _ = ret.SetHbirdKey([]byte("randomsvformacss")) _ = ret.AddInternalInterface(mInternal, net.IP{}) for remote, ifIDs := range routers { for _, ifID := range ifIDs { @@ -394,7 +394,7 @@ func TestDataPlaneRun(t *testing.T) { mInternal.EXPECT().ReadBatch(gomock.Any()).Return(0, nil).AnyTimes() _ = ret.SetKey([]byte("randomkeyformacs")) - _ = ret.SetSecretValue([]byte("randomsvformacss")) + _ = ret.SetHbirdKey([]byte("randomsvformacss")) _ = ret.AddInternalInterface(mInternal, net.IP{}) _ = ret.AddNextHop(3, localAddr) _ = ret.AddNextHopBFD(3, localAddr, remoteAddr, bfd(), "") @@ -449,7 +449,7 @@ func TestDataPlaneRun(t *testing.T) { Addr: &net.UDPAddr{IP: net.ParseIP("10.0.0.200")}, } _ = ret.SetKey([]byte("randomkeyformacs")) - _ = ret.SetSecretValue([]byte("randomsvformacss")) + _ = ret.SetHbirdKey([]byte("randomsvformacss")) _ = ret.AddInternalInterface(mInternal, net.IP{}) _ = ret.AddExternalInterface(ifID, mExternal) _ = ret.AddExternalInterfaceBFD(ifID, mExternal, local, remote, bfd()) @@ -530,7 +530,7 @@ func TestDataPlaneRun(t *testing.T) { Addr: &net.UDPAddr{IP: net.ParseIP("10.0.0.200")}, } _ = ret.SetKey([]byte("randomkeyformacs")) - _ = ret.SetSecretValue([]byte("randomsvformacss")) + _ = ret.SetHbirdKey([]byte("randomsvformacss")) _ = ret.AddInternalInterface(mInternal, net.IP{}) _ = ret.AddExternalInterface(1, mExternal) _ = ret.AddExternalInterfaceBFD(1, mExternal, local, remote, bfd()) diff --git a/router/export_test.go b/router/export_test.go index 5cb770aaf6..ffa745eb57 100644 --- a/router/export_test.go +++ b/router/export_test.go @@ -62,7 +62,7 @@ func NewDP( if err := dp.SetKey(key); err != nil { panic(err) } - if err := dp.SetSecretValue(sv); err != nil { + if err := dp.SetHbirdKey(sv); err != nil { panic(err) } return dp From aac847ba3f0d61997c12ba7c6b3d51a6484e6776 Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Fri, 17 Nov 2023 10:49:27 +0100 Subject: [PATCH 046/100] moved reservationExpired error to dataplane_hbird --- router/dataplane.go | 1 - router/dataplane_hbird.go | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/router/dataplane.go b/router/dataplane.go index a57231a362..2e1d9f87ff 100644 --- a/router/dataplane.go +++ b/router/dataplane.go @@ -151,7 +151,6 @@ var ( macVerificationFailed = errors.New("MAC verification failed") badPacketSize = errors.New("bad packet size") slowPathRequired = errors.New("slow-path required") - reservationExpired = errors.New("current time is outside of reservation validity") // zeroBuffer will be used to reset the Authenticator option in the // scionPacketProcessor.OptAuth diff --git a/router/dataplane_hbird.go b/router/dataplane_hbird.go index 47ad51e189..fa2a5662a0 100644 --- a/router/dataplane_hbird.go +++ b/router/dataplane_hbird.go @@ -5,6 +5,7 @@ import ( "crypto/cipher" "crypto/subtle" "encoding/binary" + "errors" "fmt" "time" @@ -21,6 +22,10 @@ import ( "github.com/scionproto/scion/router/tokenbucket" ) +var ( + reservationExpired = errors.New("current time is outside of reservation validity") +) + // SetHbirdKey sets the key for the PRF function used to compute the Hummingbird Auth Key func (d *DataPlane) SetHbirdKey(key []byte) error { d.mtx.Lock() From 759ce0b10d08affc98a20c5ee50e9358067988a1 Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Fri, 17 Nov 2023 10:58:36 +0100 Subject: [PATCH 047/100] grouping together hbird values in dataplane structs; minor changes --- router/dataplane.go | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/router/dataplane.go b/router/dataplane.go index 2e1d9f87ff..41e72633f0 100644 --- a/router/dataplane.go +++ b/router/dataplane.go @@ -21,7 +21,6 @@ import ( "crypto/cipher" "crypto/rand" "crypto/subtle" - "encoding/binary" "errors" "fmt" "hash" @@ -1014,12 +1013,12 @@ func (p *scionPacketProcessor) reset() error { p.ingressID = 0 //p.scionLayer // cannot easily be reset p.path = nil - p.hbirdPath = nil p.hopField = path.HopField{} p.infoField = path.InfoField{} - p.flyoverField = hummingbird.FlyoverHopField{} p.effectiveXover = false p.peering = false + p.hbirdPath = nil + p.flyoverField = hummingbird.FlyoverHopField{} p.hasPriority = false if err := p.buffer.Clear(); err != nil { @@ -1200,9 +1199,6 @@ type scionPacketProcessor struct { buffer gopacket.SerializeBuffer // mac is the hasher for the MAC computation. mac hash.Hash - // block is the keyed PRF for the hummingbird auth key computation - prf cipher.Block - // scionLayer is the SCION gopacket layer. scionLayer slayers.SCION hbhLayer slayers.HopByHopExtnSkipper @@ -1212,8 +1208,6 @@ type scionPacketProcessor struct { // path is the raw SCION path. Will be set during processing. path *scion.Raw - // hbirdPath is the raw Hummingbird path. Will be set during processing - hbirdPath *hummingbird.Raw // hopField is the current hopField field, is updated during processing. hopField path.HopField // infoField is the current infoField field, is updated during processing. @@ -1222,16 +1216,21 @@ type scionPacketProcessor struct { effectiveXover bool // peering indicates that the hop field being processed is a peering hop field. peering bool - // flyoverField is the flyoverfield containing the current hopfield for hummingbird packets - flyoverField hummingbird.FlyoverHopField - // hasPriority indicates whether this packet has forwarding priority - hasPriority bool // cachedMac contains the full 16 bytes of the MAC. Will be set during processing. // For a hop performing an Xover, it is the MAC corresponding to the down segment. cachedMac []byte // macInputBuffer avoid allocating memory during processing. macInputBuffer []byte + + // prf is the keyed PRF for the hummingbird auth key computation + prf cipher.Block + // hbirdPath is the raw Hummingbird path. Will be set during processing + hbirdPath *hummingbird.Raw + // flyoverField is the flyoverfield containing the current hopfield for hummingbird packets + flyoverField hummingbird.FlyoverHopField + // hasPriority indicates whether this packet has forwarding priority + hasPriority bool // hbirdXkbuffer avoid allocating memory during aes computation for hummingbird flyover mac hbirdXkbuffer []uint32 @@ -1548,16 +1547,6 @@ func (p *scionPacketProcessor) currentHopPointer() uint16 { scion.MetaLen + path.InfoLen*p.path.NumINF + path.HopLen*int(p.path.PathMeta.CurrHF)) } -// Compares two 6 byte arrays. -// Always returns false if at least one input is of a different length. -// Returns true if equal, false otherwise. -func CompareMacThisIsNotConstant(a, b []byte) bool { - if len(a) != 6 || len(b) != 6 { - return false - } - return binary.BigEndian.Uint32(a) == binary.BigEndian.Uint32(b) && a[4] == b[4] && a[5] == b[5] -} - func (p *scionPacketProcessor) verifyCurrentMAC() (processResult, error) { fullMac := path.FullMAC(p.mac, p.infoField, p.hopField, p.macInputBuffer[:path.MACBufferSize]) if subtle.ConstantTimeCompare(p.hopField.Mac[:path.MacLen], fullMac[:path.MacLen]) == 0 { From 621778b7541d61a9cb575e89216ee2cffb4c906c Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Fri, 17 Nov 2023 11:00:08 +0100 Subject: [PATCH 048/100] re-adding some comments that were lost earlier --- router/dataplane.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/router/dataplane.go b/router/dataplane.go index 41e72633f0..ca8d12d466 100644 --- a/router/dataplane.go +++ b/router/dataplane.go @@ -1564,6 +1564,8 @@ func (p *scionPacketProcessor) verifyCurrentMAC() (processResult, error) { } return processResult{SlowPathRequest: slowPathRequest}, slowPathRequired } + // Add the full MAC to the SCION packet processor + // such that EPIC does not need to recalculate it p.cachedMac = fullMac return processResult{}, nil From b3c2953d23383dfacd80e22cf1d6792c00994550 Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Fri, 17 Nov 2023 11:31:57 +0100 Subject: [PATCH 049/100] added a forgotten assert.NoError --- pkg/slayers/path/hummingbird/raw_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/slayers/path/hummingbird/raw_test.go b/pkg/slayers/path/hummingbird/raw_test.go index 20440bb6a6..f66a9e8392 100644 --- a/pkg/slayers/path/hummingbird/raw_test.go +++ b/pkg/slayers/path/hummingbird/raw_test.go @@ -206,11 +206,12 @@ func TestSetHopfield(t *testing.T) { expected.HopFields[3] = hop3 buffer := make([]byte, expected.Len()) - expected.SerializeTo(buffer) + err := expected.SerializeTo(buffer) + assert.NoError(t, err) testPath := rawHbirdTestPath - err := testPath.SetHopField(hop1, 0) + err = testPath.SetHopField(hop1, 0) assert.NoError(t, err) err = testPath.SetHopField(hop2, 5) From 97ff22ca9042b860e0132ada28f6507a2f043fe0 Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Fri, 17 Nov 2023 14:14:07 +0100 Subject: [PATCH 050/100] adapted comments in hummingbird path files --- pkg/slayers/path/hummingbird/decoded.go | 4 ++-- pkg/slayers/path/hummingbird/raw.go | 15 +++++++++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/pkg/slayers/path/hummingbird/decoded.go b/pkg/slayers/path/hummingbird/decoded.go index 55afe9a23d..b235a2b31e 100644 --- a/pkg/slayers/path/hummingbird/decoded.go +++ b/pkg/slayers/path/hummingbird/decoded.go @@ -121,7 +121,7 @@ func (s *Decoded) SerializeTo(b []byte) error { return nil } -// Reverse reverses a SCION path. +// Reverse reverses a hummingbird path. // Removes all reservations from a Hummingbird path, as these are not bidirectional func (s *Decoded) Reverse() (path.Path, error) { if s.NumINF == 0 { @@ -186,7 +186,7 @@ func (s *Decoded) removeFlyovers() error { return nil } -// ToRaw tranforms scion.Decoded into scion.Raw. +// ToRaw tranforms hummingbird.Decoded into hummingbird.Raw. func (s *Decoded) ToRaw() (*Raw, error) { b := make([]byte, s.Len()) if err := s.SerializeTo(b); err != nil { diff --git a/pkg/slayers/path/hummingbird/raw.go b/pkg/slayers/path/hummingbird/raw.go index b96d440a6e..313193f8a1 100644 --- a/pkg/slayers/path/hummingbird/raw.go +++ b/pkg/slayers/path/hummingbird/raw.go @@ -69,7 +69,7 @@ func (s *Raw) Reverse() (path.Path, error) { return s, err } -// ToDecoded transforms a scion.Raw to a scion.Decoded. +// ToDecoded transforms a hummingbird.Raw to a hummingbird.Decoded. func (s *Raw) ToDecoded() (*Decoded, error) { // Serialize PathMeta to ensure potential changes are reflected Raw. @@ -107,7 +107,7 @@ func (s *Raw) GetInfoField(idx int) (path.InfoField, error) { return info, nil } -// GetCurrentInfoField is a convenience method that returns the current hop field pointed to by the +// GetCurrentInfoField is a convenience method that returns the current info field pointed to by the // CurrINF index in the path meta header. func (s *Raw) GetCurrentInfoField() (path.InfoField, error) { return s.GetInfoField(int(s.PathMeta.CurrINF)) @@ -130,7 +130,9 @@ func (s *Raw) SetInfoField(info path.InfoField, idx int) error { return info.SerializeTo(s.Raw[infOffset : infOffset+path.InfoLen]) } -// GetHopField returns the HopField at a given index. +// GetHopField returns the HopField beginninrg at a given index. +// Does NOT check whether the given index is the first line of a hopfield +// Responsibility to check that falls to the caller func (s *Raw) GetHopField(idx int) (FlyoverHopField, error) { if idx >= s.NumLines-HopLines+1 { return FlyoverHopField{}, @@ -179,6 +181,7 @@ func (s *Raw) ReplaceCurrentMac(mac []byte) error { } // Returns a slice of the MAC of the hopfield starting at index idx +// It is the caller's responsibility to make sure line idx is the beginning of a hopfield. func (s *Raw) GetMac(idx int) ([]byte, error) { if idx >= s.NumLines-HopLines+1 { return nil, serrors.New("HopField index out of bounds", @@ -195,6 +198,8 @@ func (s *Raw) GetMac(idx int) ([]byte, error) { // it is replaced by a FlyoverHopField with dummy values. // This works for SCMP packets as Flyover hops are removed later // in the process of building a SCMP packet. +// +// Does not allow replacing a normal hopfield with a FlyoverHopField func (s *Raw) SetHopField(hop FlyoverHopField, idx int) error { if idx >= s.NumLines-HopLines+1 { return serrors.New("HopField index out of bounds", @@ -260,7 +265,7 @@ func (s *Raw) GetNextEgress() (uint16, error) { } // Returns the ingress interface of the previous hop -// Assumes the previous hop is NOT a flyoverhop +// Does NOT work if the previous hop is a flyover hop func (s *Raw) GetPreviousIngress() (uint16, error) { idx := int(s.Base.PathMeta.CurrHF) - HopLines if idx < 0 { @@ -275,6 +280,7 @@ func (s *Raw) GetPreviousIngress() (uint16, error) { // Attaches current flyoverfield to next hopfield. // DOES NOT adapt MACS. +// Next hopfield has to NOT already have a flyover func (s *Raw) DoFlyoverXover() error { idx := int(s.Base.PathMeta.CurrHF) if idx >= s.NumLines-7 { @@ -309,6 +315,7 @@ func (s *Raw) DoFlyoverXover() error { // Attaches current flyoverfield to previous hopfield // DOES NOT adapt MACs +// It is assumed that the previous hopfield does NOT already have a flyover func (s *Raw) ReverseFlyoverXover() error { idx := int(s.Base.PathMeta.CurrHF) if idx < 6 { From 5d20e243c46340c4385bd9d5ea5197d62abb05ce Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Fri, 17 Nov 2023 14:25:32 +0100 Subject: [PATCH 051/100] added additional test case for bandwidth check, added comments to tokenbucket functions --- router/dataplane_hbird_test.go | 59 ++++++++++++++++++++++++++++++- router/tokenbucket/tokenbucket.go | 3 ++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/router/dataplane_hbird_test.go b/router/dataplane_hbird_test.go index 5a718953b4..8d4752f793 100644 --- a/router/dataplane_hbird_test.go +++ b/router/dataplane_hbird_test.go @@ -3730,7 +3730,7 @@ func TestBandwidthCheck(t *testing.T) { spkt, dpath := prepHbirdMsg(now) dpath.HopFields = []hummingbird.FlyoverHopField{ {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, - {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}, + {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}, ResID: 42, Bw: 2, ResStartTime: 123, Duration: 304}, {HopField: path.HopField{ConsIngress: 40, ConsEgress: 41}}, } @@ -3758,6 +3758,63 @@ func TestBandwidthCheck(t *testing.T) { assert.NoError(t, err) } +func TestBandwidthCheckDifferentResID(t *testing.T) { + // Verifies that packets of one reservation do not affect + // available bandwidth of another reservation + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + key := []byte("testkey_xxxxxxxx") + sv := []byte("test_secretvalue") + now := time.Now() + + dp := router.NewDP( + map[uint16]router.BatchConn{ + uint16(2): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 1: topology.Parent, + 2: topology.Child, + }, + nil, nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + + spkt, dpath := prepHbirdMsg(now) + dpath.HopFields = []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, + {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}, ResID: 24, + Bw: 2, ResStartTime: 123, Duration: 304}, + {HopField: path.HopField{ConsIngress: 40, ConsEgress: 41}}, + } + dpath.Base.PathMeta.SegLen[0] = 11 + dpath.Base.PathMeta.CurrHF = 3 + dpath.Base.NumLines = 11 + + spkt.PayloadLen = 120 + dpath.HopFields[1].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, + spkt.PayloadLen, dpath.InfoFields[0], dpath.HopFields[1], dpath.Base.PathMeta) + + msg := toLongMsg(t, spkt, dpath) + + _, err := dp.ProcessPkt(1, msg) + assert.NoError(t, err) + + dpath.HopFields[1].ResID = 32 + dpath.HopFields[1].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, + spkt.PayloadLen, dpath.InfoFields[0], dpath.HopFields[1], dpath.Base.PathMeta) + + msg = toLongMsg(t, spkt, dpath) + _, err = dp.ProcessPkt(1, msg) + assert.NoError(t, err) + + dpath.HopFields[1].ResID = 42 + dpath.HopFields[1].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, + spkt.PayloadLen, dpath.InfoFields[0], dpath.HopFields[1], dpath.Base.PathMeta) + + msg = toLongMsg(t, spkt, dpath) + _, err = dp.ProcessPkt(1, msg) + assert.NoError(t, err) +} + func toLongMsg(t *testing.T, spkt *slayers.SCION, dpath path.Path) *ipv4.Message { t.Helper() ret := &ipv4.Message{} diff --git a/router/tokenbucket/tokenbucket.go b/router/tokenbucket/tokenbucket.go index 85460e6be4..aa9f45198a 100644 --- a/router/tokenbucket/tokenbucket.go +++ b/router/tokenbucket/tokenbucket.go @@ -19,6 +19,7 @@ type TokenBucket struct { lock sync.Mutex } +// Initializes a new tockenbucket for the given burstSize and rate func NewTokenBucket(initialTime time.Time, burstSize float64, rate float64) *TokenBucket { return &TokenBucket{ CurrentTokens: rate, @@ -28,12 +29,14 @@ func NewTokenBucket(initialTime time.Time, burstSize float64, rate float64) *Tok } } +// Sets a new rate for the token bucket func (t *TokenBucket) SetRate(rate float64) { t.lock.Lock() defer t.lock.Unlock() t.CIR = rate } +// Sets a new burst size for the token bucket func (t *TokenBucket) SetBurstSize(burstSize float64) { t.lock.Lock() defer t.lock.Unlock() From 0340e781f695931947e11fa127c16334d307772a Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Fri, 17 Nov 2023 17:15:42 +0100 Subject: [PATCH 052/100] fixed faulty test --- router/dataplane_hbird_test.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/router/dataplane_hbird_test.go b/router/dataplane_hbird_test.go index 8d4752f793..67ba724eb0 100644 --- a/router/dataplane_hbird_test.go +++ b/router/dataplane_hbird_test.go @@ -235,11 +235,11 @@ func TestProcessHbirdPacket(t *testing.T) { return router.NewDP(nil, map[uint16]topology.LinkType{ 51: topology.Child, - 3: topology.Core, + 31: topology.Core, }, mock_router.NewMockBatchConn(ctrl), map[uint16]*net.UDPAddr{ - uint16(3): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + uint16(51): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, }, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) }, mockMsg: func(afterProcessing bool) *ipv4.Message { @@ -247,7 +247,7 @@ func TestProcessHbirdPacket(t *testing.T) { dpath := &hummingbird.Decoded{ Base: hummingbird.Base{ PathMeta: hummingbird.MetaHdr{ - CurrHF: 6, + CurrHF: 3, SegLen: [3]uint8{6, 6, 0}, }, NumINF: 2, @@ -260,19 +260,19 @@ func TestProcessHbirdPacket(t *testing.T) { {SegID: 0x222, ConsDir: false, Timestamp: util.TimeToSecs(now)}, }, HopFields: []hummingbird.FlyoverHopField{ - {HopField: path.HopField{ConsIngress: 0, ConsEgress: 1}}, // IA 110 - {HopField: path.HopField{ConsIngress: 31, ConsEgress: 0}}, // Src - {HopField: path.HopField{ConsIngress: 0, ConsEgress: 51}}, // Dst - {HopField: path.HopField{ConsIngress: 3, ConsEgress: 0}}, // IA 110 + {HopField: path.HopField{ConsIngress: 1, ConsEgress: 0}}, + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 31}}, + {HopField: path.HopField{ConsIngress: 51, ConsEgress: 0}}, + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 3}}, }, } - dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[1].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], + dpath.HopFields[1].HopField) + dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], dpath.HopFields[2].HopField) - dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], - dpath.HopFields[3].HopField) if !afterProcessing { - dpath.InfoFields[0].UpdateSegID(dpath.HopFields[2].HopField.Mac) + dpath.InfoFields[0].UpdateSegID(dpath.HopFields[1].HopField.Mac) return toMsg(t, spkt, dpath) } require.NoError(t, dpath.IncPath(hummingbird.HopLines)) @@ -281,7 +281,7 @@ func TestProcessHbirdPacket(t *testing.T) { ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil return ret }, - srcInterface: 51, + srcInterface: 31, egressInterface: 0, assertFunc: assert.NoError, }, From 6f02cf71d51244cfe0740129ee12b6632ef80bad Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Fri, 17 Nov 2023 18:53:03 +0100 Subject: [PATCH 053/100] removed flyover checks after crossover. Moving flyover to next hop now only happens on crossover astransit --- pkg/slayers/path/hummingbird/raw.go | 37 +++++------ router/dataplane.go | 3 + router/dataplane_hbird.go | 99 ++++++++++++++++------------- 3 files changed, 75 insertions(+), 64 deletions(-) diff --git a/pkg/slayers/path/hummingbird/raw.go b/pkg/slayers/path/hummingbird/raw.go index 313193f8a1..0140a08630 100644 --- a/pkg/slayers/path/hummingbird/raw.go +++ b/pkg/slayers/path/hummingbird/raw.go @@ -162,7 +162,8 @@ func (s *Raw) GetCurrentHopField() (FlyoverHopField, error) { return s.GetHopField(int(s.PathMeta.CurrHF)) } -func (s *Raw) replacMac(idx int, mac []byte) error { +// ReplaceMac replaces the Mac of the hopfield at the given index with a new mac +func (s *Raw) ReplacMac(idx int, mac []byte) error { if idx >= s.NumLines-HopLines+1 { return serrors.New("HopField index out of bounds", "max", s.NumLines-HopLines, "actual", idx) @@ -177,7 +178,7 @@ func (s *Raw) replacMac(idx int, mac []byte) error { // SetCurrentMac replaces the Mac of the current hopfield by a new mac func (s *Raw) ReplaceCurrentMac(mac []byte) error { - return s.replacMac(int(s.PathMeta.CurrHF), mac) + return s.ReplacMac(int(s.PathMeta.CurrHF), mac) } // Returns a slice of the MAC of the hopfield starting at index idx @@ -278,24 +279,19 @@ func (s *Raw) GetPreviousIngress() (uint16, error) { return binary.BigEndian.Uint16(s.Raw[hopOffset+4 : hopOffset+6]), nil } -// Attaches current flyoverfield to next hopfield. +// Attaches previous flyoverfield to current hopfield. // DOES NOT adapt MACS. -// Next hopfield has to NOT already have a flyover +// Assumes previous hopfield has a flyover +// Assumes to be the first hop of the second or third segment func (s *Raw) DoFlyoverXover() error { idx := int(s.Base.PathMeta.CurrHF) - if idx >= s.NumLines-7 { - return serrors.New("CurrHF out of bounds for flyover crossover", - "max", s.NumLines-7, "actual", idx) - } - if s.PathMeta.CurrINF == 2 { - return serrors.New("Cannot do FlyoverXover if CurrINF = 2") - } - hopOffset := MetaLen + s.NumINF*path.InfoLen + idx*LineLen - if s.Raw[hopOffset]&0x80 == 0x00 { - return serrors.New("Current hop does not have a Flyover") + if idx >= s.NumLines-2 { + return serrors.New("CurrHF out of bounds", + "max", s.NumLines-2, "actual", idx) } - if s.Raw[hopOffset+FlyoverLen]&0x80 != 0x00 { - return serrors.New("Hop after Crossover has Flyover") + hopOffset := MetaLen + s.NumINF*path.InfoLen + idx*LineLen - FlyoverLen + if s.Raw[hopOffset+FlyoverLen]&0x80 == 0x80 { + return serrors.New("Current hop does already have a Flyover") } // buffer flyover and copy data var t [2 * LineLen]byte @@ -308,9 +304,10 @@ func (s *Raw) DoFlyoverXover() error { s.Raw[hopOffset] &= 0x7f s.Raw[hopOffset+HopLen] |= 0x80 // Adatp seglens - s.Base.PathMeta.SegLen[s.PathMeta.CurrINF] -= 2 - s.Base.PathMeta.SegLen[s.PathMeta.CurrINF+1] += 2 - return nil + s.Base.PathMeta.CurrHF -= 2 + s.Base.PathMeta.SegLen[s.PathMeta.CurrINF-1] -= 2 + s.Base.PathMeta.SegLen[s.PathMeta.CurrINF] += 2 + return s.Base.PathMeta.SerializeTo(s.Raw[:]) } // Attaches current flyoverfield to previous hopfield @@ -345,5 +342,5 @@ func (s *Raw) ReverseFlyoverXover() error { s.Base.PathMeta.SegLen[s.PathMeta.CurrINF] -= 2 s.Base.PathMeta.SegLen[s.PathMeta.CurrINF-1] += 2 s.Base.PathMeta.CurrHF += 2 - return nil + return s.Base.PathMeta.SerializeTo(s.Raw[:]) } diff --git a/router/dataplane.go b/router/dataplane.go index ca8d12d466..3825e5a042 100644 --- a/router/dataplane.go +++ b/router/dataplane.go @@ -1020,6 +1020,7 @@ func (p *scionPacketProcessor) reset() error { p.hbirdPath = nil p.flyoverField = hummingbird.FlyoverHopField{} p.hasPriority = false + p.isFlyoverXover = false if err := p.buffer.Clear(); err != nil { return serrors.WrapStr("Failed to clear buffer", err) @@ -1231,6 +1232,8 @@ type scionPacketProcessor struct { flyoverField hummingbird.FlyoverHopField // hasPriority indicates whether this packet has forwarding priority hasPriority bool + // isFlyoverXover is true if the current hop has a flyover and a cross-over is performed + isFlyoverXover bool // hbirdXkbuffer avoid allocating memory during aes computation for hummingbird flyover mac hbirdXkbuffer []uint32 diff --git a/router/dataplane_hbird.go b/router/dataplane_hbird.go index fa2a5662a0..833b2faa05 100644 --- a/router/dataplane_hbird.go +++ b/router/dataplane_hbird.go @@ -4,7 +4,6 @@ import ( "crypto/aes" "crypto/cipher" "crypto/subtle" - "encoding/binary" "errors" "fmt" "time" @@ -186,10 +185,7 @@ func (p *scionPacketProcessor) verifyCurrentHbirdMAC() (processResult, error) { scionMac := path.FullMAC(p.mac, p.infoField, p.hopField, p.macInputBuffer[:path.MACBufferSize]) // Aggregate MACS and verify if necessary if p.flyoverField.Flyover { - binary.BigEndian.PutUint32(flyoverMac[0:4], - binary.BigEndian.Uint32(scionMac[0:4])^binary.BigEndian.Uint32(flyoverMac[0:4])) - binary.BigEndian.PutUint16(flyoverMac[4:6], - binary.BigEndian.Uint16(scionMac[4:6])^binary.BigEndian.Uint16(flyoverMac[4:6])) + macXor(flyoverMac[:], scionMac[:], flyoverMac[:]) verified = subtle.ConstantTimeCompare(p.hopField.Mac[:path.MacLen], flyoverMac[:path.MacLen]) @@ -221,8 +217,12 @@ func (p *scionPacketProcessor) verifyCurrentHbirdMAC() (processResult, error) { } } // Add the full MAC to the SCION packet processor, - // such that EPIC and hummingbird mac de-aggregation do not need to recalculate it. - p.cachedMac = scionMac + // such that hummingbird mac de-aggregation do not need to recalculate it. + // Do not overwrite cachedmac after doing xover, as it may contain a flyovermac + if !p.effectiveXover { + p.cachedMac = scionMac + } + if verified == 0 { slowPathRequest := slowPathRequest{ scmpType: slayers.SCMPTypeParameterProblem, @@ -440,6 +440,15 @@ func (p *scionPacketProcessor) updateHbirdNonConsDirIngressSegID() error { return nil } +// Xors a and b and writes the result into d. +// +// Expects all arguments to have a length of macLen +func macXor(d, a, b []byte) { + for i := 0; i < path.MacLen; i++ { + d[i] = a[i] ^ b[i] + } +} + func (p *scionPacketProcessor) deAggregateMac() (processResult, error) { if !p.flyoverField.Flyover { return processResult{}, nil @@ -452,32 +461,37 @@ func (p *scionPacketProcessor) deAggregateMac() (processResult, error) { return processResult{}, nil } +// de-aggregates mac and stores the flyovermac part of the mac in cachedMac +func (p *scionPacketProcessor) deAggregateAndCacheMac() (processResult, error) { + if !p.flyoverField.Flyover { + return processResult{}, nil + } + // obtain flyoverMac and buffer in macInputBuffer + // such that it is not overwritten by the following standard mac computation + macXor(p.macInputBuffer[path.MACBufferSize:], p.cachedMac, p.hopField.Mac[:]) + // deaggregate Mac + copy(p.hopField.Mac[:], p.cachedMac[:path.MacLen]) + if err := p.hbirdPath.ReplaceCurrentMac(p.cachedMac); err != nil { + log.Debug("Failed to replace MAC after de-aggregation", "error", err.Error()) + return processResult{}, serrors.Join(err, serrors.New("Mac replacement failed")) + } + // set cachedMac to the buffered flyoverMac + p.cachedMac = p.macInputBuffer[path.MACBufferSize : path.MACBufferSize+path.MacLen] + return processResult{}, nil +} + func (p *scionPacketProcessor) doFlyoverXover() error { // Move flyoverhopfield to next hop for benefit of egress router if err := p.hbirdPath.DoFlyoverXover(); err != nil { return err } - // Buffer flyover part of current mac by xoring it with cached scionMac - binary.BigEndian.PutUint32(p.macInputBuffer[6:10], - binary.BigEndian.Uint32(p.flyoverField.HopField.Mac[0:4])^ - binary.BigEndian.Uint32(p.cachedMac[0:4])) - p.macInputBuffer[10] = p.flyoverField.HopField.Mac[4] ^ p.cachedMac[4] - p.macInputBuffer[11] = p.flyoverField.HopField.Mac[5] ^ p.cachedMac[5] - // deaggregate current mac - copy(p.hopField.Mac[:], p.cachedMac[0:6]) - if err := p.hbirdPath.ReplaceCurrentMac(p.cachedMac); err != nil { - return err - } - // Aggregate Mac of second hop - mac, err := p.hbirdPath.GetMac(int(p.hbirdPath.PathMeta.CurrHF) + hummingbird.HopLines) + // Aggregate mac of current hopfield with buffered flyoverMac + mac, err := p.hbirdPath.GetMac(int(p.hbirdPath.PathMeta.CurrHF)) if err != nil { return err } - binary.BigEndian.PutUint32(mac[0:4], - binary.BigEndian.Uint32(mac[0:4])^binary.BigEndian.Uint32(p.macInputBuffer[6:10])) - mac[4] ^= p.macInputBuffer[10] - mac[5] ^= p.macInputBuffer[11] + macXor(mac, mac, p.cachedMac) return nil } @@ -492,13 +506,15 @@ func (p *scionPacketProcessor) reverseFlyoverXover() error { func (p *scionPacketProcessor) doHbirdXover() (processResult, error) { p.effectiveXover = true - + inc := hummingbird.HopLines if p.flyoverField.Flyover { - if err := p.doFlyoverXover(); err != nil { - return processResult{}, err + p.isFlyoverXover = true + inc = hummingbird.FlyoverLines + if r, err := p.deAggregateAndCacheMac(); err != nil { + return r, err } } - if err := p.hbirdPath.IncPath(hummingbird.HopLines); err != nil { + if err := p.hbirdPath.IncPath(inc); err != nil { // TODO parameter problem invalid path return processResult{}, serrors.WrapStr("incrementing path", err) } @@ -513,7 +529,6 @@ func (p *scionPacketProcessor) doHbirdXover() (processResult, error) { return processResult{}, err } p.hopField = p.flyoverField.HopField - //TODO: modify method once we have definite design for flyover Xover return processResult{}, nil } @@ -574,9 +589,6 @@ func (p *scionPacketProcessor) processHBIRD() (processResult, error) { return r, err } } - // if err := p.updateHbirdNonConsDirIngressSegID(); err != nil { - // return processResult{}, err - // } if r, err := p.verifyCurrentHbirdMAC(); err != nil { return r, err } @@ -611,18 +623,11 @@ func (p *scionPacketProcessor) processHBIRD() (processResult, error) { if r, err := p.validateHopExpiry(); err != nil { return r, serrors.WithCtx(err, "info", "after xover") } - // verify the new block - if p.flyoverField.Flyover { - // TODO: can possibly skip this once we modify flyover at Xover implementation. - // Will need to aggregate new MAC though - if r, err := p.verifyCurrentHbirdMAC(); err != nil { - return r, err - } - } else { - if r, err := p.verifyCurrentMAC(); err != nil { - return r, err - } + // verify the new hopField + if r, err := p.verifyCurrentHbirdMAC(); err != nil { + return r, err } + if p.flyoverField.Flyover { //TODO: can skip repeating those if/once moving previous flyover at Xover is confirmed if r, err := p.validateReservationExpiry(); err != nil { @@ -651,7 +656,8 @@ func (p *scionPacketProcessor) processHBIRD() (processResult, error) { if r, err := p.deAggregateMac(); err != nil { return r, err } - if p.flyoverField.Flyover && p.hbirdPath.IsFirstHopAfterXover() && !p.peering { + if p.flyoverField.Flyover && p.hbirdPath.IsFirstHopAfterXover() && + !p.effectiveXover && !p.peering { if err := p.reverseFlyoverXover(); err != nil { return processResult{}, err } @@ -663,6 +669,11 @@ func (p *scionPacketProcessor) processHBIRD() (processResult, error) { } // ASTransit: pkts leaving from another AS BR. if a, ok := p.d.internalNextHops[egressID]; ok { + if p.isFlyoverXover { + if err := p.doFlyoverXover(); err != nil { + return processResult{}, err + } + } return processResult{OutAddr: a, OutPkt: p.rawPkt}, nil } errCode := slayers.SCMPCodeUnknownHopFieldEgress From e1b913c20f9a1de96229ddcc145e22fc9f33fd24 Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Mon, 20 Nov 2023 10:11:51 +0100 Subject: [PATCH 054/100] improved currentHopPointer() and currentInfoPointer() by making a clear case distinction --- router/dataplane.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/router/dataplane.go b/router/dataplane.go index 3825e5a042..dfd7228052 100644 --- a/router/dataplane.go +++ b/router/dataplane.go @@ -1534,16 +1534,18 @@ func (p *scionPacketProcessor) updateNonConsDirIngressSegID() error { return nil } +// TODO: write PR for scionproto for this case distinction here (juagargi) func (p *scionPacketProcessor) currentInfoPointer() uint16 { - if p.path == nil { + if p.scionLayer.PathType == hummingbird.PathType { return p.currentHbirdInfoPointer() } return uint16(slayers.CmnHdrLen + p.scionLayer.AddrHdrLen() + scion.MetaLen + path.InfoLen*int(p.path.PathMeta.CurrINF)) } +// TODO: write PR for scionproto for this case distinction here (juagargi) func (p *scionPacketProcessor) currentHopPointer() uint16 { - if p.path == nil { + if p.scionLayer.PathType == hummingbird.PathType { return p.currentHbirdHopPointer() } return uint16(slayers.CmnHdrLen + p.scionLayer.AddrHdrLen() + From 37a6528e28beffbbadd7b55d730f9198403f53b6 Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Mon, 20 Nov 2023 15:11:16 +0100 Subject: [PATCH 055/100] Update Bazel files. --- pkg/hummingbird/BUILD.bazel | 22 +++++++++++++++++---- pkg/slayers/path/hummingbird/BUILD.bazel | 1 - router/BUILD.bazel | 1 - router/tokenbucket/BUILD.bazel | 14 ++++--------- tools/end2end_hbird_integration/BUILD.bazel | 2 +- 5 files changed, 23 insertions(+), 17 deletions(-) diff --git a/pkg/hummingbird/BUILD.bazel b/pkg/hummingbird/BUILD.bazel index 08a2c48b55..c4005fde0e 100644 --- a/pkg/hummingbird/BUILD.bazel +++ b/pkg/hummingbird/BUILD.bazel @@ -1,10 +1,8 @@ -load("//tools/lint:go.bzl", "go_library") +load("//tools/lint:go.bzl", "go_library", "go_test") go_library( name = "go_default_library", - srcs = [ - "hummingbird.go", - ], + srcs = ["hummingbird.go"], importpath = "github.com/scionproto/scion/pkg/hummingbird", visibility = ["//visibility:public"], deps = [ @@ -20,3 +18,19 @@ go_library( ], ) +go_test( + name = "go_default_test", + srcs = [ + "hummingbird_test.go", + "utils_test.go", + ], + deps = [ + ":go_default_library", + "//pkg/slayers/path:go_default_library", + "//pkg/slayers/path/hummingbird:go_default_library", + "//pkg/slayers/path/scion:go_default_library", + "//pkg/snet:go_default_library", + "//pkg/snet/path:go_default_library", + "@com_github_stretchr_testify//assert:go_default_library", + ], +) diff --git a/pkg/slayers/path/hummingbird/BUILD.bazel b/pkg/slayers/path/hummingbird/BUILD.bazel index 083ea1e616..2250cf3e48 100644 --- a/pkg/slayers/path/hummingbird/BUILD.bazel +++ b/pkg/slayers/path/hummingbird/BUILD.bazel @@ -48,7 +48,6 @@ go_test( ":go_default_library", "//pkg/addr:go_default_library", "//pkg/slayers/path:go_default_library", - "@com_github_dchest_cmac//:go_default_library", "@com_github_stretchr_testify//assert:go_default_library", "@com_github_stretchr_testify//require:go_default_library", ], diff --git a/router/BUILD.bazel b/router/BUILD.bazel index 174a4b5e2f..8d444bed8d 100644 --- a/router/BUILD.bazel +++ b/router/BUILD.bazel @@ -70,7 +70,6 @@ go_test( "//private/underlay/conn:go_default_library", "//router/control:go_default_library", "//router/mock_router:go_default_library", - "//router/tokenbucket:go_default_library", "@com_github_golang_mock//gomock:go_default_library", "@com_github_google_gopacket//:go_default_library", "@com_github_google_gopacket//layers:go_default_library", diff --git a/router/tokenbucket/BUILD.bazel b/router/tokenbucket/BUILD.bazel index 616b2879a2..2ef5049512 100644 --- a/router/tokenbucket/BUILD.bazel +++ b/router/tokenbucket/BUILD.bazel @@ -2,22 +2,16 @@ load("//tools/lint:go.bzl", "go_library", "go_test") go_library( name = "go_default_library", - srcs = [ - "tokenbucket.go", - ], + srcs = ["tokenbucket.go"], importpath = "github.com/scionproto/scion/router/tokenbucket", visibility = ["//visibility:public"], - deps = [ - ], ) go_test( name = "go_default_test", - srcs = [ - "tokenbucket_test.go", - ], - embed = [":go_default_library"], + srcs = ["tokenbucket_test.go"], deps = [ + ":go_default_library", "@com_github_stretchr_testify//assert:go_default_library", ], -) \ No newline at end of file +) diff --git a/tools/end2end_hbird_integration/BUILD.bazel b/tools/end2end_hbird_integration/BUILD.bazel index 34ab53645b..9686daa652 100644 --- a/tools/end2end_hbird_integration/BUILD.bazel +++ b/tools/end2end_hbird_integration/BUILD.bazel @@ -21,4 +21,4 @@ scion_go_binary( name = "end2end_hbird_integration", embed = [":go_default_library"], visibility = ["//visibility:public"], -) \ No newline at end of file +) From ed0d3f91343c3ec1bfe89dd4f2d320785d5f32f6 Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Fri, 24 Nov 2023 15:47:47 +0100 Subject: [PATCH 056/100] changed bandwidth check to use resId-ingress-egress to differentiate between reservations --- router/dataplane_hbird.go | 60 +++++++++++++++++++++------------- router/dataplane_hbird_test.go | 54 ++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 22 deletions(-) diff --git a/router/dataplane_hbird.go b/router/dataplane_hbird.go index 833b2faa05..89b0d9ccdc 100644 --- a/router/dataplane_hbird.go +++ b/router/dataplane_hbird.go @@ -130,32 +130,41 @@ func (p *scionPacketProcessor) currentHbirdHopPointer() uint16 { hummingbird.LineLen*int(p.hbirdPath.PathMeta.CurrHF)) } +// Returns the ingress and egress through which the current packet enters and leves the AS +func (p *scionPacketProcessor) getFlyoverInterfaces() (uint16, uint16, error) { + ingress := p.hopField.ConsIngress + egress := p.hopField.ConsEgress + // Reservations are not bidirectional, + // reservation ingress and egress are always real ingress and egress + if !p.infoField.ConsDir { + ingress, egress = egress, ingress + } + // On crossovers, A Reservation goes from the ingress of the incoming hop to + // the egress of the outgoing one + var err error + if p.hbirdPath.IsXover() && !p.peering { + egress, err = p.hbirdPath.GetNextEgress() + if err != nil { + return 0, 0, err + } + } else if p.hbirdPath.IsFirstHopAfterXover() && !p.peering { + ingress, err = p.hbirdPath.GetPreviousIngress() + if err != nil { + return 0, 0, err + } + } + return ingress, egress, nil +} + func (p *scionPacketProcessor) verifyCurrentHbirdMAC() (processResult, error) { var flyoverMac []byte var verified int // Compute flyovermac if p.flyoverField.Flyover { - ingress := p.hopField.ConsIngress - egress := p.hopField.ConsEgress - // Reservations are not bidirectional, - // reservation ingress and egress are always real ingress and egress - if !p.infoField.ConsDir { - ingress, egress = egress, ingress - } - // On crossovers, A Reservation goes from the ingress of the incoming hop to - // the egress of the outgoing one - var err error - if p.hbirdPath.IsXover() && !p.peering { - egress, err = p.hbirdPath.GetNextEgress() - if err != nil { - return processResult{}, err - } - } else if p.hbirdPath.IsFirstHopAfterXover() && !p.peering { - ingress, err = p.hbirdPath.GetPreviousIngress() - if err != nil { - return processResult{}, err - } + ingress, egress, err := p.getFlyoverInterfaces() + if err != nil { + return processResult{}, err } ak := hummingbird.DeriveAuthKey(p.prf, p.flyoverField.ResID, p.flyoverField.Bw, @@ -329,7 +338,14 @@ func (p *scionPacketProcessor) checkReservationBandwidth() (processResult, error if !p.hasPriority { return processResult{}, nil } - v, ok := p.d.tokenBuckets.Load(p.flyoverField.ResID) + // resID only has to be unique per interface pair + // key for the tokenbuckets map is based on flyover resID, ingress and egress + ingress, egress, err := p.getFlyoverInterfaces() + if err != nil { + return processResult{}, err + } + resKey := uint64(p.flyoverField.ResID) + uint64(ingress)<<22 + uint64(egress)<<38 + v, ok := p.d.tokenBuckets.Load(resKey) if ok { // Check bandwidth tb, ok := v.(*tokenbucket.TokenBucket) @@ -355,7 +371,7 @@ func (p *scionPacketProcessor) checkReservationBandwidth() (processResult, error resBw := convertResBw(p.flyoverField.Bw) now := time.Now() tb := tokenbucket.NewTokenBucket(now, resBw, resBw) - p.d.tokenBuckets.Store(p.flyoverField.ResID, tb) + p.d.tokenBuckets.Store(resKey, tb) if tb.Apply(int(p.scionLayer.PayloadLen), time.Now()) { return processResult{}, nil diff --git a/router/dataplane_hbird_test.go b/router/dataplane_hbird_test.go index 67ba724eb0..4da0f3499f 100644 --- a/router/dataplane_hbird_test.go +++ b/router/dataplane_hbird_test.go @@ -3815,6 +3815,60 @@ func TestBandwidthCheckDifferentResID(t *testing.T) { assert.NoError(t, err) } +func TestBandwidthCheckDifferentEgress(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + key := []byte("testkey_xxxxxxxx") + sv := []byte("test_secretvalue") + now := time.Now() + + dp := router.NewDP( + map[uint16]router.BatchConn{ + uint16(2): mock_router.NewMockBatchConn(ctrl), + uint16(3): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 1: topology.Parent, + 2: topology.Child, + 3: topology.Child, + }, + nil, nil, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) + + spkt, dpath := prepHbirdMsg(now) + dpath.HopFields = []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 31, ConsEgress: 30}}, + {Flyover: true, HopField: path.HopField{ConsIngress: 1, ConsEgress: 2}, ResID: 42, + Bw: 2, ResStartTime: 123, Duration: 304}, + {HopField: path.HopField{ConsIngress: 40, ConsEgress: 41}}, + } + dpath.Base.PathMeta.SegLen[0] = 11 + dpath.Base.PathMeta.CurrHF = 3 + dpath.Base.NumLines = 11 + + spkt.PayloadLen = 120 + dpath.HopFields[1].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, + spkt.PayloadLen, dpath.InfoFields[0], dpath.HopFields[1], dpath.Base.PathMeta) + + msg := toLongMsg(t, spkt, dpath) + + _, err := dp.ProcessPkt(1, msg) + assert.NoError(t, err) + + msg = toLongMsg(t, spkt, dpath) + _, err = dp.ProcessPkt(1, msg) + assert.Error(t, err) + + // Reservation with same resID but different Ingress/Egress pair is a different reservation + dpath.HopFields[1].HopField.ConsEgress = 3 + spkt.PayloadLen = 120 + dpath.HopFields[1].HopField.Mac = computeAggregateMac(t, key, sv, spkt.DstIA, + spkt.PayloadLen, dpath.InfoFields[0], dpath.HopFields[1], dpath.Base.PathMeta) + msg = toLongMsg(t, spkt, dpath) + _, err = dp.ProcessPkt(1, msg) + assert.NoError(t, err) +} + func toLongMsg(t *testing.T, spkt *slayers.SCION, dpath path.Path) *ipv4.Message { t.Helper() ret := &ipv4.Message{} From 7a8f061c443df22727d12eba23a2202db34d6740 Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Wed, 6 Dec 2023 13:49:02 +0100 Subject: [PATCH 057/100] fixed issue when multiple threads create new tokenbucket of the same reservation --- router/dataplane_hbird.go | 8 +++++++- router/dataplane_hbird_test.go | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/router/dataplane_hbird.go b/router/dataplane_hbird.go index 89b0d9ccdc..a6abb239d1 100644 --- a/router/dataplane_hbird.go +++ b/router/dataplane_hbird.go @@ -371,7 +371,13 @@ func (p *scionPacketProcessor) checkReservationBandwidth() (processResult, error resBw := convertResBw(p.flyoverField.Bw) now := time.Now() tb := tokenbucket.NewTokenBucket(now, resBw, resBw) - p.d.tokenBuckets.Store(resKey, tb) + r, _ := p.d.tokenBuckets.LoadOrStore(resKey, tb) + + tb, ok = r.(*tokenbucket.TokenBucket) + if !ok { + log.Error("Non-tokenbucket value found in tokenbucket map") + panic("tokenbucket map contains value of different type") + } if tb.Apply(int(p.scionLayer.PayloadLen), time.Now()) { return processResult{}, nil diff --git a/router/dataplane_hbird_test.go b/router/dataplane_hbird_test.go index 4da0f3499f..b1fe265e51 100644 --- a/router/dataplane_hbird_test.go +++ b/router/dataplane_hbird_test.go @@ -3709,6 +3709,8 @@ func TestHbirdPacketPath(t *testing.T) { } } +// TODO(juagargi): write test for concurrent bandwidth check calls + func TestBandwidthCheck(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() From 688cf3240c52c40f6d48d7632ab2720f954ce39d Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Thu, 18 Jan 2024 17:15:45 +0100 Subject: [PATCH 058/100] split processing of hbird with and without flyover, fixed last remaining linter errors --- pkg/hummingbird/hummingbird_test.go | 18 +- router/dataplane_hbird.go | 296 +++++++++++++++++++--------- router/dataplane_hbird_test.go | 76 ------- 3 files changed, 220 insertions(+), 170 deletions(-) diff --git a/pkg/hummingbird/hummingbird_test.go b/pkg/hummingbird/hummingbird_test.go index a580e4c8e9..2acb6ca3b8 100644 --- a/pkg/hummingbird/hummingbird_test.go +++ b/pkg/hummingbird/hummingbird_test.go @@ -1,12 +1,28 @@ +// Copyright 2020 Anapaya Systems +// Copyright 2023 ETH Zurich +// +// 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 hummingbird_test import ( "testing" "time" + "github.com/stretchr/testify/assert" + "github.com/scionproto/scion/pkg/hummingbird" snetpath "github.com/scionproto/scion/pkg/snet/path" - "github.com/stretchr/testify/assert" ) var testHops = []hummingbird.Hop{ diff --git a/router/dataplane_hbird.go b/router/dataplane_hbird.go index a6abb239d1..e2f9340370 100644 --- a/router/dataplane_hbird.go +++ b/router/dataplane_hbird.go @@ -1,3 +1,18 @@ +// Copyright 2020 Anapaya Systems +// Copyright 2023 ETH Zurich +// +// 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 router import ( @@ -101,6 +116,24 @@ func (p *scionPacketProcessor) determinePeerHbird() (processResult, error) { return processResult{}, err } +func (p *scionPacketProcessor) validateHopExpiryHbird() (processResult, error) { + expiration := util.SecsToTime(p.infoField.Timestamp). + Add(path.ExpTimeToDuration(p.hopField.ExpTime)) + expired := expiration.Before(time.Now()) + if !expired { + return processResult{}, nil + } + log.Debug("SCMP: expired hop", "cons_dir", p.infoField.ConsDir, "if_id", p.ingressID, + "curr_inf", p.hbirdPath.PathMeta.CurrINF, "curr_hf", p.hbirdPath.PathMeta.CurrHF) + slowPathRequest := slowPathRequest{ + scmpType: slayers.SCMPTypeParameterProblem, + code: slayers.SCMPCodePathExpired, + pointer: p.currentHopPointer(), + cause: expiredHop, + } + return processResult{SlowPathRequest: slowPathRequest}, slowPathRequired +} + func (p *scionPacketProcessor) validateReservationExpiry() (processResult, error) { startTime := util.SecsToTime(p.hbirdPath.PathMeta.BaseTS - uint32(p.flyoverField.ResStartTime)) endTime := startTime.Add(time.Duration(p.flyoverField.Duration) * time.Second) @@ -156,81 +189,77 @@ func (p *scionPacketProcessor) getFlyoverInterfaces() (uint16, uint16, error) { return ingress, egress, nil } -func (p *scionPacketProcessor) verifyCurrentHbirdMAC() (processResult, error) { +func (p *scionPacketProcessor) verifyHbirdScionMac() (processResult, error) { + scionMac := path.FullMAC(p.mac, p.infoField, p.hopField, p.macInputBuffer[:path.MACBufferSize]) + verified := subtle.ConstantTimeCompare(p.hopField.Mac[:path.MacLen], scionMac[:path.MacLen]) + if verified == 0 { + log.Debug("SCMP: MAC verification failed", "expected", fmt.Sprintf( + "%x", scionMac[:path.MacLen]), + "actual", fmt.Sprintf("%x", p.hopField.Mac[:path.MacLen]), + "cons_dir", p.infoField.ConsDir, + "if_id", p.ingressID, "curr_inf", p.hbirdPath.PathMeta.CurrINF, + "curr_hf", p.hbirdPath.PathMeta.CurrHF, "seg_id", p.infoField.SegID) + slowPathRequest := slowPathRequest{ + scmpType: slayers.SCMPTypeParameterProblem, + code: slayers.SCMPCodeInvalidHopFieldMAC, + pointer: p.currentHopPointer(), + cause: macVerificationFailed, + } + return processResult{SlowPathRequest: slowPathRequest}, slowPathRequired + } + return processResult{}, nil +} + +func (p *scionPacketProcessor) verifyHbirdFlyoverMac() (processResult, error) { var flyoverMac []byte var verified int - // Compute flyovermac - if p.flyoverField.Flyover { - ingress, egress, err := p.getFlyoverInterfaces() - if err != nil { - return processResult{}, err - } - - ak := hummingbird.DeriveAuthKey(p.prf, p.flyoverField.ResID, p.flyoverField.Bw, - ingress, egress, p.hbirdPath.PathMeta.BaseTS-uint32(p.flyoverField.ResStartTime), - p.flyoverField.Duration, - p.macInputBuffer[path.MACBufferSize+hummingbird.FlyoverMacBufferSize:]) - flyoverMac = hummingbird.FullFlyoverMac(ak, p.scionLayer.DstIA, p.scionLayer.PayloadLen, - p.flyoverField.ResStartTime, p.hbirdPath.PathMeta.HighResTS, - p.macInputBuffer[path.MACBufferSize:], p.hbirdXkbuffer) + ingress, egress, err := p.getFlyoverInterfaces() + if err != nil { + return processResult{}, err } - // Perform updateHbirdNonConsDirIngressSegID - // This needs de-aggregated MAC but needs to be done before scionMac is computed - // Therefore, we check this here instead of before MAC computation like in standard SCiON + + ak := hummingbird.DeriveAuthKey(p.prf, p.flyoverField.ResID, p.flyoverField.Bw, + ingress, egress, p.hbirdPath.PathMeta.BaseTS-uint32(p.flyoverField.ResStartTime), + p.flyoverField.Duration, + p.macInputBuffer[path.MACBufferSize+hummingbird.FlyoverMacBufferSize:]) + flyoverMac = hummingbird.FullFlyoverMac(ak, p.scionLayer.DstIA, p.scionLayer.PayloadLen, + p.flyoverField.ResStartTime, p.hbirdPath.PathMeta.HighResTS, + p.macInputBuffer[path.MACBufferSize:], p.hbirdXkbuffer) + if !p.hbirdPath.IsFirstHopAfterXover() { - if p.flyoverField.Flyover { - err := p.updateHbirdNonConsDirIngressSegIDFlyover(flyoverMac) - if err != nil { - return processResult{}, err - } - } else { - if err := p.updateHbirdNonConsDirIngressSegID(); err != nil { - return processResult{}, err - } + err := p.updateHbirdNonConsDirIngressSegIDFlyover(flyoverMac) + if err != nil { + return processResult{}, err } } - // Compute scionMac scionMac := path.FullMAC(p.mac, p.infoField, p.hopField, p.macInputBuffer[:path.MACBufferSize]) - // Aggregate MACS and verify if necessary - if p.flyoverField.Flyover { - macXor(flyoverMac[:], scionMac[:], flyoverMac[:]) - - verified = subtle.ConstantTimeCompare(p.hopField.Mac[:path.MacLen], - flyoverMac[:path.MacLen]) - if verified == 0 { - log.Debug("SCMP: Aggregate MAC verification failed", - "expected", fmt.Sprintf("%x", flyoverMac[:path.MacLen]), - "actual", fmt.Sprintf("%x", p.hopField.Mac[:path.MacLen]), - "cons_dir", p.infoField.ConsDir, - "scionMac", fmt.Sprintf("%x", scionMac[:path.MacLen]), - "if_id", p.ingressID, "curr_inf", p.hbirdPath.PathMeta.CurrINF, - "curr_hf", p.hbirdPath.PathMeta.CurrHF, "seg_id", p.infoField.SegID, - "packet length", p.scionLayer.PayloadLen, - "dest", p.scionLayer.DstIA, "startTime", p.flyoverField.ResStartTime, - "highResTS", p.hbirdPath.PathMeta.HighResTS, - "ResID", p.flyoverField.ResID, "Bw", p.flyoverField.Bw, - "in", p.hopField.ConsIngress, "Eg", p.hopField.ConsEgress, - "start ak", p.hbirdPath.PathMeta.BaseTS-uint32(p.flyoverField.ResStartTime), - "Duration", p.flyoverField.Duration) - } - } else { - verified = subtle.ConstantTimeCompare(p.hopField.Mac[:path.MacLen], scionMac[:path.MacLen]) - if verified == 0 { - log.Debug("SCMP: MAC verification failed", "expected", fmt.Sprintf( - "%x", scionMac[:path.MacLen]), - "actual", fmt.Sprintf("%x", p.hopField.Mac[:path.MacLen]), - "cons_dir", p.infoField.ConsDir, - "if_id", p.ingressID, "curr_inf", p.hbirdPath.PathMeta.CurrINF, - "curr_hf", p.hbirdPath.PathMeta.CurrHF, "seg_id", p.infoField.SegID) - } + + macXor(flyoverMac[:], scionMac[:], flyoverMac[:]) + verified = subtle.ConstantTimeCompare(p.hopField.Mac[:path.MacLen], flyoverMac[:path.MacLen]) + if verified == 0 { + log.Debug("SCMP: Aggregate MAC verification failed", + "expected", fmt.Sprintf("%x", flyoverMac[:path.MacLen]), + "actual", fmt.Sprintf("%x", p.hopField.Mac[:path.MacLen]), + "cons_dir", p.infoField.ConsDir, + "scionMac", fmt.Sprintf("%x", scionMac[:path.MacLen]), + "if_id", p.ingressID, "curr_inf", p.hbirdPath.PathMeta.CurrINF, + "curr_hf", p.hbirdPath.PathMeta.CurrHF, "seg_id", p.infoField.SegID, + "packet length", p.scionLayer.PayloadLen, + "dest", p.scionLayer.DstIA, "startTime", p.flyoverField.ResStartTime, + "highResTS", p.hbirdPath.PathMeta.HighResTS, + "ResID", p.flyoverField.ResID, "Bw", p.flyoverField.Bw, + "in", p.hopField.ConsIngress, "Eg", p.hopField.ConsEgress, + "start ak", p.hbirdPath.PathMeta.BaseTS-uint32(p.flyoverField.ResStartTime), + "Duration", p.flyoverField.Duration) } + // Add the full MAC to the SCION packet processor, // such that hummingbird mac de-aggregation do not need to recalculate it. // Do not overwrite cachedmac after doing xover, as it may contain a flyovermac - if !p.effectiveXover { - p.cachedMac = scionMac - } + // This function is currently not called after a xover, so no need to check + // Keep in mind for future changes + p.cachedMac = scionMac if verified == 0 { slowPathRequest := slowPathRequest{ @@ -526,17 +555,33 @@ func (p *scionPacketProcessor) reverseFlyoverXover() error { return nil } -func (p *scionPacketProcessor) doHbirdXover() (processResult, error) { +func (p *scionPacketProcessor) doHbirdXoverFlyover() (processResult, error) { p.effectiveXover = true - inc := hummingbird.HopLines - if p.flyoverField.Flyover { - p.isFlyoverXover = true - inc = hummingbird.FlyoverLines - if r, err := p.deAggregateAndCacheMac(); err != nil { - return r, err - } + p.isFlyoverXover = true + + if r, err := p.deAggregateAndCacheMac(); err != nil { + return r, err } - if err := p.hbirdPath.IncPath(inc); err != nil { + + if err := p.hbirdPath.IncPath(hummingbird.FlyoverLines); err != nil { + return processResult{}, serrors.WrapStr("incrementing path", err) + } + + var err error + if p.flyoverField, err = p.hbirdPath.GetCurrentHopField(); err != nil { + return processResult{}, err + } + if p.infoField, err = p.hbirdPath.GetCurrentInfoField(); err != nil { + return processResult{}, err + } + p.hopField = p.flyoverField.HopField + return processResult{}, nil +} + +func (p *scionPacketProcessor) doHbirdXoverBestEffort() (processResult, error) { + p.effectiveXover = true + + if err := p.hbirdPath.IncPath(hummingbird.HopLines); err != nil { // TODO parameter problem invalid path return processResult{}, serrors.WrapStr("incrementing path", err) } @@ -591,7 +636,7 @@ func (p *scionPacketProcessor) processHBIRD() (processResult, error) { if r, err := p.determinePeerHbird(); err != nil { return r, err } - if r, err := p.validateHopExpiry(); err != nil { + if r, err := p.validateHopExpiryHbird(); err != nil { return r, err } if r, err := p.validateIngressID(); err != nil { @@ -607,16 +652,20 @@ func (p *scionPacketProcessor) processHBIRD() (processResult, error) { return r, err } if p.flyoverField.Flyover { - if r, err := p.validateReservationExpiry(); err != nil { - return r, err - } + return p.processHBIRDFlyover() } - if r, err := p.verifyCurrentHbirdMAC(); err != nil { + return p.processHBIRDBestEffort() +} + +func (p *scionPacketProcessor) processHBIRDFlyover() (processResult, error) { + + if r, err := p.validateReservationExpiry(); err != nil { return r, err } - if p.hasPriority && p.flyoverField.Flyover { - p.validatePathMetaTimestamp() + if r, err := p.verifyHbirdFlyoverMac(); err != nil { + return r, err } + p.validatePathMetaTimestamp() if r, err := p.checkReservationBandwidth(); err != nil { return r, err } @@ -639,26 +688,16 @@ func (p *scionPacketProcessor) processHBIRD() (processResult, error) { // Outbound: pkts leaving the local IA. // BRTransit: pkts leaving from the same BR different interface. if !p.peering && p.hbirdPath.IsXover() { - if r, err := p.doHbirdXover(); err != nil { + if r, err := p.doHbirdXoverFlyover(); err != nil { return r, err } if r, err := p.validateHopExpiry(); err != nil { return r, serrors.WithCtx(err, "info", "after xover") } // verify the new hopField - if r, err := p.verifyCurrentHbirdMAC(); err != nil { + if r, err := p.verifyHbirdScionMac(); err != nil { return r, err } - - if p.flyoverField.Flyover { - //TODO: can skip repeating those if/once moving previous flyover at Xover is confirmed - if r, err := p.validateReservationExpiry(); err != nil { - return r, serrors.WithCtx(err, "info", "after xover") - } - if p.hasPriority { - p.validatePathMetaTimestamp() - } - } } if r, err := p.validateEgressID(); err != nil { return r, err @@ -678,8 +717,7 @@ func (p *scionPacketProcessor) processHBIRD() (processResult, error) { if r, err := p.deAggregateMac(); err != nil { return r, err } - if p.flyoverField.Flyover && p.hbirdPath.IsFirstHopAfterXover() && - !p.effectiveXover && !p.peering { + if p.hbirdPath.IsFirstHopAfterXover() && !p.effectiveXover && !p.peering { if err := p.reverseFlyoverXover(); err != nil { return processResult{}, err } @@ -712,6 +750,78 @@ func (p *scionPacketProcessor) processHBIRD() (processResult, error) { return processResult{SlowPathRequest: slowPathRequest}, slowPathRequired } +func (p *scionPacketProcessor) processHBIRDBestEffort() (processResult, error) { + + if err := p.updateHbirdNonConsDirIngressSegID(); err != nil { + return processResult{}, err + } + if r, err := p.verifyHbirdScionMac(); err != nil { + return r, err + } + if r, err := p.handleHbirdIngressRouterAlert(); err != nil { + return r, err + } + // Inbound: pkts destined to the local IA. + if p.scionLayer.DstIA == p.d.localIA { + a, r, err := p.resolveInbound() + if err != nil { + return r, err + } + return processResult{OutAddr: a, OutPkt: p.rawPkt}, nil + } + + // Outbound: pkts leaving the local IA. + // BRTransit: pkts leaving from the same BR different interface. + if !p.peering && p.hbirdPath.IsXover() { + if r, err := p.doHbirdXoverBestEffort(); err != nil { + return r, err + } + if r, err := p.validateHopExpiryHbird(); err != nil { + return r, serrors.WithCtx(err, "info", "after xover") + } + // verify the new hopField + if r, err := p.verifyHbirdScionMac(); err != nil { + return r, err + } + } + if r, err := p.validateEgressID(); err != nil { + return r, err + } + // handle egress router alert before we check if it's up because we want to + // send the reply anyway, so that trace route can pinpoint the exact link + // that failed. + if r, err := p.handleHbirdEgressRouterAlert(); err != nil { + return r, err + } + if r, err := p.validateEgressUp(); err != nil { + return r, err + } + + egressID := p.egressInterface() + if _, ok := p.d.external[egressID]; ok { + if err := p.processHbirdEgress(); err != nil { + return processResult{}, err + } + return processResult{EgressID: egressID, OutPkt: p.rawPkt}, nil + } + // ASTransit: pkts leaving from another AS BR. + if a, ok := p.d.internalNextHops[egressID]; ok { + return processResult{OutAddr: a, OutPkt: p.rawPkt}, nil + } + errCode := slayers.SCMPCodeUnknownHopFieldEgress + if !p.infoField.ConsDir { + errCode = slayers.SCMPCodeUnknownHopFieldIngress + } + log.Debug("SCMP: cannot route") + slowPathRequest := slowPathRequest{ + scmpType: slayers.SCMPTypeParameterProblem, + code: errCode, + pointer: p.currentHopPointer(), + cause: cannotRoute, + } + return processResult{SlowPathRequest: slowPathRequest}, slowPathRequired +} + // Functions for SCMP packets preparation func (p *slowPathPacketProcessor) prepareHbirdSCMP( diff --git a/router/dataplane_hbird_test.go b/router/dataplane_hbird_test.go index b1fe265e51..017ce8545f 100644 --- a/router/dataplane_hbird_test.go +++ b/router/dataplane_hbird_test.go @@ -848,79 +848,6 @@ func TestProcessHbirdPacket(t *testing.T) { egressInterface: 0, assertFunc: assert.NoError, }, - "astransit xover flyover non consdir": { - // Test currently not working - prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { - return router.NewDP(nil, - map[uint16]topology.LinkType{ - 51: topology.Child, - 3: topology.Core, - }, - mock_router.NewMockBatchConn(ctrl), - map[uint16]*net.UDPAddr{ - uint16(3): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, - }, nil, xtest.MustParseIA("1-ff00:0:110"), nil, key, sv) - }, - mockMsg: func(afterProcessing bool) *ipv4.Message { - spkt, _ := prepHbirdMsg(now) - dpath := &hummingbird.Decoded{ - Base: hummingbird.Base{ - PathMeta: hummingbird.MetaHdr{ - CurrHF: 6, - SegLen: [3]uint8{6, 8, 0}, - BaseTS: util.TimeToSecs(now), - }, - NumINF: 2, - NumLines: 16, - }, - InfoFields: []path.InfoField{ - // up seg - {SegID: 0x111, ConsDir: false, Timestamp: util.TimeToSecs(now)}, - // core seg - {SegID: 0x222, ConsDir: false, Timestamp: util.TimeToSecs(now)}, - }, - HopFields: []hummingbird.FlyoverHopField{ - {HopField: path.HopField{ConsIngress: 0, ConsEgress: 1}}, // IA 110 - {HopField: path.HopField{ConsIngress: 31, ConsEgress: 0}}, // Src - {Flyover: true, HopField: path.HopField{ConsIngress: 0, ConsEgress: 51}, - ResID: 34, ResStartTime: 5, Duration: 310}, // Dst - {HopField: path.HopField{ConsIngress: 3, ConsEgress: 0}}, // IA 110 - }, - } - dpath.HopFields[2].HopField.Mac = computeAggregateMacXover(t, key, sv, spkt.DstIA, - spkt.PayloadLen, 3, 51, - dpath.InfoFields[0], dpath.HopFields[2], dpath.PathMeta) - dpath.HopFields[3].HopField.Mac = computeMAC(t, key, dpath.InfoFields[1], - dpath.HopFields[3].HopField) - if !afterProcessing { - dpath.InfoFields[0].UpdateSegID(computeMAC(t, key, dpath.InfoFields[0], - dpath.HopFields[2].HopField)) - - return toMsg(t, spkt, dpath) - } - dpath.HopFields[2].Flyover = false - dpath.HopFields[3].Flyover = true - dpath.HopFields[3].ResID = 34 - dpath.HopFields[3].ResStartTime = 5 - dpath.HopFields[3].Duration = 310 - - dpath.HopFields[2].HopField.Mac = computeMAC(t, key, dpath.InfoFields[0], - dpath.HopFields[2].HopField) - dpath.HopFields[3].HopField.Mac = computeAggregateMacXover(t, key, sv, - spkt.DstIA, spkt.PayloadLen, 3, 51, dpath.InfoFields[1], dpath.HopFields[3], - dpath.PathMeta) - dpath.PathMeta.SegLen[0] -= 2 - dpath.PathMeta.SegLen[1] += 2 - require.NoError(t, dpath.IncPath(hummingbird.FlyoverLines)) - ret := toMsg(t, spkt, dpath) - ret.Addr = &net.UDPAddr{IP: net.ParseIP("10.0.200.200").To4(), Port: 30043} - ret.Flags, ret.NN, ret.N, ret.OOB = 0, 0, 0, nil - return ret - }, - srcInterface: 51, - egressInterface: 0, - assertFunc: assert.NoError, - }, "astransit xover flyover ingress": { prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { return router.NewDP(nil, @@ -1400,9 +1327,6 @@ func TestProcessHbirdPacket(t *testing.T) { for name, tc := range testCases { name, tc := name, tc - if name == "astransit xover flyover non consdir" { - continue - } t.Run(name, func(t *testing.T) { t.Parallel() dp := tc.prepareDP(ctrl) From 5a005b6417c4ef2bf05039d5f2c68c359f23f95e Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Tue, 23 Jan 2024 13:27:39 +0100 Subject: [PATCH 059/100] removed log statement printing every packet --- router/dataplane_hbird.go | 1 - 1 file changed, 1 deletion(-) diff --git a/router/dataplane_hbird.go b/router/dataplane_hbird.go index e2f9340370..ef44acdada 100644 --- a/router/dataplane_hbird.go +++ b/router/dataplane_hbird.go @@ -625,7 +625,6 @@ func (p *scionPacketProcessor) processHbirdEgress() error { func (p *scionPacketProcessor) processHBIRD() (processResult, error) { var ok bool p.hbirdPath, ok = p.scionLayer.Path.(*hummingbird.Raw) - log.Debug("raw path", "path", fmt.Sprintf("%x", p.hbirdPath.Raw)) if !ok { // TODO(lukedirtwalker) parameter problem invalid path? return processResult{}, malformedPath From 15a93adb83bc8ff1307103a5dea08aafa3337ea5 Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Tue, 23 Jan 2024 13:39:24 +0100 Subject: [PATCH 060/100] added benchmarks for scion and hbird flyoverless processing --- router/dataplane_benchmark_test.go | 373 +++++++++++++++++++++++++++++ router/export_test.go | 13 + 2 files changed, 386 insertions(+) create mode 100644 router/dataplane_benchmark_test.go diff --git a/router/dataplane_benchmark_test.go b/router/dataplane_benchmark_test.go new file mode 100644 index 0000000000..e42024c97d --- /dev/null +++ b/router/dataplane_benchmark_test.go @@ -0,0 +1,373 @@ +package router_test + +import ( + "net" + "testing" + "time" + + "github.com/golang/mock/gomock" + "github.com/google/gopacket" + "github.com/scionproto/scion/pkg/private/util" + "github.com/scionproto/scion/pkg/private/xtest" + "github.com/scionproto/scion/pkg/scrypto" + "github.com/scionproto/scion/pkg/slayers" + "github.com/scionproto/scion/pkg/slayers/path" + "github.com/scionproto/scion/pkg/slayers/path/hummingbird" + "github.com/scionproto/scion/pkg/slayers/path/scion" + "github.com/scionproto/scion/private/topology" + "github.com/scionproto/scion/router" + "github.com/scionproto/scion/router/mock_router" + "github.com/stretchr/testify/require" + "golang.org/x/net/ipv4" +) + +const ( + benchmarkPayloadLen = 120 +) + +// We measure the time necessary to process 100 packets (process function and reset function in between) +// This allows to differentiate between different space usages of bandwidth (TODO: what sizes are relevant???) + +// standard SCION benchmark for reference +func BenchmarkProcessScion(b *testing.B) { + // prepare Dataplane + ctrl := gomock.NewController(b) + defer ctrl.Finish() + + key := []byte("testkey_xxxxxxxx") + sv := []byte("test_secretvalue") + + dp := router.NewDP( + map[uint16]router.BatchConn{ + uint16(31): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 7: topology.Core, + 31: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(7): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) + + // prepare PacketProcessor + pp := dp.NewBenchmarkPP() + + // prepare packet + now := time.Now() + + spkt := &slayers.SCION{ + Version: 0, + TrafficClass: 0xb8, + FlowID: 0xdead, + NextHdr: slayers.L4UDP, + PathType: scion.PathType, + DstIA: xtest.MustParseIA("4-ff00:0:411"), + SrcIA: xtest.MustParseIA("2-ff00:0:222"), + Path: &scion.Raw{}, + PayloadLen: benchmarkPayloadLen, + } + + dpath := &scion.Decoded{ + Base: scion.Base{ + PathMeta: scion.MetaHdr{ + CurrHF: 1, + SegLen: [3]uint8{3, 3, 0}, + }, + NumINF: 2, + NumHops: 6, + }, + InfoFields: []path.InfoField{ + {SegID: 0x111, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + {SegID: 0x222, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + }, + + HopFields: []path.HopField{ + {ConsIngress: 0, ConsEgress: 2}, + {ConsIngress: 7, ConsEgress: 31}, + {ConsIngress: 3, ConsEgress: 0}, + {ConsIngress: 0, ConsEgress: 6}, + {ConsIngress: 8, ConsEgress: 9}, + {ConsIngress: 11, ConsEgress: 0}, + }, + } + + dpath.HopFields[1].Mac = benchmarkScionMac(b, key, dpath.InfoFields[0], dpath.HopFields[1]) + msg := toBenchmarkMsg(b, spkt, dpath) + + backup := make([]byte, len(msg.Buffers[0])) + copy(backup, msg.Buffers[0]) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + copy(msg.Buffers[0], backup) + pp.ProcessPkt(7, msg) + // DO NOT check for errors when getting actual numbers from benchmark + // require.NoError(b, err) // verify no failures on repeated usage of same packet + } +} + +func BenchmarkProcessScionXover(b *testing.B) { + // prepare Dataplane + ctrl := gomock.NewController(b) + defer ctrl.Finish() + + key := []byte("testkey_xxxxxxxx") + sv := []byte("test_secretvalue") + + dp := router.NewDP( + map[uint16]router.BatchConn{ + uint16(31): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 7: topology.Core, + 31: topology.Child, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(7): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) + + // prepare PacketProcessor + pp := dp.NewBenchmarkPP() + + // prepare packet + now := time.Now() + + spkt := &slayers.SCION{ + Version: 0, + TrafficClass: 0xb8, + FlowID: 0xdead, + NextHdr: slayers.L4UDP, + PathType: scion.PathType, + DstIA: xtest.MustParseIA("4-ff00:0:411"), + SrcIA: xtest.MustParseIA("2-ff00:0:222"), + Path: &scion.Raw{}, + PayloadLen: benchmarkPayloadLen, + } + + dpath := &scion.Decoded{ + Base: scion.Base{ + PathMeta: scion.MetaHdr{ + CurrHF: 2, + SegLen: [3]uint8{3, 3, 0}, + }, + NumINF: 2, + NumHops: 6, + }, + InfoFields: []path.InfoField{ + {SegID: 0x111, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + {SegID: 0x222, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + }, + + HopFields: []path.HopField{ + {ConsIngress: 0, ConsEgress: 2}, + {ConsIngress: 3, ConsEgress: 4}, + {ConsIngress: 7, ConsEgress: 0}, + {ConsIngress: 0, ConsEgress: 31}, + {ConsIngress: 8, ConsEgress: 9}, + {ConsIngress: 11, ConsEgress: 0}, + }, + } + + dpath.HopFields[2].Mac = benchmarkScionMac(b, key, dpath.InfoFields[0], dpath.HopFields[2]) + dpath.HopFields[3].Mac = benchmarkScionMac(b, key, dpath.InfoFields[1], dpath.HopFields[3]) + msg := toBenchmarkMsg(b, spkt, dpath) + + backup := make([]byte, len(msg.Buffers[0])) + copy(backup, msg.Buffers[0]) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + copy(msg.Buffers[0], backup) + pp.ProcessPkt(7, msg) + // DO NOT check for errors when getting actual numbers from benchmark + // require.NoError(b, err) // verify no failures on repeated usage of same packet + } +} + +// standard Hbird packet, no flyover +func BenchmarkProcessHbirdFlyoverless(b *testing.B) { + // prepare Dataplane + ctrl := gomock.NewController(b) + defer ctrl.Finish() + + key := []byte("testkey_xxxxxxxx") + sv := []byte("test_secretvalue") + + dp := router.NewDP( + map[uint16]router.BatchConn{ + uint16(31): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 7: topology.Core, + 31: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(7): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) + + // prepare PacketProcessor + pp := dp.NewBenchmarkPP() + + // prepare packet + now := time.Now() + + spkt := &slayers.SCION{ + Version: 0, + TrafficClass: 0xb8, + FlowID: 0xdead, + NextHdr: slayers.L4UDP, + PathType: hummingbird.PathType, + DstIA: xtest.MustParseIA("4-ff00:0:411"), + SrcIA: xtest.MustParseIA("2-ff00:0:222"), + Path: &hummingbird.Raw{}, + PayloadLen: 18, + } + + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrHF: 3, + SegLen: [3]uint8{9, 9, 0}, + }, + NumINF: 2, + NumLines: 18, + }, + InfoFields: []path.InfoField{ + {SegID: 0x111, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + {SegID: 0x222, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + }, + + HopFields: []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 2}}, + {HopField: path.HopField{ConsIngress: 7, ConsEgress: 31}}, + {HopField: path.HopField{ConsIngress: 3, ConsEgress: 0}}, + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 6}}, + {HopField: path.HopField{ConsIngress: 8, ConsEgress: 9}}, + {HopField: path.HopField{ConsIngress: 11, ConsEgress: 0}}, + }, + } + + dpath.HopFields[1].HopField.Mac = benchmarkScionMac(b, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + msg := toBenchmarkMsg(b, spkt, dpath) + + backup := make([]byte, len(msg.Buffers[0])) + copy(backup, msg.Buffers[0]) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + copy(msg.Buffers[0], backup) + pp.ProcessPkt(7, msg) + // DO NOT check for errors when getting actual numbers from benchmark + //require.NoError(b, err) // verify no failures on repeated usage of same packet + } +} + +func BenchmarkProcessHbirdFlyoverlessXover(b *testing.B) { + // prepare Dataplane + ctrl := gomock.NewController(b) + defer ctrl.Finish() + + key := []byte("testkey_xxxxxxxx") + sv := []byte("test_secretvalue") + + dp := router.NewDP( + map[uint16]router.BatchConn{ + uint16(31): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 7: topology.Core, + 31: topology.Child, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(7): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) + + // prepare PacketProcessor + pp := dp.NewBenchmarkPP() + + // prepare packet + now := time.Now() + + spkt := &slayers.SCION{ + Version: 0, + TrafficClass: 0xb8, + FlowID: 0xdead, + NextHdr: slayers.L4UDP, + PathType: hummingbird.PathType, + DstIA: xtest.MustParseIA("4-ff00:0:411"), + SrcIA: xtest.MustParseIA("2-ff00:0:222"), + Path: &hummingbird.Raw{}, + PayloadLen: 18, + } + + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrHF: 6, + SegLen: [3]uint8{9, 9, 0}, + }, + NumINF: 2, + NumLines: 18, + }, + InfoFields: []path.InfoField{ + {SegID: 0x111, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + {SegID: 0x222, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + }, + + HopFields: []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 2}}, + {HopField: path.HopField{ConsIngress: 3, ConsEgress: 4}}, + {HopField: path.HopField{ConsIngress: 7, ConsEgress: 0}}, + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 31}}, + {HopField: path.HopField{ConsIngress: 8, ConsEgress: 9}}, + {HopField: path.HopField{ConsIngress: 11, ConsEgress: 0}}, + }, + } + + dpath.HopFields[2].HopField.Mac = benchmarkScionMac(b, key, dpath.InfoFields[0], dpath.HopFields[2].HopField) + dpath.HopFields[3].HopField.Mac = benchmarkScionMac(b, key, dpath.InfoFields[1], dpath.HopFields[3].HopField) + msg := toBenchmarkMsg(b, spkt, dpath) + + backup := make([]byte, len(msg.Buffers[0])) + copy(backup, msg.Buffers[0]) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + copy(msg.Buffers[0], backup) + pp.ProcessPkt(7, msg) + // DO NOT check for errors when getting actual numbers from benchmark + // require.NoError(b, err) // verify no failures on repeated usage of same packet + } +} + +// Helper Functions for Benchmarking + +func toBenchmarkMsg(b *testing.B, spkt *slayers.SCION, dpath path.Path) *ipv4.Message { + b.Helper() + ret := &ipv4.Message{} + spkt.Path = dpath + buffer := gopacket.NewSerializeBuffer() + spkt.PayloadLen = benchmarkPayloadLen + payload := [benchmarkPayloadLen]byte{} + err := gopacket.SerializeLayers(buffer, gopacket.SerializeOptions{FixLengths: true}, + spkt, gopacket.Payload(payload[:])) + require.NoError(b, err) + raw := buffer.Bytes() + ret.Buffers = make([][]byte, 1) + ret.Buffers[0] = make([]byte, 1500) + copy(ret.Buffers[0], raw) + ret.N = len(raw) + ret.Buffers[0] = ret.Buffers[0][:ret.N] + return ret +} + +func benchmarkScionMac(b *testing.B, key []byte, info path.InfoField, hf path.HopField) [path.MacLen]byte { + mac, err := scrypto.InitMac(key) + require.NoError(b, err) + buffer := [path.MacLen]byte{} + return path.MAC(mac, info, hf, buffer[:]) +} diff --git a/router/export_test.go b/router/export_test.go index ffa745eb57..011c5195d8 100644 --- a/router/export_test.go +++ b/router/export_test.go @@ -88,3 +88,16 @@ func (d *DataPlane) ProcessPkt(ifID uint16, m *ipv4.Message) (ProcessResult, err func ExtractServices(s *services) map[addr.SVC][]*net.UDPAddr { return s.m } + +type BenchmarkPacketProcessor struct { + p *scionPacketProcessor +} + +func (d *DataPlane) NewBenchmarkPP() BenchmarkPacketProcessor { + return BenchmarkPacketProcessor{newPacketProcessor(d)} +} + +func (p BenchmarkPacketProcessor) ProcessPkt(ifID uint16, m *ipv4.Message) error { + _, err := p.p.processPkt(m.Buffers[0], nil, ifID) + return err +} From 1c7310bb936d26ea3554e6755aaee18f7a133734 Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Tue, 23 Jan 2024 14:28:14 +0100 Subject: [PATCH 061/100] added benchmarks for flyovers, no xover, xover brtransit and xover astransit --- router/dataplane_benchmark_test.go | 283 ++++++++++++++++++++++++++++- 1 file changed, 280 insertions(+), 3 deletions(-) diff --git a/router/dataplane_benchmark_test.go b/router/dataplane_benchmark_test.go index e42024c97d..0f6f1f417f 100644 --- a/router/dataplane_benchmark_test.go +++ b/router/dataplane_benchmark_test.go @@ -1,12 +1,14 @@ package router_test import ( + "crypto/aes" "net" "testing" "time" "github.com/golang/mock/gomock" "github.com/google/gopacket" + "github.com/scionproto/scion/pkg/addr" "github.com/scionproto/scion/pkg/private/util" "github.com/scionproto/scion/pkg/private/xtest" "github.com/scionproto/scion/pkg/scrypto" @@ -22,7 +24,7 @@ import ( ) const ( - benchmarkPayloadLen = 120 + benchmarkPayloadLen = 10 ) // We measure the time necessary to process 100 packets (process function and reset function in between) @@ -223,7 +225,7 @@ func BenchmarkProcessHbirdFlyoverless(b *testing.B) { DstIA: xtest.MustParseIA("4-ff00:0:411"), SrcIA: xtest.MustParseIA("2-ff00:0:222"), Path: &hummingbird.Raw{}, - PayloadLen: 18, + PayloadLen: benchmarkPayloadLen, } dpath := &hummingbird.Decoded{ @@ -301,7 +303,7 @@ func BenchmarkProcessHbirdFlyoverlessXover(b *testing.B) { DstIA: xtest.MustParseIA("4-ff00:0:411"), SrcIA: xtest.MustParseIA("2-ff00:0:222"), Path: &hummingbird.Raw{}, - PayloadLen: 18, + PayloadLen: benchmarkPayloadLen, } dpath := &hummingbird.Decoded{ @@ -344,6 +346,259 @@ func BenchmarkProcessHbirdFlyoverlessXover(b *testing.B) { } } +// To run any benchmark containing flyovers, temporarily modify +// dataplane_hbird.go:checkReservationBandwidth() such that it never fails, +// but still performs all operations +func BenchmarkProcessHbirdFlyover(b *testing.B) { + // prepare Dataplane + ctrl := gomock.NewController(b) + defer ctrl.Finish() + + key := []byte("testkey_xxxxxxxx") + sv := []byte("test_secretvalue") + + dp := router.NewDP( + map[uint16]router.BatchConn{ + uint16(31): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 7: topology.Core, + 31: topology.Core, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(7): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) + + // prepare PacketProcessor + pp := dp.NewBenchmarkPP() + + // prepare packet + now := time.Now() + + spkt := &slayers.SCION{ + Version: 0, + TrafficClass: 0xb8, + FlowID: 0xdead, + NextHdr: slayers.L4UDP, + PathType: hummingbird.PathType, + DstIA: xtest.MustParseIA("4-ff00:0:411"), + SrcIA: xtest.MustParseIA("2-ff00:0:222"), + Path: &hummingbird.Raw{}, + PayloadLen: benchmarkPayloadLen, + } + + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrHF: 3, + SegLen: [3]uint8{11, 9, 0}, + BaseTS: util.TimeToSecs(now), + HighResTS: 500 << 22, + }, + NumINF: 2, + NumLines: 20, + }, + InfoFields: []path.InfoField{ + {SegID: 0x111, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + {SegID: 0x222, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + }, + + HopFields: []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 2}}, + {Flyover: true, HopField: path.HopField{ConsIngress: 7, ConsEgress: 31}, ResStartTime: 10, Duration: 180, Bw: 777}, + {HopField: path.HopField{ConsIngress: 3, ConsEgress: 0}}, + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 6}}, + {HopField: path.HopField{ConsIngress: 8, ConsEgress: 9}}, + {HopField: path.HopField{ConsIngress: 11, ConsEgress: 0}}, + }, + } + + dpath.HopFields[1].HopField.Mac = benchmarkAggregateMac(b, key, sv, spkt.DstIA, benchmarkPayloadLen, + 7, 31, dpath.InfoFields[0], dpath.HopFields[1], dpath.PathMeta) + msg := toBenchmarkMsg(b, spkt, dpath) + + backup := make([]byte, len(msg.Buffers[0])) + copy(backup, msg.Buffers[0]) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + copy(msg.Buffers[0], backup) + pp.ProcessPkt(7, msg) + // DO NOT check for errors when getting actual numbers from benchmark + // require.NoError(b, err) // verify no failures on repeated usage of same packet + } +} + +// To run any benchmark containing flyovers, temporarily modify +// dataplane_hbird.go:checkReservationBandwidth() such that it never fails, +// but still performs all operations +func BenchmarkProcessHbirdFlyoverXoverBrtransit(b *testing.B) { + // prepare Dataplane + ctrl := gomock.NewController(b) + defer ctrl.Finish() + + key := []byte("testkey_xxxxxxxx") + sv := []byte("test_secretvalue") + + dp := router.NewDP( + map[uint16]router.BatchConn{ + uint16(31): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 7: topology.Core, + 31: topology.Child, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(7): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) + + // prepare PacketProcessor + pp := dp.NewBenchmarkPP() + + // prepare packet + now := time.Now() + + spkt := &slayers.SCION{ + Version: 0, + TrafficClass: 0xb8, + FlowID: 0xdead, + NextHdr: slayers.L4UDP, + PathType: hummingbird.PathType, + DstIA: xtest.MustParseIA("4-ff00:0:411"), + SrcIA: xtest.MustParseIA("2-ff00:0:222"), + Path: &hummingbird.Raw{}, + PayloadLen: benchmarkPayloadLen, + } + + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrHF: 6, + SegLen: [3]uint8{11, 9, 0}, + BaseTS: util.TimeToSecs(now), + HighResTS: 500 << 22, + }, + NumINF: 2, + NumLines: 20, + }, + InfoFields: []path.InfoField{ + {SegID: 0x111, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + {SegID: 0x222, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + }, + + HopFields: []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 2}}, + {HopField: path.HopField{ConsIngress: 4, ConsEgress: 5}}, + {Flyover: true, HopField: path.HopField{ConsIngress: 7, ConsEgress: 0}, + ResID: 2345, ResStartTime: 10, Duration: 180, Bw: 777}, + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 31}}, + {HopField: path.HopField{ConsIngress: 8, ConsEgress: 9}}, + {HopField: path.HopField{ConsIngress: 11, ConsEgress: 0}}, + }, + } + + dpath.HopFields[2].HopField.Mac = benchmarkAggregateMac(b, key, sv, spkt.DstIA, benchmarkPayloadLen, + 7, 31, dpath.InfoFields[0], dpath.HopFields[2], dpath.PathMeta) + dpath.HopFields[3].HopField.Mac = benchmarkScionMac(b, key, dpath.InfoFields[1], dpath.HopFields[3].HopField) + msg := toBenchmarkMsg(b, spkt, dpath) + + backup := make([]byte, len(msg.Buffers[0])) + copy(backup, msg.Buffers[0]) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + copy(msg.Buffers[0], backup) + pp.ProcessPkt(7, msg) + // DO NOT check for errors when getting actual numbers from benchmark + // require.NoError(b, err) // verify no failures on repeated usage of same packet + } +} + +func BenchmarkProcessHbirdFlyoverXoverAstransit(b *testing.B) { + // prepare Dataplane + ctrl := gomock.NewController(b) + defer ctrl.Finish() + + key := []byte("testkey_xxxxxxxx") + sv := []byte("test_secretvalue") + + dp := router.NewDP( + map[uint16]router.BatchConn{ + uint16(7): mock_router.NewMockBatchConn(ctrl), + }, + map[uint16]topology.LinkType{ + 7: topology.Core, + 31: topology.Child, + }, + mock_router.NewMockBatchConn(ctrl), + map[uint16]*net.UDPAddr{ + uint16(31): {IP: net.ParseIP("10.0.200.200").To4(), Port: 30043}, + }, nil, xtest.MustParseIA("1-ff00:0:111"), nil, key, sv) + + // prepare PacketProcessor + pp := dp.NewBenchmarkPP() + + // prepare packet + now := time.Now() + + spkt := &slayers.SCION{ + Version: 0, + TrafficClass: 0xb8, + FlowID: 0xdead, + NextHdr: slayers.L4UDP, + PathType: hummingbird.PathType, + DstIA: xtest.MustParseIA("4-ff00:0:411"), + SrcIA: xtest.MustParseIA("2-ff00:0:222"), + Path: &hummingbird.Raw{}, + PayloadLen: benchmarkPayloadLen, + } + + dpath := &hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + CurrHF: 6, + SegLen: [3]uint8{11, 9, 0}, + BaseTS: util.TimeToSecs(now), + HighResTS: 500 << 22, + }, + NumINF: 2, + NumLines: 20, + }, + InfoFields: []path.InfoField{ + {SegID: 0x111, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + {SegID: 0x222, ConsDir: true, Timestamp: util.TimeToSecs(now)}, + }, + + HopFields: []hummingbird.FlyoverHopField{ + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 2}}, + {HopField: path.HopField{ConsIngress: 4, ConsEgress: 5}}, + {Flyover: true, HopField: path.HopField{ConsIngress: 7, ConsEgress: 0}, + ResID: 2345, ResStartTime: 10, Duration: 180, Bw: 777}, + {HopField: path.HopField{ConsIngress: 0, ConsEgress: 31}}, + {HopField: path.HopField{ConsIngress: 8, ConsEgress: 9}}, + {HopField: path.HopField{ConsIngress: 11, ConsEgress: 0}}, + }, + } + + dpath.HopFields[2].HopField.Mac = benchmarkAggregateMac(b, key, sv, spkt.DstIA, benchmarkPayloadLen, + 7, 31, dpath.InfoFields[0], dpath.HopFields[2], dpath.PathMeta) + dpath.HopFields[3].HopField.Mac = benchmarkScionMac(b, key, dpath.InfoFields[1], dpath.HopFields[3].HopField) + msg := toBenchmarkMsg(b, spkt, dpath) + + backup := make([]byte, len(msg.Buffers[0])) + copy(backup, msg.Buffers[0]) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + copy(msg.Buffers[0], backup) + pp.ProcessPkt(7, msg) + // DO NOT check for errors when getting actual numbers from benchmark + // require.NoError(b, err) // verify no failures on repeated usage of same packet + } +} + // Helper Functions for Benchmarking func toBenchmarkMsg(b *testing.B, spkt *slayers.SCION, dpath path.Path) *ipv4.Message { @@ -371,3 +626,25 @@ func benchmarkScionMac(b *testing.B, key []byte, info path.InfoField, hf path.Ho buffer := [path.MacLen]byte{} return path.MAC(mac, info, hf, buffer[:]) } + +func benchmarkAggregateMac(b *testing.B, key, sv []byte, dst addr.IA, l, ingress, egress uint16, info path.InfoField, + hf hummingbird.FlyoverHopField, meta hummingbird.MetaHdr) [path.MacLen]byte { + + scionMac := benchmarkScionMac(b, key, info, hf.HopField) + block, err := aes.NewCipher(sv) + require.NoError(b, err) + + akBuffer := make([]byte, hummingbird.AkBufferSize) + macBuffer := make([]byte, hummingbird.FlyoverMacBufferSize) + xkBuffer := make([]uint32, hummingbird.XkBufferSize) + + ak := hummingbird.DeriveAuthKey(block, hf.ResID, hf.Bw, ingress, egress, + meta.BaseTS-uint32(hf.ResStartTime), hf.Duration, akBuffer) + flyoverMac := hummingbird.FullFlyoverMac(ak, dst, l, hf.ResStartTime, + meta.HighResTS, macBuffer, xkBuffer) + + for i, b := range scionMac { + scionMac[i] = b ^ flyoverMac[i] + } + return scionMac +} From 9eea093712ed654cba1604488a506c9e34969218 Mon Sep 17 00:00:00 2001 From: Jules Bachmann Date: Tue, 23 Jan 2024 14:46:30 +0100 Subject: [PATCH 062/100] compy to linter --- router/dataplane_benchmark_test.go | 56 ++++++++++++++++++++---------- router/dataplane_hbird_test.go | 15 ++++++++ 2 files changed, 53 insertions(+), 18 deletions(-) diff --git a/router/dataplane_benchmark_test.go b/router/dataplane_benchmark_test.go index 0f6f1f417f..7f72cb8bac 100644 --- a/router/dataplane_benchmark_test.go +++ b/router/dataplane_benchmark_test.go @@ -1,3 +1,18 @@ +// Copyright 2020 Anapaya Systems +// Copyright 2023 ETH Zurich +// +// 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 router_test import ( @@ -27,9 +42,6 @@ const ( benchmarkPayloadLen = 10 ) -// We measure the time necessary to process 100 packets (process function and reset function in between) -// This allows to differentiate between different space usages of bandwidth (TODO: what sizes are relevant???) - // standard SCION benchmark for reference func BenchmarkProcessScion(b *testing.B) { // prepare Dataplane @@ -252,7 +264,8 @@ func BenchmarkProcessHbirdFlyoverless(b *testing.B) { }, } - dpath.HopFields[1].HopField.Mac = benchmarkScionMac(b, key, dpath.InfoFields[0], dpath.HopFields[1].HopField) + dpath.HopFields[1].HopField.Mac = benchmarkScionMac(b, key, dpath.InfoFields[0], + dpath.HopFields[1].HopField) msg := toBenchmarkMsg(b, spkt, dpath) backup := make([]byte, len(msg.Buffers[0])) @@ -330,8 +343,10 @@ func BenchmarkProcessHbirdFlyoverlessXover(b *testing.B) { }, } - dpath.HopFields[2].HopField.Mac = benchmarkScionMac(b, key, dpath.InfoFields[0], dpath.HopFields[2].HopField) - dpath.HopFields[3].HopField.Mac = benchmarkScionMac(b, key, dpath.InfoFields[1], dpath.HopFields[3].HopField) + dpath.HopFields[2].HopField.Mac = benchmarkScionMac(b, key, dpath.InfoFields[0], + dpath.HopFields[2].HopField) + dpath.HopFields[3].HopField.Mac = benchmarkScionMac(b, key, dpath.InfoFields[1], + dpath.HopFields[3].HopField) msg := toBenchmarkMsg(b, spkt, dpath) backup := make([]byte, len(msg.Buffers[0])) @@ -406,7 +421,8 @@ func BenchmarkProcessHbirdFlyover(b *testing.B) { HopFields: []hummingbird.FlyoverHopField{ {HopField: path.HopField{ConsIngress: 0, ConsEgress: 2}}, - {Flyover: true, HopField: path.HopField{ConsIngress: 7, ConsEgress: 31}, ResStartTime: 10, Duration: 180, Bw: 777}, + {Flyover: true, HopField: path.HopField{ConsIngress: 7, ConsEgress: 31}, + ResStartTime: 10, Duration: 180, Bw: 777}, {HopField: path.HopField{ConsIngress: 3, ConsEgress: 0}}, {HopField: path.HopField{ConsIngress: 0, ConsEgress: 6}}, {HopField: path.HopField{ConsIngress: 8, ConsEgress: 9}}, @@ -414,8 +430,8 @@ func BenchmarkProcessHbirdFlyover(b *testing.B) { }, } - dpath.HopFields[1].HopField.Mac = benchmarkAggregateMac(b, key, sv, spkt.DstIA, benchmarkPayloadLen, - 7, 31, dpath.InfoFields[0], dpath.HopFields[1], dpath.PathMeta) + dpath.HopFields[1].HopField.Mac = benchmarkAggregateMac(b, key, sv, spkt.DstIA, + benchmarkPayloadLen, 7, 31, dpath.InfoFields[0], dpath.HopFields[1], dpath.PathMeta) msg := toBenchmarkMsg(b, spkt, dpath) backup := make([]byte, len(msg.Buffers[0])) @@ -499,9 +515,10 @@ func BenchmarkProcessHbirdFlyoverXoverBrtransit(b *testing.B) { }, } - dpath.HopFields[2].HopField.Mac = benchmarkAggregateMac(b, key, sv, spkt.DstIA, benchmarkPayloadLen, - 7, 31, dpath.InfoFields[0], dpath.HopFields[2], dpath.PathMeta) - dpath.HopFields[3].HopField.Mac = benchmarkScionMac(b, key, dpath.InfoFields[1], dpath.HopFields[3].HopField) + dpath.HopFields[2].HopField.Mac = benchmarkAggregateMac(b, key, sv, spkt.DstIA, + benchmarkPayloadLen, 7, 31, dpath.InfoFields[0], dpath.HopFields[2], dpath.PathMeta) + dpath.HopFields[3].HopField.Mac = benchmarkScionMac(b, key, dpath.InfoFields[1], + dpath.HopFields[3].HopField) msg := toBenchmarkMsg(b, spkt, dpath) backup := make([]byte, len(msg.Buffers[0])) @@ -582,9 +599,10 @@ func BenchmarkProcessHbirdFlyoverXoverAstransit(b *testing.B) { }, } - dpath.HopFields[2].HopField.Mac = benchmarkAggregateMac(b, key, sv, spkt.DstIA, benchmarkPayloadLen, - 7, 31, dpath.InfoFields[0], dpath.HopFields[2], dpath.PathMeta) - dpath.HopFields[3].HopField.Mac = benchmarkScionMac(b, key, dpath.InfoFields[1], dpath.HopFields[3].HopField) + dpath.HopFields[2].HopField.Mac = benchmarkAggregateMac(b, key, sv, spkt.DstIA, + benchmarkPayloadLen, 7, 31, dpath.InfoFields[0], dpath.HopFields[2], dpath.PathMeta) + dpath.HopFields[3].HopField.Mac = benchmarkScionMac(b, key, dpath.InfoFields[1], + dpath.HopFields[3].HopField) msg := toBenchmarkMsg(b, spkt, dpath) backup := make([]byte, len(msg.Buffers[0])) @@ -620,15 +638,17 @@ func toBenchmarkMsg(b *testing.B, spkt *slayers.SCION, dpath path.Path) *ipv4.Me return ret } -func benchmarkScionMac(b *testing.B, key []byte, info path.InfoField, hf path.HopField) [path.MacLen]byte { +func benchmarkScionMac(b *testing.B, key []byte, info path.InfoField, + hf path.HopField) [path.MacLen]byte { mac, err := scrypto.InitMac(key) require.NoError(b, err) buffer := [path.MacLen]byte{} return path.MAC(mac, info, hf, buffer[:]) } -func benchmarkAggregateMac(b *testing.B, key, sv []byte, dst addr.IA, l, ingress, egress uint16, info path.InfoField, - hf hummingbird.FlyoverHopField, meta hummingbird.MetaHdr) [path.MacLen]byte { +func benchmarkAggregateMac(b *testing.B, key, sv []byte, dst addr.IA, l, ingress, egress uint16, + info path.InfoField, hf hummingbird.FlyoverHopField, + meta hummingbird.MetaHdr) [path.MacLen]byte { scionMac := benchmarkScionMac(b, key, info, hf.HopField) block, err := aes.NewCipher(sv) diff --git a/router/dataplane_hbird_test.go b/router/dataplane_hbird_test.go index 017ce8545f..c1a20bfcfe 100644 --- a/router/dataplane_hbird_test.go +++ b/router/dataplane_hbird_test.go @@ -1,3 +1,18 @@ +// Copyright 2020 Anapaya Systems +// Copyright 2023 ETH Zurich +// +// 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 router_test import ( From 94458b74465e8b835dc7a1cb17e17d5da692b9f3 Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Thu, 16 Nov 2023 21:20:52 +0100 Subject: [PATCH 063/100] First commit ----------------------------- From b2a03d594dcacc7fccf17c11819f224709f5f3e9 Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Tue, 14 Nov 2023 16:23:59 +0100 Subject: [PATCH 064/100] Working on GetReservations sciond call. Find flyovers for SCION paths, plus UT. Prepare to rebase on br-hummingbird. --- daemon/internal/servers/BUILD.bazel | 19 +- daemon/internal/servers/grpc.go | 2 + daemon/internal/servers/grpc_hummingbird.go | 316 +++++++++ .../internal/servers/grpc_hummingbird_test.go | 213 ++++++ pkg/daemon/BUILD.bazel | 2 + pkg/daemon/daemon.go | 1 + pkg/daemon/grpc_hummingbird.go | 45 ++ pkg/daemon/mock_daemon/mock.go | 15 + pkg/hummingbird/BUILD.bazel | 7 +- pkg/hummingbird/flyover.go | 26 + pkg/hummingbird/resevation.go | 36 + pkg/proto/daemon/daemon.pb.go | 624 +++++++++++------- pkg/proto/daemon/hummingbird.pb.go | 516 +++++++++++++++ private/hummingbirddb/BUILD.bazel | 12 + private/hummingbirddb/db.go | 45 ++ .../mock_hummingbirddb/BUILD.bazel | 23 + .../hummingbirddb/mock_hummingbirddb/mock.go | 69 ++ proto/daemon/v1/BUILD.bazel | 1 + proto/daemon/v1/daemon.proto | 9 + proto/daemon/v1/hummingbird.proto | 45 ++ 20 files changed, 1783 insertions(+), 243 deletions(-) create mode 100644 daemon/internal/servers/grpc_hummingbird.go create mode 100644 daemon/internal/servers/grpc_hummingbird_test.go create mode 100644 pkg/daemon/grpc_hummingbird.go create mode 100644 pkg/hummingbird/flyover.go create mode 100644 pkg/hummingbird/resevation.go create mode 100644 pkg/proto/daemon/hummingbird.pb.go create mode 100644 private/hummingbirddb/BUILD.bazel create mode 100644 private/hummingbirddb/db.go create mode 100644 private/hummingbirddb/mock_hummingbirddb/BUILD.bazel create mode 100644 private/hummingbirddb/mock_hummingbirddb/mock.go create mode 100644 proto/daemon/v1/hummingbird.proto diff --git a/daemon/internal/servers/BUILD.bazel b/daemon/internal/servers/BUILD.bazel index 1d1534f2c2..1ff8d8c7d2 100644 --- a/daemon/internal/servers/BUILD.bazel +++ b/daemon/internal/servers/BUILD.bazel @@ -1,9 +1,10 @@ -load("//tools/lint:go.bzl", "go_library") +load("//tools/lint:go.bzl", "go_library", "go_test") go_library( name = "go_default_library", srcs = [ "grpc.go", + "grpc_hummingbird.go", "metrics.go", ], importpath = "github.com/scionproto/scion/daemon/internal/servers", @@ -13,6 +14,7 @@ go_library( "//daemon/fetcher:go_default_library", "//pkg/addr:go_default_library", "//pkg/drkey:go_default_library", + "//pkg/hummingbird:go_default_library", "//pkg/log:go_default_library", "//pkg/metrics:go_default_library", "//pkg/private/common:go_default_library", @@ -33,3 +35,18 @@ go_library( "@org_golang_x_sync//singleflight:go_default_library", ], ) + +go_test( + name = "go_default_test", + srcs = ["grpc_hummingbird_test.go"], + embed = [":go_default_library"], + deps = [ + "//pkg/addr:go_default_library", + "//pkg/hummingbird:go_default_library", + "//pkg/private/common:go_default_library", + "//pkg/private/xtest:go_default_library", + "//pkg/snet:go_default_library", + "//pkg/snet/path:go_default_library", + "@com_github_stretchr_testify//require:go_default_library", + ], +) diff --git a/daemon/internal/servers/grpc.go b/daemon/internal/servers/grpc.go index b9e7af48fe..993a70a0d8 100644 --- a/daemon/internal/servers/grpc.go +++ b/daemon/internal/servers/grpc.go @@ -61,6 +61,8 @@ type DaemonServer struct { ASInspector trust.Inspector DRKeyClient *drkey_daemon.ClientEngine + HummingbirdFetcher HummingbirdFetcher + Metrics Metrics foregroundPathDedupe singleflight.Group diff --git a/daemon/internal/servers/grpc_hummingbird.go b/daemon/internal/servers/grpc_hummingbird.go new file mode 100644 index 0000000000..01905be691 --- /dev/null +++ b/daemon/internal/servers/grpc_hummingbird.go @@ -0,0 +1,316 @@ +// Copyright 2023 ETH Zurich +// +// 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 servers + +import ( + "context" + "net" + "sort" + "time" + + "github.com/scionproto/scion/pkg/addr" + "github.com/scionproto/scion/pkg/hummingbird" + "github.com/scionproto/scion/pkg/private/common" + "github.com/scionproto/scion/pkg/private/serrors" + sdpb "github.com/scionproto/scion/pkg/proto/daemon" + "github.com/scionproto/scion/pkg/snet" + "github.com/scionproto/scion/pkg/snet/path" +) + +type HummingbirdFetcher interface { + ListFlyovers(ctx context.Context, owners []addr.IA) ([]*hummingbird.FlyoverJuanDeleteme, error) +} + +func (s *DaemonServer) StoreFlyovers( + ctx context.Context, + req *sdpb.StoreFlyoversRequest, +) (*sdpb.StoreFlyoversResponse, error) { + return nil, nil +} +func (s *DaemonServer) ListFlyovers( + ctx context.Context, + req *sdpb.ListFlyoversRequest, +) (*sdpb.ListFlyoversResponse, error) { + return nil, nil +} + +func (s *DaemonServer) GetReservations( + ctx context.Context, + req *sdpb.GetReservationsRequest, +) (*sdpb.GetReservationsResponse, error) { + + // Get SCION paths. + paths, err := s.getScionPaths(ctx, addr.IA(req.SourceIsdAs), addr.IA(req.DestinationIsdAs), + req.Refresh) + if err != nil { + return nil, err + } + + // Obtain reservations composing flyovers for those paths. + rsvs, err := s.getReservations(ctx, paths) + if err != nil { + return nil, err + } + + // Prepare response. + res := &sdpb.GetReservationsResponse{ + Reservations: make([]*sdpb.Reservation, len(paths)), + } + + _ = rsvs + + return res, nil +} + +func (s *DaemonServer) getScionPaths( + ctx context.Context, + src, dst addr.IA, + refresh bool, +) ([]path.Path, error) { + pathReq := &sdpb.PathsRequest{ + SourceIsdAs: uint64(src), + DestinationIsdAs: uint64(dst), + Refresh: refresh, + Hidden: false, + } + pathRes, err := s.paths(ctx, pathReq) + err = unwrapMetricsError(err) + if err != nil { + return nil, serrors.WrapStr("obtaining reservations", err) + } + + // Unwrap the response to a slice of path.Path + paths := make([]path.Path, len(pathRes.Paths)) + for i, p := range pathRes.Paths { + paths[i], err = convertPath(p, dst) + if err != nil { + return nil, err + } + } + + return paths, nil +} + +func (s *DaemonServer) getReservations( + ctx context.Context, + paths []path.Path, +) ([]*hummingbird.ReservationJuanDeleteme, error) { + + // Make a set with all appearing IASet. Then a slice of them to obtain flyovers. + IASet := make(map[addr.IA]struct{}, 0) + for _, p := range paths { + for _, iface := range p.Meta.Interfaces { + IASet[iface.IA] = struct{}{} + } + } + IAs := make([]addr.IA, 0, len(IASet)) + for ia := range IASet { + IAs = append(IAs, ia) + } + + // Get flyovers on any AS present in the paths. + flyovers, err := s.HummingbirdFetcher.ListFlyovers(ctx, IAs) + if err != nil { + return nil, err + } + + // For each path, try to assign as many flyovers as possible. + reservations := make([]*hummingbird.ReservationJuanDeleteme, len(paths)) + mFlyovers := flyoversToMap(flyovers) + for i, p := range paths { + flyovers, ratio := assignFlyovers(p.Meta.Interfaces, mFlyovers) + reservations[i] = &hummingbird.ReservationJuanDeleteme{ + SCIONPath: p, + Flyovers: flyovers, + Ratio: ratio, + } + } + + // Rank the reservations by flyover / hop ratio. + sort.Slice(reservations, func(i, j int) bool { + return reservations[i].LessThan(reservations[j]) + }) + + return reservations, nil +} + +func convertPath(p *sdpb.Path, dst addr.IA) (path.Path, error) { + expiry := time.Unix(p.Expiration.Seconds, int64(p.Expiration.Nanos)) + if len(p.Interfaces) == 0 { + return path.Path{ + Src: dst, + Dst: dst, + Meta: snet.PathMetadata{ + MTU: uint16(p.Mtu), + Expiry: expiry, + }, + DataplanePath: path.Empty{}, + }, nil + } + underlayA, err := net.ResolveUDPAddr("udp", p.Interface.Address.Address) + if err != nil { + return path.Path{}, serrors.WrapStr("resolving underlay", err) + } + interfaces := make([]snet.PathInterface, len(p.Interfaces)) + for i, pi := range p.Interfaces { + interfaces[i] = snet.PathInterface{ + ID: common.IFIDType(pi.Id), + IA: addr.IA(pi.IsdAs), + } + } + latency := make([]time.Duration, len(p.Latency)) + for i, v := range p.Latency { + latency[i] = time.Second*time.Duration(v.Seconds) + time.Duration(v.Nanos) + } + geo := make([]snet.GeoCoordinates, len(p.Geo)) + for i, v := range p.Geo { + geo[i] = snet.GeoCoordinates{ + Latitude: v.Latitude, + Longitude: v.Longitude, + Address: v.Address, + } + } + linkType := make([]snet.LinkType, len(p.LinkType)) + for i, v := range p.LinkType { + linkType[i] = linkTypeFromPB(v) + } + + res := path.Path{ + Src: interfaces[0].IA, + Dst: dst, + DataplanePath: path.SCION{ + Raw: p.Raw, + }, + NextHop: underlayA, + Meta: snet.PathMetadata{ + Interfaces: interfaces, + MTU: uint16(p.Mtu), + Expiry: expiry, + Latency: latency, + Bandwidth: p.Bandwidth, + Geo: geo, + LinkType: linkType, + InternalHops: p.InternalHops, + Notes: p.Notes, + }, + } + + if p.EpicAuths == nil { + return res, nil + } + res.Meta.EpicAuths = snet.EpicAuths{ + AuthPHVF: append([]byte(nil), p.EpicAuths.AuthPhvf...), + AuthLHVF: append([]byte(nil), p.EpicAuths.AuthLhvf...), + } + return res, nil +} + +func linkTypeFromPB(lt sdpb.LinkType) snet.LinkType { + switch lt { + case sdpb.LinkType_LINK_TYPE_DIRECT: + return snet.LinkTypeDirect + case sdpb.LinkType_LINK_TYPE_MULTI_HOP: + return snet.LinkTypeMultihop + case sdpb.LinkType_LINK_TYPE_OPEN_NET: + return snet.LinkTypeOpennet + default: + return snet.LinkTypeUnset + } +} + +// flyoverMapKey is a map of flyovers keyed by IA, ingress, and egress. +// The assumption is that at most one hop field exists per triplet. +type flyoverMapKey struct { + IA addr.IA + Ingress common.IFIDType + Egress common.IFIDType +} +type flyoverMap map[flyoverMapKey]*hummingbird.FlyoverJuanDeleteme + +func flyoversToMap(flyovers []*hummingbird.FlyoverJuanDeleteme) flyoverMap { + ret := make(flyoverMap) + for _, flyover := range flyovers { + k := flyoverMapKey{ + IA: flyover.IA, + Ingress: flyover.Ingress, + Egress: flyover.Egress, + } + ret[k] = flyover + } + return ret +} + +// assignFlyovers assigns as many flyovers as possible to a path. +// The first returned value is a slice of flyovers, with the same length as the hop sequence, +// and when not nil, it points to a Flyover that can be used in the hop of that specific index. +// As SCION hops appear twice per ingress/egress pairs (with the exception of the first and +// last ones), the flyovers are never located on tbe egress index, which means, always located +// on odd indices. +// The second returned value is the ratio of flyovers (1.0 being all, 0.0 being none) that exist +// for that path. +func assignFlyovers( + hopSequence []snet.PathInterface, + flyovers flyoverMap, +) ([]*hummingbird.FlyoverJuanDeleteme, float64) { + + ret := make([]*hummingbird.FlyoverJuanDeleteme, len(hopSequence)) + flyoverExistsCount := 0 + + // Do the first flyover appart. + k := flyoverMapKey{ + IA: hopSequence[0].IA, + Ingress: 0, + Egress: hopSequence[0].ID, + } + ret[0] = flyovers[k] + if ret[0] != nil { + flyoverExistsCount++ + } + + // Do everything in the middle, except the last flyover. + for i := 1; i < len(hopSequence)-1; i += 2 { + // Prepare to look for the next flyover. + hop := hopSequence[i] + k = flyoverMapKey{ + IA: hop.IA, + Ingress: hop.ID, + Egress: hopSequence[i+1].ID, + } + + // Find out if there's any flyover we can use. + ret[i] = flyovers[k] + if ret[i] != nil { + flyoverExistsCount++ + } + } + + // Do the last flyover. + k = flyoverMapKey{ + IA: hopSequence[len(hopSequence)-1].IA, + Ingress: hopSequence[len(hopSequence)-1].ID, + Egress: 0, + } + ret[len(ret)-1] = flyovers[k] + if ret[len(ret)-1] != nil { + flyoverExistsCount++ + } + + return ret, float64(flyoverExistsCount) / float64(len(hopSequence)/2) +} + +// func assignRemainingFlyovers( +// hopSequence []snet.PathInterface, +// flyovers flyoverMap, +// ) [] diff --git a/daemon/internal/servers/grpc_hummingbird_test.go b/daemon/internal/servers/grpc_hummingbird_test.go new file mode 100644 index 0000000000..92a7aa60f9 --- /dev/null +++ b/daemon/internal/servers/grpc_hummingbird_test.go @@ -0,0 +1,213 @@ +// Copyright 2023 ETH Zurich +// +// 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 servers + +import ( + "context" + "testing" + + "github.com/scionproto/scion/pkg/addr" + "github.com/scionproto/scion/pkg/hummingbird" + "github.com/scionproto/scion/pkg/private/common" + "github.com/scionproto/scion/pkg/private/xtest" + "github.com/scionproto/scion/pkg/snet" + "github.com/scionproto/scion/pkg/snet/path" + "github.com/stretchr/testify/require" +) + +// TestGetReservation checks that given a set of SCION paths, the functions getting the +// reservation correctly finds the appropriate flyovers and uses them. +func TestGetReservation(t *testing.T) { + cases := map[string]struct { + // paths' hops, like { {0, "1-ff00:0:1", 1, 2, "1-ff00:0:2", 0} , ... } + scionPaths [][]any + expected [][]any // this is a slice of flyovers with nils in it + flyoverDB [][]any + }{ + "onepath_oneflyover": { + scionPaths: [][]any{ + {0, "1-ff00:0:1", 1, 2, "1-ff00:0:2", 0}, + }, + expected: [][]any{ + {0, "1-ff00:0:1", 1, nil}, + }, + flyoverDB: [][]any{ + {0, "1-ff00:0:1", 1}, + {0, "1-ff00:0:2", 1}, + {0, "1-ff00:0:3", 1}, + }, + }, + "onepath_twoflyovers": { + scionPaths: [][]any{ + {0, "1-ff00:0:1", 1, 2, "1-ff00:0:2", 3, 4, "1-ff00:0:3", 0}, + }, + expected: [][]any{ + { + 0, "1-ff00:0:1", 1, + 2, "1-ff00:0:2", 3, + nil, + nil, + }, + }, + flyoverDB: [][]any{ + {0, "1-ff00:0:1", 1}, + {2, "1-ff00:0:2", 3}, + {0, "1-ff00:0:2", 1}, + {0, "1-ff00:0:3", 1}, + }, + }, + } + + for name, tc := range cases { + name, tc := name, tc + t.Run(name, func(t *testing.T) { + t.Parallel() + deadline, _ := t.Deadline() + ctx, cancelF := context.WithDeadline(context.Background(), deadline) + defer cancelF() + + flyoverDB := make([]*hummingbird.FlyoverJuanDeleteme, len(tc.flyoverDB)) + for i, flyoverDesc := range tc.flyoverDB { + flyover := getMockFlyovers(t, flyoverDesc...) + require.Len(t, flyover, 1, "bad test") + flyoverDB[i] = flyover[0] + } + mockHbirdServer := &mockServer{ + Flyovers: flyoverDB, + } + s := &DaemonServer{ + HummingbirdFetcher: mockHbirdServer, + } + scion := getMockScionPaths(t, tc.scionPaths) + rsvs, err := s.getReservations(ctx, scion) + require.NoError(t, err) + + // Check the size. + require.Len(t, rsvs, len(scion)) + + // For each path, check the flyovers. + for i, p := range scion { + // Same hop count in both SCION path and reservation. + ifaces := p.Meta.Interfaces + require.Equal(t, len(ifaces), len(rsvs[i].Flyovers)) + + expected := getMockFlyovers(t, tc.expected[i]...) + require.Equal(t, expected, rsvs[i].Flyovers) + } + }) + } +} + +func getMockScionPaths(t require.TestingT, paths [][]any) []path.Path { + ret := make([]path.Path, len(paths)) + for i, p := range paths { + ret[i] = *getMockScionPath(t, p...) + } + return ret +} + +// getMockScionPath returns a snet.path.Path that resembles a SCION path, with appropriate +// metadata included. +// The parameter `hops` must be of the form (0, "1-ff00:0:1", 1, 2, "1-ff00:0:2", 0) to indicate +// one hop between those two ASes. For more ASes, add more hops in the middle. +// First and last interface IDs must always be 0. +func getMockScionPath(t require.TestingT, hops ...any) *path.Path { + // Check the arguments. + require.Equal(t, 0, len(hops)%3, "invalid hops field") + require.Equal(t, 0, hops[0].(int)) + require.Equal(t, 0, hops[len(hops)-1].(int)) + for i := 0; i < len(hops); i += 3 { + require.IsType(t, 0, hops[i]) + require.IsType(t, "", hops[i+1]) + require.IsType(t, 0, hops[i+2]) + } + + // Parse hops argument. + interfaces := make([]snet.PathInterface, len(hops)/3*2) // SCION interfaces plus src and dst + for i := 0; i < len(hops); i += 3 { + in := hops[i].(int) + ia := xtest.MustParseIA(hops[i+1].(string)) + eg := hops[i+2].(int) + + interfaces[i/3*2].IA = ia + interfaces[i/3*2].ID = common.IFIDType(in) + interfaces[i/3*2+1].IA = ia + interfaces[i/3*2+1].ID = common.IFIDType(eg) + } + + path := &path.Path{ + Src: interfaces[0].IA, + Dst: interfaces[len(interfaces)-1].IA, + Meta: snet.PathMetadata{ + // Remove the extra start and end hops. + Interfaces: interfaces[1 : len(interfaces)-1], + }, + } + return path +} + +func getMockFlyovers(t require.TestingT, hops ...any) []*hummingbird.FlyoverJuanDeleteme { + // Parse hops argument. + flyovers := make([]*hummingbird.FlyoverJuanDeleteme, 0) + for i := 0; i < len(hops); i++ { + var f *hummingbird.FlyoverJuanDeleteme + if hops[i] != nil { + in := hops[i].(int) + ia := xtest.MustParseIA(hops[i+1].(string)) + eg := hops[i+2].(int) + f = &hummingbird.FlyoverJuanDeleteme{ + IA: ia, + Ingress: common.IFIDType(in), + Egress: common.IFIDType(eg), + } + i += 2 // advance faster + } + flyovers = append(flyovers, f) + } + return flyovers +} + +// func mockFly(t require.TestingT, in int, IA string, eg int) *hummingbird.Flyover { +// return &hummingbird.Flyover{ +// Ingress: hummingbird.Link{IfaceID: uint64(in)}, +// Egress: hummingbird.Link{IfaceID: uint64(eg)}, +// IA: xtest.MustParseIA(IA), +// } +// } + +type mockServer struct { + Flyovers []*hummingbird.FlyoverJuanDeleteme +} + +func (m *mockServer) ListFlyovers( + ctx context.Context, + owners []addr.IA, +) ([]*hummingbird.FlyoverJuanDeleteme, error) { + + // Create a set of the requested IAs. + ownerMap := make(map[addr.IA]struct{}) + for _, o := range owners { + ownerMap[o] = struct{}{} + } + + // Find any flyover with any such IA and return it. + ret := make([]*hummingbird.FlyoverJuanDeleteme, 0) + for _, f := range m.Flyovers { + if _, ok := ownerMap[f.IA]; ok { + ret = append(ret, f) + } + } + return ret, nil +} diff --git a/pkg/daemon/BUILD.bazel b/pkg/daemon/BUILD.bazel index 1d0f0d9fdc..6bac6b037d 100644 --- a/pkg/daemon/BUILD.bazel +++ b/pkg/daemon/BUILD.bazel @@ -6,6 +6,7 @@ go_library( "apitypes.go", "daemon.go", "grpc.go", + "grpc_hummingbird.go", "metrics.go", ], importpath = "github.com/scionproto/scion/pkg/daemon", @@ -15,6 +16,7 @@ go_library( "//pkg/daemon/internal/metrics:go_default_library", "//pkg/drkey:go_default_library", "//pkg/grpc:go_default_library", + "//pkg/hummingbird:go_default_library", "//pkg/metrics:go_default_library", "//pkg/private/common:go_default_library", "//pkg/private/ctrl/path_mgmt:go_default_library", diff --git a/pkg/daemon/daemon.go b/pkg/daemon/daemon.go index 24d0b6b9f8..ab7833fff4 100644 --- a/pkg/daemon/daemon.go +++ b/pkg/daemon/daemon.go @@ -87,6 +87,7 @@ type Connector interface { DRKeyGetHostASKey(ctx context.Context, meta drkey.HostASMeta) (drkey.HostASKey, error) // DRKeyGetHostHostKey requests a Host-Host Key from the daemon. DRKeyGetHostHostKey(ctx context.Context, meta drkey.HostHostMeta) (drkey.HostHostKey, error) + GetReservations(ctx context.Context, src, dst addr.IA) ([]snet.Path, error) // Close shuts down the connection to the daemon. Close() error } diff --git a/pkg/daemon/grpc_hummingbird.go b/pkg/daemon/grpc_hummingbird.go new file mode 100644 index 0000000000..1030fe594c --- /dev/null +++ b/pkg/daemon/grpc_hummingbird.go @@ -0,0 +1,45 @@ +// Copyright 2023 ETH Zurich +// +// 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 daemon + +import ( + "context" + + "github.com/scionproto/scion/pkg/addr" + "github.com/scionproto/scion/pkg/hummingbird" + "github.com/scionproto/scion/pkg/snet" +) + +func (c grpcConn) StoreFlyovers( + ctx context.Context, + flyovers []*hummingbird.FlyoverJuanDeleteme, +) error { + + return nil +} + +func (c grpcConn) ListFlyovers(ctx context.Context, +) ([]*hummingbird.FlyoverJuanDeleteme, error) { + + return nil, nil +} + +func (c grpcConn) GetReservations( + ctx context.Context, + src, dst addr.IA, +) ([]snet.Path, error) { + + return nil, nil +} diff --git a/pkg/daemon/mock_daemon/mock.go b/pkg/daemon/mock_daemon/mock.go index 61c85508c3..eaf77055f1 100644 --- a/pkg/daemon/mock_daemon/mock.go +++ b/pkg/daemon/mock_daemon/mock.go @@ -115,6 +115,21 @@ func (mr *MockConnectorMockRecorder) DRKeyGetHostHostKey(arg0, arg1 interface{}) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DRKeyGetHostHostKey", reflect.TypeOf((*MockConnector)(nil).DRKeyGetHostHostKey), arg0, arg1) } +// GetReservations mocks base method. +func (m *MockConnector) GetReservations(arg0 context.Context, arg1, arg2 addr.IA) ([]snet.Path, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetReservations", arg0, arg1, arg2) + ret0, _ := ret[0].([]snet.Path) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetReservations indicates an expected call of GetReservations. +func (mr *MockConnectorMockRecorder) GetReservations(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetReservations", reflect.TypeOf((*MockConnector)(nil).GetReservations), arg0, arg1, arg2) +} + // IFInfo mocks base method. func (m *MockConnector) IFInfo(arg0 context.Context, arg1 []common.IFIDType) (map[common.IFIDType]*net.UDPAddr, error) { m.ctrl.T.Helper() diff --git a/pkg/hummingbird/BUILD.bazel b/pkg/hummingbird/BUILD.bazel index c4005fde0e..3cfc18001c 100644 --- a/pkg/hummingbird/BUILD.bazel +++ b/pkg/hummingbird/BUILD.bazel @@ -2,12 +2,17 @@ load("//tools/lint:go.bzl", "go_library", "go_test") go_library( name = "go_default_library", - srcs = ["hummingbird.go"], + srcs = [ + "flyover.go", + "hummingbird.go", + "resevation.go", + ], importpath = "github.com/scionproto/scion/pkg/hummingbird", visibility = ["//visibility:public"], deps = [ "//pkg/addr:go_default_library", "//pkg/log:go_default_library", + "//pkg/private/common:go_default_library", "//pkg/private/serrors:go_default_library", "//pkg/slayers/path/hummingbird:go_default_library", "//pkg/slayers/path/scion:go_default_library", diff --git a/pkg/hummingbird/flyover.go b/pkg/hummingbird/flyover.go new file mode 100644 index 0000000000..841ac56055 --- /dev/null +++ b/pkg/hummingbird/flyover.go @@ -0,0 +1,26 @@ +// Copyright 2023 ETH Zurich +// +// 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 hummingbird + +import ( + "github.com/scionproto/scion/pkg/addr" + "github.com/scionproto/scion/pkg/private/common" +) + +type FlyoverJuanDeleteme struct { + Ingress common.IFIDType + Egress common.IFIDType + IA addr.IA +} diff --git a/pkg/hummingbird/resevation.go b/pkg/hummingbird/resevation.go new file mode 100644 index 0000000000..813e8642cf --- /dev/null +++ b/pkg/hummingbird/resevation.go @@ -0,0 +1,36 @@ +// Copyright 2023 ETH Zurich +// +// 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 hummingbird + +import "github.com/scionproto/scion/pkg/snet/path" + +type ReservationJuanDeleteme struct { + SCIONPath path.Path + Flyovers []*FlyoverJuanDeleteme + Ratio float64 // flyover/hops ratio +} + +// HopCount returns the number of hops in this path, as understood by a hop in a regular SCION path. +func (r ReservationJuanDeleteme) HopCount() int { + return len(r.SCIONPath.Meta.Interfaces) +} + +func (r ReservationJuanDeleteme) FlyoverCount() int { + return len(r.Flyovers) +} + +func (r ReservationJuanDeleteme) LessThan(other *ReservationJuanDeleteme) bool { + return r.Ratio < other.Ratio +} diff --git a/pkg/proto/daemon/daemon.pb.go b/pkg/proto/daemon/daemon.pb.go index 24bddc037b..ee136c5a70 100644 --- a/pkg/proto/daemon/daemon.pb.go +++ b/pkg/proto/daemon/daemon.pb.go @@ -1511,183 +1511,137 @@ var file_proto_daemon_v1_daemon_proto_rawDesc = []byte{ 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1a, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x64, 0x72, 0x6b, 0x65, 0x79, 0x2f, 0x76, 0x31, - 0x2f, 0x64, 0x72, 0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x92, 0x01, 0x0a, - 0x0c, 0x50, 0x61, 0x74, 0x68, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x22, 0x0a, - 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x73, 0x64, 0x5f, 0x61, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x73, 0x64, 0x41, - 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x5f, 0x69, 0x73, 0x64, 0x5f, 0x61, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x64, - 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x73, 0x64, 0x41, 0x73, 0x12, - 0x18, 0x0a, 0x07, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x07, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x69, 0x64, - 0x64, 0x65, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x68, 0x69, 0x64, 0x64, 0x65, - 0x6e, 0x22, 0x3c, 0x0a, 0x0d, 0x50, 0x61, 0x74, 0x68, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x05, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, - 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x05, 0x70, 0x61, 0x74, 0x68, 0x73, 0x22, - 0x94, 0x04, 0x0a, 0x04, 0x50, 0x61, 0x74, 0x68, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x61, 0x77, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x72, 0x61, 0x77, 0x12, 0x38, 0x0a, 0x09, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, - 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x52, 0x09, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x66, 0x61, 0x63, 0x65, 0x12, 0x3e, 0x0a, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, - 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x49, - 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x52, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, - 0x61, 0x63, 0x65, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x74, 0x75, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x03, 0x6d, 0x74, 0x75, 0x12, 0x3a, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x33, 0x0a, 0x07, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x06, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, - 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x62, 0x61, 0x6e, 0x64, 0x77, - 0x69, 0x64, 0x74, 0x68, 0x18, 0x07, 0x20, 0x03, 0x28, 0x04, 0x52, 0x09, 0x62, 0x61, 0x6e, 0x64, - 0x77, 0x69, 0x64, 0x74, 0x68, 0x12, 0x31, 0x0a, 0x03, 0x67, 0x65, 0x6f, 0x18, 0x08, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, - 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6f, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, - 0x74, 0x65, 0x73, 0x52, 0x03, 0x67, 0x65, 0x6f, 0x12, 0x36, 0x0a, 0x09, 0x6c, 0x69, 0x6e, 0x6b, - 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, - 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x52, 0x08, 0x6c, 0x69, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x68, 0x6f, 0x70, - 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, - 0x6c, 0x48, 0x6f, 0x70, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x74, 0x65, 0x73, 0x18, 0x0b, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x6f, 0x74, 0x65, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x65, - 0x70, 0x69, 0x63, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, - 0x31, 0x2e, 0x45, 0x70, 0x69, 0x63, 0x41, 0x75, 0x74, 0x68, 0x73, 0x52, 0x09, 0x65, 0x70, 0x69, - 0x63, 0x41, 0x75, 0x74, 0x68, 0x73, 0x22, 0x45, 0x0a, 0x09, 0x45, 0x70, 0x69, 0x63, 0x41, 0x75, - 0x74, 0x68, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x68, 0x76, 0x66, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x61, 0x75, 0x74, 0x68, 0x50, 0x68, 0x76, 0x66, - 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6c, 0x68, 0x76, 0x66, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x08, 0x61, 0x75, 0x74, 0x68, 0x4c, 0x68, 0x76, 0x66, 0x22, 0x36, 0x0a, - 0x0d, 0x50, 0x61, 0x74, 0x68, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x12, 0x15, - 0x0a, 0x06, 0x69, 0x73, 0x64, 0x5f, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, - 0x69, 0x73, 0x64, 0x41, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x02, 0x69, 0x64, 0x22, 0x64, 0x0a, 0x0e, 0x47, 0x65, 0x6f, 0x43, 0x6f, 0x6f, 0x72, - 0x64, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x61, 0x74, 0x69, 0x74, - 0x75, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x02, 0x52, 0x08, 0x6c, 0x61, 0x74, 0x69, 0x74, - 0x75, 0x64, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6c, 0x6f, 0x6e, 0x67, 0x69, 0x74, 0x75, 0x64, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x02, 0x52, 0x09, 0x6c, 0x6f, 0x6e, 0x67, 0x69, 0x74, 0x75, 0x64, - 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x22, 0x0a, 0x09, 0x41, - 0x53, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x69, 0x73, 0x64, 0x5f, - 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x69, 0x73, 0x64, 0x41, 0x73, 0x22, - 0x49, 0x0a, 0x0a, 0x41, 0x53, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x15, 0x0a, - 0x06, 0x69, 0x73, 0x64, 0x5f, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x69, - 0x73, 0x64, 0x41, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x04, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x74, 0x75, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6d, 0x74, 0x75, 0x22, 0x13, 0x0a, 0x11, 0x49, 0x6e, - 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, - 0xc4, 0x01, 0x0a, 0x12, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, - 0x61, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x74, - 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, - 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, - 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x73, 0x1a, 0x59, 0x0a, 0x0f, 0x49, - 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, - 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x40, 0x0a, 0x09, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, - 0x61, 0x63, 0x65, 0x12, 0x33, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, - 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x64, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x52, - 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x11, 0x0a, 0x0f, 0x53, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xba, 0x01, 0x0a, 0x10, - 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x4b, 0x0a, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, - 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x1a, 0x59, 0x0a, - 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x32, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, - 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x43, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, - 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x34, 0x0a, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x52, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x22, 0x1b, 0x0a, - 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x69, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x69, 0x22, 0x24, 0x0a, 0x08, 0x55, 0x6e, - 0x64, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x22, 0x43, 0x0a, 0x1a, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, - 0x61, 0x63, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, - 0x0a, 0x06, 0x69, 0x73, 0x64, 0x5f, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, - 0x69, 0x73, 0x64, 0x41, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x02, 0x69, 0x64, 0x22, 0x1d, 0x0a, 0x1b, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x49, - 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xcf, 0x01, 0x0a, 0x12, 0x44, 0x52, 0x4b, 0x65, 0x79, 0x48, 0x6f, - 0x73, 0x74, 0x41, 0x53, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x35, 0x0a, 0x08, 0x76, - 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x2f, 0x64, 0x72, 0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x21, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2f, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2f, 0x76, 0x31, 0x2f, 0x68, 0x75, + 0x6d, 0x6d, 0x69, 0x6e, 0x67, 0x62, 0x69, 0x72, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, + 0x92, 0x01, 0x0a, 0x0c, 0x50, 0x61, 0x74, 0x68, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x22, 0x0a, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x73, 0x64, 0x5f, 0x61, + 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, + 0x73, 0x64, 0x41, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x73, 0x64, 0x5f, 0x61, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x10, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x73, 0x64, + 0x41, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x07, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x12, 0x16, 0x0a, 0x06, + 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x68, 0x69, + 0x64, 0x64, 0x65, 0x6e, 0x22, 0x3c, 0x0a, 0x0d, 0x50, 0x61, 0x74, 0x68, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x05, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, + 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x05, 0x70, 0x61, 0x74, + 0x68, 0x73, 0x22, 0x94, 0x04, 0x0a, 0x04, 0x50, 0x61, 0x74, 0x68, 0x12, 0x10, 0x0a, 0x03, 0x72, + 0x61, 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x72, 0x61, 0x77, 0x12, 0x38, 0x0a, + 0x09, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, + 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x52, 0x09, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x12, 0x3e, 0x0a, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x66, 0x61, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, + 0x74, 0x68, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x52, 0x0a, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x74, 0x75, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6d, 0x74, 0x75, 0x12, 0x3a, 0x0a, 0x0a, 0x65, 0x78, 0x70, + 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x07, 0x76, 0x61, 0x6c, 0x54, 0x69, - 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x5f, 0x69, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, - 0x64, 0x72, 0x6b, 0x65, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, - 0x6c, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x49, 0x64, 0x12, 0x15, 0x0a, - 0x06, 0x73, 0x72, 0x63, 0x5f, 0x69, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x73, - 0x72, 0x63, 0x49, 0x61, 0x12, 0x15, 0x0a, 0x06, 0x64, 0x73, 0x74, 0x5f, 0x69, 0x61, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x64, 0x73, 0x74, 0x49, 0x61, 0x12, 0x19, 0x0a, 0x08, 0x73, - 0x72, 0x63, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, - 0x72, 0x63, 0x48, 0x6f, 0x73, 0x74, 0x22, 0x9d, 0x01, 0x0a, 0x13, 0x44, 0x52, 0x4b, 0x65, 0x79, - 0x48, 0x6f, 0x73, 0x74, 0x41, 0x53, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, - 0x0a, 0x0b, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, - 0x0a, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x12, 0x37, 0x0a, 0x09, 0x65, - 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x65, 0x70, 0x6f, 0x63, - 0x68, 0x45, 0x6e, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0xcf, 0x01, 0x0a, 0x12, 0x44, 0x52, 0x4b, 0x65, 0x79, - 0x41, 0x53, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x35, 0x0a, - 0x08, 0x76, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x07, 0x76, 0x61, 0x6c, - 0x54, 0x69, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, - 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2e, 0x64, 0x72, 0x6b, 0x65, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, - 0x63, 0x6f, 0x6c, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x49, 0x64, 0x12, - 0x15, 0x0a, 0x06, 0x73, 0x72, 0x63, 0x5f, 0x69, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x05, 0x73, 0x72, 0x63, 0x49, 0x61, 0x12, 0x15, 0x0a, 0x06, 0x64, 0x73, 0x74, 0x5f, 0x69, 0x61, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x64, 0x73, 0x74, 0x49, 0x61, 0x12, 0x19, 0x0a, - 0x08, 0x64, 0x73, 0x74, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x64, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x22, 0x9d, 0x01, 0x0a, 0x13, 0x44, 0x52, 0x4b, - 0x65, 0x79, 0x41, 0x53, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x3b, 0x0a, 0x0b, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x52, 0x0a, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x12, 0x37, 0x0a, - 0x09, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x33, 0x0a, 0x07, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, + 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x07, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x62, 0x61, + 0x6e, 0x64, 0x77, 0x69, 0x64, 0x74, 0x68, 0x18, 0x07, 0x20, 0x03, 0x28, 0x04, 0x52, 0x09, 0x62, + 0x61, 0x6e, 0x64, 0x77, 0x69, 0x64, 0x74, 0x68, 0x12, 0x31, 0x0a, 0x03, 0x67, 0x65, 0x6f, 0x18, + 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, + 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6f, 0x43, 0x6f, 0x6f, 0x72, 0x64, + 0x69, 0x6e, 0x61, 0x74, 0x65, 0x73, 0x52, 0x03, 0x67, 0x65, 0x6f, 0x12, 0x36, 0x0a, 0x09, 0x6c, + 0x69, 0x6e, 0x6b, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x19, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, + 0x2e, 0x4c, 0x69, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x52, 0x08, 0x6c, 0x69, 0x6e, 0x6b, 0x54, + 0x79, 0x70, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, + 0x68, 0x6f, 0x70, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x48, 0x6f, 0x70, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x74, 0x65, + 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x6f, 0x74, 0x65, 0x73, 0x12, 0x39, + 0x0a, 0x0a, 0x65, 0x70, 0x69, 0x63, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x73, 0x18, 0x0c, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, + 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x70, 0x69, 0x63, 0x41, 0x75, 0x74, 0x68, 0x73, 0x52, 0x09, + 0x65, 0x70, 0x69, 0x63, 0x41, 0x75, 0x74, 0x68, 0x73, 0x22, 0x45, 0x0a, 0x09, 0x45, 0x70, 0x69, + 0x63, 0x41, 0x75, 0x74, 0x68, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, + 0x68, 0x76, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x61, 0x75, 0x74, 0x68, 0x50, + 0x68, 0x76, 0x66, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6c, 0x68, 0x76, 0x66, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x61, 0x75, 0x74, 0x68, 0x4c, 0x68, 0x76, 0x66, + 0x22, 0x36, 0x0a, 0x0d, 0x50, 0x61, 0x74, 0x68, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, + 0x65, 0x12, 0x15, 0x0a, 0x06, 0x69, 0x73, 0x64, 0x5f, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x05, 0x69, 0x73, 0x64, 0x41, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x22, 0x64, 0x0a, 0x0e, 0x47, 0x65, 0x6f, 0x43, + 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x61, + 0x74, 0x69, 0x74, 0x75, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x02, 0x52, 0x08, 0x6c, 0x61, + 0x74, 0x69, 0x74, 0x75, 0x64, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6c, 0x6f, 0x6e, 0x67, 0x69, 0x74, + 0x75, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x02, 0x52, 0x09, 0x6c, 0x6f, 0x6e, 0x67, 0x69, + 0x74, 0x75, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x22, + 0x0a, 0x09, 0x41, 0x53, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x69, + 0x73, 0x64, 0x5f, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x69, 0x73, 0x64, + 0x41, 0x73, 0x22, 0x49, 0x0a, 0x0a, 0x41, 0x53, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x15, 0x0a, 0x06, 0x69, 0x73, 0x64, 0x5f, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x05, 0x69, 0x73, 0x64, 0x41, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x72, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6d, + 0x74, 0x75, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6d, 0x74, 0x75, 0x22, 0x13, 0x0a, + 0x11, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x22, 0xc4, 0x01, 0x0a, 0x12, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x0a, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, + 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x0a, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x73, 0x1a, 0x59, + 0x0a, 0x0f, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, + 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x40, 0x0a, 0x09, 0x49, 0x6e, 0x74, + 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x12, 0x33, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x64, 0x65, 0x72, 0x6c, + 0x61, 0x79, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x11, 0x0a, 0x0f, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xba, + 0x01, 0x0a, 0x10, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, + 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, + 0x1a, 0x59, 0x0a, 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x32, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, + 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x43, 0x0a, 0x0b, 0x4c, + 0x69, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x34, 0x0a, 0x08, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, + 0x22, 0x1b, 0x0a, 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, + 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x69, 0x22, 0x24, 0x0a, + 0x08, 0x55, 0x6e, 0x64, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x22, 0x43, 0x0a, 0x1a, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x49, 0x6e, 0x74, + 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x15, 0x0a, 0x06, 0x69, 0x73, 0x64, 0x5f, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x05, 0x69, 0x73, 0x64, 0x41, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x22, 0x1d, 0x0a, 0x1b, 0x4e, 0x6f, 0x74, 0x69, + 0x66, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xcf, 0x01, 0x0a, 0x12, 0x44, 0x52, 0x4b, 0x65, + 0x79, 0x48, 0x6f, 0x73, 0x74, 0x41, 0x53, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x35, + 0x0a, 0x08, 0x76, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x65, 0x70, - 0x6f, 0x63, 0x68, 0x45, 0x6e, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0xec, 0x01, 0x0a, 0x14, 0x44, 0x52, 0x4b, - 0x65, 0x79, 0x48, 0x6f, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x35, 0x0a, 0x08, 0x76, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, - 0x07, 0x76, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x63, 0x6f, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x72, 0x6b, 0x65, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x50, - 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, - 0x6c, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x73, 0x72, 0x63, 0x5f, 0x69, 0x61, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x05, 0x73, 0x72, 0x63, 0x49, 0x61, 0x12, 0x15, 0x0a, 0x06, 0x64, 0x73, - 0x74, 0x5f, 0x69, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x64, 0x73, 0x74, 0x49, - 0x61, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x72, 0x63, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x72, 0x63, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, - 0x64, 0x73, 0x74, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x64, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x22, 0x9f, 0x01, 0x0a, 0x15, 0x44, 0x52, 0x4b, 0x65, - 0x79, 0x48, 0x6f, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x07, 0x76, 0x61, + 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x64, 0x72, 0x6b, 0x65, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x49, 0x64, + 0x12, 0x15, 0x0a, 0x06, 0x73, 0x72, 0x63, 0x5f, 0x69, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x05, 0x73, 0x72, 0x63, 0x49, 0x61, 0x12, 0x15, 0x0a, 0x06, 0x64, 0x73, 0x74, 0x5f, 0x69, + 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x64, 0x73, 0x74, 0x49, 0x61, 0x12, 0x19, + 0x0a, 0x08, 0x73, 0x72, 0x63, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x73, 0x72, 0x63, 0x48, 0x6f, 0x73, 0x74, 0x22, 0x9d, 0x01, 0x0a, 0x13, 0x44, 0x52, + 0x4b, 0x65, 0x79, 0x48, 0x6f, 0x73, 0x74, 0x41, 0x53, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x0b, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, @@ -1696,63 +1650,130 @@ var file_proto_daemon_v1_daemon_proto_rawDesc = []byte{ 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x45, 0x6e, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x2a, 0x6c, 0x0a, 0x08, 0x4c, 0x69, 0x6e, - 0x6b, 0x54, 0x79, 0x70, 0x65, 0x12, 0x19, 0x0a, 0x15, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, 0x54, 0x59, - 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, - 0x12, 0x14, 0x0a, 0x10, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x49, - 0x52, 0x45, 0x43, 0x54, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, 0x54, - 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x55, 0x4c, 0x54, 0x49, 0x5f, 0x48, 0x4f, 0x50, 0x10, 0x02, 0x12, - 0x16, 0x0a, 0x12, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4f, 0x50, 0x45, - 0x4e, 0x5f, 0x4e, 0x45, 0x54, 0x10, 0x03, 0x32, 0xd4, 0x05, 0x0a, 0x0d, 0x44, 0x61, 0x65, 0x6d, - 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x48, 0x0a, 0x05, 0x50, 0x61, 0x74, - 0x68, 0x73, 0x12, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, - 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, - 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x3f, 0x0a, 0x02, 0x41, 0x53, 0x12, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x53, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, - 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x53, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x12, 0x57, 0x0a, 0x0a, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, - 0x65, 0x73, 0x12, 0x22, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, - 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, - 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, - 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x51, 0x0a, - 0x08, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x12, 0x72, 0x0a, 0x13, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, - 0x61, 0x63, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x12, 0x2b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, - 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, - 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, - 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x49, 0x6e, 0x74, - 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x0b, 0x44, 0x52, 0x4b, 0x65, 0x79, 0x41, 0x53, 0x48, - 0x6f, 0x73, 0x74, 0x12, 0x23, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, - 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x52, 0x4b, 0x65, 0x79, 0x41, 0x53, 0x48, 0x6f, 0x73, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x52, 0x4b, 0x65, 0x79, - 0x41, 0x53, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x12, 0x5a, 0x0a, 0x0b, 0x44, 0x52, 0x4b, 0x65, 0x79, 0x48, 0x6f, 0x73, 0x74, 0x41, 0x53, 0x12, - 0x23, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, - 0x31, 0x2e, 0x44, 0x52, 0x4b, 0x65, 0x79, 0x48, 0x6f, 0x73, 0x74, 0x41, 0x53, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, - 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x52, 0x4b, 0x65, 0x79, 0x48, 0x6f, 0x73, 0x74, - 0x41, 0x53, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x60, 0x0a, 0x0d, - 0x44, 0x52, 0x4b, 0x65, 0x79, 0x48, 0x6f, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x25, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0xcf, 0x01, 0x0a, 0x12, 0x44, 0x52, + 0x4b, 0x65, 0x79, 0x41, 0x53, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x35, 0x0a, 0x08, 0x76, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x07, + 0x76, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x63, 0x6f, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x72, 0x6b, 0x65, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, + 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x73, 0x72, 0x63, 0x5f, 0x69, 0x61, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x05, 0x73, 0x72, 0x63, 0x49, 0x61, 0x12, 0x15, 0x0a, 0x06, 0x64, 0x73, 0x74, + 0x5f, 0x69, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x64, 0x73, 0x74, 0x49, 0x61, + 0x12, 0x19, 0x0a, 0x08, 0x64, 0x73, 0x74, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x64, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x22, 0x9d, 0x01, 0x0a, 0x13, + 0x44, 0x52, 0x4b, 0x65, 0x79, 0x41, 0x53, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x0b, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x62, 0x65, 0x67, + 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x42, 0x65, 0x67, 0x69, 0x6e, + 0x12, 0x37, 0x0a, 0x09, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, + 0x08, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x45, 0x6e, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0xec, 0x01, 0x0a, 0x14, 0x44, 0x52, 0x4b, 0x65, 0x79, 0x48, 0x6f, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, - 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x52, 0x4b, 0x65, 0x79, 0x48, 0x6f, 0x73, 0x74, - 0x48, 0x6f, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x2e, - 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x63, 0x69, - 0x6f, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x73, 0x63, 0x69, 0x6f, 0x6e, 0x2f, 0x70, 0x6b, - 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x35, 0x0a, 0x08, 0x76, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x52, 0x07, 0x76, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x0b, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x72, 0x6b, 0x65, 0x79, 0x2e, 0x76, + 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6c, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x73, 0x72, 0x63, 0x5f, 0x69, 0x61, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x73, 0x72, 0x63, 0x49, 0x61, 0x12, 0x15, 0x0a, + 0x06, 0x64, 0x73, 0x74, 0x5f, 0x69, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x64, + 0x73, 0x74, 0x49, 0x61, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x72, 0x63, 0x5f, 0x68, 0x6f, 0x73, 0x74, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x72, 0x63, 0x48, 0x6f, 0x73, 0x74, 0x12, + 0x19, 0x0a, 0x08, 0x64, 0x73, 0x74, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x64, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x22, 0x9f, 0x01, 0x0a, 0x15, 0x44, + 0x52, 0x4b, 0x65, 0x79, 0x48, 0x6f, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x0b, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x62, 0x65, + 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x42, 0x65, 0x67, 0x69, + 0x6e, 0x12, 0x37, 0x0a, 0x09, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x65, 0x6e, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x52, 0x08, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x45, 0x6e, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x2a, 0x6c, 0x0a, 0x08, + 0x4c, 0x69, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x12, 0x19, 0x0a, 0x15, 0x4c, 0x49, 0x4e, 0x4b, + 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, + 0x44, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, 0x54, 0x59, 0x50, 0x45, + 0x5f, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x4c, 0x49, 0x4e, + 0x4b, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x55, 0x4c, 0x54, 0x49, 0x5f, 0x48, 0x4f, 0x50, + 0x10, 0x02, 0x12, 0x16, 0x0a, 0x12, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x4f, 0x50, 0x45, 0x4e, 0x5f, 0x4e, 0x45, 0x54, 0x10, 0x03, 0x32, 0xfd, 0x07, 0x0a, 0x0d, 0x44, + 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x48, 0x0a, 0x05, + 0x50, 0x61, 0x74, 0x68, 0x73, 0x12, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, + 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, + 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3f, 0x0a, 0x02, 0x41, 0x53, 0x12, 0x1a, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x41, + 0x53, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x53, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x57, 0x0a, 0x0a, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x66, 0x61, 0x63, 0x65, 0x73, 0x12, 0x22, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, + 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, + 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x66, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x12, 0x51, 0x0a, 0x08, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x20, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x72, 0x0a, 0x13, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x49, 0x6e, 0x74, + 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x12, 0x2b, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x74, + 0x69, 0x66, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x44, 0x6f, 0x77, 0x6e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, + 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x0b, 0x44, 0x52, 0x4b, 0x65, 0x79, + 0x41, 0x53, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x23, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, + 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x52, 0x4b, 0x65, 0x79, 0x41, 0x53, + 0x48, 0x6f, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x52, + 0x4b, 0x65, 0x79, 0x41, 0x53, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x0b, 0x44, 0x52, 0x4b, 0x65, 0x79, 0x48, 0x6f, 0x73, 0x74, + 0x41, 0x53, 0x12, 0x23, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, + 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x52, 0x4b, 0x65, 0x79, 0x48, 0x6f, 0x73, 0x74, 0x41, 0x53, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x52, 0x4b, 0x65, 0x79, 0x48, + 0x6f, 0x73, 0x74, 0x41, 0x53, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x60, 0x0a, 0x0d, 0x44, 0x52, 0x4b, 0x65, 0x79, 0x48, 0x6f, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, + 0x12, 0x25, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, + 0x76, 0x31, 0x2e, 0x44, 0x52, 0x4b, 0x65, 0x79, 0x48, 0x6f, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x52, 0x4b, 0x65, 0x79, 0x48, + 0x6f, 0x73, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x60, 0x0a, 0x0d, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x46, 0x6c, 0x79, 0x6f, 0x76, 0x65, + 0x72, 0x73, 0x12, 0x25, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, + 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x46, 0x6c, 0x79, 0x6f, 0x76, 0x65, + 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x6f, 0x72, + 0x65, 0x46, 0x6c, 0x79, 0x6f, 0x76, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x5d, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x46, 0x6c, 0x79, 0x6f, 0x76, + 0x65, 0x72, 0x73, 0x12, 0x24, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, + 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x46, 0x6c, 0x79, 0x6f, 0x76, 0x65, + 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x46, 0x6c, 0x79, 0x6f, 0x76, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x66, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x27, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, + 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x65, 0x72, + 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, + 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x63, 0x69, 0x6f, 0x6e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2f, 0x73, 0x63, 0x69, 0x6f, 0x6e, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2f, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -1800,6 +1821,12 @@ var file_proto_daemon_v1_daemon_proto_goTypes = []interface{}{ (*timestamppb.Timestamp)(nil), // 27: google.protobuf.Timestamp (*durationpb.Duration)(nil), // 28: google.protobuf.Duration (drkey.Protocol)(0), // 29: proto.drkey.v1.Protocol + (*StoreFlyoversRequest)(nil), // 30: proto.daemon.v1.StoreFlyoversRequest + (*ListFlyoversRequest)(nil), // 31: proto.daemon.v1.ListFlyoversRequest + (*GetReservationsRequest)(nil), // 32: proto.daemon.v1.GetReservationsRequest + (*StoreFlyoversResponse)(nil), // 33: proto.daemon.v1.StoreFlyoversResponse + (*ListFlyoversResponse)(nil), // 34: proto.daemon.v1.ListFlyoversResponse + (*GetReservationsResponse)(nil), // 35: proto.daemon.v1.GetReservationsResponse } var file_proto_daemon_v1_daemon_proto_depIdxs = []int32{ 3, // 0: proto.daemon.v1.PathsResponse.paths:type_name -> proto.daemon.v1.Path @@ -1836,16 +1863,22 @@ var file_proto_daemon_v1_daemon_proto_depIdxs = []int32{ 21, // 31: proto.daemon.v1.DaemonService.DRKeyASHost:input_type -> proto.daemon.v1.DRKeyASHostRequest 19, // 32: proto.daemon.v1.DaemonService.DRKeyHostAS:input_type -> proto.daemon.v1.DRKeyHostASRequest 23, // 33: proto.daemon.v1.DaemonService.DRKeyHostHost:input_type -> proto.daemon.v1.DRKeyHostHostRequest - 2, // 34: proto.daemon.v1.DaemonService.Paths:output_type -> proto.daemon.v1.PathsResponse - 8, // 35: proto.daemon.v1.DaemonService.AS:output_type -> proto.daemon.v1.ASResponse - 10, // 36: proto.daemon.v1.DaemonService.Interfaces:output_type -> proto.daemon.v1.InterfacesResponse - 13, // 37: proto.daemon.v1.DaemonService.Services:output_type -> proto.daemon.v1.ServicesResponse - 18, // 38: proto.daemon.v1.DaemonService.NotifyInterfaceDown:output_type -> proto.daemon.v1.NotifyInterfaceDownResponse - 22, // 39: proto.daemon.v1.DaemonService.DRKeyASHost:output_type -> proto.daemon.v1.DRKeyASHostResponse - 20, // 40: proto.daemon.v1.DaemonService.DRKeyHostAS:output_type -> proto.daemon.v1.DRKeyHostASResponse - 24, // 41: proto.daemon.v1.DaemonService.DRKeyHostHost:output_type -> proto.daemon.v1.DRKeyHostHostResponse - 34, // [34:42] is the sub-list for method output_type - 26, // [26:34] is the sub-list for method input_type + 30, // 34: proto.daemon.v1.DaemonService.StoreFlyovers:input_type -> proto.daemon.v1.StoreFlyoversRequest + 31, // 35: proto.daemon.v1.DaemonService.ListFlyovers:input_type -> proto.daemon.v1.ListFlyoversRequest + 32, // 36: proto.daemon.v1.DaemonService.GetReservations:input_type -> proto.daemon.v1.GetReservationsRequest + 2, // 37: proto.daemon.v1.DaemonService.Paths:output_type -> proto.daemon.v1.PathsResponse + 8, // 38: proto.daemon.v1.DaemonService.AS:output_type -> proto.daemon.v1.ASResponse + 10, // 39: proto.daemon.v1.DaemonService.Interfaces:output_type -> proto.daemon.v1.InterfacesResponse + 13, // 40: proto.daemon.v1.DaemonService.Services:output_type -> proto.daemon.v1.ServicesResponse + 18, // 41: proto.daemon.v1.DaemonService.NotifyInterfaceDown:output_type -> proto.daemon.v1.NotifyInterfaceDownResponse + 22, // 42: proto.daemon.v1.DaemonService.DRKeyASHost:output_type -> proto.daemon.v1.DRKeyASHostResponse + 20, // 43: proto.daemon.v1.DaemonService.DRKeyHostAS:output_type -> proto.daemon.v1.DRKeyHostASResponse + 24, // 44: proto.daemon.v1.DaemonService.DRKeyHostHost:output_type -> proto.daemon.v1.DRKeyHostHostResponse + 33, // 45: proto.daemon.v1.DaemonService.StoreFlyovers:output_type -> proto.daemon.v1.StoreFlyoversResponse + 34, // 46: proto.daemon.v1.DaemonService.ListFlyovers:output_type -> proto.daemon.v1.ListFlyoversResponse + 35, // 47: proto.daemon.v1.DaemonService.GetReservations:output_type -> proto.daemon.v1.GetReservationsResponse + 37, // [37:48] is the sub-list for method output_type + 26, // [26:37] is the sub-list for method input_type 26, // [26:26] is the sub-list for extension type_name 26, // [26:26] is the sub-list for extension extendee 0, // [0:26] is the sub-list for field type_name @@ -1856,6 +1889,7 @@ func file_proto_daemon_v1_daemon_proto_init() { if File_proto_daemon_v1_daemon_proto != nil { return } + file_proto_daemon_v1_hummingbird_proto_init() if !protoimpl.UnsafeEnabled { file_proto_daemon_v1_daemon_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PathsRequest); i { @@ -2187,6 +2221,9 @@ type DaemonServiceClient interface { DRKeyASHost(ctx context.Context, in *DRKeyASHostRequest, opts ...grpc.CallOption) (*DRKeyASHostResponse, error) DRKeyHostAS(ctx context.Context, in *DRKeyHostASRequest, opts ...grpc.CallOption) (*DRKeyHostASResponse, error) DRKeyHostHost(ctx context.Context, in *DRKeyHostHostRequest, opts ...grpc.CallOption) (*DRKeyHostHostResponse, error) + StoreFlyovers(ctx context.Context, in *StoreFlyoversRequest, opts ...grpc.CallOption) (*StoreFlyoversResponse, error) + ListFlyovers(ctx context.Context, in *ListFlyoversRequest, opts ...grpc.CallOption) (*ListFlyoversResponse, error) + GetReservations(ctx context.Context, in *GetReservationsRequest, opts ...grpc.CallOption) (*GetReservationsResponse, error) } type daemonServiceClient struct { @@ -2269,6 +2306,33 @@ func (c *daemonServiceClient) DRKeyHostHost(ctx context.Context, in *DRKeyHostHo return out, nil } +func (c *daemonServiceClient) StoreFlyovers(ctx context.Context, in *StoreFlyoversRequest, opts ...grpc.CallOption) (*StoreFlyoversResponse, error) { + out := new(StoreFlyoversResponse) + err := c.cc.Invoke(ctx, "/proto.daemon.v1.DaemonService/StoreFlyovers", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *daemonServiceClient) ListFlyovers(ctx context.Context, in *ListFlyoversRequest, opts ...grpc.CallOption) (*ListFlyoversResponse, error) { + out := new(ListFlyoversResponse) + err := c.cc.Invoke(ctx, "/proto.daemon.v1.DaemonService/ListFlyovers", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *daemonServiceClient) GetReservations(ctx context.Context, in *GetReservationsRequest, opts ...grpc.CallOption) (*GetReservationsResponse, error) { + out := new(GetReservationsResponse) + err := c.cc.Invoke(ctx, "/proto.daemon.v1.DaemonService/GetReservations", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // DaemonServiceServer is the server API for DaemonService service. type DaemonServiceServer interface { Paths(context.Context, *PathsRequest) (*PathsResponse, error) @@ -2279,6 +2343,9 @@ type DaemonServiceServer interface { DRKeyASHost(context.Context, *DRKeyASHostRequest) (*DRKeyASHostResponse, error) DRKeyHostAS(context.Context, *DRKeyHostASRequest) (*DRKeyHostASResponse, error) DRKeyHostHost(context.Context, *DRKeyHostHostRequest) (*DRKeyHostHostResponse, error) + StoreFlyovers(context.Context, *StoreFlyoversRequest) (*StoreFlyoversResponse, error) + ListFlyovers(context.Context, *ListFlyoversRequest) (*ListFlyoversResponse, error) + GetReservations(context.Context, *GetReservationsRequest) (*GetReservationsResponse, error) } // UnimplementedDaemonServiceServer can be embedded to have forward compatible implementations. @@ -2309,6 +2376,15 @@ func (*UnimplementedDaemonServiceServer) DRKeyHostAS(context.Context, *DRKeyHost func (*UnimplementedDaemonServiceServer) DRKeyHostHost(context.Context, *DRKeyHostHostRequest) (*DRKeyHostHostResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method DRKeyHostHost not implemented") } +func (*UnimplementedDaemonServiceServer) StoreFlyovers(context.Context, *StoreFlyoversRequest) (*StoreFlyoversResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method StoreFlyovers not implemented") +} +func (*UnimplementedDaemonServiceServer) ListFlyovers(context.Context, *ListFlyoversRequest) (*ListFlyoversResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListFlyovers not implemented") +} +func (*UnimplementedDaemonServiceServer) GetReservations(context.Context, *GetReservationsRequest) (*GetReservationsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetReservations not implemented") +} func RegisterDaemonServiceServer(s *grpc.Server, srv DaemonServiceServer) { s.RegisterService(&_DaemonService_serviceDesc, srv) @@ -2458,6 +2534,60 @@ func _DaemonService_DRKeyHostHost_Handler(srv interface{}, ctx context.Context, return interceptor(ctx, in, info, handler) } +func _DaemonService_StoreFlyovers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StoreFlyoversRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DaemonServiceServer).StoreFlyovers(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/proto.daemon.v1.DaemonService/StoreFlyovers", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DaemonServiceServer).StoreFlyovers(ctx, req.(*StoreFlyoversRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _DaemonService_ListFlyovers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListFlyoversRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DaemonServiceServer).ListFlyovers(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/proto.daemon.v1.DaemonService/ListFlyovers", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DaemonServiceServer).ListFlyovers(ctx, req.(*ListFlyoversRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _DaemonService_GetReservations_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetReservationsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DaemonServiceServer).GetReservations(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/proto.daemon.v1.DaemonService/GetReservations", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DaemonServiceServer).GetReservations(ctx, req.(*GetReservationsRequest)) + } + return interceptor(ctx, in, info, handler) +} + var _DaemonService_serviceDesc = grpc.ServiceDesc{ ServiceName: "proto.daemon.v1.DaemonService", HandlerType: (*DaemonServiceServer)(nil), @@ -2494,6 +2624,18 @@ var _DaemonService_serviceDesc = grpc.ServiceDesc{ MethodName: "DRKeyHostHost", Handler: _DaemonService_DRKeyHostHost_Handler, }, + { + MethodName: "StoreFlyovers", + Handler: _DaemonService_StoreFlyovers_Handler, + }, + { + MethodName: "ListFlyovers", + Handler: _DaemonService_ListFlyovers_Handler, + }, + { + MethodName: "GetReservations", + Handler: _DaemonService_GetReservations_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "proto/daemon/v1/daemon.proto", diff --git a/pkg/proto/daemon/hummingbird.pb.go b/pkg/proto/daemon/hummingbird.pb.go new file mode 100644 index 0000000000..c66981198b --- /dev/null +++ b/pkg/proto/daemon/hummingbird.pb.go @@ -0,0 +1,516 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.30.0 +// protoc v3.15.3 +// source: proto/daemon/v1/hummingbird.proto + +package daemon + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type StoreFlyoversRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *StoreFlyoversRequest) Reset() { + *x = StoreFlyoversRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_daemon_v1_hummingbird_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StoreFlyoversRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StoreFlyoversRequest) ProtoMessage() {} + +func (x *StoreFlyoversRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_daemon_v1_hummingbird_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StoreFlyoversRequest.ProtoReflect.Descriptor instead. +func (*StoreFlyoversRequest) Descriptor() ([]byte, []int) { + return file_proto_daemon_v1_hummingbird_proto_rawDescGZIP(), []int{0} +} + +type StoreFlyoversResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *StoreFlyoversResponse) Reset() { + *x = StoreFlyoversResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_daemon_v1_hummingbird_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StoreFlyoversResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StoreFlyoversResponse) ProtoMessage() {} + +func (x *StoreFlyoversResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_daemon_v1_hummingbird_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StoreFlyoversResponse.ProtoReflect.Descriptor instead. +func (*StoreFlyoversResponse) Descriptor() ([]byte, []int) { + return file_proto_daemon_v1_hummingbird_proto_rawDescGZIP(), []int{1} +} + +type ListFlyoversRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ListFlyoversRequest) Reset() { + *x = ListFlyoversRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_daemon_v1_hummingbird_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListFlyoversRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListFlyoversRequest) ProtoMessage() {} + +func (x *ListFlyoversRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_daemon_v1_hummingbird_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListFlyoversRequest.ProtoReflect.Descriptor instead. +func (*ListFlyoversRequest) Descriptor() ([]byte, []int) { + return file_proto_daemon_v1_hummingbird_proto_rawDescGZIP(), []int{2} +} + +type ListFlyoversResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ListFlyoversResponse) Reset() { + *x = ListFlyoversResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_daemon_v1_hummingbird_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListFlyoversResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListFlyoversResponse) ProtoMessage() {} + +func (x *ListFlyoversResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_daemon_v1_hummingbird_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListFlyoversResponse.ProtoReflect.Descriptor instead. +func (*ListFlyoversResponse) Descriptor() ([]byte, []int) { + return file_proto_daemon_v1_hummingbird_proto_rawDescGZIP(), []int{3} +} + +type GetReservationsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SourceIsdAs uint64 `protobuf:"varint,1,opt,name=source_isd_as,json=sourceIsdAs,proto3" json:"source_isd_as,omitempty"` + DestinationIsdAs uint64 `protobuf:"varint,2,opt,name=destination_isd_as,json=destinationIsdAs,proto3" json:"destination_isd_as,omitempty"` + Refresh bool `protobuf:"varint,3,opt,name=refresh,proto3" json:"refresh,omitempty"` +} + +func (x *GetReservationsRequest) Reset() { + *x = GetReservationsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_daemon_v1_hummingbird_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetReservationsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetReservationsRequest) ProtoMessage() {} + +func (x *GetReservationsRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_daemon_v1_hummingbird_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetReservationsRequest.ProtoReflect.Descriptor instead. +func (*GetReservationsRequest) Descriptor() ([]byte, []int) { + return file_proto_daemon_v1_hummingbird_proto_rawDescGZIP(), []int{4} +} + +func (x *GetReservationsRequest) GetSourceIsdAs() uint64 { + if x != nil { + return x.SourceIsdAs + } + return 0 +} + +func (x *GetReservationsRequest) GetDestinationIsdAs() uint64 { + if x != nil { + return x.DestinationIsdAs + } + return 0 +} + +func (x *GetReservationsRequest) GetRefresh() bool { + if x != nil { + return x.Refresh + } + return false +} + +type GetReservationsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Reservations []*Reservation `protobuf:"bytes,1,rep,name=reservations,proto3" json:"reservations,omitempty"` +} + +func (x *GetReservationsResponse) Reset() { + *x = GetReservationsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_daemon_v1_hummingbird_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetReservationsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetReservationsResponse) ProtoMessage() {} + +func (x *GetReservationsResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_daemon_v1_hummingbird_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetReservationsResponse.ProtoReflect.Descriptor instead. +func (*GetReservationsResponse) Descriptor() ([]byte, []int) { + return file_proto_daemon_v1_hummingbird_proto_rawDescGZIP(), []int{5} +} + +func (x *GetReservationsResponse) GetReservations() []*Reservation { + if x != nil { + return x.Reservations + } + return nil +} + +type Reservation struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Raw []byte `protobuf:"bytes,1,opt,name=raw,proto3" json:"raw,omitempty"` + Ratio float64 `protobuf:"fixed64,2,opt,name=ratio,proto3" json:"ratio,omitempty"` +} + +func (x *Reservation) Reset() { + *x = Reservation{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_daemon_v1_hummingbird_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Reservation) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Reservation) ProtoMessage() {} + +func (x *Reservation) ProtoReflect() protoreflect.Message { + mi := &file_proto_daemon_v1_hummingbird_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Reservation.ProtoReflect.Descriptor instead. +func (*Reservation) Descriptor() ([]byte, []int) { + return file_proto_daemon_v1_hummingbird_proto_rawDescGZIP(), []int{6} +} + +func (x *Reservation) GetRaw() []byte { + if x != nil { + return x.Raw + } + return nil +} + +func (x *Reservation) GetRatio() float64 { + if x != nil { + return x.Ratio + } + return 0 +} + +var File_proto_daemon_v1_hummingbird_proto protoreflect.FileDescriptor + +var file_proto_daemon_v1_hummingbird_proto_rawDesc = []byte{ + 0x0a, 0x21, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2f, 0x76, + 0x31, 0x2f, 0x68, 0x75, 0x6d, 0x6d, 0x69, 0x6e, 0x67, 0x62, 0x69, 0x72, 0x64, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, + 0x6e, 0x2e, 0x76, 0x31, 0x22, 0x16, 0x0a, 0x14, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x46, 0x6c, 0x79, + 0x6f, 0x76, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x17, 0x0a, 0x15, + 0x53, 0x74, 0x6f, 0x72, 0x65, 0x46, 0x6c, 0x79, 0x6f, 0x76, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x46, 0x6c, 0x79, + 0x6f, 0x76, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x16, 0x0a, 0x14, + 0x4c, 0x69, 0x73, 0x74, 0x46, 0x6c, 0x79, 0x6f, 0x76, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x84, 0x01, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x65, + 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x22, 0x0a, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x73, 0x64, 0x5f, 0x61, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x73, + 0x64, 0x41, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x69, 0x73, 0x64, 0x5f, 0x61, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x10, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x73, 0x64, 0x41, + 0x73, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x07, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x22, 0x5b, 0x0a, 0x17, 0x47, + 0x65, 0x74, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0c, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x52, + 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x65, + 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x35, 0x0a, 0x0b, 0x52, 0x65, 0x73, 0x65, + 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x61, 0x77, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x72, 0x61, 0x77, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x42, + 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x63, + 0x69, 0x6f, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x73, 0x63, 0x69, 0x6f, 0x6e, 0x2f, 0x70, + 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_proto_daemon_v1_hummingbird_proto_rawDescOnce sync.Once + file_proto_daemon_v1_hummingbird_proto_rawDescData = file_proto_daemon_v1_hummingbird_proto_rawDesc +) + +func file_proto_daemon_v1_hummingbird_proto_rawDescGZIP() []byte { + file_proto_daemon_v1_hummingbird_proto_rawDescOnce.Do(func() { + file_proto_daemon_v1_hummingbird_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_daemon_v1_hummingbird_proto_rawDescData) + }) + return file_proto_daemon_v1_hummingbird_proto_rawDescData +} + +var file_proto_daemon_v1_hummingbird_proto_msgTypes = make([]protoimpl.MessageInfo, 7) +var file_proto_daemon_v1_hummingbird_proto_goTypes = []interface{}{ + (*StoreFlyoversRequest)(nil), // 0: proto.daemon.v1.StoreFlyoversRequest + (*StoreFlyoversResponse)(nil), // 1: proto.daemon.v1.StoreFlyoversResponse + (*ListFlyoversRequest)(nil), // 2: proto.daemon.v1.ListFlyoversRequest + (*ListFlyoversResponse)(nil), // 3: proto.daemon.v1.ListFlyoversResponse + (*GetReservationsRequest)(nil), // 4: proto.daemon.v1.GetReservationsRequest + (*GetReservationsResponse)(nil), // 5: proto.daemon.v1.GetReservationsResponse + (*Reservation)(nil), // 6: proto.daemon.v1.Reservation +} +var file_proto_daemon_v1_hummingbird_proto_depIdxs = []int32{ + 6, // 0: proto.daemon.v1.GetReservationsResponse.reservations:type_name -> proto.daemon.v1.Reservation + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_proto_daemon_v1_hummingbird_proto_init() } +func file_proto_daemon_v1_hummingbird_proto_init() { + if File_proto_daemon_v1_hummingbird_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_proto_daemon_v1_hummingbird_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StoreFlyoversRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_daemon_v1_hummingbird_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StoreFlyoversResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_daemon_v1_hummingbird_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListFlyoversRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_daemon_v1_hummingbird_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListFlyoversResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_daemon_v1_hummingbird_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetReservationsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_daemon_v1_hummingbird_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetReservationsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_daemon_v1_hummingbird_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Reservation); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_proto_daemon_v1_hummingbird_proto_rawDesc, + NumEnums: 0, + NumMessages: 7, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_proto_daemon_v1_hummingbird_proto_goTypes, + DependencyIndexes: file_proto_daemon_v1_hummingbird_proto_depIdxs, + MessageInfos: file_proto_daemon_v1_hummingbird_proto_msgTypes, + }.Build() + File_proto_daemon_v1_hummingbird_proto = out.File + file_proto_daemon_v1_hummingbird_proto_rawDesc = nil + file_proto_daemon_v1_hummingbird_proto_goTypes = nil + file_proto_daemon_v1_hummingbird_proto_depIdxs = nil +} diff --git a/private/hummingbirddb/BUILD.bazel b/private/hummingbirddb/BUILD.bazel new file mode 100644 index 0000000000..7edb99b21b --- /dev/null +++ b/private/hummingbirddb/BUILD.bazel @@ -0,0 +1,12 @@ +load("//tools/lint:go.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["db.go"], + importpath = "github.com/scionproto/scion/private/hummingbirddb", + visibility = ["//visibility:public"], + deps = [ + "//pkg/addr:go_default_library", + "//pkg/hummingbird:go_default_library", + ], +) diff --git a/private/hummingbirddb/db.go b/private/hummingbirddb/db.go new file mode 100644 index 0000000000..c72a94f03e --- /dev/null +++ b/private/hummingbirddb/db.go @@ -0,0 +1,45 @@ +// Copyright 2023 ETH Zurich +// +// 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 hummingbirddb + +import ( + "context" + "database/sql" + + "github.com/scionproto/scion/pkg/addr" + "github.com/scionproto/scion/pkg/hummingbird" +) + +type DB interface { + ReadWrite + BeginTransaction(ctx context.Context, opts *sql.TxOptions) (Transaction, error) +} + +type Read interface { + ListFlyovers(ctx context.Context, owners []addr.IA) ([]*hummingbird.Flyover, error) +} +type Write interface { +} + +type ReadWrite interface { + Read + Write +} + +type Transaction interface { + ReadWrite + Commit() error + Rollback() error +} diff --git a/private/hummingbirddb/mock_hummingbirddb/BUILD.bazel b/private/hummingbirddb/mock_hummingbirddb/BUILD.bazel new file mode 100644 index 0000000000..882ddc5478 --- /dev/null +++ b/private/hummingbirddb/mock_hummingbirddb/BUILD.bazel @@ -0,0 +1,23 @@ +load("//tools/lint:go.bzl", "go_library") +load("@io_bazel_rules_go//go:def.bzl", "gomock") + +gomock( + name = "go_default_mock", + out = "mock.go", + interfaces = ["DB"], + library = "//private/hummingbirddb:go_default_library", + package = "mock_hummingbirddb", +) + +go_library( + name = "go_default_library", + srcs = ["mock.go"], + importpath = "github.com/scionproto/scion/private/hummingbirddb/mock_hummingbirddb", + visibility = ["//visibility:public"], + deps = [ + "//pkg/addr:go_default_library", + "//pkg/hummingbird:go_default_library", + "//private/hummingbirddb:go_default_library", + "@com_github_golang_mock//gomock:go_default_library", + ], +) diff --git a/private/hummingbirddb/mock_hummingbirddb/mock.go b/private/hummingbirddb/mock_hummingbirddb/mock.go new file mode 100644 index 0000000000..ec6dab2ab6 --- /dev/null +++ b/private/hummingbirddb/mock_hummingbirddb/mock.go @@ -0,0 +1,69 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/scionproto/scion/private/hummingbirddb (interfaces: DB) + +// Package mock_hummingbirddb is a generated GoMock package. +package mock_hummingbirddb + +import ( + context "context" + sql "database/sql" + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + addr "github.com/scionproto/scion/pkg/addr" + hummingbird "github.com/scionproto/scion/pkg/hummingbird" + hummingbirddb "github.com/scionproto/scion/private/hummingbirddb" +) + +// MockDB is a mock of DB interface. +type MockDB struct { + ctrl *gomock.Controller + recorder *MockDBMockRecorder +} + +// MockDBMockRecorder is the mock recorder for MockDB. +type MockDBMockRecorder struct { + mock *MockDB +} + +// NewMockDB creates a new mock instance. +func NewMockDB(ctrl *gomock.Controller) *MockDB { + mock := &MockDB{ctrl: ctrl} + mock.recorder = &MockDBMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockDB) EXPECT() *MockDBMockRecorder { + return m.recorder +} + +// BeginTransaction mocks base method. +func (m *MockDB) BeginTransaction(arg0 context.Context, arg1 *sql.TxOptions) (hummingbirddb.Transaction, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BeginTransaction", arg0, arg1) + ret0, _ := ret[0].(hummingbirddb.Transaction) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// BeginTransaction indicates an expected call of BeginTransaction. +func (mr *MockDBMockRecorder) BeginTransaction(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BeginTransaction", reflect.TypeOf((*MockDB)(nil).BeginTransaction), arg0, arg1) +} + +// ListFlyovers mocks base method. +func (m *MockDB) ListFlyovers(arg0 context.Context, arg1 []addr.IA) ([]*hummingbird.Flyover, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListFlyovers", arg0, arg1) + ret0, _ := ret[0].([]*hummingbird.Flyover) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListFlyovers indicates an expected call of ListFlyovers. +func (mr *MockDBMockRecorder) ListFlyovers(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListFlyovers", reflect.TypeOf((*MockDB)(nil).ListFlyovers), arg0, arg1) +} diff --git a/proto/daemon/v1/BUILD.bazel b/proto/daemon/v1/BUILD.bazel index cf258adada..4a2a8968ea 100644 --- a/proto/daemon/v1/BUILD.bazel +++ b/proto/daemon/v1/BUILD.bazel @@ -4,6 +4,7 @@ proto_library( name = "daemon", srcs = [ "daemon.proto", + "hummingbird.proto", ], visibility = ["//visibility:public"], deps = [ diff --git a/proto/daemon/v1/daemon.proto b/proto/daemon/v1/daemon.proto index e8b2758b2b..329696a889 100644 --- a/proto/daemon/v1/daemon.proto +++ b/proto/daemon/v1/daemon.proto @@ -21,6 +21,7 @@ package proto.daemon.v1; import "google/protobuf/timestamp.proto"; import "google/protobuf/duration.proto"; import "proto/drkey/v1/drkey.proto"; +import "proto/daemon/v1/hummingbird.proto"; service DaemonService { // Return a set of paths to the requested destination. @@ -41,6 +42,14 @@ service DaemonService { rpc DRKeyHostAS (DRKeyHostASRequest) returns (DRKeyHostASResponse) {} // DRKeyHostHost returns a key that matches the request. rpc DRKeyHostHost (DRKeyHostHostRequest) returns (DRKeyHostHostResponse) {} + + + rpc StoreFlyovers(StoreFlyoversRequest) returns (StoreFlyoversResponse) {} + rpc ListFlyovers(ListFlyoversRequest) returns (ListFlyoversResponse) {} + + // GetReservations finds paths from source to destination, and then tries to assign flyovers to + // as many hops as possible. + rpc GetReservations(GetReservationsRequest) returns (GetReservationsResponse) {} } message PathsRequest { diff --git a/proto/daemon/v1/hummingbird.proto b/proto/daemon/v1/hummingbird.proto new file mode 100644 index 0000000000..f6f73e222e --- /dev/null +++ b/proto/daemon/v1/hummingbird.proto @@ -0,0 +1,45 @@ +// Copyright 2023 ETH Zurich +// +// 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. + +syntax = "proto3"; + +option go_package = "github.com/scionproto/scion/pkg/proto/daemon"; + +package proto.daemon.v1; + + +message StoreFlyoversRequest {} +message StoreFlyoversResponse {} +message ListFlyoversRequest {} +message ListFlyoversResponse {} + + +message GetReservationsRequest { + // ISD-AS of the source of the path request. + uint64 source_isd_as = 1; + // ISD-AS of the destination of the path request. + uint64 destination_isd_as = 2; + // Choose to fetch fresh paths for this request instead + // of having the server reply from its cache. + bool refresh = 3; +} + +message GetReservationsResponse { + repeated Reservation reservations = 1; +} + +message Reservation { + bytes raw = 1; + double ratio = 2; // ratio flyovers / hops +} From 273162cbe7d3ed241d7d468eeaa3cd19aac9789e Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Fri, 24 Nov 2023 17:27:01 +0100 Subject: [PATCH 065/100] Refactoring. Common types, modified some signatures of hummingbird client. --- daemon/internal/servers/grpc_hummingbird.go | 24 +- .../internal/servers/grpc_hummingbird_test.go | 28 +-- pkg/daemon/grpc_hummingbird.go | 4 +- pkg/hummingbird/BUILD.bazel | 3 +- pkg/hummingbird/flyover.go | 26 --- pkg/hummingbird/hummingbird.go | 140 ++++++------ pkg/hummingbird/hummingbird_test.go | 215 ++++++++++-------- pkg/hummingbird/resevation.go | 2 +- tools/end2end_hbird/main.go | 8 +- 9 files changed, 223 insertions(+), 227 deletions(-) delete mode 100644 pkg/hummingbird/flyover.go diff --git a/daemon/internal/servers/grpc_hummingbird.go b/daemon/internal/servers/grpc_hummingbird.go index 01905be691..4572aaa7e3 100644 --- a/daemon/internal/servers/grpc_hummingbird.go +++ b/daemon/internal/servers/grpc_hummingbird.go @@ -30,7 +30,7 @@ import ( ) type HummingbirdFetcher interface { - ListFlyovers(ctx context.Context, owners []addr.IA) ([]*hummingbird.FlyoverJuanDeleteme, error) + ListFlyovers(ctx context.Context, owners []addr.IA) ([]*hummingbird.Hop, error) } func (s *DaemonServer) StoreFlyovers( @@ -125,10 +125,10 @@ func (s *DaemonServer) getReservations( if err != nil { return nil, err } + mFlyovers := flyoversToMap(flyovers) // For each path, try to assign as many flyovers as possible. reservations := make([]*hummingbird.ReservationJuanDeleteme, len(paths)) - mFlyovers := flyoversToMap(flyovers) for i, p := range paths { flyovers, ratio := assignFlyovers(p.Meta.Interfaces, mFlyovers) reservations[i] = &hummingbird.ReservationJuanDeleteme{ @@ -234,12 +234,12 @@ func linkTypeFromPB(lt sdpb.LinkType) snet.LinkType { // The assumption is that at most one hop field exists per triplet. type flyoverMapKey struct { IA addr.IA - Ingress common.IFIDType - Egress common.IFIDType + Ingress uint16 + Egress uint16 } -type flyoverMap map[flyoverMapKey]*hummingbird.FlyoverJuanDeleteme +type flyoverMap map[flyoverMapKey]*hummingbird.Hop -func flyoversToMap(flyovers []*hummingbird.FlyoverJuanDeleteme) flyoverMap { +func flyoversToMap(flyovers []*hummingbird.Hop) flyoverMap { ret := make(flyoverMap) for _, flyover := range flyovers { k := flyoverMapKey{ @@ -263,16 +263,16 @@ func flyoversToMap(flyovers []*hummingbird.FlyoverJuanDeleteme) flyoverMap { func assignFlyovers( hopSequence []snet.PathInterface, flyovers flyoverMap, -) ([]*hummingbird.FlyoverJuanDeleteme, float64) { +) ([]*hummingbird.Hop, float64) { - ret := make([]*hummingbird.FlyoverJuanDeleteme, len(hopSequence)) + ret := make([]*hummingbird.Hop, len(hopSequence)) flyoverExistsCount := 0 // Do the first flyover appart. k := flyoverMapKey{ IA: hopSequence[0].IA, Ingress: 0, - Egress: hopSequence[0].ID, + Egress: uint16(hopSequence[0].ID), } ret[0] = flyovers[k] if ret[0] != nil { @@ -285,8 +285,8 @@ func assignFlyovers( hop := hopSequence[i] k = flyoverMapKey{ IA: hop.IA, - Ingress: hop.ID, - Egress: hopSequence[i+1].ID, + Ingress: uint16(hop.ID), + Egress: uint16(hopSequence[i+1].ID), } // Find out if there's any flyover we can use. @@ -299,7 +299,7 @@ func assignFlyovers( // Do the last flyover. k = flyoverMapKey{ IA: hopSequence[len(hopSequence)-1].IA, - Ingress: hopSequence[len(hopSequence)-1].ID, + Ingress: uint16(hopSequence[len(hopSequence)-1].ID), Egress: 0, } ret[len(ret)-1] = flyovers[k] diff --git a/daemon/internal/servers/grpc_hummingbird_test.go b/daemon/internal/servers/grpc_hummingbird_test.go index 92a7aa60f9..c4cec5dce9 100644 --- a/daemon/internal/servers/grpc_hummingbird_test.go +++ b/daemon/internal/servers/grpc_hummingbird_test.go @@ -78,7 +78,7 @@ func TestGetReservation(t *testing.T) { ctx, cancelF := context.WithDeadline(context.Background(), deadline) defer cancelF() - flyoverDB := make([]*hummingbird.FlyoverJuanDeleteme, len(tc.flyoverDB)) + flyoverDB := make([]*hummingbird.Hop, len(tc.flyoverDB)) for i, flyoverDesc := range tc.flyoverDB { flyover := getMockFlyovers(t, flyoverDesc...) require.Len(t, flyover, 1, "bad test") @@ -158,19 +158,19 @@ func getMockScionPath(t require.TestingT, hops ...any) *path.Path { return path } -func getMockFlyovers(t require.TestingT, hops ...any) []*hummingbird.FlyoverJuanDeleteme { +func getMockFlyovers(t require.TestingT, hops ...any) []*hummingbird.Hop { // Parse hops argument. - flyovers := make([]*hummingbird.FlyoverJuanDeleteme, 0) + flyovers := make([]*hummingbird.Hop, 0) for i := 0; i < len(hops); i++ { - var f *hummingbird.FlyoverJuanDeleteme + var f *hummingbird.Hop if hops[i] != nil { in := hops[i].(int) ia := xtest.MustParseIA(hops[i+1].(string)) eg := hops[i+2].(int) - f = &hummingbird.FlyoverJuanDeleteme{ + f = &hummingbird.Hop{ IA: ia, - Ingress: common.IFIDType(in), - Egress: common.IFIDType(eg), + Ingress: uint16(in), + Egress: uint16(eg), } i += 2 // advance faster } @@ -179,22 +179,14 @@ func getMockFlyovers(t require.TestingT, hops ...any) []*hummingbird.FlyoverJuan return flyovers } -// func mockFly(t require.TestingT, in int, IA string, eg int) *hummingbird.Flyover { -// return &hummingbird.Flyover{ -// Ingress: hummingbird.Link{IfaceID: uint64(in)}, -// Egress: hummingbird.Link{IfaceID: uint64(eg)}, -// IA: xtest.MustParseIA(IA), -// } -// } - type mockServer struct { - Flyovers []*hummingbird.FlyoverJuanDeleteme + Flyovers []*hummingbird.Hop } func (m *mockServer) ListFlyovers( ctx context.Context, owners []addr.IA, -) ([]*hummingbird.FlyoverJuanDeleteme, error) { +) ([]*hummingbird.Hop, error) { // Create a set of the requested IAs. ownerMap := make(map[addr.IA]struct{}) @@ -203,7 +195,7 @@ func (m *mockServer) ListFlyovers( } // Find any flyover with any such IA and return it. - ret := make([]*hummingbird.FlyoverJuanDeleteme, 0) + ret := make([]*hummingbird.Hop, 0) for _, f := range m.Flyovers { if _, ok := ownerMap[f.IA]; ok { ret = append(ret, f) diff --git a/pkg/daemon/grpc_hummingbird.go b/pkg/daemon/grpc_hummingbird.go index 1030fe594c..512decb874 100644 --- a/pkg/daemon/grpc_hummingbird.go +++ b/pkg/daemon/grpc_hummingbird.go @@ -24,14 +24,14 @@ import ( func (c grpcConn) StoreFlyovers( ctx context.Context, - flyovers []*hummingbird.FlyoverJuanDeleteme, + flyovers []*hummingbird.Hop , ) error { return nil } func (c grpcConn) ListFlyovers(ctx context.Context, -) ([]*hummingbird.FlyoverJuanDeleteme, error) { +) ([]*hummingbird.Hop, error) { return nil, nil } diff --git a/pkg/hummingbird/BUILD.bazel b/pkg/hummingbird/BUILD.bazel index 3cfc18001c..62650d0f4c 100644 --- a/pkg/hummingbird/BUILD.bazel +++ b/pkg/hummingbird/BUILD.bazel @@ -3,7 +3,6 @@ load("//tools/lint:go.bzl", "go_library", "go_test") go_library( name = "go_default_library", srcs = [ - "flyover.go", "hummingbird.go", "resevation.go", ], @@ -12,7 +11,6 @@ go_library( deps = [ "//pkg/addr:go_default_library", "//pkg/log:go_default_library", - "//pkg/private/common:go_default_library", "//pkg/private/serrors:go_default_library", "//pkg/slayers/path/hummingbird:go_default_library", "//pkg/slayers/path/scion:go_default_library", @@ -37,5 +35,6 @@ go_test( "//pkg/snet:go_default_library", "//pkg/snet/path:go_default_library", "@com_github_stretchr_testify//assert:go_default_library", + "@com_github_stretchr_testify//require:go_default_library", ], ) diff --git a/pkg/hummingbird/flyover.go b/pkg/hummingbird/flyover.go deleted file mode 100644 index 841ac56055..0000000000 --- a/pkg/hummingbird/flyover.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2023 ETH Zurich -// -// 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 hummingbird - -import ( - "github.com/scionproto/scion/pkg/addr" - "github.com/scionproto/scion/pkg/private/common" -) - -type FlyoverJuanDeleteme struct { - Ingress common.IFIDType - Egress common.IFIDType - IA addr.IA -} diff --git a/pkg/hummingbird/hummingbird.go b/pkg/hummingbird/hummingbird.go index 0ce7a95b52..49e8d55c68 100644 --- a/pkg/hummingbird/hummingbird.go +++ b/pkg/hummingbird/hummingbird.go @@ -18,9 +18,19 @@ import ( "github.com/scionproto/scion/router/control" ) -type Reservation struct { - // AS denotes the AS for which a reservation is valid - AS addr.IA +// Describes a pair of Ingress and Egress interfaces in a specific AS +type Hop struct { + // IA denotes the IA for which a reservation is valid + IA addr.IA + // Ingress is the ingress interface for the reserved hop + Ingress uint16 + // Egress is the egress interface of the reserved hop + Egress uint16 +} + +type Flyover struct { + Hop + // ResID is the reservation ID of the reservation. It is unique PER AS ResID uint32 // Ak is the authentication key of the reservation @@ -34,45 +44,30 @@ type Reservation struct { // EndTime is the unix timestamp at which the reservation ends. // Is not strictly necessary but included for simplicity EndTime uint32 - // Ingress is the ingress interface for the reserved hop - Ingress uint16 - // Egress is the egress interface of the reserved hop - Egress uint16 -} - -// Describes a pair of Ingress and Egress interfaces in a specific AS -type Hop struct { - AS addr.IA - Ingress uint16 - Egress uint16 } type hbirdHop struct { + Hop + // The FlyoverHopField in the path associated to this hop hopfield *hummingbird.FlyoverHopField // The Index of the Segment the aboe hopfield is part of infIdx int - // The AS this hop traverses - as addr.IA - // The ingress used by packets traversing this hop - ingress uint16 - // The egress used by packets traversing this hop - egress uint16 // The reservations that can be used for this hop. // The reservation at index 0 is the one used to build the path // MUST be non-empty if hopfield.Flyiver == true - reservations []Reservation + reservations []Flyover // The original scion mac of the corresponding hopfield scionMac [6]byte } // Temporary cheating function until the system to request keys is available // return true if successful -func cheat_auth_key(res *Reservation) (Reservation, error) { +func cheat_auth_key(res *Flyover) (Flyover, error) { // ResID is set by seller, pick random res.ResID = uint32(rand.Int31() >> 10) - asstr := res.AS.String() + asstr := res.IA.String() asstr = strings.ReplaceAll(asstr, ":", "_") asstr = strings.TrimLeft(asstr, "1234567890-") fpath := "gen/AS" + asstr + "/keys" @@ -91,12 +86,12 @@ func cheat_auth_key(res *Reservation) (Reservation, error) { // Requests a reservation for each given reservation. // Expects AS, Bw, StartTime, EndTime, Ingress and Egress to be filled in -func RequestReservations(rs []Reservation) { +func RequestReservations(rs []Flyover) { } // Adds a reservation to be used for transmission -func AddReservation(res Reservation) error { +func AddReservation(res Flyover) error { return nil } @@ -136,7 +131,6 @@ func ConvertToHbirdPath(p snet.Path, timeStamp time.Time) (snet.Path, error) { } func convertSCIONToHbirdDecoded(p []byte) (hummingbird.Decoded, error) { - scionDec := scion.Decoded{} if err := scionDec.DecodeFromBytes(p); err != nil { return hummingbird.Decoded{}, err @@ -147,6 +141,8 @@ func convertSCIONToHbirdDecoded(p []byte) (hummingbird.Decoded, error) { return hbirdDec, nil } +// HummingbirdClient represents a possibly partially reserved path, with zero or more flyovers. +// TODO(juagargi) refactor functionality in two types: Reservation and move the rest to sciond. type HummingbirdClient struct { // caches a decoded path for multiple uses dec hummingbird.Decoded @@ -161,30 +157,32 @@ type HummingbirdClient struct { xkbuffer [hummingbird.XkBufferSize]uint32 } +func NewReservation(p snet.Path) (*HummingbirdClient, error) { + c := &HummingbirdClient{} + return c, c.PrepareHbirdPath(p) +} + func (c *HummingbirdClient) parseIAs(ifs []snet.PathInterface) error { - c.hops[0].as = ifs[0].IA - i, j := 1, 1 - for i < len(c.hops) && j < len(ifs) { + c.hops[0].IA = ifs[0].IA + + for i, j := 1, 1; i < len(c.hops) && j < len(ifs); { if ifs[j].IA == ifs[j-1].IA { // Ignore duplicates j++ } else { - c.hops[i].as = ifs[j].IA + c.hops[i].IA = ifs[j].IA i++ j++ } } - if i < len(c.hops) { - return serrors.New("Not enough ASes for this path") - } return nil } // Prepares as hummingbird path and initializes the fields of the hummingbirdClient struct // Returns an array of Hops containing the AS, Ingress and Egress of each hop on the path -func (c *HummingbirdClient) PrepareHbirdPath(p snet.Path) ([]Hop, error) { +func (c *HummingbirdClient) PrepareHbirdPath(p snet.Path) error { if p == nil { - return nil, serrors.New("Empty path") + return serrors.New("Empty path") } c.dec = hummingbird.Decoded{} switch v := p.Dataplane().(type) { @@ -192,15 +190,15 @@ func (c *HummingbirdClient) PrepareHbirdPath(p snet.Path) ([]Hop, error) { // Convert path to decoded hbird path scionDec := scion.Decoded{} if err := scionDec.DecodeFromBytes(v.Raw); err != nil { - return nil, serrors.Join(err, serrors.New("Failed to Prepare Hummingbird Path")) + return serrors.Join(err, serrors.New("Failed to Prepare Hummingbird Path")) } c.dec.ConvertFromScionDecoded(scionDec) case snetpath.Hummingbird: if err := c.dec.DecodeFromBytes(v.Raw); err != nil { - return nil, serrors.Join(err, serrors.New("Failed to Prepare Hummingbird Path")) + return serrors.Join(err, serrors.New("Failed to Prepare Hummingbird Path")) } default: - return nil, serrors.New("Unsupported path type") + return serrors.New("Unsupported path type") } // Initialize a hop for each traversed as c.hops = make([]hbirdHop, len(c.dec.HopFields)) @@ -227,7 +225,7 @@ func (c *HummingbirdClient) PrepareHbirdPath(p snet.Path) ([]Hop, error) { } else { infIdx = 2 if c.dec.InfoFields[2].Peer { - return nil, serrors.New("Invalid path, cannot have 3 segments on peering path") + return serrors.New("Invalid path, cannot have 3 segments on peering path") } if i == int(c.dec.FirstHopPerSeg[1]) && i < len(c.dec.HopFields)-1 { // First hop after Crossover, nothing to be done @@ -239,47 +237,47 @@ func (c *HummingbirdClient) PrepareHbirdPath(p snet.Path) ([]Hop, error) { // Set ingress/egress if xover { if c.dec.InfoFields[infIdx].ConsDir { - c.hops[j].ingress = c.dec.HopFields[i].HopField.ConsIngress + c.hops[j].Ingress = c.dec.HopFields[i].HopField.ConsIngress } else { - c.hops[j].ingress = c.dec.HopFields[i].HopField.ConsEgress + c.hops[j].Ingress = c.dec.HopFields[i].HopField.ConsEgress } if c.dec.InfoFields[infIdx+1].ConsDir { - c.hops[j].egress = c.dec.HopFields[i+1].HopField.ConsEgress + c.hops[j].Egress = c.dec.HopFields[i+1].HopField.ConsEgress } else { - c.hops[j].egress = c.dec.HopFields[i+1].HopField.ConsIngress + c.hops[j].Egress = c.dec.HopFields[i+1].HopField.ConsIngress } } else { if c.dec.InfoFields[infIdx].ConsDir { - c.hops[j].ingress = c.dec.HopFields[i].HopField.ConsIngress - c.hops[j].egress = c.dec.HopFields[i].HopField.ConsEgress + c.hops[j].Ingress = c.dec.HopFields[i].HopField.ConsIngress + c.hops[j].Egress = c.dec.HopFields[i].HopField.ConsEgress } else { - c.hops[j].ingress = c.dec.HopFields[i].HopField.ConsEgress - c.hops[j].egress = c.dec.HopFields[i].HopField.ConsIngress + c.hops[j].Ingress = c.dec.HopFields[i].HopField.ConsEgress + c.hops[j].Egress = c.dec.HopFields[i].HopField.ConsIngress } } // cache scion mac copy(c.hops[j].scionMac[:], c.hops[j].hopfield.HopField.Mac[:]) // Initialiaze reservations - c.hops[j].reservations = make([]Reservation, 0, 2) + c.hops[j].reservations = make([]Flyover, 0, 2) j++ } c.hops = c.hops[:j] // Parse the list of ASes on path if err := c.parseIAs(p.Metadata().Interfaces); err != nil { - return nil, serrors.Join(err, serrors.New("Malformed path")) + return serrors.Join(err, serrors.New("Malformed path")) } - c.dest = c.hops[len(c.hops)-1].as + c.dest = c.hops[len(c.hops)-1].IA - return c.GetPathASes(), nil + return nil } // For each hop in the path, returns a reservation containing the AS, Ingress and Egress of that hop func (c *HummingbirdClient) GetPathASes() []Hop { hops := make([]Hop, len(c.hops)) for i, h := range c.hops { - hops[i].AS = h.as - hops[i].Ingress = h.ingress - hops[i].Egress = h.egress + hops[i].IA = h.IA + hops[i].Ingress = h.Ingress + hops[i].Egress = h.Egress } return hops } @@ -290,12 +288,12 @@ func (c *HummingbirdClient) GetPathASes() []Hop { // duration: The duration of the reservation in seconds // TODO: add async version once we have request api func (c *HummingbirdClient) RequestReservationsAllHops( - bw uint16, start uint32, duration uint16) ([]Reservation, error) { + bw uint16, start uint32, duration uint16) ([]Flyover, error) { hops := make([]Hop, len(c.hops)) for i, h := range c.hops { - hops[i].AS = h.as - hops[i].Ingress = h.ingress - hops[i].Egress = h.egress + hops[i].IA = h.IA + hops[i].Ingress = h.Ingress + hops[i].Egress = h.Egress } return RequestReservationForASes(hops[:], bw, start, duration) @@ -306,10 +304,10 @@ func (c *HummingbirdClient) RequestReservationsAllHops( // (if any) are returned once we have actual requests // TODO: add fully async version of this func RequestReservationForASes( - hops []Hop, bw uint16, start uint32, duration uint16) ([]Reservation, error) { + hops []Hop, bw uint16, start uint32, duration uint16) ([]Flyover, error) { log.Debug("Requesting reservations for", "Hops", hops) - reservations := make([]Reservation, len(hops)) + reservations := make([]Flyover, len(hops)) for i, h := range hops { //TODO: Once we have API for requests // Request (AS, ingress, egress, bw, start, duration) @@ -317,7 +315,7 @@ func RequestReservationForASes( // Temporary Cheating // Current implementation cheats by writing data directly into c.hops instead - reservations[i].AS = h.AS + reservations[i].IA = h.IA reservations[i].Ingress = h.Ingress reservations[i].Egress = h.Egress reservations[i].Bw = bw @@ -334,12 +332,12 @@ func RequestReservationForASes( } // Return all Reservations present in the database -func GetAllReservations() ([]Reservation, error) { +func GetAllReservations() ([]Flyover, error) { return nil, serrors.New("Not Implemented") } // Returns all reservations in the database that can be used for the current path -func (c *HummingbirdClient) GetAvailableReservations() ([]Reservation, error) { +func (c *HummingbirdClient) GetAvailableReservations() ([]Flyover, error) { // Return all reservations in the data base //TODO: return all reservations in the db that fit the path (AS, ingress egress) @@ -349,12 +347,12 @@ func (c *HummingbirdClient) GetAvailableReservations() ([]Reservation, error) { } // Adds the listed reservations to the path -func (c *HummingbirdClient) ApplyReservations(res []Reservation) error { +func (c *HummingbirdClient) ApplyReservations(res []Flyover) error { log.Debug("Applying reservations", "reservations", res) for _, r := range res { for j, h := range c.hops { - if r.AS == h.as { - if r.Ingress == h.ingress && r.Egress == h.egress { + if r.IA == h.IA { + if r.Ingress == h.Ingress && r.Egress == h.Egress { c.hops[j].reservations = append(c.hops[j].reservations, r) if len(c.hops[j].reservations) == 1 { c.hops[j].hopfield.Flyover = true @@ -377,8 +375,8 @@ func (c *HummingbirdClient) ApplyReservations(res []Reservation) error { // Returns all the reservations that the client may currently use // If there are multiple reservations for a hop, // The one currently used is the first appearing in the returned array -func (c *HummingbirdClient) GetUsedReservations() []Reservation { - res := make([]Reservation, 0, len(c.hops)) +func (c *HummingbirdClient) GetUsedReservations() []Flyover { + res := make([]Flyover, 0, len(c.hops)) for _, h := range c.hops { res = append(res, h.reservations...) } @@ -395,7 +393,7 @@ func (c *HummingbirdClient) removeReservation(hopIdx int, resID uint32) { h.hopfield.Flyover = false c.dec.NumLines -= 2 c.dec.PathMeta.SegLen[c.hops[hopIdx].infIdx] -= 2 - h.reservations = []Reservation{} + h.reservations = []Flyover{} } else { copy(h.reservations[:], h.reservations[1:]) h.reservations = h.reservations[:len(h.reservations)-1] @@ -417,10 +415,10 @@ func (c *HummingbirdClient) removeReservation(hopIdx int, resID uint32) { // Removes res from the reservations the client is allowed to use // Reservations are identified based on their AS and ResID // Does NOT check for validity of remaining reservations -func (c *HummingbirdClient) RemoveReservations(res []Reservation) error { +func (c *HummingbirdClient) RemoveReservations(res []Flyover) error { for _, r := range res { for i, h := range c.hops { - if r.AS == h.as { + if r.IA == h.IA { c.removeReservation(i, r.ResID) break } diff --git a/pkg/hummingbird/hummingbird_test.go b/pkg/hummingbird/hummingbird_test.go index 2acb6ca3b8..b474845aed 100644 --- a/pkg/hummingbird/hummingbird_test.go +++ b/pkg/hummingbird/hummingbird_test.go @@ -20,53 +20,62 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/scionproto/scion/pkg/hummingbird" snetpath "github.com/scionproto/scion/pkg/snet/path" ) var testHops = []hummingbird.Hop{ - {AS: 12, Ingress: 0, Egress: 1}, - {AS: 13, Ingress: 2, Egress: 2}, - {AS: 16, Ingress: 1, Egress: 0}, + {IA: 12, Ingress: 0, Egress: 1}, + {IA: 13, Ingress: 2, Egress: 2}, + {IA: 16, Ingress: 1, Egress: 0}, } -var testReservatons = []hummingbird.Reservation{ +var testReservatons = []hummingbird.Flyover{ { - AS: 12, + Hop: hummingbird.Hop{ + IA: 12, + Ingress: 0, + Egress: 1, + }, ResID: 1234, - Ingress: 0, - Egress: 1, Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Bw: 16, Duration: 120, StartTime: uint32(fixedTime.Unix()) - 10, }, { - AS: 13, + Hop: hummingbird.Hop{ + IA: 13, + Ingress: 2, + Egress: 2, + }, ResID: 42, - Ingress: 2, - Egress: 2, Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4, 3, 2, 1, 0}, Bw: 16, Duration: 180, StartTime: uint32(fixedTime.Unix()) - 32, }, { - AS: 16, + Hop: hummingbird.Hop{ + IA: 16, + Ingress: 1, + Egress: 0, + }, ResID: 365, - Ingress: 1, - Egress: 0, Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7}, Bw: 20, Duration: 150, StartTime: uint32(fixedTime.Unix()) - 80, }, { - AS: 16, + Hop: hummingbird.Hop{ + IA: 16, + Ingress: 1, + Egress: 0, + }, ResID: 21, - Ingress: 1, - Egress: 0, Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7}, Bw: 20, Duration: 150, @@ -75,7 +84,6 @@ var testReservatons = []hummingbird.Reservation{ } func TestPrepareHbirdPath(t *testing.T) { - now := time.Now() scionPath, err := getScionSnetPath() @@ -88,14 +96,12 @@ func TestPrepareHbirdPath(t *testing.T) { assert.NoError(t, err) assert.Equal(t, hbirdPath, out) - c := hummingbird.HummingbirdClient{} - scionPath, err = getScionSnetPath() assert.NoError(t, err) - hops, err := c.PrepareHbirdPath(scionPath) - - assert.NoError(t, err) + c, err := hummingbird.NewReservation(scionPath) + require.NoError(t, err) + hops := c.GetPathASes() assert.Equal(t, testHops, hops) scionPath, err = getScionSnetPath() @@ -107,15 +113,12 @@ func TestPrepareHbirdPath(t *testing.T) { } func TestGetPathASes(t *testing.T) { - - c := hummingbird.HummingbirdClient{} - scionPath, err := getScionSnetPath() assert.NoError(t, err) - hops, err := c.PrepareHbirdPath(scionPath) - - assert.NoError(t, err) + c, err := hummingbird.NewReservation(scionPath) + require.NoError(t, err) + hops := c.GetPathASes() assert.Equal(t, testHops, hops) hops = c.GetPathASes() @@ -124,17 +127,15 @@ func TestGetPathASes(t *testing.T) { } func TestApplyReservations(t *testing.T) { - c := hummingbird.HummingbirdClient{} - scionPath, err := getScionSnetPath() assert.NoError(t, err) - _, err = c.PrepareHbirdPath(scionPath) - - assert.NoError(t, err) + c, err := hummingbird.NewReservation(scionPath) + require.NoError(t, err) + hops := c.GetPathASes() + assert.Equal(t, testHops, hops) err = c.ApplyReservations(testReservatons) - assert.NoError(t, err) scionPath, err = getScionSnetPath() @@ -149,12 +150,13 @@ func TestApplyReservations(t *testing.T) { } func TestCheckReservationExpiry(t *testing.T) { - c := hummingbird.HummingbirdClient{} - scionPath, err := getScionSnetPath() assert.NoError(t, err) - _, err = c.PrepareHbirdPath(scionPath) + c, err := hummingbird.NewReservation(scionPath) + require.NoError(t, err) + hops := c.GetPathASes() + assert.Equal(t, testHops, hops) assert.NoError(t, err) @@ -164,103 +166,127 @@ func TestCheckReservationExpiry(t *testing.T) { // hop1: first reservation expired, second ok // hop2: first reservation expired, second not started, third expired, fourth ok // hop3: first not yet valid, second expired - input := []hummingbird.Reservation{ + input := []hummingbird.Flyover{ { - AS: 12, + Hop: hummingbird.Hop{ + IA: 12, + Ingress: 0, + Egress: 1, + }, ResID: 1234, - Ingress: 0, - Egress: 1, Duration: 70, StartTime: now - 80, }, { - AS: 12, + Hop: hummingbird.Hop{ + IA: 12, + Ingress: 0, + Egress: 1, + }, ResID: 34, - Ingress: 0, - Egress: 1, Duration: 560, StartTime: now - 10, }, { - AS: 13, + Hop: hummingbird.Hop{ + IA: 13, + Ingress: 2, + Egress: 2, + }, ResID: 42, - Ingress: 2, - Egress: 2, Duration: 80, StartTime: now - 100, }, { - AS: 13, + Hop: hummingbird.Hop{ + IA: 13, + Ingress: 2, + Egress: 2, + }, ResID: 31, - Ingress: 2, - Egress: 2, Duration: 389, StartTime: now + 50, }, { - AS: 13, + Hop: hummingbird.Hop{ + IA: 13, + Ingress: 2, + Egress: 2, + }, ResID: 12, - Ingress: 2, - Egress: 2, Duration: 64, StartTime: now - 60, }, { - AS: 13, + Hop: hummingbird.Hop{ + IA: 13, + Ingress: 2, + Egress: 2, + }, ResID: 5, - Ingress: 2, - Egress: 2, Duration: 180, StartTime: now - 30, }, { - AS: 16, + Hop: hummingbird.Hop{ + IA: 16, + Ingress: 1, + Egress: 0, + }, ResID: 365, - Ingress: 1, - Egress: 0, Duration: 150, StartTime: now + 60, }, { - AS: 16, + Hop: hummingbird.Hop{ + IA: 16, + Ingress: 1, + Egress: 0, + }, ResID: 345, - Ingress: 1, - Egress: 0, Duration: 150, StartTime: now - 345, }, } - expected := []hummingbird.Reservation{ + expected := []hummingbird.Flyover{ { - AS: 12, + Hop: hummingbird.Hop{ + IA: 12, + Ingress: 0, + Egress: 1, + }, ResID: 34, - Ingress: 0, - Egress: 1, Duration: 560, StartTime: now - 10, }, { - AS: 13, + Hop: hummingbird.Hop{ + IA: 13, + Ingress: 2, + Egress: 2, + }, ResID: 5, - Ingress: 2, - Egress: 2, Duration: 180, StartTime: now - 30, }, { - AS: 13, + Hop: hummingbird.Hop{ + IA: 13, + Ingress: 2, + Egress: 2, + }, ResID: 31, - Ingress: 2, - Egress: 2, Duration: 389, StartTime: now + 50, }, { - AS: 16, + Hop: hummingbird.Hop{ + IA: 16, + Ingress: 1, + Egress: 0, + }, ResID: 365, - Ingress: 1, - Egress: 0, Duration: 150, StartTime: now + 60, }, @@ -293,49 +319,56 @@ func TestCheckReservationExpiry(t *testing.T) { } func TestRemoveReservations(t *testing.T) { - c := hummingbird.HummingbirdClient{} - scionPath, err := getScionSnetPath() assert.NoError(t, err) - _, err = c.PrepareHbirdPath(scionPath) - - assert.NoError(t, err) + c, err := hummingbird.NewReservation(scionPath) + require.NoError(t, err) err = c.ApplyReservations(testReservatons) assert.NoError(t, err) - remove := []hummingbird.Reservation{ + remove := []hummingbird.Flyover{ { - AS: 12, + Hop: hummingbird.Hop{ + IA: 12, + }, ResID: 1234, }, { - AS: 13, + Hop: hummingbird.Hop{ + IA: 13, + }, ResID: 53, }, { - AS: 16, + Hop: hummingbird.Hop{ + IA: 16, + }, ResID: 365, }, } - expected := []hummingbird.Reservation{ + expected := []hummingbird.Flyover{ { - AS: 13, + Hop: hummingbird.Hop{ + IA: 13, + Ingress: 2, + Egress: 2, + }, ResID: 42, - Ingress: 2, - Egress: 2, Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4, 3, 2, 1, 0}, Bw: 16, Duration: 180, StartTime: uint32(fixedTime.Unix()) - 32, }, { - AS: 16, + Hop: hummingbird.Hop{ + IA: 16, + Ingress: 1, + Egress: 0, + }, ResID: 21, - Ingress: 1, - Egress: 0, Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7}, Bw: 20, Duration: 150, diff --git a/pkg/hummingbird/resevation.go b/pkg/hummingbird/resevation.go index 813e8642cf..b720b65842 100644 --- a/pkg/hummingbird/resevation.go +++ b/pkg/hummingbird/resevation.go @@ -18,7 +18,7 @@ import "github.com/scionproto/scion/pkg/snet/path" type ReservationJuanDeleteme struct { SCIONPath path.Path - Flyovers []*FlyoverJuanDeleteme + Flyovers []*Hop Ratio float64 // flyover/hops ratio } diff --git a/tools/end2end_hbird/main.go b/tools/end2end_hbird/main.go index 09bcce7760..3ee78c633f 100644 --- a/tools/end2end_hbird/main.go +++ b/tools/end2end_hbird/main.go @@ -313,8 +313,8 @@ func (c *client) attemptRequest(n int) bool { remote.Path = path.Dataplane() } else if !partial { // full path with reservations - hbirdClient := hummingbird.HummingbirdClient{} - if _, err := hbirdClient.PrepareHbirdPath(path); err != nil { + hbirdClient, err := hummingbird.NewReservation(path) + if err != nil { logger.Error("Error converting path to Hummingbird", "err", err) return false } @@ -336,8 +336,8 @@ func (c *client) attemptRequest(n int) bool { remote.Path = path.Dataplane() } else { //partial reservations, alternating resrved and not reserved - hbirdClient := hummingbird.HummingbirdClient{} - if _, err := hbirdClient.PrepareHbirdPath(path); err != nil { + hbirdClient, err := hummingbird.NewReservation(path) + if err != nil { logger.Error("Error converting path to Hummingbird", "err", err) return false } From d16e8113c99392426ef535c5997d803fb99076f4 Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Wed, 13 Dec 2023 12:39:41 +0100 Subject: [PATCH 066/100] Modified how clients build a hbird path. --- daemon/internal/servers/grpc_hummingbird.go | 19 +- .../internal/servers/grpc_hummingbird_test.go | 16 +- pkg/daemon/grpc_hummingbird.go | 4 +- pkg/hummingbird/BUILD.bazel | 1 + pkg/hummingbird/hummingbird.go | 416 +-------------- pkg/hummingbird/hummingbird_test.go | 146 ++---- pkg/hummingbird/resevation.go | 482 +++++++++++++++++- pkg/hummingbird/utils_test.go | 89 ++-- tools/end2end_hbird/main.go | 25 +- 9 files changed, 588 insertions(+), 610 deletions(-) diff --git a/daemon/internal/servers/grpc_hummingbird.go b/daemon/internal/servers/grpc_hummingbird.go index 4572aaa7e3..0f69d9a076 100644 --- a/daemon/internal/servers/grpc_hummingbird.go +++ b/daemon/internal/servers/grpc_hummingbird.go @@ -30,7 +30,7 @@ import ( ) type HummingbirdFetcher interface { - ListFlyovers(ctx context.Context, owners []addr.IA) ([]*hummingbird.Hop, error) + ListFlyovers(ctx context.Context, owners []addr.IA) ([]*hummingbird.BaseHop, error) } func (s *DaemonServer) StoreFlyovers( @@ -237,9 +237,9 @@ type flyoverMapKey struct { Ingress uint16 Egress uint16 } -type flyoverMap map[flyoverMapKey]*hummingbird.Hop +type flyoverMap map[flyoverMapKey]*hummingbird.BaseHop -func flyoversToMap(flyovers []*hummingbird.Hop) flyoverMap { +func flyoversToMap(flyovers []*hummingbird.BaseHop) flyoverMap { ret := make(flyoverMap) for _, flyover := range flyovers { k := flyoverMapKey{ @@ -252,7 +252,7 @@ func flyoversToMap(flyovers []*hummingbird.Hop) flyoverMap { return ret } -// assignFlyovers assigns as many flyovers as possible to a path. +// assignFlyovers assigns as flyovers to as many hops of a path as possible. // The first returned value is a slice of flyovers, with the same length as the hop sequence, // and when not nil, it points to a Flyover that can be used in the hop of that specific index. // As SCION hops appear twice per ingress/egress pairs (with the exception of the first and @@ -263,12 +263,12 @@ func flyoversToMap(flyovers []*hummingbird.Hop) flyoverMap { func assignFlyovers( hopSequence []snet.PathInterface, flyovers flyoverMap, -) ([]*hummingbird.Hop, float64) { +) ([]*hummingbird.BaseHop, float64) { - ret := make([]*hummingbird.Hop, len(hopSequence)) + ret := make([]*hummingbird.BaseHop, len(hopSequence)) flyoverExistsCount := 0 - // Do the first flyover appart. + // Do the first flyover apart. k := flyoverMapKey{ IA: hopSequence[0].IA, Ingress: 0, @@ -309,8 +309,3 @@ func assignFlyovers( return ret, float64(flyoverExistsCount) / float64(len(hopSequence)/2) } - -// func assignRemainingFlyovers( -// hopSequence []snet.PathInterface, -// flyovers flyoverMap, -// ) [] diff --git a/daemon/internal/servers/grpc_hummingbird_test.go b/daemon/internal/servers/grpc_hummingbird_test.go index c4cec5dce9..b1c6a499ad 100644 --- a/daemon/internal/servers/grpc_hummingbird_test.go +++ b/daemon/internal/servers/grpc_hummingbird_test.go @@ -78,7 +78,7 @@ func TestGetReservation(t *testing.T) { ctx, cancelF := context.WithDeadline(context.Background(), deadline) defer cancelF() - flyoverDB := make([]*hummingbird.Hop, len(tc.flyoverDB)) + flyoverDB := make([]*hummingbird.BaseHop, len(tc.flyoverDB)) for i, flyoverDesc := range tc.flyoverDB { flyover := getMockFlyovers(t, flyoverDesc...) require.Len(t, flyover, 1, "bad test") @@ -158,16 +158,16 @@ func getMockScionPath(t require.TestingT, hops ...any) *path.Path { return path } -func getMockFlyovers(t require.TestingT, hops ...any) []*hummingbird.Hop { +func getMockFlyovers(t require.TestingT, hops ...any) []*hummingbird.BaseHop { // Parse hops argument. - flyovers := make([]*hummingbird.Hop, 0) + flyovers := make([]*hummingbird.BaseHop, 0) for i := 0; i < len(hops); i++ { - var f *hummingbird.Hop + var f *hummingbird.BaseHop if hops[i] != nil { in := hops[i].(int) ia := xtest.MustParseIA(hops[i+1].(string)) eg := hops[i+2].(int) - f = &hummingbird.Hop{ + f = &hummingbird.BaseHop{ IA: ia, Ingress: uint16(in), Egress: uint16(eg), @@ -180,13 +180,13 @@ func getMockFlyovers(t require.TestingT, hops ...any) []*hummingbird.Hop { } type mockServer struct { - Flyovers []*hummingbird.Hop + Flyovers []*hummingbird.BaseHop } func (m *mockServer) ListFlyovers( ctx context.Context, owners []addr.IA, -) ([]*hummingbird.Hop, error) { +) ([]*hummingbird.BaseHop, error) { // Create a set of the requested IAs. ownerMap := make(map[addr.IA]struct{}) @@ -195,7 +195,7 @@ func (m *mockServer) ListFlyovers( } // Find any flyover with any such IA and return it. - ret := make([]*hummingbird.Hop, 0) + ret := make([]*hummingbird.BaseHop, 0) for _, f := range m.Flyovers { if _, ok := ownerMap[f.IA]; ok { ret = append(ret, f) diff --git a/pkg/daemon/grpc_hummingbird.go b/pkg/daemon/grpc_hummingbird.go index 512decb874..a62ec51799 100644 --- a/pkg/daemon/grpc_hummingbird.go +++ b/pkg/daemon/grpc_hummingbird.go @@ -24,14 +24,14 @@ import ( func (c grpcConn) StoreFlyovers( ctx context.Context, - flyovers []*hummingbird.Hop , + flyovers []*hummingbird.BaseHop, ) error { return nil } func (c grpcConn) ListFlyovers(ctx context.Context, -) ([]*hummingbird.Hop, error) { +) ([]*hummingbird.BaseHop, error) { return nil, nil } diff --git a/pkg/hummingbird/BUILD.bazel b/pkg/hummingbird/BUILD.bazel index 62650d0f4c..d7bc0587e3 100644 --- a/pkg/hummingbird/BUILD.bazel +++ b/pkg/hummingbird/BUILD.bazel @@ -12,6 +12,7 @@ go_library( "//pkg/addr:go_default_library", "//pkg/log:go_default_library", "//pkg/private/serrors:go_default_library", + "//pkg/slayers/path:go_default_library", "//pkg/slayers/path/hummingbird:go_default_library", "//pkg/slayers/path/scion:go_default_library", "//pkg/snet:go_default_library", diff --git a/pkg/hummingbird/hummingbird.go b/pkg/hummingbird/hummingbird.go index 49e8d55c68..d7b7b33c97 100644 --- a/pkg/hummingbird/hummingbird.go +++ b/pkg/hummingbird/hummingbird.go @@ -2,13 +2,11 @@ package hummingbird import ( "crypto/aes" - "encoding/binary" "math/rand" "strings" "time" "github.com/scionproto/scion/pkg/addr" - "github.com/scionproto/scion/pkg/log" "github.com/scionproto/scion/pkg/private/serrors" "github.com/scionproto/scion/pkg/slayers/path/hummingbird" "github.com/scionproto/scion/pkg/slayers/path/scion" @@ -19,7 +17,7 @@ import ( ) // Describes a pair of Ingress and Egress interfaces in a specific AS -type Hop struct { +type BaseHop struct { // IA denotes the IA for which a reservation is valid IA addr.IA // Ingress is the ingress interface for the reserved hop @@ -29,7 +27,7 @@ type Hop struct { } type Flyover struct { - Hop + BaseHop // ResID is the reservation ID of the reservation. It is unique PER AS ResID uint32 @@ -46,21 +44,6 @@ type Flyover struct { EndTime uint32 } -type hbirdHop struct { - Hop - - // The FlyoverHopField in the path associated to this hop - hopfield *hummingbird.FlyoverHopField - // The Index of the Segment the aboe hopfield is part of - infIdx int - // The reservations that can be used for this hop. - // The reservation at index 0 is the one used to build the path - // MUST be non-empty if hopfield.Flyiver == true - reservations []Flyover - // The original scion mac of the corresponding hopfield - scionMac [6]byte -} - // Temporary cheating function until the system to request keys is available // return true if successful func cheat_auth_key(res *Flyover) (Flyover, error) { @@ -140,398 +123,3 @@ func convertSCIONToHbirdDecoded(p []byte) (hummingbird.Decoded, error) { hbirdDec.ConvertFromScionDecoded(scionDec) return hbirdDec, nil } - -// HummingbirdClient represents a possibly partially reserved path, with zero or more flyovers. -// TODO(juagargi) refactor functionality in two types: Reservation and move the rest to sciond. -type HummingbirdClient struct { - // caches a decoded path for multiple uses - dec hummingbird.Decoded - // Destination of the path - dest addr.IA - // The hops for which it is possible to add reservations - hops []hbirdHop - // counter for duplicate detection - counter uint32 - // buffers for computing Vk - byteBuffer [hummingbird.FlyoverMacBufferSize]byte - xkbuffer [hummingbird.XkBufferSize]uint32 -} - -func NewReservation(p snet.Path) (*HummingbirdClient, error) { - c := &HummingbirdClient{} - return c, c.PrepareHbirdPath(p) -} - -func (c *HummingbirdClient) parseIAs(ifs []snet.PathInterface) error { - c.hops[0].IA = ifs[0].IA - - for i, j := 1, 1; i < len(c.hops) && j < len(ifs); { - if ifs[j].IA == ifs[j-1].IA { - // Ignore duplicates - j++ - } else { - c.hops[i].IA = ifs[j].IA - i++ - j++ - } - } - return nil -} - -// Prepares as hummingbird path and initializes the fields of the hummingbirdClient struct -// Returns an array of Hops containing the AS, Ingress and Egress of each hop on the path -func (c *HummingbirdClient) PrepareHbirdPath(p snet.Path) error { - if p == nil { - return serrors.New("Empty path") - } - c.dec = hummingbird.Decoded{} - switch v := p.Dataplane().(type) { - case snetpath.SCION: - // Convert path to decoded hbird path - scionDec := scion.Decoded{} - if err := scionDec.DecodeFromBytes(v.Raw); err != nil { - return serrors.Join(err, serrors.New("Failed to Prepare Hummingbird Path")) - } - c.dec.ConvertFromScionDecoded(scionDec) - case snetpath.Hummingbird: - if err := c.dec.DecodeFromBytes(v.Raw); err != nil { - return serrors.Join(err, serrors.New("Failed to Prepare Hummingbird Path")) - } - default: - return serrors.New("Unsupported path type") - } - // Initialize a hop for each traversed as - c.hops = make([]hbirdHop, len(c.dec.HopFields)) - j := 0 - for i := 0; i < len(c.dec.HopFields); i++ { - var xover bool - var infIdx int - if i < int(c.dec.FirstHopPerSeg[0]) { - infIdx = 0 - if !c.dec.InfoFields[0].Peer { - xover = (i == int(c.dec.FirstHopPerSeg[0])-1) && - i < len(c.dec.HopFields)-1 - } - } else if i < int(c.dec.FirstHopPerSeg[1]) { - infIdx = 1 - if !c.dec.InfoFields[1].Peer { - if i == int(c.dec.FirstHopPerSeg[0]) { - // First hop after Crossover, nothing to be done - continue - } - xover = i == int(c.dec.FirstHopPerSeg[1])-1 && - i < len(c.dec.HopFields)-1 - } - } else { - infIdx = 2 - if c.dec.InfoFields[2].Peer { - return serrors.New("Invalid path, cannot have 3 segments on peering path") - } - if i == int(c.dec.FirstHopPerSeg[1]) && i < len(c.dec.HopFields)-1 { - // First hop after Crossover, nothing to be done - continue - } - } - c.hops[j].infIdx = infIdx - c.hops[j].hopfield = &c.dec.HopFields[i] - // Set ingress/egress - if xover { - if c.dec.InfoFields[infIdx].ConsDir { - c.hops[j].Ingress = c.dec.HopFields[i].HopField.ConsIngress - } else { - c.hops[j].Ingress = c.dec.HopFields[i].HopField.ConsEgress - } - if c.dec.InfoFields[infIdx+1].ConsDir { - c.hops[j].Egress = c.dec.HopFields[i+1].HopField.ConsEgress - } else { - c.hops[j].Egress = c.dec.HopFields[i+1].HopField.ConsIngress - } - } else { - if c.dec.InfoFields[infIdx].ConsDir { - c.hops[j].Ingress = c.dec.HopFields[i].HopField.ConsIngress - c.hops[j].Egress = c.dec.HopFields[i].HopField.ConsEgress - } else { - c.hops[j].Ingress = c.dec.HopFields[i].HopField.ConsEgress - c.hops[j].Egress = c.dec.HopFields[i].HopField.ConsIngress - } - } - // cache scion mac - copy(c.hops[j].scionMac[:], c.hops[j].hopfield.HopField.Mac[:]) - // Initialiaze reservations - c.hops[j].reservations = make([]Flyover, 0, 2) - j++ - } - c.hops = c.hops[:j] - // Parse the list of ASes on path - if err := c.parseIAs(p.Metadata().Interfaces); err != nil { - return serrors.Join(err, serrors.New("Malformed path")) - } - c.dest = c.hops[len(c.hops)-1].IA - - return nil -} - -// For each hop in the path, returns a reservation containing the AS, Ingress and Egress of that hop -func (c *HummingbirdClient) GetPathASes() []Hop { - hops := make([]Hop, len(c.hops)) - for i, h := range c.hops { - hops[i].IA = h.IA - hops[i].Ingress = h.Ingress - hops[i].Egress = h.Egress - } - return hops -} - -// Request reservations for the full path -// bw: the bandwidth to request -// start: The start time of the reservation, in unix seconds -// duration: The duration of the reservation in seconds -// TODO: add async version once we have request api -func (c *HummingbirdClient) RequestReservationsAllHops( - bw uint16, start uint32, duration uint16) ([]Flyover, error) { - hops := make([]Hop, len(c.hops)) - for i, h := range c.hops { - hops[i].IA = h.IA - hops[i].Ingress = h.Ingress - hops[i].Egress = h.Egress - } - - return RequestReservationForASes(hops[:], bw, start, duration) -} - -// Requests new reservations for the listed Hops and returns them once they are obtained -// TODO: add timeout after which already received reservations -// (if any) are returned once we have actual requests -// TODO: add fully async version of this -func RequestReservationForASes( - hops []Hop, bw uint16, start uint32, duration uint16) ([]Flyover, error) { - - log.Debug("Requesting reservations for", "Hops", hops) - reservations := make([]Flyover, len(hops)) - for i, h := range hops { - //TODO: Once we have API for requests - // Request (AS, ingress, egress, bw, start, duration) - - // Temporary Cheating - // Current implementation cheats by writing data directly into c.hops instead - - reservations[i].IA = h.IA - reservations[i].Ingress = h.Ingress - reservations[i].Egress = h.Egress - reservations[i].Bw = bw - reservations[i].StartTime = start - reservations[i].Duration = duration - - var err error - reservations[i], err = cheat_auth_key(&reservations[i]) - if err != nil { - return nil, err - } - } - return reservations, nil -} - -// Return all Reservations present in the database -func GetAllReservations() ([]Flyover, error) { - return nil, serrors.New("Not Implemented") -} - -// Returns all reservations in the database that can be used for the current path -func (c *HummingbirdClient) GetAvailableReservations() ([]Flyover, error) { - // Return all reservations in the data base - //TODO: return all reservations in the db that fit the path (AS, ingress egress) - - //TODO: incorporate into integration test once we have async version of previous functions - - return nil, serrors.New("Not Implemented") -} - -// Adds the listed reservations to the path -func (c *HummingbirdClient) ApplyReservations(res []Flyover) error { - log.Debug("Applying reservations", "reservations", res) - for _, r := range res { - for j, h := range c.hops { - if r.IA == h.IA { - if r.Ingress == h.Ingress && r.Egress == h.Egress { - c.hops[j].reservations = append(c.hops[j].reservations, r) - if len(c.hops[j].reservations) == 1 { - c.hops[j].hopfield.Flyover = true - c.dec.NumLines += 2 - c.dec.PathMeta.SegLen[h.infIdx] += 2 - c.hops[j].hopfield.Bw = r.Bw - c.hops[j].hopfield.Duration = r.Duration - c.hops[j].hopfield.ResID = r.ResID - } - } else { - // TODO: inform caller that this reservation cannot be set on this path - break - } - } - } - } - return nil -} - -// Returns all the reservations that the client may currently use -// If there are multiple reservations for a hop, -// The one currently used is the first appearing in the returned array -func (c *HummingbirdClient) GetUsedReservations() []Flyover { - res := make([]Flyover, 0, len(c.hops)) - for _, h := range c.hops { - res = append(res, h.reservations...) - } - return res -} - -// Removes the reservation with the given resID from a hop -func (c *HummingbirdClient) removeReservation(hopIdx int, resID uint32) { - h := &c.hops[hopIdx] - for i, r := range h.reservations { - if r.ResID == resID { - if i == 0 { - if len(h.reservations) == 1 { - h.hopfield.Flyover = false - c.dec.NumLines -= 2 - c.dec.PathMeta.SegLen[c.hops[hopIdx].infIdx] -= 2 - h.reservations = []Flyover{} - } else { - copy(h.reservations[:], h.reservations[1:]) - h.reservations = h.reservations[:len(h.reservations)-1] - h.hopfield.Bw = h.reservations[0].Bw - h.hopfield.ResID = h.reservations[0].ResID - h.hopfield.Duration = h.reservations[0].Duration - } - } else { - if i < len(h.reservations)-1 { - copy(h.reservations[i:], h.reservations[i+1:]) - } - h.reservations = h.reservations[:len(h.reservations)-1] - } - break - } - } -} - -// Removes res from the reservations the client is allowed to use -// Reservations are identified based on their AS and ResID -// Does NOT check for validity of remaining reservations -func (c *HummingbirdClient) RemoveReservations(res []Flyover) error { - for _, r := range res { - for i, h := range c.hops { - if r.IA == h.IA { - c.removeReservation(i, r.ResID) - break - } - } - } - return nil -} - -// Checks whether any current reservation that has expired or will expire in t seconds -// If yes, remove reservation from list of used reservations -func (c *HummingbirdClient) CheckExpiry(t uint32) { - now := uint32(time.Now().Unix()) - for i := range c.hops { - - // Remove expired reservations - for j := 0; j < len(c.hops[i].reservations); { - if c.hops[i].reservations[j].StartTime+uint32(c.hops[i].reservations[j].Duration) < - (now + t) { - copy(c.hops[i].reservations[j:], c.hops[i].reservations[j+1:]) - c.hops[i].reservations = c.hops[i].reservations[:len(c.hops[i].reservations)-1] - } else { - j++ - } - } - - if len(c.hops[i].reservations) == 0 { - if c.hops[i].hopfield.Flyover { - c.hops[i].hopfield.Flyover = false - c.dec.NumLines -= 2 - c.dec.PathMeta.SegLen[c.hops[i].infIdx] -= 2 - } - continue - } - // If there's any currently valid reservation, put it to the front - if !(c.hops[i].reservations[0].StartTime <= now) { - for j := 1; j < len(c.hops[i].reservations); j++ { - if c.hops[i].reservations[j].StartTime <= now { - temp := c.hops[i].reservations[0] - c.hops[i].reservations[0] = c.hops[i].reservations[j] - c.hops[i].reservations[j] = temp - break - } - } - } - - // Check whether reservation at the front is currently valid - if c.hops[i].reservations[0].StartTime <= now { - if !c.hops[i].hopfield.Flyover { - c.hops[i].hopfield.Flyover = true - c.dec.NumLines += 2 - c.dec.PathMeta.SegLen[c.hops[i].infIdx] += 2 - } - c.hops[i].hopfield.Bw = c.hops[i].reservations[0].Bw - c.hops[i].hopfield.Duration = c.hops[i].reservations[0].Duration - c.hops[i].hopfield.ResID = c.hops[i].reservations[0].ResID - } else { - if c.hops[i].hopfield.Flyover { - c.hops[i].hopfield.Flyover = false - c.dec.NumLines -= 2 - c.dec.PathMeta.SegLen[c.hops[i].infIdx] -= 2 - } - } - } -} - -// Sets pathmeta timestamps and increments duplicate detection counter. -// Updates MACs of all flyoverfields -// replaces the dataplane of the input snet.path with the finished hummingbird path -func (c *HummingbirdClient) FinalizePath(p snet.Path, pktLen uint16, - timeStamp time.Time) (snet.Path, error) { - if p == nil { - return nil, serrors.New("snet path is nil") - } - var dphb snetpath.Hummingbird - - // Update timestamps - secs := uint32(timeStamp.Unix()) - millis := uint32(timeStamp.Nanosecond()/1000) << 22 - millis |= c.counter - c.dec.Base.PathMeta.BaseTS = secs - c.dec.Base.PathMeta.HighResTS = millis - //increment counter for next packet - if c.counter >= 1<<22-1 { - c.counter = 0 - } else { - c.counter += 1 - } - // compute Macs for Flyovers - for _, h := range c.hops { - if !h.hopfield.Flyover { - continue - } - res := h.reservations[0] - h.hopfield.ResStartTime = uint16(secs - res.StartTime) - flyovermac := hummingbird.FullFlyoverMac(res.Ak[:], c.dest, pktLen, - h.hopfield.ResStartTime, millis, c.byteBuffer[:], c.xkbuffer[:]) - - binary.BigEndian.PutUint32(h.hopfield.HopField.Mac[:4], - binary.BigEndian.Uint32(flyovermac[:4])^binary.BigEndian.Uint32(h.scionMac[:4])) - binary.BigEndian.PutUint16(h.hopfield.HopField.Mac[4:], - binary.BigEndian.Uint16(flyovermac[4:])^binary.BigEndian.Uint16(h.scionMac[4:])) - } - dphb.Raw = make([]byte, c.dec.Len()) - if err := c.dec.SerializeTo(dphb.Raw); err != nil { - return nil, err - } - switch v := p.(type) { - case snetpath.Path: - v.DataplanePath = dphb - p = v - default: - return nil, serrors.New("Unsupported snet path struct", "path", p) - } - - return p, nil -} diff --git a/pkg/hummingbird/hummingbird_test.go b/pkg/hummingbird/hummingbird_test.go index b474845aed..0ac0a3eaf5 100644 --- a/pkg/hummingbird/hummingbird_test.go +++ b/pkg/hummingbird/hummingbird_test.go @@ -26,19 +26,27 @@ import ( snetpath "github.com/scionproto/scion/pkg/snet/path" ) -var testHops = []hummingbird.Hop{ - {IA: 12, Ingress: 0, Egress: 1}, - {IA: 13, Ingress: 2, Egress: 2}, - {IA: 16, Ingress: 1, Egress: 0}, +var testHops = []hummingbird.BaseHop{ + { + IA: interfacesTest[0].IA, + Ingress: 0, + Egress: 1, + }, + { + IA: 13, + Ingress: 2, + Egress: 4, + }, + { + IA: interfacesTest[len(interfacesTest)-1].IA, + Ingress: 5, + Egress: 0, + }, } -var testReservatons = []hummingbird.Flyover{ +var testFlyovers = []hummingbird.Flyover{ { - Hop: hummingbird.Hop{ - IA: 12, - Ingress: 0, - Egress: 1, - }, + BaseHop: testHops[0], ResID: 1234, Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Bw: 16, @@ -46,11 +54,7 @@ var testReservatons = []hummingbird.Flyover{ StartTime: uint32(fixedTime.Unix()) - 10, }, { - Hop: hummingbird.Hop{ - IA: 13, - Ingress: 2, - Egress: 2, - }, + BaseHop: testHops[1], ResID: 42, Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4, 3, 2, 1, 0}, Bw: 16, @@ -58,11 +62,7 @@ var testReservatons = []hummingbird.Flyover{ StartTime: uint32(fixedTime.Unix()) - 32, }, { - Hop: hummingbird.Hop{ - IA: 16, - Ingress: 1, - Egress: 0, - }, + BaseHop: testHops[2], ResID: 365, Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7}, Bw: 20, @@ -70,11 +70,7 @@ var testReservatons = []hummingbird.Flyover{ StartTime: uint32(fixedTime.Unix()) - 80, }, { - Hop: hummingbird.Hop{ - IA: 16, - Ingress: 1, - Egress: 0, - }, + BaseHop: testHops[2], ResID: 21, Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7}, Bw: 20, @@ -135,7 +131,7 @@ func TestApplyReservations(t *testing.T) { hops := c.GetPathASes() assert.Equal(t, testHops, hops) - err = c.ApplyReservations(testReservatons) + err = c.ApplyReservations(testFlyovers) assert.NoError(t, err) scionPath, err = getScionSnetPath() @@ -168,81 +164,49 @@ func TestCheckReservationExpiry(t *testing.T) { // hop3: first not yet valid, second expired input := []hummingbird.Flyover{ { - Hop: hummingbird.Hop{ - IA: 12, - Ingress: 0, - Egress: 1, - }, + BaseHop: testHops[0], ResID: 1234, Duration: 70, StartTime: now - 80, }, { - Hop: hummingbird.Hop{ - IA: 12, - Ingress: 0, - Egress: 1, - }, + BaseHop: testHops[0], ResID: 34, Duration: 560, StartTime: now - 10, }, { - Hop: hummingbird.Hop{ - IA: 13, - Ingress: 2, - Egress: 2, - }, + BaseHop: testHops[1], ResID: 42, Duration: 80, StartTime: now - 100, }, { - Hop: hummingbird.Hop{ - IA: 13, - Ingress: 2, - Egress: 2, - }, + BaseHop: testHops[1], ResID: 31, Duration: 389, StartTime: now + 50, }, { - Hop: hummingbird.Hop{ - IA: 13, - Ingress: 2, - Egress: 2, - }, + BaseHop: testHops[1], ResID: 12, Duration: 64, StartTime: now - 60, }, { - Hop: hummingbird.Hop{ - IA: 13, - Ingress: 2, - Egress: 2, - }, + BaseHop: testHops[1], ResID: 5, Duration: 180, StartTime: now - 30, }, { - Hop: hummingbird.Hop{ - IA: 16, - Ingress: 1, - Egress: 0, - }, + BaseHop: testHops[2], ResID: 365, Duration: 150, StartTime: now + 60, }, { - Hop: hummingbird.Hop{ - IA: 16, - Ingress: 1, - Egress: 0, - }, + BaseHop: testHops[2], ResID: 345, Duration: 150, StartTime: now - 345, @@ -251,41 +215,25 @@ func TestCheckReservationExpiry(t *testing.T) { expected := []hummingbird.Flyover{ { - Hop: hummingbird.Hop{ - IA: 12, - Ingress: 0, - Egress: 1, - }, + BaseHop: testHops[0], ResID: 34, Duration: 560, StartTime: now - 10, }, { - Hop: hummingbird.Hop{ - IA: 13, - Ingress: 2, - Egress: 2, - }, + BaseHop: testHops[1], ResID: 5, Duration: 180, StartTime: now - 30, }, { - Hop: hummingbird.Hop{ - IA: 13, - Ingress: 2, - Egress: 2, - }, + BaseHop: testHops[1], ResID: 31, Duration: 389, StartTime: now + 50, }, { - Hop: hummingbird.Hop{ - IA: 16, - Ingress: 1, - Egress: 0, - }, + BaseHop: testHops[2], ResID: 365, Duration: 150, StartTime: now + 60, @@ -325,25 +273,25 @@ func TestRemoveReservations(t *testing.T) { c, err := hummingbird.NewReservation(scionPath) require.NoError(t, err) - err = c.ApplyReservations(testReservatons) + err = c.ApplyReservations(testFlyovers) assert.NoError(t, err) remove := []hummingbird.Flyover{ { - Hop: hummingbird.Hop{ - IA: 12, + BaseHop: hummingbird.BaseHop{ + IA: testHops[0].IA, }, ResID: 1234, }, { - Hop: hummingbird.Hop{ - IA: 13, + BaseHop: hummingbird.BaseHop{ + IA: testHops[1].IA, }, ResID: 53, }, { - Hop: hummingbird.Hop{ - IA: 16, + BaseHop: hummingbird.BaseHop{ + IA: testHops[2].IA, }, ResID: 365, }, @@ -351,11 +299,7 @@ func TestRemoveReservations(t *testing.T) { expected := []hummingbird.Flyover{ { - Hop: hummingbird.Hop{ - IA: 13, - Ingress: 2, - Egress: 2, - }, + BaseHop: testHops[1], ResID: 42, Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4, 3, 2, 1, 0}, Bw: 16, @@ -363,11 +307,7 @@ func TestRemoveReservations(t *testing.T) { StartTime: uint32(fixedTime.Unix()) - 32, }, { - Hop: hummingbird.Hop{ - IA: 16, - Ingress: 1, - Egress: 0, - }, + BaseHop: testHops[2], ResID: 21, Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7}, Bw: 20, diff --git a/pkg/hummingbird/resevation.go b/pkg/hummingbird/resevation.go index b720b65842..8181953486 100644 --- a/pkg/hummingbird/resevation.go +++ b/pkg/hummingbird/resevation.go @@ -14,11 +14,22 @@ package hummingbird -import "github.com/scionproto/scion/pkg/snet/path" +import ( + "encoding/binary" + "time" + + "github.com/scionproto/scion/pkg/addr" + "github.com/scionproto/scion/pkg/log" + "github.com/scionproto/scion/pkg/private/serrors" + "github.com/scionproto/scion/pkg/slayers/path/hummingbird" + "github.com/scionproto/scion/pkg/slayers/path/scion" + "github.com/scionproto/scion/pkg/snet" + "github.com/scionproto/scion/pkg/snet/path" +) type ReservationJuanDeleteme struct { SCIONPath path.Path - Flyovers []*Hop + Flyovers []*BaseHop Ratio float64 // flyover/hops ratio } @@ -34,3 +45,470 @@ func (r ReservationJuanDeleteme) FlyoverCount() int { func (r ReservationJuanDeleteme) LessThan(other *ReservationJuanDeleteme) bool { return r.Ratio < other.Ratio } + +// Reservation represents a possibly partially reserved path, with zero or more flyovers. +// TODO(juagargi) refactor functionality in two types: Reservation and move the rest to sciond. +type Reservation struct { + dec hummingbird.Decoded // caches a decoded path for multiple uses + hops []Hop // possible flyovers + counter uint32 // duplicate detection counter + byteBuffer [hummingbird.FlyoverMacBufferSize]byte + xkbuffer [hummingbird.XkBufferSize]uint32 +} + +type Hop struct { + BaseHop + + // The FlyoverHopField in the path associated to this hop. + hopfield *hummingbird.FlyoverHopField + // The Index of the Segment the above hopfield is part of. + infIdx int + // The reservations that can be used for this hop. + // The reservation at index 0 is the one used to build the path + // MUST be non-empty if hopfield.Flyiver == true + reservations []Flyover + // The original scion mac of the corresponding hopfield. + scionMac [6]byte +} + +func NewReservation(p snet.Path) (*Reservation, error) { + c := &Reservation{} + return c, c.prepareHbirdPath(p) +} + +// prepareHbirdPathOlD prepares as hummingbird path and initializes the Resevation object. +func (c *Reservation) prepareHbirdPathOlD(p snet.Path) error { + if p == nil { + return serrors.New("Empty path") + } + c.dec = hummingbird.Decoded{} + switch v := p.Dataplane().(type) { + case path.SCION: + // Convert path to decoded hbird path + scionDec := scion.Decoded{} + if err := scionDec.DecodeFromBytes(v.Raw); err != nil { + return serrors.Join(err, serrors.New("Failed to Prepare Hummingbird Path")) + } + c.dec.ConvertFromScionDecoded(scionDec) + case path.Hummingbird: + if err := c.dec.DecodeFromBytes(v.Raw); err != nil { + return serrors.Join(err, serrors.New("Failed to Prepare Hummingbird Path")) + } + default: + return serrors.New("Unsupported path type") + } + + // Initialize a hop for each traversed AS. + infIdx := 0 + for i := 0; i < len(c.dec.HopFields); i++ { + // Determine the segment index and whether the hop is a cross over or not. + var xover bool + for ; infIdx < 2; infIdx++ { + if i < int(c.dec.FirstHopPerSeg[infIdx]) { + if !c.dec.InfoFields[infIdx].Peer { + xover = (i == int(c.dec.FirstHopPerSeg[infIdx])-1) && + i < len(c.dec.HopFields)-1 + } + break + } + } + + // If not first segment, check if this hop is the first one after a xover. + if infIdx > 0 && + !c.dec.InfoFields[infIdx].Peer && + i == int(c.dec.FirstHopPerSeg[infIdx-1]) && + i < len(c.dec.HopFields)-1 { + + // First hop after Crossover, nothing to be done. + continue + } + + // Setting egress: if crossing over then egress ID comes from the next hop in next segment. + offset := 0 + if xover { + offset = 1 + } + // We use the path metadata to get the IA from it. This sequence of interfaces does not + // include the egress-to-ingress crossed over in the core AS. + pathInterfaces := p.Metadata().Interfaces + + c.hops = append(c.hops, Hop{ + BaseHop: BaseHop{ + // To get the current PathInterface we use `len(c.hops)` instead of `i` because + // we want to skip those crossed-over interfaces. + IA: pathInterfaces[hopIndexToPathInterfaceIndex(len(c.hops))].IA, + Ingress: getIfaceID(c.dec, infIdx, i, false), + Egress: getIfaceID(c.dec, infIdx+offset, i+offset, true), + }, + infIdx: infIdx, + hopfield: &c.dec.HopFields[i], + scionMac: c.dec.HopFields[i].HopField.Mac, + reservations: make([]Flyover, 0, 2), + }) + } + + return nil +} + +func (c *Reservation) prepareHbirdPath(p snet.Path) error { + switch p := p.Dataplane().(type) { + case path.SCION: + scion := scion.Decoded{} + if err := scion.DecodeFromBytes(p.Raw); err != nil { + return serrors.Join(err, serrors.New("Failed to Prepare Hummingbird Path")) + } + c.dec = hummingbird.Decoded{} + c.dec.ConvertFromScionDecoded(scion) + default: + return serrors.New("Unsupported path type") + } + + // We use the path metadata to get the IA from it. This sequence of interfaces does not + // include the egress-to-ingress crossed over interfaces in the core AS. + pathInterfaces := p.Metadata().Interfaces + + c.hops = append(c.hops, newHop( + pathInterfaces[0].IA, + 0, + uint16(pathInterfaces[0].ID), + 0, + &c.dec.HopFields[0], + )) + + // The dataplane path in c.dec contains inf fields and cross-over hops. + // Do each segment at a time to ignore the first hop of every segment except the first. + hopIdx := 1 // the index of the current hop in the dataplane. + for infIdx := 0; infIdx < c.dec.NumINF; infIdx, hopIdx = infIdx+1, hopIdx+1 { + for i := 1; i < int( + c.dec.Base.PathMeta.SegLen[infIdx])/hummingbird.HopLines; i, hopIdx = i+1, hopIdx+1 { + + c.hops = append(c.hops, newHop( + pathInterfaces[len(c.hops)*2-1].IA, + uint16(pathInterfaces[len(c.hops)*2-1].ID), + egressID(pathInterfaces, len(c.hops)), + infIdx, + &c.dec.HopFields[hopIdx], + )) + } + } + return nil +} + +func iaID(ifaces []snet.PathInterface, idx int) addr.IA { + i := idx*2 - 1 + if i < 0 { + i = 0 + } + return ifaces[i].IA +} + +func ingressID(ifaces []snet.PathInterface, idx int) uint16 { + i := idx*2 - 1 + if i < 0 { + return 0 + } + return uint16(ifaces[i].ID) +} + +func egressID(ifaces []snet.PathInterface, idx int) uint16 { + i := idx * 2 + if i >= len(ifaces) { + return 0 + } + return uint16(ifaces[i].ID) +} + +// For each hop in the path, returns a reservation containing the AS, Ingress and Egress of that hop +func (c *Reservation) GetPathASes() []BaseHop { + hops := make([]BaseHop, len(c.hops)) + for i, h := range c.hops { + hops[i].IA = h.IA + hops[i].Ingress = h.Ingress + hops[i].Egress = h.Egress + } + return hops +} + +func (c *Reservation) Destination() addr.IA { + return c.hops[len(c.hops)-1].IA +} + +// Request reservations for the full path +// bw: the bandwidth to request +// start: The start time of the reservation, in unix seconds +// duration: The duration of the reservation in seconds +// TODO: add async version once we have request api +func (c *Reservation) RequestReservationsAllHops( + bw uint16, start uint32, duration uint16) ([]Flyover, error) { + hops := make([]BaseHop, len(c.hops)) + for i, h := range c.hops { + hops[i].IA = h.IA + hops[i].Ingress = h.Ingress + hops[i].Egress = h.Egress + } + + return RequestReservationForASes(hops, bw, start, duration) +} + +// Requests new reservations for the listed Hops and returns them once they are obtained +// TODO: add timeout after which already received reservations +// (if any) are returned once we have actual requests +// TODO: add fully async version of this +func RequestReservationForASes( + hops []BaseHop, bw uint16, start uint32, duration uint16) ([]Flyover, error) { + + log.Debug("Requesting reservations for", "Hops", hops) + reservations := make([]Flyover, len(hops)) + for i, h := range hops { + //TODO: Once we have API for requests + // Request (AS, ingress, egress, bw, start, duration) + + // Temporary Cheating + // Current implementation cheats by writing data directly into c.hops instead + + reservations[i].IA = h.IA + reservations[i].Ingress = h.Ingress + reservations[i].Egress = h.Egress + reservations[i].Bw = bw + reservations[i].StartTime = start + reservations[i].Duration = duration + + var err error + reservations[i], err = cheat_auth_key(&reservations[i]) + if err != nil { + return nil, err + } + } + return reservations, nil +} + +// Adds the listed reservations to the path +func (c *Reservation) ApplyReservations(res []Flyover) error { + log.Debug("Applying reservations", "reservations", res) + for _, r := range res { + for j, h := range c.hops { + if r.IA == h.IA { + if r.Ingress == h.Ingress && r.Egress == h.Egress { + c.hops[j].reservations = append(c.hops[j].reservations, r) + if len(c.hops[j].reservations) == 1 { + c.hops[j].hopfield.Flyover = true + c.dec.NumLines += 2 + c.dec.PathMeta.SegLen[h.infIdx] += 2 + c.hops[j].hopfield.Bw = r.Bw + c.hops[j].hopfield.Duration = r.Duration + c.hops[j].hopfield.ResID = r.ResID + } + } else { + // TODO: inform caller that this reservation cannot be set on this path + break + } + } + } + } + return nil +} + +// Returns all the reservations that the client may currently use +// If there are multiple reservations for a hop, +// The one currently used is the first appearing in the returned array +func (c *Reservation) GetUsedReservations() []Flyover { + res := make([]Flyover, 0, len(c.hops)) + for _, h := range c.hops { + res = append(res, h.reservations...) + } + return res +} + +// Removes the reservation with the given resID from a hop +func (c *Reservation) removeReservation(hopIdx int, resID uint32) { + h := &c.hops[hopIdx] + for i, r := range h.reservations { + if r.ResID == resID { + if i == 0 { + if len(h.reservations) == 1 { + h.hopfield.Flyover = false + c.dec.NumLines -= 2 + c.dec.PathMeta.SegLen[c.hops[hopIdx].infIdx] -= 2 + h.reservations = []Flyover{} + } else { + copy(h.reservations[:], h.reservations[1:]) + h.reservations = h.reservations[:len(h.reservations)-1] + h.hopfield.Bw = h.reservations[0].Bw + h.hopfield.ResID = h.reservations[0].ResID + h.hopfield.Duration = h.reservations[0].Duration + } + } else { + if i < len(h.reservations)-1 { + copy(h.reservations[i:], h.reservations[i+1:]) + } + h.reservations = h.reservations[:len(h.reservations)-1] + } + break + } + } +} + +// Removes res from the reservations the client is allowed to use +// Reservations are identified based on their AS and ResID +// Does NOT check for validity of remaining reservations +func (c *Reservation) RemoveReservations(res []Flyover) error { + for _, r := range res { + for i, h := range c.hops { + if r.IA == h.IA { + c.removeReservation(i, r.ResID) + break + } + } + } + return nil +} + +// Checks whether any current reservation that has expired or will expire in t seconds +// If yes, remove reservation from list of used reservations +func (c *Reservation) CheckExpiry(t uint32) { + now := uint32(time.Now().Unix()) + for i := range c.hops { + + // Remove expired reservations + for j := 0; j < len(c.hops[i].reservations); { + if c.hops[i].reservations[j].StartTime+uint32(c.hops[i].reservations[j].Duration) < + (now + t) { + copy(c.hops[i].reservations[j:], c.hops[i].reservations[j+1:]) + c.hops[i].reservations = c.hops[i].reservations[:len(c.hops[i].reservations)-1] + } else { + j++ + } + } + + if len(c.hops[i].reservations) == 0 { + if c.hops[i].hopfield.Flyover { + c.hops[i].hopfield.Flyover = false + c.dec.NumLines -= 2 + c.dec.PathMeta.SegLen[c.hops[i].infIdx] -= 2 + } + continue + } + // If there's any currently valid reservation, put it to the front + if !(c.hops[i].reservations[0].StartTime <= now) { + for j := 1; j < len(c.hops[i].reservations); j++ { + if c.hops[i].reservations[j].StartTime <= now { + temp := c.hops[i].reservations[0] + c.hops[i].reservations[0] = c.hops[i].reservations[j] + c.hops[i].reservations[j] = temp + break + } + } + } + + // Check whether reservation at the front is currently valid + if c.hops[i].reservations[0].StartTime <= now { + if !c.hops[i].hopfield.Flyover { + c.hops[i].hopfield.Flyover = true + c.dec.NumLines += 2 + c.dec.PathMeta.SegLen[c.hops[i].infIdx] += 2 + } + c.hops[i].hopfield.Bw = c.hops[i].reservations[0].Bw + c.hops[i].hopfield.Duration = c.hops[i].reservations[0].Duration + c.hops[i].hopfield.ResID = c.hops[i].reservations[0].ResID + } else { + if c.hops[i].hopfield.Flyover { + c.hops[i].hopfield.Flyover = false + c.dec.NumLines -= 2 + c.dec.PathMeta.SegLen[c.hops[i].infIdx] -= 2 + } + } + } +} + +// Sets pathmeta timestamps and increments duplicate detection counter. +// Updates MACs of all flyoverfields +// replaces the dataplane of the input snet.path with the finished hummingbird path +func (c *Reservation) FinalizePath(p snet.Path, pktLen uint16, + timeStamp time.Time) (snet.Path, error) { + if p == nil { + return nil, serrors.New("snet path is nil") + } + var dphb path.Hummingbird + + // Update timestamps + secs := uint32(timeStamp.Unix()) + millis := uint32(timeStamp.Nanosecond()/1000) << 22 + millis |= c.counter + c.dec.Base.PathMeta.BaseTS = secs + c.dec.Base.PathMeta.HighResTS = millis + //increment counter for next packet + if c.counter >= 1<<22-1 { + c.counter = 0 + } else { + c.counter += 1 + } + // compute Macs for Flyovers + for _, h := range c.hops { + if !h.hopfield.Flyover { + continue + } + res := h.reservations[0] + h.hopfield.ResStartTime = uint16(secs - res.StartTime) + flyovermac := hummingbird.FullFlyoverMac(res.Ak[:], c.Destination(), pktLen, + h.hopfield.ResStartTime, millis, c.byteBuffer[:], c.xkbuffer[:]) + + binary.BigEndian.PutUint32(h.hopfield.HopField.Mac[:4], + binary.BigEndian.Uint32(flyovermac[:4])^binary.BigEndian.Uint32(h.scionMac[:4])) + binary.BigEndian.PutUint16(h.hopfield.HopField.Mac[4:], + binary.BigEndian.Uint16(flyovermac[4:])^binary.BigEndian.Uint16(h.scionMac[4:])) + } + dphb.Raw = make([]byte, c.dec.Len()) + if err := c.dec.SerializeTo(dphb.Raw); err != nil { + return nil, err + } + switch v := p.(type) { + case path.Path: + v.DataplanePath = dphb + p = v + default: + return nil, serrors.New("Unsupported snet path struct", "path", p) + } + + return p, nil +} + +func newHop(ia addr.IA, in, eg uint16, infIdx int, hf *hummingbird.FlyoverHopField) Hop { + return Hop{ + BaseHop: BaseHop{ + IA: ia, + Ingress: in, + Egress: eg, + }, + infIdx: infIdx, + hopfield: hf, + scionMac: hf.HopField.Mac, + reservations: make([]Flyover, 0, 2), + } +} + +func getIfaceID(dec hummingbird.Decoded, segIdx, hopIdx int, returnEgress bool) uint16 { + in := dec.HopFields[hopIdx].HopField.ConsIngress + eg := dec.HopFields[hopIdx].HopField.ConsEgress + if !dec.InfoFields[segIdx].ConsDir { + returnEgress = !returnEgress + } + if returnEgress { + in = eg + } + return in +} + +// hopIndexToPathInterfaceIndex converts an index of a hummingbird hop into its corresponding +// index in the PathInterface sequence (found e.g. in snet.Path.Metadata()). +// This is not straightforward, as the PathInterface sequence omits the starting interface +// (its ID. is always 0 and same IA as next one), and ending interface (its ID is always 0 and +// the same IA as the previous one). +// Example: if we have four hummingbird hops, indexed as. 0,1,2,3, their corresponding indices +// in a PathInterface sequence would be 0,1,3,5. +func hopIndexToPathInterfaceIndex(i int) int { + idx := i*2 - 1 + if idx < 0 { + idx = 0 + } + return idx +} diff --git a/pkg/hummingbird/utils_test.go b/pkg/hummingbird/utils_test.go index e151eacad7..b9ee8fa41b 100644 --- a/pkg/hummingbird/utils_test.go +++ b/pkg/hummingbird/utils_test.go @@ -43,12 +43,12 @@ var testHopFields = []path.HopField{ { ExpTime: 63, ConsIngress: 0, - ConsEgress: 2, + ConsEgress: 4, Mac: [path.MacLen]byte{1, 2, 3, 4, 5, 6}, }, { ExpTime: 63, - ConsIngress: 1, + ConsIngress: 5, ConsEgress: 0, Mac: [path.MacLen]byte{1, 2, 3, 4, 5, 6}, }, @@ -119,6 +119,25 @@ var decodedTestPath = &scion.Decoded{ HopFields: testHopFields, } +var interfacesTest = []snet.PathInterface{ + { + IA: 12, + ID: 1, + }, + { + IA: 13, + ID: 2, + }, + { + IA: 13, + ID: 4, + }, + { + IA: 14, + ID: 5, + }, +} + var decodedHbirdTestPath = &hummingbird.Decoded{ Base: hummingbird.Base{ PathMeta: hummingbird.MetaHdr{ @@ -158,26 +177,13 @@ func getRawScionPath(d scion.Decoded) ([]byte, error) { func getScionSnetPath() (snetpath.Path, error) { rawScion, err := getRawScionPath(*decodedTestPath) p := snetpath.Path{ - Src: 12, - Dst: 16, + Src: interfacesTest[0].IA, + Dst: interfacesTest[len(interfacesTest)-1].IA, DataplanePath: snetpath.SCION{ Raw: rawScion, }, Meta: snet.PathMetadata{ - Interfaces: []snet.PathInterface{ - { - IA: 12, - }, - { - IA: 13, - }, - { - IA: 13, - }, - { - IA: 16, - }, - }, + Interfaces: interfacesTest, }, } return p, err @@ -198,26 +204,13 @@ func getHbirdNoFlyoversSnetPath(t time.Time) (snetpath.Path, error) { rawHbird, err := getRawHbirdPath(decoded) p := snetpath.Path{ - Src: 12, - Dst: 16, + Src: interfacesTest[0].IA, + Dst: interfacesTest[len(interfacesTest)-1].IA, DataplanePath: snetpath.Hummingbird{ Raw: rawHbird, }, Meta: snet.PathMetadata{ - Interfaces: []snet.PathInterface{ - { - IA: 12, - }, - { - IA: 13, - }, - { - IA: 13, - }, - { - IA: 16, - }, - }, + Interfaces: interfacesTest, }, } return p, err @@ -236,15 +229,18 @@ func getHbirdFlyoversSnetPath(t time.Time) (snetpath.Path, error) { macBuffer2 := make([]byte, hummingbird.FlyoverMacBufferSize) ak0 := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} - flyover0 := hummingbird.FullFlyoverMac(ak0, 16, 16, decoded.HopFields[0].ResStartTime, + flyover0 := hummingbird.FullFlyoverMac(ak0, interfacesTest[len(interfacesTest)-1].IA, + 16, decoded.HopFields[0].ResStartTime, millis, macBuffer0, xkBuffer) ak1 := []byte{0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4, 3, 2, 1, 0} - flyover1 := hummingbird.FullFlyoverMac(ak1, 16, 16, decoded.HopFields[1].ResStartTime, + flyover1 := hummingbird.FullFlyoverMac(ak1, interfacesTest[len(interfacesTest)-1].IA, + 16, decoded.HopFields[1].ResStartTime, millis, macBuffer1, xkBuffer) ak2 := []byte{0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7} - flyover2 := hummingbird.FullFlyoverMac(ak2, 16, 16, decoded.HopFields[3].ResStartTime, + flyover2 := hummingbird.FullFlyoverMac(ak2, interfacesTest[len(interfacesTest)-1].IA, + 16, decoded.HopFields[3].ResStartTime, millis, macBuffer2, xkBuffer) for i := 0; i < 6; i++ { @@ -255,26 +251,13 @@ func getHbirdFlyoversSnetPath(t time.Time) (snetpath.Path, error) { rawHbird, err := getRawHbirdPath(decoded) p := snetpath.Path{ - Src: 12, - Dst: 16, + Src: interfacesTest[0].IA, + Dst: interfacesTest[len(interfacesTest)-1].IA, DataplanePath: snetpath.Hummingbird{ Raw: rawHbird, }, Meta: snet.PathMetadata{ - Interfaces: []snet.PathInterface{ - { - IA: 12, - }, - { - IA: 13, - }, - { - IA: 13, - }, - { - IA: 16, - }, - }, + Interfaces: interfacesTest, }, } return p, err diff --git a/tools/end2end_hbird/main.go b/tools/end2end_hbird/main.go index 3ee78c633f..ed143484d9 100644 --- a/tools/end2end_hbird/main.go +++ b/tools/end2end_hbird/main.go @@ -71,7 +71,6 @@ var ( timeout = &util.DurWrap{Duration: 10 * time.Second} scionPacketConnMetrics = metrics.NewSCIONPacketConnMetrics() scmpErrorsCounter = scionPacketConnMetrics.SCMPErrors - epic bool flyovers bool partial bool ) @@ -108,7 +107,6 @@ func realMain() int { func addFlags() { flag.Var(&remote, "remote", "(Mandatory for clients) address to connect to") flag.Var(timeout, "timeout", "The timeout for each attempt") - flag.BoolVar(&epic, "epic", false, "Enable EPIC.") flag.BoolVar(&flyovers, "flyovers", true, "Enable Flyovers") flag.BoolVar(&partial, "partial", false, "If true, only subset of hops have flyovers") } @@ -303,6 +301,13 @@ func (c *client) attemptRequest(n int) bool { defer span.Finish() // Convert path to Hummingbird path if path != nil { + + // TODO: request flyovers for existing path(s). This is done by the user asynchrously + + // TODO: insert flyovers obtained from Mysten's library. + + // TODO: + if !flyovers { // Standard path, no reservations at all path, err = hummingbird.ConvertToHbirdPath(path, time.Now()) @@ -438,6 +443,7 @@ func (c *client) ping(ctx context.Context, n int, path snet.Path) error { } func (c *client) getRemote(ctx context.Context, n int) (snet.Path, error) { + // TODO: this function should return all possible paths. if remote.IA.Equal(integration.Local.IA) { remote.Path = snetpath.Empty{} return nil, nil @@ -475,20 +481,7 @@ func (c *client) getRemote(ctx context.Context, n int) (snet.Path, error) { } // Extract forwarding path from the SCION Daemon response. - // If the epic flag is set, try to use the EPIC path type header. - if epic { - scionPath, ok := path.Dataplane().(snetpath.SCION) - if !ok { - return nil, serrors.New("provided path must be of type scion") - } - epicPath, err := snetpath.NewEPICDataplanePath(scionPath, path.Metadata().EpicAuths) - if err != nil { - return nil, err - } - remote.Path = epicPath - } else { - remote.Path = path.Dataplane() - } + remote.Path = path.Dataplane() remote.NextHop = path.UnderlayNextHop() return path, nil } From 86ca23296c05eb13c86fb67ad3b1d1320dcfc3d7 Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Wed, 13 Dec 2023 12:46:48 +0100 Subject: [PATCH 067/100] Cleanup, add comments. --- pkg/hummingbird/resevation.go | 19 +++++++++++-------- tools/end2end_hbird/main.go | 2 +- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/pkg/hummingbird/resevation.go b/pkg/hummingbird/resevation.go index 8181953486..81691e3872 100644 --- a/pkg/hummingbird/resevation.go +++ b/pkg/hummingbird/resevation.go @@ -210,14 +210,6 @@ func ingressID(ifaces []snet.PathInterface, idx int) uint16 { return uint16(ifaces[i].ID) } -func egressID(ifaces []snet.PathInterface, idx int) uint16 { - i := idx * 2 - if i >= len(ifaces) { - return 0 - } - return uint16(ifaces[i].ID) -} - // For each hop in the path, returns a reservation containing the AS, Ingress and Egress of that hop func (c *Reservation) GetPathASes() []BaseHop { hops := make([]BaseHop, len(c.hops)) @@ -512,3 +504,14 @@ func hopIndexToPathInterfaceIndex(i int) int { } return idx } + +// egressID returns the egress ID from a sequence of IDs (such as that in the metadata field +// of a snet.Path) given its index. If index is past the length of the sequence, the egress +// ID 0 is returning, meaning egress ID is that last AS. +func egressID(ifaces []snet.PathInterface, idx int) uint16 { + i := idx * 2 + if i >= len(ifaces) { + return 0 + } + return uint16(ifaces[i].ID) +} diff --git a/tools/end2end_hbird/main.go b/tools/end2end_hbird/main.go index ed143484d9..8c874a988c 100644 --- a/tools/end2end_hbird/main.go +++ b/tools/end2end_hbird/main.go @@ -302,7 +302,7 @@ func (c *client) attemptRequest(n int) bool { // Convert path to Hummingbird path if path != nil { - // TODO: request flyovers for existing path(s). This is done by the user asynchrously + // TODO: buy flyovers for existing path(s). This is done by the user asynchronously // TODO: insert flyovers obtained from Mysten's library. From fe5f48b3ed23a5ae8e4ae47726ab1fa608e920a0 Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Wed, 13 Dec 2023 16:57:07 +0100 Subject: [PATCH 068/100] Cleanup. --- pkg/hummingbird/hummingbird_test.go | 68 ++++++++++++++--------------- pkg/hummingbird/resevation.go | 46 ++++++++++++++----- tools/end2end_hbird/main.go | 2 +- 3 files changed, 68 insertions(+), 48 deletions(-) diff --git a/pkg/hummingbird/hummingbird_test.go b/pkg/hummingbird/hummingbird_test.go index 0ac0a3eaf5..22731f0fdd 100644 --- a/pkg/hummingbird/hummingbird_test.go +++ b/pkg/hummingbird/hummingbird_test.go @@ -22,6 +22,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/scionproto/scion/pkg/addr" "github.com/scionproto/scion/pkg/hummingbird" snetpath "github.com/scionproto/scion/pkg/snet/path" ) @@ -33,7 +34,7 @@ var testHops = []hummingbird.BaseHop{ Egress: 1, }, { - IA: 13, + IA: interfacesTest[1].IA, Ingress: 2, Egress: 4, }, @@ -79,60 +80,48 @@ var testFlyovers = []hummingbird.Flyover{ }, } -func TestPrepareHbirdPath(t *testing.T) { - now := time.Now() - +func TestConvertToHbirdPath(t *testing.T) { scionPath, err := getScionSnetPath() assert.NoError(t, err) - hbirdPath, err := getHbirdNoFlyoversSnetPath(now) + now := time.Now() + expectecPath, err := getHbirdNoFlyoversSnetPath(now) assert.NoError(t, err) out, err := hummingbird.ConvertToHbirdPath(scionPath, now) assert.NoError(t, err) - assert.Equal(t, hbirdPath, out) + assert.Equal(t, expectecPath, out) +} - scionPath, err = getScionSnetPath() +// deleteme remove this test +func TestPrepareHbirdPath(t *testing.T) { + scionPath, err := getScionSnetPath() assert.NoError(t, err) - c, err := hummingbird.NewReservation(scionPath) - require.NoError(t, err) - hops := c.GetPathASes() - assert.Equal(t, testHops, hops) + now := time.Now() + flyovers := flyoverSliceToMap(testFlyovers) + flyovers = nil + c, err := hummingbird.NewReservation(scionPath, flyovers) + require.NoError(t, err) + err = c.ApplyReservations(nil) + assert.NoError(t, err) scionPath, err = getScionSnetPath() assert.NoError(t, err) - output, err := c.FinalizePath(scionPath, 0, now) assert.NoError(t, err) - assert.Equal(t, hbirdPath, output) -} - -func TestGetPathASes(t *testing.T) { - scionPath, err := getScionSnetPath() + expectecPath, err := getHbirdNoFlyoversSnetPath(now) assert.NoError(t, err) - - c, err := hummingbird.NewReservation(scionPath) - require.NoError(t, err) - hops := c.GetPathASes() - assert.Equal(t, testHops, hops) - - hops = c.GetPathASes() - - assert.Equal(t, testHops, hops) + assert.Equal(t, expectecPath, output) } +// deleteme rename this test as TestPrepareHbirdPath func TestApplyReservations(t *testing.T) { scionPath, err := getScionSnetPath() assert.NoError(t, err) - c, err := hummingbird.NewReservation(scionPath) + c, err := hummingbird.NewReservation(scionPath, flyoverSliceToMap(testFlyovers)) require.NoError(t, err) - hops := c.GetPathASes() - assert.Equal(t, testHops, hops) - - err = c.ApplyReservations(testFlyovers) - assert.NoError(t, err) scionPath, err = getScionSnetPath() assert.NoError(t, err) @@ -149,10 +138,8 @@ func TestCheckReservationExpiry(t *testing.T) { scionPath, err := getScionSnetPath() assert.NoError(t, err) - c, err := hummingbird.NewReservation(scionPath) + c, err := hummingbird.NewReservation(scionPath, nil) require.NoError(t, err) - hops := c.GetPathASes() - assert.Equal(t, testHops, hops) assert.NoError(t, err) @@ -270,7 +257,7 @@ func TestRemoveReservations(t *testing.T) { scionPath, err := getScionSnetPath() assert.NoError(t, err) - c, err := hummingbird.NewReservation(scionPath) + c, err := hummingbird.NewReservation(scionPath, nil) require.NoError(t, err) err = c.ApplyReservations(testFlyovers) @@ -338,3 +325,12 @@ func TestRemoveReservations(t *testing.T) { assert.False(t, dec.HopFields[2].Flyover) assert.True(t, dec.HopFields[3].Flyover) } + +func flyoverSliceToMap(flyovers []hummingbird.Flyover) map[addr.IA][]*hummingbird.Flyover { + m := make(map[addr.IA][]*hummingbird.Flyover) + for _, flyover := range flyovers { + clone := flyover + m[clone.IA] = append(m[clone.IA], &clone) + } + return m +} diff --git a/pkg/hummingbird/resevation.go b/pkg/hummingbird/resevation.go index 81691e3872..3e33b1a1c9 100644 --- a/pkg/hummingbird/resevation.go +++ b/pkg/hummingbird/resevation.go @@ -71,9 +71,15 @@ type Hop struct { scionMac [6]byte } -func NewReservation(p snet.Path) (*Reservation, error) { +func NewReservation(p snet.Path, flyovers map[addr.IA][]*Flyover) (*Reservation, error) { + // deleteme + // func NewReservation(p snet.Path) (*Reservation, error) { c := &Reservation{} - return c, c.prepareHbirdPath(p) + err := c.prepareHbirdPath(p) + if err == nil { + c.applyFlyovers(flyovers) + } + return c, err } // prepareHbirdPathOlD prepares as hummingbird path and initializes the Resevation object. @@ -275,20 +281,20 @@ func RequestReservationForASes( } // Adds the listed reservations to the path -func (c *Reservation) ApplyReservations(res []Flyover) error { - log.Debug("Applying reservations", "reservations", res) - for _, r := range res { +func (c *Reservation) ApplyReservations(flyovers []Flyover) error { + log.Debug("Applying reservations", "reservations", flyovers) + for _, f := range flyovers { for j, h := range c.hops { - if r.IA == h.IA { - if r.Ingress == h.Ingress && r.Egress == h.Egress { - c.hops[j].reservations = append(c.hops[j].reservations, r) + if f.IA == h.IA { + if f.Ingress == h.Ingress && f.Egress == h.Egress { + c.hops[j].reservations = append(c.hops[j].reservations, f) if len(c.hops[j].reservations) == 1 { c.hops[j].hopfield.Flyover = true c.dec.NumLines += 2 c.dec.PathMeta.SegLen[h.infIdx] += 2 - c.hops[j].hopfield.Bw = r.Bw - c.hops[j].hopfield.Duration = r.Duration - c.hops[j].hopfield.ResID = r.ResID + c.hops[j].hopfield.Bw = f.Bw + c.hops[j].hopfield.Duration = f.Duration + c.hops[j].hopfield.ResID = f.ResID } } else { // TODO: inform caller that this reservation cannot be set on this path @@ -300,6 +306,24 @@ func (c *Reservation) ApplyReservations(res []Flyover) error { return nil } +func (c *Reservation) applyFlyovers(flyovers map[addr.IA][]*Flyover) { + for i, h := range c.hops { + flyovers := flyovers[h.IA] + for _, flyover := range flyovers { + if flyover.Ingress == h.Ingress && flyover.Egress == h.Egress { + c.hops[i].reservations = append(c.hops[i].reservations, *flyover) + c.hops[i].hopfield.Flyover = true + c.dec.NumLines += 2 + c.dec.PathMeta.SegLen[h.infIdx] += 2 + c.hops[i].hopfield.Bw = flyover.Bw + c.hops[i].hopfield.Duration = flyover.Duration + c.hops[i].hopfield.ResID = flyover.ResID + break + } + } + } +} + // Returns all the reservations that the client may currently use // If there are multiple reservations for a hop, // The one currently used is the first appearing in the returned array diff --git a/tools/end2end_hbird/main.go b/tools/end2end_hbird/main.go index 8c874a988c..a3061b8197 100644 --- a/tools/end2end_hbird/main.go +++ b/tools/end2end_hbird/main.go @@ -306,7 +306,7 @@ func (c *client) attemptRequest(n int) bool { // TODO: insert flyovers obtained from Mysten's library. - // TODO: + // TODO: using the reservations in the DB, create a hummingbird path if !flyovers { // Standard path, no reservations at all From 8e44e108363d035ad023accd4550656a940ee93e Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Wed, 13 Dec 2023 17:02:35 +0100 Subject: [PATCH 069/100] Add new UT file for the reservations. --- pkg/hummingbird/BUILD.bazel | 3 ++- pkg/hummingbird/resevation_test.go | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 pkg/hummingbird/resevation_test.go diff --git a/pkg/hummingbird/BUILD.bazel b/pkg/hummingbird/BUILD.bazel index d7bc0587e3..520a0e5b0d 100644 --- a/pkg/hummingbird/BUILD.bazel +++ b/pkg/hummingbird/BUILD.bazel @@ -12,7 +12,6 @@ go_library( "//pkg/addr:go_default_library", "//pkg/log:go_default_library", "//pkg/private/serrors:go_default_library", - "//pkg/slayers/path:go_default_library", "//pkg/slayers/path/hummingbird:go_default_library", "//pkg/slayers/path/scion:go_default_library", "//pkg/snet:go_default_library", @@ -26,10 +25,12 @@ go_test( name = "go_default_test", srcs = [ "hummingbird_test.go", + "resevation_test.go", "utils_test.go", ], deps = [ ":go_default_library", + "//pkg/addr:go_default_library", "//pkg/slayers/path:go_default_library", "//pkg/slayers/path/hummingbird:go_default_library", "//pkg/slayers/path/scion:go_default_library", diff --git a/pkg/hummingbird/resevation_test.go b/pkg/hummingbird/resevation_test.go new file mode 100644 index 0000000000..9fcb70a655 --- /dev/null +++ b/pkg/hummingbird/resevation_test.go @@ -0,0 +1 @@ +package hummingbird_test From c635f4b4a2af472d15b5a12cbe6d330c4ceab44f Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Wed, 13 Dec 2023 17:13:23 +0100 Subject: [PATCH 070/100] New protobuf definitions for Hummingbird. --- pkg/proto/daemon/hummingbird.pb.go | 226 ++++++++++++++++++++++++----- proto/daemon/v1/daemon.proto | 4 +- proto/daemon/v1/hummingbird.proto | 42 +++++- 3 files changed, 222 insertions(+), 50 deletions(-) diff --git a/pkg/proto/daemon/hummingbird.pb.go b/pkg/proto/daemon/hummingbird.pb.go index c66981198b..c02a4e5108 100644 --- a/pkg/proto/daemon/hummingbird.pb.go +++ b/pkg/proto/daemon/hummingbird.pb.go @@ -24,6 +24,8 @@ type StoreFlyoversRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + + Flyovers []*Flyover `protobuf:"bytes,1,rep,name=flyovers,proto3" json:"flyovers,omitempty"` } func (x *StoreFlyoversRequest) Reset() { @@ -58,6 +60,13 @@ func (*StoreFlyoversRequest) Descriptor() ([]byte, []int) { return file_proto_daemon_v1_hummingbird_proto_rawDescGZIP(), []int{0} } +func (x *StoreFlyoversRequest) GetFlyovers() []*Flyover { + if x != nil { + return x.Flyovers + } + return nil +} + type StoreFlyoversResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -138,6 +147,8 @@ type ListFlyoversResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + + Flyovers []*Flyover `protobuf:"bytes,1,rep,name=flyovers,proto3" json:"flyovers,omitempty"` } func (x *ListFlyoversResponse) Reset() { @@ -172,6 +183,13 @@ func (*ListFlyoversResponse) Descriptor() ([]byte, []int) { return file_proto_daemon_v1_hummingbird_proto_rawDescGZIP(), []int{3} } +func (x *ListFlyoversResponse) GetFlyovers() []*Flyover { + if x != nil { + return x.Flyovers + } + return nil +} + type GetReservationsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -180,6 +198,7 @@ type GetReservationsRequest struct { SourceIsdAs uint64 `protobuf:"varint,1,opt,name=source_isd_as,json=sourceIsdAs,proto3" json:"source_isd_as,omitempty"` DestinationIsdAs uint64 `protobuf:"varint,2,opt,name=destination_isd_as,json=destinationIsdAs,proto3" json:"destination_isd_as,omitempty"` Refresh bool `protobuf:"varint,3,opt,name=refresh,proto3" json:"refresh,omitempty"` + MinBandwidth uint64 `protobuf:"varint,4,opt,name=min_bandwidth,json=minBandwidth,proto3" json:"min_bandwidth,omitempty"` } func (x *GetReservationsRequest) Reset() { @@ -235,6 +254,13 @@ func (x *GetReservationsRequest) GetRefresh() bool { return false } +func (x *GetReservationsRequest) GetMinBandwidth() uint64 { + if x != nil { + return x.MinBandwidth + } + return 0 +} + type GetReservationsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -282,6 +308,93 @@ func (x *GetReservationsResponse) GetReservations() []*Reservation { return nil } +type Flyover struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Ia uint64 `protobuf:"varint,1,opt,name=ia,proto3" json:"ia,omitempty"` + Ingress uint32 `protobuf:"varint,2,opt,name=ingress,proto3" json:"ingress,omitempty"` + Egress uint32 `protobuf:"varint,3,opt,name=egress,proto3" json:"egress,omitempty"` + Bw uint32 `protobuf:"varint,4,opt,name=bw,proto3" json:"bw,omitempty"` + Duration uint32 `protobuf:"varint,5,opt,name=duration,proto3" json:"duration,omitempty"` + Endtime uint32 `protobuf:"varint,6,opt,name=endtime,proto3" json:"endtime,omitempty"` +} + +func (x *Flyover) Reset() { + *x = Flyover{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_daemon_v1_hummingbird_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Flyover) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Flyover) ProtoMessage() {} + +func (x *Flyover) ProtoReflect() protoreflect.Message { + mi := &file_proto_daemon_v1_hummingbird_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Flyover.ProtoReflect.Descriptor instead. +func (*Flyover) Descriptor() ([]byte, []int) { + return file_proto_daemon_v1_hummingbird_proto_rawDescGZIP(), []int{6} +} + +func (x *Flyover) GetIa() uint64 { + if x != nil { + return x.Ia + } + return 0 +} + +func (x *Flyover) GetIngress() uint32 { + if x != nil { + return x.Ingress + } + return 0 +} + +func (x *Flyover) GetEgress() uint32 { + if x != nil { + return x.Egress + } + return 0 +} + +func (x *Flyover) GetBw() uint32 { + if x != nil { + return x.Bw + } + return 0 +} + +func (x *Flyover) GetDuration() uint32 { + if x != nil { + return x.Duration + } + return 0 +} + +func (x *Flyover) GetEndtime() uint32 { + if x != nil { + return x.Endtime + } + return 0 +} + type Reservation struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -294,7 +407,7 @@ type Reservation struct { func (x *Reservation) Reset() { *x = Reservation{} if protoimpl.UnsafeEnabled { - mi := &file_proto_daemon_v1_hummingbird_proto_msgTypes[6] + mi := &file_proto_daemon_v1_hummingbird_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -307,7 +420,7 @@ func (x *Reservation) String() string { func (*Reservation) ProtoMessage() {} func (x *Reservation) ProtoReflect() protoreflect.Message { - mi := &file_proto_daemon_v1_hummingbird_proto_msgTypes[6] + mi := &file_proto_daemon_v1_hummingbird_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -320,7 +433,7 @@ func (x *Reservation) ProtoReflect() protoreflect.Message { // Deprecated: Use Reservation.ProtoReflect.Descriptor instead. func (*Reservation) Descriptor() ([]byte, []int) { - return file_proto_daemon_v1_hummingbird_proto_rawDescGZIP(), []int{6} + return file_proto_daemon_v1_hummingbird_proto_rawDescGZIP(), []int{7} } func (x *Reservation) GetRaw() []byte { @@ -343,34 +456,52 @@ var file_proto_daemon_v1_hummingbird_proto_rawDesc = []byte{ 0x0a, 0x21, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2f, 0x76, 0x31, 0x2f, 0x68, 0x75, 0x6d, 0x6d, 0x69, 0x6e, 0x67, 0x62, 0x69, 0x72, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, - 0x6e, 0x2e, 0x76, 0x31, 0x22, 0x16, 0x0a, 0x14, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x46, 0x6c, 0x79, - 0x6f, 0x76, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x17, 0x0a, 0x15, - 0x53, 0x74, 0x6f, 0x72, 0x65, 0x46, 0x6c, 0x79, 0x6f, 0x76, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x46, 0x6c, 0x79, - 0x6f, 0x76, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x16, 0x0a, 0x14, - 0x4c, 0x69, 0x73, 0x74, 0x46, 0x6c, 0x79, 0x6f, 0x76, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x84, 0x01, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x65, - 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x22, 0x0a, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x73, 0x64, 0x5f, 0x61, 0x73, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x73, - 0x64, 0x41, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x5f, 0x69, 0x73, 0x64, 0x5f, 0x61, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x10, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x73, 0x64, 0x41, - 0x73, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x07, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x22, 0x5b, 0x0a, 0x17, 0x47, - 0x65, 0x74, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0c, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x52, - 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x65, - 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x35, 0x0a, 0x0b, 0x52, 0x65, 0x73, 0x65, - 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x61, 0x77, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x72, 0x61, 0x77, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x42, - 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x63, - 0x69, 0x6f, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x73, 0x63, 0x69, 0x6f, 0x6e, 0x2f, 0x70, - 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6e, 0x2e, 0x76, 0x31, 0x22, 0x4c, 0x0a, 0x14, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x46, 0x6c, 0x79, + 0x6f, 0x76, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x34, 0x0a, 0x08, + 0x66, 0x6c, 0x79, 0x6f, 0x76, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, + 0x2e, 0x46, 0x6c, 0x79, 0x6f, 0x76, 0x65, 0x72, 0x52, 0x08, 0x66, 0x6c, 0x79, 0x6f, 0x76, 0x65, + 0x72, 0x73, 0x22, 0x17, 0x0a, 0x15, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x46, 0x6c, 0x79, 0x6f, 0x76, + 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x0a, 0x13, 0x4c, + 0x69, 0x73, 0x74, 0x46, 0x6c, 0x79, 0x6f, 0x76, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x22, 0x4c, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x46, 0x6c, 0x79, 0x6f, 0x76, 0x65, + 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x08, 0x66, 0x6c, + 0x79, 0x6f, 0x76, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x46, + 0x6c, 0x79, 0x6f, 0x76, 0x65, 0x72, 0x52, 0x08, 0x66, 0x6c, 0x79, 0x6f, 0x76, 0x65, 0x72, 0x73, + 0x22, 0xa9, 0x01, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x73, 0x64, 0x5f, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x73, 0x64, 0x41, 0x73, 0x12, + 0x2c, 0x0a, 0x12, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, + 0x73, 0x64, 0x5f, 0x61, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x64, 0x65, 0x73, + 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x73, 0x64, 0x41, 0x73, 0x12, 0x18, 0x0a, + 0x07, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, + 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x69, 0x6e, 0x5f, 0x62, + 0x61, 0x6e, 0x64, 0x77, 0x69, 0x64, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, + 0x6d, 0x69, 0x6e, 0x42, 0x61, 0x6e, 0x64, 0x77, 0x69, 0x64, 0x74, 0x68, 0x22, 0x5b, 0x0a, 0x17, + 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0c, 0x72, 0x65, 0x73, 0x65, 0x72, + 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, + 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x72, 0x65, 0x73, + 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x91, 0x01, 0x0a, 0x07, 0x46, 0x6c, + 0x79, 0x6f, 0x76, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x02, 0x69, 0x61, 0x12, 0x18, 0x0a, 0x07, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12, + 0x16, 0x0a, 0x06, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x06, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x62, 0x77, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x02, 0x62, 0x77, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x64, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x35, 0x0a, + 0x0b, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, + 0x72, 0x61, 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x72, 0x61, 0x77, 0x12, 0x14, + 0x0a, 0x05, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x73, 0x63, 0x69, 0x6f, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x73, 0x63, + 0x69, 0x6f, 0x6e, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x64, 0x61, + 0x65, 0x6d, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -385,7 +516,7 @@ func file_proto_daemon_v1_hummingbird_proto_rawDescGZIP() []byte { return file_proto_daemon_v1_hummingbird_proto_rawDescData } -var file_proto_daemon_v1_hummingbird_proto_msgTypes = make([]protoimpl.MessageInfo, 7) +var file_proto_daemon_v1_hummingbird_proto_msgTypes = make([]protoimpl.MessageInfo, 8) var file_proto_daemon_v1_hummingbird_proto_goTypes = []interface{}{ (*StoreFlyoversRequest)(nil), // 0: proto.daemon.v1.StoreFlyoversRequest (*StoreFlyoversResponse)(nil), // 1: proto.daemon.v1.StoreFlyoversResponse @@ -393,15 +524,18 @@ var file_proto_daemon_v1_hummingbird_proto_goTypes = []interface{}{ (*ListFlyoversResponse)(nil), // 3: proto.daemon.v1.ListFlyoversResponse (*GetReservationsRequest)(nil), // 4: proto.daemon.v1.GetReservationsRequest (*GetReservationsResponse)(nil), // 5: proto.daemon.v1.GetReservationsResponse - (*Reservation)(nil), // 6: proto.daemon.v1.Reservation + (*Flyover)(nil), // 6: proto.daemon.v1.Flyover + (*Reservation)(nil), // 7: proto.daemon.v1.Reservation } var file_proto_daemon_v1_hummingbird_proto_depIdxs = []int32{ - 6, // 0: proto.daemon.v1.GetReservationsResponse.reservations:type_name -> proto.daemon.v1.Reservation - 1, // [1:1] is the sub-list for method output_type - 1, // [1:1] is the sub-list for method input_type - 1, // [1:1] is the sub-list for extension type_name - 1, // [1:1] is the sub-list for extension extendee - 0, // [0:1] is the sub-list for field type_name + 6, // 0: proto.daemon.v1.StoreFlyoversRequest.flyovers:type_name -> proto.daemon.v1.Flyover + 6, // 1: proto.daemon.v1.ListFlyoversResponse.flyovers:type_name -> proto.daemon.v1.Flyover + 7, // 2: proto.daemon.v1.GetReservationsResponse.reservations:type_name -> proto.daemon.v1.Reservation + 3, // [3:3] is the sub-list for method output_type + 3, // [3:3] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name } func init() { file_proto_daemon_v1_hummingbird_proto_init() } @@ -483,6 +617,18 @@ func file_proto_daemon_v1_hummingbird_proto_init() { } } file_proto_daemon_v1_hummingbird_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Flyover); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_daemon_v1_hummingbird_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Reservation); i { case 0: return &v.state @@ -501,7 +647,7 @@ func file_proto_daemon_v1_hummingbird_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_proto_daemon_v1_hummingbird_proto_rawDesc, NumEnums: 0, - NumMessages: 7, + NumMessages: 8, NumExtensions: 0, NumServices: 0, }, diff --git a/proto/daemon/v1/daemon.proto b/proto/daemon/v1/daemon.proto index 329696a889..f97d655eb4 100644 --- a/proto/daemon/v1/daemon.proto +++ b/proto/daemon/v1/daemon.proto @@ -43,10 +43,10 @@ service DaemonService { // DRKeyHostHost returns a key that matches the request. rpc DRKeyHostHost (DRKeyHostHostRequest) returns (DRKeyHostHostResponse) {} - + // StoreFlyovers stores the flyovers in the DB. rpc StoreFlyovers(StoreFlyoversRequest) returns (StoreFlyoversResponse) {} + // ListFlyovers lists all stored flyovers in this DB. rpc ListFlyovers(ListFlyoversRequest) returns (ListFlyoversResponse) {} - // GetReservations finds paths from source to destination, and then tries to assign flyovers to // as many hops as possible. rpc GetReservations(GetReservationsRequest) returns (GetReservationsResponse) {} diff --git a/proto/daemon/v1/hummingbird.proto b/proto/daemon/v1/hummingbird.proto index f6f73e222e..3940e53b57 100644 --- a/proto/daemon/v1/hummingbird.proto +++ b/proto/daemon/v1/hummingbird.proto @@ -19,27 +19,53 @@ option go_package = "github.com/scionproto/scion/pkg/proto/daemon"; package proto.daemon.v1; -message StoreFlyoversRequest {} -message StoreFlyoversResponse {} -message ListFlyoversRequest {} -message ListFlyoversResponse {} +message StoreFlyoversRequest { + repeated Flyover flyovers = 1; +} + +message StoreFlyoversResponse {} // empty + +message ListFlyoversRequest {} // empty + +message ListFlyoversResponse { + repeated Flyover flyovers = 1; +} message GetReservationsRequest { - // ISD-AS of the source of the path request. + // ISD-AS of the source of the path request. uint64 source_isd_as = 1; // ISD-AS of the destination of the path request. uint64 destination_isd_as = 2; // Choose to fetch fresh paths for this request instead // of having the server reply from its cache. bool refresh = 3; + // The minimum bandwidth for the obtained reservations. + uint64 min_bandwidth = 4; } message GetReservationsResponse { - repeated Reservation reservations = 1; + repeated Reservation reservations = 1; +} + +message Flyover { + // The AS ID in ISD-AS form (64 bits). + uint64 ia = 1; + // Ingress ID. 16 bits. + uint32 ingress = 2; + // Egress ID. 16 bits. + uint32 egress = 3; + // Bandwidth. 16 bits. + uint32 bw = 4; + // Duration in seconds. 16 bits. + uint32 duration = 5; + // Endtime is the unix timestamp when the reservation is no longer valid. + uint32 endtime = 6; } message Reservation { - bytes raw = 1; - double ratio = 2; // ratio flyovers / hops + // The raw Hummingbird path. + bytes raw = 1; + // Ratio # flyovers / # hops. + double ratio = 2; } From 0783f5ae33b904422eebebe583dea17c9183736e Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Wed, 13 Dec 2023 17:13:55 +0100 Subject: [PATCH 071/100] Fix integration test. --- tools/end2end_hbird/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/end2end_hbird/main.go b/tools/end2end_hbird/main.go index a3061b8197..9f65119fbf 100644 --- a/tools/end2end_hbird/main.go +++ b/tools/end2end_hbird/main.go @@ -318,7 +318,7 @@ func (c *client) attemptRequest(n int) bool { remote.Path = path.Dataplane() } else if !partial { // full path with reservations - hbirdClient, err := hummingbird.NewReservation(path) + hbirdClient, err := hummingbird.NewReservation(path, nil) if err != nil { logger.Error("Error converting path to Hummingbird", "err", err) return false @@ -341,7 +341,7 @@ func (c *client) attemptRequest(n int) bool { remote.Path = path.Dataplane() } else { //partial reservations, alternating resrved and not reserved - hbirdClient, err := hummingbird.NewReservation(path) + hbirdClient, err := hummingbird.NewReservation(path, nil) if err != nil { logger.Error("Error converting path to Hummingbird", "err", err) return false From f0bcf17610ab32de655b555896c8df7b7da15f6b Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Thu, 14 Dec 2023 10:24:41 +0100 Subject: [PATCH 072/100] cleanup. --- pkg/hummingbird/BUILD.bazel | 4 ++-- pkg/hummingbird/{resevation.go => reservation.go} | 9 ++++----- .../{resevation_test.go => reservation_test.go} | 0 3 files changed, 6 insertions(+), 7 deletions(-) rename pkg/hummingbird/{resevation.go => reservation.go} (99%) rename pkg/hummingbird/{resevation_test.go => reservation_test.go} (100%) diff --git a/pkg/hummingbird/BUILD.bazel b/pkg/hummingbird/BUILD.bazel index 520a0e5b0d..8ef5d0d8dc 100644 --- a/pkg/hummingbird/BUILD.bazel +++ b/pkg/hummingbird/BUILD.bazel @@ -4,7 +4,7 @@ go_library( name = "go_default_library", srcs = [ "hummingbird.go", - "resevation.go", + "reservation.go", ], importpath = "github.com/scionproto/scion/pkg/hummingbird", visibility = ["//visibility:public"], @@ -25,7 +25,7 @@ go_test( name = "go_default_test", srcs = [ "hummingbird_test.go", - "resevation_test.go", + "reservation_test.go", "utils_test.go", ], deps = [ diff --git a/pkg/hummingbird/resevation.go b/pkg/hummingbird/reservation.go similarity index 99% rename from pkg/hummingbird/resevation.go rename to pkg/hummingbird/reservation.go index 3e33b1a1c9..ca28d47cbd 100644 --- a/pkg/hummingbird/resevation.go +++ b/pkg/hummingbird/reservation.go @@ -72,14 +72,13 @@ type Hop struct { } func NewReservation(p snet.Path, flyovers map[addr.IA][]*Flyover) (*Reservation, error) { - // deleteme - // func NewReservation(p snet.Path) (*Reservation, error) { c := &Reservation{} err := c.prepareHbirdPath(p) - if err == nil { - c.applyFlyovers(flyovers) + if err != nil { + return nil, err } - return c, err + c.applyFlyovers(flyovers) + return c, nil } // prepareHbirdPathOlD prepares as hummingbird path and initializes the Resevation object. diff --git a/pkg/hummingbird/resevation_test.go b/pkg/hummingbird/reservation_test.go similarity index 100% rename from pkg/hummingbird/resevation_test.go rename to pkg/hummingbird/reservation_test.go From a49083979fc9f484bde7d4263d1a6fa3ac20164e Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Thu, 14 Dec 2023 10:34:45 +0100 Subject: [PATCH 073/100] Cleanup. --- pkg/hummingbird/hummingbird.go | 14 ++ pkg/hummingbird/hummingbird_test.go | 251 +------------------------ pkg/hummingbird/reservation.go | 2 +- pkg/hummingbird/reservation_test.go | 274 ++++++++++++++++++++++++++++ pkg/hummingbird/utils_test.go | 20 +- tools/end2end_hbird/main.go | 3 +- 6 files changed, 306 insertions(+), 258 deletions(-) diff --git a/pkg/hummingbird/hummingbird.go b/pkg/hummingbird/hummingbird.go index d7b7b33c97..094ded0f01 100644 --- a/pkg/hummingbird/hummingbird.go +++ b/pkg/hummingbird/hummingbird.go @@ -1,3 +1,17 @@ +// Copyright 2024 ETH Zurich +// +// 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 hummingbird import ( diff --git a/pkg/hummingbird/hummingbird_test.go b/pkg/hummingbird/hummingbird_test.go index 22731f0fdd..b3f0333db9 100644 --- a/pkg/hummingbird/hummingbird_test.go +++ b/pkg/hummingbird/hummingbird_test.go @@ -1,5 +1,4 @@ -// Copyright 2020 Anapaya Systems -// Copyright 2023 ETH Zurich +// Copyright 2024 ETH Zurich // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -19,12 +18,8 @@ import ( "testing" "time" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/scionproto/scion/pkg/addr" "github.com/scionproto/scion/pkg/hummingbird" - snetpath "github.com/scionproto/scion/pkg/snet/path" + "github.com/stretchr/testify/assert" ) var testHops = []hummingbird.BaseHop{ @@ -92,245 +87,3 @@ func TestConvertToHbirdPath(t *testing.T) { assert.NoError(t, err) assert.Equal(t, expectecPath, out) } - -// deleteme remove this test -func TestPrepareHbirdPath(t *testing.T) { - scionPath, err := getScionSnetPath() - assert.NoError(t, err) - - now := time.Now() - - flyovers := flyoverSliceToMap(testFlyovers) - flyovers = nil - c, err := hummingbird.NewReservation(scionPath, flyovers) - require.NoError(t, err) - err = c.ApplyReservations(nil) - assert.NoError(t, err) - scionPath, err = getScionSnetPath() - assert.NoError(t, err) - output, err := c.FinalizePath(scionPath, 0, now) - assert.NoError(t, err) - expectecPath, err := getHbirdNoFlyoversSnetPath(now) - assert.NoError(t, err) - assert.Equal(t, expectecPath, output) -} - -// deleteme rename this test as TestPrepareHbirdPath -func TestApplyReservations(t *testing.T) { - scionPath, err := getScionSnetPath() - assert.NoError(t, err) - - c, err := hummingbird.NewReservation(scionPath, flyoverSliceToMap(testFlyovers)) - require.NoError(t, err) - - scionPath, err = getScionSnetPath() - assert.NoError(t, err) - - hbirdPath, err := getHbirdFlyoversSnetPath(fixedTime) - assert.NoError(t, err) - - output, err := c.FinalizePath(scionPath, 16, fixedTime) - assert.NoError(t, err) - assert.Equal(t, hbirdPath, output) -} - -func TestCheckReservationExpiry(t *testing.T) { - scionPath, err := getScionSnetPath() - assert.NoError(t, err) - - c, err := hummingbird.NewReservation(scionPath, nil) - require.NoError(t, err) - - assert.NoError(t, err) - - tnow := time.Now() - now := uint32(tnow.Unix()) - - // hop1: first reservation expired, second ok - // hop2: first reservation expired, second not started, third expired, fourth ok - // hop3: first not yet valid, second expired - input := []hummingbird.Flyover{ - { - BaseHop: testHops[0], - ResID: 1234, - Duration: 70, - StartTime: now - 80, - }, - { - BaseHop: testHops[0], - ResID: 34, - Duration: 560, - StartTime: now - 10, - }, - { - BaseHop: testHops[1], - ResID: 42, - Duration: 80, - StartTime: now - 100, - }, - { - BaseHop: testHops[1], - ResID: 31, - Duration: 389, - StartTime: now + 50, - }, - { - BaseHop: testHops[1], - ResID: 12, - Duration: 64, - StartTime: now - 60, - }, - { - BaseHop: testHops[1], - ResID: 5, - Duration: 180, - StartTime: now - 30, - }, - { - BaseHop: testHops[2], - ResID: 365, - Duration: 150, - StartTime: now + 60, - }, - { - BaseHop: testHops[2], - ResID: 345, - Duration: 150, - StartTime: now - 345, - }, - } - - expected := []hummingbird.Flyover{ - { - BaseHop: testHops[0], - ResID: 34, - Duration: 560, - StartTime: now - 10, - }, - { - BaseHop: testHops[1], - ResID: 5, - Duration: 180, - StartTime: now - 30, - }, - { - BaseHop: testHops[1], - ResID: 31, - Duration: 389, - StartTime: now + 50, - }, - { - BaseHop: testHops[2], - ResID: 365, - Duration: 150, - StartTime: now + 60, - }, - } - - err = c.ApplyReservations(input) - assert.NoError(t, err) - - c.CheckExpiry(5) - - output := c.GetUsedReservations() - - assert.Equal(t, expected, output) - - // Verify last reservation is unused as it is not yet valid - scionPath, err = getScionSnetPath() - assert.NoError(t, err) - - outPath, err := c.FinalizePath(scionPath, 16, tnow) - assert.NoError(t, err) - - raw := outPath.Dataplane().(snetpath.Hummingbird).Raw - - dec, err := decodeDataplane(raw) - assert.NoError(t, err) - assert.True(t, dec.HopFields[0].Flyover) - assert.True(t, dec.HopFields[1].Flyover) - assert.False(t, dec.HopFields[2].Flyover) - assert.False(t, dec.HopFields[3].Flyover) -} - -func TestRemoveReservations(t *testing.T) { - scionPath, err := getScionSnetPath() - assert.NoError(t, err) - - c, err := hummingbird.NewReservation(scionPath, nil) - require.NoError(t, err) - - err = c.ApplyReservations(testFlyovers) - assert.NoError(t, err) - - remove := []hummingbird.Flyover{ - { - BaseHop: hummingbird.BaseHop{ - IA: testHops[0].IA, - }, - ResID: 1234, - }, - { - BaseHop: hummingbird.BaseHop{ - IA: testHops[1].IA, - }, - ResID: 53, - }, - { - BaseHop: hummingbird.BaseHop{ - IA: testHops[2].IA, - }, - ResID: 365, - }, - } - - expected := []hummingbird.Flyover{ - { - BaseHop: testHops[1], - ResID: 42, - Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4, 3, 2, 1, 0}, - Bw: 16, - Duration: 180, - StartTime: uint32(fixedTime.Unix()) - 32, - }, - { - BaseHop: testHops[2], - ResID: 21, - Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7}, - Bw: 20, - Duration: 150, - StartTime: uint32(fixedTime.Unix()) - 10, - }, - } - - err = c.RemoveReservations(remove) - assert.NoError(t, err) - - output := c.GetUsedReservations() - assert.Equal(t, expected, output) - - // Verify removal has resulted in correct path - scionPath, err = getScionSnetPath() - assert.NoError(t, err) - - outPath, err := c.FinalizePath(scionPath, 16, time.Now()) - assert.NoError(t, err) - - raw := outPath.Dataplane().(snetpath.Hummingbird).Raw - - dec, err := decodeDataplane(raw) - assert.NoError(t, err) - assert.False(t, dec.HopFields[0].Flyover) - assert.True(t, dec.HopFields[1].Flyover) - assert.False(t, dec.HopFields[2].Flyover) - assert.True(t, dec.HopFields[3].Flyover) -} - -func flyoverSliceToMap(flyovers []hummingbird.Flyover) map[addr.IA][]*hummingbird.Flyover { - m := make(map[addr.IA][]*hummingbird.Flyover) - for _, flyover := range flyovers { - clone := flyover - m[clone.IA] = append(m[clone.IA], &clone) - } - return m -} diff --git a/pkg/hummingbird/reservation.go b/pkg/hummingbird/reservation.go index ca28d47cbd..d0bb459c51 100644 --- a/pkg/hummingbird/reservation.go +++ b/pkg/hummingbird/reservation.go @@ -1,4 +1,4 @@ -// Copyright 2023 ETH Zurich +// Copyright 2024 ETH Zurich // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/hummingbird/reservation_test.go b/pkg/hummingbird/reservation_test.go index 9fcb70a655..4a7018d381 100644 --- a/pkg/hummingbird/reservation_test.go +++ b/pkg/hummingbird/reservation_test.go @@ -1 +1,275 @@ +// Copyright 2024 ETH Zurich +// +// 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 hummingbird_test + +import ( + "testing" + "time" + + "github.com/scionproto/scion/pkg/addr" + "github.com/scionproto/scion/pkg/hummingbird" + dphbird "github.com/scionproto/scion/pkg/slayers/path/hummingbird" + snetpath "github.com/scionproto/scion/pkg/snet/path" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// deleteme remove this test +func TestPrepareHbirdPath(t *testing.T) { + scionPath, err := getScionSnetPath() + assert.NoError(t, err) + + now := time.Now() + + flyovers := flyoverSliceToMap(testFlyovers) + flyovers = nil + c, err := hummingbird.NewReservation(scionPath, flyovers) + require.NoError(t, err) + err = c.ApplyReservations(nil) + assert.NoError(t, err) + scionPath, err = getScionSnetPath() + assert.NoError(t, err) + output, err := c.FinalizePath(scionPath, 0, now) + assert.NoError(t, err) + expectecPath, err := getHbirdNoFlyoversSnetPath(now) + assert.NoError(t, err) + assert.Equal(t, expectecPath, output) +} + +// deleteme rename this test as TestPrepareHbirdPath +func TestApplyReservations(t *testing.T) { + scionPath, err := getScionSnetPath() + assert.NoError(t, err) + + c, err := hummingbird.NewReservation(scionPath, flyoverSliceToMap(testFlyovers)) + require.NoError(t, err) + + scionPath, err = getScionSnetPath() + assert.NoError(t, err) + + hbirdPath, err := getHbirdFlyoversSnetPath(fixedTime) + assert.NoError(t, err) + + output, err := c.FinalizePath(scionPath, 16, fixedTime) + assert.NoError(t, err) + assert.Equal(t, hbirdPath, output) +} + +func TestCheckReservationExpiry(t *testing.T) { + scionPath, err := getScionSnetPath() + assert.NoError(t, err) + + c, err := hummingbird.NewReservation(scionPath, nil) + require.NoError(t, err) + + assert.NoError(t, err) + + tnow := time.Now() + now := uint32(tnow.Unix()) + + // hop1: first reservation expired, second ok + // hop2: first reservation expired, second not started, third expired, fourth ok + // hop3: first not yet valid, second expired + input := []hummingbird.Flyover{ + { + BaseHop: testHops[0], + ResID: 1234, + Duration: 70, + StartTime: now - 80, + }, + { + BaseHop: testHops[0], + ResID: 34, + Duration: 560, + StartTime: now - 10, + }, + { + BaseHop: testHops[1], + ResID: 42, + Duration: 80, + StartTime: now - 100, + }, + { + BaseHop: testHops[1], + ResID: 31, + Duration: 389, + StartTime: now + 50, + }, + { + BaseHop: testHops[1], + ResID: 12, + Duration: 64, + StartTime: now - 60, + }, + { + BaseHop: testHops[1], + ResID: 5, + Duration: 180, + StartTime: now - 30, + }, + { + BaseHop: testHops[2], + ResID: 365, + Duration: 150, + StartTime: now + 60, + }, + { + BaseHop: testHops[2], + ResID: 345, + Duration: 150, + StartTime: now - 345, + }, + } + + expected := []hummingbird.Flyover{ + { + BaseHop: testHops[0], + ResID: 34, + Duration: 560, + StartTime: now - 10, + }, + { + BaseHop: testHops[1], + ResID: 5, + Duration: 180, + StartTime: now - 30, + }, + { + BaseHop: testHops[1], + ResID: 31, + Duration: 389, + StartTime: now + 50, + }, + { + BaseHop: testHops[2], + ResID: 365, + Duration: 150, + StartTime: now + 60, + }, + } + + err = c.ApplyReservations(input) + assert.NoError(t, err) + + c.CheckExpiry(5) + + output := c.GetUsedReservations() + + assert.Equal(t, expected, output) + + // Verify last reservation is unused as it is not yet valid + scionPath, err = getScionSnetPath() + assert.NoError(t, err) + + outPath, err := c.FinalizePath(scionPath, 16, tnow) + assert.NoError(t, err) + + raw := outPath.Dataplane().(snetpath.Hummingbird).Raw + + dec := decodeDataplane(t, raw) + assert.True(t, dec.HopFields[0].Flyover) + assert.True(t, dec.HopFields[1].Flyover) + assert.False(t, dec.HopFields[2].Flyover) + assert.False(t, dec.HopFields[3].Flyover) +} + +func TestRemoveReservations(t *testing.T) { + scionPath, err := getScionSnetPath() + assert.NoError(t, err) + + c, err := hummingbird.NewReservation(scionPath, nil) + require.NoError(t, err) + + err = c.ApplyReservations(testFlyovers) + assert.NoError(t, err) + + remove := []hummingbird.Flyover{ + { + BaseHop: hummingbird.BaseHop{ + IA: testHops[0].IA, + }, + ResID: 1234, + }, + { + BaseHop: hummingbird.BaseHop{ + IA: testHops[1].IA, + }, + ResID: 53, + }, + { + BaseHop: hummingbird.BaseHop{ + IA: testHops[2].IA, + }, + ResID: 365, + }, + } + + expected := []hummingbird.Flyover{ + { + BaseHop: testHops[1], + ResID: 42, + Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4, 3, 2, 1, 0}, + Bw: 16, + Duration: 180, + StartTime: uint32(fixedTime.Unix()) - 32, + }, + { + BaseHop: testHops[2], + ResID: 21, + Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7}, + Bw: 20, + Duration: 150, + StartTime: uint32(fixedTime.Unix()) - 10, + }, + } + + err = c.RemoveReservations(remove) + assert.NoError(t, err) + + output := c.GetUsedReservations() + assert.Equal(t, expected, output) + + // Verify removal has resulted in correct path + scionPath, err = getScionSnetPath() + assert.NoError(t, err) + + outPath, err := c.FinalizePath(scionPath, 16, time.Now()) + assert.NoError(t, err) + + raw := outPath.Dataplane().(snetpath.Hummingbird).Raw + + dec := decodeDataplane(t, raw) + assert.False(t, dec.HopFields[0].Flyover) + assert.True(t, dec.HopFields[1].Flyover) + assert.False(t, dec.HopFields[2].Flyover) + assert.True(t, dec.HopFields[3].Flyover) +} + +func decodeDataplane(t *testing.T, raw []byte) dphbird.Decoded { + t.Helper() + dec := dphbird.Decoded{} + err := dec.DecodeFromBytes(raw) + assert.NoError(t, err) + return dec +} + +func flyoverSliceToMap(flyovers []hummingbird.Flyover) map[addr.IA][]*hummingbird.Flyover { + m := make(map[addr.IA][]*hummingbird.Flyover) + for _, flyover := range flyovers { + clone := flyover + m[clone.IA] = append(m[clone.IA], &clone) + } + return m +} diff --git a/pkg/hummingbird/utils_test.go b/pkg/hummingbird/utils_test.go index b9ee8fa41b..5feb65d149 100644 --- a/pkg/hummingbird/utils_test.go +++ b/pkg/hummingbird/utils_test.go @@ -1,3 +1,17 @@ +// Copyright 2024 ETH Zurich +// +// 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 hummingbird_test import ( @@ -262,9 +276,3 @@ func getHbirdFlyoversSnetPath(t time.Time) (snetpath.Path, error) { } return p, err } - -func decodeDataplane(raw []byte) (hummingbird.Decoded, error) { - dec := hummingbird.Decoded{} - err := dec.DecodeFromBytes(raw) - return dec, err -} diff --git a/tools/end2end_hbird/main.go b/tools/end2end_hbird/main.go index 9f65119fbf..3ca8372e5e 100644 --- a/tools/end2end_hbird/main.go +++ b/tools/end2end_hbird/main.go @@ -1,5 +1,4 @@ -// Copyright 2018 ETH Zurich -// Copyright 2019 ETH Zurich, Anapaya Systems +// Copyright 2024 ETH Zurich // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. From 0bd9a976161cef3d9b81e4874322d3f6388a6270 Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Thu, 14 Dec 2023 10:41:51 +0100 Subject: [PATCH 074/100] Fix hummingbird protobuf definitions. --- pkg/proto/daemon/hummingbird.pb.go | 59 ++++++++++++++++++++---------- proto/daemon/v1/hummingbird.proto | 14 +++++-- 2 files changed, 50 insertions(+), 23 deletions(-) diff --git a/pkg/proto/daemon/hummingbird.pb.go b/pkg/proto/daemon/hummingbird.pb.go index c02a4e5108..582ad7dc7c 100644 --- a/pkg/proto/daemon/hummingbird.pb.go +++ b/pkg/proto/daemon/hummingbird.pb.go @@ -313,12 +313,14 @@ type Flyover struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Ia uint64 `protobuf:"varint,1,opt,name=ia,proto3" json:"ia,omitempty"` - Ingress uint32 `protobuf:"varint,2,opt,name=ingress,proto3" json:"ingress,omitempty"` - Egress uint32 `protobuf:"varint,3,opt,name=egress,proto3" json:"egress,omitempty"` - Bw uint32 `protobuf:"varint,4,opt,name=bw,proto3" json:"bw,omitempty"` - Duration uint32 `protobuf:"varint,5,opt,name=duration,proto3" json:"duration,omitempty"` - Endtime uint32 `protobuf:"varint,6,opt,name=endtime,proto3" json:"endtime,omitempty"` + Ia uint64 `protobuf:"varint,1,opt,name=ia,proto3" json:"ia,omitempty"` + Ingress uint32 `protobuf:"varint,2,opt,name=ingress,proto3" json:"ingress,omitempty"` + Egress uint32 `protobuf:"varint,3,opt,name=egress,proto3" json:"egress,omitempty"` + Bw uint32 `protobuf:"varint,4,opt,name=bw,proto3" json:"bw,omitempty"` + StartTime uint32 `protobuf:"varint,5,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` + Duration uint32 `protobuf:"varint,6,opt,name=duration,proto3" json:"duration,omitempty"` + ResId uint32 `protobuf:"varint,7,opt,name=res_id,json=resId,proto3" json:"res_id,omitempty"` + Ak []byte `protobuf:"bytes,8,opt,name=ak,proto3" json:"ak,omitempty"` } func (x *Flyover) Reset() { @@ -381,6 +383,13 @@ func (x *Flyover) GetBw() uint32 { return 0 } +func (x *Flyover) GetStartTime() uint32 { + if x != nil { + return x.StartTime + } + return 0 +} + func (x *Flyover) GetDuration() uint32 { if x != nil { return x.Duration @@ -388,13 +397,20 @@ func (x *Flyover) GetDuration() uint32 { return 0 } -func (x *Flyover) GetEndtime() uint32 { +func (x *Flyover) GetResId() uint32 { if x != nil { - return x.Endtime + return x.ResId } return 0 } +func (x *Flyover) GetAk() []byte { + if x != nil { + return x.Ak + } + return nil +} + type Reservation struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -485,23 +501,26 @@ var file_proto_daemon_v1_hummingbird_proto_rawDesc = []byte{ 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x72, 0x65, 0x73, - 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x91, 0x01, 0x0a, 0x07, 0x46, 0x6c, + 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xbd, 0x01, 0x0a, 0x07, 0x46, 0x6c, 0x79, 0x6f, 0x76, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x61, 0x12, 0x18, 0x0a, 0x07, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x65, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x62, 0x77, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x02, 0x62, 0x77, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x64, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x35, 0x0a, - 0x0b, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, - 0x72, 0x61, 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x72, 0x61, 0x77, 0x12, 0x14, - 0x0a, 0x05, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x73, 0x63, 0x69, 0x6f, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x73, 0x63, - 0x69, 0x6f, 0x6e, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x64, 0x61, - 0x65, 0x6d, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x01, 0x28, 0x0d, 0x52, 0x02, 0x62, 0x77, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x15, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x05, 0x72, 0x65, 0x73, 0x49, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x61, 0x6b, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x61, 0x6b, 0x22, 0x35, 0x0a, 0x0b, 0x52, 0x65, 0x73, + 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x61, 0x77, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x72, 0x61, 0x77, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, + 0x63, 0x69, 0x6f, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x73, 0x63, 0x69, 0x6f, 0x6e, 0x2f, + 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/proto/daemon/v1/hummingbird.proto b/proto/daemon/v1/hummingbird.proto index 3940e53b57..7cb6419f58 100644 --- a/proto/daemon/v1/hummingbird.proto +++ b/proto/daemon/v1/hummingbird.proto @@ -55,12 +55,20 @@ message Flyover { uint32 ingress = 2; // Egress ID. 16 bits. uint32 egress = 3; + // Bandwidth. 16 bits. uint32 bw = 4; + // The unix timestamp when the reservation starts. + uint32 start_time = 5; // Duration in seconds. 16 bits. - uint32 duration = 5; - // Endtime is the unix timestamp when the reservation is no longer valid. - uint32 endtime = 6; + uint32 duration = 6; + + // Reservation ID, unique per AS. + uint32 res_id = 7; + // This Ak is the derived key for this flyover, that is used to derive the per-host key. + // It is 16 bytes long. + bytes ak = 8; + } message Reservation { From 8e9337e7f45b5777f93dc0b8ea2945ce1d5d5255 Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Thu, 14 Dec 2023 11:04:53 +0100 Subject: [PATCH 075/100] More cleanup in tests. --- pkg/hummingbird/hummingbird_test.go | 3 +-- pkg/hummingbird/reservation_test.go | 40 ++++++++++------------------- pkg/hummingbird/utils_test.go | 8 ++++-- 3 files changed, 20 insertions(+), 31 deletions(-) diff --git a/pkg/hummingbird/hummingbird_test.go b/pkg/hummingbird/hummingbird_test.go index b3f0333db9..a1bb4f3ed9 100644 --- a/pkg/hummingbird/hummingbird_test.go +++ b/pkg/hummingbird/hummingbird_test.go @@ -76,8 +76,7 @@ var testFlyovers = []hummingbird.Flyover{ } func TestConvertToHbirdPath(t *testing.T) { - scionPath, err := getScionSnetPath() - assert.NoError(t, err) + scionPath:= getScionSnetPath(t) now := time.Now() expectecPath, err := getHbirdNoFlyoversSnetPath(now) diff --git a/pkg/hummingbird/reservation_test.go b/pkg/hummingbird/reservation_test.go index 4a7018d381..acdbbeb720 100644 --- a/pkg/hummingbird/reservation_test.go +++ b/pkg/hummingbird/reservation_test.go @@ -28,19 +28,14 @@ import ( // deleteme remove this test func TestPrepareHbirdPath(t *testing.T) { - scionPath, err := getScionSnetPath() - assert.NoError(t, err) - + scionPath := getScionSnetPath(t) now := time.Now() flyovers := flyoverSliceToMap(testFlyovers) flyovers = nil c, err := hummingbird.NewReservation(scionPath, flyovers) require.NoError(t, err) - err = c.ApplyReservations(nil) - assert.NoError(t, err) - scionPath, err = getScionSnetPath() - assert.NoError(t, err) + scionPath = getScionSnetPath(t) output, err := c.FinalizePath(scionPath, 0, now) assert.NoError(t, err) expectecPath, err := getHbirdNoFlyoversSnetPath(now) @@ -50,13 +45,12 @@ func TestPrepareHbirdPath(t *testing.T) { // deleteme rename this test as TestPrepareHbirdPath func TestApplyReservations(t *testing.T) { - scionPath, err := getScionSnetPath() - assert.NoError(t, err) + scionPath := getScionSnetPath(t) c, err := hummingbird.NewReservation(scionPath, flyoverSliceToMap(testFlyovers)) require.NoError(t, err) - scionPath, err = getScionSnetPath() + scionPath = getScionSnetPath(t) assert.NoError(t, err) hbirdPath, err := getHbirdFlyoversSnetPath(fixedTime) @@ -68,13 +62,6 @@ func TestApplyReservations(t *testing.T) { } func TestCheckReservationExpiry(t *testing.T) { - scionPath, err := getScionSnetPath() - assert.NoError(t, err) - - c, err := hummingbird.NewReservation(scionPath, nil) - require.NoError(t, err) - - assert.NoError(t, err) tnow := time.Now() now := uint32(tnow.Unix()) @@ -160,18 +147,19 @@ func TestCheckReservationExpiry(t *testing.T) { }, } + scionPath := getScionSnetPath(t) + + c, err := hummingbird.NewReservation(scionPath, nil) + assert.NoError(t, err) + err = c.ApplyReservations(input) assert.NoError(t, err) c.CheckExpiry(5) - - output := c.GetUsedReservations() - - assert.Equal(t, expected, output) + assert.Equal(t, expected, c.GetUsedReservations()) // Verify last reservation is unused as it is not yet valid - scionPath, err = getScionSnetPath() - assert.NoError(t, err) + scionPath = getScionSnetPath(t) outPath, err := c.FinalizePath(scionPath, 16, tnow) assert.NoError(t, err) @@ -186,8 +174,7 @@ func TestCheckReservationExpiry(t *testing.T) { } func TestRemoveReservations(t *testing.T) { - scionPath, err := getScionSnetPath() - assert.NoError(t, err) + scionPath := getScionSnetPath(t) c, err := hummingbird.NewReservation(scionPath, nil) require.NoError(t, err) @@ -242,8 +229,7 @@ func TestRemoveReservations(t *testing.T) { assert.Equal(t, expected, output) // Verify removal has resulted in correct path - scionPath, err = getScionSnetPath() - assert.NoError(t, err) + scionPath = getScionSnetPath(t) outPath, err := c.FinalizePath(scionPath, 16, time.Now()) assert.NoError(t, err) diff --git a/pkg/hummingbird/utils_test.go b/pkg/hummingbird/utils_test.go index 5feb65d149..5c5e071bb1 100644 --- a/pkg/hummingbird/utils_test.go +++ b/pkg/hummingbird/utils_test.go @@ -15,6 +15,7 @@ package hummingbird_test import ( + "testing" "time" "github.com/scionproto/scion/pkg/slayers/path" @@ -22,6 +23,7 @@ import ( "github.com/scionproto/scion/pkg/slayers/path/scion" "github.com/scionproto/scion/pkg/snet" snetpath "github.com/scionproto/scion/pkg/snet/path" + "github.com/stretchr/testify/assert" ) var fixedTime = time.Unix(1136239445, 432) @@ -188,8 +190,10 @@ func getRawScionPath(d scion.Decoded) ([]byte, error) { return b, err } -func getScionSnetPath() (snetpath.Path, error) { +func getScionSnetPath(t *testing.T) snetpath.Path { + t.Helper() rawScion, err := getRawScionPath(*decodedTestPath) + assert.NoError(t, err) p := snetpath.Path{ Src: interfacesTest[0].IA, Dst: interfacesTest[len(interfacesTest)-1].IA, @@ -200,7 +204,7 @@ func getScionSnetPath() (snetpath.Path, error) { Interfaces: interfacesTest, }, } - return p, err + return p } func getRawHbirdPath(h hummingbird.Decoded) ([]byte, error) { From 4cf3ee91b46d80cb37b451ca3f1be799e92175e5 Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Thu, 14 Dec 2023 11:11:35 +0100 Subject: [PATCH 076/100] Cleanup, names. --- pkg/hummingbird/reservation.go | 246 +++++++++------------------- pkg/hummingbird/reservation_test.go | 10 +- tools/end2end_hbird/main.go | 8 +- 3 files changed, 87 insertions(+), 177 deletions(-) diff --git a/pkg/hummingbird/reservation.go b/pkg/hummingbird/reservation.go index d0bb459c51..a3fedb2ab1 100644 --- a/pkg/hummingbird/reservation.go +++ b/pkg/hummingbird/reservation.go @@ -63,10 +63,10 @@ type Hop struct { hopfield *hummingbird.FlyoverHopField // The Index of the Segment the above hopfield is part of. infIdx int - // The reservations that can be used for this hop. - // The reservation at index 0 is the one used to build the path + // The flyovers that can be used for this hop. + // The flyover at index 0 is the one used to build the path // MUST be non-empty if hopfield.Flyiver == true - reservations []Flyover + flyovers []Flyover // The original scion mac of the corresponding hopfield. scionMac [6]byte } @@ -81,80 +81,6 @@ func NewReservation(p snet.Path, flyovers map[addr.IA][]*Flyover) (*Reservation, return c, nil } -// prepareHbirdPathOlD prepares as hummingbird path and initializes the Resevation object. -func (c *Reservation) prepareHbirdPathOlD(p snet.Path) error { - if p == nil { - return serrors.New("Empty path") - } - c.dec = hummingbird.Decoded{} - switch v := p.Dataplane().(type) { - case path.SCION: - // Convert path to decoded hbird path - scionDec := scion.Decoded{} - if err := scionDec.DecodeFromBytes(v.Raw); err != nil { - return serrors.Join(err, serrors.New("Failed to Prepare Hummingbird Path")) - } - c.dec.ConvertFromScionDecoded(scionDec) - case path.Hummingbird: - if err := c.dec.DecodeFromBytes(v.Raw); err != nil { - return serrors.Join(err, serrors.New("Failed to Prepare Hummingbird Path")) - } - default: - return serrors.New("Unsupported path type") - } - - // Initialize a hop for each traversed AS. - infIdx := 0 - for i := 0; i < len(c.dec.HopFields); i++ { - // Determine the segment index and whether the hop is a cross over or not. - var xover bool - for ; infIdx < 2; infIdx++ { - if i < int(c.dec.FirstHopPerSeg[infIdx]) { - if !c.dec.InfoFields[infIdx].Peer { - xover = (i == int(c.dec.FirstHopPerSeg[infIdx])-1) && - i < len(c.dec.HopFields)-1 - } - break - } - } - - // If not first segment, check if this hop is the first one after a xover. - if infIdx > 0 && - !c.dec.InfoFields[infIdx].Peer && - i == int(c.dec.FirstHopPerSeg[infIdx-1]) && - i < len(c.dec.HopFields)-1 { - - // First hop after Crossover, nothing to be done. - continue - } - - // Setting egress: if crossing over then egress ID comes from the next hop in next segment. - offset := 0 - if xover { - offset = 1 - } - // We use the path metadata to get the IA from it. This sequence of interfaces does not - // include the egress-to-ingress crossed over in the core AS. - pathInterfaces := p.Metadata().Interfaces - - c.hops = append(c.hops, Hop{ - BaseHop: BaseHop{ - // To get the current PathInterface we use `len(c.hops)` instead of `i` because - // we want to skip those crossed-over interfaces. - IA: pathInterfaces[hopIndexToPathInterfaceIndex(len(c.hops))].IA, - Ingress: getIfaceID(c.dec, infIdx, i, false), - Egress: getIfaceID(c.dec, infIdx+offset, i+offset, true), - }, - infIdx: infIdx, - hopfield: &c.dec.HopFields[i], - scionMac: c.dec.HopFields[i].HopField.Mac, - reservations: make([]Flyover, 0, 2), - }) - } - - return nil -} - func (c *Reservation) prepareHbirdPath(p snet.Path) error { switch p := p.Dataplane().(type) { case path.SCION: @@ -199,23 +125,7 @@ func (c *Reservation) prepareHbirdPath(p snet.Path) error { return nil } -func iaID(ifaces []snet.PathInterface, idx int) addr.IA { - i := idx*2 - 1 - if i < 0 { - i = 0 - } - return ifaces[i].IA -} - -func ingressID(ifaces []snet.PathInterface, idx int) uint16 { - i := idx*2 - 1 - if i < 0 { - return 0 - } - return uint16(ifaces[i].ID) -} - -// For each hop in the path, returns a reservation containing the AS, Ingress and Egress of that hop +// For each hop in the path, returns a flyover containing the AS, Ingress and Egress of that hop func (c *Reservation) GetPathASes() []BaseHop { hops := make([]BaseHop, len(c.hops)) for i, h := range c.hops { @@ -230,12 +140,12 @@ func (c *Reservation) Destination() addr.IA { return c.hops[len(c.hops)-1].IA } -// Request reservations for the full path +// Request flyovers for the full path // bw: the bandwidth to request -// start: The start time of the reservation, in unix seconds -// duration: The duration of the reservation in seconds +// start: The start time of the flyover, in unix seconds +// duration: The duration of the flyover in seconds // TODO: add async version once we have request api -func (c *Reservation) RequestReservationsAllHops( +func (c *Reservation) RequestFlyoversAllHops( bw uint16, start uint32, duration uint16) ([]Flyover, error) { hops := make([]BaseHop, len(c.hops)) for i, h := range c.hops { @@ -244,18 +154,18 @@ func (c *Reservation) RequestReservationsAllHops( hops[i].Egress = h.Egress } - return RequestReservationForASes(hops, bw, start, duration) + return RequestFlyoversForASes(hops, bw, start, duration) } -// Requests new reservations for the listed Hops and returns them once they are obtained -// TODO: add timeout after which already received reservations +// Requests new flyovers for the listed Hops and returns them once they are obtained +// TODO: add timeout after which already received flyovers // (if any) are returned once we have actual requests // TODO: add fully async version of this -func RequestReservationForASes( +func RequestFlyoversForASes( hops []BaseHop, bw uint16, start uint32, duration uint16) ([]Flyover, error) { - log.Debug("Requesting reservations for", "Hops", hops) - reservations := make([]Flyover, len(hops)) + log.Debug("Requesting flyovers for", "Hops", hops) + flyovers := make([]Flyover, len(hops)) for i, h := range hops { //TODO: Once we have API for requests // Request (AS, ingress, egress, bw, start, duration) @@ -263,31 +173,31 @@ func RequestReservationForASes( // Temporary Cheating // Current implementation cheats by writing data directly into c.hops instead - reservations[i].IA = h.IA - reservations[i].Ingress = h.Ingress - reservations[i].Egress = h.Egress - reservations[i].Bw = bw - reservations[i].StartTime = start - reservations[i].Duration = duration + flyovers[i].IA = h.IA + flyovers[i].Ingress = h.Ingress + flyovers[i].Egress = h.Egress + flyovers[i].Bw = bw + flyovers[i].StartTime = start + flyovers[i].Duration = duration var err error - reservations[i], err = cheat_auth_key(&reservations[i]) + flyovers[i], err = cheat_auth_key(&flyovers[i]) if err != nil { return nil, err } } - return reservations, nil + return flyovers, nil } // Adds the listed reservations to the path -func (c *Reservation) ApplyReservations(flyovers []Flyover) error { - log.Debug("Applying reservations", "reservations", flyovers) +func (c *Reservation) Applyflyovers(flyovers []Flyover) error { + log.Debug("Applying flyovers", "flyovers", flyovers) for _, f := range flyovers { for j, h := range c.hops { if f.IA == h.IA { if f.Ingress == h.Ingress && f.Egress == h.Egress { - c.hops[j].reservations = append(c.hops[j].reservations, f) - if len(c.hops[j].reservations) == 1 { + c.hops[j].flyovers = append(c.hops[j].flyovers, f) + if len(c.hops[j].flyovers) == 1 { c.hops[j].hopfield.Flyover = true c.dec.NumLines += 2 c.dec.PathMeta.SegLen[h.infIdx] += 2 @@ -296,7 +206,7 @@ func (c *Reservation) ApplyReservations(flyovers []Flyover) error { c.hops[j].hopfield.ResID = f.ResID } } else { - // TODO: inform caller that this reservation cannot be set on this path + // TODO: inform caller that this flyover cannot be set on this path break } } @@ -310,7 +220,7 @@ func (c *Reservation) applyFlyovers(flyovers map[addr.IA][]*Flyover) { flyovers := flyovers[h.IA] for _, flyover := range flyovers { if flyover.Ingress == h.Ingress && flyover.Egress == h.Egress { - c.hops[i].reservations = append(c.hops[i].reservations, *flyover) + c.hops[i].flyovers = append(c.hops[i].flyovers, *flyover) c.hops[i].hopfield.Flyover = true c.dec.NumLines += 2 c.dec.PathMeta.SegLen[h.infIdx] += 2 @@ -323,54 +233,54 @@ func (c *Reservation) applyFlyovers(flyovers map[addr.IA][]*Flyover) { } } -// Returns all the reservations that the client may currently use -// If there are multiple reservations for a hop, +// Returns all the flyovers that the client may currently use +// If there are multiple flyovers for a hop, // The one currently used is the first appearing in the returned array -func (c *Reservation) GetUsedReservations() []Flyover { +func (c *Reservation) GetUsedFlyovers() []Flyover { res := make([]Flyover, 0, len(c.hops)) for _, h := range c.hops { - res = append(res, h.reservations...) + res = append(res, h.flyovers...) } return res } -// Removes the reservation with the given resID from a hop -func (c *Reservation) removeReservation(hopIdx int, resID uint32) { +// Removes the flyovers with the given resID from a hop. +func (c *Reservation) removeFlyover(hopIdx int, resID uint32) { h := &c.hops[hopIdx] - for i, r := range h.reservations { + for i, r := range h.flyovers { if r.ResID == resID { if i == 0 { - if len(h.reservations) == 1 { + if len(h.flyovers) == 1 { h.hopfield.Flyover = false c.dec.NumLines -= 2 c.dec.PathMeta.SegLen[c.hops[hopIdx].infIdx] -= 2 - h.reservations = []Flyover{} + h.flyovers = []Flyover{} } else { - copy(h.reservations[:], h.reservations[1:]) - h.reservations = h.reservations[:len(h.reservations)-1] - h.hopfield.Bw = h.reservations[0].Bw - h.hopfield.ResID = h.reservations[0].ResID - h.hopfield.Duration = h.reservations[0].Duration + copy(h.flyovers[:], h.flyovers[1:]) + h.flyovers = h.flyovers[:len(h.flyovers)-1] + h.hopfield.Bw = h.flyovers[0].Bw + h.hopfield.ResID = h.flyovers[0].ResID + h.hopfield.Duration = h.flyovers[0].Duration } } else { - if i < len(h.reservations)-1 { - copy(h.reservations[i:], h.reservations[i+1:]) + if i < len(h.flyovers)-1 { + copy(h.flyovers[i:], h.flyovers[i+1:]) } - h.reservations = h.reservations[:len(h.reservations)-1] + h.flyovers = h.flyovers[:len(h.flyovers)-1] } break } } } -// Removes res from the reservations the client is allowed to use -// Reservations are identified based on their AS and ResID -// Does NOT check for validity of remaining reservations -func (c *Reservation) RemoveReservations(res []Flyover) error { - for _, r := range res { +// Removes res from the flyovers the client is allowed to use +// Flyovers are identified based on their AS and ResID +// Does NOT check for validity of remaining flyovers. +func (c *Reservation) RemoveFlyovers(flyovers []Flyover) error { + for _, r := range flyovers { for i, h := range c.hops { if r.IA == h.IA { - c.removeReservation(i, r.ResID) + c.removeFlyover(i, r.ResID) break } } @@ -378,24 +288,24 @@ func (c *Reservation) RemoveReservations(res []Flyover) error { return nil } -// Checks whether any current reservation that has expired or will expire in t seconds -// If yes, remove reservation from list of used reservations -func (c *Reservation) CheckExpiry(t uint32) { +// Checks whether any current flyover that has expired or will expire in t seconds +// If yes, remove flyover from list of used flyovers. +func (c *Reservation) CheckExpiry(duration uint32) { now := uint32(time.Now().Unix()) for i := range c.hops { - // Remove expired reservations - for j := 0; j < len(c.hops[i].reservations); { - if c.hops[i].reservations[j].StartTime+uint32(c.hops[i].reservations[j].Duration) < - (now + t) { - copy(c.hops[i].reservations[j:], c.hops[i].reservations[j+1:]) - c.hops[i].reservations = c.hops[i].reservations[:len(c.hops[i].reservations)-1] + // Remove expired flyovers. + for j := 0; j < len(c.hops[i].flyovers); { + if c.hops[i].flyovers[j].StartTime+uint32(c.hops[i].flyovers[j].Duration) < + (now + duration) { + copy(c.hops[i].flyovers[j:], c.hops[i].flyovers[j+1:]) + c.hops[i].flyovers = c.hops[i].flyovers[:len(c.hops[i].flyovers)-1] } else { j++ } } - if len(c.hops[i].reservations) == 0 { + if len(c.hops[i].flyovers) == 0 { if c.hops[i].hopfield.Flyover { c.hops[i].hopfield.Flyover = false c.dec.NumLines -= 2 @@ -403,28 +313,28 @@ func (c *Reservation) CheckExpiry(t uint32) { } continue } - // If there's any currently valid reservation, put it to the front - if !(c.hops[i].reservations[0].StartTime <= now) { - for j := 1; j < len(c.hops[i].reservations); j++ { - if c.hops[i].reservations[j].StartTime <= now { - temp := c.hops[i].reservations[0] - c.hops[i].reservations[0] = c.hops[i].reservations[j] - c.hops[i].reservations[j] = temp + // If there's any currently valid flyover, put it to the front + if !(c.hops[i].flyovers[0].StartTime <= now) { + for j := 1; j < len(c.hops[i].flyovers); j++ { + if c.hops[i].flyovers[j].StartTime <= now { + temp := c.hops[i].flyovers[0] + c.hops[i].flyovers[0] = c.hops[i].flyovers[j] + c.hops[i].flyovers[j] = temp break } } } - // Check whether reservation at the front is currently valid - if c.hops[i].reservations[0].StartTime <= now { + // Check whether flyover at the front is currently valid + if c.hops[i].flyovers[0].StartTime <= now { if !c.hops[i].hopfield.Flyover { c.hops[i].hopfield.Flyover = true c.dec.NumLines += 2 c.dec.PathMeta.SegLen[c.hops[i].infIdx] += 2 } - c.hops[i].hopfield.Bw = c.hops[i].reservations[0].Bw - c.hops[i].hopfield.Duration = c.hops[i].reservations[0].Duration - c.hops[i].hopfield.ResID = c.hops[i].reservations[0].ResID + c.hops[i].hopfield.Bw = c.hops[i].flyovers[0].Bw + c.hops[i].hopfield.Duration = c.hops[i].flyovers[0].Duration + c.hops[i].hopfield.ResID = c.hops[i].flyovers[0].ResID } else { if c.hops[i].hopfield.Flyover { c.hops[i].hopfield.Flyover = false @@ -462,7 +372,7 @@ func (c *Reservation) FinalizePath(p snet.Path, pktLen uint16, if !h.hopfield.Flyover { continue } - res := h.reservations[0] + res := h.flyovers[0] h.hopfield.ResStartTime = uint16(secs - res.StartTime) flyovermac := hummingbird.FullFlyoverMac(res.Ak[:], c.Destination(), pktLen, h.hopfield.ResStartTime, millis, c.byteBuffer[:], c.xkbuffer[:]) @@ -494,10 +404,10 @@ func newHop(ia addr.IA, in, eg uint16, infIdx int, hf *hummingbird.FlyoverHopFie Ingress: in, Egress: eg, }, - infIdx: infIdx, - hopfield: hf, - scionMac: hf.HopField.Mac, - reservations: make([]Flyover, 0, 2), + infIdx: infIdx, + hopfield: hf, + scionMac: hf.HopField.Mac, + flyovers: make([]Flyover, 0, 2), } } diff --git a/pkg/hummingbird/reservation_test.go b/pkg/hummingbird/reservation_test.go index acdbbeb720..f05d7219e7 100644 --- a/pkg/hummingbird/reservation_test.go +++ b/pkg/hummingbird/reservation_test.go @@ -152,11 +152,11 @@ func TestCheckReservationExpiry(t *testing.T) { c, err := hummingbird.NewReservation(scionPath, nil) assert.NoError(t, err) - err = c.ApplyReservations(input) + err = c.Applyflyovers(input) assert.NoError(t, err) c.CheckExpiry(5) - assert.Equal(t, expected, c.GetUsedReservations()) + assert.Equal(t, expected, c.GetUsedFlyovers()) // Verify last reservation is unused as it is not yet valid scionPath = getScionSnetPath(t) @@ -179,7 +179,7 @@ func TestRemoveReservations(t *testing.T) { c, err := hummingbird.NewReservation(scionPath, nil) require.NoError(t, err) - err = c.ApplyReservations(testFlyovers) + err = c.Applyflyovers(testFlyovers) assert.NoError(t, err) remove := []hummingbird.Flyover{ @@ -222,10 +222,10 @@ func TestRemoveReservations(t *testing.T) { }, } - err = c.RemoveReservations(remove) + err = c.RemoveFlyovers(remove) assert.NoError(t, err) - output := c.GetUsedReservations() + output := c.GetUsedFlyovers() assert.Equal(t, expected, output) // Verify removal has resulted in correct path diff --git a/tools/end2end_hbird/main.go b/tools/end2end_hbird/main.go index 3ca8372e5e..2db311bec4 100644 --- a/tools/end2end_hbird/main.go +++ b/tools/end2end_hbird/main.go @@ -323,13 +323,13 @@ func (c *client) attemptRequest(n int) bool { return false } secs := uint32(time.Now().Unix()) - res, err := hbirdClient.RequestReservationsAllHops(16, secs, 120) + res, err := hbirdClient.RequestFlyoversAllHops(16, secs, 120) if err != nil { logger.Error("Error requesting reservations", "err", err) return false } - if err := hbirdClient.ApplyReservations(res); err != nil { + if err := hbirdClient.Applyflyovers(res); err != nil { logger.Error("Error applying reservations", "err", err) } @@ -353,13 +353,13 @@ func (c *client) attemptRequest(n int) bool { } ases = ases[:n] secs := uint32(time.Now().Unix()) - res, err := hummingbird.RequestReservationForASes(ases, 16, secs, 120) + res, err := hummingbird.RequestFlyoversForASes(ases, 16, secs, 120) if err != nil { logger.Error("Error requesting reservations", "err", err) return false } - if err := hbirdClient.ApplyReservations(res); err != nil { + if err := hbirdClient.Applyflyovers(res); err != nil { logger.Error("Error applying reservations", "err", err) } From 7bfd7763a794ee4686464b22d4c80665df33bb89 Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Thu, 14 Dec 2023 19:29:08 +0100 Subject: [PATCH 077/100] Modified Reservation internals. WIP. The assignment of the flyover is equivalent. Removed unnecessary fields from Hop and Reservation. --- pkg/hummingbird/hummingbird.go | 3 - pkg/hummingbird/hummingbird_test.go | 51 +++- pkg/hummingbird/reservation.go | 372 ++++++-------------------- pkg/hummingbird/reservation_test.go | 397 +++++++++++++--------------- tools/end2end_hbird/main.go | 88 +++--- 5 files changed, 370 insertions(+), 541 deletions(-) diff --git a/pkg/hummingbird/hummingbird.go b/pkg/hummingbird/hummingbird.go index 094ded0f01..87864bb844 100644 --- a/pkg/hummingbird/hummingbird.go +++ b/pkg/hummingbird/hummingbird.go @@ -53,9 +53,6 @@ type Flyover struct { StartTime uint32 // Duration is the duration of the reservation in seconds Duration uint16 - // EndTime is the unix timestamp at which the reservation ends. - // Is not strictly necessary but included for simplicity - EndTime uint32 } // Temporary cheating function until the system to request keys is available diff --git a/pkg/hummingbird/hummingbird_test.go b/pkg/hummingbird/hummingbird_test.go index a1bb4f3ed9..fcfb9196b2 100644 --- a/pkg/hummingbird/hummingbird_test.go +++ b/pkg/hummingbird/hummingbird_test.go @@ -41,6 +41,35 @@ var testHops = []hummingbird.BaseHop{ } var testFlyovers = []hummingbird.Flyover{ + // For the first hop: + { + BaseHop: testHops[0], + ResID: 1234, + Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + Bw: 16, + Duration: 70, + StartTime: uint32(fixedTime.Unix()) - 80, // expired + }, + { + BaseHop: hummingbird.BaseHop{ + IA: testHops[0].IA, + Ingress: testHops[0].Ingress, + Egress: testHops[0].Egress + 1, // not the expected egress + }, + ResID: 1234, + Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + Bw: 16, + Duration: 120, + StartTime: uint32(fixedTime.Unix()) - 10, + }, + { + BaseHop: testHops[0], + ResID: 1234, + Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + Bw: 16, + Duration: 120, + StartTime: uint32(fixedTime.Unix()) - 1000, // expired + }, { BaseHop: testHops[0], ResID: 1234, @@ -49,6 +78,16 @@ var testFlyovers = []hummingbird.Flyover{ Duration: 120, StartTime: uint32(fixedTime.Unix()) - 10, }, + + // For the second hop: + { + BaseHop: testHops[1], + ResID: 42, + Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4, 3, 2, 1, 0}, + Bw: 16, + Duration: 180, + StartTime: uint32(fixedTime.Unix()) - 1000, + }, { BaseHop: testHops[1], ResID: 42, @@ -57,6 +96,16 @@ var testFlyovers = []hummingbird.Flyover{ Duration: 180, StartTime: uint32(fixedTime.Unix()) - 32, }, + + // For the third hop: + { + BaseHop: testHops[2], + ResID: 365, + Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7}, + Bw: 20, + Duration: 150, + StartTime: uint32(fixedTime.Unix()) + 1, // not yet valid + }, { BaseHop: testHops[2], ResID: 365, @@ -76,7 +125,7 @@ var testFlyovers = []hummingbird.Flyover{ } func TestConvertToHbirdPath(t *testing.T) { - scionPath:= getScionSnetPath(t) + scionPath := getScionSnetPath(t) now := time.Now() expectecPath, err := getHbirdNoFlyoversSnetPath(now) diff --git a/pkg/hummingbird/reservation.go b/pkg/hummingbird/reservation.go index a3fedb2ab1..88409f3866 100644 --- a/pkg/hummingbird/reservation.go +++ b/pkg/hummingbird/reservation.go @@ -19,7 +19,6 @@ import ( "time" "github.com/scionproto/scion/pkg/addr" - "github.com/scionproto/scion/pkg/log" "github.com/scionproto/scion/pkg/private/serrors" "github.com/scionproto/scion/pkg/slayers/path/hummingbird" "github.com/scionproto/scion/pkg/slayers/path/scion" @@ -49,60 +48,87 @@ func (r ReservationJuanDeleteme) LessThan(other *ReservationJuanDeleteme) bool { // Reservation represents a possibly partially reserved path, with zero or more flyovers. // TODO(juagargi) refactor functionality in two types: Reservation and move the rest to sciond. type Reservation struct { - dec hummingbird.Decoded // caches a decoded path for multiple uses - hops []Hop // possible flyovers - counter uint32 // duplicate detection counter - byteBuffer [hummingbird.FlyoverMacBufferSize]byte - xkbuffer [hummingbird.XkBufferSize]uint32 + dec hummingbird.Decoded // caches a decoded path for multiple uses + hops []Hop // possible flyovers + counter uint32 // duplicate detection counter + + now time.Time // the current time + interfaces []snet.PathInterface + flyovers FlyoverSet // the flyovers used to creatre this reservation } type Hop struct { BaseHop - // The FlyoverHopField in the path associated to this hop. - hopfield *hummingbird.FlyoverHopField - // The Index of the Segment the above hopfield is part of. - infIdx int - // The flyovers that can be used for this hop. - // The flyover at index 0 is the one used to build the path - // MUST be non-empty if hopfield.Flyiver == true - flyovers []Flyover - // The original scion mac of the corresponding hopfield. - scionMac [6]byte + hopfield *hummingbird.FlyoverHopField // dataplane hop field + flyover *Flyover // flyover used to build this hop } -func NewReservation(p snet.Path, flyovers map[addr.IA][]*Flyover) (*Reservation, error) { - c := &Reservation{} - err := c.prepareHbirdPath(p) - if err != nil { - return nil, err +func NewReservation(opts ...reservationModFcn) (*Reservation, error) { + c := &Reservation{ + now: time.Now(), + } + // Run all options on this object. + for _, fcn := range opts { + if err := fcn(c); err != nil { + return nil, err + } } - c.applyFlyovers(flyovers) + + // Create reservation. + c.prepareHbirdPath() + c.applyFlyovers() + return c, nil } -func (c *Reservation) prepareHbirdPath(p snet.Path) error { - switch p := p.Dataplane().(type) { - case path.SCION: - scion := scion.Decoded{} - if err := scion.DecodeFromBytes(p.Raw); err != nil { - return serrors.Join(err, serrors.New("Failed to Prepare Hummingbird Path")) +type reservationModFcn func(*Reservation) error + +func WithPath(p snet.Path) reservationModFcn { + return func(r *Reservation) error { + switch p := p.Dataplane().(type) { + case path.SCION: + scion := scion.Decoded{} + if err := scion.DecodeFromBytes(p.Raw); err != nil { + return serrors.Join(err, serrors.New("Failed to Prepare Hummingbird Path")) + } + r.dec = hummingbird.Decoded{} + r.dec.ConvertFromScionDecoded(scion) + default: + return serrors.New("Unsupported path type") } - c.dec = hummingbird.Decoded{} - c.dec.ConvertFromScionDecoded(scion) - default: - return serrors.New("Unsupported path type") + // We use the path metadata to get the IA from it. This sequence of interfaces does not + // include the egress-to-ingress crossed over interfaces in the core AS. + r.interfaces = p.Metadata().Interfaces + + return nil } +} + +type FlyoverSet map[BaseHop][]*Flyover + +func WithFlyovers(flyovers FlyoverSet) reservationModFcn { + return func(r *Reservation) error { + r.flyovers = flyovers + return nil + } +} - // We use the path metadata to get the IA from it. This sequence of interfaces does not - // include the egress-to-ingress crossed over interfaces in the core AS. - pathInterfaces := p.Metadata().Interfaces +func WithNow(now time.Time) reservationModFcn { + return func(r *Reservation) error { + r.now = now + return nil + } +} +// func (c *Reservation) prepareHbirdPath(p snet.Path) error { +func (c *Reservation) prepareHbirdPath() { + c.dec.PathMeta.SegLen[0] += 2 c.hops = append(c.hops, newHop( - pathInterfaces[0].IA, - 0, - uint16(pathInterfaces[0].ID), + c.interfaces[0].IA, 0, + uint16(c.interfaces[0].ID), + // 0, &c.dec.HopFields[0], )) @@ -110,120 +136,33 @@ func (c *Reservation) prepareHbirdPath(p snet.Path) error { // Do each segment at a time to ignore the first hop of every segment except the first. hopIdx := 1 // the index of the current hop in the dataplane. for infIdx := 0; infIdx < c.dec.NumINF; infIdx, hopIdx = infIdx+1, hopIdx+1 { - for i := 1; i < int( - c.dec.Base.PathMeta.SegLen[infIdx])/hummingbird.HopLines; i, hopIdx = i+1, hopIdx+1 { - + // Preserve the hopcount locally, as we modify it inside the loop itself. + hopCount := int(c.dec.Base.PathMeta.SegLen[infIdx]) / hummingbird.HopLines + for i := 1; i < hopCount; i, hopIdx = i+1, hopIdx+1 { + c.dec.PathMeta.SegLen[infIdx] += 2 c.hops = append(c.hops, newHop( - pathInterfaces[len(c.hops)*2-1].IA, - uint16(pathInterfaces[len(c.hops)*2-1].ID), - egressID(pathInterfaces, len(c.hops)), - infIdx, + c.interfaces[len(c.hops)*2-1].IA, + uint16(c.interfaces[len(c.hops)*2-1].ID), + egressID(c.interfaces, len(c.hops)), &c.dec.HopFields[hopIdx], )) } } - return nil -} - -// For each hop in the path, returns a flyover containing the AS, Ingress and Egress of that hop -func (c *Reservation) GetPathASes() []BaseHop { - hops := make([]BaseHop, len(c.hops)) - for i, h := range c.hops { - hops[i].IA = h.IA - hops[i].Ingress = h.Ingress - hops[i].Egress = h.Egress - } - return hops } func (c *Reservation) Destination() addr.IA { return c.hops[len(c.hops)-1].IA } -// Request flyovers for the full path -// bw: the bandwidth to request -// start: The start time of the flyover, in unix seconds -// duration: The duration of the flyover in seconds -// TODO: add async version once we have request api -func (c *Reservation) RequestFlyoversAllHops( - bw uint16, start uint32, duration uint16) ([]Flyover, error) { - hops := make([]BaseHop, len(c.hops)) +func (c *Reservation) applyFlyovers() { + now := uint32(c.now.Unix()) for i, h := range c.hops { - hops[i].IA = h.IA - hops[i].Ingress = h.Ingress - hops[i].Egress = h.Egress - } - - return RequestFlyoversForASes(hops, bw, start, duration) -} - -// Requests new flyovers for the listed Hops and returns them once they are obtained -// TODO: add timeout after which already received flyovers -// (if any) are returned once we have actual requests -// TODO: add fully async version of this -func RequestFlyoversForASes( - hops []BaseHop, bw uint16, start uint32, duration uint16) ([]Flyover, error) { - - log.Debug("Requesting flyovers for", "Hops", hops) - flyovers := make([]Flyover, len(hops)) - for i, h := range hops { - //TODO: Once we have API for requests - // Request (AS, ingress, egress, bw, start, duration) - - // Temporary Cheating - // Current implementation cheats by writing data directly into c.hops instead - - flyovers[i].IA = h.IA - flyovers[i].Ingress = h.Ingress - flyovers[i].Egress = h.Egress - flyovers[i].Bw = bw - flyovers[i].StartTime = start - flyovers[i].Duration = duration - - var err error - flyovers[i], err = cheat_auth_key(&flyovers[i]) - if err != nil { - return nil, err - } - } - return flyovers, nil -} - -// Adds the listed reservations to the path -func (c *Reservation) Applyflyovers(flyovers []Flyover) error { - log.Debug("Applying flyovers", "flyovers", flyovers) - for _, f := range flyovers { - for j, h := range c.hops { - if f.IA == h.IA { - if f.Ingress == h.Ingress && f.Egress == h.Egress { - c.hops[j].flyovers = append(c.hops[j].flyovers, f) - if len(c.hops[j].flyovers) == 1 { - c.hops[j].hopfield.Flyover = true - c.dec.NumLines += 2 - c.dec.PathMeta.SegLen[h.infIdx] += 2 - c.hops[j].hopfield.Bw = f.Bw - c.hops[j].hopfield.Duration = f.Duration - c.hops[j].hopfield.ResID = f.ResID - } - } else { - // TODO: inform caller that this flyover cannot be set on this path - break - } - } - } - } - return nil -} - -func (c *Reservation) applyFlyovers(flyovers map[addr.IA][]*Flyover) { - for i, h := range c.hops { - flyovers := flyovers[h.IA] + flyovers := c.flyovers[h.BaseHop] for _, flyover := range flyovers { - if flyover.Ingress == h.Ingress && flyover.Egress == h.Egress { - c.hops[i].flyovers = append(c.hops[i].flyovers, *flyover) + if flyover.StartTime <= now && uint32(flyover.Duration) >= now-flyover.StartTime { + c.hops[i].flyover = flyover c.hops[i].hopfield.Flyover = true c.dec.NumLines += 2 - c.dec.PathMeta.SegLen[h.infIdx] += 2 c.hops[i].hopfield.Bw = flyover.Bw c.hops[i].hopfield.Duration = flyover.Duration c.hops[i].hopfield.ResID = flyover.ResID @@ -233,122 +172,10 @@ func (c *Reservation) applyFlyovers(flyovers map[addr.IA][]*Flyover) { } } -// Returns all the flyovers that the client may currently use -// If there are multiple flyovers for a hop, -// The one currently used is the first appearing in the returned array -func (c *Reservation) GetUsedFlyovers() []Flyover { - res := make([]Flyover, 0, len(c.hops)) - for _, h := range c.hops { - res = append(res, h.flyovers...) - } - return res -} - -// Removes the flyovers with the given resID from a hop. -func (c *Reservation) removeFlyover(hopIdx int, resID uint32) { - h := &c.hops[hopIdx] - for i, r := range h.flyovers { - if r.ResID == resID { - if i == 0 { - if len(h.flyovers) == 1 { - h.hopfield.Flyover = false - c.dec.NumLines -= 2 - c.dec.PathMeta.SegLen[c.hops[hopIdx].infIdx] -= 2 - h.flyovers = []Flyover{} - } else { - copy(h.flyovers[:], h.flyovers[1:]) - h.flyovers = h.flyovers[:len(h.flyovers)-1] - h.hopfield.Bw = h.flyovers[0].Bw - h.hopfield.ResID = h.flyovers[0].ResID - h.hopfield.Duration = h.flyovers[0].Duration - } - } else { - if i < len(h.flyovers)-1 { - copy(h.flyovers[i:], h.flyovers[i+1:]) - } - h.flyovers = h.flyovers[:len(h.flyovers)-1] - } - break - } - } -} - -// Removes res from the flyovers the client is allowed to use -// Flyovers are identified based on their AS and ResID -// Does NOT check for validity of remaining flyovers. -func (c *Reservation) RemoveFlyovers(flyovers []Flyover) error { - for _, r := range flyovers { - for i, h := range c.hops { - if r.IA == h.IA { - c.removeFlyover(i, r.ResID) - break - } - } - } - return nil -} - -// Checks whether any current flyover that has expired or will expire in t seconds -// If yes, remove flyover from list of used flyovers. -func (c *Reservation) CheckExpiry(duration uint32) { - now := uint32(time.Now().Unix()) - for i := range c.hops { - - // Remove expired flyovers. - for j := 0; j < len(c.hops[i].flyovers); { - if c.hops[i].flyovers[j].StartTime+uint32(c.hops[i].flyovers[j].Duration) < - (now + duration) { - copy(c.hops[i].flyovers[j:], c.hops[i].flyovers[j+1:]) - c.hops[i].flyovers = c.hops[i].flyovers[:len(c.hops[i].flyovers)-1] - } else { - j++ - } - } - - if len(c.hops[i].flyovers) == 0 { - if c.hops[i].hopfield.Flyover { - c.hops[i].hopfield.Flyover = false - c.dec.NumLines -= 2 - c.dec.PathMeta.SegLen[c.hops[i].infIdx] -= 2 - } - continue - } - // If there's any currently valid flyover, put it to the front - if !(c.hops[i].flyovers[0].StartTime <= now) { - for j := 1; j < len(c.hops[i].flyovers); j++ { - if c.hops[i].flyovers[j].StartTime <= now { - temp := c.hops[i].flyovers[0] - c.hops[i].flyovers[0] = c.hops[i].flyovers[j] - c.hops[i].flyovers[j] = temp - break - } - } - } - - // Check whether flyover at the front is currently valid - if c.hops[i].flyovers[0].StartTime <= now { - if !c.hops[i].hopfield.Flyover { - c.hops[i].hopfield.Flyover = true - c.dec.NumLines += 2 - c.dec.PathMeta.SegLen[c.hops[i].infIdx] += 2 - } - c.hops[i].hopfield.Bw = c.hops[i].flyovers[0].Bw - c.hops[i].hopfield.Duration = c.hops[i].flyovers[0].Duration - c.hops[i].hopfield.ResID = c.hops[i].flyovers[0].ResID - } else { - if c.hops[i].hopfield.Flyover { - c.hops[i].hopfield.Flyover = false - c.dec.NumLines -= 2 - c.dec.PathMeta.SegLen[c.hops[i].infIdx] -= 2 - } - } - } -} - // Sets pathmeta timestamps and increments duplicate detection counter. // Updates MACs of all flyoverfields // replaces the dataplane of the input snet.path with the finished hummingbird path -func (c *Reservation) FinalizePath(p snet.Path, pktLen uint16, +func (c *Reservation) DeriveDataPlanePath(p snet.Path, pktLen uint16, timeStamp time.Time) (snet.Path, error) { if p == nil { return nil, serrors.New("snet path is nil") @@ -368,19 +195,20 @@ func (c *Reservation) FinalizePath(p snet.Path, pktLen uint16, c.counter += 1 } // compute Macs for Flyovers + var byteBuffer [hummingbird.FlyoverMacBufferSize]byte + var xkbuffer [hummingbird.XkBufferSize]uint32 for _, h := range c.hops { if !h.hopfield.Flyover { continue } - res := h.flyovers[0] - h.hopfield.ResStartTime = uint16(secs - res.StartTime) - flyovermac := hummingbird.FullFlyoverMac(res.Ak[:], c.Destination(), pktLen, - h.hopfield.ResStartTime, millis, c.byteBuffer[:], c.xkbuffer[:]) + h.hopfield.ResStartTime = uint16(secs - h.flyover.StartTime) + flyovermac := hummingbird.FullFlyoverMac(h.flyover.Ak[:], c.Destination(), pktLen, + h.hopfield.ResStartTime, millis, byteBuffer[:], xkbuffer[:]) binary.BigEndian.PutUint32(h.hopfield.HopField.Mac[:4], - binary.BigEndian.Uint32(flyovermac[:4])^binary.BigEndian.Uint32(h.scionMac[:4])) + binary.BigEndian.Uint32(flyovermac[:4])^binary.BigEndian.Uint32(h.hopfield.HopField.Mac[:4])) binary.BigEndian.PutUint16(h.hopfield.HopField.Mac[4:], - binary.BigEndian.Uint16(flyovermac[4:])^binary.BigEndian.Uint16(h.scionMac[4:])) + binary.BigEndian.Uint16(flyovermac[4:])^binary.BigEndian.Uint16(h.hopfield.HopField.Mac[4:])) } dphb.Raw = make([]byte, c.dec.Len()) if err := c.dec.SerializeTo(dphb.Raw); err != nil { @@ -397,45 +225,15 @@ func (c *Reservation) FinalizePath(p snet.Path, pktLen uint16, return p, nil } -func newHop(ia addr.IA, in, eg uint16, infIdx int, hf *hummingbird.FlyoverHopField) Hop { +func newHop(ia addr.IA, in, eg uint16, hf *hummingbird.FlyoverHopField) Hop { return Hop{ BaseHop: BaseHop{ IA: ia, Ingress: in, Egress: eg, }, - infIdx: infIdx, hopfield: hf, - scionMac: hf.HopField.Mac, - flyovers: make([]Flyover, 0, 2), - } -} - -func getIfaceID(dec hummingbird.Decoded, segIdx, hopIdx int, returnEgress bool) uint16 { - in := dec.HopFields[hopIdx].HopField.ConsIngress - eg := dec.HopFields[hopIdx].HopField.ConsEgress - if !dec.InfoFields[segIdx].ConsDir { - returnEgress = !returnEgress - } - if returnEgress { - in = eg - } - return in -} - -// hopIndexToPathInterfaceIndex converts an index of a hummingbird hop into its corresponding -// index in the PathInterface sequence (found e.g. in snet.Path.Metadata()). -// This is not straightforward, as the PathInterface sequence omits the starting interface -// (its ID. is always 0 and same IA as next one), and ending interface (its ID is always 0 and -// the same IA as the previous one). -// Example: if we have four hummingbird hops, indexed as. 0,1,2,3, their corresponding indices -// in a PathInterface sequence would be 0,1,3,5. -func hopIndexToPathInterfaceIndex(i int) int { - idx := i*2 - 1 - if idx < 0 { - idx = 0 } - return idx } // egressID returns the egress ID from a sequence of IDs (such as that in the metadata field diff --git a/pkg/hummingbird/reservation_test.go b/pkg/hummingbird/reservation_test.go index f05d7219e7..3a16f4fcd5 100644 --- a/pkg/hummingbird/reservation_test.go +++ b/pkg/hummingbird/reservation_test.go @@ -16,38 +16,21 @@ package hummingbird_test import ( "testing" - "time" - "github.com/scionproto/scion/pkg/addr" "github.com/scionproto/scion/pkg/hummingbird" dphbird "github.com/scionproto/scion/pkg/slayers/path/hummingbird" - snetpath "github.com/scionproto/scion/pkg/snet/path" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -// deleteme remove this test func TestPrepareHbirdPath(t *testing.T) { scionPath := getScionSnetPath(t) - now := time.Now() - flyovers := flyoverSliceToMap(testFlyovers) - flyovers = nil - c, err := hummingbird.NewReservation(scionPath, flyovers) - require.NoError(t, err) - scionPath = getScionSnetPath(t) - output, err := c.FinalizePath(scionPath, 0, now) - assert.NoError(t, err) - expectecPath, err := getHbirdNoFlyoversSnetPath(now) - assert.NoError(t, err) - assert.Equal(t, expectecPath, output) -} - -// deleteme rename this test as TestPrepareHbirdPath -func TestApplyReservations(t *testing.T) { - scionPath := getScionSnetPath(t) - - c, err := hummingbird.NewReservation(scionPath, flyoverSliceToMap(testFlyovers)) + c, err := hummingbird.NewReservation( + hummingbird.WithPath(scionPath), + hummingbird.WithFlyovers(flyoverSliceToMap(testFlyovers)), + hummingbird.WithNow(fixedTime), + ) require.NoError(t, err) scionPath = getScionSnetPath(t) @@ -56,192 +39,192 @@ func TestApplyReservations(t *testing.T) { hbirdPath, err := getHbirdFlyoversSnetPath(fixedTime) assert.NoError(t, err) - output, err := c.FinalizePath(scionPath, 16, fixedTime) + output, err := c.DeriveDataPlanePath(scionPath, 16, fixedTime) assert.NoError(t, err) assert.Equal(t, hbirdPath, output) } -func TestCheckReservationExpiry(t *testing.T) { - - tnow := time.Now() - now := uint32(tnow.Unix()) - - // hop1: first reservation expired, second ok - // hop2: first reservation expired, second not started, third expired, fourth ok - // hop3: first not yet valid, second expired - input := []hummingbird.Flyover{ - { - BaseHop: testHops[0], - ResID: 1234, - Duration: 70, - StartTime: now - 80, - }, - { - BaseHop: testHops[0], - ResID: 34, - Duration: 560, - StartTime: now - 10, - }, - { - BaseHop: testHops[1], - ResID: 42, - Duration: 80, - StartTime: now - 100, - }, - { - BaseHop: testHops[1], - ResID: 31, - Duration: 389, - StartTime: now + 50, - }, - { - BaseHop: testHops[1], - ResID: 12, - Duration: 64, - StartTime: now - 60, - }, - { - BaseHop: testHops[1], - ResID: 5, - Duration: 180, - StartTime: now - 30, - }, - { - BaseHop: testHops[2], - ResID: 365, - Duration: 150, - StartTime: now + 60, - }, - { - BaseHop: testHops[2], - ResID: 345, - Duration: 150, - StartTime: now - 345, - }, - } - - expected := []hummingbird.Flyover{ - { - BaseHop: testHops[0], - ResID: 34, - Duration: 560, - StartTime: now - 10, - }, - { - BaseHop: testHops[1], - ResID: 5, - Duration: 180, - StartTime: now - 30, - }, - { - BaseHop: testHops[1], - ResID: 31, - Duration: 389, - StartTime: now + 50, - }, - { - BaseHop: testHops[2], - ResID: 365, - Duration: 150, - StartTime: now + 60, - }, - } - - scionPath := getScionSnetPath(t) - - c, err := hummingbird.NewReservation(scionPath, nil) - assert.NoError(t, err) - - err = c.Applyflyovers(input) - assert.NoError(t, err) - - c.CheckExpiry(5) - assert.Equal(t, expected, c.GetUsedFlyovers()) - - // Verify last reservation is unused as it is not yet valid - scionPath = getScionSnetPath(t) - - outPath, err := c.FinalizePath(scionPath, 16, tnow) - assert.NoError(t, err) - - raw := outPath.Dataplane().(snetpath.Hummingbird).Raw - - dec := decodeDataplane(t, raw) - assert.True(t, dec.HopFields[0].Flyover) - assert.True(t, dec.HopFields[1].Flyover) - assert.False(t, dec.HopFields[2].Flyover) - assert.False(t, dec.HopFields[3].Flyover) -} - -func TestRemoveReservations(t *testing.T) { - scionPath := getScionSnetPath(t) - - c, err := hummingbird.NewReservation(scionPath, nil) - require.NoError(t, err) - - err = c.Applyflyovers(testFlyovers) - assert.NoError(t, err) - - remove := []hummingbird.Flyover{ - { - BaseHop: hummingbird.BaseHop{ - IA: testHops[0].IA, - }, - ResID: 1234, - }, - { - BaseHop: hummingbird.BaseHop{ - IA: testHops[1].IA, - }, - ResID: 53, - }, - { - BaseHop: hummingbird.BaseHop{ - IA: testHops[2].IA, - }, - ResID: 365, - }, - } - - expected := []hummingbird.Flyover{ - { - BaseHop: testHops[1], - ResID: 42, - Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4, 3, 2, 1, 0}, - Bw: 16, - Duration: 180, - StartTime: uint32(fixedTime.Unix()) - 32, - }, - { - BaseHop: testHops[2], - ResID: 21, - Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7}, - Bw: 20, - Duration: 150, - StartTime: uint32(fixedTime.Unix()) - 10, - }, - } - - err = c.RemoveFlyovers(remove) - assert.NoError(t, err) - - output := c.GetUsedFlyovers() - assert.Equal(t, expected, output) - - // Verify removal has resulted in correct path - scionPath = getScionSnetPath(t) - - outPath, err := c.FinalizePath(scionPath, 16, time.Now()) - assert.NoError(t, err) - - raw := outPath.Dataplane().(snetpath.Hummingbird).Raw - - dec := decodeDataplane(t, raw) - assert.False(t, dec.HopFields[0].Flyover) - assert.True(t, dec.HopFields[1].Flyover) - assert.False(t, dec.HopFields[2].Flyover) - assert.True(t, dec.HopFields[3].Flyover) -} +// func TestCheckReservationExpiry(t *testing.T) { + +// tnow := time.Now() +// now := uint32(tnow.Unix()) + +// // hop1: first reservation expired, second ok +// // hop2: first reservation expired, second not started, third expired, fourth ok +// // hop3: first not yet valid, second expired +// input := []hummingbird.Flyover{ +// { +// BaseHop: testHops[0], +// ResID: 1234, +// Duration: 70, +// StartTime: now - 80, +// }, +// { +// BaseHop: testHops[0], +// ResID: 34, +// Duration: 560, +// StartTime: now - 10, +// }, +// { +// BaseHop: testHops[1], +// ResID: 42, +// Duration: 80, +// StartTime: now - 100, +// }, +// { +// BaseHop: testHops[1], +// ResID: 31, +// Duration: 389, +// StartTime: now + 50, +// }, +// { +// BaseHop: testHops[1], +// ResID: 12, +// Duration: 64, +// StartTime: now - 60, +// }, +// { +// BaseHop: testHops[1], +// ResID: 5, +// Duration: 180, +// StartTime: now - 30, +// }, +// { +// BaseHop: testHops[2], +// ResID: 365, +// Duration: 150, +// StartTime: now + 60, +// }, +// { +// BaseHop: testHops[2], +// ResID: 345, +// Duration: 150, +// StartTime: now - 345, +// }, +// } + +// expected := []hummingbird.Flyover{ +// { +// BaseHop: testHops[0], +// ResID: 34, +// Duration: 560, +// StartTime: now - 10, +// }, +// { +// BaseHop: testHops[1], +// ResID: 5, +// Duration: 180, +// StartTime: now - 30, +// }, +// { +// BaseHop: testHops[1], +// ResID: 31, +// Duration: 389, +// StartTime: now + 50, +// }, +// { +// BaseHop: testHops[2], +// ResID: 365, +// Duration: 150, +// StartTime: now + 60, +// }, +// } + +// scionPath := getScionSnetPath(t) + +// c, err := hummingbird.NewReservation(scionPath, nil) +// assert.NoError(t, err) + +// err = c.Applyflyovers(input) +// assert.NoError(t, err) + +// c.CheckExpiry(5) +// assert.Equal(t, expected, c.GetUsedFlyovers()) + +// // Verify last reservation is unused as it is not yet valid +// scionPath = getScionSnetPath(t) + +// outPath, err := c.FinalizePath(scionPath, 16, tnow) +// assert.NoError(t, err) + +// raw := outPath.Dataplane().(snetpath.Hummingbird).Raw + +// dec := decodeDataplane(t, raw) +// assert.True(t, dec.HopFields[0].Flyover) +// assert.True(t, dec.HopFields[1].Flyover) +// assert.False(t, dec.HopFields[2].Flyover) +// assert.False(t, dec.HopFields[3].Flyover) +// } + +// func TestRemoveReservations(t *testing.T) { +// scionPath := getScionSnetPath(t) + +// c, err := hummingbird.NewReservation(scionPath, nil) +// require.NoError(t, err) + +// err = c.Applyflyovers(testFlyovers) +// assert.NoError(t, err) + +// remove := []hummingbird.Flyover{ +// { +// BaseHop: hummingbird.BaseHop{ +// IA: testHops[0].IA, +// }, +// ResID: 1234, +// }, +// { +// BaseHop: hummingbird.BaseHop{ +// IA: testHops[1].IA, +// }, +// ResID: 53, +// }, +// { +// BaseHop: hummingbird.BaseHop{ +// IA: testHops[2].IA, +// }, +// ResID: 365, +// }, +// } + +// expected := []hummingbird.Flyover{ +// { +// BaseHop: testHops[1], +// ResID: 42, +// Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4, 3, 2, 1, 0}, +// Bw: 16, +// Duration: 180, +// StartTime: uint32(fixedTime.Unix()) - 32, +// }, +// { +// BaseHop: testHops[2], +// ResID: 21, +// Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7}, +// Bw: 20, +// Duration: 150, +// StartTime: uint32(fixedTime.Unix()) - 10, +// }, +// } + +// err = c.RemoveFlyovers(remove) +// assert.NoError(t, err) + +// output := c.GetUsedFlyovers() +// assert.Equal(t, expected, output) + +// // Verify removal has resulted in correct path +// scionPath = getScionSnetPath(t) + +// outPath, err := c.FinalizePath(scionPath, 16, time.Now()) +// assert.NoError(t, err) + +// raw := outPath.Dataplane().(snetpath.Hummingbird).Raw + +// dec := decodeDataplane(t, raw) +// assert.False(t, dec.HopFields[0].Flyover) +// assert.True(t, dec.HopFields[1].Flyover) +// assert.False(t, dec.HopFields[2].Flyover) +// assert.True(t, dec.HopFields[3].Flyover) +// } func decodeDataplane(t *testing.T, raw []byte) dphbird.Decoded { t.Helper() @@ -251,11 +234,11 @@ func decodeDataplane(t *testing.T, raw []byte) dphbird.Decoded { return dec } -func flyoverSliceToMap(flyovers []hummingbird.Flyover) map[addr.IA][]*hummingbird.Flyover { - m := make(map[addr.IA][]*hummingbird.Flyover) +func flyoverSliceToMap(flyovers []hummingbird.Flyover) hummingbird.FlyoverSet { + m := make(hummingbird.FlyoverSet) for _, flyover := range flyovers { clone := flyover - m[clone.IA] = append(m[clone.IA], &clone) + m[clone.BaseHop] = append(m[clone.BaseHop], &clone) } return m } diff --git a/tools/end2end_hbird/main.go b/tools/end2end_hbird/main.go index 2db311bec4..c9ceaadd3a 100644 --- a/tools/end2end_hbird/main.go +++ b/tools/end2end_hbird/main.go @@ -315,55 +315,57 @@ func (c *client) attemptRequest(n int) bool { return false } remote.Path = path.Dataplane() - } else if !partial { - // full path with reservations - hbirdClient, err := hummingbird.NewReservation(path, nil) - if err != nil { - logger.Error("Error converting path to Hummingbird", "err", err) - return false - } - secs := uint32(time.Now().Unix()) - res, err := hbirdClient.RequestFlyoversAllHops(16, secs, 120) - if err != nil { - logger.Error("Error requesting reservations", "err", err) - return false - } - - if err := hbirdClient.Applyflyovers(res); err != nil { - logger.Error("Error applying reservations", "err", err) - } - - path, err = hbirdClient.FinalizePath(path, pingPayloadLen, time.Now()) - if err != nil { - logger.Error("Error assembling hummingbird path", "err", err) - } - remote.Path = path.Dataplane() + // } else if !partial { + // // full path with reservations + // hbirdClient, err := hummingbird.NewReservation(path, nil) + // if err != nil { + // logger.Error("Error converting path to Hummingbird", "err", err) + // return false + // } + // secs := uint32(time.Now().Unix()) + // res, err := hbirdClient.RequestFlyoversAllHops(16, secs, 120) + // if err != nil { + // logger.Error("Error requesting reservations", "err", err) + // return false + // } + + // if err := hbirdClient.Applyflyovers(res); err != nil { + // logger.Error("Error applying reservations", "err", err) + // } + + // path, err = hbirdClient.FinalizePath(path, pingPayloadLen, time.Now()) + // if err != nil { + // logger.Error("Error assembling hummingbird path", "err", err) + // } + // remote.Path = path.Dataplane() } else { //partial reservations, alternating resrved and not reserved - hbirdClient, err := hummingbird.NewReservation(path, nil) + hbirdClient, err := hummingbird.NewReservation( + hummingbird.WithPath(path), hummingbird.WithFlyovers(nil)) + // hbirdClient, err := hummingbird.NewReservation(path, nil) if err != nil { logger.Error("Error converting path to Hummingbird", "err", err) return false } - ases := hbirdClient.GetPathASes() - n := len(ases) - for i := 1; i < n; i++ { - copy(ases[i:n-1], ases[i+1:n]) - n-- - } - ases = ases[:n] - secs := uint32(time.Now().Unix()) - res, err := hummingbird.RequestFlyoversForASes(ases, 16, secs, 120) - if err != nil { - logger.Error("Error requesting reservations", "err", err) - return false - } - - if err := hbirdClient.Applyflyovers(res); err != nil { - logger.Error("Error applying reservations", "err", err) - } - - path, err = hbirdClient.FinalizePath(path, pingPayloadLen, time.Now()) + // ases := hbirdClient.GetPathASes() + // n := len(ases) + // for i := 1; i < n; i++ { + // copy(ases[i:n-1], ases[i+1:n]) + // n-- + // } + // ases = ases[:n] + // secs := uint32(time.Now().Unix()) + // res, err := hummingbird.RequestFlyoversForASes(ases, 16, secs, 120) + // if err != nil { + // logger.Error("Error requesting reservations", "err", err) + // return false + // } + + // if err := hbirdClient.Applyflyovers(res); err != nil { + // logger.Error("Error applying reservations", "err", err) + // } + + path, err = hbirdClient.DeriveDataPlanePath(path, pingPayloadLen, time.Now()) if err != nil { logger.Error("Error assembling hummingbird path", "err", err) } From 362bce2372d14288486fbc5377131f45a1442a8c Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Thu, 14 Dec 2023 19:32:13 +0100 Subject: [PATCH 078/100] Rename receiver in Reservation methods. --- pkg/hummingbird/reservation.go | 74 +++++++++++++++++----------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/pkg/hummingbird/reservation.go b/pkg/hummingbird/reservation.go index 88409f3866..ea18868e5c 100644 --- a/pkg/hummingbird/reservation.go +++ b/pkg/hummingbird/reservation.go @@ -122,50 +122,50 @@ func WithNow(now time.Time) reservationModFcn { } // func (c *Reservation) prepareHbirdPath(p snet.Path) error { -func (c *Reservation) prepareHbirdPath() { - c.dec.PathMeta.SegLen[0] += 2 - c.hops = append(c.hops, newHop( - c.interfaces[0].IA, +func (r *Reservation) prepareHbirdPath() { + r.dec.PathMeta.SegLen[0] += 2 + r.hops = append(r.hops, newHop( + r.interfaces[0].IA, 0, - uint16(c.interfaces[0].ID), + uint16(r.interfaces[0].ID), // 0, - &c.dec.HopFields[0], + &r.dec.HopFields[0], )) // The dataplane path in c.dec contains inf fields and cross-over hops. // Do each segment at a time to ignore the first hop of every segment except the first. hopIdx := 1 // the index of the current hop in the dataplane. - for infIdx := 0; infIdx < c.dec.NumINF; infIdx, hopIdx = infIdx+1, hopIdx+1 { + for infIdx := 0; infIdx < r.dec.NumINF; infIdx, hopIdx = infIdx+1, hopIdx+1 { // Preserve the hopcount locally, as we modify it inside the loop itself. - hopCount := int(c.dec.Base.PathMeta.SegLen[infIdx]) / hummingbird.HopLines + hopCount := int(r.dec.Base.PathMeta.SegLen[infIdx]) / hummingbird.HopLines for i := 1; i < hopCount; i, hopIdx = i+1, hopIdx+1 { - c.dec.PathMeta.SegLen[infIdx] += 2 - c.hops = append(c.hops, newHop( - c.interfaces[len(c.hops)*2-1].IA, - uint16(c.interfaces[len(c.hops)*2-1].ID), - egressID(c.interfaces, len(c.hops)), - &c.dec.HopFields[hopIdx], + r.dec.PathMeta.SegLen[infIdx] += 2 + r.hops = append(r.hops, newHop( + r.interfaces[len(r.hops)*2-1].IA, + uint16(r.interfaces[len(r.hops)*2-1].ID), + egressID(r.interfaces, len(r.hops)), + &r.dec.HopFields[hopIdx], )) } } } -func (c *Reservation) Destination() addr.IA { - return c.hops[len(c.hops)-1].IA +func (r *Reservation) Destination() addr.IA { + return r.hops[len(r.hops)-1].IA } -func (c *Reservation) applyFlyovers() { - now := uint32(c.now.Unix()) - for i, h := range c.hops { - flyovers := c.flyovers[h.BaseHop] +func (r *Reservation) applyFlyovers() { + now := uint32(r.now.Unix()) + for i, h := range r.hops { + flyovers := r.flyovers[h.BaseHop] for _, flyover := range flyovers { if flyover.StartTime <= now && uint32(flyover.Duration) >= now-flyover.StartTime { - c.hops[i].flyover = flyover - c.hops[i].hopfield.Flyover = true - c.dec.NumLines += 2 - c.hops[i].hopfield.Bw = flyover.Bw - c.hops[i].hopfield.Duration = flyover.Duration - c.hops[i].hopfield.ResID = flyover.ResID + r.hops[i].flyover = flyover + r.hops[i].hopfield.Flyover = true + r.dec.NumLines += 2 + r.hops[i].hopfield.Bw = flyover.Bw + r.hops[i].hopfield.Duration = flyover.Duration + r.hops[i].hopfield.ResID = flyover.ResID break } } @@ -175,7 +175,7 @@ func (c *Reservation) applyFlyovers() { // Sets pathmeta timestamps and increments duplicate detection counter. // Updates MACs of all flyoverfields // replaces the dataplane of the input snet.path with the finished hummingbird path -func (c *Reservation) DeriveDataPlanePath(p snet.Path, pktLen uint16, +func (r *Reservation) DeriveDataPlanePath(p snet.Path, pktLen uint16, timeStamp time.Time) (snet.Path, error) { if p == nil { return nil, serrors.New("snet path is nil") @@ -185,24 +185,24 @@ func (c *Reservation) DeriveDataPlanePath(p snet.Path, pktLen uint16, // Update timestamps secs := uint32(timeStamp.Unix()) millis := uint32(timeStamp.Nanosecond()/1000) << 22 - millis |= c.counter - c.dec.Base.PathMeta.BaseTS = secs - c.dec.Base.PathMeta.HighResTS = millis + millis |= r.counter + r.dec.Base.PathMeta.BaseTS = secs + r.dec.Base.PathMeta.HighResTS = millis //increment counter for next packet - if c.counter >= 1<<22-1 { - c.counter = 0 + if r.counter >= 1<<22-1 { + r.counter = 0 } else { - c.counter += 1 + r.counter += 1 } // compute Macs for Flyovers var byteBuffer [hummingbird.FlyoverMacBufferSize]byte var xkbuffer [hummingbird.XkBufferSize]uint32 - for _, h := range c.hops { + for _, h := range r.hops { if !h.hopfield.Flyover { continue } h.hopfield.ResStartTime = uint16(secs - h.flyover.StartTime) - flyovermac := hummingbird.FullFlyoverMac(h.flyover.Ak[:], c.Destination(), pktLen, + flyovermac := hummingbird.FullFlyoverMac(h.flyover.Ak[:], r.Destination(), pktLen, h.hopfield.ResStartTime, millis, byteBuffer[:], xkbuffer[:]) binary.BigEndian.PutUint32(h.hopfield.HopField.Mac[:4], @@ -210,8 +210,8 @@ func (c *Reservation) DeriveDataPlanePath(p snet.Path, pktLen uint16, binary.BigEndian.PutUint16(h.hopfield.HopField.Mac[4:], binary.BigEndian.Uint16(flyovermac[4:])^binary.BigEndian.Uint16(h.hopfield.HopField.Mac[4:])) } - dphb.Raw = make([]byte, c.dec.Len()) - if err := c.dec.SerializeTo(dphb.Raw); err != nil { + dphb.Raw = make([]byte, r.dec.Len()) + if err := r.dec.SerializeTo(dphb.Raw); err != nil { return nil, err } switch v := p.(type) { From 11bc2255b5031cfb7e719108ab41fb43a0b02f2d Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Thu, 14 Dec 2023 20:20:29 +0100 Subject: [PATCH 079/100] No need for applyReservations. --- pkg/hummingbird/reservation.go | 85 ++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 40 deletions(-) diff --git a/pkg/hummingbird/reservation.go b/pkg/hummingbird/reservation.go index ea18868e5c..c7b6a93893 100644 --- a/pkg/hummingbird/reservation.go +++ b/pkg/hummingbird/reservation.go @@ -54,12 +54,10 @@ type Reservation struct { now time.Time // the current time interfaces []snet.PathInterface - flyovers FlyoverSet // the flyovers used to creatre this reservation + flyoverSet FlyoverSet // the flyovers used to creatre this reservation } type Hop struct { - BaseHop - hopfield *hummingbird.FlyoverHopField // dataplane hop field flyover *Flyover // flyover used to build this hop } @@ -77,7 +75,6 @@ func NewReservation(opts ...reservationModFcn) (*Reservation, error) { // Create reservation. c.prepareHbirdPath() - c.applyFlyovers() return c, nil } @@ -109,7 +106,7 @@ type FlyoverSet map[BaseHop][]*Flyover func WithFlyovers(flyovers FlyoverSet) reservationModFcn { return func(r *Reservation) error { - r.flyovers = flyovers + r.flyoverSet = flyovers return nil } } @@ -124,13 +121,12 @@ func WithNow(now time.Time) reservationModFcn { // func (c *Reservation) prepareHbirdPath(p snet.Path) error { func (r *Reservation) prepareHbirdPath() { r.dec.PathMeta.SegLen[0] += 2 - r.hops = append(r.hops, newHop( + r.newHop( r.interfaces[0].IA, 0, uint16(r.interfaces[0].ID), - // 0, &r.dec.HopFields[0], - )) + ) // The dataplane path in c.dec contains inf fields and cross-over hops. // Do each segment at a time to ignore the first hop of every segment except the first. @@ -140,47 +136,32 @@ func (r *Reservation) prepareHbirdPath() { hopCount := int(r.dec.Base.PathMeta.SegLen[infIdx]) / hummingbird.HopLines for i := 1; i < hopCount; i, hopIdx = i+1, hopIdx+1 { r.dec.PathMeta.SegLen[infIdx] += 2 - r.hops = append(r.hops, newHop( + r.newHop( r.interfaces[len(r.hops)*2-1].IA, uint16(r.interfaces[len(r.hops)*2-1].ID), egressID(r.interfaces, len(r.hops)), &r.dec.HopFields[hopIdx], - )) + ) } } } func (r *Reservation) Destination() addr.IA { - return r.hops[len(r.hops)-1].IA -} - -func (r *Reservation) applyFlyovers() { - now := uint32(r.now.Unix()) - for i, h := range r.hops { - flyovers := r.flyovers[h.BaseHop] - for _, flyover := range flyovers { - if flyover.StartTime <= now && uint32(flyover.Duration) >= now-flyover.StartTime { - r.hops[i].flyover = flyover - r.hops[i].hopfield.Flyover = true - r.dec.NumLines += 2 - r.hops[i].hopfield.Bw = flyover.Bw - r.hops[i].hopfield.Duration = flyover.Duration - r.hops[i].hopfield.ResID = flyover.ResID - break - } - } - } + return r.hops[len(r.hops)-1].flyover.IA } // Sets pathmeta timestamps and increments duplicate detection counter. // Updates MACs of all flyoverfields // replaces the dataplane of the input snet.path with the finished hummingbird path -func (r *Reservation) DeriveDataPlanePath(p snet.Path, pktLen uint16, - timeStamp time.Time) (snet.Path, error) { +func (r *Reservation) DeriveDataPlanePath( + p snet.Path, + pktLen uint16, + timeStamp time.Time, +) (snet.Path, error) { + if p == nil { return nil, serrors.New("snet path is nil") } - var dphb path.Hummingbird // Update timestamps secs := uint32(timeStamp.Unix()) @@ -210,6 +191,8 @@ func (r *Reservation) DeriveDataPlanePath(p snet.Path, pktLen uint16, binary.BigEndian.PutUint16(h.hopfield.HopField.Mac[4:], binary.BigEndian.Uint16(flyovermac[4:])^binary.BigEndian.Uint16(h.hopfield.HopField.Mac[4:])) } + + var dphb path.Hummingbird dphb.Raw = make([]byte, r.dec.Len()) if err := r.dec.SerializeTo(dphb.Raw); err != nil { return nil, err @@ -225,15 +208,37 @@ func (r *Reservation) DeriveDataPlanePath(p snet.Path, pktLen uint16, return p, nil } -func newHop(ia addr.IA, in, eg uint16, hf *hummingbird.FlyoverHopField) Hop { - return Hop{ - BaseHop: BaseHop{ - IA: ia, - Ingress: in, - Egress: eg, - }, - hopfield: hf, +func (r *Reservation) newHop(ia addr.IA, in, eg uint16, hf *hummingbird.FlyoverHopField) { + // Look for a valid flyover. + now := uint32(r.now.Unix()) + k := BaseHop{ + IA: ia, + Ingress: in, + Egress: eg, } + flyovers := r.flyoverSet[k] + var hopFlyover *Flyover + for _, flyover := range flyovers { + if flyover.StartTime <= now && uint32(flyover.Duration) >= now-flyover.StartTime { + hopFlyover = flyover + hf.Flyover = true + r.dec.NumLines += 2 + hf.Bw = flyover.Bw + hf.Duration = flyover.Duration + hf.ResID = flyover.ResID + break + } + } + + r.hops = append(r.hops, Hop{ + // BaseHop: BaseHop{ + // IA: ia, + // Ingress: in, + // Egress: eg, + // }, + hopfield: hf, + flyover: hopFlyover, + }) } // egressID returns the egress ID from a sequence of IDs (such as that in the metadata field From 7eb8b965a9eacabb2a5a601c7a0ab686aaf32b4e Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Thu, 14 Dec 2023 20:21:26 +0100 Subject: [PATCH 080/100] Cleanup. --- pkg/hummingbird/reservation_test.go | 191 ---------------------------- 1 file changed, 191 deletions(-) diff --git a/pkg/hummingbird/reservation_test.go b/pkg/hummingbird/reservation_test.go index 3a16f4fcd5..33853d66e4 100644 --- a/pkg/hummingbird/reservation_test.go +++ b/pkg/hummingbird/reservation_test.go @@ -18,7 +18,6 @@ import ( "testing" "github.com/scionproto/scion/pkg/hummingbird" - dphbird "github.com/scionproto/scion/pkg/slayers/path/hummingbird" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -44,196 +43,6 @@ func TestPrepareHbirdPath(t *testing.T) { assert.Equal(t, hbirdPath, output) } -// func TestCheckReservationExpiry(t *testing.T) { - -// tnow := time.Now() -// now := uint32(tnow.Unix()) - -// // hop1: first reservation expired, second ok -// // hop2: first reservation expired, second not started, third expired, fourth ok -// // hop3: first not yet valid, second expired -// input := []hummingbird.Flyover{ -// { -// BaseHop: testHops[0], -// ResID: 1234, -// Duration: 70, -// StartTime: now - 80, -// }, -// { -// BaseHop: testHops[0], -// ResID: 34, -// Duration: 560, -// StartTime: now - 10, -// }, -// { -// BaseHop: testHops[1], -// ResID: 42, -// Duration: 80, -// StartTime: now - 100, -// }, -// { -// BaseHop: testHops[1], -// ResID: 31, -// Duration: 389, -// StartTime: now + 50, -// }, -// { -// BaseHop: testHops[1], -// ResID: 12, -// Duration: 64, -// StartTime: now - 60, -// }, -// { -// BaseHop: testHops[1], -// ResID: 5, -// Duration: 180, -// StartTime: now - 30, -// }, -// { -// BaseHop: testHops[2], -// ResID: 365, -// Duration: 150, -// StartTime: now + 60, -// }, -// { -// BaseHop: testHops[2], -// ResID: 345, -// Duration: 150, -// StartTime: now - 345, -// }, -// } - -// expected := []hummingbird.Flyover{ -// { -// BaseHop: testHops[0], -// ResID: 34, -// Duration: 560, -// StartTime: now - 10, -// }, -// { -// BaseHop: testHops[1], -// ResID: 5, -// Duration: 180, -// StartTime: now - 30, -// }, -// { -// BaseHop: testHops[1], -// ResID: 31, -// Duration: 389, -// StartTime: now + 50, -// }, -// { -// BaseHop: testHops[2], -// ResID: 365, -// Duration: 150, -// StartTime: now + 60, -// }, -// } - -// scionPath := getScionSnetPath(t) - -// c, err := hummingbird.NewReservation(scionPath, nil) -// assert.NoError(t, err) - -// err = c.Applyflyovers(input) -// assert.NoError(t, err) - -// c.CheckExpiry(5) -// assert.Equal(t, expected, c.GetUsedFlyovers()) - -// // Verify last reservation is unused as it is not yet valid -// scionPath = getScionSnetPath(t) - -// outPath, err := c.FinalizePath(scionPath, 16, tnow) -// assert.NoError(t, err) - -// raw := outPath.Dataplane().(snetpath.Hummingbird).Raw - -// dec := decodeDataplane(t, raw) -// assert.True(t, dec.HopFields[0].Flyover) -// assert.True(t, dec.HopFields[1].Flyover) -// assert.False(t, dec.HopFields[2].Flyover) -// assert.False(t, dec.HopFields[3].Flyover) -// } - -// func TestRemoveReservations(t *testing.T) { -// scionPath := getScionSnetPath(t) - -// c, err := hummingbird.NewReservation(scionPath, nil) -// require.NoError(t, err) - -// err = c.Applyflyovers(testFlyovers) -// assert.NoError(t, err) - -// remove := []hummingbird.Flyover{ -// { -// BaseHop: hummingbird.BaseHop{ -// IA: testHops[0].IA, -// }, -// ResID: 1234, -// }, -// { -// BaseHop: hummingbird.BaseHop{ -// IA: testHops[1].IA, -// }, -// ResID: 53, -// }, -// { -// BaseHop: hummingbird.BaseHop{ -// IA: testHops[2].IA, -// }, -// ResID: 365, -// }, -// } - -// expected := []hummingbird.Flyover{ -// { -// BaseHop: testHops[1], -// ResID: 42, -// Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4, 3, 2, 1, 0}, -// Bw: 16, -// Duration: 180, -// StartTime: uint32(fixedTime.Unix()) - 32, -// }, -// { -// BaseHop: testHops[2], -// ResID: 21, -// Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7}, -// Bw: 20, -// Duration: 150, -// StartTime: uint32(fixedTime.Unix()) - 10, -// }, -// } - -// err = c.RemoveFlyovers(remove) -// assert.NoError(t, err) - -// output := c.GetUsedFlyovers() -// assert.Equal(t, expected, output) - -// // Verify removal has resulted in correct path -// scionPath = getScionSnetPath(t) - -// outPath, err := c.FinalizePath(scionPath, 16, time.Now()) -// assert.NoError(t, err) - -// raw := outPath.Dataplane().(snetpath.Hummingbird).Raw - -// dec := decodeDataplane(t, raw) -// assert.False(t, dec.HopFields[0].Flyover) -// assert.True(t, dec.HopFields[1].Flyover) -// assert.False(t, dec.HopFields[2].Flyover) -// assert.True(t, dec.HopFields[3].Flyover) -// } - -func decodeDataplane(t *testing.T, raw []byte) dphbird.Decoded { - t.Helper() - dec := dphbird.Decoded{} - err := dec.DecodeFromBytes(raw) - assert.NoError(t, err) - return dec -} - func flyoverSliceToMap(flyovers []hummingbird.Flyover) hummingbird.FlyoverSet { m := make(hummingbird.FlyoverSet) for _, flyover := range flyovers { From 8000bf18a577c80e9a94019304e09657b482de74 Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Thu, 14 Dec 2023 21:00:49 +0100 Subject: [PATCH 081/100] Changed signature of DeriveDataPlanePath. --- pkg/hummingbird/reservation.go | 31 +++++++---------------------- pkg/hummingbird/reservation_test.go | 13 ++++++++++-- 2 files changed, 18 insertions(+), 26 deletions(-) diff --git a/pkg/hummingbird/reservation.go b/pkg/hummingbird/reservation.go index c7b6a93893..5c42b21d59 100644 --- a/pkg/hummingbird/reservation.go +++ b/pkg/hummingbird/reservation.go @@ -118,7 +118,9 @@ func WithNow(now time.Time) reservationModFcn { } } -// func (c *Reservation) prepareHbirdPath(p snet.Path) error { +// prepareHbirdPath uses the decoded hummingbird path to walk each hop of each segment, +// and tries to find a suitable flyover and assign it to each hop. +// Crossover (xover) hops are treated by setting the flyover on the first hop. func (r *Reservation) prepareHbirdPath() { r.dec.PathMeta.SegLen[0] += 2 r.newHop( @@ -150,18 +152,12 @@ func (r *Reservation) Destination() addr.IA { return r.hops[len(r.hops)-1].flyover.IA } -// Sets pathmeta timestamps and increments duplicate detection counter. -// Updates MACs of all flyoverfields -// replaces the dataplane of the input snet.path with the finished hummingbird path +// DeriveDataPlanePath sets pathmeta timestamps and increments duplicate detection counter and +// updates MACs of all flyoverfields. func (r *Reservation) DeriveDataPlanePath( - p snet.Path, pktLen uint16, timeStamp time.Time, -) (snet.Path, error) { - - if p == nil { - return nil, serrors.New("snet path is nil") - } +) *hummingbird.Decoded { // Update timestamps secs := uint32(timeStamp.Unix()) @@ -192,20 +188,7 @@ func (r *Reservation) DeriveDataPlanePath( binary.BigEndian.Uint16(flyovermac[4:])^binary.BigEndian.Uint16(h.hopfield.HopField.Mac[4:])) } - var dphb path.Hummingbird - dphb.Raw = make([]byte, r.dec.Len()) - if err := r.dec.SerializeTo(dphb.Raw); err != nil { - return nil, err - } - switch v := p.(type) { - case path.Path: - v.DataplanePath = dphb - p = v - default: - return nil, serrors.New("Unsupported snet path struct", "path", p) - } - - return p, nil + return &r.dec } func (r *Reservation) newHop(ia addr.IA, in, eg uint16, hf *hummingbird.FlyoverHopField) { diff --git a/pkg/hummingbird/reservation_test.go b/pkg/hummingbird/reservation_test.go index 33853d66e4..2f1aa28d02 100644 --- a/pkg/hummingbird/reservation_test.go +++ b/pkg/hummingbird/reservation_test.go @@ -18,6 +18,7 @@ import ( "testing" "github.com/scionproto/scion/pkg/hummingbird" + "github.com/scionproto/scion/pkg/snet/path" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -38,9 +39,17 @@ func TestPrepareHbirdPath(t *testing.T) { hbirdPath, err := getHbirdFlyoversSnetPath(fixedTime) assert.NoError(t, err) - output, err := c.DeriveDataPlanePath(scionPath, 16, fixedTime) + // output, err := c.DeriveDataPlanePath(scionPath, 16, fixedTime) + decoded := c.DeriveDataPlanePath(16, fixedTime) + raw := path.Hummingbird{ + Raw: make([]byte, decoded.Len()), + } + err = decoded.SerializeTo(raw.Raw) + assert.NoError(t, err) + scionPath.DataplanePath = raw + assert.NoError(t, err) - assert.Equal(t, hbirdPath, output) + assert.Equal(t, hbirdPath, scionPath) } func flyoverSliceToMap(flyovers []hummingbird.Flyover) hummingbird.FlyoverSet { From 8fa6c04f3de8c70a1465763ac8523c1b7d58b0f8 Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Fri, 15 Dec 2023 06:46:45 +0100 Subject: [PATCH 082/100] Extend protobuf definition of Reservation with flyovers. --- pkg/proto/daemon/hummingbird.pb.go | 40 +++++++++++++++++++----------- proto/daemon/v1/hummingbird.proto | 7 ++++-- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/pkg/proto/daemon/hummingbird.pb.go b/pkg/proto/daemon/hummingbird.pb.go index 582ad7dc7c..86edf4020b 100644 --- a/pkg/proto/daemon/hummingbird.pb.go +++ b/pkg/proto/daemon/hummingbird.pb.go @@ -416,8 +416,9 @@ type Reservation struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Raw []byte `protobuf:"bytes,1,opt,name=raw,proto3" json:"raw,omitempty"` - Ratio float64 `protobuf:"fixed64,2,opt,name=ratio,proto3" json:"ratio,omitempty"` + Raw []byte `protobuf:"bytes,1,opt,name=raw,proto3" json:"raw,omitempty"` + Flyovers []*Flyover `protobuf:"bytes,2,rep,name=flyovers,proto3" json:"flyovers,omitempty"` + Ratio float64 `protobuf:"fixed64,3,opt,name=ratio,proto3" json:"ratio,omitempty"` } func (x *Reservation) Reset() { @@ -459,6 +460,13 @@ func (x *Reservation) GetRaw() []byte { return nil } +func (x *Reservation) GetFlyovers() []*Flyover { + if x != nil { + return x.Flyovers + } + return nil +} + func (x *Reservation) GetRatio() float64 { if x != nil { return x.Ratio @@ -513,14 +521,17 @@ var file_proto_daemon_v1_hummingbird_proto_rawDesc = []byte{ 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x15, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x72, 0x65, 0x73, 0x49, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x61, 0x6b, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x61, 0x6b, 0x22, 0x35, 0x0a, 0x0b, 0x52, 0x65, 0x73, + 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x61, 0x6b, 0x22, 0x6b, 0x0a, 0x0b, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x61, 0x77, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x72, 0x61, 0x77, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, - 0x63, 0x69, 0x6f, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x73, 0x63, 0x69, 0x6f, 0x6e, 0x2f, - 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x72, 0x61, 0x77, 0x12, 0x34, 0x0a, 0x08, 0x66, 0x6c, + 0x79, 0x6f, 0x76, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x46, + 0x6c, 0x79, 0x6f, 0x76, 0x65, 0x72, 0x52, 0x08, 0x66, 0x6c, 0x79, 0x6f, 0x76, 0x65, 0x72, 0x73, + 0x12, 0x14, 0x0a, 0x05, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, + 0x05, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x63, 0x69, 0x6f, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, + 0x73, 0x63, 0x69, 0x6f, 0x6e, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, + 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -550,11 +561,12 @@ var file_proto_daemon_v1_hummingbird_proto_depIdxs = []int32{ 6, // 0: proto.daemon.v1.StoreFlyoversRequest.flyovers:type_name -> proto.daemon.v1.Flyover 6, // 1: proto.daemon.v1.ListFlyoversResponse.flyovers:type_name -> proto.daemon.v1.Flyover 7, // 2: proto.daemon.v1.GetReservationsResponse.reservations:type_name -> proto.daemon.v1.Reservation - 3, // [3:3] is the sub-list for method output_type - 3, // [3:3] is the sub-list for method input_type - 3, // [3:3] is the sub-list for extension type_name - 3, // [3:3] is the sub-list for extension extendee - 0, // [0:3] is the sub-list for field type_name + 6, // 3: proto.daemon.v1.Reservation.flyovers:type_name -> proto.daemon.v1.Flyover + 4, // [4:4] is the sub-list for method output_type + 4, // [4:4] is the sub-list for method input_type + 4, // [4:4] is the sub-list for extension type_name + 4, // [4:4] is the sub-list for extension extendee + 0, // [0:4] is the sub-list for field type_name } func init() { file_proto_daemon_v1_hummingbird_proto_init() } diff --git a/proto/daemon/v1/hummingbird.proto b/proto/daemon/v1/hummingbird.proto index 7cb6419f58..328dca38e2 100644 --- a/proto/daemon/v1/hummingbird.proto +++ b/proto/daemon/v1/hummingbird.proto @@ -68,12 +68,15 @@ message Flyover { // This Ak is the derived key for this flyover, that is used to derive the per-host key. // It is 16 bytes long. bytes ak = 8; - } message Reservation { // The raw Hummingbird path. bytes raw = 1; + // The hop-sequence of flyovers. There are no crossovers in this sequence, + // i.e. for each xover in the hummingbird path, only one hop is present. + // Hops in the path without corresponding flyover have a nil entry. + repeated Flyover flyovers = 2; // Ratio # flyovers / # hops. - double ratio = 2; + double ratio = 3; } From d9674f742db1dce49890da3fb179ecf458d19278 Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Sat, 16 Dec 2023 09:06:56 +0100 Subject: [PATCH 083/100] Build reservation from existing hbird and flyover sequence. Cleanup. Tests. --- pkg/hummingbird/hummingbird_test.go | 60 ++++++--- pkg/hummingbird/reservation.go | 187 +++++++++++++++++----------- pkg/hummingbird/reservation_test.go | 40 ++++-- pkg/hummingbird/utils_test.go | 39 +++--- tools/end2end_hbird/main.go | 56 ++------- 5 files changed, 217 insertions(+), 165 deletions(-) diff --git a/pkg/hummingbird/hummingbird_test.go b/pkg/hummingbird/hummingbird_test.go index fcfb9196b2..a46be88efe 100644 --- a/pkg/hummingbird/hummingbird_test.go +++ b/pkg/hummingbird/hummingbird_test.go @@ -40,11 +40,11 @@ var testHops = []hummingbird.BaseHop{ }, } -var testFlyovers = []hummingbird.Flyover{ +var testFlyoversInDB = []hummingbird.Flyover{ // For the first hop: { BaseHop: testHops[0], - ResID: 1234, + ResID: testFlyoverFieldsReserved[0].ResID + 1, Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Bw: 16, Duration: 70, @@ -56,7 +56,7 @@ var testFlyovers = []hummingbird.Flyover{ Ingress: testHops[0].Ingress, Egress: testHops[0].Egress + 1, // not the expected egress }, - ResID: 1234, + ResID: testFlyoverFieldsReserved[0].ResID + 2, Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Bw: 16, Duration: 120, @@ -64,7 +64,7 @@ var testFlyovers = []hummingbird.Flyover{ }, { BaseHop: testHops[0], - ResID: 1234, + ResID: testFlyoverFieldsReserved[0].ResID + 3, Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Bw: 16, Duration: 120, @@ -72,7 +72,7 @@ var testFlyovers = []hummingbird.Flyover{ }, { BaseHop: testHops[0], - ResID: 1234, + ResID: testFlyoverFieldsReserved[0].ResID, Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Bw: 16, Duration: 120, @@ -82,15 +82,15 @@ var testFlyovers = []hummingbird.Flyover{ // For the second hop: { BaseHop: testHops[1], - ResID: 42, + ResID: testFlyoverFieldsReserved[1].ResID + 1, Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4, 3, 2, 1, 0}, Bw: 16, Duration: 180, - StartTime: uint32(fixedTime.Unix()) - 1000, + StartTime: uint32(fixedTime.Unix()) - 1000, // expired }, { BaseHop: testHops[1], - ResID: 42, + ResID: testFlyoverFieldsReserved[1].ResID, Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4, 3, 2, 1, 0}, Bw: 16, Duration: 180, @@ -100,7 +100,7 @@ var testFlyovers = []hummingbird.Flyover{ // For the third hop: { BaseHop: testHops[2], - ResID: 365, + ResID: testFlyoverFieldsReserved[3].ResID + 1, Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7}, Bw: 20, Duration: 150, @@ -108,19 +108,51 @@ var testFlyovers = []hummingbird.Flyover{ }, { BaseHop: testHops[2], - ResID: 365, + ResID: testFlyoverFieldsReserved[3].ResID, Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7}, - Bw: 20, - Duration: 150, + Bw: testFlyoverFieldsReserved[3].Bw, + Duration: testFlyoverFieldsReserved[3].Duration, StartTime: uint32(fixedTime.Unix()) - 80, }, { BaseHop: testHops[2], - ResID: 21, + ResID: testFlyoverFieldsReserved[3].ResID + 2, + Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7}, + Bw: testFlyoverFieldsReserved[3].Bw, + Duration: testFlyoverFieldsReserved[3].Duration, + StartTime: uint32(fixedTime.Unix()) - 10, + }, +} + +var testExpectedFlyovers = []*hummingbird.Flyover{ + // For the first hop: + { + BaseHop: testHops[0], + ResID: testFlyoverFieldsReserved[0].ResID, + Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + Bw: 16, + Duration: 120, + StartTime: uint32(fixedTime.Unix()) - 10, + }, + // For the second hop: + { + BaseHop: testHops[1], + ResID: testFlyoverFieldsReserved[1].ResID, + Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4, 3, 2, 1, 0}, + Bw: 16, + Duration: 180, + StartTime: uint32(fixedTime.Unix()) - 32, + }, + // for the third hop: + nil, + // For the fourth hop: + { + BaseHop: testHops[2], + ResID: testFlyoverFieldsReserved[3].ResID, Ak: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7}, Bw: 20, Duration: 150, - StartTime: uint32(fixedTime.Unix()) - 10, + StartTime: uint32(fixedTime.Unix()) - 80, }, } diff --git a/pkg/hummingbird/reservation.go b/pkg/hummingbird/reservation.go index 5c42b21d59..7b060c0c5d 100644 --- a/pkg/hummingbird/reservation.go +++ b/pkg/hummingbird/reservation.go @@ -48,40 +48,46 @@ func (r ReservationJuanDeleteme) LessThan(other *ReservationJuanDeleteme) bool { // Reservation represents a possibly partially reserved path, with zero or more flyovers. // TODO(juagargi) refactor functionality in two types: Reservation and move the rest to sciond. type Reservation struct { - dec hummingbird.Decoded // caches a decoded path for multiple uses - hops []Hop // possible flyovers - counter uint32 // duplicate detection counter + dec *hummingbird.Decoded // caches a decoded path for multiple uses + hops []Hop // possible flyovers, one per dec.HopField that has a flyover. + now time.Time // the current time - now time.Time // the current time - interfaces []snet.PathInterface - flyoverSet FlyoverSet // the flyovers used to creatre this reservation + counter uint32 // duplicate detection counter } type Hop struct { - hopfield *hummingbird.FlyoverHopField // dataplane hop field - flyover *Flyover // flyover used to build this hop + Hopfield *hummingbird.FlyoverHopField // dataplane hop field + Flyover *Flyover // flyover used to build this hop } +// NewReservation creates a new reservation object. The option setting functions are executed in +// the order they appear in the slice. func NewReservation(opts ...reservationModFcn) (*Reservation, error) { - c := &Reservation{ + r := &Reservation{ now: time.Now(), } // Run all options on this object. for _, fcn := range opts { - if err := fcn(c); err != nil { + if err := fcn(r); err != nil { return nil, err } } - // Create reservation. - c.prepareHbirdPath() - - return c, nil + return r, nil } +// reservationModFcn is a options setting function for a reservation. type reservationModFcn func(*Reservation) error -func WithPath(p snet.Path) reservationModFcn { +// FlyoverSet is a map between a flyover IA,ingress,egress and its corresponding collection of +// flyover objects (each of them can have e.g. different starting times). +type FlyoverSet map[BaseHop][]*Flyover + +// WithScionPath allows to build a Reservation based on the SCION path and flyovers passed as +// arguments. +// The flyovers are chosen from the map in order of appearance iff they are suitable, i.e. if +// they have a validity period intersecting with now. +func WithScionPath(p snet.Path, flyovers FlyoverSet) reservationModFcn { return func(r *Reservation) error { switch p := p.Dataplane().(type) { case path.SCION: @@ -89,28 +95,72 @@ func WithPath(p snet.Path) reservationModFcn { if err := scion.DecodeFromBytes(p.Raw); err != nil { return serrors.Join(err, serrors.New("Failed to Prepare Hummingbird Path")) } - r.dec = hummingbird.Decoded{} + r.dec = &hummingbird.Decoded{} r.dec.ConvertFromScionDecoded(scion) default: return serrors.New("Unsupported path type") } // We use the path metadata to get the IA from it. This sequence of interfaces does not // include the egress-to-ingress crossed over interfaces in the core AS. - r.interfaces = p.Metadata().Interfaces + interfaces := p.Metadata().Interfaces + + r.dec.PathMeta.SegLen[0] += 2 + r.newHopSelectFlyover( + interfaces[0].IA, + 0, + uint16(interfaces[0].ID), + &r.dec.HopFields[0], + flyovers, + ) + + // The dataplane path in c.dec contains inf fields and cross-over hops. + // Do each segment at a time to ignore the first hop of every segment except the first. + hopIdx := 1 // the index of the current hop in the dataplane. + for infIdx := 0; infIdx < r.dec.NumINF; infIdx, hopIdx = infIdx+1, hopIdx+1 { + // Preserve the hopcount locally, as we modify it inside the loop itself. + hopCount := int(r.dec.Base.PathMeta.SegLen[infIdx]) / hummingbird.HopLines + for i := 1; i < hopCount; i, hopIdx = i+1, hopIdx+1 { + r.dec.PathMeta.SegLen[infIdx] += 2 + r.newHopSelectFlyover( + interfaces[len(r.hops)*2-1].IA, + uint16(interfaces[len(r.hops)*2-1].ID), + egressID(interfaces, len(r.hops)), + &r.dec.HopFields[hopIdx], + flyovers, + ) + } + } return nil } } -type FlyoverSet map[BaseHop][]*Flyover - -func WithFlyovers(flyovers FlyoverSet) reservationModFcn { +// WithExistingHbirdPath allows to create a Reservation from an existing Hummingbird decoded +// path and its corresponding hop sequence. +func WithExistingHbirdPath(p *hummingbird.Decoded, flyovers []*Flyover) reservationModFcn { + // func WithExistingHbirdPath(p *hummingbird.Decoded, hops []Hop) reservationModFcn { return func(r *Reservation) error { - r.flyoverSet = flyovers + r.dec = p + // Create as many hops as non nil flyovers. + for i, flyover := range flyovers { + if flyover == nil { + continue + } + r.newHop(flyover.IA, flyover.Ingress, flyover.Egress, + &r.dec.HopFields[i], flyover) + } + // For each hop field, clean up the ResStartTime as it's set when deriving the dataplane + // path. + for i := range r.dec.HopFields { + r.dec.HopFields[i].ResStartTime = 0 + } + return nil } } +// WithNow modifies the current point in time for this reservation. It is useful to filter +// the different flyovers that can be passed to WithScionPath. func WithNow(now time.Time) reservationModFcn { return func(r *Reservation) error { r.now = now @@ -118,38 +168,25 @@ func WithNow(now time.Time) reservationModFcn { } } -// prepareHbirdPath uses the decoded hummingbird path to walk each hop of each segment, -// and tries to find a suitable flyover and assign it to each hop. -// Crossover (xover) hops are treated by setting the flyover on the first hop. -func (r *Reservation) prepareHbirdPath() { - r.dec.PathMeta.SegLen[0] += 2 - r.newHop( - r.interfaces[0].IA, - 0, - uint16(r.interfaces[0].ID), - &r.dec.HopFields[0], - ) - - // The dataplane path in c.dec contains inf fields and cross-over hops. - // Do each segment at a time to ignore the first hop of every segment except the first. - hopIdx := 1 // the index of the current hop in the dataplane. - for infIdx := 0; infIdx < r.dec.NumINF; infIdx, hopIdx = infIdx+1, hopIdx+1 { - // Preserve the hopcount locally, as we modify it inside the loop itself. - hopCount := int(r.dec.Base.PathMeta.SegLen[infIdx]) / hummingbird.HopLines - for i := 1; i < hopCount; i, hopIdx = i+1, hopIdx+1 { - r.dec.PathMeta.SegLen[infIdx] += 2 - r.newHop( - r.interfaces[len(r.hops)*2-1].IA, - uint16(r.interfaces[len(r.hops)*2-1].ID), - egressID(r.interfaces, len(r.hops)), - &r.dec.HopFields[hopIdx], - ) +func (r *Reservation) Destination() addr.IA { + return r.hops[len(r.hops)-1].Flyover.IA +} + +// FlyoverPerHopField returns a slice of pointers to flyovers, one per hop field present in the path, +// i.e. the length of the slice is the hop field count. +// If a hop field is not covered by a flyover, nil is used in its place. +func (r *Reservation) FlyoverPerHopField() []*Flyover { + flyovers := make([]*Flyover, len(r.dec.HopFields)) + for hopIdx, i := 0, 0; i < len(flyovers); i++ { + var flyover *Flyover + if r.hops[hopIdx].Hopfield == &r.dec.HopFields[i] { + flyover = r.hops[hopIdx].Flyover + hopIdx++ } + flyovers[i] = flyover } -} -func (r *Reservation) Destination() addr.IA { - return r.hops[len(r.hops)-1].flyover.IA + return flyovers } // DeriveDataPlanePath sets pathmeta timestamps and increments duplicate detection counter and @@ -175,23 +212,25 @@ func (r *Reservation) DeriveDataPlanePath( var byteBuffer [hummingbird.FlyoverMacBufferSize]byte var xkbuffer [hummingbird.XkBufferSize]uint32 for _, h := range r.hops { - if !h.hopfield.Flyover { + if !h.Hopfield.Flyover { continue } - h.hopfield.ResStartTime = uint16(secs - h.flyover.StartTime) - flyovermac := hummingbird.FullFlyoverMac(h.flyover.Ak[:], r.Destination(), pktLen, - h.hopfield.ResStartTime, millis, byteBuffer[:], xkbuffer[:]) - - binary.BigEndian.PutUint32(h.hopfield.HopField.Mac[:4], - binary.BigEndian.Uint32(flyovermac[:4])^binary.BigEndian.Uint32(h.hopfield.HopField.Mac[:4])) - binary.BigEndian.PutUint16(h.hopfield.HopField.Mac[4:], - binary.BigEndian.Uint16(flyovermac[4:])^binary.BigEndian.Uint16(h.hopfield.HopField.Mac[4:])) + h.Hopfield.ResStartTime = uint16(secs - h.Flyover.StartTime) + flyovermac := hummingbird.FullFlyoverMac(h.Flyover.Ak[:], r.Destination(), pktLen, + h.Hopfield.ResStartTime, millis, byteBuffer[:], xkbuffer[:]) + + binary.BigEndian.PutUint32(h.Hopfield.HopField.Mac[:4], + binary.BigEndian.Uint32(flyovermac[:4])^binary.BigEndian.Uint32(h.Hopfield.HopField.Mac[:4])) + binary.BigEndian.PutUint16(h.Hopfield.HopField.Mac[4:], + binary.BigEndian.Uint16(flyovermac[4:])^binary.BigEndian.Uint16(h.Hopfield.HopField.Mac[4:])) } - return &r.dec + return r.dec } -func (r *Reservation) newHop(ia addr.IA, in, eg uint16, hf *hummingbird.FlyoverHopField) { +func (r *Reservation) newHopSelectFlyover(ia addr.IA, in, eg uint16, + hf *hummingbird.FlyoverHopField, flyoverSet FlyoverSet) { + // Look for a valid flyover. now := uint32(r.now.Unix()) k := BaseHop{ @@ -199,28 +238,26 @@ func (r *Reservation) newHop(ia addr.IA, in, eg uint16, hf *hummingbird.FlyoverH Ingress: in, Egress: eg, } - flyovers := r.flyoverSet[k] - var hopFlyover *Flyover + flyovers := flyoverSet[k] for _, flyover := range flyovers { if flyover.StartTime <= now && uint32(flyover.Duration) >= now-flyover.StartTime { - hopFlyover = flyover - hf.Flyover = true r.dec.NumLines += 2 - hf.Bw = flyover.Bw - hf.Duration = flyover.Duration - hf.ResID = flyover.ResID + r.newHop(ia, in, eg, hf, flyover) break } } +} + +func (r *Reservation) newHop(ia addr.IA, in, eg uint16, + hf *hummingbird.FlyoverHopField, flyover *Flyover) { + hf.Flyover = true + hf.Bw = flyover.Bw + hf.Duration = flyover.Duration + hf.ResID = flyover.ResID r.hops = append(r.hops, Hop{ - // BaseHop: BaseHop{ - // IA: ia, - // Ingress: in, - // Egress: eg, - // }, - hopfield: hf, - flyover: hopFlyover, + Hopfield: hf, + Flyover: flyover, }) } diff --git a/pkg/hummingbird/reservation_test.go b/pkg/hummingbird/reservation_test.go index 2f1aa28d02..8b7c6cc9fc 100644 --- a/pkg/hummingbird/reservation_test.go +++ b/pkg/hummingbird/reservation_test.go @@ -23,24 +23,17 @@ import ( "github.com/stretchr/testify/require" ) -func TestPrepareHbirdPath(t *testing.T) { +func TestReservationWithScionPath(t *testing.T) { scionPath := getScionSnetPath(t) - c, err := hummingbird.NewReservation( - hummingbird.WithPath(scionPath), - hummingbird.WithFlyovers(flyoverSliceToMap(testFlyovers)), + r, err := hummingbird.NewReservation( hummingbird.WithNow(fixedTime), + hummingbird.WithScionPath(scionPath, flyoverSliceToMap(testFlyoversInDB)), ) require.NoError(t, err) - - scionPath = getScionSnetPath(t) - assert.NoError(t, err) - - hbirdPath, err := getHbirdFlyoversSnetPath(fixedTime) + hbirdPath, err := getHbirdFlyoversSnetPath(t, fixedTime) assert.NoError(t, err) - - // output, err := c.DeriveDataPlanePath(scionPath, 16, fixedTime) - decoded := c.DeriveDataPlanePath(16, fixedTime) + decoded := r.DeriveDataPlanePath(16, fixedTime) raw := path.Hummingbird{ Raw: make([]byte, decoded.Len()), } @@ -52,6 +45,29 @@ func TestPrepareHbirdPath(t *testing.T) { assert.Equal(t, hbirdPath, scionPath) } +func TestReservationWithHbirdPath(t *testing.T) { + // Build a Reservation from an existing decoded hummingbird path and its associated + // flyover sequence. + r, err := hummingbird.NewReservation( + hummingbird.WithNow(fixedTime), + hummingbird.WithExistingHbirdPath( + decodedHbirdTestPathFlyovers, + // selectUsedFlyovers(t, testFlyoverFieldsReserved, testExpectedFlyovers)), + testExpectedFlyovers), + ) + assert.NoError(t, err) + + // Expected: + expected, err := hummingbird.NewReservation( + hummingbird.WithNow(fixedTime), + hummingbird.WithScionPath(getScionSnetPath(t), + flyoverSliceToMap(testFlyoversInDB), + ), + ) + require.NoError(t, err) + require.Equal(t, expected, r) +} + func flyoverSliceToMap(flyovers []hummingbird.Flyover) hummingbird.FlyoverSet { m := make(hummingbird.FlyoverSet) for _, flyover := range flyovers { diff --git a/pkg/hummingbird/utils_test.go b/pkg/hummingbird/utils_test.go index 5c5e071bb1..49d3be0d44 100644 --- a/pkg/hummingbird/utils_test.go +++ b/pkg/hummingbird/utils_test.go @@ -43,7 +43,7 @@ var testInfoFields = []path.InfoField{ }, } -var testHopFields = []path.HopField{ +var testScionHopFields = []path.HopField{ { ExpTime: 63, ConsIngress: 1, @@ -72,26 +72,26 @@ var testHopFields = []path.HopField{ var testFlyoverFields = []hummingbird.FlyoverHopField{ { - HopField: testHopFields[0], + HopField: testScionHopFields[0], Flyover: false, }, { - HopField: testHopFields[1], + HopField: testScionHopFields[1], Flyover: false, }, { - HopField: testHopFields[2], + HopField: testScionHopFields[2], Flyover: false, }, { - HopField: testHopFields[3], + HopField: testScionHopFields[3], Flyover: false, }, } var testFlyoverFieldsReserved = []hummingbird.FlyoverHopField{ { - HopField: testHopFields[0], + HopField: testScionHopFields[0], Flyover: true, ResID: 1234, Bw: 16, @@ -99,7 +99,7 @@ var testFlyoverFieldsReserved = []hummingbird.FlyoverHopField{ ResStartTime: 10, }, { - HopField: testHopFields[1], + HopField: testScionHopFields[1], Flyover: true, ResID: 42, Bw: 16, @@ -107,11 +107,11 @@ var testFlyoverFieldsReserved = []hummingbird.FlyoverHopField{ ResStartTime: 32, }, { - HopField: testHopFields[2], + HopField: testScionHopFields[2], Flyover: false, }, { - HopField: testHopFields[3], + HopField: testScionHopFields[3], Flyover: true, ResID: 365, Bw: 20, @@ -120,7 +120,7 @@ var testFlyoverFieldsReserved = []hummingbird.FlyoverHopField{ }, } -var decodedTestPath = &scion.Decoded{ +var decodedScionTestPath = &scion.Decoded{ Base: scion.Base{ PathMeta: scion.MetaHdr{ CurrINF: 0, @@ -132,7 +132,7 @@ var decodedTestPath = &scion.Decoded{ NumHops: 4, }, InfoFields: testInfoFields, - HopFields: testHopFields, + HopFields: testScionHopFields, } var interfacesTest = []snet.PathInterface{ @@ -192,7 +192,7 @@ func getRawScionPath(d scion.Decoded) ([]byte, error) { func getScionSnetPath(t *testing.T) snetpath.Path { t.Helper() - rawScion, err := getRawScionPath(*decodedTestPath) + rawScion, err := getRawScionPath(*decodedScionTestPath) assert.NoError(t, err) p := snetpath.Path{ Src: interfacesTest[0].IA, @@ -234,10 +234,17 @@ func getHbirdNoFlyoversSnetPath(t time.Time) (snetpath.Path, error) { return p, err } -func getHbirdFlyoversSnetPath(t time.Time) (snetpath.Path, error) { - decoded := *decodedHbirdTestPathFlyovers - secs := uint32(t.Unix()) - millis := uint32(t.Nanosecond()/1000) << 22 +func getHbirdFlyoversSnetPath(t *testing.T, now time.Time) (snetpath.Path, error) { + // Fully clone from the global variable, to avoid mutating it. + serialized := make([]byte, decodedHbirdTestPathFlyovers.Len()) + err := decodedHbirdTestPathFlyovers.SerializeTo(serialized) + assert.NoError(t, err) + decoded := hummingbird.Decoded{} + err = decoded.DecodeFromBytes(serialized) + assert.NoError(t, err) + + secs := uint32(now.Unix()) + millis := uint32(now.Nanosecond()/1000) << 22 decoded.Base.PathMeta.BaseTS = secs decoded.Base.PathMeta.HighResTS = millis diff --git a/tools/end2end_hbird/main.go b/tools/end2end_hbird/main.go index c9ceaadd3a..55a9c830df 100644 --- a/tools/end2end_hbird/main.go +++ b/tools/end2end_hbird/main.go @@ -71,7 +71,6 @@ var ( scionPacketConnMetrics = metrics.NewSCIONPacketConnMetrics() scmpErrorsCounter = scionPacketConnMetrics.SCMPErrors flyovers bool - partial bool ) func main() { @@ -107,7 +106,6 @@ func addFlags() { flag.Var(&remote, "remote", "(Mandatory for clients) address to connect to") flag.Var(timeout, "timeout", "The timeout for each attempt") flag.BoolVar(&flyovers, "flyovers", true, "Enable Flyovers") - flag.BoolVar(&partial, "partial", false, "If true, only subset of hops have flyovers") } func validateFlags() { @@ -315,61 +313,23 @@ func (c *client) attemptRequest(n int) bool { return false } remote.Path = path.Dataplane() - // } else if !partial { - // // full path with reservations - // hbirdClient, err := hummingbird.NewReservation(path, nil) - // if err != nil { - // logger.Error("Error converting path to Hummingbird", "err", err) - // return false - // } - // secs := uint32(time.Now().Unix()) - // res, err := hbirdClient.RequestFlyoversAllHops(16, secs, 120) - // if err != nil { - // logger.Error("Error requesting reservations", "err", err) - // return false - // } - - // if err := hbirdClient.Applyflyovers(res); err != nil { - // logger.Error("Error applying reservations", "err", err) - // } - - // path, err = hbirdClient.FinalizePath(path, pingPayloadLen, time.Now()) - // if err != nil { - // logger.Error("Error assembling hummingbird path", "err", err) - // } - // remote.Path = path.Dataplane() } else { - //partial reservations, alternating resrved and not reserved hbirdClient, err := hummingbird.NewReservation( - hummingbird.WithPath(path), hummingbird.WithFlyovers(nil)) - // hbirdClient, err := hummingbird.NewReservation(path, nil) + hummingbird.WithScionPath(path, nil)) if err != nil { logger.Error("Error converting path to Hummingbird", "err", err) return false } - // ases := hbirdClient.GetPathASes() - // n := len(ases) - // for i := 1; i < n; i++ { - // copy(ases[i:n-1], ases[i+1:n]) - // n-- - // } - // ases = ases[:n] - // secs := uint32(time.Now().Unix()) - // res, err := hummingbird.RequestFlyoversForASes(ases, 16, secs, 120) - // if err != nil { - // logger.Error("Error requesting reservations", "err", err) - // return false - // } - - // if err := hbirdClient.Applyflyovers(res); err != nil { - // logger.Error("Error applying reservations", "err", err) - // } - - path, err = hbirdClient.DeriveDataPlanePath(path, pingPayloadLen, time.Now()) + + decoded := hbirdClient.DeriveDataPlanePath(16, time.Now()) + raw := snetpath.Hummingbird{ + Raw: make([]byte, decoded.Len()), + } + err = decoded.SerializeTo(raw.Raw) if err != nil { logger.Error("Error assembling hummingbird path", "err", err) } - remote.Path = path.Dataplane() + remote.Path = raw } } From 803776f74d2a3b19a93b5f0a1c69f1765056fdb3 Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Sat, 16 Dec 2023 09:21:37 +0100 Subject: [PATCH 084/100] Cleanup. --- pkg/hummingbird/BUILD.bazel | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkg/hummingbird/BUILD.bazel b/pkg/hummingbird/BUILD.bazel index 8ef5d0d8dc..ef3f3b4099 100644 --- a/pkg/hummingbird/BUILD.bazel +++ b/pkg/hummingbird/BUILD.bazel @@ -10,7 +10,6 @@ go_library( visibility = ["//visibility:public"], deps = [ "//pkg/addr:go_default_library", - "//pkg/log:go_default_library", "//pkg/private/serrors:go_default_library", "//pkg/slayers/path/hummingbird:go_default_library", "//pkg/slayers/path/scion:go_default_library", @@ -30,7 +29,6 @@ go_test( ], deps = [ ":go_default_library", - "//pkg/addr:go_default_library", "//pkg/slayers/path:go_default_library", "//pkg/slayers/path/hummingbird:go_default_library", "//pkg/slayers/path/scion:go_default_library", From 68fdcd5fca3ea9e70f0913c40dd1b9fbfeba1f75 Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Tue, 19 Dec 2023 17:24:04 +0100 Subject: [PATCH 085/100] Use the new Reservation in our gRPC code. --- daemon/internal/servers/BUILD.bazel | 3 + daemon/internal/servers/grpc_hummingbird.go | 133 ++++++++---------- .../internal/servers/grpc_hummingbird_test.go | 131 ++++++++++++----- pkg/hummingbird/reservation.go | 58 ++++---- pkg/hummingbird/reservation_test.go | 9 +- 5 files changed, 195 insertions(+), 139 deletions(-) diff --git a/daemon/internal/servers/BUILD.bazel b/daemon/internal/servers/BUILD.bazel index 1ff8d8c7d2..33c7b83a73 100644 --- a/daemon/internal/servers/BUILD.bazel +++ b/daemon/internal/servers/BUILD.bazel @@ -44,7 +44,10 @@ go_test( "//pkg/addr:go_default_library", "//pkg/hummingbird:go_default_library", "//pkg/private/common:go_default_library", + "//pkg/private/util:go_default_library", "//pkg/private/xtest:go_default_library", + "//pkg/slayers/path:go_default_library", + "//pkg/slayers/path/scion:go_default_library", "//pkg/snet:go_default_library", "//pkg/snet/path:go_default_library", "@com_github_stretchr_testify//require:go_default_library", diff --git a/daemon/internal/servers/grpc_hummingbird.go b/daemon/internal/servers/grpc_hummingbird.go index 0f69d9a076..5e3a57e41e 100644 --- a/daemon/internal/servers/grpc_hummingbird.go +++ b/daemon/internal/servers/grpc_hummingbird.go @@ -30,7 +30,7 @@ import ( ) type HummingbirdFetcher interface { - ListFlyovers(ctx context.Context, owners []addr.IA) ([]*hummingbird.BaseHop, error) + ListFlyovers(ctx context.Context, owners []addr.IA) ([]*hummingbird.Flyover, error) } func (s *DaemonServer) StoreFlyovers( @@ -59,7 +59,7 @@ func (s *DaemonServer) GetReservations( } // Obtain reservations composing flyovers for those paths. - rsvs, err := s.getReservations(ctx, paths) + rsvs, err := s.getReservations(ctx, paths, time.Now(), uint16(req.MinBandwidth)) if err != nil { return nil, err } @@ -68,8 +68,12 @@ func (s *DaemonServer) GetReservations( res := &sdpb.GetReservationsResponse{ Reservations: make([]*sdpb.Reservation, len(paths)), } - - _ = rsvs + for i := range res.Reservations { + res.Reservations[i], err = convertReservationToPB(rsvs[i]) + if err != nil { + return nil, err + } + } return res, nil } @@ -106,7 +110,9 @@ func (s *DaemonServer) getScionPaths( func (s *DaemonServer) getReservations( ctx context.Context, paths []path.Path, -) ([]*hummingbird.ReservationJuanDeleteme, error) { + now time.Time, + minBW uint16, +) ([]*hummingbird.Reservation, error) { // Make a set with all appearing IASet. Then a slice of them to obtain flyovers. IASet := make(map[addr.IA]struct{}, 0) @@ -128,19 +134,26 @@ func (s *DaemonServer) getReservations( mFlyovers := flyoversToMap(flyovers) // For each path, try to assign as many flyovers as possible. - reservations := make([]*hummingbird.ReservationJuanDeleteme, len(paths)) + reservations := make([]*hummingbird.Reservation, len(paths)) for i, p := range paths { - flyovers, ratio := assignFlyovers(p.Meta.Interfaces, mFlyovers) - reservations[i] = &hummingbird.ReservationJuanDeleteme{ - SCIONPath: p, - Flyovers: flyovers, - Ratio: ratio, + reservations[i], err = hummingbird.NewReservation( + hummingbird.WithNow(now), + hummingbird.WithMinBW(minBW), + hummingbird.WithScionPath(p, mFlyovers), + ) + if err != nil { + return nil, err } } // Rank the reservations by flyover / hop ratio. sort.Slice(reservations, func(i, j int) bool { - return reservations[i].LessThan(reservations[j]) + nFa, nHFa := reservations[i].FlyoverAndHFCount() + ratioa := float64(nFa) / float64(nHFa) + nFb, nHFb := reservations[i].FlyoverAndHFCount() + ratiob := float64(nFb) / float64(nHFb) + + return ratioa < ratiob }) return reservations, nil @@ -230,82 +243,56 @@ func linkTypeFromPB(lt sdpb.LinkType) snet.LinkType { } } -// flyoverMapKey is a map of flyovers keyed by IA, ingress, and egress. -// The assumption is that at most one hop field exists per triplet. -type flyoverMapKey struct { - IA addr.IA - Ingress uint16 - Egress uint16 -} -type flyoverMap map[flyoverMapKey]*hummingbird.BaseHop - -func flyoversToMap(flyovers []*hummingbird.BaseHop) flyoverMap { - ret := make(flyoverMap) +func flyoversToMap(flyovers []*hummingbird.Flyover) hummingbird.FlyoverMap { + ret := make(hummingbird.FlyoverMap) for _, flyover := range flyovers { - k := flyoverMapKey{ + k := hummingbird.BaseHop{ IA: flyover.IA, Ingress: flyover.Ingress, Egress: flyover.Egress, } - ret[k] = flyover + ret[k] = append(ret[k], flyover) } return ret } -// assignFlyovers assigns as flyovers to as many hops of a path as possible. -// The first returned value is a slice of flyovers, with the same length as the hop sequence, -// and when not nil, it points to a Flyover that can be used in the hop of that specific index. -// As SCION hops appear twice per ingress/egress pairs (with the exception of the first and -// last ones), the flyovers are never located on tbe egress index, which means, always located -// on odd indices. -// The second returned value is the ratio of flyovers (1.0 being all, 0.0 being none) that exist -// for that path. -func assignFlyovers( - hopSequence []snet.PathInterface, - flyovers flyoverMap, -) ([]*hummingbird.BaseHop, float64) { - - ret := make([]*hummingbird.BaseHop, len(hopSequence)) - flyoverExistsCount := 0 +func convertReservationToPB(r *hummingbird.Reservation) (*sdpb.Reservation, error) { + // Prepare the hummingbird path. + p := r.GetHummingbirdPath() + raw := make([]byte, p.Len()) + if err := p.SerializeTo(raw); err != nil { + return nil, err + } - // Do the first flyover apart. - k := flyoverMapKey{ - IA: hopSequence[0].IA, - Ingress: 0, - Egress: uint16(hopSequence[0].ID), + // Prepare the flyovers. + flyovers := r.FlyoverPerHopField() + numF, numHF := r.FlyoverAndHFCount() + ret := &sdpb.Reservation{ + Raw: raw, + Ratio: float64(numF) / float64(numHF), + Flyovers: make([]*sdpb.Flyover, len(flyovers)), } - ret[0] = flyovers[k] - if ret[0] != nil { - flyoverExistsCount++ + for i, f := range flyovers { + ret.Flyovers[i] = convertFlyoverToPB(f) } - // Do everything in the middle, except the last flyover. - for i := 1; i < len(hopSequence)-1; i += 2 { - // Prepare to look for the next flyover. - hop := hopSequence[i] - k = flyoverMapKey{ - IA: hop.IA, - Ingress: uint16(hop.ID), - Egress: uint16(hopSequence[i+1].ID), - } - - // Find out if there's any flyover we can use. - ret[i] = flyovers[k] - if ret[i] != nil { - flyoverExistsCount++ - } - } + return ret, nil +} - // Do the last flyover. - k = flyoverMapKey{ - IA: hopSequence[len(hopSequence)-1].IA, - Ingress: uint16(hopSequence[len(hopSequence)-1].ID), - Egress: 0, +func convertFlyoverToPB(f *hummingbird.Flyover) *sdpb.Flyover { + if f == nil { + return nil } - ret[len(ret)-1] = flyovers[k] - if ret[len(ret)-1] != nil { - flyoverExistsCount++ + ret := &sdpb.Flyover{ + Ia: uint64(f.IA), + Ingress: uint32(f.Ingress), + Egress: uint32(f.Egress), + Bw: uint32(f.Bw), + ResId: f.ResID, + StartTime: f.StartTime, + Duration: uint32(f.Duration), + Ak: append([]byte{}, f.Ak[:]...), } - return ret, float64(flyoverExistsCount) / float64(len(hopSequence)/2) + return ret } diff --git a/daemon/internal/servers/grpc_hummingbird_test.go b/daemon/internal/servers/grpc_hummingbird_test.go index b1c6a499ad..a55cfaae9c 100644 --- a/daemon/internal/servers/grpc_hummingbird_test.go +++ b/daemon/internal/servers/grpc_hummingbird_test.go @@ -21,27 +21,38 @@ import ( "github.com/scionproto/scion/pkg/addr" "github.com/scionproto/scion/pkg/hummingbird" "github.com/scionproto/scion/pkg/private/common" + "github.com/scionproto/scion/pkg/private/util" "github.com/scionproto/scion/pkg/private/xtest" + pathlayers "github.com/scionproto/scion/pkg/slayers/path" + "github.com/scionproto/scion/pkg/slayers/path/scion" "github.com/scionproto/scion/pkg/snet" "github.com/scionproto/scion/pkg/snet/path" "github.com/stretchr/testify/require" ) +const currUnixTimestamp = 100 + // TestGetReservation checks that given a set of SCION paths, the functions getting the // reservation correctly finds the appropriate flyovers and uses them. func TestGetReservation(t *testing.T) { cases := map[string]struct { // paths' hops, like { {0, "1-ff00:0:1", 1, 2, "1-ff00:0:2", 0} , ... } scionPaths [][]any - expected [][]any // this is a slice of flyovers with nils in it + expected [][]any // this is a slice of flyovers allowing nils in it flyoverDB [][]any }{ "onepath_oneflyover": { scionPaths: [][]any{ - {0, "1-ff00:0:1", 1, 2, "1-ff00:0:2", 0}, + { + 0, "1-ff00:0:1", 1, + 2, "1-ff00:0:2", 0, + }, }, expected: [][]any{ - {0, "1-ff00:0:1", 1, nil}, + { + 0, "1-ff00:0:1", 1, + nil, + }, }, flyoverDB: [][]any{ {0, "1-ff00:0:1", 1}, @@ -51,14 +62,17 @@ func TestGetReservation(t *testing.T) { }, "onepath_twoflyovers": { scionPaths: [][]any{ - {0, "1-ff00:0:1", 1, 2, "1-ff00:0:2", 3, 4, "1-ff00:0:3", 0}, + { + 0, "1-ff00:0:1", 1, + 2, "1-ff00:0:2", 3, + 4, "1-ff00:0:3", 0, + }, }, expected: [][]any{ { 0, "1-ff00:0:1", 1, 2, "1-ff00:0:2", 3, nil, - nil, }, }, flyoverDB: [][]any{ @@ -78,7 +92,7 @@ func TestGetReservation(t *testing.T) { ctx, cancelF := context.WithDeadline(context.Background(), deadline) defer cancelF() - flyoverDB := make([]*hummingbird.BaseHop, len(tc.flyoverDB)) + flyoverDB := make([]*hummingbird.Flyover, len(tc.flyoverDB)) for i, flyoverDesc := range tc.flyoverDB { flyover := getMockFlyovers(t, flyoverDesc...) require.Len(t, flyover, 1, "bad test") @@ -90,21 +104,31 @@ func TestGetReservation(t *testing.T) { s := &DaemonServer{ HummingbirdFetcher: mockHbirdServer, } - scion := getMockScionPaths(t, tc.scionPaths) - rsvs, err := s.getReservations(ctx, scion) + scionPaths := getMockScionPaths(t, tc.scionPaths) + rsvs, err := s.getReservations(ctx, scionPaths, util.SecsToTime(currUnixTimestamp), 0) require.NoError(t, err) // Check the size. - require.Len(t, rsvs, len(scion)) + require.Len(t, rsvs, len(scionPaths)) // For each path, check the flyovers. - for i, p := range scion { + for i, pRaw := range scionPaths { + // Decode pRaw into a SCION decoded path. + require.IsType(t, path.SCION{}, pRaw.DataplanePath) + dpRaw := pRaw.DataplanePath.(path.SCION) + + p := scion.Decoded{} + err := p.DecodeFromBytes(dpRaw.Raw) + require.NoError(t, err) + // Same hop count in both SCION path and reservation. - ifaces := p.Meta.Interfaces - require.Equal(t, len(ifaces), len(rsvs[i].Flyovers)) + r := rsvs[i] + flyoverSequence := r.FlyoverPerHopField() + require.Equal(t, len(p.HopFields), len(flyoverSequence)) + // Check the flyover sequence. expected := getMockFlyovers(t, tc.expected[i]...) - require.Equal(t, expected, rsvs[i].Flyovers) + require.Equal(t, expected, flyoverSequence) } }) } @@ -113,7 +137,7 @@ func TestGetReservation(t *testing.T) { func getMockScionPaths(t require.TestingT, paths [][]any) []path.Path { ret := make([]path.Path, len(paths)) for i, p := range paths { - ret[i] = *getMockScionPath(t, p...) + ret[i] = getMockScionPath(t, p...) } return ret } @@ -123,70 +147,109 @@ func getMockScionPaths(t require.TestingT, paths [][]any) []path.Path { // The parameter `hops` must be of the form (0, "1-ff00:0:1", 1, 2, "1-ff00:0:2", 0) to indicate // one hop between those two ASes. For more ASes, add more hops in the middle. // First and last interface IDs must always be 0. -func getMockScionPath(t require.TestingT, hops ...any) *path.Path { +func getMockScionPath(t require.TestingT, hops ...any) path.Path { // Check the arguments. require.Equal(t, 0, len(hops)%3, "invalid hops field") require.Equal(t, 0, hops[0].(int)) require.Equal(t, 0, hops[len(hops)-1].(int)) - for i := 0; i < len(hops); i += 3 { - require.IsType(t, 0, hops[i]) - require.IsType(t, "", hops[i+1]) - require.IsType(t, 0, hops[i+2]) - } // Parse hops argument. - interfaces := make([]snet.PathInterface, len(hops)/3*2) // SCION interfaces plus src and dst + hopFields := make([]pathlayers.HopField, len(hops)/3) + // interfaces has src and dst as extra. Will have to remove first and last items. + interfaces := make([]snet.PathInterface, len(hops)/3*2) for i := 0; i < len(hops); i += 3 { - in := hops[i].(int) + require.IsType(t, 0, hops[i]) // check is int + require.IsType(t, "", hops[i+1]) // check is string + require.IsType(t, 0, hops[i+2]) // check is int + ia := xtest.MustParseIA(hops[i+1].(string)) + in := hops[i].(int) eg := hops[i+2].(int) + // Set the values for this hop. + hopFields[i/3].ConsIngress = uint16(in) + hopFields[i/3].ConsEgress = uint16(eg) + hopFields[i/3].ExpTime = currUnixTimestamp + 100 + hopFields[i/3].Mac = [6]byte{1, 2, 3, 4, 5, 6} + + // Set the values for the ingress and the egress interfaces for the metadata field. interfaces[i/3*2].IA = ia interfaces[i/3*2].ID = common.IFIDType(in) interfaces[i/3*2+1].IA = ia interfaces[i/3*2+1].ID = common.IFIDType(eg) } - path := &path.Path{ + // Build a SCION decoded path. + scionPath := scion.Decoded{ + Base: scion.Base{ + PathMeta: scion.MetaHdr{ + SegLen: [3]uint8{uint8(len(hops) / 3), 0, 0}, + }, + NumINF: 1, + NumHops: len(hopFields), + }, + InfoFields: []pathlayers.InfoField{ + { + ConsDir: true, + SegID: 1, + Timestamp: 10, + }, + }, + HopFields: hopFields, + } + + // Build a SCION path based on the decoded one. + raw, err := scionPath.ToRaw() + require.NoError(t, err) + return path.Path{ Src: interfaces[0].IA, Dst: interfaces[len(interfaces)-1].IA, Meta: snet.PathMetadata{ // Remove the extra start and end hops. Interfaces: interfaces[1 : len(interfaces)-1], }, + DataplanePath: path.SCION{ + Raw: raw.Raw, + }, } - return path } -func getMockFlyovers(t require.TestingT, hops ...any) []*hummingbird.BaseHop { +// getMockFlyovers receives a []any like {0, "1-ff00:0:1", 1} and creates a flyover +// using those values. +func getMockFlyovers(t require.TestingT, hops ...any) []*hummingbird.Flyover { // Parse hops argument. - flyovers := make([]*hummingbird.BaseHop, 0) + flyovers := make([]*hummingbird.Flyover, 0) for i := 0; i < len(hops); i++ { - var f *hummingbird.BaseHop + var f *hummingbird.Flyover if hops[i] != nil { in := hops[i].(int) ia := xtest.MustParseIA(hops[i+1].(string)) eg := hops[i+2].(int) - f = &hummingbird.BaseHop{ - IA: ia, - Ingress: uint16(in), - Egress: uint16(eg), + f = &hummingbird.Flyover{ + BaseHop: hummingbird.BaseHop{ + IA: ia, + Ingress: uint16(in), + Egress: uint16(eg), + }, + StartTime: currUnixTimestamp, + Duration: 100, } i += 2 // advance faster } + // Append a new flyover or nil. flyovers = append(flyovers, f) } return flyovers } type mockServer struct { - Flyovers []*hummingbird.BaseHop + Flyovers []*hummingbird.Flyover } func (m *mockServer) ListFlyovers( ctx context.Context, owners []addr.IA, -) ([]*hummingbird.BaseHop, error) { +) ([]*hummingbird.Flyover, error) { // Create a set of the requested IAs. ownerMap := make(map[addr.IA]struct{}) @@ -195,7 +258,7 @@ func (m *mockServer) ListFlyovers( } // Find any flyover with any such IA and return it. - ret := make([]*hummingbird.BaseHop, 0) + ret := make([]*hummingbird.Flyover, 0) for _, f := range m.Flyovers { if _, ok := ownerMap[f.IA]; ok { ret = append(ret, f) diff --git a/pkg/hummingbird/reservation.go b/pkg/hummingbird/reservation.go index 7b060c0c5d..ec6f31c55c 100644 --- a/pkg/hummingbird/reservation.go +++ b/pkg/hummingbird/reservation.go @@ -26,31 +26,12 @@ import ( "github.com/scionproto/scion/pkg/snet/path" ) -type ReservationJuanDeleteme struct { - SCIONPath path.Path - Flyovers []*BaseHop - Ratio float64 // flyover/hops ratio -} - -// HopCount returns the number of hops in this path, as understood by a hop in a regular SCION path. -func (r ReservationJuanDeleteme) HopCount() int { - return len(r.SCIONPath.Meta.Interfaces) -} - -func (r ReservationJuanDeleteme) FlyoverCount() int { - return len(r.Flyovers) -} - -func (r ReservationJuanDeleteme) LessThan(other *ReservationJuanDeleteme) bool { - return r.Ratio < other.Ratio -} - // Reservation represents a possibly partially reserved path, with zero or more flyovers. -// TODO(juagargi) refactor functionality in two types: Reservation and move the rest to sciond. type Reservation struct { - dec *hummingbird.Decoded // caches a decoded path for multiple uses - hops []Hop // possible flyovers, one per dec.HopField that has a flyover. - now time.Time // the current time + dec *hummingbird.Decoded // caches a decoded path for multiple uses + hops []Hop // possible flyovers, one per dec.HopField that has a flyover. + now time.Time // the current time + minBW uint16 // the minimum required bandwidth counter uint32 // duplicate detection counter } @@ -79,15 +60,15 @@ func NewReservation(opts ...reservationModFcn) (*Reservation, error) { // reservationModFcn is a options setting function for a reservation. type reservationModFcn func(*Reservation) error -// FlyoverSet is a map between a flyover IA,ingress,egress and its corresponding collection of +// FlyoverMap is a map between a flyover IA,ingress,egress and its corresponding collection of // flyover objects (each of them can have e.g. different starting times). -type FlyoverSet map[BaseHop][]*Flyover +type FlyoverMap map[BaseHop][]*Flyover // WithScionPath allows to build a Reservation based on the SCION path and flyovers passed as // arguments. // The flyovers are chosen from the map in order of appearance iff they are suitable, i.e. if // they have a validity period intersecting with now. -func WithScionPath(p snet.Path, flyovers FlyoverSet) reservationModFcn { +func WithScionPath(p snet.Path, flyovers FlyoverMap) reservationModFcn { return func(r *Reservation) error { switch p := p.Dataplane().(type) { case path.SCION: @@ -168,16 +149,29 @@ func WithNow(now time.Time) reservationModFcn { } } +// WithMinBW modifies the minimum bandwidth required when filtering flyovers at the time of +// reservation creation. +func WithMinBW(bw uint16) reservationModFcn { + return func(r *Reservation) error { + r.minBW = bw + return nil + } +} + func (r *Reservation) Destination() addr.IA { return r.hops[len(r.hops)-1].Flyover.IA } +func (r *Reservation) GetHummingbirdPath() *hummingbird.Decoded { + return r.dec +} + // FlyoverPerHopField returns a slice of pointers to flyovers, one per hop field present in the path, // i.e. the length of the slice is the hop field count. // If a hop field is not covered by a flyover, nil is used in its place. func (r *Reservation) FlyoverPerHopField() []*Flyover { flyovers := make([]*Flyover, len(r.dec.HopFields)) - for hopIdx, i := 0, 0; i < len(flyovers); i++ { + for hopIdx, i := 0, 0; i < len(flyovers) && hopIdx < len(r.hops); i++ { var flyover *Flyover if r.hops[hopIdx].Hopfield == &r.dec.HopFields[i] { flyover = r.hops[hopIdx].Flyover @@ -189,6 +183,10 @@ func (r *Reservation) FlyoverPerHopField() []*Flyover { return flyovers } +func (r *Reservation) FlyoverAndHFCount() (int, int) { + return len(r.hops), len(r.dec.HopFields) +} + // DeriveDataPlanePath sets pathmeta timestamps and increments duplicate detection counter and // updates MACs of all flyoverfields. func (r *Reservation) DeriveDataPlanePath( @@ -229,7 +227,7 @@ func (r *Reservation) DeriveDataPlanePath( } func (r *Reservation) newHopSelectFlyover(ia addr.IA, in, eg uint16, - hf *hummingbird.FlyoverHopField, flyoverSet FlyoverSet) { + hf *hummingbird.FlyoverHopField, flyoverSet FlyoverMap) { // Look for a valid flyover. now := uint32(r.now.Unix()) @@ -240,7 +238,9 @@ func (r *Reservation) newHopSelectFlyover(ia addr.IA, in, eg uint16, } flyovers := flyoverSet[k] for _, flyover := range flyovers { - if flyover.StartTime <= now && uint32(flyover.Duration) >= now-flyover.StartTime { + if flyover.StartTime <= now && uint32(flyover.Duration) >= now-flyover.StartTime && + flyover.Bw >= r.minBW { + r.dec.NumLines += 2 r.newHop(ia, in, eg, hf, flyover) break diff --git a/pkg/hummingbird/reservation_test.go b/pkg/hummingbird/reservation_test.go index 8b7c6cc9fc..5ce5339933 100644 --- a/pkg/hummingbird/reservation_test.go +++ b/pkg/hummingbird/reservation_test.go @@ -43,6 +43,10 @@ func TestReservationWithScionPath(t *testing.T) { assert.NoError(t, err) assert.Equal(t, hbirdPath, scionPath) + + // Check that the number of flyovers, nil or otherwise, is the same as hop fields. + flyovers := r.FlyoverPerHopField() + assert.Equal(t, len(decodedScionTestPath.HopFields), len(flyovers)) } func TestReservationWithHbirdPath(t *testing.T) { @@ -52,7 +56,6 @@ func TestReservationWithHbirdPath(t *testing.T) { hummingbird.WithNow(fixedTime), hummingbird.WithExistingHbirdPath( decodedHbirdTestPathFlyovers, - // selectUsedFlyovers(t, testFlyoverFieldsReserved, testExpectedFlyovers)), testExpectedFlyovers), ) assert.NoError(t, err) @@ -68,8 +71,8 @@ func TestReservationWithHbirdPath(t *testing.T) { require.Equal(t, expected, r) } -func flyoverSliceToMap(flyovers []hummingbird.Flyover) hummingbird.FlyoverSet { - m := make(hummingbird.FlyoverSet) +func flyoverSliceToMap(flyovers []hummingbird.Flyover) hummingbird.FlyoverMap { + m := make(hummingbird.FlyoverMap) for _, flyover := range flyovers { clone := flyover m[clone.BaseHop] = append(m[clone.BaseHop], &clone) From 9f7a943d3363e72a4a289e353b0b28a97bfc32ab Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Tue, 19 Dec 2023 17:24:48 +0100 Subject: [PATCH 086/100] Update protobuf definition for bandwith. --- pkg/proto/daemon/hummingbird.pb.go | 6 +++--- proto/daemon/v1/hummingbird.proto | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/proto/daemon/hummingbird.pb.go b/pkg/proto/daemon/hummingbird.pb.go index 86edf4020b..5b9b694d17 100644 --- a/pkg/proto/daemon/hummingbird.pb.go +++ b/pkg/proto/daemon/hummingbird.pb.go @@ -198,7 +198,7 @@ type GetReservationsRequest struct { SourceIsdAs uint64 `protobuf:"varint,1,opt,name=source_isd_as,json=sourceIsdAs,proto3" json:"source_isd_as,omitempty"` DestinationIsdAs uint64 `protobuf:"varint,2,opt,name=destination_isd_as,json=destinationIsdAs,proto3" json:"destination_isd_as,omitempty"` Refresh bool `protobuf:"varint,3,opt,name=refresh,proto3" json:"refresh,omitempty"` - MinBandwidth uint64 `protobuf:"varint,4,opt,name=min_bandwidth,json=minBandwidth,proto3" json:"min_bandwidth,omitempty"` + MinBandwidth uint32 `protobuf:"varint,4,opt,name=min_bandwidth,json=minBandwidth,proto3" json:"min_bandwidth,omitempty"` } func (x *GetReservationsRequest) Reset() { @@ -254,7 +254,7 @@ func (x *GetReservationsRequest) GetRefresh() bool { return false } -func (x *GetReservationsRequest) GetMinBandwidth() uint64 { +func (x *GetReservationsRequest) GetMinBandwidth() uint32 { if x != nil { return x.MinBandwidth } @@ -502,7 +502,7 @@ var file_proto_daemon_v1_hummingbird_proto_rawDesc = []byte{ 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x73, 0x64, 0x41, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x69, 0x6e, 0x5f, 0x62, - 0x61, 0x6e, 0x64, 0x77, 0x69, 0x64, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, + 0x61, 0x6e, 0x64, 0x77, 0x69, 0x64, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6d, 0x69, 0x6e, 0x42, 0x61, 0x6e, 0x64, 0x77, 0x69, 0x64, 0x74, 0x68, 0x22, 0x5b, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0c, 0x72, 0x65, 0x73, 0x65, 0x72, diff --git a/proto/daemon/v1/hummingbird.proto b/proto/daemon/v1/hummingbird.proto index 328dca38e2..561789477a 100644 --- a/proto/daemon/v1/hummingbird.proto +++ b/proto/daemon/v1/hummingbird.proto @@ -40,8 +40,8 @@ message GetReservationsRequest { // Choose to fetch fresh paths for this request instead // of having the server reply from its cache. bool refresh = 3; - // The minimum bandwidth for the obtained reservations. - uint64 min_bandwidth = 4; + // The minimum bandwidth for the obtained reservations, 16 bits. + uint32 min_bandwidth = 4; } message GetReservationsResponse { From c3f48989c2a3e7449c3432acb6cd71aa91f950f8 Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Thu, 21 Dec 2023 16:21:48 +0100 Subject: [PATCH 087/100] Added DB logic. --- daemon/internal/servers/BUILD.bazel | 2 + daemon/internal/servers/grpc.go | 3 +- daemon/internal/servers/grpc_hummingbird.go | 6 +- .../internal/servers/grpc_hummingbird_test.go | 19 +- private/hummingbirddb/db.go | 4 +- private/storage/BUILD.bazel | 2 + .../storage/hummingbird/sqlite/BUILD.bazel | 32 ++ private/storage/hummingbird/sqlite/schema.go | 38 +++ private/storage/hummingbird/sqlite/sqlite.go | 257 ++++++++++++++++ .../storage/hummingbird/sqlite/sqlite_test.go | 282 ++++++++++++++++++ private/storage/storage.go | 47 +++ 11 files changed, 683 insertions(+), 9 deletions(-) create mode 100644 private/storage/hummingbird/sqlite/BUILD.bazel create mode 100644 private/storage/hummingbird/sqlite/schema.go create mode 100644 private/storage/hummingbird/sqlite/sqlite.go create mode 100644 private/storage/hummingbird/sqlite/sqlite_test.go diff --git a/daemon/internal/servers/BUILD.bazel b/daemon/internal/servers/BUILD.bazel index 33c7b83a73..65db5bb795 100644 --- a/daemon/internal/servers/BUILD.bazel +++ b/daemon/internal/servers/BUILD.bazel @@ -26,6 +26,7 @@ go_library( "//pkg/proto/daemon:go_default_library", "//pkg/snet:go_default_library", "//pkg/snet/path:go_default_library", + "//private/hummingbirddb:go_default_library", "//private/revcache:go_default_library", "//private/topology:go_default_library", "//private/trust:go_default_library", @@ -50,6 +51,7 @@ go_test( "//pkg/slayers/path/scion:go_default_library", "//pkg/snet:go_default_library", "//pkg/snet/path:go_default_library", + "//private/hummingbirddb:go_default_library", "@com_github_stretchr_testify//require:go_default_library", ], ) diff --git a/daemon/internal/servers/grpc.go b/daemon/internal/servers/grpc.go index 993a70a0d8..39305c115f 100644 --- a/daemon/internal/servers/grpc.go +++ b/daemon/internal/servers/grpc.go @@ -40,6 +40,7 @@ import ( sdpb "github.com/scionproto/scion/pkg/proto/daemon" "github.com/scionproto/scion/pkg/snet" snetpath "github.com/scionproto/scion/pkg/snet/path" + "github.com/scionproto/scion/private/hummingbirddb" "github.com/scionproto/scion/private/revcache" "github.com/scionproto/scion/private/topology" "github.com/scionproto/scion/private/trust" @@ -61,7 +62,7 @@ type DaemonServer struct { ASInspector trust.Inspector DRKeyClient *drkey_daemon.ClientEngine - HummingbirdFetcher HummingbirdFetcher + FlyoverDB hummingbirddb.DB Metrics Metrics diff --git a/daemon/internal/servers/grpc_hummingbird.go b/daemon/internal/servers/grpc_hummingbird.go index 5e3a57e41e..81639ab31a 100644 --- a/daemon/internal/servers/grpc_hummingbird.go +++ b/daemon/internal/servers/grpc_hummingbird.go @@ -29,10 +29,6 @@ import ( "github.com/scionproto/scion/pkg/snet/path" ) -type HummingbirdFetcher interface { - ListFlyovers(ctx context.Context, owners []addr.IA) ([]*hummingbird.Flyover, error) -} - func (s *DaemonServer) StoreFlyovers( ctx context.Context, req *sdpb.StoreFlyoversRequest, @@ -127,7 +123,7 @@ func (s *DaemonServer) getReservations( } // Get flyovers on any AS present in the paths. - flyovers, err := s.HummingbirdFetcher.ListFlyovers(ctx, IAs) + flyovers, err := s.FlyoverDB.GetFlyovers(ctx, IAs) if err != nil { return nil, err } diff --git a/daemon/internal/servers/grpc_hummingbird_test.go b/daemon/internal/servers/grpc_hummingbird_test.go index a55cfaae9c..3d0921774d 100644 --- a/daemon/internal/servers/grpc_hummingbird_test.go +++ b/daemon/internal/servers/grpc_hummingbird_test.go @@ -16,6 +16,7 @@ package servers import ( "context" + "database/sql" "testing" "github.com/scionproto/scion/pkg/addr" @@ -27,6 +28,7 @@ import ( "github.com/scionproto/scion/pkg/slayers/path/scion" "github.com/scionproto/scion/pkg/snet" "github.com/scionproto/scion/pkg/snet/path" + "github.com/scionproto/scion/private/hummingbirddb" "github.com/stretchr/testify/require" ) @@ -102,7 +104,7 @@ func TestGetReservation(t *testing.T) { Flyovers: flyoverDB, } s := &DaemonServer{ - HummingbirdFetcher: mockHbirdServer, + FlyoverDB: mockHbirdServer, } scionPaths := getMockScionPaths(t, tc.scionPaths) rsvs, err := s.getReservations(ctx, scionPaths, util.SecsToTime(currUnixTimestamp), 0) @@ -246,7 +248,12 @@ type mockServer struct { Flyovers []*hummingbird.Flyover } -func (m *mockServer) ListFlyovers( +func (m *mockServer) BeginTransaction(ctx context.Context, opts *sql.TxOptions, +) (hummingbirddb.Transaction, error) { + panic("not implemented") +} + +func (m *mockServer) GetFlyovers( ctx context.Context, owners []addr.IA, ) ([]*hummingbird.Flyover, error) { @@ -266,3 +273,11 @@ func (m *mockServer) ListFlyovers( } return ret, nil } + +func (m *mockServer) StoreFlyovers(ctx context.Context, flyovers []*hummingbird.Flyover) error { + panic("not implemented") +} + +func (m *mockServer) DeleteExpiredFlyovers(ctx context.Context) (int, error) { + panic("not implemented") +} diff --git a/private/hummingbirddb/db.go b/private/hummingbirddb/db.go index c72a94f03e..4193aa6b81 100644 --- a/private/hummingbirddb/db.go +++ b/private/hummingbirddb/db.go @@ -28,9 +28,11 @@ type DB interface { } type Read interface { - ListFlyovers(ctx context.Context, owners []addr.IA) ([]*hummingbird.Flyover, error) + GetFlyovers(ctx context.Context, IAs []addr.IA) ([]*hummingbird.Flyover, error) } type Write interface { + StoreFlyovers(ctx context.Context, flyovers []*hummingbird.Flyover) error + DeleteExpiredFlyovers(ctx context.Context) (int, error) } type ReadWrite interface { diff --git a/private/storage/BUILD.bazel b/private/storage/BUILD.bazel index b2a9d0176d..e8caf464d0 100644 --- a/private/storage/BUILD.bazel +++ b/private/storage/BUILD.bazel @@ -14,6 +14,7 @@ go_library( "//pkg/drkey:go_default_library", "//pkg/log:go_default_library", "//private/config:go_default_library", + "//private/hummingbirddb:go_default_library", "//private/pathdb:go_default_library", "//private/periodic:go_default_library", "//private/revcache:go_default_library", @@ -25,6 +26,7 @@ go_library( "//private/storage/drkey/level1/sqlite:go_default_library", "//private/storage/drkey/level2/sqlite:go_default_library", "//private/storage/drkey/secret/sqlite:go_default_library", + "//private/storage/hummingbird/sqlite:go_default_library", "//private/storage/path/sqlite:go_default_library", "//private/storage/trust:go_default_library", "//private/storage/trust/sqlite:go_default_library", diff --git a/private/storage/hummingbird/sqlite/BUILD.bazel b/private/storage/hummingbird/sqlite/BUILD.bazel new file mode 100644 index 0000000000..e93ee151ba --- /dev/null +++ b/private/storage/hummingbird/sqlite/BUILD.bazel @@ -0,0 +1,32 @@ +load("//tools/lint:go.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = [ + "schema.go", + "sqlite.go", + ], + importpath = "github.com/scionproto/scion/private/storage/hummingbird/sqlite", + visibility = ["//visibility:public"], + deps = [ + "//pkg/addr:go_default_library", + "//pkg/hummingbird:go_default_library", + "//pkg/private/serrors:go_default_library", + "//private/hummingbirddb:go_default_library", + "//private/storage/db:go_default_library", + ], +) + +go_test( + name = "go_default_test", + srcs = ["sqlite_test.go"], + embed = [":go_default_library"], + deps = [ + "//pkg/addr:go_default_library", + "//pkg/hummingbird:go_default_library", + "//pkg/private/util:go_default_library", + "//pkg/private/xtest:go_default_library", + "@com_github_stretchr_testify//assert:go_default_library", + "@com_github_stretchr_testify//require:go_default_library", + ], +) diff --git a/private/storage/hummingbird/sqlite/schema.go b/private/storage/hummingbird/sqlite/schema.go new file mode 100644 index 0000000000..5eeedaf5f3 --- /dev/null +++ b/private/storage/hummingbird/sqlite/schema.go @@ -0,0 +1,38 @@ +// Copyright 2024 ETH Zurich +// +// 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. + +// This file contains an SQLite backend for the HummingbirdDB. + +package sqlite + +const ( + // SchemaVersion is the version of the SQLite schema understood by this backend. + // Whenever changes to the schema are made, this version number should be increased + // to prevent data corruption between incompatible database schemas. + SchemaVersion = 1 + // Schema is the SQLite database layout. + Schema = `CREATE TABLE Flyovers( + RowID INTEGER PRIMARY KEY, + ia INTEGER NOT NULL, + ingress INTEGER NOT NULL, + egress INTEGER NOT NULL, + resID INTEGER NOT NULL, + bw INTEGER NOT NULL, + notBefore INTEGER NOT NULL, + notAfter INTEGER NOT NULL, + ak BLOB NOT NULL, + UNIQUE(ia,resID) ON CONFLICT REPLACE + ); + ` +) diff --git a/private/storage/hummingbird/sqlite/sqlite.go b/private/storage/hummingbird/sqlite/sqlite.go new file mode 100644 index 0000000000..4011956cec --- /dev/null +++ b/private/storage/hummingbird/sqlite/sqlite.go @@ -0,0 +1,257 @@ +// Copyright 2017 ETH Zurich +// Copyright 2018 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. + +// This file contains an SQLite backend for the PathDB. + +package sqlite + +import ( + "context" + "database/sql" + "strings" + "sync" + "time" + + "github.com/scionproto/scion/pkg/addr" + "github.com/scionproto/scion/pkg/hummingbird" + "github.com/scionproto/scion/pkg/private/serrors" + "github.com/scionproto/scion/private/hummingbirddb" + "github.com/scionproto/scion/private/storage/db" +) + +var _ hummingbirddb.DB = (*Backend)(nil) + +type Backend struct { + db *sql.DB + *executor +} + +// New returns a new SQLite backend opening a database at the given path. If +// no database exists a new database is be created. If the schema version of the +// stored database is different from the one in schema.go, an error is returned. +func New(path string) (*Backend, error) { + db, err := db.NewSqlite(path, Schema, SchemaVersion) + if err != nil { + return nil, serrors.WrapStr("opening db", err, "schema", Schema) + } + return &Backend{ + executor: &executor{ + db: db, + }, + db: db, + }, nil +} + +func (b *Backend) Close() error { + return b.db.Close() +} + +func (b *Backend) SetMaxOpenConns(maxOpenConns int) { + b.db.SetMaxOpenConns(maxOpenConns) +} +func (b *Backend) SetMaxIdleConns(maxIdleConns int) { + b.db.SetMaxIdleConns(maxIdleConns) +} + +func (b *Backend) BeginTransaction(ctx context.Context, + opts *sql.TxOptions) (hummingbirddb.Transaction, error) { + + b.Lock() + defer b.Unlock() + tx, err := b.db.BeginTx(ctx, opts) + if err != nil { + return nil, serrors.WrapStr("Failed to create transaction", err) + } + return &transaction{ + executor: &executor{ + db: tx, + }, + tx: tx, + }, nil +} + +var _ (hummingbirddb.Transaction) = (*transaction)(nil) + +type transaction struct { + *executor + tx *sql.Tx +} + +func (tx *transaction) Commit() error { + tx.Lock() + defer tx.Unlock() + return tx.tx.Commit() +} + +func (tx *transaction) Rollback() error { + tx.Lock() + defer tx.Unlock() + return tx.tx.Rollback() +} + +var _ (hummingbirddb.ReadWrite) = (*executor)(nil) + +type executor struct { + sync.RWMutex + db db.Sqler +} + +type IASet map[addr.IA]struct{} + +func (e *executor) GetFlyovers(ctx context.Context, IAs []addr.IA) ([]*hummingbird.Flyover, error) { + var flyovers []*hummingbird.Flyover + err := db.DoInTx(ctx, e.db, func(ctx context.Context, tx *sql.Tx) error { + var err error + flyovers, err = getFlyovers(ctx, tx, IAs) + return err + }) + return flyovers, err +} + +func (e *executor) StoreFlyovers(ctx context.Context, flyovers []*hummingbird.Flyover, +) error { + + err := db.DoInTx(ctx, e.db, func(ctx context.Context, tx *sql.Tx) error { + return storeFlyovers(ctx, tx, flyovers) + }) + return err +} + +func (e *executor) DeleteExpiredFlyovers(ctx context.Context) (n int, err error) { + return e.deleteExpiredFlyovers(ctx, time.Now()) +} + +func (e *executor) deleteExpiredFlyovers( + ctx context.Context, + notAfter time.Time, +) (n int, err error) { + + db.DoInTx(ctx, e.db, func(ctx context.Context, tx *sql.Tx) error { + n, err = deleteExpired(ctx, tx, notAfter) + return err + }) + // n and err have been already set in the closure, just return. + return +} + +func getFlyovers(ctx context.Context, tx *sql.Tx, IAs []addr.IA) ([]*hummingbird.Flyover, error) { + query := "SELECT ia,ingress,egress,resID,bw,notBefore,notAfter,ak FROM flyovers" + queryParams := []any{} + // If there are any parameters, format them for SQL. + if len(IAs) > 0 { + query += " WHERE IA in (" + + strings.Repeat("?,", len(IAs)-1) + query += "?)" + for _, ia := range IAs { + queryParams = append(queryParams, uint64(ia)) + } + } + // Query with or without query parameters. + rows, err := tx.QueryContext(ctx, query, queryParams...) + if err != nil { + return nil, serrors.WrapStr("looking for flyovers in DB", err, "q", query) + } + flyovers := make([]*hummingbird.Flyover, 0) + for rows.Next() { + var ia uint64 + var ingress uint16 + var egress uint16 + var resID uint32 + var bw uint16 + var notBefore uint32 + var notAfter uint32 + var ak []byte + if err := rows.Scan(&ia, &ingress, &egress, &resID, &bw, ¬Before, ¬After, + &ak); err != nil { + + return nil, serrors.WrapStr("error reading flyover from DB", err) + } + // Convert ak from slice to array. + var akArray [16]byte + copy(akArray[:], ak) + // Add the flyover. + flyovers = append(flyovers, &hummingbird.Flyover{ + BaseHop: hummingbird.BaseHop{ + IA: addr.IA(ia), + Ingress: ingress, + Egress: egress, + }, + ResID: resID, + Bw: bw, + StartTime: notBefore, + Duration: uint16(notAfter - notBefore), + Ak: akArray, + }) + } + return flyovers, nil +} + +func storeFlyovers( + ctx context.Context, + tx *sql.Tx, + flyovers []*hummingbird.Flyover, +) error { + + const numCols = 8 // 8 columns + rowParams := "(" + strings.Repeat("?,", numCols-1) + "?)" // => (?,?,?,?,?,?,?,?) + // query has the form: + // INSERT INTO flyovers (ia,ingress,egress,resID,bw,notBefore,notAfter,ak) VALUES + // (?,?,?,?,?,?,?,?),(?,?,?,?,?,?,?,?) + // for a total of number of flyovers. + query := "INSERT INTO flyovers (ia,ingress,egress,resID,bw,notBefore,notAfter,ak) VALUES " + + strings.Repeat(rowParams+",", len(flyovers)-1) + rowParams + + params := make([]any, 0, len(flyovers)*numCols) + for _, f := range flyovers { + params = append(params, + uint64(f.IA), + uint16(f.Ingress), + uint16(f.Egress), + uint32(f.ResID), + uint16(f.Bw), + f.StartTime, + f.StartTime+uint32(f.Duration), + f.Ak[:], + ) + } + + res, err := tx.ExecContext(ctx, query, params...) + if err != nil { + return serrors.WrapStr("storing flyovers", err, "query", query) + } + n, err := res.RowsAffected() + if err != nil { + return serrors.WrapStr("checking inserted flyovers, cannot retrieve num. affected rows", + err) + } + if int(n) != len(flyovers) { + return serrors.New("storing flyovers: affected rows differs from request", + "affected", n, "requested", len(flyovers)) + } + return nil +} + +func deleteExpired(ctx context.Context, tx *sql.Tx, now time.Time) (int, error) { + query := "DELETE FROM flyovers WHERE notAfter < ?" + res, err := tx.ExecContext(ctx, query, now.Unix()) + if err != nil { + return 0, serrors.WrapStr("deleting expired flyovers", err) + } + n, err := res.RowsAffected() + if err != nil { + return 0, serrors.WrapStr("computing affected expired flyovers", err) + } + return int(n), nil +} diff --git a/private/storage/hummingbird/sqlite/sqlite_test.go b/private/storage/hummingbird/sqlite/sqlite_test.go new file mode 100644 index 0000000000..d7e70631cf --- /dev/null +++ b/private/storage/hummingbird/sqlite/sqlite_test.go @@ -0,0 +1,282 @@ +// Copyright 2017 ETH Zurich +// Copyright 2018 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 sqlite + +import ( + "context" + "fmt" + "os" + "path" + "path/filepath" + "strings" + "testing" + "time" + + "github.com/scionproto/scion/pkg/addr" + "github.com/scionproto/scion/pkg/hummingbird" + "github.com/scionproto/scion/pkg/private/util" + "github.com/scionproto/scion/pkg/private/xtest" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var ( + timeout = time.Second + flyover1 = &hummingbird.Flyover{ + BaseHop: hummingbird.BaseHop{ + IA: xtest.MustParseIA("1-ff00:0:111"), + Ingress: 42, + Egress: 43, + }, + ResID: 12345, + Bw: 666, + Ak: [16]byte{1, 2, 3, 3, 3, 3}, + StartTime: 100, + Duration: 10, + } + flyover2 = &hummingbird.Flyover{ + BaseHop: hummingbird.BaseHop{ + IA: xtest.MustParseIA("1-ff00:0:112"), + Ingress: 42, + Egress: 43, + }, + ResID: 12346, + Bw: 666, + Ak: [16]byte{1, 2, 3, 3, 3, 3}, + StartTime: 333, + Duration: 10, + } +) + +// TestOpenExisting tests that New does not overwrite an existing database if +// versions match. +func TestOpenExisting(t *testing.T) { + ctx, cancelF := context.WithTimeout(context.Background(), timeout) + defer cancelF() + + b, tmpF := setupDB(t) + defer cleanup(tmpF) + + // Insert a new flyover. + f := &hummingbird.Flyover{ + BaseHop: hummingbird.BaseHop{ + IA: xtest.MustParseIA("1-ff00:0:111"), + Ingress: 42, + Egress: 43, + }, + ResID: 12345, + Bw: 666, + Ak: [16]byte{1, 2, 3, 3, 3, 3}, + StartTime: 333, + Duration: 10, + } + err := b.StoreFlyovers(ctx, []*hummingbird.Flyover{f}) + require.NoError(t, err) + b.db.Close() + + // Open again. + b, err = New(tmpF) + require.NoError(t, err) + // Check the flyover is present + flyovers, err := b.GetFlyovers(ctx, nil) + require.NoError(t, err) + require.Equal(t, 1, len(flyovers)) + require.Equal(t, f, flyovers[0]) +} + +// TestOpenNewer tests that New does not overwrite an existing database if it's +// of a newer version. +func TestOpenNewer(t *testing.T) { + b, tmpF := setupDB(t) + defer cleanup(tmpF) + // Write a newer version + _, err := b.db.Exec(fmt.Sprintf("PRAGMA user_version = %d", SchemaVersion+1)) + require.NoError(t, err) + b.db.Close() + // Call + b, err = New(tmpF) + // Test + assert.Error(t, err) + assert.Nil(t, b) +} + +func TestGetFlyovers(t *testing.T) { + allFlyovers := []*hummingbird.Flyover{ + flyover1, + flyover2, + } + testCases := map[string]struct { + IAs []string + expected []*hummingbird.Flyover + }{ + "nil-filter": { + IAs: nil, + expected: []*hummingbird.Flyover{flyover1, flyover2}, + }, + "all": { + IAs: []string{}, + expected: []*hummingbird.Flyover{flyover1, flyover2}, + }, + "one": { + IAs: []string{"1-ff00:0:112"}, + expected: []*hummingbird.Flyover{flyover2}, + }, + "two": { + IAs: []string{"1-ff00:0:111", "1-ff00:0:112"}, + expected: []*hummingbird.Flyover{flyover1, flyover2}, + }, + } + + for name, tc := range testCases { + name, tc := name, tc + t.Run(name, func(t *testing.T) { + t.Parallel() + + ctx, cancelF := context.WithTimeout(context.Background(), timeout) + defer cancelF() + // Create new DB + b, tmpF := setupDB(t) + defer b.Close() + defer cleanup(tmpF) + + // Insert all flyovers. + err := b.StoreFlyovers(ctx, allFlyovers) + require.NoError(t, err) + + // Retrieve. + IAs := make([]addr.IA, len(tc.IAs)) + for i, ia := range tc.IAs { + IAs[i] = xtest.MustParseIA(ia) + } + flyovers, err := b.GetFlyovers(ctx, IAs) + require.NoError(t, err) + // Check. + require.EqualValues(t, tc.expected, flyovers) + }) + } +} + +func TestInsertUniqueFlyovers(t *testing.T) { + // All flyovers have a unique (IA,resID) + flyovers := []*hummingbird.Flyover{ + flyover1, + flyover2, + } + + ctx, cancelF := context.WithTimeout(context.Background(), timeout) + defer cancelF() + + b, tmpF := setupDB(t) + defer b.Close() + defer cleanup(tmpF) + + // Store a sequence of flyovers. + err := b.StoreFlyovers(ctx, flyovers) + require.NoError(t, err) + + // Check stored flyovers. + expected := flyovers + flyovers, err = b.GetFlyovers(ctx, nil) + require.NoError(t, err) + require.Equal(t, expected, flyovers) +} + +func TestInsertNonUniqueFlyovers(t *testing.T) { + // All flyovers have a unique (IA,resID) + flyovers := []*hummingbird.Flyover{ + flyover1, + flyover1, + } + + ctx, cancelF := context.WithTimeout(context.Background(), timeout) + defer cancelF() + + b, tmpF := setupDB(t) + defer b.Close() + defer cleanup(tmpF) + + // Store a sequence of flyovers. + err := b.StoreFlyovers(ctx, flyovers) + require.NoError(t, err) + + // Check stored flyovers. + expected := []*hummingbird.Flyover{flyover1} + flyovers, err = b.GetFlyovers(ctx, nil) + require.NoError(t, err) + require.Equal(t, expected, flyovers) +} + +func TestDeleteExpired(t *testing.T) { + ctx, cancelF := context.WithTimeout(context.Background(), timeout) + defer cancelF() + + b, tmpF := setupDB(t) + defer cleanup(tmpF) + + // Insert two flyovers. They (start,end) at (100,110)(333,343) respectively. + allFlyovers := []*hummingbird.Flyover{flyover1, flyover2} + err := b.StoreFlyovers(ctx, allFlyovers) + require.NoError(t, err) + + // Expire as if time was now 10. + n, err := b.deleteExpiredFlyovers(ctx, util.SecsToTime(0)) + require.NoError(t, err) + require.Equal(t, 0, n) + + // Expire as if time was now 101. + n, err = b.deleteExpiredFlyovers(ctx, util.SecsToTime(101)) + require.NoError(t, err) + require.Equal(t, 0, n) + + // Expire as if time was now 111. + n, err = b.deleteExpiredFlyovers(ctx, util.SecsToTime(111)) + require.NoError(t, err) + require.Equal(t, 1, n) // one has been deleted + // Check we still have flyover2. + flyovers, err := b.GetFlyovers(ctx, nil) + require.NoError(t, err) + require.Len(t, flyovers, 1) + require.Equal(t, flyover2, flyovers[0]) + + // Expire as if time was now 500. + n, err = b.deleteExpiredFlyovers(ctx, util.SecsToTime(500)) + require.NoError(t, err) + require.Equal(t, 1, n) // one has been deleted + // Check we don't have flyovers. + flyovers, err = b.GetFlyovers(ctx, nil) + require.NoError(t, err) + require.Len(t, flyovers, 0) +} + +func setupDB(t *testing.T) (*Backend, string) { + tmpFile := tempFilename(t) + b, err := New(tmpFile) + // b, err := New("file::memory:") + require.NoError(t, err, "Failed to open DB") + return b, tmpFile +} + +func tempFilename(t *testing.T) string { + dir, err := os.MkdirTemp("", "hummingbirddb-sqlite") + require.NoError(t, err) + n := t.Name() + n = strings.ReplaceAll(n, "/", "-") + return path.Join(dir, n) +} + +func cleanup(tmpFile string) { + os.RemoveAll(filepath.Dir(tmpFile)) +} diff --git a/private/storage/storage.go b/private/storage/storage.go index bc400dd462..f6f0164e01 100644 --- a/private/storage/storage.go +++ b/private/storage/storage.go @@ -26,6 +26,7 @@ import ( "github.com/scionproto/scion/pkg/drkey" "github.com/scionproto/scion/pkg/log" "github.com/scionproto/scion/private/config" + "github.com/scionproto/scion/private/hummingbirddb" "github.com/scionproto/scion/private/pathdb" "github.com/scionproto/scion/private/periodic" "github.com/scionproto/scion/private/revcache" @@ -37,9 +38,11 @@ import ( sqlitelevel1 "github.com/scionproto/scion/private/storage/drkey/level1/sqlite" sqlitelevel2 "github.com/scionproto/scion/private/storage/drkey/level2/sqlite" sqlitesecret "github.com/scionproto/scion/private/storage/drkey/secret/sqlite" + sqlitehbirddb "github.com/scionproto/scion/private/storage/hummingbird/sqlite" sqlitepathdb "github.com/scionproto/scion/private/storage/path/sqlite" truststorage "github.com/scionproto/scion/private/storage/trust" sqlitetrustdb "github.com/scionproto/scion/private/storage/trust/sqlite" + "github.com/scionproto/scion/private/trust" ) @@ -105,6 +108,11 @@ type PathDB interface { pathdb.DB } +type HbirdDB interface { + io.Closer + hummingbirddb.DB +} + var _ (config.Config) = (*DBConfig)(nil) // DBConfig is the configuration for the connection to a database. @@ -236,6 +244,45 @@ func (b pathDBWithCleaner) Close() error { return b.dbCloser.Close() } +func NewHummingbirdStorage(c DBConfig) (HbirdDB, error) { + log.Info("Connecting HummingbirdDB", "backend", BackendSqlite, "connection", c.Connection) + db, err := sqlitehbirddb.New(c.Connection) + if err != nil { + return nil, err + } + SetConnLimits(db, c) + + // Start a periodic task that cleans up the expired path segments. + cleaner := periodic.Start( + cleaner.New( + func(ctx context.Context) (int, error) { + return db.DeleteExpiredFlyovers(ctx) + }, + "control_pathstorage_cleaner", + ), + 30*time.Second, + 30*time.Second, + ) + return hbirdDBWithCleaner{ + DB: db, + cleaner: cleaner, + dbCloser: db, + }, nil +} + +// pathDBWithCleaner implements the path DB interface and stops both the +// database and the cleanup task on Close. +type hbirdDBWithCleaner struct { + hummingbirddb.DB + cleaner *periodic.Runner + dbCloser io.Closer +} + +func (b hbirdDBWithCleaner) Close() error { + b.cleaner.Kill() + return b.dbCloser.Close() +} + func NewRevocationStorage() revcache.RevCache { return memrevcache.New() } From 0e3ee1e3797c1578b6718339fa7aaa83a9e4744c Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Thu, 21 Dec 2023 16:43:54 +0100 Subject: [PATCH 088/100] Hook scion daemon with a hummingbird DB. --- daemon/BUILD.bazel | 1 + daemon/cmd/daemon/main.go | 14 ++++++++++++++ daemon/config/config.go | 10 ++++++++++ daemon/daemon.go | 4 ++++ private/storage/storage.go | 11 ++++++++++- 5 files changed, 39 insertions(+), 1 deletion(-) diff --git a/daemon/BUILD.bazel b/daemon/BUILD.bazel index 420946cf80..4bcda5f304 100644 --- a/daemon/BUILD.bazel +++ b/daemon/BUILD.bazel @@ -17,6 +17,7 @@ go_library( "//pkg/private/prom:go_default_library", "//pkg/private/serrors:go_default_library", "//private/env:go_default_library", + "//private/hummingbirddb:go_default_library", "//private/revcache:go_default_library", "//private/trust:go_default_library", "//private/trust/grpc:go_default_library", diff --git a/daemon/cmd/daemon/main.go b/daemon/cmd/daemon/main.go index ded6ef1106..1f7777268f 100644 --- a/daemon/cmd/daemon/main.go +++ b/daemon/cmd/daemon/main.go @@ -113,12 +113,24 @@ func realMain(ctx context.Context) error { }) defer pathDB.Close() defer revCache.Close() + + hbirdDB, err := storage.NewHummingbirdStorage(globalCfg.HbirdDB) + if err != nil { + return serrors.WrapStr("initializing hummingbird storage", err) + } + defer hbirdDB.Close() + cleaner := periodic.Start(pathdb.NewCleaner(pathDB, "sd_segments"), 300*time.Second, 295*time.Second) defer cleaner.Stop() rcCleaner := periodic.Start(revcache.NewCleaner(revCache, "sd_revocation"), 10*time.Second, 10*time.Second) defer rcCleaner.Stop() + defer cleaner.Stop() + + // Hummingbird DB has its cleaner started already in the New method. Use that one. + hbirdCleaner := hbirdDB.GetCleaner() + defer hbirdCleaner.Stop() dialer := &libgrpc.TCPDialer{ SvcResolver: func(dst addr.SVC) []resolver.Address { @@ -271,9 +283,11 @@ func realMain(ctx context.Context) error { Cfg: globalCfg.SD, }, ), + Engine: engine, RevCache: revCache, DRKeyClient: drkeyClientEngine, + FlyoverDB: hbirdDB, }, )) diff --git a/daemon/config/config.go b/daemon/config/config.go index d4724e83f2..1da619a903 100644 --- a/daemon/config/config.go +++ b/daemon/config/config.go @@ -46,6 +46,7 @@ type Config struct { Tracing env.Tracing `toml:"tracing,omitempty"` TrustDB storage.DBConfig `toml:"trust_db,omitempty"` PathDB storage.DBConfig `toml:"path_db,omitempty"` + HbirdDB storage.DBConfig `toml:"hbird_db,omitempty"` SD SDConfig `toml:"sd,omitempty"` TrustEngine trustengine.Config `toml:"trustengine,omitempty"` DRKeyLevel2DB storage.DBConfig `toml:"drkey_level2_db,omitempty"` @@ -61,6 +62,7 @@ func (cfg *Config) InitDefaults() { &cfg.Tracing, cfg.TrustDB.WithDefault(fmt.Sprintf(storage.DefaultTrustDBPath, "sd")), cfg.PathDB.WithDefault(fmt.Sprintf(storage.DefaultPathDBPath, "sd")), + cfg.HbirdDB.WithDefault(fmt.Sprintf(storage.DefaultHbirdDBPath, "sd")), &cfg.SD, &cfg.TrustEngine, ) @@ -75,6 +77,7 @@ func (cfg *Config) Validate() error { &cfg.API, &cfg.TrustDB, &cfg.PathDB, + &cfg.HbirdDB, &cfg.SD, &cfg.TrustEngine, &cfg.DRKeyLevel2DB, @@ -103,6 +106,13 @@ func (cfg *Config) Sample(dst io.Writer, path config.Path, _ config.CtxMap) { ), "path_db", ), + config.OverrideName( + config.FormatData( + &cfg.HbirdDB, + fmt.Sprintf(storage.DefaultHbirdDBPath, "sd"), + ), + "hbird_db", + ), &cfg.SD, &cfg.TrustEngine, config.OverrideName( diff --git a/daemon/daemon.go b/daemon/daemon.go index 8fbc106938..94e7bb8295 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -36,6 +36,7 @@ import ( "github.com/scionproto/scion/pkg/private/prom" "github.com/scionproto/scion/pkg/private/serrors" "github.com/scionproto/scion/private/env" + "github.com/scionproto/scion/private/hummingbirddb" "github.com/scionproto/scion/private/revcache" "github.com/scionproto/scion/private/trust" trustgrpc "github.com/scionproto/scion/private/trust/grpc" @@ -114,6 +115,8 @@ type ServerConfig struct { Engine trust.Engine Topology servers.Topology DRKeyClient *drkey.ClientEngine + + FlyoverDB hummingbirddb.DB } // NewServer constructs a daemon API server. @@ -126,6 +129,7 @@ func NewServer(cfg ServerConfig) *servers.DaemonServer { ASInspector: cfg.Engine.Inspector, RevCache: cfg.RevCache, DRKeyClient: cfg.DRKeyClient, + FlyoverDB: cfg.FlyoverDB, Metrics: servers.Metrics{ PathsRequests: servers.RequestMetrics{ Requests: metrics.NewPromCounterFrom(prometheus.CounterOpts{ diff --git a/private/storage/storage.go b/private/storage/storage.go index f6f0164e01..093d70cb41 100644 --- a/private/storage/storage.go +++ b/private/storage/storage.go @@ -59,6 +59,7 @@ const ( DefaultDRKeyLevel1DBPath = "/share/cache/%s.drkey_level1.db" DefaultDRKeyLevel2DBPath = "/share/cache/%s.drkey_level2.db" DefaultDRKeySVDBPath = "/share/cache/%s.drkey_secret_value.db" + DefaultHbirdDBPath = "/share/cache/%s.hbird.db" ) // Default samples for various databases. @@ -81,6 +82,9 @@ var ( SampleDRKeySecretValueDB = DBConfig{ Connection: DefaultDRKeySVDBPath, } + SampleHbirdPathDB = DBConfig{ + Connection: DefaultHbirdDBPath, + } ) // SetID returns a clone of the configuration that has the ID set on the connection string. @@ -111,6 +115,7 @@ type PathDB interface { type HbirdDB interface { io.Closer hummingbirddb.DB + GetCleaner() *periodic.Runner } var _ (config.Config) = (*DBConfig)(nil) @@ -258,7 +263,7 @@ func NewHummingbirdStorage(c DBConfig) (HbirdDB, error) { func(ctx context.Context) (int, error) { return db.DeleteExpiredFlyovers(ctx) }, - "control_pathstorage_cleaner", + "hbirdstorage_cleaner", ), 30*time.Second, 30*time.Second, @@ -283,6 +288,10 @@ func (b hbirdDBWithCleaner) Close() error { return b.dbCloser.Close() } +func (b hbirdDBWithCleaner) GetCleaner() *periodic.Runner { + return b.cleaner +} + func NewRevocationStorage() revcache.RevCache { return memrevcache.New() } From e40b9cd3730c2106ba485d81366600b50b0ba05c Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Thu, 21 Dec 2023 16:53:31 +0100 Subject: [PATCH 089/100] Forward calls from scion daemon to hbird DB. --- daemon/internal/servers/grpc_hummingbird.go | 65 ++++++++-- .../internal/servers/grpc_hummingbird_test.go | 115 +++++++++++++++++- 2 files changed, 169 insertions(+), 11 deletions(-) diff --git a/daemon/internal/servers/grpc_hummingbird.go b/daemon/internal/servers/grpc_hummingbird.go index 81639ab31a..6a6ac96fcc 100644 --- a/daemon/internal/servers/grpc_hummingbird.go +++ b/daemon/internal/servers/grpc_hummingbird.go @@ -33,13 +33,29 @@ func (s *DaemonServer) StoreFlyovers( ctx context.Context, req *sdpb.StoreFlyoversRequest, ) (*sdpb.StoreFlyoversResponse, error) { - return nil, nil + + // Translate flyovers from protobuf and store them. + err := s.FlyoverDB.StoreFlyovers(ctx, convertFlyoversFromPB(req.Flyovers)) + if err != nil { + return nil, err + } + + return &sdpb.StoreFlyoversResponse{}, nil } func (s *DaemonServer) ListFlyovers( ctx context.Context, req *sdpb.ListFlyoversRequest, ) (*sdpb.ListFlyoversResponse, error) { - return nil, nil + + // Get all flyovers. + flyovers, err := s.FlyoverDB.GetFlyovers(ctx, nil) + if err != nil { + return nil, err + } + + return &sdpb.ListFlyoversResponse{ + Flyovers: convertFlyoversToPB(flyovers), + }, nil } func (s *DaemonServer) GetReservations( @@ -263,16 +279,12 @@ func convertReservationToPB(r *hummingbird.Reservation) (*sdpb.Reservation, erro // Prepare the flyovers. flyovers := r.FlyoverPerHopField() numF, numHF := r.FlyoverAndHFCount() - ret := &sdpb.Reservation{ + + return &sdpb.Reservation{ Raw: raw, Ratio: float64(numF) / float64(numHF), - Flyovers: make([]*sdpb.Flyover, len(flyovers)), - } - for i, f := range flyovers { - ret.Flyovers[i] = convertFlyoverToPB(f) - } - - return ret, nil + Flyovers: convertFlyoversToPB(flyovers), + }, nil } func convertFlyoverToPB(f *hummingbird.Flyover) *sdpb.Flyover { @@ -292,3 +304,36 @@ func convertFlyoverToPB(f *hummingbird.Flyover) *sdpb.Flyover { return ret } +func convertFlyoversToPB(flyovers []*hummingbird.Flyover) []*sdpb.Flyover { + ret := make([]*sdpb.Flyover, len(flyovers)) + for i, f := range flyovers { + ret[i] = convertFlyoverToPB(f) + } + return ret +} + +func convertFlyoverFromPB(f *sdpb.Flyover) *hummingbird.Flyover { + if f == nil { + return nil + } + ret := &hummingbird.Flyover{ + BaseHop: hummingbird.BaseHop{ + IA: addr.IA(f.Ia), + Ingress: uint16(f.Ingress), + Egress: uint16(f.Egress), + }, + Bw: uint16(f.Bw), + ResID: f.ResId, + StartTime: f.StartTime, + Duration: uint16(f.Duration), + } + copy(ret.Ak[:], f.Ak) + return ret +} +func convertFlyoversFromPB(flyovers []*sdpb.Flyover) []*hummingbird.Flyover { + ret := make([]*hummingbird.Flyover, len(flyovers)) + for i, f := range flyovers { + ret[i] = convertFlyoverFromPB(f) + } + return ret +} diff --git a/daemon/internal/servers/grpc_hummingbird_test.go b/daemon/internal/servers/grpc_hummingbird_test.go index 3d0921774d..082f91f8b1 100644 --- a/daemon/internal/servers/grpc_hummingbird_test.go +++ b/daemon/internal/servers/grpc_hummingbird_test.go @@ -24,6 +24,7 @@ import ( "github.com/scionproto/scion/pkg/private/common" "github.com/scionproto/scion/pkg/private/util" "github.com/scionproto/scion/pkg/private/xtest" + sdpb "github.com/scionproto/scion/pkg/proto/daemon" pathlayers "github.com/scionproto/scion/pkg/slayers/path" "github.com/scionproto/scion/pkg/slayers/path/scion" "github.com/scionproto/scion/pkg/snet" @@ -136,6 +137,113 @@ func TestGetReservation(t *testing.T) { } } +func TestStoreFlyovers(t *testing.T) { + deadline, _ := t.Deadline() + ctx, cancelF := context.WithDeadline(context.Background(), deadline) + defer cancelF() + + mockHbirdServer := &mockServer{ + Flyovers: nil, // empty + } + s := &DaemonServer{ + FlyoverDB: mockHbirdServer, + } + + expected := []*hummingbird.Flyover{ + { + BaseHop: hummingbird.BaseHop{ + IA: xtest.MustParseIA("1-ff00:0:333"), + Ingress: 3, + Egress: 2, + }, + ResID: 40, + Bw: 3, + StartTime: 10, + Duration: 60, + Ak: [16]byte{3, 4, 5, 6}, + }, + { // same as previous one, not unique (IA,resID) + BaseHop: hummingbird.BaseHop{ + IA: xtest.MustParseIA("1-ff00:0:333"), + Ingress: 3, + Egress: 2, + }, + ResID: 40, + Bw: 3, + StartTime: 10, + Duration: 60, + Ak: [16]byte{3, 4, 5, 6}, + }, + { + BaseHop: hummingbird.BaseHop{ + IA: xtest.MustParseIA("1-ff00:0:111"), + Ingress: 1, + Egress: 2, + }, + ResID: 40, + Bw: 3, + StartTime: 10, + Duration: 60, + Ak: [16]byte{3, 4, 5, 6}, + }, + } + req := &sdpb.StoreFlyoversRequest{ + Flyovers: convertFlyoversToPB(expected), + } + _, err := s.StoreFlyovers(ctx, req) + require.NoError(t, err) + + // Check DB. + require.EqualValues(t, expected, mockHbirdServer.Flyovers) +} + +func TestListFlyovers(t *testing.T) { + deadline, _ := t.Deadline() + ctx, cancelF := context.WithDeadline(context.Background(), deadline) + defer cancelF() + + expected := []*hummingbird.Flyover{ + { + BaseHop: hummingbird.BaseHop{ + IA: xtest.MustParseIA("1-ff00:0:333"), + Ingress: 3, + Egress: 2, + }, + ResID: 40, + Bw: 3, + StartTime: 10, + Duration: 60, + Ak: [16]byte{3, 4, 5, 6}, + }, + { + BaseHop: hummingbird.BaseHop{ + IA: xtest.MustParseIA("1-ff00:0:111"), + Ingress: 1, + Egress: 2, + }, + ResID: 40, + Bw: 3, + StartTime: 10, + Duration: 60, + Ak: [16]byte{3, 4, 5, 6}, + }, + } + + mockHbirdServer := &mockServer{ + Flyovers: expected, + } + s := &DaemonServer{ + FlyoverDB: mockHbirdServer, + } + + req := &sdpb.ListFlyoversRequest{} + res, err := s.ListFlyovers(ctx, req) + require.NoError(t, err) + + // Check response. + require.EqualValues(t, expected, convertFlyoversFromPB(res.Flyovers)) +} + func getMockScionPaths(t require.TestingT, paths [][]any) []path.Path { ret := make([]path.Path, len(paths)) for i, p := range paths { @@ -258,6 +366,10 @@ func (m *mockServer) GetFlyovers( owners []addr.IA, ) ([]*hummingbird.Flyover, error) { + if len(owners) == 0 { + return m.Flyovers, nil + } + // Create a set of the requested IAs. ownerMap := make(map[addr.IA]struct{}) for _, o := range owners { @@ -275,7 +387,8 @@ func (m *mockServer) GetFlyovers( } func (m *mockServer) StoreFlyovers(ctx context.Context, flyovers []*hummingbird.Flyover) error { - panic("not implemented") + m.Flyovers = append(m.Flyovers, flyovers...) + return nil } func (m *mockServer) DeleteExpiredFlyovers(ctx context.Context) (int, error) { From 134746708474ea12756273932d447ba0c6ef91d4 Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Thu, 21 Dec 2023 17:24:48 +0100 Subject: [PATCH 090/100] Make hbird-protobuf functions public. --- daemon/internal/servers/BUILD.bazel | 1 + daemon/internal/servers/grpc_hummingbird.go | 76 +--------------- .../internal/servers/grpc_hummingbird_test.go | 4 +- pkg/hummingbird/BUILD.bazel | 2 + pkg/hummingbird/translate.go | 91 +++++++++++++++++++ 5 files changed, 99 insertions(+), 75 deletions(-) create mode 100644 pkg/hummingbird/translate.go diff --git a/daemon/internal/servers/BUILD.bazel b/daemon/internal/servers/BUILD.bazel index 65db5bb795..9b52fc875a 100644 --- a/daemon/internal/servers/BUILD.bazel +++ b/daemon/internal/servers/BUILD.bazel @@ -47,6 +47,7 @@ go_test( "//pkg/private/common:go_default_library", "//pkg/private/util:go_default_library", "//pkg/private/xtest:go_default_library", + "//pkg/proto/daemon:go_default_library", "//pkg/slayers/path:go_default_library", "//pkg/slayers/path/scion:go_default_library", "//pkg/snet:go_default_library", diff --git a/daemon/internal/servers/grpc_hummingbird.go b/daemon/internal/servers/grpc_hummingbird.go index 6a6ac96fcc..61049f6c53 100644 --- a/daemon/internal/servers/grpc_hummingbird.go +++ b/daemon/internal/servers/grpc_hummingbird.go @@ -35,7 +35,7 @@ func (s *DaemonServer) StoreFlyovers( ) (*sdpb.StoreFlyoversResponse, error) { // Translate flyovers from protobuf and store them. - err := s.FlyoverDB.StoreFlyovers(ctx, convertFlyoversFromPB(req.Flyovers)) + err := s.FlyoverDB.StoreFlyovers(ctx, hummingbird.ConvertFlyoversFromPB(req.Flyovers)) if err != nil { return nil, err } @@ -54,7 +54,7 @@ func (s *DaemonServer) ListFlyovers( } return &sdpb.ListFlyoversResponse{ - Flyovers: convertFlyoversToPB(flyovers), + Flyovers: hummingbird.ConvertFlyoversToPB(flyovers), }, nil } @@ -81,7 +81,7 @@ func (s *DaemonServer) GetReservations( Reservations: make([]*sdpb.Reservation, len(paths)), } for i := range res.Reservations { - res.Reservations[i], err = convertReservationToPB(rsvs[i]) + res.Reservations[i], err = hummingbird.ConvertReservationToPB(rsvs[i]) if err != nil { return nil, err } @@ -267,73 +267,3 @@ func flyoversToMap(flyovers []*hummingbird.Flyover) hummingbird.FlyoverMap { } return ret } - -func convertReservationToPB(r *hummingbird.Reservation) (*sdpb.Reservation, error) { - // Prepare the hummingbird path. - p := r.GetHummingbirdPath() - raw := make([]byte, p.Len()) - if err := p.SerializeTo(raw); err != nil { - return nil, err - } - - // Prepare the flyovers. - flyovers := r.FlyoverPerHopField() - numF, numHF := r.FlyoverAndHFCount() - - return &sdpb.Reservation{ - Raw: raw, - Ratio: float64(numF) / float64(numHF), - Flyovers: convertFlyoversToPB(flyovers), - }, nil -} - -func convertFlyoverToPB(f *hummingbird.Flyover) *sdpb.Flyover { - if f == nil { - return nil - } - ret := &sdpb.Flyover{ - Ia: uint64(f.IA), - Ingress: uint32(f.Ingress), - Egress: uint32(f.Egress), - Bw: uint32(f.Bw), - ResId: f.ResID, - StartTime: f.StartTime, - Duration: uint32(f.Duration), - Ak: append([]byte{}, f.Ak[:]...), - } - - return ret -} -func convertFlyoversToPB(flyovers []*hummingbird.Flyover) []*sdpb.Flyover { - ret := make([]*sdpb.Flyover, len(flyovers)) - for i, f := range flyovers { - ret[i] = convertFlyoverToPB(f) - } - return ret -} - -func convertFlyoverFromPB(f *sdpb.Flyover) *hummingbird.Flyover { - if f == nil { - return nil - } - ret := &hummingbird.Flyover{ - BaseHop: hummingbird.BaseHop{ - IA: addr.IA(f.Ia), - Ingress: uint16(f.Ingress), - Egress: uint16(f.Egress), - }, - Bw: uint16(f.Bw), - ResID: f.ResId, - StartTime: f.StartTime, - Duration: uint16(f.Duration), - } - copy(ret.Ak[:], f.Ak) - return ret -} -func convertFlyoversFromPB(flyovers []*sdpb.Flyover) []*hummingbird.Flyover { - ret := make([]*hummingbird.Flyover, len(flyovers)) - for i, f := range flyovers { - ret[i] = convertFlyoverFromPB(f) - } - return ret -} diff --git a/daemon/internal/servers/grpc_hummingbird_test.go b/daemon/internal/servers/grpc_hummingbird_test.go index 082f91f8b1..22ca241d00 100644 --- a/daemon/internal/servers/grpc_hummingbird_test.go +++ b/daemon/internal/servers/grpc_hummingbird_test.go @@ -188,7 +188,7 @@ func TestStoreFlyovers(t *testing.T) { }, } req := &sdpb.StoreFlyoversRequest{ - Flyovers: convertFlyoversToPB(expected), + Flyovers: hummingbird.ConvertFlyoversToPB(expected), } _, err := s.StoreFlyovers(ctx, req) require.NoError(t, err) @@ -241,7 +241,7 @@ func TestListFlyovers(t *testing.T) { require.NoError(t, err) // Check response. - require.EqualValues(t, expected, convertFlyoversFromPB(res.Flyovers)) + require.EqualValues(t, expected, hummingbird.ConvertFlyoversFromPB(res.Flyovers)) } func getMockScionPaths(t require.TestingT, paths [][]any) []path.Path { diff --git a/pkg/hummingbird/BUILD.bazel b/pkg/hummingbird/BUILD.bazel index ef3f3b4099..d17517832c 100644 --- a/pkg/hummingbird/BUILD.bazel +++ b/pkg/hummingbird/BUILD.bazel @@ -5,12 +5,14 @@ go_library( srcs = [ "hummingbird.go", "reservation.go", + "translate.go", ], importpath = "github.com/scionproto/scion/pkg/hummingbird", visibility = ["//visibility:public"], deps = [ "//pkg/addr:go_default_library", "//pkg/private/serrors:go_default_library", + "//pkg/proto/daemon:go_default_library", "//pkg/slayers/path/hummingbird:go_default_library", "//pkg/slayers/path/scion:go_default_library", "//pkg/snet:go_default_library", diff --git a/pkg/hummingbird/translate.go b/pkg/hummingbird/translate.go new file mode 100644 index 0000000000..0a8d49b496 --- /dev/null +++ b/pkg/hummingbird/translate.go @@ -0,0 +1,91 @@ +// Copyright 2023 ETH Zurich +// +// 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 hummingbird + +import ( + "github.com/scionproto/scion/pkg/addr" + sdpb "github.com/scionproto/scion/pkg/proto/daemon" +) + +func ConvertFlyoverToPB(f *Flyover) *sdpb.Flyover { + if f == nil { + return nil + } + ret := &sdpb.Flyover{ + Ia: uint64(f.IA), + Ingress: uint32(f.Ingress), + Egress: uint32(f.Egress), + Bw: uint32(f.Bw), + ResId: f.ResID, + StartTime: f.StartTime, + Duration: uint32(f.Duration), + Ak: append([]byte{}, f.Ak[:]...), + } + + return ret +} + +func ConvertFlyoversToPB(flyovers []*Flyover) []*sdpb.Flyover { + ret := make([]*sdpb.Flyover, len(flyovers)) + for i, f := range flyovers { + ret[i] = ConvertFlyoverToPB(f) + } + return ret +} + +func ConvertFlyoverFromPB(f *sdpb.Flyover) *Flyover { + if f == nil { + return nil + } + ret := &Flyover{ + BaseHop: BaseHop{ + IA: addr.IA(f.Ia), + Ingress: uint16(f.Ingress), + Egress: uint16(f.Egress), + }, + Bw: uint16(f.Bw), + ResID: f.ResId, + StartTime: f.StartTime, + Duration: uint16(f.Duration), + } + copy(ret.Ak[:], f.Ak) + return ret +} +func ConvertFlyoversFromPB(flyovers []*sdpb.Flyover) []*Flyover { + ret := make([]*Flyover, len(flyovers)) + for i, f := range flyovers { + ret[i] = ConvertFlyoverFromPB(f) + } + return ret +} + +func ConvertReservationToPB(r *Reservation) (*sdpb.Reservation, error) { + // Prepare the hummingbird path. + p := r.GetHummingbirdPath() + raw := make([]byte, p.Len()) + if err := p.SerializeTo(raw); err != nil { + return nil, err + } + + // Prepare the flyovers. + flyovers := r.FlyoverPerHopField() + numF, numHF := r.FlyoverAndHFCount() + + return &sdpb.Reservation{ + Raw: raw, + Ratio: float64(numF) / float64(numHF), + Flyovers: ConvertFlyoversToPB(flyovers), + }, nil +} From 35a8979f01834422456fc942d857dc30179e7032 Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Thu, 21 Dec 2023 17:50:39 +0100 Subject: [PATCH 091/100] Hook daemon client with server via gRPC. --- pkg/daemon/daemon.go | 8 ++++++- pkg/daemon/grpc_hummingbird.go | 44 +++++++++++++++++++++++++++------- pkg/hummingbird/translate.go | 26 ++++++++++++++++++++ 3 files changed, 69 insertions(+), 9 deletions(-) diff --git a/pkg/daemon/daemon.go b/pkg/daemon/daemon.go index ab7833fff4..7bcfc87fb1 100644 --- a/pkg/daemon/daemon.go +++ b/pkg/daemon/daemon.go @@ -23,6 +23,7 @@ import ( "github.com/scionproto/scion/pkg/addr" "github.com/scionproto/scion/pkg/daemon/internal/metrics" "github.com/scionproto/scion/pkg/drkey" + "github.com/scionproto/scion/pkg/hummingbird" libmetrics "github.com/scionproto/scion/pkg/metrics" "github.com/scionproto/scion/pkg/private/common" "github.com/scionproto/scion/pkg/private/ctrl/path_mgmt" @@ -87,7 +88,12 @@ type Connector interface { DRKeyGetHostASKey(ctx context.Context, meta drkey.HostASMeta) (drkey.HostASKey, error) // DRKeyGetHostHostKey requests a Host-Host Key from the daemon. DRKeyGetHostHostKey(ctx context.Context, meta drkey.HostHostMeta) (drkey.HostHostKey, error) - GetReservations(ctx context.Context, src, dst addr.IA) ([]snet.Path, error) + + StoreFlyovers(ctx context.Context, flyovers []*hummingbird.Flyover) error + ListFlyovers(ctx context.Context) ([]*hummingbird.Flyover, error) + GetReservations(ctx context.Context, src, dst addr.IA, minBW uint16, refresh bool, + ) ([]*hummingbird.Reservation, error) + // Close shuts down the connection to the daemon. Close() error } diff --git a/pkg/daemon/grpc_hummingbird.go b/pkg/daemon/grpc_hummingbird.go index a62ec51799..3246fd541c 100644 --- a/pkg/daemon/grpc_hummingbird.go +++ b/pkg/daemon/grpc_hummingbird.go @@ -19,27 +19,55 @@ import ( "github.com/scionproto/scion/pkg/addr" "github.com/scionproto/scion/pkg/hummingbird" - "github.com/scionproto/scion/pkg/snet" + sdpb "github.com/scionproto/scion/pkg/proto/daemon" ) func (c grpcConn) StoreFlyovers( ctx context.Context, - flyovers []*hummingbird.BaseHop, + flyovers []*hummingbird.Flyover, ) error { - return nil + client := sdpb.NewDaemonServiceClient(c.conn) + _, err := client.StoreFlyovers(ctx, &sdpb.StoreFlyoversRequest{ + Flyovers: hummingbird.ConvertFlyoversToPB(flyovers), + }) + + return err } func (c grpcConn) ListFlyovers(ctx context.Context, -) ([]*hummingbird.BaseHop, error) { +) ([]*hummingbird.Flyover, error) { - return nil, nil + client := sdpb.NewDaemonServiceClient(c.conn) + res, err := client.ListFlyovers(ctx, &sdpb.ListFlyoversRequest{}) + if err != nil { + return nil, err + } + return hummingbird.ConvertFlyoversFromPB(res.Flyovers), nil } func (c grpcConn) GetReservations( ctx context.Context, - src, dst addr.IA, -) ([]snet.Path, error) { + src addr.IA, + dst addr.IA, + minBW uint16, + refresh bool, +) ([]*hummingbird.Reservation, error) { + + client := sdpb.NewDaemonServiceClient(c.conn) + res, err := client.GetReservations(ctx, &sdpb.GetReservationsRequest{ + SourceIsdAs: uint64(src), + DestinationIsdAs: uint64(dst), + MinBandwidth: uint32(minBW), + Refresh: refresh, + }) + if err != nil { + return nil, err + } - return nil, nil + // Return those reservations. + if res == nil { + return nil, nil + } + return hummingbird.ConvertReservationsFromPB(res.Reservations) } diff --git a/pkg/hummingbird/translate.go b/pkg/hummingbird/translate.go index 0a8d49b496..af494290dd 100644 --- a/pkg/hummingbird/translate.go +++ b/pkg/hummingbird/translate.go @@ -17,6 +17,7 @@ package hummingbird import ( "github.com/scionproto/scion/pkg/addr" sdpb "github.com/scionproto/scion/pkg/proto/daemon" + "github.com/scionproto/scion/pkg/slayers/path/hummingbird" ) func ConvertFlyoverToPB(f *Flyover) *sdpb.Flyover { @@ -89,3 +90,28 @@ func ConvertReservationToPB(r *Reservation) (*sdpb.Reservation, error) { Flyovers: ConvertFlyoversToPB(flyovers), }, nil } + +func ConvertReservationFromPB(pb *sdpb.Reservation) (*Reservation, error) { + // Decode path. + decoded := &hummingbird.Decoded{} + err := decoded.DecodeFromBytes(pb.Raw) + if err != nil { + return nil, err + } + + // Create reservation. + flyovers := ConvertFlyoversFromPB(pb.Flyovers) + return NewReservation(WithExistingHbirdPath(decoded, flyovers)) +} + +func ConvertReservationsFromPB(pb []*sdpb.Reservation) ([]*Reservation, error) { + ret := make([]*Reservation, len(pb)) + var err error + for i, r := range pb { + ret[i], err = ConvertReservationFromPB(r) + if err != nil { + return nil, err + } + } + return ret, nil +} From f49db766364fc0f6b7de383314a88f4b0f0cd333 Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Wed, 10 Jan 2024 16:13:05 +0100 Subject: [PATCH 092/100] Fix: no flyovers should work. --- pkg/hummingbird/hummingbird.go | 11 ----------- pkg/hummingbird/reservation.go | 22 +++++++++++++--------- pkg/hummingbird/reservation_test.go | 25 ++++++++++++++++++++++++- 3 files changed, 37 insertions(+), 21 deletions(-) diff --git a/pkg/hummingbird/hummingbird.go b/pkg/hummingbird/hummingbird.go index 87864bb844..a461a35cd5 100644 --- a/pkg/hummingbird/hummingbird.go +++ b/pkg/hummingbird/hummingbird.go @@ -78,17 +78,6 @@ func cheat_auth_key(res *Flyover) (Flyover, error) { return *res, nil } -// Requests a reservation for each given reservation. -// Expects AS, Bw, StartTime, EndTime, Ingress and Egress to be filled in -func RequestReservations(rs []Flyover) { - -} - -// Adds a reservation to be used for transmission -func AddReservation(res Flyover) error { - return nil -} - // Converts a SCiON path to a Hummingbird path without adding any reservations // Relaces the SCiON dataplane path by a Hummingbird path func ConvertToHbirdPath(p snet.Path, timeStamp time.Time) (snet.Path, error) { diff --git a/pkg/hummingbird/reservation.go b/pkg/hummingbird/reservation.go index ec6f31c55c..dd56a4e716 100644 --- a/pkg/hummingbird/reservation.go +++ b/pkg/hummingbird/reservation.go @@ -85,6 +85,7 @@ func WithScionPath(p snet.Path, flyovers FlyoverMap) reservationModFcn { // include the egress-to-ingress crossed over interfaces in the core AS. interfaces := p.Metadata().Interfaces + // Add the first hop of the first segment now. r.dec.PathMeta.SegLen[0] += 2 r.newHopSelectFlyover( interfaces[0].IA, @@ -95,20 +96,23 @@ func WithScionPath(p snet.Path, flyovers FlyoverMap) reservationModFcn { ) // The dataplane path in c.dec contains inf fields and cross-over hops. - // Do each segment at a time to ignore the first hop of every segment except the first. - hopIdx := 1 // the index of the current hop in the dataplane. - for infIdx := 0; infIdx < r.dec.NumINF; infIdx, hopIdx = infIdx+1, hopIdx+1 { - // Preserve the hopcount locally, as we modify it inside the loop itself. + // Do each segment at a time to ignore the first hop of every segment + // (the first segment's first hop was already added above). + dpHopIdx := 1 // the index of the current hop in the dataplane. + interfaceCount := 1 // the index of the interfaces (no cross overs). + for infIdx := 0; infIdx < r.dec.NumINF; infIdx, dpHopIdx = infIdx+1, dpHopIdx+1 { + // Preserve the hop count locally, as we modify the path inside the loop itself. hopCount := int(r.dec.Base.PathMeta.SegLen[infIdx]) / hummingbird.HopLines - for i := 1; i < hopCount; i, hopIdx = i+1, hopIdx+1 { + for i := 1; i < hopCount; i, dpHopIdx = i+1, dpHopIdx+1 { r.dec.PathMeta.SegLen[infIdx] += 2 r.newHopSelectFlyover( - interfaces[len(r.hops)*2-1].IA, - uint16(interfaces[len(r.hops)*2-1].ID), - egressID(interfaces, len(r.hops)), - &r.dec.HopFields[hopIdx], + interfaces[interfaceCount*2-1].IA, + uint16(interfaces[interfaceCount*2-1].ID), + egressID(interfaces, interfaceCount), + &r.dec.HopFields[dpHopIdx], flyovers, ) + interfaceCount++ } } diff --git a/pkg/hummingbird/reservation_test.go b/pkg/hummingbird/reservation_test.go index 5ce5339933..e58aa95d3e 100644 --- a/pkg/hummingbird/reservation_test.go +++ b/pkg/hummingbird/reservation_test.go @@ -31,6 +31,9 @@ func TestReservationWithScionPath(t *testing.T) { hummingbird.WithScionPath(scionPath, flyoverSliceToMap(testFlyoversInDB)), ) require.NoError(t, err) + + // Check that the derived path in the reservation is the same as hbirdPath (which is the + // expected one). hbirdPath, err := getHbirdFlyoversSnetPath(t, fixedTime) assert.NoError(t, err) decoded := r.DeriveDataPlanePath(16, fixedTime) @@ -41,7 +44,6 @@ func TestReservationWithScionPath(t *testing.T) { assert.NoError(t, err) scionPath.DataplanePath = raw - assert.NoError(t, err) assert.Equal(t, hbirdPath, scionPath) // Check that the number of flyovers, nil or otherwise, is the same as hop fields. @@ -49,6 +51,27 @@ func TestReservationWithScionPath(t *testing.T) { assert.Equal(t, len(decodedScionTestPath.HopFields), len(flyovers)) } +func TestReservationWithScionPathNoFlyovers(t *testing.T) { + scionPath := getScionSnetPath(t) + + r, err := hummingbird.NewReservation( + hummingbird.WithNow(fixedTime), + hummingbird.WithScionPath(scionPath, nil), // nil == "no flyovers" + ) + require.NoError(t, err) + + // Should not have any flyovers, but the same amount of hop fields in the dataplane. + flyovers, hfs := r.FlyoverAndHFCount() + assert.Equal(t, 0, flyovers) + assert.Equal(t, decodedScionTestPath.NumHops, hfs) + + // All hop fields must not be flyovers. + decoded := r.DeriveDataPlanePath(16, fixedTime) + for i, hf := range decoded.HopFields { + assert.False(t, hf.Flyover, "failed at index %d", i) + } +} + func TestReservationWithHbirdPath(t *testing.T) { // Build a Reservation from an existing decoded hummingbird path and its associated // flyover sequence. From c4b48386d8604c015abab96a414bf4c6c0c3306f Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Fri, 12 Jan 2024 11:01:48 +0100 Subject: [PATCH 093/100] Fix bug deriving reservation without flyovers. --- pkg/hummingbird/BUILD.bazel | 1 - pkg/hummingbird/reservation.go | 32 +++-- pkg/hummingbird/reservation_test.go | 68 ++++++++++- pkg/hummingbird/utils_test.go | 2 +- pkg/slayers/path/hummingbird/BUILD.bazel | 1 + pkg/slayers/path/hummingbird/base.go | 17 +-- pkg/slayers/path/hummingbird/decoded.go | 15 ++- pkg/slayers/path/hummingbird/decoded_test.go | 114 ++++++++++++++++++ .../path/hummingbird/flyoverhopfield.go | 15 ++- 9 files changed, 229 insertions(+), 36 deletions(-) diff --git a/pkg/hummingbird/BUILD.bazel b/pkg/hummingbird/BUILD.bazel index d17517832c..e411154116 100644 --- a/pkg/hummingbird/BUILD.bazel +++ b/pkg/hummingbird/BUILD.bazel @@ -37,6 +37,5 @@ go_test( "//pkg/snet:go_default_library", "//pkg/snet/path:go_default_library", "@com_github_stretchr_testify//assert:go_default_library", - "@com_github_stretchr_testify//require:go_default_library", ], ) diff --git a/pkg/hummingbird/reservation.go b/pkg/hummingbird/reservation.go index dd56a4e716..0cf2477a4e 100644 --- a/pkg/hummingbird/reservation.go +++ b/pkg/hummingbird/reservation.go @@ -81,35 +81,34 @@ func WithScionPath(p snet.Path, flyovers FlyoverMap) reservationModFcn { default: return serrors.New("Unsupported path type") } + // We use the path metadata to get the IA from it. This sequence of interfaces does not // include the egress-to-ingress crossed over interfaces in the core AS. interfaces := p.Metadata().Interfaces // Add the first hop of the first segment now. - r.dec.PathMeta.SegLen[0] += 2 r.newHopSelectFlyover( interfaces[0].IA, 0, uint16(interfaces[0].ID), - &r.dec.HopFields[0], + 0, flyovers, ) // The dataplane path in c.dec contains inf fields and cross-over hops. // Do each segment at a time to ignore the first hop of every segment // (the first segment's first hop was already added above). - dpHopIdx := 1 // the index of the current hop in the dataplane. - interfaceCount := 1 // the index of the interfaces (no cross overs). + dpHopIdx := uint8(1) // the index of the current hop in the dataplane. + interfaceCount := 1 // the index of the interfaces (no cross overs). for infIdx := 0; infIdx < r.dec.NumINF; infIdx, dpHopIdx = infIdx+1, dpHopIdx+1 { // Preserve the hop count locally, as we modify the path inside the loop itself. hopCount := int(r.dec.Base.PathMeta.SegLen[infIdx]) / hummingbird.HopLines for i := 1; i < hopCount; i, dpHopIdx = i+1, dpHopIdx+1 { - r.dec.PathMeta.SegLen[infIdx] += 2 r.newHopSelectFlyover( interfaces[interfaceCount*2-1].IA, uint16(interfaces[interfaceCount*2-1].ID), egressID(interfaces, interfaceCount), - &r.dec.HopFields[dpHopIdx], + dpHopIdx, flyovers, ) interfaceCount++ @@ -131,8 +130,7 @@ func WithExistingHbirdPath(p *hummingbird.Decoded, flyovers []*Flyover) reservat if flyover == nil { continue } - r.newHop(flyover.IA, flyover.Ingress, flyover.Egress, - &r.dec.HopFields[i], flyover) + r.newHop(flyover.IA, flyover.Ingress, flyover.Egress, uint8(i), flyover) } // For each hop field, clean up the ResStartTime as it's set when deriving the dataplane // path. @@ -231,7 +229,7 @@ func (r *Reservation) DeriveDataPlanePath( } func (r *Reservation) newHopSelectFlyover(ia addr.IA, in, eg uint16, - hf *hummingbird.FlyoverHopField, flyoverSet FlyoverMap) { + hfIdx uint8, flyoverSet FlyoverMap) { // Look for a valid flyover. now := uint32(r.now.Unix()) @@ -245,17 +243,25 @@ func (r *Reservation) newHopSelectFlyover(ia addr.IA, in, eg uint16, if flyover.StartTime <= now && uint32(flyover.Duration) >= now-flyover.StartTime && flyover.Bw >= r.minBW { - r.dec.NumLines += 2 - r.newHop(ia, in, eg, hf, flyover) + r.newHop(ia, in, eg, hfIdx, flyover) break } } } func (r *Reservation) newHop(ia addr.IA, in, eg uint16, - hf *hummingbird.FlyoverHopField, flyover *Flyover) { + hfIdx uint8, flyover *Flyover) { + + // Find the hop field from its index. + hf := &r.dec.HopFields[hfIdx] + + if !hf.Flyover { + // Because we are setting a plain hop field as a flyover, it will use two more lines. + r.dec.NumLines += 2 + r.dec.PathMeta.SegLen[r.dec.InfIndexForHFIndex(hfIdx)] += 2 + hf.Flyover = true + } - hf.Flyover = true hf.Bw = flyover.Bw hf.Duration = flyover.Duration hf.ResID = flyover.ResID diff --git a/pkg/hummingbird/reservation_test.go b/pkg/hummingbird/reservation_test.go index e58aa95d3e..dc1cf32651 100644 --- a/pkg/hummingbird/reservation_test.go +++ b/pkg/hummingbird/reservation_test.go @@ -17,10 +17,11 @@ package hummingbird_test import ( "testing" + "github.com/stretchr/testify/assert" + "github.com/scionproto/scion/pkg/hummingbird" + hbirddp "github.com/scionproto/scion/pkg/slayers/path/hummingbird" "github.com/scionproto/scion/pkg/snet/path" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestReservationWithScionPath(t *testing.T) { @@ -30,7 +31,7 @@ func TestReservationWithScionPath(t *testing.T) { hummingbird.WithNow(fixedTime), hummingbird.WithScionPath(scionPath, flyoverSliceToMap(testFlyoversInDB)), ) - require.NoError(t, err) + assert.NoError(t, err) // Check that the derived path in the reservation is the same as hbirdPath (which is the // expected one). @@ -58,7 +59,7 @@ func TestReservationWithScionPathNoFlyovers(t *testing.T) { hummingbird.WithNow(fixedTime), hummingbird.WithScionPath(scionPath, nil), // nil == "no flyovers" ) - require.NoError(t, err) + assert.NoError(t, err) // Should not have any flyovers, but the same amount of hop fields in the dataplane. flyovers, hfs := r.FlyoverAndHFCount() @@ -90,8 +91,63 @@ func TestReservationWithHbirdPath(t *testing.T) { flyoverSliceToMap(testFlyoversInDB), ), ) - require.NoError(t, err) - require.Equal(t, expected, r) + assert.NoError(t, err) + assert.Equal(t, expected, r) +} + +func TestDeriveDataPlanePath(t *testing.T) { + // New reservation with a scion path. + scionPath := getScionSnetPath(t) + r, err := hummingbird.NewReservation( + hummingbird.WithNow(fixedTime), + hummingbird.WithScionPath(scionPath, flyoverSliceToMap(testFlyoversInDB)), + ) + assert.NoError(t, err) + + // Run twice. + for i := 0; i < 2; i++ { + // Derive dataplane path. + decoded := r.DeriveDataPlanePath(16, fixedTime) + + // Check that it is a valid path. + buf := make([]byte, decoded.Len()) + err = decoded.SerializeTo(buf) + assert.NoError(t, err) + // Deserialize to hummingbird Decoded. + decoded = &hbirddp.Decoded{} + err = decoded.DecodeFromBytes(buf) + assert.NoError(t, err) + // Deserialize to hummingbird Raw. + hbirdRaw := hbirddp.Raw{} + err = hbirdRaw.DecodeFromBytes(buf) + assert.NoError(t, err) + } +} + +func TestDeriveDataPlanePathNoFlyovers(t *testing.T) { + // New reservation with a scion path. + scionPath := getScionSnetPath(t) + r, err := hummingbird.NewReservation( + hummingbird.WithNow(fixedTime), + hummingbird.WithScionPath(scionPath, nil), + ) + assert.NoError(t, err) + + // Derive dataplane path. + decoded := r.DeriveDataPlanePath(16, fixedTime) + + // Check that it is a valid path. + buf := make([]byte, decoded.Len()) + err = decoded.SerializeTo(buf) + assert.NoError(t, err) + // Deserialize to hummingbird Decoded. + decoded = &hbirddp.Decoded{} + err = decoded.DecodeFromBytes(buf) + assert.NoError(t, err) + // Deserialize to hummingbird Raw. + hbirdRaw := hbirddp.Raw{} + err = hbirdRaw.DecodeFromBytes(buf) + assert.NoError(t, err) } func flyoverSliceToMap(flyovers []hummingbird.Flyover) hummingbird.FlyoverMap { diff --git a/pkg/hummingbird/utils_test.go b/pkg/hummingbird/utils_test.go index 49d3be0d44..3fd2e5fddc 100644 --- a/pkg/hummingbird/utils_test.go +++ b/pkg/hummingbird/utils_test.go @@ -174,7 +174,7 @@ var decodedHbirdTestPathFlyovers = &hummingbird.Decoded{ PathMeta: hummingbird.MetaHdr{ CurrINF: 0, CurrHF: 0, - SegLen: [3]uint8{10, 8, 0}, + SegLen: [3]uint8{10, 8, 0}, // [5+5, 5+3, 0] }, NumINF: 2, NumLines: 18, diff --git a/pkg/slayers/path/hummingbird/BUILD.bazel b/pkg/slayers/path/hummingbird/BUILD.bazel index 2250cf3e48..416de18608 100644 --- a/pkg/slayers/path/hummingbird/BUILD.bazel +++ b/pkg/slayers/path/hummingbird/BUILD.bazel @@ -48,6 +48,7 @@ go_test( ":go_default_library", "//pkg/addr:go_default_library", "//pkg/slayers/path:go_default_library", + "//pkg/slayers/path/scion:go_default_library", "@com_github_stretchr_testify//assert:go_default_library", "@com_github_stretchr_testify//require:go_default_library", ], diff --git a/pkg/slayers/path/hummingbird/base.go b/pkg/slayers/path/hummingbird/base.go index d1f80d5447..379023a9e2 100644 --- a/pkg/slayers/path/hummingbird/base.go +++ b/pkg/slayers/path/hummingbird/base.go @@ -27,7 +27,7 @@ type Base struct { PathMeta MetaHdr // NumINF is the number of InfoFields in the path. NumINF int - // NumLines is the number of 4 bytes lines in the path. + // NumLines is the number of 4 bytes lines in the path. NumLines = SegLen[i] for 0<=i<=2. NumLines int } @@ -60,7 +60,7 @@ func (s *Base) IncPath(n int) error { return serrors.New("empty path cannot be increased") } if int(s.PathMeta.CurrHF) >= s.NumLines-n { - s.PathMeta.CurrHF = uint8(s.NumLines - n) + // s.PathMeta.CurrHF = uint8(s.NumLines - n) return serrors.New("Incrementing path over end") } s.PathMeta.CurrHF += uint8(n) @@ -83,11 +83,12 @@ func (s *Base) IsFirstHopAfterXover() bool { } // InfIndexForHF returns the segment to which the HopField hf belongs -func (s *Base) InfIndexForHF(hf uint8) uint8 { +// The argument hfLines is the line count until the first line of this hop field. +func (s *Base) InfIndexForHF(hfLines uint8) uint8 { switch { - case hf < s.PathMeta.SegLen[0]: + case hfLines < s.PathMeta.SegLen[0]: return 0 - case hf < s.PathMeta.SegLen[0]+s.PathMeta.SegLen[1]: + case hfLines < s.PathMeta.SegLen[0]+s.PathMeta.SegLen[1]: return 1 default: return 2 @@ -106,9 +107,9 @@ func (s *Base) Type() path.Type { // MetaHdr is the PathMetaHdr of a Hummingbird (data-plane) path type. type MetaHdr struct { - CurrINF uint8 - CurrHF uint8 - SegLen [3]uint8 + CurrINF uint8 // Index of the current info field. + CurrHF uint8 // Index of the current hop field. + SegLen [3]uint8 // Length in bytes / 4 of each segment. BaseTS uint32 HighResTS uint32 } diff --git a/pkg/slayers/path/hummingbird/decoded.go b/pkg/slayers/path/hummingbird/decoded.go index b235a2b31e..4005de8b05 100644 --- a/pkg/slayers/path/hummingbird/decoded.go +++ b/pkg/slayers/path/hummingbird/decoded.go @@ -116,7 +116,6 @@ func (s *Decoded) SerializeTo(b []byte) error { } offset += HopLen } - } return nil } @@ -199,6 +198,20 @@ func (s *Decoded) ToRaw() (*Raw, error) { return raw, nil } +// InfIndexForHFIndex takes the index of the hop field in the HopFields slice and returns its +// corresponding info field index in the InfoFields slice. Expected 0 <= hfIdx < len(HopFields). +func (s *Decoded) InfIndexForHFIndex(hfIdx uint8) uint8 { + lineCount := uint8(0) + for i := uint8(0); i < hfIdx; i++ { + if s.HopFields[i].Flyover { + lineCount += FlyoverLines + } else { + lineCount += HopLines + } + } + return s.InfIndexForHF(lineCount) +} + // Converts a SCiON decoded path to a hummingbird decoded path // Does NOT perform a deep copy of hop and info fields. // Does NOT set the PathMeta Timestamps and counter diff --git a/pkg/slayers/path/hummingbird/decoded_test.go b/pkg/slayers/path/hummingbird/decoded_test.go index 8ec3857575..7463636e94 100644 --- a/pkg/slayers/path/hummingbird/decoded_test.go +++ b/pkg/slayers/path/hummingbird/decoded_test.go @@ -8,6 +8,7 @@ import ( "github.com/scionproto/scion/pkg/slayers/path" "github.com/scionproto/scion/pkg/slayers/path/hummingbird" + "github.com/scionproto/scion/pkg/slayers/path/scion" ) var testInfoFields = []path.InfoField{ @@ -235,6 +236,44 @@ func TestDecodedDecodeFromBytesHbird(t *testing.T) { assert.Equal(t, decodedHbirdTestPath, s) } +func TestDecodedDecodeFromBytesNoFlyovers(t *testing.T) { + // p is the scion decoded path we would observe using the Tiny topology of the + // topology generator, when going from 111 to 112. This is one up segment with 2 hops, followed + // by a down segment with two hops as well. There is a cross over at core 110 gluing both. + p := &scion.Decoded{ + Base: scion.Base{ + PathMeta: scion.MetaHdr{ + SegLen: [3]uint8{2, 2, 0}, + }, + NumINF: 2, + NumHops: 4, + }, + InfoFields: []path.InfoField{ + {}, // up + {}, // down + }, + HopFields: []path.HopField{ + {}, // 111: 0->41 up + {}, // 110: 1->0 up + {}, // 110: 0->2 down + {}, // 112: 1->0 down + }, + } + + // Create a hummingbird path from the scion one. + hbird := &hummingbird.Decoded{} + hbird.ConvertFromScionDecoded(*p) // SegLen will be [6,6,0] after this + + // Check the hummingbird path is correct by serializing and deserializing it. + buf := make([]byte, hbird.Len()) + err := hbird.SerializeTo(buf) + assert.NoError(t, err) + // Deserialize. + hbird = &hummingbird.Decoded{} + err = hbird.DecodeFromBytes(buf) + assert.NoError(t, err) +} + func TestDecodedSerializeDecodeHbird(t *testing.T) { b := make([]byte, decodedHbirdTestPath.Len()) assert.NoError(t, decodedHbirdTestPath.SerializeTo(b)) @@ -293,6 +332,81 @@ func TestDecodedToRawHbird(t *testing.T) { assert.Equal(t, rawHbirdTestPath, raw) } +func TestInfIndexForHFIndex(t *testing.T) { + cases := map[string]struct { + path hummingbird.Decoded + expected []uint8 // the INF indices of each hop field in the test case + }{ + "empty": { + path: hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + SegLen: [3]uint8{0, 0, 0}, + }, + }, + }, + }, + "one_segment_o": { + path: hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + SegLen: [3]uint8{3, 0, 0}, + }, + }, + HopFields: []hummingbird.FlyoverHopField{ + {Flyover: false}, + }, + }, + expected: []uint8{0}, + }, + // one_segment_oxx means there is one segment with three hops, first is not flyover, + // second and third are. + "one_segment_oxx": { + path: hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + SegLen: [3]uint8{13, 0, 0}, + }, + }, + HopFields: []hummingbird.FlyoverHopField{ + {Flyover: false}, + {Flyover: true}, + {Flyover: true}, + }, + }, + expected: []uint8{0, 0, 0}, + }, + "two_segments_o_oxx": { + path: hummingbird.Decoded{ + Base: hummingbird.Base{ + PathMeta: hummingbird.MetaHdr{ + SegLen: [3]uint8{3, 13, 0}, + }, + }, + HopFields: []hummingbird.FlyoverHopField{ + {Flyover: false}, + {Flyover: false}, + {Flyover: true}, + {Flyover: true}, + }, + }, + expected: []uint8{0, 1, 1, 1}, + }, + } + for name, tc := range cases { + name, tc := name, tc + t.Run(name, func(t *testing.T) { + for i := range tc.path.HopFields { + got := tc.path.InfIndexForHFIndex(uint8(i)) + assert.Equal(t, tc.expected[i], got) + } + assert.Panics(t, func() { + tc.path.InfIndexForHFIndex(uint8(len(tc.path.HopFields)) + 1) + }) + }) + } +} + func mkDecodedHbirdPath(t *testing.T, pcase hbirdPathCase, infIdx, hopIdx uint8) *hummingbird.Decoded { t.Helper() diff --git a/pkg/slayers/path/hummingbird/flyoverhopfield.go b/pkg/slayers/path/hummingbird/flyoverhopfield.go index f693d3f4b6..160e6d08f1 100644 --- a/pkg/slayers/path/hummingbird/flyoverhopfield.go +++ b/pkg/slayers/path/hummingbird/flyoverhopfield.go @@ -8,18 +8,21 @@ import ( ) const ( - // LineLen is the number of bytes in a line as considered by CurrHF in the PathMEtaHeader - LineLen = 4 - // Length in bytes of a FlyoverHopField - FlyoverLen = 20 - // HopLen is the size of a HopField in bytes. - HopLen = 12 // MacOffset is the offset of the MAC field from the beginning of the HopField MacOffset = 6 + + // LineLen is the number of bytes in a line as considered by CurrHF in the PathMEtaHeader + LineLen = 4 + // The number of lines in a hopfield HopLines = 3 // The number of lines in a flyoverhopfield FlyoverLines = 5 + + // HopLen is the size of a HopField in bytes. + HopLen = LineLen * HopLines + // HopLen is the size of a FlyoverHopField in bytes. + FlyoverLen = LineLen * FlyoverLines ) type FlyoverHopField struct { From 0423a20e03da2abdc16a3eb8fc6be66b81c4f063 Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Fri, 12 Jan 2024 11:02:52 +0100 Subject: [PATCH 094/100] Topology generator friendly with hummingbird. --- tools/topology/go.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/topology/go.py b/tools/topology/go.py index d8f06f6058..c6199ee09c 100644 --- a/tools/topology/go.py +++ b/tools/topology/go.py @@ -158,6 +158,10 @@ def _build_sciond_conf(self, topo_id, ia, base): 'path_db': { 'connection': os.path.join(self.db_dir, '%s.path.db' % name), }, + + 'hbird_db': { + 'connection': os.path.join(self.db_dir, '%s.hbird.db' % name), + }, 'sd': { 'address': socket_address_str(ip, SD_API_PORT), }, From f3ee7dc9d175493f2520321c1ac25af5b779c624 Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Fri, 12 Jan 2024 11:50:53 +0100 Subject: [PATCH 095/100] Cleanup cheat function. --- pkg/hummingbird/hummingbird.go | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/pkg/hummingbird/hummingbird.go b/pkg/hummingbird/hummingbird.go index a461a35cd5..132d6f5f1e 100644 --- a/pkg/hummingbird/hummingbird.go +++ b/pkg/hummingbird/hummingbird.go @@ -15,9 +15,6 @@ package hummingbird import ( - "crypto/aes" - "math/rand" - "strings" "time" "github.com/scionproto/scion/pkg/addr" @@ -26,8 +23,6 @@ import ( "github.com/scionproto/scion/pkg/slayers/path/scion" "github.com/scionproto/scion/pkg/snet" snetpath "github.com/scionproto/scion/pkg/snet/path" - "github.com/scionproto/scion/private/keyconf" - "github.com/scionproto/scion/router/control" ) // Describes a pair of Ingress and Egress interfaces in a specific AS @@ -55,29 +50,6 @@ type Flyover struct { Duration uint16 } -// Temporary cheating function until the system to request keys is available -// return true if successful -func cheat_auth_key(res *Flyover) (Flyover, error) { - // ResID is set by seller, pick random - res.ResID = uint32(rand.Int31() >> 10) - - asstr := res.IA.String() - asstr = strings.ReplaceAll(asstr, ":", "_") - asstr = strings.TrimLeft(asstr, "1234567890-") - fpath := "gen/AS" + asstr + "/keys" - mkeys, err := keyconf.LoadMaster(fpath) - if err != nil { - return *res, err - } - key0 := control.DeriveHbirdSecretValue(mkeys.Key0) - prf, _ := aes.NewCipher(key0) - buffer := make([]byte, 16) - ak := hummingbird.DeriveAuthKey(prf, res.ResID, res.Bw, res.Ingress, res.Egress, - res.StartTime, res.Duration, buffer) - copy(res.Ak[:], ak[0:16]) - return *res, nil -} - // Converts a SCiON path to a Hummingbird path without adding any reservations // Relaces the SCiON dataplane path by a Hummingbird path func ConvertToHbirdPath(p snet.Path, timeStamp time.Time) (snet.Path, error) { From a0e47487e7641daf5f8c35598fa051fb0b223323 Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Fri, 12 Jan 2024 11:53:40 +0100 Subject: [PATCH 096/100] New integration test for hummingbird. The new integration test uses sciond, the scion daemon. --- tools/end2end_hbird/BUILD.bazel | 3 + tools/end2end_hbird/main.go | 44 ++--- tools/end2end_hbird_integration/BUILD.bazel | 7 + tools/end2end_hbird_integration/main.go | 208 ++++++++++++++++++-- 4 files changed, 222 insertions(+), 40 deletions(-) diff --git a/tools/end2end_hbird/BUILD.bazel b/tools/end2end_hbird/BUILD.bazel index 6d3c3a2927..9a915d92f4 100644 --- a/tools/end2end_hbird/BUILD.bazel +++ b/tools/end2end_hbird/BUILD.bazel @@ -14,12 +14,15 @@ go_library( "//pkg/private/common:go_default_library", "//pkg/private/serrors:go_default_library", "//pkg/private/util:go_default_library", + "//pkg/slayers/path/hummingbird:go_default_library", "//pkg/snet:go_default_library", "//pkg/snet/metrics:go_default_library", "//pkg/snet/path:go_default_library", "//pkg/sock/reliable:go_default_library", + "//private/keyconf:go_default_library", "//private/topology:go_default_library", "//private/tracing:go_default_library", + "//router/control:go_default_library", "//tools/integration:go_default_library", "//tools/integration/integrationlib:go_default_library", "@com_github_opentracing_opentracing_go//:go_default_library", diff --git a/tools/end2end_hbird/main.go b/tools/end2end_hbird/main.go index 55a9c830df..7380ac7338 100644 --- a/tools/end2end_hbird/main.go +++ b/tools/end2end_hbird/main.go @@ -70,7 +70,6 @@ var ( timeout = &util.DurWrap{Duration: 10 * time.Second} scionPacketConnMetrics = metrics.NewSCIONPacketConnMetrics() scmpErrorsCounter = scionPacketConnMetrics.SCMPErrors - flyovers bool ) func main() { @@ -94,6 +93,7 @@ func realMain() int { return 1 } defer closeTracer() + if integration.Mode == integration.ModeServer { server{}.run() return 0 @@ -105,7 +105,6 @@ func realMain() int { func addFlags() { flag.Var(&remote, "remote", "(Mandatory for clients) address to connect to") flag.Var(timeout, "timeout", "The timeout for each attempt") - flag.BoolVar(&flyovers, "flyovers", true, "Enable Flyovers") } func validateFlags() { @@ -256,6 +255,7 @@ func (c *client) run() int { log.Info("Starting", "pair", pair) defer log.Info("Finished", "pair", pair) defer integration.Done(integration.Local.IA, remote.IA) + connFactory := &snet.DefaultPacketDispatcherService{ Dispatcher: reliable.NewDispatcher(""), SCMPHandler: snet.DefaultSCMPHandler{ @@ -305,32 +305,22 @@ func (c *client) attemptRequest(n int) bool { // TODO: using the reservations in the DB, create a hummingbird path - if !flyovers { - // Standard path, no reservations at all - path, err = hummingbird.ConvertToHbirdPath(path, time.Now()) - if err != nil { - logger.Error("Error converting path to Hummingbird", "err", err) - return false - } - remote.Path = path.Dataplane() - } else { - hbirdClient, err := hummingbird.NewReservation( - hummingbird.WithScionPath(path, nil)) - if err != nil { - logger.Error("Error converting path to Hummingbird", "err", err) - return false - } - - decoded := hbirdClient.DeriveDataPlanePath(16, time.Now()) - raw := snetpath.Hummingbird{ - Raw: make([]byte, decoded.Len()), - } - err = decoded.SerializeTo(raw.Raw) - if err != nil { - logger.Error("Error assembling hummingbird path", "err", err) - } - remote.Path = raw + reservation, err := hummingbird.NewReservation( + hummingbird.WithScionPath(path, nil)) + if err != nil { + logger.Error("Error converting path to Hummingbird", "err", err) + return false + } + + decoded := reservation.DeriveDataPlanePath(16, time.Now()) + raw := snetpath.Hummingbird{ + Raw: make([]byte, decoded.Len()), + } + err = decoded.SerializeTo(raw.Raw) + if err != nil { + logger.Error("Error assembling hummingbird path", "err", err) } + remote.Path = raw } // Send ping diff --git a/tools/end2end_hbird_integration/BUILD.bazel b/tools/end2end_hbird_integration/BUILD.bazel index 9686daa652..c8152a2e3b 100644 --- a/tools/end2end_hbird_integration/BUILD.bazel +++ b/tools/end2end_hbird_integration/BUILD.bazel @@ -8,11 +8,18 @@ go_library( visibility = ["//visibility:private"], deps = [ "//pkg/addr:go_default_library", + "//pkg/daemon:go_default_library", + "//pkg/hummingbird:go_default_library", "//pkg/log:go_default_library", + "//pkg/private/common:go_default_library", "//pkg/private/serrors:go_default_library", "//pkg/private/util:go_default_library", + "//pkg/slayers/path/hummingbird:go_default_library", "//pkg/snet:go_default_library", "//private/app/feature:go_default_library", + "//private/keyconf:go_default_library", + "//private/topology:go_default_library", + "//router/control:go_default_library", "//tools/integration:go_default_library", ], ) diff --git a/tools/end2end_hbird_integration/main.go b/tools/end2end_hbird_integration/main.go index 9de511fe20..ca800a19cf 100644 --- a/tools/end2end_hbird_integration/main.go +++ b/tools/end2end_hbird_integration/main.go @@ -16,6 +16,7 @@ package main import ( "context" + "crypto/aes" "flag" "fmt" "os" @@ -26,11 +27,18 @@ import ( "time" "github.com/scionproto/scion/pkg/addr" + "github.com/scionproto/scion/pkg/daemon" + "github.com/scionproto/scion/pkg/hummingbird" "github.com/scionproto/scion/pkg/log" + "github.com/scionproto/scion/pkg/private/common" "github.com/scionproto/scion/pkg/private/serrors" "github.com/scionproto/scion/pkg/private/util" + hbirddp "github.com/scionproto/scion/pkg/slayers/path/hummingbird" "github.com/scionproto/scion/pkg/snet" "github.com/scionproto/scion/private/app/feature" + "github.com/scionproto/scion/private/keyconf" + "github.com/scionproto/scion/private/topology" + "github.com/scionproto/scion/router/control" "github.com/scionproto/scion/tools/integration" ) @@ -42,9 +50,6 @@ var ( name string cmd string features string - epic bool - flyovers bool - partial bool ) func getCmd() (string, bool) { @@ -76,9 +81,6 @@ func realMain() int { "-timeout", timeout.String(), "-local", integration.SrcAddrPattern + ":0", "-remote", integration.DstAddrPattern + ":" + integration.ServerPortReplace, - fmt.Sprintf("-epic=%t", epic), - fmt.Sprintf("-flyovers=%t", flyovers), - fmt.Sprintf("-partial=%t", partial), } serverArgs := []string{ "-mode", "server", @@ -99,6 +101,11 @@ func realMain() int { log.Error("Error selecting tests", "err", err) return 1 } + err = addMockFlyovers(time.Now(), pairs) + if err != nil { + log.Error("Error adding mock flyovers", "err", err) + return 1 + } if err := runTests(in, pairs); err != nil { log.Error("Error during tests", "err", err) return 1 @@ -119,9 +126,6 @@ func addFlags() { flag.IntVar(¶llelism, "parallelism", 1, "How many end2end tests run in parallel.") flag.StringVar(&features, "features", "", fmt.Sprintf("enable development features (%v)", feature.String(&feature.Default{}, "|"))) - flag.BoolVar(&epic, "epic", false, "Enable EPIC.") - flag.BoolVar(&flyovers, "flyovers", true, "Enable Flyovers") - flag.BoolVar(&partial, "partial", false, "If true, only subset of hops have flyovers") } // runTests runs the end2end hbird tests for all pairs. In case of an error the @@ -193,7 +197,7 @@ func runTests(in integration.Integration, pairs []integration.IAPair) error { // and inside bazel tests we easily have longer paths, therefore we // create a temporary symlink to the directory where we put the socket // file. - tmpDir, err := os.MkdirTemp("", "e2e_integration") + tmpDir, err := os.MkdirTemp("", "e2e_hbird_integration") if err != nil { return serrors.WrapStr("creating temp dir", err) } @@ -285,9 +289,6 @@ func clientTemplate(progressSock string) integration.Cmd { "-timeout", timeout.String(), "-local", integration.SrcAddrPattern + ":0", "-remote", integration.DstAddrPattern + ":" + integration.ServerPortReplace, - fmt.Sprintf("-epic=%t", epic), - fmt.Sprintf("-flyovers=%t", flyovers), - fmt.Sprintf("-partial=%t", partial), }, } if len(features) != 0 { @@ -359,6 +360,187 @@ func contains(ases *integration.ASList, core bool, ia addr.IA) bool { return false } +// addMockFlyovers creates and stores the necessary flyovers for the given pairs. +// It uses the scion daemon to add the flyovers to the DB. +func addMockFlyovers(now time.Time, pairs []integration.IAPair) error { + perAS, err := getTopoPerAS(pairs) + if err != nil { + return nil + } + + flyovers, err := createMockFlyovers(perAS, now) + if err != nil { + return err + } + + // Insert each flyover into the DB of each AS. Allow timeout.Duration per AS to do so. + wg := sync.WaitGroup{} + wg.Add(len(perAS)) + errCh := make(chan error) + for ia, c := range perAS { + ia, c := ia, c + go func() { + defer log.HandlePanic() + defer wg.Done() + ctx, cancelF := context.WithTimeout(context.Background(), timeout.Duration) + defer cancelF() + errCh <- insertFlyoversInAS(ctx, ia, c, flyovers) + }() + } + // Collect any possible error and bail on the first non nil one. + go func() { + defer log.HandlePanic() + for errPerAS := range errCh { + if err != nil { + err = errPerAS + } + } + }() + wg.Wait() + close(errCh) + + if err != nil { + return serrors.WrapStr("at least one AS returned an error while inserting flyovers", err) + } + return nil +} + +type topoPerAS struct { + ASDirName string + Interfaces []common.IFIDType +} + +func getTopoPerAS(pairs []integration.IAPair) (map[addr.IA]topoPerAS, error) { + m := make(map[addr.IA]topoPerAS) + for _, pair := range pairs { + ia := pair.Src.IA + if _, ok := m[ia]; ok { + continue + } + + // Load their topology. + path := integration.GenFile( + filepath.Join( + addr.FormatAS(ia.AS(), addr.WithDefaultPrefix(), addr.WithFileSeparator()), + "topology.json", + ), + ) + topo, err := topology.FromJSONFile(path) + if err != nil { + return nil, serrors.WrapStr("loading topology", err, "ia", ia) + } + + // Set the values for this AS. + m[ia] = topoPerAS{ + ASDirName: addr.FormatAS(ia.AS(), + addr.WithDefaultPrefix(), addr.WithFileSeparator()), + Interfaces: topo.InterfaceIDs(), + } + } + + return m, nil +} + +func createMockFlyovers( + perAS map[addr.IA]topoPerAS, + now time.Time, +) ([]*hummingbird.Flyover, error) { + + // Per IA, insert a flyover with BW units of bandwidth for each interface pair. + // Note that BW has to be enough for one AS to send a ping to another. + const BW = uint16(10) + flyovers := make([]*hummingbird.Flyover, 0) + for ia, c := range perAS { + var resIDPerIA uint32 // reservation ID unique per IA + // Load master key for this ia. It is used to create the mock flyover, by deriving here + // the correct Ak that the border routers will check. + masterFile := integration.GenFile(filepath.Join(c.ASDirName, "keys")) + master0, err := keyconf.LoadMaster(masterFile) + if err != nil { + return nil, serrors.WrapStr("could not load master secret for IA", err, "ia", ia) + } + + // Add the "itself" interface ID to the slice. + ifaces := append(c.Interfaces, 0) + // Create a flyover for each possible ingress->egress s.t. ingress <> egress + inToEgressesMap := ifIDSequenceToMap(ifaces) + for in, egressInterfaces := range inToEgressesMap { + for _, eg := range egressInterfaces { + f := hummingbird.Flyover{ + BaseHop: hummingbird.BaseHop{ + IA: ia, + Ingress: uint16(in), + Egress: uint16(eg), + }, + Bw: BW, + StartTime: util.TimeToSecs(now), + Duration: 60, // 1 Minute + ResID: resIDPerIA, // unique per ia + } + + key0 := control.DeriveHbirdSecretValue(master0.Key0) + prf, _ := aes.NewCipher(key0) + buffer := make([]byte, 16) + ak := hbirddp.DeriveAuthKey(prf, f.ResID, f.Bw, f.Ingress, f.Egress, + f.StartTime, f.Duration, buffer) + copy(f.Ak[:], ak[0:16]) + + // Increment the reservation ID per AS to make it unique (per AS). + resIDPerIA++ + + flyovers = append(flyovers, &f) + } + } + } + return flyovers, nil +} + +func insertFlyoversInAS( + ctx context.Context, + ia addr.IA, + config topoPerAS, + flyovers []*hummingbird.Flyover, +) error { + + daemonAddr, err := integration.GetSCIONDAddress( + integration.GenFile(integration.DaemonAddressesFile), ia) + if err != nil { + return serrors.WrapStr("getting the sciond address", err, "ia", ia) + } + conn, err := daemon.NewService(daemonAddr).Connect(ctx) + if err != nil { + return serrors.WrapStr("opening daemon connection", err, "ia", ia) + } + + err = conn.StoreFlyovers(ctx, flyovers) + if err != nil { + return serrors.WrapStr("storing flyovers using daemon", err, "ia", ia) + } + + err = conn.Close() + if err != nil { + return serrors.WrapStr("closing daemon connection", err, "ia", ia) + } + + return nil +} + +// ifIDSequenceToMap takes a slice of interfaces and returns a map where each ingress has +// a list to egress interfaces from the slice. +func ifIDSequenceToMap(ifSeq []common.IFIDType) map[common.IFIDType][]common.IFIDType { + + m := make(map[common.IFIDType][]common.IFIDType, len(ifSeq)) + for _, src := range ifSeq { + for _, dst := range ifSeq { + if src == dst { + continue + } + m[src] = append(m[src], dst) + } + } + return m +} + func logDir() string { return filepath.Join(integration.LogDir(), name) } From b3c5c7fe2817aba999f17500a39fe89871a05ddf Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Fri, 12 Jan 2024 13:00:03 +0100 Subject: [PATCH 097/100] Flyovers to Map is a public function now. --- daemon/internal/servers/grpc_hummingbird.go | 15 +-------------- pkg/hummingbird/BUILD.bazel | 2 -- pkg/hummingbird/reservation.go | 21 +++++++++++++++++---- 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/daemon/internal/servers/grpc_hummingbird.go b/daemon/internal/servers/grpc_hummingbird.go index 61049f6c53..c22931e71f 100644 --- a/daemon/internal/servers/grpc_hummingbird.go +++ b/daemon/internal/servers/grpc_hummingbird.go @@ -143,7 +143,7 @@ func (s *DaemonServer) getReservations( if err != nil { return nil, err } - mFlyovers := flyoversToMap(flyovers) + mFlyovers := hummingbird.FlyoversToMap(flyovers) // For each path, try to assign as many flyovers as possible. reservations := make([]*hummingbird.Reservation, len(paths)) @@ -254,16 +254,3 @@ func linkTypeFromPB(lt sdpb.LinkType) snet.LinkType { return snet.LinkTypeUnset } } - -func flyoversToMap(flyovers []*hummingbird.Flyover) hummingbird.FlyoverMap { - ret := make(hummingbird.FlyoverMap) - for _, flyover := range flyovers { - k := hummingbird.BaseHop{ - IA: flyover.IA, - Ingress: flyover.Ingress, - Egress: flyover.Egress, - } - ret[k] = append(ret[k], flyover) - } - return ret -} diff --git a/pkg/hummingbird/BUILD.bazel b/pkg/hummingbird/BUILD.bazel index e411154116..c702873aeb 100644 --- a/pkg/hummingbird/BUILD.bazel +++ b/pkg/hummingbird/BUILD.bazel @@ -17,8 +17,6 @@ go_library( "//pkg/slayers/path/scion:go_default_library", "//pkg/snet:go_default_library", "//pkg/snet/path:go_default_library", - "//private/keyconf:go_default_library", - "//router/control:go_default_library", ], ) diff --git a/pkg/hummingbird/reservation.go b/pkg/hummingbird/reservation.go index 0cf2477a4e..7d8d076af3 100644 --- a/pkg/hummingbird/reservation.go +++ b/pkg/hummingbird/reservation.go @@ -60,10 +60,6 @@ func NewReservation(opts ...reservationModFcn) (*Reservation, error) { // reservationModFcn is a options setting function for a reservation. type reservationModFcn func(*Reservation) error -// FlyoverMap is a map between a flyover IA,ingress,egress and its corresponding collection of -// flyover objects (each of them can have e.g. different starting times). -type FlyoverMap map[BaseHop][]*Flyover - // WithScionPath allows to build a Reservation based on the SCION path and flyovers passed as // arguments. // The flyovers are chosen from the map in order of appearance iff they are suitable, i.e. if @@ -281,3 +277,20 @@ func egressID(ifaces []snet.PathInterface, idx int) uint16 { } return uint16(ifaces[i].ID) } + +// FlyoverMap is a map between a flyover IA,ingress,egress and its corresponding collection of +// flyover objects (each of them can have e.g. different starting times). +type FlyoverMap map[BaseHop][]*Flyover + +func FlyoversToMap(flyovers []*Flyover) FlyoverMap { + ret := make(FlyoverMap) + for _, flyover := range flyovers { + k := BaseHop{ + IA: flyover.IA, + Ingress: flyover.Ingress, + Egress: flyover.Egress, + } + ret[k] = append(ret[k], flyover) + } + return ret +} From 56389719492d9c0e720903832495712cfdc238bd Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Fri, 12 Jan 2024 13:01:06 +0100 Subject: [PATCH 098/100] WIP integration test uses flyovers in three different ways. --- tools/end2end_hbird/BUILD.bazel | 3 --- tools/end2end_hbird/main.go | 34 +++++++++++++++++++------ tools/end2end_hbird_integration/main.go | 6 +++-- 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/tools/end2end_hbird/BUILD.bazel b/tools/end2end_hbird/BUILD.bazel index 9a915d92f4..6d3c3a2927 100644 --- a/tools/end2end_hbird/BUILD.bazel +++ b/tools/end2end_hbird/BUILD.bazel @@ -14,15 +14,12 @@ go_library( "//pkg/private/common:go_default_library", "//pkg/private/serrors:go_default_library", "//pkg/private/util:go_default_library", - "//pkg/slayers/path/hummingbird:go_default_library", "//pkg/snet:go_default_library", "//pkg/snet/metrics:go_default_library", "//pkg/snet/path:go_default_library", "//pkg/sock/reliable:go_default_library", - "//private/keyconf:go_default_library", "//private/topology:go_default_library", "//private/tracing:go_default_library", - "//router/control:go_default_library", "//tools/integration:go_default_library", "//tools/integration/integrationlib:go_default_library", "@com_github_opentracing_opentracing_go//:go_default_library", diff --git a/tools/end2end_hbird/main.go b/tools/end2end_hbird/main.go index 7380ac7338..c96cc59b1b 100644 --- a/tools/end2end_hbird/main.go +++ b/tools/end2end_hbird/main.go @@ -298,21 +298,39 @@ func (c *client) attemptRequest(n int) bool { defer span.Finish() // Convert path to Hummingbird path if path != nil { - - // TODO: buy flyovers for existing path(s). This is done by the user asynchronously - - // TODO: insert flyovers obtained from Mysten's library. - - // TODO: using the reservations in the DB, create a hummingbird path - + // // This works: + // reservation, err := hummingbird.NewReservation( + // hummingbird.WithScionPath(path, nil)) + // if err != nil { + // logger.Error("Error converting path to Hummingbird", "err", err) + // return false + // } + + // deleteme this doesn't work and should: + // reservations, err := c.sdConn.GetReservations(ctx, integration.Local.IA, remote.IA, 1, true) + // if err != nil { + // logger.Error("getting reservations from daemon", "err", err) + // return false + // } + // logger.Info("deleteme", "reservation count", len(reservations)) + // _ = reservations + // reservation := reservations[0] + + // deleteme this doesn't work and should: + flyovers, err := c.sdConn.ListFlyovers(ctx) + if err != nil { + logger.Error("listing flyovers", "err", err) + return false + } reservation, err := hummingbird.NewReservation( - hummingbird.WithScionPath(path, nil)) + hummingbird.WithScionPath(path, hummingbird.FlyoversToMap(flyovers))) if err != nil { logger.Error("Error converting path to Hummingbird", "err", err) return false } decoded := reservation.DeriveDataPlanePath(16, time.Now()) + logger.Info("deleteme", "reservation path", fmt.Sprintf("%+v", decoded)) raw := snetpath.Hummingbird{ Raw: make([]byte, decoded.Len()), } diff --git a/tools/end2end_hbird_integration/main.go b/tools/end2end_hbird_integration/main.go index ca800a19cf..3581986ec4 100644 --- a/tools/end2end_hbird_integration/main.go +++ b/tools/end2end_hbird_integration/main.go @@ -474,8 +474,10 @@ func createMockFlyovers( }, Bw: BW, StartTime: util.TimeToSecs(now), - Duration: 60, // 1 Minute - ResID: resIDPerIA, // unique per ia + // Duration: 60, // 1 Minute + // deleteme: change to 1 minute again + Duration: 300, // 1 Hour + ResID: resIDPerIA, // unique per ia } key0 := control.DeriveHbirdSecretValue(master0.Key0) From 5ff4d2e9c55ab0b466d12768a54ab8cfefac1093 Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Mon, 29 Jan 2024 15:01:26 +0100 Subject: [PATCH 099/100] Fix bug in translate. Nil messages in gRPC are deserialized as default-constructed vars, not nils. Extend tests. --- pkg/hummingbird/reservation_test.go | 28 +++++++++++++++--- pkg/hummingbird/translate.go | 13 ++++++++- tools/end2end_hbird/main.go | 44 ++++++++++++++--------------- 3 files changed, 58 insertions(+), 27 deletions(-) diff --git a/pkg/hummingbird/reservation_test.go b/pkg/hummingbird/reservation_test.go index dc1cf32651..08e56d6c86 100644 --- a/pkg/hummingbird/reservation_test.go +++ b/pkg/hummingbird/reservation_test.go @@ -61,16 +61,36 @@ func TestReservationWithScionPathNoFlyovers(t *testing.T) { ) assert.NoError(t, err) - // Should not have any flyovers, but the same amount of hop fields in the dataplane. - flyovers, hfs := r.FlyoverAndHFCount() - assert.Equal(t, 0, flyovers) - assert.Equal(t, decodedScionTestPath.NumHops, hfs) + // Should not have any flyoverCount, but the same amount of hop fields in the dataplane. + flyoverCount, hfCount := r.FlyoverAndHFCount() + assert.Equal(t, 0, flyoverCount) + assert.Equal(t, decodedScionTestPath.NumHops, hfCount) // All hop fields must not be flyovers. decoded := r.DeriveDataPlanePath(16, fixedTime) for i, hf := range decoded.HopFields { assert.False(t, hf.Flyover, "failed at index %d", i) } + + // Check reconstruction from parts. + // First create a reservation from no flyovers again. + rOrig, err := hummingbird.NewReservation( + hummingbird.WithNow(fixedTime), + hummingbird.WithScionPath(scionPath, nil), // nil == "no flyovers" + ) + assert.NoError(t, err) + + // Now create a reservation from an existing hummingbird path and flyovers. + flyovers := r.FlyoverPerHopField() + r2, err := hummingbird.NewReservation( + hummingbird.WithNow(fixedTime), + hummingbird.WithExistingHbirdPath(decoded, flyovers), + ) + assert.NoError(t, err) + // And derive a hummingbird path from both reservations. + expected := rOrig.DeriveDataPlanePath(16, fixedTime) + got := r2.DeriveDataPlanePath(16, fixedTime) + assert.Equal(t, expected, got) } func TestReservationWithHbirdPath(t *testing.T) { diff --git a/pkg/hummingbird/translate.go b/pkg/hummingbird/translate.go index af494290dd..6a3d80a6d4 100644 --- a/pkg/hummingbird/translate.go +++ b/pkg/hummingbird/translate.go @@ -46,10 +46,21 @@ func ConvertFlyoversToPB(flyovers []*Flyover) []*sdpb.Flyover { return ret } +// protobuf field Ak of Flyover. Used to check if the serialized flyover was a nil value or not. +var akField = (&sdpb.Flyover{}).ProtoReflect().Descriptor().Fields().ByName("ak") + +// flyoverWasNil returns true if the Flyover protobuf message was nil. +// Heuristically returns true if the field `Ak` was not set. +func flyoverWasNil(f *sdpb.Flyover) bool { + return !f.ProtoReflect().Has(akField) +} + func ConvertFlyoverFromPB(f *sdpb.Flyover) *Flyover { - if f == nil { + if f == nil || flyoverWasNil(f) { + // No flyover. return nil } + ret := &Flyover{ BaseHop: BaseHop{ IA: addr.IA(f.Ia), diff --git a/tools/end2end_hbird/main.go b/tools/end2end_hbird/main.go index c96cc59b1b..a68cbfe017 100644 --- a/tools/end2end_hbird/main.go +++ b/tools/end2end_hbird/main.go @@ -31,7 +31,6 @@ import ( "github.com/scionproto/scion/pkg/addr" "github.com/scionproto/scion/pkg/daemon" - "github.com/scionproto/scion/pkg/hummingbird" "github.com/scionproto/scion/pkg/log" "github.com/scionproto/scion/pkg/private/common" "github.com/scionproto/scion/pkg/private/serrors" @@ -298,7 +297,18 @@ func (c *client) attemptRequest(n int) bool { defer span.Finish() // Convert path to Hummingbird path if path != nil { + + // This works: + // Directly query the scion daemon. + reservations, err := c.sdConn.GetReservations(ctx, integration.Local.IA, remote.IA, 1, true) + if err != nil { + logger.Error("getting reservations from daemon", "err", err) + return false + } + reservation := reservations[0] + // // This works: + // // Build with no flyovers. // reservation, err := hummingbird.NewReservation( // hummingbird.WithScionPath(path, nil)) // if err != nil { @@ -306,31 +316,21 @@ func (c *client) attemptRequest(n int) bool { // return false // } - // deleteme this doesn't work and should: - // reservations, err := c.sdConn.GetReservations(ctx, integration.Local.IA, remote.IA, 1, true) + // // This works: + // // Get flyovers and build path. + // flyovers, err := c.sdConn.ListFlyovers(ctx) // if err != nil { - // logger.Error("getting reservations from daemon", "err", err) + // logger.Error("listing flyovers", "err", err) + // return false + // } + // reservation, err := hummingbird.NewReservation( + // hummingbird.WithScionPath(path, hummingbird.FlyoversToMap(flyovers))) + // if err != nil { + // logger.Error("Error converting path to Hummingbird", "err", err) // return false // } - // logger.Info("deleteme", "reservation count", len(reservations)) - // _ = reservations - // reservation := reservations[0] - - // deleteme this doesn't work and should: - flyovers, err := c.sdConn.ListFlyovers(ctx) - if err != nil { - logger.Error("listing flyovers", "err", err) - return false - } - reservation, err := hummingbird.NewReservation( - hummingbird.WithScionPath(path, hummingbird.FlyoversToMap(flyovers))) - if err != nil { - logger.Error("Error converting path to Hummingbird", "err", err) - return false - } - decoded := reservation.DeriveDataPlanePath(16, time.Now()) - logger.Info("deleteme", "reservation path", fmt.Sprintf("%+v", decoded)) + decoded := reservation.DeriveDataPlanePath(113, time.Now()) raw := snetpath.Hummingbird{ Raw: make([]byte, decoded.Len()), } From 299b738fb664075b16770279cdf76101c73238c5 Mon Sep 17 00:00:00 2001 From: "Juan A. Garcia Pardo" Date: Mon, 29 Jan 2024 15:15:31 +0100 Subject: [PATCH 100/100] Fix bazel BUILD file. --- router/BUILD.bazel | 1 + 1 file changed, 1 insertion(+) diff --git a/router/BUILD.bazel b/router/BUILD.bazel index 8d444bed8d..75b7a1f165 100644 --- a/router/BUILD.bazel +++ b/router/BUILD.bazel @@ -45,6 +45,7 @@ go_library( go_test( name = "go_default_test", srcs = [ + "dataplane_benchmark_test.go", "dataplane_hbird_test.go", "dataplane_internal_test.go", "dataplane_test.go",