diff --git a/doc/manuals/router.rst b/doc/manuals/router.rst index cd5f97b578..6427ab6165 100644 --- a/doc/manuals/router.rst +++ b/doc/manuals/router.rst @@ -160,8 +160,8 @@ considers the following options. .. option:: router.num_slow_processors = (Default: 1) - Number of goroutines started for the slow-path processing. This feature will be implemented soon. Currently - this setting has no effect. + Number of goroutines started for the slow-path processing which includes all SCMP traffic and traceroutes. + A minimum of 1 slow-path processor is required. .. option:: router.batch_size = (Default: 256) diff --git a/router/dataplane.go b/router/dataplane.go index 649d16b42b..57b533da4a 100644 --- a/router/dataplane.go +++ b/router/dataplane.go @@ -118,25 +118,31 @@ type DataPlane struct { } var ( - alreadySet = serrors.New("already set") - invalidSrcIA = serrors.New("invalid source ISD-AS") - invalidDstIA = serrors.New("invalid destination ISD-AS") - invalidSrcAddrForTransit = serrors.New("invalid source address for transit pkt") - cannotRoute = serrors.New("cannot route, dropping pkt") - emptyValue = serrors.New("empty value") - malformedPath = serrors.New("malformed path content") - modifyExisting = serrors.New("modifying a running dataplane is not allowed") - noSVCBackend = serrors.New("cannot find internal IP for the SVC") - unsupportedPathType = serrors.New("unsupported path type") - unsupportedPathTypeNextHeader = serrors.New("unsupported combination") - noBFDSessionFound = serrors.New("no BFD sessions was found") - noBFDSessionConfigured = serrors.New("no BFD sessions have been configured") - errBFDDisabled = serrors.New("BFD is disabled") - errPeeringEmptySeg0 = serrors.New("zero-length segment[0] in peering path") - errPeeringEmptySeg1 = serrors.New("zero-length segment[1] in peering path") - errPeeringNonemptySeg2 = serrors.New("non-zero-length segment[2] in peering path") - errShortPacket = serrors.New("Packet is too short") - errBFDSessionDown = serrors.New("bfd session down") + alreadySet = errors.New("already set") + invalidSrcIA = errors.New("invalid source ISD-AS") + invalidDstIA = errors.New("invalid destination ISD-AS") + invalidSrcAddrForTransit = errors.New("invalid source address for transit pkt") + cannotRoute = errors.New("cannot route, dropping pkt") + emptyValue = errors.New("empty value") + malformedPath = errors.New("malformed path content") + modifyExisting = errors.New("modifying a running dataplane is not allowed") + noSVCBackend = errors.New("cannot find internal IP for the SVC") + unsupportedPathType = errors.New("unsupported path type") + unsupportedPathTypeNextHeader = errors.New("unsupported combination") + noBFDSessionFound = errors.New("no BFD sessions was found") + noBFDSessionConfigured = errors.New("no BFD sessions have been configured") + errBFDDisabled = errors.New("BFD is disabled") + errPeeringEmptySeg0 = errors.New("zero-length segment[0] in peering path") + errPeeringEmptySeg1 = errors.New("zero-length segment[1] in peering path") + errPeeringNonemptySeg2 = errors.New("non-zero-length segment[2] in peering path") + errShortPacket = errors.New("Packet is too short") + errBFDSessionDown = errors.New("bfd session down") + expiredHop = errors.New("expired hop") + ingressInterfaceInvalid = errors.New("ingress interface invalid") + macVerificationFailed = errors.New("MAC verification failed") + badPacketSize = errors.New("bad packet size") + slowPathRequired = errors.New("slow-path required") + // zeroBuffer will be used to reset the Authenticator option in the // scionPacketProcessor.OptAuth zeroBuffer = make([]byte, 16) @@ -152,15 +158,6 @@ type drkeyProvider interface { ) (drkey.ASHostKey, error) } -type scmpError struct { - TypeCode slayers.SCMPTypeCode - Cause error -} - -func (e scmpError) Error() string { - return serrors.New("scmp", "typecode", e.TypeCode, "cause", e.Cause).Error() -} - // SetIA sets the local IA for the dataplane. func (d *DataPlane) SetIA(ia addr.IA) error { d.mtx.Lock() @@ -511,7 +508,7 @@ func (d *DataPlane) Run(ctx context.Context, cfg *RunConfig) error { cfg.BatchSize) d.initPacketPool(cfg, processorQueueSize) - procQs, fwQs := initQueues(cfg, d.interfaces, processorQueueSize) + procQs, fwQs, slowQs := initQueues(cfg, d.interfaces, processorQueueSize) for ifID, conn := range d.interfaces { go func(ifID uint16, conn BatchConn) { @@ -526,7 +523,13 @@ func (d *DataPlane) Run(ctx context.Context, cfg *RunConfig) error { for i := 0; i < cfg.NumProcessors; i++ { go func(i int) { defer log.HandlePanic() - d.runProcessor(i, procQs[i], fwQs) + d.runProcessor(i, procQs[i], fwQs, slowQs[i%cfg.NumSlowPathProcessors]) + }(i) + } + for i := 0; i < cfg.NumSlowPathProcessors; i++ { + go func(i int) { + defer log.HandlePanic() + d.runSlowPathProcessor(i, slowQs[i], fwQs) }(i) } @@ -548,7 +551,7 @@ func (d *DataPlane) Run(ctx context.Context, cfg *RunConfig) error { // current dataplane settings and allocates all the buffers func (d *DataPlane) initPacketPool(cfg *RunConfig, processorQueueSize int) { poolSize := len(d.interfaces)*cfg.BatchSize + - cfg.NumProcessors*(processorQueueSize+1) + + (cfg.NumProcessors+cfg.NumSlowPathProcessors)*(processorQueueSize+1) + len(d.interfaces)*(2*cfg.BatchSize) log.Debug("Initialize packet pool of size", "poolSize", poolSize) @@ -560,17 +563,22 @@ func (d *DataPlane) initPacketPool(cfg *RunConfig, processorQueueSize int) { // initializes the processing routines and forwarders queues func initQueues(cfg *RunConfig, interfaces map[uint16]BatchConn, - processorQueueSize int) ([]chan packet, map[uint16]chan packet) { + processorQueueSize int) ([]chan packet, map[uint16]chan packet, + []chan slowPacket) { procQs := make([]chan packet, cfg.NumProcessors) for i := 0; i < cfg.NumProcessors; i++ { procQs[i] = make(chan packet, processorQueueSize) } + slowQs := make([]chan slowPacket, cfg.NumSlowPathProcessors) + for i := 0; i < cfg.NumSlowPathProcessors; i++ { + slowQs[i] = make(chan slowPacket, processorQueueSize) + } fwQs := make(map[uint16]chan packet) for ifID := range interfaces { fwQs[ifID] = make(chan packet, cfg.BatchSize) } - return procQs, fwQs + return procQs, fwQs, slowQs } type packet struct { @@ -585,6 +593,11 @@ type packet struct { rawPacket []byte } +type slowPacket struct { + packet + slowPathRequest slowPathRequest +} + func (d *DataPlane) runReceiver(ifID uint16, conn BatchConn, cfg *RunConfig, procQs []chan packet) { @@ -675,11 +688,10 @@ func (d *DataPlane) returnPacketToPool(pkt []byte) { } func (d *DataPlane) runProcessor(id int, q <-chan packet, - fwQs map[uint16]chan packet) { + fwQs map[uint16]chan packet, slowQ chan<- slowPacket) { log.Debug("Initialize processor with", "id", id) processor := newPacketProcessor(d) - var scmpErr scmpError for d.running { p, ok := <-q if !ok { @@ -691,10 +703,14 @@ func (d *DataPlane) runProcessor(id int, q <-chan packet, egress := result.EgressID switch { case err == nil: - case errors.As(err, &scmpErr): - if !scmpErr.TypeCode.InfoMsg() { - log.Debug("SCMP", "err", scmpErr) + case errors.Is(err, slowPathRequired): + select { + case slowQ <- slowPacket{p, result.SlowPathRequest}: + default: + metrics.DroppedPacketsBusySlowPath.Inc() + d.returnPacketToPool(p.rawPacket) } + continue default: log.Debug("Error processing packet", "err", err) metrics.DroppedPacketsInvalid.Inc() @@ -725,6 +741,150 @@ func (d *DataPlane) runProcessor(id int, q <-chan packet, } } +func (d *DataPlane) runSlowPathProcessor(id int, q <-chan slowPacket, + fwQs map[uint16]chan packet) { + + log.Debug("Initialize slow-path processor with", "id", id) + processor := newSlowPathProcessor(d) + for d.running { + p, ok := <-q + if !ok { + continue + } + metrics := d.forwardingMetrics[p.packet.ingress] + res, err := processor.processPacket(p) + if err != nil { + log.Debug("Error processing packet", "err", err) + metrics.DroppedPacketsInvalid.Inc() + d.returnPacketToPool(p.packet.rawPacket) + continue + } + p.packet.dstAddr = res.OutAddr + p.packet.rawPacket = res.OutPkt + + fwCh, ok := fwQs[res.EgressID] + if !ok { + log.Debug("Error determining forwarder. Egress is invalid", "egress", res.EgressID) + d.returnPacketToPool(p.packet.rawPacket) + continue + } + select { + case fwCh <- p.packet: + default: + d.returnPacketToPool(p.packet.rawPacket) + } + } +} + +func newSlowPathProcessor(d *DataPlane) *slowPathPacketProcessor { + p := &slowPathPacketProcessor{ + d: d, + buffer: gopacket.NewSerializeBuffer(), + macInputBuffer: make([]byte, spao.MACBufferSize), + drkeyProvider: &drkeyutil.FakeProvider{ + EpochDuration: drkeyutil.LoadEpochDuration(), + AcceptanceWindow: drkeyutil.LoadAcceptanceWindow(), + }, + optAuth: slayers.PacketAuthOption{EndToEndOption: new(slayers.EndToEndOption)}, + validAuthBuf: make([]byte, 16), + } + p.scionLayer.RecyclePaths() + return p +} + +type slowPathPacketProcessor struct { + d *DataPlane + ingressID uint16 + rawPkt []byte + srcAddr *net.UDPAddr + buffer gopacket.SerializeBuffer + + scionLayer slayers.SCION + hbhLayer slayers.HopByHopExtnSkipper + e2eLayer slayers.EndToEndExtnSkipper + lastLayer gopacket.DecodingLayer + path *scion.Raw + + // macInputBuffer avoid allocating memory during processing. + macInputBuffer []byte + + // optAuth is a reusable Packet Authenticator Option + optAuth slayers.PacketAuthOption + // validAuthBuf is a reusable buffer for the authentication tag + // to be used in the hasValidAuth() method. + validAuthBuf []byte + + // DRKey key derivation for SCMP authentication + drkeyProvider drkeyProvider +} + +func (p *slowPathPacketProcessor) reset() { + if err := p.buffer.Clear(); err != nil { + log.Debug("Error while clearing buffer", "err", err) + } + p.path = nil + p.hbhLayer = slayers.HopByHopExtnSkipper{} + p.e2eLayer = slayers.EndToEndExtnSkipper{} +} + +func (p *slowPathPacketProcessor) processPacket(pkt slowPacket) (processResult, error) { + var err error + p.reset() + p.ingressID = pkt.ingress + p.srcAddr = pkt.srcAddr + p.rawPkt = pkt.rawPacket + + p.lastLayer, err = decodeLayers(pkt.rawPacket, &p.scionLayer, &p.hbhLayer, &p.e2eLayer) + if err != nil { + return processResult{}, err + } + pathType := p.scionLayer.PathType + switch pathType { + case scion.PathType: + var ok bool + p.path, ok = p.scionLayer.Path.(*scion.Raw) + if !ok { + return processResult{}, malformedPath + } + case epic.PathType: + epicPath, ok := p.scionLayer.Path.(*epic.Path) + if !ok { + return processResult{}, malformedPath + } + p.path = epicPath.ScionPath + if p.path == nil { + return processResult{}, malformedPath + } + default: + //unsupported path type + return processResult{}, serrors.New("Path type not supported for slow-path", + "type", pathType) + } + switch pkt.slowPathRequest.typ { + case slowPathSCMP: //SCMP + s := pkt.slowPathRequest + var layer gopacket.SerializableLayer + switch s.scmpType { + case slayers.SCMPTypeParameterProblem: + layer = &slayers.SCMPParameterProblem{Pointer: s.pointer} + case slayers.SCMPTypeDestinationUnreachable: + layer = &slayers.SCMPDestinationUnreachable{} + case slayers.SCMPTypeExternalInterfaceDown: + layer = &slayers.SCMPExternalInterfaceDown{IA: s.ia, + IfID: uint64(s.interfaceId)} + case slayers.SCMPTypeInternalConnectivityDown: + layer = &slayers.SCMPInternalConnectivityDown{IA: s.ia, + Ingress: uint64(s.ingressId), Egress: uint64(s.egressId)} + } + return p.packSCMP(s.scmpType, s.code, layer, s.cause) + + case slowPathRouterAlert: //Traceroute + return p.handleSCMPTraceRouteRequest(pkt.slowPathRequest.interfaceId) + default: + panic("Unsupported slow-path type") + } +} + func (d *DataPlane) runForwarder(ifID uint16, conn BatchConn, cfg *RunConfig, c <-chan packet) { @@ -818,29 +978,18 @@ func (d *DataPlane) initMetrics() { } type processResult struct { - EgressID uint16 - OutAddr *net.UDPAddr - OutPkt []byte + EgressID uint16 + OutAddr *net.UDPAddr + OutPkt []byte + SlowPathRequest slowPathRequest } func newPacketProcessor(d *DataPlane) *scionPacketProcessor { p := &scionPacketProcessor{ - d: d, - buffer: gopacket.NewSerializeBuffer(), - mac: d.macFactory(), - macBuffers: macBuffers{ - scionInput: make([]byte, path.MACBufferSize), - epicInput: make([]byte, libepic.MACBufferSize), - drkeyInput: make([]byte, spao.MACBufferSize), - }, - // TODO(JordiSubira): Replace this with a useful implementation. - - drkeyProvider: &drkeyutil.FakeProvider{ - EpochDuration: drkeyutil.LoadEpochDuration(), - AcceptanceWindow: drkeyutil.LoadAcceptanceWindow(), - }, - optAuth: slayers.PacketAuthOption{EndToEndOption: new(slayers.EndToEndOption)}, - validAuthBuf: make([]byte, 16), + d: d, + buffer: gopacket.NewSerializeBuffer(), + mac: d.macFactory(), + macInputBuffer: make([]byte, max(path.MACBufferSize, libepic.MACBufferSize)), } p.scionLayer.RecyclePaths() return p @@ -1005,7 +1154,7 @@ func (p *scionPacketProcessor) processEPIC() (processResult, error) { HVF = epicPath.LHVF } err = libepic.VerifyHVF(p.cachedMac, epicPath.PktID, - &p.scionLayer, firstInfo.Timestamp, HVF, p.macBuffers.epicInput) + &p.scionLayer, firstInfo.Timestamp, HVF, p.macInputBuffer[:libepic.MACBufferSize]) if err != nil { // TODO(mawyss): Send back SCMP packet return processResult{}, err @@ -1054,29 +1203,37 @@ type scionPacketProcessor struct { // 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 - // macBuffers avoid allocating memory during processing. - macBuffers macBuffers + // macInputBuffer avoid allocating memory during processing. + macInputBuffer []byte // bfdLayer is reusable buffer for parsing BFD messages bfdLayer layers.BFD - // optAuth is a reusable Packet Authenticator Option - optAuth slayers.PacketAuthOption - // validAuthBuf is a reusable buffer for the authentication tag - // to be used in the hasValidAuth() method. - validAuthBuf []byte - - // DRKey key derivation for SCMP authentication - drkeyProvider drkeyProvider } -// macBuffers are preallocated buffers for the in- and outputs of MAC functions. -type macBuffers struct { - scionInput []byte - epicInput []byte - drkeyInput []byte +type slowPathType int + +const ( + slowPathSCMP slowPathType = iota + slowPathRouterAlert +) + +type slowPathRequest struct { + typ slowPathType + scmpType slayers.SCMPType + code slayers.SCMPCode + cause error + + // The parameters. Only those used for that particular mode and + // type will be valid. + + pointer uint16 + ia addr.IA + interfaceId uint16 + ingressId uint16 + egressId uint16 } -func (p *scionPacketProcessor) packSCMP( +func (p *slowPathPacketProcessor) packSCMP( typ slayers.SCMPType, code slayers.SCMPCode, scmpP gopacket.SerializableLayer, @@ -1119,30 +1276,35 @@ func (p *scionPacketProcessor) parsePath() (processResult, error) { return processResult{}, nil } -func (p *scionPacketProcessor) determinePeer() (processResult, error) { - if !p.infoField.Peer { - return processResult{}, nil +func determinePeer(pathMeta scion.MetaHdr, inf path.InfoField) (bool, error) { + if !inf.Peer { + return false, nil } - if p.path.PathMeta.SegLen[0] == 0 { - return processResult{}, errPeeringEmptySeg0 + if pathMeta.SegLen[0] == 0 { + return false, errPeeringEmptySeg0 } - if p.path.PathMeta.SegLen[1] == 0 { - return processResult{}, errPeeringEmptySeg1 + if pathMeta.SegLen[1] == 0 { + return false, errPeeringEmptySeg1 } - if p.path.PathMeta.SegLen[2] != 0 { - return processResult{}, errPeeringNonemptySeg2 + 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 := p.path.PathMeta.CurrHF - segLen := p.path.PathMeta.SegLen[0] - p.peering = currHF == segLen-1 || currHF == segLen - return processResult{}, nil + currHF := pathMeta.CurrHF + segLen := pathMeta.SegLen[0] + return currHF == segLen-1 || currHF == segLen, nil +} + +func (p *scionPacketProcessor) determinePeer() (processResult, error) { + peer, err := determinePeer(p.path.PathMeta, p.infoField) + p.peering = peer + return processResult{}, err } func (p *scionPacketProcessor) validateHopExpiry() (processResult, error) { @@ -1152,13 +1314,15 @@ func (p *scionPacketProcessor) validateHopExpiry() (processResult, error) { if !expired { return processResult{}, nil } - return p.packSCMP( - slayers.SCMPTypeParameterProblem, - slayers.SCMPCodePathExpired, - &slayers.SCMPParameterProblem{Pointer: p.currentHopPointer()}, - serrors.New("expired hop", "cons_dir", p.infoField.ConsDir, "if_id", p.ingressID, - "curr_inf", p.path.PathMeta.CurrINF, "curr_hf", p.path.PathMeta.CurrHF), - ) + log.Debug("SCMP: expired hop", "cons_dir", p.infoField.ConsDir, "if_id", p.ingressID, + "curr_inf", p.path.PathMeta.CurrINF, "curr_hf", p.path.PathMeta.CurrHF) + slowPathRequest := slowPathRequest{ + scmpType: slayers.SCMPTypeParameterProblem, + code: slayers.SCMPCodePathExpired, + pointer: p.currentHopPointer(), + cause: expiredHop, + } + return processResult{SlowPathRequest: slowPathRequest}, slowPathRequired } func (p *scionPacketProcessor) validateIngressID() (processResult, error) { @@ -1169,13 +1333,15 @@ func (p *scionPacketProcessor) validateIngressID() (processResult, error) { errCode = slayers.SCMPCodeUnknownHopFieldEgress } if p.ingressID != 0 && p.ingressID != pktIngressID { - return p.packSCMP( - slayers.SCMPTypeParameterProblem, - errCode, - &slayers.SCMPParameterProblem{Pointer: p.currentHopPointer()}, - serrors.New("ingress interface invalid", - "pkt_ingress", pktIngressID, "router_ingress", p.ingressID), - ) + log.Debug("SCMP: ingress interface invalid", "pkt_ingress", + pktIngressID, "router_ingress", p.ingressID) + slowPathRequest := slowPathRequest{ + scmpType: slayers.SCMPTypeParameterProblem, + code: errCode, + pointer: p.currentHopPointer(), + cause: ingressInterfaceInvalid, + } + return processResult{SlowPathRequest: slowPathRequest}, slowPathRequired } return processResult{}, nil } @@ -1208,22 +1374,26 @@ func (p *scionPacketProcessor) validateSrcDstIA() (processResult, error) { // invalidSrcIA is a helper to return an SCMP error for an invalid SrcIA. func (p *scionPacketProcessor) invalidSrcIA() (processResult, error) { - return p.packSCMP( - slayers.SCMPTypeParameterProblem, - slayers.SCMPCodeInvalidSourceAddress, - &slayers.SCMPParameterProblem{Pointer: uint16(slayers.CmnHdrLen + addr.IABytes)}, - invalidSrcIA, - ) + log.Debug("SCMP: invalid source IA") + slowPathRequest := slowPathRequest{ + scmpType: slayers.SCMPTypeParameterProblem, + code: slayers.SCMPCodeInvalidSourceAddress, + pointer: uint16(slayers.CmnHdrLen + addr.IABytes), + cause: invalidSrcIA, + } + return processResult{SlowPathRequest: slowPathRequest}, slowPathRequired } // invalidDstIA is a helper to return an SCMP error for an invalid DstIA. func (p *scionPacketProcessor) invalidDstIA() (processResult, error) { - return p.packSCMP( - slayers.SCMPTypeParameterProblem, - slayers.SCMPCodeInvalidDestinationAddress, - &slayers.SCMPParameterProblem{Pointer: uint16(slayers.CmnHdrLen)}, - invalidDstIA, - ) + log.Debug("SCMP: invalid destination IA") + slowPathRequest := slowPathRequest{ + scmpType: slayers.SCMPTypeParameterProblem, + code: slayers.SCMPCodeInvalidDestinationAddress, + pointer: uint16(slayers.CmnHdrLen), + cause: invalidDstIA, + } + return processResult{SlowPathRequest: slowPathRequest}, slowPathRequired } // validateTransitUnderlaySrc checks that the source address of transit packets @@ -1254,12 +1424,14 @@ func (p *scionPacketProcessor) validateEgressID() (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 } ingress, egress := p.d.linkTypes[p.ingressID], p.d.linkTypes[pktEgressID] @@ -1282,12 +1454,15 @@ func (p *scionPacketProcessor) validateEgressID() (processResult, error) { case ingress == topology.Peer && egress == topology.Child: return processResult{}, nil default: // malicious - return p.packSCMP( - slayers.SCMPTypeParameterProblem, - slayers.SCMPCodeInvalidPath, // XXX(matzf) new code InvalidHop? - &slayers.SCMPParameterProblem{Pointer: p.currentHopPointer()}, - serrors.WithCtx(cannotRoute, "ingress_id", p.ingressID, "ingress_type", ingress, - "egress_id", pktEgressID, "egress_type", egress)) + log.Debug("SCMP: cannot route", "ingress_id", p.ingressID, + "ingress_type", ingress, "egress_id", pktEgressID, "egress_type", egress) + slowPathRequest := slowPathRequest{ + scmpType: slayers.SCMPTypeParameterProblem, + code: slayers.SCMPCodeInvalidPath, // XXX(matzf) new code InvalidHop?, + pointer: p.currentHopPointer(), + cause: cannotRoute, + } + return processResult{SlowPathRequest: slowPathRequest}, slowPathRequired } } // Check that the interface pair is valid on a segment switch. @@ -1303,12 +1478,15 @@ func (p *scionPacketProcessor) validateEgressID() (processResult, error) { case ingress == topology.Child && egress == topology.Child: return processResult{}, nil default: - return p.packSCMP( - slayers.SCMPTypeParameterProblem, - slayers.SCMPCodeInvalidSegmentChange, - &slayers.SCMPParameterProblem{Pointer: p.currentInfoPointer()}, - serrors.WithCtx(cannotRoute, "ingress_id", p.ingressID, "ingress_type", ingress, - "egress_id", pktEgressID, "egress_type", egress)) + log.Debug("SCMP: cannot route", "ingress_id", p.ingressID, "ingress_type", ingress, + "egress_id", pktEgressID, "egress_type", egress) + slowPathRequest := slowPathRequest{ + scmpType: slayers.SCMPTypeParameterProblem, + code: slayers.SCMPCodeInvalidSegmentChange, + pointer: p.currentInfoPointer(), + cause: cannotRoute, + } + return processResult{SlowPathRequest: slowPathRequest}, slowPathRequired } } @@ -1336,21 +1514,21 @@ func (p *scionPacketProcessor) currentHopPointer() uint16 { } func (p *scionPacketProcessor) verifyCurrentMAC() (processResult, error) { - fullMac := path.FullMAC(p.mac, p.infoField, p.hopField, p.macBuffers.scionInput) + 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 { - return p.packSCMP( - slayers.SCMPTypeParameterProblem, - slayers.SCMPCodeInvalidHopFieldMAC, - &slayers.SCMPParameterProblem{Pointer: p.currentHopPointer()}, - serrors.New("MAC verification failed", - "expected", fmt.Sprintf("%x", fullMac[: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), - ) + log.Debug("SCMP: MAC verification failed", "expected", fmt.Sprintf( + "%x", fullMac[: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) + slowPathRequest := slowPathRequest{ + scmpType: slayers.SCMPTypeParameterProblem, + code: slayers.SCMPCodeInvalidHopFieldMAC, + 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. @@ -1363,11 +1541,13 @@ func (p *scionPacketProcessor) resolveInbound() (*net.UDPAddr, processResult, er a, err := p.d.resolveLocalDst(p.scionLayer) switch { case errors.Is(err, noSVCBackend): - r, err := p.packSCMP( - slayers.SCMPTypeDestinationUnreachable, - slayers.SCMPCodeNoRoute, - &slayers.SCMPDestinationUnreachable{}, err) - return nil, r, err + log.Debug("SCMP: no SVC backend") + slowPathRequest := slowPathRequest{ + scmpType: slayers.SCMPTypeDestinationUnreachable, + code: slayers.SCMPCodeNoRoute, + cause: err, + } + return nil, processResult{SlowPathRequest: slowPathRequest}, slowPathRequired default: return a, processResult{}, nil } @@ -1442,20 +1622,28 @@ func (p *scionPacketProcessor) validateEgressUp() (processResult, error) { egressID := p.egressInterface() if v, ok := p.d.bfdSessions[egressID]; ok { if !v.IsUp() { - typ := slayers.SCMPTypeExternalInterfaceDown - var scmpP gopacket.SerializableLayer = &slayers.SCMPExternalInterfaceDown{ - IA: p.d.localIA, - IfID: uint64(egressID), - } + var s slowPathRequest + log.Debug("SCMP: bfd session down") if _, external := p.d.external[egressID]; !external { - typ = slayers.SCMPTypeInternalConnectivityDown - scmpP = &slayers.SCMPInternalConnectivityDown{ - IA: p.d.localIA, - Ingress: uint64(p.ingressID), - Egress: uint64(egressID), + s = slowPathRequest{ + scmpType: slayers.SCMPTypeInternalConnectivityDown, + code: 0, + ia: p.d.localIA, + ingressId: p.ingressID, + egressId: egressID, + cause: errBFDSessionDown, + } + } else { + s = slowPathRequest{ + scmpType: slayers.SCMPTypeExternalInterfaceDown, + code: 0, + ia: p.d.localIA, + interfaceId: egressID, + cause: errBFDSessionDown, } } - return p.packSCMP(typ, 0, scmpP, errBFDSessionDown) + return processResult{SlowPathRequest: s}, slowPathRequired + } } return processResult{}, nil @@ -1473,7 +1661,11 @@ func (p *scionPacketProcessor) handleIngressRouterAlert() (processResult, error) if err := p.path.SetHopField(p.hopField, int(p.path.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) ingressRouterAlertFlag() *bool { @@ -1496,7 +1688,11 @@ func (p *scionPacketProcessor) handleEgressRouterAlert() (processResult, error) if err := p.path.SetHopField(p.hopField, int(p.path.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) egressRouterAlertFlag() *bool { @@ -1506,7 +1702,7 @@ func (p *scionPacketProcessor) egressRouterAlertFlag() *bool { return &p.hopField.EgressRouterAlert } -func (p *scionPacketProcessor) handleSCMPTraceRouteRequest( +func (p *slowPathPacketProcessor) handleSCMPTraceRouteRequest( interfaceID uint16) (processResult, error) { if p.lastLayer.NextLayerType() != slayers.LayerTypeSCMP { @@ -1542,13 +1738,15 @@ func (p *scionPacketProcessor) validatePktLen() (processResult, error) { if int(p.scionLayer.PayloadLen) == len(p.scionLayer.Payload) { return processResult{}, nil } - return p.packSCMP( - slayers.SCMPTypeParameterProblem, - slayers.SCMPCodeInvalidPacketSize, - &slayers.SCMPParameterProblem{Pointer: 0}, - serrors.New("bad packet size", - "header", p.scionLayer.PayloadLen, "actual", len(p.scionLayer.Payload)), - ) + log.Debug("SCMP: bad packet size", "header", p.scionLayer.PayloadLen, + "actual", len(p.scionLayer.Payload)) + slowPathRequest := slowPathRequest{ + scmpType: slayers.SCMPTypeParameterProblem, + code: slayers.SCMPCodeInvalidPacketSize, + pointer: 0, + cause: badPacketSize, + } + return processResult{SlowPathRequest: slowPathRequest}, slowPathRequired } func (p *scionPacketProcessor) process() (processResult, error) { @@ -1636,12 +1834,14 @@ func (p *scionPacketProcessor) process() (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 } func (p *scionPacketProcessor) processOHP() (processResult, error) { @@ -1677,7 +1877,7 @@ func (p *scionPacketProcessor) processOHP() (processResult, error) { "type", "ohp", "egress", ohp.FirstHop.ConsEgress, "neighborIA", neighborIA, "dstIA", s.DstIA) } - mac := path.MAC(p.mac, ohp.Info, ohp.FirstHop, p.macBuffers.scionInput) + mac := path.MAC(p.mac, ohp.Info, ohp.FirstHop, p.macInputBuffer[:path.MACBufferSize]) if subtle.ConstantTimeCompare(ohp.FirstHop.Mac[:], mac[:]) == 0 { // TODO parameter problem -> invalid MAC return processResult{}, serrors.New("MAC", "expected", fmt.Sprintf("%x", mac), @@ -1712,7 +1912,8 @@ func (p *scionPacketProcessor) processOHP() (processResult, error) { // XXX(roosd): Here we leak the buffer into the SCION packet header. // This is okay because we do not operate on the buffer or the packet // for the rest of processing. - ohp.SecondHop.Mac = path.MAC(p.mac, ohp.Info, ohp.SecondHop, p.macBuffers.scionInput) + ohp.SecondHop.Mac = path.MAC(p.mac, ohp.Info, ohp.SecondHop, + p.macInputBuffer[:path.MACBufferSize]) if err := updateSCIONLayer(p.rawPkt, s, p.buffer); err != nil { return processResult{}, err @@ -1861,7 +2062,7 @@ func (b *bfdSend) Send(bfd *layers.BFD) error { return err } -func (p *scionPacketProcessor) prepareSCMP( +func (p *slowPathPacketProcessor) prepareSCMP( typ slayers.SCMPType, code slayers.SCMPCode, scmpP gopacket.SerializableLayer, @@ -1901,8 +2102,13 @@ func (p *scionPacketProcessor) prepareSCMP( } revPath := revPathTmp.(*scion.Decoded) + peering, err := determinePeer(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() && !p.peering { + if revPath.IsXover() && !peering { // An effective cross-over is a change of segment other than at // a peering hop. if err := revPath.IncPath(); err != nil { @@ -1914,7 +2120,7 @@ func (p *scionPacketProcessor) prepareSCMP( _, external := p.d.external[p.ingressID] if external { infoField := &revPath.InfoFields[revPath.PathMeta.CurrINF] - if infoField.ConsDir && !p.peering { + if infoField.ConsDir && !peering { hopField := revPath.HopFields[revPath.PathMeta.CurrHF] infoField.UpdateSegID(hopField.Mac) } @@ -2017,7 +2223,7 @@ func (p *scionPacketProcessor) prepareSCMP( PldType: slayers.L4SCMP, Pld: p.buffer.Bytes(), }, - p.macBuffers.drkeyInput, + p.macInputBuffer, p.optAuth.Authenticator(), ) if err != nil { @@ -2033,10 +2239,11 @@ func (p *scionPacketProcessor) prepareSCMP( return nil, serrors.Wrap(cannotRoute, err, "details", "serializing SCION header") } - return p.buffer.Bytes(), scmpError{TypeCode: scmpH.TypeCode, Cause: cause} + log.Debug("scmp", "typecode", scmpH.TypeCode, "cause", cause) + return p.buffer.Bytes(), nil } -func (p *scionPacketProcessor) resetSPAOMetadata(key drkey.ASHostKey, now time.Time) error { +func (p *slowPathPacketProcessor) resetSPAOMetadata(key drkey.ASHostKey, now time.Time) error { // For creating SCMP responses we use sender side. dir := slayers.PacketAuthSenderSide drkeyType := slayers.PacketAuthASHost @@ -2059,7 +2266,7 @@ func (p *scionPacketProcessor) resetSPAOMetadata(key drkey.ASHostKey, now time.T }) } -func (p *scionPacketProcessor) hasValidAuth(t time.Time) bool { +func (p *slowPathPacketProcessor) hasValidAuth(t time.Time) bool { // Check if e2eLayer was parsed for this packet if !p.lastLayer.CanDecode().Contains(slayers.LayerTypeEndToEndExtn) { return false @@ -2107,13 +2314,12 @@ func (p *scionPacketProcessor) hasValidAuth(t time.Time) bool { PldType: slayers.L4SCMP, Pld: p.lastLayer.LayerPayload(), }, - p.macBuffers.drkeyInput, + p.macInputBuffer, p.validAuthBuf, ) if err != nil { return false } - // compare incoming authField with computed authentication tag return subtle.ConstantTimeCompare(authOption.Authenticator(), p.validAuthBuf) != 0 } @@ -2164,6 +2370,7 @@ type forwardingMetrics struct { DroppedPacketsInvalid prometheus.Counter DroppedPacketsBusyProcessor prometheus.Counter DroppedPacketsBusyForwarder prometheus.Counter + DroppedPacketsBusySlowPath prometheus.Counter ProcessedPackets prometheus.Counter } @@ -2181,6 +2388,8 @@ func initForwardingMetrics(metrics *Metrics, labels prometheus.Labels) forwardin c.DroppedPacketsBusyProcessor = metrics.DroppedPacketsTotal.With(labels) labels["reason"] = "busy_forwarder" c.DroppedPacketsBusyForwarder = metrics.DroppedPacketsTotal.With(labels) + labels["reason"] = "busy_slow_path" + c.DroppedPacketsBusySlowPath = metrics.DroppedPacketsTotal.With(labels) c.InputBytesTotal.Add(0) c.InputPacketsTotal.Add(0) @@ -2189,6 +2398,7 @@ func initForwardingMetrics(metrics *Metrics, labels prometheus.Labels) forwardin c.DroppedPacketsInvalid.Add(0) c.DroppedPacketsBusyProcessor.Add(0) c.DroppedPacketsBusyForwarder.Add(0) + c.DroppedPacketsBusySlowPath.Add(0) c.ProcessedPackets.Add(0) return c } diff --git a/router/dataplane_internal_test.go b/router/dataplane_internal_test.go index 60291aaa4c..6e92a8219d 100644 --- a/router/dataplane_internal_test.go +++ b/router/dataplane_internal_test.go @@ -34,6 +34,7 @@ import ( "github.com/scionproto/scion/pkg/private/serrors" "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/scion" @@ -41,6 +42,8 @@ import ( "github.com/scionproto/scion/router/mock_router" ) +var testKey = []byte("testkey_xxxxxxxx") + // TestReceiver sets up a mocked batchConn, starts the receiver that reads from // this batchConn and forwards it to the processing routines channels. We verify // by directly reading from the processing routine channels that we received @@ -80,7 +83,7 @@ func TestReceiver(t *testing.T) { BatchSize: 64, } dp.initPacketPool(runConfig, 64) - procCh, _ := initQueues(runConfig, dp.interfaces, 64) + procCh, _, _ := initQueues(runConfig, dp.interfaces, 64) initialPoolSize := len(dp.packetPool) dp.running = true dp.initMetrics() @@ -174,7 +177,7 @@ func TestForwarder(t *testing.T) { BatchSize: 64, } dp.initPacketPool(runConfig, 64) - _, fwCh := initQueues(runConfig, dp.interfaces, 64) + _, fwCh, _ := initQueues(runConfig, dp.interfaces, 64) initialPoolSize := len(dp.packetPool) dp.running = true dp.initMetrics() @@ -414,6 +417,140 @@ func TestComputeProcIdErrorCases(t *testing.T) { } } +func TestSlowPathProcessing(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + payload := []byte("actualpayloadbytes") + testCases := map[string]struct { + mockMsg func() []byte + prepareDP func(*gomock.Controller) *DataPlane + expectedSlowPathRequest slowPathRequest + srcInterface uint16 + expectedLayerType gopacket.LayerType + }{ + "svc nobackend": { + 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) + }, + mockMsg: func() []byte { + spkt := prepBaseMsg(t, payload, 0) + _ = spkt.SetDstAddr(addr.MustParseHost("CS")) + spkt.DstIA = xtest.MustParseIA("1-ff00:0:110") + ret := toMsg(t, spkt) + return ret + }, + srcInterface: 1, + expectedSlowPathRequest: slowPathRequest{ + typ: slowPathSCMP, + scmpType: slayers.SCMPTypeDestinationUnreachable, + code: slayers.SCMPCodeNoRoute, + cause: noSVCBackend, + }, + expectedLayerType: slayers.LayerTypeSCMPDestinationUnreachable, + }, + "svc invalid": { + 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) + }, + mockMsg: func() []byte { + spkt := prepBaseMsg(t, payload, 0) + _ = spkt.SetDstAddr(addr.MustParseHost("CS")) + spkt.DstIA = xtest.MustParseIA("1-ff00:0:110") + ret := toMsg(t, spkt) + return ret + }, + srcInterface: 1, + expectedSlowPathRequest: slowPathRequest{ + typ: slowPathSCMP, + scmpType: slayers.SCMPTypeDestinationUnreachable, + code: slayers.SCMPCodeNoRoute, + cause: noSVCBackend, + }, + expectedLayerType: slayers.LayerTypeSCMPDestinationUnreachable, + }, + "invalid dest": { + 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) + }, + mockMsg: func() []byte { + spkt := prepBaseMsg(t, payload, 0) + spkt.DstIA = xtest.MustParseIA("1-ff00:0:f1") + ret := toMsg(t, spkt) + return ret + }, + srcInterface: 1, + expectedSlowPathRequest: slowPathRequest{ + typ: slowPathSCMP, + scmpType: slayers.SCMPTypeParameterProblem, + code: slayers.SCMPCodeInvalidDestinationAddress, + cause: invalidDstIA, + pointer: 0xc, + }, + expectedLayerType: slayers.LayerTypeSCMPParameterProblem, + }, + } + for name, tc := range testCases { + name, tc := name, tc + t.Run(name, func(t *testing.T) { + t.Parallel() + dp := tc.prepareDP(ctrl) + rawPacket := tc.mockMsg() + var srcAddr *net.UDPAddr + + processor := newPacketProcessor(dp) + result, err := processor.processPkt(rawPacket, srcAddr, tc.srcInterface) + assert.ErrorIs(t, err, SlowPathRequired) + + assert.Equal(t, tc.expectedSlowPathRequest, result.SlowPathRequest) + slowPathProcessor := newSlowPathProcessor(dp) + result, err = slowPathProcessor.processPacket(slowPacket{ + packet: packet{ + srcAddr: srcAddr, + dstAddr: result.OutAddr, + ingress: tc.srcInterface, + rawPacket: rawPacket, + }, + slowPathRequest: result.SlowPathRequest, + }) + assert.NoError(t, err) + + // here we parse the result.OutPkt to verify that it contains the correct SCMP + // header and typecodes. + packet := gopacket.NewPacket(result.OutPkt, slayers.LayerTypeSCION, gopacket.Default) + scmp := packet.Layer(slayers.LayerTypeSCMP).(*slayers.SCMP) + expectedTypeCode := slayers.CreateSCMPTypeCode(tc.expectedSlowPathRequest.scmpType, + tc.expectedSlowPathRequest.code) + assert.Equal(t, expectedTypeCode, scmp.TypeCode) + assert.NotNil(t, packet.Layer(tc.expectedLayerType)) + }) + } +} + +func toMsg(t *testing.T, spkt *slayers.SCION) []byte { + t.Helper() + buffer := gopacket.NewSerializeBuffer() + payload := []byte("actualpayloadbytes") + err := gopacket.SerializeLayers(buffer, gopacket.SerializeOptions{FixLengths: true}, + spkt, gopacket.Payload(payload)) + require.NoError(t, err) + raw := buffer.Bytes() + ret := make([]byte, bufSize) + copy(ret, raw) + return ret[:len(raw)] +} + +func computeMAC(t *testing.T, key []byte, info path.InfoField, hf path.HopField) [path.MacLen]byte { + mac, err := scrypto.InitMac(key) + require.NoError(t, err) + return path.MAC(mac, info, hf, nil) +} + func serializedBaseMsg(t *testing.T, payload []byte, flowId uint32) []byte { s := prepBaseMsg(t, payload, flowId) buffer := gopacket.NewSerializeBuffer() @@ -440,7 +577,7 @@ func prepBaseMsg(t *testing.T, payload []byte, flowId uint32) *slayers.SCION { dpath := &scion.Decoded{ Base: scion.Base{ PathMeta: scion.MetaHdr{ - CurrHF: 1, + CurrHF: 2, SegLen: [3]uint8{3, 0, 0}, }, NumINF: 1, @@ -456,6 +593,7 @@ func prepBaseMsg(t *testing.T, payload []byte, flowId uint32) *slayers.SCION { {ConsIngress: 1, ConsEgress: 0}, }, } + dpath.HopFields[2].Mac = computeMAC(t, testKey, dpath.InfoFields[0], dpath.HopFields[2]) spkt.Path = dpath return spkt } diff --git a/router/dataplane_test.go b/router/dataplane_test.go index c83bc2701a..57f3b20ddf 100644 --- a/router/dataplane_test.go +++ b/router/dataplane_test.go @@ -539,8 +539,9 @@ func TestDataPlaneRun(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() runConfig := &router.RunConfig{ - NumProcessors: 8, - BatchSize: 256, + NumProcessors: 8, + BatchSize: 256, + NumSlowPathProcessors: 1, } ch := make(chan struct{}) dp := tc.prepareDP(ctrl, ch) @@ -1146,54 +1147,6 @@ func TestProcessPkt(t *testing.T) { egressInterface: 0, assertFunc: assert.NoError, }, - "svc nobackend": { - 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) - }, - mockMsg: func(afterProcessing bool) *ipv4.Message { - spkt, dpath := prepBaseMsg(now) - _ = spkt.SetDstAddr(addr.MustParseHost("CS")) - spkt.DstIA = xtest.MustParseIA("1-ff00:0:110") - dpath.HopFields = []path.HopField{ - {ConsIngress: 41, ConsEgress: 40}, - {ConsIngress: 31, ConsEgress: 30}, - {ConsIngress: 1, ConsEgress: 0}, - } - dpath.Base.PathMeta.CurrHF = 2 - dpath.HopFields[2].Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[2]) - ret := toMsg(t, spkt, dpath) - return ret - }, - srcInterface: 1, - egressInterface: 0, - assertFunc: assertIsSCMPError(slayers.SCMPTypeDestinationUnreachable, 0), - }, - "svc invalid": { - 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) - }, - mockMsg: func(afterProcessing bool) *ipv4.Message { - spkt, dpath := prepBaseMsg(now) - _ = spkt.SetDstAddr(addr.MustParseHost("CS")) - spkt.DstIA = xtest.MustParseIA("1-ff00:0:110") - dpath.HopFields = []path.HopField{ - {ConsIngress: 41, ConsEgress: 40}, - {ConsIngress: 31, ConsEgress: 30}, - {ConsIngress: 1, ConsEgress: 0}, - } - dpath.Base.PathMeta.CurrHF = 2 - dpath.HopFields[2].Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[2]) - ret := toMsg(t, spkt, dpath) - return ret - }, - srcInterface: 1, - egressInterface: 0, - assertFunc: assertIsSCMPError(slayers.SCMPTypeDestinationUnreachable, 0), - }, "onehop inbound": { prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { return router.NewDP( @@ -1401,32 +1354,6 @@ func TestProcessPkt(t *testing.T) { egressInterface: 2, 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) - }, - mockMsg: func(afterProcessing bool) *ipv4.Message { - spkt, dpath := prepBaseMsg(now) - spkt.DstIA = xtest.MustParseIA("1-ff00:0:f1") - dpath.HopFields = []path.HopField{ - {ConsIngress: 41, ConsEgress: 40}, - {ConsIngress: 31, ConsEgress: 404}, - {ConsIngress: 1, ConsEgress: 0}, - } - dpath.Base.PathMeta.CurrHF = 2 - dpath.HopFields[1].Mac = computeMAC(t, key, dpath.InfoFields[0], dpath.HopFields[1]) - ret := toMsg(t, spkt, dpath) - return ret - }, - srcInterface: 1, - egressInterface: 0, - assertFunc: assertIsSCMPError( - slayers.SCMPTypeParameterProblem, - slayers.SCMPCodeInvalidDestinationAddress, - ), - }, "epic inbound": { prepareDP: func(ctrl *gomock.Controller) *router.DataPlane { return router.NewDP(nil, nil, mock_router.NewMockBatchConn(ctrl), nil, @@ -1655,14 +1582,3 @@ func bfd() control.BFD { RequiredMinRxInterval: 25 * time.Millisecond, } } - -// assertIsSCMPError returns an ErrorAssertionFunc asserting that error is an -// scmpError with the specified type/code. -func assertIsSCMPError(typ slayers.SCMPType, code slayers.SCMPCode) assert.ErrorAssertionFunc { - return func(t assert.TestingT, err error, args ...interface{}) bool { - target := router.SCMPError{} - return assert.ErrorAs(t, err, &target, args) && - assert.Equal(t, typ, target.TypeCode.Type(), args) && - assert.Equal(t, code, target.TypeCode.Code(), args) - } -} diff --git a/router/export_test.go b/router/export_test.go index 32d2d14b80..a82d01e5a0 100644 --- a/router/export_test.go +++ b/router/export_test.go @@ -37,7 +37,7 @@ type ProcessResult struct { processResult } -type SCMPError = scmpError +var SlowPathRequired error = slowPathRequired func NewDP( external map[uint16]BatchConn,