Files
osrg-gobgp/server/peer.go
T
Naoto HanaueandFUJITA Tomonori 749df65ac9 cli: support the AsPathLength to display in routing policy
% gobgp -u 10.0.255.1 policy routepolicy policy0
PolicyName policy0:
  StatementName st0:
    Conditions:
      PrefixSet:    ps0   192.168.0.0/16  16..24
      NeighborSet:  ns0   10.0.0.2
      AsPathLength: 10    eq
      MatchOption:  ALL
    Actions:
      REJECT
2015-05-19 16:33:16 +09:00

1110 lines
32 KiB
Go

// Copyright (C) 2014 Nippon Telegraph and Telephone Corporation.
//
// 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 server
import (
"encoding/json"
"fmt"
log "github.com/Sirupsen/logrus"
"github.com/osrg/gobgp/api"
"github.com/osrg/gobgp/config"
"github.com/osrg/gobgp/packet"
"github.com/osrg/gobgp/policy"
"github.com/osrg/gobgp/table"
"gopkg.in/tomb.v2"
"net"
"strconv"
"time"
)
const (
FSM_CHANNEL_LENGTH = 1024
FLOP_THRESHOLD = time.Second * 30
MIN_CONNECT_RETRY = 10
)
type peerMsgType int
const (
_ peerMsgType = iota
PEER_MSG_PATH
PEER_MSG_PEER_DOWN
)
type peerMsg struct {
msgType peerMsgType
msgData interface{}
}
type Peer struct {
t tomb.Tomb
globalConfig config.Global
peerConfig config.Neighbor
connCh chan net.Conn
serverMsgCh chan *serverMsg
peerMsgCh chan *peerMsg
getActiveCh chan struct{}
fsm *FSM
adjRib *table.AdjRib
// peer and rib are always not one-to-one so should not be
// here but it's the simplest and works our first target.
rib *table.TableManager
isGlobalRib bool
rfMap map[bgp.RouteFamily]bool
capMap map[bgp.BGPCapabilityCode]bgp.ParameterCapabilityInterface
peerInfo *table.PeerInfo
siblings map[string]*serverMsgDataPeer
outgoing chan *bgp.BGPMessage
importPolicies []*policy.Policy
defaultImportPolicy config.DefaultPolicyType
exportPolicies []*policy.Policy
defaultExportPolicy config.DefaultPolicyType
}
func NewPeer(g config.Global, peer config.Neighbor, serverMsgCh chan *serverMsg, peerMsgCh chan *peerMsg, peerList []*serverMsgDataPeer, isGlobalRib bool, policyMap map[string]*policy.Policy) *Peer {
p := &Peer{
globalConfig: g,
peerConfig: peer,
connCh: make(chan net.Conn),
serverMsgCh: serverMsgCh,
peerMsgCh: peerMsgCh,
getActiveCh: make(chan struct{}),
rfMap: make(map[bgp.RouteFamily]bool),
capMap: make(map[bgp.BGPCapabilityCode]bgp.ParameterCapabilityInterface),
isGlobalRib: isGlobalRib,
}
p.siblings = make(map[string]*serverMsgDataPeer)
for _, s := range peerList {
p.siblings[s.address.String()] = s
}
p.fsm = NewFSM(&g, &peer, p.connCh)
peer.BgpNeighborCommonState.State = uint32(bgp.BGP_FSM_IDLE)
peer.BgpNeighborCommonState.Downtime = time.Now().Unix()
for _, rf := range peer.AfiSafiList {
k, _ := bgp.GetRouteFamily(rf.AfiSafiName)
p.rfMap[k] = true
}
p.peerInfo = &table.PeerInfo{
AS: peer.PeerAs,
LocalID: g.RouterId,
Address: peer.NeighborAddress,
}
if isGlobalRib {
p.peerInfo.ID = g.RouterId
}
rfList := p.configuredRFlist()
p.adjRib = table.NewAdjRib(rfList)
p.rib = table.NewTableManager(p.peerConfig.NeighborAddress.String(), rfList)
p.setPolicy(policyMap)
p.t.Go(p.loop)
if !peer.TransportOptions.PassiveMode && !isGlobalRib {
p.t.Go(p.connectLoop)
}
return p
}
func (peer *Peer) setPolicy(policyMap map[string]*policy.Policy) {
// configure import policy
policyConfig := peer.peerConfig.ApplyPolicy
inPolicies := make([]*policy.Policy, 0)
for _, policyName := range policyConfig.ImportPolicies {
log.WithFields(log.Fields{
"Topic": "Peer",
"Key": peer.peerConfig.NeighborAddress,
"PolicyName": policyName,
}).Info("import policy installed")
if pol, ok := policyMap[policyName]; ok {
log.Debug("import policy : ", pol)
inPolicies = append(inPolicies, pol)
}
}
peer.importPolicies = inPolicies
// configure export policy
outPolicies := make([]*policy.Policy, 0)
for _, policyName := range policyConfig.ExportPolicies {
log.WithFields(log.Fields{
"Topic": "Peer",
"Key": peer.peerConfig.NeighborAddress,
"PolicyName": policyName,
}).Info("export policy installed")
if pol, ok := policyMap[policyName]; ok {
log.Debug("export policy : ", pol)
outPolicies = append(outPolicies, pol)
}
}
peer.exportPolicies = outPolicies
}
func (peer *Peer) configuredRFlist() []bgp.RouteFamily {
rfList := []bgp.RouteFamily{}
for _, rf := range peer.peerConfig.AfiSafiList {
k, _ := bgp.GetRouteFamily(rf.AfiSafiName)
rfList = append(rfList, k)
}
return rfList
}
func (peer *Peer) sendPathsToSiblings(pathList []table.Path) {
if len(pathList) == 0 {
return
}
for _, s := range peer.siblings {
var pm *peerMsg
if peer.peerConfig.RouteServer.RouteServerClient {
checkas := func(asnum uint32, p []table.Path) []table.Path {
plist := []table.Path{}
for _, path := range p {
asList := path.GetAsList()
send := true
for _, as := range asList {
if as == asnum {
send = false
break
}
}
if send {
plist = append(plist, path)
}
}
return plist
}
p := checkas(s.As, pathList)
if len(p) == 0 {
continue
} else {
pm = &peerMsg{
msgType: PEER_MSG_PATH,
msgData: p,
}
}
} else {
pm = &peerMsg{
msgType: PEER_MSG_PATH,
msgData: pathList,
}
}
s.peerMsgCh <- pm
}
}
func (peer *Peer) handleBGPmessage(m *bgp.BGPMessage) {
log.WithFields(log.Fields{
"Topic": "Peer",
"Key": peer.peerConfig.NeighborAddress,
"data": m,
}).Debug("received")
switch m.Header.Type {
case bgp.BGP_MSG_OPEN:
body := m.Body.(*bgp.BGPOpen)
peer.peerInfo.ID = m.Body.(*bgp.BGPOpen).ID
r := make(map[bgp.RouteFamily]bool)
for _, p := range body.OptParams {
if paramCap, y := p.(*bgp.OptionParameterCapability); y {
for _, c := range paramCap.Capability {
peer.capMap[c.Code()] = c
if c.Code() == bgp.BGP_CAP_MULTIPROTOCOL {
m := c.(*bgp.CapMultiProtocol)
r[bgp.AfiSafiToRouteFamily(m.CapValue.AFI, m.CapValue.SAFI)] = true
}
}
}
}
for rf, _ := range peer.rfMap {
if _, y := r[rf]; !y {
delete(peer.rfMap, rf)
}
}
for _, rf := range peer.configuredRFlist() {
if _, ok := r[rf]; ok {
peer.rfMap[rf] = true
}
}
// calculate HoldTime
// RFC 4271 P.13
// a BGP speaker MUST calculate the value of the Hold Timer
// by using the smaller of its configured Hold Time and the Hold Time
// received in the OPEN message.
holdTime := float64(body.HoldTime)
myHoldTime := peer.fsm.peerConfig.Timers.HoldTime
if holdTime > myHoldTime {
peer.fsm.negotiatedHoldTime = myHoldTime
} else {
peer.fsm.negotiatedHoldTime = holdTime
}
case bgp.BGP_MSG_ROUTE_REFRESH:
rr := m.Body.(*bgp.BGPRouteRefresh)
rf := bgp.AfiSafiToRouteFamily(rr.AFI, rr.SAFI)
if _, ok := peer.rfMap[rf]; !ok {
log.WithFields(log.Fields{
"Topic": "Peer",
"Key": peer.peerConfig.NeighborAddress,
"Data": rf,
}).Warn("Route family isn't supported")
return
}
if _, ok := peer.capMap[bgp.BGP_CAP_ROUTE_REFRESH]; ok {
pathList := peer.adjRib.GetOutPathList(rf)
peer.sendMessages(table.CreateUpdateMsgFromPaths(pathList))
} else {
log.WithFields(log.Fields{
"Topic": "Peer",
"Key": peer.peerConfig.NeighborAddress,
}).Warn("ROUTE_REFRESH received but the capability wasn't advertised")
}
case bgp.BGP_MSG_UPDATE:
peer.peerConfig.BgpNeighborCommonState.UpdateRecvTime = time.Now().Unix()
body := m.Body.(*bgp.BGPUpdate)
_, err := bgp.ValidateUpdateMsg(body, peer.rfMap)
if err != nil {
log.WithFields(log.Fields{
"Topic": "Peer",
"Key": peer.peerConfig.NeighborAddress,
"error": err,
}).Warn("malformed BGP update message")
m := err.(*bgp.MessageError)
if m.TypeCode != 0 {
peer.outgoing <- bgp.NewBGPNotificationMessage(m.TypeCode, m.SubTypeCode, m.Data)
}
return
}
table.UpdatePathAttrs4ByteAs(body)
pathList := table.ProcessMessage(m, peer.peerInfo)
peer.adjRib.UpdateIn(pathList)
peer.sendPathsToSiblings(pathList)
}
}
func (peer *Peer) sendMessages(msgs []*bgp.BGPMessage) {
for _, m := range msgs {
if peer.peerConfig.BgpNeighborCommonState.State != uint32(bgp.BGP_FSM_ESTABLISHED) {
continue
}
if m.Header.Type != bgp.BGP_MSG_UPDATE {
log.Fatal("not update message ", m.Header.Type)
}
_, y := peer.capMap[bgp.BGP_CAP_FOUR_OCTET_AS_NUMBER]
if !y {
log.WithFields(log.Fields{
"Topic": "Peer",
"Key": peer.peerConfig.NeighborAddress,
"data": m,
}).Debug("update for 2byte AS peer")
table.UpdatePathAttrs2ByteAs(m.Body.(*bgp.BGPUpdate))
}
peer.outgoing <- m
}
}
func (peer *Peer) handleGrpc(grpcReq *GrpcRequest) {
result := &GrpcResponse{}
switch grpcReq.RequestType {
case REQ_GLOBAL_ADD, REQ_GLOBAL_DELETE:
rf := grpcReq.RouteFamily
path, ok := grpcReq.Data.(*api.Path)
if !ok {
result.ResponseErr = fmt.Errorf("type assertion failed")
grpcReq.ResponseCh <- result
close(grpcReq.ResponseCh)
return
}
var isWithdraw bool
if grpcReq.RequestType == REQ_GLOBAL_DELETE {
isWithdraw = true
}
var nlri bgp.AddrPrefixInterface
pattr := make([]bgp.PathAttributeInterface, 0)
pattr = append(pattr, bgp.NewPathAttributeOrigin(bgp.BGP_ORIGIN_ATTR_TYPE_IGP))
asparam := bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint32{peer.peerInfo.AS})
pattr = append(pattr, bgp.NewPathAttributeAsPath([]bgp.AsPathParamInterface{asparam}))
switch rf {
case bgp.RF_IPv4_UC:
ip, net, _ := net.ParseCIDR(path.Nlri.Prefix)
if ip.To4() == nil {
result.ResponseErr = fmt.Errorf("Invalid ipv4 prefix: %s", path.Nlri.Prefix)
grpcReq.ResponseCh <- result
close(grpcReq.ResponseCh)
return
}
ones, _ := net.Mask.Size()
nlri = &bgp.NLRInfo{
IPAddrPrefix: *bgp.NewIPAddrPrefix(uint8(ones), ip.String()),
}
pattr = append(pattr, bgp.NewPathAttributeNextHop("0.0.0.0"))
case bgp.RF_IPv6_UC:
ip, net, _ := net.ParseCIDR(path.Nlri.Prefix)
if ip.To16() == nil {
result.ResponseErr = fmt.Errorf("Invalid ipv6 prefix: %s", path.Nlri.Prefix)
grpcReq.ResponseCh <- result
close(grpcReq.ResponseCh)
return
}
ones, _ := net.Mask.Size()
nlri = bgp.NewIPv6AddrPrefix(uint8(ones), ip.String())
pattr = append(pattr, bgp.NewPathAttributeMpReachNLRI("::", []bgp.AddrPrefixInterface{nlri}))
case bgp.RF_EVPN:
mac, err := net.ParseMAC(path.Nlri.EvpnNlri.MacIpAdv.MacAddr)
if err != nil {
result.ResponseErr = fmt.Errorf("Invalid mac: %s", path.Nlri.EvpnNlri.MacIpAdv.MacAddr)
grpcReq.ResponseCh <- result
close(grpcReq.ResponseCh)
return
}
ip := net.ParseIP(path.Nlri.EvpnNlri.MacIpAdv.IpAddr)
if ip == nil {
result.ResponseErr = fmt.Errorf("Invalid ip prefix: %s", path.Nlri.EvpnNlri.MacIpAdv.IpAddr)
grpcReq.ResponseCh <- result
close(grpcReq.ResponseCh)
return
}
iplen := net.IPv4len * 8
if ip.To4() == nil {
iplen = net.IPv6len * 8
}
macIpAdv := &bgp.EVPNMacIPAdvertisementRoute{
RD: bgp.NewRouteDistinguisherTwoOctetAS(0, 0),
ESI: bgp.EthernetSegmentIdentifier{
Type: bgp.ESI_ARBITRARY,
},
MacAddressLength: 48,
MacAddress: mac,
IPAddressLength: uint8(iplen),
IPAddress: ip,
Labels: []uint32{0},
}
nlri = bgp.NewEVPNNLRI(bgp.EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT, 0, macIpAdv)
pattr = append(pattr, bgp.NewPathAttributeMpReachNLRI("0.0.0.0", []bgp.AddrPrefixInterface{nlri}))
case bgp.RF_ENCAP:
endpoint := net.ParseIP(path.Nlri.Prefix)
if endpoint == nil {
result.ResponseErr = fmt.Errorf("Invalid endpoint ip address: %s", path.Nlri.Prefix)
grpcReq.ResponseCh <- result
close(grpcReq.ResponseCh)
return
}
nlri = bgp.NewEncapNLRI(endpoint.String())
pattr = append(pattr, bgp.NewPathAttributeMpReachNLRI("0.0.0.0", []bgp.AddrPrefixInterface{nlri}))
iterSubTlvs := func(subTlvs []*api.TunnelEncapSubTLV) {
for _, subTlv := range subTlvs {
if subTlv.Type == api.ENCAP_SUBTLV_TYPE_COLOR {
color := subTlv.Color
subTlv := &bgp.TunnelEncapSubTLV{
Type: bgp.ENCAP_SUBTLV_TYPE_COLOR,
Value: &bgp.TunnelEncapSubTLVColor{color},
}
tlv := &bgp.TunnelEncapTLV{
Type: bgp.TUNNEL_TYPE_VXLAN,
Value: []*bgp.TunnelEncapSubTLV{subTlv},
}
attr := bgp.NewPathAttributeTunnelEncap([]*bgp.TunnelEncapTLV{tlv})
pattr = append(pattr, attr)
break
}
}
}
iterTlvs := func(tlvs []*api.TunnelEncapTLV) {
for _, tlv := range tlvs {
if tlv.Type == api.TUNNEL_TYPE_VXLAN {
iterSubTlvs(tlv.SubTlv)
break
}
}
}
func(attrs []*api.PathAttr) {
for _, attr := range attrs {
if attr.Type == api.BGP_ATTR_TYPE_TUNNEL_ENCAP {
iterTlvs(attr.TunnelEncap)
break
}
}
}(path.Attrs)
case bgp.RF_RTC_UC:
var ec bgp.ExtendedCommunityInterface
target := path.Nlri.RtNlri.Target
ec_type := target.Type
ec_subtype := target.Subtype
switch ec_type {
case api.EXTENDED_COMMUNITIE_TYPE_TWO_OCTET_AS_SPECIFIC:
if target.Asn == 0 && target.LocalAdmin == 0 {
break
}
ec = &bgp.TwoOctetAsSpecificExtended{
SubType: bgp.ExtendedCommunityAttrSubType(ec_subtype),
AS: uint16(target.Asn),
LocalAdmin: target.LocalAdmin,
IsTransitive: true,
}
default:
result.ResponseErr = fmt.Errorf("Invalid endpoint ip address: %s", path.Nlri.Prefix)
grpcReq.ResponseCh <- result
close(grpcReq.ResponseCh)
return
}
nlri = bgp.NewRouteTargetMembershipNLRI(peer.globalConfig.As, ec)
pattr = append(pattr, bgp.NewPathAttributeMpReachNLRI("0.0.0.0", []bgp.AddrPrefixInterface{nlri}))
default:
result.ResponseErr = fmt.Errorf("Unsupported address family: %s", rf)
grpcReq.ResponseCh <- result
close(grpcReq.ResponseCh)
return
}
p, err := table.CreatePath(peer.peerInfo, nlri, pattr, isWithdraw, time.Now())
if err != nil {
result.ResponseErr = err
grpcReq.ResponseCh <- result
close(grpcReq.ResponseCh)
return
}
pm := &peerMsg{
msgType: PEER_MSG_PATH,
msgData: []table.Path{p},
}
peer.peerMsgCh <- pm
case REQ_LOCAL_RIB, REQ_GLOBAL_RIB:
if peer.fsm.adminState == ADMIN_STATE_DOWN {
close(grpcReq.ResponseCh)
return
}
if t, ok := peer.rib.Tables[grpcReq.RouteFamily]; ok {
for _, dst := range t.GetDestinations() {
result := &GrpcResponse{}
result.Data = dst.ToApiStruct()
grpcReq.ResponseCh <- result
}
}
close(grpcReq.ResponseCh)
return
case REQ_NEIGHBOR_SHUTDOWN:
peer.outgoing <- bgp.NewBGPNotificationMessage(bgp.BGP_ERROR_CEASE, bgp.BGP_ERROR_SUB_ADMINISTRATIVE_SHUTDOWN, nil)
case REQ_NEIGHBOR_RESET:
peer.fsm.idleHoldTime = peer.peerConfig.Timers.IdleHoldTimeAfterReset
peer.outgoing <- bgp.NewBGPNotificationMessage(bgp.BGP_ERROR_CEASE, bgp.BGP_ERROR_SUB_ADMINISTRATIVE_RESET, nil)
case REQ_NEIGHBOR_SOFT_RESET, REQ_NEIGHBOR_SOFT_RESET_IN:
// soft-reconfiguration inbound
peer.sendPathsToSiblings(peer.adjRib.GetInPathList(grpcReq.RouteFamily))
if grpcReq.RequestType == REQ_NEIGHBOR_SOFT_RESET_IN {
break
}
fallthrough
case REQ_NEIGHBOR_SOFT_RESET_OUT:
pathList := peer.adjRib.GetOutPathList(grpcReq.RouteFamily)
peer.sendMessages(table.CreateUpdateMsgFromPaths(pathList))
case REQ_ADJ_RIB_IN, REQ_ADJ_RIB_OUT:
rf := grpcReq.RouteFamily
var paths []table.Path
if grpcReq.RequestType == REQ_ADJ_RIB_IN {
paths = peer.adjRib.GetInPathList(rf)
log.Debugf("RouteFamily=%v adj-rib-in found : %d", rf.String(), len(paths))
} else {
paths = peer.adjRib.GetOutPathList(rf)
log.Debugf("RouteFamily=%v adj-rib-out found : %d", rf.String(), len(paths))
}
for _, p := range paths {
result := &GrpcResponse{}
path := &api.Path{}
j, _ := json.Marshal(p)
err := json.Unmarshal(j, path)
if err != nil {
result.ResponseErr = err
} else {
result.Data = path
}
grpcReq.ResponseCh <- result
}
close(grpcReq.ResponseCh)
return
case REQ_NEIGHBOR_ENABLE, REQ_NEIGHBOR_DISABLE:
var err api.Error
if grpcReq.RequestType == REQ_NEIGHBOR_ENABLE {
select {
case peer.fsm.adminStateCh <- ADMIN_STATE_UP:
log.WithFields(log.Fields{
"Topic": "Peer",
"Key": peer.peerConfig.NeighborAddress,
}).Debug("ADMIN_STATE_UP requested")
err.Code = api.Error_SUCCESS
err.Msg = "ADMIN_STATE_UP"
default:
log.Warning("previous request is still remaining. : ", peer.peerConfig.NeighborAddress)
err.Code = api.Error_FAIL
err.Msg = "previous request is still remaining"
}
} else {
select {
case peer.fsm.adminStateCh <- ADMIN_STATE_DOWN:
log.WithFields(log.Fields{
"Topic": "Peer",
"Key": peer.peerConfig.NeighborAddress,
}).Debug("ADMIN_STATE_DOWN requested")
err.Code = api.Error_SUCCESS
err.Msg = "ADMIN_STATE_DOWN"
default:
log.Warning("previous request is still remaining. : ", peer.peerConfig.NeighborAddress)
err.Code = api.Error_FAIL
err.Msg = "previous request is still remaining"
}
}
result.Data = err
case REQ_NEIGHBOR_POLICY:
result := &GrpcResponse{}
resInPolicies := []*api.PolicyDefinition{}
resOutPolicies := []*api.PolicyDefinition{}
// Add importpolies that has been set in the configuration file to the list.
// However, peer haven't target importpolicy when add PolicyDefinition of name only to the list.
conInPolicyNames := peer.peerConfig.ApplyPolicy.ImportPolicies
for _, conInPolicyName := range conInPolicyNames {
match := false
for _, inPolicy := range peer.importPolicies {
if conInPolicyName == inPolicy.Name {
match = true
resInPolicies = append(resInPolicies, inPolicy.ToApiStruct())
break
}
}
if !match {
resInPolicies = append(resInPolicies, &api.PolicyDefinition{PolicyDefinitionName: conInPolicyName})
}
}
// Add importpolies that has been set in the configuration file to the list.
// However, peer haven't target importpolicy when add PolicyDefinition of name only to the list.
conOutPolicyNames := peer.peerConfig.ApplyPolicy.ExportPolicies
for _, conOutPolicyName := range conOutPolicyNames {
match := false
for _, outPolicy := range peer.exportPolicies {
if conOutPolicyName == outPolicy.Name {
match = true
resOutPolicies = append(resOutPolicies, outPolicy.ToApiStruct())
break
}
}
if !match {
resOutPolicies = append(resOutPolicies, &api.PolicyDefinition{PolicyDefinitionName: conOutPolicyName})
}
}
result.Data = &api.ApplyPolicy{
DefaultImportPolicy: int64(peer.defaultImportPolicy),
ImportPolicies: resInPolicies,
DefaultExportPolicy: int64(peer.defaultExportPolicy),
ExportPolicies: resOutPolicies,
}
grpcReq.ResponseCh <- result
close(grpcReq.ResponseCh)
return
}
grpcReq.ResponseCh <- result
close(grpcReq.ResponseCh)
}
func (peer *Peer) sendUpdateMsgFromPaths(pList []table.Path) {
pList = func(arg []table.Path) []table.Path {
ret := make([]table.Path, 0, len(arg))
for _, path := range arg {
if _, ok := peer.rfMap[path.GetRouteFamily()]; !ok {
continue
}
if peer.peerConfig.NeighborAddress.Equal(path.GetSource().Address) {
log.WithFields(log.Fields{
"Topic": "Peer",
"Key": peer.peerConfig.NeighborAddress,
"Data": path,
}).Debug("From me, ignore.")
continue
}
if peer.peerConfig.PeerAs == path.GetSourceAs() {
log.WithFields(log.Fields{
"Topic": "Peer",
"Key": peer.peerConfig.NeighborAddress,
"Data": path,
}).Debug("AS PATH loop, ignore.")
continue
}
if !path.IsWithdraw() {
applied, path := peer.applyPolicies(peer.exportPolicies, path)
if applied && path == nil {
log.WithFields(log.Fields{
"Topic": "Peer",
"Key": peer.peerConfig.NeighborAddress,
"Data": path,
}).Debug("Export policy applied, reject.")
continue
} else if peer.defaultExportPolicy != config.DEFAULT_POLICY_TYPE_ACCEPT_ROUTE {
log.WithFields(log.Fields{
"Topic": "Peer",
"Key": peer.peerConfig.NeighborAddress,
"Data": path,
}).Debug("Default export policy applied, reject.")
continue
}
}
ret = append(ret, path.Clone(path.IsWithdraw()))
}
return ret
}(pList)
peer.adjRib.UpdateOut(pList)
if bgp.FSMState(peer.peerConfig.BgpNeighborCommonState.State) != bgp.BGP_FSM_ESTABLISHED || len(pList) == 0 {
return
}
pList = func(arg []table.Path) []table.Path {
ret := make([]table.Path, 0, len(arg))
for _, path := range pList {
isLocal := path.GetSource().ID.Equal(peer.peerInfo.LocalID)
if isLocal {
path.SetNexthop(peer.peerConfig.LocalAddress)
} else {
table.UpdatePathAttrs(path, &peer.globalConfig, &peer.peerConfig)
}
ret = append(ret, path)
}
return ret
}(pList)
peer.sendMessages(table.CreateUpdateMsgFromPaths(pList))
}
// apply policies to the path
// if multiple policies are defined,
// this function applies each policy to the path in the order that
// policies are stored in the array passed to this function.
//
// the way of applying statements inside a single policy
// - apply statement until the condition in the statement matches.
// if the condition matches the path, apply the action on the statement and
// return value that indicates 'applied' to caller of this function
// - if no statement applied, then process the next policy
//
// if no policy applied, return value that indicates 'not applied' to the caller of this function
//
// return values:
// bool -- indicates that any of policy applied to the path that is passed to this function
// table.Path -- indicates new path object that is the result of modification according to
// policy's action.
// If the applied policy doesn't have a modification action,
// then return the path itself that is passed to this function, otherwise return
// modified path.
// If action of the policy is 'reject', return nil
//
func (peer *Peer) applyPolicies(policies []*policy.Policy, original table.Path) (bool, table.Path) {
var applied bool = true
for _, pol := range policies {
if result, action, newpath := pol.Apply(original); result {
log.Debug("newpath: ", newpath)
if action == policy.ROUTE_TYPE_REJECT {
log.WithFields(log.Fields{
"Topic": "Peer",
"Key": peer.peerConfig.NeighborAddress,
"NRLI": original.GetNlri(),
}).Debug("path was rejected")
// return applied, nil, this means path was rejected
return applied, nil
} else {
// return applied, new path
return applied, newpath
}
}
}
log.Debug("no policy applied.", original)
// return not applied, original path
return !applied, original
}
func (peer *Peer) handlePeerMsg(m *peerMsg) {
switch m.msgType {
case PEER_MSG_PATH:
pList := m.msgData.([]table.Path)
tmp := make([]table.Path, 0, len(pList))
for _, path := range pList {
if path.IsWithdraw() {
tmp = append(tmp, path)
continue
}
applied, path := peer.applyPolicies(peer.importPolicies, path)
if applied && path == nil {
log.WithFields(log.Fields{
"Topic": "Peer",
"Key": peer.peerConfig.NeighborAddress,
"Data": path,
}).Debug("Import policy applied, reject.")
continue
} else if peer.defaultImportPolicy != config.DEFAULT_POLICY_TYPE_ACCEPT_ROUTE {
log.WithFields(log.Fields{
"Topic": "Peer",
"Key": peer.peerConfig.NeighborAddress,
"Data": path,
}).Debug("Default import policy applied, reject.")
continue
}
tmp = append(tmp, path)
}
pList = tmp
if peer.peerConfig.RouteServer.RouteServerClient || peer.isGlobalRib {
pList, _ = peer.rib.ProcessPaths(pList)
}
if peer.isGlobalRib {
peer.sendPathsToSiblings(pList)
} else {
peer.sendUpdateMsgFromPaths(pList)
}
case PEER_MSG_PEER_DOWN:
for _, rf := range peer.configuredRFlist() {
pList, _ := peer.rib.DeletePathsforPeer(m.msgData.(*table.PeerInfo), rf)
if peer.peerConfig.RouteServer.RouteServerClient {
peer.sendUpdateMsgFromPaths(pList)
} else if peer.isGlobalRib {
peer.sendPathsToSiblings(pList)
}
}
}
}
func (peer *Peer) handleServerMsg(m *serverMsg) {
switch m.msgType {
case SRV_MSG_PEER_ADDED:
d := m.msgData.(*serverMsgDataPeer)
peer.siblings[d.address.String()] = d
for _, rf := range peer.configuredRFlist() {
if peer.peerConfig.RouteServer.RouteServerClient {
peer.sendPathsToSiblings(peer.adjRib.GetInPathList(rf))
} else if peer.isGlobalRib {
peer.sendPathsToSiblings(peer.rib.GetPathList(rf))
}
}
case SRV_MSG_PEER_DELETED:
d := m.msgData.(*table.PeerInfo)
if _, ok := peer.siblings[d.Address.String()]; ok {
delete(peer.siblings, d.Address.String())
for _, rf := range peer.configuredRFlist() {
pList, _ := peer.rib.DeletePathsforPeer(d, rf)
if peer.peerConfig.RouteServer.RouteServerClient {
peer.sendUpdateMsgFromPaths(pList)
} else {
peer.sendPathsToSiblings(pList)
}
}
} else {
log.Warning("can not find peer: ", d.Address.String())
}
case SRV_MSG_API:
peer.handleGrpc(m.msgData.(*GrpcRequest))
case SRV_MSG_POLICY_UPDATED:
log.Debug("policy updated")
d := m.msgData.(map[string]*policy.Policy)
peer.setPolicy(d)
default:
log.Fatal("unknown server msg type ", m.msgType)
}
}
func (peer *Peer) connectLoop() error {
var tick int
if tick = int(peer.fsm.peerConfig.Timers.ConnectRetry); tick < MIN_CONNECT_RETRY {
tick = MIN_CONNECT_RETRY
}
ticker := time.NewTicker(time.Duration(tick) * time.Second)
ticker.Stop()
connect := func() {
if bgp.FSMState(peer.peerConfig.BgpNeighborCommonState.State) == bgp.BGP_FSM_ACTIVE {
var host string
addr := peer.peerConfig.NeighborAddress
if addr.To4() != nil {
host = addr.String() + ":" + strconv.Itoa(bgp.BGP_PORT)
} else {
host = "[" + addr.String() + "]:" + strconv.Itoa(bgp.BGP_PORT)
}
conn, err := net.DialTimeout("tcp", host, time.Duration(MIN_CONNECT_RETRY-1)*time.Second)
if err == nil {
peer.connCh <- conn
} else {
log.WithFields(log.Fields{
"Topic": "Peer",
"Key": peer.peerConfig.NeighborAddress,
}).Debugf("failed to connect: %s", err)
}
}
}
for {
select {
case <-peer.t.Dying():
log.WithFields(log.Fields{
"Topic": "Peer",
"Key": peer.peerConfig.NeighborAddress,
}).Debug("stop connect loop")
ticker.Stop()
return nil
case <-ticker.C:
connect()
case <-peer.getActiveCh:
ticker = time.NewTicker(time.Duration(tick) * time.Second)
}
}
}
// this goroutine handles routing table operations
func (peer *Peer) loop() error {
for {
incoming := make(chan *fsmMsg, FSM_CHANNEL_LENGTH)
peer.outgoing = make(chan *bgp.BGPMessage, FSM_CHANNEL_LENGTH)
var h *FSMHandler
if !peer.isGlobalRib {
h = NewFSMHandler(peer.fsm, incoming, peer.outgoing)
switch peer.peerConfig.BgpNeighborCommonState.State {
case uint32(bgp.BGP_FSM_ESTABLISHED):
peer.peerConfig.LocalAddress = peer.fsm.LocalAddr()
for rf, _ := range peer.rfMap {
pathList := peer.adjRib.GetOutPathList(rf)
if !peer.peerConfig.RouteServer.RouteServerClient {
for _, path := range pathList {
path.SetNexthop(peer.peerConfig.LocalAddress)
}
}
peer.sendMessages(table.CreateUpdateMsgFromPaths(pathList))
}
peer.fsm.peerConfig.BgpNeighborCommonState.Uptime = time.Now().Unix()
peer.fsm.peerConfig.BgpNeighborCommonState.EstablishedCount++
case uint32(bgp.BGP_FSM_ACTIVE):
if !peer.peerConfig.TransportOptions.PassiveMode {
peer.getActiveCh <- struct{}{}
}
fallthrough
default:
peer.fsm.peerConfig.BgpNeighborCommonState.Downtime = time.Now().Unix()
}
}
sameState := true
for sameState {
select {
case <-peer.t.Dying():
close(peer.connCh)
peer.outgoing <- bgp.NewBGPNotificationMessage(bgp.BGP_ERROR_CEASE, bgp.BGP_ERROR_SUB_PEER_DECONFIGURED, nil)
// h.t.Kill(nil) will be called
// internall so even goroutines in
// non-established will be killed.
h.Stop()
return nil
case e := <-incoming:
switch e.MsgType {
case FSM_MSG_STATE_CHANGE:
nextState := e.MsgData.(bgp.FSMState)
// waits for all goroutines created for the current state
h.Wait()
oldState := bgp.FSMState(peer.peerConfig.BgpNeighborCommonState.State)
peer.peerConfig.BgpNeighborCommonState.State = uint32(nextState)
peer.fsm.StateChange(nextState)
sameState = false
if oldState == bgp.BGP_FSM_ESTABLISHED {
t := time.Now()
if t.Sub(time.Unix(peer.fsm.peerConfig.BgpNeighborCommonState.Uptime, 0)) < FLOP_THRESHOLD {
peer.fsm.peerConfig.BgpNeighborCommonState.Flops++
}
for _, rf := range peer.configuredRFlist() {
peer.adjRib.DropAllIn(rf)
}
pm := &peerMsg{
msgType: PEER_MSG_PEER_DOWN,
msgData: peer.peerInfo,
}
for _, s := range peer.siblings {
s.peerMsgCh <- pm
}
}
// clear counter
if h.fsm.adminState == ADMIN_STATE_DOWN {
h.fsm.peerConfig.BgpNeighborCommonState = config.BgpNeighborCommonState{}
}
case FSM_MSG_BGP_MESSAGE:
switch m := e.MsgData.(type) {
case *bgp.MessageError:
peer.outgoing <- bgp.NewBGPNotificationMessage(m.TypeCode, m.SubTypeCode, m.Data)
case *bgp.BGPMessage:
peer.handleBGPmessage(m)
default:
log.WithFields(log.Fields{
"Topic": "Peer",
"Key": peer.peerConfig.NeighborAddress,
"Data": e.MsgData,
}).Panic("unknonw msg type")
}
}
case m := <-peer.serverMsgCh:
peer.handleServerMsg(m)
case m := <-peer.peerMsgCh:
peer.handlePeerMsg(m)
}
}
}
}
func (peer *Peer) Stop() error {
peer.t.Kill(nil)
return peer.t.Wait()
}
func (peer *Peer) PassConn(conn *net.TCPConn) {
peer.connCh <- conn
}
func (peer *Peer) MarshalJSON() ([]byte, error) {
return json.Marshal(peer.ToApiStruct())
}
func (peer *Peer) ToApiStruct() *api.Peer {
f := peer.fsm
c := f.peerConfig
remoteCap := make([]*api.Capability, 0, len(peer.capMap))
for _, c := range peer.capMap {
remoteCap = append(remoteCap, c.ToApiStruct())
}
caps := capabilitiesFromConfig(&peer.globalConfig, &peer.peerConfig)
localCap := make([]*api.Capability, 0, len(caps))
for _, c := range caps {
localCap = append(localCap, c.ToApiStruct())
}
conf := &api.PeerConf{
RemoteIp: c.NeighborAddress.String(),
Id: peer.peerInfo.ID.To4().String(),
RemoteAs: c.PeerAs,
RemoteCap: remoteCap,
LocalCap: localCap,
KeepaliveInterval: uint32(peer.fsm.peerConfig.Timers.KeepaliveInterval),
Holdtime: uint32(peer.fsm.peerConfig.Timers.HoldTime),
}
s := c.BgpNeighborCommonState
uptime := int64(0)
if s.Uptime != 0 {
uptime = int64(time.Now().Sub(time.Unix(s.Uptime, 0)).Seconds())
}
downtime := int64(0)
if s.Downtime != 0 {
downtime = int64(time.Now().Sub(time.Unix(s.Downtime, 0)).Seconds())
}
advertized := uint32(0)
received := uint32(0)
accepted := uint32(0)
if f.state == bgp.BGP_FSM_ESTABLISHED {
for _, rf := range peer.configuredRFlist() {
advertized += uint32(peer.adjRib.GetOutCount(rf))
received += uint32(peer.adjRib.GetInCount(rf))
accepted += uint32(peer.adjRib.GetInCount(rf))
}
}
keepalive := uint32(0)
if f.negotiatedHoldTime != 0 {
if f.negotiatedHoldTime < f.peerConfig.Timers.HoldTime {
keepalive = uint32(f.negotiatedHoldTime / 3)
} else {
keepalive = uint32(f.peerConfig.Timers.KeepaliveInterval)
}
}
info := &api.PeerInfo{
BgpState: f.state.String(),
AdminState: f.adminState.String(),
FsmEstablishedTransitions: s.EstablishedCount,
TotalMessageOut: s.TotalOut,
TotalMessageIn: s.TotalIn,
UpdateMessageOut: s.UpdateOut,
UpdateMessageIn: s.UpdateIn,
KeepAliveMessageOut: s.KeepaliveOut,
KeepAliveMessageIn: s.KeepaliveIn,
OpenMessageOut: s.OpenOut,
OpenMessageIn: s.OpenIn,
NotificationOut: s.NotifyOut,
NotificationIn: s.NotifyIn,
RefreshMessageOut: s.RefreshOut,
RefreshMessageIn: s.RefreshIn,
DiscardedOut: s.DiscardedOut,
DiscardedIn: s.DiscardedIn,
Uptime: uptime,
Downtime: downtime,
Received: received,
Accepted: accepted,
Advertized: advertized,
OutQ: uint32(len(peer.outgoing)),
Flops: s.Flops,
NegotiatedHoldtime: uint32(f.negotiatedHoldTime),
KeepaliveInterval: keepalive,
}
return &api.Peer{
Conf: conf,
Info: info,
}
}