diff options
author | Satoshi Fujimoto <satoshi.fujimoto7@gmail.com> | 2017-08-03 10:44:27 +0900 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2017-09-29 15:05:43 +0900 |
commit | f70827b68c190e3793ace65f116fef8c7c36e752 (patch) | |
tree | 52b1132afb0bbb4336e92b1d20eaa08150030e2e | |
parent | e2e8a840e67868cc3070530fe5e1a0dc3edf73f1 (diff) |
server: Revised Error Handling (RFC7606)
This patch enables GoBGP to keep the session established
even if the received BGPUpdate message contains some errors,
and to handle these errors according to what defined in RFC7606.
This feature is enabled when 'treat-as-withdraw' in
'neighbors.error-handling.config' is specified to true
in the GoBGP config file.
Signed-off-by: Satoshi Fujimoto <satoshi.fujimoto7@gmail.com>
-rw-r--r-- | packet/bgp/bgp.go | 25 | ||||
-rw-r--r-- | server/fsm.go | 122 |
2 files changed, 136 insertions, 11 deletions
diff --git a/packet/bgp/bgp.go b/packet/bgp/bgp.go index 7e60b96e..fb9e2a1c 100644 --- a/packet/bgp/bgp.go +++ b/packet/bgp/bgp.go @@ -7982,6 +7982,31 @@ func (msg *BGPUpdate) IsEndOfRib() (bool, RouteFamily) { return false, RouteFamily(0) } +func TreatAsWithdraw(msg *BGPUpdate) *BGPUpdate { + withdraw := &BGPUpdate{ + WithdrawnRoutesLen: 0, + WithdrawnRoutes: []*IPAddrPrefix{}, + TotalPathAttributeLen: 0, + PathAttributes: make([]PathAttributeInterface, 0, len(msg.PathAttributes)), + NLRI: []*IPAddrPrefix{}, + } + withdraw.WithdrawnRoutes = append(msg.WithdrawnRoutes, msg.NLRI...) + var unreach []AddrPrefixInterface + + for _, p := range msg.PathAttributes { + switch nlri := p.(type) { + case *PathAttributeMpReachNLRI: + unreach = append(unreach, nlri.Value...) + case *PathAttributeMpUnreachNLRI: + unreach = append(unreach, nlri.Value...) + } + } + if len(unreach) != 0 { + withdraw.PathAttributes = append(withdraw.PathAttributes, NewPathAttributeMpUnreachNLRI(unreach)) + } + return withdraw +} + func NewBGPUpdateMessage(withdrawnRoutes []*IPAddrPrefix, pathattrs []PathAttributeInterface, nlri []*IPAddrPrefix) *BGPMessage { return &BGPMessage{ Header: BGPHeader{Type: BGP_MSG_UPDATE}, diff --git a/server/fsm.go b/server/fsm.go index df31b30b..0d208eb4 100644 --- a/server/fsm.go +++ b/server/fsm.go @@ -681,6 +681,92 @@ func hasOwnASLoop(ownAS uint32, limit int, aspath *bgp.PathAttributeAsPath) bool return false } +func extractRouteFamily(p *bgp.PathAttributeInterface) *bgp.RouteFamily { + attr := *p + + var afi uint16 + var safi uint8 + + switch a := attr.(type) { + case *bgp.PathAttributeMpReachNLRI: + afi = a.AFI + safi = a.SAFI + case *bgp.PathAttributeMpUnreachNLRI: + afi = a.AFI + safi = a.SAFI + default: + return nil + } + + rf := bgp.AfiSafiToRouteFamily(afi, safi) + return &rf +} + +func (h *FSMHandler) afiSafiDisable(rf bgp.RouteFamily) string { + n := bgp.AddressFamilyNameMap[rf] + + for i, a := range h.fsm.pConf.AfiSafis { + if string(a.Config.AfiSafiName) == n { + h.fsm.pConf.AfiSafis[i].State.Enabled = false + break + } + } + newList := make([]bgp.ParameterCapabilityInterface, 0) + for _, c := range h.fsm.capMap[bgp.BGP_CAP_MULTIPROTOCOL] { + if c.(*bgp.CapMultiProtocol).CapValue == rf { + continue + } + newList = append(newList, c) + } + h.fsm.capMap[bgp.BGP_CAP_MULTIPROTOCOL] = newList + return n +} + +func (h *FSMHandler) handlingError(m *bgp.BGPMessage, e error, useRevisedError bool) bgp.ErrorHandling { + handling := bgp.ERROR_HANDLING_NONE + if m.Header.Type == bgp.BGP_MSG_UPDATE && useRevisedError { + factor := e.(*bgp.MessageError) + handling = factor.ErrorHandling + switch handling { + case bgp.ERROR_HANDLING_ATTRIBUTE_DISCARD: + log.WithFields(log.Fields{ + "Topic": "Peer", + "Key": h.fsm.pConf.State.NeighborAddress, + "State": h.fsm.state.String(), + "error": e, + }).Warn("Some attributes were discarded") + case bgp.ERROR_HANDLING_TREAT_AS_WITHDRAW: + m.Body = bgp.TreatAsWithdraw(m.Body.(*bgp.BGPUpdate)) + log.WithFields(log.Fields{ + "Topic": "Peer", + "Key": h.fsm.pConf.State.NeighborAddress, + "State": h.fsm.state.String(), + "error": e, + }).Warn("the received Update message was treated as withdraw") + case bgp.ERROR_HANDLING_AFISAFI_DISABLE: + rf := extractRouteFamily(factor.ErrorAttribute) + if rf == nil { + log.WithFields(log.Fields{ + "Topic": "Peer", + "Key": h.fsm.pConf.State.NeighborAddress, + "State": h.fsm.state.String(), + }).Warn("Error occured during AFI/SAFI disabling") + } else { + n := h.afiSafiDisable(*rf) + log.WithFields(log.Fields{ + "Topic": "Peer", + "Key": h.fsm.pConf.State.NeighborAddress, + "State": h.fsm.state.String(), + "error": e, + }).Warnf("Capability %s was disabled", n) + } + } + } else { + handling = bgp.ERROR_HANDLING_SESSION_RESET + } + return handling +} + func (h *FSMHandler) recvMessageWithError() (*FsmMsg, error) { sendToErrorCh := func(reason FsmStateReason) { // probably doesn't happen but be cautious @@ -705,7 +791,7 @@ func (h *FSMHandler) recvMessageWithError() (*FsmMsg, error) { "Key": h.fsm.pConf.State.NeighborAddress, "State": h.fsm.state.String(), "error": err, - }).Warn("malformed BGP Header") + }).Warn("Session will be reset due to malformed BGP Header") fmsg := &FsmMsg{ MsgType: FSM_MSG_BGP_MESSAGE, MsgSrc: h.fsm.pConf.State.NeighborAddress, @@ -722,12 +808,16 @@ func (h *FSMHandler) recvMessageWithError() (*FsmMsg, error) { } now := time.Now() + useRevisedError := h.fsm.pConf.ErrorHandling.Config.TreatAsWithdraw + handling := bgp.ERROR_HANDLING_NONE + m, err := bgp.ParseBGPBody(hd, bodyBuf, h.fsm.marshallingOptions) - if err == nil { + if err != nil { + handling = h.handlingError(m, err, useRevisedError) + h.fsm.bgpMessageStateUpdate(0, true) + } else { h.fsm.bgpMessageStateUpdate(m.Header.Type, true) err = bgp.ValidateBGPMessage(m) - } else { - h.fsm.bgpMessageStateUpdate(0, true) } fmsg := &FsmMsg{ MsgType: FSM_MSG_BGP_MESSAGE, @@ -735,15 +825,21 @@ func (h *FSMHandler) recvMessageWithError() (*FsmMsg, error) { timestamp: now, Version: h.fsm.version, } - if err != nil { + + switch handling { + case bgp.ERROR_HANDLING_AFISAFI_DISABLE: + fmsg.MsgData = m + return fmsg, nil + case bgp.ERROR_HANDLING_SESSION_RESET: log.WithFields(log.Fields{ "Topic": "Peer", "Key": h.fsm.pConf.State.NeighborAddress, "State": h.fsm.state.String(), "error": err, - }).Warn("malformed BGP message") + }).Warn("Session will be reset due to malformed BGP message") fmsg.MsgData = err - } else { + return fmsg, err + default: fmsg.MsgData = m if h.fsm.state == bgp.BGP_FSM_ESTABLISHED { switch m.Header.Type { @@ -757,14 +853,18 @@ func (h *FSMHandler) recvMessageWithError() (*FsmMsg, error) { copy(fmsg.payload, headerBuf) copy(fmsg.payload[len(headerBuf):], bodyBuf) - _, err = bgp.ValidateUpdateMsg(body, h.fsm.rfMap, confedCheck) - if err != nil { + ok, err := bgp.ValidateUpdateMsg(body, h.fsm.rfMap, confedCheck) + if !ok { + handling = h.handlingError(m, err, useRevisedError) + } + + if handling == bgp.ERROR_HANDLING_SESSION_RESET { log.WithFields(log.Fields{ "Topic": "Peer", "Key": h.fsm.pConf.State.NeighborAddress, "State": h.fsm.state.String(), "error": err, - }).Warn("malformed BGP update message") + }).Warn("Session will be reset due to malformed BGP update message") fmsg.MsgData = err return fmsg, err } @@ -848,7 +948,7 @@ func (h *FSMHandler) recvMessageWithError() (*FsmMsg, error) { } } } - return fmsg, err + return fmsg, nil } func (h *FSMHandler) recvMessage() error { |