diff options
-rw-r--r-- | packet/bgp.go | 5 | ||||
-rw-r--r-- | server/peer.go | 12 | ||||
-rw-r--r-- | table/message.go | 11 | ||||
-rw-r--r-- | table/path.go | 110 |
4 files changed, 124 insertions, 14 deletions
diff --git a/packet/bgp.go b/packet/bgp.go index db7ff88a..f8d1f637 100644 --- a/packet/bgp.go +++ b/packet/bgp.go @@ -49,6 +49,11 @@ const ( ) const ( + BGP_ASPATH_ATTR_TYPE_SET = 1 + BGP_ASPATH_ATTR_TYPE_SEQ = 2 +) + +const ( _ = iota BGP_MSG_OPEN BGP_MSG_UPDATE diff --git a/server/peer.go b/server/peer.go index 77e6a01b..68e67254 100644 --- a/server/peer.go +++ b/server/peer.go @@ -24,6 +24,7 @@ import ( "github.com/osrg/gobgp/table" "gopkg.in/tomb.v2" "net" + "strings" "time" ) @@ -314,6 +315,8 @@ func (peer *Peer) handleREST(restReq *api.RestRequest) { } func (peer *Peer) sendUpdateMsgFromPaths(pList []table.Path) { + pList = table.CloneAndUpdatePathAttrs(pList, &peer.globalConfig, &peer.peerConfig) + peer.adjRib.UpdateOut(pList) sendpathList := []table.Path{} for _, p := range pList { @@ -475,6 +478,15 @@ func (peer *Peer) Stop() error { } func (peer *Peer) PassConn(conn *net.TCPConn) { + localAddr := func(addrPort string) string { + if strings.Index(addrPort, "[") == -1 { + return strings.Split(addrPort, ":")[0] + } + idx := strings.LastIndex(addrPort, ":") + return addrPort[1 : idx-1] + }(conn.LocalAddr().String()) + + peer.peerConfig.LocalAddress = net.ParseIP(localAddr) peer.acceptedConnCh <- conn } diff --git a/table/message.go b/table/message.go index 12a51cf6..b19bcec2 100644 --- a/table/message.go +++ b/table/message.go @@ -17,6 +17,7 @@ package table import ( "bytes" + "github.com/osrg/gobgp/config" "github.com/osrg/gobgp/packet" ) @@ -127,6 +128,16 @@ func cloneAttrSlice(attrs []bgp.PathAttributeInterface) []bgp.PathAttributeInter return clonedAttrs } +func CloneAndUpdatePathAttrs(pathList []Path, global *config.Global, peer *config.Neighbor) []Path { + newPathList := make([]Path, 0, len(pathList)) + for _, p := range pathList { + clone := p.clone(p.IsWithdraw()) + clone.updatePathAttrs(global, peer) + newPathList = append(newPathList, clone) + } + return newPathList +} + func createUpdateMsgFromPath(path Path, msg *bgp.BGPMessage) *bgp.BGPMessage { rf := path.GetRouteFamily() diff --git a/table/path.go b/table/path.go index 832a0ec6..634eda39 100644 --- a/table/path.go +++ b/table/path.go @@ -19,6 +19,7 @@ import ( "encoding/json" "fmt" log "github.com/Sirupsen/logrus" + "github.com/osrg/gobgp/config" "github.com/osrg/gobgp/packet" "net" "reflect" @@ -29,6 +30,7 @@ type Path interface { String() string getPathAttrs() []bgp.PathAttributeInterface getPathAttr(bgp.BGPAttrType) (int, bgp.PathAttributeInterface) + updatePathAttrs(global *config.Global, peer *config.Neighbor) GetRouteFamily() bgp.RouteFamily setSource(source *PeerInfo) getSource() *PeerInfo @@ -80,6 +82,99 @@ func NewPathDefault(rf bgp.RouteFamily, source *PeerInfo, nlri bgp.AddrPrefixInt return path } +func cloneAsPath(asAttr *bgp.PathAttributeAsPath) *bgp.PathAttributeAsPath { + newASparams := make([]bgp.AsPathParamInterface, len(asAttr.Value)) + for i, param := range asAttr.Value { + asParam := param.(*bgp.As4PathParam) + as := make([]uint32, len(asParam.AS)) + copy(as, asParam.AS) + newASparams[i] = bgp.NewAs4PathParam(asParam.Type, as) + } + return bgp.NewPathAttributeAsPath(newASparams) +} + +func (pd *PathDefault) updatePathAttrs(global *config.Global, peer *config.Neighbor) { + newPathAttrs := make([]bgp.PathAttributeInterface, len(pd.pathAttrs)) + for i, v := range pd.pathAttrs { + newPathAttrs[i] = v + } + pd.pathAttrs = newPathAttrs + + if peer.RouteServer.RouteServerClient { + return + } + + if peer.PeerType == config.PEER_TYPE_EXTERNAL { + // NEXTHOP handling + idx, _ := pd.getPathAttr(bgp.BGP_ATTR_TYPE_NEXT_HOP) + if idx < 0 { + log.Fatal("missing NEXTHOP mandatory attribute") + } + newNexthop := bgp.NewPathAttributeNextHop(peer.LocalAddress.String()) + newPathAttrs[idx] = newNexthop + + // AS_PATH handling + // + // When a given BGP speaker advertises the route to an external + // peer, the advertising speaker updates the AS_PATH attribute + // as follows: + // 1) if the first path segment of the AS_PATH is of type + // AS_SEQUENCE, the local system prepends its own AS num as + // the last element of the sequence (put it in the left-most + // position with respect to the position of octets in the + // protocol message). If the act of prepending will cause an + // overflow in the AS_PATH segment (i.e., more than 255 + // ASes), it SHOULD prepend a new segment of type AS_SEQUENCE + // and prepend its own AS number to this new segment. + // + // 2) if the first path segment of the AS_PATH is of type AS_SET + // , the local system prepends a new path segment of type + // AS_SEQUENCE to the AS_PATH, including its own AS number in + // that segment. + // + // 3) if the AS_PATH is empty, the local system creates a path + // segment of type AS_SEQUENCE, places its own AS into that + // segment, and places that segment into the AS_PATH. + idx, originalAsPath := pd.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH) + if idx < 0 { + log.Fatal("missing AS_PATH mandatory attribute") + } + asPath := cloneAsPath(originalAsPath.(*bgp.PathAttributeAsPath)) + newPathAttrs[idx] = asPath + fst := asPath.Value[0].(*bgp.As4PathParam) + if len(asPath.Value) > 0 && fst.Type == bgp.BGP_ASPATH_ATTR_TYPE_SEQ && + fst.ASLen() < 255 { + fst.AS = append([]uint32{global.As}, fst.AS...) + fst.Num += 1 + } else { + p := bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint32{global.As}) + asPath.Value = append([]bgp.AsPathParamInterface{p}, asPath.Value...) + } + + // MED Handling + idx, _ = pd.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC) + if idx >= 0 { + newPathAttrs = append(newPathAttrs[:idx], newPathAttrs[idx+1:]...) + } + } else if peer.PeerType == config.PEER_TYPE_INTERNAL { + // For iBGP peers we are required to send local-pref attribute + // for connected or local prefixes. + // We set default local-pref 100. + p := bgp.NewPathAttributeLocalPref(100) + idx, _ := pd.getPathAttr(bgp.BGP_ATTR_TYPE_LOCAL_PREF) + if idx < 0 { + newPathAttrs = append(newPathAttrs, p) + } else { + newPathAttrs[idx] = p + } + } else { + log.WithFields(log.Fields{ + "Topic": "Peer", + "Key": peer.NeighborAddress, + }).Warnf("invalid peer type: %d", peer.PeerType) + } +} + func (pd *PathDefault) getTimestamp() time.Time { return pd.timestamp } @@ -107,11 +202,7 @@ func (pd *PathDefault) clone(isWithdraw bool) Path { nlri := pd.nlri if isWithdraw { if pd.IsWithdraw() { - log.WithFields(log.Fields{ - "Topic": "Table", - "Key": pd.getNlri().String(), - "Peer": pd.getSource().Address.String(), - }).Fatal("Withdraw path is not supposed to be cloned") + nlri = pd.nlri } else { nlri = &bgp.WithdrawnRoute{pd.nlri.(*bgp.NLRInfo).IPAddrPrefix} } @@ -271,15 +362,6 @@ func NewIPv6Path(source *PeerInfo, nlri bgp.AddrPrefixInterface, isWithdraw bool func (ipv6p *IPv6Path) clone(isWithdraw bool) Path { nlri := ipv6p.nlri - if isWithdraw { - if ipv6p.IsWithdraw() { - log.WithFields(log.Fields{ - "Topic": "Table", - "Key": ipv6p.getNlri().String(), - "Peer": ipv6p.getSource().Address.String(), - }).Fatal("Withdraw path is not supposed to be cloned") - } - } return CreatePath(ipv6p.source, nlri, ipv6p.pathAttrs, isWithdraw, ipv6p.PathDefault.timestamp) } |